Bug 1256282 - [webext] options_ui browsers should have access to the full WebExtension API. r=kmag draft
authorLuca Greco <lgreco@mozilla.com>
Fri, 08 Apr 2016 16:00:48 +0200
changeset 348928 180faf0ae642e7762cf4a1c29bcf2d8454de4b66
parent 348900 5ae810198a9c8c7f4e75cd0881bfc8d1295cbc8d
child 348929 68e3a345231abc66efad8d43851e44872306b0cb
push id14959
push userluca.greco@alcacoop.it
push dateFri, 08 Apr 2016 14:05:51 +0000
reviewerskmag
bugs1256282
milestone48.0a1
Bug 1256282 - [webext] options_ui browsers should have access to the full WebExtension API. r=kmag MozReview-Commit-ID: E1GwT0zfGie
browser/components/extensions/test/browser/browser.ini
browser/components/extensions/test/browser/browser_ext_optionsPage_privileges.js
browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage.js
toolkit/components/extensions/ExtensionManagement.jsm
--- 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 = {