Bug 1517389 - Ensure that we emit content blocking events when setting cookie headers and reading cookies from the cookies database; r=baku
authorEhsan Akhgari <ehsan@mozilla.com>
Tue, 22 Jan 2019 19:46:10 +0000
changeset 514860 ec6834457f75cb4ba703a5031eaa06529361d8a4
parent 514859 2b0f0d220a7cc3d6262706c160cc1900148dbe79
child 514861 c9e244ccc88db4b7eb30e3543098fd0c7be3b7b9
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1517389
milestone66.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 1517389 - Ensure that we emit content blocking events when setting cookie headers and reading cookies from the cookies database; r=baku Differential Revision: https://phabricator.services.mozilla.com/D17203
browser/base/content/test/trackingUI/browser_trackingUI_cookies_subview.js
netwerk/cookie/CookieServiceChild.cpp
netwerk/cookie/CookieServiceParent.cpp
netwerk/cookie/nsCookieService.cpp
netwerk/cookie/nsCookieService.h
--- a/browser/base/content/test/trackingUI/browser_trackingUI_cookies_subview.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_cookies_subview.js
@@ -16,117 +16,171 @@ add_task(async function setup() {
   await UrlClassifierTestUtils.addTestTrackers();
 
   registerCleanupFunction(() => {
     UrlClassifierTestUtils.cleanupTestTrackers();
   });
 });
 
 async function assertSitesListed(trackersBlocked, thirdPartyBlocked, firstPartyBlocked) {
-  await BrowserTestUtils.withNewTab(COOKIE_PAGE, async function(browser) {
-    await openIdentityPopup();
+  let promise = BrowserTestUtils.openNewForegroundTab({url: COOKIE_PAGE, gBrowser});
+  let specialCase = firstPartyBlocked ||
+                    (trackersBlocked && thirdPartyBlocked && !firstPartyBlocked) ||
+                    (!trackersBlocked && !thirdPartyBlocked && !firstPartyBlocked);
+  let count = 4;
+  if (firstPartyBlocked) {
+    count = 6;
+  } else if (trackersBlocked && thirdPartyBlocked && !firstPartyBlocked) {
+    count = 5;
+  } else if (!trackersBlocked && !thirdPartyBlocked && !firstPartyBlocked) {
+    count = 3;
+  }
+  let [tab] = await Promise.all([promise, waitForContentBlockingEvent(count)]);
+  let browser = tab.linkedBrowser;
+
+  await openIdentityPopup();
+
+  let categoryItem =
+    document.getElementById("identity-popup-content-blocking-category-cookies");
+  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;
 
-    let categoryItem =
-      document.getElementById("identity-popup-content-blocking-category-cookies");
-    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, 3, "We have 3 list headers");
+
+  let emptyLabels = cookiesView.querySelectorAll(".identity-popup-content-blocking-empty-label");
+  if (specialCase) {
+    count = 1;
+  } else {
+    count = 2;
+  }
+  is(emptyLabels.length, count, `We have ${count} empty labels`);
+
+  let listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
+  if (specialCase) {
+    count = 2;
+  } else {
+    count = 1;
+  }
+  is(listItems.length, count, `We have ${count} cookie in the list`);
+
+  let listItem = listItems[specialCase ? 1 : 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");
+  is(listItem.classList.contains("allowed"), !trackersBlocked,
+    "Indicates whether the cookie was blocked or allowed");
 
-    ok(true, "Cookies view was shown");
+  if (specialCase) {
+    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 not-tracking.example.com");
+    ok(BrowserTestUtils.is_visible(listItem), "List item is visible");
+    is(listItem.classList.contains("allowed"), !firstPartyBlocked,
+      "Indicates whether the cookie was blocked");
+  }
 
-    let listHeaders = cookiesView.querySelectorAll(".identity-popup-cookiesView-list-header");
-    is(listHeaders.length, 3, "We have 3 list headers");
+  let mainView = document.getElementById("identity-popup-mainView");
+  viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown");
+  let backButton = cookiesView.querySelector(".subviewbutton-back");
+  backButton.click();
+  await viewShown;
+
+  ok(true, "Main view was shown");
+
+  let change = waitForContentBlockingEvent();
+  let timeoutPromise = new Promise(resolve => setTimeout(resolve, 1000));
+
+  await ContentTask.spawn(browser, {}, function() {
+    content.postMessage("third-party-cookie", "*");
+  });
+
+  let result = await Promise.race([change, timeoutPromise]);
+  is(result, undefined, "No contentBlockingEvent events should be received");
 
-    let emptyLabels = cookiesView.querySelectorAll(".identity-popup-content-blocking-empty-label");
-    is(emptyLabels.length, 2, "We have 2 empty labels");
+  viewShown = BrowserTestUtils.waitForEvent(cookiesView, "ViewShown");
+  categoryItem.click();
+  await viewShown;
+
+  ok(true, "Cookies view was shown");
+
+  emptyLabels = cookiesView.querySelectorAll(".identity-popup-content-blocking-empty-label");
+  if (specialCase) {
+    count = 0;
+  } else {
+    count = 1;
+  }
+  is(emptyLabels.length, count, `We have ${count} empty label`);
 
-    let listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
-    is(listItems.length, 1, "We have 1 cookie in the list");
+  listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
+  if (specialCase) {
+    count = 3;
+  } else {
+    count = 2;
+  }
+  is(listItems.length, count, `We have ${count} cookies in the list`);
 
-    let listItem = listItems[0];
-    let label = listItem.querySelector(".identity-popup-content-blocking-list-host-label");
+  listItem = listItems[specialCase ? 2 : 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");
+  is(listItem.classList.contains("allowed"), !thirdPartyBlocked,
+    "Indicates whether the cookie was blocked or allowed");
+
+  if (specialCase) {
+    listItem = listItems[1];
+    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");
-    is(listItem.classList.contains("allowed"), !trackersBlocked,
-      "Indicates whether the cookie was blocked or allowed");
+    is(listItem.classList.contains("allowed"),
+      (!trackersBlocked && !thirdPartyBlocked && !firstPartyBlocked),
+      "Indicates whether the cookie was blocked");
+  }
 
-    let mainView = document.getElementById("identity-popup-mainView");
-    viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown");
-    let backButton = cookiesView.querySelector(".subviewbutton-back");
-    backButton.click();
-    await viewShown;
-
-    ok(true, "Main view was shown");
-
-    let change = waitForSecurityChange();
-    let timeoutPromise = new Promise(resolve => setTimeout(resolve, 1000));
+  viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown");
+  backButton.click();
+  await viewShown;
 
-    await ContentTask.spawn(browser, {}, function() {
-      content.postMessage("third-party-cookie", "*");
-    });
+  ok(true, "Main view was shown");
 
-    let result = await Promise.race([change, timeoutPromise]);
-    is(result, undefined, "No securityChange events should be received");
+  change = waitForSecurityChange();
+  timeoutPromise = new Promise(resolve => setTimeout(resolve, 1000));
 
-    viewShown = BrowserTestUtils.waitForEvent(cookiesView, "ViewShown");
-    categoryItem.click();
-    await viewShown;
-
-    ok(true, "Cookies view was shown");
+  await ContentTask.spawn(browser, {}, function() {
+    content.postMessage("first-party-cookie", "*");
+  });
 
-    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");
+  result = await Promise.race([change, timeoutPromise]);
+  is(result, undefined, "No securityChange events should be received");
 
-    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");
-    is(listItem.classList.contains("allowed"), !thirdPartyBlocked,
-      "Indicates whether the cookie was blocked or allowed");
+  viewShown = BrowserTestUtils.waitForEvent(cookiesView, "ViewShown");
+  categoryItem.click();
+  await viewShown;
 
-    viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown");
-    backButton.click();
-    await viewShown;
-
-    ok(true, "Main view was shown");
+  ok(true, "Cookies view was shown");
 
-    change = waitForSecurityChange();
-    timeoutPromise = new Promise(resolve => setTimeout(resolve, 1000));
-
-    await ContentTask.spawn(browser, {}, function() {
-      content.postMessage("first-party-cookie", "*");
-    });
+  emptyLabels = cookiesView.querySelectorAll(".identity-popup-content-blocking-empty-label");
+  is(emptyLabels.length, 0, "We have 0 empty label");
 
-    result = await Promise.race([change, timeoutPromise]);
-    is(result, undefined, "No securityChange events should be received");
-
-    viewShown = BrowserTestUtils.waitForEvent(cookiesView, "ViewShown");
-    categoryItem.click();
-    await viewShown;
-
-    ok(true, "Cookies view was shown");
+  listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
+  is(listItems.length, 3, "We have 2 cookies in the list");
 
-    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");
+  is(listItem.classList.contains("allowed"), !firstPartyBlocked,
+    "Indicates whether the cookie was blocked or allowed");
 
-    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");
-    is(listItem.classList.contains("allowed"), !firstPartyBlocked,
-      "Indicates whether the cookie was blocked or allowed");
-  });
+  BrowserTestUtils.removeTab(tab);
 }
 
 add_task(async function testCookiesSubView() {
   info("Testing cookies subview with reject tracking cookies.");
   Services.prefs.setIntPref(TPC_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER);
   await assertSitesListed(true, false, false);
   info("Testing cookies subview with reject third party cookies.");
   Services.prefs.setIntPref(TPC_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN);
@@ -141,137 +195,165 @@ add_task(async function testCookiesSubVi
   Services.prefs.clearUserPref(TPC_PREF);
 });
 
 add_task(async function testCookiesSubViewAllowed() {
   Services.prefs.setIntPref(TPC_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER);
   let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("http://trackertest.org/");
   Services.perms.addFromPrincipal(principal, "cookie", Services.perms.ALLOW_ACTION);
 
-  await BrowserTestUtils.withNewTab(COOKIE_PAGE, async function(browser) {
-    await openIdentityPopup();
+  let promise = BrowserTestUtils.openNewForegroundTab({url: COOKIE_PAGE, gBrowser});
+  let [tab] = await Promise.all([promise, waitForContentBlockingEvent(3)]);
+
+  await openIdentityPopup();
 
-    let categoryItem =
-      document.getElementById("identity-popup-content-blocking-category-cookies");
-    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;
+  let categoryItem =
+    document.getElementById("identity-popup-content-blocking-category-cookies");
+  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");
+  ok(true, "Cookies view was shown");
+
+  let listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
+  is(listItems.length, 2, "We have 1 cookie in the list");
 
-    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://not-tracking.example.com", "has an item for not-tracking.example.com");
+  ok(BrowserTestUtils.is_visible(listItem), "list item is visible");
+  ok(listItem.classList.contains("allowed"), "indicates whether the cookie was blocked or allowed");
 
-    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");
+  listItem = listItems[1];
+  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");
 
-    let button = listItem.querySelector(".identity-popup-permission-remove-button");
-    ok(BrowserTestUtils.is_visible(button), "Permission remove button is visible");
-    button.click();
-    is(Services.perms.testExactPermissionFromPrincipal(principal, "cookie"), Services.perms.UNKNOWN_ACTION, "Button click should remove cookie pref.");
-    ok(!listItem.classList.contains("allowed"), "Has removed the allowed class");
-  });
+  let button = listItem.querySelector(".identity-popup-permission-remove-button");
+  ok(BrowserTestUtils.is_visible(button), "Permission remove button is visible");
+  button.click();
+  is(Services.perms.testExactPermissionFromPrincipal(principal, "cookie"), Services.perms.UNKNOWN_ACTION, "Button click should remove cookie pref.");
+  ok(!listItem.classList.contains("allowed"), "Has removed the allowed class");
+
+  BrowserTestUtils.removeTab(tab);
 
   Services.prefs.clearUserPref(TPC_PREF);
 });
 
 add_task(async function testCookiesSubViewAllowedHeuristic() {
   Services.prefs.setIntPref(TPC_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER);
   let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("http://not-tracking.example.com/");
 
   // Pretend that the tracker has already been interacted with
   let trackerPrincipal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("http://trackertest.org/");
   Services.perms.addFromPrincipal(trackerPrincipal, "storageAccessAPI", Services.perms.ALLOW_ACTION);
 
-  await BrowserTestUtils.withNewTab(COOKIE_PAGE, async function(browser) {
-    let popup;
-    let windowCreated = TestUtils.topicObserved("chrome-document-global-created", (subject, data) => {
-      popup = subject;
-      return true;
+  let promise = BrowserTestUtils.openNewForegroundTab({url: COOKIE_PAGE, gBrowser});
+  let [tab] = await Promise.all([promise, waitForContentBlockingEvent(5)]);
+  let browser = tab.linkedBrowser;
+
+  let popup;
+  let windowCreated = TestUtils.topicObserved("chrome-document-global-created", (subject, data) => {
+    popup = subject;
+    return true;
+  });
+  let permChanged = TestUtils.topicObserved("perm-changed",
+    (subject, data) => {
+      return subject &&
+             subject.QueryInterface(Ci.nsIPermission)
+                    .type == "3rdPartyStorage^http://trackertest.org" &&
+             subject.principal.origin == principal.origin &&
+             data == "added";
     });
-    let permChanged = TestUtils.topicObserved("perm-changed",
-      (subject, data) => {
-        return subject &&
-               subject.QueryInterface(Ci.nsIPermission)
-                      .type == "3rdPartyStorage^http://trackertest.org" &&
-               subject.principal.origin == principal.origin &&
-               data == "added";
-      });
 
-    await ContentTask.spawn(browser, {}, function() {
-      content.postMessage("window-open", "*");
-    });
-    await Promise.all([windowCreated, permChanged]);
+  await ContentTask.spawn(browser, {}, function() {
+    content.postMessage("window-open", "*");
+  });
+  await Promise.all([windowCreated, permChanged]);
 
-    await new Promise(resolve => waitForFocus(resolve, popup));
-    await new Promise(resolve => waitForFocus(resolve, window));
+  await new Promise(resolve => waitForFocus(resolve, popup));
+  await new Promise(resolve => waitForFocus(resolve, window));
 
-    await openIdentityPopup();
+  await openIdentityPopup();
 
-    let categoryItem =
-      document.getElementById("identity-popup-content-blocking-category-cookies");
-    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;
+  let categoryItem =
+    document.getElementById("identity-popup-content-blocking-category-cookies");
+  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");
+  ok(true, "Cookies view was shown");
 
-    let listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
-    is(listItems.length, 1, "We have 1 cookie in the list");
+  let listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
+  is(listItems.length, 2, "We have 2 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");
+  let listItem = listItems[0];
+  let label = listItem.querySelector(".identity-popup-content-blocking-list-host-label");
+  is(label.value, "http://not-tracking.example.com", "has an item for not-tracking.example.com");
+  ok(BrowserTestUtils.is_visible(listItem), "list item is visible");
+  ok(listItem.classList.contains("allowed"), "indicates whether the cookie was blocked or allowed");
+
+  listItem = listItems[1];
+  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");
 
-    let button = listItem.querySelector(".identity-popup-permission-remove-button");
-    ok(BrowserTestUtils.is_visible(button), "Permission remove button is visible");
-    button.click();
-    is(Services.perms.testExactPermissionFromPrincipal(principal, "3rdPartyStorage^http://trackertest.org"), Services.perms.UNKNOWN_ACTION, "Button click should remove the storage pref.");
-    ok(!listItem.classList.contains("allowed"), "Has removed the allowed class");
+  let button = listItem.querySelector(".identity-popup-permission-remove-button");
+  ok(BrowserTestUtils.is_visible(button), "Permission remove button is visible");
+  button.click();
+  is(Services.perms.testExactPermissionFromPrincipal(principal, "3rdPartyStorage^http://trackertest.org"), Services.perms.UNKNOWN_ACTION, "Button click should remove the storage pref.");
+  ok(!listItem.classList.contains("allowed"), "Has removed the allowed class");
 
-    await ContentTask.spawn(browser, {}, function() {
-      content.postMessage("window-close", "*");
-    });
+  await ContentTask.spawn(browser, {}, function() {
+    content.postMessage("window-close", "*");
   });
 
+  BrowserTestUtils.removeTab(tab);
+
   Services.prefs.clearUserPref(TPC_PREF);
 });
 
 add_task(async function testCookiesSubViewBlockedDoublyNested() {
   Services.prefs.setIntPref(TPC_PREF, Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER);
 
-  await BrowserTestUtils.withNewTab(CONTAINER_PAGE, async function(browser) {
-    await openIdentityPopup();
+  let promise = BrowserTestUtils.openNewForegroundTab({url: CONTAINER_PAGE, gBrowser});
+  let [tab] = await Promise.all([promise, waitForContentBlockingEvent(3)]);
+
+  await openIdentityPopup();
 
-    let categoryItem =
-      document.getElementById("identity-popup-content-blocking-category-cookies");
-    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;
+  let categoryItem =
+    document.getElementById("identity-popup-content-blocking-category-cookies");
+  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");
+  ok(true, "Cookies view was shown");
 
-    let listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
-    is(listItems.length, 1, "We have 1 cookie in the list");
+  let listItems = cookiesView.querySelectorAll(".identity-popup-content-blocking-list-item");
+  is(listItems.length, 2, "We have 2 cookie in the list");
+
+  let listItem = listItems[0];
+  let label = listItem.querySelector(".identity-popup-content-blocking-list-host-label");
+  is(label.value, "http://not-tracking.example.com", "has an item for not-tracking.example.com");
+  ok(BrowserTestUtils.is_visible(listItem), "list item is visible");
+  ok(listItem.classList.contains("allowed"), "indicates whether the cookie was blocked or allowed");
 
-    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");
+  listItem = listItems[1];
+  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");
 
-    let button = listItem.querySelector(".identity-popup-permission-remove-button");
-    ok(!button, "Permission remove button doesn't exist");
-  });
+  let button = listItem.querySelector(".identity-popup-permission-remove-button");
+  ok(!button, "Permission remove button doesn't exist");
+
+  BrowserTestUtils.removeTab(tab);
 
   Services.prefs.clearUserPref(TPC_PREF);
 });
--- a/netwerk/cookie/CookieServiceChild.cpp
+++ b/netwerk/cookie/CookieServiceChild.cpp
@@ -156,20 +156,30 @@ void CookieServiceChild::TrackCookieLoad
     mThirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign);
   }
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   if (httpChannel) {
     isTrackingResource = httpChannel->GetIsTrackingResource();
     // Check first-party storage access even for non-tracking resources, since
     // we will need the result when computing the access rights for the reject
     // foreign cookie behavior mode.
+    uint32_t rejectedReason = 0;
     if (isForeign && AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
-                         httpChannel, uri, nullptr)) {
+                         httpChannel, uri, &rejectedReason)) {
       firstPartyStorageAccessGranted = true;
     }
+
+    // We need to notify about the outcome of the content blocking check here
+    // since the parent process can't do it for us as it won't have a channel
+    // object handy.
+    if (!firstPartyStorageAccessGranted) {
+      AntiTrackingCommon::NotifyBlockingDecision(
+          aChannel, AntiTrackingCommon::BlockingDecision::eBlock,
+          rejectedReason);
+    }
   }
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   mozilla::OriginAttributes attrs;
   if (loadInfo) {
     attrs = loadInfo->GetOriginAttributes();
   }
   URIParams uriParams;
   SerializeURI(uri, uriParams);
--- a/netwerk/cookie/CookieServiceParent.cpp
+++ b/netwerk/cookie/CookieServiceParent.cpp
@@ -157,17 +157,17 @@ void CookieServiceParent::TrackCookieLoa
     if (isForeign && AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
                          httpChannel, uri, nullptr)) {
       storageAccessGranted = true;
     }
   }
 
   nsTArray<nsCookie *> foundCookieList;
   mCookieService->GetCookiesForURI(
-      uri, isForeign, isTrackingResource, storageAccessGranted,
+      uri, aChannel, isForeign, isTrackingResource, storageAccessGranted,
       isSafeTopLevelNav, aIsSameSiteForeign, false, attrs, foundCookieList);
   nsTArray<CookieStruct> matchingCookiesList;
   SerialializeCookieList(foundCookieList, matchingCookiesList, uri);
   Unused << SendTrackCookiesLoad(matchingCookiesList, attrs);
 }
 
 void CookieServiceParent::SerialializeCookieList(
     const nsTArray<nsCookie *> &aFoundCookieList,
@@ -194,19 +194,23 @@ mozilla::ipc::IPCResult CookieServicePar
     const URIParams &aHost, const bool &aIsForeign,
     const bool &aIsTrackingResource,
     const bool &aFirstPartyStorageAccessGranted, const bool &aIsSafeTopLevelNav,
     const bool &aIsSameSiteForeign, const OriginAttributes &aAttrs) {
   nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
 
   // Send matching cookies to Child.
   nsTArray<nsCookie *> foundCookieList;
+  // Note: passing nullptr as aChannel to GetCookiesForURI() here is fine since
+  // this argument is only used for proper reporting of cookie loads, but the
+  // child process already does the necessary reporting in this case for us.
   mCookieService->GetCookiesForURI(
-      hostURI, aIsForeign, aIsTrackingResource, aFirstPartyStorageAccessGranted,
-      aIsSafeTopLevelNav, aIsSameSiteForeign, false, aAttrs, foundCookieList);
+      hostURI, nullptr, aIsForeign, aIsTrackingResource,
+      aFirstPartyStorageAccessGranted, aIsSafeTopLevelNav, aIsSameSiteForeign,
+      false, aAttrs, foundCookieList);
   nsTArray<CookieStruct> matchingCookiesList;
   SerialializeCookieList(foundCookieList, matchingCookiesList, hostURI);
   Unused << SendTrackCookiesLoad(matchingCookiesList, aAttrs);
   return IPC_OK();
 }
 
 void CookieServiceParent::ActorDestroy(ActorDestroyReason aWhy) {
   // Nothing needed here. Called right before destructor since this is a
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -2007,17 +2007,17 @@ nsresult nsCookieService::GetCookieStrin
   OriginAttributes attrs;
   if (aChannel) {
     NS_GetOriginAttributes(aChannel, attrs);
   }
 
   bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
   bool isSameSiteForeign = NS_IsSameSiteForeign(aChannel, aHostURI);
   nsAutoCString result;
-  GetCookieStringInternal(aHostURI, isForeign, isTrackingResource,
+  GetCookieStringInternal(aHostURI, aChannel, isForeign, isTrackingResource,
                           firstPartyStorageAccessGranted, isSafeTopLevelNav,
                           isSameSiteForeign, aHttpBound, attrs, result);
   *aCookie = result.IsEmpty() ? nullptr : ToNewCString(result);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCookieService::SetCookieString(nsIURI *aHostURI, nsIPrompt *aPrompt,
@@ -2166,17 +2166,17 @@ void nsCookieService::SetCookieStringInt
       aOriginAttrs, &rejectedReason);
 
   MOZ_ASSERT_IF(rejectedReason, cookieStatus == STATUS_REJECTED);
 
   // fire a notification if third party or if cookie was rejected
   // (but not if there was an error)
   switch (cookieStatus) {
     case STATUS_REJECTED:
-      NotifyRejected(aHostURI, aChannel, rejectedReason);
+      NotifyRejected(aHostURI, aChannel, rejectedReason, OPERATION_WRITE);
       if (aIsForeign) {
         NotifyThirdParty(aHostURI, false, aChannel);
       }
       return;  // Stop here
     case STATUS_REJECTED_WITH_ERROR:
       return;
     case STATUS_ACCEPTED:  // Fallthrough
     case STATUS_ACCEPT_SESSION:
@@ -2201,20 +2201,25 @@ void nsCookieService::SetCookieStringInt
 
 void nsCookieService::NotifyAccepted(nsIChannel *aChannel) {
   AntiTrackingCommon::NotifyBlockingDecision(
       aChannel, AntiTrackingCommon::BlockingDecision::eAllow, 0);
 }
 
 // notify observers that a cookie was rejected due to the users' prefs.
 void nsCookieService::NotifyRejected(nsIURI *aHostURI, nsIChannel *aChannel,
-                                     uint32_t aRejectedReason) {
-  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-  if (os) {
-    os->NotifyObservers(aHostURI, "cookie-rejected", nullptr);
+                                     uint32_t aRejectedReason,
+                                     CookieOperation aOperation) {
+  if (aOperation == OPERATION_WRITE) {
+    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+    if (os) {
+      os->NotifyObservers(aHostURI, "cookie-rejected", nullptr);
+    }
+  } else {
+    MOZ_ASSERT(aOperation == OPERATION_READ);
   }
 
   AntiTrackingCommon::NotifyBlockingDecision(
       aChannel, AntiTrackingCommon::BlockingDecision::eBlock, aRejectedReason);
 }
 
 // notify observers that a third-party cookie was accepted/rejected
 // if the cookie issuer is unknown, it defaults to "?"
@@ -3011,23 +3016,21 @@ bool nsCookieService::PathMatches(nsCook
     return false;
   }
 
   // either the paths match exactly, or the cookie path is a prefix of
   // the given path.
   return true;
 }
 
-void nsCookieService::GetCookiesForURI(nsIURI *aHostURI, bool aIsForeign,
-                                       bool aIsTrackingResource,
-                                       bool aFirstPartyStorageAccessGranted,
-                                       bool aIsSafeTopLevelNav,
-                                       bool aIsSameSiteForeign, bool aHttpBound,
-                                       const OriginAttributes &aOriginAttrs,
-                                       nsTArray<nsCookie *> &aCookieList) {
+void nsCookieService::GetCookiesForURI(
+    nsIURI *aHostURI, nsIChannel *aChannel, bool aIsForeign,
+    bool aIsTrackingResource, bool aFirstPartyStorageAccessGranted,
+    bool aIsSafeTopLevelNav, bool aIsSameSiteForeign, bool aHttpBound,
+    const OriginAttributes &aOriginAttrs, nsTArray<nsCookie *> &aCookieList) {
   NS_ASSERTION(aHostURI, "null host!");
 
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return;
   }
 
   EnsureReadComplete(true);
@@ -3049,28 +3052,32 @@ void nsCookieService::GetCookiesForURI(n
   if (NS_SUCCEEDED(rv)) rv = aHostURI->GetPathQueryRef(pathFromURI);
   if (NS_FAILED(rv)) {
     COOKIE_LOGFAILURE(GET_COOKIE, aHostURI, nullptr,
                       "invalid host/path from URI");
     return;
   }
 
   // check default prefs
+  uint32_t rejectedReason = 0;
   uint32_t priorCookieCount = 0;
   CountCookiesFromHost(hostFromURI, &priorCookieCount);
   CookieStatus cookieStatus =
       CheckPrefs(mPermissionService, mCookieBehavior, mThirdPartySession,
                  mThirdPartyNonsecureSession, aHostURI, aIsForeign,
                  aIsTrackingResource, aFirstPartyStorageAccessGranted, nullptr,
-                 priorCookieCount, aOriginAttrs, nullptr);
-
-  // for GetCookie(), we don't fire rejection notifications.
+                 priorCookieCount, aOriginAttrs, &rejectedReason);
+
+  MOZ_ASSERT_IF(rejectedReason, cookieStatus == STATUS_REJECTED);
+
+  // for GetCookie(), we only fire acceptance/rejection notifications
+  // (but not if there was an error)
   switch (cookieStatus) {
     case STATUS_REJECTED:
-    case STATUS_REJECTED_WITH_ERROR:
+      NotifyRejected(aHostURI, aChannel, rejectedReason, OPERATION_READ);
       return;
     default:
       break;
   }
 
   // Note: The following permissions logic is mirrored in
   // extensions::MatchPattern::MatchesCookie.
   // If it changes, please update that function, or file a bug for someone
@@ -3139,16 +3146,20 @@ void nsCookieService::GetCookiesForURI(n
     if (cookie->IsStale()) {
       stale = true;
     }
   }
 
   int32_t count = aCookieList.Length();
   if (count == 0) return;
 
+  // Send a notification about the acceptance of the cookies now that we found
+  // some.
+  NotifyAccepted(aChannel);
+
   // update lastAccessed timestamps. we only do this if the timestamp is stale
   // by a certain amount, to avoid thrashing the db during pageload.
   if (stale) {
     // Create an array of parameters to bind to our update statement. Batching
     // is OK here since we're updating cookies with no interleaved operations.
     nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
     mozIStorageAsyncStatement *stmt = mDBState->stmtUpdate;
     if (mDBState->dbConn) {
@@ -3179,22 +3190,22 @@ void nsCookieService::GetCookiesForURI(n
 
   // return cookies in order of path length; longest to shortest.
   // this is required per RFC2109.  if cookies match in length,
   // then sort by creation time (see bug 236772).
   aCookieList.Sort(CompareCookiesForSending());
 }
 
 void nsCookieService::GetCookieStringInternal(
-    nsIURI *aHostURI, bool aIsForeign, bool aIsTrackingResource,
-    bool aFirstPartyStorageAccessGranted, bool aIsSafeTopLevelNav,
-    bool aIsSameSiteForeign, bool aHttpBound,
+    nsIURI *aHostURI, nsIChannel *aChannel, bool aIsForeign,
+    bool aIsTrackingResource, bool aFirstPartyStorageAccessGranted,
+    bool aIsSafeTopLevelNav, bool aIsSameSiteForeign, bool aHttpBound,
     const OriginAttributes &aOriginAttrs, nsCString &aCookieString) {
   AutoTArray<nsCookie *, 8> foundCookieList;
-  GetCookiesForURI(aHostURI, aIsForeign, aIsTrackingResource,
+  GetCookiesForURI(aHostURI, aChannel, aIsForeign, aIsTrackingResource,
                    aFirstPartyStorageAccessGranted, aIsSafeTopLevelNav,
                    aIsSameSiteForeign, aHttpBound, aOriginAttrs,
                    foundCookieList);
 
   nsCookie *cookie;
   for (uint32_t i = 0; i < foundCookieList.Length(); ++i) {
     cookie = foundCookieList.ElementAt(i);
 
@@ -3442,17 +3453,18 @@ bool nsCookieService::SetCookieInternal(
         aHostURI, aChannel,
         static_cast<nsICookie2 *>(static_cast<nsCookie *>(cookie)),
         &cookieAttributes.isSession, &cookieAttributes.expiryTime, &permission);
     if (!permission) {
       COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader,
                         "cookie rejected by permission manager");
       NotifyRejected(
           aHostURI, aChannel,
-          nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION);
+          nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION,
+          OPERATION_WRITE);
       return newCookie;
     }
 
     // update isSession and expiry attributes, in case they changed
     cookie->SetIsSession(cookieAttributes.isSession);
     cookie->SetExpiry(cookieAttributes.expiryTime);
   }
 
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -169,16 +169,19 @@ struct DBState final {
 
   // DB completion handlers.
   nsCOMPtr<mozIStorageStatementCallback> insertListener;
   nsCOMPtr<mozIStorageStatementCallback> updateListener;
   nsCOMPtr<mozIStorageStatementCallback> removeListener;
   nsCOMPtr<mozIStorageCompletionCallback> closeListener;
 };
 
+// these constants represent an operation being performed on cookies
+enum CookieOperation { OPERATION_READ, OPERATION_WRITE };
+
 // these constants represent a decision about a cookie based on user prefs.
 enum CookieStatus {
   STATUS_ACCEPTED,
   STATUS_ACCEPT_SESSION,
   STATUS_REJECTED,
   // STATUS_REJECTED_WITH_ERROR indicates the cookie should be rejected because
   // of an error (rather than something the user can control). this is used for
   // notification purposes, since we only want to notify of rejections where
@@ -258,20 +261,20 @@ class nsCookieService final : public nsI
   static CookieStatus CheckPrefs(
       nsICookiePermission *aPermissionServices, uint8_t aCookieBehavior,
       bool aThirdPartySession, bool aThirdPartyNonsecureSession,
       nsIURI *aHostURI, bool aIsForeign, bool aIsTrackingResource,
       bool aIsFirstPartyStorageAccessGranted, const char *aCookieHeader,
       const int aNumOfCookies, const OriginAttributes &aOriginAttrs,
       uint32_t *aRejectedReason);
   static int64_t ParseServerTime(const nsCString &aServerTime);
-  void GetCookiesForURI(nsIURI *aHostURI, bool aIsForeign,
+  void GetCookiesForURI(nsIURI *aHostURI, nsIChannel *aChannel, bool aIsForeign,
                         bool aIsTrackingResource,
                         bool aFirstPartyStorageAccessGranted,
-                        bool aIsSafeTopLevelNav, bool aIsTopLevelForeign,
+                        bool aIsSafeTopLevelNav, bool aIsSameSiteForeign,
                         bool aHttpBound, const OriginAttributes &aOriginAttrs,
                         nsTArray<nsCookie *> &aCookieList);
 
  protected:
   virtual ~nsCookieService();
 
   void PrefChanged(nsIPrefBranch *aPrefBranch);
   void InitDBStates();
@@ -291,20 +294,20 @@ class nsCookieService final : public nsI
   void RebuildCorruptDB(DBState *aDBState);
   OpenDBResult Read();
   mozilla::UniquePtr<ConstCookie> GetCookieFromRow(
       mozIStorageStatement *aRow, const OriginAttributes &aOriginAttributes);
   void EnsureReadComplete(bool aInitDBConn);
   nsresult NormalizeHost(nsCString &aHost);
   nsresult GetCookieStringCommon(nsIURI *aHostURI, nsIChannel *aChannel,
                                  bool aHttpBound, char **aCookie);
-  void GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign,
-                               bool aIsTrackingResource,
+  void GetCookieStringInternal(nsIURI *aHostURI, nsIChannel *aChannel,
+                               bool aIsForeign, bool aIsTrackingResource,
                                bool aFirstPartyStorageAccessGranted,
-                               bool aIsSafeTopLevelNav, bool aIsTopLevelForeign,
+                               bool aIsSafeTopLevelNav, bool aIsSameSiteForeign,
                                bool aHttpBound,
                                const OriginAttributes &aOriginAttrs,
                                nsCString &aCookie);
   nsresult SetCookieStringCommon(nsIURI *aHostURI, const char *aCookieHeader,
                                  const char *aServerTime, nsIChannel *aChannel,
                                  bool aFromHttp);
   void SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign,
                                bool aIsTrackingResource,
@@ -351,17 +354,17 @@ class nsCookieService final : public nsI
   bool FindSecureCookie(const nsCookieKey &aKey, nsCookie *aCookie);
   void FindStaleCookies(nsCookieEntry *aEntry, int64_t aCurrentTime,
                         const mozilla::Maybe<bool> &aIsSecure,
                         nsTArray<nsListIter> &aOutput, uint32_t aLimit);
   void TelemetryForEvictingStaleCookie(nsCookie *aEvicted,
                                        int64_t oldestCookieTime);
   void NotifyAccepted(nsIChannel *aChannel);
   void NotifyRejected(nsIURI *aHostURI, nsIChannel *aChannel,
-                      uint32_t aRejectedReason);
+                      uint32_t aRejectedReason, CookieOperation aOperation);
   void NotifyThirdParty(nsIURI *aHostURI, bool aAccepted, nsIChannel *aChannel);
   void NotifyChanged(nsISupports *aSubject, const char16_t *aData,
                      bool aOldCookieIsSession = false, bool aFromHttp = false);
   void NotifyPurged(nsICookie2 *aCookie);
   already_AddRefed<nsIArray> CreatePurgeList(nsICookie2 *aCookie);
   void CreateOrUpdatePurgeList(nsIArray **aPurgeList, nsICookie2 *aCookie);
   void UpdateCookieOldestTime(DBState *aDBState, nsCookie *aCookie);