--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -9,27 +9,27 @@
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "E10SUtils",
"resource://gre/modules/E10SUtils.jsm");
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
"resource://gre/modules/BrowserUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "Utils",
- "resource://gre/modules/sessionstore/Utils.jsm");
-ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
- "resource://gre/modules/PrivateBrowsingUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AboutReader",
"resource://gre/modules/AboutReader.jsm");
ChromeUtils.defineModuleGetter(this, "ReaderMode",
"resource://gre/modules/ReaderMode.jsm");
ChromeUtils.defineModuleGetter(this, "PageStyleHandler",
"resource:///modules/PageStyleHandler.jsm");
+ChromeUtils.import("resource://gre/modules/ActorManagerChild.jsm");
+
+ActorManagerChild.attach(this, "browsers");
+
// TabChildGlobal
var global = this;
addEventListener("MozDOMPointerLock:Entered", function(aEvent) {
sendAsyncMessage("PointerLock:Entered", {
originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix
});
--- a/browser/base/content/test/performance/browser_startup.js
+++ b/browser/base/content/test/performance/browser_startup.js
@@ -31,16 +31,18 @@ const startupPhases = {
"MainProcessSingleton.js",
// Bugs to fix: The following components shouldn't be initialized that early.
"nsSessionStartup.js", // bug 1369456
"PushComponents.js", // bug 1369436
]),
modules: new Set([
"resource://gre/modules/AppConstants.jsm",
+ "resource://gre/modules/ActorManagerParent.jsm",
+ "resource://gre/modules/ExtensionUtils.jsm",
"resource://gre/modules/XPCOMUtils.jsm",
"resource://gre/modules/Services.jsm",
])
}},
// For the following phases of startup we have only a black list for now
// We are at this phase after creating the first browser window (ie. after final-ui-startup).
--- a/browser/base/content/test/performance/browser_startup_content.js
+++ b/browser/base/content/test/performance/browser_startup_content.js
@@ -46,16 +46,18 @@ const whitelist = {
// Forms and passwords
"resource://formautofill/FormAutofill.jsm",
"resource://formautofill/FormAutofillContent.jsm",
// Browser front-end
"resource:///modules/ContentLinkHandler.jsm",
"resource:///modules/ContentMetaHandler.jsm",
"resource:///modules/PageStyleHandler.jsm",
+ "resource://gre/modules/ActorChild.jsm",
+ "resource://gre/modules/ActorManagerChild.jsm",
"resource://gre/modules/BrowserUtils.jsm",
"resource://gre/modules/E10SUtils.jsm",
"resource://gre/modules/PrivateBrowsingUtils.jsm",
"resource://gre/modules/ReaderMode.jsm",
"resource://gre/modules/WebProgressChild.jsm",
"resource://gre/modules/WebNavigationChild.jsm",
// Pocket
--- a/browser/components/BrowserComponents.manifest
+++ b/browser/components/BrowserComponents.manifest
@@ -31,11 +31,9 @@ category command-line-validator b-browse
#
# browser: {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
# mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
component {eab9012e-5f74-4cbc-b2b5-a590235513cc} nsBrowserGlue.js
contract @mozilla.org/browser/browserglue;1 {eab9012e-5f74-4cbc-b2b5-a590235513cc}
category app-startup nsBrowserGlue service,@mozilla.org/browser/browserglue;1 application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110}
component {d8903bf6-68d5-4e97-bcd1-e4d3012f721a} nsBrowserGlue.js
-#ifndef MOZ_MULET
contract @mozilla.org/content-permission/prompt;1 {d8903bf6-68d5-4e97-bcd1-e4d3012f721a}
-#endif
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -61,21 +61,18 @@ if CONFIG['NIGHTLY_BUILD']:
DIRS += ['payments']
XPIDL_SOURCES += [
'nsIBrowserHandler.idl',
]
XPIDL_MODULE = 'browsercompsbase'
-EXTRA_PP_COMPONENTS += [
+EXTRA_COMPONENTS += [
'BrowserComponents.manifest',
-]
-
-EXTRA_COMPONENTS += [
'nsBrowserContentHandler.js',
'nsBrowserGlue.js',
'tests/startupRecorder.js',
'tests/testComponents.manifest',
]
EXTRA_JS_MODULES += [
'distribution.js',
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -3,16 +3,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+ChromeUtils.defineModuleGetter(this, "ActorManagerParent",
+ "resource://gre/modules/ActorManagerParent.jsm");
+
+let ACTORS = {
+};
+
(function earlyBlankFirstPaint() {
if (!Services.prefs.getBoolPref("browser.startup.blankWindow", false))
return;
let store = Services.xulStore;
let getValue = attr =>
store.getValue(AppConstants.BROWSER_CHROME_URL, "main-window", attr);
let width = getValue("width");
@@ -640,16 +646,19 @@ BrowserGlue.prototype = {
os.addObserver(this, "browser-search-engine-modified");
os.addObserver(this, "restart-in-safe-mode");
os.addObserver(this, "flash-plugin-hang");
os.addObserver(this, "xpi-signature-changed");
os.addObserver(this, "sync-ui-state:update");
os.addObserver(this, "handlersvc-store-initialized");
os.addObserver(this, "shield-init-complete");
+ ActorManagerParent.addActors(ACTORS);
+ ActorManagerParent.flush();
+
this._flashHangCount = 0;
this._firstWindowReady = new Promise(resolve => this._firstWindowLoaded = resolve);
if (AppConstants.platform == "win") {
JawsScreenReaderVersionCheck.init();
}
},
// cleanup (called on application shutdown)
--- a/dom/ipc/tests/test_sharedMap.js
+++ b/dom/ipc/tests/test_sharedMap.js
@@ -34,16 +34,25 @@ function getContents(sharedMap = Service
getValues: Array.from(sharedMap.keys(),
key => sharedMap.get(key)),
};
}
function checkMap(contents, expected) {
expected = Array.from(expected);
+ // Remove keys already defined by ActorManagerParent.jsm
+ for (let i = contents.keys.length - 1; i >= 0; i--) {
+ if (/^Child(Singleton)?Actors/.test(contents.keys[i])) {
+ contents.keys.splice(i, 1);
+ contents.values.splice(i, 1);
+ contents.entries.splice(i, 1);
+ }
+ }
+
equal(contents.keys.length, expected.length,
"Got correct number of keys");
equal(contents.values.length, expected.length,
"Got correct number of values");
equal(contents.entries.length, expected.length,
"Got correct number of entries");
for (let [i, [key, val]] of contents.entries.entries()) {
--- a/mobile/android/components/BrowserCLH.js
+++ b/mobile/android/components/BrowserCLH.js
@@ -1,16 +1,17 @@
/* 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/. */
"use strict";
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
+ ActorManagerParent: "resource://gre/modules/ActorManagerParent.jsm",
AppConstants: "resource://gre/modules/AppConstants.jsm",
DelayedInit: "resource://gre/modules/DelayedInit.jsm",
GeckoViewUtils: "resource://gre/modules/GeckoViewUtils.jsm",
Services: "resource://gre/modules/Services.jsm",
});
function BrowserCLH() {
this.wrappedJSObject = this;
@@ -36,16 +37,18 @@ BrowserCLH.prototype = {
observe: function(subject, topic, data) {
switch (topic) {
case "app-startup": {
this.setResourceSubstitutions();
Services.obs.addObserver(this, "chrome-document-interactive");
Services.obs.addObserver(this, "content-document-interactive");
+ ActorManagerParent.flush();
+
GeckoViewUtils.addLazyGetter(this, "DownloadNotifications", {
module: "resource://gre/modules/DownloadNotifications.jsm",
observers: ["chrome-document-loaded"],
once: true,
});
if (AppConstants.MOZ_WEBRTC) {
GeckoViewUtils.addLazyGetter(this, "WebrtcUI", {
--- a/mobile/android/components/geckoview/GeckoViewStartup.js
+++ b/mobile/android/components/geckoview/GeckoViewStartup.js
@@ -1,15 +1,16 @@
/* 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/. */
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
+ ActorManagerParent: "resource://gre/modules/ActorManagerParent.jsm",
FileSource: "resource://gre/modules/L10nRegistry.jsm",
GeckoViewTelemetryController: "resource://gre/modules/GeckoViewTelemetryController.jsm",
GeckoViewUtils: "resource://gre/modules/GeckoViewUtils.jsm",
L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
Services: "resource://gre/modules/Services.jsm",
});
const {debug, warn} = GeckoViewUtils.initLogging("GeckoViewStartup", this);
@@ -63,16 +64,18 @@ GeckoViewStartup.prototype = {
GeckoViewUtils.addLazyPrefObserver({
name: "geckoview.console.enabled",
default: false,
}, {
handler: _ => this.GeckoViewConsole,
});
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT) {
+ ActorManagerParent.flush();
+
// Parent process only.
this.setResourceSubstitutions();
Services.mm.loadFrameScript(
"chrome://geckoview/content/GeckoViewPromptContent.js", true);
GeckoViewUtils.addLazyGetter(this, "ContentCrashHandler", {
module: "resource://gre/modules/ContentCrashHandler.jsm",
--- a/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
+++ b/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
@@ -2,16 +2,17 @@
/* vim: set sts=2 sw=2 et tw=80: */
/* 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["ExtensionTestUtils"];
+ChromeUtils.import("resource://gre/modules/ActorManagerParent.jsm");
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
ChromeUtils.defineModuleGetter(this, "AddonTestUtils",
"resource://testing-common/AddonTestUtils.jsm");
ChromeUtils.defineModuleGetter(this, "ContentTask",
@@ -29,16 +30,20 @@ ChromeUtils.defineModuleGetter(this, "Se
ChromeUtils.defineModuleGetter(this, "TestUtils",
"resource://testing-common/TestUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "Management", () => {
const {Management} = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
return Management;
});
+Services.mm.loadFrameScript("chrome://global/content/browser-content.js", true);
+
+ActorManagerParent.flush();
+
/* exported ExtensionTestUtils */
const {
promiseDocumentLoaded,
promiseEvent,
promiseObserved,
} = ExtensionUtils;
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -4,16 +4,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint-env mozilla/frame-script */
/* eslint no-unused-vars: ["error", {args: "none"}] */
/* global sendAsyncMessage */
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/ActorManagerChild.jsm");
+
+ActorManagerChild.attach(this);
ChromeUtils.defineModuleGetter(this, "AutoCompletePopup",
"resource://gre/modules/AutoCompletePopupContent.jsm");
ChromeUtils.defineModuleGetter(this, "AutoScrollController",
"resource://gre/modules/AutoScrollController.jsm");
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
"resource://gre/modules/BrowserUtils.jsm");
ChromeUtils.defineModuleGetter(this, "SelectContentHelper",
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/ActorChild.jsm
@@ -0,0 +1,27 @@
+/* vim: set ts=2 sw=2 sts=2 et tw=80: */
+/* 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/. */
+"use strict";
+
+var EXPORTED_SYMBOLS = ["ActorChild"];
+
+/**
+ * This should be the base class of any actor class registered via
+ * ActorManagerParent and implemented in the child process. It currently takes
+ * care of setting the `mm`, `content`, and `docShell` properties based on the
+ * message manager it's bound to, but may do more in the future.
+ */
+class ActorChild {
+ constructor(mm) {
+ this.mm = mm;
+ }
+
+ get content() {
+ return this.mm.content;
+ }
+
+ get docShell() {
+ return this.mm.docShell;
+ }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/ActorManagerChild.jsm
@@ -0,0 +1,219 @@
+/* vim: set ts=2 sw=2 sts=2 et tw=80: */
+/* 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/. */
+"use strict";
+
+/**
+ * This module implements logic for creating JavaScript IPC actors, as defined
+ * in ActorManagerParent, for frame message manager contexts. See
+ * ActorManagerParent.jsm for more information.
+ */
+
+var EXPORTED_SYMBOLS = ["ActorManagerChild"];
+
+ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const {DefaultMap} = ExtensionUtils;
+
+const {sharedData} = Services.cpmm;
+
+function getMessageManager(window) {
+ return window.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIContentFrameMessageManager);
+}
+
+class Dispatcher {
+ constructor(mm, data) {
+ this.mm = mm;
+
+ this.actors = data.actors;
+ this.events = data.events;
+ this.messages = data.messages;
+ this.observers = data.observers;
+
+ this.instances = new Map();
+ }
+
+ init() {
+ for (let msg of this.messages.keys()) {
+ this.mm.addMessageListener(msg, this);
+ }
+ for (let topic of this.observers.keys()) {
+ Services.obs.addObserver(this, topic, true);
+ }
+ for (let {event, options, actor} of this.events) {
+ this.addEventListener(event, this.handleActorEvent.bind(this, actor), options);
+ }
+
+ this.mm.addEventListener("unload", this);
+ }
+
+ cleanup() {
+ for (let topic of this.observers.keys()) {
+ Services.obs.removeObserver(this, topic);
+ }
+ }
+
+ addEventListener(event, listener, options) {
+ this.mm.addEventListener(event, listener, options);
+ }
+
+ getActor(actorName) {
+ let inst = this.instances.get(actorName);
+ if (!inst) {
+ let actor = this.actors.get(actorName);
+
+ let obj = {};
+ ChromeUtils.import(actor.module, obj);
+
+ inst = new obj[actorName](this.mm);
+ this.instances.set(actorName, inst);
+ }
+ return inst;
+ }
+
+ handleEvent(event) {
+ if (event.type == "unload") {
+ this.cleanup();
+ }
+ }
+
+ handleActorEvent(actor, event) {
+ return this.getActor(actor).handleEvent(event);
+ }
+
+ receiveMessage(message) {
+ let actors = this.messages.get(message.name);
+ let result;
+ for (let actor of actors) {
+ try {
+ result = this.getActor(actor).receiveMessage(message);
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ return result;
+ }
+
+ observe(subject, topic, data) {
+ let actors = this.observers.get(topic);
+ for (let actor of actors) {
+ try {
+ this.getActor(actor).observe(subject, topic, data);
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ }
+ }
+}
+
+Dispatcher.prototype.QueryInterface =
+ ChromeUtils.generateQI(["nsIObserver",
+ "nsISupportsWeakReference"]);
+
+class SingletonDispatcher extends Dispatcher {
+ constructor(window, data) {
+ super(getMessageManager(window), data);
+
+ window.addEventListener("pageshow", this);
+ window.addEventListener("pagehide", this);
+
+ this.window = window;
+ this.listeners = [];
+ }
+
+ cleanup() {
+ super.cleanup();
+
+ for (let msg of this.messages.keys()) {
+ this.mm.removeMessageListener(msg, this);
+ }
+ for (let [event, listener, options] of this.listeners) {
+ this.window.removeEventListener(event, listener, options);
+ }
+ this.listeners = null;
+ }
+
+ handleEvent(event) {
+ if (event.type == "pageshow") {
+ if (this.hidden) {
+ this.init();
+ }
+ this.hidden = false;
+ } else if (event.type == "pagehide") {
+ this.hidden = true;
+ this.cleanup();
+ }
+ }
+
+ addEventListener(event, listener, options) {
+ this.listeners.push([event, listener, options]);
+ this.window.addEventListener(event, listener, options);
+ }
+}
+
+/* globals MatchPatternSet, MozDocumentMatcher, MozDocumentObserver */
+
+var ActorManagerChild = {
+ groups: new DefaultMap(group => {
+ return sharedData.get(`ChildActors:${group || ""}`);
+ }),
+
+ singletons: new Map(),
+
+ init() {
+ let singletons = sharedData.get("ChildSingletonActors");
+ for (let [filter, data] of singletons.entries()) {
+ let options = {
+ matches: new MatchPatternSet(filter.matches, {restrictSchemes: false}),
+ allFrames: filter.allFrames,
+ matchAboutBlank: filter.matchAboutBlank,
+ };
+
+ this.singletons.set(new MozDocumentMatcher(options), data);
+ }
+
+ this.observer = new MozDocumentObserver(this);
+ this.observer.observe(this.singletons.keys());
+
+ this.init = null;
+ },
+
+ /**
+ * MozDocumentObserver callbacks. These handle instantiating singleton actors
+ * for documents which match their MozDocumentMatcher filters.
+ */
+ onNewDocument(matcher, window) {
+ new SingletonDispatcher(window, this.singletons.get(matcher)).init();
+ },
+ onPreloadDocument(matcher, loadInfo) {
+ },
+
+ /**
+ * Attaches the appropriate set of actors to the given frame message manager.
+ *
+ * @param {ContentFrameMessageManager} mm
+ * The message manager to which to attach the actors.
+ * @param {string} [group]
+ * The messagemanagergroup of the <browser> to which the caller frame
+ * script belongs. This restricts the attached set of actors based on
+ * the "group" that their actor definitions specify.
+ */
+ attach(mm, group = null) {
+ new Dispatcher(mm, this.groups.get(group)).init();
+ },
+
+ getActor(mm, actorName) {
+ for (let dispatcher of this.dispatchers.get(mm)) {
+ let actor = dispatcher.getActor(actorName);
+ if (actor) {
+ return actor;
+ }
+ }
+ return null;
+ }
+};
+
+ActorManagerChild.init();
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/ActorManagerParent.jsm
@@ -0,0 +1,186 @@
+/* vim: set ts=2 sw=2 sts=2 et tw=80: */
+/* 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/. */
+"use strict";
+
+/**
+ * This module implements logic for managing JavaScript actor instances bound to
+ * message managers. It handles lazily instantiating those actors based on
+ * DOM events, IPC messages, or observer notifications, and is meant to entirely
+ * replace the existing concept of frame scripts.
+ *
+ * All actors must be registered in the parent process, before the first child
+ * process starts. Once all actors have been registered, the actor data is
+ * mangled into a form which can be handled efficiently during content process
+ * startup, and shared with all content processes. Frame scripts in those
+ * content processes attach that data to frame message managers via
+ * ActorManagerChild, which handles instantiating and dispatching to those
+ * actors as necessary.
+ *
+ *
+ * Each actor is a class which lives in a JSM, and has a constructor which takes
+ * a single message manager argument. Each actor may conceptually have both
+ * Child and Parent variants, but only Child variants are currently implemented.
+ * The parent and child variants live in separate JSMs, and have separate class
+ * names, each of which have Child or Parent appended to their names, as
+ * appropriate. For instance, the Browser actor has a child instance named
+ * BrowserChild which lives in BrowserChild.jsm.
+ *
+ *
+ * Actors are defined by calling ActorManagerParent.addActors, with an object
+ * containing a property for each actor being defined, whose value is an object
+ * describing how the actor should be loaded. That object may have the following
+ * properties:
+ *
+ * - "module": The base URL for the module, without "Child.jsm" or "Parent.jsm"
+ * suffixes. "resource://gre/modules/Browser" will be mapped to
+ * "resource://gre/modules/BrowserChild.jsm" in the child process and
+ * "resource://gre/modules/BrowserParent.jsm" in the parent.
+ *
+ * - "child": The actor definition for the child side of the actor.
+ *
+ * Each "child" (or "parent", when it is implemented) actor definition may
+ * contain the following properties:
+ *
+ * - "group": A group name which restricts the message managers to which this
+ * actor may be attached. This should match the "messagemanagergroup"
+ * attribute of a <browser> element. Frame scripts are responsible for
+ * attaching the appropriate actors to the appropriate browsers using
+ * ActorManagerChild.attach().
+ *
+ * - "events": An object containing a property for each event the actor will
+ * listen for, with an options object, as accepted by addEventListener, as its
+ * value. For each such property, an event listener will be added to the
+ * message manager[1] for the given event name, which delegates to the actor's
+ * handleEvent method.
+ *
+ * - "messages": An array of message manager message names. For each message
+ * name in the list, a message listener will be added to the frame message
+ * manager, and the messages it receives will be delegated to the actor's
+ * receiveMessage method.
+ *
+ * - "observers": An array of observer topics. A global observer will be added
+ * for each topic in the list, and observer notifications for it will be
+ * delegated to the actor's observe method. Note that observers are global in
+ * nature, and these notifications may therefore have nothing to do with the
+ * message manager the actor is bound to. The actor itself is responsible for
+ * filtering the notifications that apply to it.
+ *
+ * These observers are automatically unregistered when the message manager is
+ * destroyed.
+ *
+ * - "matches": An array of URL match patterns (as accepted by the MatchPattern
+ * class in MatchPattern.webidl) which restrict which pages the actor may be
+ * instantiated for. If this is defined, the actor will only receive DOM
+ * events sent to windows which match this pattern, and will only receive
+ * message manager messages for frame message managers which are currently
+ * hosting a matching DOM window.
+ *
+ * - "allFrames": If "matches" is specified, this modifies its behavior to
+ * allow it to match sub-frames as well as top-level frames. If "allFrames" is
+ * not specified, it will match only top-level frames. See
+ * MozDocumentMather.webidl for more information.
+ *
+ * - "matchAboutBlank": If "matches" is specified, this modifies its behavior to
+ * allow it to match about:blank pages. See MozDocumentMather.webidl for more
+ * information.
+ *
+ * [1]: For actors which specify "matches" to restrict them to a certain set of
+ * windows, the listener will be added to the DOM window rather than the
+ * frame message manager.
+ */
+
+var EXPORTED_SYMBOLS = ["ActorManagerParent"];
+
+ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const {DefaultMap} = ExtensionUtils;
+
+let ACTORS = {
+};
+
+class ActorSet {
+ constructor(group, actorSide) {
+ this.group = group;
+ this.actorSide = actorSide;
+
+ this.actors = new Map();
+ this.events = [];
+ this.messages = new DefaultMap(() => []);
+ this.observers = new DefaultMap(() => []);
+ }
+
+ addActor(actorName, actor) {
+ actorName += this.actorSide;
+ this.actors.set(actorName, {module: actor.module});
+
+ if (actor.events) {
+ for (let [event, options] of Object.entries(actor.events)) {
+ this.events.push({actor: actorName, event, options});
+ }
+ }
+ for (let msg of actor.messages || []) {
+ this.messages.get(msg).push(actorName);
+ }
+ for (let topic of actor.observers || []) {
+ this.observers.get(topic).push(actorName);
+ }
+ }
+}
+
+const {sharedData} = Services.ppmm;
+
+var ActorManagerParent = {
+ // Actor sets which should be loaded in the child side, keyed by
+ // "messagemanagergroup".
+ childGroups: new DefaultMap(group => new ActorSet(group, "Child")),
+ // Actor sets which should be loaded in the parent side, keyed by
+ // "messagemanagergroup".
+ parentGroups: new DefaultMap(group => new ActorSet(group, "Parent")),
+
+ // Singleton actor sets, which should be loaded only for documents which match
+ // a specific pattern. The keys in this map are plain objects specifying
+ // filter keys as understood by MozDocumentMatcher.
+ singletons: new DefaultMap(() => new ActorSet(null, "Child")),
+
+ addActors(actors) {
+ for (let [actorName, actor] of Object.entries(actors)) {
+ let {child} = actor;
+ {
+ let actorSet;
+ if (child.matches) {
+ actorSet = this.singletons.get({matches: child.matches,
+ allFrames: child.allFrames,
+ matchAboutBlank: child.matchAboutBlank});
+ } else {
+ actorSet = this.childGroups.get(child.group || null);
+ }
+
+ actorSet.addActor(actorName, child);
+ }
+
+ if (actor.parent) {
+ let {parent} = actor;
+ this.parentGroups.get(parent.group || null).addActor(
+ actorName, parent);
+ }
+ }
+ },
+
+ /**
+ * Serializes the current set of registered actors into ppmm.sharedData, for
+ * use by ActorManagerChild. This must be called before any frame message
+ * managers have been created. It will have no effect on existing message
+ * managers.
+ */
+ flush() {
+ for (let [name, data] of this.childGroups) {
+ sharedData.set(`ChildActors:${name || ""}`, data);
+ }
+ sharedData.set("ChildSingletonActors", this.singletons);
+ },
+};
+
+ActorManagerParent.addActors(ACTORS);
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -164,16 +164,19 @@ TESTING_JS_MODULES += [
]
SPHINX_TREES['toolkit_modules'] = 'docs'
with Files('docs/**'):
SCHEDULES.exclusive = ['docs']
EXTRA_JS_MODULES += [
+ 'ActorChild.jsm',
+ 'ActorManagerChild.jsm',
+ 'ActorManagerParent.jsm',
'addons/MatchURLFilters.jsm',
'addons/SecurityInfo.jsm',
'addons/WebNavigation.jsm',
'addons/WebNavigationContent.js',
'addons/WebNavigationFrames.jsm',
'addons/WebRequest.jsm',
'addons/WebRequestCommon.jsm',
'addons/WebRequestContent.js',