Merge mozilla-central to inbound. a=merge CLOSED TREE
authorGurzau Raul <rgurzau@mozilla.com>
Sat, 08 Dec 2018 23:38:27 +0200
changeset 449673 e9ea0dbc015be66d996deeb7c6e71088488dcea1
parent 449672 387f770bf58c175b69b5803b3c0d4a7ea55d18c4 (current diff)
parent 449643 09493e80dbe7c1486eb3c93960cc81f73c8c5356 (diff)
child 449674 a24d661d8362f388f002a47b3a0d18a265590b65
push idunknown
push userunknown
push dateunknown
reviewersmerge
milestone65.0a1
Merge mozilla-central to inbound. a=merge CLOSED TREE
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1516,16 +1516,17 @@ pref("dom.storage_access.enabled", true)
 
 pref("dom.storage_access.auto_grants", true);
 pref("dom.storage_access.max_concurrent_auto_grants", 5);
 
 // Define a set of default features for the Content Blocking UI.
 pref("browser.contentblocking.trackingprotection.control-center.ui.enabled", true);
 pref("browser.contentblocking.rejecttrackers.control-center.ui.enabled", true);
 
+pref("browser.contentblocking.control-center.ui.showBlockedLabels", false);
 pref("browser.contentblocking.control-center.ui.showAllowedLabels", false);
 
 // Enable the Report Breakage UI on Nightly and Beta but not on Release yet.
 #ifdef EARLY_BETA_OR_EARLIER
 pref("browser.contentblocking.reportBreakage.enabled", true);
 #else
 pref("browser.contentblocking.reportBreakage.enabled", false);
 #endif
--- a/browser/base/content/browser-contentblocking.js
+++ b/browser/base/content/browser-contentblocking.js
@@ -73,17 +73,17 @@ var TrackingProtection = {
     this.enabledInPrivateWindows =
       Services.prefs.getBoolPref(this.PREF_ENABLED_IN_PRIVATE_WINDOWS);
     this.updateCategoryLabel();
   },
 
   updateCategoryLabel() {
     let label;
     if (this.enabled) {
-      label = "contentBlocking.trackers.blocked.label";
+      label = ContentBlocking.showBlockedLabels ? "contentBlocking.trackers.blocking.label" : null;
     } else {
       label = ContentBlocking.showAllowedLabels ? "contentBlocking.trackers.allowed.label" : null;
     }
     this.categoryLabel.textContent = label ? gNavigatorBundle.getString(label) : "";
   },
 
   isBlocking(state) {
     return (state & Ci.nsIWebProgressListener.STATE_BLOCKED_TRACKING_CONTENT) != 0;
@@ -110,16 +110,41 @@ var TrackingProtection = {
     let fragment = document.createDocumentFragment();
     for (let [origin, actions] of Object.entries(contentBlockingLog)) {
       let listItem = await this._createListItem(origin, actions);
       if (listItem) {
         fragment.appendChild(listItem);
       }
     }
 
+    // If we don't have trackers we would usually not show the menu item
+    // allowing the user to show the sub-panel. However, in the edge case
+    // that we annotated trackers on the page using the strict list but did
+    // not detect trackers on the page using the basic list, we currently
+    // still show the panel. To reduce the confusion, tell the user that we have
+    // not detected any tracker.
+    if (fragment.childNodes.length == 0) {
+      let emptyBox = document.createXULElement("vbox");
+      let emptyImage = document.createXULElement("image");
+      emptyImage.classList.add("identity-popup-content-blocking-trackersView-empty-image");
+      emptyImage.classList.add("tracking-protection-icon");
+
+      let emptyLabel = document.createXULElement("label");
+      emptyLabel.classList.add("identity-popup-content-blocking-empty-label");
+      emptyLabel.textContent = gNavigatorBundle.getString("contentBlocking.trackersView.empty.label");
+
+      emptyBox.appendChild(emptyImage);
+      emptyBox.appendChild(emptyLabel);
+      fragment.appendChild(emptyBox);
+
+      this.subViewList.classList.add("empty");
+    } else {
+      this.subViewList.classList.remove("empty");
+    }
+
     // This might have taken a while. Only update the list if we're still on the same page.
     if (previousURI == gBrowser.currentURI.spec &&
         previousWindow == gBrowser.selectedBrowser.innerWindowID) {
       this.subViewList.textContent = "";
       this.subViewList.append(fragment);
     }
   },
 
@@ -246,26 +271,26 @@ var ThirdPartyCookies = {
       return "cookierestrictions";
     }
   },
 
   updateCategoryLabel() {
     let label;
     switch (this.behaviorPref) {
     case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
-      label = "contentBlocking.cookies.3rdPartyBlocked.label";
+      label = ContentBlocking.showBlockedLabels ? "contentBlocking.cookies.blocking3rdParty.label" : null;
       break;
     case Ci.nsICookieService.BEHAVIOR_REJECT:
-      label = "contentBlocking.cookies.allBlocked.label";
+      label = ContentBlocking.showBlockedLabels ? "contentBlocking.cookies.blockingAll.label" : null;
       break;
     case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
-      label = "contentBlocking.cookies.unvisitedBlocked.label";
+      label = ContentBlocking.showBlockedLabels ? "contentBlocking.cookies.blockingUnvisited.label" : null;
       break;
     case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
-      label = "contentBlocking.cookies.trackersBlocked.label";
+      label = ContentBlocking.showBlockedLabels ? "contentBlocking.cookies.blockingTrackers.label" : null;
       break;
     default:
       Cu.reportError(`Error: Unknown cookieBehavior pref observed: ${this.behaviorPref}`);
       // fall through
     case Ci.nsICookieService.BEHAVIOR_ACCEPT:
       label = ContentBlocking.showAllowedLabels ? "contentBlocking.cookies.allowed.label" : null;
       break;
     }
@@ -299,27 +324,36 @@ var ThirdPartyCookies = {
     let contentBlockingLogJSON = await gBrowser.selectedBrowser.getContentBlockingLog();
     let contentBlockingLog = JSON.parse(contentBlockingLogJSON);
 
     let categories = this._processContentBlockingLog(contentBlockingLog);
 
     this.subViewList.textContent = "";
 
     for (let category of ["firstParty", "trackers", "thirdParty"]) {
-      if (categories[category].length) {
-        let box = document.createXULElement("vbox");
-        let label = document.createXULElement("label");
-        label.className = "identity-popup-cookiesView-list-header";
-        label.textContent = gNavigatorBundle.getString(`contentBlocking.cookiesView.${category}.label`);
-        box.appendChild(label);
-        for (let info of categories[category]) {
-          box.appendChild(this._createListItem(info));
-        }
-        this.subViewList.appendChild(box);
+      let box = document.createXULElement("vbox");
+      let label = document.createXULElement("label");
+      label.className = "identity-popup-cookiesView-list-header";
+      label.textContent = gNavigatorBundle.getString(`contentBlocking.cookiesView.${category}.label`);
+      box.appendChild(label);
+
+      for (let info of categories[category]) {
+        box.appendChild(this._createListItem(info));
       }
+
+      // If the category is empty, add a label noting that to the user.
+      if (categories[category].length == 0) {
+        let emptyLabel = document.createXULElement("label");
+        emptyLabel.classList.add("identity-popup-content-blocking-empty-label");
+        emptyLabel.textContent =
+          gNavigatorBundle.getString(`contentBlocking.cookiesView.${category}.empty.label`);
+        box.appendChild(emptyLabel);
+      }
+
+      this.subViewList.appendChild(box);
     }
   },
 
   _hasException(origin) {
     for (let perm of Services.perms.getAllForPrincipal(gBrowser.contentPrincipal)) {
       if (perm.type == "3rdPartyStorage^" + origin || perm.type.startsWith("3rdPartyStorage^" + origin + "^")) {
         return true;
       }
@@ -484,16 +518,17 @@ var ContentBlocking = {
   // If the user ignores the doorhanger, we stop showing it after some time.
   MAX_INTROS: 20,
   PREF_ANIMATIONS_ENABLED: "toolkit.cosmeticAnimations.enabled",
   PREF_REPORT_BREAKAGE_ENABLED: "browser.contentblocking.reportBreakage.enabled",
   PREF_REPORT_BREAKAGE_URL: "browser.contentblocking.reportBreakage.url",
   PREF_INTRO_COUNT_CB: "browser.contentblocking.introCount",
   PREF_CB_CATEGORY: "browser.contentblocking.category",
   PREF_SHOW_ALLOWED_LABELS: "browser.contentblocking.control-center.ui.showAllowedLabels",
+  PREF_SHOW_BLOCKED_LABELS: "browser.contentblocking.control-center.ui.showBlockedLabels",
   content: null,
   icon: null,
   activeTooltipText: null,
   disabledTooltipText: null,
 
   get prefIntroCount() {
     return this.PREF_INTRO_COUNT_CB;
   },
@@ -541,27 +576,27 @@ var ContentBlocking = {
     } catch (e) {
       // Getting the hostPort for about: and file: URIs fails, but TP doesn't work with
       // these URIs anyway, so just return null here.
       return null;
     }
   },
 
   init() {
-    let $ = selector => document.querySelector(selector);
-    this.content = $("#identity-popup-content-blocking-content");
-    this.icon = $("#tracking-protection-icon");
-    this.iconBox = $("#tracking-protection-icon-box");
-    this.animatedIcon = $("#tracking-protection-icon-animatable-image");
+    let $ = id => document.getElementById(id);
+    this.content = $("identity-popup-content-blocking-content");
+    this.icon = $("tracking-protection-icon");
+    this.iconBox = $("tracking-protection-icon-box");
+    this.animatedIcon = $("tracking-protection-icon-animatable-image");
     this.animatedIcon.addEventListener("animationend", () => this.iconBox.removeAttribute("animate"));
 
-    this.identityPopupMultiView = $("#identity-popup-multiView");
-    this.reportBreakageButton = $("#identity-popup-content-blocking-report-breakage");
-    this.reportBreakageURL = $("#identity-popup-breakageReportView-collection-url");
-    this.reportBreakageLearnMore = $("#identity-popup-breakageReportView-learn-more");
+    this.identityPopupMultiView = $("identity-popup-multiView");
+    this.reportBreakageButton = $("identity-popup-content-blocking-report-breakage");
+    this.reportBreakageURL = $("identity-popup-breakageReportView-collection-url");
+    this.reportBreakageLearnMore = $("identity-popup-breakageReportView-learn-more");
 
     let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
     this.reportBreakageLearnMore.href = baseURL + "blocking-breakage";
 
     this.updateAnimationsEnabled = () => {
       this.iconBox.toggleAttribute("animationsenabled",
         Services.prefs.getBoolPref(this.PREF_ANIMATIONS_ENABLED, false));
     };
@@ -571,16 +606,22 @@ var ContentBlocking = {
         blocker.init();
       }
     }
 
     this.updateAnimationsEnabled();
 
     Services.prefs.addObserver(this.PREF_ANIMATIONS_ENABLED, this.updateAnimationsEnabled);
 
+    XPCOMUtils.defineLazyPreferenceGetter(this, "showBlockedLabels",
+      this.PREF_SHOW_BLOCKED_LABELS, false, () => {
+        for (let blocker of this.blockers) {
+          blocker.updateCategoryLabel();
+        }
+    });
     XPCOMUtils.defineLazyPreferenceGetter(this, "showAllowedLabels",
       this.PREF_SHOW_ALLOWED_LABELS, false, () => {
         for (let blocker of this.blockers) {
           blocker.updateCategoryLabel();
         }
     });
     XPCOMUtils.defineLazyPreferenceGetter(this, "reportBreakageEnabled",
       this.PREF_REPORT_BREAKAGE_ENABLED, false);
--- a/browser/base/content/test/trackingUI/browser_trackingUI_categories.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_categories.js
@@ -65,27 +65,30 @@ add_task(async function testCategoryLabe
     await TestUtils.waitForCondition(() => appMenuCategoryLabel.value ==
       gNavigatorBundle.getString("contentBlocking.category.custom"));
     is(appMenuCategoryLabel.value, gNavigatorBundle.getString("contentBlocking.category.custom"),
       "The appMenuCategory label has been changed to custom");
   });
 });
 
 add_task(async function testSubcategoryLabels() {
-  SpecialPowers.pushPrefEnv({set: [["browser.contentblocking.control-center.ui.showAllowedLabels", true]]});
+  SpecialPowers.pushPrefEnv({set: [
+    ["browser.contentblocking.control-center.ui.showAllowedLabels", true],
+    ["browser.contentblocking.control-center.ui.showBlockedLabels", true],
+  ]});
 
   await BrowserTestUtils.withNewTab("http://www.example.com", async function() {
     let categoryLabel =
       document.getElementById("identity-popup-content-blocking-tracking-protection-state-label");
 
     Services.prefs.setBoolPref(TP_PREF, true);
     await TestUtils.waitForCondition(() => categoryLabel.textContent ==
-      gNavigatorBundle.getString("contentBlocking.trackers.blocked.label"),
+      gNavigatorBundle.getString("contentBlocking.trackers.blocking.label"),
       "The category label has updated correctly");
-    is(categoryLabel.textContent, gNavigatorBundle.getString("contentBlocking.trackers.blocked.label"));
+    is(categoryLabel.textContent, gNavigatorBundle.getString("contentBlocking.trackers.blocking.label"));
 
     Services.prefs.setBoolPref(TP_PREF, false);
     await TestUtils.waitForCondition(() => categoryLabel.textContent ==
       gNavigatorBundle.getString("contentBlocking.trackers.allowed.label"),
       "The category label has updated correctly");
     is(categoryLabel.textContent, gNavigatorBundle.getString("contentBlocking.trackers.allowed.label"));
 
     categoryLabel =
@@ -94,31 +97,31 @@ add_task(async function testSubcategoryL
     Services.prefs.setIntPref(TPC_PREF, Ci.nsICookieService.BEHAVIOR_ACCEPT);
     await TestUtils.waitForCondition(() => categoryLabel.textContent ==
       gNavigatorBundle.getString("contentBlocking.cookies.allowed.label"),
       "The category label has updated correctly");
     is(categoryLabel.textContent, gNavigatorBundle.getString("contentBlocking.cookies.allowed.label"));
 
     Services.prefs.setIntPref(TPC_PREF, Ci.nsICookieService.BEHAVIOR_REJECT);
     await TestUtils.waitForCondition(() => categoryLabel.textContent ==
-      gNavigatorBundle.getString("contentBlocking.cookies.allBlocked.label"),
+      gNavigatorBundle.getString("contentBlocking.cookies.blockingAll.label"),
       "The category label has updated correctly");
-    is(categoryLabel.textContent, gNavigatorBundle.getString("contentBlocking.cookies.allBlocked.label"));
+    is(categoryLabel.textContent, gNavigatorBundle.getString("contentBlocking.cookies.blockingAll.label"));
 
     Services.prefs.setIntPref(TPC_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN);
     await TestUtils.waitForCondition(() => categoryLabel.textContent ==
-      gNavigatorBundle.getString("contentBlocking.cookies.3rdPartyBlocked.label"),
+      gNavigatorBundle.getString("contentBlocking.cookies.blocking3rdParty.label"),
       "The category label has updated correctly");
-    is(categoryLabel.textContent, gNavigatorBundle.getString("contentBlocking.cookies.3rdPartyBlocked.label"));
+    is(categoryLabel.textContent, gNavigatorBundle.getString("contentBlocking.cookies.blocking3rdParty.label"));
 
     Services.prefs.setIntPref(TPC_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER);
     await TestUtils.waitForCondition(() => categoryLabel.textContent ==
-      gNavigatorBundle.getString("contentBlocking.cookies.trackersBlocked.label"),
+      gNavigatorBundle.getString("contentBlocking.cookies.blockingTrackers.label"),
       "The category label has updated correctly");
-    is(categoryLabel.textContent, gNavigatorBundle.getString("contentBlocking.cookies.trackersBlocked.label"));
+    is(categoryLabel.textContent, gNavigatorBundle.getString("contentBlocking.cookies.blockingTrackers.label"));
 
     Services.prefs.setIntPref(TPC_PREF, Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN);
     await TestUtils.waitForCondition(() => categoryLabel.textContent ==
-      gNavigatorBundle.getString("contentBlocking.cookies.unvisitedBlocked.label"),
+      gNavigatorBundle.getString("contentBlocking.cookies.blockingUnvisited.label"),
       "The category label has updated correctly");
-    is(categoryLabel.textContent, gNavigatorBundle.getString("contentBlocking.cookies.unvisitedBlocked.label"));
+    is(categoryLabel.textContent, gNavigatorBundle.getString("contentBlocking.cookies.blockingUnvisited.label"));
   });
 });
--- a/browser/base/content/test/trackingUI/browser_trackingUI_cookies_subview.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_cookies_subview.js
@@ -29,20 +29,20 @@ async function assertSitesListed(tracker
     let cookiesView = document.getElementById("identity-popup-cookiesView");
     let viewShown = BrowserTestUtils.waitForEvent(cookiesView, "ViewShown");
     categoryItem.click();
     await viewShown;
 
     ok(true, "Cookies view was shown");
 
     let listHeaders = cookiesView.querySelectorAll(".identity-popup-cookiesView-list-header");
-    is(listHeaders.length, 1, "We have 1 list header");
-    is(listHeaders[0].textContent,
-       gNavigatorBundle.getString(`contentBlocking.cookiesView.trackers.label`),
-       "The list header is for tracking cookies.");
+    is(listHeaders.length, 3, "We have 3 list headers");
+
+    let emptyLabels = cookiesView.querySelectorAll(".identity-popup-content-blocking-empty-label");
+    is(emptyLabels.length, 2, "We have 2 empty labels");
 
     let listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
     is(listItems.length, 1, "We have 1 cookie in the list");
 
     let listItem = listItems[0];
     let label = listItem.querySelector(".identity-popup-content-blocking-list-host-label");
     is(label.value, "http://trackertest.org", "Has an item for trackertest.org");
     ok(BrowserTestUtils.is_visible(listItem), "List item is visible");
@@ -68,21 +68,18 @@ async function assertSitesListed(tracker
     is(result, undefined, "No securityChange events should be received");
 
     viewShown = BrowserTestUtils.waitForEvent(cookiesView, "ViewShown");
     categoryItem.click();
     await viewShown;
 
     ok(true, "Cookies view was shown");
 
-    listHeaders = cookiesView.querySelectorAll(".identity-popup-cookiesView-list-header");
-    is(listHeaders.length, 2, "We now have 2 list headers");
-    is(listHeaders[1].textContent,
-       gNavigatorBundle.getString(`contentBlocking.cookiesView.thirdParty.label`),
-       "The new list header is for third party cookies.");
+    emptyLabels = cookiesView.querySelectorAll(".identity-popup-content-blocking-empty-label");
+    is(emptyLabels.length, 1, "We have 1 empty label");
 
     listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
     is(listItems.length, 2, "We have 2 cookies in the list");
 
     listItem = listItems[1];
     label = listItem.querySelector(".identity-popup-content-blocking-list-host-label");
     is(label.value, "https://test1.example.org", "Has an item for test1.example.org");
     ok(BrowserTestUtils.is_visible(listItem), "List item is visible");
@@ -106,21 +103,18 @@ async function assertSitesListed(tracker
     is(result, undefined, "No securityChange events should be received");
 
     viewShown = BrowserTestUtils.waitForEvent(cookiesView, "ViewShown");
     categoryItem.click();
     await viewShown;
 
     ok(true, "Cookies view was shown");
 
-    listHeaders = cookiesView.querySelectorAll(".identity-popup-cookiesView-list-header");
-    is(listHeaders.length, 3, "We now have 3 list headers");
-    is(listHeaders[0].textContent,
-       gNavigatorBundle.getString(`contentBlocking.cookiesView.firstParty.label`),
-       "The new list header is for first party cookies.");
+    emptyLabels = cookiesView.querySelectorAll(".identity-popup-content-blocking-empty-label");
+    is(emptyLabels.length, 0, "We have 0 empty label");
 
     listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
     is(listItems.length, 3, "We have 2 cookies in the list");
 
     listItem = listItems[0];
     label = listItem.querySelector(".identity-popup-content-blocking-list-host-label");
     is(label.value, "http://not-tracking.example.com", "Has an item for the first party");
     ok(BrowserTestUtils.is_visible(listItem), "List item is visible");
@@ -159,22 +153,16 @@ add_task(async function testCookiesSubVi
     ok(BrowserTestUtils.is_visible(categoryItem), "TP category item is visible");
     let cookiesView = document.getElementById("identity-popup-cookiesView");
     let viewShown = BrowserTestUtils.waitForEvent(cookiesView, "ViewShown");
     categoryItem.click();
     await viewShown;
 
     ok(true, "Cookies view was shown");
 
-    let listHeaders = cookiesView.querySelectorAll(".identity-popup-cookiesView-list-header");
-    is(listHeaders.length, 1, "We have 1 list header");
-    is(listHeaders[0].textContent,
-       gNavigatorBundle.getString(`contentBlocking.cookiesView.trackers.label`),
-       "The list header is for tracking cookies.");
-
     let listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
     is(listItems.length, 1, "We have 1 cookie in the list");
 
     let listItem = listItems[0];
     let label = listItem.querySelector(".identity-popup-content-blocking-list-host-label");
     is(label.value, "http://trackertest.org", "Has an item for trackertest.org");
     ok(BrowserTestUtils.is_visible(listItem), "List item is visible");
     ok(listItem.classList.contains("allowed"), "Indicates whether the cookie was blocked or allowed");
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -490,42 +490,70 @@ contentBlocking.category.strict=Strict
 contentBlocking.category.custom=Custom
 
 # LOCALIZATION NOTE (contentBlocking.trackers.allowed.label):
 #   This label signals that this type of content blocking is turned
 #   OFF and is not blocking tracker content, so this is not
 #   a positive thing. It forms the end of the (imaginary) sentence
 #   "Trackers [are] Allowed"
 contentBlocking.trackers.allowed.label=Allowed
-# LOCALIZATION NOTE (contentBlocking.trackers.blocked.label):
+# LOCALIZATION NOTE (contentBlocking.trackers.blocking.label):
 #   This label signals that this type of content blocking is turned
 #   ON and is successfully blocking tracker content, so this is
-#   a positive thing. It forms the end of the (imaginary) sentence
-#   "Trackers [are] Blocked"
-contentBlocking.trackers.blocked.label=Blocked
+#   a positive thing. However, it is important to note that there is no
+#   guarantee that we _actually_ blocked anything, hence we present it
+#   in the present tense, not the past tense in English. The idea is that
+#   past tense would imply that something was blocked, while present
+#   tense expresses that we are waiting for trackers to load
+#   and will block them as appropriate. This concept may not directly
+#   translate to your language, but it is still preferable if the translation
+#   would not make it seem like the blocking had already happened.
+#   So in full context this word could be part of the sentence:
+#   "[Firefox is] Blocking [trackers when they get loaded.]"
+contentBlocking.trackers.blocking.label=Blocking
 
 # LOCALIZATION NOTE (contentBlocking.trackersView.blocked.label):
 #   This label is shown next to a tracker in the trackers subview.
 #   It forms the end of the (imaginary) sentence "www.example.com [was] Blocked"
 contentBlocking.trackersView.blocked.label=Blocked
 
+contentBlocking.trackersView.empty.label=None detected on this site
+
 # LOCALIZATION NOTE (contentBlocking.cookies.allowed.label):
 #   This label signals that this type of content blocking is turned
 #   OFF and is not blocking tracker content, so this is not
 #   a positive thing. It forms the end of the (imaginary) sentence
 #   "Cookies [are] Allowed"
 contentBlocking.cookies.allowed.label=Allowed
-contentBlocking.cookies.trackersBlocked.label=Tracking Cookies Blocked
-contentBlocking.cookies.3rdPartyBlocked.label=Third-Party Cookies Blocked
-contentBlocking.cookies.unvisitedBlocked.label=Unvisited Site Cookies Blocked
-contentBlocking.cookies.allBlocked.label=All Cookies Blocked
+# LOCALIZATION NOTE (contentBlocking.cookies.blockingTrackers.label, contentBlocking.cookies.blocking3rdParty.label,
+#   contentBlocking.cookies.blockingUnvisited.label,contentBlocking.cookies.blockingAll.label):
+# See localization note for contentBlocking.trackers.blocking.label to get recommendations on translating "Blocking".
+contentBlocking.cookies.blockingTrackers.label=Blocking Tracking Cookies
+contentBlocking.cookies.blocking3rdParty.label=Blocking Third-Party Cookies
+contentBlocking.cookies.blockingUnvisited.label=Blocking Unvisited Site Cookies
+contentBlocking.cookies.blockingAll.label=Blocking All Cookies
 
 contentBlocking.cookiesView.firstParty.label=From This Site
+# LOCALIZATION NOTE (contentBlocking.cookiesView.firstParty.empty.label):
+#  This references the header from contentBlocking.cookiesView.firstParty.label:
+#  "[Cookies] From This Site: None detected on this site".
+contentBlocking.cookiesView.firstParty.empty.label=None detected on this site
+
 contentBlocking.cookiesView.trackers.label=Tracking Cookies
+# LOCALIZATION NOTE (contentBlocking.cookiesView.trackers.empty.label):
+#  This references the header from contentBlocking.cookiesView.trackers.label:
+#  "Tracking Cookies: None detected on this site".
+contentBlocking.cookiesView.trackers.empty.label=None detected on this site
+
 contentBlocking.cookiesView.thirdParty.label=Third-Party Cookies
+# LOCALIZATION NOTE (contentBlocking.cookiesView.thirdParty.empty.label):
+#  This references the header from contentBlocking.cookiesView.thirdParty.label:
+#  "Third-Party Cookies: None detected on this site".
+contentBlocking.cookiesView.thirdParty.empty.label=None detected on this site
+
 # LOCALIZATION NOTE (contentBlocking.cookiesView.allowed.label):
 #   This label is shown next to a cookie origin in the cookies subview.
 #   It forms the end of the (imaginary) sentence "www.example.com [was] Allowed"
 contentBlocking.cookiesView.allowed.label=Allowed
 # LOCALIZATION NOTE (contentBlocking.cookiesView.blocked.label):
 #   This label is shown next to a cookie origin in the cookies subview.
 #   It forms the end of the (imaginary) sentence "www.example.com [was] Blocked"
 contentBlocking.cookiesView.blocked.label=Blocked
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -156,16 +156,17 @@
   display: inline;
   padding-inline-end: 25px;
   padding-inline-start: 0px;
   color: var(--panel-disabled-color);
 }
 
 /* CONTENT */
 
+.identity-popup-content-blocking-empty-label,
 #tracking-protection-preferences-button > .toolbarbutton-text,
 .identity-popup-footer,
 .tracking-protection-button,
 #identity-popup-trackersView-strict-info > label,
 .identity-popup-cookiesView-list-header,
 .identity-popup-content-blocking-list-item > label,
 #identity-popup-mainView-panel-header > label,
 #identity-popup-trackersView > .panel-header,
@@ -434,16 +435,39 @@ description#identity-popup-content-verif
 
 /* This subview could get filled with a lot of trackers, set a maximum size
  * and allow it to scroll vertically.*/
 #identity-popup-cookiesView,
 #identity-popup-trackersView {
   max-height: 600px;
 }
 
+#identity-popup-trackersView-list.empty {
+  -moz-box-align: center;
+  -moz-box-pack: center;
+}
+
+.identity-popup-content-blocking-empty-label {
+  margin-inline-start: 0;
+  color: var(--panel-disabled-color);
+}
+
+.identity-popup-content-blocking-trackersView-empty-image {
+  width: 48px;
+  height: 48px;
+  -moz-context-properties: fill, fill-opacity;
+  margin-bottom: 16px;
+}
+
+#identity-popup-cookiesView .identity-popup-content-blocking-empty-label {
+  margin-inline-start: 24px;
+  margin-top: 2px;
+  margin-bottom: 4px;
+}
+
 .identity-popup-cookiesView-list-header {
   color: var(--panel-disabled-color);
   margin: 5px 0;
 }
 
 .identity-popup-content-blocking-list {
   padding: 5px 20px;
   -moz-box-flex: 1;
--- a/js/src/jit-test/tests/realms/basic.js
+++ b/js/src/jit-test/tests/realms/basic.js
@@ -38,8 +38,19 @@ function testSystemNonSystemRealms() {
     try {
         systemRealm = newGlobal({systemPrincipal: true, sameCompartmentAs: this});
     } catch(e) {
         ex = e;
     }
     assertEq(ex.toString().includes("non-system realms"), true);
 }
 testSystemNonSystemRealms();
+
+function testNewObjectCache() {
+    // NewObjectCache lookup based on the proto should not return a cross-realm
+    // object.
+    var g = newGlobal({sameCompartmentAs: this});
+    var o1 = g.evaluate("Object.create(Math)");
+    var o2 = Object.create(g.Math);
+    assertEq(objectGlobal(o1), g);
+    assertEq(objectGlobal(o2), this);
+}
+testNewObjectCache();
--- a/js/src/vm/Caches-inl.h
+++ b/js/src/vm/Caches-inl.h
@@ -48,16 +48,22 @@ inline NativeObject* NewObjectCache::new
   NativeObject* templateObj =
       reinterpret_cast<NativeObject*>(&entry->templateObject);
 
   // Do an end run around JSObject::group() to avoid doing AutoUnprotectCell
   // on the templateObj, which is not a GC thing and can't use
   // runtimeFromAnyThread.
   ObjectGroup* group = templateObj->group_;
 
+  // If we did the lookup based on the proto we might have a group/object from a
+  // different (same-compartment) realm, so we have to do a realm check.
+  if (group->realm() != cx->realm()) {
+    return nullptr;
+  }
+
   MOZ_ASSERT(!group->hasUnanalyzedPreliminaryObjects());
 
   {
     AutoSweepObjectGroup sweepGroup(group);
     if (group->shouldPreTenure(sweepGroup)) {
       heap = gc::TenuredHeap;
     }
   }
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -2343,16 +2343,17 @@ static inline JSFunction* NewFunctionClo
   return clone;
 }
 
 JSFunction* js::CloneFunctionReuseScript(
     JSContext* cx, HandleFunction fun, HandleObject enclosingEnv,
     gc::AllocKind allocKind /* = FUNCTION */,
     NewObjectKind newKind /* = GenericObject */,
     HandleObject proto /* = nullptr */) {
+  MOZ_ASSERT(cx->realm() == fun->realm());
   MOZ_ASSERT(NewFunctionEnvironmentIsWellFormed(cx, enclosingEnv));
   MOZ_ASSERT(fun->isInterpreted());
   MOZ_ASSERT(!fun->isBoundFunction());
   MOZ_ASSERT(CanReuseScriptForClone(cx->realm(), fun, enclosingEnv));
 
   RootedFunction clone(cx,
                        NewFunctionClone(cx, fun, newKind, allocKind, proto));
   if (!clone) {
--- a/js/src/vm/JSFunction.h
+++ b/js/src/vm/JSFunction.h
@@ -643,19 +643,25 @@ class JSFunction : public js::NativeObje
       return lazyScript()->isAsync();
     }
     if (hasScript()) {
       return nonLazyScript()->isAsync();
     }
     return false;
   }
 
-  void setScript(JSScript* script_) { mutableScript() = script_; }
+  void setScript(JSScript* script) {
+    MOZ_ASSERT(realm() == script->realm());
+    mutableScript() = script;
+  }
 
-  void initScript(JSScript* script_) { mutableScript().init(script_); }
+  void initScript(JSScript* script) {
+    MOZ_ASSERT_IF(script, realm() == script->realm());
+    mutableScript().init(script);
+  }
 
   void setUnlazifiedScript(JSScript* script) {
     MOZ_ASSERT(isInterpretedLazy());
     if (lazyScriptOrNull()) {
       // Trigger a pre barrier on the lazy script being overwritten.
       js::LazyScript::writeBarrierPre(lazyScriptOrNull());
       if (!lazyScript()->maybeScript()) {
         lazyScript()->initScript(script);
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -497,18 +497,20 @@ MOZ_ALWAYS_INLINE ObjectGroup* ObjectGro
   if (associated && !associated->is<TypeDescr>()) {
     MOZ_ASSERT(!clasp);
     if (associated->is<JSFunction>()) {
       // Canonicalize new functions to use the original one associated with its
       // script.
       associated = associated->as<JSFunction>().maybeCanonicalFunction();
 
       // If we have previously cleared the 'new' script information for this
-      // function, don't try to construct another one.
-      if (associated && associated->as<JSFunction>().wasNewScriptCleared()) {
+      // function, don't try to construct another one. Also, for simplicity,
+      // don't bother optimizing cross-realm constructors.
+      if (associated && (associated->as<JSFunction>().wasNewScriptCleared() ||
+                         associated->as<JSFunction>().realm() != cx->realm())) {
         associated = nullptr;
       }
 
     } else {
       associated = nullptr;
     }
 
     if (!associated) {
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3839,16 +3839,18 @@ void PreliminaryObjectArrayWithTemplate:
 // Make a TypeNewScript for |group|, and set it up to hold the preliminary
 // objects created with the group.
 /* static */ bool TypeNewScript::make(JSContext* cx, ObjectGroup* group,
                                       JSFunction* fun) {
   AutoSweepObjectGroup sweep(group);
   MOZ_ASSERT(cx->zone()->types.activeAnalysis);
   MOZ_ASSERT(!group->newScript(sweep));
   MOZ_ASSERT(!group->maybeUnboxedLayout(sweep));
+  MOZ_ASSERT(cx->realm() == group->realm());
+  MOZ_ASSERT(cx->realm() == fun->realm());
 
   // rollbackPartiallyInitializedObjects expects function_ to be
   // canonicalized.
   MOZ_ASSERT(fun->maybeCanonicalFunction() == fun);
 
   if (group->unknownProperties(sweep)) {
     return true;
   }
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -181,19 +181,19 @@ static bool EvalScript(JSContext* cx, Ha
     }
     retval.setUndefined();
   } else {
     JS::AutoObjectVector envChain(cx);
     if (!envChain.append(targetObj)) {
       return false;
     }
     if (!loadScope) {
-      // A null loadScope means we are cross-compartment. In this case, we
-      // should check the target isn't in the JSM loader shared-global or
-      // we will contaiminate all JSMs in the compartment.
+      // A null loadScope means we are cross-realm. In this case, we should
+      // check the target isn't in the JSM loader shared-global or we will
+      // contaminate all JSMs in the realm.
       //
       // NOTE: If loadScope is already a shared-global JSM, we can't
       // determine which JSM the target belongs to and have to assume it
       // is in our JSM.
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
       JSObject* targetGlobal = JS::GetNonCCWObjectGlobal(targetObj);
       MOZ_DIAGNOSTIC_ASSERT(
           !mozJSComponentLoader::Get()->IsLoaderGlobal(targetGlobal),
@@ -577,18 +577,18 @@ nsresult mozJSSubScriptLoader::DoLoadSub
 
   targetObj = JS_FindCompilationScope(cx, targetObj);
   if (!targetObj || !loadScope) {
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(!js::IsWrapper(targetObj), "JS_FindCompilationScope must unwrap");
 
-  if (js::GetObjectCompartment(loadScope) !=
-      js::GetObjectCompartment(targetObj)) {
+  if (js::GetNonCCWObjectRealm(loadScope) !=
+      js::GetNonCCWObjectRealm(targetObj)) {
     loadScope = nullptr;
   }
 
   /* load up the url.  From here on, failures are reflected as ``custom''
    * js exceptions */
   nsCOMPtr<nsIURI> uri;
   nsAutoCString uriStr;
   nsAutoCString scheme;
--- a/toolkit/themes/linux/global/in-content/common.css
+++ b/toolkit/themes/linux/global/in-content/common.css
@@ -5,22 +5,16 @@
 %include ../../../shared/in-content/common.inc.css
 
 xul|tab[visuallyselected] {
   /* Override styles for tab[selected] from
      toolkit/themes/linux/global/tabbox.css */
   margin-bottom: 0;
 }
 
-*|button.primary:focus,
-xul|button:-moz-focusring {
-  outline: 1px dotted;
-  outline-offset: -2px;
-}
-
 xul|button > xul|*.button-box,
 xul|menulist > xul|*.menulist-label-box {
   -moz-appearance: none;
 }
 
 xul|button[type="menu"] > xul|*.button-box > xul|*.button-menu-dropmarker {
   -moz-appearance: none !important;
 }
--- a/toolkit/themes/osx/global/in-content/common.css
+++ b/toolkit/themes/osx/global/in-content/common.css
@@ -52,20 +52,18 @@ xul|*.radio-icon {
 }
 
 xul|*.text-link:-moz-focusring {
   color: var(--in-content-link-highlight);
   text-decoration: underline;
   box-shadow: none;
 }
 
-xul|button:-moz-focusring,
 xul|menulist:-moz-focusring,
 xul|checkbox:-moz-focusring > .checkbox-check,
-*|button.primary:focus,
 html|input[type="checkbox"]:-moz-focusring + html|label:before,
 xul|radio[focused="true"] > .radio-check,
 xul|tab:-moz-focusring > .tab-middle > .tab-text {
   outline: 2px solid rgba(0,149,221,0.5);
   outline-offset: 1px;
   -moz-outline-radius: 2px;
 }
 
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 %endif
 @namespace html "http://www.w3.org/1999/xhtml";
 @namespace xul "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 *|*:root {
   --in-content-page-color: #0c0c0d;
   --in-content-page-background: #f9f9fa;
-  --in-content-text-color: #0c0c0d;
+  --in-content-text-color: var(--grey-90);
   --in-content-selected-text: #fff;
   --in-content-box-background: #fff;
   --in-content-box-background-odd: rgba(12, 12, 13, 0.05); /* grey 90 a05 */
   --in-content-box-background-hover: #ededf0; /* grey 20 */
   --in-content-box-background-active: #d7d7db; /* grey 30 */
   --in-content-box-border-color: var(--grey-90-a30);
   --in-content-item-hover: rgba(69, 161, 255, 0.2); /* blue 40 a20 */
   --in-content-item-selected: #0a84ff;
@@ -34,16 +34,19 @@
   --in-content-category-background-active: rgba(12,12,13,0.15);
   --in-content-category-background-selected-hover: rgba(12,12,13,0.15);
   --in-content-category-background-selected-active: rgba(12,12,13,0.2);
   --in-content-tab-color: #424f5a;
   --in-content-link-color: #0a8dff;
   --in-content-link-color-hover: #0060df;
   --in-content-link-color-active: #003eaa;
   --in-content-link-color-visited: #0a8dff;
+  --in-content-button-background: var(--grey-90-a10);
+  --in-content-button-background-hover: var(--grey-90-a20);
+  --in-content-button-background-active: var(--grey-30);
   --in-content-primary-button-background: var(--blue-60);
   --in-content-primary-button-background-hover: var(--blue-70);
   --in-content-primary-button-background-active: var(--blue-80);
   --in-content-table-background: #ebebeb;
   --in-content-table-border-dark-color: #d1d1d1;
   --in-content-table-header-background: #0a84ff;
 
   --blue-50: #0a84ff;
@@ -158,34 +161,48 @@ html|button {
 
 *|button,
 html|select,
 html|input[type="color"],
 xul|menulist,
 html|*.numberbox-input::-moz-number-spin-up,
 html|*.numberbox-input::-moz-number-spin-down {
   -moz-appearance: none;
-  min-height: 30px;
+  min-height: 32px;
   /* !important overrides button.css for disabled and default XUL buttons: */
   color: var(--in-content-text-color) !important;
-  border: 1px solid var(--in-content-box-border-color);
+  border: none;
   border-radius: 2px;
-  background-color: var(--in-content-page-background);
+  background-color: var(--in-content-button-background);
+  font-weight: 400;
+  padding: 0 8px;
+  text-align: center;
+  text-decoration: none;
   margin: 4px 8px;
   /* Ensure font-size isn't overridden by widget styling (e.g. in forms.css) */
   font-size: 1em;
 }
 
 xul|button,
 html|button {
   /* use the same margin of other elements for the alignment */
   margin-left: 4px;
   margin-right: 4px;
 }
 
+*|button::-moz-focus-inner {
+  border: none;
+}
+
+*|button:-moz-focusring {
+  box-shadow: 0 0 0 1px var(--in-content-border-active) inset,
+    0 0 0 1px var(--in-content-border-active),
+    0 0 0 4px var(--in-content-border-active-shadow);
+}
+
 html|select:not([size]):not([multiple]) {
   background-image: url("chrome://global/skin/in-content/dropdown.svg#dropdown");
   background-position: right 3px center;
   background-repeat: no-repeat;
   background-size: auto 18px;
   font-size: inherit;
   padding-inline-start: 5px;
   padding-inline-end: 24px;
@@ -198,41 +215,40 @@ html|select:not([size]):not([multiple]):
 
 html|button:enabled:hover,
 html|select:not([size]):not([multiple]):enabled:hover,
 html|*.numberbox-input::-moz-number-spin-up:hover,
 html|*.numberbox-input::-moz-number-spin-down:hover,
 html|input[type="color"]:hover,
 xul|button:not([disabled="true"]):hover,
 xul|menulist:not([disabled="true"]):hover {
-  background-color: var(--in-content-box-background-hover);
+  background-color: var(--in-content-button-background-hover);
 }
 
 html|button:enabled:hover:active,
 html|select:not([size]):not([multiple]):enabled:hover:active,
 html|*.numberbox-input::-moz-number-spin-up:hover:active,
 html|*.numberbox-input::-moz-number-spin-down:hover:active,
 html|input[type="color"]:enabled:hover:active,
 xul|button:not([disabled="true"]):hover:active,
 xul|menulist[open="true"]:not([disabled="true"]) {
-  background-color: var(--in-content-box-background-active);
+  background-color: var(--in-content-button-background-active);
 }
 
 html|button:disabled,
 html|select:disabled,
 html|*.numberbox-input:disabled::-moz-number-spin-box,
 html|input[type="color"]:disabled,
 xul|button[disabled="true"],
 xul|menulist[disabled="true"] {
-  opacity: 0.5;
+  opacity: 0.4;
 }
 
 *|button.primary {
   background-color: var(--in-content-primary-button-background);
-  border-color: transparent;
   color: var(--in-content-selected-text) !important;
 }
 
 html|button.primary:enabled:hover,
 xul|button.primary:not([disabled="true"]):hover {
   background-color: var(--in-content-primary-button-background-hover);
 }
 
@@ -431,17 +447,18 @@ xul|textbox:not([disabled="true"]):not([
 }
 
 html|input[type="email"]:focus,
 html|input[type="tel"]:focus,
 html|input[type="text"]:focus,
 html|textarea:focus,
 xul|textbox[focused] {
   border-color: var(--in-content-border-active);
-  box-shadow: 0 0 0 3px var(--in-content-border-active-shadow);
+  box-shadow: 0 0 0 1px var(--in-content-border-active),
+    0 0 0 4px var(--in-content-border-active-shadow);
 }
 
 html|input[type="email"]:-moz-ui-invalid,
 html|input[type="tel"]:-moz-ui-invalid,
 html|input[type="text"]:-moz-ui-invalid,
 html|textarea:-moz-ui-invalid {
   border-color: var(--in-content-border-invalid);
 }
--- a/toolkit/themes/windows/global/in-content/common.css
+++ b/toolkit/themes/windows/global/in-content/common.css
@@ -27,21 +27,16 @@ xul|*.menulist-label-box {
   border-style: none;
 }
 
 xul|menulist:-moz-focusring > xul|*.menulist-label-box,
 html|input[type="checkbox"]:-moz-focusring + html|label:before {
   outline: 1px dotted;
 }
 
-*|button.primary:focus {
-  outline: 1px dotted;
-  outline-offset: -3px;
-}
-
 /* Use a 2px border so that selected row highlight is still visible behind
     an existing high-contrast border that uses the background color */
 @media (-moz-windows-default-theme: 0) {
   xul|treechildren::-moz-tree-row(selected) {
      border: 2px dotted Highlight;
   }
 }