Bug 1341126 implement open for browser/page/sidebar actions, r?kmag
MozReview-Commit-ID: 5r5aGpyPQ6W
--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/ext-browserAction.js
@@ -665,14 +665,19 @@ this.browserAction = class extends Exten
},
getBadgeBackgroundColor: function(details, callback) {
let tab = getTab(details.tabId);
let color = browserAction.getProperty(tab, "badgeBackgroundColor");
return Promise.resolve(color || [0xd9, 0, 0, 255]);
},
+
+ openPopup: function() {
+ let window = windowTracker.topWindow;
+ browserAction.triggerAction(window);
+ },
},
};
}
};
global.browserActionFor = this.browserAction.for;
--- a/browser/components/extensions/ext-pageAction.js
+++ b/browser/components/extensions/ext-pageAction.js
@@ -333,14 +333,19 @@ this.pageAction = class extends Extensio
},
getPopup(details) {
let tab = tabTracker.getTab(details.tabId);
let popup = pageAction.getProperty(tab, "popup");
return Promise.resolve(popup);
},
+
+ openPopup: function() {
+ let window = windowTracker.topWindow;
+ pageAction.triggerAction(window);
+ },
},
};
}
};
global.pageActionFor = this.pageAction.for;
--- a/browser/components/extensions/ext-sidebarAction.js
+++ b/browser/components/extensions/ext-sidebarAction.js
@@ -341,16 +341,40 @@ this.sidebarAction = class extends Exten
*/
triggerAction(window) {
let {SidebarUI} = window;
if (SidebarUI) {
SidebarUI.toggle(this.id);
}
}
+ /**
+ * Opens this sidebar action for the given window.
+ *
+ * @param {ChromeWindow} window
+ */
+ open(window) {
+ let {SidebarUI} = window;
+ if (SidebarUI) {
+ SidebarUI.show(this.id);
+ }
+ }
+
+ /**
+ * Closes this sidebar action for the given window if this sidebar action is open.
+ *
+ * @param {ChromeWindow} window
+ */
+ close(window) {
+ let {SidebarUI} = window;
+ if (SidebarUI.isOpen && this.id == SidebarUI.currentID) {
+ SidebarUI.hide();
+ }
+ }
+
getAPI(context) {
let {extension} = context;
const sidebarAction = this;
function getTab(tabId) {
if (tabId !== null) {
return tabTracker.getTab(tabId);
}
@@ -401,14 +425,24 @@ this.sidebarAction = class extends Exten
},
getPanel(details) {
let nativeTab = getTab(details.tabId);
let panel = sidebarAction.getProperty(nativeTab, "panel");
return Promise.resolve(panel);
},
+
+ open() {
+ let window = windowTracker.topWindow;
+ sidebarAction.open(window);
+ },
+
+ close() {
+ let window = windowTracker.topWindow;
+ sidebarAction.close(window);
+ },
},
};
}
};
global.sidebarActionFor = this.sidebarAction.for;
--- a/browser/components/extensions/schemas/browser_action.json
+++ b/browser/components/extensions/schemas/browser_action.json
@@ -401,34 +401,20 @@
"optional": true,
"parameters": []
}
]
},
{
"name": "openPopup",
"type": "function",
- "description": "Opens the extension popup window in the active window but does not grant tab permissions.",
- "unsupported": true,
- "async": "callback",
- "parameters": [
- {
- "type": "function",
- "name": "callback",
- "parameters": [
- {
- "name": "popupView",
- "type": "object",
- "optional": true,
- "description": "JavaScript 'window' object for the popup window if it was succesfully opened.",
- "additionalProperties": { "type": "any" }
- }
- ]
- }
- ]
+ "requireUserInput": true,
+ "description": "Opens the extension popup window in the active window.",
+ "async": true,
+ "parameters": []
}
],
"events": [
{
"name": "onClicked",
"type": "function",
"description": "Fired when a browser action icon is clicked. This event will not fire if the browser action has a popup.",
"parameters": [
--- a/browser/components/extensions/schemas/page_action.json
+++ b/browser/components/extensions/schemas/page_action.json
@@ -210,16 +210,24 @@
"parameters": [
{
"name": "result",
"type": "string"
}
]
}
]
+ },
+ {
+ "name": "openPopup",
+ "type": "function",
+ "requireUserInput": true,
+ "description": "Opens the extension page action in the active window.",
+ "async": true,
+ "parameters": []
}
],
"events": [
{
"name": "onClicked",
"type": "function",
"description": "Fired when a page action icon is clicked. This event will not fire if the page action has a popup.",
"parameters": [
--- a/browser/components/extensions/schemas/sidebar_action.json
+++ b/browser/components/extensions/schemas/sidebar_action.json
@@ -176,12 +176,28 @@
"tabId": {
"type": "integer",
"optional": true,
"description": "Specify the tab to get the sidebar from. If no tab is specified, the non-tab-specific sidebar is returned."
}
}
}
]
+ },
+ {
+ "name": "open",
+ "type": "function",
+ "requireUserInput": true,
+ "description": "Opens the extension sidebar in the active window.",
+ "async": true,
+ "parameters": []
+ },
+ {
+ "name": "close",
+ "type": "function",
+ "requireUserInput": true,
+ "description": "Closes the extension sidebar in the active window if the sidebar belongs to the extension.",
+ "async": true,
+ "parameters": []
}
]
}
]
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -78,16 +78,17 @@ skip-if = true # bug 1382487
[browser_ext_getViews.js]
[browser_ext_identity_indication.js]
[browser_ext_incognito_views.js]
[browser_ext_incognito_popup.js]
[browser_ext_lastError.js]
[browser_ext_menus.js]
[browser_ext_omnibox.js]
skip-if = debug || asan # Bug 1354681
+[browser_ext_openPanel.js]
[browser_ext_optionsPage_browser_style.js]
[browser_ext_optionsPage_privileges.js]
[browser_ext_pageAction_context.js]
[browser_ext_pageAction_contextMenu.js]
[browser_ext_pageAction_popup.js]
[browser_ext_pageAction_popup_resize.js]
[browser_ext_pageAction_simple.js]
[browser_ext_pageAction_telemetry.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_openPanel.js
@@ -0,0 +1,139 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(async function test_openPopup_requires_user_interaction() {
+ const {GlobalManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
+
+ async function backgroundScript() {
+ browser.tabs.onUpdated.addListener(async (tabId, changeInfo, tabInfo) => {
+ if (changeInfo.status != "complete") {
+ return;
+ }
+ await browser.pageAction.show(tabId);
+
+ await browser.test.assertRejects(
+ browser.pageAction.openPopup(),
+ "pageAction.openPopup may only be called from a user input handler",
+ "The error is informative.");
+ await browser.test.assertRejects(
+ browser.browserAction.openPopup(),
+ "browserAction.openPopup may only be called from a user input handler",
+ "The error is informative.");
+ await browser.test.assertRejects(
+ browser.sidebarAction.open(),
+ "sidebarAction.open may only be called from a user input handler",
+ "The error is informative.");
+ await browser.test.assertRejects(
+ browser.sidebarAction.close(),
+ "sidebarAction.close may only be called from a user input handler",
+ "The error is informative.");
+
+ browser.runtime.onMessage.addListener(async msg => {
+ browser.test.assertEq(msg, "from-panel", "correct message received");
+ browser.test.sendMessage("panel-opened");
+ });
+
+ browser.test.sendMessage("ready");
+ });
+ browser.tabs.create({url: "tab.html"});
+ }
+
+ let extensionData = {
+ background: backgroundScript,
+ manifest: {
+ "browser_action": {
+ "default_popup": "panel.html",
+ },
+ "page_action": {
+ "default_popup": "panel.html",
+ },
+ "sidebar_action": {
+ "default_panel": "panel.html",
+ },
+ },
+
+ files: {
+ "tab.html": `
+ <!DOCTYPE html>
+ <html><head><meta charset="utf-8"></head><body>
+ <button id="openBrowserAction">openBrowserAction</button>
+ <button id="openPageAction">openPageAction</button>
+ <button id="openSidebarAction">openSidebarAction</button>
+ <button id="closeSidebarAction">closeSidebarAction</button>
+ <script src="tab.js"></script>
+ </body></html>
+ `,
+ "panel.html": `
+ <!DOCTYPE html>
+ <html><head><meta charset="utf-8"></head><body>
+ <script src="panel.js"></script>
+ </body></html>
+ `,
+ "tab.js": function() {
+ document.getElementById("openBrowserAction").addEventListener("click", () => {
+ browser.browserAction.openPopup();
+ }, {once: true});
+ document.getElementById("openPageAction").addEventListener("click", () => {
+ browser.pageAction.openPopup();
+ }, {once: true});
+ document.getElementById("openSidebarAction").addEventListener("click", () => {
+ browser.sidebarAction.open();
+ }, {once: true});
+ document.getElementById("closeSidebarAction").addEventListener("click", () => {
+ browser.sidebarAction.close();
+ }, {once: true});
+ },
+ "panel.js": function() {
+ browser.runtime.sendMessage("from-panel");
+ },
+ },
+ };
+
+ let extension = ExtensionTestUtils.loadExtension(extensionData);
+
+ async function click(id) {
+ let open = extension.awaitMessage("panel-opened");
+ await BrowserTestUtils.synthesizeMouseAtCenter(id, {}, gBrowser.selectedBrowser);
+ return open;
+ }
+
+ function testActiveTab(extension, expected) {
+ let ext = GlobalManager.extensionMap.get(extension.id);
+ is(ext.tabManager.hasActiveTabPermission(gBrowser.selectedTab), expected,
+ "activeTab permission is correct");
+ }
+
+ await extension.startup();
+ await extension.awaitMessage("ready");
+
+ await click("#openBrowserAction");
+ testActiveTab(extension, false);
+ closeBrowserAction(extension);
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ await click("#openPageAction");
+ closePageAction(extension);
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ await click("#openSidebarAction");
+
+ await BrowserTestUtils.synthesizeMouseAtCenter("#closeSidebarAction", {}, gBrowser.selectedBrowser);
+ await BrowserTestUtils.waitForCondition(() => !SidebarUI.isOpen);
+
+ await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ await extension.unload();
+
+ extensionData.manifest.permissions = ["activeTab"];
+ extension = ExtensionTestUtils.loadExtension(extensionData);
+ await extension.startup();
+ await extension.awaitMessage("ready");
+
+ await click("#openBrowserAction");
+ testActiveTab(extension, true);
+ closeBrowserAction(extension);
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ await extension.unload();
+});