Merge mozilla-inbound to mozilla-central. a=merge on a CLOSED TREE
authorAndreea Pavel <apavel@mozilla.com>
Wed, 28 Feb 2018 00:24:02 +0200
changeset 405575 5297541590781af40ff09e067646f3115960af75
parent 405574 81ff01f63004d466910c058e97121237dfc2f236 (current diff)
parent 405530 f361cfca3755aae660df9131540d3b469f9e29e6 (diff)
child 405576 49cb3c5cd110a4e3e7e339bb9b724ef777fe578e
child 405650 d8e3e0530ebc04c5265cdb44803f89f2534f272c
push id100248
push userapavel@mozilla.com
push dateTue, 27 Feb 2018 22:27:24 +0000
treeherdermozilla-inbound@49cb3c5cd110 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone60.0a1
first release with
nightly linux32
529754159078 / 60.0a1 / 20180228100110 / files
nightly linux64
529754159078 / 60.0a1 / 20180228100110 / files
nightly mac
529754159078 / 60.0a1 / 20180228100110 / files
nightly win32
529754159078 / 60.0a1 / 20180228100110 / files
nightly win64
529754159078 / 60.0a1 / 20180228100110 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central. a=merge on a CLOSED TREE
dom/base/nsContentUtils.cpp
gfx/thebes/dw-extra.h
--- a/accessible/tests/mochitest/elm/test_shadowroot_subframe.html
+++ b/accessible/tests/mochitest/elm/test_shadowroot_subframe.html
@@ -32,12 +32,19 @@
 
 </head>
 <body>
   <div role="group" id="component"></div>
   <script>
     var component = document.getElementById("component");
     var shadow = component.attachShadow({mode: "open"});
 
-    shadow.innerHTML = "<button>Hello</button>" +
-      '<a href="#"> World</a>';
+    var button = document.createElement("button");
+    button.append("Hello");
+
+    var a = document.createElement("a");
+    a.setAttribute("href", "#");
+    a.append(" World");
+
+    shadow.appendChild(button);
+    shadow.appendChild(a);
   </script>
 </body>
--- a/browser/base/content/newtab/grid.js
+++ b/browser/base/content/newtab/grid.js
@@ -174,28 +174,47 @@ var gGrid = {
   /**
    * Creates the DOM fragment that is re-used when creating sites.
    */
   _createSiteFragment: function Grid_createSiteFragment() {
     let site = document.createElementNS(HTML_NAMESPACE, "div");
     site.classList.add("newtab-site");
     site.setAttribute("draggable", "true");
 
-    // Create the site's inner HTML code.
-    site.innerHTML =
-      '<a class="newtab-link">' +
-      '  <span class="newtab-thumbnail placeholder"/>' +
-      '  <img src="" alt="" class="newtab-thumbnail thumbnail"/>' +
-      '  <img src="" alt="" class="newtab-thumbnail enhanced-content"/>' +
-      '  <span class="newtab-title"/>' +
-      '</a>' +
-      '<input type="button" title="' + newTabString("pin") + '"' +
-      '       class="newtab-control newtab-control-pin"/>' +
-      '<input type="button" title="' + newTabString("block") + '"' +
-      '       class="newtab-control newtab-control-block"/>';
+    let link = document.createElement("a");
+    link.className = "newtab-link";
+    site.appendChild(link);
+
+    let thumbnailPlaceHolder = document.createElement("span");
+    thumbnailPlaceHolder.className = "newtab-thumbnail placeholder";
+    link.appendChild(thumbnailPlaceHolder);
+
+    let thumbnail = document.createElement("img");
+    thumbnail.className = "newtab-thumbnail thumbnail";
+    link.appendChild(thumbnail);
+
+    let enhancedContent = document.createElement("img");
+    enhancedContent.className = "newtab-thumbnail enhanced-content";
+    link.appendChild(enhancedContent);
+
+    let title = document.createElement("span");
+    title.className = "newtab-title";
+    link.appendChild(title);
+
+    let pinButton = document.createElement("input");
+    pinButton.type = "button";
+    pinButton.title = newTabString("pin");
+    pinButton.className = "newtab-control newtab-control-pin";
+    site.appendChild(pinButton);
+
+    let removeButton = document.createElement("input");
+    removeButton.type = "button";
+    removeButton.title = newTabString("block");
+    removeButton.className = "newtab-control newtab-control-block";
+    site.appendChild(removeButton);
 
     this._siteFragment = document.createDocumentFragment();
     this._siteFragment.appendChild(site);
   },
 
   /**
    * Test a tile at a given position for being pinned or history
    * @param position Position in sites array
--- a/browser/base/content/test/general/contentSearchUI.js
+++ b/browser/base/content/test/general/contentSearchUI.js
@@ -163,18 +163,17 @@ function waitForSuggestions(cb) {
   });
   observer.observe(gController.input, {
     attributes: true,
     attributeFilter: ["aria-expanded"],
   });
 }
 
 function waitForContentSearchEvent(messageType, cb) {
-  let mm = content.SpecialPowers.Cc["@mozilla.org/globalmessagemanager;1"].
-    getService(content.SpecialPowers.Ci.nsIMessageListenerManager);
+  let mm = content.SpecialPowers.Cc["@mozilla.org/globalmessagemanager;1"].getService();
   mm.addMessageListener("ContentSearch", function listener(aMsg) {
     if (aMsg.data.type != messageType) {
       return;
     }
     mm.removeMessageListener("ContentSearch", listener);
     cb(aMsg.data.data);
   });
 }
--- a/browser/base/content/test/pageinfo/browser_pageInfo.js
+++ b/browser/base/content/test/pageinfo/browser_pageInfo.js
@@ -1,19 +1,22 @@
+const URI = "https://example.com/browser/browser/base/content/test/pageinfo/feed_tab.html";
+
 function test() {
   waitForExplicitFinish();
 
   var pageInfo;
 
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
-  BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+  BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false,
+                                 URI).then(() => {
     Services.obs.addObserver(observer, "page-info-dialog-loaded");
     pageInfo = BrowserPageInfo();
   });
-  gBrowser.selectedBrowser.loadURI("https://example.com/browser/browser/base/content/test/pageinfo/feed_tab.html");
+  gBrowser.selectedBrowser.loadURI(URI);
 
   function observer(win, topic, data) {
     Services.obs.removeObserver(observer, "page-info-dialog-loaded");
     pageInfo.onFinished.push(handlePageInfo);
   }
 
   function handlePageInfo() {
     ok(pageInfo.document.getElementById("feedTab"), "Feed tab");
--- a/browser/base/content/test/pageinfo/browser_pageinfo_firstPartyIsolation.js
+++ b/browser/base/content/test/pageinfo/browser_pageinfo_firstPartyIsolation.js
@@ -68,18 +68,19 @@ async function test() {
 
   Services.prefs.setBoolPref("privacy.firstparty.isolate", true);
   registerCleanupFunction(function() {
     Services.prefs.clearUserPref("privacy.firstparty.isolate");
   });
 
   let url = "https://example.com/browser/browser/base/content/test/pageinfo/image.html";
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
+  let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, url);
   gBrowser.selectedBrowser.loadURI(url);
-  await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, url);
+  await loadPromise;
 
   // Pass a dummy imageElement, if there isn't an imageElement, pageInfo.js
   // will do a preview, however this sometimes will cause intermittent failures,
   // see bug 1403365.
   let pageInfo = BrowserPageInfo(url, "mediaTab", {});
   info("waitForEvent pageInfo");
   await waitForEvent(pageInfo, "load");
 
--- a/browser/base/content/test/pageinfo/browser_pageinfo_image_info.js
+++ b/browser/base/content/test/pageinfo/browser_pageinfo_image_info.js
@@ -3,22 +3,33 @@ function getImageInfo(imageElement) {
   return {
     currentSrc: imageElement.currentSrc,
     width: imageElement.width,
     height: imageElement.height,
     imageText: imageElement.title || imageElement.alt
   };
 }
 
+const URI =
+  "data:text/html," +
+  "<style type='text/css'>%23test-image,%23not-test-image {background-image: url('about:logo?c');}</style>" +
+  "<img src='about:logo?b' height=300 width=350 alt=2 id='not-test-image'>" +
+  "<img src='about:logo?b' height=300 width=350 alt=2>" +
+  "<img src='about:logo?a' height=200 width=250>" +
+  "<img src='about:logo?b' height=200 width=250 alt=1>" +
+  "<img src='about:logo?b' height=100 width=150 alt=2 id='test-image'>";
+
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
 
-  BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(function() {
+  let uriToWaitFor = URI.replace(/ /g, "%20");
+  BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false,
+                                 uriToWaitFor).then(function() {
     // eslint-disable-next-line mozilla/no-cpows-in-tests
     var doc = gBrowser.contentDocumentAsCPOW;
     var testImg = doc.getElementById("test-image");
     var pageInfo = BrowserPageInfo(gBrowser.selectedBrowser.currentURI.spec,
                                    "mediaTab", getImageInfo(testImg));
 
     pageInfo.addEventListener("load", function() {
       pageInfo.onFinished.push(function() {
@@ -32,17 +43,10 @@ function test() {
           pageInfo.close();
           gBrowser.removeCurrentTab();
           finish();
         });
       });
     }, {capture: true, once: true});
   });
 
-  gBrowser.loadURI(
-    "data:text/html," +
-    "<style type='text/css'>%23test-image,%23not-test-image {background-image: url('about:logo?c');}</style>" +
-    "<img src='about:logo?b' height=300 width=350 alt=2 id='not-test-image'>" +
-    "<img src='about:logo?b' height=300 width=350 alt=2>" +
-    "<img src='about:logo?a' height=200 width=250>" +
-    "<img src='about:logo?b' height=200 width=250 alt=1>" +
-    "<img src='about:logo?b' height=100 width=150 alt=2 id='test-image'>");
+  gBrowser.loadURI(URI);
 }
--- a/browser/base/content/test/pageinfo/browser_pageinfo_svg_image.js
+++ b/browser/base/content/test/pageinfo/browser_pageinfo_svg_image.js
@@ -1,14 +1,17 @@
+const URI = "https://example.com/browser/browser/base/content/test/pageinfo/svg_image.html";
+
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
 
-  BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(() => {
+  BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false,
+                                 URI).then(() => {
     var pageInfo = BrowserPageInfo(gBrowser.selectedBrowser.currentURI.spec,
                                    "mediaTab");
 
     pageInfo.addEventListener("load", function() {
       pageInfo.onFinished.push(function() {
         executeSoon(function() {
           var imageTree = pageInfo.document.getElementById("imagetree");
           var imageRowsNum = imageTree.view.rowCount;
@@ -27,11 +30,10 @@ function test() {
           pageInfo.close();
           gBrowser.removeCurrentTab();
           finish();
         });
       });
     }, {capture: true, once: true});
   });
 
-  gBrowser.selectedBrowser.loadURI(
-    "https://example.com/browser/browser/base/content/test/pageinfo/svg_image.html");
+  gBrowser.selectedBrowser.loadURI(URI);
 }
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
@@ -37,20 +37,17 @@ var gTests = [
     ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
     is(webrtcUI.getActiveStreams(false, true).length, 1, "1 active audio stream");
     is(webrtcUI.getActiveStreams(true, true, true).length, 1, "1 active stream");
 
     await expectNoObserverCalled();
 
     // If we have reached the max process count already, increase it to ensure
     // our new tab can have its own content process.
-    var ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
-                 .getService(Ci.nsIMessageBroadcaster);
-    ppmm.QueryInterface(Ci.nsIProcessScriptLoader);
-    let childCount = ppmm.childCount;
+    let childCount = Services.ppmm.childCount;
     let maxContentProcess = Services.prefs.getIntPref("dom.ipc.processCount");
     // The first check is because if we are on a branch where e10s-multi is
     // disabled, we want to keep testing e10s with a single content process.
     // The + 1 is because ppmm.childCount also counts the chrome process
     // (which also runs process scripts).
     if (maxContentProcess > 1 && childCount == maxContentProcess + 1) {
       await SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount",
                                                 childCount]]});
@@ -141,20 +138,17 @@ var gTests = [
     ok(!webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator hidden");
     is(webrtcUI.getActiveStreams(true).length, 1, "1 active camera stream");
     is(webrtcUI.getActiveStreams(true, true, true).length, 1, "1 active stream");
 
     await expectNoObserverCalled();
 
     // If we have reached the max process count already, increase it to ensure
     // our new tab can have its own content process.
-    var ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
-                 .getService(Ci.nsIMessageBroadcaster);
-    ppmm.QueryInterface(Ci.nsIProcessScriptLoader);
-    let childCount = ppmm.childCount;
+    let childCount = Services.ppmm.childCount;
     let maxContentProcess = Services.prefs.getIntPref("dom.ipc.processCount");
     // The first check is because if we are on a branch where e10s-multi is
     // disabled, we want to keep testing e10s with a single content process.
     // The + 1 is because ppmm.childCount also counts the chrome process
     // (which also runs process scripts).
     if (maxContentProcess > 1 && childCount == maxContentProcess + 1) {
       await SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount",
                                                 childCount]]});
@@ -250,20 +244,17 @@ var gTests = [
     ok(!webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator hidden");
     is(webrtcUI.getActiveStreams(false, false, true).length, 1, "1 active screen sharing stream");
     is(webrtcUI.getActiveStreams(true, true, true).length, 1, "1 active stream");
 
     await expectNoObserverCalled();
 
     // If we have reached the max process count already, increase it to ensure
     // our new tab can have its own content process.
-    var ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
-                 .getService(Ci.nsIMessageBroadcaster);
-    ppmm.QueryInterface(Ci.nsIProcessScriptLoader);
-    let childCount = ppmm.childCount;
+    let childCount = Services.ppmm.childCount;
     let maxContentProcess = Services.prefs.getIntPref("dom.ipc.processCount");
     // The first check is because if we are on a branch where e10s-multi is
     // disabled, we want to keep testing e10s with a single content process.
     // The + 1 is because ppmm.childCount also counts the chrome process
     // (which also runs process scripts).
     if (maxContentProcess > 1 && childCount == maxContentProcess + 1) {
       await SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount",
                                                 childCount]]});
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
@@ -14,22 +14,16 @@ add_task(async function testExecuteScrip
     // count them in the test because MessageChannel unsubscribes when the
     // message manager closes, which never happens to these, of course.
     let globalMMs = [
       Services.mm,
       Services.ppmm,
       Services.ppmm.getChildAt(0),
     ];
     for (let mm of messageManagerMap.keys()) {
-      // Sanity check: mm is a message manager.
-      try {
-        mm.QueryInterface(Ci.nsIMessageSender);
-      } catch (e) {
-        mm.QueryInterface(Ci.nsIMessageBroadcaster);
-      }
       if (!globalMMs.includes(mm)) {
         ++count;
       }
     }
     return count;
   }
 
   let messageManagersSize = countMM(MessageChannel.messageManagers);
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -3183,19 +3183,18 @@ var JawsScreenReaderVersionCheck = {
 };
 
 var components = [BrowserGlue, ContentPermissionPrompt];
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
 
 // Listen for UITour messages.
 // Do it here instead of the UITour module itself so that the UITour module is lazy loaded
 // when the first message is received.
-var globalMM = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
-globalMM.addMessageListener("UITour:onPageEvent", function(aMessage) {
+Services.mm.addMessageListener("UITour:onPageEvent", function(aMessage) {
   UITour.onPageEvent(aMessage, aMessage.data);
 });
 
 // Listen for HybridContentTelemetry messages.
 // Do it here instead of HybridContentTelemetry.init() so that
 // the module can be lazily loaded on the first message.
-globalMM.addMessageListener("HybridContentTelemetry:onTelemetryMessage", aMessage => {
+Services.mm.addMessageListener("HybridContentTelemetry:onTelemetryMessage", aMessage => {
   HybridContentTelemetry.onTelemetryMessage(aMessage, aMessage.data);
 });
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -10,26 +10,23 @@ const TAB_STATE_RESTORING = 2;
 
 const ROOT = getRootDirectory(gTestPath);
 const HTTPROOT = ROOT.replace("chrome://mochitests/content/", "http://example.com/");
 const FRAME_SCRIPTS = [
   ROOT + "content.js",
   ROOT + "content-forms.js"
 ];
 
-var globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
-           .getService(Ci.nsIMessageListenerManager);
-
 for (let script of FRAME_SCRIPTS) {
-  globalMM.loadFrameScript(script, true);
+  Services.mm.loadFrameScript(script, true);
 }
 
 registerCleanupFunction(() => {
   for (let script of FRAME_SCRIPTS) {
-    globalMM.removeDelayedFrameScript(script, true);
+    Services.mm.removeDelayedFrameScript(script, true);
   }
 });
 
 const {SessionStore} = ChromeUtils.import("resource:///modules/sessionstore/SessionStore.jsm", {});
 const {SessionSaver} = ChromeUtils.import("resource:///modules/sessionstore/SessionSaver.jsm", {});
 const {SessionFile} = ChromeUtils.import("resource:///modules/sessionstore/SessionFile.jsm", {});
 const {TabState} = ChromeUtils.import("resource:///modules/sessionstore/TabState.jsm", {});
 const {TabStateFlusher} = ChromeUtils.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
--- a/browser/extensions/onboarding/content/onboarding.js
+++ b/browser/extensions/onboarding/content/onboarding.js
@@ -21,16 +21,72 @@ const PROMPT_COUNT_PREF = "browser.onboa
 const NOTIFICATION_FINISHED_PREF = "browser.onboarding.notification.finished";
 const ONBOARDING_DIALOG_ID = "onboarding-overlay-dialog";
 const ONBOARDING_MIN_WIDTH_PX = 960;
 const SPEECH_BUBBLE_MIN_WIDTH_PX = 1130;
 const SPEECH_BUBBLE_NEWTOUR_STRING_ID = "onboarding.overlay-icon-tooltip2";
 const SPEECH_BUBBLE_UPDATETOUR_STRING_ID = "onboarding.overlay-icon-tooltip-updated2";
 const ICON_STATE_WATERMARK = "watermark";
 const ICON_STATE_DEFAULT = "default";
+
+/**
+ * Helper function to create the tour description UI element.
+ */
+function createOnboardingTourDescription(div, title, description) {
+  let doc = div.ownerDocument;
+  let section = doc.createElement("section");
+  section.className = "onboarding-tour-description";
+
+  let h1 = doc.createElement("h1");
+  h1.setAttribute("data-l10n-id", title);
+  section.appendChild(h1);
+
+  let p = doc.createElement("p");
+  p.setAttribute("data-l10n-id", description);
+  section.appendChild(p);
+
+  div.appendChild(section);
+  return section;
+}
+
+/**
+ * Helper function to create the tour content UI element.
+ */
+function createOnboardingTourContent(div, imageSrc) {
+  let doc = div.ownerDocument;
+  let section = doc.createElement("section");
+  section.className = "onboarding-tour-content";
+
+  let img = doc.createElement("img");
+  img.setAttribute("src", imageSrc);
+  img.setAttribute("role", "presentation");
+  section.appendChild(img);
+
+  div.appendChild(section);
+  return section;
+}
+
+/**
+ * Helper function to create the tour button UI element.
+ */
+function createOnboardingTourButton(div, buttonId, l10nId) {
+  let doc = div.ownerDocument;
+  let aside = doc.createElement("aside");
+  aside.className = "onboarding-tour-button-container";
+
+  let button = doc.createElement("button");
+  button.id = buttonId;
+  button.className = "onboarding-tour-action-button";
+  button.setAttribute("data-l10n-id", l10nId);
+  aside.appendChild(button);
+
+  div.appendChild(aside);
+  return aside;
+}
+
 /**
  * Add any number of tours, key is the tourId, value should follow the format below
  * "tourId": { // The short tour id which could be saved in pref
  *   // The unique tour id
  *   id: "onboarding-tour-addons",
  *   // (optional) mark tour as complete instantly when the user enters the tour
  *   instantComplete: false,
  *   // The string id of tour name which would be displayed on the navigation bar
@@ -55,82 +111,67 @@ var onboardingTourset = {
       return {
         title: bundle.GetStringFromName("onboarding.notification.onboarding-tour-private-browsing.title"),
         message: bundle.GetStringFromName("onboarding.notification.onboarding-tour-private-browsing.message2"),
         button: bundle.GetStringFromName("onboarding.button.learnMore"),
       };
     },
     getPage(win) {
       let div = win.document.createElement("div");
-      div.innerHTML = `
-        <section class="onboarding-tour-description">
-          <h1 data-l10n-id="onboarding.tour-private-browsing.title2"></h1>
-          <p data-l10n-id="onboarding.tour-private-browsing.description3"></p>
-        </section>
-        <section class="onboarding-tour-content">
-          <img src="resource://onboarding/img/figure_private.svg" role="presentation"/>
-        </section>
-        <aside class="onboarding-tour-button-container">
-          <button id="onboarding-tour-private-browsing-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-private-browsing.button"></button>
-        </aside>
-      `;
+
+      createOnboardingTourDescription(div,
+        "onboarding.tour-private-browsing.title2", "onboarding.tour-private-browsing.description3");
+      createOnboardingTourContent(div, "resource://onboarding/img/figure_private.svg");
+      createOnboardingTourButton(div,
+        "onboarding-tour-private-browsing-button", "onboarding.tour-private-browsing.button");
+
       return div;
     },
   },
   "addons": {
     id: "onboarding-tour-addons",
     tourNameId: "onboarding.tour-addons",
     getNotificationStrings(bundle) {
       return {
         title: bundle.GetStringFromName("onboarding.notification.onboarding-tour-addons.title"),
         message: bundle.formatStringFromName("onboarding.notification.onboarding-tour-addons.message", [BRAND_SHORT_NAME], 1),
         button: bundle.GetStringFromName("onboarding.button.learnMore"),
       };
     },
     getPage(win) {
       let div = win.document.createElement("div");
-      div.innerHTML = `
-        <section class="onboarding-tour-description">
-          <h1 data-l10n-id="onboarding.tour-addons.title2"></h1>
-          <p data-l10n-id="onboarding.tour-addons.description2"></p>
-        </section>
-        <section class="onboarding-tour-content">
-          <img src="resource://onboarding/img/figure_addons.svg" role="presentation"/>
-        </section>
-        <aside class="onboarding-tour-button-container">
-          <button id="onboarding-tour-addons-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-addons.button"></button>
-        </aside>
-      `;
+
+      createOnboardingTourDescription(div,
+        "onboarding.tour-addons.title2", "onboarding.tour-addons.description2");
+      createOnboardingTourContent(div, "resource://onboarding/img/figure_addons.svg");
+      createOnboardingTourButton(div,
+        "onboarding-tour-addons-button", "onboarding.tour-addons.button");
+
       return div;
     },
   },
   "customize": {
     id: "onboarding-tour-customize",
     tourNameId: "onboarding.tour-customize",
     getNotificationStrings(bundle) {
       return {
         title: bundle.GetStringFromName("onboarding.notification.onboarding-tour-customize.title"),
         message: bundle.formatStringFromName("onboarding.notification.onboarding-tour-customize.message", [BRAND_SHORT_NAME], 1),
         button: bundle.GetStringFromName("onboarding.button.learnMore"),
       };
     },
     getPage(win) {
       let div = win.document.createElement("div");
-      div.innerHTML = `
-        <section class="onboarding-tour-description">
-          <h1 data-l10n-id="onboarding.tour-customize.title2"></h1>
-          <p data-l10n-id="onboarding.tour-customize.description2"></p>
-        </section>
-        <section class="onboarding-tour-content">
-          <img src="resource://onboarding/img/figure_customize.svg" role="presentation"/>
-        </section>
-        <aside class="onboarding-tour-button-container">
-          <button id="onboarding-tour-customize-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-customize.button"></button>
-        </aside>
-      `;
+
+      createOnboardingTourDescription(div,
+        "onboarding.tour-customize.title2", "onboarding.tour-customize.description2");
+      createOnboardingTourContent(div, "resource://onboarding/img/figure_customize.svg");
+      createOnboardingTourButton(div,
+        "onboarding-tour-customize-button", "onboarding.tour-customize.button");
+
       return div;
     },
   },
   "default": {
     id: "onboarding-tour-default-browser",
     instantComplete: true,
     tourNameId: "onboarding.tour-default-browser",
     getNotificationStrings(bundle) {
@@ -141,31 +182,41 @@ var onboardingTourset = {
       };
     },
     getPage(win, bundle) {
       let div = win.document.createElement("div");
       let setFromBackGround = bundle.formatStringFromName("onboarding.tour-default-browser.win7.button", [BRAND_SHORT_NAME], 1);
       let setFromPanel = bundle.GetStringFromName("onboarding.tour-default-browser.button");
       let isDefaultMessage = bundle.GetStringFromName("onboarding.tour-default-browser.is-default.message");
       let isDefault2ndMessage = bundle.formatStringFromName("onboarding.tour-default-browser.is-default.2nd-message", [BRAND_SHORT_NAME], 1);
-      // eslint-disable-next-line no-unsanitized/property
-      div.innerHTML = `
-        <section class="onboarding-tour-description">
-          <h1 data-l10n-id="onboarding.tour-default-browser.title2"></h1>
-          <p data-l10n-id="onboarding.tour-default-browser.description2"></p>
-        </section>
-        <section class="onboarding-tour-content">
-          <img src="resource://onboarding/img/figure_default.svg" role="presentation"/>
-        </section>
-        <aside class="onboarding-tour-button-container">
-          <button id="onboarding-tour-default-browser-button" class="onboarding-tour-action-button"
-            data-bg="${setFromBackGround}" data-panel="${setFromPanel}"></button>
-          <div id="onboarding-tour-is-default-browser-msg" class="onboarding-hidden">${isDefaultMessage}<br/>${isDefault2ndMessage}</div>
-        </aside>
-      `;
+
+      createOnboardingTourDescription(div,
+        "onboarding.tour-default-browser.title2", "onboarding.tour-default-browser.description2");
+      createOnboardingTourContent(div, "resource://onboarding/img/figure_default.svg");
+
+      let aside = win.document.createElement("aside");
+      aside.className = "onboarding-tour-button-container";
+      div.appendChild(aside);
+
+      let button = win.document.createElement("button");
+      button.id = "onboarding-tour-default-browser-button";
+      button.className = "onboarding-tour-action-button";
+      button.setAttribute("data-bg", setFromBackGround);
+      button.setAttribute("data-panel", setFromPanel);
+      aside.appendChild(button);
+
+      let isDefaultBrowserMsg = win.document.createElement("div");
+      isDefaultBrowserMsg.id = "onboarding-tour-is-default-browser-msg";
+      isDefaultBrowserMsg.className = "onboarding-hidden";
+      aside.appendChild(isDefaultBrowserMsg);
+      isDefaultBrowserMsg.append(isDefaultMessage);
+
+      let br = win.document.createElement("br");
+      isDefaultBrowserMsg.appendChild(br);
+      isDefaultBrowserMsg.append(isDefault2ndMessage);
 
       div.addEventListener("beforeshow", () => {
         win.document.dispatchEvent(new Event("Agent:CanSetDefaultBrowserInBackground"));
       });
       return div;
     },
   },
   "sync": {
@@ -184,40 +235,81 @@ var onboardingTourset = {
       const STATE_LOGIN = "logged-in";
       let div = win.document.createElement("div");
       div.dataset.loginState = STATE_LOGOUT;
       // The email validation pattern used in the form comes from IETF rfc5321,
       // which is identical to server-side checker of Firefox Account. See
       // discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1378770#c6
       // for detail.
       let emailRegex = "^[\\w.!#$%&’*+\\/=?^`{|}~-]{1,64}@[a-z\\d](?:[a-z\\d-]{0,253}[a-z\\d])?(?:\\.[a-z\\d](?:[a-z\\d-]{0,253}[a-z\\d])?)+$";
-      div.innerHTML = `
-        <section class="onboarding-tour-description">
-          <h1 data-l10n-id="onboarding.tour-sync.title2" class="show-on-logged-out"></h1>
-          <p data-l10n-id="onboarding.tour-sync.description2" class="show-on-logged-out"></p>
-          <h1 data-l10n-id="onboarding.tour-sync.logged-in.title" class="show-on-logged-in"></h1>
-          <p data-l10n-id="onboarding.tour-sync.logged-in.description" class="show-on-logged-in"></p>
-        </section>
-        <section class="onboarding-tour-content">
-          <form class="show-on-logged-out">
-            <h3 data-l10n-id="onboarding.tour-sync.form.title"></h3>
-            <p data-l10n-id="onboarding.tour-sync.form.description"></p>
-            <input id="onboarding-tour-sync-email-input" type="email" required="true"></input><br />
-            <button id="onboarding-tour-sync-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-sync.button"></button>
-          </form>
-          <img src="resource://onboarding/img/figure_sync.svg" role="presentation"/>
-        </section>
-        <aside class="onboarding-tour-button-container show-on-logged-in">
-          <button id="onboarding-tour-sync-connect-device-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-sync.connect-device.button"></button>
-        </aside>
-      `;
-      let emailInput = div.querySelector("#onboarding-tour-sync-email-input");
-      emailInput.placeholder =
+
+      let description = createOnboardingTourDescription(div,
+        "onboarding.tour-sync.title2", "onboarding.tour-sync.description2");
+
+      description.querySelector("h1").className = "show-on-logged-out";
+      description.querySelector("p").className = "show-on-logged-out";
+
+      let h1LoggedIn = win.document.createElement("h1");
+      h1LoggedIn.setAttribute("data-l10n-id", "onboarding.tour-sync.logged-in.title");
+      h1LoggedIn.className = "show-on-logged-in";
+      description.appendChild(h1LoggedIn);
+
+      let pLoggedIn = win.document.createElement("p");
+      pLoggedIn.setAttribute("data-l10n-id", "onboarding.tour-sync.logged-in.description");
+      pLoggedIn.className = "show-on-logged-in";
+      description.appendChild(pLoggedIn);
+
+      let content = win.document.createElement("section");
+      content.className = "onboarding-tour-content";
+      div.appendChild(content);
+
+      let form = win.document.createElement("form");
+      form.className = "show-on-logged-out";
+      content.appendChild(form);
+
+      let h3 = win.document.createElement("h3");
+      h3.setAttribute("data-l10n-id", "onboarding.tour-sync.form.title");
+      form.appendChild(h3);
+
+      let p = win.document.createElement("p");
+      p.setAttribute("data-l10n-id", "onboarding.tour-sync.form.description");
+      form.appendChild(p);
+
+      let input = win.document.createElement("input");
+      input.id = "onboarding-tour-sync-email-input";
+      input.setAttribute("required", "true");
+      input.setAttribute("type", "email");
+      input.placeholder =
         bundle.GetStringFromName("onboarding.tour-sync.email-input.placeholder");
-      emailInput.pattern = emailRegex;
+      input.pattern = emailRegex;
+      form.appendChild(input);
+
+      let br = win.document.createElement("br");
+      form.appendChild(br);
+
+      let button = win.document.createElement("button");
+      button.id = "onboarding-tour-sync-button";
+      button.className = "onboarding-tour-action-button";
+      button.setAttribute("data-l10n-id", "onboarding.tour-sync.button");
+      form.appendChild(button);
+
+      let img = win.document.createElement("img");
+      img.setAttribute("src", "resource://onboarding/img/figure_sync.svg");
+      img.setAttribute("role", "presentation");
+      content.appendChild(img);
+
+      let aside = win.document.createElement("aside");
+      aside.className = "onboarding-tour-button-container show-on-logged-in";
+      div.appendChild(aside);
+
+      let connectDeviceButton = win.document.createElement("button");
+      connectDeviceButton.id = "onboarding-tour-sync-connect-device-button";
+      connectDeviceButton.className = "onboarding-tour-action-button";
+      connectDeviceButton.setAttribute("data-l10n-id", "onboarding.tour-sync.connect-device.button");
+      aside.appendChild(connectDeviceButton);
 
       div.addEventListener("beforeshow", () => {
         function loginStatusListener(msg) {
           removeMessageListener("Onboarding:ResponseLoginStatus", loginStatusListener);
           div.dataset.loginState = msg.data.isLoggedIn ? STATE_LOGIN : STATE_LOGOUT;
         }
         sendMessageToChrome("get-login-status");
         addMessageListener("Onboarding:ResponseLoginStatus", loginStatusListener);
@@ -233,80 +325,66 @@ var onboardingTourset = {
       return {
         title: bundle.GetStringFromName("onboarding.notification.onboarding-tour-library.title"),
         message: bundle.formatStringFromName("onboarding.notification.onboarding-tour-library.message", [BRAND_SHORT_NAME], 1),
         button: bundle.GetStringFromName("onboarding.button.learnMore"),
       };
     },
     getPage(win) {
       let div = win.document.createElement("div");
-      div.innerHTML = `
-        <section class="onboarding-tour-description">
-          <h1 data-l10n-id="onboarding.tour-library.title"></h1>
-          <p data-l10n-id="onboarding.tour-library.description2"></p>
-        </section>
-        <section class="onboarding-tour-content">
-          <img src="resource://onboarding/img/figure_library.svg" role="presentation"/>
-        </section>
-        <aside class="onboarding-tour-button-container">
-          <button id="onboarding-tour-library-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-library.button2"></button>
-        </aside>
-      `;
+
+      createOnboardingTourDescription(div,
+        "onboarding.tour-library.title", "onboarding.tour-library.description2");
+      createOnboardingTourContent(div, "resource://onboarding/img/figure_library.svg");
+      createOnboardingTourButton(div,
+        "onboarding-tour-library-button", "onboarding.tour-library.button2");
+
       return div;
     },
   },
   "singlesearch": {
     id: "onboarding-tour-singlesearch",
     tourNameId: "onboarding.tour-singlesearch",
     getNotificationStrings(bundle) {
       return {
         title: bundle.GetStringFromName("onboarding.notification.onboarding-tour-singlesearch.title"),
         message: bundle.GetStringFromName("onboarding.notification.onboarding-tour-singlesearch.message"),
         button: bundle.GetStringFromName("onboarding.button.learnMore"),
       };
     },
     getPage(win, bundle) {
       let div = win.document.createElement("div");
-      div.innerHTML = `
-        <section class="onboarding-tour-description">
-          <h1 data-l10n-id="onboarding.tour-singlesearch.title"></h1>
-          <p data-l10n-id="onboarding.tour-singlesearch.description"></p>
-        </section>
-        <section class="onboarding-tour-content">
-          <img src="resource://onboarding/img/figure_singlesearch.svg" role="presentation"/>
-        </section>
-        <aside class="onboarding-tour-button-container">
-          <button id="onboarding-tour-singlesearch-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-singlesearch.button"></button>
-        </aside>
-      `;
+
+      createOnboardingTourDescription(div,
+        "onboarding.tour-singlesearch.title", "onboarding.tour-singlesearch.description");
+      createOnboardingTourContent(div, "resource://onboarding/img/figure_singlesearch.svg");
+      createOnboardingTourButton(div,
+        "onboarding-tour-singlesearch-button", "onboarding.tour-singlesearch.button");
+
       return div;
     },
   },
   "performance": {
     id: "onboarding-tour-performance",
     instantComplete: true,
     tourNameId: "onboarding.tour-performance",
     getNotificationStrings(bundle) {
       return {
         title: bundle.GetStringFromName("onboarding.notification.onboarding-tour-performance.title"),
         message: bundle.formatStringFromName("onboarding.notification.onboarding-tour-performance.message", [BRAND_SHORT_NAME], 1),
         button: bundle.GetStringFromName("onboarding.button.learnMore"),
       };
     },
     getPage(win, bundle) {
       let div = win.document.createElement("div");
-      div.innerHTML = `
-        <section class="onboarding-tour-description">
-          <h1 data-l10n-id="onboarding.tour-performance.title"></h1>
-          <p data-l10n-id="onboarding.tour-performance.description"></p>
-        </section>
-        <section class="onboarding-tour-content">
-          <img src="resource://onboarding/img/figure_performance.svg" role="presentation"/>
-        </section>
-      `;
+
+      createOnboardingTourDescription(div,
+        "onboarding.tour-performance.title", "onboarding.tour-performance.description");
+      createOnboardingTourContent(div, "resource://onboarding/img/figure_performance.svg");
+
       return div;
     },
   },
   "screenshots": {
     id: "onboarding-tour-screenshots",
     tourNameId: "onboarding.tour-screenshots",
     getNotificationStrings(bundle) {
       return {
@@ -314,29 +392,28 @@ var onboardingTourset = {
         message: bundle.formatStringFromName("onboarding.notification.onboarding-tour-screenshots.message", [BRAND_SHORT_NAME], 1),
         button: bundle.GetStringFromName("onboarding.button.learnMore"),
       };
     },
     getPage(win, bundle) {
       let div = win.document.createElement("div");
       // Screenshot tour opens the screenshot page directly, see below a#onboarding-tour-screenshots-button.
       // The screenshots page should be responsible for highlighting the Screenshots button
-      div.innerHTML = `
-        <section class="onboarding-tour-description">
-          <h1 data-l10n-id="onboarding.tour-screenshots.title"></h1>
-          <p data-l10n-id="onboarding.tour-screenshots.description"></p>
-        </section>
-        <section class="onboarding-tour-content">
-          <img src="resource://onboarding/img/figure_screenshots.svg" role="presentation"/>
-        </section>
-        <aside class="onboarding-tour-button-container">
-          <a id="onboarding-tour-screenshots-button" class="onboarding-tour-action-button" data-l10n-id="onboarding.tour-screenshots.button"
-             href="https://screenshots.firefox.com/#tour" target="_blank"></a>
-        </aside>
-      `;
+
+      createOnboardingTourDescription(div,
+        "onboarding.tour-screenshots.title", "onboarding.tour-screenshots.description");
+      createOnboardingTourContent(div, "resource://onboarding/img/figure_screenshots.svg");
+
+      let aside = createOnboardingTourButton(div,
+        "onboarding-tour-screenshots-button", "onboarding.tour-screenshots.button");
+
+      let button = aside.querySelector("button");
+      button.setAttribute("href", "https://screenshots.firefox.com/#tour");
+      button.setAttribute("target", "_blank");
+
       return div;
     },
   },
 };
 
 /**
  * @param {String} action the action to ask the chrome to do
  * @param {Array | Object} params the parameters for the action
@@ -1243,33 +1320,53 @@ class Onboarding {
     }
   }
 
   _renderNotificationBar() {
     let footer = this._window.document.createElement("footer");
     footer.id = "onboarding-notification-bar";
     footer.setAttribute("aria-live", "polite");
     footer.setAttribute("aria-labelledby", "onboarding-notification-tour-title");
-    // We use `innerHTML` for more friendly reading.
-    // The security should be fine because this is not from an external input.
-    footer.innerHTML = `
-      <section id="onboarding-notification-message-section" role="presentation">
-        <div id="onboarding-notification-tour-icon" role="presentation"></div>
-        <div id="onboarding-notification-body" role="presentation">
-          <h1 id="onboarding-notification-tour-title"></h1>
-          <p id="onboarding-notification-tour-message"></p>
-        </div>
-        <button id="onboarding-notification-action-btn" class="onboarding-action-button"></button>
-      </section>
-      <button id="onboarding-notification-close-btn" class="onboarding-close-btn"></button>
-    `;
+
+    let section = this._window.document.createElement("section");
+    section.id = "onboarding-notification-message-section";
+    section.setAttribute("role", "presentation");
+    footer.appendChild(section);
+
+    let icon = this._window.document.createElement("div");
+    icon.id = "onboarding-notification-tour-icon";
+    icon.setAttribute("role", "presentation");
+    section.appendChild(icon);
+
+    let onboardingNotificationBody = this._window.document.createElement("div");
+    onboardingNotificationBody.id = "onboarding-notification-body";
+    onboardingNotificationBody.setAttribute("role", "presentation");
+    section.appendChild(onboardingNotificationBody);
 
-    let closeBtn = footer.querySelector("#onboarding-notification-close-btn");
-    closeBtn.setAttribute("title",
+    let title = this._window.document.createElement("h1");
+    title.id = "onboarding-notification-tour-title";
+    onboardingNotificationBody.appendChild(title);
+
+    let message = this._window.document.createElement("p");
+    message.id = "onboarding-notification-tour-message";
+    onboardingNotificationBody.appendChild(message);
+
+    let actionButton = this._window.document.createElement("button");
+    actionButton.id = "onboarding-notification-action-btn";
+    actionButton.className = "onboarding-action-button";
+    section.appendChild(actionButton);
+
+    let closeButton = this._window.document.createElement("button");
+    closeButton.id = "onboarding-notification-close-btn";
+    closeButton.className = "onboarding-close-btn";
+    footer.appendChild(closeButton);
+
+    closeButton.setAttribute("title",
       this._bundle.GetStringFromName("onboarding.notification-close-button-tooltip"));
+
     return footer;
   }
 
   skipTour() {
     this.setToursCompleted(this._tours.map(tour => tour.id));
     sendMessageToChrome("set-prefs", [
       {
         name: NOTIFICATION_FINISHED_PREF,
@@ -1286,45 +1383,57 @@ class Onboarding {
       session_key: this._session_key,
       width: this._windowWidthRounded,
     });
   }
 
   _renderOverlay() {
     let div = this._window.document.createElement("div");
     div.id = "onboarding-overlay";
-    // We use `innerHTML` for more friendly reading.
-    // The security should be fine because this is not from an external input.
-    div.innerHTML = `
-      <div role="dialog" tabindex="-1" aria-labelledby="onboarding-header">
-        <header id="onboarding-header"></header>
-        <nav>
-          <ul id="onboarding-tour-list" role="tablist"></ul>
-        </nav>
-        <footer id="onboarding-footer"></footer>
-        <button id="onboarding-overlay-close-btn" class="onboarding-close-btn"></button>
-      </div>
-    `;
+
+    this._dialog = this._window.document.createElement("div");
+    this._dialog.setAttribute("role", "dialog");
+    this._dialog.setAttribute("tabindex", "-1");
+    this._dialog.setAttribute("aria-labelledby", "onboarding-header");
+    this._dialog.id = ONBOARDING_DIALOG_ID;
+    div.appendChild(this._dialog);
+
+    let header = this._window.document.createElement("header");
+    header.id = "onboarding-header";
+    header.textContent = this._bundle.GetStringFromName("onboarding.overlay-title2");
+    this._dialog.appendChild(header);
+
+    let nav = this._window.document.createElement("nav");
+    this._dialog.appendChild(nav);
 
-    this._dialog = div.querySelector(`[role="dialog"]`);
-    this._dialog.id = ONBOARDING_DIALOG_ID;
-    div.querySelector("#onboarding-header").textContent =
-      this._bundle.GetStringFromName("onboarding.overlay-title2");
+    let tourList = this._window.document.createElement("ul");
+    tourList.id = "onboarding-tour-list";
+    tourList.setAttribute("role", "tablist");
+    nav.appendChild(tourList);
+
+    let footer = this._window.document.createElement("footer");
+    footer.id = "onboarding-footer";
+    this._dialog.appendChild(footer);
+
+    let button = this._window.document.createElement("button");
+    button.id = "onboarding-overlay-close-btn";
+    button.className = "onboarding-close-btn";
+    button.setAttribute("title",
+      this._bundle.GetStringFromName("onboarding.overlay-close-button-tooltip"));
+    this._dialog.appendChild(button);
+
     // support show/hide skip tour button via pref
     if (!Services.prefs.getBoolPref("browser.onboarding.skip-tour-button.hide", false)) {
-      let footer = div.querySelector("#onboarding-footer");
       let skipButton = this._window.document.createElement("button");
       skipButton.id = "onboarding-skip-tour-button";
       skipButton.classList.add("onboarding-action-button");
       skipButton.textContent = this._bundle.GetStringFromName("onboarding.skip-tour-button-label");
       footer.appendChild(skipButton);
     }
-    let closeBtn = div.querySelector("#onboarding-overlay-close-btn");
-    closeBtn.setAttribute("title",
-      this._bundle.GetStringFromName("onboarding.overlay-close-button-tooltip"));
+
     return div;
   }
 
   _renderOverlayButton() {
     let button = this._window.document.createElement("button");
     // support customize speech bubble string via pref
     let tooltipStringPrefId = "";
     let defaultTourStringId = "";
--- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
@@ -63,28 +63,26 @@ var PdfjsChromeUtils = {
   /*
    * Public API
    */
 
   init() {
     this._browsers = new WeakSet();
     if (!this._ppmm) {
       // global parent process message manager (PPMM)
-      this._ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"].
-        getService(Ci.nsIMessageBroadcaster);
+      this._ppmm = Services.ppmm;
       this._ppmm.addMessageListener("PDFJS:Parent:clearUserPref", this);
       this._ppmm.addMessageListener("PDFJS:Parent:setIntPref", this);
       this._ppmm.addMessageListener("PDFJS:Parent:setBoolPref", this);
       this._ppmm.addMessageListener("PDFJS:Parent:setCharPref", this);
       this._ppmm.addMessageListener("PDFJS:Parent:setStringPref", this);
       this._ppmm.addMessageListener("PDFJS:Parent:isDefaultHandlerApp", this);
 
       // global dom message manager (MMg)
-      this._mmg = Cc["@mozilla.org/globalmessagemanager;1"].
-        getService(Ci.nsIMessageListenerManager);
+      this._mmg = Services.mm;
       this._mmg.addMessageListener("PDFJS:Parent:displayWarning", this);
 
       this._mmg.addMessageListener("PDFJS:Parent:addEventListener", this);
       this._mmg.addMessageListener("PDFJS:Parent:removeEventListener", this);
       this._mmg.addMessageListener("PDFJS:Parent:updateControlState", this);
 
       // Observer to handle shutdown.
       Services.obs.addObserver(this, "quit-application");
--- a/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm
@@ -30,18 +30,17 @@ var PdfjsContentUtils = {
     return (Services.appinfo.processType ===
             Services.appinfo.PROCESS_TYPE_CONTENT);
   },
 
   init() {
     // child *process* mm, or when loaded into the parent for in-content
     // support the psuedo child process mm 'child PPMM'.
     if (!this._mm) {
-      this._mm = Cc["@mozilla.org/childprocessmessagemanager;1"].
-        getService(Ci.nsISyncMessageSender);
+      this._mm = Services.cpmm;
       this._mm.addMessageListener("PDFJS:Child:updateSettings", this);
 
       Services.obs.addObserver(this, "quit-application");
     }
   },
 
   uninit() {
     if (this._mm) {
--- a/browser/extensions/pocket/bootstrap.js
+++ b/browser/extensions/pocket/bootstrap.js
@@ -403,19 +403,17 @@ var PocketOverlay = {
     PocketReader.startup();
     PocketPageAction.init();
     PocketContextMenu.init();
     for (let win of browserWindows()) {
       this.onWindowOpened(win);
     }
   },
   shutdown(reason) {
-    let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
-                 .getService(Ci.nsIMessageBroadcaster);
-    ppmm.broadcastAsyncMessage("PocketShuttingDown");
+    Services.ppmm.broadcastAsyncMessage("PocketShuttingDown");
     Services.obs.removeObserver(this, "browser-delayed-startup-finished");
     // Although the ppmm loads the scripts into the chrome process as well,
     // we need to manually unregister here anyway to ensure these aren't part
     // of the chrome process and avoid errors.
     AboutPocket.aboutSaved.unregister();
     AboutPocket.aboutSignup.unregister();
 
     PocketPageAction.shutdown();
--- a/browser/modules/AboutHome.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -173,17 +173,16 @@ var AboutHome = {
         // Set pref to indicate we've shown the notification.
         let currentVersion = Services.prefs.getIntPref("browser.rights.version");
         Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
       }
 
       if (target && target.messageManager) {
         target.messageManager.sendAsyncMessage("AboutHome:Update", data);
       } else {
-        let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
-        mm.broadcastAsyncMessage("AboutHome:Update", data);
+        Services.mm.broadcastAsyncMessage("AboutHome:Update", data);
       }
     }).catch(function onError(x) {
       Cu.reportError("Error in AboutHome.sendAboutHomeData: " + x);
     });
   },
 
 };
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -990,22 +990,20 @@ var PluginCrashReporter = {
         let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
         if (pluginDumpID) {
           this.crashReports.set(pluginID, { pluginDumpID });
         }
 
         // Only the parent process gets the gmp-plugin-crash observer
         // notification, so we need to inform any content processes that
         // the GMP has crashed.
-        if (Cc["@mozilla.org/parentprocessmessagemanager;1"]) {
+        if (Services.ppmm) {
           let pluginName = propertyBag.getPropertyAsAString("pluginName");
-          let mm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
-            .getService(Ci.nsIMessageListenerManager);
-          mm.broadcastAsyncMessage("gmp-plugin-crash",
-                                   { pluginName, pluginID });
+          Services.ppmm.broadcastAsyncMessage("gmp-plugin-crash",
+                                              { pluginName, pluginID });
         }
         break;
       }
       case "profile-after-change":
         this.uninit();
         break;
     }
   },
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -484,19 +484,17 @@ var ContentSearch = {
     // We reply asyncly to messages, and by the time we reply the browser we're
     // responding to may have been destroyed.  messageManager is null then.
     if (!Cu.isDeadWrapper(msg.target) && msg.target.messageManager) {
       msg.target.messageManager.sendAsyncMessage(...this._msgArgs(type, data));
     }
   },
 
   _broadcast(type, data) {
-    Cc["@mozilla.org/globalmessagemanager;1"].
-      getService(Ci.nsIMessageListenerManager).
-      broadcastAsyncMessage(...this._msgArgs(type, data));
+    Services.mm.broadcastAsyncMessage(...this._msgArgs(type, data));
   },
 
   _msgArgs(type, data) {
     return [OUTBOUND_MESSAGE, {
       type,
       data,
     }];
   },
--- a/devtools/client/jsonview/converter-child.js
+++ b/devtools/client/jsonview/converter-child.js
@@ -1,30 +1,27 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=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/. */
 
 "use strict";
 
-const {Cc, Ci, Cu, CC} = require("chrome");
+const {Ci, Cu, CC} = require("chrome");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 const Services = require("Services");
 
 loader.lazyRequireGetter(this, "NetworkHelper",
                                "devtools/shared/webconsole/network-helper");
 loader.lazyGetter(this, "debug", function () {
   let {AppConstants} = require("resource://gre/modules/AppConstants.jsm");
   return !!(AppConstants.DEBUG || AppConstants.DEBUG_JS_MODULES);
 });
 
-const childProcessMessageManager =
-  Cc["@mozilla.org/childprocessmessagemanager;1"]
-    .getService(Ci.nsISyncMessageSender);
 const BinaryInput = CC("@mozilla.org/binaryinputstream;1",
                        "nsIBinaryInputStream", "setInputStream");
 const BufferStream = CC("@mozilla.org/io/arraybuffer-input-stream;1",
                        "nsIArrayBufferInputStream", "setData");
 
 // Localization
 loader.lazyGetter(this, "jsonViewStrings", () => {
   return Services.strings.createBundle(
@@ -299,17 +296,17 @@ function onContentMessage(e) {
   let win = this;
   if (win != e.target) {
     return;
   }
 
   let value = e.detail.value;
   switch (e.detail.type) {
     case "save":
-      childProcessMessageManager.sendAsyncMessage(
+      Services.cpmm.sendAsyncMessage(
         "devtools:jsonview:save", value);
   }
 }
 
 function createInstance() {
   return new Converter();
 }
 
--- a/devtools/client/netmonitor/test/browser_net_view-source-debugger.js
+++ b/devtools/client/netmonitor/test/browser_net_view-source-debugger.js
@@ -18,17 +18,17 @@ add_task(async function () {
   store.dispatch(Actions.batchEnable(false));
 
   let waitForContentRequests = waitForNetworkEvents(monitor, 2);
   await ContentTask.spawn(tab.linkedBrowser, {},
     () => content.wrappedJSObject.performRequests());
   await waitForContentRequests;
 
   info("Clicking stack-trace tab and waiting for stack-trace panel to open");
-  let wait = waitForDOM(document, "#stack-trace-panel .frame-link", 4);
+  let wait = waitForDOM(document, "#stack-trace-panel .frame-link", 5);
   // Click on the first request
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelector(".request-list-item"));
   // Open the stack-trace tab for that request
   document.getElementById("stack-trace-tab").click();
   await wait;
 
   let frameLinkNode = document.querySelector(".frame-link");
--- a/devtools/client/shared/test/browser_html_tooltip-03.js
+++ b/devtools/client/shared/test/browser_html_tooltip-03.js
@@ -126,13 +126,16 @@ function blurNode(doc, selector) {
  * @return {Promise} promise that will resolve the HTMLTooltip instance created when the
  *         tooltip content will be ready.
  */
 function* createTooltip(doc, autofocus) {
   let tooltip = new HTMLTooltip(doc, {autofocus, useXulWrapper});
   let div = doc.createElementNS(HTML_NS, "div");
   div.classList.add("tooltip-content");
   div.style.height = "50px";
-  div.innerHTML = '<input type="text"></input>';
+
+  let input = doc.createElementNS(HTML_NS, "input");
+  input.setAttribute("type", "text");
+  div.appendChild(input);
 
   tooltip.setContent(div, {width: 150, height: 50});
   return tooltip;
 }
--- a/devtools/client/shared/widgets/FilterWidget.js
+++ b/devtools/client/shared/widgets/FilterWidget.js
@@ -145,62 +145,102 @@ function CSSFilterEditorWidget(el, value
   this.setCssValue(value);
   this.renderPresets();
 }
 
 exports.CSSFilterEditorWidget = CSSFilterEditorWidget;
 
 CSSFilterEditorWidget.prototype = {
   _initMarkup: function () {
-    let filterListSelectPlaceholder =
-      L10N.getStr("filterListSelectPlaceholder");
-    let addNewFilterButton = L10N.getStr("addNewFilterButton");
-    let presetsToggleButton = L10N.getStr("presetsToggleButton");
-    let newPresetPlaceholder = L10N.getStr("newPresetPlaceholder");
-    let savePresetButton = L10N.getStr("savePresetButton");
+    // The following structure is created:
+    //   <div class="filters-list">
+    //     <div id="filters"></div>
+    //     <div class="footer">
+    //       <select value="">
+    //         <option value="">${filterListSelectPlaceholder}</option>
+    //       </select>
+    //       <button id="add-filter" class="add">${addNewFilterButton}</button>
+    //       <button id="toggle-presets">${presetsToggleButton}</button>
+    //     </div>
+    //   </div>
+    //   <div class="presets-list">
+    //     <div id="presets"></div>
+    //     <div class="footer">
+    //       <input value="" class="devtools-textinput"
+    //              placeholder="${newPresetPlaceholder}"></input>
+    //       <button class="add">${savePresetButton}</button>
+    //     </div>
+    //   </div>
+    let content = this.doc.createDocumentFragment();
+
+    let filterListWrapper = this.doc.createElementNS(XHTML_NS, "div");
+    filterListWrapper.classList.add("filters-list");
+    content.appendChild(filterListWrapper);
+
+    this.filterList = this.doc.createElementNS(XHTML_NS, "div");
+    this.filterList.setAttribute("id", "filters");
+    filterListWrapper.appendChild(this.filterList);
+
+    let filterListFooter = this.doc.createElementNS(XHTML_NS, "div");
+    filterListFooter.classList.add("footer");
+    filterListWrapper.appendChild(filterListFooter);
+
+    this.filterSelect = this.doc.createElementNS(XHTML_NS, "select");
+    this.filterSelect.setAttribute("value", "");
+    filterListFooter.appendChild(this.filterSelect);
 
-    // eslint-disable-next-line no-unsanitized/property
-    this.el.innerHTML = `
-      <div class="filters-list">
-        <div id="filters"></div>
-        <div class="footer">
-          <select value="">
-            <option value="">${filterListSelectPlaceholder}</option>
-          </select>
-          <button id="add-filter" class="add">${addNewFilterButton}</button>
-          <button id="toggle-presets">${presetsToggleButton}</button>
-        </div>
-      </div>
+    let filterListPlaceholder = this.doc.createElementNS(XHTML_NS, "option");
+    filterListPlaceholder.setAttribute("value", "");
+    filterListPlaceholder.textContent = L10N.getStr("filterListSelectPlaceholder");
+    this.filterSelect.appendChild(filterListPlaceholder);
+
+    let addFilter = this.doc.createElementNS(XHTML_NS, "button");
+    addFilter.setAttribute("id", "add-filter");
+    addFilter.classList.add("add");
+    addFilter.textContent = L10N.getStr("addNewFilterButton");
+    filterListFooter.appendChild(addFilter);
+
+    this.togglePresets = this.doc.createElementNS(XHTML_NS, "button");
+    this.togglePresets.setAttribute("id", "toggle-presets");
+    this.togglePresets.textContent = L10N.getStr("presetsToggleButton");
+    filterListFooter.appendChild(this.togglePresets);
+
+    let presetListWrapper = this.doc.createElementNS(XHTML_NS, "div");
+    presetListWrapper.classList.add("presets-list");
+    content.appendChild(presetListWrapper);
 
-      <div class="presets-list">
-        <div id="presets"></div>
-        <div class="footer">
-          <input value="" class="devtools-textinput"
-                 placeholder="${newPresetPlaceholder}"></input>
-          <button class="add">${savePresetButton}</button>
-        </div>
-      </div>
-    `;
-    this.filtersList = this.el.querySelector("#filters");
-    this.presetsList = this.el.querySelector("#presets");
-    this.togglePresets = this.el.querySelector("#toggle-presets");
-    this.filterSelect = this.el.querySelector("select");
-    this.addPresetButton = this.el.querySelector(".presets-list .add");
-    this.addPresetInput = this.el.querySelector(".presets-list .footer input");
+    this.presetList = this.doc.createElementNS(XHTML_NS, "div");
+    this.presetList.setAttribute("id", "presets");
+    presetListWrapper.appendChild(this.presetList);
+
+    let presetListFooter = this.doc.createElementNS(XHTML_NS, "div");
+    presetListFooter.classList.add("footer");
+    presetListWrapper.appendChild(presetListFooter);
 
-    this.el.querySelector(".presets-list input").value = "";
+    this.addPresetInput = this.doc.createElementNS(XHTML_NS, "input");
+    this.addPresetInput.setAttribute("value", "");
+    this.addPresetInput.classList.add("devtools-textinput");
+    this.addPresetInput.setAttribute("placeholder", L10N.getStr("newPresetPlaceholder"));
+    presetListFooter.appendChild(this.addPresetInput);
+
+    this.addPresetButton = this.doc.createElementNS(XHTML_NS, "button");
+    this.addPresetButton.classList.add("add");
+    this.addPresetButton.textContent = L10N.getStr("savePresetButton");
+    presetListFooter.appendChild(this.addPresetButton);
+
+    this.el.appendChild(content);
 
     this._populateFilterSelect();
   },
 
   _destroyMarkup: function () {
     this._filterItemMarkup.remove();
     this.el.remove();
-    this.el = this.filtersList = this._filterItemMarkup = null;
-    this.presetsList = this.togglePresets = this.filterSelect = null;
+    this.el = this.filterList = this._filterItemMarkup = null;
+    this.presetList = this.togglePresets = this.filterSelect = null;
     this.addPresetButton = null;
   },
 
   destroy: function () {
     this._removeEventListeners();
     this._destroyMarkup();
   },
 
@@ -272,55 +312,55 @@ CSSFilterEditorWidget.prototype = {
     base.appendChild(removeButton);
 
     this._presetItemMarkup = base;
   },
 
   _addEventListeners: function () {
     this.addButton = this.el.querySelector("#add-filter");
     this.addButton.addEventListener("click", this._addButtonClick);
-    this.filtersList.addEventListener("click", this._removeButtonClick);
-    this.filtersList.addEventListener("mousedown", this._mouseDown);
-    this.filtersList.addEventListener("keydown", this._keyDown);
+    this.filterList.addEventListener("click", this._removeButtonClick);
+    this.filterList.addEventListener("mousedown", this._mouseDown);
+    this.filterList.addEventListener("keydown", this._keyDown);
     this.el.addEventListener("mousedown", this._resetFocus);
 
-    this.presetsList.addEventListener("click", this._presetClick);
+    this.presetList.addEventListener("click", this._presetClick);
     this.togglePresets.addEventListener("click", this._togglePresets);
     this.addPresetButton.addEventListener("click", this._savePreset);
 
     // These events are event delegators for
     // drag-drop re-ordering and label-dragging
     this.win.addEventListener("mousemove", this._mouseMove);
     this.win.addEventListener("mouseup", this._mouseUp);
 
     // Used to workaround float-precision problems
-    this.filtersList.addEventListener("input", this._input);
+    this.filterList.addEventListener("input", this._input);
   },
 
   _removeEventListeners: function () {
     this.addButton.removeEventListener("click", this._addButtonClick);
-    this.filtersList.removeEventListener("click", this._removeButtonClick);
-    this.filtersList.removeEventListener("mousedown", this._mouseDown);
-    this.filtersList.removeEventListener("keydown", this._keyDown);
+    this.filterList.removeEventListener("click", this._removeButtonClick);
+    this.filterList.removeEventListener("mousedown", this._mouseDown);
+    this.filterList.removeEventListener("keydown", this._keyDown);
     this.el.removeEventListener("mousedown", this._resetFocus);
 
-    this.presetsList.removeEventListener("click", this._presetClick);
+    this.presetList.removeEventListener("click", this._presetClick);
     this.togglePresets.removeEventListener("click", this._togglePresets);
     this.addPresetButton.removeEventListener("click", this._savePreset);
 
     // These events are used for drag drop re-ordering
     this.win.removeEventListener("mousemove", this._mouseMove);
     this.win.removeEventListener("mouseup", this._mouseUp);
 
     // Used to workaround float-precision problems
-    this.filtersList.removeEventListener("input", this._input);
+    this.filterList.removeEventListener("input", this._input);
   },
 
   _getFilterElementIndex: function (el) {
-    return [...this.filtersList.children].indexOf(el);
+    return [...this.filterList.children].indexOf(el);
   },
 
   _keyDown: function (e) {
     if (e.target.tagName.toLowerCase() !== "input" ||
        (e.keyCode !== 40 && e.keyCode !== 38)) {
       return;
     }
     let input = e.target;
@@ -449,57 +489,57 @@ CSSFilterEditorWidget.prototype = {
     if (this.isReorderingFilter) {
       this._dragFilterElement(e);
     } else if (this.isDraggingLabel) {
       this._dragLabel(e);
     }
   },
 
   _dragFilterElement: function (e) {
-    const rect = this.filtersList.getBoundingClientRect();
+    const rect = this.filterList.getBoundingClientRect();
     let top = e.pageY - LIST_PADDING;
     let bottom = e.pageY + LIST_PADDING;
     // don't allow dragging over top/bottom of list
     if (top < rect.top || bottom > rect.bottom) {
       return;
     }
 
-    const filterEl = this.filtersList.querySelector(".dragging");
+    const filterEl = this.filterList.querySelector(".dragging");
 
     const delta = e.pageY - filterEl.startingY;
     filterEl.style.top = delta + "px";
 
     // change is the number of _steps_ taken from initial position
     // i.e. how many elements we have passed
     let change = delta / LIST_ITEM_HEIGHT;
     if (change > 0) {
       change = Math.floor(change);
     } else if (change < 0) {
       change = Math.ceil(change);
     }
 
-    const children = this.filtersList.children;
+    const children = this.filterList.children;
     const index = [...children].indexOf(filterEl);
     const destination = index + change;
 
     // If we're moving out, or there's no change at all, stop and return
     if (destination >= children.length || destination < 0 || change === 0) {
       return;
     }
 
     // Re-order filter objects
     swapArrayIndices(this.filters, index, destination);
 
     // Re-order the dragging element in markup
     const target = change > 0 ? children[destination + 1]
                               : children[destination];
     if (target) {
-      this.filtersList.insertBefore(filterEl, target);
+      this.filterList.insertBefore(filterEl, target);
     } else {
-      this.filtersList.appendChild(filterEl);
+      this.filterList.appendChild(filterEl);
     }
 
     filterEl.removeAttribute("style");
 
     const currentPosition = change * LIST_ITEM_HEIGHT;
     filterEl.startingY = e.pageY + currentPosition - delta;
   },
 
@@ -540,17 +580,17 @@ CSSFilterEditorWidget.prototype = {
     // Label-dragging is disabled on mouseup
     this._dragging = null;
     this.isDraggingLabel = false;
 
     // Filter drag/drop needs more cleaning
     if (!this.isReorderingFilter) {
       return;
     }
-    let filterEl = this.filtersList.querySelector(".dragging");
+    let filterEl = this.filterList.querySelector(".dragging");
 
     this.isReorderingFilter = false;
     filterEl.classList.remove("dragging");
     this.el.classList.remove("dragging");
     filterEl.removeAttribute("style");
 
     this.emit("updated", this.getCssValue());
     this.render();
@@ -621,23 +661,23 @@ CSSFilterEditorWidget.prototype = {
 
   /**
    * Clears the list and renders filters, binding required events.
    * There are some delegated events bound in _addEventListeners method
    */
   render: function () {
     if (!this.filters.length) {
   // eslint-disable-next-line no-unsanitized/property
-      this.filtersList.innerHTML = `<p> ${L10N.getStr("emptyFilterList")} <br />
+      this.filterList.innerHTML = `<p> ${L10N.getStr("emptyFilterList")} <br />
                                  ${L10N.getStr("addUsingList")} </p>`;
       this.emit("render");
       return;
     }
 
-    this.filtersList.innerHTML = "";
+    this.filterList.innerHTML = "";
 
     let base = this._filterItemMarkup;
 
     for (let filter of this.filters) {
       const def = this._definition(filter.name);
 
       let el = base.cloneNode(true);
 
@@ -677,61 +717,61 @@ CSSFilterEditorWidget.prototype = {
 
         label.classList.add("devtools-draglabel");
         label.title = L10N.getStr("labelDragTooltipText");
       } else {
         // string-type filters have no unit
         unitPreview.remove();
       }
 
-      this.filtersList.appendChild(el);
+      this.filterList.appendChild(el);
     }
 
     let lastInput =
-        this.filtersList.querySelector(".filter:last-of-type input");
+        this.filterList.querySelector(".filter:last-of-type input");
     if (lastInput) {
       lastInput.focus();
       if (lastInput.type === "text") {
         // move cursor to end of input
         const end = lastInput.value.length;
         lastInput.setSelectionRange(end, end);
       }
     }
 
     this.emit("render");
   },
 
   renderPresets: function () {
     this.getPresets().then(presets => {
       // getPresets is async and the widget may be destroyed in between.
-      if (!this.presetsList) {
+      if (!this.presetList) {
         return;
       }
 
       if (!presets || !presets.length) {
       // eslint-disable-next-line no-unsanitized/property
-        this.presetsList.innerHTML = `<p>${L10N.getStr("emptyPresetList")}</p>`;
+        this.presetList.innerHTML = `<p>${L10N.getStr("emptyPresetList")}</p>`;
         this.emit("render");
         return;
       }
       let base = this._presetItemMarkup;
 
-      this.presetsList.innerHTML = "";
+      this.presetList.innerHTML = "";
 
       for (let [index, preset] of presets.entries()) {
         let el = base.cloneNode(true);
 
         let [label, span] = el.children;
 
         el.dataset.id = index;
 
         label.textContent = preset.name;
         span.textContent = preset.value;
 
-        this.presetsList.appendChild(el);
+        this.presetList.appendChild(el);
       }
 
       this.emit("render");
     });
   },
 
   /**
     * returns definition of a filter as defined in filterList
--- a/devtools/client/sourceeditor/README
+++ b/devtools/client/sourceeditor/README
@@ -49,42 +49,42 @@ below.
 The following files in this directory and devtools/client/sourceeditor/test/codemirror/
 are licensed according to the contents in the LICENSE file.
 
 # Localization patches
 
 diff --git a/devtools/client/sourceeditor/codemirror/addon/search/search.js b/devtools/client/sourceeditor/codemirror/addon/search/search.js
 --- a/devtools/client/sourceeditor/codemirror/addon/search/search.js
 +++ b/devtools/client/sourceeditor/codemirror/addon/search/search.js
-@@ -92,32 +92,47 @@
+@@ -93,32 +93,47 @@
      } else {
        query = parseString(query)
      }
      if (typeof query == "string" ? query == "" : query.test(""))
        query = /x^/;
      return query;
    }
 
 -  var queryDialog =
--    'Search: <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
+-    '<span class="CodeMirror-search-label">Search:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
 +  var queryDialog;
 
    function startSearch(cm, state, query) {
      state.queryText = query;
      state.query = parseQuery(query);
      cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
      state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
      cm.addOverlay(state.overlay);
      if (cm.showMatchesOnScrollbar) {
        if (state.annotate) { state.annotate.clear(); state.annotate = null; }
        state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
      }
    }
 
-   function doSearch(cm, rev, persistent) {
+   function doSearch(cm, rev, persistent, immediate) {
 +    if (!queryDialog) {
 +      let doc = cm.getWrapperElement().ownerDocument;
 +      let inp = doc.createElement("input");
 +
 +      inp.type = "search";
 +      inp.placeholder = cm.l10n("findCmd.promptMessage");
 +      inp.style.marginInlineStart = "1em";
 +      inp.style.marginInlineEnd = "1em";
@@ -94,21 +94,142 @@ diff --git a/devtools/client/sourceedito
 +      queryDialog = doc.createElement("div");
 +      queryDialog.appendChild(inp);
 +      queryDialog.style.display = "flex";
 +    }
 +
      var state = getSearchState(cm);
      if (state.query) return findNext(cm, rev);
      var q = cm.getSelection() || state.lastQuery;
+     if (q instanceof RegExp && q.source == "x^") q = null
      if (persistent && cm.openDialog) {
        var hiding = null
-       persistentDialog(cm, queryDialog, q, function(query, event) {
+       var searchNext = function(query, event) {
          CodeMirror.e_stop(event);
-         if (!query) return;
+@@ -181,56 +196,110 @@
+     var state = getSearchState(cm);
+     state.lastQuery = state.query;
+     if (!state.query) return;
+     state.query = state.queryText = null;
+     cm.removeOverlay(state.overlay);
+     if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+   });}
+ 
+-  var replaceQueryDialog =
+-    ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
+-  var replacementQueryDialog = '<span class="CodeMirror-search-label">With:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
+-  var doReplaceConfirm = '<span class="CodeMirror-search-label">Replace?</span> <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>';
+-
+   function replaceAll(cm, query, text) {
+     cm.operation(function() {
+       for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
+         if (typeof query != "string") {
+           var match = cm.getRange(cursor.from(), cursor.to()).match(query);
+           cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+         } else cursor.replace(text);
+       }
+     });
+   }
+ 
+   function replace(cm, all) {
+     if (cm.getOption("readOnly")) return;
+     var query = cm.getSelection() || getSearchState(cm).lastQuery;
+-    var dialogText = '<span class="CodeMirror-search-label">' + (all ? 'Replace all:' : 'Replace:') + '</span>';
+-    dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
++
++    let doc = cm.getWrapperElement().ownerDocument;
++
++    // `searchLabel` is used as part of `replaceQueryFragment` and as a separate
++    // argument by itself, so it should be cloned.
++    let searchLabel = doc.createElement("span");
++    searchLabel.classList.add("CodeMirror-search-label");
++    searchLabel.textContent = all ? "Replace all:" : "Replace:";
++
++    let replaceQueryFragment = doc.createDocumentFragment();
++    replaceQueryFragment.appendChild(searchLabel.cloneNode(true));
++
++    let searchField = doc.createElement("input");
++    searchField.setAttribute("type", "text");
++    searchField.setAttribute("style", "width: 10em");
++    searchField.classList.add("CodeMirror-search-field");
++    replaceQueryFragment.appendChild(searchField);
++
++    let searchHint = doc.createElement("span");
++    searchHint.setAttribute("style", "color: #888");
++    searchHint.classList.add("CodeMirror-search-hint");
++    searchHint.textContent = "(Use /re/ syntax for regexp search)";
++    replaceQueryFragment.appendChild(searchHint);
++
++    dialog(cm, replaceQueryFragment, searchLabel, query, function(query) {
+       if (!query) return;
+       query = parseQuery(query);
+-      dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
++
++      let replacementQueryFragment = doc.createDocumentFragment();
++
++      let replaceWithLabel = searchLabel.cloneNode(false);
++      replaceWithLabel.textContent = "With:";
++      replacementQueryFragment.appendChild(replaceWithLabel);
++
++      let replaceField = doc.createElement("input");
++      replaceField.setAttribute("type", "text");
++      replaceField.setAttribute("style", "width: 10em");
++      replaceField.classList.add("CodeMirror-search-field");
++      replacementQueryFragment.appendChild(replaceField);
++
++      dialog(cm, replacementQueryFragment, "Replace with:", "", function(text) {
+         text = parseString(text)
+         if (all) {
+           replaceAll(cm, query, text)
+         } else {
+           clearSearch(cm);
+           var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
+           var advance = function() {
+             var start = cursor.from(), match;
+             if (!(match = cursor.findNext())) {
+               cursor = getSearchCursor(cm, query);
+               if (!(match = cursor.findNext()) ||
+                   (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
+             }
+             cm.setSelection(cursor.from(), cursor.to());
+-            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
+-            confirmDialog(cm, doReplaceConfirm, "Replace?",
++            cm.scrollIntoView({ from: cursor.from(), to: cursor.to() });
++
++            let replaceConfirmFragment = doc.createDocumentFragment();
++
++            let replaceConfirmLabel = searchLabel.cloneNode(false);
++            replaceConfirmLabel.textContent = "Replace?";
++            replaceConfirmFragment.appendChild(replaceConfirmLabel);
++
++            let yesButton = doc.createElement("button");
++            yesButton.textContent = "Yes";
++            replaceConfirmFragment.appendChild(yesButton);
++
++            let noButton = doc.createElement("button");
++            noButton.textContent = "No";
++            replaceConfirmFragment.appendChild(noButton);
++
++            let allButton = doc.createElement("button");
++            allButton.textContent = "All";
++            replaceConfirmFragment.appendChild(allButton);
++
++            let stopButton = doc.createElement("button");
++            stopButton.textContent = "Stop";
++            replaceConfirmFragment.appendChild(stopButton);
++
++            confirmDialog(cm, replaceConfirmFragment, "Replace?",
+                           [function() {doReplace(match);}, advance,
+                            function() {replaceAll(cm, query, text)}]);
+           };
+           var doReplace = function(match) {
+             cursor.replace(typeof query == "string" ? text :
+                            text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+             advance();
+           };
 
 # Footnotes
 
 [1] http://codemirror.net
 [2] devtools/client/sourceeditor/codemirror
 [3] devtools/client/sourceeditor/test/browser_codemirror.js
 [4] devtools/client/jar.mn
 [5] devtools/client/sourceeditor/editor.js
--- a/devtools/client/sourceeditor/codemirror/addon/search/search.js
+++ b/devtools/client/sourceeditor/codemirror/addon/search/search.js
@@ -196,56 +196,110 @@
     var state = getSearchState(cm);
     state.lastQuery = state.query;
     if (!state.query) return;
     state.query = state.queryText = null;
     cm.removeOverlay(state.overlay);
     if (state.annotate) { state.annotate.clear(); state.annotate = null; }
   });}
 
-  var replaceQueryDialog =
-    ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
-  var replacementQueryDialog = '<span class="CodeMirror-search-label">With:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
-  var doReplaceConfirm = '<span class="CodeMirror-search-label">Replace?</span> <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>';
-
   function replaceAll(cm, query, text) {
     cm.operation(function() {
       for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
         if (typeof query != "string") {
           var match = cm.getRange(cursor.from(), cursor.to()).match(query);
           cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
         } else cursor.replace(text);
       }
     });
   }
 
   function replace(cm, all) {
     if (cm.getOption("readOnly")) return;
     var query = cm.getSelection() || getSearchState(cm).lastQuery;
-    var dialogText = '<span class="CodeMirror-search-label">' + (all ? 'Replace all:' : 'Replace:') + '</span>';
-    dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
+
+    let doc = cm.getWrapperElement().ownerDocument;
+
+    // `searchLabel` is used as part of `replaceQueryFragment` and as a separate
+    // argument by itself, so it should be cloned.
+    let searchLabel = doc.createElement("span");
+    searchLabel.classList.add("CodeMirror-search-label");
+    searchLabel.textContent = all ? "Replace all:" : "Replace:";
+
+    let replaceQueryFragment = doc.createDocumentFragment();
+    replaceQueryFragment.appendChild(searchLabel.cloneNode(true));
+
+    let searchField = doc.createElement("input");
+    searchField.setAttribute("type", "text");
+    searchField.setAttribute("style", "width: 10em");
+    searchField.classList.add("CodeMirror-search-field");
+    replaceQueryFragment.appendChild(searchField);
+
+    let searchHint = doc.createElement("span");
+    searchHint.setAttribute("style", "color: #888");
+    searchHint.classList.add("CodeMirror-search-hint");
+    searchHint.textContent = "(Use /re/ syntax for regexp search)";
+    replaceQueryFragment.appendChild(searchHint);
+
+    dialog(cm, replaceQueryFragment, searchLabel, query, function(query) {
       if (!query) return;
       query = parseQuery(query);
-      dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
+
+      let replacementQueryFragment = doc.createDocumentFragment();
+
+      let replaceWithLabel = searchLabel.cloneNode(false);
+      replaceWithLabel.textContent = "With:";
+      replacementQueryFragment.appendChild(replaceWithLabel);
+
+      let replaceField = doc.createElement("input");
+      replaceField.setAttribute("type", "text");
+      replaceField.setAttribute("style", "width: 10em");
+      replaceField.classList.add("CodeMirror-search-field");
+      replacementQueryFragment.appendChild(replaceField);
+
+      dialog(cm, replacementQueryFragment, "Replace with:", "", function(text) {
         text = parseString(text)
         if (all) {
           replaceAll(cm, query, text)
         } else {
           clearSearch(cm);
           var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
           var advance = function() {
             var start = cursor.from(), match;
             if (!(match = cursor.findNext())) {
               cursor = getSearchCursor(cm, query);
               if (!(match = cursor.findNext()) ||
                   (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
             }
             cm.setSelection(cursor.from(), cursor.to());
-            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
-            confirmDialog(cm, doReplaceConfirm, "Replace?",
+            cm.scrollIntoView({ from: cursor.from(), to: cursor.to() });
+
+            let replaceConfirmFragment = doc.createDocumentFragment();
+
+            let replaceConfirmLabel = searchLabel.cloneNode(false);
+            replaceConfirmLabel.textContent = "Replace?";
+            replaceConfirmFragment.appendChild(replaceConfirmLabel);
+
+            let yesButton = doc.createElement("button");
+            yesButton.textContent = "Yes";
+            replaceConfirmFragment.appendChild(yesButton);
+
+            let noButton = doc.createElement("button");
+            noButton.textContent = "No";
+            replaceConfirmFragment.appendChild(noButton);
+
+            let allButton = doc.createElement("button");
+            allButton.textContent = "All";
+            replaceConfirmFragment.appendChild(allButton);
+
+            let stopButton = doc.createElement("button");
+            stopButton.textContent = "Stop";
+            replaceConfirmFragment.appendChild(stopButton);
+
+            confirmDialog(cm, replaceConfirmFragment, "Replace?",
                           [function() {doReplace(match);}, advance,
                            function() {replaceAll(cm, query, text)}]);
           };
           var doReplace = function(match) {
             cursor.replace(typeof query == "string" ? text :
                            text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
             advance();
           };
--- a/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
+++ b/devtools/client/sourceeditor/codemirror/codemirror.bundle.js
@@ -10402,56 +10402,110 @@ var CodeMirror =
 	    var state = getSearchState(cm);
 	    state.lastQuery = state.query;
 	    if (!state.query) return;
 	    state.query = state.queryText = null;
 	    cm.removeOverlay(state.overlay);
 	    if (state.annotate) { state.annotate.clear(); state.annotate = null; }
 	  });}
 
-	  var replaceQueryDialog =
-	    ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
-	  var replacementQueryDialog = '<span class="CodeMirror-search-label">With:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
-	  var doReplaceConfirm = '<span class="CodeMirror-search-label">Replace?</span> <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>';
-
 	  function replaceAll(cm, query, text) {
 	    cm.operation(function() {
 	      for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
 	        if (typeof query != "string") {
 	          var match = cm.getRange(cursor.from(), cursor.to()).match(query);
 	          cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
 	        } else cursor.replace(text);
 	      }
 	    });
 	  }
 
 	  function replace(cm, all) {
 	    if (cm.getOption("readOnly")) return;
 	    var query = cm.getSelection() || getSearchState(cm).lastQuery;
-	    var dialogText = '<span class="CodeMirror-search-label">' + (all ? 'Replace all:' : 'Replace:') + '</span>';
-	    dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
+
+	    let doc = cm.getWrapperElement().ownerDocument;
+
+	    // `searchLabel` is used as part of `replaceQueryFragment` and as a separate
+	    // argument by itself, so it should be cloned.
+	    let searchLabel = doc.createElement("span");
+	    searchLabel.classList.add("CodeMirror-search-label");
+	    searchLabel.textContent = all ? "Replace all:" : "Replace:";
+
+	    let replaceQueryFragment = doc.createDocumentFragment();
+	    replaceQueryFragment.appendChild(searchLabel.cloneNode(true));
+
+	    let searchField = doc.createElement("input");
+	    searchField.setAttribute("type", "text");
+	    searchField.setAttribute("style", "width: 10em");
+	    searchField.classList.add("CodeMirror-search-field");
+	    replaceQueryFragment.appendChild(searchField);
+
+	    let searchHint = doc.createElement("span");
+	    searchHint.setAttribute("style", "color: #888");
+	    searchHint.classList.add("CodeMirror-search-hint");
+	    searchHint.textContent = "(Use /re/ syntax for regexp search)";
+	    replaceQueryFragment.appendChild(searchHint);
+
+	    dialog(cm, replaceQueryFragment, searchLabel, query, function(query) {
 	      if (!query) return;
 	      query = parseQuery(query);
-	      dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
+
+	      let replacementQueryFragment = doc.createDocumentFragment();
+
+	      let replaceWithLabel = searchLabel.cloneNode(false);
+	      replaceWithLabel.textContent = "With:";
+	      replacementQueryFragment.appendChild(replaceWithLabel);
+
+	      let replaceField = doc.createElement("input");
+	      replaceField.setAttribute("type", "text");
+	      replaceField.setAttribute("style", "width: 10em");
+	      replaceField.classList.add("CodeMirror-search-field");
+	      replacementQueryFragment.appendChild(replaceField);
+
+	      dialog(cm, replacementQueryFragment, "Replace with:", "", function(text) {
 	        text = parseString(text)
 	        if (all) {
 	          replaceAll(cm, query, text)
 	        } else {
 	          clearSearch(cm);
 	          var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
 	          var advance = function() {
 	            var start = cursor.from(), match;
 	            if (!(match = cursor.findNext())) {
 	              cursor = getSearchCursor(cm, query);
 	              if (!(match = cursor.findNext()) ||
 	                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
 	            }
 	            cm.setSelection(cursor.from(), cursor.to());
-	            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
-	            confirmDialog(cm, doReplaceConfirm, "Replace?",
+	            cm.scrollIntoView({ from: cursor.from(), to: cursor.to() });
+
+	            let replaceConfirmFragment = doc.createDocumentFragment();
+
+	            let replaceConfirmLabel = searchLabel.cloneNode(false);
+	            replaceConfirmLabel.textContent = "Replace?";
+	            replaceConfirmFragment.appendChild(replaceConfirmLabel);
+
+	            let yesButton = doc.createElement("button");
+	            yesButton.textContent = "Yes";
+	            replaceConfirmFragment.appendChild(yesButton);
+
+	            let noButton = doc.createElement("button");
+	            noButton.textContent = "No";
+	            replaceConfirmFragment.appendChild(noButton);
+
+	            let allButton = doc.createElement("button");
+	            allButton.textContent = "All";
+	            replaceConfirmFragment.appendChild(allButton);
+
+	            let stopButton = doc.createElement("button");
+	            stopButton.textContent = "Stop";
+	            replaceConfirmFragment.appendChild(stopButton);
+
+	            confirmDialog(cm, replaceConfirmFragment, "Replace?",
 	                          [function() {doReplace(match);}, advance,
 	                           function() {replaceAll(cm, query, text)}]);
 	          };
 	          var doReplace = function(match) {
 	            cursor.replace(typeof query == "string" ? text :
 	                           text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
 	            advance();
 	          };
--- a/devtools/client/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
+++ b/devtools/client/webaudioeditor/test/browser_wa_properties-view-media-nodes.js
@@ -15,22 +15,20 @@ function waitForDeviceClosed() {
   ChromeUtils.import("resource:///modules/webrtcUI.jsm", temp);
   let webrtcUI = temp.webrtcUI;
 
   if (!webrtcUI.showGlobalIndicator)
     return Promise.resolve();
 
   return new Promise((resolve, reject) => {
     const message = "webrtc:UpdateGlobalIndicators";
-    let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
-                 .getService(Ci.nsIMessageBroadcaster);
-    ppmm.addMessageListener(message, function listener(aMessage) {
+    Services.ppmm.addMessageListener(message, function listener(aMessage) {
       info("Received " + message + " message");
       if (!aMessage.data.showGlobalIndicator) {
-        ppmm.removeMessageListener(message, listener);
+        Services.ppmm.removeMessageListener(message, listener);
         resolve();
       }
     });
   });
 }
 
 add_task(function* () {
   let { target, panel } = yield initWebAudioEditor(MEDIA_NODES_URL);
--- a/devtools/server/actors/process.js
+++ b/devtools/server/actors/process.js
@@ -1,19 +1,18 @@
 /* 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 { Cc, Ci } = require("chrome");
+var { Cc } = require("chrome");
 
 loader.lazyGetter(this, "ppmm", () => {
-  return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(
-    Ci.nsIMessageBroadcaster);
+  return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService();
 });
 
 function ProcessActorList() {
   this._actors = new Map();
   this._onListChanged = null;
   this._mustNotify = false;
 
   this._onMessage = this._onMessage.bind(this);
--- a/devtools/server/actors/root.js
+++ b/devtools/server/actors/root.js
@@ -1,25 +1,21 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=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/. */
 
 "use strict";
 
-const { Cc, Ci, Cu } = require("chrome");
+const { Cu } = require("chrome");
 const Services = require("Services");
 const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common");
 const { DebuggerServer } = require("devtools/server/main");
 
-loader.lazyGetter(this, "ppmm", () => {
-  return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(
-    Ci.nsIMessageBroadcaster);
-});
 loader.lazyRequireGetter(this, "WindowActor",
   "devtools/server/actors/window", true);
 
 /* Root actor for the remote debugging protocol. */
 
 /**
  * Create a remote debugging protocol root actor.
  *
@@ -542,17 +538,17 @@ RootActor.prototype = {
         this._chromeActor = new ChromeActor(this.conn);
         this._globalActorPool.addActor(this._chromeActor);
       }
 
       return { form: this._chromeActor.form() };
     }
 
     let { id } = request;
-    let mm = ppmm.getChildAt(id);
+    let mm = Services.ppmm.getChildAt(id);
     if (!mm) {
       return { error: "noProcess",
                message: "There is no process with id '" + id + "'." };
     }
     let form = this._processActors.get(id);
     if (form) {
       return { form };
     }
--- a/devtools/server/actors/storage.js
+++ b/devtools/server/actors/storage.js
@@ -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/. */
 
 "use strict";
 
-const {Cc, Ci, Cu, CC} = require("chrome");
+const {Ci, Cu, CC} = require("chrome");
 const protocol = require("devtools/shared/protocol");
 const {LongStringActor} = require("devtools/server/actors/string");
 const {DebuggerServer} = require("devtools/server/main");
 const Services = require("Services");
 const defer = require("devtools/shared/defer");
 const {isWindowIncluded} = require("devtools/shared/layout/utils");
 const specs = require("devtools/shared/specs/storage");
 
@@ -1933,30 +1933,24 @@ StorageActors.createActor({
           { name: "objectStores", editable: false },
         ];
     }
   }
 });
 
 var indexedDBHelpers = {
   backToChild(...args) {
-    let mm = Cc["@mozilla.org/globalmessagemanager;1"]
-               .getService(Ci.nsIMessageListenerManager);
-
-    mm.broadcastAsyncMessage("debug:storage-indexedDB-request-child", {
+    Services.mm.broadcastAsyncMessage("debug:storage-indexedDB-request-child", {
       method: "backToChild",
       args: args
     });
   },
 
   onItemUpdated(action, host, path) {
-    let mm = Cc["@mozilla.org/globalmessagemanager;1"]
-               .getService(Ci.nsIMessageListenerManager);
-
-    mm.broadcastAsyncMessage("debug:storage-indexedDB-request-child", {
+    Services.mm.broadcastAsyncMessage("debug:storage-indexedDB-request-child", {
       method: "onItemUpdated",
       args: [ action, host, path ]
     });
   },
 
   /**
    * Fetches and stores all the metadata information for the given database
    * `name` for the given `host` with its `principal`. The stored metadata
--- a/devtools/server/actors/webconsole/content-process-forward.js
+++ b/devtools/server/actors/webconsole/content-process-forward.js
@@ -2,19 +2,16 @@
  * 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";
 
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
 const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
 
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsIMessageSender");
 ChromeUtils.defineModuleGetter(this, "E10SUtils",
                                "resource://gre/modules/E10SUtils.jsm");
 
 /*
  * The message manager has an upper limit on message sizes that it can
  * reliably forward to the parent so we limit the size of console log event
  * messages that we forward here. The web console is local and receives the
  * full console message, but addons subscribed to console event messages
@@ -32,17 +29,17 @@ ChromeUtils.defineModuleGetter(this, "E1
  */
 const MSG_MGR_CONSOLE_MAX_SIZE = 1024 * 1024; // 1MB
 const MSG_MGR_CONSOLE_VAR_SIZE = 8;
 const MSG_MGR_CONSOLE_INFO_MAX = 1024;
 
 function ContentProcessForward() {
   Services.obs.addObserver(this, "console-api-log-event");
   Services.obs.addObserver(this, "xpcom-shutdown");
-  cpmm.addMessageListener("DevTools:StopForwardingContentProcessMessage", this);
+  Services.cpmm.addMessageListener("DevTools:StopForwardingContentProcessMessage", this);
 }
 ContentProcessForward.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
   receiveMessage(message) {
     if (message.name == "DevTools:StopForwardingContentProcessMessage") {
       this.uninit();
@@ -103,30 +100,31 @@ ContentProcessForward.prototype = {
             msgData.arguments.push(arg);
           } else {
             // arguments take up too much space
             msgData.arguments = ["<truncated>"];
             break;
           }
         }
 
-        cpmm.sendAsyncMessage("Console:Log", msgData);
+        Services.cpmm.sendAsyncMessage("Console:Log", msgData);
         break;
       }
 
       case "xpcom-shutdown":
         this.uninit();
         break;
     }
   },
 
   uninit() {
     Services.obs.removeObserver(this, "console-api-log-event");
     Services.obs.removeObserver(this, "xpcom-shutdown");
-    cpmm.removeMessageListener("DevTools:StopForwardingContentProcessMessage", this);
+    Services.cpmm.removeMessageListener("DevTools:StopForwardingContentProcessMessage",
+                                        this);
   }
 };
 
 // loadProcessScript loads in all processes, including the parent,
 // in which we don't need any forwarding
 if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
   new ContentProcessForward();
 }
--- a/devtools/server/content-server.jsm
+++ b/devtools/server/content-server.jsm
@@ -44,17 +44,16 @@ function setupServer(mm) {
     gLoader = null;
   });
 
   return gLoader;
 }
 
 function init(msg) {
   let mm = msg.target;
-  mm.QueryInterface(Ci.nsISyncMessageSender);
   let prefix = msg.data.prefix;
 
   // Using the JS debugger causes problems when we're trying to
   // schedule those zone groups across different threads. Calling
   // blockThreadedExecution causes Gecko to switch to a simpler
   // single-threaded model until unblockThreadedExecution is called
   // later. We cannot start the debugger until the callback passed to
   // blockThreadedExecution has run, signaling that we're running
--- a/devtools/server/tests/mochitest/inspector-traversal-data.html
+++ b/devtools/server/tests/mochitest/inspector-traversal-data.html
@@ -20,17 +20,28 @@
   <script type="text/javascript">
     "use strict";
 
     window.onload = function () {
       // Set up a basic shadow DOM
       let host = document.querySelector("#shadow");
       if (host.createShadowRoot) {
         let root = host.createShadowRoot();
-        root.innerHTML = "<h3>Shadow <em>DOM</em></h3><select multiple></select>";
+
+        let h3 = document.createElement("h3");
+        h3.append("Shadow ");
+
+        let em = document.createElement("em");
+        em.append("DOM");
+
+        let select = document.createElement("select");
+        select.setAttribute("multiple", "");
+        h3.appendChild(em);
+        root.appendChild(h3);
+        root.appendChild(select);
       }
 
       // Put a copy of the body in an iframe to test frame traversal.
       let body = document.querySelector("body");
       let data = "data:text/html,<html>" + body.outerHTML + "<html>";
       let iframe = document.createElement("iframe");
       iframe.setAttribute("id", "childFrame");
       iframe.onload = function () {
--- a/devtools/server/tests/mochitest/setup-in-child.js
+++ b/devtools/server/tests/mochitest/setup-in-child.js
@@ -1,13 +1,12 @@
 "use strict";
 
-const {Cc, Ci} = require("chrome");
-const cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
-             .getService(Ci.nsIMessageListenerManager);
+const {Cc} = require("chrome");
+const cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService();
 const { DebuggerServer } = require("devtools/server/main");
 
 exports.setupChild = function (a, b, c) {
   cpmm.sendAsyncMessage("test:setupChild", [a, b, c]);
 };
 
 exports.callParent = function () {
   // Hack! Fetch DebuggerServerConnection objects directly within DebuggerServer guts.
--- a/devtools/server/tests/mochitest/test_setupInParentChild.html
+++ b/devtools/server/tests/mochitest/test_setupInParentChild.html
@@ -44,33 +44,31 @@ function runTests() {
   }
 
   // Fake a connection to an iframe
   let transport = DebuggerServer.connectPipe();
   let conn = transport._serverConnection;
   let client = new DebuggerClient(transport);
 
   // Wait for a response from setupInChild
-  const ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
-                 .getService(Ci.nsIMessageListenerManager);
   let onChild = msg => {
-    ppmm.removeMessageListener("test:setupChild", onChild);
+    Services.ppmm.removeMessageListener("test:setupChild", onChild);
     let args = msg.json;
 
     is(args[0], 1, "Got first numeric argument");
     is(args[1], "two", "Got second string argument");
     is(args[2].three, true, "Got last JSON argument");
 
     // Ask the child to call setupInParent
     DebuggerServer.setupInChild({
       module: "chrome://mochitests/content/chrome/devtools/server/tests/mochitest/setup-in-child.js",
       setupChild: "callParent"
     });
   };
-  ppmm.addMessageListener("test:setupChild", onChild);
+  Services.ppmm.addMessageListener("test:setupChild", onChild);
 
   // Wait also for a reponse from setupInParent called from setup-in-child.js
   let onParent = (_, topic, args) => {
     Services.obs.removeObserver(onParent, "test:setupParent");
     args = JSON.parse(args);
 
     is(args[0], true, "Got `mm` argument, a message manager");
     ok(args[1].match(/server\d+.conn\d+.child\d+/), "Got `prefix` argument");
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -644,21 +644,20 @@ nsDocShell::GetInterface(const nsIID& aI
     nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
     if (NS_SUCCEEDED(rv) && treeOwner) {
       return treeOwner->QueryInterface(aIID, aSink);
     }
   } else if (aIID.Equals(NS_GET_IID(nsITabChild))) {
     *aSink = GetTabChild().take();
     return *aSink ? NS_OK : NS_ERROR_FAILURE;
   } else if (aIID.Equals(NS_GET_IID(nsIContentFrameMessageManager))) {
-    nsCOMPtr<nsITabChild> tabChild =
-      do_GetInterface(static_cast<nsIDocShell*>(this));
+    RefPtr<TabChild> tabChild = TabChild::GetFrom(this);
     nsCOMPtr<nsIContentFrameMessageManager> mm;
     if (tabChild) {
-      tabChild->GetMessageManager(getter_AddRefs(mm));
+      mm = tabChild->GetMessageManager();
     } else {
       if (nsPIDOMWindowOuter* win = GetWindow()) {
         mm = do_QueryInterface(win->GetParentTarget());
       }
     }
     *aSink = mm.get();
   } else {
     return nsDocLoader::GetInterface(aIID, aSink);
new file mode 100644
--- /dev/null
+++ b/dom/base/ChildProcessMessageManager.h
@@ -0,0 +1,42 @@
+/* -*- 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_ChildProcessMessageManager_h
+#define mozilla_dom_ChildProcessMessageManager_h
+
+#include "mozilla/dom/SyncMessageSender.h"
+#include "mozilla/dom/MessageManagerBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+class ChildProcessMessageManager final : public SyncMessageSender
+{
+public:
+  explicit ChildProcessMessageManager(ipc::MessageManagerCallback* aCallback)
+    : SyncMessageSender(aCallback,
+                        MessageManagerFlags::MM_PROCESSMANAGER |
+                        MessageManagerFlags::MM_OWNSCALLBACK)
+  {
+    mozilla::HoldJSObjects(this);
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
+  {
+    return ChildProcessMessageManagerBinding::Wrap(aCx, this, aGivenProto);
+  }
+
+protected:
+  virtual ~ChildProcessMessageManager()
+  {
+    mozilla::DropJSObjects(this);
+  }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ChildProcessMessageManager_h
new file mode 100644
--- /dev/null
+++ b/dom/base/ChromeMessageBroadcaster.cpp
@@ -0,0 +1,47 @@
+/* -*- 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/ChromeMessageBroadcaster.h"
+#include "AccessCheck.h"
+#include "mozilla/HoldDropJSObjects.h"
+#include "mozilla/dom/MessageManagerBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+ChromeMessageBroadcaster::ChromeMessageBroadcaster(nsFrameMessageManager* aParentManager,
+                                                   MessageManagerFlags aFlags)
+  : MessageListenerManager(nullptr, aParentManager,
+                           aFlags |
+                           MessageManagerFlags::MM_BROADCASTER |
+                           MessageManagerFlags::MM_CHROME)
+{
+  if (mIsProcessManager) {
+    mozilla::HoldJSObjects(this);
+  }
+  if (aParentManager) {
+    aParentManager->AddChildManager(this);
+  }
+}
+
+ChromeMessageBroadcaster::~ChromeMessageBroadcaster()
+{
+  if (mIsProcessManager) {
+    mozilla::DropJSObjects(this);
+  }
+}
+
+JSObject*
+ChromeMessageBroadcaster::WrapObject(JSContext* aCx,
+                                     JS::Handle<JSObject*> aGivenProto)
+{
+  MOZ_ASSERT(nsContentUtils::IsSystemCaller(aCx));
+
+  return ChromeMessageBroadcasterBinding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/ChromeMessageBroadcaster.h
@@ -0,0 +1,95 @@
+/* -*- 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_ChromeMessageBroadcaster_h
+#define mozilla_dom_ChromeMessageBroadcaster_h
+
+#include "mozilla/dom/MessageListenerManager.h"
+
+namespace mozilla {
+namespace dom {
+
+class ChromeMessageBroadcaster final : public MessageListenerManager
+{
+public:
+  explicit ChromeMessageBroadcaster(MessageManagerFlags aFlags)
+    : ChromeMessageBroadcaster(nullptr, aFlags)
+  {
+    MOZ_ASSERT(!(aFlags & ~(MessageManagerFlags::MM_GLOBAL |
+                            MessageManagerFlags::MM_PROCESSMANAGER |
+                            MessageManagerFlags::MM_OWNSCALLBACK)));
+  }
+  explicit ChromeMessageBroadcaster(nsFrameMessageManager* aParentManager)
+    : ChromeMessageBroadcaster(aParentManager, MessageManagerFlags::MM_NONE)
+  {}
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  using nsFrameMessageManager::BroadcastAsyncMessage;
+  void BroadcastAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
+                             JS::Handle<JS::Value> aObj,
+                             JS::Handle<JSObject*> aObjects,
+                             mozilla::ErrorResult& aError)
+  {
+    DispatchAsyncMessage(aCx, aMessageName, aObj, aObjects, nullptr,
+                         JS::UndefinedHandleValue, aError);
+  }
+  uint32_t ChildCount()
+  {
+    return mChildManagers.Length();
+  }
+  using nsFrameMessageManager::GetChildAt;
+  MessageListenerManager* GetChildAt(uint32_t aIndex)
+  {
+    return mChildManagers.SafeElementAt(aIndex);
+  }
+  // XPCOM ReleaseCachedProcesses is OK
+
+  // ProcessScriptLoader
+  using nsFrameMessageManager::LoadProcessScript;
+  void LoadProcessScript(const nsAString& aUrl, bool aAllowDelayedLoad,
+                         mozilla::ErrorResult& aError)
+  {
+    LoadScript(aUrl, aAllowDelayedLoad, false, aError);
+  }
+  // XPCOM RemoveDelayedProcessScript is OK
+  using nsFrameMessageManager::GetDelayedProcessScripts;
+  void GetDelayedProcessScripts(JSContext* aCx,
+                                nsTArray<nsTArray<JS::Value>>& aScripts,
+                                mozilla::ErrorResult& aError)
+  {
+    GetDelayedScripts(aCx, aScripts, aError);
+  }
+
+  // GlobalProcessScriptLoader
+  // XPCOM GetInitialProcessData is OK
+
+  // FrameScriptLoader
+  using nsFrameMessageManager::LoadFrameScript;
+  void LoadFrameScript(const nsAString& aUrl, bool aAllowDelayedLoad,
+                       bool aRunInGlobalScope, mozilla::ErrorResult& aError)
+  {
+    LoadScript(aUrl, aAllowDelayedLoad, aRunInGlobalScope, aError);
+  }
+  using nsFrameMessageManager::GetDelayedFrameScripts;
+  void GetDelayedFrameScripts(JSContext* aCx,
+                              nsTArray<nsTArray<JS::Value>>& aScripts,
+                              mozilla::ErrorResult& aError)
+  {
+    GetDelayedScripts(aCx, aScripts, aError);
+  }
+
+private:
+  ChromeMessageBroadcaster(nsFrameMessageManager* aParentManager,
+                           MessageManagerFlags aFlags);
+  virtual ~ChromeMessageBroadcaster();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ChromeMessageBroadcaster_h
new file mode 100644
--- /dev/null
+++ b/dom/base/ChromeMessageSender.cpp
@@ -0,0 +1,41 @@
+/* -*- 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/ChromeMessageSender.h"
+#include "mozilla/dom/MessageManagerBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+ChromeMessageSender::ChromeMessageSender(ipc::MessageManagerCallback* aCallback,
+                                         nsFrameMessageManager* aParentManager,
+                                         MessageManagerFlags aFlags)
+  : MessageSender(aCallback, aParentManager, aFlags | MessageManagerFlags::MM_CHROME)
+{
+  MOZ_ASSERT(!(aFlags & ~(MessageManagerFlags::MM_GLOBAL |
+                          MessageManagerFlags::MM_PROCESSMANAGER |
+                          MessageManagerFlags::MM_OWNSCALLBACK)));
+
+  // This is a bit hackish. We attach to the parent, but only if we have a callback. We
+  // don't have a callback for the frame message manager, and for parent process message
+  // managers (except the parent in-process message manager). In those cases we wait until
+  // the child process is running (see MessageSender::InitWithCallback).
+  if (aParentManager && mCallback) {
+    aParentManager->AddChildManager(this);
+  }
+}
+
+JSObject*
+ChromeMessageSender::WrapObject(JSContext* aCx,
+                                JS::Handle<JSObject*> aGivenProto)
+{
+  MOZ_ASSERT(nsContentUtils::IsSystemCaller(aCx));
+
+  return ChromeMessageSenderBinding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/ChromeMessageSender.h
@@ -0,0 +1,60 @@
+/* -*- 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_ChromeMessageSender_h
+#define mozilla_dom_ChromeMessageSender_h
+
+#include "mozilla/dom/MessageSender.h"
+
+namespace mozilla {
+namespace dom {
+
+class ChromeMessageSender final : public MessageSender
+{
+public:
+  ChromeMessageSender(ipc::MessageManagerCallback* aCallback,
+                      nsFrameMessageManager* aParentManager,
+                      MessageManagerFlags aFlags=MessageManagerFlags::MM_NONE);
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  // ProcessScriptLoader
+  using nsFrameMessageManager::LoadProcessScript;
+  void LoadProcessScript(const nsAString& aUrl, bool aAllowDelayedLoad,
+                         mozilla::ErrorResult& aError)
+  {
+    LoadScript(aUrl, aAllowDelayedLoad, false, aError);
+  }
+  // XPCOM RemoveDelayedProcessScript is OK
+  using nsFrameMessageManager::GetDelayedProcessScripts;
+  void GetDelayedProcessScripts(JSContext* aCx,
+                                nsTArray<nsTArray<JS::Value>>& aScripts,
+                                mozilla::ErrorResult& aError)
+  {
+    GetDelayedScripts(aCx, aScripts, aError);
+  }
+
+  // FrameScriptLoader
+  using nsFrameMessageManager::LoadFrameScript;
+  void LoadFrameScript(const nsAString& aUrl, bool aAllowDelayedLoad,
+                       bool aRunInGlobalScope, mozilla::ErrorResult& aError)
+  {
+    LoadScript(aUrl, aAllowDelayedLoad, aRunInGlobalScope, aError);
+  }
+  using nsFrameMessageManager::GetDelayedFrameScripts;
+  void GetDelayedFrameScripts(JSContext* aCx,
+                              nsTArray<nsTArray<JS::Value>>& aScripts,
+                              mozilla::ErrorResult& aError)
+  {
+    GetDelayedScripts(aCx, aScripts, aError);
+  }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ChromeMessageSender_h
new file mode 100644
--- /dev/null
+++ b/dom/base/ContentFrameMessageManager.h
@@ -0,0 +1,95 @@
+/* -*- 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_ContentFrameMessageManager_h
+#define mozilla_dom_ContentFrameMessageManager_h
+
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/dom/MessageManagerGlobal.h"
+#include "mozilla/dom/ResolveSystemBinding.h"
+#include "nsContentUtils.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);
+  }
+
+  nsresult AddEventListener(const nsAString& aType,
+                            nsIDOMEventListener* aListener,
+                            bool aUseCapture)
+  {
+    // By default add listeners only for trusted events!
+    return DOMEventTargetHelper::AddEventListener(aType, aListener,
+                                                  aUseCapture, false, 2);
+  }
+  using DOMEventTargetHelper::AddEventListener;
+  NS_IMETHOD AddEventListener(const nsAString& aType,
+                              nsIDOMEventListener* aListener,
+                              bool aUseCapture, bool aWantsUntrusted,
+                              uint8_t optional_argc) override
+  {
+    return DOMEventTargetHelper::AddEventListener(aType, aListener,
+                                                  aUseCapture,
+                                                  aWantsUntrusted,
+                                                  optional_argc);
+  }
+
+  virtual already_AddRefed<nsPIDOMWindowOuter> GetContent(ErrorResult& aError) = 0;
+  virtual already_AddRefed<nsIDocShell> GetDocShell(ErrorResult& aError) = 0;
+  virtual already_AddRefed<nsIEventTarget> GetTabEventTarget() = 0;
+
+  nsFrameMessageManager* GetMessageManager()
+  {
+    return mMessageManager;
+  }
+  void DisconnectMessageManager()
+  {
+    mMessageManager->Disconnect();
+    mMessageManager = nullptr;
+  }
+
+protected:
+  explicit ContentFrameMessageManager(nsFrameMessageManager* aMessageManager)
+    : MessageManagerGlobal(aMessageManager)
+  {}
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ContentFrameMessageManager_h
--- a/dom/base/DOMMatrix.cpp
+++ b/dom/base/DOMMatrix.cpp
@@ -7,18 +7,20 @@
 #include "mozilla/dom/DOMMatrix.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMMatrixBinding.h"
 #include "mozilla/dom/DOMPoint.h"
 #include "mozilla/dom/DOMPointBinding.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ToJSValue.h"
+#include "mozilla/RuleNodeCacheConditions.h"
 #include "mozilla/ServoCSSParser.h"
 #include "nsCSSParser.h"
+#include "nsGlobalWindowInner.h"
 #include "nsStyleTransformMatrix.h"
 
 #include <math.h>
 
 namespace mozilla {
 namespace dom {
 
 static const double radPerDegree = 2.0 * M_PI / 360.0;
--- a/dom/base/DOMRequestHelper.jsm
+++ b/dom/base/DOMRequestHelper.jsm
@@ -16,20 +16,16 @@
  * to the child side of frame and process message manager and removing them
  * when needed.
  */
 var EXPORTED_SYMBOLS = ["DOMRequestIpcHelper"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsIMessageListenerManager");
-
 function DOMRequestIpcHelper() {
   // _listeners keeps a list of messages for which we added a listener and the
   // kind of listener that we added (strong or weak). It's an object of this
   // form:
   //  {
   //    "message1": true,
   //    "messagen": false
   //  }
@@ -84,18 +80,18 @@ DOMRequestIpcHelper.prototype = {
         if (!!aMsg.weakRef == this._listeners[name].weakRef) {
           this._listeners[name].count++;
           return;
         } else {
           throw Cr.NS_ERROR_FAILURE;
         }
       }
 
-      aMsg.weakRef ? cpmm.addWeakMessageListener(name, this)
-                   : cpmm.addMessageListener(name, this);
+      aMsg.weakRef ? Services.cpmm.addWeakMessageListener(name, this)
+                   : Services.cpmm.addMessageListener(name, this);
       this._listeners[name] = {
         weakRef: !!aMsg.weakRef,
         count: 1
       };
     });
   },
 
   /**
@@ -115,18 +111,18 @@ DOMRequestIpcHelper.prototype = {
       if (this._listeners[aName] == undefined) {
         return;
       }
 
       // Only remove the listener really when we don't have anybody that could
       // be waiting on a message.
       if (!--this._listeners[aName].count) {
         this._listeners[aName].weakRef ?
-            cpmm.removeWeakMessageListener(aName, this)
-          : cpmm.removeMessageListener(aName, this);
+            Services.cpmm.removeWeakMessageListener(aName, this)
+          : Services.cpmm.removeMessageListener(aName, this);
         delete this._listeners[aName];
       }
     });
   },
 
   /**
    * Initialize the helper adding the corresponding listeners to the messages
    * provided as the second parameter.
@@ -176,18 +172,19 @@ DOMRequestIpcHelper.prototype = {
     }
 
     this._destroyed = true;
 
     Services.obs.removeObserver(this, "inner-window-destroyed");
 
     if (this._listeners) {
       Object.keys(this._listeners).forEach((aName) => {
-        this._listeners[aName].weakRef ? cpmm.removeWeakMessageListener(aName, this)
-                                       : cpmm.removeMessageListener(aName, this);
+        this._listeners[aName].weakRef ?
+            Services.cpmm.removeWeakMessageListener(aName, this)
+          : Services.cpmm.removeMessageListener(aName, this);
       });
     }
 
     this._listeners = null;
     this._requests = null;
 
     // Objects inheriting from DOMRequestIPCHelper may have an uninit function.
     if (this.uninit) {
new file mode 100644
--- /dev/null
+++ b/dom/base/MessageListenerManager.cpp
@@ -0,0 +1,55 @@
+/* -*- 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"
+
+namespace mozilla {
+namespace dom {
+
+MessageListenerManager::MessageListenerManager(ipc::MessageManagerCallback* aCallback,
+                                               nsFrameMessageManager* aParentManager,
+                                               ipc::MessageManagerFlags aFlags)
+  : nsFrameMessageManager(aCallback, aFlags),
+    mParentManager(aParentManager)
+{
+}
+
+MessageListenerManager::~MessageListenerManager()
+{
+}
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessageListenerManager)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_END_INHERITING(nsFrameMessageManager)
+NS_IMPL_ADDREF_INHERITED(MessageListenerManager, nsFrameMessageManager)
+NS_IMPL_RELEASE_INHERITED(MessageListenerManager, nsFrameMessageManager)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(MessageListenerManager)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessageListenerManager,
+                                                  nsFrameMessageManager)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentManager)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MessageListenerManager,
+                                               nsFrameMessageManager)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessageListenerManager,
+                                                nsFrameMessageManager)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentManager)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+void
+MessageListenerManager::ClearParentManager(bool aRemove)
+{
+  if (aRemove && mParentManager) {
+    mParentManager->RemoveChildManager(this);
+  }
+  mParentManager = nullptr;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/MessageListenerManager.h
@@ -0,0 +1,53 @@
+/* -*- 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_MessageListenerManager_h
+#define mozilla_dom_MessageListenerManager_h
+
+#include "nsCycleCollectionNoteChild.h"
+#include "nsFrameMessageManager.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+namespace dom {
+
+class MessageListenerManager : public nsFrameMessageManager,
+                               public nsWrapperCache
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MessageListenerManager,
+                                                         nsFrameMessageManager)
+
+  nsISupports* GetParentObject()
+  {
+    return ToSupports(mParentManager.get());
+  }
+
+  virtual nsFrameMessageManager* GetParentManager() override
+  {
+    return mParentManager;
+  }
+
+  /**
+   * If aRemove is true then RemoveChildManager(this) will be called on the parent manager
+   * first.
+   */
+  virtual void ClearParentManager(bool aRemove) override;
+
+protected:
+  MessageListenerManager(ipc::MessageManagerCallback* aCallback,
+                         nsFrameMessageManager* aParentManager,
+                         MessageManagerFlags aFlags);
+  virtual ~MessageListenerManager();
+
+  RefPtr<nsFrameMessageManager> mParentManager;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MessageListenerManager_h
new file mode 100644
--- /dev/null
+++ b/dom/base/MessageManagerGlobal.h
@@ -0,0 +1,181 @@
+/* -*- 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_MessageManagerGlobal_h
+#define mozilla_dom_MessageManagerGlobal_h
+
+#include "nsFrameMessageManager.h"
+#include "mozilla/ErrorResult.h"
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * Base class for implementing the WebIDL MessageManagerGlobal class.
+ */
+class MessageManagerGlobal
+{
+public:
+  // MessageListenerManager
+  void AddMessageListener(const nsAString& aMessageName,
+                          MessageListener& aListener,
+                          bool aListenWhenClosed,
+                          ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    mMessageManager->AddMessageListener(aMessageName, aListener,
+                                        aListenWhenClosed, aError);
+  }
+  void RemoveMessageListener(const nsAString& aMessageName,
+                             MessageListener& aListener,
+                             ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    mMessageManager->RemoveMessageListener(aMessageName, aListener, aError);
+  }
+  void AddWeakMessageListener(const nsAString& aMessageName,
+                              MessageListener& aListener,
+                              ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    mMessageManager->AddWeakMessageListener(aMessageName, aListener, aError);
+  }
+  void RemoveWeakMessageListener(const nsAString& aMessageName,
+                                 MessageListener& aListener,
+                                 ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    mMessageManager->RemoveWeakMessageListener(aMessageName, aListener, aError);
+  }
+
+  // MessageSender
+  void SendAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
+                        JS::Handle<JS::Value> aObj,
+                        JS::Handle<JSObject*> aObjects,
+                        nsIPrincipal* aPrincipal,
+                        JS::Handle<JS::Value> aTransfers,
+                        ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    mMessageManager->SendAsyncMessage(aCx, aMessageName, aObj, aObjects,
+                                      aPrincipal, aTransfers, aError);
+  }
+  already_AddRefed<nsIMessageSender> GetProcessMessageManager(mozilla::ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return nullptr;
+    }
+    return mMessageManager->GetProcessMessageManager(aError);
+  }
+
+  void GetRemoteType(nsAString& aRemoteType, mozilla::ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    mMessageManager->GetRemoteType(aRemoteType, aError);
+  }
+
+  // SyncMessageSender
+  void SendSyncMessage(JSContext* aCx, const nsAString& aMessageName,
+                       JS::Handle<JS::Value> aObj,
+                       JS::Handle<JSObject*> aObjects,
+                       nsIPrincipal* aPrincipal,
+                       nsTArray<JS::Value>& aResult,
+                       ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    mMessageManager->SendSyncMessage(aCx, aMessageName, aObj, aObjects,
+                                     aPrincipal, aResult, aError);
+  }
+  void SendRpcMessage(JSContext* aCx, const nsAString& aMessageName,
+                      JS::Handle<JS::Value> aObj,
+                      JS::Handle<JSObject*> aObjects,
+                      nsIPrincipal* aPrincipal,
+                      nsTArray<JS::Value>& aResult,
+                      ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    mMessageManager->SendRpcMessage(aCx, aMessageName, aObj, aObjects,
+                                    aPrincipal, aResult, aError);
+  }
+
+  // MessageManagerGlobal
+  void Dump(const nsAString& aStr, ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    aError = mMessageManager->Dump(aStr);
+  }
+  void PrivateNoteIntentionalCrash(ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    aError = mMessageManager->PrivateNoteIntentionalCrash();
+  }
+  void Atob(const nsAString& aAsciiString, nsAString& aBase64Data,
+            ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    aError = mMessageManager->Atob(aAsciiString, aBase64Data);
+  }
+  void Btoa(const nsAString& aBase64Data, nsAString& aAsciiString,
+            ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    aError = mMessageManager->Btoa(aBase64Data, aAsciiString);
+  }
+
+  bool MarkForCC()
+  {
+    return mMessageManager && mMessageManager->MarkForCC();
+  }
+
+protected:
+  explicit MessageManagerGlobal(nsFrameMessageManager* aMessageManager)
+    : mMessageManager(aMessageManager)
+  {}
+
+  RefPtr<nsFrameMessageManager> mMessageManager;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MessageManagerGlobal_h
new file mode 100644
--- /dev/null
+++ b/dom/base/MessageSender.cpp
@@ -0,0 +1,33 @@
+/* -*- 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/MessageSender.h"
+
+namespace mozilla {
+namespace dom {
+
+void
+MessageSender::InitWithCallback(ipc::MessageManagerCallback* aCallback)
+{
+  if (mCallback) {
+    // Initialization should only happen once.
+    return;
+  }
+
+  SetCallback(aCallback);
+
+  // First load parent scripts by adding this to parent manager.
+  if (mParentManager) {
+    mParentManager->AddChildManager(this);
+  }
+
+  for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
+    LoadFrameScript(mPendingScripts[i], false, mPendingScriptsGlobalStates[i]);
+  }
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/MessageSender.h
@@ -0,0 +1,31 @@
+/* -*- 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_MessageSender_h
+#define mozilla_dom_MessageSender_h
+
+#include "mozilla/dom/MessageListenerManager.h"
+
+namespace mozilla {
+namespace dom {
+
+class MessageSender : public MessageListenerManager
+{
+public:
+  void InitWithCallback(ipc::MessageManagerCallback* aCallback);
+
+protected:
+  MessageSender(ipc::MessageManagerCallback* aCallback,
+                nsFrameMessageManager* aParentManager,
+                MessageManagerFlags aFlags)
+    : MessageListenerManager(aCallback, aParentManager, aFlags)
+  {}
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MessageSender_h
--- a/dom/base/ProcessGlobal.cpp
+++ b/dom/base/ProcessGlobal.cpp
@@ -2,53 +2,82 @@
 /* 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 "nsContentCID.h"
-#include "nsDOMClassInfoID.h"
-#include "mozilla/HoldDropJSObjects.h"
+#include "mozilla/dom/MessageManagerBinding.h"
+#include "mozilla/dom/ResolveSystemBinding.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 ProcessGlobal::ProcessGlobal(nsFrameMessageManager* aMessageManager)
- : mInitialized(false),
-   mMessageManager(aMessageManager)
+ : MessageManagerGlobal(aMessageManager),
+   mInitialized(false)
 {
-  SetIsNotDOMBinding();
   mozilla::HoldJSObjects(this);
 }
 
 ProcessGlobal::~ProcessGlobal()
 {
   mAnonymousGlobalScopes.Clear();
   mozilla::DropJSObjects(this);
 }
 
+bool
+ProcessGlobal::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
+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<nsISyncMessageSender> service = do_GetService(NS_CHILDPROCESSMESSAGEMANAGER_CONTRACTID);
   if (!service) {
     return nullptr;
   }
   return static_cast<ProcessGlobal*>(service.get());
 }
 
 // This method isn't automatically forwarded safely because it's notxpcom, so
 // the IDL binding doesn't know what value to return.
 NS_IMETHODIMP_(bool)
 ProcessGlobal::MarkForCC()
 {
   MarkScopesForCC();
-  return mMessageManager ? mMessageManager->MarkForCC() : false;
+  return MessageManagerGlobal::MarkForCC();
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(ProcessGlobal)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ProcessGlobal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
   tmp->TraverseHostObjectURIs(cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -70,38 +99,52 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentProcessMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsIContentProcessMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
   NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ContentProcessMessageManager)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ProcessGlobal)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ProcessGlobal)
 
 bool
 ProcessGlobal::Init()
 {
   if (mInitialized) {
     return true;
   }
   mInitialized = true;
 
-  nsISupports* scopeSupports = NS_ISUPPORTS_CAST(nsIContentProcessMessageManager*, this);
-  return InitChildGlobalInternal(scopeSupports, NS_LITERAL_CSTRING("processChildGlobal"));
+  return InitChildGlobalInternal(NS_LITERAL_CSTRING("processChildGlobal"));
+}
+
+bool
+ProcessGlobal::WrapGlobalObject(JSContext* aCx,
+                                JS::CompartmentOptions& aOptions,
+                                JS::MutableHandle<JSObject*> aReflector)
+{
+  bool ok = ContentProcessMessageManagerBinding::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;
 }
 
 void
 ProcessGlobal::LoadScript(const nsAString& aURL)
 {
   Init();
-  LoadScriptInternal(aURL, false);
+  JS::Rooted<JSObject*> global(mozilla::dom::RootingCx(), GetWrapper());
+  LoadScriptInternal(global, aURL, false);
 }
 
 void
 ProcessGlobal::SetInitialProcessData(JS::HandleValue aInitialData)
 {
   mMessageManager->SetInitialProcessData(aInitialData);
 }
--- a/dom/base/ProcessGlobal.h
+++ b/dom/base/ProcessGlobal.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ProcessGlobal_h
 #define mozilla_dom_ProcessGlobal_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"
@@ -20,61 +21,98 @@
 #include "nsServiceManagerUtils.h"
 #include "nsWeakReference.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
 class ProcessGlobal :
+  public nsIContentProcessMessageManager,
   public nsMessageManagerScriptExecutor,
-  public nsIContentProcessMessageManager,
   public nsIGlobalObject,
   public nsIScriptObjectPrincipal,
   public nsSupportsWeakReference,
-  public mozilla::dom::ipc::MessageManagerCallback,
+  public ipc::MessageManagerCallback,
+  public MessageManagerGlobal,
   public nsWrapperCache
 {
 public:
   explicit ProcessGlobal(nsFrameMessageManager* aMessageManager);
 
-  using mozilla::dom::ipc::MessageManagerCallback::GetProcessMessageManager;
+  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);
+
+  using ipc::MessageManagerCallback::GetProcessMessageManager;
 
   bool Init();
 
   static ProcessGlobal* Get();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(ProcessGlobal, nsIContentProcessMessageManager)
 
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override
+  {
+    MOZ_CRASH("We should never get here!");
+  }
+  virtual bool WrapGlobalObject(JSContext* aCx,
+                                JS::CompartmentOptions& aOptions,
+                                JS::MutableHandle<JSObject*> aReflector) override;
+
+  using MessageManagerGlobal::AddMessageListener;
+  using MessageManagerGlobal::RemoveMessageListener;
+  using MessageManagerGlobal::AddWeakMessageListener;
+  using MessageManagerGlobal::RemoveWeakMessageListener;
+  using MessageManagerGlobal::SendAsyncMessage;
+  using MessageManagerGlobal::GetProcessMessageManager;
+  using MessageManagerGlobal::GetRemoteType;
+  using MessageManagerGlobal::SendSyncMessage;
+  using MessageManagerGlobal::SendRpcMessage;
+  using MessageManagerGlobal::Dump;
+  using MessageManagerGlobal::PrivateNoteIntentionalCrash;
+  using MessageManagerGlobal::Atob;
+  using MessageManagerGlobal::Btoa;
+
+  // ContentProcessMessageManager
+  void GetInitialProcessData(JSContext* aCx,
+                             JS::MutableHandle<JS::Value> aInitialProcessData,
+                             ErrorResult& aError)
+  {
+    if (!mMessageManager) {
+      aError.Throw(NS_ERROR_NULL_POINTER);
+      return;
+    }
+    mMessageManager->GetInitialProcessData(aCx, aInitialProcessData, aError);
+  }
+
   NS_FORWARD_SAFE_NSIMESSAGELISTENERMANAGER(mMessageManager)
   NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
   NS_FORWARD_SAFE_NSISYNCMESSAGESENDER(mMessageManager)
   NS_FORWARD_SAFE_NSIMESSAGEMANAGERGLOBAL(mMessageManager)
   NS_FORWARD_SAFE_NSICONTENTPROCESSMESSAGEMANAGER(mMessageManager)
 
   virtual void LoadScript(const nsAString& aURL);
 
   virtual JSObject* GetGlobalJSObject() override
   {
-    return mGlobal;
+    return GetWrapper();
   }
   virtual nsIPrincipal* GetPrincipal() override { return mPrincipal; }
 
-  virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override
-  {
-    MOZ_CRASH("ProcessGlobal doesn't use DOM bindings!");
-  }
-
   void SetInitialProcessData(JS::HandleValue aInitialData);
 
 protected:
   virtual ~ProcessGlobal();
 
 private:
   bool mInitialized;
-  RefPtr<nsFrameMessageManager> mMessageManager;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ProcessGlobal_h
new file mode 100644
--- /dev/null
+++ b/dom/base/SyncMessageSender.h
@@ -0,0 +1,27 @@
+/* -*- 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_SyncMessageSender_h
+#define mozilla_dom_SyncMessageSender_h
+
+#include "mozilla/dom/MessageSender.h"
+
+namespace mozilla {
+namespace dom {
+
+class SyncMessageSender : public MessageSender
+{
+protected:
+  SyncMessageSender(ipc::MessageManagerCallback* aCallback,
+                    MessageManagerFlags aFlags)
+    : MessageSender(aCallback, nullptr, aFlags)
+  {}
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_SyncMessageSender_h
--- a/dom/base/TextInputProcessor.cpp
+++ b/dom/base/TextInputProcessor.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/widget/IMEData.h"
 #include "mozilla/dom/KeyboardEvent.h"
 #include "nsContentUtils.h"
 #include "nsIDocShell.h"
 #include "nsIWidget.h"
 #include "nsPIDOMWindow.h"
 #include "nsPresContext.h"
 
+using mozilla::dom::KeyboardEvent;
 using namespace mozilla::widget;
 
 namespace mozilla {
 
 /******************************************************************************
  * TextInputProcessorNotification
  ******************************************************************************/
 
--- a/dom/base/TimeoutExecutor.cpp
+++ b/dom/base/TimeoutExecutor.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TimeoutExecutor.h"
 
 #include "mozilla/dom/TimeoutManager.h"
 #include "nsComponentManagerUtils.h"
+#include "nsIEventTarget.h"
 #include "nsString.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(TimeoutExecutor, nsIRunnable, nsITimerCallback, nsINamed)
 
 TimeoutExecutor::~TimeoutExecutor()
--- a/dom/base/TimeoutManager.h
+++ b/dom/base/TimeoutManager.h
@@ -7,16 +7,17 @@
 #ifndef mozilla_dom_TimeoutManager_h__
 #define mozilla_dom_TimeoutManager_h__
 
 #include "mozilla/dom/Timeout.h"
 #include "nsTArray.h"
 
 class nsIEventTarget;
 class nsITimeoutHandler;
+class nsITimer;
 class nsGlobalWindowInner;
 
 namespace mozilla {
 namespace dom {
 
 class OrderedTimeoutIterator;
 class TimeoutExecutor;
 
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -151,19 +151,23 @@ EXPORTS.mozilla += [
 EXPORTS.mozilla.dom += [
     '!UseCounterList.h',
     'AnonymousContent.h',
     'Attr.h',
     'BarProps.h',
     'BodyUtil.h',
     'BorrowedAttrInfo.h',
     'ChildIterator.h',
+    'ChildProcessMessageManager.h',
+    'ChromeMessageBroadcaster.h',
+    'ChromeMessageSender.h',
     'ChromeNodeList.h',
     'ChromeUtils.h',
     'Comment.h',
+    'ContentFrameMessageManager.h',
     'CustomElementRegistry.h',
     'DirectionalityUtils.h',
     'DispatcherTrait.h',
     'DocGroup.h',
     'DocumentFragment.h',
     'DocumentOrShadowRoot.h',
     'DocumentType.h',
     'DOMCursor.h',
@@ -190,16 +194,19 @@ EXPORTS.mozilla.dom += [
     'IdleDeadline.h',
     'IdleRequest.h',
     'IDTracker.h',
     'ImageEncoder.h',
     'ImageTracker.h',
     'IntlUtils.h',
     'Link.h',
     'Location.h',
+    'MessageListenerManager.h',
+    'MessageManagerGlobal.h',
+    'MessageSender.h',
     'NameSpaceConstants.h',
     'Navigator.h',
     'NodeInfo.h',
     'NodeInfoInlines.h',
     'NodeIterator.h',
     'Pose.h',
     'ProcessGlobal.h',
     'ResponsiveImageSelector.h',
@@ -207,16 +214,17 @@ EXPORTS.mozilla.dom += [
     'ScreenOrientation.h',
     'Selection.h',
     'ShadowRoot.h',
     'StructuredCloneBlob.h',
     'StructuredCloneHolder.h',
     'StructuredCloneTags.h',
     'StyleSheetList.h',
     'SubtleCrypto.h',
+    'SyncMessageSender.h',
     'TabGroup.h',
     'Text.h',
     'Timeout.h',
     'TimeoutHandler.h',
     'TimeoutManager.h',
     'TreeWalker.h',
     'WebKitCSSMatrix.h',
     'WindowOrientationObserver.h',
@@ -229,16 +237,18 @@ if CONFIG['FUZZING']:
 
 UNIFIED_SOURCES += [
     'AnonymousContent.cpp',
     'Attr.cpp',
     'BarProps.cpp',
     'BodyUtil.cpp',
     'BorrowedAttrInfo.cpp',
     'ChildIterator.cpp',
+    'ChromeMessageBroadcaster.cpp',
+    'ChromeMessageSender.cpp',
     'ChromeNodeList.cpp',
     'ChromeUtils.cpp',
     'Comment.cpp',
     'Crypto.cpp',
     'CustomElementRegistry.cpp',
     'DirectionalityUtils.cpp',
     'DispatcherTrait.cpp',
     'DocGroup.cpp',
@@ -264,16 +274,18 @@ UNIFIED_SOURCES += [
     'IdleDeadline.cpp',
     'IdleRequest.cpp',
     'IDTracker.cpp',
     'ImageEncoder.cpp',
     'ImageTracker.cpp',
     'IntlUtils.cpp',
     'Link.cpp',
     'Location.cpp',
+    'MessageListenerManager.cpp',
+    'MessageSender.cpp',
     'Navigator.cpp',
     'NodeInfo.cpp',
     'NodeIterator.cpp',
     'nsAtomListUtils.cpp',
     'nsAttrAndChildArray.cpp',
     'nsAttrValue.cpp',
     'nsAttrValueOrString.cpp',
     'nsCCUncollectableMarker.cpp',
--- a/dom/base/nsCCUncollectableMarker.cpp
+++ b/dom/base/nsCCUncollectableMarker.cpp
@@ -12,32 +12,32 @@
 #include "nsIDocument.h"
 #include "XULDocument.h"
 #include "nsIWindowMediator.h"
 #include "nsPIDOMWindow.h"
 #include "nsIWebNavigation.h"
 #include "nsISHistory.h"
 #include "nsISHEntry.h"
 #include "nsISHContainer.h"
-#include "nsITabChild.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/Element.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;
 using namespace mozilla::dom;
@@ -306,21 +306,19 @@ MarkWindowList(nsISimpleEnumerator* aWin
   nsCOMPtr<nsISupports> iter;
   while (NS_SUCCEEDED(aWindowList->GetNext(getter_AddRefs(iter))) &&
          iter) {
     if (nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(iter)) {
       nsCOMPtr<nsIDocShell> rootDocShell = window->GetDocShell();
 
       MarkDocShell(rootDocShell, aCleanupJS, aPrepareForCC);
 
-      nsCOMPtr<nsITabChild> tabChild =
-        rootDocShell ? rootDocShell->GetTabChild() : nullptr;
+      RefPtr<TabChild> tabChild = TabChild::GetFrom(rootDocShell);
       if (tabChild) {
-        nsCOMPtr<nsIContentFrameMessageManager> mm;
-        tabChild->GetMessageManager(getter_AddRefs(mm));
+        nsCOMPtr<nsIContentFrameMessageManager> mm = tabChild->GetMessageManager();
         if (mm) {
           // MarkForCC ends up calling UnmarkGray on message listeners, which
           // TraceBlackJS can't do yet.
           mm->MarkForCC();
         }
       }
     }
   }
@@ -528,17 +526,17 @@ mozilla::dom::TraceBlackJS(JSTracer* aTr
         if (window->IsRootOuterWindow()) {
           // In child process trace all the TabChildGlobals.
           // Since there is one root outer window per TabChildGlobal, we need
           // to look for only those windows, not all.
           nsIDocShell* ds = window->GetDocShell();
           if (ds) {
             nsCOMPtr<nsITabChild> tabChild = ds->GetTabChild();
             if (tabChild) {
-              nsCOMPtr<nsIContentFrameMessageManager> mm;
+              nsCOMPtr<nsISupports> mm;
               tabChild->GetMessageManager(getter_AddRefs(mm));
               nsCOMPtr<EventTarget> et = do_QueryInterface(mm);
               if (et) {
                 nsCOMPtr<nsISupports> tabChildAsSupports =
                   do_QueryInterface(tabChild);
                 mozilla::TraceScriptHolder(tabChildAsSupports, aTrc);
                 EventListenerManager* elm = et->GetExistingListenerManager();
                 if (elm) {
--- a/dom/base/nsContentIterator.cpp
+++ b/dom/base/nsContentIterator.cpp
@@ -13,16 +13,17 @@
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsContentUtils.h"
 #include "nsINode.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsElementTable.h"
 
 using mozilla::DebugOnly;
+using mozilla::Move;
 using mozilla::RawRangeBoundary;
 
 // couple of utility static functs
 
 ///////////////////////////////////////////////////////////////////////////
 // NodeIsInTraversalRange: returns true if content is visited during
 // the traversal of the range in the specified mode.
 //
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -5234,16 +5234,17 @@ nsContentUtils::ParseFragmentHTML(const 
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (fragment) {
     // Don't fire mutation events for nodes removed by the sanitizer.
     nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
 
     nsTreeSanitizer sanitizer(nsIParserUtils::SanitizerAllowStyle |
                               nsIParserUtils::SanitizerAllowComments |
+                              nsIParserUtils::SanitizerDropForms |
                               nsIParserUtils::SanitizerLogRemovals);
     sanitizer.Sanitize(fragment);
 
     ErrorResult error;
     aTargetNode->AppendChild(*fragment, error);
     rv = error.StealNSResult();
   }
 
@@ -5328,16 +5329,17 @@ nsContentUtils::ParseFragmentXML(const n
   if (aSanitize != NeverSanitize && !aDocument->AllowUnsafeHTML()) {
     // Don't fire mutation events for nodes removed by the sanitizer.
     nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
 
     RefPtr<DocumentFragment> fragment = static_cast<DocumentFragment*>(*aReturn);
 
     nsTreeSanitizer sanitizer(nsIParserUtils::SanitizerAllowStyle |
                               nsIParserUtils::SanitizerAllowComments |
+                              nsIParserUtils::SanitizerDropForms |
                               nsIParserUtils::SanitizerLogRemovals);
     sanitizer.Sanitize(fragment);
   }
 
   return rv;
 }
 
 /* static */
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -68,17 +68,16 @@
 #include "nsIDOMEventTarget.h"
 
 // CSS related includes
 #include "nsMemory.h"
 
 // includes needed for the prototype chain interfaces
 
 #include "nsIEventListenerService.h"
-#include "nsIMessageManager.h"
 
 #include "mozilla/dom/TouchEvent.h"
 
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/dom/HTMLCollectionBinding.h"
 
 #include "nsDebug.h"
 
@@ -126,19 +125,16 @@ using namespace mozilla::dom;
     _allowXBL,                                                                \
     false,                                                                    \
     NS_DEFINE_CLASSINFO_DATA_DEBUG(_class)                                    \
   },
 
 #define NS_DEFINE_CLASSINFO_DATA(_class, _helper, _flags)                     \
   NS_DEFINE_CLASSINFO_DATA_HELPER(_class, _helper, _flags, false, false)
 
-#define NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(_class, _helper, _flags)         \
-  NS_DEFINE_CLASSINFO_DATA_HELPER(_class, _helper, _flags, true, false)
-
 #define NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(_class, _helper, _flags)          \
   NS_DEFINE_CLASSINFO_DATA_HELPER(_class, _helper, _flags, true, true)
 
 
 // This list of NS_DEFINE_CLASSINFO_DATA macros is what gives the DOM
 // classes their correct behavior when used through XPConnect. The
 // arguments that are passed to NS_DEFINE_CLASSINFO_DATA are
 //
@@ -163,32 +159,16 @@ static nsDOMClassInfoData sClassInfoData
                            XPC_SCRIPTABLE_WANT_RESOLVE |
                            XPC_SCRIPTABLE_WANT_HASINSTANCE |
                            XPC_SCRIPTABLE_WANT_CALL |
                            XPC_SCRIPTABLE_WANT_CONSTRUCT |
                            XPC_SCRIPTABLE_DONT_ENUM_QUERY_INTERFACE)
 
   // Misc Core related classes
 
-  NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentFrameMessageManager,
-                                       nsMessageManagerSH<nsEventTargetSH>,
-                                       DOM_DEFAULT_SCRIPTABLE_FLAGS |
-                                       XPC_SCRIPTABLE_WANT_ENUMERATE |
-                                       XPC_SCRIPTABLE_IS_GLOBAL_OBJECT)
-  NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentProcessMessageManager,
-                                       nsMessageManagerSH<nsDOMGenericSH>,
-                                       DOM_DEFAULT_SCRIPTABLE_FLAGS |
-                                       XPC_SCRIPTABLE_WANT_ENUMERATE |
-                                       XPC_SCRIPTABLE_IS_GLOBAL_OBJECT)
-  NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageBroadcaster, nsDOMGenericSH,
-                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageSender, nsDOMGenericSH,
-                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
-
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULControlElement, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULLabeledControlElement, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULButtonElement, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULCheckboxElement, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -420,46 +400,16 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(DOMPrototype, nsIDOMDOMConstructor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMConstructor)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(DOMConstructor, nsIDOMDOMConstructor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMConstructor)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ContentFrameMessageManager, nsISupports)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
-    DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager)
-    DOM_CLASSINFO_MAP_ENTRY(nsIMessageSender)
-    DOM_CLASSINFO_MAP_ENTRY(nsISyncMessageSender)
-    DOM_CLASSINFO_MAP_ENTRY(nsIContentFrameMessageManager)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ContentProcessMessageManager, nsISupports)
-    DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager)
-    DOM_CLASSINFO_MAP_ENTRY(nsIMessageSender)
-    DOM_CLASSINFO_MAP_ENTRY(nsISyncMessageSender)
-    DOM_CLASSINFO_MAP_ENTRY(nsIContentProcessMessageManager)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ChromeMessageBroadcaster, nsISupports)
-    DOM_CLASSINFO_MAP_ENTRY(nsIFrameScriptLoader)
-    DOM_CLASSINFO_MAP_ENTRY(nsIProcessScriptLoader)
-    DOM_CLASSINFO_MAP_ENTRY(nsIGlobalProcessScriptLoader)
-    DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager)
-    DOM_CLASSINFO_MAP_ENTRY(nsIMessageBroadcaster)
-  DOM_CLASSINFO_MAP_END
-
-  DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ChromeMessageSender, nsISupports)
-    DOM_CLASSINFO_MAP_ENTRY(nsIFrameScriptLoader)
-    DOM_CLASSINFO_MAP_ENTRY(nsIProcessScriptLoader)
-    DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager)
-    DOM_CLASSINFO_MAP_ENTRY(nsIMessageSender)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XULControlElement, nsIDOMXULControlElement)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULControlElement)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XULLabeledControlElement, nsIDOMXULLabeledControlElement)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULLabeledControlElement)
   DOM_CLASSINFO_MAP_END
 
@@ -1917,46 +1867,8 @@ nsDOMConstructorSH::HasInstance(nsIXPCon
     nsCOMPtr<nsIDOMDOMConstructor> is_constructor =
       do_QueryWrappedNative(wrapper);
     NS_ASSERTION(is_constructor, "How did we not get a constructor?");
   }
 #endif
 
   return wrapped->HasInstance(wrapper, cx, obj, val, bp, _retval);
 }
-
-// nsContentFrameMessageManagerSH
-
-template<typename Super>
-NS_IMETHODIMP
-nsMessageManagerSH<Super>::Resolve(nsIXPConnectWrappedNative* wrapper,
-                                   JSContext* cx, JSObject* obj_,
-                                   jsid id_, bool* resolvedp,
-                                   bool* _retval)
-{
-  JS::Rooted<JSObject*> obj(cx, obj_);
-  JS::Rooted<jsid> id(cx, id_);
-
-  *_retval = SystemGlobalResolve(cx, obj, id, resolvedp);
-  NS_ENSURE_TRUE(*_retval, NS_ERROR_FAILURE);
-
-  if (*resolvedp) {
-    return NS_OK;
-  }
-
-  return Super::Resolve(wrapper, cx, obj, id, resolvedp, _retval);
-}
-
-template<typename Super>
-NS_IMETHODIMP
-nsMessageManagerSH<Super>::Enumerate(nsIXPConnectWrappedNative* wrapper,
-                                     JSContext* cx, JSObject* obj_,
-                                     bool* _retval)
-{
-  JS::Rooted<JSObject*> obj(cx, obj_);
-
-  *_retval = SystemGlobalEnumerate(cx, obj);
-  NS_ENSURE_TRUE(*_retval, NS_ERROR_FAILURE);
-
-  // Don't call up to our superclass, since neither nsDOMGenericSH nor
-  // nsEventTargetSH have WANT_ENUMERATE.
-  return NS_OK;
-}
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -207,34 +207,9 @@ public:
                          bool *_retval) override;
 
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsDOMConstructorSH(aData);
   }
 };
 
-template<typename Super>
-class nsMessageManagerSH : public Super
-{
-protected:
-  explicit nsMessageManagerSH(nsDOMClassInfoData* aData)
-    : Super(aData)
-  {
-  }
-
-  virtual ~nsMessageManagerSH()
-  {
-  }
-public:
-  NS_IMETHOD Resolve(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
-                     JSObject* obj_, jsid id_, bool* resolvedp,
-                     bool* _retval) override;
-  NS_IMETHOD Enumerate(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
-                       JSObject* obj_, bool* _retval) override;
-
-  static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
-  {
-    return new nsMessageManagerSH(aData);
-  }
-};
-
 #endif /* nsDOMClassInfo_h___ */
--- a/dom/base/nsDOMClassInfoID.h
+++ b/dom/base/nsDOMClassInfoID.h
@@ -14,21 +14,16 @@
 
 #include "nsIXPCScriptable.h"
 
 enum nsDOMClassInfoID
 {
   eDOMClassInfo_DOMPrototype_id,
   eDOMClassInfo_DOMConstructor_id,
 
-  eDOMClassInfo_ContentFrameMessageManager_id,
-  eDOMClassInfo_ContentProcessMessageManager_id,
-  eDOMClassInfo_ChromeMessageBroadcaster_id,
-  eDOMClassInfo_ChromeMessageSender_id,
-
   eDOMClassInfo_XULControlElement_id,
   eDOMClassInfo_XULLabeledControlElement_id,
   eDOMClassInfo_XULButtonElement_id,
   eDOMClassInfo_XULCheckboxElement_id,
 
   // This one better be the last one in this list
   eDOMClassInfoIDCount
 };
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -74,16 +74,17 @@
 #include "ContentParent.h"
 #include "TabParent.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
+#include "mozilla/dom/ChromeMessageSender.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/FrameLoaderBinding.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "nsGenericHTMLFrameElement.h"
 #include "GeckoProfiler.h"
 
 #include "jsapi.h"
@@ -1637,24 +1638,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 =
-      static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get());
+    nsInProcessTabChildGlobal* tabChild = mChildMessageManager;
     tabChild->SetOwner(otherContent);
     tabChild->SetChromeMessageManager(otherMessageManager);
   }
   if (aOther->mChildMessageManager) {
-    nsInProcessTabChildGlobal* otherTabChild =
-      static_cast<nsInProcessTabChildGlobal*>(aOther->mChildMessageManager.get());
+    nsInProcessTabChildGlobal* 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) {
@@ -1879,29 +1878,29 @@ nsFrameLoader::DestroyDocShell()
   // docshell, and finally destroy the PBrowser actor. This eventually leads to
   // nsFrameLoader::DestroyComplete being called.
   if (mRemoteBrowser) {
     mRemoteBrowser->Destroy();
   }
 
   // Fire the "unload" event if we're in-process.
   if (mChildMessageManager) {
-    static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->FireUnloadEvent();
+    mChildMessageManager->FireUnloadEvent();
   }
 
   // Destroy the docshell.
   nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
   if (base_win) {
     base_win->Destroy();
   }
   mDocShell = nullptr;
 
   if (mChildMessageManager) {
     // Stop handling events in the in-process frame script.
-    static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->DisconnectEventListeners();
+    mChildMessageManager->DisconnectEventListeners();
   }
 }
 
 void
 nsFrameLoader::DestroyComplete()
 {
   // We get here, as part of StartDestroy, after the docshell has been destroyed
   // and all message manager messages sent during docshell destruction have been
@@ -1926,17 +1925,17 @@ nsFrameLoader::DestroyComplete()
     mRemoteBrowser = nullptr;
   }
 
   if (mMessageManager) {
     mMessageManager->Disconnect();
   }
 
   if (mChildMessageManager) {
-    static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->Disconnect();
+    mChildMessageManager->Disconnect();
   }
 
   mMessageManager = nullptr;
   mChildMessageManager = nullptr;
 }
 
 NS_IMETHODIMP
 nsFrameLoader::GetDepthTooGreat(bool* aDepthTooGreat)
@@ -2982,22 +2981,21 @@ public:
     : nsSameProcessAsyncMessageBase(aRootingCx, aCpows)
     , mozilla::Runnable("nsAsyncMessageToChild")
     , mFrameLoader(aFrameLoader)
   {
   }
 
   NS_IMETHOD Run() override
   {
-    nsInProcessTabChildGlobal* tabChild =
-      static_cast<nsInProcessTabChildGlobal*>(mFrameLoader->mChildMessageManager.get());
+    nsInProcessTabChildGlobal* 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->GetGlobal());
+      JS::Rooted<JSObject*> kungFuDeathGrip(dom::RootingCx(), tabChild->GetWrapper());
       ReceiveMessage(static_cast<EventTarget*>(tabChild), mFrameLoader,
                      tabChild->GetInnerManager());
     }
     return NS_OK;
   }
   RefPtr<nsFrameLoader> mFrameLoader;
 };
 
@@ -3101,19 +3099,18 @@ nsFrameLoader::EnsureMessageManager()
 
     if (!parentManager) {
       chromeWindow->GetMessageManager(getter_AddRefs(parentManager));
     }
   } else {
     parentManager = do_GetService("@mozilla.org/globalmessagemanager;1");
   }
 
-  mMessageManager = new nsFrameMessageManager(nullptr,
-                                              static_cast<nsFrameMessageManager*>(parentManager.get()),
-                                              MM_CHROME);
+  mMessageManager = new ChromeMessageSender(nullptr,
+                                            static_cast<nsFrameMessageManager*>(parentManager.get()));
   if (!IsRemoteFrame()) {
     nsresult rv = MaybeCreateDocShell();
     if (NS_FAILED(rv)) {
       return rv;
     }
     NS_ASSERTION(mDocShell,
                  "MaybeCreateDocShell succeeded, but null mDocShell");
     if (!mDocShell) {
@@ -3136,17 +3133,17 @@ nsFrameLoader::ReallyLoadFrameScripts()
     mMessageManager->InitWithCallback(this);
   }
   return NS_OK;
 }
 
 EventTarget*
 nsFrameLoader::GetTabChildGlobalAsEventTarget()
 {
-  return static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get());
+  return mChildMessageManager.get();
 }
 
 already_AddRefed<Element>
 nsFrameLoader::GetOwnerElement()
 {
   nsCOMPtr<Element> element = do_QueryInterface(mOwnerContent);
   return element.forget();
 }
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -27,28 +27,29 @@
 #include "Units.h"
 #include "nsIWebBrowserPersistable.h"
 #include "nsIFrame.h"
 #include "nsPluginTags.h"
 
 class nsIURI;
 class nsSubDocumentFrame;
 class nsView;
-class nsIInProcessContentFrameMessageManager;
+class nsInProcessTabChildGlobal;
 class AutoResetInShow;
 class AutoResetInFrameSwap;
 class nsITabParent;
 class nsIDocShellTreeItem;
 class nsIDocShellTreeOwner;
 
 namespace mozilla {
 
 class OriginAttributes;
 
 namespace dom {
+class ChromeMessageSender;
 class ContentParent;
 class PBrowserParent;
 class Promise;
 class TabParent;
 class MutableTabContext;
 
 namespace ipc {
 class StructuredCloneData;
@@ -263,17 +264,17 @@ public:
    * constructed for this->GetURL().  This can happen, e.g., if the
    * <browser> was just navigated to a new URL, but hasn't painted the
    * new page yet.  A render frame for the previous page may be
    * returned.  (In-process <browser> behaves similarly, and this
    * behavior seems desirable.)
    */
   RenderFrameParent* GetCurrentRenderFrame() const;
 
-  nsFrameMessageManager* GetFrameMessageManager() { return mMessageManager; }
+  mozilla::dom::ChromeMessageSender* GetFrameMessageManager() { return mMessageManager; }
 
   mozilla::dom::Element* GetOwnerContent() { return mOwnerContent; }
   bool ShouldClipSubdocument() { return mClipSubdocument; }
 
   bool ShouldClampScrollPosition() { return mClampScrollPosition; }
 
   /**
    * Tell this FrameLoader to use a particular remote browser.
@@ -314,18 +315,18 @@ public:
   void GetURL(nsString& aURL, nsIPrincipal** aTriggeringPrincipal);
 
   // Properly retrieves documentSize of any subdocument type.
   nsresult GetWindowDimensions(nsIntRect& aRect);
 
   virtual nsIMessageSender* GetProcessMessageManager() const override;
 
   // public because a callback needs these.
-  RefPtr<nsFrameMessageManager> mMessageManager;
-  nsCOMPtr<nsIInProcessContentFrameMessageManager> mChildMessageManager;
+  RefPtr<mozilla::dom::ChromeMessageSender> mMessageManager;
+  RefPtr<nsInProcessTabChildGlobal> 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
@@ -7,54 +7,59 @@
 #include "base/basictypes.h"
 
 #include "nsFrameMessageManager.h"
 
 #include "ContentChild.h"
 #include "GeckoProfiler.h"
 #include "nsASCIIMask.h"
 #include "nsContentUtils.h"
-#include "nsDOMClassInfoID.h"
 #include "nsError.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "nsJSUtils.h"
 #include "nsJSPrincipals.h"
 #include "nsNetUtil.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "nsFrameLoader.h"
 #include "nsIInputStream.h"
 #include "nsIXULRuntime.h"
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "nsIMemoryReporter.h"
 #include "nsIProtocolHandler.h"
 #include "nsIScriptSecurityManager.h"
-#include "nsIDOMClassInfo.h"
 #include "xpcpublic.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/IntentionalCrash.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ScriptPreloader.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/DOMPrefs.h"
+#include "mozilla/dom/ChildProcessMessageManager.h"
+#include "mozilla/dom/ChromeMessageBroadcaster.h"
+#include "mozilla/dom/ChromeMessageSender.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/MessageManagerBinding.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/ProcessGlobal.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/StructuredCloneData.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "nsPrintfCString.h"
 #include "nsXULAppAPI.h"
 #include "nsQueryObject.h"
+#include "xpcprivate.h"
 #include <algorithm>
 #include "chrome/common/ipc_channel.h" // for IPC::Channel::kMaximumMessageSize
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 #ifdef XP_WIN
 #include <windows.h>
@@ -66,56 +71,39 @@
 #ifdef FUZZING
 #include "MessageManagerFuzzer.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
-nsFrameMessageManager::nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
-                                             nsFrameMessageManager* aParentManager,
-                                             /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags)
- : mChrome(!!(aFlags & mozilla::dom::ipc::MM_CHROME)),
-   mGlobal(!!(aFlags & mozilla::dom::ipc::MM_GLOBAL)),
-   mIsProcessManager(!!(aFlags & mozilla::dom::ipc::MM_PROCESSMANAGER)),
-   mIsBroadcaster(!!(aFlags & mozilla::dom::ipc::MM_BROADCASTER)),
-   mOwnsCallback(!!(aFlags & mozilla::dom::ipc::MM_OWNSCALLBACK)),
-  mHandlingMessage(false),
-  mClosed(false),
-  mDisconnected(false),
-  mCallback(aCallback),
-  mParentManager(aParentManager)
+nsFrameMessageManager::nsFrameMessageManager(MessageManagerCallback* aCallback,
+                                             MessageManagerFlags aFlags)
+ : mChrome(aFlags & MessageManagerFlags::MM_CHROME),
+   mGlobal(aFlags & MessageManagerFlags::MM_GLOBAL),
+   mIsProcessManager(aFlags & MessageManagerFlags::MM_PROCESSMANAGER),
+   mIsBroadcaster(aFlags & MessageManagerFlags::MM_BROADCASTER),
+   mOwnsCallback(aFlags & MessageManagerFlags::MM_OWNSCALLBACK),
+   mHandlingMessage(false),
+   mClosed(false),
+   mDisconnected(false),
+   mCallback(aCallback)
 {
-  NS_ASSERTION(mChrome || !aParentManager, "Should not set parent manager!");
   NS_ASSERTION(!mIsBroadcaster || !mCallback,
                "Broadcasters cannot have callbacks!");
-  if (mIsProcessManager && (!mChrome || IsBroadcaster())) {
-    mozilla::HoldJSObjects(this);
-  }
-  // This is a bit hackish. When parent manager is global, we want
-  // to attach the message manager to it immediately.
-  // Is it just the frame message manager which waits until the
-  // content process is running.
-  if (mParentManager && (mCallback || IsBroadcaster())) {
-    mParentManager->AddChildManager(this);
-  }
   if (mOwnsCallback) {
     mOwnedCallback = aCallback;
   }
 }
 
 nsFrameMessageManager::~nsFrameMessageManager()
 {
-  if (mIsProcessManager && (!mChrome || IsBroadcaster())) {
-    mozilla::DropJSObjects(this);
-  }
-  for (int32_t i = mChildManagers.Count(); i > 0; --i) {
-    static_cast<nsFrameMessageManager*>(mChildManagers[i - 1])->
-      Disconnect(false);
+  for (int32_t i = mChildManagers.Length(); i > 0; --i) {
+    mChildManagers[i - 1]->Disconnect(false);
   }
   if (mIsProcessManager) {
     if (this == sParentProcessManager) {
       sParentProcessManager = nullptr;
     }
     if (this == sChildProcessManager) {
       sChildProcessManager = nullptr;
       delete mozilla::dom::SameProcessMessageQueue::Get();
@@ -124,40 +112,30 @@ nsFrameMessageManager::~nsFrameMessageMa
       sSameProcessParentManager = nullptr;
     }
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
-  for (auto iter = tmp->mListeners.Iter(); !iter.Done(); iter.Next()) {
-    nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = iter.UserData();
-    uint32_t count = listeners->Length();
-    for (uint32_t i = 0; i < count; ++i) {
-      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "listeners[i] mStrongListener");
-      cb.NoteXPCOMChild(listeners->ElementAt(i).mStrongListener.get());
-    }
-  }
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentManager)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsFrameMessageManager)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mInitialProcessData)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager)
-  tmp->mListeners.Clear();
-  for (int32_t i = tmp->mChildManagers.Count(); i > 0; --i) {
-    static_cast<nsFrameMessageManager*>(tmp->mChildManagers[i - 1])->
-      Disconnect(false);
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners)
+  for (int32_t i = tmp->mChildManagers.Length(); i > 0; --i) {
+    tmp->mChildManagers[i - 1]->Disconnect(false);
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildManagers)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentManager)
   tmp->mInitialProcessData.setNull();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentFrameMessageManager)
 
   /* nsFrameMessageManager implements nsIMessageSender and nsIMessageBroadcaster,
@@ -190,20 +168,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   /* Process message managers (process message managers) support nsIProcessScriptLoader. */
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIProcessScriptLoader,
                                      mChrome && mIsProcessManager)
 
   /* Global process message managers (process message managers) support nsIGlobalProcessScriptLoader. */
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIGlobalProcessScriptLoader,
                                      mChrome && mIsProcessManager && mIsBroadcaster)
 
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageBroadcaster,
-                                                   mChrome && mIsBroadcaster)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageSender,
-                                                   mChrome && !mIsBroadcaster)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager)
 
 bool
 MessageManagerCallback::BuildClonedMessageDataForParent(nsIContentParent* aParent,
                                                         StructuredCloneData& aData,
@@ -243,56 +217,105 @@ SameProcessCpowHolder::ToObject(JSContex
   }
 
   aObjp.set(mObj);
   return JS_WrapObject(aCx, aObjp);
 }
 
 // nsIMessageListenerManager
 
+void
+nsFrameMessageManager::AddMessageListener(const nsAString& aMessageName,
+                                          MessageListener& aListener,
+                                          bool aListenWhenClosed,
+                                          ErrorResult& aError)
+{
+  AddMessageListener(aMessageName, MessageListenerHolder(&aListener), aListenWhenClosed);
+}
+
 NS_IMETHODIMP
 nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
                                           nsIMessageListener* aListener,
                                           bool aListenWhenClosed)
 {
+  AddMessageListener(aMessage, MessageListenerHolder(aListener), aListenWhenClosed);
+  return NS_OK;
+}
+
+void
+nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
+                                          MessageListenerHolder&& aListener,
+                                          bool aListenWhenClosed)
+{
   auto listeners = mListeners.LookupForAdd(aMessage).OrInsert([]() {
       return new nsAutoTObserverArray<nsMessageListenerInfo, 1>();
     });
   uint32_t len = listeners->Length();
   for (uint32_t i = 0; i < len; ++i) {
     if (listeners->ElementAt(i).mStrongListener == aListener) {
-      return NS_OK;
+      return;
     }
   }
 
   nsMessageListenerInfo* entry = listeners->AppendElement();
-  NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
-  entry->mStrongListener = aListener;
+  entry->mStrongListener = Move(aListener);
   entry->mListenWhenClosed = aListenWhenClosed;
-  return NS_OK;
+}
+
+void
+nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessageName,
+                                             MessageListener& aListener,
+                                             ErrorResult& aError)
+{
+  RemoveMessageListener(aMessageName, MessageListenerHolder(&aListener));
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage,
                                              nsIMessageListener* aListener)
 {
+  RemoveMessageListener(aMessage, MessageListenerHolder(aListener));
+  return NS_OK;
+}
+
+void
+nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage,
+                                             const MessageListenerHolder& aListener)
+{
   nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
     mListeners.Get(aMessage);
-  if (!listeners) {
-    return NS_OK;
+  if (listeners) {
+    uint32_t len = listeners->Length();
+    for (uint32_t i = 0; i < len; ++i) {
+      if (listeners->ElementAt(i).mStrongListener == aListener) {
+        listeners->RemoveElementAt(i);
+        return;
+      }
+    }
+  }
+}
+
+static already_AddRefed<nsIMessageListener>
+ToXPCOMMessageListener(MessageListener& aListener)
+{
+  return MessageListenerHolder(&aListener).ToXPCOMCallback();
+}
+
+void
+nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessageName,
+                                              MessageListener& aListener,
+                                              ErrorResult& aError)
+{
+  nsCOMPtr<nsIMessageListener> listener(ToXPCOMMessageListener(aListener));
+  if (!listener) {
+    aError.Throw(NS_ERROR_FAILURE);
+    return;
   }
 
-  uint32_t len = listeners->Length();
-  for (uint32_t i = 0; i < len; ++i) {
-    if (listeners->ElementAt(i).mStrongListener == aListener) {
-      listeners->RemoveElementAt(i);
-      return NS_OK;
-    }
-  }
-  return NS_OK;
+  aError = AddWeakMessageListener(aMessageName, listener);
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage,
                                               nsIMessageListener* aListener)
 {
   nsWeakPtr weak = do_GetWeakReference(aListener);
   NS_ENSURE_TRUE(weak, NS_ERROR_NO_INTERFACE);
@@ -327,16 +350,30 @@ nsFrameMessageManager::AddWeakMessageLis
   }
 
   nsMessageListenerInfo* entry = listeners->AppendElement();
   entry->mWeakListener = weak;
   entry->mListenWhenClosed = false;
   return NS_OK;
 }
 
+void
+nsFrameMessageManager::RemoveWeakMessageListener(const nsAString& aMessageName,
+                                                 MessageListener& aListener,
+                                                 ErrorResult& aError)
+{
+  nsCOMPtr<nsIMessageListener> listener(ToXPCOMMessageListener(aListener));
+  if (!listener) {
+    aError.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  aError = RemoveWeakMessageListener(aMessageName, listener);
+}
+
 NS_IMETHODIMP
 nsFrameMessageManager::RemoveWeakMessageListener(const nsAString& aMessage,
                                                  nsIMessageListener* aListener)
 {
   nsWeakPtr weak = do_GetWeakReference(aListener);
   NS_ENSURE_TRUE(weak, NS_OK);
 
   nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
@@ -353,130 +390,151 @@ nsFrameMessageManager::RemoveWeakMessage
     }
   }
 
   return NS_OK;
 }
 
 // nsIFrameScriptLoader
 
-NS_IMETHODIMP
+void
 nsFrameMessageManager::LoadScript(const nsAString& aURL,
                                   bool aAllowDelayedLoad,
-                                  bool aRunInGlobalScope)
+                                  bool aRunInGlobalScope,
+                                  ErrorResult& aError)
 {
   if (aAllowDelayedLoad) {
     // Cache for future windows or frames
     mPendingScripts.AppendElement(aURL);
     mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope);
   }
 
   if (mCallback) {
 #ifdef DEBUG_smaug
     printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL).get());
 #endif
-    NS_ENSURE_TRUE(mCallback->DoLoadMessageManagerScript(aURL, aRunInGlobalScope),
-                   NS_ERROR_FAILURE);
+    if (!mCallback->DoLoadMessageManagerScript(aURL, aRunInGlobalScope)) {
+      aError.Throw(NS_ERROR_FAILURE);
+      return;
+    }
   }
 
-  for (int32_t i = 0; i < mChildManagers.Count(); ++i) {
-    RefPtr<nsFrameMessageManager> mm =
-      static_cast<nsFrameMessageManager*>(mChildManagers[i]);
+  for (uint32_t i = 0; i < mChildManagers.Length(); ++i) {
+    RefPtr<nsFrameMessageManager> mm = mChildManagers[i];
     if (mm) {
       // Use false here, so that child managers don't cache the script, which
       // is already cached in the parent.
-      mm->LoadScript(aURL, false, aRunInGlobalScope);
+      mm->LoadScript(aURL, false, aRunInGlobalScope, IgnoreErrors());
     }
   }
-  return NS_OK;
 }
 
-NS_IMETHODIMP
+void
 nsFrameMessageManager::RemoveDelayedScript(const nsAString& aURL)
 {
   for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
     if (mPendingScripts[i] == aURL) {
       mPendingScripts.RemoveElementAt(i);
       mPendingScriptsGlobalStates.RemoveElementAt(i);
       break;
     }
   }
-  return NS_OK;
 }
 
-NS_IMETHODIMP
-nsFrameMessageManager::GetDelayedScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList)
+void
+nsFrameMessageManager::GetDelayedScripts(JSContext* aCx,
+                                         nsTArray<nsTArray<JS::Value>>& aList,
+                                         ErrorResult& aError)
 {
   // Frame message managers may return an incomplete list because scripts
   // that were loaded after it was connected are not added to the list.
   if (!IsGlobal() && !IsBroadcaster()) {
     NS_WARNING("Cannot retrieve list of pending frame scripts for frame"
                "message managers as it may be incomplete");
-    return NS_ERROR_NOT_IMPLEMENTED;
+    aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
+    return;
   }
 
-  JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, mPendingScripts.Length()));
-  NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
+  aError.MightThrowJSException();
 
-  JS::Rooted<JSString*> url(aCx);
-  JS::Rooted<JSObject*> pair(aCx);
+  aList.SetCapacity(mPendingScripts.Length());
   for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
-    url = JS_NewUCStringCopyN(aCx, mPendingScripts[i].get(), mPendingScripts[i].Length());
-    NS_ENSURE_TRUE(url, NS_ERROR_OUT_OF_MEMORY);
+    JS::Rooted<JS::Value> url(aCx);
+    if (!ToJSValue(aCx, mPendingScripts[i], &url)) {
+      aError.NoteJSContextException(aCx);
+      return;
+    }
 
-    JS::AutoValueArray<2> pairElts(aCx);
-    pairElts[0].setString(url);
-    pairElts[1].setBoolean(mPendingScriptsGlobalStates[i]);
+    nsTArray<JS::Value>* array = aList.AppendElement(2);
+    array->AppendElement(url);
+    array->AppendElement(JS::BooleanValue(mPendingScriptsGlobalStates[i]));
+  }
+}
 
-    pair = JS_NewArrayObject(aCx, pairElts);
-    NS_ENSURE_TRUE(pair, NS_ERROR_OUT_OF_MEMORY);
-
-    NS_ENSURE_TRUE(JS_DefineElement(aCx, array, i, pair, JSPROP_ENUMERATE),
-                   NS_ERROR_OUT_OF_MEMORY);
+nsresult
+nsFrameMessageManager::GetDelayedScripts(JSContext* aCx,
+                                         JS::MutableHandle<JS::Value> aList)
+{
+  ErrorResult rv;
+  nsTArray<nsTArray<JS::Value>> list;
+  SequenceRooter<nsTArray<JS::Value>> listRooter(aCx, &list);
+  GetDelayedScripts(aCx, list, rv);
+  rv.WouldReportJSException();
+  if (rv.Failed()) {
+    return rv.StealNSResult();
   }
 
-  aList.setObject(*array);
+  if (!ToJSValue(aCx, list, aList)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
   return NS_OK;
 }
 
 // nsIFrameScriptLoader
 
 NS_IMETHODIMP
 nsFrameMessageManager::LoadFrameScript(const nsAString& aURL,
                                        bool aAllowDelayedLoad,
                                        bool aRunInGlobalScope)
 {
-  return LoadScript(aURL, aAllowDelayedLoad, aRunInGlobalScope);
+  ErrorResult rv;
+  LoadScript(aURL, aAllowDelayedLoad, aRunInGlobalScope, rv);
+  return rv.StealNSResult();
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::RemoveDelayedFrameScript(const nsAString& aURL)
 {
-  return RemoveDelayedScript(aURL);
+  RemoveDelayedScript(aURL);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::GetDelayedFrameScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList)
 {
   return GetDelayedScripts(aCx, aList);
 }
 
 // nsIProcessScriptLoader
 
 NS_IMETHODIMP
 nsFrameMessageManager::LoadProcessScript(const nsAString& aURL,
                                          bool aAllowDelayedLoad)
 {
-  return LoadScript(aURL, aAllowDelayedLoad, false);
+  ErrorResult rv;
+  LoadScript(aURL, aAllowDelayedLoad, false, rv);
+  return rv.StealNSResult();
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::RemoveDelayedProcessScript(const nsAString& aURL)
 {
-  return RemoveDelayedScript(aURL);
+  RemoveDelayedScript(aURL);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::GetDelayedProcessScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList)
 {
   return GetDelayedScripts(aCx, aList);
 }
 
@@ -599,57 +657,120 @@ nsFrameMessageManager::SendMessage(const
                                    JSContext* aCx,
                                    uint8_t aArgc,
                                    JS::MutableHandle<JS::Value> aRetval,
                                    bool aIsSync)
 {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
     "nsFrameMessageManager::SendMessage", EVENTS, aMessageName);
 
-  NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
-  NS_ASSERTION(!IsBroadcaster(), "Should not call SendSyncMessage in chrome");
-  NS_ASSERTION(!mParentManager, "Should not have parent manager in content!");
-
   aRetval.setUndefined();
-  NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
 
   if (sSendingSyncMessage && aIsSync) {
     // No kind of blocking send should be issued on top of a sync message.
     return NS_ERROR_UNEXPECTED;
   }
 
   StructuredCloneData data;
   if (aArgc >= 2 && !GetParamsForMessage(aCx, aJSON, JS::UndefinedHandleValue, data)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
+  JS::Rooted<JSObject*> objects(aCx);
+  if (aArgc >= 3 && aObjects.isObject()) {
+    objects = &aObjects.toObject();
+  }
+
+  nsTArray<JS::Value> result;
+  SequenceRooter<JS::Value> resultRooter(aCx, &result);
+  ErrorResult rv;
+  SendMessage(aCx, aMessageName, data, objects, aPrincipal, aIsSync, result, rv);
+  rv.WouldReportJSException();
+  if (rv.Failed()) {
+    return rv.StealNSResult();
+  }
+
+  JS::Rooted<JSObject*> dataArray(aCx);
+  if (!ToJSValue(aCx, result, aRetval)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+void
+nsFrameMessageManager::SendMessage(JSContext* aCx,
+                                   const nsAString& aMessageName,
+                                   JS::Handle<JS::Value> aObj,
+                                   JS::Handle<JSObject*> aObjects,
+                                   nsIPrincipal* aPrincipal,
+                                   bool aIsSync,
+                                   nsTArray<JS::Value>& aResult,
+                                   ErrorResult& aError)
+{
+  AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
+    "nsFrameMessageManager::SendMessage", EVENTS, aMessageName);
+
+  if (sSendingSyncMessage && aIsSync) {
+    // No kind of blocking send should be issued on top of a sync message.
+    aError.Throw(NS_ERROR_UNEXPECTED);
+    return;
+  }
+
+  StructuredCloneData data;
+  if (!aObj.isUndefined() &&
+      !GetParamsForMessage(aCx, aObj, JS::UndefinedHandleValue, data)) {
+    aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
+    return;
+  }
+
+  SendMessage(aCx, aMessageName, data, aObjects, aPrincipal, aIsSync, aResult,
+              aError);
+}
+
+void
+nsFrameMessageManager::SendMessage(JSContext* aCx,
+                                   const nsAString& aMessageName,
+                                   StructuredCloneData& aData,
+                                   JS::Handle<JSObject*> aObjects,
+                                   nsIPrincipal* aPrincipal,
+                                   bool aIsSync,
+                                   nsTArray<JS::Value>& aResult,
+                                   ErrorResult& aError)
+{
+  NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
+  NS_ASSERTION(!IsBroadcaster(), "Should not call SendSyncMessage in chrome");
+  NS_ASSERTION(!GetParentManager(),
+               "Should not have parent manager in content!");
+
+  if (!AllowMessage(aData.DataLength(), aMessageName)) {
+    aError.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
 #ifdef FUZZING
-  if (data.DataLength() > 0) {
+  if (aData.DataLength() > 0) {
     MessageManagerFuzzer::TryMutate(
       aCx,
       aMessageName,
-      &data,
+      &aData,
       JS::UndefinedHandleValue);
   }
 #endif
 
-  if (!AllowMessage(data.DataLength(), aMessageName)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  JS::Rooted<JSObject*> objects(aCx);
-  if (aArgc >= 3 && aObjects.isObject()) {
-    objects = &aObjects.toObject();
+  if (!mCallback) {
+    aError.Throw(NS_ERROR_NOT_INITIALIZED);
+    return;
   }
 
   nsTArray<StructuredCloneData> retval;
 
   TimeStamp start = TimeStamp::Now();
   sSendingSyncMessage |= aIsSync;
-  bool ok = mCallback->DoSendBlockingMessage(aCx, aMessageName, data, objects,
+  bool ok = mCallback->DoSendBlockingMessage(aCx, aMessageName, aData, aObjects,
                                              aPrincipal, &retval, aIsSync);
   if (aIsSync) {
     sSendingSyncMessage = false;
   }
 
   uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
   if (latencyMs >= kMinTelemetrySyncMessageManagerLatencyMs) {
     NS_ConvertUTF16toUTF8 messageName(aMessageName);
@@ -657,52 +778,43 @@ nsFrameMessageManager::SendMessage(const
     // avoid a large number of buckets due to generated names from addons (such
     // as "ublock:sb:{N}"). See bug 1348113 comment 10.
     messageName.StripTaggedASCII(ASCIIMask::Mask0to9());
     Telemetry::Accumulate(Telemetry::IPC_SYNC_MESSAGE_MANAGER_LATENCY_MS,
                           messageName, latencyMs);
   }
 
   if (!ok) {
-    return NS_OK;
+    return;
   }
 
   uint32_t len = retval.Length();
-  JS::Rooted<JSObject*> dataArray(aCx, JS_NewArrayObject(aCx, len));
-  NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
-
+  aResult.SetCapacity(len);
   for (uint32_t i = 0; i < len; ++i) {
     JS::Rooted<JS::Value> ret(aCx);
-    ErrorResult rv;
-    retval[i].Read(aCx, &ret, rv);
-    if (rv.Failed()) {
+    retval[i].Read(aCx, &ret, aError);
+    if (aError.Failed()) {
       MOZ_ASSERT(false, "Unable to read structured clone in SendMessage");
-      rv.SuppressException();
-      return NS_ERROR_UNEXPECTED;
+      return;
     }
-
-    NS_ENSURE_TRUE(JS_DefineElement(aCx, dataArray, i, ret, JSPROP_ENUMERATE),
-                   NS_ERROR_OUT_OF_MEMORY);
+    aResult.AppendElement(ret);
   }
-
-  aRetval.setObject(*dataArray);
-  return NS_OK;
 }
 
 nsresult
 nsFrameMessageManager::DispatchAsyncMessageInternal(JSContext* aCx,
                                                     const nsAString& aMessage,
                                                     StructuredCloneData& aData,
                                                     JS::Handle<JSObject *> aCpows,
                                                     nsIPrincipal* aPrincipal)
 {
   if (mIsBroadcaster) {
-    int32_t len = mChildManagers.Count();
-    for (int32_t i = 0; i < len; ++i) {
-      static_cast<nsFrameMessageManager*>(mChildManagers[i])->
+    uint32_t len = mChildManagers.Length();
+    for (uint32_t i = 0; i < len; ++i) {
+      mChildManagers[i]->
          DispatchAsyncMessageInternal(aCx, aMessage, aData, aCpows, aPrincipal);
     }
     return NS_OK;
   }
 
   if (!mCallback) {
     return NS_ERROR_NOT_INITIALIZED;
   }
@@ -742,16 +854,35 @@ nsFrameMessageManager::DispatchAsyncMess
   if (aArgc >= 3 && aObjects.isObject()) {
     objects = &aObjects.toObject();
   }
 
   return DispatchAsyncMessageInternal(aCx, aMessageName, data, objects,
                                       aPrincipal);
 }
 
+void
+nsFrameMessageManager::DispatchAsyncMessage(JSContext* aCx,
+                                            const nsAString& aMessageName,
+                                            JS::Handle<JS::Value> aObj,
+                                            JS::Handle<JSObject*> aObjects,
+                                            nsIPrincipal* aPrincipal,
+                                            JS::Handle<JS::Value> aTransfers,
+                                            ErrorResult& aError)
+{
+  StructuredCloneData data;
+  if (!aObj.isUndefined() && !GetParamsForMessage(aCx, aObj, aTransfers, data)) {
+    aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
+    return;
+  }
+
+  aError = DispatchAsyncMessageInternal(aCx, aMessageName, data, aObjects,
+                                        aPrincipal);
+}
+
 // nsIMessageSender
 
 NS_IMETHODIMP
 nsFrameMessageManager::SendAsyncMessage(const nsAString& aMessageName,
                                         JS::Handle<JS::Value> aJSON,
                                         JS::Handle<JS::Value> aObjects,
                                         nsIPrincipal* aPrincipal,
                                         JS::Handle<JS::Value> aTransfers,
@@ -774,28 +905,30 @@ nsFrameMessageManager::BroadcastAsyncMes
 {
   return DispatchAsyncMessage(aMessageName, aJSON, aObjects, nullptr,
                               JS::UndefinedHandleValue, aCx, aArgc);
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::GetChildCount(uint32_t* aChildCount)
 {
-  *aChildCount = static_cast<uint32_t>(mChildManagers.Count());
+  *aChildCount = mChildManagers.Length();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::GetChildAt(uint32_t aIndex,
                                   nsIMessageListenerManager** aMM)
 {
-  *aMM = nullptr;
-  nsCOMPtr<nsIMessageListenerManager> mm =
-    do_QueryInterface(mChildManagers.SafeObjectAt(static_cast<uint32_t>(aIndex)));
-  mm.swap(*aMM);
+  MessageListenerManager* mm = mChildManagers.SafeElementAt(aIndex);
+  if (mm) {
+    CallQueryInterface(mm, aMM);
+  } else {
+    *aMM = nullptr;
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::ReleaseCachedProcesses()
 {
   ContentParent::ReleaseCachedProcesses();
   return NS_OK;
@@ -914,16 +1047,18 @@ nsFrameMessageManager::ReceiveMessage(ns
                                       bool aTargetClosed,
                                       const nsAString& aMessage,
                                       bool aIsSync,
                                       StructuredCloneData* aCloneData,
                                       mozilla::jsipc::CpowHolder* aCpows,
                                       nsIPrincipal* aPrincipal,
                                       nsTArray<StructuredCloneData>* aRetVal)
 {
+  MOZ_ASSERT(aTarget);
+
   nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
     mListeners.Get(aMessage);
   if (listeners) {
 
     MMListenerRemover lr(this);
 
     nsAutoTObserverArray<nsMessageListenerInfo, 1>::EndLimitedIterator
       iter(*listeners);
@@ -938,228 +1073,220 @@ nsFrameMessageManager::ReceiveMessage(ns
           continue;
         }
       }
 
       if (!listener.mListenWhenClosed && aTargetClosed) {
         continue;
       }
 
-      nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
-      if (weakListener) {
-        wrappedJS = do_QueryInterface(weakListener);
+      JS::RootingContext* rcx = RootingCx();
+      JS::Rooted<JSObject*> object(rcx);
+
+      RefPtr<MessageListener> webIDLListener;
+      if (!weakListener && listener.mStrongListener.HasWebIDLCallback()) {
+        webIDLListener = listener.mStrongListener.GetWebIDLCallback();
       } else {
-        wrappedJS = do_QueryInterface(listener.mStrongListener);
+        webIDLListener = nullptr;
       }
 
-      if (!wrappedJS) {
+      if (webIDLListener) {
+        object = webIDLListener->CallbackOrNull();
+      } else {
+        nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
+        if (weakListener) {
+          wrappedJS = do_QueryInterface(weakListener);
+        } else {
+          wrappedJS = do_QueryInterface(listener.mStrongListener.GetXPCOMCallback());
+        }
+
+        if (!wrappedJS) {
+          continue;
+        }
+
+        object = wrappedJS->GetJSObject();
+      }
+
+      if (!object) {
         continue;
       }
 
-      if (!wrappedJS->GetJSObject()) {
-        continue;
-      }
-
-      AutoEntryScript aes(wrappedJS->GetJSObject(), "message manager handler");
+      AutoEntryScript aes(object, "message manager handler");
       JSContext* cx = aes.cx();
-      JS::Rooted<JSObject*> object(cx, wrappedJS->GetJSObject());
 
-      // The parameter for the listener function.
-      JS::Rooted<JSObject*> param(cx, JS_NewPlainObject(cx));
-      NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY);
-
-      JS::Rooted<JS::Value> targetv(cx);
-      js::AssertSameCompartment(cx, object);
-      nsresult rv = nsContentUtils::WrapNative(cx, aTarget, &targetv);
-      NS_ENSURE_SUCCESS(rv, rv);
+      RootedDictionary<ReceiveMessageArgument> argument(cx);
 
       JS::Rooted<JSObject*> cpows(cx);
       if (aCpows) {
         if (!aCpows->ToObject(cx, &cpows)) {
           return NS_ERROR_UNEXPECTED;
         }
       }
 
       if (!cpows) {
         cpows = JS_NewPlainObject(cx);
         if (!cpows) {
           return NS_ERROR_UNEXPECTED;
         }
       }
-
-      JS::Rooted<JS::Value> cpowsv(cx, JS::ObjectValue(*cpows));
+      argument.mObjects = cpows;
 
       JS::Rooted<JS::Value> json(cx, JS::NullValue());
       if (aCloneData && aCloneData->DataLength()) {
         ErrorResult rv;
         aCloneData->Read(cx, &json, rv);
         if (NS_WARN_IF(rv.Failed())) {
           rv.SuppressException();
           JS_ClearPendingException(cx);
           return NS_OK;
         }
       }
+      argument.mData = json;
+      argument.mJson = json;
 
       // Get cloned MessagePort from StructuredCloneData.
-      nsTArray<RefPtr<mozilla::dom::MessagePort>> ports;
       if (aCloneData) {
-        ports = aCloneData->TakeTransferredPorts();
-      }
-
-      JS::Rooted<JS::Value> transferredList(cx);
-      if (NS_WARN_IF(!ToJSValue(cx, ports, &transferredList))) {
-        return NS_ERROR_UNEXPECTED;
+        Sequence<OwningNonNull<MessagePort>> ports;
+        if (!aCloneData->TakeTransferredPortsAsSequence(ports)) {
+          return NS_ERROR_FAILURE;
+        }
+        argument.mPorts.Construct(Move(ports));
       }
 
-      JS::Rooted<JSString*> jsMessage(cx,
-        JS_NewUCStringCopyN(cx,
-                            static_cast<const char16_t*>(aMessage.BeginReading()),
-                            aMessage.Length()));
-      NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
-      JS::Rooted<JS::Value> syncv(cx, JS::BooleanValue(aIsSync));
-      bool ok = JS_DefineProperty(cx, param, "target", targetv, JSPROP_ENUMERATE) &&
-                JS_DefineProperty(cx, param, "name", jsMessage, JSPROP_ENUMERATE) &&
-                JS_DefineProperty(cx, param, "sync", syncv, JSPROP_ENUMERATE) &&
-                JS_DefineProperty(cx, param, "json", json, JSPROP_ENUMERATE) && // deprecated
-                JS_DefineProperty(cx, param, "data", json, JSPROP_ENUMERATE) &&
-                JS_DefineProperty(cx, param, "objects", cpowsv, JSPROP_ENUMERATE) &&
-                JS_DefineProperty(cx, param, "ports", transferredList, JSPROP_ENUMERATE);
-
-      NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
-
+      argument.mName = aMessage;
+      argument.mPrincipal = aPrincipal;
+      argument.mSync = aIsSync;
+      argument.mTarget = aTarget;
       if (aTargetFrameLoader) {
-        JS::Rooted<JS::Value> targetFrameLoaderv(cx);
-        nsresult rv = nsContentUtils::WrapNative(cx, aTargetFrameLoader, &targetFrameLoaderv);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        ok = JS_DefineProperty(cx, param, "targetFrameLoader", targetFrameLoaderv,
-                               JSPROP_ENUMERATE);
-        NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
-      }
-
-      // message.principal == null
-      if (!aPrincipal) {
-        bool ok = JS_DefineProperty(cx, param, "principal",
-                                    JS::UndefinedHandleValue, JSPROP_ENUMERATE);
-        NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
-      }
-
-      // message.principal = the principal
-      else {
-        JS::Rooted<JS::Value> principalValue(cx);
-        nsresult rv = nsContentUtils::WrapNative(cx, aPrincipal,
-                                                 &NS_GET_IID(nsIPrincipal),
-                                                 &principalValue);
-        NS_ENSURE_SUCCESS(rv, rv);
-        bool ok = JS_DefineProperty(cx, param, "principal", principalValue,
-                                    JSPROP_ENUMERATE);
-        NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
+        argument.mTargetFrameLoader.Construct(aTargetFrameLoader);
       }
 
       JS::Rooted<JS::Value> thisValue(cx, JS::UndefinedValue());
 
-      JS::Rooted<JS::Value> funval(cx);
       if (JS::IsCallable(object)) {
-        // If the listener is a JS function:
-        funval.setObject(*object);
-
         // A small hack to get 'this' value right on content side where
         // messageManager is wrapped in TabChildGlobal.
         nsCOMPtr<nsISupports> defaultThisValue;
         if (mChrome) {
           defaultThisValue = do_QueryObject(this);
         } else {
           defaultThisValue = aTarget;
         }
         js::AssertSameCompartment(cx, object);
         nsresult rv = nsContentUtils::WrapNative(cx, defaultThisValue, &thisValue);
         NS_ENSURE_SUCCESS(rv, rv);
-      } else {
-        // If the listener is a JS object which has receiveMessage function:
-        if (!JS_GetProperty(cx, object, "receiveMessage", &funval) ||
-            !funval.isObject()) {
-          return NS_ERROR_UNEXPECTED;
-        }
-
-        // Check if the object is even callable.
-        NS_ENSURE_STATE(JS::IsCallable(&funval.toObject()));
-        thisValue.setObject(*object);
       }
 
       JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());
-      JS::Rooted<JS::Value> argv(cx, JS::ObjectValue(*param));
+      if (webIDLListener) {
+        ErrorResult rv;
+        webIDLListener->ReceiveMessage(thisValue, argument, &rval, rv);
+        if (rv.Failed()) {
+          // At this point the call to ReceiveMessage will have reported any exceptions
+          // (we kept the default of eReportExceptions). We suppress the failure in the
+          // ErrorResult and continue.
+          rv.SuppressException();
+          continue;
+        }
+      } else {
+        JS::Rooted<JS::Value> funval(cx);
+        if (JS::IsCallable(object)) {
+          // If the listener is a JS function:
+          funval.setObject(*object);
+        } else {
+          // If the listener is a JS object which has receiveMessage function:
+          if (!JS_GetProperty(cx, object, "receiveMessage", &funval) ||
+              !funval.isObject()) {
+            return NS_ERROR_UNEXPECTED;
+          }
 
-      {
-        JS::Rooted<JSObject*> thisObject(cx, thisValue.toObjectOrNull());
+          // Check if the object is even callable.
+          NS_ENSURE_STATE(JS::IsCallable(&funval.toObject()));
+          thisValue.setObject(*object);
+        }
 
-        JSAutoCompartment tac(cx, thisObject);
-        if (!JS_WrapValue(cx, &argv)) {
+        JS::Rooted<JS::Value> argv(cx);
+        if (!ToJSValue(cx, argument, &argv)) {
           return NS_ERROR_UNEXPECTED;
         }
 
-        if (!JS_CallFunctionValue(cx, thisObject, funval,
-                                  JS::HandleValueArray(argv), &rval)) {
-          continue;
-        }
-        if (aRetVal) {
-          ErrorResult rv;
-          StructuredCloneData* data = aRetVal->AppendElement();
-          data->Write(cx, rval, rv);
-          if (NS_WARN_IF(rv.Failed())) {
-            aRetVal->RemoveElementAt(aRetVal->Length() - 1);
-            nsString msg = aMessage + NS_LITERAL_STRING(": message reply cannot be cloned. Are you trying to send an XPCOM object?");
-
-            nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
-            if (console) {
-              nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
-              error->Init(msg, EmptyString(), EmptyString(),
-                          0, 0, nsIScriptError::warningFlag, "chrome javascript");
-              console->LogMessage(error);
-            }
-
-            JS_ClearPendingException(cx);
+        {
+          JS::Rooted<JSObject*> thisObject(cx, thisValue.toObjectOrNull());
+          js::AssertSameCompartment(cx, thisObject);
+          if (!JS_CallFunctionValue(cx, thisObject, funval,
+                                    JS::HandleValueArray(argv), &rval)) {
+            // Because the AutoEntryScript is inside the loop this continue will make us
+            // report any exceptions (after which we'll move on to the next listener).
             continue;
           }
         }
       }
+
+      if (aRetVal) {
+        ErrorResult rv;
+        StructuredCloneData* data = aRetVal->AppendElement();
+        data->Write(cx, rval, rv);
+        if (NS_WARN_IF(rv.Failed())) {
+          aRetVal->RemoveElementAt(aRetVal->Length() - 1);
+          nsString msg = aMessage + NS_LITERAL_STRING(": message reply cannot be cloned. Are you trying to send an XPCOM object?");
+
+          nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+          if (console) {
+            nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
+            error->Init(msg, EmptyString(), EmptyString(),
+                        0, 0, nsIScriptError::warningFlag, "chrome javascript");
+            console->LogMessage(error);
+          }
+
+          JS_ClearPendingException(cx);
+          continue;
+        }
+      }
     }
   }
 
-  RefPtr<nsFrameMessageManager> kungFuDeathGrip = mParentManager;
+  RefPtr<nsFrameMessageManager> kungFuDeathGrip = GetParentManager();
   if (kungFuDeathGrip) {
     return kungFuDeathGrip->ReceiveMessage(aTarget, aTargetFrameLoader,
                                            aTargetClosed, aMessage,
                                            aIsSync, aCloneData,
                                            aCpows, aPrincipal,
                                            aRetVal);
   }
   return NS_OK;
 }
 
 void
-nsFrameMessageManager::AddChildManager(nsFrameMessageManager* aManager)
+nsFrameMessageManager::AddChildManager(MessageListenerManager* aManager)
 {
-  mChildManagers.AppendObject(aManager);
+  mChildManagers.AppendElement(aManager);
 
   RefPtr<nsFrameMessageManager> kungfuDeathGrip = this;
   RefPtr<nsFrameMessageManager> kungfuDeathGrip2 = aManager;
 
   LoadPendingScripts(this, aManager);
 }
 
 void
+nsFrameMessageManager::RemoveChildManager(MessageListenerManager* aManager)
+{
+  mChildManagers.RemoveElement(aManager);
+}
+
+void
 nsFrameMessageManager::LoadPendingScripts(nsFrameMessageManager* aManager,
                                           nsFrameMessageManager* aChildMM)
 {
   // We have parent manager if we're a message broadcaster.
   // In that case we want to load the pending scripts from all parent
   // message managers in the hierarchy. Process the parent first so
   // that pending scripts higher up in the hierarchy are loaded before others.
-  if (aManager->mParentManager) {
-    LoadPendingScripts(aManager->mParentManager, aChildMM);
+  nsFrameMessageManager* parentManager = aManager->GetParentManager();
+  if (parentManager) {
+    LoadPendingScripts(parentManager, aChildMM);
   }
 
   for (uint32_t i = 0; i < aManager->mPendingScripts.Length(); ++i) {
     aChildMM->LoadFrameScript(aManager->mPendingScripts[i],
                               false,
                               aManager->mPendingScriptsGlobalStates[i]);
   }
 }
@@ -1180,47 +1307,16 @@ nsFrameMessageManager::SetCallback(Messa
     mCallback = aCallback;
     if (mOwnsCallback) {
       mOwnedCallback = aCallback;
     }
   }
 }
 
 void
-nsFrameMessageManager::InitWithCallback(MessageManagerCallback* aCallback)
-{
-  if (mCallback) {
-    // Initialization should only happen once.
-    return;
-  }
-
-  SetCallback(aCallback);
-
-  // First load parent scripts by adding this to parent manager.
-  if (mParentManager) {
-    mParentManager->AddChildManager(this);
-  }
-
-  for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
-    LoadFrameScript(mPendingScripts[i], false, mPendingScriptsGlobalStates[i]);
-  }
-}
-
-void
-nsFrameMessageManager::RemoveFromParent()
-{
-  if (mParentManager) {
-    mParentManager->RemoveChildManager(this);
-  }
-  mParentManager = nullptr;
-  mCallback = nullptr;
-  mOwnedCallback = nullptr;
-}
-
-void
 nsFrameMessageManager::Close()
 {
   if (!mClosed) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->NotifyObservers(NS_ISUPPORTS_CAST(nsIContentFrameMessageManager*, this),
                             "message-manager-close", nullptr);
     }
@@ -1238,90 +1334,115 @@ nsFrameMessageManager::Disconnect(bool a
 
   if (!mDisconnected) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
        obs->NotifyObservers(NS_ISUPPORTS_CAST(nsIContentFrameMessageManager*, this),
                             "message-manager-disconnect", nullptr);
     }
   }
-  if (mParentManager && aRemoveFromParent) {
-    mParentManager->RemoveChildManager(this);
-  }
+
+  ClearParentManager(aRemoveFromParent);
+
   mDisconnected = true;
-  mParentManager = nullptr;
   if (!mHandlingMessage) {
     mListeners.Clear();
   }
 }
 
 void
 nsFrameMessageManager::SetInitialProcessData(JS::HandleValue aInitialData)
 {
   MOZ_ASSERT(!mChrome);
   MOZ_ASSERT(mIsProcessManager);
+  MOZ_ASSERT(aInitialData.isObject());
   mInitialProcessData = aInitialData;
 }
 
-NS_IMETHODIMP
-nsFrameMessageManager::GetInitialProcessData(JSContext* aCx, JS::MutableHandleValue aResult)
+void
+nsFrameMessageManager::GetInitialProcessData(JSContext* aCx,
+                                             JS::MutableHandle<JS::Value> aInitialProcessData,
+                                             ErrorResult& aError)
 {
   MOZ_ASSERT(mIsProcessManager);
   MOZ_ASSERT_IF(mChrome, IsBroadcaster());
 
   JS::RootedValue init(aCx, mInitialProcessData);
   if (mChrome && init.isUndefined()) {
     // We create the initial object in the junk scope. If we created it in a
     // normal compartment, that compartment would leak until shutdown.
     JS::RootedObject global(aCx, xpc::PrivilegedJunkScope());
     JSAutoCompartment ac(aCx, global);
 
     JS::RootedObject obj(aCx, JS_NewPlainObject(aCx));
     if (!obj) {
-      return NS_ERROR_OUT_OF_MEMORY;
+      aError.NoteJSContextException(aCx);
+      return;
     }
 
     mInitialProcessData.setObject(*obj);
     init.setObject(*obj);
   }
 
   if (!mChrome && XRE_IsParentProcess()) {
     // This is the cpmm in the parent process. We should use the same object as the ppmm.
     nsCOMPtr<nsIGlobalProcessScriptLoader> ppmm =
       do_GetService("@mozilla.org/parentprocessmessagemanager;1");
     ppmm->GetInitialProcessData(aCx, &init);
     mInitialProcessData = init;
   }
 
   if (!JS_WrapValue(aCx, &init)) {
-    return NS_ERROR_OUT_OF_MEMORY;
+    aError.NoteJSContextException(aCx);
+    return;
   }
-  aResult.set(init);
-  return NS_OK;
+  aInitialProcessData.set(init);
+}
+
+NS_IMETHODIMP
+nsFrameMessageManager::GetInitialProcessData(JSContext* aCx, JS::MutableHandleValue aResult)
+{
+  ErrorResult rv;
+  GetInitialProcessData(aCx, aResult, rv);
+  return rv.StealNSResult();
+}
+
+already_AddRefed<nsIMessageSender>
+nsFrameMessageManager::GetProcessMessageManager(ErrorResult& aError)
+{
+  nsCOMPtr<nsIMessageSender> pmm;
+  if (mCallback) {
+    pmm = mCallback->GetProcessMessageManager();
+  }
+  return pmm.forget();
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::GetProcessMessageManager(nsIMessageSender** aPMM)
 {
-  *aPMM = nullptr;
+  ErrorResult rv;
+  *aPMM = GetProcessMessageManager(rv).take();
+  return rv.StealNSResult();
+}
+
+void
+nsFrameMessageManager::GetRemoteType(nsAString& aRemoteType, ErrorResult& aError) const
+{
+  aRemoteType.Truncate();
   if (mCallback) {
-    nsCOMPtr<nsIMessageSender> pmm = mCallback->GetProcessMessageManager();
-    pmm.swap(*aPMM);
+    aError = mCallback->DoGetRemoteType(aRemoteType);
   }
-  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::GetRemoteType(nsAString& aRemoteType)
 {
-  aRemoteType.Truncate();
-  if (mCallback) {
-    return mCallback->DoGetRemoteType(aRemoteType);
-  }
-  return NS_OK;
+  ErrorResult rv;
+  GetRemoteType(aRemoteType, rv);
+  return rv.StealNSResult();
 }
 
 namespace {
 
 struct MessageManagerReferentCount
 {
   MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
   size_t mStrong;
@@ -1390,18 +1511,17 @@ MessageManagerReporter::CountReferents(n
         aReferentCount->mStrong++;
       }
     }
   }
 
   // Add referent count in child managers because the listeners
   // participate in messages dispatched from parent message manager.
   for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); ++i) {
-    RefPtr<nsFrameMessageManager> mm =
-      static_cast<nsFrameMessageManager*>(aMessageManager->mChildManagers[i]);
+    RefPtr<nsFrameMessageManager> mm = aMessageManager->mChildManagers[i];
     CountReferents(mm, aReferentCount);
   }
 }
 
 static void
 ReportReferentCount(const char* aManagerType,
                     const MessageManagerReferentCount& aReferentCount,
                     nsIHandleReportCallback* aHandleReport,
@@ -1479,32 +1599,30 @@ MessageManagerReporter::CollectReports(n
 } // namespace dom
 } // namespace mozilla
 
 nsresult
 NS_NewGlobalMessageManager(nsIMessageBroadcaster** aResult)
 {
   NS_ENSURE_TRUE(XRE_IsParentProcess(),
                  NS_ERROR_NOT_AVAILABLE);
-  RefPtr<nsFrameMessageManager> mm = new nsFrameMessageManager(nullptr,
-                                                                 nullptr,
-                                                                 MM_CHROME | MM_GLOBAL | MM_BROADCASTER);
+  RefPtr<nsFrameMessageManager> mm =
+    new ChromeMessageBroadcaster(MessageManagerFlags::MM_GLOBAL);
   RegisterStrongMemoryReporter(new MessageManagerReporter());
   mm.forget(aResult);
   return NS_OK;
 }
 
 nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>*
   nsMessageManagerScriptExecutor::sCachedScripts = nullptr;
 StaticRefPtr<nsScriptCacheCleaner> nsMessageManagerScriptExecutor::sScriptCacheCleaner;
 
 void
 nsMessageManagerScriptExecutor::DidCreateGlobal()
 {
-  NS_ASSERTION(mGlobal, "Should have mGlobal!");
   if (!sCachedScripts) {
     sCachedScripts =
       new nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>;
     sScriptCacheCleaner = new nsScriptCacheCleaner();
   }
 }
 
 // static
@@ -1529,23 +1647,24 @@ nsMessageManagerScriptExecutor::Shutdown
 
     delete sCachedScripts;
     sCachedScripts = nullptr;
     sScriptCacheCleaner = nullptr;
   }
 }
 
 void
-nsMessageManagerScriptExecutor::LoadScriptInternal(const nsAString& aURL,
+nsMessageManagerScriptExecutor::LoadScriptInternal(JS::Handle<JSObject*> aGlobal,
+                                                   const nsAString& aURL,
                                                    bool aRunInGlobalScope)
 {
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
     "nsMessageManagerScriptExecutor::LoadScriptInternal", OTHER, aURL);
 
-  if (!mGlobal || !sCachedScripts) {
+  if (!sCachedScripts) {
     return;
   }
 
   JS::RootingContext* rcx = RootingCx();
   JS::Rooted<JSScript*> script(rcx);
 
   nsMessageManagerScriptHolder* holder = sCachedScripts->Get(aURL);
   if (holder && holder->WillRunInGlobalScope() == aRunInGlobalScope) {
@@ -1553,31 +1672,28 @@ nsMessageManagerScriptExecutor::LoadScri
   } 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, &script);
   }
 
-  JS::Rooted<JSObject*> global(rcx, mGlobal);
-  if (global) {
-    AutoEntryScript aes(global, "message manager script load");
-    JSContext* cx = aes.cx();
-    if (script) {
-      if (aRunInGlobalScope) {
-        JS::RootedValue rval(cx);
-        JS::CloneAndExecuteScript(cx, script, &rval);
-      } else {
-        JS::Rooted<JSObject*> scope(cx);
-        bool ok = js::ExecuteInGlobalAndReturnScope(cx, global, script, &scope);
-        if (ok) {
-          // Force the scope to stay alive.
-          mAnonymousGlobalScopes.AppendElement(scope);
-        }
+  AutoEntryScript aes(aGlobal, "message manager script load");
+  JSContext* cx = aes.cx();
+  if (script) {
+    if (aRunInGlobalScope) {
+      JS::RootedValue rval(cx);
+      JS::CloneAndExecuteScript(cx, script, &rval);
+    } else {
+      JS::Rooted<JSObject*> scope(cx);
+      bool ok = js::ExecuteInGlobalAndReturnScope(cx, aGlobal, script, &scope);
+      if (ok) {
+        // Force the scope to stay alive.
+        mAnonymousGlobalScopes.AppendElement(scope);
       }
     }
   }
 }
 
 void
 nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
   const nsAString& aURL,
@@ -1688,71 +1804,64 @@ nsMessageManagerScriptExecutor::TryCache
 }
 
 void
 nsMessageManagerScriptExecutor::Trace(const TraceCallbacks& aCallbacks, void* aClosure)
 {
   for (size_t i = 0, length = mAnonymousGlobalScopes.Length(); i < length; ++i) {
     aCallbacks.Trace(&mAnonymousGlobalScopes[i], "mAnonymousGlobalScopes[i]", aClosure);
   }
-  aCallbacks.Trace(&mGlobal, "mGlobal", aClosure);
 }
 
 void
 nsMessageManagerScriptExecutor::Unlink()
 {
   ImplCycleCollectionUnlink(mAnonymousGlobalScopes);
-  mGlobal = nullptr;
 }
 
 bool
-nsMessageManagerScriptExecutor::InitChildGlobalInternal(
-  nsISupports* aScope,
-  const nsACString& aID)
+nsMessageManagerScriptExecutor::InitChildGlobalInternal(const nsACString& aID)
 {
   AutoSafeJSContext cx;
-  nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
+  if (!SystemBindingInitIds(cx)) {
+    return false;
+  }
 
-  const uint32_t flags = xpc::INIT_JS_STANDARD_CLASSES;
+  nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
 
   JS::CompartmentOptions options;
   options.creationOptions().setSystemZone();
 
-  if (xpc::SharedMemoryEnabled()) {
-    options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
+  xpc::InitGlobalObjectOptions(options, mPrincipal);
+  JS::Rooted<JSObject*> global(cx);
+  if (!WrapGlobalObject(cx, options, &global)) {
+    return false;
   }
 
-  JS::Rooted<JSObject*> global(cx);
-  nsresult rv = xpc::InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
-                                                     flags, options,
-                                                     &global);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  mGlobal = global;
-  NS_ENSURE_TRUE(mGlobal, 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(mGlobal, aID);
+  xpc::SetLocationForGlobal(global, aID);
 
   DidCreateGlobal();
   return true;
 }
 
 void
 nsMessageManagerScriptExecutor::MarkScopesForCC()
 {
   for (uint32_t i = 0; i < mAnonymousGlobalScopes.Length(); ++i) {
     mAnonymousGlobalScopes[i].exposeToActiveJS();
   }
 }
 
 NS_IMPL_ISUPPORTS(nsScriptCacheCleaner, nsIObserver)
 
-nsFrameMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr;
+ChildProcessMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sParentProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = nullptr;
 
 class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase,
                                          public Runnable
 {
 public:
   nsAsyncMessageToSameProcessChild(JS::RootingContext* aRootingCx,
@@ -1963,47 +2072,47 @@ public:
 
 
 // This creates the global parent process message manager.
 nsresult
 NS_NewParentProcessMessageManager(nsIMessageBroadcaster** aResult)
 {
   NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager,
                "Re-creating sParentProcessManager");
-  RefPtr<nsFrameMessageManager> mm = new nsFrameMessageManager(nullptr,
-                                                                 nullptr,
-                                                                 MM_CHROME | MM_PROCESSMANAGER | MM_BROADCASTER);
+  RefPtr<nsFrameMessageManager> mm =
+    new ChromeMessageBroadcaster(MessageManagerFlags::MM_PROCESSMANAGER);
   nsFrameMessageManager::sParentProcessManager = mm;
   nsFrameMessageManager::NewProcessMessageManager(false); // Create same process message manager.
   mm.forget(aResult);
   return NS_OK;
 }
 
 
-nsFrameMessageManager*
+ChromeMessageSender*
 nsFrameMessageManager::NewProcessMessageManager(bool aIsRemote)
 {
   if (!nsFrameMessageManager::sParentProcessManager) {
      nsCOMPtr<nsIMessageBroadcaster> dummy =
        do_GetService("@mozilla.org/parentprocessmessagemanager;1");
   }
 
   MOZ_ASSERT(nsFrameMessageManager::sParentProcessManager,
              "parent process manager not created");
-  nsFrameMessageManager* mm;
+  ChromeMessageSender* mm;
   if (aIsRemote) {
     // Callback is set in ContentParent::InitInternal so that the process has
     // already started when we send pending scripts.
-    mm = new nsFrameMessageManager(nullptr,
-                                   nsFrameMessageManager::sParentProcessManager,
-                                   MM_CHROME | MM_PROCESSMANAGER);
+    mm = new ChromeMessageSender(nullptr,
+                                 nsFrameMessageManager::sParentProcessManager,
+                                 MessageManagerFlags::MM_PROCESSMANAGER);
   } else {
-    mm = new nsFrameMessageManager(new SameParentProcessMessageManagerCallback(),
-                                   nsFrameMessageManager::sParentProcessManager,
-                                   MM_CHROME | MM_PROCESSMANAGER | MM_OWNSCALLBACK);
+    mm = new ChromeMessageSender(new SameParentProcessMessageManagerCallback(),
+                                 nsFrameMessageManager::sParentProcessManager,
+                                 MessageManagerFlags::MM_PROCESSMANAGER |
+                                 MessageManagerFlags::MM_OWNSCALLBACK);
     sSameProcessParentManager = mm;
   }
   return mm;
 }
 
 nsresult
 NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult)
 {
@@ -2012,36 +2121,38 @@ NS_NewChildProcessMessageManager(nsISync
 
   MessageManagerCallback* cb;
   if (XRE_IsParentProcess()) {
     cb = new SameChildProcessMessageManagerCallback();
   } else {
     cb = new ChildProcessMessageManagerCallback();
     RegisterStrongMemoryReporter(new MessageManagerReporter());
   }
-  auto* mm = new nsFrameMessageManager(cb, nullptr,
-                                       MM_PROCESSMANAGER | MM_OWNSCALLBACK);
+  auto* mm = new ChildProcessMessageManager(cb);
   nsFrameMessageManager::SetChildProcessManager(mm);
   RefPtr<ProcessGlobal> global = new ProcessGlobal(mm);
   NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED);
   global.forget(aResult);
   return NS_OK;
 }
 
 bool
 nsFrameMessageManager::MarkForCC()
 {
   for (auto iter = mListeners.Iter(); !iter.Done(); iter.Next()) {
     nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = iter.UserData();
     uint32_t count = listeners->Length();
     for (uint32_t i = 0; i < count; i++) {
-      nsCOMPtr<nsIMessageListener> strongListener =
-        listeners->ElementAt(i).mStrongListener;
+      MessageListenerHolder& strongListener = listeners->ElementAt(i).mStrongListener;
       if (strongListener) {
-        xpc_TryUnmarkWrappedGrayObject(strongListener);
+        if (strongListener.HasWebIDLCallback()) {
+          strongListener.GetWebIDLCallback()->MarkForCC();
+        } else {
+          xpc_TryUnmarkWrappedGrayObject(strongListener.GetXPCOMCallback());
+        }
       }
     }
   }
 
   if (mRefCnt.IsPurple()) {
     mRefCnt.RemovePurple();
   }
   return true;
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -23,44 +23,52 @@
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"
 #include "nsWeakPtr.h"
 #include "mozilla/Attributes.h"
 #include "js/RootingAPI.h"
 #include "nsTObserverArray.h"
+#include "mozilla/TypedEnumBits.h"
+#include "mozilla/dom/CallbackObject.h"
 #include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/jsipc/CpowHolder.h"
 
 class nsIFrameLoader;
 
 namespace mozilla {
 namespace dom {
 
 class nsIContentParent;
 class nsIContentChild;
+class ChildProcessMessageManager;
+class ChromeMessageSender;
 class ClonedMessageData;
+class MessageListener;
+class MessageListenerManager;
 class MessageManagerReporter;
+template<typename T> class Optional;
 
 namespace ipc {
 
 // Note: we round the time we spend to the nearest millisecond. So a min value
 // of 1 ms actually captures from 500us and above.
 static const uint32_t kMinTelemetrySyncMessageManagerLatencyMs = 1;
 
-enum MessageManagerFlags {
-  MM_CHILD = 0,
+enum class MessageManagerFlags {
+  MM_NONE = 0,
   MM_CHROME = 1,
   MM_GLOBAL = 2,
   MM_PROCESSMANAGER = 4,
   MM_BROADCASTER = 8,
   MM_OWNSCALLBACK = 16
 };
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(MessageManagerFlags);
 
 class MessageManagerCallback
 {
 public:
   virtual ~MessageManagerCallback() {}
 
   virtual bool DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope)
   {
@@ -113,95 +121,166 @@ protected:
 
 void UnpackClonedMessageDataForParent(const ClonedMessageData& aClonedData,
                                       StructuredCloneData& aData);
 
 void UnpackClonedMessageDataForChild(const ClonedMessageData& aClonedData,
                                      StructuredCloneData& aData);
 
 } // namespace ipc
+
+typedef CallbackObjectHolder<mozilla::dom::MessageListener,
+                             nsIMessageListener> MessageListenerHolder;
+
 } // namespace dom
 } // namespace mozilla
 
 struct nsMessageListenerInfo
 {
   bool operator==(const nsMessageListenerInfo& aOther) const
   {
     return &aOther == this;
   }
 
-  // Exactly one of mStrongListener and mWeakListener must be non-null.
-  nsCOMPtr<nsIMessageListener> mStrongListener;
+  // If mWeakListener is null then mStrongListener holds either a MessageListener or an
+  // nsIMessageListener. If mWeakListener is non-null then mStrongListener contains null.
+  mozilla::dom::MessageListenerHolder mStrongListener;
   nsWeakPtr mWeakListener;
   bool mListenWhenClosed;
 };
 
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+                            nsMessageListenerInfo& aField,
+                            const char* aName,
+                            uint32_t aFlags = 0)
+{
+  ImplCycleCollectionTraverse(aCallback, aField.mStrongListener, aName, aFlags);
+  ImplCycleCollectionTraverse(aCallback, aField.mWeakListener, aName, aFlags);
+}
 
 class MOZ_STACK_CLASS SameProcessCpowHolder : public mozilla::jsipc::CpowHolder
 {
 public:
   SameProcessCpowHolder(JS::RootingContext* aRootingCx, JS::Handle<JSObject*> aObj)
     : mObj(aRootingCx, aObj)
   {
   }
 
   virtual bool ToObject(JSContext* aCx, JS::MutableHandle<JSObject*> aObjp)
     override;
 
 private:
   JS::Rooted<JSObject*> mObj;
 };
 
-class nsFrameMessageManager final : public nsIContentFrameMessageManager,
-                                    public nsIMessageBroadcaster,
-                                    public nsIFrameScriptLoader,
-                                    public nsIGlobalProcessScriptLoader
+class nsFrameMessageManager : public nsIContentFrameMessageManager,
+                              public nsIMessageBroadcaster,
+                              public nsIFrameScriptLoader,
+                              public nsIGlobalProcessScriptLoader
 {
   friend class mozilla::dom::MessageManagerReporter;
   typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
-public:
+
+protected:
+  typedef mozilla::dom::ipc::MessageManagerFlags MessageManagerFlags;
+
   nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
-                        nsFrameMessageManager* aParentManager,
-                        /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags);
+                        MessageManagerFlags aFlags);
 
-private:
-  ~nsFrameMessageManager();
+  virtual ~nsFrameMessageManager();
 
 public:
+  explicit nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback)
+    : nsFrameMessageManager(aCallback, MessageManagerFlags::MM_NONE)
+  {}
+
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsFrameMessageManager,
                                                          nsIContentFrameMessageManager)
+
+  // MessageListenerManager
+  void AddMessageListener(const nsAString& aMessageName,
+                          mozilla::dom::MessageListener& aListener,
+                          bool aListenWhenClosed,
+                          mozilla::ErrorResult& aError);
+  void RemoveMessageListener(const nsAString& aMessageName,
+                             mozilla::dom::MessageListener& aListener,
+                             mozilla::ErrorResult& aError);
+  void AddWeakMessageListener(const nsAString& aMessageName,
+                              mozilla::dom::MessageListener& aListener,
+                              mozilla::ErrorResult& aError);
+  void RemoveWeakMessageListener(const nsAString& aMessageName,
+                                 mozilla::dom::MessageListener& aListener,
+                                 mozilla::ErrorResult& aError);
+
+  // MessageSender
+  void SendAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
+                        JS::Handle<JS::Value> aObj,
+                        JS::Handle<JSObject*> aObjects,
+                        nsIPrincipal* aPrincipal,
+                        JS::Handle<JS::Value> aTransfers,
+                        mozilla::ErrorResult& aError)
+  {
+    DispatchAsyncMessage(aCx, aMessageName, aObj, aObjects, aPrincipal, aTransfers,
+                         aError);
+  }
+  already_AddRefed<nsIMessageSender>
+    GetProcessMessageManager(mozilla::ErrorResult& aError);
+  void GetRemoteType(nsAString& aRemoteType, mozilla::ErrorResult& aError) const;
+
+  // SyncMessageSender
+  void SendSyncMessage(JSContext* aCx, const nsAString& aMessageName,
+                       JS::Handle<JS::Value> aObj,
+                       JS::Handle<JSObject*> aObjects,
+                       nsIPrincipal* aPrincipal,
+                       nsTArray<JS::Value>& aResult,
+                       mozilla::ErrorResult& aError)
+  {
+    SendMessage(aCx, aMessageName, aObj, aObjects, aPrincipal, true, aResult, aError);
+  }
+  void SendRpcMessage(JSContext* aCx, const nsAString& aMessageName,
+                      JS::Handle<JS::Value> aObj,
+                      JS::Handle<JSObject*> aObjects,
+                      nsIPrincipal* aPrincipal,
+                      nsTArray<JS::Value>& aResult,
+                      mozilla::ErrorResult& aError)
+  {
+    SendMessage(aCx, aMessageName, aObj, aObjects, aPrincipal, false, aResult, aError);
+  }
+
+  // GlobalProcessScriptLoader
+  void GetInitialProcessData(JSContext* aCx,
+                             JS::MutableHandle<JS::Value> aInitialProcessData,
+                             mozilla::ErrorResult& aError);
+
   NS_DECL_NSIMESSAGELISTENERMANAGER
   NS_DECL_NSIMESSAGESENDER
   NS_DECL_NSIMESSAGEBROADCASTER
   NS_DECL_NSISYNCMESSAGESENDER
   NS_DECL_NSIMESSAGEMANAGERGLOBAL
   NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER
   NS_DECL_NSIFRAMESCRIPTLOADER
   NS_DECL_NSIPROCESSSCRIPTLOADER
   NS_DECL_NSIGLOBALPROCESSSCRIPTLOADER
 
-  static nsFrameMessageManager*
+  static mozilla::dom::ChromeMessageSender*
   NewProcessMessageManager(bool aIsRemote);
 
   nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader,
                           const nsAString& aMessage,
                           bool aIsSync, StructuredCloneData* aCloneData,
                           mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
                           nsTArray<StructuredCloneData>* aRetVal);
 
-  void AddChildManager(nsFrameMessageManager* aManager);
-  void RemoveChildManager(nsFrameMessageManager* aManager)
-  {
-    mChildManagers.RemoveObject(aManager);
-  }
+  void AddChildManager(mozilla::dom::MessageListenerManager* aManager);
+  void RemoveChildManager(mozilla::dom::MessageListenerManager* aManager);
   void Disconnect(bool aRemoveFromParent = true);
   void Close();
 
-  void InitWithCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback);
   void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback);
 
   mozilla::dom::ipc::MessageManagerCallback* GetCallback()
   {
     return mCallback;
   }
 
   nsresult DispatchAsyncMessage(const nsAString& aMessageName,
@@ -212,104 +291,127 @@ public:
                                 JSContext* aCx,
                                 uint8_t aArgc);
 
   nsresult DispatchAsyncMessageInternal(JSContext* aCx,
                                         const nsAString& aMessage,
                                         StructuredCloneData& aData,
                                         JS::Handle<JSObject*> aCpows,
                                         nsIPrincipal* aPrincipal);
-  void RemoveFromParent();
-  nsFrameMessageManager* GetParentManager() { return mParentManager; }
-  void SetParentManager(nsFrameMessageManager* aParent)
-  {
-    NS_ASSERTION(!mParentManager, "We have parent manager already!");
-    NS_ASSERTION(mChrome, "Should not set parent manager!");
-    mParentManager = aParent;
-  }
   bool IsGlobal() { return mGlobal; }
   bool IsBroadcaster() { return mIsBroadcaster; }
 
   static nsFrameMessageManager* GetParentProcessManager()
   {
     return sParentProcessManager;
   }
-  static nsFrameMessageManager* GetChildProcessManager()
+  static mozilla::dom::ChildProcessMessageManager* GetChildProcessManager()
   {
     return sChildProcessManager;
   }
-  static void SetChildProcessManager(nsFrameMessageManager* aManager)
+  static void SetChildProcessManager(mozilla::dom::ChildProcessMessageManager* aManager)
   {
     sChildProcessManager = aManager;
   }
 
   void SetInitialProcessData(JS::HandleValue aInitialData);
 
   void LoadPendingScripts();
 
-private:
+protected:
+  friend class MMListenerRemover;
+
+  virtual nsFrameMessageManager* GetParentManager()
+  {
+    return nullptr;
+  }
+  virtual void ClearParentManager(bool aRemove)
+  {
+  }
+
+  void DispatchAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
+                            JS::Handle<JS::Value> aObj,
+                            JS::Handle<JSObject*> aObjects,
+                            nsIPrincipal* aPrincipal,
+                            JS::Handle<JS::Value> aTransfers,
+                            mozilla::ErrorResult& aError);
+
   nsresult SendMessage(const nsAString& aMessageName,
                        JS::Handle<JS::Value> aJSON,
                        JS::Handle<JS::Value> aObjects,
                        nsIPrincipal* aPrincipal,
                        JSContext* aCx,
                        uint8_t aArgc,
                        JS::MutableHandle<JS::Value> aRetval,
                        bool aIsSync);
+  void SendMessage(JSContext* aCx, const nsAString& aMessageName,
+                   JS::Handle<JS::Value> aObj, JS::Handle<JSObject*> aObjects,
+                   nsIPrincipal* aPrincipal, bool aIsSync, nsTArray<JS::Value>& aResult,
+                   mozilla::ErrorResult& aError);
+  void SendMessage(JSContext* aCx, const nsAString& aMessageName,
+                   StructuredCloneData& aData, JS::Handle<JSObject*> aObjects,
+                   nsIPrincipal* aPrincipal, bool aIsSync,
+                   nsTArray<JS::Value>& aResult, mozilla::ErrorResult& aError);
 
   nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader,
                           bool aTargetClosed, const nsAString& aMessage,
                           bool aIsSync, StructuredCloneData* aCloneData,
                           mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
                           nsTArray<StructuredCloneData>* aRetVal);
 
-  NS_IMETHOD LoadScript(const nsAString& aURL,
-                        bool aAllowDelayedLoad,
-                        bool aRunInGlobalScope);
-  NS_IMETHOD RemoveDelayedScript(const nsAString& aURL);
-  NS_IMETHOD GetDelayedScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList);
+  void LoadScript(const nsAString& aURL, bool aAllowDelayedLoad,
+                  bool aRunInGlobalScope, mozilla::ErrorResult& aError);
+  void RemoveDelayedScript(const nsAString& aURL);
+  nsresult GetDelayedScripts(JSContext* aCx,
+                             JS::MutableHandle<JS::Value> aList);
+  void GetDelayedScripts(JSContext* aCx, nsTArray<nsTArray<JS::Value>>& aList,
+                         mozilla::ErrorResult& aError);
 
-protected:
-  friend class MMListenerRemover;
+  enum ProcessCheckerType {
+    PROCESS_CHECKER_PERMISSION,
+    PROCESS_CHECKER_MANIFEST_URL,
+    ASSERT_APP_HAS_PERMISSION
+  };
+  bool AssertProcessInternal(ProcessCheckerType aType,
+                             const nsAString& aCapability,
+                             mozilla::ErrorResult& aError);
+
   // We keep the message listeners as arrays in a hastable indexed by the
   // message name. That gives us fast lookups in ReceiveMessage().
   nsClassHashtable<nsStringHashKey,
                    nsAutoTObserverArray<nsMessageListenerInfo, 1>> mListeners;
-  nsCOMArray<nsIContentFrameMessageManager> mChildManagers;
+  nsTArray<RefPtr<mozilla::dom::MessageListenerManager>> mChildManagers;
   bool mChrome;     // true if we're in the chrome process
   bool mGlobal;     // true if we're the global frame message manager
   bool mIsProcessManager; // true if the message manager belongs to the process realm
   bool mIsBroadcaster; // true if the message manager is a broadcaster
   bool mOwnsCallback;
   bool mHandlingMessage;
   bool mClosed;    // true if we can no longer send messages
   bool mDisconnected;
   mozilla::dom::ipc::MessageManagerCallback* mCallback;
   nsAutoPtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback;
-  RefPtr<nsFrameMessageManager> mParentManager;
   nsTArray<nsString> mPendingScripts;
   nsTArray<bool> mPendingScriptsGlobalStates;
   JS::Heap<JS::Value> mInitialProcessData;
 
   void LoadPendingScripts(nsFrameMessageManager* aManager,
                           nsFrameMessageManager* aChildMM);
 public:
   static nsFrameMessageManager* sParentProcessManager;
   static nsFrameMessageManager* sSameProcessParentManager;
   static nsTArray<nsCOMPtr<nsIRunnable> >* sPendingSameProcessAsyncMessages;
 private:
-  static nsFrameMessageManager* sChildProcessManager;
-  enum ProcessCheckerType {
-    PROCESS_CHECKER_PERMISSION,
-    PROCESS_CHECKER_MANIFEST_URL,
-    ASSERT_APP_HAS_PERMISSION
-  };
-  nsresult AssertProcessInternal(ProcessCheckerType aType,
-                                 const nsAString& aCapability,
-                                 bool* aValid);
+  void AddMessageListener(const nsAString& aMessageName,
+                          mozilla::dom::MessageListenerHolder&& aListener,
+                          bool aListenWhenClosed);
+  void RemoveMessageListener(const nsAString& aMessageName,
+                             const mozilla::dom::MessageListenerHolder&  aListener);
+
+  static mozilla::dom::ChildProcessMessageManager* sChildProcessManager;
 };
 
 /* A helper class for taking care of many details for async message sending
    within a single process.  Intended to be used like so:
 
    class MyAsyncMessage : public nsSameProcessAsyncMessageBase, public Runnable
    {
      NS_IMETHOD Run() {
@@ -369,39 +471,38 @@ struct nsMessageManagerScriptHolder
   bool mRunInGlobalScope;
 };
 
 class nsMessageManagerScriptExecutor
 {
 public:
   static void PurgeCache();
   static void Shutdown();
-  JSObject* GetGlobal()
-  {
-    return mGlobal;
-  }
 
   void MarkScopesForCC();
 protected:
   friend class nsMessageManagerScriptCx;
   nsMessageManagerScriptExecutor() { MOZ_COUNT_CTOR(nsMessageManagerScriptExecutor); }
   ~nsMessageManagerScriptExecutor() { MOZ_COUNT_DTOR(nsMessageManagerScriptExecutor); }
 
   void DidCreateGlobal();
-  void LoadScriptInternal(const nsAString& aURL, bool aRunInGlobalScope);
+  void LoadScriptInternal(JS::Handle<JSObject*> aGlobal, const nsAString& aURL,
+                          bool aRunInGlobalScope);
   void TryCacheLoadAndCompileScript(const nsAString& aURL,
                                     bool aRunInGlobalScope,
                                     bool aShouldCache,
                                     JS::MutableHandle<JSScript*> aScriptp);
   void TryCacheLoadAndCompileScript(const nsAString& aURL,
                                     bool aRunInGlobalScope);
-  bool InitChildGlobalInternal(nsISupports* aScope, const nsACString& aID);
+  bool InitChildGlobalInternal(const nsACString& aID);
+  virtual bool WrapGlobalObject(JSContext* aCx,
+                                JS::CompartmentOptions& aOptions,
+                                JS::MutableHandle<JSObject*> aReflector) = 0;
   void Trace(const TraceCallbacks& aCallbacks, void* aClosure);
   void Unlink();
-  JS::TenuredHeap<JSObject*> mGlobal;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   AutoTArray<JS::Heap<JSObject*>, 2> mAnonymousGlobalScopes;
 
   static nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>* sCachedScripts;
   static mozilla::StaticRefPtr<nsScriptCacheCleaner> sScriptCacheCleaner;
 };
 
 class nsScriptCacheCleaner final : public nsIObserver
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -1241,18 +1241,17 @@ nsGlobalWindowInner::CleanUp()
   DisableVRUpdates();
   mHasVREvents = false;
   mHasVRDisplayActivateEvents = false;
   DisableIdleCallbackRequests();
 
   if (mCleanMessageManager) {
     MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned");
     if (mChromeFields.mMessageManager) {
-      static_cast<nsFrameMessageManager*>(
-        mChromeFields.mMessageManager.get())->Disconnect();
+      mChromeFields.mMessageManager->Disconnect();
     }
   }
 
   CleanupCachedXBLHandlers();
 
   for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
     mAudioContexts[i]->Shutdown();
   }
@@ -7588,56 +7587,51 @@ nsGlobalWindowInner::NotifyDefaultButton
 NS_IMETHODIMP
 nsGlobalWindowInner::GetMessageManager(nsIMessageBroadcaster** aManager)
 {
   ErrorResult rv;
   NS_IF_ADDREF(*aManager = GetMessageManager(rv));
   return rv.StealNSResult();
 }
 
-nsIMessageBroadcaster*
+ChromeMessageBroadcaster*
 nsGlobalWindowInner::GetMessageManager(ErrorResult& aError)
 {
   MOZ_ASSERT(IsChromeWindow());
   if (!mChromeFields.mMessageManager) {
     nsCOMPtr<nsIMessageBroadcaster> globalMM =
       do_GetService("@mozilla.org/globalmessagemanager;1");
     mChromeFields.mMessageManager =
-      new nsFrameMessageManager(nullptr,
-                                static_cast<nsFrameMessageManager*>(globalMM.get()),
-                                MM_CHROME | MM_BROADCASTER);
+      new ChromeMessageBroadcaster(static_cast<nsFrameMessageManager*>(globalMM.get()));
   }
   return mChromeFields.mMessageManager;
 }
 
 NS_IMETHODIMP
 nsGlobalWindowInner::GetGroupMessageManager(const nsAString& aGroup,
                                             nsIMessageBroadcaster** aManager)
 {
   MOZ_RELEASE_ASSERT(IsChromeWindow());
   ErrorResult rv;
   NS_IF_ADDREF(*aManager = GetGroupMessageManager(aGroup, rv));
   return rv.StealNSResult();
 }
 
-nsIMessageBroadcaster*
+ChromeMessageBroadcaster*
 nsGlobalWindowInner::GetGroupMessageManager(const nsAString& aGroup,
                                             ErrorResult& aError)
 {
   MOZ_ASSERT(IsChromeWindow());
 
-  nsCOMPtr<nsIMessageBroadcaster> messageManager =
+  RefPtr<ChromeMessageBroadcaster> messageManager =
     mChromeFields.mGroupMessageManagers.LookupForAdd(aGroup).OrInsert(
       [this, &aError] () {
-        nsFrameMessageManager* parent =
-          static_cast<nsFrameMessageManager*>(GetMessageManager(aError));
-
-        return new nsFrameMessageManager(nullptr,
-                                         parent,
-                                         MM_CHROME | MM_BROADCASTER);
+        nsFrameMessageManager* parent = GetMessageManager(aError);
+
+        return new ChromeMessageBroadcaster(parent);
       });
   return messageManager;
 }
 
 void
 nsGlobalWindowInner::InitWasOffline()
 {
   mWasOffline = NS_IsOffline();
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -32,21 +32,21 @@
 #include "nsITimer.h"
 #include "mozilla/EventListenerManager.h"
 #include "nsIPrincipal.h"
 #include "nsSize.h"
 #include "mozilla/FlushType.h"
 #include "prclist.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/ChromeMessageBroadcaster.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/ErrorResult.h"
-#include "nsFrameMessageManager.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsIIdleObserver.h"
 #include "nsIDocument.h"
 #include "mozilla/dom/EventTarget.h"
@@ -936,19 +936,21 @@ public:
   void GetAttentionWithCycleCount(int32_t aCycleCount,
                                   mozilla::ErrorResult& aError);
   void SetCursor(const nsAString& aCursor, mozilla::ErrorResult& aError);
   void Maximize();
   void Minimize();
   void Restore();
   void NotifyDefaultButtonLoaded(mozilla::dom::Element& aDefaultButton,
                                  mozilla::ErrorResult& aError);
-  nsIMessageBroadcaster* GetMessageManager(mozilla::ErrorResult& aError);
-  nsIMessageBroadcaster* GetGroupMessageManager(const nsAString& aGroup,
-                                                mozilla::ErrorResult& aError);
+  mozilla::dom::ChromeMessageBroadcaster*
+    GetMessageManager(mozilla::ErrorResult& aError);
+  mozilla::dom::ChromeMessageBroadcaster*
+    GetGroupMessageManager(const nsAString& aGroup,
+                           mozilla::ErrorResult& aError);
   void BeginWindowMove(mozilla::dom::Event& aMouseDownEvent,
                        mozilla::dom::Element* aPanel,
                        mozilla::ErrorResult& aError);
 
   already_AddRefed<mozilla::dom::Promise>
   PromiseDocumentFlushed(mozilla::dom::PromiseDocumentFlushedCallback& aCallback,
                          mozilla::ErrorResult& aError);
 
@@ -1266,19 +1268,19 @@ private:
 
   bool IsBackgroundInternal() const;
 
   // NOTE: Chrome Only
   void DisconnectAndClearGroupMessageManagers()
   {
     MOZ_RELEASE_ASSERT(IsChromeWindow());
     for (auto iter = mChromeFields.mGroupMessageManagers.Iter(); !iter.Done(); iter.Next()) {
-      nsIMessageBroadcaster* mm = iter.UserData();
+      mozilla::dom::ChromeMessageBroadcaster* mm = iter.UserData();
       if (mm) {
-        static_cast<nsFrameMessageManager*>(mm)->Disconnect();
+        mm->Disconnect();
       }
     }
     mChromeFields.mGroupMessageManagers.Clear();
   }
 
   void CallDocumentFlushedResolvers();
   void CancelDocumentFlushedResolvers();
 
@@ -1468,18 +1470,19 @@ protected:
 
   // Members in the mChromeFields member should only be used in chrome windows.
   // All accesses to this field should be guarded by a check of mIsChrome.
   struct ChromeFields {
     ChromeFields()
       : mGroupMessageManagers(1)
     {}
 
-    nsCOMPtr<nsIMessageBroadcaster> mMessageManager;
-    nsInterfaceHashtable<nsStringHashKey, nsIMessageBroadcaster> mGroupMessageManagers;
+    RefPtr<mozilla::dom::ChromeMessageBroadcaster> mMessageManager;
+    nsRefPtrHashtable<nsStringHashKey,
+                      mozilla::dom::ChromeMessageBroadcaster> mGroupMessageManagers;
   } mChromeFields;
 
   // These fields are used by the inner and outer windows to prevent
   // programatically moving the window while the mouse is down.
   static bool sMouseDown;
   static bool sDragServiceDisabled;
 
   friend class nsDOMScriptableHelper;
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -32,21 +32,21 @@
 #include "nsIScriptObjectPrincipal.h"
 #include "nsITimer.h"
 #include "mozilla/EventListenerManager.h"
 #include "nsIPrincipal.h"
 #include "nsSize.h"
 #include "mozilla/FlushType.h"
 #include "prclist.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/ChromeMessageBroadcaster.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/ErrorResult.h"
-#include "nsFrameMessageManager.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsIIdleObserver.h"
 #include "nsIDocument.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/WindowBinding.h"
--- a/dom/base/nsIMessageManager.idl
+++ b/dom/base/nsIMessageManager.idl
@@ -198,17 +198,17 @@ interface nsIMessageListener : nsISuppor
    * returned as JSON (will be changed to use structured clones).
    * When there are multiple listeners to sync messages, each
    * listener's return value is sent back as an array.  |undefined|
    * return values show up as undefined values in the array.
    */
   void receiveMessage();
 };
 
-[scriptable, builtinclass, uuid(b949bfec-bb7d-47bc-b387-ac6a9b655072)]
+[uuid(b949bfec-bb7d-47bc-b387-ac6a9b655072)]
 interface nsIMessageListenerManager : nsISupports
 {
   /**
    * Register |listener| to receive |messageName|.  All listener
    * callbacks for a particular message are invoked when that message
    * is received.
    *
    * The message manager holds a strong ref to |listener|.
@@ -256,17 +256,17 @@ interface nsIMessageListenerManager : ns
 };
 
 /**
  * Message "senders" have a single "other side" to which messages are
  * sent.  For example, a child-process message manager will send
  * messages that are only delivered to its one parent-process message
  * manager.
  */
-[scriptable, builtinclass, uuid(bb5d79e4-e73c-45e7-9651-4d718f4b994c)]
+[uuid(bb5d79e4-e73c-45e7-9651-4d718f4b994c)]
 interface nsIMessageSender : nsIMessageListenerManager
 {
   /**
    * Send |messageName| and |obj| to the "other side" of this message
    * manager.  This invokes listeners who registered for
    * |messageName|.
    *
    * See nsIMessageListener::receiveMessage() for the format of the
@@ -304,17 +304,17 @@ interface nsIMessageSender : nsIMessageL
 
 /**
  * Message "broadcasters" don't have a single "other side" that they
  * send messages to, but rather a set of subordinate message managers.
  * For example, broadcasting a message through a window message
  * manager will broadcast the message to all frame message managers
  * within its window.
  */
-[scriptable, builtinclass, uuid(4d7d62ad-4725-4f39-86cf-8fb22bf9c1d8)]
+[uuid(4d7d62ad-4725-4f39-86cf-8fb22bf9c1d8)]
 interface nsIMessageBroadcaster : nsIMessageListenerManager
 {
   /**
    * Like |sendAsyncMessage()|, but also broadcasts this message to
    * all "child" message managers of this message manager.  See long
    * comment above for details.
    *
    * WARNING: broadcasting messages can be very expensive and leak
@@ -337,17 +337,17 @@ interface nsIMessageBroadcaster : nsIMes
 
   /**
    * Some processes are kept alive after their last tab/window are closed for testing
    * (see dom.ipc.keepProcessesAlive). This function releases those.
    */
    void releaseCachedProcesses();
 };
 
-[scriptable, builtinclass, uuid(0e602c9e-1977-422a-a8e4-fe0d4a4f78d0)]
+[uuid(0e602c9e-1977-422a-a8e4-fe0d4a4f78d0)]
 interface nsISyncMessageSender : nsIMessageSender
 {
   /**
    * Like |sendAsyncMessage()|, except blocks the sender until all
    * listeners of the message have been invoked.  Returns an array
    * containing return values from each listener invoked.
    */
   [implicit_jscontext, optional_argc]
@@ -367,17 +367,17 @@ interface nsISyncMessageSender : nsIMess
    */
   [implicit_jscontext, optional_argc]
   jsval sendRpcMessage([optional] in AString messageName,
                        [optional] in jsval obj,
                        [optional] in jsval objects,
                        [optional] in nsIPrincipal principal);
 };
 
-[scriptable, builtinclass, uuid(13f3555f-769e-44ea-b607-5239230c3162)]
+[uuid(13f3555f-769e-44ea-b607-5239230c3162)]
 interface nsIMessageManagerGlobal : nsISyncMessageSender
 {
   /**
    * Print a string to stdout.
    */
   void dump(in DOMString aStr);
 
   /**
@@ -388,17 +388,17 @@ interface nsIMessageManagerGlobal : nsIS
 
   /**
    * Ascii base64 data to binary data and vice versa
    */
   DOMString atob(in DOMString aAsciiString);
   DOMString btoa(in DOMString aBase64Data);
 };
 
-[scriptable, builtinclass, uuid(694e367c-aa25-4446-8499-2c527c4bd838)]
+[uuid(694e367c-aa25-4446-8499-2c527c4bd838)]
 interface nsIContentFrameMessageManager : nsIMessageManagerGlobal
 {
   /**
    * The current top level window in the frame or null.
    */
   readonly attribute mozIDOMWindowProxy content;
 
   /**
@@ -415,28 +415,28 @@ interface nsIContentFrameMessageManager 
 
 [uuid(b39a3324-b574-4f85-8cdb-274d04f807ef)]
 interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager
 {
   [notxpcom] nsIContent getOwnerContent();
   [notxpcom] void cacheFrameLoader(in nsIFrameLoader aFrameLoader);
 };
 
-[scriptable, builtinclass, uuid(6d12e467-2446-46db-9965-e4e93cb87ca5)]
+[uuid(6d12e467-2446-46db-9965-e4e93cb87ca5)]
 interface nsIContentProcessMessageManager : nsIMessageManagerGlobal
 {
   /**
    * Read out a copy of the object that was initialized in the parent
    * process via nsIProcessScriptLoader.initialProcessData.
    */
   [implicit_jscontext]
   readonly attribute jsval initialProcessData;
 };
 
-[scriptable, builtinclass, uuid(bf61446b-ba24-4b1d-88c7-4f94724b9ce1)]
+[uuid(bf61446b-ba24-4b1d-88c7-4f94724b9ce1)]
 interface nsIFrameScriptLoader : nsISupports
 {
   /**
    * Load a script in the (remote) frame. aURL must be the absolute URL.
    * data: URLs are also supported. For example data:,dump("foo\n");
    * If aAllowDelayedLoad is true, script will be loaded when the
    * remote frame becomes available. Otherwise the script will be loaded
    * only if the frame is already available.
@@ -453,17 +453,17 @@ interface nsIFrameScriptLoader : nsISupp
    * Returns all delayed scripts that will be loaded once a (remote)
    * frame becomes available. The return value is a list of pairs
    * [<URL>, <WasLoadedInGlobalScope>].
    */
   [implicit_jscontext]
   jsval getDelayedFrameScripts();
 };
 
-[scriptable, builtinclass, uuid(7e1e1a20-b24f-11e4-ab27-0800200c9a66)]
+[uuid(7e1e1a20-b24f-11e4-ab27-0800200c9a66)]
 interface nsIProcessScriptLoader : nsISupports
 {
   /**
    * Load a script in the (possibly remote) process. aURL must be the absolute URL.
    * data: URLs are also supported. For example data:,dump("foo\n");
    * If aAllowDelayedLoad is true, script will be loaded when the
    * remote frame becomes available. Otherwise the script will be loaded
    * only if the frame is already available.
@@ -478,17 +478,17 @@ interface nsIProcessScriptLoader : nsISu
   /**
    * Returns all delayed scripts that will be loaded once a (remote)
    * frame becomes available. The return value is a list of URLs.
    */
   [implicit_jscontext]
   jsval getDelayedProcessScripts();
 };
 
-[scriptable, builtinclass, uuid(5b390753-abb3-49b0-ae3b-b803dab58144)]
+[uuid(5b390753-abb3-49b0-ae3b-b803dab58144)]
 interface nsIGlobalProcessScriptLoader : nsIProcessScriptLoader
 {
   /**
    * Allows the parent process to set the initial process data for
    * new, not-yet-created child processes. This attribute should only
    * be used by the global parent process message manager. When a new
    * process is created, it gets a copy of this data (via structured
    * cloning). It can access the data via the initialProcessData
--- a/dom/base/nsInProcessTabChildGlobal.cpp
+++ b/dom/base/nsInProcessTabChildGlobal.cpp
@@ -9,18 +9,19 @@
 #include "nsIScriptSecurityManager.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIComponentManager.h"
 #include "nsIServiceManager.h"
 #include "nsComponentManagerUtils.h"
 #include "nsFrameLoader.h"
 #include "xpcpublic.h"
 #include "nsIMozBrowserFrame.h"
-#include "nsDOMClassInfoID.h"
 #include "mozilla/EventDispatcher.h"
+#include "mozilla/dom/ChromeMessageSender.h"
+#include "mozilla/dom/MessageManagerBinding.h"
 #include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/ScriptLoader.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
 bool
@@ -84,21 +85,21 @@ nsInProcessTabChildGlobal::DoSendAsyncMe
 
   queue->Push(ev);
   return NS_OK;
 }
 
 nsInProcessTabChildGlobal::nsInProcessTabChildGlobal(nsIDocShell* aShell,
                                                      nsIContent* aOwner,
                                                      nsFrameMessageManager* aChrome)
-: mDocShell(aShell), mInitialized(false), mLoadingScript(false),
+: ContentFrameMessageManager(aChrome),
+  mDocShell(aShell), mInitialized(false), mLoadingScript(false),
   mPreventEventsEscaping(false),
   mOwner(aOwner), mChromeMessageManager(aChrome)
 {
-  SetIsNotDOMBinding();
   mozilla::HoldJSObjects(this);
 
   // If owner corresponds to an <iframe mozbrowser>, we'll have to tweak our
   // GetEventTargetParent implementation.
   nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwner);
   if (browserFrame) {
     mIsBrowserFrame = browserFrame->GetReallyIsBrowser();
   }
@@ -114,31 +115,29 @@ nsInProcessTabChildGlobal::~nsInProcessT
 }
 
 // This method isn't automatically forwarded safely because it's notxpcom, so
 // the IDL binding doesn't know what value to return.
 NS_IMETHODIMP_(bool)
 nsInProcessTabChildGlobal::MarkForCC()
 {
   MarkScopesForCC();
-  return mMessageManager ? mMessageManager->MarkForCC() : false;
+  return MessageManagerGlobal::MarkForCC();
 }
 
 nsresult
 nsInProcessTabChildGlobal::Init()
 {
 #ifdef DEBUG
   nsresult rv =
 #endif
   InitTabChildGlobal();
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                        "Couldn't initialize nsInProcessTabChildGlobal");
-  mMessageManager = new nsFrameMessageManager(this,
-                                              nullptr,
-                                              dom::ipc::MM_CHILD);
+  mMessageManager = new nsFrameMessageManager(this);
   return NS_OK;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsInProcessTabChildGlobal)
 
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsInProcessTabChildGlobal,
                                                   DOMEventTargetHelper)
@@ -164,53 +163,79 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsIContentFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIInProcessContentFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
   NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ContentFrameMessageManager)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(nsInProcessTabChildGlobal, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(nsInProcessTabChildGlobal, DOMEventTargetHelper)
 
+bool
+nsInProcessTabChildGlobal::WrapGlobalObject(JSContext* aCx,
+                                            JS::CompartmentOptions& aOptions,
+                                            JS::MutableHandle<JSObject*> aReflector)
+{
+  bool ok = ContentFrameMessageManagerBinding::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;
+}
+
 void
 nsInProcessTabChildGlobal::CacheFrameLoader(nsIFrameLoader* aFrameLoader)
 {
   mFrameLoader = aFrameLoader;
 }
 
+already_AddRefed<nsPIDOMWindowOuter>
+nsInProcessTabChildGlobal::GetContent(ErrorResult& aError)
+{
+  nsCOMPtr<nsPIDOMWindowOuter> content;
+  if (mDocShell) {
+    content = mDocShell->GetWindow();
+  }
+  return content.forget();
+}
+
 NS_IMETHODIMP
 nsInProcessTabChildGlobal::GetContent(mozIDOMWindowProxy** aContent)
 {
-  *aContent = nullptr;
-  if (!mDocShell) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow();
-  window.forget(aContent);
-  return NS_OK;
+  ErrorResult rv;
+  *aContent = GetContent(rv).take();
+  return rv.StealNSResult();
 }
 
 NS_IMETHODIMP
 nsInProcessTabChildGlobal::GetDocShell(nsIDocShell** aDocShell)
 {
-  NS_IF_ADDREF(*aDocShell = mDocShell);
-  return NS_OK;
+  ErrorResult rv;
+  *aDocShell = GetDocShell(rv).take();
+  return rv.StealNSResult();
+}
+
+already_AddRefed<nsIEventTarget>
+nsInProcessTabChildGlobal::GetTabEventTarget()
+{
+  nsCOMPtr<nsIEventTarget> target = GetMainThreadEventTarget();
+  return target.forget();
 }
 
 NS_IMETHODIMP
 nsInProcessTabChildGlobal::GetTabEventTarget(nsIEventTarget** aTarget)
 {
-  nsCOMPtr<nsIEventTarget> target = GetMainThreadEventTarget();
-  target.forget(aTarget);
+  *aTarget = GetTabEventTarget().take();
   return NS_OK;
 }
 
 void
 nsInProcessTabChildGlobal::FireUnloadEvent()
 {
   // We're called from nsDocument::MaybeInitializeFinalizeFrameLoaders, so it
   // should be safe to run script.
@@ -307,18 +332,17 @@ nsInProcessTabChildGlobal::InitTabChildG
   nsIURI* uri = mOwner->OwnerDoc()->GetDocumentURI();
   if (uri) {
     nsAutoCString u;
     nsresult rv = uri->GetSpec(u);
     NS_ENSURE_SUCCESS(rv, rv);
     id.AppendLiteral("?ownedBy=");
     id.Append(u);
   }
-  nsISupports* scopeSupports = NS_ISUPPORTS_CAST(EventTarget*, this);
-  NS_ENSURE_STATE(InitChildGlobalInternal(scopeSupports, id));
+  NS_ENSURE_STATE(InitChildGlobalInternal(id));
   return NS_OK;
 }
 
 class nsAsyncScriptLoad : public Runnable
 {
 public:
   nsAsyncScriptLoad(nsInProcessTabChildGlobal* aTabChild,
                     const nsAString& aURL,
@@ -348,17 +372,18 @@ nsInProcessTabChildGlobal::LoadFrameScri
     return;
   }
   if (!mInitialized) {
     mInitialized = true;
     Init();
   }
   bool tmp = mLoadingScript;
   mLoadingScript = true;
-  LoadScriptInternal(aURL, aRunInGlobalScope);
+  JS::Rooted<JSObject*> global(mozilla::dom::RootingCx(), GetWrapper());
+  LoadScriptInternal(global, aURL, aRunInGlobalScope);
   mLoadingScript = tmp;
 }
 
 already_AddRefed<nsIFrameLoader>
 nsInProcessTabChildGlobal::GetFrameLoader()
 {
   nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface(mOwner);
   nsCOMPtr<nsIFrameLoader> fl = owner ? owner->GetFrameLoader() : nullptr;
--- a/dom/base/nsInProcessTabChildGlobal.h
+++ b/dom/base/nsInProcessTabChildGlobal.h
@@ -4,16 +4,17 @@
  * 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 nsInProcessTabChildGlobal_h
 #define nsInProcessTabChildGlobal_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/dom/ContentFrameMessageManager.h"
 #include "nsCOMPtr.h"
 #include "nsFrameMessageManager.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIClassInfo.h"
 #include "nsIDocShell.h"
 #include "nsIDOMElement.h"
@@ -22,17 +23,17 @@
 #include "nsIGlobalObject.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsWeakReference.h"
 
 namespace mozilla {
 class EventChainPreVisitor;
 } // namespace mozilla
 
-class nsInProcessTabChildGlobal : public mozilla::DOMEventTargetHelper,
+class nsInProcessTabChildGlobal : public mozilla::dom::ContentFrameMessageManager,
                                   public nsMessageManagerScriptExecutor,
                                   public nsIInProcessContentFrameMessageManager,
                                   public nsIGlobalObject,
                                   public nsIScriptObjectPrincipal,
                                   public nsSupportsWeakReference,
                                   public mozilla::dom::ipc::MessageManagerCallback
 {
   typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
@@ -41,48 +42,40 @@ class nsInProcessTabChildGlobal : public
 
 public:
   nsInProcessTabChildGlobal(nsIDocShell* aShell, nsIContent* aOwner,
                             nsFrameMessageManager* aChrome);
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsInProcessTabChildGlobal,
                                                          mozilla::DOMEventTargetHelper)
 
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override
+  {
+    MOZ_CRASH("We should never get here!");
+  }
+  virtual bool WrapGlobalObject(JSContext* aCx,
+                                JS::CompartmentOptions& aOptions,
+                                JS::MutableHandle<JSObject*> aReflector) override;
+
+  virtual already_AddRefed<nsPIDOMWindowOuter>
+    GetContent(mozilla::ErrorResult& aError) override;
+  virtual already_AddRefed<nsIDocShell>
+    GetDocShell(mozilla::ErrorResult& aError) override
+  {
+    nsCOMPtr<nsIDocShell> docShell(mDocShell);
+    return docShell.forget();
+  }
+  virtual already_AddRefed<nsIEventTarget> GetTabEventTarget() override;
+
   NS_FORWARD_SAFE_NSIMESSAGELISTENERMANAGER(mMessageManager)
   NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
+  NS_FORWARD_SAFE_NSISYNCMESSAGESENDER(mMessageManager);
   NS_FORWARD_SAFE_NSIMESSAGEMANAGERGLOBAL(mMessageManager)
-  NS_IMETHOD SendSyncMessage(const nsAString& aMessageName,
-                             JS::Handle<JS::Value> aObject,
-                             JS::Handle<JS::Value> aRemote,
-                             nsIPrincipal* aPrincipal,
-                             JSContext* aCx,
-                             uint8_t aArgc,
-                             JS::MutableHandle<JS::Value> aRetval) override
-  {
-    return mMessageManager
-      ? mMessageManager->SendSyncMessage(aMessageName, aObject, aRemote,
-                                         aPrincipal, aCx, aArgc, aRetval)
-      : NS_ERROR_NULL_POINTER;
-  }
-  NS_IMETHOD SendRpcMessage(const nsAString& aMessageName,
-                            JS::Handle<JS::Value> aObject,
-                            JS::Handle<JS::Value> aRemote,
-                            nsIPrincipal* aPrincipal,
-                            JSContext* aCx,
-                            uint8_t aArgc,
-                            JS::MutableHandle<JS::Value> aRetval) override
-  {
-    return mMessageManager
-      ? mMessageManager->SendRpcMessage(aMessageName, aObject, aRemote,
-                                        aPrincipal, aCx, aArgc, aRetval)
-      : NS_ERROR_NULL_POINTER;
-  }
-  NS_IMETHOD GetContent(mozIDOMWindowProxy** aContent) override;
-  NS_IMETHOD GetDocShell(nsIDocShell** aDocShell) override;
-  NS_IMETHOD GetTabEventTarget(nsIEventTarget** aTarget) override;
+  NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER
 
   NS_DECL_NSIINPROCESSCONTENTFRAMEMESSAGEMANAGER
 
   /**
    * MessageManagerCallback methods that we override.
    */
   virtual bool DoSendBlockingMessage(JSContext* aCx,
                                       const nsAString& aMessage,
@@ -94,36 +87,16 @@ public:
   virtual nsresult DoSendAsyncMessage(JSContext* aCx,
                                       const nsAString& aMessage,
                                       StructuredCloneData& aData,
                                       JS::Handle<JSObject *> aCpows,
                                       nsIPrincipal* aPrincipal) override;
 
   virtual nsresult GetEventTargetParent(
                      mozilla::EventChainPreVisitor& aVisitor) override;
-  NS_IMETHOD AddEventListener(const nsAString& aType,
-                              nsIDOMEventListener* aListener,
-                              bool aUseCapture)
-  {
-    // By default add listeners only for trusted events!
-    return mozilla::DOMEventTargetHelper::AddEventListener(aType, aListener,
-                                                           aUseCapture, false,
-                                                           2);
-  }
-  NS_IMETHOD AddEventListener(const nsAString& aType,
-                              nsIDOMEventListener* aListener,
-                              bool aUseCapture, bool aWantsUntrusted,
-                              uint8_t optional_argc) override
-  {
-    return mozilla::DOMEventTargetHelper::AddEventListener(aType, aListener,
-                                                           aUseCapture,
-                                                           aWantsUntrusted,
-                                                           optional_argc);
-  }
-  using mozilla::DOMEventTargetHelper::AddEventListener;
 
   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,
@@ -138,32 +111,28 @@ public:
   {
     return mChromeMessageManager;
   }
   void SetChromeMessageManager(nsFrameMessageManager* aParent)
   {
     mChromeMessageManager = aParent;
   }
 
-  virtual JSObject* GetGlobalJSObject() override {
-    return mGlobal;
-  }
-  virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override
+  virtual JSObject* GetGlobalJSObject() override
   {
-    MOZ_CRASH("nsInProcessTabChildGlobal doesn't use DOM bindings!");
+    return GetWrapper();
   }
 
   already_AddRefed<nsIFrameLoader> GetFrameLoader();
 
 protected:
   virtual ~nsInProcessTabChildGlobal();
 
   nsresult Init();
   nsresult InitTabChildGlobal();
-  nsCOMPtr<nsIContentFrameMessageManager> mMessageManager;
   nsCOMPtr<nsIDocShell> mDocShell;
   bool mInitialized;
   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;
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -10,16 +10,17 @@
  * The goal of the utility functions is to cut down on the size of
  * the generated code itself.
  */
 
 #include "nsJSUtils.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "nsIScriptContext.h"
+#include "nsIScriptElement.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIXPConnect.h"
 #include "nsCOMPtr.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsPIDOMWindow.h"
 #include "GeckoProfiler.h"
 #include "nsJSPrincipals.h"
 #include "xpcpublic.h"
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -339,21 +339,17 @@ protected:
       mWrapper = reinterpret_cast<JSObject*>(1);
     }
   }
 
 private:
   // Friend declarations for things that need to be able to call
   // SetIsNotDOMBinding().  The goal is to get rid of all of these, and
   // SetIsNotDOMBinding() too.
-  friend class mozilla::dom::TabChildGlobal;
-  friend class mozilla::dom::ProcessGlobal;
   friend class SandboxPrivate;
-  friend class nsInProcessTabChildGlobal;
-  friend class nsWindowRoot;
   void SetIsNotDOMBinding()
   {
     MOZ_ASSERT(!mWrapper && !(GetWrapperFlags() & ~WRAPPER_IS_NOT_DOM_BINDING),
                "This flag should be set before creating any wrappers.");
     SetWrapperFlags(WRAPPER_IS_NOT_DOM_BINDING);
   }
 
   void SetWrapperJSObject(JSObject* aWrapper);
--- a/dom/base/test/browser_messagemanager_loadprocessscript.js
+++ b/dom/base/test/browser_messagemanager_loadprocessscript.js
@@ -1,30 +1,24 @@
-var ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
-           .getService(Ci.nsIMessageBroadcaster);
-ppmm.QueryInterface(Ci.nsIProcessScriptLoader);
-
 const BASE_NUMBER_OF_PROCESSES = 3;
 function checkBaseProcessCount(description) {
-  const {childCount} = ppmm;
+  const {childCount} = Services.ppmm;
   // With preloaded activity-stream, process count is a bit undeterministic, so
   // allow for some variation
   const extraCount = BASE_NUMBER_OF_PROCESSES + 1;
   ok(childCount === BASE_NUMBER_OF_PROCESSES || childCount === extraCount, `${description} (${BASE_NUMBER_OF_PROCESSES} or ${extraCount})`);
 }
 
 function processScript() {
-  let cpmm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]
-           .getService(Components.interfaces.nsISyncMessageSender);
-  if (cpmm !== this) {
+  if (Services.cpmm !== this) {
     dump("Test failed: wrong global object\n");
     return;
   }
 
-  this.cpmm = cpmm;
+  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() + ")()";
@@ -65,89 +59,89 @@ add_task(async function(){
   ]});
 })
 
 add_task(async function(){
   // This test is only relevant in e10s.
   if (!gMultiProcessBrowser)
     return;
 
-  ppmm.releaseCachedProcesses();
+  Services.ppmm.releaseCachedProcesses();
 
   await SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 5]]})
   await SpecialPowers.pushPrefEnv({"set": [["dom.ipc.keepProcessesAlive.web", 5]]})
 
   let tabs = [];
   for (let i = 0; i < 3; i++) {
     tabs[i] = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
   }
 
   for (let i = 0; i < 3; i++) {
     await BrowserTestUtils.removeTab(tabs[i]);
   }
 
-  ppmm.releaseCachedProcesses();
+  Services.ppmm.releaseCachedProcesses();
   checkBaseProcessCount("Should get back to the base number of processes at this point");
 })
 
 // Test that loading a process script loads in all existing processes
 add_task(async function() {
   let checks = [];
-  for (let i = 0; i < ppmm.childCount; i++)
-    checks.push(checkProcess(ppmm.getChildAt(i)));
+  for (let i = 0; i < Services.ppmm.childCount; i++)
+    checks.push(checkProcess(Services.ppmm.getChildAt(i)));
 
-  ppmm.loadProcessScript(processScriptURL, false);
+  Services.ppmm.loadProcessScript(processScriptURL, false);
   await Promise.all(checks);
 });
 
 // Test that loading a process script loads in new processes
 add_task(async function() {
   // This test is only relevant in e10s
   if (!gMultiProcessBrowser)
     return;
 
   checkBaseProcessCount("Should still be at the base number of processes at this point");
 
   // Load something in the main process
   gBrowser.selectedBrowser.loadURI("about:robots");
   await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
 
-  let init = ppmm.initialProcessData;
+  let init = Services.ppmm.initialProcessData;
   init.test123 = "hello";
   init.test456 = new Map();
   init.test456.set("hi", "bye");
 
   // With no remote frames left we should be down to one process.
   // However, stuff like remote thumbnails can cause a content
   // process to exist nonetheless. This should be rare, though,
   // so the test is useful most of the time.
-  if (ppmm.childCount == 2) {
-    let mainMM = ppmm.getChildAt(0);
+  if (Services.ppmm.childCount == 2) {
+    let mainMM = Services.ppmm.getChildAt(0);
 
-    let check = checkProcess(ppmm);
-    ppmm.loadProcessScript(processScriptURL, true);
+    let check = checkProcess(Services.ppmm);
+    Services.ppmm.loadProcessScript(processScriptURL, true);
 
     // The main process should respond
     await check;
 
-    check = checkProcess(ppmm);
+    check = checkProcess(Services.ppmm);
     // Reset the default browser to start a new child process
     gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, true);
     gBrowser.selectedBrowser.loadURI("about:blank");
     await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
 
     checkBaseProcessCount("Should be back to the base number of processes at this point");
 
     // The new process should have responded
     await check;
 
-    ppmm.removeDelayedProcessScript(processScriptURL);
+    Services.ppmm.removeDelayedProcessScript(processScriptURL);
 
     let childMM;
-    childMM = ppmm.getChildAt(2);
+    childMM = Services.ppmm.getChildAt(2);
 
     childMM.loadProcessScript(initTestScriptURL, false);
     let msg = await promiseMessage(childMM, "ProcessTest:InitGood");
     is(msg.data, "bye", "initial process data was correct");
   } else {
     info("Unable to finish test entirely");
   }
 });
--- a/dom/base/test/chrome/file_bug1139964.xul
+++ b/dom/base/test/chrome/file_bug1139964.xul
@@ -6,18 +6,17 @@
 https://bugzilla.mozilla.org/show_bug.cgi?id=1139964
 -->
 <window title="Mozilla Bug 1139964"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
   onload="run()">
   <label value="Mozilla Bug 1139964"/>
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
-  var ppm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
-              .getService(Ci.nsIMessageBroadcaster);
+  var ppm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService();
 
   function ok(cond, msg) {
     opener.wrappedJSObject.ok(cond, msg);
   }
 
   var msgName = "TEST:Global_has_Promise";
 
   function mmScriptForPromiseTest() {
--- a/dom/base/test/chrome/file_bug549682.xul
+++ b/dom/base/test/chrome/file_bug549682.xul
@@ -9,22 +9,19 @@ https://bugzilla.mozilla.org/show_bug.cg
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
   onload="run()">
   <label value="Mozilla Bug 549682"/>
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
   var didRunAsync = false;
   var didRunLocal = false;
 
-  var global = Cc["@mozilla.org/globalmessagemanager;1"]
-                 .getService(Components.interfaces.nsIMessageBroadcaster);
-  var ppm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
-              .getService(Components.interfaces.nsIMessageBroadcaster);
-  var cpm = Cc["@mozilla.org/childprocessmessagemanager;1"]
-              .getService(Components.interfaces.nsISyncMessageSender);
+  var global = Cc["@mozilla.org/globalmessagemanager;1"].getService();
+  var ppm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService();
+  var cpm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService();
 
   ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
   function ok(cond, msg) {
     opener.wrappedJSObject.ok(cond, msg);
   }
 
   function is(actual, expected, msg) {
--- a/dom/base/test/chrome/file_bug990812-1.xul
+++ b/dom/base/test/chrome/file_bug990812-1.xul
@@ -11,18 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <label value="Mozilla Bug 990812"/>
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
     var FRAME_SCRIPT_GLOBAL = "data:,sendSyncMessage('test', 'global')";
     var FRAME_SCRIPT_WINDOW = "data:,sendSyncMessage('test', 'window')";
     var FRAME_SCRIPT_GROUP  = "data:,sendSyncMessage('test', 'group')";
 
-    var globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
-                     .getService(Ci.nsIMessageListenerManager);
+    var globalMM = Cc["@mozilla.org/globalmessagemanager;1"].getService();
 
     function is(val, exp, msg) {
       opener.wrappedJSObject.is(val, exp, msg);
     }
 
     /**
      * Ensures that delayed frame scripts are loaded in the expected order.
      * Global frame scripts will be loaded before delayed frame scripts from
--- a/dom/base/test/chrome/file_bug990812-2.xul
+++ b/dom/base/test/chrome/file_bug990812-2.xul
@@ -10,18 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   onload="start();">
   <label value="Mozilla Bug 990812"/>
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
     var FRAME_SCRIPT = "data:,sendAsyncMessage('test')";
     var order = ["group", "window", "global"];
 
-    var globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
-                     .getService(Ci.nsIMessageListenerManager);
+    var globalMM = Cc["@mozilla.org/globalmessagemanager;1"].getService();
 
     function is(val, exp, msg) {
       opener.wrappedJSObject.is(val, exp, msg);
     }
 
     function promiseMessage(type, mm) {
       return new Promise(function (resolve) {
         mm.addMessageListener("test", function onMessage() {
--- a/dom/base/test/chrome/file_bug990812-3.xul
+++ b/dom/base/test/chrome/file_bug990812-3.xul
@@ -10,18 +10,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   onload="start();">
   <label value="Mozilla Bug 990812"/>
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
     var FRAME_SCRIPT = "data:,addMessageListener('test', function (msg) {" +
                        "sendSyncMessage('test', msg.data)})";
 
-    var globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
-                     .getService(Ci.nsIMessageListenerManager);
+    var globalMM = Cc["@mozilla.org/globalmessagemanager;1"].getService();
 
     function is(val, exp, msg) {
       opener.wrappedJSObject.is(val, exp, msg);
     }
 
     function promiseMessage(type, mm) {
       var order = [type, "window", "global"];
 
--- a/dom/base/test/chrome/file_bug990812-4.xul
+++ b/dom/base/test/chrome/file_bug990812-4.xul
@@ -12,18 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
     var FRAME_SCRIPT1 = "data:,addMessageListener('test', function () {" +
                         "sendSyncMessage('test', 'frame1')})";
     var FRAME_SCRIPT2 = "data:,addMessageListener('test', function () {" +
                         "sendSyncMessage('test', 'frame2')})";
 
-    var globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
-                     .getService(Ci.nsIMessageListenerManager);
+    var globalMM = Cc["@mozilla.org/globalmessagemanager;1"].getService();
 
     function is(val, exp, msg) {
       opener.wrappedJSObject.is(val, exp, msg);
     }
 
     function promiseMessage(type, mm) {
       return new Promise(function (resolve) {
         mm.addMessageListener("test", function onMessage(msg) {
--- a/dom/base/test/chrome/file_bug990812-5.xul
+++ b/dom/base/test/chrome/file_bug990812-5.xul
@@ -12,18 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
     var FRAME_SCRIPT1 = "data:,addMessageListener('test', function () {" +
                         "sendSyncMessage('test', 'group1')})";
     var FRAME_SCRIPT2 = "data:,addMessageListener('test', function () {" +
                         "sendSyncMessage('test', 'group2')})";
 
-    var globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
-                     .getService(Ci.nsIMessageListenerManager);
+    var globalMM = Cc["@mozilla.org/globalmessagemanager;1"].getService();
 
     function is(val, exp, msg) {
       opener.wrappedJSObject.is(val, exp, msg);
     }
 
     function promiseTwoMessages(type, mm) {
       var numLeft = 2;
 
--- a/dom/base/test/chrome/file_bug990812.xul
+++ b/dom/base/test/chrome/file_bug990812.xul
@@ -11,18 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <label value="Mozilla Bug 990812"/>
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
     var FRAME_SCRIPT_GLOBAL = "data:,sendSyncMessage('test', 'global')";
     var FRAME_SCRIPT_WINDOW = "data:,sendSyncMessage('test', 'window')";
     var FRAME_SCRIPT_GROUP  = "data:,sendSyncMessage('test', 'group')";
 
-    var globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
-                     .getService(Ci.nsIMessageListenerManager);
+    var globalMM = Cc["@mozilla.org/globalmessagemanager;1"].getService();
 
     function is(val, exp, msg) {
       opener.wrappedJSObject.is(val, exp, msg);
     }
 
     function start() {
       globalMM.loadFrameScript(FRAME_SCRIPT_GLOBAL, true);
       messageManager.loadFrameScript(FRAME_SCRIPT_WINDOW, true);
--- a/dom/base/test/chrome/test_bug1098074_throw_from_ReceiveMessage.xul
+++ b/dom/base/test/chrome/test_bug1098074_throw_from_ReceiveMessage.xul
@@ -16,18 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   /** Test for Bug 1098074 **/
   SimpleTest.waitForExplicitFinish();
   SimpleTest.expectUncaughtException();
 
   // Tell the test to expect exactly one console error with the given parameters,
   // with SimpleTest.finish as a continuation function.
   SimpleTest.monitorConsole(SimpleTest.finish, [{errorMessage: new RegExp('acopia')}]);
 
-  var globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
-                   .getService(Ci.nsIMessageListenerManager);
+  var globalMM = Cc["@mozilla.org/globalmessagemanager;1"].getService();
   globalMM.addMessageListener("flimfniffle", function onMessage(msg) {
     globalMM.removeMessageListener("flimfniffle", onMessage);
     is(msg.data, "teufeltor", "correct message");
 
     // Cleanup the monitor after we throw.
     SimpleTest.executeSoon(SimpleTest.endMonitorConsole);
 
     throw "acopia";
--- a/dom/base/test/test_domrequesthelper.xul
+++ b/dom/base/test/test_domrequesthelper.xul
@@ -13,18 +13,17 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 
   <script type="application/javascript">
   <![CDATA[
     ChromeUtils.import("resource://gre/modules/DOMRequestHelper.jsm");
     let obs = Cc["@mozilla.org/observer-service;1"].
               getService(Ci.nsIObserverService);
-    let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"].
-              getService(Ci.nsIMessageBroadcaster);
+    let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService();
 
     function DummyHelperSubclass() {
       this.onuninit = null;
     }
     DummyHelperSubclass.prototype = {
       __proto__: DOMRequestIpcHelper.prototype,
       uninit: function() {
         if (typeof this.onuninit === "function") {
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3122,21 +3122,32 @@ ConvertExceptionToPromise(JSContext* cx,
   }
 
   rval.setObject(*promise);
   return true;
 }
 
 /* static */
 void
-CreateGlobalOptions<nsGlobalWindowInner>::TraceGlobal(JSTracer* aTrc, JSObject* aObj)
+CreateGlobalOptionsWithXPConnect::TraceGlobal(JSTracer* aTrc, JSObject* aObj)
 {
   xpc::TraceXPCGlobal(aTrc, aObj);
 }
 
+/* static */
+bool
+CreateGlobalOptionsWithXPConnect::PostCreateGlobal(JSContext* aCx,
+                                                   JS::Handle<JSObject*> aGlobal)
+{
+  // Invoking the XPCWrappedNativeScope constructor automatically hooks it
+  // up to the compartment of aGlobal.
+  (void) new XPCWrappedNativeScope(aCx, aGlobal);
+  return true;
+}
+
 static bool sRegisteredDOMNames = false;
 
 nsresult
 RegisterDOMNames()
 {
   if (sRegisteredDOMNames) {
     return NS_OK;
   }
@@ -3160,20 +3171,17 @@ bool
 CreateGlobalOptions<nsGlobalWindowInner>::PostCreateGlobal(JSContext* aCx,
                                                            JS::Handle<JSObject*> aGlobal)
 {
   nsresult rv = RegisterDOMNames();
   if (NS_FAILED(rv)) {
     return Throw(aCx, rv);
   }
 
-  // Invoking the XPCWrappedNativeScope constructor automatically hooks it
-  // up to the compartment of aGlobal.
-  (void) new XPCWrappedNativeScope(aCx, aGlobal);
-  return true;
+  return CreateGlobalOptionsWithXPConnect::PostCreateGlobal(aCx, aGlobal);
 }
 
 #ifdef DEBUG
 void
 AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitInfo,
                                JS::Handle<JS::Value> aValue)
 {
   switch (aJitInfo->returnType()) {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -45,16 +45,17 @@ class nsGenericHTMLElement;
 class nsIJSID;
 
 namespace mozilla {
 
 enum UseCounter : int16_t;
 
 namespace dom {
 class CustomElementReactionsStack;
+class MessageManagerGlobal;
 template<typename KeyType, typename ValueType> class Record;
 
 nsresult
 UnwrapArgImpl(JSContext* cx, JS::Handle<JSObject*> src, const nsIID& iid,
               void** ppArg);
 
 nsresult
 UnwrapWindowProxyImpl(JSContext* cx, JS::Handle<JSObject*> src,
@@ -3095,39 +3096,59 @@ ResolveGlobal(JSContext* aCx, JS::Handle
 
 bool
 MayResolveGlobal(const JSAtomState& aNames, jsid aId, JSObject* aMaybeObj);
 
 bool
 EnumerateGlobal(JSContext* aCx, JS::HandleObject aObj,
                 JS::AutoIdVector& aProperties, bool aEnumerableOnly);
 
-template <class T>
-struct CreateGlobalOptions
+
+struct CreateGlobalOptionsGeneric
 {
-  static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
-    ProtoAndIfaceCache::NonWindowLike;
   static void TraceGlobal(JSTracer* aTrc, JSObject* aObj)
   {
     mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj);
   }
   static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
   {
     MOZ_ALWAYS_TRUE(TryPreserveWrapper(aGlobal));
 
     return true;
   }
 };
 
+struct CreateGlobalOptionsWithXPConnect
+{
+  static void TraceGlobal(JSTracer* aTrc, JSObject* aObj);
+  static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
+};
+
+template <class T>
+using IsGlobalWithXPConnect =
+  IntegralConstant<bool,
+                   IsBaseOf<nsGlobalWindowInner, T>::value ||
+                   IsBaseOf<MessageManagerGlobal, T>::value>;
+
+template <class T>
+struct CreateGlobalOptions
+  : Conditional<IsGlobalWithXPConnect<T>::value,
+                CreateGlobalOptionsWithXPConnect,
+                CreateGlobalOptionsGeneric>::Type
+{
+  static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
+    ProtoAndIfaceCache::NonWindowLike;
+};
+
 template <>
 struct CreateGlobalOptions<nsGlobalWindowInner>
+  : public CreateGlobalOptionsWithXPConnect
 {
   static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
     ProtoAndIfaceCache::WindowLike;
-  static void TraceGlobal(JSTracer* aTrc, JSObject* aObj);
   static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
 };
 
 nsresult
 RegisterDOMNames();
 
 // The return value is true if we created and successfully performed our part of
 // the setup for the global, false otherwise.
@@ -3208,21 +3229,21 @@ class PinnedStringId
   bool init(JSContext *cx, const char *string) {
     JSString* str = JS_AtomizeAndPinString(cx, string);
     if (!str)
       return false;
     id = INTERNED_STRING_TO_JSID(cx, str);
     return true;
   }
 
-  operator const jsid& () {
+  operator const jsid& () const {
     return id;
   }
 
-  operator JS::Handle<jsid> () {
+  operator JS::Handle<jsid> () const {
     /* This is safe because we have pinned the string. */
     return JS::Handle<jsid>::fromMarkedLocation(&id);
   }
 };
 
 bool
 GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp);
 
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -147,16 +147,20 @@ DOMInterfaces = {
 'console': {
     'nativeType': 'mozilla::dom::Console',
 },
 
 'ConsoleInstance': {
     'implicitJSContext': ['clear', 'count', 'groupEnd', 'time', 'timeEnd'],
 },
 
+'ContentProcessMessageManager': {
+    'nativeType': 'mozilla::dom::ProcessGlobal'
+},
+
 'ConvolverNode': {
     'implicitJSContext': [ 'buffer' ],
 },
 
 'Coordinates': {
     'headerFile': 'nsGeoPosition.h'
 },
 
@@ -565,16 +569,28 @@ DOMInterfaces = {
 'MediaStreamTrack': {
     'concrete': False
 },
 
 'MediaRecorder': {
     'headerFile': 'MediaRecorder.h',
 },
 
+'MessageBroadcaster': {
+    'concrete': False
+},
+
+'MessageListenerManager': {
+    'concrete': False
+},
+
+'MessageSender': {
+    'concrete': False
+},
+
 'MimeType': {
     'headerFile' : 'nsMimeTypeArray.h',
     'nativeType': 'nsMimeType',
 },
 
 'MimeTypeArray': {
     'nativeType': 'nsMimeTypeArray',
 },
@@ -1010,16 +1026,20 @@ DOMInterfaces = {
 'SVGUnitTypes' : {
     'concrete': False,
 },
 
 'SVGZoomAndPan' : {
     'concrete': False,
 },
 
+'SyncMessageSender' : {
+    'concrete': False,
+},
+
 'TestFunctions': {
     'wrapperCache': False
 },
 
 'Text': {
     # Total hack to allow binding code to realize that nsTextNode can
     # in fact be cast to Text.
     'headerFile': 'nsTextNode.h',
@@ -1706,23 +1726,20 @@ addExternalIface('MozObserver', nativeTy
 addExternalIface('MozRDFCompositeDataSource', nativeType='nsIRDFCompositeDataSource',
                  notflattened=True)
 addExternalIface('MozRDFResource', nativeType='nsIRDFResource', notflattened=True)
 addExternalIface('MozTreeView', nativeType='nsITreeView',
                   headerFile='nsITreeView.h', notflattened=True)
 addExternalIface('MozWakeLockListener', headerFile='nsIDOMWakeLockListener.h')
 addExternalIface('nsIBrowserDOMWindow', nativeType='nsIBrowserDOMWindow',
                  notflattened=True)
+addExternalIface('nsIEventTarget', nativeType='nsIEventTarget', notflattened=True)
 addExternalIface('nsIFile', nativeType='nsIFile', notflattened=True)
 addExternalIface('nsILoadGroup', nativeType='nsILoadGroup',
                  headerFile='nsILoadGroup.h', notflattened=True)
-addExternalIface('nsIMessageBroadcaster', nativeType='nsIMessageBroadcaster',
-                 headerFile='nsIMessageManager.h', notflattened=True)
-addExternalIface('nsIMessageSender', nativeType='nsIMessageSender',
-                 headerFile='nsIMessageManager.h', notflattened=True)
 addExternalIface('nsIPrintSettings', nativeType='nsIPrintSettings',
                  notflattened=True)
 addExternalIface('nsISelectionListener', nativeType='nsISelectionListener')
 addExternalIface('nsIStreamListener', nativeType='nsIStreamListener', notflattened=True)
 addExternalIface('nsITransportProvider', nativeType='nsITransportProvider')
 addExternalIface('nsITreeSelection', nativeType='nsITreeSelection',
                  notflattened=True)
 addExternalIface('nsISupports', nativeType='nsISupports')
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -276,41 +276,45 @@ class CGStringTable(CGThing):
 
     const char *table[] = {
       ...
     };
 
     The uint16_t indices are smaller than the pointer equivalents, and the
     string table requires no runtime relocations.
     """
-    def __init__(self, accessorName, strings):
+    def __init__(self, accessorName, strings, static=False):
         CGThing.__init__(self)
         self.accessorName = accessorName
         self.strings = strings
+        self.static = static
 
     def declare(self):
+        if self.static:
+            return ""
         return "extern const char *%s(unsigned int aIndex);\n" % self.accessorName
 
     def define(self):
         table = ' "\\0" '.join('"%s"' % s for s in self.strings)
         indices = []
         currentIndex = 0
         for s in self.strings:
             indices.append(currentIndex)
             currentIndex += len(s) + 1  # for the null terminator
         return fill(
             """
-            const char *${name}(unsigned int aIndex)
+            ${static}const char *${name}(unsigned int aIndex)
             {
               static const char table[] = ${table};
               static const uint16_t indices[] = { ${indices} };
               static_assert(${currentIndex} <= UINT16_MAX, "string table overflow!");
               return &table[indices[aIndex]];
             }
             """,
+            static="static " if self.static else "",
             name=self.accessorName,
             table=table,
             indices=", ".join("%d" % index for index in indices),
             currentIndex=currentIndex)
 
 
 class CGNativePropertyHooks(CGThing):
     """
@@ -1098,17 +1102,17 @@ class CGHeaders(CGWrapper):
         # Grab the includes for checking hasInstance
         interfacesImplementingSelf = set()
         for d in descriptors:
             interfacesImplementingSelf |= d.interface.interfacesImplementingSelf
         implementationIncludes |= set(self.getDeclarationFilename(i) for i in
                                       interfacesImplementingSelf)
 
         # Grab the includes for the things that involve XPCOM interfaces
-        hasInstanceIncludes = set("nsIDOM" + d.interface.identifier.name + ".h" for d
+        hasInstanceIncludes = set(self.getDeclarationFilename(d.interface) for d
                                   in descriptors if
                                   d.interface.hasInterfaceObject() and
                                   NeedsGeneratedHasInstance(d) and
                                   d.interface.hasInterfacePrototypeObject())
         if len(hasInstanceIncludes) > 0:
             hasInstanceIncludes.add("nsContentUtils.h")
 
         # Now find all the things we'll need as arguments because we
@@ -13944,68 +13948,130 @@ class CGRegisterWorkletBindings(CGAbstra
                     "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS
                     + condition)
             conditions.append(condition)
         lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for
                  condition in conditions]
         lines.append(CGGeneric("return true;\n"))
         return CGList(lines, "\n").define()
 
+
+class CGSystemBindingInitIds(CGAbstractMethod):
+    def __init__(self):
+        CGAbstractMethod.__init__(self, None, 'SystemBindingInitIds', 'bool',
+                                  [Argument('JSContext*', 'aCx')])
+
+    def definition_body(self):
+        return dedent("""
+            MOZ_ASSERT(NS_IsMainThread());
+
+            if (!idsInited) {
+              // We can't use range-based for because we need the index to call IdString.
+              for (uint32_t i = 0; i < ArrayLength(properties); ++i) {
+                if (!properties[i].id.init(aCx, IdString(i))) {
+                  return false;
+                }
+              }
+              idsInited = true;
+            }
+
+            return true;
+            """)
+
+
 class CGResolveSystemBinding(CGAbstractMethod):
-    def __init__(self, config):
+    def __init__(self):
         CGAbstractMethod.__init__(self, None, 'ResolveSystemBinding', 'bool',
                                   [Argument('JSContext*', 'aCx'),
                                    Argument('JS::Handle<JSObject*>', 'aObj'),
                                    Argument('JS::Handle<jsid>', 'aId'),
                                    Argument('bool*', 'aResolvedp')])
-        self.config = config
+
+    def definition_body(self):
+        return dedent("""
+            MOZ_ASSERT(NS_IsMainThread());
+            MOZ_ASSERT(idsInited);
+
+            if (JSID_IS_VOID(aId)) {
+              for (const auto& property : properties) {
+                if (!property.enabled || property.enabled(aCx, aObj)) {
+                  if (!property.define(aCx)) {
+                    return false;
+                  }
+                  *aResolvedp = true;
+                }
+              }
+              return true;
+            }
+
+            for (const auto& property : properties) {
+              if (property.id == aId) {
+                if (!property.enabled || property.enabled(aCx, aObj)) {
+                  if (!property.define(aCx)) {
+                    return false;
+                  }
+                  *aResolvedp = true;
+                  break;
+                }
+              }
+            }
+            return true;
+            """)
+
+
+class CGMayResolveAsSystemBindingName(CGAbstractMethod):
+    def __init__(self):
+        CGAbstractMethod.__init__(self, None, 'MayResolveAsSystemBindingName', 'bool',
+                                  [Argument('jsid', 'aId')])
 
     def definition_body(self):
-        descriptors = self.config.getDescriptors(hasInterfaceObject=True,
-                                                 isExposedInSystemGlobals=True,
-                                                 register=True)
-
-        def descNameToId(name):
-            return "s%s_id" % name
-        jsidNames = [descNameToId(desc.name) for desc in descriptors]
-        jsidDecls = CGList(CGGeneric("static jsid %s;\n" % name)
-                           for name in jsidNames)
-
-        jsidInits = CGList(
-            (CGIfWrapper(
-                CGGeneric("return false;\n"),
-                '!AtomizeAndPinJSString(aCx, %s, "%s")' %
-                (descNameToId(desc.name), desc.interface.identifier.name))
-             for desc in descriptors),
-            "\n")
-        jsidInits.append(CGGeneric("idsInited = true;\n"))
-        jsidInits = CGIfWrapper(jsidInits, "!idsInited")
-        jsidInits = CGList([CGGeneric("static bool idsInited = false;\n"),
-                            jsidInits])
-
-        definitions = CGList([], "\n")
-        for desc in descriptors:
-            bindingNS = toBindingNamespace(desc.name)
-            defineCode = "!%s::GetConstructorObject(aCx)" % bindingNS
-            defineCode = CGIfWrapper(CGGeneric("return false;\n"), defineCode)
-            defineCode = CGList([defineCode,
-                                 CGGeneric("*aResolvedp = true;\n")])
-
-            condition = "JSID_IS_VOID(aId) || aId == %s" % descNameToId(desc.name)
-            if desc.isExposedConditionally():
-                condition = "(%s) && %s::ConstructorEnabled(aCx, aObj)" % (condition, bindingNS)
-
-            definitions.append(CGIfWrapper(defineCode, condition))
-
-        return CGList([CGGeneric("MOZ_ASSERT(NS_IsMainThread());\n"),
-                       jsidDecls,
-                       jsidInits,
-                       definitions,
-                       CGGeneric("return true;\n")],
-                      "\n").define()
+        return dedent("""
+            MOZ_ASSERT(NS_IsMainThread());
+            MOZ_ASSERT(idsInited);
+
+            for (const auto& property : properties) {
+              if (aId == property.id) {
+                return true;
+              }
+            }
+            return false;
+            """)
+
+
+class CGGetSystemBindingNames(CGAbstractMethod):
+    def __init__(self):
+        CGAbstractMethod.__init__(self, None, 'GetSystemBindingNames', 'void',
+                                  [Argument('JSContext*', 'aCx'),
+                                   Argument('JS::Handle<JSObject*>', 'aObj'),
+                                   Argument('JS::AutoIdVector&', 'aNames'),
+                                   Argument('bool', 'aEnumerableOnly'),
+                                   Argument('mozilla::ErrorResult&', 'aRv')])
+
+    def definition_body(self):
+        return dedent("""
+            MOZ_ASSERT(NS_IsMainThread());
+
+            if (aEnumerableOnly) {
+              return;
+            }
+
+            if (!SystemBindingInitIds(aCx)) {
+              aRv.NoteJSContextException(aCx);
+              return;
+            }
+
+            for (const auto& property : properties) {
+              if (!property.enabled || property.enabled(aCx, aObj)) {
+                if (!aNames.append(property.id)) {
+                  aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+                  return;
+                }
+              }
+            }
+            """)
 
 
 def getGlobalNames(config):
     names = []
     for desc in config.getDescriptors(registersGlobalNamesOnWindow=True):
         names.append((desc.name, desc))
         names.extend((n.identifier.name, desc) for n in desc.interface.namedConstructors)
     return names
@@ -17491,32 +17557,68 @@ class GlobalGenRoots():
         # Add include guards.
         curr = CGIncludeGuard('RegisterWorkletBindings', curr)
 
         # Done.
         return curr
 
     @staticmethod
     def ResolveSystemBinding(config):
-
-        curr = CGResolveSystemBinding(config)
+        curr = CGList([], "\n")
+        
+        descriptors = config.getDescriptors(hasInterfaceObject=True,
+                                            isExposedInSystemGlobals=True,
+                                            register=True)
+        properties = [desc.name for desc in descriptors]
+        
+        curr.append(CGStringTable("IdString", properties, static=True))
+
+        initValues = []
+        for desc in descriptors:
+            bindingNS = toBindingNamespace(desc.name)
+            if desc.isExposedConditionally():
+                enabled = "%s::ConstructorEnabled" % bindingNS
+            else:
+                enabled = "nullptr"
+            define = "%s::GetConstructorObject" % bindingNS
+            initValues.append("{ %s, %s },\n" % (enabled, define))
+        curr.append(CGGeneric(fill("""
+            struct SystemProperty
+            {
+              WebIDLGlobalNameHash::ConstructorEnabled enabled;
+              ProtoGetter define;
+              PinnedStringId id;
+            };
+          
+            static SystemProperty properties[] = {
+              $*{init}
+            };
+
+            static bool idsInited = false;
+            """,
+            init="".join(initValues))))
+
+        curr.append(CGSystemBindingInitIds())
+        curr.append(CGResolveSystemBinding())
+        curr.append(CGMayResolveAsSystemBindingName())
+        curr.append(CGGetSystemBindingNames())
 
         # Wrap all of that in our namespaces.
         curr = CGNamespace.build(['mozilla', 'dom'],
                                  CGWrapper(curr, post='\n'))
         curr = CGWrapper(curr, post='\n')
 
         # Add the includes
         defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
                           for desc in config.getDescriptors(hasInterfaceObject=True,
                                                             register=True,
                                                             isExposedInSystemGlobals=True)]
         defineIncludes.append("nsThreadUtils.h")  # For NS_IsMainThread
         defineIncludes.append("js/Id.h")  # For jsid
-        defineIncludes.append("mozilla/dom/BindingUtils.h")  # AtomizeAndPinJSString
+        defineIncludes.append("mozilla/dom/WebIDLGlobalNameHash.h")
 
         curr = CGHeaders([], [], [], [], [], defineIncludes,
                          'ResolveSystemBinding', curr)
 
         # Add include guards.
         curr = CGIncludeGuard('ResolveSystemBinding', curr)
 
         # Done.
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -228,16 +228,23 @@ ToJSValue(JSContext* aCx,
           const T& aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   return aArgument.ToObjectInternal(aCx, aValue);
 }
 
 // Accept existing JS values (which may not be same-compartment with us
 MOZ_MUST_USE inline bool
+ToJSValue(JSContext* aCx, const JS::Value& aArgument,
+          JS::MutableHandle<JS::Value> aValue)
+{
+  aValue.set(aArgument);
+  return MaybeWrapValue(aCx, aValue);
+}
+MOZ_MUST_USE inline bool
 ToJSValue(JSContext* aCx, JS::Handle<JS::Value> aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   aValue.set(aArgument);
   return MaybeWrapValue(aCx, aValue);
 }
 
 // Accept existing JS values on the Heap (which may not be same-compartment with us
@@ -307,43 +314,23 @@ ToJSValue(JSContext* aCx,
 }
 
 // Accept Promise objects, which need special handling.
 MOZ_MUST_USE bool
 ToJSValue(JSContext* aCx,
           Promise& aArgument,
           JS::MutableHandle<JS::Value> aValue);
 
-// Accept arrays of other things we accept
+// Accept arrays (and nested arrays) of other things we accept
 template <typename T>
 MOZ_MUST_USE bool
 ToJSValue(JSContext* aCx,
           T* aArguments,
           size_t aLength,
-          JS::MutableHandle<JS::Value> aValue)
-{
-  // Make sure we're called in a compartment
-  MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
-
-  JS::AutoValueVector v(aCx);
-  if (!v.resize(aLength)) {
-    return false;
-  }
-  for (size_t i = 0; i < aLength; ++i) {
-    if (!ToJSValue(aCx, aArguments[i], v[i])) {
-      return false;
-    }
-  }
-  JSObject* arrayObj = JS_NewArrayObject(aCx, v);
-  if (!arrayObj) {
-    return false;
-  }
-  aValue.setObject(*arrayObj);
-  return true;
-}
+          JS::MutableHandle<JS::Value> aValue);
 
 template <typename T>
 MOZ_MUST_USE bool
 ToJSValue(JSContext* aCx,
           const nsTArray<T>& aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   return ToJSValue(aCx, aArgument.Elements(),
@@ -364,12 +351,40 @@ template <typename T, int N>
 MOZ_MUST_USE bool
 ToJSValue(JSContext* aCx,
           const T(&aArgument)[N],
           JS::MutableHandle<JS::Value> aValue)
 {
   return ToJSValue(aCx, aArgument, N, aValue);
 }
 
+// Accept arrays of other things we accept
+template <typename T>
+MOZ_MUST_USE bool
+ToJSValue(JSContext* aCx,
+          T* aArguments,
+          size_t aLength,
+          JS::MutableHandle<JS::Value> aValue)
+{
+  // Make sure we're called in a compartment
+  MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+
+  JS::AutoValueVector v(aCx);
+  if (!v.resize(aLength)) {
+    return false;
+  }
+  for (size_t i = 0; i < aLength; ++i) {
+    if (!ToJSValue(aCx, aArguments[i], v[i])) {
+      return false;
+    }
+  }
+  JSObject* arrayObj = JS_NewArrayObject(aCx, v);
+  if (!arrayObj) {
+    return false;
+  }
+  aValue.setObject(*arrayObj);
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_ToJSValue_h */
--- a/dom/bindings/WebIDLGlobalNameHash.cpp
+++ b/dom/bindings/WebIDLGlobalNameHash.cpp
@@ -106,17 +106,17 @@ struct WebIDLNameTableEntry : public PLD
 
   enum { ALLOW_MEMMOVE = true };
 
   uint16_t mNameOffset;
   uint16_t mNameLength;
   constructors::id::ID mConstructorId;
   WebIDLGlobalNameHash::DefineGlobalName mDefine;
   // May be null if enabled unconditionally
-  WebIDLGlobalNameHash::ConstructorEnabled* mEnabled;
+  WebIDLGlobalNameHash::ConstructorEnabled mEnabled;
 };
 
 static nsTHashtable<WebIDLNameTableEntry>* sWebIDLGlobalNames;
 
 class WebIDLGlobalNamesHashReporter final : public nsIMemoryReporter
 {
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
@@ -158,17 +158,17 @@ WebIDLGlobalNameHash::Shutdown()
 {
   delete sWebIDLGlobalNames;
 }
 
 /* static */
 void
 WebIDLGlobalNameHash::Register(uint16_t aNameOffset, uint16_t aNameLength,
                                DefineGlobalName aDefine,
-                               ConstructorEnabled* aEnabled,
+                               ConstructorEnabled aEnabled,
                                constructors::id::ID aConstructorId)
 {
   const char* name = sNames + aNameOffset;
   WebIDLNameTableKey key(name, aNameLength);
   WebIDLNameTableEntry* entry = sWebIDLGlobalNames->PutEntry(key);
   entry->mNameOffset = aNameOffset;
   entry->mNameLength = aNameLength;
   entry->mDefine = aDefine;
@@ -206,17 +206,17 @@ WebIDLGlobalNameHash::DefineIfEnabled(JS
 
   if (!entry) {
     *aFound = false;
     return true;
   }
 
   *aFound = true;
 
-  ConstructorEnabled* checkEnabledForScope = entry->mEnabled;
+  ConstructorEnabled checkEnabledForScope = entry->mEnabled;
   // We do the enabled check on the current compartment of aCx, but for the
   // actual object we pass in the underlying object in the Xray case.  That
   // way the callee can decide whether to allow access based on the caller
   // or the window being touched.
   JS::Rooted<JSObject*> global(aCx,
     js::CheckedUnwrap(aObj, /* stopAtWindowProxy = */ false));
   if (!global) {
     return Throw(aCx, NS_ERROR_DOM_SECURITY_ERR);
--- a/dom/bindings/WebIDLGlobalNameHash.h
+++ b/dom/bindings/WebIDLGlobalNameHash.h
@@ -33,20 +33,20 @@ public:
 
   // Check whether a constructor should be enabled for the given object.
   // Note that the object should NOT be an Xray, since Xrays will end up
   // defining constructors on the underlying object.
   // This is a typedef for the function type itself, not the function
   // pointer, so it's more obvious that pointers to a ConstructorEnabled
   // can be null.
   typedef bool
-  (ConstructorEnabled)(JSContext* cx, JS::Handle<JSObject*> obj);
+  (*ConstructorEnabled)(JSContext* cx, JS::Handle<JSObject*> obj);
 
   static void Register(uint16_t aNameOffset, uint16_t aNameLength,
-                       DefineGlobalName aDefine, ConstructorEnabled* aEnabled,
+                       DefineGlobalName aDefine, ConstructorEnabled aEnabled,
                        constructors::id::ID aConstructorId);
 
   static void Remove(const char* aName, uint32_t aLength);
 
   // Returns false if something failed. aFound is set to true if the name is in
   // the hash, whether it's enabled or not.
   static bool DefineIfEnabled(JSContext* aCx, JS::Handle<JSObject*> aObj,
                               JS::Handle<jsid> aId,
--- a/dom/file/tests/test_ipc_messagemanager_blob.html
+++ b/dom/file/tests/test_ipc_messagemanager_blob.html
@@ -67,20 +67,20 @@
 
           // Make sure this one is always last.
           new Blob(["this ", "is ", "a ", "great ", "success!"],
                    {"type" : "text\/plain"}),
         ];
         let receivedMessageIndex = 0;
 
         let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
-        mm.addMessageListener("test:ipcClonedMessage", function(message) {
+        mm.addMessageListener("test:ipcClonedMessage", SpecialPowers.wrapCallback(function(message) {
           let data = message.json;
 
-          if (data instanceof Blob) {
+          if (SpecialPowers.call_Instanceof(data, Blob)) {
             is(receivedMessageIndex, messages.length - 1, "Blob is last");
             is (data.size,
                 messages[receivedMessageIndex].size,
                 "Correct blob size");
             is (data.type,
                 messages[receivedMessageIndex].type,
                 "Correct blob type");
 
@@ -99,25 +99,25 @@
             reader2.onload = function() {
               result2 = reader2.result == blobString ? reader2.result : "bad2";
               if (result1) {
                 is(result1, result2, "Same results");
                 done();
               }
             };
 
-            reader1.readAsText(data);
+            SpecialPowers.wrap(reader1).readAsText(data);
             reader2.readAsText(messages[receivedMessageIndex]);
             return;
           }
 
           is(message.json,
              messages[receivedMessageIndex++],
              "Got correct round-tripped response");
-        });
+        }));
         mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
                            false);
 
         for (let message of messages) {
           mm.sendAsyncMessage("test:ipcClonedMessage", message);
         }
       });
 
--- a/dom/indexedDB/test/head.js
+++ b/dom/indexedDB/test/head.js
@@ -75,21 +75,24 @@ function dismissNotification(popup)
     EventUtils.synthesizeKey("KEY_Escape");
   });
 }
 
 function waitForMessage(aMessage, browser)
 {
   return new Promise((resolve, reject) => {
     /* eslint-disable no-undef */
+    // When contentScript runs, "this" is a ContentFrameMessageManager (so that's where
+    // addEventListener will add the listener), but the non-bubbling "message" event is
+    // sent to the Window involved, so we need a capturing listener.
     function contentScript() {
       addEventListener("message", function(event) {
         sendAsyncMessage("testLocal:message",
           {message: event.data});
-      }, {once: true}, true);
+      }, {once: true, capture: true}, true);
     }
     /* eslint-enable no-undef */
 
     let script = "data:,(" + contentScript.toString() + ")();";
 
     let mm = browser.selectedBrowser.messageManager;
 
     mm.addMessageListener("testLocal:message", function listener(msg) {
--- a/dom/indexedDB/test/test_message_manager_ipc.html
+++ b/dom/indexedDB/test/test_message_manager_ipc.html
@@ -209,69 +209,69 @@ function parentFrameScript(mm) {
 
   function grabAndContinue(arg) {
     testGenerator.next(arg);
   }
 
   function* testSteps() {
     let result = yield undefined;
 
-    is(Array.isArray(result), true, "Child delivered an array of results");
+    is(SpecialPowers.Cu.getClassName(result, true), "Array", "Child delivered an array of results");
     is(result.length, 2, "Child delivered two results");
 
     let blob = result[0];
-    is(blob instanceof Blob, true, "Child delivered a blob");
+    is(SpecialPowers.call_Instanceof(blob, Blob), true, "Child delivered a blob");
     is(blob.size, blobText.length, "Blob has correct size");
     is(blob.type, blobType, "Blob has correct type");
 
     let slice = result[1];
-    is(slice instanceof Blob, true, "Child delivered a slice");
+    is(SpecialPowers.call_Instanceof(slice, Blob), true, "Child delivered a slice");
     is(slice.size, blobData[0].length, "Slice has correct size");
     is(slice.type, blobType, "Slice has correct type");
 
     info("Reading blob");
 
     let reader = new FileReader();
     reader.onload = grabAndContinue;
-    reader.readAsText(blob);
+    SpecialPowers.wrap(reader).readAsText(blob);
     yield undefined;
 
     is(reader.result, blobText, "Blob has correct data");
 
     info("Reading slice");
 
     reader = new FileReader();
     reader.onload = grabAndContinue;
-    reader.readAsText(slice);
+    SpecialPowers.wrap(reader).readAsText(slice);
     yield undefined;
 
     is(reader.result, blobData[0], "Slice has correct data");
 
     slice = blob.slice(0, blobData[0].length, blobType);
 
-    is(slice instanceof Blob, true, "Made a new slice from blob");
+    is(SpecialPowers.call_Instanceof(slice, Blob), true, "Child delivered a slice");
     is(slice.size, blobData[0].length, "Second slice has correct size");
     is(slice.type, blobType, "Second slice has correct type");
 
     info("Reading second slice");
 
     reader = new FileReader();
     reader.onload = grabAndContinue;
-    reader.readAsText(slice);
+    SpecialPowers.wrap(reader).readAsText(slice);
     yield undefined;
 
     is(reader.result, blobData[0], "Second slice has correct data");
 
     SimpleTest.finish();
   }
 
   let testGenerator = testSteps();
   testGenerator.next();
 
-  mm.addMessageListener(messageName, function(message) {
+  mm.addMessageListener(messageName, SpecialPowers.wrapCallback(function(message) {
     let data = message.data;
     switch (data.op) {
       case "info": {
         info(data.msg);
         break;
       }
 
       case "ok": {
@@ -284,17 +284,17 @@ function parentFrameScript(mm) {
         break;
       }
 
       default: {
         ok(false, "Unknown op: " + data.op);
         SimpleTest.finish();
       }
     }
-  });
+  }));
 
   mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
                       false);
 
   mm.sendAsyncMessage(messageName, blob);
 }
 
 function setup() {
--- a/dom/interfaces/base/nsIContentProcess.idl
+++ b/dom/interfaces/base/nsIContentProcess.idl
@@ -1,16 +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/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMElement;
-interface nsIMessageSender;
 interface nsIURI;
 
 [scriptable, builtinclass, uuid(456f58be-29dd-4973-885b-95aece1c9a8a)]
 interface nsIContentProcessInfo : nsISupports
 {
   /**
    * Is this content process alive?
    */
@@ -31,17 +30,17 @@ interface nsIContentProcessInfo : nsISup
    * Number of opened tabs living in this content process.
    */
   readonly attribute int32_t tabCount;
 
   /**
    * The process manager for this ContentParent (so a process message manager
    * as opposed to a frame message manager.
    */
-  readonly attribute nsIMessageSender messageManager;
+  readonly attribute nsISupports messageManager;
 };
 
 [scriptable, uuid(83ffb063-5f65-4c45-ae07-3f553e0809bb)]
 interface nsIContentProcessProvider : nsISupports
 {
   /**
    * Return this from provideProcess to create a new process.
    */
--- a/dom/interfaces/base/nsITabChild.idl
+++ b/dom/interfaces/base/nsITabChild.idl
@@ -1,26 +1,25 @@
 /* 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 "domstubs.idl"
 #include "nsIDroppedLinkHandler.idl"
 
-interface nsIContentFrameMessageManager;
 interface nsIWebBrowserChrome3;
 
 native CommandsArray(nsTArray<nsCString>);
 [ref] native CommandsArrayRef(nsTArray<nsCString>);
 
 [scriptable, uuid(1fb79c27-e760-4088-b19c-1ce3673ec24e)]
 interface nsITabChild : nsISupports
 {
-  readonly attribute nsIContentFrameMessageManager messageManager;
+  readonly attribute nsISupports messageManager;
 
   attribute nsIWebBrowserChrome3 webBrowserChrome;
 
   [notxpcom] void sendRequestFocus(in boolean canFocus);
 
   [notxpcom] void sendGetTabCount(out uint32_t tabCount);
 
   [noscript, notxpcom] void enableDisableCommands(in AString action,
--- a/dom/ipc/ContentBridgeParent.cpp
+++ b/dom/ipc/ContentBridgeParent.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/ContentBridgeParent.h"
+#include "mozilla/dom/ChromeMessageSender.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "nsXULAppAPI.h"
 #include "nsIObserverService.h"
 #include "base/task.h"
 
 using namespace mozilla::ipc;
 using namespace mozilla::jsipc;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -19,16 +19,17 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 #include "mozilla/Unused.h"
 #include "mozilla/TelemetryIPC.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/ClientManager.h"
 #include "mozilla/dom/ClientOpenWindowOpActors.h"
+#include "mozilla/dom/ChildProcessMessageManager.h"
 #include "mozilla/dom/ContentBridgeChild.h"
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -521,17 +521,17 @@ ScriptableCPInfo::GetTabCount(int32_t* a
 
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   *aTabCount = cpm->GetTabParentCountByProcessId(mContentParent->ChildID());
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-ScriptableCPInfo::GetMessageManager(nsIMessageSender** aMessenger)
+ScriptableCPInfo::GetMessageManager(nsISupports** aMessenger)
 {
   *aMessenger = nullptr;
   if (!mContentParent) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   nsCOMPtr<nsIMessageSender> manager = mContentParent->GetMessageManager();
   manager.forget(aMessenger);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -14,16 +14,17 @@
 #endif
 #include "Layers.h"
 #include "ContentChild.h"
 #include "TabParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
+#include "mozilla/dom/MessageManagerBinding.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
 #include "mozilla/dom/PaymentRequestChild.h"
 #include "mozilla/dom/TelemetryScrollProbe.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "mozilla/layers/APZCTreeManager.h"
@@ -99,17 +100,16 @@
 #include "StructuredCloneData.h"
 #include "nsViewportInfo.h"
 #include "nsILoadContext.h"
 #include "ipc/nsGUIEventIPC.h"
 #include "mozilla/gfx/Matrix.h"
 #include "UnitTransforms.h"
 #include "ClientLayerManager.h"
 #include "LayersLogging.h"
-#include "nsDOMClassInfoID.h"
 #include "nsColorPickerProxy.h"
 #include "nsContentPermissionHelper.h"
 #include "nsNetUtil.h"
 #include "nsIPermissionManager.h"
 #include "nsIURILoader.h"
 #include "nsIScriptError.h"
 #include "mozilla/EventForwards.h"
 #include "nsDeviceContext.h"
@@ -231,21 +231,20 @@ TabChildBase::DispatchMessageManagerMess
         ErrorResult rv;
         data.Write(cx, json, rv);
         if (NS_WARN_IF(rv.Failed())) {
             rv.SuppressException();
             return;
         }
     }
 
-    JS::Rooted<JSObject*> kungFuDeathGrip(cx, GetGlobal());
+    JS::Rooted<JSObject*> kungFuDeathGrip(cx, mTabChildGlobal->GetWrapper());
     // Let the BrowserElementScrolling helper (if it exists) for this
     // content manipulate the frame state.
-    RefPtr<nsFrameMessageManager> mm =
-      static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get());
+    RefPtr<nsFrameMessageManager> mm = mTabChildGlobal->GetMessageManager();
     mm->ReceiveMessage(static_cast<EventTarget*>(mTabChildGlobal), nullptr,
                        aMessageName, false, &data, nullptr, nullptr, nullptr);
 }
 
 bool
 TabChildBase::UpdateFrameHandler(const FrameMetrics& aFrameMetrics)
 {
   MOZ_ASSERT(aFrameMetrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID);
@@ -267,17 +266,17 @@ TabChildBase::UpdateFrameHandler(const F
     return true;
   }
   return true;
 }
 
 void
 TabChildBase::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics)
 {
-    if (!mGlobal || !mTabChildGlobal) {
+    if (!mTabChildGlobal) {
         return;
     }
 
     FrameMetrics newMetrics = aFrameMetrics;
     APZCCallbackHelper::UpdateRootFrame(newMetrics);
 }
 
 NS_IMETHODIMP
@@ -1104,23 +1103,21 @@ TabChild::ActorDestroy(ActorDestroyReaso
   mIPCOpen = false;
 
   DestroyWindow();
 
   if (mTabChildGlobal) {
     // We should have a message manager if the global is alive, but it
     // seems sometimes we don't.  Assert in aurora/nightly, but don't
     // crash in release builds.
-    MOZ_DIAGNOSTIC_ASSERT(mTabChildGlobal->mMessageManager);
-    if (mTabChildGlobal->mMessageManager) {
+    MOZ_DIAGNOSTIC_ASSERT(mTabChildGlobal->GetMessageManager());
+    if (mTabChildGlobal->GetMessageManager()) {
       // The messageManager relays messages via the TabChild which
       // no longer exists.
-      static_cast<nsFrameMessageManager*>
-        (mTabChildGlobal->mMessageManager.get())->Disconnect();
-      mTabChildGlobal->mMessageManager = nullptr;
+      mTabChildGlobal->DisconnectMessageManager();
     }
   }
 
   CompositorBridgeChild* compositorChild = CompositorBridgeChild::Get();
   if (compositorChild) {
     compositorChild->CancelNotifyAfterRemotePaint(this);
   }
 
@@ -1357,19 +1354,21 @@ TabChild::RecvSuppressDisplayport(const 
   return IPC_OK();
 }
 
 void
 TabChild::HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
                           const ScrollableLayerGuid& aGuid)
 {
   TABC_LOG("Handling double tap at %s with %p %p\n",
-    Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get());
-
-  if (!mGlobal || !mTabChildGlobal) {
+    Stringify(aPoint).c_str(),
+    mTabChildGlobal ? mTabChildGlobal->GetWrapper() : nullptr,
+    mTabChildGlobal.get());
+
+  if (!mTabChildGlobal || !mTabChildGlobal->GetWrapper()) {
     return;
   }
 
   // Note: there is nothing to do with the modifiers here, as we are not
   // synthesizing any sort of mouse event.
   nsCOMPtr<nsIDocument> document = GetDocument();
   CSSRect zoomToRect = CalculateRectToZoomTo(document, aPoint);
   // The double-tap can be dispatched by any scroll frame (so |aGuid| could be
@@ -1400,36 +1399,36 @@ TabChild::RecvHandleTap(const GeckoConte
   if (!presShell->GetPresContext()) {
     return IPC_OK();
   }
   CSSToLayoutDeviceScale scale(presShell->GetPresContext()->CSSToDevPixelScale());
   CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint / scale, aGuid);
 
   switch (aType) {
   case GeckoContentController::TapType::eSingleTap:
-    if (mGlobal && mTabChildGlobal) {
+    if (mTabChildGlobal) {
       mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 1);
     }
     break;
   case GeckoContentController::TapType::eDoubleTap:
     HandleDoubleTap(point, aModifiers, aGuid);
     break;
   case GeckoContentController::TapType::eSecondTap:
-    if (mGlobal && mTabChildGlobal) {
+    if (mTabChildGlobal) {
       mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 2);
     }
     break;
   case GeckoContentController::TapType::eLongTap:
-    if (mGlobal && mTabChildGlobal) {
+    if (mTabChildGlobal) {
       mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
           aInputBlockId);
     }
     break;
   case GeckoContentController::TapType::eLongTapUp:
-    if (mGlobal && mTabChildGlobal) {
+    if (mTabChildGlobal) {
       mAPZEventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
     }
     break;
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
@@ -2271,22 +2270,28 @@ TabChild::RecvActivateFrameEvent(const n
   RefPtr<ContentListener> listener = new ContentListener(this);
   chromeHandler->AddEventListener(aType, listener, capture);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvLoadRemoteScript(const nsString& aURL, const bool& aRunInGlobalScope)
 {
-  if (!mGlobal && !InitTabChildGlobal())
+  if (!InitTabChildGlobal())
     // This can happen if we're half-destroyed.  It's not a fatal
     // error.
     return IPC_OK();
 
-  LoadScriptInternal(aURL, aRunInGlobalScope);
+  JS::Rooted<JSObject*> global(RootingCx(), mTabChildGlobal->GetWrapper());
+  if (!global) {
+    // This can happen if we're half-destroyed.  It's not a fatal error.
+    return IPC_OK();
+  }
+
+  LoadScriptInternal(global, aURL, aRunInGlobalScope);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvAsyncMessage(const nsString& aMessage,
                            InfallibleTArray<CpowEntry>&& aCpows,
                            const IPC::Principal& aPrincipal,
                            const ClonedMessageData& aData)
@@ -2294,29 +2299,29 @@ TabChild::RecvAsyncMessage(const nsStrin
   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
     "TabChild::RecvAsyncMessage", EVENTS, aMessage);
 
   CrossProcessCpowHolder cpows(Manager(), aCpows);
   if (!mTabChildGlobal) {
     return IPC_OK();
   }
 
+  RefPtr<nsFrameMessageManager> mm = mTabChildGlobal->GetMessageManager();
+
   // We should have a message manager if the global is alive, but it
   // seems sometimes we don't.  Assert in aurora/nightly, but don't
   // crash in release builds.
-  MOZ_DIAGNOSTIC_ASSERT(mTabChildGlobal->mMessageManager);
-  if (!mTabChildGlobal->mMessageManager) {
+  MOZ_DIAGNOSTIC_ASSERT(mm);
+  if (!mm) {
     return IPC_OK();
   }
 
-  JS::Rooted<JSObject*> kungFuDeathGrip(dom::RootingCx(), GetGlobal());
+  JS::Rooted<JSObject*> kungFuDeathGrip(dom::RootingCx(), mTabChildGlobal->GetWrapper());
   StructuredCloneData data;
   UnpackClonedMessageDataForChild(aData, data);
-  RefPtr<nsFrameMessageManager> mm =
-    static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get());
   mm->ReceiveMessage(static_cast<EventTarget*>(mTabChildGlobal), nullptr,
                      aMessage, false, &data, &cpows, aPrincipal, nullptr);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvSwappedWithOtherRemoteLoader(const IPCTabContext& aContext)
 {
@@ -2712,37 +2717,39 @@ TabChild::DeallocPRenderFrameChild(PRend
 {
     delete aFrame;
     return true;
 }
 
 bool
 TabChild::InitTabChildGlobal()
 {
-  if (!mGlobal && !mTabChildGlobal) {
+  if (!mTabChildGlobal) {
     nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
     NS_ENSURE_TRUE(window, false);
     nsCOMPtr<EventTarget> chromeHandler =
       do_QueryInterface(window->GetChromeEventHandler());
     NS_ENSURE_TRUE(chromeHandler, false);
 
-    RefPtr<TabChildGlobal> scope = new TabChildGlobal(this);
-
-    nsISupports* scopeSupports = NS_ISUPPORTS_CAST(EventTarget*, scope);
+    RefPtr<TabChildGlobal> scope = mTabChildGlobal = new TabChildGlobal(this);
 
     NS_NAMED_LITERAL_CSTRING(globalId, "outOfProcessTabChildGlobal");
-    NS_ENSURE_TRUE(InitChildGlobalInternal(scopeSupports, globalId), false);
+    if (NS_WARN_IF(!InitChildGlobalInternal(globalId))) {
+        mTabChildGlobal = nullptr;
+        return false;
+    }
 
     scope->Init();
 
     nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler);
-    NS_ENSURE_TRUE(root, false);
+    if (NS_WARN_IF(!root)) {
+        mTabChildGlobal = nullptr;
+        return false;
+    }
     root->SetParentTarget(scope);
-
-    mTabChildGlobal = scope.forget();;
   }
 
   if (!mTriedBrowserInit) {
     mTriedBrowserInit = true;
     // Initialize the child side of the browser element machinery,
     // if appropriate.
     if (IsMozBrowser()) {
       RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT, true);
@@ -2953,24 +2960,21 @@ TabChild::MakeHidden()
 
 bool
 TabChild::IsVisible()
 {
   return mPuppetWidget && mPuppetWidget->IsVisible();
 }
 
 NS_IMETHODIMP
-TabChild::GetMessageManager(nsIContentFrameMessageManager** aResult)
+TabChild::GetMessageManager(nsISupports** aResult)
 {
-  if (mTabChildGlobal) {
-    NS_ADDREF(*aResult = mTabChildGlobal);
-    return NS_OK;
-  }
-  *aResult = nullptr;
-  return NS_ERROR_FAILURE;
+  nsCOMPtr<nsIContentFrameMessageManager> mm(mTabChildGlobal);
+  mm.forget(aResult);
+  return *aResult ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 TabChild::GetWebBrowserChrome(nsIWebBrowserChrome3** aWebBrowserChrome)
 {
   NS_IF_ADDREF(*aWebBrowserChrome = mWebBrowserChrome);
   return NS_OK;
 }
@@ -3468,33 +3472,28 @@ TabChild::BeforeUnloadRemoved()
 
 mozilla::dom::TabGroup*
 TabChild::TabGroup()
 {
   return mTabGroup;
 }
 
 TabChildGlobal::TabChildGlobal(TabChild* aTabChild)
-: mTabChild(aTabChild)
+: ContentFrameMessageManager(new nsFrameMessageManager(aTabChild)),
+  mTabChild(aTabChild)
 {
-  SetIsNotDOMBinding();
 }
 
 TabChildGlobal::~TabChildGlobal()
 {
 }
 
 void
 TabChildGlobal::Init()
 {
-  NS_ASSERTION(!mMessageManager, "Re-initializing?!?");
-  mMessageManager = new nsFrameMessageManager(mTabChild,
-                                              nullptr,
-                                              MM_CHILD);
-
   TelemetryScrollProbe::Create(this);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(TabChildGlobal)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TabChildGlobal,
                                                 DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager);
@@ -3512,80 +3511,117 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChildGlobal)
   NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsIContentFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
   NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ContentFrameMessageManager)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(TabChildGlobal, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TabChildGlobal, DOMEventTargetHelper)
 
+bool
+TabChildGlobal::WrapGlobalObject(JSContext* aCx,
+                                 JS::CompartmentOptions& aOptions,
+                                 JS::MutableHandle<JSObject*> aReflector)
+{
+  bool ok = ContentFrameMessageManagerBinding::Wrap(aCx, this, this, aOptions,
+                                                    nsJSPrincipals::get(mTabChild->GetPrincipal()),
+                                                    true, aReflector);
+  if (ok) {
+    // Since we can't rewrap we have to preserve the global's wrapper here.
+    PreserveWrapper(ToSupports(this));
+  }
+  return ok;
+}
+
 // This method isn't automatically forwarded safely because it's notxpcom, so
 // the IDL binding doesn't know what value to return.
 NS_IMETHODIMP_(bool)
 TabChildGlobal::MarkForCC()
 {
   if (mTabChild) {
     mTabChild->MarkScopesForCC();
   }
   EventListenerManager* elm = GetExistingListenerManager();
   if (elm) {
     elm->MarkForCC();
   }
-  return mMessageManager ? mMessageManager->MarkForCC() : false;
+  return MessageManagerGlobal::MarkForCC();
+}
+
+already_AddRefed<nsPIDOMWindowOuter>
+TabChildGlobal::GetContent(ErrorResult& aError)
+{
+  if (!mTabChild) {
+    aError.Throw(NS_ERROR_NULL_POINTER);
+    return nullptr;
+  }
+  nsCOMPtr<nsPIDOMWindowOuter> window =
+    do_GetInterface(mTabChild->WebNavigation());
+  return window.forget();
 }
 
 NS_IMETHODIMP
 TabChildGlobal::GetContent(mozIDOMWindowProxy** aContent)
 {
-  *aContent = nullptr;
-  if (!mTabChild)
-    return NS_ERROR_NULL_POINTER;
-  nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(mTabChild->WebNavigation());
-  window.forget(aContent);
-  return NS_OK;
+  ErrorResult rv;
+  *aContent = GetContent(rv).take();
+  return rv.StealNSResult();
+}
+
+already_AddRefed<nsIDocShell>
+TabChildGlobal::GetDocShell(ErrorResult& aError)
+{
+  if (!mTabChild) {
+    aError.Throw(NS_ERROR_NULL_POINTER);
+    return nullptr;
+  }
+  nsCOMPtr<nsIDocShell> window = do_GetInterface(mTabChild->WebNavigation());
+  return window.forget();
 }
 
 NS_IMETHODIMP
 TabChildGlobal::GetDocShell(nsIDocShell** aDocShell)
 {
-  *aDocShell = nullptr;
-  if (!mTabChild)
-    return NS_ERROR_NULL_POINTER;
-  nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mTabChild->WebNavigation());
-  docShell.swap(*aDocShell);
-  return NS_OK;
+  ErrorResult rv;
+  *aDocShell = GetDocShell(rv).take();
+  return rv.StealNSResult();
+}
+
+already_AddRefed<nsIEventTarget>
+TabChildGlobal::GetTabEventTarget()
+{
+  nsCOMPtr<nsIEventTarget> target = EventTargetFor(TaskCategory::Other);
+  return target.forget();
 }
 
 NS_IMETHODIMP
 TabChildGlobal::GetTabEventTarget(nsIEventTarget** aTarget)
 {
-  nsCOMPtr<nsIEventTarget> target = EventTargetFor(TaskCategory::Other);
-  target.forget(aTarget);
+  *aTarget = GetTabEventTarget().take();
   return NS_OK;
 }
 
 nsIPrincipal*
 TabChildGlobal::GetPrincipal()
 {
   if (!mTabChild)
     return nullptr;
   return mTabChild->GetPrincipal();
 }
 
 JSObject*
 TabChildGlobal::GetGlobalJSObject()
 {
   NS_ENSURE_TRUE(mTabChild, nullptr);
-  return mTabChild->GetGlobal();
+  return GetWrapper();
 }
 
 nsresult
 TabChildGlobal::Dispatch(TaskCategory aCategory,
                          already_AddRefed<nsIRunnable>&& aRunnable)
 {
   if (mTabChild && mTabChild->TabGroup()) {
     return mTabChild->TabGroup()->Dispatch(aCategory, Move(aRunnable));
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -2,16 +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 mozilla_dom_TabChild_h
 #define mozilla_dom_TabChild_h
 
+#include "mozilla/dom/ContentFrameMessageManager.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "nsIWebNavigation.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsIWebBrowserChrome2.h"
 #include "nsIEmbeddingSiteWindow.h"
 #include "nsIWebBrowserChromeFocus.h"
 #include "nsIDOMEventListener.h"
@@ -73,106 +74,67 @@ struct AutoCacheNativeKeyCommands;
 namespace dom {
 
 class TabChild;
 class TabGroup;
 class ClonedMessageData;
 class CoalescedMouseData;
 class CoalescedWheelData;
 
-class TabChildGlobal : public DOMEventTargetHelper,
+class TabChildGlobal : public ContentFrameMessageManager,
                        public nsIContentFrameMessageManager,
                        public nsIScriptObjectPrincipal,
                        public nsIGlobalObject,
                        public nsSupportsWeakReference
 {
 public:
   explicit TabChildGlobal(TabChild* aTabChild);
   void Init();
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TabChildGlobal, DOMEventTargetHelper)
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override
+  {
+    MOZ_CRASH("We should never get here!");
+  }
+  bool WrapGlobalObject(JSContext* aCx,
+                        JS::CompartmentOptions& aOptions,
+                        JS::MutableHandle<JSObject*> aReflector);
+
+  virtual already_AddRefed<nsPIDOMWindowOuter> GetContent(ErrorResult& aError) override;
+  virtual already_AddRefed<nsIDocShell> GetDocShell(ErrorResult& aError) override;
+  virtual already_AddRefed<nsIEventTarget> GetTabEventTarget() override;
+
   NS_FORWARD_SAFE_NSIMESSAGELISTENERMANAGER(mMessageManager)
   NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
+  NS_FORWARD_SAFE_NSISYNCMESSAGESENDER(mMessageManager);
   NS_FORWARD_SAFE_NSIMESSAGEMANAGERGLOBAL(mMessageManager)
-  NS_IMETHOD SendSyncMessage(const nsAString& aMessageName,
-                             JS::Handle<JS::Value> aObject,
-                             JS::Handle<JS::Value> aRemote,
-                             nsIPrincipal* aPrincipal,
-                             JSContext* aCx,
-                             uint8_t aArgc,
-                             JS::MutableHandle<JS::Value> aRetval) override
-  {
-    return mMessageManager
-      ? mMessageManager->SendSyncMessage(aMessageName, aObject, aRemote,
-                                         aPrincipal, aCx, aArgc, aRetval)
-      : NS_ERROR_NULL_POINTER;
-  }
-  NS_IMETHOD SendRpcMessage(const nsAString& aMessageName,
-                            JS::Handle<JS::Value> aObject,
-                            JS::Handle<JS::Value> aRemote,
-                            nsIPrincipal* aPrincipal,
-                            JSContext* aCx,
-                            uint8_t aArgc,
-                            JS::MutableHandle<JS::Value> aRetval) override
-  {
-    return mMessageManager
-      ? mMessageManager->SendRpcMessage(aMessageName, aObject, aRemote,
-                                        aPrincipal, aCx, aArgc, aRetval)
-      : NS_ERROR_NULL_POINTER;
-  }
-  NS_IMETHOD GetContent(mozIDOMWindowProxy** aContent) override;
-  NS_IMETHOD GetDocShell(nsIDocShell** aDocShell) override;
-  NS_IMETHOD GetTabEventTarget(nsIEventTarget** aTarget) override;
-
-  nsresult AddEventListener(const nsAString& aType,
-                            nsIDOMEventListener* aListener,
-                            bool aUseCapture)
-  {
-    // By default add listeners only for trusted events!
-    return DOMEventTargetHelper::AddEventListener(aType, aListener,
-                                                  aUseCapture, false, 2);
-  }
-  using DOMEventTargetHelper::AddEventListener;
-  NS_IMETHOD AddEventListener(const nsAString& aType,
-                              nsIDOMEventListener* aListener,
-                              bool aUseCapture, bool aWantsUntrusted,
-                              uint8_t optional_argc) override
-  {
-    return DOMEventTargetHelper::AddEventListener(aType, aListener,
-                                                  aUseCapture,
-                                                  aWantsUntrusted,
-                                                  optional_argc);
-  }
+  NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER
 
   nsresult
   GetEventTargetParent(EventChainPreVisitor& aVisitor) override
   {
     aVisitor.mForceContentDispatch = true;
     return NS_OK;
   }
 
   virtual nsIPrincipal* GetPrincipal() override;
   virtual JSObject* GetGlobalJSObject() override;
 
-  virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override
-  {
-    MOZ_CRASH("TabChildGlobal doesn't use DOM bindings!");
-  }
-
   // Dispatch a runnable related to the global.
   virtual nsresult Dispatch(mozilla::TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   virtual nsISerialEventTarget*
   EventTargetFor(mozilla::TaskCategory aCategory) const override;
 
   virtual AbstractThread*
   AbstractMainThreadFor(mozilla::TaskCategory aCategory) override;
 
-  nsCOMPtr<nsIContentFrameMessageManager> mMessageManager;
   RefPtr<TabChild> mTabChild;
 
 protected:
   ~TabChildGlobal();
 };
 
 class ContentListener final : public nsIDOMEventListener
 {
@@ -197,16 +159,23 @@ protected:
   typedef mozilla::widget::PuppetWidget PuppetWidget;
 
 public:
   TabChildBase();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TabChildBase)
 
+  virtual bool WrapGlobalObject(JSContext* aCx,
+                                JS::CompartmentOptions& aOptions,
+                                JS::MutableHandle<JSObject*> aReflector) override
+  {
+    return mTabChildGlobal->WrapGlobalObject(aCx, aOptions, aReflector);
+  }
+
   virtual nsIWebNavigation* WebNavigation() const = 0;
   virtual PuppetWidget* WebWidget() = 0;
   nsIPrincipal* GetPrincipal() { return mPrincipal; }
   virtual bool DoUpdateZoomConstraints(const uint32_t& aPresShellId,
                                        const mozilla::layers::FrameMetrics::ViewID& aViewId,
                                        const Maybe<mozilla::layers::ZoomConstraints>& aConstraints) = 0;
 
   virtual ScreenIntSize GetInnerSize() = 0;
@@ -305,16 +274,21 @@ public:
   NS_DECL_NSITABCHILD
   NS_DECL_NSIOBSERVER
   NS_DECL_NSITOOLTIPLISTENER
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(TabChild, TabChildBase)
 
   FORWARD_SHMEM_ALLOCATOR_TO(PBrowserChild)
 
+  nsIContentFrameMessageManager* GetMessageManager()
+  {
+    return mTabChildGlobal;
+  }
+
   /**
    * MessageManagerCallback methods that we override.
    */
   virtual bool DoSendBlockingMessage(JSContext* aCx,
                                      const nsAString& aMessage,
                                      StructuredCloneData& aData,
                                      JS::Handle<JSObject *> aCpows,
                                      nsIPrincipal* aPrincipal,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -8,16 +8,17 @@
 
 #include "TabParent.h"
 
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/DocAccessibleParent.h"
 #include "nsAccessibilityService.h"
 #endif
 #include "mozilla/BrowserElementParent.h"
+#include "mozilla/dom/ChromeMessageSender.h"
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DataTransferItemList.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/IPCBlobUtils.h"
 #include "mozilla/dom/PaymentRequestParent.h"
--- a/dom/ipc/nsIContentChild.cpp
+++ b/dom/ipc/nsIContentChild.cpp
@@ -2,16 +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 "nsIContentChild.h"
 
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ChildProcessMessageManager.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/InputStreamUtils.h"
--- a/dom/ipc/nsIContentParent.cpp
+++ b/dom/ipc/nsIContentParent.cpp
@@ -3,33 +3,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/. */
 
 #include "nsIContentParent.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/ChromeMessageSender.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/ContentProcessManager.h"
 #include "mozilla/dom/PTabContext.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamParent.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/IPCStreamDestination.h"
 #include "mozilla/ipc/IPCStreamSource.h"
 #include "mozilla/Unused.h"
 
-#include "nsFrameMessageManager.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsPrintfCString.h"
 #include "xpcpublic.h"
 
 using namespace mozilla::jsipc;
 
 // XXX need another bug to move this to a common header.
 #ifdef DISABLE_ASSERTS_FOR_FUZZING
--- a/dom/ipc/nsIContentParent.h
+++ b/dom/ipc/nsIContentParent.h
@@ -39,16 +39,17 @@ class PParentToChildStreamParent;
 class PIPCBlobInputStreamParent;
 }
 
 namespace dom {
 
 class Blob;
 class BlobConstructorParams;
 class BlobImpl;
+class ChromeMessageSender;
 class ContentParent;
 class ContentBridgeParent;
 class IPCTabContext;
 class PBrowserParent;
 
 class nsIContentParent : public nsISupports
                        , public mozilla::dom::ipc::MessageManagerCallback
                        , public CPOWManagerGetter
@@ -83,17 +84,17 @@ public:
   virtual bool IsContentParent() const { return false; }
 
   ContentParent* AsContentParent();
 
   virtual bool IsContentBridgeParent() const { return false; }
 
   ContentBridgeParent* AsContentBridgeParent();
 
-  nsFrameMessageManager* GetMessageManager() const { return mMessageManager; }
+  mozilla::dom::ChromeMessageSender* GetMessageManager() const { return mMessageManager; }
 
   virtual bool SendActivate(PBrowserParent* aTab) = 0;
 
   virtual bool SendDeactivate(PBrowserParent* aTab) = 0;
 
   virtual int32_t Pid() const = 0;
 
   virtual mozilla::ipc::PParentToChildStreamParent*
@@ -156,17 +157,17 @@ protected: // IPDL methods
                                                  const IPC::Principal& aPrincipal,
                                                  nsTArray<ipc::StructuredCloneData>* aRetvals);
   virtual mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aMsg,
                                                    InfallibleTArray<jsipc::CpowEntry>&& aCpows,
                                                    const IPC::Principal& aPrincipal,
                                                    const ClonedMessageData& aData);
 
 protected: // members
-  RefPtr<nsFrameMessageManager> mMessageManager;
+  RefPtr<mozilla::dom::ChromeMessageSender> mMessageManager;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentParent, NS_ICONTENTPARENT_IID)
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_nsIContentParent_h */
--- a/dom/ipc/remote-test.js
+++ b/dom/ipc/remote-test.js
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 dump("Loading remote script!\n");
 dump(content + "\n");
 
-var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]
-                            .getService(Components.interfaces.nsISyncMessageSender);
+var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"].getService();
 cpm.addMessageListener("cpm-async",
   function(m) {
     cpm.sendSyncMessage("ppm-sync");
     dump(content.document.documentElement);
     cpm.sendAsyncMessage("ppm-async");
   });
 
 var dshell = content.QueryInterface(Ci.nsIInterfaceRequestor)
--- a/dom/ipc/test.xul
+++ b/dom/ipc/test.xul
@@ -80,21 +80,21 @@
     function closeWindow() {
       window.close();
     }
 
     function initRemoteFrameScript() {
       // 1. Test that loading a script works, and that accessing process level mm and
       //     global mm works.
       var ppm = Components.classes["@mozilla.org/parentprocessmessagemanager;1"]
-                          .getService(Components.interfaces.nsIMessageBroadcaster);
+                          .getService();
       var gm = Components.classes["@mozilla.org/globalmessagemanager;1"]
-                         .getService(Components.interfaces.nsIMessageBroadcaster);
+                         .getService();
       var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]
-                            .getService(Components.interfaces.nsISyncMessageSender);
+                          .getService();
 
       if (ppm.childCount != 2) {
         alert("Should have two child processes!");
       }
       var childprocessmm = ppm.getChildAt(1); // 0 is the in-process child process mm
       
       childprocessmm.addMessageListener("ppm-sync",
         function(m) {
--- a/dom/ipc/tests/test_blob_sliced_from_child_process.html
+++ b/dom/ipc/tests/test_blob_sliced_from_child_process.html
@@ -48,37 +48,37 @@ function parentFrameScript(mm) {
   const sliceText = "an";
 
   let receivedBlob = false;
   let receivedSlice = false;
 
   let finishedTestingBlob = false;
   let finishedTestingSlice = false;
 
-  mm.addMessageListener(messageName, function(message) {
+  mm.addMessageListener(messageName, SpecialPowers.wrapCallback(function(message) {
     if ("blob" in message.data) {
       is(receivedBlob, false, "Have not yet received Blob");
       is(receivedSlice, false, "Have not yet received Slice");
       is(finishedTestingBlob, false, "Have not yet finished testing Blob");
       is(finishedTestingSlice, false, "Have not yet finished testing Slice");
 
       receivedBlob = true;
 
       let blob = message.data.blob;
 
-      ok(blob instanceof Blob, "Received a Blob");
+      ok(SpecialPowers.call_Instanceof(blob, Blob), "Received a Blob");
       is(blob.size, blobText.length, "Blob has correct size");
       is(blob.type, blobType, "Blob has correct type");
 
       let slice = blob.slice(blobText.length -
                                 blobData[blobData.length - 1].length,
                               blob.size,
                               blobType);
 
-      ok(slice instanceof Blob, "Slice returned a Blob");
+      ok(SpecialPowers.call_Instanceof(slice, Blob), "Slice returned a Blob");
       is(slice.size,
           blobData[blobData.length - 1].length,
           "Slice has correct size");
       is(slice.type, blobType, "Slice has correct type");
 
       let reader = new FileReader();
       reader.onload = function() {
         is(reader.result,
@@ -86,63 +86,63 @@ function parentFrameScript(mm) {
             "Slice has correct data");
 
         finishedTestingBlob = true;
 
         if (finishedTestingSlice) {
           SimpleTest.finish();
         }
       };
-      reader.readAsText(slice);
+      SpecialPowers.wrap(reader).readAsText(slice);
 
       return;
     }
 
     if ("slice" in message.data) {
       is(receivedBlob, true, "Already received Blob");
       is(receivedSlice, false, "Have not yet received Slice");
       is(finishedTestingSlice, false, "Have not yet finished testing Slice");
 
       receivedSlice = true;
 
       let slice = message.data.slice;
 
-      ok(slice instanceof Blob, "Received a Blob for slice");
+      ok(SpecialPowers.call_Instanceof(slice, Blob), "Received a Blob for slice");
       is(slice.size, sliceText.length, "Slice has correct size");
       is(slice.type, blobType, "Slice has correct type");
 
       let reader = new FileReader();
       reader.onload = function() {
         is(reader.result, sliceText, "Slice has correct data");
 
         let slice2 = slice.slice(1, 2, blobType);
 
-        ok(slice2 instanceof Blob, "Slice returned a Blob");
+        ok(SpecialPowers.call_Instanceof(slice2, Blob), "Slice returned a Blob");
         is(slice2.size, 1, "Slice has correct size");
         is(slice2.type, blobType, "Slice has correct type");
 
         let reader2 = new FileReader();
         reader2.onload = function() {
           is(reader2.result, sliceText[1], "Slice has correct data");
 
           finishedTestingSlice = true;
 
           if (finishedTestingBlob) {
             SimpleTest.finish();
           }
         };
-        reader2.readAsText(slice2);
+        SpecialPowers.wrap(reader2).readAsText(slice2);
       };
-      reader.readAsText(slice);
+      SpecialPowers.wrap(reader).readAsText(slice);
 
       return;
     }
 
     ok(false, "Received a bad message: " + JSON.stringify(message.data));
-  });
+  }));
 
   mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
                       false);
 }
 
 function setup() {
   info("Got load event");
 
--- a/dom/ipc/tests/test_blob_sliced_from_parent_process.html
+++ b/dom/ipc/tests/test_blob_sliced_from_parent_process.html
@@ -112,33 +112,33 @@ function parentFrameScript(mm) {
 
   function grabAndContinue(arg) {
     testGenerator.next(arg);
   }
 
   function* testSteps() {
     let slice = yield undefined;
 
-    ok(slice instanceof Blob, "Received a Blob");
+    ok(SpecialPowers.call_Instanceof(slice, Blob), "Received a Blob");
     is(slice.size, sliceText.length, "Slice has correct size");
     is(slice.type, blobType, "Slice has correct type");
 
     let reader = new FileReader();
     reader.onload = grabAndContinue;
-    reader.readAsText(slice);
+    SpecialPowers.wrap(reader).readAsText(slice);
     yield undefined;
 
     is(reader.result, sliceText, "Slice has correct data");
     SimpleTest.finish();
   }
 
   let testGenerator = testSteps();
   testGenerator.next();
 
-  mm.addMessageListener(messageName, function(message) {
+  mm.addMessageListener(messageName, SpecialPowers.wrapCallback(function(message) {
     let data = message.data;
     switch (data.op) {
       case "info": {
         info(data.msg);
         break;
       }
 
       case "ok": {
@@ -151,17 +151,17 @@ function parentFrameScript(mm) {
         break;
       }
 
       default: {
         ok(false, "Unknown op: " + data.op);
         SimpleTest.finish();
       }
     }
-  });
+  }));
 
   mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
                       false);
 
   let blob = new Blob(blobData, { type: blobType });
   mm.sendAsyncMessage(messageName, blob);
 }
 
--- a/dom/ipc/tests/test_bug1086684.html
+++ b/dom/ipc/tests/test_bug1086684.html
@@ -47,17 +47,18 @@
     let test;
     function* testStructure(mm) {
       let value;
 
       function testDone(msg) {
         test.next(msg.data.value);
       }
 
-      mm.addMessageListener("testBug1086684:childDone", testDone);
+      mm.addMessageListener("testBug1086684:childDone",
+                            SpecialPowers.wrapCallback(testDone));
 
       let blob = new Blob([]);
       let file = new File([blob], "helloworld.txt", { type: "text/plain" });
 
       mm.sendAsyncMessage("testBug1086684:parentReady", { file });
       value = yield;
 
       // Note that the "helloworld.txt" passed in above doesn't affect the
--- a/dom/ipc/tests/test_cpow_cookies.html
+++ b/dom/ipc/tests/test_cpow_cookies.html
@@ -35,17 +35,18 @@
     let test;
     function* testStructure(mm) {
       let lastResult;
 
       function testDone(msg) {
         test.next(msg.data);
       }
 
-      mm.addMessageListener("testCPOWCookies:test1Finished", testDone);
+      mm.addMessageListener("testCPOWCookies:test1Finished",
+                            SpecialPowers.wrapCallback(testDone));
 
       mm.sendAsyncMessage("testCPOWCookies:test1", {});
       lastResult = yield;
       ok(lastResult.pass, "got the right answer and didn't crash");
 
       SimpleTest.finish();
     }
 
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -63,19 +63,18 @@ class GlobalPCList {
     this._nextId = 1;
     Services.obs.addObserver(this, "inner-window-destroyed", true);
     Services.obs.addObserver(this, "profile-change-net-teardown", true);
     Services.obs.addObserver(this, "network:offline-about-to-go-offline", true);
     Services.obs.addObserver(this, "network:offline-status-changed", true);
     Services.obs.addObserver(this, "gmp-plugin-crash", true);
     Services.obs.addObserver(this, "PeerConnection:response:allow", true);
     Services.obs.addObserver(this, "PeerConnection:response:deny", true);
-    if (Cc["@mozilla.org/childprocessmessagemanager;1"]) {
-      let mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
-      mm.addMessageListener("gmp-plugin-crash", this);
+    if (Services.cpmm) {
+      Services.cpmm.addMessageListener("gmp-plugin-crash", this);
     }
   }
 
   notifyLifecycleObservers(pc, type) {
     for (var key of Object.keys(this._lifecycleobservers)) {
       this._lifecycleobservers[key](pc, pc._winID, type);
     }
   }
--- a/dom/notification/NotificationDB.jsm
+++ b/dom/notification/NotificationDB.jsm
@@ -10,20 +10,16 @@ const DEBUG = false;
 function debug(s) { dump("-*- NotificationDB component: " + s + "\n"); }
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/osfile.jsm");
 
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
-                                   "@mozilla.org/parentprocessmessagemanager;1",
-                                   "nsIMessageListenerManager");
-
 XPCOMUtils.defineLazyServiceGetter(this, "notificationStorage",
                                    "@mozilla.org/notificationStorage;1",
                                    "nsINotificationStorage");
 
 const NOTIFICATION_STORE_DIR = OS.Constants.Path.profileDir;
 const NOTIFICATION_STORE_PATH =
         OS.Path.join(NOTIFICATION_STORE_DIR, "notificationstore.json");
 
@@ -51,23 +47,23 @@ var NotificationDB = {
     this.runningTask = null;
 
     Services.obs.addObserver(this, "xpcom-shutdown");
     this.registerListeners();
   },
 
   registerListeners: function() {
     for (let message of kMessages) {
-      ppmm.addMessageListener(message, this);
+      Services.ppmm.addMessageListener(message, this);
     }
   },
 
   unregisterListeners: function() {
     for (let message of kMessages) {
-      ppmm.removeMessageListener(message, this);
+      Services.ppmm.removeMessageListener(message, this);
     }
   },
 
   observe: function(aSubject, aTopic, aData) {
     if (DEBUG) debug("Topic: " + aTopic);
     if (aTopic == "xpcom-shutdown") {
       this._shutdownInProgress = true;
       Services.obs.removeObserver(this, "xpcom-shutdown");
--- a/dom/notification/NotificationStorage.js
+++ b/dom/notification/NotificationStorage.js
@@ -10,20 +10,16 @@ function debug(s) { dump("-*- Notificati
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const NOTIFICATIONSTORAGE_CID = "{37f819b0-0b5c-11e3-8ffd-0800200c9a66}";
 const NOTIFICATIONSTORAGE_CONTRACTID = "@mozilla.org/notificationStorage;1";
 
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsIMessageSender");
-
 const kMessageNotificationGetAllOk = "Notification:GetAll:Return:OK";
 const kMessageNotificationGetAllKo = "Notification:GetAll:Return:KO";
 const kMessageNotificationSaveKo   = "Notification:Save:Return:KO";
 const kMessageNotificationDeleteKo = "Notification:Delete:Return:KO";
 
 const kMessages = [
   kMessageNotificationGetAllOk,
   kMessageNotificationGetAllKo,
@@ -45,23 +41,23 @@ function NotificationStorage() {
   // Register for message listeners.
   this.registerListeners();
 }
 
 NotificationStorage.prototype = {
 
   registerListeners: function() {
     for (let message of kMessages) {
-      cpmm.addMessageListener(message, this);
+      Services.cpmm.addMessageListener(message, this);
     }
   },
 
   unregisterListeners: function() {
     for (let message of kMessages) {
-      cpmm.removeMessageListener(message, this);
+      Services.cpmm.removeMessageListener(message, this);
     }
   },
 
   observe: function(aSubject, aTopic, aData) {
     if (DEBUG) debug("Topic: " + aTopic);
     if (aTopic === "xpcom-shutdown") {
       Services.obs.removeObserver(this, "xpcom-shutdown");
       this.unregisterListeners();
@@ -99,17 +95,17 @@ NotificationStorage.prototype = {
         var oldNotification = this._byTag[origin][tag];
         delete this._notifications[oldNotification.id];
       }
 
       this._byTag[origin][tag] = notification;
     };
 
     if (serviceWorkerRegistrationScope) {
-      cpmm.sendAsyncMessage("Notification:Save", {
+      Services.cpmm.sendAsyncMessage("Notification:Save", {
         origin: origin,
         notification: notification
       });
     }
   },
 
   get: function(origin, tag, callback) {
     if (DEBUG) { debug("GET: " + origin + " " + tag); }
@@ -144,17 +140,17 @@ NotificationStorage.prototype = {
     var notification = this._notifications[id];
     if (notification) {
       if (notification.tag) {
         delete this._byTag[origin][notification.tag];
       }
       delete this._notifications[id];
     }
 
-    cpmm.sendAsyncMessage("Notification:Delete", {
+    Services.cpmm.sendAsyncMessage("Notification:Delete", {
       origin: origin,
       id: id
     });
   },
 
   receiveMessage: function(message) {
     var request = this._requests[message.data.requestID];
 
@@ -189,17 +185,17 @@ NotificationStorage.prototype = {
   _fetchFromDB: function(origin, tag, callback) {
     var request = {
       origin: origin,
       tag: tag,
       callback: callback
     };
     var requestID = this._requestCount++;
     this._requests[requestID] = request;
-    cpmm.sendAsyncMessage("Notification:GetAll", {
+    Services.cpmm.sendAsyncMessage("Notification:GetAll", {
       origin: origin,
       requestID: requestID
     });
   },
 
   _fetchFromCache: function(origin, tag, callback) {
     var notifications = [];
     // If a tag was specified and we have a notification
--- a/dom/notification/test/unit/common_test_notificationdb.js
+++ b/dom/notification/test/unit/common_test_notificationdb.js
@@ -1,17 +1,13 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsIMessageSender");
-
 function getNotificationObject(app, id, tag) {
   return {
     origin: "https://" + app + ".gaiamobile.org/",
     id: id,
     title: app + "Notification:" + Date.now(),
     dir: "auto",
     lang: "",
     body: app + " notification body",
@@ -31,26 +27,26 @@ function startNotificationDB() {
   ChromeUtils.import("resource://gre/modules/NotificationDB.jsm");
 }
 
 // Helper function to add a listener, send message and treat the reply
 function addAndSend(msg, reply, callback, payload, runNext = true) {
   let handler = {
     receiveMessage: function(message) {
       if (message.name === reply) {
-        cpmm.removeMessageListener(reply, handler);
+        Services.cpmm.removeMessageListener(reply, handler);
         callback(message);
         if (runNext) {
           run_next_test();
         }
       }
     }
   };
-  cpmm.addMessageListener(reply, handler);
-  cpmm.sendAsyncMessage(msg, payload);
+  Services.cpmm.addMessageListener(reply, handler);
+  Services.cpmm.sendAsyncMessage(msg, payload);
 }
 
 // helper fonction, comparing two notifications
 function compareNotification(notif1, notif2) {
   // retrieved notification should be the second one sent
   for (let prop in notif1) {
     // compare each property
     Assert.equal(notif1[prop], notif2[prop]);
--- a/dom/notification/test/unit/test_notificationdb.js
+++ b/dom/notification/test/unit/test_notificationdb.js
@@ -159,36 +159,36 @@ add_test(function test_send_two_get_one(
     getNotificationObject("system", "{f271f9ee-3955-4c10-b1f2-af552fb270ee}", tag);
   let systemNotification2 =
     getNotificationObject("system", "{8ef9a628-f0f4-44b4-820d-c117573c33e3}", tag);
 
   let msgGetReply = "Notification:GetAll:Return:OK";
   let msgGetNotifHandler = {
     receiveMessage: function(message) {
       if (message.name === msgGetReply) {
-        cpmm.removeMessageListener(msgGetReply, msgGetNotifHandler);
+        Services.cpmm.removeMessageListener(msgGetReply, msgGetNotifHandler);
         let notifications = message.data.notifications;
         // same tag, so replaced
         Assert.equal(1, notifications.length);
         // compare the content
         compareNotification(systemNotification2, notifications[0]);
         run_next_test();
       }
     }
   };
 
-  cpmm.addMessageListener(msgGetReply, msgGetNotifHandler);
+  Services.cpmm.addMessageListener(msgGetReply, msgGetNotifHandler);
 
   let msgSaveReply = "Notification:Save:Return:OK";
   let msgSaveCalls = 0;
   let msgSaveHandler = function(message) {
     msgSaveCalls++;
     // Once both request have been sent, trigger getall
     if (msgSaveCalls === 2) {
-      cpmm.sendAsyncMessage("Notification:GetAll", {
+      Services.cpmm.sendAsyncMessage("Notification:GetAll", {
         origin: systemNotification1.origin,
         requestID: message.data.requestID + 2 // 12, 13
       });
     }
   };
 
   addAndSend("Notification:Save", msgSaveReply, msgSaveHandler, {
     origin: systemNotification1.origin,
@@ -242,57 +242,57 @@ add_test(function test_send_two_get_two(
 
         // first call should be system notification
         if (msgGetCalls === 1) {
           compareNotification(systemNotification1, notifications[0]);
         }
 
         // second and last call should be calendar notification
         if (msgGetCalls === 2) {
-          cpmm.removeMessageListener(msgGetReply, msgGetHandler);
+          Services.cpmm.removeMessageListener(msgGetReply, msgGetHandler);
           compareNotification(calendarNotification2, notifications[0]);
           run_next_test();
         }
       }
     }
   };
-  cpmm.addMessageListener(msgGetReply, msgGetHandler);
+  Services.cpmm.addMessageListener(msgGetReply, msgGetHandler);
 
   let msgSaveReply = "Notification:Save:Return:OK";
   let msgSaveCalls = 0;
   let msgSaveHandler = {
     receiveMessage: function(message) {
       if (message.name === msgSaveReply) {
         msgSaveCalls++;
         if (msgSaveCalls === 2) {
-          cpmm.removeMessageListener(msgSaveReply, msgSaveHandler);
+          Services.cpmm.removeMessageListener(msgSaveReply, msgSaveHandler);
 
           // Trigger getall for each origin
-          cpmm.sendAsyncMessage("Notification:GetAll", {
+          Services.cpmm.sendAsyncMessage("Notification:GetAll", {
             origin: systemNotification1.origin,
             requestID: message.data.requestID + 1 // 22
           });
 
-          cpmm.sendAsyncMessage("Notification:GetAll", {
+          Services.cpmm.sendAsyncMessage("Notification:GetAll", {
             origin: calendarNotification2.origin,
             requestID: message.data.requestID + 2 // 23
           });
         }
       }
     }
   };
-  cpmm.addMessageListener(msgSaveReply, msgSaveHandler);
+  Services.cpmm.addMessageListener(msgSaveReply, msgSaveHandler);
 
-  cpmm.sendAsyncMessage("Notification:Save", {
+  Services.cpmm.sendAsyncMessage("Notification:Save", {
     origin: systemNotification1.origin,
     notification: systemNotification1,
     requestID: requestID // 20
   });
 
-  cpmm.sendAsyncMessage("Notification:Save", {
+  Services.cpmm.sendAsyncMessage("Notification:Save", {
     origin: calendarNotification2.origin,
     notification: calendarNotification2,
     requestID: (requestID + 1) // 21
   });
 });
 
 // Cleanup previous notification
 add_test(function test_delete_previous() {
--- a/dom/presentation/tests/mochitest/test_presentation_dc_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/test_presentation_dc_receiver_oop.html
@@ -125,27 +125,27 @@ function setup() {
     gScript.addMessageListener('control-channel-closed', function controlChannelClosedHandler(aReason) {
       gScript.removeMessageListener('control-channel-closed', controlChannelClosedHandler);
       is(aReason, SpecialPowers.Cr.NS_OK, "The control channel is closed normally.");
     });
 
     var mm = SpecialPowers.getBrowserFrameMessageManager(receiverIframe);
     mm.addMessageListener('check-navigator', function checknavigatorHandler(aSuccess) {
       mm.removeMessageListener('check-navigator', checknavigatorHandler);
-      ok(aSuccess.data.data, "buildDataChannel get correct window object");
+      ok(SpecialPowers.wrap(aSuccess).data.data, "buildDataChannel get correct window object");
     });
 
     mm.addMessageListener('data-transport-notification-enabled', function dataTransportNotificationEnabledHandler() {
       mm.removeMessageListener('data-transport-notification-enabled', dataTransportNotificationEnabledHandler);
       info("Data notification is enabled for data transport channel.");
     });
 
     mm.addMessageListener('data-transport-closed', function dataTransportClosedHandler(aReason) {
       mm.removeMessageListener('data-transport-closed', dataTransportClosedHandler);
-      is(aReason.data.data, SpecialPowers.Cr.NS_OK, "The data transport should be closed normally.");
+      is(SpecialPowers.wrap(aReason).data.data, SpecialPowers.Cr.NS_OK, "The data transport should be closed normally.");
     });
 
     aResolve();
   });
 }
 
 function testIncomingSessionRequest() {
   return new Promise(function(aResolve, aReject) {
--- a/dom/push/PushComponents.js
+++ b/dom/push/PushComponents.js
@@ -112,17 +112,17 @@ PushServiceBase.prototype = {
  */
 function PushServiceParent() {
   PushServiceBase.call(this);
 }
 
 PushServiceParent.prototype = Object.create(PushServiceBase.prototype);
 
 XPCOMUtils.defineLazyServiceGetter(PushServiceParent.prototype, "_mm",
-  "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageBroadcaster");
+  "@mozilla.org/parentprocessmessagemanager;1", "nsISupports");
 
 Object.assign(PushServiceParent.prototype, {
   _xpcom_factory: XPCOMUtils.generateSingletonFactory(PushServiceParent),
 
   _messages: [
     "Push:Register",
     "Push:Registration",
     "Push:Unregister",
@@ -207,24 +207,23 @@ Object.assign(PushServiceParent.prototyp
     if (name === "Push:NotificationForOriginClosed") {
       this.notificationForOriginClosed(data);
       return;
     }
     if (name === "Push:ReportError") {
       this.reportDeliveryError(data.messageId, data.reason);
       return;
     }
-    let sender = target.QueryInterface(Ci.nsIMessageSender);
     return this._handleRequest(name, principal, data).then(result => {
-      sender.sendAsyncMessage(this._getResponseName(name, "OK"), {
+      target.sendAsyncMessage(this._getResponseName(name, "OK"), {
         requestID: data.requestID,
         result: result
       });
     }, error => {
-      sender.sendAsyncMessage(this._getResponseName(name, "KO"), {
+      target.sendAsyncMessage(this._getResponseName(name, "KO"), {
         requestID: data.requestID,
         result: error.result,
       });
     }).catch(Cu.reportError);
   },
 
   _handleReady() {
     this.service.init();
@@ -315,17 +314,17 @@ function PushServiceContent() {
   this._requests = new Map();
   this._requestId = 0;
 }
 
 PushServiceContent.prototype = Object.create(PushServiceBase.prototype);
 
 XPCOMUtils.defineLazyServiceGetter(PushServiceContent.prototype,
   "_mm", "@mozilla.org/childprocessmessagemanager;1",
-  "nsISyncMessageSender");
+  "nsISupports");
 
 Object.assign(PushServiceContent.prototype, {
   _xpcom_factory: XPCOMUtils.generateSingletonFactory(PushServiceContent),
 
   _messages: [
     "PushService:Register:OK",
     "PushService:Register:KO",
     "PushService:Registration:OK",
--- a/dom/quota/test/head.js
+++ b/dom/quota/test/head.js
@@ -75,21 +75,24 @@ function dismissNotification(popup, win)
   executeSoon(function () {
     EventUtils.synthesizeKey("VK_ESCAPE", {}, win);
   });
 }
 
 function waitForMessage(aMessage, browser)
 {
   return new Promise((resolve, reject) => {
+    // When contentScript runs, "this" is a ContentFrameMessageManager (so that's where
+    // addEventListener will add the listener), but the non-bubbling "message" event is
+    // sent to the Window involved, so we need a capturing listener.
     function contentScript() {
       addEventListener("message", function(event) {
         sendAsyncMessage("testLocal:persisted",
           {persisted: event.data});
-      }, {once: true}, true);
+      }, {once: true, capture: true}, true);
     }
 
     let script = "data:,(" + contentScript.toString() + ")();";
 
     let mm = browser.selectedBrowser.messageManager;
 
     mm.addMessageListener("testLocal:persisted", function listener(msg) {
       mm.removeMessageListener("testLocal:persisted", listener);
--- a/dom/webidl/FrameLoader.webidl
+++ b/dom/webidl/FrameLoader.webidl
@@ -3,17 +3,16 @@
  * 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 LoadContext;
 interface TabParent;
 interface URI;
 interface nsIDocShell;
-interface nsIMessageSender;
 interface nsIPrintSettings;
 interface nsIWebBrowserPersistDocumentReceiver;
 interface nsIWebProgressListener;
 
 [ChromeOnly]
 interface FrameLoader {
   /**
    * Get the docshell from the frame loader.
@@ -99,17 +98,17 @@ interface FrameLoader {
 
   /**
    * Activate event forwarding from client (remote frame) to parent.
    */
   [Throws]
   void activateFrameEvent(DOMString aType, boolean capture);
 
   // Note, when frameloaders are swapped, also messageManagers are swapped.
-  readonly attribute nsIMessageSender? messageManager;
+  readonly attribute MessageSender? messageManager;
 
   /**
    * Request that the next time a remote layer transaction has been
    * received by the Compositor, a MozAfterRemoteFrame event be sent
    * to the window.
    */
   [Throws]
   void requestNotifyAfterRemotePaint();
--- a/dom/webidl/LegacyQueryInterface.webidl
+++ b/dom/webidl/LegacyQueryInterface.webidl
@@ -17,17 +17,22 @@ interface LegacyQueryInterface {
   [Exposed=Window]
   nsISupports queryInterface(IID iid);
 };
 
 Attr implements LegacyQueryInterface;
 BarProp implements LegacyQueryInterface;
 BoxObject implements LegacyQueryInterface;
 CaretPosition implements LegacyQueryInterface;
+ChildProcessMessageManager implements LegacyQueryInterface;
+ChromeMessageBroadcaster implements LegacyQueryInterface;
+ChromeMessageSender implements LegacyQueryInterface;
 Comment implements LegacyQueryInterface;
+ContentFrameMessageManager implements LegacyQueryInterface;
+ContentProcessMessageManager implements LegacyQueryInterface;
 Crypto implements LegacyQueryInterface;
 CSSMozDocumentRule implements LegacyQueryInterface;
 CSSPrimitiveValue implements LegacyQueryInterface;
 CSSStyleDeclaration implements LegacyQueryInterface;
 CSSStyleRule implements LegacyQueryInterface;
 CSSValueList implements LegacyQueryInterface;
 DOMImplementation implements LegacyQueryInterface;
 DOMParser implements LegacyQueryInterface;
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MessageManager.webidl
@@ -0,0 +1,378 @@
+/* -*- 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/. */
+
+interface MozFrameLoader;
+interface nsIEventTarget;
+interface Principal;
+
+dictionary ReceiveMessageArgument
+{
+  /**
+   * The target of the message. Either an element owning the message manager, or message
+   * manager itself if no element owns it.
+   */
+  required nsISupports target;
+
+  /**
+   * Message name.
+   */
+  required DOMString name;
+
+  required boolean sync;
+
+  /**
+   * Structured clone of the sent message data
+   */
+  any data = null;
+
+  /**
+   * Same as .data, deprecated.
+   */
+  any json = null;
+
+  /**
+   * Named table of jsvals/objects, or null.
+   */
+  required object objects;
+
+  sequence<MessagePort> ports;
+
+  /**
+   * Principal for the window app.
+   */
+  required Principal? principal;
+
+  MozFrameLoader targetFrameLoader;
+};
+
+callback interface MessageListener
+{
+  /**
+   * Each listener is invoked with its own copy of the message
+   * parameter.
+   *
+   * When the listener is called, 'this' value is the target of the message.
+   *
+   * If the message is synchronous, the possible return value is
+   * returned as JSON (will be changed to use structured clones).
+   * When there are multiple listeners to sync messages, each
+   * listener's return value is sent back as an array.  |undefined|
+   * return values show up as undefined values in the array.
+   */
+  any receiveMessage(optional ReceiveMessageArgument argument);
+};
+
+[ChromeOnly]
+interface MessageListenerManager
+{
+  /**
+   * Register |listener| to receive |messageName|.  All listener
+   * callbacks for a particular message are invoked when that message
+   * is received.
+   *
+   * The message manager holds a strong ref to |listener|.
+   *
+   * If the same listener registers twice for the same message, the
+   * second registration is ignored.
+   *
+   * Pass true for listenWhenClosed if you want to receive messages
+   * during the short period after a frame has been removed from the
+   * DOM and before its frame script has finished unloading. This
+   * parameter only has an effect for frame message managers in
+   * the main process. Default is false.
+   */
+  [Throws]
+  void addMessageListener(DOMString messageName,
+                          MessageListener listener,
+                          optional boolean listenWhenClosed = false);
+
+  /**
+   * Undo an |addMessageListener| call -- that is, calling this causes us to no
+   * longer invoke |listener| when |messageName| is received.
+   *
+   * removeMessageListener does not remove a message listener added via
+   * addWeakMessageListener; use removeWeakMessageListener for that.
+   */
+  [Throws]
+  void removeMessageListener(DOMString messageName,
+                             MessageListener listener);
+
+  /**
+   * This is just like addMessageListener, except the message manager holds a
+   * weak ref to |listener|.
+   *
+   * If you have two weak message listeners for the same message, they may be
+   * called in any order.
+   */
+  [Throws]
+  void addWeakMessageListener(DOMString messageName,
+                              MessageListener listener);
+
+  /**
+   * This undoes an |addWeakMessageListener| call.
+   */
+  [Throws]
+  void removeWeakMessageListener(DOMString messageName,
+                                 MessageListener listener);
+};
+
+[ChromeOnly]
+interface MessageSender : MessageListenerManager
+{
+  /**
+   * Send |messageName| and |obj| to the "other side" of this message
+   * manager.  This invokes listeners who registered for
+   * |messageName|.
+   *
+   * See ReceiveMessageArgument for the format of the data delivered to listeners.