Merge inbound to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Sun, 29 Jul 2018 12:45:56 +0300
changeset 483992 0be4463d2915
parent 483973 34388a2acea8 (current diff)
parent 483991 b1e7eb636989 (diff)
child 483994 30f97c168234
child 484030 9202a03ea489
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
0be4463d2915 / 63.0a1 / 20180729100102 / files
nightly linux64
0be4463d2915 / 63.0a1 / 20180729100102 / files
nightly mac
0be4463d2915 / 63.0a1 / 20180729100102 / files
nightly win32
0be4463d2915 / 63.0a1 / 20180729100102 / files
nightly win64
0be4463d2915 / 63.0a1 / 20180729100102 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
dom/base/test/test_declare_stylesheet_obsolete.html
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -852,22 +852,17 @@ function shortenString(aString, aMaxLeng
 
 // //////////////////////////////////////////////////////////////////////////////
 // General Utils
 // //////////////////////////////////////////////////////////////////////////////
 /**
  * Return main chrome window (crosses chrome boundary)
  */
 function getMainChromeWindow(aWindow) {
-  return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                .getInterface(Ci.nsIWebNavigation)
-                .QueryInterface(Ci.nsIDocShellTreeItem)
-                .rootTreeItem
-                .QueryInterface(Ci.nsIInterfaceRequestor)
-                .getInterface(Ci.nsIDOMWindow);
+  return aWindow.docShell.rootTreeItem.domWindow;
 }
 
 /** Sets the test plugin(s) initially expected enabled state.
  * It will automatically be reset to it's previous value after the test
  * ends.
  * @param aNewEnabledState [in] the enabled state, e.g. SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED
  * @param aPluginName [in, optional] The name of the plugin, defaults to "Test Plug-in"
  */
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7075,22 +7075,17 @@ var MailIntegration = {
 };
 
 function BrowserOpenAddonsMgr(aView) {
   return new Promise(resolve => {
     let emWindow;
     let browserWindow;
 
     var receivePong = function(aSubject, aTopic, aData) {
-      let browserWin = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIWebNavigation)
-                               .QueryInterface(Ci.nsIDocShellTreeItem)
-                               .rootTreeItem
-                               .QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIDOMWindow);
+      let browserWin = aSubject.docShell.rootTreeItem.domWindow;
       if (!emWindow || browserWin == window /* favor the current window */) {
         emWindow = aSubject;
         browserWindow = browserWin;
       }
     };
     Services.obs.addObserver(receivePong, "EM-pong");
     Services.obs.notifyObservers(null, "EM-ping");
     Services.obs.removeObserver(receivePong, "EM-pong");
--- a/browser/base/content/test/about/browser_aboutCertError.js
+++ b/browser/base/content/test/about/browser_aboutCertError.js
@@ -297,26 +297,27 @@ add_task(async function checkAdvancedDet
       let el = doc.getElementById("errorCode");
       return { textContent: el.textContent, tagName: el.tagName };
     });
     is(message.textContent, "SEC_ERROR_EXPIRED_CERTIFICATE",
        "Correct error message found");
     is(message.tagName, "a", "Error message is a link");
 
     message = await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) {
-      let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document;
+      let win = frame ? content.document.querySelector("iframe").contentWindow : content;
+      let doc = win.document;
 
       let errorCode = doc.getElementById("errorCode");
       errorCode.click();
       let div = doc.getElementById("certificateErrorDebugInformation");
       let text = doc.getElementById("certificateErrorText");
 
       let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
                        .getService(Ci.nsISerializationHelper);
-      let serializable =  doc.docShell.failedChannel.securityInfo
+      let serializable =  win.docShell.failedChannel.securityInfo
                                       .QueryInterface(Ci.nsITransportSecurityInfo)
                                       .QueryInterface(Ci.nsISerializable);
       let serializedSecurityInfo = serhelper.serializeToString(serializable);
       return {
         divDisplay: content.getComputedStyle(div).display,
         text: text.textContent,
         securityInfoAsString: serializedSecurityInfo
       };
@@ -383,26 +384,27 @@ add_task(async function checkAdvancedDet
        "Correct error message found");
     is(message.ecTagName, "a", "Error message is a link");
     const url = badStsUri.prePath.slice(badStsUri.prePath.indexOf(".") + 1);
     is(message.cdlTextContent, url,
        "Correct cert_domain_link contents found");
     is(message.cdlTagName, "a", "cert_domain_link is a link");
 
     message = await ContentTask.spawn(browser, {frame: useFrame}, async function({frame}) {
-      let doc = frame ? content.document.querySelector("iframe").contentDocument : content.document;
+      let win = frame ? content.document.querySelector("iframe").contentWindow : content;
+      let doc = win.document;
 
       let errorCode = doc.getElementById("errorCode");
       errorCode.click();
       let div = doc.getElementById("certificateErrorDebugInformation");
       let text = doc.getElementById("certificateErrorText");
 
       let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
                        .getService(Ci.nsISerializationHelper);
-      let serializable =  doc.docShell.failedChannel.securityInfo
+      let serializable =  win.docShell.failedChannel.securityInfo
                                       .QueryInterface(Ci.nsITransportSecurityInfo)
                                       .QueryInterface(Ci.nsISerializable);
       let serializedSecurityInfo = serhelper.serializeToString(serializable);
       return {
         divDisplay: content.getComputedStyle(div).display,
         text: text.textContent,
         securityInfoAsString: serializedSecurityInfo
       };
--- a/browser/base/content/test/general/browser_bug676619.js
+++ b/browser/base/content/test/general/browser_bug676619.js
@@ -7,18 +7,17 @@ function waitForNewWindow() {
 
         function downloadOnLoad() {
           domwindow.removeEventListener("load", downloadOnLoad, true);
 
           is(domwindow.document.location.href, "chrome://mozapps/content/downloads/unknownContentType.xul", "Download page appeared");
           resolve(domwindow);
         }
 
-        var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                  .getInterface(Ci.nsIDOMWindow);
+        var domwindow = aXULWindow.docShell.domWindow;
         domwindow.addEventListener("load", downloadOnLoad, true);
       },
       onCloseWindow: aXULWindow => {},
     };
 
     Services.wm.addListener(listener);
   });
 }
--- a/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
+++ b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
@@ -23,18 +23,17 @@ function frameScript() {
   });
   content.document.addEventListener("fullscreenchange", () => {
     sendAsyncMessage("Test:FullscreenChanged", {
       inDOMFullscreen: !!content.document.fullscreenElement,
       inFullscreen: content.fullScreen
     });
   });
   function waitUntilActive() {
-    let doc = content.document;
-    if (doc.docShell.isActive && doc.hasFocus()) {
+    if (docShell.isActive && content.document.hasFocus()) {
       sendAsyncMessage("Test:Activated");
     } else {
       setTimeout(waitUntilActive, 10);
     }
   }
   waitUntilActive();
 }
 
--- a/browser/base/content/test/general/browser_e10s_about_page_triggeringprincipal.js
+++ b/browser/base/content/test/general/browser_e10s_about_page_triggeringprincipal.js
@@ -16,17 +16,17 @@ add_task(async function test_principal_c
   await kAboutPagesRegistered;
   await BrowserTestUtils.withNewTab("about:test-about-principal-parent", async function(browser) {
     let loadPromise = BrowserTestUtils.browserLoaded(browser, false, "about:test-about-principal-child");
     let myLink = browser.contentDocument.getElementById("aboutchildprincipal");
     myLink.click();
     await loadPromise;
 
     await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
-      let channel = content.document.docShell.currentDocumentChannel;
+      let channel = content.docShell.currentDocumentChannel;
       is(channel.originalURI.asciiSpec,
          "about:test-about-principal-child",
          "sanity check - make sure we test the principal for the correct URI");
 
       let triggeringPrincipal = channel.loadInfo.triggeringPrincipal;
       ok(Services.scriptSecurityManager.isSystemPrincipal(triggeringPrincipal),
          "loading about: from privileged page must have a triggering of System");
 
@@ -52,17 +52,17 @@ add_task(async function test_principal_c
     // simulate ctrl+click
     BrowserTestUtils.synthesizeMouseAtCenter("#aboutchildprincipal",
                                              { ctrlKey: true, metaKey: true },
                                              gBrowser.selectedBrowser);
     let tab = await loadPromise;
     gBrowser.selectTabAtIndex(2);
 
     await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
-      let channel = content.document.docShell.currentDocumentChannel;
+      let channel = content.docShell.currentDocumentChannel;
       is(channel.originalURI.asciiSpec,
          "about:test-about-principal-child",
          "sanity check - make sure we test the principal for the correct URI");
 
       let triggeringPrincipal = channel.loadInfo.triggeringPrincipal;
       ok(Services.scriptSecurityManager.isSystemPrincipal(triggeringPrincipal),
          "loading about: from privileged page must have a triggering of System");
 
@@ -97,17 +97,17 @@ add_task(async function test_principal_r
     BrowserTestUtils.synthesizeMouseAtCenter("#aboutchildprincipal",
                                              { type: "contextmenu", button: 2 },
                                              gBrowser.selectedBrowser);
 
     let tab = await loadPromise;
     gBrowser.selectTabAtIndex(2);
 
     await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
-      let channel = content.document.docShell.currentDocumentChannel;
+      let channel = content.docShell.currentDocumentChannel;
       is(channel.originalURI.asciiSpec,
          "about:test-about-principal-child",
          "sanity check - make sure we test the principal for the correct URI");
 
       let triggeringPrincipal = channel.loadInfo.triggeringPrincipal;
       ok(Services.scriptSecurityManager.isSystemPrincipal(triggeringPrincipal),
          "loading about: from privileged page must have a triggering of System");
 
--- a/browser/base/content/test/general/browser_fullscreen-window-open.js
+++ b/browser/base/content/test/general/browser_fullscreen-window-open.js
@@ -312,18 +312,17 @@ WindowListener.prototype = {
   test_title: null,
   test_url: null,
   callback_onSuccess: null,
   callBack_onFinalize: null,
 
   onOpenWindow(aXULWindow) {
     Services.wm.removeListener(this);
 
-    let domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindow);
+    let domwindow = aXULWindow.docShell.domWindow;
     let onLoad = aEvent => {
       is(domwindow.document.location.href, this.test_url,
         "Opened Window is expected: " + this.test_title);
       if (this.callback_onSuccess) {
         this.callback_onSuccess();
       }
 
       domwindow.removeEventListener("load", onLoad, true);
--- a/browser/base/content/test/static/browser_parsable_css.js
+++ b/browser/base/content/test/static/browser_parsable_css.js
@@ -351,17 +351,17 @@ add_task(async function checkAllTheCSS()
   let hiddenFrame = new HiddenFrame();
   let win = await hiddenFrame.get();
   let iframe = win.document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe");
   win.document.documentElement.appendChild(iframe);
   let iframeLoaded = BrowserTestUtils.waitForEvent(iframe, "load", true);
   iframe.contentWindow.location = testFile;
   await iframeLoaded;
   let doc = iframe.contentWindow.document;
-  doc.docShell.cssErrorReportingEnabled = true;
+  iframe.contentWindow.docShell.cssErrorReportingEnabled = true;
 
   // Parse and remove all manifests from the list.
   // NOTE that this must be done before filtering out devtools paths
   // so that all chrome paths can be recorded.
   let manifestURIs = [];
   uris = uris.filter(uri => {
     if (uri.pathQueryRef.endsWith(".manifest")) {
       manifestURIs.push(uri);
--- a/browser/components/customizableui/test/browser_panelUINotifications_fullscreen_noAutoHideToolbar.js
+++ b/browser/components/customizableui/test/browser_panelUINotifications_fullscreen_noAutoHideToolbar.js
@@ -5,17 +5,17 @@ ChromeUtils.import("resource://gre/modul
 function waitForDocshellActivated() {
   return ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
     // Setting docshell activated/deactivated will trigger visibility state
     // changes to relevant state ("visible" or "hidden"). AFAIK, there is no
     // such event notifying docshell is being activated, so I use
     // "visibilitychange" event rather than polling the docShell.isActive.
     await ContentTaskUtils.waitForEvent(content.document, "visibilitychange",
                                         true /* capture */, (aEvent) => {
-      return content.document.docShell.isActive;
+      return content.docShell.isActive;
     });
   });
 }
 
 function waitForFullscreen() {
   return Promise.all([
     BrowserTestUtils.waitForEvent(window, "fullscreen"),
     // In the platforms that support reporting occlusion state (e.g. Mac),
--- a/browser/components/extensions/parent/ext-browser.js
+++ b/browser/components/extensions/parent/ext-browser.js
@@ -607,17 +607,17 @@ class TabTracker extends TabTrackerBase 
 
   getBrowserData(browser) {
     let {gBrowser} = browser.ownerGlobal;
     // Some non-browser windows have gBrowser but not getTabForBrowser!
     if (!gBrowser || !gBrowser.getTabForBrowser) {
       if (browser.ownerDocument.documentURI === "about:addons") {
         // When we're loaded into a <browser> inside about:addons, we need to go up
         // one more level.
-        browser = browser.ownerDocument.docShell.chromeEventHandler;
+        browser = browser.ownerGlobal.docShell.chromeEventHandler;
 
         ({gBrowser} = browser.ownerGlobal);
       } else {
         return {
           tabId: -1,
           windowId: -1,
         };
       }
--- a/browser/components/feeds/WebContentConverter.js
+++ b/browser/components/feeds/WebContentConverter.js
@@ -282,22 +282,17 @@ WebContentConverterRegistrar.prototype =
                                        notificationBox.PRIORITY_INFO_LOW,
                                        [addButton]);
   },
 
   /**
    * Returns the browser chrome window in which the content window is in
    */
   _getBrowserWindowForContentWindow(aContentWindow) {
-    return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShellTreeItem)
-                         .rootTreeItem
-                         .QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDOMWindow)
+    return aContentWindow.docShell.rootTreeItem.domWindow
                          .wrappedJSObject;
   },
 
   /**
    * Returns the <xul:browser> element associated with the given content
    * window.
    *
    * @param aBrowserWindow
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -665,22 +665,17 @@ function handURIToExistingBrowser(uri, l
   var allowPrivate = forcePrivate || PrivateBrowsingUtils.permanentPrivateBrowsing;
   var navWin = BrowserWindowTracker.getTopWindow({private: allowPrivate});
   if (!navWin) {
     // if we couldn't load it in an existing window, open a new one
     openBrowserWindow(cmdLine, uri.spec, null, forcePrivate);
     return;
   }
 
-  var navNav = navWin.QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIWebNavigation);
-  var rootItem = navNav.QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem;
-  var rootWin = rootItem.QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIDOMWindow);
-  var bwin = rootWin.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow;
+  var bwin = navWin.docShell.rootTreeItem.domWindow;
   bwin.openURI(uri, null, location,
                Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL, triggeringPrincipal);
 }
 
 function nsDefaultCommandLineHandler() {
 }
 
 nsDefaultCommandLineHandler.prototype = {
--- a/browser/components/privatebrowsing/test/browser/browser_oa_private_browsing_window.js
+++ b/browser/components/privatebrowsing/test/browser/browser_oa_private_browsing_window.js
@@ -16,17 +16,17 @@ add_task(async function test_principal_r
     });
     BrowserTestUtils.synthesizeMouseAtCenter("#checkPrincipalOA",
                                              { type: "contextmenu", button: 2 },
                                              gBrowser.selectedBrowser);
     let privateWin = await promiseNewWindow;
 
     await ContentTask.spawn(privateWin.gBrowser.selectedBrowser, {DUMMY_PAGE, TEST_PAGE}, async function({DUMMY_PAGE, TEST_PAGE}) { // eslint-disable-line
 
-      let channel = content.document.docShell.currentDocumentChannel;
+      let channel = content.docShell.currentDocumentChannel;
       is(channel.URI.spec, DUMMY_PAGE,
          "sanity check to ensure we check principal for right URI");
 
       let triggeringPrincipal = channel.loadInfo.triggeringPrincipal;
       ok(triggeringPrincipal.isCodebasePrincipal,
          "sanity check to ensure principal is a codebasePrincipal");
       is(triggeringPrincipal.URI.spec, TEST_PAGE,
          "test page must be the triggering page");
--- a/browser/components/search/content/searchReset.js
+++ b/browser/components/search/content/searchReset.js
@@ -41,22 +41,17 @@ function doSearch() {
     }
   }
 
   let engine = Services.search.currentEngine;
   let submission = engine.getSubmission(queryString, null, purpose);
 
   window.removeEventListener("unload", recordPageClosed);
 
-  let win = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                  .getInterface(Ci.nsIWebNavigation)
-                  .QueryInterface(Ci.nsIDocShellTreeItem)
-                  .rootTreeItem
-                  .QueryInterface(Ci.nsIInterfaceRequestor)
-                  .getInterface(Ci.nsIDOMWindow);
+  let win = window.docShell.rootTreeItem.domWindow;
   win.openTrustedLinkIn(submission.uri.spec, "current", {
     allowThirdPartyFixup: false,
     postData: submission.postData,
   });
 }
 
 function openingSettings() {
   record(TELEMETRY_RESULT_ENUM.OPENED_SETTINGS);
--- a/browser/components/search/test/browser_webapi.js
+++ b/browser/components/search/test/browser_webapi.js
@@ -12,18 +12,17 @@ function AddSearchProvider(...args) {
 }
 
 function promiseDialogOpened() {
   return new Promise((resolve, reject) => {
     Services.wm.addListener({
       onOpenWindow(xulWin) {
         Services.wm.removeListener(this);
 
-        let win = xulWin.QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIDOMWindow);
+        let win = xulWin.docShell.domWindow;
         waitForFocus(() => {
           if (win.location == "chrome://global/content/commonDialog.xul")
             resolve(win);
           else
             reject();
         }, win);
       }
     });
--- a/browser/components/sessionstore/ContentRestore.jsm
+++ b/browser/components/sessionstore/ContentRestore.jsm
@@ -309,18 +309,17 @@ ContentRestoreInternal.prototype = {
    */
   restoreDocument() {
     if (!this._restoringDocument) {
       return;
     }
     let {formdata, scrollPositions} = this._restoringDocument;
     this._restoringDocument = null;
 
-    let window = this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIDOMWindow);
+    let window = this.docShell.domWindow;
 
     // Restore form data.
     restoreFrameTreeData(window, formdata, (frame, data) => {
       // restore() will return false, and thus abort restoration for the
       // current |frame| and its descendants, if |data.url| is given but
       // doesn't match the loaded document's URL.
       return FormData.restore(frame, data);
     });
--- a/browser/components/sessionstore/SessionStorage.jsm
+++ b/browser/components/sessionstore/SessionStorage.jsm
@@ -145,17 +145,17 @@ var SessionStorageInternal = {
         let dataPrincipal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin);
         principal = Services.scriptSecurityManager.createCodebasePrincipal(dataPrincipal.URI, attrs);
       } catch (e) {
         console.error(e);
         continue;
       }
 
       let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
-      let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+      let window = aDocShell.domWindow;
 
       // There is no need to pass documentURI, it's only used to fill documentURI property of
       // domstorage event, which in this case has no consumer. Prevention of events in case
       // of missing documentURI will be solved in a followup bug to bug 600307.
       let storage = storageManager.createStorage(window, principal, "", aDocShell.usePrivateBrowsing);
 
       for (let key of Object.keys(data)) {
         try {
@@ -174,17 +174,17 @@ var SessionStorageInternal = {
    *        That history entry uri
    * @param aDocShell
    *        A tab's docshell (containing the sessionStorage)
    */
   _readEntry(aPrincipal, aDocShell) {
     let hostData = {};
     let storage;
 
-    let window = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+    let window = aDocShell.domWindow;
 
     try {
       let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
       storage = storageManager.getStorage(window, aPrincipal);
       storage.length; // XXX: Bug 1232955 - storage.length can throw, catch that failure
     } catch (e) {
       // sessionStorage might throw if it's turned off, see bug 458954
       storage = null;
--- a/browser/components/sessionstore/content/aboutSessionRestore.js
+++ b/browser/components/sessionstore/content/aboutSessionRestore.js
@@ -220,19 +220,17 @@ function onListKeyDown(aEvent) {
       restoreSingleTab(ix, aEvent.shiftKey);
     break;
   }
 }
 
 // Helper functions
 
 function getBrowserWindow() {
-  return window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
-               .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
-               .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+  return window.docShell.rootTreeItem.domWindow;
 }
 
 function toggleRowChecked(aIx) {
   function isChecked(aItem) {
     return aItem.checked;
   }
 
   var item = gTreeData[aIx];
--- a/browser/components/shell/HeadlessShell.jsm
+++ b/browser/components/shell/HeadlessShell.jsm
@@ -25,18 +25,17 @@ function loadContentWindow(webNavigation
         // Ignore inner-frame events
         if (progress != webProgress) {
           return;
         }
         // Ignore events that don't change the document
         if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
           return;
         }
-        let contentWindow = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIDOMWindow);
+        let contentWindow = docShell.domWindow;
         progressListeners.delete(progressListener);
         webProgress.removeProgressListener(progressListener);
         contentWindow.addEventListener("load", (event) => {
           resolve(contentWindow);
         }, { once: true });
       },
       QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
                                               "nsISupportsWeakReference"])
--- a/browser/components/syncedtabs/util.js
+++ b/browser/components/syncedtabs/util.js
@@ -5,17 +5,11 @@
 "use strict";
 
 var EXPORTED_SYMBOLS = [
   "getChromeWindow"
 ];
 
 // Get the chrome (ie, browser) window hosting this content.
 function getChromeWindow(window) {
-  return window
-         .QueryInterface(Ci.nsIInterfaceRequestor)
-         .getInterface(Ci.nsIWebNavigation)
-         .QueryInterface(Ci.nsIDocShellTreeItem)
-         .rootTreeItem
-         .QueryInterface(Ci.nsIInterfaceRequestor)
-         .getInterface(Ci.nsIDOMWindow)
+  return window.docShell.rootTreeItem.domWindow
          .wrappedJSObject;
 }
--- a/browser/extensions/pdfjs/test/browser_pdfjs_savedialog.js
+++ b/browser/extensions/pdfjs/test/browser_pdfjs_savedialog.js
@@ -42,18 +42,17 @@ function changeMimeHandler(preferredActi
 }
 
 function addWindowListener(aURL, aCallback) {
   Services.wm.addListener({
     onOpenWindow(aXULWindow) {
       info("window opened, waiting for focus");
       Services.wm.removeListener(this);
 
-      var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                .getInterface(Ci.nsIDOMWindow);
+      var domwindow = aXULWindow.docShell.domWindow;
       waitForFocus(function() {
         is(domwindow.document.location.href, aURL, "should have seen the right window open");
         domwindow.close();
         aCallback();
       }, domwindow);
     },
     onCloseWindow(aXULWindow) { },
   });
--- a/browser/modules/ClickEventHandler.jsm
+++ b/browser/modules/ClickEventHandler.jsm
@@ -84,17 +84,17 @@ class ClickEventHandler {
         json.title = node.getAttribute("title");
       }
       json.noReferrer = BrowserUtils.linkHasNoReferrer(node);
 
       // Check if the link needs to be opened with mixed content allowed.
       // Only when the owner doc has |mixedContentChannel| and the same origin
       // should we allow mixed content.
       json.allowMixedContent = false;
-      let docshell = ownerDoc.docShell;
+      let docshell = ownerDoc.defaultView.docShell;
       if (this.mm.docShell.mixedContentChannel) {
         const sm = Services.scriptSecurityManager;
         try {
           let targetURI = Services.io.newURI(href);
           sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false);
           json.allowMixedContent = true;
         } catch (e) {}
       }
--- a/browser/modules/ContentLinkHandler.jsm
+++ b/browser/modules/ContentLinkHandler.jsm
@@ -8,23 +8,24 @@ const EXPORTED_SYMBOLS = ["ContentLinkHa
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyGlobalGetters(this, ["Blob", "FileReader"]);
 
 ChromeUtils.defineModuleGetter(this, "Feeds",
   "resource:///modules/Feeds.jsm");
-ChromeUtils.defineModuleGetter(this, "NetUtil",
-  "resource://gre/modules/NetUtil.jsm");
 ChromeUtils.defineModuleGetter(this, "DeferredTask",
   "resource://gre/modules/DeferredTask.jsm");
 ChromeUtils.defineModuleGetter(this, "PromiseUtils",
   "resource://gre/modules/PromiseUtils.jsm");
 
+const BinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1",
+                                                 "nsIBinaryInputStream", "setInputStream");
+
 const SIZES_TELEMETRY_ENUM = {
   NO_SIZES: 0,
   ANY: 1,
   DIMENSION: 2,
   INVALID: 3,
 };
 
 const FAVICON_PARSING_TIMEOUT = 100;
@@ -64,26 +65,25 @@ function promiseBlobAsOctets(blob) {
   });
 }
 
 class FaviconLoad {
   constructor(iconInfo) {
     this.buffers = [];
     this.icon = iconInfo;
 
-    this.channel = NetUtil.newChannel({
-      uri: iconInfo.iconUri,
-      loadingNode: iconInfo.node,
-      loadingPrincipal: iconInfo.node.nodePrincipal,
-      triggeringPrincipal: iconInfo.node.nodePrincipal,
-      contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON,
-      securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
-                     Ci.nsILoadInfo.SEC_ALLOW_CHROME |
-                     Ci.nsILoadInfo.SEC_DISALLOW_SCRIPT,
-    });
+    this.channel = Services.io.newChannelFromURI2(
+      iconInfo.iconUri,
+      iconInfo.node,
+      iconInfo.node.nodePrincipal,
+      iconInfo.node.nodePrincipal,
+      (Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
+       Ci.nsILoadInfo.SEC_ALLOW_CHROME |
+       Ci.nsILoadInfo.SEC_DISALLOW_SCRIPT),
+      Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON);
 
     this.channel.loadFlags |= Ci.nsIRequest.LOAD_BACKGROUND;
     // Sometimes node is a document and sometimes it is an element. This is
     // the easiest single way to get to the load group in both those cases.
     this.channel.loadGroup = iconInfo.node.ownerGlobal.document.documentLoadGroup;
     this.channel.notificationCallbacks = this;
 
     if (Services.prefs.getBoolPref("network.http.tailing.enabled", true) &&
@@ -116,18 +116,20 @@ class FaviconLoad {
 
     this.channel.cancel(Cr.NS_BINDING_ABORTED);
   }
 
   onStartRequest(request, context) {
   }
 
   onDataAvailable(request, context, inputStream, offset, count) {
-    let data = NetUtil.readInputStreamToString(inputStream, count);
-    this.buffers.push(Uint8Array.from(data, c => c.charCodeAt(0)));
+    let stream = new BinaryInputStream(inputStream);
+    let buffer = new ArrayBuffer(count);
+    stream.readArrayBuffer(buffer.byteLength, buffer);
+    this.buffers.push(new Uint8Array(buffer));
   }
 
   asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) {
     if (oldChannel == this.channel) {
       this.channel = newChannel;
     }
 
     callback.onRedirectVerifyCallback(Cr.NS_OK);
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -22,18 +22,18 @@ XPCOMUtils.defineLazyPreferenceGetter(th
 const DEFAULT_EXTENSION_ICON = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
 
 const BROWSER_PROPERTIES = "chrome://browser/locale/browser.properties";
 const BRAND_PROPERTIES = "chrome://branding/locale/brand.properties";
 
 const HTML_NS = "http://www.w3.org/1999/xhtml";
 
 function getTabBrowser(browser) {
-  while (browser.ownerDocument.docShell.itemType !== Ci.nsIDocShell.typeChrome) {
-    browser = browser.ownerDocument.docShell.chromeEventHandler;
+  while (browser.ownerGlobal.docShell.itemType !== Ci.nsIDocShell.typeChrome) {
+    browser = browser.ownerGlobal.docShell.chromeEventHandler;
   }
   return {browser, window: browser.ownerGlobal};
 }
 
 var ExtensionsUI = {
   sideloaded: new Set(),
   updates: new Set(),
   sideloadListener: null,
@@ -101,17 +101,17 @@ var ExtensionsUI = {
       AppMenuNotifications.showBadgeOnlyNotification("addon-alert");
     }
     this.emit("change");
   },
 
   showAddonsManager(browser, strings, icon, histkey) {
     let global = browser.selectedBrowser.ownerGlobal;
     return global.BrowserOpenAddonsMgr("addons://list/extension").then(aomWin => {
-      let aomBrowser = aomWin.document.docShell.chromeEventHandler;
+      let aomBrowser = aomWin.docShell.chromeEventHandler;
       return this.showPermissionsPrompt(aomBrowser, strings, icon, histkey);
     });
   },
 
   showSideloaded(browser, addon) {
     addon.markAsSeen();
     this.sideloaded.delete(addon);
     this._updateNotifications();
--- a/browser/modules/NetErrorContent.jsm
+++ b/browser/modules/NetErrorContent.jsm
@@ -547,17 +547,17 @@ var NetErrorContent = {
     let hideAddExceptionButton = false;
 
     if (this.isAboutCertError(win.document)) {
       this.onCertError(global, originalTarget, win);
       hideAddExceptionButton =
         Services.prefs.getBoolPref("security.certerror.hideAddException", false);
     }
     if (this.isAboutNetError(win.document)) {
-      let docShell = win.document.docShell;
+      let docShell = win.docShell;
       if (docShell) {
         let {securityInfo} = docShell.failedChannel;
         // We don't have a securityInfo when this is for example a DNS error.
         if (securityInfo) {
           securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
           let msg = this._getErrorMessageFromCode(securityInfo,
                                                   win.document);
           let id = win.document.getElementById("errorShortDescText");
@@ -592,30 +592,30 @@ var NetErrorContent = {
   onSetAutomatic(global, evt) {
     global.sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
       automatic: evt.detail
     });
 
     // If we're enabling reports, send a report for this failure.
     if (evt.detail) {
       let win = evt.originalTarget.ownerGlobal;
-      let docShell = win.document.docShell;
+      let docShell = win.docShell;
 
       let {securityInfo} = docShell.failedChannel;
       securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
       let {host, port} = win.document.mozDocumentURIIfNotForErrorPages;
 
       let errorReporter = Cc["@mozilla.org/securityreporter;1"]
                             .getService(Ci.nsISecurityReporter);
       errorReporter.reportTLSError(securityInfo, host, port);
     }
   },
 
   onCertError(global, targetElement, win) {
-    let docShell = win.document.docShell;
+    let docShell = win.docShell;
     global.sendAsyncMessage("Browser:CertExceptionError", {
       frameId: WebNavigationFrames.getFrameId(win),
       location: win.document.location.href,
       elementId: targetElement.getAttribute("id"),
       isTopFrame: (win.parent === win),
       securityInfoAsString: getSerializedSecurityInfo(docShell),
     });
   },
--- a/browser/modules/PageStyleHandler.jsm
+++ b/browser/modules/PageStyleHandler.jsm
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 var EXPORTED_SYMBOLS = ["PageStyleHandler"];
 
 var PageStyleHandler = {
   getViewer(content) {
-    return content.document.docShell.contentViewer;
+    return content.docShell.contentViewer;
   },
 
   sendStyleSheetInfo(mm) {
     let content = mm.content;
     let filteredStyleSheets = this._filterStyleSheets(this.getAllStyleSheets(content), content);
 
     mm.sendAsyncMessage("PageStyle:StyleSheets", {
       filteredStyleSheets,
@@ -45,17 +45,17 @@ var PageStyleHandler = {
   },
 
   handleEvent(event) {
     let win = event.target.ownerGlobal;
     if (win != win.top) {
       return;
     }
 
-    let mm = win.document.docShell
+    let mm = win.docShell
                 .QueryInterface(Ci.nsIInterfaceRequestor)
                 .getInterface(Ci.nsIContentFrameMessageManager);
     this.sendStyleSheetInfo(mm);
   },
 
   _stylesheetSwitchAll(frameset, title) {
     if (!title || this._stylesheetInFrame(frameset, title)) {
       this._stylesheetSwitchFrame(frameset, title);
--- a/browser/modules/test/browser/contentSearch.js
+++ b/browser/modules/test/browser/contentSearch.js
@@ -47,18 +47,18 @@ addMessageListener(TEST_MSG, msg => {
 
 // We need to keep a reference to the listener as webProgress records a weak
 // reference, hence we need to stop garbage collection cleaning it up before the
 // test is completed.
 var webProgressListener;
 
 function waitForLoadAndStopIt(expectedURL) {
   return new Promise(resolve => {
-    let webProgress = content.document.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                                               .getInterface(Ci.nsIWebProgress);
+    let webProgress = content.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                                      .getInterface(Ci.nsIWebProgress);
     webProgressListener = {
       onStateChange(webProg, req, flags, status) {
         if (req instanceof Ci.nsIChannel) {
           let url = req.originalURI.spec;
           dump("waitForLoadAndStopIt: onStateChange " + url + "\n");
           let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
                          Ci.nsIWebProgressListener.STATE_START;
           if ((flags & docStart) && webProg.isTopLevel && url == expectedURL) {
--- a/devtools/client/debugger/test/mochitest/head.js
+++ b/devtools/client/debugger/test/mochitest/head.js
@@ -70,20 +70,17 @@ var helpersjs = testDir + "/../../../com
 Services.scriptloader.loadSubScript(helpersjs, this);
 
 function addWindow(aUrl) {
   info("Adding window: " + aUrl);
   return promise.resolve(getChromeWindow(window.open(aUrl)));
 }
 
 function getChromeWindow(aWindow) {
-  return aWindow
-    .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation)
-    .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
-    .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+  return aWindow.docShell.rootTreeItem.domWindow;
 }
 
 // Override addTab/removeTab as defined by shared-head, since these have
 // an extra window parameter and add a frame script
 this.addTab = function addTab(aUrl, aWindow) {
   info("Adding tab: " + aUrl);
 
   let deferred = promise.defer();
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -514,23 +514,17 @@ var gDevToolsBrowser = exports.gDevTools
           default:
             throw Error("invalid thread client state in slow script debug handler: " +
                         threadClient.state);
         }
       });
     }
 
     debugService.activationHandler = function(window) {
-      const chromeWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                                .getInterface(Ci.nsIWebNavigation)
-                                .QueryInterface(Ci.nsIDocShellTreeItem)
-                                .rootTreeItem
-                                .QueryInterface(Ci.nsIInterfaceRequestor)
-                                .getInterface(Ci.nsIDOMWindow)
-                                .QueryInterface(Ci.nsIDOMChromeWindow);
+      const chromeWindow = window.docShell.rootTreeItem.domWindow;
 
       let setupFinished = false;
       slowScriptDebugHandler(chromeWindow.gBrowser.selectedTab,
         () => {
           setupFinished = true;
         });
 
       // Don't return from the interrupt handler until the debugger is brought
--- a/devtools/client/jsonview/test/browser_json_refresh.js
+++ b/devtools/client/jsonview/test/browser_json_refresh.js
@@ -13,17 +13,17 @@ add_task(async function() {
   // generate file:// URI for JSON file and load in new tab
   const dir = getChromeDir(getResolvedURI(gTestPath));
   dir.append(TEST_JSON_FILE);
   const uri = Services.io.newFileURI(dir);
   const tab = await addJsonViewTab(uri.spec);
 
   // perform sanity checks for URI and pricnipals in loadInfo
   await ContentTask.spawn(tab.linkedBrowser, {TEST_JSON_FILE}, async function ({TEST_JSON_FILE}) { // eslint-disable-line
-    const channel = content.document.docShell.currentDocumentChannel;
+    const channel = content.docShell.currentDocumentChannel;
     const channelURI = channel.URI.spec;
     ok(channelURI.startsWith("file://") && channelURI.includes(TEST_JSON_FILE),
        "sanity: correct channel uri");
     const contentPolicyType = channel.loadInfo.externalContentPolicyType;
     is(contentPolicyType, Ci.nsIContentPolicy.TYPE_DOCUMENT,
        "sanity: correct contentPolicyType");
 
     const loadingPrincipal = channel.loadInfo.loadingPrincipal;
@@ -39,17 +39,17 @@ add_task(async function() {
 
   // reload the tab
   const loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   tab.linkedBrowser.reload();
   await loaded;
 
   // check principals in loadInfo are still correct
   await ContentTask.spawn(tab.linkedBrowser, {TEST_JSON_FILE}, async function ({TEST_JSON_FILE}) { // eslint-disable-line
-    const channel = content.document.docShell.currentDocumentChannel;
+    const channel = content.docShell.currentDocumentChannel;
     const channelURI = channel.URI.spec;
     ok(channelURI.startsWith("file://") && channelURI.includes(TEST_JSON_FILE),
        "reloaded: correct channel uri");
     const contentPolicyType = channel.loadInfo.externalContentPolicyType;
     is(contentPolicyType, Ci.nsIContentPolicy.TYPE_DOCUMENT,
        "reloaded: correct contentPolicyType");
 
     const loadingPrincipal = channel.loadInfo.loadingPrincipal;
--- a/devtools/client/responsive.html/utils/window.js
+++ b/devtools/client/responsive.html/utils/window.js
@@ -1,27 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { Ci } = require("chrome");
 const Services = require("Services");
 
 /**
  * Returns the `nsIDOMWindow` toplevel window for any child/inner window
  */
 function getToplevelWindow(window) {
-  return window.QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIWebNavigation)
-               .QueryInterface(Ci.nsIDocShellTreeItem)
-               .rootTreeItem
-               .QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIDOMWindow);
+  return window.docShell.rootTreeItem.domWindow;
 }
 exports.getToplevelWindow = getToplevelWindow;
 
 function getDOMWindowUtils(window) {
   return window.windowUtils;
 }
 exports.getDOMWindowUtils = getDOMWindowUtils;
 
--- a/devtools/client/webconsole/test/fixtures/stub-generators/head.js
+++ b/devtools/client/webconsole/test/fixtures/stub-generators/head.js
@@ -368,17 +368,17 @@ async function generateCssMessageStubs()
         resolve();
       });
     });
 
     await ContentTask.spawn(
       gBrowser.selectedBrowser,
       [key, code],
       function([subKey, subCode]) {
-        content.document.docShell.cssErrorReportingEnabled = true;
+        content.docShell.cssErrorReportingEnabled = true;
         const style = content.document.createElement("style");
         // eslint-disable-next-line no-unsanitized/property
         style.innerHTML = subCode;
         content.document.body.appendChild(style);
       }
     );
 
     await received;
--- a/devtools/client/webide/modules/runtimes.js
+++ b/devtools/client/webide/modules/runtimes.js
@@ -1,15 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {Ci} = require("chrome");
 const Services = require("Services");
 const {Devices} = require("resource://devtools/shared/apps/Devices.jsm");
 const {DebuggerServer} = require("devtools/server/main");
 const discovery = require("devtools/shared/discovery/discovery");
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
 loader.lazyRequireGetter(this, "AuthenticationResult",
   "devtools/shared/security/auth", true);
@@ -386,18 +385,17 @@ WiFiRuntime.prototype = {
     if (authResult != AuthenticationResult.PENDING) {
       throw new Error("Expected PENDING result, got " + authResult);
     }
 
     // Listen for the window our prompt opens, so we can close it programatically
     let promptWindow;
     const windowListener = {
       onOpenWindow(xulWindow) {
-        const win = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsIDOMWindow);
+        const win = xulWindow.docShell.domWindow;
         win.addEventListener("load", function() {
           if (win.document.documentElement.getAttribute("id") != WINDOW_ID) {
             return;
           }
           // Found the window
           promptWindow = win;
           Services.wm.removeListener(windowListener);
         }, {once: true});
--- a/devtools/server/actors/addon/webextension-inspected-window.js
+++ b/devtools/server/actors/addon/webextension-inspected-window.js
@@ -178,19 +178,17 @@ CustomizedReload.prototype = {
                                 .QueryInterface(Ci.nsIDocShell);
 
     // Keep track of the set of window objects where we are going to inject
     // the injectedScript: the top level window and all its descendant
     // that are still of type content (filtering out loaded XUL pages, if any).
     if (window == this.window) {
       this.customizedReloadWindows.add(window);
     } else if (subjectDocShell.sameTypeParent) {
-      const parentWindow = subjectDocShell.sameTypeParent
-                                        .QueryInterface(Ci.nsIInterfaceRequestor)
-                                        .getInterface(Ci.nsIDOMWindow);
+      const parentWindow = subjectDocShell.sameTypeParent.domWindow;
       if (parentWindow && this.customizedReloadWindows.has(parentWindow)) {
         this.customizedReloadWindows.add(window);
       }
     }
 
     if (this.customizedReloadWindows.has(window)) {
       const {
         apiErrorResult
--- a/devtools/server/actors/targets/browsing-context.js
+++ b/devtools/server/actors/targets/browsing-context.js
@@ -57,34 +57,34 @@ function getWindowID(window) {
 }
 
 function getDocShellChromeEventHandler(docShell) {
   let handler = docShell.chromeEventHandler;
   if (!handler) {
     try {
       // Toplevel xul window's docshell doesn't have chromeEventHandler
       // attribute. The chrome event handler is just the global window object.
-      handler = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                        .getInterface(Ci.nsIDOMWindow);
+      handler = docShell.domWindow;
     } catch (e) {
       // ignore
     }
   }
   return handler;
 }
 
 function getChildDocShells(parentDocShell) {
   const docShellsEnum = parentDocShell.getDocShellEnumerator(
     Ci.nsIDocShellTreeItem.typeAll,
     Ci.nsIDocShell.ENUMERATE_FORWARDS
   );
 
   const docShells = [];
   while (docShellsEnum.hasMoreElements()) {
     const docShell = docShellsEnum.getNext();
+    docShell.QueryInterface(Ci.nsIDocShell);
     docShell.QueryInterface(Ci.nsIInterfaceRequestor)
             .getInterface(Ci.nsIWebProgress);
     docShells.push(docShell);
   }
   return docShells;
 }
 
 exports.getChildDocShells = getChildDocShells;
@@ -340,19 +340,17 @@ const browsingContextTargetPrototype = {
   },
 
   /**
    * Getter for the browsing context's current DOM window.
    */
   get window() {
     // On xpcshell, there is no document
     if (this.docShell) {
-      return this.docShell
-        .QueryInterface(Ci.nsIInterfaceRequestor)
-        .getInterface(Ci.nsIDOMWindow);
+      return this.docShell.domWindow;
     }
     return null;
   },
 
   get outerWindowID() {
     if (this.window) {
       return this.window.windowUtils.outerWindowID;
     }
@@ -376,18 +374,17 @@ const browsingContextTargetPrototype = {
   },
 
   /**
    * Getter for the list of all content DOM windows in the browsing context.
    * @return {Array}
    */
   get windows() {
     return this.docShells.map(docShell => {
-      return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIDOMWindow);
+      return docShell.domWindow;
     });
   },
 
   /**
    * Getter for the original docShell this actor got attached to in the first
    * place.
    * Note that your actor should normally *not* rely on this top level docShell
    * if you want it to show information relative to the iframe that's currently
@@ -1483,18 +1480,17 @@ DebuggerProgressListener.prototype = {
     this._knownWindowIDs.clear();
     this._knownWindowIDs = null;
   },
 
   watch(docShell) {
     // Add the docshell to the watched set. We're actually adding the window,
     // because docShell objects are not wrappercached and would be rejected
     // by the WeakSet.
-    const docShellWindow = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIDOMWindow);
+    const docShellWindow = docShell.domWindow;
     this._watchedDocShells.add(docShellWindow);
 
     const webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebProgress);
     webProgress.addProgressListener(this,
                                     Ci.nsIWebProgress.NOTIFY_STATE_WINDOW |
                                     Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
 
@@ -1506,18 +1502,17 @@ DebuggerProgressListener.prototype = {
     // Dispatch the _windowReady event on the targetActor for pre-existing windows
     for (const win of this._getWindowsInDocShell(docShell)) {
       this._targetActor._windowReady(win);
       this._knownWindowIDs.set(getWindowID(win), win);
     }
   },
 
   unwatch(docShell) {
-    const docShellWindow = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIDOMWindow);
+    const docShellWindow = docShell.domWindow;
     if (!this._watchedDocShells.has(docShellWindow)) {
       return;
     }
 
     const webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebProgress);
     // During process shutdown, the docshell may already be cleaned up and throw
     try {
@@ -1534,18 +1529,17 @@ DebuggerProgressListener.prototype = {
 
     for (const win of this._getWindowsInDocShell(docShell)) {
       this._knownWindowIDs.delete(getWindowID(win));
     }
   },
 
   _getWindowsInDocShell(docShell) {
     return getChildDocShells(docShell).map(d => {
-      return d.QueryInterface(Ci.nsIInterfaceRequestor)
-              .getInterface(Ci.nsIDOMWindow);
+      return d.domWindow;
     });
   },
 
   onWindowCreated: DevToolsUtils.makeInfallible(function(evt) {
     if (!this._targetActor.attached) {
       return;
     }
 
--- a/devtools/server/actors/targets/webextension.js
+++ b/devtools/server/actors/targets/webextension.js
@@ -160,18 +160,17 @@ webExtensionTargetPrototype._createFallb
   }
 
   // Create an empty hidden window as a fallback (e.g. the background page could be
   // not defined for the target add-on or not yet when the actor instance has been
   // created).
   this.fallbackWebNav = Services.appShell.createWindowlessBrowser(true);
 
   // Save the reference to the fallback DOMWindow.
-  this.fallbackWindow = this.fallbackWebNav.QueryInterface(Ci.nsIInterfaceRequestor)
-                                           .getInterface(Ci.nsIDOMWindow);
+  this.fallbackWindow = this.fallbackWebNav.document.defaultView;
 
   // Insert the fallback doc message.
   this.fallbackWindow.document.body.innerText = FALLBACK_DOC_MESSAGE;
 };
 
 webExtensionTargetPrototype._destroyFallbackWindow = function() {
   if (this.fallbackWebNav) {
     // Explicitly close the fallback windowless browser to prevent it to leak
@@ -268,21 +267,18 @@ webExtensionTargetPrototype._docShellToW
 
   const webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                             .getInterface(Ci.nsIWebProgress);
   const window = webProgress.DOMWindow;
 
   // Collect the addonID from the document origin attributes and its sameType top level
   // frame.
   const addonID = window.document.nodePrincipal.addonId;
-  const sameTypeRootAddonID = docShell.QueryInterface(Ci.nsIDocShellTreeItem)
-                                    .sameTypeRootTreeItem
-                                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIDOMWindow)
-                                    .document.nodePrincipal.addonId;
+  const sameTypeRootAddonID = docShell.sameTypeRootTreeItem.domWindow
+                                      .document.nodePrincipal.addonId;
 
   return Object.assign(baseWindowDetails, {
     addonID,
     sameTypeRootAddonID,
   });
 };
 
 /**
@@ -299,20 +295,17 @@ webExtensionTargetPrototype._docShellsTo
 };
 
 webExtensionTargetPrototype.isExtensionWindow = function(window) {
   return window.document.nodePrincipal.addonId == this.id;
 };
 
 webExtensionTargetPrototype.isExtensionWindowDescendent = function(window) {
   // Check if the source is coming from a descendant docShell of an extension window.
-  const docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDocShell);
-  const rootWin = docShell.sameTypeRootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
-                                             .getInterface(Ci.nsIDOMWindow);
+  const rootWin = window.docShell.sameTypeRootTreeItem.domWindow;
   return this.isExtensionWindow(rootWin);
 };
 
 /**
  * Return true if the given source is associated with this addon and should be
  * added to the visible sources (retrieved and used by the webbrowser actor module).
  */
 webExtensionTargetPrototype._allowSource = function(source) {
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -652,18 +652,19 @@ DevToolsUtils.makeInfallible(function(wi
   window = window.QueryInterface(Ci.nsIInterfaceRequestor)
                  .getInterface(Ci.nsIDOMWindow);
 
   window.addEventListener("load", handleLoad);
 }, "BrowserTabList.prototype.onOpenWindow");
 
 BrowserTabList.prototype.onCloseWindow =
 DevToolsUtils.makeInfallible(function(window) {
-  window = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                   .getInterface(Ci.nsIDOMWindow);
+  if (window instanceof Ci.nsIXULWindow) {
+    window = window.docShell.domWindow;
+  }
 
   if (appShellDOMWindowType(window) !== DebuggerServer.chromeWindowType) {
     return;
   }
 
   /*
    * nsIWindowMediator deadlocks if you call its GetEnumerator method from
    * a nsIWindowMediatorListener's onCloseWindow hook (bug 873589), so
--- a/devtools/shared/security/prompt.js
+++ b/devtools/shared/security/prompt.js
@@ -1,17 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-var { Ci } = require("chrome");
 var Services = require("Services");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 loader.lazyRequireGetter(this, "AuthenticationResult",
   "devtools/shared/security/auth", true);
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/shared/locales/debugger.properties");
 
@@ -55,18 +54,17 @@ Client.defaultSendOOB = ({ authResult, o
   const msg = `${header}\n\n${hashMsg}\n${tokenMsg}`;
   const prompt = Services.prompt;
   const flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_CANCEL;
 
   // Listen for the window our prompt opens, so we can close it programatically
   let promptWindow;
   const windowListener = {
     onOpenWindow(xulWindow) {
-      const win = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDOMWindow);
+      const win = xulWindow.docShell.domWindow;
       win.addEventListener("load", function() {
         if (win.document.documentElement.getAttribute("id") != "commonDialog") {
           return;
         }
         // Found the window
         promptWindow = win;
         Services.wm.removeListener(windowListener);
       }, {once: true});
--- a/devtools/shared/webconsole/test/test_cached_messages.html
+++ b/devtools/shared/webconsole/test/test_cached_messages.html
@@ -7,17 +7,17 @@
   <script type="text/javascript" src="common.js"></script>
   <!-- Any copyright is dedicated to the Public Domain.
      - http://creativecommons.org/publicdomain/zero/1.0/ -->
 </head>
 <body>
 <p>Test for cached messages</p>
 
 <script class="testbody" type="application/javascript">
-document.docShell.cssErrorReportingEnabled = true;
+window.docShell.cssErrorReportingEnabled = true;
 var ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
                           .getService(Ci.nsIConsoleAPIStorage);
 let expectedConsoleCalls = [];
 let expectedPageErrors = [];
 
 function doPageErrors() {
   Services.console.reset();
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -3959,16 +3959,29 @@ nsDocShell::GetWindow()
 {
   if (NS_FAILED(EnsureScriptEnvironment())) {
     return nullptr;
   }
   return mScriptGlobal->AsOuter();
 }
 
 NS_IMETHODIMP
+nsDocShell::GetDomWindow(mozIDOMWindowProxy** aWindow)
+{
+  NS_ENSURE_ARG_POINTER(aWindow);
+
+  nsresult rv = EnsureScriptEnvironment();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsPIDOMWindowOuter> window = mScriptGlobal->AsOuter();
+  window.forget(aWindow);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::SetDeviceSizeIsPageSize(bool aValue)
 {
   if (mDeviceSizeIsPageSize != aValue) {
     mDeviceSizeIsPageSize = aValue;
     RefPtr<nsPresContext> presContext;
     GetPresContext(getter_AddRefs(presContext));
     if (presContext) {
       presContext->MediaFeatureValuesChanged({
--- a/docshell/base/nsIDocShellTreeItem.idl
+++ b/docshell/base/nsIDocShellTreeItem.idl
@@ -1,21 +1,21 @@
 /* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
+interface mozIDOMWindowProxy;
 interface nsIDocShellTreeOwner;
 interface nsIDocument;
 interface nsPIDOMWindowOuter;
 
-
 /**
  * The nsIDocShellTreeItem supplies the methods that are required of any item
  * that wishes to be able to live within the docshell tree either as a middle
  * node or a leaf. 
  */
 
 [scriptable, uuid(9b7c586f-9214-480c-a2c4-49b526fff1a6)]
 interface nsIDocShellTreeItem : nsISupports
@@ -174,12 +174,17 @@ interface nsIDocShellTreeItem : nsISuppo
 	Note the search is depth first when recursing.
 	*/
 	nsIDocShellTreeItem findChildWithName(in AString aName,
 	                                      in boolean aRecurse,
 	                                      in boolean aSameType,
 	                                      in nsIDocShellTreeItem aRequestor,
 	                                      in nsIDocShellTreeItem aOriginalRequestor);
 
+  /**
+   * Returns the DOM outer window for the content viewer.
+   */
+  readonly attribute mozIDOMWindowProxy domWindow;
+
   [noscript,nostdcall,notxpcom] nsIDocument getDocument();
   [noscript,nostdcall,notxpcom] nsPIDOMWindowOuter getWindow();
 };
 
--- a/docshell/test/chrome/test_docRedirect.xul
+++ b/docshell/test/chrome/test_docRedirect.xul
@@ -66,17 +66,17 @@ https://bugzilla.mozilla.org/show_bug.cg
           iid.equals(Ci.nsISupportsWeakReference))
         return this;
       throw Cr.NS_ERROR_NO_INTERFACE;
     }
   }
 
   var webNav = Cc["@mozilla.org/appshell/appShellService;1"].
       getService(Ci.nsIAppShellService).createWindowlessBrowser(true);
-  var docShell = webNav.QueryInterface(Ci.nsIInterfaceRequestor).
+  let docShell = webNav.QueryInterface(Ci.nsIInterfaceRequestor).
       getInterface(Ci.nsIDocShell);
   docShell.createAboutBlankContentViewer(
       Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal));
 
   progressListener.add(docShell, function(success) {
     webNav.close();
     SimpleTest.is(success, true, "Received document redirect event");
     SimpleTest.finish();
--- a/docshell/test/chrome/test_private_hidden_window.html
+++ b/docshell/test/chrome/test_private_hidden_window.html
@@ -13,22 +13,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none">
   <iframe name="target"></iframe>
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
-var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIWebNavigation)
-                       .QueryInterface(Ci.nsIDocShellTreeItem)
-                       .rootTreeItem
-                       .QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDOMWindow);
+var mainWindow = window.docShell.rootTreeItem.domWindow;
 
 // We need to wait for the hidden window to load, but can't access
 // an event target for a regular event listener.
 var hidden = mainWindow.Services.appShell.hiddenPrivateDOMWindow;
 
 function isNotLoaded() {
   return !["complete", "interactive"].includes(hidden.document.readyState);
 }
--- a/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html
+++ b/docshell/test/navigation/file_triggeringprincipal_subframe_nav.html
@@ -3,17 +3,17 @@
 <head><meta charset="utf-8"></head>
 <body onload="checkResults()">
 <b>Sub Frame 2 Navigated</b><br/>
 
 <script type='application/javascript'>
   function checkResults() {
   	// query the uri of the loadingPrincipal and the TriggeringPrincipal and pass
   	// that information on to the parent for verification.
-    var channel = SpecialPowers.wrap(document).docShell.currentDocumentChannel;
+    var channel = SpecialPowers.wrap(window).docShell.currentDocumentChannel;
     var triggeringPrincipalURI = channel.loadInfo.triggeringPrincipal.URI.asciiSpec;
     var loadingPrincipalURI = channel.loadInfo.loadingPrincipal.URI.asciiSpec;
     var referrerURI = document.referrer;
     window.parent.parent.postMessage({triggeringPrincipalURI,
     	                              loadingPrincipalURI,
     	                              referrerURI}, '*');
   }
 </script>
--- a/docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html
+++ b/docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html
@@ -46,17 +46,17 @@ const TRIGGERING_PRINCIPAL_URI =
 const LOADING_PRINCIPAL_URI =
   "http://mochi.test:8888/tests/docshell/test/navigation/test_triggeringprincipal_iframe_iframe_window_open.html";
 
 var frameA = document.getElementById("framea");
 
 function checkResults() {
   frameA.removeEventListener('load', checkResults);
 
-  var channel = SpecialPowers.wrap(frameA.contentDocument).docShell.currentDocumentChannel;
+  var channel = SpecialPowers.wrap(frameA.contentWindow).docShell.currentDocumentChannel;
   var triggeringPrincipal = channel.loadInfo.triggeringPrincipal.URI.asciiSpec;
   var loadingPrincipal = channel.loadInfo.loadingPrincipal.URI.asciiSpec;
 
   is(triggeringPrincipal, TRIGGERING_PRINCIPAL_URI,
     "TriggeringPrincipal for targeted window.open() should be the iframe triggering the load");
 
   is(frameA.contentDocument.referrer, TRIGGERING_PRINCIPAL_URI,
     "Referrer for targeted window.open() should be the principal of the iframe triggering the load");
--- a/docshell/test/navigation/test_triggeringprincipal_parent_iframe_window_open.html
+++ b/docshell/test/navigation/test_triggeringprincipal_parent_iframe_window_open.html
@@ -32,17 +32,17 @@ const TRIGGERING_PRINCIPAL_URI =
 
 const LOADING_PRINCIPAL_URI = TRIGGERING_PRINCIPAL_URI;
 
 var testframe = document.getElementById("testframe");
 
 function checkResults() {
   testframe.removeEventListener('load', checkResults);
 
-  var channel = SpecialPowers.wrap(testframe.contentDocument).docShell.currentDocumentChannel;
+  var channel = SpecialPowers.wrap(testframe.contentWindow).docShell.currentDocumentChannel;
   var triggeringPrincipal = channel.loadInfo.triggeringPrincipal.URI.asciiSpec;
   var loadingPrincipal = channel.loadInfo.loadingPrincipal.URI.asciiSpec;
 
   is(triggeringPrincipal, TRIGGERING_PRINCIPAL_URI,
     "TriggeringPrincipal for targeted window.open() should be the principal of the document");
 
   is(testframe.contentDocument.referrer, TRIGGERING_PRINCIPAL_URI,
     "Referrer for targeted window.open() should be the principal of the document");
--- a/docshell/test/navigation/test_triggeringprincipal_window_open.html
+++ b/docshell/test/navigation/test_triggeringprincipal_window_open.html
@@ -30,17 +30,17 @@ function checkFinish() {
     SimpleTest.finish();
   }
 }
 
 // ----------------------------------------------------------------------------
 // Test 1: window.open(http:)
 var httpWin = window.open("file_triggeringprincipal_window_open.html", "_blank", "width=10,height=10");
 httpWin.onload = function() {
-  var httpChannel = SpecialPowers.wrap(httpWin.document).docShell.currentDocumentChannel;
+  var httpChannel = SpecialPowers.wrap(httpWin).docShell.currentDocumentChannel;
   var httpTriggeringPrincipal = httpChannel.loadInfo.triggeringPrincipal.URI.asciiSpec;
   var httpLoadingPrincipal = httpChannel.loadInfo.loadingPrincipal;
 
   is(httpTriggeringPrincipal, TRIGGERING_PRINCIPAL_URI,
      "TriggeringPrincipal for window.open(http:) should be the principal of the document");
 
   is(httpWin.document.referrer, TRIGGERING_PRINCIPAL_URI,
      "Referrer for window.open(http:) should be the principal of the document");
@@ -51,17 +51,17 @@ httpWin.onload = function() {
   httpWin.close();
   checkFinish();
 }
 
 // ----------------------------------------------------------------------------
 // Test 2: window.open(javascript:)
 var jsWin = window.open("javascript:'<html><body>js</body></html>';", "_blank", "width=10,height=10");
 jsWin.onload = function() {
-  var jsChannel = SpecialPowers.wrap(jsWin.document).docShell.currentDocumentChannel;
+  var jsChannel = SpecialPowers.wrap(jsWin).docShell.currentDocumentChannel;
   var jsTriggeringPrincipal = jsChannel.loadInfo.triggeringPrincipal.URI.asciiSpec;
   var jsLoadingPrincipal = jsChannel.loadInfo.loadingPrincipal;
 
   is(jsTriggeringPrincipal, TRIGGERING_PRINCIPAL_URI,
      "TriggeringPrincipal for window.open(javascript:) should be the principal of the document");
 
   is(jsWin.document.referrer, "",
      "Referrer for window.open(javascript:) should be empty");
--- a/docshell/test/test_forceinheritprincipal_overrule_owner.html
+++ b/docshell/test/test_forceinheritprincipal_overrule_owner.html
@@ -3,17 +3,17 @@
 <head>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 
 <script type="text/javascript">
 
-var channel = SpecialPowers.wrap(document).docShell.currentDocumentChannel;
+var channel = SpecialPowers.wrap(window).docShell.currentDocumentChannel;
 var loadInfo = channel.loadInfo;
 
 // 1) perform some sanity checks
 var triggeringPrincipal = channel.loadInfo.triggeringPrincipal.URI.asciiSpec;
 var loadingPrincipal = channel.loadInfo.loadingPrincipal.URI.asciiSpec;
 var principalToInherit = channel.loadInfo.principalToInherit.URI.asciiSpec;
 
 ok(triggeringPrincipal.startsWith("http://mochi.test:8888/"),
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -10000,40 +10000,16 @@ nsIDocument::IsScrollingElement(Element*
 
   // Now we know body is non-null, since aElement is not null.  It's the
   // scrolling element for the document if it itself is not potentially
   // scrollable.
   RefPtr<HTMLBodyElement> strongBody(body);
   return !IsPotentiallyScrollable(strongBody);
 }
 
-void
-nsIDocument::ObsoleteSheet(nsIURI *aSheetURI, ErrorResult& rv)
-{
-  nsresult res = CSSLoader()->ObsoleteSheet(aSheetURI);
-  if (NS_FAILED(res)) {
-    rv.Throw(res);
-  }
-}
-
-void
-nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv)
-{
-  nsCOMPtr<nsIURI> uri;
-  nsresult res = NS_NewURI(getter_AddRefs(uri), aSheetURI);
-  if (NS_FAILED(res)) {
-    rv.Throw(res);
-    return;
-  }
-  res = CSSLoader()->ObsoleteSheet(uri);
-  if (NS_FAILED(res)) {
-    rv.Throw(res);
-  }
-}
-
 class UnblockParsingPromiseHandler final : public PromiseNativeHandler
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler)
 
   explicit UnblockParsingPromiseHandler(nsIDocument* aDocument, Promise* aPromise,
                                         const BlockParsingOptions& aOptions)
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -3319,20 +3319,16 @@ public:
     mStyleSheetChangeEventsEnabled = aValue;
   }
 
   bool StyleSheetChangeEventsEnabled() const
   {
     return mStyleSheetChangeEventsEnabled;
   }
 
-  void ObsoleteSheet(nsIURI *aSheetURI, mozilla::ErrorResult& rv);
-
-  void ObsoleteSheet(const nsAString& aSheetURI, mozilla::ErrorResult& rv);
-
   already_AddRefed<mozilla::dom::Promise> BlockParsing(mozilla::dom::Promise& aPromise,
                                                        const mozilla::dom::BlockParsingOptions& aOptions,
                                                        mozilla::ErrorResult& aRv);
 
   already_AddRefed<nsIURI> GetMozDocumentURIIfNotForErrorPages();
 
   mozilla::dom::Promise* GetDocumentReadyForIdle(mozilla::ErrorResult& aRv);
 
--- a/dom/base/test/chrome/cpows_parent.xul
+++ b/dom/base/test/chrome/cpows_parent.xul
@@ -242,17 +242,17 @@
         // alive as long as we have a reference to the first CPOW (so as long
         // as it's detectable).
         is(savedElement.isEqualNode, savedElement.isEqualNode, "webidl function identity works");
 
         // We want to test what happens when a CPOW is passed through XPConnect
         // as an XPCOM interface.  But elements are losing all their XPCOM
         // interfaces, so let's use an object that will likely stay an XPCOM
         // one.
-        let docshell = savedElement.ownerDocument.docShell;
+        let docshell = savedElement.ownerGlobal.docShell;
         ok(docshell, "We should have a docshell here!");
 
         let secureUI = Cc['@mozilla.org/secure_browser_ui;1']
                          .createInstance(Ci.nsISecureBrowserUI);
 
         try {
           secureUI.setDocShell(docshell);
           ok(false, "expected exception passing CPOW to C++");
--- a/dom/base/test/chrome/file_bug549682.xul
+++ b/dom/base/test/chrome/file_bug549682.xul
@@ -136,17 +136,18 @@ https://bugzilla.mozilla.org/show_bug.cg
       sendAsyncMessage('weak', {});\
       sendAsyncMessage('weakdone', {});", false);
   }
 
   function run() {
     var localmm = document.getElementById('ifr').messageManager;
 
     var wn = document.getElementById('ifr').contentWindow
-      .getInterface(Ci.nsIWebNavigation);
+      .getInterface(Ci.nsIWebNavigation)
+      .QueryInterface(Ci.nsIInterfaceRequestor);
     ok(wn, "Should have webnavigation");
     var cfmm = wn.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIContentFrameMessageManager);
     ok(cfmm, "Should have content messageManager");
 
     var didGetSyncMessage = false;
     function syncContinueTestFn() {
       didGetSyncMessage = true;
     }
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -630,17 +630,16 @@ subsuite = clipboard
 skip-if = toolkit == 'android' #bug 904183
 [test_copypaste.html]
 subsuite = clipboard
 skip-if = toolkit == 'android' #bug 904183
 [test_copypaste.xhtml]
 subsuite = clipboard
 skip-if = toolkit == 'android' #bug 904183
 [test_createHTMLDocument.html]
-[test_declare_stylesheet_obsolete.html]
 [test_data_uri.html]
 skip-if = verify
 [test_document.all_iteration.html]
 [test_document.all_unqualified.html]
 [test_document_constructor.html]
 [test_document_importNode_document.html]
 [test_custom_element.html]
 [test_domparser_null_char.html]
--- a/dom/base/test/test_bug513194.html
+++ b/dom/base/test/test_bug513194.html
@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 <a target="_blank"
    href="https://bugzilla.mozilla.org/show_bug.cgi?id=631615"
    >Mozilla Bug 513194</a>
 <script>
 // The use of document.write is deliberate.  We are testing for the
 // HTML parser to call the CSS parser once and only once when it
 // encounters a new <style> element.
-SpecialPowers.wrap(document).docShell.cssErrorReportingEnabled = true;
+SpecialPowers.wrap(window).docShell.cssErrorReportingEnabled = true;
 SimpleTest.runTestExpectingConsoleMessages(
   function () { document.write("<style>qux { foo : bar; }<\/style>") },
   [{ errorMessage: /Unknown property/ }]
 );
 </script>
 </body>
 </html>
 
deleted file mode 100644
--- a/dom/base/test/test_declare_stylesheet_obsolete.html
+++ /dev/null
@@ -1,94 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=713564
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 713564</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-
-  <!-- Load the variable stylesheet, which changes the color of #content. -->
-  <link rel="stylesheet" type="text/css" href="variable_style_sheet.sjs"/>
-
-  <script type="application/javascript">
-
-  function insertLinkToVarSSAndRun(callback) {
-    var ss = document.createElement("link");
-    ss.rel = "stylesheet";
-    ss.type = "text/css";
-    ss.href = "variable_style_sheet.sjs";
-    document.getElementsByTagName("head")[0].appendChild(ss);
-    ss.addEventListener("load", callback);
-  }
-
-  /** Test for Bug 713564 **/
-
-  // Then you link to that sheet, remove the link from the DOM, insert a new link to
-  // the same url and check that there was no new access, then call our new method,
-  // insert _another_ <link> to the same url, and check that this time we hit the
-  // server.
-  SimpleTest.waitForExplicitFinish();
-
-  function do_test() {
-    var var_sheet = document.getElementsByTagName("link")[1];
-    var head = document.getElementsByTagName("head")[0];
-    var content = document.getElementById("content");
-    var var_sheet_url = var_sheet.href;
-
-    var previousBgColor = window.getComputedStyle(content).
-                                 getPropertyValue("background-color");
-    var_sheet.remove();
-    insertLinkToVarSSAndRun(function() {
-      is(window.getComputedStyle(content).getPropertyValue("background-color"),
-         previousBgColor,
-         "Sheet should still be the same.");
-
-      // Obsolete sheet
-      try {
-        SpecialPowers.wrap(document).obsoleteSheet(var_sheet_url);
-      } catch (e) {
-        ok(false, "obsoleteSheet should not raise an error on valid URL.");
-      }
-      insertLinkToVarSSAndRun(function() {
-        isnot(window.getComputedStyle(content).getPropertyValue("background-color"),
-              previousBgColor,
-              "Sheet should change after obsoleted and reinserted.");
-        SimpleTest.finish();
-      });
-    });
-    // obsoleteSheet should throw with invalid input:
-    try {
-      SpecialPowers.wrap(document).obsoleteSheet("");
-      ok(false, "obsoleteSheet should throw with empty string.");
-    } catch (e) {
-      ok(true, "obsoleteSheet throws with empty string.");
-    }
-    try {
-      SpecialPowers.wrap(document).obsoleteSheet("foo");
-      ok(false, "obsoleteSheet should throw with invalid URL.");
-    } catch (e) {
-      ok(true, "obsoleteSheet throws with invalid URL.");
-    }
-    try {
-      SpecialPowers.wrap(document).obsoleteSheet("http://www.mozilla.org");
-      ok(true, "obsoleteSheet should not throw with valid URL.");
-    } catch (e) {
-      ok(false, "obsoleteSheet throws with valid URL.");
-    }
-  }
-
-  </script>
-</head>
-<body onload="do_test();">
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=713564">Mozilla Bug 713564</a>
-<p id="display"></p>
-<div id="content">
-  <br>
-  <br>
-</div>
-<pre id="test">
-</pre>
-</body>
-</html>
--- a/dom/html/test/browser_fullscreen-api-keys.js
+++ b/dom/html/test/browser_fullscreen-api-keys.js
@@ -33,17 +33,17 @@ function frameScript() {
       keyCode: evt.keyCode
     });
   }
   doc.addEventListener("keydown", keyHandler, true);
   doc.addEventListener("keyup", keyHandler, true);
   doc.addEventListener("keypress", keyHandler, true);
 
   function waitUntilActive() {
-    if (doc.docShell.isActive && doc.hasFocus()) {
+    if (docShell.isActive && doc.hasFocus()) {
       sendAsyncMessage("Test:Activated");
     } else {
       setTimeout(waitUntilActive, 10);
     }
   }
   waitUntilActive();
 }
 
--- a/dom/html/test/browser_fullscreen-contextmenu-esc.js
+++ b/dom/html/test/browser_fullscreen-contextmenu-esc.js
@@ -8,18 +8,17 @@ function frameScript() {
     sendAsyncMessage("Test:FullscreenChanged",
                      !!content.document.fullscreenElement);
   });
   addMessageListener("Test:QueryFullscreenState", () => {
     sendAsyncMessage("Test:FullscreenState",
                      !!content.document.fullscreenElement);
   });
   function waitUntilActive() {
-    let doc = content.document;
-    if (doc.docShell.isActive && doc.hasFocus()) {
+    if (docShell.isActive && content.document.hasFocus()) {
       sendAsyncMessage("Test:Activated");
     } else {
       setTimeout(waitUntilActive, 10);
     }
   }
   waitUntilActive();
 }
 
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -54,16 +54,17 @@
 #endif
 
 extern "C" {
 
 struct AudioIpcInitParams {
   int mServerConnection;
   size_t mPoolSize;
   size_t mStackSize;
+  void (*mThreadCreateCallback)(const char*);
 };
 
 // These functions are provided by audioipc-server crate
 extern void* audioipc_server_start();
 extern mozilla::ipc::FileDescriptor::PlatformHandleType audioipc_server_new_client(void*);
 extern void audioipc_server_stop(void*);
 // These functions are provided by audioipc-client crate
 extern int audioipc_client_init(cubeb**, const char*, const AudioIpcInitParams*);
@@ -425,16 +426,19 @@ cubeb* GetCubebContextUnlocked()
     } else {
       MOZ_DIAGNOSTIC_ASSERT(sIPCConnection);
     }
 
     AudioIpcInitParams initParams;
     initParams.mPoolSize = sAudioIPCPoolSize;
     initParams.mStackSize = sAudioIPCStackSize;
     initParams.mServerConnection = sIPCConnection->ClonePlatformHandle().release();
+    initParams.mThreadCreateCallback = [](const char* aName) {
+      PROFILER_REGISTER_THREAD(aName);
+    };
 
     MOZ_LOG(gCubebLog, LogLevel::Debug, ("%s: %d", PREF_AUDIOIPC_POOL_SIZE, (int) initParams.mPoolSize));
     MOZ_LOG(gCubebLog, LogLevel::Debug, ("%s: %d", PREF_AUDIOIPC_STACK_SIZE, (int) initParams.mStackSize));
 
     rv = audioipc_client_init(&sCubebContext, sBrandName, &initParams);
   } else {
     rv = cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
   }
--- a/dom/plugins/test/mochitest/test_privatemode_perwindowpb.xul
+++ b/dom/plugins/test/mochitest/test_privatemode_perwindowpb.xul
@@ -46,22 +46,17 @@ function runTestsCallback() {
   } catch (e) {
     exceptionThrown = true;
   }
   is(exceptionThrown, false, "Exception thrown getting private mode state.");
   is(state1, false, "Browser returned incorrect private mode state.");
   is(state2, false, "Browser returned incorrect private mode state.");
 
   // open a window with private mode and get the references of the elements.
-  var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShellTreeItem)
-                    .rootTreeItem
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindow);
+  var mainWindow = window.docShell.rootTreeItem.domWindow;
   var contentPage = getRootDirectory(window.location.href) + "privatemode_perwindowpb.xul";
 
   function testOnWindow(aIsPrivate, aCallback) {
     var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
     whenDelayedStartupFinished(win, function () {
       win.addEventListener("DOMContentLoaded", function onInnerLoad() {
         if (win.content.location.href == "about:privatebrowsing") {
           win.gBrowser.loadURI(contentPage);
--- a/dom/security/test/general/browser_test_data_download.js
+++ b/dom/security/test/general/browser_test_data_download.js
@@ -4,18 +4,17 @@ const kTestPath = getRootDirectory(gTest
                   .replace("chrome://mochitests/content", "http://example.com")
 const kTestURI = kTestPath + "file_data_download.html";
 
 function addWindowListener(aURL, aCallback) {
   Services.wm.addListener({
     onOpenWindow(aXULWindow) {
       info("window opened, waiting for focus");
       Services.wm.removeListener(this);
-      var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                .getInterface(Ci.nsIDOMWindow);
+      var domwindow = aXULWindow.docShell.domWindow;
       waitForFocus(function() {
         is(domwindow.document.location.href, aURL, "should have seen the right window open");
         aCallback(domwindow);
       }, domwindow);
     },
     onCloseWindow(aXULWindow) { },
   });
 }
--- a/dom/security/test/general/browser_test_data_text_csv.js
+++ b/dom/security/test/general/browser_test_data_text_csv.js
@@ -4,18 +4,17 @@ const kTestPath = getRootDirectory(gTest
                   .replace("chrome://mochitests/content", "http://example.com")
 const kTestURI = kTestPath + "file_data_text_csv.html";
 
 function addWindowListener(aURL, aCallback) {
   Services.wm.addListener({
     onOpenWindow(aXULWindow) {
       info("window opened, waiting for focus");
       Services.wm.removeListener(this);
-      var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                .getInterface(Ci.nsIDOMWindow);
+      var domwindow = aXULWindow.docShell.domWindow;
       waitForFocus(function() {
         is(domwindow.document.location.href, aURL, "should have seen the right window open");
         aCallback(domwindow);
       }, domwindow);
     },
     onCloseWindow(aXULWindow) { },
   });
 }
--- a/dom/serviceworkers/test/fetch/hsts/realindex.html
+++ b/dom/serviceworkers/test/fetch/hsts/realindex.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <script>
-  var securityInfoPresent = !!SpecialPowers.wrap(document).docShell.currentDocumentChannel.securityInfo;
+  var securityInfoPresent = !!SpecialPowers.wrap(window).docShell.currentDocumentChannel.securityInfo;
   window.parent.postMessage({status: "protocol",
                              data: location.protocol,
                              securityInfoPresent: securityInfoPresent},
                             "*");
 </script>
--- a/dom/serviceworkers/test/test_privateBrowsing.html
+++ b/dom/serviceworkers/test/test_privateBrowsing.html
@@ -29,22 +29,17 @@ function testOnWindow(aIsPrivate, aCallb
 
     if (!aIsPrivate) {
       win.gBrowser.loadURI(contentPage);
     }
   }, {capture: true, once: true});
 }
 
 function setupWindow() {
-  mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIWebNavigation)
-                     .QueryInterface(Ci.nsIDocShellTreeItem)
-                     .rootTreeItem
-                     .QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIDOMWindow);
+  mainWindow = window.docShell.rootTreeItem.domWindow;
   runTest();
 }
 
 var wN;
 var registration;
 var wP;
 
 function testPrivateWindow() {
--- a/dom/tests/mochitest/beacon/test_beaconCookies.html
+++ b/dom/tests/mochitest/beacon/test_beaconCookies.html
@@ -30,22 +30,17 @@ function whenDelayedStartupFinished(aWin
     if (aWindow == aSubject) {
       Services.obs.removeObserver(observer, aTopic);
       setTimeout(aCallback, 0);
     }
   }, "browser-delayed-startup-finished");
 }
 
 function testOnWindow(options, callback) {
-  var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShellTreeItem)
-                         .rootTreeItem
-                         .QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDOMWindow);
+  var mainWindow = window.docShell.rootTreeItem.domWindow;
 
   var win = mainWindow.OpenBrowserWindow(options);
   windowsToClose.push(win);
   whenDelayedStartupFinished(win, function() {
     callback(win);
   });
 };
 
--- a/dom/tests/mochitest/chrome/window_focus.xul
+++ b/dom/tests/mochitest/chrome/window_focus.xul
@@ -269,21 +269,17 @@ function checkSelection(node, testid)
   is(selection.focusNode, range.startContainer, testid + " selection focusNode");
   is(selection.focusOffset, range.startOffset, testid + " selection focusOffset");
   is(selection.anchorNode, range.endContainer, testid + " selection anchorNode");
   is(selection.anchorOffset, range.endOffset, testid + " selection anchorOffset");
 }
 
 function getTopWindow(win)
 {
-  return win.QueryInterface(Ci.nsIInterfaceRequestor).
-             getInterface(Ci.nsIWebNavigation).
-             QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem.
-             QueryInterface(Ci.nsIInterfaceRequestor).
-             getInterface(Ci.nsIDOMWindow);
+  return win.docShell.rootTreeItem.domWindow;
 }
 
 function mouseWillTriggerFocus(element)
 {
   if (!element) {
     return false;
   }
 
--- a/dom/tests/mochitest/localstorage/test_localStorageBasePrivateBrowsing_perwindowpb.html
+++ b/dom/tests/mochitest/localstorage/test_localStorageBasePrivateBrowsing_perwindowpb.html
@@ -10,22 +10,17 @@
 var mainWindow;
 
 var prefBranch = Cc["@mozilla.org/preferences-service;1"]
                    .getService(Ci.nsIPrefBranch);
 prefBranch.setIntPref("browser.startup.page", 0);
 prefBranch.setCharPref("browser.startup.homepage_override.mstone", "ignore");
 
 function startTest() {
-  mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIWebNavigation)
-                      .QueryInterface(Ci.nsIDocShellTreeItem)
-                      .rootTreeItem
-                      .QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIDOMWindow);
+  mainWindow = window.docShell.rootTreeItem.domWindow;
 
   doTest();
 }
 
 var contentPage = "http://mochi.test:8888/chrome/dom/tests/mochitest/localstorage/page_blank.html";
 
 function testOnWindow(aIsPrivate, aCallback) {
   var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
--- a/dom/tests/mochitest/localstorage/test_localStorageQuotaPrivateBrowsing_perwindowpb.html
+++ b/dom/tests/mochitest/localstorage/test_localStorageQuotaPrivateBrowsing_perwindowpb.html
@@ -156,22 +156,17 @@ function whenDelayedStartupFinished(aCal
 
     aSubject.addEventListener("DOMContentLoaded", function() {
       SimpleTest.executeSoon(function() { aCallback(aSubject); });
     }, {capture: true, once: true});
   }, "browser-delayed-startup-finished");
 }
 
 function testOnWindow(aIsPrivate, callback) {
-  var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocShellTreeItem)
-                         .rootTreeItem
-                         .QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIDOMWindow);
+  var mainWindow = window.docShell.rootTreeItem.domWindow;
 
   mainWindow.openWebLinkIn(CONTENT_PAGE, "window", {
                                   private: aIsPrivate });
   whenDelayedStartupFinished(callback);
 };
 </script>
 </head>
 <body onload="startTest();">
--- a/dom/url/tests/browser_download_after_revoke.js
+++ b/dom/url/tests/browser_download_after_revoke.js
@@ -15,18 +15,17 @@ function test () {
 
 	  is(domwindow.document.location.href, "chrome://mozapps/content/downloads/unknownContentType.xul", "Download page appeared");
 
 	  domwindow.close();
           gBrowser.removeTab(gBrowser.selectedTab);
 	  finish();
         }
 
-        var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                  .getInterface(Ci.nsIDOMWindow);
+        var domwindow = aXULWindow.docShell.domWindow;
         domwindow.addEventListener("load", downloadOnLoad, true);
       },
       onCloseWindow: function(aXULWindow) {},
     }
 
     Services.wm.addListener(listener);
 
     info("Creating BlobURL and clicking on a HTMLAnchorElement...");
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -378,23 +378,16 @@ partial interface Document {
   [NewObject, Func="nsGenericHTMLElement::TouchEventsEnabled"]
   TouchList createTouchList();
   [NewObject, Func="nsGenericHTMLElement::TouchEventsEnabled"]
   TouchList createTouchList(sequence<Touch> touches);
 
   [ChromeOnly]
   attribute boolean styleSheetChangeEventsEnabled;
 
-  [ChromeOnly, Throws]
-  void obsoleteSheet(URI sheetURI);
-  [ChromeOnly, Throws]
-  void obsoleteSheet(DOMString sheetURI);
-
-  [ChromeOnly] readonly attribute nsIDocShell? docShell;
-
   [ChromeOnly] readonly attribute DOMString contentLanguage;
 
   [ChromeOnly] readonly attribute nsILoadGroup? documentLoadGroup;
 
   // Blocks the initial document parser until the given promise is settled.
   [ChromeOnly, Throws]
   Promise<any> blockParsing(Promise<any> promise,
                             optional BlockParsingOptions options);
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -250,16 +250,18 @@ partial interface Window {
    */
   [Throws, NeedsCallerType] void sizeToContent();
 
   // XXX Shouldn't this be in nsIDOMChromeWindow?
   [ChromeOnly, Replaceable, Throws] readonly attribute XULControllers controllers;
 
   [ChromeOnly, Throws] readonly attribute Element? realFrameElement;
 
+  [ChromeOnly] readonly attribute nsIDocShell? docShell;
+
   [Throws, NeedsCallerType]
   readonly attribute float mozInnerScreenX;
   [Throws, NeedsCallerType]
   readonly attribute float mozInnerScreenY;
   [Replaceable, Throws, NeedsCallerType]
   readonly attribute double devicePixelRatio;
 
   /* The maximum offset that the window can be scrolled to
--- a/dom/workers/test/test_sharedWorker_privateBrowsing.html
+++ b/dom/workers/test/test_sharedWorker_privateBrowsing.html
@@ -27,22 +27,17 @@ function testOnWindow(aIsPrivate, aCallb
 
     if (!aIsPrivate) {
       win.gBrowser.loadURI(contentPage);
     }
   }, {capture: true, once: true});
 }
 
 function setupWindow() {
-  mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIWebNavigation)
-                     .QueryInterface(Ci.nsIDocShellTreeItem)
-                     .rootTreeItem
-                     .QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIDOMWindow);
+  mainWindow = window.docShell.rootTreeItem.domWindow;
   runTest();
 }
 
 var wN;
 var wP;
 
 function doTests() {
   testOnWindow(false, function(aWin) {
--- a/editor/spellchecker/tests/test_bug1209414.html
+++ b/editor/spellchecker/tests/test_bug1209414.html
@@ -38,23 +38,18 @@ var script;
 
 var onSpellCheck =
   SpecialPowers.Cu.import(
     "resource://testing-common/AsyncSpellCheckTestHelper.jsm").onSpellCheck;
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function() {
   script = SpecialPowers.loadChromeScript(function() {
-    var chromeWin = browserElement.ownerDocument.defaultView
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShellTreeItem)
-                    .rootTreeItem
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindow)
+    var chromeWin = browserElement.ownerGlobal.docShell
+                    .rootTreeItem.domWindow
                     .QueryInterface(Ci.nsIDOMChromeWindow);
     var contextMenu = chromeWin.document.getElementById("contentAreaContextMenu");
     contextMenu.addEventListener("popupshown",
                                  () => sendAsyncMessage("popupshown"));
 
     var dir = Cc["@mozilla.org/file/directory_service;1"]
                 .getService(Ci.nsIProperties)
                 .get("CurWorkD", Ci.nsIFile);
--- a/image/test/mochitest/test_bug1132427.html
+++ b/image/test/mochitest/test_bug1132427.html
@@ -48,22 +48,17 @@ function checkPixel(canvas, context, x1,
 }
 
 var iterationsLeft = 10;
 
 function continueTest() {
   // we need to drawWindow the chrome window so we can get a dump of the retained widget layers
   // if we have to repaint to fulfill this drawWindow request then it will be impossible to
   // observe the bug
-  var chromewin = SpecialPowers.wrap(win).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                     .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-                     .QueryInterface(SpecialPowers.Ci.nsIDocShellTreeItem)
-                     .rootTreeItem
-                     .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                     .getInterface(SpecialPowers.Ci.nsIDOMWindow);
+  var chromewin = SpecialPowers.wrap(win).docShell.rootTreeItem.domWindow;
 
   var el = window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
   el.width = chromewin.innerWidth;
   el.height = chromewin.innerHeight;
   var ctx = el.getContext("2d");
   // pass the correct flags so we don't have to flush the retained layers
   SpecialPowers.wrap(ctx).drawWindow(chromewin, 0, 0, chromewin.innerWidth, chromewin.innerHeight, "rgba(0,0,0,0)",
     ctx.DRAWWINDOW_USE_WIDGET_LAYERS | ctx.DRAWWINDOW_DRAW_VIEW | ctx.DRAWWINDOW_DRAW_CARET);
--- a/ipc/chromium/src/base/platform_thread_posix.cc
+++ b/ipc/chromium/src/base/platform_thread_posix.cc
@@ -21,23 +21,29 @@
 #if !defined(OS_MACOSX)
 #include <unistd.h>
 #endif
 
 #if defined(OS_BSD) && !defined(OS_NETBSD) && !defined(__GLIBC__)
 #include <pthread_np.h>
 #endif
 
+#include "nsThreadUtils.h"
+
 #if defined(OS_MACOSX)
 namespace base {
 void InitThreading();
 }  // namespace
 #endif
 
 static void* ThreadFunc(void* closure) {
+  // Create a nsThread wrapper for the current platform thread, and register it
+  // with the thread manager.
+  (void) NS_GetCurrentThread();
+
   PlatformThread::Delegate* delegate =
       static_cast<PlatformThread::Delegate*>(closure);
   delegate->ThreadMain();
   return NULL;
 }
 
 // static
 PlatformThreadId PlatformThread::CurrentId() {
@@ -87,31 +93,20 @@ void PlatformThread::Sleep(int duration_
 void PlatformThread::SetName(const char* name) {
   // On linux we can get the thread names to show up in the debugger by setting
   // the process name for the LWP.  We don't want to do this for the main
   // thread because that would rename the process, causing tools like killall
   // to stop working.
   if (PlatformThread::CurrentId() == getpid())
     return;
 
-  // http://0pointer.de/blog/projects/name-your-threads.html
-  // Set the name for the LWP (which gets truncated to 15 characters).
-  // Note that glibc also has a 'pthread_setname_np' api, but it may not be
-  // available everywhere and it's only benefit over using prctl directly is
-  // that it can set the name of threads other than the current thread.
-#if defined(OS_LINUX)
-  prctl(PR_SET_NAME, reinterpret_cast<uintptr_t>(name), 0, 0, 0); 
-#elif defined(OS_NETBSD)
-  pthread_setname_np(pthread_self(), "%s", (void *)name);
-#elif defined(OS_BSD) && !defined(__GLIBC__)
-  pthread_set_name_np(pthread_self(), name);
-#elif defined(OS_SOLARIS)
-  pthread_setname_np(pthread_self(), name);
-#else
-#endif
+  // Using NS_SetCurrentThreadName, as opposed to using platform APIs directly,
+  // also sets the thread name on the PRThread wrapper, and allows us to
+  // retrieve it using PR_GetThreadName.
+  NS_SetCurrentThreadName(name);
 }
 #endif // !OS_MACOSX
 
 namespace {
 
 bool CreateThread(size_t stack_size, bool joinable,
                   PlatformThread::Delegate* delegate,
                   PlatformThreadHandle* thread_handle) {
@@ -124,18 +119,19 @@ bool CreateThread(size_t stack_size, boo
   pthread_attr_init(&attributes);
 
   // Pthreads are joinable by default, so only specify the detached attribute if
   // the thread should be non-joinable.
   if (!joinable) {
     pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
   }
 
-  if (stack_size > 0)
-    pthread_attr_setstacksize(&attributes, stack_size);
+  if (stack_size == 0)
+    stack_size = nsIThreadManager::DEFAULT_STACK_SIZE;
+  pthread_attr_setstacksize(&attributes, stack_size);
 
   success = !pthread_create(thread_handle, &attributes, ThreadFunc, delegate);
 
   pthread_attr_destroy(&attributes);
   return success;
 }
 
 }  // anonymous namespace
--- a/ipc/chromium/src/base/platform_thread_win.cc
+++ b/ipc/chromium/src/base/platform_thread_win.cc
@@ -4,30 +4,36 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "base/platform_thread.h"
 
 #include "base/logging.h"
 #include "base/win_util.h"
 
+#include "nsThreadUtils.h"
+
 namespace {
 
 // The information on how to set the thread name comes from
 // a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
 const DWORD kVCThreadNameException = 0x406D1388;
 
 typedef struct tagTHREADNAME_INFO {
   DWORD dwType;  // Must be 0x1000.
   LPCSTR szName;  // Pointer to name (in user addr space).
   DWORD dwThreadID;  // Thread ID (-1=caller thread).
   DWORD dwFlags;  // Reserved for future use, must be zero.
 } THREADNAME_INFO;
 
 DWORD __stdcall ThreadFunc(void* closure) {
+  // Create a nsThread wrapper for the current platform thread, and register it
+  // with the thread manager.
+  (void) NS_GetCurrentThread();
+
   PlatformThread::Delegate* delegate =
       static_cast<PlatformThread::Delegate*>(closure);
   delegate->ThreadMain();
   return 0;
 }
 
 }  // namespace
 
@@ -43,34 +49,20 @@ void PlatformThread::YieldCurrentThread(
 
 // static
 void PlatformThread::Sleep(int duration_ms) {
   ::Sleep(duration_ms);
 }
 
 // static
 void PlatformThread::SetName(const char* name) {
-#ifdef HAVE_SEH_EXCEPTIONS
-  // The debugger needs to be around to catch the name in the exception.  If
-  // there isn't a debugger, we are just needlessly throwing an exception.
-  if (!::IsDebuggerPresent())
-    return;
-
-  THREADNAME_INFO info;
-  info.dwType = 0x1000;
-  info.szName = name;
-  info.dwThreadID = CurrentId();
-  info.dwFlags = 0;
-
-  MOZ_SEH_TRY {
-    RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD),
-                   reinterpret_cast<DWORD_PTR*>(&info));
-  } MOZ_SEH_EXCEPT(EXCEPTION_CONTINUE_EXECUTION) {
-  }
-#endif
+  // Using NS_SetCurrentThreadName, as opposed to using platform APIs directly,
+  // also sets the thread name on the PRThread wrapper, and allows us to
+  // retrieve it using PR_GetThreadName.
+  NS_SetCurrentThreadName(name);
 }
 
 // static
 bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
                             PlatformThreadHandle* thread_handle) {
   unsigned int flags = 0;
   if (stack_size > 0) {
     flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -938,17 +938,22 @@ js::EnqueuePendingParseTasksAfterGC(JSRu
 bool
 js::CurrentThreadIsParseThread()
 {
     JSContext* cx = TlsContext.get();
     return cx->helperThread() && cx->helperThread()->parseTask();
 }
 #endif
 
-static const uint32_t kDefaultHelperStackSize = 2048 * 1024;
+// We want our default stack size limit to be approximately 2MB, to be safe, but
+// expect most threads to use much less. On Linux, however, requesting a stack
+// of 2MB or larger risks the kernel allocating an entire 2MB huge page for it
+// on first access, which we do not want. To avoid this possibility, we subtract
+// 2 standard VM page sizes from our default.
+static const uint32_t kDefaultHelperStackSize = 2048 * 1024 - 2 * 4096;
 static const uint32_t kDefaultHelperStackQuota = 1800 * 1024;
 
 // TSan enforces a minimum stack size that's just slightly larger than our
 // default helper stack size.  It does this to store blobs of TSan-specific
 // data on each thread's stack.  Unfortunately, that means that even though
 // we'll actually receive a larger stack than we requested, the effective
 // usable space of that stack is significantly less than what we expect.
 // To offset TSan stealing our stack space from underneath us, double the
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -74,16 +74,20 @@
 
 static MOZ_THREAD_LOCAL(XPCJSContext*) gTlsContext;
 
 using namespace mozilla;
 using namespace xpc;
 using namespace JS;
 using mozilla::dom::AutoEntryScript;
 
+// The watchdog thread loop is pretty trivial, and should not require much stack
+// space to do its job. So only give it 32KiB.
+static constexpr size_t kWatchdogStackSize = 32 * 1024;
+
 static void WatchdogMain(void* arg);
 class Watchdog;
 class WatchdogManager;
 class MOZ_RAII AutoLockWatchdog final
 {
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     Watchdog* const mWatchdog;
 
@@ -138,17 +142,17 @@ class Watchdog
         {
             AutoLockWatchdog lock(this);
 
             // Gecko uses thread private for accounting and has to clean up at thread exit.
             // Therefore, even though we don't have a return value from the watchdog, we need to
             // join it on shutdown.
             mThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this,
                                       PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
-                                      PR_JOINABLE_THREAD, 0);
+                                      PR_JOINABLE_THREAD, kWatchdogStackSize);
             if (!mThread)
                 MOZ_CRASH("PR_CreateThread failed!");
 
             // WatchdogMain acquires the lock and then asserts mInitialized. So
             // make sure to set mInitialized before releasing the lock here so
             // that it's atomic with the creation of the thread.
             mInitialized = true;
         }
@@ -467,16 +471,18 @@ AutoLockWatchdog::~AutoLockWatchdog()
         PR_Unlock(mWatchdog->GetLock());
     }
 }
 
 static void
 WatchdogMain(void* arg)
 {
     AUTO_PROFILER_REGISTER_THREAD("JS Watchdog");
+    // Create an nsThread wrapper for the thread and register it with the thread manager.
+    Unused << NS_GetCurrentThread();
     NS_SetCurrentThreadName("JS Watchdog");
 
     Watchdog* self = static_cast<Watchdog*>(arg);
     WatchdogManager* manager = self->Manager();
 
     // Lock lasts until we return
     AutoLockWatchdog lock(self);
 
--- a/js/xpconnect/tests/unit/test_xray_named_element_access.js
+++ b/js/xpconnect/tests/unit/test_xray_named_element_access.js
@@ -7,17 +7,17 @@ ChromeUtils.import("resource://gre/modul
 add_task(async function() {
   let webnav = Services.appShell.createWindowlessBrowser(false);
 
   let docShell = webnav.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDocShell);
 
   docShell.createAboutBlankContentViewer(null);
 
-  let window = webnav.getInterface(Ci.nsIDOMWindow);
+  let window = webnav.document.defaultView;
   let unwrapped = Cu.waiveXrays(window);
 
   window.document.body.innerHTML = '<div id="foo"></div>';
 
   equal(window.foo, undefined, "Should not have named X-ray property access");
   equal(typeof unwrapped.foo, "object", "Should always have non-X-ray named property access");
 
   webnav.close();
--- a/layout/base/tests/chrome/printpreview_bug396024_helper.xul
+++ b/layout/base/tests/chrome/printpreview_bug396024_helper.xul
@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 <![CDATA[
 var is = window.opener.wrappedJSObject.is;
 var ok = window.opener.wrappedJSObject.ok;
 var todo = window.opener.wrappedJSObject.todo;
 var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 var gWbp;
 function printpreview() {
-  gWbp = window.frames[1].document.docShell.printPreview;
+  gWbp = window.frames[1].docShell.printPreview;
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
@@ -38,17 +38,17 @@ function printpreview() {
   prefs.setBoolPref('print.show_print_progress', false);
   //XXX I would have thought this would work, instead I'm forced to use prefs service
   gWbp.globalPrintSettings.showPrintProgress = false; 
   gWbp.printPreview(gWbp.globalPrintSettings, window.frames[0], listener);
   prefs.clearUserPref('print.show_print_progress');
 }
 
 function exitprintpreview() {
-  window.frames[1].document.docShell.printPreview.exitPrintPreview();
+  window.frames[1].docShell.printPreview.exitPrintPreview();
 }
 
 function finish() {
   SimpleTest.finish();
   window.close();
 }
 
 function run()
@@ -84,17 +84,17 @@ function run2() {
     document.getElementById("i").removeEventListener("load", arguments.callee, true);
     setTimeout(run3, 0);
   };
   document.getElementById("i").addEventListener("load", loadhandler, true);
   window.frames[0].location.reload();
 }
 
 function run3() {
-  gWbp = window.frames[1].document.docShell.printPreview;
+  gWbp = window.frames[1].docShell.printPreview;
   ok(gWbp.doingPrintPreview, "Should be doing print preview");
   exitprintpreview();
   setTimeout(run4, 0);
 }
 
 function run4() {
   var i = document.getElementById("i");
   i.remove();
@@ -103,17 +103,17 @@ function run4() {
     setTimeout(run5, 0);
   };
   i.addEventListener("load", loadhandler, true);
   document.documentElement.getBoundingClientRect();
   document.documentElement.appendChild(i);
 }
 
 function run5() {
-  gWbp = window.frames[0].document.docShell.printPreview;
+  gWbp = window.frames[0].docShell.printPreview;
   ok(!gWbp.doingPrintPreview, "Should not be doing print preview anymore2");
 
   //XXX this shouldn't be necessary, see bug 405555
   printpreview();
   exitprintpreview();
   finish(); //should not have crashed after all of this
 }
 ]]></script>
--- a/layout/base/tests/chrome/printpreview_bug482976_helper.xul
+++ b/layout/base/tests/chrome/printpreview_bug482976_helper.xul
@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 <![CDATA[
 var is = window.opener.wrappedJSObject.is;
 var ok = window.opener.wrappedJSObject.ok;
 var todo = window.opener.wrappedJSObject.todo;
 var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 var gWbp;
 function printpreview() {
-  gWbp = window.frames[1].document.docShell.printPreview;
+  gWbp = window.frames[1].docShell.printPreview;
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
@@ -38,17 +38,17 @@ function printpreview() {
   prefs.setBoolPref('print.show_print_progress', false);
   //XXX I would have thought this would work, instead I'm forced to use prefs service
   gWbp.globalPrintSettings.showPrintProgress = false; 
   gWbp.printPreview(gWbp.globalPrintSettings, window.frames[0], listener);
   prefs.clearUserPref('print.show_print_progress');
 }
 
 function exitprintpreview() {
-  window.frames[1].document.docShell.printPreview.exitPrintPreview();
+  window.frames[1].docShell.printPreview.exitPrintPreview();
 }
 
 function finish() {
   SimpleTest.finish();
   window.close();
 }
 
 function run1()
--- a/layout/base/tests/chrome/printpreview_helper.xul
+++ b/layout/base/tests/chrome/printpreview_helper.xul
@@ -19,17 +19,17 @@ var ctx2;
 var counter = 0;
 
 var file = Cc["@mozilla.org/file/directory_service;1"]
              .getService(Ci.nsIProperties)
              .get("TmpD", Ci.nsIFile);
 filePath = file.path;
 
 function printpreview() {
-  gWbp = window.frames[1].document.docShell.printPreview;
+  gWbp = window.frames[1].docShell.printPreview;
   var listener = {
     onLocationChange: function(webProgress, request, location, flags) { },
     onProgressChange: function(webProgress, request, curSelfProgress, 
                                maxSelfProgress, curTotalProgress,
                                maxTotalProgress) { },
     onSecurityChange: function(webProgress, request, state) { },
     onStateChange: function(webProgress, request, stateFlags, status) { },
     onStatusChange: function(webProgress, request, status, message) { },
@@ -55,17 +55,17 @@ function printpreview() {
   is(before, 1, "Should have called beforeprint listener!");
   is(after, 1, "Should have called afterprint listener!");
   window.frames[0].removeEventListener("beforeprint", beforeprint, true);
   window.frames[0].removeEventListener("afterprint", afterprint, true);
   prefs.clearUserPref('print.show_print_progress');
 }
 
 function exitprintpreview() {
-  window.frames[1].document.docShell.printPreview.exitPrintPreview();
+  window.frames[1].docShell.printPreview.exitPrintPreview();
 }
 
 function finish() {
   SimpleTest.finish();
   window.close();
 }
 
 function runTests()
--- a/layout/base/tests/chrome/test_bug420499.xul
+++ b/layout/base/tests/chrome/test_bug420499.xul
@@ -55,17 +55,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
     
     /** Test for Bug 420499 **/
     SimpleTest.waitForExplicitFinish();
 
     function getSelectionController() {
-      return document.docShell
+      return window.docShell
         .QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsISelectionDisplay)
         .QueryInterface(Ci.nsISelectionController);
     }
 
     function isCaretVisible() {
       window.QueryInterface(Ci.nsIInterfaceRequestor);
       var docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
--- a/layout/forms/test/test_bug536567_perwindowpb.html
+++ b/layout/forms/test/test_bug536567_perwindowpb.html
@@ -124,21 +124,17 @@ function endTest() {
   }
 
   normalWindow.close();
   privateWindow.close();
   MockFilePicker.cleanup();
   SimpleTest.finish();
 }
 
-var mainWindow =
-  window.QueryInterface(Ci.nsIInterfaceRequestor).
-    getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShellTreeItem).
-    rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor).
-    getInterface(Ci.nsIDOMWindow);
+var mainWindow = window.docShell.rootTreeItem.domWindow;
 var contentPage = "http://mochi.test:8888/chrome/layout/forms/test/bug536567_iframe.html";
 
 function whenDelayedStartupFinished(aWindow, aCallback) {
   Services.obs.addObserver(function observer(aSubject, aTopic) {
     if (aWindow == aSubject) {
       Services.obs.removeObserver(observer, aTopic);
       setTimeout(aCallback, 0);
     }
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-mediaqueries/mq_prefers_reduced_motion_both.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>prefers-reduced-motion</title>
+<style>
+div {
+  width: 100px;
+  height: 100px;
+  background-color: red;
+}
+
+@media (prefers-reduced-motion: no-preference) {
+  div { background-color: green; }
+}
+
+@media (prefers-reduced-motion) {
+  div { background-color: green; }
+}
+</style>
+<div></div>
--- a/layout/reftests/css-mediaqueries/reftest.list
+++ b/layout/reftests/css-mediaqueries/reftest.list
@@ -16,8 +16,9 @@ fuzzy-if(Android,8,454) == mq_print_maxh
 == mq_print_minwidth_updown.xhtml mq_print-ref.xhtml
 test-pref(ui.prefersReducedMotion,0) == mq_prefers_reduced_motion.html about:blank
 test-pref(ui.prefersReducedMotion,1) == mq_prefers_reduced_motion.html greenbox.html
 test-pref(ui.prefersReducedMotion,0) == mq_prefers_reduced_motion_no_preference.html greenbox.html
 test-pref(ui.prefersReducedMotion,1) == mq_prefers_reduced_motion_no_preference.html about:blank
 test-pref(ui.prefersReducedMotion,0) == mq_prefers_reduced_motion_reduce.html about:blank
 test-pref(ui.prefersReducedMotion,1) == mq_prefers_reduced_motion_reduce.html greenbox.html
 test-pref(privacy.resistFingerprinting,true) test-pref(ui.prefersReducedMotion,1) == mq_prefers_reduced_motion_reduce.html about:blank
+test-pref(privacy.resistFingerprinting,true) == mq_prefers_reduced_motion_both.html greenbox.html
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -848,36 +848,16 @@ Loader::IsAlternateSheet(const nsAString
       return IsAlternate::No;
     }
   }
 
   return IsAlternate::Yes;
 }
 
 nsresult
-Loader::ObsoleteSheet(nsIURI* aURI)
-{
-  if (!mSheets) {
-    return NS_OK;
-  }
-  if (!aURI) {
-    return NS_ERROR_INVALID_ARG;
-  }
-  for (auto iter = mSheets->mCompleteSheets.Iter(); !iter.Done(); iter.Next()) {
-    nsIURI* sheetURI = iter.Key()->GetURI();
-    bool areEqual;
-    nsresult rv = sheetURI->Equals(aURI, &areEqual);
-    if (NS_SUCCEEDED(rv) && areEqual) {
-      iter.Remove();
-    }
-  }
-  return NS_OK;
-}
-
-nsresult
 Loader::CheckContentPolicy(nsIPrincipal* aLoadingPrincipal,
                            nsIPrincipal* aTriggeringPrincipal,
                            nsIURI* aTargetURI,
                            nsINode* aRequestingNode,
                            bool aIsPreload)
 {
   // When performing a system load (e.g. aUseSystemPrincipal = true)
   // then aLoadingPrincipal == null; don't consult content policies.
--- a/layout/style/Loader.h
+++ b/layout/style/Loader.h
@@ -438,20 +438,16 @@ public:
   // selected and aHasAlternateRel is false.
   IsAlternate IsAlternateSheet(const nsAString& aTitle, bool aHasAlternateRel);
 
   typedef nsTArray<RefPtr<SheetLoadData>> LoadDataArray;
 
   // Measure our size.
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
-  // Marks all the sheets at the given URI obsolete, and removes them from the
-  // cache.
-  nsresult ObsoleteSheet(nsIURI* aURI);
-
 private:
   friend class SheetLoadData;
   friend class StreamLoader;
 
   // Helpers to conditionally block onload if mDocument is non-null.
   void BlockOnload();
   void UnblockOnload(bool aFireSync);
 
--- a/layout/style/ServoStyleConsts.h
+++ b/layout/style/ServoStyleConsts.h
@@ -124,23 +124,24 @@ enum class StyleAppearance : uint8_t {
   // A small scrollbar.
   ScrollbarSmall,
   // The scrollbar slider
   ScrollbarHorizontal,
   ScrollbarVertical,
   // A scrollbar button (up/down/left/right).
   // Keep these in order (some code casts these values to `int` in order to
   // compare them against each other).
+  ScrollbarbuttonUp,
   ScrollbarbuttonDown,
   ScrollbarbuttonLeft,
   ScrollbarbuttonRight,
-  ScrollbarbuttonUp,
   // The scrollbar thumb.
   ScrollbarthumbHorizontal,
   ScrollbarthumbVertical,
+  // The scrollbar track.
   ScrollbartrackHorizontal,
   ScrollbartrackVertical,
   // The scroll corner
   Scrollcorner,
   // A searchfield.
   Searchfield,
   // A separator.  Can be horizontal or vertical.
   Separator,
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -515,41 +515,33 @@ GetOperatingSystemVersion(nsIDocument* a
 static void
 GetIsGlyph(nsIDocument* aDocument, const nsMediaFeature* aFeature,
            nsCSSValue& aResult)
 {
   MOZ_ASSERT(aFeature->mReqFlags & nsMediaFeature::eUserAgentAndChromeOnly);
   aResult.SetIntValue(aDocument->IsSVGGlyphsDocument() ? 1 : 0, eCSSUnit_Integer);
 }
 
+static bool
+PrefersReducedMotion(nsIDocument* aDocument)
+{
+  if (nsContentUtils::ShouldResistFingerprinting(aDocument)) {
+    return false;
+  }
+  return LookAndFeel::GetInt(LookAndFeel::eIntID_PrefersReducedMotion, 0) == 1;
+}
+
 static void
 GetPrefersReducedMotion(nsIDocument* aDocument,
-                        const nsMediaFeature* aFeature,
+                        const nsMediaFeature*,
                         nsCSSValue& aResult)
 {
-  const bool isAccessibleFromContentPages =
-    !(aFeature->mReqFlags & nsMediaFeature::eUserAgentAndChromeOnly);
-  if (isAccessibleFromContentPages &&
-      nsContentUtils::ShouldResistFingerprinting(aDocument)) {
-    return;
-  }
-
-  StylePrefersReducedMotion prefersReducedMotion =
-    StylePrefersReducedMotion::NoPreference;
-
-  switch (LookAndFeel::GetInt(LookAndFeel::eIntID_PrefersReducedMotion, 0)) {
-    case 0:
-      prefersReducedMotion = StylePrefersReducedMotion::NoPreference;
-      break;
-    case 1:
-      prefersReducedMotion = StylePrefersReducedMotion::Reduce;
-      break;
-    default:
-      return;
-  }
+  auto prefersReducedMotion = PrefersReducedMotion(aDocument)
+    ? StylePrefersReducedMotion::Reduce
+    : StylePrefersReducedMotion::NoPreference;
 
   aResult.SetEnumValue(prefersReducedMotion);
 }
 
 /* static */ void
 nsMediaFeatures::InitSystemMetrics()
 {
   if (sSystemMetrics)
--- a/layout/style/test/test_bug413958.html
+++ b/layout/style/test/test_bug413958.html
@@ -15,17 +15,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       href="https://bugzilla.mozilla.org/show_bug.cgi?id=413958"
       >Mozilla Bug 413958</a>. All text below should be black on white.</p>
 <p>Sheet: <span id="s1">1</span>
           <span id="s2">2</span>
           <span id="s3">3</span>.
    Style attr: <span id="setStyle">4</span>.
    Properties: <span id="setStyleProp" style="">5</span>.</p>
 <script>
-SpecialPowers.wrap(document).docShell.cssErrorReportingEnabled = true;
+SpecialPowers.wrap(window).docShell.cssErrorReportingEnabled = true;
 
 var tests = [
   function() {
     var s = document.createTextNode(
 "#s1{nosuchprop:auto; color:black}\n"+
 "#s2{nosuchprop:auto; color:black}invalid?sel{}#s3{color:black}"),
         e = document.createElement("style");
     e.appendChild(s);
--- a/layout/style/test/test_css_parse_error_smoketest.html
+++ b/layout/style/test/test_css_parse_error_smoketest.html
@@ -4,17 +4,17 @@
   <meta charset="utf-8">
   <title>Test for CSS parser reporting parsing errors with expected precision</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 </head>
 <body>
 <style id="testbench"></style>
 <script>
-  SpecialPowers.wrap(document).docShell.cssErrorReportingEnabled = true;
+  SpecialPowers.wrap(window).docShell.cssErrorReportingEnabled = true;
   var tests = [
     { css: "@unknown {}", error: "Unrecognized at-rule or error parsing at-rule ‘@unknown’." },
 
     { css: "x { color: invalid; }", error: "Expected color but found ‘invalid’.  Error in parsing value for ‘color’.  Declaration dropped." },
 
     { css: "x { filter: alpha(foo); }", error: "Expected ‘none’, URL, or filter function but found ‘alpha(’.  Error in parsing value for ‘filter’.  Declaration dropped." },
     { css: "x { filter: 5; }", error: "Expected ‘none’, URL, or filter function but found ‘5’.  Error in parsing value for ‘filter’.  Declaration dropped." },
 
--- a/layout/style/test/test_parser_diagnostics_unprintables.html
+++ b/layout/style/test/test_parser_diagnostics_unprintables.html
@@ -13,17 +13,17 @@
 >Mozilla Bug 229827</a>
 <style id="testbench"></style>
 <script type="application/javascript">
 // This test has intimate knowledge of how to get the CSS parser to
 // emit diagnostics that contain text under control of the user.
 // That's not the point of the test, though; the point is only that
 // *that text* is properly escaped.
 
-SpecialPowers.wrap(document).docShell.cssErrorReportingEnabled = true;
+SpecialPowers.wrap(window).docShell.cssErrorReportingEnabled = true;
 
 // There is one "pattern" for each code path through the error reporter
 // that might need to escape some kind of user-supplied text.
 // Each "pattern" is tested once with each of the "substitution"s below:
 // <t>, <i>, and <s> are replaced by the t:, i:, and s: fields of
 // each substitution object in turn.
 let patterns = [
   // REPORT_UNEXPECTED_P (only ever used in contexts where identifier-like
--- a/layout/tools/reftest/bootstrap.js
+++ b/layout/tools/reftest/bootstrap.js
@@ -13,20 +13,20 @@ function processTerminated() {
         Services.obs.removeObserver(observe, topic);
         resolve();
       }
     }, "ipc:content-shutdown");
   });
 }
 
 var WindowListener = {
-  onOpenWindow: function(win) {
+  onOpenWindow: function(xulWin) {
     Services.wm.removeListener(WindowListener);
 
-    win = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+    let win = xulWin.docShell.domWindow;
     win.addEventListener("load", function listener() {
       // Load into any existing windows.
       let windows = Services.wm.getEnumerator("navigator:browser");
       while (windows.hasMoreElements()) {
         win = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
         break;
       }
 
--- a/layout/tools/reftest/manifest.jsm
+++ b/layout/tools/reftest/manifest.jsm
@@ -522,17 +522,17 @@ sandbox.compareRetainedDisplayLists = g.
         getIntPref:  function(p) { return prefs.getIntPref(p); }
     }, sandbox, { cloneFunctions: true });
 
     // Tests shouldn't care about this except for when they need to
     // crash the content process
     sandbox.browserIsRemote = g.browserIsRemote;
 
     try {
-        sandbox.asyncPan = g.containingWindow.document.docShell.asyncPanZoomEnabled;
+        sandbox.asyncPan = g.containingWindow.docShell.asyncPanZoomEnabled;
     } catch (e) {
         sandbox.asyncPan = false;
     }
 
     // Graphics features
     sandbox.usesRepeatResampling = sandbox.d2d;
 
     // Running in a test-verify session?
--- a/media/audioipc/client/src/context.rs
+++ b/media/audioipc/client/src/context.rs
@@ -15,16 +15,17 @@ use futures::Future;
 use futures_cpupool::{self, CpuPool};
 use libc;
 use std::{fmt, io, mem, ptr};
 use std::ffi::{CStr, CString};
 use std::os::raw::c_void;
 use std::os::unix::io::FromRawFd;
 use std::os::unix::net;
 use std::sync::mpsc;
+use std::thread;
 use stream;
 use tokio_core::reactor::{Handle, Remote};
 use tokio_uds::UnixStream;
 
 struct CubebClient;
 
 impl rpc::Client for CubebClient {
     type Request = ServerMessage;
@@ -94,41 +95,55 @@ impl ContextOps for ClientContext {
             let _ = tx_rpc.send(rpc);
             Some(())
         }
 
         assert_not_in_callback();
 
         let (tx_rpc, rx_rpc) = mpsc::channel();
 
+        let params = CPUPOOL_INIT_PARAMS.with(|p| {
+            p.replace(None).unwrap()
+        });
+
+        let thread_create_callback = params.thread_create_callback;
+
+        let register_thread = move || {
+            if let Some(func) = thread_create_callback {
+                let thr = thread::current();
+                let name = CString::new(thr.name().unwrap()).unwrap();
+                func(name.as_ptr());
+            }
+        };
+
         let core = t!(core::spawn_thread("AudioIPC Client RPC", move || {
             let handle = core::handle();
 
+            register_thread();
+
             open_server_stream()
                 .ok()
                 .and_then(|stream| UnixStream::from_stream(stream, &handle).ok())
                 .and_then(|stream| bind_and_send_client(stream, &handle, &tx_rpc))
                 .ok_or_else(|| {
                     io::Error::new(
                         io::ErrorKind::Other,
                         "Failed to open stream and create rpc.",
                     )
                 })
         }));
 
         let rpc = t!(rx_rpc.recv());
 
-        let cpupool = CPUPOOL_INIT_PARAMS.with(|p| {
-            let params = p.replace(None).unwrap();
-            futures_cpupool::Builder::new()
+        let cpupool = futures_cpupool::Builder::new()
                 .name_prefix("AudioIPC")
+                .after_start(register_thread)
                 .pool_size(params.pool_size)
                 .stack_size(params.stack_size)
-                .create()
-        });
+                .create();
 
         let ctx = Box::new(ClientContext {
             _ops: &CLIENT_OPS as *const _,
             rpc: rpc,
             core: core,
             cpu_pool: cpupool,
         });
         Ok(unsafe { Context::from_ptr(Box::into_raw(ctx) as *mut _) })
--- a/media/audioipc/client/src/lib.rs
+++ b/media/audioipc/client/src/lib.rs
@@ -32,29 +32,32 @@ thread_local!(static IN_CALLBACK: std::c
 thread_local!(static CPUPOOL_INIT_PARAMS: InitParamsTls = std::cell::RefCell::new(None));
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug)]
 pub struct AudioIpcInitParams {
     pub server_connection: c_int,
     pub pool_size: usize,
     pub stack_size: usize,
+    pub thread_create_callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>,
 }
 
 #[derive(Clone, Copy, Debug)]
 struct CpuPoolInitParams {
     pub pool_size: usize,
     pub stack_size: usize,
+    pub thread_create_callback: Option<extern "C" fn(*const ::std::os::raw::c_char)>,
 }
 
 impl CpuPoolInitParams {
     pub fn init_with(params: &AudioIpcInitParams) -> Self {
         CpuPoolInitParams {
             pool_size: params.pool_size,
             stack_size: params.stack_size,
+            thread_create_callback: params.thread_create_callback,
         }
     }
 }
 
 fn set_in_callback(in_callback: bool) {
     IN_CALLBACK.with(|b| {
         assert_eq!(*b.borrow(), !in_callback);
         *b.borrow_mut() = in_callback;
--- a/mobile/android/chrome/content/aboutAccounts.js
+++ b/mobile/android/chrome/content/aboutAccounts.js
@@ -299,23 +299,17 @@ document.addEventListener("DOMContentLoa
   var buttonOpenPrefs = document.getElementById("buttonOpenPrefs");
   buttonOpenPrefs.addEventListener("click", openPrefs);
 }, {capture: true, once: true});
 
 // This window is contained in a XUL <browser> element.  Return the
 // messageManager of that <browser> element, or null.
 function getBrowserMessageManager() {
   let browser = window
-        .QueryInterface(Ci.nsIInterfaceRequestor)
-        .getInterface(Ci.nsIWebNavigation)
-        .QueryInterface(Ci.nsIDocShellTreeItem)
-        .rootTreeItem
-        .QueryInterface(Ci.nsIInterfaceRequestor)
-        .getInterface(Ci.nsIDOMWindow)
-        .QueryInterface(Ci.nsIDOMChromeWindow)
+        .docShell.rootTreeItem.domWindow
         .BrowserApp
         .getBrowserForDocument(document);
   if (browser) {
     return browser.messageManager;
   }
   return null;
 }
 
--- a/mobile/android/chrome/content/aboutAddons.js
+++ b/mobile/android/chrome/content/aboutAddons.js
@@ -11,22 +11,17 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const AMO_ICON = "chrome://browser/skin/images/amo-logo.png";
 const UPDATE_INDICATOR = "chrome://browser/skin/images/extension-update.svg";
 
 var gStringBundle = Services.strings.createBundle("chrome://browser/locale/aboutAddons.properties");
 
 XPCOMUtils.defineLazyGetter(window, "gChromeWin", function() {
-  return window.QueryInterface(Ci.nsIInterfaceRequestor)
-           .getInterface(Ci.nsIWebNavigation)
-           .QueryInterface(Ci.nsIDocShellTreeItem)
-           .rootTreeItem
-           .QueryInterface(Ci.nsIInterfaceRequestor)
-           .getInterface(Ci.nsIDOMWindow)
+  return window.docShell.rootTreeItem.domWindow
            .QueryInterface(Ci.nsIDOMChromeWindow);
 });
 ChromeUtils.defineModuleGetter(window, "Preferences",
                                "resource://gre/modules/Preferences.jsm");
 
 var ContextMenus = {
   target: null,
 
--- a/mobile/android/chrome/content/aboutLogins.js
+++ b/mobile/android/chrome/content/aboutLogins.js
@@ -3,22 +3,17 @@
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 ChromeUtils.import("resource://services-common/utils.js"); /* global: CommonUtils */
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/TelemetryStopwatch.jsm");
 
 XPCOMUtils.defineLazyGetter(window, "gChromeWin", () =>
-  window.QueryInterface(Ci.nsIInterfaceRequestor)
-    .getInterface(Ci.nsIWebNavigation)
-    .QueryInterface(Ci.nsIDocShellTreeItem)
-    .rootTreeItem
-    .QueryInterface(Ci.nsIInterfaceRequestor)
-    .getInterface(Ci.nsIDOMWindow)
+  window.docShell.rootTreeItem.domWindow
     .QueryInterface(Ci.nsIDOMChromeWindow));
 
 ChromeUtils.defineModuleGetter(this, "EventDispatcher",
                                "resource://gre/modules/Messaging.jsm");
 ChromeUtils.defineModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");
 ChromeUtils.defineModuleGetter(this, "Prompt",
                                "resource://gre/modules/Prompt.jsm");
 
--- a/mobile/android/chrome/content/aboutPrivateBrowsing.js
+++ b/mobile/android/chrome/content/aboutPrivateBrowsing.js
@@ -4,22 +4,17 @@
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(window, "gChromeWin", () =>
-  window.QueryInterface(Ci.nsIInterfaceRequestor)
-    .getInterface(Ci.nsIWebNavigation)
-    .QueryInterface(Ci.nsIDocShellTreeItem)
-    .rootTreeItem
-    .QueryInterface(Ci.nsIInterfaceRequestor)
-    .getInterface(Ci.nsIDOMWindow)
+  window.docShell.rootTreeItem.domWindow
     .QueryInterface(Ci.nsIDOMChromeWindow));
 
 document.addEventListener("DOMContentLoaded", function() {
     let BrowserApp = window.gChromeWin.BrowserApp;
 
     if (!PrivateBrowsingUtils.isContentWindowPrivate(window)) {
       document.body.setAttribute("class", "normal");
       document.getElementById("newPrivateTabLink").addEventListener("click", function() {
--- a/mobile/android/components/ContentDispatchChooser.js
+++ b/mobile/android/components/ContentDispatchChooser.js
@@ -81,17 +81,17 @@ ContentDispatchChooser.prototype =
         }
 
         // We couldn't open this. If this was from a click, it's likely that we just
         // want this to fail silently. If the user entered this on the address bar, though,
         // we want to show the neterror page.
         let dwu = window.windowUtils;
         let millis = dwu.millisSinceLastUserInput;
         if (millis < 0 || millis >= 1000) {
-          window.document.docShell.displayLoadError(Cr.NS_ERROR_UNKNOWN_PROTOCOL, aURI, null);
+          window.docShell.displayLoadError(Cr.NS_ERROR_UNKNOWN_PROTOCOL, aURI, null);
         } else {
           this._closeBlankWindow(window);
         }
       });
     }
   },
 };
 
--- a/mobile/android/components/ContentPermissionPrompt.js
+++ b/mobile/android/components/ContentPermissionPrompt.js
@@ -46,22 +46,17 @@ ContentPermissionPrompt.prototype = {
       callback(/* allow */ false);
       return true;
     }
 
     return false;
   },
 
   getChromeWindow: function getChromeWindow(aWindow) {
-     let chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIWebNavigation)
-                            .QueryInterface(Ci.nsIDocShellTreeItem)
-                            .rootTreeItem
-                            .QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIDOMWindow)
+     let chromeWin = aWindow.docShell.rootTreeItem.domWindow
                             .QueryInterface(Ci.nsIDOMChromeWindow);
      return chromeWin;
   },
 
   getChromeForRequest: function getChromeForRequest(request) {
     if (request.window) {
       let requestingWindow = request.window.top;
       return this.getChromeWindow(requestingWindow).wrappedJSObject;
--- a/mobile/android/components/extensions/ext-tabs.js
+++ b/mobile/android/components/extensions/ext-tabs.js
@@ -1,19 +1,17 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 ChromeUtils.defineModuleGetter(this, "PromiseUtils",
                                "resource://gre/modules/PromiseUtils.jsm");
 
 const getBrowserWindow = window => {
-  return window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDocShell)
-               .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
-               .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+  return window.docShell.rootTreeItem.domWindow;
 };
 
 let tabListener = {
   tabReadyInitialized: false,
   tabReadyPromises: new WeakMap(),
   initializingTabs: new WeakSet(),
 
   initTabReady() {
--- a/mobile/android/modules/WebrtcUI.jsm
+++ b/mobile/android/modules/WebrtcUI.jsm
@@ -278,22 +278,17 @@ var WebrtcUI = {
     } else {
       message = Strings.browser.GetStringFromName("getUserMedia.blockedCameraAndMicrophoneAccess");
     }
 
     DoorHanger.show(aWindow, message, "webrtc-blocked");
   },
 
   getChromeWindow: function getChromeWindow(aWindow) {
-     let chromeWin = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIWebNavigation)
-                            .QueryInterface(Ci.nsIDocShellTreeItem)
-                            .rootTreeItem
-                            .QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIDOMWindow)
+     let chromeWin = aWindow.docShell.rootTreeItem.domWindow
                             .QueryInterface(Ci.nsIDOMChromeWindow);
      return chromeWin;
   },
 
   prompt: function prompt(aContentWindow, aCallID, aAudioRequested,
                           aVideoRequested, aDevices) {
     let audioDevices = [];
     let videoDevices = [];
--- a/mobile/android/modules/geckoview/GeckoViewUtils.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewUtils.jsm
@@ -254,17 +254,17 @@ var GeckoViewUtils = {
   /**
    * Return the outermost chrome DOM window (the XUL window) for a given DOM
    * window, in the parent process.
    *
    * @param aWin a DOM window.
    */
   getChromeWindow: function(aWin) {
     const docShell = this.getRootDocShell(aWin);
-    return docShell && docShell.getInterface(Ci.nsIDOMWindow);
+    return docShell && docShell.domWindow;
   },
 
   /**
    * Return the content frame message manager (aka the frame script global
    * object) for a given DOM window, in a child process.
    *
    * @param aWin a DOM window.
    */
--- a/mobile/android/tests/browser/chrome/test_awsy_lite.html
+++ b/mobile/android/tests/browser/chrome/test_awsy_lite.html
@@ -41,18 +41,18 @@
 
   function checkpoint(aName) {
     var mrm = Cc["@mozilla.org/memory-reporter-manager;1"].getService(Ci.nsIMemoryReporterManager);
     gResults.push( { name: aName, resident: mrm.resident } );
     info(`${aName} | Resident Memory: ${mrm.resident}`);
   }
 
   var browserListener = {
-    onOpenWindow: function(aWindow) {
-        var win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+    onOpenWindow: function(aXulWin) {
+        var win = aXulWin.docShell.domWindow;
         win.addEventListener("UIReady", function(aEvent) {
             attachTo(win);
         }, {once: true});
     },
 
     onCloseWindow: function(aWindow) {
         detachFrom(aWindow);
     },
--- a/netwerk/test/browser/browser_NetUtil.js
+++ b/netwerk/test/browser/browser_NetUtil.js
@@ -67,18 +67,17 @@ function test_asyncFetchBadCert() {
 }
 
 function WindowListener(aURL, aCallback) {
   this.callback = aCallback;
   this.url = aURL;
 }
 WindowListener.prototype = {
   onOpenWindow: function(aXULWindow) {
-    var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIDOMWindow);
+    var domwindow = aXULWindow.docShell.domWindow;
     var self = this;
     domwindow.addEventListener("load", function() {
       if (domwindow.document.location.href != self.url)
         return;
 
       // Allow other window load listeners to execute before passing to callback
       executeSoon(function() {
         self.callback(domwindow);
--- a/netwerk/test/browser/browser_about_cache.js
+++ b/netwerk/test/browser/browser_about_cache.js
@@ -17,17 +17,17 @@ add_task(async function() {
     // Can't easily use searchParms and new URL() because it's an about: URI...
     return uri.startsWith("about:cache?") && uri.includes("storage=disk");
   };
   let diskPageLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, expectedPageCheck);
   await ContentTask.spawn(tab.linkedBrowser, null, function() {
     ok(!content.document.nodePrincipal.isSystemPrincipal,
        "about:cache should not have system principal");
     let principalURI = content.document.nodePrincipal.URI;
-    let channel = content.document.docShell.currentDocumentChannel;
+    let channel = content.docShell.currentDocumentChannel;
     ok(!channel.loadInfo.loadingPrincipal, "Loading principal should be null.");
     is(principalURI && principalURI.spec, content.document.location.href, "Principal matches location");
     let links = [... content.document.querySelectorAll("a[href*=disk]")];
     is(links.length, 1, "Should have 1 link to the disk entries");
     links[0].click();
   });
   await diskPageLoaded;
   info("about:cache disk subpage loaded");
@@ -38,34 +38,34 @@ add_task(async function() {
   };
   let triggeringURISpec = tab.linkedBrowser.currentURI.spec;
   let entryLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, expectedPageCheck);
   await ContentTask.spawn(tab.linkedBrowser, kTestPage, function(kTestPage) {
     ok(!content.document.nodePrincipal.isSystemPrincipal,
        "about:cache with query params should still not have system principal");
     let principalURI = content.document.nodePrincipal.URI;
     is(principalURI && principalURI.spec, content.document.location.href, "Principal matches location");
-    let channel = content.document.docShell.currentDocumentChannel;
+    let channel = content.docShell.currentDocumentChannel;
     principalURI = channel.loadInfo.triggeringPrincipal.URI;
     is(principalURI && principalURI.spec, "about:cache", "Triggering principal matches previous location");
     ok(!channel.loadInfo.loadingPrincipal, "Loading principal should be null.");
     let links = [... content.document.querySelectorAll("a[href*='" + kTestPage + "']")];
     is(links.length, 1, "Should have 1 link to the entry for " + kTestPage);
     links[0].click();
   });
   await entryLoaded;
   info("about:cache entry loaded");
 
 
   await ContentTask.spawn(tab.linkedBrowser, triggeringURISpec, function(triggeringURISpec) {
     ok(!content.document.nodePrincipal.isSystemPrincipal,
        "about:cache-entry should also not have system principal");
     let principalURI = content.document.nodePrincipal.URI;
     is(principalURI && principalURI.spec, content.document.location.href, "Principal matches location");
-    let channel = content.document.docShell.currentDocumentChannel;
+    let channel = content.docShell.currentDocumentChannel;
     principalURI = channel.loadInfo.triggeringPrincipal.URI;
     is(principalURI && principalURI.spec, triggeringURISpec, "Triggering principal matches previous location");
     ok(!channel.loadInfo.loadingPrincipal, "Loading principal should be null.");
     ok(content.document.querySelectorAll("th").length,
        "Should have several table headers with data.");
   });
   BrowserTestUtils.removeTab(tab);
 });
--- a/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing_perwindowpb.html
+++ b/security/manager/ssl/tests/mochitest/stricttransportsecurity/test_sts_privatebrowsing_perwindowpb.html
@@ -24,21 +24,17 @@
   ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
   ChromeUtils.import("resource://testing-common/ContentTask.jsm");
   ChromeUtils.import("resource://gre/modules/Services.jsm");
 
   // This is how many sub-tests (testframes) in each round.
   // When the round begins, this will be initialized.
   var testsleftinround = 0;
   var currentround = "";
-  var mainWindow =
-    window.QueryInterface(Ci.nsIInterfaceRequestor).
-    getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsIDocShellTreeItem).
-    rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor).
-    getInterface(Ci.nsIDOMWindow);
+  var mainWindow = window.docShell.rootTreeItem.domWindow;
 
   SpecialPowers.Services.prefs.setIntPref("browser.startup.page", 0);
 
   var testframes = {
     samedom: {
       url: `http://example.com/${STSPATH}/verify.sjs`,
       expected: {plain: "SECURE", subdom: "SECURE", nosts: "INSECURE"}
     },
--- a/servo/components/style/values/specified/box.rs
+++ b/servo/components/style/values/specified/box.rs
@@ -994,20 +994,20 @@ pub enum Appearance {
     /// A small scrollbar.
     ScrollbarSmall,
     /// The scrollbar slider
     ScrollbarHorizontal,
     ScrollbarVertical,
     /// A scrollbar button (up/down/left/right).
     /// Keep these in order (some code casts these values to `int` in order to
     /// compare them against each other).
+    ScrollbarbuttonUp,
     ScrollbarbuttonDown,
     ScrollbarbuttonLeft,
     ScrollbarbuttonRight,
-    ScrollbarbuttonUp,
     /// The scrollbar thumb.
     ScrollbarthumbHorizontal,
     ScrollbarthumbVertical,
     /// The scrollbar track.
     ScrollbartrackHorizontal,
     ScrollbartrackVertical,
     /// The scroll corner
     Scrollcorner,
--- a/testing/mochitest/bootstrap.js
+++ b/testing/mochitest/bootstrap.js
@@ -59,18 +59,18 @@ var WindowListener = {
 
   tearDownWindow(win) {
     if (win.nativeConsole) {
       win.console = win.nativeConsole;
       win.nativeConsole = undefined;
     }
   },
 
-  onOpenWindow(win) {
-    win = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+  onOpenWindow(xulWin) {
+    let win = xulWin.docShell.domWindow;
 
     win.addEventListener("load", function() {
       if (win.document.documentElement.getAttribute("windowtype") == "navigator:browser") {
         WindowListener.setupWindow(win);
       }
     }, {once: true});
   }
 };
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -1410,22 +1410,17 @@ SpecialPowersAPI.prototype = {
                  .QueryInterface(Ci.nsIDocShell);
   },
   _getMUDV(window) {
     return this._getDocShell(window).contentViewer;
   },
   // XXX: these APIs really ought to be removed, they're not e10s-safe.
   // (also they're pretty Firefox-specific)
   _getTopChromeWindow(window) {
-    return window.QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsIWebNavigation)
-                 .QueryInterface(Ci.nsIDocShellTreeItem)
-                 .rootTreeItem
-                 .QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsIDOMWindow)
+    return window.docShell.rootTreeItem.domWindow
                  .QueryInterface(Ci.nsIDOMChromeWindow);
   },
   _getAutoCompletePopup(window) {
     return this._getTopChromeWindow(window).document
                                            .getElementById("PopupAutoComplete");
   },
   addAutoCompletePopupEventListener(window, eventname, listener) {
     this._getAutoCompletePopup(window).addEventListener(eventname,
--- a/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
+++ b/toolkit/components/backgroundhangmonitor/BackgroundHangMonitor.cpp
@@ -263,17 +263,18 @@ BackgroundHangManager::BackgroundHangMan
   : mShutdown(false)
   , mLock("BackgroundHangManager")
 {
   // Lock so we don't race against the new monitor thread
   MonitorAutoLock autoLock(mLock);
 
   mHangMonitorThread = PR_CreateThread(
     PR_USER_THREAD, MonitorThread, this,
-    PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+    PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
+    nsIThreadManager::DEFAULT_STACK_SIZE);
 
   MOZ_ASSERT(mHangMonitorThread, "Failed to create BHR monitor thread");
 
   DebugOnly<nsresult> rv
     = NS_NewNamedThread("BHMgr Processor",
                         getter_AddRefs(mHangProcessingThread));
   MOZ_ASSERT(NS_SUCCEEDED(rv) && mHangProcessingThread,
              "Failed to create BHR processing thread");
--- a/toolkit/components/browser/nsWebBrowser.cpp
+++ b/toolkit/components/browser/nsWebBrowser.cpp
@@ -548,16 +548,24 @@ nsWebBrowser::GetDocument()
 
 nsPIDOMWindowOuter*
 nsWebBrowser::GetWindow()
 {
   return mDocShell ? mDocShell->GetWindow() : nullptr;
 }
 
 NS_IMETHODIMP
+nsWebBrowser::GetDomWindow(mozIDOMWindowProxy** aWindow)
+{
+  if (!mDocShell)
+    return NS_ERROR_NOT_INITIALIZED;
+  return mDocShell->GetDomWindow(aWindow);
+}
+
+NS_IMETHODIMP
 nsWebBrowser::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner)
 {
   NS_ENSURE_ARG_POINTER(aTreeOwner);
   *aTreeOwner = nullptr;
   if (mDocShellTreeOwner) {
     if (mDocShellTreeOwner->mTreeOwner) {
       *aTreeOwner = mDocShellTreeOwner->mTreeOwner;
     } else {
--- a/toolkit/components/extensions/ExtensionCommon.jsm
+++ b/toolkit/components/extensions/ExtensionCommon.jsm
@@ -363,18 +363,17 @@ class BaseContext {
     this.incognito = null;
     this.messageManager = null;
     this.docShell = null;
     this.contentWindow = null;
     this.innerWindowID = 0;
   }
 
   setContentWindow(contentWindow) {
-    let {document} = contentWindow;
-    let {docShell} = document;
+    let {document, docShell} = contentWindow;
 
     this.innerWindowID = getInnerWindowID(contentWindow);
     this.messageManager = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIContentFrameMessageManager);
 
     if (this.incognito == null) {
       this.incognito = PrivateBrowsingUtils.isContentWindowPrivate(contentWindow);
     }
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -916,18 +916,18 @@ var ExtensionContent = {
   },
 
   // Helpers
 
   * enumerateWindows(docShell) {
     let enum_ = docShell.getDocShellEnumerator(docShell.typeContent,
                                                docShell.ENUMERATE_FORWARDS);
 
-    for (let docShell of XPCOMUtils.IterSimpleEnumerator(enum_, Ci.nsIInterfaceRequestor)) {
+    for (let docShell of XPCOMUtils.IterSimpleEnumerator(enum_, Ci.nsIDocShell)) {
       try {
-        yield docShell.getInterface(Ci.nsIDOMWindow);
+        yield docShell.domWindow;
       } catch (e) {
         // This can fail if the docShell is being destroyed, so just
         // ignore the error.
       }
     }
   },
 };
--- a/toolkit/components/extensions/ExtensionPageChild.jsm
+++ b/toolkit/components/extensions/ExtensionPageChild.jsm
@@ -394,17 +394,17 @@ ExtensionPageChild = {
     let context = this.extensionContexts.get(windowId);
     if (context) {
       if (context.extension !== extension) {
         throw new Error("A different extension context already exists for this frame");
       }
       throw new Error("An extension context was already initialized for this frame");
     }
 
-    let mm = contentWindow.document.docShell
+    let mm = contentWindow.docShell
                           .QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIContentFrameMessageManager);
 
     let {viewType, tabId, devtoolsToolboxInfo} = getFrameData(mm) || {};
 
     let uri = contentWindow.document.documentURIObject;
 
     if (devtoolsToolboxInfo) {
--- a/toolkit/components/extensions/ExtensionParent.jsm
+++ b/toolkit/components/extensions/ExtensionParent.jsm
@@ -656,18 +656,17 @@ class ExtensionPageContextParent extends
     this.extension.views.add(this);
 
     extension.emit("extension-proxy-context-load", this);
   }
 
   // The window that contains this context. This may change due to moving tabs.
   get xulWindow() {
     let win = this.xulBrowser.ownerGlobal;
-    return win.document.docShell.rootTreeItem
-              .QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+    return win.docShell.rootTreeItem.domWindow;
   }
 
   get currentWindow() {
     if (this.viewType !== "background") {
       return this.xulWindow;
     }
   }
 
--- a/toolkit/components/extensions/extension-process-script.js
+++ b/toolkit/components/extensions/extension-process-script.js
@@ -84,17 +84,17 @@ var extensions = new DefaultWeakMap(poli
 });
 
 var contentScripts = new DefaultWeakMap(matcher => {
   return new ExtensionContent.Script(extensions.get(matcher.extension),
                                      matcher);
 });
 
 function getMessageManager(window) {
-  let docShell = window.document.docShell.QueryInterface(Ci.nsIInterfaceRequestor);
+  let docShell = window.docShell.QueryInterface(Ci.nsIInterfaceRequestor);
   try {
     return docShell.getInterface(Ci.nsIContentFrameMessageManager);
   } catch (e) {
     // Some windows don't support this interface (hidden window).
     return null;
   }
 }
 
@@ -278,18 +278,18 @@ DocumentManager = {
 
   // Helpers
 
   * enumerateWindows(docShell) {
     if (docShell) {
       let enum_ = docShell.getDocShellEnumerator(docShell.typeContent,
                                                  docShell.ENUMERATE_FORWARDS);
 
-      for (let docShell of XPCOMUtils.IterSimpleEnumerator(enum_, Ci.nsIInterfaceRequestor)) {
-        yield docShell.getInterface(Ci.nsIDOMWindow);
+      for (let docShell of XPCOMUtils.IterSimpleEnumerator(enum_, Ci.nsIDocShell)) {
+        yield docShell.domWindow;
       }
     } else {
       for (let global of this.globals.keys()) {
         yield* this.enumerateWindows(global.docShell);
       }
     }
   },
 };
--- a/toolkit/components/extensions/parent/ext-tabs-base.js
+++ b/toolkit/components/extensions/parent/ext-tabs-base.js
@@ -783,17 +783,17 @@ class WindowBase {
   }
 
   /**
    * @property {nsIXULWindow} xulWindow
    *        The nsIXULWindow object for this browser window.
    *        @readonly
    */
   get xulWindow() {
-    return this.window.document.docShell.treeOwner
+    return this.window.docShell.treeOwner
                .QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIXULWindow);
   }
 
   /**
    * Returns true if this window is the current window for the given extension
    * context, false otherwise.
    *
--- a/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_switchtab.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_switchtab.js
@@ -6,19 +6,18 @@ const PROMPT_URL = "chrome://global/cont
 
 add_task(async function test() {
   await new Promise(resolve => {
 
   let tab = BrowserTestUtils.addTab(gBrowser);
   isnot(tab, gBrowser.selectedTab, "New tab shouldn't be selected");
 
   let listener = {
-    onOpenWindow(window) {
-      var domwindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIDOMWindow);
+    onOpenWindow(xulWin) {
+      var domwindow = xulWin.docShell.domWindow;
       waitForFocus(() => {
         is(domwindow.document.location.href, PROMPT_URL, "Should have seen a prompt window");
         is(domwindow.args.promptType, "promptUserAndPass", "Should be an authenticate prompt");
 
         is(gBrowser.selectedTab, tab, "Should have selected the new tab");
 
         domwindow.document.documentElement.cancelDialog();
       }, domwindow);
--- a/toolkit/components/passwordmgr/test/chrome/test_privbrowsing_perwindowpb.html
+++ b/toolkit/components/passwordmgr/test/chrome/test_privbrowsing_perwindowpb.html
@@ -189,22 +189,17 @@ function checkTest() {
       break;
 
     default:
       ok(false, "Unexpected call to checkTest for test #" + testNum);
 
   }
 }
 
-var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShellTreeItem)
-                    .rootTreeItem
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindow);
+var mainWindow = window.docShell.rootTreeItem.domWindow;
 var contentPage = "http://mochi.test:8888/chrome/toolkit/components/passwordmgr/test/chrome/privbrowsing_perwindowpb_iframe.html";
 var testWindows = [];
 
 function whenDelayedStartupFinished(aWindow, aCallback) {
   Services.obs.addObserver(function obs(aSubject, aTopic) {
     if (aWindow == aSubject) {
       Services.obs.removeObserver(obs, aTopic);
       setTimeout(aCallback, 0);
--- a/toolkit/components/search/nsSearchService.js
+++ b/toolkit/components/search/nsSearchService.js
@@ -2454,19 +2454,17 @@ Engine.prototype = {
                            .QueryInterface(Ci.nsILoadContext);
 
     // Using the codebase principal which is constructed by the search URI
     // and given originAttributes. If originAttributes are not given, we
     // fallback to use the docShell's originAttributes.
     let attrs = options.originAttributes;
 
     if (!attrs) {
-      attrs = options.window.document
-                            .docShell
-                            .getOriginAttributes();
+      attrs = options.window.docShell.getOriginAttributes();
     }
 
     let principal = Services.scriptSecurityManager
                             .createCodebasePrincipal(searchURI, attrs);
 
     connector.speculativeConnect2(searchURI, principal, callbacks);
 
     if (this.supportsResponseType(URLTYPE_SUGGEST_JSON)) {
--- a/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js
+++ b/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js
@@ -178,18 +178,17 @@ const backgroundPageThumbsContent = {
       capture.canvasDrawTime = new Date() - canvasDrawDate;
 
       finalCanvas.toBlob(blob => {
         capture.imageBlob = new Blob([blob]);
         // Load about:blank to finish the capture and wait for onStateChange.
         this._loadAboutBlank();
       });
     };
-    let win = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                      .getInterface(Ci.nsIDOMWindow);
+    let win = docShell.domWindow;
     win.requestIdleCallback(() => doCapture().catch(ex => this._failCurrentCapture(ex.message)));
   },
 
   _finishCurrentCapture() {
     let capture = this._currentCapture;
     let fileReader = new FileReader();
     fileReader.onloadend = () => {
       sendAsyncMessage("BackgroundPageThumbs:didCapture", {
--- a/toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html
@@ -15,22 +15,17 @@
 </div>
 <pre id="test">
 
 <script class="testbody" type="text/javascript">
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
 
-var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShellTreeItem)
-                    .rootTreeItem
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindow);
+var mainWindow = window.docShell.rootTreeItem.domWindow;
 
 var testDatas = [
   { url: "itisaphishingsite.org/phishing.html",
     list: "mochi1-phish-simple",
     provider: "google",
   },
 
   { url: "fakeitisaphishingsite.org/phishing.html",
--- a/toolkit/components/url-classifier/tests/mochitest/test_donottrack.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_donottrack.html
@@ -9,22 +9,17 @@
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 
 <script class="testbody" type="text/javascript">
 
-var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShellTreeItem)
-                    .rootTreeItem
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindow);
+var mainWindow = window.docShell.rootTreeItem.domWindow;
 
 const tests = [
   // DNT turned on and TP turned off, DNT signal sent in both private browsing
   // and normal mode.
   {
     setting:  {dntPref: true, tpPref: false, tppbPref: false, pbMode: true},
     expected: {dnt: "1"},
   },
--- a/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html
@@ -11,22 +11,17 @@
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 
 <script class="testbody" type="text/javascript">
 
-var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShellTreeItem)
-                    .rootTreeItem
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindow);
+var mainWindow = window.docShell.rootTreeItem.domWindow;
 var contentPage = "http://www.itisatrap.org/tests/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
 ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 
 function testOnWindow(aPrivate, aCallback) {
   var win = mainWindow.OpenBrowserWindow({private: aPrivate});
--- a/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html
@@ -16,22 +16,17 @@
 <pre id="test">
 
 <script class="testbody" type="text/javascript">
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
 ChromeUtils.import("resource://testing-common/ContentTask.jsm");
 ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 
-var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShellTreeItem)
-                    .rootTreeItem
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindow);
+var mainWindow = window.docShell.rootTreeItem.domWindow;
 const SJS = "mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs";
 const BASE_URL = "http://" + SJS + "?";
 
 var pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p});
 
 function addUrlToDB(list, url) {
   let testData = [{ db: list, url}];
 
--- a/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
@@ -16,22 +16,17 @@
 <pre id="test">
 
 <script src="head.js"></script>
 <script class="testbody" type="text/javascript">
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShellTreeItem)
-                    .rootTreeItem
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindow);
+var mainWindow = window.docShell.rootTreeItem.domWindow;
 
 var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"].
                     getService(Ci.nsIUrlListManager);
 const SJS = "mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/threathit.sjs";
 
 function hash(str) {
   function bytesFromString(str1) {
     let converter =
@@ -192,17 +187,17 @@ function testOnWindow(aTestData) {
   return new Promise(resolve => {
     let win = mainWindow.OpenBrowserWindow();
 
     (async function() {
       await new Promise(rs => whenDelayedStartupFinished(win, rs));
 
       let expected;
       let browser = win.gBrowser.selectedBrowser;
-      let wp = win.gBrowser.contentDocument.docShell.QueryInterface(Ci.nsIWebProgress);
+      let wp = win.gBrowser.contentWindow.docShell.QueryInterface(Ci.nsIWebProgress);
       let progressListener = {
         onSecurityChange(aWebProgress, aRequest, aState) {
           expected = aTestData.reportUrl;
         },
         QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"])
       };
       wp.addProgressListener(progressListener, wp.NOTIFY_SECURITY);
 
--- a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html
@@ -11,22 +11,17 @@
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 
 <script class="testbody" type="text/javascript">
 
-var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShellTreeItem)
-                    .rootTreeItem
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindow);
+var mainWindow = window.docShell.rootTreeItem.domWindow;
 var contentPage = "chrome://mochitests/content/chrome/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
 ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 
 function testOnWindow(aCallback) {
   var win = mainWindow.OpenBrowserWindow();
--- a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html
@@ -11,22 +11,17 @@
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 
 <script class="testbody" type="text/javascript">
 
-var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIWebNavigation)
-                       .QueryInterface(Ci.nsIDocShellTreeItem)
-                       .rootTreeItem
-                       .QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDOMWindow);
+var mainWindow = window.docShell.rootTreeItem.domWindow;
 var contentPage = "http://www.itisatrap.org/tests/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
 ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 
 function testOnWindow(aPrivate, aCallback) {
   var win = mainWindow.OpenBrowserWindow({private: aPrivate});
--- a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html
@@ -11,22 +11,17 @@
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 
 <script class="testbody" type="text/javascript">
 
-var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocShellTreeItem)
-                    .rootTreeItem
-                    .QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIDOMWindow);
+var mainWindow = window.docShell.rootTreeItem.domWindow;
 var contentPage1 = "http://www.itisatrap.org/tests/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html";
 var contentPage2 = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
 ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 
 function testOnWindow(contentPage, aCallback) {
--- a/toolkit/content/aboutTelemetry.js
+++ b/toolkit/content/aboutTelemetry.js
@@ -124,22 +124,17 @@ function sectionalizeObject(obj) {
   }
   return map;
 }
 
 /**
  * Obtain the main DOMWindow for the current context.
  */
 function getMainWindow() {
-  return window.QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIWebNavigation)
-               .QueryInterface(Ci.nsIDocShellTreeItem)
-               .rootTreeItem
-               .QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIDOMWindow);
+  return window.docShell.rootTreeItem.domWindow;
 }
 
 /**
  * Obtain the DOMWindow that can open a preferences pane.
  *
  * This is essentially "get the browser chrome window" with the added check
  * that the supposed browser chrome window is capable of opening a preferences
  * pane.
--- a/toolkit/content/tests/browser/browser_saveImageURL.js
+++ b/toolkit/content/tests/browser/browser_saveImageURL.js
@@ -33,17 +33,17 @@ add_task(async function preferred_API() 
   }, async function(browser) {
     let url = await ContentTask.spawn(browser, null, async function() {
       let image = content.document.getElementById("image");
       return image.href;
     });
 
     saveImageURL(url, "image.jpg", null, true, false, null, null, null, null,
       false, gBrowser.contentPrincipal);
-    let channel = gBrowser.contentDocumentAsCPOW.docShell.currentDocumentChannel;
+    let channel = gBrowser.contentWindowAsCPOW.docShell.currentDocumentChannel;
     if (channel) {
       ok(true, channel.QueryInterface(Ci.nsIHttpChannelInternal)
                       .channelIsForDownload);
 
       // Throttleable is the only class flag assigned to downloads.
       ok(channel.QueryInterface(Ci.nsIClassOfService).classFlags,
          Ci.nsIClassOfService.Throttleable);
     }
@@ -69,17 +69,17 @@ add_task(async function deprecated_API()
       return image.href;
     });
 
     // Now get the document directly from content. If we run this test with
     // e10s-enabled, this will be a CPOW, which is forbidden. We'll just
     // pass the XUL document instead to test this interface.
     let doc = document;
 
-    let channel = gBrowser.contentDocumentAsCPOW.docShell.currentDocumentChannel;
+    let channel = gBrowser.contentWindowAsCPOW.docShell.currentDocumentChannel;
     if (channel) {
       ok(true, channel.QueryInterface(Ci.nsIHttpChannelInternal)
                       .channelIsForDownload);
 
       // Throttleable is the only class flag assigned to downloads.
       ok(channel.QueryInterface(Ci.nsIClassOfService).classFlags,
          Ci.nsIClassOfService.Throttleable);
     }
--- a/toolkit/modules/BrowserUtils.jsm
+++ b/toolkit/modules/BrowserUtils.jsm
@@ -419,19 +419,17 @@ var BrowserUtils = {
   /**
    * Retrieve the root window object (i.e. the top-most content global) for a
    * specific docShell object.
    *
    * @param  {nsIDocShell} docShell
    * @return {nsIDOMWindow}
    */
   getRootWindow(docShell) {
-    return docShell.QueryInterface(Ci.nsIDocShellTreeItem)
-      .sameTypeRootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIDOMWindow);
+    return docShell.sameTypeRootTreeItem.domWindow;
   },
 
   /**
    * Trim the selection text to a reasonable size and sanitize it to make it
    * safe for search query input.
    *
    * @param aSelection
    *        The selection text to trim.
--- a/toolkit/modules/Finder.jsm
+++ b/toolkit/modules/Finder.jsm
@@ -465,17 +465,17 @@ Finder.prototype = {
       current: 0,
       _currentFound: false
     };
   },
 
   _getWindow() {
     if (!this._docShell)
       return null;
-    return this._docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
+    return this._docShell.domWindow;
   },
 
   /**
    * Get the bounding selection rect in CSS px relative to the origin of the
    * top-level content document.
    */
   _getResultRect() {
     let topWin = this._getWindow();
--- a/toolkit/modules/HiddenFrame.jsm
+++ b/toolkit/modules/HiddenFrame.jsm
@@ -54,18 +54,17 @@ HiddenFrame.prototype = {
     return this._deferred.promise;
   },
 
   /**
    * Fetch a sync ref to the window inside the frame (needed for the add-on SDK).
    */
   getWindow() {
     this.get();
-    this._browser.QueryInterface(Ci.nsIInterfaceRequestor);
-    return this._browser.getInterface(Ci.nsIDOMWindow);
+    return this._browser.document.ownerGlobal;
   },
 
 
   destroy() {
     if (this._browser) {
       if (this._listener) {
         this._webProgress.removeProgressListener(this._listener);
         this._listener = null;
--- a/toolkit/modules/WebChannelContent.jsm
+++ b/toolkit/modules/WebChannelContent.jsm
@@ -7,17 +7,17 @@
 
 var EXPORTED_SYMBOLS = ["WebChannelContent"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 function getMessageManager(event) {
   let window = Cu.getGlobalForObject(event.target);
 
-  return window.document.docShell
+  return window.docShell
                .QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIContentFrameMessageManager);
 }
 
 var WebChannelContent = {
   // Preference containing the list (space separated) of origins that are
   // allowed to send non-string values through a WebChannel, mainly for
   // backwards compatability. See bug 1238128 for more information.
--- a/toolkit/modules/addons/WebNavigationContent.js
+++ b/toolkit/modules/addons/WebNavigationContent.js
@@ -8,18 +8,17 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.defineModuleGetter(this, "WebNavigationFrames",
                                "resource://gre/modules/WebNavigationFrames.jsm");
 
 function getDocShellOuterWindowId(docShell) {
   if (!docShell) {
     return undefined;
   }
 
-  return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsIDOMWindow)
+  return docShell.domWindow
                  .windowUtils
                  .outerWindowID;
 }
 
 function loadListener(event) {
   let document = event.target;
   let window = document.defaultView;
   let url = document.documentURI;
@@ -115,18 +114,17 @@ var FormSubmitListener = {
 var WebProgressListener = {
   init: function() {
     // This WeakMap (DOMWindow -> nsIURI) keeps track of the pathname and hash
     // of the previous location for all the existent docShells.
     this.previousURIMap = new WeakMap();
 
     // Populate the above previousURIMap by iterating over the docShells tree.
     for (let currentDocShell of WebNavigationFrames.iterateDocShellTree(docShell)) {
-      let win = currentDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIDOMWindow);
+      let win = currentDocShell.domWindow;
       let {currentURI} = currentDocShell.QueryInterface(Ci.nsIWebNavigation);
 
       this.previousURIMap.set(win, currentURI);
     }
 
     // This WeakSet of DOMWindows keeps track of the attempted refresh.
     this.refreshAttemptedDOMWindows = new WeakSet();
 
--- a/toolkit/modules/addons/WebNavigationFrames.jsm
+++ b/toolkit/modules/addons/WebNavigationFrames.jsm
@@ -4,27 +4,16 @@
 
 "use strict";
 
 const EXPORTED_SYMBOLS = ["WebNavigationFrames"];
 
 /* exported WebNavigationFrames */
 
 /**
- * Retrieve the DOMWindow associated to the docShell passed as parameter.
- *
- * @param    {nsIDocShell}  docShell - the docShell that we want to get the DOMWindow from.
- * @returns  {nsIDOMWindow}          - the DOMWindow associated to the docShell.
- */
-function docShellToWindow(docShell) {
-  return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsIDOMWindow);
-}
-
-/**
  * The FrameDetail object which represents a frame in WebExtensions APIs.
  *
  * @typedef  {Object}  FrameDetail
  * @inner
  * @property {number}  frameId        - Represents the numeric id which identify the frame in its tab.
  * @property {number}  parentFrameId  - Represents the numeric id which identify the parent frame.
  * @property {string}  url            - Represents the current location URL loaded in the frame.
  * @property {boolean} errorOccurred  - Indicates whether an error is occurred during the last load
@@ -36,17 +25,17 @@ function docShellToWindow(docShell) {
  *
  * @param   {nsIDocShell} docShell - the root docShell object
  */
 function* iterateDocShellTree(docShell) {
   let docShellsEnum = docShell.getDocShellEnumerator(
     docShell.typeContent, docShell.ENUMERATE_FORWARDS);
 
   while (docShellsEnum.hasMoreElements()) {
-    yield docShellsEnum.getNext();
+    yield docShellsEnum.getNext().QueryInterface(Ci.nsIDocShell);
   }
 }
 
 /**
  * Returns the frame ID of the given window. If the window is the
  * top-level content window, its frame ID is 0. Otherwise, its frame ID
  * is its outer window ID.
  *
@@ -76,28 +65,27 @@ function getParentFrameId(window) {
   return getFrameId(window.parent);
 }
 
 function getDocShellFrameId(docShell) {
   if (!docShell) {
     return undefined;
   }
 
-  return getFrameId(docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIDOMWindow));
+  return getFrameId(docShell.domWindow);
 }
 
 /**
  * Convert a docShell object into its internal FrameDetail representation.
  *
  * @param    {nsIDocShell} docShell - the docShell object to be converted into a FrameDetail JSON object.
  * @returns  {FrameDetail} the FrameDetail JSON object which represents the docShell.
  */
 function convertDocShellToFrameDetail(docShell) {
-  let window = docShellToWindow(docShell);
+  let window = docShell.domWindow;
 
   return {
     frameId: getFrameId(window),
     parentFrameId: getParentFrameId(window),
     url: window.location.href,
   };
 }
 
@@ -108,17 +96,17 @@ function convertDocShellToFrameDetail(do
  * @param  {number}      frameId - the frame ID of the frame to retrieve, as
  *                                 described in getFrameId.
  * @param   {nsIDocShell} rootDocShell - the root docShell object
  * @returns {nsIDocShell?} the docShell with the given frameId, or null
  *                         if no match.
  */
 function findDocShell(frameId, rootDocShell) {
   for (let docShell of iterateDocShellTree(rootDocShell)) {
-    if (frameId == getFrameId(docShellToWindow(docShell))) {
+    if (frameId == getFrameId(docShell.domWindow)) {
       return docShell;
     }
   }
 
   return null;
 }
 
 var WebNavigationFrames = {
--- a/toolkit/mozapps/downloads/nsHelperAppDlg.js
+++ b/toolkit/mozapps/downloads/nsHelperAppDlg.js
@@ -151,20 +151,17 @@ nsUnknownContentTypeDialog.prototype = {
   // When opening from new tab, if tab closes while dialog is opening,
   // (which is a race condition on the XUL file being cached and the timer
   // in nsExternalHelperAppService), the dialog gets a blur and doesn't
   // activate the OK button.  So we wait a bit before doing opening it.
   reallyShow() {
     try {
       let ir = this.mContext.QueryInterface(Ci.nsIInterfaceRequestor);
       let docShell = ir.getInterface(Ci.nsIDocShell);
-      let rootWin = docShell.QueryInterface(Ci.nsIDocShellTreeItem)
-                                 .rootTreeItem
-                                 .QueryInterface(Ci.nsIInterfaceRequestor)
-                                 .getInterface(Ci.nsIDOMWindow);
+      let rootWin = docShell.rootTreeItem.domWindow;
       this.mDialog = Services.ww.openWindow(rootWin,
                                             "chrome://mozapps/content/downloads/unknownContentType.xul",
                                             null,
                                             "chrome,centerscreen,titlebar,dialog=yes,dependent",
                                             null);
     } catch (ex) {
       // The containing window may have gone away.  Break reference
       // cycles and stop doing the download.
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -273,22 +273,17 @@ function isDiscoverEnabled() {
 
   return true;
 }
 
 /**
  * Obtain the main DOMWindow for the current context.
  */
 function getMainWindow() {
-  return window.QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIWebNavigation)
-               .QueryInterface(Ci.nsIDocShellTreeItem)
-               .rootTreeItem
-               .QueryInterface(Ci.nsIInterfaceRequestor)
-               .getInterface(Ci.nsIDOMWindow);
+  return window.docShell.rootTreeItem.domWindow;
 }
 
 function getBrowserElement() {
   return window.QueryInterface(Ci.nsIInterfaceRequestor)
                .getInterface(Ci.nsIDocShell)
                .chromeEventHandler;
 }
 
@@ -1905,18 +1900,17 @@ var gHeader = {
     var docshellItem = window.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIWebNavigation)
                              .QueryInterface(Ci.nsIDocShellTreeItem);
 
     // If there is no outer frame then make the buttons visible
     if (docshellItem.rootTreeItem == docshellItem)
       return true;
 
-    var outerWin = docshellItem.rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
-                                            .getInterface(Ci.nsIDOMWindow);
+    var outerWin = docshellItem.rootTreeItem.domWindow;
     var outerDoc = outerWin.document;
     var node = outerDoc.getElementById("back-button");
     // If the outer frame has no back-button then make the buttons visible
     if (!node)
       return true;
 
     // If the back-button or any of its parents are hidden then make the buttons
     // visible
@@ -3451,17 +3445,17 @@ var gDragDrop = {
   }
 };
 
 // Stub tabbrowser implementation for use by the tab-modal alert code
 // when an alert/prompt/confirm method is called in a WebExtensions options_ui page
 // (See Bug 1385548 for rationale).
 var gBrowser = {
   getTabModalPromptBox(browser) {
-    const parentWindow = document.docShell.chromeEventHandler.ownerGlobal;
+    const parentWindow = window.docShell.chromeEventHandler.ownerGlobal;
 
     if (parentWindow.gBrowser) {
       return parentWindow.gBrowser.getTabModalPromptBox(browser);
     }
 
     return null;
   }
 };
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -449,21 +449,20 @@ function restart_manager(aManagerWindow,
 
   return close_manager(aManagerWindow)
     .then(() => open_manager(aView, aCallback, aLoadCallback));
 }
 
 function wait_for_window_open(aCallback) {
   let p = new Promise(resolve => {
     Services.wm.addListener({
-      onOpenWindow(aWindow) {
+      onOpenWindow(aXulWin) {
         Services.wm.removeListener(this);
 
-        let domwindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                               .getInterface(Ci.nsIDOMWindow);
+        let domwindow = aXulWin.docShell.domWindow;
         domwindow.addEventListener("load", function() {
           executeSoon(function() {
             resolve(domwindow);
           });
         }, {once: true});
       },
 
       onCloseWindow(aWindow) {
--- a/toolkit/mozapps/extensions/test/xpinstall/head.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/head.js
@@ -316,19 +316,18 @@ var Harness = {
       });
       this.expectingCancelled = false;
       this.endTest();
     }
   },
 
   // nsIWindowMediatorListener
 
-  onOpenWindow(window) {
-    var domwindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindow);
+  onOpenWindow(xulWin) {
+    var domwindow = xulWin.docShell.domWindow;
     var self = this;
     waitForFocus(function() {
       self.windowReady(domwindow);
     }, domwindow);
   },
 
   onCloseWindow(window) {
   },
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -3293,16 +3293,21 @@ profiler_feature_active(uint32_t aFeatur
 void
 profiler_register_thread(const char* aName, void* aGuessStackTop)
 {
   DEBUG_LOG("profiler_register_thread(%s)", aName);
 
   MOZ_ASSERT_IF(NS_IsMainThread(), Scheduler::IsCooperativeThread());
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
+  // Make sure we have a nsThread wrapper for the current thread, and that NSPR
+  // knows its name.
+  (void) NS_GetCurrentThread();
+  NS_SetCurrentThreadName(aName);
+
   PSAutoLock lock(gPSMutex);
 
   void* stackTop = GetStackTop(aGuessStackTop);
   locked_register_thread(lock, aName, stackTop);
 }
 
 void
 profiler_unregister_thread()
--- a/widget/headless/tests/test_headless.js
+++ b/widget/headless/tests/test_headless.js
@@ -32,18 +32,17 @@ function loadContentWindow(webNavigation
           return;
         }
         // Ignore events that don't change the document
         if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) {
           return;
         }
         let docShell = webNavigation.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDocShell);
-        let contentWindow = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-                            .getInterface(Ci.nsIDOMWindow);
+        let contentWindow = docShell.domWindow;
         webProgress.removeProgressListener(progressListener);
         progressListeners.delete(progressListener);
         contentWindow.addEventListener("load", (event) => {
           resolve(contentWindow);
         }, { once: true });
       },
       QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener",
                                              "nsISupportsWeakReference"])
--- a/widget/tests/test_bug593307.xul
+++ b/widget/tests/test_bug593307.xul
@@ -24,22 +24,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 SimpleTest.waitForExplicitFinish();
 
 function finish() {
   offscreenWindow.close();
   SimpleTest.finish();
 }
 
-var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIWebNavigation)
-                       .QueryInterface(Ci.nsIDocShellTreeItem)
-                       .rootTreeItem
-                       .QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDOMWindow);
+var mainWindow = window.docShell.rootTreeItem.domWindow;
 
 var offscreenWindow = mainWindow.openDialog("window_bug593307_offscreen.xul", "",
                                             "dialog=no,chrome,width=200,height=200,screenX=-3000,screenY=-3000",
                                             SimpleTest, finish);
 
 ]]>
 </script>
 
--- a/widget/tests/test_bug760802.xul
+++ b/widget/tests/test_bug760802.xul
@@ -53,17 +53,17 @@ function doesntThrowException(fun) {
 }
 
 var baseWindow = getBaseWindowInterface(this);
 var nativeHandle = baseWindow.nativeHandle;
 $("display").innerHTML = "found nativeHandle for this window: "+nativeHandle;
 
 var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
 var win = wm.getMostRecentWindow("navigator:browser");
-var docShell = getBaseWindowInterfaceFromDocShell(win);
+let docShell = getBaseWindowInterfaceFromDocShell(win);
 
 ok(
   shouldThrowException(function(){docShell.nativeHandle;},
               Cr.NS_ERROR_NOT_IMPLEMENTED),
   "nativeHandle should not be implemented for nsDocShell"
 );
 
 ok(typeof(nativeHandle) === "string", "nativeHandle should be a string");
--- a/widget/tests/test_taskbar_progress.xul
+++ b/widget/tests/test_taskbar_progress.xul
@@ -35,17 +35,17 @@
 
       is(taskbar.available, IsWin7OrHigher(), "Expected availability of taskbar");
       if (!taskbar.available)
         return null;
       
       // HACK from mconnor:
       var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
       let win = wm.getMostRecentWindow("navigator:browser");
-      let docShell = win.document.docShell;
+      let docShell = win.docShell;
 
       let progress = taskbar.getTaskbarProgress(docShell);
       isnot(progress, null, "Progress is not null");
 
       try {
         taskbar.getTaskbarProgress(null);
         ok(false, "Got progress for null docshell");
       } catch (e) {
--- a/widget/windows/nsNativeThemeWin.cpp
+++ b/widget/windows/nsNativeThemeWin.cpp
@@ -1051,32 +1051,32 @@ nsNativeThemeWin::GetThemePartAndState(n
       aState = TS_NORMAL;
       return NS_OK;
     }
     case StyleAppearance::ScrollbarbuttonUp:
     case StyleAppearance::ScrollbarbuttonDown:
     case StyleAppearance::ScrollbarbuttonLeft:
     case StyleAppearance::ScrollbarbuttonRight: {
       aPart = SP_BUTTON;
-      aState = (int(aWidgetType) - int(StyleAppearance::ScrollbarbuttonDown))*4;
+      aState = (int(aWidgetType) - int(StyleAppearance::ScrollbarbuttonUp))*4;
       EventStates eventState = GetContentState(aFrame, aWidgetType);
       if (!aFrame)
         aState += TS_NORMAL;
       else if (IsDisabled(aFrame, eventState))
         aState += TS_DISABLED;
       else {
         nsIFrame *parent = aFrame->GetParent();
         EventStates parentState =
           GetContentState(parent, parent->StyleDisplay()->mAppearance);
         if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
           aState += TS_ACTIVE;
         else if (eventState.HasState(NS_EVENT_STATE_HOVER))
           aState += TS_HOVER;
         else if (parentState.HasState(NS_EVENT_STATE_HOVER))
-          aState = (int(aWidgetType) - int(StyleAppearance::ScrollbarbuttonDown)) + SP_BUTTON_IMPLICIT_HOVER_BASE;
+          aState = (int(aWidgetType) - int(StyleAppearance::ScrollbarbuttonUp)) + SP_BUTTON_IMPLICIT_HOVER_BASE;
         else
           aState += TS_NORMAL;
       }
       return NS_OK;
     }
     case StyleAppearance::ScrollbarHorizontal:
     case StyleAppearance::ScrollbarVertical: {
       aPart = (aWidgetType == StyleAppearance::ScrollbarHorizontal) ?
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -400,105 +400,47 @@ nsThread::ThreadListMutex()
 
 /* static */ LinkedList<nsThread>&
 nsThread::ThreadList()
 {
   static LinkedList<nsThread> sList;
   return sList;
 }
 
+/* static */ void
+nsThread::ClearThreadList()
+{
+  OffTheBooksMutexAutoLock mal(ThreadListMutex());
+  while (ThreadList().popFirst()) {}
+}
+
 /* static */ nsThreadEnumerator
 nsThread::Enumerate()
 {
   return {};
 }
 
 /*static*/ void
 nsThread::ThreadFunc(void* aArg)
 {
   using mozilla::ipc::BackgroundChild;
 
   ThreadInitData* initData = static_cast<ThreadInitData*>(aArg);
   nsThread* self = initData->thread;  // strong reference
 
   self->mThread = PR_GetCurrentThread();
-  self->mThreadId = uint32_t(PlatformThread::CurrentId());
   self->mVirtualThread = GetCurrentVirtualThread();
   self->mEventTarget->SetCurrentThread();
   SetupCurrentThreadForChaosMode();
 
   if (!initData->name.IsEmpty()) {
     NS_SetCurrentThreadName(initData->name.BeginReading());
   }
 
-  {
-#if defined(XP_LINUX)
-    pthread_attr_t attr;
-    pthread_attr_init(&attr);
-    pthread_getattr_np(pthread_self(), &attr);
-
-    size_t stackSize;
-    pthread_attr_getstack(&attr, &self->mStackBase, &stackSize);
-
-    // Glibc prior to 2.27 reports the stack size and base including the guard
-    // region, so we need to compensate for it to get accurate accounting.
-    // Also, this behavior difference isn't guarded by a versioned symbol, so we
-    // actually need to check the runtime glibc version, not the version we were
-    // compiled against.
-    static bool sAdjustForGuardSize = ({
-#ifdef __GLIBC__
-      unsigned major, minor;
-      sscanf(gnu_get_libc_version(), "%u.%u", &major, &minor) < 2 ||
-        major < 2 || (major == 2 && minor < 27);
-#else
-      false;
-#endif
-    });
-    if (sAdjustForGuardSize) {
-      size_t guardSize;
-      pthread_attr_getguardsize(&attr, &guardSize);
-
-      // Note: This assumes that the stack grows down, as is the case on all of
-      // our tier 1 platforms. On platforms where the stack grows up, the
-      // mStackBase adjustment is unnecessary, but doesn't cause any harm other
-      // than under-counting stack memory usage by one page.
-      self->mStackBase = reinterpret_cast<char*>(self->mStackBase) + guardSize;
-      stackSize -= guardSize;
-    }
-
-    self->mStackSize = stackSize;
-
-    // This is a bit of a hack.
-    //
-    // We really do want the NOHUGEPAGE flag on our thread stacks, since we
-    // don't expect any of them to need anywhere near 2MB of space. But setting
-    // it here is too late to have an effect, since the first stack page has
-    // already been faulted in existence, and NSPR doesn't give us a way to set
-    // it beforehand.
-    //
-    // What this does get us, however, is a different set of VM flags on our
-    // thread stacks compared to normal heap memory. Which makes the Linux
-    // kernel report them as separate regions, even when they are adjacent to
-    // heap memory. This allows us to accurately track the actual memory
-    // consumption of our allocated stacks.
-    madvise(self->mStackBase, stackSize, MADV_NOHUGEPAGE);
-
-    pthread_attr_destroy(&attr);
-#elif defined(XP_WIN)
-    static const DynamicallyLinkedFunctionPtr<GetCurrentThreadStackLimitsFn>
-      sGetStackLimits(L"kernel32.dll", "GetCurrentThreadStackLimits");
-
-    if (sGetStackLimits) {
-      ULONG_PTR stackBottom, stackTop;
-      sGetStackLimits(&stackBottom, &stackTop);
-      self->mStackBase = reinterpret_cast<void*>(stackBottom);
-      self->mStackSize = stackTop - stackBottom;
-    }
-#endif
-  }
+  self->InitCommon();
 
   // Inform the ThreadManager
   nsThreadManager::get().RegisterCurrentThread(*self);
 
   mozilla::IOInterposer::RegisterCurrentThread();
 
   // This must come after the call to nsThreadManager::RegisterCurrentThread(),
   // because that call is needed to properly set up this thread as an nsThread,
@@ -569,16 +511,91 @@ nsThread::ThreadFunc(void* aArg)
 
 #ifdef MOZ_TASK_TRACER
   FreeTraceInfo();
 #endif
 
   NS_RELEASE(self);
 }
 
+void
+nsThread::InitCommon()
+{
+  mThreadId = uint32_t(PlatformThread::CurrentId());
+
+  {
+#if defined(XP_LINUX)
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_getattr_np(pthread_self(), &attr);
+
+    size_t stackSize;
+    pthread_attr_getstack(&attr, &mStackBase, &stackSize);
+
+    // Glibc prior to 2.27 reports the stack size and base including the guard
+    // region, so we need to compensate for it to get accurate accounting.
+    // Also, this behavior difference isn't guarded by a versioned symbol, so we
+    // actually need to check the runtime glibc version, not the version we were
+    // compiled against.
+    static bool sAdjustForGuardSize = ({
+#ifdef __GLIBC__
+      unsigned major, minor;
+      sscanf(gnu_get_libc_version(), "%u.%u", &major, &minor) < 2 ||
+        major < 2 || (major == 2 && minor < 27);
+#else
+      false;
+#endif
+    });
+    if (sAdjustForGuardSize) {
+      size_t guardSize;
+      pthread_attr_getguardsize(&attr, &guardSize);
+
+      // Note: This assumes that the stack grows down, as is the case on all of
+      // our tier 1 platforms. On platforms where the stack grows up, the
+      // mStackBase adjustment is unnecessary, but doesn't cause any harm other
+      // than under-counting stack memory usage by one page.
+      mStackBase = reinterpret_cast<char*>(mStackBase) + guardSize;
+      stackSize -= guardSize;
+    }
+
+    mStackSize = stackSize;
+
+    // This is a bit of a hack.
+    //
+    // We really do want the NOHUGEPAGE flag on our thread stacks, since we
+    // don't expect any of them to need anywhere near 2MB of space. But setting
+    // it here is too late to have an effect, since the first stack page has
+    // already been faulted in existence, and NSPR doesn't give us a way to set
+    // it beforehand.
+    //
+    // What this does get us, however, is a different set of VM flags on our
+    // thread stacks compared to normal heap memory. Which makes the Linux
+    // kernel report them as separate regions, even when they are adjacent to
+    // heap memory. This allows us to accurately track the actual memory
+    // consumption of our allocated stacks.
+    madvise(mStackBase, stackSize, MADV_NOHUGEPAGE);
+
+    pthread_attr_destroy(&attr);
+#elif defined(XP_WIN)
+    static const DynamicallyLinkedFunctionPtr<GetCurrentThreadStackLimitsFn>
+      sGetStackLimits(L"kernel32.dll", "GetCurrentThreadStackLimits");
+
+    if (sGetStackLimits) {
+      ULONG_PTR stackBottom, stackTop;
+      sGetStackLimits(&stackBottom, &stackTop);
+      mStackBase = reinterpret_cast<void*>(stackBottom);
+      mStackSize = stackTop - stackBottom;
+    }
+#endif
+  }
+
+  OffTheBooksMutexAutoLock mal(ThreadListMutex());
+  ThreadList().insertBack(this);
+}
+
 //-----------------------------------------------------------------------------
 
 // Tell the crash reporter to save a memory report if our heuristics determine
 // that an OOM failure is likely to occur soon.
 // Memory usage will not be checked more than every 30 seconds or saved more
 // than every 3 minutes
 // If |aShouldSave == kForceReport|, a report will be saved regardless of
 // whether the process is low on memory or not. However, it will still not be
@@ -665,17 +682,27 @@ nsThread::nsThread(NotNull<SynchronizedE
   , mCurrentPerformanceCounter(nullptr)
 {
 }
 
 nsThread::~nsThread()
 {
   NS_ASSERTION(mRequestedShutdownContexts.IsEmpty(),
                "shouldn't be waiting on other threads to shutdown");
-  MOZ_ASSERT(!isInList());
+
+  // We shouldn't need to lock before checking isInList at this point. We're
+  // destroying the last reference to this object, so there's no way for anyone
+  // else to remove it in the middle of our check. And the not-in-list state is
+  // determined the element's next and previous members pointing to itself, so a
+  // non-atomic update to an adjacent member won't affect the outcome either.
+  if (isInList()) {
+    OffTheBooksMutexAutoLock mal(ThreadListMutex());
+    removeFrom(ThreadList());
+  }
+
 #ifdef DEBUG
   // We deliberately leak these so they can be tracked by the leak checker.
   // If you're having nsThreadShutdownContext leaks, you can set:
   //   XPCOM_MEM_LOG_CLASSES=nsThreadShutdownContext
   // during a test run and that will at least tell you what thread is
   // requesting shutdown on another, which can be helpful for diagnosing
   // the leak.
   for (size_t i = 0; i < mRequestedShutdownContexts.Length(); ++i) {
@@ -699,21 +726,16 @@ nsThread::Init(const nsACString& aName)
   // ThreadFunc is responsible for setting mThread
   if (!PR_CreateThread(PR_USER_THREAD, ThreadFunc, &initData,
                        PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
                        PR_JOINABLE_THREAD, mStackSize)) {
     NS_RELEASE_THIS();
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  {
-    OffTheBooksMutexAutoLock mal(ThreadListMutex());
-    ThreadList().insertBack(this);
-  }
-
   // ThreadFunc will wait for this event to be run before it tries to access
   // mThread.  By delaying insertion of this event into the queue, we ensure
   // that mThread is set properly.
   {
     mEvents->PutEvent(do_AddRef(startup), EventPriority::Normal); // retain a reference
   }
 
   // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
@@ -723,16 +745,17 @@ nsThread::Init(const nsACString& aName)
 }
 
 nsresult
 nsThread::InitCurrentThread()
 {
   mThread = PR_GetCurrentThread();
   mVirtualThread = GetCurrentVirtualThread();
   SetupCurrentThreadForChaosMode();
+  InitCommon();
 
   nsThreadManager::get().RegisterCurrentThread(*this);
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsIEventTarget
 
@@ -851,16 +874,23 @@ nsThread::ShutdownInternal(bool aSync)
 }
 
 void
 nsThread::ShutdownComplete(NotNull<nsThreadShutdownContext*> aContext)
 {
   MOZ_ASSERT(mThread);
   MOZ_ASSERT(aContext->mTerminatingThread == this);
 
+  {
+    OffTheBooksMutexAutoLock mal(ThreadListMutex());
+    if (isInList()) {
+      removeFrom(ThreadList());
+    }
+  }
+
   if (aContext->mAwaitingShutdownAck) {
     // We're in a synchronous shutdown, so tell whatever is up the stack that
     // we're done and unwind the stack so it can call us again.
     aContext->mAwaitingShutdownAck = false;
     return;
   }
 
   // Now, it should be safe to join without fear of dead-locking.
--- a/xpcom/threads/nsThread.h
+++ b/xpcom/threads/nsThread.h
@@ -61,16 +61,22 @@ public:
            uint32_t aStackSize);
 
   // Initialize this as a wrapper for a new PRThread, and optionally give it a name.
   nsresult Init(const nsACString& aName = NS_LITERAL_CSTRING(""));
 
   // Initialize this as a wrapper for the current PRThread.
   nsresult InitCurrentThread();
 
+private:
+  // Initializes the mThreadId and stack base/size members, and adds the thread
+  // to the ThreadList().
+  void InitCommon();
+
+public:
   // The PRThread corresponding to this thread.
   PRThread* GetPRThread()
   {
     return mThread;
   }
 
   const void* StackBase() const { return mStackBase; }
   size_t StackSize() const { return mStackSize; }
@@ -165,18 +171,21 @@ protected:
   {
     nsIThreadObserver* obs;
     nsThread::GetObserver(&obs);
     return already_AddRefed<nsIThreadObserver>(obs);
   }
 
   struct nsThreadShutdownContext* ShutdownInternal(bool aSync);
 
+  friend class nsThreadManager;
+
   static mozilla::OffTheBooksMutex& ThreadListMutex();
   static mozilla::LinkedList<nsThread>& ThreadList();
+  static void ClearThreadList();
 
   RefPtr<mozilla::SynchronizedEventQueue> mEvents;
   RefPtr<mozilla::ThreadEventTarget> mEventTarget;
 
   // The shutdown contexts for any other threads we've asked to shut down.
   nsTArray<nsAutoPtr<struct nsThreadShutdownContext>> mRequestedShutdownContexts;
   // The shutdown context for ourselves.
   struct nsThreadShutdownContext* mShutdownContext;
@@ -200,16 +209,18 @@ protected:
   bool IsMainThread() const
   {
     return MainThreadFlag(mIsMainThread) == MAIN_THREAD;
   }
 
   // Set to true if this thread creates a JSRuntime.
   bool mCanInvokeJS;
 
+  bool mHasTLSEntry = false;
+
   // Used to track which event is being executed by ProcessNextEvent
   nsCOMPtr<nsIRunnable> mCurrentEvent;
 
   mozilla::TimeStamp mCurrentEventStart;
   mozilla::TimeStamp mNextIdleDeadline;
 
   RefPtr<mozilla::PerformanceCounter> mCurrentPerformanceCounter;
 };
--- a/xpcom/threads/nsThreadManager.cpp
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -89,22 +89,37 @@ AssertIsOnMainThread()
 }
 
 } // mozilla namespace
 
 #endif
 
 typedef nsTArray<NotNull<RefPtr<nsThread>>> nsThreadArray;
 
+static bool sShutdownComplete;
+
 //-----------------------------------------------------------------------------
 
-static void
-ReleaseObject(void* aData)
+/* static */ void
+nsThreadManager::ReleaseThread(void* aData)
 {
-  static_cast<nsISupports*>(aData)->Release();
+  if (sShutdownComplete) {
+    // We've already completed shutdown and released the references to all or
+    // our TLS wrappers. Don't try to release them again.
+    return;
+  }
+
+  auto* thread = static_cast<nsThread*>(aData);
+
+  get().UnregisterCurrentThread(*thread, true);
+
+  if (thread->mHasTLSEntry) {
+    thread->mHasTLSEntry = false;
+    thread->Release();
+  }
 }
 
 // statically allocated instance
 NS_IMETHODIMP_(MozExternalRefCountType)
 nsThreadManager::AddRef()
 {
   return 2;
 }
@@ -231,17 +246,17 @@ nsThreadManager::Init()
   }
 
   if (!gTlsCurrentVirtualThread.init()) {
     return NS_ERROR_UNEXPECTED;
   }
 
   Scheduler::EventLoopActivation::Init();
 
-  if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE) {
+  if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseThread) == PR_FAILURE) {
     return NS_ERROR_FAILURE;
   }
 
 
 #ifdef MOZ_CANARY
   const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
   const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
   char* env_var_flag = getenv("MOZ_KILL_CANARIES");
@@ -299,42 +314,44 @@ nsThreadManager::Shutdown()
   // between when NewThread started, and when the thread finished initializing
   // and registering with ThreadManager.
   //
   mInitialized = false;
 
   // Empty the main thread event queue before we begin shutting down threads.
   NS_ProcessPendingEvents(mMainThread);
 
-  // We gather the threads from the hashtable into a list, so that we avoid
-  // holding the hashtable lock while calling nsIThread::Shutdown.
-  nsThreadArray threads;
   {
-    OffTheBooksMutexAutoLock lock(mLock);
-    for (auto iter = mThreadsByPRThread.Iter(); !iter.Done(); iter.Next()) {
-      RefPtr<nsThread>& thread = iter.Data();
-      threads.AppendElement(WrapNotNull(thread));
-      iter.Remove();
+    // We gather the threads from the hashtable into a list, so that we avoid
+    // holding the hashtable lock while calling nsIThread::Shutdown.
+    nsThreadArray threads;
+    {
+      OffTheBooksMutexAutoLock lock(mLock);
+      for (auto iter = mThreadsByPRThread.Iter(); !iter.Done(); iter.Next()) {
+        RefPtr<nsThread>& thread = iter.Data();
+        threads.AppendElement(WrapNotNull(thread));
+        iter.Remove();
+      }
     }
-  }
 
-  // It's tempting to walk the list of threads here and tell them each to stop
-  // accepting new events, but that could lead to badness if one of those
-  // threads is stuck waiting for a response from another thread.  To do it
-  // right, we'd need some way to interrupt the threads.
-  //
-  // Instead, we process events on the current thread while waiting for threads
-  // to shutdown.  This means that we have to preserve a mostly functioning
-  // world until such time as the threads exit.
+    // It's tempting to walk the list of threads here and tell them each to stop
+    // accepting new events, but that could lead to badness if one of those
+    // threads is stuck waiting for a response from another thread.  To do it
+    // right, we'd need some way to interrupt the threads.
+    //
+    // Instead, we process events on the current thread while waiting for threads
+    // to shutdown.  This means that we have to preserve a mostly functioning
+    // world until such time as the threads exit.
 
-  // Shutdown all threads that require it (join with threads that we created).
-  for (uint32_t i = 0; i < threads.Length(); ++i) {
-    NotNull<nsThread*> thread = threads[i];
-    if (thread->ShutdownRequired()) {
-      thread->Shutdown();
+    // Shutdown all threads that require it (join with threads that we created).
+    for (uint32_t i = 0; i < threads.Length(); ++i) {
+      NotNull<nsThread*> thread = threads[i];
+      if (thread->ShutdownRequired()) {
+        thread->Shutdown();
+      }
     }
   }
 
   // NB: It's possible that there are events in the queue that want to *start*
   // an asynchronous shutdown. But we have already shutdown the threads above,
   // so there's no need to worry about them. We only have to wait for all
   // in-flight asynchronous thread shutdowns to complete.
   mMainThread->WaitForAllAsynchronousShutdowns();
@@ -355,16 +372,34 @@ nsThreadManager::Shutdown()
   // have been processed.
   mMainThread->SetObserver(nullptr);
 
   // Release main thread object.
   mMainThread = nullptr;
 
   // Remove the TLS entry for the main thread.
   PR_SetThreadPrivate(mCurThreadIndex, nullptr);
+
+  {
+    // Cleanup the last references to any threads which haven't shut down yet.
+    nsTArray<RefPtr<nsThread>> threads;
+    for (auto* thread : nsThread::Enumerate()) {
+      if (thread->mHasTLSEntry) {
+        threads.AppendElement(dont_AddRef(thread));
+        thread->mHasTLSEntry = false;
+      }
+    }
+  }
+
+  // xpcshell tests sometimes leak the main thread. They don't enable leak
+  // checking, so that doesn't cause the test to fail, but leaving the entry in
+  // the thread list triggers an assertion, which does.
+  nsThread::ClearThreadList();
+
+  sShutdownComplete = true;
 }
 
 void
 nsThreadManager::RegisterCurrentThread(nsThread& aThread)
 {
   MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
 
   OffTheBooksMutexAutoLock lock(mLock);
@@ -372,31 +407,38 @@ nsThreadManager::RegisterCurrentThread(n
   ++mCurrentNumberOfThreads;
   if (mCurrentNumberOfThreads > mHighestNumberOfThreads) {
     mHighestNumberOfThreads = mCurrentNumberOfThreads;
   }
 
   mThreadsByPRThread.Put(aThread.GetPRThread(), &aThread);  // XXX check OOM?
 
   aThread.AddRef();  // for TLS entry
+  aThread.mHasTLSEntry = true;
   PR_SetThreadPrivate(mCurThreadIndex, &aThread);
 }
 
 void
-nsThreadManager::UnregisterCurrentThread(nsThread& aThread)
+nsThreadManager::UnregisterCurrentThread(nsThread& aThread, bool aIfExists)
 {
-  MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
+  {
+    OffTheBooksMutexAutoLock lock(mLock);
 
-  OffTheBooksMutexAutoLock lock(mLock);
+    if (aIfExists && !mThreadsByPRThread.GetWeak(aThread.GetPRThread())) {
+      return;
+    }
 
-  --mCurrentNumberOfThreads;
-  mThreadsByPRThread.Remove(aThread.GetPRThread());
+    MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
+
+    --mCurrentNumberOfThreads;
+    mThreadsByPRThread.Remove(aThread.GetPRThread());
+  }
 
   PR_SetThreadPrivate(mCurThreadIndex, nullptr);
-  // Ref-count balanced via ReleaseObject
+  // Ref-count balanced via ReleaseThread
 }
 
 nsThread*
 nsThreadManager::CreateCurrentThread(SynchronizedEventQueue* aQueue,
                                      nsThread::MainThreadFlag aMainThread)
 {
   // Make sure we don't have an nsThread yet.
   MOZ_ASSERT(!PR_GetThreadPrivate(mCurThreadIndex));
@@ -436,17 +478,23 @@ nsThreadManager::GetCurrentThread()
   }
 
   return thread.get();  // reference held in TLS
 }
 
 bool
 nsThreadManager::IsNSThread() const
 {
-  return mInitialized && !!PR_GetThreadPrivate(mCurThreadIndex);
+  if (!mInitialized) {
+    return false;
+  }
+  if (auto* thread = (nsThread*)PR_GetThreadPrivate(mCurThreadIndex)) {
+    return thread->mShutdownRequired;
+  }
+  return false;
 }
 
 NS_IMETHODIMP
 nsThreadManager::NewThread(uint32_t aCreationFlags,
                            uint32_t aStackSize,
                            nsIThread** aResult)
 {
   return NewNamedThread(NS_LITERAL_CSTRING(""), aStackSize, aResult);
--- a/xpcom/threads/nsThreadManager.h
+++ b/xpcom/threads/nsThreadManager.h
@@ -31,17 +31,17 @@ public:
   void Shutdown();
 
   // Called by nsThread to inform the ThreadManager it exists.  This method
   // must be called when the given thread is the current thread.
   void RegisterCurrentThread(nsThread& aThread);
 
   // Called by nsThread to inform the ThreadManager it is going away.  This
   // method must be called when the given thread is the current thread.
-  void UnregisterCurrentThread(nsThread& aThread);
+  void UnregisterCurrentThread(nsThread& aThread, bool aIfExists = false);
 
   // Returns the current thread.  Returns null if OOM or if ThreadManager isn't
   // initialized.  Creates the nsThread if one does not exist yet.
   nsThread* GetCurrentThread();
 
   // Returns true iff the currently running thread has an nsThread associated
   // with it (ie; whether this is a thread that we can dispatch runnables to).
   bool IsNSThread() const;
@@ -80,16 +80,18 @@ private:
     , mHighestNumberOfThreads(1)
   {
   }
 
   nsresult
   SpinEventLoopUntilInternal(nsINestedEventLoopCondition* aCondition,
                              bool aCheckingShutdown);
 
+  static void ReleaseThread(void* aData);
+
   nsRefPtrHashtable<nsPtrHashKey<PRThread>, nsThread> mThreadsByPRThread;
   unsigned            mCurThreadIndex;  // thread-local-storage index
   RefPtr<nsThread>  mMainThread;
   PRThread*         mMainPRThread;
   mozilla::OffTheBooksMutex mLock;  // protects tables
   mozilla::Atomic<bool,
                   mozilla::SequentiallyConsistent,
                   mozilla::recordreplay::Behavior::DontPreserve> mInitialized;