@duckduckgo/autoconsent 16.63.0 → 17.1.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 (180) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/dist/addon-firefox/background.bundle.js +1 -0
  3. package/dist/addon-firefox/compact-rules.json +1 -1
  4. package/dist/addon-firefox/content.bundle.js +177 -38
  5. package/dist/addon-firefox/manifest.json +1 -1
  6. package/dist/addon-firefox/popup.bundle.js +1 -0
  7. package/dist/addon-firefox/rule-index.json +1 -1
  8. package/dist/addon-firefox/rules.json +1 -1
  9. package/dist/addon-mv3/background.bundle.js +1 -0
  10. package/dist/addon-mv3/compact-rules.json +1 -1
  11. package/dist/addon-mv3/content.bundle.js +177 -38
  12. package/dist/addon-mv3/manifest.json +1 -1
  13. package/dist/addon-mv3/popup.bundle.js +1 -0
  14. package/dist/addon-mv3/rule-index.json +1 -1
  15. package/dist/addon-mv3/rules.json +1 -1
  16. package/dist/autoconsent.cjs.js +177 -38
  17. package/dist/autoconsent.esm.js +177 -38
  18. package/dist/autoconsent.playwright.js +180 -38
  19. package/dist/autoconsent.standalone.js +178 -39
  20. package/dist/heuristics.cjs.js +15 -3
  21. package/dist/heuristics.mjs +15 -3
  22. package/dist/types/heuristics.d.ts +12 -3
  23. package/dist/types/types.d.ts +12 -1
  24. package/lib/cmps/base.ts +5 -1
  25. package/lib/heuristic-patterns.ts +18 -2
  26. package/lib/heuristics.ts +240 -45
  27. package/lib/types.ts +16 -1
  28. package/lib/utils.ts +1 -0
  29. package/package.json +1 -1
  30. package/readme.md +1 -1
  31. package/rules/autoconsent/1pmobile.json +44 -0
  32. package/rules/autoconsent/amasty-gdpr-hyva.json +131 -0
  33. package/rules/autoconsent/amazonforum.json +86 -0
  34. package/rules/autoconsent/asrock.json +44 -0
  35. package/rules/autoconsent/babylist.json +23 -0
  36. package/rules/autoconsent/babylonbee.json +36 -0
  37. package/rules/autoconsent/bathnes-cookie-banner.json +31 -0
  38. package/rules/autoconsent/chairish.json +17 -0
  39. package/rules/autoconsent/chemicalbook.json +13 -0
  40. package/rules/autoconsent/chimnie-co-uk.json +21 -0
  41. package/rules/autoconsent/complianz-optin.json +5 -3
  42. package/rules/autoconsent/consentmo.json +5 -2
  43. package/rules/autoconsent/cookie-pref-form.json +48 -0
  44. package/rules/autoconsent/cookie-script.json +34 -15
  45. package/rules/autoconsent/endeavorhealth.json +31 -0
  46. package/rules/autoconsent/ensighten.json +3 -3
  47. package/rules/autoconsent/epaas.json +18 -1
  48. package/rules/autoconsent/fepblue-org.json +13 -0
  49. package/rules/autoconsent/fnsp-cookieconsent.json +30 -0
  50. package/rules/autoconsent/fod-fujitv.json +13 -0
  51. package/rules/autoconsent/fpl-com.json +34 -0
  52. package/rules/autoconsent/fuqer.json +11 -0
  53. package/rules/autoconsent/greatschools.json +13 -0
  54. package/rules/autoconsent/hamradio-com.json +13 -0
  55. package/rules/autoconsent/hkdir.json +33 -0
  56. package/rules/autoconsent/hubspot.json +7 -1
  57. package/rules/autoconsent/ibkr.json +16 -0
  58. package/rules/autoconsent/jetpens.json +44 -0
  59. package/rules/autoconsent/jokul-cookie-consent.json +28 -0
  60. package/rules/autoconsent/kanopy.json +88 -0
  61. package/rules/autoconsent/lego.json +27 -5
  62. package/rules/autoconsent/mainlinemenswear.json +39 -0
  63. package/rules/autoconsent/mars-cookies-prompt.json +20 -0
  64. package/rules/autoconsent/money-co-uk.json +21 -0
  65. package/rules/autoconsent/myjobquote.json +19 -0
  66. package/rules/autoconsent/niedziela-pl.json +35 -0
  67. package/rules/autoconsent/nikkeibp.json +10 -0
  68. package/rules/autoconsent/northwell-edu.json +29 -0
  69. package/rules/autoconsent/nts-org-uk.json +20 -0
  70. package/rules/autoconsent/nyu-langone.json +30 -0
  71. package/rules/autoconsent/ofsted-reports-cosmetic.json +13 -0
  72. package/rules/autoconsent/onecom-cookie-banner.json +25 -0
  73. package/rules/autoconsent/optum-legal-banner.json +10 -0
  74. package/rules/autoconsent/optum.json +35 -0
  75. package/rules/autoconsent/oslosportslager.no.json +34 -0
  76. package/rules/autoconsent/planned-parenthood.json +17 -0
  77. package/rules/autoconsent/plex.tv.json +52 -0
  78. package/rules/autoconsent/pornhub-compact-cookie-banner.json +1 -1
  79. package/rules/autoconsent/pornhub.json +1 -1
  80. package/rules/autoconsent/radio-canada.json +28 -0
  81. package/rules/autoconsent/regjeringen.no.json +13 -0
  82. package/rules/autoconsent/reviewsolicitors.json +41 -0
  83. package/rules/autoconsent/ring.json +1 -1
  84. package/rules/autoconsent/roll20.json +50 -0
  85. package/rules/autoconsent/sankei-digital.json +37 -0
  86. package/rules/autoconsent/sirdata.json +8 -3
  87. package/rules/autoconsent/site-notifications-cookie-usage.json +25 -0
  88. package/rules/autoconsent/sparebank1-optin.json +10 -0
  89. package/rules/autoconsent/texas-gov.json +22 -0
  90. package/rules/autoconsent/tiresize.com.json +17 -0
  91. package/rules/autoconsent/tri-table.json +6 -2
  92. package/rules/autoconsent/trillian.json +10 -0
  93. package/rules/autoconsent/tyreseeker.json +44 -0
  94. package/rules/autoconsent/uhcprovider.json +13 -0
  95. package/rules/autoconsent/united-online.json +66 -0
  96. package/rules/autoconsent/unitedutilities.json +52 -0
  97. package/rules/autoconsent/uttings.co.uk.json +22 -0
  98. package/rules/autoconsent/vinnova-se.json +50 -0
  99. package/rules/autoconsent/vortex-consent.json +33 -0
  100. package/rules/autoconsent/watchfinder.json +16 -0
  101. package/rules/compact-rules.json +1 -1
  102. package/rules/rule-index.json +1 -1
  103. package/rules/rules.json +1 -1
  104. package/tests/1pmobile.spec.ts +5 -0
  105. package/tests/amasty-gdpr-hyva.spec.ts +17 -0
  106. package/tests/amazonforum.spec.ts +9 -0
  107. package/tests/asrock.spec.ts +3 -0
  108. package/tests/babylist.spec.ts +3 -0
  109. package/tests/babylonbee.spec.ts +6 -0
  110. package/tests/bathnes-cookie-banner.spec.ts +3 -0
  111. package/tests/chairish.spec.ts +3 -0
  112. package/tests/chemicalbook.spec.ts +5 -0
  113. package/tests/chimnie-co-uk.spec.ts +3 -0
  114. package/tests/complianz-optin.spec.ts +2 -0
  115. package/tests/consentmo.spec.ts +2 -0
  116. package/tests/cookie-pref-form.spec.ts +8 -0
  117. package/tests/cookie-script.spec.ts +5 -0
  118. package/tests/endeavorhealth.spec.ts +3 -0
  119. package/tests/ensighten.spec.ts +7 -1
  120. package/tests/epaas.spec.ts +3 -0
  121. package/tests/fepblue-org.spec.ts +3 -0
  122. package/tests/fnsp-cookieconsent.spec.ts +9 -0
  123. package/tests/fod-fujitv.spec.ts +3 -0
  124. package/tests/fpl-com.spec.ts +3 -0
  125. package/tests/fuqer.spec.ts +3 -0
  126. package/tests/greatschools.spec.ts +3 -0
  127. package/tests/hamradio-com.spec.ts +3 -0
  128. package/tests/hkdir.spec.ts +10 -0
  129. package/tests/hubspot.spec.ts +6 -1
  130. package/tests/ibkr.spec.ts +18 -0
  131. package/tests/jetpens.spec.ts +5 -0
  132. package/tests/jokul-cookie-consent.spec.ts +3 -0
  133. package/tests/kanopy.spec.ts +5 -0
  134. package/tests/mainlinemenswear.spec.ts +3 -0
  135. package/tests/mars-cookies-prompt.spec.ts +3 -0
  136. package/tests/money-co-uk.spec.ts +3 -0
  137. package/tests/myjobquote.spec.ts +5 -0
  138. package/tests/niedziela-pl.spec.ts +3 -0
  139. package/tests/nikkeibp.spec.ts +8 -0
  140. package/tests/northwell-edu.spec.ts +3 -0
  141. package/tests/nts-org-uk.spec.ts +3 -0
  142. package/tests/nyu-langone.spec.ts +3 -0
  143. package/tests/ofsted-reports-cosmetic.spec.ts +3 -0
  144. package/tests/onecom-cookie-banner.spec.ts +5 -0
  145. package/tests/optum-legal-banner.spec.ts +9 -0
  146. package/tests/optum.spec.ts +6 -0
  147. package/tests/oslosportslager.no.spec.ts +3 -0
  148. package/tests/planned-parenthood.spec.ts +3 -0
  149. package/tests/plex.tv.spec.ts +5 -0
  150. package/tests/pornhub.spec.ts +1 -1
  151. package/tests/radio-canada.spec.ts +15 -0
  152. package/tests/regjeringen.no.spec.ts +3 -0
  153. package/tests/reviewsolicitors.spec.ts +3 -0
  154. package/tests/roll20.spec.ts +3 -0
  155. package/tests/sankei-digital.spec.ts +9 -0
  156. package/tests/sirdata.spec.ts +13 -1
  157. package/tests/site-notifications-cookie-usage.spec.ts +3 -0
  158. package/tests/sparebank1-optin.spec.ts +8 -0
  159. package/tests/texas-gov.spec.ts +3 -0
  160. package/tests/tiresize.com.spec.ts +3 -0
  161. package/tests/tri-table.spec.ts +1 -1
  162. package/tests/trillian.spec.ts +15 -0
  163. package/tests/tyreseeker.spec.ts +3 -0
  164. package/tests/uhcprovider.spec.ts +3 -0
  165. package/tests/united-online.spec.ts +4 -0
  166. package/tests/unitedutilities.spec.ts +11 -0
  167. package/tests/uttings.co.uk.spec.ts +3 -0
  168. package/tests/vinnova-se.spec.ts +6 -0
  169. package/tests/vortex-consent.spec.ts +18 -0
  170. package/tests/watchfinder.spec.ts +3 -0
  171. package/tests-wtr/heuristics/get-actionable-popups.ts +41 -7
  172. package/tests-wtr/heuristics/get-potential-popups.test.ts +235 -4
  173. package/tests-wtr/heuristics/heuristic-cmp.ts +16 -1
  174. package/tests-wtr/heuristics/heuristics-utils.test.ts +34 -4
  175. package/rules/generated/auto_CA_plex.tv_ee4.json +0 -40
  176. package/rules/generated/auto_CA_support.plex.tv_gn3.json +0 -40
  177. package/rules/generated/auto_US_interactivebrokers.com_0.json +0 -28
  178. package/tests/generated/auto_CA_plex.tv_ee4.spec.ts +0 -2
  179. package/tests/generated/auto_CA_support.plex.tv_gn3.spec.ts +0 -6
  180. package/tests/generated/auto_US_interactivebrokers.com_0.spec.ts +0 -3
@@ -159,6 +159,7 @@
159
159
  },
160
160
  performanceLoggingEnabled: false,
161
161
  heuristicPopupSearchTimeout: 100,
162
+ enableHeuristicHitTest: false,
162
163
  heuristicMode: "off"
163
164
  // heuristic disabled by default
164
165
  };
@@ -470,8 +471,8 @@
470
471
  // "only necessary" is probably too broad
471
472
  /(?:by continuing.{0,100}cookie)|(?:cookie.{0,100}by continuing)/gi,
472
473
  /(?:by continuing.{0,100}privacy)|(?:privacy.{0,100}by continuing)/gi,
473
- // a single qualifier is common: "optional", "essential", "necessary", "analytics", ...
474
- /we (?:use|serve)(?: \w+)? cookies/gi,
474
+ // up to two qualifiers are common: "optional", "essential", "analytics", "google analytics", "third party", ...
475
+ /we (?:use|serve)(?: \w+){0,2} cookies/gi,
475
476
  /we are using cookies/gi,
476
477
  /use of cookies/gi,
477
478
  /website uses cookies to enhance your browsing experience/gi,
@@ -628,7 +629,10 @@
628
629
  /(?:cookie|クッキー).{0,10}を(?:使用|利用)/gi,
629
630
  // Danish (DA)
630
631
  // e.g. "vi bruger cookies", "hjemmesiden bruger cookies til at ..."
631
- /bruger cookies/gi
632
+ /bruger cookies/gi,
633
+ // Norwegian (NO)
634
+ // e.g. "vi bruker informasjonskapsler", "kan vi bruke valgfrie informasjonskapsler", "nettstedet bruker cookies"
635
+ /\b(?:vi|nettstedet|nettsiden|siden)\s+bruker?\s.{0,60}?(?:informasjonskapsler|cookies)/gi
632
636
  ];
633
637
  var REJECT_PATTERNS_ENGLISH = [
634
638
  // e.g. "reject", "reject all", "reject all cookies", "deny all", "refuse cookies", "decline",
@@ -770,6 +774,14 @@
770
774
  // "nej tak" (no thanks)
771
775
  /^nej(,?\s+tak)?$/is
772
776
  ];
777
+ var REJECT_PATTERNS_NORWEGIAN = [
778
+ // avvis / avslå (reject verbs)
779
+ /(^|\s)(avvis|avslå)/is,
780
+ // (accept/allow/use) only necessary cookies
781
+ /^(godta\s+|tillat\s+|aksepter\s+|bruk\s+)?(kun|bare)\s+(strengt\s+)?nødvendige( (informasjonskapsler|cookies))?$/is,
782
+ // "nei" / "nei takk" (no thanks)
783
+ /^nei(,?\s+takk)?$/is
784
+ ];
773
785
  var REJECT_PATTERNS_CATALAN = [
774
786
  /(^|\s)rebutj/is,
775
787
  "no accepto",
@@ -825,6 +837,7 @@
825
837
  ...REJECT_PATTERNS_SPANISH,
826
838
  ...REJECT_PATTERNS_SWEDISH,
827
839
  ...REJECT_PATTERNS_DANISH,
840
+ ...REJECT_PATTERNS_NORWEGIAN,
828
841
  ...REJECT_PATTERNS_CATALAN,
829
842
  ...REJECT_PATTERNS_GALICIAN,
830
843
  ...REJECT_PATTERNS_BASQUE,
@@ -1334,28 +1347,73 @@
1334
1347
  // lib/heuristics.ts
1335
1348
  var BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
1336
1349
  var POPUP_SEARCH_MAX_TIME = 100;
1337
- function getActionablePopups(mode = "reject", timeout = POPUP_SEARCH_MAX_TIME) {
1338
- const acceptedLevels = mode === "tier2" ? ["reject", "tier1", "tier2"] : mode === "tier1" ? ["reject", "tier1"] : ["reject"];
1339
- const popups = getPotentialPopups(timeout).filter((popup) => {
1340
- const popupText = popup.text?.trim();
1341
- if (!popupText || isExcludedPopup(popupText) || !hasHeuristicPatterns(popupText)) {
1342
- return false;
1343
- }
1344
- popup.regexClassification = classifyPopup(popup.buttons);
1345
- return popup.regexClassification !== void 0 && acceptedLevels.includes(popup.regexClassification);
1346
- });
1347
- return popups.sort((a, b) => (a.regexClassification ?? "") > (b.regexClassification ?? "") ? 1 : -1);
1350
+ var ACCEPTED_LEVELS = {
1351
+ off: [],
1352
+ reject: ["reject"],
1353
+ tier1: ["reject", "tier1"],
1354
+ tier2: ["reject", "tier1", "tier2"]
1355
+ };
1356
+ function getActionablePopups(mode = "reject", timeout = POPUP_SEARCH_MAX_TIME, hitTest = false) {
1357
+ const acceptedLevels = ACCEPTED_LEVELS[mode];
1358
+ const isActionable = (popup) => acceptedLevels.includes(popup.regexClassification) && isConsentPopup(popup);
1359
+ const popups = searchPopups(timeout, hitTest, DEFAULT_HIT_TEST_GRID, true).filter(isActionable);
1360
+ return popups.sort((a, b) => classificationRank(a) - classificationRank(b));
1361
+ }
1362
+ function getPotentialPopups(timeout = POPUP_SEARCH_MAX_TIME, hitTest = false, hitTestGrid = DEFAULT_HIT_TEST_GRID) {
1363
+ return searchPopups(timeout, hitTest, hitTestGrid);
1348
1364
  }
1349
- function getPotentialPopups(timeout = POPUP_SEARCH_MAX_TIME) {
1365
+ function searchPopups(timeout, hitTest, hitTestGrid, stopOnReject = false) {
1350
1366
  if (!isTopFrame()) {
1351
- if (window.parent && window.parent !== window.top) {
1352
- return [];
1353
- }
1354
- const frameElement = getFramePopupElement(document);
1355
- const popup = frameElement && toPopupData(frameElement);
1356
- return popup ? [popup] : [];
1367
+ return getFramePopups();
1357
1368
  }
1358
1369
  const deadline = performance.now() + timeout;
1370
+ const hitTestPopups = hitTest ? findPopupsByHitTest(deadline, hitTestGrid) : [];
1371
+ if (stopOnReject && hitTestPopups.some((p) => p.regexClassification === "reject" && isConsentPopup(p))) {
1372
+ return hitTestPopups;
1373
+ }
1374
+ const walkPopups = collectPositionedPopups(deadline);
1375
+ return [...walkPopups, ...mergeWalkOverlaps(hitTestPopups, walkPopups)];
1376
+ }
1377
+ var consentPopups = /* @__PURE__ */ new WeakMap();
1378
+ function classificationRank(popup) {
1379
+ return ACCEPTED_LEVELS.tier2.indexOf(popup.regexClassification);
1380
+ }
1381
+ function isConsentPopup(popup) {
1382
+ if (!ACCEPTED_LEVELS.tier2.includes(popup.regexClassification)) {
1383
+ return false;
1384
+ }
1385
+ let result = consentPopups.get(popup);
1386
+ if (result === void 0) {
1387
+ const popupText = popup.text.trim();
1388
+ result = !!popupText && !isExcludedPopup(popupText) && hasHeuristicPatterns(popupText);
1389
+ consentPopups.set(popup, result);
1390
+ }
1391
+ return result;
1392
+ }
1393
+ function mergeWalkOverlaps(hitTestPopups, walkPopups) {
1394
+ return hitTestPopups.filter((h) => {
1395
+ const hitIsConsent = isConsentPopup(h);
1396
+ const absorbs = (p) => !hitIsConsent || isConsentPopup(p) && classificationRank(p) <= classificationRank(h);
1397
+ const walkPopup = walkPopups.find((p) => composedOverlaps(p.element, h.element) && absorbs(p));
1398
+ if (walkPopup) {
1399
+ (walkPopup.hitPoints ??= []).push(...h.hitPoints ?? []);
1400
+ }
1401
+ return !walkPopup;
1402
+ });
1403
+ }
1404
+ function getFramePopups() {
1405
+ if (window.parent && window.parent !== window.top) {
1406
+ return [];
1407
+ }
1408
+ const frameElement = getFramePopupElement(document);
1409
+ const popup = frameElement && toPopupData(frameElement);
1410
+ if (!popup) {
1411
+ return [];
1412
+ }
1413
+ popup.source = "frame";
1414
+ return [popup];
1415
+ }
1416
+ function collectPositionedPopups(deadline) {
1359
1417
  const { elements, shadowHosts } = getPopupLikeElements(deadline);
1360
1418
  const potentialPopups = [];
1361
1419
  const expanded = [];
@@ -1371,17 +1429,86 @@
1371
1429
  if (root !== popup) {
1372
1430
  expanded.push(root.element);
1373
1431
  }
1432
+ root.source = "walk";
1374
1433
  potentialPopups.push(root);
1375
1434
  }
1376
1435
  return potentialPopups.filter((p) => !expanded.some((root) => root !== p.element && composedContains(root, p.element)));
1377
1436
  }
1437
+ var DEFAULT_HIT_TEST_GRID = {
1438
+ columns: [0.1, 0.5, 0.9],
1439
+ // banners sit along the top and bottom edges and modals in the middle; rows 150px in added almost nothing in crawls
1440
+ edgeRows: [30, 90]
1441
+ };
1442
+ function findPopupsByHitTest(deadline, grid) {
1443
+ const climbed = /* @__PURE__ */ new Map();
1444
+ const found = [];
1445
+ const toPopup = (el) => el.querySelector(TEXT_ENTRY_SELECTOR) ? null : toPopupRoot(el);
1446
+ for (const [x, y] of getHitTestPoints(grid)) {
1447
+ if (performance.now() > deadline) {
1448
+ return found;
1449
+ }
1450
+ const path = [];
1451
+ let reached = null;
1452
+ const shouldStop = (el) => {
1453
+ if (climbed.has(el)) {
1454
+ reached = climbed.get(el) ?? null;
1455
+ return true;
1456
+ }
1457
+ const overlapped = found.find((p) => composedOverlaps(p.element, el));
1458
+ if (overlapped) {
1459
+ reached = composedContains(overlapped.element, el) ? overlapped : null;
1460
+ return true;
1461
+ }
1462
+ path.push(el);
1463
+ return false;
1464
+ };
1465
+ const hit = closestHTMLElement(deepElementFromPoint(x, y));
1466
+ const popup = climbToPopup(hit, MAX_HIT_TEST_CLIMB_DEPTH, toPopup, shouldStop);
1467
+ if (popup) {
1468
+ popup.source = "hitTest";
1469
+ found.push(popup);
1470
+ reached = popup;
1471
+ }
1472
+ for (const el of path) {
1473
+ climbed.set(el, reached);
1474
+ }
1475
+ if (reached) {
1476
+ (reached.hitPoints ??= []).push([x, y]);
1477
+ }
1478
+ }
1479
+ return found;
1480
+ }
1481
+ function getHitTestPoints(grid) {
1482
+ const { innerWidth: width, innerHeight: height } = window;
1483
+ const rows = [...grid.edgeRows, height / 2, ...grid.edgeRows.map((y) => height - y)].map((y) => Math.round(y));
1484
+ const ys = [...new Set(rows)].filter((y) => y >= 0 && y < height);
1485
+ return ys.flatMap((y) => grid.columns.map((column) => [Math.round(column * width), y]));
1486
+ }
1487
+ var TEXT_ENTRY_SELECTOR = 'textarea, input:not([type]), input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="tel"], input[type="number"], input[type="url"]';
1488
+ function deepElementFromPoint(x, y) {
1489
+ let el = document.elementFromPoint(x, y);
1490
+ while (el?.shadowRoot) {
1491
+ const inner = el.shadowRoot.elementFromPoint(x, y);
1492
+ if (!inner || inner === el) {
1493
+ break;
1494
+ }
1495
+ el = inner;
1496
+ }
1497
+ return el;
1498
+ }
1499
+ function closestHTMLElement(el) {
1500
+ while (el && !(el instanceof HTMLElement)) {
1501
+ el = el.parentElement;
1502
+ }
1503
+ return el;
1504
+ }
1378
1505
  function toPopupData(el, shadowHosts, text = getElementText(el)) {
1379
1506
  if (!text) {
1380
1507
  return null;
1381
1508
  }
1382
1509
  const buttons = getButtonData(el, shadowHosts);
1383
1510
  classifyButtons(buttons);
1384
- return { text, element: el, buttons };
1511
+ return { text, element: el, buttons, regexClassification: classifyPopup(buttons) };
1385
1512
  }
1386
1513
  function hasPopupButtons(text, buttons) {
1387
1514
  return !isOnlyButtonText(text, buttons) && buttons.some((b) => b.regexClassification !== "other");
@@ -1393,26 +1520,34 @@
1393
1520
  }
1394
1521
  return rest.replace(/\s+/g, "").length < 10;
1395
1522
  }
1523
+ function toPopupRoot(el, shadowHosts) {
1524
+ const text = getElementText(el);
1525
+ if (!text || !hasHeuristicPatterns(text)) {
1526
+ return null;
1527
+ }
1528
+ const popup = toPopupData(el, shadowHosts, text);
1529
+ return popup && hasPopupButtons(text, popup.buttons) ? popup : null;
1530
+ }
1396
1531
  var MAX_FRAGMENT_EXPANSION_DEPTH = 6;
1532
+ var MAX_HIT_TEST_CLIMB_DEPTH = 8;
1533
+ function climbToPopup(el, maxDepth, toPopup, shouldStop) {
1534
+ for (let depth = 0; el && depth < maxDepth; depth++, el = getComposedParent(el)) {
1535
+ if (shouldStop?.(el) || el === document.body || el === document.documentElement || el.getBoundingClientRect().height > window.innerHeight) {
1536
+ return null;
1537
+ }
1538
+ const popup = toPopup(el);
1539
+ if (popup) {
1540
+ return popup;
1541
+ }
1542
+ }
1543
+ return null;
1544
+ }
1397
1545
  function expandPopupFragment(popup, shadowHosts) {
1398
1546
  if (hasHeuristicPatterns(popup.text) && hasPopupButtons(popup.text, popup.buttons)) {
1399
1547
  return popup;
1400
1548
  }
1401
- let el = getComposedParent(popup.element);
1402
- for (let depth = 0; el && depth < MAX_FRAGMENT_EXPANSION_DEPTH; depth++, el = getComposedParent(el)) {
1403
- if (el === document.body || el === document.documentElement || el.getBoundingClientRect().height > window.innerHeight) {
1404
- break;
1405
- }
1406
- const text = getElementText(el);
1407
- if (!text || !hasHeuristicPatterns(text)) {
1408
- continue;
1409
- }
1410
- const root = toPopupData(el, shadowHosts, text);
1411
- if (root && hasPopupButtons(text, root.buttons)) {
1412
- return root;
1413
- }
1414
- }
1415
- return popup;
1549
+ const parent = getComposedParent(popup.element);
1550
+ return climbToPopup(parent, MAX_FRAGMENT_EXPANSION_DEPTH, (el) => toPopupRoot(el, shadowHosts)) ?? popup;
1416
1551
  }
1417
1552
  function getComposedParent(el) {
1418
1553
  if (el.parentElement) {
@@ -1443,6 +1578,9 @@
1443
1578
  function isRenderedTextElement(el) {
1444
1579
  return el instanceof HTMLElement && !["STYLE", "SCRIPT", "TEMPLATE", "NOSCRIPT", "LINK", "SLOT"].includes(el.tagName) && window.getComputedStyle(el).display !== "none";
1445
1580
  }
1581
+ function composedOverlaps(a, b) {
1582
+ return composedContains(a, b) || composedContains(b, a);
1583
+ }
1446
1584
  function composedContains(container, node) {
1447
1585
  if (container.contains(node)) {
1448
1586
  return true;
@@ -1934,7 +2072,11 @@
1934
2072
  async detectCmp() {
1935
2073
  await new Promise((resolve) => setTimeout(resolve, 0));
1936
2074
  this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorStart");
1937
- this.popups = getActionablePopups(this.mode, this.autoconsent.config.heuristicPopupSearchTimeout);
2075
+ this.popups = getActionablePopups(
2076
+ this.mode,
2077
+ this.autoconsent.config.heuristicPopupSearchTimeout,
2078
+ this.autoconsent.config.enableHeuristicHitTest
2079
+ );
1938
2080
  this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorEnd");
1939
2081
  this.autoconsent.config.performanceLoggingEnabled && performance.measure("heuristicDetector", "heuristicDetectorStart", "heuristicDetectorEnd");
1940
2082
  if (this.popups.length > 0) {