Bug 1486462 - Add checkboxes to the Content Blocking Preferences UI; r=johannh,flod
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 27 Aug 2018 11:14:55 -0400
changeset 491654 0dba07112ba9a40cd6a6e0996faf567b3e4fbb71
parent 491653 e3e0487c3651fe20887270c70276a15164876436
child 491655 f3ea8e9882548d737572d4c7a5a8d72518bf7183
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh, flod
bugs1486462
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 1486462 - Add checkboxes to the Content Blocking Preferences UI; r=johannh,flod
browser/components/preferences/in-content/privacy.js
browser/components/preferences/in-content/privacy.xul
browser/components/preferences/in-content/tests/browser_contentblocking.js
browser/locales/en-US/browser/preferences/preferences.ftl
browser/themes/shared/incontentprefs/privacy.css
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -191,27 +191,37 @@ var gPrivacyPane = {
    */
   _updateTrackingProtectionUI() {
     let isLocked = TRACKING_PROTECTION_PREFS.some(
       pref => Services.prefs.prefIsLocked(pref));
 
     function setInputsDisabledState(isControlled) {
       let disabled = isLocked || isControlled;
       if (contentBlockingUiEnabled) {
+        let tpCheckbox =
+          document.getElementById("contentBlockingTrackingProtectionCheckbox");
+        if (!tpCheckbox.checked) {
+          disabled = true;
+        }
         // Only enable the TP menu if content blocking is enabled.
         document.getElementById("trackingProtectionMenu").disabled = disabled ||
           !contentBlockingEnabled;
       } else {
         document.querySelectorAll("#trackingProtectionRadioGroup > radio")
           .forEach((element) => {
             element.disabled = disabled;
           });
         document.querySelector("#trackingProtectionDesc > label")
           .disabled = disabled;
       }
+
+      // Notify observers that the TP UI has been updated.
+      // This is needed since our tests need to be notified about the
+      // trackingProtectionMenu element getting disabled/enabled at the right time.
+      Services.obs.notifyObservers(window, "privacy-pane-tp-ui-updated");
     }
 
     if (isLocked) {
       // An extension can't control this setting if either pref is locked.
       hideControllingExtension(TRACKING_PROTECTION_KEY);
       setInputsDisabledState(false);
     } else {
       handleControllingExtension(
@@ -453,23 +463,36 @@ var gPrivacyPane = {
 
   /**
    * Initializes the content blocking section.
    */
   initContentBlocking() {
     let contentBlockingCheckbox = document.getElementById("contentBlockingCheckbox");
     setEventListener("contentBlockingToggle", "command",
       () => contentBlockingCheckbox.click());
+    setEventListener("contentBlockingToggle", "command",
+      this.updateContentBlockingControls);
     setEventListener("changeBlockListLink", "click", this.showBlockLists);
     setEventListener("contentBlockingRestoreDefaults", "command",
       this.restoreContentBlockingPrefs);
+    setEventListener("contentBlockingTrackingProtectionCheckbox", "command",
+      this.trackingProtectionWritePrefs);
+    setEventListener("contentBlockingTrackingProtectionCheckbox", "command",
+      this._updateTrackingProtectionUI);
     setEventListener("trackingProtectionMenu", "command",
       this.trackingProtectionWritePrefs);
     setEventListener("contentBlockingChangeCookieSettings", "command",
       this.changeCookieSettings);
+    setEventListener("contentBlockingBlockCookiesCheckbox", "command",
+      this.writeBlockCookiesCheckbox);
+
+    Preferences.get("network.cookie.cookieBehavior").on("change",
+      gPrivacyPane.readBlockCookiesCheckbox.bind(gPrivacyPane));
+
+    this.readBlockCookiesCheckbox();
 
     let link = document.getElementById("contentBlockingLearnMore");
     let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "tracking-protection";
     link.setAttribute("href", url);
 
     // Disable the Content Blocking Third-Party Cookies UI based on a pref
     if (!contentBlockingRejectTrackersUiEnabled) {
       let elements = document.querySelectorAll(".reject-trackers-ui");
@@ -570,33 +593,33 @@ var gPrivacyPane = {
   },
 
   /**
    * Changes the disabled state of controls that depend on the browser.contentblocking.enabled pref.
    */
   updateContentBlockingControls() {
     let dependentControls = [
       "#content-blocking-categories-label",
+      ".content-blocking-checkbox",
       ".content-blocking-icon",
-      ".content-blocking-category-menu",
       ".content-blocking-category-name",
       "#changeBlockListLink",
       "#contentBlockingChangeCookieSettings",
       "#blockCookiesCB, #blockCookiesCB > radio",
     ];
 
     this._toggleControls(dependentControls, contentBlockingEnabled);
 
     // Need to make sure we account for pref locking/extension overrides when enabling the TP menu.
     this._updateTrackingProtectionUI();
 
     // If we are turning Content Blocking on, we may need to keep some parts of the Third-Party Cookies
-    // UI off, depending on the value of the cookieBehavior pref.  readBlockCookiesCB() can do the work
-    // that is needed for that.
-    this.readBlockCookiesCB();
+    // UI off, depending on the value of the cookieBehavior pref.  readBlockCookiesCheckbox() can do
+    // the work that is needed for that.
+    this.readBlockCookiesCheckbox();
   },
 
   _toggleControls(dependentControls, enabled) {
     for (let selector of dependentControls) {
       let controls = document.querySelectorAll(selector);
 
       for (let control of controls) {
         if (enabled) {
@@ -611,32 +634,42 @@ var gPrivacyPane = {
   // TRACKING PROTECTION MODE
 
   /**
    * Selects the right item of the Tracking Protection radiogroup.
    */
   trackingProtectionReadPrefs() {
     let enabledPref = Preferences.get("privacy.trackingprotection.enabled");
     let pbmPref = Preferences.get("privacy.trackingprotection.pbmode.enabled");
-    let tpControl;
+    let tpControl,
+        tpCheckbox;
     if (contentBlockingUiEnabled) {
       tpControl = document.getElementById("trackingProtectionMenu");
+      tpCheckbox = document.getElementById("contentBlockingTrackingProtectionCheckbox");
     } else {
       tpControl = document.getElementById("trackingProtectionRadioGroup");
     }
 
     this._updateTrackingProtectionUI();
 
     // Global enable takes precedence over enabled in Private Browsing.
     if (enabledPref.value) {
       tpControl.value = "always";
+      if (tpCheckbox) {
+        tpCheckbox.checked = true;
+      }
     } else if (pbmPref.value) {
       tpControl.value = "private";
+      if (tpCheckbox) {
+        tpCheckbox.checked = true;
+      }
+    } else if (!tpCheckbox) {
+      tpControl.value = "never";
     } else {
-      tpControl.value = "never";
+      tpCheckbox.checked = false;
     }
   },
 
   /**
    * Selects the right items of the new Cookies & Site Data UI.
    */
   networkCookieBehaviorReadPrefs() {
     let behavior = Preferences.get("network.cookie.cookieBehavior").value;
@@ -681,24 +714,37 @@ var gPrivacyPane = {
   },
 
   /**
    * 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;
+    let tpControl,
+        tpCheckbox;
     if (contentBlockingUiEnabled) {
       tpControl = document.getElementById("trackingProtectionMenu");
+      tpCheckbox = document.getElementById("contentBlockingTrackingProtectionCheckbox");
     } else {
       tpControl = document.getElementById("trackingProtectionRadioGroup");
     }
 
-    switch (tpControl.value) {
+    let value;
+    if (tpCheckbox) {
+      if (tpCheckbox.checked) {
+        value = tpControl.value;
+      } else {
+        value = "never";
+      }
+    } else {
+      value = tpControl.value;
+    }
+
+    switch (value) {
       case "always":
         enabledPref.value = true;
         pbmPref.value = true;
         break;
       case "private":
         enabledPref.value = false;
         pbmPref.value = true;
         break;
@@ -1070,20 +1116,20 @@ var gPrivacyPane = {
     return Ci.nsICookieService.BEHAVIOR_ACCEPT;
   },
 
   enableThirdPartyCookiesUI() {
     document.getElementById("blockCookiesCBDeck").selectedIndex = 0;
     document.getElementById("contentBlockingChangeCookieSettings").hidden = true;
 
     let dependentControls = [
+      ".reject-trackers-ui .content-blocking-checkbox",
       ".reject-trackers-ui .content-blocking-icon",
-      ".reject-trackers-ui .content-blocking-category-menu",
       ".reject-trackers-ui .content-blocking-category-name",
-      "#blockCookiesCB > radio",
+      "#blockCookiesCB, #blockCookiesCB > radio",
       "#blockCookiesCBDeck",
     ];
 
     this._toggleControls(dependentControls, contentBlockingEnabled);
   },
 
   disableThirdPartyCookiesUI(reason) {
     let deckIndex = 0;
@@ -1094,41 +1140,35 @@ var gPrivacyPane = {
       case "unvisited":
         deckIndex = 2;
         break;
     }
     document.getElementById("blockCookiesCBDeck").selectedIndex = deckIndex;
     document.getElementById("contentBlockingChangeCookieSettings").hidden = false;
 
     let dependentControls = [
+      ".reject-trackers-ui .content-blocking-checkbox",
       ".reject-trackers-ui .content-blocking-icon",
-      ".reject-trackers-ui .content-blocking-category-menu",
       ".reject-trackers-ui .content-blocking-category-name",
-      "#blockCookiesCB > radio",
+      "#blockCookiesCB, #blockCookiesCB > radio",
       "#blockCookiesCBDeck",
     ];
 
     this._toggleControls(dependentControls, false);
   },
 
   /**
    * Converts between network.cookie.cookieBehavior and the new content blocking UI
    */
   readBlockCookiesCB() {
     let pref = Preferences.get("network.cookie.cookieBehavior");
     switch (pref.value) {
       case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
-        this.enableThirdPartyCookiesUI();
         return "all-third-parties";
-      case Ci.nsICookieService.BEHAVIOR_REJECT:
-        return this.disableThirdPartyCookiesUI("always");
-      case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
-        return this.disableThirdPartyCookiesUI("unvisited");
       case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
-        this.enableThirdPartyCookiesUI();
         return "trackers";
       default:
         return undefined;
     }
   },
 
   writeBlockCookiesCB() {
     let block = document.getElementById("blockCookiesCB").selectedItem;
@@ -1137,16 +1177,73 @@ var gPrivacyPane = {
         return Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
       case "all-third-parties":
         return Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
       default:
         return undefined;
     }
   },
 
+  writeBlockCookiesCheckbox() {
+    let pref = Preferences.get("network.cookie.cookieBehavior");
+    let bcCheckbox = document.getElementById("contentBlockingBlockCookiesCheckbox");
+    let bcControl = document.getElementById("blockCookiesCB");
+
+    let value;
+    if (bcCheckbox.checked) {
+      value = bcControl.selectedItem.value;
+    } else {
+      value = "none";
+    }
+
+    switch (value) {
+      case "trackers":
+      case "all-third-parties":
+        bcControl.disabled = false;
+        pref.value = this.writeBlockCookiesCB();
+        break;
+      default:
+        bcControl.disabled = true;
+        pref.value = Ci.nsICookieService.BEHAVIOR_ACCEPT;
+        break;
+    }
+  },
+
+  readBlockCookiesCheckbox() {
+    let pref = Preferences.get("network.cookie.cookieBehavior");
+    let bcCheckbox = document.getElementById("contentBlockingBlockCookiesCheckbox");
+    let bcControl = document.getElementById("blockCookiesCB");
+
+    switch (pref.value) {
+      case Ci.nsICookieService.BEHAVIOR_ACCEPT:
+        this.enableThirdPartyCookiesUI();
+        bcCheckbox.checked = false;
+        bcControl.disabled = true;
+        break;
+      case Ci.nsICookieService.BEHAVIOR_REJECT:
+        this.disableThirdPartyCookiesUI("always");
+        break;
+      case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
+        this.disableThirdPartyCookiesUI("unvisited");
+        break;
+      case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
+        this.enableThirdPartyCookiesUI();
+        bcCheckbox.checked = true;
+        bcControl.disabled = !contentBlockingEnabled;
+        break;
+      case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
+        this.enableThirdPartyCookiesUI();
+        bcCheckbox.checked = true;
+        bcControl.disabled = !contentBlockingEnabled;
+        break;
+      default:
+        break;
+    }
+  },
+
   /**
    * 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";
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -327,125 +327,109 @@
               data-l10n-id="content-blocking-toggle-on"/>
       <label id="contentBlockingToggleLabel"
              data-l10n-id="content-blocking-toggle-label-on"
              control="contentBlockingToggle"/>
     </hbox>
     <vbox id="contentBlockingCategories">
       <label id="content-blocking-categories-label" data-l10n-id="content-blocking-category-label"/>
       <hbox class="content-blocking-category">
+        <vbox class="content-blocking-category-checkbox">
+          <checkbox id="contentBlockingFastBlockCheckbox"
+                    class="content-blocking-checkbox"
+                    preference="browser.fastblock.enabled" />
+        </vbox>
         <vbox class="content-blocking-category-icon">
           <image class="fastblock-icon content-blocking-icon"/>
         </vbox>
         <vbox class="content-blocking-category-labels" flex="1">
           <label data-l10n-id="content-blocking-fastblock-label"
                  class="content-blocking-category-name"
-                 control="fastBlockMenu"/>
+                 control="contentBlockingFastBlockCheckbox"/>
           <description data-l10n-id="content-blocking-fastblock-description" class="content-blocking-category-description"/>
         </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>
-            <menulist class="content-blocking-category-menu" id="fastBlockMenu" preference="browser.fastblock.enabled">
-              <menupopup>
-                <menuitem data-l10n-id="content-blocking-fastblock-option-enabled"
-                          value="true"
-                          search-l10n-ids=""/>
-                <menuitem data-l10n-id="content-blocking-fastblock-option-disabled"
-                          value="false"
-                          search-l10n-ids=""/>
-              </menupopup>
-            </menulist>
-          </hbox>
-        </vbox>
       </hbox>
       <vbox>
         <hbox id="contentBlockingTrackingProtectionExtensionContentLabel" align="center" hidden="true">
           <description control="contentBlockingDisableTrackingProtectionExtension" flex="1"/>
         </hbox>
         <hbox id="contentBlockingTrackingProtectionExtensionContentButton" hidden="true">
           <button id="contentBlockingDisableTrackingProtectionExtension"
                   class="extension-controlled-button accessory-button"
                   data-l10n-id="disable-extension"/>
         </hbox>
         <hbox class="content-blocking-category">
+          <vbox class="content-blocking-category-checkbox">
+            <checkbox id="contentBlockingTrackingProtectionCheckbox" class="content-blocking-checkbox" />
+          </vbox>
           <vbox class="content-blocking-category-icon">
             <image class="tracking-protection-icon content-blocking-icon" />
           </vbox>
           <vbox class="content-blocking-category-labels" flex="1">
             <label data-l10n-id="content-blocking-tracking-protection-label"
                    class="content-blocking-category-name"
-                   control="trackingProtectionMenu"/>
-            <description data-l10n-id="content-blocking-tracking-protection-description" class="content-blocking-category-description"/>
+                   control="contentBlockingTrackingProtectionCheckbox"/>
+            <description data-l10n-id="content-blocking-tracking-protection-description"
+                         class="content-blocking-category-description"
+                         id="trackingProtectionMenuDesc"/>
+            <radiogroup id="trackingProtectionMenu"
+                        aria-labelledby="trackingProtectionMenuDesc">
+              <radio value="private"
+                     data-l10n-id="content-blocking-tracking-protection-option-private"
+                     flex="1" />
+              <radio value="always"
+                     data-l10n-id="content-blocking-tracking-protection-option-always"
+                     flex="1" />
+            </radiogroup>
             <label id="changeBlockListLink" data-l10n-id="content-blocking-tracking-protection-change-blocklist" class="text-link"/>
           </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>
-              <menulist class="content-blocking-category-menu" id="trackingProtectionMenu">
-                <menupopup>
-                  <menuitem data-l10n-id="content-blocking-tracking-protection-option-enabled"
-                            value="always"
-                            search-l10n-ids=""/>
-                  <menuitem data-l10n-id="content-blocking-tracking-protection-option-pbm"
-                            value="private"
-                            search-l10n-ids=""/>
-                  <menuitem data-l10n-id="content-blocking-tracking-protection-option-disabled"
-                            value="never"
-                            search-l10n-ids=""/>
-                </menupopup>
-              </menulist>
-            </hbox>
-          </vbox>
         </hbox>
       </vbox>
       <hbox class="content-blocking-category reject-trackers-ui">
         <hbox flex="1">
+          <vbox class="content-blocking-category-checkbox">
+            <checkbox id="contentBlockingBlockCookiesCheckbox" class="content-blocking-checkbox" />
+          </vbox>
           <vbox class="content-blocking-category-icon">
             <image class="reject-trackers-icon content-blocking-icon"/>
           </vbox>
           <vbox class="content-blocking-category-labels" flex="1">
-            <label data-l10n-id="content-blocking-reject-trackers-label"
-                   class="content-blocking-category-name"
-                   control="blockCookiesCB"/>
-            <deck id="blockCookiesCBDeck">
-              <description id="blockCookiesCBDesc"
-                           data-l10n-id="content-blocking-reject-trackers-description"
-                           class="content-blocking-category-description"/>
-              <hbox id="warningAllCookiesBlocked">
-                <image class="reject-trackers-warning-icon"/>
-                <description data-l10n-id="content-blocking-reject-trackers-warning-all-cookies-blocked"
-                             class="content-blocking-category-description"/>
-              </hbox>
-              <hbox id="warningFromVisitedCookiesBlocked">
-                <image class="reject-trackers-warning-icon"/>
-                <description data-l10n-id="content-blocking-reject-trackers-warning-from-unvisited-cookies-blocked"
-                             class="content-blocking-category-description"/>
+            <hbox>
+              <vbox>
+                <label data-l10n-id="content-blocking-reject-trackers-label"
+                       class="content-blocking-category-name"
+                       control="contentBlockingBlockCookiesCheckbox"/>
+                <deck id="blockCookiesCBDeck">
+                  <description id="blockCookiesCBDesc"
+                               data-l10n-id="content-blocking-reject-trackers-description"
+                               class="content-blocking-category-description"/>
+                  <hbox id="warningAllCookiesBlocked">
+                    <image class="reject-trackers-warning-icon"/>
+                    <description data-l10n-id="content-blocking-reject-trackers-warning-all-cookies-blocked"
+                                 class="content-blocking-category-description"/>
+                  </hbox>
+                  <hbox id="warningFromVisitedCookiesBlocked">
+                    <image class="reject-trackers-warning-icon"/>
+                    <description data-l10n-id="content-blocking-reject-trackers-warning-from-unvisited-cookies-blocked"
+                                 class="content-blocking-category-description"/>
+                  </hbox>
+                </deck>
+              </vbox>
+              <hbox align="center">
+                <vbox align="center">
+                  <button id="contentBlockingChangeCookieSettings"
+                          class="accessory-button"
+                          flex="1"
+                          hidden="true"
+                          data-l10n-id="content-blocking-change-cookie-settings"/>
+                </vbox>
               </hbox>
-            </deck>
-          </vbox>
-        </hbox>
-        <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="contentBlockingChangeCookieSettings"
-                    class="accessory-button"
-                    flex="1"
-                    hidden="true"
-                    data-l10n-id="content-blocking-change-cookie-settings"/>
-          </hbox>
-        </vbox>
-      </hbox>
-      <hbox class="content-blocking-category reject-trackers-ui">
-        <hbox>
-          <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
-          <vbox class="indent">
+            </hbox>
             <radiogroup id="blockCookiesCB"
                         aria-labelledby="blockCookiesCBDesc"
-                        class="content-blocking-category-menu"
                         preference="network.cookie.cookieBehavior"
                         onsyncfrompreference="return gPrivacyPane.readBlockCookiesCB();"
                         onsynctopreference="return gPrivacyPane.writeBlockCookiesCB();">
               <radio value="trackers"
                      id="blockCookiesFromTrackersCB"
                      data-l10n-id="content-blocking-reject-trackers-block-trackers-option"
                      flex="1" />
               <radio value="all-third-parties"
--- a/browser/components/preferences/in-content/tests/browser_contentblocking.js
+++ b/browser/components/preferences/in-content/tests/browser_contentblocking.js
@@ -189,107 +189,198 @@ async function doDependentControlChecks(
   await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
   doc = gBrowser.contentDocument;
 
   is(Services.prefs.getBoolPref(CB_PREF), false, "Content Blocking is off");
   checkControlState(doc, dependentControls);
   checkControlStateWorker(doc, alwaysDisabledControls, false);
 
   let contentBlockingToggle = doc.getElementById("contentBlockingToggle");
-  contentBlockingToggle.click();
+  contentBlockingToggle.doCommand();
+
+  await TestUtils.topicObserved("privacy-pane-tp-ui-updated");
 
   is(Services.prefs.getBoolPref(CB_PREF), true, "Content Blocking is on");
   checkControlState(doc, dependentControls);
   checkControlStateWorker(doc, alwaysDisabledControls, false);
 
-  contentBlockingToggle.click();
+  contentBlockingToggle.doCommand();
+
+  await TestUtils.topicObserved("privacy-pane-tp-ui-updated");
 
   is(Services.prefs.getBoolPref(CB_PREF), false, "Content Blocking is off");
   checkControlState(doc, dependentControls);
   checkControlStateWorker(doc, alwaysDisabledControls, false);
 
   Services.prefs.clearUserPref(CB_PREF);
   gBrowser.removeCurrentTab();
 }
 
 // Checks that the granular controls are disabled or enabled depending on the master pref for CB.
 add_task(async function testContentBlockingDependentControls() {
+  // In Accept All Cookies mode, the radiogroup under Third-Party Cookies is always disabled
+  // since the checkbox next to Third-Party Cookies would be unchecked.
   SpecialPowers.pushPrefEnv({set: [
     [CB_UI_PREF, true],
     [CB_RT_UI_PREF, true],
+    [NCB_PREF, Ci.nsICookieService.BEHAVIOR_ACCEPT],
   ]});
 
   let dependentControls = [
     "#content-blocking-categories-label",
+    ".content-blocking-checkbox",
     ".content-blocking-icon",
-    ".content-blocking-category-menu",
+    ".content-blocking-category-name",
+    "#changeBlockListLink",
+    "#contentBlockingChangeCookieSettings",
+  ];
+  let alwaysDisabledControls = [
+    "#blockCookiesCB, #blockCookiesCB > radio",
+  ];
+
+  await doDependentControlChecks(dependentControls, alwaysDisabledControls);
+
+  // In Block Cookies from Trackers (or Block Cookies from All Third-Parties) mode, the
+  // radiogroup's disabled status must obey the content blocking enabled state.
+  SpecialPowers.pushPrefEnv({set: [
+    [CB_UI_PREF, true],
+    [CB_RT_UI_PREF, true],
+    [NCB_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
+  ]});
+
+  dependentControls = [
+    "#content-blocking-categories-label",
+    ".content-blocking-checkbox",
+    ".content-blocking-icon",
     ".content-blocking-category-name",
     "#changeBlockListLink",
     "#contentBlockingChangeCookieSettings",
     "#blockCookiesCB, #blockCookiesCB > radio",
   ];
 
   await doDependentControlChecks(dependentControls);
 });
 
+// Checks that the controls for tracking protection are disabled when all TP prefs are off.
+add_task(async function testContentBlockingDependentTPControls() {
+  SpecialPowers.pushPrefEnv({set: [
+    [CB_UI_PREF, true],
+    [CB_RT_UI_PREF, true],
+    [TP_PREF, false],
+    [TP_PBM_PREF, false],
+    [NCB_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
+  ]});
+
+  let dependentControls = [
+    "#content-blocking-categories-label",
+    ".content-blocking-checkbox",
+    ".content-blocking-icon",
+    ".content-blocking-category-name",
+    "#changeBlockListLink",
+    "#contentBlockingChangeCookieSettings",
+    "#blockCookiesCB, #blockCookiesCB > radio",
+  ];
+  let alwaysDisabledControls = [
+    "#trackingProtectionMenu",
+    "[control=trackingProtectionMenu]",
+  ];
+
+  await doDependentControlChecks(dependentControls, alwaysDisabledControls);
+});
+
 
 // Checks that the granular controls are disabled or enabled depending on the master pref for CB
 // when the Cookies and Site Data section is set to block either "All Cookies" or "Cookies from
 // unvisited websites".
 add_task(async function testContentBlockingDependentControlsOnSiteDataUI() {
   let prefValuesToTest = [
     Ci.nsICookieService.BEHAVIOR_REJECT,        // Block All Cookies
     Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN, // Block Cookies from unvisited websites
   ];
   for (let value of prefValuesToTest) {
     await SpecialPowers.pushPrefEnv({set: [
       [CB_UI_PREF, true],
       [CB_RT_UI_PREF, true],
+      [TP_PREF, false],
+      [TP_PBM_PREF, true],
       [NCB_PREF, value],
     ]});
 
     let dependentControls = [
       "#content-blocking-categories-label",
+      "#contentBlockingFastBlockCheckbox",
+      "#contentBlockingTrackingProtectionCheckbox",
       ".fastblock-icon",
       ".tracking-protection-icon",
-      "#fastBlockMenu",
       "#trackingProtectionMenu",
-      "[control=fastBlockMenu]",
       "[control=trackingProtectionMenu]",
       "#changeBlockListLink",
       "#contentBlockingChangeCookieSettings",
     ];
     let alwaysDisabledControls = [
+      ".reject-trackers-checkbox",
       ".reject-trackers-icon",
       "[control=blockCookiesCB]",
       "#blockCookiesCBDeck",
       "#blockCookiesCB, #blockCookiesCB > radio",
     ];
 
     await doDependentControlChecks(dependentControls, alwaysDisabledControls);
   }
 
+  prefValuesToTest = [
+    Ci.nsICookieService.BEHAVIOR_ACCEPT,         // Accept All Cookies
+  ];
+  for (let value of prefValuesToTest) {
+    await SpecialPowers.pushPrefEnv({set: [
+      [CB_UI_PREF, true],
+      [CB_RT_UI_PREF, true],
+      [TP_PREF, false],
+      [TP_PBM_PREF, true],
+      [NCB_PREF, value],
+    ]});
+
+    let dependentControls = [
+      "#content-blocking-categories-label",
+      ".content-blocking-checkbox",
+      ".content-blocking-icon",
+      ".content-blocking-category-name",
+      "#trackingProtectionMenu",
+      "[control=trackingProtectionMenu]",
+      "#changeBlockListLink",
+      "#contentBlockingChangeCookieSettings",
+    ];
+    let alwaysDisabledControls = [
+      "#blockCookiesCB, #blockCookiesCB > radio",
+    ];
+
+    await doDependentControlChecks(dependentControls, alwaysDisabledControls);
+  }
+
   // The rest of the values
   prefValuesToTest = [
-    Ci.nsICookieService.BEHAVIOR_ACCEPT,         // Accept All Cookies
     Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN, // Block All Third-Party Cookies
     Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER, // Block Cookies from third-party trackers
   ];
   for (let value of prefValuesToTest) {
     await SpecialPowers.pushPrefEnv({set: [
       [CB_UI_PREF, true],
       [CB_RT_UI_PREF, true],
+      [TP_PREF, false],
+      [TP_PBM_PREF, true],
       [NCB_PREF, value],
     ]});
 
     let dependentControls = [
       "#content-blocking-categories-label",
+      ".content-blocking-checkbox",
       ".content-blocking-icon",
-      ".content-blocking-category-menu",
       ".content-blocking-category-name",
+      "#trackingProtectionMenu",
+      "[control=trackingProtectionMenu]",
       "#changeBlockListLink",
       "#contentBlockingChangeCookieSettings",
       "#blockCookiesCB, #blockCookiesCB > radio",
     ];
 
     await doDependentControlChecks(dependentControls);
   }
 });
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -819,30 +819,25 @@ content-blocking-toggle-label-off = OFF
 content-blocking-category-label = Choose what to block
 
 # "Slow" in this instance means "slow to load on the network".
 # FastBlock is a feature that blocks requests to tracking sites if they
 # have not finished loading after a certain threshold of seconds.
 content-blocking-fastblock-label = Slow Tracking Elements
   .accesskey = S
 content-blocking-fastblock-description = Blocks third-party content that takes longer than 5 seconds to load.
-content-blocking-fastblock-option-enabled =
-  .label = Always block
-content-blocking-fastblock-option-disabled =
-  .label = Never block
-
 content-blocking-tracking-protection-label = Trackers
   .accesskey = T
 content-blocking-tracking-protection-description = Blocks all known trackers (Note: may also prevent some pages from loading).
-content-blocking-tracking-protection-option-enabled =
-  .label = Always block
-content-blocking-tracking-protection-option-pbm =
-  .label = Block only in private windows
-content-blocking-tracking-protection-option-disabled =
-  .label = Never block
+content-blocking-tracking-protection-option-always =
+  .label = Always
+  .accesskey = A
+content-blocking-tracking-protection-option-private =
+  .label = Only in private windows
+  .accesskey = p
 content-blocking-tracking-protection-change-blocklist = Change Block List…
 
 content-blocking-reject-trackers-label = Third-Party Cookies
   .accesskey = C
 content-blocking-reject-trackers-description = Block all third-party cookies or just those set by trackers.
 # This is a warning message shown next to a yellow warning icon when the Third-Party Cookies subsection
 # of the Content Blocking UI in Preferences has been disabled due to the "All cookies" option
 # being selected in the Cookies and Site Data section of the UI.
--- a/browser/themes/shared/incontentprefs/privacy.css
+++ b/browser/themes/shared/incontentprefs/privacy.css
@@ -1,19 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Permissions */
 
 .content-blocking-icon,
-.permission-icon {
+.permission-icon,
+.content-blocking-checkbox {
+  width: 20px;
   height: 20px;
-  width: 20px;
   vertical-align: middle;
+}
+
+.content-blocking-icon,
+.permission-icon {
   -moz-context-properties: fill;
   fill: currentColor;
 }
 
 .content-blocking-icon[disabled] {
   fill: GrayText;
 }
 
@@ -103,41 +108,47 @@
 }
 
 #contentBlockingCheckbox[checked] + #contentBlockingToggle:hover,
 #contentBlockingCheckbox[checked] + #contentBlockingToggle:-moz-focusring {
   background-color: #0a84ff;
 }
 
 .content-blocking-category {
-  margin: 8px 0;
+  margin: 16px 0;
+}
+
+.content-blocking-category-checkbox {
+  padding: 4px;
 }
 
 .content-blocking-category-icon {
   padding: 4px;
 }
 
 .content-blocking-category-labels {
   padding-inline-start: 4px;
 }
 
+#trackingProtectionMenu,
+#blockCookiesCB {
+  margin-top: 0.75em;
+}
+
 #changeBlockListLink {
   font-size: 90%;
+  /* In order to override the margins set in preferences.inc.css, we have to use !important. */
+  margin-top: 1em !important;
 }
 
 .content-blocking-category-description {
   font-size: 90%;
   opacity: 0.6;
 }
 
-.content-blocking-category-menu {
-  margin-top: -2px;
-  min-width: 250px;
-}
-
 .fastblock-icon {
   list-style-image: url(chrome://browser/skin/controlcenter/slowtrackers.svg);
 }
 
 .tracking-protection-icon {
   list-style-image: url(chrome://browser/skin/controlcenter/trackers.svg);
 }