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
This commit is contained in:
Felipe Gomes 2018-10-30 21:47:04 +00:00
Родитель 51817cd36d
Коммит 3b972f7f7a
2 изменённых файлов: 73 добавлений и 8 удалений

Просмотреть файл

@ -23,7 +23,7 @@ class ActorChild {
} }
get content() { get content() {
return this._dispatcher.window || this.mm.content; return this._dispatcher.window;
} }
get docShell() { get docShell() {
@ -34,6 +34,16 @@ class ActorChild {
this._dispatcher.addEventListener(event, this.constructor.name, options); 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() { cleanup() {
this._dispatcher = null; this._dispatcher = null;
} }

Просмотреть файл

@ -16,6 +16,9 @@ ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm"); ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "WebNavigationFrames",
"resource://gre/modules/WebNavigationFrames.jsm");
const {DefaultMap} = ExtensionUtils; const {DefaultMap} = ExtensionUtils;
const {sharedData} = Services.cpmm; const {sharedData} = Services.cpmm;
@ -41,6 +44,9 @@ class Dispatcher {
init() { init() {
for (let msg of this.messages.keys()) { 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); this.mm.addMessageListener(msg, this);
} }
for (let topic of this.observers.keys()) { 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) { addEventListener(event, actor, options) {
let listener = this.handleActorEvent.bind(this, actor); let listener = this.handleActorEvent.bind(this, actor);
this.mm.addEventListener(event, listener, options); 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) { getActor(actorName) {
let inst = this.instances.get(actorName); let inst = this.instances.get(actorName);
if (!inst) { if (!inst) {
@ -89,9 +125,7 @@ class Dispatcher {
if (simulateFission) { if (simulateFission) {
targetWindow = event.target.ownerGlobal; targetWindow = event.target.ownerGlobal;
let dispatcherWindow = this.window || this.mm.content; if (targetWindow != this.window) {
if (targetWindow != dispatcherWindow) {
// events can't propagate across frame boundaries because the // events can't propagate across frame boundaries because the
// frames will be hosted on separated processes. // frames will be hosted on separated processes.
return; return;
@ -102,15 +136,32 @@ class Dispatcher {
receiveMessage(message) { receiveMessage(message) {
let actors = this.messages.get(message.name); 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) { for (let actor of actors) {
try { try {
result = this.getActor(actor).receiveMessage(message); this.getActor(actor).receiveMessage(message);
} catch (e) { } catch (e) {
Cu.reportError(e); Cu.reportError(e);
} }
} }
return result;
} }
observe(subject, topic, data) { observe(subject, topic, data) {
@ -136,7 +187,7 @@ class SingletonDispatcher extends Dispatcher {
window.addEventListener("pageshow", this, {mozSystemGroup: true}); window.addEventListener("pageshow", this, {mozSystemGroup: true});
window.addEventListener("pagehide", this, {mozSystemGroup: true}); window.addEventListener("pagehide", this, {mozSystemGroup: true});
this.window = window; this._window = window;
this.listeners = []; this.listeners = [];
} }
@ -175,6 +226,10 @@ class SingletonDispatcher extends Dispatcher {
this.listeners = []; this.listeners = [];
} }
get window() {
return this._window;
}
handleEvent(event) { handleEvent(event) {
if (event.type == "pageshow") { if (event.type == "pageshow") {
if (this.hidden) { if (this.hidden) {