@duckduckgo/autoconsent 15.1.0 → 16.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/.github/PULL_REQUEST_TEMPLATE.md +7 -0
  2. package/.github/dependabot.yml +0 -3
  3. package/.github/labeler.yml +36 -0
  4. package/.github/workflows/release-labels.yml +110 -0
  5. package/.github/workflows/release.yml +0 -4
  6. package/AGENTS.md +3 -3
  7. package/CHANGELOG.md +33 -0
  8. package/build.sh +0 -2
  9. package/dist/addon-firefox/background.bundle.js +8 -24
  10. package/dist/addon-firefox/compact-rules.json +1 -1
  11. package/dist/addon-firefox/content.bundle.js +176 -686
  12. package/dist/addon-firefox/manifest.json +1 -1
  13. package/dist/addon-firefox/popup.bundle.js +31 -27
  14. package/dist/addon-firefox/popup.html +21 -13
  15. package/dist/addon-firefox/rule-index.json +1 -1
  16. package/dist/addon-firefox/rules.json +1 -1
  17. package/dist/addon-mv3/background.bundle.js +8 -24
  18. package/dist/addon-mv3/compact-rules.json +1 -1
  19. package/dist/addon-mv3/content.bundle.js +176 -686
  20. package/dist/addon-mv3/manifest.json +1 -1
  21. package/dist/addon-mv3/popup.bundle.js +31 -27
  22. package/dist/addon-mv3/popup.html +21 -13
  23. package/dist/addon-mv3/rule-index.json +1 -1
  24. package/dist/addon-mv3/rules.json +1 -1
  25. package/dist/autoconsent.cjs.js +176 -685
  26. package/dist/autoconsent.esm.js +176 -685
  27. package/dist/autoconsent.playwright.js +177 -689
  28. package/dist/autoconsent.standalone.js +177 -690
  29. package/dist/types/cmps/base.d.ts +3 -3
  30. package/dist/types/heuristics.d.ts +2 -2
  31. package/dist/types/types.d.ts +10 -23
  32. package/dist/types/web.d.ts +0 -9
  33. package/docs/api.md +1 -8
  34. package/docs/puppeteer.md +1 -3
  35. package/docs/release-notes.md +65 -0
  36. package/docs/rule-syntax.md +0 -17
  37. package/eslint.config.mjs +1 -1
  38. package/lib/cmps/base.ts +5 -6
  39. package/lib/heuristics.ts +15 -15
  40. package/lib/types.ts +9 -21
  41. package/lib/utils.ts +2 -4
  42. package/lib/web.ts +3 -53
  43. package/package.json +112 -18
  44. package/playwright/content.ts +1 -2
  45. package/readme.md +3 -6
  46. package/rules/autoconsent/dreamlab-cmp.json +13 -0
  47. package/rules/build.ts +1 -24
  48. package/rules/compact-rules.json +1 -1
  49. package/rules/rule-index-builder.ts +2 -7
  50. package/rules/rule-index.json +1 -1
  51. package/rules/rules.json +1 -1
  52. package/scripts/check-pr-release-labels.ts +255 -0
  53. package/standalone/content.ts +0 -2
  54. package/tests/dreamlab-cmp.spec.ts +3 -0
  55. package/tests/quantcast.spec.ts +0 -6
  56. package/tests-wtr/heuristics/get-actionable-popups.ts +13 -13
  57. package/tests-wtr/heuristics/heuristic-cmp.ts +32 -33
  58. package/tests-wtr/lifecycle/find-cmp.ts +5 -8
  59. package/tests-wtr/web/do-opt-in.test.ts +0 -1
  60. package/tests-wtr/web/do-opt-out.test.ts +0 -1
  61. package/tests-wtr/web/do-self-test.test.ts +0 -1
  62. package/tests-wtr/web/filter-cmps.test.ts +0 -1
  63. package/tests-wtr/web/receive-message-callback.test.ts +1 -2
  64. package/tests-wtr/web/update-state.test.ts +1 -3
  65. package/tsconfig.build.json +1 -1
  66. package/.github/workflows/update-filterlist.yml +0 -42
  67. package/dist/autoconsent.extra.cjs.js +0 -15717
  68. package/dist/autoconsent.extra.esm.js +0 -15649
  69. package/dist/types/cmps/consentomatic.d.ts +0 -34
  70. package/dist/types/consentomatic/index.d.ts +0 -2
  71. package/dist/types/consentomatic/tools.d.ts +0 -10
  72. package/dist/types/filterlist-engine.d.ts +0 -2
  73. package/dist/types/filterlist-utils.d.ts +0 -4
  74. package/dist/types/web-extra.d.ts +0 -11
  75. package/lib/cmps/consentomatic.ts +0 -97
  76. package/lib/consentomatic/index.ts +0 -252
  77. package/lib/consentomatic/tools.ts +0 -199
  78. package/lib/filterlist-engine.ts +0 -5
  79. package/lib/filterlist-utils.ts +0 -45
  80. package/lib/web-extra.ts +0 -116
  81. package/rules/consentomatic.json +0 -1
  82. package/rules/filterlist.txt +0 -18440
  83. package/rules/filterlists/overrides.txt +0 -4
  84. package/scripts/compile-filterlist.mjs +0 -63
  85. package/scripts/fetch-easylist.sh +0 -25
  86. package/scripts/rebuild-filterlist.mjs +0 -216
  87. package/tests/oil.spec.ts +0 -7
  88. package/tests/springer.spec.ts +0 -7
  89. package/tests/wordpressgdpr.spec.ts +0 -6
@@ -1,4 +1,4 @@
1
- import { AutoCMP, DomActionsProvider, PopupData, PopupHandlingMode } from '../types';
1
+ import { AutoCMP, DomActionsProvider, HeuristicLevel, PopupData } from '../types';
2
2
  import { AutoConsentCMPRule, AutoConsentRuleStep, ElementSelector, HideMethod, RunContext, VisibilityCheck } from '../rules';
3
3
  import AutoConsent from '../web';
4
4
  import { snippets } from '../eval-snippets';
@@ -62,8 +62,8 @@ export declare class AutoConsentCMP extends AutoConsentCMPBase {
62
62
  }
63
63
  export declare class AutoConsentHeuristicCMP extends AutoConsentCMPBase {
64
64
  popups: PopupData[];
65
- mode: PopupHandlingMode;
66
- constructor(autoconsentInstance: AutoConsent, mode?: PopupHandlingMode);
65
+ mode: HeuristicLevel;
66
+ constructor(autoconsentInstance: AutoConsent, mode?: HeuristicLevel);
67
67
  get hasSelfTest(): boolean;
68
68
  get isIntermediate(): boolean;
69
69
  get isCosmetic(): boolean;
@@ -1,9 +1,9 @@
1
- import { ButtonData, ButtonRegexClassification, PopupData, PopupHandlingMode } from './types';
1
+ import { ButtonData, ButtonRegexClassification, HeuristicLevel, PopupData } from './types';
2
2
  export declare function checkHeuristicPatterns(allText: string, detectPatterns?: RegExp[]): {
3
3
  patterns: string[];
4
4
  snippets: string[];
5
5
  };
6
- export declare function getActionablePopups(mode?: PopupHandlingMode, timeout?: number): PopupData[];
6
+ export declare function getActionablePopups(mode?: HeuristicLevel, timeout?: number): PopupData[];
7
7
  export declare function classifyButtons(buttons: ButtonData[]): void;
8
8
  export declare function cleanButtonText(buttonText: string): string;
9
9
  export declare function classifyButtonTextRegex(buttonText: string): ButtonRegexClassification;
@@ -1,5 +1,4 @@
1
1
  import { ContentScriptMessage } from './messages';
2
- import { ConsentOMaticConfig } from './cmps/consentomatic';
3
2
  import { AutoConsentCMPRule, ElementSelector, HideMethod, RunContext, VisibilityCheck } from './rules';
4
3
  import { CompactCMPRuleset } from './encoding';
5
4
  export type MessageSender = (message: ContentScriptMessage) => Promise<void>;
@@ -43,9 +42,6 @@ export interface DomActionsProvider {
43
42
  export type RuleBundle = {
44
43
  autoconsent: AutoConsentCMPRule[];
45
44
  compact?: CompactCMPRuleset;
46
- consentomatic?: {
47
- [name: string]: ConsentOMaticConfig;
48
- };
49
45
  };
50
46
  export type AutoAction = 'optOut' | 'optIn' | null;
51
47
  export type Config = {
@@ -58,9 +54,7 @@ export type Config = {
58
54
  detectRetries: number;
59
55
  isMainWorld: boolean;
60
56
  prehideTimeout: number;
61
- enableFilterList: boolean;
62
57
  enableHeuristicDetection: boolean;
63
- enableHeuristicAction: boolean;
64
58
  enablePopupMutationObserver: boolean;
65
59
  visualTest: boolean;
66
60
  logs: {
@@ -74,12 +68,10 @@ export type Config = {
74
68
  };
75
69
  performanceLoggingEnabled: boolean;
76
70
  heuristicPopupSearchTimeout: number;
77
- heuristicMode: PopupHandlingMode;
71
+ heuristicMode: HeuristicLevel;
78
72
  };
79
- export type LifecycleState = 'loading' | 'initialized' | 'waitingForInitResponse' | 'started' | 'nothingDetected' | 'cosmeticFiltersDetected' | 'cmpDetected' | 'openPopupDetected' | 'runningOptOut' | 'runningOptIn' | 'optOutSucceeded' | 'optOutFailed' | 'optInSucceeded' | 'optInFailed' | 'done';
73
+ export type LifecycleState = 'loading' | 'initialized' | 'waitingForInitResponse' | 'started' | 'nothingDetected' | 'cmpDetected' | 'openPopupDetected' | 'runningOptOut' | 'runningOptIn' | 'optOutSucceeded' | 'optOutFailed' | 'optInSucceeded' | 'optInFailed' | 'done';
80
74
  export type ConsentState = {
81
- cosmeticFiltersOn: boolean;
82
- filterListReported: boolean;
83
75
  lifecycle: LifecycleState;
84
76
  prehideOn: boolean;
85
77
  findCmpAttempts: number;
@@ -103,19 +95,14 @@ export interface PopupData {
103
95
  text: string;
104
96
  element: HTMLElement;
105
97
  buttons: ButtonData[];
106
- regexClassification?: PopupHandlingMode;
98
+ regexClassification?: PopupClassification;
107
99
  }
108
100
  /**
109
- * Controls the behavior of the heuristic popup detection.
110
- * - Reject: Will click the reject button on a popup if it exists.
111
- * - Tier1: Will also click the acknowledge button on a popup if it exists, and no Reject button exists.
112
- * - Tier2: Will also click the accept button on a popup if it exists, and no Reject or Acknowledge button exists.
113
- * - None: Disabled
101
+ * The user setting for autoconsent and heuristic detection.
102
+ * - off: Heuristic detection is disabled.
103
+ * - reject: Heuristic detection is enabled, and will click the reject button on a popup if it exists.
104
+ * - tier1: Heuristic detection is enabled, and will click the acknowledge button on a popup if it exists, and no Reject or Settings button exists.
105
+ * - tier2: Heuristic detection is enabled, and will click the accept button on a popup if it exists, and no Reject, Acknowledge, or Settings button exists.
114
106
  */
115
- export declare const PopupHandlingModes: {
116
- readonly None: -1;
117
- readonly Reject: 0;
118
- readonly Tier1: 1;
119
- readonly Tier2: 2;
120
- };
121
- export type PopupHandlingMode = (typeof PopupHandlingModes)[keyof typeof PopupHandlingModes];
107
+ export type HeuristicLevel = 'off' | 'reject' | 'tier1' | 'tier2';
108
+ export type PopupClassification = 'none' | 'reject' | 'tier1' | 'tier2';
@@ -1,9 +1,7 @@
1
1
  import { MessageSender, AutoCMP, RuleBundle, Config, ConsentState } from './types';
2
- import { ConsentOMaticConfig } from './cmps/consentomatic';
3
2
  import { AutoConsentCMPRule } from './rules';
4
3
  import { BackgroundMessage } from './messages';
5
4
  import { DomActions } from './dom-actions';
6
- import { FiltersEngine } from '@ghostery/adblocker';
7
5
  export { snippets as evalSnippets } from './eval-snippets';
8
6
  export { filterCompactRules, encodeRules, decodeRules } from './encoding';
9
7
  export type { CompactRuleStep, CompactCMPRuleStep, CompactCMPRule, CompactCMPRuleset, IndexedCMPRuleset } from './encoding';
@@ -18,21 +16,17 @@ export default class AutoConsent {
18
16
  foundCmp?: AutoCMP;
19
17
  state: ConsentState;
20
18
  domActions: DomActions;
21
- filtersEngine?: FiltersEngine;
22
19
  sendContentMessage: MessageSender;
23
- protected cosmeticStyleSheet?: CSSStyleSheet;
24
20
  protected focusedElement?: HTMLElement;
25
21
  constructor(sendContentMessage: MessageSender, config?: Partial<Config> | null, declarativeRules?: RuleBundle | null);
26
22
  get config(): Config;
27
23
  initialize(config: Partial<Config>, declarativeRules: RuleBundle | null): void;
28
- initializeFilterList(): void;
29
24
  get shouldPrehide(): boolean;
30
25
  saveFocus(): void;
31
26
  restoreFocus(): void;
32
27
  addDynamicRules(): void;
33
28
  parseDeclarativeRules(declarativeRules: RuleBundle): void;
34
29
  addDeclarativeCMP(ruleset: AutoConsentCMPRule): void;
35
- addConsentomaticCMP(name: string, config: ConsentOMaticConfig): void;
36
30
  start(): void;
37
31
  _start(): Promise<boolean>;
38
32
  findCmp(retries: number): Promise<AutoCMP[]>;
@@ -52,9 +46,6 @@ export default class AutoConsent {
52
46
  waitForPopup(cmp: AutoCMP, retries?: number, interval?: number): Promise<boolean>;
53
47
  prehideElements(): boolean;
54
48
  undoPrehide(): void;
55
- undoCosmetics(): void;
56
- reportFilterlist(): void;
57
- filterListFallback(): boolean;
58
49
  updateState(change: Partial<ConsentState>): void;
59
50
  receiveMessageCallback(message: BackgroundMessage): Promise<void>;
60
51
  measurePerformance(): {
package/docs/api.md CHANGED
@@ -12,7 +12,6 @@ const consent = new AutoConsent( // make sure not to leak anything to the page g
12
12
  disabledCmps: [],
13
13
  enablePrehide: true,
14
14
  enableCosmeticRules: true,
15
- enableFilterList: false,
16
15
  detectRetries: 20,
17
16
  isMainWorld: false,
18
17
  prehideTimeout: 2000,
@@ -27,7 +26,6 @@ const consent = new AutoConsent( // make sure not to leak anything to the page g
27
26
  },
28
27
  { // optionally, pass JSON rules
29
28
  autoconsent: [ ... ],
30
- consentomatic: [ ... ],
31
29
  }
32
30
  );
33
31
 
@@ -59,14 +57,9 @@ sequenceDiagram
59
57
 
60
58
  activate CS
61
59
  Note right of CS: Parse rules and initialize autoconsent code
62
- Note right of CS: apply prehideSelectors and filterlist rules (if enabled)
60
+ Note right of CS: apply prehideSelectors (if enabled)
63
61
  Note right of CS: wait for DOMContentLoaded
64
62
 
65
- opt if filterlist enabled and filterlist selectors matched
66
- CS -->> BG: cmpDetected (with cmp: "filterList")
67
- CS -->> BG: popupFound (with cmp: "filterList")
68
- end
69
-
70
63
  Note right of CS: detect a CMP presence (not necessarily visible)
71
64
 
72
65
  CS ->> BG: cmpDetected
package/docs/puppeteer.md CHANGED
@@ -40,7 +40,6 @@ const autoconsentConfig = {
40
40
  detectRetries: 20,
41
41
  isMainWorld: false,
42
42
  prehideTimeout: 2000,
43
- enableFilterList: false,
44
43
  enableHeuristicDetection: true,
45
44
  enableHeuristicAction: true,
46
45
  logs: {
@@ -144,13 +143,12 @@ Key options:
144
143
  | `enableCosmeticRules` | boolean | — | Enable rules that hide popups via CSS |
145
144
  | `enableGeneratedRules` | boolean | — | Include auto-generated rules |
146
145
  | `detectRetries` | number | — | How many times to retry CMP detection |
147
- | `enableFilterList` | boolean | — | Enable ABP/uBO cosmetic filter support (requires the `/extra` build) |
148
146
 
149
147
  ## Rules
150
148
 
151
149
  The `initResp` message **must** include a `rules` object. Without rules, no CMPs will be detected. The package ships pre-built rule bundles:
152
150
 
153
- - `@duckduckgo/autoconsent/rules/rules.json` — full rules (includes `autoconsent` and `consentomatic` arrays)
151
+ - `@duckduckgo/autoconsent/rules/rules.json` — full rules (includes `autoconsent` array)
154
152
  - `@duckduckgo/autoconsent/rules/compact-rules.json` — compact encoding for opt-out only (smaller payload)
155
153
 
156
154
  Load either file and pass it as the `rules` field in the `initResp` message.
@@ -0,0 +1,65 @@
1
+ # Release note labels
2
+
3
+ Release notes are generated by `auto` from merged PR labels. Each PR that should
4
+ appear in release notes needs two release-related decisions before merge:
5
+
6
+ 1. Exactly one release impact label.
7
+ 2. Exactly one primary release-note category label.
8
+
9
+ Use `other` as the category for changes that should appear in release notes but
10
+ do not fit another category.
11
+
12
+ ## Release impact labels
13
+
14
+ Choose exactly one:
15
+
16
+ | Label | Use when |
17
+ | --- | --- |
18
+ | `major` | The change is breaking for consumers or downstream app integration. |
19
+ | `minor` | The change adds new behavior, rule coverage, or capabilities. |
20
+ | `patch` | The change fixes existing behavior or makes a non-breaking maintenance update. |
21
+
22
+ ## Release-note category labels
23
+
24
+ Choose exactly one category:
25
+
26
+ | Label | Use when |
27
+ | --- | --- |
28
+ | `rules` | Adding, updating, or regenerating autoconsent rules. |
29
+ | `bug` | Fixing broken behavior, including rule fixes and race-condition fixes. |
30
+ | `enhancement` | Adding or improving non-rule functionality. |
31
+ | `performance` | Improving runtime performance or measurement overhead. |
32
+ | `dependencies` | Updating package dependencies. |
33
+ | `ci` | Changing CI, release automation, or scripts used by automation. |
34
+ | `ai` | Changing agent instructions, workflows, or skills. |
35
+ | `documentation` | Documentation-only changes. |
36
+ | `tests` | Changing test harnesses, fixtures, or test infrastructure. Do not use this for ordinary tests added with feature or rule work. |
37
+ | `internal` | Internal-only maintenance that does not fit the categories above. |
38
+ | `other` | Changes that should appear in release notes but do not fit another category. |
39
+
40
+ ## Common examples
41
+
42
+ | PR type | Labels |
43
+ | --- | --- |
44
+ | New CMP rule or new site-specific rule | `minor`, `rules` |
45
+ | Existing rule fix | `patch`, `bug` |
46
+ | Generated rule update | `minor` or `patch`, `rules` |
47
+ | Core API or integration breaking change | `major`, `enhancement` |
48
+ | CI workflow or release script update | `patch`, `ci` |
49
+ | Dependency update | `patch`, `dependencies` |
50
+ | Agent skill or instruction update | `patch`, `ai` |
51
+ | Documentation-only update | `patch`, `documentation` |
52
+ | Test runner, fixture, or harness update | `patch`, `tests` |
53
+ | Miscellaneous repo maintenance | `patch`, `other` |
54
+
55
+ If a PR spans multiple categories, choose the category that best describes the
56
+ user-visible release note. Mention secondary details in the PR description.
57
+
58
+ ## Syncing GitHub labels
59
+
60
+ The label definitions live in `package.json` under the `auto.labels` config. If
61
+ labels are missing or descriptions/colors drift, sync them with:
62
+
63
+ ```bash
64
+ npx auto create-labels
65
+ ```
@@ -225,23 +225,6 @@ Sometimes the opt-out process requires actions that span across multiple pages o
225
225
 
226
226
  Some rules do not interact with the page, and only hide the cookie pop-ups with CSS. These rules are marked with the `cosmetic: true` flag. They are useful for pop-ups that do not provide a Reject button. Cosmetic rules can be disabled with the `enableCosmeticRules` config option.
227
227
 
228
- ### Filterlist
229
- Autoconsent supports cosmetic filters in common ABP/uBO format. For performance reasons, it needs to be bundled at build time. At the moment we include cosmetic filters from [Easylist Cookie](https://github.com/easylist/easylist/tree/master/easylist_cookie).
230
- Note that by default filterlist rules are not included, as this significantly increases the resulting bundle size. To use filterlist rules, you need to explicitly import the "extra" version of the library (`@duckduckgo/autoconsent/extra`), and set the `enableFilterlist` config option to `true`.
231
-
232
- ```javascript
233
- // import the library version with bundled filterlist rules
234
- import AutoConsent from '@duckduckgo/autoconsent/extra'
235
-
236
- // ...
237
-
238
- new AutoConsent({
239
- enableFilterlist: true,
240
- // other options
241
- })
242
-
243
- ```
244
-
245
228
  ## Context filters
246
229
 
247
230
  By default, rules will be executed in all top-level documents. Some rules are designed for specific contexts (e.g. only nested iframes, or only specific URLs). This can be configured in `runContext` field (see [runContext](#rule-syntax-reference) above).
package/eslint.config.mjs CHANGED
@@ -8,7 +8,7 @@ const c = tseslint.config(
8
8
  ...ddgConfig,
9
9
 
10
10
  {
11
- ignores: ['lib/consentomatic/**/*', 'dist/**/*', '.agents/**/*'],
11
+ ignores: ['dist/**/*', '.agents/**/*'],
12
12
  },
13
13
 
14
14
  {
package/lib/cmps/base.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AutoCMP, DomActionsProvider, PopupData, PopupHandlingMode, PopupHandlingModes } from '../types';
1
+ import { AutoCMP, DomActionsProvider, HeuristicLevel, PopupData } from '../types';
2
2
  import { AutoConsentCMPRule, AutoConsentRuleStep, ElementSelector, HideMethod, RunContext, VisibilityCheck } from '../rules';
3
3
  import { requestEval } from '../eval-handler';
4
4
  import AutoConsent from '../web';
@@ -419,9 +419,9 @@ export class AutoConsentCMP extends AutoConsentCMPBase {
419
419
 
420
420
  export class AutoConsentHeuristicCMP extends AutoConsentCMPBase {
421
421
  popups: PopupData[] = [];
422
- mode: PopupHandlingMode;
422
+ mode: HeuristicLevel;
423
423
 
424
- constructor(autoconsentInstance: AutoConsent, mode: PopupHandlingMode = PopupHandlingModes.Reject) {
424
+ constructor(autoconsentInstance: AutoConsent, mode: HeuristicLevel = 'reject') {
425
425
  super(autoconsentInstance);
426
426
  this.name = 'HEURISTIC';
427
427
  this.runContext = {
@@ -453,7 +453,7 @@ export class AutoConsentHeuristicCMP extends AutoConsentCMPBase {
453
453
  performance.measure('heuristicDetector', 'heuristicDetectorStart', 'heuristicDetectorEnd');
454
454
 
455
455
  if (this.popups.length > 0) {
456
- this.name = `HEURISTIC-TIER${this.popups[0].regexClassification}`;
456
+ this.name = `HEURISTIC-${this.popups[0].regexClassification?.toUpperCase()}`;
457
457
  return Promise.resolve(true);
458
458
  }
459
459
  return Promise.resolve(false);
@@ -473,8 +473,7 @@ export class AutoConsentHeuristicCMP extends AutoConsentCMPBase {
473
473
  const popup = this.popups[0];
474
474
  const level = popup.regexClassification;
475
475
  const buttons = popup.buttons;
476
- const targetButtonType =
477
- level === PopupHandlingModes.Reject ? 'reject' : level === PopupHandlingModes.Tier1 ? 'acknowledge' : 'accept';
476
+ const targetButtonType = level === 'reject' ? 'reject' : level === 'tier1' ? 'acknowledge' : 'accept';
478
477
  return buttons.find((button) => button.regexClassification === targetButtonType);
479
478
  }
480
479
 
package/lib/heuristics.ts CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  REJECT_PATTERNS,
7
7
  SETTINGS_PATTERNS,
8
8
  } from './heuristic-patterns';
9
- import { ButtonData, ButtonRegexClassification, PopupData, PopupHandlingMode, PopupHandlingModes } from './types';
9
+ import { ButtonData, ButtonRegexClassification, HeuristicLevel, PopupClassification, PopupData } from './types';
10
10
  import { isElementVisible, isTopFrame } from './utils';
11
11
 
12
12
  const BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
@@ -28,7 +28,12 @@ export function checkHeuristicPatterns(allText: string, detectPatterns = DETECT_
28
28
  return { patterns, snippets };
29
29
  }
30
30
 
31
- export function getActionablePopups(mode: PopupHandlingMode = PopupHandlingModes.Reject, timeout = POPUP_SEARCH_MAX_TIME): PopupData[] {
31
+ export function getActionablePopups(mode: HeuristicLevel = 'reject', timeout = POPUP_SEARCH_MAX_TIME): PopupData[] {
32
+ const acceptedLevels: PopupClassification[] =
33
+ mode === 'tier2' ? ['reject', 'tier1', 'tier2'] : mode === 'tier1' ? ['reject', 'tier1'] : ['reject'];
34
+ if (acceptedLevels.length === 0) {
35
+ return [];
36
+ }
32
37
  const popups = getPotentialPopups(timeout);
33
38
  const result = popups.reduce((acc, popup) => {
34
39
  const popupText = popup.text?.trim();
@@ -46,13 +51,8 @@ export function getActionablePopups(mode: PopupHandlingMode = PopupHandlingModes
46
51
  }, [] as PopupData[]);
47
52
  // popups filtered by mode and sorted so a popup with reject will always win.
48
53
  return result
49
- .filter(
50
- (popup) =>
51
- popup.regexClassification !== undefined &&
52
- popup.regexClassification !== PopupHandlingModes.None &&
53
- popup.regexClassification <= mode,
54
- )
55
- .sort((a, b) => (a.regexClassification ?? 0) - (b.regexClassification ?? 0));
54
+ .filter((popup) => popup.regexClassification !== undefined && acceptedLevels.includes(popup.regexClassification))
55
+ .sort((a, b) => ((a.regexClassification ?? '') > (b.regexClassification ?? '') ? 1 : -1));
56
56
  }
57
57
 
58
58
  export function classifyButtons(buttons: ButtonData[]) {
@@ -61,7 +61,7 @@ export function classifyButtons(buttons: ButtonData[]) {
61
61
  }
62
62
  }
63
63
 
64
- function classifyPopup(buttons: ButtonData[]): PopupHandlingMode {
64
+ function classifyPopup(buttons: ButtonData[]): PopupClassification {
65
65
  const { reject, settings, accept, acknowledge } = buttons.reduce(
66
66
  (acc, button) => {
67
67
  if (button.regexClassification && button.regexClassification !== 'other') {
@@ -72,18 +72,18 @@ function classifyPopup(buttons: ButtonData[]): PopupHandlingMode {
72
72
  { reject: 0, settings: 0, accept: 0, acknowledge: 0 },
73
73
  );
74
74
  if (reject > 0) {
75
- return PopupHandlingModes.Reject;
75
+ return 'reject';
76
76
  }
77
77
  if (settings > 0) {
78
- return PopupHandlingModes.None;
78
+ return 'none';
79
79
  }
80
80
  if (acknowledge > 0) {
81
- return PopupHandlingModes.Tier1;
81
+ return 'tier1';
82
82
  }
83
83
  if (accept > 0) {
84
- return accept === 1 ? PopupHandlingModes.Tier2 : PopupHandlingModes.None;
84
+ return accept === 1 ? 'tier2' : 'none';
85
85
  }
86
- return PopupHandlingModes.None;
86
+ return 'none';
87
87
  }
88
88
 
89
89
  /**
package/lib/types.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { ContentScriptMessage } from './messages';
2
- import { ConsentOMaticConfig } from './cmps/consentomatic';
3
2
  import { AutoConsentCMPRule, ElementSelector, HideMethod, RunContext, VisibilityCheck } from './rules';
4
3
  import { CompactCMPRuleset } from './encoding';
5
4
 
@@ -47,7 +46,6 @@ export interface DomActionsProvider {
47
46
  export type RuleBundle = {
48
47
  autoconsent: AutoConsentCMPRule[];
49
48
  compact?: CompactCMPRuleset;
50
- consentomatic?: { [name: string]: ConsentOMaticConfig };
51
49
  };
52
50
 
53
51
  export type AutoAction = 'optOut' | 'optIn' | null;
@@ -62,9 +60,7 @@ export type Config = {
62
60
  detectRetries: number;
63
61
  isMainWorld: boolean;
64
62
  prehideTimeout: number;
65
- enableFilterList: boolean;
66
63
  enableHeuristicDetection: boolean;
67
- enableHeuristicAction: boolean;
68
64
  enablePopupMutationObserver: boolean;
69
65
  visualTest: boolean; // If true, the script will delay before every click action
70
66
  logs: {
@@ -78,7 +74,7 @@ export type Config = {
78
74
  };
79
75
  performanceLoggingEnabled: boolean;
80
76
  heuristicPopupSearchTimeout: number;
81
- heuristicMode: PopupHandlingMode; // controls the behavior of the heuristic popup detection. Has no effect if enableHeuristicDetection is false.
77
+ heuristicMode: HeuristicLevel; // controls the behavior of the heuristic popup detection.
82
78
  };
83
79
 
84
80
  export type LifecycleState =
@@ -87,7 +83,6 @@ export type LifecycleState =
87
83
  | 'waitingForInitResponse'
88
84
  | 'started'
89
85
  | 'nothingDetected'
90
- | 'cosmeticFiltersDetected'
91
86
  | 'cmpDetected'
92
87
  | 'openPopupDetected'
93
88
  | 'runningOptOut'
@@ -99,8 +94,6 @@ export type LifecycleState =
99
94
  | 'done';
100
95
 
101
96
  export type ConsentState = {
102
- cosmeticFiltersOn: boolean; // true if cosmetic filter rules are currently applied.
103
- filterListReported: boolean; // true if the cosmetic filter list has been reported to the user.
104
97
  lifecycle: LifecycleState; // What point in the autoconsent lifecycle this script is at.
105
98
  prehideOn: boolean; // If the script is currently hiding preHide elements.
106
99
  findCmpAttempts: number; // Number of times we tried to find CMPs in this frame.
@@ -127,20 +120,15 @@ export interface PopupData {
127
120
  text: string;
128
121
  element: HTMLElement;
129
122
  buttons: ButtonData[];
130
- regexClassification?: PopupHandlingMode;
123
+ regexClassification?: PopupClassification;
131
124
  }
132
125
 
133
126
  /**
134
- * Controls the behavior of the heuristic popup detection.
135
- * - Reject: Will click the reject button on a popup if it exists.
136
- * - Tier1: Will also click the acknowledge button on a popup if it exists, and no Reject button exists.
137
- * - Tier2: Will also click the accept button on a popup if it exists, and no Reject or Acknowledge button exists.
138
- * - None: Disabled
127
+ * The user setting for autoconsent and heuristic detection.
128
+ * - off: Heuristic detection is disabled.
129
+ * - reject: Heuristic detection is enabled, and will click the reject button on a popup if it exists.
130
+ * - tier1: Heuristic detection is enabled, and will click the acknowledge button on a popup if it exists, and no Reject or Settings button exists.
131
+ * - tier2: Heuristic detection is enabled, and will click the accept button on a popup if it exists, and no Reject, Acknowledge, or Settings button exists.
139
132
  */
140
- export const PopupHandlingModes = {
141
- None: -1,
142
- Reject: 0,
143
- Tier1: 1,
144
- Tier2: 2,
145
- } as const;
146
- export type PopupHandlingMode = (typeof PopupHandlingModes)[keyof typeof PopupHandlingModes];
133
+ export type HeuristicLevel = 'off' | 'reject' | 'tier1' | 'tier2';
134
+ export type PopupClassification = 'none' | 'reject' | 'tier1' | 'tier2';
package/lib/utils.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { HideMethod } from './rules';
2
- import { Config, PopupHandlingModes } from './types';
2
+ import { Config } from './types';
3
3
 
4
4
  // get or create a style container for CSS overrides
5
5
  export function getStyleElement(styleOverrideElementId = 'autoconsent-css-rules'): HTMLStyleElement {
@@ -77,12 +77,10 @@ export function normalizeConfig(providedConfig: any): Config {
77
77
  enableCosmeticRules: true,
78
78
  enableGeneratedRules: true,
79
79
  enableHeuristicDetection: false,
80
- enableHeuristicAction: false,
81
80
  enablePopupMutationObserver: false,
82
81
  detectRetries: 20,
83
82
  isMainWorld: false,
84
83
  prehideTimeout: 2000,
85
- enableFilterList: false,
86
84
  visualTest: false,
87
85
  logs: {
88
86
  lifecycle: false,
@@ -95,7 +93,7 @@ export function normalizeConfig(providedConfig: any): Config {
95
93
  },
96
94
  performanceLoggingEnabled: false,
97
95
  heuristicPopupSearchTimeout: 100,
98
- heuristicMode: PopupHandlingModes.Reject,
96
+ heuristicMode: 'off', // heuristic disabled by default
99
97
  };
100
98
  const updatedConfig: Config = copyObject(defaultConfig);
101
99
  // filter out any unknown entries
package/lib/web.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { MessageSender, AutoCMP, RuleBundle, Config, ConsentState } from './types';
2
- import { ConsentOMaticCMP, ConsentOMaticConfig } from './cmps/consentomatic';
3
2
  import { AutoConsentCMPRule, SUPPORTED_RULE_STEP_VERSION } from './rules';
4
3
  import { BackgroundMessage, InitMessage } from './messages';
5
4
  import { evalState, resolveEval } from './eval-handler';
@@ -8,7 +7,6 @@ import { dynamicCMPs } from './cmps/all';
8
7
  import { AutoConsentCMP, AutoConsentHeuristicCMP } from './cmps/base';
9
8
  import { DomActions } from './dom-actions';
10
9
  import { isTopFrame, normalizeConfig, scheduleWhenIdle } from './utils';
11
- import { FiltersEngine } from '@ghostery/adblocker';
12
10
  import { checkHeuristicPatterns } from './heuristics';
13
11
  import { decodeRules } from './encoding';
14
12
 
@@ -36,8 +34,6 @@ export default class AutoConsent {
36
34
  #config?: Config;
37
35
  foundCmp?: AutoCMP;
38
36
  state: ConsentState = {
39
- cosmeticFiltersOn: false,
40
- filterListReported: false,
41
37
  lifecycle: 'loading',
42
38
  prehideOn: false,
43
39
  findCmpAttempts: 0,
@@ -51,9 +47,7 @@ export default class AutoConsent {
51
47
  endTime: 0,
52
48
  };
53
49
  domActions: DomActions;
54
- filtersEngine?: FiltersEngine;
55
50
  sendContentMessage: MessageSender;
56
- protected cosmeticStyleSheet?: CSSStyleSheet;
57
51
  protected focusedElement?: HTMLElement;
58
52
 
59
53
  constructor(sendContentMessage: MessageSender, config: Partial<Config> | null = null, declarativeRules: RuleBundle | null = null) {
@@ -100,9 +94,6 @@ export default class AutoConsent {
100
94
  this.parseDeclarativeRules(declarativeRules);
101
95
  }
102
96
 
103
- if (config.enableFilterList) {
104
- this.initializeFilterList();
105
- }
106
97
  this.rules = filterCMPs(this.rules, normalizedConfig);
107
98
 
108
99
  if (this.shouldPrehide) {
@@ -131,10 +122,6 @@ export default class AutoConsent {
131
122
  this.updateState({ lifecycle: 'initialized' });
132
123
  }
133
124
 
134
- initializeFilterList() {
135
- // no-op by default
136
- }
137
-
138
125
  get shouldPrehide() {
139
126
  return this.config.enablePrehide && !this.config.visualTest;
140
127
  }
@@ -167,11 +154,6 @@ export default class AutoConsent {
167
154
  parseDeclarativeRules(declarativeRules: RuleBundle) {
168
155
  const perfEnabled = this.#config?.performanceLoggingEnabled;
169
156
  perfEnabled && performance.mark('parseDeclarativeRulesStart');
170
- if (declarativeRules.consentomatic) {
171
- for (const [name, rule] of Object.entries(declarativeRules.consentomatic)) {
172
- this.addConsentomaticCMP(name, rule);
173
- }
174
- }
175
157
 
176
158
  if (declarativeRules.autoconsent) {
177
159
  declarativeRules.autoconsent.forEach((ruleset) => {
@@ -198,10 +180,6 @@ export default class AutoConsent {
198
180
  }
199
181
  }
200
182
 
201
- addConsentomaticCMP(name: string, config: ConsentOMaticConfig) {
202
- this.rules.push(new ConsentOMaticCMP(`com_${name}`, config));
203
- }
204
-
205
183
  // start the detection process, possibly with a delay
206
184
  start() {
207
185
  scheduleWhenIdle(() => this._start());
@@ -222,7 +200,8 @@ export default class AutoConsent {
222
200
  this.undoPrehide();
223
201
  }
224
202
 
225
- return this.filterListFallback();
203
+ this.updateState({ lifecycle: 'nothingDetected' });
204
+ return false;
226
205
  }
227
206
 
228
207
  this.updateState({ lifecycle: 'cmpDetected' });
@@ -296,7 +275,7 @@ export default class AutoConsent {
296
275
  });
297
276
  // heuristic CMP is only run in the top frame and only if heuristic action is enabled and retries is odd
298
277
  const heuristicRules =
299
- isTop && this.config.enableHeuristicAction && this.state.findCmpAttempts % 2 === 0
278
+ isTop && this.config.heuristicMode !== 'off' && this.state.findCmpAttempts % 2 === 0
300
279
  ? [new AutoConsentHeuristicCMP(this, this.config.heuristicMode)]
301
280
  : [];
302
281
 
@@ -436,11 +415,6 @@ export default class AutoConsent {
436
415
  // prehide might have timeouted by this time, apply it again
437
416
  this.prehideElements();
438
417
  }
439
- if (this.state.cosmeticFiltersOn) {
440
- // cancel cosmetic filters if we have a rule for this popup
441
- this.undoCosmetics();
442
- }
443
-
444
418
  this.foundCmp = cmp;
445
419
 
446
420
  if (this.config.autoAction === 'optOut') {
@@ -631,30 +605,6 @@ export default class AutoConsent {
631
605
  this.domActions.undoPrehide();
632
606
  }
633
607
 
634
- undoCosmetics() {
635
- // no-op by default
636
- }
637
-
638
- reportFilterlist() {
639
- this.sendContentMessage({
640
- type: 'cmpDetected',
641
- url: location.href,
642
- cmp: 'filterList',
643
- });
644
- this.sendContentMessage({
645
- type: 'popupFound',
646
- cmp: 'filterList',
647
- url: location.href,
648
- });
649
- this.updateState({ filterListReported: true });
650
- }
651
-
652
- filterListFallback() {
653
- // no-op by default
654
- this.updateState({ lifecycle: 'nothingDetected' });
655
- return false;
656
- }
657
-
658
608
  updateState(change: Partial<ConsentState>) {
659
609
  Object.assign(this.state, change);
660
610
  this.sendContentMessage({