Bug 1034036 - Part 1: Merge RecentWindow.jsm and UpdateTopLevelContentWindowIDHelper.jsm into one module called 'BrowserWindowTracker.jsm'. r=dao
authorMike de Boer <mdeboer@mozilla.com>
Wed, 11 Apr 2018 12:05:56 +0200
changeset 414461 39baa8f0bc04b30b3fa1e618fc6bea0d7bb8a8ab
parent 414460 3ae765ce2b287848d0dd57292239197f3e3e7988
child 414462 b86203bcf975e96ffc3d5d6a254af6bededd04f5
push id33870
push usercsabou@mozilla.com
push dateThu, 19 Apr 2018 22:28:45 +0000
treeherdermozilla-central@5c983ad5a421 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdao
bugs1034036
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1034036 - Part 1: Merge RecentWindow.jsm and UpdateTopLevelContentWindowIDHelper.jsm into one module called 'BrowserWindowTracker.jsm'. r=dao MozReview-Commit-ID: 9qzq1aGvjDu
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",
@@ -1484,18 +1484,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
@@ -2670,17 +2669,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.
@@ -5296,17 +5295,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
@@ -881,17 +881,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
@@ -85,18 +85,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",
@@ -123,17 +123,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";
 
@@ -183,17 +183,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});
   }
 }
 
 class TabTracker extends TabTrackerBase {
   constructor() {
     super();
 
     this._tabs = new WeakMap();
--- 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.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"
+});
 XPCOMUtils.defineLazyServiceGetter(this, "WindowsUIUtils",
-                                   "@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
-ChromeUtils.defineModuleGetter(this, "UpdatePing",
-                               "resource://gre/modules/UpdatePing.jsm");
+  "@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils");
 
 function shouldLoadURI(aURI) {
   if (aURI && !aURI.schemeIs("chrome"))
     return true;
 
   dump("*** Preventing external load of chrome: URI into browser window\n");
   dump("    Use --chrome <uri> instead\n");
   return false;
@@ -672,17 +668,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
     openBrowserWindow(cmdLine, uri.spec, null, forcePrivate);
     return;
   }
 
   var navNav = navWin.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIWebNavigation);
@@ -784,17 +780,17 @@ nsDefaultCommandLineHandler.prototype = 
         openBrowserWindow(cmdLine, URLlist);
       }
 
     } else if (!cmdLine.preventDefault) {
       if (AppConstants.isPlatformAndVersionAtLeast("win", "10") &&
           cmdLine.state != Ci.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;
         }
       }
       openBrowserWindow(cmdLine);
     } else {
       // Need a better solution in the future to avoid opening the blank window
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -91,16 +91,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",
@@ -122,17 +123,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",
@@ -500,17 +500,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";
             }
@@ -529,17 +529,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");
@@ -827,17 +827,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 = [
       {
@@ -865,17 +865,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");
@@ -902,17 +902,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"),
@@ -1476,17 +1476,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) {
@@ -1511,17 +1511,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.openTrustedLinkIn(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);
@@ -1772,17 +1772,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.openTrustedLinkIn(url, "tab");
@@ -2279,17 +2279,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());
     }
   },
 
   async _migrateMatchBucketsPrefForUI66() {
     // This does two related things.
     //
     // (1) Profiles created on or after Firefox 57's release date were eligible
     // for a Shield study that changed the browser.urlbar.matchBuckets pref in
@@ -2501,17 +2501,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"),
@@ -2526,17 +2526,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 {
@@ -2609,17 +2609,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);
@@ -2644,17 +2644,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 {
@@ -2694,17 +2694,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"),
@@ -3061,17 +3061,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.openTrustedLinkIn(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",
@@ -4225,17 +4225,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().openTrustedLinkIn(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,24 +4,22 @@
 
 "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, "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",
+  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;
@@ -261,17 +259,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";
--- 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")
@@ -104,19 +107,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 += [
@@ -128,16 +128,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',
@@ -149,25 +150,23 @@ 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',
     'SiteDataManager.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(