@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
@@ -342,6 +342,27 @@ var snippets = {
342
342
  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,
343
343
  EVAL_COOKIEINFORMATION_0: () => CookieInformation.declineAllCategories() || true,
344
344
  EVAL_COOKIEINFORMATION_1: () => CookieInformation.submitAllCategories() || true,
345
+ // banner button order and meaning are configurable per site, so set the consent levels through the public API
346
+ EVAL_COOKIEREPORTS_OPT_OUT: () => {
347
+ const cr = window._cookiereports;
348
+ if (typeof cr?.saveConsent !== "function") return false;
349
+ cr.saveConsent({ 1: true, 2: false, 3: false, 4: false, 5: false });
350
+ cr.processConsent("banner", { trigger: "ok" });
351
+ cr.hideBanner();
352
+ return true;
353
+ },
354
+ EVAL_COOKIEREPORTS_OPT_IN: () => {
355
+ const cr = window._cookiereports;
356
+ if (typeof cr?.saveConsent !== "function") return false;
357
+ cr.saveConsent({ 1: true, 2: true, 3: true, 4: true, 5: true });
358
+ cr.processConsent("banner", { trigger: "ok" });
359
+ cr.hideBanner();
360
+ return true;
361
+ },
362
+ EVAL_COOKIEREPORTS_TEST: () => {
363
+ const levels = window._cookiereports?.loadConsent?.();
364
+ return !!levels && Object.keys(levels).length > 0 && Object.values(levels).every((v) => !v);
365
+ },
345
366
  // turn the single-button form into a decline submission, same fields as the CMP's own decline form
346
367
  EVAL_EE_CONSENT_DECLINE_0: () => document.querySelectorAll('#cookieConsentFormAccept input[name^="ee:cookies_"]').forEach((i) => i.value = "n") || true,
347
368
  EVAL_ETSY_0: () => document.querySelectorAll(".gdpr-overlay-body input").forEach((toggle) => {
@@ -592,10 +613,20 @@ var DETECT_PATTERNS = [
592
613
  // "файлы cookie" / "cookie-файлы" / "куки-файлы" in any phrasing
593
614
  /(?:файл[а-яё]*[\s-]+(?:cookie|куки)|(?:cookie|куки)[\s-]+файл[а-яё]*)/gi,
594
615
  // Italian (IT)
595
- /usiamo.{0,20}cookie/gi,
616
+ // e.g. "usiamo i cookie", "utilizziamo cookie tecnici", "questo sito utilizza i cookie", "noi e i nostri partner utilizziamo i cookie"
617
+ /(?:usiamo|utilizziamo).{0,20}cookie/gi,
618
+ /sito.{0,50}(?:usa|utilizza).{0,20}cookie/gi,
619
+ /noi e i nostri.{0,20}partner.{0,100}cookie/gi,
620
+ /(?:archiviare|memorizzare) e\/o accedere a informazioni su un dispositivo/gi,
621
+ // Swedish (SV)
622
+ // e.g. "vi använder cookies", "vi använder spårningstekniker såsom cookies", "webbplatsen använder kakor"
623
+ /(?:vi|webbplatsen|webbplats|sajten|sidan)\s+använder.{0,60}(?:cookies|kakor)/gi,
596
624
  // Japanese (JA)
597
625
  // e.g. "当サイトはCookieを使用しています", "クッキー(Cookie)を利用しています"
598
- /(?:cookie|クッキー).{0,10}を(?:使用|利用)/gi
626
+ /(?:cookie|クッキー).{0,10}を(?:使用|利用)/gi,
627
+ // Danish (DA)
628
+ // e.g. "vi bruger cookies", "hjemmesiden bruger cookies til at ..."
629
+ /bruger cookies/gi
599
630
  ];
600
631
  var REJECT_PATTERNS_ENGLISH = [
601
632
  // e.g. "reject", "reject all", "reject all cookies", "deny all", "refuse cookies", "decline",
@@ -729,6 +760,14 @@ var REJECT_PATTERNS_SWEDISH = [
729
760
  "till\xE5t inte cookies",
730
761
  "jag accepterar endast grundl\xE4ggande kakor"
731
762
  ];
763
+ var REJECT_PATTERNS_DANISH = [
764
+ // afvis / afvis alle / afvis alle cookies (reject verbs)
765
+ /(^|\s)afvis/is,
766
+ // (accept/allow) only necessary cookies
767
+ /^(accepter\s+|tillad\s+)?(kun|udelukkende)\s+(strengt\s+)?nødvendige( cookies)?$/is,
768
+ // "nej tak" (no thanks)
769
+ /^nej(,?\s+tak)?$/is
770
+ ];
732
771
  var REJECT_PATTERNS_CATALAN = [/(^|\s)rebutj/is, "no accepto", "no, gr\xE0cies"];
733
772
  var REJECT_PATTERNS_GALICIAN = [/(^|\s)rexeitar/is];
734
773
  var REJECT_PATTERNS_BASQUE = [/(^|\s)(baztertu|ukatu)/is];
@@ -775,6 +814,7 @@ var REJECT_PATTERNS = [
775
814
  ...REJECT_PATTERNS_BRAZILIAN_PORTUGUESE,
776
815
  ...REJECT_PATTERNS_SPANISH,
777
816
  ...REJECT_PATTERNS_SWEDISH,
817
+ ...REJECT_PATTERNS_DANISH,
778
818
  ...REJECT_PATTERNS_CATALAN,
779
819
  ...REJECT_PATTERNS_GALICIAN,
780
820
  ...REJECT_PATTERNS_BASQUE,
@@ -1124,7 +1164,8 @@ var ACCEPT_PATTERNS = [
1124
1164
  "kabul et",
1125
1165
  // Italian (IT)
1126
1166
  "accetta",
1127
- "accetta tutti i cookie"
1167
+ "accetta tutti i cookie",
1168
+ "accetto tutti i cookie"
1128
1169
  ];
1129
1170
  var ACKNOWLEDGE_PATTERNS = [
1130
1171
  // close / dismiss the banner/dialog/message (multilingual). The negative lookahead avoids
@@ -1200,6 +1241,10 @@ function checkHeuristicPatterns(allText, detectPatterns = DETECT_PATTERNS) {
1200
1241
  }
1201
1242
  return { patterns, snippets: snippets2 };
1202
1243
  }
1244
+ function hasHeuristicPatterns(allText, detectPatterns = DETECT_PATTERNS) {
1245
+ const text = allText.slice(0, TEXT_LIMIT);
1246
+ return detectPatterns.some((p) => text.search(p) !== -1);
1247
+ }
1203
1248
  function classifyButtons(buttons) {
1204
1249
  for (const button of buttons) {
1205
1250
  button.regexClassification = classifyButtonTextRegex(button.text);
@@ -1276,57 +1321,135 @@ var BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="su
1276
1321
  var POPUP_SEARCH_MAX_TIME = 100;
1277
1322
  function getActionablePopups(mode = "reject", timeout = POPUP_SEARCH_MAX_TIME) {
1278
1323
  const acceptedLevels = mode === "tier2" ? ["reject", "tier1", "tier2"] : mode === "tier1" ? ["reject", "tier1"] : ["reject"];
1279
- if (acceptedLevels.length === 0) {
1280
- return [];
1281
- }
1282
- const popups = getPotentialPopups(timeout);
1283
- const result = popups.reduce((acc, popup) => {
1324
+ const popups = getPotentialPopups(timeout).filter((popup) => {
1284
1325
  const popupText = popup.text?.trim();
1285
- if (popupText) {
1286
- if (isExcludedPopup(popupText)) {
1287
- return acc;
1288
- }
1289
- const { patterns } = checkHeuristicPatterns(popupText);
1290
- if (patterns.length > 0) {
1291
- classifyButtons(popup.buttons);
1292
- popup.regexClassification = classifyPopup(popup.buttons);
1293
- acc.push({
1294
- ...popup
1295
- });
1296
- }
1326
+ if (!popupText || isExcludedPopup(popupText) || !hasHeuristicPatterns(popupText)) {
1327
+ return false;
1297
1328
  }
1298
- return acc;
1299
- }, []);
1300
- return result.filter((popup) => popup.regexClassification !== void 0 && acceptedLevels.includes(popup.regexClassification)).sort((a, b) => (a.regexClassification ?? "") > (b.regexClassification ?? "") ? 1 : -1);
1329
+ popup.regexClassification = classifyPopup(popup.buttons);
1330
+ return popup.regexClassification !== void 0 && acceptedLevels.includes(popup.regexClassification);
1331
+ });
1332
+ return popups.sort((a, b) => (a.regexClassification ?? "") > (b.regexClassification ?? "") ? 1 : -1);
1301
1333
  }
1302
1334
  function getPotentialPopups(timeout = POPUP_SEARCH_MAX_TIME) {
1303
- const isFramed = !isTopFrame();
1304
- if (isFramed && window.parent && window.parent !== window.top) {
1305
- return [];
1306
- }
1307
- return collectPotentialPopups(isFramed, timeout);
1308
- }
1309
- function collectPotentialPopups(isFramed, timeout = POPUP_SEARCH_MAX_TIME) {
1310
- let elements = [];
1311
- if (!isFramed) {
1312
- elements = getPopupLikeElements(timeout);
1313
- } else {
1314
- const doc = document.body || document.documentElement;
1315
- if (doc && isElementVisible(doc) && doc.innerText) {
1316
- elements.push(doc);
1335
+ if (!isTopFrame()) {
1336
+ if (window.parent && window.parent !== window.top) {
1337
+ return [];
1317
1338
  }
1339
+ const frameElement = getFramePopupElement(document);
1340
+ const popup = frameElement && toPopupData(frameElement);
1341
+ return popup ? [popup] : [];
1318
1342
  }
1343
+ const deadline = performance.now() + timeout;
1344
+ const { elements, shadowHosts } = getPopupLikeElements(deadline);
1319
1345
  const potentialPopups = [];
1346
+ const expanded = [];
1320
1347
  for (const el of elements) {
1321
- if (el.innerText) {
1322
- potentialPopups.push({
1323
- text: el.innerText,
1324
- element: el,
1325
- buttons: getButtonData(el)
1326
- });
1348
+ const popup = toPopupData(el, shadowHosts);
1349
+ if (!popup) {
1350
+ continue;
1351
+ }
1352
+ const root = performance.now() > deadline ? popup : expandPopupFragment(popup, shadowHosts);
1353
+ if (potentialPopups.some((p) => p.element === root.element)) {
1354
+ continue;
1355
+ }
1356
+ if (root !== popup) {
1357
+ expanded.push(root.element);
1358
+ }
1359
+ potentialPopups.push(root);
1360
+ }
1361
+ return potentialPopups.filter((p) => !expanded.some((root) => root !== p.element && composedContains(root, p.element)));
1362
+ }
1363
+ function toPopupData(el, shadowHosts, text = getElementText(el)) {
1364
+ if (!text) {
1365
+ return null;
1366
+ }
1367
+ const buttons = getButtonData(el, shadowHosts);
1368
+ classifyButtons(buttons);
1369
+ return { text, element: el, buttons };
1370
+ }
1371
+ function hasPopupButtons(text, buttons) {
1372
+ return !isOnlyButtonText(text, buttons) && buttons.some((b) => b.regexClassification !== "other");
1373
+ }
1374
+ function isOnlyButtonText(text, buttons) {
1375
+ let rest = text;
1376
+ for (const button of buttons) {
1377
+ rest = rest.replace(button.text, "");
1378
+ }
1379
+ return rest.replace(/\s+/g, "").length < 10;
1380
+ }
1381
+ var MAX_FRAGMENT_EXPANSION_DEPTH = 6;
1382
+ function expandPopupFragment(popup, shadowHosts) {
1383
+ if (hasHeuristicPatterns(popup.text) && hasPopupButtons(popup.text, popup.buttons)) {
1384
+ return popup;
1385
+ }
1386
+ let el = getComposedParent(popup.element);
1387
+ for (let depth = 0; el && depth < MAX_FRAGMENT_EXPANSION_DEPTH; depth++, el = getComposedParent(el)) {
1388
+ if (el === document.body || el === document.documentElement || el.getBoundingClientRect().height > window.innerHeight) {
1389
+ break;
1390
+ }
1391
+ const text = getElementText(el);
1392
+ if (!text || !hasHeuristicPatterns(text)) {
1393
+ continue;
1394
+ }
1395
+ const root = toPopupData(el, shadowHosts, text);
1396
+ if (root && hasPopupButtons(text, root.buttons)) {
1397
+ return root;
1398
+ }
1399
+ }
1400
+ return popup;
1401
+ }
1402
+ function getComposedParent(el) {
1403
+ if (el.parentElement) {
1404
+ return el.parentElement;
1405
+ }
1406
+ const parent = el.parentNode;
1407
+ return parent instanceof ShadowRoot && parent.host instanceof HTMLElement ? parent.host : null;
1408
+ }
1409
+ function getFramePopupElement(doc) {
1410
+ const el = doc.body || doc.documentElement;
1411
+ const win = doc.defaultView;
1412
+ if (!el || !win || win.innerWidth === 0 || win.innerHeight === 0 || !isStyleVisible(el)) {
1413
+ return null;
1414
+ }
1415
+ return getElementText(el).trim() ? el : null;
1416
+ }
1417
+ function isStyleVisible(el) {
1418
+ return typeof el.checkVisibility !== "function" || el.checkVisibility({ checkVisibilityCSS: true });
1419
+ }
1420
+ function getElementText(el) {
1421
+ const text = el.innerText || "";
1422
+ if (!el.shadowRoot) {
1423
+ return text;
1424
+ }
1425
+ const shadowTexts = Array.from(el.shadowRoot.children, (child) => isRenderedTextElement(child) ? getElementText(child) : "");
1426
+ return [text, ...shadowTexts].filter((t) => t.trim()).join("\n");
1427
+ }
1428
+ function isRenderedTextElement(el) {
1429
+ return el instanceof HTMLElement && !["STYLE", "SCRIPT", "TEMPLATE", "NOSCRIPT", "LINK", "SLOT"].includes(el.tagName) && window.getComputedStyle(el).display !== "none";
1430
+ }
1431
+ function composedContains(container, node) {
1432
+ if (container.contains(node)) {
1433
+ return true;
1434
+ }
1435
+ let root = node.getRootNode();
1436
+ while (root instanceof ShadowRoot) {
1437
+ if (container.contains(root.host)) {
1438
+ return true;
1327
1439
  }
1440
+ root = root.host.getRootNode();
1441
+ }
1442
+ return false;
1443
+ }
1444
+ function isPopupCandidateVisible(el) {
1445
+ return isElementVisible(el) && isStyleVisible(el) && isOnScreen(el);
1446
+ }
1447
+ function isOnScreen(el) {
1448
+ const rect = el.getBoundingClientRect();
1449
+ if (rect.width === 0 || rect.height === 0) {
1450
+ return window.getComputedStyle(el).overflow === "visible" && Array.from(el.children).some((child) => isOnScreen(child));
1328
1451
  }
1329
- return potentialPopups;
1452
+ return rect.bottom > 0 && rect.right > 0 && rect.top < window.innerHeight && rect.left < window.innerWidth;
1330
1453
  }
1331
1454
  function isDialogLikeElement(node) {
1332
1455
  if (node.tagName === "DIALOG" && node.hasAttribute("open")) {
@@ -1337,72 +1460,95 @@ function isDialogLikeElement(node) {
1337
1460
  }
1338
1461
  return false;
1339
1462
  }
1340
- function getPopupLikeElements(timeout = POPUP_SEARCH_MAX_TIME) {
1341
- const startTime = performance.now();
1342
- const walker = document.createTreeWalker(
1343
- document.documentElement,
1344
- NodeFilter.SHOW_ELEMENT,
1345
- // visit only element nodes
1346
- {
1347
- acceptNode(node) {
1348
- if (node.tagName === "BODY") {
1349
- return NodeFilter.FILTER_SKIP;
1350
- }
1351
- if (isElementVisible(node)) {
1352
- const cssPosition = window.getComputedStyle(node).position;
1353
- if (cssPosition === "fixed" || cssPosition === "sticky") {
1354
- return NodeFilter.FILTER_ACCEPT;
1463
+ function getPopupLikeElements(deadline) {
1464
+ const found = [];
1465
+ const shadowHosts = [];
1466
+ const roots = [document.documentElement];
1467
+ for (let i = 0; i < roots.length; i++) {
1468
+ if (i > 0 && performance.now() > deadline) {
1469
+ break;
1470
+ }
1471
+ const walker = document.createTreeWalker(
1472
+ roots[i],
1473
+ NodeFilter.SHOW_ELEMENT,
1474
+ // visit only element nodes
1475
+ {
1476
+ acceptNode(node) {
1477
+ if (node.shadowRoot) {
1478
+ shadowHosts.push(node);
1479
+ roots.push(node.shadowRoot);
1480
+ }
1481
+ if (node.tagName === "BODY") {
1482
+ return NodeFilter.FILTER_SKIP;
1355
1483
  }
1356
- if (isDialogLikeElement(node)) {
1484
+ const style = window.getComputedStyle(node);
1485
+ if (style.display === "none") {
1486
+ return NodeFilter.FILTER_REJECT;
1487
+ }
1488
+ const cssPosition = style.position;
1489
+ if ((cssPosition === "fixed" || cssPosition === "sticky" || isDialogLikeElement(node)) && isPopupCandidateVisible(node)) {
1357
1490
  return NodeFilter.FILTER_ACCEPT;
1358
1491
  }
1492
+ if (performance.now() > deadline) {
1493
+ return NodeFilter.FILTER_REJECT;
1494
+ }
1495
+ return NodeFilter.FILTER_SKIP;
1359
1496
  }
1360
- if (performance.now() - startTime > timeout) {
1361
- return NodeFilter.FILTER_REJECT;
1362
- }
1363
- return NodeFilter.FILTER_SKIP;
1364
1497
  }
1498
+ );
1499
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
1500
+ found.push(node);
1365
1501
  }
1366
- );
1367
- const found = [];
1368
- for (let node = walker.nextNode(); node; node = walker.nextNode()) {
1369
- found.push(node);
1370
1502
  }
1371
- return excludeContainers(found.filter((element) => element.innerText?.trim()));
1503
+ return { elements: excludeContainers(found.filter((element) => getElementText(element).trim())), shadowHosts };
1372
1504
  }
1373
- function getButtonData(el) {
1374
- const actionableButtons = excludeContainers(getButtonLikeElements(el)).filter(
1375
- (b) => isElementVisible(b) && !isDisabled(b) && (b.innerText?.trim() || // <input> values do not appear in innerText
1376
- b instanceof HTMLInputElement && ["submit", "button"].includes(b.type) && b.value?.trim())
1377
- );
1378
- return actionableButtons.map((b) => ({
1379
- text: (b.innerText || b.textContent || "").trim() || b.value?.trim() || "",
1380
- element: b
1381
- }));
1505
+ function getButtonData(el, shadowHosts) {
1506
+ const buttons = [];
1507
+ for (const b of excludeContainers(getButtonLikeElements(el, shadowHosts))) {
1508
+ if (!isElementVisible(b) || isDisabled(b)) {
1509
+ continue;
1510
+ }
1511
+ const text = getElementText(b).trim() || // <input> values do not appear in innerText
1512
+ (b instanceof HTMLInputElement && ["submit", "button"].includes(b.type) ? b.value?.trim() : "");
1513
+ if (text) {
1514
+ buttons.push({ text, element: b });
1515
+ }
1516
+ }
1517
+ return buttons;
1382
1518
  }
1383
- function getButtonLikeElements(el) {
1384
- return Array.from(el.querySelectorAll(BUTTON_LIKE_ELEMENT_SELECTOR));
1519
+ function getButtonLikeElements(el, shadowHosts = findShadowHosts(el)) {
1520
+ const buttons = Array.from(el.querySelectorAll(BUTTON_LIKE_ELEMENT_SELECTOR));
1521
+ for (const host of shadowHosts) {
1522
+ if (composedContains(el, host)) {
1523
+ buttons.push(...host.shadowRoot.querySelectorAll(BUTTON_LIKE_ELEMENT_SELECTOR));
1524
+ }
1525
+ }
1526
+ return buttons;
1527
+ }
1528
+ function findShadowHosts(root) {
1529
+ const hosts = [];
1530
+ const candidates = root instanceof HTMLElement ? [root, ...root.querySelectorAll("*")] : root.querySelectorAll("*");
1531
+ for (const el of candidates) {
1532
+ if (el.shadowRoot) {
1533
+ hosts.push(el, ...findShadowHosts(el.shadowRoot));
1534
+ }
1535
+ }
1536
+ return hosts;
1385
1537
  }
1386
1538
  function isDisabled(el) {
1387
1539
  return "disabled" in el && Boolean(el.disabled) || el.hasAttribute("disabled");
1388
1540
  }
1389
1541
  function excludeContainers(elements) {
1390
- const results = [];
1391
- if (elements.length > 0) {
1392
- for (let i = elements.length - 1; i >= 0; i--) {
1393
- let container = false;
1394
- for (let j = 0; j < elements.length; j++) {
1395
- if (i !== j && elements[i].contains(elements[j])) {
1396
- container = true;
1397
- break;
1398
- }
1399
- }
1400
- if (!container) {
1401
- results.push(elements[i]);
1542
+ const candidates = new Set(elements);
1543
+ const containers = /* @__PURE__ */ new Set();
1544
+ for (const el of elements) {
1545
+ for (let node = el.parentNode; node; node = node.parentNode || (node instanceof ShadowRoot ? node.host : null)) {
1546
+ if (candidates.has(node)) {
1547
+ containers.add(node);
1402
1548
  }
1403
1549
  }
1404
1550
  }
1405
- return results;
1551
+ return elements.filter((el) => !containers.has(el)).reverse();
1406
1552
  }
1407
1553
 
1408
1554
  // lib/cmps/base.ts
@@ -2196,6 +2342,18 @@ var Onetrust = class extends AutoConsentCMPBase {
2196
2342
  get isCosmetic() {
2197
2343
  return false;
2198
2344
  }
2345
+ // Some sites pre-render the banner before the SDK loads, and clicks before then may be lost.
2346
+ // Sites that copy the markup without the SDK never get #onetrust-style, so only wait when the page loads the SDK.
2347
+ async waitForSdk() {
2348
+ if (this.elementExists("#onetrust-style")) {
2349
+ return;
2350
+ }
2351
+ const sdkRef = /cookielaw\.org|otSDKStub|otBannerSdk/;
2352
+ const sdkExpected = Array.from(document.scripts).some((s) => sdkRef.test(s.src) || !s.src && sdkRef.test(s.textContent || ""));
2353
+ if (sdkExpected) {
2354
+ await this.waitForElement("#onetrust-style", 1e4);
2355
+ }
2356
+ }
2199
2357
  async detectCmp() {
2200
2358
  return this.elementExists("#onetrust-banner-sdk") || this.elementVisible("#onetrust-pc-sdk", "any");
2201
2359
  }
@@ -2204,6 +2362,7 @@ var Onetrust = class extends AutoConsentCMPBase {
2204
2362
  }
2205
2363
  async optOut() {
2206
2364
  await this.wait(500);
2365
+ await this.waitForSdk();
2207
2366
  if (this.elementVisible("#onetrust-reject-all-handler", "any")) {
2208
2367
  return await this.click("#onetrust-reject-all-handler");
2209
2368
  }
@@ -2243,6 +2402,7 @@ var Onetrust = class extends AutoConsentCMPBase {
2243
2402
  return true;
2244
2403
  }
2245
2404
  async optIn() {
2405
+ await this.waitForSdk();
2246
2406
  return await this.click("#onetrust-accept-btn-handler,#accept-recommended-btn-handler,.js-accept-cookies");
2247
2407
  }
2248
2408
  async test() {