From 3b972f7f7ab28f239b6166bec0977d8c9812fe17 Mon Sep 17 00:00:00 2001 From: Felipe Gomes Date: Tue, 30 Oct 2018 21:47:04 +0000 Subject: [PATCH] Bug 1493984 - Implement Fission-aware message handling to actors. r=mconley This patch adds a messaging mechanism to actors that work like this: If browser.fission.simulate is true, messages handled by ActorManagerChild will only be dispatched to an actor tied to a specific frame, specified either by its frameId (outerWindowId) or by its browsingContextId. Messages that lack the information about the destination frame will be considered meant for the top-level frame. In addition, a sendMessage API is added to ActorChild that automatically adds the id information to the outbound message, making it easier for it to be handled in the parent. The frameId support is to ease the transition of some code that is already using outerWindowIds. For new code, it is preferred to use the browsing context ids, together with bug 1496840 Differential Revision: https://phabricator.services.mozilla.com/D7933 --HG-- extra : moz-landing-system : lando --- toolkit/modules/ActorChild.jsm | 12 ++++- toolkit/modules/ActorManagerChild.jsm | 69 ++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/toolkit/modules/ActorChild.jsm b/toolkit/modules/ActorChild.jsm index 629bbdde5a85..27576747aea1 100644 --- a/toolkit/modules/ActorChild.jsm +++ b/toolkit/modules/ActorChild.jsm @@ -23,7 +23,7 @@ class ActorChild { } get content() { - return this._dispatcher.window || this.mm.content; + return this._dispatcher.window; } get docShell() { @@ -34,6 +34,16 @@ class ActorChild { this._dispatcher.addEventListener(event, this.constructor.name, options); } + addMessageListener(msg) { + this._dispatcher.addMessageListener(msg, this.constructor.name); + } + + sendAsyncMessage(msg, data = {}) { + data.frameId = this._dispatcher.frameId; + data.browsingContextId = this._dispatcher.browsingContextId; + this.mm.sendAsyncMessage(msg, data); + } + cleanup() { this._dispatcher = null; } diff --git a/toolkit/modules/ActorManagerChild.jsm b/toolkit/modules/ActorManagerChild.jsm index 30560c75619c..fdfd58435ec9 100644 --- a/toolkit/modules/ActorManagerChild.jsm +++ b/toolkit/modules/ActorManagerChild.jsm @@ -16,6 +16,9 @@ ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm"); ChromeUtils.import("resource://gre/modules/Services.jsm"); ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +ChromeUtils.defineModuleGetter(this, "WebNavigationFrames", + "resource://gre/modules/WebNavigationFrames.jsm"); + const {DefaultMap} = ExtensionUtils; const {sharedData} = Services.cpmm; @@ -41,6 +44,9 @@ class Dispatcher { init() { for (let msg of this.messages.keys()) { + // This is directly called on the message manager + // because this.addMessageListener is meant to handle + // additions after initialization. this.mm.addMessageListener(msg, this); } for (let topic of this.observers.keys()) { @@ -59,11 +65,41 @@ class Dispatcher { } } + get window() { + return this.mm.content; + } + + get frameId() { + // 0 for top-level windows, outerWindowId otherwise + return WebNavigationFrames.getFrameId(this.window); + } + + get browsingContextId() { + return this.window.docShell.browsingContext.id; + } + addEventListener(event, actor, options) { let listener = this.handleActorEvent.bind(this, actor); this.mm.addEventListener(event, listener, options); } + addMessageListener(msg, actor) { + let actors = this.messages.get(msg); + + if (!actors) { + actors = []; + this.messages.set(msg, actors); + } + + if (actors.length == 0) { + this.mm.addMessageListener(msg, this); + } + + if (!actors.includes(actor)) { + actors.push(actor); + } + } + getActor(actorName) { let inst = this.instances.get(actorName); if (!inst) { @@ -89,9 +125,7 @@ class Dispatcher { if (simulateFission) { targetWindow = event.target.ownerGlobal; - let dispatcherWindow = this.window || this.mm.content; - - if (targetWindow != dispatcherWindow) { + if (targetWindow != this.window) { // events can't propagate across frame boundaries because the // frames will be hosted on separated processes. return; @@ -102,15 +136,32 @@ class Dispatcher { receiveMessage(message) { let actors = this.messages.get(message.name); - let result; + + if (simulateFission) { + let match = false; + let data = message.data || {}; + if (data.hasOwnProperty("frameId")) { + match = (data.frameId == this.frameId); + } else if (data.hasOwnProperty("browsingContextId")) { + match = (data.browsingContextId == this.browsingContextId); + } else { + // if no specific target was given, just dispatch it to + // top-level actors. + match = (this.frameId == 0); + } + + if (!match) { + return; + } + } + for (let actor of actors) { try { - result = this.getActor(actor).receiveMessage(message); + this.getActor(actor).receiveMessage(message); } catch (e) { Cu.reportError(e); } } - return result; } observe(subject, topic, data) { @@ -136,7 +187,7 @@ class SingletonDispatcher extends Dispatcher { window.addEventListener("pageshow", this, {mozSystemGroup: true}); window.addEventListener("pagehide", this, {mozSystemGroup: true}); - this.window = window; + this._window = window; this.listeners = []; } @@ -175,6 +226,10 @@ class SingletonDispatcher extends Dispatcher { this.listeners = []; } + get window() { + return this._window; + } + handleEvent(event) { if (event.type == "pageshow") { if (this.hidden) {