Bug 1203233 implement geolocation permission, r=aswan,florian
authorShane Caraveo <scaraveo@mozilla.com>
Sat, 04 Feb 2017 16:10:50 -0800
changeset 479642 22651b5d537d8f6d0c09a2148f4c1017ab0a368e
parent 479641 53023771039ea8c799ac1b5a382f987066aeebfe
child 479643 e54a95d4fec35114fa7d2b7bf3df84695c1732d2
push id44315
push usertnguyen@mozilla.com
push dateTue, 07 Feb 2017 02:03:39 +0000
reviewersaswan, florian
bugs1203233
milestone54.0a1
Bug 1203233 implement geolocation permission, r=aswan,florian MozReview-Commit-ID: 8V7bNyeGZNh
browser/locales/en-US/chrome/browser/browser.properties
browser/modules/PermissionUI.jsm
browser/modules/SitePermissions.jsm
toolkit/components/extensions/ext-geolocation.js
toolkit/components/extensions/extensions-toolkit.manifest
toolkit/components/extensions/jar.mn
toolkit/components/extensions/schemas/manifest.json
toolkit/components/extensions/test/mochitest/mochitest-common.ini
toolkit/components/extensions/test/mochitest/test_ext_geolocation.html
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -78,16 +78,17 @@ webextPerms.updateMenuItem=%S requires n
 # Note, this string will be used as raw markup. Avoid characters like <, >, &
 webextPerms.updateText=%S has been updated. You must approve new permissions before the updated version will install. Choosing “Cancel” will maintain your current add-on version.
 
 webextPerms.updateAccept.label=Update
 webextPerms.updateAccept.accessKey=U
 
 webextPerms.description.bookmarks=Read and modify bookmarks
 webextPerms.description.downloads=Download files and read and modify the browser’s download history
+webextPerms.description.geolocation=Access your location
 webextPerms.description.history=Access browsing history
 # LOCALIZATION NOTE (webextPerms.description.nativeMessaging)
 # %S will be replaced with the name of the application
 webextPerms.description.nativeMessaging=Exchange messages with programs other than %S
 webextPerms.description.notifications=Display notifications to you
 webextPerms.description.sessions=Access recently closed tabs
 webextPerms.description.tabs=Access browser tabs
 webextPerms.description.topSites=Access browsing history
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -241,26 +241,21 @@ this.PermissionPromptPrototype = {
   /**
    * Will determine if a prompt should be shown to the user, and if so,
    * will show it.
    *
    * If a permissionKey is defined prompt() might automatically
    * allow or cancel itself based on the user's current
    * permission settings without displaying the prompt.
    *
-   * If the <xul:browser> that the request is associated with
-   * does not belong to a browser window with the PopupNotifications
-   * global set, the prompt request is ignored.
+   * If the permission is not already set and the <xul:browser> that the request
+   * is associated with does not belong to a browser window with the
+   * PopupNotifications global set, the prompt request is ignored.
    */
   prompt() {
-    let chromeWin = this.browser.ownerGlobal;
-    if (!chromeWin.PopupNotifications) {
-      return;
-    }
-
     // We ignore requests from non-nsIStandardURLs
     let requestingURI = this.principal.URI;
     if (!(requestingURI instanceof Ci.nsIStandardURL)) {
       return;
     }
 
     if (this.permissionKey) {
       // If we're reading and setting permissions, then we need
@@ -281,16 +276,22 @@ this.PermissionPromptPrototype = {
       }
 
       // Tell the browser to refresh the identity block display in case there
       // are expired permission states.
       this.browser.dispatchEvent(new this.browser.ownerGlobal
                                          .CustomEvent("PermissionStateChange"));
     }
 
+    let chromeWin = this.browser.ownerGlobal;
+    if (!chromeWin.PopupNotifications) {
+      this.cancel();
+      return;
+    }
+
     // Transform the PermissionPrompt actions into PopupNotification actions.
     let popupNotificationActions = [];
     for (let promptAction of this.promptActions) {
       let action = {
         label: promptAction.label,
         accessKey: promptAction.accessKey,
         callback: state => {
           if (promptAction.callback) {
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -247,17 +247,17 @@ this.SitePermissions = {
    * even though nsIPermissionManager can still handle them.
    *
    * @param {nsIURI} uri
    *        The URI to check.
    *
    * @return {boolean} if the URI is supported.
    */
   isSupportedURI(uri) {
-    return uri && (uri.schemeIs("http") || uri.schemeIs("https"));
+    return uri && ["http", "https", "moz-extension"].includes(uri.scheme);
   },
 
   /**
    * Gets an array of all permission IDs.
    *
    * @return {Array<String>} an array of all permission IDs.
    */
   listPermissions() {
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/ext-geolocation.js
@@ -0,0 +1,28 @@
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+                                  "resource://gre/modules/Services.jsm");
+
+// If the user has changed the permission on the addon to something other than
+// always allow, then we want to preserve that choice.  We only set the
+// permission if it is not set (unknown_action), and we only remove the
+// permission on shutdown if it is always allow.
+
+/* eslint-disable mozilla/balanced-listeners */
+extensions.on("startup", (type, extension) => {
+  if (extension.hasPermission("geolocation") &&
+      Services.perms.testPermission(extension.principal.URI, "geo") == Services.perms.UNKNOWN_ACTION) {
+    Services.perms.add(extension.principal.URI, "geo",
+                       Services.perms.ALLOW_ACTION,
+                       Services.perms.EXPIRE_SESSION);
+  }
+});
+
+extensions.on("shutdown", (type, extension) => {
+  if (extension.hasPermission("geolocation") &&
+      Services.perms.testPermission(extension.principal.URI, "geo") == Services.perms.ALLOW_ACTION) {
+    Services.perms.remove(extension.principal.URI, "geo");
+  }
+});
--- a/toolkit/components/extensions/extensions-toolkit.manifest
+++ b/toolkit/components/extensions/extensions-toolkit.manifest
@@ -1,14 +1,15 @@
 # scripts
 category webextension-scripts alarms chrome://extensions/content/ext-alarms.js
 category webextension-scripts backgroundPage chrome://extensions/content/ext-backgroundPage.js
 category webextension-scripts contextualIdentities chrome://extensions/content/ext-contextualIdentities.js
 category webextension-scripts cookies chrome://extensions/content/ext-cookies.js
 category webextension-scripts downloads chrome://extensions/content/ext-downloads.js
+category webextension-scripts geolocation chrome://extensions/content/ext-geolocation.js
 category webextension-scripts management chrome://extensions/content/ext-management.js
 category webextension-scripts notifications chrome://extensions/content/ext-notifications.js
 category webextension-scripts i18n chrome://extensions/content/ext-i18n.js
 category webextension-scripts idle chrome://extensions/content/ext-idle.js
 category webextension-scripts webRequest chrome://extensions/content/ext-webRequest.js
 category webextension-scripts webNavigation chrome://extensions/content/ext-webNavigation.js
 category webextension-scripts runtime chrome://extensions/content/ext-runtime.js
 category webextension-scripts extension chrome://extensions/content/ext-extension.js
--- a/toolkit/components/extensions/jar.mn
+++ b/toolkit/components/extensions/jar.mn
@@ -5,16 +5,17 @@
 toolkit.jar:
 % content extensions %content/extensions/
     content/extensions/ext-alarms.js
     content/extensions/ext-backgroundPage.js
     content/extensions/ext-browser-content.js
     content/extensions/ext-contextualIdentities.js
     content/extensions/ext-cookies.js
     content/extensions/ext-downloads.js
+    content/extensions/ext-geolocation.js
     content/extensions/ext-management.js
     content/extensions/ext-notifications.js
     content/extensions/ext-i18n.js
     content/extensions/ext-idle.js
     content/extensions/ext-webRequest.js
     content/extensions/ext-webNavigation.js
     content/extensions/ext-runtime.js
     content/extensions/ext-extension.js
--- a/toolkit/components/extensions/schemas/manifest.json
+++ b/toolkit/components/extensions/schemas/manifest.json
@@ -207,16 +207,17 @@
       {
         "id": "Permission",
         "choices": [
           {
             "type": "string",
             "enum": [
               "alarms",
               "clipboardWrite",
+              "geolocation",
               "idle",
               "notifications",
               "storage"
             ]
           },
           { "$ref": "MatchPattern" }
         ]
       },
--- a/toolkit/components/extensions/test/mochitest/mochitest-common.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest-common.ini
@@ -35,16 +35,17 @@ support-files =
   redirection.sjs
   file_privilege_escalation.html
   file_ext_test_api_injection.js
   file_permission_xhr.html
   file_teardown_test.js
   return_headers.sjs
   webrequest_worker.js
   !/toolkit/components/passwordmgr/test/authenticate.sjs
+  !/dom/tests/mochitest/geolocation/network_geolocation.sjs
 
 [test_clipboard.html]
 # skip-if = # disabled test case with_permission_allow_copy, see inline comment.
 [test_ext_inIncognitoContext_window.html]
 skip-if = os == 'android' # Android does not support multiple windows.
 [test_ext_geturl.html]
 [test_ext_background_canvas.html]
 [test_ext_content_security_policy.html]
@@ -59,16 +60,18 @@ skip-if = os == 'android' # Android does
 skip-if = os == 'android' # Android does not support multiple windows.
 [test_ext_contentscript_css.html]
 [test_ext_contentscript_about_blank.html]
 [test_ext_contentscript_permission.html]
 [test_ext_contentscript_teardown.html]
 [test_ext_exclude_include_globs.html]
 [test_ext_i18n_css.html]
 [test_ext_generate.html]
+[test_ext_geolocation.html]
+skip-if = os == 'android' # Android support Bug 1336194
 [test_ext_notifications.html]
 [test_ext_permission_xhr.html]
 [test_ext_runtime_connect.html]
 [test_ext_runtime_connect_twoway.html]
 [test_ext_runtime_connect2.html]
 [test_ext_runtime_disconnect.html]
 [test_ext_runtime_id.html]
 [test_ext_sandbox_var.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_geolocation.html
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+
+<html>
+<head>
+<meta charset="utf-8">
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <script type="text/javascript" src="head.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<script>
+"use strict";
+
+add_task(function* test_geolocation_nopermission() {
+  let GEO_URL = "http://mochi.test:8888/tests/dom/tests/mochitest/geolocation/network_geolocation.sjs";
+  yield SpecialPowers.pushPrefEnv({"set": [["geo.wifi.uri", GEO_URL]]});
+});
+
+add_task(function* test_geolocation() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: [
+        "geolocation",
+      ],
+    },
+    background() {
+      navigator.geolocation.getCurrentPosition(() => {
+        browser.test.notifyPass("success geolocation call");
+      }, (error) => {
+        browser.test.notifyFail(`geolocation call ${error}`);
+      });
+    },
+  });
+
+  yield extension.startup();
+  yield extension.awaitFinish();
+  yield extension.unload();
+});
+
+add_task(function* test_geolocation_nopermission() {
+  let extension = ExtensionTestUtils.loadExtension({
+    background() {
+      navigator.geolocation.getCurrentPosition(() => {
+        browser.test.notifyFail("success geolocation call");
+      }, (error) => {
+        browser.test.notifyPass(`geolocation call ${error}`);
+      });
+    },
+  });
+
+  yield extension.startup();
+  yield extension.awaitFinish();
+  yield extension.unload();
+});
+
+add_task(function* test_geolocation_prompt() {
+  let extension = ExtensionTestUtils.loadExtension({
+    background() {
+      browser.tabs.create({url: "tab.html"});
+    },
+    files: {
+      "tab.html": `<html><head>
+        <meta charset="utf-8">
+        <script src="tab.js"><\/script>
+      </head></html>`,
+      "tab.js": () => {
+        navigator.geolocation.getCurrentPosition(() => {
+          browser.test.notifyPass("success geolocation call");
+        }, (error) => {
+          browser.test.notifyFail(`geolocation call ${error}`);
+        });
+      },
+    },
+  });
+
+  // Bypass the actual prompt, but the prompt result is to allow access.
+  yield SpecialPowers.pushPrefEnv({"set": [["geo.prompt.testing", true], ["geo.prompt.testing.allow", true]]});
+  yield extension.startup();
+  yield extension.awaitFinish();
+  yield extension.unload();
+});
+</script>
+</head>
+<body>
+
+</body>
+</html>