Backed out 16 changesets (bug 827976) for causing bug 1532054. a=backout
authorCosmin Sabou <csabou@mozilla.com>
Sat, 02 Mar 2019 20:40:01 +0200
changeset 462132 bf3cbcc825276823afba2778333cf62cfa75039c
parent 462131 82134a2f7b8970a282a8bd3c80fab9fd73bc6b26
child 462155 42f9208ed5f3f4c98374661d05c5c74b8df4ff70
push id35636
push usercsabou@mozilla.com
push dateSat, 02 Mar 2019 18:40:48 +0000
treeherdermozilla-central@bf3cbcc82527 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout
bugs827976, 1532054
milestone67.0a1
backs out54c0b12443edcbc5df3328ee7772345325bac70e
92b45080d080e1cfb967afcd6164e859c0dd58be
443087a359f9baec6e41e9693adc32ad420c561c
f976c2d4cebb29588222e0c5122c475aae3be509
66cffb171024564fa70e9f8d237c76181cc51514
845e1d0b24022796c587f7121825046cbcbc02f9
fd94066a1d76d155a0edc9a2b9c67bac381fdf58
e253b264e7bdfdf28bfb2f15ab0f22cded77d91c
07ef335770a8c0e985b9491c21882c658d458260
9d3805d77b99ae1e3a6b9b60a8b34af857724974
eed600ceb6064d5f47b7bf74024fd4849c7b9e15
3b64368cff52c1393816a40e7ccb9cc545f08e7a
69d0378e0c098473f288d02557b7871f0eb685ff
9203871a5c6f9bbb69c425ae2ec0b1cc805a061d
96a507c818e278fc85c23b60f83a93d9ca517100
e254496ff95b683df29479a41341f3f2fa6a71f4
first release with
nightly linux32
bf3cbcc82527 / 67.0a1 / 20190302184048 / files
nightly linux64
bf3cbcc82527 / 67.0a1 / 20190302184048 / files
nightly mac
bf3cbcc82527 / 67.0a1 / 20190302184048 / files
nightly win32
bf3cbcc82527 / 67.0a1 / 20190302184048 / files
nightly win64
bf3cbcc82527 / 67.0a1 / 20190302184048 / 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
Backed out 16 changesets (bug 827976) for causing bug 1532054. a=backout Backed out changeset 54c0b12443ed (bug 827976) Backed out changeset 92b45080d080 (bug 827976) Backed out changeset 443087a359f9 (bug 827976) Backed out changeset f976c2d4cebb (bug 827976) Backed out changeset 66cffb171024 (bug 827976) Backed out changeset 845e1d0b2402 (bug 827976) Backed out changeset fd94066a1d76 (bug 827976) Backed out changeset e253b264e7bd (bug 827976) Backed out changeset 07ef335770a8 (bug 827976) Backed out changeset 9d3805d77b99 (bug 827976) Backed out changeset eed600ceb606 (bug 827976) Backed out changeset 3b64368cff52 (bug 827976) Backed out changeset 69d0378e0c09 (bug 827976) Backed out changeset 9203871a5c6f (bug 827976) Backed out changeset 96a507c818e2 (bug 827976) Backed out changeset e254496ff95b (bug 827976)
browser/app/profile/firefox.js
browser/base/content/browser-places.js
browser/base/content/browser.js
browser/base/content/newInstall.js
browser/base/content/test/performance/browser.ini
browser/base/content/test/performance/browser_startup_hiddenwindow.js
browser/components/BrowserGlue.jsm
browser/components/downloads/DownloadsCommon.jsm
browser/components/newtab/lib/DownloadsManager.jsm
browser/components/sessionstore/SessionSaver.jsm
browser/components/tests/startupRecorder.js
browser/modules/Sanitizer.jsm
devtools/server/actors/root.js
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
dom/base/nsCCUncollectableMarker.cpp
testing/mochitest/leaks.py
toolkit/modules/Timer.jsm
toolkit/modules/tests/xpcshell/test_timer.js
toolkit/xre/nsAppRunner.cpp
tools/lint/eslint/modules.json
widget/windows/WindowsUIUtils.cpp
xpfe/appshell/nsAppShellService.cpp
xpfe/appshell/nsAppShellService.h
xpfe/appshell/nsIAppShellService.idl
xpfe/appshell/nsXULWindow.cpp
--- 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/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;