Bug 1508759 - Add support for hooking up permission prompts that don't use SitePermissions to TemporaryPermissions r=johannh
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 26 Nov 2018 10:32:19 +0000
changeset 507204 b1b57d2da9b146a2953afb27746515c36e862877
parent 507203 689677e786f3ed6c9c54094bc9e55ee482ba797e
child 507205 b15134c683ba5b2b05554ea6e4c358bd2108028d
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh
bugs1508759
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1508759 - Add support for hooking up permission prompts that don't use SitePermissions to TemporaryPermissions r=johannh Normally, permission prompts would define a permissionKey attribute in order to get integrated with SitePermissions. Since SitePermissions is internally hooked up to TemporaryPermissions, such permission prompts do not need any extra handling for taking benefit from the temporary permissions infrastructure. For the Storage Access API, however, we're not going to use SitePermissions, and instead Gecko will be in charge of defining the required permissions in the permission manager database when the prompt is responded to with an Allow action. This means that by default we won't be integrated with the temporary permissions setup either. This patch allows prompts to define a new way to opt out of reading and writing permissions through the permission manager but still being integrated with temporary permissions. That is, through returning false from the new usePermissionManager attribute and returning a name from the permissionKey attribute. TemporaryPermissions will do the expected work in order to ensure that each prompt with a unique key will be automatically blocked if a previous instance of the same prompt type with the same key has been blocked in the current tab. Note that this doesn't yet include support for showGloballyBlocked or permitTemporaryAllow since those features aren't needed for our use case. Differential Revision: https://phabricator.services.mozilla.com/D12466
browser/modules/PermissionUI.jsm
browser/modules/test/browser/browser_PermissionUI_prompts.js
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -121,29 +121,39 @@ var PermissionPromptPrototype = {
     }
 
     return this.principal.URI.hostPort;
   },
 
   /**
    * If the nsIPermissionManager is being queried and written
    * to for this permission request, set this to the key to be
-   * used. If this is undefined, user permissions will not be
-   * read from or written to.
+   * used. If this is undefined, no integration with temporary
+   * permissions infrastructure will be provided.
    *
    * Note that if a permission is set, in any follow-up
    * prompting within the expiry window of that permission,
    * the prompt will be skipped and the allow or deny choice
    * will be selected automatically.
    */
   get permissionKey() {
     return undefined;
   },
 
   /**
+   * If true, user permissions will be read from and written to.
+   * When this is false, we still provide integration with
+   * infrastructure such as temporary permissions. permissionKey should
+   * still return a valid name in those cases for that integration to work.
+   */
+  get usePermissionManager() {
+    return true;
+  },
+
+  /**
    * These are the options that will be passed to the
    * PopupNotification when it is shown. See the documentation
    * for PopupNotification for more details.
    *
    * Note that prompt() will automatically set displayURI to
    * be the URI of the requesting pricipal, unless the displayURI is exactly
    * set to false.
    */
@@ -266,17 +276,18 @@ var PermissionPromptPrototype = {
    */
   prompt() {
     // We ignore requests from non-nsIStandardURLs
     let requestingURI = this.principal.URI;
     if (!(requestingURI instanceof Ci.nsIStandardURL)) {
       return;
     }
 
-    if (this.permissionKey) {
+    if (this.usePermissionManager &&
+        this.permissionKey) {
       // If we're reading and setting permissions, then we need
       // to check to see if we already have a permission setting
       // for this particular principal.
       let {state} = SitePermissions.get(requestingURI,
                                         this.permissionKey,
                                         this.browser);
 
       if (state == SitePermissions.BLOCK) {
@@ -301,16 +312,29 @@ var PermissionPromptPrototype = {
         this.allow();
         return;
       }
 
       // 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"));
+    } else if (this.permissionKey) {
+      // If we're reading a permission which already has a temporary value,
+      // see if we can use the temporary value.
+      let {state} = SitePermissions.get(null,
+                                        this.permissionKey,
+                                        this.browser);
+
+      if (state == SitePermissions.BLOCK) {
+        // TODO: Add support for showGloballyBlocked
+
+        this.cancel();
+        return;
+      }
     }
 
     let chromeWin = this.browser.ownerGlobal;
     if (!chromeWin.PopupNotifications) {
       this.cancel();
       return;
     }
 
@@ -320,17 +344,18 @@ var PermissionPromptPrototype = {
       let action = {
         label: promptAction.label,
         accessKey: promptAction.accessKey,
         callback: state => {
           if (promptAction.callback) {
             promptAction.callback();
           }
 
-          if (this.permissionKey) {
+          if (this.usePermissionManager &&
+              this.permissionKey) {
             if ((state && state.checkboxChecked && state.source != "esc-press") ||
                 promptAction.scope == SitePermissions.SCOPE_PERSISTENT) {
               // Permanently store permission.
               let scope = SitePermissions.SCOPE_PERSISTENT;
               // Only remember permission for session if in PB mode.
               if (PrivateBrowsingUtils.isBrowserPrivate(this.browser)) {
                 scope = SitePermissions.SCOPE_SESSION;
               }
@@ -352,16 +377,28 @@ var PermissionPromptPrototype = {
             }
 
             // Grant permission if action is ALLOW.
             if (promptAction.action == SitePermissions.ALLOW) {
               this.allow();
             } else {
               this.cancel();
             }
+          } else if (this.permissionKey) {
+            // TODO: Add support for permitTemporaryAllow
+            if (promptAction.action == SitePermissions.BLOCK) {
+              // Temporarily store BLOCK permissions.
+              // We don't consider subframes when storing temporary
+              // permissions on a tab, thus storing ALLOW could be exploited.
+              SitePermissions.set(null,
+                                  this.permissionKey,
+                                  promptAction.action,
+                                  SitePermissions.SCOPE_TEMPORARY,
+                                  this.browser);
+            }
           }
         },
       };
       if (promptAction.dismiss) {
         action.dismiss = promptAction.dismiss;
       }
 
       popupNotificationActions.push(action);
--- a/browser/modules/test/browser/browser_PermissionUI_prompts.js
+++ b/browser/modules/test/browser/browser_PermissionUI_prompts.js
@@ -40,17 +40,18 @@ add_task(async function test_autoplay_pe
 async function testPrompt(Prompt) {
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: "http://example.com",
   }, async function(browser) {
     let mockRequest = makeMockPermissionRequest(browser);
     let principal = mockRequest.principal;
     let TestPrompt = new Prompt(mockRequest);
-    let permissionKey = TestPrompt.permissionKey;
+    let permissionKey = TestPrompt.usePermissionManager &&
+                        TestPrompt.permissionKey;
 
     registerCleanupFunction(function() {
       SitePermissions.remove(principal.URI, permissionKey);
     });
 
     let shownPromise =
       BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
     TestPrompt.prompt();