From c3f891b5ec822fa9dcc466070520656b94de2776 Mon Sep 17 00:00:00 2001
From: Paul Zuehlcke <pbz@mozilla.com>
Date: Tue, 13 Sep 2022 03:08:23 +0000
Subject: [PATCH] Bug 1785872 - Extend tests for global CookieBannerRules.
 r=timhuang

Differential Revision: https://phabricator.services.mozilla.com/D156867
---
 .../cookiebanners/test/browser/browser.ini    |   1 +
 .../test/browser/browser_bannerClicking.js    | 222 ++++++++++++++++--
 .../browser/browser_cookiebannerservice.js    | 209 ++++++++++++++++-
 .../test/browser/file_banner_b.html           |  22 ++
 .../test/unit/test_cookiebannerlistservice.js |  66 ++++--
 5 files changed, 474 insertions(+), 46 deletions(-)
 create mode 100644 toolkit/components/cookiebanners/test/browser/file_banner_b.html

diff --git a/toolkit/components/cookiebanners/test/browser/browser.ini b/toolkit/components/cookiebanners/test/browser/browser.ini
index 9fab2eefb805d..32064a4fe0352 100644
--- a/toolkit/components/cookiebanners/test/browser/browser.ini
+++ b/toolkit/components/cookiebanners/test/browser/browser.ini
@@ -3,6 +3,7 @@
 [browser_bannerClicking.js]
 support-files =
     file_banner.html
+    file_banner_b.html
     file_delayed_banner.html
 [browser_cookiebannerservice.js]
 [browser_cookieinjector.js]
diff --git a/toolkit/components/cookiebanners/test/browser/browser_bannerClicking.js b/toolkit/components/cookiebanners/test/browser/browser_bannerClicking.js
index 649793f69d97f..c3d03b23c235f 100644
--- a/toolkit/components/cookiebanners/test/browser/browser_bannerClicking.js
+++ b/toolkit/components/cookiebanners/test/browser/browser_bannerClicking.js
@@ -18,6 +18,12 @@ const TEST_PATH = getRootDirectory(gTestPath).replace(
 
 const TEST_PAGE_A = TEST_ORIGIN_A + TEST_PATH + "file_banner.html";
 const TEST_PAGE_B = TEST_ORIGIN_B + TEST_PATH + "file_banner.html";
+// Page C has a different banner element ID than A and B.
+const TEST_PAGE_C = TEST_ORIGIN_C + TEST_PATH + "file_banner_b.html";
+
+function genUUID() {
+  return Services.uuid.generateUUID().number.slice(1, -1);
+}
 
 /**
  * A helper function returns a promise which resolves when the banner clicking
@@ -47,38 +53,51 @@ function promiseBannerClickingFinish(domain) {
  * @param {BrowsingContext} bc - the browsing context
  * @param {boolean} visible - if the banner should be visible.
  * @param {boolean} expected - the expected banner click state.
+ * @param {string} [bannerId] - id of the cookie banner element.
  */
-async function verifyBannerState(bc, visible, expected) {
+async function verifyBannerState(bc, visible, expected, bannerId = "banner") {
   info("Verify the cookie banner state.");
 
-  await SpecialPowers.spawn(bc, [visible, expected], (visible, expected) => {
-    let banner = content.document.getElementById("banner");
-
-    is(
-      banner.checkVisibility({
-        checkOpacity: true,
-        checkVisibilityCSS: true,
-      }),
-      visible,
-      `The banner element should be ${visible ? "visible" : "hidden"}`
-    );
+  await SpecialPowers.spawn(
+    bc,
+    [visible, expected, bannerId],
+    (visible, expected, bannerId) => {
+      let banner = content.document.getElementById(bannerId);
+
+      is(
+        banner.checkVisibility({
+          checkOpacity: true,
+          checkVisibilityCSS: true,
+        }),
+        visible,
+        `The banner element should be ${visible ? "visible" : "hidden"}`
+      );
 
-    let result = content.document.getElementById("result");
+      let result = content.document.getElementById("result");
 
-    is(result.textContent, expected, "The build click state is correct.");
-  });
+      is(result.textContent, expected, "The build click state is correct.");
+    }
+  );
 }
 
 /**
  * A helper function to open the test page and verify the banner state.
  *
- * @param {Window} win - the chrome window object.
+ * @param {Window} [win] - the chrome window object.
  * @param {String} domain - the domain of the testing page.
  * @param {String} testURL - the url of the testing page.
  * @param {boolean} visible - if the banner should be visible.
  * @param {boolean} expected - the expected banner click state.
+ * @param {string} [bannerId] - id of the cookie banner element.
  */
-async function openPageAndVerify({ win, domain, testURL, visible, expected }) {
+async function openPageAndVerify({
+  win = window,
+  domain,
+  testURL,
+  visible,
+  expected,
+  bannerId = "banner",
+}) {
   info(`Opening ${testURL}`);
   let promise = promiseBannerClickingFinish(domain);
 
@@ -86,7 +105,7 @@ async function openPageAndVerify({ win, domain, testURL, visible, expected }) {
 
   await promise;
 
-  await verifyBannerState(tab.linkedBrowser, visible, expected);
+  await verifyBannerState(tab.linkedBrowser, visible, expected, bannerId);
 
   BrowserTestUtils.removeTab(tab);
 }
@@ -143,27 +162,56 @@ function insertTestRules() {
 
   info("Inserting test rules.");
 
+  info("Add opt-out click rule for DOMAIN_A.");
   let ruleA = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
     Ci.nsICookieBannerRule
   );
+  ruleA.id = genUUID();
   ruleA.domain = TEST_DOMAIN_A;
 
   ruleA.addClickRule("div#banner", null, "button#optOut", "button#optIn");
   Services.cookieBanners.insertRule(ruleA);
 
-  // An opt-in click rule for DOMAIN_B.
+  info("Add opt-in click rule for DOMAIN_B.");
   let ruleB = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
     Ci.nsICookieBannerRule
   );
+  ruleB.id = genUUID();
   ruleB.domain = TEST_DOMAIN_B;
 
   ruleB.addClickRule("div#banner", null, null, "button#optIn");
   Services.cookieBanners.insertRule(ruleB);
+
+  info("Add global ruleC which targets a non-existing banner (presence).");
+  let ruleC = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
+    Ci.nsICookieBannerRule
+  );
+  ruleC.id = genUUID();
+  ruleC.domain = "*";
+  ruleC.addClickRule("div#nonExistingBanner", null, null, "button#optIn");
+  Services.cookieBanners.insertRule(ruleC);
+
+  info("Add global ruleD which targets a non-existing banner (presence).");
+  let ruleD = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
+    Ci.nsICookieBannerRule
+  );
+  ruleD.id = genUUID();
+  ruleD.domain = "*";
+  ruleD.addClickRule(
+    "div#nonExistingBanner2",
+    null,
+    "button#optOut",
+    "button#optIn"
+  );
+  Services.cookieBanners.insertRule(ruleD);
 }
 
 add_setup(async function() {
   await SpecialPowers.pushPrefEnv({
     set: [
+      // Enable debug logging.
+      ["cookiebanners.listService.logLevel", "Debug"],
+      ["cookiebanners.bannerClicking.logLevel", "Debug"],
       ["cookiebanners.bannerClicking.testing", true],
       ["cookiebanners.bannerClicking.timeout", 500],
       ["cookiebanners.bannerClicking.enabled", true],
@@ -383,3 +431,139 @@ add_task(async function test_embedded_iframe_pbm() {
 
   await BrowserTestUtils.closeWindow(pbmWindow);
 });
+
+/**
+ * Test the banner clicking with global rules and MODE_REJECT.
+ */
+add_task(async function test_clicking_global_rules() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["cookiebanners.service.mode", Ci.nsICookieBannerService.MODE_REJECT],
+      ["cookiebanners.service.enableGlobalRules", true],
+    ],
+  });
+
+  info("Clearing existing rules");
+  Services.cookieBanners.resetRules(false);
+
+  info("Inserting global test rules.");
+
+  info(
+    "Add global ruleA which targets an existing banner (presence) with existing buttons. This rule should handle the banner."
+  );
+  let ruleA = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
+    Ci.nsICookieBannerRule
+  );
+  ruleA.id = genUUID();
+  ruleA.domain = "*";
+  ruleA.addClickRule("div#banner", null, "button#optOut", "button#optIn");
+  Services.cookieBanners.insertRule(ruleA);
+
+  info(
+    "Add global ruleC which targets an existing banner (presence) but non-existing buttons."
+  );
+  let ruleC = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
+    Ci.nsICookieBannerRule
+  );
+  ruleC.id = genUUID();
+  ruleC.domain = "*";
+  ruleC.addClickRule(
+    "div#banner",
+    null,
+    "button#nonExistingOptOut",
+    "button#nonExistingOptIn"
+  );
+  Services.cookieBanners.insertRule(ruleC);
+
+  info("Add global ruleD which targets a non-existing banner (presence).");
+  let ruleD = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
+    Ci.nsICookieBannerRule
+  );
+  ruleD.id = genUUID();
+  ruleD.domain = "*";
+  ruleD.addClickRule("div#nonExistingBanner", null, null, "button#optIn");
+  Services.cookieBanners.insertRule(ruleD);
+
+  info("The global rule ruleA should handle both test pages with div#banner.");
+  await openPageAndVerify({
+    domain: TEST_DOMAIN_A,
+    testURL: TEST_PAGE_A,
+    visible: false,
+    expected: "OptOut",
+  });
+  await openPageAndVerify({
+    domain: TEST_DOMAIN_B,
+    testURL: TEST_PAGE_B,
+    visible: false,
+    expected: "OptOut",
+  });
+
+  info("No global rule should handle TEST_PAGE_C with div#bannerB.");
+  await openPageAndVerify({
+    domain: TEST_DOMAIN_C,
+    testURL: TEST_PAGE_C,
+    visible: true,
+    expected: "NoClick",
+    bannerId: "bannerB",
+  });
+
+  info("Test delayed banner handling with global rules.");
+  let TEST_PAGE =
+    TEST_ORIGIN_A + TEST_PATH + "file_delayed_banner.html?delay=100";
+  await openPageAndVerify({
+    domain: TEST_DOMAIN_A,
+    testURL: TEST_PAGE,
+    visible: false,
+    expected: "OptOut",
+  });
+});
+
+/**
+ * Test that domain-specific rules take precedence over global rules.
+ */
+add_task(async function test_clicking_global_rules_precedence() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      [
+        "cookiebanners.service.mode",
+        Ci.nsICookieBannerService.MODE_REJECT_OR_ACCEPT,
+      ],
+      ["cookiebanners.service.enableGlobalRules", true],
+    ],
+  });
+
+  info("Clearing existing rules");
+  Services.cookieBanners.resetRules(false);
+
+  info("Inserting global test rules.");
+
+  info(
+    "Add global ruleA which targets an existing banner (presence) with existing buttons."
+  );
+  let ruleGlobal = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
+    Ci.nsICookieBannerRule
+  );
+  ruleGlobal.id = genUUID();
+  ruleGlobal.domain = "*";
+  ruleGlobal.addClickRule("div#banner", null, "button#optOut", null);
+  Services.cookieBanners.insertRule(ruleGlobal);
+
+  info("Add domain specific rule which also targets the existing banner.");
+  let ruleDomain = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
+    Ci.nsICookieBannerRule
+  );
+  ruleDomain.id = genUUID();
+  ruleDomain.domain = TEST_DOMAIN_A;
+  ruleDomain.addClickRule("div#banner", null, null, "button#optIn");
+  Services.cookieBanners.insertRule(ruleDomain);
+
+  info("Test that the domain-specific rule applies, not the global one.");
+  await openPageAndVerify({
+    domain: TEST_DOMAIN_A,
+    testURL: TEST_PAGE_A,
+    visible: false,
+    // Because of the way the rules are setup OptOut would mean the global rule
+    // applies, opt-in means the domain specific rule applies.
+    expected: "OptIn",
+  });
+});
diff --git a/toolkit/components/cookiebanners/test/browser/browser_cookiebannerservice.js b/toolkit/components/cookiebanners/test/browser/browser_cookiebannerservice.js
index 90ca277f45143..62e2d5053b221 100644
--- a/toolkit/components/cookiebanners/test/browser/browser_cookiebannerservice.js
+++ b/toolkit/components/cookiebanners/test/browser/browser_cookiebannerservice.js
@@ -3,6 +3,10 @@
 
 "use strict";
 
+function genUUID() {
+  return Services.uuid.generateUUID().number.slice(1, -1);
+}
+
 add_setup(async function() {
   registerCleanupFunction(() => {
     Services.prefs.clearUserPref("cookiebanners.service.mode");
@@ -47,6 +51,7 @@ add_task(async function test_enabled_pref() {
   let rule = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
     Ci.nsICookieBannerRule
   );
+  rule.id = genUUID();
   rule.domain = "example.com";
 
   Assert.throws(
@@ -54,15 +59,15 @@ add_task(async function test_enabled_pref() {
       Services.cookieBanners.insertRule(rule);
     },
     /NS_ERROR_NOT_AVAILABLE/,
-    "Should have thrown NS_ERROR_NOT_AVAILABLE for rules insertRule."
+    "Should have thrown NS_ERROR_NOT_AVAILABLE for insertRule."
   );
 
   Assert.throws(
     () => {
-      Services.cookieBanners.removeRuleForDomain("example.com");
+      Services.cookieBanners.removeRule(rule);
     },
     /NS_ERROR_NOT_AVAILABLE/,
-    "Should have thrown NS_ERROR_NOT_AVAILABLE for rules removeRuleForDomain."
+    "Should have thrown NS_ERROR_NOT_AVAILABLE for removeRule."
   );
 
   Assert.throws(
@@ -76,7 +81,7 @@ add_task(async function test_enabled_pref() {
   );
   Assert.throws(
     () => {
-      Services.cookieBanners.getClickRuleForDomain("example.com");
+      Services.cookieBanners.getClickRulesForDomain("example.com");
     },
     /NS_ERROR_NOT_AVAILABLE/,
     "Should have thrown NS_ERROR_NOT_AVAILABLE for rules getClickRuleForDomain."
@@ -272,7 +277,14 @@ add_task(async function test_insertAndGetRule() {
   is(rule.cookiesOptIn.length, 0, "Should have no opt-in cookies.");
 
   info("Getting the click rule for example.com.");
-  let clickRule = Services.cookieBanners.getClickRuleForDomain("example.com");
+  let clickRules = Services.cookieBanners.getClickRulesForDomain("example.com");
+  is(
+    clickRules.length,
+    1,
+    "There should be one domain-specific click rule for example.com"
+  );
+  let [clickRule] = clickRules;
+
   is(
     clickRule.presence,
     "div#presence",
@@ -301,7 +313,15 @@ add_task(async function test_insertAndGetRule() {
   );
 
   info("Getting the click rule for example.org.");
-  let clickRule2 = Services.cookieBanners.getClickRuleForDomain("example.org");
+  let clickRules2 = Services.cookieBanners.getClickRulesForDomain(
+    "example.org"
+  );
+  is(
+    clickRules2.length,
+    1,
+    "There should be one domain-specific click rule for example.org"
+  );
+  let [clickRule2] = clickRules2;
   is(
     clickRule2.presence,
     "div#presence",
@@ -368,6 +388,7 @@ add_task(async function test_removeRule() {
   let rule = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
     Ci.nsICookieBannerRule
   );
+  rule.id = genUUID();
   rule.domain = "example.com";
 
   Services.cookieBanners.insertRule(rule);
@@ -375,6 +396,7 @@ add_task(async function test_removeRule() {
   let rule2 = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
     Ci.nsICookieBannerRule
   );
+  rule2.id = genUUID();
   rule2.domain = "example.org";
 
   Services.cookieBanners.insertRule(rule2);
@@ -386,7 +408,26 @@ add_task(async function test_removeRule() {
   );
 
   info("Removing rule for non existent example.net");
-  Services.cookieBanners.removeRuleForDomain("example.net");
+  let ruleExampleNet = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
+    Ci.nsICookieBannerRule
+  );
+  ruleExampleNet.id = genUUID();
+  ruleExampleNet.domain = "example.net";
+  Services.cookieBanners.removeRule(ruleExampleNet);
+
+  is(
+    Services.cookieBanners.rules.length,
+    2,
+    "Cookie banner service still has two rules."
+  );
+
+  info("Removing rule for non existent global rule.");
+  let ruleGlobal = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
+    Ci.nsICookieBannerRule
+  );
+  ruleGlobal.id = genUUID();
+  ruleGlobal.domain = "*";
+  Services.cookieBanners.removeRule(ruleGlobal);
 
   is(
     Services.cookieBanners.rules.length,
@@ -394,8 +435,8 @@ add_task(async function test_removeRule() {
     "Cookie banner service still has two rules."
   );
 
-  info("Removing rule for non example.com");
-  Services.cookieBanners.removeRuleForDomain("example.com");
+  info("Removing rule for example.com");
+  Services.cookieBanners.removeRule(rule);
 
   is(
     Services.cookieBanners.rules.length,
@@ -497,3 +538,153 @@ add_task(async function test_overwriteRule() {
   // Cleanup.
   Services.cookieBanners.resetRules(false);
 });
+
+add_task(async function test_globalRules() {
+  info("Enabling cookie banner service with MODE_REJECT");
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["cookiebanners.service.mode", Ci.nsICookieBannerService.MODE_REJECT],
+      ["cookiebanners.service.enableGlobalRules", true],
+    ],
+  });
+
+  info("Clear any preexisting rules");
+  Services.cookieBanners.resetRules(false);
+
+  is(
+    Services.cookieBanners.rules.length,
+    0,
+    "Cookie banner service has no rules initially."
+  );
+
+  info("Insert a site-specific rule for example.com");
+  let rule = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
+    Ci.nsICookieBannerRule
+  );
+  rule.id = genUUID();
+  rule.domain = "example.com";
+  rule.addCookie(
+    true,
+    "foo",
+    "new",
+    "example.com",
+    "/",
+    3600,
+    "",
+    false,
+    false,
+    false,
+    0,
+    0
+  );
+  rule.addClickRule("#cookieBannerExample", "#btnOptOut", "#btnOptIn");
+  Services.cookieBanners.insertRule(rule);
+
+  info(
+    "Insert a global rule with a cookie and a click rule. The cookie rule shouldn't be used."
+  );
+  let ruleGlobalA = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
+    Ci.nsICookieBannerRule
+  );
+  ruleGlobalA.id = genUUID();
+  ruleGlobalA.domain = "*";
+  ruleGlobalA.addCookie(
+    true,
+    "foo",
+    "new",
+    "example.net",
+    "/",
+    3600,
+    "",
+    false,
+    false,
+    false,
+    0,
+    0
+  );
+  ruleGlobalA.addClickRule("#globalCookieBanner", "#btnOptOut", "#btnOptIn");
+  Services.cookieBanners.insertRule(ruleGlobalA);
+
+  info("Insert a second global rule");
+  let ruleGlobalB = Cc["@mozilla.org/cookie-banner-rule;1"].createInstance(
+    Ci.nsICookieBannerRule
+  );
+  ruleGlobalB.id = genUUID();
+  ruleGlobalB.domain = "*";
+  ruleGlobalB.addClickRule("#globalCookieBannerB", "#btnOptOutB", "#btnOptIn");
+  Services.cookieBanners.insertRule(ruleGlobalB);
+
+  is(
+    Services.cookieBanners.rules.length,
+    3,
+    "Cookie Banner Service has three rules."
+  );
+
+  is(
+    Services.cookieBanners.getCookiesForURI(
+      Services.io.newURI("http://example.com")
+    ).length,
+    1,
+    "There should be a cookie rule for example.com"
+  );
+
+  is(
+    Services.cookieBanners.getClickRulesForDomain("example.com").length,
+    1,
+    "There should be a a click rule for example.com"
+  );
+
+  is(
+    Services.cookieBanners.getCookiesForURI(
+      Services.io.newURI("http://thishasnorule.com")
+    ).length,
+    0,
+    "There should be no cookie rule for thishasnorule.com"
+  );
+
+  let clickRules = Services.cookieBanners.getClickRulesForDomain(
+    Services.io.newURI("http://thishasnorule.com")
+  );
+  is(
+    clickRules.length,
+    2,
+    "There should be two click rules for thishasnorule.com"
+  );
+  ok(
+    clickRules.every(rule => rule.presence.startsWith("#globalCookieBanner")),
+    "The returned click rules should be global rules."
+  );
+
+  info("Disabling global rules");
+  await SpecialPowers.pushPrefEnv({
+    set: [["cookiebanners.service.enableGlobalRules", false]],
+  });
+
+  is(
+    Services.cookieBanners.rules.length,
+    1,
+    "Cookie Banner Service has 1 rule."
+  );
+
+  is(
+    Services.cookieBanners.rules[0].id,
+    rule.id,
+    "It should be the domain specific rule"
+  );
+
+  is(
+    Services.cookieBanners.getCookiesForURI(
+      Services.io.newURI("http://thishasnorule.com")
+    ).length,
+    0,
+    "There should be no cookie rule for thishasnorule.com"
+  );
+
+  is(
+    Services.cookieBanners.getClickRulesForDomain(
+      Services.io.newURI("http://thishasnorule.com")
+    ).length,
+    0,
+    "There should be no click rules for thishasnorule.com since global rules are disabled"
+  );
+});
diff --git a/toolkit/components/cookiebanners/test/browser/file_banner_b.html b/toolkit/components/cookiebanners/test/browser/file_banner_b.html
new file mode 100644
index 0000000000000..bf8df76bd3dc5
--- /dev/null
+++ b/toolkit/components/cookiebanners/test/browser/file_banner_b.html
@@ -0,0 +1,22 @@
+<html>
+<head>
+  <title>A top-level page with cookie banner</title>
+  <script>
+    function clickOptOut() {
+      document.getElementById("result").textContent = "OptOut";
+    }
+
+    function clickOptIn() {
+      document.getElementById("result").textContent = "OptIn";
+    }
+  </script>
+</head>
+<body>
+  <h1>This is the top-level page</h1>
+  <div id="bannerB">
+    <button id="optOut" onclick="clickOptOut()">OptOut</button>
+    <button id="optIn" onclick="clickOptIn()">OptIn</button>
+  </div>
+  <p id="result">NoClick</p>
+</body>
+</html>
diff --git a/toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js b/toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js
index a27a041a11d91..8bc65e50a544b 100644
--- a/toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js
+++ b/toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js
@@ -19,8 +19,12 @@ let rulesInserted = [];
 let rulesRemoved = [];
 let insertCallback = null;
 
+function genUUID() {
+  return Services.uuid.generateUUID().number.slice(1, -1);
+}
+
 const RULE_A_ORIGINAL = {
-  id: "example.com",
+  id: genUUID(),
   click: {
     optOut: "#fooOut",
     presence: "#foobar",
@@ -39,7 +43,7 @@ const RULE_A_ORIGINAL = {
 };
 
 const RULE_B = {
-  id: "example.org",
+  id: genUUID(),
   click: {
     optOut: "#fooOutB",
     presence: "#foobarB",
@@ -57,7 +61,7 @@ const RULE_B = {
 };
 
 const RULE_C = {
-  id: "example.net",
+  id: genUUID(),
   click: {
     optOut: "#fooOutC",
     presence: "#foobarC",
@@ -76,7 +80,7 @@ const RULE_C = {
 };
 
 const RULE_A_UPDATED = {
-  id: "example.com",
+  id: RULE_A_ORIGINAL,
   click: {
     optOut: "#fooOut",
     optIn: "#barIn",
@@ -99,7 +103,7 @@ const RULE_A_UPDATED = {
 };
 
 const INVALID_RULE_CLICK = {
-  id: "foobar.com",
+  id: genUUID(),
   domain: "foobar.com",
   click: {
     presence: 1,
@@ -109,6 +113,26 @@ const INVALID_RULE_CLICK = {
 
 const INVALID_RULE_EMPTY = {};
 
+const RULE_D_GLOBAL = {
+  id: genUUID(),
+  click: {
+    optOut: "#globalOptOutD",
+    presence: "#globalBannerD",
+  },
+  domain: "*",
+  cookies: {},
+};
+
+const RULE_E_GLOBAL = {
+  id: genUUID(),
+  click: {
+    optOut: "#globalOptOutE",
+    presence: "#globalBannerE",
+  },
+  domain: "*",
+  cookies: {},
+};
+
 // Testing with RemoteSettings requires a profile.
 do_get_profile();
 
@@ -117,8 +141,8 @@ add_setup(async () => {
   Services.prefs.setStringPref("cookiebanners.listService.logLevel", "Debug");
 
   // Stub some nsICookieBannerService methods for easy testing.
-  let removeRuleForDomain = sinon.stub().callsFake(domain => {
-    rulesRemoved.push(domain);
+  let removeRule = sinon.stub().callsFake(rule => {
+    rulesRemoved.push(rule);
   });
 
   let insertRule = sinon.stub().callsFake(rule => {
@@ -127,7 +151,7 @@ add_setup(async () => {
   });
 
   let oldCookieBanners = Services.cookieBanners;
-  Services.cookieBanners = { insertRule, removeRuleForDomain, resetRules() {} };
+  Services.cookieBanners = { insertRule, removeRule, resetRules() {} };
 
   // Remove stubs on test end.
   registerCleanupFunction(() => {
@@ -247,10 +271,10 @@ add_task(async function test_remotesettings_sync() {
   await cookieBannerListService.initForTest();
 
   const payload = {
-    current: [RULE_A_ORIGINAL, RULE_C],
-    created: [RULE_B],
+    current: [RULE_A_ORIGINAL, RULE_C, RULE_D_GLOBAL],
+    created: [RULE_B, RULE_E_GLOBAL],
     updated: [{ old: RULE_A_ORIGINAL, new: RULE_A_UPDATED }],
-    deleted: [RULE_C],
+    deleted: [RULE_C, RULE_D_GLOBAL],
   };
 
   Assert.equal(rulesInserted.length, 0, "No inserted rules initially.");
@@ -259,12 +283,13 @@ add_task(async function test_remotesettings_sync() {
   info("Dispatching artificial RemoteSettings sync event");
   await RemoteSettings(COLLECTION_NAME).emit("sync", { data: payload });
 
-  Assert.equal(rulesInserted.length, 2, "Two inserted rules after sync.");
-  Assert.equal(rulesRemoved.length, 1, "One removed rules after sync.");
+  Assert.equal(rulesInserted.length, 3, "Three inserted rules after sync.");
+  Assert.equal(rulesRemoved.length, 2, "Two removed rules after sync.");
 
-  let ruleA = rulesInserted.find(rule => rule.domain == RULE_A_UPDATED.domain);
-  let ruleB = rulesInserted.find(rule => rule.domain == RULE_B.domain);
-  let ruleCDomain = rulesRemoved[0];
+  let ruleA = rulesInserted.find(rule => rule.id == RULE_A_UPDATED.id);
+  let ruleB = rulesInserted.find(rule => rule.id == RULE_B.id);
+  let ruleE = rulesInserted.find(rule => rule.id == RULE_E_GLOBAL.id);
+  let ruleC = rulesRemoved[0];
 
   info("Testing that service inserted updated version of RULE_A.");
   Assert.equal(
@@ -292,8 +317,13 @@ add_task(async function test_remotesettings_sync() {
     );
   }
 
-  Assert.equal(ruleB.domain, RULE_B.domain, "Should have inserted RULE_B");
-  Assert.equal(ruleCDomain, RULE_C.domain, "Should have removed RULE_C");
+  Assert.equal(ruleB.id, RULE_B.id, "Should have inserted RULE_B");
+  Assert.equal(ruleC.id, RULE_C.id, "Should have removed RULE_C");
+  Assert.equal(
+    ruleE.id,
+    RULE_E_GLOBAL.id,
+    "Should have inserted RULE_E_GLOBAL"
+  );
 
   // Cleanup
   cookieBannerListService.shutdown();
-- 
GitLab