Bug 1256282 - [webext] options_ui browsers should have access to the full WebExtension API. r=kmag
MozReview-Commit-ID: E1GwT0zfGie
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -63,8 +63,9 @@ tags = fullscreen
[browser_ext_windows_size.js]
skip-if = os == 'mac' # Fails when windows are randomly opened in fullscreen mode
[browser_ext_windows_update.js]
tags = fullscreen
[browser_ext_contentscript_connect.js]
[browser_ext_tab_runtimeConnect.js]
[browser_ext_topwindowid.js]
[browser_ext_webNavigation_getFrames.js]
+[browser_ext_optionsPage_privileges.js]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_optionsPage_privileges.js
@@ -0,0 +1,63 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(function* test_tab_options_privileges() {
+ function backgroundScript() {
+ browser.runtime.onMessage.addListener(({msgName, tabId}) => {
+ if (msgName == "removeTabId") {
+ browser.tabs.remove(tabId).then(() => {
+ browser.test.notifyPass("options-ui-privileges");
+ }).catch(error => {
+ browser.test.log(`Error: ${error} :: ${error.stack}`);
+ browser.test.notifyFail("options-ui-privileges");
+ });
+ }
+ });
+ browser.runtime.openOptionsPage();
+ }
+
+ function optionsScript() {
+ browser.tabs.query({url: "http://example.com/"}).then(tabs => {
+ browser.test.assertEq("http://example.com/", tabs[0].url, "Got the expect tab");
+ return browser.tabs.getCurrent();
+ }).then(tab => {
+ browser.runtime.sendMessage({msgName: "removeTabId", tabId: tab.id});
+ }).catch(error => {
+ browser.test.log(`Error: ${error} :: ${error.stack}`);
+ browser.test.notifyFail("options-ui-privileges");
+ });
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: true,
+
+ manifest: {
+ "permissions": ["tabs"],
+ "options_ui": {
+ "page": "options.html",
+ },
+ },
+ files: {
+ "options.html": `<!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset="utf-8">
+ <script src="options.js" type="text/javascript"></script>
+ </head>
+ </html>`,
+ "options.js": optionsScript,
+ },
+ background: backgroundScript,
+ });
+
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+
+ yield extension.startup();
+
+ yield extension.awaitFinish("options-ui-privileges");
+
+ yield extension.unload();
+
+ yield BrowserTestUtils.removeTab(tab);
+});
--- a/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage.js
+++ b/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage.js
@@ -92,19 +92,19 @@ add_task(function* test_inline_options()
return browser.runtime.openOptionsPage();
}).then(() => {
return browser.tabs.query({currentWindow: true, active: true});
}).then(([tab]) => {
browser.test.assertEq(optionsTab, tab.id, "Tab is the same as the previous options tab");
browser.test.assertEq("about:addons", tab.url, "Tab contains AddonManager");
browser.test.log("Ping options page.");
- return new Promise(resolve => browser.tabs.sendMessage(optionsTab, "ping", resolve));
- }).then(() => {
- browser.test.log("Got pong.");
+ return browser.runtime.sendMessage("ping");
+ }).then((pong) => {
+ browser.test.assertEq("pong", pong, "Got pong.");
browser.test.log("Remove options tab.");
return browser.tabs.remove(optionsTab);
}).then(() => {
browser.test.log("Open options page again. Expect fresh load.");
return Promise.all([
browser.runtime.openOptionsPage(),
awaitOptions(),
--- a/toolkit/components/extensions/ExtensionManagement.jsm
+++ b/toolkit/components/extensions/ExtensionManagement.jsm
@@ -231,27 +231,42 @@ function getAPILevelForWindow(window, ad
const {NO_PRIVILEGES, CONTENTSCRIPT_PRIVILEGES, FULL_PRIVILEGES} = API_LEVELS;
// Non WebExtension URLs and WebExtension URLs from a different extension
// has no access to APIs.
if (!addonId && getAddonIdForWindow(window) != addonId) {
return NO_PRIVILEGES;
}
- let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDocShell);
-
- // WebExtension URLs loaded into sub-frame UI have "content script API level privileges".
- // (see Bug 1214658 for rationale)
- if (docShell.sameTypeParent) {
+ // Extension pages running in the content process always defaults to
+ // "content script API level privileges".
+ if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
return CONTENTSCRIPT_PRIVILEGES;
}
- // Extension pages running in the content process defaults to "content script API level privileges".
- if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+ let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDocShell);
+
+ // Handling of ExtensionPages running inside sub-frames.
+ if (docShell.sameTypeParent) {
+ let parentWindow = docShell.sameTypeParent.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+
+ // The option page iframe embedded in the about:addons tab should have
+ // full API level privileges. (see Bug 1256282 for rationale)
+ let parentDocument = parentWindow.document;
+ let parentIsSystemPrincipal = Services.scriptSecurityManager
+ .isSystemPrincipal(parentDocument.nodePrincipal);
+ if (parentDocument.location.href == "about:addons" && parentIsSystemPrincipal) {
+ return FULL_PRIVILEGES;
+ }
+
+ // In all the other cases, WebExtension URLs loaded into sub-frame UI
+ // will have "content script API level privileges".
+ // (see Bug 1214658 for rationale)
return CONTENTSCRIPT_PRIVILEGES;
}
// WebExtension URLs loaded into top frames UI could have full API level privileges.
return FULL_PRIVILEGES;
}
this.ExtensionManagement = {