@duckduckgo/autoconsent 14.97.0 → 15.0.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 (39) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/addon-firefox/background.bundle.js +10 -1
  3. package/dist/addon-firefox/compact-rules.json +1 -1
  4. package/dist/addon-firefox/content.bundle.js +142 -33
  5. package/dist/addon-firefox/manifest.json +1 -1
  6. package/dist/addon-firefox/popup.bundle.js +10 -1
  7. package/dist/addon-firefox/rules.json +1 -1
  8. package/dist/addon-mv3/background.bundle.js +10 -1
  9. package/dist/addon-mv3/compact-rules.json +1 -1
  10. package/dist/addon-mv3/content.bundle.js +142 -33
  11. package/dist/addon-mv3/manifest.json +1 -1
  12. package/dist/addon-mv3/popup.bundle.js +10 -1
  13. package/dist/addon-mv3/rules.json +1 -1
  14. package/dist/autoconsent.cjs.js +142 -33
  15. package/dist/autoconsent.esm.js +142 -33
  16. package/dist/autoconsent.extra.cjs.js +142 -33
  17. package/dist/autoconsent.extra.esm.js +142 -33
  18. package/dist/autoconsent.playwright.js +142 -33
  19. package/dist/types/cmps/base.d.ts +4 -2
  20. package/dist/types/heuristic-patterns.d.ts +3 -0
  21. package/dist/types/heuristics.d.ts +4 -7
  22. package/dist/types/types.d.ts +18 -2
  23. package/lib/cmps/base.ts +20 -7
  24. package/lib/heuristic-patterns.ts +68 -1
  25. package/lib/heuristics.ts +76 -27
  26. package/lib/types.ts +20 -2
  27. package/lib/utils.ts +2 -1
  28. package/lib/web.ts +3 -1
  29. package/package.json +1 -1
  30. package/rules/autoconsent/twitch.json +1 -1
  31. package/rules/compact-rules.json +1 -1
  32. package/rules/rules.json +1 -1
  33. package/tests-wtr/heuristics/get-actionable-popups.html +16 -0
  34. package/tests-wtr/heuristics/get-actionable-popups.ts +85 -6
  35. package/tests-wtr/heuristics/heuristic-cmp.html +69 -0
  36. package/tests-wtr/heuristics/heuristic-cmp.ts +178 -0
  37. package/tests-wtr/heuristics/heuristics-utils.test.ts +96 -28
  38. package/tests-wtr/lifecycle/find-cmp.html +14 -0
  39. package/tests-wtr/lifecycle/find-cmp.ts +44 -2
@@ -435,6 +435,14 @@ async function evalAction(config) {
435
435
  });
436
436
  }
437
437
 
438
+ // lib/types.ts
439
+ var PopupHandlingModes = {
440
+ None: -1,
441
+ Reject: 0,
442
+ Tier1: 1,
443
+ Tier2: 2
444
+ };
445
+
438
446
  // lib/random.ts
439
447
  function getRandomID() {
440
448
  if (crypto && typeof crypto.randomUUID !== "undefined") {
@@ -744,7 +752,8 @@ function normalizeConfig(providedConfig) {
744
752
  waits: false
745
753
  },
746
754
  performanceLoggingEnabled: false,
747
- heuristicPopupSearchTimeout: 100
755
+ heuristicPopupSearchTimeout: 100,
756
+ heuristicMode: PopupHandlingModes.Reject
748
757
  };
749
758
  const updatedConfig = copyObject(defaultConfig);
750
759
  for (const key of Object.keys(defaultConfig)) {
@@ -911,12 +920,17 @@ var REJECT_PATTERNS_ENGLISH = [
911
920
  /^\s*(use|accept|allow|continue\s+with)?\s*only\s*(strictly)?\s*(necessary|essentials?|required)?\s*(cookies)?\s*$/is,
912
921
  // e.g. "do not sell or share my personal information", "do not sell my personal information"
913
922
  // often used in CCPA
914
- /^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*information\s*$/is,
923
+ /^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*info(rmation)?\s*$/is,
915
924
  "allow selection",
916
- "disagree and close"
925
+ "disagree and close",
917
926
  // These are impactful, but look error-prone
927
+ // // e.g. "disagree"
928
+ /^(i)?\s*disagree\s*(and\s+close)?$/i,
918
929
  // // e.g. "i do not agree"
919
930
  // /^\s*(i\s+)?do\s+not\s+agree\s*$/i,
931
+ "no",
932
+ /^no,? thanks$/is,
933
+ /^opt[ -]out$/is
920
934
  ];
921
935
  var REJECT_PATTERNS_DUTCH = [
922
936
  "weigeren",
@@ -1492,6 +1506,64 @@ var NEVER_MATCH_PATTERNS = [
1492
1506
  /sostienici/is,
1493
1507
  /suscribir/is
1494
1508
  ];
1509
+ var SETTINGS_PATTERNS = [
1510
+ "settings",
1511
+ "preferences",
1512
+ /customi(s|z)e/is,
1513
+ "show details",
1514
+ "more options",
1515
+ /(manage|configure) (my|your) (preferences|choices|cookies)/is,
1516
+ "manage choices",
1517
+ /(cookie )?preference center/is,
1518
+ "change settings",
1519
+ "configure",
1520
+ "change my preferences",
1521
+ "cookie manager",
1522
+ "cookie preference",
1523
+ "let me choose",
1524
+ "cookieconsent preferences",
1525
+ /privacy choices/is,
1526
+ /(privacy|cookie|custom) settings/is,
1527
+ /cookies? (settings|preferences|setting)/is,
1528
+ /(manage|customize|customise|opt-out|edit).*(cookies|preferences|settings|options)/is,
1529
+ "cookie consent options",
1530
+ "privacy controls",
1531
+ "show purposes",
1532
+ // German
1533
+ "einstellungen"
1534
+ ];
1535
+ var ACCEPT_PATTERNS = [
1536
+ /^(accept|allow)( all)?( cookies)?$/is,
1537
+ /i (accept|allow)( all)?/is,
1538
+ "yes",
1539
+ /^(i )?agree$/is,
1540
+ "continue with all",
1541
+ "accept and continue",
1542
+ /accept all above/is,
1543
+ /^(accept|agree) and close/is,
1544
+ "accept continue",
1545
+ "agree proceed",
1546
+ "allow and continue",
1547
+ "close and accept",
1548
+ /accept all$/is,
1549
+ "im ok with that",
1550
+ "accept optional cookies",
1551
+ /^alle (cookies )?akzeptieren$/is
1552
+ ];
1553
+ var ACKNOWLEDGE_PATTERNS = [
1554
+ "ok",
1555
+ "close",
1556
+ "continue",
1557
+ "x",
1558
+ /^got it!?$/,
1559
+ "i understand",
1560
+ "dismiss",
1561
+ "okay",
1562
+ "acknowledge",
1563
+ /^close (banner|cookie notification)$/is,
1564
+ /understood$/is,
1565
+ "confirm my choices"
1566
+ ];
1495
1567
 
1496
1568
  // lib/heuristics.ts
1497
1569
  var BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
@@ -1510,48 +1582,61 @@ function checkHeuristicPatterns(allText, detectPatterns = DETECT_PATTERNS) {
1510
1582
  }
1511
1583
  return { patterns, snippets: snippets2 };
1512
1584
  }
1513
- function getActionablePopups(timeout = POPUP_SEARCH_MAX_TIME) {
1585
+ function getActionablePopups(mode = PopupHandlingModes.Reject, timeout = POPUP_SEARCH_MAX_TIME) {
1514
1586
  const popups = getPotentialPopups(timeout);
1515
1587
  const result = popups.reduce((acc, popup) => {
1516
1588
  const popupText = popup.text?.trim();
1517
1589
  if (popupText) {
1518
1590
  const { patterns } = checkHeuristicPatterns(popupText);
1519
1591
  if (patterns.length > 0) {
1520
- const { rejectButtons, otherButtons } = classifyButtons(popup.buttons);
1521
- if (rejectButtons.length > 0) {
1522
- acc.push({
1523
- ...popup,
1524
- rejectButtons,
1525
- otherButtons
1526
- });
1527
- }
1592
+ classifyButtons(popup.buttons);
1593
+ popup.regexClassification = classifyPopup(popup.buttons);
1594
+ acc.push({
1595
+ ...popup
1596
+ });
1528
1597
  }
1529
1598
  }
1530
1599
  return acc;
1531
1600
  }, []);
1532
- return result;
1601
+ return result.filter(
1602
+ (popup) => popup.regexClassification !== void 0 && popup.regexClassification !== PopupHandlingModes.None && popup.regexClassification <= mode
1603
+ ).sort((a, b) => (a.regexClassification ?? 0) - (b.regexClassification ?? 0));
1533
1604
  }
1534
1605
  function classifyButtons(buttons) {
1535
- const rejectButtons = [];
1536
- const otherButtons = [];
1537
1606
  for (const button of buttons) {
1538
- if (isRejectButton(button.text)) {
1539
- rejectButtons.push(button);
1540
- } else {
1541
- otherButtons.push(button);
1542
- }
1607
+ button.regexClassification = classifyButtonTextRegex(button.text);
1543
1608
  }
1544
- return {
1545
- rejectButtons,
1546
- otherButtons
1547
- };
1548
1609
  }
1549
- function isRejectButton(buttonText, rejectPatterns = REJECT_PATTERNS, neverMatchPatterns = NEVER_MATCH_PATTERNS) {
1610
+ function classifyPopup(buttons) {
1611
+ const { reject, settings, accept, acknowledge } = buttons.reduce(
1612
+ (acc, button) => {
1613
+ if (button.regexClassification && button.regexClassification !== "other") {
1614
+ acc[button.regexClassification]++;
1615
+ }
1616
+ return acc;
1617
+ },
1618
+ { reject: 0, settings: 0, accept: 0, acknowledge: 0 }
1619
+ );
1620
+ if (reject > 0) {
1621
+ return PopupHandlingModes.Reject;
1622
+ }
1623
+ if (settings > 0) {
1624
+ return PopupHandlingModes.None;
1625
+ }
1626
+ if (acknowledge > 0) {
1627
+ return PopupHandlingModes.Tier1;
1628
+ }
1629
+ if (accept > 0) {
1630
+ return accept === 1 ? PopupHandlingModes.Tier2 : PopupHandlingModes.None;
1631
+ }
1632
+ return PopupHandlingModes.None;
1633
+ }
1634
+ function testButtonMatches(buttonText, matchPatterns, neverMatchPatterns) {
1550
1635
  if (!buttonText) {
1551
1636
  return false;
1552
1637
  }
1553
1638
  const cleanedButtonText = cleanButtonText(buttonText);
1554
- return !neverMatchPatterns.some((p) => p.test(cleanedButtonText)) && rejectPatterns.some((p) => p instanceof RegExp && p.test(cleanedButtonText) || p === cleanedButtonText);
1639
+ return !neverMatchPatterns.some((p) => p instanceof RegExp && p.test(cleanedButtonText) || p === cleanedButtonText) && matchPatterns.some((p) => p instanceof RegExp && p.test(cleanedButtonText) || p === cleanedButtonText);
1555
1640
  }
1556
1641
  function cleanButtonText(buttonText) {
1557
1642
  let result = buttonText.toLowerCase();
@@ -1565,6 +1650,21 @@ function cleanButtonText(buttonText) {
1565
1650
  result = result.trim();
1566
1651
  return result;
1567
1652
  }
1653
+ function classifyButtonTextRegex(buttonText) {
1654
+ if (testButtonMatches(buttonText, REJECT_PATTERNS, NEVER_MATCH_PATTERNS)) {
1655
+ return "reject";
1656
+ }
1657
+ if (testButtonMatches(buttonText, SETTINGS_PATTERNS, NEVER_MATCH_PATTERNS)) {
1658
+ return "settings";
1659
+ }
1660
+ if (testButtonMatches(buttonText, ACCEPT_PATTERNS, NEVER_MATCH_PATTERNS)) {
1661
+ return "accept";
1662
+ }
1663
+ if (testButtonMatches(buttonText, ACKNOWLEDGE_PATTERNS, NEVER_MATCH_PATTERNS)) {
1664
+ return "acknowledge";
1665
+ }
1666
+ return "other";
1667
+ }
1568
1668
  function getPotentialPopups(timeout = POPUP_SEARCH_MAX_TIME) {
1569
1669
  const isFramed = !isTopFrame();
1570
1670
  if (isFramed && window.parent && window.parent !== window.top) {
@@ -2010,7 +2110,7 @@ var AutoConsentCMP = class extends AutoConsentCMPBase {
2010
2110
  }
2011
2111
  };
2012
2112
  var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
2013
- constructor(autoconsentInstance) {
2113
+ constructor(autoconsentInstance, mode = PopupHandlingModes.Reject) {
2014
2114
  super(autoconsentInstance);
2015
2115
  this.popups = [];
2016
2116
  this.name = "HEURISTIC";
@@ -2019,6 +2119,7 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
2019
2119
  frame: false
2020
2120
  // do not run in iframes for security reasons
2021
2121
  };
2122
+ this.mode = mode;
2022
2123
  }
2023
2124
  get hasSelfTest() {
2024
2125
  return true;
@@ -2032,25 +2133,33 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
2032
2133
  async detectCmp() {
2033
2134
  await new Promise((resolve) => setTimeout(resolve, 0));
2034
2135
  this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorStart");
2035
- this.popups = getActionablePopups(this.autoconsent.config.heuristicPopupSearchTimeout);
2136
+ this.popups = getActionablePopups(this.mode, this.autoconsent.config.heuristicPopupSearchTimeout);
2036
2137
  this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorEnd");
2037
2138
  this.autoconsent.config.performanceLoggingEnabled && performance.measure("heuristicDetector", "heuristicDetectorStart", "heuristicDetectorEnd");
2038
2139
  if (this.popups.length > 0) {
2140
+ this.name = `HEURISTIC-TIER${this.popups[0].regexClassification}`;
2039
2141
  return Promise.resolve(true);
2040
2142
  }
2041
2143
  return Promise.resolve(false);
2042
2144
  }
2043
2145
  async detectPopup() {
2044
2146
  if (this.popups.length > 0) {
2045
- if (this.popups.length > 1 || this.popups[0].rejectButtons && this.popups[0].rejectButtons.length > 1) {
2046
- this.autoconsent.config.logs.errors && console.warn("Heuristic found multiple reject buttons");
2147
+ if (this.popups.length > 1) {
2148
+ this.autoconsent.config.logs.errors && console.warn("Heuristic found multiple popups");
2047
2149
  }
2048
2150
  return true;
2049
2151
  }
2050
2152
  return false;
2051
2153
  }
2154
+ getTargetButton() {
2155
+ const popup = this.popups[0];
2156
+ const level = popup.regexClassification;
2157
+ const buttons = popup.buttons;
2158
+ const targetButtonType = level === PopupHandlingModes.Reject ? "reject" : level === PopupHandlingModes.Tier1 ? "acknowledge" : "accept";
2159
+ return buttons.find((button) => button.regexClassification === targetButtonType);
2160
+ }
2052
2161
  optOut() {
2053
- const button = this.popups[0]?.rejectButtons?.[0];
2162
+ const button = this.getTargetButton();
2054
2163
  if (button) {
2055
2164
  return this.clickElement(button.element);
2056
2165
  }
@@ -2063,7 +2172,7 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
2063
2172
  throw new Error("Not Implemented");
2064
2173
  }
2065
2174
  async test() {
2066
- const button = this.popups[0].rejectButtons?.[0];
2175
+ const button = this.getTargetButton();
2067
2176
  if (button) {
2068
2177
  await this.wait(500);
2069
2178
  return !isElementVisible(button.element);
@@ -3765,7 +3874,7 @@ var AutoConsent = class {
3765
3874
  }
3766
3875
  }
3767
3876
  });
3768
- const heuristicRules = isTop && this.config.enableHeuristicAction && this.state.findCmpAttempts % 2 === 0 ? [new AutoConsentHeuristicCMP(this)] : [];
3877
+ const heuristicRules = isTop && this.config.enableHeuristicAction && this.state.findCmpAttempts % 2 === 0 ? [new AutoConsentHeuristicCMP(this, this.config.heuristicMode)] : [];
3769
3878
  const rulesPriorityStages = [
3770
3879
  ["site-specific", siteSpecificRules],
3771
3880
  ["generic", genericRules],
@@ -405,6 +405,14 @@ async function evalAction(config) {
405
405
  });
406
406
  }
407
407
 
408
+ // lib/types.ts
409
+ var PopupHandlingModes = {
410
+ None: -1,
411
+ Reject: 0,
412
+ Tier1: 1,
413
+ Tier2: 2
414
+ };
415
+
408
416
  // lib/random.ts
409
417
  function getRandomID() {
410
418
  if (crypto && typeof crypto.randomUUID !== "undefined") {
@@ -714,7 +722,8 @@ function normalizeConfig(providedConfig) {
714
722
  waits: false
715
723
  },
716
724
  performanceLoggingEnabled: false,
717
- heuristicPopupSearchTimeout: 100
725
+ heuristicPopupSearchTimeout: 100,
726
+ heuristicMode: PopupHandlingModes.Reject
718
727
  };
719
728
  const updatedConfig = copyObject(defaultConfig);
720
729
  for (const key of Object.keys(defaultConfig)) {
@@ -881,12 +890,17 @@ var REJECT_PATTERNS_ENGLISH = [
881
890
  /^\s*(use|accept|allow|continue\s+with)?\s*only\s*(strictly)?\s*(necessary|essentials?|required)?\s*(cookies)?\s*$/is,
882
891
  // e.g. "do not sell or share my personal information", "do not sell my personal information"
883
892
  // often used in CCPA
884
- /^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*information\s*$/is,
893
+ /^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*info(rmation)?\s*$/is,
885
894
  "allow selection",
886
- "disagree and close"
895
+ "disagree and close",
887
896
  // These are impactful, but look error-prone
897
+ // // e.g. "disagree"
898
+ /^(i)?\s*disagree\s*(and\s+close)?$/i,
888
899
  // // e.g. "i do not agree"
889
900
  // /^\s*(i\s+)?do\s+not\s+agree\s*$/i,
901
+ "no",
902
+ /^no,? thanks$/is,
903
+ /^opt[ -]out$/is
890
904
  ];
891
905
  var REJECT_PATTERNS_DUTCH = [
892
906
  "weigeren",
@@ -1462,6 +1476,64 @@ var NEVER_MATCH_PATTERNS = [
1462
1476
  /sostienici/is,
1463
1477
  /suscribir/is
1464
1478
  ];
1479
+ var SETTINGS_PATTERNS = [
1480
+ "settings",
1481
+ "preferences",
1482
+ /customi(s|z)e/is,
1483
+ "show details",
1484
+ "more options",
1485
+ /(manage|configure) (my|your) (preferences|choices|cookies)/is,
1486
+ "manage choices",
1487
+ /(cookie )?preference center/is,
1488
+ "change settings",
1489
+ "configure",
1490
+ "change my preferences",
1491
+ "cookie manager",
1492
+ "cookie preference",
1493
+ "let me choose",
1494
+ "cookieconsent preferences",
1495
+ /privacy choices/is,
1496
+ /(privacy|cookie|custom) settings/is,
1497
+ /cookies? (settings|preferences|setting)/is,
1498
+ /(manage|customize|customise|opt-out|edit).*(cookies|preferences|settings|options)/is,
1499
+ "cookie consent options",
1500
+ "privacy controls",
1501
+ "show purposes",
1502
+ // German
1503
+ "einstellungen"
1504
+ ];
1505
+ var ACCEPT_PATTERNS = [
1506
+ /^(accept|allow)( all)?( cookies)?$/is,
1507
+ /i (accept|allow)( all)?/is,
1508
+ "yes",
1509
+ /^(i )?agree$/is,
1510
+ "continue with all",
1511
+ "accept and continue",
1512
+ /accept all above/is,
1513
+ /^(accept|agree) and close/is,
1514
+ "accept continue",
1515
+ "agree proceed",
1516
+ "allow and continue",
1517
+ "close and accept",
1518
+ /accept all$/is,
1519
+ "im ok with that",
1520
+ "accept optional cookies",
1521
+ /^alle (cookies )?akzeptieren$/is
1522
+ ];
1523
+ var ACKNOWLEDGE_PATTERNS = [
1524
+ "ok",
1525
+ "close",
1526
+ "continue",
1527
+ "x",
1528
+ /^got it!?$/,
1529
+ "i understand",
1530
+ "dismiss",
1531
+ "okay",
1532
+ "acknowledge",
1533
+ /^close (banner|cookie notification)$/is,
1534
+ /understood$/is,
1535
+ "confirm my choices"
1536
+ ];
1465
1537
 
1466
1538
  // lib/heuristics.ts
1467
1539
  var BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
@@ -1480,48 +1552,61 @@ function checkHeuristicPatterns(allText, detectPatterns = DETECT_PATTERNS) {
1480
1552
  }
1481
1553
  return { patterns, snippets: snippets2 };
1482
1554
  }
1483
- function getActionablePopups(timeout = POPUP_SEARCH_MAX_TIME) {
1555
+ function getActionablePopups(mode = PopupHandlingModes.Reject, timeout = POPUP_SEARCH_MAX_TIME) {
1484
1556
  const popups = getPotentialPopups(timeout);
1485
1557
  const result = popups.reduce((acc, popup) => {
1486
1558
  const popupText = popup.text?.trim();
1487
1559
  if (popupText) {
1488
1560
  const { patterns } = checkHeuristicPatterns(popupText);
1489
1561
  if (patterns.length > 0) {
1490
- const { rejectButtons, otherButtons } = classifyButtons(popup.buttons);
1491
- if (rejectButtons.length > 0) {
1492
- acc.push({
1493
- ...popup,
1494
- rejectButtons,
1495
- otherButtons
1496
- });
1497
- }
1562
+ classifyButtons(popup.buttons);
1563
+ popup.regexClassification = classifyPopup(popup.buttons);
1564
+ acc.push({
1565
+ ...popup
1566
+ });
1498
1567
  }
1499
1568
  }
1500
1569
  return acc;
1501
1570
  }, []);
1502
- return result;
1571
+ return result.filter(
1572
+ (popup) => popup.regexClassification !== void 0 && popup.regexClassification !== PopupHandlingModes.None && popup.regexClassification <= mode
1573
+ ).sort((a, b) => (a.regexClassification ?? 0) - (b.regexClassification ?? 0));
1503
1574
  }
1504
1575
  function classifyButtons(buttons) {
1505
- const rejectButtons = [];
1506
- const otherButtons = [];
1507
1576
  for (const button of buttons) {
1508
- if (isRejectButton(button.text)) {
1509
- rejectButtons.push(button);
1510
- } else {
1511
- otherButtons.push(button);
1512
- }
1577
+ button.regexClassification = classifyButtonTextRegex(button.text);
1513
1578
  }
1514
- return {
1515
- rejectButtons,
1516
- otherButtons
1517
- };
1518
1579
  }
1519
- function isRejectButton(buttonText, rejectPatterns = REJECT_PATTERNS, neverMatchPatterns = NEVER_MATCH_PATTERNS) {
1580
+ function classifyPopup(buttons) {
1581
+ const { reject, settings, accept, acknowledge } = buttons.reduce(
1582
+ (acc, button) => {
1583
+ if (button.regexClassification && button.regexClassification !== "other") {
1584
+ acc[button.regexClassification]++;
1585
+ }
1586
+ return acc;
1587
+ },
1588
+ { reject: 0, settings: 0, accept: 0, acknowledge: 0 }
1589
+ );
1590
+ if (reject > 0) {
1591
+ return PopupHandlingModes.Reject;
1592
+ }
1593
+ if (settings > 0) {
1594
+ return PopupHandlingModes.None;
1595
+ }
1596
+ if (acknowledge > 0) {
1597
+ return PopupHandlingModes.Tier1;
1598
+ }
1599
+ if (accept > 0) {
1600
+ return accept === 1 ? PopupHandlingModes.Tier2 : PopupHandlingModes.None;
1601
+ }
1602
+ return PopupHandlingModes.None;
1603
+ }
1604
+ function testButtonMatches(buttonText, matchPatterns, neverMatchPatterns) {
1520
1605
  if (!buttonText) {
1521
1606
  return false;
1522
1607
  }
1523
1608
  const cleanedButtonText = cleanButtonText(buttonText);
1524
- return !neverMatchPatterns.some((p) => p.test(cleanedButtonText)) && rejectPatterns.some((p) => p instanceof RegExp && p.test(cleanedButtonText) || p === cleanedButtonText);
1609
+ return !neverMatchPatterns.some((p) => p instanceof RegExp && p.test(cleanedButtonText) || p === cleanedButtonText) && matchPatterns.some((p) => p instanceof RegExp && p.test(cleanedButtonText) || p === cleanedButtonText);
1525
1610
  }
1526
1611
  function cleanButtonText(buttonText) {
1527
1612
  let result = buttonText.toLowerCase();
@@ -1535,6 +1620,21 @@ function cleanButtonText(buttonText) {
1535
1620
  result = result.trim();
1536
1621
  return result;
1537
1622
  }
1623
+ function classifyButtonTextRegex(buttonText) {
1624
+ if (testButtonMatches(buttonText, REJECT_PATTERNS, NEVER_MATCH_PATTERNS)) {
1625
+ return "reject";
1626
+ }
1627
+ if (testButtonMatches(buttonText, SETTINGS_PATTERNS, NEVER_MATCH_PATTERNS)) {
1628
+ return "settings";
1629
+ }
1630
+ if (testButtonMatches(buttonText, ACCEPT_PATTERNS, NEVER_MATCH_PATTERNS)) {
1631
+ return "accept";
1632
+ }
1633
+ if (testButtonMatches(buttonText, ACKNOWLEDGE_PATTERNS, NEVER_MATCH_PATTERNS)) {
1634
+ return "acknowledge";
1635
+ }
1636
+ return "other";
1637
+ }
1538
1638
  function getPotentialPopups(timeout = POPUP_SEARCH_MAX_TIME) {
1539
1639
  const isFramed = !isTopFrame();
1540
1640
  if (isFramed && window.parent && window.parent !== window.top) {
@@ -1980,7 +2080,7 @@ var AutoConsentCMP = class extends AutoConsentCMPBase {
1980
2080
  }
1981
2081
  };
1982
2082
  var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
1983
- constructor(autoconsentInstance) {
2083
+ constructor(autoconsentInstance, mode = PopupHandlingModes.Reject) {
1984
2084
  super(autoconsentInstance);
1985
2085
  this.popups = [];
1986
2086
  this.name = "HEURISTIC";
@@ -1989,6 +2089,7 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
1989
2089
  frame: false
1990
2090
  // do not run in iframes for security reasons
1991
2091
  };
2092
+ this.mode = mode;
1992
2093
  }
1993
2094
  get hasSelfTest() {
1994
2095
  return true;
@@ -2002,25 +2103,33 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
2002
2103
  async detectCmp() {
2003
2104
  await new Promise((resolve) => setTimeout(resolve, 0));
2004
2105
  this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorStart");
2005
- this.popups = getActionablePopups(this.autoconsent.config.heuristicPopupSearchTimeout);
2106
+ this.popups = getActionablePopups(this.mode, this.autoconsent.config.heuristicPopupSearchTimeout);
2006
2107
  this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorEnd");
2007
2108
  this.autoconsent.config.performanceLoggingEnabled && performance.measure("heuristicDetector", "heuristicDetectorStart", "heuristicDetectorEnd");
2008
2109
  if (this.popups.length > 0) {
2110
+ this.name = `HEURISTIC-TIER${this.popups[0].regexClassification}`;
2009
2111
  return Promise.resolve(true);
2010
2112
  }
2011
2113
  return Promise.resolve(false);
2012
2114
  }
2013
2115
  async detectPopup() {
2014
2116
  if (this.popups.length > 0) {
2015
- if (this.popups.length > 1 || this.popups[0].rejectButtons && this.popups[0].rejectButtons.length > 1) {
2016
- this.autoconsent.config.logs.errors && console.warn("Heuristic found multiple reject buttons");
2117
+ if (this.popups.length > 1) {
2118
+ this.autoconsent.config.logs.errors && console.warn("Heuristic found multiple popups");
2017
2119
  }
2018
2120
  return true;
2019
2121
  }
2020
2122
  return false;
2021
2123
  }
2124
+ getTargetButton() {
2125
+ const popup = this.popups[0];
2126
+ const level = popup.regexClassification;
2127
+ const buttons = popup.buttons;
2128
+ const targetButtonType = level === PopupHandlingModes.Reject ? "reject" : level === PopupHandlingModes.Tier1 ? "acknowledge" : "accept";
2129
+ return buttons.find((button) => button.regexClassification === targetButtonType);
2130
+ }
2022
2131
  optOut() {
2023
- const button = this.popups[0]?.rejectButtons?.[0];
2132
+ const button = this.getTargetButton();
2024
2133
  if (button) {
2025
2134
  return this.clickElement(button.element);
2026
2135
  }
@@ -2033,7 +2142,7 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
2033
2142
  throw new Error("Not Implemented");
2034
2143
  }
2035
2144
  async test() {
2036
- const button = this.popups[0].rejectButtons?.[0];
2145
+ const button = this.getTargetButton();
2037
2146
  if (button) {
2038
2147
  await this.wait(500);
2039
2148
  return !isElementVisible(button.element);
@@ -3735,7 +3844,7 @@ var AutoConsent = class {
3735
3844
  }
3736
3845
  }
3737
3846
  });
3738
- const heuristicRules = isTop && this.config.enableHeuristicAction && this.state.findCmpAttempts % 2 === 0 ? [new AutoConsentHeuristicCMP(this)] : [];
3847
+ const heuristicRules = isTop && this.config.enableHeuristicAction && this.state.findCmpAttempts % 2 === 0 ? [new AutoConsentHeuristicCMP(this, this.config.heuristicMode)] : [];
3739
3848
  const rulesPriorityStages = [
3740
3849
  ["site-specific", siteSpecificRules],
3741
3850
  ["generic", genericRules],