From b20724f725f023ab2146697fe2b3a8ea7f085e11 Mon Sep 17 00:00:00 2001 From: Roy Tam Date: Sat, 25 Aug 2018 22:56:03 +0800 Subject: [PATCH] ported mozilla patches: - bug677638 - (initial) MessageChannel API - bug887241, Use strings, not atoms, for worker events, r=khuey --- content/base/src/nsContentUtils.cpp | 9 +- content/base/src/nsINode.cpp | 6 +- content/events/public/EventTarget.h | 17 +- content/events/src/EventTarget.cpp | 25 +- content/events/src/nsDOMEvent.cpp | 21 +- content/events/src/nsDOMEvent.h | 2 +- content/events/src/nsDOMEventTargetHelper.cpp | 4 +- content/events/src/nsDOMEventTargetHelper.h | 13 +- content/events/src/nsEventListenerManager.cpp | 112 ++-- content/events/src/nsEventListenerManager.h | 33 +- .../html/content/src/nsGenericHTMLElement.cpp | 2 +- content/svg/content/src/nsSVGElement.cpp | 2 +- dom/base/MessageChannel.cpp | 81 +++ dom/base/MessageChannel.h | 72 +++ dom/base/MessagePort.cpp | 477 ++++++++++++++++++ dom/base/MessagePort.h | 86 ++++ dom/base/StructuredCloneTags.h | 1 + dom/base/moz.build | 4 + dom/base/nsGlobalWindow.cpp | 33 ++ dom/base/nsGlobalWindow.h | 6 +- dom/base/test/Makefile.in | 11 + .../test/iframe_messageChannel_cloning.html | 22 + .../test/iframe_messageChannel_pingpong.html | 33 ++ dom/base/test/iframe_messageChannel_post.html | 24 + dom/base/test/test_messageChannel.html | 49 ++ .../test/test_messageChannel_cloning.html | 70 +++ .../test/test_messageChannel_pingpong.html | 77 +++ dom/base/test/test_messageChannel_post.html | 72 +++ dom/base/test/test_messageChannel_pref.html | 42 ++ dom/base/test/test_messageChannel_start.html | 234 +++++++++ .../test_messageChannel_transferable.html | 67 +++ .../test/test_messageChannel_unshipped.html | 123 +++++ dom/webidl/MessageChannel.webidl | 14 + dom/webidl/MessagePort.webidl | 23 + dom/webidl/WebIDL.mk | 2 + widget/nsGUIEvent.h | 3 + 36 files changed, 1787 insertions(+), 85 deletions(-) create mode 100644 dom/base/MessageChannel.cpp create mode 100644 dom/base/MessageChannel.h create mode 100644 dom/base/MessagePort.cpp create mode 100644 dom/base/MessagePort.h create mode 100644 dom/base/test/iframe_messageChannel_cloning.html create mode 100644 dom/base/test/iframe_messageChannel_pingpong.html create mode 100644 dom/base/test/iframe_messageChannel_post.html create mode 100644 dom/base/test/test_messageChannel.html create mode 100644 dom/base/test/test_messageChannel_cloning.html create mode 100644 dom/base/test/test_messageChannel_pingpong.html create mode 100644 dom/base/test/test_messageChannel_post.html create mode 100644 dom/base/test/test_messageChannel_pref.html create mode 100644 dom/base/test/test_messageChannel_start.html create mode 100644 dom/base/test/test_messageChannel_transferable.html create mode 100644 dom/base/test/test_messageChannel_unshipped.html create mode 100644 dom/webidl/MessageChannel.webidl create mode 100644 dom/webidl/MessagePort.webidl diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 8eb116652..ae21a59d8 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -3181,9 +3181,12 @@ nsContentUtils::IsEventAttributeName(nsIAtom* aName, int32_t aType) uint32_t nsContentUtils::GetEventId(nsIAtom* aName) { - EventNameMapping mapping; - if (sAtomEventTable->Get(aName, &mapping)) - return mapping.mId; + if (aName) { + EventNameMapping mapping; + if (sAtomEventTable->Get(aName, &mapping)) { + return mapping.mId; + } + } return NS_USER_DEFINED_EVENT; } diff --git a/content/base/src/nsINode.cpp b/content/base/src/nsINode.cpp index eb9bfccc4..18a5d32b4 100644 --- a/content/base/src/nsINode.cpp +++ b/content/base/src/nsINode.cpp @@ -2115,13 +2115,15 @@ nsINode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const #define EVENT(name_, id_, type_, struct_) \ EventHandlerNonNull* nsINode::GetOn##name_() { \ nsEventListenerManager *elm = GetListenerManager(false); \ - return elm ? elm->GetEventHandler(nsGkAtoms::on##name_) : nullptr; \ + return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString()) \ + : nullptr; \ } \ void nsINode::SetOn##name_(EventHandlerNonNull* handler, \ ErrorResult& error) { \ nsEventListenerManager *elm = GetListenerManager(true); \ if (elm) { \ - error = elm->SetEventHandler(nsGkAtoms::on##name_, handler); \ + error = elm->SetEventHandler(nsGkAtoms::on##name_, \ + EmptyString(), handler); \ } else { \ error.Throw(NS_ERROR_OUT_OF_MEMORY); \ } \ diff --git a/content/events/public/EventTarget.h b/content/events/public/EventTarget.h index 9d8ebdb1e..cc0a74d46 100644 --- a/content/events/public/EventTarget.h +++ b/content/events/public/EventTarget.h @@ -48,18 +48,16 @@ public: ErrorResult& aRv); bool DispatchEvent(nsDOMEvent& aEvent, ErrorResult& aRv); + // Note, this takes the type in onfoo form! EventHandlerNonNull* GetEventHandler(const nsAString& aType) { nsCOMPtr type = do_GetAtom(aType); - return GetEventHandler(type); + return GetEventHandler(type, EmptyString()); } + // Note, this takes the type in onfoo form! void SetEventHandler(const nsAString& aType, EventHandlerNonNull* aHandler, - ErrorResult& rv) - { - nsCOMPtr type = do_GetAtom(aType); - return SetEventHandler(type, aHandler, rv); - } + ErrorResult& rv); // Note, for an event 'foo' aType will be 'onfoo'. virtual void EventListenerAdded(nsIAtom* aType) {} @@ -71,9 +69,10 @@ public: virtual nsIDOMWindow* GetOwnerGlobal() = 0; protected: - EventHandlerNonNull* GetEventHandler(nsIAtom* aType); - void SetEventHandler(nsIAtom* aType, EventHandlerNonNull* aHandler, - ErrorResult& rv); + EventHandlerNonNull* GetEventHandler(nsIAtom* aType, + const nsAString& aTypeString); + void SetEventHandler(nsIAtom* aType, const nsAString& aTypeString, + EventHandlerNonNull* aHandler, ErrorResult& rv); }; NS_DEFINE_STATIC_IID_ACCESSOR(EventTarget, NS_EVENTTARGET_IID) diff --git a/content/events/src/EventTarget.cpp b/content/events/src/EventTarget.cpp index befa0b395..35a304756 100644 --- a/content/events/src/EventTarget.cpp +++ b/content/events/src/EventTarget.cpp @@ -23,17 +23,34 @@ EventTarget::RemoveEventListener(const nsAString& aType, } EventHandlerNonNull* -EventTarget::GetEventHandler(nsIAtom* aType) +EventTarget::GetEventHandler(nsIAtom* aType, const nsAString& aTypeString) { nsEventListenerManager* elm = GetListenerManager(false); - return elm ? elm->GetEventHandler(aType) : nullptr; + return elm ? elm->GetEventHandler(aType, aTypeString) : nullptr; } void -EventTarget::SetEventHandler(nsIAtom* aType, EventHandlerNonNull* aHandler, +EventTarget::SetEventHandler(const nsAString& aType, + EventHandlerNonNull* aHandler, ErrorResult& rv) { - rv = GetListenerManager(true)->SetEventHandler(aType, aHandler); + if (NS_IsMainThread()) { + nsCOMPtr type = do_GetAtom(aType); + return SetEventHandler(type, EmptyString(), aHandler, rv); + } + return SetEventHandler(nullptr, + Substring(aType, 2), // Remove "on" + aHandler, rv); +} + +void +EventTarget::SetEventHandler(nsIAtom* aType, const nsAString& aTypeString, + EventHandlerNonNull* aHandler, + ErrorResult& rv) +{ + rv = GetListenerManager(true)->SetEventHandler(aType, + aTypeString, + aHandler); } } // namespace dom diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp index 247367b93..4251775f9 100644 --- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -59,6 +59,7 @@ nsDOMEvent::ConstructorInit(mozilla::dom::EventTarget* aOwner, { SetIsDOMBinding(); SetOwner(aOwner); + mIsMainThreadEvent = mOwner || NS_IsMainThread(); mPrivateDataDuplicated = false; @@ -221,19 +222,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END // nsIDOMEventInterface NS_METHOD nsDOMEvent::GetType(nsAString& aType) { - if (!mCachedType.IsEmpty()) { - aType = mCachedType; + if (!mIsMainThreadEvent || !mEvent->typeString.IsEmpty()) { + aType = mEvent->typeString; return NS_OK; } const char* name = GetEventName(mEvent->message); if (name) { CopyASCIItoUTF16(name, aType); - mCachedType = aType; return NS_OK; } else if (mEvent->message == NS_USER_DEFINED_EVENT && mEvent->userType) { aType = Substring(nsDependentAtomString(mEvent->userType), 2); // Remove "on" - mCachedType = aType; + mEvent->typeString = aType; return NS_OK; } @@ -459,9 +459,15 @@ nsDOMEvent::PreventDefault() void nsDOMEvent::SetEventType(const nsAString& aEventTypeArg) { - mEvent->userType = - nsContentUtils::GetEventIdAndAtom(aEventTypeArg, mEvent->eventStructType, - &(mEvent->message)); + if (mIsMainThreadEvent) { + mEvent->userType = + nsContentUtils::GetEventIdAndAtom(aEventTypeArg, mEvent->eventStructType, + &(mEvent->message)); + } else { + mEvent->userType = nullptr; + mEvent->message = NS_USER_DEFINED_EVENT; + mEvent->typeString = aEventTypeArg; + } } NS_IMETHODIMP @@ -488,7 +494,6 @@ nsDOMEvent::InitEvent(const nsAString& aEventTypeArg, bool aCanBubbleArg, bool a // re-dispatching it. mEvent->target = nullptr; mEvent->originalTarget = nullptr; - mCachedType = aEventTypeArg; return NS_OK; } diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h index 3ca30a02c..90f921216 100644 --- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -185,9 +185,9 @@ protected: nsRefPtr mPresContext; nsCOMPtr mExplicitOriginalTarget; nsCOMPtr mOwner; // nsPIDOMWindow for now. - nsString mCachedType; bool mEventIsInternal; bool mPrivateDataDuplicated; + bool mIsMainThreadEvent; }; #define NS_FORWARD_TO_NSDOMEVENT \ diff --git a/content/events/src/nsDOMEventTargetHelper.cpp b/content/events/src/nsDOMEventTargetHelper.cpp index d4356e447..1015b184b 100644 --- a/content/events/src/nsDOMEventTargetHelper.cpp +++ b/content/events/src/nsDOMEventTargetHelper.cpp @@ -289,7 +289,7 @@ nsDOMEventTargetHelper::SetEventHandler(nsIAtom* aType, handler = new EventHandlerNonNull(callable); } ErrorResult rv; - SetEventHandler(aType, handler, rv); + SetEventHandler(aType, EmptyString(), handler, rv); return rv.ErrorCode(); } @@ -298,7 +298,7 @@ nsDOMEventTargetHelper::GetEventHandler(nsIAtom* aType, JSContext* aCx, JS::Value* aValue) { - EventHandlerNonNull* handler = GetEventHandler(aType); + EventHandlerNonNull* handler = GetEventHandler(aType, EmptyString()); if (handler) { *aValue = JS::ObjectValue(*handler->Callable()); } else { diff --git a/content/events/src/nsDOMEventTargetHelper.h b/content/events/src/nsDOMEventTargetHelper.h index cda3b61aa..b83904656 100644 --- a/content/events/src/nsDOMEventTargetHelper.h +++ b/content/events/src/nsDOMEventTargetHelper.h @@ -166,12 +166,21 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMEventTargetHelper, #define IMPL_EVENT_HANDLER(_event) \ inline mozilla::dom::EventHandlerNonNull* GetOn##_event() \ { \ - return GetEventHandler(nsGkAtoms::on##_event); \ + if (NS_IsMainThread()) { \ + return GetEventHandler(nsGkAtoms::on##_event, EmptyString()); \ + } \ + return GetEventHandler(nullptr, NS_LITERAL_STRING(#_event)); \ } \ inline void SetOn##_event(mozilla::dom::EventHandlerNonNull* aCallback, \ mozilla::ErrorResult& aRv) \ { \ - SetEventHandler(nsGkAtoms::on##_event, aCallback, aRv); \ + if (NS_IsMainThread()) { \ + SetEventHandler(nsGkAtoms::on##_event, EmptyString(), \ + aCallback, aRv); \ + } else { \ + SetEventHandler(nullptr, NS_LITERAL_STRING(#_event), \ + aCallback, aRv); \ + } \ } /* Use this macro to declare functions that forward the behavior of this diff --git a/content/events/src/nsEventListenerManager.cpp b/content/events/src/nsEventListenerManager.cpp index 3a2294f4c..ebcd8c349 100644 --- a/content/events/src/nsEventListenerManager.cpp +++ b/content/events/src/nsEventListenerManager.cpp @@ -64,9 +64,11 @@ using namespace mozilla::hal; static PRLogModuleInfo* gXssPRLog; #endif -#define EVENT_TYPE_EQUALS(ls, type, userType, allEvents) \ - ((ls->mEventType == type && \ - (ls->mEventType != NS_USER_DEFINED_EVENT || ls->mTypeAtom == userType)) || \ +#define EVENT_TYPE_EQUALS(ls, type, userType, typeString, allEvents) \ + ((ls->mEventType == type && \ + (ls->mEventType != NS_USER_DEFINED_EVENT || \ + (mIsMainThreadELM && ls->mTypeAtom == userType) || \ + (!mIsMainThreadELM && ls->mTypeString.Equals(typeString)))) || \ (allEvents && ls->mAllEvents)) static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, @@ -116,12 +118,14 @@ nsEventListenerManager::nsEventListenerManager(EventTarget* aTarget) : mMayHaveTouchEventListener(false), mMayHaveMouseEnterLeaveEventListener(false), mClearingListeners(false), + mIsMainThreadELM(NS_IsMainThread()), mNoListenerForEvent(0), mTarget(aTarget) { NS_ASSERTION(aTarget, "unexpected null pointer"); - ++sCreatedCount; + if (mIsMainThreadELM) + ++sCreatedCount; // enable logging for XSS #ifdef PR_LOGGING @@ -217,11 +221,14 @@ nsEventListenerManager::AddEventListenerInternal( const EventListenerHolder& aListener, uint32_t aType, nsIAtom* aTypeAtom, + const nsAString& aTypeString, const EventListenerFlags& aFlags, bool aHandler, bool aAllEvents) { - NS_ABORT_IF_FALSE((aType && aTypeAtom) || aAllEvents, "Missing type"); + MOZ_ASSERT((NS_IsMainThread() && aType && aTypeAtom) || // Main thread + (!NS_IsMainThread() && aType && !aTypeString.IsEmpty()) || // non-main-thread + aAllEvents, "Missing type"); // all-events listener if (!aListener || mClearingListeners) { return; @@ -238,7 +245,7 @@ nsEventListenerManager::AddEventListenerInternal( if (ls->mListener == aListener && ls->mListenerIsHandler == aHandler && ls->mFlags == aFlags && - EVENT_TYPE_EQUALS(ls, aType, aTypeAtom, aAllEvents)) { + EVENT_TYPE_EQUALS(ls, aType, aTypeAtom, aTypeString, aAllEvents)) { return; } } @@ -248,7 +255,9 @@ nsEventListenerManager::AddEventListenerInternal( ls = aAllEvents ? mListeners.InsertElementAt(0) : mListeners.AppendElement(); ls->mListener = aListener; + MOZ_ASSERT(aType < PR_UINT16_MAX); ls->mEventType = aType; + ls->mTypeString = aTypeString; ls->mTypeAtom = aTypeAtom; ls->mFlags = aFlags; ls->mListenerIsHandler = aHandler; @@ -451,6 +460,7 @@ nsEventListenerManager::RemoveEventListenerInternal( const EventListenerHolder& aListener, uint32_t aType, nsIAtom* aUserType, + const nsAString& aTypeString, const EventListenerFlags& aFlags, bool aAllEvents) { @@ -471,7 +481,7 @@ nsEventListenerManager::RemoveEventListenerInternal( for (uint32_t i = 0; i < count; ++i) { ls = &mListeners.ElementAt(i); - if (EVENT_TYPE_EQUALS(ls, aType, aUserType, aAllEvents)) { + if (EVENT_TYPE_EQUALS(ls, aType, aUserType, aTypeString, aAllEvents)) { ++typeCount; if (ls->mListener == aListener && ls->mFlags.EqualsIgnoringTrustness(aFlags)) { @@ -513,15 +523,23 @@ nsEventListenerManager::RemoveEventListenerInternal( } } -static inline bool -ListenerCanHandle(nsListenerStruct* aLs, nsEvent* aEvent) +bool +nsEventListenerManager::ListenerCanHandle(nsListenerStruct* aLs, nsEvent* aEvent) { // This is slightly different from EVENT_TYPE_EQUALS in that it returns // true even when aEvent->message == NS_USER_DEFINED_EVENT and // aLs=>mEventType != NS_USER_DEFINED_EVENT as long as the atoms are the same - return (aEvent->message == NS_USER_DEFINED_EVENT ? - (aLs->mTypeAtom == aEvent->userType) : - (aLs->mEventType == aEvent->message)) || aLs->mAllEvents; + if (aLs->mAllEvents) { + return true; + } + if (aEvent->message == NS_USER_DEFINED_EVENT) { + if (mIsMainThreadELM) { + return aLs->mTypeAtom == aEvent->userType; + } + return aLs->mTypeString.Equals(aEvent->typeString); + } + MOZ_ASSERT(mIsMainThreadELM); + return aLs->mEventType == aEvent->message; } void @@ -529,9 +547,10 @@ nsEventListenerManager::AddEventListenerByType(const EventListenerHolder& aListe const nsAString& aType, const EventListenerFlags& aFlags) { - nsCOMPtr atom = do_GetAtom(NS_LITERAL_STRING("on") + aType); + nsCOMPtr atom = + mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr; uint32_t type = nsContentUtils::GetEventId(atom); - AddEventListenerInternal(aListener, type, atom, aFlags); + AddEventListenerInternal(aListener, type, atom, aType, aFlags); } void @@ -540,14 +559,16 @@ nsEventListenerManager::RemoveEventListenerByType( const nsAString& aType, const EventListenerFlags& aFlags) { - nsCOMPtr atom = do_GetAtom(NS_LITERAL_STRING("on") + aType); + nsCOMPtr atom = + mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr; uint32_t type = nsContentUtils::GetEventId(atom); - RemoveEventListenerInternal(aListener, type, atom, aFlags); + RemoveEventListenerInternal(aListener, type, atom, aType, aFlags); } nsListenerStruct* nsEventListenerManager::FindEventHandler(uint32_t aEventType, - nsIAtom* aTypeAtom) + nsIAtom* aTypeAtom, + const nsAString& aTypeString) { // Run through the listeners for this type and see if a script // listener is registered @@ -556,7 +577,7 @@ nsEventListenerManager::FindEventHandler(uint32_t aEventType, for (uint32_t i = 0; i < count; ++i) { ls = &mListeners.ElementAt(i); if (ls->mListenerIsHandler && - EVENT_TYPE_EQUALS(ls, aEventType, aTypeAtom, false)) { + EVENT_TYPE_EQUALS(ls, aEventType, aTypeAtom, aTypeString, false)) { return ls; } } @@ -567,6 +588,7 @@ nsresult nsEventListenerManager::SetEventHandlerInternal(nsIScriptContext *aContext, JS::Handle aScopeObject, nsIAtom* aName, + const nsAString& aTypeString, const nsEventHandler& aHandler, bool aPermitUntrustedEvents, nsListenerStruct **aListenerStruct) @@ -576,7 +598,7 @@ nsEventListenerManager::SetEventHandlerInternal(nsIScriptContext *aContext, nsresult rv = NS_OK; uint32_t eventType = nsContentUtils::GetEventId(aName); - nsListenerStruct* ls = FindEventHandler(eventType, aName); + nsListenerStruct* ls = FindEventHandler(eventType, aName, aTypeString); if (!ls) { // If we didn't find a script listener or no listeners existed @@ -590,9 +612,10 @@ nsEventListenerManager::SetEventHandlerInternal(nsIScriptContext *aContext, if (NS_SUCCEEDED(rv)) { EventListenerHolder holder(scriptListener); - AddEventListenerInternal(holder, eventType, aName, flags, true); + AddEventListenerInternal(holder, eventType, aName, aTypeString, flags, + true); - ls = FindEventHandler(eventType, aName); + ls = FindEventHandler(eventType, aName, aTypeString); } } else { nsIJSEventListener* scriptListener = ls->GetJSListener(); @@ -761,8 +784,8 @@ nsEventListenerManager::SetEventHandler(nsIAtom *aName, global->GetGlobalJSObject()); nsListenerStruct *ls; - rv = SetEventHandlerInternal(context, scope, aName, nsEventHandler(), - aPermitUntrustedEvents, &ls); + rv = SetEventHandlerInternal(context, scope, aName, EmptyString(), + nsEventHandler(), aPermitUntrustedEvents, &ls); NS_ENSURE_SUCCESS(rv, rv); if (!aDeferCompilation) { @@ -773,14 +796,15 @@ nsEventListenerManager::SetEventHandler(nsIAtom *aName, } void -nsEventListenerManager::RemoveEventHandler(nsIAtom* aName) +nsEventListenerManager::RemoveEventHandler(nsIAtom* aName, + const nsAString& aTypeString) { if (mClearingListeners) { return; } uint32_t eventType = nsContentUtils::GetEventId(aName); - nsListenerStruct* ls = FindEventHandler(eventType, aName); + nsListenerStruct* ls = FindEventHandler(eventType, aName, aTypeString); if (ls) { mListeners.RemoveElementAt(uint32_t(ls - &mListeners.ElementAt(0))); @@ -1095,8 +1119,8 @@ nsEventListenerManager::AddListenerForAllEvents(nsIDOMEventListener* aListener, flags.mAllowUntrustedEvents = aWantsUntrusted; flags.mInSystemGroup = aSystemEventGroup; EventListenerHolder holder(aListener); - AddEventListenerInternal(holder, NS_EVENT_TYPE_ALL, nullptr, flags, - false, true); + AddEventListenerInternal(holder, NS_EVENT_TYPE_ALL, nullptr, EmptyString(), + flags, false, true); } void @@ -1108,8 +1132,8 @@ nsEventListenerManager::RemoveListenerForAllEvents(nsIDOMEventListener* aListene flags.mCapture = aUseCapture; flags.mInSystemGroup = aSystemEventGroup; EventListenerHolder holder(aListener); - RemoveEventListenerInternal(holder, NS_EVENT_TYPE_ALL, nullptr, flags, - true); + RemoveEventListenerInternal(holder, NS_EVENT_TYPE_ALL, nullptr, EmptyString(), + flags, true); } bool @@ -1231,10 +1255,11 @@ nsEventListenerManager::HasUnloadListeners() nsresult nsEventListenerManager::SetEventHandler(nsIAtom* aEventName, + const nsAString& aTypeString, EventHandlerNonNull* aHandler) { if (!aHandler) { - RemoveEventHandler(aEventName); + RemoveEventHandler(aEventName, aTypeString); return NS_OK; } @@ -1242,15 +1267,17 @@ nsEventListenerManager::SetEventHandler(nsIAtom* aEventName, // handlers. nsListenerStruct *ignored; return SetEventHandlerInternal(nullptr, JS::NullPtr(), aEventName, - nsEventHandler(aHandler), - !nsContentUtils::IsCallerChrome(), &ignored); + aTypeString, nsEventHandler(aHandler), + !mIsMainThreadELM || + !nsContentUtils::IsCallerChrome(), + &ignored); } nsresult nsEventListenerManager::SetEventHandler(OnErrorEventHandlerNonNull* aHandler) { if (!aHandler) { - RemoveEventHandler(nsGkAtoms::onerror); + RemoveEventHandler(nsGkAtoms::onerror, EmptyString()); return NS_OK; } @@ -1258,15 +1285,17 @@ nsEventListenerManager::SetEventHandler(OnErrorEventHandlerNonNull* aHandler) // handlers. nsListenerStruct *ignored; return SetEventHandlerInternal(nullptr, JS::NullPtr(), nsGkAtoms::onerror, - nsEventHandler(aHandler), - !nsContentUtils::IsCallerChrome(), &ignored); + EmptyString(), nsEventHandler(aHandler), + !mIsMainThreadELM || + !nsContentUtils::IsCallerChrome(), + &ignored); } nsresult nsEventListenerManager::SetEventHandler(BeforeUnloadEventHandlerNonNull* aHandler) { if (!aHandler) { - RemoveEventHandler(nsGkAtoms::onbeforeunload); + RemoveEventHandler(nsGkAtoms::onbeforeunload, EmptyString()); return NS_OK; } @@ -1274,15 +1303,18 @@ nsEventListenerManager::SetEventHandler(BeforeUnloadEventHandlerNonNull* aHandle // handlers. nsListenerStruct *ignored; return SetEventHandlerInternal(nullptr, JS::NullPtr(), nsGkAtoms::onbeforeunload, - nsEventHandler(aHandler), - !nsContentUtils::IsCallerChrome(), &ignored); + EmptyString(), nsEventHandler(aHandler), + !mIsMainThreadELM || + !nsContentUtils::IsCallerChrome(), + &ignored); } const nsEventHandler* -nsEventListenerManager::GetEventHandlerInternal(nsIAtom *aEventName) +nsEventListenerManager::GetEventHandlerInternal(nsIAtom *aEventName, + const nsAString& aTypeString) { uint32_t eventType = nsContentUtils::GetEventId(aEventName); - nsListenerStruct* ls = FindEventHandler(eventType, aEventName); + nsListenerStruct* ls = FindEventHandler(eventType, aEventName, aTypeString); if (!ls) { return nullptr; diff --git a/content/events/src/nsEventListenerManager.h b/content/events/src/nsEventListenerManager.h index 3e5d9c698..a41cc7ce9 100644 --- a/content/events/src/nsEventListenerManager.h +++ b/content/events/src/nsEventListenerManager.h @@ -163,8 +163,9 @@ typedef enum struct nsListenerStruct { mozilla::dom::EventListenerHolder mListener; - nsCOMPtr mTypeAtom; - uint32_t mEventType; + nsCOMPtr mTypeAtom; // for the main thread + nsString mTypeString; // for non-main-threads + uint16_t mEventType; uint8_t mListenerType; bool mListenerIsHandler : 1; bool mHandlerIsString : 1; @@ -298,7 +299,7 @@ public: /** * Remove the current "inline" event listener for aName. */ - void RemoveEventHandler(nsIAtom *aName); + void RemoveEventHandler(nsIAtom *aName, const nsAString& aTypeString); void HandleEvent(nsPresContext* aPresContext, nsEvent* aEvent, @@ -431,7 +432,8 @@ protected: /** * Find the nsListenerStruct for the "inline" event listener for aTypeAtom. */ - nsListenerStruct* FindEventHandler(uint32_t aEventType, nsIAtom* aTypeAtom); + nsListenerStruct* FindEventHandler(uint32_t aEventType, nsIAtom* aTypeAtom, + const nsAString& aTypeString); /** * Set the "inline" event listener for aName to aHandler. aHandler may be @@ -444,6 +446,7 @@ protected: nsresult SetEventHandlerInternal(nsIScriptContext *aContext, JS::Handle aScopeGlobal, nsIAtom* aName, + const nsAString& aTypeString, const nsEventHandler& aHandler, bool aPermitUntrustedEvents, nsListenerStruct **aListenerStruct); @@ -458,6 +461,7 @@ public: * aHandler is null, this will actually remove the event listener */ nsresult SetEventHandler(nsIAtom* aEventName, + const nsAString& aTypeString, mozilla::dom::EventHandlerNonNull* aHandler); nsresult SetEventHandler(mozilla::dom::OnErrorEventHandlerNonNull* aHandler); nsresult SetEventHandler(mozilla::dom::BeforeUnloadEventHandlerNonNull* aHandler); @@ -471,20 +475,23 @@ public: * OnErrorEventHandlerNonNull for some event targets and EventHandlerNonNull * for others. */ - mozilla::dom::EventHandlerNonNull* GetEventHandler(nsIAtom *aEventName) + mozilla::dom::EventHandlerNonNull* GetEventHandler(nsIAtom *aEventName, + const nsAString& aTypeString) { - const nsEventHandler* handler = GetEventHandlerInternal(aEventName); + const nsEventHandler* handler = + GetEventHandlerInternal(aEventName, aTypeString); return handler ? handler->EventHandler() : nullptr; } mozilla::dom::OnErrorEventHandlerNonNull* GetOnErrorEventHandler() { - const nsEventHandler* handler = GetEventHandlerInternal(nsGkAtoms::onerror); + const nsEventHandler* handler = + GetEventHandlerInternal(nsGkAtoms::onerror, EmptyString()); return handler ? handler->OnErrorEventHandler() : nullptr; } mozilla::dom::BeforeUnloadEventHandlerNonNull* GetOnBeforeUnloadEventHandler() { const nsEventHandler* handler = - GetEventHandlerInternal(nsGkAtoms::onbeforeunload); + GetEventHandlerInternal(nsGkAtoms::onbeforeunload, EmptyString()); return handler ? handler->BeforeUnloadEventHandler() : nullptr; } @@ -493,7 +500,8 @@ protected: * Helper method for implementing the various Get*EventHandler above. Will * return null if we don't have an event handler for this event name. */ - const nsEventHandler* GetEventHandlerInternal(nsIAtom* aEventName); + const nsEventHandler* GetEventHandlerInternal(nsIAtom* aEventName, + const nsAString& aTypeString); void AddEventListener(const nsAString& aType, const mozilla::dom::EventListenerHolder& aListener, @@ -507,6 +515,7 @@ protected: const mozilla::dom::EventListenerHolder& aListener, uint32_t aType, nsIAtom* aTypeAtom, + const nsAString& aTypeString, const mozilla::dom::EventListenerFlags& aFlags, bool aHandler = false, bool aAllEvents = false); @@ -514,6 +523,7 @@ protected: const mozilla::dom::EventListenerHolder& aListener, uint32_t aType, nsIAtom* aUserType, + const nsAString& aTypeString, const mozilla::dom::EventListenerFlags& aFlags, bool aAllEvents = false); void RemoveAllListeners(); @@ -522,6 +532,8 @@ protected: nsPIDOMWindow* GetInnerWindowForTarget(); already_AddRefed GetTargetAsInnerWindow() const; + bool ListenerCanHandle(nsListenerStruct* aLs, nsEvent* aEvent); + uint32_t mMayHavePaintEventListener : 1; uint32_t mMayHaveMutationListeners : 1; uint32_t mMayHaveCapturingListeners : 1; @@ -530,7 +542,8 @@ protected: uint32_t mMayHaveTouchEventListener : 1; uint32_t mMayHaveMouseEnterLeaveEventListener : 1; uint32_t mClearingListeners : 1; - uint32_t mNoListenerForEvent : 24; + uint32_t mIsMainThreadELM : 1; + uint32_t mNoListenerForEvent : 23; nsAutoTObserverArray mListeners; mozilla::dom::EventTarget* mTarget; //WEAK diff --git a/content/html/content/src/nsGenericHTMLElement.cpp b/content/html/content/src/nsGenericHTMLElement.cpp index ce130549f..656aa68f3 100644 --- a/content/html/content/src/nsGenericHTMLElement.cpp +++ b/content/html/content/src/nsGenericHTMLElement.cpp @@ -1020,7 +1020,7 @@ nsGenericHTMLElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, else if (IsEventAttributeName(aAttribute)) { nsEventListenerManager* manager = GetListenerManager(false); if (manager) { - manager->RemoveEventHandler(aAttribute); + manager->RemoveEventHandler(aAttribute, EmptyString()); } } } diff --git a/content/svg/content/src/nsSVGElement.cpp b/content/svg/content/src/nsSVGElement.cpp index 7a98fb5d9..59a3383b5 100644 --- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -666,7 +666,7 @@ nsSVGElement::UnsetAttrInternal(int32_t aNamespaceID, nsIAtom* aName, nsEventListenerManager* manager = GetListenerManager(false); if (manager) { nsIAtom* eventName = GetEventNameForAttr(aName); - manager->RemoveEventHandler(eventName); + manager->RemoveEventHandler(eventName, EmptyString()); } return; } diff --git a/dom/base/MessageChannel.cpp b/dom/base/MessageChannel.cpp new file mode 100644 index 000000000..e8d460759 --- /dev/null +++ b/dom/base/MessageChannel.cpp @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MessageChannel.h" +#include "mozilla/Preferences.h" +#include "mozilla/dom/MessageChannelBinding.h" +#include "mozilla/dom/MessagePort.h" +#include "nsContentUtils.h" +#include "nsPIDOMWindow.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_3(MessageChannel, mWindow, mPort1, mPort2) +NS_IMPL_CYCLE_COLLECTING_ADDREF(MessageChannel) +NS_IMPL_CYCLE_COLLECTING_RELEASE(MessageChannel) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessageChannel) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +namespace { + bool gPrefInitialized = false; + bool gPrefEnabled = false; + +} + + +/* static */ bool +MessageChannel::PrefEnabled() +{ + if (!gPrefInitialized) { + Preferences::AddBoolVarCache(&gPrefEnabled, "dom.messageChannel.enabled"); + gPrefInitialized = true; + } + + return gPrefEnabled; +} + +MessageChannel::MessageChannel(nsPIDOMWindow* aWindow) + : mWindow(aWindow) +{ + MOZ_COUNT_CTOR(MessageChannel); + SetIsDOMBinding(); + + mPort1 = new MessagePort(mWindow); + mPort2 = new MessagePort(mWindow); + + mPort1->Entangle(mPort2); + mPort2->Entangle(mPort1); +} + +MessageChannel::~MessageChannel() +{ + MOZ_COUNT_DTOR(MessageChannel); +} + +JSObject* +MessageChannel::WrapObject(JSContext* aCx, JS::Handle aScope) +{ + return MessageChannelBinding::Wrap(aCx, aScope, this); +} + +/* static */ already_AddRefed +MessageChannel::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) +{ + nsCOMPtr window = do_QueryInterface(aGlobal.Get()); + if (!window) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + nsRefPtr channel = new MessageChannel(window); + return channel.forget(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/base/MessageChannel.h b/dom/base/MessageChannel.h new file mode 100644 index 000000000..1f5a22e9e --- /dev/null +++ b/dom/base/MessageChannel.h @@ -0,0 +1,72 @@ + +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_MessageChannel_h +#define mozilla_dom_MessageChannel_h + +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" +#include "nsCOMPtr.h" + +class nsPIDOMWindow; + +namespace mozilla { +namespace dom { + +class MessagePort; + +class MessageChannel MOZ_FINAL : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MessageChannel) + + static bool PrefEnabled(); + +public: + MessageChannel(nsPIDOMWindow* aWindow); + + ~MessageChannel(); + + nsPIDOMWindow* + GetParentObject() const + { + return mWindow; + } + + virtual JSObject* + WrapObject(JSContext* aCx, JS::Handle aScope) MOZ_OVERRIDE; + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); + + MessagePort* + Port1() const + { + return mPort1; + } + + MessagePort* + Port2() const + { + return mPort2; + } + +private: + nsCOMPtr mWindow; + + nsRefPtr mPort1; + nsRefPtr mPort2; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_MessageChannel_h diff --git a/dom/base/MessagePort.cpp b/dom/base/MessagePort.cpp new file mode 100644 index 000000000..364075495 --- /dev/null +++ b/dom/base/MessagePort.cpp @@ -0,0 +1,477 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MessagePort.h" +#include "mozilla/dom/MessageChannel.h" +#include "mozilla/dom/MessagePortBinding.h" +#include "mozilla/dom/StructuredCloneTags.h" +#include "nsGlobalWindow.h" +#include "nsContentUtils.h" +#include "nsEventDispatcher.h" +#include "nsPresContext.h" +#include "nsDOMEvent.h" + +#include "nsIDocument.h" +#include "nsIDOMFile.h" +#include "nsIDOMFileList.h" +#include "nsIDOMMessageEvent.h" +#include "nsIPresShell.h" + +namespace mozilla { +namespace dom { + +class DispatchEventRunnable : public nsRunnable +{ + friend class MessagePort; + + public: + DispatchEventRunnable(MessagePort* aPort) + : mPort(aPort) + { + } + + NS_IMETHOD + Run() + { + nsRefPtr mKungFuDeathGrip(this); + + mPort->mDispatchRunnable = nullptr; + mPort->Dispatch(); + + return NS_OK; + } + + private: + nsRefPtr mPort; +}; + +class PostMessageRunnable : public nsRunnable +{ + friend class MessagePort; + + public: + NS_DECL_NSIRUNNABLE + + PostMessageRunnable() + : mMessage(nullptr) + , mMessageLen(0) + { + } + + ~PostMessageRunnable() + { + // Ensure that the buffer is freed + if (mMessage) { + JSAutoStructuredCloneBuffer buffer; + buffer.adopt(mMessage, mMessageLen); + } + } + + void SetJSData(JSAutoStructuredCloneBuffer& aBuffer) + { + NS_ASSERTION(!mMessage && mMessageLen == 0, "Don't call twice!"); + aBuffer.steal(&mMessage, &mMessageLen); + } + + bool StoreISupports(nsISupports* aSupports) + { + mSupportsArray.AppendElement(aSupports); + return true; + } + + void Dispatch(MessagePort* aPort) + { + mPort = aPort; + NS_DispatchToCurrentThread(this); + } + + private: + nsRefPtr mPort; + uint64_t* mMessage; + size_t mMessageLen; + + nsTArray > mSupportsArray; +}; + +namespace { + +struct StructuredCloneInfo +{ + PostMessageRunnable* mEvent; + MessagePort* mPort; +}; + +static JSObject* +PostMessageReadStructuredClone(JSContext* cx, + JSStructuredCloneReader* reader, + uint32_t tag, + uint32_t data, + void* closure) +{ + NS_ASSERTION(closure, "Must have closure!"); + + if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) { + NS_ASSERTION(!data, "Data should be empty"); + + nsISupports* supports; + if (JS_ReadBytes(reader, &supports, sizeof(supports))) { + JS::Rooted global(cx, JS_GetGlobalForScopeChain(cx)); + if (global) { + JS::Rooted val(cx); + nsCOMPtr wrapper; + if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, global, supports, + val.address(), + getter_AddRefs(wrapper)))) { + return JSVAL_TO_OBJECT(val); + } + } + } + } + + if (tag == SCTAG_DOM_MESSAGEPORT) { + NS_ASSERTION(!data, "Data should be empty"); + + MessagePort* port; + if (JS_ReadBytes(reader, &port, sizeof(port))) { + JS::Rooted global(cx, JS_GetGlobalForScopeChain(cx)); + if (global) { + JS::Rooted obj(cx, port->WrapObject(cx, global)); + if (JS_WrapObject(cx, obj.address())) { + return obj; + } + } + } + } + + const JSStructuredCloneCallbacks* runtimeCallbacks = + js::GetContextStructuredCloneCallbacks(cx); + + if (runtimeCallbacks) { + return runtimeCallbacks->read(cx, reader, tag, data, nullptr); + } + + return nullptr; +} + +static JSBool +PostMessageWriteStructuredClone(JSContext* cx, + JSStructuredCloneWriter* writer, + JS::Handle obj, + void *closure) +{ + StructuredCloneInfo* scInfo = static_cast(closure); + NS_ASSERTION(scInfo, "Must have scInfo!"); + + nsCOMPtr wrappedNative; + nsContentUtils::XPConnect()-> + GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative)); + if (wrappedNative) { + uint32_t scTag = 0; + nsISupports* supports = wrappedNative->Native(); + + nsCOMPtr blob = do_QueryInterface(supports); + if (blob) { + scTag = SCTAG_DOM_BLOB; + } + + nsCOMPtr list = do_QueryInterface(supports); + if (list) { + scTag = SCTAG_DOM_FILELIST; + } + + if (scTag) { + return JS_WriteUint32Pair(writer, scTag, 0) && + JS_WriteBytes(writer, &supports, sizeof(supports)) && + scInfo->mEvent->StoreISupports(supports); + } + } + + MessagePort* port = nullptr; + nsresult rv = mozilla::dom::UnwrapObject(cx, obj, port); + if (NS_SUCCEEDED(rv)) { + nsRefPtr newPort = port->Clone(scInfo->mPort->GetOwner()); + + return JS_WriteUint32Pair(writer, SCTAG_DOM_MESSAGEPORT, 0) && + JS_WriteBytes(writer, &newPort, sizeof(newPort)) && + scInfo->mEvent->StoreISupports(newPort); + } + + const JSStructuredCloneCallbacks* runtimeCallbacks = + js::GetContextStructuredCloneCallbacks(cx); + + if (runtimeCallbacks) { + return runtimeCallbacks->write(cx, writer, obj, nullptr); + } + + return false; +} + +JSStructuredCloneCallbacks kPostMessageCallbacks = { + PostMessageReadStructuredClone, + PostMessageWriteStructuredClone, + nullptr +}; + +} // anonymous namespace + +NS_IMETHODIMP +PostMessageRunnable::Run() +{ + nsresult rv; + MOZ_ASSERT(mPort); + + // Ensure that the buffer is freed even if we fail to post the message + JSAutoStructuredCloneBuffer buffer; + buffer.adopt(mMessage, mMessageLen); + mMessage = nullptr; + mMessageLen = 0; + + // Get the JSContext for the target window + nsCOMPtr sgo = do_QueryInterface(mPort->GetOwner()); + NS_ENSURE_STATE(sgo); + nsCOMPtr scriptContext = sgo->GetContext(); + AutoPushJSContext cx(scriptContext ? scriptContext->GetNativeContext() + : nsContentUtils::GetSafeJSContext()); + + MOZ_ASSERT(cx); + + // Deserialize the structured clone data + JS::Rooted messageData(cx); + { + StructuredCloneInfo scInfo; + scInfo.mEvent = this; + scInfo.mPort = mPort; + + if (!buffer.read(cx, messageData.address(), &kPostMessageCallbacks, + &scInfo)) { + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + } + + // Create the event + nsIDocument* doc = mPort->GetOwner()->GetExtantDoc(); + if (!doc) { + return NS_OK; + } + + ErrorResult error; + nsRefPtr event = + doc->CreateEvent(NS_LITERAL_STRING("MessageEvent"), error); + if (error.Failed()) { + return NS_OK; + } + + nsCOMPtr message = do_QueryInterface(event); + rv = message->InitMessageEvent(NS_LITERAL_STRING("message"), + false /* non-bubbling */, + true /* cancelable */, + messageData, + EmptyString(), + EmptyString(), + mPort->GetOwner()); + if (NS_FAILED(rv)) { + return NS_OK; + } + + message->SetTrusted(true); + + bool status; + mPort->DispatchEvent(event, &status); + return status ? NS_OK : NS_ERROR_FAILURE; +} + +//NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort, + nsDOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntangledPort) + + // Custom unlink loop because this array contains nsRunnable objects + // which are not cycle colleactable. + while (!tmp->mMessageQueue.IsEmpty()) { + NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mPort); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mSupportsArray); + tmp->mMessageQueue.RemoveElementAt(0); + } + + if (tmp->mDispatchRunnable) { + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDispatchRunnable->mPort); + } + +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort, + nsDOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntangledPort) + + // Custom unlink loop because this array contains nsRunnable objects + // which are not cycle colleactable. + for (uint32_t i = 0, len = tmp->mMessageQueue.Length(); i < len; ++i) { + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mPort); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mSupportsArray); + } + + if (tmp->mDispatchRunnable) { + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDispatchRunnable->mPort); + } + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort) +NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(MessagePort, nsDOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(MessagePort, nsDOMEventTargetHelper) + +MessagePort::MessagePort(nsPIDOMWindow* aWindow) + : nsDOMEventTargetHelper(aWindow) + , mMessageQueueEnabled(false) +{ + MOZ_COUNT_CTOR(MessagePort); + SetIsDOMBinding(); +} + +MessagePort::~MessagePort() +{ + MOZ_COUNT_DTOR(MessagePort); + Close(); +} + +JSObject* +MessagePort::WrapObject(JSContext* aCx, JS::Handle aScope) +{ + return MessagePortBinding::Wrap(aCx, aScope, this); +} + +void +MessagePort::PostMessage(JSContext* aCx, JS::Handle aMessage, + const Optional >& aTransfer, + ErrorResult& aRv) +{ + nsRefPtr event = new PostMessageRunnable(); + + // We *must* clone the data here, or the JS::Value could be modified + // by script + JSAutoStructuredCloneBuffer buffer; + StructuredCloneInfo scInfo; + scInfo.mEvent = event; + scInfo.mPort = this; + + JS::Handle transferable = aTransfer.WasPassed() + ? aTransfer.Value() + : JS::UndefinedHandleValue; + + if (!buffer.write(aCx, aMessage, transferable, &kPostMessageCallbacks, + &scInfo)) { + aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); + return; + } + + event->SetJSData(buffer); + + if (!mEntangledPort) { + return; + } + + mEntangledPort->mMessageQueue.AppendElement(event); + mEntangledPort->Dispatch(); +} + +void +MessagePort::Start() +{ + if (mMessageQueueEnabled) { + return; + } + + mMessageQueueEnabled = true; + Dispatch(); +} + +void +MessagePort::Dispatch() +{ + if (!mMessageQueueEnabled || mMessageQueue.IsEmpty() || mDispatchRunnable) { + return; + } + + nsRefPtr event = mMessageQueue.ElementAt(0); + mMessageQueue.RemoveElementAt(0); + + event->Dispatch(this); + + mDispatchRunnable = new DispatchEventRunnable(this); + NS_DispatchToCurrentThread(mDispatchRunnable); +} + +void +MessagePort::Close() +{ + if (!mEntangledPort) { + return; + } + + // This avoids loops. + nsRefPtr port = mEntangledPort; + mEntangledPort = nullptr; + + // Let's disentangle the 2 ports symmetrically. + port->Close(); +} + +EventHandlerNonNull* +MessagePort::GetOnmessage() +{ + if (NS_IsMainThread()) { + return GetEventHandler(nsGkAtoms::onmessage, EmptyString()); + } + return GetEventHandler(nullptr, NS_LITERAL_STRING("message")); +} + +void +MessagePort::SetOnmessage(EventHandlerNonNull* aCallback, ErrorResult& aRv) +{ + if (NS_IsMainThread()) { + SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback, aRv); + } else { + SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback, aRv); + } + + // When using onmessage, the call to start() is implied. + Start(); +} + +void +MessagePort::Entangle(MessagePort* aMessagePort) +{ + MOZ_ASSERT(aMessagePort); + MOZ_ASSERT(aMessagePort != this); + + Close(); + + mEntangledPort = aMessagePort; +} + +already_AddRefed +MessagePort::Clone(nsPIDOMWindow* aWindow) +{ + nsRefPtr newPort = new MessagePort(aWindow->GetCurrentInnerWindow()); + + // Move all the events in the port message queue of original port. + newPort->mMessageQueue.SwapElements(mMessageQueue); + + if (mEntangledPort) { + nsRefPtr port = mEntangledPort; + mEntangledPort = nullptr; + + newPort->Entangle(port); + port->Entangle(newPort); + } + + return newPort.forget(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/base/MessagePort.h b/dom/base/MessagePort.h new file mode 100644 index 000000000..09afd1e30 --- /dev/null +++ b/dom/base/MessagePort.h @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_MessagePort_h +#define mozilla_dom_MessagePort_h + +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "nsDOMEventTargetHelper.h" + +class nsPIDOMWindow; + +namespace mozilla { +namespace dom { + +class DispatchEventRunnable; +class PostMessageRunnable; + +class MessagePort MOZ_FINAL : public nsDOMEventTargetHelper +{ + friend class DispatchEventRunnable; + friend class PostMessageRunnable; + +public: + NS_DECL_ISUPPORTS_INHERITED + NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper) + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort, + nsDOMEventTargetHelper) + + MessagePort(nsPIDOMWindow* aWindow); + ~MessagePort(); + + virtual JSObject* + WrapObject(JSContext* aCx, JS::Handle aScope) MOZ_OVERRIDE; + + void + PostMessage(JSContext* aCx, JS::Handle aMessage, + const Optional >& aTransfer, + ErrorResult& aRv); + + void + Start(); + + void + Close(); + + // The 'message' event handler has to call |Start()| method, so we + // cannot use IMPL_EVENT_HANDLER macro here. + EventHandlerNonNull* + GetOnmessage(); + + void + SetOnmessage(EventHandlerNonNull* aCallback, ErrorResult& aRv); + + // Non WebIDL methods + + // This method entangles this MessagePort with another one. + // If it is already entangled, it's disentangled first and enatangle to the + // new one. + void + Entangle(MessagePort* aMessagePort); + + // Duplicate this message port. This method is used by the Structured Clone + // Algorithm and makes the new MessagePort active with the entangled + // MessagePort of this object. + already_AddRefed + Clone(nsPIDOMWindow* aWindow); + +private: + // Dispatch events from the Message Queue using a nsRunnable. + void Dispatch(); + + nsRefPtr mDispatchRunnable; + + nsRefPtr mEntangledPort; + + nsTArray > mMessageQueue; + bool mMessageQueueEnabled; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_MessagePort_h diff --git a/dom/base/StructuredCloneTags.h b/dom/base/StructuredCloneTags.h index 1af0dfad6..6ecec2ff3 100644 --- a/dom/base/StructuredCloneTags.h +++ b/dom/base/StructuredCloneTags.h @@ -28,6 +28,7 @@ enum StructuredCloneTags { // These tags are used for both main thread and workers. SCTAG_DOM_IMAGEDATA, + SCTAG_DOM_MESSAGEPORT, SCTAG_DOM_MAX }; diff --git a/dom/base/moz.build b/dom/base/moz.build index 082d917c6..d2707d47e 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -60,6 +60,8 @@ EXPORTS.mozilla.dom += [ 'DOMCursor.h', 'DOMError.h', 'DOMRequest.h', + 'MessageChannel.h', + 'MessagePort.h', 'ScreenOrientation.h', 'StructuredCloneTags.h', 'URL.h', @@ -72,6 +74,8 @@ CPP_SOURCES += [ 'DOMError.cpp', 'DOMRequest.cpp', 'Navigator.cpp', + 'MessageChannel.cpp', + 'MessagePort.cpp', 'URL.cpp', 'nsContentPermissionHelper.cpp', 'nsDOMClassInfo.cpp', diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 8e58d97a1..fa9439485 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -210,6 +210,9 @@ #include "prlog.h" #include "prenv.h" +#include "mozilla/dom/MessageChannel.h" +#include "mozilla/dom/MessagePort.h" +#include "mozilla/dom/MessagePortBinding.h" #include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/dom/quota/QuotaManager.h" @@ -6667,6 +6670,7 @@ namespace { struct StructuredCloneInfo { PostMessageEvent* event; bool subsumes; + nsPIDOMWindow* window; }; static JSObject* @@ -6696,6 +6700,21 @@ PostMessageReadStructuredClone(JSContext* cx, } } + if (MessageChannel::PrefEnabled() && tag == SCTAG_DOM_MESSAGEPORT) { + NS_ASSERTION(!data, "Data should be empty"); + + MessagePort* port; + if (JS_ReadBytes(reader, &port, sizeof(port))) { + JS::Rooted global(cx, JS_GetGlobalForScopeChain(cx)); + if (global) { + JS::Rooted obj(cx, port->WrapObject(cx, global)); + if (JS_WrapObject(cx, obj.address())) { + return obj; + } + } + } + } + const JSStructuredCloneCallbacks* runtimeCallbacks = js::GetContextStructuredCloneCallbacks(cx); @@ -6736,6 +6755,18 @@ PostMessageWriteStructuredClone(JSContext* cx, scInfo->event->StoreISupports(supports); } + if (MessageChannel::PrefEnabled()) { + MessagePort* port = nullptr; + nsresult rv = mozilla::dom::UnwrapObject(cx, obj, port); + if (NS_SUCCEEDED(rv) && scInfo->subsumes) { + nsRefPtr newPort = port->Clone(scInfo->window); + + return JS_WriteUint32Pair(writer, SCTAG_DOM_MESSAGEPORT, 0) && + JS_WriteBytes(writer, &newPort, sizeof(newPort)) && + scInfo->event->StoreISupports(newPort); + } + } + const JSStructuredCloneCallbacks* runtimeCallbacks = js::GetContextStructuredCloneCallbacks(cx); @@ -6827,6 +6858,7 @@ PostMessageEvent::Run() { StructuredCloneInfo scInfo; scInfo.event = this; + scInfo.window = targetWindow; if (!buffer.read(cx, messageData.address(), &kPostMessageCallbacks, &scInfo)) { @@ -6973,6 +7005,7 @@ nsGlobalWindow::PostMessageMoz(const JS::Value& aMessage, JSAutoStructuredCloneBuffer buffer; StructuredCloneInfo scInfo; scInfo.event = event; + scInfo.window = this; nsIPrincipal* principal = GetPrincipal(); if (NS_FAILED(callerPrin->Subsumes(principal, &scInfo.subsumes))) diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 613dd2bee..e25f00411 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -715,14 +715,16 @@ public: mozilla::dom::EventHandlerNonNull* GetOn##name_() \ { \ nsEventListenerManager *elm = GetListenerManager(false); \ - return elm ? elm->GetEventHandler(nsGkAtoms::on##name_) : nullptr; \ + return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString()) \ + : nullptr; \ } \ void SetOn##name_(mozilla::dom::EventHandlerNonNull* handler, \ mozilla::ErrorResult& error) \ { \ nsEventListenerManager *elm = GetListenerManager(true); \ if (elm) { \ - error = elm->SetEventHandler(nsGkAtoms::on##name_, handler); \ + error = elm->SetEventHandler(nsGkAtoms::on##name_, EmptyString(), \ + handler); \ } else { \ error.Throw(NS_ERROR_OUT_OF_MEMORY); \ } \ diff --git a/dom/base/test/Makefile.in b/dom/base/test/Makefile.in index a01ef0b56..7eddd73c5 100644 --- a/dom/base/test/Makefile.in +++ b/dom/base/test/Makefile.in @@ -31,6 +31,17 @@ MOCHITEST_FILES = \ test_Image_constructor.html \ test_setting_opener.html \ test_error.html \ + test_messageChannel.html \ + test_messageChannel_cloning.html \ + iframe_messageChannel_cloning.html \ + test_messageChannel_post.html \ + iframe_messageChannel_post.html \ + test_messageChannel_transferable.html \ + test_messageChannel_start.html \ + test_messageChannel_pingpong.html \ + iframe_messageChannel_pingpong.html \ + test_messageChannel_unshipped.html \ + test_messageChannel_pref.html \ test_url.html \ $(NULL) diff --git a/dom/base/test/iframe_messageChannel_cloning.html b/dom/base/test/iframe_messageChannel_cloning.html new file mode 100644 index 000000000..ee53e855a --- /dev/null +++ b/dom/base/test/iframe_messageChannel_cloning.html @@ -0,0 +1,22 @@ + + + + + + + diff --git a/dom/base/test/iframe_messageChannel_pingpong.html b/dom/base/test/iframe_messageChannel_pingpong.html new file mode 100644 index 000000000..2cd0b79da --- /dev/null +++ b/dom/base/test/iframe_messageChannel_pingpong.html @@ -0,0 +1,33 @@ + + + + + + + diff --git a/dom/base/test/iframe_messageChannel_post.html b/dom/base/test/iframe_messageChannel_post.html new file mode 100644 index 000000000..e551dd61c --- /dev/null +++ b/dom/base/test/iframe_messageChannel_post.html @@ -0,0 +1,24 @@ + + + + + + + diff --git a/dom/base/test/test_messageChannel.html b/dom/base/test/test_messageChannel.html new file mode 100644 index 000000000..dbb857660 --- /dev/null +++ b/dom/base/test/test_messageChannel.html @@ -0,0 +1,49 @@ + + + + + + + Test for Bug 677638 - basic support + + + + +Mozilla Bug 677638 +

+ +
+
+ + + diff --git a/dom/base/test/test_messageChannel_cloning.html b/dom/base/test/test_messageChannel_cloning.html new file mode 100644 index 000000000..8f8958303 --- /dev/null +++ b/dom/base/test/test_messageChannel_cloning.html @@ -0,0 +1,70 @@ + + + + + + Test for Bug 677638 - port cloning + + + + +Mozilla Bug 677638 +
+
+
+ + + diff --git a/dom/base/test/test_messageChannel_pingpong.html b/dom/base/test/test_messageChannel_pingpong.html new file mode 100644 index 000000000..372e87b07 --- /dev/null +++ b/dom/base/test/test_messageChannel_pingpong.html @@ -0,0 +1,77 @@ + + + + + + Test for Bug 677638 - port cloning + + + + +Mozilla Bug 677638 +
+
+
+ + + diff --git a/dom/base/test/test_messageChannel_post.html b/dom/base/test/test_messageChannel_post.html new file mode 100644 index 000000000..b9517c22a --- /dev/null +++ b/dom/base/test/test_messageChannel_post.html @@ -0,0 +1,72 @@ + + + + + + Test for Bug 677638 - port cloning + + + + +Mozilla Bug 677638 +
+
+
+ + + diff --git a/dom/base/test/test_messageChannel_pref.html b/dom/base/test/test_messageChannel_pref.html new file mode 100644 index 000000000..5fe30aa52 --- /dev/null +++ b/dom/base/test/test_messageChannel_pref.html @@ -0,0 +1,42 @@ + + + + + + Test for Bug 677638 - pref + + + + +Mozilla Bug 677638 +
+
+
+ + + diff --git a/dom/base/test/test_messageChannel_start.html b/dom/base/test/test_messageChannel_start.html new file mode 100644 index 000000000..b739a732a --- /dev/null +++ b/dom/base/test/test_messageChannel_start.html @@ -0,0 +1,234 @@ + + + + + + Test for Bug 677638 - start/close + + + + +Mozilla Bug 677638 +
+
+
+ + + diff --git a/dom/base/test/test_messageChannel_transferable.html b/dom/base/test/test_messageChannel_transferable.html new file mode 100644 index 000000000..2750d5324 --- /dev/null +++ b/dom/base/test/test_messageChannel_transferable.html @@ -0,0 +1,67 @@ + + + + + + Test for Bug 677638 - port cloning + + + + +Mozilla Bug 677638 +
+
+
+ + + diff --git a/dom/base/test/test_messageChannel_unshipped.html b/dom/base/test/test_messageChannel_unshipped.html new file mode 100644 index 000000000..60c8070fa --- /dev/null +++ b/dom/base/test/test_messageChannel_unshipped.html @@ -0,0 +1,123 @@ + + + + + + Test for Bug 677638 - unshipped message port queue + + + + +Mozilla Bug 677638 +
+
+
+ + + diff --git a/dom/webidl/MessageChannel.webidl b/dom/webidl/MessageChannel.webidl new file mode 100644 index 000000000..c5ef60d07 --- /dev/null +++ b/dom/webidl/MessageChannel.webidl @@ -0,0 +1,14 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For more information on this interface, please see + * http://www.whatwg.org/specs/web-apps/current-work/#channel-messaging + */ + +[Constructor, Pref="dom.messageChannel.enabled"] +interface MessageChannel { + readonly attribute MessagePort port1; + readonly attribute MessagePort port2; +}; diff --git a/dom/webidl/MessagePort.webidl b/dom/webidl/MessagePort.webidl new file mode 100644 index 000000000..797de75be --- /dev/null +++ b/dom/webidl/MessagePort.webidl @@ -0,0 +1,23 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For more information on this interface, please see + * http://www.whatwg.org/specs/web-apps/current-work/#channel-messaging + */ + +[Pref="dom.messageChannel.enabled"] +interface MessagePort : EventTarget { + // TODO void postMessage(any message, optional sequence transfer); + [Throws] + void postMessage(any message, optional any transfer); + + void start(); + void close(); + + // event handlers + [SetterThrows] + attribute EventHandler onmessage; +}; +// MessagePort implements Transferable; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index f82d7756e..baf3f7543 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -176,7 +176,9 @@ webidl_files = \ MediaStreamAudioDestinationNode.webidl \ MediaStreamEvent.webidl \ MediaStreamTrack.webidl \ + MessageChannel.webidl \ MessageEvent.webidl \ + MessagePort.webidl \ MobileMessageManager.webidl \ MouseEvent.webidl \ MouseScrollEvent.webidl \ diff --git a/widget/nsGUIEvent.h b/widget/nsGUIEvent.h index 400ea4d0c..8817c52d8 100644 --- a/widget/nsGUIEvent.h +++ b/widget/nsGUIEvent.h @@ -681,6 +681,9 @@ public: // Additional type info for user defined events nsCOMPtr userType; + + nsString typeString; // always set on non-main-thread events + // Event targets, needed by DOM Events nsCOMPtr target; nsCOMPtr currentTarget;