diff --git a/dom/chrome-webidl/JSWindowActor.webidl b/dom/chrome-webidl/JSWindowActor.webidl index afeeecd57fd6..55b3e489014b 100644 --- a/dom/chrome-webidl/JSWindowActor.webidl +++ b/dom/chrome-webidl/JSWindowActor.webidl @@ -119,6 +119,20 @@ dictionary WindowActorSidedOptions { required ByteString moduleURI; }; +dictionary WindowActorEventListenerOptions : AddEventListenerOptions { + /** + * If this attribute is set to true (the default), this event will cause the + * actor to be created when it is fired. If the attribute is set false, the + * actor will not receive the event unless it had already been created through + * some other mechanism. + * + * This should be set to `false` for event listeners which are only intended + * to perform cleanup operations, and will have no effect if the actor doesn't + * already exist. + */ + boolean createActor = true; +}; + dictionary WindowActorChildOptions : WindowActorSidedOptions { /** * Events which this actor wants to be listening to. When these events fire, @@ -129,7 +143,7 @@ dictionary WindowActorChildOptions : WindowActorSidedOptions { * NOTE: `once` option is not support due to we register listeners in a shared * location. */ - record events; + record events; /** * An array of observer topics to listen to. An observer will be added for each diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 622c877e9ebc..7e4c3667f480 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -281,6 +281,7 @@ struct JSWindowActorEventDecl bool systemGroup; bool allowUntrusted; bool? passive; + bool createActor; }; struct JSWindowActorInfo diff --git a/dom/ipc/jsactor/JSWindowActorProtocol.cpp b/dom/ipc/jsactor/JSWindowActorProtocol.cpp index 4d18b02116a9..6b8fc35cb7aa 100644 --- a/dom/ipc/jsactor/JSWindowActorProtocol.cpp +++ b/dom/ipc/jsactor/JSWindowActorProtocol.cpp @@ -53,6 +53,7 @@ JSWindowActorProtocol::FromIPC(const JSWindowActorInfo& aInfo) { if (ipc.passive()) { event->mPassive.Construct(ipc.passive().value()); } + event->mCreateActor = ipc.createActor(); } proto->mChild.mObservers = aInfo.observers().Clone(); @@ -80,6 +81,7 @@ JSWindowActorInfo JSWindowActorProtocol::ToIPC() { if (event.mPassive.WasPassed()) { ipc->passive() = Some(event.mPassive.Value()); } + ipc->createActor() = event.mCreateActor; } info.observers() = mChild.mObservers.Clone(); @@ -151,6 +153,7 @@ JSWindowActorProtocol::FromWebIDLOptions(const nsACString& aName, if (entry.mValue.mPassive.WasPassed()) { evt->mPassive.Construct(entry.mValue.mPassive.Value()); } + evt->mCreateActor = entry.mValue.mCreateActor; } } @@ -188,15 +191,33 @@ NS_IMETHODIMP JSWindowActorProtocol::HandleEvent(Event* aEvent) { } // Ensure our actor is present. - AutoJSAPI jsapi; - jsapi.Init(); - RefPtr actor = wgc->GetActor(jsapi.cx(), mName, IgnoreErrors()); + RefPtr actor = wgc->GetExistingActor(mName); + if (!actor) { + // Check if we're supposed to create the actor when this event is fired. + bool createActor = true; + nsAutoString typeStr; + aEvent->GetType(typeStr); + for (auto& event : mChild.mEvents) { + if (event.mName == typeStr) { + createActor = event.mCreateActor; + break; + } + } + + // If we're supposed to create the actor, call GetActor to cause it to be + // created. + if (createActor) { + AutoJSAPI jsapi; + jsapi.Init(); + actor = wgc->GetActor(jsapi.cx(), mName, IgnoreErrors()); + } + } if (!actor || NS_WARN_IF(!actor->GetWrapperPreserveColor())) { return NS_OK; } // Build our event listener & call it. - JS::Rooted global(jsapi.cx(), + JS::Rooted global(RootingCx(), JS::GetNonCCWObjectGlobal(actor->GetWrapper())); RefPtr eventListener = new EventListener(actor->GetWrapper(), global, nullptr, nullptr); diff --git a/dom/ipc/jsactor/JSWindowActorProtocol.h b/dom/ipc/jsactor/JSWindowActorProtocol.h index 993e6b4e6ed3..e39babdc3434 100644 --- a/dom/ipc/jsactor/JSWindowActorProtocol.h +++ b/dom/ipc/jsactor/JSWindowActorProtocol.h @@ -56,6 +56,7 @@ class JSWindowActorProtocol final : public JSActorProtocol, nsString mName; EventListenerFlags mFlags; Optional mPassive; + bool mCreateActor = true; }; struct ChildSide : public Sided {