Bug 1415913 - tabs.create without a windowId should target only normal windows, r?aswan draft
authorBob Silverberg <bsilverberg@mozilla.com>
Mon, 20 Nov 2017 17:00:20 -0500
changeset 705228 90c59dc8e4eb5ce149409c604843dab65375a871
parent 703662 cad9c9573579698c223b4b6cb53ca723cd930ad2
child 742308 9138961e634686d3c792a41b3bab62972fd65d9d
push id91413
push userbmo:bob.silverberg@gmail.com
push dateWed, 29 Nov 2017 18:01:25 +0000
reviewersaswan
bugs1415913, 1421709
milestone59.0a1
Bug 1415913 - tabs.create without a windowId should target only normal windows, r?aswan This introduces a new property to WindowTracker (on desktop only) called topNormalWindow which returns the topmost window which is not a popup window. That property is then used by tabs.create to identify the top "normal" window when a windowId is not passed to it. Bug 1421709 has been filed to look for other opportunities to use topNormalWindow in place of topWindow in our codebase. MozReview-Commit-ID: DcV93wO6FoV
browser/components/extensions/ext-browser.js
browser/components/extensions/ext-tabs.js
browser/components/extensions/test/browser/browser_ext_tabs_create.js
--- a/browser/components/extensions/ext-browser.js
+++ b/browser/components/extensions/ext-browser.js
@@ -9,16 +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 */
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
+                                  "resource:///modules/RecentWindow.jsm");
 
 var {
   ExtensionError,
   defineLazyGetter,
 } = ExtensionUtils;
 
 const READER_MODE_PREFIX = "about:reader";
 
@@ -168,16 +170,27 @@ global.TabContext = class extends EventE
 class WindowTracker extends WindowTrackerBase {
   addProgressListener(window, listener) {
     window.gBrowser.addTabsProgressListener(listener);
   }
 
   removeProgressListener(window, listener) {
     window.gBrowser.removeTabsProgressListener(listener);
   }
+
+  /**
+   * @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});
+  }
 }
 
 /**
  * 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/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -323,17 +323,17 @@ this.tabs = class extends ExtensionAPI {
             tabTracker.off("tab-isarticle", isArticleChangeListener);
           };
         }).api(),
 
         create(createProperties) {
           return new Promise((resolve, reject) => {
             let window = createProperties.windowId !== null ?
               windowTracker.getWindow(createProperties.windowId, context) :
-              windowTracker.topWindow;
+              windowTracker.topNormalWindow;
 
             if (!window.gBrowser) {
               let obs = (finishedWindow, topic, data) => {
                 if (finishedWindow != window) {
                   return;
                 }
                 Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
                 resolve(window);
--- a/browser/components/extensions/test/browser/browser_ext_tabs_create.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_create.js
@@ -220,8 +220,32 @@ add_task(async function test_urlbar_focu
   is(active.tagName, "html:input", "Input element focused");
   ok(active.classList.contains("urlbar-input"), "Urlbar focused");
 
   extension.sendMessage("remove", tab2.id);
   await extension.awaitMessage("result");
 
   await extension.unload();
 });
+
+add_task(async function test_create_with_popup() {
+  const extension = ExtensionTestUtils.loadExtension({
+    async background() {
+      let normalWin = await browser.windows.create();
+      let lastFocusedNormalWin = await browser.windows.getLastFocused({});
+      browser.test.assertEq(lastFocusedNormalWin.id, normalWin.id, "The normal window is the last focused window.");
+      let popupWin = await browser.windows.create({type: "popup"});
+      let lastFocusedPopupWin = await browser.windows.getLastFocused({});
+      browser.test.assertEq(lastFocusedPopupWin.id, popupWin.id, "The popup window is the last focused window.");
+      let newtab = await browser.tabs.create({});
+      browser.test.assertEq(normalWin.id, newtab.windowId, "New tab was created in last focused normal window.");
+      await Promise.all([
+        browser.windows.remove(normalWin.id),
+        browser.windows.remove(popupWin.id),
+      ]);
+      browser.test.sendMessage("complete");
+    },
+  });
+
+  await extension.startup();
+  await extension.awaitMessage("complete");
+  await extension.unload();
+});