Bug 1483378 - Part 1: Update the Cookies and Site Data UI in the Preferences window; r=johannh,flod
☠☠ backed out by b6e4ff816183 ☠ ☠
authorEhsan Akhgari <ehsan@mozilla.com>
Fri, 17 Aug 2018 11:14:18 -0400
changeset 487498 330301cd20bec579d0459606caf3ace55affff57
parent 487497 ff38da9052f5ae4145ae541ba22875b737a009bc
child 487499 9bfb53d863aaf64d6b4adb51b4f3b4880c972e4e
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"];
@@ -261,25 +267,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",
@@ -470,31 +485,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";
@@ -561,16 +602,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");
@@ -968,38 +1054,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,