@duckduckgo/autoconsent 14.67.0 → 14.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/AGENTS.md +36 -11
  2. package/CHANGELOG.md +39 -0
  3. package/data/coverage.json +5846 -2663
  4. package/dist/addon-firefox/background.bundle.js +0 -2
  5. package/dist/addon-firefox/compact-rules.json +1 -1
  6. package/dist/addon-firefox/content.bundle.js +17 -5
  7. package/dist/addon-firefox/manifest.json +1 -1
  8. package/dist/addon-firefox/rules.json +1 -1
  9. package/dist/addon-mv3/background.bundle.js +0 -2
  10. package/dist/addon-mv3/compact-rules.json +1 -1
  11. package/dist/addon-mv3/content.bundle.js +17 -5
  12. package/dist/addon-mv3/manifest.json +1 -1
  13. package/dist/addon-mv3/rules.json +1 -1
  14. package/dist/autoconsent.cjs.js +17 -5
  15. package/dist/autoconsent.esm.js +17 -5
  16. package/dist/autoconsent.extra.cjs.js +17 -5
  17. package/dist/autoconsent.extra.esm.js +17 -5
  18. package/dist/autoconsent.playwright.js +17 -5
  19. package/dist/types/eval-snippets.d.ts +0 -2
  20. package/dist/types/heuristics.d.ts +1 -0
  21. package/docs/rule-syntax.md +32 -1
  22. package/lib/eval-snippets.ts +0 -3
  23. package/lib/heuristics.ts +18 -3
  24. package/package.json +1 -1
  25. package/rules/autoconsent/elsevier-pure.json +31 -0
  26. package/rules/autoconsent/facebook.json +21 -7
  27. package/rules/autoconsent/fullertonhotels.com.json +37 -0
  28. package/rules/autoconsent/geni.com.json +8 -5
  29. package/rules/autoconsent/remarkable.com.json +6 -27
  30. package/rules/autoconsent/unicourt.json +5 -5
  31. package/rules/compact-rules.json +1 -1
  32. package/rules/generated/auto_AU_flysaa.com_qsm.json +10 -9
  33. package/rules/generated/auto_DE_immobilien.sparkasse.de_zj7.json +10 -10
  34. package/rules/generated/auto_GB_www2.hm.com_0.json +15 -6
  35. package/rules/rules.json +1 -1
  36. package/tests/elsevier-pure.spec.ts +11 -0
  37. package/tests/fullertonhotels.com.spec.ts +6 -0
  38. package/tests/generated/auto_GB_www2.hm.com_0.spec.ts +16 -0
  39. package/tests-wtr/heuristics/get-actionable-popups.html +24 -0
  40. package/tests-wtr/heuristics/get-actionable-popups.ts +36 -0
  41. package/tests-wtr/heuristics/heuristics-utils.test.ts +43 -0
  42. package/rules/autoconsent/mediavine.json +0 -17
  43. package/rules/autoconsent/national-lottery-co-uk.json +0 -11
  44. package/rules/autoconsent/ouraring.json +0 -36
  45. package/rules/generated/auto_AU_discover.utas.edu.au_k2n.json +0 -37
  46. package/rules/generated/auto_AU_experts.deakin.edu.au_0kw.json +0 -40
  47. package/rules/generated/auto_AU_experts.griffith.edu.au_oi0.json +0 -37
  48. package/rules/generated/auto_AU_fullertonhotels.com_y46.json +0 -40
  49. package/rules/generated/auto_AU_profiles.uts.edu.au_ouv.json +0 -37
  50. package/rules/generated/auto_AU_scholars.latrobe.edu.au_li6.json +0 -40
  51. package/rules/generated/auto_CA_discover.research.utoronto.ca_vtf.json +0 -40
  52. package/rules/generated/auto_CA_remarkable.com_kt9.json +0 -40
  53. package/rules/generated/auto_DE_www2.hm.com_beo.json +0 -40
  54. package/rules/generated/auto_GB_profiles.ucl.ac.uk_lpo.json +0 -40
  55. package/tests/generated/auto_AU_discover.utas.edu.au_k2n.spec.ts +0 -6
  56. package/tests/generated/auto_AU_experts.deakin.edu.au_0kw.spec.ts +0 -6
  57. package/tests/generated/auto_AU_experts.griffith.edu.au_oi0.spec.ts +0 -6
  58. package/tests/generated/auto_AU_fullertonhotels.com_y46.spec.ts +0 -6
  59. package/tests/generated/auto_AU_profiles.uts.edu.au_ouv.spec.ts +0 -6
  60. package/tests/generated/auto_AU_scholars.latrobe.edu.au_li6.spec.ts +0 -6
  61. package/tests/generated/auto_CA_discover.research.utoronto.ca_vtf.spec.ts +0 -6
  62. package/tests/generated/auto_CA_remarkable.com_kt9.spec.ts +0 -2
  63. package/tests/generated/auto_DE_www2.hm.com_beo.spec.ts +0 -6
  64. package/tests/generated/auto_GB_profiles.ucl.ac.uk_lpo.spec.ts +0 -6
  65. package/tests/mediavine.spec.ts +0 -5
  66. package/tests/national-lottery.spec.ts +0 -5
  67. package/tests/ouraring.spec.ts +0 -3
@@ -583,7 +583,6 @@ var snippets = {
583
583
  if (x.checked) x.click();
584
584
  }) || true,
585
585
  EVAL_IUBENDA_1: () => !!document.cookie.match(/_iub_cs-\d+=/),
586
- EVAL_MEDIAVINE_0: () => document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((x) => x.checked && x.click()) || true,
587
586
  EVAL_MICROSOFT_0: () => Array.from(document.querySelectorAll("div > button")).filter((el) => el.innerText.match("Reject|Ablehnen"))[0].click() || true,
588
587
  EVAL_MICROSOFT_1: () => Array.from(document.querySelectorAll("div > button")).filter((el) => el.innerText.match("Accept|Annehmen"))[0].click() || true,
589
588
  EVAL_MICROSOFT_2: () => !!document.cookie.match("MSCC|GHCC"),
@@ -599,7 +598,6 @@ var snippets = {
599
598
  ).status === "deny",
600
599
  EVAL_POVR_GOBACK: () => window.history.back() || true,
601
600
  EVAL_PUBTECH_0: () => document.cookie.includes("euconsent-v2") && (document.cookie.match(/.YAAAAAAAAAAA/) || document.cookie.match(/.aAAAAAAAAAAA/) || document.cookie.match(/.YAAACFgAAAAA/)),
602
- EVAL_REMARKABLE_TEST: () => !!localStorage.getItem("rmCookieConsent"),
603
601
  EVAL_SHOPIFY_TEST: () => document.cookie.includes("gdpr_cookie_consent=0") || document.cookie.includes("_tracking_consent=") && JSON.parse(
604
602
  decodeURIComponent(
605
603
  document.cookie.split(";").find((s) => s.trim().startsWith("_tracking_consent")).split("=")[1]
@@ -1577,6 +1575,15 @@ function collectPotentialPopups(isFramed) {
1577
1575
  }
1578
1576
  return potentialPopups;
1579
1577
  }
1578
+ function isDialogLikeElement(node) {
1579
+ if (node.tagName === "DIALOG" && node.hasAttribute("open")) {
1580
+ return true;
1581
+ }
1582
+ if (node.getAttribute("role") === "dialog" || node.getAttribute("aria-modal") === "true") {
1583
+ return true;
1584
+ }
1585
+ return false;
1586
+ }
1580
1587
  function getPopupLikeElements() {
1581
1588
  const walker = document.createTreeWalker(
1582
1589
  document.documentElement,
@@ -1587,9 +1594,14 @@ function getPopupLikeElements() {
1587
1594
  if (node.tagName === "BODY") {
1588
1595
  return NodeFilter.FILTER_SKIP;
1589
1596
  }
1590
- const cssPosition = window.getComputedStyle(node).position;
1591
- if ((cssPosition === "fixed" || cssPosition === "sticky") && isElementVisible(node)) {
1592
- return NodeFilter.FILTER_ACCEPT;
1597
+ if (isElementVisible(node)) {
1598
+ const cssPosition = window.getComputedStyle(node).position;
1599
+ if (cssPosition === "fixed" || cssPosition === "sticky") {
1600
+ return NodeFilter.FILTER_ACCEPT;
1601
+ }
1602
+ if (isDialogLikeElement(node)) {
1603
+ return NodeFilter.FILTER_ACCEPT;
1604
+ }
1593
1605
  }
1594
1606
  return NodeFilter.FILTER_SKIP;
1595
1607
  }
@@ -553,7 +553,6 @@ var snippets = {
553
553
  if (x.checked) x.click();
554
554
  }) || true,
555
555
  EVAL_IUBENDA_1: () => !!document.cookie.match(/_iub_cs-\d+=/),
556
- EVAL_MEDIAVINE_0: () => document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((x) => x.checked && x.click()) || true,
557
556
  EVAL_MICROSOFT_0: () => Array.from(document.querySelectorAll("div > button")).filter((el) => el.innerText.match("Reject|Ablehnen"))[0].click() || true,
558
557
  EVAL_MICROSOFT_1: () => Array.from(document.querySelectorAll("div > button")).filter((el) => el.innerText.match("Accept|Annehmen"))[0].click() || true,
559
558
  EVAL_MICROSOFT_2: () => !!document.cookie.match("MSCC|GHCC"),
@@ -569,7 +568,6 @@ var snippets = {
569
568
  ).status === "deny",
570
569
  EVAL_POVR_GOBACK: () => window.history.back() || true,
571
570
  EVAL_PUBTECH_0: () => document.cookie.includes("euconsent-v2") && (document.cookie.match(/.YAAAAAAAAAAA/) || document.cookie.match(/.aAAAAAAAAAAA/) || document.cookie.match(/.YAAACFgAAAAA/)),
572
- EVAL_REMARKABLE_TEST: () => !!localStorage.getItem("rmCookieConsent"),
573
571
  EVAL_SHOPIFY_TEST: () => document.cookie.includes("gdpr_cookie_consent=0") || document.cookie.includes("_tracking_consent=") && JSON.parse(
574
572
  decodeURIComponent(
575
573
  document.cookie.split(";").find((s) => s.trim().startsWith("_tracking_consent")).split("=")[1]
@@ -1547,6 +1545,15 @@ function collectPotentialPopups(isFramed) {
1547
1545
  }
1548
1546
  return potentialPopups;
1549
1547
  }
1548
+ function isDialogLikeElement(node) {
1549
+ if (node.tagName === "DIALOG" && node.hasAttribute("open")) {
1550
+ return true;
1551
+ }
1552
+ if (node.getAttribute("role") === "dialog" || node.getAttribute("aria-modal") === "true") {
1553
+ return true;
1554
+ }
1555
+ return false;
1556
+ }
1550
1557
  function getPopupLikeElements() {
1551
1558
  const walker = document.createTreeWalker(
1552
1559
  document.documentElement,
@@ -1557,9 +1564,14 @@ function getPopupLikeElements() {
1557
1564
  if (node.tagName === "BODY") {
1558
1565
  return NodeFilter.FILTER_SKIP;
1559
1566
  }
1560
- const cssPosition = window.getComputedStyle(node).position;
1561
- if ((cssPosition === "fixed" || cssPosition === "sticky") && isElementVisible(node)) {
1562
- return NodeFilter.FILTER_ACCEPT;
1567
+ if (isElementVisible(node)) {
1568
+ const cssPosition = window.getComputedStyle(node).position;
1569
+ if (cssPosition === "fixed" || cssPosition === "sticky") {
1570
+ return NodeFilter.FILTER_ACCEPT;
1571
+ }
1572
+ if (isDialogLikeElement(node)) {
1573
+ return NodeFilter.FILTER_ACCEPT;
1574
+ }
1563
1575
  }
1564
1576
  return NodeFilter.FILTER_SKIP;
1565
1577
  }
@@ -11549,7 +11549,6 @@ var snippets = {
11549
11549
  if (x.checked) x.click();
11550
11550
  }) || true,
11551
11551
  EVAL_IUBENDA_1: () => !!document.cookie.match(/_iub_cs-\d+=/),
11552
- EVAL_MEDIAVINE_0: () => document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((x) => x.checked && x.click()) || true,
11553
11552
  EVAL_MICROSOFT_0: () => Array.from(document.querySelectorAll("div > button")).filter((el) => el.innerText.match("Reject|Ablehnen"))[0].click() || true,
11554
11553
  EVAL_MICROSOFT_1: () => Array.from(document.querySelectorAll("div > button")).filter((el) => el.innerText.match("Accept|Annehmen"))[0].click() || true,
11555
11554
  EVAL_MICROSOFT_2: () => !!document.cookie.match("MSCC|GHCC"),
@@ -11565,7 +11564,6 @@ var snippets = {
11565
11564
  ).status === "deny",
11566
11565
  EVAL_POVR_GOBACK: () => window.history.back() || true,
11567
11566
  EVAL_PUBTECH_0: () => document.cookie.includes("euconsent-v2") && (document.cookie.match(/.YAAAAAAAAAAA/) || document.cookie.match(/.aAAAAAAAAAAA/) || document.cookie.match(/.YAAACFgAAAAA/)),
11568
- EVAL_REMARKABLE_TEST: () => !!localStorage.getItem("rmCookieConsent"),
11569
11567
  EVAL_SHOPIFY_TEST: () => document.cookie.includes("gdpr_cookie_consent=0") || document.cookie.includes("_tracking_consent=") && JSON.parse(
11570
11568
  decodeURIComponent(
11571
11569
  document.cookie.split(";").find((s) => s.trim().startsWith("_tracking_consent")).split("=")[1]
@@ -12399,6 +12397,15 @@ function collectPotentialPopups(isFramed) {
12399
12397
  }
12400
12398
  return potentialPopups;
12401
12399
  }
12400
+ function isDialogLikeElement(node) {
12401
+ if (node.tagName === "DIALOG" && node.hasAttribute("open")) {
12402
+ return true;
12403
+ }
12404
+ if (node.getAttribute("role") === "dialog" || node.getAttribute("aria-modal") === "true") {
12405
+ return true;
12406
+ }
12407
+ return false;
12408
+ }
12402
12409
  function getPopupLikeElements() {
12403
12410
  const walker = document.createTreeWalker(
12404
12411
  document.documentElement,
@@ -12409,9 +12416,14 @@ function getPopupLikeElements() {
12409
12416
  if (node.tagName === "BODY") {
12410
12417
  return NodeFilter.FILTER_SKIP;
12411
12418
  }
12412
- const cssPosition = window.getComputedStyle(node).position;
12413
- if ((cssPosition === "fixed" || cssPosition === "sticky") && isElementVisible(node)) {
12414
- return NodeFilter.FILTER_ACCEPT;
12419
+ if (isElementVisible(node)) {
12420
+ const cssPosition = window.getComputedStyle(node).position;
12421
+ if (cssPosition === "fixed" || cssPosition === "sticky") {
12422
+ return NodeFilter.FILTER_ACCEPT;
12423
+ }
12424
+ if (isDialogLikeElement(node)) {
12425
+ return NodeFilter.FILTER_ACCEPT;
12426
+ }
12415
12427
  }
12416
12428
  return NodeFilter.FILTER_SKIP;
12417
12429
  }
@@ -11483,7 +11483,6 @@ var snippets = {
11483
11483
  if (x.checked) x.click();
11484
11484
  }) || true,
11485
11485
  EVAL_IUBENDA_1: () => !!document.cookie.match(/_iub_cs-\d+=/),
11486
- EVAL_MEDIAVINE_0: () => document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((x) => x.checked && x.click()) || true,
11487
11486
  EVAL_MICROSOFT_0: () => Array.from(document.querySelectorAll("div > button")).filter((el) => el.innerText.match("Reject|Ablehnen"))[0].click() || true,
11488
11487
  EVAL_MICROSOFT_1: () => Array.from(document.querySelectorAll("div > button")).filter((el) => el.innerText.match("Accept|Annehmen"))[0].click() || true,
11489
11488
  EVAL_MICROSOFT_2: () => !!document.cookie.match("MSCC|GHCC"),
@@ -11499,7 +11498,6 @@ var snippets = {
11499
11498
  ).status === "deny",
11500
11499
  EVAL_POVR_GOBACK: () => window.history.back() || true,
11501
11500
  EVAL_PUBTECH_0: () => document.cookie.includes("euconsent-v2") && (document.cookie.match(/.YAAAAAAAAAAA/) || document.cookie.match(/.aAAAAAAAAAAA/) || document.cookie.match(/.YAAACFgAAAAA/)),
11502
- EVAL_REMARKABLE_TEST: () => !!localStorage.getItem("rmCookieConsent"),
11503
11501
  EVAL_SHOPIFY_TEST: () => document.cookie.includes("gdpr_cookie_consent=0") || document.cookie.includes("_tracking_consent=") && JSON.parse(
11504
11502
  decodeURIComponent(
11505
11503
  document.cookie.split(";").find((s) => s.trim().startsWith("_tracking_consent")).split("=")[1]
@@ -12333,6 +12331,15 @@ function collectPotentialPopups(isFramed) {
12333
12331
  }
12334
12332
  return potentialPopups;
12335
12333
  }
12334
+ function isDialogLikeElement(node) {
12335
+ if (node.tagName === "DIALOG" && node.hasAttribute("open")) {
12336
+ return true;
12337
+ }
12338
+ if (node.getAttribute("role") === "dialog" || node.getAttribute("aria-modal") === "true") {
12339
+ return true;
12340
+ }
12341
+ return false;
12342
+ }
12336
12343
  function getPopupLikeElements() {
12337
12344
  const walker = document.createTreeWalker(
12338
12345
  document.documentElement,
@@ -12343,9 +12350,14 @@ function getPopupLikeElements() {
12343
12350
  if (node.tagName === "BODY") {
12344
12351
  return NodeFilter.FILTER_SKIP;
12345
12352
  }
12346
- const cssPosition = window.getComputedStyle(node).position;
12347
- if ((cssPosition === "fixed" || cssPosition === "sticky") && isElementVisible(node)) {
12348
- return NodeFilter.FILTER_ACCEPT;
12353
+ if (isElementVisible(node)) {
12354
+ const cssPosition = window.getComputedStyle(node).position;
12355
+ if (cssPosition === "fixed" || cssPosition === "sticky") {
12356
+ return NodeFilter.FILTER_ACCEPT;
12357
+ }
12358
+ if (isDialogLikeElement(node)) {
12359
+ return NodeFilter.FILTER_ACCEPT;
12360
+ }
12349
12361
  }
12350
12362
  return NodeFilter.FILTER_SKIP;
12351
12363
  }
@@ -555,7 +555,6 @@
555
555
  if (x.checked) x.click();
556
556
  }) || true,
557
557
  EVAL_IUBENDA_1: () => !!document.cookie.match(/_iub_cs-\d+=/),
558
- EVAL_MEDIAVINE_0: () => document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((x) => x.checked && x.click()) || true,
559
558
  EVAL_MICROSOFT_0: () => Array.from(document.querySelectorAll("div > button")).filter((el) => el.innerText.match("Reject|Ablehnen"))[0].click() || true,
560
559
  EVAL_MICROSOFT_1: () => Array.from(document.querySelectorAll("div > button")).filter((el) => el.innerText.match("Accept|Annehmen"))[0].click() || true,
561
560
  EVAL_MICROSOFT_2: () => !!document.cookie.match("MSCC|GHCC"),
@@ -571,7 +570,6 @@
571
570
  ).status === "deny",
572
571
  EVAL_POVR_GOBACK: () => window.history.back() || true,
573
572
  EVAL_PUBTECH_0: () => document.cookie.includes("euconsent-v2") && (document.cookie.match(/.YAAAAAAAAAAA/) || document.cookie.match(/.aAAAAAAAAAAA/) || document.cookie.match(/.YAAACFgAAAAA/)),
574
- EVAL_REMARKABLE_TEST: () => !!localStorage.getItem("rmCookieConsent"),
575
573
  EVAL_SHOPIFY_TEST: () => document.cookie.includes("gdpr_cookie_consent=0") || document.cookie.includes("_tracking_consent=") && JSON.parse(
576
574
  decodeURIComponent(
577
575
  document.cookie.split(";").find((s) => s.trim().startsWith("_tracking_consent")).split("=")[1]
@@ -1549,6 +1547,15 @@
1549
1547
  }
1550
1548
  return potentialPopups;
1551
1549
  }
1550
+ function isDialogLikeElement(node) {
1551
+ if (node.tagName === "DIALOG" && node.hasAttribute("open")) {
1552
+ return true;
1553
+ }
1554
+ if (node.getAttribute("role") === "dialog" || node.getAttribute("aria-modal") === "true") {
1555
+ return true;
1556
+ }
1557
+ return false;
1558
+ }
1552
1559
  function getPopupLikeElements() {
1553
1560
  const walker = document.createTreeWalker(
1554
1561
  document.documentElement,
@@ -1559,9 +1566,14 @@
1559
1566
  if (node.tagName === "BODY") {
1560
1567
  return NodeFilter.FILTER_SKIP;
1561
1568
  }
1562
- const cssPosition = window.getComputedStyle(node).position;
1563
- if ((cssPosition === "fixed" || cssPosition === "sticky") && isElementVisible(node)) {
1564
- return NodeFilter.FILTER_ACCEPT;
1569
+ if (isElementVisible(node)) {
1570
+ const cssPosition = window.getComputedStyle(node).position;
1571
+ if (cssPosition === "fixed" || cssPosition === "sticky") {
1572
+ return NodeFilter.FILTER_ACCEPT;
1573
+ }
1574
+ if (isDialogLikeElement(node)) {
1575
+ return NodeFilter.FILTER_ACCEPT;
1576
+ }
1565
1577
  }
1566
1578
  return NodeFilter.FILTER_SKIP;
1567
1579
  }
@@ -42,7 +42,6 @@ export declare const snippets: {
42
42
  EVAL_GDPR_LEGAL_COOKIE_TEST: () => boolean;
43
43
  EVAL_IUBENDA_0: () => boolean;
44
44
  EVAL_IUBENDA_1: () => boolean;
45
- EVAL_MEDIAVINE_0: () => boolean;
46
45
  EVAL_MICROSOFT_0: () => any;
47
46
  EVAL_MICROSOFT_1: () => any;
48
47
  EVAL_MICROSOFT_2: () => boolean;
@@ -52,7 +51,6 @@ export declare const snippets: {
52
51
  EVAL_PANDECTES_TEST: () => boolean;
53
52
  EVAL_POVR_GOBACK: () => boolean;
54
53
  EVAL_PUBTECH_0: () => false | RegExpMatchArray | null;
55
- EVAL_REMARKABLE_TEST: () => boolean;
56
54
  EVAL_SHOPIFY_TEST: () => boolean;
57
55
  EVAL_SKYSCANNER_TEST: () => boolean | null;
58
56
  EVAL_SIRDATA_UNBLOCK_SCROLL: () => boolean;
@@ -10,6 +10,7 @@ export declare function classifyButtons(buttons: ButtonData[]): {
10
10
  };
11
11
  export declare function isRejectButton(buttonText: string, rejectPatterns?: (string | RegExp)[], neverMatchPatterns?: RegExp[]): boolean;
12
12
  export declare function cleanButtonText(buttonText: string): string;
13
+ export declare function isDialogLikeElement(node: HTMLElement): boolean;
13
14
  /**
14
15
  * Serialize all actionable buttons on the page
15
16
  */
@@ -22,9 +22,14 @@ Both JSON and class implementations have the following components:
22
22
  * `frame` - boolean, set to `true` if the rule should be executed in nested frames (default: `false`)
23
23
  * `urlPattern` - string, specifies a regular expression that should match the page URL (default: empty)
24
24
  * (optional) `test` - a list of actions to verify a successful opt-out. This is currently only used in Playwright tests.
25
+ * (optional) `minimumRuleStepVersion` - the minimum rule step version needed to execute this rule. Defaults to `1`. See [Rule Step Versioning](#rule-step-versioning).
25
26
 
26
27
 
27
- `detectCMP`, `detectPopup`, `optOut`, `optIn`, and `test` are defined as a set of checks or actions on the page. In the JSON syntax this is a list of `AutoConsentRuleStep` objects. For `detect` checks, we return true for the check if all steps return true. For opt in and out, we execute actions in order, exiting if one fails. The following checks/actions are supported:
28
+ `detectCMP`, `detectPopup`, `optOut`, `optIn`, and `test` are defined as a set of checks or actions on the page. In the JSON syntax this is a list of `AutoConsentRuleStep` objects. For `detect` checks, we return true for the check if all steps return true. For opt in and out, we execute actions in order, exiting if one fails.
29
+
30
+ **Important:** Do not use `wait` steps in `detectCmp` or `detectPopup` arrays. Detection must be fast and non-blocking -- the engine already retries detection automatically with its own timing. Adding `wait` steps to detection slows down detection of other rules.
31
+
32
+ The following checks/actions are supported:
28
33
 
29
34
  ## Element selectors
30
35
 
@@ -240,3 +245,29 @@ new AutoConsent({
240
245
  ## Context filters
241
246
 
242
247
  By default, rules will be executed in all top-level documents. Some rules are designed for specific contexts (e.g. only nested iframes, or only specific URLs). This can be configured in `runContext` field (see [runContext](#rule-syntax-reference) above).
248
+
249
+ ## Rule Step Versioning
250
+
251
+ New step types are occasionally added to the autoconsent engine (e.g. `removeClass`, `setStyle`, `addStyle` were added in version 2). Because rules can be shipped to clients independently of app releases, a rule that uses a newer step type could end up on a client that doesn't support it yet.
252
+
253
+ The `minimumRuleStepVersion` field solves this: clients compare the rule's declared version against their own supported version (`SUPPORTED_RULE_STEP_VERSION` in `lib/rules.ts`) and silently skip rules they cannot execute.
254
+
255
+ ### Version history
256
+
257
+ | Version | Step types added |
258
+ |---------|-----------------|
259
+ | 1 | All original step types (`exists`, `visible`, `waitFor`, `waitForVisible`, `click`, `waitForThenClick`, `wait`, `hide`, `if`/`then`/`else`, `any`, `eval`, `cookieContains`, `negated`) |
260
+ | 2 | `removeClass`, `setStyle`, `addStyle` |
261
+
262
+ ### When to set it
263
+
264
+ - If a rule only uses version-1 step types, omit the field (defaults to `1`).
265
+ - If a rule uses `removeClass`, `setStyle`, or `addStyle`, set `"minimumRuleStepVersion": 2`.
266
+ - When a future version introduces new step types, any rule using them must set `minimumRuleStepVersion` to the corresponding version number.
267
+
268
+ ### Adding new step types
269
+
270
+ When introducing a new step type:
271
+ 1. Bump `SUPPORTED_RULE_STEP_VERSION` in `lib/rules.ts` and add a comment describing what was added.
272
+ 2. Add the new step type definition to the `AutoConsentRuleStep` union in the same file.
273
+ 3. Any rule using the new step type must set `minimumRuleStepVersion` to the new version number.
@@ -127,8 +127,6 @@ export const snippets = {
127
127
  if (x.checked) x.click();
128
128
  }) || true,
129
129
  EVAL_IUBENDA_1: () => !!document.cookie.match(/_iub_cs-\d+=/),
130
- EVAL_MEDIAVINE_0: () =>
131
- document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((x) => x.checked && x.click()) || true,
132
130
  EVAL_MICROSOFT_0: () =>
133
131
  Array.from(document.querySelectorAll('div > button'))
134
132
  .filter((el) => el.innerText.match('Reject|Ablehnen'))[0]
@@ -158,7 +156,6 @@ export const snippets = {
158
156
  EVAL_PUBTECH_0: () =>
159
157
  document.cookie.includes('euconsent-v2') &&
160
158
  (document.cookie.match(/.YAAAAAAAAAAA/) || document.cookie.match(/.aAAAAAAAAAAA/) || document.cookie.match(/.YAAACFgAAAAA/)),
161
- EVAL_REMARKABLE_TEST: () => !!localStorage.getItem('rmCookieConsent'),
162
159
  EVAL_SHOPIFY_TEST: () =>
163
160
  document.cookie.includes('gdpr_cookie_consent=0') ||
164
161
  (document.cookie.includes('_tracking_consent=') &&
package/lib/heuristics.ts CHANGED
@@ -126,6 +126,16 @@ function collectPotentialPopups(isFramed: boolean): PopupData[] {
126
126
  return potentialPopups;
127
127
  }
128
128
 
129
+ export function isDialogLikeElement(node: HTMLElement): boolean {
130
+ if (node.tagName === 'DIALOG' && node.hasAttribute('open')) {
131
+ return true;
132
+ }
133
+ if (node.getAttribute('role') === 'dialog' || node.getAttribute('aria-modal') === 'true') {
134
+ return true;
135
+ }
136
+ return false;
137
+ }
138
+
129
139
  /**
130
140
  * Heuristic to get all elements that look like "popups"
131
141
  * TODO: this heuristic is too strict, not all popups are actually sticky/fixed
@@ -139,9 +149,14 @@ function getPopupLikeElements(): HTMLElement[] {
139
149
  if (node.tagName === 'BODY') {
140
150
  return NodeFilter.FILTER_SKIP;
141
151
  }
142
- const cssPosition = window.getComputedStyle(node).position;
143
- if ((cssPosition === 'fixed' || cssPosition === 'sticky') && isElementVisible(node)) {
144
- return NodeFilter.FILTER_ACCEPT;
152
+ if (isElementVisible(node)) {
153
+ const cssPosition = window.getComputedStyle(node).position;
154
+ if (cssPosition === 'fixed' || cssPosition === 'sticky') {
155
+ return NodeFilter.FILTER_ACCEPT;
156
+ }
157
+ if (isDialogLikeElement(node)) {
158
+ return NodeFilter.FILTER_ACCEPT;
159
+ }
145
160
  }
146
161
  return NodeFilter.FILTER_SKIP;
147
162
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duckduckgo/autoconsent",
3
- "version": "14.67.0",
3
+ "version": "14.69.0",
4
4
  "description": "",
5
5
  "types": "./dist/types/web.d.ts",
6
6
  "exports": {
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "elsevier-pure",
3
+ "prehideSelectors": ["div[role='dialog'][aria-modal='true']:has([data-qa='accept-all-button'])"],
4
+ "detectCmp": [
5
+ {
6
+ "exists": "[data-qa='accept-all-button']"
7
+ }
8
+ ],
9
+ "detectPopup": [
10
+ {
11
+ "visible": "[data-qa='accept-all-button']"
12
+ }
13
+ ],
14
+ "optIn": [
15
+ {
16
+ "waitForThenClick": "[data-qa='accept-all-button']"
17
+ }
18
+ ],
19
+ "optOut": [
20
+ {
21
+ "waitForThenClick": "[data-qa='reject-non-essential-button']"
22
+ }
23
+ ],
24
+ "test": [
25
+ {
26
+ "waitForVisible": "[data-qa='accept-all-button']",
27
+ "timeout": 1000,
28
+ "check": "none"
29
+ }
30
+ ]
31
+ }
@@ -3,19 +3,33 @@
3
3
  "runContext": {
4
4
  "urlPattern": "^https://([a-z0-9-]+\\.)?facebook\\.com/"
5
5
  },
6
- "prehideSelectors": ["div[data-testid=\"cookie-policy-manage-dialog\"]"],
7
- "detectCmp": [{ "exists": "div[data-testid=\"cookie-policy-manage-dialog\"]" }],
8
- "detectPopup": [{ "visible": "div[data-testid=\"cookie-policy-manage-dialog\"]" }],
6
+ "prehideSelectors": [],
7
+ "detectCmp": [
8
+ {
9
+ "exists": "body > div:not([id]) > div:nth-child(1):not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:nth-child(1):not([id]) > div:nth-child(2):not([id])"
10
+ }
11
+ ],
12
+ "detectPopup": [
13
+ {
14
+ "visible": "body > div:not([id]) > div:nth-child(1):not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:nth-child(1):not([id]) > div:nth-child(2):not([id])"
15
+ }
16
+ ],
9
17
  "optIn": [
10
18
  {
11
- "waitForThenClick": "body:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:nth-child(3):not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:nth-child(1):not([id])"
19
+ "waitForThenClick": "body > div:not([id]) > div:nth-child(1):not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:nth-child(1):not([id])"
12
20
  },
13
- { "waitForVisible": "div[data-testid=\"cookie-policy-manage-dialog\"]", "check": "none" }
21
+ {
22
+ "waitForVisible": "body > div:not([id]) > div:nth-child(1):not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:nth-child(1):not([id])",
23
+ "check": "none"
24
+ }
14
25
  ],
15
26
  "optOut": [
16
27
  {
17
- "waitForThenClick": "body:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:nth-child(3):not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:nth-child(1):not([id]) > div:nth-child(2):not([id])"
28
+ "waitForThenClick": "body > div:not([id]) > div:nth-child(1):not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:nth-child(1):not([id]) > div:nth-child(2):not([id])"
18
29
  },
19
- { "waitForVisible": "div[data-testid=\"cookie-policy-manage-dialog\"]", "check": "none" }
30
+ {
31
+ "waitForVisible": "body > div:not([id]) > div:nth-child(1):not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:not([id]) > div:nth-child(2):not([id]) > div:not([id]) > div:nth-child(1):not([id]) > div:nth-child(2):not([id])",
32
+ "check": "none"
33
+ }
20
34
  ]
21
35
  }
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "fullertonhotels.com",
3
+ "cosmetic": false,
4
+ "_metadata": {
5
+ "vendorUrl": "http://www.fullertonhotels.com/"
6
+ },
7
+ "runContext": {
8
+ "main": true,
9
+ "frame": false
10
+ },
11
+ "prehideSelectors": [],
12
+ "detectCmp": [
13
+ {
14
+ "exists": ["#ifrmCookieBanner", "#sp-decline"]
15
+ }
16
+ ],
17
+ "detectPopup": [
18
+ {
19
+ "visible": ["#ifrmCookieBanner", "#sp-decline"]
20
+ }
21
+ ],
22
+ "optIn": [
23
+ {
24
+ "waitForThenClick": ["#ifrmCookieBanner", "#sp-accept"]
25
+ }
26
+ ],
27
+ "optOut": [
28
+ {
29
+ "wait": 500
30
+ },
31
+ {
32
+ "waitForThenClick": ["#ifrmCookieBanner", "#sp-decline"],
33
+ "comment": "Decline"
34
+ }
35
+ ],
36
+ "test": []
37
+ }
@@ -2,25 +2,28 @@
2
2
  "name": "geni.com",
3
3
  "vendorUrl": "https://www.geni.com/",
4
4
  "cosmetic": true,
5
- "prehideSelectors": [".cookie-notify-anchor"],
5
+ "runContext": {
6
+ "urlPattern": "^https://(www\\.)?geni\\.com/"
7
+ },
8
+ "prehideSelectors": [".modal-cookie"],
6
9
  "detectCmp": [
7
10
  {
8
- "exists": ".cookie-notify-anchor .cookie-notify"
11
+ "exists": ".modal-cookie"
9
12
  }
10
13
  ],
11
14
  "detectPopup": [
12
15
  {
13
- "visible": ".cookie-notify-anchor .cookie-notify"
16
+ "visible": ".modal-cookie"
14
17
  }
15
18
  ],
16
19
  "optIn": [
17
20
  {
18
- "waitForThenClick": ".cookie-notify-anchor .cookie-notify button"
21
+ "waitForThenClick": ".modal-cookie #accept-all"
19
22
  }
20
23
  ],
21
24
  "optOut": [
22
25
  {
23
- "hide": ".cookie-notify-anchor"
26
+ "hide": ".modal-cookie"
24
27
  }
25
28
  ]
26
29
  }
@@ -1,50 +1,29 @@
1
1
  {
2
2
  "name": "remarkable.com",
3
3
  "vendorUrl": "https://www.remarkable.com/",
4
- "cosmetic": false,
4
+ "cosmetic": true,
5
5
  "runContext": {
6
6
  "urlPattern": "^https://(www\\.)?remarkable\\.com/"
7
7
  },
8
- "prehideSelectors": [".overlay-cookies"],
8
+ "prehideSelectors": ["div:has(> button#cookieBar-button)"],
9
9
  "detectCmp": [
10
10
  {
11
- "exists": ".overlay-cookies,#cookieBar-button"
11
+ "exists": "#cookieBar-button"
12
12
  }
13
13
  ],
14
14
  "detectPopup": [
15
15
  {
16
- "visible": ".overlay-cookies,#cookieBar-button"
16
+ "visible": "#cookieBar-button"
17
17
  }
18
18
  ],
19
19
  "optIn": [
20
20
  {
21
- "waitForThenClick": ".overlay-cookies .ark-button--primary-neutral,#cookieBar-button"
21
+ "waitForThenClick": "#cookieBar-button"
22
22
  }
23
23
  ],
24
24
  "optOut": [
25
25
  {
26
- "if": { "exists": "#cookieBar-button" },
27
- "then": [
28
- {
29
- "hide": "div:has(> button#cookieBar-button)"
30
- }
31
- ],
32
- "else": [
33
- {
34
- "waitForThenClick": ".overlay-cookies .ark-button--tertiary-neutral"
35
- },
36
- {
37
- "waitFor": ".overlay-cookies #cookie-group-control-necessary"
38
- },
39
- {
40
- "waitForThenClick": ".overlay-cookies .ark-button--secondary"
41
- }
42
- ]
43
- }
44
- ],
45
- "test": [
46
- {
47
- "eval": "EVAL_REMARKABLE_TEST"
26
+ "hide": "div:has(> button#cookieBar-button)"
48
27
  }
49
28
  ]
50
29
  }
@@ -5,25 +5,25 @@
5
5
  "runContext": {
6
6
  "urlPattern": "^https://(www\\.)?unicourt\\.com/"
7
7
  },
8
- "prehideSelectors": [".cookie-notification"],
8
+ "prehideSelectors": [".cookie-banner"],
9
9
  "detectCmp": [
10
10
  {
11
- "exists": ".cookie-notification"
11
+ "exists": ".cookie-banner"
12
12
  }
13
13
  ],
14
14
  "detectPopup": [
15
15
  {
16
- "visible": ".cookie-notification"
16
+ "visible": ".cookie-banner"
17
17
  }
18
18
  ],
19
19
  "optIn": [
20
20
  {
21
- "waitForThenClick": ".cookie-notification button"
21
+ "waitForThenClick": ".cookie-banner .button"
22
22
  }
23
23
  ],
24
24
  "optOut": [
25
25
  {
26
- "hide": ".cookie-notification"
26
+ "hide": ".cookie-banner"
27
27
  }
28
28
  ]
29
29
  }