Merge mozilla-central to autoland
authorDorel Luca <dluca@mozilla.com>
Thu, 29 Mar 2018 12:54:51 +0300
changeset 410580 cf7d049478794a0242ca60e49d032c6675d44735
parent 410579 ca73bbba52d35660cf50c5107afbd3805a9c3dd8 (current diff)
parent 410571 8c71359d60e21055074ae8bc3dcb796d20f0cbaf (diff)
child 410581 0d99bcacaf32420a676a35927a19b2ef7654f9b8
push id33733
push useraciure@mozilla.com
push dateThu, 29 Mar 2018 22:05:29 +0000
treeherdermozilla-central@7ca58ce09779 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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
browser/components/extensions/ext-bookmarks.js
browser/components/extensions/ext-browser.js
browser/components/extensions/ext-browserAction.js
browser/components/extensions/ext-browsingData.js
browser/components/extensions/ext-c-browser.js
browser/components/extensions/ext-c-devtools-inspectedWindow.js
browser/components/extensions/ext-c-devtools-network.js
browser/components/extensions/ext-c-devtools-panels.js
browser/components/extensions/ext-c-devtools.js
browser/components/extensions/ext-c-menus.js
browser/components/extensions/ext-c-omnibox.js
browser/components/extensions/ext-c-tabs.js
browser/components/extensions/ext-chrome-settings-overrides.js
browser/components/extensions/ext-commands.js
browser/components/extensions/ext-devtools-inspectedWindow.js
browser/components/extensions/ext-devtools-network.js
browser/components/extensions/ext-devtools-panels.js
browser/components/extensions/ext-devtools.js
browser/components/extensions/ext-find.js
browser/components/extensions/ext-geckoProfiler.js
browser/components/extensions/ext-history.js
browser/components/extensions/ext-menus.js
browser/components/extensions/ext-omnibox.js
browser/components/extensions/ext-pageAction.js
browser/components/extensions/ext-pkcs11.js
browser/components/extensions/ext-sessions.js
browser/components/extensions/ext-sidebarAction.js
browser/components/extensions/ext-tabs.js
browser/components/extensions/ext-url-overrides.js
browser/components/extensions/ext-windows.js
toolkit/components/extensions/ext-alarms.js
toolkit/components/extensions/ext-backgroundPage.js
toolkit/components/extensions/ext-browserSettings.js
toolkit/components/extensions/ext-c-backgroundPage.js
toolkit/components/extensions/ext-c-contentScripts.js
toolkit/components/extensions/ext-c-extension.js
toolkit/components/extensions/ext-c-identity.js
toolkit/components/extensions/ext-c-runtime.js
toolkit/components/extensions/ext-c-storage.js
toolkit/components/extensions/ext-c-test.js
toolkit/components/extensions/ext-c-toolkit.js
toolkit/components/extensions/ext-c-webRequest.js
toolkit/components/extensions/ext-clipboard.js
toolkit/components/extensions/ext-contentScripts.js
toolkit/components/extensions/ext-contextualIdentities.js
toolkit/components/extensions/ext-cookies.js
toolkit/components/extensions/ext-dns.js
toolkit/components/extensions/ext-downloads.js
toolkit/components/extensions/ext-extension.js
toolkit/components/extensions/ext-i18n.js
toolkit/components/extensions/ext-identity.js
toolkit/components/extensions/ext-idle.js
toolkit/components/extensions/ext-management.js
toolkit/components/extensions/ext-notifications.js
toolkit/components/extensions/ext-permissions.js
toolkit/components/extensions/ext-privacy.js
toolkit/components/extensions/ext-protocolHandlers.js
toolkit/components/extensions/ext-proxy.js
toolkit/components/extensions/ext-runtime.js
toolkit/components/extensions/ext-storage.js
toolkit/components/extensions/ext-tabs-base.js
toolkit/components/extensions/ext-theme.js
toolkit/components/extensions/ext-toolkit.js
toolkit/components/extensions/ext-topSites.js
toolkit/components/extensions/ext-webNavigation.js
toolkit/components/extensions/ext-webRequest.js
toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/install.rdf
toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/version.jsm
toolkit/mozapps/extensions/test/addons/test_bootstrap1_4/install.rdf
toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/bootstrap.js
toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/install.rdf
--- a/.taskcluster.yml
+++ b/.taskcluster.yml
@@ -56,20 +56,22 @@ tasks:
         then:
           - "index.gecko.v2.${repository.project}.latest.firefox.decision"
           - "index.gecko.v2.${repository.project}.revision.${push.revision}.firefox.decision"
           - "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.decision"
           - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
           - "notify.email.${ownerEmail}.on-failed"
           - "notify.email.${ownerEmail}.on-exception"
         else:
-          - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
-          - $if: 'tasks_for == "action"'
-            then: "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.actions.${ownTaskId}"
-            else: "index.gecko.v2.${repository.project}.latest.firefox.decision-${cron.job_name}"
+          $if: 'tasks_for == "action"'
+          then:
+            - "index.gecko.v2.${repository.project}.pushlog-id.${push.pushlog_id}.actions.${ownTaskId}"
+            - "tc-treeherder.v2.${repository.project}.${push.revision}.${push.pushlog_id}"
+          else:
+            - "index.gecko.v2.${repository.project}.latest.firefox.decision-${cron.job_name}"
 
       scopes:
         $if: 'tasks_for == "hg-push"'
         then:
           - 'assume:repo:${repoUrl[8:]}:branch:default'
           - 'queue:route:notify.email.${ownerEmail}.*'
         else:
           $if: 'tasks_for == "action"'
--- a/accessible/base/nsTextEquivUtils.cpp
+++ b/accessible/base/nsTextEquivUtils.cpp
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsTextEquivUtils.h"
 
 #include "Accessible-inl.h"
 #include "AccIterator.h"
 #include "nsCoreUtils.h"
 #include "nsIDOMXULLabeledControlEl.h"
+#include "mozilla/dom/Text.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 /**
  * The accessible for which we are computing a text equivalent. It is useful
  * for bailing out during recursive text computation, or for special cases
  * like step f. of the ARIA implementation guide.
@@ -111,17 +112,17 @@ nsTextEquivUtils::AppendTextEquivFromCon
   sInitiatorAcc = nullptr;
   return rv;
 }
 
 nsresult
 nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent *aContent,
                                                  nsAString *aString)
 {
-  if (aContent->IsNodeOfType(nsINode::eTEXT)) {
+  if (aContent->IsText()) {
     bool isHTMLBlock = false;
 
     nsIContent *parentContent = aContent->GetFlattenedTreeParent();
     if (parentContent) {
       nsIFrame *frame = parentContent->GetPrimaryFrame();
       if (frame) {
         // If this text is inside a block level frame (as opposed to span
         // level), we need to add spaces around that block's text, so we don't
@@ -141,17 +142,17 @@ nsTextEquivUtils::AppendTextEquivFromTex
       nsIFrame *frame = aContent->GetPrimaryFrame();
       if (frame) {
         nsIFrame::RenderedText text = frame->GetRenderedText(0,
             UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
             nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
         aString->Append(text.mString);
       } else {
         // If aContent is an object that is display: none, we have no a frame.
-        aContent->AppendTextTo(*aString);
+        aContent->GetAsText()->AppendTextTo(*aString);
       }
       if (isHTMLBlock && !aString->IsEmpty()) {
         aString->Append(char16_t(' '));
       }
     }
 
     return NS_OK;
   }
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/child/.eslintrc.js
@@ -0,0 +1,6 @@
+"use strict";
+
+module.exports = {
+  "extends": "../../../../toolkit/components/extensions/child/.eslintrc.js",
+};
+
rename from browser/components/extensions/ext-c-browser.js
rename to browser/components/extensions/child/ext-browser.js
--- a/browser/components/extensions/ext-c-browser.js
+++ b/browser/components/extensions/child/ext-browser.js
@@ -1,55 +1,55 @@
 "use strict";
 
 extensions.registerModules({
   devtools: {
-    url: "chrome://browser/content/ext-c-devtools.js",
+    url: "chrome://browser/content/child/ext-devtools.js",
     scopes: ["devtools_child"],
     paths: [
       ["devtools"],
     ],
   },
   devtools_inspectedWindow: {
-    url: "chrome://browser/content/ext-c-devtools-inspectedWindow.js",
+    url: "chrome://browser/content/child/ext-devtools-inspectedWindow.js",
     scopes: ["devtools_child"],
     paths: [
       ["devtools", "inspectedWindow"],
     ],
   },
   devtools_panels: {
-    url: "chrome://browser/content/ext-c-devtools-panels.js",
+    url: "chrome://browser/content/child/ext-devtools-panels.js",
     scopes: ["devtools_child"],
     paths: [
       ["devtools", "panels"],
     ],
   },
   devtools_network: {
-    url: "chrome://browser/content/ext-c-devtools-network.js",
+    url: "chrome://browser/content/child/ext-devtools-network.js",
     scopes: ["devtools_child"],
     paths: [
       ["devtools", "network"],
     ],
   },
   // Because of permissions, the module name must differ from both namespaces.
   menusInternal: {
-    url: "chrome://browser/content/ext-c-menus.js",
+    url: "chrome://browser/content/child/ext-menus.js",
     scopes: ["addon_child"],
     paths: [
       ["contextMenus"],
       ["menus"],
     ],
   },
   omnibox: {
-    url: "chrome://browser/content/ext-c-omnibox.js",
+    url: "chrome://browser/content/child/ext-omnibox.js",
     scopes: ["addon_child"],
     paths: [
       ["omnibox"],
     ],
   },
   tabs: {
-    url: "chrome://browser/content/ext-c-tabs.js",
+    url: "chrome://browser/content/child/ext-tabs.js",
     scopes: ["addon_child"],
     paths: [
       ["tabs"],
     ],
   },
 });
rename from browser/components/extensions/ext-c-devtools-inspectedWindow.js
rename to browser/components/extensions/child/ext-devtools-inspectedWindow.js
rename from browser/components/extensions/ext-c-devtools-network.js
rename to browser/components/extensions/child/ext-devtools-network.js
--- a/browser/components/extensions/ext-c-devtools-network.js
+++ b/browser/components/extensions/child/ext-devtools-network.js
@@ -1,15 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ../../../toolkit/components/extensions/ext-c-toolkit.js */
-
 /**
  * Responsible for fetching HTTP response content from the backend.
  *
  * @param {DevtoolsExtensionContext}
  *   A devtools extension context running in a child process.
  * @param {object} options
  */
 class ChildNetworkResponseLoader {
rename from browser/components/extensions/ext-c-devtools-panels.js
rename to browser/components/extensions/child/ext-devtools-panels.js
--- a/browser/components/extensions/ext-c-devtools-panels.js
+++ b/browser/components/extensions/child/ext-devtools-panels.js
@@ -1,15 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ../../../toolkit/components/extensions/ext-c-toolkit.js */
-
 ChromeUtils.defineModuleGetter(this, "ExtensionChildDevToolsUtils",
                                "resource://gre/modules/ExtensionChildDevToolsUtils.jsm");
 
 var {
   promiseDocumentLoaded,
 } = ExtensionUtils;
 
 /**
rename from browser/components/extensions/ext-c-devtools.js
rename to browser/components/extensions/child/ext-devtools.js
rename from browser/components/extensions/ext-c-menus.js
rename to browser/components/extensions/child/ext-menus.js
--- a/browser/components/extensions/ext-c-menus.js
+++ b/browser/components/extensions/child/ext-menus.js
@@ -1,15 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ../../../toolkit/components/extensions/ext-c-toolkit.js */
-
 var {
   withHandlingUserInput,
 } = ExtensionUtils;
 
 // If id is not specified for an item we use an integer.
 // This ID need only be unique within a single addon. Since all addon code that
 // can use this API runs in the same process, this local variable suffices.
 var gNextMenuItemID = 0;
rename from browser/components/extensions/ext-c-omnibox.js
rename to browser/components/extensions/child/ext-omnibox.js
--- a/browser/components/extensions/ext-c-omnibox.js
+++ b/browser/components/extensions/child/ext-omnibox.js
@@ -1,15 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ../../../toolkit/components/extensions/ext-c-toolkit.js */
-
 this.omnibox = class extends ExtensionAPI {
   getAPI(context) {
     return {
       omnibox: {
         onInputChanged: new EventManager(context, "omnibox.onInputChanged", fire => {
           let listener = (text, id) => {
             fire.asyncWithoutClone(text, suggestions => {
               context.childManager.callParentFunctionNoReturn("omnibox.addSuggestions", [
rename from browser/components/extensions/ext-c-tabs.js
rename to browser/components/extensions/child/ext-tabs.js
--- a/browser/components/extensions/ext-browser.json
+++ b/browser/components/extensions/ext-browser.json
@@ -1,188 +1,188 @@
 {
   "bookmarks": {
-    "url": "chrome://browser/content/ext-bookmarks.js",
+    "url": "chrome://browser/content/parent/ext-bookmarks.js",
     "schema": "chrome://browser/content/schemas/bookmarks.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["bookmarks"]
     ]
   },
   "browserAction": {
-    "url": "chrome://browser/content/ext-browserAction.js",
+    "url": "chrome://browser/content/parent/ext-browserAction.js",
     "schema": "chrome://browser/content/schemas/browser_action.json",
     "scopes": ["addon_parent"],
     "manifest": ["browser_action"],
     "paths": [
       ["browserAction"]
     ]
   },
   "browsingData": {
-    "url": "chrome://browser/content/ext-browsingData.js",
+    "url": "chrome://browser/content/parent/ext-browsingData.js",
     "schema": "chrome://browser/content/schemas/browsing_data.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["browsingData"]
     ]
   },
   "chrome_settings_overrides": {
-    "url": "chrome://browser/content/ext-chrome-settings-overrides.js",
+    "url": "chrome://browser/content/parent/ext-chrome-settings-overrides.js",
     "scopes": [],
     "events": ["update", "uninstall"],
     "schema": "chrome://browser/content/schemas/chrome_settings_overrides.json",
     "manifest": ["chrome_settings_overrides"]
   },
   "commands": {
-    "url": "chrome://browser/content/ext-commands.js",
+    "url": "chrome://browser/content/parent/ext-commands.js",
     "schema": "chrome://browser/content/schemas/commands.json",
     "scopes": ["addon_parent"],
     "events": ["uninstall"],
     "manifest": ["commands"],
     "paths": [
       ["commands"]
     ]
   },
   "devtools": {
-    "url": "chrome://browser/content/ext-devtools.js",
+    "url": "chrome://browser/content/parent/ext-devtools.js",
     "schema": "chrome://browser/content/schemas/devtools.json",
     "scopes": ["devtools_parent"],
     "manifest": ["devtools_page"],
     "paths": [
       ["devtools"]
     ]
   },
   "devtools_inspectedWindow": {
-    "url": "chrome://browser/content/ext-devtools-inspectedWindow.js",
+    "url": "chrome://browser/content/parent/ext-devtools-inspectedWindow.js",
     "schema": "chrome://browser/content/schemas/devtools_inspected_window.json",
     "scopes": ["devtools_parent"],
     "paths": [
       ["devtools", "inspectedWindow"]
     ]
   },
   "devtools_network": {
-    "url": "chrome://browser/content/ext-devtools-network.js",
+    "url": "chrome://browser/content/parent/ext-devtools-network.js",
     "schema": "chrome://browser/content/schemas/devtools_network.json",
     "scopes": ["devtools_parent"],
     "paths": [
       ["devtools", "network"]
     ]
   },
   "devtools_panels": {
-    "url": "chrome://browser/content/ext-devtools-panels.js",
+    "url": "chrome://browser/content/parent/ext-devtools-panels.js",
     "schema": "chrome://browser/content/schemas/devtools_panels.json",
     "scopes": ["devtools_parent"],
     "paths": [
       ["devtools", "panels"]
     ]
   },
   "find": {
-    "url": "chrome://browser/content/ext-find.js",
+    "url": "chrome://browser/content/parent/ext-find.js",
     "schema": "chrome://browser/content/schemas/find.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["find"]
     ]
   },
   "history": {
-    "url": "chrome://browser/content/ext-history.js",
+    "url": "chrome://browser/content/parent/ext-history.js",
     "schema": "chrome://browser/content/schemas/history.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["history"]
     ]
   },
   "identity": {
-    "url": "chrome://extensions/content/ext-identity.js",
+    "url": "chrome://extensions/content/parent/ext-identity.js",
     "schema": "chrome://extensions/content/schemas/identity.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["identity"]
     ]
   },
   "menusInternal": {
-    "url": "chrome://browser/content/ext-menus.js",
+    "url": "chrome://browser/content/parent/ext-menus.js",
     "schema": "chrome://browser/content/schemas/menus.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["contextMenus"],
       ["menus"],
       ["menusInternal"]
     ]
   },
   "omnibox": {
-    "url": "chrome://browser/content/ext-omnibox.js",
+    "url": "chrome://browser/content/parent/ext-omnibox.js",
     "schema": "chrome://browser/content/schemas/omnibox.json",
     "scopes": ["addon_parent"],
     "manifest": ["omnibox"],
     "paths": [
       ["omnibox"]
     ]
   },
   "pageAction": {
-    "url": "chrome://browser/content/ext-pageAction.js",
+    "url": "chrome://browser/content/parent/ext-pageAction.js",
     "schema": "chrome://browser/content/schemas/page_action.json",
     "scopes": ["addon_parent"],
     "manifest": ["page_action"],
     "paths": [
       ["pageAction"]
     ]
   },
   "pkcs11": {
-    "url": "chrome://browser/content/ext-pkcs11.js",
+    "url": "chrome://browser/content/parent/ext-pkcs11.js",
     "schema": "chrome://browser/content/schemas/pkcs11.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["pkcs11"]
     ]
   },
   "geckoProfiler": {
-    "url": "chrome://browser/content/ext-geckoProfiler.js",
+    "url": "chrome://browser/content/parent/ext-geckoProfiler.js",
     "schema": "chrome://browser/content/schemas/geckoProfiler.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["geckoProfiler"]
     ]
   },
   "sessions": {
-    "url": "chrome://browser/content/ext-sessions.js",
+    "url": "chrome://browser/content/parent/ext-sessions.js",
     "schema": "chrome://browser/content/schemas/sessions.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["sessions"]
     ]
   },
   "sidebarAction": {
-    "url": "chrome://browser/content/ext-sidebarAction.js",
+    "url": "chrome://browser/content/parent/ext-sidebarAction.js",
     "schema": "chrome://browser/content/schemas/sidebar_action.json",
     "scopes": ["addon_parent"],
     "manifest": ["sidebar_action"],
     "paths": [
       ["sidebarAction"]
     ]
   },
   "tabs": {
-    "url": "chrome://browser/content/ext-tabs.js",
+    "url": "chrome://browser/content/parent/ext-tabs.js",
     "schema": "chrome://browser/content/schemas/tabs.json",
     "scopes": ["addon_parent"],
     "events": ["update", "disable"],
     "paths": [
       ["tabs"]
     ]
   },
   "urlOverrides": {
-    "url": "chrome://browser/content/ext-url-overrides.js",
+    "url": "chrome://browser/content/parent/ext-url-overrides.js",
     "schema": "chrome://browser/content/schemas/url_overrides.json",
     "scopes": ["addon_parent"],
     "manifest": ["chrome_url_overrides"],
     "paths": [
       ["urlOverrides"]
     ]
   },
   "windows": {
-    "url": "chrome://browser/content/ext-windows.js",
+    "url": "chrome://browser/content/parent/ext-windows.js",
     "schema": "chrome://browser/content/schemas/windows.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["windows"]
     ]
   }
 }
--- a/browser/components/extensions/extensions-browser.manifest
+++ b/browser/components/extensions/extensions-browser.manifest
@@ -1,7 +1,7 @@
 category webextension-modules browser chrome://browser/content/ext-browser.json
 
-category webextension-scripts c-browser chrome://browser/content/ext-browser.js
-category webextension-scripts-devtools browser chrome://browser/content/ext-c-browser.js
-category webextension-scripts-addon browser chrome://browser/content/ext-c-browser.js
+category webextension-scripts c-browser chrome://browser/content/parent/ext-browser.js
+category webextension-scripts-devtools browser chrome://browser/content/child/ext-browser.js
+category webextension-scripts-addon browser chrome://browser/content/child/ext-browser.js
 
 category webextension-schemas menus_internal chrome://browser/content/schemas/menus_internal.json
--- a/browser/components/extensions/jar.mn
+++ b/browser/components/extensions/jar.mn
@@ -7,39 +7,39 @@ browser.jar:
 #ifdef XP_MACOSX
     content/browser/extension-mac.css
     content/browser/extension-mac-panel.css
 #endif
 #ifdef XP_WIN
     content/browser/extension-win-panel.css
 #endif
     content/browser/extension.svg
-    content/browser/ext-bookmarks.js
-    content/browser/ext-browser.js
     content/browser/ext-browser.json
-    content/browser/ext-browserAction.js
-    content/browser/ext-browsingData.js
-    content/browser/ext-chrome-settings-overrides.js
-    content/browser/ext-commands.js
-    content/browser/ext-devtools.js
-    content/browser/ext-devtools-inspectedWindow.js
-    content/browser/ext-devtools-network.js
-    content/browser/ext-devtools-panels.js
-    content/browser/ext-find.js
-    content/browser/ext-geckoProfiler.js
-    content/browser/ext-history.js
-    content/browser/ext-menus.js
-    content/browser/ext-omnibox.js
-    content/browser/ext-pageAction.js
-    content/browser/ext-pkcs11.js
-    content/browser/ext-sessions.js
-    content/browser/ext-sidebarAction.js
-    content/browser/ext-tabs.js
-    content/browser/ext-url-overrides.js
-    content/browser/ext-windows.js
-    content/browser/ext-c-browser.js
-    content/browser/ext-c-devtools-inspectedWindow.js
-    content/browser/ext-c-devtools-network.js
-    content/browser/ext-c-devtools-panels.js
-    content/browser/ext-c-devtools.js
-    content/browser/ext-c-menus.js
-    content/browser/ext-c-omnibox.js
-    content/browser/ext-c-tabs.js
+    content/browser/parent/ext-bookmarks.js (parent/ext-bookmarks.js)
+    content/browser/parent/ext-browser.js (parent/ext-browser.js)
+    content/browser/parent/ext-browserAction.js (parent/ext-browserAction.js)
+    content/browser/parent/ext-browsingData.js (parent/ext-browsingData.js)
+    content/browser/parent/ext-chrome-settings-overrides.js (parent/ext-chrome-settings-overrides.js)
+    content/browser/parent/ext-commands.js (parent/ext-commands.js)
+    content/browser/parent/ext-devtools.js (parent/ext-devtools.js)
+    content/browser/parent/ext-devtools-inspectedWindow.js (parent/ext-devtools-inspectedWindow.js)
+    content/browser/parent/ext-devtools-network.js (parent/ext-devtools-network.js)
+    content/browser/parent/ext-devtools-panels.js (parent/ext-devtools-panels.js)
+    content/browser/parent/ext-find.js (parent/ext-find.js)
+    content/browser/parent/ext-geckoProfiler.js (parent/ext-geckoProfiler.js)
+    content/browser/parent/ext-history.js (parent/ext-history.js)
+    content/browser/parent/ext-menus.js (parent/ext-menus.js)
+    content/browser/parent/ext-omnibox.js (parent/ext-omnibox.js)
+    content/browser/parent/ext-pageAction.js (parent/ext-pageAction.js)
+    content/browser/parent/ext-pkcs11.js (parent/ext-pkcs11.js)
+    content/browser/parent/ext-sessions.js (parent/ext-sessions.js)
+    content/browser/parent/ext-sidebarAction.js (parent/ext-sidebarAction.js)
+    content/browser/parent/ext-tabs.js (parent/ext-tabs.js)
+    content/browser/parent/ext-url-overrides.js (parent/ext-url-overrides.js)
+    content/browser/parent/ext-windows.js (parent/ext-windows.js)
+    content/browser/child/ext-browser.js (child/ext-browser.js)
+    content/browser/child/ext-devtools-inspectedWindow.js (child/ext-devtools-inspectedWindow.js)
+    content/browser/child/ext-devtools-network.js (child/ext-devtools-network.js)
+    content/browser/child/ext-devtools-panels.js (child/ext-devtools-panels.js)
+    content/browser/child/ext-devtools.js (child/ext-devtools.js)
+    content/browser/child/ext-menus.js (child/ext-menus.js)
+    content/browser/child/ext-omnibox.js (child/ext-omnibox.js)
+    content/browser/child/ext-tabs.js (child/ext-tabs.js)
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/parent/.eslintrc.js
@@ -0,0 +1,29 @@
+"use strict";
+
+module.exports = {
+  "extends": "../../../../toolkit/components/extensions/parent/.eslintrc.js",
+
+  "globals": {
+    "Tab": true,
+    "TabContext": true,
+    "Window": true,
+    "WindowEventManager": true,
+    "actionContextMenu": true,
+    "browserActionFor": true,
+    "getContainerForCookieStoreId": true,
+    "getDevToolsTargetForContext": true,
+    "getInspectedWindowFront": true,
+    "getTargetTabIdForToolbox": true,
+    "getToolboxEvalOptions": true,
+    "isContainerCookieStoreId": true,
+    "isPrivateCookieStoreId": true,
+    "isValidCookieStoreId": true,
+    "makeWidgetId": true,
+    "openOptionsPage": true,
+    "pageActionFor": true,
+    "sidebarActionFor": true,
+    "tabGetSender": true,
+    "tabTracker": true,
+    "windowTracker": true,
+  },
+};
rename from browser/components/extensions/ext-bookmarks.js
rename to browser/components/extensions/parent/ext-bookmarks.js
--- a/browser/components/extensions/ext-bookmarks.js
+++ b/browser/components/extensions/parent/ext-bookmarks.js
@@ -1,15 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-browserAction.js */
-
 ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
 
 const {
   TYPE_BOOKMARK,
   TYPE_FOLDER,
   TYPE_SEPARATOR,
 } = PlacesUtils.bookmarks;
 
rename from browser/components/extensions/ext-browser.js
rename to browser/components/extensions/parent/ext-browser.js
--- a/browser/components/extensions/ext-browser.js
+++ b/browser/components/extensions/parent/ext-browser.js
@@ -1,22 +1,16 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 // This file provides some useful code for the |tabs| and |windows|
 // modules. All of the code is installed on |global|, which is a scope
 // shared among the different ext-*.js scripts.
 
-/* global EventEmitter:false, TabContext:false, WindowEventManager:false,
-          makeWidgetId:false, tabTracker:true, windowTracker:true */
-/* import-globals-from ../../../toolkit/components/extensions/ext-toolkit.js */
-
-/* globals TabBase, WindowBase, TabTrackerBase, WindowTrackerBase, TabManagerBase, WindowManagerBase */
-
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "RecentWindow",
                                "resource:///modules/RecentWindow.jsm");
 
 var {
   ExtensionError,
   defineLazyGetter,
rename from browser/components/extensions/ext-browserAction.js
rename to browser/components/extensions/parent/ext-browserAction.js
--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/parent/ext-browserAction.js
@@ -1,18 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-/* exported browserActionFor, sidebarActionFor, pageActionFor */
-/* global browserActionFor:false, sidebarActionFor:false, pageActionFor:false */
-
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-browser.js */
-
 ChromeUtils.defineModuleGetter(this, "CustomizableUI",
                                "resource:///modules/CustomizableUI.jsm");
 ChromeUtils.defineModuleGetter(this, "clearTimeout",
                                "resource://gre/modules/Timer.jsm");
 ChromeUtils.defineModuleGetter(this, "setTimeout",
                                "resource://gre/modules/Timer.jsm");
 ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
                                "resource://gre/modules/TelemetryStopwatch.jsm");
rename from browser/components/extensions/ext-browsingData.js
rename to browser/components/extensions/parent/ext-browsingData.js
rename from browser/components/extensions/ext-chrome-settings-overrides.js
rename to browser/components/extensions/parent/ext-chrome-settings-overrides.js
rename from browser/components/extensions/ext-commands.js
rename to browser/components/extensions/parent/ext-commands.js
--- a/browser/components/extensions/ext-commands.js
+++ b/browser/components/extensions/parent/ext-commands.js
@@ -1,21 +1,21 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-browserAction.js */
-/* import-globals-from ext-browser.js */
-
 ChromeUtils.defineModuleGetter(this, "ExtensionParent",
                                "resource://gre/modules/ExtensionParent.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                "resource://gre/modules/ExtensionSettingsStore.jsm");
 
+var {
+  ExtensionError,
+} = ExtensionUtils;
+
 var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 const EXECUTE_PAGE_ACTION = "_execute_page_action";
 const EXECUTE_BROWSER_ACTION = "_execute_browser_action";
 const EXECUTE_SIDEBAR_ACTION = "_execute_sidebar_action";
 
 function normalizeShortcut(shortcut) {
   return shortcut ? shortcut.replace(/\s+/g, "") : null;
rename from browser/components/extensions/ext-devtools-inspectedWindow.js
rename to browser/components/extensions/parent/ext-devtools-inspectedWindow.js
--- a/browser/components/extensions/ext-devtools-inspectedWindow.js
+++ b/browser/components/extensions/parent/ext-devtools-inspectedWindow.js
@@ -1,16 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-devtools.js */
-/* import-globals-from ext-browser.js */
-
 var {
   SpreadArgs,
 } = ExtensionCommon;
 
 this.devtools_inspectedWindow = class extends ExtensionAPI {
   getAPI(context) {
     // Lazily retrieved inspectedWindow actor front per child context.
     let waitForInspectedWindowFront;
rename from browser/components/extensions/ext-devtools-network.js
rename to browser/components/extensions/parent/ext-devtools-network.js
--- a/browser/components/extensions/ext-devtools-network.js
+++ b/browser/components/extensions/parent/ext-devtools-network.js
@@ -1,19 +1,20 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-devtools.js */
-
 var {
   SpreadArgs,
 } = ExtensionCommon;
 
+var {
+  ExtensionError,
+} = ExtensionUtils;
+
 this.devtools_network = class extends ExtensionAPI {
   getAPI(context) {
     return {
       devtools: {
         network: {
           onNavigated: new EventManager(context, "devtools.onNavigated", fire => {
             let listener = data => {
               fire.async(data.url);
rename from browser/components/extensions/ext-devtools-panels.js
rename to browser/components/extensions/parent/ext-devtools-panels.js
--- a/browser/components/extensions/ext-devtools-panels.js
+++ b/browser/components/extensions/parent/ext-devtools-panels.js
@@ -1,16 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-devtools.js */
-/* import-globals-from ext-browser.js */
-
 ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
 
 ChromeUtils.defineModuleGetter(this, "E10SUtils",
                                "resource://gre/modules/E10SUtils.jsm");
 
 var {
   IconDetails,
   watchExtensionProxyContextLoad,
rename from browser/components/extensions/ext-devtools.js
rename to browser/components/extensions/parent/ext-devtools.js
--- a/browser/components/extensions/ext-devtools.js
+++ b/browser/components/extensions/parent/ext-devtools.js
@@ -1,18 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-/* exported getDevToolsTargetForContext, getInspectedWindowFront, getToolboxEvalOptions */
-/* global getTargetTabIdForToolbox, getDevToolsTargetForContext, getInspectedWindowFront, getToolboxEvalOptions */
-
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-browser.js */
-
 /**
  * This module provides helpers used by the other specialized `ext-devtools-*.js` modules
  * and the implementation of the `devtools_page`.
  */
 
 ChromeUtils.defineModuleGetter(this, "DevToolsShim",
                                "chrome://devtools-startup/content/DevToolsShim.jsm");
 
rename from browser/components/extensions/ext-find.js
rename to browser/components/extensions/parent/ext-find.js
rename from browser/components/extensions/ext-geckoProfiler.js
rename to browser/components/extensions/parent/ext-geckoProfiler.js
--- a/browser/components/extensions/ext-geckoProfiler.js
+++ b/browser/components/extensions/parent/ext-geckoProfiler.js
@@ -1,15 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ../../../toolkit/components/extensions/ext-toolkit.js */
-
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 Cu.importGlobalProperties(["TextEncoder", "TextDecoder"]);
 
 ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 ChromeUtils.defineModuleGetter(this, "Subprocess", "resource://gre/modules/Subprocess.jsm");
 
 const PREF_ASYNC_STACK = "javascript.options.asyncstack";
 const PREF_SYMBOLS_URL = "extensions.geckoProfiler.symbols.url";
rename from browser/components/extensions/ext-history.js
rename to browser/components/extensions/parent/ext-history.js
--- a/browser/components/extensions/ext-history.js
+++ b/browser/components/extensions/parent/ext-history.js
@@ -1,15 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-browserAction.js */
-
 ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                                "resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 
 var {
   normalizeTime,
 } = ExtensionUtils;
rename from browser/components/extensions/ext-menus.js
rename to browser/components/extensions/parent/ext-menus.js
--- a/browser/components/extensions/ext-menus.js
+++ b/browser/components/extensions/parent/ext-menus.js
@@ -1,15 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-browser.js */
-
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 var {
   DefaultMap,
   ExtensionError,
 } = ExtensionUtils;
 
 ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
rename from browser/components/extensions/ext-omnibox.js
rename to browser/components/extensions/parent/ext-omnibox.js
--- a/browser/components/extensions/ext-omnibox.js
+++ b/browser/components/extensions/parent/ext-omnibox.js
@@ -1,15 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ../../../toolkit/components/extensions/ext-toolkit.js */
-
 ChromeUtils.defineModuleGetter(this, "ExtensionSearchHandler",
                                "resource://gre/modules/ExtensionSearchHandler.jsm");
 
 this.omnibox = class extends ExtensionAPI {
   onManifestEntry(entryName) {
     let {extension} = this;
     let {manifest} = extension;
 
rename from browser/components/extensions/ext-pageAction.js
rename to browser/components/extensions/parent/ext-pageAction.js
--- a/browser/components/extensions/ext-pageAction.js
+++ b/browser/components/extensions/parent/ext-pageAction.js
@@ -1,31 +1,31 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-browserAction.js */
-/* import-globals-from ext-browser.js */
-
 ChromeUtils.defineModuleGetter(this, "PageActions",
                                "resource:///modules/PageActions.jsm");
 ChromeUtils.defineModuleGetter(this, "PanelPopup",
                                "resource:///modules/ExtensionPopups.jsm");
 ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
                                "resource://gre/modules/TelemetryStopwatch.jsm");
 
 
 ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
 
 var {
   IconDetails,
   StartupCache,
 } = ExtensionParent;
 
+var {
+  DefaultWeakMap,
+} = ExtensionUtils;
+
 const popupOpenTimingHistogram = "WEBEXT_PAGEACTION_POPUP_OPEN_MS";
 
 // WeakMap[Extension -> PageAction]
 let pageActionMap = new WeakMap();
 
 this.pageAction = class extends ExtensionAPI {
   static for(extension) {
     return pageActionMap.get(extension);
rename from browser/components/extensions/ext-pkcs11.js
rename to browser/components/extensions/parent/ext-pkcs11.js
rename from browser/components/extensions/ext-sessions.js
rename to browser/components/extensions/parent/ext-sessions.js
--- a/browser/components/extensions/ext-sessions.js
+++ b/browser/components/extensions/parent/ext-sessions.js
@@ -1,15 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-browser.js */
-
 var {
   ExtensionError,
   promiseObserved,
 } = ExtensionUtils;
 
 ChromeUtils.defineModuleGetter(this, "AddonManagerPrivate",
                                "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "SessionStore",
rename from browser/components/extensions/ext-sidebarAction.js
rename to browser/components/extensions/parent/ext-sidebarAction.js
--- a/browser/components/extensions/ext-sidebarAction.js
+++ b/browser/components/extensions/parent/ext-sidebarAction.js
@@ -1,16 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-browser.js */
-/* globals WINDOW_ID_CURRENT */
-
 ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
 
 var {
   IconDetails,
 } = ExtensionParent;
 
 var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
@@ -427,17 +423,17 @@ this.sidebarAction = class extends Exten
         close() {
           let window = windowTracker.topWindow;
           sidebarAction.close(window);
         },
 
         isOpen(details) {
           let {windowId} = details;
           if (windowId == null) {
-            windowId = WINDOW_ID_CURRENT;
+            windowId = Window.WINDOW_ID_CURRENT;
           }
           let window = windowTracker.getWindow(windowId, context);
           return sidebarAction.isOpen(window);
         },
       },
     };
   }
 };
rename from browser/components/extensions/ext-tabs.js
rename to browser/components/extensions/parent/ext-tabs.js
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/parent/ext-tabs.js
@@ -1,16 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-browser.js */
-/* import-globals-from ../../../toolkit/components/extensions/ext-tabs-base.js */
-
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "PromiseUtils",
                                "resource://gre/modules/PromiseUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "SessionStore",
                                "resource:///modules/sessionstore/SessionStore.jsm");
@@ -141,17 +137,17 @@ class TabsUpdateFilterEventManager exten
             nonempty = true;
             result[prop] = changeInfo[prop];
           }
         }
         return nonempty && result;
       }
 
       function getWindowID(windowId) {
-        if (windowId === WINDOW_ID_CURRENT) {
+        if (windowId === Window.WINDOW_ID_CURRENT) {
           return windowTracker.getId(windowTracker.topWindow);
         }
         return windowId;
       }
 
       function matchFilters(tab, changed) {
         if (!filterProps) {
           return true;
rename from browser/components/extensions/ext-url-overrides.js
rename to browser/components/extensions/parent/ext-url-overrides.js
--- a/browser/components/extensions/ext-url-overrides.js
+++ b/browser/components/extensions/parent/ext-url-overrides.js
@@ -1,12 +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/. */
-/* import-globals-from ext-browser.js */
 
 "use strict";
 
 ChromeUtils.defineModuleGetter(this, "AddonManager",
                                "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                                "resource://gre/modules/BrowserUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "CustomizableUI",
rename from browser/components/extensions/ext-windows.js
rename to browser/components/extensions/parent/ext-windows.js
--- a/browser/components/extensions/ext-windows.js
+++ b/browser/components/extensions/parent/ext-windows.js
@@ -1,15 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-browser.js */
-
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 var {
   promiseObserved,
--- a/browser/components/extensions/test/mochitest/mochitest.ini
+++ b/browser/components/extensions/test/mochitest/mochitest.ini
@@ -1,7 +1,8 @@
 [DEFAULT]
 support-files =
   ../../../../../toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
   ../../../../../toolkit/components/extensions/test/mochitest/file_sample.html
 tags = webextensions
 
 [test_ext_all_apis.html]
+skip-if = true
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -40,16 +40,17 @@ class RemoteAutomation(Automation):
         # Because we are running remote, we don't want to mimic the local env
         # so no copying of os.environ
         if env is None:
             env = {}
 
         if crashreporter and not debugger:
             env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
             env['MOZ_CRASHREPORTER'] = '1'
+            env['MOZ_CRASHREPORTER_SHUTDOWN'] = '1'
         else:
             env['MOZ_CRASHREPORTER_DISABLE'] = '1'
 
         # Crash on non-local network connections by default.
         # MOZ_DISABLE_NONLOCAL_CONNECTIONS can be set to "0" to temporarily
         # enable non-local connections for the purposes of local testing.
         # Don't override the user's choice here.  See bug 1049688.
         env.setdefault('MOZ_DISABLE_NONLOCAL_CONNECTIONS', '1')
--- a/caps/NullPrincipal.cpp
+++ b/caps/NullPrincipal.cpp
@@ -61,16 +61,22 @@ NullPrincipal::Create(const OriginAttrib
 {
   RefPtr<NullPrincipal> nullPrin = new NullPrincipal();
   nsresult rv = nullPrin->Init(aOriginAttributes, aURI);
   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
 
   return nullPrin.forget();
 }
 
+/* static */ already_AddRefed<NullPrincipal>
+NullPrincipal::CreateWithoutOriginAttributes()
+{
+  return NullPrincipal::Create(mozilla::OriginAttributes(), nullptr);
+}
+
 nsresult
 NullPrincipal::Init(const OriginAttributes& aOriginAttributes, nsIURI* aURI)
 {
   if (aURI) {
     nsAutoCString scheme;
     nsresult rv = aURI->GetScheme(scheme);
     NS_ENSURE_SUCCESS(rv, rv);
 
--- a/caps/NullPrincipal.h
+++ b/caps/NullPrincipal.h
@@ -59,19 +59,22 @@ public:
   // Create NullPrincipal with origin attributes from docshell.
   // If aIsFirstParty is true, and the pref 'privacy.firstparty.isolate' is also
   // enabled, the mFirstPartyDomain value of the origin attributes will be set
   // to an unique value.
   static already_AddRefed<NullPrincipal>
   CreateWithInheritedAttributes(nsIDocShell* aDocShell, bool aIsFirstParty = false);
 
   static already_AddRefed<NullPrincipal>
-  Create(const mozilla::OriginAttributes& aOriginAttributes = mozilla::OriginAttributes(),
+  Create(const mozilla::OriginAttributes& aOriginAttributes,
          nsIURI* aURI = nullptr);
 
+  static already_AddRefed<NullPrincipal>
+  CreateWithoutOriginAttributes();
+
   nsresult Init(const mozilla::OriginAttributes& aOriginAttributes = mozilla::OriginAttributes(),
                 nsIURI* aURI = nullptr);
 
   virtual nsresult GetScriptLocation(nsACString &aStr) override;
 
  protected:
   virtual ~NullPrincipal() = default;
 
--- a/dom/base/CharacterData.cpp
+++ b/dom/base/CharacterData.cpp
@@ -85,62 +85,50 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(CharacterData)
   return Element::CanSkipInCC(tmp);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(CharacterData)
   return Element::CanSkipThis(tmp);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
 
+// We purposefully don't TRAVERSE_BEGIN_INHERITED here.  All the bits
+// we should traverse should be added here or in nsINode::Traverse.
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(CharacterData)
   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
     char name[40];
     SprintfLiteral(name, "CharacterData (len=%d)",
                    tmp->mText.GetLength());
     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
   } else {
     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(CharacterData, tmp->mRefCnt.get())
   }
 
   if (!nsIContent::Traverse(tmp, cb)) {
     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+// We purposefully don't UNLINK_BEGIN_INHERITED here.
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CharacterData)
   nsIContent::Unlink(tmp);
 
   // Clear flag here because unlinking slots will clear the
   // containing shadow root pointer.
   tmp->UnsetFlags(NODE_IS_IN_SHADOW_TREE);
 
   nsContentSlots* slots = tmp->GetExistingContentSlots();
   if (slots) {
     slots->Unlink();
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN(CharacterData)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(CharacterData)
-  NS_INTERFACE_MAP_ENTRY(nsIContent)
-  NS_INTERFACE_MAP_ENTRY(nsINode)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
-  NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
-  NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
-                                 new nsNodeSupportsWeakRefTearoff(this))
-  // DOM bindings depend on the identity pointer being the
-  // same as nsINode (which nsIContent inherits).
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(CharacterData)
-NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(CharacterData,
-                                                                    nsNodeUtils::LastRelease(this))
-
+NS_INTERFACE_MAP_END_INHERITING(nsIContent)
 
 void
 CharacterData::GetNodeValueInternal(nsAString& aNodeValue)
 {
   GetData(aNodeValue);
 }
 
 void
@@ -781,56 +769,16 @@ CharacterData::ThreadSafeTextIsOnlyWhite
     }
 
     ++cp;
   }
 
   return true;
 }
 
-bool
-CharacterData::HasTextForTranslation()
-{
-  if (NodeType() != TEXT_NODE &&
-      NodeType() != CDATA_SECTION_NODE) {
-    return false;
-  }
-
-  if (mText.Is2b()) {
-    // The fragment contains non-8bit characters which means there
-    // was at least one "interesting" character to trigger non-8bit.
-    return true;
-  }
-
-  if (HasFlag(NS_CACHED_TEXT_IS_ONLY_WHITESPACE) &&
-      HasFlag(NS_TEXT_IS_ONLY_WHITESPACE)) {
-    return false;
-  }
-
-  const char* cp = mText.Get1b();
-  const char* end = cp + mText.GetLength();
-
-  unsigned char ch;
-  for (; cp < end; cp++) {
-    ch = *cp;
-
-    // These are the characters that are letters
-    // in the first 256 UTF-8 codepoints.
-    if ((ch >= 'a' && ch <= 'z') ||
-       (ch >= 'A' && ch <= 'Z') ||
-       (ch >= 192 && ch <= 214) ||
-       (ch >= 216 && ch <= 246) ||
-       (ch >= 248)) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
 void
 CharacterData::AppendTextTo(nsAString& aResult)
 {
   mText.AppendTo(aResult);
 }
 
 bool
 CharacterData::AppendTextTo(nsAString& aResult,
--- a/dom/base/CharacterData.h
+++ b/dom/base/CharacterData.h
@@ -72,17 +72,21 @@ ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIF
 #undef CHARACTER_DATA_FLAG_BIT
 
 namespace mozilla {
 namespace dom {
 
 class CharacterData : public nsIContent
 {
 public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  // We want to avoid the overhead of extra function calls for
+  // refcounting when we're not doing refcount logging, so we can't
+  // NS_DECL_ISUPPORTS_INHERITED.
+  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
+  NS_INLINE_DECL_REFCOUNTING_INHERITED(CharacterData, nsIContent);
 
   NS_DECL_ADDSIZEOFEXCLUDINGTHIS
 
   explicit CharacterData(already_AddRefed<dom::NodeInfo>& aNodeInfo);
   explicit CharacterData(already_AddRefed<dom::NodeInfo>&& aNodeInfo);
 
   void MarkAsMaybeModifiedFrequently()
   {
@@ -125,32 +129,52 @@ public:
                               bool aCompileEventHandlers) override;
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true) override;
 
   virtual already_AddRefed<nsINodeList> GetChildren(uint32_t aFilter) override;
 
   virtual const nsTextFragment *GetText() override;
   virtual uint32_t TextLength() const override;
-  virtual nsresult SetText(const char16_t* aBuffer, uint32_t aLength,
-                           bool aNotify) override;
-  // Need to implement this here too to avoid hiding.
+
+  /**
+   * Set the text to the given value. If aNotify is true then
+   * the document is notified of the content change.
+   */
+  nsresult SetText(const char16_t* aBuffer, uint32_t aLength,
+                   bool aNotify);
+  /**
+   * Append the given value to the current text. If aNotify is true then
+   * the document is notified of the content change.
+   */
   nsresult SetText(const nsAString& aStr, bool aNotify)
   {
     return SetText(aStr.BeginReading(), aStr.Length(), aNotify);
   }
-  virtual nsresult AppendText(const char16_t* aBuffer, uint32_t aLength,
-                              bool aNotify) override;
+
+  /**
+   * Append the given value to the current text. If aNotify is true then
+   * the document is notified of the content change.
+   */
+  nsresult AppendText(const char16_t* aBuffer, uint32_t aLength,
+                      bool aNotify);
+
   virtual bool TextIsOnlyWhitespace() override;
   bool ThreadSafeTextIsOnlyWhitespace() const final;
-  virtual bool HasTextForTranslation() override;
-  virtual void AppendTextTo(nsAString& aResult) override;
+
+  /**
+   * Append the text content to aResult.
+   */
+  void AppendTextTo(nsAString& aResult);
+  /**
+   * Append the text content to aResult.
+   */
   MOZ_MUST_USE
-  virtual bool AppendTextTo(nsAString& aResult,
-                            const fallible_t&) override;
+  bool AppendTextTo(nsAString& aResult, const fallible_t&);
+
   virtual void SaveSubtreeState() override;
 
 #ifdef DEBUG
   virtual void List(FILE* out, int32_t aIndent) const override;
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const override;
 #endif
 
   virtual nsXBLBinding* DoGetXBLBinding() const override;
@@ -188,17 +212,18 @@ public:
   }
 
   //----------------------------------------
 
 #ifdef DEBUG
   void ToCString(nsAString& aBuf, int32_t aOffset, int32_t aLen) const;
 #endif
 
-  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(CharacterData)
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(CharacterData,
+                                                                   nsIContent)
 
 protected:
   virtual ~CharacterData();
 
   virtual Element* GetNameSpaceElement() override
   {
     nsINode *parent = GetParentNode();
 
@@ -218,31 +243,16 @@ protected:
    * @param aCloneText if true the text content will be cloned too
    * @return the clone
    */
   virtual already_AddRefed<CharacterData>
     CloneDataNode(dom::NodeInfo *aNodeInfo, bool aCloneText) const = 0;
 
   nsTextFragment mText;
 
-public:
-  virtual bool OwnedOnlyByTheDOMTree() override
-  {
-    return GetParent() && mRefCnt.get() == 1;
-  }
-
-  virtual bool IsPurple() override
-  {
-    return mRefCnt.IsPurple();
-  }
-  virtual void RemovePurple() override
-  {
-    mRefCnt.RemovePurple();
-  }
-
 private:
   already_AddRefed<nsAtom> GetCurrentValueAtom();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_CharacterData_h */
--- a/dom/base/DOMParser.cpp
+++ b/dom/base/DOMParser.cpp
@@ -341,17 +341,17 @@ DOMParser::Init(nsIPrincipal* principal,
     OriginAttributes attrs;
     mPrincipal = BasePrincipal::CreateCodebasePrincipal(mDocumentURI, attrs);
     NS_ENSURE_TRUE(mPrincipal, NS_ERROR_FAILURE);
   } else {
     if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
       // Don't give DOMParsers the system principal.  Use a null
       // principal instead.
       mOriginalPrincipalWasSystem = true;
-      mPrincipal = NullPrincipal::Create();
+      mPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
 
       if (!mDocumentURI) {
         rv = mPrincipal->GetURI(getter_AddRefs(mDocumentURI));
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
   }
 
@@ -452,17 +452,17 @@ DOMParser::SetUpDocument(DocumentFlavor 
   // off of nsIScriptGlobalObject, but that's a yak to shave another day.
   nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
     do_QueryReferent(mScriptHandlingObject);
   nsresult rv;
   if (!mPrincipal) {
     NS_ENSURE_TRUE(!mAttemptedInit, NS_ERROR_NOT_INITIALIZED);
     AttemptedInitMarker marker(&mAttemptedInit);
 
-    nsCOMPtr<nsIPrincipal> prin = NullPrincipal::Create();
+    nsCOMPtr<nsIPrincipal> prin = NullPrincipal::CreateWithoutOriginAttributes();
     rv = Init(prin, nullptr, nullptr, scriptHandlingObject);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Try to inherit a style backend.
   NS_ASSERTION(mPrincipal, "Must have principal by now");
   NS_ASSERTION(mDocumentURI, "Must have document URI by now");
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -936,17 +936,21 @@ public:
   {
     List(out, aIndent, EmptyCString());
   }
   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const override;
   void List(FILE* out, int32_t aIndent, const nsCString& aPrefix) const;
   void ListAttributes(FILE* out) const;
 #endif
 
-  void Describe(nsAString& aOutDescription) const override;
+  /**
+   * Append to aOutDescription a short (preferably one line) string
+   * describing the element.
+   */
+  void Describe(nsAString& aOutDescription) const;
 
   /*
    * Attribute Mapping Helpers
    */
   struct MappedAttributeEntry {
     nsStaticAtom** attribute;
   };
 
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -129,16 +129,45 @@
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 int32_t nsIContent::sTabFocusModel = eTabFocus_any;
 bool nsIContent::sTabFocusModelAppliesToXUL = false;
 uint64_t nsMutationGuard::sGeneration = 0;
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsIContent)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsIContent)
+  MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsIContent)
+  MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN(nsIContent)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  // Don't bother to QI to cycle collection, because our CC impl is
+  // not doing anything anyway.
+  NS_INTERFACE_MAP_ENTRY(nsIContent)
+  NS_INTERFACE_MAP_ENTRY(nsINode)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
+  NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
+  NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
+                                 new nsNodeSupportsWeakRefTearoff(this))
+  // DOM bindings depend on the identity pointer being the
+  // same as nsINode (which nsIContent inherits).
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(nsIContent)
+NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsIContent,
+                                                                    nsNodeUtils::LastRelease(this))
+
 nsIContent*
 nsIContent::FindFirstNonChromeOnlyAccessContent() const
 {
   // This handles also nested native anonymous content.
   for (const nsIContent *content = this; content;
        content = content->GetBindingParent()) {
     if (!content->ChromeOnlyAccess()) {
       // Oops, this function signature allows casting const to
@@ -177,17 +206,17 @@ nsIContent::GetAssignedSlotByMode() cons
   }
 
   return slot;
 }
 
 nsIContent::IMEState
 nsIContent::GetDesiredIMEState()
 {
-  if (!IsEditableInternal()) {
+  if (!IsEditable()) {
     // Check for the special case where we're dealing with elements which don't
     // have the editable flag set, but are readwrite (such as text controls).
     if (!IsElement() ||
         !AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
       return IMEState(IMEState::DISABLED);
     }
   }
   // NOTE: The content for independent editors (e.g., input[type=text],
@@ -222,17 +251,17 @@ nsIContent::HasIndependentSelection()
   nsIFrame* frame = GetPrimaryFrame();
   return (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
 }
 
 dom::Element*
 nsIContent::GetEditingHost()
 {
   // If this isn't editable, return nullptr.
-  if (!IsEditableInternal()) {
+  if (!IsEditable()) {
     return nullptr;
   }
 
   nsIDocument* doc = GetComposedDoc();
   if (!doc) {
     return nullptr;
   }
 
@@ -1421,18 +1450,19 @@ ContentUnbinder* ContentUnbinder::sConte
 void
 FragmentOrElement::ClearContentUnbinder()
 {
   ContentUnbinder::UnbindAll();
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(FragmentOrElement)
 
+// We purposefully don't UNLINK_BEGIN_INHERITED here.
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
-  nsINode::Unlink(tmp);
+  nsIContent::Unlink(tmp);
 
   // The XBL binding is removed by RemoveFromBindingManagerRunnable
   // which is dispatched in UnbindFromTree.
 
   if (tmp->HasProperties()) {
     if (tmp->IsElement()) {
       Element* elem = tmp->AsElement();
       elem->UnlinkIntersectionObservers();
@@ -1916,16 +1946,18 @@ static const char* kNSURIs[] = {
   " (XBL)",
   " (MathML)",
   " (RDF)",
   " (XUL)",
   " (SVG)",
   " (XML Events)"
 };
 
+// We purposefully don't TRAVERSE_BEGIN_INHERITED here.  All the bits
+// we should traverse should be added here or in nsINode::Traverse.
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
     char name[512];
     uint32_t nsid = tmp->GetNameSpaceID();
     nsAtomCString localName(tmp->NodeInfo()->NameAtom());
     nsAutoCString uri;
     if (tmp->OwnerDoc()->GetDocumentURI()) {
       uri = tmp->OwnerDoc()->GetDocumentURI()->GetSpecOrDefault();
@@ -1968,17 +2000,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
                    orphan.get(),
                    uri.get());
     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
   }
   else {
     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get())
   }
 
-  if (!nsINode::Traverse(tmp, cb)) {
+  if (!nsIContent::Traverse(tmp, cb)) {
     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   }
 
   tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb);
 
   // Check that whenever we have effect properties, MayHaveAnimations is set.
 #ifdef DEBUG
   nsAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
@@ -2040,32 +2072,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrsAndChildren[i]");
       cb.NoteXPCOMChild(tmp->mAttrsAndChildren.GetSafeChildAt(i));
     }
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
 NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
-  NS_INTERFACE_MAP_ENTRY(nsIContent)
-  NS_INTERFACE_MAP_ENTRY(nsINode)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
-  NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
-  NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
-                                 new nsNodeSupportsWeakRefTearoff(this))
-  // DOM bindings depend on the identity pointer being the
-  // same as nsINode (which nsIContent inherits).
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(FragmentOrElement)
-NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FragmentOrElement,
-                                                                    nsNodeUtils::LastRelease(this))
+NS_INTERFACE_MAP_END_INHERITING(nsIContent)
 
 //----------------------------------------------------------------------
 
 nsresult
 FragmentOrElement::CopyInnerTo(FragmentOrElement* aDst,
                                bool aPreallocateChildren)
 {
   nsresult rv = aDst->mAttrsAndChildren.EnsureCapacityToClone(mAttrsAndChildren,
@@ -2097,70 +2115,28 @@ FragmentOrElement::TextLength() const
 {
   // We can remove this assertion if it turns out to be useful to be able
   // to depend on this returning 0
   NS_NOTREACHED("called FragmentOrElement::TextLength");
 
   return 0;
 }
 
-nsresult
-FragmentOrElement::SetText(const char16_t* aBuffer, uint32_t aLength,
-                          bool aNotify)
-{
-  NS_ERROR("called FragmentOrElement::SetText");
-
-  return NS_ERROR_FAILURE;
-}
-
-nsresult
-FragmentOrElement::AppendText(const char16_t* aBuffer, uint32_t aLength,
-                             bool aNotify)
-{
-  NS_ERROR("called FragmentOrElement::AppendText");
-
-  return NS_ERROR_FAILURE;
-}
-
 bool
 FragmentOrElement::TextIsOnlyWhitespace()
 {
   return false;
 }
 
 bool
 FragmentOrElement::ThreadSafeTextIsOnlyWhitespace() const
 {
   return false;
 }
 
-bool
-FragmentOrElement::HasTextForTranslation()
-{
-  return false;
-}
-
-void
-FragmentOrElement::AppendTextTo(nsAString& aResult)
-{
-  // We can remove this assertion if it turns out to be useful to be able
-  // to depend on this appending nothing.
-  NS_NOTREACHED("called FragmentOrElement::TextLength");
-}
-
-bool
-FragmentOrElement::AppendTextTo(nsAString& aResult, const mozilla::fallible_t&)
-{
-  // We can remove this assertion if it turns out to be useful to be able
-  // to depend on this appending nothing.
-  NS_NOTREACHED("called FragmentOrElement::TextLength");
-
-  return false;
-}
-
 uint32_t
 FragmentOrElement::GetChildCount() const
 {
   return mAttrsAndChildren.ChildCount();
 }
 
 nsIContent *
 FragmentOrElement::GetChildAt_Deprecated(uint32_t aIndex) const
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -108,17 +108,21 @@ namespace dom {
 class ShadowRoot;
 
 class FragmentOrElement : public nsIContent
 {
 public:
   explicit FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
   explicit FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  // We want to avoid the overhead of extra function calls for
+  // refcounting when we're not doing refcount logging, so we can't
+  // NS_DECL_ISUPPORTS_INHERITED.
+  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
+  NS_INLINE_DECL_REFCOUNTING_INHERITED(FragmentOrElement, nsIContent);
 
   NS_DECL_ADDSIZEOFEXCLUDINGTHIS
 
   // nsINode interface methods
   virtual uint32_t GetChildCount() const override;
   virtual nsIContent *GetChildAt_Deprecated(uint32_t aIndex) const override;
   virtual int32_t ComputeIndexOf(const nsINode* aPossibleChild) const override;
   virtual nsresult InsertChildBefore(nsIContent* aKid, nsIContent* aBeforeThis,
@@ -132,31 +136,18 @@ public:
   virtual void SetTextContentInternal(const nsAString& aTextContent,
                                       nsIPrincipal* aSubjectPrincipal,
                                       mozilla::ErrorResult& aError) override;
 
   // nsIContent interface methods
   virtual already_AddRefed<nsINodeList> GetChildren(uint32_t aFilter) override;
   virtual const nsTextFragment *GetText() override;
   virtual uint32_t TextLength() const override;
-  virtual nsresult SetText(const char16_t* aBuffer, uint32_t aLength,
-                           bool aNotify) override;
-  // Need to implement this here too to avoid hiding.
-  nsresult SetText(const nsAString& aStr, bool aNotify)
-  {
-    return SetText(aStr.BeginReading(), aStr.Length(), aNotify);
-  }
-  virtual nsresult AppendText(const char16_t* aBuffer, uint32_t aLength,
-                              bool aNotify) override;
   virtual bool TextIsOnlyWhitespace() override;
   virtual bool ThreadSafeTextIsOnlyWhitespace() const override;
-  virtual bool HasTextForTranslation() override;
-  virtual void AppendTextTo(nsAString& aResult) override;
-  MOZ_MUST_USE
-  virtual bool AppendTextTo(nsAString& aResult, const mozilla::fallible_t&) override;
   virtual nsXBLBinding* DoGetXBLBinding() const override;
   virtual bool IsLink(nsIURI** aURI) const override;
 
   virtual void DestroyContent() override;
   virtual void SaveSubtreeState() override;
 
   nsIHTMLCollection* Children();
   uint32_t ChildElementCount()
@@ -168,43 +159,24 @@ public:
   /**
    * If there are listeners for DOMNodeInserted event, fires the event on all
    * aNodes
    */
   static void FireNodeInserted(nsIDocument* aDoc,
                                nsINode* aParent,
                                nsTArray<nsCOMPtr<nsIContent> >& aNodes);
 
-  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(FragmentOrElement)
+  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(FragmentOrElement,
+                                                                   nsIContent)
 
   /**
    * Fire a DOMNodeRemoved mutation event for all children of this node
    */
   void FireNodeRemovedForChildren();
 
-  virtual bool OwnedOnlyByTheDOMTree() override
-  {
-    uint32_t rc = mRefCnt.get();
-    if (GetParent()) {
-      --rc;
-    }
-    rc -= mAttrsAndChildren.ChildCount();
-    return rc == 0;
-  }
-
-  virtual bool IsPurple() override
-  {
-    return mRefCnt.IsPurple();
-  }
-
-  virtual void RemovePurple() override
-  {
-    mRefCnt.RemovePurple();
-  }
-
   static void ClearContentUnbinder();
   static bool CanSkip(nsINode* aNode, bool aRemovingAllowed);
   static bool CanSkipInCC(nsINode* aNode);
   static bool CanSkipThis(nsINode* aNode);
   static void RemoveBlackMarkedNode(nsINode* aNode);
   static void MarkNodeChildren(nsINode* aNode);
   static void InitCCCallbacks();
 
--- a/dom/base/Text.cpp
+++ b/dom/base/Text.cpp
@@ -138,10 +138,45 @@ Text::Constructor(const GlobalObject& aG
   if (!window || !window->GetDoc()) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   return window->GetDoc()->CreateTextNode(aData);
 }
 
+bool
+Text::HasTextForTranslation()
+{
+  if (mText.Is2b()) {
+    // The fragment contains non-8bit characters which means there
+    // was at least one "interesting" character to trigger non-8bit.
+    return true;
+  }
+
+  if (HasFlag(NS_CACHED_TEXT_IS_ONLY_WHITESPACE) &&
+      HasFlag(NS_TEXT_IS_ONLY_WHITESPACE)) {
+    return false;
+  }
+
+  const char* cp = mText.Get1b();
+  const char* end = cp + mText.GetLength();
+
+  unsigned char ch;
+  for (; cp < end; cp++) {
+    ch = *cp;
+
+    // These are the characters that are letters
+    // in the first 256 UTF-8 codepoints.
+    if ((ch >= 'a' && ch <= 'z') ||
+       (ch >= 'A' && ch <= 'Z') ||
+       (ch >= 192 && ch <= 214) ||
+       (ch >= 216 && ch <= 246) ||
+       (ch >= 248)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/Text.h
+++ b/dom/base/Text.h
@@ -26,26 +26,43 @@ public:
 
   // WebIDL API
   already_AddRefed<Text> SplitText(uint32_t aOffset, ErrorResult& rv);
   void GetWholeText(nsAString& aWholeText, ErrorResult& rv);
 
   static already_AddRefed<Text>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aData, ErrorResult& aRv);
+
+  /**
+   * Method to see if the text node contains data that is useful
+   * for a translation: i.e., it consists of more than just whitespace,
+   * digits and punctuation.
+   */
+  bool HasTextForTranslation();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 inline mozilla::dom::Text* nsINode::GetAsText()
 {
-  return IsText()  ? static_cast<mozilla::dom::Text*>(this)
-                   : nullptr;
+  return IsText() ? AsText() : nullptr;
 }
 
 inline const mozilla::dom::Text* nsINode::GetAsText() const
 {
-  return IsText() ? static_cast<const mozilla::dom::Text*>(this)
-                  : nullptr;
+  return IsText() ? AsText() : nullptr;
+}
+
+inline mozilla::dom::Text* nsINode::AsText()
+{
+  MOZ_ASSERT(IsText());
+  return static_cast<mozilla::dom::Text*>(this);
+}
+
+inline const mozilla::dom::Text* nsINode::AsText() const
+{
+  MOZ_ASSERT(IsText());
+  return static_cast<const mozilla::dom::Text*>(this);
 }
 
 #endif // mozilla_dom_Text_h
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -54,16 +54,17 @@
 #include "mozilla/dom/IDTracker.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/KeyboardEventBinding.h"
 #include "mozilla/dom/IPCBlobUtils.h"
 #include "mozilla/dom/NodeBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/Text.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/XULCommandEvent.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
@@ -585,17 +586,17 @@ nsContentUtils::Init()
   sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
   if(!sSecurityManager)
     return NS_ERROR_FAILURE;
   NS_ADDREF(sSecurityManager);
 
   sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
   MOZ_ASSERT(sSystemPrincipal);
 
-  RefPtr<NullPrincipal> nullPrincipal = NullPrincipal::Create();
+  RefPtr<NullPrincipal> nullPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
   if (!nullPrincipal) {
     return NS_ERROR_FAILURE;
   }
 
   nullPrincipal.forget(&sNullSubjectPrincipal);
 
   nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
   if (NS_FAILED(rv)) {
@@ -5215,17 +5216,17 @@ nsContentUtils::ParseFragmentXML(const n
 nsresult
 nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
                                    nsAString& aResultBuffer,
                                    uint32_t aFlags,
                                    uint32_t aWrapCol)
 {
   nsCOMPtr<nsIURI> uri;
   NS_NewURI(getter_AddRefs(uri), "about:blank");
-  nsCOMPtr<nsIPrincipal> principal = NullPrincipal::Create();
+  nsCOMPtr<nsIPrincipal> principal = NullPrincipal::CreateWithoutOriginAttributes();
   nsCOMPtr<nsIDOMDocument> domDocument;
   nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument),
                                   EmptyString(),
                                   EmptyString(),
                                   nullptr,
                                   uri,
                                   uri,
                                   principal,
@@ -5292,27 +5293,26 @@ nsContentUtils::SetNodeTextContent(nsICo
   mozAutoDocUpdate updateBatch(aContent->GetComposedDoc(),
     UPDATE_CONTENT_MODEL, true);
   nsAutoMutationBatch mb;
 
   if (aTryReuse && !aValue.IsEmpty()) {
     // Let's remove nodes until we find a eTEXT.
     while (aContent->HasChildren()) {
       nsIContent* child = aContent->GetFirstChild();
-      if (child->IsNodeOfType(nsINode::eTEXT)) {
+      if (child->IsText()) {
         break;
       }
       aContent->RemoveChildNode(child, true);
     }
 
     // If we have a node, it must be a eTEXT and we reuse it.
     if (aContent->HasChildren()) {
       nsIContent* child = aContent->GetFirstChild();
-      MOZ_ASSERT(child->IsNodeOfType(nsINode::eTEXT));
-      nsresult rv = child->SetText(aValue, true);
+      nsresult rv = child->AsText()->SetText(aValue, true);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // All the following nodes, if they exist, must be deleted.
       while (nsIContent* nextChild = child->GetNextSibling()) {
         aContent->RemoveChildNode(nextChild, true);
       }
     }
 
@@ -5351,46 +5351,45 @@ AppendNodeTextContentsRecurse(nsINode* a
        child = child->GetNextSibling()) {
     if (child->IsElement()) {
       bool ok = AppendNodeTextContentsRecurse(child, aResult,
                                               aFallible);
       if (!ok) {
         return false;
       }
     }
-    else if (child->IsNodeOfType(nsINode::eTEXT)) {
-      bool ok = child->AppendTextTo(aResult, aFallible);
+    else if (Text* text = child->GetAsText()) {
+      bool ok = text->AppendTextTo(aResult, aFallible);
       if (!ok) {
         return false;
       }
     }
   }
 
   return true;
 }
 
 /* static */
 bool
 nsContentUtils::AppendNodeTextContent(nsINode* aNode, bool aDeep,
                                       nsAString& aResult,
                                       const fallible_t& aFallible)
 {
-  if (aNode->IsNodeOfType(nsINode::eTEXT)) {
-    return static_cast<nsIContent*>(aNode)->AppendTextTo(aResult,
-                                                         aFallible);
+  if (Text* text = aNode->GetAsText()) {
+    return text->AppendTextTo(aResult, aFallible);
   }
   if (aDeep) {
     return AppendNodeTextContentsRecurse(aNode, aResult, aFallible);
   }
 
   for (nsIContent* child = aNode->GetFirstChild();
        child;
        child = child->GetNextSibling()) {
-    if (child->IsNodeOfType(nsINode::eTEXT)) {
-      bool ok = child->AppendTextTo(aResult, fallible);
+    if (Text* text = child->GetAsText()) {
+      bool ok = text->AppendTextTo(aResult, fallible);
       if (!ok) {
         return false;
       }
     }
   }
   return true;
 }
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -73,16 +73,17 @@
 #include "mozilla/dom/AudioDeviceInfo.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/IDBFactoryBinding.h"
 #include "mozilla/dom/IDBMutableFileBinding.h"
 #include "mozilla/dom/IDBMutableFile.h"
 #include "mozilla/dom/IndexedDatabaseManager.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/Text.h"
 #include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/layers/FrameUniformityData.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "nsPrintfCString.h"
 #include "nsViewportInfo.h"
 #include "nsIFormControl.h"
@@ -1420,17 +1421,17 @@ nsDOMWindowUtils::GetTranslationNodes(ns
 
     // An element is a translation node if it contains
     // at least one text node that has meaningful data
     // for translation
     for (nsIContent* child = content->GetFirstChild();
          child;
          child = child->GetNextSibling()) {
 
-      if (child->HasTextForTranslation()) {
+      if (child->IsText() && child->GetAsText()->HasTextForTranslation()) {
         translationNodesHash.PutEntry(content);
 
         bool isBlockFrame = false;
         nsIFrame* frame = content->GetPrimaryFrame();
         if (frame) {
           isBlockFrame = frame->IsFrameOfType(nsIFrame::eBlockFrame);
         }
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12254,16 +12254,17 @@ nsIDocument::SetContentTypeInternal(cons
 {
   if (!IsHTMLOrXHTML() && mDefaultElementType == kNameSpaceID_None &&
       aType.EqualsLiteral("application/xhtml+xml")) {
     mDefaultElementType = kNameSpaceID_XHTML;
   }
 
   mCachedEncoder = nullptr;
   mContentType = aType;
+  mContentTypeForWriteCalls = aType;
 }
 
 nsILoadContext*
 nsIDocument::GetLoadContext() const
 {
   return mDocumentContainer;
 }
 
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -48,16 +48,17 @@
 #include "nsNumberControlFrame.h"
 #include "nsNetUtil.h"
 #include "nsRange.h"
 
 #include "mozilla/ContentEvents.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/dom/HTMLSlotElement.h"
+#include "mozilla/dom/Text.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
@@ -2581,17 +2582,17 @@ nsFocusManager::GetSelectionLocation(nsI
     if (isCollapsed) {
       // Next check to see if our caret is at the very end of a node
       // If so, the caret is actually sitting in front of the next
       // logical frame's primary node - so for this case we need to
       // change caretContent to that node.
 
       if (startContent->NodeType() == nsINode::TEXT_NODE) {
         nsAutoString nodeValue;
-        startContent->AppendTextTo(nodeValue);
+        startContent->GetAsText()->AppendTextTo(nodeValue);
 
         bool isFormControl =
           startContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL);
 
         if (nodeValue.Length() == startOffset && !isFormControl &&
             startContent != aDocument->GetRootElement()) {
           // Yes, indeed we were at the end of the last node
           nsCOMPtr<nsIFrameEnumerator> frameTraversal;
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -164,17 +164,16 @@ NS_INTERFACE_MAP_END
 nsFrameLoader::nsFrameLoader(Element* aOwner, nsPIDOMWindowOuter* aOpener,
                              bool aNetworkCreated, int32_t aJSPluginID)
   : mOwnerContent(aOwner)
   , mDetachedSubdocFrame(nullptr)
   , mOpener(aOpener)
   , mRemoteBrowser(nullptr)
   , mChildID(0)
   , mJSPluginID(aJSPluginID)
-  , mEventMode(FrameLoaderBinding::EVENT_MODE_NORMAL_DISPATCH)
   , mBrowserChangingProcessBlockers(nullptr)
   , mDepthTooGreat(false)
   , mIsTopLevelContent(false)
   , mDestroyCalled(false)
   , mNeedsAsyncDestroy(false)
   , mInSwap(false)
   , mInShow(false)
   , mHideCalled(false)
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -168,22 +168,16 @@ public:
   void StartPersistence(uint64_t aOuterWindowID,
                         nsIWebBrowserPersistDocumentReceiver* aRecv,
                         mozilla::ErrorResult& aRv);
 
   // WebIDL getters
 
   already_AddRefed<nsIMessageSender> GetMessageManager();
 
-  uint32_t EventMode() const { return mEventMode; }
-  void SetEventMode(uint32_t aEventMode)
-  {
-    mEventMode = aEventMode;
-  }
-
   already_AddRefed<Element> GetOwnerElement();
 
   uint32_t LazyWidth() const;
 
   uint32_t LazyHeight() const;
 
   uint64_t ChildID() const { return mChildID; }
 
@@ -474,20 +468,16 @@ private:
   // An opener window which should be used when the docshell is created.
   nsCOMPtr<nsPIDOMWindowOuter> mOpener;
 
   TabParent* mRemoteBrowser;
   uint64_t mChildID;
 
   int32_t mJSPluginID;
 
-  // See FrameLoader.webidl. EVENT_MODE_NORMAL_DISPATCH automatically
-  // forwards some input events to out-of-process content.
-  uint32_t mEventMode;
-
   // Holds the last known size of the frame.
   mozilla::ScreenIntSize mLazySize;
 
   // A stack-maintained reference to an array of promises which are blocking
   // grouped history navigation
   nsTArray<RefPtr<mozilla::dom::Promise>>* mBrowserChangingProcessBlockers;
 
   bool mDepthTooGreat : 1;
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -6,16 +6,17 @@
 #ifndef nsIContent_h___
 #define nsIContent_h___
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BorrowedAttrInfo.h"
 #include "nsCaseTreatment.h" // for enum, cannot be forward-declared
 #include "nsINode.h"
 #include "nsStringFwd.h"
+#include "nsISupportsImpl.h"
 
 // Forward declarations
 class nsAtom;
 class nsIURI;
 class nsAttrValue;
 class nsAttrName;
 class nsTextFragment;
 class nsIFrame;
@@ -63,16 +64,19 @@ public:
   {
     MOZ_ASSERT(mNodeInfo);
     SetNodeIsContent();
   }
 #endif // MOZILLA_INTERNAL_API
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENT_IID)
 
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(nsIContent)
+
   /**
    * Bind this content node to a tree.  If this method throws, the caller must
    * call UnbindFromTree() on the node.  In the typical case of a node being
    * appended to a parent, this will be called after the node has been added to
    * the parent's child list and before nsIDocumentObserver notifications for
    * the addition are dispatched.
    * @param aDocument The new document for the content node.  May not be null
    *                  if aParent is null.  Must match the current document of
@@ -371,74 +375,27 @@ public:
   bool IsEventAttributeName(nsAtom* aName);
 
   virtual bool IsEventAttributeNameInternal(nsAtom* aName)
   {
     return false;
   }
 
   /**
-   * Set the text to the given value. If aNotify is true then
-   * the document is notified of the content change.
-   * NOTE: For elements this always ASSERTS and returns NS_ERROR_FAILURE
-   */
-  virtual nsresult SetText(const char16_t* aBuffer, uint32_t aLength,
-                           bool aNotify) = 0;
-
-  /**
-   * Append the given value to the current text. If aNotify is true then
-   * the document is notified of the content change.
-   * NOTE: For elements this always ASSERTS and returns NS_ERROR_FAILURE
-   */
-  virtual nsresult AppendText(const char16_t* aBuffer, uint32_t aLength,
-                              bool aNotify) = 0;
-
-  /**
-   * Set the text to the given value. If aNotify is true then
-   * the document is notified of the content change.
-   * NOTE: For elements this always asserts and returns NS_ERROR_FAILURE
-   */
-  nsresult SetText(const nsAString& aStr, bool aNotify)
-  {
-    return SetText(aStr.BeginReading(), aStr.Length(), aNotify);
-  }
-
-  /**
    * Query method to see if the frame is nothing but whitespace
    * NOTE: Always returns false for elements
    */
   virtual bool TextIsOnlyWhitespace() = 0;
 
   /**
    * Thread-safe version of TextIsOnlyWhitespace.
    */
   virtual bool ThreadSafeTextIsOnlyWhitespace() const = 0;
 
   /**
-   * Method to see if the text node contains data that is useful
-   * for a translation: i.e., it consists of more than just whitespace,
-   * digits and punctuation.
-   * NOTE: Always returns false for elements.
-   */
-  virtual bool HasTextForTranslation() = 0;
-
-  /**
-   * Append the text content to aResult.
-   * NOTE: This asserts and returns for elements
-   */
-  virtual void AppendTextTo(nsAString& aResult) = 0;
-
-  /**
-   * Append the text content to aResult.
-   * NOTE: This asserts and returns for elements
-   */
-  MOZ_MUST_USE
-  virtual bool AppendTextTo(nsAString& aResult, const mozilla::fallible_t&) = 0;
-
-  /**
    * Check if this content is focusable and in the current tab order.
    * Note: most callers should use nsIFrame::IsFocusable() instead as it
    *       checks visibility and other layout factors as well.
    * Tabbable is indicated by a nonnegative tabindex & is a subset of focusable.
    * For example, only the selected radio button in a group is in the
    * tab order, unless the radio group has no selection in which case
    * all of the visible, non-disabled radio buttons in the group are
    * in the tab order. On the other hand, all of the visible, non-disabled
@@ -662,26 +619,16 @@ public:
    * element and then call setAttribute() directly, at which point
    * DoneCreatingElement() has already been called and is out of the picture).
    */
   virtual void DoneCreatingElement()
   {
   }
 
   /**
-   * This method is called when the parser begins creating the element's
-   * children, if any are present.
-   *
-   * This is only called for XTF elements currently.
-   */
-  virtual void BeginAddingChildren()
-  {
-  }
-
-  /**
    * This method is called when the parser finishes creating the element's children,
    * if any are present.
    *
    * NOTE: this is currently only called for textarea, select, and object
    * elements in the HTML content sink. If you want to call it on your element,
    * modify the content sink of your choice to do so. This is an efficiency
    * measure.
    *
@@ -822,24 +769,34 @@ public:
   // If aSubjectPrincipal is passed, it should be the scripted principal
   // responsible for generating the URL data.
   already_AddRefed<mozilla::URLExtraData>
   GetURLDataForStyleAttr(nsIPrincipal* aSubjectPrincipal = nullptr) const;
 
   virtual nsresult GetEventTargetParent(
                      mozilla::EventChainPreVisitor& aVisitor) override;
 
-  virtual bool IsPurple() = 0;
-  virtual void RemovePurple() = 0;
+  bool IsPurple() const
+  {
+    return mRefCnt.IsPurple();
+  }
 
-  virtual bool OwnedOnlyByTheDOMTree() { return false; }
+  void RemovePurple()
+  {
+    mRefCnt.RemovePurple();
+  }
 
-  virtual already_AddRefed<nsITextControlElement> GetAsTextControlElement()
+  bool OwnedOnlyByTheDOMTree()
   {
-    return nullptr;
+    uint32_t rc = mRefCnt.get();
+    if (GetParent()) {
+      --rc;
+    }
+    rc -= GetChildCount();
+    return rc == 0;
   }
 
 protected:
   /**
    * Lazily allocated extended slots to avoid
    * that may only be instantiated when a content object is accessed
    * through the DOM. Rather than burn actual slots in the content
    * objects for each of these instance variables, we put them off
@@ -947,41 +904,34 @@ protected:
   }
 
   /**
    * Hook for implementing GetID.  This is guaranteed to only be
    * called if HasID() is true.
    */
   nsAtom* DoGetID() const;
 
+  ~nsIContent() {}
+
 public:
 #ifdef DEBUG
   /**
    * List the content (and anything it contains) out to the given
    * file stream. Use aIndent as the base indent during formatting.
    */
   virtual void List(FILE* out = stdout, int32_t aIndent = 0) const = 0;
 
   /**
    * Dump the content (and anything it contains) out to the given
    * file stream. Use aIndent as the base indent during formatting.
    */
   virtual void DumpContent(FILE* out = stdout, int32_t aIndent = 0,
                            bool aDumpAll = true) const = 0;
 #endif
 
-  /**
-   * Append to aOutDescription a short (preferably one line) string
-   * describing the content.
-   * Currently implemented for elements only.
-   */
-  virtual void Describe(nsAString& aOutDescription) const {
-    aOutDescription = NS_LITERAL_STRING("(not an element)");
-  }
-
   enum ETabFocusType {
     eTabFocus_textControlsMask = (1<<0),  // textboxes and lists always tabbable
     eTabFocus_formElementsMask = (1<<1),  // non-text form elements
     eTabFocus_linksMask = (1<<2),         // links
     eTabFocus_any = 1 + (1<<1) + (1<<2)   // everything that can be focused
   };
 
   // Tab focus model bit field:
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -4234,16 +4234,18 @@ protected:
 
   nsCString mContentLanguage;
 
   // The channel that got passed to nsDocument::StartDocumentLoad(), if any.
   nsCOMPtr<nsIChannel> mChannel;
 private:
   nsCString mContentType;
 protected:
+  // For document.write() we may need a different content type than mContentType.
+  nsCString mContentTypeForWriteCalls;
 
   // The document's security info
   nsCOMPtr<nsISupports> mSecurityInfo;
 
   // The channel that failed to load and resulted in an error page.
   // This only applies to error pages. Might be null.
   nsCOMPtr<nsIChannel> mFailedChannel;
 
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TextEditor.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/dom/CharacterData.h"
 #include "mozilla/dom/DocumentType.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/L10nUtilsBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "nsAttrValueOrString.h"
@@ -200,17 +201,17 @@ nsINode::UnsetProperty(nsAtom* aProperty
 
 nsINode::nsSlots*
 nsINode::CreateSlots()
 {
   return new nsSlots();
 }
 
 bool
-nsINode::IsEditableInternal() const
+nsINode::IsEditable() const
 {
   if (HasFlag(NODE_IS_EDITABLE)) {
     // The node is in an editable contentEditable subtree.
     return true;
   }
 
   nsIDocument *doc = GetUncomposedDoc();
 
@@ -903,20 +904,22 @@ nsINode::IsEqualNode(nsINode* aOther)
         }
         break;
       }
       case TEXT_NODE:
       case COMMENT_NODE:
       case CDATA_SECTION_NODE:
       case PROCESSING_INSTRUCTION_NODE:
       {
+        MOZ_ASSERT(node1->IsCharacterData());
+        MOZ_ASSERT(node2->IsCharacterData());
         string1.Truncate();
-        static_cast<nsIContent*>(node1)->AppendTextTo(string1);
+        static_cast<CharacterData*>(node1)->AppendTextTo(string1);
         string2.Truncate();
-        static_cast<nsIContent*>(node2)->AppendTextTo(string2);
+        static_cast<CharacterData*>(node2)->AppendTextTo(string2);
 
         if (!string1.Equals(string2)) {
           return false;
         }
 
         break;
       }
       case DOCUMENT_NODE:
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -506,16 +506,23 @@ public:
 
   /**
    * Return this node as Text if it is one, otherwise null.  This is defined
    * inline in Text.h.
    */
   mozilla::dom::Text* GetAsText();
   const mozilla::dom::Text* GetAsText() const;
 
+  /**
+   * Return this node as Text.  Asserts IsText().  This is defined inline in
+   * Text.h.
+   */
+  mozilla::dom::Text* AsText();
+  const mozilla::dom::Text* AsText() const;
+
   /*
    * Return whether the node is a ProcessingInstruction node.
    */
   bool IsProcessingInstruction() const
   {
     return NodeType() == PROCESSING_INSTRUCTION_NODE;
   }
 
@@ -1212,24 +1219,17 @@ public:
     if (aEditable) {
       SetFlags(NODE_IS_EDITABLE);
     }
     else {
       UnsetFlags(NODE_IS_EDITABLE);
     }
   }
 
-  bool IsEditable() const
-  {
-#ifdef MOZILLA_INTERNAL_API
-    return IsEditableInternal();
-#else
-    return IsEditableExternal();
-#endif
-  }
+  bool IsEditable() const;
 
   /**
    * Returns true if |this| is native anonymous (i.e. created by
    * nsIAnonymousContentCreator);
    */
   bool IsNativeAnonymous() const
   {
     return HasFlag(NODE_IS_NATIVE_ANONYMOUS);
@@ -1293,17 +1293,17 @@ public:
    * user does "Select All" while the focus is in this node. Note that if this
    * node is not in an editor, the result comes from the nsFrameSelection that
    * is related to aPresShell, so the result might not be the ancestor of this
    * node. Be aware that if this node and the computed selection limiter are
    * not in same subtree, this returns the root content of the closeset subtree.
    */
   nsIContent* GetSelectionRootContent(nsIPresShell* aPresShell);
 
-  virtual nsINodeList* ChildNodes();
+  nsINodeList* ChildNodes();
   nsIContent* GetFirstChild() const { return mFirstChild; }
   nsIContent* GetLastChild() const
   {
     uint32_t count = GetChildCount();
 
     return count > 0 ? GetChildAt_Deprecated(count - 1) : nullptr;
   }
 
@@ -1344,24 +1344,16 @@ public:
    * to let APZC be aware of it. It's used when the node may handle the apz
    * aware events and may do preventDefault to stop APZC to do default actions.
    *
    * For example, instead of scrolling page by APZ, we handle mouse wheel event
    * in HTMLInputElement with number type as increasing / decreasing its value.
    */
   virtual bool IsNodeApzAwareInternal() const;
 
-  // HTML elements named <shadow> may or may not be HTMLShadowElement.  This is
-  // a way to ask an element whether it's an HTMLShadowElement.
-  virtual bool IsHTMLShadowElement() const { return false; }
-
-  // Elements named <content> may or may not be HTMLContentElement.  This is a
-  // way to ask an element whether it's an HTMLContentElement.
-  virtual bool IsHTMLContentElement() const { return false; }
-
   void GetTextContent(nsAString& aTextContent,
                       mozilla::OOMReporter& aError)
   {
     GetTextContentInternal(aTextContent, aError);
   }
   void SetTextContent(const nsAString& aTextContent,
                       nsIPrincipal* aSubjectPrincipal,
                       mozilla::ErrorResult& aError)
@@ -1956,22 +1948,16 @@ protected:
   }
 
   /**
    * Invalidate cached child array inside mChildNodes
    * of type nsParentNodeChildContentList.
    */
   void InvalidateChildNodes();
 
-  bool IsEditableInternal() const;
-  virtual bool IsEditableExternal() const
-  {
-    return IsEditableInternal();
-  }
-
   virtual void GetTextContentInternal(nsAString& aTextContent,
                                       mozilla::OOMReporter& aError);
   virtual void SetTextContentInternal(const nsAString& aTextContent,
                                       nsIPrincipal* aSubjectPrincipal,
                                       mozilla::ErrorResult& aError)
   {
   }
 
--- a/dom/base/nsNodeInfoManager.cpp
+++ b/dom/base/nsNodeInfoManager.cpp
@@ -105,17 +105,17 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_B
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
 
 nsresult
 nsNodeInfoManager::Init(nsIDocument *aDocument)
 {
   NS_PRECONDITION(!mPrincipal,
                   "Being inited when we already have a principal?");
 
-  mPrincipal = NullPrincipal::Create();
+  mPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
 
   if (aDocument) {
     mBindingManager = new nsBindingManager(aDocument);
   }
 
   mDefaultPrincipal = mPrincipal;
 
   mDocument = aDocument;
--- a/dom/base/nsTreeSanitizer.cpp
+++ b/dom/base/nsTreeSanitizer.cpp
@@ -1581,17 +1581,17 @@ nsTreeSanitizer::InitializeStatics()
   }
 
   sAttributesMathML =
     new nsTHashtable<nsRefPtrHashKey<nsAtom>>(ArrayLength(kAttributesMathML));
   for (uint32_t i = 0; kAttributesMathML[i]; i++) {
     sAttributesMathML->PutEntry(*kAttributesMathML[i]);
   }
 
-  nsCOMPtr<nsIPrincipal> principal = NullPrincipal::Create();
+  nsCOMPtr<nsIPrincipal> principal = NullPrincipal::CreateWithoutOriginAttributes();
   principal.forget(&sNullPrincipal);
 }
 
 void
 nsTreeSanitizer::ReleaseStatics()
 {
   delete sElementsHTML;
   sElementsHTML = nullptr;
--- a/dom/bindings/SimpleGlobalObject.cpp
+++ b/dom/bindings/SimpleGlobalObject.cpp
@@ -113,17 +113,17 @@ SimpleGlobalObject::Create(GlobalType gl
            .setInvisibleToDebugger(true)
            // Put our SimpleGlobalObjects in the system zone, so we won't create
            // lots of zones for what are probably very short-lived
            // compartments.  This should help them be GCed quicker and take up
            // less memory before they're GCed.
            .setSystemZone();
 
     if (NS_IsMainThread()) {
-      nsCOMPtr<nsIPrincipal> principal = NullPrincipal::Create();
+      nsCOMPtr<nsIPrincipal> principal = NullPrincipal::CreateWithoutOriginAttributes();
       options.creationOptions().setTrace(xpc::TraceXPCGlobal);
       global = xpc::CreateGlobalObject(cx, js::Jsvalify(&SimpleGlobalClass),
                                        nsJSPrincipals::get(principal),
                                        options);
     } else {
       global = JS_NewGlobalObject(cx, js::Jsvalify(&SimpleGlobalClass),
                                   nullptr,
                                   JS::DontFireOnNewGlobalHook, options);
--- a/dom/console/ConsoleUtils.cpp
+++ b/dom/console/ConsoleUtils.cpp
@@ -145,17 +145,17 @@ JSObject*
 ConsoleUtils::GetOrCreateSandbox(JSContext* aCx)
 {
   AssertIsOnMainThread();
 
   if (!mSandbox) {
     nsIXPConnect* xpc = nsContentUtils::XPConnect();
     MOZ_ASSERT(xpc, "This should never be null!");
 
-    RefPtr<NullPrincipal> nullPrincipal = NullPrincipal::Create();
+    RefPtr<NullPrincipal> nullPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
 
     JS::Rooted<JSObject*> sandbox(aCx);
     nsresult rv = xpc->CreateSandbox(aCx, nullPrincipal, sandbox.address());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return nullptr;
     }
 
     mSandbox = new JSObjectHolder(aCx, sandbox);
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -1434,21 +1434,16 @@ EventStateManager::HandleCrossProcessEve
       continue;
     }
 
     RefPtr<nsFrameLoader> frameLoader = loaderOwner->GetFrameLoader();
     if (!frameLoader) {
       continue;
     }
 
-    if (frameLoader->EventMode() ==
-          FrameLoaderBinding::EVENT_MODE_DONT_FORWARD_TO_CHILD) {
-      continue;
-    }
-
     DispatchCrossProcessEvent(aEvent, frameLoader, aStatus);
   }
   return aEvent->HasBeenPostedToRemoteProcess();
 }
 
 //
 // CreateClickHoldTimer
 //
--- a/dom/gamepad/windows/WindowsGamepad.cpp
+++ b/dom/gamepad/windows/WindowsGamepad.cpp
@@ -855,17 +855,24 @@ WindowsGamepadService::HandleRawInput(HR
   }
 
   nsTArray<bool> buttons(gamepad->numButtons);
   buttons.SetLength(gamepad->numButtons);
   // If we don't zero out the buttons array first, sometimes it can reuse values.
   memset(buttons.Elements(), 0, gamepad->numButtons * sizeof(bool));
 
   for (unsigned i = 0; i < usageLength; i++) {
-    buttons[usages[i] - 1] = true;
+    // The button index in usages may be larger than what we detected when
+    // enumerating gamepads. If so, warn and continue.
+    //
+    // Usage ID of 0 is reserved, so it should always be 1 or higher.
+    if (NS_WARN_IF((usages[i] - 1u) >= buttons.Length())) {
+      continue;
+    }
+    buttons[usages[i] - 1u] = true;
   }
 
   if (gamepad->hasDpad) {
     // Get d-pad position as 4 buttons.
     ULONG value;
     if (mHID.mHidP_GetUsageValue(HidP_Input, gamepad->dpadCaps.UsagePage, 0, gamepad->dpadCaps.Range.UsageMin, &value, parsed, (PCHAR)raw->data.hid.bRawData, raw->data.hid.dwSizeHid) == HIDP_STATUS_SUCCESS) {
       UnpackDpad(static_cast<LONG>(value), gamepad, buttons);
     }
--- a/dom/html/HTMLAnchorElement.h
+++ b/dom/html/HTMLAnchorElement.h
@@ -17,17 +17,16 @@ class EventChainPostVisitor;
 class EventChainPreVisitor;
 namespace dom {
 
 class HTMLAnchorElement final : public nsGenericHTMLElement,
                                 public Link
 {
 public:
   using Element::GetText;
-  using Element::SetText;
 
   explicit HTMLAnchorElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
     : nsGenericHTMLElement(aNodeInfo)
     , Link(this)
   {
   }
 
   // nsISupports
--- a/dom/html/HTMLBodyElement.h
+++ b/dom/html/HTMLBodyElement.h
@@ -16,17 +16,16 @@ class TextEditor;
 namespace dom {
 
 class OnBeforeUnloadEventHandlerNonNull;
 
 class HTMLBodyElement final : public nsGenericHTMLElement
 {
 public:
   using Element::GetText;
-  using Element::SetText;
 
   explicit HTMLBodyElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
     : nsGenericHTMLElement(aNodeInfo)
   {
   }
 
   // nsISupports
   NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLBodyElement, nsGenericHTMLElement)
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -337,22 +337,16 @@ public:
     return mSelectionProperties;
   }
 
   bool HasPatternAttribute() const
   {
     return mHasPatternAttribute;
   }
 
-  virtual already_AddRefed<nsITextControlElement> GetAsTextControlElement() override
-  {
-    nsCOMPtr<nsITextControlElement> txt = this;
-    return txt.forget();
-  }
-
   // nsIConstraintValidation
   bool     IsTooLong();
   bool     IsTooShort();
   bool     IsValueMissing() const;
   bool     HasTypeMismatch() const;
   bool     HasPatternMismatch() const;
   bool     IsRangeOverflow() const;
   bool     IsRangeUnderflow() const;
--- a/dom/html/HTMLOptionElement.cpp
+++ b/dom/html/HTMLOptionElement.cpp
@@ -242,19 +242,18 @@ HTMLOptionElement::AfterSetAttr(int32_t 
 
 void
 HTMLOptionElement::GetText(nsAString& aText)
 {
   nsAutoString text;
 
   nsIContent* child = nsINode::GetFirstChild();
   while (child) {
-    if (child->NodeType() == TEXT_NODE ||
-        child->NodeType() == CDATA_SECTION_NODE) {
-      child->AppendTextTo(text);
+    if (Text* textChild = child->GetAsText()) {
+      textChild->AppendTextTo(text);
     }
     if (child->IsHTMLElement(nsGkAtoms::script) ||
         child->IsSVGElement(nsGkAtoms::script)) {
       child = child->GetNextNonChildNode(this);
     } else {
       child = child->GetNextNode(this);
     }
   }
--- a/dom/html/HTMLOptionElement.h
+++ b/dom/html/HTMLOptionElement.h
@@ -29,17 +29,16 @@ public:
            bool aSelected,
            ErrorResult& aError);
 
   NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLOptionElement, option)
 
   // nsISupports
   NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLOptionElement, nsGenericHTMLElement)
 
-  using mozilla::dom::Element::SetText;
   using mozilla::dom::Element::GetText;
 
   bool Selected() const
   {
     return mIsSelected;
   }
   void SetSelected(bool aValue);
 
--- a/dom/html/HTMLScriptElement.h
+++ b/dom/html/HTMLScriptElement.h
@@ -14,17 +14,16 @@
 namespace mozilla {
 namespace dom {
 
 class HTMLScriptElement final : public nsGenericHTMLElement,
                                 public ScriptElement
 {
 public:
   using Element::GetText;
-  using Element::SetText;
 
   HTMLScriptElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                     FromParser aFromParser);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   void GetInnerHTML(nsAString& aInnerHTML, OOMReporter& aError) override;
--- a/dom/html/HTMLTextAreaElement.h
+++ b/dom/html/HTMLTextAreaElement.h
@@ -159,22 +159,16 @@ public:
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLTextAreaElement,
                                            nsGenericHTMLFormElementWithState)
 
-  virtual already_AddRefed<nsITextControlElement> GetAsTextControlElement() override
-  {
-    nsCOMPtr<nsITextControlElement> txt = this;
-    return txt.forget();
-  }
-
   // nsIConstraintValidation
   bool     IsTooLong();
   bool     IsTooShort();
   bool     IsValueMissing() const;
   void     UpdateTooLongValidityState();
   void     UpdateTooShortValidityState();
   void     UpdateValueMissingValidityState();
   void     UpdateBarredFromConstraintValidation();
--- a/dom/html/HTMLTitleElement.h
+++ b/dom/html/HTMLTitleElement.h
@@ -16,17 +16,16 @@ class ErrorResult;
 
 namespace dom {
 
 class HTMLTitleElement final : public nsGenericHTMLElement,
                                public nsStubMutationObserver
 {
 public:
   using Element::GetText;
-  using Element::SetText;
 
   explicit HTMLTitleElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   //HTMLTitleElement
   void GetText(DOMString& aText, ErrorResult& aError);
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1200,17 +1200,17 @@ nsHTMLDocument::Open(JSContext* /* unuse
   nsCOMPtr<nsPIDOMWindowOuter> newWindow;
   // XXXbz We ignore aReplace for now.
   rv = win->OpenJS(aURL, aName, aFeatures, getter_AddRefs(newWindow));
   return newWindow.forget();
 }
 
 already_AddRefed<nsIDocument>
 nsHTMLDocument::Open(JSContext* cx,
-                     const nsAString& aType,
+                     const Optional<nsAString>& /* unused */,
                      const nsAString& aReplace,
                      ErrorResult& aError)
 {
   // Implements the "When called with two arguments (or fewer)" steps here:
   // https://html.spec.whatwg.org/multipage/webappapis.html#opening-the-input-stream
 
   MOZ_ASSERT(nsContentUtils::CanCallerAccess(static_cast<nsIDOMDocument*>(this)),
              "XOW should have caught this!");
@@ -1220,28 +1220,16 @@ nsHTMLDocument::Open(JSContext* cx,
     return nullptr;
   }
 
   if (ShouldThrowOnDynamicMarkupInsertion()) {
     aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
-  nsAutoCString contentType;
-  contentType.AssignLiteral("text/html");
-
-  nsAutoString type;
-  nsContentUtils::ASCIIToLower(aType, type);
-  nsAutoCString actualType, dummy;
-  NS_ParseRequestContentType(NS_ConvertUTF16toUTF8(type), actualType, dummy);
-  if (!actualType.EqualsLiteral("text/html") &&
-      !type.EqualsLiteral("replace")) {
-    contentType.AssignLiteral("text/plain");
-  }
-
   // If we already have a parser we ignore the document.open call.
   if (mParser || mParserAborted) {
     // The WHATWG spec says: "If the document has an active parser that isn't
     // a script-created parser, and the insertion point associated with that
     // parser's input stream is not undefined (that is, it does point to
     // somewhere in the input stream), then the method does nothing. Abort
     // these steps and return the Document object on which the method was
     // invoked."
@@ -1484,22 +1472,27 @@ nsHTMLDocument::Open(JSContext* cx,
           }
         }
       }
     }
   }
 
   mDidDocumentOpen = true;
 
+  nsAutoCString contentType(GetContentTypeInternal());
+
   // Call Reset(), this will now do the full reset
   Reset(channel, group);
   if (baseURI) {
     mDocumentBaseURI = baseURI;
   }
 
+  // Restore our type, since Reset() resets it.
+  SetContentTypeInternal(contentType);
+
   // Store the security info of the caller now that we're done
   // resetting the document.
   mSecurityInfo = securityInfo;
 
   mParserAborted = false;
   mParser = nsHtml5Module::NewHtml5Parser();
   nsHtml5Module::Initialize(mParser, this, uri, shell, channel);
   if (mReferrerPolicySet) {
@@ -1507,18 +1500,17 @@ nsHTMLDocument::Open(JSContext* cx,
     // start with the new referrer policy.
     nsHtml5TreeOpExecutor* executor = nullptr;
     executor = static_cast<nsHtml5TreeOpExecutor*> (mParser->GetContentSink());
     if (executor && mReferrerPolicySet) {
       executor->SetSpeculationReferrerPolicy(static_cast<ReferrerPolicy>(mReferrerPolicy));
     }
   }
 
-  // This will be propagated to the parser when someone actually calls write()
-  SetContentTypeInternal(contentType);
+  mContentTypeForWriteCalls.AssignLiteral("text/html");
 
   // Prepare the docshell and the document viewer for the impending
   // out of band document.write()
   shell->PrepareForNewContentModel();
 
   // Now check whether we were opened with a "replace" argument.  If
   // so, we need to tell the docshell to not create a new history
   // entry for this load. Otherwise, make sure that we're doing a normal load,
@@ -1574,17 +1566,17 @@ nsHTMLDocument::Close(ErrorResult& rv)
   }
 
   if (!mParser || !mParser->IsScriptCreated()) {
     return;
   }
 
   ++mWriteLevel;
   rv = (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(
-    EmptyString(), nullptr, GetContentTypeInternal(), true);
+    EmptyString(), nullptr, mContentTypeForWriteCalls, true);
   --mWriteLevel;
 
   // Even if that Parse() call failed, do the rest of this method
 
   // XXX Make sure that all the document.written content is
   // reflowed.  We should remove this call once we change
   // nsHTMLDocument::OpenCommon() so that it completely destroys the
   // earlier document's content and frame hierarchy.  Right now, it
@@ -1706,17 +1698,17 @@ nsHTMLDocument::WriteCommon(JSContext *c
       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                       NS_LITERAL_CSTRING("DOM Events"), this,
                                       nsContentUtils::eDOM_PROPERTIES,
                                       "DocumentWriteIgnored",
                                       nullptr, 0,
                                       mDocumentURI);
       return;
     }
-    nsCOMPtr<nsIDocument> ignored  = Open(cx, NS_LITERAL_STRING("text/html"),
+    nsCOMPtr<nsIDocument> ignored  = Open(cx, Optional<nsAString>(),
                                           EmptyString(), aRv);
 
     // If Open() fails, or if it didn't create a parser (as it won't
     // if the user chose to not discard the current document through
     // onbeforeunload), don't write anything.
     if (aRv.Failed() || !mParser) {
       return;
     }
@@ -1740,20 +1732,20 @@ nsHTMLDocument::WriteCommon(JSContext *c
   ++mWriteLevel;
 
   // This could be done with less code, but for performance reasons it
   // makes sense to have the code for two separate Parse() calls here
   // since the concatenation of strings costs more than we like. And
   // why pay that price when we don't need to?
   if (aNewlineTerminate) {
     aRv = (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(
-      aText + new_line, key, GetContentTypeInternal(), false);
+      aText + new_line, key, mContentTypeForWriteCalls, false);
   } else {
     aRv = (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(
-      aText, key, GetContentTypeInternal(), false);
+      aText, key, mContentTypeForWriteCalls, false);
   }
 
   --mWriteLevel;
 
   mTooDeepWriteRecursion = (mWriteLevel != 0 && mTooDeepWriteRecursion);
 }
 
 void
--- a/dom/html/nsHTMLDocument.h
+++ b/dom/html/nsHTMLDocument.h
@@ -162,17 +162,17 @@ public:
                                             const nsACString& aOrigHost);
   void GetCookie(nsAString& aCookie, mozilla::ErrorResult& rv);
   void SetCookie(const nsAString& aCookie, mozilla::ErrorResult& rv);
   void NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
                    JS::MutableHandle<JSObject*> aRetval,
                    mozilla::ErrorResult& rv);
   void GetSupportedNames(nsTArray<nsString>& aNames);
   already_AddRefed<nsIDocument> Open(JSContext* cx,
-                                     const nsAString& aType,
+                                     const mozilla::dom::Optional<nsAString>& /* unused */,
                                      const nsAString& aReplace,
                                      mozilla::ErrorResult& aError);
   already_AddRefed<nsPIDOMWindowOuter>
   Open(JSContext* cx,
        const nsAString& aURL,
        const nsAString& aName,
        const nsAString& aFeatures,
        bool aReplace,
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -37,16 +37,17 @@
 #include "mozilla/Preferences.h"
 #include "nsTextNode.h"
 #include "nsIController.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/dom/HTMLTextAreaElement.h"
+#include "mozilla/dom/Text.h"
 #include "nsNumberControlFrame.h"
 #include "nsFrameSelection.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -2580,17 +2581,17 @@ nsTextEditorState::SetPreviewText(const 
   Element* previewDiv = GetPreviewNode();
   if (!previewDiv)
     return;
 
   nsAutoString previewValue(aValue);
 
   nsContentUtils::RemoveNewlines(previewValue);
   MOZ_ASSERT(previewDiv->GetFirstChild(), "preview div has no child");
-  previewDiv->GetFirstChild()->SetText(previewValue, aNotify);
+  previewDiv->GetFirstChild()->AsText()->SetText(previewValue, aNotify);
 
   UpdateOverlayTextVisibility(aNotify);
 }
 
 void
 nsTextEditorState::GetPreviewText(nsAString& aValue)
 {
   // If we don't have a preview div, there's nothing to do.
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1374,17 +1374,17 @@ private:
   JSObject*
   GetSandboxInternal(JSContext* aCx)
   {
     if (!mSandbox) {
       nsIXPConnect* xpc = nsContentUtils::XPConnect();
       MOZ_ASSERT(xpc, "This should never be null!");
 
       // Let's use a null principal.
-      nsCOMPtr<nsIPrincipal> principal = NullPrincipal::Create();
+      nsCOMPtr<nsIPrincipal> principal = NullPrincipal::CreateWithoutOriginAttributes();
 
       JS::Rooted<JSObject*> sandbox(aCx);
       nsresult rv = xpc->CreateSandbox(aCx, principal, sandbox.address());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return nullptr;
       }
 
       mSandbox = new JSObjectHolder(aCx, sandbox);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -768,17 +768,17 @@ GetCreateWindowParams(mozIDOMWindowProxy
                       nsIDocShellLoadInfo* aLoadInfo,
                       nsACString& aBaseURIString, float* aFullZoom,
                       uint32_t* aReferrerPolicy,
                       nsIPrincipal** aTriggeringPrincipal)
 {
   *aFullZoom = 1.0f;
   auto* opener = nsPIDOMWindowOuter::From(aParent);
   if (!opener) {
-    nsCOMPtr<nsIPrincipal> nullPrincipal = NullPrincipal::Create();
+    nsCOMPtr<nsIPrincipal> nullPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
     NS_ADDREF(*aTriggeringPrincipal = nullPrincipal);
     return NS_OK;
   }
 
   nsCOMPtr<nsIDocument> doc = opener->GetDoc();
   NS_ADDREF(*aTriggeringPrincipal = doc->NodePrincipal());
   nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
   if (!baseURI) {
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -558,17 +558,17 @@ TabParent::RecvDropLinks(nsTArray<nsStri
       }
       links[i] = aLinks[i].get();
     }
     mVerifyDropLinks.Clear();
     nsCOMPtr<nsIPrincipal> triggeringPrincipal;
     if (loadUsingSystemPrincipal) {
       triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
     } else {
-      triggeringPrincipal = NullPrincipal::Create();
+      triggeringPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
     }
     browser->DropLinks(aLinks.Length(), links.get(), triggeringPrincipal);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 TabParent::RecvEvent(const RemoteDOMEvent& aEvent)
--- a/dom/svg/SVGTransformListParser.cpp
+++ b/dom/svg/SVGTransformListParser.cpp
@@ -109,25 +109,33 @@ SVGTransformListParser::ParseArguments(f
     return false;
   }
 
   if (!SVGContentUtils::ParseNumber(mIter, mEnd, aResult[0])) {
     return false;
   }
   *aParsedCount = 1;
 
-  while (SkipWsp()) {
+  while (true) {
+    RangedPtr<const char16_t> iter(mIter);
+    if (!SkipWsp()) {
+      return false;
+    }
     if (*mIter == ')') {
       ++mIter;
       return true;
     }
     if (*aParsedCount == aMaxCount) {
       return false;
     }
     SkipCommaWsp();
+    if (iter == mIter) {
+      // There must be either whitespace or a comma between values
+      return false;
+    }
     if (!SVGContentUtils::ParseNumber(mIter, mEnd, aResult[(*aParsedCount)++])) {
       return false;
     }
   }
   return false;
 }
 
 bool
--- a/dom/webidl/FrameLoader.webidl
+++ b/dom/webidl/FrameLoader.webidl
@@ -112,35 +112,16 @@ interface FrameLoader {
    * @param aProgressListener optional print progress listener.
    */
   [Throws]
   void print(unsigned long long aOuterWindowID,
              nsIPrintSettings aPrintSettings,
              optional nsIWebProgressListener? aProgressListener = null);
 
   /**
-   * The default event mode automatically forwards the events
-   * handled in EventStateManager::HandleCrossProcessEvent to
-   * the child content process when these events are targeted to
-   * the remote browser element.
-   *
-   * Used primarly for input events (mouse, keyboard)
-   */
-  const unsigned long EVENT_MODE_NORMAL_DISPATCH = 0x00000000;
-
-  /**
-   * With this event mode, it's the application's responsability to
-   * convert and forward events to the content process
-   */
-  const unsigned long EVENT_MODE_DONT_FORWARD_TO_CHILD = 0x00000001;
-
-  [Pure]
-  attribute unsigned long eventMode;
-
-  /**
    * If false, then the subdocument is not clipped to its CSS viewport, and the
    * subdocument's viewport scrollbar(s) are not rendered.
    * Defaults to true.
    */
   attribute boolean clipSubdocument;
 
   /**
    * If false, then the subdocument's scroll coordinates will not be clamped
--- a/dom/webidl/HTMLDocument.webidl
+++ b/dom/webidl/HTMLDocument.webidl
@@ -11,17 +11,17 @@ interface HTMLDocument : Document {
            [Throws]
            attribute DOMString cookie;
   // DOM tree accessors
   [Throws]
   getter object (DOMString name);
 
   // dynamic markup insertion
   [CEReactions, Throws]
-  Document open(optional DOMString type = "text/html", optional DOMString replace = "");
+  Document open(optional DOMString type, optional DOMString replace = ""); // type is ignored
   [CEReactions, Throws]
   WindowProxy? open(DOMString url, DOMString name, DOMString features, optional boolean replace = false);
   [CEReactions, Throws]
   void close();
   [CEReactions, Throws]
   void write(DOMString... text);
   [CEReactions, Throws]
   void writeln(DOMString... text);
--- a/dom/xbl/nsXBLPrototypeBinding.cpp
+++ b/dom/xbl/nsXBLPrototypeBinding.cpp
@@ -36,16 +36,17 @@
 #include "nsContentUtils.h"
 #include "nsTextFragment.h"
 #include "nsTextNode.h"
 #include "nsIInterfaceInfo.h"
 #include "nsIScriptError.h"
 
 #include "nsXBLResourceLoader.h"
 #include "mozilla/dom/CDATASection.h"
+#include "mozilla/dom/CharacterData.h"
 #include "mozilla/dom/Comment.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
 
 #ifdef MOZ_XUL
 #include "nsXULElement.h"
 #endif
@@ -1209,17 +1210,17 @@ nsXBLPrototypeBinding::ReadContentNode(n
   // There is no content to read so just return.
   if (namespaceID == XBLBinding_Serialize_NoContent)
     return NS_OK;
 
   // If this is a text type, just read the string and return.
   if (namespaceID == XBLBinding_Serialize_TextNode ||
       namespaceID == XBLBinding_Serialize_CDATANode ||
       namespaceID == XBLBinding_Serialize_CommentNode) {
-    nsCOMPtr<nsIContent> content;
+    RefPtr<CharacterData> content;
     switch (namespaceID) {
       case XBLBinding_Serialize_TextNode:
         content = new nsTextNode(aNim);
         break;
       case XBLBinding_Serialize_CDATANode:
         content = new CDATASection(aNim);
         break;
       case XBLBinding_Serialize_CommentNode:
@@ -1229,17 +1230,17 @@ nsXBLPrototypeBinding::ReadContentNode(n
         break;
     }
 
     nsAutoString text;
     rv = aStream->ReadString(text);
     NS_ENSURE_SUCCESS(rv, rv);
 
     content->SetText(text, false);
-    content.swap(*aContent);
+    content.forget(aContent);
     return NS_OK;
   }
 
   // Otherwise, it's an element, so read its tag, attributes and children.
   nsAutoString prefix, tag;
   rv = aStream->ReadString(prefix);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/xml/nsXMLContentSink.h
+++ b/dom/xml/nsXMLContentSink.h
@@ -18,16 +18,17 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDTD.h"
 #include "mozilla/dom/FromParser.h"
 
 class nsIDocument;
 class nsIURI;
 class nsIContent;
 class nsIParser;
+class nsTextNode;
 
 namespace mozilla {
 namespace dom {
 class NodeInfo;
 class ProcessingInstruction;
 } // namespace dom
 } // namespace mozilla
 
@@ -189,17 +190,17 @@ protected:
   nsCOMPtr<nsIContent> mCurrentHead;  // When set, we're in an XHTML <haed>
 
   XMLContentSinkState mState;
 
   // The length of the valid data in mText.
   int32_t mTextLength;
 
   int32_t mNotifyLevel;
-  nsCOMPtr<nsIContent> mLastTextNode;
+  RefPtr<nsTextNode> mLastTextNode;
 
   uint8_t mPrettyPrintXML : 1;
   uint8_t mPrettyPrintHasSpecialRoot : 1;
   uint8_t mPrettyPrintHasFactoredElements : 1;
   uint8_t mPrettyPrinting : 1;  // True if we called PrettyPrint() and it
                                 // decided we should in fact prettyprint.
   // True to call prevent script execution in the fragment mode.
   uint8_t mPreventScriptExecution : 1;
--- a/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp
+++ b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp
@@ -14,16 +14,17 @@
 #include "nsString.h"
 #include "nsTextFragment.h"
 #include "txXMLUtils.h"
 #include "txLog.h"
 #include "nsUnicharUtils.h"
 #include "nsAttrName.h"
 #include "nsTArray.h"
 #include "mozilla/dom/Attr.h"
+#include "mozilla/dom/CharacterData.h"
 #include "mozilla/dom/Element.h"
 #include <stdint.h>
 #include <algorithm>
 
 using namespace mozilla::dom;
 
 txXPathTreeWalker::txXPathTreeWalker(const txXPathTreeWalker& aOther)
     : mPosition(aOther.mPosition)
@@ -464,17 +465,18 @@ txXPathNodeUtils::appendNodeValue(const 
         aNode.mNode->IsElement() ||
         aNode.mNode->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT)) {
         nsContentUtils::AppendNodeTextContent(aNode.mNode, true, aResult,
                                               mozilla::fallible);
 
         return;
     }
 
-    aNode.Content()->AppendTextTo(aResult);
+    MOZ_ASSERT(aNode.mNode->IsCharacterData());
+    static_cast<CharacterData*>(aNode.Content())->AppendTextTo(aResult);
 }
 
 /* static */
 bool
 txXPathNodeUtils::isWhitespace(const txXPathNode& aNode)
 {
     NS_ASSERTION(aNode.isContent() && isText(aNode), "Wrong type!");
 
--- a/dom/xslt/xslt/txEXSLTFunctions.cpp
+++ b/dom/xslt/xslt/txEXSLTFunctions.cpp
@@ -81,22 +81,24 @@ createTextNode(txIEvalContext *aContext,
         NS_ERROR("Need txExecutionState!");
 
         return NS_ERROR_UNEXPECTED;
     }
 
     const txXPathNode& document = es->getSourceDocument();
 
     nsIDocument *doc = txXPathNativeNode::getDocument(document);
-    nsCOMPtr<nsIContent> text = new nsTextNode(doc->NodeInfoManager());
+    RefPtr<nsTextNode> text = new nsTextNode(doc->NodeInfoManager());
 
     nsresult rv = text->SetText(aValue, false);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    *aResult = txXPathNativeNode::createXPathNode(text, true);
+    // nsTextNode implements both nsIDOMNode and nsIContent, so the
+    // call would be ambiguous without the AsContent() call.
+    *aResult = txXPathNativeNode::createXPathNode(text->AsContent(), true);
     NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
 
     return NS_OK;
 }
 
 static already_AddRefed<DocumentFragment>
 createDocFragment(txIEvalContext *aContext)
 {
--- a/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txMozillaStylesheetCompiler.cpp
@@ -32,16 +32,17 @@
 #include "txStylesheetCompiler.h"
 #include "txXMLUtils.h"
 #include "nsAttrName.h"
 #include "nsIScriptError.h"
 #include "nsIURL.h"
 #include "nsError.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/Text.h"
 #include "mozilla/Encoding.h"
 #include "mozilla/UniquePtr.h"
 
 using namespace mozilla;
 using mozilla::net::ReferrerPolicy;
 
 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
@@ -559,19 +560,19 @@ handleNode(nsINode* aNode, txStylesheetC
 
             rv = handleNode(child, aCompiler);
             NS_ENSURE_SUCCESS(rv, rv);
         }
 
         rv = aCompiler->endElement();
         NS_ENSURE_SUCCESS(rv, rv);
     }
-    else if (aNode->IsNodeOfType(nsINode::eTEXT)) {
+    else if (dom::Text* text = aNode->GetAsText()) {
         nsAutoString chars;
-        static_cast<nsIContent*>(aNode)->AppendTextTo(chars);
+        text->AppendTextTo(chars);
         rv = aCompiler->characters(chars);
         NS_ENSURE_SUCCESS(rv, rv);
     }
     else if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
         for (nsIContent* child = aNode->GetFirstChild();
              child;
              child = child->GetNextSibling()) {
 
--- a/gfx/thebes/gfxSVGGlyphs.cpp
+++ b/gfx/thebes/gfxSVGGlyphs.cpp
@@ -352,17 +352,17 @@ gfxSVGGlyphsDocument::ParseDocument(cons
     nsCOMPtr<nsIURI> uri;
     nsHostObjectProtocolHandler::GenerateURIString(NS_LITERAL_CSTRING(FONTTABLEURI_SCHEME),
                                                    nullptr,
                                                    mSVGGlyphsDocumentURI);
 
     rv = NS_NewURI(getter_AddRefs(uri), mSVGGlyphsDocumentURI);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsIPrincipal> principal = NullPrincipal::Create();
+    nsCOMPtr<nsIPrincipal> principal = NullPrincipal::CreateWithoutOriginAttributes();
 
     nsCOMPtr<nsIDOMDocument> domDoc;
     rv = NS_NewDOMDocument(getter_AddRefs(domDoc),
                            EmptyString(),   // aNamespaceURI
                            EmptyString(),   // aQualifiedName
                            nullptr,          // aDoctype
                            uri, uri, principal,
                            false,           // aLoadedAsData
--- a/image/decoders/icon/android/nsIconChannel.cpp
+++ b/image/decoders/icon/android/nsIconChannel.cpp
@@ -110,17 +110,17 @@ moz_icon_to_channel(nsIURI* aURI, const 
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = stream->AdoptData((char*)buf, buf_size);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // nsIconProtocolHandler::NewChannel2 will provide the correct loadInfo for
   // this iconChannel. Use the most restrictive security settings for the
   // temporary loadInfo to make sure the channel can not be openend.
-  nsCOMPtr<nsIPrincipal> nullPrincipal = NullPrincipal::Create();
+  nsCOMPtr<nsIPrincipal> nullPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
   return NS_NewInputStreamChannel(aChannel,
                                   aURI,
                                   stream.forget(),
                                   nullPrincipal,
                                   nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
                                   nsIContentPolicy::TYPE_INTERNAL_IMAGE,
                                   NS_LITERAL_CSTRING(IMAGE_ICON_MS));
 }
--- a/image/decoders/icon/gtk/nsIconChannel.cpp
+++ b/image/decoders/icon/gtk/nsIconChannel.cpp
@@ -101,17 +101,17 @@ moz_gdk_pixbuf_to_channel(GdkPixbuf* aPi
 
   // If this no longer holds then re-examine buf's lifetime.
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // nsIconProtocolHandler::NewChannel2 will provide the correct loadInfo for
   // this iconChannel. Use the most restrictive security settings for the
   // temporary loadInfo to make sure the channel can not be openend.
-  nsCOMPtr<nsIPrincipal> nullPrincipal = NullPrincipal::Create();
+  nsCOMPtr<nsIPrincipal> nullPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
   return NS_NewInputStreamChannel(aChannel,
                                   aURI,
                                   stream.forget(),
                                   nullPrincipal,
                                   nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
                                   nsIContentPolicy::TYPE_INTERNAL_IMAGE,
                                   NS_LITERAL_CSTRING(IMAGE_ICON_MS));
 }
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -974,17 +974,17 @@ xpc::CreateSandboxObject(JSContext* cx, 
 {
     // Create the sandbox global object
     nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop);
     if (!principal) {
         nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(prinOrSop);
         if (sop) {
             principal = sop->GetPrincipal();
         } else {
-            RefPtr<NullPrincipal> nullPrin = NullPrincipal::Create();
+            RefPtr<NullPrincipal> nullPrin = NullPrincipal::CreateWithoutOriginAttributes();
             principal = nullPrin;
         }
     }
     MOZ_ASSERT(principal);
 
     JS::CompartmentOptions compartmentOptions;
 
     auto& creationOptions = compartmentOptions.creationOptions();
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsBidiPresUtils.h"
 
 #include "mozilla/IntegerRange.h"
+#include "mozilla/dom/Text.h"
 
 #include "gfxContext.h"
 #include "nsAutoPtr.h"
 #include "nsFontMetrics.h"
 #include "nsGkAtoms.h"
 #include "nsPresContext.h"
 #include "nsBidiUtils.h"
 #include "nsCSSFrameConstructor.h"
@@ -1131,24 +1132,24 @@ nsBidiPresUtils::TraverseFrames(nsBlockI
 
       // Append the content of the frame to the paragraph buffer
       LayoutFrameType frameType = frame->Type();
       if (LayoutFrameType::Text == frameType) {
         if (content != aBpd->mPrevContent) {
           aBpd->mPrevContent = content;
           if (!frame->StyleText()->NewlineIsSignificant(
                 static_cast<nsTextFrame*>(frame))) {
-            content->AppendTextTo(aBpd->mBuffer);
+            content->GetAsText()->AppendTextTo(aBpd->mBuffer);
           } else {
             /*
              * For preformatted text we have to do bidi resolution on each line
              * separately.
              */
             nsAutoString text;
-            content->AppendTextTo(text);
+            content->GetAsText()->AppendTextTo(text);
             nsIFrame* next;
             do {
               next = nullptr;
 
               int32_t start, end;
               frame->GetOffsets(start, end);
               int32_t endLine = text.FindChar('\n', start);
               if (endLine == -1) {
--- a/layout/base/nsCounterManager.cpp
+++ b/layout/base/nsCounterManager.cpp
@@ -9,16 +9,17 @@
 #include "nsCounterManager.h"
 
 #include "mozilla/Likely.h"
 #include "mozilla/WritingModes.h"
 #include "nsBulletFrame.h" // legacy location for list style type to text code
 #include "nsContentUtils.h"
 #include "nsIContent.h"
 #include "nsTArray.h"
+#include "mozilla/dom/Text.h"
 
 using namespace mozilla;
 
 bool
 nsCounterUseNode::InitTextFrame(nsGenConList* aList,
                                 nsIFrame* aPseudoFrame,
                                 nsIFrame* aTextFrame)
 {
@@ -28,17 +29,17 @@ nsCounterUseNode::InitTextFrame(nsGenCon
   counterList->Insert(this);
   aPseudoFrame->AddStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE);
   bool dirty = counterList->IsDirty();
   if (!dirty) {
     if (counterList->IsLast(this)) {
       Calc(counterList);
       nsAutoString contentString;
       GetText(contentString);
-      aTextFrame->GetContent()->SetText(contentString, false);
+      aTextFrame->GetContent()->AsText()->SetText(contentString, false);
     } else {
       // In all other cases (list already dirty or node not at the end),
       // just start with an empty string for now and when we recalculate
       // the list we'll change the value to the right one.
       counterList->SetDirty();
       return true;
     }
   }
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -9448,17 +9448,21 @@ nsLayoutUtils::ComputeScrollMetadata(nsI
 
   metrics.SetRootCompositionSize(
     nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame ? aScrollFrame : aForFrame,
                                                 isRootContentDocRootScrollFrame, metrics));
 
   if (gfxPrefs::APZPrintTree() || gfxPrefs::APZTestLoggingEnabled()) {
     if (nsIContent* content = frameForCompositionBoundsCalculation->GetContent()) {
       nsAutoString contentDescription;
-      content->Describe(contentDescription);
+      if (content->IsElement()) {
+        content->AsElement()->Describe(contentDescription);
+      } else {
+        contentDescription.AssignLiteral("(not an element)");
+      }
       metadata.SetContentDescription(NS_LossyConvertUTF16toASCII(contentDescription));
       if (IsAPZTestLoggingEnabled()) {
         LogTestDataForPaint(aLayerManager, scrollId, "contentDescription",
                             metadata.GetContentDescription().get());
       }
     }
   }
 
--- a/layout/base/nsQuoteList.cpp
+++ b/layout/base/nsQuoteList.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* implementation of quotes for the CSS 'content' property */
 
 #include "nsQuoteList.h"
 #include "nsReadableUtils.h"
 #include "nsIContent.h"
 #include "mozilla/ErrorResult.h"
+#include "mozilla/dom/Text.h"
 
 using namespace mozilla;
 
 bool
 nsQuoteNode::InitTextFrame(nsGenConList* aList, nsIFrame* aPseudoFrame,
                            nsIFrame* aTextFrame)
 {
   nsGenConNode::InitTextFrame(aList, aPseudoFrame, aTextFrame);
@@ -24,17 +25,17 @@ nsQuoteNode::InitTextFrame(nsGenConList*
   quoteList->Insert(this);
   if (quoteList->IsLast(this))
     quoteList->Calc(this);
   else
     dirty = true;
 
   // Don't set up text for 'no-open-quote' and 'no-close-quote'.
   if (IsRealQuote()) {
-    aTextFrame->GetContent()->SetText(*Text(), false);
+    aTextFrame->GetContent()->AsText()->SetText(*Text(), false);
   }
   return dirty;
 }
 
 const nsString*
 nsQuoteNode::Text()
 {
   NS_ASSERTION(mType == eStyleContentType_OpenQuote ||
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -1292,16 +1292,22 @@ nsComboboxControlFrame::AppendAnonymousC
     aElements.AppendElement(mDisplayContent);
   }
 
   if (mButtonContent) {
     aElements.AppendElement(mButtonContent);
   }
 }
 
+nsIContent*
+nsComboboxControlFrame::GetDisplayNode() const
+{
+  return mDisplayContent;
+}
+
 // XXXbz this is a for-now hack.  Now that display:inline-block works,
 // need to revisit this.
 class nsComboboxDisplayFrame : public nsBlockFrame {
 public:
   NS_DECL_FRAMEARENA_HELPERS(nsComboboxDisplayFrame)
 
   nsComboboxDisplayFrame(ComputedStyle* aStyle,
                          nsComboboxControlFrame* aComboBox)
--- a/layout/forms/nsComboboxControlFrame.h
+++ b/layout/forms/nsComboboxControlFrame.h
@@ -31,16 +31,17 @@
 #include "nsIRollupListener.h"
 #include "nsIStatefulFrame.h"
 #include "nsThreadUtils.h"
 
 class nsIListControlFrame;
 class nsComboboxDisplayFrame;
 class nsIDOMEventListener;
 class nsIScrollableFrame;
+class nsTextNode;
 
 namespace mozilla {
 namespace gfx {
 class DrawTarget;
 } // namespace gfx
 } // namespace mozilla
 
 class nsComboboxControlFrame final : public nsBlockFrame,
@@ -65,17 +66,17 @@ public:
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS(nsComboboxControlFrame)
 
   // nsIAnonymousContentCreator
   virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
   virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
                                         uint32_t aFilter) override;
 
-  nsIContent* GetDisplayNode() { return mDisplayContent; }
+  nsIContent* GetDisplayNode() const;
   nsIFrame* CreateFrameForDisplayNode();
 
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
 #endif
 
   virtual nscoord GetMinISize(gfxContext *aRenderingContext) override;
 
@@ -280,17 +281,17 @@ protected:
 
 private:
   // If our total transform to the root frame of the root document is only a 2d
   // translation then return that translation, otherwise returns (0,0).
   nsPoint GetCSSTransformTranslation();
 
 protected:
   nsFrameList              mPopupFrames;             // additional named child list
-  nsCOMPtr<nsIContent>     mDisplayContent;          // Anonymous content used to display the current selection
+  RefPtr<nsTextNode>       mDisplayContent;          // Anonymous content used to display the current selection
   RefPtr<Element>     mButtonContent;                // Anonymous content for the button
   nsContainerFrame*        mDisplayFrame;            // frame to display selection
   nsIFrame*                mButtonFrame;             // button frame
   nsIFrame*                mDropdownFrame;           // dropdown list frame
   nsIListControlFrame *    mListControlFrame;        // ListControl Interface for the dropdown frame
 
   // The inline size of our display area.  Used by that frame's reflow
   // to size to the full inline size except the drop-marker.
--- a/layout/forms/nsGfxButtonControlFrame.h
+++ b/layout/forms/nsGfxButtonControlFrame.h
@@ -7,16 +7,18 @@
 #ifndef nsGfxButtonControlFrame_h___
 #define nsGfxButtonControlFrame_h___
 
 #include "mozilla/Attributes.h"
 #include "nsHTMLButtonControlFrame.h"
 #include "nsCOMPtr.h"
 #include "nsIAnonymousContentCreator.h"
 
+class nsTextNode;
+
 // Class which implements the input[type=button, reset, submit] and
 // browse button for input[type=file].
 // The label for button is specified through generated content
 // in the ua.css file.
 
 class nsGfxButtonControlFrame final
   : public nsHTMLButtonControlFrame
   , public nsIAnonymousContentCreator
@@ -51,14 +53,14 @@ public:
 
 protected:
   nsresult GetDefaultLabel(nsAString& aLabel) const;
 
   nsresult GetLabel(nsString& aLabel);
 
   virtual bool IsInput() override { return true; }
 private:
-  nsCOMPtr<nsIContent> mTextContent;
+  RefPtr<nsTextNode> mTextContent;
 };
 
 
 #endif
 
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -503,17 +503,17 @@ nsTextControlFrame::CreatePlaceholderIfN
 
   if (placeholderTxt.IsEmpty()) {
     return;
   }
 
   mPlaceholderDiv = CreateEmptyDivWithTextNode(*this);
   // Associate ::placeholder pseudo-element with the placeholder node.
   mPlaceholderDiv->SetPseudoElementType(CSSPseudoElementType::placeholder);
-  mPlaceholderDiv->GetFirstChild()->SetText(placeholderTxt, false);
+  mPlaceholderDiv->GetFirstChild()->AsText()->SetText(placeholderTxt, false);
 }
 
 void
 nsTextControlFrame::CreatePreviewIfNeeded()
 {
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   if (!txtCtrl->IsPreviewEnabled()) {
     return;
@@ -1236,18 +1236,20 @@ nsTextControlFrame::SetInitialChildList(
       delete contentScrollPos;
     }
   }
 }
 
 void
 nsTextControlFrame::SetValueChanged(bool aValueChanged)
 {
-  nsCOMPtr<nsITextControlElement> txtCtrl =
-    GetContent()->GetAsTextControlElement();
+  nsCOMPtr<nsITextControlElement> txtCtrl = HTMLInputElement::FromNode(GetContent());
+  if (!txtCtrl) {
+    txtCtrl = HTMLTextAreaElement::FromNode(GetContent());
+  }
   MOZ_ASSERT(txtCtrl, "Content not a text control element");
 
   if (mPlaceholderDiv) {
     AutoWeakFrame weakFrame(this);
     txtCtrl->UpdateOverlayTextVisibility(true);
     if (!weakFrame.IsAlive()) {
       return;
     }
@@ -1264,25 +1266,28 @@ nsTextControlFrame::UpdateValueDisplay(b
 {
   if (!IsSingleLineTextControl()) // textareas don't use this
     return NS_OK;
 
   NS_PRECONDITION(mRootNode, "Must have a div content\n");
   NS_PRECONDITION(!mEditorHasBeenInitialized,
                   "Do not call this after editor has been initialized");
 
-  nsIContent* textContent = mRootNode->GetFirstChild();
-  if (!textContent) {
+  nsIContent* childContent = mRootNode->GetFirstChild();
+  Text* textContent;
+  if (!childContent) {
     // Set up a textnode with our value
     RefPtr<nsTextNode> textNode =
       new nsTextNode(mContent->NodeInfo()->NodeInfoManager());
     textNode->MarkAsMaybeModifiedFrequently();
 
     mRootNode->AppendChildTo(textNode, aNotify);
     textContent = textNode;
+  } else {
+    textContent = childContent->AsText();
   }
 
   NS_ENSURE_TRUE(textContent, NS_ERROR_UNEXPECTED);
 
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   MOZ_ASSERT(txtCtrl);
 
   // Get the current value of the textfield from the content.
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2172,34 +2172,61 @@ public:
                                mozilla::wr::IpcResourceUpdateQueue& aResources,
                                const StackingContextHelper& aSc,
                                mozilla::layers::WebRenderLayerManager* aManager,
                                nsDisplayListBuilder* aDisplayListBuilder) override;
   NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY)
 private:
   Color ComputeColor() const;
 
+  static Color ComputeColorFromSelectionStyle(ComputedStyle&);
+  static Color ApplyTransparencyIfNecessary(nscolor);
+
   int16_t mSelectionValue;
 };
 
 Color
+nsDisplaySelectionOverlay::ApplyTransparencyIfNecessary(nscolor aColor)
+{
+  // If it has already alpha, leave it like that.
+  if (NS_GET_A(aColor) != 255) {
+    return ToDeviceColor(aColor);
+  }
+
+  // NOTE(emilio): Blink and WebKit do something slightly different here, and
+  // blend the color with white instead, both for overlays and text backgrounds.
+  auto color = Color::FromABGR(aColor);
+  color.a = 0.5;
+  return ToDeviceColor(color);
+}
+
+Color
+nsDisplaySelectionOverlay::ComputeColorFromSelectionStyle(ComputedStyle& aStyle)
+{
+  return ApplyTransparencyIfNecessary(
+    aStyle.GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor));
+}
+
+Color
 nsDisplaySelectionOverlay::ComputeColor() const
 {
   LookAndFeel::ColorID colorID;
   if (mSelectionValue == nsISelectionController::SELECTION_ON) {
+    if (RefPtr<ComputedStyle> style = mFrame->ComputeSelectionStyle()) {
+      return ComputeColorFromSelectionStyle(*style);
+    }
     colorID = LookAndFeel::eColorID_TextSelectBackground;
   } else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) {
     colorID = LookAndFeel::eColorID_TextSelectBackgroundAttention;
   } else {
     colorID = LookAndFeel::eColorID_TextSelectBackgroundDisabled;
   }
 
-  Color c = Color::FromABGR(LookAndFeel::GetColor(colorID, NS_RGB(255, 255, 255)));
-  c.a = .5;
-  return ToDeviceColor(c);
+  return ApplyTransparencyIfNecessary(
+    LookAndFeel::GetColor(colorID, NS_RGB(255, 255, 255)));
 }
 
 void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder,
                                       gfxContext* aCtx)
 {
   DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
   ColorPattern color(ComputeColor());
 
@@ -2222,55 +2249,81 @@ nsDisplaySelectionOverlay::CreateWebRend
   wr::LayoutRect bounds = aSc.ToRelativeLayoutRect(
     LayoutDeviceRect::FromAppUnits(nsRect(ToReferenceFrame(), Frame()->GetSize()),
                                    mFrame->PresContext()->AppUnitsPerDevPixel()));
   aBuilder.PushRect(bounds, bounds, !BackfaceIsHidden(),
                     wr::ToColorF(ComputeColor()));
   return true;
 }
 
+static Element*
+FindElementAncestorForMozSelection(nsIContent* aContent)
+{
+  NS_ENSURE_TRUE(aContent, nullptr);
+  while (aContent && aContent->IsInNativeAnonymousSubtree()) {
+    aContent = aContent->GetBindingParent();
+  }
+  NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
+  while (aContent && !aContent->IsElement()) {
+    aContent = aContent->GetParent();
+  }
+  return aContent ? aContent->AsElement() : nullptr;
+}
+
+
+already_AddRefed<ComputedStyle>
+nsIFrame::ComputeSelectionStyle() const
+{
+  Element* element = FindElementAncestorForMozSelection(GetContent());
+  if (!element) {
+    return nullptr;
+  }
+  RefPtr<ComputedStyle> sc =
+    PresContext()->StyleSet()->ProbePseudoElementStyle(
+      element, CSSPseudoElementType::mozSelection, Style());
+  return sc.forget();
+}
+
 /********************************************************
 * Refreshes each content's frame
 *********************************************************/
 
 void
 nsFrame::DisplaySelectionOverlay(nsDisplayListBuilder*   aBuilder,
                                  nsDisplayList*          aList,
                                  uint16_t                aContentType)
 {
-  if (!IsSelected() || !IsVisibleForPainting(aBuilder))
+  if (!IsSelected() || !IsVisibleForPainting(aBuilder)) {
     return;
-
-  nsPresContext* presContext = PresContext();
-  nsIPresShell *shell = presContext->PresShell();
-  if (!shell)
+  }
+
+  int16_t displaySelection = PresShell()->GetSelectionFlags();
+  if (!(displaySelection & aContentType)) {
     return;
-
-  int16_t displaySelection = shell->GetSelectionFlags();
-  if (!(displaySelection & aContentType))
-    return;
+  }
 
   const nsFrameSelection* frameSelection = GetConstFrameSelection();
   int16_t selectionValue = frameSelection->GetDisplaySelection();
 
-  if (selectionValue <= nsISelectionController::SELECTION_HIDDEN)
+  if (selectionValue <= nsISelectionController::SELECTION_HIDDEN) {
     return; // selection is hidden or off
-
-  nsIContent *newContent = mContent->GetParent();
+  }
+
+  nsIContent* newContent = mContent->GetParent();
 
   //check to see if we are anonymous content
   int32_t offset = 0;
   if (newContent) {
     // XXXbz there has GOT to be a better way of determining this!
     offset = newContent->ComputeIndexOf(mContent);
   }
 
   //look up to see what selection(s) are on this frame
-  UniquePtr<SelectionDetails> details
-    = frameSelection->LookUpSelection(newContent, offset, 1, false);
+  UniquePtr<SelectionDetails> details =
+    frameSelection->LookUpSelection(newContent, offset, 1, false);
   if (!details)
     return;
 
   bool normal = false;
   for (SelectionDetails* sd = details.get(); sd; sd = sd->mNext.get()) {
     if (sd->mSelectionType == SelectionType::eNormal) {
       normal = true;
     }
@@ -4240,17 +4293,17 @@ nsFrame::HandlePress(nsPresContext* aPre
 
   fc->SetDelayedCaretData(0);
 
   // Check if any part of this frame is selected, and if the
   // user clicked inside the selected region. If so, we delay
   // starting a new selection since the user may be trying to
   // drag the selected region to some other app.
 
-  if (GetContent()->IsSelectionDescendant())
+  if (GetContent() && GetContent()->IsSelectionDescendant())
   {
     bool inSelection = false;
     UniquePtr<SelectionDetails> details
       = frameselection->LookUpSelection(offsets.content, 0,
                                         offsets.EndOffset(), false);
 
     //
     // If there are any details, check to see if the user clicked
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -867,16 +867,18 @@ public:
    * The indicies must be consecutive and implementations MUST return null if
    * asked for an index that is out of range.
    */
   virtual ComputedStyle* GetAdditionalComputedStyle(int32_t aIndex) const = 0;
 
   virtual void SetAdditionalComputedStyle(int32_t aIndex,
                                           ComputedStyle* aComputedStyle) = 0;
 
+  already_AddRefed<ComputedStyle> ComputeSelectionStyle() const;
+
   /**
    * Accessor functions for geometric parent.
    */
   nsContainerFrame* GetParent() const { return mParent; }
 
   /**
    * Gets the parent of a frame, using the parent of the placeholder for
    * out-of-flow frames.
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1894,24 +1894,20 @@ nsImageFrame::ShouldDisplaySelection()
 {
   nsPresContext* presContext = PresContext();
   int16_t displaySelection = presContext->PresShell()->GetSelectionFlags();
   if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES))
     return false;//no need to check the blue border, we cannot be drawn selected
 
   // If the image is the only selected node, don't draw the selection overlay.
   // This can happen when selecting an image in contenteditable context.
-  if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
-  {
-    const nsFrameSelection* frameSelection = GetConstFrameSelection();
-    if (frameSelection)
-    {
+  if (displaySelection == nsISelectionDisplay::DISPLAY_ALL) {
+    if (const nsFrameSelection* frameSelection = GetConstFrameSelection()) {
       const Selection* selection = frameSelection->GetSelection(SelectionType::eNormal);
-      if (selection && selection->RangeCount() == 1)
-      {
+      if (selection && selection->RangeCount() == 1) {
         nsINode* parent = mContent->GetParent();
         int32_t thisOffset = parent->ComputeIndexOf(mContent);
         nsRange* range = selection->GetRangeAt(0);
         if (range->GetStartContainer() == parent &&
             range->GetEndContainer() == parent &&
             static_cast<int32_t>(range->StartOffset()) == thisOffset &&
             static_cast<int32_t>(range->EndOffset()) == thisOffset + 1) {
           return false;
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -4052,30 +4052,16 @@ nsTextPaintStyle::GetSystemFieldForegrou
 
 nscolor
 nsTextPaintStyle::GetSystemFieldBackgroundColor()
 {
   InitCommonColors();
   return mSystemFieldBackgroundColor;
 }
 
-static Element*
-FindElementAncestorForMozSelection(nsIContent* aContent)
-{
-  NS_ENSURE_TRUE(aContent, nullptr);
-  while (aContent && aContent->IsInNativeAnonymousSubtree()) {
-    aContent = aContent->GetBindingParent();
-  }
-  NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
-  while (aContent && !aContent->IsElement()) {
-    aContent = aContent->GetParent();
-  }
-  return aContent ? aContent->AsElement() : nullptr;
-}
-
 bool
 nsTextPaintStyle::InitSelectionColorsAndShadow()
 {
   if (mInitSelectionColorsAndShadow)
     return true;
 
   int16_t selectionFlags;
   int16_t selectionStatus = mFrame->GetSelectionStatus(&selectionFlags);
@@ -4084,35 +4070,25 @@ nsTextPaintStyle::InitSelectionColorsAnd
     // Not displaying the normal selection.
     // We're not caching this fact, so every call to GetSelectionColors
     // will come through here. We could avoid this, but it's not really worth it.
     return false;
   }
 
   mInitSelectionColorsAndShadow = true;
 
-  nsIFrame* nonGeneratedAncestor = nsLayoutUtils::GetNonGeneratedAncestor(mFrame);
-  Element* selectionElement =
-    FindElementAncestorForMozSelection(nonGeneratedAncestor->GetContent());
-
-  if (selectionElement &&
-      selectionStatus == nsISelectionController::SELECTION_ON) {
-    RefPtr<ComputedStyle> sc =
-      mPresContext->StyleSet()->
-        ProbePseudoElementStyle(selectionElement,
-                                CSSPseudoElementType::mozSelection,
-                                mFrame->Style());
-    // Use -moz-selection pseudo class.
-    if (sc) {
+  if (selectionStatus == nsISelectionController::SELECTION_ON) {
+    // Use ::selection pseudo class.
+    if (RefPtr<ComputedStyle> style = mFrame->ComputeSelectionStyle()) {
       mSelectionBGColor =
-        sc->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
+        style->GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor);
       mSelectionTextColor =
-        sc->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
+        style->GetVisitedDependentColor(&nsStyleText::mWebkitTextFillColor);
       mHasSelectionShadow = true;
-      mSelectionShadow = sc->StyleText()->mTextShadow;
+      mSelectionShadow = style->StyleText()->mTextShadow;
       return true;
     }
   }
 
   nscolor selectionBGColor =
     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground);
 
   if (selectionStatus == nsISelectionController::SELECTION_ATTENTION) {
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -478,16 +478,17 @@ fuzzy-if(skiaContent,1,610) == textPath-
 == textPath-06.svg pass.svg
 == textPath-line-01.svg textPath-line-01-ref.svg
 == textPath-side-attribute-01.svg pass.svg
 
 == text-white-space-01.svg text-white-space-01-ref.svg
 
 == thin-stroke-01.svg pass.svg
 
+== transform-01.svg pass.svg
 == transform-outer-svg-01.svg transform-outer-svg-01-ref.svg
 
 == tspan-dxdy-01.svg tspan-dxdy-ref.svg
 == tspan-dxdy-02.svg tspan-dxdy-ref.svg
 == tspan-dxdy-03.svg tspan-dxdy-ref.svg
 == tspan-dxdy-04.svg tspan-dxdy-ref.svg
 == tspan-dxdy-05.svg tspan-dxdy-ref.svg
 == tspan-dxdy-06.svg tspan-dxdy-ref.svg
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/transform-01.svg
@@ -0,0 +1,11 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg">
+  <rect width="100%" height="100%" fill="lime"/>
+  <rect x="25" y="25" width="50" height="50" fill="red"/>
+  <g transform="translate(-100-100)">
+    <rect x="24" y="24" width="52" height="52" fill="lime"/>
+  </g>
+</svg>
--- a/layout/style/StyleSheet.cpp
+++ b/layout/style/StyleSheet.cpp
@@ -229,17 +229,17 @@ StyleSheet::SetEnabled(bool aEnabled)
     EnabledStateChanged();
     ApplicableStateChanged(!mDisabled);
   }
 }
 
 StyleSheetInfo::StyleSheetInfo(CORSMode aCORSMode,
                                ReferrerPolicy aReferrerPolicy,
                                const dom::SRIMetadata& aIntegrity)
-  : mPrincipal(NullPrincipal::Create())
+  : mPrincipal(NullPrincipal::CreateWithoutOriginAttributes())
   , mCORSMode(aCORSMode)
   , mReferrerPolicy(aReferrerPolicy)
   , mIntegrity(aIntegrity)
   , mComplete(false)
 #ifdef DEBUG
   , mPrincipalSet(false)
 #endif
 {
--- a/layout/style/URLExtraData.cpp
+++ b/layout/style/URLExtraData.cpp
@@ -17,17 +17,17 @@ StaticRefPtr<URLExtraData> URLExtraData:
 
 /* static */ void
 URLExtraData::InitDummy()
 {
   RefPtr<nsIURI> baseURI = NullPrincipalURI::Create();
   RefPtr<nsIURI> referrer = baseURI;
   sDummy = new URLExtraData(baseURI.forget(),
                             referrer.forget(),
-                            NullPrincipal::Create());
+                            NullPrincipal::CreateWithoutOriginAttributes());
 }
 
 /* static */ void
 URLExtraData::ReleaseDummy()
 {
   sDummy = nullptr;
 }
 
--- a/layout/style/test/gtest/StyloParsingBench.cpp
+++ b/layout/style/test/gtest/StyloParsingBench.cpp
@@ -23,17 +23,17 @@ using namespace mozilla::net;
 #define GETPROPERTY_REPETITIONS (1000 * 1000)
 
 
 static void ServoParsingBench() {
   auto css = AsBytes(MakeStringSpan(EXAMPLE_STYLESHEET));
   ASSERT_EQ(Encoding::UTF8ValidUpTo(css), css.Length());
 
   RefPtr<URLExtraData> data = new URLExtraData(
-    NullPrincipalURI::Create(), nullptr, NullPrincipal::Create());
+    NullPrincipalURI::Create(), nullptr, NullPrincipal::CreateWithoutOriginAttributes());
   for (int i = 0; i < PARSING_REPETITIONS; i++) {
     RefPtr<RawServoStyleSheetContents> stylesheet =
       Servo_StyleSheet_FromUTF8Bytes(nullptr,
                                      nullptr,
                                      nullptr,
                                      css.Elements(),
                                      css.Length(),
                                      eAuthorSheetFeatures,
@@ -50,17 +50,17 @@ MOZ_GTEST_BENCH(Stylo, Servo_StyleSheet_
 
 
 
 
 
 static void ServoSetPropertyByIdBench(const nsACString& css) {
   RefPtr<RawServoDeclarationBlock> block = Servo_DeclarationBlock_CreateEmpty().Consume();
   RefPtr<URLExtraData> data = new URLExtraData(
-    NullPrincipalURI::Create(), nullptr, NullPrincipal::Create());
+    NullPrincipalURI::Create(), nullptr, NullPrincipal::CreateWithoutOriginAttributes());
 
   ASSERT_TRUE(IsUTF8(css));
 
   for (int i = 0; i < SETPROPERTY_REPETITIONS; i++) {
     Servo_DeclarationBlock_SetPropertyById(
       block,
       eCSSProperty_width,
       &css,
@@ -79,17 +79,17 @@ MOZ_GTEST_BENCH(Stylo, Servo_Declaration
 
 MOZ_GTEST_BENCH(Stylo, Servo_DeclarationBlock_SetPropertyById_WithInitialSpace_Bench, [] {
   ServoSetPropertyByIdBench(NS_LITERAL_CSTRING(" 10px"));
 });
 
 static void ServoGetPropertyValueById() {
   RefPtr<RawServoDeclarationBlock> block = Servo_DeclarationBlock_CreateEmpty().Consume();
   RefPtr<URLExtraData> data = new URLExtraData(
-    NullPrincipalURI::Create(), nullptr, NullPrincipal::Create());
+    NullPrincipalURI::Create(), nullptr, NullPrincipal::CreateWithoutOriginAttributes());
   NS_NAMED_LITERAL_CSTRING(css_, "10px");
   const nsACString& css = css_;
   Servo_DeclarationBlock_SetPropertyById(
     block,
     eCSSProperty_width,
     &css,
     /* is_important = */ false,
     data,
--- a/layout/xul/nsListBoxBodyFrame.cpp
+++ b/layout/xul/nsListBoxBodyFrame.cpp
@@ -23,16 +23,17 @@
 #include "nsScrollbarFrame.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "mozilla/ComputedStyle.h"
 #include "nsFontMetrics.h"
 #include "nsITimer.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
+#include "mozilla/dom/Text.h"
 #include "nsPIBoxObject.h"
 #include "nsLayoutUtils.h"
 #include "nsPIListBoxObject.h"
 #include "nsContentUtils.h"
 #include "ChildIterator.h"
 #include "gfxContext.h"
 #include "prtime.h"
 #include <algorithm>
@@ -728,19 +729,19 @@ nsListBoxBodyFrame::ComputeIntrinsicISiz
       width += margin.LeftRight();
 
     FlattenedChildIterator iter(mContent);
     for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
       if (child->IsXULElement(nsGkAtoms::listitem)) {
         gfxContext* rendContext = aBoxLayoutState.GetRenderingContext();
         if (rendContext) {
           nsAutoString value;
-          for (nsIContent* text = child->GetFirstChild();
-               text; text = text->GetNextSibling()) {
-            if (text->IsNodeOfType(nsINode::eTEXT)) {
+          for (nsIContent* content = child->GetFirstChild();
+               content; content = content->GetNextSibling()) {
+            if (Text* text = content->GetAsText()) {
               text->AppendTextTo(value);
             }
           }
 
           RefPtr<nsFontMetrics> fm =
             nsLayoutUtils::GetFontMetricsForComputedStyle(computedStyle);
 
           nscoord textWidth =
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -73,16 +73,44 @@
 // - <default-value> is the same as for normal prefs.
 //
 // Note that Rust code must access the global variable directly, rather than via
 // the getter.
 
 // clang-format off
 
 //---------------------------------------------------------------------------
+// HTML5 parser prefs
+//---------------------------------------------------------------------------
+
+// Toggle which thread the HTML5 parser uses for stream parsing.
+VARCACHE_PREF(
+  "html5.offmainthread",
+   html5_offmainthread,
+  bool, true
+)
+
+// Time in milliseconds between the time a network buffer is seen and the timer
+// firing when the timer hasn't fired previously in this parse in the
+// off-the-main-thread HTML5 parser.
+VARCACHE_PREF(
+  "html5.flushtimer.initialdelay",
+   html5_flushtimer_initialdelay,
+  int32_t, 120
+)
+
+// Time in milliseconds between the time a network buffer is seen and the timer
+// firing when the timer has already fired previously in this parse.
+VARCACHE_PREF(
+  "html5.flushtimer.subsequentdelay",
+   html5_flushtimer_subsequentdelay,
+  int32_t, 120
+)
+
+//---------------------------------------------------------------------------
 // Network prefs
 //---------------------------------------------------------------------------
 
 // Sub-resources HTTP-authentication:
 //   0 - don't allow sub-resources to open HTTP authentication credentials
 //       dialogs
 //   1 - allow sub-resources to open HTTP authentication credentials dialogs,
 //       but don't allow it for cross-origin sub-resources
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5060,26 +5060,16 @@ pref("device.sensors.ambientLight.enable
 #else
 pref("device.sensors.proximity.enabled", true);
 pref("device.sensors.ambientLight.enabled", true);
 #endif
 
 // Enable/Disable the device storage API for content
 pref("device.storage.enabled", false);
 
-// Toggle which thread the HTML5 parser uses for stream parsing
-pref("html5.offmainthread", true);
-// Time in milliseconds between the time a network buffer is seen and the
-// timer firing when the timer hasn't fired previously in this parse in the
-// off-the-main-thread HTML5 parser.
-pref("html5.flushtimer.initialdelay", 120);
-// Time in milliseconds between the time a network buffer is seen and the
-// timer firing when the timer has already fired previously in this parse.
-pref("html5.flushtimer.subsequentdelay", 120);
-
 // Push/Pop/Replace State prefs
 pref("browser.history.maxStateObjectSize", 655360);
 
 pref("browser.meta_refresh_when_inactive.disabled", false);
 
 // XPInstall prefs
 pref("xpinstall.whitelist.required", true);
 // Only Firefox requires add-on signatures
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -3219,17 +3219,17 @@ HttpBaseChannel::CloneLoadInfoForRedirec
   }
 
   nsCOMPtr<nsILoadInfo> newLoadInfo =
     static_cast<mozilla::LoadInfo*>(mLoadInfo.get())->Clone();
 
   nsContentPolicyType contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
   if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
       contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
-    nsCOMPtr<nsIPrincipal> nullPrincipalToInherit = NullPrincipal::Create();
+    nsCOMPtr<nsIPrincipal> nullPrincipalToInherit = NullPrincipal::CreateWithoutOriginAttributes();
     newLoadInfo->SetPrincipalToInherit(nullPrincipalToInherit);
   }
 
   // re-compute the origin attributes of the loadInfo if it's top-level load.
   bool isTopLevelDoc =
     newLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT;
 
   if (isTopLevelDoc) {
--- a/netwerk/protocol/http/nsHttp.cpp
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -438,23 +438,28 @@ ValidationRequired(bool isForcedValid, n
     if (cacheControlRequest.NoCache()) {
         LOG(("  validating, no-cache request"));
         doValidation = true;
     } else if (cacheControlRequest.MaxStale(&maxStaleRequest)) {
         uint32_t staleTime = age > freshness ? age - freshness : 0;
         doValidation = staleTime > maxStaleRequest;
         LOG(("  validating=%d, max-stale=%u requested", doValidation, maxStaleRequest));
     } else if (cacheControlRequest.MaxAge(&maxAgeRequest)) {
-        doValidation = age > maxAgeRequest;
+        // The input information for age and freshness calculation are in seconds.
+        // Hence, the internal logic can't have better resolution than seconds too.
+        // To make max-age=0 case work even for requests made in less than a second
+        // after the last response has been received, we use >= for compare.  This
+        // is correct because of the rounding down of the age calculated value.
+        doValidation = age >= maxAgeRequest;
         LOG(("  validating=%d, max-age=%u requested", doValidation, maxAgeRequest));
     } else if (cacheControlRequest.MinFresh(&minFreshRequest)) {
         uint32_t freshTime = freshness > age ? freshness - age : 0;
         doValidation = freshTime < minFreshRequest;
         LOG(("  validating=%d, min-fresh=%u requested", doValidation, minFreshRequest));
-    } else if (now <= expiration) {
+    } else if (now < expiration) {
         doValidation = false;
         LOG(("  not validating, expire time not in the past"));
     } else if (cachedResponseHead->MustValidateIfExpired()) {
         doValidation = true;
     } else if (loadFlags & nsIRequest::VALIDATE_ONCE_PER_SESSION) {
         // If the cached response does not include expiration infor-
         // mation, then we must validate the response, despite whether
         // or not this is the first access this session.  This behavior
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -62,17 +62,17 @@ nsViewSourceChannel::Init(nsIURI* uri)
       return NS_ERROR_INVALID_ARG;
     }
 
     // This function is called from within nsViewSourceHandler::NewChannel2
     // and sets the right loadInfo right after returning from this function.
     // Until then we follow the principal of least privilege and use
     // nullPrincipal as the loadingPrincipal and the least permissive
     // securityflag.
-    nsCOMPtr<nsIPrincipal> nullPrincipal = NullPrincipal::Create();
+    nsCOMPtr<nsIPrincipal> nullPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
 
     rv = pService->NewChannel2(path,
                                nullptr, // aOriginCharset
                                nullptr, // aCharSet
                                nullptr, // aLoadingNode
                                nullPrincipal,
                                nullptr, // aTriggeringPrincipal
                                nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
--- a/parser/html/nsHtml5Module.cpp
+++ b/parser/html/nsHtml5Module.cpp
@@ -1,50 +1,48 @@
 /* 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 "nsHtml5Module.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
+#include "mozilla/StaticPrefs.h"
 #include "nsHtml5AttributeName.h"
 #include "nsHtml5ElementName.h"
 #include "nsHtml5HtmlAttributes.h"
 #include "nsHtml5NamedCharacters.h"
 #include "nsHtml5Portability.h"
 #include "nsHtml5StackNode.h"
 #include "nsHtml5Tokenizer.h"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5UTF16Buffer.h"
 #include "nsIObserverService.h"
 #include "nsIServiceManager.h"
 
 using namespace mozilla;
 
 // static
-bool nsHtml5Module::sOffMainThread = true;
 nsIThread* nsHtml5Module::sStreamParserThread = nullptr;
 nsIThread* nsHtml5Module::sMainThread = nullptr;
 
 // static
 void
 nsHtml5Module::InitializeStatics()
 {
-  Preferences::AddBoolVarCache(&sOffMainThread, "html5.offmainthread");
   nsHtml5AttributeName::initializeStatics();
   nsHtml5ElementName::initializeStatics();
   nsHtml5HtmlAttributes::initializeStatics();
   nsHtml5NamedCharacters::initializeStatics();
   nsHtml5Portability::initializeStatics();
   nsHtml5StackNode::initializeStatics();
   nsHtml5Tokenizer::initializeStatics();
   nsHtml5TreeBuilder::initializeStatics();
   nsHtml5UTF16Buffer::initializeStatics();
-  nsHtml5StreamParser::InitializeStatics();
   nsHtml5TreeOpExecutor::InitializeStatics();
 #ifdef DEBUG
   sNsHtml5ModuleInitialized = true;
 #endif
 }
 
 // static
 void
@@ -113,17 +111,17 @@ private:
 };
 
 NS_IMPL_ISUPPORTS(nsHtml5ParserThreadTerminator, nsIObserver)
 
 // static
 nsIThread*
 nsHtml5Module::GetStreamParserThread()
 {
-  if (sOffMainThread) {
+  if (StaticPrefs::html5_offmainthread()) {
     if (!sStreamParserThread) {
       NS_NewNamedThread("HTML5 Parser", &sStreamParserThread);
       NS_ASSERTION(sStreamParserThread, "Thread creation failed!");
       nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
       NS_ASSERTION(os, "do_GetService failed");
       os->AddObserver(new nsHtml5ParserThreadTerminator(sStreamParserThread),
                       "xpcom-shutdown-threads",
                       false);
--- a/parser/html/nsHtml5Module.h
+++ b/parser/html/nsHtml5Module.h
@@ -15,17 +15,16 @@ public:
   static void ReleaseStatics();
   static already_AddRefed<nsIParser> NewHtml5Parser();
   static nsresult Initialize(nsIParser* aParser,
                              nsIDocument* aDoc,
                              nsIURI* aURI,
                              nsISupports* aContainer,
                              nsIChannel* aChannel);
   static nsIThread* GetStreamParserThread();
-  static bool sOffMainThread;
 
 private:
 #ifdef DEBUG
   static bool sNsHtml5ModuleInitialized;
 #endif
   static nsIThread* sStreamParserThread;
   static nsIThread* sMainThread;
 };
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -14,16 +14,17 @@
 #include "nsHtml5Parser.h"
 #include "nsHtml5TreeBuilder.h"
 #include "nsHtml5AtomTable.h"
 #include "nsHtml5Module.h"
 #include "nsHtml5StreamParserPtr.h"
 #include "nsIDocShell.h"
 #include "nsIScriptError.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs.h"
 #include "mozilla/SystemGroup.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "nsHtml5Highlighter.h"
 #include "expat_config.h"
 #include "expat.h"
 #include "nsINestedURI.h"
 #include "nsCharsetSource.h"
 #include "nsIWyciwygChannel.h"
@@ -31,29 +32,16 @@
 #include "nsPrintfCString.h"
 #include "nsNetUtil.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/SchedulerGroup.h"
 #include "nsJSEnvironment.h"
 
 using namespace mozilla;
 
-int32_t nsHtml5StreamParser::sTimerInitialDelay = 120;
-int32_t nsHtml5StreamParser::sTimerSubsequentDelay = 120;
-
-// static
-void
-nsHtml5StreamParser::InitializeStatics()
-{
-  Preferences::AddIntVarCache(&sTimerInitialDelay,
-                              "html5.flushtimer.initialdelay");
-  Preferences::AddIntVarCache(&sTimerSubsequentDelay,
-                              "html5.flushtimer.subsequentdelay");
-}
-
 /*
  * Note that nsHtml5StreamParser implements cycle collecting AddRef and
  * Release. Therefore, nsHtml5StreamParser must never be refcounted from
  * the parser thread!
  *
  * To work around this limitation, runnables posted by the main thread to the
  * parser thread hold their reference to the stream parser in an
  * nsHtml5StreamParserPtr. Upon creation, nsHtml5StreamParserPtr addrefs the
@@ -1199,17 +1187,18 @@ nsHtml5StreamParser::DoDataAvailable(con
     return;
   }
 
   {
     mozilla::MutexAutoLock flushTimerLock(mFlushTimerMutex);
     mFlushTimer->InitWithNamedFuncCallback(
       nsHtml5StreamParser::TimerCallback,
       static_cast<void*>(this),
-      mFlushTimerEverFired ? sTimerInitialDelay : sTimerSubsequentDelay,
+      mFlushTimerEverFired ? StaticPrefs::html5_flushtimer_initialdelay()
+                           : StaticPrefs::html5_flushtimer_subsequentdelay(),
       nsITimer::TYPE_ONE_SHOT,
       "nsHtml5StreamParser::DoDataAvailable");
   }
   mFlushTimerArmed = true;
 }
 
 class nsHtml5DataAvailable : public Runnable
 {
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -117,18 +117,16 @@ class nsHtml5StreamParser final : public
   friend class nsHtml5TimerKungFu;
   friend class nsHtml5StreamParserPtr;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsHtml5StreamParser,
                                            nsICharsetDetectionObserver)
 
-  static void InitializeStatics();
-
   nsHtml5StreamParser(nsHtml5TreeOpExecutor* aExecutor,
                       nsHtml5Parser* aOwner,
                       eParserMode aMode);
 
   // Methods that nsHtml5StreamListener calls
   nsresult CheckListenerChain();
 
   nsresult OnStartRequest(nsIRequest* aRequest, nsISupports* aContext);
@@ -575,25 +573,11 @@ private:
    * False initially and true after the timer has fired at least once.
    */
   bool mFlushTimerEverFired;
 
   /**
    * Whether the parser is doing a normal parse, view source or plain text.
    */
   eParserMode mMode;
-
-  /**
-   * The pref html5.flushtimer.initialdelay: Time in milliseconds between
-   * the time a network buffer is seen and the timer firing when the
-   * timer hasn't fired previously in this parse.
-   */
-  static int32_t sTimerInitialDelay;
-
-  /**
-   * The pref html5.flushtimer.subsequentdelay: Time in milliseconds between
-   * the time a network buffer is seen and the timer firing when the
-   * timer has already fired previously in this parse.
-   */
-  static int32_t sTimerSubsequentDelay;
 };
 
 #endif // nsHtml5StreamParser_h
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/Likely.h"
 #include "mozilla/Services.h"
 #include "mozilla/dom/Comment.h"
 #include "mozilla/dom/DocumentType.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLFormElement.h"
 #include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
+#include "mozilla/dom/Text.h"
 #include "nsAttrName.h"
 #include "nsBindingManager.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsContentUtils.h"
 #include "nsDocElementCreatedNotificationRunner.h"
 #include "nsEscape.h"
 #include "nsHtml5AutoPauseUpdate.h"
 #include "nsHtml5DocumentMode.h"
@@ -118,17 +119,17 @@ nsHtml5TreeOperation::~nsHtml5TreeOperat
     default: // keep the compiler happy
       break;
   }
 }
 
 nsresult
 nsHtml5TreeOperation::AppendTextToTextNode(const char16_t* aBuffer,
                                            uint32_t aLength,
-                                           nsIContent* aTextNode,
+                                           dom::Text* aTextNode,
                                            nsHtml5DocumentBuilder* aBuilder)
 {
   NS_PRECONDITION(aTextNode, "Got null text node.");
   MOZ_ASSERT(aBuilder);
   MOZ_ASSERT(aBuilder->IsInDocUpdate());
   uint32_t oldLength = aTextNode->TextLength();
   CharacterDataChangeInfo info = { true, oldLength, oldLength, aLength };
   nsNodeUtils::CharacterDataWillChange(aTextNode, info);
@@ -143,19 +144,20 @@ nsHtml5TreeOperation::AppendTextToTextNo
 nsresult
 nsHtml5TreeOperation::AppendText(const char16_t* aBuffer,
                                  uint32_t aLength,
                                  nsIContent* aParent,
                                  nsHtml5DocumentBuilder* aBuilder)
 {
   nsresult rv = NS_OK;
   nsIContent* lastChild = aParent->GetLastChild();
-  if (lastChild && lastChild->IsNodeOfType(nsINode::eTEXT)) {
+  if (lastChild && lastChild->IsText()) {
     nsHtml5OtherDocUpdate update(aParent->OwnerDoc(), aBuilder->GetDocument());
-    return AppendTextToTextNode(aBuffer, aLength, lastChild, aBuilder);
+    return AppendTextToTextNode(aBuffer, aLength, lastChild->GetAsText(),
+                                aBuilder);
   }
 
   nsNodeInfoManager* nodeInfoManager = aParent->OwnerDoc()->NodeInfoManager();
   RefPtr<nsTextNode> text = new nsTextNode(nodeInfoManager);
   NS_ASSERTION(text, "Infallible malloc failed?");
   rv = text->SetText(aBuffer, aLength, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -660,18 +662,19 @@ nsHtml5TreeOperation::FosterParentText(n
   MOZ_ASSERT(aBuilder->IsInDocUpdate());
   nsresult rv = NS_OK;
   nsIContent* foster = aTable->GetParent();
 
   if (IsElementOrTemplateContent(foster)) {
     nsHtml5OtherDocUpdate update(foster->OwnerDoc(), aBuilder->GetDocument());
 
     nsIContent* previousSibling = aTable->GetPreviousSibling();
-    if (previousSibling && previousSibling->IsNodeOfType(nsINode::eTEXT)) {
-      return AppendTextToTextNode(aBuffer, aLength, previousSibling, aBuilder);
+    if (previousSibling && previousSibling->IsText()) {
+      return AppendTextToTextNode(aBuffer, aLength,
+                                  previousSibling->GetAsText(), aBuilder);
     }
 
     nsNodeInfoManager* nodeInfoManager =
       aStackParent->OwnerDoc()->NodeInfoManager();
     RefPtr<nsTextNode> text = new nsTextNode(nodeInfoManager);
     NS_ASSERTION(text, "Infallible malloc failed?");
     rv = text->SetText(aBuffer, aLength, false);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -10,17 +10,21 @@
 #include "mozilla/dom/FromParser.h"
 #include "mozilla/NotNull.h"
 
 class nsIContent;
 class nsHtml5TreeOpExecutor;
 class nsHtml5DocumentBuilder;
 namespace mozilla {
 class Encoding;
-}
+
+namespace dom {
+class Text;
+} // namespace dom
+} // namespace mozilla
 
 enum eHtml5TreeOperation
 {
   eTreeOpUninitialized,
   // main HTML5 ops
   eTreeOpAppend,
   eTreeOpDetach,
   eTreeOpAppendChildrenToNewParent,
@@ -121,17 +125,17 @@ public:
     }
     nsAutoString str;
     aAtom->ToString(str);
     return NS_AtomizeMainThread(str);
   }
 
   static nsresult AppendTextToTextNode(const char16_t* aBuffer,
                                        uint32_t aLength,
-                                       nsIContent* aTextNode,
+                                       mozilla::dom::Text* aTextNode,
                                        nsHtml5DocumentBuilder* aBuilder);
 
   static nsresult AppendText(const char16_t* aBuffer,
                              uint32_t aLength,
                              nsIContent* aParent,
                              nsHtml5DocumentBuilder* aBuilder);
 
   static nsresult Append(nsIContent* aNode,
--- a/parser/html/nsParserUtils.cpp
+++ b/parser/html/nsParserUtils.cpp
@@ -65,17 +65,17 @@ nsParserUtils::Unescape(const nsAString&
 
 NS_IMETHODIMP
 nsParserUtils::Sanitize(const nsAString& aFromStr,
                         uint32_t aFlags,
                         nsAString& aToStr)
 {
   nsCOMPtr<nsIURI> uri;
   NS_NewURI(getter_AddRefs(uri), "about:blank");
-  nsCOMPtr<nsIPrincipal> principal = NullPrincipal::Create();
+  nsCOMPtr<nsIPrincipal> principal = NullPrincipal::CreateWithoutOriginAttributes();
   nsCOMPtr<nsIDOMDocument> domDocument;
   nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument),
                                   EmptyString(),
                                   EmptyString(),
                                   nullptr,
                                   uri,
                                   uri,
                                   principal,
--- a/parser/htmlparser/nsExpatDriver.cpp
+++ b/parser/htmlparser/nsExpatDriver.cpp
@@ -674,17 +674,17 @@ nsExpatDriver::OpenInputStreamFromExtern
     if (mOriginalSink) {
       nsCOMPtr<nsIDocument> doc;
       doc = do_QueryInterface(mOriginalSink->GetTarget());
       if (doc) {
         loadingPrincipal = doc->NodePrincipal();
       }
     }
     if (!loadingPrincipal) {
-      loadingPrincipal = NullPrincipal::Create();
+      loadingPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
     }
     rv = NS_NewChannel(getter_AddRefs(channel),
                        uri,
                        loadingPrincipal,
                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
                        nsILoadInfo::SEC_ALLOW_CHROME,
                        nsIContentPolicy::TYPE_DTD);
   }
--- a/parser/htmlparser/tests/reftest/bug659763-1-ref.html
+++ b/parser/htmlparser/tests/reftest/bug659763-1-ref.html
@@ -1,2 +1,2 @@
 <!DOCTYPE html>
-<iframe src="data:text/plain,<i>foo</i>"></iframe>
+<iframe src="data:text/html,<i>foo</i>"></iframe>
--- a/parser/htmlparser/tests/reftest/bug659763-2-ref.html
+++ b/parser/htmlparser/tests/reftest/bug659763-2-ref.html
@@ -1,2 +1,2 @@
 <!DOCTYPE html>
-<iframe src="data:text/plain,<i>foo</i>"></iframe>
+<iframe src="data:text/html,<i>foo</i>"></iframe>
--- a/parser/htmlparser/tests/reftest/bug659763-3-ref.html
+++ b/parser/htmlparser/tests/reftest/bug659763-3-ref.html
@@ -1,2 +1,2 @@
 <!DOCTYPE html>
-<iframe src="data:text/plain,<i>foo</i>"></iframe>
+<iframe src="data:text/html,<i>foo</i>"></iframe>
--- a/rdf/base/nsRDFXMLParser.cpp
+++ b/rdf/base/nsRDFXMLParser.cpp
@@ -109,17 +109,17 @@ nsRDFXMLParser::ParseString(nsIRDFDataSo
 
     if (! listener)
         return NS_ERROR_FAILURE;
 
     nsCOMPtr<nsIInputStream> stream;
     rv = NS_NewCStringInputStream(getter_AddRefs(stream), aString);
     if (NS_FAILED(rv)) return rv;
 
-    nsCOMPtr<nsIPrincipal> nullPrincipal = NullPrincipal::Create();
+    nsCOMPtr<nsIPrincipal> nullPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
 
     // The following channel is never openend, so it does not matter what
     // securityFlags we pass; let's follow the principle of least privilege.
     nsCOMPtr<nsIChannel> channel;
     nsCOMPtr<nsIInputStream> tmpStream = stream;
     rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
                                   aBaseURI,
                                   tmpStream.forget(),
--- a/taskcluster/docs/release-promotion-action.rst
+++ b/taskcluster/docs/release-promotion-action.rst
@@ -1,27 +1,32 @@
 Release Promotion Action
 ========================
 
-The `release promotion action`_ allows us to chain multiple task groups, or graphs, together.
-Essentially, we're using :ref:`optimization` logic to replace task labels in the
-current task group with task IDs from the previous task group(s).
-
-.. _snowman model:
+The release promotion action is how Releng triggers `release promotion`_
+taskgraphs. The one action covers all release promotion needs: different
+*flavors* allow for us to trigger the different :ref:`release promotion phases`
+for each product. The input schema and release promotion flavors are defined in
+the `release promotion action`_.
 
 The snowman model
 -----------------
 
-We chain task groups, or graphs, together to allow us to trigger sets of tasks
-at separate cadences. This is the ``snowman`` model. If you request the body of
+The `release promotion action`_ allows us to chain multiple taskgraphs (aka graphs, aka task groups) together.
+Essentially, we're using `optimization`_ logic to replace task labels in the
+current taskgraph with task IDs from the previous taskgraph(s).
+
+.. _snowman model:
+
+This is the ``snowman`` model. If you request the body of
 the snowman and point at the base, we only create the middle section of the snowman.
 If you request the body of the snowman and don't point it at the base, we build the
 first base and body of the snowman from scratch.
 
-For example, let's generate a task ``t2`` that depends on ``t1``. Let's call our new task group ``G``::
+For example, let's generate a task ``t2`` that depends on ``t1``. Let's call our new taskgraph ``G``::
 
     G
     |
     t1
     |
     t2
 
 Task ``t2`` will wait on task ``t1`` to finish, and downloads some artifacts from task ``t1``.
@@ -66,33 +71,33 @@ If we point the ``promote`` task group G
              \_________|
                        |
                   l10n-repack
                        |
                   l10n-signing
 
 We can also explicitly exclude certain tasks from being optimized out.
 We currently do this by specifying ``rebuild_kinds`` in the action; these
-are :ref:`kinds` that we want to explicitly rebuild in the current task group,
+are `kinds`_ that we want to explicitly rebuild in the current task group,
 even if they existed in previous task groups. We also allow for specifying a list of
 ``do_not_optimize`` labels, which would be more verbose and specific than
 specifying kinds to rebuild.
 
 Release promotion action mechanics
 ----------------------------------
 
 There are a number of inputs defined in the `release promotion action`_. Among these are the ``previous_graph_ids``, which is an ordered list of taskGroupIds of the task groups that we want to build our task group, off of. In the :ref:`snowman model`, these define the already-built portions of the snowman.
 
 The action downloads the ``parameters.yml`` from the initial ``previous_graph_id``, which matches the decision- or action- taskId. (See :ref:`taskid vs taskgroupid`.) This is most likely the decision task of the revision to promote, which is generally the same revision the release promotion action is run against.
 
 .. note:: If the parameters have been changed since the build happened, *and* we explicitly want the new parameters for the release promotion action task, the first ``previous_graph_id`` should be the new revision's decision task. Then the build and other previous action task group IDs can follow, so we're still replacing the task labels with the task IDs from the original revision.
 
 The action then downloads the various ``label-to-taskid.json`` artifacts from each previous task group, and builds an ``existing_tasks`` parameter of which labels to replace with which task IDs. Each successive update to this dictionary overwrites existing keys with new task IDs, so the rightmost task group with a given label takes precedence. Any labels that match the ``do_not_optimize`` list or that belong to tasks in the ``rebuild_kinds`` list are excluded from the ``existing_tasks`` parameter.
 
-Once all that happens, and we've gotten our configuration from the original parameters and our action config and inputs, we run the decision task function with our custom parameters. The :ref:`optimization` phase replaces any ``existing_tasks`` with the task IDs we've built from the previous task groups.
+Once all that happens, and we've gotten our configuration from the original parameters and our action config and inputs, we run the decision task function with our custom parameters. The `optimization`_ phase replaces any ``existing_tasks`` with the task IDs we've built from the previous task groups.
 
 Release Promotion Flavors
 -------------------------
 
 For the most part, release promotion flavors match the pattern ``phase_product``,
 e.g. ``promote_fennec``, ``push_devedition``, or ``ship_firefox``.
 
 We've added ``_rc`` suffix flavors, to deal with special RC behavior around rolling out updates using a different rate or channel.
@@ -101,17 +106,17 @@ We are planning on adding ``_partners`` 
 
 The various flavors are defined in the `release promotion action`_.
 
 Triggering the release promotion action via Treeherder
 ------------------------------------------------------
 
 Currently, we're able to trigger this action via `Treeherder`_; we sometimes use this method for testing purposes. This is powerful, because we can modify the inputs directly, but is less production friendly, because it requires us to enter the inputs manually. At some point we may disable the ability to trigger the action via Treeherder.
 
-This requires being signed in with the right scopes. On `Release Promotion Projects`_, here's a dropdown in the top right of a given revision. Choose ``Custom Push Action``, then ``Release Promotion``. The inputs are specifiable as raw yaml on the left hand column.
+This requires being signed in with the right scopes. On `Release Promotion Projects`_, there's a dropdown in the top right of a given revision. Choose ``Custom Push Action``, then ``Release Promotion``. The inputs are specifiable as raw yaml on the left hand column.
 
 Triggering the release promotion action via releaserunner3
 ----------------------------------------------------------
 
 `Releaserunner3`_ is our current method of triggering the release promotion action from Ship It in production. Examples of how to run this are in the `releasewarrior docs`_.
 
 To deal with the above ``previous_graph_ids`` logic, we allow for a ``decision_task_id`` in `trigger_action.py`_. As of 2018-03-14, this script assumes we want to download ``parameters.yml`` from the same decision task that we get ``actions.json`` from. At some point, we'd like the `trigger_action.py`_ call to happen automatically once we push a button on Ship It.
 
@@ -148,15 +153,18 @@ The full command for a ``promote_fennec`
         --input /src/gecko/params/promote_fennec.yml \
         -p /src/gecko/params/maple-promote-fennec.yml \
         release_promotion_action > ../promote.json
 
 The input file (in the above example, that would be ``/src/gecko/params/promote_fennec.yml``), contains the action inputs. The input schema is defined in the `release promotion action`_. Previous example inputs are embedded in previous promotion task group action task definitions (``task.extra.action.input``).
 
 The ``parameters.yml`` file is downloadable from a previous decision or action task.
 
+.. _release promotion: release-promotion.html
+.. _optimization: optimization.html
+.. _kinds: kinds.html
 .. _release promotion action: https://searchfox.org/mozilla-central/source/taskcluster/taskgraph/actions/release_promotion.py
 .. _Treeherder: https://treeherder.mozilla.org
 .. _Release Promotion Projects: https://searchfox.org/mozilla-central/search?q=RELEASE_PROMOTION_PROJECTS&path=taskcluster/taskgraph/util/attributes.py
 .. _Releaserunner3: https://hg.mozilla.org/build/tools/file/tip/buildfarm/release
 .. _releasewarrior docs: https://github.com/mozilla-releng/releasewarrior-2.0/blob/master/docs/release-promotion/desktop/howto.md#how
 .. _trigger_action.py: https://dxr.mozilla.org/build-central/source/tools/buildfarm/release/trigger_action.py#118
 .. _.taskcluster.yml: https://searchfox.org/mozilla-central/source/.taskcluster.yml
--- a/taskcluster/docs/release-promotion.rst
+++ b/taskcluster/docs/release-promotion.rst
@@ -1,26 +1,28 @@
 Release Promotion
 =================
 
 Release promotion allows us to ship the same compiled binaries that we've
 already tested.
 
 In the olden days, we used to re-compile our release builds with separate
 configs, which led to release-specific bugs which weren't caught by continuous
-integration tests. This also meant we required new builds at release time, which
- also increased the end-to-end time for a given release significantly. Release
+integration tests. This meant we required new builds at release time, which
+increased the end-to-end time for a given release significantly. Release
 promotion removes these anti-patterns.
 
 By running our continuous integration tests against our shippable builds, we
 have a higher degree of confidence at release time. By separating the build
 phase tasks (compilation, packaging, and related tests) from the promotion
 phase tasks, we can schedule each phase at their own independent cadence, as
 needed, and the end-to-end time for promotion is reduced significantly.
 
+.. _release promotion phases
+
 Release Promotion Phases
 ------------------------
 
 Currently, we have the ``build``, ``promote``, ``push``, and ``ship`` phases.
 
 The ``build`` phase creates ``shippable builds``. These optimize for correctness
 over speed, and are designed to be of shipping quality, should we decide to
 ship that revision of code. These are triggered on push on release branches.
--- a/taskcluster/mach_commands.py
+++ b/taskcluster/mach_commands.py
@@ -169,38 +169,41 @@ class MachCommands(MachCommandBase):
             return taskgraph.decision.taskgraph_decision(options)
         except Exception:
             traceback.print_exc()
             sys.exit(1)
 
     @SubCommand('taskgraph', 'cron',
                 description="Run the cron task")
     @CommandArgument('--base-repository',
-                     required=True,
-                     help='URL for "base" repository to clone')
+                     required=False,
+                     help='(ignored)')
     @CommandArgument('--head-repository',
                      required=True,
                      help='URL for "head" repository to fetch')
     @CommandArgument('--head-ref',
-                     required=True,
-                     help='Reference to fetch in head-repository (usually "default")')
+                     required=False,
+                     help='(ignored)')
     @CommandArgument('--project',
                      required=True,
                      help='Project to use for creating tasks. Example: --project=mozilla-central')
     @CommandArgument('--level',
                      required=True,
                      help='SCM level of this repository')
     @CommandArgument('--force-run',
                      required=False,
                      help='If given, force this cronjob to run regardless of time, '
                      'and run no others')
     @CommandArgument('--no-create',
                      required=False,
                      action='store_true',
                      help='Do not actually create tasks')
+    @CommandArgument('--root', '-r',
+                     required=False,
+                     help="root of the repository to get cron task definitions from")
     def taskgraph_cron(self, **options):
         """Run the cron task; this task creates zero or more decision tasks.  It is run
         from the hooks service on a regular basis."""
         import taskgraph.cron
         try:
             self.setup_logging()
             return taskgraph.cron.taskgraph_cron(options)
         except Exception:
--- a/taskcluster/taskgraph/cron/__init__.py
+++ b/taskcluster/taskgraph/cron/__init__.py
@@ -31,18 +31,18 @@ from taskgraph.util.taskcluster import g
 # createTask.
 JOB_TYPES = {
     'decision-task': decision.run_decision_task,
 }
 
 logger = logging.getLogger(__name__)
 
 
-def load_jobs(params):
-    with open(os.path.join(GECKO, '.cron.yml'), 'rb') as f:
+def load_jobs(params, root):
+    with open(os.path.join(root, '.cron.yml'), 'rb') as f:
         cron_yml = yaml.load(f)
     schema.validate(cron_yml)
 
     # resolve keyed_by fields in each job
     jobs = cron_yml['jobs']
 
     return {j['name']: j for j in jobs}
 
@@ -56,32 +56,33 @@ def should_run(job, params):
     resolve_keyed_by(job, 'when', 'Cron job ' + job['name'],
                      project=params['project'])
     if not any(match_utc(params, hour=sched.get('hour'), minute=sched.get('minute'))
                for sched in job.get('when', [])):
         return False
     return True
 
 
-def run_job(job_name, job, params):
+def run_job(job_name, job, params, root):
+    params = params.copy()
     params['job_name'] = job_name
 
     try:
         job_type = job['job']['type']
         if job_type in JOB_TYPES:
-            tasks = JOB_TYPES[job_type](job['job'], params)
+            tasks = JOB_TYPES[job_type](job['job'], params, root=root)
         else:
             raise Exception("job type {} not recognized".format(job_type))
         if params['no_create']:
             for task_id, task in tasks:
                 logger.info("Not creating task {} (--no-create):\n".format(task_id) +
                             json.dumps(task, sort_keys=True, indent=4, separators=(',', ': ')))
         else:
             for task_id, task in tasks:
-                create_task(get_session(), task_id, params['job_name'], task)
+                create_task(get_session(), task_id, job_name, task)
 
     except Exception:
         # report the exception, but don't fail the whole cron task, as that
         # would leave other jobs un-run.  NOTE: we could report job failure to
         # a responsible person here via tc-notify
         traceback.print_exc()
         logger.error("cron job {} run failed; continuing to next job".format(
             params['job_name']))
@@ -90,16 +91,17 @@ def run_job(job_name, job, params):
 def calculate_time(options):
     if 'TASK_ID' not in os.environ:
         # running in a development environment, so look for CRON_TIME or use
         # the current time
         if 'CRON_TIME' in os.environ:
             logger.warning("setting params['time'] based on $CRON_TIME")
             time = datetime.datetime.utcfromtimestamp(
                 int(os.environ['CRON_TIME']))
+            print(time)
         else:
             logger.warning("using current time for params['time']; try setting $CRON_TIME "
                            "to a timestamp")
             time = datetime.datetime.utcnow()
     else:
         # fetch this task from the queue
         res = get_session().get(
             'http://taskcluster/queue/v1/task/' + os.environ['TASK_ID'])
@@ -117,49 +119,43 @@ def calculate_time(options):
     # round down to the nearest 15m
     minute = time.minute - (time.minute % 15)
     time = time.replace(minute=minute, second=0, microsecond=0)
     logger.info("calculated cron schedule time is {}".format(time))
     return time
 
 
 def taskgraph_cron(options):
+    root = options.get('root') or GECKO
+
     params = {
-        # name of this cron job (set per job below)
-        'job_name': '..',
-
         # repositories
-        'base_repository': options['base_repository'],
-        'head_repository': options['head_repository'],
-
-        # the symbolic ref this should run against (which happens to be what
-        # run-task checked out for us)
-        'head_ref': options['head_ref'],
+        'repository_url': options['head_repository'],
 
         # *calculated* head_rev; this is based on the current meaning of this
         # reference in the working copy
-        'head_rev': calculate_head_rev(options),
+        'head_rev': calculate_head_rev(root),
 
         # the project (short name for the repository) and its SCM level
         'project': options['project'],
         'level': options['level'],
 
         # if true, tasks will not actually be created
         'no_create': options['no_create'],
 
         # the time that this cron task was created (as a UTC datetime object)
         'time': calculate_time(options),
     }
 
-    jobs = load_jobs(params)
+    jobs = load_jobs(params, root=root)
 
     if options['force_run']:
         job_name = options['force_run']
         logger.info("force-running cron job {}".format(job_name))
-        run_job(job_name, jobs[job_name], params)
+        run_job(job_name, jobs[job_name], params, root)
         return
 
     for job_name, job in sorted(jobs.items()):
         if should_run(job, params):
             logger.info("running cron job {}".format(job_name))
-            run_job(job_name, job, params)
+            run_job(job_name, job, params, root)
         else:
             logger.info("not running cron job {}".format(job_name))
--- a/taskcluster/taskgraph/cron/decision.py
+++ b/taskcluster/taskgraph/cron/decision.py
@@ -11,31 +11,32 @@ import jsone
 import pipes
 import yaml
 import os
 import slugid
 
 from taskgraph.util.time import current_json_time
 
 
-def run_decision_task(job, params):
+def run_decision_task(job, params, root):
     arguments = []
     if 'target-tasks-method' in job:
         arguments.append('--target-tasks-method={}'.format(job['target-tasks-method']))
     return [
         make_decision_task(
             params,
             symbol=job['treeherder-symbol'],
-            arguments=arguments),
+            arguments=arguments,
+            root=root),
     ]
 
 
-def make_decision_task(params, symbol, arguments=[], head_rev=None):
+def make_decision_task(params, root, symbol, arguments=[], head_rev=None):
     """Generate a basic decision task, based on the root .taskcluster.yml"""
-    with open('.taskcluster.yml') as f:
+    with open(os.path.join(root, '.taskcluster.yml'), 'rb') as f:
         taskcluster_yml = yaml.load(f)
 
     if not head_rev:
         head_rev = params['head_rev']
 
     slugids = {}
 
     def as_slugid(name):
@@ -46,26 +47,26 @@ def make_decision_task(params, symbol, a
         return slugids[name]
 
     # provide a similar JSON-e context to what mozilla-taskcluster provides:
     # https://docs.taskcluster.net/reference/integrations/mozilla-taskcluster/docs/taskcluster-yml
     # but with a different tasks_for and an extra `cron` section
     context = {
         'tasks_for': 'cron',
         'repository': {
-            'url': params['head_repository'],
+            'url': params['repository_url'],
             'project': params['project'],
             'level': params['level'],
         },
         'push': {
             'revision': params['head_rev'],
             # remainder are fake values, but the decision task expects them anyway
             'pushlog_id': -1,
             'pushdate': 0,
-            'owner': 'nobody',
+            'owner': 'cron',
             'comment': '',
         },
         'cron': {
             'task_id': os.environ.get('TASK_ID', '<cron task id>'),
             'job_name': params['job_name'],
             'job_symbol': symbol,
             # args are shell-quoted since they are given to `bash -c`
             'quoted_args': ' '.join(pipes.quote(a) for a in arguments),
--- a/taskcluster/taskgraph/cron/util.py
+++ b/taskcluster/taskgraph/cron/util.py
@@ -20,13 +20,13 @@ def match_utc(params, hour=None, minute=
         raise Exception("cron jobs only run on multiples of 15 minutes past the hour")
     if hour is not None and params['time'].hour != hour:
         return False
     if minute is not None and params['time'].minute != minute:
         return False
     return True
 
 
-def calculate_head_rev(options):
+def calculate_head_rev(root):
     # we assume that run-task has correctly checked out the revision indicated by
     # GECKO_HEAD_REF, so all that remains is to see what the current revision is.
     # Mercurial refers to that as `.`.
-    return subprocess.check_output(['hg', 'log', '-r', '.', '-T', '{node}'])
+    return subprocess.check_output(['hg', 'log', '-r', '.', '-T', '{node}'], cwd=root)
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -1644,16 +1644,23 @@ toolbar#nav-bar {
             browserEnv["MOZ_DEVELOPER_OBJ_DIR"] = options.topobjdir
 
         if options.headless:
             browserEnv["MOZ_HEADLESS"] = '1'
 
         if options.dmd:
             browserEnv["DMD"] = os.environ.get('DMD', '1')
 
+        # bug 1443327: do not set MOZ_CRASHREPORTER_SHUTDOWN during browser-chrome
+        # tests, since some browser-chrome tests test content process crashes;
+        # also exclude non-e10s since at least one non-e10s mochitest is problematic
+        if (options.flavor == 'browser' or not options.e10s) and \
+           'MOZ_CRASHREPORTER_SHUTDOWN' in browserEnv:
+            del browserEnv["MOZ_CRASHREPORTER_SHUTDOWN"]
+
         # These variables are necessary for correct application startup; change
         # via the commandline at your own risk.
         browserEnv["XPCOM_DEBUG_BREAK"] = "stack"
 
         # interpolate environment passed with options
         try:
             browserEnv.update(
                 dict(
--- a/testing/mozbase/mozrunner/mozrunner/utils.py
+++ b/testing/mozbase/mozrunner/mozrunner/utils.py
@@ -116,16 +116,17 @@ def test_environment(xrePath, env=None, 
 
     # crashreporter
     env['GNOME_DISABLE_CRASH_DIALOG'] = '1'
     env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
 
     if crashreporter and not debugger:
         env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
         env['MOZ_CRASHREPORTER'] = '1'
+        env['MOZ_CRASHREPORTER_SHUTDOWN'] = '1'
     else:
         env['MOZ_CRASHREPORTER_DISABLE'] = '1'
 
     # Crash on non-local network connections by default.
     # MOZ_DISABLE_NONLOCAL_CONNECTIONS can be set to "0" to temporarily
     # enable non-local connections for the purposes of local testing.  Don't
     # override the user's choice here.  See bug 1049688.
     env.setdefault('MOZ_DISABLE_NONLOCAL_CONNECTIONS', '1')
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -160624,16 +160624,40 @@
       [
        "/css/reference/ref-filled-green-100px-square.xht",
        "=="
       ]
      ],
      {}
     ]
    ],
+   "css/selectors/selection-image-001.html": [
+    [
+     "/css/selectors/selection-image-001.html",
+     [
+      [
+       "/css/selectors/selection-image-001-noref.html",
+       "!="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/selectors/selection-image-002.html": [
+    [
+     "/css/selectors/selection-image-002.html",
+     [
+      [
+       "/css/selectors/selection-image-001-no-selection-noref.html",
+       "!="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/selectors/selector-placeholder-shown-type-change-001.html": [
     [
      "/css/selectors/selector-placeholder-shown-type-change-001.html",
      [
       [
        "/css/selectors/selector-placeholder-shown-type-change-001-ref.html",
        "=="
       ]
@@ -262154,16 +262178,21 @@
      {}
     ]
    ],
    "css/selectors/of-type-selectors-ref.xhtml": [
     [
      {}
     ]
    ],
+   "css/selectors/resources/blue15x15.png": [
+    [
+     {}
+    ]
+   ],
    "css/selectors/selector-placeholder-shown-type-change-001-ref.html": [
     [
      {}
     ]
    ],
    "css/selectors/selector-placeholder-shown-type-change-002-ref.html": [
     [
      {}
@@ -272034,16 +272063,21 @@
      {}
     ]
    ],
    "html/dom/dynamic-markup-insertion/opening-the-input-stream/document.open-03-frame.html": [
     [
      {}
     ]
    ],
+   "html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext-subframe.txt": [
+    [
+     {}
+    ]
+   ],
    "html/dom/elements-embedded.js": [
     [
      {}
     ]
    ],
    "html/dom/elements-forms.js": [
     [
      {}
@@ -324090,16 +324124,28 @@
     ]
    ],
    "html/dom/dynamic-markup-insertion/opening-the-input-stream/document.open-03.html": [
     [
      "/html/dom/dynamic-markup-insertion/opening-the-input-stream/document.open-03.html",
      {}
     ]
    ],
+   "html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.js": [
+    [
+     "/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.html",
+     {}
+    ]
+   ],
+   "html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument.window.js": [
+    [
+     "/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument.window.html",
+     {}
+    ]
+   ],
    "html/dom/elements/elements-in-the-dom/historical.html": [
     [
      "/html/dom/elements/elements-in-the-dom/historical.html",
      {}
     ]
    ],
    "html/dom/elements/elements-in-the-dom/unknown-element.html": [
     [
@@ -385653,16 +385699,28 @@
      {}
     ]
    ],
    "css/mediaqueries/media-queries-003.xht": [
     [
      "/css/mediaqueries/media-queries-003.xht",
      {}
     ]
+   ],
+   "css/selectors/selection-image-001-no-selection-noref.html": [
+    [
+     "/css/selectors/selection-image-001-no-selection-noref.html",
+     {}
+    ]
+   ],
+   "css/selectors/selection-image-001-noref.html": [
+    [
+     "/css/selectors/selection-image-001-noref.html",
+     {}
+    ]
    ]
   },
   "wdspec": {
    "webdriver/tests/actions/key.py": [
     [
      "/webdriver/tests/actions/key.py",
      {}
     ]
@@ -529069,24 +529127,44 @@
   "css/selectors/of-type-selectors-ref.xhtml": [
    "59f848418882c75898c422a9600c14ffab64c3d9",
    "support"
   ],
   "css/selectors/of-type-selectors.xhtml": [
    "607553f41a33ce3630752cdf027c9f904833a19d",
    "reftest"
   ],
+  "css/selectors/resources/blue15x15.png": [
+   "eb48032c07bfeb1d3b6be6e5c9c34d2fe2180767",
+   "support"
+  ],
   "css/selectors/root-siblings.htm": [
    "0d6e67589dab95d5362f82a99565947ebb487658",
    "reftest"
   ],
   "css/selectors/scope-without-scoping.html": [
    "f70b8d60543c5a28fcf955b1780f15c03d60991a",
    "reftest"
   ],
+  "css/selectors/selection-image-001-no-selection-noref.html": [
+   "b9a627630a8dcfaa70c74fc11ac9635aa00ac32c",
+   "visual"
+  ],
+  "css/selectors/selection-image-001-noref.html": [
+   "add1c00be4957ffef599aee52d061be7c09607bc",
+   "visual"
+  ],
+  "css/selectors/selection-image-001.html": [
+   "134b946744b45f488470dc5d1c690ee9a4855cbb",
+   "reftest"
+  ],
+  "css/selectors/selection-image-002.html": [
+   "5e2d33709b654a1f66eedcff995c8a3e9a8e01c5",
+   "reftest"
+  ],
   "css/selectors/selector-placeholder-shown-type-change-001-ref.html": [
    "92303d06943581738f58ff5d342ef1336539f66a",
    "support"
   ],
   "css/selectors/selector-placeholder-shown-type-change-001.html": [
    "6d84d237fe4b6d7f01e6c7c6129bdc8ccb06cf90",
    "reftest"
   ],
@@ -548253,16 +548331,28 @@
   "html/dom/dynamic-markup-insertion/opening-the-input-stream/document.open-03-frame.html": [
    "bcc6a14a9c3d116d95e72d3f057d0cd7cbffd2cb",
    "support"
   ],
   "html/dom/dynamic-markup-insertion/opening-the-input-stream/document.open-03.html": [
    "a2a5acc9dfe53c7482eeaa4be3a4819238f8e120",
    "testharness"
   ],
+  "html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext-subframe.txt": [
+   "8d06cea05d408d70c59b1dbc5df3bda374d869a4",
+   "support"
+  ],
+  "html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.js": [
+   "1f440ff93300a0ab715982feb067dd3162c8fce9",
+   "testharness"
+  ],
+  "html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument.window.js": [
+   "0f0020e1d7d8050892ef146d687178cfe8eedcd2",
+   "testharness"
+  ],
   "html/dom/elements-embedded.js": [
    "99489e3a8ac2be0d7dcfb22f7f45c30b00511358",
    "support"
   ],
   "html/dom/elements-forms.js": [
    "062e50a0665b90c3b77aaca744b65036054569f5",
    "support"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/WebCryptoAPI/derive_bits_keys/hkdf.https.worker.html.ini
@@ -0,0 +1,3 @@
+[hkdf.https.worker.html]
+  disabled:
+    if ccov and (os == "win"): https://bugzilla.mozilla.org/show_bug.cgi?id=1434754
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..89de32fdb8a4e48b1320f40f5a75352773077cee
GIT binary patch
literal 185
zc%17D@N?(olHy`uVBq!ia0vp^{2<K11SGd?VUh(>oCO|{#S9F5he4R}c>anMpde#$
zkh>GZx^prwfgF}}M_)$<hK>E)e-c@Ne1&9>AYTTCDm4a%h86~fUqGRT7Yq!g1`G_Z
z5*Qe)W-u^_7tGleXakf`@^o<w(FjgXN%(Qzfs0|4n%u+%B2Ty;@ydnvnyztk0=k6z
V)g}Gv*iewk44$rjF6*2UngFS5E#&|J
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/selectors/selection-image-001-no-selection-noref.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1449010">
+<p>
+  Some text <img src="resources/blue15x15.png"> some more.
+</p>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/selectors/selection-image-001-noref.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1449010">
+<p>
+  Some text <img src="resources/blue15x15.png"> some more.
+</p>
+<script>
+onload = () => {
+  getSelection().removeAllRanges();
+  let r = document.createRange();
+  r.selectNode(document.documentElement);
+  getSelection().addRange(r);
+}
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/selectors/selection-image-001.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>::selection is respected on images</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1449010">
+<link rel="mismatch" href="selection-image-001-noref.html">
+<style>
+img::-moz-selection {
+  background: green;
+}
+img::selection {
+  background: green;
+}
+</style>
+<p>
+  Some text <img src="resources/blue15x15.png"> some more.
+</p>
+<script>
+onload = () => {
+  getSelection().removeAllRanges();
+  let r = document.createRange();
+  r.selectNode(document.documentElement);
+  getSelection().addRange(r);
+}
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/selectors/selection-image-002.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>CSS Test: Image and text selection is painted.</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1449010">
+<link rel="mismatch" href="selection-image-001-no-selection-noref.html">
+<p>
+  Some text <img src="resources/blue15x15.png"> some more.
+</p>
+<script>
+onload = () => {
+  getSelection().removeAllRanges();
+  let r = document.createRange();
+  r.selectNode(document.documentElement);
+  getSelection().addRange(r);
+}
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext-subframe.txt
@@ -0,0 +1,1 @@
+Some text.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument-plaintext.window.js
@@ -0,0 +1,23 @@
+["replace",
+ "NOBODY",
+ "@ FD ;",
+ "it does not matter, you see \f",
+ "text/plain",
+ "text/xml",
+ "application/octet-stream",
+ "\0"].forEach(type => {
+  async_test(t => {
+    const frame = document.createElement("iframe");
+    frame.src = "type-argument-plaintext-subframe.txt";
+    document.body.appendChild(frame);
+    t.add_cleanup(() => frame.remove());
+    frame.onload = t.step_func_done(() => {
+      frame.contentDocument.open(type);
+      frame.contentDocument.write("<B>heya</b>");
+      frame.contentDocument.close();
+      assert_equals(frame.contentDocument.body.firstChild.localName, "b");
+      assert_equals(frame.contentDocument.body.textContent, "heya");
+      assert_equals(frame.contentDocument.contentType, "text/plain");
+    });
+  }, "document.open() on plaintext document with type set to: " + type + " (type argument is supposed to be ignored)");
+});
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/dynamic-markup-insertion/opening-the-input-stream/type-argument.window.js
@@ -0,0 +1,20 @@
+["replace",
+ "NOBODY",
+ "@ FD ;",
+ "it does not matter, you see \f",
+ "text/plain",
+ "text/xml",
+ "application/octet-stream",
+ "\0"].forEach(type => {
+  async_test(t => {
+    const frame = document.body.appendChild(document.createElement("iframe"));
+    t.add_cleanup(() => frame.remove());
+    frame.contentDocument.open(type);
+    frame.contentDocument.write("<B>heya</b>");
+    frame.contentDocument.close();
+    assert_equals(frame.contentDocument.body.firstChild.localName, "b");
+    assert_equals(frame.contentDocument.body.textContent, "heya");
+    assert_equals(frame.contentDocument.contentType, "text/html");
+    t.done();
+  }, "document.open() with type set to: " + type + " (type argument is supposed to be ignored)");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/child/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+  "globals": {
+    "EventManager": true,
+  },
+};
rename from toolkit/components/extensions/ext-c-backgroundPage.js
rename to toolkit/components/extensions/child/ext-backgroundPage.js
rename from toolkit/components/extensions/ext-c-contentScripts.js
rename to toolkit/components/extensions/child/ext-contentScripts.js
rename from toolkit/components/extensions/ext-c-extension.js
rename to toolkit/components/extensions/child/ext-extension.js
rename from toolkit/components/extensions/ext-c-identity.js
rename to toolkit/components/extensions/child/ext-identity.js
rename from toolkit/components/extensions/ext-c-runtime.js
rename to toolkit/components/extensions/child/ext-runtime.js
rename from toolkit/components/extensions/ext-c-storage.js
rename to toolkit/components/extensions/child/ext-storage.js
--- a/toolkit/components/extensions/ext-c-storage.js
+++ b/toolkit/components/extensions/child/ext-storage.js
@@ -1,12 +1,10 @@
 "use strict";
 
-/* import-globals-from ext-c-toolkit.js */
-
 ChromeUtils.defineModuleGetter(this, "ExtensionStorage",
                                "resource://gre/modules/ExtensionStorage.jsm");
 ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
                                "resource://gre/modules/TelemetryStopwatch.jsm");
 
 var {
   ExtensionError,
 } = ExtensionUtils;
rename from toolkit/components/extensions/ext-c-test.js
rename to toolkit/components/extensions/child/ext-test.js
--- a/toolkit/components/extensions/ext-c-test.js
+++ b/toolkit/components/extensions/child/ext-test.js
@@ -1,13 +1,10 @@
 "use strict";
 
-// The ext-c-* files are imported into the same scopes.
-/* import-globals-from ext-c-toolkit.js */
-
 /**
  * Checks whether the given error matches the given expectations.
  *
  * @param {*} error
  *        The error to check.
  * @param {string|RegExp|function|null} expectedError
  *        The expectation to check against. If this parameter is:
  *
rename from toolkit/components/extensions/ext-c-toolkit.js
rename to toolkit/components/extensions/child/ext-toolkit.js
--- a/toolkit/components/extensions/ext-c-toolkit.js
+++ b/toolkit/components/extensions/child/ext-toolkit.js
@@ -9,78 +9,78 @@ ChromeUtils.defineModuleGetter(this, "Se
 // ext-c-*.js files.
 /* exported EventManager */
 /* global EventManager: false */
 
 global.EventManager = ExtensionCommon.EventManager;
 
 extensions.registerModules({
   backgroundPage: {
-    url: "chrome://extensions/content/ext-c-backgroundPage.js",
+    url: "chrome://extensions/content/child/ext-backgroundPage.js",
     scopes: ["addon_child"],
     manifest: ["background"],
     paths: [
       ["extension", "getBackgroundPage"],
       ["runtime", "getBackgroundPage"],
     ],
   },
   contentScripts: {
-    url: "chrome://extensions/content/ext-c-contentScripts.js",
+    url: "chrome://extensions/content/child/ext-contentScripts.js",
     scopes: ["addon_child"],
     paths: [
       ["contentScripts"],
     ],
   },
   extension: {
-    url: "chrome://extensions/content/ext-c-extension.js",
+    url: "chrome://extensions/content/child/ext-extension.js",
     scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
     paths: [
       ["extension"],
     ],
   },
   i18n: {
-    url: "chrome://extensions/content/ext-i18n.js",
+    url: "chrome://extensions/content/parent/ext-i18n.js",
     scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
     paths: [
       ["i18n"],
     ],
   },
   runtime: {
-    url: "chrome://extensions/content/ext-c-runtime.js",
+    url: "chrome://extensions/content/child/ext-runtime.js",
     scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
     paths: [
       ["runtime"],
     ],
   },
   storage: {
-    url: "chrome://extensions/content/ext-c-storage.js",
+    url: "chrome://extensions/content/child/ext-storage.js",
     scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
     paths: [
       ["storage"],
     ],
   },
   test: {
-    url: "chrome://extensions/content/ext-c-test.js",
+    url: "chrome://extensions/content/child/ext-test.js",
     scopes: ["addon_child", "content_child", "devtools_child", "proxy_script"],
     paths: [
       ["test"],
     ],
   },
   webRequest: {
-    url: "chrome://extensions/content/ext-c-webRequest.js",
+    url: "chrome://extensions/content/child/ext-webRequest.js",
     scopes: ["addon_child"],
     paths: [
       ["webRequest"],
     ],
   },
 });
 
 if (AppConstants.MOZ_BUILD_APP === "browser") {
   extensions.registerModules({
     identity: {
-      url: "chrome://extensions/content/ext-c-identity.js",
+      url: "chrome://extensions/content/child/ext-identity.js",
       scopes: ["addon_child"],
       paths: [
         ["identity"],
       ],
     },
   });
 }
rename from toolkit/components/extensions/ext-c-webRequest.js
rename to toolkit/components/extensions/child/ext-webRequest.js
--- a/toolkit/components/extensions/ext-toolkit.json
+++ b/toolkit/components/extensions/ext-toolkit.json
@@ -1,200 +1,200 @@
 {
   "manifest": {
     "schema": "chrome://extensions/content/schemas/extension_types.json",
     "scopes": []
   },
   "alarms": {
-    "url": "chrome://extensions/content/ext-alarms.js",
+    "url": "chrome://extensions/content/parent/ext-alarms.js",
     "schema": "chrome://extensions/content/schemas/alarms.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["alarms"]
     ]
   },
   "backgroundPage": {
-    "url": "chrome://extensions/content/ext-backgroundPage.js",
+    "url": "chrome://extensions/content/parent/ext-backgroundPage.js",
     "scopes": ["addon_parent"],
     "manifest": ["background"]
   },
   "browserSettings": {
-    "url": "chrome://extensions/content/ext-browserSettings.js",
+    "url": "chrome://extensions/content/parent/ext-browserSettings.js",
     "schema": "chrome://extensions/content/schemas/browser_settings.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["browserSettings"]
     ]
   },
   "clipboard": {
-    "url": "chrome://extensions/content/ext-clipboard.js",
+    "url": "chrome://extensions/content/parent/ext-clipboard.js",
     "schema": "chrome://extensions/content/schemas/clipboard.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["clipboard"]
     ]
   },
   "contentScripts": {
-    "url": "chrome://extensions/content/ext-contentScripts.js",
+    "url": "chrome://extensions/content/parent/ext-contentScripts.js",
     "schema": "chrome://extensions/content/schemas/content_scripts.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["contentScripts"]
     ]
   },
   "contextualIdentities": {
-    "url": "chrome://extensions/content/ext-contextualIdentities.js",
+    "url": "chrome://extensions/content/parent/ext-contextualIdentities.js",
     "schema": "chrome://extensions/content/schemas/contextual_identities.json",
     "scopes": ["addon_parent"],
     "events": ["startup"],
     "permissions": ["contextualIdentities"],
     "paths": [
       ["contextualIdentities"]
     ]
   },
   "cookies": {
-    "url": "chrome://extensions/content/ext-cookies.js",
+    "url": "chrome://extensions/content/parent/ext-cookies.js",
     "schema": "chrome://extensions/content/schemas/cookies.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["cookies"]
     ]
   },
   "dns": {
-    "url": "chrome://extensions/content/ext-dns.js",
+    "url": "chrome://extensions/content/parent/ext-dns.js",
     "schema": "chrome://extensions/content/schemas/dns.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["dns"]
     ]
   },
   "downloads": {
-    "url": "chrome://extensions/content/ext-downloads.js",
+    "url": "chrome://extensions/content/parent/ext-downloads.js",
     "schema": "chrome://extensions/content/schemas/downloads.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["downloads"]
     ]
   },
   "extension": {
-    "url": "chrome://extensions/content/ext-extension.js",
+    "url": "chrome://extensions/content/parent/ext-extension.js",
     "schema": "chrome://extensions/content/schemas/extension.json",
     "scopes": ["addon_parent", "content_child"],
     "paths": [
       ["extension"]
     ]
   },
   "i18n": {
-    "url": "chrome://extensions/content/ext-i18n.js",
+    "url": "chrome://extensions/content/parent/ext-i18n.js",
     "schema": "chrome://extensions/content/schemas/i18n.json",
     "scopes": ["addon_parent", "content_child", "devtools_child"],
     "paths": [
       ["i18n"]
     ]
   },
   "idle": {
-    "url": "chrome://extensions/content/ext-idle.js",
+    "url": "chrome://extensions/content/parent/ext-idle.js",
     "schema": "chrome://extensions/content/schemas/idle.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["idle"]
     ]
   },
   "management": {
-    "url": "chrome://extensions/content/ext-management.js",
+    "url": "chrome://extensions/content/parent/ext-management.js",
     "schema": "chrome://extensions/content/schemas/management.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["management"]
     ]
   },
   "notifications": {
-    "url": "chrome://extensions/content/ext-notifications.js",
+    "url": "chrome://extensions/content/parent/ext-notifications.js",
     "schema": "chrome://extensions/content/schemas/notifications.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["notifications"]
     ]
   },
   "permissions": {
-    "url": "chrome://extensions/content/ext-permissions.js",
+    "url": "chrome://extensions/content/parent/ext-permissions.js",
     "schema": "chrome://extensions/content/schemas/permissions.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["permissions"]
     ]
   },
   "privacy": {
-    "url": "chrome://extensions/content/ext-privacy.js",
+    "url": "chrome://extensions/content/parent/ext-privacy.js",
     "schema": "chrome://extensions/content/schemas/privacy.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["privacy"]
     ]
   },
   "protocolHandlers": {
-    "url": "chrome://extensions/content/ext-protocolHandlers.js",
+    "url": "chrome://extensions/content/parent/ext-protocolHandlers.js",
     "schema": "chrome://extensions/content/schemas/extension_protocol_handlers.json",
     "scopes": ["addon_parent"],
     "manifest": ["protocol_handlers"]
   },
   "proxy": {
-    "url": "chrome://extensions/content/ext-proxy.js",
+    "url": "chrome://extensions/content/parent/ext-proxy.js",
     "schema": "chrome://extensions/content/schemas/proxy.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["proxy"]
     ]
   },
   "runtime": {
-    "url": "chrome://extensions/content/ext-runtime.js",
+    "url": "chrome://extensions/content/parent/ext-runtime.js",
     "schema": "chrome://extensions/content/schemas/runtime.json",
     "scopes": ["addon_parent", "content_parent", "devtools_parent"],
     "paths": [
       ["runtime"]
     ]
   },
   "storage": {
-    "url": "chrome://extensions/content/ext-storage.js",
+    "url": "chrome://extensions/content/parent/ext-storage.js",
     "schema": "chrome://extensions/content/schemas/storage.json",
     "scopes": ["addon_parent", "content_parent", "devtools_parent"],
     "paths": [
       ["storage"]
     ]
   },
   "test": {
     "schema": "chrome://extensions/content/schemas/test.json",
     "scopes": ["content_child"]
   },
   "theme": {
-    "url": "chrome://extensions/content/ext-theme.js",
+    "url": "chrome://extensions/content/parent/ext-theme.js",
     "schema": "chrome://extensions/content/schemas/theme.json",
     "scopes": ["addon_parent"],
     "manifest": ["theme"],
     "paths": [
       ["theme"]
     ]
   },
   "topSites": {
-    "url": "chrome://extensions/content/ext-topSites.js",
+    "url": "chrome://extensions/content/parent/ext-topSites.js",
     "schema": "chrome://extensions/content/schemas/top_sites.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["topSites"]
     ]
   },
   "webNavigation": {
-    "url": "chrome://extensions/content/ext-webNavigation.js",
+    "url": "chrome://extensions/content/parent/ext-webNavigation.js",
     "schema": "chrome://extensions/content/schemas/web_navigation.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["webNavigation"]
     ]
   },
   "webRequest": {
-    "url": "chrome://extensions/content/ext-webRequest.js",
+    "url": "chrome://extensions/content/parent/ext-webRequest.js",
     "schema": "chrome://extensions/content/schemas/web_request.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["webRequest"]
     ]
   }
 }
--- a/toolkit/components/extensions/extensions-toolkit.manifest
+++ b/toolkit/components/extensions/extensions-toolkit.manifest
@@ -1,17 +1,17 @@
 # scripts
 category webextension-modules toolkit chrome://extensions/content/ext-toolkit.json
 
-category webextension-scripts a-toolkit chrome://extensions/content/ext-toolkit.js
-category webextension-scripts b-tabs-base chrome://extensions/content/ext-tabs-base.js
+category webextension-scripts a-toolkit chrome://extensions/content/parent/ext-toolkit.js
+category webextension-scripts b-tabs-base chrome://extensions/content/parent/ext-tabs-base.js
 
-category webextension-scripts-content toolkit chrome://extensions/content/ext-c-toolkit.js
-category webextension-scripts-devtools toolkit chrome://extensions/content/ext-c-toolkit.js
-category webextension-scripts-addon toolkit chrome://extensions/content/ext-c-toolkit.js
+category webextension-scripts-content toolkit chrome://extensions/content/child/ext-toolkit.js
+category webextension-scripts-devtools toolkit chrome://extensions/content/child/ext-toolkit.js
+category webextension-scripts-addon toolkit chrome://extensions/content/child/ext-toolkit.js
 
 category webextension-schemas events chrome://extensions/content/schemas/events.json
 category webextension-schemas native_manifest chrome://extensions/content/schemas/native_manifest.json
 category webextension-schemas types chrome://extensions/content/schemas/types.json
 
 
 component {21f9819e-4cdf-49f9-85a0-850af91a5058} extension-process-script.js
 contract @mozilla.org/webextensions/extension-process-script;1 {21f9819e-4cdf-49f9-85a0-850af91a5058}
--- a/toolkit/components/extensions/jar.mn
+++ b/toolkit/components/extensions/jar.mn
@@ -1,51 +1,49 @@
 # 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/.
 
 toolkit.jar:
 % content extensions %content/extensions/
     content/extensions/dummy.xul
-    content/extensions/ext-alarms.js
-    content/extensions/ext-backgroundPage.js
     content/extensions/ext-browser-content.js
-    content/extensions/ext-browserSettings.js
-    content/extensions/ext-contentScripts.js
-    content/extensions/ext-contextualIdentities.js
-    content/extensions/ext-clipboard.js
-    content/extensions/ext-cookies.js
-    content/extensions/ext-dns.js
-    content/extensions/ext-downloads.js
-    content/extensions/ext-extension.js
-    content/extensions/ext-i18n.js
+    content/extensions/ext-toolkit.json
+    content/extensions/parent/ext-alarms.js (parent/ext-alarms.js)
+    content/extensions/parent/ext-backgroundPage.js (parent/ext-backgroundPage.js)
+    content/extensions/parent/ext-browserSettings.js (parent/ext-browserSettings.js)
+    content/extensions/parent/ext-contentScripts.js (parent/ext-contentScripts.js)
+    content/extensions/parent/ext-contextualIdentities.js (parent/ext-contextualIdentities.js)
+    content/extensions/parent/ext-clipboard.js (parent/ext-clipboard.js)
+    content/extensions/parent/ext-cookies.js (parent/ext-cookies.js)
+    content/extensions/parent/ext-dns.js (parent/ext-dns.js)
+    content/extensions/parent/ext-downloads.js (parent/ext-downloads.js)
+    content/extensions/parent/ext-extension.js (parent/ext-extension.js)
+    content/extensions/parent/ext-i18n.js (parent/ext-i18n.js)
 #ifndef ANDROID
-    content/extensions/ext-identity.js
+    content/extensions/parent/ext-identity.js (parent/ext-identity.js)
 #endif
-    content/extensions/ext-idle.js
-    content/extensions/ext-management.js
-    content/extensions/ext-notifications.js
-    content/extensions/ext-permissions.js
-    content/extensions/ext-privacy.js
-    content/extensions/ext-protocolHandlers.js
-    content/extensions/ext-proxy.js
-    content/extensions/ext-runtime.js
-    content/extensions/ext-storage.js
-    content/extensions/ext-tabs-base.js
-    content/extensions/ext-theme.js
-    content/extensions/ext-toolkit.js
-    content/extensions/ext-toolkit.json
-    content/extensions/ext-topSites.js
-    content/extensions/ext-webRequest.js
-    content/extensions/ext-webNavigation.js
-    # Below is a separate group using the naming convention ext-c-*.js that run
-    # in the child process.
-    content/extensions/ext-c-backgroundPage.js
-    content/extensions/ext-c-contentScripts.js
-    content/extensions/ext-c-extension.js
+    content/extensions/parent/ext-idle.js (parent/ext-idle.js)
+    content/extensions/parent/ext-management.js (parent/ext-management.js)
+    content/extensions/parent/ext-notifications.js (parent/ext-notifications.js)
+    content/extensions/parent/ext-permissions.js (parent/ext-permissions.js)
+    content/extensions/parent/ext-privacy.js (parent/ext-privacy.js)
+    content/extensions/parent/ext-protocolHandlers.js (parent/ext-protocolHandlers.js)
+    content/extensions/parent/ext-proxy.js (parent/ext-proxy.js)
+    content/extensions/parent/ext-runtime.js (parent/ext-runtime.js)
+    content/extensions/parent/ext-storage.js (parent/ext-storage.js)
+    content/extensions/parent/ext-tabs-base.js (parent/ext-tabs-base.js)
+    content/extensions/parent/ext-theme.js (parent/ext-theme.js)
+    content/extensions/parent/ext-toolkit.js (parent/ext-toolkit.js)
+    content/extensions/parent/ext-topSites.js (parent/ext-topSites.js)
+    content/extensions/parent/ext-webRequest.js (parent/ext-webRequest.js)
+    content/extensions/parent/ext-webNavigation.js (parent/ext-webNavigation.js)
+    content/extensions/child/ext-backgroundPage.js (child/ext-backgroundPage.js)
+    content/extensions/child/ext-contentScripts.js (child/ext-contentScripts.js)
+    content/extensions/child/ext-extension.js (child/ext-extension.js)
 #ifndef ANDROID
-    content/extensions/ext-c-identity.js
+    content/extensions/child/ext-identity.js (child/ext-identity.js)
 #endif
-    content/extensions/ext-c-runtime.js
-    content/extensions/ext-c-storage.js
-    content/extensions/ext-c-test.js
-    content/extensions/ext-c-toolkit.js
-    content/extensions/ext-c-webRequest.js
+    content/extensions/child/ext-runtime.js (child/ext-runtime.js)
+    content/extensions/child/ext-storage.js (child/ext-storage.js)
+    content/extensions/child/ext-test.js (child/ext-test.js)
+    content/extensions/child/ext-toolkit.js (child/ext-toolkit.js)
+    content/extensions/child/ext-webRequest.js (child/ext-webRequest.js)
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/parent/.eslintrc.js
@@ -0,0 +1,25 @@
+"use strict";
+
+module.exports = {
+  "globals": {
+    "CONTAINER_STORE": true,
+    "DEFAULT_STORE": true,
+    "EventEmitter": true,
+    "EventManager": true,
+    "InputEventManager": true,
+    "PRIVATE_STORE": true,
+    "TabBase": true,
+    "TabManagerBase": true,
+    "TabTrackerBase": true,
+    "WindowBase": true,
+    "WindowManagerBase": true,
+    "WindowTrackerBase": true,
+    "getContainerForCookieStoreId": true,
+    "getCookieStoreIdForContainer": true,
+    "getCookieStoreIdForTab": true,
+    "isContainerCookieStoreId": true,
+    "isDefaultCookieStoreId": true,
+    "isPrivateCookieStoreId": true,
+    "isValidCookieStoreId": true,
+  },
+};
rename from toolkit/components/extensions/ext-alarms.js
rename to toolkit/components/extensions/parent/ext-alarms.js
rename from toolkit/components/extensions/ext-backgroundPage.js
rename to toolkit/components/extensions/parent/ext-backgroundPage.js
rename from toolkit/components/extensions/ext-browserSettings.js
rename to toolkit/components/extensions/parent/ext-browserSettings.js
rename from toolkit/components/extensions/ext-clipboard.js
rename to toolkit/components/extensions/parent/ext-clipboard.js
rename from toolkit/components/extensions/ext-contentScripts.js
rename to toolkit/components/extensions/parent/ext-contentScripts.js
rename from toolkit/components/extensions/ext-contextualIdentities.js
rename to toolkit/components/extensions/parent/ext-contextualIdentities.js
--- a/toolkit/components/extensions/ext-contextualIdentities.js
+++ b/toolkit/components/extensions/parent/ext-contextualIdentities.js
@@ -1,13 +1,10 @@
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-toolkit.js */
-
 ChromeUtils.defineModuleGetter(this, "ContextualIdentityService",
                                "resource://gre/modules/ContextualIdentityService.jsm");
 XPCOMUtils.defineLazyPreferenceGetter(this, "containersEnabled",
                                       "privacy.userContext.enabled");
 
 ChromeUtils.import("resource://gre/modules/ExtensionPreferencesManager.jsm");
 
 var {
rename from toolkit/components/extensions/ext-cookies.js
rename to toolkit/components/extensions/parent/ext-cookies.js
--- a/toolkit/components/extensions/ext-cookies.js
+++ b/toolkit/components/extensions/parent/ext-cookies.js
@@ -1,13 +1,10 @@
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-toolkit.js */
-
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 
 /* globals DEFAULT_STORE, PRIVATE_STORE */
 
 var {
   ExtensionError,
 } = ExtensionUtils;
rename from toolkit/components/extensions/ext-dns.js
rename to toolkit/components/extensions/parent/ext-dns.js
rename from toolkit/components/extensions/ext-downloads.js
rename to toolkit/components/extensions/parent/ext-downloads.js
--- a/toolkit/components/extensions/ext-downloads.js
+++ b/toolkit/components/extensions/parent/ext-downloads.js
@@ -1,13 +1,10 @@
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-toolkit.js */
-
 ChromeUtils.defineModuleGetter(this, "AppConstants",
                                "resource://gre/modules/AppConstants.jsm");
 ChromeUtils.defineModuleGetter(this, "Downloads",
                                "resource://gre/modules/Downloads.jsm");
 ChromeUtils.defineModuleGetter(this, "DownloadPaths",
                                "resource://gre/modules/DownloadPaths.jsm");
 ChromeUtils.defineModuleGetter(this, "OS",
                                "resource://gre/modules/osfile.jsm");
rename from toolkit/components/extensions/ext-extension.js
rename to toolkit/components/extensions/parent/ext-extension.js
rename from toolkit/components/extensions/ext-i18n.js
rename to toolkit/components/extensions/parent/ext-i18n.js
rename from toolkit/components/extensions/ext-identity.js
rename to toolkit/components/extensions/parent/ext-identity.js
rename from toolkit/components/extensions/ext-idle.js
rename to toolkit/components/extensions/parent/ext-idle.js
--- a/toolkit/components/extensions/ext-idle.js
+++ b/toolkit/components/extensions/parent/ext-idle.js
@@ -1,13 +1,10 @@
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-toolkit.js */
-
 XPCOMUtils.defineLazyServiceGetter(this, "idleService",
                                    "@mozilla.org/widget/idleservice;1",
                                    "nsIIdleService");
 
 // WeakMap[Extension -> Object]
 let observersMap = new WeakMap();
 
 const getIdleObserverInfo = (extension, context) => {
rename from toolkit/components/extensions/ext-management.js
rename to toolkit/components/extensions/parent/ext-management.js
--- a/toolkit/components/extensions/ext-management.js
+++ b/toolkit/components/extensions/parent/ext-management.js
@@ -1,15 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-toolkit.js */
-
 XPCOMUtils.defineLazyGetter(this, "strBundle", function() {
   return Services.strings.createBundle("chrome://global/locale/extensions.properties");
 });
 ChromeUtils.defineModuleGetter(this, "AddonManager",
                                "resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "promptService",
                                    "@mozilla.org/embedcomp/prompt-service;1",
                                    "nsIPromptService");
rename from toolkit/components/extensions/ext-notifications.js
rename to toolkit/components/extensions/parent/ext-notifications.js
--- a/toolkit/components/extensions/ext-notifications.js
+++ b/toolkit/components/extensions/parent/ext-notifications.js
@@ -1,13 +1,10 @@
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-toolkit.js */
-
 const ToolkitModules = {};
 
 ChromeUtils.defineModuleGetter(ToolkitModules, "EventEmitter",
                                "resource://gre/modules/EventEmitter.jsm");
 
 var {
   ignoreEvent,
 } = ExtensionCommon;
rename from toolkit/components/extensions/ext-permissions.js
rename to toolkit/components/extensions/parent/ext-permissions.js
rename from toolkit/components/extensions/ext-privacy.js
rename to toolkit/components/extensions/parent/ext-privacy.js
rename from toolkit/components/extensions/ext-protocolHandlers.js
rename to toolkit/components/extensions/parent/ext-protocolHandlers.js
rename from toolkit/components/extensions/ext-proxy.js
rename to toolkit/components/extensions/parent/ext-proxy.js
--- a/toolkit/components/extensions/ext-proxy.js
+++ b/toolkit/components/extensions/parent/ext-proxy.js
@@ -1,18 +1,14 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set sts=2 sw=2 et tw=80: */
-
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-toolkit.js */
-
 "use strict";
 
 ChromeUtils.defineModuleGetter(this, "ProxyScriptContext",
                                "resource://gre/modules/ProxyScriptContext.jsm");
 ChromeUtils.defineModuleGetter(this, "ProxyChannelFilter",
                                "resource://gre/modules/ProxyScriptContext.jsm");
 
 // WeakMap[Extension -> ProxyScriptContext]
rename from toolkit/components/extensions/ext-runtime.js
rename to toolkit/components/extensions/parent/ext-runtime.js
--- a/toolkit/components/extensions/ext-runtime.js
+++ b/toolkit/components/extensions/parent/ext-runtime.js
@@ -1,13 +1,10 @@
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-toolkit.js */
-
 ChromeUtils.defineModuleGetter(this, "AddonManager",
                                "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "AddonManagerPrivate",
                                "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionParent",
                                "resource://gre/modules/ExtensionParent.jsm");
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
rename from toolkit/components/extensions/ext-storage.js
rename to toolkit/components/extensions/parent/ext-storage.js
--- a/toolkit/components/extensions/ext-storage.js
+++ b/toolkit/components/extensions/parent/ext-storage.js
@@ -1,13 +1,10 @@
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-toolkit.js */
-
 XPCOMUtils.defineLazyModuleGetters(this, {
   AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm",
   ExtensionStorage: "resource://gre/modules/ExtensionStorage.jsm",
   extensionStorageSync: "resource://gre/modules/ExtensionStorageSync.jsm",
   NativeManifests: "resource://gre/modules/NativeManifests.jsm",
 });
 
 var {
rename from toolkit/components/extensions/ext-tabs-base.js
rename to toolkit/components/extensions/parent/ext-tabs-base.js
rename from toolkit/components/extensions/ext-theme.js
rename to toolkit/components/extensions/parent/ext-theme.js
rename from toolkit/components/extensions/ext-toolkit.js
rename to toolkit/components/extensions/parent/ext-toolkit.js
rename from toolkit/components/extensions/ext-topSites.js
rename to toolkit/components/extensions/parent/ext-topSites.js
rename from toolkit/components/extensions/ext-webNavigation.js
rename to toolkit/components/extensions/parent/ext-webNavigation.js
--- a/toolkit/components/extensions/ext-webNavigation.js
+++ b/toolkit/components/extensions/parent/ext-webNavigation.js
@@ -1,13 +1,10 @@
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-toolkit.js */
-
 // This file expectes tabTracker to be defined in the global scope (e.g.
 // by ext-utils.js).
 /* global tabTracker */
 
 ChromeUtils.defineModuleGetter(this, "MatchURLFilters",
                                "resource://gre/modules/MatchURLFilters.jsm");
 ChromeUtils.defineModuleGetter(this, "WebNavigation",
                                "resource://gre/modules/WebNavigation.jsm");
rename from toolkit/components/extensions/ext-webRequest.js
rename to toolkit/components/extensions/parent/ext-webRequest.js
--- a/toolkit/components/extensions/ext-webRequest.js
+++ b/toolkit/components/extensions/parent/ext-webRequest.js
@@ -1,13 +1,10 @@
 "use strict";
 
-// The ext-* files are imported into the same scopes.
-/* import-globals-from ext-toolkit.js */
-
 // This file expectes tabTracker to be defined in the global scope (e.g.
 // by ext-utils.js).
 /* global tabTracker */
 
 ChromeUtils.defineModuleGetter(this, "WebRequest",
                                "resource://gre/modules/WebRequest.jsm");
 
 // EventManager-like class specifically for WebRequest. Inherits from
--- a/toolkit/components/places/nsFaviconService.cpp
+++ b/toolkit/components/places/nsFaviconService.cpp
@@ -347,17 +347,17 @@ nsFaviconService::SetAndFetchFaviconForP
       u"nsFaviconService::setAndFetchFaviconForPage(..., [optional aLoadingPrincipal])"
     };
     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                     NS_LITERAL_CSTRING("Security by Default"),
                                     nullptr, // aDocument
                                     nsContentUtils::eNECKO_PROPERTIES,
                                     "APIDeprecationWarning",
                                     params, ArrayLength(params));
-    loadingPrincipal = NullPrincipal::Create();
+    loadingPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
   }
   NS_ENSURE_TRUE(loadingPrincipal, NS_ERROR_FAILURE);
 
   bool loadPrivate = aFaviconLoadType == nsIFaviconService::FAVICON_LOAD_PRIVATE;
 
   // Build page data.
   PageData page;
   rv = aPageURI->GetSpec(page.spec);
@@ -540,17 +540,17 @@ nsFaviconService::ReplaceFaviconDataFrom
     };
     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                     NS_LITERAL_CSTRING("Security by Default"),
                                     nullptr, // aDocument
                                     nsContentUtils::eNECKO_PROPERTIES,
                                     "APIDeprecationWarning",
                                     params, ArrayLength(params));
 
-    loadingPrincipal = NullPrincipal::Create();
+    loadingPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
   }
   NS_ENSURE_TRUE(loadingPrincipal, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsILoadInfo> loadInfo =
     new mozilla::LoadInfo(loadingPrincipal,
                           nullptr, // aTriggeringPrincipal
                           nullptr, // aLoadingNode
                           nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
--- a/toolkit/content/tests/unit/test_contentAreaUtils.js
+++ b/toolkit/content/tests/unit/test_contentAreaUtils.js
@@ -6,18 +6,17 @@
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 function loadUtilsScript() {
   /* import-globals-from ../../contentAreaUtils.js */
   Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js");
 }
 
 function test_urlSecurityCheck() {
-  var nullPrincipal = Cc["@mozilla.org/nullprincipal;1"].
-                      createInstance(Ci.nsIPrincipal);
+  var nullPrincipal = Services.scriptSecurityManager.createNullPrincipal({});
 
   const HTTP_URI = "http://www.mozilla.org/";
   const CHROME_URI = "chrome://browser/content/browser.xul";
   const DISALLOW_INHERIT_PRINCIPAL =
     Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL;
 
   try {
     urlSecurityCheck(makeURI(HTTP_URI), nullPrincipal,
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/bootstrap.js
+++ /dev/null
@@ -1,1 +0,0 @@
-ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/install.rdf
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>bootstrap1@tests.mozilla.org</em:id>
-    <em:version>3.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Bootstrap 1</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>undefined</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_3/version.jsm
+++ /dev/null
@@ -1,3 +0,0 @@
-var EXPORTED_SYMBOLS = ["VERSION"];
-
-var VERSION = 3;
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap1_4/install.rdf
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>bootstrap1@tests.mozilla.org</em:id>
-    <em:version>4.0</em:version>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Bootstrap 1</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/bootstrap.js
+++ /dev/null
@@ -1,1 +0,0 @@
-ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_bootstrap2_1/install.rdf
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>bootstrap2@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:bootstrap>true</em:bootstrap>
-    <em:multiprocessCompatible>true</em:multiprocessCompatible>
-
-    <!-- Front End MetaData -->
-    <em:name>Test Bootstrap 2</em:name>
-    <em:description>Test Description</em:description>
-
-    <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
-    <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
-    <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>1</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.json
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.json
@@ -9,71 +9,71 @@
             "name": "PASS",
             "type": "extension",
             "guid": "test1@tests.mozilla.org",
             "current_version": {
                 "version": "1.1",
                 "files": [
                     {
                         "platform": "all",
-                        "url": "http://localhost:%PORT%/addons/test_AddonRepository_2.xpi",
+                        "url": "http://example.com/addons/test_AddonRepository_2.xpi",
                         "size": 5555
                     }
                 ]
             },
             "authors": [
                 {
                     "name": "Test Creator 1",
-                    "url": "http://localhost:%PORT%/creator1.html"
+                    "url": "http://example.com/creator1.html"
                 },
                 {
                     "name": "Test Developer 1",
-                    "url": "http://localhost:%PORT%/developer1.html"
+                    "url": "http://example.com/developer1.html"
                 }
             ],
             "summary": "Test Summary 1",
             "description": "Test Description 1",
             "icons": {
-              "32": "http://localhost:%PORT%/icon1.png"
+              "32": "http://example.com/icon1.png"
             },
             "previews": [
                 {
                     "caption": "Caption 1 - 1",
                     "image_size": [400, 300],
-                    "image_url": "http://localhost:%PORT%/full1-1.png",
+                    "image_url": "http://example.com/full1-1.png",
                     "thumbnail_size": [200, 150],
-                    "thumbnail_url": "http://localhost:%PORT%/thumbnail1-1.png"
+                    "thumbnail_url": "http://example.com/thumbnail1-1.png"
                 },
                 {
                     "caption": "Caption 2 - 1",
-                    "image_url": "http://localhost:%PORT%/full2-1.png",
-                    "thumbnail_url": "http://localhost:%PORT%/thumbnail2-1.png"
+                    "image_url": "http://example.com/full2-1.png",
+                    "thumbnail_url": "http://example.com/thumbnail2-1.png"
                 }
             ],
             "ratings": {
                 "count": 1234,
                 "text_count": 1111,
                 "average": 4
             },
-            "ratings_url": "http://localhost:%PORT%/review1.html",
-            "support_url": "http://localhost:%PORT%/support1.html",
-            "contributions_url": "http://localhost:%PORT%/contribution1.html",
+            "ratings_url": "http://example.com/review1.html",
+            "support_url": "http://example.com/support1.html",
+            "contributions_url": "http://example.com/contribution1.html",
             "weekly_downloads": 3333,
             "last_updated": "2010-02-01T14:04:05Z"
         },
         {
             "name": "FAIL",
             "type": "extension",
             "guid": "notRequested@tests.mozilla.org",
             "current_version": {
                 "version": "1.3",
                 "files": [
                     {
                         "platform": "all",
-                        "url": "http://localhost:%PORT%/test3.xpi"
+                        "url": "http://example.com/test3.xpi"
                     }
                 ]
             },
             "authors": [
                 {
                     "name": "Test Creator 3"
                 }
             ],
@@ -83,20 +83,20 @@
             "name": "PASS",
             "type": "theme",
             "guid": "test_AddonRepository_1@tests.mozilla.org",
             "current_version": {
                 "version": "1.4",
                 "files": [
                     {
                         "platform": "UNKNOWN1",
-                        "url": "http://localhost:%PORT%/test4.xpi"
+                        "url": "http://example.com/test4.xpi"
                     },
                     {
                         "platform": "UNKNOWN2",
-                        "url": "http://localhost:%PORT%/test4.xpi"
+                        "url": "http://example.com/test4.xpi"
                     }
                 ]
             }
         }
     ]
 }
 
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_backgroundupdate.json
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_backgroundupdate.json
@@ -5,42 +5,42 @@
         {
           "applications": {
             "gecko": {
               "strict_min_version": "1",
               "advisory_max_version": "1"
             }
           },
           "version": "2",
-          "update_link": "http://localhost:%PORT%/broken.xpi"
+          "update_link": "http://example.com/broken.xpi"
         }
       ]
     },
     "addon3@tests.mozilla.org": {
       "updates": [
         {
           "applications": {
             "gecko": {
               "strict_min_version": "1",
               "advisory_max_version": "1"
             }
           },
           "version": "2",
-          "update_link": "http://localhost:%PORT%/broken.xpi"
+          "update_link": "http://example.com/broken.xpi"
         }
       ]
     },
     "addon1@tests.mozilla.org": {
       "updates": [
         {
           "applications": {
             "gecko": {
               "strict_min_version": "1",
               "advisory_max_version": "1"
             }
           },
           "version": "2",
-          "update_link": "http://localhost:%PORT%/broken.xpi"
+          "update_link": "http://example.com/broken.xpi"
         }
       ]
     }
   }
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_delay_updates_complete.json
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_delay_updates_complete.json
@@ -1,11 +1,11 @@
 {
   "addons": {
     "test_delay_update_complete_webext@tests.mozilla.org": {
       "updates": [
         { "version": "2.0",
-          "update_link": "http://localhost:%PORT%/addons/test_delay_update_complete_webextension_v2.xpi"
+          "update_link": "http://example.com/addons/test_delay_update_complete_webextension_v2.xpi"
         }
       ]
     }
   }
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_delay_updates_complete_legacy.json
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_delay_updates_complete_legacy.json
@@ -5,14 +5,14 @@
         {
           "applications": {
             "gecko": {
               "strict_min_version": "1",
               "advisory_max_version": "1"
             }
           },
           "version": "2.0",
-          "update_link": "http://localhost:%PORT%/addons/test_delay_update_complete_v2.xpi"
+          "update_link": "http://example.com/addons/test_delay_update_complete_v2.xpi"
         }
       ]
     }
   }
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_delay_updates_defer.json
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_delay_updates_defer.json
@@ -1,11 +1,11 @@
 {
   "addons": {
     "test_delay_update_defer_webext@tests.mozilla.org": {
       "updates": [
         { "version": "2.0",
-          "update_link": "http://localhost:%PORT%/addons/test_delay_update_defer_webextension_v2.xpi"
+          "update_link": "http://example.com/addons/test_delay_update_defer_webextension_v2.xpi"
         }
       ]
     }
   }
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_delay_updates_defer_legacy.json
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_delay_updates_defer_legacy.json
@@ -5,14 +5,14 @@
         {
           "applications": {
             "gecko": {
               "strict_min_version": "1",
               "advisory_max_version": "1"
             }
           },
           "version": "2.0",
-          "update_link": "http://localhost:%PORT%/addons/test_delay_update_defer_v2.xpi"
+          "update_link": "http://example.com/addons/test_delay_update_defer_v2.xpi"
         }
       ]
     }
   }
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_delay_updates_ignore.json
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_delay_updates_ignore.json
@@ -1,11 +1,11 @@
 {
   "addons": {
     "test_delay_update_ignore_webext@tests.mozilla.org": {
       "updates": [
         { "version": "2.0",
-          "update_link": "http://localhost:%PORT%/addons/test_delay_update_ignore_webextension_v2.xpi"
+          "update_link": "http://example.com/addons/test_delay_update_ignore_webextension_v2.xpi"
         }
       ]
     }
   }
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_delay_updates_ignore_legacy.json
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_delay_updates_ignore_legacy.json
@@ -5,14 +5,14 @@
         {
           "applications": {
             "gecko": {
               "strict_min_version": "1",
               "advisory_max_version": "1"
             }
           },
           "version": "2.0",
-          "update_link": "http://localhost:%PORT%/addons/test_delay_update_ignore_v2.xpi"
+          "update_link": "http://example.com/addons/test_delay_update_ignore_v2.xpi"
         }
       ]
     }
   }
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_ignore.json
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_ignore.json
@@ -5,14 +5,14 @@
         {
           "applications": {
             "gecko": {
               "strict_min_version": "1",
               "advisory_max_version": "2"
             }
           },
           "version": "2.0",
-          "update_link": "https://localhost:%PORT%/addons/test1.xpi"
+          "update_link": "https://example.com/addons/test1.xpi"
         }
       ]
     }
   }
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_normal.json
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_normal.json
@@ -5,14 +5,14 @@
         {
           "applications": {
             "gecko": {
               "strict_min_version": "1",
               "advisory_max_version": "2"
             }
           },
           "version": "2.0",
-          "update_link": "https://localhost:%PORT%/addons/test1.xpi"
+          "update_link": "https://example.com/addons/test1.xpi"
         }
       ]
     }
   }
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_strict.json
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_updatecompatmode_strict.json
@@ -5,14 +5,14 @@
         {
           "applications": {
             "gecko": {
               "strict_min_version": "1",
               "advisory_max_version": "2"
             }
           },
           "version": "2.0",
-          "update_link": "https://localhost:%PORT%/addons/test1.xpi"
+          "update_link": "https://example.com/addons/test1.xpi"
         }
       ]
     }
   }
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -1,27 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 /* eslint no-unused-vars: ["error", {vars: "local", args: "none"}] */
 
-var AM_Cc = Cc;
-var AM_Ci = Ci;
-var AM_Cu = Cu;
-
-AM_Cu.importGlobalProperties(["TextEncoder"]);
-
 if (!_TEST_FILE[0].includes("toolkit/mozapps/extensions/test/xpcshell/")) {
   ok(false, ("head_addons.js may not be loaded by tests outside of " +
              "the add-on manager component."));
 }
 
-const CERTDB_CONTRACTID = "@mozilla.org/security/x509certdb;1";
-const CERTDB_CID = Components.ID("{fb0bbc5c-452e-4783-b32c-80124693d871}");
+Cu.importGlobalProperties(["TextEncoder"]);
 
 const PREF_EM_CHECK_UPDATE_SECURITY   = "extensions.checkUpdateSecurity";
 const PREF_EM_STRICT_COMPATIBILITY    = "extensions.strictCompatibility";
 const PREF_EM_MIN_COMPAT_APP_VERSION      = "extensions.minCompatibleAppVersion";
 const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
 const PREF_GETADDONS_BYIDS               = "extensions.getAddons.get.url";
 const PREF_COMPAT_OVERRIDES              = "extensions.getAddons.compatOverides.url";
 const PREF_XPI_SIGNATURES_REQUIRED    = "xpinstall.signatures.required";
@@ -38,24 +31,23 @@ const TIMEOUT_MS = 900000;
 // modification times exactly. As long as we are closer than this then it
 // still passes.
 const MAX_TIME_DIFFERENCE = 3000;
 
 // Time to reset file modified time relative to Date.now() so we can test that
 // times are modified (10 hours old).
 const MAKE_FILE_OLD_DIFFERENCE = 10 * 3600 * 1000;
 
-ChromeUtils.import("resource://gre/modules/addons/AddonRepository.jsm");
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
+ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
-const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
-ChromeUtils.import("resource://gre/modules/AsyncShutdown.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/addons/AddonRepository.jsm");
+ChromeUtils.import("resource://gre/modules/osfile.jsm");
 
 ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "Extension",
                                "resource://gre/modules/Extension.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionTestUtils",
                                "resource://testing-common/ExtensionXPCShellUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionTestCommon",
@@ -71,16 +63,17 @@ ChromeUtils.defineModuleGetter(this, "Mo
 
 XPCOMUtils.defineLazyServiceGetter(this, "aomStartup",
                                    "@mozilla.org/addons/addon-manager-startup;1",
                                    "amIAddonManagerStartup");
 
 const {
   awaitPromise,
   createAppInfo,
+  createHttpServer,
   createInstallRDF,
   createTempWebExtensionFile,
   createUpdateRDF,
   getFileForAddon,
   manuallyUninstall,
   overrideBuiltIns,
   promiseAddonEvent,
   promiseCompleteAllInstalls,
@@ -120,17 +113,17 @@ Object.defineProperty(this, "gAppInfo", 
 Object.defineProperty(this, "gAddonStartup", {
   get() {
     return AddonTestUtils.addonStartup.clone();
   },
 });
 
 Object.defineProperty(this, "gInternalManager", {
   get() {
-    return AddonTestUtils.addonIntegrationService.QueryInterface(AM_Ci.nsITimerCallback);
+    return AddonTestUtils.addonIntegrationService.QueryInterface(Ci.nsITimerCallback);
   },
 });
 
 Object.defineProperty(this, "gProfD", {
   get() {
     return AddonTestUtils.profileDir.clone();
   },
 });
@@ -167,31 +160,31 @@ const promiseAddonByID = AddonManager.ge
 const promiseAddonsByIDs = AddonManager.getAddonsByIDs;
 const promiseAddonsWithOperationsByTypes = AddonManager.getAddonsWithOperationsByTypes;
 
 var gPort = null;
 var gUrlToFileMap = {};
 
 // Map resource://xpcshell-data/ to the data directory
 var resHandler = Services.io.getProtocolHandler("resource")
-                         .QueryInterface(AM_Ci.nsISubstitutingProtocolHandler);
+                         .QueryInterface(Ci.nsISubstitutingProtocolHandler);
 // Allow non-existent files because of bug 1207735
 var dataURI = NetUtil.newURI(do_get_file("data", true));
 resHandler.setSubstitution("xpcshell-data", dataURI);
 
 function isManifestRegistered(file) {
   let manifests = Components.manager.getManifestLocations();
   for (let i = 0; i < manifests.length; i++) {
-    let manifest = manifests.queryElementAt(i, AM_Ci.nsIURI);
+    let manifest = manifests.queryElementAt(i, Ci.nsIURI);
 
     // manifest is the url to the manifest file either in an XPI or a directory.
     // We want the location of the XPI or directory itself.
-    if (manifest instanceof AM_Ci.nsIJARURI) {
-      manifest = manifest.JARFile.QueryInterface(AM_Ci.nsIFileURL).file;
-    } else if (manifest instanceof AM_Ci.nsIFileURL) {
+    if (manifest instanceof Ci.nsIJARURI) {
+      manifest = manifest.JARFile.QueryInterface(Ci.nsIFileURL).file;
+    } else if (manifest instanceof Ci.nsIFileURL) {
       manifest = manifest.file.parent;
     } else {
       continue;
     }
 
     if (manifest.equals(file))
       return true;
   }
@@ -476,21 +469,21 @@ function do_get_addon_hash(aName, aAlgor
   let file = do_get_addon(aName);
   return do_get_file_hash(file);
 }
 
 function do_get_file_hash(aFile, aAlgorithm) {
   if (!aAlgorithm)
     aAlgorithm = "sha1";
 
-  let crypto = AM_Cc["@mozilla.org/security/hash;1"].
-               createInstance(AM_Ci.nsICryptoHash);
+  let crypto = Cc["@mozilla.org/security/hash;1"].
+               createInstance(Ci.nsICryptoHash);
   crypto.initWithString(aAlgorithm);
-  let fis = AM_Cc["@mozilla.org/network/file-input-stream;1"].
-            createInstance(AM_Ci.nsIFileInputStream);
+  let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+            createInstance(Ci.nsIFileInputStream);
   fis.init(aFile, -1, -1, false);
   crypto.updateFromStream(fis, aFile.fileSize);
 
   // return the two-digit hexadecimal code for a byte
   let toHexString = charCode => ("0" + charCode.toString(16)).slice(-2);
 
   let binary = crypto.finish(false);
   let hash = Array.from(binary, c => toHexString(c.charCodeAt(0)));
@@ -603,17 +596,17 @@ function do_check_addon(aActualAddon, aE
           do_check_compatibilityoverride(actualValue[i], expectedValue[i]);
         break;
 
       case "icons":
         do_check_icons(actualValue, expectedValue);
         break;
 
       default:
-        if (remove_port(actualValue) !== remove_port(expectedValue))
+        if (actualValue !== expectedValue)
           do_throw("Failed for " + aProperty + " for add-on " + aExpectedAddon.id +
                    " (" + actualValue + " === " + expectedValue + ")");
     }
   });
 }
 
 /**
  * Check that the actual author is the same as the expected author.
@@ -663,17 +656,17 @@ function do_check_compatibilityoverride(
   Assert.equal(aActual.maxVersion, aExpected.maxVersion);
   Assert.equal(aActual.appID, aExpected.appID);
   Assert.equal(aActual.appMinVersion, aExpected.appMinVersion);
   Assert.equal(aActual.appMaxVersion, aExpected.appMaxVersion);
 }
 
 function do_check_icons(aActual, aExpected) {
   for (var size in aExpected) {
-    Assert.equal(remove_port(aActual[size]), remove_port(aExpected[size]));
+    Assert.equal(aActual[size], aExpected[size]);
   }
 }
 
 function startupManager(aAppChanged) {
   promiseStartupManager(aAppChanged);
 }
 
 /**
@@ -773,17 +766,17 @@ function writeInstallRDFToXPI(aData, aDi
   };
   if (typeof aExtraFile === "object")
     Object.assign(files, aExtraFile);
   else
   if (aExtraFile)
     files[aExtraFile] = "";
 
   if (!aDir.exists())
-    aDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+    aDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
 
   var file = aDir.clone();
   file.append(`${aId}.xpi`);
 
   AddonTestUtils.writeFilesToZip(file.path, files);
 
   return file;
 }
@@ -1193,185 +1186,74 @@ function copyBlocklistToProfile(blocklis
 function timeout() {
   timer = null;
   do_throw("Test ran longer than " + TIMEOUT_MS + "ms");
 
   // Attempt to bail out of the test
   do_test_finished();
 }
 
-var timer = AM_Cc["@mozilla.org/timer;1"].createInstance(AM_Ci.nsITimer);
-timer.init(timeout, TIMEOUT_MS, AM_Ci.nsITimer.TYPE_ONE_SHOT);
+var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+timer.init(timeout, TIMEOUT_MS, Ci.nsITimer.TYPE_ONE_SHOT);
 
 // Make sure that a given path does not exist
 function pathShouldntExist(file) {
   if (file.exists()) {
     do_throw(`Test cleanup: path ${file.path} exists when it should not`);
   }
 }
 
 registerCleanupFunction(function addon_cleanup() {
   if (timer)
     timer.cancel();
 });
 
-/**
- * Creates a new HttpServer for testing, and begins listening on the
- * specified port. Automatically shuts down the server when the test
- * unit ends.
- *
- * @param port
- *        The port to listen on. If omitted, listen on a random
- *        port. The latter is the preferred behavior.
- *
- * @return HttpServer
- */
-function createHttpServer(port = -1) {
-  let server = new HttpServer();
-  server.start(port);
-
-  registerCleanupFunction(() => {
-    return new Promise(resolve => {
-      server.stop(resolve);
-    });
-  });
-
-  return server;
-}
-
-/**
- * Handler function that responds with the interpolated
- * static file associated to the URL specified by request.path.
- * This replaces the %PORT% entries in the file with the actual
- * value of the running server's port (stored in gPort).
- */
-function interpolateAndServeFile(request, response) {
-  try {
-    let file = gUrlToFileMap[request.path];
-    var data = "";
-    var fstream = Cc["@mozilla.org/network/file-input-stream;1"].
-    createInstance(Ci.nsIFileInputStream);
-    var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
-    createInstance(Ci.nsIConverterInputStream);
-    fstream.init(file, -1, 0, 0);
-    cstream.init(fstream, "UTF-8", 0, 0);
-
-    let str = {};
-    let read = 0;
-    do {
-      // read as much as we can and put it in str.value
-      read = cstream.readString(0xffffffff, str);
-      data += str.value;
-    } while (read != 0);
-    data = data.replace(/%PORT%/g, gPort);
-
-    response.write(data);
-  } catch (e) {
-    do_throw(`Exception while serving interpolated file: ${e}\n${e.stack}`);
-  } finally {
-    cstream.close(); // this closes fstream as well
-  }
-}
-
-/**
- * Sets up a path handler for the given URL and saves the
- * corresponding file in the global url -> file map.
- *
- * @param  url
- *         the actual URL
- * @param  file
- *         nsIFile representing a static file
- */
-function mapUrlToFile(url, file, server) {
-  server.registerPathHandler(url, interpolateAndServeFile);
-  gUrlToFileMap[url] = file;
-}
-
-function mapFile(path, server) {
-  mapUrlToFile(path, do_get_file(path), server);
-}
-
-/**
- * Take out the port number in an URL
- *
- * @param url
- *        String that represents an URL with a port number in it
- */
-function remove_port(url) {
-  if (typeof url === "string")
-    return url.replace(/:\d+/, "");
-  return url;
-}
 // Wrap a function (typically a callback) to catch and report exceptions
 function do_exception_wrap(func) {
   return function() {
     try {
       func.apply(null, arguments);
     } catch (e) {
       do_report_unexpected_exception(e);
     }
   };
 }
 
 /**
  * Change the schema version of the JSON extensions database
  */
-function changeXPIDBVersion(aNewVersion, aMutator = undefined) {
-  let jData = loadJSON(gExtensionsJSON);
-  jData.schemaVersion = aNewVersion;
-  if (aMutator)
-    aMutator(jData);
-  saveJSON(jData, gExtensionsJSON);
+async function changeXPIDBVersion(aNewVersion) {
+  let json = await loadJSON(gExtensionsJSON.path);
+  json.schemaVersion = aNewVersion;
+  await saveJSON(json, gExtensionsJSON.path);
 }
 
 /**
  * Load a file into a string
  */
-function loadFile(aFile) {
-  let data = "";
-  let fstream = Cc["@mozilla.org/network/file-input-stream;1"].
-          createInstance(Ci.nsIFileInputStream);
-  let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"].
-          createInstance(Ci.nsIConverterInputStream);
-  fstream.init(aFile, -1, 0, 0);
-  cstream.init(fstream, "UTF-8", 0, 0);
-  let str = {};
-  let read = 0;
-  do {
-    read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value
-    data += str.value;
-  } while (read != 0);
-  cstream.close();
-  return data;
+async function loadFile(aFile) {
+  let buffer = await OS.File.read(aFile);
+  return new TextDecoder().decode(buffer);
 }
 
 /**
  * Raw load of a JSON file
  */
-function loadJSON(aFile) {
-  let data = loadFile(aFile);
-  info("Loaded JSON file " + aFile.path);
-  return (JSON.parse(data));
+async function loadJSON(aFile) {
+  let data = await loadFile(aFile);
+  info("Loaded JSON file " + aFile);
+  return JSON.parse(data);
 }
 
 /**
  * Raw save of a JSON blob to file
  */
-function saveJSON(aData, aFile) {
-  info("Starting to save JSON file " + aFile.path);
-  let stream = FileUtils.openSafeFileOutputStream(aFile);
-  let converter = AM_Cc["@mozilla.org/intl/converter-output-stream;1"].
-    createInstance(AM_Ci.nsIConverterOutputStream);
-  converter.init(stream, "UTF-8");
-  // XXX pretty print the JSON while debugging
-  converter.writeString(JSON.stringify(aData, null, 2));
-  converter.flush();
-  // nsConverterOutputStream doesn't finish() safe output streams on close()
-  FileUtils.closeSafeFileOutputStream(stream);
-  converter.close();
+async function saveJSON(aData, aFile) {
+  info("Starting to save JSON file " + aFile);
+  await OS.File.writeAtomic(aFile, new TextEncoder().encode(JSON.stringify(aData, null, 2)));
   info("Done saving JSON file " + aFile.path);
 }
 
 /**
  * Create a callback function that calls do_execute_soon on an actual callback and arguments
  */
 function callback_soon(aFunction) {
   return function(...args) {
@@ -1549,17 +1431,17 @@ async function checkInstalledSystemAddon
 
       // Verify the add-ons file is in the right place
       let file = expectedDir.clone();
       file.append(id + ".xpi");
       Assert.ok(file.exists());
       Assert.ok(file.isFile());
 
       let uri = addon.getResourceURI(null);
-      Assert.ok(uri instanceof AM_Ci.nsIFileURL);
+      Assert.ok(uri instanceof Ci.nsIFileURL);
       Assert.equal(uri.file.path, file.path);
 
       if (isUpgrade) {
         Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_SYSTEM);
       }
 
       // Verify the add-on actually started
       BootstrapMonitor.checkAddonStarted(id, version);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js
@@ -1,23 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Tests AddonRepository.jsm
 
 ChromeUtils.import("resource://gre/modules/addons/AddonRepository.jsm");
 
-var gServer = AddonTestUtils.createHttpServer();
+var gServer = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 
 const PREF_GETADDONS_BROWSEADDONS        = "extensions.getAddons.browseAddons";
 const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";
 
 const PORT          = gServer.identity.primaryPort;
-const BASE_URL      = "http://localhost:" + PORT;
+const BASE_URL      = "http://example.com";
 const DEFAULT_URL   = "about:blank";
 
 const ADDONS = [
   {
     id: "test_AddonRepository_1@tests.mozilla.org",
     version: "1.1",
     bootstrap: true,
 
@@ -166,24 +166,22 @@ add_task(async function setup() {
 
   let xpis = ADDONS.map(addon => createTempXPIFile(addon));
 
   // Register other add-on XPI files
   gServer.registerFile(INSTALL_URL2, xpis[1]);
   gServer.registerFile(INSTALL_URL3, xpis[2]);
 
   // Register files used to test search failure
-  mapUrlToFile(GET_TEST.failedURL,
-               do_get_file("data/test_AddonRepository_fail.json"),
-               gServer);
+  gServer.registerFile(GET_TEST.failedURL,
+                       do_get_file("data/test_AddonRepository_fail.json"));
 
   // Register files used to test search success
-  mapUrlToFile(GET_TEST.successfulURL,
-               do_get_file("data/test_AddonRepository_getAddonsByIDs.json"),
-               gServer);
+  gServer.registerFile(GET_TEST.successfulURL,
+                       do_get_file("data/test_AddonRepository_getAddonsByIDs.json"));
 
   await promiseStartupManager();
 
   // Install an add-on so can check that it isn't returned in the results
   await promiseInstallFile(xpis[0]);
   await promiseRestartManager();
 
   // Create an active AddonInstall so can check that it isn't returned in the results
--- a/toolkit/mozapps/extensions/test/xpcshell/test_ProductAddonChecker.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_ProductAddonChecker.js
@@ -1,15 +1,15 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
 ChromeUtils.import("resource://testing-common/httpd.js");
 ChromeUtils.import("resource://gre/modules/osfile.jsm");
 
-const LocalFile = new Components.Constructor("@mozilla.org/file/local;1", AM_Ci.nsIFile, "initWithPath");
+const LocalFile = new Components.Constructor("@mozilla.org/file/local;1", Ci.nsIFile, "initWithPath");
 
 var testserver = new HttpServer();
 testserver.registerDirectory("/data/", do_get_file("data/productaddons"));
 testserver.start();
 var root = testserver.identity.primaryScheme + "://" +
            testserver.identity.primaryHost + ":" +
            testserver.identity.primaryPort + "/data/";
 
@@ -33,23 +33,23 @@ function compareBinaryData(arr1, arr2) {
 
 /**
  * Reads a file's data and returns it
  *
  * @param file The file to read the data from
  * @return array of bytes for the data in the file.
 */
 function getBinaryFileData(file) {
-  let fileStream = AM_Cc["@mozilla.org/network/file-input-stream;1"].
-                   createInstance(AM_Ci.nsIFileInputStream);
+  let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
+                   createInstance(Ci.nsIFileInputStream);
   // Open as RD_ONLY with default permissions.
   fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
 
-  let stream = AM_Cc["@mozilla.org/binaryinputstream;1"].
-               createInstance(AM_Ci.nsIBinaryInputStream);
+  let stream = Cc["@mozilla.org/binaryinputstream;1"].
+               createInstance(Ci.nsIBinaryInputStream);
   stream.setInputStream(fileStream);
   let bytes = stream.readByteArray(stream.available());
   fileStream.close();
   return bytes;
 }
 
 /**
  * Compares binary data of 2 files and returns true if they are the same
--- a/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_asyncBlocklistLoad.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 add_task(async function() {
-  let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+  let blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
                   getService().wrappedJSObject;
   let scope = ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
 
   // sync -> async. Check that async code doesn't try to read the file
   // once it's already been read synchronously.
   let read = scope.OS.File.read;
   let triedToRead = false;
   scope.OS.File.read = () => triedToRead = true;
--- a/toolkit/mozapps/extensions/test/xpcshell/test_backgroundupdate.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_backgroundupdate.js
@@ -2,39 +2,34 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // This verifies that background updates & notifications work as expected
 
 // The test extension uses an insecure update url.
 Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
 
-ChromeUtils.import("resource://testing-common/httpd.js");
-var testserver = new HttpServer();
-testserver.start(-1);
-gPort = testserver.identity.primaryPort;
+var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
-// register static files with server and interpolate port numbers in them
-mapFile("/data/test_backgroundupdate.json", testserver);
-
 function run_test() {
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
   testserver.registerDirectory("/addons/", do_get_file("addons"));
+  testserver.registerDirectory("/data/", do_get_file("data"));
 
   startupManager();
 
   do_test_pending();
   run_test_1();
 }
 
 function end_test() {
-  testserver.stop(do_test_finished);
+  do_test_finished();
 }
 
 // Verify that with no add-ons installed the background update notifications get
 // called
 function run_test_1() {
   AddonManager.getAddonsByTypes(["extension", "theme", "locale"], function(aAddons) {
     Assert.equal(aAddons.length, 0);
 
@@ -50,30 +45,30 @@ function run_test_1() {
 }
 
 // Verify that with two add-ons installed both of which claim to have updates
 // available we get the notification after both updates attempted to start
 function run_test_2() {
   writeInstallRDFForExtension({
     id: "addon1@tests.mozilla.org",
     version: "1.0",
-    updateURL: "http://localhost:" + gPort + "/data/test_backgroundupdate.json",
+    updateURL: "http://example.com/data/test_backgroundupdate.json",
     bootstrap: true,
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1"
     }],
     name: "Test Addon 1",
   }, profileDir);
 
   writeInstallRDFForExtension({
     id: "addon2@tests.mozilla.org",
     version: "1.0",
-    updateURL: "http://localhost:" + gPort + "/data/test_backgroundupdate.json",
+    updateURL: "http://example.com/data/test_backgroundupdate.json",
     bootstrap: true,
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1"
     }],
     name: "Test Addon 2",
   }, profileDir);
@@ -90,17 +85,17 @@ function run_test_2() {
     name: "Test Addon 3",
   }, profileDir);
 
   // Disable rcwn to make cache behavior deterministic.
   Services.prefs.setBoolPref("network.http.rcwn.enabled", false);
 
   // Background update uses a different pref, if set
   Services.prefs.setCharPref("extensions.update.background.url",
-                             "http://localhost:" + gPort + "/data/test_backgroundupdate.json");
+                             "http://example.com/data/test_backgroundupdate.json");
 
   restartManager();
 
   let installCount = 0;
   let completeCount = 0;
   let sawCompleteNotification = false;
 
   Services.obs.addObserver(function observer() {
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bad_json.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bad_json.js
@@ -15,41 +15,41 @@ var addon1 = {
     minVersion: "1",
     maxVersion: "1"
   }]
 };
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
-function run_test() {
+async function run_test() {
   do_test_pending("Bad JSON");
 
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
 
   // This addon will be auto-installed at startup
   writeInstallRDFForExtension(addon1, profileDir);
 
   startupManager();
 
   shutdownManager();
 
   // First startup/shutdown finished
   // Replace the JSON store with something bogus
-  saveJSON({not: "what we expect to find"}, gExtensionsJSON);
+  await saveJSON({not: "what we expect to find"}, gExtensionsJSON.path);
 
   startupManager(false);
   // Retrieve an addon to force the database to rebuild
   AddonManager.getAddonsByIDs([addon1.id], callback_soon(after_db_rebuild));
 }
 
-function after_db_rebuild([a1]) {
+async function after_db_rebuild([a1]) {
   Assert.equal(a1.id, addon1.id);
 
   shutdownManager();
 
   // Make sure our JSON database has schemaVersion and our installed extension
-  let data = loadJSON(gExtensionsJSON);
+  let data = await loadJSON(gExtensionsJSON.path);
   Assert.ok("schemaVersion" in data);
   Assert.equal(data.addons[0].id, addon1.id);
 
   do_test_finished("Bad JSON");
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_badschema.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_badschema.js
@@ -1,20 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Checks that we rebuild something sensible from a database with a bad schema
 
-var testserver = AddonTestUtils.createHttpServer();
-gPort = testserver.identity.primaryPort;
+var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 
 // register files with server
 testserver.registerDirectory("/addons/", do_get_file("addons"));
-mapFile("/data/test_corrupt.json", testserver);
+testserver.registerDirectory("/data/", do_get_file("data"));
 
 // The test extension uses an insecure update url.
 Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
 Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
 
 const ADDONS = {
   "addon1@tests.mozilla.org": {
     "install.rdf": {
@@ -64,17 +63,17 @@ const ADDONS = {
   },
 
   "addon3@tests.mozilla.org": {
     "install.rdf": {
       id: "addon3@tests.mozilla.org",
       version: "1.0",
       name: "Test 3",
       bootstrap: true,
-      updateURL: "http://localhost:" + gPort + "/data/test_corrupt.json",
+      updateURL: "http://example.com/data/test_corrupt.json",
       targetApplications: [{
         id: "xpcshell@tests.mozilla.org",
         minVersion: "1",
         maxVersion: "1"
       }]
     },
     findUpdates: true,
     desiredValues: {
@@ -88,17 +87,17 @@ const ADDONS = {
   },
 
   "addon4@tests.mozilla.org": {
     "install.rdf": {
       id: "addon4@tests.mozilla.org",
       version: "1.0",
       name: "Test 4",
       bootstrap: true,
-      updateURL: "http://localhost:" + gPort + "/data/test_corrupt.json",
+      updateURL: "http://example.com/data/test_corrupt.json",
       targetApplications: [{
         id: "xpcshell@tests.mozilla.org",
         minVersion: "1",
         maxVersion: "1"
       }]
     },
     initialState: {
       userDisabled: true,
@@ -278,17 +277,17 @@ add_task(async function test_after_resta
   }
 
   await promiseShutdownManager();
 });
 
 add_task(async function test_after_schema_version_change() {
   // After restarting the database won't be open so we can alter
   // the schema
-  changeXPIDBVersion(100);
+  await changeXPIDBVersion(100);
 
   await promiseStartupManager(false);
 
   info("Test add-on state after schema version change");
   let addons = await getAddons(IDS);
   for (let [id, addon] of Object.entries(ADDONS)) {
     checkAddon(id, addons.get(id),
                Object.assign({}, addon.desiredValues, addon.afterCorruption));
--- a/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_metadata_filters.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_metadata_filters.js
@@ -2,24 +2,21 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Tests blocking of extensions by ID, name, creator, homepageURL, updateURL
 // and RegExps for each. See bug 897735.
 
 const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
 
-ChromeUtils.import("resource://testing-common/httpd.js");
 ChromeUtils.import("resource://testing-common/MockRegistrar.jsm");
-var testserver = new HttpServer();
-testserver.start(-1);
+var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 gPort = testserver.identity.primaryPort;
 
-// register static files with server and interpolate port numbers in them
-mapFile("/data/test_blocklist_metadata_filters_1.xml", testserver);
+testserver.registerDirectory("/data/", do_get_file("data"));
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
 // Don't need the full interface, attempts to call other methods will just
 // throw which is just fine
 var WindowWatcher = {
   openWindow(parent, url, name, features, args) {
@@ -60,17 +57,17 @@ function load_blocklist(aFile, aCallback
                              gPort + "/data/" + aFile);
   var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
                   getService(Ci.nsITimerCallback);
   blocklist.notify(null);
 }
 
 
 function end_test() {
-  testserver.stop(do_test_finished);
+  do_test_finished();
 }
 
 function run_test() {
   do_test_pending();
 
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
 
   // Should get blocked by name
--- a/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_prefs.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_prefs.js
@@ -2,24 +2,21 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Tests resetting of preferences in blocklist entry when an add-on is blocked.
 // See bug 802434.
 
 const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
 
-ChromeUtils.import("resource://testing-common/httpd.js");
 ChromeUtils.import("resource://testing-common/MockRegistrar.jsm");
-var testserver = new HttpServer();
-testserver.start(-1);
+var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 gPort = testserver.identity.primaryPort;
 
-// register static files with server and interpolate port numbers in them
-mapFile("/data/test_blocklist_prefs_1.xml", testserver);
+testserver.registerDirectory("/data/", do_get_file("data"));
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
 // A window watcher to handle the blocklist UI.
 // Don't need the full interface, attempts to call other methods will just
 // throw which is just fine
 var WindowWatcher = {
@@ -60,17 +57,17 @@ function load_blocklist(aFile, aCallback
   Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
                              gPort + "/data/" + aFile);
   var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
                   getService(Ci.nsITimerCallback);
   blocklist.notify(null);
 }
 
 function end_test() {
-  testserver.stop(do_test_finished);
+  do_test_finished();
 }
 
 function run_test() {
   do_test_pending();
 
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
 
   // Add 2 extensions
--- a/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_regexp.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_regexp.js
@@ -3,24 +3,21 @@
  */
 
 // Checks that blocklist entries using RegExp work as expected. This only covers
 // behavior specific to RegExp entries - general behavior is already tested
 // in test_blocklistchange.js.
 
 const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
 
-ChromeUtils.import("resource://testing-common/httpd.js");
 ChromeUtils.import("resource://testing-common/MockRegistrar.jsm");
-var testserver = new HttpServer();
-testserver.start(-1);
+var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
 gPort = testserver.identity.primaryPort;
 
-// register static files with server and interpolate port numbers in them
-mapFile("/data/test_blocklist_regexp_1.xml", testserver);
+testserver.registerDirectory("/data/", do_get_file("data"));
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 
 // Don't need the full interface, attempts to call other methods will just
 // throw which is just fine
 var WindowWatcher = {
   openWindow(parent, url, name, features, args) {
@@ -64,17 +61,17 @@ function load_blocklist(aFile, aCallback
                   getService(Ci.nsITimerCallback);
   ok(Services.prefs.getBoolPref("services.blocklist.update_enabled"),
                                 "Kinto update should be enabled");
   blocklist.notify(null);
 }
 
 
 function end_test() {
-  testserver.stop(do_test_finished);
+  do_test_finished();
 }
 
 
 function run_test() {
   do_test_pending();
 
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_blocklistchange.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklistchange.js
@@ -27,17 +27,16 @@ const URI_EXTENSION_BLOCKLIST_DIALOG = "
 
 ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
 ChromeUtils.import("resource://testing-common/MockRegistrar.jsm");
 
 // Allow insecure updates
 Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
 
 var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
-gPort = testserver.identity.primaryPort;
 
 testserver.registerDirectory("/data/", do_get_file("data"));
 
 const ADDONS = {
   "blocklist_hard1_1": {
     id: "hardblock@tests.mozilla.org",
     version: "1.0",
     name: "Hardblocked add-on",
@@ -625,17 +624,17 @@ function Pbackground_update() {
 }
 
 // Manually updates the test add-ons to the given version
 function Pmanual_update(aVersion) {
   let Pinstalls = [];
   for (let name of ["soft1", "soft2", "soft3", "soft4", "hard1", "regexp1"]) {
     Pinstalls.push(
       AddonManager.getInstallForURL(
-        `http://localhost:${gPort}/addons/blocklist_${name}_${aVersion}.xpi`,
+        `http://example.com/addons/blocklist_${name}_${aVersion}.xpi`,
         null, "application/x-xpinstall"));
   }
 
   return Promise.all(Pinstalls).then(installs => {
     let completePromises = [];
     for (let install of installs) {
       completePromises.push(new Promise(resolve => {
         install.addListener({
@@ -798,17 +797,17 @@ add_task(async function run_app_update_s
   check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
 });
 
 add_task(async function update_schema_2() {
   await promiseShutdownManager();
 
-  changeXPIDBVersion(100);
+  await changeXPIDBVersion(100);
   gAppInfo.version = "2";
   startupManager(true);
 
   let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
 
   check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
@@ -822,50 +821,50 @@ add_task(async function update_schema_2(
   s3.userDisabled = false;
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
 });
 
 add_task(async function update_schema_3() {
   await promiseRestartManager();
 
   await promiseShutdownManager();
-  changeXPIDBVersion(100);
+  await changeXPIDBVersion(100);
   gAppInfo.version = "2.5";
   startupManager(true);
 
   let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
 
   check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
 });
 
 add_task(async function update_schema_4() {
   await promiseShutdownManager();
 
-  changeXPIDBVersion(100);
+  await changeXPIDBVersion(100);
   startupManager(false);
 
   let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
 
   check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
   check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
   check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
 });
 
 add_task(async function update_schema_5() {
   await promiseShutdownManager();
 
-  changeXPIDBVersion(100);
+  await changeXPIDBVersion(100);
   gAppInfo.version = "1";
   startupManager(true);
 
   let [s1, s2, s3, s4, h, r] = await promiseAddonsByIDs(ADDON_IDS);
 
   check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
   check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap.js
@@ -27,22 +27,106 @@ createAppInfo("xpcshell@tests.mozilla.or
 
 const profileDir = gProfD.clone();
 profileDir.append("extensions");
 const userExtDir = gProfD.clone();
 userExtDir.append("extensions2");
 userExtDir.append(gAppInfo.ID);
 registerDirectory("XREUSysExt", userExtDir.parent);
 
-ChromeUtils.import("resource://testing-common/httpd.js");
-var testserver = new HttpServer();
-testserver.start(undefined);
-gPort = testserver.identity.primaryPort;
+
+const BOOTSTRAP = `
+  ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
+`;
+
+const ADDONS = {
+  test_bootstrap1_1: {
+    "install.rdf": {
+      id: "bootstrap1@tests.mozilla.org",
+      version: "1.0",
+      bootstrap: "true",
+      multiprocessCompatible: "true",
+
+      name: "Test Bootstrap 1",
+      description: "Test Description",
+
+      iconURL: "chrome://foo/skin/icon.png",
+      aboutURL: "chrome://foo/content/about.xul",
+      optionsURL: "chrome://foo/content/options.xul",
+
+      targetApplications: [{
+        id: "xpcshell@tests.mozilla.org",
+        minVersion: "1",
+        maxVersion: "1"}],
+    },
+    "bootstrap.js": BOOTSTRAP,
+  },
+  test_bootstrap1_2: {
+    "install.rdf": {
+      id: "bootstrap1@tests.mozilla.org",
+      version: "2.0",
+      bootstrap: "true",
+
+      name: "Test Bootstrap 1",
+      description: "Test Description",
 
-testserver.registerDirectory("/addons/", do_get_file("addons"));
+      targetApplications: [{
+        id: "xpcshell@tests.mozilla.org",
+        minVersion: "1",
+        maxVersion: "1"}],
+    },
+    "bootstrap.js": BOOTSTRAP,
+  },
+  test_bootstrap1_3: {
+    "install.rdf": {
+      id: "bootstrap1@tests.mozilla.org",
+      version: "3.0",
+      bootstrap: "true",
+
+      name: "Test Bootstrap 1",
+      description: "Test Description",
+
+      targetApplications: [{
+        id: "undefined",
+        minVersion: "1",
+        maxVersion: "1"}],
+    },
+    "bootstrap.js": BOOTSTRAP,
+  },
+  test_bootstrap2_1: {
+    "install.rdf": {
+      id: "bootstrap2@tests.mozilla.org",
+      version: "1.0",
+      bootstrap: "true",
+      multiprocessCompatible: "true",
+
+      name: "Test Bootstrap 2",
+      description: "Test Description",
+
+      iconURL: "chrome://foo/skin/icon.png",
+      aboutURL: "chrome://foo/content/about.xul",
+      optionsURL: "chrome://foo/content/options.xul",
+
+      targetApplications: [{
+        id: "xpcshell@tests.mozilla.org",
+        minVersion: "1",
+        maxVersion: "1"}],
+    },
+    "bootstrap.js": BOOTSTRAP,
+  },
+};
+
+var testserver = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
+
+const XPIS = {};
+for (let [name, addon] of Object.entries(ADDONS)) {
+  XPIS[name] = AddonTestUtils.createTempXPIFile(addon);
+  testserver.registerFile(`/addons/${name}.xpi`, XPIS[name]);
+}
+
 
 function getStartupReason() {
   let info = BootstrapMonitor.started.get(ID1);
   return info ? info.reason : undefined;
 }
 
 function getShutdownReason() {
   let info = BootstrapMonitor.stopped.get(ID1);
@@ -74,1337 +158,1096 @@ function getInstallOldVersion() {
   return info ? info.data.oldVersion : undefined;
 }
 
 function getUninstallNewVersion() {
   let info = BootstrapMonitor.uninstalled.get(ID1);
   return info ? info.data.newVersion : undefined;
 }
 
-function do_check_bootstrappedPref(aCallback) {
-  let XPIScope = AM_Cu.import("resource://gre/modules/addons/XPIProvider.jsm", {});
+async function checkBootstrappedPref() {
+  let XPIScope = ChromeUtils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
 
-  let data = {};
+  let data = new Map();
   for (let entry of XPIScope.XPIStates.bootstrappedAddons()) {
-    data[entry.id] = entry;
+    data.set(entry.id, entry);
   }
 
-  AddonManager.getAddonsByTypes(["extension"], function(aAddons) {
-    for (let addon of aAddons) {
-      if (!addon.id.endsWith("@tests.mozilla.org"))
-        continue;
-      if (!addon.isActive)
-        continue;
-      if (addon.operationsRequiringRestart != AddonManager.OP_NEEDS_RESTART_NONE)
-        continue;
+  let addons = await AddonManager.getAddonsByTypes(["extension"]);
+  for (let addon of addons) {
+    if (!addon.id.endsWith("@tests.mozilla.org"))
+      continue;
+    if (!addon.isActive)
+      continue;
+    if (addon.operationsRequiringRestart != AddonManager.OP_NEEDS_RESTART_NONE)
+      continue;
 
-      Assert.ok(addon.id in data);
-      let addonData = data[addon.id];
-      delete data[addon.id];
+    ok(data.has(addon.id));
+    let addonData = data.get(addon.id);
+    data.delete(addon.id);
 
-      Assert.equal(addonData.version, addon.version);
-      Assert.equal(addonData.type, addon.type);
-      let file = addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file;
-      Assert.equal(addonData.path, file.path);
-    }
-    Assert.equal(Object.keys(data).length, 0);
-
-    executeSoon(aCallback);
-  });
+    equal(addonData.version, addon.version);
+    equal(addonData.type, addon.type);
+    let file = addon.getResourceURI().QueryInterface(Ci.nsIFileURL).file;
+    equal(addonData.path, file.path);
+  }
+  equal(data.size, 0);
 }
 
 
-function run_test() {
-  do_test_pending();
-
-  startupManager();
+add_task(async function run_test() {
+  promiseStartupManager();
 
-  Assert.ok(!gExtensionsJSON.exists());
-
-  Assert.ok(!gAddonStartup.exists());
-
-  run_test_1();
-}
+  ok(!gExtensionsJSON.exists());
+  ok(!gAddonStartup.exists());
+});
 
 // Tests that installing doesn't require a restart
-function run_test_1() {
-  prepare_test({ }, [
-    "onNewInstall"
-  ]);
-
-  AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), function(install) {
-    ensure_test_completed();
-
-    Assert.notEqual(install, null);
-    Assert.equal(install.type, "extension");
-    Assert.equal(install.version, "1.0");
-    Assert.equal(install.name, "Test Bootstrap 1");
-    Assert.equal(install.state, AddonManager.STATE_DOWNLOADED);
-    Assert.notEqual(install.addon.syncGUID, null);
-    Assert.ok(install.addon.hasResource("install.rdf"));
-    Assert.ok(install.addon.hasResource("bootstrap.js"));
-    Assert.ok(!install.addon.hasResource("foo.bar"));
-    Assert.equal(install.addon.operationsRequiringRestart &
-                 AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
-    do_check_not_in_crash_annotation(ID1, "1.0");
-
-    let addon = install.addon;
-
-    BootstrapMonitor.promiseAddonStartup(ID1).then(function() {
-      do_check_bootstrappedPref(function() {
-        check_test_1(addon.syncGUID);
-      });
-    });
-
-    prepare_test({
-      [ID1]: [
-        ["onInstalling", false],
-        "onInstalled"
-      ]
-    }, [
-      "onInstallStarted",
-      "onInstallEnded",
-    ], function() {
-      Assert.ok(addon.hasResource("install.rdf"));
-
-      // startup should not have been called yet.
-      BootstrapMonitor.checkAddonNotStarted(ID1);
-    });
-    install.install();
-  });
-}
-
-function check_test_1(installSyncGUID) {
-  AddonManager.getAllInstalls(function(installs) {
-    // There should be no active installs now since the install completed and
-    // doesn't require a restart.
-    Assert.equal(installs.length, 0);
-
-    AddonManager.getAddonByID(ID1, function(b1) {
-      Assert.notEqual(b1, null);
-      Assert.equal(b1.version, "1.0");
-      Assert.notEqual(b1.syncGUID, null);
-      Assert.equal(b1.syncGUID, installSyncGUID);
-      Assert.ok(!b1.appDisabled);
-      Assert.ok(!b1.userDisabled);
-      Assert.ok(b1.isActive);
-      Assert.ok(!b1.isSystem);
-      BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-      BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-      Assert.equal(getStartupReason(), ADDON_INSTALL);
-      Assert.equal(getStartupOldVersion(), undefined);
-      Assert.ok(b1.hasResource("install.rdf"));
-      Assert.ok(b1.hasResource("bootstrap.js"));
-      Assert.ok(!b1.hasResource("foo.bar"));
-      do_check_in_crash_annotation(ID1, "1.0");
-
-      let dir = do_get_addon_root_uri(profileDir, ID1);
-      Assert.equal(b1.getResourceURI("bootstrap.js").spec, dir + "bootstrap.js");
-
-      AddonManager.getAddonsWithOperationsByTypes(null, function(list) {
-        Assert.equal(list.length, 0);
-
-        executeSoon(run_test_2);
-      });
-    });
-  });
-}
-
-// Tests that disabling doesn't require a restart
-function run_test_2() {
-  AddonManager.getAddonByID(ID1, function(b1) {
-    prepare_test({
-      [ID1]: [
-        ["onDisabling", false],
-        "onDisabled"
-      ]
-    });
-
-    Assert.equal(b1.operationsRequiringRestart &
-                 AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
-    b1.userDisabled = true;
-    ensure_test_completed();
-
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "1.0");
-    Assert.ok(!b1.appDisabled);
-    Assert.ok(b1.userDisabled);
-    Assert.ok(!b1.isActive);
-    BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-    BootstrapMonitor.checkAddonNotStarted(ID1);
-    Assert.equal(getShutdownReason(), ADDON_DISABLE);
-    Assert.equal(getShutdownNewVersion(), undefined);
-    do_check_not_in_crash_annotation(ID1, "1.0");
-
-    AddonManager.getAddonByID(ID1, function(newb1) {
-      Assert.notEqual(newb1, null);
-      Assert.equal(newb1.version, "1.0");
-      Assert.ok(!newb1.appDisabled);
-      Assert.ok(newb1.userDisabled);
-      Assert.ok(!newb1.isActive);
-
-      do_check_bootstrappedPref(run_test_3);
-    });
-  });
-}
-
-// Test that restarting doesn't accidentally re-enable
-function run_test_3() {
-  shutdownManager();
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonNotStarted(ID1);
-  Assert.equal(getShutdownReason(), ADDON_DISABLE);
-  Assert.equal(getShutdownNewVersion(), undefined);
-  startupManager(false);
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonNotStarted(ID1);
-  Assert.equal(getShutdownReason(), ADDON_DISABLE);
-  Assert.equal(getShutdownNewVersion(), undefined);
-  do_check_not_in_crash_annotation(ID1, "1.0");
-
-  Assert.ok(gAddonStartup.exists());
-
-  AddonManager.getAddonByID(ID1, function(b1) {
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "1.0");
-    Assert.ok(!b1.appDisabled);
-    Assert.ok(b1.userDisabled);
-    Assert.ok(!b1.isActive);
-
-    do_check_bootstrappedPref(run_test_4);
-  });
-}
-
-// Tests that enabling doesn't require a restart
-function run_test_4() {
-  AddonManager.getAddonByID(ID1, function(b1) {
-    prepare_test({
-      [ID1]: [
-        ["onEnabling", false],
-        "onEnabled"
-      ]
-    });
-
-    Assert.equal(b1.operationsRequiringRestart &
-                 AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
-    b1.userDisabled = false;
-    ensure_test_completed();
-
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "1.0");
-    Assert.ok(!b1.appDisabled);
-    Assert.ok(!b1.userDisabled);
-    Assert.ok(b1.isActive);
-    Assert.ok(!b1.isSystem);
-    BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-    BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-    Assert.equal(getStartupReason(), ADDON_ENABLE);
-    Assert.equal(getStartupOldVersion(), undefined);
-    do_check_in_crash_annotation(ID1, "1.0");
-
-    AddonManager.getAddonByID(ID1, function(newb1) {
-      Assert.notEqual(newb1, null);
-      Assert.equal(newb1.version, "1.0");
-      Assert.ok(!newb1.appDisabled);
-      Assert.ok(!newb1.userDisabled);
-      Assert.ok(newb1.isActive);
-
-      do_check_bootstrappedPref(run_test_5);
-    });
-  });
-}
-
-// Tests that a restart shuts down and restarts the add-on
-function run_test_5() {
-  shutdownManager();
-  // By the time we've shut down, the database must have been written
-  Assert.ok(gExtensionsJSON.exists());
-
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonNotStarted(ID1);
-  Assert.equal(getShutdownReason(), APP_SHUTDOWN);
-  Assert.equal(getShutdownNewVersion(), undefined);
-  do_check_not_in_crash_annotation(ID1, "1.0");
-  startupManager(false);
-  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-  Assert.equal(getStartupReason(), APP_STARTUP);
-  Assert.equal(getStartupOldVersion(), undefined);
-  do_check_in_crash_annotation(ID1, "1.0");
-
-  AddonManager.getAddonByID(ID1, function(b1) {
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "1.0");
-    Assert.ok(!b1.appDisabled);
-    Assert.ok(!b1.userDisabled);
-    Assert.ok(b1.isActive);
-    Assert.ok(!b1.isSystem);
-    Assert.ok(!isExtensionInAddonsList(profileDir, b1.id));
-
-    do_check_bootstrappedPref(run_test_6);
-  });
-}
-
-// Tests that installing an upgrade doesn't require a restart
-function run_test_6() {
-  prepare_test({ }, [
-    "onNewInstall"
-  ]);
-
-  AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), function(install) {
-    ensure_test_completed();
-
-    Assert.notEqual(install, null);
-    Assert.equal(install.type, "extension");
-    Assert.equal(install.version, "2.0");
-    Assert.equal(install.name, "Test Bootstrap 1");
-    Assert.equal(install.state, AddonManager.STATE_DOWNLOADED);
-
-    BootstrapMonitor.promiseAddonStartup(ID1).then(check_test_6);
-    prepare_test({
-      [ID1]: [
-        ["onInstalling", false],
-        "onInstalled"
-      ]
-    }, [
-      "onInstallStarted",
-      "onInstallEnded",
-    ], function() {
-    });
-    install.install();
-  });
-}
-
-function check_test_6() {
-  AddonManager.getAddonByID(ID1, function(b1) {
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "2.0");
-    Assert.ok(!b1.appDisabled);
-    Assert.ok(!b1.userDisabled);
-    Assert.ok(b1.isActive);
-    Assert.ok(!b1.isSystem);
-    BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
-    BootstrapMonitor.checkAddonStarted(ID1, "2.0");
-    Assert.equal(getStartupReason(), ADDON_UPGRADE);
-    Assert.equal(getInstallOldVersion(), 1);
-    Assert.equal(getStartupOldVersion(), 1);
-    Assert.equal(getShutdownReason(), ADDON_UPGRADE);
-    Assert.equal(getShutdownNewVersion(), 2);
-    Assert.equal(getUninstallNewVersion(), 2);
-    do_check_not_in_crash_annotation(ID1, "1.0");
-    do_check_in_crash_annotation(ID1, "2.0");
-
-    do_check_bootstrappedPref(run_test_7);
-  });
-}
-
-// Tests that uninstalling doesn't require a restart
-function run_test_7() {
-  AddonManager.getAddonByID(ID1, function(b1) {
-    prepare_test({
-      [ID1]: [
-        ["onUninstalling", false],
-        "onUninstalled"
-      ]
-    });
-
-    Assert.equal(b1.operationsRequiringRestart &
-                 AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
-    b1.uninstall();
-
-    do_check_bootstrappedPref(check_test_7);
-  });
-}
-
-function check_test_7() {
-  ensure_test_completed();
-  BootstrapMonitor.checkAddonNotInstalled(ID1);
-  BootstrapMonitor.checkAddonNotStarted(ID1);
-  Assert.equal(getShutdownReason(), ADDON_UNINSTALL);
-  Assert.equal(getShutdownNewVersion(), undefined);
-  do_check_not_in_crash_annotation(ID1, "2.0");
-
-  AddonManager.getAddonByID(ID1, callback_soon(function(b1) {
-    Assert.equal(b1, null);
-
-    restartManager();
-
-    AddonManager.getAddonByID(ID1, function(newb1) {
-      Assert.equal(newb1, null);
-
-      do_check_bootstrappedPref(run_test_8);
-    });
-  }));
-}
-
-// Test that a bootstrapped extension dropped into the profile loads properly
-// on startup and doesn't cause an EM restart
-function run_test_8() {
-  shutdownManager();
-
-  manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir,
-                  ID1);
-
-  startupManager(false);
-
-  AddonManager.getAddonByID(ID1, function(b1) {
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "1.0");
-    Assert.ok(!b1.appDisabled);
-    Assert.ok(!b1.userDisabled);
-    Assert.ok(b1.isActive);
-    Assert.ok(!b1.isSystem);
-    BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-    BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-    Assert.equal(getStartupReason(), ADDON_INSTALL);
-    Assert.equal(getStartupOldVersion(), undefined);
-    do_check_in_crash_annotation(ID1, "1.0");
-
-    do_check_bootstrappedPref(run_test_9);
-  });
-}
-
-// Test that items detected as removed during startup get removed properly
-function run_test_9() {
-  shutdownManager();
-
-  manuallyUninstall(profileDir, ID1);
-  BootstrapMonitor.clear(ID1);
-
-  startupManager(false);
-
-  AddonManager.getAddonByID(ID1, function(b1) {
-    Assert.equal(b1, null);
-    do_check_not_in_crash_annotation(ID1, "1.0");
-
-    do_check_bootstrappedPref(run_test_10);
-  });
-}
-
-
-// Tests that installing a downgrade sends the right reason
-function run_test_10() {
-  prepare_test({ }, [
+add_task(async function test_1() {
+  prepare_test({}, [
     "onNewInstall"
   ]);
 
-  AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), function(install) {
-    ensure_test_completed();
-
-    Assert.notEqual(install, null);
-    Assert.equal(install.type, "extension");
-    Assert.equal(install.version, "2.0");
-    Assert.equal(install.name, "Test Bootstrap 1");
-    Assert.equal(install.state, AddonManager.STATE_DOWNLOADED);
-    Assert.ok(install.addon.hasResource("install.rdf"));
-    Assert.ok(install.addon.hasResource("bootstrap.js"));
-    Assert.ok(!install.addon.hasResource("foo.bar"));
-    do_check_not_in_crash_annotation(ID1, "2.0");
-
-    BootstrapMonitor.promiseAddonStartup(ID1).then(check_test_10_pt1);
-    prepare_test({
-      [ID1]: [
-        ["onInstalling", false],
-        "onInstalled"
-      ]
-    }, [
-      "onInstallStarted",
-      "onInstallEnded",
-    ], function() {
-      info("Waiting for startup of bootstrap1_2");
-    });
-    install.install();
-  });
-}
-
-function check_test_10_pt1() {
-  AddonManager.getAddonByID(ID1, function(b1) {
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "2.0");
-    Assert.ok(!b1.appDisabled);
-    Assert.ok(!b1.userDisabled);
-    Assert.ok(b1.isActive);
-    Assert.ok(!b1.isSystem);
-    BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
-    BootstrapMonitor.checkAddonStarted(ID1, "2.0");
-    Assert.equal(getStartupReason(), ADDON_INSTALL);
-    Assert.equal(getStartupOldVersion(), undefined);
-    Assert.ok(b1.hasResource("install.rdf"));
-    Assert.ok(b1.hasResource("bootstrap.js"));
-    Assert.ok(!b1.hasResource("foo.bar"));
-    do_check_in_crash_annotation(ID1, "2.0");
-
-    prepare_test({ }, [
-      "onNewInstall"
-    ]);
-
-    AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), function(install) {
-      ensure_test_completed();
-
-      Assert.notEqual(install, null);
-      Assert.equal(install.type, "extension");
-      Assert.equal(install.version, "1.0");
-      Assert.equal(install.name, "Test Bootstrap 1");
-      Assert.equal(install.state, AddonManager.STATE_DOWNLOADED);
+  let install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_1);
+  ensure_test_completed();
 
-      BootstrapMonitor.promiseAddonStartup(ID1).then(check_test_10_pt2);
-      prepare_test({
-        [ID1]: [
-          ["onInstalling", false],
-          "onInstalled"
-        ]
-      }, [
-        "onInstallStarted",
-        "onInstallEnded",
-      ], function() { });
-      install.install();
-    });
-  });
-}
-
-function check_test_10_pt2() {
-  AddonManager.getAddonByID(ID1, function(b1) {
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "1.0");
-    Assert.ok(!b1.appDisabled);
-    Assert.ok(!b1.userDisabled);
-    Assert.ok(b1.isActive);
-    Assert.ok(!b1.isSystem);
-    BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-    BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-    Assert.equal(getStartupReason(), ADDON_DOWNGRADE);
-    Assert.equal(getInstallOldVersion(), 2);
-    Assert.equal(getStartupOldVersion(), 2);
-    Assert.equal(getShutdownReason(), ADDON_DOWNGRADE);
-    Assert.equal(getShutdownNewVersion(), 1);
-    Assert.equal(getUninstallNewVersion(), 1);
-    do_check_in_crash_annotation(ID1, "1.0");
-    do_check_not_in_crash_annotation(ID1, "2.0");
-
-    do_check_bootstrappedPref(run_test_11);
-  });
-}
-
-// Tests that uninstalling a disabled add-on still calls the uninstall method
-function run_test_11() {
-  AddonManager.getAddonByID(ID1, function(b1) {
-    prepare_test({
-      [ID1]: [
-        ["onDisabling", false],
-        "onDisabled",
-        ["onUninstalling", false],
-        "onUninstalled"
-      ]
-    });
-
-    b1.userDisabled = true;
-
-    BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-    BootstrapMonitor.checkAddonNotStarted(ID1);
-    Assert.equal(getShutdownReason(), ADDON_DISABLE);
-    Assert.equal(getShutdownNewVersion(), undefined);
-    do_check_not_in_crash_annotation(ID1, "1.0");
-
-    b1.uninstall();
-
-    check_test_11();
-  });
-}
-
-function check_test_11() {
-  ensure_test_completed();
-  BootstrapMonitor.checkAddonNotInstalled(ID1);
-  BootstrapMonitor.checkAddonNotStarted(ID1);
+  notEqual(install, null);
+  equal(install.type, "extension");
+  equal(install.version, "1.0");
+  equal(install.name, "Test Bootstrap 1");
+  equal(install.state, AddonManager.STATE_DOWNLOADED);
+  notEqual(install.addon.syncGUID, null);
+  ok(install.addon.hasResource("install.rdf"));
+  ok(install.addon.hasResource("bootstrap.js"));
+  ok(!install.addon.hasResource("foo.bar"));
+  equal(install.addon.operationsRequiringRestart &
+               AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
   do_check_not_in_crash_annotation(ID1, "1.0");
 
-  do_check_bootstrappedPref(run_test_12);
-}
-
-// Tests that bootstrapped extensions are correctly loaded even if the app is
-// upgraded at the same time
-function run_test_12() {
-  shutdownManager();
-
-  manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir,
-                  ID1);
-
-  startupManager(true);
-
-  AddonManager.getAddonByID(ID1, function(b1) {
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "1.0");
-    Assert.ok(!b1.appDisabled);
-    Assert.ok(!b1.userDisabled);
-    Assert.ok(b1.isActive);
-    Assert.ok(!b1.isSystem);
-    BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-    BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-    Assert.equal(getStartupReason(), ADDON_INSTALL);
-    Assert.equal(getStartupOldVersion(), undefined);
-    do_check_in_crash_annotation(ID1, "1.0");
-
-    b1.uninstall();
-    executeSoon(test_12_restart);
-  });
-}
-
-function test_12_restart() {
-  restartManager();
-  do_check_bootstrappedPref(run_test_13);
-}
-
-
-// Tests that installing a bootstrapped extension with an invalid application
-// entry doesn't call it's startup method
-function run_test_13() {
-  prepare_test({ }, [
-    "onNewInstall"
-  ]);
-
-  AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_3"), function(install) {
-    ensure_test_completed();
-
-    Assert.notEqual(install, null);
-    Assert.equal(install.type, "extension");
-    Assert.equal(install.version, "3.0");
-    Assert.equal(install.name, "Test Bootstrap 1");
-    Assert.equal(install.state, AddonManager.STATE_DOWNLOADED);
-    do_check_not_in_crash_annotation(ID1, "3.0");
-
-    prepare_test({
-      [ID1]: [
-        ["onInstalling", false],
-        "onInstalled"
-      ]
-    }, [
-      "onInstallStarted",
-      "onInstallEnded",
-    ], callback_soon(check_test_13));
-    install.install();
-  });
-}
-
-function check_test_13() {
-  AddonManager.getAllInstalls(function(installs) {
-    // There should be no active installs now since the install completed and
-    // doesn't require a restart.
-    Assert.equal(installs.length, 0);
-
-    AddonManager.getAddonByID(ID1, function(b1) {
-      Assert.notEqual(b1, null);
-      Assert.equal(b1.version, "3.0");
-      Assert.ok(b1.appDisabled);
-      Assert.ok(!b1.userDisabled);
-      Assert.ok(!b1.isActive);
-      BootstrapMonitor.checkAddonInstalled(ID1, "3.0"); // We call install even for disabled add-ons
-      BootstrapMonitor.checkAddonNotStarted(ID1); // Should not have called startup though
-      do_check_not_in_crash_annotation(ID1, "3.0");
-
-      executeSoon(test_13_restart);
-    });
-  });
-}
-
-function test_13_restart() {
-  restartManager();
-
-  AddonManager.getAddonByID(ID1, function(b1) {
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "3.0");
-    Assert.ok(b1.appDisabled);
-    Assert.ok(!b1.userDisabled);
-    Assert.ok(!b1.isActive);
-    BootstrapMonitor.checkAddonInstalled(ID1, "3.0"); // We call install even for disabled add-ons
-    BootstrapMonitor.checkAddonNotStarted(ID1); // Should not have called startup though
-    do_check_not_in_crash_annotation(ID1, "3.0");
-
-    do_check_bootstrappedPref(function() {
-      b1.uninstall();
-      executeSoon(run_test_14);
-    });
-  });
-}
-
-// Tests that a bootstrapped extension with an invalid target application entry
-// does not get loaded when detected during startup
-function run_test_14() {
-  restartManager();
-
-  shutdownManager();
-
-  manuallyInstall(do_get_addon("test_bootstrap1_3"), profileDir,
-                  ID1);
-
-  startupManager(false);
-
-  AddonManager.getAddonByID(ID1, function(b1) {
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "3.0");
-    Assert.ok(b1.appDisabled);
-    Assert.ok(!b1.userDisabled);
-    Assert.ok(!b1.isActive);
-    BootstrapMonitor.checkAddonInstalled(ID1, "3.0"); // We call install even for disabled add-ons
-    BootstrapMonitor.checkAddonNotStarted(ID1); // Should not have called startup though
-    do_check_not_in_crash_annotation(ID1, "3.0");
-
-    do_check_bootstrappedPref(function() {
-      b1.uninstall();
-
-      run_test_15();
-    });
-  });
-}
-
-// Tests that upgrading a disabled bootstrapped extension still calls uninstall
-// and install but doesn't startup the new version
-function run_test_15() {
-  BootstrapMonitor.promiseAddonStartup(ID1).then(function test_15_after_startup() {
-    AddonManager.getAddonByID(ID1, function(b1) {
-      Assert.notEqual(b1, null);
-      Assert.equal(b1.version, "1.0");
-      Assert.ok(!b1.appDisabled);
-      Assert.ok(!b1.userDisabled);
-      Assert.ok(b1.isActive);
-      Assert.ok(!b1.isSystem);
-      BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-      BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-
-      b1.userDisabled = true;
-      Assert.ok(!b1.isActive);
-      BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-      BootstrapMonitor.checkAddonNotStarted(ID1);
-
-      prepare_test({ }, [
-        "onNewInstall"
-      ]);
-
-      AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), function(install) {
-        ensure_test_completed();
-
-        Assert.notEqual(install, null);
-        Assert.ok(install.addon.userDisabled);
-
-        prepare_test({
-          [ID1]: [
-            ["onInstalling", false],
-            "onInstalled"
-          ]
-        }, [
-          "onInstallStarted",
-          "onInstallEnded",
-        ], callback_soon(check_test_15));
-        install.install();
-      });
-    });
-  });
-  installAllFiles([do_get_addon("test_bootstrap1_1")], function test_15_addon_installed() { });
-}
-
-function check_test_15() {
-  AddonManager.getAddonByID(ID1, function(b1) {
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "2.0");
-    Assert.ok(!b1.appDisabled);
-    Assert.ok(b1.userDisabled);
-    Assert.ok(!b1.isActive);
-    BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
-    BootstrapMonitor.checkAddonNotStarted(ID1);
-
-    do_check_bootstrappedPref(function() {
-      restartManager();
-
-      AddonManager.getAddonByID(ID1, callback_soon(function(b1_2) {
-        Assert.notEqual(b1_2, null);
-        Assert.equal(b1_2.version, "2.0");
-        Assert.ok(!b1_2.appDisabled);
-        Assert.ok(b1_2.userDisabled);
-        Assert.ok(!b1_2.isActive);
-        BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
-        BootstrapMonitor.checkAddonNotStarted(ID1);
-
-        b1_2.uninstall();
-
-        run_test_16();
-      }));
-    });
-  });
-}
-
-// Tests that bootstrapped extensions don't get loaded when in safe mode
-function run_test_16() {
-  BootstrapMonitor.promiseAddonStartup(ID1).then(function test_16_after_startup() {
-    AddonManager.getAddonByID(ID1, callback_soon(function(b1) {
-      // Should have installed and started
-      BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-      BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-      Assert.ok(b1.isActive);
-      Assert.ok(!b1.isSystem);
-      Assert.equal(b1.iconURL, "chrome://foo/skin/icon.png");
-      Assert.equal(b1.aboutURL, "chrome://foo/content/about.xul");
-      Assert.equal(b1.optionsURL, "chrome://foo/content/options.xul");
-
-      shutdownManager();
-
-      // Should have stopped
-      BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-      BootstrapMonitor.checkAddonNotStarted(ID1);
-
-      gAppInfo.inSafeMode = true;
-      startupManager(false);
-
-      AddonManager.getAddonByID(ID1, callback_soon(function(b1_2) {
-        // Should still be stopped
-        BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-        BootstrapMonitor.checkAddonNotStarted(ID1);
-        Assert.ok(!b1_2.isActive);
-        Assert.equal(b1_2.iconURL, null);
-        Assert.equal(b1_2.aboutURL, null);
-        Assert.equal(b1_2.optionsURL, null);
-
-        shutdownManager();
-        gAppInfo.inSafeMode = false;
-        startupManager(false);
-
-        // Should have started
-        BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-        BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-
-        AddonManager.getAddonByID(ID1, function(b1_3) {
-          b1_3.uninstall();
+  let addon = install.addon;
 
-          executeSoon(run_test_17);
-        });
-      }));
-    }));
-  });
-  installAllFiles([do_get_addon("test_bootstrap1_1")], function() { });
-}
-
-// Check that a bootstrapped extension in a non-profile location is loaded
-function run_test_17() {
-  shutdownManager();
-
-  manuallyInstall(do_get_addon("test_bootstrap1_1"), userExtDir,
-                  ID1);
-
-  startupManager();
-
-  AddonManager.getAddonByID(ID1, function(b1) {
-    // Should have installed and started
-    BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-    BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "1.0");
-    Assert.ok(b1.isActive);
-    Assert.ok(!b1.isSystem);
-
-    do_check_bootstrappedPref(run_test_18);
-  });
-}
-
-// Check that installing a new bootstrapped extension in the profile replaces
-// the existing one
-function run_test_18() {
-  BootstrapMonitor.promiseAddonStartup(ID1).then(function test_18_after_startup() {
-    AddonManager.getAddonByID(ID1, function(b1) {
-      // Should have installed and started
-      BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
-      BootstrapMonitor.checkAddonStarted(ID1, "2.0");
-      Assert.notEqual(b1, null);
-      Assert.equal(b1.version, "2.0");
-      Assert.ok(b1.isActive);
-      Assert.ok(!b1.isSystem);
-
-      Assert.equal(getShutdownReason(), ADDON_UPGRADE);
-      Assert.equal(getUninstallReason(), ADDON_UPGRADE);
-      Assert.equal(getInstallReason(), ADDON_UPGRADE);
-      Assert.equal(getStartupReason(), ADDON_UPGRADE);
-
-      Assert.equal(getShutdownNewVersion(), 2);
-      Assert.equal(getUninstallNewVersion(), 2);
-      Assert.equal(getInstallOldVersion(), 1);
-      Assert.equal(getStartupOldVersion(), 1);
-
-      do_check_bootstrappedPref(run_test_19);
-    });
-  });
-  installAllFiles([do_get_addon("test_bootstrap1_2")], function() { });
-}
-
-// Check that uninstalling the profile version reveals the non-profile one
-function run_test_19() {
-  AddonManager.getAddonByID(ID1, function(b1) {
-    // The revealed add-on gets activated asynchronously
-    prepare_test({
-      [ID1]: [
-        ["onUninstalling", false],
-        "onUninstalled",
-        ["onInstalling", false],
-        "onInstalled"
-      ]
-    }, [], check_test_19);
-
-    b1.uninstall();
-  });
-}
-
-function check_test_19() {
-  AddonManager.getAddonByID(ID1, function(b1) {
-    // Should have reverted to the older version
-    BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-    BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "1.0");
-    Assert.ok(b1.isActive);
-    Assert.ok(!b1.isSystem);
-
-    Assert.equal(getShutdownReason(), ADDON_DOWNGRADE);
-    Assert.equal(getUninstallReason(), ADDON_DOWNGRADE);
-    Assert.equal(getInstallReason(), ADDON_DOWNGRADE);
-    Assert.equal(getStartupReason(), ADDON_DOWNGRADE);
-
-    Assert.equal(getShutdownNewVersion(), undefined);
-    Assert.equal(getUninstallNewVersion(), undefined);
-    Assert.equal(getInstallOldVersion(), undefined);
-    Assert.equal(getStartupOldVersion(), undefined);
-
-    do_check_bootstrappedPref(run_test_20);
-  });
-}
-
-// Check that a new profile extension detected at startup replaces the non-profile
-// one
-function run_test_20() {
-  shutdownManager();
-
-  manuallyInstall(do_get_addon("test_bootstrap1_2"), profileDir,
-                  ID1);
-
-  startupManager();
-
-  AddonManager.getAddonByID(ID1, function(b1) {
-    // Should have installed and started
-    BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
-    BootstrapMonitor.checkAddonStarted(ID1, "2.0");
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "2.0");
-    Assert.ok(b1.isActive);
-    Assert.ok(!b1.isSystem);
-
-    Assert.equal(getShutdownReason(), APP_SHUTDOWN);
-    Assert.equal(getUninstallReason(), ADDON_UPGRADE);
-    Assert.equal(getInstallReason(), ADDON_UPGRADE);
-    Assert.equal(getStartupReason(), APP_STARTUP);
-
-    Assert.equal(getShutdownNewVersion(), undefined);
-    Assert.equal(getUninstallNewVersion(), 2);
-    Assert.equal(getInstallOldVersion(), 1);
-    Assert.equal(getStartupOldVersion(), undefined);
-
-    executeSoon(run_test_21);
-  });
-}
-
-// Check that a detected removal reveals the non-profile one
-function run_test_21() {
-  shutdownManager();
-
-  Assert.equal(getShutdownReason(), APP_SHUTDOWN);
-  Assert.equal(getShutdownNewVersion(), undefined);
-
-  manuallyUninstall(profileDir, ID1);
-  BootstrapMonitor.clear(ID1);
-
-  startupManager();
-
-  AddonManager.getAddonByID(ID1, function(b1) {
-    // Should have installed and started
-    BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-    BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "1.0");
-    Assert.ok(b1.isActive);
-    Assert.ok(!b1.isSystem);
-
-    // This won't be set as the bootstrap script was gone so we couldn't
-    // uninstall it properly
-    Assert.equal(getUninstallReason(), undefined);
-    Assert.equal(getUninstallNewVersion(), undefined);
-
-    Assert.equal(getInstallReason(), ADDON_DOWNGRADE);
-    Assert.equal(getInstallOldVersion(), 2);
-
-    Assert.equal(getStartupReason(), APP_STARTUP);
-    Assert.equal(getStartupOldVersion(), undefined);
-
-    do_check_bootstrappedPref(function() {
-      shutdownManager();
-
-      manuallyUninstall(userExtDir, ID1);
-      BootstrapMonitor.clear(ID1);
-
-      startupManager(false);
-      run_test_22();
-    });
-  });
-}
-
-// Check that an upgrade from the filesystem is detected and applied correctly
-function run_test_22() {
-  shutdownManager();
-
-  let file = manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir,
-                             ID1);
-  if (file.isDirectory())
-    file.append("install.rdf");
-
-  // Make it look old so changes are detected
-  setExtensionModifiedTime(file, file.lastModifiedTime - 5000);
-
-  startupManager();
-
-  AddonManager.getAddonByID(ID1, callback_soon(function(b1) {
-    // Should have installed and started
-    BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
-    BootstrapMonitor.checkAddonStarted(ID1, "1.0");
-    Assert.notEqual(b1, null);
-    Assert.equal(b1.version, "1.0");
-    Assert.ok(b1.isActive);
-    Assert.ok(!b1.isSystem);
-
-    shutdownManager();
-
-    Assert.equal(getShutdownReason(), APP_SHUTDOWN);
-    Assert.equal(getShutdownNewVersion(), undefined);
-
-    manuallyUninstall(profileDir, ID1);
-    BootstrapMonitor.clear(ID1);
-    manuallyInstall(do_get_addon("test_bootstrap1_2"), profileDir,
-                    ID1);
-
-    startupManager();
-
-    AddonManager.getAddonByID(ID1, function(b1_2) {
-      // Should have installed and started
-      BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
-      BootstrapMonitor.checkAddonStarted(ID1, "2.0");
-      Assert.notEqual(b1_2, null);
-      Assert.equal(b1_2.version, "2.0");
-      Assert.ok(b1_2.isActive);
-      Assert.ok(!b1_2.isSystem);
-
-      // This won't be set as the bootstrap script was gone so we couldn't
-      // uninstall it properly
-      Assert.equal(getUninstallReason(), undefined);
-      Assert.equal(getUninstallNewVersion(), undefined);
-
-      Assert.equal(getInstallReason(), ADDON_UPGRADE);
-      Assert.equal(getInstallOldVersion(), 1);
-      Assert.equal(getStartupReason(), APP_STARTUP);
-      Assert.equal(getStartupOldVersion(), undefined);
-
-      do_check_bootstrappedPref(function() {
-        b1_2.uninstall();
-
-        run_test_23();
-      });
-    });
-  }));
-}
-
-
-// Tests that installing from a URL doesn't require a restart
-function run_test_23() {
-  prepare_test({ }, [
-    "onNewInstall"
-  ]);
-
-  let url = "http://localhost:" + gPort + "/addons/test_bootstrap1_1.xpi";
-  AddonManager.getInstallForURL(url, function(install) {
-    ensure_test_completed();
-
-    Assert.notEqual(install, null);
-
-    prepare_test({ }, [
-      "onDownloadStarted",
-      "onDownloadEnded"
-    ], function() {
-      Assert.equal(install.type, "extension");
-      Assert.equal(install.version, "1.0");
-      Assert.equal(install.name, "Test Bootstrap 1");
-      Assert.equal(install.state, AddonManager.STATE_DOWNLOADED);
-      Assert.ok(install.addon.hasResource("install.rdf"));
-      Assert.ok(install.addon.hasResource("bootstrap.js"));
-      Assert.ok(!install.addon.hasResource("foo.bar"));
-      Assert.equal(install.addon.operationsRequiringRestart &
-                   AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
-      do_check_not_in_crash_annotation(ID1, "1.0");
-
-      let addon = install.addon;
+  await Promise.all([
+    BootstrapMonitor.promiseAddonStartup(ID1),
+    new Promise(resolve => {
       prepare_test({
         [ID1]: [
           ["onInstalling", false],
           "onInstalled"
         ]
       }, [
         "onInstallStarted",
         "onInstallEnded",
       ], function() {
-        Assert.ok(addon.hasResource("install.rdf"));
-        do_check_bootstrappedPref(check_test_23);
+        ok(addon.hasResource("install.rdf"));
+
+        // startup should not have been called yet.
+        BootstrapMonitor.checkAddonNotStarted(ID1);
+        resolve();
       });
+      install.install();
+    }),
+  ]);
+
+  await checkBootstrappedPref();
+  let installSyncGUID = addon.syncGUID;
+
+  let installs = await AddonManager.getAllInstalls();
+  // There should be no active installs now since the install completed and
+  // doesn't require a restart.
+  equal(installs.length, 0);
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "1.0");
+  notEqual(b1.syncGUID, null);
+  equal(b1.syncGUID, installSyncGUID);
+  ok(!b1.appDisabled);
+  ok(!b1.userDisabled);
+  ok(b1.isActive);
+  ok(!b1.isSystem);
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+  equal(getStartupReason(), ADDON_INSTALL);
+  equal(getStartupOldVersion(), undefined);
+  ok(b1.hasResource("install.rdf"));
+  ok(b1.hasResource("bootstrap.js"));
+  ok(!b1.hasResource("foo.bar"));
+  do_check_in_crash_annotation(ID1, "1.0");
+
+  let dir = do_get_addon_root_uri(profileDir, ID1);
+  equal(b1.getResourceURI("bootstrap.js").spec, dir + "bootstrap.js");
+
+  let list = await AddonManager.getAddonsWithOperationsByTypes(null);
+  equal(list.length, 0);
+});
+
+// Tests that disabling doesn't require a restart
+add_task(async function test_2() {
+  let b1 = await AddonManager.getAddonByID(ID1);
+  prepare_test({
+    [ID1]: [
+      ["onDisabling", false],
+      "onDisabled"
+    ]
+  });
+
+  equal(b1.operationsRequiringRestart &
+        AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
+  b1.userDisabled = true;
+  ensure_test_completed();
+
+  notEqual(b1, null);
+  equal(b1.version, "1.0");
+  ok(!b1.appDisabled);
+  ok(b1.userDisabled);
+  ok(!b1.isActive);
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonNotStarted(ID1);
+  equal(getShutdownReason(), ADDON_DISABLE);
+  equal(getShutdownNewVersion(), undefined);
+  do_check_not_in_crash_annotation(ID1, "1.0");
+
+  let newb1 = await AddonManager.getAddonByID(ID1);
+  notEqual(newb1, null);
+  equal(newb1.version, "1.0");
+  ok(!newb1.appDisabled);
+  ok(newb1.userDisabled);
+  ok(!newb1.isActive);
+
+  await checkBootstrappedPref();
+});
+
+// Test that restarting doesn't accidentally re-enable
+add_task(async function test_3() {
+  await promiseShutdownManager();
+
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonNotStarted(ID1);
+  equal(getShutdownReason(), ADDON_DISABLE);
+  equal(getShutdownNewVersion(), undefined);
+
+  await promiseStartupManager(false);
+
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonNotStarted(ID1);
+  equal(getShutdownReason(), ADDON_DISABLE);
+  equal(getShutdownNewVersion(), undefined);
+  do_check_not_in_crash_annotation(ID1, "1.0");
+
+  ok(gAddonStartup.exists());
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "1.0");
+  ok(!b1.appDisabled);
+  ok(b1.userDisabled);
+  ok(!b1.isActive);
+
+  await checkBootstrappedPref();
+});
+
+// Tests that enabling doesn't require a restart
+add_task(async function test_4() {
+  let b1 = await AddonManager.getAddonByID(ID1);
+  prepare_test({
+    [ID1]: [
+      ["onEnabling", false],
+      "onEnabled"
+    ]
+  });
+
+  equal(b1.operationsRequiringRestart &
+               AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
+  b1.userDisabled = false;
+  ensure_test_completed();
+
+  notEqual(b1, null);
+  equal(b1.version, "1.0");
+  ok(!b1.appDisabled);
+  ok(!b1.userDisabled);
+  ok(b1.isActive);
+  ok(!b1.isSystem);
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+  equal(getStartupReason(), ADDON_ENABLE);
+  equal(getStartupOldVersion(), undefined);
+  do_check_in_crash_annotation(ID1, "1.0");
+
+  let newb1 = await AddonManager.getAddonByID(ID1);
+  notEqual(newb1, null);
+  equal(newb1.version, "1.0");
+  ok(!newb1.appDisabled);
+  ok(!newb1.userDisabled);
+  ok(newb1.isActive);
+
+  await checkBootstrappedPref();
+});
+
+// Tests that a restart shuts down and restarts the add-on
+add_task(async function test_5() {
+  await promiseShutdownManager();
+  // By the time we've shut down, the database must have been written
+  ok(gExtensionsJSON.exists());
+
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonNotStarted(ID1);
+  equal(getShutdownReason(), APP_SHUTDOWN);
+  equal(getShutdownNewVersion(), undefined);
+  do_check_not_in_crash_annotation(ID1, "1.0");
+  await promiseStartupManager(false);
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+  equal(getStartupReason(), APP_STARTUP);
+  equal(getStartupOldVersion(), undefined);
+  do_check_in_crash_annotation(ID1, "1.0");
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "1.0");
+  ok(!b1.appDisabled);
+  ok(!b1.userDisabled);
+  ok(b1.isActive);
+  ok(!b1.isSystem);
+  ok(!isExtensionInAddonsList(profileDir, b1.id));
+
+  await checkBootstrappedPref();
+});
+
+// Tests that installing an upgrade doesn't require a restart
+add_task(async function test_6() {
+  prepare_test({}, [
+    "onNewInstall"
+  ]);
+
+  let install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_2);
+  ensure_test_completed();
+
+  notEqual(install, null);
+  equal(install.type, "extension");
+  equal(install.version, "2.0");
+  equal(install.name, "Test Bootstrap 1");
+  equal(install.state, AddonManager.STATE_DOWNLOADED);
+
+  await Promise.all([
+    BootstrapMonitor.promiseAddonStartup(ID1),
+    new Promise(resolve => {
+      prepare_test({
+        [ID1]: [
+          ["onInstalling", false],
+          "onInstalled"
+        ]
+      }, [
+        "onInstallStarted",
+        "onInstallEnded",
+      ], resolve);
+      install.install();
+    }),
+  ]);
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "2.0");
+  ok(!b1.appDisabled);
+  ok(!b1.userDisabled);
+  ok(b1.isActive);
+  ok(!b1.isSystem);
+  BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
+  BootstrapMonitor.checkAddonStarted(ID1, "2.0");
+  equal(getStartupReason(), ADDON_UPGRADE);
+  equal(getInstallOldVersion(), 1);
+  equal(getStartupOldVersion(), 1);
+  equal(getShutdownReason(), ADDON_UPGRADE);
+  equal(getShutdownNewVersion(), 2);
+  equal(getUninstallNewVersion(), 2);
+  do_check_not_in_crash_annotation(ID1, "1.0");
+  do_check_in_crash_annotation(ID1, "2.0");
+
+  await checkBootstrappedPref();
+});
+
+// Tests that uninstalling doesn't require a restart
+add_task(async function test_7() {
+  let b1 = await AddonManager.getAddonByID(ID1);
+  prepare_test({
+    [ID1]: [
+      ["onUninstalling", false],
+      "onUninstalled"
+    ]
+  });
+
+  equal(b1.operationsRequiringRestart &
+        AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
+  b1.uninstall();
+
+  await checkBootstrappedPref();
+
+  ensure_test_completed();
+  BootstrapMonitor.checkAddonNotInstalled(ID1);
+  BootstrapMonitor.checkAddonNotStarted(ID1);
+  equal(getShutdownReason(), ADDON_UNINSTALL);
+  equal(getShutdownNewVersion(), undefined);
+  do_check_not_in_crash_annotation(ID1, "2.0");
+
+  b1 = await AddonManager.getAddonByID(ID1);
+  equal(b1, null);
+
+  await promiseRestartManager();
+
+  let newb1 = await AddonManager.getAddonByID(ID1);
+  equal(newb1, null);
+
+  await checkBootstrappedPref();
+});
+
+// Test that a bootstrapped extension dropped into the profile loads properly
+// on startup and doesn't cause an EM restart
+add_task(async function test_8() {
+  await promiseShutdownManager();
+
+  manuallyInstall(XPIS.test_bootstrap1_1, profileDir,
+                  ID1);
+
+  await promiseStartupManager(false);
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "1.0");
+  ok(!b1.appDisabled);
+  ok(!b1.userDisabled);
+  ok(b1.isActive);
+  ok(!b1.isSystem);
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+  equal(getStartupReason(), ADDON_INSTALL);
+  equal(getStartupOldVersion(), undefined);
+  do_check_in_crash_annotation(ID1, "1.0");
+
+  await checkBootstrappedPref();
+});
+
+// Test that items detected as removed during startup get removed properly
+add_task(async function test_9() {
+  await promiseShutdownManager();
+
+  manuallyUninstall(profileDir, ID1);
+  BootstrapMonitor.clear(ID1);
+
+  await promiseStartupManager(false);
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  equal(b1, null);
+  do_check_not_in_crash_annotation(ID1, "1.0");
+
+  await checkBootstrappedPref();
+});
+
+
+// Tests that installing a downgrade sends the right reason
+add_task(async function test_10() {
+  prepare_test({}, [
+    "onNewInstall"
+  ]);
+
+  let install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_2);
+  ensure_test_completed();
+
+  notEqual(install, null);
+  equal(install.type, "extension");
+  equal(install.version, "2.0");
+  equal(install.name, "Test Bootstrap 1");
+  equal(install.state, AddonManager.STATE_DOWNLOADED);
+  ok(install.addon.hasResource("install.rdf"));
+  ok(install.addon.hasResource("bootstrap.js"));
+  ok(!install.addon.hasResource("foo.bar"));
+  do_check_not_in_crash_annotation(ID1, "2.0");
+
+  await Promise.all([
+    BootstrapMonitor.promiseAddonStartup(ID1),
+    new Promise(resolve => {
+      prepare_test({
+        [ID1]: [
+          ["onInstalling", false],
+          "onInstalled"
+        ]
+      }, [
+        "onInstallStarted",
+        "onInstallEnded",
+      ], resolve);
+      install.install();
+    }),
+  ]);
+
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "2.0");
+  ok(!b1.appDisabled);
+  ok(!b1.userDisabled);
+  ok(b1.isActive);
+  ok(!b1.isSystem);
+  BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
+  BootstrapMonitor.checkAddonStarted(ID1, "2.0");
+  equal(getStartupReason(), ADDON_INSTALL);
+  equal(getStartupOldVersion(), undefined);
+  ok(b1.hasResource("install.rdf"));
+  ok(b1.hasResource("bootstrap.js"));
+  ok(!b1.hasResource("foo.bar"));
+  do_check_in_crash_annotation(ID1, "2.0");
+
+  prepare_test({}, [
+    "onNewInstall"
+  ]);
+
+  install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_1);
+  ensure_test_completed();
+
+  notEqual(install, null);
+  equal(install.type, "extension");
+  equal(install.version, "1.0");
+  equal(install.name, "Test Bootstrap 1");
+  equal(install.state, AddonManager.STATE_DOWNLOADED);
+
+  await Promise.all([
+    BootstrapMonitor.promiseAddonStartup(ID1),
+    new Promise(resolve => {
+      prepare_test({
+        [ID1]: [
+          ["onInstalling", false],
+          "onInstalled"
+        ]
+      }, [
+        "onInstallStarted",
+        "onInstallEnded",
+      ], resolve);
+      install.install();
+    }),
+  ]);
+
+  b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "1.0");
+  ok(!b1.appDisabled);
+  ok(!b1.userDisabled);
+  ok(b1.isActive);
+  ok(!b1.isSystem);
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+  equal(getStartupReason(), ADDON_DOWNGRADE);
+  equal(getInstallOldVersion(), 2);
+  equal(getStartupOldVersion(), 2);
+  equal(getShutdownReason(), ADDON_DOWNGRADE);
+  equal(getShutdownNewVersion(), 1);
+  equal(getUninstallNewVersion(), 1);
+  do_check_in_crash_annotation(ID1, "1.0");
+  do_check_not_in_crash_annotation(ID1, "2.0");
+
+  await checkBootstrappedPref();
+});
+
+// Tests that uninstalling a disabled add-on still calls the uninstall method
+add_task(async function test_11() {
+  let b1 = await AddonManager.getAddonByID(ID1);
+  prepare_test({
+    [ID1]: [
+      ["onDisabling", false],
+      "onDisabled",
+      ["onUninstalling", false],
+      "onUninstalled"
+    ]
+  });
+
+  b1.userDisabled = true;
+
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonNotStarted(ID1);
+  equal(getShutdownReason(), ADDON_DISABLE);
+  equal(getShutdownNewVersion(), undefined);
+  do_check_not_in_crash_annotation(ID1, "1.0");
+
+  b1.uninstall();
+
+  ensure_test_completed();
+  BootstrapMonitor.checkAddonNotInstalled(ID1);
+  BootstrapMonitor.checkAddonNotStarted(ID1);
+  do_check_not_in_crash_annotation(ID1, "1.0");
+
+  await checkBootstrappedPref();
+});
+
+// Tests that bootstrapped extensions are correctly loaded even if the app is
+// upgraded at the same time
+add_task(async function test_12() {
+  await promiseShutdownManager();
+
+  manuallyInstall(XPIS.test_bootstrap1_1, profileDir,
+                  ID1);
+
+  await promiseStartupManager(true);
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "1.0");
+  ok(!b1.appDisabled);
+  ok(!b1.userDisabled);
+  ok(b1.isActive);
+  ok(!b1.isSystem);
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+  equal(getStartupReason(), ADDON_INSTALL);
+  equal(getStartupOldVersion(), undefined);
+  do_check_in_crash_annotation(ID1, "1.0");
+
+  b1.uninstall();
+
+  await promiseRestartManager();
+  await checkBootstrappedPref();
+});
+
+
+// Tests that installing a bootstrapped extension with an invalid application
+// entry doesn't call it's startup method
+add_task(async function test_13() {
+  prepare_test({}, [
+    "onNewInstall"
+  ]);
+
+  let install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_3);
+  ensure_test_completed();
+
+  notEqual(install, null);
+  equal(install.type, "extension");
+  equal(install.version, "3.0");
+  equal(install.name, "Test Bootstrap 1");
+  equal(install.state, AddonManager.STATE_DOWNLOADED);
+  do_check_not_in_crash_annotation(ID1, "3.0");
+
+  await new Promise(resolve => {
+    prepare_test({
+      [ID1]: [
+        ["onInstalling", false],
+        "onInstalled"
+      ]
+    }, [
+      "onInstallStarted",
+      "onInstallEnded",
+    ], resolve);
+    install.install();
+  });
+
+  let installs = await AddonManager.getAllInstalls();
+
+  // There should be no active installs now since the install completed and
+  // doesn't require a restart.
+  equal(installs.length, 0);
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "3.0");
+  ok(b1.appDisabled);
+  ok(!b1.userDisabled);
+  ok(!b1.isActive);
+  BootstrapMonitor.checkAddonInstalled(ID1, "3.0"); // We call install even for disabled add-ons
+  BootstrapMonitor.checkAddonNotStarted(ID1); // Should not have called startup though
+  do_check_not_in_crash_annotation(ID1, "3.0");
+
+  await promiseRestartManager();
+
+  b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "3.0");
+  ok(b1.appDisabled);
+  ok(!b1.userDisabled);
+  ok(!b1.isActive);
+  BootstrapMonitor.checkAddonInstalled(ID1, "3.0"); // We call install even for disabled add-ons
+  BootstrapMonitor.checkAddonNotStarted(ID1); // Should not have called startup though
+  do_check_not_in_crash_annotation(ID1, "3.0");
+
+  await checkBootstrappedPref();
+  b1.uninstall();
+});
+
+// Tests that a bootstrapped extension with an invalid target application entry
+// does not get loaded when detected during startup
+add_task(async function test_14() {
+  await promiseRestartManager();
+
+  await promiseShutdownManager();
+
+  manuallyInstall(XPIS.test_bootstrap1_3, profileDir,
+                  ID1);
+
+  await promiseStartupManager(false);
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "3.0");
+  ok(b1.appDisabled);
+  ok(!b1.userDisabled);
+  ok(!b1.isActive);
+  BootstrapMonitor.checkAddonInstalled(ID1, "3.0"); // We call install even for disabled add-ons
+  BootstrapMonitor.checkAddonNotStarted(ID1); // Should not have called startup though
+  do_check_not_in_crash_annotation(ID1, "3.0");
+
+  await checkBootstrappedPref();
+  b1.uninstall();
+});
+
+// Tests that upgrading a disabled bootstrapped extension still calls uninstall
+// and install but doesn't startup the new version
+add_task(async function test_15() {
+  await Promise.all([
+    BootstrapMonitor.promiseAddonStartup(ID1),
+    promiseInstallFile(XPIS.test_bootstrap1_1),
+  ]);
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "1.0");
+  ok(!b1.appDisabled);
+  ok(!b1.userDisabled);
+  ok(b1.isActive);
+  ok(!b1.isSystem);
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+
+  b1.userDisabled = true;
+  ok(!b1.isActive);
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonNotStarted(ID1);
+
+  prepare_test({}, [
+    "onNewInstall"
+  ]);
+
+  let install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_2);
+  ensure_test_completed();
+
+  notEqual(install, null);
+  ok(install.addon.userDisabled);
+
+  await new Promise(resolve => {
+    prepare_test({
+      [ID1]: [
+        ["onInstalling", false],
+        "onInstalled"
+      ]
+    }, [
+      "onInstallStarted",
+      "onInstallEnded",
+    ], resolve);
+    install.install();
+  });
+
+  b1 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1, null);
+  equal(b1.version, "2.0");
+  ok(!b1.appDisabled);
+  ok(b1.userDisabled);
+  ok(!b1.isActive);
+  BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
+  BootstrapMonitor.checkAddonNotStarted(ID1);
+
+  await checkBootstrappedPref();
+  await promiseRestartManager();
+
+  let b1_2 = await AddonManager.getAddonByID(ID1);
+  notEqual(b1_2, null);
+  equal(b1_2.version, "2.0");
+  ok(!b1_2.appDisabled);
+  ok(b1_2.userDisabled);
+  ok(!b1_2.isActive);
+  BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
+  BootstrapMonitor.checkAddonNotStarted(ID1);
+
+  b1_2.uninstall();
+});
+
+// Tests that bootstrapped extensions don't get loaded when in safe mode
+add_task(async function test_16() {
+  await Promise.all([
+    BootstrapMonitor.promiseAddonStartup(ID1),
+    promiseInstallFile(XPIS.test_bootstrap1_1),
+  ]);
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  // Should have installed and started
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+  ok(b1.isActive);
+  ok(!b1.isSystem);
+  equal(b1.iconURL, "chrome://foo/skin/icon.png");
+  equal(b1.aboutURL, "chrome://foo/content/about.xul");
+  equal(b1.optionsURL, "chrome://foo/content/options.xul");
+
+  await promiseShutdownManager();
+
+  // Should have stopped
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonNotStarted(ID1);
+
+  gAppInfo.inSafeMode = true;
+  await promiseStartupManager(false);
+
+  let b1_2 = await AddonManager.getAddonByID(ID1);
+  // Should still be stopped
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonNotStarted(ID1);
+  ok(!b1_2.isActive);
+  equal(b1_2.iconURL, null);
+  equal(b1_2.aboutURL, null);
+  equal(b1_2.optionsURL, null);
+
+  await promiseShutdownManager();
+  gAppInfo.inSafeMode = false;
+  await promiseStartupManager(false);
+
+  // Should have started
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+
+  let b1_3 = await AddonManager.getAddonByID(ID1);
+  b1_3.uninstall();
+});
+
+// Check that a bootstrapped extension in a non-profile location is loaded
+add_task(async function test_17() {
+  await promiseShutdownManager();
+
+  manuallyInstall(XPIS.test_bootstrap1_1, userExtDir,
+                  ID1);
+
+  await promiseStartupManager();
+
+  let b1 = await AddonManager.getAddonByID(ID1);
+  // Should have installed and started
+  BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+  BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+  notEqual(b1, null);
+  equal(b1.version, "1.0");
+  ok(b1.isActive);
+  ok(!b1.isSystem);
+
+  await checkBootstrappedPref();
+});
+
+// Check that installing a new bootstrapped extension in the profile replaces
+// the existing one
+add_ta