@duckduckgo/autoconsent 16.59.0 → 16.61.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 (216) hide show
  1. package/.agents/lib/regional-testing/harness.mjs +4 -172
  2. package/.agents/skills/oxylabs-testing/SKILL.md +4 -4
  3. package/.agents/skills/oxylabs-testing/scripts/oxylabs.mjs +17 -164
  4. package/.agents/skills/oxylabs-testing/scripts/oxylabs.test.mjs +6 -3
  5. package/.agents/skills/verify-pr/SKILL.md +1 -1
  6. package/AGENTS.md +1 -1
  7. package/CHANGELOG.md +107 -0
  8. package/Jenkinsfile +36 -1
  9. package/dist/addon-firefox/background.bundle.js +21 -0
  10. package/dist/addon-firefox/compact-rules.json +1 -1
  11. package/dist/addon-firefox/content.bundle.js +253 -93
  12. package/dist/addon-firefox/manifest.json +1 -1
  13. package/dist/addon-firefox/rule-index.json +1 -1
  14. package/dist/addon-firefox/rules.json +1 -1
  15. package/dist/addon-mv3/background.bundle.js +21 -0
  16. package/dist/addon-mv3/compact-rules.json +1 -1
  17. package/dist/addon-mv3/content.bundle.js +253 -93
  18. package/dist/addon-mv3/manifest.json +1 -1
  19. package/dist/addon-mv3/rule-index.json +1 -1
  20. package/dist/addon-mv3/rules.json +1 -1
  21. package/dist/autoconsent.cjs.js +253 -93
  22. package/dist/autoconsent.esm.js +253 -93
  23. package/dist/autoconsent.playwright.js +253 -93
  24. package/dist/autoconsent.standalone.js +254 -94
  25. package/dist/heuristics.cjs.js +29 -3
  26. package/dist/heuristics.mjs +28 -3
  27. package/dist/types/cmps/onetrust.d.ts +1 -0
  28. package/dist/types/eval-snippets.d.ts +3 -0
  29. package/dist/types/heuristic-classify.d.ts +1 -0
  30. package/dist/types/heuristics.d.ts +10 -1
  31. package/lib/cmps/onetrust.ts +15 -0
  32. package/lib/eval-snippets.ts +21 -0
  33. package/lib/heuristic-classify.ts +6 -0
  34. package/lib/heuristic-patterns.ts +26 -1
  35. package/lib/heuristics.ts +256 -104
  36. package/package.json +1 -1
  37. package/playwright/isolated-world.mjs +217 -0
  38. package/playwright/oxylabs.mjs +196 -0
  39. package/playwright/runner.ts +183 -39
  40. package/playwright.config.ts +4 -1
  41. package/rules/autoconsent/aeolservice.json +39 -0
  42. package/rules/autoconsent/airbnb.json +32 -0
  43. package/rules/autoconsent/amazon-music.json +13 -0
  44. package/rules/autoconsent/annunci69.json +19 -0
  45. package/rules/autoconsent/appconsent.json +6 -1
  46. package/rules/autoconsent/arbeidsplassen-nav-no.json +33 -0
  47. package/rules/autoconsent/att.json +36 -0
  48. package/rules/autoconsent/auctionet.json +39 -0
  49. package/rules/autoconsent/autodoc.json +18 -0
  50. package/rules/autoconsent/bbc.com-orb-cosmetic.json +34 -0
  51. package/rules/autoconsent/bbc.com.json +8 -5
  52. package/rules/autoconsent/bokadirekt.se.json +50 -0
  53. package/rules/autoconsent/bouvet-cookiepanel.json +29 -0
  54. package/rules/autoconsent/com-consent-layer.json +10 -0
  55. package/rules/autoconsent/consent-studio.json +25 -13
  56. package/rules/autoconsent/cookielegit.json +21 -0
  57. package/rules/autoconsent/cookieprompter.json +21 -0
  58. package/rules/autoconsent/cookiereports.json +10 -0
  59. package/rules/autoconsent/cookietractor.json +37 -0
  60. package/rules/autoconsent/de-volksbank.json +14 -0
  61. package/rules/autoconsent/dpgmedia-nl.json +20 -8
  62. package/rules/autoconsent/eon.pl.json +18 -0
  63. package/rules/autoconsent/eroticiracconti-it.json +13 -0
  64. package/rules/autoconsent/ersatzteile-24.json +49 -0
  65. package/rules/autoconsent/fastweb-cookie-center.json +13 -0
  66. package/rules/autoconsent/futurabolzano-it.json +35 -0
  67. package/rules/autoconsent/gobasic.json +10 -0
  68. package/rules/autoconsent/gravito.json +23 -16
  69. package/rules/autoconsent/hacon-next-gen.json +41 -0
  70. package/rules/autoconsent/ing-de.json +31 -0
  71. package/rules/autoconsent/interia.json +1 -4
  72. package/rules/autoconsent/intesasanpaolo.json +37 -0
  73. package/rules/autoconsent/jobindex.json +40 -0
  74. package/rules/autoconsent/kaspersky-cookies-notification.json +9 -0
  75. package/rules/autoconsent/lantmateriet.json +13 -0
  76. package/rules/autoconsent/livsmedelsverket.json +18 -0
  77. package/rules/autoconsent/mainichi.json +39 -0
  78. package/rules/autoconsent/mango.json +56 -0
  79. package/rules/autoconsent/nederlandseloterij.json +35 -0
  80. package/rules/autoconsent/newsbreak.json +16 -0
  81. package/rules/autoconsent/nhsapp.json +10 -0
  82. package/rules/autoconsent/nordea.json +21 -0
  83. package/rules/autoconsent/nordnet.json +33 -0
  84. package/rules/autoconsent/ok.json +4 -12
  85. package/rules/autoconsent/onlineradiobox.json +35 -0
  86. package/rules/autoconsent/pe-cookies.json +30 -0
  87. package/rules/autoconsent/pensionsmyndigheten.json +13 -0
  88. package/rules/autoconsent/pro-euclp-cookies-box.json +31 -0
  89. package/rules/autoconsent/pubfactory.json +45 -0
  90. package/rules/autoconsent/puesc-gov-pl.json +37 -0
  91. package/rules/autoconsent/rambler.json +30 -0
  92. package/rules/autoconsent/rejestr-io.json +36 -0
  93. package/rules/autoconsent/sas.json +6 -6
  94. package/rules/autoconsent/scb.se.json +13 -0
  95. package/rules/autoconsent/schumacher.com.json +53 -0
  96. package/rules/autoconsent/sitevision.json +18 -1
  97. package/rules/autoconsent/skatteverket-se.json +33 -0
  98. package/rules/autoconsent/smile-insurances.json +13 -0
  99. package/rules/autoconsent/studentapan.json +16 -0
  100. package/rules/autoconsent/svt.se.json +7 -7
  101. package/rules/autoconsent/temu.json +12 -16
  102. package/rules/autoconsent/thecode.json +21 -0
  103. package/rules/autoconsent/tv2reg-cookie-notice.json +39 -0
  104. package/rules/autoconsent/uk-parliament.json +19 -0
  105. package/rules/autoconsent/wirtualna-polska.json +13 -10
  106. package/rules/autoconsent/xenforo-cookie-consent.json +62 -0
  107. package/rules/autoconsent/zorginstituut-cookiewarning.json +33 -0
  108. package/rules/compact-rules.json +1 -1
  109. package/rules/rule-index.json +1 -1
  110. package/rules/rules.json +1 -1
  111. package/tests/aeolservice.spec.ts +5 -0
  112. package/tests/airbnb.spec.ts +3 -0
  113. package/tests/amazon-music.spec.ts +9 -0
  114. package/tests/annunci69.spec.ts +5 -0
  115. package/tests/appconsent.spec.ts +6 -0
  116. package/tests/arbeidsplassen-nav-no.spec.ts +3 -0
  117. package/tests/att.spec.ts +3 -0
  118. package/tests/auctionet.spec.ts +3 -0
  119. package/tests/autodoc.spec.ts +8 -0
  120. package/tests/bbc.com-orb-cosmetic.spec.ts +8 -0
  121. package/tests/bbc.com.spec.ts +11 -9
  122. package/tests/bokadirekt.se.spec.ts +3 -0
  123. package/tests/bouvet-cookiepanel.spec.ts +9 -0
  124. package/tests/com-consent-layer.spec.ts +6 -0
  125. package/tests/consent-studio.spec.ts +5 -0
  126. package/tests/cookiebot.spec.ts +1 -0
  127. package/tests/cookielegit.spec.ts +3 -0
  128. package/tests/cookieprompter.spec.ts +9 -0
  129. package/tests/cookiereports.spec.ts +13 -0
  130. package/tests/cookietractor.spec.ts +12 -0
  131. package/tests/de-volksbank.spec.ts +5 -0
  132. package/tests/dpgmedia-nl.spec.ts +15 -1
  133. package/tests/eon.pl.spec.ts +3 -0
  134. package/tests/eroticiracconti-it.spec.ts +3 -0
  135. package/tests/ersatzteile-24.spec.ts +6 -0
  136. package/tests/fastweb-cookie-center.spec.ts +3 -0
  137. package/tests/futurabolzano-it.spec.ts +3 -0
  138. package/tests/gobasic.spec.ts +3 -0
  139. package/tests/gravito.spec.ts +7 -1
  140. package/tests/hacon-next-gen.spec.ts +6 -0
  141. package/tests/ing-de.spec.ts +3 -0
  142. package/tests/interia.spec.ts +1 -1
  143. package/tests/intesasanpaolo.spec.ts +3 -0
  144. package/tests/jobindex.spec.ts +3 -0
  145. package/tests/kaspersky-cookies-notification.spec.ts +8 -0
  146. package/tests/lantmateriet.spec.ts +3 -0
  147. package/tests/livsmedelsverket.spec.ts +5 -0
  148. package/tests/mainichi.spec.ts +10 -0
  149. package/tests/mango.spec.ts +3 -0
  150. package/tests/nederlandseloterij.spec.ts +9 -0
  151. package/tests/newsbreak.spec.ts +3 -0
  152. package/tests/nhsapp.spec.ts +3 -0
  153. package/tests/nordea.spec.ts +6 -0
  154. package/tests/nordnet.spec.ts +8 -0
  155. package/tests/onlineradiobox.spec.ts +5 -0
  156. package/tests/pe-cookies.spec.ts +6 -0
  157. package/tests/pensionsmyndigheten.spec.ts +3 -0
  158. package/tests/pro-euclp-cookies-box.spec.ts +8 -0
  159. package/tests/pubfactory.spec.ts +10 -0
  160. package/tests/puesc-gov-pl.spec.ts +3 -0
  161. package/tests/rambler.spec.ts +3 -0
  162. package/tests/rejestr-io.spec.ts +3 -0
  163. package/tests/sas.spec.ts +1 -1
  164. package/tests/scb.se.spec.ts +4 -0
  165. package/tests/schumacher.com.spec.ts +6 -0
  166. package/tests/sitevision-cookie-consent.spec.ts +1 -0
  167. package/tests/sitevision.spec.ts +7 -0
  168. package/tests/skatteverket-se.spec.ts +3 -0
  169. package/tests/smile-insurances.spec.ts +6 -0
  170. package/tests/studentapan.spec.ts +3 -0
  171. package/tests/svt.se.spec.ts +1 -3
  172. package/tests/temu.spec.ts +3 -1
  173. package/tests/thecode.spec.ts +3 -0
  174. package/tests/tv2reg-cookie-notice.spec.ts +16 -0
  175. package/tests/uk-parliament.spec.ts +8 -0
  176. package/tests/wirtualna-polska.spec.ts +6 -1
  177. package/tests/xenforo-cookie-consent.spec.ts +6 -0
  178. package/tests/zorginstituut-cookiewarning.spec.ts +10 -0
  179. package/tests-wtr/heuristics/get-potential-popups.test.ts +330 -9
  180. package/tests-wtr/heuristics/heuristics-utils.test.ts +98 -0
  181. package/rules/autoconsent/twitter.json +0 -34
  182. package/rules/generated/auto_AU_flysas.com_r1s.json +0 -40
  183. package/rules/generated/auto_CH_airbnb.de_83z.json +0 -40
  184. package/rules/generated/auto_CH_autodoc.de_krd.json +0 -40
  185. package/rules/generated/auto_CH_svtplay.se_d39.json +0 -40
  186. package/rules/generated/auto_GB_parliamentlive.tv_r3v.json +0 -28
  187. package/rules/generated/auto_NL_asnbank.nl_e28.json +0 -40
  188. package/rules/generated/auto_NL_eurojackpot.nederlandseloterij.nl_7hr.json +0 -40
  189. package/rules/generated/auto_NL_lotto.nederlandseloterij.nl_7c8.json +0 -40
  190. package/rules/generated/auto_NL_nederlandseloterij.nl_b60.json +0 -40
  191. package/rules/generated/auto_NL_staatsloterij.nederlandseloterij.nl_197.json +0 -40
  192. package/rules/generated/auto_NO_arronet.se_vo5.json +0 -40
  193. package/rules/generated/auto_NO_liseberg.se_qy4.json +0 -40
  194. package/rules/generated/auto_NO_nordnet.se_2en.json +0 -40
  195. package/rules/generated/auto_NO_regeringen.se_uhv.json +0 -40
  196. package/rules/generated/auto_NO_svtplay.se_ujn.json +0 -40
  197. package/rules/generated/auto_NO_uu.se_eea.json +0 -40
  198. package/rules/generated/auto_US_newsbreak.com_0.json +0 -28
  199. package/tests/generated/auto_AU_flysas.com_r1s.spec.ts +0 -2
  200. package/tests/generated/auto_CH_airbnb.de_83z.spec.ts +0 -2
  201. package/tests/generated/auto_CH_autodoc.de_krd.spec.ts +0 -2
  202. package/tests/generated/auto_CH_svtplay.se_d39.spec.ts +0 -2
  203. package/tests/generated/auto_GB_parliamentlive.tv_r3v.spec.ts +0 -6
  204. package/tests/generated/auto_NL_asnbank.nl_e28.spec.ts +0 -6
  205. package/tests/generated/auto_NL_eurojackpot.nederlandseloterij.nl_7hr.spec.ts +0 -6
  206. package/tests/generated/auto_NL_lotto.nederlandseloterij.nl_7c8.spec.ts +0 -6
  207. package/tests/generated/auto_NL_nederlandseloterij.nl_b60.spec.ts +0 -6
  208. package/tests/generated/auto_NL_staatsloterij.nederlandseloterij.nl_197.spec.ts +0 -6
  209. package/tests/generated/auto_NO_arronet.se_vo5.spec.ts +0 -2
  210. package/tests/generated/auto_NO_liseberg.se_qy4.spec.ts +0 -2
  211. package/tests/generated/auto_NO_nordnet.se_2en.spec.ts +0 -2
  212. package/tests/generated/auto_NO_regeringen.se_uhv.spec.ts +0 -6
  213. package/tests/generated/auto_NO_svtplay.se_ujn.spec.ts +0 -2
  214. package/tests/generated/auto_NO_uu.se_eea.spec.ts +0 -2
  215. package/tests/generated/auto_US_newsbreak.com_0.spec.ts +0 -2
  216. package/tests/twitter.spec.ts +0 -10
@@ -344,6 +344,27 @@
344
344
  EVAL_COOKIEFIRST_1: () => document.querySelectorAll("button[data-cookiefirst-accent-color=true][role=checkbox]:not([disabled])").forEach((i) => i.getAttribute("aria-checked") === "true" && i.click()) || true,
345
345
  EVAL_COOKIEINFORMATION_0: () => CookieInformation.declineAllCategories() || true,
346
346
  EVAL_COOKIEINFORMATION_1: () => CookieInformation.submitAllCategories() || true,
347
+ // banner button order and meaning are configurable per site, so set the consent levels through the public API
348
+ EVAL_COOKIEREPORTS_OPT_OUT: () => {
349
+ const cr = window._cookiereports;
350
+ if (typeof cr?.saveConsent !== "function") return false;
351
+ cr.saveConsent({ 1: true, 2: false, 3: false, 4: false, 5: false });
352
+ cr.processConsent("banner", { trigger: "ok" });
353
+ cr.hideBanner();
354
+ return true;
355
+ },
356
+ EVAL_COOKIEREPORTS_OPT_IN: () => {
357
+ const cr = window._cookiereports;
358
+ if (typeof cr?.saveConsent !== "function") return false;
359
+ cr.saveConsent({ 1: true, 2: true, 3: true, 4: true, 5: true });
360
+ cr.processConsent("banner", { trigger: "ok" });
361
+ cr.hideBanner();
362
+ return true;
363
+ },
364
+ EVAL_COOKIEREPORTS_TEST: () => {
365
+ const levels = window._cookiereports?.loadConsent?.();
366
+ return !!levels && Object.keys(levels).length > 0 && Object.values(levels).every((v) => !v);
367
+ },
347
368
  // turn the single-button form into a decline submission, same fields as the CMP's own decline form
348
369
  EVAL_EE_CONSENT_DECLINE_0: () => document.querySelectorAll('#cookieConsentFormAccept input[name^="ee:cookies_"]').forEach((i) => i.value = "n") || true,
349
370
  EVAL_ETSY_0: () => document.querySelectorAll(".gdpr-overlay-body input").forEach((toggle) => {
@@ -594,10 +615,20 @@
594
615
  // "файлы cookie" / "cookie-файлы" / "куки-файлы" in any phrasing
595
616
  /(?:файл[а-яё]*[\s-]+(?:cookie|куки)|(?:cookie|куки)[\s-]+файл[а-яё]*)/gi,
596
617
  // Italian (IT)
597
- /usiamo.{0,20}cookie/gi,
618
+ // e.g. "usiamo i cookie", "utilizziamo cookie tecnici", "questo sito utilizza i cookie", "noi e i nostri partner utilizziamo i cookie"
619
+ /(?:usiamo|utilizziamo).{0,20}cookie/gi,
620
+ /sito.{0,50}(?:usa|utilizza).{0,20}cookie/gi,
621
+ /noi e i nostri.{0,20}partner.{0,100}cookie/gi,
622
+ /(?:archiviare|memorizzare) e\/o accedere a informazioni su un dispositivo/gi,
623
+ // Swedish (SV)
624
+ // e.g. "vi använder cookies", "vi använder spårningstekniker såsom cookies", "webbplatsen använder kakor"
625
+ /(?:vi|webbplatsen|webbplats|sajten|sidan)\s+använder.{0,60}(?:cookies|kakor)/gi,
598
626
  // Japanese (JA)
599
627
  // e.g. "当サイトはCookieを使用しています", "クッキー(Cookie)を利用しています"
600
- /(?:cookie|クッキー).{0,10}を(?:使用|利用)/gi
628
+ /(?:cookie|クッキー).{0,10}を(?:使用|利用)/gi,
629
+ // Danish (DA)
630
+ // e.g. "vi bruger cookies", "hjemmesiden bruger cookies til at ..."
631
+ /bruger cookies/gi
601
632
  ];
602
633
  var REJECT_PATTERNS_ENGLISH = [
603
634
  // e.g. "reject", "reject all", "reject all cookies", "deny all", "refuse cookies", "decline",
@@ -731,6 +762,14 @@
731
762
  "till\xE5t inte cookies",
732
763
  "jag accepterar endast grundl\xE4ggande kakor"
733
764
  ];
765
+ var REJECT_PATTERNS_DANISH = [
766
+ // afvis / afvis alle / afvis alle cookies (reject verbs)
767
+ /(^|\s)afvis/is,
768
+ // (accept/allow) only necessary cookies
769
+ /^(accepter\s+|tillad\s+)?(kun|udelukkende)\s+(strengt\s+)?nødvendige( cookies)?$/is,
770
+ // "nej tak" (no thanks)
771
+ /^nej(,?\s+tak)?$/is
772
+ ];
734
773
  var REJECT_PATTERNS_CATALAN = [/(^|\s)rebutj/is, "no accepto", "no, gr\xE0cies"];
735
774
  var REJECT_PATTERNS_GALICIAN = [/(^|\s)rexeitar/is];
736
775
  var REJECT_PATTERNS_BASQUE = [/(^|\s)(baztertu|ukatu)/is];
@@ -777,6 +816,7 @@
777
816
  ...REJECT_PATTERNS_BRAZILIAN_PORTUGUESE,
778
817
  ...REJECT_PATTERNS_SPANISH,
779
818
  ...REJECT_PATTERNS_SWEDISH,
819
+ ...REJECT_PATTERNS_DANISH,
780
820
  ...REJECT_PATTERNS_CATALAN,
781
821
  ...REJECT_PATTERNS_GALICIAN,
782
822
  ...REJECT_PATTERNS_BASQUE,
@@ -1126,7 +1166,8 @@
1126
1166
  "kabul et",
1127
1167
  // Italian (IT)
1128
1168
  "accetta",
1129
- "accetta tutti i cookie"
1169
+ "accetta tutti i cookie",
1170
+ "accetto tutti i cookie"
1130
1171
  ];
1131
1172
  var ACKNOWLEDGE_PATTERNS = [
1132
1173
  // close / dismiss the banner/dialog/message (multilingual). The negative lookahead avoids
@@ -1202,6 +1243,10 @@
1202
1243
  }
1203
1244
  return { patterns, snippets: snippets2 };
1204
1245
  }
1246
+ function hasHeuristicPatterns(allText, detectPatterns = DETECT_PATTERNS) {
1247
+ const text = allText.slice(0, TEXT_LIMIT);
1248
+ return detectPatterns.some((p) => text.search(p) !== -1);
1249
+ }
1205
1250
  function classifyButtons(buttons) {
1206
1251
  for (const button of buttons) {
1207
1252
  button.regexClassification = classifyButtonTextRegex(button.text);
@@ -1278,57 +1323,135 @@
1278
1323
  var POPUP_SEARCH_MAX_TIME = 100;
1279
1324
  function getActionablePopups(mode = "reject", timeout = POPUP_SEARCH_MAX_TIME) {
1280
1325
  const acceptedLevels = mode === "tier2" ? ["reject", "tier1", "tier2"] : mode === "tier1" ? ["reject", "tier1"] : ["reject"];
1281
- if (acceptedLevels.length === 0) {
1282
- return [];
1283
- }
1284
- const popups = getPotentialPopups(timeout);
1285
- const result = popups.reduce((acc, popup) => {
1326
+ const popups = getPotentialPopups(timeout).filter((popup) => {
1286
1327
  const popupText = popup.text?.trim();
1287
- if (popupText) {
1288
- if (isExcludedPopup(popupText)) {
1289
- return acc;
1290
- }
1291
- const { patterns } = checkHeuristicPatterns(popupText);
1292
- if (patterns.length > 0) {
1293
- classifyButtons(popup.buttons);
1294
- popup.regexClassification = classifyPopup(popup.buttons);
1295
- acc.push({
1296
- ...popup
1297
- });
1298
- }
1328
+ if (!popupText || isExcludedPopup(popupText) || !hasHeuristicPatterns(popupText)) {
1329
+ return false;
1299
1330
  }
1300
- return acc;
1301
- }, []);
1302
- return result.filter((popup) => popup.regexClassification !== void 0 && acceptedLevels.includes(popup.regexClassification)).sort((a, b) => (a.regexClassification ?? "") > (b.regexClassification ?? "") ? 1 : -1);
1331
+ popup.regexClassification = classifyPopup(popup.buttons);
1332
+ return popup.regexClassification !== void 0 && acceptedLevels.includes(popup.regexClassification);
1333
+ });
1334
+ return popups.sort((a, b) => (a.regexClassification ?? "") > (b.regexClassification ?? "") ? 1 : -1);
1303
1335
  }
1304
1336
  function getPotentialPopups(timeout = POPUP_SEARCH_MAX_TIME) {
1305
- const isFramed = !isTopFrame();
1306
- if (isFramed && window.parent && window.parent !== window.top) {
1307
- return [];
1308
- }
1309
- return collectPotentialPopups(isFramed, timeout);
1310
- }
1311
- function collectPotentialPopups(isFramed, timeout = POPUP_SEARCH_MAX_TIME) {
1312
- let elements = [];
1313
- if (!isFramed) {
1314
- elements = getPopupLikeElements(timeout);
1315
- } else {
1316
- const doc = document.body || document.documentElement;
1317
- if (doc && isElementVisible(doc) && doc.innerText) {
1318
- elements.push(doc);
1337
+ if (!isTopFrame()) {
1338
+ if (window.parent && window.parent !== window.top) {
1339
+ return [];
1319
1340
  }
1341
+ const frameElement = getFramePopupElement(document);
1342
+ const popup = frameElement && toPopupData(frameElement);
1343
+ return popup ? [popup] : [];
1320
1344
  }
1345
+ const deadline = performance.now() + timeout;
1346
+ const { elements, shadowHosts } = getPopupLikeElements(deadline);
1321
1347
  const potentialPopups = [];
1348
+ const expanded = [];
1322
1349
  for (const el of elements) {
1323
- if (el.innerText) {
1324
- potentialPopups.push({
1325
- text: el.innerText,
1326
- element: el,
1327
- buttons: getButtonData(el)
1328
- });
1350
+ const popup = toPopupData(el, shadowHosts);
1351
+ if (!popup) {
1352
+ continue;
1353
+ }
1354
+ const root = performance.now() > deadline ? popup : expandPopupFragment(popup, shadowHosts);
1355
+ if (potentialPopups.some((p) => p.element === root.element)) {
1356
+ continue;
1357
+ }
1358
+ if (root !== popup) {
1359
+ expanded.push(root.element);
1360
+ }
1361
+ potentialPopups.push(root);
1362
+ }
1363
+ return potentialPopups.filter((p) => !expanded.some((root) => root !== p.element && composedContains(root, p.element)));
1364
+ }
1365
+ function toPopupData(el, shadowHosts, text = getElementText(el)) {
1366
+ if (!text) {
1367
+ return null;
1368
+ }
1369
+ const buttons = getButtonData(el, shadowHosts);
1370
+ classifyButtons(buttons);
1371
+ return { text, element: el, buttons };
1372
+ }
1373
+ function hasPopupButtons(text, buttons) {
1374
+ return !isOnlyButtonText(text, buttons) && buttons.some((b) => b.regexClassification !== "other");
1375
+ }
1376
+ function isOnlyButtonText(text, buttons) {
1377
+ let rest = text;
1378
+ for (const button of buttons) {
1379
+ rest = rest.replace(button.text, "");
1380
+ }
1381
+ return rest.replace(/\s+/g, "").length < 10;
1382
+ }
1383
+ var MAX_FRAGMENT_EXPANSION_DEPTH = 6;
1384
+ function expandPopupFragment(popup, shadowHosts) {
1385
+ if (hasHeuristicPatterns(popup.text) && hasPopupButtons(popup.text, popup.buttons)) {
1386
+ return popup;
1387
+ }
1388
+ let el = getComposedParent(popup.element);
1389
+ for (let depth = 0; el && depth < MAX_FRAGMENT_EXPANSION_DEPTH; depth++, el = getComposedParent(el)) {
1390
+ if (el === document.body || el === document.documentElement || el.getBoundingClientRect().height > window.innerHeight) {
1391
+ break;
1392
+ }
1393
+ const text = getElementText(el);
1394
+ if (!text || !hasHeuristicPatterns(text)) {
1395
+ continue;
1396
+ }
1397
+ const root = toPopupData(el, shadowHosts, text);
1398
+ if (root && hasPopupButtons(text, root.buttons)) {
1399
+ return root;
1400
+ }
1401
+ }
1402
+ return popup;
1403
+ }
1404
+ function getComposedParent(el) {
1405
+ if (el.parentElement) {
1406
+ return el.parentElement;
1407
+ }
1408
+ const parent = el.parentNode;
1409
+ return parent instanceof ShadowRoot && parent.host instanceof HTMLElement ? parent.host : null;
1410
+ }
1411
+ function getFramePopupElement(doc) {
1412
+ const el = doc.body || doc.documentElement;
1413
+ const win = doc.defaultView;
1414
+ if (!el || !win || win.innerWidth === 0 || win.innerHeight === 0 || !isStyleVisible(el)) {
1415
+ return null;
1416
+ }
1417
+ return getElementText(el).trim() ? el : null;
1418
+ }
1419
+ function isStyleVisible(el) {
1420
+ return typeof el.checkVisibility !== "function" || el.checkVisibility({ checkVisibilityCSS: true });
1421
+ }
1422
+ function getElementText(el) {
1423
+ const text = el.innerText || "";
1424
+ if (!el.shadowRoot) {
1425
+ return text;
1426
+ }
1427
+ const shadowTexts = Array.from(el.shadowRoot.children, (child) => isRenderedTextElement(child) ? getElementText(child) : "");
1428
+ return [text, ...shadowTexts].filter((t) => t.trim()).join("\n");
1429
+ }
1430
+ function isRenderedTextElement(el) {
1431
+ return el instanceof HTMLElement && !["STYLE", "SCRIPT", "TEMPLATE", "NOSCRIPT", "LINK", "SLOT"].includes(el.tagName) && window.getComputedStyle(el).display !== "none";
1432
+ }
1433
+ function composedContains(container, node) {
1434
+ if (container.contains(node)) {
1435
+ return true;
1436
+ }
1437
+ let root = node.getRootNode();
1438
+ while (root instanceof ShadowRoot) {
1439
+ if (container.contains(root.host)) {
1440
+ return true;
1329
1441
  }
1442
+ root = root.host.getRootNode();
1443
+ }
1444
+ return false;
1445
+ }
1446
+ function isPopupCandidateVisible(el) {
1447
+ return isElementVisible(el) && isStyleVisible(el) && isOnScreen(el);
1448
+ }
1449
+ function isOnScreen(el) {
1450
+ const rect = el.getBoundingClientRect();
1451
+ if (rect.width === 0 || rect.height === 0) {
1452
+ return window.getComputedStyle(el).overflow === "visible" && Array.from(el.children).some((child) => isOnScreen(child));
1330
1453
  }
1331
- return potentialPopups;
1454
+ return rect.bottom > 0 && rect.right > 0 && rect.top < window.innerHeight && rect.left < window.innerWidth;
1332
1455
  }
1333
1456
  function isDialogLikeElement(node) {
1334
1457
  if (node.tagName === "DIALOG" && node.hasAttribute("open")) {
@@ -1339,72 +1462,95 @@
1339
1462
  }
1340
1463
  return false;
1341
1464
  }
1342
- function getPopupLikeElements(timeout = POPUP_SEARCH_MAX_TIME) {
1343
- const startTime = performance.now();
1344
- const walker = document.createTreeWalker(
1345
- document.documentElement,
1346
- NodeFilter.SHOW_ELEMENT,
1347
- // visit only element nodes
1348
- {
1349
- acceptNode(node) {
1350
- if (node.tagName === "BODY") {
1351
- return NodeFilter.FILTER_SKIP;
1352
- }
1353
- if (isElementVisible(node)) {
1354
- const cssPosition = window.getComputedStyle(node).position;
1355
- if (cssPosition === "fixed" || cssPosition === "sticky") {
1356
- return NodeFilter.FILTER_ACCEPT;
1465
+ function getPopupLikeElements(deadline) {
1466
+ const found = [];
1467
+ const shadowHosts = [];
1468
+ const roots = [document.documentElement];
1469
+ for (let i = 0; i < roots.length; i++) {
1470
+ if (i > 0 && performance.now() > deadline) {
1471
+ break;
1472
+ }
1473
+ const walker = document.createTreeWalker(
1474
+ roots[i],
1475
+ NodeFilter.SHOW_ELEMENT,
1476
+ // visit only element nodes
1477
+ {
1478
+ acceptNode(node) {
1479
+ if (node.shadowRoot) {
1480
+ shadowHosts.push(node);
1481
+ roots.push(node.shadowRoot);
1482
+ }
1483
+ if (node.tagName === "BODY") {
1484
+ return NodeFilter.FILTER_SKIP;
1357
1485
  }
1358
- if (isDialogLikeElement(node)) {
1486
+ const style = window.getComputedStyle(node);
1487
+ if (style.display === "none") {
1488
+ return NodeFilter.FILTER_REJECT;
1489
+ }
1490
+ const cssPosition = style.position;
1491
+ if ((cssPosition === "fixed" || cssPosition === "sticky" || isDialogLikeElement(node)) && isPopupCandidateVisible(node)) {
1359
1492
  return NodeFilter.FILTER_ACCEPT;
1360
1493
  }
1494
+ if (performance.now() > deadline) {
1495
+ return NodeFilter.FILTER_REJECT;
1496
+ }
1497
+ return NodeFilter.FILTER_SKIP;
1361
1498
  }
1362
- if (performance.now() - startTime > timeout) {
1363
- return NodeFilter.FILTER_REJECT;
1364
- }
1365
- return NodeFilter.FILTER_SKIP;
1366
1499
  }
1500
+ );
1501
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
1502
+ found.push(node);
1367
1503
  }
1368
- );
1369
- const found = [];
1370
- for (let node = walker.nextNode(); node; node = walker.nextNode()) {
1371
- found.push(node);
1372
1504
  }
1373
- return excludeContainers(found.filter((element) => element.innerText?.trim()));
1505
+ return { elements: excludeContainers(found.filter((element) => getElementText(element).trim())), shadowHosts };
1374
1506
  }
1375
- function getButtonData(el) {
1376
- const actionableButtons = excludeContainers(getButtonLikeElements(el)).filter(
1377
- (b) => isElementVisible(b) && !isDisabled(b) && (b.innerText?.trim() || // <input> values do not appear in innerText
1378
- b instanceof HTMLInputElement && ["submit", "button"].includes(b.type) && b.value?.trim())
1379
- );
1380
- return actionableButtons.map((b) => ({
1381
- text: (b.innerText || b.textContent || "").trim() || b.value?.trim() || "",
1382
- element: b
1383
- }));
1507
+ function getButtonData(el, shadowHosts) {
1508
+ const buttons = [];
1509
+ for (const b of excludeContainers(getButtonLikeElements(el, shadowHosts))) {
1510
+ if (!isElementVisible(b) || isDisabled(b)) {
1511
+ continue;
1512
+ }
1513
+ const text = getElementText(b).trim() || // <input> values do not appear in innerText
1514
+ (b instanceof HTMLInputElement && ["submit", "button"].includes(b.type) ? b.value?.trim() : "");
1515
+ if (text) {
1516
+ buttons.push({ text, element: b });
1517
+ }
1518
+ }
1519
+ return buttons;
1384
1520
  }
1385
- function getButtonLikeElements(el) {
1386
- return Array.from(el.querySelectorAll(BUTTON_LIKE_ELEMENT_SELECTOR));
1521
+ function getButtonLikeElements(el, shadowHosts = findShadowHosts(el)) {
1522
+ const buttons = Array.from(el.querySelectorAll(BUTTON_LIKE_ELEMENT_SELECTOR));
1523
+ for (const host of shadowHosts) {
1524
+ if (composedContains(el, host)) {
1525
+ buttons.push(...host.shadowRoot.querySelectorAll(BUTTON_LIKE_ELEMENT_SELECTOR));
1526
+ }
1527
+ }
1528
+ return buttons;
1529
+ }
1530
+ function findShadowHosts(root) {
1531
+ const hosts = [];
1532
+ const candidates = root instanceof HTMLElement ? [root, ...root.querySelectorAll("*")] : root.querySelectorAll("*");
1533
+ for (const el of candidates) {
1534
+ if (el.shadowRoot) {
1535
+ hosts.push(el, ...findShadowHosts(el.shadowRoot));
1536
+ }
1537
+ }
1538
+ return hosts;
1387
1539
  }
1388
1540
  function isDisabled(el) {
1389
1541
  return "disabled" in el && Boolean(el.disabled) || el.hasAttribute("disabled");
1390
1542
  }
1391
1543
  function excludeContainers(elements) {
1392
- const results = [];
1393
- if (elements.length > 0) {
1394
- for (let i = elements.length - 1; i >= 0; i--) {
1395
- let container = false;
1396
- for (let j = 0; j < elements.length; j++) {
1397
- if (i !== j && elements[i].contains(elements[j])) {
1398
- container = true;
1399
- break;
1400
- }
1401
- }
1402
- if (!container) {
1403
- results.push(elements[i]);
1544
+ const candidates = new Set(elements);
1545
+ const containers = /* @__PURE__ */ new Set();
1546
+ for (const el of elements) {
1547
+ for (let node = el.parentNode; node; node = node.parentNode || (node instanceof ShadowRoot ? node.host : null)) {
1548
+ if (candidates.has(node)) {
1549
+ containers.add(node);
1404
1550
  }
1405
1551
  }
1406
1552
  }
1407
- return results;
1553
+ return elements.filter((el) => !containers.has(el)).reverse();
1408
1554
  }
1409
1555
 
1410
1556
  // lib/cmps/base.ts
@@ -2198,6 +2344,18 @@
2198
2344
  get isCosmetic() {
2199
2345
  return false;
2200
2346
  }
2347
+ // Some sites pre-render the banner before the SDK loads, and clicks before then may be lost.
2348
+ // Sites that copy the markup without the SDK never get #onetrust-style, so only wait when the page loads the SDK.
2349
+ async waitForSdk() {
2350
+ if (this.elementExists("#onetrust-style")) {
2351
+ return;
2352
+ }
2353
+ const sdkRef = /cookielaw\.org|otSDKStub|otBannerSdk/;
2354
+ const sdkExpected = Array.from(document.scripts).some((s) => sdkRef.test(s.src) || !s.src && sdkRef.test(s.textContent || ""));
2355
+ if (sdkExpected) {
2356
+ await this.waitForElement("#onetrust-style", 1e4);
2357
+ }
2358
+ }
2201
2359
  async detectCmp() {
2202
2360
  return this.elementExists("#onetrust-banner-sdk") || this.elementVisible("#onetrust-pc-sdk", "any");
2203
2361
  }
@@ -2206,6 +2364,7 @@
2206
2364
  }
2207
2365
  async optOut() {
2208
2366
  await this.wait(500);
2367
+ await this.waitForSdk();
2209
2368
  if (this.elementVisible("#onetrust-reject-all-handler", "any")) {
2210
2369
  return await this.click("#onetrust-reject-all-handler");
2211
2370
  }
@@ -2245,6 +2404,7 @@
2245
2404
  return true;
2246
2405
  }
2247
2406
  async optIn() {
2407
+ await this.waitForSdk();
2248
2408
  return await this.click("#onetrust-accept-btn-handler,#accept-recommended-btn-handler,.js-accept-cookies");
2249
2409
  }
2250
2410
  async test() {