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
--- 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();
+});