Bug 1034036 - Part 1: Merge RecentWindow.jsm and UpdateTopLevelContentWindowIDHelper.jsm into one module called 'WindowTracker.jsm'. r?dao draft
authorMike de Boer <mdeboer@mozilla.com>
Fri, 23 Feb 2018 14:06:10 +0100
changeset 758991 831f1c3d044a21ab031bbd23d55c9966bf69c767
parent 758927 6661c077325c35af028f1cdaa660f673cbea39be
child 758992 989b94c7055e859f475e8d012f531c4d736d1e7e
push id100245
push usermdeboer@mozilla.com
push dateFri, 23 Feb 2018 14:49:39 +0000
reviewersdao
bugs1034036
milestone60.0a1
Bug 1034036 - Part 1: Merge RecentWindow.jsm and UpdateTopLevelContentWindowIDHelper.jsm into one module called 'WindowTracker.jsm'. r?dao MozReview-Commit-ID: Jum8nCqkWUV
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/allDownloadsViewOverlay.js
browser/components/extensions/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/ContentCrashHandlers.jsm
browser/modules/ExtensionsUI.jsm
browser/modules/Feeds.jsm
browser/modules/RecentWindow.jsm
browser/modules/UpdateTopLevelContentWindowIDHelper.jsm
browser/modules/WindowTracker.jsm
browser/modules/moz.build
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 = WindowTracker.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 = WindowTracker.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
@@ -40,32 +40,32 @@ 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",
   TelemetryStopwatch: "resource://gre/modules/TelemetryStopwatch.jsm",
   Translation: "resource:///modules/translation/Translation.jsm",
   UITour: "resource:///modules/UITour.jsm",
   UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
   Utils: "resource://gre/modules/sessionstore/Utils.jsm",
   Weave: "resource://services-sync/main.js",
   WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
+  WindowTracker: "resource:///modules/WindowTracker.jsm",
   fxAccounts: "resource://gre/modules/FxAccounts.jsm",
   webrtcUI: "resource:///modules/webrtcUI.jsm",
   ZoomUI: "resource:///modules/ZoomUI.jsm",
 });
 
 if (AppConstants.MOZ_CRASHREPORTER) {
   ChromeUtils.defineModuleGetter(this, "PluginCrashReporter",
     "resource:///modules/ContentCrashHandlers.jsm");
@@ -1476,18 +1476,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);
+    WindowTracker.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
@@ -2620,17 +2619,17 @@ function BrowserViewSourceOfDocument(aAr
       // 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 = WindowTracker.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.
@@ -5141,17 +5140,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 = WindowTracker.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
@@ -870,17 +870,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 = WindowTracker.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/WindowTracker.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 WindowTracker.getMostRecentBrowserWindow in an attempt to
   // mask its presence.
   window.CaptivePortalWatcher.uninit();
-  let getMostRecentBrowserWindowCopy = RecentWindow.getMostRecentBrowserWindow;
+  let getMostRecentBrowserWindowCopy = WindowTracker.getMostRecentBrowserWindow;
   let defaultWindow = window;
-  RecentWindow.getMostRecentBrowserWindow = () => {
+  WindowTracker.getMostRecentBrowserWindow = () => {
     let win = getMostRecentBrowserWindowCopy();
     if (win == defaultWindow) {
       return null;
     }
     return win;
   };
 
   registerCleanupFunction(function cleanUp() {
-    RecentWindow.getMostRecentBrowserWindow = getMostRecentBrowserWindowCopy;
+    WindowTracker.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 - WindowTracker.jsm is intermittently used.
+      // "resource:///modules/WindowTracker.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",
@@ -119,17 +119,17 @@ const startupPhases = {
       "resource://gre/modules/AsyncPrefs.jsm",
       "resource://gre/modules/LoginManagerContextMenu.jsm",
       "resource://gre/modules/Task.jsm",
     ]),
   }},
 };
 
 if (!gBrowser.selectedBrowser.isRemoteBrowser) {
-  // With e10s disabled, Places and RecentWindow.jsm (from a
+  // With e10s disabled, Places and WindowTracker.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
@@ -4,25 +4,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // 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.import("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, {
+  ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
+  ShellService: "resource:///modules/ShellService.jsm",
+  WindowTracker: "resource:///modules/WindowTracker.jsm"
+});
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
 Object.defineProperty(this, "BROWSER_NEW_TAB_URL", {
   configurable: true,
   enumerable: true,
   get() {
@@ -56,17 +52,17 @@ 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,
+  return WindowTracker.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
@@ -41,17 +41,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   AppConstants: "resource://gre/modules/AppConstants.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",
+  WindowTracker: "resource:///modules/WindowTracker.jsm",
 });
 
 XPCOMUtils.defineLazyGetter(this, "DownloadsLogger", () => {
   let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
   let consoleOptions = {
     maxLogLevelPref: "browser.download.loglevel",
     prefix: "Downloads"
   };
@@ -823,17 +823,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 = WindowTracker.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 @@
 
 this.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, {
+  Downloads: "resource://gre/modules/Downloads.jsm",
+  Services: "resource://gre/modules/Services.jsm",
+  WindowTracker: "resource:///modules/WindowTracker.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;
@@ -125,17 +123,17 @@ this.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 = WindowTracker.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;
--- a/browser/components/downloads/DownloadsViewUI.jsm
+++ b/browser/components/downloads/DownloadsViewUI.jsm
@@ -10,30 +10,25 @@
 "use strict";
 
 this.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, {
+  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",
+  WindowTracker: "resource:///modules/WindowTracker.jsm"
+});
 
 this.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_");
@@ -105,17 +100,17 @@ this.DownloadsViewUI.DownloadElementShel
   get displayName() {
     if (!this.download.target.path) {
       return this.download.source.url;
     }
     return OS.Path.basename(this.download.target.path);
   },
 
   get browserWindow() {
-    return RecentWindow.getMostRecentBrowserWindow();
+    return WindowTracker.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/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.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, {
+  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",
+  WindowTracker: "resource:///modules/WindowTracker.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 = WindowTracker.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 = WindowTracker.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/ext-browser.js
+++ b/browser/components/extensions/ext-browser.js
@@ -9,18 +9,18 @@
 /* global EventEmitter:false, TabContext:false, WindowEventManager:false,
           makeWidgetId:false, tabTracker:true, windowTracker:true */
 /* import-globals-from ../../../toolkit/components/extensions/ext-toolkit.js */
 
 /* globals TabBase, WindowBase, TabTrackerBase, WindowTrackerBase, TabManagerBase, WindowManagerBase */
 
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "RecentWindow",
-                               "resource:///modules/RecentWindow.jsm");
+ChromeUtils.defineModuleGetter(this, "WindowTracker",
+                               "resource:///modules/WindowTracker.jsm");
 
 var {
   ExtensionError,
   defineLazyGetter,
 } = ExtensionUtils;
 
 const READER_MODE_PREFIX = "about:reader";
 
@@ -168,17 +168,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 WindowTracker.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, {
+  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",
+  WindowTracker: "resource:///modules/WindowTracker.jsm"
+});
 
 const nsISupports            = Components.interfaces.nsISupports;
 
 const nsIBrowserDOMWindow    = Components.interfaces.nsIBrowserDOMWindow;
 const nsIBrowserHandler      = Components.interfaces.nsIBrowserHandler;
 const nsIBrowserHistory      = Components.interfaces.nsIBrowserHistory;
 const nsIChannel             = Components.interfaces.nsIChannel;
 const nsICommandLine         = Components.interfaces.nsICommandLine;
@@ -669,17 +665,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 = WindowTracker.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;
@@ -786,17 +782,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 = WindowTracker.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
@@ -118,28 +118,28 @@ 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",
   SimpleServiceDiscovery: "resource://gre/modules/SimpleServiceDiscovery.jsm",
   TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
   UIState: "resource://services-sync/UIState.jsm",
   UITour: "resource:///modules/UITour.jsm",
   WebChannel: "resource://gre/modules/WebChannel.jsm",
   WindowsRegistry: "resource://gre/modules/WindowsRegistry.jsm",
+  WindowTracker: "resource:///modules/WindowTracker.jsm",
 });
 
 /* global AboutHome:false, ContentPrefServiceParent:false, ContentSearch:false,
           UpdateListener:false, webrtcUI:false */
 
 /**
  * IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL.
  * XXX Bug 1325373 is for making eslint detect these automatically.
@@ -494,17 +494,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 = WindowTracker.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";
             }
@@ -523,17 +523,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 = WindowTracker.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");
@@ -816,17 +816,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 = WindowTracker.getMostRecentBrowserWindow();
     if (!win)
       return;
 
     let productName = gBrandBundle.GetStringFromName("brandFullName");
     let message = win.gNavigatorBundle.getFormattedString("slowStartup.message", [productName]);
 
     let buttons = [
       {
@@ -854,17 +854,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 = WindowTracker.getMostRecentBrowserWindow();
     if (!win)
       return;
 
     ChromeUtils.import("resource://gre/modules/ResetProfile.jsm");
     if (!ResetProfile.resetSupported())
       return;
 
     let productName = gBrandBundle.GetStringFromName("brandShortName");
@@ -891,17 +891,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 = WindowTracker.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"),
@@ -1461,17 +1461,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 = WindowTracker.getMostRecentBrowserWindow();
       let notifyBox = win.document.getElementById("high-priority-global-notificationbox");
 
       let buttons = [
                       {
                         label,
                         accessKey: key,
                         popup:     null,
                         callback(aNotificationBar, aButton) {
@@ -1496,17 +1496,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 = WindowTracker.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);
@@ -1749,17 +1749,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 = WindowTracker.getMostRecentBrowserWindow();
 
     var buttons = [
                     {
                       label:     buttonText,
                       accessKey,
                       popup:     null,
                       callback(aNotificationBar, aButton) {
                         win.openUILinkIn(url, "tab");
@@ -2388,17 +2388,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(WindowTracker.getMostRecentBrowserWindow());
     }
   },
 
   _migrateMatchBucketsPrefForUIVersion60(forceCheck = false) {
     function check() {
       if (CustomizableUI.getPlacementOfWidget("search-container")) {
         Services.prefs.setCharPref(prefName,
                                    "general:5,suggestion:Infinity");
@@ -2568,17 +2568,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 = WindowTracker.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"),
@@ -2593,17 +2593,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 = WindowTracker.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 {
@@ -2676,17 +2676,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 = WindowTracker.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);
@@ -2711,17 +2711,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 = WindowTracker.getMostRecentBrowserWindow({private: false});
       if (!win) {
         this._openURLInNewWindow(url);
       } else {
         win.gBrowser.addTab(url);
       }
     };
 
     try {
@@ -2761,17 +2761,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 = WindowTracker.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"),
@@ -3127,17 +3127,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 = WindowTracker.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
@@ -8,20 +8,20 @@ this.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, {
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
-  RecentWindow: "resource:///modules/RecentWindow.jsm",
   PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
   PlacesTransactions: "resource://gre/modules/PlacesTransactions.jsm",
   Weave: "resource://services-sync/main.js",
+  WindowTracker: "resource:///modules/WindowTracker.jsm",
 });
 
 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;
 const FAVICON_REQUEST_TIMEOUT = 60 * 1000;
@@ -638,17 +638,17 @@ this.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 : WindowTracker.getMostRecentBrowserWindow();
 
     var urls = [];
     let skipMarking = browserWindow && PrivateBrowsingUtils.isWindowPrivate(browserWindow);
     for (let item of aItemsToOpen) {
       urls.push(item.uri);
       if (skipMarking) {
         continue;
       }
@@ -772,17 +772,17 @@ this.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 = WindowTracker.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/WindowTracker.jsm");
+var win = WindowTracker.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
@@ -164,30 +164,30 @@ XPCOMUtils.defineLazyServiceGetters(this
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
   DevToolsShim: "chrome://devtools-shim/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",
   TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.jsm",
   Utils: "resource://gre/modules/sessionstore/Utils.jsm",
   ViewSourceBrowser: "resource://gre/modules/ViewSourceBrowser.jsm",
   console: "resource://gre/modules/Console.jsm",
   setTimeout: "resource://gre/modules/Timer.jsm",
+  WindowTracker: "resource:///modules/WindowTracker.jsm",
 });
 
 /**
  * |true| if we are in debug mode, |false| otherwise.
  * Debug mode is controlled by preference browser.sessionstore.debug
  */
 var gDebuggingEnabled = false;
 function debug(aMsg) {
@@ -4224,17 +4224,17 @@ var SessionStoreInternal = {
     }
   },
 
   /**
    * Returns most recent window
    * @returns Window reference
    */
   _getMostRecentBrowserWindow: function ssi_getMostRecentBrowserWindow() {
-    return RecentWindow.getMostRecentBrowserWindow({ allowPopups: true });
+    return WindowTracker.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 "WindowTracker.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 @@ this.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/WindowTracker.jsm");
+    WindowTracker.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.
 this.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, {
+  FormAutofillPreferences: "resource://formautofill/FormAutofillPreferences.jsm",
+  FormAutofillDoorhanger: "resource://formautofill/FormAutofillDoorhanger.jsm",
+  MasterPassword: "resource://formautofill/MasterPassword.jsm",
+  WindowTracker: "resource:///modules/WindowTracker.jsm",
+});
 
 this.log = null;
 FormAutofillUtils.defineLazyLogGetter(this, 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.profileStorage.creditCards.remove(guid));
         break;
       }
       case "FormAutofill:OnFormSubmit": {
         this._onFormSubmit(data, target);
         break;
       }
       case "FormAutofill:OpenPreferences": {
-        const win = RecentWindow.getMostRecentBrowserWindow();
+        const win = WindowTracker.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";
 
 this.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",
+  UITelemetry: "resource://gre/modules/UITelemetry.jsm",
+  CustomizableUI: "resource:///modules/CustomizableUI.jsm",
+  UITour: "resource:///modules/UITour.jsm",
+  WindowTracker: "resource:///modules/WindowTracker.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 @@ this.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 = WindowTracker.getMostRecentBrowserWindow({
         private: false,
         allowPopups: false,
       });
       // If there are no such windows, we're out of luck. :(
       this._firstWindowMeasurements = win ? this._getWindowMeasurements(win, rv)
                                           : {};
     });
   },
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -6,32 +6,26 @@
 
 this.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",
+  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",
+  WindowTracker: "resource:///modules/WindowTracker.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
@@ -835,17 +829,17 @@ this.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 = WindowTracker.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";
 
 this.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",
+  ExtensionData: "resource://gre/modules/Extension.jsm",
+  Services: "resource://gre/modules/Services.jsm",
+  WindowTracker: "resource:///modules/WindowTracker.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";
@@ -85,17 +81,17 @@ this.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 = WindowTracker.getMostRecentBrowserWindow();
       for (let addon of sideloaded) {
         win.openUILinkIn(`about:newaddon?id=${addon.id}`, "tab");
       }
     }
   },
 
   _updateNotifications() {
     if (this.sideloaded.size + this.updates.size == 0) {
--- a/browser/modules/Feeds.jsm
+++ b/browser/modules/Feeds.jsm
@@ -6,18 +6,18 @@
 
 this.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, "WindowTracker",
+                               "resource:///modules/WindowTracker.jsm");
 
 this.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 @@ this.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 = WindowTracker.getMostRecentBrowserWindow();
         topWindow.PlacesCommandHook.addLiveBookmark(data.spec, data.title, data.subtitle)
                                    .catch(Components.utils.reportError);
         break;
       }
     }
   },
 
   /**
deleted file mode 100644
--- a/browser/modules/UpdateTopLevelContentWindowIDHelper.jsm
+++ /dev/null
@@ -1,138 +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/. */
-
-/*
- * This module tracks each browser window and informs network module
- * the current selected tab's content outer window ID.
- */
-
-this.EXPORTED_SYMBOLS = ["trackBrowserWindow"];
-
-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");
-
-// Constants
-const TAB_EVENTS = ["TabBrowserInserted", "TabSelect"];
-const WINDOW_EVENTS = ["activate", "unload"];
-const DEBUG = false;
-
-// Variables
-var _lastFocusedWindow = null;
-var _lastTopLevelWindowID = 0;
-
-// Exported symbol
-this.trackBrowserWindow = function trackBrowserWindow(aWindow) {
-  WindowHelper.addWindow(aWindow);
-};
-
-// Global methods
-function debug(s) {
-  if (DEBUG) {
-    dump("-*- UpdateTopLevelContentWindowIDHelper: " + s + "\n");
-  }
-}
-
-function _updateCurrentContentOuterWindowID(aBrowser) {
-  if (!aBrowser.outerWindowID ||
-      aBrowser.outerWindowID === _lastTopLevelWindowID) {
-    return;
-  }
-
-  debug("Current window uri=" + aBrowser.currentURI.spec +
-        " id=" + aBrowser.outerWindowID);
-
-  _lastTopLevelWindowID = aBrowser.outerWindowID;
-  let windowIDWrapper = Components.classes["@mozilla.org/supports-PRUint64;1"]
-                          .createInstance(Ci.nsISupportsPRUint64);
-  windowIDWrapper.data = _lastTopLevelWindowID;
-  Services.obs.notifyObservers(windowIDWrapper,
-                               "net:current-toplevel-outer-content-windowid");
-}
-
-function _handleEvent(aEvent) {
-  switch (aEvent.type) {
-    case "TabBrowserInserted":
-      if (aEvent.target.ownerGlobal.gBrowser.selectedBrowser === aEvent.target.linkedBrowser) {
-        _updateCurrentContentOuterWindowID(aEvent.target.linkedBrowser);
-      }
-      break;
-    case "TabSelect":
-      _updateCurrentContentOuterWindowID(aEvent.target.linkedBrowser);
-      break;
-    case "activate":
-      WindowHelper.onActivate(aEvent.target);
-      break;
-    case "unload":
-      WindowHelper.removeWindow(aEvent.currentTarget);
-      break;
-  }
-}
-
-function _handleMessage(aMessage) {
-  let browser = aMessage.target;
-  if (aMessage.name === "Browser:Init" &&
-      browser === browser.ownerGlobal.gBrowser.selectedBrowser) {
-    _updateCurrentContentOuterWindowID(browser);
-  }
-}
-
-// Methods that impact a window. Put into single object for organization.
-var WindowHelper = {
-  addWindow: function NP_WH_addWindow(aWindow) {
-    // Add event listeners
-    TAB_EVENTS.forEach(function(event) {
-      aWindow.gBrowser.tabContainer.addEventListener(event, _handleEvent);
-    });
-    WINDOW_EVENTS.forEach(function(event) {
-      aWindow.addEventListener(event, _handleEvent);
-    });
-
-    let messageManager = aWindow.getGroupMessageManager("browsers");
-    messageManager.addMessageListener("Browser:Init", _handleMessage);
-
-    // This gets called AFTER activate event, so if this is the focused window
-    // we want to activate it.
-    if (aWindow == _focusManager.activeWindow)
-      this.handleFocusedWindow(aWindow);
-
-    // Update the selected tab's content outer window ID.
-    _updateCurrentContentOuterWindowID(aWindow.gBrowser.selectedBrowser);
-  },
-
-  removeWindow: function NP_WH_removeWindow(aWindow) {
-    if (aWindow == _lastFocusedWindow)
-      _lastFocusedWindow = null;
-
-    // Remove the event listeners
-    TAB_EVENTS.forEach(function(event) {
-      aWindow.gBrowser.tabContainer.removeEventListener(event, _handleEvent);
-    });
-    WINDOW_EVENTS.forEach(function(event) {
-      aWindow.removeEventListener(event, _handleEvent);
-    });
-
-    let messageManager = aWindow.getGroupMessageManager("browsers");
-    messageManager.removeMessageListener("Browser:Init", _handleMessage);
-  },
-
-  onActivate: function NP_WH_onActivate(aWindow, aHasFocus) {
-    // If this window was the last focused window, we don't need to do anything
-    if (aWindow == _lastFocusedWindow)
-      return;
-
-    this.handleFocusedWindow(aWindow);
-
-    _updateCurrentContentOuterWindowID(aWindow.gBrowser.selectedBrowser);
-  },
-
-  handleFocusedWindow: function NP_WH_handleFocusedWindow(aWindow) {
-    // aWindow is now focused
-    _lastFocusedWindow = aWindow;
-  },
-};
rename from browser/modules/RecentWindow.jsm
rename to browser/modules/WindowTracker.jsm
--- a/browser/modules/RecentWindow.jsm
+++ b/browser/modules/WindowTracker.jsm
@@ -1,31 +1,146 @@
 /* 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/. */
+ * 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.
+ */
+
+this.EXPORTED_SYMBOLS = ["WindowTracker"];
+
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-"use strict";
+// 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;
 
-this.EXPORTED_SYMBOLS = ["RecentWindow"];
+// Variables
+var _lastFocusedWindow = null;
+var _lastTopLevelWindowID = 0;
+
+// Global methods
+function debug(s) {
+  if (DEBUG) {
+    dump("-*- WindowTracker: " + s + "\n");
+  }
+}
 
-ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+function _updateCurrentContentOuterWindowID(aBrowser) {
+  if (!aBrowser.outerWindowID ||
+      aBrowser.outerWindowID === _lastTopLevelWindowID) {
+    return;
+  }
+
+  debug("Current window uri=" + aBrowser.currentURI.spec +
+        " id=" + aBrowser.outerWindowID);
+
+  _lastTopLevelWindowID = aBrowser.outerWindowID;
+  let windowIDWrapper = Components.classes["@mozilla.org/supports-PRUint64;1"]
+                          .createInstance(Ci.nsISupportsPRUint64);
+  windowIDWrapper.data = _lastTopLevelWindowID;
+  Services.obs.notifyObservers(windowIDWrapper,
+                               "net:current-toplevel-outer-content-windowid");
+}
 
-this.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) {
+function _handleEvent(aEvent) {
+  switch (aEvent.type) {
+    case "TabBrowserInserted":
+      if (aEvent.target.ownerGlobal.gBrowser.selectedBrowser === aEvent.target.linkedBrowser) {
+        _updateCurrentContentOuterWindowID(aEvent.target.linkedBrowser);
+      }
+      break;
+    case "TabSelect":
+      _updateCurrentContentOuterWindowID(aEvent.target.linkedBrowser);
+      break;
+    case "activate":
+      WindowHelper.onActivate(aEvent.target);
+      break;
+    case "unload":
+      WindowHelper.removeWindow(aEvent.currentTarget);
+      break;
+  }
+}
+
+function _handleMessage(aMessage) {
+  let browser = aMessage.target;
+  if (aMessage.name === "Browser:Init" &&
+      browser === browser.ownerGlobal.gBrowser.selectedBrowser) {
+    _updateCurrentContentOuterWindowID(browser);
+  }
+}
+
+// Methods that impact a window. Put into single object for organization.
+var WindowHelper = {
+  addWindow(aWindow) {
+    // Add event listeners
+    TAB_EVENTS.forEach(function(event) {
+      aWindow.gBrowser.tabContainer.addEventListener(event, _handleEvent);
+    });
+    WINDOW_EVENTS.forEach(function(event) {
+      aWindow.addEventListener(event, _handleEvent);
+    });
+
+    let messageManager = aWindow.getGroupMessageManager("browsers");
+    messageManager.addMessageListener("Browser:Init", _handleMessage);
+
+    // This gets called AFTER activate event, so if this is the focused window
+    // we want to activate it.
+    if (aWindow == _focusManager.activeWindow)
+      this.handleFocusedWindow(aWindow);
+
+    // Update the selected tab's content outer window ID.
+    _updateCurrentContentOuterWindowID(aWindow.gBrowser.selectedBrowser);
+  },
+
+  removeWindow(aWindow) {
+    if (aWindow == _lastFocusedWindow)
+      _lastFocusedWindow = null;
+
+    // Remove the event listeners
+    TAB_EVENTS.forEach(function(event) {
+      aWindow.gBrowser.tabContainer.removeEventListener(event, _handleEvent);
+    });
+    WINDOW_EVENTS.forEach(function(event) {
+      aWindow.removeEventListener(event, _handleEvent);
+    });
+
+    let messageManager = aWindow.getGroupMessageManager("browsers");
+    messageManager.removeMessageListener("Browser:Init", _handleMessage);
+  },
+
+  onActivate(aWindow, aHasFocus) {
+    // If this window was the last focused window, we don't need to do anything
+    if (aWindow == _lastFocusedWindow)
+      return;
+
+    this.handleFocusedWindow(aWindow);
+
+    _updateCurrentContentOuterWindowID(aWindow.gBrowser.selectedBrowser);
+  },
+
+  handleFocusedWindow(aWindow) {
+    // aWindow is now focused
+    _lastFocusedWindow = aWindow;
+  },
+
+  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) &&
@@ -58,8 +173,26 @@ this.RecentWindow = {
       let win = windowList.getNext();
       if (isSuitableBrowserWindow(win))
         return win;
     }
     return null;
   }
 };
 
+this.WindowTracker = {
+  /**
+   * 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/moz.build
+++ b/browser/modules/moz.build
@@ -101,22 +101,22 @@ 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("WindowTracker.jsm"):
+    BUG_COMPONENT = ("Core", "Networking")
+
 with Files("ZoomUI.jsm"):
     BUG_COMPONENT = ("Firefox", "Toolbars and Customization")
 
 
 BROWSER_CHROME_MANIFESTS += [
     'test/browser/browser.ini',
     'test/browser/formValidation/browser.ini',
 ]
@@ -145,25 +145,24 @@ EXTRA_JS_MODULES += [
     'LaterRun.jsm',
     'offlineAppCache.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',
+    'WindowTracker.jsm',
     'ZoomUI.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES += [
         'Windows8WindowFrameColor.jsm',
         'WindowsJumpLists.jsm',
         'WindowsPreviewPerTab.jsm',
--- 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 { WindowTracker } = ChromeUtils.import("resource:///modules/WindowTracker.jsm", {});
+      win = WindowTracker.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, "WindowTracker",
+                               "resource:///modules/WindowTracker.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 = WindowTracker.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(