Bug 1324255 make webextension panels focused when opened, r?Gijs,rpl
MozReview-Commit-ID: LhZSm4U8XxJ
--- a/browser/components/extensions/ExtensionPopups.jsm
+++ b/browser/components/extensions/ExtensionPopups.jsm
@@ -75,16 +75,17 @@ class BasePopup {
extension.callOnClose(this);
this.contentReady = new Promise(resolve => {
this._resolveContentReady = resolve;
});
this.viewNode.addEventListener(this.DESTROY_EVENT, this);
+ this.panel.addEventListener("popuppositioned", this, true);
this.browser = null;
this.browserLoaded = new Promise((resolve, reject) => {
this.browserLoadedDeferred = {resolve, reject};
});
this.browserReady = this.createBrowser(viewNode, popupURL);
BasePopup.instances.get(this.window).set(extension, this);
@@ -121,16 +122,17 @@ class BasePopup {
this.viewNode.removeEventListener(this.DESTROY_EVENT, this);
delete this.viewNode.customRectGetter;
}
let {panel} = this;
if (panel) {
panel.style.removeProperty("--arrowpanel-background");
panel.removeAttribute("remote");
+ panel.removeEventListener("popuppositioned", this, true);
}
this.browser = null;
this.stack = null;
this.viewNode = null;
});
}
@@ -208,16 +210,26 @@ class BasePopup {
handleEvent(event) {
switch (event.type) {
case this.DESTROY_EVENT:
if (!this.destroyed) {
this.destroy();
}
break;
+ case "popuppositioned":
+ if (!this.destroyed) {
+ this.browserLoaded.then(() => {
+ if (this.destroyed) {
+ return;
+ }
+ this.browser.messageManager.sendAsyncMessage("Extension:GrabFocus", {});
+ });
+ }
+ break;
}
}
createBrowser(viewNode, popupURL = null) {
let document = viewNode.ownerDocument;
let stack = document.createElementNS(XUL_NS, "stack");
stack.setAttribute("class", "webextension-popup-stack");
@@ -435,16 +447,17 @@ class ViewPopup extends BasePopup {
* browser was destroyed before it was fully loaded, and the popup
* should be closed, or `true` otherwise.
*/
async attach(viewNode) {
this.viewNode = viewNode;
this.viewNode.addEventListener(this.DESTROY_EVENT, this);
this.viewNode.setAttribute("closemenu", "none");
+ this.panel.addEventListener("popuppositioned", this, true);
if (this.extension.remote) {
this.panel.setAttribute("remote", "true");
}
// Wait until the browser element is fully initialized, and give it at least
// a short grace period to finish loading its initial content, if necessary.
//
// In practice, the browser that was created by the mousdown handler should
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -108,16 +108,17 @@ skip-if = (os == 'win' && ccov) # Bug 14
[browser_ext_pageAction_popup_resize.js]
[browser_ext_pageAction_show_matches.js]
[browser_ext_pageAction_simple.js]
[browser_ext_pageAction_telemetry.js]
[browser_ext_pageAction_title.js]
[browser_ext_popup_api_injection.js]
[browser_ext_popup_background.js]
[browser_ext_popup_corners.js]
+[browser_ext_popup_focus.js]
[browser_ext_popup_sendMessage.js]
[browser_ext_popup_shutdown.js]
[browser_ext_runtime_openOptionsPage.js]
[browser_ext_runtime_openOptionsPage_uninstall.js]
[browser_ext_runtime_setUninstallURL.js]
[browser_ext_sessions_forgetClosedTab.js]
[browser_ext_sessions_forgetClosedWindow.js]
[browser_ext_sessions_getRecentlyClosed.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_popup_focus.js
@@ -0,0 +1,73 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+const DUMMY_PAGE = "http://example.com/browser/browser/components/extensions/test/browser/file_dummy.html";
+
+add_task(async function testPageActionFocus() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ "page_action": {
+ "default_popup": "popup.html",
+ "show_matches": ["<all_urls>"],
+ },
+ },
+ files: {
+ "popup.html": `<!DOCTYPE html><html><head><meta charset="utf-8">
+ <script src="popup.js"></script>
+ </head><body>
+ </body></html>
+ `,
+ "popup.js": function() {
+ window.addEventListener("focus", (event) => {
+ browser.test.assertEq(true, document.hasFocus(), "document should be focused");
+ browser.test.notifyPass("focused");
+ }, {once: true});
+ },
+ },
+ });
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, DUMMY_PAGE);
+
+ await extension.startup();
+ let finish = extension.awaitFinish("focused");
+ clickPageAction(extension);
+ await finish;
+
+ let panelId = `${makeWidgetId(extension.id)}-panel`;
+ let panel = document.getElementById(panelId);
+ panel.hidePopup();
+ await BrowserTestUtils.removeTab(tab);
+ await extension.unload();
+});
+
+add_task(async function testBrowserActionFocus() {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ "browser_action": {"default_popup": "popup.html"},
+ },
+ files: {
+ "popup.html": `<!DOCTYPE html><html><head><meta charset="utf-8">
+ <script src="popup.js"></script>
+ </head><body>
+ </body></html>
+ `,
+ "popup.js": function() {
+ window.addEventListener("focus", (event) => {
+ browser.test.assertEq(true, document.hasFocus(), "document should be focused");
+ browser.test.notifyPass("focused");
+ }, {once: true});
+ },
+ },
+ });
+ await extension.startup();
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, DUMMY_PAGE);
+ let finish = extension.awaitFinish("focused");
+ clickBrowserAction(extension);
+ await finish;
+
+ await closeBrowserAction(extension);
+
+ await BrowserTestUtils.removeTab(tab);
+ await extension.unload();
+});
--- a/toolkit/components/extensions/ext-browser-content.js
+++ b/toolkit/components/extensions/ext-browser-content.js
@@ -111,16 +111,20 @@ const BrowserListener = {
receiveMessage({name, data}) {
if (name === "Extension:InitBrowser") {
this.init(data);
} else if (name === "Extension:UnblockParser") {
if (this.unblockParser) {
this.unblockParser();
this.blockingPromise = null;
}
+ } else if (name === "Extension:GrabFocus") {
+ content.window.requestAnimationFrame(() => {
+ Services.focus.focusedWindow = content.window;
+ });
}
},
loadStylesheets() {
let winUtils = getWinUtils(content);
for (let url of this.stylesheets) {
winUtils.addSheet(ExtensionCommon.stylesheetMap.get(url), winUtils.AGENT_SHEET);
@@ -301,16 +305,17 @@ const BrowserListener = {
}
sendAsyncMessage("Extension:BrowserResized", result);
},
};
addMessageListener("Extension:InitBrowser", BrowserListener);
addMessageListener("Extension:UnblockParser", BrowserListener);
+addMessageListener("Extension:GrabFocus", BrowserListener);
var WebBrowserChrome = {
onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) {
// isAppTab is the value for the docShell that received the click. We're
// handling this in the top-level frame and want traversal behavior to
// match the value for this frame rather than any subframe, so we pass
// through the docShell.isAppTab value rather than what we were handed.
return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, docShell.isAppTab);