Merge mozilla-central to autoland.
authorCosmin Sabou <csabou@mozilla.com>
Sat, 02 Mar 2019 23:49:23 +0200
changeset 520043 9d7e7f0bc3eb64db9db2e816b32f16ab3782cac5
parent 520042 f05208c48b860a5fd4461e13a1eafc61832326b4 (current diff)
parent 520041 997e98493dbcd55c95b4a6eb4a44f563cd6174e9 (diff)
child 520044 922daa7856228500ecad60dfba4442f8f3707588
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland.
browser/base/content/test/performance/browser_startup_hiddenwindow.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -235,20 +235,16 @@ pref("browser.startup.firstrunSkipsHomep
 // Held to nightly on Linux due to bug 1450626.
 // Disabled on Mac because the bouncing dock icon already provides feedback.
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) && defined(NIGHTLY_BUILD)
 pref("browser.startup.blankWindow", true);
 #else
 pref("browser.startup.blankWindow", false);
 #endif
 
-// Don't create the hidden window during startup on
-// platforms that don't always need it (Win/Linux).
-pref("toolkit.lazyHiddenWindow", true);
-
 pref("browser.slowStartup.notificationDisabled", false);
 pref("browser.slowStartup.timeThreshold", 20000);
 pref("browser.slowStartup.maxSamples", 5);
 
 // This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into
 // this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream
 // repackager of this code using an alternate snippet url, please keep your users safe
 pref("browser.aboutHomeSnippets.updateUrl", "https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/");
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -567,22 +567,22 @@ HistoryMenu.prototype = {
   _elements: {
     undoTabMenu: "historyUndoMenu",
     hiddenTabsMenu: "hiddenTabsMenu",
     undoWindowMenu: "historyUndoWindowMenu",
     syncTabsMenuitem: "sync-tabs-menuitem",
   },
 
   _getClosedTabCount() {
-    try {
-      return SessionStore.getClosedTabCount(window);
-    } catch (ex) {
-      // SessionStore doesn't track the hidden window, so just return zero then.
+    // SessionStore doesn't track the hidden window, so just return zero then.
+    if (window == Services.appShell.hiddenDOMWindow) {
       return 0;
     }
+
+    return SessionStore.getClosedTabCount(window);
   },
 
   toggleHiddenTabs() {
     if (window.gBrowser &&
         gBrowser.visibleTabs.length < gBrowser.tabs.length) {
       this.hiddenTabsMenu.removeAttribute("hidden");
     } else {
       this.hiddenTabsMenu.setAttribute("hidden", "true");
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1511,20 +1511,16 @@ var gBrowserInit = {
 
   _delayedStartup() {
     let { TelemetryTimestamps } =
       ChromeUtils.import("resource://gre/modules/TelemetryTimestamps.jsm");
     TelemetryTimestamps.add("delayedStartupStarted");
 
     this._cancelDelayedStartup();
 
-    // Bug 1531854 - The hidden window is force-created here
-    // until all of its dependencies are handled.
-    Services.appShell.hiddenDOMWindow;
-
     // We need to set the OfflineApps message listeners up before we
     // load homepages, which might need them.
     OfflineApps.init();
 
     gBrowser.addEventListener("InsecureLoginFormsStateChange", function() {
       gIdentityHandler.refreshForInsecureLoginForms();
     }, true);
 
--- a/browser/base/content/newInstall.js
+++ b/browser/base/content/newInstall.js
@@ -12,26 +12,25 @@ function init() {
   if (navigator.platform == "MacIntel") {
     hideMenus();
     window.addEventListener("unload", showMenus);
   }
 }
 
 let gHidden = [];
 let gCollapsed = [];
+let hiddenDoc = Services.appShell.hiddenDOMWindow.document;
 
 function hideItem(id) {
-  let hiddenDoc = Services.appShell.hiddenDOMWindow.document;
   let element = hiddenDoc.getElementById(id);
   element.hidden = true;
   gHidden.push(element);
 }
 
 function collapseItem(id) {
-  let hiddenDoc = Services.appShell.hiddenDOMWindow.document;
   let element = hiddenDoc.getElementById(id);
   element.collapsed = true;
   gCollapsed.push(element);
 }
 
 function hideMenus() {
   hideItem("macDockMenuNewWindow");
   hideItem("macDockMenuNewPrivateWindow");
--- a/browser/base/content/test/performance/browser.ini
+++ b/browser/base/content/test/performance/browser.ini
@@ -18,18 +18,16 @@ support-files =
   head.js
 [browser_appmenu.js]
 skip-if = asan || debug || (os == 'win' && bits == 32) # Bug 1382809, bug 1369959, Win32 because of intermittent OOM failures
 [browser_preferences_usage.js]
 skip-if = !debug
 [browser_startup.js]
 [browser_startup_content.js]
 skip-if = !e10s
-[browser_startup_hiddenwindow.js]
-skip-if = os == 'mac'
 [browser_startup_flicker.js]
 run-if = debug || devedition || nightly_build # Requires startupRecorder.js, which isn't shipped everywhere by default
 [browser_tabclose_grow.js]
 [browser_tabclose.js]
 skip-if = (os == 'win' && bits == 32) || (os == 'mac') # Bug 1488537, Bug 1531417
 [browser_tabdetach.js]
 [browser_tabopen.js]
 skip-if = (verify && (os == 'mac'))
deleted file mode 100644
--- a/browser/base/content/test/performance/browser_startup_hiddenwindow.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-add_task(async function() {
-  if (!AppConstants.NIGHTLY_BUILD && !AppConstants.MOZ_DEV_EDITION && !AppConstants.DEBUG) {
-    ok(!("@mozilla.org/test/startuprecorder;1" in Cc),
-       "the startup recorder component shouldn't exist in this non-nightly/non-devedition/" +
-       "non-debug build.");
-    return;
-  }
-
-  let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
-  await startupRecorder.done;
-
-  let extras = Cu.cloneInto(startupRecorder.data.extras, {});
-
-  let phasesExpectations = {
-    "before profile selection": false,
-    "before opening first browser window": false,
-    "before first paint": !Services.prefs.getBoolPref("toolkit.lazyHiddenWindow"),
-
-    // Bug 1531854
-    "before handling user events": true,
-    "before becoming idle": true,
-  };
-
-  for (let phase in extras) {
-    if (!(phase in phasesExpectations)) {
-      ok(false, `Startup phase '${phase}' should be specified.`);
-      continue;
-    }
-
-    is(extras[phase].hiddenWindowLoaded, phasesExpectations[phase],
-       `Hidden window loaded at '${phase}': ${phasesExpectations[phase]}`);
-  }
-});
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -1438,21 +1438,19 @@ BrowserGlue.prototype = {
     }
 
     Services.telemetry.scalarSet("contentblocking.fingerprinting_blocking_enabled", fpEnabled);
     Services.telemetry.scalarSet("contentblocking.cryptomining_blocking_enabled", cmEnabled);
     Services.telemetry.scalarSet("contentblocking.category", categoryPref);
   },
 
   _sendMediaTelemetry() {
-    let win = Services.wm.getMostRecentWindow("navigator:browser");
-    if (win) {
-      let v = win.document.createElementNS("http://www.w3.org/1999/xhtml", "video");
-      v.reportCanPlayTelemetry();
-    }
+    let win = Services.appShell.hiddenDOMWindow;
+    let v = win.document.createElementNS("http://www.w3.org/1999/xhtml", "video");
+    v.reportCanPlayTelemetry();
   },
 
   /**
    * Application shutdown handler.
    */
   _onQuitApplicationGranted() {
     // This pref must be set here because SessionStore will use its value
     // on quit-application.
@@ -2802,25 +2800,23 @@ BrowserGlue.prototype = {
     prefValue = prefValue || "general:5,suggestion:Infinity";
     Services.prefs.setCharPref(prefName, prefValue);
   },
 
   /**
    * Open preferences even if there are no open windows.
    */
   _openPreferences(...args) {
-    let chromeWindow = BrowserWindowTracker.getTopWindow();
-    if (chromeWindow) {
-      chromeWindow.openPreferences(...args);
+    if (Services.appShell.hiddenDOMWindow.openPreferences) {
+      Services.appShell.hiddenDOMWindow.openPreferences(...args);
       return;
     }
 
-    if (Services.appShell.hiddenDOMWindow.openPreferences) {
-      Services.appShell.hiddenDOMWindow.openPreferences(...args);
-    }
+    let chromeWindow = BrowserWindowTracker.getTopWindow();
+    chromeWindow.openPreferences(...args);
   },
 
   _openURLInNewWindow(url) {
     let urlString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
     urlString.data = url;
     return new Promise(resolve => {
       let win = Services.ww.openWindow(null, AppConstants.BROWSER_CHROME_URL,
                                        "_blank", "chrome,all,dialog=no", urlString);
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -180,30 +180,29 @@ var DownloadsCommon = {
     return PrefObserver.animateNotifications;
   },
 
   /**
    * Get access to one of the DownloadsData, PrivateDownloadsData, or
    * HistoryDownloadsData objects, depending on the privacy status of the
    * specified window and on whether history downloads should be included.
    *
-   * @param [optional] window
+   * @param window
    *        The browser window which owns the download button.
-   *        If not given, the privacy status will be assumed as non-private.
    * @param [optional] history
    *        True to include history downloads when the window is public.
    * @param [optional] privateAll
    *        Whether to force the public downloads data to be returned together
    *        with the private downloads data for a private window.
    * @param [optional] limited
    *        True to limit the amount of downloads returned to
    *        `kMaxHistoryResultsForLimitedView`.
    */
   getData(window, history = false, privateAll = false, limited = false) {
-    let isPrivate = window && PrivateBrowsingUtils.isContentWindowPrivate(window);
+    let isPrivate = PrivateBrowsingUtils.isContentWindowPrivate(window);
     if (isPrivate && !privateAll) {
       return PrivateDownloadsData;
     }
     if (history) {
       if (isPrivate && privateAll)
         return LimitedPrivateHistoryDownloadData;
       return limited ? LimitedHistoryDownloadsData : HistoryDownloadsData;
     }
--- a/browser/components/newtab/lib/DownloadsManager.jsm
+++ b/browser/components/newtab/lib/DownloadsManager.jsm
@@ -1,16 +1,15 @@
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 const {actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
-  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
   DownloadsViewUI: "resource:///modules/DownloadsViewUI.jsm",
   FileUtils: "resource://gre/modules/FileUtils.jsm",
 });
 
 const DOWNLOAD_CHANGED_DELAY_TIME = 1000; // time in ms to delay timer for downloads changed events
 
 this.DownloadsManager = class DownloadsManager {
@@ -37,18 +36,18 @@ this.DownloadsManager = class DownloadsM
                    DownloadsCommon.strings.sizeUnknown,
       referrer: download.source.referrer,
       date_added: download.endTime,
     };
   }
 
   init(store) {
     this._store = store;
-    this._downloadData = DownloadsCommon.getData(null /* null for non-private downloads */, 
-                                                 true, false, true);
+    this._browser = Services.appShell.hiddenDOMWindow;
+    this._downloadData = DownloadsCommon.getData(this._browser.ownerGlobal, true, false, true);
     this._downloadData.addView(this);
   }
 
   onDownloadAdded(download) {
     if (!this._downloadItems.has(download.source.url)) {
       this._downloadItems.set(download.source.url, download);
 
       // On startup, all existing downloads fire this notification, so debounce them
@@ -137,17 +136,17 @@ this.DownloadsManager = class DownloadsM
           DownloadsCommon.showDownloadedFile(
             new FileUtils.File(download.target.path));
         });
         break;
       case at.OPEN_DOWNLOAD_FILE:
         doDownloadAction(download => {
           DownloadsCommon.openDownloadedFile(
             new FileUtils.File(download.target.path), null,
-            BrowserWindowTracker.getTopWindow());
+            this._browser.ownerGlobal);
         });
         break;
       case at.UNINIT:
         this.uninit();
         break;
     }
   }
 };
--- a/browser/components/sessionstore/SessionSaver.jsm
+++ b/browser/components/sessionstore/SessionSaver.jsm
@@ -171,30 +171,32 @@ var SessionSaverInternal = {
 
     // Interval until the next disk operation is allowed.
     let interval = this._isIdle ? this._intervalWhileIdle : this._intervalWhileActive;
     delay = Math.max(this._lastSaveTime + interval - Date.now(), delay, 0);
 
     // Schedule a state save.
     this._wasIdle = this._isIdle;
     this._timeoutID = setTimeout(() => {
+      let hiddenDOMWindow = Services.appShell.hiddenDOMWindow;
+
       // Execute _saveStateAsync when we have enough idle time. Otherwise,
       // another idle request is made to schedule _saveStateAsync again.
       let saveStateAsyncWhenIdle = (deadline) => {
         // When looking at the telemetry data, the time it takes to execute
         // _saveStateAsync is around 5.9ms (median). Therefore,
         // we'll not execute the function when the idle time is less than 5ms.
         if (deadline.timeRemaining() < 5) {
-          this._idleCallbackID = requestIdleCallback(saveStateAsyncWhenIdle);
+          this._idleCallbackID = hiddenDOMWindow.requestIdleCallback(saveStateAsyncWhenIdle);
           return;
         }
         this._saveStateAsync();
       };
 
-      this._idleCallbackID = requestIdleCallback(saveStateAsyncWhenIdle);
+      this._idleCallbackID = hiddenDOMWindow.requestIdleCallback(saveStateAsyncWhenIdle);
     }, delay);
   },
 
   /**
    * Sets the last save time to the current time. This will cause us to wait for
    * at least the configured interval when runDelayed() is called next.
    */
   updateLastSaveTime() {
@@ -202,17 +204,17 @@ var SessionSaverInternal = {
   },
 
   /**
    * Cancels all pending session saves.
    */
   cancel() {
     clearTimeout(this._timeoutID);
     this._timeoutID = null;
-    cancelIdleCallback(this._idleCallbackID);
+    Services.appShell.hiddenDOMWindow.cancelIdleCallback(this._idleCallbackID);
     this._idleCallbackID = null;
   },
 
   /**
    * Observe idle/ active notifications.
    */
   observe(subject, topic, data) {
     switch (topic) {
--- a/browser/components/tests/startupRecorder.js
+++ b/browser/components/tests/startupRecorder.js
@@ -44,17 +44,16 @@ let afterPaintListener = () => {
 function startupRecorder() {
   this.wrappedJSObject = this;
   this.data = {
     images: {
       "image-drawing": new Set(),
       "image-loading": new Set(),
     },
     code: {},
-    extras: {},
     prefStats: {},
   };
   this.done = new Promise(resolve => { this._resolve = resolve; });
 }
 startupRecorder.prototype = {
   classID: Components.ID("{11c095b2-e42e-4bdf-9dd0-aed87595f6a4}"),
 
   QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
@@ -69,19 +68,16 @@ startupRecorder.prototype = {
       services: Object.keys(Cc).filter(c => {
         try {
           return Cm.isServiceInstantiatedByContractID(c, Ci.nsISupports);
         } catch (e) {
           return false;
         }
       }),
     };
-    this.data.extras[name] = {
-      hiddenWindowLoaded: Services.appShell.hasHiddenWindow,
-    };
   },
 
   observe(subject, topic, data) {
     if (topic == "app-startup") {
       // We can't ensure our observer will be called first or last, so the list of
       // topics we observe here should avoid the topics used to trigger things
       // during startup (eg. the topics observed by BrowserGlue.jsm).
       let topics = [
--- a/browser/modules/Sanitizer.jsm
+++ b/browser/modules/Sanitizer.jsm
@@ -465,54 +465,51 @@ var Sanitizer = {
         }
       },
       async clear(range, privateStateForNewWindow = "non-private") {
         // NB: this closes all *browser* windows, not other windows like the library, about window,
         // browser console, etc.
 
         // Keep track of the time in case we get stuck in la-la-land because of onbeforeunload
         // dialogs
-        let startDate = Date.now();
+        let existingWindow = Services.appShell.hiddenDOMWindow;
+        let startDate = existingWindow.performance.now();
 
         // First check if all these windows are OK with being closed:
         let windowList = [];
         for (let someWin of Services.wm.getEnumerator("navigator:browser")) {
           windowList.push(someWin);
           // If someone says "no" to a beforeunload prompt, we abort here:
           if (!this._canCloseWindow(someWin)) {
             this._resetAllWindowClosures(windowList);
             throw new Error("Sanitize could not close windows: cancelled by user");
           }
 
           // ...however, beforeunload prompts spin the event loop, and so the code here won't get
           // hit until the prompt has been dismissed. If more than 1 minute has elapsed since we
           // started prompting, stop, because the user might not even remember initiating the
           // 'forget', and the timespans will be all wrong by now anyway:
-          if (Date.now() > (startDate + 60 * 1000)) {
+          if (existingWindow.performance.now() > (startDate + 60 * 1000)) {
             this._resetAllWindowClosures(windowList);
             throw new Error("Sanitize could not close windows: timeout");
           }
         }
 
-        if (windowList.length == 0) {
-          return;
-        }
-
         // If/once we get here, we should actually be able to close all windows.
 
         let refObj = {};
         TelemetryStopwatch.start("FX_SANITIZE_OPENWINDOWS", refObj);
 
         // First create a new window. We do this first so that on non-mac, we don't
         // accidentally close the app by closing all the windows.
         let handler = Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler);
         let defaultArgs = handler.defaultArgs;
         let features = "chrome,all,dialog=no," + privateStateForNewWindow;
-        let newWindow = windowList[0].openDialog(AppConstants.BROWSER_CHROME_URL, "_blank",
-                                                 features, defaultArgs);
+        let newWindow = existingWindow.openDialog(AppConstants.BROWSER_CHROME_URL, "_blank",
+                                                  features, defaultArgs);
 
         let onFullScreen = null;
         if (AppConstants.platform == "macosx") {
           onFullScreen = function(e) {
             newWindow.removeEventListener("fullscreen", onFullScreen);
             let docEl = newWindow.document.documentElement;
             let sizemode = docEl.getAttribute("sizemode");
             if (!newWindow.fullScreen && sizemode == "fullscreen") {
--- a/devtools/server/actors/root.js
+++ b/devtools/server/actors/root.js
@@ -1,17 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { Cc, Ci, Cu } = require("chrome");
+const { Cu } = require("chrome");
 const Services = require("Services");
 const { Pool } = require("devtools/shared/protocol");
 const { LazyPool, createExtraActors } = require("devtools/shared/protocol/lazy-pool");
 const { DebuggerServer } = require("devtools/server/main");
 
 loader.lazyRequireGetter(this, "ChromeWindowTargetActor",
   "devtools/server/actors/targets/chrome-window", true);
 loader.lazyRequireGetter(this, "ContentProcessTargetActor",
@@ -537,23 +537,26 @@ RootActor.prototype = {
     }
     if (("id" in request) && typeof (request.id) != "number") {
       return { error: "wrongParameter",
                message: "getProcess requires a valid `id` attribute." };
     }
     // If the request doesn't contains id parameter or id is 0
     // (id == 0, based on onListProcesses implementation)
     if ((!("id" in request)) || request.id === 0) {
-      // Check if we are running on xpcshell.
+      // Check if we are running on xpcshell. hiddenDOMWindow is going to throw on it.
       // When running on xpcshell, there is no valid browsing context to attach to
       // and so ParentProcessTargetActor doesn't make sense as it inherits from
       // BrowsingContextTargetActor. So instead use ContentProcessTargetActor, which
       // matches xpcshell needs.
-      const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
-      const isXpcshell = env.exists("XPCSHELL_TEST_PROFILE_DIR");
+      let isXpcshell = true;
+      try {
+        isXpcshell = !Services.wm.getMostRecentWindow(null) &&
+                     !Services.appShell.hiddenDOMWindow;
+      } catch (e) {}
 
       if (!isXpcshell && this._parentProcessTargetActor &&
           (!this._parentProcessTargetActor.docShell ||
             this._parentProcessTargetActor.docShell.isBeingDestroyed)) {
         this._parentProcessTargetActor.destroy();
         this._parentProcessTargetActor = null;
       }
       if (!this._parentProcessTargetActor) {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -434,33 +434,22 @@ nsDocShell::~nsDocShell() {
 
   if (--gDocShellCount == 0) {
     NS_IF_RELEASE(sURIFixup);
   }
 
   MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
 
 #ifdef DEBUG
-  nsAutoCString url;
-  if (mLastOpenedURI) {
-    url = mLastOpenedURI->GetSpecOrDefault();
-
-    // Data URLs can be very long, so truncate to avoid flooding the log.
-    const uint32_t maxURLLength = 1000;
-    if (url.Length() > maxURLLength) {
-      url.Truncate(maxURLLength);
-    }
-  }
   // We're counting the number of |nsDocShells| to help find leaks
   --gNumberOfDocShells;
   if (!PR_GetEnv("MOZ_QUIET")) {
-    printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %s] [url = %s]\n", (void*)this,
+    printf_stderr("--DOCSHELL %p == %ld [pid = %d] [id = %s]\n", (void*)this,
                   gNumberOfDocShells, getpid(),
-                  nsIDToCString(mHistoryID).get(),
-                  url.get());
+                  nsIDToCString(mHistoryID).get());
   }
 #endif
 }
 
 /* static */
 already_AddRefed<nsDocShell> nsDocShell::Create(
     BrowsingContext* aBrowsingContext) {
   MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
@@ -1176,20 +1165,16 @@ bool nsDocShell::SetCurrentURI(nsIURI* a
   bool uriIsEqual = false;
   if (!mCurrentURI || !aURI ||
       NS_FAILED(mCurrentURI->Equals(aURI, &uriIsEqual)) || !uriIsEqual) {
     mTitleValidForCurrentURI = false;
   }
 
   mCurrentURI = aURI;
 
-#ifdef DEBUG
-  mLastOpenedURI = aURI;
-#endif
-
   if (!NS_IsAboutBlank(mCurrentURI)) {
     mHasLoadedNonBlankURI = true;
   }
 
   bool isRoot = false;      // Is this the root docshell
   bool isSubFrame = false;  // Is this a subframe navigation?
 
   nsCOMPtr<nsIDocShellTreeItem> root;
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -971,20 +971,16 @@ class nsDocShell final : public nsDocLoa
 
   // An observed docshell wrapper is created when recording markers is enabled.
   mozilla::UniquePtr<mozilla::ObservedDocShell> mObserved;
 
   // mCurrentURI should be marked immutable on set if possible.
   nsCOMPtr<nsIURI> mCurrentURI;
   nsCOMPtr<nsIReferrerInfo> mReferrerInfo;
 
-#ifdef DEBUG
-  nsCOMPtr<nsIURI> mLastOpenedURI;
-#endif
-
   // Reference to the SHEntry for this docshell until the page is destroyed.
   // Somebody give me better name
   nsCOMPtr<nsISHEntry> mOSHE;
 
   // Reference to the SHEntry for this docshell until the page is loaded
   // Somebody give me better name.
   // If mLSHE is non-null, non-pushState subframe loads don't create separate
   // root history entries. That is, frames loaded during the parent page
--- a/dom/base/nsCCUncollectableMarker.cpp
+++ b/dom/base/nsCCUncollectableMarker.cpp
@@ -344,29 +344,26 @@ nsresult nsCCUncollectableMarker::Observ
     NS_ENSURE_SUCCESS(rv, rv);
 
     MarkWindowList(windowList, cleanupJS);
   }
 
   nsCOMPtr<nsIAppShellService> appShell =
       do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
   if (appShell) {
-    bool hasHiddenWindow = false;
-    appShell->GetHasHiddenWindow(&hasHiddenWindow);
-    if (hasHiddenWindow) {
-      nsCOMPtr<nsIXULWindow> hw;
-      appShell->GetHiddenWindow(getter_AddRefs(hw));
+    nsCOMPtr<nsIXULWindow> hw;
+    appShell->GetHiddenWindow(getter_AddRefs(hw));
+    if (hw) {
       nsCOMPtr<nsIDocShell> shell;
       hw->GetDocShell(getter_AddRefs(shell));
       MarkDocShell(shell, cleanupJS);
     }
     bool hasHiddenPrivateWindow = false;
     appShell->GetHasHiddenPrivateWindow(&hasHiddenPrivateWindow);
     if (hasHiddenPrivateWindow) {
-      nsCOMPtr<nsIXULWindow> hw;
       appShell->GetHiddenPrivateWindow(getter_AddRefs(hw));
       if (hw) {
         nsCOMPtr<nsIDocShell> shell;
         hw->GetDocShell(getter_AddRefs(shell));
         MarkDocShell(shell, cleanupJS);
       }
     }
   }
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1901,21 +1901,22 @@ void nsJSContext::RunNextCollectorTimer(
     runnable = sInterSliceGCRunner;
   } else {
     // Check the CC timers after the GC timers, because the CC timers won't do
     // anything if a GC is in progress.
     MOZ_ASSERT(!sCCLockedOut,
                "Don't check the CC timers if the CC is locked out.");
 
     if (sCCRunner) {
+      MOZ_ASSERT(!sICCRunner,
+                 "Shouldn't have both sCCRunner and sICCRunner active at the "
+                 "same time");
       sCCRunner->SetDeadline(aDeadline);
       runnable = sCCRunner;
-    }
-
-    if (sICCRunner) {
+    } else if (sICCRunner) {
       sICCRunner->SetDeadline(aDeadline);
       runnable = sICCRunner;
     }
   }
 
   if (runnable) {
     runnable->Run();
   }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3765,25 +3765,25 @@ JS_PUBLIC_API void JS::SetScriptPrivateR
   rt->scriptPrivateAddRefHook = addRefHook;
   rt->scriptPrivateReleaseHook = releaseHook;
 }
 
 JS_PUBLIC_API bool JS::ModuleInstantiate(JSContext* cx,
                                          JS::HandleObject moduleArg) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
-  cx->check(moduleArg);
+  cx->releaseCheck(moduleArg);
   return ModuleObject::Instantiate(cx, moduleArg.as<ModuleObject>());
 }
 
 JS_PUBLIC_API bool JS::ModuleEvaluate(JSContext* cx,
                                       JS::HandleObject moduleArg) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
-  cx->check(moduleArg);
+  cx->releaseCheck(moduleArg);
   return ModuleObject::Evaluate(cx, moduleArg.as<ModuleObject>());
 }
 
 JS_PUBLIC_API JSObject* JS::GetRequestedModules(JSContext* cx,
                                                 JS::HandleObject moduleArg) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   cx->check(moduleArg);
@@ -5157,17 +5157,17 @@ JS_PUBLIC_API bool JS_GetPendingExceptio
     return false;
   }
   return cx->getPendingException(vp);
 }
 
 JS_PUBLIC_API void JS_SetPendingException(JSContext* cx, HandleValue value) {
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
-  cx->check(value);
+  cx->releaseCheck(value);
   cx->setPendingException(value);
 }
 
 JS_PUBLIC_API void JS_ClearPendingException(JSContext* cx) {
   AssertHeapIsIdle();
   cx->clearPendingException();
 }
 
--- a/testing/mochitest/leaks.py
+++ b/testing/mochitest/leaks.py
@@ -16,19 +16,17 @@ class ShutdownLeaks(object):
     DOM windows (that are still around after test suite shutdown, despite running
     the GC) to the tests that created them and prints leak statistics.
     """
 
     def __init__(self, logger):
         self.logger = logger
         self.tests = []
         self.leakedWindows = {}
-        self.hiddenWindowsCount = 0
         self.leakedDocShells = set()
-        self.hiddenDocShellsCount = 0
         self.currentTest = None
         self.seenShutdown = set()
 
     def log(self, message):
         action = message['action']
 
         # Remove 'log' when clipboard is gone and/or structured.
         if action in ('log', 'process_output'):
@@ -76,29 +74,16 @@ class ShutdownLeaks(object):
                                   "shutdown" %
                                   (test["fileName"], len(test["leakedDocShells"])))
                 failures += 1
                 self.logger.info("TEST-INFO | %s | docShell(s) leaked: %s" %
                                  (test["fileName"], ', '.join(["[pid = %s] [id = %s]" %
                                                                x for x in test["leakedDocShells"]]
                                                               )))
 
-            if test["hiddenWindowsCount"] > 0:
-                # Note: to figure out how many hidden windows were created, we divide
-                # this number by 2, because 1 hidden window creation implies in
-                # 1 outer window + 1 inner window.
-                self.logger.info(
-                    "TEST-INFO | %s | This test created %d hidden window(s)"
-                    % (test["fileName"], test["hiddenWindowsCount"] / 2))
-
-            if test["hiddenDocShellsCount"] > 0:
-                self.logger.info(
-                    "TEST-INFO | %s | This test created %d hidden docshell(s)"
-                    % (test["fileName"], test["hiddenDocShellsCount"]))
-
         return failures
 
     def _logWindow(self, line):
         created = line[:2] == "++"
         pid = self._parseValue(line, "pid")
         serial = self._parseValue(line, "serial")
 
         # log line has invalid format
@@ -111,21 +96,17 @@ class ShutdownLeaks(object):
 
         if self.currentTest:
             windows = self.currentTest["windows"]
             if created:
                 windows.add(key)
             else:
                 windows.discard(key)
         elif int(pid) in self.seenShutdown and not created:
-            url = self._parseValue(line, "url")
-            if not self._isHiddenWindowURL(url):
-                self.leakedWindows[key] = url
-            else:
-                self.hiddenWindowsCount += 1
+            self.leakedWindows[key] = self._parseValue(line, "url")
 
     def _logDocShell(self, line):
         created = line[:2] == "++"
         pid = self._parseValue(line, "pid")
         id = self._parseValue(line, "id")
 
         # log line has invalid format
         if not pid or not id:
@@ -137,65 +118,55 @@ class ShutdownLeaks(object):
 
         if self.currentTest:
             docShells = self.currentTest["docShells"]
             if created:
                 docShells.add(key)
             else:
                 docShells.discard(key)
         elif int(pid) in self.seenShutdown and not created:
-            url = self._parseValue(line, "url")
-            if not self._isHiddenWindowURL(url):
-                self.leakedDocShells.add(key)
-            else:
-                self.hiddenDocShellsCount += 1
+            self.leakedDocShells.add(key)
 
     def _parseValue(self, line, name):
         match = re.search("\[%s = (.+?)\]" % name, line)
         if match:
             return match.group(1)
         return None
 
     def _parseLeakingTests(self):
         leakingTests = []
 
         for test in self.tests:
             leakedWindows = [
                 id for id in test["windows"] if id in self.leakedWindows]
             test["leakedWindows"] = [self.leakedWindows[id]
                                      for id in leakedWindows]
-            test["hiddenWindowsCount"] = self.hiddenWindowsCount
             test["leakedWindowsString"] = ', '.join(
                 ["[pid = %s] [serial = %s]" % x for x in leakedWindows])
             test["leakedDocShells"] = [
                 id for id in test["docShells"] if id in self.leakedDocShells]
-            test["hiddenDocShellsCount"] = self.hiddenDocShellsCount
             test["leakCount"] = len(
                 test["leakedWindows"]) + len(test["leakedDocShells"])
 
-            if test["leakCount"] or test["hiddenWindowsCount"] or test["hiddenDocShellsCount"]:
+            if test["leakCount"]:
                 leakingTests.append(test)
 
         return sorted(leakingTests, key=itemgetter("leakCount"), reverse=True)
 
     def _zipLeakedWindows(self, leakedWindows):
         counts = []
         counted = set()
 
         for url in leakedWindows:
             if url not in counted:
                 counts.append((url, leakedWindows.count(url)))
                 counted.add(url)
 
         return sorted(counts, key=itemgetter(1), reverse=True)
 
-    def _isHiddenWindowURL(self, url):
-        return (url == "resource://gre-resources/hiddenWindow.html" or  # Win / Linux
-                url == "chrome://browser/content/hiddenWindow.xul")     # Mac
-
 
 class LSANLeaks(object):
 
     """
     Parses the log when running an LSAN build, looking for interesting stack frames
     in allocation stacks, and prints out reports.
     """
 
--- a/toolkit/modules/Timer.jsm
+++ b/toolkit/modules/Timer.jsm
@@ -4,23 +4,22 @@
 
 "use strict";
 
 /**
  * JS module implementation of setTimeout and clearTimeout.
  */
 
 var EXPORTED_SYMBOLS = ["setTimeout", "setTimeoutWithTarget", "clearTimeout",
-                        "setInterval", "setIntervalWithTarget", "clearInterval",
-                        "requestIdleCallback", "cancelIdleCallback"];
+                        "setInterval", "setIntervalWithTarget", "clearInterval"];
 
 // This gives us >=2^30 unique timer IDs, enough for 1 per ms for 12.4 days.
 var gNextId = 1; // setTimeout and setInterval must return a positive integer
 
-var gTimerTable = new Map(); // int -> nsITimer or idleCallback
+var gTimerTable = new Map(); // int -> nsITimer
 
 // Don't generate this for every timer.
 var setTimeout_timerCallbackQI = ChromeUtils.generateQI([Ci.nsITimerCallback, Ci.nsINamed]);
 
 function _setTimeoutOrIsInterval(aCallback, aMilliseconds, aIsInterval,
                                  aTarget, aArgs) {
   if (typeof aCallback !== "function") {
     throw new Error(`callback is not a function in ${aIsInterval ? "setInterval" : "setTimeout"}`);
@@ -82,32 +81,8 @@ function setIntervalWithTarget(aCallback
 }
 
 var clearInterval = this.clearTimeout = function clearTimeout(aId) {
   if (gTimerTable.has(aId)) {
     gTimerTable.get(aId).cancel();
     gTimerTable.delete(aId);
   }
 };
-
-function requestIdleCallback(aCallback, aOptions) {
-  if (typeof aCallback !== "function") {
-    throw new Error("callback is not a function in requestIdleCallback");
-  }
-  let id = gNextId++;
-
-  let callback = (...aArgs) => {
-    if (gTimerTable.has(id)) {
-      gTimerTable.delete(id);
-      aCallback(...aArgs);
-    }
-  };
-
-  ChromeUtils.idleDispatch(callback, aOptions);
-  gTimerTable.set(id, callback);
-  return id;
-}
-
-function cancelIdleCallback(aId) {
-  if (gTimerTable.has(aId)) {
-    gTimerTable.delete(aId);
-  }
-}
--- a/toolkit/modules/tests/xpcshell/test_timer.js
+++ b/toolkit/modules/tests/xpcshell/test_timer.js
@@ -58,22 +58,21 @@ add_task(async function test_setInterval
   Assert.ok(interval1 > 0, "setTimeout returns a positive number");
 
   imported.clearInterval(interval1);
 
   const EXPECTED_CALLS = 5;
   let calls = 0;
 
   await new Promise((resolve) => {
-    let interval2 = imported.setInterval((param1, param2) => {
+    imported.setInterval((param1, param2) => {
       Assert.ok(true, "Should be called");
       Assert.equal(param1, 15, "first parameter is correct");
       Assert.equal(param2, "hola", "second parameter is correct");
       if (calls >= EXPECTED_CALLS) {
-        imported.clearInterval(interval2);
         resolve();
       }
       calls++;
     }, 100, 15, "hola");
   });
 });
 
 add_task(async function test_setIntervalWithTarget() {
@@ -84,22 +83,21 @@ add_task(async function test_setInterval
   Assert.ok(interval1 > 0, "setTimeout returns a positive number");
 
   imported.clearInterval(interval1);
 
   const EXPECTED_CALLS = 5;
   let calls = 0;
 
   await new Promise((resolve) => {
-    let interval2 = imported.setIntervalWithTarget((param1, param2) => {
+    imported.setIntervalWithTarget((param1, param2) => {
       Assert.ok(true, "Should be called");
       Assert.equal(param1, 15, "first parameter is correct");
       Assert.equal(param2, "hola", "second parameter is correct");
       if (calls >= EXPECTED_CALLS) {
-        imported.clearInterval(interval2);
         resolve();
       }
       calls++;
     }, 100, target, 15, "hola");
   });
 });
 
 add_task(async function test_setTimeoutNonFunction() {
@@ -116,29 +114,8 @@ add_task(async function test_setTimeoutW
   Assert.throws(() => { imported.setTimeoutWithTarget({}, 0); },
                 /callback is not a function in setTimeout/);
 });
 
 add_task(async function test_setIntervalWithTargetNonFunction() {
   Assert.throws(() => { imported.setIntervalWithTarget({}, 0); },
                 /callback is not a function in setInterval/);
 });
-
-add_task(async function test_requestIdleCallback() {
-  let request1 = imported.requestIdleCallback(() => do_throw("Should not be called"));
-  Assert.equal(typeof request1, "number", "requestIdleCallback returns a number");
-  Assert.ok(request1 > 0, "setTimeout returns a positive number");
-
-  imported.cancelIdleCallback(request1);
-
-  await new Promise((resolve) => {
-    let request2 = imported.requestIdleCallback((deadline) => {
-      Assert.ok(true, "Should be called");
-      Assert.equal(typeof deadline.didTimeout, "boolean", "deadline parameter has .didTimeout property");
-      Assert.equal(typeof deadline.timeRemaining(), "number", "deadline parameter has .timeRemaining() function");
-      resolve();
-    }, {timeout: 100});
-
-    Assert.equal(typeof request2, "number", "requestIdleCallback returns a number");
-    Assert.ok(request2 > 0, "requestIdleCallback returns a positive number");
-    Assert.notEqual(request1, request2, "Calling requestIdleCallback again returns a different value");
-  });
-});
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -4605,26 +4605,18 @@ nsresult XREMain::XRE_mainRun() {
   SaveToEnv("XRE_PROFILE_PATH=");
   SaveToEnv("XRE_PROFILE_LOCAL_PATH=");
   SaveToEnv("XRE_START_OFFLINE=");
   SaveToEnv("XUL_APP_FILE=");
   SaveToEnv("XRE_BINARY_PATH=");
   SaveToEnv("XRE_RESTARTED_BY_PROFILE_MANAGER=");
 
   if (!mShuttingDown) {
-#ifdef XP_MACOSX
-    bool lazyHiddenWindow = false;
-#else
-    bool lazyHiddenWindow = Preferences::GetBool("toolkit.lazyHiddenWindow", false);
-#endif
-
-    if (!lazyHiddenWindow) {
-      rv = appStartup->CreateHiddenWindow();
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
-    }
+    rv = appStartup->CreateHiddenWindow();
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
 #ifdef XP_WIN
     Preferences::RegisterCallbackAndCall(RegisterApplicationRestartChanged,
                                          PREF_WIN_REGISTER_APPLICATION_RESTART);
 #  if defined(MOZ_LAUNCHER_PROCESS)
     SetupLauncherProcessPref();
 #  endif  // defined(MOZ_LAUNCHER_PROCESS)
 #endif
--- a/tools/lint/eslint/modules.json
+++ b/tools/lint/eslint/modules.json
@@ -220,17 +220,17 @@
   "SyncedBookmarksMirror.jsm": ["SyncedBookmarksMirror"],
   "tabs.js": ["TabEngine", "TabSetRecord"],
   "tabs.jsm": ["BrowserTabs"],
   "tcpsocket_test.jsm": ["createSocket", "createServer", "enablePrefsAndPermissions", "socketCompartmentInstanceOfArrayBuffer"],
   "telemetry.js": ["SyncTelemetry"],
   "test.jsm": ["Foo"],
   "test2.jsm": ["Bar"],
   "test_bug883784.jsm": ["Test"],
-  "Timer.jsm": ["setTimeout", "setTimeoutWithTarget", "clearTimeout", "setInterval", "setIntervalWithTarget", "clearInterval", "requestIdleCallback", "cancelIdleCallback"],
+  "Timer.jsm": ["setTimeout", "setTimeoutWithTarget", "clearTimeout", "setInterval", "setIntervalWithTarget", "clearInterval"],
   "TippyTopProvider.jsm": ["TippyTopProvider", "getDomain"],
   "Tokenize.jsm": ["tokenize", "toksToTfIdfVector"],
   "tokenserverclient.js": ["TokenServerClient", "TokenServerClientError", "TokenServerClientNetworkError", "TokenServerClientServerError"],
   "ToolboxProcess.jsm": ["BrowserToolboxProcess"],
   "tps.jsm": ["ACTIONS", "Addons", "Addresses", "Bookmarks", "CreditCards", "Formdata", "History", "Passwords", "Prefs", "Tabs", "TPS", "Windows"],
   "Translation.jsm": ["Translation", "TranslationTelemetry"],
   "Traversal.jsm": ["TraversalRules", "TraversalHelper"],
   "UpdateTelemetry.jsm": ["AUSTLMY"],
--- a/widget/windows/WindowsUIUtils.cpp
+++ b/widget/windows/WindowsUIUtils.cpp
@@ -13,22 +13,19 @@
 
 #include "nsIObserverService.h"
 #include "nsIBaseWindow.h"
 #include "nsIDocShell.h"
 #include "nsIAppShellService.h"
 #include "nsAppShellCID.h"
 #include "nsIXULWindow.h"
 #include "mozilla/Services.h"
-#include "mozilla/WidgetUtils.h"
 #include "mozilla/WindowsVersion.h"
 #include "nsString.h"
 #include "nsIWidget.h"
-#include "nsIWindowMediator.h"
-#include "nsPIDOMWindow.h"
 
 /* mingw currently doesn't support windows.ui.viewmanagement.h, so we disable it
  * until it's fixed. */
 #ifndef __MINGW32__
 
 #  include <windows.ui.viewmanagement.h>
 
 #  pragma comment(lib, "runtimeobject.lib")
@@ -134,41 +131,37 @@ WindowsUIUtils::GetInTabletMode(bool* aR
 
 NS_IMETHODIMP
 WindowsUIUtils::UpdateTabletModeState() {
 #ifndef __MINGW32__
   if (!IsWin10OrLater()) {
     return NS_OK;
   }
 
-  nsresult rv;
-  nsCOMPtr<nsIWindowMediator> winMediator(
-      do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv));
+  nsCOMPtr<nsIAppShellService> appShell(
+      do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
+  nsCOMPtr<nsIXULWindow> hiddenWindow;
+
+  nsresult rv = appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  nsCOMPtr<nsIWidget> widget;
-  nsCOMPtr<mozIDOMWindowProxy> navWin;
-
-  rv = winMediator->GetMostRecentWindow(u"navigator:browser",
-                                        getter_AddRefs(navWin));
-  if (NS_FAILED(rv) || !navWin) {
-    // Fall back to the hidden window
-    nsCOMPtr<nsIAppShellService> appShell(
-      do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
-
-    rv = appShell->GetHiddenDOMWindow(getter_AddRefs(navWin));
-    if (NS_FAILED(rv) || !navWin) {
-      return rv;
-    }
+  nsCOMPtr<nsIDocShell> docShell;
+  rv = hiddenWindow->GetDocShell(getter_AddRefs(docShell));
+  if (NS_FAILED(rv) || !docShell) {
+    return rv;
   }
 
-  nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(navWin);
-  widget = widget::WidgetUtils::DOMWindowToWidget(win);
+  nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(docShell));
+
+  if (!baseWindow) return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIWidget> widget;
+  baseWindow->GetMainWidget(getter_AddRefs(widget));
 
   if (!widget) return NS_ERROR_FAILURE;
 
   HWND winPtr = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW);
   ComPtr<IUIViewSettingsInterop> uiViewSettingsInterop;
 
   HRESULT hr = GetActivationFactory(
       HStringReference(RuntimeClass_Windows_UI_ViewManagement_UIViewSettings)
--- a/xpfe/appshell/nsAppShellService.cpp
+++ b/xpfe/appshell/nsAppShellService.cpp
@@ -86,25 +86,19 @@ nsAppShellService::CreateHiddenWindow() 
 }
 
 NS_IMETHODIMP
 nsAppShellService::SetScreenId(uint32_t aScreenId) {
   mScreenId = aScreenId;
   return NS_OK;
 }
 
-void nsAppShellService::EnsureHiddenWindow() {
-  if (!mHiddenWindow) {
-    CreateHiddenWindowHelper(/* aIsPrivate = */ false);
-  }
-}
-
 void nsAppShellService::EnsurePrivateHiddenWindow() {
   if (!mHiddenPrivateWindow) {
-    CreateHiddenWindowHelper(/* aIsPrivate = */ true);
+    CreateHiddenWindowHelper(true);
   }
 }
 
 nsresult nsAppShellService::CreateHiddenWindowHelper(bool aIsPrivate) {
   nsresult rv;
   int32_t initialHeight = 100, initialWidth = 100;
 
 #ifdef XP_MACOSX
@@ -720,29 +714,23 @@ nsresult nsAppShellService::JustCreateTo
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsAppShellService::GetHiddenWindow(nsIXULWindow** aWindow) {
   NS_ENSURE_ARG_POINTER(aWindow);
 
-  EnsureHiddenWindow();
-
   *aWindow = mHiddenWindow;
   NS_IF_ADDREF(*aWindow);
   return *aWindow ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsAppShellService::GetHiddenDOMWindow(mozIDOMWindowProxy** aWindow) {
-  NS_ENSURE_ARG_POINTER(aWindow);
-
-  EnsureHiddenWindow();
-
   nsresult rv;
   nsCOMPtr<nsIDocShell> docShell;
   NS_ENSURE_TRUE(mHiddenWindow, NS_ERROR_FAILURE);
 
   rv = mHiddenWindow->GetDocShell(getter_AddRefs(docShell));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
@@ -759,42 +747,32 @@ nsAppShellService::GetHiddenPrivateWindo
 
   *aWindow = mHiddenPrivateWindow;
   NS_IF_ADDREF(*aWindow);
   return *aWindow ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsAppShellService::GetHiddenPrivateDOMWindow(mozIDOMWindowProxy** aWindow) {
-  NS_ENSURE_ARG_POINTER(aWindow);
-
   EnsurePrivateHiddenWindow();
 
   nsresult rv;
   nsCOMPtr<nsIDocShell> docShell;
   NS_ENSURE_TRUE(mHiddenPrivateWindow, NS_ERROR_FAILURE);
 
   rv = mHiddenPrivateWindow->GetDocShell(getter_AddRefs(docShell));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsPIDOMWindowOuter> hiddenPrivateDOMWindow(docShell->GetWindow());
   hiddenPrivateDOMWindow.forget(aWindow);
   return *aWindow ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
-nsAppShellService::GetHasHiddenWindow(bool* aHasHiddenWindow) {
-  NS_ENSURE_ARG_POINTER(aHasHiddenWindow);
-
-  *aHasHiddenWindow = !!mHiddenWindow;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsAppShellService::GetHasHiddenPrivateWindow(bool* aHasPrivateWindow) {
   NS_ENSURE_ARG_POINTER(aHasPrivateWindow);
 
   *aHasPrivateWindow = !!mHiddenPrivateWindow;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/xpfe/appshell/nsAppShellService.h
+++ b/xpfe/appshell/nsAppShellService.h
@@ -31,17 +31,16 @@ class nsAppShellService final : public n
   NS_DECL_NSIOBSERVER
 
   nsAppShellService();
 
  protected:
   ~nsAppShellService();
 
   nsresult CreateHiddenWindowHelper(bool aIsPrivate);
-  void EnsureHiddenWindow();
   void EnsurePrivateHiddenWindow();
 
   nsresult JustCreateTopWindow(nsIXULWindow *aParent, nsIURI *aUrl,
                                uint32_t aChromeMask, int32_t aInitialWidth,
                                int32_t aInitialHeight, bool aIsHiddenWindow,
                                nsITabParent *aOpeningTab,
                                mozIDOMWindowProxy *aOpenerWindow,
                                nsWebShellWindow **aResult);
--- a/xpfe/appshell/nsIAppShellService.idl
+++ b/xpfe/appshell/nsIAppShellService.idl
@@ -128,21 +128,16 @@ interface nsIAppShellService : nsISuppor
    * Remove a window from the application's window registry. Note that
    * this method won't automatically attempt to quit the app when
    * the last window is unregistered. For that, see Quit().
    * @param aWindow you see the pattern
    */
   void unregisterTopLevelWindow(in nsIXULWindow aWindow);
 
   /**
-   * Whether the hidden window has been lazily created.
-   */
-  readonly attribute boolean hasHiddenWindow;
-
-  /**
    * Whether the hidden private window has been lazily created.
    */
   [noscript]
   readonly attribute boolean hasHiddenPrivateWindow;
 
   /**
    * Start/stop tracking lags in the event loop.
    * If the event loop gets unresponsive, a "event-loop-lag" notification
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -457,38 +457,29 @@ NS_IMETHODIMP nsXULWindow::Destroy() {
 
 #if defined(XP_WIN)
   // We need to explicitly set the focus on Windows, but
   // only if the parent is visible.
   nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
   if (parent) {
     nsCOMPtr<nsIWidget> parentWidget;
     parent->GetMainWidget(getter_AddRefs(parentWidget));
-
-    if (parentWidget && parentWidget->IsVisible()) {
-      bool isParentHiddenWindow = false;
-
+    if (!parentWidget || parentWidget->IsVisible()) {
+      nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
       if (appShell) {
-        bool hasHiddenWindow = false;
-        appShell->GetHasHiddenWindow(&hasHiddenWindow);
-        if (hasHiddenWindow) {
-          nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
-          nsCOMPtr<nsIXULWindow> hiddenWindow;
-          appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
-          if (hiddenWindow) {
-            baseHiddenWindow = do_GetInterface(hiddenWindow);
-            isParentHiddenWindow = (baseHiddenWindow == parent);
-          }
-        }
+        nsCOMPtr<nsIXULWindow> hiddenWindow;
+        appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
+        if (hiddenWindow) baseHiddenWindow = do_GetInterface(hiddenWindow);
       }
-
       // somebody screwed up somewhere. hiddenwindow shouldn't be anybody's
       // parent. still, when it happens, skip activating it.
-      if (!isParentHiddenWindow) {
-        parentWidget->PlaceBehind(eZPlacementTop, 0, true);
+      if (baseHiddenWindow != parent) {
+        nsCOMPtr<nsIWidget> parentWidget;
+        parent->GetMainWidget(getter_AddRefs(parentWidget));
+        if (parentWidget) parentWidget->PlaceBehind(eZPlacementTop, 0, true);
       }
     }
   }
 #endif
 
   RemoveTooltipSupport();
 
   mDOMWindow = nullptr;