Bug 1034036 - Part 1: Merge RecentWindow.jsm and UpdateTopLevelContentWindowIDHelper.jsm into one module called 'BrowserWindowTracker.jsm'. r?dao draft
authorMike de Boer <mdeboer@mozilla.com>
Thu, 29 Mar 2018 19:18:51 +0200
changeset 774850 5de87be93e918f87680f5b2f87530c2dfb2f4362
parent 774711 8c71359d60e21055074ae8bc3dcb796d20f0cbaf
child 774851 6fc7bb758f81f1dae9804e2257f1425e77cffbac
push id104528
push usermdeboer@mozilla.com
push dateThu, 29 Mar 2018 17:20:41 +0000
reviewersdao
bugs1034036
milestone61.0a1
Bug 1034036 - Part 1: Merge RecentWindow.jsm and UpdateTopLevelContentWindowIDHelper.jsm into one module called 'BrowserWindowTracker.jsm'. r?dao MozReview-Commit-ID: 8qaOJAb3q5O
browser/base/content/browser-captivePortal.js
browser/base/content/browser.js
browser/base/content/nsContextMenu.js
browser/base/content/test/captivePortal/head.js
browser/base/content/test/performance/browser_startup.js
browser/base/content/utilityOverlay.js
browser/components/downloads/DownloadsCommon.jsm
browser/components/downloads/DownloadsTaskbar.jsm
browser/components/downloads/DownloadsViewUI.jsm
browser/components/downloads/content/allDownloadsView.js
browser/components/extensions/parent/ext-browser.js
browser/components/nsBrowserContentHandler.js
browser/components/nsBrowserGlue.js
browser/components/places/PlacesUIUtils.jsm
browser/components/places/tests/browser/browser_bookmarksProperties.js
browser/components/sessionstore/SessionStore.jsm
browser/components/sessionstore/test/browser_354894_perwindowpb.js
browser/components/translation/Translation.jsm
browser/extensions/formautofill/FormAutofillParent.jsm
browser/extensions/pocket/bootstrap.js
browser/modules/BrowserUITelemetry.jsm
browser/modules/BrowserWindowTracker.jsm
browser/modules/ContentCrashHandlers.jsm
browser/modules/ExtensionsUI.jsm
browser/modules/Feeds.jsm
browser/modules/RecentWindow.jsm
browser/modules/UpdateTopLevelContentWindowIDHelper.jsm
browser/modules/moz.build
testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py
testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
testing/talos/talos/tests/tabpaint/bootstrap.js
toolkit/components/telemetry/TelemetryReportingPolicy.jsm
toolkit/modules/addons/WebNavigation.jsm
--- a/browser/base/content/browser-captivePortal.js
+++ b/browser/base/content/browser-captivePortal.js
@@ -107,17 +107,17 @@ var CaptivePortalWatcher = {
     }
   },
 
   _captivePortalDetected() {
     if (this._delayedCaptivePortalDetectedInProgress) {
       return;
     }
 
-    let win = RecentWindow.getMostRecentBrowserWindow();
+    let win = BrowserWindowTracker.getMostRecentBrowserWindow();
     // If no browser window has focus, open and show the tab when we regain focus.
     // This is so that if a different application was focused, when the user
     // (re-)focuses a browser window, we open the tab immediately in that window
     // so they can log in before continuing to browse.
     if (win != Services.ww.activeWindow) {
       this._delayedCaptivePortalDetectedInProgress = true;
       Services.obs.addObserver(this, "xul-window-visible");
     }
@@ -130,17 +130,17 @@ var CaptivePortalWatcher = {
    * doesn't have focus. Triggers a portal recheck to reaffirm state, and adds
    * the tab if needed after a short delay to allow the recheck to complete.
    */
   _delayedCaptivePortalDetected() {
     if (!this._delayedCaptivePortalDetectedInProgress) {
       return;
     }
 
-    let win = RecentWindow.getMostRecentBrowserWindow();
+    let win = BrowserWindowTracker.getMostRecentBrowserWindow();
     if (win != Services.ww.activeWindow) {
       // The window that got focused was not a browser window.
       return;
     }
     Services.obs.removeObserver(this, "xul-window-visible");
     this._delayedCaptivePortalDetectedInProgress = false;
 
     if (win != window) {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -15,16 +15,17 @@ const {WebExtensionPolicy} = Cu.getGloba
 
 // lazy module getters
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AboutHome: "resource:///modules/AboutHome.jsm",
   BrowserUITelemetry: "resource:///modules/BrowserUITelemetry.jsm",
   BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
   BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   CharsetMenu: "resource://gre/modules/CharsetMenu.jsm",
   Color: "resource://gre/modules/Color.jsm",
   ContentSearch: "resource:///modules/ContentSearch.jsm",
   ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
   CustomizableUI: "resource:///modules/CustomizableUI.jsm",
   Deprecated: "resource://gre/modules/Deprecated.jsm",
   DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
   E10SUtils: "resource://gre/modules/E10SUtils.jsm",
@@ -43,17 +44,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   PanelMultiView: "resource:///modules/PanelMultiView.jsm",
   PanelView: "resource:///modules/PanelMultiView.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
   PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
   ReaderMode: "resource://gre/modules/ReaderMode.jsm",
   ReaderParent: "resource:///modules/ReaderParent.jsm",
-  RecentWindow: "resource:///modules/RecentWindow.jsm",
   SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
   Sanitizer: "resource:///modules/Sanitizer.jsm",
   SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
   SchedulePressure: "resource:///modules/SchedulePressure.jsm",
   ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm",
   SimpleServiceDiscovery: "resource://gre/modules/SimpleServiceDiscovery.jsm",
   SitePermissions: "resource:///modules/SitePermissions.jsm",
   TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
@@ -1485,18 +1485,17 @@ var gBrowserInit = {
       document.getElementById("textfieldDirection-swap").hidden = false;
     }
 
     // Setup click-and-hold gestures access to the session history
     // menus if global click-and-hold isn't turned on
     if (!getBoolPref("ui.click_hold_context_menus", false))
       SetClickAndHoldHandlers();
 
-    ChromeUtils.import("resource:///modules/UpdateTopLevelContentWindowIDHelper.jsm", {})
-      .trackBrowserWindow(window);
+    BrowserWindowTracker.track(window);
 
     PlacesToolbarHelper.init();
 
     ctrlTab.readPref();
     Services.prefs.addObserver(ctrlTab.prefName, ctrlTab);
 
     // The object handling the downloads indicator is initialized here in the
     // delayed startup function, but the actual indicator element is not loaded
@@ -2650,17 +2649,17 @@ async function BrowserViewSourceOfDocume
     // URL.
     preferredRemoteType =
       E10SUtils.getRemoteTypeForURI(args.URL, gMultiProcessBrowser);
   }
 
   // In the case of popups, we need to find a non-popup browser window.
   if (!tabBrowser || !window.toolbar.visible) {
     // This returns only non-popup browser windows by default.
-    let browserWindow = RecentWindow.getMostRecentBrowserWindow();
+    let browserWindow = BrowserWindowTracker.getMostRecentBrowserWindow();
     tabBrowser = browserWindow.gBrowser;
   }
 
   // `viewSourceInBrowser` will load the source content from the page
   // descriptor for the tab (when possible) or fallback to the network if
   // that fails.  Either way, the view source module will manage the tab's
   // location, so use "about:blank" here to avoid unnecessary redundant
   // requests.
@@ -5277,17 +5276,17 @@ nsBrowserAccess.prototype = {
                    aOpenerWindow = null, aOpenerBrowser = null,
                    aTriggeringPrincipal = null, aNextTabParentId = 0, aName = "") {
     let win, needToFocusWin;
 
     // try the current window.  if we're in a popup, fall back on the most recent browser window
     if (window.toolbar.visible)
       win = window;
     else {
-      win = RecentWindow.getMostRecentBrowserWindow({private: aIsPrivate});
+      win = BrowserWindowTracker.getMostRecentBrowserWindow({private: aIsPrivate});
       needToFocusWin = true;
     }
 
     if (!win) {
       // we couldn't find a suitable window, a new one needs to be opened.
       return null;
     }
 
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -872,17 +872,17 @@ nsContextMenu.prototype = {
     let openSelectionFn = function() {
       let tabBrowser = gBrowser;
       // In the case of popups, we need to find a non-popup browser window.
       // We might also not have a tabBrowser reference (if this isn't in a
       // a tabbrowser scope) or might have a fake/stub tabbrowser reference
       // (in the sidebar). Deal with those cases:
       if (!tabBrowser || !tabBrowser.loadOneTab || !window.toolbar.visible) {
         // This returns only non-popup browser windows by default.
-        let browserWindow = RecentWindow.getMostRecentBrowserWindow();
+        let browserWindow = BrowserWindowTracker.getMostRecentBrowserWindow();
         tabBrowser = browserWindow.gBrowser;
       }
       let relatedToCurrent = gBrowser && gBrowser.selectedBrowser == browser;
       let tab = tabBrowser.loadOneTab("about:blank", {
         relatedToCurrent,
         inBackground: false,
         triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
       });
--- a/browser/base/content/test/captivePortal/head.js
+++ b/browser/base/content/test/captivePortal/head.js
@@ -1,9 +1,9 @@
-ChromeUtils.import("resource:///modules/RecentWindow.jsm");
+ChromeUtils.import("resource:///modules/BrowserWindowTracker.jsm");
 
 ChromeUtils.defineModuleGetter(this, "CaptivePortalWatcher",
   "resource:///modules/CaptivePortalWatcher.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cps",
                                    "@mozilla.org/network/captive-portal-service;1",
                                    "nsICaptivePortalService");
 
@@ -15,31 +15,31 @@ const PORTAL_NOTIFICATION_VALUE = "capti
 async function setupPrefsAndRecentWindowBehavior() {
   await SpecialPowers.pushPrefEnv({
     set: [["captivedetect.canonicalURL", CANONICAL_URL],
           ["captivedetect.canonicalContent", CANONICAL_CONTENT]],
   });
   // We need to test behavior when a portal is detected when there is no browser
   // window, but we can't close the default window opened by the test harness.
   // Instead, we deactivate CaptivePortalWatcher in the default window and
-  // exclude it from RecentWindow.getMostRecentBrowserWindow in an attempt to
+  // exclude it from BrowserWindowTracker.getMostRecentBrowserWindow in an attempt to
   // mask its presence.
   window.CaptivePortalWatcher.uninit();
-  let getMostRecentBrowserWindowCopy = RecentWindow.getMostRecentBrowserWindow;
+  let getMostRecentBrowserWindowCopy = BrowserWindowTracker.getMostRecentBrowserWindow;
   let defaultWindow = window;
-  RecentWindow.getMostRecentBrowserWindow = () => {
+  BrowserWindowTracker.getMostRecentBrowserWindow = () => {
     let win = getMostRecentBrowserWindowCopy();
     if (win == defaultWindow) {
       return null;
     }
     return win;
   };
 
   registerCleanupFunction(function cleanUp() {
-    RecentWindow.getMostRecentBrowserWindow = getMostRecentBrowserWindowCopy;
+    BrowserWindowTracker.getMostRecentBrowserWindow = getMostRecentBrowserWindowCopy;
     window.CaptivePortalWatcher.init();
   });
 }
 
 async function portalDetected() {
   Services.obs.notifyObservers(null, "captive-portal-login");
   await BrowserTestUtils.waitForCondition(() => {
     return cps.state == cps.LOCKED_PORTAL;
--- a/browser/base/content/test/performance/browser_startup.js
+++ b/browser/base/content/test/performance/browser_startup.js
@@ -84,18 +84,18 @@ const startupPhases = {
   // interacting with the first browser window.
   "before handling user events": {blacklist: {
     components: new Set([
       "PageIconProtocolHandler.js",
       "PlacesCategoriesStarter.js",
       "nsPlacesExpiration.js",
     ]),
     modules: new Set([
-      // Bug 1391495 - RecentWindow.jsm is intermittently used.
-      // "resource:///modules/RecentWindow.jsm",
+      // Bug 1391495 - BrowserWindowTracker.jsm is intermittently used.
+      // "resource:///modules/BrowserWindowTracker.jsm",
       "resource://gre/modules/BookmarkHTMLUtils.jsm",
       "resource://gre/modules/Bookmarks.jsm",
       "resource://gre/modules/ContextualIdentityService.jsm",
       "resource://gre/modules/CrashSubmit.jsm",
       "resource://gre/modules/FxAccounts.jsm",
       "resource://gre/modules/FxAccountsStorage.jsm",
       "resource://gre/modules/PlacesBackups.jsm",
       "resource://gre/modules/PlacesSyncUtils.jsm",
@@ -122,17 +122,17 @@ const startupPhases = {
   }},
 };
 
 if (Services.prefs.getBoolPref("browser.startup.blankWindow")) {
   startupPhases["before profile selection"].whitelist.components.add("XULStore.js");
 }
 
 if (!gBrowser.selectedBrowser.isRemoteBrowser) {
-  // With e10s disabled, Places and RecentWindow.jsm (from a
+  // With e10s disabled, Places and BrowserWindowTracker.jsm (from a
   // SessionSaver.jsm timer) intermittently get loaded earlier. Likely
   // due to messages from the 'content' process arriving synchronously
   // instead of crossing a process boundary.
   info("merging the 'before handling user events' blacklist into the " +
        "'before first paint' one when e10s is disabled.");
   let from = startupPhases["before handling user events"].blacklist;
   let to = startupPhases["before first paint"].blacklist;
   for (let scriptType in from) {
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -6,24 +6,21 @@
 // Services = object with smart getters for common XPCOM services
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-                               "resource:///modules/RecentWindow.jsm");
-
-ChromeUtils.defineModuleGetter(this, "ShellService",
-                               "resource:///modules/ShellService.jsm");
-
-ChromeUtils.defineModuleGetter(this, "ContextualIdentityService",
-                               "resource://gre/modules/ContextualIdentityService.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
+  ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
+  ShellService: "resource:///modules/ShellService.jsm"
+});
 
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
 Object.defineProperty(this, "BROWSER_NEW_TAB_URL", {
   configurable: true,
   enumerable: true,
@@ -58,18 +55,18 @@ function getTopWin(skipPopups) {
   // If this is called in a browser window, use that window regardless of
   // whether it's the frontmost window, since commands can be executed in
   // background windows (bug 626148).
   if (top.document.documentElement.getAttribute("windowtype") == "navigator:browser" &&
       (!skipPopups || top.toolbar.visible))
     return top;
 
   let isPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
-  return RecentWindow.getMostRecentBrowserWindow({private: isPrivate,
-                                                  allowPopups: !skipPopups});
+  return BrowserWindowTracker.getMostRecentBrowserWindow({private: isPrivate,
+                                                         allowPopups: !skipPopups});
 }
 
 function getBoolPref(prefname, def) {
   try {
     return Services.prefs.getBoolPref(prefname);
   } catch (er) {
     return def;
   }
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -34,24 +34,24 @@ var EXPORTED_SYMBOLS = [
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   NetUtil: "resource://gre/modules/NetUtil.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
   AppConstants: "resource://gre/modules/AppConstants.jsm",
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   DownloadHistory: "resource://gre/modules/DownloadHistory.jsm",
   Downloads: "resource://gre/modules/Downloads.jsm",
   DownloadUIHelper: "resource://gre/modules/DownloadUIHelper.jsm",
   DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
   FileUtils: "resource://gre/modules/FileUtils.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
-  RecentWindow: "resource:///modules/RecentWindow.jsm",
 });
 
 XPCOMUtils.defineLazyGetter(this, "DownloadsLogger", () => {
   let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
   let consoleOptions = {
     maxLogLevelPref: "browser.download.loglevel",
     prefix: "Downloads"
   };
@@ -821,17 +821,17 @@ DownloadsDataCtor.prototype = {
    *
    * @param aType
    *        Set to "start" for new downloads, "finish" for completed downloads.
    */
   _notifyDownloadEvent(aType) {
     DownloadsCommon.log("Attempting to notify that a new download has started or finished.");
 
     // Show the panel in the most recent browser window, if present.
-    let browserWin = RecentWindow.getMostRecentBrowserWindow({ private: this._isPrivate });
+    let browserWin = BrowserWindowTracker.getMostRecentBrowserWindow({ private: this._isPrivate });
     if (!browserWin) {
       return;
     }
 
     if (this.panelHasShownBefore) {
       // For new downloads after the first one, don't show the panel
       // automatically, but provide a visible notification in the topmost
       // browser window, if the status indicator is already visible.
--- a/browser/components/downloads/DownloadsTaskbar.jsm
+++ b/browser/components/downloads/DownloadsTaskbar.jsm
@@ -12,23 +12,21 @@
 
 var EXPORTED_SYMBOLS = [
   "DownloadsTaskbar",
 ];
 
 // Globals
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-ChromeUtils.defineModuleGetter(this, "Downloads",
-                               "resource://gre/modules/Downloads.jsm");
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-                               "resource:///modules/RecentWindow.jsm");
-ChromeUtils.defineModuleGetter(this, "Services",
-                               "resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
+  Downloads: "resource://gre/modules/Downloads.jsm",
+  Services: "resource://gre/modules/Services.jsm",
+});
 
 XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function() {
   if (!("@mozilla.org/windows-taskbar;1" in Cc)) {
     return null;
   }
   let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"]
                      .getService(Ci.nsIWinTaskbar);
   return winTaskbar.available && winTaskbar;
@@ -135,17 +133,17 @@ var DownloadsTaskbar = {
     // the state of the new indicator, otherwise it will be updated as soon as
     // the DownloadSummary view is registered.
     if (this._summary) {
       this.onSummaryChanged();
     }
 
     aWindow.addEventListener("unload", () => {
       // Locate another browser window, excluding the one being closed.
-      let browserWindow = RecentWindow.getMostRecentBrowserWindow();
+      let browserWindow = BrowserWindowTracker.getMostRecentBrowserWindow();
       if (browserWindow) {
         // Move the progress indicator to the other browser window.
         this._attachIndicator(browserWindow);
       } else {
         // The last browser window has been closed.  We remove the reference to
         // the taskbar progress object so that the indicator will be registered
         // again on the next browser window that is opened.
         this._taskbarProgress = null;
@@ -164,17 +162,17 @@ var DownloadsTaskbar = {
     // the state of the new indicator, otherwise it will be updated as soon as
     // the DownloadSummary view is registered.
     if (this._summary) {
       this.onSummaryChanged();
     }
 
     aWindow.addEventListener("unload", () => {
       // Locate another browser window, excluding the one being closed.
-      let browserWindow = RecentWindow.getMostRecentBrowserWindow();
+      let browserWindow = BrowserWindowTracker.getMostRecentBrowserWindow();
       if (browserWindow) {
         // Move the progress indicator to the other browser window.
         this._attachGtkTaskbarProgress(browserWindow);
       } else {
         // The last browser window has been closed.  We remove the reference to
         // the taskbar progress object so that the indicator will be registered
         // again on the next browser window that is opened.
         this._taskbarProgress = null;
--- a/browser/components/downloads/DownloadsViewUI.jsm
+++ b/browser/components/downloads/DownloadsViewUI.jsm
@@ -10,30 +10,25 @@
 "use strict";
 
 var EXPORTED_SYMBOLS = [
   "DownloadsViewUI",
 ];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-ChromeUtils.defineModuleGetter(this, "Downloads",
-                               "resource://gre/modules/Downloads.jsm");
-ChromeUtils.defineModuleGetter(this, "DownloadUtils",
-                               "resource://gre/modules/DownloadUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "DownloadsCommon",
-                               "resource:///modules/DownloadsCommon.jsm");
-ChromeUtils.defineModuleGetter(this, "FileUtils",
-                               "resource://gre/modules/FileUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "OS",
-                               "resource://gre/modules/osfile.jsm");
-ChromeUtils.defineModuleGetter(this, "PlacesUtils",
-                               "resource://gre/modules/PlacesUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-                               "resource:///modules/RecentWindow.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
+  Downloads: "resource://gre/modules/Downloads.jsm",
+  DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
+  DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
+  FileUtils: "resource://gre/modules/FileUtils.jsm",
+  OS: "resource://gre/modules/osfile.jsm",
+  PlacesUtils: "resource://gre/modules/PlacesUtils.jsm"
+});
 
 var DownloadsViewUI = {
   /**
    * Returns true if the given string is the name of a command that can be
    * handled by the Downloads user interface, including standard commands.
    */
   isCommandName(name) {
     return name.startsWith("cmd_") || name.startsWith("downloadsCmd_");
@@ -127,17 +122,17 @@ this.DownloadsViewUI.DownloadElementShel
       // History downloads may not have a size defined.
       sizeStrings.stateLabel = s.sizeUnknown;
       sizeStrings.status = s.stateCompleted;
     }
     return sizeStrings;
   },
 
   get browserWindow() {
-    return RecentWindow.getMostRecentBrowserWindow();
+    return BrowserWindowTracker.getMostRecentBrowserWindow();
   },
 
   /**
    * The progress element for the download, or undefined in case the XBL binding
    * has not been applied yet.
    */
   get _progressElement() {
     if (!this.__progressElement) {
--- a/browser/components/downloads/content/allDownloadsView.js
+++ b/browser/components/downloads/content/allDownloadsView.js
@@ -1,31 +1,25 @@
 /* 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/. */
 /* eslint-env mozilla/browser-window */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-ChromeUtils.defineModuleGetter(this, "Downloads",
-                               "resource://gre/modules/Downloads.jsm");
-ChromeUtils.defineModuleGetter(this, "DownloadsCommon",
-                               "resource:///modules/DownloadsCommon.jsm");
-ChromeUtils.defineModuleGetter(this, "DownloadsViewUI",
-                               "resource:///modules/DownloadsViewUI.jsm");
-ChromeUtils.defineModuleGetter(this, "FileUtils",
-                               "resource://gre/modules/FileUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "NetUtil",
-                               "resource://gre/modules/NetUtil.jsm");
-ChromeUtils.defineModuleGetter(this, "OS",
-                               "resource://gre/modules/osfile.jsm");
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-                               "resource:///modules/RecentWindow.jsm");
-ChromeUtils.defineModuleGetter(this, "Services",
-                               "resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
+  Downloads: "resource://gre/modules/Downloads.jsm",
+  DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
+  DownloadsViewUI: "resource:///modules/DownloadsViewUI.jsm",
+  FileUtils: "resource://gre/modules/FileUtils.jsm",
+  NetUtil: "resource://gre/modules/NetUtil.jsm",
+  OS: "resource://gre/modules/osfile.jsm",
+  Services: "resource://gre/modules/Services.jsm"
+});
 
 /**
  * A download element shell is responsible for handling the commands and the
  * displayed data for a single download view element.
  *
  * The shell may contain a session download, a history download, or both.  When
  * both a history and a session download are present, the session download gets
  * priority and its information is displayed.
@@ -567,17 +561,17 @@ DownloadsPlacesView.prototype = {
 
   _canDownloadClipboardURL() {
     let [url /* ,name */] = this._getURLFromClipboardData();
     return url != "";
   },
 
   _downloadURLFromClipboard() {
     let [url, name] = this._getURLFromClipboardData();
-    let browserWin = RecentWindow.getMostRecentBrowserWindow();
+    let browserWin = BrowserWindowTracker.getMostRecentBrowserWindow();
     let initiatingDoc = browserWin ? browserWin.document : document;
     DownloadURL(url, name, initiatingDoc);
   },
 
   // nsIController
   doCommand(aCommand) {
     // Commands may be invoked with keyboard shortcuts even if disabled.
     if (!this.isCommandEnabled(aCommand)) {
@@ -747,17 +741,17 @@ DownloadsPlacesView.prototype = {
     // redownload already downloaded file.
     if (dt.mozGetDataAt("application/x-moz-file", 0)) {
       return;
     }
 
     let links = Services.droppedLinkHandler.dropLinks(aEvent);
     if (!links.length)
       return;
-    let browserWin = RecentWindow.getMostRecentBrowserWindow();
+    let browserWin = BrowserWindowTracker.getMostRecentBrowserWindow();
     let initiatingDoc = browserWin ? browserWin.document : document;
     for (let link of links) {
       if (link.url.startsWith("about:"))
         continue;
       DownloadURL(link.url, link.name, initiatingDoc);
     }
   },
 };
--- a/browser/components/extensions/parent/ext-browser.js
+++ b/browser/components/extensions/parent/ext-browser.js
@@ -3,18 +3,18 @@
 "use strict";
 
 // This file provides some useful code for the |tabs| and |windows|
 // modules. All of the code is installed on |global|, which is a scope
 // shared among the different ext-*.js scripts.
 
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-                               "resource:///modules/RecentWindow.jsm");
+ChromeUtils.defineModuleGetter(this, "BrowserWindowTracker",
+                               "resource:///modules/BrowserWindowTracker.jsm");
 
 var {
   ExtensionError,
   defineLazyGetter,
 } = ExtensionUtils;
 
 const READER_MODE_PREFIX = "about:reader";
 
@@ -160,17 +160,17 @@ class WindowTracker extends WindowTracke
   /**
    * @property {DOMWindow|null} topNormalWindow
    *        The currently active, or topmost, browser window, or null if no
    *        browser window is currently open.
    *        Will return the topmost "normal" (i.e., not popup) window.
    *        @readonly
    */
   get topNormalWindow() {
-    return RecentWindow.getMostRecentBrowserWindow({allowPopups: false});
+    return BrowserWindowTracker.getMostRecentBrowserWindow({allowPopups: false});
   }
 }
 
 /**
  * An event manager API provider which listens for a DOM event in any browser
  * window, and calls the given listener function whenever an event is received.
  * That listener function receives a `fire` object, which it can use to dispatch
  * events to the extension, and a DOM event object.
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -1,30 +1,26 @@
 /* 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/. */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 
-ChromeUtils.defineModuleGetter(this, "HeadlessShell",
-                               "resource:///modules/HeadlessShell.jsm");
-ChromeUtils.defineModuleGetter(this, "LaterRun",
-                               "resource:///modules/LaterRun.jsm");
-ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
-                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-                               "resource:///modules/RecentWindow.jsm");
-ChromeUtils.defineModuleGetter(this, "ShellService",
-                               "resource:///modules/ShellService.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
                                    "@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
-ChromeUtils.defineModuleGetter(this, "UpdatePing",
-                               "resource://gre/modules/UpdatePing.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
+  HeadlessShell: "resource:///modules/HeadlessShell.jsm",
+  LaterRun: "resource:///modules/LaterRun.jsm",
+  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
+  ShellService: "resource:///modules/ShellService.jsm",
+  UpdatePing: "resource://gre/modules/UpdatePing.jsm"
+});
 
 const nsISupports            = Ci.nsISupports;
 
 const nsIBrowserDOMWindow    = Ci.nsIBrowserDOMWindow;
 const nsIBrowserHandler      = Ci.nsIBrowserHandler;
 const nsIBrowserHistory      = Ci.nsIBrowserHistory;
 const nsIChannel             = Ci.nsIChannel;
 const nsICommandLine         = Ci.nsICommandLine;
@@ -682,17 +678,17 @@ var gBrowserContentHandler = new nsBrows
 
 function handURIToExistingBrowser(uri, location, cmdLine, forcePrivate, triggeringPrincipal) {
   if (!shouldLoadURI(uri))
     return;
 
   // Unless using a private window is forced, open external links in private
   // windows only if we're in perma-private mode.
   var allowPrivate = forcePrivate || PrivateBrowsingUtils.permanentPrivateBrowsing;
-  var navWin = RecentWindow.getMostRecentBrowserWindow({private: allowPrivate});
+  var navWin = BrowserWindowTracker.getMostRecentBrowserWindow({private: allowPrivate});
   if (!navWin) {
     // if we couldn't load it in an existing window, open a new one
     var features = "chrome,dialog=no,all" + gBrowserContentHandler.getFeatures(cmdLine);
     if (forcePrivate) {
       features += ",private";
     }
     openWindow(null, gBrowserContentHandler.chromeURL, "_blank", features, uri.spec);
     return;
@@ -799,17 +795,17 @@ nsDefaultCommandLineHandler.prototype = 
                    URLlist);
       }
 
     } else if (!cmdLine.preventDefault) {
       if (AppConstants.isPlatformAndVersionAtLeast("win", "10") &&
           cmdLine.state != nsICommandLine.STATE_INITIAL_LAUNCH &&
           WindowsUIUtils.inTabletMode) {
         // In windows 10 tablet mode, do not create a new window, but reuse the existing one.
-        let win = RecentWindow.getMostRecentBrowserWindow();
+        let win = BrowserWindowTracker.getMostRecentBrowserWindow();
         if (win) {
           win.focus();
           return;
         }
       }
       if (cmdLine.state == nsICommandLine.STATE_INITIAL_LAUNCH) {
         let win = Services.wm.getMostRecentWindow("navigator:blank");
         if (win) {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -86,16 +86,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   AsyncPrefs: "resource://gre/modules/AsyncPrefs.jsm",
   AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
   AutoCompletePopup: "resource://gre/modules/AutoCompletePopup.jsm",
   BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.jsm",
   BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.jsm",
   BrowserErrorReporter: "resource:///modules/BrowserErrorReporter.jsm",
   BrowserUITelemetry: "resource:///modules/BrowserUITelemetry.jsm",
   BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   ContentClick: "resource:///modules/ContentClick.jsm",
   ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
   CustomizableUI: "resource:///modules/CustomizableUI.jsm",
   DateTimePickerHelper: "resource://gre/modules/DateTimePickerHelper.jsm",
   ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
   Feeds: "resource:///modules/Feeds.jsm",
   FileSource: "resource://gre/modules/L10nRegistry.jsm",
   FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
@@ -116,17 +117,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   PermissionUI: "resource:///modules/PermissionUI.jsm",
   PingCentre: "resource:///modules/PingCentre.jsm",
   PlacesBackups: "resource://gre/modules/PlacesBackups.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   ProcessHangMonitor: "resource:///modules/ProcessHangMonitor.jsm",
   ReaderParent: "resource:///modules/ReaderParent.jsm",
-  RecentWindow: "resource:///modules/RecentWindow.jsm",
   RemotePrompt: "resource:///modules/RemotePrompt.jsm",
   SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
   Sanitizer: "resource:///modules/Sanitizer.jsm",
   SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
   ShellService: "resource:///modules/ShellService.jsm",
   TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
   UIState: "resource://services-sync/UIState.jsm",
   UITour: "resource:///modules/UITour.jsm",
@@ -491,17 +491,17 @@ BrowserGlue.prototype = {
         this._migrationImportsDefaultBookmarks = true;
         break;
       case "initial-migration-did-import-default-bookmarks":
         this._initPlaces(true);
         break;
       case "handle-xul-text-link":
         let linkHandled = subject.QueryInterface(Ci.nsISupportsPRBool);
         if (!linkHandled.data) {
-          let win = RecentWindow.getMostRecentBrowserWindow();
+          let win = BrowserWindowTracker.getMostRecentBrowserWindow();
           if (win) {
             data = JSON.parse(data);
             let where = win.whereToOpenLink(data);
             // Preserve legacy behavior of non-modifier left-clicks
             // opening in a new selected tab.
             if (where == "current") {
               where = "tab";
             }
@@ -520,17 +520,17 @@ BrowserGlue.prototype = {
         // This notification is broadcast by the docshell when it "fixes up" a
         // URI that it's been asked to load into a keyword search.
         let engine = null;
         try {
           engine = subject.QueryInterface(Ci.nsISearchEngine);
         } catch (ex) {
           Cu.reportError(ex);
         }
-        let win = RecentWindow.getMostRecentBrowserWindow();
+        let win = BrowserWindowTracker.getMostRecentBrowserWindow();
         win.BrowserSearch.recordSearchInTelemetry(engine, "urlbar");
         break;
       case "browser-search-engine-modified":
         // Ensure we cleanup the hiddenOneOffs pref when removing
         // an engine, and that newly added engines are visible.
         if (data == "engine-added" || data == "engine-removed") {
           let engineName = subject.QueryInterface(Ci.nsISearchEngine).name;
           let pref = Services.prefs.getStringPref("browser.search.hiddenOneOffs");
@@ -817,17 +817,17 @@ BrowserGlue.prototype = {
     const ONE_DAY = 24 * 60 * 60 * 1000;
     return (Date.now() - profileDate) / ONE_DAY;
   },
 
   _showSlowStartupNotification(profileAge) {
     if (profileAge < 90) // 3 months
       return;
 
-    let win = RecentWindow.getMostRecentBrowserWindow();
+    let win = BrowserWindowTracker.getMostRecentBrowserWindow();
     if (!win)
       return;
 
     let productName = gBrandBundle.GetStringFromName("brandFullName");
     let message = win.gNavigatorBundle.getFormattedString("slowStartup.message", [productName]);
 
     let buttons = [
       {
@@ -855,17 +855,17 @@ BrowserGlue.prototype = {
   /**
    * Show a notification bar offering a reset.
    *
    * @param reason
    *        String of either "unused" or "uninstall", specifying the reason
    *        why a profile reset is offered.
    */
   _resetProfileNotification(reason) {
-    let win = RecentWindow.getMostRecentBrowserWindow();
+    let win = BrowserWindowTracker.getMostRecentBrowserWindow();
     if (!win)
       return;
 
     ChromeUtils.import("resource://gre/modules/ResetProfile.jsm");
     if (!ResetProfile.resetSupported())
       return;
 
     let productName = gBrandBundle.GetStringFromName("brandShortName");
@@ -892,17 +892,17 @@ BrowserGlue.prototype = {
 
     let nb = win.document.getElementById("global-notificationbox");
     nb.appendNotification(message, "reset-profile-notification",
                           "chrome://global/skin/icons/question-16.png",
                           nb.PRIORITY_INFO_LOW, buttons);
   },
 
   _notifyUnsignedAddonsDisabled() {
-    let win = RecentWindow.getMostRecentBrowserWindow();
+    let win = BrowserWindowTracker.getMostRecentBrowserWindow();
     if (!win)
       return;
 
     let message = win.gNavigatorBundle.getString("unsignedAddonsDisabled.message");
     let buttons = [
       {
         label:     win.gNavigatorBundle.getString("unsignedAddonsDisabled.learnMore.label"),
         accessKey: win.gNavigatorBundle.getString("unsignedAddonsDisabled.learnMore.accesskey"),
@@ -1466,17 +1466,17 @@ BrowserGlue.prototype = {
                                   stringParams: [appName]});
       let url = getNotifyString({propName: "notificationURL",
                                  prefName: "startup.homepage_override_url"});
       let label = getNotifyString({propName: "notificationButtonLabel",
                                    stringName: "pu.notifyButton.label"});
       let key = getNotifyString({propName: "notificationButtonAccessKey",
                                  stringName: "pu.notifyButton.accesskey"});
 
-      let win = RecentWindow.getMostRecentBrowserWindow();
+      let win = BrowserWindowTracker.getMostRecentBrowserWindow();
       let notifyBox = win.document.getElementById("high-priority-global-notificationbox");
 
       let buttons = [
                       {
                         label,
                         accessKey: key,
                         popup:     null,
                         callback(aNotificationBar, aButton) {
@@ -1501,17 +1501,17 @@ BrowserGlue.prototype = {
                                 stringParams: [appName]});
     let url = getNotifyString({propName: "alertURL",
                                prefName: "startup.homepage_override_url"});
 
     function clickCallback(subject, topic, data) {
       // This callback will be called twice but only once with this topic
       if (topic != "alertclickcallback")
         return;
-      let win = RecentWindow.getMostRecentBrowserWindow();
+      let win = BrowserWindowTracker.getMostRecentBrowserWindow();
       win.openUILinkIn(data, "tab");
     }
 
     try {
       // This will throw NS_ERROR_NOT_AVAILABLE if the notification cannot
       // be displayed per the idl.
       this.AlertsService.showAlertNotification(null, title, text,
                                           true, url, clickCallback);
@@ -1762,17 +1762,17 @@ BrowserGlue.prototype = {
     var text = placesBundle.formatStringFromName("lockPrompt.text", [applicationName], 1);
     var buttonText = placesBundle.GetStringFromName("lockPromptInfoButton.label");
     var accessKey = placesBundle.GetStringFromName("lockPromptInfoButton.accessKey");
 
     var helpTopic = "places-locked";
     var url = Services.urlFormatter.formatURLPref("app.support.baseURL");
     url += helpTopic;
 
-    var win = RecentWindow.getMostRecentBrowserWindow();
+    var win = BrowserWindowTracker.getMostRecentBrowserWindow();
 
     var buttons = [
                     {
                       label:     buttonText,
                       accessKey,
                       popup:     null,
                       callback(aNotificationBar, aButton) {
                         win.openUILinkIn(url, "tab");
@@ -2250,17 +2250,17 @@ BrowserGlue.prototype = {
                         .add(isDefaultError);
       Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_ALWAYS_CHECK")
                         .add(shouldCheck);
       Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_DIALOG_PROMPT_RAWCOUNT")
                         .add(promptCount);
     } catch (ex) { /* Don't break the default prompt if telemetry is broken. */ }
 
     if (willPrompt) {
-      DefaultBrowserCheck.prompt(RecentWindow.getMostRecentBrowserWindow());
+      DefaultBrowserCheck.prompt(BrowserWindowTracker.getMostRecentBrowserWindow());
     }
   },
 
   _migrateMatchBucketsPrefForUIVersion60(forceCheck = false) {
     function check() {
       if (CustomizableUI.getPlacementOfWidget("search-container")) {
         Services.prefs.setCharPref(prefName,
                                    "general:5,suggestion:Infinity");
@@ -2431,17 +2431,17 @@ BrowserGlue.prototype = {
    * Open preferences even if there are no open windows.
    */
   _openPreferences(...args) {
     if (Services.appShell.hiddenDOMWindow.openPreferences) {
       Services.appShell.hiddenDOMWindow.openPreferences(...args);
       return;
     }
 
-    let chromeWindow = RecentWindow.getMostRecentBrowserWindow();
+    let chromeWindow = BrowserWindowTracker.getMostRecentBrowserWindow();
     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, Services.prefs.getCharPref("browser.chromeURL"),
@@ -2456,17 +2456,17 @@ BrowserGlue.prototype = {
    * We open the received URIs in background tabs.
    */
   async _onDisplaySyncURIs(data) {
     try {
       // The payload is wrapped weirdly because of how Sync does notifications.
       const URIs = data.wrappedJSObject.object;
 
       // win can be null, but it's ok, we'll assign it later in openTab()
-      let win = RecentWindow.getMostRecentBrowserWindow({private: false});
+      let win = BrowserWindowTracker.getMostRecentBrowserWindow({private: false});
 
       const openTab = async (URI) => {
         let tab;
         if (!win) {
           win = await this._openURLInNewWindow(URI.uri);
           let tabs = win.gBrowser.tabs;
           tab = tabs[tabs.length - 1];
         } else {
@@ -2539,17 +2539,17 @@ BrowserGlue.prototype = {
   },
 
   async _onVerifyLoginNotification({body, title, url}) {
     let tab;
     let imageURL;
     if (AppConstants.platform == "win") {
       imageURL = "chrome://branding/content/icon64.png";
     }
-    let win = RecentWindow.getMostRecentBrowserWindow({private: false});
+    let win = BrowserWindowTracker.getMostRecentBrowserWindow({private: false});
     if (!win) {
       win = await this._openURLInNewWindow(url);
       let tabs = win.gBrowser.tabs;
       tab = tabs[tabs.length - 1];
     } else {
       tab = win.gBrowser.addTab(url);
     }
     tab.setAttribute("attention", true);
@@ -2574,17 +2574,17 @@ BrowserGlue.prototype = {
     let body = accountsBundle.formatStringFromName("deviceConnectedBody" +
                                                    (deviceName ? "" : ".noDeviceName"),
                                                    [deviceName], 1);
 
     let clickCallback = async (subject, topic, data) => {
       if (topic != "alertclickcallback")
         return;
       let url = await FxAccounts.config.promiseManageDevicesURI("device-connected-notification");
-      let win = RecentWindow.getMostRecentBrowserWindow({private: false});
+      let win = BrowserWindowTracker.getMostRecentBrowserWindow({private: false});
       if (!win) {
         this._openURLInNewWindow(url);
       } else {
         win.gBrowser.addTab(url);
       }
     };
 
     try {
@@ -2624,17 +2624,17 @@ BrowserGlue.prototype = {
       return;
     }
     if (Services.prefs.getBoolPref("browser.flash-protected-mode-flip.done")) {
       return;
     }
     Services.prefs.setBoolPref("dom.ipc.plugins.flash.disable-protected-mode", true);
     Services.prefs.setBoolPref("browser.flash-protected-mode-flip.done", true);
 
-    let win = RecentWindow.getMostRecentBrowserWindow();
+    let win = BrowserWindowTracker.getMostRecentBrowserWindow();
     if (!win) {
       return;
     }
     let productName = gBrandBundle.GetStringFromName("brandShortName");
     let message = win.gNavigatorBundle.
       getFormattedString("flashHang.message", [productName]);
     let buttons = [{
       label: win.gNavigatorBundle.getString("flashHang.helpButton.label"),
@@ -2993,17 +2993,17 @@ var JawsScreenReaderVersionCheck = {
     // support and never prompt if e10s is disabled or if we're on
     // nightly.
     if (!Services.appinfo.shouldBlockIncompatJaws ||
         !Services.appinfo.browserTabsRemoteAutostart ||
         AppConstants.NIGHTLY_BUILD) {
       return;
     }
 
-    let win = RecentWindow.getMostRecentBrowserWindow();
+    let win = BrowserWindowTracker.getMostRecentBrowserWindow();
     if (!win || !win.gBrowser || !win.gBrowser.selectedBrowser) {
       Services.console.logStringMessage(
           "Content access support for older versions of JAWS is disabled " +
           "due to compatibility issues with this version of Firefox.");
       this._prompted = false;
       return;
     }
 
--- a/browser/components/places/PlacesUIUtils.jsm
+++ b/browser/components/places/PlacesUIUtils.jsm
@@ -6,23 +6,23 @@
 var EXPORTED_SYMBOLS = ["PlacesUIUtils"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AppConstants: "resource://gre/modules/AppConstants.jsm",
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   OpenInTabsUtils: "resource:///modules/OpenInTabsUtils.jsm",
   PlacesTransactions: "resource://gre/modules/PlacesTransactions.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
-  RecentWindow: "resource:///modules/RecentWindow.jsm",
   Weave: "resource://services-sync/main.js",
 });
 
 XPCOMUtils.defineLazyGetter(this, "bundle", function() {
   return Services.strings.createBundle("chrome://browser/locale/places/places.properties");
 });
 
 const gInContentProcess = Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
@@ -602,17 +602,17 @@ var PlacesUIUtils = {
     if (!aItemsToOpen.length)
       return;
 
     // Prefer the caller window if it's a browser window, otherwise use
     // the top browser window.
     var browserWindow = null;
     browserWindow =
       aWindow && aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser" ?
-      aWindow : RecentWindow.getMostRecentBrowserWindow();
+      aWindow : BrowserWindowTracker.getMostRecentBrowserWindow();
 
     var urls = [];
     let skipMarking = browserWindow && PrivateBrowsingUtils.isWindowPrivate(browserWindow);
     for (let item of aItemsToOpen) {
       urls.push(item.uri);
       if (skipMarking) {
         continue;
       }
@@ -740,17 +740,17 @@ var PlacesUIUtils = {
           this.markPageAsTyped(aNode.uri);
       }
 
       // Check whether the node is a bookmark which should be opened as
       // a web panel
       if (aWhere == "current" && isBookmark) {
         if (PlacesUtils.annotations
                        .itemHasAnnotation(aNode.itemId, this.LOAD_IN_SIDEBAR_ANNO)) {
-          let browserWin = RecentWindow.getMostRecentBrowserWindow();
+          let browserWin = BrowserWindowTracker.getMostRecentBrowserWindow();
           if (browserWin) {
             browserWin.openWebPanel(aNode.title, aNode.uri);
             return;
           }
         }
       }
 
       aWindow.openUILinkIn(aNode.uri, aWhere, {
--- a/browser/components/places/tests/browser/browser_bookmarksProperties.js
+++ b/browser/components/places/tests/browser/browser_bookmarksProperties.js
@@ -29,18 +29,18 @@ const ACTION_ADD = 1;
 const TYPE_FOLDER = 0;
 const TYPE_BOOKMARK = 1;
 
 const TEST_URL = "http://www.example.com/";
 
 const DIALOG_URL = "chrome://browser/content/places/bookmarkProperties.xul";
 const DIALOG_URL_MINIMAL_UI = "chrome://browser/content/places/bookmarkProperties2.xul";
 
-ChromeUtils.import("resource:///modules/RecentWindow.jsm");
-var win = RecentWindow.getMostRecentBrowserWindow();
+ChromeUtils.import("resource:///modules/BrowserWindowTracker.jsm");
+var win = BrowserWindowTracker.getMostRecentBrowserWindow();
 
 function add_bookmark(url) {
   return PlacesUtils.bookmarks.insert({
     parentGuid: PlacesUtils.bookmarks.unfiledGuid,
     url,
     title: `bookmark/${url}`
   });
 }
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -160,21 +160,21 @@ XPCOMUtils.defineLazyServiceGetters(this
   gSessionStartup: ["@mozilla.org/browser/sessionstartup;1", "nsISessionStartup"],
   gScreenManager: ["@mozilla.org/gfx/screenmanager;1", "nsIScreenManager"],
   Telemetry: ["@mozilla.org/base/telemetry;1", "nsITelemetry"],
 });
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   DevToolsShim: "chrome://devtools-startup/content/DevToolsShim.jsm",
   GlobalState: "resource:///modules/sessionstore/GlobalState.jsm",
   PrivacyFilter: "resource:///modules/sessionstore/PrivacyFilter.jsm",
   PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
-  RecentWindow: "resource:///modules/RecentWindow.jsm",
   RunState: "resource:///modules/sessionstore/RunState.jsm",
   SessionCookies: "resource:///modules/sessionstore/SessionCookies.jsm",
   SessionFile: "resource:///modules/sessionstore/SessionFile.jsm",
   SessionSaver: "resource:///modules/sessionstore/SessionSaver.jsm",
   TabAttributes: "resource:///modules/sessionstore/TabAttributes.jsm",
   TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
   TabState: "resource:///modules/sessionstore/TabState.jsm",
   TabStateCache: "resource:///modules/sessionstore/TabStateCache.jsm",
@@ -4224,17 +4224,17 @@ var SessionStoreInternal = {
     }
   },
 
   /**
    * Returns most recent window
    * @returns Window reference
    */
   _getMostRecentBrowserWindow: function ssi_getMostRecentBrowserWindow() {
-    return RecentWindow.getMostRecentBrowserWindow({ allowPopups: true });
+    return BrowserWindowTracker.getMostRecentBrowserWindow({ allowPopups: true });
   },
 
   /**
    * Calls onClose for windows that are determined to be closed but aren't
    * destroyed yet, which would otherwise cause getBrowserState and
    * setBrowserState to treat them as open windows.
    */
   _handleClosedWindows: function ssi_handleClosedWindows() {
--- a/browser/components/sessionstore/test/browser_354894_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_354894_perwindowpb.js
@@ -19,17 +19,17 @@
  * not enabled on that platform (platform shim; the application is kept running
  * although there are no windows left)
  * @note There is a difference when closing a browser window with
  * BrowserTryToCloseWindow() as opposed to close(). The former will make
  * nsSessionStore restore a window next time it gets a chance and will post
  * notifications. The latter won't.
  */
 
-// The rejection "RecentWindow.getMostRecentBrowserWindow(...) is null" is left
+// The rejection "BrowserWindowTracker.getMostRecentBrowserWindow(...) is null" is left
 // unhandled in some cases. This bug should be fixed, but for the moment this
 // file is whitelisted.
 //
 // NOTE: Whitelisting a class of rejections should be limited. Normally you
 //       should use "expectUncaughtRejection" to flag individual failures.
 ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", this);
 PromiseTestUtils.whitelistRejectionsGlobally(/getMostRecentBrowserWindow/);
 
--- a/browser/components/translation/Translation.jsm
+++ b/browser/components/translation/Translation.jsm
@@ -69,18 +69,18 @@ var Translation = {
     trUI.showURLBarIcon();
 
     if (trUI.shouldShowInfoBar(aBrowser.currentURI))
       trUI.showTranslationInfoBar();
   },
 
   openProviderAttribution() {
     let attribution = this.supportedEngines[this.translationEngine];
-    ChromeUtils.import("resource:///modules/RecentWindow.jsm");
-    RecentWindow.getMostRecentBrowserWindow().openUILinkIn(attribution, "tab");
+    ChromeUtils.import("resource:///modules/BrowserWindowTracker.jsm");
+    BrowserWindowTracker.getMostRecentBrowserWindow().openUILinkIn(attribution, "tab");
   },
 
   /**
    * The list of translation engines and their attributions.
    */
   supportedEngines: {
     "bing": "http://aka.ms/MicrosoftTranslatorAttribution",
     "yandex": "http://translate.yandex.com/"
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -31,24 +31,22 @@
 // constructor via a backstage pass.
 var EXPORTED_SYMBOLS = ["formAutofillParent"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
 
-ChromeUtils.defineModuleGetter(this, "FormAutofillPreferences",
-                               "resource://formautofill/FormAutofillPreferences.jsm");
-ChromeUtils.defineModuleGetter(this, "FormAutofillDoorhanger",
-                               "resource://formautofill/FormAutofillDoorhanger.jsm");
-ChromeUtils.defineModuleGetter(this, "MasterPassword",
-                               "resource://formautofill/MasterPassword.jsm");
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-                               "resource:///modules/RecentWindow.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
+  FormAutofillPreferences: "resource://formautofill/FormAutofillPreferences.jsm",
+  FormAutofillDoorhanger: "resource://formautofill/FormAutofillDoorhanger.jsm",
+  MasterPassword: "resource://formautofill/MasterPassword.jsm",
+});
 
 this.log = null;
 FormAutofillUtils.defineLazyLogGetter(this, EXPORTED_SYMBOLS[0]);
 
 const {
   ENABLED_AUTOFILL_ADDRESSES_PREF,
   ENABLED_AUTOFILL_CREDITCARDS_PREF,
   CREDITCARDS_COLLECTION_NAME,
@@ -238,17 +236,17 @@ FormAutofillParent.prototype = {
         data.guids.forEach(guid => this.formAutofillStorage.creditCards.remove(guid));
         break;
       }
       case "FormAutofill:OnFormSubmit": {
         this._onFormSubmit(data, target);
         break;
       }
       case "FormAutofill:OpenPreferences": {
-        const win = RecentWindow.getMostRecentBrowserWindow();
+        const win = BrowserWindowTracker.getMostRecentBrowserWindow();
         win.openPreferences("panePrivacy", {origin: "autofillFooter"});
         break;
       }
       case "FormAutofill:GetDecryptedString": {
         let {cipherText, reauth} = data;
         let string;
         try {
           string = await MasterPassword.decrypt(cipherText, reauth);
--- a/browser/extensions/pocket/bootstrap.js
+++ b/browser/extensions/pocket/bootstrap.js
@@ -17,18 +17,16 @@ ChromeUtils.defineModuleGetter(this, "Ad
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                                "resource://gre/modules/BrowserUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "PageActions",
                                "resource:///modules/PageActions.jsm");
 ChromeUtils.defineModuleGetter(this, "Pocket",
                                "chrome://pocket/content/Pocket.jsm");
 ChromeUtils.defineModuleGetter(this, "ReaderMode",
                                "resource://gre/modules/ReaderMode.jsm");
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-                               "resource:///modules/RecentWindow.jsm");
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyGetter(this, "gPocketBundle", function() {
   return Services.strings.createBundle("chrome://pocket/locale/pocket.properties");
 });
 XPCOMUtils.defineLazyGetter(this, "gPocketStyleURI", function() {
   return Services.io.newURI("chrome://pocket-shared/skin/pocket.css");
 });
--- a/browser/modules/BrowserUITelemetry.jsm
+++ b/browser/modules/BrowserUITelemetry.jsm
@@ -4,26 +4,23 @@
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["BrowserUITelemetry"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-ChromeUtils.defineModuleGetter(this, "AppConstants",
-  "resource://gre/modules/AppConstants.jsm");
-ChromeUtils.defineModuleGetter(this, "UITelemetry",
-  "resource://gre/modules/UITelemetry.jsm");
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-  "resource:///modules/RecentWindow.jsm");
-ChromeUtils.defineModuleGetter(this, "CustomizableUI",
-  "resource:///modules/CustomizableUI.jsm");
-ChromeUtils.defineModuleGetter(this, "UITour",
-  "resource:///modules/UITour.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  AppConstants: "resource://gre/modules/AppConstants.jsm",
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
+  UITelemetry: "resource://gre/modules/UITelemetry.jsm",
+  CustomizableUI: "resource:///modules/CustomizableUI.jsm",
+  UITour: "resource:///modules/UITour.jsm",
+});
 XPCOMUtils.defineLazyGetter(this, "Timer", function() {
   let timer = {};
   ChromeUtils.import("resource://gre/modules/Timer.jsm", timer);
   return timer;
 });
 
 const MS_SECOND = 1000;
 const MS_MINUTE = MS_SECOND * 60;
@@ -276,17 +273,17 @@ var BrowserUITelemetry = {
   _firstWindowMeasurements: null,
   _gatherFirstWindowMeasurements() {
     // We'll gather measurements as soon as the session has restored.
     // We do this here instead of waiting for UITelemetry to ask for
     // our measurements because at that point all browser windows have
     // probably been closed, since the vast majority of saved-session
     // pings are gathered during shutdown.
     Services.search.init(rv => {
-      let win = RecentWindow.getMostRecentBrowserWindow({
+      let win = BrowserWindowTracker.getMostRecentBrowserWindow({
         private: false,
         allowPopups: false,
       });
       // If there are no such windows, we're out of luck. :(
       this._firstWindowMeasurements = win ? this._getWindowMeasurements(win, rv)
                                           : {};
     });
   },
rename from browser/modules/UpdateTopLevelContentWindowIDHelper.jsm
rename to browser/modules/BrowserWindowTracker.jsm
--- a/browser/modules/UpdateTopLevelContentWindowIDHelper.jsm
+++ b/browser/modules/BrowserWindowTracker.jsm
@@ -2,40 +2,39 @@
  * 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/. */
 
 /*
  * This module tracks each browser window and informs network module
  * the current selected tab's content outer window ID.
  */
 
-var EXPORTED_SYMBOLS = ["trackBrowserWindow"];
+var EXPORTED_SYMBOLS = ["BrowserWindowTracker"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 // Lazy getters
 XPCOMUtils.defineLazyServiceGetter(this, "_focusManager",
                                    "@mozilla.org/focus-manager;1",
                                    "nsIFocusManager");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  AppConstants: "resource://gre/modules/AppConstants.jsm",
+  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm"
+});
 
 // Constants
 const TAB_EVENTS = ["TabBrowserInserted", "TabSelect"];
 const WINDOW_EVENTS = ["activate", "unload"];
 const DEBUG = false;
 
 // Variables
 var _lastFocusedWindow = null;
 var _lastTopLevelWindowID = 0;
 
-// Exported symbol
-function trackBrowserWindow(aWindow) {
-  WindowHelper.addWindow(aWindow);
-}
-
 // Global methods
 function debug(s) {
   if (DEBUG) {
     dump("-*- UpdateTopLevelContentWindowIDHelper: " + s + "\n");
   }
 }
 
 function _updateCurrentContentOuterWindowID(aBrowser) {
@@ -130,9 +129,70 @@ var WindowHelper = {
 
     _updateCurrentContentOuterWindowID(aWindow.gBrowser.selectedBrowser);
   },
 
   handleFocusedWindow: function NP_WH_handleFocusedWindow(aWindow) {
     // aWindow is now focused
     _lastFocusedWindow = aWindow;
   },
+
+  getMostRecentBrowserWindow: function RW_getMostRecentBrowserWindow(aOptions) {
+    let checkPrivacy = typeof aOptions == "object" &&
+                       "private" in aOptions;
+
+    let allowPopups = typeof aOptions == "object" && !!aOptions.allowPopups;
+
+    function isSuitableBrowserWindow(win) {
+      return (!win.closed &&
+              (allowPopups || win.toolbar.visible) &&
+              (!checkPrivacy ||
+               PrivateBrowsingUtils.permanentPrivateBrowsing ||
+               PrivateBrowsingUtils.isWindowPrivate(win) == aOptions.private));
+    }
+
+    let broken_wm_z_order =
+      AppConstants.platform != "macosx" && AppConstants.platform != "win";
+
+    if (broken_wm_z_order) {
+      let win = Services.wm.getMostRecentWindow("navigator:browser");
+
+      // if we're lucky, this isn't a popup, and we can just return this
+      if (win && !isSuitableBrowserWindow(win)) {
+        win = null;
+        let windowList = Services.wm.getEnumerator("navigator:browser");
+        // this is oldest to newest, so this gets a bit ugly
+        while (windowList.hasMoreElements()) {
+          let nextWin = windowList.getNext();
+          if (isSuitableBrowserWindow(nextWin))
+            win = nextWin;
+        }
+      }
+      return win;
+    }
+    let windowList = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
+    while (windowList.hasMoreElements()) {
+      let win = windowList.getNext();
+      if (isSuitableBrowserWindow(win))
+        return win;
+    }
+    return null;
+  }
 };
+
+this.BrowserWindowTracker = {
+  /**
+   * Get the most recent browser window.
+   *
+   * @param aOptions an object accepting the arguments for the search.
+   *        * private: true to restrict the search to private windows
+   *            only, false to restrict the search to non-private only.
+   *            Omit the property to search in both groups.
+   *        * allowPopups: true if popup windows are permissable.
+   */
+  getMostRecentBrowserWindow(options) {
+    return WindowHelper.getMostRecentBrowserWindow(options);
+  },
+
+  track(window) {
+    return WindowHelper.addWindow(window);
+  }
+};
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -6,32 +6,26 @@
 
 var EXPORTED_SYMBOLS = [ "TabCrashHandler",
                          "PluginCrashReporter",
                          "UnsubmittedCrashHandler" ];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-ChromeUtils.defineModuleGetter(this, "CrashSubmit",
-  "resource://gre/modules/CrashSubmit.jsm");
-ChromeUtils.defineModuleGetter(this, "AppConstants",
-  "resource://gre/modules/AppConstants.jsm");
-ChromeUtils.defineModuleGetter(this, "RemotePages",
-  "resource://gre/modules/RemotePageManager.jsm");
-ChromeUtils.defineModuleGetter(this, "SessionStore",
-  "resource:///modules/sessionstore/SessionStore.jsm");
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-  "resource:///modules/RecentWindow.jsm");
-ChromeUtils.defineModuleGetter(this, "PluralForm",
-  "resource://gre/modules/PluralForm.jsm");
-ChromeUtils.defineModuleGetter(this, "setTimeout",
-  "resource://gre/modules/Timer.jsm");
-ChromeUtils.defineModuleGetter(this, "clearTimeout",
-  "resource://gre/modules/Timer.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  AppConstants: "resource://gre/modules/AppConstants.jsm",
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
+  clearTimeout: "resource://gre/modules/Timer.jsm",
+  CrashSubmit: "resource://gre/modules/CrashSubmit.jsm",
+  PluralForm: "resource://gre/modules/PluralForm.jsm",
+  RemotePages: "resource://gre/modules/RemotePageManager.jsm",
+  SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
+  setTimeout: "resource://gre/modules/Timer.jsm"
+});
 
 XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
   const url = "chrome://browser/locale/browser.properties";
   return Services.strings.createBundle(url);
 });
 
 // We don't process crash reports older than 28 days, so don't bother
 // submitting them
@@ -833,17 +827,17 @@ var UnsubmittedCrashHandler = {
    *        onAction (function, optional)
    *          A callback to fire once the user performs an
    *          action on the notification bar (this includes
    *          dismissing the notification).
    *
    * @returns The <xul:notification> if one is shown. null otherwise.
    */
   show({ notificationID, message, reportIDs, onAction }) {
-    let chromeWin = RecentWindow.getMostRecentBrowserWindow();
+    let chromeWin = BrowserWindowTracker.getMostRecentBrowserWindow();
     if (!chromeWin) {
       // Can't show a notification in this case. We'll hopefully
       // get another opportunity to have the user submit their
       // crash reports later.
       return null;
     }
 
     let nb =  chromeWin.document.getElementById("global-notificationbox");
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -3,28 +3,24 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 var EXPORTED_SYMBOLS = ["ExtensionsUI"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
 
-ChromeUtils.defineModuleGetter(this, "AddonManager",
-                               "resource://gre/modules/AddonManager.jsm");
-ChromeUtils.defineModuleGetter(this, "AddonManagerPrivate",
-                               "resource://gre/modules/AddonManager.jsm");
-ChromeUtils.defineModuleGetter(this, "AppMenuNotifications",
-                               "resource://gre/modules/AppMenuNotifications.jsm");
-ChromeUtils.defineModuleGetter(this, "ExtensionData",
-                               "resource://gre/modules/Extension.jsm");
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-                               "resource:///modules/RecentWindow.jsm");
-ChromeUtils.defineModuleGetter(this, "Services",
-                               "resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetters(this, {
+  AddonManager: "resource://gre/modules/AddonManager.jsm",
+  AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm",
+  AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.jsm",
+  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
+  ExtensionData: "resource://gre/modules/Extension.jsm",
+  Services: "resource://gre/modules/Services.jsm"
+});
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
                                       "extensions.webextPermissionPrompts", false);
 
 const DEFAULT_EXTENSION_ICON = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
 
 const BROWSER_PROPERTIES = "chrome://browser/locale/browser.properties";
 const BRAND_PROPERTIES = "chrome://branding/locale/brand.properties";
@@ -88,17 +84,17 @@ var ExtensionsUI = {
 
       for (let addon of sideloaded) {
         this.sideloaded.add(addon);
       }
         this._updateNotifications();
     } else {
       // This and all the accompanying about:newaddon code can eventually
       // be removed.  See bug 1331521.
-      let win = RecentWindow.getMostRecentBrowserWindow();
+      let win = BrowserWindowTracker.getMostRecentBrowserWindow();
       for (let addon of sideloaded) {
         win.openUILinkIn(`about:newaddon?id=${addon.id}`, "tab");
       }
     }
   },
 
   async _checkNewDistroAddons() {
     let newDistroAddons = AddonManagerPrivate.getNewDistroAddons();
--- a/browser/modules/Feeds.jsm
+++ b/browser/modules/Feeds.jsm
@@ -6,18 +6,18 @@
 
 var EXPORTED_SYMBOLS = [ "Feeds" ];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                                "resource://gre/modules/BrowserUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-                               "resource:///modules/RecentWindow.jsm");
+ChromeUtils.defineModuleGetter(this, "BrowserWindowTracker",
+                               "resource:///modules/BrowserWindowTracker.jsm");
 
 var Feeds = {
   // Listeners are added in nsBrowserGlue.js
   receiveMessage(aMessage) {
     let data = aMessage.data;
     switch (aMessage.name) {
       case "WCCR:registerProtocolHandler": {
         let registrar = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
@@ -38,17 +38,17 @@ var Feeds = {
       case "WCCR:setAutoHandler": {
         let registrar = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
                           getService(Ci.nsIWebContentConverterService);
         registrar.setAutoHandler(data.contentType, data.handler);
         break;
       }
 
       case "FeedConverter:addLiveBookmark": {
-        let topWindow = RecentWindow.getMostRecentBrowserWindow();
+        let topWindow = BrowserWindowTracker.getMostRecentBrowserWindow();
         topWindow.PlacesCommandHook.addLiveBookmark(data.spec, data.title, data.subtitle)
                                    .catch(Cu.reportError);
         break;
       }
     }
   },
 
   /**
deleted file mode 100644
--- a/browser/modules/RecentWindow.jsm
+++ /dev/null
@@ -1,65 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-var EXPORTED_SYMBOLS = ["RecentWindow"];
-
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
-
-var RecentWindow = {
-  /*
-   * Get the most recent browser window.
-   *
-   * @param aOptions an object accepting the arguments for the search.
-   *        * private: true to restrict the search to private windows
-   *            only, false to restrict the search to non-private only.
-   *            Omit the property to search in both groups.
-   *        * allowPopups: true if popup windows are permissable.
-   */
-  getMostRecentBrowserWindow: function RW_getMostRecentBrowserWindow(aOptions) {
-    let checkPrivacy = typeof aOptions == "object" &&
-                       "private" in aOptions;
-
-    let allowPopups = typeof aOptions == "object" && !!aOptions.allowPopups;
-
-    function isSuitableBrowserWindow(win) {
-      return (!win.closed &&
-              (allowPopups || win.toolbar.visible) &&
-              (!checkPrivacy ||
-               PrivateBrowsingUtils.permanentPrivateBrowsing ||
-               PrivateBrowsingUtils.isWindowPrivate(win) == aOptions.private));
-    }
-
-    let broken_wm_z_order =
-      AppConstants.platform != "macosx" && AppConstants.platform != "win";
-
-    if (broken_wm_z_order) {
-      let win = Services.wm.getMostRecentWindow("navigator:browser");
-
-      // if we're lucky, this isn't a popup, and we can just return this
-      if (win && !isSuitableBrowserWindow(win)) {
-        win = null;
-        let windowList = Services.wm.getEnumerator("navigator:browser");
-        // this is oldest to newest, so this gets a bit ugly
-        while (windowList.hasMoreElements()) {
-          let nextWin = windowList.getNext();
-          if (isSuitableBrowserWindow(nextWin))
-            win = nextWin;
-        }
-      }
-      return win;
-    }
-    let windowList = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
-    while (windowList.hasMoreElements()) {
-      let win = windowList.getNext();
-      if (isSuitableBrowserWindow(win))
-        return win;
-    }
-    return null;
-  }
-};
-
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -44,16 +44,19 @@ with Files("AboutNewTab.jsm"):
     BUG_COMPONENT = ("Firefox", "New Tab Page")
 
 with Files('AsyncTabSwitcher.jsm'):
     BUG_COMPONENT = ('Firefox', 'Tabbed Browser')
 
 with Files("AttributionCode.jsm"):
     BUG_COMPONENT = ("Toolkit", "Telemetry")
 
+with Files("BrowserWindowTracker.jsm"):
+    BUG_COMPONENT = ("Core", "Networking")
+
 with Files("*Telemetry.jsm"):
     BUG_COMPONENT = ("Toolkit", "Telemetry")
 
 with Files("ContentCrashHandlers.jsm"):
     BUG_COMPONENT = ("Toolkit", "Crash Reporting")
 
 with Files("ContentSearch.jsm"):
     BUG_COMPONENT = ("Firefox", "Search")
@@ -101,19 +104,16 @@ with Files("WindowsJumpLists.jsm"):
     BUG_COMPONENT = ("Firefox", "Shell Integration")
 
 with Files("WindowsPreviewPerTab.jsm"):
     BUG_COMPONENT = ("Core", "Widget: Win32")
 
 with Files("offlineAppCache.jsm"):
     BUG_COMPONENT = ("Firefox", "Preferences")
 
-with Files("UpdateTopLevelContentWindowIDHelper.jsm"):
-    BUG_COMPONENT = ("Core", "Networking")
-
 with Files("webrtcUI.jsm"):
     BUG_COMPONENT = ("Firefox", "Device Permissions")
 
 with Files("ZoomUI.jsm"):
     BUG_COMPONENT = ("Firefox", "Toolbars and Customization")
 
 
 BROWSER_CHROME_MANIFESTS += [
@@ -125,16 +125,17 @@ XPCSHELL_TESTS_MANIFESTS += ['test/unit/
 EXTRA_JS_MODULES += [
     'AboutHome.jsm',
     'AboutNewTab.jsm',
     'AsyncTabSwitcher.jsm',
     'AttributionCode.jsm',
     'BrowserErrorReporter.jsm',
     'BrowserUITelemetry.jsm',
     'BrowserUsageTelemetry.jsm',
+    'BrowserWindowTracker.jsm',
     'ContentClick.jsm',
     'ContentCrashHandlers.jsm',
     'ContentLinkHandler.jsm',
     'ContentMetaHandler.jsm',
     'ContentObservers.js',
     'ContentSearch.jsm',
     'ContentWebRTC.jsm',
     'ContextMenu.jsm',
@@ -146,24 +147,22 @@ EXTRA_JS_MODULES += [
     'offlineAppCache.jsm',
     'OpenInTabsUtils.jsm',
     'PageActions.jsm',
     'PermissionUI.jsm',
     'PingCentre.jsm',
     'PluginContent.jsm',
     'ProcessHangMonitor.jsm',
     'ReaderParent.jsm',
-    'RecentWindow.jsm',
     'RemotePrompt.jsm',
     'Sanitizer.jsm',
     'SchedulePressure.jsm',
     'SitePermissions.jsm',
     'ThemeVariableMap.jsm',
     'TransientPrefs.jsm',
-    'UpdateTopLevelContentWindowIDHelper.jsm',
     'webrtcUI.jsm',
     'ZoomUI.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES += [
         'Windows8WindowFrameColor.jsm',
         'WindowsJumpLists.jsm',
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
@@ -84,18 +84,18 @@ class BaseNavigationTestCase(WindowManag
               Components.utils.import("resource://gre/modules/AppConstants.jsm");
 
               let win = null;
 
               if (AppConstants.MOZ_APP_NAME == "fennec") {
                 Components.utils.import("resource://gre/modules/Services.jsm");
                 win = Services.wm.getMostRecentWindow("navigator:browser");
               } else {
-                Components.utils.import("resource:///modules/RecentWindow.jsm");
-                win = RecentWindow.getMostRecentBrowserWindow();
+                Components.utils.import("resource:///modules/BrowserWindowTracker.jsm");
+                win = BrowserWindowTracker.getMostRecentBrowserWindow();
               }
 
               let tabBrowser = null;
 
               // Fennec
               if (win.BrowserApp) {
                 tabBrowser = win.BrowserApp.selectedBrowser;
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py
@@ -39,18 +39,18 @@ class TestSwitchToWindowContent(WindowMa
                 Components.utils.import("resource://gre/modules/AppConstants.jsm");
 
                 let win = null;
 
                 if (AppConstants.MOZ_APP_NAME == "fennec") {
                   Components.utils.import("resource://gre/modules/Services.jsm");
                   win = Services.wm.getMostRecentWindow("navigator:browser");
                 } else {
-                  Components.utils.import("resource:///modules/RecentWindow.jsm");
-                  win = RecentWindow.getMostRecentBrowserWindow();
+                  Components.utils.import("resource:///modules/BrowserWindowTracker.jsm");
+                  win = BrowserWindowTracker.getMostRecentBrowserWindow();
                 }
 
                 let tabBrowser = null;
 
                 // Fennec
                 if (win.BrowserApp) {
                   tabBrowser = win.BrowserApp;
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
@@ -90,19 +90,19 @@ class TestCloseWindow(WindowManagerMixin
         test_page = self.marionette.absolute_url("windowHandles.html")
         tab = self.open_tab()
         self.marionette.switch_to_window(tab)
         self.marionette.navigate(test_page)
         self.marionette.switch_to_window(self.start_tab)
 
         with self.marionette.using_context("chrome"):
             self.marionette.execute_async_script("""
-              Components.utils.import("resource:///modules/RecentWindow.jsm");
+              Components.utils.import("resource:///modules/BrowserWindowTracker.jsm");
 
-              let win = RecentWindow.getMostRecentBrowserWindow();
+              let win = BrowserWindowTracker.getMostRecentBrowserWindow();
               win.addEventListener("TabBrowserDiscarded", ev => {
                 marionetteScriptFinished(true);
               }, { once: true});
               win.gBrowser.discardBrowser(win.gBrowser.tabs[1].linkedBrowser);
             """)
 
         window_handles = self.marionette.window_handles
         window_handles.remove(self.start_tab)
--- a/testing/talos/talos/tests/tabpaint/bootstrap.js
+++ b/testing/talos/talos/tests/tabpaint/bootstrap.js
@@ -17,17 +17,16 @@
  *    comes from the operating system, to the parent.
  *
  * 2) The tab has been opened by clicking on a link in content. It is possible
  *    for certain types of links (_blank links for example) to open new tabs.
  */
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Task.jsm");
-ChromeUtils.import("resource:///modules/RecentWindow.jsm");
 
 const ANIMATION_PREF = "toolkit.cosmeticAnimations.enabled";
 
 const MULTI_OPT_OUT_PREF = "dom.ipc.multiOptOut";
 
 const TARGET_URI = "chrome://tabpaint/content/target.html";
 
 var TabPaint = {
--- a/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
+++ b/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
@@ -431,18 +431,18 @@ var TelemetryReportingPolicyImpl = {
     let firstRunPolicyURL = Services.prefs.getStringPref(TelemetryUtils.Preferences.FirstRunURL, "");
     if (!firstRunPolicyURL) {
       return false;
     }
     firstRunPolicyURL = Services.urlFormatter.formatURL(firstRunPolicyURL);
 
     let win;
     try {
-      const { RecentWindow } = ChromeUtils.import("resource:///modules/RecentWindow.jsm", {});
-      win = RecentWindow.getMostRecentBrowserWindow();
+      const { BrowserWindowTracker } = ChromeUtils.import("resource:///modules/BrowserWindowTracker.jsm", {});
+      win = BrowserWindowTracker.getMostRecentBrowserWindow();
     } catch (e) {}
 
     if (!win) {
       this._log.info("Couldn't find browser window to open first-run page. Falling back to infobar.");
       return false;
     }
 
     // We'll consider the user notified once the privacy policy has been loaded
--- a/toolkit/modules/addons/WebNavigation.jsm
+++ b/toolkit/modules/addons/WebNavigation.jsm
@@ -4,18 +4,18 @@
 
 "use strict";
 
 const EXPORTED_SYMBOLS = ["WebNavigation"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-                               "resource:///modules/RecentWindow.jsm");
+ChromeUtils.defineModuleGetter(this, "BrowserWindowTracker",
+                               "resource:///modules/BrowserWindowTracker.jsm");
 
 // Maximum amount of time that can be passed and still consider
 // the data recent (similar to how is done in nsNavHistory,
 // e.g. nsNavHistory::CheckIsRecentEvent, but with a lower threshold value).
 const RECENT_DATA_THRESHOLD = 5 * 1000000;
 
 var Manager = {
   // Map[string -> Map[listener -> URLFilter]]
@@ -211,17 +211,17 @@ var Manager = {
    * @param {boolean} [tabTransitionData.auto_bookmark]
    * @param {boolean} [tabTransitionData.from_address_bar]
    * @param {boolean} [tabTransitionData.generated]
    * @param {boolean} [tabTransitionData.keyword]
    * @param {boolean} [tabTransitionData.link]
    * @param {boolean} [tabTransitionData.typed]
    */
   setRecentTabTransitionData(tabTransitionData) {
-    let window = RecentWindow.getMostRecentBrowserWindow();
+    let window = BrowserWindowTracker.getMostRecentBrowserWindow();
     if (window && window.gBrowser && window.gBrowser.selectedTab &&
         window.gBrowser.selectedTab.linkedBrowser) {
       let browser = window.gBrowser.selectedTab.linkedBrowser;
 
       // Get recent tab transition data to update if any.
       let prevData = this.getAndForgetRecentTabTransitionData(browser);
 
       let newData = Object.assign(