@duckduckgo/autoconsent 14.68.0 → 14.69.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.
- package/AGENTS.md +36 -11
- package/CHANGELOG.md +17 -0
- package/dist/addon-firefox/compact-rules.json +1 -1
- package/dist/addon-firefox/content.bundle.js +17 -3
- package/dist/addon-firefox/manifest.json +1 -1
- package/dist/addon-firefox/rules.json +1 -1
- package/dist/addon-mv3/compact-rules.json +1 -1
- package/dist/addon-mv3/content.bundle.js +17 -3
- package/dist/addon-mv3/manifest.json +1 -1
- package/dist/addon-mv3/rules.json +1 -1
- package/dist/autoconsent.cjs.js +17 -3
- package/dist/autoconsent.esm.js +17 -3
- package/dist/autoconsent.extra.cjs.js +17 -3
- package/dist/autoconsent.extra.esm.js +17 -3
- package/dist/autoconsent.playwright.js +17 -3
- package/dist/types/heuristics.d.ts +1 -0
- package/docs/rule-syntax.md +32 -1
- package/lib/heuristics.ts +18 -3
- package/package.json +1 -1
- package/rules/compact-rules.json +1 -1
- package/rules/generated/auto_GB_www2.hm.com_0.json +15 -6
- package/rules/rules.json +1 -1
- package/tests/generated/auto_GB_www2.hm.com_0.spec.ts +16 -0
- package/tests-wtr/heuristics/get-actionable-popups.html +24 -0
- package/tests-wtr/heuristics/get-actionable-popups.ts +36 -0
- package/tests-wtr/heuristics/heuristics-utils.test.ts +43 -0
- package/rules/generated/auto_DE_www2.hm.com_beo.json +0 -40
- package/tests/generated/auto_DE_www2.hm.com_beo.spec.ts +0 -6
package/dist/autoconsent.cjs.js
CHANGED
|
@@ -1575,6 +1575,15 @@ function collectPotentialPopups(isFramed) {
|
|
|
1575
1575
|
}
|
|
1576
1576
|
return potentialPopups;
|
|
1577
1577
|
}
|
|
1578
|
+
function isDialogLikeElement(node) {
|
|
1579
|
+
if (node.tagName === "DIALOG" && node.hasAttribute("open")) {
|
|
1580
|
+
return true;
|
|
1581
|
+
}
|
|
1582
|
+
if (node.getAttribute("role") === "dialog" || node.getAttribute("aria-modal") === "true") {
|
|
1583
|
+
return true;
|
|
1584
|
+
}
|
|
1585
|
+
return false;
|
|
1586
|
+
}
|
|
1578
1587
|
function getPopupLikeElements() {
|
|
1579
1588
|
const walker = document.createTreeWalker(
|
|
1580
1589
|
document.documentElement,
|
|
@@ -1585,9 +1594,14 @@ function getPopupLikeElements() {
|
|
|
1585
1594
|
if (node.tagName === "BODY") {
|
|
1586
1595
|
return NodeFilter.FILTER_SKIP;
|
|
1587
1596
|
}
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1597
|
+
if (isElementVisible(node)) {
|
|
1598
|
+
const cssPosition = window.getComputedStyle(node).position;
|
|
1599
|
+
if (cssPosition === "fixed" || cssPosition === "sticky") {
|
|
1600
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
1601
|
+
}
|
|
1602
|
+
if (isDialogLikeElement(node)) {
|
|
1603
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
1604
|
+
}
|
|
1591
1605
|
}
|
|
1592
1606
|
return NodeFilter.FILTER_SKIP;
|
|
1593
1607
|
}
|
package/dist/autoconsent.esm.js
CHANGED
|
@@ -1545,6 +1545,15 @@ function collectPotentialPopups(isFramed) {
|
|
|
1545
1545
|
}
|
|
1546
1546
|
return potentialPopups;
|
|
1547
1547
|
}
|
|
1548
|
+
function isDialogLikeElement(node) {
|
|
1549
|
+
if (node.tagName === "DIALOG" && node.hasAttribute("open")) {
|
|
1550
|
+
return true;
|
|
1551
|
+
}
|
|
1552
|
+
if (node.getAttribute("role") === "dialog" || node.getAttribute("aria-modal") === "true") {
|
|
1553
|
+
return true;
|
|
1554
|
+
}
|
|
1555
|
+
return false;
|
|
1556
|
+
}
|
|
1548
1557
|
function getPopupLikeElements() {
|
|
1549
1558
|
const walker = document.createTreeWalker(
|
|
1550
1559
|
document.documentElement,
|
|
@@ -1555,9 +1564,14 @@ function getPopupLikeElements() {
|
|
|
1555
1564
|
if (node.tagName === "BODY") {
|
|
1556
1565
|
return NodeFilter.FILTER_SKIP;
|
|
1557
1566
|
}
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1567
|
+
if (isElementVisible(node)) {
|
|
1568
|
+
const cssPosition = window.getComputedStyle(node).position;
|
|
1569
|
+
if (cssPosition === "fixed" || cssPosition === "sticky") {
|
|
1570
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
1571
|
+
}
|
|
1572
|
+
if (isDialogLikeElement(node)) {
|
|
1573
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
1574
|
+
}
|
|
1561
1575
|
}
|
|
1562
1576
|
return NodeFilter.FILTER_SKIP;
|
|
1563
1577
|
}
|
|
@@ -12397,6 +12397,15 @@ function collectPotentialPopups(isFramed) {
|
|
|
12397
12397
|
}
|
|
12398
12398
|
return potentialPopups;
|
|
12399
12399
|
}
|
|
12400
|
+
function isDialogLikeElement(node) {
|
|
12401
|
+
if (node.tagName === "DIALOG" && node.hasAttribute("open")) {
|
|
12402
|
+
return true;
|
|
12403
|
+
}
|
|
12404
|
+
if (node.getAttribute("role") === "dialog" || node.getAttribute("aria-modal") === "true") {
|
|
12405
|
+
return true;
|
|
12406
|
+
}
|
|
12407
|
+
return false;
|
|
12408
|
+
}
|
|
12400
12409
|
function getPopupLikeElements() {
|
|
12401
12410
|
const walker = document.createTreeWalker(
|
|
12402
12411
|
document.documentElement,
|
|
@@ -12407,9 +12416,14 @@ function getPopupLikeElements() {
|
|
|
12407
12416
|
if (node.tagName === "BODY") {
|
|
12408
12417
|
return NodeFilter.FILTER_SKIP;
|
|
12409
12418
|
}
|
|
12410
|
-
|
|
12411
|
-
|
|
12412
|
-
|
|
12419
|
+
if (isElementVisible(node)) {
|
|
12420
|
+
const cssPosition = window.getComputedStyle(node).position;
|
|
12421
|
+
if (cssPosition === "fixed" || cssPosition === "sticky") {
|
|
12422
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
12423
|
+
}
|
|
12424
|
+
if (isDialogLikeElement(node)) {
|
|
12425
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
12426
|
+
}
|
|
12413
12427
|
}
|
|
12414
12428
|
return NodeFilter.FILTER_SKIP;
|
|
12415
12429
|
}
|
|
@@ -12331,6 +12331,15 @@ function collectPotentialPopups(isFramed) {
|
|
|
12331
12331
|
}
|
|
12332
12332
|
return potentialPopups;
|
|
12333
12333
|
}
|
|
12334
|
+
function isDialogLikeElement(node) {
|
|
12335
|
+
if (node.tagName === "DIALOG" && node.hasAttribute("open")) {
|
|
12336
|
+
return true;
|
|
12337
|
+
}
|
|
12338
|
+
if (node.getAttribute("role") === "dialog" || node.getAttribute("aria-modal") === "true") {
|
|
12339
|
+
return true;
|
|
12340
|
+
}
|
|
12341
|
+
return false;
|
|
12342
|
+
}
|
|
12334
12343
|
function getPopupLikeElements() {
|
|
12335
12344
|
const walker = document.createTreeWalker(
|
|
12336
12345
|
document.documentElement,
|
|
@@ -12341,9 +12350,14 @@ function getPopupLikeElements() {
|
|
|
12341
12350
|
if (node.tagName === "BODY") {
|
|
12342
12351
|
return NodeFilter.FILTER_SKIP;
|
|
12343
12352
|
}
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
|
|
12353
|
+
if (isElementVisible(node)) {
|
|
12354
|
+
const cssPosition = window.getComputedStyle(node).position;
|
|
12355
|
+
if (cssPosition === "fixed" || cssPosition === "sticky") {
|
|
12356
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
12357
|
+
}
|
|
12358
|
+
if (isDialogLikeElement(node)) {
|
|
12359
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
12360
|
+
}
|
|
12347
12361
|
}
|
|
12348
12362
|
return NodeFilter.FILTER_SKIP;
|
|
12349
12363
|
}
|
|
@@ -1547,6 +1547,15 @@
|
|
|
1547
1547
|
}
|
|
1548
1548
|
return potentialPopups;
|
|
1549
1549
|
}
|
|
1550
|
+
function isDialogLikeElement(node) {
|
|
1551
|
+
if (node.tagName === "DIALOG" && node.hasAttribute("open")) {
|
|
1552
|
+
return true;
|
|
1553
|
+
}
|
|
1554
|
+
if (node.getAttribute("role") === "dialog" || node.getAttribute("aria-modal") === "true") {
|
|
1555
|
+
return true;
|
|
1556
|
+
}
|
|
1557
|
+
return false;
|
|
1558
|
+
}
|
|
1550
1559
|
function getPopupLikeElements() {
|
|
1551
1560
|
const walker = document.createTreeWalker(
|
|
1552
1561
|
document.documentElement,
|
|
@@ -1557,9 +1566,14 @@
|
|
|
1557
1566
|
if (node.tagName === "BODY") {
|
|
1558
1567
|
return NodeFilter.FILTER_SKIP;
|
|
1559
1568
|
}
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1569
|
+
if (isElementVisible(node)) {
|
|
1570
|
+
const cssPosition = window.getComputedStyle(node).position;
|
|
1571
|
+
if (cssPosition === "fixed" || cssPosition === "sticky") {
|
|
1572
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
1573
|
+
}
|
|
1574
|
+
if (isDialogLikeElement(node)) {
|
|
1575
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
1576
|
+
}
|
|
1563
1577
|
}
|
|
1564
1578
|
return NodeFilter.FILTER_SKIP;
|
|
1565
1579
|
}
|
|
@@ -10,6 +10,7 @@ export declare function classifyButtons(buttons: ButtonData[]): {
|
|
|
10
10
|
};
|
|
11
11
|
export declare function isRejectButton(buttonText: string, rejectPatterns?: (string | RegExp)[], neverMatchPatterns?: RegExp[]): boolean;
|
|
12
12
|
export declare function cleanButtonText(buttonText: string): string;
|
|
13
|
+
export declare function isDialogLikeElement(node: HTMLElement): boolean;
|
|
13
14
|
/**
|
|
14
15
|
* Serialize all actionable buttons on the page
|
|
15
16
|
*/
|
package/docs/rule-syntax.md
CHANGED
|
@@ -22,9 +22,14 @@ Both JSON and class implementations have the following components:
|
|
|
22
22
|
* `frame` - boolean, set to `true` if the rule should be executed in nested frames (default: `false`)
|
|
23
23
|
* `urlPattern` - string, specifies a regular expression that should match the page URL (default: empty)
|
|
24
24
|
* (optional) `test` - a list of actions to verify a successful opt-out. This is currently only used in Playwright tests.
|
|
25
|
+
* (optional) `minimumRuleStepVersion` - the minimum rule step version needed to execute this rule. Defaults to `1`. See [Rule Step Versioning](#rule-step-versioning).
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
`detectCMP`, `detectPopup`, `optOut`, `optIn`, and `test` are defined as a set of checks or actions on the page. In the JSON syntax this is a list of `AutoConsentRuleStep` objects. For `detect` checks, we return true for the check if all steps return true. For opt in and out, we execute actions in order, exiting if one fails.
|
|
28
|
+
`detectCMP`, `detectPopup`, `optOut`, `optIn`, and `test` are defined as a set of checks or actions on the page. In the JSON syntax this is a list of `AutoConsentRuleStep` objects. For `detect` checks, we return true for the check if all steps return true. For opt in and out, we execute actions in order, exiting if one fails.
|
|
29
|
+
|
|
30
|
+
**Important:** Do not use `wait` steps in `detectCmp` or `detectPopup` arrays. Detection must be fast and non-blocking -- the engine already retries detection automatically with its own timing. Adding `wait` steps to detection slows down detection of other rules.
|
|
31
|
+
|
|
32
|
+
The following checks/actions are supported:
|
|
28
33
|
|
|
29
34
|
## Element selectors
|
|
30
35
|
|
|
@@ -240,3 +245,29 @@ new AutoConsent({
|
|
|
240
245
|
## Context filters
|
|
241
246
|
|
|
242
247
|
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).
|
|
248
|
+
|
|
249
|
+
## Rule Step Versioning
|
|
250
|
+
|
|
251
|
+
New step types are occasionally added to the autoconsent engine (e.g. `removeClass`, `setStyle`, `addStyle` were added in version 2). Because rules can be shipped to clients independently of app releases, a rule that uses a newer step type could end up on a client that doesn't support it yet.
|
|
252
|
+
|
|
253
|
+
The `minimumRuleStepVersion` field solves this: clients compare the rule's declared version against their own supported version (`SUPPORTED_RULE_STEP_VERSION` in `lib/rules.ts`) and silently skip rules they cannot execute.
|
|
254
|
+
|
|
255
|
+
### Version history
|
|
256
|
+
|
|
257
|
+
| Version | Step types added |
|
|
258
|
+
|---------|-----------------|
|
|
259
|
+
| 1 | All original step types (`exists`, `visible`, `waitFor`, `waitForVisible`, `click`, `waitForThenClick`, `wait`, `hide`, `if`/`then`/`else`, `any`, `eval`, `cookieContains`, `negated`) |
|
|
260
|
+
| 2 | `removeClass`, `setStyle`, `addStyle` |
|
|
261
|
+
|
|
262
|
+
### When to set it
|
|
263
|
+
|
|
264
|
+
- If a rule only uses version-1 step types, omit the field (defaults to `1`).
|
|
265
|
+
- If a rule uses `removeClass`, `setStyle`, or `addStyle`, set `"minimumRuleStepVersion": 2`.
|
|
266
|
+
- When a future version introduces new step types, any rule using them must set `minimumRuleStepVersion` to the corresponding version number.
|
|
267
|
+
|
|
268
|
+
### Adding new step types
|
|
269
|
+
|
|
270
|
+
When introducing a new step type:
|
|
271
|
+
1. Bump `SUPPORTED_RULE_STEP_VERSION` in `lib/rules.ts` and add a comment describing what was added.
|
|
272
|
+
2. Add the new step type definition to the `AutoConsentRuleStep` union in the same file.
|
|
273
|
+
3. Any rule using the new step type must set `minimumRuleStepVersion` to the new version number.
|
package/lib/heuristics.ts
CHANGED
|
@@ -126,6 +126,16 @@ function collectPotentialPopups(isFramed: boolean): PopupData[] {
|
|
|
126
126
|
return potentialPopups;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
export function isDialogLikeElement(node: HTMLElement): boolean {
|
|
130
|
+
if (node.tagName === 'DIALOG' && node.hasAttribute('open')) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
if (node.getAttribute('role') === 'dialog' || node.getAttribute('aria-modal') === 'true') {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
129
139
|
/**
|
|
130
140
|
* Heuristic to get all elements that look like "popups"
|
|
131
141
|
* TODO: this heuristic is too strict, not all popups are actually sticky/fixed
|
|
@@ -139,9 +149,14 @@ function getPopupLikeElements(): HTMLElement[] {
|
|
|
139
149
|
if (node.tagName === 'BODY') {
|
|
140
150
|
return NodeFilter.FILTER_SKIP;
|
|
141
151
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
if (isElementVisible(node)) {
|
|
153
|
+
const cssPosition = window.getComputedStyle(node).position;
|
|
154
|
+
if (cssPosition === 'fixed' || cssPosition === 'sticky') {
|
|
155
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
156
|
+
}
|
|
157
|
+
if (isDialogLikeElement(node)) {
|
|
158
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
159
|
+
}
|
|
145
160
|
}
|
|
146
161
|
return NodeFilter.FILTER_SKIP;
|
|
147
162
|
},
|