Bug 1570466 - Enhanced Tracking Protection card hides content when some/all protections are off in Custom r=fluent-reviewers,johannh,flod
☠☠ backed out by 9b2ebbf56577 ☠ ☠
authorErica Wright <ewright@mozilla.com>
Tue, 29 Oct 2019 03:26:10 +0000
changeset 499575 84d18816918c18b14d05591481d1351765448144
parent 499574 0ba412a99ed5e79f4ce5ca6c34d67e590c455835
child 499576 9b2ebbf56577ec44c8c2304564bd003e1ccf0231
push id99057
push userewright@mozilla.com
push dateTue, 29 Oct 2019 03:27:15 +0000
treeherderautoland@84d18816918c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfluent-reviewers, johannh, flod
bugs1570466
milestone72.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 1570466 - Enhanced Tracking Protection card hides content when some/all protections are off in Custom r=fluent-reviewers,johannh,flod Differential Revision: https://phabricator.services.mozilla.com/D44713
browser/components/protections/content/protections.css
browser/components/protections/content/protections.html
browser/components/protections/content/protections.js
browser/components/protections/test/browser/browser_protections_report_ui.js
browser/locales/en-US/browser/protections.ftl
toolkit/components/remotepagemanager/MessagePort.jsm
--- a/browser/components/protections/content/protections.css
+++ b/browser/components/protections/content/protections.css
@@ -72,16 +72,17 @@ h2 {
 }
 
 .card-header .wrapper {
   display: grid;
   grid-template-columns: repeat(7, 1fr);
   align-items: center;
 }
 
+#manage-protections,
 .card-header > button,
 #get-proxy-extension-link,
 #open-about-logins-button,
 #sign-up-for-monitor-link {
   grid-area: 1 / 5 / 1 / -1;
   margin: 0;
   font-size: 0.95em;
   cursor: pointer;
@@ -95,16 +96,17 @@ h2 {
   padding-inline-end: 15px;
 }
 
 .card.has-logins .wrapper div:nth-child(1) {
   grid-area: 1 / 1 / 1 / -1;
 }
 
 /* We want to hide certain components depending on its state. */
+#manage-protections,
 .etp-card .icon.dark,
 a.hidden,
 .loading .card-body.hidden,
 .lockwise-card.hidden,
 #lockwise-body-content .has-logins.hidden,
 #lockwise-body-content .no-logins.hidden,
 .monitor-card.hidden,
 .monitor-card.no-logins .card-body,
@@ -175,16 +177,29 @@ a.hidden,
 .card .content {
   margin-bottom: 0px;
   margin-top: 5px;
   font-size: .93em;
   cursor: default;
   color: var(--in-content-deemphasized-text);
 }
 
+.custom-not-blocking .content {
+  margin-bottom: 5px;
+}
+
+.etp-card.custom-not-blocking .card-body,
+.etp-card.custom-not-blocking #protection-details {
+  display: none;
+}
+
+.etp-card.custom-not-blocking #manage-protections {
+  display: block;
+}
+
 #protection-details {
   padding-inline-start: 24px;
   padding-inline-end: 3px;
   -moz-context-properties: fill;
   fill: currentColor;
   background: url("chrome://browser/skin/settings.svg") no-repeat 3px 3px;
   font-size: 0.75em;
   cursor: default;
@@ -665,36 +680,40 @@ label[for="tab-cryptominer"]:hover ~ #hi
   width: 70px;
   height: 48px;
 }
 
 #password-warning {
   grid-column: 2 / -1;
 }
 
+#manage-protections,
 #sign-up-for-monitor-link,
 #get-proxy-extension-link {
   -moz-appearance: button;
   background-color: var(--blue-60);
   border: 1px solid transparent;
   border-radius: 2px;
   text-decoration: none;
   color: #fff;
 }
 
+#manage-protections:active,
 #sign-up-for-monitor-link:active,
 #get-proxy-extension-link:active {
   background-color: var(--blue-80) !important;
 }
 
+#manage-protections:hover,
 #sign-up-for-monitor-link:hover,
 #get-proxy-extension-link:hover {
   background-color: var(--blue-70);
 }
 
+##manage-protections:focus,
 #sign-up-for-monitor-link:focus,
 #get-proxy-extension-link:focus {
   box-shadow: 0 0 0 1px #0a84ff inset, 0 0 0 1px #0a84ff, 0 0 0 4px rgba(10, 132, 255, 0.3);
   outline: none;
 }
 
 .monitor-card.loading::after,
 .lockwise-card.loading::after {
--- a/browser/components/protections/content/protections.html
+++ b/browser/components/protections/content/protections.html
@@ -25,20 +25,23 @@
 
   <body>
     <div id="report-content">
       <h1 id="report-title" data-l10n-id="protection-report-content-title"></h1>
       <div class="card card-no-hover etp-card">
         <div class="card-header">
           <img class="icon light" src="chrome://browser/content/logos/tracking-protection.svg"/>
           <img class="icon dark" src="chrome://browser/content/logos/tracking-protection-dark-theme.svg"/>
-          <div>
-            <h2 class="card-title" data-l10n-id="etp-card-title"></h2>
-            <p class="content" data-l10n-id="etp-card-content"></p>
-            <p id="protection-details" role="link" tabindex="0"></p>
+          <div class="wrapper">
+            <div>
+              <h2 class="card-title" data-l10n-id="etp-card-title"></h2>
+              <p id="etp-card-content" class="content" data-l10n-id="etp-card-content"></p>
+              <p id="protection-details" role="link" tabindex="0"></p>
+            </div>
+            <a target="_blank" id="manage-protections" data-l10n-id="protection-report-manage-protections"></a>
           </div>
         </div>
         <div class="card-body">
           <div class="body-wrapper">
             <p id="graph-week-summary"></p>
             <div id="graph-wrapper">
               <div id="graph" role="table" aria-labelledby="graphLegendDescription"></div>
               <div id="legend">
--- a/browser/components/protections/content/protections.js
+++ b/browser/components/protections/content/protections.js
@@ -24,23 +24,26 @@ document.addEventListener("DOMContentLoa
     "cryptominer",
     "fingerprinter",
     "tracker",
     "cookie",
     "social",
   ];
 
   let protectionDetails = document.getElementById("protection-details");
+  let manageProtections = document.getElementById("manage-protections");
   let protectionDetailsEvtHandler = evt => {
     if (evt.keyCode == evt.DOM_VK_RETURN || evt.type == "click") {
       RPMSendAsyncMessage("OpenContentBlockingPreferences");
     }
   };
   protectionDetails.addEventListener("click", protectionDetailsEvtHandler);
   protectionDetails.addEventListener("keypress", protectionDetailsEvtHandler);
+  manageProtections.addEventListener("click", protectionDetailsEvtHandler);
+  manageProtections.addEventListener("keypress", protectionDetailsEvtHandler);
 
   let cbCategory = RPMGetStringPref("browser.contentblocking.category");
   if (cbCategory == "custom") {
     protectionDetails.setAttribute(
       "data-l10n-id",
       "protection-report-header-details-custom"
     );
   } else if (cbCategory == "strict") {
@@ -200,41 +203,103 @@ document.addEventListener("DOMContentLoa
       learnMoreLink.href = RPMGetFormatURLPref(
         `browser.contentblocking.report.${type}.url`
       );
       learnMoreLink.addEventListener("click", () => {
         document.sendTelemetryEvent("click", "trackers_about_link", type);
       });
     }
 
-    // Hide the trackers tab if the user is in standard and
-    // has no recorded trackers blocked.
-    if (weekTypeCounts.tracker == 0 && cbCategory == "standard") {
+    let blockingCookies =
+      RPMGetIntPref("network.cookie.cookieBehavior", 0) != 0;
+    let cryptominingEnabled = RPMGetBoolPref(
+      "privacy.trackingprotection.cryptomining.enabled",
+      false
+    );
+    let fingerprintingEnabled = RPMGetBoolPref(
+      "privacy.trackingprotection.fingerprinting.enabled",
+      false
+    );
+    let tpEnabled = RPMGetBoolPref("privacy.trackingprotection.enabled", false);
+    let socialTracking = RPMGetBoolPref(
+      "privacy.trackingprotection.socialtracking.enabled",
+      false
+    );
+    let socialCookies = RPMGetBoolPref(
+      "privacy.socialtracking.block_cookies.enabled",
+      false
+    );
+    let socialEnabled =
+      socialCookies && (blockingCookies || (tpEnabled && socialTracking));
+    let notBlocking =
+      !blockingCookies &&
+      !cryptominingEnabled &&
+      !fingerprintingEnabled &&
+      !tpEnabled &&
+      !socialEnabled;
+
+    // User has turned off all blocking, show a different card.
+    if (notBlocking) {
+      document
+        .getElementById("etp-card-content")
+        .setAttribute(
+          "data-l10n-id",
+          "protection-report-etp-card-content-custom-not-blocking"
+        );
+      document.querySelector(".etp-card").classList.add("custom-not-blocking");
+    }
+
+    // Hide each type of tab if the user has no recorded
+    // trackers of that type blocked and blocking of that type is off.
+    if (weekTypeCounts.tracker == 0 && !tpEnabled) {
       legend.style.gridTemplateAreas = legend.style.gridTemplateAreas.replace(
         "tracker",
         ""
       );
       let radio = document.getElementById("tab-tracker");
       radio.setAttribute("disabled", true);
       document.querySelector("#tab-tracker ~ label").style.display = "none";
     }
-    let socialEnabled = RPMGetBoolPref(
-      "privacy.socialtracking.block_cookies.enabled",
-      false
-    );
-
     if (weekTypeCounts.social == 0 && !socialEnabled) {
       legend.style.gridTemplateAreas = legend.style.gridTemplateAreas.replace(
         "social",
         ""
       );
       let radio = document.getElementById("tab-social");
       radio.setAttribute("disabled", true);
       document.querySelector("#tab-social ~ label").style.display = "none";
     }
+    if (weekTypeCounts.cookie == 0 && !blockingCookies) {
+      legend.style.gridTemplateAreas = legend.style.gridTemplateAreas.replace(
+        "cookie",
+        ""
+      );
+      let radio = document.getElementById("tab-cookie");
+      radio.setAttribute("disabled", true);
+      document.querySelector("#tab-cookie ~ label").style.display = "none";
+    }
+    if (weekTypeCounts.cryptominer == 0 && !cryptominingEnabled) {
+      legend.style.gridTemplateAreas = legend.style.gridTemplateAreas.replace(
+        "cryptominer",
+        ""
+      );
+      let radio = document.getElementById("tab-cryptominer");
+      radio.setAttribute("disabled", true);
+      document.querySelector("#tab-cryptominer ~ label").style.display = "none";
+    }
+    if (weekTypeCounts.fingerprinter == 0 && !fingerprintingEnabled) {
+      legend.style.gridTemplateAreas = legend.style.gridTemplateAreas.replace(
+        "fingerprinter",
+        ""
+      );
+      let radio = document.getElementById("tab-fingerprinter");
+      radio.setAttribute("disabled", true);
+      document.querySelector("#tab-fingerprinter ~ label").style.display =
+        "none";
+    }
 
     let firstRadio = document.querySelector("input:not(:disabled)");
     firstRadio.checked = true;
     document.body.setAttribute("focuseddatatype", firstRadio.dataset.type);
 
     addListeners();
   };
 
--- a/browser/components/protections/test/browser/browser_protections_report_ui.js
+++ b/browser/components/protections/test/browser/browser_protections_report_ui.js
@@ -172,320 +172,327 @@ add_task(async function test_graph_displ
     count: 8,
     timestamp: date,
   });
 
   let tab = await BrowserTestUtils.openNewForegroundTab({
     url: "about:protections",
     gBrowser,
   });
-  await ContentTask.spawn(tab.linkedBrowser, {}, async function() {
+  await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
     const DATA_TYPES = [
       "cryptominer",
       "fingerprinter",
       "tracker",
       "cookie",
       "social",
     ];
     let allBars = null;
     await ContentTaskUtils.waitForCondition(() => {
       allBars = content.document.querySelectorAll(".graph-bar");
       return allBars.length;
     }, "The graph has been built");
 
-    is(allBars.length, 7, "7 bars have been found on the graph");
+    Assert.equal(allBars.length, 7, "7 bars have been found on the graph");
 
     // For accessibility, test if the graph is a table
     // and has a correct column count (number of data types + total + day)
-    is(
+    Assert.equal(
       content.document.getElementById("graph").getAttribute("role"),
       "table",
       "Graph is an accessible table"
     );
-    is(
+    Assert.equal(
       content.document.getElementById("graph").getAttribute("aria-colcount"),
       DATA_TYPES.length + 2,
       "Table has the right number of columns"
     );
-    is(
+    Assert.equal(
       content.document.getElementById("graph").getAttribute("aria-labelledby"),
       "graphLegendDescription",
       "Table has an accessible label"
     );
 
     // today has each type
     // yesterday will have no tracking cookies
     // 2 days ago will have no fingerprinters
     // 3 days ago will have no cryptominers
     // 4 days ago will have no trackers
     // 5 days ago will have no social (when we add social)
     // 6 days ago will be empty
-    is(
+    Assert.equal(
       allBars[6].querySelectorAll(".inner-bar").length,
       DATA_TYPES.length,
       "today has all of the data types shown"
     );
-    is(allBars[6].getAttribute("role"), "row", "Today has the correct role");
-    is(
+    Assert.equal(
+      allBars[6].getAttribute("role"),
+      "row",
+      "Today has the correct role"
+    );
+    Assert.equal(
       allBars[6].getAttribute("aria-owns"),
       "day0 count0 cryptominer0 fingerprinter0 tracker0 cookie0 social0",
       "Row has the columns in the right order"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".tracker-bar").style.height,
       "10%",
       "trackers take 10%"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".tracker-bar").parentNode.getAttribute("role"),
       "cell",
       "Trackers have the correct role"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".tracker-bar").getAttribute("role"),
       "img",
       "Tracker bar has the correct image role"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".tracker-bar").getAttribute("aria-label"),
       "1 tracking content (10%)",
       "Trackers have the correct accessible text"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".cryptominer-bar").style.height,
       "20%",
       "cryptominers take 20%"
     );
-    is(
+    Assert.equal(
       allBars[6]
         .querySelector(".cryptominer-bar")
         .parentNode.getAttribute("role"),
       "cell",
       "Cryptominers have the correct role"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".cryptominer-bar").getAttribute("role"),
       "img",
       "Cryptominer bar has the correct image role"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".cryptominer-bar").getAttribute("aria-label"),
       "2 cryptominers (20%)",
       "Cryptominers have the correct accessible label"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".fingerprinter-bar").style.height,
       "20%",
       "fingerprinters take 20%"
     );
-    is(
+    Assert.equal(
       allBars[6]
         .querySelector(".fingerprinter-bar")
         .parentNode.getAttribute("role"),
       "cell",
       "Fingerprinters have the correct role"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".fingerprinter-bar").getAttribute("role"),
       "img",
       "Fingerprinter bar has the correct image role"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".fingerprinter-bar").getAttribute("aria-label"),
       "2 fingerprinters (20%)",
       "Fingerprinters have the correct accessible label"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".cookie-bar").style.height,
       "40%",
       "cross site tracking cookies take 40%"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".cookie-bar").parentNode.getAttribute("role"),
       "cell",
       "cross site tracking cookies have the correct role"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".cookie-bar").getAttribute("role"),
       "img",
       "Cross site tracking cookies bar has the correct image role"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".cookie-bar").getAttribute("aria-label"),
       "4 cross-site tracking cookies (40%)",
       "cross site tracking cookies have the correct accessible label"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".social-bar").style.height,
       "10%",
       "social trackers take 10%"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".social-bar").parentNode.getAttribute("role"),
       "cell",
       "social trackers have the correct role"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".social-bar").getAttribute("role"),
       "img",
       "social tracker bar has the correct image role"
     );
-    is(
+    Assert.equal(
       allBars[6].querySelector(".social-bar").getAttribute("aria-label"),
       "1 social media tracker (10%)",
       "social trackers have the correct accessible text"
     );
 
-    is(
+    Assert.equal(
       allBars[5].querySelectorAll(".inner-bar").length,
       DATA_TYPES.length - 1,
       "1 day ago is missing one type"
     );
-    ok(
+    Assert.ok(
       !allBars[5].querySelector(".cookie-bar"),
       "there is no cross site tracking cookie section 1 day ago."
     );
-    is(
+    Assert.equal(
       allBars[5].getAttribute("aria-owns"),
       "day1 count1 cryptominer1 fingerprinter1 tracker1 social1",
       "Row has the columns in the right order"
     );
 
-    is(
+    Assert.equal(
       allBars[4].querySelectorAll(".inner-bar").length,
       DATA_TYPES.length - 1,
       "2 days ago is missing one type"
     );
-    ok(
+    Assert.ok(
       !allBars[4].querySelector(".fingerprinter-bar"),
       "there is no fingerprinter section 1 day ago."
     );
-    is(
+    Assert.equal(
       allBars[4].getAttribute("aria-owns"),
       "day2 count2 cryptominer2 tracker2 cookie2 social2",
       "Row has the columns in the right order"
     );
 
-    is(
+    Assert.equal(
       allBars[3].querySelectorAll(".inner-bar").length,
       DATA_TYPES.length - 1,
       "3 days ago is missing one type"
     );
-    ok(
+    Assert.ok(
       !allBars[3].querySelector(".cryptominer-bar"),
       "there is no cryptominer section 1 day ago."
     );
-    is(
+    Assert.equal(
       allBars[3].getAttribute("aria-owns"),
       "day3 count3 fingerprinter3 tracker3 cookie3 social3",
       "Row has the columns in the right order"
     );
 
-    is(
+    Assert.equal(
       allBars[2].querySelectorAll(".inner-bar").length,
       DATA_TYPES.length - 1,
       "4 days ago is missing one type"
     );
-    ok(
+    Assert.ok(
       !allBars[2].querySelector(".tracker-bar"),
       "there is no tracker section 1 day ago."
     );
-    is(
+    Assert.equal(
       allBars[2].getAttribute("aria-owns"),
       "day4 count4 cryptominer4 fingerprinter4 cookie4 social4",
       "Row has the columns in the right order"
     );
 
-    is(
+    Assert.equal(
       allBars[1].querySelectorAll(".inner-bar").length,
       DATA_TYPES.length - 1,
       "5 days ago is missing one type"
     );
-    ok(
+    Assert.ok(
       !allBars[1].querySelector(".social-bar"),
       "there is no social section 1 day ago."
     );
-    is(
+    Assert.equal(
       allBars[1].getAttribute("aria-owns"),
       "day5 count5 cryptominer5 fingerprinter5 tracker5 cookie5",
       "Row has the columns in the right order"
     );
 
-    is(
+    Assert.equal(
       allBars[0].querySelectorAll(".inner-bar").length,
       0,
       "6 days ago has no content"
     );
-    ok(allBars[0].classList.contains("empty"), "6 days ago is an empty bar");
-    is(
+    Assert.ok(
+      allBars[0].classList.contains("empty"),
+      "6 days ago is an empty bar"
+    );
+    Assert.equal(
       allBars[0].getAttribute("aria-owns"),
       "day6 ",
       "Row has the columns in the right order"
     );
 
     // Check that each tab has the correct aria-labelledby and aria-describedby
     // values. This helps screen readers know what type of tracker the reported
     // tab number is referencing.
     const socialTab = content.document.getElementById("tab-social");
-    is(
+    Assert.equal(
       socialTab.getAttribute("aria-labelledby"),
       "socialLabel socialTitle",
       "aria-labelledby attribute is socialLabel socialTitle"
     );
-    is(
+    Assert.equal(
       socialTab.getAttribute("aria-describedby"),
       "socialContent",
       "aria-describedby attribute is socialContent"
     );
 
     const cookieTab = content.document.getElementById("tab-cookie");
-    is(
+    Assert.equal(
       cookieTab.getAttribute("aria-labelledby"),
       "cookieLabel cookieTitle",
       "aria-labelledby attribute is cookieLabel cookieTitle"
     );
-    is(
+    Assert.equal(
       cookieTab.getAttribute("aria-describedby"),
       "cookieContent",
       "aria-describedby attribute is cookieContent"
     );
 
     const trackerTab = content.document.getElementById("tab-tracker");
-    is(
+    Assert.equal(
       trackerTab.getAttribute("aria-labelledby"),
       "trackerLabel trackerTitle",
       "aria-labelledby attribute is trackerLabel trackerTitle"
     );
-    is(
+    Assert.equal(
       trackerTab.getAttribute("aria-describedby"),
       "trackerContent",
       "aria-describedby attribute is trackerContent"
     );
 
     const fingerprinterTab = content.document.getElementById(
       "tab-fingerprinter"
     );
-    is(
+    Assert.equal(
       fingerprinterTab.getAttribute("aria-labelledby"),
       "fingerprinterLabel fingerprinterTitle",
       "aria-labelledby attribute is fingerprinterLabel fingerprinterTitle"
     );
-    is(
+    Assert.equal(
       fingerprinterTab.getAttribute("aria-describedby"),
       "fingerprinterContent",
       "aria-describedby attribute is fingerprinterContent"
     );
 
     const cryptominerTab = content.document.getElementById("tab-cryptominer");
-    is(
+    Assert.equal(
       cryptominerTab.getAttribute("aria-labelledby"),
       "cryptominerLabel cryptominerTitle",
       "aria-labelledby attribute is cryptominerLabel cryptominerTitle"
     );
-    is(
+    Assert.equal(
       cryptominerTab.getAttribute("aria-describedby"),
       "cryptominerContent",
       "aria-describedby attribute is cryptominerContent"
     );
   });
 
   // Use the TrackingDBService API to delete the data.
   await TrackingDBService.clearAll();
@@ -499,42 +506,219 @@ add_task(async function test_graph_displ
 // Ensure that the string in the ETP card header is changing when we change
 // the category pref.
 add_task(async function test_etp_header_string() {
   Services.prefs.setStringPref("browser.contentblocking.category", "standard");
   let tab = await BrowserTestUtils.openNewForegroundTab({
     url: "about:protections",
     gBrowser,
   });
-  await ContentTask.spawn(tab.linkedBrowser, {}, async function() {
+  await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
     await ContentTaskUtils.waitForCondition(() => {
       let l10nID = content.document
         .querySelector("#protection-details")
         .getAttribute("data-l10n-id");
       return l10nID == "protection-report-header-details-standard";
     }, "The standard string is showing");
   });
 
   Services.prefs.setStringPref("browser.contentblocking.category", "strict");
   await reloadTab(tab);
-  await ContentTask.spawn(tab.linkedBrowser, {}, async function() {
+  await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
     await ContentTaskUtils.waitForCondition(() => {
       let l10nID = content.document
         .querySelector("#protection-details")
         .getAttribute("data-l10n-id");
       return l10nID == "protection-report-header-details-strict";
     }, "The strict string is showing");
   });
 
   Services.prefs.setStringPref("browser.contentblocking.category", "custom");
   await reloadTab(tab);
-  await ContentTask.spawn(tab.linkedBrowser, {}, async function() {
+  await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
     await ContentTaskUtils.waitForCondition(() => {
       let l10nID = content.document
         .querySelector("#protection-details")
         .getAttribute("data-l10n-id");
       return l10nID == "protection-report-header-details-custom";
     }, "The custom string is showing");
   });
 
   Services.prefs.setStringPref("browser.contentblocking.category", "standard");
   BrowserTestUtils.removeTab(tab);
 });
+
+// Ensure that each type of tracker is hidden from the graph if there are no recorded
+// trackers of that type and the user has chosen to not block that type.
+add_task(async function test_etp_custom_settings() {
+  Services.prefs.setStringPref("browser.contentblocking.category", "strict");
+  // hide cookies from the graph
+  Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+  let tab = await BrowserTestUtils.openNewForegroundTab({
+    url: "about:protections",
+    gBrowser,
+  });
+
+  await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
+    await ContentTaskUtils.waitForCondition(() => {
+      let legend = content.document.getElementById("legend");
+      return ContentTaskUtils.is_visible(legend);
+    }, "The legend is visible");
+
+    let label = content.document.getElementById("cookieLabel");
+    Assert.ok(ContentTaskUtils.is_hidden(label), "Cookie Label is hidden");
+
+    label = content.document.getElementById("trackerLabel");
+    Assert.ok(ContentTaskUtils.is_visible(label), "Tracker Label is visible");
+    label = content.document.getElementById("socialLabel");
+    Assert.ok(ContentTaskUtils.is_visible(label), "Social Label is visible");
+    label = content.document.getElementById("cryptominerLabel");
+    Assert.ok(
+      ContentTaskUtils.is_visible(label),
+      "Cryptominer Label is visible"
+    );
+    label = content.document.getElementById("fingerprinterLabel");
+    Assert.ok(
+      ContentTaskUtils.is_visible(label),
+      "Fingerprinter Label is visible"
+    );
+  });
+  BrowserTestUtils.removeTab(tab);
+
+  // hide ad trackers from the graph
+  Services.prefs.setBoolPref("privacy.trackingprotection.enabled", false);
+  tab = await BrowserTestUtils.openNewForegroundTab({
+    url: "about:protections",
+    gBrowser,
+  });
+  await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
+    await ContentTaskUtils.waitForCondition(() => {
+      let legend = content.document.getElementById("legend");
+      return ContentTaskUtils.is_visible(legend);
+    }, "The legend is visible");
+
+    let label = content.document.querySelector("#trackerLabel");
+    Assert.ok(ContentTaskUtils.is_hidden(label), "Tracker Label is hidden");
+
+    label = content.document.querySelector("#socialLabel");
+    Assert.ok(ContentTaskUtils.is_hidden(label), "Social Label is hidden");
+  });
+  BrowserTestUtils.removeTab(tab);
+
+  // hide social from the graph
+  Services.prefs.setBoolPref(
+    "privacy.trackingprotection.socialtracking.enabled",
+    false
+  );
+  Services.prefs.setBoolPref(
+    "privacy.socialtracking.block_cookies.enabled",
+    false
+  );
+  tab = await BrowserTestUtils.openNewForegroundTab({
+    url: "about:protections",
+    gBrowser,
+  });
+  await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
+    await ContentTaskUtils.waitForCondition(() => {
+      let legend = content.document.getElementById("legend");
+      return ContentTaskUtils.is_visible(legend);
+    }, "The legend is visible");
+
+    let label = content.document.querySelector("#socialLabel");
+    Assert.ok(ContentTaskUtils.is_hidden(label), "Social Label is hidden");
+  });
+  BrowserTestUtils.removeTab(tab);
+
+  // hide fingerprinting from the graph
+  Services.prefs.setBoolPref(
+    "privacy.trackingprotection.fingerprinting.enabled",
+    false
+  );
+  tab = await BrowserTestUtils.openNewForegroundTab({
+    url: "about:protections",
+    gBrowser,
+  });
+  await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
+    await ContentTaskUtils.waitForCondition(() => {
+      let legend = content.document.getElementById("legend");
+      return ContentTaskUtils.is_visible(legend);
+    }, "The legend is visible");
+
+    let label = content.document.querySelector("#fingerprinterLabel");
+    Assert.ok(
+      ContentTaskUtils.is_hidden(label),
+      "Fingerprinter Label is hidden"
+    );
+  });
+  BrowserTestUtils.removeTab(tab);
+
+  // hide cryptomining from the graph
+  Services.prefs.setBoolPref(
+    "privacy.trackingprotection.cryptomining.enabled",
+    false
+  );
+  // Turn fingerprinting on so that all protectionsare not turned off, otherwise we will get a special card.
+  Services.prefs.setBoolPref(
+    "privacy.trackingprotection.fingerprinting.enabled",
+    true
+  );
+  tab = await BrowserTestUtils.openNewForegroundTab({
+    url: "about:protections",
+    gBrowser,
+  });
+  await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
+    await ContentTaskUtils.waitForCondition(() => {
+      let legend = content.document.getElementById("legend");
+      return ContentTaskUtils.is_visible(legend);
+    }, "The legend is visible");
+
+    let label = content.document.querySelector("#cryptominerLabel");
+    Assert.ok(ContentTaskUtils.is_hidden(label), "Cryptominer Label is hidden");
+  });
+  Services.prefs.clearUserPref("browser.contentblocking.category");
+  Services.prefs.clearUserPref(
+    "privacy.trackingprotection.fingerprinting.enabled"
+  );
+  Services.prefs.clearUserPref(
+    "privacy.trackingprotection.cryptomining.enabled"
+  );
+  Services.prefs.clearUserPref("privacy.trackingprotection.enabled");
+  Services.prefs.clearUserPref("network.cookie.cookieBehavior");
+
+  BrowserTestUtils.removeTab(tab);
+});
+
+// Ensure that the Custom manage Protections card is shown if the user has all protections turned off.
+add_task(async function test_etp_custom_protections_off() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.contentblocking.category", "custom"],
+      ["network.cookie.cookieBehavior", 0], // not blocking
+      ["privacy.trackingprotection.cryptomining.enabled", false], // not blocking
+      ["privacy.trackingprotection.fingerprinting.enabled", false],
+      ["privacy.trackingprotection.enabled", false],
+      ["privacy.trackingprotection.socialtracking.enabled", false],
+      ["privacy.socialtracking.block_cookies.enabled", false],
+    ],
+  });
+
+  let tab = await BrowserTestUtils.openNewForegroundTab({
+    url: "about:protections",
+    gBrowser,
+  });
+
+  await SpecialPowers.spawn(tab.linkedBrowser, [], async function() {
+    await ContentTaskUtils.waitForCondition(() => {
+      let etpCard = content.document.querySelector(".etp-card");
+      return etpCard.classList.contains("custom-not-blocking");
+    }, "The custom protections warning card is showing");
+
+    let manageProtectionsCard = content.document.querySelector(
+      "#manage-protections"
+    );
+    Assert.ok(
+      ContentTaskUtils.is_visible(manageProtectionsCard),
+      "Button to manage protections is displayed"
+    );
+  });
+  Services.prefs.setStringPref("browser.contentblocking.category", "standard");
+  BrowserTestUtils.removeTab(tab);
+});
--- a/browser/locales/en-US/browser/protections.ftl
+++ b/browser/locales/en-US/browser/protections.ftl
@@ -29,16 +29,18 @@ protection-report-header-details-strict 
   .title = Go to Privacy Settings
 protection-report-header-details-custom = Protection Level is set to <b>Custom</b>
   .title = Go to Privacy Settings
 protection-report-page-title = Privacy Protections
 protection-report-content-title = Privacy Protections
 
 etp-card-title = Enhanced Tracking Protection
 etp-card-content = Trackers follow you around online to collect information about your browsing habits and interests. { -brand-short-name } blocks many of these trackers and other malicious scripts.
+protection-report-etp-card-content-custom-not-blocking = All protections are currently turned off. Choose which trackers to block by managing your { -brand-short-name } protections settings.
+protection-report-manage-protections = Manage Settings
 
 # This string is used to label the X axis of a graph. Other days of the week are generated via Intl.DateTimeFormat,
 # capitalization for this string should match the output for your locale.
 graph-today = Today
 
 # This string is used to describe the graph for screenreader users.
 graph-legend-description = A graph containing the total number of each type of tracker blocked this week.
 
--- a/toolkit/components/remotepagemanager/MessagePort.jsm
+++ b/toolkit/components/remotepagemanager/MessagePort.jsm
@@ -72,25 +72,30 @@ let RPMAccessManager = {
       isWindowPrivate: ["yes"],
     },
     "about:protections": {
       getBoolPref: [
         "browser.contentblocking.report.lockwise.enabled",
         "browser.contentblocking.report.monitor.enabled",
         "privacy.socialtracking.block_cookies.enabled",
         "browser.contentblocking.report.proxy.enabled",
+        "privacy.trackingprotection.cryptomining.enabled",
+        "privacy.trackingprotection.fingerprinting.enabled",
+        "privacy.trackingprotection.enabled",
+        "privacy.trackingprotection.socialtracking.enabled",
       ],
       getStringPref: [
         "browser.contentblocking.category",
         "browser.contentblocking.report.lockwise.url",
         "browser.contentblocking.report.monitor.url",
         "browser.contentblocking.report.monitor.sign_in_url",
         "browser.contentblocking.report.manage_devices.url",
         "browser.contentblocking.report.proxy_extension.url",
       ],
+      getIntPref: ["network.cookie.cookieBehavior"],
       getFormatURLPref: [
         "browser.contentblocking.report.monitor.how_it_works.url",
         "browser.contentblocking.report.lockwise.how_it_works.url",
         "browser.contentblocking.report.social.url",
         "browser.contentblocking.report.cookie.url",
         "browser.contentblocking.report.tracker.url",
         "browser.contentblocking.report.fingerprinter.url",
         "browser.contentblocking.report.cryptominer.url",