@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
@@ -11483,6 +11483,14 @@ function extractFeaturesFromDOM(roots = [document.documentElement]) {
11483
11483
  // lib/filterlist-utils.ts
11484
11484
  var import_tldts_experimental3 = __toESM(require_cjs());
11485
11485
 
11486
+ // lib/types.ts
11487
+ var PopupHandlingModes = {
11488
+ None: -1,
11489
+ Reject: 0,
11490
+ Tier1: 1,
11491
+ Tier2: 2
11492
+ };
11493
+
11486
11494
  // lib/utils.ts
11487
11495
  function getStyleElement(styleOverrideElementId = "autoconsent-css-rules") {
11488
11496
  const styleSelector = `style#${styleOverrideElementId}`;
@@ -11567,7 +11575,8 @@ function normalizeConfig(providedConfig) {
11567
11575
  waits: false
11568
11576
  },
11569
11577
  performanceLoggingEnabled: false,
11570
- heuristicPopupSearchTimeout: 100
11578
+ heuristicPopupSearchTimeout: 100,
11579
+ heuristicMode: PopupHandlingModes.Reject
11571
11580
  };
11572
11581
  const updatedConfig = copyObject(defaultConfig);
11573
11582
  for (const key of Object.keys(defaultConfig)) {
@@ -12401,12 +12410,17 @@ var REJECT_PATTERNS_ENGLISH = [
12401
12410
  /^\s*(use|accept|allow|continue\s+with)?\s*only\s*(strictly)?\s*(necessary|essentials?|required)?\s*(cookies)?\s*$/is,
12402
12411
  // e.g. "do not sell or share my personal information", "do not sell my personal information"
12403
12412
  // often used in CCPA
12404
- /^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*information\s*$/is,
12413
+ /^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*info(rmation)?\s*$/is,
12405
12414
  "allow selection",
12406
- "disagree and close"
12415
+ "disagree and close",
12407
12416
  // These are impactful, but look error-prone
12417
+ // // e.g. "disagree"
12418
+ /^(i)?\s*disagree\s*(and\s+close)?$/i,
12408
12419
  // // e.g. "i do not agree"
12409
12420
  // /^\s*(i\s+)?do\s+not\s+agree\s*$/i,
12421
+ "no",
12422
+ /^no,? thanks$/is,
12423
+ /^opt[ -]out$/is
12410
12424
  ];
12411
12425
  var REJECT_PATTERNS_DUTCH = [
12412
12426
  "weigeren",
@@ -12982,6 +12996,64 @@ var NEVER_MATCH_PATTERNS = [
12982
12996
  /sostienici/is,
12983
12997
  /suscribir/is
12984
12998
  ];
12999
+ var SETTINGS_PATTERNS = [
13000
+ "settings",
13001
+ "preferences",
13002
+ /customi(s|z)e/is,
13003
+ "show details",
13004
+ "more options",
13005
+ /(manage|configure) (my|your) (preferences|choices|cookies)/is,
13006
+ "manage choices",
13007
+ /(cookie )?preference center/is,
13008
+ "change settings",
13009
+ "configure",
13010
+ "change my preferences",
13011
+ "cookie manager",
13012
+ "cookie preference",
13013
+ "let me choose",
13014
+ "cookieconsent preferences",
13015
+ /privacy choices/is,
13016
+ /(privacy|cookie|custom) settings/is,
13017
+ /cookies? (settings|preferences|setting)/is,
13018
+ /(manage|customize|customise|opt-out|edit).*(cookies|preferences|settings|options)/is,
13019
+ "cookie consent options",
13020
+ "privacy controls",
13021
+ "show purposes",
13022
+ // German
13023
+ "einstellungen"
13024
+ ];
13025
+ var ACCEPT_PATTERNS = [
13026
+ /^(accept|allow)( all)?( cookies)?$/is,
13027
+ /i (accept|allow)( all)?/is,
13028
+ "yes",
13029
+ /^(i )?agree$/is,
13030
+ "continue with all",
13031
+ "accept and continue",
13032
+ /accept all above/is,
13033
+ /^(accept|agree) and close/is,
13034
+ "accept continue",
13035
+ "agree proceed",
13036
+ "allow and continue",
13037
+ "close and accept",
13038
+ /accept all$/is,
13039
+ "im ok with that",
13040
+ "accept optional cookies",
13041
+ /^alle (cookies )?akzeptieren$/is
13042
+ ];
13043
+ var ACKNOWLEDGE_PATTERNS = [
13044
+ "ok",
13045
+ "close",
13046
+ "continue",
13047
+ "x",
13048
+ /^got it!?$/,
13049
+ "i understand",
13050
+ "dismiss",
13051
+ "okay",
13052
+ "acknowledge",
13053
+ /^close (banner|cookie notification)$/is,
13054
+ /understood$/is,
13055
+ "confirm my choices"
13056
+ ];
12985
13057
 
12986
13058
  // lib/heuristics.ts
12987
13059
  var BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
@@ -13000,48 +13072,61 @@ function checkHeuristicPatterns(allText, detectPatterns = DETECT_PATTERNS) {
13000
13072
  }
13001
13073
  return { patterns, snippets: snippets2 };
13002
13074
  }
13003
- function getActionablePopups(timeout = POPUP_SEARCH_MAX_TIME) {
13075
+ function getActionablePopups(mode = PopupHandlingModes.Reject, timeout = POPUP_SEARCH_MAX_TIME) {
13004
13076
  const popups = getPotentialPopups(timeout);
13005
13077
  const result = popups.reduce((acc, popup) => {
13006
13078
  const popupText = popup.text?.trim();
13007
13079
  if (popupText) {
13008
13080
  const { patterns } = checkHeuristicPatterns(popupText);
13009
13081
  if (patterns.length > 0) {
13010
- const { rejectButtons, otherButtons } = classifyButtons(popup.buttons);
13011
- if (rejectButtons.length > 0) {
13012
- acc.push({
13013
- ...popup,
13014
- rejectButtons,
13015
- otherButtons
13016
- });
13017
- }
13082
+ classifyButtons(popup.buttons);
13083
+ popup.regexClassification = classifyPopup(popup.buttons);
13084
+ acc.push({
13085
+ ...popup
13086
+ });
13018
13087
  }
13019
13088
  }
13020
13089
  return acc;
13021
13090
  }, []);
13022
- return result;
13091
+ return result.filter(
13092
+ (popup) => popup.regexClassification !== void 0 && popup.regexClassification !== PopupHandlingModes.None && popup.regexClassification <= mode
13093
+ ).sort((a, b) => (a.regexClassification ?? 0) - (b.regexClassification ?? 0));
13023
13094
  }
13024
13095
  function classifyButtons(buttons) {
13025
- const rejectButtons = [];
13026
- const otherButtons = [];
13027
13096
  for (const button of buttons) {
13028
- if (isRejectButton(button.text)) {
13029
- rejectButtons.push(button);
13030
- } else {
13031
- otherButtons.push(button);
13032
- }
13097
+ button.regexClassification = classifyButtonTextRegex(button.text);
13033
13098
  }
13034
- return {
13035
- rejectButtons,
13036
- otherButtons
13037
- };
13038
13099
  }
13039
- function isRejectButton(buttonText, rejectPatterns = REJECT_PATTERNS, neverMatchPatterns = NEVER_MATCH_PATTERNS) {
13100
+ function classifyPopup(buttons) {
13101
+ const { reject, settings, accept, acknowledge } = buttons.reduce(
13102
+ (acc, button) => {
13103
+ if (button.regexClassification && button.regexClassification !== "other") {
13104
+ acc[button.regexClassification]++;
13105
+ }
13106
+ return acc;
13107
+ },
13108
+ { reject: 0, settings: 0, accept: 0, acknowledge: 0 }
13109
+ );
13110
+ if (reject > 0) {
13111
+ return PopupHandlingModes.Reject;
13112
+ }
13113
+ if (settings > 0) {
13114
+ return PopupHandlingModes.None;
13115
+ }
13116
+ if (acknowledge > 0) {
13117
+ return PopupHandlingModes.Tier1;
13118
+ }
13119
+ if (accept > 0) {
13120
+ return accept === 1 ? PopupHandlingModes.Tier2 : PopupHandlingModes.None;
13121
+ }
13122
+ return PopupHandlingModes.None;
13123
+ }
13124
+ function testButtonMatches(buttonText, matchPatterns, neverMatchPatterns) {
13040
13125
  if (!buttonText) {
13041
13126
  return false;
13042
13127
  }
13043
13128
  const cleanedButtonText = cleanButtonText(buttonText);
13044
- return !neverMatchPatterns.some((p) => p.test(cleanedButtonText)) && rejectPatterns.some((p) => p instanceof RegExp && p.test(cleanedButtonText) || p === cleanedButtonText);
13129
+ return !neverMatchPatterns.some((p) => p instanceof RegExp && p.test(cleanedButtonText) || p === cleanedButtonText) && matchPatterns.some((p) => p instanceof RegExp && p.test(cleanedButtonText) || p === cleanedButtonText);
13045
13130
  }
13046
13131
  function cleanButtonText(buttonText) {
13047
13132
  let result = buttonText.toLowerCase();
@@ -13055,6 +13140,21 @@ function cleanButtonText(buttonText) {
13055
13140
  result = result.trim();
13056
13141
  return result;
13057
13142
  }
13143
+ function classifyButtonTextRegex(buttonText) {
13144
+ if (testButtonMatches(buttonText, REJECT_PATTERNS, NEVER_MATCH_PATTERNS)) {
13145
+ return "reject";
13146
+ }
13147
+ if (testButtonMatches(buttonText, SETTINGS_PATTERNS, NEVER_MATCH_PATTERNS)) {
13148
+ return "settings";
13149
+ }
13150
+ if (testButtonMatches(buttonText, ACCEPT_PATTERNS, NEVER_MATCH_PATTERNS)) {
13151
+ return "accept";
13152
+ }
13153
+ if (testButtonMatches(buttonText, ACKNOWLEDGE_PATTERNS, NEVER_MATCH_PATTERNS)) {
13154
+ return "acknowledge";
13155
+ }
13156
+ return "other";
13157
+ }
13058
13158
  function getPotentialPopups(timeout = POPUP_SEARCH_MAX_TIME) {
13059
13159
  const isFramed = !isTopFrame();
13060
13160
  if (isFramed && window.parent && window.parent !== window.top) {
@@ -13500,7 +13600,7 @@ var AutoConsentCMP = class extends AutoConsentCMPBase {
13500
13600
  }
13501
13601
  };
13502
13602
  var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
13503
- constructor(autoconsentInstance) {
13603
+ constructor(autoconsentInstance, mode = PopupHandlingModes.Reject) {
13504
13604
  super(autoconsentInstance);
13505
13605
  this.popups = [];
13506
13606
  this.name = "HEURISTIC";
@@ -13509,6 +13609,7 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
13509
13609
  frame: false
13510
13610
  // do not run in iframes for security reasons
13511
13611
  };
13612
+ this.mode = mode;
13512
13613
  }
13513
13614
  get hasSelfTest() {
13514
13615
  return true;
@@ -13522,25 +13623,33 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
13522
13623
  async detectCmp() {
13523
13624
  await new Promise((resolve) => setTimeout(resolve, 0));
13524
13625
  this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorStart");
13525
- this.popups = getActionablePopups(this.autoconsent.config.heuristicPopupSearchTimeout);
13626
+ this.popups = getActionablePopups(this.mode, this.autoconsent.config.heuristicPopupSearchTimeout);
13526
13627
  this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorEnd");
13527
13628
  this.autoconsent.config.performanceLoggingEnabled && performance.measure("heuristicDetector", "heuristicDetectorStart", "heuristicDetectorEnd");
13528
13629
  if (this.popups.length > 0) {
13630
+ this.name = `HEURISTIC-TIER${this.popups[0].regexClassification}`;
13529
13631
  return Promise.resolve(true);
13530
13632
  }
13531
13633
  return Promise.resolve(false);
13532
13634
  }
13533
13635
  async detectPopup() {
13534
13636
  if (this.popups.length > 0) {
13535
- if (this.popups.length > 1 || this.popups[0].rejectButtons && this.popups[0].rejectButtons.length > 1) {
13536
- this.autoconsent.config.logs.errors && console.warn("Heuristic found multiple reject buttons");
13637
+ if (this.popups.length > 1) {
13638
+ this.autoconsent.config.logs.errors && console.warn("Heuristic found multiple popups");
13537
13639
  }
13538
13640
  return true;
13539
13641
  }
13540
13642
  return false;
13541
13643
  }
13644
+ getTargetButton() {
13645
+ const popup = this.popups[0];
13646
+ const level = popup.regexClassification;
13647
+ const buttons = popup.buttons;
13648
+ const targetButtonType = level === PopupHandlingModes.Reject ? "reject" : level === PopupHandlingModes.Tier1 ? "acknowledge" : "accept";
13649
+ return buttons.find((button) => button.regexClassification === targetButtonType);
13650
+ }
13542
13651
  optOut() {
13543
- const button = this.popups[0]?.rejectButtons?.[0];
13652
+ const button = this.getTargetButton();
13544
13653
  if (button) {
13545
13654
  return this.clickElement(button.element);
13546
13655
  }
@@ -13553,7 +13662,7 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
13553
13662
  throw new Error("Not Implemented");
13554
13663
  }
13555
13664
  async test() {
13556
- const button = this.popups[0].rejectButtons?.[0];
13665
+ const button = this.getTargetButton();
13557
13666
  if (button) {
13558
13667
  await this.wait(500);
13559
13668
  return !isElementVisible(button.element);
@@ -15023,7 +15132,7 @@ var AutoConsent = class {
15023
15132
  }
15024
15133
  }
15025
15134
  });
15026
- const heuristicRules = isTop && this.config.enableHeuristicAction && this.state.findCmpAttempts % 2 === 0 ? [new AutoConsentHeuristicCMP(this)] : [];
15135
+ const heuristicRules = isTop && this.config.enableHeuristicAction && this.state.findCmpAttempts % 2 === 0 ? [new AutoConsentHeuristicCMP(this, this.config.heuristicMode)] : [];
15027
15136
  const rulesPriorityStages = [
15028
15137
  ["site-specific", siteSpecificRules],
15029
15138
  ["generic", genericRules],
@@ -11412,6 +11412,14 @@ function extractFeaturesFromDOM(roots = [document.documentElement]) {
11412
11412
  };
11413
11413
  }
11414
11414
 
11415
+ // lib/types.ts
11416
+ var PopupHandlingModes = {
11417
+ None: -1,
11418
+ Reject: 0,
11419
+ Tier1: 1,
11420
+ Tier2: 2
11421
+ };
11422
+
11415
11423
  // lib/utils.ts
11416
11424
  function getStyleElement(styleOverrideElementId = "autoconsent-css-rules") {
11417
11425
  const styleSelector = `style#${styleOverrideElementId}`;
@@ -11496,7 +11504,8 @@ function normalizeConfig(providedConfig) {
11496
11504
  waits: false
11497
11505
  },
11498
11506
  performanceLoggingEnabled: false,
11499
- heuristicPopupSearchTimeout: 100
11507
+ heuristicPopupSearchTimeout: 100,
11508
+ heuristicMode: PopupHandlingModes.Reject
11500
11509
  };
11501
11510
  const updatedConfig = copyObject(defaultConfig);
11502
11511
  for (const key of Object.keys(defaultConfig)) {
@@ -12330,12 +12339,17 @@ var REJECT_PATTERNS_ENGLISH = [
12330
12339
  /^\s*(use|accept|allow|continue\s+with)?\s*only\s*(strictly)?\s*(necessary|essentials?|required)?\s*(cookies)?\s*$/is,
12331
12340
  // e.g. "do not sell or share my personal information", "do not sell my personal information"
12332
12341
  // often used in CCPA
12333
- /^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*information\s*$/is,
12342
+ /^\s*do\s+not\s+sell(\s+or\s+share)?\s*my\s*personal\s*info(rmation)?\s*$/is,
12334
12343
  "allow selection",
12335
- "disagree and close"
12344
+ "disagree and close",
12336
12345
  // These are impactful, but look error-prone
12346
+ // // e.g. "disagree"
12347
+ /^(i)?\s*disagree\s*(and\s+close)?$/i,
12337
12348
  // // e.g. "i do not agree"
12338
12349
  // /^\s*(i\s+)?do\s+not\s+agree\s*$/i,
12350
+ "no",
12351
+ /^no,? thanks$/is,
12352
+ /^opt[ -]out$/is
12339
12353
  ];
12340
12354
  var REJECT_PATTERNS_DUTCH = [
12341
12355
  "weigeren",
@@ -12911,6 +12925,64 @@ var NEVER_MATCH_PATTERNS = [
12911
12925
  /sostienici/is,
12912
12926
  /suscribir/is
12913
12927
  ];
12928
+ var SETTINGS_PATTERNS = [
12929
+ "settings",
12930
+ "preferences",
12931
+ /customi(s|z)e/is,
12932
+ "show details",
12933
+ "more options",
12934
+ /(manage|configure) (my|your) (preferences|choices|cookies)/is,
12935
+ "manage choices",
12936
+ /(cookie )?preference center/is,
12937
+ "change settings",
12938
+ "configure",
12939
+ "change my preferences",
12940
+ "cookie manager",
12941
+ "cookie preference",
12942
+ "let me choose",
12943
+ "cookieconsent preferences",
12944
+ /privacy choices/is,
12945
+ /(privacy|cookie|custom) settings/is,
12946
+ /cookies? (settings|preferences|setting)/is,
12947
+ /(manage|customize|customise|opt-out|edit).*(cookies|preferences|settings|options)/is,
12948
+ "cookie consent options",
12949
+ "privacy controls",
12950
+ "show purposes",
12951
+ // German
12952
+ "einstellungen"
12953
+ ];
12954
+ var ACCEPT_PATTERNS = [
12955
+ /^(accept|allow)( all)?( cookies)?$/is,
12956
+ /i (accept|allow)( all)?/is,
12957
+ "yes",
12958
+ /^(i )?agree$/is,
12959
+ "continue with all",
12960
+ "accept and continue",
12961
+ /accept all above/is,
12962
+ /^(accept|agree) and close/is,
12963
+ "accept continue",
12964
+ "agree proceed",
12965
+ "allow and continue",
12966
+ "close and accept",
12967
+ /accept all$/is,
12968
+ "im ok with that",
12969
+ "accept optional cookies",
12970
+ /^alle (cookies )?akzeptieren$/is
12971
+ ];
12972
+ var ACKNOWLEDGE_PATTERNS = [
12973
+ "ok",
12974
+ "close",
12975
+ "continue",
12976
+ "x",
12977
+ /^got it!?$/,
12978
+ "i understand",
12979
+ "dismiss",
12980
+ "okay",
12981
+ "acknowledge",
12982
+ /^close (banner|cookie notification)$/is,
12983
+ /understood$/is,
12984
+ "confirm my choices"
12985
+ ];
12914
12986
 
12915
12987
  // lib/heuristics.ts
12916
12988
  var BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
@@ -12929,48 +13001,61 @@ function checkHeuristicPatterns(allText, detectPatterns = DETECT_PATTERNS) {
12929
13001
  }
12930
13002
  return { patterns, snippets: snippets2 };
12931
13003
  }
12932
- function getActionablePopups(timeout = POPUP_SEARCH_MAX_TIME) {
13004
+ function getActionablePopups(mode = PopupHandlingModes.Reject, timeout = POPUP_SEARCH_MAX_TIME) {
12933
13005
  const popups = getPotentialPopups(timeout);
12934
13006
  const result = popups.reduce((acc, popup) => {
12935
13007
  const popupText = popup.text?.trim();
12936
13008
  if (popupText) {
12937
13009
  const { patterns } = checkHeuristicPatterns(popupText);
12938
13010
  if (patterns.length > 0) {
12939
- const { rejectButtons, otherButtons } = classifyButtons(popup.buttons);
12940
- if (rejectButtons.length > 0) {
12941
- acc.push({
12942
- ...popup,
12943
- rejectButtons,
12944
- otherButtons
12945
- });
12946
- }
13011
+ classifyButtons(popup.buttons);
13012
+ popup.regexClassification = classifyPopup(popup.buttons);
13013
+ acc.push({
13014
+ ...popup
13015
+ });
12947
13016
  }
12948
13017
  }
12949
13018
  return acc;
12950
13019
  }, []);
12951
- return result;
13020
+ return result.filter(
13021
+ (popup) => popup.regexClassification !== void 0 && popup.regexClassification !== PopupHandlingModes.None && popup.regexClassification <= mode
13022
+ ).sort((a, b) => (a.regexClassification ?? 0) - (b.regexClassification ?? 0));
12952
13023
  }
12953
13024
  function classifyButtons(buttons) {
12954
- const rejectButtons = [];
12955
- const otherButtons = [];
12956
13025
  for (const button of buttons) {
12957
- if (isRejectButton(button.text)) {
12958
- rejectButtons.push(button);
12959
- } else {
12960
- otherButtons.push(button);
12961
- }
13026
+ button.regexClassification = classifyButtonTextRegex(button.text);
12962
13027
  }
12963
- return {
12964
- rejectButtons,
12965
- otherButtons
12966
- };
12967
13028
  }
12968
- function isRejectButton(buttonText, rejectPatterns = REJECT_PATTERNS, neverMatchPatterns = NEVER_MATCH_PATTERNS) {
13029
+ function classifyPopup(buttons) {
13030
+ const { reject, settings, accept, acknowledge } = buttons.reduce(
13031
+ (acc, button) => {
13032
+ if (button.regexClassification && button.regexClassification !== "other") {
13033
+ acc[button.regexClassification]++;
13034
+ }
13035
+ return acc;
13036
+ },
13037
+ { reject: 0, settings: 0, accept: 0, acknowledge: 0 }
13038
+ );
13039
+ if (reject > 0) {
13040
+ return PopupHandlingModes.Reject;
13041
+ }
13042
+ if (settings > 0) {
13043
+ return PopupHandlingModes.None;
13044
+ }
13045
+ if (acknowledge > 0) {
13046
+ return PopupHandlingModes.Tier1;
13047
+ }
13048
+ if (accept > 0) {
13049
+ return accept === 1 ? PopupHandlingModes.Tier2 : PopupHandlingModes.None;
13050
+ }
13051
+ return PopupHandlingModes.None;
13052
+ }
13053
+ function testButtonMatches(buttonText, matchPatterns, neverMatchPatterns) {
12969
13054
  if (!buttonText) {
12970
13055
  return false;
12971
13056
  }
12972
13057
  const cleanedButtonText = cleanButtonText(buttonText);
12973
- return !neverMatchPatterns.some((p) => p.test(cleanedButtonText)) && rejectPatterns.some((p) => p instanceof RegExp && p.test(cleanedButtonText) || p === cleanedButtonText);
13058
+ return !neverMatchPatterns.some((p) => p instanceof RegExp && p.test(cleanedButtonText) || p === cleanedButtonText) && matchPatterns.some((p) => p instanceof RegExp && p.test(cleanedButtonText) || p === cleanedButtonText);
12974
13059
  }
12975
13060
  function cleanButtonText(buttonText) {
12976
13061
  let result = buttonText.toLowerCase();
@@ -12984,6 +13069,21 @@ function cleanButtonText(buttonText) {
12984
13069
  result = result.trim();
12985
13070
  return result;
12986
13071
  }
13072
+ function classifyButtonTextRegex(buttonText) {
13073
+ if (testButtonMatches(buttonText, REJECT_PATTERNS, NEVER_MATCH_PATTERNS)) {
13074
+ return "reject";
13075
+ }
13076
+ if (testButtonMatches(buttonText, SETTINGS_PATTERNS, NEVER_MATCH_PATTERNS)) {
13077
+ return "settings";
13078
+ }
13079
+ if (testButtonMatches(buttonText, ACCEPT_PATTERNS, NEVER_MATCH_PATTERNS)) {
13080
+ return "accept";
13081
+ }
13082
+ if (testButtonMatches(buttonText, ACKNOWLEDGE_PATTERNS, NEVER_MATCH_PATTERNS)) {
13083
+ return "acknowledge";
13084
+ }
13085
+ return "other";
13086
+ }
12987
13087
  function getPotentialPopups(timeout = POPUP_SEARCH_MAX_TIME) {
12988
13088
  const isFramed = !isTopFrame();
12989
13089
  if (isFramed && window.parent && window.parent !== window.top) {
@@ -13429,7 +13529,7 @@ var AutoConsentCMP = class extends AutoConsentCMPBase {
13429
13529
  }
13430
13530
  };
13431
13531
  var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
13432
- constructor(autoconsentInstance) {
13532
+ constructor(autoconsentInstance, mode = PopupHandlingModes.Reject) {
13433
13533
  super(autoconsentInstance);
13434
13534
  this.popups = [];
13435
13535
  this.name = "HEURISTIC";
@@ -13438,6 +13538,7 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
13438
13538
  frame: false
13439
13539
  // do not run in iframes for security reasons
13440
13540
  };
13541
+ this.mode = mode;
13441
13542
  }
13442
13543
  get hasSelfTest() {
13443
13544
  return true;
@@ -13451,25 +13552,33 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
13451
13552
  async detectCmp() {
13452
13553
  await new Promise((resolve) => setTimeout(resolve, 0));
13453
13554
  this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorStart");
13454
- this.popups = getActionablePopups(this.autoconsent.config.heuristicPopupSearchTimeout);
13555
+ this.popups = getActionablePopups(this.mode, this.autoconsent.config.heuristicPopupSearchTimeout);
13455
13556
  this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorEnd");
13456
13557
  this.autoconsent.config.performanceLoggingEnabled && performance.measure("heuristicDetector", "heuristicDetectorStart", "heuristicDetectorEnd");
13457
13558
  if (this.popups.length > 0) {
13559
+ this.name = `HEURISTIC-TIER${this.popups[0].regexClassification}`;
13458
13560
  return Promise.resolve(true);
13459
13561
  }
13460
13562
  return Promise.resolve(false);
13461
13563
  }
13462
13564
  async detectPopup() {
13463
13565
  if (this.popups.length > 0) {
13464
- if (this.popups.length > 1 || this.popups[0].rejectButtons && this.popups[0].rejectButtons.length > 1) {
13465
- this.autoconsent.config.logs.errors && console.warn("Heuristic found multiple reject buttons");
13566
+ if (this.popups.length > 1) {
13567
+ this.autoconsent.config.logs.errors && console.warn("Heuristic found multiple popups");
13466
13568
  }
13467
13569
  return true;
13468
13570
  }
13469
13571
  return false;
13470
13572
  }
13573
+ getTargetButton() {
13574
+ const popup = this.popups[0];
13575
+ const level = popup.regexClassification;
13576
+ const buttons = popup.buttons;
13577
+ const targetButtonType = level === PopupHandlingModes.Reject ? "reject" : level === PopupHandlingModes.Tier1 ? "acknowledge" : "accept";
13578
+ return buttons.find((button) => button.regexClassification === targetButtonType);
13579
+ }
13471
13580
  optOut() {
13472
- const button = this.popups[0]?.rejectButtons?.[0];
13581
+ const button = this.getTargetButton();
13473
13582
  if (button) {
13474
13583
  return this.clickElement(button.element);
13475
13584
  }
@@ -13482,7 +13591,7 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
13482
13591
  throw new Error("Not Implemented");
13483
13592
  }
13484
13593
  async test() {
13485
- const button = this.popups[0].rejectButtons?.[0];
13594
+ const button = this.getTargetButton();
13486
13595
  if (button) {
13487
13596
  await this.wait(500);
13488
13597
  return !isElementVisible(button.element);
@@ -14952,7 +15061,7 @@ var AutoConsent = class {
14952
15061
  }
14953
15062
  }
14954
15063
  });
14955
- const heuristicRules = isTop && this.config.enableHeuristicAction && this.state.findCmpAttempts % 2 === 0 ? [new AutoConsentHeuristicCMP(this)] : [];
15064
+ const heuristicRules = isTop && this.config.enableHeuristicAction && this.state.findCmpAttempts % 2 === 0 ? [new AutoConsentHeuristicCMP(this, this.config.heuristicMode)] : [];
14956
15065
  const rulesPriorityStages = [
14957
15066
  ["site-specific", siteSpecificRules],
14958
15067
  ["generic", genericRules],