Bug 1509047 - Part 5: Add heuristics to the storage access API for automatically granting temporary session-scoped storage access without displaying a doorhanger prompt; r=johannh
authorEhsan Akhgari <ehsan@mozilla.com>
Sun, 25 Nov 2018 14:04:52 -0500
changeset 505113 cb7e6dc0469e54273fb84beb92ac20d4c7656b62
parent 505112 b51ddda4b7cac89989c74c9b93cf725a81077c7d
child 505114 e54c1b4fa9c653c03d90600d7dd7d04279c1f889
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh
bugs1509047
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 1509047 - Part 5: Add heuristics to the storage access API for automatically granting temporary session-scoped storage access without displaying a doorhanger prompt; r=johannh Differential Revision: https://phabricator.services.mozilla.com/D12866
browser/app/profile/firefox.js
browser/modules/PermissionUI.jsm
browser/modules/test/browser/browser_PermissionUI_prompts.js
toolkit/components/antitracking/test/browser/browser_storageAccessDoorHanger.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1528,16 +1528,19 @@ pref("network.cookie.cookieBehavior", 4 
 
 pref("browser.contentblocking.allowlist.storage.enabled", true);
 
 #ifdef NIGHTLY_BUILD
 // Enable the Storage Access API in Nightly
 pref("dom.storage_access.enabled", true);
 #endif
 
+pref("dom.storage_access.auto_grants", true);
+pref("dom.storage_access.max_concurrent_auto_grants", 5);
+
 // Define a set of default features for the Content Blocking UI.
 pref("browser.contentblocking.trackingprotection.control-center.ui.enabled", true);
 pref("browser.contentblocking.rejecttrackers.control-center.ui.enabled", true);
 
 // Enable the Report Breakage UI on Nightly and Beta but not on Release yet.
 #ifdef EARLY_BETA_OR_EARLIER
 pref("browser.contentblocking.reportBreakage.enabled", true);
 #else
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -63,16 +63,18 @@ var EXPORTED_SYMBOLS = [
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "SitePermissions",
   "resource:///modules/SitePermissions.jsm");
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "URICountListener",
+  "resource:///modules/BrowserUsageTelemetry.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
   return Services.strings
                  .createBundle("chrome://browser/locale/browser.properties");
 });
 
 var PermissionUI = {};
 
@@ -917,16 +919,21 @@ AutoplayPermissionPrompt.prototype = {
     return true;
   },
 };
 
 PermissionUI.AutoplayPermissionPrompt = AutoplayPermissionPrompt;
 
 function StorageAccessPermissionPrompt(request) {
   this.request = request;
+
+  XPCOMUtils.defineLazyPreferenceGetter(this, "_autoGrants",
+                                        "dom.storage_access.auto_grants");
+  XPCOMUtils.defineLazyPreferenceGetter(this, "_maxConcurrentAutoGrants",
+                                        "dom.storage_access.max_concurrent_auto_grants");
 }
 
 StorageAccessPermissionPrompt.prototype = {
   __proto__: PermissionPromptForRequestPrototype,
 
   get usePermissionManager() {
     return false;
   },
@@ -1007,11 +1014,43 @@ StorageAccessPermissionPrompt.prototype 
           self.allow({"storage-access": "allow-on-any-site"});
         },
     }];
   },
 
   get topLevelPrincipal() {
     return this.request.topLevelPrincipal;
   },
+
+  get maxConcurrentAutomaticGrants() {
+    // one percent of the number of top-levels origins visited in the current
+    // session (but not to exceed 24 hours), or the value of the
+    // dom.storage_access.max_concurrent_auto_grants preference, whichever is
+    // higher.
+    return Math.max(Math.max(Math.floor(URICountListener.uniqueOriginsVisitedInPast24Hours / 100),
+                             this._maxConcurrentAutoGrants), 0);
+  },
+
+  getOriginsThirdPartyHasAccessTo(thirdPartyOrigin) {
+    let prefix = `3rdPartyStorage^${thirdPartyOrigin}`;
+    let perms = Services.perms.getAllWithTypePrefix(prefix);
+    let origins = new Set();
+    while (perms.length) {
+      let perm = perms.shift();
+      origins.add(perm.principal.origin);
+    }
+    return origins.size;
+  },
+
+  onBeforeShow() {
+    let thirdPartyOrigin = this.request.principal.origin;
+    if (this._autoGrants &&
+        this.getOriginsThirdPartyHasAccessTo(thirdPartyOrigin) <
+          this.maxConcurrentAutomaticGrants) {
+      // Automatically accept the prompt
+      this.allow({"storage-access": "allow-auto-grant"});
+      return false;
+    }
+    return true;
+  },
 };
 
 PermissionUI.StorageAccessPermissionPrompt = StorageAccessPermissionPrompt;
--- a/browser/modules/test/browser/browser_PermissionUI_prompts.js
+++ b/browser/modules/test/browser/browser_PermissionUI_prompts.js
@@ -34,17 +34,19 @@ add_task(async function test_midi_permis
 add_task(async function test_autoplay_permission_prompt() {
   Services.prefs.setIntPref("media.autoplay.default", Ci.nsIAutoplay.PROMPT);
   await testPrompt(PermissionUI.AutoplayPermissionPrompt);
   Services.prefs.clearUserPref("media.autoplay.default");
 });
 
 // Tests that AutoplayPermissionPrompt works as expected
 add_task(async function test_storage_access_permission_prompt() {
+  Services.prefs.setBoolPref("dom.storage_access.auto_grants", false);
   await testPrompt(PermissionUI.StorageAccessPermissionPrompt);
+  Services.prefs.clearUserPref("dom.storage_access.auto_grants");
 });
 
 async function testPrompt(Prompt) {
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: "http://example.com",
   }, async function(browser) {
     let mockRequest = makeMockPermissionRequest(browser);
--- a/toolkit/components/antitracking/test/browser/browser_storageAccessDoorHanger.js
+++ b/toolkit/components/antitracking/test/browser/browser_storageAccessDoorHanger.js
@@ -7,16 +7,17 @@ Services.scriptloader.loadSubScript(CHRO
 async function testDoorHanger(choice) {
   info(`Running doorhanger test with choice #${choice}`);
 
   await SpecialPowers.flushPrefEnv();
   await SpecialPowers.pushPrefEnv({"set": [
     ["browser.contentblocking.allowlist.annotations.enabled", true],
     ["browser.contentblocking.allowlist.storage.enabled", true],
     [ContentBlocking.prefIntroCount, ContentBlocking.MAX_INTROS],
+    ["dom.storage_access.auto_grants", false],
     ["dom.storage_access.enabled", true],
     ["dom.storage_access.prompt.testing", false],
     ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
     ["privacy.trackingprotection.enabled", false],
     ["privacy.trackingprotection.pbmode.enabled", false],
     ["privacy.trackingprotection.annotate_channels", true],
     ["privacy.restrict3rdpartystorage.userInteractionRequiredForHosts", "tracking.example.com,tracking.example.org"],
   ]});