Bug 1352239 Don't prompt for optional permissions an extension already has draft
authorAndrew Swan <aswan@mozilla.com>
Sat, 01 Jul 2017 16:49:14 -0700
changeset 608734 4608337b62cea7a7fe9776ad1d1f439b688ae31e
parent 608658 67cd1ee26f2661fa5efe3d952485ab3c89af4271
child 637409 90908cf721474d8eafd45f973d233097473c362d
push id68397
push useraswan@mozilla.com
push dateFri, 14 Jul 2017 05:08:55 +0000
bugs1352239
milestone56.0a1
Bug 1352239 Don't prompt for optional permissions an extension already has MozReview-Commit-ID: EwyzfFB3LyS
toolkit/components/extensions/ext-permissions.js
toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
--- a/toolkit/components/extensions/ext-permissions.js
+++ b/toolkit/components/extensions/ext-permissions.js
@@ -31,16 +31,23 @@ this.permissions = class extends Extensi
           let optionalOrigins = context.extension.optionalOrigins;
           for (let origin of origins) {
             if (!optionalOrigins.subsumes(new MatchPattern(origin))) {
               throw new ExtensionError(`Cannot request origin permission for ${origin} since it was not declared in optional_permissions`);
             }
           }
 
           if (promptsEnabled) {
+            permissions = permissions.filter(perm => !context.extension.hasPermission(perm));
+            origins = origins.filter(origin => !context.extension.whiteListedHosts.subsumes(new MatchPattern(origin)));
+
+            if (permissions.length == 0 && origins.length == 0) {
+              return true;
+            }
+
             let browser = context.pendingEventBrowser || context.xulBrowser;
             let allow = await new Promise(resolve => {
               let subject = {
                 wrappedJSObject: {
                   browser,
                   name: context.extension.name,
                   icon: context.extension.iconURL,
                   permissions: {permissions, origins},
--- a/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
@@ -21,42 +21,46 @@ function findWinUtils(extension) {
       bgwin = view.contentWindow;
     }
   }
   notEqual(bgwin, null, "Found background window for the test extension");
   return bgwin.QueryInterface(Ci.nsIInterfaceRequestor)
               .getInterface(Ci.nsIDOMWindowUtils);
 }
 
+let sawPrompt = false;
+let acceptPrompt = false;
+const observer = {
+  observe(subject, topic, data) {
+    if (topic == "webextension-optional-permission-prompt") {
+      sawPrompt = true;
+      let {resolve} = subject.wrappedJSObject;
+      resolve(acceptPrompt);
+    }
+  },
+};
+
+add_task(function setup() {
+  Services.prefs.setBoolPref("extensions.webextOptionalPermissionPrompts", true);
+  Services.obs.addObserver(observer, "webextension-optional-permission-prompt");
+  do_register_cleanup(() => {
+    Services.obs.removeObserver(observer, "webextension-optional-permission-prompt");
+    Services.prefs.clearUserPref("extensions.webextOptionalPermissionPrompts");
+  });
+});
+
 add_task(async function test_permissions() {
   const REQUIRED_PERMISSIONS = ["downloads"];
   const REQUIRED_ORIGINS = ["*://site.com/", "*://*.domain.com/"];
   const REQUIRED_ORIGINS_NORMALIZED = ["*://site.com/*", "*://*.domain.com/*"];
 
   const OPTIONAL_PERMISSIONS = ["idle", "clipboardWrite"];
   const OPTIONAL_ORIGINS = ["http://optionalsite.com/", "https://*.optionaldomain.com/"];
   const OPTIONAL_ORIGINS_NORMALIZED = ["http://optionalsite.com/*", "https://*.optionaldomain.com/*"];
 
-  let acceptPrompt = false;
-  const observer = {
-    observe(subject, topic, data) {
-      if (topic == "webextension-optional-permission-prompt") {
-        let {resolve} = subject.wrappedJSObject;
-        resolve(acceptPrompt);
-      }
-    },
-  };
-
-  Services.prefs.setBoolPref("extensions.webextOptionalPermissionPrompts", true);
-  Services.obs.addObserver(observer, "webextension-optional-permission-prompt");
-  do_register_cleanup(() => {
-    Services.obs.removeObserver(observer, "webextension-optional-permission-prompt");
-    Services.prefs.clearUserPref("extensions.webextOptionalPermissionPrompts");
-  });
-
   await AddonTestUtils.promiseStartupManager();
 
   function background() {
     browser.test.onMessage.addListener(async (method, arg) => {
       if (method == "getAll") {
         let perms = await browser.permissions.getAll();
         let url = browser.extension.getURL("*");
         perms.origins = perms.origins.filter(i => i != url);
@@ -227,19 +231,17 @@ add_task(async function test_startup() {
     manifest: {optional_permissions: PERMS2.origins},
     useAddonManager: "permanent",
   });
 
   await extension1.startup();
   await extension2.startup();
 
   let perms = await extension1.awaitMessage("perms");
-  dump(`perms1 ${JSON.stringify(perms)}\n`);
   perms = await extension2.awaitMessage("perms");
-  dump(`perms2 ${JSON.stringify(perms)}\n`);
 
   let winUtils = findWinUtils(extension1);
   let handle = winUtils.setHandlingUserInput(true);
   extension1.sendMessage(PERMS1);
   await extension1.awaitMessage("requested");
   handle.destruct();
 
   winUtils = findWinUtils(extension2);
@@ -262,8 +264,103 @@ add_task(async function test_startup() {
   }
 
   await checkPermissions(extension1, PERMS1);
   await checkPermissions(extension2, PERMS2);
 
   await extension1.unload();
   await extension2.unload();
 });
+
+// Test that we don't prompt for permissions an extension already has.
+add_task(async function test_alreadyGranted() {
+  const REQUIRED_PERMISSIONS = [
+    "geolocation",
+    "*://required-host.com/",
+    "*://*.required-domain.com/",
+  ];
+  const OPTIONAL_PERMISSIONS = [
+    ...REQUIRED_PERMISSIONS,
+    "clipboardRead",
+    "*://optional-host.com/",
+    "*://*.optional-domain.com/",
+  ];
+
+  function pageScript() {
+    browser.test.onMessage.addListener(async (msg, arg) => {
+      if (msg == "request") {
+        let result = await browser.permissions.request(arg);
+        browser.test.sendMessage("request.result", result);
+      } else if (msg == "remove") {
+        let result = await browser.permissions.remove(arg);
+        browser.test.sendMessage("remove.result", result);
+      } else if (msg == "close") {
+        window.close();
+      }
+    });
+
+    browser.test.sendMessage("page-ready");
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    background() {
+      browser.test.sendMessage("ready", browser.runtime.getURL("page.html"));
+    },
+
+    manifest: {
+      permissions: REQUIRED_PERMISSIONS,
+      optional_permissions: OPTIONAL_PERMISSIONS,
+    },
+
+    files: {
+      "page.html": `<html><head>
+          <script src="page.js"><\/script>
+        </head></html>`,
+
+      "page.js": pageScript,
+    },
+  });
+
+  await extension.startup();
+
+  let winUtils = findWinUtils(extension);
+  let handle = winUtils.setHandlingUserInput(true);
+
+  let url = await extension.awaitMessage("ready");
+  await ExtensionTestUtils.loadContentPage(url);
+  await extension.awaitMessage("page-ready");
+
+  async function checkRequest(arg, expectPrompt, msg) {
+    sawPrompt = false;
+    extension.sendMessage("request", arg);
+    let result = await extension.awaitMessage("request.result");
+    ok(result, "request() call succeeded");
+    equal(sawPrompt, expectPrompt,
+          `Got ${expectPrompt ? "" : "no "}permission prompt for ${msg}`);
+  }
+
+  await checkRequest({permissions: ["geolocation"]}, false,
+                     "required permission from manifest");
+  await checkRequest({origins: ["http://required-host.com/"]}, false,
+                     "origin permission from manifest");
+  await checkRequest({origins: ["http://host.required-domain.com/"]}, false,
+                     "wildcard origin permission from manifest");
+
+  await checkRequest({permissions: ["clipboardRead"]}, true,
+                     "optional permission");
+  await checkRequest({permissions: ["clipboardRead"]}, false,
+                     "already granted optional permission");
+
+  await checkRequest({origins: ["http://optional-host.com/"]}, true,
+                     "optional origin");
+  await checkRequest({origins: ["http://optional-host.com/"]}, false,
+                     "already granted origin permission");
+
+  await checkRequest({origins: ["http://*.optional-domain.com/"]}, true,
+                     "optional wildcard origin");
+  await checkRequest({origins: ["http://*.optional-domain.com/"]}, false,
+                     "already granted optional wildcard origin");
+  await checkRequest({origins: ["http://host.optional-domain.com/"]}, false,
+                     "host matching optional wildcard origin");
+
+  handle.destruct();
+  await extension.unload();
+});