Merge mozilla-central to autoland. a=merge CLOSED TREE
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Wed, 04 Apr 2018 12:50:16 +0300
changeset 411676 545bb9b8a294def427902f5faa66b91ca56686b9
parent 411675 64486670492f5c9cc2e890c5931284bb6a85d194 (current diff)
parent 411646 ff0efa4132f0efd78af0910762aec7dcc1a8de66 (diff)
child 411677 0a5788b2cbb81f8ecfc4148a5114edfd7c197652
push id101729
push usercsabou@mozilla.com
push dateWed, 04 Apr 2018 18:07:35 +0000
treeherdermozilla-inbound@3c240f56a113 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
devtools/client/framework/test/browser_toolbox_minimize.js
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-001.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-002.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-003.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-004.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-radial-gradient-001.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-000.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-001.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-002.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-003.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-004.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-005.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-012.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-013.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-014.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-015.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-016.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-017.html.ini
testing/web-platform/meta/css/css-shapes/shape-outside/shape-image/shape-image-025.html.ini
testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-010.html.ini
testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-011.html.ini
testing/web-platform/meta/css/css-shapes/spec-examples/shape-outside-012.html.ini
toolkit/content/widgets/browser.xml
--- a/browser/base/content/browser-media.js
+++ b/browser/base/content/browser-media.js
@@ -167,17 +167,16 @@ var gEMEHandler = {
     };
     let options = {
       dismissed: true,
       eventCallback: aTopic => aTopic == "swapping",
       learnMoreURL: Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content",
     };
     PopupNotifications.show(browser, "drmContentPlaying", message, anchorId, mainAction, null, options);
   },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener])
 };
 
 XPCOMUtils.defineLazyGetter(gEMEHandler, "_brandShortName", function() {
   return document.getElementById("bundle_brand").getString("brandShortName");
 });
 
 const TELEMETRY_DDSTAT_SHOWN = 0;
 const TELEMETRY_DDSTAT_SHOWN_FIRST = 1;
--- a/browser/base/content/test/static/browser_parsable_css.js
+++ b/browser/base/content/test/static/browser_parsable_css.js
@@ -74,27 +74,16 @@ let whitelist = [
 if (!Services.prefs.getBoolPref("full-screen-api.unprefix.enabled")) {
   whitelist.push({
     sourceName: /(?:res|gre-resources)\/(ua|html)\.css$/i,
     errorMessage: /Unknown pseudo-class .*\bfullscreen\b/i,
     isFromDevTools: false
   });
 }
 
-// Platform can be "linux", "macosx" or "win". If omitted, the exception applies to all platforms.
-let allowedImageReferences = [
-  // Bug 1302691
-  {file: "chrome://devtools/skin/images/dock-bottom-minimize@2x.png",
-   from: "chrome://devtools/skin/toolbox.css",
-   isFromDevTools: true},
-  {file: "chrome://devtools/skin/images/dock-bottom-maximize@2x.png",
-   from: "chrome://devtools/skin/toolbox.css",
-   isFromDevTools: true},
-];
-
 let propNameWhitelist = [
   // These are CSS custom properties that we found a definition of but
   // no reference to.
   // Bug 1441837
   {propName: "--in-content-category-text-active",
    isFromDevTools: false},
   // Bug 1441855
   {propName: "--chrome-nav-buttons-background",
@@ -414,28 +403,17 @@ add_task(async function checkAllTheCSS()
 
   // Wait for all the files to have actually loaded:
   await throttledMapPromises(allPromises, loadCSS);
 
   // Check if all the files referenced from CSS actually exist.
   for (let [image, references] of imageURIsToReferencesMap) {
     if (!chromeFileExists(image)) {
       for (let ref of references) {
-        let ignored = false;
-        for (let item of allowedImageReferences) {
-          if (image.endsWith(item.file) && ref.endsWith(item.from) &&
-              isDevtools == item.isFromDevTools &&
-              (!item.platforms || item.platforms.includes(AppConstants.platform))) {
-            item.used = true;
-            ignored = true;
-            break;
-          }
-        }
-        if (!ignored)
-          ok(false, "missing " + image + " referenced from " + ref);
+        ok(false, "missing " + image + " referenced from " + ref);
       }
     }
   }
 
   // Check if all the properties that are defined are referenced.
   for (let [prop, refCount] of customPropsToReferencesMap) {
     if (!refCount) {
       let ignored = false;
@@ -467,17 +445,16 @@ add_task(async function checkAllTheCSS()
       if (!item.used && isDevtools == item.isFromDevTools &&
           (!item.platforms || item.platforms.includes(AppConstants.platform)) &&
           !item.intermittent) {
         ok(false, "Unused whitelist item: " + dumpWhitelistItem(item));
       }
     }
   }
   checkWhitelist(whitelist);
-  checkWhitelist(allowedImageReferences);
   checkWhitelist(propNameWhitelist);
 
   // Clean up to avoid leaks:
   iframe.remove();
   doc.head.innerHTML = "";
   doc = null;
   iframe = null;
   win = null;
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -799,16 +799,17 @@ if (AppConstants.platform == "win") {
 }
 CustomizableWidgets.push(preferencesButton);
 
 if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) {
   CustomizableWidgets.push({
     id: "panic-button",
     type: "view",
     viewId: "PanelUI-panicView",
+    disabled: !Services.policies.isAllowed("panicButton"),
 
     forgetButtonCalled(aEvent) {
       let doc = aEvent.target.ownerDocument;
       let group = doc.getElementById("PanelUI-panic-timeSpan");
       BrowserUITelemetry.countPanicEvent(group.selectedItem.id);
       let itemsToClear = [
         "cookies", "history", "openWindows", "formdata", "sessions", "cache", "downloads"
       ];
--- a/browser/components/enterprisepolicies/EnterprisePoliciesContent.js
+++ b/browser/components/enterprisepolicies/EnterprisePoliciesContent.js
@@ -46,18 +46,17 @@ function EnterprisePoliciesManagerConten
 
   Services.cpmm.addMessageListener("EnterprisePolicies:DisallowFeature", this);
   Services.cpmm.addMessageListener("EnterprisePolicies:Restart", this);
 }
 
 EnterprisePoliciesManagerContent.prototype = {
   // for XPCOM
   classID:          Components.ID("{dc6358f8-d167-4566-bf5b-4350b5e6a7a2}"),
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
-                                         Ci.nsIEnterprisePolicies]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIEnterprisePolicies]),
 
   // redefine the default factory for XPCOMUtils
   _xpcom_factory: EnterprisePoliciesFactory,
 
   _status: Ci.nsIEnterprisePolicies.INACTIVE,
 
   _disallowedFeatures: [],
 
--- a/browser/components/enterprisepolicies/Policies.jsm
+++ b/browser/components/enterprisepolicies/Policies.jsm
@@ -252,16 +252,24 @@ var Policies = {
   "DisableFirefoxStudies": {
     onBeforeAddons(manager, param) {
       if (param) {
         manager.disallowFeature("Shield");
       }
     }
   },
 
+  "DisableForgetButton": {
+    onProfileAfterChange(manager, param) {
+      if (param) {
+        manager.disallowFeature("panicButton");
+      }
+    }
+  },
+
   "DisableFormHistory": {
     onBeforeUIStartup(manager, param) {
       if (param) {
         setAndLockPref("browser.formfill.enable", false);
       }
     }
   },
 
@@ -286,16 +294,24 @@ var Policies = {
   "DisableSafeMode": {
     onBeforeUIStartup(manager, param) {
       if (param) {
         manager.disallowFeature("safeMode");
       }
     }
   },
 
+  "DisableSecurityBypass": {
+    onBeforeUIStartup(manager, param) {
+      if ("SafeBrowsing" in param) {
+        setAndLockPref("browser.safebrowsing.allowOverride", !param.SafeBrowsing);
+      }
+    }
+  },
+
   "DisableSysAddonUpdate": {
     onBeforeAddons(manager, param) {
       if (param) {
         manager.disallowFeature("SysAddonUpdate");
       }
     }
   },
 
@@ -484,17 +500,22 @@ var Policies = {
           Services.prefs.clearUserPref("browser.startup.page");
         });
       }
     }
   },
 
   "InstallAddons": {
     onBeforeUIStartup(manager, param) {
-      addAllowDenyPermissions("install", param.Allow, null);
+      if ("Allow" in param) {
+        addAllowDenyPermissions("install", param.Allow, null);
+      }
+      if ("Default" in param) {
+        setAndLockPref("xpinstall.enabled", param.Default);
+      }
     }
   },
 
   "NoDefaultBookmarks": {
     onProfileAfterChange(manager, param) {
       if (param) {
         manager.disallowFeature("defaultBookmarks");
       }
@@ -503,16 +524,27 @@ var Policies = {
 
   "OverrideFirstRunPage": {
     onProfileAfterChange(manager, param) {
       let url = param ? param.spec : "";
       setAndLockPref("startup.homepage_welcome_url", url);
     }
   },
 
+  "OverridePostUpdatePage": {
+    onProfileAfterChange(manager, param) {
+      let url = param ? param.spec : "";
+      setAndLockPref("startup.homepage_override_url", url);
+      // The pref startup.homepage_override_url is only used
+      // as a fallback when the update.xml file hasn't provided
+      // a specific post-update URL.
+      manager.disallowFeature("postUpdateCustomPage");
+    }
+  },
+
   "PopupBlocking": {
     onBeforeUIStartup(manager, param) {
       addAllowDenyPermissions("popup", param.Allow, null);
 
       if (param.Locked) {
         let blockValue = true;
         if (param.Default !== undefined && !param.Default) {
           blockValue = false;
--- a/browser/components/enterprisepolicies/schemas/policies-schema.json
+++ b/browser/components/enterprisepolicies/schemas/policies-schema.json
@@ -201,16 +201,23 @@
 
     "DisableFirefoxStudies": {
       "description": "Prevents Firefox from running studies.",
       "first_available": "60.0",
 
       "type": "boolean"
     },
 
+    "DisableForgetButton": {
+      "description": "Prevents access to the \"Forget\" button.",
+      "first_available": "60.0",
+
+      "type": "boolean"
+    },
+
     "DisableFormHistory": {
       "description": "Don't remember search and form history.",
       "first_available": "60.0",
 
       "type": "boolean"
     },
 
     "DisablePocket": {
@@ -229,16 +236,28 @@
 
     "DisableSafeMode": {
       "description": "Prevents ability to restart in safe mode.",
       "first_available": "60.0",
 
       "type": "boolean"
     },
 
+    "DisableSecurityBypass": {
+      "description": "Prevents the user from bypassing certain security warnings.",
+      "first_available": "60.0",
+
+      "type": "object",
+      "properties": {
+        "SafeBrowsing": {
+          "type": "boolean"
+        }
+      }
+    },
+
     "DisableSysAddonUpdate": {
       "description": "Prevent the browser from installing and updating system addons.",
       "first_available": "60.0",
       "enterprise_only": true,
 
       "type": "boolean"
     },
 
@@ -374,16 +393,19 @@
 
       "type": "object",
       "properties": {
         "Allow": {
           "type": "array",
           "items": {
             "type": "origin"
           }
+        },
+        "Default": {
+          "type": "boolean"
         }
       }
     },
 
     "NoDefaultBookmarks": {
       "description": "Don't create the default bookmarks bundled with Firefox, nor the Smart Bookmarks (Most Visited, Recent Tags). Note: this policy is only effective if used before the first run of the profile.",
       "first_available": "60.0",
 
@@ -393,16 +415,24 @@
     "OverrideFirstRunPage": {
       "description": "Override the first run page. Set this policy to blank if you want to disable the first run page.",
       "first_available": "60.0",
       "enterprise_only": true,
 
       "type": "URLorEmpty"
     },
 
+    "OverridePostUpdatePage": {
+      "description": "Override the post-update \"What's New\" page. Set this policy to blank if you want to disable the post-update page.",
+      "first_available": "60.0",
+      "enterprise_only": true,
+
+      "type": "URLorEmpty"
+    },
+
     "PopupBlocking": {
       "description": "Allow or deny popup usage.",
       "first_available": "60.0",
 
       "type": "object",
       "properties": {
         "Allow": {
           "type": "array",
--- a/browser/components/enterprisepolicies/tests/browser/browser.ini
+++ b/browser/components/enterprisepolicies/tests/browser/browser.ini
@@ -38,13 +38,14 @@ support-files =
 [browser_policy_disable_popup_blocker.js]
 [browser_policy_disable_privatebrowsing.js]
 [browser_policy_disable_safemode.js]
 [browser_policy_disable_shield.js]
 [browser_policy_disable_telemetry.js]
 [browser_policy_display_bookmarks.js]
 [browser_policy_display_menu.js]
 [browser_policy_extensions.js]
+[browser_policy_override_postupdatepage.js]
 [browser_policy_proxy.js]
 [browser_policy_search_engine.js]
 [browser_policy_searchbar.js]
 [browser_policy_set_homepage.js]
 [browser_policy_websitefilter.js]
--- a/browser/components/enterprisepolicies/tests/browser/browser_policies_simple_pref_policies.js
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policies_simple_pref_policies.js
@@ -34,16 +34,27 @@ const POLICIES_TESTS = [
     policies: { "RememberPasswords": false },
     lockedPrefs: { "signon.rememberSignons": false },
   },
   {
     policies: { "RememberPasswords": true },
     lockedPrefs: { "signon.rememberSignons": true },
   },
 
+  // POLICY: DisableSecurityBypass
+  {
+    policies: {
+      "DisableSecurityBypass": {
+        "SafeBrowsing": true
+      }
+    },
+    lockedPrefs: { "browser.safebrowsing.allowOverride": false },
+  },
+
+
   // POLICY: DisableFormHistory
   {
     policies: { "DisableFormHistory": true },
     lockedPrefs: { "browser.formfill.enable": false },
   },
 
   // POLICY: EnableTrackingProtection
   {
@@ -86,26 +97,40 @@ const POLICIES_TESTS = [
       }
     },
     lockedPrefs: {
       "network.negotiate-auth.trusted-uris": "a.com, b.com",
       "network.negotiate-auth.delegation-uris": "a.com, b.com",
       "network.automatic-ntlm-auth.trusted-uris": "a.com, b.com",
     }
   },
+
+  // POLICY: Certificates
   {
     policies: {
       "Certificates": {
         "ImportEnterpriseRoots": true,
       }
     },
     lockedPrefs: {
       "security.enterprise_roots.enabled": true,
     }
   },
+
+  // POLICY: InstallAddons.Default (block addon installs)
+  {
+    policies: {
+      "InstallAddons": {
+        "Default": false,
+      }
+    },
+    lockedPrefs: {
+      "xpinstall.enabled": false,
+    }
+  },
 ];
 
 add_task(async function test_policy_remember_passwords() {
   for (let test of POLICIES_TESTS) {
     await setupPolicyEngineWithJson({
       "policies": test.policies
     });
 
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_override_postupdatepage.js
@@ -0,0 +1,99 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This test was based on the test browser_bug538331.js
+
+const UPDATE_PROVIDED_PAGE = "https://default.example.com/";
+const POLICY_PROVIDED_PAGE = "https://policy.example.com/";
+
+const PREF_MSTONE = "browser.startup.homepage_override.mstone";
+const PREF_POSTUPDATE = "app.update.postupdate";
+
+/*
+ * The important parts for this test are:
+ *  - actions="showURL"
+ *  - openURL="${UPDATE_PROVIDED_PAGE}"
+ */
+const XML_UPDATE = `<?xml version="1.0"?>
+<updates xmlns="http://www.mozilla.org/2005/app-update">
+  <update appVersion="1.0" buildID="20080811053724" channel="nightly"
+          displayVersion="Version 1.0" installDate="1238441400314"
+          isCompleteUpdate="true" name="Update Test 1.0" type="minor"
+          detailsURL="http://example.com/" previousAppVersion="1.0"
+          serviceURL="https://example.com/" statusText="The Update was successfully installed"
+          foregroundDownload="true"
+          actions="showURL"
+          openURL="${UPDATE_PROVIDED_PAGE}">
+    <patch type="complete" URL="http://example.com/" size="775" selected="true" state="succeeded"/>
+  </update>
+</updates>`;
+
+const XML_EMPTY = `<?xml version="1.0"?>
+<updates xmlns="http://www.mozilla.org/2005/app-update">
+</updates>`;
+
+let gOriginalMStone = null;
+
+add_task(async function test_override_postupdate_page() {
+  // Remember this to clean-up afterwards
+  if (Services.prefs.prefHasUserValue(PREF_MSTONE)) {
+    gOriginalMStone = Services.prefs.getCharPref(PREF_MSTONE);
+  }
+  Services.prefs.setBoolPref(PREF_POSTUPDATE, true);
+
+  writeUpdatesToXMLFile(XML_UPDATE);
+  reloadUpdateManagerData();
+
+  is(getPostUpdatePage(), UPDATE_PROVIDED_PAGE, "Post-update page was provided by update.xml.");
+
+  // Now perform the same action but set the policy to override this page
+  await setupPolicyEngineWithJson({
+    "policies": {
+      "OverridePostUpdatePage": POLICY_PROVIDED_PAGE
+    }
+  });
+
+  is(getPostUpdatePage(), POLICY_PROVIDED_PAGE, "Post-update page was provided by policy.");
+
+  // Clean-up
+  writeUpdatesToXMLFile(XML_EMPTY);
+  if (gOriginalMStone) {
+    Services.prefs.setCharPref(PREF_MSTONE, gOriginalMStone);
+  }
+  Services.prefs.clearUserPref(PREF_POSTUPDATE);
+  reloadUpdateManagerData();
+});
+
+
+function getPostUpdatePage() {
+  Services.prefs.setCharPref(PREF_MSTONE, "PreviousMilestone");
+  return Cc["@mozilla.org/browser/clh;1"]
+           .getService(Ci.nsIBrowserHandler)
+           .defaultArgs;
+}
+
+function reloadUpdateManagerData() {
+  // Reloads the update metadata from disk
+  Cc["@mozilla.org/updates/update-manager;1"].getService(Ci.nsIUpdateManager).
+  QueryInterface(Ci.nsIObserver).observe(null, "um-reload-update-data", "");
+}
+
+
+function writeUpdatesToXMLFile(aText) {
+  const PERMS_FILE = 0o644;
+
+  const MODE_WRONLY   = 0x02;
+  const MODE_CREATE   = 0x08;
+  const MODE_TRUNCATE = 0x20;
+
+  let file = Services.dirsvc.get("UpdRootD", Ci.nsIFile);
+  file.append("updates.xml");
+  let fos = Cc["@mozilla.org/network/file-output-stream;1"].
+            createInstance(Ci.nsIFileOutputStream);
+  if (!file.exists()) {
+    file.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
+  }
+  fos.init(file, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, PERMS_FILE, 0);
+  fos.write(aText, aText.length);
+  fos.close();
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/disable_forget_button/browser.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+prefs =
+  browser.policies.alternatePath='<test-root>/browser/components/enterprisepolicies/tests/browser/disable_forget_button/forget_button.json'
+support-files =
+  forget_button.json
+
+[browser_policy_disable_forgetbutton.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/disable_forget_button/browser_policy_disable_forgetbutton.js
@@ -0,0 +1,9 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_policy_disable_forget_button() {
+  let widget = CustomizableUI.getWidget("panic-button");
+  is(widget.disabled, true, "Forget Button is disabled");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/disable_forget_button/forget_button.json
@@ -0,0 +1,5 @@
+{
+  "policies": {
+    "DisableForgetButton": true
+  }
+}
--- a/browser/components/enterprisepolicies/tests/moz.build
+++ b/browser/components/enterprisepolicies/tests/moz.build
@@ -7,14 +7,15 @@
 with Files("**"):
     BUG_COMPONENT = ("Firefox", "General")
 
 BROWSER_CHROME_MANIFESTS += [
     'browser/browser.ini',
     'browser/disable_app_update/browser.ini',
     'browser/disable_default_bookmarks/browser.ini',
     'browser/disable_developer_tools/browser.ini',
+    'browser/disable_forget_button/browser.ini',
     'browser/disable_fxscreenshots/browser.ini',
 ]
 
 TESTING_JS_MODULES += [
     'EnterprisePolicyTesting.jsm',
 ]
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -140,16 +140,23 @@ function getPostUpdateOverridePage(defau
   if (!actions)
     return defaultOverridePage;
 
   // The existence of silent or the non-existence of showURL in the actions both
   // mean that an override page should not be displayed.
   if (actions.includes("silent") || !actions.includes("showURL"))
     return "";
 
+  // If a policy was set to not allow the update.xml-provided
+  // URL to be used, use the default fallback (which will also
+  // be provided by the policy).
+  if (!Services.policies.isAllowed("postUpdateCustomPage")) {
+    return defaultOverridePage;
+  }
+
   return update.getProperty("openURL") || defaultOverridePage;
 }
 
 /**
  * Open a browser window. If this is the initial launch, this function will
  * attempt to use the navigator:blank window opened by nsBrowserGlue.js during
  * early startup.
  *
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/accessibility-panel.js
@@ -0,0 +1,225 @@
+/* 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 { AccessibilityFront } = require("devtools/shared/fronts/accessibility");
+const EventEmitter = require("devtools/shared/event-emitter");
+
+const { Picker } = require("./picker");
+
+// The panel's window global is an EventEmitter firing the following events:
+const EVENTS = {
+  // When the accessibility inspector has a new accessible front selected.
+  NEW_ACCESSIBLE_FRONT_SELECTED: "Accessibility:NewAccessibleFrontSelected",
+  // When the accessibility inspector has a new accessible front highlighted.
+  NEW_ACCESSIBLE_FRONT_HIGHLIGHTED: "Accessibility:NewAccessibleFrontHighlighted",
+  // When the accessibility inspector has a new accessible front inspected.
+  NEW_ACCESSIBLE_FRONT_INSPECTED: "Accessibility:NewAccessibleFrontInspected",
+  // When the accessibility inspector is updated.
+  ACCESSIBILITY_INSPECTOR_UPDATED: "Accessibility:AccessibilityInspectorUpdated"
+};
+
+/**
+ * This object represents Accessibility panel. It's responsibility is to
+ * render Accessibility Tree of the current debugger target and the sidebar that
+ * displays current relevant accessible details.
+ */
+function AccessibilityPanel(iframeWindow, toolbox) {
+  this.panelWin = iframeWindow;
+  this._toolbox = toolbox;
+
+  this.onTabNavigated = this.onTabNavigated.bind(this);
+  this.onPanelVisibilityChange = this.onPanelVisibilityChange.bind(this);
+  this.onNewAccessibleFrontSelected =
+    this.onNewAccessibleFrontSelected.bind(this);
+  this.onAccessibilityInspectorUpdated =
+    this.onAccessibilityInspectorUpdated.bind(this);
+  this.updatePickerButton = this.updatePickerButton.bind(this);
+
+  EventEmitter.decorate(this);
+}
+
+AccessibilityPanel.prototype = {
+  /**
+   * Open is effectively an asynchronous constructor.
+   */
+  async open() {
+    if (this._opening) {
+      await this._opening;
+      return this._opening;
+    }
+
+    let resolver;
+    this._opening = new Promise(resolve => {
+      resolver = resolve;
+    });
+
+    // Local monitoring needs to make the target remote.
+    if (!this.target.isRemote) {
+      await this.target.makeRemote();
+    }
+
+    this.target.on("navigate", this.onTabNavigated);
+    this._toolbox.on("select", this.onPanelVisibilityChange);
+
+    this.panelWin.EVENTS = EVENTS;
+    EventEmitter.decorate(this.panelWin);
+    this.panelWin.on(EVENTS.NEW_ACCESSIBLE_FRONT_SELECTED,
+      this.onNewAccessibleFrontSelected);
+    this.panelWin.on(EVENTS.ACCESSIBILITY_INSPECTOR_UPDATED,
+      this.onAccessibilityInspectorUpdated);
+
+    this.shouldRefresh = true;
+    this.panelWin.gToolbox = this._toolbox;
+
+    await this._toolbox.initInspector();
+    this._front = new AccessibilityFront(this.target.client,
+                                         this.target.form);
+    this._walker = await this._front.getWalker();
+
+    this._isOldVersion = !(await this.target.actorHasMethod("accessibility", "enable"));
+    if (!this._isOldVersion) {
+      await this._front.bootstrap();
+      this.picker = new Picker(this);
+    }
+
+    this.isReady = true;
+    this.emit("ready");
+    resolver(this);
+    return this._opening;
+  },
+
+  onNewAccessibleFrontSelected(selected) {
+    this.emit("new-accessible-front-selected", selected);
+  },
+
+  onAccessibilityInspectorUpdated() {
+    this.emit("accessibility-inspector-updated");
+  },
+
+  /**
+   * Make sure the panel is refreshed when the page is reloaded. The panel is
+   * refreshed immediatelly if it's currently selected or lazily when the user
+   * actually selects it.
+   */
+  onTabNavigated() {
+    this.shouldRefresh = true;
+    this._opening.then(() => this.refresh());
+  },
+
+  /**
+   * Make sure the panel is refreshed (if needed) when it's selected.
+   */
+  onPanelVisibilityChange() {
+    this._opening.then(() => this.refresh());
+  },
+
+  refresh() {
+    this.cancelPicker();
+
+    if (this.isVisible) {
+      this._front.on("init", this.updatePickerButton);
+      this._front.on("shutdown", this.updatePickerButton);
+    } else {
+      this._front.off("init", this.updatePickerButton);
+      this._front.off("shutdown", this.updatePickerButton);
+      // Do not refresh if the panel isn't visible.
+      return;
+    }
+
+    // Do not refresh if it isn't necessary.
+    if (!this.shouldRefresh) {
+      return;
+    }
+    // Alright reset the flag we are about to refresh the panel.
+    this.shouldRefresh = false;
+    this.postContentMessage("initialize", this._front, this._walker, this._isOldVersion);
+  },
+
+  selectAccessible(accessibleFront) {
+    this.postContentMessage("selectAccessible", this._walker, accessibleFront);
+  },
+
+  highlightAccessible(accessibleFront) {
+    this.postContentMessage("highlightAccessible", this._walker, accessibleFront);
+  },
+
+  postContentMessage(type, ...args) {
+    const event = new this.panelWin.MessageEvent("devtools/chrome/message", {
+      bubbles: true,
+      cancelable: true,
+      data: { type, args }
+    });
+
+    this.panelWin.dispatchEvent(event);
+  },
+
+  updatePickerButton() {
+    this.picker && this.picker.updateButton();
+  },
+
+  togglePicker(focus) {
+    this.picker && this.picker.toggle();
+  },
+
+  cancelPicker() {
+    this.picker && this.picker.cancel();
+  },
+
+  stopPicker() {
+    this.picker && this.picker.stop();
+  },
+
+  get walker() {
+    return this._walker;
+  },
+
+  /**
+   * Return true if the Accessibility panel is currently selected.
+   */
+  get isVisible() {
+    return this._toolbox.currentToolId === "accessibility";
+  },
+
+  get target() {
+    return this._toolbox.target;
+  },
+
+  async destroy() {
+    if (this._destroying) {
+      await this._destroying;
+      return;
+    }
+
+    let resolver;
+    this._destroying = new Promise(resolve => {
+      resolver = resolve;
+    });
+
+    this.target.off("navigate", this.onTabNavigated);
+    this._toolbox.off("select", this.onPanelVisibilityChange);
+
+    this.panelWin.off(EVENTS.NEW_ACCESSIBLE_FRONT_SELECTED,
+      this.onNewAccessibleFrontSelected);
+    this.panelWin.off(EVENTS.ACCESSIBILITY_INSPECTOR_UPDATED,
+      this.onAccessibilityInspectorUpdated);
+
+    this.picker.release();
+    this.picker = null;
+
+    if (this._front) {
+      await this._front.destroy();
+    }
+
+    this._front = null;
+    this.panelWin.gToolbox = null;
+
+    this.emit("destroyed");
+
+    resolver();
+  }
+};
+
+// Exports from this module
+exports.AccessibilityPanel = AccessibilityPanel;
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/accessibility-view.js
@@ -0,0 +1,97 @@
+/* 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";
+
+/* global EVENTS */
+
+// React & Redux
+const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
+const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+const { Provider } = require("devtools/client/shared/vendor/react-redux");
+const { combineReducers } = require("devtools/client/shared/vendor/redux");
+
+// Accessibility Panel
+const MainFrame = createFactory(require("./components/MainFrame"));
+const OldVersionDescription =
+  createFactory(require("./components/Description").OldVersionDescription);
+
+// Store
+const createStore = require("devtools/client/shared/redux/create-store")();
+
+// Reducers
+const { reducers } = require("./reducers/index");
+const store = createStore(combineReducers(reducers));
+
+// Actions
+const { reset } = require("./actions/ui");
+const { select, highlight } = require("./actions/accessibles");
+
+/**
+ * This object represents view of the Accessibility panel and is responsible
+ * for rendering the content. It renders the top level ReactJS
+ * component: the MainFrame.
+ */
+function AccessibilityView(localStore) {
+  addEventListener("devtools/chrome/message", this.onMessage.bind(this), true);
+  this.store = localStore;
+}
+
+AccessibilityView.prototype = {
+  /**
+   * Initialize accessibility view, create its top level component and set the
+   * data store.
+   *
+   * @param {Object} accessibility  front that can initialize accessibility
+   *                                walker and enable/disable accessibility
+   *                                services.
+   */
+  async initialize(accessibility, walker, isOldVersion) {
+    // Make sure state is reset every time accessibility panel is initialized.
+    await this.store.dispatch(reset(accessibility));
+    const container = document.getElementById("content");
+
+    if (isOldVersion) {
+      ReactDOM.render(OldVersionDescription(), container);
+      return;
+    }
+
+    const mainFrame = MainFrame({ accessibility, walker });
+    // Render top level component
+    const provider = createElement(Provider, { store: this.store }, mainFrame);
+    this.mainFrame = ReactDOM.render(provider, container);
+  },
+
+  async selectAccessible(walker, accessible) {
+    await this.store.dispatch(select(walker, accessible));
+    window.emit(EVENTS.NEW_ACCESSIBLE_FRONT_INSPECTED);
+  },
+
+  async highlightAccessible(walker, accessible) {
+    await this.store.dispatch(highlight(walker, accessible));
+    window.emit(EVENTS.NEW_ACCESSIBLE_FRONT_HIGHLIGHTED);
+  },
+
+  async selectNodeAccessible(walker, node) {
+    const accessible = await walker.getAccessibleFor(node);
+
+    await this.store.dispatch(select(walker, accessible));
+    window.emit(EVENTS.NEW_ACCESSIBLE_FRONT_HIGHLIGHTED);
+  },
+
+  /**
+   * Process message from accessibility panel.
+   *
+   * @param {Object} event  message type and data.
+   */
+  onMessage(event) {
+    const data = event.data;
+    const method = data.type;
+
+    if (typeof this[method] === "function") {
+      this[method](...data.args);
+    }
+  },
+};
+
+window.view = new AccessibilityView(store);
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/accessibility.css
@@ -0,0 +1,332 @@
+/* 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/. */
+
+:root {
+  --accessibility-font-size: 12px;
+  --accessibility-toolbar-height: 24px;
+  --accessibility-toolbar-height-tall: 35px;
+  --accessibility-toolbar-focus: var(--blue-50);
+  --accessibility-toolbar-focus-alpha30: rgba(10, 132, 255, 0.3);
+  --accessibility-full-length-minus-splitter: calc(100% - 1px);
+  --accessibility-horizontal-padding: 5px;
+  --accessibility-arrow-horizontal-padding: 4px;
+  --accessibility-tree-row-height: 21px;
+}
+
+/* General */
+html,
+body {
+  height: 100%;
+  margin: 0;
+  padding: 0;
+  width: 100%;
+}
+
+:root .flash-out {
+  animation: flash-out 0.5s ease-out;
+}
+
+@keyframes flash-out {
+  from {
+    background: var(--theme-contrast-background);
+  }
+}
+
+.accessible .tree .node.focused .theme-twisty,
+.treeTable .treeRow.selected .theme-twisty {
+  background-position: -28px -14px;
+}
+
+.accessible .tree .node.focused .theme-twisty.open,
+.treeTable .treeRow.selected .theme-twisty.open {
+  background-position: -42px -14px;
+}
+
+.mainFrame .main-panel {
+  flex: 1 1 auto;
+  overflow: auto;
+}
+
+.mainFrame {
+  height: 100%;
+  color: var(--theme-toolbar-color);
+}
+
+.split-box.horz {
+  height: calc(100vh - var(--accessibility-toolbar-height));
+}
+
+.mainFrame .devtools-button,
+.description .devtools-button {
+  padding: unset;
+}
+
+.mainFrame .devtools-button > .btn-content {
+  padding: 2px var(--accessibility-horizontal-padding);
+}
+
+.description .devtools-button > .btn-content {
+  padding: 7px var(--accessibility-horizontal-padding);
+}
+
+.devtools-button:focus,
+.devtools-button > .btn-content:focus,
+.devtools-button::-moz-focus-inner {
+  border: 1px solid transparent;
+  outline: none;
+}
+
+.devtools-button:focus > .btn-content:not(.devtools-throbber) {
+  outline: 2px solid var(--accessibility-toolbar-focus);
+  outline-offset: -2px;
+  box-shadow: 0 0 0 2px var(--accessibility-toolbar-focus-alpha30);
+  border-radius: 2px;
+  -moz-outline-radius: 2px;
+}
+
+/* Description */
+.description {
+  color: var(--theme-toolbar-color);
+  font: message-box;
+  font-size: calc(var(--accessibility-font-size) + 1px);
+  margin: auto;
+  padding-top: 15vh;
+  width: 50vw;
+}
+
+.description .general {
+  display: flex;
+  align-items: center;
+  margin-bottom: 1.7em;
+}
+
+.description img {
+  margin-right: 12px;
+  flex-basis: 42px;
+  -moz-context-properties: fill;
+  fill: var(--grey-40);
+}
+
+.description .devtools-button {
+  display: flex;
+  align-items: center;
+  margin: auto;
+}
+
+/* TreeView Customization */
+.split-box:not(.horz) .main-panel {
+  height: calc(100vh - var(--accessibility-toolbar-height));
+}
+
+.treeTable > thead {
+  position: sticky;
+  top: 0;
+}
+
+.split-box:not(.horz) .treeTable {
+  /* To compenstate for 1px splitter between the tree and sidebar. */
+  width: var(--accessibility-full-length-minus-splitter);
+}
+
+.split-box.horz .treeTable {
+  width: 100%;
+}
+
+.treeTable .treeRow.highlighted:not(.selected) {
+  background-color: var(--theme-selection-background-hover);
+}
+
+.treeTable .treeLabelCell {
+  min-width: 50%;
+}
+
+.treeTable:focus,
+.treeTable > tbody:focus {
+  outline: 0;
+}
+
+.treeTable::-moz-focus-inner,
+.treeTable > tbody::-moz-focus-inner {
+  border: 0;
+}
+
+.treeTable:focus > tbody {
+  outline: var(--theme-focus-outline);
+  outline-offset: -1px;
+}
+
+.treeTable > thead {
+  pointer-events: none;
+}
+
+.treeTable > tbody tr {
+  height: var(--accessibility-tree-row-height);
+}
+
+.treeTable > tbody td {
+  -moz-user-select: none;
+}
+
+.treeTable > tbody td > span {
+  -moz-user-select: text;
+}
+
+.mainFrame .treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
+  cursor: unset;
+  text-decoration: none;
+}
+
+.mainFrame .treeTable .treeHeaderRow > .treeHeaderCell:first-child > .treeHeaderCellBox,
+.mainFrame .treeTable .treeHeaderRow > .treeHeaderCell > .treeHeaderCellBox {
+  padding: 0;
+  padding-inline-start: var(--accessibility-arrow-horizontal-padding);
+}
+
+.mainFrame .treeTable .treeHeaderCell {
+  border-bottom: 1px solid var(--theme-splitter-color);
+  background: var(--theme-toolbar-background);
+  font: message-box;
+  font-size: var(--accessibility-font-size);
+  height: var(--accessibility-toolbar-height);
+  color: var(--theme-toolbar-color);
+}
+
+/* Right Sidebar */
+.right-sidebar {
+  display: flex;
+  flex-direction: column;
+  flex: 1;
+  white-space: nowrap;
+  font: message-box;
+  font-size: var(--accessibility-font-size);
+}
+
+.split-box:not(.horz) .right-sidebar {
+  position: fixed;
+  width: inherit;
+  height: calc(100vh - (var(--accessibility-toolbar-height)));
+}
+
+.right-sidebar ._header {
+  background-color: var(--theme-toolbar-background);
+  border-bottom: 1px solid var(--theme-splitter-color);
+  height: var(--accessibility-toolbar-height);
+  line-height: var(--accessibility-toolbar-height);
+  padding-inline-start: 14px;
+  padding-inline-end: var(--accessibility-arrow-horizontal-padding);
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+}
+
+.right-sidebar ._content {
+  font-size: var(--accessibility-font-size);
+  flex: 2 0;
+  overflow: auto;
+}
+
+/* Tree customization */
+.accessible .tree {
+  flex: 1;
+  height: 100%;
+  white-space: nowrap;
+  overflow: auto;
+  display: block;
+}
+
+.split-box.horz .accessible .tree {
+  width: 100vw;
+}
+
+.accessible .tree button {
+  display: block;
+}
+
+/* NOTE: total height of the node (height + padding + border + margin) must
+   be exactly the same as the value of TREE_ROW_HEIGHT constant in
+   devtools/client/accessibility/constants.js */
+.accessible .tree .node {
+  padding: 0 var(--accessibility-horizontal-padding);
+  position: relative;
+  display: flex;
+  height: var(--accessibility-tree-row-height);
+  width: calc(100% - var(--accessibility-horizontal-padding));
+  cursor: default;
+  align-items: center;
+}
+
+.accessible .tree .node.focused {
+  background-color: var(--theme-selection-background);
+}
+
+.accessible .tree .tree-node:hover:not(.focused) {
+  background-color: var(--theme-selection-background-hover);
+}
+
+.accessible .tree .node.focused * {
+  color: var(--theme-selection-color);
+}
+
+.accessible .tree .node.focused .open-inspector {
+  background-color: var(--grey-30);
+}
+
+.accessible .tree .node.focused:hover .open-inspector {
+  background-color: var(--theme-selection-color);
+}
+
+.accessible .tree .arrow {
+  flex-shrink: 0;
+}
+
+.accessible .tree .object-value {
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.accessible .tree .object-delimiter {
+  padding-inline-end: var(--accessibility-arrow-horizontal-padding);
+}
+
+.accessible .tree .object-label {
+  color: var(--theme-highlight-blue);
+}
+
+.accessible .tree .objectBox-node {
+  width: 100%;
+  display: flex;
+}
+
+.accessible .tree .objectBox-node .attrName {
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.accessible .tree .objectBox-node .open-inspector{
+  width: 17px;
+}
+
+.accessible .tree .objectBox-object,
+.accessible .tree .objectBox-string,
+.accessible .tree .objectBox-text,
+.accessible .tree .objectBox-table,
+.accessible .tree .objectLink-textNode,
+.accessible .tree .objectLink-event,
+.accessible .tree .objectLink-eventLog,
+.accessible .tree .objectLink-regexp,
+.accessible .tree .objectLink-object,
+.accessible .tree .objectLink-Date,
+.theme-dark .accessible .tree .objectBox-object,
+.theme-light .accessible .tree .objectBox-object {
+  white-space: nowrap;
+}
+
+/* Styling for accessible details panel when accessible is not available */
+.accessible .info {
+  color: var(--theme-body-color);
+  font-size: 110%;
+  padding-inline-start: var(--accessibility-arrow-horizontal-padding);
+  height: var(--accessibility-toolbar-height-tall);
+  line-height: var(--accessibility-toolbar-height-tall);
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/accessibility.html
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!DOCTYPE html>
+<html dir="">
+<head>
+  <meta charset="utf-8"/>
+
+  <link href="resource://devtools/client/accessibility/accessibility.css" rel="stylesheet"/>
+  <link href="resource://devtools/client/shared/components/splitter/SplitBox.css" rel="stylesheet" />
+  <link href="resource://devtools/client/shared/components/tree/TreeView.css" rel="stylesheet" />
+
+  <script type="application/javascript"
+          src="chrome://devtools/content/shared/theme-switching.js"></script>
+</head>
+<body class="theme-body devtools-monospace" role="application">
+  <div id="content" role="presentation"></div>
+  <script type="text/javascript" src="./main.js"></script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/actions/accessibles.js
@@ -0,0 +1,28 @@
+/* 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 { FETCH_CHILDREN, SELECT, HIGHLIGHT, UNHIGHLIGHT } = require("../constants");
+
+/**
+ * Fetch child accessibles for a given accessible object.
+ * @param {Object} accessible front
+ */
+exports.fetchChildren = accessible =>
+  dispatch => accessible.children()
+    .then(response => dispatch({ accessible, type: FETCH_CHILDREN, response }))
+    .catch(error => dispatch({ accessible, type: FETCH_CHILDREN, error }));
+
+exports.select = (walker, accessible) =>
+  dispatch => walker.getAncestry(accessible)
+    .then(response => dispatch({ accessible, type: SELECT, response }))
+    .catch(error => dispatch({ accessible, type: SELECT, error }));
+
+exports.highlight = (walker, accessible) =>
+  dispatch => walker.getAncestry(accessible)
+    .then(response => dispatch({ accessible, type: HIGHLIGHT, response }))
+    .catch(error => dispatch({ accessible, type: HIGHLIGHT, error }));
+
+exports.unhighlight = () =>
+  dispatch => dispatch({ type: UNHIGHLIGHT });
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/actions/details.js
@@ -0,0 +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";
+
+const { UPDATE_DETAILS } = require("../constants");
+
+/**
+ * Update details with the given accessible object.
+ *
+ * @param {Object} dom walker front
+ * @param {Object} accessible front
+ */
+exports.updateDetails = (domWalker, accessible) =>
+  dispatch =>
+    domWalker.getNodeFromActor(accessible.actorID, ["rawAccessible", "DOMNode"])
+      .then(response => dispatch({ accessible, type: UPDATE_DETAILS, response }))
+      .catch(error => dispatch({ accessible, type: UPDATE_DETAILS, error }));
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/actions/moz.build
@@ -0,0 +1,9 @@
+# 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/.
+
+DevToolsModules(
+    'accessibles.js',
+    'details.js',
+    'ui.js'
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/actions/ui.js
@@ -0,0 +1,46 @@
+/* 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 {
+  ENABLE,
+  DISABLE,
+  RESET,
+  UPDATE_CAN_BE_DISABLED,
+  UPDATE_CAN_BE_ENABLED
+} = require("../constants");
+
+/**
+ * Reset accessibility panel UI.
+ */
+exports.reset = accessibility =>
+  dispatch => dispatch({ accessibility, type: RESET });
+
+/**
+ * Update a "canBeDisabled" flag for accessibility service.
+ */
+exports.updateCanBeDisabled = canBeDisabled =>
+  dispatch => dispatch({ canBeDisabled, type: UPDATE_CAN_BE_DISABLED });
+
+/**
+ * Update a "canBeEnabled" flag for accessibility service.
+ */
+exports.updateCanBeEnabled = canBeEnabled =>
+  dispatch => dispatch({ canBeEnabled, type: UPDATE_CAN_BE_ENABLED });
+
+/**
+ * Enable accessibility services in order to view accessible tree.
+ */
+exports.enable = accessibility =>
+  dispatch => accessibility.enable()
+    .then(() => dispatch({ type: ENABLE }))
+    .catch(error => dispatch({ error, type: ENABLE }));
+
+/**
+ * Enable accessibility services in order to view accessible tree.
+ */
+exports.disable = accessibility =>
+  dispatch => accessibility.disable()
+    .then(() => dispatch({ type: DISABLE }))
+    .catch(error => dispatch({ type: DISABLE, error }));
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/components/AccessibilityRow.js
@@ -0,0 +1,156 @@
+/* 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";
+
+/* global gToolbox, EVENTS */
+
+// React & Redux
+const { Component, createFactory } = require("devtools/client/shared/vendor/react");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+const TreeRow = require("devtools/client/shared/components/tree/TreeRow");
+
+// Utils
+const {flashElementOn, flashElementOff} =
+  require("devtools/client/inspector/markup/utils");
+const { VALUE_FLASHING_DURATION, VALUE_HIGHLIGHT_DURATION } = require("../constants");
+
+// Actions
+const { updateDetails } = require("../actions/details");
+const { unhighlight } = require("../actions/accessibles");
+
+class HighlightableTreeRowClass extends TreeRow {
+  shouldComponentUpdate(nextProps) {
+    const props = ["name", "open", "value", "loading", "selected", "hasChildren"];
+
+    for (const p of props) {
+      if (nextProps.member[p] !== this.props.member[p]) {
+        return true;
+      }
+    }
+
+    if (nextProps.highlighted !== this.props.highlighted) {
+      return true;
+    }
+
+    return false;
+  }
+}
+
+const HighlightableTreeRow = createFactory(HighlightableTreeRowClass);
+
+// Component that expands TreeView's own TreeRow and is responsible for
+// rendering an accessible object.
+class AccessibilityRow extends Component {
+  static get propTypes() {
+    return {
+      ...TreeRow.propTypes,
+      dispatch: PropTypes.func.isRequired,
+      walker: PropTypes.object
+    };
+  }
+
+  componentDidMount() {
+    const { selected, object } = this.props.member;
+    if (selected) {
+      this.updateAndScrollIntoViewIfNeeded();
+      this.highlight(object, { duration: VALUE_HIGHLIGHT_DURATION });
+    }
+
+    if (this.props.highlighted) {
+      this.scrollIntoView();
+    }
+  }
+
+  /**
+   * Update accessible object details that are going to be rendered inside the
+   * accessible panel sidebar.
+   */
+  componentDidUpdate(prevProps) {
+    const { selected, object } = this.props.member;
+    // If row is selected, update corresponding accessible details.
+    if (!prevProps.member.selected && selected) {
+      this.updateAndScrollIntoViewIfNeeded();
+      this.highlight(object, { duration: VALUE_HIGHLIGHT_DURATION });
+    }
+
+    if (this.props.highlighted) {
+      this.scrollIntoView();
+    }
+
+    if (!selected && prevProps.member.value !== this.props.member.value) {
+      this.flashValue();
+    }
+  }
+
+  scrollIntoView() {
+    const row = findDOMNode(this);
+    row.scrollIntoView({ block: "center" });
+  }
+
+  updateAndScrollIntoViewIfNeeded() {
+    const { dispatch, member } = this.props;
+    if (gToolbox) {
+      dispatch(updateDetails(gToolbox.walker, member.object));
+    }
+
+    this.scrollIntoView();
+    window.emit(EVENTS.NEW_ACCESSIBLE_FRONT_SELECTED, member.object);
+  }
+
+  flashValue() {
+    const row = findDOMNode(this);
+    const value = row.querySelector(".objectBox");
+
+    flashElementOn(value);
+    if (this._flashMutationTimer) {
+      clearTimeout(this._flashMutationTimer);
+      this._flashMutationTimer = null;
+    }
+    this._flashMutationTimer = setTimeout(() => {
+      flashElementOff(value);
+    }, VALUE_FLASHING_DURATION);
+  }
+
+  highlight(accessible, options) {
+    const { walker, dispatch } = this.props;
+    dispatch(unhighlight());
+
+    if (!accessible || !walker) {
+      return;
+    }
+
+    walker.highlightAccessible(accessible, options).catch(error =>
+      console.warn(error));
+  }
+
+  unhighlight() {
+    const { walker, dispatch } = this.props;
+    dispatch(unhighlight());
+
+    if (!walker) {
+      return;
+    }
+
+    walker.unhighlight().catch(error => console.warn(error));
+  }
+
+  /**
+   * Render accessible row component.
+   * @returns acecssible-row React component.
+   */
+  render() {
+    const { object } = this.props.member;
+    const props = Object.assign({}, this.props, {
+      onMouseOver: () => this.highlight(object),
+      onMouseOut: () => this.unhighlight()
+    });
+
+    return (HighlightableTreeRow(props));
+  }
+}
+
+module.exports = connect()(AccessibilityRow);
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/components/AccessibilityTree.js
@@ -0,0 +1,195 @@
+/* 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";
+
+/* global EVENTS */
+
+// React & Redux
+const { Component, createFactory } = require("devtools/client/shared/vendor/react");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+const TreeView = createFactory(require("devtools/client/shared/components/tree/TreeView"));
+// Reps
+const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
+const Rep = createFactory(REPS.Rep);
+const Grip = REPS.Grip;
+
+const { fetchChildren } = require("../actions/accessibles");
+
+const { L10N } = require("../utils/l10n");
+const AccessibilityRow = createFactory(require("./AccessibilityRow"));
+const { Provider } = require("../provider");
+
+/**
+ * Renders Accessibility panel tree.
+ */
+class AccessibilityTree extends Component {
+  static get propTypes() {
+    return {
+      walker: PropTypes.object,
+      dispatch: PropTypes.func.isRequired,
+      accessibles: PropTypes.object,
+      expanded: PropTypes.object,
+      selected: PropTypes.string,
+      highlighted: PropTypes.object
+    };
+  }
+
+  constructor(props) {
+    super(props);
+
+    this.onNameChange = this.onNameChange.bind(this);
+    this.onReorder = this.onReorder.bind(this);
+    this.onTextChange = this.onTextChange.bind(this);
+  }
+
+  /**
+   * Add accessibility walker front event listeners that affect tree rendering
+   * and updates.
+   */
+  componentWillMount() {
+    let { walker } = this.props;
+    walker.on("reorder", this.onReorder);
+    walker.on("name-change", this.onNameChange);
+    walker.on("text-change", this.onTextChange);
+    return null;
+  }
+
+  componentDidUpdate() {
+    window.emit(EVENTS.ACCESSIBILITY_INSPECTOR_UPDATED);
+  }
+
+  /**
+   * Remove accessible walker front event listeners.
+   */
+  componentWillUnmount() {
+    let { walker } = this.props;
+    walker.off("reorder", this.onReorder);
+    walker.off("name-change", this.onNameChange);
+    walker.off("text-change", this.onTextChange);
+  }
+
+  /**
+   * Handle accessible reorder event. If the accessible is cached and rendered
+   * within the accessibility tree, re-fetch its children and re-render the
+   * corresponding subtree.
+   * @param {Object} accessible accessible object that had its subtree
+   *                            reordered.
+   */
+  onReorder(accessible) {
+    if (this.props.accessibles.has(accessible.actorID)) {
+      this.props.dispatch(fetchChildren(accessible));
+    }
+  }
+
+  /**
+   * Handle accessible name change event. If the name of an accessible changes
+   * and that accessible is cached and rendered within the accessibility tree,
+   * re-fetch its parent's children and re-render the corresponding subtree.
+   * @param {Object} accessible accessible object that had its name changed.
+   * @param {Object} parent     optional parent accessible object. Note: if it
+   *                            parent is not present, we assume that the top
+   *                            level document's name has changed and use
+   *                            accessible walker as a parent.
+   */
+  onNameChange(accessible, parent) {
+    let { accessibles, walker, dispatch } = this.props;
+    parent = parent || walker;
+
+    if (accessibles.has(accessible.actorID) ||
+        accessibles.has(parent.actorID)) {
+      dispatch(fetchChildren(parent));
+    }
+  }
+
+  /**
+   * Handle accessible text change (change/insert/remove) event. If the text of
+   * an accessible changes and that accessible is cached and rendered within the
+   * accessibility tree, re-fetch its children and re-render the corresponding
+   * subtree.
+   * @param  {Object} accessible  accessible object that had its child text
+   *                              changed.
+   */
+  onTextChange(accessible) {
+    let { accessibles, dispatch } = this.props;
+    if (accessibles.has(accessible.actorID)) {
+      dispatch(fetchChildren(accessible));
+    }
+  }
+
+  /**
+   * Render Accessibility panel content
+   */
+  render() {
+    let columns = [{
+      "id": "default",
+      "title": L10N.getStr("accessibility.role")
+    }, {
+      "id": "value",
+      "title": L10N.getStr("accessibility.name")
+    }];
+
+    let {
+      accessibles,
+      dispatch,
+      expanded,
+      selected,
+      highlighted: highlightedItem,
+      walker
+    } = this.props;
+
+    let renderValue = props => {
+      return Rep(Object.assign({}, props, {
+        defaultRep: Grip,
+        cropLimit: 50,
+      }));
+    };
+
+    let renderRow = rowProps => {
+      let { object } = rowProps.member;
+      let highlighted = object === highlightedItem;
+      return AccessibilityRow(Object.assign({}, rowProps, {
+        walker,
+        highlighted,
+        decorator: {
+          getRowClass: function() {
+            return highlighted ? ["highlighted"] : [];
+          }
+        }
+      }));
+    };
+
+    return (
+      TreeView({
+        object: walker,
+        mode: MODE.SHORT,
+        provider: new Provider(accessibles, dispatch),
+        columns: columns,
+        renderValue,
+        renderRow,
+        label: L10N.getStr("accessibility.treeName"),
+        header: true,
+        expandedNodes: expanded,
+        selected,
+        onClickRow(nodePath, event) {
+          event.stopPropagation();
+          if (event.target.classList.contains("theme-twisty")) {
+            this.toggle(nodePath);
+          }
+          this.selectRow(event.currentTarget);
+        }
+      })
+    );
+  }
+}
+
+const mapStateToProps = ({ accessibles, ui }) => ({
+  accessibles,
+  expanded: ui.expanded,
+  selected: ui.selected,
+  highlighted: ui.highlighted
+});
+// Exports from this module
+module.exports = connect(mapStateToProps)(AccessibilityTree);
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/components/Accessible.js
@@ -0,0 +1,355 @@
+/* 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";
+
+/* global EVENTS, gToolbox */
+
+// React & Redux
+const { createFactory, Component } = require("devtools/client/shared/vendor/react");
+const { div, span } = require("devtools/client/shared/vendor/react-dom-factories");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+const { TREE_ROW_HEIGHT, ORDERED_PROPS, ACCESSIBLE_EVENTS, VALUE_FLASHING_DURATION } =
+  require("../constants");
+const { L10N } = require("../utils/l10n");
+const {flashElementOn, flashElementOff} =
+      require("devtools/client/inspector/markup/utils");
+const { updateDetails } = require("../actions/details");
+
+const Tree = createFactory(require("devtools/client/shared/components/VirtualizedTree"));
+// Reps
+const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
+const { Rep, ElementNode } = REPS;
+
+class AccessiblePropertyClass extends Component {
+  static get propTypes() {
+    return {
+      accessible: PropTypes.string,
+      object: PropTypes.any,
+      focused: PropTypes.bool,
+      children: PropTypes.func
+    };
+  }
+
+  componentDidUpdate({ object: prevObject, accessible: prevAccessible }) {
+    let { accessible, object, focused } = this.props;
+    // Fast check if row is focused or if the value did not update.
+    if (focused || accessible !== prevAccessible || prevObject === object ||
+        (object && prevObject && typeof object === "object")) {
+      return;
+    }
+
+    this.flashRow();
+  }
+
+  flashRow() {
+    let row = findDOMNode(this);
+    flashElementOn(row);
+    if (this._flashMutationTimer) {
+      clearTimeout(this._flashMutationTimer);
+      this._flashMutationTimer = null;
+    }
+    this._flashMutationTimer = setTimeout(() => {
+      flashElementOff(row);
+    }, VALUE_FLASHING_DURATION);
+  }
+
+  render() {
+    return this.props.children();
+  }
+}
+
+const AccessibleProperty = createFactory(AccessiblePropertyClass);
+
+class Accessible extends Component {
+  static get propTypes() {
+    return {
+      accessible: PropTypes.object,
+      dispatch: PropTypes.func.isRequired,
+      DOMNode: PropTypes.object,
+      items: PropTypes.array,
+      labelledby: PropTypes.string.isRequired,
+      parents: PropTypes.object
+    };
+  }
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      expanded: new Set(),
+      focused: null
+    };
+
+    this.onAccessibleInspected = this.onAccessibleInspected.bind(this);
+    this.renderItem = this.renderItem.bind(this);
+    this.update = this.update.bind(this);
+  }
+
+  componentWillMount() {
+    window.on(EVENTS.NEW_ACCESSIBLE_FRONT_INSPECTED, this.onAccessibleInspected);
+  }
+
+  componentWillReceiveProps({ accessible }) {
+    let oldAccessible = this.props.accessible;
+
+    if (oldAccessible) {
+      if (accessible && accessible.actorID === oldAccessible.actorID) {
+        return;
+      }
+      ACCESSIBLE_EVENTS.forEach(event => oldAccessible.off(event, this.update));
+    }
+
+    if (accessible) {
+      ACCESSIBLE_EVENTS.forEach(event => accessible.on(event, this.update));
+    }
+  }
+
+  componentWillUnmount() {
+    window.off(EVENTS.NEW_ACCESSIBLE_FRONT_INSPECTED, this.onAccessibleInspected);
+
+    let { accessible } = this.props;
+    if (accessible) {
+      ACCESSIBLE_EVENTS.forEach(event => accessible.off(event, this.update));
+    }
+  }
+
+  onAccessibleInspected() {
+    let { props } = this.refs;
+    if (props) {
+      props.refs.tree.focus();
+    }
+  }
+
+  update() {
+    let { dispatch, accessible } = this.props;
+    if (gToolbox) {
+      dispatch(updateDetails(gToolbox.walker, accessible));
+    }
+  }
+
+  setExpanded(item, isExpanded) {
+    const { expanded } = this.state;
+
+    if (isExpanded) {
+      expanded.add(item.path);
+    } else {
+      expanded.delete(item.path);
+    }
+
+    this.setState({ expanded });
+  }
+
+  showHighlighter(nodeFront) {
+    if (!gToolbox) {
+      return;
+    }
+
+    gToolbox.highlighterUtils.highlightNodeFront(nodeFront);
+  }
+
+  hideHighlighter() {
+    if (!gToolbox) {
+      return;
+    }
+
+    gToolbox.highlighterUtils.unhighlight();
+  }
+
+  selectNode(nodeFront, reason = "accessibility") {
+    if (!gToolbox) {
+      return;
+    }
+
+    gToolbox.selectTool("inspector").then(() =>
+      gToolbox.selection.setNodeFront(nodeFront, reason));
+  }
+
+  renderItem(item, depth, focused, arrow, expanded) {
+    const object = item.contents;
+    let valueProps = { object, mode: MODE.TINY, title: "Object" };
+    if (isNode(object)) {
+      valueProps.defaultRep = ElementNode;
+      valueProps.onDOMNodeMouseOut = () => this.hideHighlighter();
+      valueProps.onDOMNodeMouseOver = () => this.showHighlighter(this.props.DOMNode);
+      valueProps.onInspectIconClick = () => this.selectNode(this.props.DOMNode);
+    }
+
+    let classList = [ "node", "object-node" ];
+    if (focused) {
+      classList.push("focused");
+    }
+
+    return AccessibleProperty(
+      { object, focused, accessible: this.props.accessible.actorID },
+      () => div({
+        className: classList.join(" "),
+        style: { paddingInlineStart: depth * 15 },
+        onClick: e => {
+          if (e.target.classList.contains("theme-twisty")) {
+            this.setExpanded(item, !expanded);
+          }
+        }
+      },
+        arrow,
+        span({ className: "object-label" }, item.name),
+        span({ className: "object-delimiter" }, ":"),
+        span({ className: "object-value" }, Rep(valueProps) || "")
+      )
+    );
+  }
+
+  render() {
+    const { expanded, focused } = this.state;
+    const { items, parents, accessible, labelledby } = this.props;
+
+    if (accessible) {
+      return Tree({
+        ref: "props",
+        key: "accessible-properties",
+        itemHeight: TREE_ROW_HEIGHT,
+        getRoots: () => items,
+        getKey: item => item.path,
+        getParent: item => parents.get(item),
+        getChildren: item => item.children,
+        isExpanded: item => expanded.has(item.path),
+        onExpand: item => this.setExpanded(item, true),
+        onCollapse: item => this.setExpanded(item, false),
+        onFocus: item => {
+          if (this.state.focused !== item.path) {
+            this.setState({ focused: item.path });
+          }
+        },
+        onActivate: ({ contents }) => {
+          if (isNode(contents)) {
+            this.selectNode(this.props.DOMNode, "accessibility-keyboard");
+          }
+        },
+        focused: findFocused(focused, items),
+        renderItem: this.renderItem,
+        labelledby
+      });
+    }
+
+    return div({ className: "info" },
+               L10N.getStr("accessibility.accessible.notAvailable"));
+  }
+}
+
+/**
+ * Find currently focused item.
+ * @param  {String} focused Key of the currently focused item.
+ * @param  {Array}  items   Accessibility properties array.
+ * @return {Object?}        Possibly found focused item.
+ */
+const findFocused = (focused, items) => {
+  for (let item of items) {
+    if (item.path === focused) {
+      return item;
+    }
+    let found = findFocused(focused, item.children);
+    if (found) {
+      return found;
+    }
+  }
+  return null;
+};
+
+/**
+ * Check if a given property is a DOMNode actor.
+ * @param  {Object?} value A property to check for being a DOMNode.
+ * @return {Boolean}       A flag that indicates whether a property is a DOMNode.
+ */
+const isNode = value => value && value.typeName === "domnode";
+
+/**
+ * While waiting for a reps fix in https://github.com/devtools-html/reps/issues/92,
+ * translate nodeFront to a grip-like object that can be used with an ElementNode rep.
+ *
+ * @params  {NodeFront} nodeFront
+ *          The NodeFront for which we want to create a grip-like object.
+ * @returns {Object} a grip-like object that can be used with Reps.
+ */
+const translateNodeFrontToGrip = nodeFront => {
+  let { attributes, actorID, typeName, nodeName, nodeType } = nodeFront;
+
+  // The main difference between NodeFront and grips is that attributes are treated as
+  // a map in grips and as an array in NodeFronts.
+  let attributesMap = {};
+  for (let { name, value } of attributes) {
+    attributesMap[name] = value;
+  }
+
+  return {
+    actor: actorID,
+    typeName,
+    preview: {
+      attributes: attributesMap,
+      attributesLength: attributes.length,
+      // All the grid containers are assumed to be in the DOM tree.
+      isConnected: true,
+      // nodeName is already lowerCased in Node grips
+      nodeName: nodeName.toLowerCase(),
+      nodeType
+    }
+  };
+};
+
+/**
+ * Build props ingestible by Tree component.
+ * @param  {Object} props      Component properties to be processed.
+ * @param  {String} parentPath Unique path that is used to identify a Tree Node.
+ * @return {Object}            Processed properties.
+ */
+const makeItemsForDetails = (props, parentPath) =>
+  Object.getOwnPropertyNames(props).map(name => {
+    let children = [];
+    let path = `${parentPath}/${name}`;
+    let contents = props[name];
+
+    if (contents) {
+      if (isNode(contents)) {
+        contents = translateNodeFrontToGrip(contents);
+      } else if (Array.isArray(contents) || typeof contents === "object") {
+        children = makeItemsForDetails(contents, path);
+      }
+    }
+
+    return { name, path, contents, children };
+  });
+
+const makeParentMap = (items) => {
+  const map = new WeakMap();
+
+  function _traverse(item) {
+    if (item.children.length > 0) {
+      for (const child of item.children) {
+        map.set(child, item);
+        _traverse(child);
+      }
+    }
+  }
+
+  items.forEach(_traverse);
+  return map;
+};
+
+const mapStateToProps = ({ details }) => {
+  let { accessible, DOMNode } = details;
+  if (!accessible || !DOMNode) {
+    return {};
+  }
+
+  let items = makeItemsForDetails(ORDERED_PROPS.reduce((props, key) => {
+    props[key] = key === "DOMNode" ? DOMNode : accessible[key];
+    return props;
+  }, {}), "");
+  let parents = makeParentMap(items);
+
+  return { accessible, DOMNode, items, parents };
+};
+
+module.exports = connect(mapStateToProps)(Accessible);
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/components/Button.js
@@ -0,0 +1,63 @@
+/* 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 { Component } = require("devtools/client/shared/vendor/react");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { button, span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+const defaultProps = {
+  disabled: false,
+  busy: false,
+  title: null,
+  children: null,
+  className: ""
+};
+
+/**
+ * Button component that handles keyboard in an accessible way. When user
+ * uses the mouse to hover/click on the button, there is no focus
+ * highlighting. However if the user uses a keyboard to focus on the button,
+ * it will have focus highlighting in the form of an outline.
+ */
+class Button extends Component {
+  static get propTypes() {
+    return {
+      disabled: PropTypes.bool,
+      busy: PropTypes.bool,
+      title: PropTypes.string,
+      children: PropTypes.string,
+      className: PropTypes.string
+    };
+  }
+
+  static get defaultProps() {
+    return defaultProps;
+  }
+
+  render() {
+    let className = [
+      ...this.props.className.split(" "),
+      "devtools-button"
+    ].join(" ");
+    let props = Object.assign({}, this.props, {
+      className,
+      "aria-busy": this.props.busy
+    });
+
+    let classList = ["btn-content"];
+    if (this.props.busy) {
+      classList.push("devtools-throbber");
+    }
+
+    return (button(props, span({
+      className: classList.join(" "),
+      tabIndex: -1
+    }, this.props.children)));
+  }
+}
+
+// Exports from this module
+module.exports = Button;
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/components/Description.js
@@ -0,0 +1,119 @@
+/* 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";
+
+// React & Redux
+const { createFactory, Component } = require("devtools/client/shared/vendor/react");
+const { div, p, img } = require("devtools/client/shared/vendor/react-dom-factories");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+const Button = createFactory(require("./Button"));
+const { enable, updateCanBeEnabled } = require("../actions/ui");
+
+// Localization
+const { L10N } = require("../utils/l10n");
+
+class OldVersionDescription extends Component {
+  render() {
+    return (
+      div({ className: "description" },
+        p({ className: "general" },
+          img({
+            src: "chrome://devtools/skin/images/accessibility.svg",
+            alt: L10N.getStr("accessibility.logo")
+          }), L10N.getStr("accessibility.description.oldVersion")))
+    );
+  }
+}
+
+/**
+ * Landing UI for the accessibility panel when Accessibility features are
+ * deactivated.
+ */
+class Description extends Component {
+  static get propTypes() {
+    return {
+      accessibility: PropTypes.object.isRequired,
+      canBeEnabled: PropTypes.bool,
+      dispatch: PropTypes.func.isRequired
+    };
+  }
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      enabling: false
+    };
+
+    this.onEnable = this.onEnable.bind(this);
+    this.onCanBeEnabledChange = this.onCanBeEnabledChange.bind(this);
+  }
+
+  componentWillMount() {
+    this.props.accessibility.on("can-be-enabled-change",
+      this.onCanBeEnabledChange);
+  }
+
+  componentWillUnmount() {
+    this.props.accessibility.off("can-be-enabled-change",
+      this.onCanBeEnabledChange);
+  }
+
+  onEnable() {
+    let { accessibility, dispatch } = this.props;
+    this.setState({ enabling: true });
+    dispatch(enable(accessibility))
+      .then(() => this.setState({ enabling: false }))
+      .catch(() => this.setState({ enabling: false }));
+  }
+
+  onCanBeEnabledChange(canBeEnabled) {
+    this.props.dispatch(updateCanBeEnabled(canBeEnabled));
+  }
+
+  render() {
+    let { canBeEnabled } = this.props;
+    let { enabling } = this.state;
+    let enableButtonStr = enabling ? "accessibility.enabling" : "accessibility.enable";
+
+    let title;
+    let disableButton = false;
+
+    if (canBeEnabled) {
+      title = L10N.getStr("accessibility.enable.enabledTitle");
+    } else {
+      disableButton = true;
+      title = L10N.getStr("accessibility.enable.disabledTitle");
+    }
+
+    return (
+      div({ className: "description" },
+        p({ className: "general" },
+          img({
+            src: "chrome://devtools/skin/images/accessibility.svg",
+            alt: L10N.getStr("accessibility.logo")
+          }),
+          L10N.getStr("accessibility.description.general")),
+        Button({
+          id: "accessibility-enable-button",
+          onClick: this.onEnable,
+          disabled: enabling || disableButton,
+          busy: enabling,
+          "data-standalone": true,
+          title
+        }, L10N.getStr(enableButtonStr))
+      )
+    );
+  }
+}
+
+const mapStateToProps = ({ ui }) => ({
+  canBeEnabled: ui.canBeEnabled
+});
+
+// Exports from this module
+exports.Description = connect(mapStateToProps)(Description);
+exports.OldVersionDescription = OldVersionDescription;
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/components/MainFrame.js
@@ -0,0 +1,126 @@
+/* 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";
+
+/* global gToolbox */
+
+// React & Redux
+const { Component, createFactory } = require("devtools/client/shared/vendor/react");
+const { div } = require("devtools/client/shared/vendor/react-dom-factories");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { reset } = require("../actions/ui");
+
+// Constants
+const { SIDEBAR_WIDTH, PORTRAIT_MODE_WIDTH } = require("../constants");
+
+// Accessibility Panel
+const AccessibilityTree = createFactory(require("./AccessibilityTree"));
+const Description = createFactory(require("./Description").Description);
+const RightSidebar = createFactory(require("./RightSidebar"));
+const Toolbar = createFactory(require("./Toolbar"));
+const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));
+
+/**
+ * Renders basic layout of the Accessibility panel. The Accessibility panel
+ * content consists of two main parts: tree and sidebar.
+ */
+class MainFrame extends Component {
+  static get propTypes() {
+    return {
+      accessibility: PropTypes.object.isRequired,
+      walker: PropTypes.object.isRequired,
+      enabled: PropTypes.bool.isRequired,
+      dispatch: PropTypes.func.isRequired
+    };
+  }
+
+  constructor(props) {
+    super(props);
+
+    this.resetAccessibility = this.resetAccessibility.bind(this);
+    this.onPanelWindowResize = this.onPanelWindowResize.bind(this);
+  }
+
+  componentWillMount() {
+    // Need inspector for many things such as dom node preview etc.
+    gToolbox.loadTool("inspector");
+    this.props.accessibility.on("init", this.resetAccessibility);
+    this.props.accessibility.on("shutdown", this.resetAccessibility);
+    this.props.walker.on("document-ready", this.resetAccessibility);
+
+    window.addEventListener("resize", this.onPanelWindowResize, true);
+  }
+
+  componentWillReceiveProps({ enabled }) {
+    if (this.props.enabled && !enabled) {
+      this.resetAccessibility();
+    }
+  }
+
+  componentWillUnmount() {
+    this.props.accessibility.off("init", this.resetAccessibility);
+    this.props.accessibility.off("shutdown", this.resetAccessibility);
+    this.props.walker.off("document-ready", this.resetAccessibility);
+
+    window.removeEventListener("resize", this.onPanelWindowResize, true);
+  }
+
+  resetAccessibility() {
+    let { dispatch, accessibility } = this.props;
+    dispatch(reset(accessibility));
+  }
+
+  get useLandscapeMode() {
+    let { clientWidth } = document.getElementById("content");
+    return clientWidth > PORTRAIT_MODE_WIDTH;
+  }
+
+  /**
+   * If panel width is less than PORTRAIT_MODE_WIDTH px, the splitter changes
+   * its mode to `horizontal` to support portrait view.
+   */
+  onPanelWindowResize() {
+    if (this.refs.splitBox) {
+      this.refs.splitBox.setState({ vert: this.useLandscapeMode });
+    }
+  }
+
+  /**
+   * Render Accessibility panel content
+   */
+  render() {
+    let { accessibility, walker, enabled } = this.props;
+
+    if (!enabled) {
+      return Description({ accessibility });
+    }
+
+    return (
+      div({ className: "mainFrame", role: "presentation" },
+        Toolbar({ accessibility, walker }),
+        SplitBox({
+          ref: "splitBox",
+          initialSize: SIDEBAR_WIDTH,
+          minSize: "10px",
+          maxSize: "80%",
+          splitterSize: 1,
+          endPanelControl: true,
+          startPanel: div({
+            className: "main-panel",
+            role: "presentation"
+          }, AccessibilityTree({ walker })),
+          endPanel: RightSidebar(),
+          vert: this.useLandscapeMode,
+        })
+      ));
+  }
+}
+
+const mapStateToProps = ({ ui }) => ({
+  enabled: ui.enabled
+});
+
+// Exports from this module
+module.exports = connect(mapStateToProps)(MainFrame);
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/components/RightSidebar.js
@@ -0,0 +1,40 @@
+/* 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";
+
+// React
+const { Component, createFactory } = require("devtools/client/shared/vendor/react");
+const { div } = require("devtools/client/shared/vendor/react-dom-factories");
+
+const { L10N } = require("../utils/l10n");
+const Accessible = createFactory(require("./Accessible"));
+
+// Component that is responsible for rendering accessible panel's sidebar.
+class RightSidebar extends Component {
+  /**
+   * Render the sidebar component.
+   * @returns Sidebar React component.
+   */
+  render() {
+    const headerID = "accessibility-right-sidebar-header";
+    return (
+      div({
+        className: "right-sidebar",
+        role: "presentation"
+      },
+        div({
+          className: "_header",
+          id: headerID,
+          role: "heading"
+        }, L10N.getStr("accessibility.properties")),
+        div({
+          className: "_content accessible",
+          role: "presentation"
+        }, Accessible({ labelledby: headerID }))
+      )
+    );
+  }
+}
+
+module.exports = RightSidebar;
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/components/Toolbar.js
@@ -0,0 +1,94 @@
+/* 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";
+
+// React
+const { createFactory, Component } = require("devtools/client/shared/vendor/react");
+const { div } = require("devtools/client/shared/vendor/react-dom-factories");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { L10N } = require("../utils/l10n");
+const Button = createFactory(require("./Button"));
+
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { disable, updateCanBeDisabled } = require("../actions/ui");
+
+class Toolbar extends Component {
+  static get propTypes() {
+    return {
+      dispatch: PropTypes.func.isRequired,
+      accessibility: PropTypes.object.isRequired,
+      canBeDisabled: PropTypes.bool.isRequired
+    };
+  }
+
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      disabling: false
+    };
+
+    this.onDisable = this.onDisable.bind(this);
+    this.onCanBeDisabledChange = this.onCanBeDisabledChange.bind(this);
+  }
+
+  componentWillMount() {
+    this.props.accessibility.on("can-be-disabled-change",
+      this.onCanBeDisabledChange);
+  }
+
+  componentWillUnmount() {
+    this.props.accessibility.off("can-be-disabled-change",
+      this.onCanBeDisabledChange);
+  }
+
+  onCanBeDisabledChange(canBeDisabled) {
+    this.props.dispatch(updateCanBeDisabled(canBeDisabled));
+  }
+
+  onDisable() {
+    let { accessibility, dispatch } = this.props;
+    this.setState({ disabling: true });
+    dispatch(disable(accessibility))
+      .then(() => this.setState({ disabling: false }))
+      .catch(() => this.setState({ disabling: false }));
+  }
+
+  render() {
+    let { canBeDisabled } = this.props;
+    let { disabling } = this.state;
+    let disableButtonStr = disabling ?
+      "accessibility.disabling" : "accessibility.disable";
+    let title;
+    let isDisabled = false;
+
+    if (canBeDisabled) {
+      title = L10N.getStr("accessibility.disable.enabledTitle");
+    } else {
+      isDisabled = true;
+      title = L10N.getStr("accessibility.disable.disabledTitle");
+    }
+
+    return (
+      div({
+        className: "devtools-toolbar",
+        role: "toolbar"
+      }, Button({
+        className: "disable",
+        id: "accessibility-disable-button",
+        onClick: this.onDisable,
+        disabled: disabling || isDisabled,
+        busy: disabling,
+        title
+      }, L10N.getStr(disableButtonStr)))
+    );
+  }
+}
+
+const mapStateToProps = ({ ui }) => ({
+  canBeDisabled: ui.canBeDisabled
+});
+
+// Exports from this module
+module.exports = connect(mapStateToProps)(Toolbar);
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/components/moz.build
@@ -0,0 +1,14 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DevToolsModules(
+    'AccessibilityRow.js',
+    'AccessibilityTree.js',
+    'Accessible.js',
+    'Button.js',
+    'Description.js',
+    'MainFrame.js',
+    'RightSidebar.js',
+    'Toolbar.js'
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/constants.js
@@ -0,0 +1,63 @@
+/* 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";
+
+// Used in accessible component for properties tree rendering.
+exports.TREE_ROW_HEIGHT = 21;
+
+// Initial sidebar width.
+exports.SIDEBAR_WIDTH = "350px";
+
+// When value is updated either in the tree or sidebar.
+exports.VALUE_FLASHING_DURATION = 500;
+// When new row is selected, flash highlighter.
+exports.VALUE_HIGHLIGHT_DURATION = 1000;
+
+// If the panel width is smaller than given amount of pixels,
+// the sidebar automatically switches from 'landscape' to 'portrait' mode.
+exports.PORTRAIT_MODE_WIDTH = 700;
+
+// Names for Redux actions.
+exports.FETCH_CHILDREN = "FETCH_CHILDREN";
+exports.UPDATE_DETAILS = "UPDATE_DETAILS";
+exports.RESET = "RESET";
+exports.SELECT = "SELECT";
+exports.HIGHLIGHT = "HIGHLIGHT";
+exports.UNHIGHLIGHT = "UNHIGHLIGHT";
+exports.ENABLE = "ENABLE";
+exports.DISABLE = "DISABLE";
+exports.UPDATE_CAN_BE_DISABLED = "UPDATE_CAN_BE_DISABLED";
+exports.UPDATE_CAN_BE_ENABLED = "UPDATE_CAN_BE_ENABLED";
+
+// Ordered accessible properties to be displayed by the accessible component.
+exports.ORDERED_PROPS = [
+  "name",
+  "role",
+  "actions",
+  "value",
+  "DOMNode",
+  "description",
+  "help",
+  "keyboardShortcut",
+  "childCount",
+  "indexInParent",
+  "states",
+  "attributes"
+];
+
+// Accessible events (emitted by accessible front) that the accessible component
+// listens to for a current accessible.
+exports.ACCESSIBLE_EVENTS = [
+  "actions-change",
+  "attributes-change",
+  "description-change",
+  "help-change",
+  "name-change",
+  "reorder",
+  "shortcut-change",
+  "states-change",
+  "text-change",
+  "value-change",
+  "index-in-parent-change"
+];
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/main.js
@@ -0,0 +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/. */
+"use strict";
+
+const { utils: Cu } = Components;
+const { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
+
+// Module Loader
+const require = BrowserLoader({
+  baseURI: "resource://devtools/client/accessibility/",
+  window
+}).require;
+
+// Load accessibility panel content
+require("./accessibility-view.js");
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/moz.build
@@ -0,0 +1,24 @@
+# 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/.
+
+BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
+
+DIRS += [
+    'actions',
+    'components',
+    'reducers',
+    'utils'
+]
+
+DevToolsModules(
+    'accessibility-panel.js',
+    'accessibility-view.js',
+    'accessibility.css',
+    'constants.js',
+    'picker.js',
+    'provider.js',
+)
+
+with Files('**'):
+    BUG_COMPONENT = ('Firefox', 'Developer Tools: Accessibility')
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/picker.js
@@ -0,0 +1,184 @@
+/* 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 { L10N } = require("./utils/l10n");
+
+class Picker {
+  constructor(panel) {
+    this._panel = panel;
+
+    this.isPicking = false;
+
+    this.onPickerAccessibleHovered = this.onPickerAccessibleHovered.bind(this);
+    this.onPickerAccessiblePicked = this.onPickerAccessiblePicked.bind(this);
+    this.onPickerAccessiblePreviewed = this.onPickerAccessiblePreviewed.bind(this);
+    this.onPickerAccessibleCanceled = this.onPickerAccessibleCanceled.bind(this);
+  }
+
+  get toolbox() {
+    return this._panel._toolbox;
+  }
+
+  get walker() {
+    return this._panel._walker;
+  }
+
+  get pickerButton() {
+    return this.toolbox.pickerButton;
+  }
+
+  release() {
+    this._panel = null;
+  }
+
+  emit(event, data) {
+    this.toolbox.emit(event, data);
+  }
+
+  /**
+   * Select accessible object in the tree.
+   * @param  {Object} accessible
+   *         Accessiblle object to be selected in the inspector tree.
+   */
+  select(accessible) {
+    this._panel.selectAccessible(accessible);
+  }
+
+  /**
+   * Highlight accessible object in the tree.
+   * @param  {Object} accessible
+   *         Accessiblle object to be selected in the inspector tree.
+   */
+  highlight(accessible) {
+    this._panel.highlightAccessible(accessible);
+  }
+
+  getStr(key) {
+    return L10N.getStr(key);
+  }
+
+  /**
+   * Override the default presentation of the picker button in toolbox's top
+   * level toolbar.
+   */
+  updateButton() {
+    this.pickerButton.description = this.getStr("accessibility.pick");
+    this.pickerButton.className = "accessibility";
+    this.pickerButton.disabled = !this._panel._front.enabled;
+    if (!this._panel._front.enabled && this.isPicking) {
+      this.cancel();
+    }
+  }
+
+  /**
+   * Handle an event when a new accessible object is hovered over.
+   * @param  {Object} accessible
+   *         newly hovered accessible object
+   */
+  onPickerAccessibleHovered(accessible) {
+    if (accessible) {
+      this.emit("picker-accessible-hovered", accessible);
+      this.highlight(accessible);
+    }
+  }
+
+  /**
+   * Handle an event when a new accessible is picked by the user.
+   * @param  {Object} accessible
+   *         newly picked accessible object
+   */
+  onPickerAccessiblePicked(accessible) {
+    if (accessible) {
+      this.select(accessible);
+    }
+    this.stop();
+  }
+
+  /**
+   * Handle an event when a new accessible is previewed by the user.
+   * @param  {Object} accessible
+   *         newly previewed accessible object
+   */
+  onPickerAccessiblePreviewed(accessible) {
+    if (accessible) {
+      this.select(accessible);
+    }
+  }
+
+  /**
+   * Handle an event when picking is cancelled by the user.s
+   */
+  onPickerAccessibleCanceled() {
+    this.cancel();
+    this.emit("picker-accessible-canceled");
+  }
+
+  /**
+   * Cancel picking.
+   */
+  async cancel() {
+    await this.stop();
+  }
+
+  /**
+   * Stop picking and remove all walker listeners.
+   */
+  async stop() {
+    if (!this.isPicking) {
+      return;
+    }
+
+    this.isPicking = false;
+
+    this.pickerButton.isChecked = false;
+
+    await this.walker.cancelPick();
+    this.walker.off("picker-accessible-hovered", this.onPickerAccessibleHovered);
+    this.walker.off("picker-accessible-picked", this.onPickerAccessiblePicked);
+    this.walker.off("picker-accessible-previewed", this.onPickerAccessiblePreviewed);
+    this.walker.off("picker-accessible-canceled", this.onPickerAccessibleCanceled);
+
+    this.emit("picker-stopped");
+  }
+
+  /**
+   * Start picking and add walker listeners.
+   * @param  {Boolean} doFocus
+   *         If true, move keyboard focus into content.
+   */
+  async start(doFocus = true) {
+    if (this.isPicking) {
+      return;
+    }
+
+    this.isPicking = true;
+
+    this.pickerButton.isChecked = true;
+
+    this.walker.on("picker-accessible-hovered", this.onPickerAccessibleHovered);
+    this.walker.on("picker-accessible-picked", this.onPickerAccessiblePicked);
+    this.walker.on("picker-accessible-previewed", this.onPickerAccessiblePreviewed);
+    this.walker.on("picker-accessible-canceled", this.onPickerAccessibleCanceled);
+
+    await this.walker.pick(doFocus);
+    this.emit("picker-started");
+  }
+
+  /**
+   * Toggle between starting and canceling the picker.
+   * @param  {Boolean} doFocus
+   *         If true, move keyboard focus into content.
+   */
+  toggle(doFocus) {
+    if (this.isPicking) {
+      return this.cancel();
+    }
+
+    return this.start(doFocus);
+  }
+}
+
+exports.Picker = Picker;
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/provider.js
@@ -0,0 +1,92 @@
+/* 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 { fetchChildren } = require("./actions/accessibles");
+
+/**
+ * Data provider that is responsible for mapping of an accessibles cache to the
+ * data format that is supported by the TreeView component.
+ * @param {Map}      accessibles accessibles object cache
+ * @param {Function} dispatch    react dispatch function that triggers a redux
+ *                               action.
+ */
+
+class Provider {
+  constructor(accessibles, dispatch) {
+    this.accessibles = accessibles;
+    this.dispatch = dispatch;
+  }
+
+  /**
+   * Get accessible's cached children if available, if not fetch them from
+   * backend.
+   * @param {Object}  accessible accessible object whose children to get.
+   * @returns {Array} arraof of accessible children.
+   */
+  getChildren(accessible) {
+    if (!accessible || !accessible.actor || accessible.childCount === 0) {
+      return [];
+    }
+
+    let obj = this.accessibles.get(accessible.actorID);
+    if (!obj || !obj.children) {
+      return this.dispatch(fetchChildren(accessible));
+    }
+
+    return obj.children;
+  }
+
+  /**
+   * Return a flag indicating if an accessible object has any children.
+   * @param {Object}    accessible accessible object whose children to get.
+   * @returns {Boolean} idicator of whether accessible object has children.
+   */
+  hasChildren(accessible) {
+    return accessible.childCount > 0;
+  }
+
+  /**
+   * Get a value for an accessible object. Used to render the second (value)
+   * column of the accessible tree. Corresponds to an accesible object name, if
+   * available.
+   * @param {Object}   accessible accessible object
+   * @returns {String} accessible object value.
+   */
+  getValue(accessible) {
+    return accessible.name || "";
+  }
+
+  /**
+   * Get a label for an accessible object. Used to render the first column of
+   * the accessible tree. Corresponds to an accessible object role.
+   * @param {Object}   accessible accessible object
+   * @returns {String} accessible object label.
+   */
+  getLabel(accessible) {
+    return accessible.role;
+  }
+
+  /**
+   * Get a unique key for an accessible object. Corresponds to an accessible
+   * front's actorID.
+   * @param {Object}   accessible accessible object
+   * @returns {String} a key for an accessible object.
+   */
+  getKey(accessible) {
+    return accessible.actorID;
+  }
+
+  /**
+   * Get a type of an accesible object. Corresponds to the type of an accessible
+   * front.
+   * @param {Object}   accessible accessible object
+   * @returns {String} accessible object type
+   */
+  getType(accessible) {
+    return accessible.typeName;
+  }
+}
+
+exports.Provider = Provider;
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/reducers/accessibles.js
@@ -0,0 +1,124 @@
+/* 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 { FETCH_CHILDREN, RESET, SELECT, HIGHLIGHT } = require("../constants");
+
+/**
+ * Initial state definition
+ */
+function getInitialState() {
+  return new Map();
+}
+
+/**
+ * Maintain a cache of received accessibles responses from the backend.
+ */
+function accessibles(state = getInitialState(), action) {
+  switch (action.type) {
+    case FETCH_CHILDREN:
+      return onReceiveChildren(state, action);
+    case HIGHLIGHT:
+    case SELECT:
+      return onReceiveAncestry(state, action);
+    case RESET:
+      return getInitialState();
+    default:
+      return state;
+  }
+}
+
+/**
+ * If accessible is cached recursively remove all its children and remove itself
+ * from cache.
+ * @param {Map}    cache      Previous state maintaining a cache of previously
+ *                            fetched accessibles.
+ * @param {Object} accessible Accessible object to remove from cache.
+ */
+function cleanupChild(cache, accessible) {
+  let cached = cache.get(accessible.actorID);
+  if (!cached) {
+    return;
+  }
+
+  for (let child of cached.children) {
+    cleanupChild(cache, child);
+  }
+
+  cache.delete(accessible.actorID);
+}
+
+/**
+ * Determine if accessible in cache is stale. Accessible object is stale if its
+ * cached children array has the size other than the value of its childCount
+ * property that updates on accessible actor event.
+ * @param {Map}    cache      Previous state maintaining a cache of previously
+ *                             fetched accessibles.
+ * @param {Object} accessible Accessible object to test for staleness.
+ */
+function staleChildren(cache, accessible) {
+  let cached = cache.get(accessible.actorID);
+  if (!cached) {
+    return false;
+  }
+
+  return cached.children.length !== accessible.childCount;
+}
+
+function updateChildrenCache(cache, accessible, children) {
+  let { actorID } = accessible;
+
+  if (cache.has(actorID)) {
+    let cached = cache.get(actorID);
+    for (let child of cached.children) {
+      // If exhisting children cache includes an accessible that is not present
+      // any more or if child accessible is stale remove it and all its children
+      // from cache.
+      if (!children.includes(child) || staleChildren(cache, child)) {
+        cleanupChild(cache, child);
+      }
+    }
+    cached.children = children;
+    cache.set(actorID, cached);
+  } else {
+    cache.set(actorID, { children });
+  }
+
+  return cache;
+}
+
+/**
+ * Handles fetching of accessible children.
+ * @param {Map}     cache  Previous state maintaining a cache of previously
+ *                         fetched accessibles.
+ * @param {Object}  action Redux action object.
+ * @return {Object} updated state
+ */
+function onReceiveChildren(cache, action) {
+  let { accessible, response: children, error } = action;
+
+  if (error) {
+    console.warn("Error fetching children", accessible, error);
+    return cache;
+  }
+
+  return updateChildrenCache(new Map(cache), accessible, children);
+}
+
+function onReceiveAncestry(cache, action) {
+  let { accessible: acc, response: ancestry, error } = action;
+
+  if (error) {
+    console.warn("Error fetching ancestry", acc, error);
+    return cache;
+  }
+
+  let newCache = new Map(cache);
+  ancestry.forEach(({ accessible, children }) =>
+    updateChildrenCache(newCache, accessible, children));
+
+  return newCache;
+}
+
+exports.accessibles = accessibles;
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/reducers/details.js
@@ -0,0 +1,45 @@
+/* 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 { UPDATE_DETAILS, RESET } = require("../constants");
+
+/**
+ * Initial state definition
+ */
+function getInitialState() {
+  return {};
+}
+
+/**
+ * Maintain details of a current relevant accessible.
+ */
+function details(state = getInitialState(), action) {
+  switch (action.type) {
+    case UPDATE_DETAILS:
+      return onUpdateDetails(state, action);
+    case RESET:
+      return getInitialState();
+    default:
+      return state;
+  }
+}
+
+/**
+ * Handle details update for an accessible object
+ * @param {Object} state  Current accessible object details.
+ * @param {Object} action Redux action object
+ * @return {Object}  updated state
+ */
+function onUpdateDetails(state, action) {
+  let { accessible, response: DOMNode, error } = action;
+  if (error) {
+    console.warn("Error fetching DOMNode for accessible", accessible, error);
+    return state;
+  }
+
+  return { accessible, DOMNode };
+}
+
+exports.details = details;
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/reducers/index.js
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { accessibles } = require("./accessibles");
+const { details } = require("./details");
+const { ui } = require("./ui");
+
+exports.reducers = {
+  accessibles,
+  details,
+  ui
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/reducers/moz.build
@@ -0,0 +1,10 @@
+# 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/.
+
+DevToolsModules(
+    'accessibles.js',
+    'details.js',
+    'index.js',
+    'ui.js'
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/reducers/ui.js
@@ -0,0 +1,168 @@
+/* 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";
+
+/* global gToolbox */
+
+const {
+  ENABLE,
+  DISABLE,
+  RESET,
+  SELECT,
+  HIGHLIGHT,
+  UNHIGHLIGHT,
+  UPDATE_CAN_BE_DISABLED,
+  UPDATE_CAN_BE_ENABLED,
+  UPDATE_DETAILS
+} = require("../constants");
+
+const TreeView = require("devtools/client/shared/components/tree/TreeView");
+
+/**
+ * Initial state definition
+ */
+function getInitialState() {
+  return {
+    enabled: false,
+    canBeDisabled: true,
+    canBeEnabled: true,
+    selected: null,
+    highlighted: null,
+    expanded: new Set()
+  };
+}
+
+/**
+ * Maintain ui components of the accessibility panel.
+ */
+function ui(state = getInitialState(), action) {
+  switch (action.type) {
+    case ENABLE:
+      return onToggle(state, action, true);
+    case DISABLE:
+      return onToggle(state, action, false);
+    case UPDATE_CAN_BE_DISABLED:
+      return onCanBeDisabledChange(state, action);
+    case UPDATE_CAN_BE_ENABLED:
+      return onCanBeEnabledChange(state, action);
+    case UPDATE_DETAILS:
+      return onUpdateDetails(state, action);
+    case HIGHLIGHT:
+      return onHighlight(state, action);
+    case UNHIGHLIGHT:
+      return onUnhighlight(state, action);
+    case SELECT:
+      return onSelect(state, action);
+    case RESET:
+      return onReset(state, action);
+    default:
+      return state;
+  }
+}
+
+function onUpdateDetails(state) {
+  if (!state.selected) {
+    return state;
+  }
+
+  // Clear selected state that should only be set when select action is
+  // performed.
+  return Object.assign({}, state, { selected: null });
+}
+
+function onUnhighlight(state) {
+  return Object.assign({}, state, { highlighted: null });
+}
+
+function updateExpandedNodes(state, ancestry) {
+  let expanded = new Set(state.expanded);
+  let path = ancestry.reduceRight((accPath, { accessible }) => {
+    accPath = TreeView.subPath(accPath, accessible.actorID);
+    expanded.add(accPath);
+    return accPath;
+  }, "");
+
+  return { path, expanded };
+}
+
+function onHighlight(state, { accessible, response: ancestry, error }) {
+  if (error) {
+    console.warn("Error fetching ancestry", accessible, error);
+    return state;
+  }
+
+  let { expanded } = updateExpandedNodes(state, ancestry);
+  return Object.assign({}, state, { expanded, highlighted: accessible });
+}
+
+function onSelect(state, { accessible, response: ancestry, error }) {
+  if (error) {
+    console.warn("Error fetching ancestry", accessible, error);
+    return state;
+  }
+
+  let { path, expanded } = updateExpandedNodes(state, ancestry);
+  let selected = TreeView.subPath(path, accessible.actorID);
+
+  return Object.assign({}, state, { expanded, selected });
+}
+
+/**
+ * Handle "canBeDisabled" flag update for accessibility service
+ * @param  {Object}  state   Current ui state
+ * @param  {Object}  action  Redux action object
+ * @return {Object}  updated state
+ */
+function onCanBeDisabledChange(state, { canBeDisabled }) {
+  return Object.assign({}, state, { canBeDisabled });
+}
+
+/**
+ * Handle "canBeEnabled" flag update for accessibility service
+ * @param  {Object}  state   Current ui state.
+ * @param  {Object}  action  Redux action object
+ * @return {Object}  updated state
+ */
+function onCanBeEnabledChange(state, { canBeEnabled }) {
+  return Object.assign({}, state, { canBeEnabled });
+}
+
+/**
+ * Handle reset action for the accessibility panel UI.
+ * @param  {Object}  state   Current ui state.
+ * @param  {Object}  action  Redux action object
+ * @return {Object}  updated state
+ */
+function onReset(state, { accessibility }) {
+  let { enabled, canBeDisabled, canBeEnabled } = accessibility;
+  toggleHighlightTool(enabled);
+  return Object.assign({}, state, { enabled, canBeDisabled, canBeEnabled });
+}
+
+/**
+ * Handle accessibilty service enabling/disabling.
+ * @param {Object}  state   Current accessibility services enabled state.
+ * @param {Object}  action  Redux action object
+ * @param {Boolean} enabled New enabled state.
+ * @return {Object}  updated state
+ */
+function onToggle(state, { error }, enabled) {
+  if (error) {
+    console.warn("Error enabling accessibility service: ", error);
+    return state;
+  }
+
+  toggleHighlightTool(enabled);
+  return Object.assign({}, state, { enabled });
+}
+
+function toggleHighlightTool(enabled) {
+  if (enabled) {
+    gToolbox.highlightTool("accessibility");
+  } else {
+    gToolbox.unhighlightTool("accessibility");
+  }
+}
+
+exports.ui = ui;
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/test/.eslintrc.js
@@ -0,0 +1,6 @@
+"use strict";
+
+module.exports = {
+  // Extend from the shared list of defined globals for mochitests.
+  "extends": "../../../.eslintrc.mochitests.js"
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/test/browser.ini
@@ -0,0 +1,14 @@
+[DEFAULT]
+tags = devtools
+subsuite = devtools
+support-files =
+  head.js
+  !/devtools/client/shared/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
+  !/devtools/client/shared/test/shared-redux-head.js
+
+[browser_accessibility_mutations.js]
+[browser_accessibility_reload.js]
+[browser_accessibility_sidebar.js]
+[browser_accessibility_tree.js]
+[browser_accessibility_tree_nagivation.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/test/browser_accessibility_mutations.js
@@ -0,0 +1,118 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = `<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Accessibility Panel Test</title>
+  </head>
+  <body>
+    <h1 id="h1">Top level header</h1>
+    <p id="p">This is a paragraph.</p>
+  </body>
+</html>`;
+
+/**
+ * Test data has the format of:
+ * {
+ *   desc     {String}    description for better logging
+ *   action   {Function}  An optional action that needs to be performed before
+ *                        the state of the tree and the sidebar can be checked.
+ *   expected {JSON}      An expected states for the tree and the sidebar.
+ * }
+ */
+const tests = [{
+  desc: "Expand first and second rows, select third row.",
+  action: async ({ doc }) => {
+    await toggleRow(doc, 0);
+    await toggleRow(doc, 1);
+    selectRow(doc, 3);
+  },
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }, {
+      role: "heading",
+      name: `"Top level header"`
+    }, {
+      role: "text leaf",
+      name: `"Top level header"`
+    }, {
+      role: "paragraph",
+      name: `""`
+    }],
+    sidebar: {
+      name: null,
+      role: "paragraph",
+      actions: [],
+      value: "",
+      description: "",
+      help: "",
+      keyboardShortcut: "",
+      childCount: 1,
+      indexInParent: 1,
+      states: ["selectable text", "opaque", "enabled", "sensitive"]
+    }
+  }
+}, {
+  desc: "Remove a child from a document.",
+  action: async ({ doc, browser }) => {
+    is(doc.querySelectorAll(".treeRow").length, 4, "Tree size is correct.");
+    await ContentTask.spawn(browser, {}, () =>
+      content.document.getElementById("p").remove());
+    await BrowserTestUtils.waitForCondition(() =>
+      doc.querySelectorAll(".treeRow").length === 3, "Tree updated.");
+  },
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }, {
+      role: "heading",
+      name: `"Top level header"`
+    }, {
+      role: "text leaf",
+      name: `"Top level header"`
+    }],
+    sidebar: {
+      name: "Top level header",
+      role: "text leaf"
+    }
+  }
+}, {
+  desc: "Update child's text content.",
+  action: async ({ browser }) => {
+    await ContentTask.spawn(browser, {}, () => {
+      content.document.getElementById("h1").textContent = "New Header";
+    });
+  },
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }, {
+      role: "heading",
+      name: `"New Header"`
+    }, {
+      role: "text leaf",
+      name: `"New Header"`
+    }]
+  }
+}, {
+  desc: "Select third row in the tree.",
+  action: ({ doc }) => selectRow(doc, 1),
+  expected: {
+    sidebar: {
+      name: "New Header"
+    }
+  }
+}];
+
+/**
+ * Test that checks the Accessibility panel after DOM tree mutations.
+ */
+addA11yPanelTestsTask(tests, TEST_URI,
+  "Test Accessibility panel after DOM tree mutations.");
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/test/browser_accessibility_reload.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI_1 = `<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Accessibility Panel Test</title>
+  </head>
+  <body>
+    <h1>Top level header</h1>
+    <p>This is a paragraph.</p>
+  </body>
+</html>`;
+
+const TEST_URI_2 = `<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Navigation Accessibility Panel</title>
+  </head>
+  <body></body>
+</html>`;
+
+/**
+ * Test data has the format of:
+ * {
+ *   desc     {String}    description for better logging
+ *   action   {Function}  An optional action that needs to be performed before
+ *                        the state of the tree and the sidebar can be checked.
+ *   expected {JSON}      An expected states for the tree and the sidebar.
+ * }
+ */
+const tests = [{
+  desc: "Test the initial accessibility tree state after first row is expanded.",
+  action: async ({ doc }) => toggleRow(doc, 0),
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }, {
+      role: "heading",
+      name: `"Top level header"`
+    }, {
+      role: "paragraph",
+      name: `""`
+    }],
+    sidebar: {
+      name: "Accessibility Panel Test",
+      role: "document"
+    }
+  }
+}, {
+  desc: "Reload the page.",
+  action: async ({ panel }) => reload(panel.target),
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }],
+    sidebar: {
+      name: "Accessibility Panel Test",
+      role: "document"
+    }
+  }
+}, {
+  desc: "Navigate to a new page.",
+  action: async ({ panel }) => navigate(panel.target, buildURL(TEST_URI_2)),
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Navigation Accessibility Panel"`
+    }],
+    sidebar: {
+      name: "Navigation Accessibility Panel",
+      role: "document"
+    }
+  }
+}];
+
+/**
+ * Simple test that checks content of the Accessibility panel tree on reload.
+ */
+addA11yPanelTestsTask(tests, TEST_URI_1, "Test Accessibility panel tree on reload.");
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/test/browser_accessibility_sidebar.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = `<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Accessibility Panel Test</title>
+  </head>
+  <body></body>
+</html>`;
+
+/**
+ * Test data has the format of:
+ * {
+ *   desc     {String}    description for better logging
+ *   action   {Function}  An optional action that needs to be performed before
+ *                        the state of the tree and the sidebar can be checked.
+ *   expected {JSON}      An expected states for the tree and the sidebar.
+ * }
+ */
+const tests = [{
+  desc: "Test the initial accessibility sidebar state.",
+  expected: {
+    sidebar: {
+      name: "Accessibility Panel Test",
+      role: "document",
+      actions: [],
+      value: "",
+      description: "",
+      help: "",
+      keyboardShortcut: "",
+      childCount: 0,
+      indexInParent: 0,
+      states: ["readonly", "focusable", "opaque", "enabled", "sensitive"]
+    }
+  }
+}, {
+  desc: "Mark document as disabled for accessibility.",
+  action: async ({ browser }) => ContentTask.spawn(browser, {}, () =>
+    content.document.body.setAttribute("aria-disabled", true)),
+  expected: {
+    sidebar: {
+      states: ["unavailable", "readonly", "focusable", "opaque"]
+    }
+  }
+}, {
+  desc: "Append a new child to the document.",
+  action: async ({ browser }) => ContentTask.spawn(browser, {}, () => {
+    let doc = content.document;
+    let button = doc.createElement("button");
+    button.textContent = "Press Me!";
+    doc.body.appendChild(button);
+  }),
+  expected: {
+    sidebar: {
+      childCount: 1
+    }
+  }
+}];
+
+/**
+ * Test that checks the Accessibility panel sidebar.
+ */
+addA11yPanelTestsTask(tests, TEST_URI, "Test Accessibility panel sidebar.");
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/test/browser_accessibility_tree.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = `<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Accessibility Panel Test</title>
+  </head>
+  <body>
+    <h1>Top level header</h1>
+    <p>This is a paragraph.</p>
+  </body>
+</html>`;
+
+/**
+ * Test data has the format of:
+ * {
+ *   desc     {String}    description for better logging
+ *   action   {Function}  An optional action that needs to be performed before
+ *                        the state of the tree and the sidebar can be checked.
+ *   expected {JSON}      An expected states for the tree and the sidebar.
+ * }
+ */
+const tests = [{
+  desc: "Test the initial accessibility tree state.",
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }]
+  }
+}, {
+  desc: "Expand first tree node.",
+  action: async ({ doc }) => toggleRow(doc, 0),
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }, {
+      role: "heading",
+      name: `"Top level header"`
+    }, {
+      role: "paragraph",
+      name: `""`
+    }]
+  }
+}, {
+  desc: "Collapse first tree node.",
+  action: async ({ doc }) => toggleRow(doc, 0),
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }]
+  }
+}];
+
+/**
+ * Simple test that checks content of the Accessibility panel tree.
+ */
+addA11yPanelTestsTask(tests, TEST_URI, "Test Accessibility panel tree.");
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/test/browser_accessibility_tree_nagivation.js
@@ -0,0 +1,152 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = `<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Accessibility Panel Test</title>
+  </head>
+  <body>
+    <h1>Top level header</h1>
+    <p>This is a paragraph.</p>
+  </body>
+</html>`;
+
+/**
+ * Test data has the format of:
+ * {
+ *   desc     {String}    description for better logging
+ *   action   {Function}  An optional action that needs to be performed before
+ *                        the state of the tree and the sidebar can be checked.
+ *   expected {JSON}      An expected states for the tree and the sidebar.
+ * }
+ */
+const tests = [{
+  desc: "Test the initial accessibility tree and sidebar states.",
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }],
+    sidebar: {
+      name: "Accessibility Panel Test",
+      role: "document",
+      actions: [],
+      value: "",
+      description: "",
+      help: "",
+      keyboardShortcut: "",
+      childCount: 2,
+      indexInParent: 0,
+      states: ["readonly", "focusable", "opaque", "enabled", "sensitive"]
+    }
+  }
+}, {
+  desc: "Expand first tree node.",
+  action: async ({ doc }) => toggleRow(doc, 0),
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }, {
+      role: "heading",
+      name: `"Top level header"`
+    }, {
+      role: "paragraph",
+      name: `""`
+    } ]
+  }
+}, {
+  desc: "Expand second tree node.",
+  action: async ({ doc }) => toggleRow(doc, 1),
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }, {
+      role: "heading",
+      name: `"Top level header"`
+    }, {
+      role: "text leaf",
+      name: `"Top level header"`
+    }, {
+      role: "paragraph",
+      name: `""`
+    }],
+    sidebar: {
+      name: "Top level header",
+      role: "heading",
+      actions: [],
+      value: "",
+      description: "",
+      help: "",
+      keyboardShortcut: "",
+      childCount: 1,
+      indexInParent: 0,
+      states: ["selectable text", "opaque", "enabled", "sensitive"]
+    }
+  }
+}, {
+  desc: "Select third tree node.",
+  action: ({ doc }) => selectRow(doc, 2),
+  expected: {
+    sidebar: {
+      name: "Top level header",
+      role: "text leaf",
+      actions: [],
+      value: "",
+      description: "",
+      help: "",
+      keyboardShortcut: "",
+      childCount: 0,
+      indexInParent: 0,
+      states: ["opaque", "enabled", "sensitive"]
+    }
+  }
+}, {
+  desc: "Collapse first tree node.",
+  action: async ({ doc }) => toggleRow(doc, 0),
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }],
+    sidebar: {
+      name: "Accessibility Panel Test",
+      role: "document",
+      actions: [],
+      value: "",
+      description: "",
+      help: "",
+      keyboardShortcut: "",
+      childCount: 2,
+      indexInParent: 0,
+      states: ["readonly", "focusable", "opaque", "enabled", "sensitive"]
+    }
+  }
+}, {
+  desc: "Expand first tree node again.",
+  action: async ({ doc }) => toggleRow(doc, 0),
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`
+    }, {
+      role: "heading",
+      name: `"Top level header"`
+    }, {
+      role: "text leaf",
+      name: `"Top level header"`
+    }, {
+      role: "paragraph",
+      name: `""`
+    }]
+  }
+}];
+
+/**
+ * Check navigation within the tree.
+ */
+addA11yPanelTestsTask(tests, TEST_URI, "Test Accessibility panel tree navigation.");
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/test/head.js
@@ -0,0 +1,294 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* import-globals-from ../../shared/test/shared-head.js */
+/* import-globals-from ../../inspector/test/shared-head.js */
+
+/* global waitUntilState, gBrowser */
+/* exported addTestTab, checkTreeState, checkSidebarState, selectRow,
+            toggleRow, addA11yPanelTestsTask, reload, navigate */
+
+"use strict";
+
+// Import framework's shared head.
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
+  this);
+
+// Import inspector's shared head.
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
+  this);
+
+// Load the shared Redux helpers into this compartment.
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/shared-redux-head.js",
+  this);
+
+const { ORDERED_PROPS } = require("devtools/client/accessibility/constants");
+
+// Enable the Accessibility panel
+Services.prefs.setBoolPref("devtools.accessibility.enabled", true);
+
+/**
+ * Wait for accessibility service to shut down. We consider it shut down when
+ * an "a11y-init-or-shutdown" event is received with a value of "0".
+ */
+function shutdownA11y() {
+  if (!Services.appinfo.accessibilityEnabled) {
+    return Promise.resolve();
+  }
+
+  // Force collections to speed up accessibility service shutdown.
+  Cu.forceGC();
+  Cu.forceCC();
+  Cu.forceShrinkingGC();
+
+  return new Promise(resolve => {
+    let observe = (subject, topic, data) => {
+      if (data === "0") {
+        Services.obs.removeObserver(observe, "a11y-init-or-shutdown");
+        resolve();
+      }
+    };
+    // This event is coming from Gecko accessibility module when the
+    // accessibility service is shutdown or initialzied. We attempt to shutdown
+    // accessibility service naturally if there are no more XPCOM references to
+    // a11y related objects (after GC/CC).
+    Services.obs.addObserver(observe, "a11y-init-or-shutdown");
+  });
+}
+
+registerCleanupFunction(async () => {
+  info("Cleaning up...");
+  await shutdownA11y();
+  Services.prefs.clearUserPref("devtools.accessibility.enabled");
+});
+
+const EXPANDABLE_PROPS = ["actions", "states", "attributes"];
+
+/**
+ * Add a new test tab in the browser and load the given url.
+ * @param {String} url
+ *        The url to be loaded in the new tab
+ * @return a promise that resolves to the tab object when
+ *        the url is loaded
+ */
+async function addTestTab(url) {
+  info("Adding a new test tab with URL: '" + url + "'");
+
+  let tab = await addTab(url);
+  let panel = await initAccessibilityPanel(tab);
+  let win = panel.panelWin;
+  let doc = win.document;
+  let store = win.view.store;
+
+  EventUtils.sendMouseEvent({ type: "click" },
+    doc.getElementById("accessibility-enable-button"), win);
+
+  await waitUntilState(store, state =>
+    state.accessibles.size === 1 && state.details.accessible &&
+    state.details.accessible.role === "document");
+
+  // Wait for inspector load here to avoid protocol errors on shutdown, since
+  // accessibility panel test can be too fast.
+  await win.gToolbox.loadTool("inspector");
+
+  return {
+    tab,
+    browser: tab.linkedBrowser,
+    panel,
+    win,
+    doc,
+    store
+  };
+}
+
+/**
+ * Turn off accessibility features from within the panel. We call it before the
+ * cleanup function to make sure that the panel is still present.
+ */
+function disableAccessibilityInspector(env) {
+  let { doc, win } = env;
+  EventUtils.sendMouseEvent({ type: "click" },
+    doc.getElementById("accessibility-disable-button"), win);
+}
+
+/**
+ * Open the Accessibility panel for the given tab.
+ *
+ * @param {nsIDOMElement} tab
+ *        Optional tab element for which you want open the Accessibility panel.
+ *        The default tab is taken from the global variable |tab|.
+ * @return a promise that is resolved once the panel is open.
+ */
+async function initAccessibilityPanel(tab = gBrowser.selectedTab) {
+  let target = TargetFactory.forTab(tab);
+  let toolbox = await gDevTools.showToolbox(target, "accessibility");
+  return toolbox.getCurrentPanel();
+}
+
+/**
+ * Check the state of the accessibility tree.
+ * @param  {document} doc       panel documnent.
+ * @param  {Array}    expected  an array that represents an expected row list.
+ */
+async function checkTreeState(doc, expected) {
+  info("Checking tree state.");
+  let hasExpectedStructure = await BrowserTestUtils.waitForCondition(() =>
+    [...doc.querySelectorAll(".treeRow")].every((row, i) =>
+      row.querySelector(".treeLabelCell").textContent === expected[i].role &&
+      row.querySelector(".treeValueCell").textContent === expected[i].name),
+    "Wait for the right tree update.");
+
+  ok(hasExpectedStructure, "Tree structure is correct.");
+}
+
+/**
+ * Check the state of the accessibility sidebar.
+ * @param  {Object} store         React store for the panel (includes store for
+ *                                the sidebar).
+ * @param  {Object} expectedState Expected state of the sidebar.
+ */
+async function checkSidebarState(store, expectedState) {
+  info("Checking sidebar state.");
+  await waitUntilState(store, ({ details }) => {
+    for (let key of ORDERED_PROPS) {
+      let expected = expectedState[key];
+      if (expected === undefined) {
+        continue;
+      }
+
+      if (EXPANDABLE_PROPS.includes(key)) {
+        if (JSON.stringify(details.accessible[key]) !== JSON.stringify(expected)) {
+          return false;
+        }
+      } else if (details.accessible && details.accessible[key] !== expected) {
+        return false;
+      }
+    }
+
+    ok(true, "Sidebar state is correct.");
+    return true;
+  });
+}
+
+/**
+ * Select tree row.
+ * @param  {document} doc       panel documnent.
+ * @param  {Number}   rowNumber number of the row/tree node to be selected.
+ */
+function selectRow(doc, rowNumber) {
+  info(`Selecting row ${rowNumber}.`);
+  EventUtils.sendMouseEvent({ type: "click" },
+    doc.querySelectorAll(".treeRow")[rowNumber], doc.defaultView);
+}
+
+/**
+ * Toggle an expandable tree row.
+ * @param  {document} doc       panel documnent.
+ * @param  {Number}   rowNumber number of the row/tree node to be toggled.
+ */
+async function toggleRow(doc, rowNumber) {
+  let win = doc.defaultView;
+  let twisty = doc.querySelectorAll(".theme-twisty")[rowNumber];
+  let expected = !twisty.classList.contains("open");
+
+  info(`${expected ? "Expanding" : "Collapsing"} row ${rowNumber}.`);
+
+  EventUtils.sendMouseEvent({ type: "click" }, twisty, win);
+  await BrowserTestUtils.waitForCondition(() =>
+    !twisty.classList.contains("devtools-throbber") &&
+    expected === twisty.classList.contains("open"), "Twisty updated.");
+}
+
+/**
+ * Iterate over actions/tests structure and test the state of the
+ * accessibility panel.
+ * @param  {JSON}   tests test data that has the format of:
+ *                    {
+ *                      desc     {String}    description for better logging
+ *                      action   {Function}  An optional action that needs to be
+ *                                           performed before the state of the
+ *                                           tree and the sidebar can be checked
+ *                      expected {JSON}      An expected states for the tree and
+ *                                           the sidebar
+ *                    }
+ * @param  {Object} env  contains all relevant environment objects (same
+ *                       structure as the return value of 'addTestTab' funciton)
+ */
+async function runA11yPanelTests(tests, env) {
+  for (let { desc, action, expected } of tests) {
+    info(desc);
+
+    if (action) {
+      await action(env);
+    }
+
+    let { tree, sidebar } = expected;
+    if (tree) {
+      await checkTreeState(env.doc, tree);
+    }
+
+    if (sidebar) {
+      await checkSidebarState(env.store, sidebar);
+    }
+  }
+}
+
+/**
+ * Build a valid URL from an HTML snippet.
+ * @param  {String} uri HTML snippet
+ * @return {String}     built URL
+ */
+function buildURL(uri) {
+  return `data:text/html,${encodeURIComponent(uri)}`;
+}
+
+/**
+ * Add a test task based on the test structure and a test URL.
+ * @param  {JSON}   tests  test data that has the format of:
+ *                    {
+ *                      desc     {String}    description for better logging
+ *                      action   {Function}  An optional action that needs to be
+ *                                           performed before the state of the
+ *                                           tree and the sidebar can be checked
+ *                      expected {JSON}      An expected states for the tree and
+ *                                           the sidebar
+ *                    }
+ * @param {String}  uri    test URL
+ * @param {String}  msg    a message that is printed for the test
+ */
+function addA11yPanelTestsTask(tests, uri, msg) {
+  tests.push({
+    desc: "Disable accessibility inspector.",
+    action: env => disableAccessibilityInspector(env),
+    expected: {}
+  });
+  add_task(async function a11yPanelTests() {
+    info(msg);
+    let env = await addTestTab(buildURL(uri));
+    await runA11yPanelTests(tests, env);
+  });
+}
+
+/**
+ * Reload panel target.
+ * @param  {Object} target             Panel target.
+ * @param  {String} waitForTargetEvent Event to wait for after reload.
+ */
+function reload(target, waitForTargetEvent = "navigate") {
+  executeSoon(() => target.activeTab.reload());
+  return once(target, waitForTargetEvent);
+}
+
+/**
+ * Navigate to a new URL within the panel target.
+ * @param  {Object} target             Panel target.
+ * @param  {Srting} url                URL to navigate to.
+ * @param  {String} waitForTargetEvent Event to wait for after reload.
+ */
+function navigate(target, url, waitForTargetEvent = "navigate") {
+  executeSoon(() => target.activeTab.navigateTo(url));
+  return once(target, waitForTargetEvent);
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/utils/l10n.js
@@ -0,0 +1,11 @@
+/* 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 { LocalizationHelper } = require("devtools/shared/l10n");
+
+const A11Y_STRINGS_URI = "devtools/client/locales/accessibility.properties";
+
+exports.L10N = new LocalizationHelper(A11Y_STRINGS_URI);
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/utils/moz.build
@@ -0,0 +1,7 @@
+# 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/.
+
+DevToolsModules(
+    'l10n.js'
+)
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -19,16 +19,17 @@ loader.lazyGetter(this, "CanvasDebuggerP
 loader.lazyGetter(this, "WebAudioEditorPanel", () => require("devtools/client/webaudioeditor/panel").WebAudioEditorPanel);
 loader.lazyGetter(this, "MemoryPanel", () => require("devtools/client/memory/panel").MemoryPanel);
 loader.lazyGetter(this, "PerformancePanel", () => require("devtools/client/performance/panel").PerformancePanel);
 loader.lazyGetter(this, "NewPerformancePanel", () => require("devtools/client/performance-new/panel").PerformancePanel);
 loader.lazyGetter(this, "NetMonitorPanel", () => require("devtools/client/netmonitor/panel").NetMonitorPanel);
 loader.lazyGetter(this, "StoragePanel", () => require("devtools/client/storage/panel").StoragePanel);
 loader.lazyGetter(this, "ScratchpadPanel", () => require("devtools/client/scratchpad/scratchpad-panel").ScratchpadPanel);
 loader.lazyGetter(this, "DomPanel", () => require("devtools/client/dom/dom-panel").DomPanel);
+loader.lazyGetter(this, "AccessibilityPanel", () => require("devtools/client/accessibility/accessibility-panel").AccessibilityPanel);
 
 // Other dependencies
 loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true);
 loader.lazyRequireGetter(this, "CommandState", "devtools/shared/gcli/command-state", true);
 loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);
 loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 
 const {MultiLocalizationHelper} = require("devtools/shared/l10n");
@@ -433,31 +434,58 @@ Tools.dom = {
     return target.getTrait("webConsoleCommands");
   },
 
   build: function(iframeWindow, toolbox) {
     return new DomPanel(iframeWindow, toolbox);
   }
 };
 
+Tools.accessibility = {
+  id: "accessibility",
+  accesskey: l10n("accessibility.accesskey"),
+  ordinal: 14,
+  modifiers: osString == "Darwin" ? "accel,alt" : "accel,shift",
+  visibilityswitch: "devtools.accessibility.enabled",
+  icon: "chrome://devtools/skin/images/tool-accessibility.svg",
+  url: "chrome://devtools/content/accessibility/accessibility.html",
+  label: l10n("accessibility.label"),
+  panelLabel: l10n("accessibility.panelLabel"),
+  get tooltip() {
+    return l10n("accessibility.tooltip",
+      (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
+      l10n("accessibility.commandkey"));
+  },
+  inMenu: true,
+
+  isTargetSupported(target) {
+    return target.hasActor("accessibility");
+  },
+
+  build(iframeWindow, toolbox) {
+    return new AccessibilityPanel(iframeWindow, toolbox);
+  }
+};
+
 var defaultTools = [
   Tools.options,
   Tools.webConsole,
   Tools.inspector,
   Tools.jsdebugger,
   Tools.styleEditor,
   Tools.shaderEditor,
   Tools.canvasDebugger,
   Tools.webAudioEditor,
   Tools.performance,
   Tools.netMonitor,
   Tools.storage,
   Tools.scratchpad,
   Tools.memory,
   Tools.dom,
+  Tools.accessibility,
 ];
 
 exports.defaultTools = defaultTools;
 
 Tools.darkTheme = {
   id: "dark",
   label: l10n("options.darkTheme.label2"),
   ordinal: 1,
--- a/devtools/client/framework/components/toolbox-controller.js
+++ b/devtools/client/framework/components/toolbox-controller.js
@@ -43,17 +43,16 @@ class ToolboxController extends Componen
     this.setOptionsPanel = this.setOptionsPanel.bind(this);
     this.highlightTool = this.highlightTool.bind(this);
     this.unhighlightTool = this.unhighlightTool.bind(this);
     this.setDockButtonsEnabled = this.setDockButtonsEnabled.bind(this);
     this.setHostTypes = this.setHostTypes.bind(this);
     this.setCanCloseToolbox = this.setCanCloseToolbox.bind(this);
     this.setPanelDefinitions = this.setPanelDefinitions.bind(this);
     this.setToolboxButtons = this.setToolboxButtons.bind(this);
-    this.setCanMinimize = this.setCanMinimize.bind(this);
   }
 
   shouldComponentUpdate() {
     return this.state.canRender;
   }
 
   componentWillUnmount() {
     this.state.toolboxButtons.forEach(button => {
@@ -157,21 +156,14 @@ class ToolboxController extends Componen
     });
     toolboxButtons.forEach(button => {
       button.on("updatechecked", this.state.checkedButtonsUpdated);
     });
 
     this.setState({ toolboxButtons }, this.updateButtonIds);
   }
 
-  setCanMinimize(canMinimize) {
-    /* Bug 1177463 - The minimize button is currently hidden until we agree on
-       the UI for it, and until bug 1173849 is fixed too. */
-
-    // this.setState({ canMinimize });
-  }
-
   render() {
     return ToolboxToolbar(Object.assign({}, this.props, this.state));
   }
 }
 
 module.exports = ToolboxController;
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -76,18 +76,16 @@ skip-if = os == 'win' || debug # Bug 128
 [browser_toolbox_dynamic_registration.js]
 [browser_toolbox_getpanelwhenready.js]
 [browser_toolbox_highlight.js]
 [browser_toolbox_hosts.js]
 [browser_toolbox_hosts_size.js]
 [browser_toolbox_hosts_telemetry.js]
 [browser_toolbox_keyboard_navigation.js]
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
-[browser_toolbox_minimize.js]
-skip-if = true # Bug 1177463 - Temporarily hide the minimize button
 [browser_toolbox_options.js]
 [browser_toolbox_options_disable_buttons.js]
 [browser_toolbox_options_disable_cache-01.js]
 [browser_toolbox_options_disable_cache-02.js]
 [browser_toolbox_options_disable_js.js]
 [browser_toolbox_options_enable_serviceworkers_testing.js]
 [browser_toolbox_raise.js]
 disabled=Bug 962258
deleted file mode 100644
--- a/devtools/client/framework/test/browser_toolbox_minimize.js
+++ /dev/null
@@ -1,106 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// Test that when the toolbox is displayed in a bottom host, that host can be
-// minimized to just the tabbar height, and maximized again.
-// Also test that while minimized, switching to a tool, clicking on the
-// settings, or clicking on the selected tool's tab maximizes the toolbox again.
-// Finally test that the minimize button doesn't exist in other host types.
-
-const URL = "data:text/html;charset=utf8,test page";
-const {Toolbox} = require("devtools/client/framework/toolbox");
-
-add_task(async function () {
-  info("Create a test tab and open the toolbox");
-  let tab = await addTab(URL);
-  let target = TargetFactory.forTab(tab);
-  let toolbox = await gDevTools.showToolbox(target, "webconsole");
-
-  let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
-  ok(button, "The minimize button exists in the default bottom host");
-
-  info("Try to minimize the toolbox");
-  await minimize(toolbox);
-  ok(parseInt(toolbox._host.frame.style.marginBottom, 10) < 0,
-     "The toolbox host has been hidden away with a negative-margin");
-
-  info("Try to maximize again the toolbox");
-  await maximize(toolbox);
-  ok(parseInt(toolbox._host.frame.style.marginBottom, 10) == 0,
-     "The toolbox host is shown again");
-
-  info("Try to minimize again using the keyboard shortcut");
-  await minimizeWithShortcut(toolbox);
-  ok(parseInt(toolbox._host.frame.style.marginBottom, 10) < 0,
-     "The toolbox host has been hidden away with a negative-margin");
-
-  info("Try to maximize again using the keyboard shortcut");
-  await maximizeWithShortcut(toolbox);
-  ok(parseInt(toolbox._host.frame.style.marginBottom, 10) == 0,
-     "The toolbox host is shown again");
-
-  info("Minimize again and switch to another tool");
-  await minimize(toolbox);
-  let onMaximized = toolbox._host.once("maximized");
-  await toolbox.selectTool("inspector");
-  await onMaximized;
-
-  info("Minimize again and click on the tab of the current tool");
-  await minimize(toolbox);
-  onMaximized = toolbox._host.once("maximized");
-  let tabButton = toolbox.doc.querySelector("#toolbox-tab-inspector");
-  EventUtils.synthesizeMouseAtCenter(tabButton, {}, toolbox.win);
-  await onMaximized;
-
-  info("Minimize again and click on the settings tab");
-  await minimize(toolbox);
-  onMaximized = toolbox._host.once("maximized");
-  let settingsButton = toolbox.doc.querySelector("#toolbox-tab-options");
-  EventUtils.synthesizeMouseAtCenter(settingsButton, {}, toolbox.win);
-  await onMaximized;
-
-  info("Switch to a different host");
-  await toolbox.switchHost(Toolbox.HostType.SIDE);
-  button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
-  ok(!button, "The minimize button doesn't exist in the side host");
-
-  Services.prefs.clearUserPref("devtools.toolbox.host");
-  await toolbox.destroy();
-  gBrowser.removeCurrentTab();
-});
-
-async function minimize(toolbox) {
-  let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
-  let onMinimized = toolbox._host.once("minimized");
-  EventUtils.synthesizeMouseAtCenter(button, {}, toolbox.win);
-  await onMinimized;
-}
-
-async function minimizeWithShortcut(toolbox) {
-  let key = toolbox.doc.getElementById("toolbox-minimize-key")
-                       .getAttribute("key");
-  let onMinimized = toolbox._host.once("minimized");
-  EventUtils.synthesizeKey(key, {accelKey: true, shiftKey: true},
-                           toolbox.win);
-  await onMinimized;
-}
-
-async function maximize(toolbox) {
-  let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
-  let onMaximized = toolbox._host.once("maximized");
-  EventUtils.synthesizeMouseAtCenter(button, {}, toolbox.win);
-  await onMaximized;
-}
-
-async function maximizeWithShortcut(toolbox) {
-  let key = toolbox.doc.getElementById("toolbox-minimize-key")
-                       .getAttribute("key");
-  let onMaximized = toolbox._host.once("maximized");
-  EventUtils.synthesizeKey(key, {accelKey: true, shiftKey: true},
-                           toolbox.win);
-  await onMaximized;
-}
--- a/devtools/client/framework/toolbox-host-manager.js
+++ b/devtools/client/framework/toolbox-host-manager.js
@@ -18,51 +18,38 @@ loader.lazyRequireGetter(this, "Hosts", 
  * This component handles iframe creation within Firefox, in which we are loading
  * the toolbox document. Then both the chrome and the toolbox document communicate
  * via "message" events.
  *
  * Messages sent by the toolbox to the chrome:
  * - switch-host:
  *   Order to display the toolbox in another host (side, bottom, window, or the
  *   previously used one)
- * - toggle-minimize-mode:
- *   When using the bottom host, the toolbox can be miximized to only display
- *   the tool titles
- * - maximize-host:
- *   When using the bottom host in minimized mode, revert back to regular mode
- *   in order to see tool titles and the tools
  * - raise-host:
  *   Focus the tools
  * - set-host-title:
  *   When using the window host, update the window title
  *
  * Messages sent by the chrome to the toolbox:
- * - host-minimized:
- *   The bottom host is done minimizing (after animation end)
- * - host-maximized:
- *   The bottom host is done switching back to regular mode (after animation
- *   end)
  * - switched-host:
  *   The `switch-host` command sent by the toolbox is done
  */
 
 const LAST_HOST = "devtools.toolbox.host";
 const PREVIOUS_HOST = "devtools.toolbox.previousHost";
 let ID_COUNTER = 1;
 
 function ToolboxHostManager(target, hostType, hostOptions) {
   this.target = target;
 
   this.frameId = ID_COUNTER++;
 
   if (!hostType) {
     hostType = Services.prefs.getCharPref(LAST_HOST);
   }
-  this.onHostMinimized = this.onHostMinimized.bind(this);
-  this.onHostMaximized = this.onHostMaximized.bind(this);
   this.host = this.createHost(hostType, hostOptions);
   this.hostType = hostType;
 }
 
 ToolboxHostManager.prototype = {
   async create(toolId) {
     await this.host.create();
 
@@ -116,25 +103,19 @@ ToolboxHostManager.prototype = {
     // origin via event.source as it is null. So use a custom id.
     if (event.data.frameId != this.frameId) {
       return;
     }
     switch (event.data.name) {
       case "switch-host":
         this.switchHost(event.data.hostType);
         break;
-      case "maximize-host":
-        this.host.maximize();
-        break;
       case "raise-host":
         this.host.raise();
         break;
-      case "toggle-minimize-mode":
-        this.host.toggleMinimizeMode(event.data.toolbarHeight);
-        break;
       case "set-host-title":
         this.host.setTitle(event.data.title);
         break;
     }
   },
 
   postMessage(data) {
     let window = this.host.frame.contentWindow;
@@ -162,34 +143,19 @@ ToolboxHostManager.prototype = {
    *        The created host object
    */
   createHost(hostType, options) {
     if (!Hosts[hostType]) {
       throw new Error("Unknown hostType: " + hostType);
     }
 
     let newHost = new Hosts[hostType](this.target.tab, options);
-    // Update the label and icon when the state changes.
-    newHost.on("minimized", this.onHostMinimized);
-    newHost.on("maximized", this.onHostMaximized);
     return newHost;
   },
 
-  onHostMinimized() {
-    this.postMessage({
-      name: "host-minimized"
-    });
-  },
-
-  onHostMaximized() {
-    this.postMessage({
-      name: "host-maximized"
-    });
-  },
-
   async switchHost(hostType) {
     if (hostType == "previous") {
       // Switch to the last used host for the toolbox UI.
       // This is determined by the devtools.toolbox.previousHost pref.
       hostType = Services.prefs.getCharPref(PREVIOUS_HOST);
 
       // Handle the case where the previous host happens to match the current
       // host. If so, switch to bottom if it's not already used, and side if not.
@@ -238,14 +204,12 @@ ToolboxHostManager.prototype = {
   destroyHost() {
     // When Firefox toplevel is closed, the frame may already be detached and
     // the top level document gone
     if (this.host.frame.ownerDocument.defaultView) {
       this.host.frame.ownerDocument.defaultView.removeEventListener("message", this);
     }
     this.host.frame.removeEventListener("unload", this, true);
 
-    this.host.off("minimized", this.onHostMinimized);
-    this.host.off("maximized", this.onHostMaximized);
     return this.host.destroy();
   }
 };
 exports.ToolboxHostManager = ToolboxHostManager;
--- a/devtools/client/framework/toolbox-hosts.js
+++ b/devtools/client/framework/toolbox-hosts.js
@@ -97,73 +97,16 @@ BottomHost.prototype = {
   /**
    * Raise the host.
    */
   raise: function() {
     focusTab(this.hostTab);
   },
 
   /**
-   * Minimize this host so that only the toolbox tabbar remains visible.
-   * @param {Number} height The height to minimize to. Defaults to 0, which
-   * means that the toolbox won't be visible at all once minimized.
-   */
-  minimize: function(height = 0) {
-    if (this.isMinimized) {
-      return;
-    }
-    this.isMinimized = true;
-
-    let onTransitionEnd = event => {
-      if (event.propertyName !== "margin-bottom") {
-        // Ignore transitionend on unrelated properties.
-        return;
-      }
-
-      this.frame.removeEventListener("transitionend", onTransitionEnd);
-      this.emit("minimized");
-    };
-    this.frame.addEventListener("transitionend", onTransitionEnd);
-    this.frame.style.marginBottom = -this.frame.height + height + "px";
-    this._splitter.classList.add("disabled");
-  },
-
-  /**
-   * If the host was minimized before, maximize it again (the host will be
-   * maximized to the height it previously had).
-   */
-  maximize: function() {
-    if (!this.isMinimized) {
-      return;
-    }
-    this.isMinimized = false;
-
-    let onTransitionEnd = event => {
-      if (event.propertyName !== "margin-bottom") {
-        // Ignore transitionend on unrelated properties.
-        return;
-      }
-
-      this.frame.removeEventListener("transitionend", onTransitionEnd);
-      this.emit("maximized");
-    };
-    this.frame.addEventListener("transitionend", onTransitionEnd);
-    this.frame.style.marginBottom = "0";
-    this._splitter.classList.remove("disabled");
-  },
-
-  /**
-   * Toggle the minimize mode.
-   * @param {Number} minHeight The height to minimize to.
-   */
-  toggleMinimizeMode: function(minHeight) {
-    this.isMinimized ? this.maximize() : this.minimize(minHeight);
-  },
-
-  /**
    * Set the toolbox title.
    * Nothing to do for this host type.
    */
   setTitle: function() {},
 
   /**
    * Destroy the bottom dock.
    */
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -136,22 +136,17 @@ function Toolbox(target, selectedTool, h
   this._applyCacheSettings = this._applyCacheSettings.bind(this);
   this._applyServiceWorkersTestingSettings =
     this._applyServiceWorkersTestingSettings.bind(this);
   this._saveSplitConsoleHeight = this._saveSplitConsoleHeight.bind(this);
   this._onFocus = this._onFocus.bind(this);
   this._onBrowserMessage = this._onBrowserMessage.bind(this);
   this._showDevEditionPromo = this._showDevEditionPromo.bind(this);
   this._updateTextBoxMenuItems = this._updateTextBoxMenuItems.bind(this);
-  this._onBottomHostMinimized = this._onBottomHostMinimized.bind(this);
-  this._onBottomHostMaximized = this._onBottomHostMaximized.bind(this);
-  this._onToolSelectWhileMinimized = this._onToolSelectWhileMinimized.bind(this);
   this._onPerformanceFrontEvent = this._onPerformanceFrontEvent.bind(this);
-  this._onBottomHostWillChange = this._onBottomHostWillChange.bind(this);
-  this._toggleMinimizeMode = this._toggleMinimizeMode.bind(this);
   this._onToolbarFocus = this._onToolbarFocus.bind(this);
   this._onToolbarArrowKeypress = this._onToolbarArrowKeypress.bind(this);
   this._onPickerClick = this._onPickerClick.bind(this);
   this._onPickerKeypress = this._onPickerKeypress.bind(this);
   this._onPickerStarted = this._onPickerStarted.bind(this);
   this._onPickerStopped = this._onPickerStopped.bind(this);
   this._onInspectObject = this._onInspectObject.bind(this);
   this._onNewSelectedNodeFront = this._onNewSelectedNodeFront.bind(this);
@@ -886,21 +881,16 @@ Toolbox.prototype = {
                    this.selectNextTool();
                    event.preventDefault();
                  });
     this.shortcuts.on(L10N.getStr("toolbox.previousTool.key"),
                  event => {
                    this.selectPreviousTool();
                    event.preventDefault();
                  });
-    this.shortcuts.on(L10N.getStr("toolbox.minimize.key"),
-                 event => {
-                   this._toggleMinimizeMode();
-                   event.preventDefault();
-                 });
     this.shortcuts.on(L10N.getStr("toolbox.toggleHost.key"),
                  event => {
                    this.switchToPreviousHost();
                    event.preventDefault();
                  });
 
     this.doc.addEventListener("keypress", this._splitConsoleOnKeypress);
     this.doc.addEventListener("focus", this._onFocus, true);
@@ -915,33 +905,18 @@ Toolbox.prototype = {
       this.doc.removeEventListener("focus", this._onFocus, true);
       this.win.removeEventListener("unload", this.destroy);
       this.win.removeEventListener("message", this._onBrowserMessage, true);
     }
   },
 
   // Called whenever the chrome send a message
   _onBrowserMessage: function(event) {
-    if (!event.data) {
-      return;
-    }
-    switch (event.data.name) {
-      case "switched-host":
-        this._onSwitchedHost(event.data);
-        break;
-      case "host-minimized":
-        if (this.hostType == Toolbox.HostType.BOTTOM) {
-          this._onBottomHostMinimized();
-        }
-        break;
-      case "host-maximized":
-        if (this.hostType == Toolbox.HostType.BOTTOM) {
-          this._onBottomHostMaximized();
-        }
-        break;
+    if (event.data && event.data.name === "switched-host") {
+      this._onSwitchedHost(event.data);
     }
   },
 
   _registerOverlays: function() {
     registerHarOverlay(this);
   },
 
   _saveSplitConsoleHeight: function() {
@@ -1077,26 +1052,16 @@ Toolbox.prototype = {
    * the host changes.
    */
   _buildDockButtons: function() {
     if (!this._target.isLocalTab) {
       this.component.setDockButtonsEnabled(false);
       return;
     }
 
-    // Bottom-type host can be minimized, add a button for this.
-    if (this.hostType == Toolbox.HostType.BOTTOM) {
-      this.component.setCanMinimize(true);
-
-      // Maximize again when a tool gets selected.
-      this.on("before-select", this._onToolSelectWhileMinimized);
-      // Maximize and stop listening before the host type changes.
-      this.once("host-will-change", this._onBottomHostWillChange);
-    }
-
     this.component.setDockButtonsEnabled(true);
     this.component.setCanCloseToolbox(this.hostType !== Toolbox.HostType.WINDOW);
 
     let sideEnabled = Services.prefs.getBoolPref(this._prefs.SIDE_ENABLED);
 
     let hostTypes = [];
     for (let type in Toolbox.HostType) {
       let position = Toolbox.HostType[type];
@@ -1110,64 +1075,27 @@ Toolbox.prototype = {
         position,
         switchHost: this.switchHost.bind(this, position)
       });
     }
 
     this.component.setHostTypes(hostTypes);
   },
 
-  _onBottomHostMinimized: function() {
-    this.component.setMinimizeState("minimized");
-  },
-
-  _onBottomHostMaximized: function() {
-    this.component.setMinimizeState("maximized");
-  },
-
-  _onToolSelectWhileMinimized: function() {
-    this.postMessage({
-      name: "maximize-host"
-    });
-  },
-
   postMessage: function(msg) {
     // We sometime try to send messages in middle of destroy(), where the
     // toolbox iframe may already be detached and no longer have a parent.
     if (this.win.parent) {
       // Toolbox document is still chrome and disallow identifying message
       // origin via event.source as it is null. So use a custom id.
       msg.frameId = this.frameId;
       this.win.parent.postMessage(msg, "*");
     }
   },
 
-  _onBottomHostWillChange: function() {
-    this.postMessage({
-      name: "maximize-host"
-    });
-
-    this.off("before-select", this._onToolSelectWhileMinimized);
-  },
-
-  _toggleMinimizeMode: function() {
-    if (this.hostType !== Toolbox.HostType.BOTTOM) {
-      return;
-    }
-
-    // Calculate the height to which the host should be minimized so the
-    // tabbar is still visible.
-    let toolbarHeight = this._componentMount.getBoxQuads({box: "content"})[0].bounds
-                                                                             .height;
-    this.postMessage({
-      name: "toggle-minimize-mode",
-      toolbarHeight
-    });
-  },
-
   /**
    * Initiate ToolboxTabs React component and all it's properties. Do the initial render.
    */
   _buildTabs: function() {
     // Get the initial list of tab definitions. This list can be amended at a later time
     // by tools registering themselves.
     const definitions = gDevTools.getToolDefinitionArray();
     definitions.forEach(definition => this._buildPanelForTool(definition));
@@ -1184,17 +1112,16 @@ Toolbox.prototype = {
   _mountReactComponent: function() {
     // Ensure the toolbar doesn't try to render until the tool is ready.
     const element = this.React.createElement(this.ToolboxController, {
       L10N,
       currentToolId: this.currentToolId,
       selectTool: this.selectTool,
       closeToolbox: this.destroy,
       focusButton: this._onToolbarFocus,
-      toggleMinimizeMode: this._toggleMinimizeMode,
       toolbox: this
     });
 
     this.component = this.ReactDOM.render(element, this._componentMount);
   },
 
   /**
    * Reset tabindex attributes across all focusable elements inside the toolbar.
@@ -1866,18 +1793,16 @@ Toolbox.prototype = {
 
   /**
    * Switch to the tool with the given id
    *
    * @param {string} id
    *        The id of the tool to switch to
    */
   selectTool: function(id) {
-    this.emit("before-select", id);
-
     if (this.currentToolId == id) {
       let panel = this._toolPanels.get(id);
       if (panel) {
         // We have a panel instance, so the tool is already fully loaded.
 
         // re-focus tool to get key events again
         this.focusTool(id);
 
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -101,24 +101,27 @@ devtools.jar:
     content/shared/widgets/spectrum.css (shared/widgets/spectrum.css)
     content/aboutdebugging/aboutdebugging.xhtml (aboutdebugging/aboutdebugging.xhtml)
     content/aboutdebugging/aboutdebugging.css (aboutdebugging/aboutdebugging.css)
     content/aboutdebugging/initializer.js (aboutdebugging/initializer.js)
     content/responsive.html/index.xhtml (responsive.html/index.xhtml)
     content/responsive.html/index.js (responsive.html/index.js)
     content/dom/dom.html (dom/dom.html)
     content/dom/main.js (dom/main.js)
+    content/accessibility/accessibility.html (accessibility/accessibility.html)
+    content/accessibility/main.js (accessibility/main.js)
 %   skin devtools classic/1.0 %skin/
     skin/devtools-browser.css (themes/devtools-browser.css)
     skin/dark-theme.css (themes/dark-theme.css)
     skin/light-theme.css (themes/light-theme.css)
     skin/firebug-theme.css (themes/firebug-theme.css)
     skin/toolbars.css (themes/toolbars.css)
     skin/toolbox.css (themes/toolbox.css)
     skin/tooltips.css (themes/tooltips.css)
+    skin/images/accessibility.svg (themes/images/accessibility.svg)
     skin/images/add.svg (themes/images/add.svg)
     skin/images/breadcrumbs-divider.svg (themes/images/breadcrumbs-divider.svg)
     skin/images/filters.svg (themes/images/filters.svg)
     skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
     skin/images/font-swatch.svg (themes/images/font-swatch.svg)
     skin/images/grid.svg (themes/images/grid.svg)
     skin/images/angle-swatch.svg (themes/images/angle-swatch.svg)
     skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
@@ -140,16 +143,17 @@ devtools.jar:
     skin/images/alerticon-warning.png (themes/images/alerticon-warning.png)
     skin/images/alerticon-warning@2x.png (themes/images/alerticon-warning@2x.png)
     skin/rules.css (themes/rules.css)
     skin/commandline.css (themes/commandline.css)
     skin/images/command-paintflashing.svg (themes/images/command-paintflashing.svg)
     skin/images/command-screenshot.svg (themes/images/command-screenshot.svg)
     skin/images/command-responsivemode.svg (themes/images/command-responsivemode.svg)
     skin/images/command-pick.svg (themes/images/command-pick.svg)
+    skin/images/command-pick-accessibility.svg (themes/images/command-pick-accessibility.svg)
     skin/images/command-frames.svg (themes/images/command-frames.svg)
     skin/images/command-console.svg (themes/images/command-console.svg)
     skin/images/command-eyedropper.svg (themes/images/command-eyedropper.svg)
     skin/images/command-rulers.svg (themes/images/command-rulers.svg)
     skin/images/command-measure.svg (themes/images/command-measure.svg)
     skin/images/command-noautohide.svg (themes/images/command-noautohide.svg)
     skin/markup.css (themes/markup.css)
     skin/images/editor-error.png (themes/images/editor-error.png)
@@ -219,16 +223,17 @@ devtools.jar:
     skin/images/tool-styleeditor.svg (themes/images/tool-styleeditor.svg)
     skin/images/tool-storage.svg (themes/images/tool-storage.svg)
     skin/images/tool-profiler.svg (themes/images/tool-profiler.svg)
     skin/images/tool-network.svg (themes/images/tool-network.svg)
     skin/images/tool-scratchpad.svg (themes/images/tool-scratchpad.svg)
     skin/images/tool-webaudio.svg (themes/images/tool-webaudio.svg)
     skin/images/tool-memory.svg (themes/images/tool-memory.svg)
     skin/images/tool-dom.svg (themes/images/tool-dom.svg)
+    skin/images/tool-accessibility.svg (themes/images/tool-accessibility.svg)
     skin/images/close.svg (themes/images/close.svg)
     skin/images/clear.svg (themes/images/clear.svg)
     skin/images/vview-delete.png (themes/images/vview-delete.png)
     skin/images/vview-delete@2x.png (themes/images/vview-delete@2x.png)
     skin/images/vview-edit.png (themes/images/vview-edit.png)
     skin/images/vview-edit@2x.png (themes/images/vview-edit@2x.png)
     skin/images/vview-lock.png (themes/images/vview-lock.png)
     skin/images/vview-lock@2x.png (themes/images/vview-lock@2x.png)
new file mode 100644
--- /dev/null
+++ b/devtools/client/locales/en-US/accessibility.properties
@@ -0,0 +1,88 @@
+# 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/.
+
+# LOCALIZATION NOTE These strings are used inside the Accessibility panel
+# which is available from the Web Developer sub-menu -> 'Accessibility'.
+# The correct localization of this file might be to keep it in
+# English, or another language commonly spoken among web developers.
+# You want to make that choice consistent across the developer tools.
+# A good criteria is the language in which you'd find the best
+# documentation on web development on the web.
+
+# LOCALIZATION NOTE (accessibility.role): A title text used for Accessibility
+# tree header column that represents accessible element role.
+accessibility.role=Role
+
+# LOCALIZATION NOTE (accessibility.name): A title text used for Accessibility
+# tree header column that represents accessible element name.
+accessibility.name=Name
+
+# LOCALIZATION NOTE (accessibility.logo): A title text used for Accessibility
+# logo used on the accessibility panel landing page.
+accessibility.logo=Accessibility Logo
+
+# LOCALIZATION NOTE (accessibility.properties): A title text used for header
+# for Accessibility details sidebar.
+accessibility.properties=Properties
+
+# LOCALIZATION NOTE (accessibility.treeName): A title text used for
+# Accessibility tree (that represents accessible element name) container.
+accessibility.treeName=Accessibility Tree
+
+# LOCALIZATION NOTE (accessibility.accessible.notAvailable): A title text
+# displayed when accessible sidebar panel does not have an accessible object to
+# display.
+accessibility.accessible.notAvailable=Accessible Information Unavailable
+
+# LOCALIZATION NOTE (accessibility.enable): A title text for Enable
+# accessibility button used to enable accessibility service.
+accessibility.enable=Turn On Accessibility Features
+
+# LOCALIZATION NOTE (accessibility.enabling): A title text for Enable
+# accessibility button used when accessibility service is being enabled.
+accessibility.enabling=Turning on accessibility features…
+
+# LOCALIZATION NOTE (accessibility.disable): A title text for Disable
+# accessibility button used to disable accessibility service.
+accessibility.disable=Turn Off Accessibility Features
+
+# LOCALIZATION NOTE (accessibility.disabling): A title text for Disable
+# accessibility button used when accessibility service is being
+# disabled.
+accessibility.disabling=Turning off accessibility features…
+
+# LOCALIZATION NOTE (accessibility.pick): A title text for Picker button
+# button used to pick accessible objects from the page.
+accessibility.pick=Pick accessible object from the page
+
+# LOCALIZATION NOTE (accessibility.disable.disabledTitle): A title text used for
+# a tooltip for Disable accessibility button when accessibility service can not
+# be disabled. It is the case when a user is using a 3rd party accessibility
+# tool such as screen reader.
+accessibility.disable.disabledTitle=Accessibility service can not be turned off. It is used outside Developer Tools.
+
+# LOCALIZATION NOTE (accessibility.disable.enabledTitle): A title text used for
+# a tooltip for Disable accessibility button when accessibility service can be
+# disabled.
+accessibility.disable.enabledTitle=Accessibility service will be turned off for all tabs and windows.
+
+# LOCALIZATION NOTE (accessibility.enable.disabledTitle): A title text used for
+# a tooltip for Enabled accessibility button when accessibility service can not
+# be enabled.
+accessibility.enable.disabledTitle=Accessibility service can not be turned on. It is turned off via accessibility services privacy preference.
+
+# LOCALIZATION NOTE (accessibility.enable.enabledTitle): A title text used for
+# a tooltip for Enabled accessibility button when accessibility service can be
+# enabled.
+accessibility.enable.enabledTitle=Accessibility service will be turned on for all tabs and windows.
+
+# LOCALIZATION NOTE (accessibility.description.general): A title text used when
+# accessibility service description is provided before accessibility inspector
+# is enabled.
+accessibility.description.general=Accessibility features are deactivated by default because they negatively impact performance. Consider turning off accessibility features before using other Developer Tools panels.
+
+# LOCALIZATION NOTE (accessibility.description.oldVersion): A title text used
+# when accessibility service description is provided when a client is connected
+# to an older version of accessibility actor.
+accessibility.description.oldVersion=You are connected to a debugger server that is too old. To use Accessibility panel, please connect to the latest debugger server version.
--- a/devtools/client/locales/en-US/startup.properties
+++ b/devtools/client/locales/en-US/startup.properties
@@ -242,16 +242,35 @@ dom.panelLabel=DOM Panel
 dom.accesskey=D
 
 # LOCALIZATION NOTE (dom.tooltip):
 # This string is displayed in the tooltip of the tab when the DOM is
 # displayed inside the developer tools window.
 # Keyboard shortcut for DOM panel will be shown inside the brackets.
 dom.tooltip=DOM (%S)
 
+# LOCALIZATION NOTE (accessibility.label):
+# This string is displayed in the title of the tab when the Accessibility panel
+# is displayed inside the developer tools window and in the Developer Tools Menu.
+accessibility.label=Accessibility
+
+# LOCALIZATION NOTE (accessibility.panelLabel):
+# This is used as the label for the toolbox panel.
+accessibility.panelLabel=Accessibility Panel
+
+# LOCALIZATION NOTE (accessibility.accesskey)
+# Used for the menuitem in the tool menu
+accessibility.accesskey=y
+
+# LOCALIZATION NOTE (accessibility.tooltip):
+# This string is displayed in the tooltip of the tab when the Accessibility is
+# displayed inside the developer tools window.
+# Keyboard shortcut for Accessibility panel will be shown inside the brackets.
+accessibility.tooltip=Accessibility (%S)
+
 # LOCALIZATION NOTE (toolbox.buttons.splitconsole):
 # This is the tooltip of the button in the toolbox toolbar used to toggle
 # the split console.
 # Keyboard shortcut will be shown inside brackets.
 toolbox.buttons.splitconsole = Toggle split console (%S)
 
 # LOCALIZATION NOTE (toolbox.buttons.responsive):
 # This is the tooltip of the button in the toolbox toolbar that toggles
--- a/devtools/client/locales/en-US/toolbox.properties
+++ b/devtools/client/locales/en-US/toolbox.properties
@@ -1,30 +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/.
 
 toolboxDockButtons.bottom.tooltip=Dock to bottom of browser window
 toolboxDockButtons.side.tooltip=Dock to side of browser window
 toolboxDockButtons.window.tooltip=Show in separate window
 
-# LOCALIZATION NOTE (toolboxDockButtons.bottom.minimize): This string is shown
-# as a tooltip that appears in the toolbox when it is in "bottom host" mode and
-# when hovering over the minimize button in the toolbar. When clicked, the
-# button minimizes the toolbox so that just the toolbar is visible at the
-# bottom.
-toolboxDockButtons.bottom.minimize=Minimize the toolbox
-
-# LOCALIZATION NOTE (toolboxDockButtons.bottom.maximize): This string is shown
-# as a tooltip that appears in the toolbox when it is in "bottom host" mode and
-# when hovering over the maximize button in the toolbar. When clicked, the
-# button maximizes the toolbox again (if it had been minimized before) so that
-# the whole toolbox is visible again.
-toolboxDockButtons.bottom.maximize=Maximize the toolbox
-
 # LOCALIZATION NOTE (toolboxToggleButton.errors): Semi-colon list of plural
 # forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 number of errors in the current web page
 toolboxToggleButton.errors=#1 error;#1 errors
 
 # LOCALIZATION NOTE (toolboxToggleButton.warnings): Semi-colon list of plural
 # forms.
@@ -146,20 +132,16 @@ toolbox.zoomReset2.key=
 toolbox.reload.key=CmdOrCtrl+R
 toolbox.reload2.key=F5
 
 # LOCALIZATION NOTE (toolbox.forceReload*.key)
 # Key shortcuts used to force reload of the page by bypassing caches
 toolbox.forceReload.key=CmdOrCtrl+Shift+R
 toolbox.forceReload2.key=CmdOrCtrl+F5
 
-# LOCALIZATION NOTE (toolbox.minimize.key)
-# Key shortcut used to minimize the toolbox
-toolbox.minimize.key=CmdOrCtrl+Shift+U
-
 # LOCALIZATION NOTE (toolbox.toggleHost.key)
 # Key shortcut used to move the toolbox in bottom or side of the browser window
 toolbox.toggleHost.key=CmdOrCtrl+Shift+D
 
 # LOCALIZATION NOTE (toolbox.frames.tooltip): This is the label for
 # the iframes menu list that appears only when the document has some.
 # It allows you to switch the context of the whole toolbox.
 toolbox.frames.tooltip=Select an iframe as the currently targeted document
--- a/devtools/client/moz.build
+++ b/devtools/client/moz.build
@@ -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('../templates.mozbuild')
 
 DIRS += [
     'aboutdebugging',
+    'accessibility',
     'animationinspector',
     'canvasdebugger',
     'commandline',
     'debugger',
     'dom',
     'framework',
     'inspector',
     'jsonview',
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -225,16 +225,19 @@ pref("devtools.canvasdebugger.enabled", 
 pref("devtools.webaudioeditor.enabled", false);
 
 // Enable Scratchpad
 pref("devtools.scratchpad.enabled", false);
 
 // Make sure the DOM panel is hidden by default
 pref("devtools.dom.enabled", false);
 
+// Make sure the Accessibility panel is hidden by default
+pref("devtools.accessibility.enabled", false);
+
 // Web Audio Editor Inspector Width should be a preference
 pref("devtools.webaudioeditor.inspectorWidth", 300);
 
 // Web console filters
 pref("devtools.webconsole.filter.error", true);
 pref("devtools.webconsole.filter.warn", true);
 pref("devtools.webconsole.filter.info", true);
 pref("devtools.webconsole.filter.log", true);
--- a/devtools/client/shared/autocomplete-popup.js
+++ b/devtools/client/shared/autocomplete-popup.js
@@ -249,20 +249,26 @@ AutocompletePopup.prototype = {
   __maxLabelLength: -1,
 
   get _maxLabelLength() {
     if (this.__maxLabelLength !== -1) {
       return this.__maxLabelLength;
     }
 
     let max = 0;
-    for (let {label, count} of this.items) {
+
+    for (let {label, postLabel, count} of this.items) {
       if (count) {
         label += count + "";
       }
+
+      if (postLabel) {
+        label += postLabel;
+      }
+
       max = Math.max(label.length, max);
     }
 
     this.__maxLabelLength = max;
     return this.__maxLabelLength;
   },
 
   /**
@@ -417,39 +423,54 @@ AutocompletePopup.prototype = {
    *        The item object can have the following properties:
    *        - label {String} Property which is used as the displayed value.
    *        - preLabel {String} [Optional] The String that will be displayed
    *                   before the label indicating that this is the already
    *                   present text in the input box, and label is the text
    *                   that will be auto completed. When this property is
    *                   present, |preLabel.length| starting characters will be
    *                   removed from label.
+   *        - postLabel {String} [Optional] The string that will be displayed
+   *                  after the label. Currently used to display the value of
+   *                  a desired variable.
    *        - count {Number} [Optional] The number to represent the count of
    *                autocompleted label.
    */
   appendItem: function(item) {
     let listItem = this._document.createElementNS(HTML_NS, "li");
     // Items must have an id for accessibility.
     listItem.setAttribute("id", "autocomplete-item-" + itemIdCounter++);
     listItem.className = "autocomplete-item";
     listItem.setAttribute("data-index", this.items.length);
+
     if (this.direction) {
       listItem.setAttribute("dir", this.direction);
     }
+
     let label = this._document.createElementNS(HTML_NS, "span");
     label.textContent = item.label;
     label.className = "autocomplete-value";
+
     if (item.preLabel) {
       let preDesc = this._document.createElementNS(HTML_NS, "span");
       preDesc.textContent = item.preLabel;
       preDesc.className = "initial-value";
       listItem.appendChild(preDesc);
       label.textContent = item.label.slice(item.preLabel.length);
     }
+
     listItem.appendChild(label);
+
+    if (item.postLabel) {
+      let postDesc = this._document.createElementNS(HTML_NS, "span");
+      postDesc.textContent = item.postLabel;
+      postDesc.className = "autocomplete-postlabel";
+      listItem.appendChild(postDesc);
+    }
+
     if (item.count && item.count > 1) {
       let countDesc = this._document.createElementNS(HTML_NS, "span");
       countDesc.textContent = item.count;
       countDesc.setAttribute("flex", "1");
       countDesc.className = "autocomplete-count";
       listItem.appendChild(countDesc);
     }
 
--- a/devtools/client/shared/inplace-editor.js
+++ b/devtools/client/shared/inplace-editor.js
@@ -1327,17 +1327,20 @@ InplaceEditor.prototype = {
         // Check if the next character is a valid word character, no suggestion should be
         // provided when preceeding a word.
         if (/[\w-]/.test(nextChar)) {
           // This emit is mainly to make the test flow simpler.
           this.emit("after-suggest", "nothing to autocomplete");
           return;
         }
       }
+
       let list = [];
+      let postLabelValues = [];
+
       if (this.contentType == CONTENT_TYPES.CSS_PROPERTY) {
         list = this._getCSSPropertyList();
       } else if (this.contentType == CONTENT_TYPES.CSS_VALUE) {
         // Get the last query to be completed before the caret.
         let match = /([^\s,.\/]+$)/.exec(query);
         if (match) {
           startCheckQuery = match[0];
         } else {
@@ -1345,16 +1348,17 @@ InplaceEditor.prototype = {
         }
 
         // Check if the query to be completed is a CSS variable.
         let varMatch = /^var\(([^\s]+$)/.exec(startCheckQuery);
 
         if (varMatch && varMatch.length == 2) {
           startCheckQuery = varMatch[1];
           list = this._getCSSVariableNames();
+          postLabelValues = list.map(varName => this._getCSSVariableValue(varName));
         } else {
           list = ["!important",
                   ...this._getCSSValuesForPropertyName(this.property.name)];
         }
 
         if (query == "") {
           // Do not suggest '!important' without any manually typed character.
           list.splice(0, 1);
@@ -1410,17 +1414,18 @@ InplaceEditor.prototype = {
 
       let finalList = [];
       let length = list.length;
       for (let i = 0, count = 0; i < length && count < MAX_POPUP_ENTRIES; i++) {
         if (startCheckQuery != null && list[i].startsWith(startCheckQuery)) {
           count++;
           finalList.push({
             preLabel: startCheckQuery,
-            label: list[i]
+            label: list[i],
+            postLabel: postLabelValues[i] ? postLabelValues[i] : ""
           });
         } else if (count > 0) {
           // Since count was incremented, we had already crossed the entries
           // which would have started with query, assuming that list is sorted.
           break;
         } else if (startCheckQuery != null && list[i][0] > startCheckQuery[0]) {
           // We have crossed all possible matches alphabetically.
           break;
@@ -1513,16 +1518,27 @@ InplaceEditor.prototype = {
   /**
    * Returns the list of all CSS variables to use for the autocompletion.
    *
    * @return {Array} array of CSS variable names (Strings)
    */
   _getCSSVariableNames: function() {
     return Array.from(this.cssVariables.keys()).sort();
   },
+
+  /**
+  * Returns the variable's value for the given CSS variable name.
+  *
+  * @param {String} varName
+  *        The variable name to retrieve the value of
+  * @return {String} the variable value to the given CSS variable name
+  */
+  _getCSSVariableValue: function(varName) {
+    return this.cssVariables.get(varName);
+  },
 };
 
 /**
  * Copy text-related styles from one element to another.
  */
 function copyTextStyles(from, to) {
   let win = from.ownerDocument.defaultView;
   let style = win.getComputedStyle(from);
--- a/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js
+++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js
@@ -16,69 +16,72 @@ loadHelperScript("helper_inplace_editor.
 // typing in "var"
 
 // format :
 //  [
 //    what key to press,
 //    expected input box value after keypress,
 //    selected suggestion index (-1 if popup is hidden),
 //    number of suggestions in the popup (0 if popup is hidden),
+//    expected post label corresponding with the input box value,
 //  ]
 const testData = [
-  ["v", "v", -1, 0],
-  ["a", "va", -1, 0],
-  ["r", "var", -1, 0],
-  ["(", "var(", -1, 0],
-  ["-", "var(--abc", 0, 2],
-  ["VK_BACK_SPACE", "var(-", -1, 0],
-  ["-", "var(--abc", 0, 2],
-  ["VK_DOWN", "var(--def", 1, 2],
-  ["VK_DOWN", "var(--abc", 0, 2],
-  ["VK_LEFT", "var(--abc", -1, 0],
+  ["v", "v", -1, 0, null],
+  ["a", "va", -1, 0, null],
+  ["r", "var", -1, 0, null],
+  ["(", "var(", -1, 0, null],
+  ["-", "var(--abc", 0, 4, "blue"],
+  ["VK_BACK_SPACE", "var(-", -1, 0, null],
+  ["-", "var(--abc", 0, 4, "blue"],
+  ["VK_DOWN", "var(--def", 1, 4, "red"],
+  ["VK_DOWN", "var(--ghi", 2, 4, "green"],
+  ["VK_DOWN", "var(--jkl", 3, 4, "yellow"],
+  ["VK_DOWN", "var(--abc", 0, 4, "blue"],
+  ["VK_DOWN", "var(--def", 1, 4, "red"],
+  ["VK_LEFT", "var(--def", -1, 0, null],
+];
+
+const CSS_VARIABLES = [
+  ["--abc", "blue"],
+  ["--def", "red"],
+  ["--ghi", "green"],
+  ["--jkl", "yellow"]
 ];
 
 const mockGetCSSValuesForPropertyName = function(propertyName) {
   return [];
 };
 
-const mockGetCSSVariableNames = function() {
-  return [
-    "--abc",
-    "--def",
-  ];
-};
-
 add_task(async function() {
-  await addTab("data:text/html;charset=utf-8," +
-    "inplace editor CSS variable autocomplete");
+  await addTab("data:text/html;charset=utf-8,inplace editor CSS variable autocomplete");
   let [host, win, doc] = await createHost();
 
   let xulDocument = win.top.document;
   let popup = new AutocompletePopup(xulDocument, { autoSelect: true });
 
   await new Promise(resolve => {
     createInplaceEditorAndClick({
       start: runAutocompletionTest,
       contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
       property: {
         name: "color"
       },
+      cssVariables: new Map(CSS_VARIABLES),
       done: resolve,
       popup: popup
     }, doc);
   });
 
   popup.destroy();
   host.destroy();
   gBrowser.removeCurrentTab();
 });
 
 let runAutocompletionTest = async function(editor) {
   info("Starting to test for css variable completion");
   editor._getCSSValuesForPropertyName = mockGetCSSValuesForPropertyName;
-  editor._getCSSVariableNames = mockGetCSSVariableNames;
 
   for (let data of testData) {
     await testCompletion(data, editor);
   }
 
   EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
 };
--- a/devtools/client/shared/test/helper_inplace_editor.js
+++ b/devtools/client/shared/test/helper_inplace_editor.js
@@ -67,20 +67,21 @@ function createSpan(doc) {
  * Test helper simulating a key event in an InplaceEditor and checking that the
  * autocompletion works as expected.
  *
  * @param {Array} testData
  *        - {String} key, the key to send
  *        - {String} completion, the expected value of the auto-completion
  *        - {Number} index, the index of the selected suggestion in the popup
  *        - {Number} total, the total number of suggestions in the popup
+ *        - {String} postLabel, the expected post label for the selected suggestion
  * @param {InplaceEditor} editor
  *        The InplaceEditor instance being tested
  */
-async function testCompletion([key, completion, index, total], editor) {
+async function testCompletion([key, completion, index, total, postLabel], editor) {
   info("Pressing key " + key);
   info("Expecting " + completion);
 
   let onVisibilityChange = null;
   let open = total > 0;
   if (editor.popup.isOpen != open) {
     onVisibilityChange = editor.popup.once(open ? "popup-opened" : "popup-closed");
   }
@@ -100,16 +101,24 @@ async function testCompletion([key, comp
   await onSuggest;
   await onVisibilityChange;
   await waitForTime(5);
 
   info("Checking the state");
   if (completion !== null) {
     is(editor.input.value, completion, "Correct value is autocompleted");
   }
+
+  if (postLabel) {
+    let selectedItem = editor.popup.getItems()[index];
+    let selectedElement = editor.popup.elements.get(selectedItem);
+    ok(selectedElement.textContent.includes(postLabel),
+      "Selected popup element contains the expected post-label");
+  }
+
   if (total === 0) {
     ok(!(editor.popup && editor.popup.isOpen), "Popup is closed");
   } else {
     ok(editor.popup.isOpen, "Popup is open");
     is(editor.popup.getItems().length, total, "Number of suggestions match");
     is(editor.popup.selectedIndex, index, "Expected item is selected");
   }
 }
--- a/devtools/client/themes/common.css
+++ b/devtools/client/themes/common.css
@@ -96,16 +96,22 @@ html|button, html|select {
   white-space: pre;
   overflow: hidden;
 }
 
 .devtools-autocomplete-listbox .autocomplete-item > .initial-value,
 .devtools-autocomplete-listbox .autocomplete-item > .autocomplete-value {
   margin: 0;
   padding: 0;
+  float: left;
+}
+
+.devtools-autocomplete-listbox .autocomplete-item > .autocomplete-postlabel {
+  font-style: italic;
+  float: right;
 }
 
 .devtools-autocomplete-listbox .autocomplete-item > .autocomplete-count {
   text-align: end;
 }
 
 /* Rest of the dark and light theme */
 
--- a/devtools/client/themes/devtools-browser.css
+++ b/devtools/client/themes/devtools-browser.css
@@ -1,20 +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/. */
 
 @import url("resource://devtools/client/themes/splitters.css");
 @import url("resource://devtools/client/themes/commandline-browser.css");
 
-/* Bottom-docked toolbox minimize transition */
-.devtools-toolbox-bottom-iframe {
-  transition: margin-bottom .1s;
-}
-
 .devtools-toolbox-side-iframe {
   min-width: 465px;
 }
 
 /* Eyedropper Widget */
 /* <panel> added to mainPopupSet */
 
 .devtools-eyedropper-panel {
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/accessibility.svg
@@ -0,0 +1,9 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="context-fill">
+<path d="M8,0C3.6,0,0,3.6,0,8s3.6,8,8,8s8-3.6,8-8S12.4,0,8,0z M8,2.7c0.7,0,1.3,0.6,1.3,1.3S8.7,5.3,8,5.3
+	S6.7,4.7,6.7,4S7.3,2.7,8,2.7z M12,6.9H9.7v3v2.7c0,0.4-0.3,0.7-0.7,0.7S8.3,13,8.3,12.6V9.9H7.7v2.7c0,0.4-0.3,0.7-0.7,0.7
+	S6.3,13,6.3,12.6V7.4V7H4C3.6,7,3.3,6.7,3.3,6.3c0-0.4,0.3-0.7,0.7-0.7h2.3h3.3h2.4c0.3,0,0.6,0.2,0.6,0.5C12.7,6.6,12.4,6.9,12,6.9
+	z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/command-pick-accessibility.svg
@@ -0,0 +1,9 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill #0b0b0b">
+  <path d="M15,7.5V2.8c0-0.6-0.5-1-1-1H2c-0.6,0-1,0.4-1,1v10c0,0.5,0.5,1,1,1h6.8l-0.4-1H2v-10h12v4.3L15,7.5z"/>
+  <circle cx="11.5" cy="7.1" r="1"/>
+  <path d="M14.6,8.4H8.5C8.2,8.4,8,8.6,8,8.9s0.2,0.5,0.5,0.5h1.8v4.2c0,0.3,0.2,0.6,0.5,0.6s0.5-0.3,0.5-0.6v-2.1
+   h0.5v2.1c0,0.3,0.2,0.6,0.5,0.6s0.5-0.3,0.5-0.6V9.3h1.8c0.3,0,0.5-0.2,0.5-0.5C15,8.5,14.8,8.4,14.6,8.4z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/tool-accessibility.svg
@@ -0,0 +1,8 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" fill="none" stroke="context-fill #0b0b0b" stroke-width="0.8">
+<path d="M8,2.7c0.7,0,1.3,0.6,1.3,1.3S8.7,5.3,8,5.3S6.7,4.7,6.7,4S7.3,2.7,8,2.7z"/>
+<path d="M12,6.9H9.7v3v2.7c0,0.4-0.3,0.7-0.7,0.7S8.3,13,8.3,12.6V9.9H7.7v2.7c0,0.4-0.3,0.7-0.7,0.7S6.3,13,6.3,12.6
+       V7.4V7H4C3.6,7,3.3,6.7,3.3,6.3c0-0.4,0.3-0.7,0.7-0.7h2.3h3.3H12c0.3,0,0.6,0.2,0.6,0.5C12.7,6.6,12.4,6.9,12,6.9z"/>
+</svg>
--- a/devtools/client/themes/toolbox.css
+++ b/devtools/client/themes/toolbox.css
@@ -9,16 +9,17 @@
   --dock-side-image: url(chrome://devtools/skin/images/dock-side.svg);
   --dock-undock-image: url(chrome://devtools/skin/images/dock-undock.svg);
 
   --command-paintflashing-image: url(images/command-paintflashing.svg);
   --command-screenshot-image: url(images/command-screenshot.svg);
   --command-responsive-image: url(images/command-responsivemode.svg);
   --command-scratchpad-image: url(images/tool-scratchpad.svg);
   --command-pick-image: url(images/command-pick.svg);
+  --command-pick-accessibility-image: url(images/command-pick-accessibility.svg);
   --command-frames-image: url(images/command-frames.svg);
   --command-splitconsole-image: url(images/command-console.svg);
   --command-noautohide-image: url(images/command-noautohide.svg);
   --command-rulers-image: url(images/command-rulers.svg);
   --command-measure-image: url(images/command-measure.svg);
 }
 
 .theme-firebug {
@@ -226,24 +227,16 @@
 #toolbox-dock-side::before {
   background-image: var(--dock-side-image);
 }
 
 #toolbox-dock-window::before {
   background-image: var(--dock-undock-image);
 }
 
-#toolbox-dock-bottom-minimize::before {
-  background-image: url("chrome://devtools/skin/images/dock-bottom-minimize@2x.png");
-}
-
-#toolbox-dock-bottom-minimize.minimized::before {
-  background-image: url("chrome://devtools/skin/images/dock-bottom-maximize@2x.png");
-}
-
 /* Command buttons */
 
 .command-button,
 #toolbox-controls > button,
 #toolbox-dock-buttons > button {
   /* !important is needed to override .devtools-button rules in common.css */
   padding: 0 !important;
   margin: 0 !important;
@@ -274,16 +267,20 @@
 #command-button-scratchpad::before {
   background-image: var(--command-scratchpad-image);
 }
 
 #command-button-pick::before {
   background-image: var(--command-pick-image);
 }
 
+#command-button-pick.accessibility::before {
+  background-image: var(--command-pick-accessibility-image);
+}
+
 #command-button-splitconsole::before {
   background-image: var(--command-splitconsole-image);
 }
 
 #command-button-noautohide::before {
   background-image: var(--command-noautohide-image);
 }
 
--- a/devtools/startup/devtools-startup.js
+++ b/devtools/startup/devtools-startup.js
@@ -162,16 +162,22 @@ XPCOMUtils.defineLazyGetter(this, "KeySh
       modifiers: "shift"
     },
     // Key for opening the DOM Panel
     {
       toolId: "dom",
       shortcut: KeyShortcutsBundle.GetStringFromName("dom.commandkey"),
       modifiers
     },
+    // Key for opening the Accessibility Panel
+    {
+      toolId: "accessibility",
+      shortcut: KeyShortcutsBundle.GetStringFromName("accessibility.commandkey"),
+      modifiers
+    },
   ];
 });
 
 function DevToolsStartup() {
   this.onEnabledPrefChanged = this.onEnabledPrefChanged.bind(this);
   this.onWindowReady = this.onWindowReady.bind(this);
 }
 
--- a/devtools/startup/locales/en-US/key-shortcuts.properties
+++ b/devtools/startup/locales/en-US/key-shortcuts.properties
@@ -60,8 +60,12 @@ performance.commandkey=VK_F5
 
 # LOCALIZATION NOTE (storage.commandkey):
 # Key pressed to open a toolbox with the storage panel selected
 storage.commandkey=VK_F9
 
 # LOCALIZATION NOTE (dom.commandkey):
 # Key pressed to open a toolbox with the DOM panel selected
 dom.commandkey=W
+
+# LOCALIZATION NOTE (accessibility.commandkey):
+# Key pressed to open a toolbox with the accessibility panel selected
+accessibility.commandkey=Y
--- a/dom/base/ChromeMessageBroadcaster.cpp
+++ b/dom/base/ChromeMessageBroadcaster.cpp
@@ -2,22 +2,23 @@
 /* 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/ContentParent.h"
 #include "mozilla/dom/MessageManagerBinding.h"
 
 namespace mozilla {
 namespace dom {
 
-ChromeMessageBroadcaster::ChromeMessageBroadcaster(nsFrameMessageManager* aParentManager,
+ChromeMessageBroadcaster::ChromeMessageBroadcaster(ChromeMessageBroadcaster* aParentManager,
                                                    MessageManagerFlags aFlags)
   : MessageListenerManager(nullptr, aParentManager,
                            aFlags |
                            MessageManagerFlags::MM_BROADCASTER |
                            MessageManagerFlags::MM_CHROME)
 {
   if (mIsProcessManager) {
     mozilla::HoldJSObjects(this);
@@ -38,10 +39,33 @@ JSObject*
 ChromeMessageBroadcaster::WrapObject(JSContext* aCx,
                                      JS::Handle<JSObject*> aGivenProto)
 {
   MOZ_ASSERT(nsContentUtils::IsSystemCaller(aCx));
 
   return ChromeMessageBroadcasterBinding::Wrap(aCx, this, aGivenProto);
 }
 
+void
+ChromeMessageBroadcaster::ReleaseCachedProcesses()
+{
+  ContentParent::ReleaseCachedProcesses();
+}
+
+void
+ChromeMessageBroadcaster::AddChildManager(MessageListenerManager* aManager)
+{
+  mChildManagers.AppendElement(aManager);
+
+  RefPtr<nsFrameMessageManager> kungfuDeathGrip = this;
+  RefPtr<nsFrameMessageManager> kungfuDeathGrip2 = aManager;
+
+  LoadPendingScripts(this, aManager);
+}
+
+void
+ChromeMessageBroadcaster::RemoveChildManager(MessageListenerManager* aManager)
+{
+  mChildManagers.RemoveElement(aManager);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/ChromeMessageBroadcaster.h
+++ b/dom/base/ChromeMessageBroadcaster.h
@@ -17,79 +17,91 @@ class ChromeMessageBroadcaster final : p
 public:
   explicit ChromeMessageBroadcaster(MessageManagerFlags aFlags)
     : ChromeMessageBroadcaster(nullptr, aFlags)
   {
     MOZ_ASSERT(!(aFlags & ~(MessageManagerFlags::MM_GLOBAL |
                             MessageManagerFlags::MM_PROCESSMANAGER |
                             MessageManagerFlags::MM_OWNSCALLBACK)));
   }
-  explicit ChromeMessageBroadcaster(nsFrameMessageManager* aParentManager)
+  explicit ChromeMessageBroadcaster(ChromeMessageBroadcaster* aParentManager)
     : ChromeMessageBroadcaster(aParentManager, MessageManagerFlags::MM_NONE)
   {}
 
+  static ChromeMessageBroadcaster* From(nsFrameMessageManager* aManager)
+  {
+    if (aManager->IsBroadcaster() && aManager->IsChrome()) {
+      return static_cast<ChromeMessageBroadcaster*>(aManager);
+    }
+    return nullptr;
+  }
+
   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
+  void ReleaseCachedProcesses();
 
   // 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 RemoveDelayedProcessScript(const nsAString& aURL)
+  {
+    RemoveDelayedScript(aURL);
+  }
   void GetDelayedProcessScripts(JSContext* aCx,
                                 nsTArray<nsTArray<JS::Value>>& aScripts,
                                 mozilla::ErrorResult& aError)
   {
     GetDelayedScripts(aCx, aScripts, aError);
   }
 
   // GlobalProcessScriptLoader
-  // XPCOM GetInitialProcessData is OK
+  using nsFrameMessageManager::GetInitialProcessData;
 
   // 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 RemoveDelayedFrameScript(const nsAString& aURL)
+  {
+    RemoveDelayedScript(aURL);
+  }
   void GetDelayedFrameScripts(JSContext* aCx,
                               nsTArray<nsTArray<JS::Value>>& aScripts,
                               mozilla::ErrorResult& aError)
   {
     GetDelayedScripts(aCx, aScripts, aError);
   }
 
+  void AddChildManager(MessageListenerManager* aManager);
+  void RemoveChildManager(MessageListenerManager* aManager);
+
 private:
-  ChromeMessageBroadcaster(nsFrameMessageManager* aParentManager,
+  ChromeMessageBroadcaster(ChromeMessageBroadcaster* aParentManager,
                            MessageManagerFlags aFlags);
   virtual ~ChromeMessageBroadcaster();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ChromeMessageBroadcaster_h
--- a/dom/base/ChromeMessageSender.cpp
+++ b/dom/base/ChromeMessageSender.cpp
@@ -6,17 +6,17 @@
 
 #include "mozilla/dom/ChromeMessageSender.h"
 #include "mozilla/dom/MessageManagerBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 ChromeMessageSender::ChromeMessageSender(ipc::MessageManagerCallback* aCallback,
-                                         nsFrameMessageManager* aParentManager,
+                                         ChromeMessageBroadcaster* 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
--- a/dom/base/ChromeMessageSender.h
+++ b/dom/base/ChromeMessageSender.h
@@ -7,50 +7,55 @@
 #ifndef mozilla_dom_ChromeMessageSender_h
 #define mozilla_dom_ChromeMessageSender_h
 
 #include "mozilla/dom/MessageSender.h"
 
 namespace mozilla {
 namespace dom {
 
+class ChromeMessageBroadcaster;
+
 class ChromeMessageSender final : public MessageSender
 {
 public:
   ChromeMessageSender(ipc::MessageManagerCallback* aCallback,
-                      nsFrameMessageManager* aParentManager,
+                      ChromeMessageBroadcaster* 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 RemoveDelayedProcessScript(const nsAString& aURL)
+  {
+    RemoveDelayedScript(aURL);
+  }
   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 RemoveDelayedFrameScript(const nsAString& aURL)
+  {
+    RemoveDelayedScript(aURL);
+  }
   void GetDelayedFrameScripts(JSContext* aCx,
                               nsTArray<nsTArray<JS::Value>>& aScripts,
                               mozilla::ErrorResult& aError)
   {
     GetDelayedScripts(aCx, aScripts, aError);
   }
 };
 
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1698,20 +1698,19 @@ Element::BindToTree(nsIDocument* aDocume
   if (IsHTMLElement()) {
     SetDirOnBind(this, aParent);
   }
 
   uint32_t editableDescendantCount = 0;
 
   UpdateEditableState(false);
 
-  // If we had a pre-existing XBL binding,
-  // we might have anonymous children that also need to be told that they are
-  // moving.
-  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) && !GetShadowRoot()) {
+  // If we had a pre-existing XBL binding, we might have anonymous children that
+  // also need to be told that they are moving.
+  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
     nsXBLBinding* binding =
       OwnerDoc()->BindingManager()->GetBindingWithContent(this);
 
     if (binding) {
       binding->BindAnonymousContent(
         binding->GetAnonymousContent(),
         this,
         binding->PrototypeBinding()->ChromeOnlyContent());
@@ -1763,18 +1762,17 @@ Element::BindToTree(nsIDocument* aDocume
   if (MayHaveStyle() && !IsXULElement()) {
     // XXXbz if we already have a style attr parsed, this won't do
     // anything... need to fix that.
     // If MayHaveStyle() is true, we must be an nsStyledElement
     static_cast<nsStyledElement*>(this)->ReparseStyleAttribute(false, false);
   }
 
   // Call BindToTree on shadow root children.
-  ShadowRoot* shadowRoot = GetShadowRoot();
-  if (shadowRoot) {
+  if (ShadowRoot* shadowRoot = GetShadowRoot()) {
     shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
     for (nsIContent* child = shadowRoot->GetFirstChild(); child;
          child = child->GetNextSibling()) {
       rv = child->BindToTree(nullptr, shadowRoot,
                              shadowRoot->GetBindingParent(),
                              aCompileEventHandlers);
       NS_ENSURE_SUCCESS(rv, rv);
     }
@@ -1984,21 +1982,19 @@ Element::UnbindFromTree(bool aDeep, bool
       slots->mBindingParent = nullptr;
     }
     if (aNullParent || !mParent->IsInShadowTree()) {
       slots->mContainingShadow = nullptr;
     }
   }
 
   if (document) {
-    // Notify XBL- & nsIAnonymousContentCreator-generated
-    // anonymous content that the document is changing.
-    // Unlike XBL, bindings for web components shadow DOM
-    // do not get uninstalled.
-    if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) && !GetShadowRoot()) {
+    if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
+      // Notify XBL- & nsIAnonymousContentCreator-generated anonymous content
+      // that the document is changing.
       nsContentUtils::AddScriptRunner(
         new RemoveFromBindingManagerRunnable(
           document->BindingManager(), this, document));
       nsXBLBinding* binding =
         document->BindingManager()->GetBindingWithContent(this);
       if (binding) {
         nsXBLBinding::UnbindAnonymousContent(
           document,
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -718,34 +718,47 @@ public:
   // aParsedValue receives the old value of the attribute. That's useful if
   // either the input or output value of aParsedValue is StoresOwnData.
   nsresult SetParsedAttr(int32_t aNameSpaceID, nsAtom* aName, nsAtom* aPrefix,
                          nsAttrValue& aParsedValue, bool aNotify);
   /**
    * Get the current value of the attribute. This returns a form that is
    * suitable for passing back into SetAttr.
    *
-   * @param aNameSpaceID the namespace of the attr
+   * @param aNameSpaceID the namespace of the attr (defaults to
+                         kNameSpaceID_None in the overload that omits this arg)
    * @param aName the name of the attr
    * @param aResult the value (may legitimately be the empty string) [OUT]
    * @returns true if the attribute was set (even when set to empty string)
    *          false when not set.
    * GetAttr is not inlined on purpose, to keep down codesize from all the
    * inlined nsAttrValue bits for C++ callers.
    */
   bool GetAttr(int32_t aNameSpaceID, nsAtom* aName, nsAString& aResult) const;
 
+  bool GetAttr(nsAtom* aName, nsAString& aResult) const
+  {
+    return GetAttr(kNameSpaceID_None, aName, aResult);
+  }
+
   /**
    * Determine if an attribute has been set (empty string or otherwise).
    *
-   * @param aNameSpaceId the namespace id of the attribute
+   * @param aNameSpaceId the namespace id of the attribute (defaults to
+                         kNameSpaceID_None in the overload that omits this arg)
    * @param aAttr the attribute name
    * @return whether an attribute exists
    */
   inline bool HasAttr(int32_t aNameSpaceID, nsAtom* aName) const;
+
+  bool HasAttr(nsAtom* aAttr) const
+  {
+    return HasAttr(kNameSpaceID_None, aAttr);
+  }
+
   /**
    * Test whether this Element's given attribute has the given value.  If the
    * attribute is not set at all, this will return false.
    *
    * @param aNameSpaceID The namespace ID of the attribute.  Must not
    *                     be kNameSpaceID_Unknown.
    * @param aName The name atom of the attribute.  Must not be null.
    * @param aValue The value to compare to.
--- a/dom/base/MessageListenerManager.cpp
+++ b/dom/base/MessageListenerManager.cpp
@@ -5,17 +5,17 @@
  * 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,
+                                               ChromeMessageBroadcaster* aParentManager,
                                                ipc::MessageManagerFlags aFlags)
   : nsFrameMessageManager(aCallback, aFlags),
     mParentManager(aParentManager)
 {
 }
 
 MessageListenerManager::~MessageListenerManager()
 {
--- a/dom/base/MessageListenerManager.h
+++ b/dom/base/MessageListenerManager.h
@@ -17,37 +17,37 @@ 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()
+  ChromeMessageBroadcaster* GetParentObject()
   {
-    return ToSupports(mParentManager.get());
+    return mParentManager;
   }
 
-  virtual nsFrameMessageManager* GetParentManager() override
+  virtual ChromeMessageBroadcaster* 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,
+                         ChromeMessageBroadcaster* aParentManager,
                          MessageManagerFlags aFlags);
   virtual ~MessageListenerManager();
 
-  RefPtr<nsFrameMessageManager> mParentManager;
+  RefPtr<ChromeMessageBroadcaster> mParentManager;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_MessageListenerManager_h
new file mode 100644
--- /dev/null
+++ b/dom/base/MessageManagerGlobal.cpp
@@ -0,0 +1,66 @@
+/* -*- 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/MessageManagerGlobal.h"
+#include "mozilla/IntentionalCrash.h"
+#include "mozilla/dom/DOMPrefs.h"
+#include "nsContentUtils.h"
+
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+namespace mozilla {
+namespace dom {
+
+void
+MessageManagerGlobal::Dump(const nsAString& aStr)
+{
+  if (!DOMPrefs::DumpEnabled()) {
+    return;
+  }
+
+#ifdef ANDROID
+  __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", NS_ConvertUTF16toUTF8(aStr).get());
+#endif
+#ifdef XP_WIN
+  if (IsDebuggerPresent()) {
+    OutputDebugStringW(PromiseFlatString(aStr).get());
+  }
+#endif
+  fputs(NS_ConvertUTF16toUTF8(aStr).get(), stdout);
+  fflush(stdout);
+}
+
+void
+MessageManagerGlobal::PrivateNoteIntentionalCrash(ErrorResult& aError)
+{
+  if (XRE_IsContentProcess()) {
+    NoteIntentionalCrash("tab");
+    return;
+  }
+  aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
+}
+
+void
+MessageManagerGlobal::Atob(const nsAString& aAsciiString, nsAString& aBase64Data,
+                           ErrorResult& aError)
+{
+  aError = nsContentUtils::Atob(aAsciiString, aBase64Data);
+}
+
+void
+MessageManagerGlobal::Btoa(const nsAString& aBase64Data, nsAString& aAsciiString,
+                           ErrorResult& aError)
+{
+  aError = nsContentUtils::Btoa(aBase64Data, aAsciiString);
+}
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/base/MessageManagerGlobal.h
+++ b/dom/base/MessageManagerGlobal.h
@@ -73,17 +73,17 @@ public:
   {
     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)
+  already_AddRefed<ChromeMessageSender> GetProcessMessageManager(mozilla::ErrorResult& aError)
   {
     if (!mMessageManager) {
       aError.Throw(NS_ERROR_NULL_POINTER);
       return nullptr;
     }
     return mMessageManager->GetProcessMessageManager(aError);
   }
 
@@ -122,54 +122,26 @@ public:
       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)
+  void Dump(const nsAString& aStr);
+  void PrivateNoteIntentionalCrash(ErrorResult& aError);
+  void Atob(const nsAString& aAsciiString, nsAString& aBase64Data, ErrorResult& aError);
+  void Btoa(const nsAString& aBase64Data, nsAString& aAsciiString, ErrorResult& aError);
+
+  void MarkForCC()
   {
-    if (!mMessageManager) {
-      aError.Throw(NS_ERROR_NULL_POINTER);
-      return;
+    if (mMessageManager) {
+      mMessageManager->MarkForCC();
     }
-    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;
--- a/dom/base/MessageSender.cpp
+++ b/dom/base/MessageSender.cpp
@@ -20,14 +20,14 @@ MessageSender::InitWithCallback(ipc::Mes
   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]);
+    LoadScript(mPendingScripts[i], false, mPendingScriptsGlobalStates[i], IgnoreErrors());
   }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/MessageSender.h
+++ b/dom/base/MessageSender.h
@@ -14,17 +14,17 @@ namespace dom {
 
 class MessageSender : public MessageListenerManager
 {
 public:
   void InitWithCallback(ipc::MessageManagerCallback* aCallback);
 
 protected:
   MessageSender(ipc::MessageManagerCallback* aCallback,
-                nsFrameMessageManager* aParentManager,
+                ChromeMessageBroadcaster* aParentManager,
                 MessageManagerFlags aFlags)
     : MessageListenerManager(aCallback, aParentManager, aFlags)
   {}
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -69,17 +69,17 @@ PostMessageEvent::Run()
   sourceDocument.swap(mSourceDocument);
 
   // If we bailed before this point we're going to leak mMessage, but
   // that's probably better than crashing.
 
   RefPtr<nsGlobalWindowInner> targetWindow;
   if (mTargetWindow->IsClosedOrClosing() ||
       !(targetWindow = mTargetWindow->GetCurrentInnerWindowInternal()) ||
-      targetWindow->IsClosedOrClosing())
+      targetWindow->IsDying())
     return NS_OK;
 
   JSAutoCompartment ac(cx, targetWindow->GetWrapper());
 
   // Ensure that any origin which might have been provided is the origin of this
   // window's document.  Note that we do this *now* instead of when postMessage
   // is called because the target window might have been navigated to a
   // different location between then and now.  If this check happened when
--- a/dom/base/ProcessGlobal.cpp
+++ b/dom/base/ProcessGlobal.cpp
@@ -54,30 +54,28 @@ ProcessGlobal::GetOwnPropertyNames(JSCon
 {
   JS::Rooted<JSObject*> thisObj(aCx, GetWrapper());
   GetSystemBindingNames(aCx, thisObj, aNames, aEnumerableOnly, aRv);
 }
 
 ProcessGlobal*
 ProcessGlobal::Get()
 {
-  nsCOMPtr<nsISyncMessageSender> service = do_GetService(NS_CHILDPROCESSMESSAGEMANAGER_CONTRACTID);
+  nsCOMPtr<nsIGlobalObject> 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)
+void
 ProcessGlobal::MarkForCC()
 {
   MarkScopesForCC();
-  return MessageManagerGlobal::MarkForCC();
+  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
@@ -91,21 +89,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Pr
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
   tmp->nsMessageManagerScriptExecutor::Unlink();
   tmp->UnlinkHostObjectURIs();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ProcessGlobal)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentProcessMessageManager)
-  NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMessageSender)
   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_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ProcessGlobal)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ProcessGlobal)
 
--- a/dom/base/ProcessGlobal.h
+++ b/dom/base/ProcessGlobal.h
@@ -21,17 +21,17 @@
 #include "nsServiceManagerUtils.h"
 #include "nsWeakReference.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
 class ProcessGlobal :
-  public nsIContentProcessMessageManager,
+  public nsIMessageSender,
   public nsMessageManagerScriptExecutor,
   public nsIGlobalObject,
   public nsIScriptObjectPrincipal,
   public nsSupportsWeakReference,
   public ipc::MessageManagerCallback,
   public MessageManagerGlobal,
   public nsWrapperCache
 {
@@ -41,64 +41,54 @@ public:
   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;
+  using MessageManagerGlobal::GetProcessMessageManager;
 
   bool Init();
 
   static ProcessGlobal* Get();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(ProcessGlobal, nsIContentProcessMessageManager)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(ProcessGlobal, nsIMessageSender)
+
+  void MarkForCC();
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override
   {
     MOZ_CRASH("We should never get here!");
   }
   virtual bool WrapGlobalObject(JSContext* aCx,
                                 JS::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 GetWrapper();
   }
   virtual nsIPrincipal* GetPrincipal() override { return mPrincipal; }
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -76,22 +76,45 @@ ShadowRoot::ShadowRoot(Element* aElement
 
 ShadowRoot::~ShadowRoot()
 {
   if (auto* host = GetHost()) {
     // mHost may have been unlinked.
     host->RemoveMutationObserver(this);
   }
 
+  if (IsComposedDocParticipant()) {
+    OwnerDoc()->RemoveComposedDocShadowRoot(*this);
+  }
+
+  MOZ_DIAGNOSTIC_ASSERT(!OwnerDoc()->IsComposedDocShadowRoot(*this));
+
   UnsetFlags(NODE_IS_IN_SHADOW_TREE);
 
   // nsINode destructor expects mSubtreeRoot == this.
   SetSubtreeRootPointer(this);
 }
 
+void
+ShadowRoot::SetIsComposedDocParticipant(bool aIsComposedDocParticipant)
+{
+  bool changed = mIsComposedDocParticipant != aIsComposedDocParticipant;
+  mIsComposedDocParticipant = aIsComposedDocParticipant;
+  if (!changed) {
+    return;
+  }
+
+  nsIDocument* doc = OwnerDoc();
+  if (IsComposedDocParticipant()) {
+    doc->AddComposedDocShadowRoot(*this);
+  } else {
+    doc->RemoveComposedDocShadowRoot(*this);
+  }
+}
+
 JSObject*
 ShadowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return mozilla::dom::ShadowRootBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther)
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -181,20 +181,17 @@ public:
   void GetInnerHTML(nsAString& aInnerHTML);
   void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
 
   bool IsComposedDocParticipant() const
   {
     return mIsComposedDocParticipant;
   }
 
-  void SetIsComposedDocParticipant(bool aIsComposedDocParticipant)
-  {
-    mIsComposedDocParticipant = aIsComposedDocParticipant;
-  }
+  void SetIsComposedDocParticipant(bool aIsComposedDocParticipant);
 
   nsresult GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
 
 protected:
   // FIXME(emilio): This will need to become more fine-grained.
   void ApplicableRulesChanged();
 
   virtual ~ShadowRoot();
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -413,17 +413,17 @@ TimeoutManager::TimeoutManager(nsGlobalW
 {
   MOZ_LOG(gLog, LogLevel::Debug,
           ("TimeoutManager %p created, tracking bucketing %s\n",
            this, gAnnotateTrackingChannels ? "enabled" : "disabled"));
 }
 
 TimeoutManager::~TimeoutManager()
 {
-  MOZ_DIAGNOSTIC_ASSERT(mWindow.AsInner()->InnerObjectsFreed());
+  MOZ_DIAGNOSTIC_ASSERT(mWindow.IsDying());
   MOZ_DIAGNOSTIC_ASSERT(!mThrottleTimeoutsTimer);
 
   mExecutor->Shutdown();
 
   MOZ_LOG(gLog, LogLevel::Debug,
           ("TimeoutManager %p destroyed\n", this));
 }
 
@@ -1292,17 +1292,17 @@ TimeoutManager::OnDocumentLoaded()
     MaybeStartThrottleTimeout();
   }
 }
 
 void
 TimeoutManager::MaybeStartThrottleTimeout()
 {
   if (gTimeoutThrottlingDelay <= 0 ||
-      mWindow.AsInner()->InnerObjectsFreed() || mWindow.IsSuspended()) {
+      mWindow.IsDying() || mWindow.IsSuspended()) {
     return;
   }
 
   MOZ_DIAGNOSTIC_ASSERT(!mThrottleTimeouts);
 
   MOZ_LOG(gLog, LogLevel::Debug,
           ("TimeoutManager %p delaying tracking timeout throttling by %dms\n",
            this, gTimeoutThrottlingDelay));
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -273,16 +273,17 @@ UNIFIED_SOURCES += [
     'IdleRequest.cpp',
     'IDTracker.cpp',
     'ImageEncoder.cpp',
     'ImageTracker.cpp',
     'IntlUtils.cpp',
     'Link.cpp',
     'Location.cpp',
     'MessageListenerManager.cpp',
+    'MessageManagerGlobal.cpp',
     'MessageSender.cpp',
     'Navigator.cpp',
     'NodeInfo.cpp',
     'NodeIterator.cpp',
     'nsAttrAndChildArray.cpp',
     'nsAttrValue.cpp',
     'nsAttrValueOrString.cpp',
     'nsCCUncollectableMarker.cpp',
--- a/dom/base/nsCCUncollectableMarker.cpp
+++ b/dom/base/nsCCUncollectableMarker.cpp
@@ -81,50 +81,46 @@ nsCCUncollectableMarker::Init()
   NS_ENSURE_SUCCESS(rv, rv);
 
   sInited = true;
 
   return NS_OK;
 }
 
 static void
-MarkChildMessageManagers(nsIMessageBroadcaster* aMM)
+MarkChildMessageManagers(ChromeMessageBroadcaster* aMM)
 {
   aMM->MarkForCC();
 
-  uint32_t tabChildCount = 0;
-  aMM->GetChildCount(&tabChildCount);
+  uint32_t tabChildCount = aMM->ChildCount();
   for (uint32_t j = 0; j < tabChildCount; ++j) {
-    nsCOMPtr<nsIMessageListenerManager> childMM;
-    aMM->GetChildAt(j, getter_AddRefs(childMM));
+    RefPtr<MessageListenerManager> childMM = aMM->GetChildAt(j);
     if (!childMM) {
       continue;
     }
 
-    nsCOMPtr<nsIMessageBroadcaster> strongNonLeafMM = do_QueryInterface(childMM);
-    nsIMessageBroadcaster* nonLeafMM = strongNonLeafMM;
+    RefPtr<ChromeMessageBroadcaster> strongNonLeafMM =
+      ChromeMessageBroadcaster::From(childMM);
+    ChromeMessageBroadcaster* nonLeafMM = strongNonLeafMM;
 
-    nsCOMPtr<nsIMessageSender> strongTabMM = do_QueryInterface(childMM);
-    nsIMessageSender* tabMM = strongTabMM;
+    MessageListenerManager* tabMM = childMM;
 
     strongNonLeafMM = nullptr;
-    strongTabMM = nullptr;
     childMM = nullptr;
 
     if (nonLeafMM) {
       MarkChildMessageManagers(nonLeafMM);
       continue;
     }
 
     tabMM->MarkForCC();
 
     //XXX hack warning, but works, since we know that
     //    callback is frameloader.
-    mozilla::dom::ipc::MessageManagerCallback* cb =
-      static_cast<nsFrameMessageManager*>(tabMM)->GetCallback();
+    mozilla::dom::ipc::MessageManagerCallback* cb = tabMM->GetCallback();
     if (cb) {
       nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
       EventTarget* et = fl->GetTabChildGlobalAsEventTarget();
       if (!et) {
         continue;
       }
       static_cast<nsInProcessTabChildGlobal*>(et)->MarkForCC();
       EventListenerManager* elm = et->GetExistingListenerManager();
@@ -145,37 +141,35 @@ MarkMessageManagers()
       pg->MarkForCC();
     }
   }
 
   // The global message manager only exists in the root process.
   if (!XRE_IsParentProcess()) {
     return;
   }
-  nsCOMPtr<nsIMessageBroadcaster> strongGlobalMM =
-    do_GetService("@mozilla.org/globalmessagemanager;1");
+  RefPtr<ChromeMessageBroadcaster> strongGlobalMM =
+    nsFrameMessageManager::GetGlobalMessageManager();
   if (!strongGlobalMM) {
     return;
   }
-  nsIMessageBroadcaster* globalMM = strongGlobalMM;
+  ChromeMessageBroadcaster* globalMM = strongGlobalMM;
   strongGlobalMM = nullptr;
   MarkChildMessageManagers(globalMM);
 
   if (nsFrameMessageManager::sParentProcessManager) {
     nsFrameMessageManager::sParentProcessManager->MarkForCC();
-    uint32_t childCount = 0;
-    nsFrameMessageManager::sParentProcessManager->GetChildCount(&childCount);
+    uint32_t childCount = nsFrameMessageManager::sParentProcessManager->ChildCount();
     for (uint32_t i = 0; i < childCount; ++i) {
-      nsCOMPtr<nsIMessageListenerManager> childMM;
-      nsFrameMessageManager::sParentProcessManager->
-        GetChildAt(i, getter_AddRefs(childMM));
+      RefPtr<MessageListenerManager> childMM =
+        nsFrameMessageManager::sParentProcessManager->GetChildAt(i);
       if (!childMM) {
         continue;
       }
-      nsIMessageListenerManager* child = childMM;
+      MessageListenerManager* child = childMM;
       childMM = nullptr;
       child->MarkForCC();
     }
   }
   if (nsFrameMessageManager::sSameProcessParentManager) {
     nsFrameMessageManager::sSameProcessParentManager->MarkForCC();
   }
 }
@@ -291,17 +285,17 @@ MarkWindowList(nsISimpleEnumerator* aWin
          iter) {
     if (nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(iter)) {
       nsCOMPtr<nsIDocShell> rootDocShell = window->GetDocShell();
 
       MarkDocShell(rootDocShell, aCleanupJS);
 
       RefPtr<TabChild> tabChild = TabChild::GetFrom(rootDocShell);
       if (tabChild) {
-        nsCOMPtr<nsIContentFrameMessageManager> mm = tabChild->GetMessageManager();
+        RefPtr<TabChildGlobal> mm = tabChild->GetMessageManager();
         if (mm) {
           // MarkForCC ends up calling UnmarkGray on message listeners, which
           // TraceBlackJS can't do yet.
           mm->MarkForCC();
         }
       }
     }
   }
@@ -472,19 +466,19 @@ mozilla::dom::TraceBlackJS(JSTracer* aTr
   }
 #endif
 
   if (!nsCCUncollectableMarker::sGeneration) {
     return;
   }
 
   if (nsFrameMessageManager::GetChildProcessManager()) {
-    nsIContentProcessMessageManager* pg = ProcessGlobal::Get();
+    ProcessGlobal* pg = ProcessGlobal::Get();
     if (pg) {
-      mozilla::TraceScriptHolder(pg, aTrc);
+      mozilla::TraceScriptHolder(ToSupports(pg), aTrc);
     }
   }
 
   // Mark globals of active windows black.
   nsGlobalWindowOuter::OuterWindowByIdTable* windowsById =
     nsGlobalWindowOuter::GetWindowsTable();
   if (windowsById) {
     for (auto iter = windowsById->Iter(); !iter.Done(); iter.Next()) {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7773,41 +7773,36 @@ nsContentUtils::GetHostOrIPv6WithBracket
   if (NS_FAILED(rv)) {
     return rv;
   }
   CopyUTF8toUTF16(hostname, aHost);
   return NS_OK;
 }
 
 bool
-nsContentUtils::CallOnAllRemoteChildren(nsIMessageBroadcaster* aManager,
+nsContentUtils::CallOnAllRemoteChildren(ChromeMessageBroadcaster* aManager,
                                         CallOnRemoteChildFunction aCallback,
                                         void* aArg)
 {
-  uint32_t tabChildCount = 0;
-  aManager->GetChildCount(&tabChildCount);
+  uint32_t tabChildCount = aManager->ChildCount();
   for (uint32_t j = 0; j < tabChildCount; ++j) {
-    nsCOMPtr<nsIMessageListenerManager> childMM;
-    aManager->GetChildAt(j, getter_AddRefs(childMM));
+    RefPtr<MessageListenerManager> childMM = aManager->GetChildAt(j);
     if (!childMM) {
       continue;
     }
 
-    nsCOMPtr<nsIMessageBroadcaster> nonLeafMM = do_QueryInterface(childMM);
+    RefPtr<ChromeMessageBroadcaster> nonLeafMM = ChromeMessageBroadcaster::From(childMM);
     if (nonLeafMM) {
       if (CallOnAllRemoteChildren(nonLeafMM, aCallback, aArg)) {
         return true;
       }
       continue;
     }
 
-    nsCOMPtr<nsIMessageSender> tabMM = do_QueryInterface(childMM);
-
-    mozilla::dom::ipc::MessageManagerCallback* cb =
-     static_cast<nsFrameMessageManager*>(tabMM.get())->GetCallback();
+    mozilla::dom::ipc::MessageManagerCallback* cb = childMM->GetCallback();
     if (cb) {
       nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
       TabParent* remote = TabParent::GetFrom(fl);
       if (remote && aCallback) {
         if (aCallback(remote, aArg)) {
           return true;
         }
       }
@@ -7817,20 +7812,19 @@ nsContentUtils::CallOnAllRemoteChildren(
   return false;
 }
 
 void
 nsContentUtils::CallOnAllRemoteChildren(nsPIDOMWindowOuter* aWindow,
                                         CallOnRemoteChildFunction aCallback,
                                         void* aArg)
 {
-  nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(aWindow));
-  if (chromeWindow) {
-    nsCOMPtr<nsIMessageBroadcaster> windowMM;
-    chromeWindow->GetMessageManager(getter_AddRefs(windowMM));
+  nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(aWindow);
+  if (window->IsChromeWindow()) {
+    RefPtr<ChromeMessageBroadcaster> windowMM = window->GetMessageManager();
     if (windowMM) {
       CallOnAllRemoteChildren(windowMM, aCallback, aArg);
     }
   }
 }
 
 struct UIStateChangeInfo {
   UIStateChangeType mShowAccelerators;
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -77,17 +77,16 @@ class nsIDragSession;
 class nsIEventTarget;
 class nsIFragmentContentSink;
 class nsIFrame;
 class nsIImageLoadingContent;
 class nsIInterfaceRequestor;
 class nsIIOService;
 class nsILoadInfo;
 class nsILoadGroup;
-class nsIMessageBroadcaster;
 class nsNameSpaceManager;
 class nsIObserver;
 class nsIParser;
 class nsIPluginTag;
 class nsIPresShell;
 class nsIPrincipal;
 class nsIRequest;
 class nsIRunnable;
@@ -124,16 +123,17 @@ template<class T> class nsReadingIterato
 
 namespace mozilla {
 class Dispatcher;
 class ErrorResult;
 class EventListenerManager;
 class HTMLEditor;
 
 namespace dom {
+class ChromeMessageBroadcaster;
 struct CustomElementDefinition;
 class DocumentFragment;
 class Element;
 class EventTarget;
 class HTMLInputElement;
 class IPCDataTransfer;
 class IPCDataTransferItem;
 struct LifecycleCallbackArgs;
@@ -3338,17 +3338,17 @@ private:
   static mozilla::EventClassID
   GetEventClassIDFromMessage(mozilla::EventMessage aEventMessage);
 
   // Fills in aInfo with the tokens from the supplied autocomplete attribute.
   static AutocompleteAttrState InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrVal,
                                                                       mozilla::dom::AutocompleteInfo& aInfo,
                                                                       bool aGrantAllValidValue = false);
 
-  static bool CallOnAllRemoteChildren(nsIMessageBroadcaster* aManager,
+  static bool CallOnAllRemoteChildren(mozilla::dom::ChromeMessageBroadcaster* aManager,
                                       CallOnRemoteChildFunction aCallback,
                                       void* aArg);
 
   /**
    * Gets the current cookie lifetime policy and cookie behavior for a given
    * principal by checking with preferences and the permission manager.
    *
    * Used in the implementation of InternalStorageAllowedForPrincipal.
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -8633,16 +8633,18 @@ nsIDocument::MutationEventDispatched(nsI
 void
 nsIDocument::DestroyElementMaps()
 {
 #ifdef DEBUG
   mStyledLinksCleared = true;
 #endif
   mStyledLinks.Clear();
   mIdentifierMap.Clear();
+  mComposedShadowRoots.Clear();
+  mResponsiveContent.Clear();
   IncrementExpandoGeneration(*this);
 }
 
 void
 nsIDocument::RefreshLinkHrefs()
 {
   // Get a list of all links we know about.  We will reset them, which will
   // remove them from the document, so we need a copy of what is in the
--- a/dom/base/nsFocusManager.h
+++ b/dom/base/nsFocusManager.h
@@ -19,17 +19,16 @@
 #define FOCUSMETHOD_MASK 0xF000
 #define FOCUSMETHODANDRING_MASK 0xF0F000
 
 #define FOCUSMANAGER_CONTRACTID "@mozilla.org/focus-manager;1"
 
 class nsIContent;
 class nsIDocShellTreeItem;
 class nsPIDOMWindowOuter;
-class nsIMessageBroadcaster;
 
 namespace mozilla {
 namespace dom {
 class TabParent;
 }
 }
 
 struct nsDelayedBlurOrFocusEvent;
@@ -174,22 +173,16 @@ protected:
 
   /**
    * Ensure that the widget associated with the currently focused window is
    * focused at the widget level.
    */
   void EnsureCurrentWidgetFocused();
 
   /**
-   * Iterate over the children of the message broadcaster and notify them
-   * of the activation change.
-   */
-  void ActivateOrDeactivateChildren(nsIMessageBroadcaster* aManager, bool aActive);
-
-  /**
    * Activate or deactivate the window and send the activate/deactivate events.
    */
   void ActivateOrDeactivate(nsPIDOMWindowOuter* aWindow, bool aActive);
 
   /**
    * Blur whatever is currently focused and focus aNewContent. aFlags is a
    * bitmask of the flags defined in nsIFocusManager. If aFocusChanged is
    * true, then the focus has actually shifted and the caret position will be
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -2878,17 +2878,17 @@ nsFrameLoader::DoSendAsyncMessage(JSCont
     }
     return rv;
   }
 
   // We don't have any targets to send our asynchronous message to.
   return NS_ERROR_UNEXPECTED;
 }
 
-already_AddRefed<nsIMessageSender>
+already_AddRefed<MessageSender>
 nsFrameLoader::GetMessageManager()
 {
   EnsureMessageManager();
   return do_AddRef(mMessageManager);
 }
 
 nsresult
 nsFrameLoader::EnsureMessageManager()
@@ -2904,38 +2904,37 @@ nsFrameLoader::EnsureMessageManager()
       !IsRemoteFrame() &&
       !(mOwnerContent->IsXULElement() &&
         mOwnerContent->AttrValueIs(kNameSpaceID_None,
                                    nsGkAtoms::forcemessagemanager,
                                    nsGkAtoms::_true, eCaseMatters))) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIDOMChromeWindow> chromeWindow =
-    do_QueryInterface(GetOwnerDoc()->GetWindow());
-  nsCOMPtr<nsIMessageBroadcaster> parentManager;
-
-  if (chromeWindow) {
+  RefPtr<nsGlobalWindowOuter> window =
+    nsGlobalWindowOuter::Cast(GetOwnerDoc()->GetWindow());
+  RefPtr<ChromeMessageBroadcaster> parentManager;
+
+  if (window && window->IsChromeWindow()) {
     nsAutoString messagemanagergroup;
     if (mOwnerContent->IsXULElement() &&
         mOwnerContent->GetAttr(kNameSpaceID_None,
                                nsGkAtoms::messagemanagergroup,
                                messagemanagergroup)) {
-      chromeWindow->GetGroupMessageManager(messagemanagergroup, getter_AddRefs(parentManager));
+      parentManager = window->GetGroupMessageManager(messagemanagergroup);
     }
 
     if (!parentManager) {
-      chromeWindow->GetMessageManager(getter_AddRefs(parentManager));
+      parentManager = window->GetMessageManager();
     }
   } else {
-    parentManager = do_GetService("@mozilla.org/globalmessagemanager;1");
+    parentManager = nsFrameMessageManager::GetGlobalMessageManager();
   }
 
-  mMessageManager = new ChromeMessageSender(nullptr,
-                                            static_cast<nsFrameMessageManager*>(parentManager.get()));
+  mMessageManager = new ChromeMessageSender(nullptr, parentManager);
   if (!IsRemoteFrame()) {
     nsresult rv = MaybeCreateDocShell();
     if (NS_FAILED(rv)) {
       return rv;
     }
     NS_ASSERTION(mDocShell,
                  "MaybeCreateDocShell succeeded, but null mDocShell");
     if (!mDocShell) {
@@ -3201,17 +3200,18 @@ nsFrameLoader::InitializeBrowserAPI()
     nsresult rv = EnsureMessageManager();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
     if (mMessageManager) {
       mMessageManager->LoadFrameScript(
         NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"),
         /* allowDelayedLoad = */ true,
-        /* aRunInGlobalScope */ true);
+        /* aRunInGlobalScope */ true,
+        IgnoreErrors());
     }
   }
   nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
   if (browserFrame) {
     browserFrame->InitializeBrowserAPI();
   }
 }
 
@@ -3376,17 +3376,17 @@ nsFrameLoader::PopulateUserContextIdFrom
       aAttr.mUserContextId = userContextIdStr.ToInteger(&rv);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   return NS_OK;
 }
 
-nsIMessageSender*
+ChromeMessageSender*
 nsFrameLoader::GetProcessMessageManager() const
 {
   return mRemoteBrowser ? mRemoteBrowser->Manager()->GetMessageManager()
                         : nullptr;
 };
 
 JSObject*
 nsFrameLoader::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -45,16 +45,17 @@ class nsIWebProgressListener;
 
 namespace mozilla {
 
 class OriginAttributes;
 
 namespace dom {
 class ChromeMessageSender;
 class ContentParent;
+class MessageSender;
 class PBrowserParent;
 class Promise;
 class TabParent;
 class MutableTabContext;
 
 namespace ipc {
 class StructuredCloneData;
 } // namespace ipc
@@ -166,17 +167,17 @@ public:
              mozilla::ErrorResult& aRv);
 
   void StartPersistence(uint64_t aOuterWindowID,
                         nsIWebBrowserPersistDocumentReceiver* aRecv,
                         mozilla::ErrorResult& aRv);
 
   // WebIDL getters
 
-  already_AddRefed<nsIMessageSender> GetMessageManager();
+  already_AddRefed<mozilla::dom::MessageSender> GetMessageManager();
 
   already_AddRefed<Element> GetOwnerElement();
 
   uint32_t LazyWidth() const;
 
   uint32_t LazyHeight() const;
 
   uint64_t ChildID() const { return mChildID; }
@@ -321,17 +322,17 @@ public:
    */
   void ApplySandboxFlags(uint32_t sandboxFlags);
 
   void GetURL(nsString& aURL, nsIPrincipal** aTriggeringPrincipal);
 
   // Properly retrieves documentSize of any subdocument type.
   nsresult GetWindowDimensions(nsIntRect& aRect);
 
-  virtual nsIMessageSender* GetProcessMessageManager() const override;
+  virtual mozilla::dom::ChromeMessageSender* GetProcessMessageManager() const override;
 
   // public because a callback needs these.
   RefPtr<mozilla::dom::ChromeMessageSender> mMessageManager;
   RefPtr<nsInProcessTabChildGlobal> mChildMessageManager;
 
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
 private:
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -24,22 +24,21 @@
 #include "nsIInputStream.h"
 #include "nsIXULRuntime.h"
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "nsIMemoryReporter.h"
 #include "nsIProtocolHandler.h"
 #include "nsIScriptSecurityManager.h"
 #include "xpcpublic.h"
+#include "mozilla/ClearOnShutdown.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"
@@ -53,21 +52,17 @@
 #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>
 # if defined(SendMessage)
 #  undef SendMessage
 # endif
 #endif
 
 #ifdef FUZZING
 #include "MessageManagerFuzzer.h"
 #endif
@@ -109,16 +104,26 @@ nsFrameMessageManager::~nsFrameMessageMa
       delete mozilla::dom::SameProcessMessageQueue::Get();
     }
     if (this == sSameProcessParentManager) {
       sSameProcessParentManager = nullptr;
     }
   }
 }
 
+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);
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsFrameMessageManager)
@@ -133,56 +138,44 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildManagers)
   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,
-   * both of which descend from nsIMessageListenerManager. QI'ing to
-   * nsIMessageListenerManager is therefore ambiguous and needs explicit casts
-   * depending on which child interface applies. */
-  NS_INTERFACE_MAP_ENTRY_AGGREGATED(nsIMessageListenerManager,
-                                    (mIsBroadcaster ?
-                                       static_cast<nsIMessageListenerManager*>(
-                                         static_cast<nsIMessageBroadcaster*>(this)) :
-                                       static_cast<nsIMessageListenerManager*>(
-                                         static_cast<nsIMessageSender*>(this))))
-
-  /* Message managers in child process implement nsIMessageSender and
-     nsISyncMessageSender.  Message managers in the chrome process are
+  /* Message managers in child process implement nsIMessageSender.
+     Message managers in the chrome process are
      either broadcasters (if they have subordinate/child message
      managers) or they're simple message senders. */
-  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISyncMessageSender, !mChrome)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMessageSender, !mChrome || !mIsBroadcaster)
-  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMessageBroadcaster, mChrome && mIsBroadcaster)
 
   /* nsIContentFrameMessageManager is accessible only in TabChildGlobal. */
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIContentFrameMessageManager,
                                      !mChrome && !mIsProcessManager)
-
-  /* Frame message managers (non-process message managers) support nsIFrameScriptLoader. */
-  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFrameScriptLoader,
-                                     mChrome && !mIsProcessManager)
-
-  /* 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_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager)
 
+nsresult 
+MessageManagerCallback::DoGetRemoteType(nsAString& aRemoteType) const
+{
+  aRemoteType.Truncate();
+  mozilla::dom::ChromeMessageSender* parent = GetProcessMessageManager();
+  if (!parent) {
+    return NS_OK;
+  }
+
+  ErrorResult rv;
+  parent->GetRemoteType(aRemoteType, rv);
+  return rv.StealNSResult();
+}
+
 bool
 MessageManagerCallback::BuildClonedMessageDataForParent(nsIContentParent* aParent,
                                                         StructuredCloneData& aData,
                                                         ClonedMessageData& aClonedData)
 {
   return aData.BuildClonedMessageDataForParent(aParent, aClonedData);
 }
 
@@ -215,191 +208,137 @@ SameProcessCpowHolder::ToObject(JSContex
   if (!mObj) {
     return true;
   }
 
   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([]() {
+  auto listeners = mListeners.LookupForAdd(aMessageName).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) {
+    MessageListener* strongListener = listeners->ElementAt(i).mStrongListener;
+    if (strongListener && *strongListener == aListener) {
       return;
     }
   }
 
   nsMessageListenerInfo* entry = listeners->AppendElement();
-  entry->mStrongListener = Move(aListener);
+  entry->mStrongListener = &aListener;
   entry->mListenWhenClosed = aListenWhenClosed;
 }
 
 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);
+    mListeners.Get(aMessageName);
   if (listeners) {
     uint32_t len = listeners->Length();
     for (uint32_t i = 0; i < len; ++i) {
-      if (listeners->ElementAt(i).mStrongListener == aListener) {
+      MessageListener* strongListener = listeners->ElementAt(i).mStrongListener;
+      if (strongListener && *strongListener == aListener) {
         listeners->RemoveElementAt(i);
         return;
       }
     }
   }
 }
 
-static already_AddRefed<nsIMessageListener>
+static already_AddRefed<nsISupports>
 ToXPCOMMessageListener(MessageListener& aListener)
 {
-  return MessageListenerHolder(&aListener).ToXPCOMCallback();
+  return CallbackObjectHolder<mozilla::dom::MessageListener,
+                              nsISupports>(&aListener).ToXPCOMCallback();
 }
 
 void
 nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessageName,
                                               MessageListener& aListener,
                                               ErrorResult& aError)
 {
-  nsCOMPtr<nsIMessageListener> listener(ToXPCOMMessageListener(aListener));
-  if (!listener) {
-    aError.Throw(NS_ERROR_FAILURE);
+  nsCOMPtr<nsISupports> listener(ToXPCOMMessageListener(aListener));
+  nsWeakPtr weak = do_GetWeakReference(listener);
+  if (!weak) {
+    aError.Throw(NS_ERROR_NO_INTERFACE);
     return;
   }
 
-  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);
-
 #ifdef DEBUG
   // It's technically possible that one object X could give two different
   // nsIWeakReference*'s when you do_GetWeakReference(X).  We really don't want
   // this to happen; it will break e.g. RemoveWeakMessageListener.  So let's
   // check that we're not getting ourselves into that situation.
-  nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener);
+  nsCOMPtr<nsISupports> canonical = do_QueryInterface(listener);
   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++) {
       nsWeakPtr weakListener = listeners->ElementAt(i).mWeakListener;
       if (weakListener) {
         nsCOMPtr<nsISupports> otherCanonical = do_QueryReferent(weakListener);
         MOZ_ASSERT((canonical == otherCanonical) == (weak == weakListener));
       }
     }
   }
 #endif
 
-  auto listeners = mListeners.LookupForAdd(aMessage).OrInsert([]() {
+  auto listeners = mListeners.LookupForAdd(aMessageName).OrInsert([]() {
       return new nsAutoTObserverArray<nsMessageListenerInfo, 1>();
     });
   uint32_t len = listeners->Length();
   for (uint32_t i = 0; i < len; ++i) {
     if (listeners->ElementAt(i).mWeakListener == weak) {
-      return NS_OK;
+      return;
     }
   }
 
   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);
+  nsCOMPtr<nsISupports> listener(ToXPCOMMessageListener(aListener));
+  nsWeakPtr weak = do_GetWeakReference(listener);
+  if (!weak) {
+    aError.Throw(NS_ERROR_NO_INTERFACE);
     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 =
-    mListeners.Get(aMessage);
+    mListeners.Get(aMessageName);
   if (!listeners) {
-    return NS_OK;
+    return;
   }
 
   uint32_t len = listeners->Length();
   for (uint32_t i = 0; i < len; ++i) {
     if (listeners->ElementAt(i).mWeakListener == weak) {
       listeners->RemoveElementAt(i);
-      return NS_OK;
+      return;
     }
   }
-
-  return NS_OK;
 }
 
-// nsIFrameScriptLoader
-
 void
 nsFrameMessageManager::LoadScript(const nsAString& aURL,
                                   bool aAllowDelayedLoad,
                                   bool aRunInGlobalScope,
                                   ErrorResult& aError)
 {
   if (aAllowDelayedLoad) {
     // Cache for future windows or frames
@@ -484,65 +423,16 @@ nsFrameMessageManager::GetDelayedScripts
 
   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)
-{
-  ErrorResult rv;
-  LoadScript(aURL, aAllowDelayedLoad, aRunInGlobalScope, rv);
-  return rv.StealNSResult();
-}
-
-NS_IMETHODIMP
-nsFrameMessageManager::RemoveDelayedFrameScript(const nsAString& 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)
-{
-  ErrorResult rv;
-  LoadScript(aURL, aAllowDelayedLoad, false, rv);
-  return rv.StealNSResult();
-}
-
-NS_IMETHODIMP
-nsFrameMessageManager::RemoveDelayedProcessScript(const nsAString& aURL)
-{
-  RemoveDelayedScript(aURL);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsFrameMessageManager::GetDelayedProcessScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList)
-{
-  return GetDelayedScripts(aCx, aList);
-}
-
 static bool
 JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData)
 {
   nsAString* result = static_cast<nsAString*>(aData);
   result->Append(static_cast<const char16_t*>(aBuf),
                  static_cast<uint32_t>(aLen));
   return true;
 }
@@ -595,46 +485,19 @@ GetParamsForMessage(JSContext* aCx,
   if (NS_WARN_IF(rv.Failed())) {
     rv.SuppressException();
     return false;
   }
 
   return true;
 }
 
-// nsISyncMessageSender
 
 static bool sSendingSyncMessage = false;
 
-NS_IMETHODIMP
-nsFrameMessageManager::SendSyncMessage(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)
-{
-  return SendMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx, aArgc,
-                     aRetval, true);
-}
-
-NS_IMETHODIMP
-nsFrameMessageManager::SendRpcMessage(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)
-{
-  return SendMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx, aArgc,
-                     aRetval, false);
-}
-
 static bool
 AllowMessage(size_t aDataLength, const nsAString& aMessageName)
 {
   // A message includes more than structured clone data, so subtract
   // 20KB to make it more likely that a message within this bound won't
   // result in an overly large IPC message.
   static const size_t kMaxMessageSize = IPC::Channel::kMaximumMessageSize - 20 * 1024;
   if (aDataLength < kMaxMessageSize) {
@@ -645,63 +508,16 @@ AllowMessage(size_t aDataLength, const n
   messageName.StripTaggedASCII(ASCIIMask::Mask0to9());
 
   Telemetry::Accumulate(Telemetry::REJECTED_MESSAGE_MANAGER_MESSAGE,
                         messageName);
 
   return false;
 }
 
-nsresult
-nsFrameMessageManager::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)
-{
-  AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
-    "nsFrameMessageManager::SendMessage", EVENTS, aMessageName);
-
-  aRetval.setUndefined();
-
-  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,
@@ -874,139 +690,16 @@ nsFrameMessageManager::DispatchAsyncMess
     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,
-                                        JSContext* aCx,
-                                        uint8_t aArgc)
-{
-  return DispatchAsyncMessage(aMessageName, aJSON, aObjects, aPrincipal,
-                              aTransfers, aCx, aArgc);
-}
-
-
-// nsIMessageBroadcaster
-
-NS_IMETHODIMP
-nsFrameMessageManager::BroadcastAsyncMessage(const nsAString& aMessageName,
-                                             JS::Handle<JS::Value> aJSON,
-                                             JS::Handle<JS::Value> aObjects,
-                                             JSContext* aCx,
-                                             uint8_t aArgc)
-{
-  return DispatchAsyncMessage(aMessageName, aJSON, aObjects, nullptr,
-                              JS::UndefinedHandleValue, aCx, aArgc);
-}
-
-NS_IMETHODIMP
-nsFrameMessageManager::GetChildCount(uint32_t* aChildCount)
-{
-  *aChildCount = mChildManagers.Length();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsFrameMessageManager::GetChildAt(uint32_t aIndex,
-                                  nsIMessageListenerManager** 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;
-}
-
-// nsIContentFrameMessageManager
-
-NS_IMETHODIMP
-nsFrameMessageManager::Dump(const nsAString& aStr)
-{
-  if (!DOMPrefs::DumpEnabled()) {
-    return NS_OK;
-  }
-
-#ifdef ANDROID
-  __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", NS_ConvertUTF16toUTF8(aStr).get());
-#endif
-#ifdef XP_WIN
-  if (IsDebuggerPresent()) {
-    OutputDebugStringW(PromiseFlatString(aStr).get());
-  }
-#endif
-  fputs(NS_ConvertUTF16toUTF8(aStr).get(), stdout);
-  fflush(stdout);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsFrameMessageManager::PrivateNoteIntentionalCrash()
-{
-  if (XRE_IsContentProcess()) {
-    mozilla::NoteIntentionalCrash("tab");
-    return NS_OK;
-  }
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-nsFrameMessageManager::GetContent(mozIDOMWindowProxy** aContent)
-{
-  *aContent = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsFrameMessageManager::GetDocShell(nsIDocShell** aDocShell)
-{
-  *aDocShell = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsFrameMessageManager::GetTabEventTarget(nsIEventTarget** aTarget)
-{
-  *aTarget = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsFrameMessageManager::Btoa(const nsAString& aBinaryData,
-                            nsAString& aAsciiBase64String)
-{
-  return nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
-}
-
-NS_IMETHODIMP
-nsFrameMessageManager::Atob(const nsAString& aAsciiString,
-                            nsAString& aBinaryData)
-{
-  return nsContentUtils::Atob(aAsciiString, aBinaryData);
-}
-
 class MMListenerRemover
 {
 public:
   explicit MMListenerRemover(nsFrameMessageManager* aMM)
     : mWasHandlingMessage(aMM->mHandlingMessage)
     , mMM(aMM)
   {
     mMM->mHandlingMessage = true;
@@ -1021,42 +714,27 @@ public:
     }
   }
 
   bool mWasHandlingMessage;
   RefPtr<nsFrameMessageManager> mMM;
 };
 
 
-// nsIMessageListener
-
-nsresult
-nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
-                                      nsFrameLoader* aTargetFrameLoader,
-                                      const nsAString& aMessage,
-                                      bool aIsSync,
-                                      StructuredCloneData* aCloneData,
-                                      mozilla::jsipc::CpowHolder* aCpows,
-                                      nsIPrincipal* aPrincipal,
-                                      nsTArray<StructuredCloneData>* aRetVal)
-{
-  return ReceiveMessage(aTarget, aTargetFrameLoader, mClosed, aMessage, aIsSync,
-                        aCloneData, aCpows, aPrincipal, aRetVal);
-}
-
-nsresult
+void
 nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
                                       nsFrameLoader* aTargetFrameLoader,
                                       bool aTargetClosed,
                                       const nsAString& aMessage,
                                       bool aIsSync,
                                       StructuredCloneData* aCloneData,
                                       mozilla::jsipc::CpowHolder* aCpows,
                                       nsIPrincipal* aPrincipal,
-                                      nsTArray<StructuredCloneData>* aRetVal)
+                                      nsTArray<StructuredCloneData>* aRetVal,
+                                      ErrorResult& aError)
 {
   MOZ_ASSERT(aTarget);
 
   nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
     mListeners.Get(aMessage);
   if (listeners) {
 
     MMListenerRemover lr(this);
@@ -1078,32 +756,21 @@ nsFrameMessageManager::ReceiveMessage(ns
       if (!listener.mListenWhenClosed && aTargetClosed) {
         continue;
       }
 
       JS::RootingContext* rcx = RootingCx();
       JS::Rooted<JSObject*> object(rcx);
 
       RefPtr<MessageListener> webIDLListener;
-      if (!weakListener && listener.mStrongListener.HasWebIDLCallback()) {
-        webIDLListener = listener.mStrongListener.GetWebIDLCallback();
-      } else {
-        webIDLListener = nullptr;
-      }
-
-      if (webIDLListener) {
+      if (!weakListener) {
+        webIDLListener = listener.mStrongListener;
         object = webIDLListener->CallbackOrNull();
       } else {
-        nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
-        if (weakListener) {
-          wrappedJS = do_QueryInterface(weakListener);
-        } else {
-          wrappedJS = do_QueryInterface(listener.mStrongListener.GetXPCOMCallback());
-        }
-
+        nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = do_QueryInterface(weakListener);
         if (!wrappedJS) {
           continue;
         }
 
         object = wrappedJS->GetJSObject();
       }
 
       if (!object) {
@@ -1111,48 +778,48 @@ nsFrameMessageManager::ReceiveMessage(ns
       }
 
       AutoEntryScript aes(object, "message manager handler");
       JSContext* cx = aes.cx();
 
       RootedDictionary<ReceiveMessageArgument> argument(cx);
 
       JS::Rooted<JSObject*> cpows(cx);
-      if (aCpows) {
-        if (!aCpows->ToObject(cx, &cpows)) {
-          return NS_ERROR_UNEXPECTED;
-        }
+      if (aCpows && !aCpows->ToObject(cx, &cpows)) {
+        aError.Throw(NS_ERROR_UNEXPECTED);
+        return;
       }
 
       if (!cpows) {
         cpows = JS_NewPlainObject(cx);
         if (!cpows) {
-          return NS_ERROR_UNEXPECTED;
+          aError.Throw(NS_ERROR_UNEXPECTED);
+          return;
         }
       }
       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();
+        aCloneData->Read(cx, &json, aError);
+        if (NS_WARN_IF(aError.Failed())) {
+          aError.SuppressException();
           JS_ClearPendingException(cx);
-          return NS_OK;
+          return;
         }
       }
       argument.mData = json;
       argument.mJson = json;
 
       // Get cloned MessagePort from StructuredCloneData.
       if (aCloneData) {
         Sequence<OwningNonNull<MessagePort>> ports;
         if (!aCloneData->TakeTransferredPortsAsSequence(ports)) {
-          return NS_ERROR_FAILURE;
+          aError.Throw(NS_ERROR_FAILURE);
+          return;
         }
         argument.mPorts.Construct(Move(ports));
       }
 
       argument.mName = aMessage;
       argument.mPrincipal = aPrincipal;
       argument.mSync = aIsSync;
       argument.mTarget = aTarget;
@@ -1167,70 +834,75 @@ nsFrameMessageManager::ReceiveMessage(ns
         // 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);
+        aError = nsContentUtils::WrapNative(cx, defaultThisValue, &thisValue);
+        if (aError.Failed()) {
+          return;
+        }
       }
 
       JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());
       if (webIDLListener) {
-        ErrorResult rv;
-        webIDLListener->ReceiveMessage(thisValue, argument, &rval, rv);
-        if (rv.Failed()) {
+        webIDLListener->ReceiveMessage(thisValue, argument, &rval, aError);
+        if (aError.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();
+          aError.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;
+            aError.Throw(NS_ERROR_UNEXPECTED);
+            return;
           }
 
           // Check if the object is even callable.
-          NS_ENSURE_STATE(JS::IsCallable(&funval.toObject()));
+          if (!JS::IsCallable(&funval.toObject())) {
+            aError.Throw(NS_ERROR_UNEXPECTED);
+            return;
+          }
           thisValue.setObject(*object);
         }
 
         JS::Rooted<JS::Value> argv(cx);
         if (!ToJSValue(cx, argument, &argv)) {
-          return NS_ERROR_UNEXPECTED;
+          aError.Throw(NS_ERROR_UNEXPECTED);
+          return;
         }
 
         {
           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())) {
+        data->Write(cx, rval, aError);
+        if (NS_WARN_IF(aError.Failed())) {
           aRetVal->RemoveLastElement();
           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",
@@ -1242,59 +914,40 @@ nsFrameMessageManager::ReceiveMessage(ns
           continue;
         }
       }
     }
   }
 
   RefPtr<nsFrameMessageManager> kungFuDeathGrip = GetParentManager();
   if (kungFuDeathGrip) {
-    return kungFuDeathGrip->ReceiveMessage(aTarget, aTargetFrameLoader,
-                                           aTargetClosed, aMessage,
-                                           aIsSync, aCloneData,
-                                           aCpows, aPrincipal,
-                                           aRetVal);
+    kungFuDeathGrip->ReceiveMessage(aTarget, aTargetFrameLoader, aTargetClosed, aMessage,
+                                    aIsSync, aCloneData, aCpows, aPrincipal, aRetVal,
+                                    aError);
   }
-  return NS_OK;
-}
-
-void
-nsFrameMessageManager::AddChildManager(MessageListenerManager* 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.
   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]);
+    aChildMM->LoadScript(aManager->mPendingScripts[i],
+                         false,
+                         aManager->mPendingScriptsGlobalStates[i],
+                         IgnoreErrors());
   }
 }
 
 void
 nsFrameMessageManager::LoadPendingScripts()
 {
   RefPtr<nsFrameMessageManager> kungfuDeathGrip = this;
   LoadPendingScripts(this, this);
@@ -1381,72 +1034,52 @@ nsFrameMessageManager::GetInitialProcess
     }
 
     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);
+    // Create it first through do_GetService and use the cached pointer in
+    // sParentProcessManager.
+    nsCOMPtr<nsISupports> ppmm = do_GetService("@mozilla.org/parentprocessmessagemanager;1");
+    sParentProcessManager->GetInitialProcessData(aCx, &init, aError);
+    if (aError.Failed()) {
+      return;
+    }
     mInitialProcessData = init;
   }
 
   if (!JS_WrapValue(aCx, &init)) {
     aError.NoteJSContextException(aCx);
     return;
   }
   aInitialProcessData.set(init);
 }
 
-NS_IMETHODIMP
-nsFrameMessageManager::GetInitialProcessData(JSContext* aCx, JS::MutableHandleValue aResult)
-{
-  ErrorResult rv;
-  GetInitialProcessData(aCx, aResult, rv);
-  return rv.StealNSResult();
-}
-
-already_AddRefed<nsIMessageSender>
+already_AddRefed<ChromeMessageSender>
 nsFrameMessageManager::GetProcessMessageManager(ErrorResult& aError)
 {
-  nsCOMPtr<nsIMessageSender> pmm;
+  RefPtr<ChromeMessageSender> pmm;
   if (mCallback) {
     pmm = mCallback->GetProcessMessageManager();
   }
   return pmm.forget();
 }
 
-NS_IMETHODIMP
-nsFrameMessageManager::GetProcessMessageManager(nsIMessageSender** aPMM)
-{
-  ErrorResult rv;
-  *aPMM = GetProcessMessageManager(rv).take();
-  return rv.StealNSResult();
-}
-
 void
 nsFrameMessageManager::GetRemoteType(nsAString& aRemoteType, ErrorResult& aError) const
 {
   aRemoteType.Truncate();
   if (mCallback) {
     aError = mCallback->DoGetRemoteType(aRemoteType);
   }
 }
 
-NS_IMETHODIMP
-nsFrameMessageManager::GetRemoteType(nsAString& aRemoteType)
-{
-  ErrorResult rv;
-  GetRemoteType(aRemoteType, rv);
-  return rv.StealNSResult();
-}
-
 namespace {
 
 struct MessageManagerReferentCount
 {
   MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
   size_t mStrong;
   size_t mWeakAlive;
   size_t mWeakDead;
@@ -1562,30 +1195,26 @@ ReportReferentCount(const char* aManager
            nsPrintfCString("A message in the %s message manager with a "
                            "suspiciously large number of referents (symptom "
                            "of a leak).", aManagerType));
   }
 
 #undef REPORT
 }
 
+static StaticRefPtr<ChromeMessageBroadcaster> sGlobalMessageManager;
+
 NS_IMETHODIMP
 MessageManagerReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
                                        nsISupports* aData, bool aAnonymize)
 {
-  if (XRE_IsParentProcess()) {
-    nsCOMPtr<nsIMessageBroadcaster> globalmm =
-      do_GetService("@mozilla.org/globalmessagemanager;1");
-    if (globalmm) {
-      RefPtr<nsFrameMessageManager> mm =
-        static_cast<nsFrameMessageManager*>(globalmm.get());
-      MessageManagerReferentCount count;
-      CountReferents(mm, &count);
-      ReportReferentCount("global-manager", count, aHandleReport, aData);
-    }
+  if (XRE_IsParentProcess() && sGlobalMessageManager) {
+    MessageManagerReferentCount count;
+    CountReferents(sGlobalMessageManager, &count);
+    ReportReferentCount("global-manager", count, aHandleReport, aData);
   }
 
   if (nsFrameMessageManager::sParentProcessManager) {
     MessageManagerReferentCount count;
     CountReferents(nsFrameMessageManager::sParentProcessManager, &count);
     ReportReferentCount("parent-process-manager", count, aHandleReport, aData);
   }
 
@@ -1596,25 +1225,35 @@ MessageManagerReporter::CollectReports(n
   }
 
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
 
-nsresult
-NS_NewGlobalMessageManager(nsIMessageBroadcaster** aResult)
+already_AddRefed<ChromeMessageBroadcaster>
+nsFrameMessageManager::GetGlobalMessageManager()
 {
-  NS_ENSURE_TRUE(XRE_IsParentProcess(),
-                 NS_ERROR_NOT_AVAILABLE);
-  RefPtr<nsFrameMessageManager> mm =
-    new ChromeMessageBroadcaster(MessageManagerFlags::MM_GLOBAL);
-  RegisterStrongMemoryReporter(new MessageManagerReporter());
-  mm.forget(aResult);
+  RefPtr<ChromeMessageBroadcaster> mm;
+  if (sGlobalMessageManager) {
+    mm = sGlobalMessageManager;
+  } else {
+    sGlobalMessageManager = mm =
+      new ChromeMessageBroadcaster(MessageManagerFlags::MM_GLOBAL);
+    ClearOnShutdown(&sGlobalMessageManager);
+    RegisterStrongMemoryReporter(new MessageManagerReporter());
+  }
+  return mm.forget();
+}
+
+nsresult
+NS_NewGlobalMessageManager(nsISupports** aResult)
+{
+  *aResult = nsFrameMessageManager::GetGlobalMessageManager().take();
   return NS_OK;
 }
 
 nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>*
   nsMessageManagerScriptExecutor::sCachedScripts = nullptr;
 StaticRefPtr<nsScriptCacheCleaner> nsMessageManagerScriptExecutor::sScriptCacheCleaner;
 
 void
@@ -1854,17 +1493,17 @@ nsMessageManagerScriptExecutor::MarkScop
   for (uint32_t i = 0; i < mAnonymousGlobalScopes.Length(); ++i) {
     mAnonymousGlobalScopes[i].exposeToActiveJS();
   }
 }
 
 NS_IMPL_ISUPPORTS(nsScriptCacheCleaner, nsIObserver)
 
 ChildProcessMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr;
-nsFrameMessageManager* nsFrameMessageManager::sParentProcessManager = nullptr;
+ChromeMessageBroadcaster* nsFrameMessageManager::sParentProcessManager = nullptr;
 nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = nullptr;
 
 class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase,
                                          public Runnable
 {
 public:
   nsAsyncMessageToSameProcessChild(JS::RootingContext* aRootingCx,
                                    JS::Handle<JSObject*> aCpows)
@@ -2040,18 +1679,18 @@ public:
                              bool aIsSync) override
   {
     SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
     queue->Flush();
 
     if (nsFrameMessageManager::sSameProcessParentManager) {
       SameProcessCpowHolder cpows(JS::RootingContext::get(aCx), aCpows);
       RefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
-      ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), nullptr, aMessage,
-                          true, &aData, &cpows, aPrincipal, aRetVal);
+      ppm->ReceiveMessage(ppm, nullptr, aMessage, true, &aData, &cpows, aPrincipal,
+                          aRetVal, IgnoreErrors());
     }
     return true;
   }
 
   nsresult DoSendAsyncMessage(JSContext* aCx,
                               const nsAString& aMessage,
                               StructuredCloneData& aData,
                               JS::Handle<JSObject *> aCpows,
@@ -2070,34 +1709,34 @@ public:
     return NS_OK;
   }
 
 };
 
 
 // This creates the global parent process message manager.
 nsresult
-NS_NewParentProcessMessageManager(nsIMessageBroadcaster** aResult)
+NS_NewParentProcessMessageManager(nsISupports** aResult)
 {
   NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager,
                "Re-creating sParentProcessManager");
-  RefPtr<nsFrameMessageManager> mm =
+  RefPtr<ChromeMessageBroadcaster> mm =
     new ChromeMessageBroadcaster(MessageManagerFlags::MM_PROCESSMANAGER);
   nsFrameMessageManager::sParentProcessManager = mm;
   nsFrameMessageManager::NewProcessMessageManager(false); // Create same process message manager.
   mm.forget(aResult);
   return NS_OK;
 }
 
 
 ChromeMessageSender*
 nsFrameMessageManager::NewProcessMessageManager(bool aIsRemote)
 {
   if (!nsFrameMessageManager::sParentProcessManager) {
-     nsCOMPtr<nsIMessageBroadcaster> dummy =
+     nsCOMPtr<nsISupports> dummy =
        do_GetService("@mozilla.org/parentprocessmessagemanager;1");
   }
 
   MOZ_ASSERT(nsFrameMessageManager::sParentProcessManager,
              "parent process manager not created");
   ChromeMessageSender* mm;
   if (aIsRemote) {
     // Callback is set in ContentParent::InitInternal so that the process has
@@ -2111,58 +1750,52 @@ nsFrameMessageManager::NewProcessMessage
                                  MessageManagerFlags::MM_PROCESSMANAGER |
                                  MessageManagerFlags::MM_OWNSCALLBACK);
     sSameProcessParentManager = mm;
   }
   return mm;
 }
 
 nsresult
-NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult)
+NS_NewChildProcessMessageManager(nsISupports** aResult)
 {
   NS_ASSERTION(!nsFrameMessageManager::GetChildProcessManager(),
                "Re-creating sChildProcessManager");
 
   MessageManagerCallback* cb;
   if (XRE_IsParentProcess()) {
     cb = new SameChildProcessMessageManagerCallback();
   } else {
     cb = new ChildProcessMessageManagerCallback();
     RegisterStrongMemoryReporter(new MessageManagerReporter());
   }
   auto* mm = new ChildProcessMessageManager(cb);
   nsFrameMessageManager::SetChildProcessManager(mm);
   RefPtr<ProcessGlobal> global = new ProcessGlobal(mm);
   NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED);
-  global.forget(aResult);
-  return NS_OK;
+  return CallQueryInterface(global, aResult);
 }
 
-bool
+void
 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++) {
-      MessageListenerHolder& strongListener = listeners->ElementAt(i).mStrongListener;
+      MessageListener* strongListener = listeners->ElementAt(i).mStrongListener;
       if (strongListener) {
-        if (strongListener.HasWebIDLCallback()) {
-          strongListener.GetWebIDLCallback()->MarkForCC();
-        } else {
-          xpc_TryUnmarkWrappedGrayObject(strongListener.GetXPCOMCallback());
-        }
+        strongListener->MarkForCC();
       }
     }
   }
 
   if (mRefCnt.IsPurple()) {
     mRefCnt.RemovePurple();
   }
-  return true;
 }
 
 nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase(JS::RootingContext* aRootingCx,
                                                              JS::Handle<JSObject*> aCpows)
   : mCpows(aRootingCx, aCpows)
 #ifdef DEBUG
   , mCalledInit(false)
 #endif
@@ -2195,11 +1828,11 @@ nsSameProcessAsyncMessageBase::ReceiveMe
 {
   // Make sure that we have called Init() and it has succeeded.
   MOZ_ASSERT(mCalledInit);
   if (aManager) {
     SameProcessCpowHolder cpows(RootingCx(), mCpows);
 
     RefPtr<nsFrameMessageManager> mm = aManager;
     mm->ReceiveMessage(aTarget, aTargetFrameLoader, mMessage, false, &mData,
-                       &cpows, mPrincipal, nullptr);
+                       &cpows, mPrincipal, nullptr, IgnoreErrors());
   }
 }
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -37,16 +37,17 @@
 class nsFrameLoader;
 
 namespace mozilla {
 namespace dom {
 
 class nsIContentParent;
 class nsIContentChild;
 class ChildProcessMessageManager;
+class ChromeMessageBroadcaster;
 class ChromeMessageSender;
 class ClonedMessageData;
 class MessageListener;
 class MessageListenerManager;
 class MessageManagerReporter;
 template<typename T> class Optional;
 
 namespace ipc {
@@ -90,30 +91,22 @@ public:
                                       const nsAString& aMessage,
                                       StructuredCloneData& aData,
                                       JS::Handle<JSObject*> aCpows,
                                       nsIPrincipal* aPrincipal)
   {
     return NS_OK;
   }
 
-  virtual nsIMessageSender* GetProcessMessageManager() const
+  virtual mozilla::dom::ChromeMessageSender* GetProcessMessageManager() const
   {
     return nullptr;
   }
 
-  virtual nsresult DoGetRemoteType(nsAString& aRemoteType) const
-  {
-    aRemoteType.Truncate();
-    nsIMessageSender* parent = GetProcessMessageManager();
-    if (parent) {
-      return parent->GetRemoteType(aRemoteType);
-    }
-    return NS_OK;
-  }
+  virtual nsresult DoGetRemoteType(nsAString& aRemoteType) const;
 
 protected:
   bool BuildClonedMessageDataForParent(nsIContentParent* aParent,
                                        StructuredCloneData& aData,
                                        ClonedMessageData& aClonedData);
   bool BuildClonedMessageDataForChild(nsIContentChild* aChild,
                                       StructuredCloneData& aData,
                                       ClonedMessageData& aClonedData);
@@ -121,66 +114,49 @@ 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;
   }
 
-  // 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;
+  // If mWeakListener is null then mStrongListener holds a MessageListener.
+  // If mWeakListener is non-null then mStrongListener contains null.
+  RefPtr<mozilla::dom::MessageListener> 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 : public nsIContentFrameMessageManager,
-                              public nsIMessageBroadcaster,
-                              public nsIFrameScriptLoader,
-                              public nsIGlobalProcessScriptLoader
+class nsFrameMessageManager : public nsIContentFrameMessageManager
 {
   friend class mozilla::dom::MessageManagerReporter;
   typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
 
 protected:
   typedef mozilla::dom::ipc::MessageManagerFlags MessageManagerFlags;
 
   nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
@@ -192,16 +168,18 @@ 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)
 
+  void MarkForCC();
+
   // 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);
@@ -218,17 +196,17 @@ public:
                         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>
+  already_AddRefed<mozilla::dom::ChromeMessageSender>
     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,
@@ -247,37 +225,32 @@ public:
     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 mozilla::dom::ChromeMessageSender*
   NewProcessMessageManager(bool aIsRemote);
 
-  nsresult ReceiveMessage(nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader,
-                          const nsAString& aMessage,
-                          bool aIsSync, StructuredCloneData* aCloneData,
-                          mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
-                          nsTArray<StructuredCloneData>* aRetVal);
+  void ReceiveMessage(nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader,
+                      const nsAString& aMessage, bool aIsSync,
+                      StructuredCloneData* aCloneData, mozilla::jsipc::CpowHolder* aCpows,
+                      nsIPrincipal* aPrincipal, nsTArray<StructuredCloneData>* aRetVal,
+                      mozilla::ErrorResult& aError)
+  {
+    ReceiveMessage(aTarget, aTargetFrameLoader, mClosed, aMessage, aIsSync, aCloneData,
+                   aCpows, aPrincipal, aRetVal, aError);
+  }
 
-  void AddChildManager(mozilla::dom::MessageListenerManager* aManager);
-  void RemoveChildManager(mozilla::dom::MessageListenerManager* aManager);
   void Disconnect(bool aRemoveFromParent = true);
   void Close();
 
   void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback);
 
   mozilla::dom::ipc::MessageManagerCallback* GetCallback()
   {
     return mCallback;
@@ -293,18 +266,22 @@ public:
 
   nsresult DispatchAsyncMessageInternal(JSContext* aCx,
                                         const nsAString& aMessage,
                                         StructuredCloneData& aData,
                                         JS::Handle<JSObject*> aCpows,
                                         nsIPrincipal* aPrincipal);
   bool IsGlobal() { return mGlobal; }
   bool IsBroadcaster() { return mIsBroadcaster; }
+  bool IsChrome() { return mChrome; }
 
-  static nsFrameMessageManager* GetParentProcessManager()
+  // GetGlobalMessageManager creates the global message manager if it hasn't been yet.
+  static already_AddRefed<mozilla::dom::ChromeMessageBroadcaster>
+    GetGlobalMessageManager();
+  static mozilla::dom::ChromeMessageBroadcaster* GetParentProcessManager()
   {
     return sParentProcessManager;
   }
   static mozilla::dom::ChildProcessMessageManager* GetChildProcessManager()
   {
     return sChildProcessManager;
   }
   static void SetChildProcessManager(mozilla::dom::ChildProcessMessageManager* aManager)
@@ -314,17 +291,17 @@ public:
 
   void SetInitialProcessData(JS::HandleValue aInitialData);
 
   void LoadPendingScripts();
 
 protected:
   friend class MMListenerRemover;
 
-  virtual nsFrameMessageManager* GetParentManager()
+  virtual mozilla::dom::ChromeMessageBroadcaster* GetParentManager()
   {
     return nullptr;
   }
   virtual void ClearParentManager(bool aRemove)
   {
   }
 
   void DispatchAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
@@ -346,21 +323,21 @@ protected:
                    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, nsFrameLoader* aTargetFrameLoader,
-                          bool aTargetClosed, const nsAString& aMessage,
-                          bool aIsSync, StructuredCloneData* aCloneData,
-                          mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
-                          nsTArray<StructuredCloneData>* aRetVal);
+  void ReceiveMessage(nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader,
+                      bool aTargetClosed, const nsAString& aMessage, bool aIsSync,
+                      StructuredCloneData* aCloneData, mozilla::jsipc::CpowHolder* aCpows,
+                      nsIPrincipal* aPrincipal, nsTArray<StructuredCloneData>* aRetVal,
+                      mozilla::ErrorResult& aError);
 
   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);
@@ -391,26 +368,20 @@ protected:
   nsAutoPtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback;
   nsTArray<nsString> mPendingScripts;
   nsTArray<bool> mPendingScriptsGlobalStates;
   JS::Heap<JS::Value> mInitialProcessData;
 
   void LoadPendingScripts(nsFrameMessageManager* aManager,
                           nsFrameMessageManager* aChildMM);
 public:
-  static nsFrameMessageManager* sParentProcessManager;
+  static mozilla::dom::ChromeMessageBroadcaster* sParentProcessManager;
   static nsFrameMessageManager* sSameProcessParentManager;
   static nsTArray<nsCOMPtr<nsIRunnable> >* sPendingSameProcessAsyncMessages;
 private:
-  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
    {
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -403,17 +403,17 @@ public:
   {
     return mWindow ? mWindow->EventTargetFor(TaskCategory::Other) : nullptr;
   }
 
 private:
   ~nsGlobalWindowObserver() = default;
 
   // This reference is non-owning and safe because it's cleared by
-  // nsGlobalWindowInner::CleanUp().
+  // nsGlobalWindowInner::FreeInnerObjects().
   nsGlobalWindowInner* MOZ_NON_OWNING_REF mWindow;
 };
 
 NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor)
 
 class IdleRequestExecutor;
 
 class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler
@@ -464,17 +464,17 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)
 
   NS_DECL_NSIRUNNABLE
   NS_DECL_NSINAMED
   nsresult Cancel() override;
   void SetDeadline(TimeStamp aDeadline) override;
 
-  bool IsCancelled() const { return !mWindow || mWindow->InnerObjectsFreed(); }
+  bool IsCancelled() const { return !mWindow || mWindow->IsDying(); }
   // Checks if aRequest shouldn't execute in the current idle period
   // since it has been queued from a chained call to
   // requestIdleCallback from within a running idle callback.
   bool IneligibleForCurrentIdlePeriod(IdleRequest* aRequest) const
   {
     return aRequest->Handle() >= mIdlePeriodLimit.mLastRequestIdInIdlePeriod &&
            TimeStamp::Now() <= mIdlePeriodLimit.mEndOfIdlePeriod;
   }
@@ -785,17 +785,17 @@ NS_INTERFACE_MAP_END_INHERITING(TimeoutH
 uint32_t
 nsGlobalWindowInner::RequestIdleCallback(JSContext* aCx,
                                          IdleRequestCallback& aCallback,
                                          const IdleRequestOptions& aOptions,
                                          ErrorResult& aError)
 {
   AssertIsOnMainThread();
 
-  if (mInnerObjectsFreed) {
+  if (IsDying()) {
    return 0;
   }
 
   uint32_t handle = mIdleRequestCallbackCounter++;
 
   RefPtr<IdleRequest> request =
     new IdleRequest(&aCallback, handle);
 
@@ -912,17 +912,16 @@ nsGlobalWindowInner::nsGlobalWindowInner
     mHasVRDisplayActivateEvents(false),
     mHasSeenGamepadInput(false),
     mSuspendDepth(0),
     mFreezeDepth(0),
     mFocusMethod(0),
     mSerial(0),
     mIdleRequestCallbackCounter(1),
     mIdleRequestExecutor(nullptr),
-    mCleanedUp(false),
     mDialogAbuseCount(0),
     mAreDialogsEnabled(true),
     mObservingDidRefresh(false),
     mIteratingDocumentFlushedResolvers(false),
     mCanSkipCCGeneration(0),
     mBeforeUnloadListenerCount(0)
 {
   AssertIsOnMainThread();
@@ -1059,17 +1058,19 @@ nsGlobalWindowInner::~nsGlobalWindowInne
     if (mChromeFields.mMessageManager) {
       static_cast<nsFrameMessageManager *>(
         mChromeFields.mMessageManager.get())->Disconnect();
     }
 
     mCleanMessageManager = false;
   }
 
-  DisconnectEventTargetObjects();
+  // In most cases this should already have been called, but call it again
+  // here to catch any corner cases.
+  FreeInnerObjects();
 
   if (sInnerWindowsById) {
     MOZ_ASSERT(sInnerWindowsById->Get(mWindowID),
                 "This window should be in the hash table");
     sInnerWindowsById->Remove(mWindowID);
   }
 
   nsContentUtils::InnerOrOuterWindowDestroyed();
@@ -1098,41 +1099,30 @@ nsGlobalWindowInner::~nsGlobalWindowInne
   }
 #endif
 
   MOZ_LOG(gDOMLeakPRLogInner, LogLevel::Debug, ("DOMWINDOW %p destroyed", this));
 
   Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS,
                         mMutationBits ? 1 : 0);
 
-  if (mListenerManager) {
-    mListenerManager->Disconnect();
-    mListenerManager = nullptr;
-  }
-
   // An inner window is destroyed, pull it out of the outer window's
   // list if inner windows.
 
   PR_REMOVE_LINK(this);
 
   // If our outer window's inner window is this window, null out the
   // outer window's reference to this window that's being deleted.
   nsGlobalWindowOuter *outer = GetOuterWindowInternal();
   if (outer) {
     outer->MaybeClearInnerWindow(this);
   }
 
   // We don't have to leave the tab group if we are an inner window.
 
-  // While CleanUp generally seems to be intended to clean up outers, we've
-  // historically called it for both. Changing this would probably involve
-  // auditing all of the references that inners and outers can have, and
-  // separating the handling into CleanUp() and FreeInnerObjects.
-  CleanUp();
-
   nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
   if (ac)
     ac->RemoveWindowAsListener(this);
 
   nsLayoutStatics::Release();
 }
 
 // static
@@ -1156,130 +1146,31 @@ nsGlobalWindowInner::CleanupCachedXBLHan
 {
   if (mCachedXBLPrototypeHandlers &&
       mCachedXBLPrototypeHandlers->Count() > 0) {
     mCachedXBLPrototypeHandlers->Clear();
   }
 }
 
 void
-nsGlobalWindowInner::CleanUp()
-{
-  // Guarantee idempotence.
-  if (mCleanedUp)
+nsGlobalWindowInner::FreeInnerObjects()
+{
+  if (IsDying()) {
     return;
-  mCleanedUp = true;
-
+  }
   StartDying();
 
-  DisconnectEventTargetObjects();
-
-  if (mObserver) {
-    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-    if (os) {
-      os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
-      os->RemoveObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC);
-    }
-
-    RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
-    if (sns) {
-     sns->Unregister(mObserver);
-    }
-
-    if (mIdleService) {
-      mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
-    }
-
-    Preferences::RemoveObserver(mObserver, "intl.accept_languages");
-
-    // Drop its reference to this dying window, in case for some bogus reason
-    // the object stays around.
-    mObserver->Forget();
-  }
-
-  if (mNavigator) {
-    mNavigator->Invalidate();
-    mNavigator = nullptr;
-  }
-
-  mScreen = nullptr;
-  mMenubar = nullptr;
-  mToolbar = nullptr;
-  mLocationbar = nullptr;
-  mPersonalbar = nullptr;
-  mStatusbar = nullptr;
-  mScrollbars = nullptr;
-  mHistory = nullptr;
-  mCustomElements = nullptr;
-  mApplicationCache = nullptr;
-  mIndexedDB = nullptr;
-
-  mConsole = nullptr;
-
-  mAudioWorklet = nullptr;
-  mPaintWorklet = nullptr;
-
-  mExternal = nullptr;
-  mInstallTrigger = nullptr;
-
-  mPerformance = nullptr;
-
-#ifdef MOZ_WEBSPEECH
-  mSpeechSynthesis = nullptr;
-#endif
-
-#if defined(MOZ_WIDGET_ANDROID)
-  mOrientationChangeObserver = nullptr;
-#endif
-
-  mChromeEventHandler = nullptr; // Forces Release
-  mParentTarget = nullptr;
-
-  DisableGamepadUpdates();
-  mHasGamepad = false;
-  DisableVRUpdates();
-  mHasVREvents = false;
-  mHasVRDisplayActivateEvents = false;
-  DisableIdleCallbackRequests();
-
-  if (mCleanMessageManager) {
-    MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned");
-    if (mChromeFields.mMessageManager) {
-      mChromeFields.mMessageManager->Disconnect();
-    }
-  }
-
-  CleanupCachedXBLHandlers();
-
-  for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
-    mAudioContexts[i]->Shutdown();
-  }
-  mAudioContexts.Clear();
-
-  if (mIdleTimer) {
-    mIdleTimer->Cancel();
-    mIdleTimer = nullptr;
-  }
-
-  mIntlUtils = nullptr;
-}
-
-void
-nsGlobalWindowInner::FreeInnerObjects()
-{
   // Make sure that this is called before we null out the document and
   // other members that the window destroyed observers could
   // re-create.
   NotifyDOMWindowDestroyed(this);
   if (auto* reporter = nsWindowMemoryReporter::Get()) {
     reporter->ObserveDOMWindowDetached(this);
   }
 
-  mInnerObjectsFreed = true;
-
   // Kill all of the workers for this window.
   CancelWorkersForWindow(this);
 
   if (mTimeoutManager) {
     mTimeoutManager->ClearAllTimeouts();
   }
 
   if (mIdleTimer) {
@@ -1302,19 +1193,17 @@ nsGlobalWindowInner::FreeInnerObjects()
   mCustomElements = nullptr;
 
   if (mNavigator) {
     mNavigator->OnNavigation();
     mNavigator->Invalidate();
     mNavigator = nullptr;
   }
 
-  if (mScreen) {
-    mScreen = nullptr;
-  }
+  mScreen = nullptr;
 
 #if defined(MOZ_WIDGET_ANDROID)
   mOrientationChangeObserver = nullptr;
 #endif
 
   if (mDoc) {
     // Remember the document's principal and URI.
     mDocumentPrincipal = mDoc->NodePrincipal();
@@ -1373,32 +1262,72 @@ nsGlobalWindowInner::FreeInnerObjects()
     mBeforeUnloadListenerCount = 0;
   }
 
   // If we have any promiseDocumentFlushed callbacks, fire them now so
   // that the Promises can resolve.
   CallDocumentFlushedResolvers();
   mObservingDidRefresh = false;
 
-  // Disconnect service worker objects in FreeInnerObjects().  This is normally
-  // done from CleanUp().  In the future we plan to unify CleanUp() and
-  // FreeInnerObjects().  See bug 1450266.
-  ForEachEventTargetObject([&] (DOMEventTargetHelper* aTarget, bool* aDoneOut) {
-    RefPtr<ServiceWorkerRegistration> swr = do_QueryObject(aTarget);
-    if (swr) {
-      aTarget->DisconnectFromOwner();
-      return;
-    }
-
-    RefPtr<ServiceWorker> sw = do_QueryObject(aTarget);
-    if (sw) {
-      aTarget->DisconnectFromOwner();
-      return;
-    }
-  });
+  DisconnectEventTargetObjects();
+
+  if (mObserver) {
+    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+    if (os) {
+      os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
+      os->RemoveObserver(mObserver, MEMORY_PRESSURE_OBSERVER_TOPIC);
+    }
+
+    RefPtr<StorageNotifierService> sns = StorageNotifierService::GetOrCreate();
+    if (sns) {
+     sns->Unregister(mObserver);
+    }
+
+    if (mIdleService) {
+      mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S);
+    }
+
+    Preferences::RemoveObserver(mObserver, "intl.accept_languages");
+
+    // Drop its reference to this dying window, in case for some bogus reason
+    // the object stays around.
+    mObserver->Forget();
+  }
+
+  mMenubar = nullptr;
+  mToolbar = nullptr;
+  mLocationbar = nullptr;
+  mPersonalbar = nullptr;
+  mStatusbar = nullptr;
+  mScrollbars = nullptr;
+
+  mConsole = nullptr;
+
+  mAudioWorklet = nullptr;
+  mPaintWorklet = nullptr;
+
+  mExternal = nullptr;
+  mInstallTrigger = nullptr;
+
+  mPerformance = nullptr;
+
+#ifdef MOZ_WEBSPEECH
+  mSpeechSynthesis = nullptr;
+#endif
+
+  mParentTarget = nullptr;
+
+  if (mCleanMessageManager) {
+    MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned");
+    if (mChromeFields.mMessageManager) {
+      mChromeFields.mMessageManager->Disconnect();
+    }
+  }
+
+  mIntlUtils = nullptr;
 }
 
 //*****************************************************************************
 // nsGlobalWindowInner::nsISupports
 //*****************************************************************************
 
 // QueryInterface implementation for nsGlobalWindowInner
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindowInner)
@@ -2636,22 +2565,22 @@ nsPIDOMWindowInner::IsRunningTimeout()
 
 void
 nsPIDOMWindowInner::TryToCacheTopInnerWindow()
 {
   if (mHasTriedToCacheTopInnerWindow) {
     return;
   }
 
-  MOZ_ASSERT(!mInnerObjectsFreed);
+  nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(this);
+
+  MOZ_ASSERT(!window->IsDying());
 
   mHasTriedToCacheTopInnerWindow = true;
 
-  nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(this);
-
   MOZ_ASSERT(window);
 
   if (nsCOMPtr<nsPIDOMWindowOuter> topOutter = window->GetScriptableTop()) {
     mTopInnerWindow = topOutter->GetCurrentInnerWindow();
   }
 }
 
 void
@@ -4791,17 +4720,17 @@ nsGlobalWindowInner::SetFocusedNode(nsIC
                                     uint32_t aFocusMethod,
                                     bool aNeedsFocus)
 {
   if (aNode && aNode->GetComposedDoc() != mDoc) {
     NS_WARNING("Trying to set focus to a node from a wrong document");
     return;
   }
 
-  if (mCleanedUp) {
+  if (IsDying()) {
     NS_ASSERTION(!aNode, "Trying to focus cleaned up window!");
     aNode = nullptr;
     aNeedsFocus = false;
   }
   if (mFocusedNode != aNode) {
     UpdateCanvasFocus(false, aNode);
     mFocusedNode = aNode;
     mFocusMethod = aFocusMethod & FOCUSMETHOD_MASK;
@@ -4846,17 +4775,17 @@ nsGlobalWindowInner::ShouldShowFocusRing
 
   nsCOMPtr<nsPIWindowRoot> root = GetTopWindowRoot();
   return root ? root->ShowFocusRings() : false;
 }
 
 bool
 nsGlobalWindowInner::TakeFocus(bool aFocus, uint32_t aFocusMethod)
 {
-  if (mCleanedUp) {
+  if (IsDying()) {
     return false;
   }
 
   if (aFocus)
     mFocusMethod = aFocusMethod & FOCUSMETHOD_MASK;
 
   if (mHasFocus != aFocus) {
     mHasFocus = aFocus;
@@ -7763,59 +7692,37 @@ nsGlobalWindowInner::NotifyDefaultButton
   if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
     aError.Throw(rv);
   }
 #else
   aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
 #endif
 }
 
-NS_IMETHODIMP
-nsGlobalWindowInner::GetMessageManager(nsIMessageBroadcaster** aManager)
-{
-  ErrorResult rv;
-  NS_IF_ADDREF(*aManager = GetMessageManager(rv));
-  return rv.StealNSResult();
-}
-
 ChromeMessageBroadcaster*
-nsGlobalWindowInner::GetMessageManager(ErrorResult& aError)
+nsGlobalWindowInner::MessageManager()
 {
   MOZ_ASSERT(IsChromeWindow());
   if (!mChromeFields.mMessageManager) {
-    nsCOMPtr<nsIMessageBroadcaster> globalMM =
-      do_GetService("@mozilla.org/globalmessagemanager;1");
-    mChromeFields.mMessageManager =
-      new ChromeMessageBroadcaster(static_cast<nsFrameMessageManager*>(globalMM.get()));
+    RefPtr<ChromeMessageBroadcaster> globalMM =
+      nsFrameMessageManager::GetGlobalMessageManager();
+    mChromeFields.mMessageManager = new ChromeMessageBroadcaster(globalMM);
   }
   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();
-}
-
 ChromeMessageBroadcaster*
-nsGlobalWindowInner::GetGroupMessageManager(const nsAString& aGroup,
-                                            ErrorResult& aError)
+nsGlobalWindowInner::GetGroupMessageManager(const nsAString& aGroup)
 {
   MOZ_ASSERT(IsChromeWindow());
 
   RefPtr<ChromeMessageBroadcaster> messageManager =
     mChromeFields.mGroupMessageManagers.LookupForAdd(aGroup).OrInsert(
-      [this, &aError] () {
-        nsFrameMessageManager* parent = GetMessageManager(aError);
-
-        return new ChromeMessageBroadcaster(parent);
+      [this] () {
+        return new ChromeMessageBroadcaster(MessageManager());
       });
   return messageManager;
 }
 
 void
 nsGlobalWindowInner::InitWasOffline()
 {
   mWasOffline = NS_IsOffline();
@@ -8315,17 +8222,16 @@ NextWindowID();
 
 nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter *aOuterWindow)
 : mMutationBits(0), mActivePeerConnections(0), mIsDocumentLoaded(false),
   mIsHandlingResizeEvent(false),
   mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
   mMayHaveSelectionChangeEventListener(false),
   mMayHaveMouseEnterLeaveEventListener(false),
   mMayHavePointerEnterLeaveEventListener(false),
-  mInnerObjectsFreed(false),
   mAudioCaptured(false),
   mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
   mMarkedCCGeneration(0),
   mHasTriedToCacheTopInnerWindow(false),
   mNumOfIndexedDBDatabases(0),
   mNumOfOpenWebSockets(0)
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -499,26 +499,16 @@ public:
   virtual void EnableDeviceSensor(uint32_t aType) override;
   virtual void DisableDeviceSensor(uint32_t aType) override;
 
 #if defined(MOZ_WIDGET_ANDROID)
   virtual void EnableOrientationChangeListener() override;
   virtual void DisableOrientationChangeListener() override;
 #endif
 
-  bool IsClosedOrClosing() {
-    return mCleanedUp;
-  }
-
-  bool
-  IsCleanedUp() const
-  {
-    return mCleanedUp;
-  }
-
   virtual uint32_t GetSerial() override {
     return mSerial;
   }
 
   void AddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const;
 
   void NotifyIdleObserver(IdleObserverHolder* aIdleObserverHolder,
                           bool aCallOnidle);
@@ -942,21 +932,18 @@ 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);
-  mozilla::dom::ChromeMessageBroadcaster*
-    GetMessageManager(mozilla::ErrorResult& aError);
-  mozilla::dom::ChromeMessageBroadcaster*
-    GetGroupMessageManager(const nsAString& aGroup,
-                           mozilla::ErrorResult& aError);
+  mozilla::dom::ChromeMessageBroadcaster* MessageManager();
+  mozilla::dom::ChromeMessageBroadcaster* GetGroupMessageManager(const nsAString& aGroup);
   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);
 
@@ -1087,17 +1074,16 @@ protected:
 
   RefPtr<mozilla::dom::WakeLock> mWakeLock;
 
   friend class HashchangeCallback;
   friend class mozilla::dom::BarProp;
 
   // Object Management
   virtual ~nsGlobalWindowInner();
-  void CleanUp();
 
   void FreeInnerObjects();
   nsGlobalWindowInner *CallerInnerWindow();
 
   // Only to be called on an inner window.
   // aDocument must not be null.
   void InnerSetNewDocument(JSContext* aCx, nsIDocument* aDocument);
 
@@ -1419,18 +1405,16 @@ protected:
   uint32_t mIdleRequestCallbackCounter;
   IdleRequests mIdleRequestCallbacks;
   RefPtr<IdleRequestExecutor> mIdleRequestExecutor;
 
 #ifdef DEBUG
   nsCOMPtr<nsIURI> mLastOpenedURI;
 #endif
 
-  bool mCleanedUp;
-
   nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache;
 
   using XBLPrototypeHandlerTable = nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>;
   mozilla::UniquePtr<XBLPrototypeHandlerTable> mCachedXBLPrototypeHandlers;
 
   RefPtr<mozilla::dom::IDBFactory> mIndexedDB;
 
   // This counts the number of windows that have been opened in rapid succession
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -1049,21 +1049,16 @@ nsGlobalWindowOuter::CleanUp()
 
   mOpener = nullptr;             // Forces Release
   if (mContext) {
     mContext = nullptr;            // Forces Release
   }
   mChromeEventHandler = nullptr; // Forces Release
   mParentTarget = nullptr;
 
-  nsGlobalWindowInner* inner = GetCurrentInnerWindowInternal();
-  if (inner) {
-    inner->CleanUp();
-  }
-
   mArguments = nullptr;
 
   if (mIdleTimer) {
     mIdleTimer->Cancel();
     mIdleTimer = nullptr;
   }
 }
 
@@ -1850,16 +1845,25 @@ nsGlobalWindowOuter::SetNewDocument(nsID
         currentInner->AsInner()->CreatePerformanceObjectIfNeeded();
         if (currentInner->mPerformance) {
           newInnerWindow->mPerformance =
             Performance::CreateForMainThread(newInnerWindow->AsInner(),
                                              aDocument->NodePrincipal(),
                                              currentInner->mPerformance->GetDOMTiming(),
                                              currentInner->mPerformance->GetChannel());
         }
+
+        // Rebind DETH objects to the new global created by document.open().
+        // XXX: Is this correct?  We should consider if the spec and our
+        //      implementation should change to match other browsers by
+        //      just reusing the current window.  (Bug 1449992)
+        currentInner->ForEachEventTargetObject(
+          [&] (DOMEventTargetHelper* aDETH, bool* aDoneOut) {
+            aDETH->BindToOwner(newInnerWindow->AsGlobal());
+          });
       }
 
       // Don't free objects on our current inner window if it's going to be
       // held in the bfcache.
       if (!currentInner->IsFrozen()) {
         currentInner->FreeInnerObjects();
       }
     }
@@ -7529,28 +7533,34 @@ nsGlobalWindowOuter::GetBrowserDOMWindow
 
 void
 nsGlobalWindowOuter::SetBrowserDOMWindowOuter(nsIBrowserDOMWindow* aBrowserWindow)
 {
   MOZ_ASSERT(IsChromeWindow());
   mChromeFields.mBrowserDOMWindow = aBrowserWindow;
 }
 
-NS_IMETHODIMP
-nsGlobalWindowOuter::GetMessageManager(nsIMessageBroadcaster** aManager)
-{
-  FORWARD_TO_INNER(GetMessageManager, (aManager), NS_ERROR_UNEXPECTED);
-}
-
-NS_IMETHODIMP
-nsGlobalWindowOuter::GetGroupMessageManager(const nsAString& aGroup,
-                                            nsIMessageBroadcaster** aManager)
-{
-  MOZ_RELEASE_ASSERT(IsChromeWindow());
-  FORWARD_TO_INNER(GetGroupMessageManager, (aGroup, aManager), NS_ERROR_UNEXPECTED);
+ChromeMessageBroadcaster*
+nsGlobalWindowOuter::GetMessageManager()
+{
+  if (!mInnerWindow) {
+    NS_WARNING("No inner window available!");
+    return nullptr;
+  }
+  return GetCurrentInnerWindowInternal()->MessageManager();
+}
+
+ChromeMessageBroadcaster*
+nsGlobalWindowOuter::GetGroupMessageManager(const nsAString& aGroup)
+{
+  if (!mInnerWindow) {
+    NS_WARNING("No inner window available!");
+    return nullptr;
+  }
+  return GetCurrentInnerWindowInternal()->GetGroupMessageManager(aGroup);
 }
 
 void
 nsPIDOMWindowOuter::SetOpenerForInitialContentBrowser(nsPIDOMWindowOuter* aOpenerWindow)
 {
   MOZ_ASSERT(!mOpenerForInitialContentBrowser,
              "Don't set OpenerForInitialContentBrowser twice!");
   mOpenerForInitialContentBrowser = aOpenerWindow;
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -260,16 +260,19 @@ public:
   virtual nsIPrincipal* GetPrincipal() override;
 
   // nsIDOMWindow
   NS_DECL_NSIDOMWINDOW
 
   // nsIDOMChromeWindow (only implemented on chrome windows)
   NS_DECL_NSIDOMCHROMEWINDOW
 
+  mozilla::dom::ChromeMessageBroadcaster* GetMessageManager();
+  mozilla::dom::ChromeMessageBroadcaster* GetGroupMessageManager(const nsAString& aGroup);
+
   nsresult
   OpenJS(const nsAString& aUrl, const nsAString& aName,
          const nsAString& aOptions, nsPIDOMWindowOuter **_retval);
 
   // nsIDOMEventTarget
   NS_DECL_NSIDOMEVENTTARGET
 
   virtual mozilla::EventListenerManager*
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2962,16 +2962,43 @@ public:
   // Removes an element from mResponsiveContent when the element is
   // removed from the tree.
   void RemoveResponsiveContent(mozilla::dom::HTMLImageElement* aContent)
   {
     MOZ_ASSERT(aContent);
     mResponsiveContent.RemoveEntry(aContent);
   }
 
+  void AddComposedDocShadowRoot(mozilla::dom::ShadowRoot& aShadowRoot)
+  {
+    MOZ_ASSERT(IsShadowDOMEnabled());
+    mComposedShadowRoots.PutEntry(&aShadowRoot);
+  }
+
+  using ShadowRootSet = nsTHashtable<nsPtrHashKey<mozilla::dom::ShadowRoot>>;
+
+  void RemoveComposedDocShadowRoot(mozilla::dom::ShadowRoot& aShadowRoot)
+  {
+    MOZ_ASSERT(IsShadowDOMEnabled());
+    mComposedShadowRoots.RemoveEntry(&aShadowRoot);
+  }
+
+  // If you're considering using this, you probably want to use
+  // ShadowRoot::IsComposedDocParticipant instead. This is just for
+  // sanity-checking.
+  bool IsComposedDocShadowRoot(mozilla::dom::ShadowRoot& aShadowRoot)
+  {
+    return mComposedShadowRoots.Contains(&aShadowRoot);
+  }
+
+  const ShadowRootSet& ComposedShadowRoots() const
+  {
+    return mComposedShadowRoots;
+  }
+
   // Notifies any responsive content added by AddResponsiveContent upon media
   // features values changing.
   void NotifyMediaFeatureValuesChanged();
 
   nsresult GetStateObject(nsIVariant** aResult);
 
   nsDOMNavigationTiming* GetNavigationTiming() const
   {
@@ -3753,16 +3780,21 @@ protected:
   RefPtr<mozilla::css::Loader> mCSSLoader;
   RefPtr<mozilla::css::ImageLoader> mStyleImageLoader;
   RefPtr<nsHTMLStyleSheet> mAttrStyleSheet;
   RefPtr<nsHTMLCSSStyleSheet> mStyleAttrStyleSheet;
 
   // Tracking for images in the document.
   RefPtr<mozilla::dom::ImageTracker> mImageTracker;
 
+  // A hashtable of ShadowRoots belonging to the composed doc.
+  //
+  // See ShadowRoot::SetIsComposedDocParticipant.
+  ShadowRootSet mComposedShadowRoots;
+
   // The set of all object, embed, video/audio elements or
   // nsIObjectLoadingContent or nsIDocumentActivity for which this is the owner
   // document. (They might not be in the document.)
   //
   // These are non-owning pointers, the elements are responsible for removing
   // themselves when they go away.
   nsAutoPtr<nsTHashtable<nsPtrHashKey<nsISupports> > > mActivityObservers;
 
--- a/dom/base/nsIMessageManager.idl
+++ b/dom/base/nsIMessageManager.idl
@@ -1,508 +1,24 @@
 /* -*- 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/. */
 
 #include "nsISupports.idl"
 
-interface mozIDOMWindowProxy;
-interface nsIDocShell;
 interface nsIContent;
-interface nsIEventTarget;
-interface nsIPrincipal;
-interface nsFrameLoader;
-
-// We define a "native" type for nsFrameLoader so that the rust xpidl
-// codegen doesn't try to do anything with it.
-native nativeFrameLoader(nsFrameLoader*);
-
-/**
- * Message managers provide a way for chrome-privileged JS code to
- * communicate with each other, even across process boundaries.
- *
- * Message managers are separated into "parent side" and "child side".
- * These don't always correspond to process boundaries, but can.  For
- * each child-side message manager, there is always exactly one
- * corresponding parent-side message manager that it sends messages
- * to.  However, for each parent-side message manager, there may be
- * either one or many child-side managers it can message.
- *
- * Message managers that always have exactly one "other side" are of
- * type nsIMessageSender.  Parent-side message managers that have many
- * "other sides" are of type nsIMessageBroadcaster.
- *
- * Child-side message managers can send synchronous messages to their
- * parent side, but not the other way around.
- *
- * There are two realms of message manager hierarchies.  One realm
- * approximately corresponds to DOM elements, the other corresponds to
- * process boundaries.
- *
- * Message managers corresponding to DOM elements
- * ==============================================
- *
- * In this realm of message managers, there are
- *  - "frame message managers" which correspond to frame elements
- *  - "window message managers" which correspond to top-level chrome
- *    windows
- *  - "group message managers" which correspond to named message
- *    managers with a specific window MM as the parent
- *  - the "global message manager", on the parent side.  See below.
- *
- * The DOM-realm message managers can communicate in the ways shown by
- * the following diagram.  The parent side and child side can
- * correspond to process boundaries, but don't always.
- *
- *  Parent side                         Child side
- * -------------                       ------------
- *  global MMg
- *   |
- *   +-->window MMw1
- *   |    |
- *   |    +-->frame MMp1_1<------------>frame MMc1_1
- *   |    |
- *   |    +-->frame MMp1_2<------------>frame MMc1_2
- *   |    |
- *   |    +-->group MMgr1
- *   |    |    |
- *   |    |    +-->frame MMp2_1<------->frame MMc2_1
- *   |    |    |
- *   |    |    +-->frame MMp2_2<------->frame MMc2_2
- *   |    |
- *   |    +-->group MMgr2
- *   |    |    ...
- *   |    |
- *   |    ...
- *   |
- *   +-->window MMw2
- *   ...
- *
- * For example: a message sent from MMc1_1, from the child side, is
- * sent only to MMp1_1 on the parent side.  However, note that all
- * message managers in the hierarchy above MMp1_1, in this diagram
- * MMw1 and MMg, will also notify their message listeners when the
- * message arrives.
- *
- * A message sent from MMc2_1 will be sent to MMp2_1 and also notify
- * all message managers in the hierarchy above that, including the
- * group message manager MMgr1.
 
- * For example: a message broadcast through the global MMg on the
- * parent side would be broadcast to MMw1, which would transitively
- * broadcast it to MMp1_1, MM1p_2.  The message would next be
- * broadcast to MMgr1, which would broadcast it to MMp2_1 and MMp2_2.
- * After that it would broadcast to MMgr2 and then to MMw2, and so
- * on down the hierarchy.
- *
- *   ***** PERFORMANCE AND SECURITY WARNING *****
- * Messages broadcast through the global MM and window or group MMs
- * can result in messages being dispatched across many OS processes,
- * and to many processes with different permissions.  Great care
- * should be taken when broadcasting.
- *
- * Interfaces
- * ----------
- *
- * The global MMg and window MMw's are message broadcasters implementing
- * nsIMessageBroadcaster while the frame MMp's are simple message senders
- * (nsIMessageSender). Their counterparts in the content processes are
- * message senders implementing nsIContentFrameMessageManager.
- *
- *                    nsIMessageListenerManager
- *                  /                           \
- * nsIMessageSender                               nsIMessageBroadcaster
- *       |
- * nsISyncMessageSender (content process/in-process only)
- *       |
- * nsIContentFrameMessageManager (content process/in-process only)
- *       |
- * nsIInProcessContentFrameMessageManager (in-process only)
- *
- *
- * Message managers in the chrome process can also be QI'ed to nsIFrameScriptLoader.
- *
- *
- * Message managers corresponding to process boundaries
- * ====================================================
- *
- * The second realm of message managers is the "process message
- * managers".  With one exception, these always correspond to process
- * boundaries.  The picture looks like
- *
- *  Parent process                      Child processes
- * ----------------                    -----------------
- *  global (GPPMM)
- *   |
- *   +-->parent in-process PIPMM<-->child in-process CIPPMM
- *   |
- *   +-->parent (PPMM1)<------------------>child (CPMM1)
- *   |
- *   +-->parent (PPMM2)<------------------>child (CPMM2)
- *   ...
- *
- * Note, PIPMM and CIPPMM both run in the parent process.
- *
- * For example: the parent-process PPMM1 sends messages to the
- * child-process CPMM1.
- *
- * For example: CPMM1 sends messages directly to PPMM1. The global GPPMM
- * will also notify their message listeners when the message arrives.
- *
- * For example: messages sent through the global GPPMM will be
- * dispatched to the listeners of the same-process, CIPPMM, CPMM1,
- * CPMM2, etc.
- *
- *   ***** PERFORMANCE AND SECURITY WARNING *****
- * Messages broadcast through the GPPMM can result in messages
- * being dispatched across many OS processes, and to many processes
- * with different permissions.  Great care should be taken when
- * broadcasting.
- *
- * Requests sent to parent-process message listeners should usually
- * have replies scoped to the requesting CPMM.  The following pattern
- * is common
- *
- *  const ParentProcessListener = {
- *    receiveMessage: function(aMessage) {
- *      let childMM = aMessage.target.QueryInterface(Ci.nsIMessageSender);
- *      switch (aMessage.name) {
- *      case "Foo:Request":
- *        // service request
- *        childMM.sendAsyncMessage("Foo:Response", { data });
- *      }
- *    }
- *  };
- */
-
-[scriptable, function, uuid(2b44eb57-a9c6-4773-9a1e-fe0818739a4c)]
-interface nsIMessageListener : nsISupports
+[uuid(bb5d79e4-e73c-45e7-9651-4d718f4b994c)]
+interface nsIMessageSender : nsISupports
 {
-  /**
-   * This is for JS only.
-   * receiveMessage is called with one parameter, which has the following
-   * properties:
-   *   {
-   *     target:    %the target of the message. Either an element owning
-   *                 the message manager, or message manager itself if no
-   *                 element owns it%
-   *     name:      %message name%,
-   *     sync:      %true or false%.
-   *     data:      %structured clone of the sent message data%,
-   *     json:      %same as .data, deprecated%,
-   *     objects:   %named table of jsvals/objects, or null%
-   *     principal: %principal for the window app
-   *   }
-   *
-   * 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.
-   */
-  void receiveMessage();
-};
-
-[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|.
-   *
-   * 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.
-   */
-  void addMessageListener(in AString messageName,
-                          in nsIMessageListener listener,
-                          [optional] in boolean listenWhenClosed);
-
-  /**
-   * 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.
-   */
-  void removeMessageListener(in AString messageName,
-                             in nsIMessageListener 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.
-   */
-  void addWeakMessageListener(in AString messageName,
-                              in nsIMessageListener listener);
-
-  /**
-   * This undoes an |addWeakMessageListener| call.
-   */
-  void removeWeakMessageListener(in AString messageName,
-                                 in nsIMessageListener listener);
-
-  [notxpcom] boolean markForCC();
-};
-
-/**
- * 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.
- */
-[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
-   * data delivered to listeners.
-   * @throws NS_ERROR_NOT_INITIALIZED if the sender is not initialized.  For
-   *         example, we will throw NS_ERROR_NOT_INITIALIZED if we try to send
-   *         a message to a cross-process frame but the other process has not
-   *         yet been set up.
-   * @throws NS_ERROR_FAILURE when the message receiver cannot be found.  For
-   *         example, we will throw NS_ERROR_FAILURE if we try to send a message
-   *         to a cross-process frame whose process has crashed.
-   */
-  [implicit_jscontext, optional_argc]
-  void sendAsyncMessage([optional] in AString messageName,
-                        [optional] in jsval obj,
-                        [optional] in jsval objects,
-                        [optional] in nsIPrincipal principal,
-                        [optional] in jsval transfers);
-
- /**
-  * For remote browsers there is always a corresponding process message
-  * manager. The intention of this attribute is to link leaf level frame
-  * message managers on the parent side with the corresponding process
-  * message managers (if there is one). For any other cases this property
-  * is null.
-  */
-  readonly attribute nsIMessageSender processMessageManager;
-
- /**
-  * For remote browsers, this contains the remoteType of the content child.
-  * Otherwise, it is empty.
-  */
-  readonly attribute AString remoteType;
-};
-
-/**
- * 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.
- */
-[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
-   * sensitive data.  Use with extreme caution.
-   */
-  [implicit_jscontext, optional_argc]
-  void broadcastAsyncMessage([optional] in AString messageName,
-                             [optional] in jsval obj,
-                             [optional] in jsval objects);
-
-  /**
-   * Number of subordinate message managers.
-   */
-  readonly attribute unsigned long childCount;
-
-  /**
-   * Return a single subordinate message manager.
-   */
-  nsIMessageListenerManager getChildAt(in unsigned long aIndex);
-
-  /**
-   * Some processes are kept alive after their last tab/window are closed for testing
-   * (see dom.ipc.keepProcessesAlive). This function releases those.
-   */
-   void releaseCachedProcesses();
-};
-
-[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]
-  jsval sendSyncMessage([optional] in AString messageName,
-                        [optional] in jsval obj,
-                        [optional] in jsval objects,
-                        [optional] in nsIPrincipal principal);
-
-  /**
-   * Like |sendSyncMessage()|, except re-entrant. New RPC messages may be
-   * issued even if, earlier on the call stack, we are waiting for a reply
-   * to an earlier sendRpcMessage() call.
-   *
-   * Both sendSyncMessage and sendRpcMessage will block until a reply is
-   * received, but they may be temporarily interrupted to process an urgent
-   * incoming message (such as a CPOW request).
-   */
-  [implicit_jscontext, optional_argc]
-  jsval sendRpcMessage([optional] in AString messageName,
-                       [optional] in jsval obj,
-                       [optional] in jsval objects,
-                       [optional] in nsIPrincipal principal);
-};
-
-[uuid(13f3555f-769e-44ea-b607-5239230c3162)]
-interface nsIMessageManagerGlobal : nsISyncMessageSender
-{
-  /**
-   * Print a string to stdout.
-   */
-  void dump(in DOMString aStr);
-
-  /**
-   * If leak detection is enabled, print a note to the leak log that this
-   * process will intentionally crash.
-   */
-  void privateNoteIntentionalCrash();
-
-  /**
-   * Ascii base64 data to binary data and vice versa
-   */
-  DOMString atob(in DOMString aAsciiString);
-  DOMString btoa(in DOMString aBase64Data);
 };
 
 [uuid(694e367c-aa25-4446-8499-2c527c4bd838)]
-interface nsIContentFrameMessageManager : nsIMessageManagerGlobal
+interface nsIContentFrameMessageManager : nsIMessageSender
 {
-  /**
-   * The current top level window in the frame or null.
-   */
-  readonly attribute mozIDOMWindowProxy content;
-
-  /**
-   * The top level docshell or null.
-   */
-  readonly attribute nsIDocShell docShell;
-
-  /**
-   * Returns the SchedulerEventTarget corresponding to the TabGroup
-   * for this frame.
-   */
-  readonly attribute nsIEventTarget tabEventTarget;
 };
 
 [uuid(b39a3324-b574-4f85-8cdb-274d04f807ef)]
 interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager
 {
   [notxpcom] nsIContent getOwnerContent();
-  [notxpcom] void cacheFrameLoader(in nativeFrameLoader aFrameLoader);
 };
-
-[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;
-};
-
-[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.
-   */
-  void loadFrameScript(in AString aURL, in boolean aAllowDelayedLoad,
-                       [optional] in boolean aRunInGlobalScope);
-
-  /**
-   * Removes aURL from the list of scripts which support delayed load.
-   */
-  void removeDelayedFrameScript(in AString aURL);
-
-  /**
-   * 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();
-};
-
-[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.
-   */
-  void loadProcessScript(in AString aURL, in boolean aAllowDelayedLoad);
-
-  /**
-   * Removes aURL from the list of scripts which support delayed load.
-   */
-  void removeDelayedProcessScript(in AString aURL);
-
-  /**
-   * 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();
-};
-
-[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
-   * attribute of its childprocessmessagemanager.
-   *
-   * This value will always be a JS object. Different users are
-   * expected to set properties on this object. The property name
-   * should be unique enough that other Gecko consumers won't
-   * accidentally choose it.
-   */
-  [implicit_jscontext]
-  readonly attribute jsval initialProcessData;
-};
--- a/dom/base/nsInProcessTabChildGlobal.cpp
+++ b/dom/base/nsInProcessTabChildGlobal.cpp
@@ -36,17 +36,17 @@ nsInProcessTabChildGlobal::DoSendBlockin
   SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
   queue->Flush();
 
   if (mChromeMessageManager) {
     SameProcessCpowHolder cpows(JS::RootingContext::get(aCx), aCpows);
     RefPtr<nsFrameMessageManager> mm = mChromeMessageManager;
     RefPtr<nsFrameLoader> fl = GetFrameLoader();
     mm->ReceiveMessage(mOwner, fl, aMessage, true, &aData, &cpows, aPrincipal,
-                       aRetVal);
+                       aRetVal, IgnoreErrors());
   }
   return true;
 }
 
 class nsAsyncMessageToParent : public nsSameProcessAsyncMessageBase,
                                public SameProcessMessageQueue::Runnable
 {
 public:
@@ -111,21 +111,21 @@ nsInProcessTabChildGlobal::nsInProcessTa
 nsInProcessTabChildGlobal::~nsInProcessTabChildGlobal()
 {
   mAnonymousGlobalScopes.Clear();
   mozilla::DropJSObjects(this);
 }
 
 // This method isn't automatically forwarded safely because it's notxpcom, so
 // the IDL binding doesn't know what value to return.
-NS_IMETHODIMP_(bool)
+void
 nsInProcessTabChildGlobal::MarkForCC()
 {
   MarkScopesForCC();
-  return MessageManagerGlobal::MarkForCC();
+  MessageManagerGlobal::MarkForCC();
 }
 
 nsresult
 nsInProcessTabChildGlobal::Init()
 {
 #ifdef DEBUG
   nsresult rv =
 #endif
@@ -155,19 +155,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
                                                 DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
   tmp->nsMessageManagerScriptExecutor::Unlink();
   tmp->UnlinkHostObjectURIs();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsInProcessTabChildGlobal)
-  NS_INTERFACE_MAP_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_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(nsInProcessTabChildGlobal, DOMEventTargetHelper)
@@ -199,46 +197,23 @@ nsInProcessTabChildGlobal::GetContent(Er
 {
   nsCOMPtr<nsPIDOMWindowOuter> content;
   if (mDocShell) {
     content = mDocShell->GetWindow();
   }
   return content.forget();
 }
 
-NS_IMETHODIMP
-nsInProcessTabChildGlobal::GetContent(mozIDOMWindowProxy** aContent)
-{
-  ErrorResult rv;
-  *aContent = GetContent(rv).take();
-  return rv.StealNSResult();
-}
-
-NS_IMETHODIMP
-nsInProcessTabChildGlobal::GetDocShell(nsIDocShell** aDocShell)
-{
-  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)
-{
-  *aTarget = GetTabEventTarget().take();
-  return NS_OK;
-}
-
 void
 nsInProcessTabChildGlobal::FireUnloadEvent()
 {
   // We're called from nsDocument::MaybeInitializeFinalizeFrameLoaders, so it
   // should be safe to run script.
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
 
   // Don't let the unload event propagate to chrome event handlers.
--- a/dom/base/nsInProcessTabChildGlobal.h
+++ b/dom/base/nsInProcessTabChildGlobal.h
@@ -33,25 +33,25 @@ class nsInProcessTabChildGlobal : public
                                   public nsIInProcessContentFrameMessageManager,
                                   public nsIGlobalObject,
                                   public nsIScriptObjectPrincipal,
                                   public nsSupportsWeakReference,
                                   public mozilla::dom::ipc::MessageManagerCallback
 {
   typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
 
-  using mozilla::dom::ipc::MessageManagerCallback::GetProcessMessageManager;
-
 public:
   nsInProcessTabChildGlobal(nsIDocShell* aShell, nsIContent* aOwner,
                             nsFrameMessageManager* aChrome);
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsInProcessTabChildGlobal,
                                                          mozilla::DOMEventTargetHelper)
 
+  void MarkForCC();
+
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override
   {
     MOZ_CRASH("We should never get here!");
   }
   virtual bool WrapGlobalObject(JSContext* aCx,
                                 JS::CompartmentOptions& aOptions,
                                 JS::MutableHandle<JSObject*> aReflector) override;
@@ -61,24 +61,23 @@ public:
   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_DECL_NSICONTENTFRAMEMESSAGEMANAGER
 
   NS_DECL_NSIINPROCESSCONTENTFRAMEMESSAGEMANAGER
 
+  void CacheFrameLoader(nsFrameLoader* aFrameLoader);
+
   /**
    * MessageManagerCallback methods that we override.
    */
   virtual bool DoSendBlockingMessage(JSContext* aCx,
                                       const nsAString& aMessage,
                                       StructuredCloneData& aData,
                                       JS::Handle<JSObject *> aCpows,
                                       nsIPrincipal* aPrincipal,
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -238,17 +238,17 @@ namespace xpc {
 JSObject*
 FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
                                    JS::HandleValue exceptionValue)
 {
   if (!exceptionValue.isObject()) {
     return nullptr;
   }
 
-  if (win && win->InnerObjectsFreed()) {
+  if (win && win->AsGlobal()->IsDying()) {
     // Pretend like we have no stack, so we don't end up keeping the global
     // alive via the stack.
     return nullptr;
   }
 
   JS::RootingContext* rcx = RootingCx();
   JS::RootedObject exceptionObject(rcx, &exceptionValue.toObject());
   JSObject* stackObject = JS::ExceptionStackOrNull(exceptionObject);
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -244,24 +244,16 @@ public:
    * or content in that document) has a Pointerenter/leave event listener.
    */
   void SetHasPointerEnterLeaveEventListeners()
   {
     mMayHavePointerEnterLeaveEventListener = true;
   }
 
   /**
-   * Check whether this has had inner objects freed.
-   */
-  bool InnerObjectsFreed() const
-  {
-    return mInnerObjectsFreed;
-  }
-
-  /**
    * Check whether this window is a secure context.
    */
   bool IsSecureContext() const;
   bool IsSecureContextIfOpenerIgnored() const;
 
   // Calling suspend should prevent any asynchronous tasks from
   // executing javascript for this window.  This means setTimeout,
   // requestAnimationFrame, and events should not be fired. Suspending
@@ -656,20 +648,16 @@ protected:
   bool mIsDocumentLoaded;
   bool mIsHandlingResizeEvent;
   bool mMayHavePaintEventListener;
   bool mMayHaveTouchEventListener;
   bool mMayHaveSelectionChangeEventListener;
   bool mMayHaveMouseEnterLeaveEventListener;
   bool mMayHavePointerEnterLeaveEventListener;
 
-  // Used to detect whether we have called FreeInnerObjects() (e.g. to ensure
-  // that a call to ResumeTimeouts() after FreeInnerObjects() does nothing).
-  bool mInnerObjectsFreed;
-
   bool mAudioCaptured;
 
   // Our inner window's outer window.
   nsCOMPtr<nsPIDOMWindowOuter> mOuterWindow;
 
   // the element within the document that is currently focused when this
   // window is active
   nsCOMPtr<nsIContent> mFocusedNode;
--- a/dom/base/nsXHTMLContentSerializer.cpp
+++ b/dom/base/nsXHTMLContentSerializer.cpp
@@ -28,16 +28,18 @@
 #include "nsCRT.h"
 #include "nsContentUtils.h"
 #include "nsIScriptElement.h"
 #include "nsStubMutationObserver.h"
 #include "nsAttrName.h"
 #include "nsComputedDOMStyle.h"
 #include "mozilla/dom/Element.h"
 
+using namespace mozilla;
+
 static const int32_t kLongLineLen = 128;
 
 #define kXMLNS "xmlns"
 
 nsresult
 NS_NewXHTMLContentSerializer(nsIContentSerializer** aSerializer)
 {
   RefPtr<nsXHTMLContentSerializer> it = new nsXHTMLContentSerializer();
@@ -53,17 +55,17 @@ nsXHTMLContentSerializer::nsXHTMLContent
 nsXHTMLContentSerializer::~nsXHTMLContentSerializer()
 {
   NS_ASSERTION(mOLStateStack.IsEmpty(), "Expected OL State stack to be empty");
 }
 
 NS_IMETHODIMP
 nsXHTMLContentSerializer::Init(uint32_t aFlags,
                                uint32_t aWrapColumn,
-                               const mozilla::Encoding* aEncoding,
+                               const Encoding* aEncoding,
                                bool aIsCopying,
                                bool aRewriteEncodingDeclaration,
                                bool* aNeedsPreformatScanning)
 {
   // The previous version of the HTML serializer did implicit wrapping
   // when there is no flags, so we keep wrapping in order to keep
   // compatibility with the existing calling code
   // XXXLJ perhaps should we remove this default settings later ?
@@ -233,17 +235,17 @@ nsXHTMLContentSerializer::SerializeAttri
   // XXX Unfortunately we need a namespace manager to get
   // attribute URIs.
   for (index = 0; index < count; index++) {
 
     if (aSkipAttr == index) {
         continue;
     }
 
-    mozilla::dom::BorrowedAttrInfo info = aElement->GetAttrInfoAt(index);
+    dom::BorrowedAttrInfo info = aElement->GetAttrInfoAt(index);
     const nsAttrName* name = info.mName;
 
     int32_t namespaceID = name->NamespaceID();
     nsAtom* attrName = name->LocalName();
     nsAtom* attrPrefix = name->GetPrefix();
 
     // Filter out any attribute starting with [-|_]moz
     nsDependentAtomString attrNameStr(attrName);
@@ -436,17 +438,17 @@ nsXHTMLContentSerializer::CheckElementSt
   if (aElement->IsHTMLElement(nsGkAtoms::body)) {
     ++mInBody;
   }
 
   return true;
 }
 
 bool
-nsXHTMLContentSerializer::CheckElementEnd(mozilla::dom::Element* aElem