Bug 1483378 - Part 1: Update the Cookies and Site Data UI in the Preferences window; r=johannh,flod
authorEhsan Akhgari <ehsan@mozilla.com>
Fri, 17 Aug 2018 11:14:18 -0400
changeset 487518 7784d3275ff735261a8953fc17779223f476023b
parent 487517 89e493ded9845fa3c95fad2e45f6fd17eba165d2
child 487519 edc4aecf816ee887a1607276aecf3dfda5688334
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh, flod
bugs1483378
milestone63.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 1483378 - Part 1: Update the Cookies and Site Data UI in the Preferences window; r=johannh,flod
browser/app/profile/firefox.js
browser/components/preferences/in-content/privacy.js
browser/components/preferences/in-content/privacy.xul
browser/locales/en-US/browser/preferences/preferences.ftl
browser/themes/shared/incontentprefs/preferences.inc.css
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1483,20 +1483,23 @@ pref("browser.ping-centre.telemetry", tr
 pref("browser.ping-centre.log", false);
 pref("browser.ping-centre.staging.endpoint", "https://onyx_tiles.stage.mozaws.net/v3/links/ping-centre");
 pref("browser.ping-centre.production.endpoint", "https://tiles.services.mozilla.com/v3/links/ping-centre");
 
 // Enable GMP support in the addon manager.
 pref("media.gmp-provider.enabled", true);
 
 pref("browser.contentblocking.enabled", true);
+pref("browser.contentblocking.cookies-site-data.ui.reject-trackers.recommended", true);
 #ifdef NIGHTLY_BUILD
 pref("browser.contentblocking.ui.enabled", true);
+pref("browser.contentblocking.cookies-site-data.ui.enabled", true);
 #else
 pref("browser.contentblocking.ui.enabled", false);
+pref("browser.contentblocking.cookies-site-data.ui.enabled", false);
 #endif
 #ifdef NIGHTLY_BUILD
 pref("browser.contentblocking.reportBreakage.enabled", true);
 #else
 pref("browser.contentblocking.reportBreakage.enabled", false);
 #endif
 pref("browser.contentblocking.reportBreakage.url", "https://tracking-protection-issues.herokuapp.com/new");
 
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -18,16 +18,22 @@ ChromeUtils.defineModuleGetter(this, "Lo
 ChromeUtils.defineModuleGetter(this, "SiteDataManager",
   "resource:///modules/SiteDataManager.jsm");
 
 ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingUiEnabled",
                                       "browser.contentblocking.ui.enabled");
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingCookiesAndSiteDataUiEnabled",
+                                      "browser.contentblocking.cookies-site-data.ui.enabled");
+
+XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingCookiesAndSiteDataRejectTrackersRecommended",
+                                      "browser.contentblocking.cookies-site-data.ui.reject-trackers.recommended");
+
 XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingEnabled",
                                       "browser.contentblocking.enabled");
 
 const PREF_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
 
 const TRACKING_PROTECTION_KEY = "websites.trackingProtectionMode";
 const TRACKING_PROTECTION_PREFS = ["privacy.trackingprotection.enabled",
                                    "privacy.trackingprotection.pbmode.enabled"];
@@ -262,25 +268,34 @@ var gPrivacyPane = {
 
     if (contentBlockingUiEnabled) {
       this.initContentBlocking();
     } else {
       this._initTrackingProtection();
     }
 
     this.trackingProtectionReadPrefs();
+    this.networkCookieBehaviorReadPrefs();
     this._initTrackingProtectionExtensionControl();
 
     this.updateContentBlockingVisibility();
 
     Preferences.get("privacy.trackingprotection.enabled").on("change",
       gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane));
     Preferences.get("privacy.trackingprotection.pbmode.enabled").on("change",
       gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane));
 
+    // Watch all of the prefs that the new Cookies & Site Data UI depends on
+    Preferences.get("network.cookie.cookieBehavior").on("change",
+      gPrivacyPane.networkCookieBehaviorReadPrefs.bind(gPrivacyPane));
+    Preferences.get("network.cookie.lifetimePolicy").on("change",
+      gPrivacyPane.networkCookieBehaviorReadPrefs.bind(gPrivacyPane));
+    Preferences.get("browser.privatebrowsing.autostart").on("change",
+      gPrivacyPane.networkCookieBehaviorReadPrefs.bind(gPrivacyPane));
+
     setEventListener("trackingProtectionExceptions", "command",
       gPrivacyPane.showTrackingProtectionExceptions);
 
     Preferences.get("privacy.sanitize.sanitizeOnShutdown").on("change",
       gPrivacyPane._updateSanitizeSettingsButton.bind(gPrivacyPane));
     Preferences.get("browser.privatebrowsing.autostart").on("change",
       gPrivacyPane.updatePrivacyMicroControls.bind(gPrivacyPane));
     Preferences.get("media.autoplay.enabled.ask-permission").on("change",
@@ -471,31 +486,57 @@ var gPrivacyPane = {
     }
   },
 
   /**
    * Changes the visibility of elements in the TP/CB section depending on the
    * content blocking UI pref.
    */
   updateContentBlockingVisibility() {
+    // First, update the content blocking UI.
     let visibleState = {
       "contentBlockingHeader": true,
       "contentBlockingDescription": true,
       "contentBlockingLearnMore": true,
       "contentBlockingRestoreDefaults": true,
       "contentBlockingCheckboxContainer": true,
       "contentBlockingCategories": true,
 
       "trackingProtectionHeader": false,
       "trackingProtectionDescription": false,
       "trackingProtectionBox": false,
     };
     for (let id in visibleState) {
       document.getElementById(id).hidden = contentBlockingUiEnabled != visibleState[id];
     }
+
+    // Now, update the cookies & site data UI.
+    visibleState = {
+      "acceptCookies": false,
+      "blockCookies": true,
+    };
+    for (let id in visibleState) {
+      document.getElementById(id).hidden = contentBlockingCookiesAndSiteDataUiEnabled != visibleState[id];
+    }
+    if (contentBlockingCookiesAndSiteDataUiEnabled) {
+      // The Keep Until row doesn't change in the new UI, so we just transplant it
+      // from the old UI by moving it in the DOM!
+      let keepUntil = document.getElementById("keepRow");
+      document.getElementById("acceptCookies").removeChild(keepUntil);
+      let blockThirdPartyRow = document.getElementById("blockThirdPartyRow");
+      document.getElementById("blockCookies").appendChild(keepUntil);
+      keepUntil.classList.remove("indent"); // drop the indentation
+      keepUntil.setAttribute("style", "margin-top: 1em"); // apply a margin
+
+      // Allow turning off the "(recommended)" label using a pref
+      let blockCookiesFromTrackers = document.getElementById("blockCookiesFromTrackers");
+      if (contentBlockingCookiesAndSiteDataRejectTrackersRecommended) {
+        document.l10n.setAttributes(blockCookiesFromTrackers, "sitedata-block-trackers-option-recommended");
+      }
+    }
   },
 
   /**
    * Updates the preferences UI to reflect the browser.contentblocking.enabled pref.
    * This affects the button to toggle the pref and the disabled state of the dependent controls.
    */
   updateContentBlockingToggle() {
     let onOrOff = contentBlockingEnabled ? "on" : "off";
@@ -562,16 +603,61 @@ var gPrivacyPane = {
     } else if (pbmPref.value) {
       tpControl.value = "private";
     } else {
       tpControl.value = "never";
     }
   },
 
   /**
+   * Selects the right items of the new Cookies & Site Data UI.
+   */
+  networkCookieBehaviorReadPrefs() {
+    let behavior = Preferences.get("network.cookie.cookieBehavior").value;
+    let blockCookiesCtrl = document.getElementById("blockCookies");
+    let blockCookiesLabel = document.getElementById("blockCookiesLabel");
+    let blockCookiesMenu = document.getElementById("blockCookiesMenu");
+    let keepUntilLabel = document.getElementById("keepUntil");
+    let keepUntilMenu = document.getElementById("keepCookiesUntil");
+
+    let blockCookies = (behavior != 0);
+    let cookieBehaviorLocked = Services.prefs.prefIsLocked("network.cookie.cookieBehavior");
+    let blockCookiesControlsDisabled = !blockCookies || cookieBehaviorLocked;
+    blockCookiesLabel.disabled = blockCookiesMenu.disabled = blockCookiesControlsDisabled;
+
+    let completelyBlockCookies = (behavior == 2);
+    let privateBrowsing = Preferences.get("browser.privatebrowsing.autostart").value;
+    let cookieExpirationLocked = Services.prefs.prefIsLocked("network.cookie.lifetimePolicy");
+    let keepUntilControlsDisabled = privateBrowsing || completelyBlockCookies || cookieExpirationLocked;
+    keepUntilLabel.disabled = keepUntilMenu.disabled = keepUntilControlsDisabled;
+
+    switch (behavior) {
+      case Ci.nsICookieService.BEHAVIOR_ACCEPT:
+        blockCookiesCtrl.value = "allow";
+        break;
+      case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
+        blockCookiesCtrl.value = "disallow";
+        blockCookiesMenu.value = "all-third-parties";
+        break;
+      case Ci.nsICookieService.BEHAVIOR_REJECT:
+        blockCookiesCtrl.value = "disallow";
+        blockCookiesMenu.value = "always";
+        break;
+      case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
+        blockCookiesCtrl.value = "disallow";
+        blockCookiesMenu.value = "unvisited";
+        break;
+      case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
+        blockCookiesCtrl.value = "disallow";
+        blockCookiesMenu.value = "trackers";
+        break;
+    }
+  },
+
+  /**
    * Sets the pref values based on the selected item of the radiogroup.
    */
   trackingProtectionWritePrefs() {
     let enabledPref = Preferences.get("privacy.trackingprotection.enabled");
     let pbmPref = Preferences.get("privacy.trackingprotection.pbmode.enabled");
     let tpControl;
     if (contentBlockingUiEnabled) {
       tpControl = document.getElementById("trackingProtectionMenu");
@@ -969,38 +1055,107 @@ var gPrivacyPane = {
   },
 
   /**
    * Converts between network.cookie.cookieBehavior and the third-party cookie UI
    */
   readAcceptThirdPartyCookies() {
     var pref = Preferences.get("network.cookie.cookieBehavior");
     switch (pref.value) {
-      case 0:
+      case Ci.nsICookieService.BEHAVIOR_ACCEPT:
         return "always";
-      case 1:
+      case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
         return "never";
-      case 2:
+      case Ci.nsICookieService.BEHAVIOR_REJECT:
         return "never";
-      case 3:
+      case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
         return "visited";
       default:
         return undefined;
     }
   },
 
   writeAcceptThirdPartyCookies() {
     var accept = document.getElementById("acceptThirdPartyMenu").selectedItem;
     switch (accept.value) {
       case "always":
-        return 0;
+        return Ci.nsICookieService.BEHAVIOR_ACCEPT;
       case "visited":
-        return 3;
+        return Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN;
       case "never":
-        return 1;
+        return Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
+      default:
+        return undefined;
+    }
+  },
+
+  /**
+   * Reads the network.cookie.cookieBehavior preference value and
+   * enables/disables the rest of the new cookie & site data UI accordingly.
+   *
+   * Returns "allow" if cookies are accepted and "disallow" if they are entirely
+   * disabled.
+   */
+  readBlockCookies() {
+    // enable the rest of the UI for anything other than "accept all cookies"
+    let pref = Preferences.get("network.cookie.cookieBehavior");
+    let blockCookies = (pref.value != 0);
+
+    // Our top-level setting is a radiogroup that only sets "enable all"
+    // and "disable all", so convert the pref value accordingly.
+    return blockCookies ? "disallow" : "allow";
+  },
+
+  /**
+   * Updates the "accept third party cookies" menu based on whether the
+   * "accept cookies" or "block cookies" radio buttons are selected.
+   */
+  writeBlockCookies() {
+    let block = document.getElementById("blockCookies");
+    let blockCookiesMenu = document.getElementById("blockCookiesMenu");
+
+    // if we're disabling cookies, automatically select 'third-party trackers'
+    if (block.value == "disallow") {
+      blockCookiesMenu.selectedIndex = 0;
+      return this.writeBlockCookiesFrom();
+    }
+
+    return Ci.nsICookieService.BEHAVIOR_ACCEPT;
+  },
+
+  /**
+   * Converts between network.cookie.cookieBehavior and the new third-party cookies UI
+   */
+  readBlockCookiesFrom() {
+    let pref = Preferences.get("network.cookie.cookieBehavior");
+    switch (pref.value) {
+      case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
+        return "all-third-parties";
+      case Ci.nsICookieService.BEHAVIOR_REJECT:
+        return "always";
+      case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
+        return "unvisited";
+      case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
+        return "trackers";
+      default:
+        return undefined;
+    }
+  },
+
+  writeBlockCookiesFrom() {
+    let block = document.getElementById("blockCookiesMenu").selectedItem;
+    switch (block.value) {
+      case "trackers":
+        return Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
+      case "unvisited":
+        return Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN;
+      case "always":
+        return Ci.nsICookieService.BEHAVIOR_REJECT;
+      case "all-third-parties":
+        return Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
       default:
         return undefined;
     }
   },
 
   /**
    * Displays fine-grained, per-site preferences for cookies.
    */
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -216,16 +216,46 @@
               </menupopup>
             </menulist>
           </hbox>
         </hbox>
         <radio data-l10n-id="sitedata-block-cookies-option"
                value="2"
                flex="1" />
       </radiogroup>
+      <radiogroup id="blockCookies"
+                  preference="network.cookie.cookieBehavior"
+                  onsyncfrompreference="return gPrivacyPane.readBlockCookies();"
+                  onsynctopreference="return gPrivacyPane.writeBlockCookies();">
+        <radio value="allow"
+               data-l10n-id="sitedata-allow-cookies-option"
+               flex="1" />
+        <radio value="disallow"
+               data-l10n-id="sitedata-disallow-cookies-option"
+               flex="1" />
+        <hbox id="blockThirdPartyRow"
+              class="indent"
+              align="center">
+          <label id="blockCookiesLabel" control="blockCookiesMenu"
+                 data-l10n-id="sitedata-block-desc"/>
+          <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
+          <hbox>
+            <menulist id="blockCookiesMenu" preference="network.cookie.cookieBehavior"
+                      onsyncfrompreference="return gPrivacyPane.readBlockCookiesFrom();"
+                      onsynctopreference="return gPrivacyPane.writeBlockCookiesFrom();">
+              <menupopup>
+                <menuitem id="blockCookiesFromTrackers" data-l10n-id="sitedata-block-trackers-option" value="trackers"/>
+                <menuitem data-l10n-id="sitedata-block-unvisited-option" value="unvisited"/>
+                <menuitem data-l10n-id="sitedata-block-all-third-parties-option" value="all-third-parties"/>
+                <menuitem data-l10n-id="sitedata-block-always-option" value="always"/>
+              </menupopup>
+            </menulist>
+          </hbox>
+        </hbox>
+      </radiogroup>
     </vbox>
     <vbox>
       <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
       <hbox>
         <button id="clearSiteDataButton"
             class="accessory-button"
             icon="clear"
             search-l10n-ids="clear-site-data-cookies-empty.label, clear-site-data-cache-empty.label"
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -754,16 +754,40 @@ sitedata-accept-third-party-desc = Accep
 
 sitedata-accept-third-party-always-option =
     .label = Always
 sitedata-accept-third-party-visited-option =
     .label = From visited
 sitedata-accept-third-party-never-option =
     .label = Never
 
+sitedata-allow-cookies-option =
+    .label = Accept cookies and site data
+    .accesskey = A
+
+sitedata-disallow-cookies-option =
+    .label = Block cookies and site data
+    .accesskey = B
+
+# This label means 'type of content that is blocked', and is followed by a drop-down list with content types below.
+# The list items are the strings named sitedata-block-*-option*.
+sitedata-block-desc = Type blocked
+    .accesskey = T
+
+sitedata-block-trackers-option-recommended =
+    .label = Third party trackers (recommended)
+sitedata-block-trackers-option =
+    .label = Third party trackers
+sitedata-block-unvisited-option =
+    .label = Cookies from unvisited websites
+sitedata-block-all-third-parties-option =
+    .label = All third-party cookies
+sitedata-block-always-option =
+    .label = All cookies (may cause websites to break)
+
 sitedata-clear =
     .label = Clear Data…
     .accesskey = l
 
 sitedata-settings =
     .label = Manage Data…
     .accesskey = M
 
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -406,17 +406,17 @@ button > hbox > label {
 }
 
 #historyButtons {
   display: flex;
   flex-direction: column;
   justify-content: space-between;
 }
 
-#acceptCookies {
+#acceptCookies, #blockCookies {
   margin-top: 1.5em;
 }
 
 /* Collapse the non-active vboxes in decks to use only the height the
    active vbox needs */
 #historyPane:not([selectedIndex="1"]) > #historyDontRememberPane,
 #historyPane:not([selectedIndex="2"]) > #historyCustomPane,
 #weavePrefsDeck:not([selectedIndex="1"]) > #hasFxaAccount,