Merge mozilla-inbound to mozilla-central. a=merge
authorCosmin Sabou <csabou@mozilla.com>
Wed, 15 Aug 2018 03:51:45 +0300
changeset 486665 b80906e2fbc9f9ea6ad8eab753dafc9fb9b56b39
parent 486524 fa2f3d22c1564e6318725061c390834729a4df66 (current diff)
parent 486664 2844aaceec5f3ef74b4dac5359c38c568130c82e (diff)
child 486666 55313d9fd453e37f3a34b9f9c9b714d4ccbb320d
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.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
Merge mozilla-inbound to mozilla-central. a=merge
browser/base/content/browser.js
browser/base/content/webext-panels.js
browser/components/extensions/ExtensionPopups.jsm
browser/components/extensions/parent/ext-sidebarAction.js
browser/components/uitour/ContentUITour.jsm
browser/modules/BlockedSiteContent.jsm
browser/modules/ClickEventHandler.jsm
browser/modules/ContentWebRTC.jsm
browser/modules/ContextMenu.jsm
browser/modules/LightWeightThemeWebInstallListener.jsm
browser/modules/NetErrorContent.jsm
browser/modules/PageInfoListener.jsm
browser/modules/PageStyleHandler.jsm
browser/modules/PluginContent.jsm
browser/themes/linux/places/places.css
browser/themes/osx/places/places.css
dom/base/ProcessGlobal.cpp
dom/base/ProcessGlobal.h
dom/base/nsInProcessTabChildGlobal.cpp
dom/base/nsInProcessTabChildGlobal.h
dom/ipc/ManifestMessages.jsm
gfx/webrender/src/clip_node.rs
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/background-fetch/idlharness.any.js
testing/web-platform/tests/dom/nodes/Document-URL.sub.html
testing/web-platform/tests/html/infrastructure/urls/dynamic-changes-to-base-urls/dynamic-urls.sub.xhtml
testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.html
testing/web-platform/tests/notifications/idlharness.any.js
testing/web-platform/tests/payment-request/payment-request-canmakepayment-method-manual.https.html
testing/web-platform/tests/push-api/idlharness.any.js
toolkit/components/normandy/content/ShieldFrameListener.jsm
toolkit/modules/DateTimePickerContent.jsm
toolkit/modules/FindBarChild.jsm
toolkit/modules/PopupBlocking.jsm
toolkit/modules/PrintingContent.jsm
toolkit/modules/RemoteFinder.jsm
toolkit/modules/SelectContentHelper.jsm
toolkit/modules/SelectionSourceContent.jsm
toolkit/modules/WebChannelContent.jsm
toolkit/modules/WebNavigationChild.jsm
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2016,17 +2016,16 @@ dependencies = [
  "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "fallible 0.0.1",
  "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.0.1",
  "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "new-ordered-float 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "nsstring 0.1.0",
copy from browser/base/content/tab-content.js
copy to browser/actors/AboutReaderChild.jsm
--- a/browser/base/content/tab-content.js
+++ b/browser/actors/AboutReaderChild.jsm
@@ -1,190 +1,86 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
-
-/* This content script contains code that requires a tab browser. */
-
-/* eslint-env mozilla/frame-script */
-
-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");
-
-// TabChildGlobal
-var global = this;
-
+"use strict";
 
-addEventListener("MozDOMPointerLock:Entered", function(aEvent) {
-  sendAsyncMessage("PointerLock:Entered", {
-    originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix
-  });
-});
-
-addEventListener("MozDOMPointerLock:Exited", function(aEvent) {
-  sendAsyncMessage("PointerLock:Exited");
-});
-
+var EXPORTED_SYMBOLS = ["AboutReaderChild"];
 
-addMessageListener("Browser:HideSessionRestoreButton", function(message) {
-  // Hide session restore button on about:home
-  let doc = content.document;
-  let container;
-  if (doc.documentURI.toLowerCase() == "about:home" &&
-      (container = doc.getElementById("sessionRestoreContainer"))) {
-    container.hidden = true;
-  }
-});
-
-if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
-  addMessageListener("Browser:HasSiblings", function(message) {
-    let tabChild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsITabChild);
-    let hasSiblings = message.data;
-    tabChild.hasSiblings = hasSiblings;
-  });
-}
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 
-// XXX(nika): Should we try to call this in the parent process instead?
-addMessageListener("Browser:Reload", function(message) {
-  /* First, we'll try to use the session history object to reload so
-   * that framesets are handled properly. If we're in a special
-   * window (such as view-source) that has no session history, fall
-   * back on using the web navigation's reload method.
-   */
-
-  let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
-  try {
-    if (webNav.sessionHistory) {
-      webNav = webNav.sessionHistory;
-    }
-  } catch (e) {
-  }
-
-  let reloadFlags = message.data.flags;
-  try {
-    E10SUtils.wrapHandlingUserInput(content, message.data.handlingUserInput,
-                                    () => webNav.reload(reloadFlags));
-  } catch (e) {
-  }
-});
-
-addMessageListener("MixedContent:ReenableProtection", function() {
-  docShell.mixedContentChannel = null;
-});
-
-XPCOMUtils.defineLazyProxy(this, "LightweightThemeChildHelper",
-  "resource:///modules/LightweightThemeChildHelper.jsm");
+ChromeUtils.defineModuleGetter(this, "AboutReader",
+                               "resource://gre/modules/AboutReader.jsm");
+ChromeUtils.defineModuleGetter(this, "ReaderMode",
+                               "resource://gre/modules/ReaderMode.jsm");
 
-XPCOMUtils.defineLazyProxy(this, "ManifestMessages", () => {
-  let tmp = {};
-  ChromeUtils.import("resource://gre/modules/ManifestMessages.jsm", tmp);
-  return new tmp.ManifestMessages(global);
-});
+class AboutReaderChild extends ActorChild {
+  constructor(mm) {
+    super(mm);
 
-let themeablePagesWhitelist = new Set([
-  "about:home",
-  "about:newtab",
-  "about:welcome",
-]);
-
-addEventListener("pageshow", function({ originalTarget }) {
-  if (originalTarget.defaultView == content && themeablePagesWhitelist.has(content.document.documentURI)) {
-    LightweightThemeChildHelper.listen(themeablePagesWhitelist);
-    LightweightThemeChildHelper.update(chromeOuterWindowID, content);
+    this._articlePromise = null;
+    this._isLeavingReaderableReaderMode = false;
   }
-}, false, true);
-
-var AboutReaderListener = {
-
-  _articlePromise: null,
-
-  _isLeavingReaderableReaderMode: false,
-
-  init() {
-    addEventListener("AboutReaderContentLoaded", this, false, true);
-    addEventListener("DOMContentLoaded", this, false);
-    addEventListener("pageshow", this, false);
-    addEventListener("pagehide", this, false);
-    addMessageListener("Reader:ToggleReaderMode", this);
-    addMessageListener("Reader:PushState", this);
-    this.init = null;
-  },
 
   receiveMessage(message) {
     switch (message.name) {
       case "Reader:ToggleReaderMode":
         if (!this.isAboutReader) {
-          this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
-          ReaderMode.enterReaderMode(docShell, content);
+          this._articlePromise = ReaderMode.parseDocument(this.content.document).catch(Cu.reportError);
+          ReaderMode.enterReaderMode(this.mm.docShell, this.content);
         } else {
           this._isLeavingReaderableReaderMode = this.isReaderableAboutReader;
-          ReaderMode.leaveReaderMode(docShell, content);
+          ReaderMode.leaveReaderMode(this.mm.docShell, this.content);
         }
         break;
 
       case "Reader:PushState":
         this.updateReaderButton(!!(message.data && message.data.isArticle));
         break;
     }
-  },
+  }
 
   get isAboutReader() {
-    if (!content) {
+    if (!this.content) {
       return false;
     }
-    return content.document.documentURI.startsWith("about:reader");
-  },
+    return this.content.document.documentURI.startsWith("about:reader");
+  }
 
   get isReaderableAboutReader() {
     return this.isAboutReader &&
-      !content.document.documentElement.dataset.isError;
-  },
+      !this.content.document.documentElement.dataset.isError;
+  }
 
   handleEvent(aEvent) {
-    if (aEvent.originalTarget.defaultView != content) {
+    if (aEvent.originalTarget.defaultView != this.content) {
       return;
     }
 
     switch (aEvent.type) {
       case "AboutReaderContentLoaded":
         if (!this.isAboutReader) {
           return;
         }
 
-        if (content.document.body) {
+        if (this.content.document.body) {
           // Update the toolbar icon to show the "reader active" icon.
-          sendAsyncMessage("Reader:UpdateReaderButton");
-          new AboutReader(global, content, this._articlePromise);
+          this.mm.sendAsyncMessage("Reader:UpdateReaderButton");
+          new AboutReader(this.mm, this.content, this._articlePromise);
           this._articlePromise = null;
         }
         break;
 
       case "pagehide":
         this.cancelPotentialPendingReadabilityCheck();
         // this._isLeavingReaderableReaderMode is used here to keep the Reader Mode icon
         // visible in the location bar when transitioning from reader-mode page
         // back to the readable source page.
-        sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: this._isLeavingReaderableReaderMode });
+        this.mm.sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: this._isLeavingReaderableReaderMode });
         if (this._isLeavingReaderableReaderMode) {
           this._isLeavingReaderableReaderMode = false;
         }
         break;
 
       case "pageshow":
         // If a page is loaded from the bfcache, we won't get a "DOMContentLoaded"
         // event, so we need to rely on "pageshow" in this case.
@@ -192,339 +88,63 @@ var AboutReaderListener = {
           this.updateReaderButton();
         }
         break;
       case "DOMContentLoaded":
         this.updateReaderButton();
         break;
 
     }
-  },
+  }
 
   /**
    * NB: this function will update the state of the reader button asynchronously
    * after the next mozAfterPaint call (assuming reader mode is enabled and
    * this is a suitable document). Calling it on things which won't be
    * painted is not going to work.
    */
   updateReaderButton(forceNonArticle) {
     if (!ReaderMode.isEnabledForParseOnLoad || this.isAboutReader ||
-        !content || !(content.document instanceof content.HTMLDocument) ||
-        content.document.mozSyntheticDocument) {
+        !this.content || !(this.content.document instanceof this.content.HTMLDocument) ||
+        this.content.document.mozSyntheticDocument) {
       return;
     }
 
     this.scheduleReadabilityCheckPostPaint(forceNonArticle);
-  },
+  }
 
   cancelPotentialPendingReadabilityCheck() {
     if (this._pendingReadabilityCheck) {
-      removeEventListener("MozAfterPaint", this._pendingReadabilityCheck);
+      this.mm.removeEventListener("MozAfterPaint", this._pendingReadabilityCheck);
       delete this._pendingReadabilityCheck;
     }
-  },
+  }
 
   scheduleReadabilityCheckPostPaint(forceNonArticle) {
     if (this._pendingReadabilityCheck) {
       // We need to stop this check before we re-add one because we don't know
       // if forceNonArticle was true or false last time.
       this.cancelPotentialPendingReadabilityCheck();
     }
     this._pendingReadabilityCheck = this.onPaintWhenWaitedFor.bind(this, forceNonArticle);
-    addEventListener("MozAfterPaint", this._pendingReadabilityCheck);
-  },
+    this.mm.addEventListener("MozAfterPaint", this._pendingReadabilityCheck);
+  }
 
   onPaintWhenWaitedFor(forceNonArticle, event) {
     // In non-e10s, we'll get called for paints other than ours, and so it's
     // possible that this page hasn't been laid out yet, in which case we
     // should wait until we get an event that does relate to our layout. We
-    // determine whether any of our content got painted by checking if there
+    // determine whether any of our this.content got painted by checking if there
     // are any painted rects.
     if (!event.clientRects.length) {
       return;
     }
 
     this.cancelPotentialPendingReadabilityCheck();
     // Only send updates when there are articles; there's no point updating with
     // |false| all the time.
-    if (ReaderMode.isProbablyReaderable(content.document)) {
-      sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
+    if (ReaderMode.isProbablyReaderable(this.content.document)) {
+      this.mm.sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
     } else if (forceNonArticle) {
-      sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
-    }
-  },
-};
-AboutReaderListener.init();
-
-
-var ContentSearchMediator = {
-
-  whitelist: new Set([
-    "about:home",
-    "about:newtab",
-    "about:welcome",
-  ]),
-
-  init(chromeGlobal) {
-    chromeGlobal.addEventListener("ContentSearchClient", this, true, true);
-    addMessageListener("ContentSearch", this);
-    this.init = null;
-  },
-
-  handleEvent(event) {
-    if (this._contentWhitelisted) {
-      this._sendMsg(event.detail.type, event.detail.data);
-    }
-  },
-
-  receiveMessage(msg) {
-    if (msg.data.type == "AddToWhitelist") {
-      for (let uri of msg.data.data) {
-        this.whitelist.add(uri);
-      }
-      this._sendMsg("AddToWhitelistAck");
-      return;
-    }
-    if (this._contentWhitelisted) {
-      this._fireEvent(msg.data.type, msg.data.data);
-    }
-  },
-
-  get _contentWhitelisted() {
-    return this.whitelist.has(content.document.documentURI);
-  },
-
-  _sendMsg(type, data = null) {
-    sendAsyncMessage("ContentSearch", {
-      type,
-      data,
-    });
-  },
-
-  _fireEvent(type, data = null) {
-    let event = Cu.cloneInto({
-      detail: {
-        type,
-        data,
-      },
-    }, content);
-    content.dispatchEvent(new content.CustomEvent("ContentSearchService",
-                                                  event));
-  },
-};
-ContentSearchMediator.init(this);
-
-addMessageListener("PageStyle:Switch", PageStyleHandler);
-addMessageListener("PageStyle:Disable", PageStyleHandler);
-addEventListener("pageshow", PageStyleHandler);
-
-// Keep a reference to the translation content handler to avoid it it being GC'ed.
-var trHandler = null;
-if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) {
-  ChromeUtils.import("resource:///modules/translation/TranslationContentHandler.jsm");
-  trHandler = new TranslationContentHandler(global, docShell);
-}
-
-function gKeywordURIFixup(fixupInfo) {
-  fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
-  if (!fixupInfo.consumer) {
-    return;
-  }
-
-  // Ignore info from other docshells
-  let parent = fixupInfo.consumer.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeRootTreeItem;
-  if (parent != docShell)
-    return;
-
-  let data = {};
-  for (let f of Object.keys(fixupInfo)) {
-    if (f == "consumer" || typeof fixupInfo[f] == "function")
-      continue;
-
-    if (fixupInfo[f] && fixupInfo[f] instanceof Ci.nsIURI) {
-      data[f] = fixupInfo[f].spec;
-    } else {
-      data[f] = fixupInfo[f];
+      this.mm.sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
     }
   }
-
-  sendAsyncMessage("Browser:URIFixup", data);
 }
-Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup");
-addEventListener("unload", () => {
-  Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
-}, false);
-
-addMessageListener("Browser:AppTab", function(message) {
-  if (docShell) {
-    docShell.isAppTab = message.data.isAppTab;
-  }
-});
-
-var WebBrowserChrome = {
-  onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
-    return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
-  },
-
-  // Check whether this URI should load in the current process
-  shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData, aTriggeringPrincipal) {
-    if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData)) {
-      E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, false);
-      return false;
-    }
-
-    return true;
-  },
-
-  shouldLoadURIInThisProcess(aURI) {
-    return E10SUtils.shouldLoadURIInThisProcess(aURI);
-  },
-
-  // Try to reload the currently active or currently loading page in a new process.
-  reloadInFreshProcess(aDocShell, aURI, aReferrer, aTriggeringPrincipal, aLoadFlags) {
-    E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, true, aLoadFlags);
-    return true;
-  }
-};
-
-if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
-  let tabchild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsITabChild);
-  tabchild.webBrowserChrome = WebBrowserChrome;
-}
-
-
-var DOMFullscreenHandler = {
-
-  init() {
-    addMessageListener("DOMFullscreen:Entered", this);
-    addMessageListener("DOMFullscreen:CleanUp", this);
-    addEventListener("MozDOMFullscreen:Request", this);
-    addEventListener("MozDOMFullscreen:Entered", this);
-    addEventListener("MozDOMFullscreen:NewOrigin", this);
-    addEventListener("MozDOMFullscreen:Exit", this);
-    addEventListener("MozDOMFullscreen:Exited", this);
-    this.init = null;
-  },
-
-  receiveMessage(aMessage) {
-    let windowUtils = content && content.windowUtils;
-    switch (aMessage.name) {
-      case "DOMFullscreen:Entered": {
-        this._lastTransactionId = windowUtils.lastTransactionId;
-        if (!windowUtils.handleFullscreenRequests() &&
-            !content.document.fullscreenElement) {
-          // If we don't actually have any pending fullscreen request
-          // to handle, neither we have been in fullscreen, tell the
-          // parent to just exit.
-          sendAsyncMessage("DOMFullscreen:Exit");
-        }
-        break;
-      }
-      case "DOMFullscreen:CleanUp": {
-        // If we've exited fullscreen at this point, no need to record
-        // transaction id or call exit fullscreen. This is especially
-        // important for non-e10s, since in that case, it is possible
-        // that no more paint would be triggered after this point.
-        if (content.document.fullscreenElement && windowUtils) {
-          this._lastTransactionId = windowUtils.lastTransactionId;
-          windowUtils.exitFullscreen();
-        }
-        break;
-      }
-    }
-  },
-
-  handleEvent(aEvent) {
-    switch (aEvent.type) {
-      case "MozDOMFullscreen:Request": {
-        sendAsyncMessage("DOMFullscreen:Request");
-        break;
-      }
-      case "MozDOMFullscreen:NewOrigin": {
-        sendAsyncMessage("DOMFullscreen:NewOrigin", {
-          originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix,
-        });
-        break;
-      }
-      case "MozDOMFullscreen:Exit": {
-        sendAsyncMessage("DOMFullscreen:Exit");
-        break;
-      }
-      case "MozDOMFullscreen:Entered":
-      case "MozDOMFullscreen:Exited": {
-        addEventListener("MozAfterPaint", this);
-        if (!content || !content.document.fullscreenElement) {
-          // If we receive any fullscreen change event, and find we are
-          // actually not in fullscreen, also ask the parent to exit to
-          // ensure that the parent always exits fullscreen when we do.
-          sendAsyncMessage("DOMFullscreen:Exit");
-        }
-        break;
-      }
-      case "MozAfterPaint": {
-        // Only send Painted signal after we actually finish painting
-        // the transition for the fullscreen change.
-        // Note that this._lastTransactionId is not set when in non-e10s
-        // mode, so we need to check that explicitly.
-        if (!this._lastTransactionId ||
-            aEvent.transactionId > this._lastTransactionId) {
-          removeEventListener("MozAfterPaint", this);
-          sendAsyncMessage("DOMFullscreen:Painted");
-        }
-        break;
-      }
-    }
-  }
-};
-DOMFullscreenHandler.init();
-
-var UserContextIdNotifier = {
-  init() {
-    addEventListener("DOMWindowCreated", this);
-    this.init = null;
-  },
-
-  uninit() {
-    removeEventListener("DOMWindowCreated", this);
-  },
-
-  handleEvent(aEvent) {
-    // When the window is created, we want to inform the tabbrowser about
-    // the userContextId in use in order to update the UI correctly.
-    // Just because we cannot change the userContextId from an active docShell,
-    // we don't need to check DOMContentLoaded again.
-    this.uninit();
-
-    // We use the docShell because content.document can have been loaded before
-    // setting the originAttributes.
-    let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
-    let userContextId = loadContext.originAttributes.userContextId;
-
-    sendAsyncMessage("Browser:WindowCreated", { userContextId });
-  }
-};
-
-UserContextIdNotifier.init();
-
-Services.obs.notifyObservers(this, "tab-content-frameloader-created");
-
-addMessageListener("AllowScriptsToClose", () => {
-  content.windowUtils.allowScriptsToClose();
-});
-
-addEventListener("MozAfterPaint", function onFirstPaint() {
-  removeEventListener("MozAfterPaint", onFirstPaint);
-  sendAsyncMessage("Browser:FirstPaint");
-});
-
-// Remove this once bug 1397365 is fixed.
-addEventListener("MozAfterPaint", function onFirstNonBlankPaint() {
-  if (content.document.documentURI == "about:blank" && !content.opener)
-    return;
-  removeEventListener("MozAfterPaint", onFirstNonBlankPaint);
-  sendAsyncMessage("Browser:FirstNonBlankPaint");
-});
-
-addMessageListener("DOM:WebManifest:hasManifestLink", ManifestMessages);
-addMessageListener("DOM:ManifestObtainer:Obtain", ManifestMessages);
-addMessageListener("DOM:Manifest:FireAppInstalledEvent", ManifestMessages);
-addMessageListener("DOM:WebManifest:fetchIcon", ManifestMessages);
rename from browser/modules/BlockedSiteContent.jsm
rename to browser/actors/BlockedSiteChild.jsm
--- a/browser/modules/BlockedSiteContent.jsm
+++ b/browser/actors/BlockedSiteChild.jsm
@@ -1,16 +1,18 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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/Services.jsm");
 
-var EXPORTED_SYMBOLS = ["BlockedSiteContent"];
+var EXPORTED_SYMBOLS = ["BlockedSiteChild"];
+
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 
 ChromeUtils.defineModuleGetter(this, "SafeBrowsing",
                                "resource://gre/modules/SafeBrowsing.jsm");
 
 function getSiteBlockedErrorDetails(docShell) {
   let blockedInfo = {};
   if (docShell.failedChannel) {
     let classifiedChannel = docShell.failedChannel.
@@ -30,31 +32,36 @@ function getSiteBlockedErrorDetails(docS
       blockedInfo = { list: classifiedChannel.matchedList,
                       provider: classifiedChannel.matchedProvider,
                       uri: reportUri.asciiSpec };
     }
   }
   return blockedInfo;
 }
 
-var BlockedSiteContent = {
-  receiveMessage(global, msg) {
+class BlockedSiteChild extends ActorChild {
+  receiveMessage(msg) {
     if (msg.name == "DeceptiveBlockedDetails") {
-      global.sendAsyncMessage("DeceptiveBlockedDetails:Result", {
-        blockedInfo: getSiteBlockedErrorDetails(global.docShell),
+      this.mm.sendAsyncMessage("DeceptiveBlockedDetails:Result", {
+        blockedInfo: getSiteBlockedErrorDetails(this.mm.docShell),
       });
     }
-  },
+  }
 
-  handleEvent(global, aEvent) {
-    if (aEvent.type != "AboutBlockedLoaded") {
-      return;
+  handleEvent(event) {
+    if (event.type == "AboutBlockedLoaded") {
+      this.onAboutBlockedLoaded(event);
+    } else if (event.type == "click" && event.button == 0) {
+      this.onClick(event);
     }
+  }
 
-    let {content} = global;
+  onAboutBlockedLoaded(aEvent) {
+    let global = this.mm;
+    let content = aEvent.target.ownerGlobal;
 
     let blockedInfo = getSiteBlockedErrorDetails(global.docShell);
     let provider = blockedInfo.provider || "";
 
     let doc = content.document;
 
     /**
     * Set error description link in error details.
@@ -115,31 +122,34 @@ var BlockedSiteContent = {
       let el = content.document.getElementById("advisoryDesc");
       el.remove();
       return;
     }
 
     let anchorEl = content.document.getElementById("advisory_provider");
     anchorEl.setAttribute("href", advisoryUrl);
     anchorEl.textContent = advisoryLinkText;
-  },
+  }
 
-  onAboutBlocked(global, targetElement, ownerDoc) {
+  onClick(event) {
+    let ownerDoc = event.target.ownerDocument;
+    if (!ownerDoc) {
+      return;
+    }
+
     var reason = "phishing";
     if (/e=malwareBlocked/.test(ownerDoc.documentURI)) {
       reason = "malware";
     } else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) {
       reason = "unwanted";
     } else if (/e=harmfulBlocked/.test(ownerDoc.documentURI)) {
       reason = "harmful";
     }
 
-    let docShell = ownerDoc.defaultView.docShell;
-
-    global.sendAsyncMessage("Browser:SiteBlockedError", {
+    this.mm.sendAsyncMessage("Browser:SiteBlockedError", {
       location: ownerDoc.location.href,
       reason,
-      elementId: targetElement.getAttribute("id"),
+      elementId: event.target.getAttribute("id"),
       isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
-      blockedInfo: getSiteBlockedErrorDetails(docShell),
+      blockedInfo: getSiteBlockedErrorDetails(ownerDoc.defaultView.docShell),
     });
-  },
-};
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/actors/BrowserTabChild.jsm
@@ -0,0 +1,112 @@
+/* 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 = ["BrowserTabChild"];
+
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
+
+ChromeUtils.defineModuleGetter(this, "E10SUtils",
+                               "resource://gre/modules/E10SUtils.jsm");
+
+class BrowserTabChild extends ActorChild {
+  handleEvent(event) {
+    switch (event.type) {
+    case "DOMWindowCreated":
+      let loadContext = this.mm.docShell.QueryInterface(Ci.nsILoadContext);
+      let userContextId = loadContext.originAttributes.userContextId;
+
+      this.mm.sendAsyncMessage("Browser:WindowCreated", { userContextId });
+      break;
+
+    case "MozAfterPaint":
+      this.mm.sendAsyncMessage("Browser:FirstPaint");
+      break;
+
+    case "MozDOMPointerLock:Entered":
+      this.mm.sendAsyncMessage("PointerLock:Entered", {
+        originNoSuffix: event.target.nodePrincipal.originNoSuffix
+      });
+      break;
+
+    case "MozDOMPointerLock:Exited":
+      this.mm.sendAsyncMessage("PointerLock:Exited");
+      break;
+    }
+  }
+
+  switchDocumentDirection(window = this.content) {
+   // document.dir can also be "auto", in which case it won't change
+    if (window.document.dir == "ltr" || window.document.dir == "") {
+      window.document.dir = "rtl";
+    } else if (window.document.dir == "rtl") {
+      window.document.dir = "ltr";
+    }
+    for (let i = 0; i < window.frames.length; i++) {
+      this.switchDocumentDirection(window.frames[i]);
+    }
+  }
+
+  receiveMessage(message) {
+    switch (message.name) {
+      case "AllowScriptsToClose":
+        this.content.windowUtils.allowScriptsToClose();
+        break;
+
+      case "Browser:AppTab":
+        if (this.docShell) {
+          this.docShell.isAppTab = message.data.isAppTab;
+        }
+        break;
+
+      case "Browser:HasSiblings":
+        try {
+          let tabChild = this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsITabChild);
+          let hasSiblings = message.data;
+          tabChild.hasSiblings = hasSiblings;
+        } catch (e) {
+        }
+        break;
+
+      // XXX(nika): Should we try to call this in the parent process instead?
+      case "Browser:Reload":
+        /* First, we'll try to use the session history object to reload so
+         * that framesets are handled properly. If we're in a special
+         * window (such as view-source) that has no session history, fall
+         * back on using the web navigation's reload method.
+         */
+
+        let webNav = this.docShell.QueryInterface(Ci.nsIWebNavigation);
+        try {
+          if (webNav.sessionHistory) {
+            webNav = webNav.sessionHistory;
+          }
+        } catch (e) {
+        }
+
+        let reloadFlags = message.data.flags;
+        try {
+          E10SUtils.wrapHandlingUserInput(this.content, message.data.handlingUserInput,
+                                          () => webNav.reload(reloadFlags));
+        } catch (e) {
+        }
+        break;
+
+      case "MixedContent:ReenableProtection":
+        this.docShell.mixedContentChannel = null;
+        break;
+
+      case "SwitchDocumentDirection":
+        this.switchDocumentDirection();
+        break;
+
+      case "UpdateCharacterSet":
+        this.docShell.charset = message.data.value;
+        this.docShell.gatherCharsetMenuTelemetry();
+        break;
+    }
+  }
+}
rename from browser/modules/ClickEventHandler.jsm
rename to browser/actors/ClickHandlerChild.jsm
--- a/browser/modules/ClickEventHandler.jsm
+++ b/browser/actors/ClickHandlerChild.jsm
@@ -1,54 +1,40 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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/. */
 
-var EXPORTED_SYMBOLS = ["ClickEventHandler"];
+var EXPORTED_SYMBOLS = ["ClickHandlerChild"];
 
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-ChromeUtils.defineModuleGetter(this, "BlockedSiteContent",
-                               "resource:///modules/BlockedSiteContent.jsm");
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                                "resource://gre/modules/BrowserUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "NetErrorContent",
-                               "resource:///modules/NetErrorContent.jsm");
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "WebNavigationFrames",
                                "resource://gre/modules/WebNavigationFrames.jsm");
 
-class ClickEventHandler {
-  constructor(mm) {
-    this.mm = mm;
-  }
-
+class ClickHandlerChild extends ActorChild {
   handleEvent(event) {
     if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
       return;
     }
 
     let originalTarget = event.originalTarget;
     let ownerDoc = originalTarget.ownerDocument;
     if (!ownerDoc) {
       return;
     }
 
     // Handle click events from about pages
     if (event.button == 0) {
-      if (this.mm.AboutNetAndCertErrorListener.isAboutCertError(ownerDoc)) {
-        NetErrorContent.onCertError(this.mm, originalTarget, ownerDoc.defaultView);
-        return;
-      } else if (ownerDoc.documentURI.startsWith("about:blocked")) {
-        BlockedSiteContent.onAboutBlocked(this.mm, originalTarget, ownerDoc);
-        return;
-      } else if (this.mm.AboutNetAndCertErrorListener.isAboutNetError(ownerDoc)) {
-        NetErrorContent.onAboutNetError(this.mm, event, ownerDoc.documentURI);
+      if (ownerDoc.documentURI.startsWith("about:blocked")) {
         return;
       }
     }
 
     let [href, node, principal] = this._hrefAndLinkNodeForClickEvent(event);
 
     // get referrer attribute from clicked link and parse it
     // if per element referrer is enabled, the element referrer overrules
copy from browser/base/content/tab-content.js
copy to browser/actors/ContentSearchChild.jsm
--- a/browser/base/content/tab-content.js
+++ b/browser/actors/ContentSearchChild.jsm
@@ -1,530 +1,37 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
-
-/* This content script contains code that requires a tab browser. */
-
-/* eslint-env mozilla/frame-script */
-
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm");
+"use strict";
 
-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");
-
-// TabChildGlobal
-var global = this;
-
-
-addEventListener("MozDOMPointerLock:Entered", function(aEvent) {
-  sendAsyncMessage("PointerLock:Entered", {
-    originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix
-  });
-});
+var EXPORTED_SYMBOLS = ["ContentSearchChild"];
 
-addEventListener("MozDOMPointerLock:Exited", function(aEvent) {
-  sendAsyncMessage("PointerLock:Exited");
-});
-
-
-addMessageListener("Browser:HideSessionRestoreButton", function(message) {
-  // Hide session restore button on about:home
-  let doc = content.document;
-  let container;
-  if (doc.documentURI.toLowerCase() == "about:home" &&
-      (container = doc.getElementById("sessionRestoreContainer"))) {
-    container.hidden = true;
-  }
-});
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 
-if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
-  addMessageListener("Browser:HasSiblings", function(message) {
-    let tabChild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsITabChild);
-    let hasSiblings = message.data;
-    tabChild.hasSiblings = hasSiblings;
-  });
-}
-
-// XXX(nika): Should we try to call this in the parent process instead?
-addMessageListener("Browser:Reload", function(message) {
-  /* First, we'll try to use the session history object to reload so
-   * that framesets are handled properly. If we're in a special
-   * window (such as view-source) that has no session history, fall
-   * back on using the web navigation's reload method.
-   */
-
-  let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
-  try {
-    if (webNav.sessionHistory) {
-      webNav = webNav.sessionHistory;
-    }
-  } catch (e) {
+class ContentSearchChild extends ActorChild {
+  handleEvent(event) {
+    this._sendMsg(event.detail.type, event.detail.data);
   }
 
-  let reloadFlags = message.data.flags;
-  try {
-    E10SUtils.wrapHandlingUserInput(content, message.data.handlingUserInput,
-                                    () => webNav.reload(reloadFlags));
-  } catch (e) {
-  }
-});
-
-addMessageListener("MixedContent:ReenableProtection", function() {
-  docShell.mixedContentChannel = null;
-});
-
-XPCOMUtils.defineLazyProxy(this, "LightweightThemeChildHelper",
-  "resource:///modules/LightweightThemeChildHelper.jsm");
-
-XPCOMUtils.defineLazyProxy(this, "ManifestMessages", () => {
-  let tmp = {};
-  ChromeUtils.import("resource://gre/modules/ManifestMessages.jsm", tmp);
-  return new tmp.ManifestMessages(global);
-});
-
-let themeablePagesWhitelist = new Set([
-  "about:home",
-  "about:newtab",
-  "about:welcome",
-]);
-
-addEventListener("pageshow", function({ originalTarget }) {
-  if (originalTarget.defaultView == content && themeablePagesWhitelist.has(content.document.documentURI)) {
-    LightweightThemeChildHelper.listen(themeablePagesWhitelist);
-    LightweightThemeChildHelper.update(chromeOuterWindowID, content);
+  receiveMessage(msg) {
+    this._fireEvent(msg.data.type, msg.data.data);
   }
-}, false, true);
-
-var AboutReaderListener = {
-
-  _articlePromise: null,
-
-  _isLeavingReaderableReaderMode: false,
-
-  init() {
-    addEventListener("AboutReaderContentLoaded", this, false, true);
-    addEventListener("DOMContentLoaded", this, false);
-    addEventListener("pageshow", this, false);
-    addEventListener("pagehide", this, false);
-    addMessageListener("Reader:ToggleReaderMode", this);
-    addMessageListener("Reader:PushState", this);
-    this.init = null;
-  },
-
-  receiveMessage(message) {
-    switch (message.name) {
-      case "Reader:ToggleReaderMode":
-        if (!this.isAboutReader) {
-          this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
-          ReaderMode.enterReaderMode(docShell, content);
-        } else {
-          this._isLeavingReaderableReaderMode = this.isReaderableAboutReader;
-          ReaderMode.leaveReaderMode(docShell, content);
-        }
-        break;
-
-      case "Reader:PushState":
-        this.updateReaderButton(!!(message.data && message.data.isArticle));
-        break;
-    }
-  },
-
-  get isAboutReader() {
-    if (!content) {
-      return false;
-    }
-    return content.document.documentURI.startsWith("about:reader");
-  },
-
-  get isReaderableAboutReader() {
-    return this.isAboutReader &&
-      !content.document.documentElement.dataset.isError;
-  },
-
-  handleEvent(aEvent) {
-    if (aEvent.originalTarget.defaultView != content) {
-      return;
-    }
-
-    switch (aEvent.type) {
-      case "AboutReaderContentLoaded":
-        if (!this.isAboutReader) {
-          return;
-        }
-
-        if (content.document.body) {
-          // Update the toolbar icon to show the "reader active" icon.
-          sendAsyncMessage("Reader:UpdateReaderButton");
-          new AboutReader(global, content, this._articlePromise);
-          this._articlePromise = null;
-        }
-        break;
-
-      case "pagehide":
-        this.cancelPotentialPendingReadabilityCheck();
-        // this._isLeavingReaderableReaderMode is used here to keep the Reader Mode icon
-        // visible in the location bar when transitioning from reader-mode page
-        // back to the readable source page.
-        sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: this._isLeavingReaderableReaderMode });
-        if (this._isLeavingReaderableReaderMode) {
-          this._isLeavingReaderableReaderMode = false;
-        }
-        break;
-
-      case "pageshow":
-        // If a page is loaded from the bfcache, we won't get a "DOMContentLoaded"
-        // event, so we need to rely on "pageshow" in this case.
-        if (aEvent.persisted) {
-          this.updateReaderButton();
-        }
-        break;
-      case "DOMContentLoaded":
-        this.updateReaderButton();
-        break;
-
-    }
-  },
-
-  /**
-   * NB: this function will update the state of the reader button asynchronously
-   * after the next mozAfterPaint call (assuming reader mode is enabled and
-   * this is a suitable document). Calling it on things which won't be
-   * painted is not going to work.
-   */
-  updateReaderButton(forceNonArticle) {
-    if (!ReaderMode.isEnabledForParseOnLoad || this.isAboutReader ||
-        !content || !(content.document instanceof content.HTMLDocument) ||
-        content.document.mozSyntheticDocument) {
-      return;
-    }
-
-    this.scheduleReadabilityCheckPostPaint(forceNonArticle);
-  },
-
-  cancelPotentialPendingReadabilityCheck() {
-    if (this._pendingReadabilityCheck) {
-      removeEventListener("MozAfterPaint", this._pendingReadabilityCheck);
-      delete this._pendingReadabilityCheck;
-    }
-  },
-
-  scheduleReadabilityCheckPostPaint(forceNonArticle) {
-    if (this._pendingReadabilityCheck) {
-      // We need to stop this check before we re-add one because we don't know
-      // if forceNonArticle was true or false last time.
-      this.cancelPotentialPendingReadabilityCheck();
-    }
-    this._pendingReadabilityCheck = this.onPaintWhenWaitedFor.bind(this, forceNonArticle);
-    addEventListener("MozAfterPaint", this._pendingReadabilityCheck);
-  },
-
-  onPaintWhenWaitedFor(forceNonArticle, event) {
-    // In non-e10s, we'll get called for paints other than ours, and so it's
-    // possible that this page hasn't been laid out yet, in which case we
-    // should wait until we get an event that does relate to our layout. We
-    // determine whether any of our content got painted by checking if there
-    // are any painted rects.
-    if (!event.clientRects.length) {
-      return;
-    }
-
-    this.cancelPotentialPendingReadabilityCheck();
-    // Only send updates when there are articles; there's no point updating with
-    // |false| all the time.
-    if (ReaderMode.isProbablyReaderable(content.document)) {
-      sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
-    } else if (forceNonArticle) {
-      sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
-    }
-  },
-};
-AboutReaderListener.init();
-
-
-var ContentSearchMediator = {
-
-  whitelist: new Set([
-    "about:home",
-    "about:newtab",
-    "about:welcome",
-  ]),
-
-  init(chromeGlobal) {
-    chromeGlobal.addEventListener("ContentSearchClient", this, true, true);
-    addMessageListener("ContentSearch", this);
-    this.init = null;
-  },
-
-  handleEvent(event) {
-    if (this._contentWhitelisted) {
-      this._sendMsg(event.detail.type, event.detail.data);
-    }
-  },
-
-  receiveMessage(msg) {
-    if (msg.data.type == "AddToWhitelist") {
-      for (let uri of msg.data.data) {
-        this.whitelist.add(uri);
-      }
-      this._sendMsg("AddToWhitelistAck");
-      return;
-    }
-    if (this._contentWhitelisted) {
-      this._fireEvent(msg.data.type, msg.data.data);
-    }
-  },
-
-  get _contentWhitelisted() {
-    return this.whitelist.has(content.document.documentURI);
-  },
 
   _sendMsg(type, data = null) {
-    sendAsyncMessage("ContentSearch", {
+    this.mm.sendAsyncMessage("ContentSearch", {
       type,
       data,
     });
-  },
+  }
 
   _fireEvent(type, data = null) {
     let event = Cu.cloneInto({
       detail: {
         type,
         data,
       },
-    }, content);
-    content.dispatchEvent(new content.CustomEvent("ContentSearchService",
-                                                  event));
-  },
-};
-ContentSearchMediator.init(this);
-
-addMessageListener("PageStyle:Switch", PageStyleHandler);
-addMessageListener("PageStyle:Disable", PageStyleHandler);
-addEventListener("pageshow", PageStyleHandler);
-
-// Keep a reference to the translation content handler to avoid it it being GC'ed.
-var trHandler = null;
-if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) {
-  ChromeUtils.import("resource:///modules/translation/TranslationContentHandler.jsm");
-  trHandler = new TranslationContentHandler(global, docShell);
-}
-
-function gKeywordURIFixup(fixupInfo) {
-  fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
-  if (!fixupInfo.consumer) {
-    return;
-  }
-
-  // Ignore info from other docshells
-  let parent = fixupInfo.consumer.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeRootTreeItem;
-  if (parent != docShell)
-    return;
-
-  let data = {};
-  for (let f of Object.keys(fixupInfo)) {
-    if (f == "consumer" || typeof fixupInfo[f] == "function")
-      continue;
-
-    if (fixupInfo[f] && fixupInfo[f] instanceof Ci.nsIURI) {
-      data[f] = fixupInfo[f].spec;
-    } else {
-      data[f] = fixupInfo[f];
-    }
+    }, this.content);
+    this.content.dispatchEvent(new this.content.CustomEvent("ContentSearchService",
+                                                            event));
   }
-
-  sendAsyncMessage("Browser:URIFixup", data);
-}
-Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup");
-addEventListener("unload", () => {
-  Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
-}, false);
-
-addMessageListener("Browser:AppTab", function(message) {
-  if (docShell) {
-    docShell.isAppTab = message.data.isAppTab;
-  }
-});
-
-var WebBrowserChrome = {
-  onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
-    return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
-  },
-
-  // Check whether this URI should load in the current process
-  shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData, aTriggeringPrincipal) {
-    if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData)) {
-      E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, false);
-      return false;
-    }
-
-    return true;
-  },
-
-  shouldLoadURIInThisProcess(aURI) {
-    return E10SUtils.shouldLoadURIInThisProcess(aURI);
-  },
-
-  // Try to reload the currently active or currently loading page in a new process.
-  reloadInFreshProcess(aDocShell, aURI, aReferrer, aTriggeringPrincipal, aLoadFlags) {
-    E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, true, aLoadFlags);
-    return true;
-  }
-};
-
-if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
-  let tabchild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsITabChild);
-  tabchild.webBrowserChrome = WebBrowserChrome;
 }
-
-
-var DOMFullscreenHandler = {
-
-  init() {
-    addMessageListener("DOMFullscreen:Entered", this);
-    addMessageListener("DOMFullscreen:CleanUp", this);
-    addEventListener("MozDOMFullscreen:Request", this);
-    addEventListener("MozDOMFullscreen:Entered", this);
-    addEventListener("MozDOMFullscreen:NewOrigin", this);
-    addEventListener("MozDOMFullscreen:Exit", this);
-    addEventListener("MozDOMFullscreen:Exited", this);
-    this.init = null;
-  },
-
-  receiveMessage(aMessage) {
-    let windowUtils = content && content.windowUtils;
-    switch (aMessage.name) {
-      case "DOMFullscreen:Entered": {
-        this._lastTransactionId = windowUtils.lastTransactionId;
-        if (!windowUtils.handleFullscreenRequests() &&
-            !content.document.fullscreenElement) {
-          // If we don't actually have any pending fullscreen request
-          // to handle, neither we have been in fullscreen, tell the
-          // parent to just exit.
-          sendAsyncMessage("DOMFullscreen:Exit");
-        }
-        break;
-      }
-      case "DOMFullscreen:CleanUp": {
-        // If we've exited fullscreen at this point, no need to record
-        // transaction id or call exit fullscreen. This is especially
-        // important for non-e10s, since in that case, it is possible
-        // that no more paint would be triggered after this point.
-        if (content.document.fullscreenElement && windowUtils) {
-          this._lastTransactionId = windowUtils.lastTransactionId;
-          windowUtils.exitFullscreen();
-        }
-        break;
-      }
-    }
-  },
-
-  handleEvent(aEvent) {
-    switch (aEvent.type) {
-      case "MozDOMFullscreen:Request": {
-        sendAsyncMessage("DOMFullscreen:Request");
-        break;
-      }
-      case "MozDOMFullscreen:NewOrigin": {
-        sendAsyncMessage("DOMFullscreen:NewOrigin", {
-          originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix,
-        });
-        break;
-      }
-      case "MozDOMFullscreen:Exit": {
-        sendAsyncMessage("DOMFullscreen:Exit");
-        break;
-      }
-      case "MozDOMFullscreen:Entered":
-      case "MozDOMFullscreen:Exited": {
-        addEventListener("MozAfterPaint", this);
-        if (!content || !content.document.fullscreenElement) {
-          // If we receive any fullscreen change event, and find we are
-          // actually not in fullscreen, also ask the parent to exit to
-          // ensure that the parent always exits fullscreen when we do.
-          sendAsyncMessage("DOMFullscreen:Exit");
-        }
-        break;
-      }
-      case "MozAfterPaint": {
-        // Only send Painted signal after we actually finish painting
-        // the transition for the fullscreen change.
-        // Note that this._lastTransactionId is not set when in non-e10s
-        // mode, so we need to check that explicitly.
-        if (!this._lastTransactionId ||
-            aEvent.transactionId > this._lastTransactionId) {
-          removeEventListener("MozAfterPaint", this);
-          sendAsyncMessage("DOMFullscreen:Painted");
-        }
-        break;
-      }
-    }
-  }
-};
-DOMFullscreenHandler.init();
-
-var UserContextIdNotifier = {
-  init() {
-    addEventListener("DOMWindowCreated", this);
-    this.init = null;
-  },
-
-  uninit() {
-    removeEventListener("DOMWindowCreated", this);
-  },
-
-  handleEvent(aEvent) {
-    // When the window is created, we want to inform the tabbrowser about
-    // the userContextId in use in order to update the UI correctly.
-    // Just because we cannot change the userContextId from an active docShell,
-    // we don't need to check DOMContentLoaded again.
-    this.uninit();
-
-    // We use the docShell because content.document can have been loaded before
-    // setting the originAttributes.
-    let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
-    let userContextId = loadContext.originAttributes.userContextId;
-
-    sendAsyncMessage("Browser:WindowCreated", { userContextId });
-  }
-};
-
-UserContextIdNotifier.init();
-
-Services.obs.notifyObservers(this, "tab-content-frameloader-created");
-
-addMessageListener("AllowScriptsToClose", () => {
-  content.windowUtils.allowScriptsToClose();
-});
-
-addEventListener("MozAfterPaint", function onFirstPaint() {
-  removeEventListener("MozAfterPaint", onFirstPaint);
-  sendAsyncMessage("Browser:FirstPaint");
-});
-
-// Remove this once bug 1397365 is fixed.
-addEventListener("MozAfterPaint", function onFirstNonBlankPaint() {
-  if (content.document.documentURI == "about:blank" && !content.opener)
-    return;
-  removeEventListener("MozAfterPaint", onFirstNonBlankPaint);
-  sendAsyncMessage("Browser:FirstNonBlankPaint");
-});
-
-addMessageListener("DOM:WebManifest:hasManifestLink", ManifestMessages);
-addMessageListener("DOM:ManifestObtainer:Obtain", ManifestMessages);
-addMessageListener("DOM:Manifest:FireAppInstalledEvent", ManifestMessages);
-addMessageListener("DOM:WebManifest:fetchIcon", ManifestMessages);
rename from browser/modules/ContextMenu.jsm
rename to browser/actors/ContextMenuChild.jsm
--- a/browser/modules/ContextMenu.jsm
+++ b/browser/actors/ContextMenuChild.jsm
@@ -1,21 +1,23 @@
 /* -*- mode: js; indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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 = ["ContextMenu"];
+var EXPORTED_SYMBOLS = ["ContextMenuChild"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
+
 XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   E10SUtils: "resource://gre/modules/E10SUtils.jsm",
   BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
   findAllCssSelectors: "resource://gre/modules/css-selector.js",
   SpellCheckHelper: "resource://gre/modules/InlineSpellChecker.jsm",
   LoginManagerContent: "resource://gre/modules/LoginManagerContent.jsm",
@@ -29,24 +31,24 @@ XPCOMUtils.defineLazyGetter(this, "PageM
   ChromeUtils.import("resource://gre/modules/PageMenu.jsm", tmp);
   return new tmp.PageMenuChild();
 });
 
 const messageListeners = {
   "ContextMenu:BookmarkFrame": function(aMessage) {
     let frame = this.getTarget(aMessage).ownerDocument;
 
-    this.global.sendAsyncMessage("ContextMenu:BookmarkFrame:Result",
-                                 { title: frame.title });
+    this.mm.sendAsyncMessage("ContextMenu:BookmarkFrame:Result",
+                             { title: frame.title });
   },
 
   "ContextMenu:Canvas:ToBlobURL": function(aMessage) {
     this.getTarget(aMessage).toBlob((blob) => {
       let blobURL = URL.createObjectURL(blob);
-      this.global.sendAsyncMessage("ContextMenu:Canvas:ToBlobURL:Result", { blobURL });
+      this.mm.sendAsyncMessage("ContextMenu:Canvas:ToBlobURL:Result", { blobURL });
     });
   },
 
   "ContextMenu:DoCustomCommand": function(aMessage) {
     E10SUtils.wrapHandlingUserInput(
       this.content,
       aMessage.data.handlingUserInput,
       () => PageMenuChild.executeMenu(aMessage.data.generatedItemId)
@@ -163,31 +165,31 @@ const messageListeners = {
 
     if (isURLEncoded) {
       postData = formData.join("&");
     } else {
       let separator = spec.includes("?") ? "&" : "?";
       spec += separator + formData.join("&");
     }
 
-    this.global.sendAsyncMessage("ContextMenu:SearchFieldBookmarkData:Result",
-                                 { spec, title, postData, charset });
+    this.mm.sendAsyncMessage("ContextMenu:SearchFieldBookmarkData:Result",
+                             { spec, title, postData, charset });
   },
 
   "ContextMenu:SaveVideoFrameAsImage": function(aMessage) {
     let video = this.getTarget(aMessage);
     let canvas = this.content.document.createElementNS("http://www.w3.org/1999/xhtml",
                                                        "canvas");
     canvas.width = video.videoWidth;
     canvas.height = video.videoHeight;
 
     let ctxDraw = canvas.getContext("2d");
     ctxDraw.drawImage(video, 0, 0);
 
-    this.global.sendAsyncMessage("ContextMenu:SaveVideoFrameAsImage:Result", {
+    this.mm.sendAsyncMessage("ContextMenu:SaveVideoFrameAsImage:Result", {
       dataURL: canvas.toDataURL("image/jpeg", ""),
     });
   },
 
   "ContextMenu:SetAsDesktopBackground": function(aMessage) {
     let target = this.getTarget(aMessage);
 
     // Paranoia: check disableSetDesktopBackground again, in case the
@@ -201,45 +203,58 @@ const messageListeners = {
         let canvas = this.content.document.createElement("canvas");
         canvas.width = target.naturalWidth;
         canvas.height = target.naturalHeight;
         let ctx = canvas.getContext("2d");
         ctx.drawImage(target, 0, 0);
         let dataUrl = canvas.toDataURL();
         let url = (new URL(target.ownerDocument.location.href)).pathname;
         let imageName = url.substr(url.lastIndexOf("/") + 1);
-        this.global.sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result",
-                                     { dataUrl, imageName });
+        this.mm.sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result",
+                                 { dataUrl, imageName });
       } catch (e) {
         Cu.reportError(e);
         disable = true;
       }
     }
 
     if (disable) {
-      this.global.sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result",
-                                   { disable });
+      this.mm.sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result",
+                               { disable });
     }
   },
 };
 
-class ContextMenu {
+let contextMenus = new WeakMap();
+
+class ContextMenuChild extends ActorChild {
   // PUBLIC
   constructor(global) {
+    super(global);
+
+    contextMenus.set(global, this);
+
     this.target = null;
     this.context = null;
-    this.global = global;
-    this.content = global.content;
     this.lastMenuTarget = null;
 
     Object.keys(messageListeners).forEach(key =>
       global.addMessageListener(key, messageListeners[key].bind(this))
     );
   }
 
+  static getTarget(global, message, key) {
+    return contextMenus.get(global).getTarget(message, key);
+  }
+
+  static getLastTarget(global) {
+    let contextMenu = contextMenus.get(global);
+    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
    * @param  {String} aKey     Key for the target on aMessage.objects
    * @return {Object}          Context menu target
    */
@@ -484,17 +499,17 @@ class ContextMenu {
       referrer,
       referrerPolicy
     } = doc;
     docLocation = docLocation && docLocation.spec;
     let frameOuterWindowID = WebNavigationFrames.getFrameId(doc.defaultView);
     let loginFillInfo = LoginManagerContent.getFieldContext(aEvent.composedTarget);
 
     // The same-origin check will be done in nsContextMenu.openLinkInTab.
-    let parentAllowsMixedContent = !!this.global.docShell.mixedContentChannel;
+    let parentAllowsMixedContent = !!this.docShell.mixedContentChannel;
 
     // Get referrer attribute from clicked link and parse it
     let referrerAttrValue =
       Services.netUtils.parseAttributePolicyString(aEvent.composedTarget.
                                                    getAttribute("referrerpolicy"));
 
     if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
       referrerPolicy = referrerAttrValue;
@@ -523,17 +538,17 @@ class ContextMenu {
 
         try {
           contentDisposition = props.get("content-disposition", Ci.nsISupportsCString).data;
         } catch (e) {}
       } catch (e) {}
     }
 
     let selectionInfo = BrowserUtils.getSelectionDetails(this.content);
-    let loadContext = this.global.docShell.QueryInterface(Ci.nsILoadContext);
+    let loadContext = this.docShell.QueryInterface(Ci.nsILoadContext);
     let userContextId = loadContext.originAttributes.userContextId;
     let popupNodeSelectors = findAllCssSelectors(aEvent.composedTarget);
 
     this._setContext(aEvent);
     let context = this.context;
     this.target = context.target;
 
     let spellInfo = null;
@@ -547,24 +562,24 @@ class ContextMenu {
     }
 
     let isRemote = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
 
     if (isRemote) {
       editFlags = SpellCheckHelper.isEditable(aEvent.composedTarget, this.content);
 
       if (editFlags & SpellCheckHelper.SPELLCHECKABLE) {
-        spellInfo = InlineSpellCheckerContent.initContextMenu(aEvent, editFlags, this.global);
+        spellInfo = InlineSpellCheckerContent.initContextMenu(aEvent, editFlags, this.mm);
       }
 
       // Set the event target first as the copy image command needs it to
       // determine what was context-clicked on. Then, update the state of the
       // commands on the context menu.
-      this.global.docShell.contentViewer.QueryInterface(Ci.nsIContentViewerEdit)
-                          .setCommandNode(aEvent.composedTarget);
+      this.docShell.contentViewer.QueryInterface(Ci.nsIContentViewerEdit)
+                   .setCommandNode(aEvent.composedTarget);
       aEvent.composedTarget.ownerGlobal.updateCommands("contentcontextmenu");
 
       customMenuItems = PageMenuChild.build(aEvent.composedTarget);
       principal = doc.nodePrincipal;
     }
 
     let data = {
       context,
@@ -585,21 +600,21 @@ class ContextMenu {
       contentDisposition,
       frameOuterWindowID,
       popupNodeSelectors,
       disableSetDesktopBg,
       parentAllowsMixedContent,
     };
 
     if (isRemote) {
-      this.global.sendAsyncMessage("contextmenu", data, {
+      this.mm.sendAsyncMessage("contextmenu", data, {
         targetAsCPOW,
       });
     } else {
-      let browser = this.global.docShell.chromeEventHandler;
+      let browser = this.docShell.chromeEventHandler;
       let mainWin = browser.ownerGlobal;
 
       data.documentURIObject = doc.documentURIObject;
       data.disableSetDesktopBackground = data.disableSetDesktopBg;
       delete data.disableSetDesktopBg;
 
       data.context.targetAsCPOW = targetAsCPOW;
 
copy from browser/base/content/tab-content.js
copy to browser/actors/DOMFullscreenChild.jsm
--- a/browser/base/content/tab-content.js
+++ b/browser/actors/DOMFullscreenChild.jsm
@@ -1,530 +1,81 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
-
-/* This content script contains code that requires a tab browser. */
-
-/* eslint-env mozilla/frame-script */
-
-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");
-
-// TabChildGlobal
-var global = this;
-
-
-addEventListener("MozDOMPointerLock:Entered", function(aEvent) {
-  sendAsyncMessage("PointerLock:Entered", {
-    originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix
-  });
-});
-
-addEventListener("MozDOMPointerLock:Exited", function(aEvent) {
-  sendAsyncMessage("PointerLock:Exited");
-});
-
-
-addMessageListener("Browser:HideSessionRestoreButton", function(message) {
-  // Hide session restore button on about:home
-  let doc = content.document;
-  let container;
-  if (doc.documentURI.toLowerCase() == "about:home" &&
-      (container = doc.getElementById("sessionRestoreContainer"))) {
-    container.hidden = true;
-  }
-});
-
-if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
-  addMessageListener("Browser:HasSiblings", function(message) {
-    let tabChild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsITabChild);
-    let hasSiblings = message.data;
-    tabChild.hasSiblings = hasSiblings;
-  });
-}
-
-// XXX(nika): Should we try to call this in the parent process instead?
-addMessageListener("Browser:Reload", function(message) {
-  /* First, we'll try to use the session history object to reload so
-   * that framesets are handled properly. If we're in a special
-   * window (such as view-source) that has no session history, fall
-   * back on using the web navigation's reload method.
-   */
-
-  let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
-  try {
-    if (webNav.sessionHistory) {
-      webNav = webNav.sessionHistory;
-    }
-  } catch (e) {
-  }
-
-  let reloadFlags = message.data.flags;
-  try {
-    E10SUtils.wrapHandlingUserInput(content, message.data.handlingUserInput,
-                                    () => webNav.reload(reloadFlags));
-  } catch (e) {
-  }
-});
-
-addMessageListener("MixedContent:ReenableProtection", function() {
-  docShell.mixedContentChannel = null;
-});
-
-XPCOMUtils.defineLazyProxy(this, "LightweightThemeChildHelper",
-  "resource:///modules/LightweightThemeChildHelper.jsm");
-
-XPCOMUtils.defineLazyProxy(this, "ManifestMessages", () => {
-  let tmp = {};
-  ChromeUtils.import("resource://gre/modules/ManifestMessages.jsm", tmp);
-  return new tmp.ManifestMessages(global);
-});
+"use strict";
 
-let themeablePagesWhitelist = new Set([
-  "about:home",
-  "about:newtab",
-  "about:welcome",
-]);
-
-addEventListener("pageshow", function({ originalTarget }) {
-  if (originalTarget.defaultView == content && themeablePagesWhitelist.has(content.document.documentURI)) {
-    LightweightThemeChildHelper.listen(themeablePagesWhitelist);
-    LightweightThemeChildHelper.update(chromeOuterWindowID, content);
-  }
-}, false, true);
-
-var AboutReaderListener = {
-
-  _articlePromise: null,
-
-  _isLeavingReaderableReaderMode: false,
-
-  init() {
-    addEventListener("AboutReaderContentLoaded", this, false, true);
-    addEventListener("DOMContentLoaded", this, false);
-    addEventListener("pageshow", this, false);
-    addEventListener("pagehide", this, false);
-    addMessageListener("Reader:ToggleReaderMode", this);
-    addMessageListener("Reader:PushState", this);
-    this.init = null;
-  },
-
-  receiveMessage(message) {
-    switch (message.name) {
-      case "Reader:ToggleReaderMode":
-        if (!this.isAboutReader) {
-          this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
-          ReaderMode.enterReaderMode(docShell, content);
-        } else {
-          this._isLeavingReaderableReaderMode = this.isReaderableAboutReader;
-          ReaderMode.leaveReaderMode(docShell, content);
-        }
-        break;
-
-      case "Reader:PushState":
-        this.updateReaderButton(!!(message.data && message.data.isArticle));
-        break;
-    }
-  },
-
-  get isAboutReader() {
-    if (!content) {
-      return false;
-    }
-    return content.document.documentURI.startsWith("about:reader");
-  },
-
-  get isReaderableAboutReader() {
-    return this.isAboutReader &&
-      !content.document.documentElement.dataset.isError;
-  },
-
-  handleEvent(aEvent) {
-    if (aEvent.originalTarget.defaultView != content) {
-      return;
-    }
-
-    switch (aEvent.type) {
-      case "AboutReaderContentLoaded":
-        if (!this.isAboutReader) {
-          return;
-        }
-
-        if (content.document.body) {
-          // Update the toolbar icon to show the "reader active" icon.
-          sendAsyncMessage("Reader:UpdateReaderButton");
-          new AboutReader(global, content, this._articlePromise);
-          this._articlePromise = null;
-        }
-        break;
-
-      case "pagehide":
-        this.cancelPotentialPendingReadabilityCheck();
-        // this._isLeavingReaderableReaderMode is used here to keep the Reader Mode icon
-        // visible in the location bar when transitioning from reader-mode page
-        // back to the readable source page.
-        sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: this._isLeavingReaderableReaderMode });
-        if (this._isLeavingReaderableReaderMode) {
-          this._isLeavingReaderableReaderMode = false;
-        }
-        break;
-
-      case "pageshow":
-        // If a page is loaded from the bfcache, we won't get a "DOMContentLoaded"
-        // event, so we need to rely on "pageshow" in this case.
-        if (aEvent.persisted) {
-          this.updateReaderButton();
-        }
-        break;
-      case "DOMContentLoaded":
-        this.updateReaderButton();
-        break;
-
-    }
-  },
+var EXPORTED_SYMBOLS = ["DOMFullscreenChild"];
 
-  /**
-   * NB: this function will update the state of the reader button asynchronously
-   * after the next mozAfterPaint call (assuming reader mode is enabled and
-   * this is a suitable document). Calling it on things which won't be
-   * painted is not going to work.
-   */
-  updateReaderButton(forceNonArticle) {
-    if (!ReaderMode.isEnabledForParseOnLoad || this.isAboutReader ||
-        !content || !(content.document instanceof content.HTMLDocument) ||
-        content.document.mozSyntheticDocument) {
-      return;
-    }
-
-    this.scheduleReadabilityCheckPostPaint(forceNonArticle);
-  },
-
-  cancelPotentialPendingReadabilityCheck() {
-    if (this._pendingReadabilityCheck) {
-      removeEventListener("MozAfterPaint", this._pendingReadabilityCheck);
-      delete this._pendingReadabilityCheck;
-    }
-  },
-
-  scheduleReadabilityCheckPostPaint(forceNonArticle) {
-    if (this._pendingReadabilityCheck) {
-      // We need to stop this check before we re-add one because we don't know
-      // if forceNonArticle was true or false last time.
-      this.cancelPotentialPendingReadabilityCheck();
-    }
-    this._pendingReadabilityCheck = this.onPaintWhenWaitedFor.bind(this, forceNonArticle);
-    addEventListener("MozAfterPaint", this._pendingReadabilityCheck);
-  },
-
-  onPaintWhenWaitedFor(forceNonArticle, event) {
-    // In non-e10s, we'll get called for paints other than ours, and so it's
-    // possible that this page hasn't been laid out yet, in which case we
-    // should wait until we get an event that does relate to our layout. We
-    // determine whether any of our content got painted by checking if there
-    // are any painted rects.
-    if (!event.clientRects.length) {
-      return;
-    }
-
-    this.cancelPotentialPendingReadabilityCheck();
-    // Only send updates when there are articles; there's no point updating with
-    // |false| all the time.
-    if (ReaderMode.isProbablyReaderable(content.document)) {
-      sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
-    } else if (forceNonArticle) {
-      sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
-    }
-  },
-};
-AboutReaderListener.init();
-
-
-var ContentSearchMediator = {
-
-  whitelist: new Set([
-    "about:home",
-    "about:newtab",
-    "about:welcome",
-  ]),
-
-  init(chromeGlobal) {
-    chromeGlobal.addEventListener("ContentSearchClient", this, true, true);
-    addMessageListener("ContentSearch", this);
-    this.init = null;
-  },
-
-  handleEvent(event) {
-    if (this._contentWhitelisted) {
-      this._sendMsg(event.detail.type, event.detail.data);
-    }
-  },
-
-  receiveMessage(msg) {
-    if (msg.data.type == "AddToWhitelist") {
-      for (let uri of msg.data.data) {
-        this.whitelist.add(uri);
-      }
-      this._sendMsg("AddToWhitelistAck");
-      return;
-    }
-    if (this._contentWhitelisted) {
-      this._fireEvent(msg.data.type, msg.data.data);
-    }
-  },
-
-  get _contentWhitelisted() {
-    return this.whitelist.has(content.document.documentURI);
-  },
-
-  _sendMsg(type, data = null) {
-    sendAsyncMessage("ContentSearch", {
-      type,
-      data,
-    });
-  },
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 
-  _fireEvent(type, data = null) {
-    let event = Cu.cloneInto({
-      detail: {
-        type,
-        data,
-      },
-    }, content);
-    content.dispatchEvent(new content.CustomEvent("ContentSearchService",
-                                                  event));
-  },
-};
-ContentSearchMediator.init(this);
-
-addMessageListener("PageStyle:Switch", PageStyleHandler);
-addMessageListener("PageStyle:Disable", PageStyleHandler);
-addEventListener("pageshow", PageStyleHandler);
-
-// Keep a reference to the translation content handler to avoid it it being GC'ed.
-var trHandler = null;
-if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) {
-  ChromeUtils.import("resource:///modules/translation/TranslationContentHandler.jsm");
-  trHandler = new TranslationContentHandler(global, docShell);
-}
-
-function gKeywordURIFixup(fixupInfo) {
-  fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
-  if (!fixupInfo.consumer) {
-    return;
-  }
-
-  // Ignore info from other docshells
-  let parent = fixupInfo.consumer.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeRootTreeItem;
-  if (parent != docShell)
-    return;
-
-  let data = {};
-  for (let f of Object.keys(fixupInfo)) {
-    if (f == "consumer" || typeof fixupInfo[f] == "function")
-      continue;
-
-    if (fixupInfo[f] && fixupInfo[f] instanceof Ci.nsIURI) {
-      data[f] = fixupInfo[f].spec;
-    } else {
-      data[f] = fixupInfo[f];
-    }
-  }
-
-  sendAsyncMessage("Browser:URIFixup", data);
-}
-Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup");
-addEventListener("unload", () => {
-  Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
-}, false);
-
-addMessageListener("Browser:AppTab", function(message) {
-  if (docShell) {
-    docShell.isAppTab = message.data.isAppTab;
-  }
-});
-
-var WebBrowserChrome = {
-  onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
-    return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
-  },
-
-  // Check whether this URI should load in the current process
-  shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData, aTriggeringPrincipal) {
-    if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData)) {
-      E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, false);
-      return false;
-    }
-
-    return true;
-  },
-
-  shouldLoadURIInThisProcess(aURI) {
-    return E10SUtils.shouldLoadURIInThisProcess(aURI);
-  },
-
-  // Try to reload the currently active or currently loading page in a new process.
-  reloadInFreshProcess(aDocShell, aURI, aReferrer, aTriggeringPrincipal, aLoadFlags) {
-    E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, true, aLoadFlags);
-    return true;
-  }
-};
-
-if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
-  let tabchild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsITabChild);
-  tabchild.webBrowserChrome = WebBrowserChrome;
-}
-
-
-var DOMFullscreenHandler = {
-
-  init() {
-    addMessageListener("DOMFullscreen:Entered", this);
-    addMessageListener("DOMFullscreen:CleanUp", this);
-    addEventListener("MozDOMFullscreen:Request", this);
-    addEventListener("MozDOMFullscreen:Entered", this);
-    addEventListener("MozDOMFullscreen:NewOrigin", this);
-    addEventListener("MozDOMFullscreen:Exit", this);
-    addEventListener("MozDOMFullscreen:Exited", this);
-    this.init = null;
-  },
-
+class DOMFullscreenChild extends ActorChild {
   receiveMessage(aMessage) {
-    let windowUtils = content && content.windowUtils;
+    let windowUtils = this.content && this.content.windowUtils;
     switch (aMessage.name) {
       case "DOMFullscreen:Entered": {
         this._lastTransactionId = windowUtils.lastTransactionId;
         if (!windowUtils.handleFullscreenRequests() &&
-            !content.document.fullscreenElement) {
+            !this.content.document.fullscreenElement) {
           // If we don't actually have any pending fullscreen request
           // to handle, neither we have been in fullscreen, tell the
           // parent to just exit.
-          sendAsyncMessage("DOMFullscreen:Exit");
+          this.mm.sendAsyncMessage("DOMFullscreen:Exit");
         }
         break;
       }
       case "DOMFullscreen:CleanUp": {
         // If we've exited fullscreen at this point, no need to record
         // transaction id or call exit fullscreen. This is especially
         // important for non-e10s, since in that case, it is possible
         // that no more paint would be triggered after this point.
-        if (content.document.fullscreenElement && windowUtils) {
+        if (this.content.document.fullscreenElement && windowUtils) {
           this._lastTransactionId = windowUtils.lastTransactionId;
           windowUtils.exitFullscreen();
         }
         break;
       }
     }
-  },
+  }
 
   handleEvent(aEvent) {
     switch (aEvent.type) {
       case "MozDOMFullscreen:Request": {
-        sendAsyncMessage("DOMFullscreen:Request");
+        this.mm.sendAsyncMessage("DOMFullscreen:Request");
         break;
       }
       case "MozDOMFullscreen:NewOrigin": {
-        sendAsyncMessage("DOMFullscreen:NewOrigin", {
+        this.mm.sendAsyncMessage("DOMFullscreen:NewOrigin", {
           originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix,
         });
         break;
       }
       case "MozDOMFullscreen:Exit": {
-        sendAsyncMessage("DOMFullscreen:Exit");
+        this.mm.sendAsyncMessage("DOMFullscreen:Exit");
         break;
       }
       case "MozDOMFullscreen:Entered":
       case "MozDOMFullscreen:Exited": {
-        addEventListener("MozAfterPaint", this);
-        if (!content || !content.document.fullscreenElement) {
+        this.mm.addEventListener("MozAfterPaint", this);
+        if (!this.content || !this.content.document.fullscreenElement) {
           // If we receive any fullscreen change event, and find we are
           // actually not in fullscreen, also ask the parent to exit to
           // ensure that the parent always exits fullscreen when we do.
-          sendAsyncMessage("DOMFullscreen:Exit");
+          this.mm.sendAsyncMessage("DOMFullscreen:Exit");
         }
         break;
       }
       case "MozAfterPaint": {
         // Only send Painted signal after we actually finish painting
         // the transition for the fullscreen change.
         // Note that this._lastTransactionId is not set when in non-e10s
         // mode, so we need to check that explicitly.
         if (!this._lastTransactionId ||
             aEvent.transactionId > this._lastTransactionId) {
-          removeEventListener("MozAfterPaint", this);
-          sendAsyncMessage("DOMFullscreen:Painted");
+          this.mm.removeEventListener("MozAfterPaint", this);
+          this.mm.sendAsyncMessage("DOMFullscreen:Painted");
         }
         break;
       }
     }
   }
-};
-DOMFullscreenHandler.init();
-
-var UserContextIdNotifier = {
-  init() {
-    addEventListener("DOMWindowCreated", this);
-    this.init = null;
-  },
-
-  uninit() {
-    removeEventListener("DOMWindowCreated", this);
-  },
-
-  handleEvent(aEvent) {
-    // When the window is created, we want to inform the tabbrowser about
-    // the userContextId in use in order to update the UI correctly.
-    // Just because we cannot change the userContextId from an active docShell,
-    // we don't need to check DOMContentLoaded again.
-    this.uninit();
-
-    // We use the docShell because content.document can have been loaded before
-    // setting the originAttributes.
-    let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
-    let userContextId = loadContext.originAttributes.userContextId;
-
-    sendAsyncMessage("Browser:WindowCreated", { userContextId });
-  }
-};
-
-UserContextIdNotifier.init();
-
-Services.obs.notifyObservers(this, "tab-content-frameloader-created");
-
-addMessageListener("AllowScriptsToClose", () => {
-  content.windowUtils.allowScriptsToClose();
-});
-
-addEventListener("MozAfterPaint", function onFirstPaint() {
-  removeEventListener("MozAfterPaint", onFirstPaint);
-  sendAsyncMessage("Browser:FirstPaint");
-});
-
-// Remove this once bug 1397365 is fixed.
-addEventListener("MozAfterPaint", function onFirstNonBlankPaint() {
-  if (content.document.documentURI == "about:blank" && !content.opener)
-    return;
-  removeEventListener("MozAfterPaint", onFirstNonBlankPaint);
-  sendAsyncMessage("Browser:FirstNonBlankPaint");
-});
-
-addMessageListener("DOM:WebManifest:hasManifestLink", ManifestMessages);
-addMessageListener("DOM:ManifestObtainer:Obtain", ManifestMessages);
-addMessageListener("DOM:Manifest:FireAppInstalledEvent", ManifestMessages);
-addMessageListener("DOM:WebManifest:fetchIcon", ManifestMessages);
+}
rename from browser/modules/LightWeightThemeWebInstallListener.jsm
rename to browser/actors/LightWeightThemeInstallChild.jsm
--- a/browser/modules/LightWeightThemeWebInstallListener.jsm
+++ b/browser/actors/LightWeightThemeInstallChild.jsm
@@ -1,19 +1,19 @@
 /* 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/. */
 
-var EXPORTED_SYMBOLS = ["LightWeightThemeWebInstallListener"];
+var EXPORTED_SYMBOLS = ["LightWeightThemeInstallChild"];
+
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 
-var LightWeightThemeWebInstallListener = {
-  _previewWindow: null,
-
+class LightWeightThemeInstallChild extends ActorChild {
   handleEvent(event) {
-    let mm = getMessageManagerForContent(event.target.ownerGlobal);
+    let {mm} = this;
     switch (event.type) {
       case "InstallBrowserTheme": {
         mm.sendAsyncMessage("LightWeightThemeWebInstaller:Install", {
           baseURI: event.target.baseURI,
           principal: event.target.nodePrincipal,
           themeData: event.target.getAttribute("data-browsertheme"),
         });
         break;
@@ -37,19 +37,15 @@ var LightWeightThemeWebInstallListener =
         if (this._previewWindow) {
           mm.sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview",
                            {principal: event.target.nodePrincipal});
           this._resetPreviewWindow();
         }
         break;
       }
     }
-  },
+  }
 
   _resetPreviewWindow() {
     this._previewWindow.removeEventListener("pagehide", this, true);
     this._previewWindow = null;
   }
-};
-
-function getMessageManagerForContent(content) {
-  return content.docShell.messageManager;
 }
rename from browser/modules/NetErrorContent.jsm
rename to browser/actors/NetErrorChild.jsm
--- a/browser/modules/NetErrorContent.jsm
+++ b/browser/actors/NetErrorChild.jsm
@@ -1,17 +1,18 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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/. */
 
-var EXPORTED_SYMBOLS = ["NetErrorContent"];
+var EXPORTED_SYMBOLS = ["NetErrorChild"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                                "resource://gre/modules/BrowserUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "WebNavigationFrames",
                                "resource://gre/modules/WebNavigationFrames.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gPipNSSBundle", function() {
   return Services.strings.createBundle("chrome://pipnss/locale/pipnss.properties");
@@ -65,24 +66,24 @@ function getSerializedSecurityInfo(docSh
     return "";
   }
   securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
               .QueryInterface(Ci.nsISerializable);
 
   return serhelper.serializeToString(securityInfo);
 }
 
-var NetErrorContent = {
+class NetErrorChild extends ActorChild {
   isAboutNetError(doc) {
     return doc.documentURI.startsWith("about:neterror");
-  },
+  }
 
   isAboutCertError(doc) {
     return doc.documentURI.startsWith("about:certerror");
-  },
+  }
 
   _getCertValidityRange(docShell) {
     let {securityInfo} = docShell.failedChannel;
     securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
     let certs = securityInfo.failedCertChain.getEnumerator();
     let notBefore = 0;
     let notAfter = Number.MAX_SAFE_INTEGER;
     while (certs.hasMoreElements()) {
@@ -90,17 +91,17 @@ var NetErrorContent = {
       cert.QueryInterface(Ci.nsIX509Cert);
       notBefore = Math.max(notBefore, cert.validity.notBefore);
       notAfter = Math.min(notAfter, cert.validity.notAfter);
     }
     // nsIX509Cert reports in PR_Date terms, which uses microseconds. Convert:
     notBefore /= 1000;
     notAfter /= 1000;
     return {notBefore, notAfter};
-  },
+  }
 
   _setTechDetails(input, doc) {
     // CSS class and error code are set from nsDocShell.
     let searchParams = new URLSearchParams(doc.documentURI.split("?")[1]);
     let cssClass = searchParams.get("s");
     let error = searchParams.get("e");
     let technicalInfo = doc.getElementById("badCertTechnicalInfo");
     technicalInfo.textContent = "";
@@ -324,25 +325,25 @@ var NetErrorContent = {
     if (errorCode) {
       errorCode.href = "javascript:void(0)";
       errorCode.addEventListener("click", () => {
         let debugInfo = doc.getElementById("certificateErrorDebugInformation");
         debugInfo.style.display = "block";
         debugInfo.scrollIntoView({block: "start", behavior: "smooth"});
       });
     }
-  },
+  }
 
-  onCertErrorDetails(global, msg, docShell) {
+  onCertErrorDetails(msg, docShell) {
     let doc = docShell.document;
 
-  function updateContainerPosition() {
-    let textContainer = doc.getElementById("text-container");
-    textContainer.style.marginTop = `calc(50vh - ${textContainer.clientHeight / 2}px)`;
-  }
+    function updateContainerPosition() {
+      let textContainer = doc.getElementById("text-container");
+      textContainer.style.marginTop = `calc(50vh - ${textContainer.clientHeight / 2}px)`;
+    }
 
     let div = doc.getElementById("certificateErrorText");
     div.textContent = msg.data.info;
     this._setTechDetails(msg, doc);
     let learnMoreLink = doc.getElementById("learnMoreLink");
     let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
     let errWhatToDo = doc.getElementById("es_nssBadCert_" + msg.data.codeString);
     let es = doc.getElementById("errorWhatToDoText");
@@ -479,101 +480,133 @@ var NetErrorContent = {
               // eslint-disable-next-line no-unsanitized/property
               est.textContent = errWhatToDoTitle.textContent;
               est.style.fontWeight = "bold";
             }
             updateContainerPosition();
         }
         break;
     }
-  },
+  }
 
-  handleEvent(aGlobal, aEvent) {
+  handleEvent(aEvent) {
     // Documents have a null ownerDocument.
     let doc = aEvent.originalTarget.ownerDocument || aEvent.originalTarget;
 
     switch (aEvent.type) {
     case "AboutNetErrorLoad":
-      this.onPageLoad(aGlobal, aEvent.originalTarget, doc.defaultView);
+      this.onPageLoad(aEvent.originalTarget, doc.defaultView);
       break;
     case "AboutNetErrorOpenCaptivePortal":
-      this.openCaptivePortalPage(aGlobal, aEvent);
+      this.openCaptivePortalPage(aEvent);
       break;
     case "AboutNetErrorSetAutomatic":
-      this.onSetAutomatic(aGlobal, aEvent);
+      this.onSetAutomatic(aEvent);
       break;
     case "AboutNetErrorResetPreferences":
-      this.onResetPreferences(aGlobal, aEvent);
+      this.onResetPreferences(aEvent);
       break;
+    case "click":
+      if (aEvent.button == 0) {
+        if (this.isAboutCertError(doc)) {
+          this.onCertError(aEvent.originalTarget, doc.defaultView);
+        } else {
+          this.onClick(aEvent);
+        }
+      }
     }
-  },
+  }
+
+  receiveMessage(msg) {
+    if (msg.name == "CertErrorDetails") {
+      let frameDocShell = WebNavigationFrames.findDocShell(msg.data.frameId, this.docShell);
+      // We need nsIWebNavigation to access docShell.document.
+      frameDocShell && frameDocShell.QueryInterface(Ci.nsIWebNavigation);
+      if (!frameDocShell || !this.isAboutCertError(frameDocShell.document)) {
+        return;
+      }
+
+      this.onCertErrorDetails(msg, frameDocShell);
+    } else if (msg.name == "Browser:CaptivePortalFreed") {
+      // TODO: This check is not correct for frames.
+      if (!this.isAboutCertError(this.content.document)) {
+        return;
+      }
+
+      this.onCaptivePortalFreed(msg);
+    }
+  }
+
+  onCaptivePortalFreed(msg) {
+    this.content.dispatchEvent(new this.content.CustomEvent("AboutNetErrorCaptivePortalFreed"));
+  }
 
   changedCertPrefs() {
     let prefSSLImpact = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
        return prefs.concat(Services.prefs.getChildList(root));
     }, []);
     for (let prefName of prefSSLImpact) {
       if (Services.prefs.prefHasUserValue(prefName)) {
         return true;
       }
     }
 
     return false;
-  },
+  }
 
-   _getErrorMessageFromCode(securityInfo, doc) {
-     let uri = Services.io.newURI(doc.location);
-     let hostString = uri.host;
-     if (uri.port != 443 && uri.port != -1) {
-       hostString = uri.hostPort;
-     }
+  _getErrorMessageFromCode(securityInfo, doc) {
+    let uri = Services.io.newURI(doc.location);
+    let hostString = uri.host;
+    if (uri.port != 443 && uri.port != -1) {
+      hostString = uri.hostPort;
+    }
 
-     let id_str = "";
-     switch (securityInfo.errorCode) {
-       case SSL_ERROR_SSL_DISABLED:
-         id_str = "PSMERR_SSL_Disabled";
-         break;
-       case SSL_ERROR_SSL2_DISABLED:
-         id_str = "PSMERR_SSL2_Disabled";
-         break;
-       case SEC_ERROR_REUSED_ISSUER_AND_SERIAL:
-         id_str = "PSMERR_HostReusedIssuerSerial";
-         break;
-     }
-     let nss_error_id_str = securityInfo.errorCodeString;
-     let msg2 = "";
-     if (id_str) {
-       msg2 = gPipNSSBundle.GetStringFromName(id_str) + "\n";
-     } else if (nss_error_id_str) {
-       msg2 = gNSSErrorsBundle.GetStringFromName(nss_error_id_str) + "\n";
-     }
+    let id_str = "";
+    switch (securityInfo.errorCode) {
+      case SSL_ERROR_SSL_DISABLED:
+        id_str = "PSMERR_SSL_Disabled";
+        break;
+      case SSL_ERROR_SSL2_DISABLED:
+        id_str = "PSMERR_SSL2_Disabled";
+        break;
+      case SEC_ERROR_REUSED_ISSUER_AND_SERIAL:
+        id_str = "PSMERR_HostReusedIssuerSerial";
+        break;
+    }
+    let nss_error_id_str = securityInfo.errorCodeString;
+    let msg2 = "";
+    if (id_str) {
+      msg2 = gPipNSSBundle.GetStringFromName(id_str) + "\n";
+    } else if (nss_error_id_str) {
+      msg2 = gNSSErrorsBundle.GetStringFromName(nss_error_id_str) + "\n";
+    }
 
-     if (!msg2) {
-       // We couldn't get an error message. Use the error string.
-       // Note that this is different from before where we used PR_ErrorToString.
-       msg2 = nss_error_id_str;
-     }
-     let msg = gPipNSSBundle.formatStringFromName("SSLConnectionErrorPrefix2",
-                                                  [hostString, msg2], 2);
+    if (!msg2) {
+      // We couldn't get an error message. Use the error string.
+      // Note that this is different from before where we used PR_ErrorToString.
+      msg2 = nss_error_id_str;
+    }
+    let msg = gPipNSSBundle.formatStringFromName("SSLConnectionErrorPrefix2",
+                                                 [hostString, msg2], 2);
 
-     if (nss_error_id_str) {
-       msg += gPipNSSBundle.formatStringFromName("certErrorCodePrefix3",
-                                                 [nss_error_id_str], 1) + "\n";
-     }
-     return msg;
-   },
+    if (nss_error_id_str) {
+      msg += gPipNSSBundle.formatStringFromName("certErrorCodePrefix3",
+                                                [nss_error_id_str], 1) + "\n";
+    }
+    return msg;
+  }
 
-  onPageLoad(global, originalTarget, win) {
+  onPageLoad(originalTarget, win) {
     // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
     const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0;
 
     let hideAddExceptionButton = false;
 
     if (this.isAboutCertError(win.document)) {
-      this.onCertError(global, originalTarget, win);
+      this.onCertError(originalTarget, win);
       hideAddExceptionButton =
         Services.prefs.getBoolPref("security.certerror.hideAddException", false);
     }
     if (this.isAboutNetError(win.document)) {
       let docShell = win.docShell;
       if (docShell) {
         let {securityInfo} = docShell.failedChannel;
         // We don't have a securityInfo when this is for example a DNS error.
@@ -592,70 +625,71 @@ var NetErrorContent = {
       detail: JSON.stringify({
         enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"),
         changedCertPrefs: this.changedCertPrefs(),
         automatic,
         hideAddExceptionButton,
       })
     }));
 
-    global.sendAsyncMessage("Browser:SSLErrorReportTelemetry",
+    this.mm.sendAsyncMessage("Browser:SSLErrorReportTelemetry",
                             {reportStatus: TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN});
-  },
+  }
 
-  openCaptivePortalPage(global, evt) {
-    global.sendAsyncMessage("Browser:OpenCaptivePortalPage");
-  },
+  openCaptivePortalPage(evt) {
+    this.mm.sendAsyncMessage("Browser:OpenCaptivePortalPage");
+  }
 
 
-  onResetPreferences(global, evt) {
-    global.sendAsyncMessage("Browser:ResetSSLPreferences");
-  },
+  onResetPreferences(evt) {
+    this.mm.sendAsyncMessage("Browser:ResetSSLPreferences");
+  }
 
-  onSetAutomatic(global, evt) {
-    global.sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
+  onSetAutomatic(evt) {
+    this.mm.sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
       automatic: evt.detail
     });
 
     // If we're enabling reports, send a report for this failure.
     if (evt.detail) {
       let win = evt.originalTarget.ownerGlobal;
       let docShell = win.docShell;
 
       let {securityInfo} = docShell.failedChannel;
       securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
       let {host, port} = win.document.mozDocumentURIIfNotForErrorPages;
 
       let errorReporter = Cc["@mozilla.org/securityreporter;1"]
                             .getService(Ci.nsISecurityReporter);
       errorReporter.reportTLSError(securityInfo, host, port);
     }
-  },
+  }
 
-  onCertError(global, targetElement, win) {
-    let docShell = win.docShell;
-    global.sendAsyncMessage("Browser:CertExceptionError", {
+  onCertError(target, win) {
+    this.mm.sendAsyncMessage("Browser:CertExceptionError", {
       frameId: WebNavigationFrames.getFrameId(win),
       location: win.document.location.href,
-      elementId: targetElement.getAttribute("id"),
+      elementId: target.getAttribute("id"),
       isTopFrame: (win.parent === win),
-      securityInfoAsString: getSerializedSecurityInfo(docShell),
+      securityInfoAsString: getSerializedSecurityInfo(win.docShell),
     });
-  },
+  }
 
-  onAboutNetError(global, event, documentURI) {
+  onClick(event) {
+    let {documentURI} = event.target.ownerDocument;
+
     let elmId = event.originalTarget.getAttribute("id");
     if (elmId == "returnButton") {
-      global.sendAsyncMessage("Browser:SSLErrorGoBack", {});
+      this.mm.sendAsyncMessage("Browser:SSLErrorGoBack", {});
       return;
     }
     if (elmId != "errorTryAgain" || !/e=netOffline/.test(documentURI)) {
       return;
     }
     // browser front end will handle clearing offline mode and refreshing
     // the page *if* we're in offline mode now. Otherwise let the error page
     // handle the click.
     if (Services.io.offline) {
       event.preventDefault();
-      global.sendAsyncMessage("Browser:EnableOnlineMode", {});
+      this.mm.sendAsyncMessage("Browser:EnableOnlineMode", {});
     }
-  },
-};
+  }
+}
copy from browser/base/content/content.js
copy to browser/actors/OfflineAppsChild.jsm
--- a/browser/base/content/content.js
+++ b/browser/actors/OfflineAppsChild.jsm
@@ -1,336 +1,70 @@
+/* vim: set ts=2 sw=2 sts=2 et tw=80: */
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* 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/. */
 
-/* This content script should work in any browser or iframe and should not
- * depend on the frame being contained in tabbrowser. */
-
-/* eslint-env mozilla/frame-script */
-/* eslint no-unused-vars: ["error", {args: "none"}] */
-
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-// TabChildGlobal
-var global = this;
-
-XPCOMUtils.defineLazyModuleGetters(this, {
-  BlockedSiteContent: "resource:///modules/BlockedSiteContent.jsm",
-  ContentLinkHandler: "resource:///modules/ContentLinkHandler.jsm",
-  ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
-  ContentWebRTC: "resource:///modules/ContentWebRTC.jsm",
-  LoginFormFactory: "resource://gre/modules/LoginManagerContent.jsm",
-  InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
-  PluginContent: "resource:///modules/PluginContent.jsm",
-  FormSubmitObserver: "resource:///modules/FormSubmitObserver.jsm",
-  NetErrorContent: "resource:///modules/NetErrorContent.jsm",
-  PageMetadata: "resource://gre/modules/PageMetadata.jsm",
-  WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
-  ContextMenu: "resource:///modules/ContextMenu.jsm",
-});
-
-XPCOMUtils.defineLazyProxy(this, "contextMenu", () => {
-  return new ContextMenu(global);
-});
-
-XPCOMUtils.defineLazyProxy(this, "ClickEventHandler", () => {
-  let tmp = {};
-  ChromeUtils.import("resource:///modules/ClickEventHandler.jsm", tmp);
-  return new tmp.ClickEventHandler(global);
-});
-
-XPCOMUtils.defineLazyGetter(this, "LoginManagerContent", () => {
-  let tmp = {};
-  ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm", tmp);
-  tmp.LoginManagerContent.setupEventListeners(global);
-  return tmp.LoginManagerContent;
-});
-
-XPCOMUtils.defineLazyProxy(this, "formSubmitObserver", () => {
-  return new FormSubmitObserver(content, this);
-}, {
-  // stub QI
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIFormSubmitObserver, Ci.nsISupportsWeakReference])
-});
-
-XPCOMUtils.defineLazyProxy(this, "PageInfoListener",
-                           "resource:///modules/PageInfoListener.jsm");
-
-XPCOMUtils.defineLazyProxy(this, "LightWeightThemeWebInstallListener",
-                           "resource:///modules/LightWeightThemeWebInstallListener.jsm");
-
-Services.els.addSystemEventListener(global, "contextmenu", contextMenu, false);
-
-Services.obs.addObserver(formSubmitObserver, "invalidformsubmit", true);
-
-addMessageListener("PageInfo:getData", PageInfoListener);
+var EXPORTED_SYMBOLS = ["OfflineAppsChild"];
 
-// NOTE: Much of this logic is duplicated in BrowserCLH.js for Android.
-addMessageListener("RemoteLogins:fillForm", function(message) {
-  // intercept if ContextMenu.jsm had sent a plain object for remote targets
-  message.objects.inputElement = contextMenu.getTarget(message, "inputElement");
-  LoginManagerContent.receiveMessage(message, content);
-});
-addEventListener("DOMFormHasPassword", function(event) {
-  LoginManagerContent.onDOMFormHasPassword(event, content);
-  let formLike = LoginFormFactory.createFromForm(event.originalTarget);
-  InsecurePasswordUtils.reportInsecurePasswords(formLike);
-});
-addEventListener("DOMInputPasswordAdded", function(event) {
-  LoginManagerContent.onDOMInputPasswordAdded(event, content);
-  let formLike = LoginFormFactory.createFromField(event.originalTarget);
-  InsecurePasswordUtils.reportInsecurePasswords(formLike);
-});
-addEventListener("DOMAutoComplete", function(event) {
-  LoginManagerContent.onUsernameInput(event);
-});
-
-var AboutBlockedSiteListener = {
-  init(chromeGlobal) {
-    addMessageListener("DeceptiveBlockedDetails", this);
-    chromeGlobal.addEventListener("AboutBlockedLoaded", this, false, true);
-    this.init = null;
-  },
-
-  get isBlockedSite() {
-    return content.document.documentURI.startsWith("about:blocked");
-  },
-
-  receiveMessage(msg) {
-    if (!this.isBlockedSite) {
-      return;
-    }
-
-    BlockedSiteContent.receiveMessage(global, msg);
-  },
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 
-  handleEvent(aEvent) {
-    if (!this.isBlockedSite) {
-      return;
-    }
-
-    if (aEvent.type != "AboutBlockedLoaded") {
-      return;
-    }
-
-    BlockedSiteContent.handleEvent(global, aEvent);
-  },
-};
-AboutBlockedSiteListener.init(this);
-
-this.AboutNetAndCertErrorListener = {
-  init(chromeGlobal) {
-    addMessageListener("CertErrorDetails", this);
-    addMessageListener("Browser:CaptivePortalFreed", this);
-    chromeGlobal.addEventListener("AboutNetErrorLoad", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorOpenCaptivePortal", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorSetAutomatic", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorResetPreferences", this, false, true);
-    this.init = null;
-  },
-
-  isAboutNetError(doc) {
-    return doc.documentURI.startsWith("about:neterror");
-  },
-
-  isAboutCertError(doc) {
-    return doc.documentURI.startsWith("about:certerror");
-  },
-
-  receiveMessage(msg) {
-    if (msg.name == "CertErrorDetails") {
-      let frameDocShell = WebNavigationFrames.findDocShell(msg.data.frameId, docShell);
-      // We need nsIWebNavigation to access docShell.document.
-      frameDocShell && frameDocShell.QueryInterface(Ci.nsIWebNavigation);
-      if (!frameDocShell || !this.isAboutCertError(frameDocShell.document)) {
-        return;
-      }
+class OfflineAppsChild extends ActorChild {
+  constructor(mm) {
+    super(mm);
 
-      NetErrorContent.onCertErrorDetails(global, msg, frameDocShell);
-    } else if (msg.name == "Browser:CaptivePortalFreed") {
-      // TODO: This check is not correct for frames.
-      if (!this.isAboutCertError(content.document)) {
-        return;
-      }
-
-      this.onCaptivePortalFreed(msg);
-    }
-  },
-
-  onCaptivePortalFreed(msg) {
-    content.dispatchEvent(new content.CustomEvent("AboutNetErrorCaptivePortalFreed"));
-  },
-
-  handleEvent(aEvent) {
-    // Documents have a null ownerDocument.
-    let doc = aEvent.originalTarget.ownerDocument || aEvent.originalTarget;
-
-    if (!this.isAboutNetError(doc) && !this.isAboutCertError(doc)) {
-      return;
-    }
-
-    NetErrorContent.handleEvent(global, aEvent);
-  },
-};
-AboutNetAndCertErrorListener.init(this);
-
-Services.els.addSystemEventListener(global, "click", ClickEventHandler, true);
-
-new ContentLinkHandler(this);
-ContentMetaHandler.init(this);
-
-var PluginContentStub = {
-  EVENTS: [
-    "PluginCrashed",
-    "PluginOutdated",
-    "PluginInstantiated",
-    "PluginRemoved",
-    "HiddenPlugin",
-  ],
-
-  MESSAGES: [
-    "BrowserPlugins:ActivatePlugins",
-    "BrowserPlugins:NotificationShown",
-    "BrowserPlugins:ContextMenuCommand",
-    "BrowserPlugins:NPAPIPluginProcessCrashed",
-    "BrowserPlugins:CrashReportSubmitted",
-    "BrowserPlugins:Test:ClearCrashData",
-  ],
-
-  _pluginContent: null,
-  get pluginContent() {
-    if (!this._pluginContent) {
-      this._pluginContent = new PluginContent(global);
-    }
-    return this._pluginContent;
-  },
-
-  init() {
-    addEventListener("unload", this);
-
-    addEventListener("PluginBindingAttached", this, true, true);
-
-    for (let event of this.EVENTS) {
-      addEventListener(event, this, true);
-    }
-    for (let msg of this.MESSAGES) {
-      addMessageListener(msg, this);
-    }
-    Services.obs.addObserver(this, "decoder-doctor-notification");
-    this.init = null;
-  },
+    this._docId = 0;
+    this._docIdMap = new Map();
 
-  uninit() {
-    Services.obs.removeObserver(this, "decoder-doctor-notification");
-  },
-
-  observe(subject, topic, data) {
-    return this.pluginContent.observe(subject, topic, data);
-  },
-
-  handleEvent(event) {
-    if (event.type === "unload") {
-      return this.uninit();
-    }
-    return this.pluginContent.handleEvent(event);
-  },
-
-  receiveMessage(msg) {
-    return this.pluginContent.receiveMessage(msg);
-  },
-};
-
-PluginContentStub.init();
-
-// This is a temporary hack to prevent regressions (bug 1471327).
-void content;
-
-addEventListener("DOMWindowFocus", function(event) {
-  sendAsyncMessage("DOMWindowFocus", {});
-}, false);
-
-// We use this shim so that ContentWebRTC.jsm will not be loaded until
-// it is actually needed.
-var ContentWebRTCShim = message => ContentWebRTC.receiveMessage(message);
+    this._docManifestSet = new Set();
 
-addMessageListener("rtcpeer:Allow", ContentWebRTCShim);
-addMessageListener("rtcpeer:Deny", ContentWebRTCShim);
-addMessageListener("webrtc:Allow", ContentWebRTCShim);
-addMessageListener("webrtc:Deny", ContentWebRTCShim);
-addMessageListener("webrtc:StopSharing", ContentWebRTCShim);
+    this._observerAdded = false;
+  }
 
-var PageMetadataMessenger = {
-  init() {
-    addMessageListener("PageMetadata:GetPageData", this);
-    addMessageListener("PageMetadata:GetMicroformats", this);
-    this.init = null;
-  },
-  receiveMessage(message) {
-    switch (message.name) {
-      case "PageMetadata:GetPageData": {
-        let target = contextMenu.getTarget(message);
-        let result = PageMetadata.getData(content.document, target);
-        sendAsyncMessage("PageMetadata:PageDataResult", result);
-        break;
-      }
-      case "PageMetadata:GetMicroformats": {
-        let target = contextMenu.getTarget(message);
-        let result = PageMetadata.getMicroformats(content.document, target);
-        sendAsyncMessage("PageMetadata:MicroformatsResult", result);
-        break;
-      }
-    }
-  }
-};
-PageMetadataMessenger.init();
-
-addEventListener("InstallBrowserTheme", LightWeightThemeWebInstallListener, false, true);
-addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstallListener, false, true);
-addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstallListener, false, true);
-
-let OfflineApps = {
-  _docId: 0,
-  _docIdMap: new Map(),
-
-  _docManifestSet: new Set(),
-
-  _observerAdded: false,
   registerWindow(aWindow) {
     if (!this._observerAdded) {
       this._observerAdded = true;
       Services.obs.addObserver(this, "offline-cache-update-completed", true);
     }
     let manifestURI = this._getManifestURI(aWindow);
     this._docManifestSet.add(manifestURI.spec);
-  },
+  }
 
   handleEvent(event) {
     if (event.type == "MozApplicationManifest") {
+      let doc = event.target;
+      let info = {
+        uri: doc.documentURI,
+        characterSet: doc.characterSet,
+        manifest: doc.documentElement.getAttribute("manifest"),
+        principal: doc.nodePrincipal,
+      };
+      this.mm.sendAsyncMessage("MozApplicationManifest", info);
+
       this.offlineAppRequested(event.originalTarget.defaultView);
     }
-  },
+  }
 
   _getManifestURI(aWindow) {
     if (!aWindow.document.documentElement)
       return null;
 
     var attr = aWindow.document.documentElement.getAttribute("manifest");
     if (!attr)
       return null;
 
     try {
       return Services.io.newURI(attr, aWindow.document.characterSet,
                                 Services.io.newURI(aWindow.location.href));
     } catch (e) {
       return null;
     }
-  },
+  }
 
   offlineAppRequested(aContentWindow) {
     this.registerWindow(aContentWindow);
     if (!Services.prefs.getBoolPref("browser.offline-apps.notify")) {
       return;
     }
 
     let currentURI = aContentWindow.document.documentURIObject;
@@ -343,54 +77,53 @@ let OfflineApps = {
         // all pages can use offline capabilities, no need to ask the user
         return;
       }
     } catch (e) {
       // this pref isn't set by default, ignore failures
     }
     let docId = ++this._docId;
     this._docIdMap.set(docId, Cu.getWeakReference(aContentWindow.document));
-    sendAsyncMessage("OfflineApps:RequestPermission", {
+    this.mm.sendAsyncMessage("OfflineApps:RequestPermission", {
       uri: currentURI.spec,
       docId,
     });
-  },
+  }
 
   _startFetching(aDocument) {
     if (!aDocument.documentElement)
       return;
 
     let manifestURI = this._getManifestURI(aDocument.defaultView);
     if (!manifestURI)
       return;
 
     var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
                         getService(Ci.nsIOfflineCacheUpdateService);
     updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject,
                                  aDocument.nodePrincipal, aDocument.defaultView);
-  },
+  }
 
   receiveMessage(aMessage) {
     if (aMessage.name == "OfflineApps:StartFetching") {
       let doc = this._docIdMap.get(aMessage.data.docId);
       doc = doc && doc.get();
       if (doc) {
         this._startFetching(doc);
       }
       this._docIdMap.delete(aMessage.data.docId);
     }
-  },
+  }
 
   observe(aSubject, aTopic, aState) {
     if (aTopic == "offline-cache-update-completed") {
       let cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
       let uri = cacheUpdate.manifestURI;
       if (uri && this._docManifestSet.has(uri.spec)) {
-        sendAsyncMessage("OfflineApps:CheckUsage", {uri: uri.spec});
+        this.mm.sendAsyncMessage("OfflineApps:CheckUsage", {uri: uri.spec});
       }
     }
-  },
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
-                                          Ci.nsISupportsWeakReference]),
-};
+  }
+}
 
-addEventListener("MozApplicationManifest", OfflineApps, false);
-addMessageListener("OfflineApps:StartFetching", OfflineApps);
+OfflineAppsChild.prototype.QueryInterface =
+  ChromeUtils.generateQI([Ci.nsIObserver,
+                          Ci.nsISupportsWeakReference]);
rename from browser/modules/PageInfoListener.jsm
rename to browser/actors/PageInfoChild.jsm
--- a/browser/modules/PageInfoListener.jsm
+++ b/browser/actors/PageInfoChild.jsm
@@ -1,24 +1,26 @@
 /* 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/. */
 
-var EXPORTED_SYMBOLS = ["PageInfoListener"];
+var EXPORTED_SYMBOLS = ["PageInfoChild"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
+
 XPCOMUtils.defineLazyModuleGetters(this, {
   Feeds: "resource:///modules/Feeds.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   setTimeout: "resource://gre/modules/Timer.jsm"
 });
 
-var PageInfoListener = {
+class PageInfoChild extends ActorChild {
   /* nsIMessageListener */
   receiveMessage(message) {
     let strings = message.data.strings;
     let window;
 
     let frameOuterWindowID = message.data.frameOuterWindowID;
 
     // If inside frame then get the frame's window and document.
@@ -34,44 +36,44 @@ var PageInfoListener = {
                         docInfo: this.getDocumentInfo(document),
                         feeds: this.getFeedsInfo(document, strings),
                         windowInfo: this.getWindowInfo(window)};
 
     message.target.sendAsyncMessage("PageInfo:data", pageInfoData);
 
     // Separate step so page info dialog isn't blank while waiting for this to finish.
     this.getMediaInfo(document, window, strings, message.target);
-  },
+  }
 
   getMetaInfo(document) {
     let metaViewRows = [];
 
     // Get the meta tags from the page.
     let metaNodes = document.getElementsByTagName("meta");
 
     for (let metaNode of metaNodes) {
       metaViewRows.push([metaNode.name || metaNode.httpEquiv || metaNode.getAttribute("property"),
                         metaNode.content]);
     }
 
     return metaViewRows;
-  },
+  }
 
   getWindowInfo(window) {
     let windowInfo = {};
     windowInfo.isTopWindow = window == window.top;
 
     let hostName = null;
     try {
       hostName = Services.io.newURI(window.location.href).displayHost;
     } catch (exception) { }
 
     windowInfo.hostName = hostName;
     return windowInfo;
-  },
+  }
 
   getDocumentInfo(document) {
     let docInfo = {};
     docInfo.title = document.title;
     docInfo.location = document.location.toString();
     try {
       docInfo.location = Services.io.newURI(document.location.toString()).displaySpec;
     } catch (exception) { }
@@ -89,17 +91,17 @@ var PageInfoListener = {
 
     let documentURIObject = {};
     documentURIObject.spec = document.documentURIObject.spec;
     docInfo.documentURIObject = documentURIObject;
 
     docInfo.isContentWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(document.ownerGlobal);
 
     return docInfo;
-  },
+  }
 
   getFeedsInfo(document, strings) {
     let feeds = [];
     // Get the feeds from the page.
     let linkNodes = document.getElementsByTagName("link");
     let length = linkNodes.length;
     for (let i = 0; i < length; i++) {
       let link = linkNodes[i];
@@ -119,36 +121,36 @@ var PageInfoListener = {
         let type = Feeds.isValidFeed(link, document.nodePrincipal, "feed" in rels);
         if (type) {
           type = strings[type] || strings["application/rss+xml"];
           feeds.push([link.title, type, link.href]);
         }
       }
     }
     return feeds;
-  },
+  }
 
   // Only called once to get the media tab's media elements from the content page.
   getMediaInfo(document, window, strings, mm) {
     let frameList = this.goThroughFrames(document, window);
     this.processFrames(document, frameList, strings, mm);
-  },
+  }
 
   goThroughFrames(document, window) {
     let frameList = [document];
     if (window && window.frames.length > 0) {
       let num = window.frames.length;
       for (let i = 0; i < num; i++) {
         // Recurse through the frames.
         frameList.concat(this.goThroughFrames(window.frames[i].document,
                                               window.frames[i]));
       }
     }
     return frameList;
-  },
+  }
 
   async processFrames(document, frameList, strings, mm) {
     let nodeCount = 0;
     let content = document.ownerGlobal;
     for (let doc of frameList) {
       let iterator = doc.createTreeWalker(doc, content.NodeFilter.SHOW_ELEMENT);
 
       // Goes through all the elements on the doc. imageViewRows takes only the media elements.
@@ -163,17 +165,17 @@ var PageInfoListener = {
         if (++nodeCount % 500 == 0) {
           // setTimeout every 500 elements so we don't keep blocking the content process.
           await new Promise(resolve => setTimeout(resolve, 10));
         }
       }
     }
     // Send that page info media fetching has finished.
     mm.sendAsyncMessage("PageInfo:mediaData", {isComplete: true});
-  },
+  }
 
   getMediaItems(document, strings, elem) {
     // Check for images defined in CSS (e.g. background, borders)
     let computedStyle = elem.ownerGlobal.getComputedStyle(elem);
     // A node can have multiple media items associated with it - for example,
     // multiple background images.
     let mediaItems = [];
     let content = document.ownerGlobal;
@@ -228,17 +230,17 @@ var PageInfoListener = {
       }
     } else if (elem instanceof content.HTMLObjectElement) {
       addImage(elem.data, strings.mediaObject, this.getValueText(elem), elem, false);
     } else if (elem instanceof content.HTMLEmbedElement) {
       addImage(elem.src, strings.mediaEmbed, "", elem, false);
     }
 
     return mediaItems;
-  },
+  }
 
   /**
    * Set up a JSON element object with all the instanceOf and other infomation that
    * makePreview in pageInfo.js uses to figure out how to display the preview.
    */
 
   serializeElementInfo(document, url, type, alt, item, isBG) {
     let result = {};
@@ -311,17 +313,17 @@ var PageInfoListener = {
     if (item instanceof content.SVGImageElement) {
       result.SVGImageElementWidth = item.width.baseVal.value;
       result.SVGImageElementHeight = item.height.baseVal.value;
     }
 
     result.baseURI = item.baseURI;
 
     return result;
-  },
+  }
 
   // Other Misc Stuff
   // Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
   // parse a node to extract the contents of the node
   getValueText(node) {
 
     let valueText = "";
     let content = node.ownerGlobal;
@@ -350,37 +352,37 @@ var PageInfoListener = {
           valueText += " " + this.getAltText(childNode);
         } else {
           valueText += " " + this.getValueText(childNode);
         }
       }
     }
 
     return this.stripWS(valueText);
-  },
+  }
 
   // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
   // Traverse the tree in search of an img or area element and grab its alt tag.
   getAltText(node) {
     let altText = "";
 
     if (node.alt) {
       return node.alt;
     }
     let length = node.childNodes.length;
     for (let i = 0; i < length; i++) {
       if ((altText = this.getAltText(node.childNodes[i]) != undefined)) { // stupid js warning...
         return altText;
       }
     }
     return "";
-  },
+  }
 
   // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
   // Strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space.
   stripWS(text) {
     let middleRE = /\s+/g;
     let endRE = /(^\s+)|(\s+$)/g;
 
     text = text.replace(middleRE, " ");
     return text.replace(endRE, "");
   }
-};
+}
copy from browser/base/content/content.js
copy to browser/actors/PageMetadataChild.jsm
--- a/browser/base/content/content.js
+++ b/browser/actors/PageMetadataChild.jsm
@@ -1,396 +1,33 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
-
-/* This content script should work in any browser or iframe and should not
- * depend on the frame being contained in tabbrowser. */
-
-/* eslint-env mozilla/frame-script */
-/* eslint no-unused-vars: ["error", {args: "none"}] */
-
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-// TabChildGlobal
-var global = this;
-
-XPCOMUtils.defineLazyModuleGetters(this, {
-  BlockedSiteContent: "resource:///modules/BlockedSiteContent.jsm",
-  ContentLinkHandler: "resource:///modules/ContentLinkHandler.jsm",
-  ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
-  ContentWebRTC: "resource:///modules/ContentWebRTC.jsm",
-  LoginFormFactory: "resource://gre/modules/LoginManagerContent.jsm",
-  InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
-  PluginContent: "resource:///modules/PluginContent.jsm",
-  FormSubmitObserver: "resource:///modules/FormSubmitObserver.jsm",
-  NetErrorContent: "resource:///modules/NetErrorContent.jsm",
-  PageMetadata: "resource://gre/modules/PageMetadata.jsm",
-  WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
-  ContextMenu: "resource:///modules/ContextMenu.jsm",
-});
-
-XPCOMUtils.defineLazyProxy(this, "contextMenu", () => {
-  return new ContextMenu(global);
-});
-
-XPCOMUtils.defineLazyProxy(this, "ClickEventHandler", () => {
-  let tmp = {};
-  ChromeUtils.import("resource:///modules/ClickEventHandler.jsm", tmp);
-  return new tmp.ClickEventHandler(global);
-});
-
-XPCOMUtils.defineLazyGetter(this, "LoginManagerContent", () => {
-  let tmp = {};
-  ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm", tmp);
-  tmp.LoginManagerContent.setupEventListeners(global);
-  return tmp.LoginManagerContent;
-});
-
-XPCOMUtils.defineLazyProxy(this, "formSubmitObserver", () => {
-  return new FormSubmitObserver(content, this);
-}, {
-  // stub QI
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIFormSubmitObserver, Ci.nsISupportsWeakReference])
-});
-
-XPCOMUtils.defineLazyProxy(this, "PageInfoListener",
-                           "resource:///modules/PageInfoListener.jsm");
-
-XPCOMUtils.defineLazyProxy(this, "LightWeightThemeWebInstallListener",
-                           "resource:///modules/LightWeightThemeWebInstallListener.jsm");
-
-Services.els.addSystemEventListener(global, "contextmenu", contextMenu, false);
-
-Services.obs.addObserver(formSubmitObserver, "invalidformsubmit", true);
-
-addMessageListener("PageInfo:getData", PageInfoListener);
+"use strict";
 
-// NOTE: Much of this logic is duplicated in BrowserCLH.js for Android.
-addMessageListener("RemoteLogins:fillForm", function(message) {
-  // intercept if ContextMenu.jsm had sent a plain object for remote targets
-  message.objects.inputElement = contextMenu.getTarget(message, "inputElement");
-  LoginManagerContent.receiveMessage(message, content);
-});
-addEventListener("DOMFormHasPassword", function(event) {
-  LoginManagerContent.onDOMFormHasPassword(event, content);
-  let formLike = LoginFormFactory.createFromForm(event.originalTarget);
-  InsecurePasswordUtils.reportInsecurePasswords(formLike);
-});
-addEventListener("DOMInputPasswordAdded", function(event) {
-  LoginManagerContent.onDOMInputPasswordAdded(event, content);
-  let formLike = LoginFormFactory.createFromField(event.originalTarget);
-  InsecurePasswordUtils.reportInsecurePasswords(formLike);
-});
-addEventListener("DOMAutoComplete", function(event) {
-  LoginManagerContent.onUsernameInput(event);
-});
-
-var AboutBlockedSiteListener = {
-  init(chromeGlobal) {
-    addMessageListener("DeceptiveBlockedDetails", this);
-    chromeGlobal.addEventListener("AboutBlockedLoaded", this, false, true);
-    this.init = null;
-  },
-
-  get isBlockedSite() {
-    return content.document.documentURI.startsWith("about:blocked");
-  },
+var EXPORTED_SYMBOLS = ["PageMetadataChild"];
 
-  receiveMessage(msg) {
-    if (!this.isBlockedSite) {
-      return;
-    }
-
-    BlockedSiteContent.receiveMessage(global, msg);
-  },
-
-  handleEvent(aEvent) {
-    if (!this.isBlockedSite) {
-      return;
-    }
-
-    if (aEvent.type != "AboutBlockedLoaded") {
-      return;
-    }
-
-    BlockedSiteContent.handleEvent(global, aEvent);
-  },
-};
-AboutBlockedSiteListener.init(this);
-
-this.AboutNetAndCertErrorListener = {
-  init(chromeGlobal) {
-    addMessageListener("CertErrorDetails", this);
-    addMessageListener("Browser:CaptivePortalFreed", this);
-    chromeGlobal.addEventListener("AboutNetErrorLoad", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorOpenCaptivePortal", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorSetAutomatic", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorResetPreferences", this, false, true);
-    this.init = null;
-  },
-
-  isAboutNetError(doc) {
-    return doc.documentURI.startsWith("about:neterror");
-  },
+ChromeUtils.import("resource://gre/actors/ActorChild.jsm");
 
-  isAboutCertError(doc) {
-    return doc.documentURI.startsWith("about:certerror");
-  },
-
-  receiveMessage(msg) {
-    if (msg.name == "CertErrorDetails") {
-      let frameDocShell = WebNavigationFrames.findDocShell(msg.data.frameId, docShell);
-      // We need nsIWebNavigation to access docShell.document.
-      frameDocShell && frameDocShell.QueryInterface(Ci.nsIWebNavigation);
-      if (!frameDocShell || !this.isAboutCertError(frameDocShell.document)) {
-        return;
-      }
-
-      NetErrorContent.onCertErrorDetails(global, msg, frameDocShell);
-    } else if (msg.name == "Browser:CaptivePortalFreed") {
-      // TODO: This check is not correct for frames.
-      if (!this.isAboutCertError(content.document)) {
-        return;
-      }
-
-      this.onCaptivePortalFreed(msg);
-    }
-  },
-
-  onCaptivePortalFreed(msg) {
-    content.dispatchEvent(new content.CustomEvent("AboutNetErrorCaptivePortalFreed"));
-  },
-
-  handleEvent(aEvent) {
-    // Documents have a null ownerDocument.
-    let doc = aEvent.originalTarget.ownerDocument || aEvent.originalTarget;
-
-    if (!this.isAboutNetError(doc) && !this.isAboutCertError(doc)) {
-      return;
-    }
-
-    NetErrorContent.handleEvent(global, aEvent);
-  },
-};
-AboutNetAndCertErrorListener.init(this);
-
-Services.els.addSystemEventListener(global, "click", ClickEventHandler, true);
-
-new ContentLinkHandler(this);
-ContentMetaHandler.init(this);
-
-var PluginContentStub = {
-  EVENTS: [
-    "PluginCrashed",
-    "PluginOutdated",
-    "PluginInstantiated",
-    "PluginRemoved",
-    "HiddenPlugin",
-  ],
-
-  MESSAGES: [
-    "BrowserPlugins:ActivatePlugins",
-    "BrowserPlugins:NotificationShown",
-    "BrowserPlugins:ContextMenuCommand",
-    "BrowserPlugins:NPAPIPluginProcessCrashed",
-    "BrowserPlugins:CrashReportSubmitted",
-    "BrowserPlugins:Test:ClearCrashData",
-  ],
+ChromeUtils.defineModuleGetter(this, "ContextMenuChild",
+                               "resource:///modules/ContextMenuChild.jsm");
+ChromeUtils.defineModuleGetter(this, "PageMetadata",
+                               "resource://gre/modules/PageMetadata.jsm");
 
-  _pluginContent: null,
-  get pluginContent() {
-    if (!this._pluginContent) {
-      this._pluginContent = new PluginContent(global);
-    }
-    return this._pluginContent;
-  },
-
-  init() {
-    addEventListener("unload", this);
-
-    addEventListener("PluginBindingAttached", this, true, true);
-
-    for (let event of this.EVENTS) {
-      addEventListener(event, this, true);
-    }
-    for (let msg of this.MESSAGES) {
-      addMessageListener(msg, this);
-    }
-    Services.obs.addObserver(this, "decoder-doctor-notification");
-    this.init = null;
-  },
-
-  uninit() {
-    Services.obs.removeObserver(this, "decoder-doctor-notification");
-  },
-
-  observe(subject, topic, data) {
-    return this.pluginContent.observe(subject, topic, data);
-  },
-
-  handleEvent(event) {
-    if (event.type === "unload") {
-      return this.uninit();
-    }
-    return this.pluginContent.handleEvent(event);
-  },
-
-  receiveMessage(msg) {
-    return this.pluginContent.receiveMessage(msg);
-  },
-};
-
-PluginContentStub.init();
-
-// This is a temporary hack to prevent regressions (bug 1471327).
-void content;
-
-addEventListener("DOMWindowFocus", function(event) {
-  sendAsyncMessage("DOMWindowFocus", {});
-}, false);
-
-// We use this shim so that ContentWebRTC.jsm will not be loaded until
-// it is actually needed.
-var ContentWebRTCShim = message => ContentWebRTC.receiveMessage(message);
-
-addMessageListener("rtcpeer:Allow", ContentWebRTCShim);
-addMessageListener("rtcpeer:Deny", ContentWebRTCShim);
-addMessageListener("webrtc:Allow", ContentWebRTCShim);
-addMessageListener("webrtc:Deny", ContentWebRTCShim);
-addMessageListener("webrtc:StopSharing", ContentWebRTCShim);
-
-var PageMetadataMessenger = {
-  init() {
-    addMessageListener("PageMetadata:GetPageData", this);
-    addMessageListener("PageMetadata:GetMicroformats", this);
-    this.init = null;
-  },
+class PageMetadataChild extends ActorChild {
   receiveMessage(message) {
     switch (message.name) {
       case "PageMetadata:GetPageData": {
-        let target = contextMenu.getTarget(message);
-        let result = PageMetadata.getData(content.document, target);
-        sendAsyncMessage("PageMetadata:PageDataResult", result);
+        let target = ContextMenuChild.getTarget(this.mm, message);
+        let result = PageMetadata.getData(this.content.document, target);
+        this.mm.sendAsyncMessage("PageMetadata:PageDataResult", result);
         break;
       }
       case "PageMetadata:GetMicroformats": {
-        let target = contextMenu.getTarget(message);
-        let result = PageMetadata.getMicroformats(content.document, target);
-        sendAsyncMessage("PageMetadata:MicroformatsResult", result);
+        let target = ContextMenuChild.getTarget(this.mm, message);
+        let result = PageMetadata.getMicroformats(this.content.document, target);
+        this.mm.sendAsyncMessage("PageMetadata:MicroformatsResult", result);
         break;
       }
     }
   }
-};
-PageMetadataMessenger.init();
-
-addEventListener("InstallBrowserTheme", LightWeightThemeWebInstallListener, false, true);
-addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstallListener, false, true);
-addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstallListener, false, true);
-
-let OfflineApps = {
-  _docId: 0,
-  _docIdMap: new Map(),
-
-  _docManifestSet: new Set(),
-
-  _observerAdded: false,
-  registerWindow(aWindow) {
-    if (!this._observerAdded) {
-      this._observerAdded = true;
-      Services.obs.addObserver(this, "offline-cache-update-completed", true);
-    }
-    let manifestURI = this._getManifestURI(aWindow);
-    this._docManifestSet.add(manifestURI.spec);
-  },
-
-  handleEvent(event) {
-    if (event.type == "MozApplicationManifest") {
-      this.offlineAppRequested(event.originalTarget.defaultView);
-    }
-  },
-
-  _getManifestURI(aWindow) {
-    if (!aWindow.document.documentElement)
-      return null;
-
-    var attr = aWindow.document.documentElement.getAttribute("manifest");
-    if (!attr)
-      return null;
-
-    try {
-      return Services.io.newURI(attr, aWindow.document.characterSet,
-                                Services.io.newURI(aWindow.location.href));
-    } catch (e) {
-      return null;
-    }
-  },
-
-  offlineAppRequested(aContentWindow) {
-    this.registerWindow(aContentWindow);
-    if (!Services.prefs.getBoolPref("browser.offline-apps.notify")) {
-      return;
-    }
-
-    let currentURI = aContentWindow.document.documentURIObject;
-    // don't bother showing UI if the user has already made a decision
-    if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
-      return;
-
-    try {
-      if (Services.prefs.getBoolPref("offline-apps.allow_by_default")) {
-        // all pages can use offline capabilities, no need to ask the user
-        return;
-      }
-    } catch (e) {
-      // this pref isn't set by default, ignore failures
-    }
-    let docId = ++this._docId;
-    this._docIdMap.set(docId, Cu.getWeakReference(aContentWindow.document));
-    sendAsyncMessage("OfflineApps:RequestPermission", {
-      uri: currentURI.spec,
-      docId,
-    });
-  },
-
-  _startFetching(aDocument) {
-    if (!aDocument.documentElement)
-      return;
-
-    let manifestURI = this._getManifestURI(aDocument.defaultView);
-    if (!manifestURI)
-      return;
-
-    var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
-                        getService(Ci.nsIOfflineCacheUpdateService);
-    updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject,
-                                 aDocument.nodePrincipal, aDocument.defaultView);
-  },
-
-  receiveMessage(aMessage) {
-    if (aMessage.name == "OfflineApps:StartFetching") {
-      let doc = this._docIdMap.get(aMessage.data.docId);
-      doc = doc && doc.get();
-      if (doc) {
-        this._startFetching(doc);
-      }
-      this._docIdMap.delete(aMessage.data.docId);
-    }
-  },
-
-  observe(aSubject, aTopic, aState) {
-    if (aTopic == "offline-cache-update-completed") {
-      let cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
-      let uri = cacheUpdate.manifestURI;
-      if (uri && this._docManifestSet.has(uri.spec)) {
-        sendAsyncMessage("OfflineApps:CheckUsage", {uri: uri.spec});
-      }
-    }
-  },
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
-                                          Ci.nsISupportsWeakReference]),
-};
-
-addEventListener("MozApplicationManifest", OfflineApps, false);
-addMessageListener("OfflineApps:StartFetching", OfflineApps);
+}
rename from browser/modules/PageStyleHandler.jsm
rename to browser/actors/PageStyleChild.jsm
--- a/browser/modules/PageStyleHandler.jsm
+++ b/browser/actors/PageStyleChild.jsm
@@ -1,91 +1,93 @@
 /* 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/Services.jsm");
 
-var EXPORTED_SYMBOLS = ["PageStyleHandler"];
+var EXPORTED_SYMBOLS = ["PageStyleChild"];
 
-var PageStyleHandler = {
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
+
+class PageStyleChild extends ActorChild {
   getViewer(content) {
     return content.docShell.contentViewer;
-  },
+  }
 
   sendStyleSheetInfo(mm) {
     let content = mm.content;
     let filteredStyleSheets = this._filterStyleSheets(this.getAllStyleSheets(content), content);
 
     mm.sendAsyncMessage("PageStyle:StyleSheets", {
       filteredStyleSheets,
       authorStyleDisabled: this.getViewer(content).authorStyleDisabled,
       preferredStyleSheetSet: content.document.preferredStyleSheetSet
     });
-  },
+  }
 
   getAllStyleSheets(frameset) {
     let selfSheets = Array.slice(frameset.document.styleSheets);
     let subSheets = Array.map(frameset.frames, frame => this.getAllStyleSheets(frame));
     return selfSheets.concat(...subSheets);
-  },
+  }
 
   receiveMessage(msg) {
     let content = msg.target.content;
     switch (msg.name) {
       case "PageStyle:Switch":
         this.getViewer(content).authorStyleDisabled = false;
         this._stylesheetSwitchAll(content, msg.data.title);
         break;
 
       case "PageStyle:Disable":
         this.getViewer(content).authorStyleDisabled = true;
         break;
     }
 
     this.sendStyleSheetInfo(msg.target);
-  },
+  }
 
   handleEvent(event) {
     let win = event.target.ownerGlobal;
     if (win != win.top) {
       return;
     }
 
     let mm = win.docShell.messageManager;
     this.sendStyleSheetInfo(mm);
-  },
+  }
 
   _stylesheetSwitchAll(frameset, title) {
     if (!title || this._stylesheetInFrame(frameset, title)) {
       this._stylesheetSwitchFrame(frameset, title);
     }
 
     for (let i = 0; i < frameset.frames.length; i++) {
       // Recurse into sub-frames.
       this._stylesheetSwitchAll(frameset.frames[i], title);
     }
-  },
+  }
 
   _stylesheetSwitchFrame(frame, title) {
     var docStyleSheets = frame.document.styleSheets;
 
     for (let i = 0; i < docStyleSheets.length; ++i) {
       let docStyleSheet = docStyleSheets[i];
       if (docStyleSheet.title) {
         docStyleSheet.disabled = (docStyleSheet.title != title);
       } else if (docStyleSheet.disabled) {
         docStyleSheet.disabled = false;
       }
     }
-  },
+  }
 
   _stylesheetInFrame(frame, title) {
     return Array.some(frame.document.styleSheets, (styleSheet) => styleSheet.title == title);
-  },
+  }
 
   _filterStyleSheets(styleSheets, content) {
     let result = [];
 
     for (let currentStyleSheet of styleSheets) {
       if (!currentStyleSheet.title)
         continue;
 
@@ -118,10 +120,10 @@ var PageStyleHandler = {
       result.push({
         title: currentStyleSheet.title,
         disabled: currentStyleSheet.disabled,
         href: sentURI,
       });
     }
 
     return result;
-  },
-};
+  }
+}
rename from browser/modules/PluginContent.jsm
rename to browser/actors/PluginChild.jsm
--- a/browser/modules/PluginContent.jsm
+++ b/browser/actors/PluginChild.jsm
@@ -1,73 +1,71 @@
 /* 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 = [ "PluginContent" ];
+var EXPORTED_SYMBOLS = ["PluginChild"];
+
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
 
+ChromeUtils.defineModuleGetter(this, "ContextMenuChild",
+                               "resource:///actors/ContextMenuChild.jsm");
+
 XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
   const url = "chrome://browser/locale/browser.properties";
   return Services.strings.createBundle(url);
 });
 
 ChromeUtils.defineModuleGetter(this, "AppConstants",
   "resource://gre/modules/AppConstants.jsm");
 
-var PluginContent = function(global) {
-  this.init(global);
-};
-
 const OVERLAY_DISPLAY = {
   HIDDEN: 0, // The overlay will be transparent
   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
 };
 
-PluginContent.prototype = {
-  init(global) {
-    this.global = global;
-    // Need to hold onto the content window or else it'll get destroyed
-    this.content = this.global.content;
+class PluginChild extends ActorChild {
+  constructor(mm) {
+    super(mm);
+
     // 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();
 
-    global.addEventListener("pagehide", this, true);
-    global.addEventListener("pageshow", this, true);
-  },
+    this.mm.addEventListener("pagehide", this, true);
+    this.mm.addEventListener("pageshow", this, true);
+  }
 
   receiveMessage(msg) {
     switch (msg.name) {
       case "BrowserPlugins:ActivatePlugins":
         this.activatePlugins(msg.data.pluginInfo, msg.data.newState);
         break;
       case "BrowserPlugins:NotificationShown":
         setTimeout(() => this.updateNotificationUI(), 0);
         break;
       case "BrowserPlugins:ContextMenuCommand":
-        let contextMenu = this.global.contextMenu;
-
         switch (msg.data.command) {
           case "play":
-            this._showClickToPlayNotification(contextMenu.getTarget(msg, "plugin"), true);
+            this._showClickToPlayNotification(ContextMenuChild.getTarget(this.mm, msg, "plugin"), true);
             break;
           case "hide":
-            this.hideClickToPlayOverlay(contextMenu.getTarget(msg, "plugin"));
+            this.hideClickToPlayOverlay(ContextMenuChild.getTarget(this.mm, msg, "plugin"));
             break;
         }
         break;
       case "BrowserPlugins:NPAPIPluginProcessCrashed":
         this.NPAPIPluginProcessCrashed({
           pluginName: msg.data.pluginName,
           runID: msg.data.runID,
           state: msg.data.state,
@@ -80,61 +78,61 @@ PluginContent.prototype = {
         });
         break;
       case "BrowserPlugins:Test:ClearCrashData":
         // This message should ONLY ever be sent by automated tests.
         if (Services.prefs.getBoolPref("plugins.testmode")) {
           this.pluginCrashData.clear();
         }
     }
-  },
+  }
 
-  observe: function observe(aSubject, aTopic, aData) {
+  observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "decoder-doctor-notification":
         let data = JSON.parse(aData);
         let type = data.type.toLowerCase();
         if (type == "cannot-play" &&
             this.haveShownNotification &&
             aSubject.top.document == this.content.document &&
             data.formats.toLowerCase().includes("application/x-mpegurl", 0)) {
-          this.global.content.pluginRequiresReload = true;
+          this.content.pluginRequiresReload = true;
           this.updateNotificationUI(this.content.document);
         }
     }
-  },
+  }
 
   onPageShow(event) {
     // Ignore events that aren't from the main document.
     if (!this.content || event.target != this.content.document) {
       return;
     }
 
     // The PluginClickToPlay events are not fired when navigating using the
     // BF cache. |persisted| is true when the page is loaded from the
     // BF cache, so this code reshows the notification if necessary.
     if (event.persisted) {
       this.reshowClickToPlayNotification();
     }
-  },
+  }
 
   onPageHide(event) {
     // Ignore events that aren't from the main document.
     if (!this.content || event.target != this.content.document) {
       return;
     }
 
     this.clearPluginCaches();
     this.haveShownNotification = false;
-  },
+  }
 
   getPluginUI(plugin, anonid) {
     return plugin.ownerDocument.
            getAnonymousElementByAttribute(plugin, "anonid", anonid);
-  },
+  }
 
   _getPluginInfo(pluginElement) {
     let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
     pluginElement.QueryInterface(Ci.nsIObjectLoadingContent);
 
     let tagMimetype;
     let pluginName = gNavigatorBundle.GetStringFromName("pluginInfo.unknownPlugin");
     let pluginTag = null;
@@ -173,17 +171,17 @@ PluginContent.prototype = {
 
     return { mimetype: tagMimetype,
              pluginName,
              pluginTag,
              permissionString,
              fallbackType,
              blocklistState,
            };
-  },
+  }
 
   /**
    * _getPluginInfoForTag is called when iterating the plugins for a document,
    * and what we get from nsIDOMWindowUtils is an nsIPluginTag, and not an
    * nsIObjectLoadingContent. This only should happen if the plugin is
    * click-to-play (see bug 1186948).
    */
   _getPluginInfoForTag(pluginTag, tagMimetype) {
@@ -222,27 +220,27 @@ PluginContent.prototype = {
              permissionString,
              // Since we should only have entered _getPluginInfoForTag when
              // examining a click-to-play plugin, we can safely hard-code
              // this fallback type, since we don't actually have an
              // nsIObjectLoadingContent to check.
              fallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
              blocklistState,
            };
-  },
+  }
 
   /**
    * Update the visibility of the plugin overlay.
    */
   setVisibility(plugin, overlay, overlayDisplayState) {
     overlay.classList.toggle("visible", overlayDisplayState != OVERLAY_DISPLAY.HIDDEN);
     if (overlayDisplayState != OVERLAY_DISPLAY.HIDDEN) {
       overlay.removeAttribute("dismissed");
     }
-  },
+  }
 
   /**
    * Adjust the style in which the overlay will be displayed. It might be adjusted
    * based on its size, or if there's some other element covering all corners of
    * the overlay.
    *
    * This function will handle adjusting the style of the overlay, but will
    * not handle hiding it. That is done by setVisibility with the return value
@@ -333,17 +331,17 @@ PluginContent.prototype = {
       let el = cwu.elementFromPoint(x, y, true, true);
       if (el === plugin) {
         return overlayDisplay;
       }
     }
 
     overlay.setAttribute("sizing", "blank");
     return OVERLAY_DISPLAY.BLANK;
-  },
+  }
 
   addLinkClickCallback(linkNode, callbackName /* callbackArgs...*/) {
     // XXX just doing (callback)(arg) was giving a same-origin error. bug?
     let self = this;
     let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
     linkNode.addEventListener("click",
                               function(evt) {
                                 if (!evt.isTrusted)
@@ -363,17 +361,17 @@ PluginContent.prototype = {
                                   evt.preventDefault();
                                   if (callbackArgs.length == 0)
                                     callbackArgs = [ evt ];
                                   evt.preventDefault();
                                   (self[callbackName]).apply(self, callbackArgs);
                                 }
                               },
                               true);
-  },
+  }
 
   // Helper to get the binding handler type from a plugin object
   _getBindingType(plugin) {
     if (!(plugin instanceof Ci.nsIObjectLoadingContent))
       return null;
 
     switch (plugin.pluginFallbackType) {
       case Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED:
@@ -390,17 +388,17 @@ PluginContent.prototype = {
       case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
         return "PluginVulnerableUpdatable";
       case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
         return "PluginVulnerableNoUpdate";
       default:
         // Not all states map to a handler
         return null;
     }
-  },
+  }
 
   handleEvent(event) {
     let eventType = event.type;
 
     if (eventType == "pagehide") {
       this.onPageHide(event);
       return;
     }
@@ -533,22 +531,22 @@ PluginContent.prototype = {
           overlay.setAttribute("dismissed", "true");
         }
       }, true);
     }
 
     if (shouldShowNotification) {
       this._showClickToPlayNotification(plugin, false);
     }
-  },
+  }
 
   isKnownPlugin(objLoadingContent) {
     return (objLoadingContent.getContentTypeForMIMEType(objLoadingContent.actualType) ==
             Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
-  },
+  }
 
   canActivatePlugin(objLoadingContent) {
     // if this isn't a known plugin, we can't activate it
     // (this also guards pluginHost.getPermissionStringForType against
     // unexpected input)
     if (!this.isKnownPlugin(objLoadingContent))
       return false;
 
@@ -559,32 +557,32 @@ PluginContent.prototype = {
 
     let isFallbackTypeValid =
       objLoadingContent.pluginFallbackType >= Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY &&
       objLoadingContent.pluginFallbackType <= Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET;
 
     return !objLoadingContent.activated &&
            pluginPermission != Ci.nsIPermissionManager.DENY_ACTION &&
            isFallbackTypeValid;
-  },
+  }
 
   hideClickToPlayOverlay(plugin) {
     let overlay = this.getPluginUI(plugin, "main");
     if (overlay) {
       overlay.classList.remove("visible");
     }
-  },
+  }
 
   // Forward a link click callback to the chrome process.
   forwardCallback(name, pluginTag) {
-    this.global.sendAsyncMessage("PluginContent:LinkClickCallback",
+    this.mm.sendAsyncMessage("PluginContent:LinkClickCallback",
       { name, pluginTag });
-  },
+  }
 
-  submitReport: function submitReport(plugin) {
+  submitReport(plugin) {
     if (!AppConstants.MOZ_CRASHREPORTER) {
       return;
     }
     if (!plugin) {
       Cu.reportError("Attempted to submit crash report without an associated plugin.");
       return;
     }
     if (!(plugin instanceof Ci.nsIObjectLoadingContent)) {
@@ -597,23 +595,23 @@ PluginContent.prototype = {
     let submitURLOptIn = this.getPluginUI(plugin, "submitURLOptIn").checked;
     let keyVals = {};
     let userComment = this.getPluginUI(plugin, "submitComment").value.trim();
     if (userComment)
       keyVals.PluginUserComment = userComment;
     if (submitURLOptIn)
       keyVals.PluginContentURL = plugin.ownerDocument.URL;
 
-    this.global.sendAsyncMessage("PluginContent:SubmitReport",
+    this.mm.sendAsyncMessage("PluginContent:SubmitReport",
                                  { runID, keyVals, submitURLOptIn });
-  },
+  }
 
   reloadPage() {
-    this.global.content.location.reload();
-  },
+    this.content.location.reload();
+  }
 
   // Event listener for click-to-play plugins.
   _handleClickToPlayEvent(plugin) {
     let doc = plugin.ownerDocument;
     let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
     let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
     // guard against giving pluginHost.getPermissionStringForType a type
     // not associated with any known plugin
@@ -631,54 +629,54 @@ PluginContent.prototype = {
         overlay.classList.remove("visible");
       }
       return;
     }
 
     if (overlay) {
       overlay.addEventListener("click", this, true);
     }
-  },
+  }
 
   onOverlayClick(event) {
     let document = event.target.ownerDocument;
     let plugin = document.getBindingParent(event.target);
     let overlay = this.getPluginUI(plugin, "main");
     // Have to check that the target is not the link to update the plugin
     if (!(ChromeUtils.getClassName(event.originalTarget) === "HTMLAnchorElement") &&
         (event.originalTarget.getAttribute("anonid") != "closeIcon") &&
         !overlay.hasAttribute("dismissed") &&
         event.button == 0 &&
         event.isTrusted) {
       this._showClickToPlayNotification(plugin, true);
     event.stopPropagation();
     event.preventDefault();
     }
-  },
+  }
 
   reshowClickToPlayNotification() {
-    let contentWindow = this.global.content;
+    let contentWindow = this.content;
     let cwu = contentWindow.windowUtils;
     let plugins = cwu.plugins;
     for (let plugin of plugins) {
       let overlay = this.getPluginUI(plugin, "main");
       if (overlay)
         overlay.removeEventListener("click", this, true);
       let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
       if (this.canActivatePlugin(objLoadingContent))
         this._handleClickToPlayEvent(plugin);
     }
     this._showClickToPlayNotification(null, false);
-  },
+  }
 
   /**
    * Activate the plugins that the user has specified.
    */
   activatePlugins(pluginInfo, newState) {
-    let contentWindow = this.global.content;
+    let contentWindow = this.content;
     let cwu = contentWindow.windowUtils;
     let plugins = cwu.plugins;
     let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 
     let pluginFound = false;
     for (let plugin of plugins) {
       plugin.QueryInterface(Ci.nsIObjectLoadingContent);
       if (!this.isKnownPlugin(plugin)) {
@@ -705,17 +703,17 @@ PluginContent.prototype = {
     // If there are no instances of the plugin on the page any more or if we've
     // noted that the content needs to be reloaded due to replacing HLS, what the
     // user probably needs is for us to allow and then refresh.
     if (newState != "block" && newState != "blockalways" && newState != "continueblocking" &&
        (!pluginFound || contentWindow.pluginRequiresReload)) {
       this.reloadPage();
     }
     this.updateNotificationUI();
-  },
+  }
 
   _showClickToPlayNotification(plugin, showNow) {
     let plugins = [];
 
     // If plugin is null, that means the user has navigated back to a page with
     // plugins, and we need to collect all the plugins.
     if (plugin === null) {
       let contentWindow = this.content;
@@ -763,22 +761,22 @@ PluginContent.prototype = {
         pluginInfo.pluginPermissionType = undefined;
       }
 
       this.pluginData.set(pluginInfo.permissionString, pluginInfo);
     }
 
     this.haveShownNotification = true;
 
-    this.global.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", {
+    this.mm.sendAsyncMessage("PluginContent:ShowClickToPlayNotification", {
       plugins: [...this.pluginData.values()],
       showNow,
       location,
     }, null, principal);
-  },
+  }
 
   /**
    * Updates the "hidden plugin" notification bar UI.
    *
    * @param document (optional)
    *        Specify the document that is causing the update.
    *        This is useful when the document is possibly no longer
    *        the current loaded document (for example, if we're
@@ -846,35 +844,35 @@ PluginContent.prototype = {
         if (actions.size == 0) {
           break;
         }
       }
     }
 
     // If there are any items remaining in `actions` now, they are hidden
     // plugins that need a notification bar.
-    this.global.sendAsyncMessage("PluginContent:UpdateHiddenPluginUI", {
+    this.mm.sendAsyncMessage("PluginContent:UpdateHiddenPluginUI", {
       haveInsecure,
       actions: [...actions.values()],
       location,
     }, null, principal);
-  },
+  }
 
   removeNotification(name) {
-    this.global.sendAsyncMessage("PluginContent:RemoveNotification", { name });
-  },
+    this.mm.sendAsyncMessage("PluginContent:RemoveNotification", { name });
+  }
 
   clearPluginCaches() {
     this.pluginData.clear();
     this.pluginCrashData.clear();
-  },
+  }
 
   hideNotificationBar(name) {
-    this.global.sendAsyncMessage("PluginContent:HideNotificationBar", { name });
-  },
+    this.mm.sendAsyncMessage("PluginContent:HideNotificationBar", { name });
+  }
 
   /**
    * Determines whether or not the crashed plugin is contained within current
    * full screen DOM element.
    * @param fullScreenElement (DOM element)
    *   The DOM element that is currently full screen, or null.
    * @param domElement
    *   The DOM element which contains the crashed plugin, or the crashed plugin
@@ -905,17 +903,17 @@ PluginContent.prototype = {
     if (fullScreenElement.contains(domElement)) {
       return true;
     }
     let parentIframe = domElement.ownerGlobal.frameElement;
     if (parentIframe) {
       return this.isWithinFullScreenElement(fullScreenElement, parentIframe);
     }
     return false;
-  },
+  }
 
   /**
    * The PluginCrashed event handler. Note that the PluginCrashed event is
    * fired for both NPAPI and Gecko Media plugins. In the latter case, the
    * target of the event is the document that the GMP is being used in.
    */
   onPluginCrashed(target, aEvent) {
     if (!(aEvent instanceof this.content.PluginCrashedEvent))
@@ -949,24 +947,24 @@ PluginContent.prototype = {
       this.pluginCrashData.delete(target.runID);
     }
 
     this.setCrashedNPAPIPluginState({
       plugin: target,
       state: crashData.state,
       message: crashData.message,
     });
-  },
+  }
 
   NPAPIPluginProcessCrashed({pluginName, runID, state}) {
     let message =
       gNavigatorBundle.formatStringFromName("crashedpluginsMessage.title",
                                             [pluginName], 1);
 
-    let contentWindow = this.global.content;
+    let contentWindow = this.content;
     let cwu = contentWindow.windowUtils;
     let plugins = cwu.plugins;
 
     for (let plugin of plugins) {
       if (plugin instanceof Ci.nsIObjectLoadingContent &&
           plugin.runID == runID) {
         // The parent has told us that the plugin process has died.
         // It's possible that this content process hasn't yet noticed,
@@ -989,17 +987,17 @@ PluginContent.prototype = {
               instances: new WeakSet(),
             });
           }
           let crashData = this.pluginCrashData.get(runID);
           crashData.instances.add(plugin);
         }
       }
     }
-  },
+  }
 
   setCrashedNPAPIPluginState({plugin, state, message}) {
     // Force a layout flush so the binding is attached.
     plugin.clientTop;
     let overlay = this.getPluginUI(plugin, "main");
     let statusDiv = this.getPluginUI(plugin, "submitStatus");
     let optInCB = this.getPluginUI(plugin, "submitURLOptIn");
 
@@ -1048,39 +1046,39 @@ PluginContent.prototype = {
       // Notify others that the crash reporter UI is now ready.
       // Currently, this event is only used by tests.
       let winUtils = this.content.windowUtils;
       let event = new this.content.CustomEvent("PluginCrashReporterDisplayed", {bubbles: true});
       winUtils.dispatchEventToChromeOnly(plugin, event);
     } else if (!doc.mozNoPluginCrashedNotification) {
       // If another plugin on the page was large enough to show our UI, we don't
       // want to show a notification bar.
-      this.global.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
+      this.mm.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
                                    { messageString: message, pluginID: runID });
       // Remove the notification when the page is reloaded.
       doc.defaultView.top.addEventListener("unload", event => {
         this.hideNotificationBar("plugin-crashed");
       });
     }
-  },
+  }
 
   NPAPIPluginCrashReportSubmitted({ runID, state }) {
     this.pluginCrashData.delete(runID);
-    let contentWindow = this.global.content;
+    let contentWindow = this.content;
     let cwu = contentWindow.windowUtils;
     let plugins = cwu.plugins;
 
     for (let plugin of plugins) {
       if (plugin instanceof Ci.nsIObjectLoadingContent &&
           plugin.runID == runID) {
         let statusDiv = this.getPluginUI(plugin, "submitStatus");
         statusDiv.setAttribute("status", state);
       }
     }
-  },
+  }
 
   GMPCrashed(aEvent) {
     let target          = aEvent.target;
     let pluginName      = aEvent.pluginName;
     let gmpPlugin       = aEvent.gmpPlugin;
     let pluginID        = aEvent.pluginID;
     let doc             = target.document;
 
@@ -1088,17 +1086,17 @@ PluginContent.prototype = {
       // TODO: Throw exception? How did we get here?
       return;
     }
 
     let messageString =
       gNavigatorBundle.formatStringFromName("crashedpluginsMessage.title",
                                             [pluginName], 1);
 
-    this.global.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
+    this.mm.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
                                  { messageString, pluginID });
 
     // Remove the notification when the page is reloaded.
     doc.defaultView.top.addEventListener("unload", event => {
       this.hideNotificationBar("plugin-crashed");
     });
-  },
-};
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/actors/URIFixupChild.jsm
@@ -0,0 +1,37 @@
+/* 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 = ["URIFixupChild"];
+
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
+
+class URIFixupChild extends ActorChild {
+  observe(subject) {
+    let fixupInfo = subject.QueryInterface(Ci.nsIURIFixupInfo);
+    if (!fixupInfo.consumer) {
+      return;
+    }
+
+    // Ignore info from other docshells
+    let parent = fixupInfo.consumer.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeRootTreeItem;
+    if (parent != this.mm.docShell)
+      return;
+
+    let data = {};
+    for (let f of Object.keys(fixupInfo)) {
+      if (f == "consumer" || typeof fixupInfo[f] == "function")
+        continue;
+
+      if (fixupInfo[f] && fixupInfo[f] instanceof Ci.nsIURI) {
+        data[f] = fixupInfo[f].spec;
+      } else {
+        data[f] = fixupInfo[f];
+      }
+    }
+
+    this.mm.sendAsyncMessage("Browser:URIFixup", data);
+  }
+}
rename from browser/modules/ContentWebRTC.jsm
rename to browser/actors/WebRTCChild.jsm
--- a/browser/modules/ContentWebRTC.jsm
+++ b/browser/actors/WebRTCChild.jsm
@@ -1,41 +1,42 @@
 /* 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 = [ "ContentWebRTC" ];
+var EXPORTED_SYMBOLS = ["WebRTCChild"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
                                    "@mozilla.org/mediaManagerService;1",
                                    "nsIMediaManagerService");
 
 const kBrowserURL = AppConstants.BROWSER_CHROME_URL;
 
-var ContentWebRTC = {
+class WebRTCChild extends ActorChild {
   // Called only for 'unload' to remove pending gUM prompts in reloaded frames.
-  handleEvent(aEvent) {
+  static handleEvent(aEvent) {
     let contentWindow = aEvent.target.defaultView;
     let mm = getMessageManagerForWindow(contentWindow);
     for (let key of contentWindow.pendingGetUserMediaRequests.keys()) {
       mm.sendAsyncMessage("webrtc:CancelRequest", key);
     }
     for (let key of contentWindow.pendingPeerConnectionRequests.keys()) {
       mm.sendAsyncMessage("rtcpeer:CancelRequest", key);
     }
-  },
+  }
 
   // This observer is registered in ContentObservers.js to avoid
   // loading this .jsm when WebRTC is not in use.
-  observe(aSubject, aTopic, aData) {
+  static observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "getUserMedia:request":
         handleGUMRequest(aSubject, aTopic, aData);
         break;
       case "recording-device-stopped":
         handleGUMStop(aSubject, aTopic, aData);
         break;
       case "PeerConnection:request":
@@ -43,17 +44,17 @@ var ContentWebRTC = {
         break;
       case "recording-device-events":
         updateIndicators(aSubject, aTopic, aData);
         break;
       case "recording-window-ended":
         removeBrowserSpecificIndicator(aSubject, aTopic, aData);
         break;
     }
-  },
+  }
 
   receiveMessage(aMessage) {
     switch (aMessage.name) {
       case "rtcpeer:Allow":
       case "rtcpeer:Deny": {
         let callID = aMessage.data.callID;
         let contentWindow = Services.wm.getOuterWindowWithId(aMessage.data.windowID);
         forgetPCRequest(contentWindow, callID);
@@ -79,17 +80,17 @@ var ContentWebRTC = {
       case "webrtc:Deny":
         denyGUMRequest(aMessage.data);
         break;
       case "webrtc:StopSharing":
         Services.obs.notifyObservers(null, "getUserMedia:revoke", aMessage.data);
         break;
     }
   }
-};
+}
 
 function handlePCRequest(aSubject, aTopic, aData) {
   let { windowID, innerWindowID, callID, isSecure } = aSubject;
   let contentWindow = Services.wm.getOuterWindowWithId(windowID);
 
   let mm = getMessageManagerForWindow(contentWindow);
   if (!mm) {
     // Workaround for Bug 1207784. To use WebRTC, add-ons right now use
@@ -259,27 +260,27 @@ function forgetPCRequest(aContentWindow,
 }
 
 function setupPendingListsInitially(aContentWindow) {
   if (aContentWindow.pendingGetUserMediaRequests) {
     return;
   }
   aContentWindow.pendingGetUserMediaRequests = new Map();
   aContentWindow.pendingPeerConnectionRequests = new Set();
-  aContentWindow.addEventListener("unload", ContentWebRTC);
+  aContentWindow.addEventListener("unload", WebRTCChild.handleEvent);
 }
 
 function forgetPendingListsEventually(aContentWindow) {
   if (aContentWindow.pendingGetUserMediaRequests.size ||
       aContentWindow.pendingPeerConnectionRequests.size) {
     return;
   }
   aContentWindow.pendingGetUserMediaRequests = null;
   aContentWindow.pendingPeerConnectionRequests = null;
-  aContentWindow.removeEventListener("unload", ContentWebRTC);
+  aContentWindow.removeEventListener("unload", WebRTCChild.handleEvent);
 }
 
 function updateIndicators(aSubject, aTopic, aData) {
   if (aSubject instanceof Ci.nsIPropertyBag &&
       aSubject.getProperty("requestURL") == kBrowserURL) {
     // Ignore notifications caused by the browser UI showing previews.
     return;
   }
new file mode 100644
--- /dev/null
+++ b/browser/actors/moz.build
@@ -0,0 +1,42 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+    BUG_COMPONENT = ("Firefox", "General")
+
+with Files("LightWeightThemeInstallChild.jsm"):
+    BUG_COMPONENT = ("Firefox", "Theme")
+
+with Files("PageInfoChild.jsm"):
+    BUG_COMPONENT = ("Firefox", "Page Info Window")
+
+with Files("PageStyleChild.jsm"):
+    BUG_COMPONENT = ("Firefox", "Menus")
+
+with Files("PluginChild.jsm"):
+    BUG_COMPONENT = ("Core", "Plug-ins")
+
+with Files("WebRTCChild.jsm"):
+    BUG_COMPONENT = ("Firefox", "Device Permissions")
+
+FINAL_TARGET_FILES.actors += [
+    'AboutReaderChild.jsm',
+    'BlockedSiteChild.jsm',
+    'BrowserTabChild.jsm',
+    'ClickHandlerChild.jsm',
+    'ContentSearchChild.jsm',
+    'ContextMenuChild.jsm',
+    'DOMFullscreenChild.jsm',
+    'LightWeightThemeInstallChild.jsm',
+    'NetErrorChild.jsm',
+    'OfflineAppsChild.jsm',
+    'PageInfoChild.jsm',
+    'PageMetadataChild.jsm',
+    'PageStyleChild.jsm',
+    'PluginChild.jsm',
+    'URIFixupChild.jsm',
+    'WebRTCChild.jsm',
+]
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -484,19 +484,16 @@ const gSessionHistoryObserver = {
     if (topic != "browser:purge-session-history")
       return;
 
     var backCommand = document.getElementById("Browser:Back");
     backCommand.setAttribute("disabled", "true");
     var fwdCommand = document.getElementById("Browser:Forward");
     fwdCommand.setAttribute("disabled", "true");
 
-    // Hide session restore button on about:home
-    window.messageManager.broadcastAsyncMessage("Browser:HideSessionRestoreButton");
-
     // Clear undo history of the URL bar
     gURLBar.editor.transactionManager.clear();
   }
 };
 
 const gStoragePressureObserver = {
   _lastNotificationTime: -1,
 
@@ -1313,18 +1310,18 @@ var gBrowserInit = {
     LanguageDetectionListener.init();
     BrowserOnClick.init();
     FeedHandler.init();
     ContentBlocking.init();
     CaptivePortalWatcher.init();
     ZoomUI.init(window);
 
     let mm = window.getGroupMessageManager("browsers");
-    mm.loadFrameScript("chrome://browser/content/tab-content.js", true);
-    mm.loadFrameScript("chrome://browser/content/content.js", true);
+    mm.loadFrameScript("chrome://browser/content/tab-content.js", true, true);
+    mm.loadFrameScript("chrome://browser/content/content.js", true, true);
     mm.loadFrameScript("chrome://global/content/content-HybridContentTelemetry.js", true);
 
     window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);
 
     if (!gMultiProcessBrowser) {
       // There is a Content:Click message manually sent from content.
       Services.els.addSystemEventListener(gBrowser.tabpanels, "click",
         contentAreaClick, true);
@@ -5204,17 +5201,17 @@ const AccessibilityRefreshBlocker = {
       Services.prefs.removeObserver(this.PREF, this);
     }
   },
 
   loadFrameScript() {
     if (!this._loaded) {
       this._loaded = true;
       let mm = window.getGroupMessageManager("browsers");
-      mm.loadFrameScript("chrome://browser/content/content-refreshblocker.js", true);
+      mm.loadFrameScript("chrome://browser/content/content-refreshblocker.js", true, true);
     }
   }
 };
 
 var TabsProgressListener = {
   onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
     // Collect telemetry data about tab load times.
     if (aWebProgress.isTopLevel && (!aRequest.originalURI || aRequest.originalURI.spec.scheme != "about")) {
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser.xhtml
@@ -0,0 +1,3 @@
+#define BROWSER_XHTML
+#include browser.xul
+#undef BROWSER_XHTML
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -67,17 +67,21 @@
         retargetdocumentfocus="urlbar"
         persist="screenX screenY width height sizemode">
 
 # All JS files which are needed by browser.xul and other top level windows to
 # support MacOS specific features *must* go into the global-scripts.inc file so
 # that they can be shared with macWindow.inc.xul.
 #include global-scripts.inc
 
-<script type="application/javascript">
+<script type="application/javascript"
+#ifdef BROWSER_XHTML
+xmlns="http://www.w3.org/1999/xhtml"
+#endif
+>
   Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", this);
   Services.scriptloader.loadSubScript("chrome://browser/content/tabbrowser.js", this);
 </script>
 
 # All sets except for popupsets (commands, keys, stringbundles and broadcasters)
 # *must* go into the browser-sets.inc file so that they can be shared with other
 # top level windows in macWindow.inc.xul.
 #include browser-sets.inc
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -11,386 +11,61 @@
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 // TabChildGlobal
 var global = this;
 
 XPCOMUtils.defineLazyModuleGetters(this, {
-  BlockedSiteContent: "resource:///modules/BlockedSiteContent.jsm",
   ContentLinkHandler: "resource:///modules/ContentLinkHandler.jsm",
   ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
-  ContentWebRTC: "resource:///modules/ContentWebRTC.jsm",
   LoginFormFactory: "resource://gre/modules/LoginManagerContent.jsm",
   InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
-  PluginContent: "resource:///modules/PluginContent.jsm",
   FormSubmitObserver: "resource:///modules/FormSubmitObserver.jsm",
-  NetErrorContent: "resource:///modules/NetErrorContent.jsm",
-  PageMetadata: "resource://gre/modules/PageMetadata.jsm",
-  WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
-  ContextMenu: "resource:///modules/ContextMenu.jsm",
-});
-
-XPCOMUtils.defineLazyProxy(this, "contextMenu", () => {
-  return new ContextMenu(global);
-});
-
-XPCOMUtils.defineLazyProxy(this, "ClickEventHandler", () => {
-  let tmp = {};
-  ChromeUtils.import("resource:///modules/ClickEventHandler.jsm", tmp);
-  return new tmp.ClickEventHandler(global);
+  ContextMenuChild: "resource:///actors/ContextMenuChild.jsm",
 });
 
 XPCOMUtils.defineLazyGetter(this, "LoginManagerContent", () => {
   let tmp = {};
   ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm", tmp);
   tmp.LoginManagerContent.setupEventListeners(global);
   return tmp.LoginManagerContent;
 });
 
 XPCOMUtils.defineLazyProxy(this, "formSubmitObserver", () => {
   return new FormSubmitObserver(content, this);
 }, {
   // stub QI
   QueryInterface: ChromeUtils.generateQI([Ci.nsIFormSubmitObserver, Ci.nsISupportsWeakReference])
 });
 
-XPCOMUtils.defineLazyProxy(this, "PageInfoListener",
-                           "resource:///modules/PageInfoListener.jsm");
-
-XPCOMUtils.defineLazyProxy(this, "LightWeightThemeWebInstallListener",
-                           "resource:///modules/LightWeightThemeWebInstallListener.jsm");
-
-Services.els.addSystemEventListener(global, "contextmenu", contextMenu, false);
-
 Services.obs.addObserver(formSubmitObserver, "invalidformsubmit", true);
 
-addMessageListener("PageInfo:getData", PageInfoListener);
-
 // NOTE: Much of this logic is duplicated in BrowserCLH.js for Android.
 addMessageListener("RemoteLogins:fillForm", function(message) {
   // intercept if ContextMenu.jsm had sent a plain object for remote targets
-  message.objects.inputElement = contextMenu.getTarget(message, "inputElement");
+  message.objects.inputElement = ContextMenuChild.getTarget(global, message, "inputElement");
   LoginManagerContent.receiveMessage(message, content);
 });
 addEventListener("DOMFormHasPassword", function(event) {
   LoginManagerContent.onDOMFormHasPassword(event, content);
   let formLike = LoginFormFactory.createFromForm(event.originalTarget);
   InsecurePasswordUtils.reportInsecurePasswords(formLike);
 });
 addEventListener("DOMInputPasswordAdded", function(event) {
   LoginManagerContent.onDOMInputPasswordAdded(event, content);
   let formLike = LoginFormFactory.createFromField(event.originalTarget);
   InsecurePasswordUtils.reportInsecurePasswords(formLike);
 });
 addEventListener("DOMAutoComplete", function(event) {
   LoginManagerContent.onUsernameInput(event);
 });
 
-var AboutBlockedSiteListener = {
-  init(chromeGlobal) {
-    addMessageListener("DeceptiveBlockedDetails", this);
-    chromeGlobal.addEventListener("AboutBlockedLoaded", this, false, true);
-    this.init = null;
-  },
-
-  get isBlockedSite() {
-    return content.document.documentURI.startsWith("about:blocked");
-  },
-
-  receiveMessage(msg) {
-    if (!this.isBlockedSite) {
-      return;
-    }
-
-    BlockedSiteContent.receiveMessage(global, msg);
-  },
-
-  handleEvent(aEvent) {
-    if (!this.isBlockedSite) {
-      return;
-    }
-
-    if (aEvent.type != "AboutBlockedLoaded") {
-      return;
-    }
-
-    BlockedSiteContent.handleEvent(global, aEvent);
-  },
-};
-AboutBlockedSiteListener.init(this);
-
-this.AboutNetAndCertErrorListener = {
-  init(chromeGlobal) {
-    addMessageListener("CertErrorDetails", this);
-    addMessageListener("Browser:CaptivePortalFreed", this);
-    chromeGlobal.addEventListener("AboutNetErrorLoad", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorOpenCaptivePortal", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorSetAutomatic", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorResetPreferences", this, false, true);
-    this.init = null;
-  },
-
-  isAboutNetError(doc) {
-    return doc.documentURI.startsWith("about:neterror");
-  },
-
-  isAboutCertError(doc) {
-    return doc.documentURI.startsWith("about:certerror");
-  },
-
-  receiveMessage(msg) {
-    if (msg.name == "CertErrorDetails") {
-      let frameDocShell = WebNavigationFrames.findDocShell(msg.data.frameId, docShell);
-      // We need nsIWebNavigation to access docShell.document.
-      frameDocShell && frameDocShell.QueryInterface(Ci.nsIWebNavigation);
-      if (!frameDocShell || !this.isAboutCertError(frameDocShell.document)) {
-        return;
-      }
-
-      NetErrorContent.onCertErrorDetails(global, msg, frameDocShell);
-    } else if (msg.name == "Browser:CaptivePortalFreed") {
-      // TODO: This check is not correct for frames.
-      if (!this.isAboutCertError(content.document)) {
-        return;
-      }
-
-      this.onCaptivePortalFreed(msg);
-    }
-  },
-
-  onCaptivePortalFreed(msg) {
-    content.dispatchEvent(new content.CustomEvent("AboutNetErrorCaptivePortalFreed"));
-  },
-
-  handleEvent(aEvent) {
-    // Documents have a null ownerDocument.
-    let doc = aEvent.originalTarget.ownerDocument || aEvent.originalTarget;
-
-    if (!this.isAboutNetError(doc) && !this.isAboutCertError(doc)) {
-      return;
-    }
-
-    NetErrorContent.handleEvent(global, aEvent);
-  },
-};
-AboutNetAndCertErrorListener.init(this);
-
-Services.els.addSystemEventListener(global, "click", ClickEventHandler, true);
-
 new ContentLinkHandler(this);
 ContentMetaHandler.init(this);
 
-var PluginContentStub = {
-  EVENTS: [
-    "PluginCrashed",
-    "PluginOutdated",
-    "PluginInstantiated",
-    "PluginRemoved",
-    "HiddenPlugin",
-  ],
-
-  MESSAGES: [
-    "BrowserPlugins:ActivatePlugins",
-    "BrowserPlugins:NotificationShown",
-    "BrowserPlugins:ContextMenuCommand",
-    "BrowserPlugins:NPAPIPluginProcessCrashed",
-    "BrowserPlugins:CrashReportSubmitted",
-    "BrowserPlugins:Test:ClearCrashData",
-  ],
-
-  _pluginContent: null,
-  get pluginContent() {
-    if (!this._pluginContent) {
-      this._pluginContent = new PluginContent(global);
-    }
-    return this._pluginContent;
-  },
-
-  init() {
-    addEventListener("unload", this);
-
-    addEventListener("PluginBindingAttached", this, true, true);
-
-    for (let event of this.EVENTS) {
-      addEventListener(event, this, true);
-    }
-    for (let msg of this.MESSAGES) {
-      addMessageListener(msg, this);
-    }
-    Services.obs.addObserver(this, "decoder-doctor-notification");
-    this.init = null;
-  },
-
-  uninit() {
-    Services.obs.removeObserver(this, "decoder-doctor-notification");
-  },
-
-  observe(subject, topic, data) {
-    return this.pluginContent.observe(subject, topic, data);
-  },
-
-  handleEvent(event) {
-    if (event.type === "unload") {
-      return this.uninit();
-    }
-    return this.pluginContent.handleEvent(event);
-  },
-
-  receiveMessage(msg) {
-    return this.pluginContent.receiveMessage(msg);
-  },
-};
-
-PluginContentStub.init();
-
 // This is a temporary hack to prevent regressions (bug 1471327).
 void content;
 
 addEventListener("DOMWindowFocus", function(event) {
   sendAsyncMessage("DOMWindowFocus", {});
 }, false);
-
-// We use this shim so that ContentWebRTC.jsm will not be loaded until
-// it is actually needed.
-var ContentWebRTCShim = message => ContentWebRTC.receiveMessage(message);
-
-addMessageListener("rtcpeer:Allow", ContentWebRTCShim);
-addMessageListener("rtcpeer:Deny", ContentWebRTCShim);
-addMessageListener("webrtc:Allow", ContentWebRTCShim);
-addMessageListener("webrtc:Deny", ContentWebRTCShim);
-addMessageListener("webrtc:StopSharing", ContentWebRTCShim);
-
-var PageMetadataMessenger = {
-  init() {
-    addMessageListener("PageMetadata:GetPageData", this);
-    addMessageListener("PageMetadata:GetMicroformats", this);
-    this.init = null;
-  },
-  receiveMessage(message) {
-    switch (message.name) {
-      case "PageMetadata:GetPageData": {
-        let target = contextMenu.getTarget(message);
-        let result = PageMetadata.getData(content.document, target);
-        sendAsyncMessage("PageMetadata:PageDataResult", result);
-        break;
-      }
-      case "PageMetadata:GetMicroformats": {
-        let target = contextMenu.getTarget(message);
-        let result = PageMetadata.getMicroformats(content.document, target);
-        sendAsyncMessage("PageMetadata:MicroformatsResult", result);
-        break;
-      }
-    }
-  }
-};
-PageMetadataMessenger.init();
-
-addEventListener("InstallBrowserTheme", LightWeightThemeWebInstallListener, false, true);
-addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstallListener, false, true);
-addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstallListener, false, true);
-
-let OfflineApps = {
-  _docId: 0,
-  _docIdMap: new Map(),
-
-  _docManifestSet: new Set(),
-
-  _observerAdded: false,
-  registerWindow(aWindow) {
-    if (!this._observerAdded) {
-      this._observerAdded = true;
-      Services.obs.addObserver(this, "offline-cache-update-completed", true);
-    }
-    let manifestURI = this._getManifestURI(aWindow);
-    this._docManifestSet.add(manifestURI.spec);
-  },
-
-  handleEvent(event) {
-    if (event.type == "MozApplicationManifest") {
-      this.offlineAppRequested(event.originalTarget.defaultView);
-    }
-  },
-
-  _getManifestURI(aWindow) {
-    if (!aWindow.document.documentElement)
-      return null;
-
-    var attr = aWindow.document.documentElement.getAttribute("manifest");
-    if (!attr)
-      return null;
-
-    try {
-      return Services.io.newURI(attr, aWindow.document.characterSet,
-                                Services.io.newURI(aWindow.location.href));
-    } catch (e) {
-      return null;
-    }
-  },
-
-  offlineAppRequested(aContentWindow) {
-    this.registerWindow(aContentWindow);
-    if (!Services.prefs.getBoolPref("browser.offline-apps.notify")) {
-      return;
-    }
-
-    let currentURI = aContentWindow.document.documentURIObject;
-    // don't bother showing UI if the user has already made a decision
-    if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
-      return;
-
-    try {
-      if (Services.prefs.getBoolPref("offline-apps.allow_by_default")) {
-        // all pages can use offline capabilities, no need to ask the user
-        return;
-      }
-    } catch (e) {
-      // this pref isn't set by default, ignore failures
-    }
-    let docId = ++this._docId;
-    this._docIdMap.set(docId, Cu.getWeakReference(aContentWindow.document));
-    sendAsyncMessage("OfflineApps:RequestPermission", {
-      uri: currentURI.spec,
-      docId,
-    });
-  },
-
-  _startFetching(aDocument) {
-    if (!aDocument.documentElement)
-      return;
-
-    let manifestURI = this._getManifestURI(aDocument.defaultView);
-    if (!manifestURI)
-      return;
-
-    var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
-                        getService(Ci.nsIOfflineCacheUpdateService);
-    updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject,
-                                 aDocument.nodePrincipal, aDocument.defaultView);
-  },
-
-  receiveMessage(aMessage) {
-    if (aMessage.name == "OfflineApps:StartFetching") {
-      let doc = this._docIdMap.get(aMessage.data.docId);
-      doc = doc && doc.get();
-      if (doc) {
-        this._startFetching(doc);
-      }
-      this._docIdMap.delete(aMessage.data.docId);
-    }
-  },
-
-  observe(aSubject, aTopic, aState) {
-    if (aTopic == "offline-cache-update-completed") {
-      let cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
-      let uri = cacheUpdate.manifestURI;
-      if (uri && this._docManifestSet.has(uri.spec)) {
-        sendAsyncMessage("OfflineApps:CheckUsage", {uri: uri.spec});
-      }
-    }
-  },
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
-                                          Ci.nsISupportsWeakReference]),
-};
-
-addEventListener("MozApplicationManifest", OfflineApps, false);
-addMessageListener("OfflineApps:StartFetching", OfflineApps);
--- a/browser/base/content/global-scripts.inc
+++ b/browser/base/content/global-scripts.inc
@@ -2,17 +2,21 @@
 # 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/.
 
 # If you update this list, you may need to add a mapping within the following
 # file so that ESLint works correctly:
 # tools/lint/eslint/eslint-plugin-mozilla/lib/environments/browser-window.js
 
-<script type="application/javascript">
+<script type="text/javascript"
+#ifdef BROWSER_XHTML
+xmlns="http://www.w3.org/1999/xhtml"
+#endif
+>
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 for (let script of [
   "chrome://browser/content/browser.js",
 
   "chrome://browser/content/browser-captivePortal.js",
   "chrome://browser/content/browser-compacttheme.js",
   "chrome://browser/content/browser-contentblocking.js",
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -9,361 +9,47 @@
 
 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
-  });
-});
-
-addEventListener("MozDOMPointerLock:Exited", function(aEvent) {
-  sendAsyncMessage("PointerLock:Exited");
-});
-
-
-addMessageListener("Browser:HideSessionRestoreButton", function(message) {
-  // Hide session restore button on about:home
-  let doc = content.document;
-  let container;
-  if (doc.documentURI.toLowerCase() == "about:home" &&
-      (container = doc.getElementById("sessionRestoreContainer"))) {
-    container.hidden = true;
-  }
-});
-
-if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
-  addMessageListener("Browser:HasSiblings", function(message) {
-    let tabChild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsITabChild);
-    let hasSiblings = message.data;
-    tabChild.hasSiblings = hasSiblings;
-  });
-}
-
-// XXX(nika): Should we try to call this in the parent process instead?
-addMessageListener("Browser:Reload", function(message) {
-  /* First, we'll try to use the session history object to reload so
-   * that framesets are handled properly. If we're in a special
-   * window (such as view-source) that has no session history, fall
-   * back on using the web navigation's reload method.
-   */
-
-  let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
-  try {
-    if (webNav.sessionHistory) {
-      webNav = webNav.sessionHistory;
-    }
-  } catch (e) {
-  }
-
-  let reloadFlags = message.data.flags;
-  try {
-    E10SUtils.wrapHandlingUserInput(content, message.data.handlingUserInput,
-                                    () => webNav.reload(reloadFlags));
-  } catch (e) {
-  }
-});
-
-addMessageListener("MixedContent:ReenableProtection", function() {
-  docShell.mixedContentChannel = null;
-});
-
 XPCOMUtils.defineLazyProxy(this, "LightweightThemeChildHelper",
   "resource:///modules/LightweightThemeChildHelper.jsm");
 
-XPCOMUtils.defineLazyProxy(this, "ManifestMessages", () => {
-  let tmp = {};
-  ChromeUtils.import("resource://gre/modules/ManifestMessages.jsm", tmp);
-  return new tmp.ManifestMessages(global);
-});
-
 let themeablePagesWhitelist = new Set([
   "about:home",
   "about:newtab",
   "about:welcome",
 ]);
 
 addEventListener("pageshow", function({ originalTarget }) {
   if (originalTarget.defaultView == content && themeablePagesWhitelist.has(content.document.documentURI)) {
     LightweightThemeChildHelper.listen(themeablePagesWhitelist);
     LightweightThemeChildHelper.update(chromeOuterWindowID, content);
   }
 }, false, true);
 
-var AboutReaderListener = {
-
-  _articlePromise: null,
-
-  _isLeavingReaderableReaderMode: false,
-
-  init() {
-    addEventListener("AboutReaderContentLoaded", this, false, true);
-    addEventListener("DOMContentLoaded", this, false);
-    addEventListener("pageshow", this, false);
-    addEventListener("pagehide", this, false);
-    addMessageListener("Reader:ToggleReaderMode", this);
-    addMessageListener("Reader:PushState", this);
-    this.init = null;
-  },
-
-  receiveMessage(message) {
-    switch (message.name) {
-      case "Reader:ToggleReaderMode":
-        if (!this.isAboutReader) {
-          this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
-          ReaderMode.enterReaderMode(docShell, content);
-        } else {
-          this._isLeavingReaderableReaderMode = this.isReaderableAboutReader;
-          ReaderMode.leaveReaderMode(docShell, content);
-        }
-        break;
-
-      case "Reader:PushState":
-        this.updateReaderButton(!!(message.data && message.data.isArticle));
-        break;
-    }
-  },
-
-  get isAboutReader() {
-    if (!content) {
-      return false;
-    }
-    return content.document.documentURI.startsWith("about:reader");
-  },
-
-  get isReaderableAboutReader() {
-    return this.isAboutReader &&
-      !content.document.documentElement.dataset.isError;
-  },
-
-  handleEvent(aEvent) {
-    if (aEvent.originalTarget.defaultView != content) {
-      return;
-    }
-
-    switch (aEvent.type) {
-      case "AboutReaderContentLoaded":
-        if (!this.isAboutReader) {
-          return;
-        }
-
-        if (content.document.body) {
-          // Update the toolbar icon to show the "reader active" icon.
-          sendAsyncMessage("Reader:UpdateReaderButton");
-          new AboutReader(global, content, this._articlePromise);
-          this._articlePromise = null;
-        }
-        break;
-
-      case "pagehide":
-        this.cancelPotentialPendingReadabilityCheck();
-        // this._isLeavingReaderableReaderMode is used here to keep the Reader Mode icon
-        // visible in the location bar when transitioning from reader-mode page
-        // back to the readable source page.
-        sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: this._isLeavingReaderableReaderMode });
-        if (this._isLeavingReaderableReaderMode) {
-          this._isLeavingReaderableReaderMode = false;
-        }
-        break;
-
-      case "pageshow":
-        // If a page is loaded from the bfcache, we won't get a "DOMContentLoaded"
-        // event, so we need to rely on "pageshow" in this case.
-        if (aEvent.persisted) {
-          this.updateReaderButton();
-        }
-        break;
-      case "DOMContentLoaded":
-        this.updateReaderButton();
-        break;
-
-    }
-  },
-
-  /**
-   * NB: this function will update the state of the reader button asynchronously
-   * after the next mozAfterPaint call (assuming reader mode is enabled and
-   * this is a suitable document). Calling it on things which won't be
-   * painted is not going to work.
-   */
-  updateReaderButton(forceNonArticle) {
-    if (!ReaderMode.isEnabledForParseOnLoad || this.isAboutReader ||
-        !content || !(content.document instanceof content.HTMLDocument) ||
-        content.document.mozSyntheticDocument) {
-      return;
-    }
-
-    this.scheduleReadabilityCheckPostPaint(forceNonArticle);
-  },
-
-  cancelPotentialPendingReadabilityCheck() {
-    if (this._pendingReadabilityCheck) {
-      removeEventListener("MozAfterPaint", this._pendingReadabilityCheck);
-      delete this._pendingReadabilityCheck;
-    }
-  },
-
-  scheduleReadabilityCheckPostPaint(forceNonArticle) {
-    if (this._pendingReadabilityCheck) {
-      // We need to stop this check before we re-add one because we don't know
-      // if forceNonArticle was true or false last time.
-      this.cancelPotentialPendingReadabilityCheck();
-    }
-    this._pendingReadabilityCheck = this.onPaintWhenWaitedFor.bind(this, forceNonArticle);
-    addEventListener("MozAfterPaint", this._pendingReadabilityCheck);
-  },
-
-  onPaintWhenWaitedFor(forceNonArticle, event) {
-    // In non-e10s, we'll get called for paints other than ours, and so it's
-    // possible that this page hasn't been laid out yet, in which case we
-    // should wait until we get an event that does relate to our layout. We
-    // determine whether any of our content got painted by checking if there
-    // are any painted rects.
-    if (!event.clientRects.length) {
-      return;
-    }
-
-    this.cancelPotentialPendingReadabilityCheck();
-    // Only send updates when there are articles; there's no point updating with
-    // |false| all the time.
-    if (ReaderMode.isProbablyReaderable(content.document)) {
-      sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: true });
-    } else if (forceNonArticle) {
-      sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
-    }
-  },
-};
-AboutReaderListener.init();
-
-
-var ContentSearchMediator = {
-
-  whitelist: new Set([
-    "about:home",
-    "about:newtab",
-    "about:welcome",
-  ]),
-
-  init(chromeGlobal) {
-    chromeGlobal.addEventListener("ContentSearchClient", this, true, true);
-    addMessageListener("ContentSearch", this);
-    this.init = null;
-  },
-
-  handleEvent(event) {
-    if (this._contentWhitelisted) {
-      this._sendMsg(event.detail.type, event.detail.data);
-    }
-  },
-
-  receiveMessage(msg) {
-    if (msg.data.type == "AddToWhitelist") {
-      for (let uri of msg.data.data) {
-        this.whitelist.add(uri);
-      }
-      this._sendMsg("AddToWhitelistAck");
-      return;
-    }
-    if (this._contentWhitelisted) {
-      this._fireEvent(msg.data.type, msg.data.data);
-    }
-  },
-
-  get _contentWhitelisted() {
-    return this.whitelist.has(content.document.documentURI);
-  },
-
-  _sendMsg(type, data = null) {
-    sendAsyncMessage("ContentSearch", {
-      type,
-      data,
-    });
-  },
-
-  _fireEvent(type, data = null) {
-    let event = Cu.cloneInto({
-      detail: {
-        type,
-        data,
-      },
-    }, content);
-    content.dispatchEvent(new content.CustomEvent("ContentSearchService",
-                                                  event));
-  },
-};
-ContentSearchMediator.init(this);
-
-addMessageListener("PageStyle:Switch", PageStyleHandler);
-addMessageListener("PageStyle:Disable", PageStyleHandler);
-addEventListener("pageshow", PageStyleHandler);
-
 // Keep a reference to the translation content handler to avoid it it being GC'ed.
 var trHandler = null;
 if (Services.prefs.getBoolPref("browser.translation.detectLanguage")) {
   ChromeUtils.import("resource:///modules/translation/TranslationContentHandler.jsm");
   trHandler = new TranslationContentHandler(global, docShell);
 }
 
-function gKeywordURIFixup(fixupInfo) {
-  fixupInfo.QueryInterface(Ci.nsIURIFixupInfo);
-  if (!fixupInfo.consumer) {
-    return;
-  }
-
-  // Ignore info from other docshells
-  let parent = fixupInfo.consumer.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeRootTreeItem;
-  if (parent != docShell)
-    return;
-
-  let data = {};
-  for (let f of Object.keys(fixupInfo)) {
-    if (f == "consumer" || typeof fixupInfo[f] == "function")
-      continue;
-
-    if (fixupInfo[f] && fixupInfo[f] instanceof Ci.nsIURI) {
-      data[f] = fixupInfo[f].spec;
-    } else {
-      data[f] = fixupInfo[f];
-    }
-  }
-
-  sendAsyncMessage("Browser:URIFixup", data);
-}
-Services.obs.addObserver(gKeywordURIFixup, "keyword-uri-fixup");
-addEventListener("unload", () => {
-  Services.obs.removeObserver(gKeywordURIFixup, "keyword-uri-fixup");
-}, false);
-
-addMessageListener("Browser:AppTab", function(message) {
-  if (docShell) {
-    docShell.isAppTab = message.data.isAppTab;
-  }
-});
-
 var WebBrowserChrome = {
   onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
     return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
   },
 
   // Check whether this URI should load in the current process
   shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData, aTriggeringPrincipal) {
     if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer, aHasPostData)) {
@@ -386,145 +72,17 @@ var WebBrowserChrome = {
 };
 
 if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
   let tabchild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsITabChild);
   tabchild.webBrowserChrome = WebBrowserChrome;
 }
 
-
-var DOMFullscreenHandler = {
-
-  init() {
-    addMessageListener("DOMFullscreen:Entered", this);
-    addMessageListener("DOMFullscreen:CleanUp", this);
-    addEventListener("MozDOMFullscreen:Request", this);
-    addEventListener("MozDOMFullscreen:Entered", this);
-    addEventListener("MozDOMFullscreen:NewOrigin", this);
-    addEventListener("MozDOMFullscreen:Exit", this);
-    addEventListener("MozDOMFullscreen:Exited", this);
-    this.init = null;
-  },
-
-  receiveMessage(aMessage) {
-    let windowUtils = content && content.windowUtils;
-    switch (aMessage.name) {
-      case "DOMFullscreen:Entered": {
-        this._lastTransactionId = windowUtils.lastTransactionId;
-        if (!windowUtils.handleFullscreenRequests() &&
-            !content.document.fullscreenElement) {
-          // If we don't actually have any pending fullscreen request
-          // to handle, neither we have been in fullscreen, tell the
-          // parent to just exit.
-          sendAsyncMessage("DOMFullscreen:Exit");
-        }
-        break;
-      }
-      case "DOMFullscreen:CleanUp": {
-        // If we've exited fullscreen at this point, no need to record
-        // transaction id or call exit fullscreen. This is especially
-        // important for non-e10s, since in that case, it is possible
-        // that no more paint would be triggered after this point.
-        if (content.document.fullscreenElement && windowUtils) {
-          this._lastTransactionId = windowUtils.lastTransactionId;
-          windowUtils.exitFullscreen();
-        }
-        break;
-      }
-    }
-  },
-
-  handleEvent(aEvent) {
-    switch (aEvent.type) {
-      case "MozDOMFullscreen:Request": {
-        sendAsyncMessage("DOMFullscreen:Request");
-        break;
-      }
-      case "MozDOMFullscreen:NewOrigin": {
-        sendAsyncMessage("DOMFullscreen:NewOrigin", {
-          originNoSuffix: aEvent.target.nodePrincipal.originNoSuffix,
-        });
-        break;
-      }
-      case "MozDOMFullscreen:Exit": {
-        sendAsyncMessage("DOMFullscreen:Exit");
-        break;
-      }
-      case "MozDOMFullscreen:Entered":
-      case "MozDOMFullscreen:Exited": {
-        addEventListener("MozAfterPaint", this);
-        if (!content || !content.document.fullscreenElement) {
-          // If we receive any fullscreen change event, and find we are
-          // actually not in fullscreen, also ask the parent to exit to
-          // ensure that the parent always exits fullscreen when we do.
-          sendAsyncMessage("DOMFullscreen:Exit");
-        }
-        break;
-      }
-      case "MozAfterPaint": {
-        // Only send Painted signal after we actually finish painting
-        // the transition for the fullscreen change.
-        // Note that this._lastTransactionId is not set when in non-e10s
-        // mode, so we need to check that explicitly.
-        if (!this._lastTransactionId ||
-            aEvent.transactionId > this._lastTransactionId) {
-          removeEventListener("MozAfterPaint", this);
-          sendAsyncMessage("DOMFullscreen:Painted");
-        }
-        break;
-      }
-    }
-  }
-};
-DOMFullscreenHandler.init();
-
-var UserContextIdNotifier = {
-  init() {
-    addEventListener("DOMWindowCreated", this);
-    this.init = null;
-  },
-
-  uninit() {
-    removeEventListener("DOMWindowCreated", this);
-  },
-
-  handleEvent(aEvent) {
-    // When the window is created, we want to inform the tabbrowser about
-    // the userContextId in use in order to update the UI correctly.
-    // Just because we cannot change the userContextId from an active docShell,
-    // we don't need to check DOMContentLoaded again.
-    this.uninit();
-
-    // We use the docShell because content.document can have been loaded before
-    // setting the originAttributes.
-    let loadContext = docShell.QueryInterface(Ci.nsILoadContext);
-    let userContextId = loadContext.originAttributes.userContextId;
-
-    sendAsyncMessage("Browser:WindowCreated", { userContextId });
-  }
-};
-
-UserContextIdNotifier.init();
-
 Services.obs.notifyObservers(this, "tab-content-frameloader-created");
 
-addMessageListener("AllowScriptsToClose", () => {
-  content.windowUtils.allowScriptsToClose();
-});
-
-addEventListener("MozAfterPaint", function onFirstPaint() {
-  removeEventListener("MozAfterPaint", onFirstPaint);
-  sendAsyncMessage("Browser:FirstPaint");
-});
-
 // Remove this once bug 1397365 is fixed.
 addEventListener("MozAfterPaint", function onFirstNonBlankPaint() {
   if (content.document.documentURI == "about:blank" && !content.opener)
     return;
   removeEventListener("MozAfterPaint", onFirstNonBlankPaint);
   sendAsyncMessage("Browser:FirstNonBlankPaint");
 });
-
-addMessageListener("DOM:WebManifest:hasManifestLink", ManifestMessages);
-addMessageListener("DOM:ManifestObtainer:Obtain", ManifestMessages);
-addMessageListener("DOM:Manifest:FireAppInstalledEvent", ManifestMessages);
-addMessageListener("DOM:WebManifest:fetchIcon", ManifestMessages);
--- a/browser/base/content/test/forms/head.js
+++ b/browser/base/content/test/forms/head.js
@@ -1,12 +1,13 @@
 function hideSelectPopup(selectPopup, mode = "enter", win = window) {
   let browser = win.gBrowser.selectedBrowser;
   let selectClosedPromise = ContentTask.spawn(browser, null, async function() {
-    ChromeUtils.import("resource://gre/modules/SelectContentHelper.jsm");
+    let {SelectContentHelper} =
+      ChromeUtils.import("resource://gre/actors/SelectChild.jsm", {});
     return ContentTaskUtils.waitForCondition(() => !SelectContentHelper.open);
   });
 
   if (mode == "escape") {
     EventUtils.synthesizeKey("KEY_Escape", {}, win);
   } else if (mode == "enter") {
     EventUtils.synthesizeKey("KEY_Enter", {}, win);
   } else if (mode == "click") {
--- a/browser/base/content/test/general/browser_contentSearchUI.js
+++ b/browser/base/content/test/general/browser_contentSearchUI.js
@@ -707,25 +707,20 @@ var gMsgMan;
 async function promiseTab() {
   let deferred = PromiseUtils.defer();
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
   registerCleanupFunction(() => BrowserTestUtils.removeTab(tab));
   let pageURL = getRootDirectory(gTestPath) + TEST_PAGE_BASENAME;
   tab.linkedBrowser.addEventListener("load", function onLoad(event) {
     tab.linkedBrowser.removeEventListener("load", onLoad, true);
     gMsgMan = tab.linkedBrowser.messageManager;
-    gMsgMan.sendAsyncMessage("ContentSearch", {
-      type: "AddToWhitelist",
-      data: [pageURL],
-    });
-    promiseMsg("ContentSearch", "AddToWhitelistAck", gMsgMan).then(() => {
-      let jsURL = getRootDirectory(gTestPath) + TEST_CONTENT_SCRIPT_BASENAME;
-      gMsgMan.loadFrameScript(jsURL, false);
-      deferred.resolve(msg("init"));
-    });
+
+    let jsURL = getRootDirectory(gTestPath) + TEST_CONTENT_SCRIPT_BASENAME;
+    gMsgMan.loadFrameScript(jsURL, false);
+    deferred.resolve(msg("init"));
   }, true, true);
   openTrustedLinkIn(pageURL, "current");
   return deferred.promise;
 }
 
 function promiseMsg(name, type, msgMan) {
   return new Promise(resolve => {
     info("Waiting for " + name + " message " + type + "...");
--- a/browser/base/content/test/performance/browser_startup.js
+++ b/browser/base/content/test/performance/browser_startup.js
@@ -30,16 +30,18 @@ const startupPhases = {
       "nsBrowserGlue.js",
       "MainProcessSingleton.js",
 
       // Bugs to fix: The following components shouldn't be initialized that early.
       "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
@@ -43,25 +43,27 @@ const whitelist = {
     "resource:///modules/sessionstore/ContentSessionStore.jsm",
     "resource://gre/modules/sessionstore/SessionHistory.jsm",
 
     // Forms and passwords
     "resource://formautofill/FormAutofill.jsm",
     "resource://formautofill/FormAutofillContent.jsm",
 
     // Browser front-end
+    "resource:///actors/AboutReaderChild.jsm",
+    "resource:///actors/BrowserTabChild.jsm",
     "resource:///modules/ContentLinkHandler.jsm",
     "resource:///modules/ContentMetaHandler.jsm",
-    "resource:///modules/PageStyleHandler.jsm",
-    "resource://gre/modules/BrowserUtils.jsm",
+    "resource:///actors/PageStyleChild.jsm",
+    "resource://gre/modules/ActorChild.jsm",
+    "resource://gre/modules/ActorManagerChild.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
     "chrome://pocket/content/AboutPocket.jsm",
 
     // Telemetry
     "resource://gre/modules/TelemetryController.jsm", // bug 1470339
     "resource://gre/modules/TelemetrySession.jsm", // bug 1470339
     "resource://gre/modules/TelemetryUtils.jsm", // bug 1470339
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -3,16 +3,19 @@
 
 // Note to run this test similar to try server, you need to run:
 // ./mach package
 // ./mach mochitest --appname dist <path to test>
 
 // Slow on asan builds.
 requestLongerTimeout(5);
 
+ChromeUtils.defineModuleGetter(this, "ActorManagerParent",
+                               "resource://gre/modules/ActorManagerParent.jsm");
+
 var isDevtools = SimpleTest.harnessParameters.subsuite == "devtools";
 
 var gExceptionPaths = [
   "chrome://browser/content/defaultthemes/",
   "resource://app/defaults/settings/blocklists/",
   "resource://app/defaults/settings/main/",
   "resource://app/defaults/settings/pinning/",
   "resource://app/defaults/preferences/",
@@ -155,16 +158,18 @@ var whitelist = [
   {file: "resource://gre/modules/PerformanceWatcher.jsm"},
   // Bug 1378173 (warning: still used by devtools)
   {file: "resource://gre/modules/Promise.jsm"},
   // Still used by WebIDE, which is going away but not entirely gone.
   {file: "resource://gre/modules/ZipUtils.jsm"},
   // Bug 1463225 (on Mac this is only used by a test)
   {file: "chrome://global/content/bindings/toolbar.xml",
    platforms: ["macosx"]},
+  // Bug 1483277 (temporarily unreferenced)
+  {file: "chrome://browser/content/browser.xhtml"},
 ];
 
 whitelist = new Set(whitelist.filter(item =>
   ("isFromDevTools" in item) == isDevtools &&
   (!item.skipUnofficial || !AppConstants.MOZILLA_OFFICIAL) &&
   (!item.platforms || item.platforms.includes(AppConstants.platform))
 ).map(item => item.file));
 
@@ -410,17 +415,21 @@ function parseCodeFile(fileUri) {
 
         let pos = url.indexOf("?");
         if (pos != -1) {
           url = url.slice(0, pos);
         }
 
         // Make urls like chrome://browser/skin/ point to an actual file,
         // and remove the ref if any.
-        url = Services.io.newURI(url).specIgnoringRef;
+        try {
+          url = Services.io.newURI(url).specIgnoringRef;
+        } catch (e) {
+          continue;
+        }
 
         if (isDevtools && line.includes("require(") &&
             !/\.(properties|js|jsm|json|css)$/.test(url))
           url += ".js";
 
         addCodeReference(url, fileUri);
       }
     }
@@ -497,16 +506,27 @@ function findChromeUrlsFromArray(array, 
 
     // Only keep strings that look like real chrome or resource urls.
     if (/chrome:\/\/[a-zA-Z09-]+\/(content|skin|locale)\//.test(string) ||
         /resource:\/\/[a-zA-Z09-]*\/.*\.[a-z]+/.test(string))
       gReferencesFromCode.set(string, null);
   }
 }
 
+function addActorModules() {
+  let groups = [...ActorManagerParent.parentGroups.values(),
+                ...ActorManagerParent.childGroups.values(),
+                ...ActorManagerParent.singletons.values()];
+  for (let group of groups) {
+    for (let {module} of group.actors.values()) {
+      gReferencesFromCode.set(module, null);
+    }
+  }
+}
+
 add_task(async function checkAllTheFiles() {
   let libxulPath = OS.Constants.Path.libxul;
   if (AppConstants.platform != "macosx")
     libxulPath = OS.Constants.Path.libDir + "/" + libxulPath;
   let libxul = await OS.File.read(libxulPath);
   findChromeUrlsFromArray(libxul, "chrome://");
   findChromeUrlsFromArray(libxul, "resource://");
   // Handle NS_LITERAL_STRING.
@@ -534,16 +554,18 @@ add_task(async function checkAllTheFiles
     }
 
     return true;
   });
 
   // Wait for all manifest to be parsed
   await throttledMapPromises(manifestURIs, parseManifest);
 
+  addActorModules();
+
   // We build a list of promises that get resolved when their respective
   // files have loaded and produced no errors.
   let allPromises = [];
 
   for (let uri of uris) {
     let path = uri.pathQueryRef;
     if (path.endsWith(".css"))
       allPromises.push([parseCSSFile, uri]);
--- a/browser/base/content/webext-panels.js
+++ b/browser/base/content/webext-panels.js
@@ -59,22 +59,22 @@ function getBrowser(sidebar) {
   } else {
     readyPromise = Promise.resolve();
   }
 
   stack.appendChild(browser);
   document.documentElement.appendChild(stack);
 
   return readyPromise.then(() => {
-    browser.messageManager.loadFrameScript("chrome://browser/content/content.js", false);
+    browser.messageManager.loadFrameScript("chrome://browser/content/content.js", false, true);
     ExtensionParent.apiManager.emit("extension-browser-inserted", browser);
 
     if (sidebar.browserStyle) {
       browser.messageManager.loadFrameScript(
-        "chrome://extensions/content/ext-browser-content.js", false);
+        "chrome://extensions/content/ext-browser-content.js", false, true);
 
       browser.messageManager.sendAsyncMessage("Extension:InitBrowser", {
         stylesheets: ExtensionParent.extensionStylesheets,
       });
     }
     return browser;
   });
 }
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -24,16 +24,19 @@ browser.jar:
         content/browser/aboutNetError-new.xhtml        (content/aboutNetError-new.xhtml)
         content/browser/aboutRobots-icon.png          (content/aboutRobots-icon.png)
         content/browser/aboutRobots-widget-left.png   (content/aboutRobots-widget-left.png)
         content/browser/aboutTabCrashed.css           (content/aboutTabCrashed.css)
         content/browser/aboutTabCrashed.js            (content/aboutTabCrashed.js)
         content/browser/aboutTabCrashed.xhtml         (content/aboutTabCrashed.xhtml)
 *       content/browser/browser.css                   (content/browser.css)
         content/browser/browser.js                    (content/browser.js)
+#ifdef MOZ_BROWSER_XHTML
+*       content/browser/browser.xhtml                 (content/browser.xhtml)
+#endif
 *       content/browser/browser.xul                   (content/browser.xul)
         content/browser/browser-addons.js             (content/browser-addons.js)
         content/browser/browser-allTabsMenu.js        (content/browser-allTabsMenu.js)
         content/browser/browser-captivePortal.js      (content/browser-captivePortal.js)
         content/browser/browser-ctrlTab.js            (content/browser-ctrlTab.js)
         content/browser/browser-customization.js      (content/browser-customization.js)
         content/browser/browser-data-submission-info-bar.js (content/browser-data-submission-info-bar.js)
         content/browser/browser-compacttheme.js       (content/browser-compacttheme.js)
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -57,16 +57,17 @@ BROWSER_CHROME_MANIFESTS += [
     'content/test/trackingUI/browser.ini',
     'content/test/urlbar/browser.ini',
     'content/test/webextensions/browser.ini',
     'content/test/webrtc/browser.ini',
 ]
 
 DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
+DEFINES['MOZ_BROWSER_XHTML'] = CONFIG['MOZ_BROWSER_XHTML']
 
 DEFINES['APP_LICENSE_BLOCK'] = '%s/content/overrides/app-license.html' % SRCDIR
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk3', 'cocoa'):
     DEFINES['CONTEXT_COPY_IMAGE_CONTENTS'] = 1
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk3'):
     DEFINES['MENUBAR_CAN_AUTOHIDE'] = 1
--- 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/enterprisepolicies/content/aboutPolicies.css
+++ b/browser/components/enterprisepolicies/content/aboutPolicies.css
@@ -1,3 +1,141 @@
 /* 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/. */
+
+@import url("chrome://global/skin/in-content/common.css");
+
+html {
+  height: 100%;
+}
+
+body {
+  display: flex;
+  align-items: stretch;
+  height: 100%;
+}
+
+#sectionTitle {
+  float: left;
+}
+
+#sectionTitle:dir(rtl) {
+  float: right;
+}
+
+/** Categories **/
+
+.category {
+  cursor: pointer;
+  /* Center category names */
+  display: flex;
+  align-items: center;
+}
+
+.category .category-name {
+  pointer-events: none;
+}
+
+#categories hr {
+  border-top-color: rgba(255,255,255,0.15);
+}
+
+/** Content area **/
+
+.main-content {
+  flex: 1;
+}
+
+.tab {
+  padding: 0.5em 0;
+}
+
+.tab table {
+  width: 100%;
+}
+
+th, td, table {
+  border-collapse: collapse;
+  border: none;
+  text-align: start;
+}
+
+th {
+  padding-bottom: 8px;
+  font-size: larger;
+}
+
+td {
+  padding-bottom: 8px;
+}
+
+.active-policies tr:nth-child(even) {
+  background-color: white;
+}
+
+.errors tr:nth-child(even) {
+  background-color: white;
+}
+
+/*
+ * In Documentation Tab, this property sets the policies row in an
+ * alternate color scheme of white and grey as each policy comprises
+ * of two tbody tags, one for the description and the other for the
+ * collapsible information block.
+ */
+
+tbody:nth-child(4n + 1) {
+  background-color: white;
+}
+
+.lastpolicyrow {
+  border-bottom: 3px solid #0c0c0d;
+}
+
+tr.lastpolicyrow td {
+  padding-bottom: 16px;
+}
+
+.array {
+  border-bottom: 1px solid var(--in-content-box-border-color);
+  padding-bottom: 4px;
+}
+
+.icon {
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: 16px;
+  -moz-context-properties: fill;
+  display: inline-block;
+  fill: var(--newtab-icon-primary-color);
+  height: 14px;
+  vertical-align: middle;
+  width: 14px;
+}
+
+.icon.machine-only {
+  background-image: url("chrome://browser/skin/developer.svg");
+}
+
+.collapsible {
+  cursor: pointer;
+  border: none;
+  outline: none;
+}
+
+.content {
+  display: none;
+}
+
+.content-style {
+  background-color: white;
+  color: var(--in-content-category-text-selected);
+}
+
+tbody.collapsible td {
+  padding-bottom: 8px;
+}
+
+.schema {
+  font-family: monospace;
+  white-space: pre;
+}
\ No newline at end of file
--- a/browser/components/enterprisepolicies/content/aboutPolicies.js
+++ b/browser/components/enterprisepolicies/content/aboutPolicies.js
@@ -1,4 +1,276 @@
 /* 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/Services.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetters(this, {
+  schema: "resource:///modules/policies/schema.jsm",
+});
+
+function col(text, className) {
+  let column = document.createElement("td");
+  if (className) {
+    column.classList.add(className);
+  }
+  let content = document.createTextNode(text);
+  column.appendChild(content);
+  return column;
+}
+
+function machine_only_col(text) {
+  let icon = document.createElement("span");
+  icon.classList.add("icon");
+  icon.classList.add("machine-only");
+  icon.title = "Machine-only";
+  let column = document.createElement("td");
+  let content = document.createTextNode(text);
+  column.appendChild(content);
+  column.appendChild(icon);
+  return column;
+}
+
+/*
+ * This function generates the Active Policies content to be displayed by calling
+ * a recursive function called generatePolicy() according to the policy schema.
+ */
+
+function generateActivePolicies(data) {
+
+  let new_cont = document.getElementById("activeContent");
+  new_cont.classList.add("active-policies");
+
+  for (let policyName in data) {
+    if (schema.properties[policyName].type == "array") {
+      for (let count in data[policyName]) {
+        let isFirstRow = (count == 0);
+        let isLastRow = (count == data[policyName].length - 1);
+        let row = document.createElement("tr");
+        row.appendChild(col(isFirstRow ? policyName : ""));
+        generatePolicy(data[policyName][count], row, 1, new_cont, isLastRow, !isFirstRow);
+      }
+    } else if (schema.properties[policyName].type == "object") {
+      let count = 0;
+      for (let obj in data[policyName]) {
+        let isFirstRow = (count == 0);
+        let isLastRow = (count == data[policyName].length - 1);
+        let row = document.createElement("tr");
+        row.appendChild(col(isFirstRow ? policyName : ""));
+        row.appendChild(col(obj));
+        generatePolicy(data[policyName][obj], row, 2, new_cont, isLastRow);
+        count++;
+      }
+    } else {
+      let row = document.createElement("tr");
+      row.appendChild(col(policyName));
+      row.appendChild(col(JSON.stringify(data[policyName])));
+      row.classList.add("lastpolicyrow");
+      new_cont.appendChild(row);
+    }
+  }
+}
+
+/*
+ * This is a helper recursive function that iterates levels of each
+ * policy and formats the content to be displayed accordingly.
+ */
+
+function generatePolicy(data, row, depth, new_cont, islast, arr_sep = false) {
+  if (Array.isArray(data)) {
+    for (let count in data) {
+      if (count == 0) {
+        if (count == data.length - 1) {
+          generatePolicy(data[count], row, depth + 1, new_cont, islast ? islast : false, false);
+        } else {
+          generatePolicy(data[count], row, depth + 1, new_cont, false, true);
+        }
+      } else if (count == data.length - 1) {
+        let last_row = document.createElement("tr");
+        for (let i = 0; i < depth; i++) {
+            last_row.appendChild(col(""));
+        }
+
+        generatePolicy(data[count], last_row, depth + 1, new_cont, islast ? islast : false, arr_sep);
+      } else {
+        let new_row = document.createElement("tr");
+        for (let i = 0; i < depth; i++) {
+          new_row.appendChild(col(""));
+        }
+
+        generatePolicy(data[count], new_row, depth + 1, new_cont, false, true);
+      }
+    }
+  } else if (typeof data == "object" && Object.keys(data).length > 0) {
+    let count = 0;
+      for (let obj in data) {
+        if (count == 0) {
+          row.appendChild(col(obj));
+          if (count == Object.keys(data).length - 1) {
+            generatePolicy(data[obj], row, depth + 1, new_cont, islast ? islast : false, arr_sep);
+          } else {
+            generatePolicy(data[obj], row, depth + 1, new_cont, false, false);
+          }
+        } else if (count == Object.keys(data).length - 1) {
+          let last_row = document.createElement("tr");
+          for (let i = 0; i < depth; i++) {
+            last_row.appendChild(col(""));
+          }
+
+          if (arr_sep) {
+            last_row.appendChild(col(obj, "array"));
+          } else {
+            last_row.appendChild(col(obj));
+          }
+
+          generatePolicy(data[obj], last_row, depth + 1, new_cont, islast ? islast : false, arr_sep);
+        } else {
+          let new_row = document.createElement("tr");
+          for (let i = 0; i < depth; i++) {
+            new_row.appendChild(col(""));
+          }
+
+          new_row.appendChild(col(obj));
+          generatePolicy(data[obj], new_row, depth + 1, new_cont, false, false);
+        }
+        count++;
+      }
+  } else {
+    if (arr_sep) {
+      row.appendChild(col(JSON.stringify(data), "array"));
+    } else {
+      row.appendChild(col(JSON.stringify(data)));
+    }
+    if (islast) {
+      row.classList.add("lastpolicyrow");
+    }
+    new_cont.appendChild(row);
+  }
+}
+
+function generateErrors() {
+  const consoleStorage = Cc["@mozilla.org/consoleAPI-storage;1"];
+  const storage = consoleStorage.getService(Ci.nsIConsoleAPIStorage);
+  const consoleEvents = storage.getEvents();
+  const prefixes = ["Enterprise Policies",
+                    "JsonSchemaValidator.jsm",
+                    "Policies.jsm",
+                    "GPOParser.jsm",
+                    "Enterprise Policies Child",
+                    "BookmarksPolicies.jsm",
+                    "ProxyPolicies.jsm",
+                    "WebsiteFilter Policy"];
+
+  let new_cont = document.getElementById("errorsContent");
+  new_cont.classList.add("errors");
+
+  let flag = false;
+  for (let err of consoleEvents) {
+    if (prefixes.includes(err.prefix)) {
+      flag = true;
+      let row = document.createElement("tr");
+      row.appendChild(col(err.arguments[0], "schema"));
+      new_cont.appendChild(row);
+    }
+  }
+
+  if (!flag) {
+    let errors_tab = document.getElementById("category-errors");
+    errors_tab.style.display = "none";
+  }
+}
+
+function generateDocumentation() {
+  let new_cont = document.getElementById("documentationContent");
+  new_cont.setAttribute("id", "documentationContent");
+
+  for (let policyName in schema.properties) {
+    let main_tbody = document.createElement("tbody");
+    main_tbody.classList.add("collapsible");
+    main_tbody.addEventListener("click", function() {
+      let content = this.nextElementSibling;
+      content.classList.toggle("content");
+    });
+    let row = document.createElement("tr");
+    if (schema.properties[policyName].machine_only) {
+      row.appendChild(machine_only_col(policyName));
+    } else {
+      row.appendChild(col(policyName));
+    }
+    row.appendChild(col(schema.properties[policyName].description));
+    main_tbody.appendChild(row);
+    let sec_tbody = document.createElement("tbody");
+    sec_tbody.classList.add("content");
+    sec_tbody.classList.add("content-style");
+    let schema_row = document.createElement("tr");
+    if (schema.properties[policyName].properties) {
+      let column = col(JSON.stringify(schema.properties[policyName].properties, null, 1), "schema");
+      column.colSpan = "2";
+      schema_row.appendChild(column);
+      sec_tbody.appendChild(schema_row);
+    } else {
+      let column = col("type: " + schema.properties[policyName].type, "schema");
+      column.colSpan = "2";
+      schema_row.appendChild(column);
+      sec_tbody.appendChild(schema_row);
+      if (schema.properties[policyName].enum) {
+        let enum_row = document.createElement("tr");
+        column = col("enum: " + JSON.stringify(schema.properties[policyName].enum, null, 1), "schema");
+        column.colSpan = "2";
+        enum_row.appendChild(column);
+        sec_tbody.appendChild(enum_row);
+      }
+    }
+    new_cont.appendChild(main_tbody);
+    new_cont.appendChild(sec_tbody);
+  }
+}
+
+let gInited = false;
+function init() {
+  if (gInited) {
+    return;
+  }
+  gInited = true;
+
+  let data = Services.policies.getActivePolicies();
+  generateActivePolicies(data);
+  generateErrors();
+  generateDocumentation();
+
+  // Event delegation on #categories element
+  let menu = document.getElementById("categories");
+  menu.addEventListener("click", function click(e) {
+    if (e.target && e.target.parentNode == menu)
+      show(e.target);
+  });
+
+  if (location.hash) {
+    let sectionButton = document.getElementById("category-" + location.hash.substring(1));
+    if (sectionButton) {
+      sectionButton.click();
+    }
+  }
+}
+
+function show(button) {
+  let current_tab = document.querySelector(".active");
+  let category = button.getAttribute("id").substring("category-".length);
+  let content = document.getElementById(category);
+  if (current_tab == content)
+    return;
+  current_tab.classList.remove("active");
+  current_tab.hidden = true;
+  content.classList.add("active");
+  content.hidden = false;
+
+  let current_button = document.querySelector("[selected=true]");
+  current_button.removeAttribute("selected");
+  button.setAttribute("selected", "true");
+
+  let title = document.getElementById("sectionTitle");
+  title.textContent = button.children[0].textContent;
+  location.hash = category;
+}
--- a/browser/components/enterprisepolicies/content/aboutPolicies.xhtml
+++ b/browser/components/enterprisepolicies/content/aboutPolicies.xhtml
@@ -2,16 +2,68 @@
 <!--
 # 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/.
 -->
 <!DOCTYPE html>
 
 <html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-  	<link rel="stylesheet" href="chrome://browser/content/policies/aboutPolicies.css" type="text/css" />
-    <script type="application/javascript" src="chrome://browser/content/policies/aboutPolicies.js" />
-  </head>
+    <head>
+        <title data-l10n-id="about-policies-title"/>
+        <link rel="stylesheet" href="chrome://browser/content/policies/aboutPolicies.css" type="text/css" />
+        <link rel="localization" href="browser/aboutPolicies.ftl"/>
+        <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
+        <script type="application/javascript" src="chrome://browser/content/policies/aboutPolicies.js" />
+    </head>
+    <body id="body" onload="init()">
+        <div id="categories">
+            <div class="category" selected="true" id="category-active">
+                <span class="category-name" data-l10n-id="active-policies-tab"/>
+            </div>
+            <div class="category" id="category-documentation">
+                <span class="category-name" data-l10n-id="documentation-tab"/>
+            </div>
+            <div class="category" id="category-errors">
+                <span class="category-name" data-l10n-id="errors-tab"/>
+            </div>
+        </div>
+        <div class="main-content">
+            <div class="header">
+                <div id="sectionTitle" class="header-name" data-l10n-id="active-policies-tab"/>
+            </div>
 
-  <body>
-  </body>
+            <div id="active" class="tab active">
+                <table>
+                    <thead>
+                        <tr>
+                            <th data-l10n-id="policy-name"/>
+                            <th data-l10n-id="policy-value"/>
+                        </tr>
+                    </thead>
+                    <tbody id="activeContent" />
+                </table>
+            </div>
+
+            <div id="documentation" class="tab" hidden="true">
+                <table>
+                    <thead>
+                        <tr>
+                            <th data-l10n-id="policy-name"/>
+                        </tr>
+                    </thead>
+                    <tbody id="documentationContent" />
+                </table>
+            </div>
+
+             <div id="errors" class="tab" hidden="true">
+                <table>
+                    <thead>
+                        <tr>
+                            <th data-l10n-id="policy-errors"/>
+                        </tr>
+                    </thead>
+                    <tbody id="errorsContent" />
+                </table>
+            </div>
+        </div>
+    </body>
 </html>
--- a/browser/components/extensions/ExtensionPopups.jsm
+++ b/browser/components/extensions/ExtensionPopups.jsm
@@ -304,20 +304,20 @@ class BasePopup {
       return setupBrowser(browser);
     }
 
     return readyPromise.then(() => {
       setupBrowser(browser);
       let mm = browser.messageManager;
 
       // Sets the context information for context menus.
-      mm.loadFrameScript("chrome://browser/content/content.js", true);
+      mm.loadFrameScript("chrome://browser/content/content.js", true, true);
 
       mm.loadFrameScript(
-        "chrome://extensions/content/ext-browser-content.js", false);
+        "chrome://extensions/content/ext-browser-content.js", false, true);
 
       mm.sendAsyncMessage("Extension:InitBrowser", {
         allowScriptsToClose: true,
         blockParser: this.blockParser,
         fixedWidth: this.fixedWidth,
         maxWidth: 800,
         maxHeight: 600,
         stylesheets: this.STYLESHEETS,
--- a/browser/components/extensions/child/ext-menus-child.js
+++ b/browser/components/extensions/child/ext-menus-child.js
@@ -1,23 +1,22 @@
 "use strict";
 
+ChromeUtils.defineModuleGetter(this, "ContextMenuChild",
+                               "resource:///actors/ContextMenuChild.jsm");
+
 this.menusChild = class extends ExtensionAPI {
   getAPI(context) {
     return {
       menus: {
         getTargetElement(targetElementId) {
-          let tabChildGlobal = context.messageManager;
-          let {contextMenu} = tabChildGlobal;
           let element;
-          if (contextMenu) {
-            let {lastMenuTarget} = contextMenu;
-            if (lastMenuTarget && Math.floor(lastMenuTarget.timeStamp) === targetElementId) {
-              element = lastMenuTarget.targetRef.get();
-            }
+          let lastMenuTarget = ContextMenuChild.getLastTarget(context.messageManager);
+          if (lastMenuTarget && Math.floor(lastMenuTarget.timeStamp) === targetElementId) {
+            element = lastMenuTarget.targetRef.get();
           }
           if (element && element.getRootNode({composed: true}) === context.contentWindow.document) {
             return element;
           }
           return null;
         },
       },
     };
--- a/browser/components/extensions/test/browser/browser_ext_find.js
+++ b/browser/components/extensions/test/browser/browser_ext_find.js
@@ -1,62 +1,59 @@
 /* global browser */
 "use strict";
 
 function frameScript() {
-  function getSelectedText() {
-    let frame = this.content.frames[0].frames[1];
-    let docShell = frame.docShell;
-    let controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                             .getInterface(Ci.nsISelectionDisplay)
-                             .QueryInterface(Ci.nsISelectionController);
-    let selection = controller.getSelection(controller.SELECTION_FIND);
-    let range = selection.getRangeAt(0);
-    let scope = {};
-    ChromeUtils.import("resource://gre/modules/FindContent.jsm", scope);
-    let highlighter = (new scope.FindContent(docShell)).highlighter;
-    let r1 = frame.parent.frameElement.getBoundingClientRect();
-    let f1 = highlighter._getFrameElementOffsets(frame.parent);
-    let r2 = frame.frameElement.getBoundingClientRect();
-    let f2 = highlighter._getFrameElementOffsets(frame);
-    let r3 = range.getBoundingClientRect();
-    let rect = {
-      top: (r1.top + r2.top + r3.top + f1.y + f2.y),
-      left: (r1.left + r2.left + r3.left + f1.x + f2.x),
-    };
-    this.sendAsyncMessage("test:find:selectionTest", {text: selection.toString(), rect});
-  }
-  getSelectedText();
+  let frame = this.content.frames[0].frames[1];
+  let docShell = frame.docShell;
+  let controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsISelectionDisplay)
+                           .QueryInterface(Ci.nsISelectionController);
+  let selection = controller.getSelection(controller.SELECTION_FIND);
+  let range = selection.getRangeAt(0);
+  let scope = {};
+  ChromeUtils.import("resource://gre/modules/FindContent.jsm", scope);
+  let highlighter = (new scope.FindContent(docShell)).highlighter;
+  let r1 = frame.parent.frameElement.getBoundingClientRect();
+  let f1 = highlighter._getFrameElementOffsets(frame.parent);
+  let r2 = frame.frameElement.getBoundingClientRect();
+  let f2 = highlighter._getFrameElementOffsets(frame);
+  let r3 = range.getBoundingClientRect();
+  let rect = {
+    top: (r1.top + r2.top + r3.top + f1.y + f2.y),
+    left: (r1.left + r2.left + r3.left + f1.x + f2.x),
+  };
+  this.sendAsyncMessage("test:find:selectionTest", {text: selection.toString(), rect});
 }
 
 function waitForMessage(messageManager, topic) {
   return new Promise(resolve => {
     messageManager.addMessageListener(topic, function messageListener(message) {
       messageManager.removeMessageListener(topic, messageListener);
       resolve(message);
     });
   });
 }
 
 add_task(async function testDuplicatePinnedTab() {
   async function background() {
-    function awaitLoad(tabId) {
+    function awaitLoad(tabId, url) {
       return new Promise(resolve => {
         browser.tabs.onUpdated.addListener(function listener(tabId_, changed, tab) {
-          if (tabId == tabId_ && changed.status == "complete") {
+          if (tabId == tabId_ && changed.status == "complete" && tab.url == url) {
             browser.tabs.onUpdated.removeListener(listener);
             resolve();
           }
         });
       });
     }
 
     let url = "http://example.com/browser/browser/components/extensions/test/browser/file_find_frames.html";
     let tab = await browser.tabs.update({url});
-    await awaitLoad(tab.id);
+    await awaitLoad(tab.id, url);
 
     let data = await browser.find.find("banana", {includeRangeData: true});
     let rangeData = data.rangeData;
 
     browser.test.log("Test that `data.count` is the expected value.");
     browser.test.assertEq(6, data.count, "The value returned from `data.count`");
 
     browser.test.log("Test that `rangeData` has the proper number of values.");
@@ -139,18 +136,18 @@ add_task(async function testDuplicatePin
 
   await extension.startup();
   let rectData = await extension.awaitMessage("test:find:WebExtensionFinished");
   let {top, left} = rectData[5].rectsAndTexts.rectList[0];
   await extension.unload();
 
   let {selectedBrowser} = gBrowser;
 
-  let frameScriptUrl = `data:,(${frameScript})()`;
-  selectedBrowser.messageManager.loadFrameScript(frameScriptUrl, false);
+  let frameScriptUrl = `data:,(${frameScript}).call(this)`;
+  selectedBrowser.messageManager.loadFrameScript(frameScriptUrl, false, true);
   let message = await waitForMessage(selectedBrowser.messageManager, "test:find:selectionTest");
 
   info("Test that text was highlighted properly.");
   is(message.data.text, "bananA", `The text that was highlighted: - Expected: bananA, Actual: ${message.data.text}`);
 
   info("Test that rectangle data returned from the search matches the highlighted result.");
   is(message.data.rect.top, top, `rect.top: - Expected: ${message.data.rect.top}, Actual: ${top}`);
   is(message.data.rect.left, left, `rect.left: - Expected: ${message.data.rect.left}, Actual: ${left}`);
--- a/browser/components/extensions/test/browser/browser_ext_menus_events.js
+++ b/browser/components/extensions/test/browser/browser_ext_menus_events.js
@@ -402,16 +402,18 @@ add_task(async function test_show_hide_f
       editable: false,
       get frameId() { return frameId; },
       pageUrl: PAGE,
       frameUrl: PAGE_BASE + "context_frame.html",
       targetElementId: EXPECT_TARGET_ELEMENT,
     },
     async doOpenMenu() {
       frameId = await ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
+        ChromeUtils.import("resource://gre/modules/WebNavigationFrames.jsm");
+
         let {contentWindow} = content.document.getElementById("frame");
         return WebNavigationFrames.getFrameId(contentWindow);
       });
       await openContextMenuInFrame("#frame");
     },
     async doCloseMenu() {
       await closeExtensionContextMenu();
     },
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
@@ -114,17 +114,17 @@ add_task(async function test_on_created_
 
   const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
 
   info("Open a subframe link in a new tab using Ctrl-click");
 
   await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
-      BrowserTestUtils.synthesizeMouseAtCenter(function() {
+      BrowserTestUtils.synthesizeMouseAtCenter(() => {
         // This code runs as a framescript in the child process and it returns the
         // target link in the subframe.
         return this.content.frames[0].document
           .querySelector("#test-create-new-tab-from-mouse-click-subframe");
       }, {ctrlKey: true, metaKey: true}, tab.linkedBrowser);
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
@@ -133,17 +133,17 @@ add_task(async function test_on_created_
     },
   });
 
   info("Open a subframe link in a new window using Shift-click");
 
   await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
-      BrowserTestUtils.synthesizeMouseAtCenter(function() {
+      BrowserTestUtils.synthesizeMouseAtCenter(() => {
         // This code runs as a framescript in the child process and it returns the
         // target link in the subframe.
         return this.content.frames[0].document
                       .querySelector("#test-create-new-window-from-mouse-click-subframe");
       }, {shiftKey: true}, tab.linkedBrowser);
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
@@ -152,17 +152,17 @@ add_task(async function test_on_created_
     },
   });
 
   info("Open a subframe link with target=\"_blank\" in a new tab using click");
 
   await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
-      BrowserTestUtils.synthesizeMouseAtCenter(function() {
+      BrowserTestUtils.synthesizeMouseAtCenter(() => {
         // This code runs as a framescript in the child process and it returns the
         // target link in the subframe.
         return this.content.frames[0].document
           .querySelector("#test-create-new-tab-from-targetblank-click-subframe");
       }, {}, tab.linkedBrowser);
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js
@@ -109,17 +109,17 @@ add_task(async function test_on_created_
   const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
 
   info("Open a subframe link in a new tab from the context menu");
 
   await runCreatedNavigationTargetTest({
     extension,
     async openNavTarget() {
       await clickContextMenuItem({
-        pageElementSelector: function() {
+        pageElementSelector: () => {
           // This code runs as a framescript in the child process and it returns the
           // target link in the subframe.
           return this.content.frames[0]
             .document.querySelector("#test-create-new-tab-from-context-menu-subframe");
         },
         contextMenuItemLabel: "Open Link in New Tab",
       });
     },
@@ -131,17 +131,17 @@ add_task(async function test_on_created_
   });
 
   info("Open a subframe link in a new window from the context menu");
 
   await runCreatedNavigationTargetTest({
     extension,
     async openNavTarget() {
       await clickContextMenuItem({
-        pageElementSelector: function() {
+        pageElementSelector: () => {
           // This code runs as a framescript in the child process and it returns the
           // target link in the subframe.
           return this.content.frames[0]
             .document.querySelector("#test-create-new-window-from-context-menu-subframe");
         },
         contextMenuItemLabel: "Open Link in New Window",
       });
     },
--- 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,270 @@
  * 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 = {
+  AboutReader: {
+    child: {
+      module: "resource:///actors/AboutReaderChild.jsm",
+      group: "browsers",
+      events: {
+        "AboutReaderContentLoaded": {wantUntrusted: true},
+        "DOMContentLoaded": {},
+        "pageshow": {},
+        "pagehide": {},
+      },
+      messages: [
+        "Reader:ToggleReaderMode",
+        "Reader:PushState",
+      ],
+    },
+  },
+
+  BlockedSite: {
+    child: {
+      module: "resource:///actors/BlockedSiteChild.jsm",
+      events: {
+        "AboutBlockedLoaded": {wantUntrusted: true},
+        "click": {},
+      },
+      matches: ["about:blocked?*"],
+      allFrames: true,
+      messages: [
+        "DeceptiveBlockedDetails",
+      ],
+    },
+  },
+
+  BrowserTab: {
+    child: {
+      module: "resource:///actors/BrowserTabChild.jsm",
+      group: "browsers",
+
+      events: {
+        "DOMWindowCreated": {once: true},
+        "MozAfterPaint": {once: true},
+        "MozDOMPointerLock:Entered": {},
+        "MozDOMPointerLock:Exited": {},
+      },
+
+      messages: [
+        "AllowScriptsToClose",
+        "Browser:AppTab",
+        "Browser:HasSiblings",
+        "Browser:Reload",
+        "MixedContent:ReenableProtection",
+        "SwitchDocumentDirection",
+        "UpdateCharacterSet",
+      ],
+    },
+  },
+
+  ClickHandler: {
+    child: {
+      module: "resource:///actors/ClickHandlerChild.jsm",
+      group: "browsers",
+      events: {
+        "click": {capture: true, mozSystemGroup: true},
+      }
+    },
+  },
+
+  ContextMenu: {
+    child: {
+      module: "resource:///actors/ContextMenuChild.jsm",
+      events: {
+        "contextmenu": {mozSystemGroup: true},
+      },
+    },
+  },
+
+  ContentSearch: {
+    child: {
+      module: "resource:///actors/ContentSearchChild.jsm",
+      group: "browsers",
+      matches: ["about:home", "about:newtab", "about:welcome",
+                "chrome://mochitests/content/*"],
+      events: {
+        "ContentSearchClient": {capture: true, wantUntrusted: true},
+      },
+      messages: [
+        "ContentSearch",
+      ]
+    },
+  },
+
+  DOMFullscreen: {
+    child: {
+      module: "resource:///actors/DOMFullscreenChild.jsm",
+      group: "browsers",
+      events: {
+        "MozDOMFullscreen:Request": {},
+        "MozDOMFullscreen:Entered": {},
+        "MozDOMFullscreen:NewOrigin": {},
+        "MozDOMFullscreen:Exit": {},
+        "MozDOMFullscreen:Exited": {},
+      },
+      messages: [
+        "DOMFullscreen:Entered",
+        "DOMFullscreen:CleanUp",
+      ]
+    },
+  },
+
+  LightWeightThemeInstall: {
+    child: {
+      module: "resource:///actors/LightWeightThemeInstallChild.jsm",
+      events: {
+        "InstallBrowserTheme": {wantUntrusted: true},
+        "PreviewBrowserTheme": {wantUntrusted: true},
+        "ResetBrowserThemePreview": {wantUntrusted: true},
+      },
+    },
+  },
+
+  NetError: {
+    child: {
+      module: "resource:///actors/NetErrorChild.jsm",
+      events: {
+        "AboutNetErrorLoad": {wantUntrusted: true},
+        "AboutNetErrorOpenCaptivePortal": {wantUntrusted: true},
+        "AboutNetErrorSetAutomatic": {wantUntrusted: true},
+        "AboutNetErrorResetPreferences": {wantUntrusted: true},
+        "click": {},
+      },
+      matches: ["about:certerror?*", "about:neterror?*"],
+      allFrames: true,
+      messages: [
+        "Browser:CaptivePortalFreed",
+        "CertErrorDetails",
+      ],
+    },
+  },
+
+  OfflineApps: {
+    child: {
+      module: "resource:///actors/OfflineAppsChild.jsm",
+      events: {
+        "MozApplicationManifest": {},
+      },
+      messages: [
+        "OfflineApps:StartFetching",
+      ],
+    },
+  },
+
+  PageInfo: {
+    child: {
+      module: "resource:///actors/PageInfoChild.jsm",
+      messages: ["PageInfo:getData"],
+    },
+  },
+
+  PageMetadata: {
+    child: {
+      module: "resource:///actors/PageMetadataChild.jsm",
+      messages: [
+        "PageMetadata:GetPageData",
+        "PageMetadata:GetMicroformats",
+      ],
+    },
+  },
+
+  PageStyle: {
+    child: {
+      module: "resource:///actors/PageStyleChild.jsm",
+      group: "browsers",
+      events: {
+        "pageshow": {},
+      },
+      messages: [
+        "PageStyle:Switch",
+        "PageStyle:Disable",
+      ]
+    },
+  },
+
+  Plugin: {
+    child: {
+      module: "resource:///actors/PluginChild.jsm",
+      events: {
+        "PluginBindingAttached": {capture: true, wantUntrusted: true},
+        "PluginCrashed": {capture: true},
+        "PluginOutdated": {capture: true},
+        "PluginInstantiated": {capture: true},
+        "PluginRemoved": {capture: true},
+        "HiddenPlugin": {capture: true},
+      },
+
+      messages: [
+        "BrowserPlugins:ActivatePlugins",
+        "BrowserPlugins:NotificationShown",
+        "BrowserPlugins:ContextMenuCommand",
+        "BrowserPlugins:NPAPIPluginProcessCrashed",
+        "BrowserPlugins:CrashReportSubmitted",
+        "BrowserPlugins:Test:ClearCrashData",
+      ],
+
+      observers: [
+        "decoder-doctor-notification",
+      ],
+    },
+  },
+
+  ShieldFrame: {
+    child: {
+      module: "resource://normandy-content/ShieldFrameChild.jsm",
+      events: {
+        "ShieldPageEvent": {wantUntrusted: true},
+      },
+      matches: ["about:studies"],
+    },
+  },
+
+  UITour: {
+    child: {
+      module: "resource:///modules/UITourChild.jsm",
+      events: {
+        "mozUITour": {wantUntrusted: true},
+      },
+      permissions: ["uitour"],
+    },
+  },
+
+  URIFixup: {
+    child: {
+      module: "resource:///actors/URIFixupChild.jsm",
+      group: "browsers",
+      observers: ["keyword-uri-fixup"],
+    },
+  },
+
+  WebRTC: {
+    child: {
+      module: "resource:///actors/WebRTCChild.jsm",
+      messages: [
+        "rtcpeer:Allow",
+        "rtcpeer:Deny",
+        "webrtc:Allow",
+        "webrtc:Deny",
+        "webrtc:StopSharing",
+      ],
+    },
+  },
+};
+
 (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");
@@ -641,16 +895,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/browser/components/places/content/bookmarksSidebar.xul
+++ b/browser/components/places/content/bookmarksSidebar.xul
@@ -12,16 +12,17 @@
 %placesDTD;
 <!ENTITY % editMenuDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
 %editMenuDTD;
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
 %browserDTD;
 ]>
 
 <page id="bookmarksPanel"
+      class="sidebar-panel"
       xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
       xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
       onload="init();"
       onunload="PlacesUIUtils.setMouseoverURL('', window);"
       aria-label="&bookmarksButton.label;">
 
   <script type="application/javascript"
           src="chrome://browser/content/places/bookmarksSidebar.js"/>
--- a/browser/components/places/content/historySidebar.xul
+++ b/browser/components/places/content/historySidebar.xul
@@ -12,16 +12,17 @@
 %placesDTD;
 <!ENTITY % editMenuDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
 %editMenuDTD;
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
 %browserDTD;
 ]>
 
 <page id="history-panel"
+      class="sidebar-panel"
       orient="vertical"
       xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
       onload="HistorySidebarInit();"
       onunload="PlacesUIUtils.setMouseoverURL('', window);"
       aria-label="&historyButton.label;">
 
   <script type="application/javascript"
           src="chrome://browser/content/places/historySidebar.js"/>
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -1096,17 +1096,17 @@ var SessionStoreInternal = {
 
     let mm = aWindow.getGroupMessageManager("browsers");
     MESSAGES.forEach(msg => {
       let listenWhenClosed = CLOSED_MESSAGES.has(msg);
       mm.addMessageListener(msg, this, listenWhenClosed);
     });
 
     // Load the frame script after registering listeners.
-    mm.loadFrameScript("chrome://browser/content/content-sessionStore.js", true);
+    mm.loadFrameScript("chrome://browser/content/content-sessionStore.js", true, true);
 
     // and create its data object
     this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false };
 
     if (PrivateBrowsingUtils.isWindowPrivate(aWindow))
       this._windows[aWindow.__SSi].isPrivate = true;
     if (!this._isWindowLoaded(aWindow))
       this._windows[aWindow.__SSi]._restoring = true;
--- a/browser/components/sessionstore/test/browser_send_async_message_oom.js
+++ b/browser/components/sessionstore/test/browser_send_async_message_oom.js
@@ -24,18 +24,18 @@ function frameScript() {
     return function(name, ...args) {
       if (name != "SessionStore:update") {
         return original(name, ...args);
       }
       throw new Components.Exception("Simulated OOM", Cr.NS_ERROR_OUT_OF_MEMORY);
     };
   };
 
-  mm.sendAsyncMessage = wrap(mm.sendAsyncMessage);
-  mm.sendSyncMessage = wrap(mm.sendSyncMessage);
+  mm.sendAsyncMessage = wrap(mm.sendAsyncMessage.bind(mm));
+  mm.sendSyncMessage = wrap(mm.sendSyncMessage.bind(mm));
 }
 
 add_task(async function() {
   // Capture original state.
   let snapshot = Services.telemetry.getHistogramById(HISTOGRAM_NAME).snapshot();
 
   // Open a browser, configure it to cause OOM.
   let newTab = BrowserTestUtils.addTab(gBrowser, "about:robots");
rename from browser/components/uitour/ContentUITour.jsm
rename to browser/components/uitour/UITourChild.jsm
--- a/browser/components/uitour/ContentUITour.jsm
+++ b/browser/components/uitour/UITourChild.jsm
@@ -1,24 +1,21 @@
 /* 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/. */
 
-var EXPORTED_SYMBOLS = ["UITourListener"];
+var EXPORTED_SYMBOLS = ["UITourChild"];
 
+ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 const PREF_TEST_WHITELIST = "browser.uitour.testingOrigins";
 const UITOUR_PERMISSION   = "uitour";
 
-class UITourListener {
-  constructor(mm) {
-    this.mm = mm;
-  }
-
+class UITourChild extends ActorChild {
   handleEvent(event) {
     if (!Services.prefs.getBoolPref("browser.uitour.enabled")) {
       return;
     }
     if (!this.ensureTrustedOrigin()) {
       return;
     }
     this.mm.addMessageListener("UITour:SendPageCallback", this);
--- a/browser/components/uitour/moz.build
+++ b/browser/components/uitour/moz.build
@@ -1,15 +1,15 @@
 # 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/.
 
 EXTRA_JS_MODULES += [
-    'ContentUITour.jsm',
     'UITour.jsm',
+    'UITourChild.jsm',
 ]
 
 BROWSER_CHROME_MANIFESTS += [
     'test/browser.ini',
 ]
 
 with Files('**'):
     BUG_COMPONENT = ('Firefox', 'Tours')
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -35,17 +35,21 @@ if test "$OS_ARCH" = "WINNT"; then
 fi
 
 # Enable building ./signmar and running libmar signature tests
 MOZ_ENABLE_SIGNMAR=1
 
 MOZ_APP_VERSION=$FIREFOX_VERSION
 MOZ_APP_VERSION_DISPLAY=$FIREFOX_VERSION_DISPLAY
 
-BROWSER_CHROME_URL=chrome://browser/content/browser.xul
+if [ "${MOZ_BROWSER_XHTML}" = "1" ]; then
+  BROWSER_CHROME_URL=chrome://browser/content/browser.xhtml
+else
+  BROWSER_CHROME_URL=chrome://browser/content/browser.xul
+fi
 
 # MOZ_APP_DISPLAYNAME will be set by branding/configure.sh
 # MOZ_BRANDING_DIRECTORY is the default branding directory used when none is
 # specified. It should never point to the "official" branding directory.
 # For mozilla-beta, mozilla-release, or mozilla-central repositories, use
 # "unofficial" branding.
 # For the mozilla-aurora repository, use "aurora".
 MOZ_BRANDING_DIRECTORY=browser/branding/unofficial
--- a/browser/extensions/formautofill/bootstrap.js
+++ b/browser/extensions/formautofill/bootstrap.js
@@ -126,17 +126,17 @@ function startup(data) {
   Services.mm.addMessageListener("FormAutoComplete:MaybeOpenPopup", onMaybeOpenPopup);
 
   formAutofillParent.init().catch(Cu.reportError);
   /* eslint-disable no-unused-vars */
   Services.ppmm.loadProcessScript("data:,new " + function() {
     ChromeUtils.import("resource://formautofill/FormAutofillContent.jsm");
   }, true);
   /* eslint-enable no-unused-vars */
-  Services.mm.loadFrameScript("chrome://formautofill/content/FormAutofillFrameScript.js", true);
+  Services.mm.loadFrameScript("chrome://formautofill/content/FormAutofillFrameScript.js", true, true);
 }
 
 function shutdown() {
   resProto.setSubstitution(RESOURCE_HOST, null);
 
   Services.mm.removeMessageListener("FormAutoComplete:MaybeOpenPopup", onMaybeOpenPopup);
 
   let enumerator = Services.wm.getEnumerator("navigator:browser");
--- a/browser/extensions/onboarding/bootstrap.js
+++ b/browser/extensions/onboarding/bootstrap.js
@@ -169,17 +169,17 @@ function initContentMessageListener() {
 /**
  * onBrowserReady - Continues startup of the add-on after browser is ready.
  */
 function onBrowserReady() {
   waitingForBrowserReady = false;
 
   OnboardingTourType.check();
   OnboardingTelemetry.init(startupData);
-  Services.mm.loadFrameScript("resource://onboarding/onboarding.js", true);
+  Services.mm.loadFrameScript("resource://onboarding/onboarding.js", true, true);
   initContentMessageListener();
 }
 
 /**
  * observe - nsIObserver callback to handle various browser notifications.
  */
 function observe(subject, topic, data) {
   switch (topic) {
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -376,16 +376,18 @@
 
 ; [PDF Viewer]
 @RESPATH@/browser/components/pdfjs.manifest
 @RESPATH@/browser/components/pdfjs.js
 
 ; Modules
 @RESPATH@/browser/modules/*
 @RESPATH@/modules/*
+@RESPATH@/browser/actors/*
+@RESPATH@/actors/*
 
 ; Safe Browsing
 @RESPATH@/components/nsURLClassifier.manifest
 @RESPATH@/components/nsUrlClassifierHashCompleter.js
 @RESPATH@/components/nsUrlClassifierListManager.js
 @RESPATH@/components/nsUrlClassifierLib.js
 
 ; Private Browsing
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/browser/aboutPolicies.ftl
@@ -0,0 +1,14 @@
+# 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/.
+
+about-policies-title = Enterprise Policies
+
+# 'Active' is used to describe the policies that are currently active
+active-policies-tab = Active
+errors-tab = Errors
+documentation-tab = Documentation
+
+policy-name = Policy Name
+policy-value = Policy Value
+policy-errors = Policy Errors
--- a/browser/modules/ContentObservers.js
+++ b/browser/modules/ContentObservers.js
@@ -12,18 +12,18 @@
  * which is bad for perf.
  */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-ChromeUtils.defineModuleGetter(this, "ContentWebRTC",
-  "resource:///modules/ContentWebRTC.jsm");
+ChromeUtils.defineModuleGetter(this, "WebRTCChild",
+  "resource:///actors/WebRTCChild.jsm");
 
 var gEMEUIObserver = function(subject, topic, data) {
   let win = subject.top;
   let mm = getMessageManagerForWindow(win);
   if (mm) {
     mm.sendAsyncMessage("EMEVideo:ContentMediaKeysRequest", data);
   }
 };
@@ -39,25 +39,25 @@ var gDecoderDoctorObserver = function(su
 function getMessageManagerForWindow(aContentWindow) {
   return aContentWindow.docShell.messageManager;
 }
 
 Services.obs.addObserver(gEMEUIObserver, "mediakeys-request");
 Services.obs.addObserver(gDecoderDoctorObserver, "decoder-doctor-notification");
 
 
-// ContentWebRTC observer registration.
+// WebRTCChild observer registration.
 const kWebRTCObserverTopics = ["getUserMedia:request",
                                "recording-device-stopped",
                                "PeerConnection:request",
                                "recording-device-events",
                                "recording-window-ended"];
 
 function webRTCObserve(aSubject, aTopic, aData) {
-  ContentWebRTC.observe(aSubject, aTopic, aData);
+  WebRTCChild.observe(aSubject, aTopic, aData);
 }
 
 for (let topic of kWebRTCObserverTopics) {
   Services.obs.addObserver(webRTCObserve, topic);
 }
 
 if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT)
   Services.obs.addObserver(processShutdown, "content-child-shutdown");
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -50,46 +50,31 @@ with Files("*Telemetry.jsm"):
     BUG_COMPONENT = ("Toolkit", "Telemetry")
 
 with Files("ContentCrashHandlers.jsm"):
     BUG_COMPONENT = ("Toolkit", "Crash Reporting")
 
 with Files("ContentSearch.jsm"):
     BUG_COMPONENT = ("Firefox", "Search")
 
-with Files("ContentWebRTC.jsm"):
-    BUG_COMPONENT = ("Firefox", "Device Permissions")
-
 with Files("ExtensionsUI.jsm"):
     BUG_COMPONENT = ("WebExtensions", "General")
 
 with Files("LaterRun.jsm"):
     BUG_COMPONENT = ("Firefox", "Tours")
 
 with Files("LightweightThemeChildHelper.jsm"):
     BUG_COMPONENT = ("WebExtensions", "Themes")
 
-with Files("LightWeightThemeWebInstallListener.jsm"):
-    BUG_COMPONENT = ("Firefox", "Theme")
-
 with Files("OpenInTabsUtils.jsm"):
     BUG_COMPONENT = ("Firefox", "Tabbed Browser")
 
-with Files("PageInfoListener.jsm"):
-    BUG_COMPONENT = ("Firefox", "Page Info Window")
-
-with Files("PageStyleHandler.jsm"):
-    BUG_COMPONENT = ("Firefox", "Menus")
-
 with Files("PermissionUI.jsm"):
    BUG_COMPONENT = ("Firefox", "Site Identity and Permission Panels")
 
-with Files("PluginContent.jsm"):
-    BUG_COMPONENT = ("Core", "Plug-ins")
-
 with Files("ProcessHangMonitor.jsm"):
     BUG_COMPONENT = ("Core", "DOM: Content Processes")
 
 with Files("ReaderParent.jsm"):
     BUG_COMPONENT = ("Toolkit", "Reader Mode")
 
 with Files("Sanitizer.jsm"):
     BUG_COMPONENT = ("Firefox", "Preferences")
@@ -129,45 +114,36 @@ BROWSER_CHROME_MANIFESTS += [
     'test/browser/browser.ini',
     'test/browser/formValidation/browser.ini',
 ]
 XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 
 EXTRA_JS_MODULES += [
     'AboutNewTab.jsm',
     'AsyncTabSwitcher.jsm',
-    'BlockedSiteContent.jsm',
     'BrowserErrorReporter.jsm',
     'BrowserUsageTelemetry.jsm',
     'BrowserWindowTracker.jsm',
-    'ClickEventHandler.jsm',
     'ContentClick.jsm',
     'ContentCrashHandlers.jsm',
     'ContentLinkHandler.jsm',
     'ContentMetaHandler.jsm',
     'ContentObservers.js',
     'ContentSearch.jsm',
-    'ContentWebRTC.jsm',
-    'ContextMenu.jsm',
     'ExtensionsUI.jsm',
     'Feeds.jsm',
     'FormSubmitObserver.jsm',
     'FormValidationHandler.jsm',
     'HomePage.jsm',
     'LaterRun.jsm',
     'LightweightThemeChildHelper.jsm',
-    'LightWeightThemeWebInstallListener.jsm',
-    'NetErrorContent.jsm',
     'OpenInTabsUtils.jsm',
     'PageActions.jsm',
-    'PageInfoListener.jsm',
-    'PageStyleHandler.jsm',
     'PermissionUI.jsm',
     'PingCentre.jsm',
-    'PluginContent.jsm',
     'ProcessHangMonitor.jsm',
     'ReaderParent.jsm',
     'RemotePrompt.jsm',
     'Sanitizer.jsm',
     'SavantShieldStudy.jsm',
     'SchedulePressure.jsm',
     'SiteDataManager.jsm',
     'SitePermissions.jsm',
--- a/browser/modules/test/browser/browser_ContentSearch.js
+++ b/browser/modules/test/browser/browser_ContentSearch.js
@@ -10,16 +10,20 @@ var gMsgMan;
 /* import-globals-from ../../../components/search/test/head.js */
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/browser/components/search/test/head.js",
   this);
 
 let originalEngine = Services.search.currentEngine;
 
 add_task(async function setup() {
+  await SpecialPowers.pushPrefEnv({
+    set: [["browser.newtab.preload", false]],
+  });
+
   await promiseNewEngine("testEngine.xml", {
     setAsCurrent: true,
     testPath: "chrome://mochitests/content/browser/browser/components/search/test/",
   });
 
   registerCleanupFunction(() => {
     Services.search.currentEngine = originalEngine;
   });
@@ -341,28 +345,21 @@ function waitForNewEngine(basename, numI
       addDeferred.reject();
     },
   });
 
   return Promise.all([addDeferred.promise].concat(eventPromises));
 }
 
 async function addTab() {
-  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:newtab");
   registerCleanupFunction(() => gBrowser.removeTab(tab));
 
   let url = getRootDirectory(gTestPath) + TEST_CONTENT_SCRIPT_BASENAME;
   gMsgMan = tab.linkedBrowser.messageManager;
-  gMsgMan.sendAsyncMessage(CONTENT_SEARCH_MSG, {
-    type: "AddToWhitelist",
-    data: ["about:blank"],
-  });
-
-  await waitForMsg(CONTENT_SEARCH_MSG, "AddToWhitelistAck");
-
   gMsgMan.loadFrameScript(url, false);
 }
 
 var currentStateObj = async function() {
   let state = {
     engines: [],
     currentEngine: await currentEngineObj(),
   };
--- a/browser/moz.build
+++ b/browser/moz.build
@@ -7,16 +7,17 @@
 CONFIGURE_SUBST_FILES += ['installer/Makefile']
 
 SPHINX_TREES['browser'] = 'docs'
 
 with Files('docs/**'):
     SCHEDULES.exclusive = ['docs']
 
 DIRS += [
+    'actors',
     'base',
     'components',
     'fonts',
     'locales',
     'modules',
     'themes',
     'extensions',
 ]
--- a/browser/themes/linux/places/places.css
+++ b/browser/themes/linux/places/places.css
@@ -1,49 +1,33 @@
 /* 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/. */
 
 /* Sidebars */
 
-#history-panel,
-#bookmarksPanel {
-  -moz-appearance: none;
-  background-color: transparent;
-}
+%include ../../shared/places/places.inc.css
 
 #sidebar-search-container {
   padding: 8px;
 }
 
 #search-box {
   margin: 0;
 }
 
 #viewButton {
-  -moz-appearance: none;
-  border-radius: 4px;
   margin: 1px 0;
   margin-inline-start: 4px;
-  padding: 2px 4px;
-  color: inherit;
 }
 
 #viewButton:-moz-focusring:not(:hover):not([open]) {
   outline: 1px dotted -moz-DialogText;
 }
 
-#viewButton:hover {
-  background: hsla(240, 5%, 5%, 0.1);
-}
-
-#viewButton[open] {
-  background: hsla(240, 5%, 5%, 0.15);
-}
-
 .sidebar-placesTree {
   margin: 0;
   color: inherit;
   -moz-appearance: none;
   background: transparent;
 }
 
 :root[uidensity=touch] #search-box,
--- a/browser/themes/osx/places/places.css
+++ b/browser/themes/osx/places/places.css
@@ -1,19 +1,15 @@
 /* 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/. */
 
 /* Sidebars */
 
-#bookmarksPanel,
-#history-panel {
-  -moz-appearance: none;
-  background-color: transparent;
-}
+%include ../../shared/places/places.inc.css
 
 .sidebar-placesTree,
 .sidebar-placesTreechildren::-moz-tree-row {
   padding-bottom: 1px;
   margin: 0;
   height: 24px;
   /* Default font size is 11px on mac, so this is 12px */
   font-size: 1.0909rem;
@@ -52,20 +48,16 @@
 }
 
 .sidebar-placesTreechildren::-moz-tree-twisty(selected),
 .sidebar-placesTreechildren::-moz-tree-cell-text(selected) {
   color: #fff;
   font-weight: bold;
 }
 
-#sidebar-search-label {
-  display: none;
-}
-
 #sidebar-search-container {
   /* Native searchbar styling already adds 4px margin on Mac, so
    * adding 4px padding results in 8px of total whitespace. */
   padding: 4px;
 }
 
 @media (-moz-mac-yosemite-theme) {
   .sidebar-placesTreechildren::-moz-tree-twisty(selected),
@@ -76,39 +68,21 @@
 
   .sidebar-placesTreechildren::-moz-tree-twisty(selected, focus),
   .sidebar-placesTreechildren::-moz-tree-cell-text(selected, focus) {
     color: #fff;
   }
 }
 
 #viewButton {
-  -moz-appearance: none;
-  border-radius: 4px;
-  padding: 2px 4px;
   margin: 4px 0;
   margin-inline-end: 4px;
   /* Default font size is 11px on mac, so this is 12px */
   font-size: 1.0909rem;
 }
 
 #viewButton:focus {
   box-shadow: var(--focus-ring-box-shadow);
 }
 
-#viewButton:hover {
-  background: hsla(240, 5%, 5%, 0.1);
-}
-
-#viewButton[open] {
-  background: hsla(240, 5%, 5%, 0.15);
-}
-
-#viewButton > .button-box > .button-menu-dropmarker {
-  display: -moz-box;
-  list-style-image: url("chrome://global/skin/icons/arrow-dropdown-12.svg");
-  width: 12px;
-  height: 12px;
-}
-
 /* Trees */
 
 %include ../../shared/places/tree-icons.inc.css
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/places/places.inc.css
@@ -0,0 +1,33 @@
+/* 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/. */
+
+.sidebar-panel {
+  -moz-appearance: none;
+  background-color: transparent;
+}
+
+#viewButton {
+  -moz-appearance: none;
+  border-radius: 4px;
+  padding: 2px 4px;
+  color: inherit;
+}
+
+#viewButton:hover {
+  background-color: hsla(240, 5%, 5%, .1);
+}
+
+#viewButton[open] {
+  background-color: hsla(240, 5%, 5%, .15);
+}
+
+#viewButton > .button-box > .button-menu-dropmarker {
+  -moz-appearance: none !important;
+  display: -moz-box;
+  list-style-image: url("chrome://global/skin/icons/arrow-dropdown-12.svg");
+  width: 12px;
+  height: 12px;
+  -moz-context-properties: fill;
+  fill: currentColor;
+}
--- a/browser/themes/windows/places/places.css
+++ b/browser/themes/windows/places/places.css
@@ -1,18 +1,15 @@
 /* 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/. */
 
 /* Sidebars */
 
-#history-panel,
-#bookmarksPanel {
-  background-color: transparent;
-}
+%include ../../shared/places/places.inc.css
 
 .sidebar-placesTree {
   -moz-appearance: none;
   background-color: transparent;
   color: inherit;
   border: 0;
   margin: 0;
 }
@@ -50,35 +47,20 @@
 /* Trees */
 
 %include ../../shared/places/tree-icons.inc.css
 
 /* Browser Sidebars */
 
 /* Default button vert. margins are 1px/2px, and this can cause misalignment */
 #viewButton {
-  -moz-appearance: none;
   margin: 0;
   margin-inline-start: 4px;
   border-radius: 2px;
 }
 
-#viewButton:hover {
-  background-color: hsla(240, 5%, 5%, .1);
-}
-
-#viewButton[open] {
-  background-color: hsla(240, 5%, 5%, .15);
-}
-
-#viewButton > .button-box > .button-menu-dropmarker {
-  height: auto;
-  width: auto;
-  margin-inline-end: -3px;
-}
-
 #sidebar-search-container {
   padding: 8px;
 }
 
 #search-box {
   margin: 0;
 }
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -626,16 +626,17 @@ support-files =
   examples/pause-points.js
   examples/script-mutate.js
   examples/script-switching-02.js
   examples/script-switching-01.js
   examples/times2.js
   examples/doc_rr_basic.html
   examples/doc_rr_continuous.html
   examples/doc_rr_recovery.html
+  examples/doc_rr_error.html
 
 [browser_dbg-asm.js]
 [browser_dbg-async-stepping.js]
 [browser_dbg-sourcemapped-breakpoint-console.js]
 skip-if = (os == "win" && ccov) # Bug 1453549
 [browser_dbg-sourcemapped-scopes.js]
 skip-if = ccov || (verify && debug && (os == 'linux')) # Bug 1441545
 [browser_dbg-sourcemapped-stepping.js]
@@ -743,8 +744,10 @@ skip-if = os != "mac" || debug || !night
 [browser_dbg_rr_recovery-01.js]
 skip-if = true # See bug 1481009
 [browser_dbg_rr_replay-01.js]
 skip-if = os != "mac" || debug || !nightly_build
 [browser_dbg_rr_replay-02.js]
 skip-if = os != "mac" || debug || !nightly_build
 [browser_dbg_rr_replay-03.js]
 skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_console_warp-01.js]
+skip-if = os != "mac" || debug || !nightly_build
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-01.js
@@ -0,0 +1,83 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var { HUDService } = require("devtools/client/webconsole/hudservice");
+
+// This functionality was copied from devtools/client/webconsole/test/mochitest/head.js,
+// since this test straddles both the web console and the debugger. I couldn't
+// figure out how to load that script directly here.
+
+function findMessages(hud, text, selector = ".message") {
+  const messages = hud.ui.outputNode.querySelectorAll(selector);
+  const elements = Array.prototype.filter.call(
+    messages,
+    (el) => el.textContent.includes(text)
+  );
+  return elements;
+}
+
+async function openContextMenu(hud, element) {
+  const onConsoleMenuOpened = hud.ui.consoleOutput.once("menu-open");
+  synthesizeContextMenuEvent(element);
+  await onConsoleMenuOpened;
+  const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
+  return doc.getElementById("webconsole-menu");
+}
+
+function hideContextMenu(hud) {
+  const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
+  const popup = doc.getElementById("webconsole-menu");
+  if (!popup) {
+    return Promise.resolve();
+  }
+
+  const onPopupHidden = once(popup, "popuphidden");
+  popup.hidePopup();
+  return onPopupHidden;
+}
+
+// Test basic console time warping functionality in web replay.
+async function test() {
+  waitForExplicitFinish();
+
+  let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_error.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  let console = await openToolboxForTab(tab, "webconsole");
+  let hud = console.getCurrentPanel().hud;
+  let messages = findMessages(hud, "Number 5");
+  ok(messages.length == 1, "Found one message");
+  let message = messages.pop();
+
+  let menuPopup = await openContextMenu(hud, message);
+  let timeWarpItem = menuPopup.querySelector("#console-menu-time-warp");
+  ok(timeWarpItem, "Time warp menu item is available");
+  timeWarpItem.click();
+  await hideContextMenu(hud);
+
+  await once(Services.ppmm, "TimeWarpFinished");
+
+  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  await client.interrupt();
+
+  await checkEvaluateInTopFrame(client, "number", 5);
+
+  // Initially we are paused inside the 'new Error()' call on line 19. The
+  // first reverse step takes us to the start of that line.
+  await reverseStepOverToLine(client, 19);
+
+  await reverseStepOverToLine(client, 18);
+  await setBreakpoint(client, "doc_rr_error.html", 12);
+  await rewindToLine(client, 12);
+  await checkEvaluateInTopFrame(client, "number", 4);
+  await resumeToLine(client, 12);
+  await checkEvaluateInTopFrame(client, "number", 5);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/doc_rr_error.html
@@ -0,0 +1,23 @@
+<html lang="en" dir="ltr">
+<body>
+<div id="maindiv">Hello World!</div>
+</body>
+<script>
+const cpmm = SpecialPowers.Services.cpmm;
+function recordingFinished() {
+  cpmm.sendAsyncMessage("RecordingFinished");
+}
+var number = 0;
+function f() {
+  number++;
+  document.getElementById("maindiv").innerHTML = "Number: " + number;
+  if (number >= 10) {
+    window.setTimeout(recordingFinished);
+    return;
+  }
+  window.setTimeout(f, 1);
+  throw new Error("Number " + number);
+}
+window.setTimeout(f, 1);
+</script>
+</html>
--- a/docshell/shistory/ChildSHistory.cpp
+++ b/docshell/shistory/ChildSHistory.cpp
@@ -127,17 +127,17 @@ JSObject*
 ChildSHistory::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
 {
   return ChildSHistory_Binding::Wrap(cx, this, aGivenProto);
 }
 
 nsISupports*
 ChildSHistory::GetParentObject() const
 {
-  // We want to get the TabChildGlobal, which is the
+  // We want to get the TabChildMessageManager, which is the
   // messageManager on mDocShell.
   RefPtr<ContentFrameMessageManager> mm;
   if (mDocShell) {
     mm = mDocShell->GetMessageManager();
   }
   // else we must be unlinked... can that happen here?
   return ToSupports(mm);
 }
new file mode 100644
--- /dev/null
+++ b/dom/base/ContentFrameMessageManager.cpp
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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/. */
+
+#include "ContentFrameMessageManager.h"
+#include "mozilla/dom/ScriptSettings.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+JSObject*
+ContentFrameMessageManager::GetOrCreateWrapper()
+{
+  AutoJSAPI jsapi;
+  jsapi.Init();
+
+  JS::RootedValue val(jsapi.cx());
+  if (!GetOrCreateDOMReflectorNoWrap(jsapi.cx(), this, &val)) {
+    return nullptr;
+  }
+  MOZ_ASSERT(val.isObject());
+  return &val.toObject();
+}
--- a/dom/base/ContentFrameMessageManager.h
+++ b/dom/base/ContentFrameMessageManager.h
@@ -6,71 +6,51 @@
 
 #ifndef mozilla_dom_ContentFrameMessageManager_h
 #define mozilla_dom_ContentFrameMessageManager_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/MessageManagerGlobal.h"
 #include "mozilla/dom/ResolveSystemBinding.h"
 #include "nsContentUtils.h"
+#include "xpcpublic.h"
 
 namespace mozilla {
 namespace dom {
 
 /**
  * Base class for implementing the WebIDL ContentFrameMessageManager class.
  */
 class ContentFrameMessageManager : public DOMEventTargetHelper,
                                    public MessageManagerGlobal
 {
 public:
   using DOMEventTargetHelper::AddRef;
   using DOMEventTargetHelper::Release;
 
-  bool DoResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
-                 JS::Handle<jsid> aId,
-                 JS::MutableHandle<JS::PropertyDescriptor> aDesc)
-  {
-    bool found;
-    if (!SystemGlobalResolve(aCx, aObj, aId, &found)) {
-      return false;
-    }
-    if (found) {
-      FillPropertyDescriptor(aDesc, aObj, JS::UndefinedValue(), false);
-    }
-    return true;
-  }
-  static bool MayResolve(jsid aId)
-  {
-    return MayResolveAsSystemBindingName(aId);
-  }
-  void GetOwnPropertyNames(JSContext* aCx, JS::AutoIdVector& aNames,
-                           bool aEnumerableOnly, mozilla::ErrorResult& aRv)
-  {
-    JS::Rooted<JSObject*> thisObj(aCx, GetWrapper());
-    GetSystemBindingNames(aCx, thisObj, aNames, aEnumerableOnly, aRv);
-  }
-
   virtual already_AddRefed<nsPIDOMWindowOuter> GetContent(ErrorResult& aError) = 0;
   virtual already_AddRefed<nsIDocShell> GetDocShell(ErrorResult& aError) = 0;
   virtual already_AddRefed<nsIEventTarget> GetTabEventTarget() = 0;
   virtual uint64_t ChromeOuterWindowID() = 0;
 
   nsFrameMessageManager* GetMessageManager()
   {
     return mMessageManager;
   }
   void DisconnectMessageManager()
   {
     mMessageManager->Disconnect();
     mMessageManager = nullptr;
   }
 
+  JSObject* GetOrCreateWrapper();
+
 protected:
   explicit ContentFrameMessageManager(nsFrameMessageManager* aMessageManager)
-    : MessageManagerGlobal(aMessageManager)
+    : DOMEventTargetHelper(xpc::NativeGlobal(xpc::PrivilegedJunkScope()))
+    , MessageManagerGlobal(aMessageManager)
   {}
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ContentFrameMessageManager_h
rename from dom/base/ProcessGlobal.cpp
rename to dom/base/ContentProcessMessageManager.cpp
--- a/dom/base/ProcessGlobal.cpp
+++ b/dom/base/ContentProcessMessageManager.cpp
@@ -1,169 +1,139 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
-#include "ProcessGlobal.h"
+#include "ContentProcessMessageManager.h"
 
 #include "nsContentCID.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/MessageManagerBinding.h"
+#include "mozilla/dom/ParentProcessMessageManager.h"
 #include "mozilla/dom/ResolveSystemBinding.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ipc/SharedMap.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-bool ProcessGlobal::sWasCreated = false;
+bool ContentProcessMessageManager::sWasCreated = false;
 
-ProcessGlobal::ProcessGlobal(nsFrameMessageManager* aMessageManager)
+ContentProcessMessageManager::ContentProcessMessageManager(nsFrameMessageManager* aMessageManager)
  : MessageManagerGlobal(aMessageManager),
    mInitialized(false)
 {
   mozilla::HoldJSObjects(this);
 }
 
-ProcessGlobal::~ProcessGlobal()
+ContentProcessMessageManager::~ContentProcessMessageManager()
 {
   mAnonymousGlobalScopes.Clear();
   mozilla::DropJSObjects(this);
 }
 
-bool
-ProcessGlobal::DoResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
-                         JS::Handle<jsid> aId,
-                         JS::MutableHandle<JS::PropertyDescriptor> aDesc)
+ContentProcessMessageManager*
+ContentProcessMessageManager::Get()
 {
-    bool found;
-    if (!SystemGlobalResolve(aCx, aObj, aId, &found)) {
-      return false;
-    }
-    if (found) {
-      FillPropertyDescriptor(aDesc, aObj, JS::UndefinedValue(), false);
-    }
-    return true;
-}
-
-/* static */
-bool
-ProcessGlobal::MayResolve(jsid aId)
-{
-  return MayResolveAsSystemBindingName(aId);
-}
-
-void
-ProcessGlobal::GetOwnPropertyNames(JSContext* aCx, JS::AutoIdVector& aNames,
-                                   bool aEnumerableOnly, ErrorResult& aRv)
-{
-  JS::Rooted<JSObject*> thisObj(aCx, GetWrapper());
-  GetSystemBindingNames(aCx, thisObj, aNames, aEnumerableOnly, aRv);
-}
-
-ProcessGlobal*
-ProcessGlobal::Get()
-{
-  nsCOMPtr<nsIGlobalObject> service = do_GetService(NS_CHILDPROCESSMESSAGEMANAGER_CONTRACTID);
+  nsCOMPtr<nsIMessageSender> service = do_GetService(NS_CHILDPROCESSMESSAGEMANAGER_CONTRACTID);
   if (!service) {
     return nullptr;
   }
-  ProcessGlobal* global = static_cast<ProcessGlobal*>(service.get());
-  if (global) {
-    sWasCreated = true;
-  }
-  return global;
+  sWasCreated = true;
+  return static_cast<ContentProcessMessageManager*>(service.get());
 }
 
 already_AddRefed<mozilla::dom::ipc::SharedMap>
-ProcessGlobal::SharedData()
+ContentProcessMessageManager::SharedData()
 {
   if (ContentChild* child = ContentChild::GetSingleton()) {
     return do_AddRef(child->SharedData());
   }
   auto* ppmm = nsFrameMessageManager::sParentProcessManager;
   return do_AddRef(ppmm->SharedData()->GetReadOnly());
 }
 
 bool
-ProcessGlobal::WasCreated()
+ContentProcessMessageManager::WasCreated()
 {
   return sWasCreated;
 }
 
 void
-ProcessGlobal::MarkForCC()
+ContentProcessMessageManager::MarkForCC()
 {
   MarkScopesForCC();
   MessageManagerGlobal::MarkForCC();
 }
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(ProcessGlobal)
+NS_IMPL_CYCLE_COLLECTION_CLASS(ContentProcessMessageManager)
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ProcessGlobal)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ContentProcessMessageManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
-  tmp->TraverseHostObjectURIs(cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ProcessGlobal)
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ContentProcessMessageManager)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ProcessGlobal)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ContentProcessMessageManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
   tmp->nsMessageManagerScriptExecutor::Unlink();
-  tmp->UnlinkHostObjectURIs();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ProcessGlobal)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentProcessMessageManager)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
-  NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
-  NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(ProcessGlobal)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(ProcessGlobal)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentProcessMessageManager)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentProcessMessageManager)
 
 bool
-ProcessGlobal::Init()
+ContentProcessMessageManager::Init()
 {
   if (mInitialized) {
     return true;
   }
   mInitialized = true;
 
-  return InitChildGlobalInternal(NS_LITERAL_CSTRING("processChildGlobal"));
+  return nsMessageManagerScriptExecutor::Init();
+}
+
+JSObject*
+ContentProcessMessageManager::WrapObject(JSContext* aCx,
+                                         JS::Handle<JSObject*> aGivenProto)
+{
+  return ContentProcessMessageManager_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-bool
-ProcessGlobal::WrapGlobalObject(JSContext* aCx,
-                                JS::RealmOptions& aOptions,
-                                JS::MutableHandle<JSObject*> aReflector)
+JSObject*
+ContentProcessMessageManager::GetOrCreateWrapper()
 {
-  bool ok = ContentProcessMessageManager_Binding::Wrap(aCx, this, this, aOptions,
-                                                         nsJSPrincipals::get(mPrincipal),
-                                                         true, aReflector);
-  if (ok) {
-    // Since we can't rewrap we have to preserve the global's wrapper here.
-    PreserveWrapper(ToSupports(this));
+  AutoJSAPI jsapi;
+  jsapi.Init();
+
+  JS::RootedValue val(jsapi.cx());
+  if (!GetOrCreateDOMReflectorNoWrap(jsapi.cx(), this, &val)) {
+    return nullptr;
   }
-  return ok;
+  return &val.toObject();
 }
 
 void
-ProcessGlobal::LoadScript(const nsAString& aURL)
+ContentProcessMessageManager::LoadScript(const nsAString& aURL)
 {
   Init();
-  JS::Rooted<JSObject*> global(mozilla::dom::RootingCx(), GetWrapper());
-  LoadScriptInternal(global, aURL, false);
+  JS::Rooted<JSObject*> messageManager(mozilla::dom::RootingCx(), GetOrCreateWrapper());
+  LoadScriptInternal(messageManager, aURL, true);
 }
 
 void
-ProcessGlobal::SetInitialProcessData(JS::HandleValue aInitialData)
+ContentProcessMessageManager::SetInitialProcessData(JS::HandleValue aInitialData)
 {
   mMessageManager->SetInitialProcessData(aInitialData);
 }
rename from dom/base/ProcessGlobal.h
rename to dom/base/ContentProcessMessageManager.h
--- a/dom/base/ProcessGlobal.h
+++ b/dom/base/ContentProcessMessageManager.h
@@ -1,80 +1,72 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
-#ifndef mozilla_dom_ProcessGlobal_h
-#define mozilla_dom_ProcessGlobal_h
+#ifndef mozilla_dom_ContentProcessMessageManager_h
+#define mozilla_dom_ContentProcessMessageManager_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/MessageManagerGlobal.h"
 #include "nsCOMPtr.h"
 #include "nsFrameMessageManager.h"
 #include "nsIScriptContext.h"
-#include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIClassInfo.h"
 #include "nsIRunnable.h"
-#include "nsIGlobalObject.h"
-#include "nsIScriptObjectPrincipal.h"
 #include "nsServiceManagerUtils.h"
 #include "nsWeakReference.h"
 #include "nsWrapperCache.h"
+#include "xpcpublic.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace ipc {
   class SharedMap;
 }
 
-class ProcessGlobal :
+/**
+ * This class implements a singleton process message manager for content
+ * processes. Each child process has exactly one instance of this class, which
+ * hosts the process's process scripts, and may exchange messages with its
+ * corresponding ParentProcessMessageManager on the parent side.
+ */
+
+class ContentProcessMessageManager :
   public nsIMessageSender,
   public nsMessageManagerScriptExecutor,
-  public nsIGlobalObject,
-  public nsIScriptObjectPrincipal,
   public nsSupportsWeakReference,
   public ipc::MessageManagerCallback,
   public MessageManagerGlobal,
   public nsWrapperCache
 {
 public:
-  explicit ProcessGlobal(nsFrameMessageManager* aMessageManager);
-
-  bool DoResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
-                 JS::Handle<jsid> aId,
-                 JS::MutableHandle<JS::PropertyDescriptor> aDesc);
-  static bool MayResolve(jsid aId);
-  void GetOwnPropertyNames(JSContext* aCx, JS::AutoIdVector& aNames,
-                           bool aEnumerableOnly, ErrorResult& aRv);
+  explicit ContentProcessMessageManager(nsFrameMessageManager* aMessageManager);
 
   using ipc::MessageManagerCallback::GetProcessMessageManager;
   using MessageManagerGlobal::GetProcessMessageManager;
 
   bool Init();
 
-  static ProcessGlobal* Get();
+  static ContentProcessMessageManager* Get();
   static bool WasCreated();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(ProcessGlobal, nsIMessageSender)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(ContentProcessMessageManager, nsIMessageSender)
 
   void MarkForCC();
 
   virtual JSObject* WrapObject(JSContext* aCx,
-                               JS::Handle<JSObject*> aGivenProto) override
-  {
-    MOZ_CRASH("We should never get here!");
-  }
-  virtual bool WrapGlobalObject(JSContext* aCx,
-                                JS::RealmOptions& aOptions,
-                                JS::MutableHandle<JSObject*> aReflector) override;
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  JSObject* GetOrCreateWrapper();
 
   using MessageManagerGlobal::AddMessageListener;
   using MessageManagerGlobal::RemoveMessageListener;
   using MessageManagerGlobal::AddWeakMessageListener;
   using MessageManagerGlobal::RemoveWeakMessageListener;
 
   // ContentProcessMessageManager
   void GetInitialProcessData(JSContext* aCx,
@@ -87,36 +79,32 @@ public:
     }
     mMessageManager->GetInitialProcessData(aCx, aInitialProcessData, aError);
   }
 
   already_AddRefed<ipc::SharedMap> SharedData();
 
   NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
 
-  virtual void LoadScript(const nsAString& aURL);
+  nsIGlobalObject* GetParentObject() const { return xpc::NativeGlobal(xpc::PrivilegedJunkScope()); }
 
-  virtual JSObject* GetGlobalJSObject() override
-  {
-    return GetWrapper();
-  }
-  virtual nsIPrincipal* GetPrincipal() override { return mPrincipal; }
+  virtual void LoadScript(const nsAString& aURL);
 
   bool IsProcessScoped() const override
   {
     return true;
   }
 
   void SetInitialProcessData(JS::HandleValue aInitialData);
 
 protected:
-  virtual ~ProcessGlobal();
+  virtual ~ContentProcessMessageManager();
 
 private:
   bool mInitialized;
 
   static bool sWasCreated;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_ProcessGlobal_h
+#endif // mozilla_dom_ContentProcessMessageManager_h
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/CustomElementRegistryBinding.h"
 #include "mozilla/dom/HTMLElementBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/WebComponentsBinding.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/CustomEvent.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "nsHTMLTags.h"
 #include "jsapi.h"
 #include "xpcprivate.h"
 #include "nsGlobalWindow.h"
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/base/DocumentOrShadowRoot.cpp
+++ b/dom/base/DocumentOrShadowRoot.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
 #include "DocumentOrShadowRoot.h"
 #include "mozilla/EventStateManager.h"
+#include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/StyleSheetList.h"
 #include "nsDocument.h"
 #include "nsFocusManager.h"
 #include "nsLayoutUtils.h"
 #include "nsSVGUtils.h"
 
 namespace mozilla {
 namespace dom {
rename from dom/base/nsInProcessTabChildGlobal.cpp
rename to dom/base/InProcessTabChildMessageManager.cpp
--- a/dom/base/nsInProcessTabChildGlobal.cpp
+++ b/dom/base/InProcessTabChildMessageManager.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
-#include "nsInProcessTabChildGlobal.h"
+#include "InProcessTabChildMessageManager.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIComponentManager.h"
 #include "nsIServiceManager.h"
 #include "nsComponentManagerUtils.h"
 #include "nsFrameLoader.h"
 #include "xpcpublic.h"
@@ -20,23 +20,23 @@
 #include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/ScriptLoader.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
 bool
-nsInProcessTabChildGlobal::DoSendBlockingMessage(JSContext* aCx,
-                                                 const nsAString& aMessage,
-                                                 StructuredCloneData& aData,
-                                                 JS::Handle<JSObject *> aCpows,
-                                                 nsIPrincipal* aPrincipal,
-                                                 nsTArray<StructuredCloneData>* aRetVal,
-                                                 bool aIsSync)
+InProcessTabChildMessageManager::DoSendBlockingMessage(JSContext* aCx,
+                                                       const nsAString& aMessage,
+                                                       StructuredCloneData& aData,
+                                                       JS::Handle<JSObject *> aCpows,
+                                                       nsIPrincipal* aPrincipal,
+                                                       nsTArray<StructuredCloneData>* aRetVal,
+                                                       bool aIsSync)
 {
   SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
   queue->Flush();
 
   if (mChromeMessageManager) {
     SameProcessCpowHolder cpows(JS::RootingContext::get(aCx), aCpows);
     RefPtr<nsFrameMessageManager> mm = mChromeMessageManager;
     RefPtr<nsFrameLoader> fl = GetFrameLoader();
@@ -47,54 +47,54 @@ nsInProcessTabChildGlobal::DoSendBlockin
 }
 
 class nsAsyncMessageToParent : public nsSameProcessAsyncMessageBase,
                                public SameProcessMessageQueue::Runnable
 {
 public:
   nsAsyncMessageToParent(JS::RootingContext* aRootingCx,
                          JS::Handle<JSObject*> aCpows,
-                         nsInProcessTabChildGlobal* aTabChild)
+                         InProcessTabChildMessageManager* aTabChild)
     : nsSameProcessAsyncMessageBase(aRootingCx, aCpows)
     , mTabChild(aTabChild)
   { }
 
   virtual nsresult HandleMessage() override
   {
     RefPtr<nsFrameLoader> fl = mTabChild->GetFrameLoader();
     ReceiveMessage(mTabChild->mOwner, fl, mTabChild->mChromeMessageManager);
     return NS_OK;
   }
-  RefPtr<nsInProcessTabChildGlobal> mTabChild;
+  RefPtr<InProcessTabChildMessageManager> mTabChild;
 };
 
 nsresult
-nsInProcessTabChildGlobal::DoSendAsyncMessage(JSContext* aCx,
-                                              const nsAString& aMessage,
-                                              StructuredCloneData& aData,
-                                              JS::Handle<JSObject *> aCpows,
-                                              nsIPrincipal* aPrincipal)
+InProcessTabChildMessageManager::DoSendAsyncMessage(JSContext* aCx,
+                                                    const nsAString& aMessage,
+                                                    StructuredCloneData& aData,
+                                                    JS::Handle<JSObject *> aCpows,
+                                                    nsIPrincipal* aPrincipal)
 {
   SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
   JS::RootingContext* rcx = JS::RootingContext::get(aCx);
   RefPtr<nsAsyncMessageToParent> ev =
     new nsAsyncMessageToParent(rcx, aCpows, this);
 
   nsresult rv = ev->Init(aMessage, aData, aPrincipal);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   queue->Push(ev);
   return NS_OK;
 }
 
-nsInProcessTabChildGlobal::nsInProcessTabChildGlobal(nsIDocShell* aShell,
-                                                     nsIContent* aOwner,
-                                                     nsFrameMessageManager* aChrome)
+InProcessTabChildMessageManager::InProcessTabChildMessageManager(nsIDocShell* aShell,
+                                                                 nsIContent* aOwner,
+                                                                 nsFrameMessageManager* aChrome)
 : ContentFrameMessageManager(new nsFrameMessageManager(this)),
   mDocShell(aShell), mLoadingScript(false),
   mPreventEventsEscaping(false),
   mOwner(aOwner), mChromeMessageManager(aChrome)
 {
   mozilla::HoldJSObjects(this);
 
   // If owner corresponds to an <iframe mozbrowser>, we'll have to tweak our
@@ -103,123 +103,93 @@ nsInProcessTabChildGlobal::nsInProcessTa
   if (browserFrame) {
     mIsBrowserFrame = browserFrame->GetReallyIsBrowser();
   }
   else {
     mIsBrowserFrame = false;
   }
 }
 
-nsInProcessTabChildGlobal::~nsInProcessTabChildGlobal()
+InProcessTabChildMessageManager::~InProcessTabChildMessageManager()
 {
   mAnonymousGlobalScopes.Clear();
   mozilla::DropJSObjects(this);
 }
 
 // This method isn't automatically forwarded safely because it's notxpcom, so
 // the IDL binding doesn't know what value to return.
 void
-nsInProcessTabChildGlobal::MarkForCC()
+InProcessTabChildMessageManager::MarkForCC()
 {
   MarkScopesForCC();
   MessageManagerGlobal::MarkForCC();
 }
 
-bool
-nsInProcessTabChildGlobal::Init()
-{
-  // If you change this, please change GetCompartmentName() in XPCJSContext.cpp
-  // accordingly.
-  nsAutoCString id;
-  id.AssignLiteral("inProcessTabChildGlobal");
-  nsIURI* uri = mOwner->OwnerDoc()->GetDocumentURI();
-  if (uri) {
-    nsAutoCString u;
-    nsresult rv = uri->GetSpec(u);
-    NS_ENSURE_SUCCESS(rv, false);
-    id.AppendLiteral("?ownedBy=");
-    id.Append(u);
-  }
-  return InitChildGlobalInternal(id);
-}
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsInProcessTabChildGlobal)
+NS_IMPL_CYCLE_COLLECTION_CLASS(InProcessTabChildMessageManager)
 
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsInProcessTabChildGlobal,
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(InProcessTabChildMessageManager,
                                                   DOMEventTargetHelper)
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
-   tmp->TraverseHostObjectURIs(cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsInProcessTabChildGlobal,
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(InProcessTabChildMessageManager,
                                                DOMEventTargetHelper)
   tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsInProcessTabChildGlobal,
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(InProcessTabChildMessageManager,
                                                 DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
   tmp->nsMessageManagerScriptExecutor::Unlink();
-  tmp->UnlinkHostObjectURIs();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsInProcessTabChildGlobal)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InProcessTabChildMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsIInProcessContentFrameMessageManager)
-  NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
-  NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
-NS_IMPL_ADDREF_INHERITED(nsInProcessTabChildGlobal, DOMEventTargetHelper)
-NS_IMPL_RELEASE_INHERITED(nsInProcessTabChildGlobal, DOMEventTargetHelper)
+NS_IMPL_ADDREF_INHERITED(InProcessTabChildMessageManager, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(InProcessTabChildMessageManager, DOMEventTargetHelper)
 
-bool
-nsInProcessTabChildGlobal::WrapGlobalObject(JSContext* aCx,
-                                            JS::RealmOptions& aOptions,
-                                            JS::MutableHandle<JSObject*> aReflector)
+JSObject*
+InProcessTabChildMessageManager::WrapObject(JSContext* aCx,
+                                            JS::Handle<JSObject*> aGivenProto)
 {
-  bool ok = ContentFrameMessageManager_Binding::Wrap(aCx, this, this, aOptions,
-                                                       nsJSPrincipals::get(mPrincipal),
-                                                       true, aReflector);
-  if (ok) {
-    // Since we can't rewrap we have to preserve the global's wrapper here.
-    PreserveWrapper(ToSupports(this));
-  }
-  return ok;
+  return ContentFrameMessageManager_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 void
-nsInProcessTabChildGlobal::CacheFrameLoader(nsFrameLoader* aFrameLoader)
+InProcessTabChildMessageManager::CacheFrameLoader(nsFrameLoader* aFrameLoader)
 {
   mFrameLoader = aFrameLoader;
 }
 
 already_AddRefed<nsPIDOMWindowOuter>
-nsInProcessTabChildGlobal::GetContent(ErrorResult& aError)
+InProcessTabChildMessageManager::GetContent(ErrorResult& aError)
 {
   nsCOMPtr<nsPIDOMWindowOuter> content;
   if (mDocShell) {
     content = mDocShell->GetWindow();
   }
   return content.forget();
 }
 
 already_AddRefed<nsIEventTarget>
-nsInProcessTabChildGlobal::GetTabEventTarget()
+InProcessTabChildMessageManager::GetTabEventTarget()
 {
   nsCOMPtr<nsIEventTarget> target = GetMainThreadEventTarget();
   return target.forget();
 }
 
 uint64_t
-nsInProcessTabChildGlobal::ChromeOuterWindowID()
+InProcessTabChildMessageManager::ChromeOuterWindowID()
 {
   if (!mDocShell) {
     return 0;
   }
 
   nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(mDocShell);
   nsCOMPtr<nsIDocShellTreeItem> root;
   nsresult rv = item->GetRootTreeItem(getter_AddRefs(root));
@@ -231,75 +201,75 @@ nsInProcessTabChildGlobal::ChromeOuterWi
   if (!topWin) {
     return 0;
   }
 
   return topWin->WindowID();
 }
 
 void
-nsInProcessTabChildGlobal::FireUnloadEvent()
+InProcessTabChildMessageManager::FireUnloadEvent()
 {
   // We're called from nsDocument::MaybeInitializeFinalizeFrameLoaders, so it
   // should be safe to run script.
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
 
   // Don't let the unload event propagate to chrome event handlers.
   mPreventEventsEscaping = true;
   DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("unload"));
 
   // Allow events fired during docshell destruction (pagehide, unload) to
   // propagate to the <browser> element since chrome code depends on this.
   mPreventEventsEscaping = false;
 }
 
 void
-nsInProcessTabChildGlobal::DisconnectEventListeners()
+InProcessTabChildMessageManager::DisconnectEventListeners()
 {
   if (mDocShell) {
     if (nsCOMPtr<nsPIDOMWindowOuter> win = mDocShell->GetWindow()) {
       win->SetChromeEventHandler(win->GetChromeEventHandler());
     }
   }
   if (mListenerManager) {
     mListenerManager->Disconnect();
   }
 
   mDocShell = nullptr;
 }
 
 void
-nsInProcessTabChildGlobal::Disconnect()
+InProcessTabChildMessageManager::Disconnect()
 {
   mChromeMessageManager = nullptr;
   mOwner = nullptr;
   if (mMessageManager) {
     static_cast<nsFrameMessageManager*>(mMessageManager.get())->Disconnect();
     mMessageManager = nullptr;
   }
 }
 
 NS_IMETHODIMP_(nsIContent *)
-nsInProcessTabChildGlobal::GetOwnerContent()
+InProcessTabChildMessageManager::GetOwnerContent()
 {
   return mOwner;
 }
 
 void
-nsInProcessTabChildGlobal::GetEventTargetParent(EventChainPreVisitor& aVisitor)
+InProcessTabChildMessageManager::GetEventTargetParent(EventChainPreVisitor& aVisitor)
 {
   aVisitor.mForceContentDispatch = true;
   aVisitor.mCanHandle = true;
 
 #ifdef DEBUG
   if (mOwner) {
     nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface(mOwner);
     RefPtr<nsFrameLoader> fl = owner->GetFrameLoader();
     if (fl) {
-      NS_ASSERTION(this == fl->GetTabChildGlobal(),
+      NS_ASSERTION(this == fl->GetTabChildMessageManager(),
                    "Wrong event target!");
       NS_ASSERTION(fl->mMessageManager == mChromeMessageManager,
                    "Wrong message manager!");
     }
   }
 #endif
 
   if (mPreventEventsEscaping) {
@@ -319,52 +289,52 @@ nsInProcessTabChildGlobal::GetEventTarge
   } else {
     aVisitor.SetParentTarget(mOwner, false);
   }
 }
 
 class nsAsyncScriptLoad : public Runnable
 {
 public:
-  nsAsyncScriptLoad(nsInProcessTabChildGlobal* aTabChild,
+  nsAsyncScriptLoad(InProcessTabChildMessageManager* aTabChild,
                     const nsAString& aURL,
                     bool aRunInGlobalScope)
     : mozilla::Runnable("nsAsyncScriptLoad")
     , mTabChild(aTabChild)
     , mURL(aURL)
     , mRunInGlobalScope(aRunInGlobalScope)
   {
   }
 
   NS_IMETHOD Run() override
   {
     mTabChild->LoadFrameScript(mURL, mRunInGlobalScope);
     return NS_OK;
   }
-  RefPtr<nsInProcessTabChildGlobal> mTabChild;
+  RefPtr<InProcessTabChildMessageManager> mTabChild;
   nsString mURL;
   bool mRunInGlobalScope;
 };
 
 void
-nsInProcessTabChildGlobal::LoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope)
+InProcessTabChildMessageManager::LoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope)
 {
   if (!nsContentUtils::IsSafeToRunScript()) {
     nsContentUtils::AddScriptRunner(new nsAsyncScriptLoad(this, aURL, aRunInGlobalScope));
     return;
   }
   bool tmp = mLoadingScript;
   mLoadingScript = true;
-  JS::Rooted<JSObject*> global(mozilla::dom::RootingCx(), GetWrapper());
-  LoadScriptInternal(global, aURL, aRunInGlobalScope);
+  JS::Rooted<JSObject*> mm(mozilla::dom::RootingCx(), GetOrCreateWrapper());
+  LoadScriptInternal(mm, aURL, !aRunInGlobalScope);
   mLoadingScript = tmp;
 }
 
 already_AddRefed<nsFrameLoader>
-nsInProcessTabChildGlobal::GetFrameLoader()
+InProcessTabChildMessageManager::GetFrameLoader()
 {
   nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface(mOwner);
   RefPtr<nsFrameLoader> fl = owner ? owner->GetFrameLoader() : nullptr;
   if (!fl) {
     fl = mFrameLoader;
   }
   return fl.forget();
 }
rename from dom/base/nsInProcessTabChildGlobal.h
rename to dom/base/InProcessTabChildMessageManager.h
--- a/dom/base/nsInProcessTabChildGlobal.h
+++ b/dom/base/InProcessTabChildMessageManager.h
@@ -15,72 +15,67 @@
 #include "nsFrameMessageManager.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIClassInfo.h"
 #include "nsIDocShell.h"
 #include "nsCOMArray.h"
 #include "nsIRunnable.h"
-#include "nsIGlobalObject.h"
-#include "nsIScriptObjectPrincipal.h"
 #include "nsWeakReference.h"
 
 namespace mozilla {
 class EventChainPreVisitor;
-} // namespace mozilla
+
+namespace dom {
 
-class nsInProcessTabChildGlobal final : public mozilla::dom::ContentFrameMessageManager,
-                                        public nsMessageManagerScriptExecutor,
-                                        public nsIInProcessContentFrameMessageManager,
-                                        public nsIGlobalObject,
-                                        public nsIScriptObjectPrincipal,
-                                        public nsSupportsWeakReference,
-                                        public mozilla::dom::ipc::MessageManagerCallback
+/**
+ * This class implements a ContentFrameMessageManager for use by frame loaders
+ * in the parent process. It is bound to a DocShell rather than a TabChild, and
+ * does not use any IPC infrastructure for its message passing.
+ */
+
+class InProcessTabChildMessageManager final : public ContentFrameMessageManager,
+                                              public nsMessageManagerScriptExecutor,
+                                              public nsIInProcessContentFrameMessageManager,
+                                              public nsSupportsWeakReference,
+                                              public mozilla::dom::ipc::MessageManagerCallback
 {
   typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
 
 private:
-  nsInProcessTabChildGlobal(nsIDocShell* aShell, nsIContent* aOwner,
-                            nsFrameMessageManager* aChrome);
-
-  bool Init();
+  InProcessTabChildMessageManager(nsIDocShell* aShell, nsIContent* aOwner,
+                                  nsFrameMessageManager* aChrome);
 
 public:
-  static already_AddRefed<nsInProcessTabChildGlobal> Create(nsIDocShell* aShell,
-                                                            nsIContent* aOwner,
-                                                            nsFrameMessageManager* aChrome)
+  static already_AddRefed<InProcessTabChildMessageManager> Create(nsIDocShell* aShell,
+                                                                  nsIContent* aOwner,
+                                                                  nsFrameMessageManager* aChrome)
   {
-    RefPtr<nsInProcessTabChildGlobal> global =
-      new nsInProcessTabChildGlobal(aShell, aOwner, aChrome);
+    RefPtr<InProcessTabChildMessageManager> mm =
+      new InProcessTabChildMessageManager(aShell, aOwner, aChrome);
 
-    NS_ENSURE_TRUE(global->Init(), nullptr);
+    NS_ENSURE_TRUE(mm->Init(), nullptr);
 
-    return global.forget();
+    return mm.forget();
   }
 
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsInProcessTabChildGlobal,
-                                                         mozilla::DOMEventTargetHelper)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(InProcessTabChildMessageManager,
+                                                         DOMEventTargetHelper)
 
   void MarkForCC();
 
   virtual JSObject* WrapObject(JSContext* aCx,
-                               JS::Handle<JSObject*> aGivenProto) override
-  {
-    MOZ_CRASH("We should never get here!");
-  }
-  virtual bool WrapGlobalObject(JSContext* aCx,
-                                JS::RealmOptions& aOptions,
-                                JS::MutableHandle<JSObject*> aReflector) override;
+                               JS::Handle<JSObject*> aGivenProto) override;
 
   virtual already_AddRefed<nsPIDOMWindowOuter>
-    GetContent(mozilla::ErrorResult& aError) override;
+    GetContent(ErrorResult& aError) override;
   virtual already_AddRefed<nsIDocShell>
-    GetDocShell(mozilla::ErrorResult& aError) override
+    GetDocShell(ErrorResult& aError) override
   {
     nsCOMPtr<nsIDocShell> docShell(mDocShell);
     return docShell.forget();
   }
   virtual already_AddRefed<nsIEventTarget> GetTabEventTarget() override;
   virtual uint64_t ChromeOuterWindowID() override;
 
   NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
@@ -100,19 +95,18 @@ public:
                                       nsTArray<StructuredCloneData>* aRetVal,
                                       bool aIsSync) override;
   virtual nsresult DoSendAsyncMessage(JSContext* aCx,
                                       const nsAString& aMessage,
                                       StructuredCloneData& aData,
                                       JS::Handle<JSObject *> aCpows,
                                       nsIPrincipal* aPrincipal) override;
 
-  void GetEventTargetParent(mozilla::EventChainPreVisitor& aVisitor) override;
+  void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
 
-  virtual nsIPrincipal* GetPrincipal() override { return mPrincipal; }
   void LoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope);
   void FireUnloadEvent();
   void DisconnectEventListeners();
   void Disconnect();
   void SendMessageToParent(const nsString& aMessage, bool aSync,
                            const nsString& aJSON,
                            nsTArray<nsString>* aJSONRetVal);
   nsFrameMessageManager* GetInnerManager()
@@ -125,25 +119,20 @@ public:
   {
     return mChromeMessageManager;
   }
   void SetChromeMessageManager(nsFrameMessageManager* aParent)
   {
     mChromeMessageManager = aParent;
   }
 
-  virtual JSObject* GetGlobalJSObject() override
-  {
-    return GetWrapper();
-  }
-
   already_AddRefed<nsFrameLoader> GetFrameLoader();
 
 protected:
-  virtual ~nsInProcessTabChildGlobal();
+  virtual ~InProcessTabChildMessageManager();
 
   nsCOMPtr<nsIDocShell> mDocShell;
   bool mLoadingScript;
 
   // Is this the message manager for an in-process <iframe mozbrowser>? This
   // affects where events get sent, so it affects GetEventTargetParent.
   bool mIsBrowserFrame;
   bool mPreventEventsEscaping;
@@ -152,9 +141,12 @@ protected:
   // teardown. This allows us to dispatch message manager messages during this
   // time.
   RefPtr<nsFrameLoader> mFrameLoader;
 public:
   nsIContent* mOwner;
   nsFrameMessageManager* mChromeMessageManager;
 };
 
+} // namespace dom
+} // namespace mozilla
+
 #endif
--- a/dom/base/MessageListenerManager.cpp
+++ b/dom/base/MessageListenerManager.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
 #include "mozilla/dom/MessageListenerManager.h"
+#include "mozilla/dom/MessageBroadcaster.h"
 
 namespace mozilla {
 namespace dom {
 
 MessageListenerManager::MessageListenerManager(ipc::MessageManagerCallback* aCallback,
                                                MessageBroadcaster* aParentManager,
                                                ipc::MessageManagerFlags aFlags)
   : nsFrameMessageManager(aCallback, aFlags),
--- a/dom/base/PlacesWeakCallbackWrapper.cpp
+++ b/dom/base/PlacesWeakCallbackWrapper.cpp
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
 #include "mozilla/dom/PlacesWeakCallbackWrapper.h"
 
 #include "mozilla/HoldDropJSObjects.h"
-#include "mozilla/dom/ProcessGlobal.h"
+#include "mozilla/dom/ContentProcessMessageManager.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PlacesWeakCallbackWrapper, mParent, mCallback)
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PlacesWeakCallbackWrapper, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PlacesWeakCallbackWrapper, Release)
 
--- a/dom/base/ProcessMessageManager.cpp
+++ b/dom/base/ProcessMessageManager.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
+#include "mozilla/dom/ParentProcessMessageManager.h"
 #include "mozilla/dom/ProcessMessageManager.h"
 #include "mozilla/dom/MessageManagerBinding.h"
+#include "nsContentUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 ProcessMessageManager::ProcessMessageManager(ipc::MessageManagerCallback* aCallback,
                                              ParentProcessMessageManager* aParentManager,
                                              MessageManagerFlags aFlags)
   : MessageSender(aCallback, aParentManager,
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -147,16 +147,17 @@ EXPORTS.mozilla.dom += [
     'ChildIterator.h',
     'ChildProcessMessageManager.h',
     'ChromeMessageBroadcaster.h',
     'ChromeMessageSender.h',
     'ChromeNodeList.h',
     'ChromeUtils.h',
     'Comment.h',
     'ContentFrameMessageManager.h',
+    'ContentProcessMessageManager.h',
     'CustomElementRegistry.h',
     'DirectionalityUtils.h',
     'DispatcherTrait.h',
     'DocGroup.h',
     'DocumentFragment.h',
     'DocumentOrShadowRoot.h',
     'DocumentType.h',
     'DOMError.h',
@@ -200,17 +201,16 @@ EXPORTS.mozilla.dom += [
     'NodeInfoInlines.h',
     'NodeIterator.h',
     'ParentProcessMessageManager.h',
     'PlacesEvent.h',
     'PlacesObservers.h',
     'PlacesVisit.h',
     'PlacesWeakCallbackWrapper.h',
     'Pose.h',
-    'ProcessGlobal.h',
     'ProcessMessageManager.h',
     'ResponsiveImageSelector.h',
     'SameProcessMessageQueue.h',
     'ScreenLuminance.h',
     'ScreenOrientation.h',
     'Selection.h',
     'ShadowRoot.h',
     'StructuredCloneBlob.h',
@@ -244,16 +244,18 @@ UNIFIED_SOURCES += [
     'BorrowedAttrInfo.cpp',
     'CharacterData.cpp',
     'ChildIterator.cpp',
     'ChromeMessageBroadcaster.cpp',
     'ChromeMessageSender.cpp',
     'ChromeNodeList.cpp',
     'ChromeUtils.cpp',
     'Comment.cpp',
+    'ContentFrameMessageManager.cpp',
+    'ContentProcessMessageManager.cpp',
     'Crypto.cpp',
     'CustomElementRegistry.cpp',
     'DirectionalityUtils.cpp',
     'DispatcherTrait.cpp',
     'DocGroup.cpp',
     'DocumentFragment.cpp',
     'DocumentOrShadowRoot.cpp',
     'DocumentType.cpp',
@@ -273,16 +275,17 @@ UNIFIED_SOURCES += [
     'FormData.cpp',
     'FragmentOrElement.cpp',
     'GeneratedImageContent.cpp',
     'IdleDeadline.cpp',
     'IdleRequest.cpp',
     'IDTracker.cpp',
     'ImageEncoder.cpp',
     'ImageTracker.cpp',
+    'InProcessTabChildMessageManager.cpp',
     'IntlUtils.cpp',
     'Link.cpp',
     'Location.cpp',
     'MessageBroadcaster.cpp',
     'MessageListenerManager.cpp',
     'MessageManagerGlobal.cpp',
     'MessageSender.cpp',
     'MozQueryInterface.cpp',
@@ -313,17 +316,16 @@ UNIFIED_SOURCES += [
     'nsDOMWindowList.cpp',
     'nsFocusManager.cpp',
     'nsFrameLoader.cpp',
     'nsGlobalWindowCommands.cpp',
     'nsHistory.cpp',
     'nsHTMLContentSerializer.cpp',
     'nsIGlobalObject.cpp',
     'nsINode.cpp',
-    'nsInProcessTabChildGlobal.cpp',
     'nsJSEnvironment.cpp',
     'nsJSTimeoutHandler.cpp',
     'nsJSUtils.cpp',
     'nsLineBreaker.cpp',
     'nsMappedAttributeElement.cpp',
     'nsMappedAttributes.cpp',
     'nsMimeTypeArray.cpp',
     'nsNameSpaceManager.cpp',
@@ -352,17 +354,16 @@ UNIFIED_SOURCES += [
     'nsWindowRoot.cpp',
     'nsWrapperCache.cpp',
     'nsXHTMLContentSerializer.cpp',
     'nsXMLContentSerializer.cpp',
     'nsXMLNameSpaceMap.cpp',
     'ParentProcessMessageManager.cpp',
     'Pose.cpp',
     'PostMessageEvent.cpp',
-    'ProcessGlobal.cpp',
     'ProcessMessageManager.cpp',
     'ResponsiveImageSelector.cpp',
     'SameProcessMessageQueue.cpp',
     'ScreenLuminance.cpp',
     'ScreenOrientation.cpp',
     'Selection.cpp',
     'SelectionChangeListener.cpp',
     'ShadowRoot.cpp',
--- a/dom/base/nsCCUncollectableMarker.cpp
+++ b/dom/base/nsCCUncollectableMarker.cpp
@@ -6,40 +6,40 @@
 
 #include "nsCCUncollectableMarker.h"
 #include "nsIObserverService.h"
 #include "nsIDocShell.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIContentViewer.h"
 #include "nsIDocument.h"
 #include "XULDocument.h"
+#include "InProcessTabChildMessageManager.h"
 #include "nsIWindowMediator.h"
 #include "nsPIDOMWindow.h"
 #include "nsIWebNavigation.h"
 #include "nsISHistory.h"
 #include "nsISHEntry.h"
 #include "nsISHContainer.h"
 #include "nsIWindowWatcher.h"
 #include "mozilla/Services.h"
 #include "nsIXULWindow.h"
 #include "nsIAppShellService.h"
 #include "nsAppShellCID.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsJSEnvironment.h"
-#include "nsInProcessTabChildGlobal.h"
 #include "nsFrameLoader.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/ChromeMessageBroadcaster.h"
 #include "mozilla/dom/ContentFrameMessageManager.h"
+#include "mozilla/dom/ContentProcessMessageManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ParentProcessMessageManager.h"
-#include "mozilla/dom/ProcessGlobal.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TimeoutManager.h"
 #include "xpcpublic.h"
 #include "nsObserverService.h"
 #include "nsFocusManager.h"
 #include "nsIInterfaceRequestorUtils.h"
 
 using namespace mozilla;
@@ -115,35 +115,35 @@ MarkChildMessageManagers(MessageBroadcas
 
     tabMM->MarkForCC();
 
     //XXX hack warning, but works, since we know that
     //    callback is frameloader.
     mozilla::dom::ipc::MessageManagerCallback* cb = tabMM->GetCallback();
     if (cb) {
       nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
-      nsInProcessTabChildGlobal* et = fl->GetTabChildGlobal();
+      InProcessTabChildMessageManager* et = fl->GetTabChildMessageManager();
       if (!et) {
         continue;
       }
       et->MarkForCC();
       EventListenerManager* elm = et->GetExistingListenerManager();
       if (elm) {
         elm->MarkForCC();
       }
     }
   }
 }
 
 static void
 MarkMessageManagers()
 {
   if (nsFrameMessageManager::GetChildProcessManager()) {
-    // ProcessGlobal's MarkForCC marks also ChildProcessManager.
-    ProcessGlobal* pg = ProcessGlobal::Get();
+    // ContentProcessMessageManager's MarkForCC also marks ChildProcessManager.
+    ContentProcessMessageManager* pg = ContentProcessMessageManager::Get();
     if (pg) {
       pg->MarkForCC();
     }
   }
 
   // The global message manager only exists in the root process.
   if (!XRE_IsParentProcess()) {
     return;
@@ -286,17 +286,17 @@ MarkWindowList(nsISimpleEnumerator* aWin
          iter) {
     if (nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(iter)) {
       nsCOMPtr<nsIDocShell> rootDocShell = window->GetDocShell();
 
       MarkDocShell(rootDocShell, aCleanupJS);
 
       RefPtr<TabChild> tabChild = TabChild::GetFrom(rootDocShell);
       if (tabChild) {
-        RefPtr<TabChildGlobal> mm = tabChild->GetMessageManager();
+        RefPtr<TabChildMessageManager> mm = tabChild->GetMessageManager();
         if (mm) {
           // MarkForCC ends up calling UnmarkGray on message listeners, which
           // TraceBlackJS can't do yet.
           mm->MarkForCC();
         }
       }
     }
   }
@@ -466,18 +466,19 @@ mozilla::dom::TraceBlackJS(JSTracer* aTr
     }
   }
 #endif
 
   if (!nsCCUncollectableMarker::sGeneration) {
     return;
   }
 
-  if (ProcessGlobal::WasCreated() && nsFrameMessageManager::GetChildProcessManager()) {
-    ProcessGlobal* pg = ProcessGlobal::Get();
+  if (ContentProcessMessageManager::WasCreated() &&
+      nsFrameMessageManager::GetChildProcessManager()) {
+    auto* pg = ContentProcessMessageManager::Get();
     if (pg) {
       mozilla::TraceScriptHolder(ToSupports(pg), aTrc);
     }
   }
 
   // Mark globals of active windows black.
   nsGlobalWindowOuter::OuterWindowByIdTable* windowsById =
     nsGlobalWindowOuter::GetWindowsTable();
@@ -497,18 +498,18 @@ mozilla::dom::TraceBlackJS(JSTracer* aTr
             EventListenerManager* elm = inner->GetExistingListenerManager();
             if (elm) {
               elm->TraceListeners(aTrc);
             }
           }
         }
 
         if (window->IsRootOuterWindow()) {
-          // In child process trace all the TabChildGlobals.
-          // Since there is one root outer window per TabChildGlobal, we need
+          // In child process trace all the TabChildMessageManagers.
+          // Since there is one root outer window per TabChildMessageManager, we need
           // to look for only those windows, not all.
           nsIDocShell* ds = window->GetDocShell();
           if (ds) {
             nsCOMPtr<nsITabChild> tabChild = ds->GetTabChild();
             if (tabChild) {
               RefPtr<ContentFrameMessageManager> mm;
               tabChild->GetMessageManager(getter_AddRefs(mm));
               if (mm) {
--- a/dom/base/nsContentTypeParser.cpp
+++ b/dom/base/nsContentTypeParser.cpp
@@ -1,17 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
 #include "nsContentTypeParser.h"
+#include "nsContentUtils.h"
 #include "nsNetUtil.h"
 
+using namespace mozilla;
+
 nsContentTypeParser::nsContentTypeParser(const nsAString& aString)
   : mString(aString)
 {
 }
 
 nsresult
 nsContentTypeParser::GetParameter(const char* aParameterName,
                                   nsAString& aResult) const
--- a/dom/base/nsContentTypeParser.h
+++ b/dom/base/nsContentTypeParser.h
@@ -2,17 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 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/. */
 
 #ifndef nsContentTypeParser_h
 #define nsContentTypeParser_h
 
-#include "nsAString.h"
+#include "nsString.h"
 
 class nsContentTypeParser
 {
 public:
   explicit nsContentTypeParser(const nsAString& aString);
 
   nsresult GetParameter(const char* aParameterName, nsAString& aResult) const;
   nsresult GetType(nsAString& aResult) const;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -116,17 +116,16 @@
 #include "nsFocusManager.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGenericHTMLFrameElement.h"
 #include "nsGkAtoms.h"
 #include "nsHtml5Module.h"
 #include "nsHtml5StringParser.h"
 #include "nsHTMLDocument.h"
 #include "nsHTMLTags.h"
-#include "nsInProcessTabChildGlobal.h"
 #include "nsIAddonPolicyService.h"
 #include "nsIAnonymousContentCreator.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsICategoryManager.h"
 #include "nsIChannelEventSink.h"
 #include "nsICharsetDetectionObserver.h"
 #include "nsIChromeRegistry.h"
 #include "nsIConsoleService.h"
@@ -210,16 +209,17 @@
 #include "nsViewManager.h"
 #include "nsViewportInfo.h"
 #include "nsWidgetsCID.h"
 #include "nsIWindowProvider.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsXULPopupManager.h"
 #include "xpcprivate.h" // nsXPConnect
 #include "HTMLSplitOnSpacesTokenizer.h"
+#include "InProcessTabChildMessageManager.h"
 #include "nsContentTypeParser.h"
 #include "nsICookiePermission.h"
 #include "nsICookieService.h"
 #include "mozIThirdPartyUtil.h"
 #include "mozilla/EnumSet.h"
 #include "mozilla/BloomFilter.h"
 #include "TabChild.h"
 #include "mozilla/dom/DocGroup.h"
@@ -11024,17 +11024,17 @@ nsContentUtils::TryGetTabChildGlobal(nsI
     return nullptr;
   }
 
   RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
   if (!frameLoader) {
     return nullptr;
   }
 
-  RefPtr<ContentFrameMessageManager> manager = frameLoader->GetTabChildGlobal();
+  RefPtr<ContentFrameMessageManager> manager = frameLoader->GetTabChildMessageManager();
   return manager.forget();
 }
 
 /* static */ uint32_t
 nsContentUtils::InnerOrOuterWindowCreated()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ++sInnerOrOuterWindowCount;
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -59,17 +59,17 @@
 #include "nsNetUtil.h"
 
 #include "nsGkAtoms.h"
 #include "nsNameSpaceManager.h"
 
 #include "nsThreadUtils.h"
 
 #include "nsIDOMChromeWindow.h"
-#include "nsInProcessTabChildGlobal.h"
+#include "InProcessTabChildMessageManager.h"
 
 #include "Layers.h"
 #include "ClientLayerManager.h"
 
 #include "ContentParent.h"
 #include "TabParent.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/BasePrincipal.h"
@@ -1594,22 +1594,22 @@ nsFrameLoader::SwapWithOtherLoader(nsFra
 
   ourWindow->SetFrameElementInternal(otherFrameElement);
   otherWindow->SetFrameElementInternal(ourFrameElement);
 
   RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
   RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
   // Swap pointers in child message managers.
   if (mChildMessageManager) {
-    nsInProcessTabChildGlobal* tabChild = mChildMessageManager;
+    InProcessTabChildMessageManager* tabChild = mChildMessageManager;
     tabChild->SetOwner(otherContent);
     tabChild->SetChromeMessageManager(otherMessageManager);
   }
   if (aOther->mChildMessageManager) {
-    nsInProcessTabChildGlobal* otherTabChild = aOther->mChildMessageManager;
+    InProcessTabChildMessageManager* otherTabChild = aOther->mChildMessageManager;
     otherTabChild->SetOwner(ourContent);
     otherTabChild->SetChromeMessageManager(ourMessageManager);
   }
   // Swap and setup things in parent message managers.
   if (mMessageManager) {
     mMessageManager->SetCallback(aOther);
   }
   if (aOther->mMessageManager) {
@@ -2817,17 +2817,17 @@ nsFrameLoader::CreateStaticClone(nsFrame
 
 bool
 nsFrameLoader::DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope)
 {
   auto* tabParent = TabParent::GetFrom(GetRemoteBrowser());
   if (tabParent) {
     return tabParent->SendLoadRemoteScript(nsString(aURL), aRunInGlobalScope);
   }
-  RefPtr<nsInProcessTabChildGlobal> tabChild = GetTabChildGlobal();
+  RefPtr<InProcessTabChildMessageManager> tabChild = GetTabChildMessageManager();
   if (tabChild) {
     tabChild->LoadFrameScript(aURL, aRunInGlobalScope);
   }
   return true;
 }
 
 class nsAsyncMessageToChild : public nsSameProcessAsyncMessageBase,
                               public Runnable
@@ -2839,17 +2839,17 @@ public:
     : nsSameProcessAsyncMessageBase(aRootingCx, aCpows)
     , mozilla::Runnable("nsAsyncMessageToChild")
     , mFrameLoader(aFrameLoader)
   {
   }
 
   NS_IMETHOD Run() override
   {
-    nsInProcessTabChildGlobal* tabChild = mFrameLoader->mChildMessageManager;
+    InProcessTabChildMessageManager* tabChild = mFrameLoader->mChildMessageManager;
     // Since bug 1126089, messages can arrive even when the docShell is destroyed.
     // Here we make sure that those messages are not delivered.
     if (tabChild && tabChild->GetInnerManager() && mFrameLoader->GetExistingDocShell()) {
       JS::Rooted<JSObject*> kungFuDeathGrip(dom::RootingCx(), tabChild->GetWrapper());
       ReceiveMessage(static_cast<EventTarget*>(tabChild), mFrameLoader,
                      tabChild->GetInnerManager());
     }
     return NS_OK;
@@ -2956,17 +2956,17 @@ nsFrameLoader::EnsureMessageManager()
       return rv;
     }
     NS_ASSERTION(mDocShell,
                  "MaybeCreateDocShell succeeded, but null mDocShell");
     if (!mDocShell) {
       return NS_ERROR_FAILURE;
     }
     mChildMessageManager =
-      nsInProcessTabChildGlobal::Create(mDocShell, mOwnerContent, mMessageManager);
+      InProcessTabChildMessageManager::Create(mDocShell, mOwnerContent, mMessageManager);
     NS_ENSURE_TRUE(mChildMessageManager, NS_ERROR_UNEXPECTED);
   }
   return NS_OK;
 }
 
 nsresult
 nsFrameLoader::ReallyLoadFrameScripts()
 {
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -27,17 +27,16 @@
 #include "nsStubMutationObserver.h"
 #include "Units.h"
 #include "nsIFrame.h"
 #include "nsPluginTags.h"
 
 class nsIURI;
 class nsSubDocumentFrame;
 class nsView;
-class nsInProcessTabChildGlobal;
 class AutoResetInShow;
 class AutoResetInFrameSwap;
 class nsITabParent;
 class nsIDocShellTreeItem;
 class nsIDocShellTreeOwner;
 class nsILoadContext;
 class nsIMessageSender;
 class nsIPrintSettings;
@@ -46,16 +45,17 @@ class nsIWebProgressListener;
 
 namespace mozilla {
 
 class OriginAttributes;
 
 namespace dom {
 class ChromeMessageSender;
 class ContentParent;
+class InProcessTabChildMessageManager;
 class MessageSender;
 class PBrowserParent;
 class ProcessMessageManager;
 class Promise;
 class TabParent;
 class MutableTabContext;
 
 namespace ipc {
@@ -101,17 +101,17 @@ public:
 
   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   nsresult CheckForRecursiveLoad(nsIURI* aURI);
   nsresult ReallyStartLoading();
   void StartDestroy();
   void DestroyDocShell();
   void DestroyComplete();
   nsIDocShell* GetExistingDocShell() { return mDocShell; }
-  nsInProcessTabChildGlobal* GetTabChildGlobal() const
+  mozilla::dom::InProcessTabChildMessageManager* GetTabChildMessageManager() const
   {
     return mChildMessageManager;
   }
   nsresult CreateStaticClone(nsFrameLoader* aDest);
   nsresult UpdatePositionAndSize(nsSubDocumentFrame *aIFrame);
 
   // WebIDL methods
 
@@ -336,17 +336,17 @@ public:
 
   // Properly retrieves documentSize of any subdocument type.
   nsresult GetWindowDimensions(nsIntRect& aRect);
 
   virtual mozilla::dom::ProcessMessageManager* GetProcessMessageManager() const override;
 
   // public because a callback needs these.
   RefPtr<mozilla::dom::ChromeMessageSender> mMessageManager;
-  RefPtr<nsInProcessTabChildGlobal> mChildMessageManager;
+  RefPtr<mozilla::dom::InProcessTabChildMessageManager> mChildMessageManager;
 
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
 private:
   nsFrameLoader(mozilla::dom::Element* aOwner,
                 nsPIDOMWindowOuter* aOpener,
                 bool aNetworkCreated,
                 int32_t aJSPluginID);
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -35,19 +35,19 @@
 #include "mozilla/ScriptPreloader.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/ChildProcessMessageManager.h"
 #include "mozilla/dom/ChromeMessageBroadcaster.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MessageManagerBinding.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentProcessMessageManager.h"
 #include "mozilla/dom/ParentProcessMessageManager.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
-#include "mozilla/dom/ProcessGlobal.h"
 #include "mozilla/dom/ProcessMessageManager.h"
 #include "mozilla/dom/ResolveSystemBinding.h"
 #include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/ipc/SharedMap.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/dom/DOMStringList.h"
@@ -792,17 +792,17 @@ nsFrameMessageManager::ReceiveMessage(ns
       if (aTargetFrameLoader) {
         argument.mTargetFrameLoader.Construct(*aTargetFrameLoader);
       }
 
       JS::Rooted<JS::Value> thisValue(cx, JS::UndefinedValue());
 
       if (JS::IsCallable(object)) {
         // A small hack to get 'this' value right on content side where
-        // messageManager is wrapped in TabChildGlobal.
+        // messageManager is wrapped in TabChildMessageManager's global.
         nsCOMPtr<nsISupports> defaultThisValue;
         if (mChrome) {
           defaultThisValue = do_QueryObject(this);
         } else {
           defaultThisValue = aTarget;
         }
         js::AssertSameCompartment(cx, object);
         aError = nsContentUtils::WrapNative(cx, defaultThisValue, &thisValue);
@@ -1230,17 +1230,17 @@ NS_NewGlobalMessageManager(nsISupports**
   return NS_OK;
 }
 
 nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>*
   nsMessageManagerScriptExecutor::sCachedScripts = nullptr;
 StaticRefPtr<nsScriptCacheCleaner> nsMessageManagerScriptExecutor::sScriptCacheCleaner;
 
 void
-nsMessageManagerScriptExecutor::DidCreateGlobal()
+nsMessageManagerScriptExecutor::DidCreateScriptLoader()
 {
   if (!sCachedScripts) {
     sCachedScripts =
       new nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>;
     sScriptCacheCleaner = new nsScriptCacheCleaner();
   }
 }
 
@@ -1266,64 +1266,65 @@ nsMessageManagerScriptExecutor::Shutdown
 
     delete sCachedScripts;
     sCachedScripts = nullptr;
     sScriptCacheCleaner = nullptr;
   }
 }
 
 void
-nsMessageManagerScriptExecutor::LoadScriptInternal(JS::Handle<JSObject*> aGlobal,
+nsMessageManagerScriptExecutor::LoadScriptInternal(JS::Handle<JSObject*> aMessageManager,
                                                    const nsAString& aURL,
-                                                   bool aRunInGlobalScope)
+                                                   bool aRunInUniqueScope)
 {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
     "nsMessageManagerScriptExecutor::LoadScriptInternal", OTHER, aURL);
 
   if (!sCachedScripts) {
     return;
   }
 
   JS::RootingContext* rcx = RootingCx();
   JS::Rooted<JSScript*> script(rcx);
 
   nsMessageManagerScriptHolder* holder = sCachedScripts->Get(aURL);
-  if (holder && holder->WillRunInGlobalScope() == aRunInGlobalScope) {
+  if (holder) {
     script = holder->mScript;
   } else {
-    // Don't put anything in the cache if we already have an entry
-    // with a different WillRunInGlobalScope() value.
-    bool shouldCache = !holder;
-    TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope,
-                                 shouldCache, aGlobal, &script);
+    TryCacheLoadAndCompileScript(aURL, aRunInUniqueScope, true,
+                                 aMessageManager, &script);
   }
 
-  AutoEntryScript aes(aGlobal, "message manager script load");
+  AutoEntryScript aes(aMessageManager, "message manager script load");
   JSContext* cx = aes.cx();
   if (script) {
-    if (aRunInGlobalScope) {
-      JS::RootedValue rval(cx);
-      JS::CloneAndExecuteScript(cx, script, &rval);
-    } else {
+    if (aRunInUniqueScope) {
       JS::Rooted<JSObject*> scope(cx);
-      bool ok = js::ExecuteInGlobalAndReturnScope(cx, aGlobal, script, &scope);
+      bool ok = js::ExecuteInFrameScriptEnvironment(cx, aMessageManager, script, &scope);
       if (ok) {
         // Force the scope to stay alive.
         mAnonymousGlobalScopes.AppendElement(scope);
       }
+    } else {
+      JS::RootedValue rval(cx);
+      JS::AutoObjectVector envChain(cx);
+      if (!envChain.append(aMessageManager)) {
+        return;
+      }
+      JS::CloneAndExecuteScript(cx, envChain, script, &rval);
     }
   }
 }
 
 void
 nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
   const nsAString& aURL,
-  bool aRunInGlobalScope,
+  bool aRunInUniqueScope,
   bool aShouldCache,
-  JS::Handle<JSObject*> aGlobal,
+  JS::Handle<JSObject*> aMessageManager,
   JS::MutableHandle<JSScript*> aScriptp)
 {
   nsCString url = NS_ConvertUTF16toUTF8(aURL);
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
   if (NS_FAILED(rv)) {
     return;
   }
@@ -1342,17 +1343,17 @@ nsMessageManagerScriptExecutor::TryCache
   // scripts can be compiled directly for the target global, and will be dropped
   // from the preloader cache after they're executed and serialized.
   bool isRunOnce = !aShouldCache || IsProcessScoped();
 
   // If the script will be reused in this session, compile it in the compilation
   // scope instead of the current global to avoid keeping the current
   // compartment alive.
   AutoJSAPI jsapi;
-  if (!jsapi.Init(isRunOnce ? aGlobal : xpc::CompilationScope())) {
+  if (!jsapi.Init(isRunOnce ? aMessageManager : xpc::CompilationScope())) {
     return;
   }
   JSContext* cx = jsapi.cx();
   JS::Rooted<JSScript*> script(cx);
 
   script = ScriptPreloader::GetChildSingleton().GetCachedScript(cx, url);
 
   if (!script) {
@@ -1392,22 +1393,17 @@ nsMessageManagerScriptExecutor::TryCache
     if (!dataStringBuf || dataStringLength == 0) {
       return;
     }
 
     JS::CompileOptions options(cx);
     options.setFileAndLine(url.get(), 1);
     options.setNoScriptRval(true);
 
-    if (aRunInGlobalScope) {
-      if (!JS::Compile(cx, options, srcBuf, &script)) {
-        return;
-      }
-    // We're going to run these against some non-global scope.
-    } else if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) {
+    if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) {
       return;
     }
   }
 
   MOZ_ASSERT(script);
   aScriptp.set(script);
 
   nsAutoCString scheme;
@@ -1415,17 +1411,17 @@ nsMessageManagerScriptExecutor::TryCache
   // We don't cache data: scripts!
   if (aShouldCache && !scheme.EqualsLiteral("data")) {
     ScriptPreloader::GetChildSingleton().NoteScript(url, url, script, isRunOnce);
 
     // If this script will only run once per process, only cache it in the
     // preloader cache, not the session cache.
     if (!isRunOnce) {
       // Root the object also for caching.
-      auto* holder = new nsMessageManagerScriptHolder(cx, script, aRunInGlobalScope);
+      auto* holder = new nsMessageManagerScriptHolder(cx, script);
       sCachedScripts->Put(aURL, holder);
     }
   }
 }
 
 void
 nsMessageManagerScriptExecutor::Trace(const TraceCallbacks& aCallbacks, void* aClosure)
 {
@@ -1436,41 +1432,19 @@ nsMessageManagerScriptExecutor::Trace(co
 
 void
 nsMessageManagerScriptExecutor::Unlink()
 {
   ImplCycleCollectionUnlink(mAnonymousGlobalScopes);
 }
 
 bool
-nsMessageManagerScriptExecutor::InitChildGlobalInternal(const nsACString& aID)
+nsMessageManagerScriptExecutor::Init()
 {
-  AutoSafeJSContext cx;
-  if (!SystemBindingInitIds(cx)) {
-    return false;
-  }
-
-  nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
-
-  JS::RealmOptions options;
-  options.creationOptions().setNewCompartmentInSystemZone();
-
-  xpc::InitGlobalObjectOptions(options, mPrincipal);
-  JS::Rooted<JSObject*> global(cx);
-  if (!WrapGlobalObject(cx, options, &global)) {
-    return false;
-  }
-
-  xpc::InitGlobalObject(cx, global, 0);
-
-  // Set the location information for the new global, so that tools like
-  // about:memory may use that information.
-  xpc::SetLocationForGlobal(global, aID);
-
-  DidCreateGlobal();
+  DidCreateScriptLoader();
   return true;
 }
 
 void
 nsMessageManagerScriptExecutor::MarkScopesForCC()
 {
   for (uint32_t i = 0; i < mAnonymousGlobalScopes.Length(); ++i) {
     mAnonymousGlobalScopes[i].exposeToActiveJS();
@@ -1514,17 +1488,17 @@ public:
   ~SameParentProcessMessageManagerCallback() override
   {
     MOZ_COUNT_DTOR(SameParentProcessMessageManagerCallback);
   }
 
   bool DoLoadMessageManagerScript(const nsAString& aURL,
                                   bool aRunInGlobalScope) override
   {
-    ProcessGlobal* global = ProcessGlobal::Get();
+    auto* global = ContentProcessMessageManager::Get();
     MOZ_ASSERT(!aRunInGlobalScope);
     global->LoadScript(aURL);
     return true;
   }
 
   nsresult DoSendAsyncMessage(JSContext* aCx,
                               const nsAString& aMessage,
                               StructuredCloneData& aData,
@@ -1743,17 +1717,17 @@ NS_NewChildProcessMessageManager(nsISupp
   if (XRE_IsParentProcess()) {
     cb = new SameChildProcessMessageManagerCallback();
   } else {
     cb = new ChildProcessMessageManagerCallback();
     RegisterStrongMemoryReporter(new MessageManagerReporter());
   }
   auto* mm = new ChildProcessMessageManager(cb);
   nsFrameMessageManager::SetChildProcessManager(mm);
-  RefPtr<ProcessGlobal> global = new ProcessGlobal(mm);
+  auto global = MakeRefPtr<ContentProcessMessageManager>(mm);
   NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED);
   return CallQueryInterface(global, aResult);
 }
 
 void
 nsFrameMessageManager::MarkForCC()
 {
   for (auto iter = mListeners.Iter(); !iter.Done(); iter.Next()) {
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -402,54 +402,47 @@ private:
 #endif
 };
 
 class nsScriptCacheCleaner;
 
 struct nsMessageManagerScriptHolder
 {
   nsMessageManagerScriptHolder(JSContext* aCx,
-                               JSScript* aScript,
-                               bool aRunInGlobalScope)
-   : mScript(aCx, aScript), mRunInGlobalScope(aRunInGlobalScope)
+                               JSScript* aScript)
+   : mScript(aCx, aScript)
   { MOZ_COUNT_CTOR(nsMessageManagerScriptHolder); }
 
   ~nsMessageManagerScriptHolder()
   { MOZ_COUNT_DTOR(nsMessageManagerScriptHolder); }
 
-  bool WillRunInGlobalScope() { return mRunInGlobalScope; }
-
   JS::PersistentRooted<JSScript*> mScript;
-  bool mRunInGlobalScope;
 };
 
 class nsMessageManagerScriptExecutor
 {
 public:
   static void PurgeCache();
   static void Shutdown();
 
   void MarkScopesForCC();
 protected:
   friend class nsMessageManagerScriptCx;
   nsMessageManagerScriptExecutor() { MOZ_COUNT_CTOR(nsMessageManagerScriptExecutor); }
   ~nsMessageManagerScriptExecutor() { MOZ_COUNT_DTOR(nsMessageManagerScriptExecutor); }
 
-  void DidCreateGlobal();
-  void LoadScriptInternal(JS::Handle<JSObject*> aGlobal, const nsAString& aURL,
-                          bool aRunInGlobalScope);
+  void DidCreateScriptLoader();
+  void LoadScriptInternal(JS::Handle<JSObject*> aMessageManager, const nsAString& aURL,
+                          bool aRunInUniqueScope);
   void TryCacheLoadAndCompileScript(const nsAString& aURL,
-                                    bool aRunInGlobalScope,
+                                    bool aRunInUniqueScope,
                                     bool aShouldCache,
-                                    JS::Handle<JSObject*> aGlobal,
+                                    JS::Handle<JSObject*> aMessageManager,
                                     JS::MutableHandle<JSScript*> aScriptp);
-  bool InitChildGlobalInternal(const nsACString& aID);
-  virtual bool WrapGlobalObject(JSContext* aCx,
-                                JS::RealmOptions& aOptions,
-                                JS::MutableHandle<JSObject*> aReflector) = 0;
+  bool Init();
   void Trace(const TraceCallbacks& aCallbacks, void* aClosure);
   void Unlink();
   nsCOMPtr<nsIPrincipal> mPrincipal;
   AutoTArray<JS::Heap<JSObject*>, 2> mAnonymousGlobalScopes;
 
   // Returns true if this is a process message manager. There should only be a
   // single process message manager per session, so instances of this type will
   // optimize their script loading to avoid unnecessary duplication.
--- a/dom/base/nsNameSpaceManager.cpp
+++ b/dom/base/nsNameSpaceManager.cpp
@@ -25,17 +25,17 @@
 #include "mozilla/dom/Element.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static const char* kPrefSVGDisabled = "svg.disabled";
 static const char* kPrefMathMLDisabled = "mathml.disabled";
-static const char* kObservedPrefs[] = {
+static const char* kObservedNSPrefs[] = {
   kPrefMathMLDisabled,
   kPrefSVGDisabled,
   nullptr
 };
 StaticRefPtr<nsNameSpaceManager> nsNameSpaceManager::sInstance;
 
 /* static */ nsNameSpaceManager*
 nsNameSpaceManager::GetInstance() {
@@ -60,17 +60,17 @@ bool nsNameSpaceManager::Init()
   NS_ENSURE_SUCCESS(rv, false)
 
 #define REGISTER_DISABLED_NAMESPACE(uri, id) \
   rv = AddDisabledNameSpace(dont_AddRef(uri), id); \
   NS_ENSURE_SUCCESS(rv, false)
 
   mozilla::Preferences::RegisterCallbacks(
     PREF_CHANGE_METHOD(nsNameSpaceManager::PrefChanged),
-    kObservedPrefs, this);
+    kObservedNSPrefs, this);
 
   PrefChanged(nullptr);
 
   // Need to be ordered according to ID.
   MOZ_ASSERT(mURIArray.IsEmpty());
   REGISTER_NAMESPACE(nsGkAtoms::_empty, kNameSpaceID_None);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xmlns, kNameSpaceID_XMLNS);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xml, kNameSpaceID_XML);
--- a/dom/base/nsTextFragment.cpp
+++ b/dom/base/nsTextFragment.cpp
@@ -25,17 +25,17 @@
 #define TEXTFRAG_WHITE_AFTER_NEWLINE 50
 #define TEXTFRAG_MAX_NEWLINES 7
 
 // Static buffer used for common fragments
 static char* sSpaceSharedString[TEXTFRAG_MAX_NEWLINES + 1];
 static char* sTabSharedString[TEXTFRAG_MAX_NEWLINES + 1];
 static char sSingleCharSharedString[256];
 
-using mozilla::CheckedUint32;
+using namespace mozilla;
 
 // static
 nsresult
 nsTextFragment::Init()
 {
   // Create whitespace strings
   uint32_t i;
   for (i = 0; i <= TEXTFRAG_MAX_NEWLINES; ++i) {
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -11,22 +11,22 @@
 #include "mozilla/Assertions.h"
 #include "js/Id.h"          // must come before js/RootingAPI.h
 #include "js/Value.h"       // must come before js/RootingAPI.h
 #include "js/RootingAPI.h"
 #include "js/TracingAPI.h"
 
 namespace mozilla {
 namespace dom {
-class TabChildGlobal;
-class ProcessGlobal;
+class ContentProcessMessageManager;
+class InProcessTabChildMessageManager;
+class TabChildMessageManager;
 } // namespace dom
 } // namespace mozilla
 class SandboxPrivate;
-class nsInProcessTabChildGlobal;
 class nsWindowRoot;
 
 #define NS_WRAPPERCACHE_IID \
 { 0x6f3179a1, 0x36f7, 0x4a5c, \
   { 0x8c, 0xf1, 0xad, 0xc8, 0x7c, 0xde, 0x3e, 0x87 } }
 
 // There are two sets of flags used by DOM nodes. One comes from reusing the
 // remaining bits of the inherited nsWrapperCache flags (mFlags), and another is
--- a/dom/base/test/browser_messagemanager_loadprocessscript.js
+++ b/dom/base/test/browser_messagemanager_loadprocessscript.js
@@ -33,17 +33,17 @@ function processScript() {
   this.cpmm = Services.cpmm;
 
   addMessageListener("ProcessTest:Reply", function listener(msg) {
     removeMessageListener("ProcessTest:Reply", listener);
     sendAsyncMessage("ProcessTest:Finished");
   });
   sendSyncMessage("ProcessTest:Loaded");
 }
-var processScriptURL = "data:,(" + processScript.toString() + ")()";
+var processScriptURL = "data:,(" + processScript.toString() + ").call(this)";
 
 function initTestScript() {
   let init = initialProcessData;
   if (init.test123 != "hello") {
     dump("Initial data incorrect\n");
     return;
   }
 
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -150,20 +150,16 @@ DOMInterfaces = {
 'console': {
     'nativeType': 'mozilla::dom::Console',
 },
 
 'ConsoleInstance': {
     'implicitJSContext': ['clear', 'count', 'countReset', 'groupEnd', 'time', 'timeEnd'],
 },
 
-'ContentProcessMessageManager': {
-    'nativeType': 'mozilla::dom::ProcessGlobal'
-},
-
 'ConvolverNode': {
     'implicitJSContext': [ 'buffer' ],
 },
 
 'Coordinates': {
     'headerFile': 'nsGeoPosition.h'
 },
 
@@ -576,16 +572,25 @@ DOMInterfaces = {
     'nativeType': 'mozilla::dom::HTMLCanvasPrintState',
 },
 
 'MozChannel': {
     'nativeType': 'nsIChannel',
     'notflattened': True
 },
 
+'MozDocumentMatcher': {
+    'nativeType': 'mozilla::extensions::MozDocumentMatcher',
+    'headerFile': 'mozilla/extensions/WebExtensionContentScript.h',
+},
+
+'MozDocumentObserver': {
+    'nativeType': 'mozilla::extensions::DocumentObserver',
+},
+
 'MozSharedMap': {
     'nativeType': 'mozilla::dom::ipc::SharedMap',
 },
 
 'MozWritableSharedMap': {
     'headerFile': 'mozilla/dom/ipc/SharedMap.h',
     'nativeType': 'mozilla::dom::ipc::WritableSharedMap',
 },
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -198,19 +198,17 @@ CallbackObject::CallSetup::CallSetup(Cal
       globalObject = win;
     } else {
       // No DOM Window. Store the global.
       globalObject = xpc::NativeGlobal(realCallback);
       MOZ_ASSERT(globalObject);
     }
   }
 
-  // Bail out if there's no useful global. This seems to happen intermittently
-  // on gaia-ui tests, probably because nsInProcessTabChildGlobal is returning
-  // null in some kind of teardown state.
+  // Bail out if there's no useful global.
   if (!globalObject->GetGlobalJSObject()) {
     aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
       NS_LITERAL_CSTRING("Refusing to execute function from global which is "
                          "being torn down."));
     return;
   }
 
   mAutoEntryScript.emplace(globalObject, aExecutionReason, mIsMainThread);
--- a/dom/browser-element/BrowserElementChild.js
+++ b/dom/browser-element/BrowserElementChild.js
@@ -36,24 +36,24 @@ var BrowserElementIsReady;
 
 debug(`Might load BE scripts: BEIR: ${BrowserElementIsReady}`);
 if (!BrowserElementIsReady) {
   debug("Loading BE scripts")
   if (!("BrowserElementIsPreloaded" in this)) {
     if(Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
       // general content apps
       if (isTopBrowserElement(docShell)) {
-        Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementCopyPaste.js");
+        Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementCopyPaste.js", this);
       }
     } else {
       // rocketbar in system app and other in-process case (ex. B2G desktop client)
-      Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementCopyPaste.js");
+      Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementCopyPaste.js", this);
     }
 
-    Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementChildPreload.js");
+    Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementChildPreload.js", this);
   }
 
   function onDestroy() {
     removeMessageListener("browser-element-api:destroy", onDestroy);
 
     if (api) {
       api.destroy();
     }
--- a/dom/chrome-webidl/MessageManager.webidl
+++ b/dom/chrome-webidl/MessageManager.webidl
@@ -456,17 +456,17 @@ interface GlobalProcessScriptLoader : Pr
    * unique enough that other Gecko consumers won't accidentally choose it.
    */
   [Throws]
   readonly attribute any initialProcessData;
 
   readonly attribute MozWritableSharedMap sharedData;
 };
 
-[ChromeOnly, Global, NeedResolve]
+[ChromeOnly]
 interface ContentFrameMessageManager : EventTarget
 {
   /**
    * The current top level window in the frame or null.
    */
   [Throws]
   readonly attribute WindowProxy? content;
 
@@ -490,17 +490,17 @@ interface ContentFrameMessageManager : E
   readonly attribute long long chromeOuterWindowID;
 
 };
 // MessageManagerGlobal inherits from SyncMessageSender, which is a real interface, not a
 // mixin. This will need to change when we implement mixins according to the current
 // WebIDL spec.
 ContentFrameMessageManager implements MessageManagerGlobal;
 
-[ChromeOnly, Global, NeedResolve]
+[ChromeOnly]
 interface ContentProcessMessageManager
 {
   /**
    * Read out a copy of the object that was initialized in the parent
    * process via ProcessScriptLoader.initialProcessData.
    */
   [Throws]
   readonly attribute any initialProcessData;
new file mode 100644
--- /dev/null
+++ b/dom/chrome-webidl/MozDocumentObserver.webidl
@@ -0,0 +1,17 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+callback interface MozDocumentCallback {
+  void onNewDocument(MozDocumentMatcher matcher, WindowProxy window);
+  void onPreloadDocument(MozDocumentMatcher matcher, LoadInfo loadInfo);
+};
+
+[ChromeOnly, Constructor(MozDocumentCallback callbacks), Exposed=System]
+interface MozDocumentObserver {
+  [Throws]
+  void observe(sequence<MozDocumentMatcher> matchers);
+  void disconnect();
+};
--- a/dom/chrome-webidl/WebExtensionContentScript.webidl
+++ b/dom/chrome-webidl/WebExtensionContentScript.webidl
@@ -1,96 +1,53 @@
 /* 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/. */
 
 interface LoadInfo;
 interface URI;
 interface WindowProxy;
 
-/**
- * Describes the earliest point in the load cycle at which a script should
- * run.
- */
-enum ContentScriptRunAt {
-  /**
-   * The point in the load cycle just after the document element has been
-   * inserted, before any page scripts have been allowed to run.
-   */
-  "document_start",
-  /**
-   * The point after which the page DOM has fully loaded, but before all page
-   * resources have necessarily been loaded. Corresponds approximately to the
-   * DOMContentLoaded event.
-   */
-  "document_end",
-  /**
-   * The first point after the page and all of its resources has fully loaded
-   * when the event loop is idle, and can run scripts without delaying a paint
-   * event.
-   */
-  "document_idle",
-};
-
-[Constructor(WebExtensionPolicy extension, WebExtensionContentScriptInit options), ChromeOnly, Exposed=System]
-interface WebExtensionContentScript {
+[Constructor(MozDocumentMatcherInit options), ChromeOnly, Exposed=System]
+interface MozDocumentMatcher {
   /**
    * Returns true if the script's match and exclude patterns match the given
    * URI, without reference to attributes such as `allFrames`.
    */
   boolean matchesURI(URI uri);
 
   /**
-   * Returns true if the script matches the given URI and LoadInfo objects.
+   * Returns true if the the given URI and LoadInfo objects match.
    * This should be used to determine whether to begin pre-loading a content
    * script based on network events.
    */
   boolean matchesLoadInfo(URI uri, LoadInfo loadInfo);
 
   /**
-   * Returns true if the script matches the given window. This should be used
+   * Returns true if the given window matches. This should be used
    * to determine whether to run a script in a window at load time.
    */
   boolean matchesWindow(WindowProxy window);
 
   /**
-   * The policy object for the extension that this script belongs to.
-   */
-  [Constant]
-  readonly attribute WebExtensionPolicy extension;
-
-  /**
-   * If true, this script runs in all frames. If false, it only runs in
-   * top-level frames.
+   * If true, match all frames. If false, match only top-level frames.
    */
   [Constant]
   readonly attribute boolean allFrames;
 
   /**
    * If true, this (misleadingly-named, but inherited from Chrome) attribute
-   * causes the script to run in frames with URLs which inherit a principal
-   * that matches one of the match patterns, such as about:blank or
-   * about:srcdoc. If false, the script only runs in frames with an explicit
-   * matching URL.
+   * causes us to match frames with URLs which inherit a principal that
+   * matches one of the match patterns, such as about:blank or about:srcdoc.
+   * If false, we only match frames with an explicit matching URL.
    */
   [Constant]
   readonly attribute boolean matchAboutBlank;
 
   /**
-   * The earliest point in the load cycle at which this script should run. For
-   * static content scripts, in extensions which were present at browser
-   * startup, the browser makes every effort to make sure that the script runs
-   * no later than this point in the load cycle. For dynamic content scrip