Bug 1490810 - Simulate Fission for browser actors by blocking them from receiving sub-frame events. r=kmag
authorFelipe Gomes <felipc@gmail.com>
Wed, 26 Sep 2018 21:46:18 +0000
changeset 438370 3339b4bf9acd
parent 438369 d0595bcbe068
child 438371 23f8684d0873
push id34718
push usershindli@mozilla.com
push dateThu, 27 Sep 2018 03:13:23 +0000
treeherdermozilla-central@fa1831cceeae [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1490810
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1490810 - Simulate Fission for browser actors by blocking them from receiving sub-frame events. r=kmag If the pref browser.fission.simulate is true, the event dispatcher in ActorManagerChild will not dispatch events to actors that aren't associated with the same window as the event's target. In addition, when that pref is on, the actors associated with sub-frames will have their content property bound to the correct content window, that might differ from the message manager's window (which is always related to the top level). Then, in order to write Fission-compatible code, that specific actor will need to be declared with allFrames = true, meaning that it wants to be instantiated for every frame, and not just top-level ones Differential Revision: https://phabricator.services.mozilla.com/D6358
browser/actors/AboutReaderChild.jsm
browser/actors/ContextMenuChild.jsm
browser/actors/FormSubmitChild.jsm
browser/actors/LightweightThemeChild.jsm
browser/actors/LinkHandlerChild.jsm
browser/actors/OfflineAppsChild.jsm
browser/actors/PluginChild.jsm
browser/actors/UAWidgetsChild.jsm
browser/app/profile/firefox.js
toolkit/actors/DateTimePickerChild.jsm
toolkit/actors/FindBarChild.jsm
toolkit/actors/FinderChild.jsm
toolkit/actors/PopupBlockingChild.jsm
toolkit/actors/ZoomChild.jsm
toolkit/modules/ActorChild.jsm
toolkit/modules/ActorManagerChild.jsm
toolkit/modules/ActorManagerParent.jsm
--- a/browser/actors/AboutReaderChild.jsm
+++ b/browser/actors/AboutReaderChild.jsm
@@ -11,18 +11,18 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.defineModuleGetter(this, "AboutReader",
                                "resource://gre/modules/AboutReader.jsm");
 ChromeUtils.defineModuleGetter(this, "ReaderMode",
                                "resource://gre/modules/ReaderMode.jsm");
 ChromeUtils.defineModuleGetter(this, "Readerable",
                                "resource://gre/modules/Readerable.jsm");
 
 class AboutReaderChild extends ActorChild {
-  constructor(mm) {
-    super(mm);
+  constructor(dispatcher) {
+    super(dispatcher);
 
     this._articlePromise = null;
     this._isLeavingReaderableReaderMode = false;
   }
 
   receiveMessage(message) {
     switch (message.name) {
       case "Reader:ToggleReaderMode":
--- a/browser/actors/ContextMenuChild.jsm
+++ b/browser/actors/ContextMenuChild.jsm
@@ -222,36 +222,36 @@ const messageListeners = {
     }
   },
 };
 
 let contextMenus = new WeakMap();
 
 class ContextMenuChild extends ActorChild {
   // PUBLIC
-  constructor(global) {
-    super(global);
+  constructor(dispatcher) {
+    super(dispatcher);
 
-    contextMenus.set(global, this);
+    contextMenus.set(this.mm, this);
 
     this.target = null;
     this.context = null;
     this.lastMenuTarget = null;
 
     Object.keys(messageListeners).forEach(key =>
-      global.addMessageListener(key, messageListeners[key].bind(this))
+      this.mm.addMessageListener(key, messageListeners[key].bind(this))
     );
   }
 
-  static getTarget(global, message, key) {
-    return contextMenus.get(global).getTarget(message, key);
+  static getTarget(mm, message, key) {
+    return contextMenus.get(mm).getTarget(message, key);
   }
 
-  static getLastTarget(global) {
-    let contextMenu = contextMenus.get(global);
+  static getLastTarget(mm) {
+    let contextMenu = contextMenus.get(mm);
     return contextMenu && contextMenu.lastMenuTarget;
   }
 
   /**
    * Returns the event target of the context menu, using a locally stored
    * reference if possible. If not, and aMessage.objects is defined,
    * aMessage.objects[aKey] is returned. Otherwise null.
    * @param  {Object} aMessage Message with a objects property
--- a/browser/actors/FormSubmitChild.jsm
+++ b/browser/actors/FormSubmitChild.jsm
@@ -10,18 +10,18 @@
 
 var EXPORTED_SYMBOLS = ["FormSubmitChild"];
 
 ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 class FormSubmitChild extends ActorChild {
-  constructor(mm) {
-    super(mm);
+  constructor(dispatcher) {
+    super(dispatcher);
 
     this._validationMessage = "";
     this._element = null;
 
     this.mm.addEventListener("pageshow", this);
   }
 
   /*
--- a/browser/actors/LightweightThemeChild.jsm
+++ b/browser/actors/LightweightThemeChild.jsm
@@ -10,18 +10,29 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 /**
  * LightweightThemeChild forwards theme data to in-content pages.
  * It is both instantiated by the traditional Actor mechanism,
  * and also manually within the sidebar JS global (which has no message manager)
  */
 class LightweightThemeChild extends ActorChild {
-  constructor(mm) {
-    super(mm);
+  constructor(dispatcher) {
+    if (dispatcher.mm) {
+      // This is being instantiated by the Actor mechanism.
+      super(dispatcher);
+    } else {
+      // Manually instantiated by the sidebar.
+      let fakeDispatcher = {
+        mm: dispatcher,
+        window: dispatcher.content,
+        addEventListener: dispatcher.content.addEventListener,
+      };
+      super(fakeDispatcher);
+    }
 
     this.init();
   }
 
   /**
    * Initializes the actor for the current page, sending it any existing
    * theme data, and adding shared data change listeners so it can
    * notify the page of future updates.
@@ -37,16 +48,18 @@ class LightweightThemeChild extends Acto
 
   /**
    * Cleans up any global listeners registered by the actor.
    *
    * This is called by ActorManagerChild any time it receives a pagehide
    * event for the page we're attached to.
    */
   cleanup() {
+    super.cleanup();
+
     Services.cpmm.sharedData.removeEventListener("change", this);
   }
 
   /**
    * Handles "change" events on the child sharedData map, and notifies
    * our content page if its theme data was among the changed keys.
    */
   handleEvent(event) {
--- a/browser/actors/LinkHandlerChild.jsm
+++ b/browser/actors/LinkHandlerChild.jsm
@@ -8,18 +8,18 @@ const EXPORTED_SYMBOLS = ["LinkHandlerCh
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 
 ChromeUtils.defineModuleGetter(this, "FaviconLoader",
   "resource:///modules/FaviconLoader.jsm");
 
 class LinkHandlerChild extends ActorChild {
-  constructor(mm) {
-    super(mm);
+  constructor(dispatcher) {
+    super(dispatcher);
 
     this.seenTabIcon = false;
     this._iconLoader = null;
   }
 
   get iconLoader() {
     if (!this._iconLoader) {
       this._iconLoader = new FaviconLoader(this.mm);
--- a/browser/actors/OfflineAppsChild.jsm
+++ b/browser/actors/OfflineAppsChild.jsm
@@ -5,18 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var EXPORTED_SYMBOLS = ["OfflineAppsChild"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 
 class OfflineAppsChild extends ActorChild {
-  constructor(mm) {
-    super(mm);
+  constructor(dispatcher) {
+    super(dispatcher);
 
     this._docId = 0;
     this._docIdMap = new Map();
 
     this._docManifestSet = new Set();
 
     this._observerAdded = false;
   }
--- a/browser/actors/PluginChild.jsm
+++ b/browser/actors/PluginChild.jsm
@@ -29,18 +29,18 @@ const OVERLAY_DISPLAY = {
   BLANK: 1, // The overlay will be just a grey box
   TINY: 2, // The overlay with a 16x16 plugin icon
   REDUCED: 3, // The overlay with a 32x32 plugin icon
   NOTEXT: 4, // The overlay with a 48x48 plugin icon and the close button
   FULL: 5, // The full overlay: 48x48 plugin icon, close button and label
 };
 
 class PluginChild extends ActorChild {
-  constructor(mm) {
-    super(mm);
+  constructor(dispatcher) {
+    super(dispatcher);
 
     // Cache of plugin actions for the current page.
     this.pluginData = new Map();
     // Cache of plugin crash information sent from the parent
     this.pluginCrashData = new Map();
 
     this.mm.addEventListener("pagehide", this, {capture: true, mozSystemGroup: true});
     this.mm.addEventListener("pageshow", this, {capture: true, mozSystemGroup: true});
--- a/browser/actors/UAWidgetsChild.jsm
+++ b/browser/actors/UAWidgetsChild.jsm
@@ -5,18 +5,18 @@
 "use strict";
 
 var EXPORTED_SYMBOLS = ["UAWidgetsChild"];
 
 ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 class UAWidgetsChild extends ActorChild {
-  constructor(mm) {
-    super(mm);
+  constructor(dispatcher) {
+    super(dispatcher);
 
     this.widgets = new WeakMap();
   }
 
   handleEvent(aEvent) {
     switch (aEvent.type) {
       case "UAWidgetBindToTree":
       case "UAWidgetAttributeChanged":
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1755,16 +1755,23 @@ pref("app.normandy.shieldLearnMoreUrl", 
 pref("app.shield.optoutstudies.enabled", true);
 #else
 pref("app.shield.optoutstudies.enabled", false);
 #endif
 
 // Multi-lingual preferences
 pref("intl.multilingual.enabled", false);
 
+// Simulate conditions that will happen when the browser
+// is running with Fission enabled. This is meant to assist
+// development and testing of Fission.
+// The current simulated conditions are:
+// - Don't propagate events from subframes to JS child actors
+pref("browser.fission.simulate", false);
+
 // Prio preferences
 // Only enable by default on Nightly.
 // On platforms that do not build libprio, do not set these prefs at all, which gives us a way to detect support.
 
 // Curve25519 public keys for Prio servers
 #ifdef MOZ_LIBPRIO
 pref("prio.publicKeyA", "35AC1C7576C7C6EDD7FED6BCFC337B34D48CB4EE45C86BEEFB40BD8875707733");
 pref("prio.publicKeyB", "26E6674E65425B823F1F1D5F96E3BB3EF9E406EC7FBA7DEF8B08A35DD135AF50");
--- a/toolkit/actors/DateTimePickerChild.jsm
+++ b/toolkit/actors/DateTimePickerChild.jsm
@@ -14,54 +14,54 @@ ChromeUtils.import("resource://gre/modul
  * DateTimePickerChild is the communication channel between the input box
  * (content) for date/time input types and its picker (chrome).
  */
 class DateTimePickerChild extends ActorChild {
   /**
    * On init, just listen for the event to open the picker, once the picker is
    * opened, we'll listen for update and close events.
    */
-  constructor(global) {
-    super(global);
+  constructor(dispatcher) {
+    super(dispatcher);
+
     this._inputElement = null;
-    this._global = global;
   }
 
   /**
    * Cleanup function called when picker is closed.
    */
   close() {
     this.removeListeners();
     this._inputElement.setDateTimePickerState(false);
     this._inputElement = null;
   }
 
   /**
    * Called after picker is opened to start listening for input box update
    * events.
    */
   addListeners() {
-    this._global.addEventListener("MozUpdateDateTimePicker", this);
-    this._global.addEventListener("MozCloseDateTimePicker", this);
-    this._global.addEventListener("pagehide", this);
+    this.mm.addEventListener("MozUpdateDateTimePicker", this);
+    this.mm.addEventListener("MozCloseDateTimePicker", this);
+    this.mm.addEventListener("pagehide", this);
 
-    this._global.addMessageListener("FormDateTime:PickerValueChanged", this);
-    this._global.addMessageListener("FormDateTime:PickerClosed", this);
+    this.mm.addMessageListener("FormDateTime:PickerValueChanged", this);
+    this.mm.addMessageListener("FormDateTime:PickerClosed", this);
   }
 
   /**
    * Stop listeneing for events when picker is closed.
    */
   removeListeners() {
-    this._global.removeEventListener("MozUpdateDateTimePicker", this);
-    this._global.removeEventListener("MozCloseDateTimePicker", this);
-    this._global.removeEventListener("pagehide", this);
+    this.mm.removeEventListener("MozUpdateDateTimePicker", this);
+    this.mm.removeEventListener("MozCloseDateTimePicker", this);
+    this.mm.removeEventListener("pagehide", this);
 
-    this._global.removeMessageListener("FormDateTime:PickerValueChanged", this);
-    this._global.removeMessageListener("FormDateTime:PickerClosed", this);
+    this.mm.removeMessageListener("FormDateTime:PickerValueChanged", this);
+    this.mm.removeMessageListener("FormDateTime:PickerClosed", this);
   }
 
   /**
    * Helper function that returns the CSS direction property of the element.
    */
   getComputedDirection(aElement) {
     return aElement.ownerGlobal.getComputedStyle(aElement)
       .getPropertyValue("direction");
@@ -117,17 +117,17 @@ class DateTimePickerChild extends ActorC
           return;
         }
 
         this._inputElement = aEvent.originalTarget;
         this._inputElement.setDateTimePickerState(true);
         this.addListeners();
 
         let value = this._inputElement.getDateTimeInputBoxValue();
-        this._global.sendAsyncMessage("FormDateTime:OpenPicker", {
+        this.mm.sendAsyncMessage("FormDateTime:OpenPicker", {
           rect: this.getBoundingContentRect(this._inputElement),
           dir: this.getComputedDirection(this._inputElement),
           type: this._inputElement.type,
           detail: {
             // Pass partial value if it's available, otherwise pass input
             // element's value.
             value: Object.keys(value).length > 0 ? value
                                                  : this._inputElement.value,
@@ -137,28 +137,28 @@ class DateTimePickerChild extends ActorC
             stepBase: this._inputElement.getStepBase(),
           },
         });
         break;
       }
       case "MozUpdateDateTimePicker": {
         let value = this._inputElement.getDateTimeInputBoxValue();
         value.type = this._inputElement.type;
-        this._global.sendAsyncMessage("FormDateTime:UpdatePicker", { value });
+        this.mm.sendAsyncMessage("FormDateTime:UpdatePicker", { value });
         break;
       }
       case "MozCloseDateTimePicker": {
-        this._global.sendAsyncMessage("FormDateTime:ClosePicker");
+        this.mm.sendAsyncMessage("FormDateTime:ClosePicker");
         this.close();
         break;
       }
       case "pagehide": {
         if (this._inputElement &&
             this._inputElement.ownerDocument == aEvent.target) {
-          this._global.sendAsyncMessage("FormDateTime:ClosePicker");
+          this.mm.sendAsyncMessage("FormDateTime:ClosePicker");
           this.close();
         }
         break;
       }
       default:
         break;
     }
   }
--- a/toolkit/actors/FindBarChild.jsm
+++ b/toolkit/actors/FindBarChild.jsm
@@ -9,18 +9,19 @@ var EXPORTED_SYMBOLS = ["FindBarChild"];
 ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                                "resource://gre/modules/BrowserUtils.jsm");
 
 class FindBarChild extends ActorChild {
-  constructor(mm) {
-    super(mm);
+  constructor(dispatcher) {
+    super(dispatcher);
+
     this._findKey = null;
 
     XPCOMUtils.defineLazyProxy(this, "FindBarContent", () => {
       let tmp = {};
       ChromeUtils.import("resource://gre/modules/FindBarContent.jsm", tmp);
       return new tmp.FindBarContent(this.mm);
     }, {inQuickFind: false, inPassThrough: false});
   }
--- a/toolkit/actors/FinderChild.jsm
+++ b/toolkit/actors/FinderChild.jsm
@@ -26,24 +26,24 @@ const MESSAGES = [
   "Finder:FindbarClose",
   "Finder:FindbarOpen",
   "Finder:KeyPress",
   "Finder:MatchesCount",
   "Finder:ModalHighlightChange",
 ];
 
 class FinderChild extends ActorChild {
-  constructor(mm) {
-    super(mm);
+  constructor(dispatcher) {
+    super(dispatcher);
 
-    this._finder = new Finder(mm.docShell);
+    this._finder = new Finder(this.docShell);
     this._finder.addResultListener(this);
 
     for (let msg of MESSAGES) {
-      mm.addMessageListener(msg, this);
+      this.mm.addMessageListener(msg, this);
     }
   }
 
   onFindResult(aData) {
     this.mm.sendAsyncMessage("Finder:Result", aData);
   }
 
   // When the child receives messages with results of requestMatchesCount,
--- a/toolkit/actors/PopupBlockingChild.jsm
+++ b/toolkit/actors/PopupBlockingChild.jsm
@@ -5,27 +5,27 @@
 
 /* eslint no-unused-vars: ["error", {args: "none"}] */
 
 var EXPORTED_SYMBOLS = ["PopupBlockingChild"];
 
 ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 
 class PopupBlockingChild extends ActorChild {
-  constructor(mm) {
-    super(mm);
+  constructor(dispatcher) {
+    super(dispatcher);
 
     this.popupData = null;
     this.popupDataInternal = null;
 
-    mm.addEventListener("pageshow", this, true);
-    mm.addEventListener("pagehide", this, true);
+    this.mm.addEventListener("pageshow", this, true);
+    this.mm.addEventListener("pagehide", this, true);
 
-    mm.addMessageListener("PopupBlocking:UnblockPopup", this);
-    mm.addMessageListener("PopupBlocking:GetBlockedPopupList", this);
+    this.mm.addMessageListener("PopupBlocking:UnblockPopup", this);
+    this.mm.addMessageListener("PopupBlocking:GetBlockedPopupList", this);
   }
 
   receiveMessage(msg) {
     switch (msg.name) {
       case "PopupBlocking:UnblockPopup": {
         let i = msg.data.index;
         if (this.popupData && this.popupData[i]) {
           let data = this.popupData[i];
--- a/toolkit/actors/ZoomChild.jsm
+++ b/toolkit/actors/ZoomChild.jsm
@@ -4,18 +4,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 var EXPORTED_SYMBOLS = ["ZoomChild"];
 
 ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 
 class ZoomChild extends ActorChild {
-  constructor(mm) {
-    super(mm);
+  constructor(dispatcher) {
+    super(dispatcher);
+
     this._cache = {
       fullZoom: NaN,
       textZoom: NaN,
     };
   }
 
   get fullZoom() {
     return this._cache.fullZoom;
--- a/toolkit/modules/ActorChild.jsm
+++ b/toolkit/modules/ActorChild.jsm
@@ -6,22 +6,35 @@
 
 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.
+ *
+ * If Fission is being simulated, and the actor is registered as "allFrames",
+ * the `content` property of this class will be bound to a specific subframe.
+ * Otherwise, the `content` is always the top-level content tied to the `mm`.
  */
 class ActorChild {
-  constructor(mm) {
-    this.mm = mm;
+  constructor(dispatcher) {
+    this._dispatcher = dispatcher;
+    this.mm = dispatcher.mm;
   }
 
   get content() {
-    return this.mm.content;
+    return this._dispatcher.window || this.mm.content;
   }
 
   get docShell() {
     return this.mm.docShell;
   }
+
+  addEventListener(event, listener, options) {
+    this._dispatcher.addEventListener(event, listener, options);
+  }
+
+  cleanup() {
+    this._dispatcher = null;
+  }
 }
--- a/toolkit/modules/ActorManagerChild.jsm
+++ b/toolkit/modules/ActorManagerChild.jsm
@@ -9,21 +9,25 @@
  * 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");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const {DefaultMap} = ExtensionUtils;
 
 const {sharedData} = Services.cpmm;
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "simulateFission",
+                                      "browser.fission.simulate", false);
+
 function getMessageManager(window) {
   return window.docShell.messageManager;
 }
 
 class Dispatcher {
   constructor(mm, data) {
     this.mm = mm;
 
@@ -62,30 +66,42 @@ class Dispatcher {
   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);
+      inst = new obj[actorName](this);
       this.instances.set(actorName, inst);
     }
     return inst;
   }
 
   handleEvent(event) {
     if (event.type == "unload") {
       this.cleanup();
     }
   }
 
   handleActorEvent(actor, event) {
-    return this.getActor(actor).handleEvent(event);
+    let targetWindow = null;
+
+    if (simulateFission) {
+      targetWindow = event.target.ownerGlobal;
+      let dispatcherWindow = this.window || this.mm.content;
+
+      if (targetWindow != dispatcherWindow) {
+        // events can't propagate across frame boundaries because the
+        // frames will be hosted on separated processes.
+        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);
@@ -143,22 +159,20 @@ class SingletonDispatcher extends Dispat
     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);
     }
 
     for (let actor of this.instances.values()) {
-      if (typeof actor.cleanup === "function") {
-        try {
-          actor.cleanup();
-        } catch (e) {
-          Cu.reportError(e);
-        }
+      try {
+        actor.cleanup();
+      } catch (e) {
+        Cu.reportError(e);
       }
     }
 
     this.listeners = null;
   }
 
   handleEvent(event) {
     if (event.type == "pageshow") {
--- a/toolkit/modules/ActorManagerParent.jsm
+++ b/toolkit/modules/ActorManagerParent.jsm
@@ -73,28 +73,29 @@
  *
  * - "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.
+ * - "allFrames": 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.
  *
  * - "matchAboutBlank": If "matches" is specified, this modifies its behavior to
- *   allow it to match about:blank pages. See MozDocumentMather.webidl for more
+ *   allow it to match about:blank pages. See MozDocumentMatcher.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.
+ * [1]: For actors which specify "matches" or "allFrames", the listener will be
+ *      added to the DOM window rather than the frame message manager.
+ *
+ * If Fission is being simulated, and an actor needs to receive events from
+ * sub-frames, it must use "allFrames".
  */
 
 var EXPORTED_SYMBOLS = ["ActorManagerParent"];
 
 ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 const {DefaultMap} = ExtensionUtils;
@@ -361,18 +362,18 @@ var ActorManagerParent = {
   // 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,
+        if (child.matches || child.allFrames) {
+          actorSet = this.singletons.get({matches: child.matches || ["<all_urls>"],
                                           allFrames: child.allFrames,
                                           matchAboutBlank: child.matchAboutBlank});
         } else {
           actorSet = this.childGroups.get(child.group || null);
         }
 
         actorSet.addActor(actorName, child);
       }