Bug 1476217 - Part 1 - Update Tracking Protection preferences for Content Blocking. r=jaws draft
authorJohann Hofmann <jhofmann@mozilla.com>
Mon, 23 Jul 2018 13:02:47 +0200
changeset 825733 04efae13fd02211bb1610b7693769a74a5ff6df5
parent 825566 3cb90f16402bc5e1203e2771dc93553b8377fa40
child 825734 f36084a86f7286999f92729e0a00e2d700f078de
push id118155
push userjhofmann@mozilla.com
push dateThu, 02 Aug 2018 06:20:22 +0000
reviewersjaws
bugs1476217
milestone63.0a1
Bug 1476217 - Part 1 - Update Tracking Protection preferences for Content Blocking. r=jaws This adds a new "Content Blocking" UI in place of the Tracking Protection preferences. The Tracking Protection UI is preserved and the new UI is behind a pref, for now. This resulted in some inconveniences such as duplicate code or unwieldy long names, but we're planning to remove one version (likely the TP UI) soon. The CB preferences can contain several categories of blockers which will be added in future patches. The first one, Tracking Protection, is added in this patch and offers a replacement in functionality for the previous TP UI. MozReview-Commit-ID: 5Oj2glZMTqC
browser/app/profile/firefox.js
browser/components/preferences/in-content/extensionControlled.js
browser/components/preferences/in-content/privacy.js
browser/components/preferences/in-content/privacy.xul
browser/components/preferences/in-content/tests/browser_extension_controlled.js
browser/locales/en-US/browser/preferences/preferences.ftl
browser/themes/shared/controlcenter/trackers.svg
browser/themes/shared/incontentprefs/preferences.inc.css
browser/themes/shared/incontentprefs/privacy.css
browser/themes/shared/jar.inc.mn
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1471,16 +1471,23 @@ pref("toolkit.telemetry.hybridContent.en
 pref("browser.ping-centre.telemetry", true);
 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);
+#ifdef NIGHTLY_BUILD
+pref("browser.contentblocking.ui.enabled", true);
+#else
+pref("browser.contentblocking.ui.enabled", false);
+#endif
+
 pref("privacy.trackingprotection.introCount", 0);
 pref("privacy.trackingprotection.introURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tracking-protection/start/");
 #ifdef NIGHTLY_BUILD
 pref("privacy.trackingprotection.appMenuToggle.enabled", true);
 #else
 pref("privacy.trackingprotection.appMenuToggle.enabled", false);
 #endif
 
--- a/browser/components/preferences/in-content/extensionControlled.js
+++ b/browser/components/preferences/in-content/extensionControlled.js
@@ -12,16 +12,19 @@ ChromeUtils.defineModuleGetter(this, "Ad
                                   "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                                   "resource://gre/modules/BrowserUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "DeferredTask",
                                   "resource://gre/modules/DeferredTask.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                   "resource://gre/modules/ExtensionSettingsStore.jsm");
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingUiEnabled",
+                                      "browser.contentblocking.ui.enabled");
+
 const PREF_SETTING_TYPE = "prefs";
 const PROXY_KEY = "proxy.settings";
 const API_PROXY_PREFS = [
   "network.proxy.type",
   "network.proxy.http",
   "network.proxy.http_port",
   "network.proxy.share_proxy_settings",
   "network.proxy.ftp",
@@ -40,18 +43,22 @@ const API_PROXY_PREFS = [
 let extensionControlledContentIds = {
   "privacy.containers": "browserContainersExtensionContent",
   "homepage_override": "browserHomePageExtensionContent",
   "newTabURL": "browserNewTabExtensionContent",
   "defaultSearch": "browserDefaultSearchExtensionContent",
   "proxy.settings": "proxyExtensionContent",
   get "websites.trackingProtectionMode"() {
     return {
-      button: "trackingProtectionExtensionContentButton",
-      section: "trackingProtectionExtensionContentLabel",
+      button: contentBlockingUiEnabled ?
+        "contentBlockingTrackingProtectionExtensionContentButton" :
+        "trackingProtectionExtensionContentButton",
+      section: contentBlockingUiEnabled ?
+        "contentBlockingTrackingProtectionExtensionContentLabel" :
+        "trackingProtectionExtensionContentLabel",
     };
   }
 };
 
 const extensionControlledL10nKeys = {
   "homepage_override": "homepage-override",
   "newTabURL": "new-tab-url",
   "defaultSearch": "default-search",
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -15,16 +15,19 @@ ChromeUtils.defineModuleGetter(this, "Pl
   "resource://gre/modules/PluralForm.jsm");
 ChromeUtils.defineModuleGetter(this, "LoginHelper",
   "resource://gre/modules/LoginHelper.jsm");
 ChromeUtils.defineModuleGetter(this, "SiteDataManager",
   "resource:///modules/SiteDataManager.jsm");
 
 ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingUiEnabled",
+                                      "browser.contentblocking.ui.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"];
 
 const PREF_OPT_OUT_STUDIES_ENABLED = "app.shield.optoutstudies.enabled";
 const PREF_NORMANDY_ENABLED = "app.normandy.enabled";
@@ -38,17 +41,20 @@ XPCOMUtils.defineLazyGetter(this, "Alert
     alertsService.manualDoNotDisturb;
     return alertsService;
   } catch (ex) {
     return undefined;
   }
 });
 
 Preferences.addAll([
-  // Tracking
+  // Content Blocking
+  { id: "browser.contentblocking.enabled", type: "bool" },
+
+  // Tracking Protection
   { id: "privacy.trackingprotection.enabled", type: "bool" },
   { id: "privacy.trackingprotection.pbmode.enabled", type: "bool" },
 
   // Button prefs
   { id: "pref.privacy.disable_button.cookie_exceptions", type: "bool" },
   { id: "pref.privacy.disable_button.view_cookies", type: "bool" },
   { id: "pref.privacy.disable_button.change_blocklist", type: "bool" },
   { id: "pref.privacy.disable_button.tracking_protection_exceptions", type: "bool" },
@@ -131,50 +137,61 @@ if (AppConstants.MOZ_DATA_REPORTING) {
 // Data Choices tab
 if (AppConstants.NIGHTLY_BUILD) {
   Preferences.add({ id: "browser.chrome.errorReporter.enabled", type: "bool" });
 }
 if (AppConstants.MOZ_CRASHREPORTER) {
   Preferences.add({ id: "browser.crashReports.unsubmittedCheck.autoSubmit2", type: "bool" });
 }
 
+function setEventListener(aId, aEventType, aCallback) {
+  document.getElementById(aId)
+    .addEventListener(aEventType, aCallback.bind(gPrivacyPane));
+}
+
 var gPrivacyPane = {
   _pane: null,
 
   /**
    * Whether the prompt to restart Firefox should appear when changing the autostart pref.
    */
   _shouldPromptForRestart: true,
 
   /**
    * Initialize the tracking protection prefs and linkify its Learn More link.
    */
   _initTrackingProtection() {
+    setEventListener("trackingProtectionRadioGroup", "command",
+      this.trackingProtectionWritePrefs);
+    setEventListener("changeBlockList", "command", this.showBlockLists);
+
     let link = document.getElementById("trackingProtectionLearnMore");
     let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "tracking-protection";
     link.setAttribute("href", url);
-
-    this.trackingProtectionReadPrefs();
   },
 
   /**
    * Update the tracking protection UI to deal with extension control.
    */
   _updateTrackingProtectionUI() {
     let isLocked = TRACKING_PROTECTION_PREFS.some(
       pref => Services.prefs.prefIsLocked(pref));
 
     function setInputsDisabledState(isControlled) {
       let disabled = isLocked || isControlled;
-      document.querySelectorAll("#trackingProtectionRadioGroup > radio")
-        .forEach((element) => {
-          element.disabled = disabled;
-        });
-      document.querySelector("#trackingProtectionDesc > label")
-        .disabled = disabled;
+      if (contentBlockingUiEnabled) {
+        document.getElementById("trackingProtectionMenu").disabled = disabled;
+      } else {
+        document.querySelectorAll("#trackingProtectionRadioGroup > radio")
+          .forEach((element) => {
+            element.disabled = disabled;
+          });
+        document.querySelector("#trackingProtectionDesc > label")
+          .disabled = disabled;
+      }
     }
 
     if (isLocked) {
       // An extension can't control this setting if either pref is locked.
       hideControllingExtension(TRACKING_PROTECTION_KEY);
       setInputsDisabledState(false);
     } else {
       handleControllingExtension(
@@ -184,16 +201,22 @@ var gPrivacyPane = {
     }
   },
 
   /**
    * Set up handlers for showing and hiding controlling extension info
    * for tracking protection.
    */
   _initTrackingProtectionExtensionControl() {
+    let disableButton = contentBlockingUiEnabled ?
+      "contentBlockingDisableTrackingProtectionExtension" : "disableTrackingProtectionExtension";
+    setEventListener(disableButton, "command",
+      makeDisableControllingExtension(
+        PREF_SETTING_TYPE, TRACKING_PROTECTION_KEY));
+
     let trackingProtectionObserver = {
       observe(subject, topic, data) {
         gPrivacyPane._updateTrackingProtectionUI();
       },
     };
 
     for (let pref of TRACKING_PROTECTION_PREFS) {
       Services.prefs.addObserver(pref, trackingProtectionObserver);
@@ -213,39 +236,49 @@ var gPrivacyPane = {
       .getService(Ci.mozIPlacesAutoComplete);
   },
 
   /**
    * Sets up the UI for the number of days of history to keep, and updates the
    * label of the "Clear Now..." button.
    */
   init() {
-    function setEventListener(aId, aEventType, aCallback) {
-      document.getElementById(aId)
-        .addEventListener(aEventType, aCallback.bind(gPrivacyPane));
-    }
-
     this._updateSanitizeSettingsButton();
     this.initializeHistoryMode();
     this.updateAutoplayMediaControlsVisibility();
     this.updateHistoryModePane();
     this.updatePrivacyMicroControls();
     this.initAutoStartPrivateBrowsingReverter();
-    this._initTrackingProtection();
+    this._initAutocomplete();
+
+    /* Initialize Content Blocking / Tracking Protection */
+
+    if (contentBlockingUiEnabled) {
+      this.initContentBlocking();
+    } else {
+      this._initTrackingProtection();
+    }
+
+    this.trackingProtectionReadPrefs();
     this._initTrackingProtectionExtensionControl();
-    this._initAutocomplete();
+
+    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));
+
+    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("privacy.trackingprotection.enabled").on("change",
-      gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane));
-    Preferences.get("privacy.trackingprotection.pbmode.enabled").on("change",
-      gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane));
     Preferences.get("media.autoplay.enabled.ask-permission").on("change",
      gPrivacyPane.updateAutoplayMediaControlsVisibility.bind(gPrivacyPane));
     Preferences.get("media.autoplay.enabled.user-gestures-needed").on("change",
      gPrivacyPane.updateAutoplayMediaControlsVisibility.bind(gPrivacyPane));
     setEventListener("historyMode", "command", function() {
       gPrivacyPane.updateHistoryModePane();
       gPrivacyPane.updateHistoryModePrefs();
       gPrivacyPane.updatePrivacyMicroControls();
@@ -264,25 +297,16 @@ var gPrivacyPane = {
       return false;
     });
     setEventListener("privateBrowsingAutoStart", "command",
       gPrivacyPane.updateAutostart);
     setEventListener("cookieExceptions", "command",
       gPrivacyPane.showCookieExceptions);
     setEventListener("clearDataSettings", "command",
       gPrivacyPane.showClearPrivateDataSettings);
-    setEventListener("disableTrackingProtectionExtension", "command",
-      makeDisableControllingExtension(
-        PREF_SETTING_TYPE, TRACKING_PROTECTION_KEY));
-    setEventListener("trackingProtectionRadioGroup", "command",
-      gPrivacyPane.trackingProtectionWritePrefs);
-    setEventListener("trackingProtectionExceptions", "command",
-      gPrivacyPane.showTrackingProtectionExceptions);
-    setEventListener("changeBlockList", "command",
-      gPrivacyPane.showBlockLists);
     setEventListener("passwordExceptions", "command",
       gPrivacyPane.showPasswordExceptions);
     setEventListener("useMasterPassword", "command",
       gPrivacyPane.updateMasterPasswordButton);
     setEventListener("changeMasterPassword", "command",
       gPrivacyPane.changeMasterPassword);
     setEventListener("showPasswords", "command",
       gPrivacyPane.showPasswords);
@@ -390,47 +414,114 @@ var gPrivacyPane = {
       document.getElementById("privateBrowsingAutoStart").hidden = true;
       document.querySelector("menuitem[value='dontremember']").hidden = true;
     }
 
     // Notify observers that the UI is now ready
     Services.obs.notifyObservers(window, "privacy-pane-loaded");
   },
 
+  // CONTENT BLOCKING
+
+  /**
+   * Initializes the content blocking section.
+   */
+  initContentBlocking() {
+    let contentBlockingCheckbox = document.getElementById("contentBlockingCheckbox");
+    setEventListener("contentBlockingToggle", "command",
+      () => contentBlockingCheckbox.click());
+    setEventListener("changeBlockListLink", "click", this.showBlockLists);
+    setEventListener("trackingProtectionMenu", "command",
+      this.trackingProtectionWritePrefs);
+
+    let link = document.getElementById("contentBlockingLearnMore");
+    let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "tracking-protection";
+    link.setAttribute("href", url);
+  },
+
+  /**
+   * Changes the visibility of elements in the TP/CB section depending on the
+   * content blocking UI pref.
+   */
+  updateContentBlockingVisibility() {
+    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];
+    }
+  },
+
+  /**
+   * 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 enabled = Services.prefs.getBoolPref("browser.contentblocking.enabled");
+    let onOrOff = enabled ? "on" : "off";
+    let contentBlockingToggle = document.getElementById("contentBlockingToggle");
+    let contentBlockingToggleLabel = document.getElementById("contentBlockingToggleLabel");
+
+    document.l10n.setAttributes(contentBlockingToggle,
+      "content-blocking-toggle-" + onOrOff);
+    contentBlockingToggle.setAttribute("aria-pressed", enabled);
+    document.l10n.setAttributes(contentBlockingToggleLabel,
+      "content-blocking-toggle-label-" + onOrOff);
+  },
+
   // 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 radiogroup = document.getElementById("trackingProtectionRadioGroup");
+    let tpControl;
+    if (contentBlockingUiEnabled) {
+      tpControl = document.getElementById("trackingProtectionMenu");
+    } else {
+      tpControl = document.getElementById("trackingProtectionRadioGroup");
+    }
 
     this._updateTrackingProtectionUI();
 
     // Global enable takes precedence over enabled in Private Browsing.
     if (enabledPref.value) {
-      radiogroup.value = "always";
+      tpControl.value = "always";
     } else if (pbmPref.value) {
-      radiogroup.value = "private";
+      tpControl.value = "private";
     } else {
-      radiogroup.value = "never";
+      tpControl.value = "never";
     }
   },
 
   /**
    * 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 radiogroup = document.getElementById("trackingProtectionRadioGroup");
+    let tpControl;
+    if (contentBlockingUiEnabled) {
+      tpControl = document.getElementById("trackingProtectionMenu");
+    } else {
+      tpControl = document.getElementById("trackingProtectionRadioGroup");
+    }
 
-    switch (radiogroup.value) {
+    switch (tpControl.value) {
       case "always":
         enabledPref.value = true;
         pbmPref.value = true;
         break;
       case "private":
         enabledPref.value = false;
         pbmPref.value = true;
         break;
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -275,30 +275,105 @@
   <checkbox id="bookmarkSuggestion" data-l10n-id="addressbar-locbar-bookmarks-option"
             preference="browser.urlbar.suggest.bookmark"/>
   <checkbox id="openpageSuggestion" data-l10n-id="addressbar-locbar-openpage-option"
             preference="browser.urlbar.suggest.openpage"/>
   <label class="text-link" id="openSearchEnginePreferences"
          data-l10n-id="addressbar-suggestions-settings"/>
 </groupbox>
 
-<!-- Tracking -->
+<!-- Tracking / Content Blocking -->
 <groupbox id="trackingGroup" data-category="panePrivacy" hidden="true">
-  <caption><label data-l10n-id="tracking-header"/></caption>
+  <caption>
+    <label id="trackingProtectionHeader" hidden="true" data-l10n-id="tracking-header"/>
+    <label id="contentBlockingHeader" data-l10n-id="content-blocking-header"/>
+  </caption>
   <vbox data-subcategory="trackingprotection">
     <hbox align="start">
       <vbox flex="1">
-        <description data-l10n-id="tracking-desc">
+        <description id="trackingProtectionDescription" class="description-with-side-element" hidden="true" data-l10n-id="tracking-desc">
           <html:a id="trackingProtectionLearnMore" data-l10n-name="learn-more" target="_blank" class="learnMore text-link"/>
         </description>
+        <description id="contentBlockingDescription" class="description-with-side-element" data-l10n-id="content-blocking-desc"></description>
+        <label id="contentBlockingLearnMore" data-l10n-id="content-blocking-learn-more" class="learnMore text-link"/>
       </vbox>
-      <spacer flex="1"/>
+      <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="trackingProtectionExceptions"
+                  class="accessory-button"
+                  flex="1"
+                  data-l10n-id="tracking-exceptions"
+                  preference="pref.privacy.disable_button.tracking_protection_exceptions"
+                  search-l10n-ids="
+                    permissions-remove.label,
+                    permissions-remove-all.label,
+                    permissions-button-cancel.label,
+                    permissions-button-ok.label,
+                    permissions-exceptions-tracking-protection-window.title,
+                    permissions-exceptions-tracking-protection-desc,
+                  "/>
+        </hbox>
+      </vbox>
+    </hbox>
+    <hbox id="contentBlockingCheckboxContainer">
+      <checkbox id="contentBlockingCheckbox"
+                onsyncfrompreference="return gPrivacyPane.updateContentBlockingToggle()"
+                preference="browser.contentblocking.enabled"/>
+      <button id="contentBlockingToggle"
+              data-l10n-id="content-blocking-toggle-on"/>
+      <label id="contentBlockingToggleLabel"
+             data-l10n-id="content-blocking-toggle-label-on"
+             control="contentBlockingToggle"/>
     </hbox>
-    <hbox>
-      <vbox id="trackingProtectionBox" flex="1">
+    <vbox id="contentBlockingCategories">
+      <label id="content-blocking-categories-label" data-l10n-id="content-blocking-category-label"/>
+      <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-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"/>
+            <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>
+    </vbox>
+    <hbox id="trackingProtectionBox" hidden="true">
+      <vbox flex="1">
         <vbox>
           <hbox id="trackingProtectionExtensionContentLabel" align="center" hidden="true">
             <description control="disableTrackingProtectionExtension" flex="1"/>
           </hbox>
           <vbox>
             <description id="trackingProtectionDesc"
                          control="trackingProtectionRadioGroup">
               <label data-l10n-id="tracking-mode-label"/>
@@ -318,32 +393,16 @@
         <hbox id="trackingProtectionExtensionContentButton" hidden="true">
           <button id="disableTrackingProtectionExtension"
                   class="extension-controlled-button accessory-button"
                   flex="1"
                   data-l10n-id="disable-extension"/>
         </hbox>
         <!-- 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="trackingProtectionExceptions"
-                  class="accessory-button"
-                  flex="1"
-                  data-l10n-id="tracking-exceptions"
-                  preference="pref.privacy.disable_button.tracking_protection_exceptions"
-                  search-l10n-ids="
-                    permissions-remove.label,
-                    permissions-remove-all.label,
-                    permissions-button-cancel.label,
-                    permissions-button-ok.label,
-                    permissions-exceptions-tracking-protection-window.title,
-                    permissions-exceptions-tracking-protection-desc,
-                  "/>
-        </hbox>
-        <!-- 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="changeBlockList"
                   class="accessory-button"
                   flex="1"
                   data-l10n-id="tracking-change-block-list"
                   preference="pref.privacy.disable_button.change_blocklist"
                   search-l10n-ids="blocklist-window.title, blocklist-desc, blocklist-button-cancel.label, blocklist-button-ok.label"/>
         </hbox>
       </vbox>
--- a/browser/components/preferences/in-content/tests/browser_extension_controlled.js
+++ b/browser/components/preferences/in-content/tests/browser_extension_controlled.js
@@ -511,114 +511,146 @@ add_task(async function testExtensionCon
   // Don't finalize the current store since it will write out the bad data.
   await ExtensionSettingsStore._reloadFile(false);
 
   is(ExtensionSettingsStore.getSetting("prefs", "homepage_override"), null,
      "The ExtensionSettingsStore is left empty.");
 });
 
 add_task(async function testExtensionControlledTrackingProtection() {
+  const CB_UI_PREF = "browser.contentblocking.ui.enabled";
   const TP_PREF = "privacy.trackingprotection.enabled";
   const TP_DEFAULT = false;
   const EXTENSION_ID = "@set_tp";
-  const CONTROLLED_LABEL_ID = "trackingProtectionExtensionContentLabel";
-  const CONTROLLED_BUTTON_ID = "trackingProtectionExtensionContentButton";
+  const CONTROLLED_LABEL_ID = {
+    old: "trackingProtectionExtensionContentLabel",
+    new: "contentBlockingTrackingProtectionExtensionContentLabel",
+  };
+  const CONTROLLED_BUTTON_ID = {
+    old: "trackingProtectionExtensionContentButton",
+    new: "contentBlockingTrackingProtectionExtensionContentButton"
+  };
+  const DISABLE_BUTTON_ID = {
+    old: "disableTrackingProtectionExtension",
+    new: "contentBlockingDisableTrackingProtectionExtension"
+  };
 
   let tpEnabledPref = () => Services.prefs.getBoolPref(TP_PREF);
 
   await SpecialPowers.pushPrefEnv(
-    {"set": [[TP_PREF, TP_DEFAULT]]});
+    {"set": [[TP_PREF, TP_DEFAULT], [CB_UI_PREF, true]]});
 
   function background() {
     browser.privacy.websites.trackingProtectionMode.set({value: "always"});
   }
 
   function verifyState(isControlled) {
     is(tpEnabledPref(), isControlled, "TP pref is set to the expected value.");
 
-    let controlledLabel = doc.getElementById(CONTROLLED_LABEL_ID);
+    let controlledLabel = doc.getElementById(CONTROLLED_LABEL_ID[uiType]);
+    let controlledButton = doc.getElementById(CONTROLLED_BUTTON_ID[uiType]);
 
     is(controlledLabel.hidden, !isControlled, "The extension controlled row's visibility is as expected.");
     is(controlledButton.hidden, !isControlled, "The disable extension button's visibility is as expected.");
     if (isControlled) {
       let controlledDesc = controlledLabel.querySelector("description");
       Assert.deepEqual(doc.l10n.getAttributes(controlledDesc), {
         id: "extension-controlled-websites-tracking-protection-mode",
         args: {
           name: "set_tp",
         }
       }, "The user is notified that an extension is controlling TP.");
     }
 
-    for (let element of doc.querySelectorAll("#trackingProtectionRadioGroup > radio")) {
-      is(element.disabled, isControlled, "TP controls are enabled.");
+    if (uiType === "old") {
+      for (let element of doc.querySelectorAll("#trackingProtectionRadioGroup > radio")) {
+        is(element.disabled, isControlled, "TP controls are enabled.");
+      }
+      is(doc.querySelector("#trackingProtectionDesc > label").disabled,
+         isControlled,
+         "TP control label is enabled.");
+    } else {
+      is(doc.getElementById("trackingProtectionMenu").disabled,
+         isControlled,
+         "TP control is enabled.");
     }
-    is(doc.querySelector("#trackingProtectionDesc > label").disabled,
-       isControlled,
-       "TP control label is enabled.");
   }
 
   async function disableViaClick() {
-    let labelId = CONTROLLED_LABEL_ID;
+    let labelId = CONTROLLED_LABEL_ID[uiType];
+    let disableId = DISABLE_BUTTON_ID[uiType];
     let controlledLabel = doc.getElementById(labelId);
 
     let enableMessageShown = waitForEnableMessage(labelId);
-    doc.getElementById("disableTrackingProtectionExtension").click();
+    doc.getElementById(disableId).click();
     await enableMessageShown;
 
     // The user is notified how to enable the extension.
     let controlledDesc = controlledLabel.querySelector("description");
     is(doc.l10n.getAttributes(controlledDesc.querySelector("label")).id,
       "extension-controlled-enable",
       "The user is notified of how to enable the extension again");
 
     // The user can dismiss the enable instructions.
     let hidden = waitForMessageHidden(labelId);
     controlledLabel.querySelector("image:last-of-type").click();
     await hidden;
   }
 
   async function reEnableExtension(addon) {
-    let controlledMessageShown = waitForMessageShown(CONTROLLED_LABEL_ID);
+    let controlledMessageShown = waitForMessageShown(CONTROLLED_LABEL_ID[uiType]);
     await addon.enable();
     await controlledMessageShown;
   }
 
+  let uiType = "new";
+
   await openPreferencesViaOpenPreferencesAPI("panePrivacy", {leaveOpen: true});
   let doc = gBrowser.contentDocument;
 
   is(gBrowser.currentURI.spec, "about:preferences#privacy",
    "#privacy should be in the URI for about:preferences");
 
-  let controlledButton = doc.getElementById(CONTROLLED_BUTTON_ID);
-
   verifyState(false);
 
   // Install an extension that sets Tracking Protection.
   let extension = ExtensionTestUtils.loadExtension({
     useAddonManager: "permanent",
     manifest: {
       name: "set_tp",
       applications: {gecko: {id: EXTENSION_ID}},
       permissions: ["privacy"],
     },
     background,
   });
 
-  let messageShown = waitForMessageShown(CONTROLLED_LABEL_ID);
+  let messageShown = waitForMessageShown(CONTROLLED_LABEL_ID[uiType]);
   await extension.startup();
   await messageShown;
   let addon = await AddonManager.getAddonByID(EXTENSION_ID);
 
   verifyState(true);
 
   await disableViaClick();
 
   verifyState(false);
 
+  // Switch to the "old" Tracking Protection UI.
+  uiType = "old";
+  Services.prefs.setBoolPref(CB_UI_PREF, false);
+
+  let browserLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, "about:preferences#privacy");
+  gBrowser.selectedBrowser.reload();
+  await browserLoaded;
+  is(gBrowser.currentURI.spec, "about:preferences#privacy",
+   "#privacy should be in the URI for about:preferences");
+  doc = gBrowser.contentDocument;
+
+  verifyState(false);
+
   await reEnableExtension(addon);
 
   verifyState(true);
 
   await disableViaClick();
 
   verifyState(false);
 
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -784,16 +784,47 @@ addressbar-locbar-bookmarks-option =
     .label = Bookmarks
     .accesskey = k
 addressbar-locbar-openpage-option =
     .label = Open tabs
     .accesskey = O
 
 addressbar-suggestions-settings = Change preferences for search engine suggestions
 
+## Privacy Section - Content Blocking
+
+content-blocking-header = Content Blocking
+
+content-blocking-desc = Block third-party content, like ads or code, that can slow your browsing and track you around the web. Customize your settings for the best balance of protection and performance.
+
+content-blocking-learn-more = Learn More
+
+content-blocking-toggle-on =
+  .tooltiptext = Turn Off Content Blocking
+content-blocking-toggle-off =
+  .tooltiptext = Turn On Content Blocking
+
+content-blocking-toggle-label-on = ON
+  .accesskey = O
+content-blocking-toggle-label-off = OFF
+  .accesskey = O
+
+content-blocking-category-label = Choose what to 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-change-blocklist = Change Block List…
+
 ## Privacy Section - Tracking
 
 tracking-header = Tracking Protection
 
 tracking-desc = Tracking Protection blocks online trackers that collect your browsing data across multiple websites. <a data-l10n-name="learn-more">Learn more about Tracking Protection and your privacy</a>
 
 tracking-mode-label = Use Tracking Protection to block known trackers
 
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/controlcenter/trackers.svg
@@ -0,0 +1,9 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity">
+  <path d="M4.414,0C2.8.2,1.766,2.521,2.045,4.742c.143,1.133.828,1.675,1.028,3.707.019.194.036.37.052.536A18.56,18.56,0,0,0,6.6,8.8,4.988,4.988,0,0,1,6.545,7.79a25.267,25.267,0,0,0,.432-3.949C6.724,1.833,5.853-.177,4.414,0Z"/>
+  <path d="M6.766,9.771A18.373,18.373,0,0,1,3.854,10c-.221,0-.432,0-.636-.01a8.709,8.709,0,0,0,.2,1.351c.165.659.71,1.8,1.944,1.635A1.675,1.675,0,0,0,6.942,10.9C6.9,10.506,6.834,10.134,6.766,9.771Z"/>
+  <path d="M11.588,3c-1.439-.18-2.311,1.83-2.564,3.838a25.267,25.267,0,0,0,.432,3.949,4.938,4.938,0,0,1-.058,1,18.45,18.45,0,0,0,3.478.193c.016-.167.033-.343.052-.537.2-2.032.885-2.574,1.028-3.707C14.235,5.521,13.2,3.2,11.588,3Z"/>
+  <path d="M9.236,12.767c-.069.365-.136.737-.177,1.13a1.675,1.675,0,0,0,1.579,2.079c1.235.16,1.779-.976,1.944-1.635a8.594,8.594,0,0,0,.2-1.35c-.2.005-.4.009-.606.009A18.258,18.258,0,0,1,9.236,12.767Z"/>
+</svg>
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -379,16 +379,20 @@ button > hbox > label {
   line-height: 30px;
 }
 
 #allowSmartSize {
   margin-top: 0;
   margin-bottom: 4px;
 }
 
+#trackingProtectionBox {
+  margin-top: 16px;
+}
+
 #doNotTrackLearnMoreBox {
   margin-top: 32px;
 }
 
 #trackingProtectionAdvancedSettings {
   margin-inline-start: 15px;
 }
 
--- a/browser/themes/shared/incontentprefs/privacy.css
+++ b/browser/themes/shared/incontentprefs/privacy.css
@@ -1,12 +1,15 @@
 /* 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 {
   height: 20px;
   width: 20px;
   vertical-align: middle;
   -moz-context-properties: fill;
   fill: currentColor;
 }
 
@@ -24,8 +27,108 @@
 
 .desktop-notification-icon {
   list-style-image: url(chrome://browser/skin/notification-icons/desktop-notification.svg);
 }
 
 .midi-icon {
   list-style-image: url(chrome://browser/skin/notification-icons/midi.svg);
 }
+
+/* Content Blocking */
+
+#contentBlockingLearnMore {
+  margin-top: 4px !important;
+}
+
+#contentBlockingCheckboxContainer {
+  margin: 16px 0;
+}
+
+#contentBlockingToggleLabel {
+  line-height: 34px;
+}
+
+#contentBlockingCheckbox {
+  visibility: collapse;
+}
+
+/* If shown, the button-box takes up some space that results
+ * in unwanted changes in right-hand margin when toggling
+ * this button. We just hide it since we don't need it. */
+#contentBlockingToggle > .button-box {
+  display: none;
+}
+
+#contentBlockingToggle {
+  -moz-appearance: none;
+  box-sizing: border-box;
+  min-width: 48px;
+  max-width: 48px;
+  min-height: 30px;
+  max-height: 30px;
+  border-radius: 20px;
+  background-color: #d7d7db;
+  border: 1px solid transparent;
+  margin-top: 2px;
+  margin-bottom: 2px;
+  margin-inline-start: 1px;
+  margin-inline-end: 7px;
+  padding: 5px;
+  transition: padding .2s ease;
+}
+
+#contentBlockingToggle::before {
+  position: relative;
+  display: block;
+  content: "";
+  width: 18px;
+  height: 18px;
+  border-radius: 10px;
+  background: white;
+}
+
+#contentBlockingCheckbox[checked="true"] + #contentBlockingToggle {
+  background-color: #2292d0;
+  border: 1px solid #2292d0;
+  /* Push the toggle to the right. */
+  padding-inline-start: 23px;
+}
+
+#contentBlockingToggle:hover,
+#contentBlockingToggle:-moz-focusring {
+  border: 1px solid #b1b1b3;
+}
+
+#contentBlockingCheckbox[checked] + #contentBlockingToggle:hover,
+#contentBlockingCheckbox[checked] + #contentBlockingToggle:-moz-focusring {
+  background-color: #0a84ff;
+}
+
+.content-blocking-category {
+  margin: 8px 0;
+}
+
+.content-blocking-category-icon {
+  padding: 4px;
+}
+
+.content-blocking-category-labels {
+  padding-inline-start: 4px;
+}
+
+#changeBlockListLink {
+  font-size: 90%;
+}
+
+.content-blocking-category-description {
+  font-size: 90%;
+  opacity: 0.6;
+}
+
+.content-blocking-category-menu {
+  margin-top: -2px;
+  min-width: 250px;
+}
+
+.tracking-protection-icon {
+  list-style-image: url(chrome://browser/skin/controlcenter/trackers.svg);
+}
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -22,16 +22,17 @@
   skin/classic/browser/addons/addon-install-error.svg          (../shared/addons/addon-install-error.svg)
   skin/classic/browser/addons/addon-install-installed.svg      (../shared/addons/addon-install-installed.svg)
   skin/classic/browser/addons/addon-install-warning.svg        (../shared/addons/addon-install-warning.svg)
   skin/classic/browser/controlcenter/conn-not-secure.svg       (../shared/controlcenter/conn-not-secure.svg)
   skin/classic/browser/controlcenter/connection.svg            (../shared/controlcenter/connection.svg)
   skin/classic/browser/controlcenter/mcb-disabled.svg          (../shared/controlcenter/mcb-disabled.svg)
   skin/classic/browser/controlcenter/extension.svg             (../shared/controlcenter/extension.svg)
   skin/classic/browser/controlcenter/permissions.svg           (../shared/controlcenter/permissions.svg)
+  skin/classic/browser/controlcenter/trackers.svg              (../shared/controlcenter/trackers.svg)
   skin/classic/browser/controlcenter/tracking-protection.svg   (../shared/controlcenter/tracking-protection.svg)
   skin/classic/browser/controlcenter/tracking-protection-disabled.svg   (../shared/controlcenter/tracking-protection-disabled.svg)
   skin/classic/browser/controlcenter/warning-gray.svg          (../shared/controlcenter/warning-gray.svg)
   skin/classic/browser/controlcenter/warning-yellow.svg        (../shared/controlcenter/warning-yellow.svg)
   skin/classic/browser/customizableui/empty-overflow-panel.png     (../shared/customizableui/empty-overflow-panel.png)
   skin/classic/browser/customizableui/empty-overflow-panel@2x.png  (../shared/customizableui/empty-overflow-panel@2x.png)
   skin/classic/browser/customizableui/density-compact.svg      (../shared/customizableui/density-compact.svg)
   skin/classic/browser/customizableui/density-normal.svg       (../shared/customizableui/density-normal.svg)