@duckduckgo/autoconsent 16.63.0 → 17.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.
- package/CHANGELOG.md +41 -0
- package/dist/addon-firefox/background.bundle.js +1 -0
- package/dist/addon-firefox/compact-rules.json +1 -1
- package/dist/addon-firefox/content.bundle.js +162 -35
- package/dist/addon-firefox/manifest.json +1 -1
- package/dist/addon-firefox/popup.bundle.js +1 -0
- package/dist/addon-firefox/rule-index.json +1 -1
- package/dist/addon-firefox/rules.json +1 -1
- package/dist/addon-mv3/background.bundle.js +1 -0
- package/dist/addon-mv3/compact-rules.json +1 -1
- package/dist/addon-mv3/content.bundle.js +162 -35
- package/dist/addon-mv3/manifest.json +1 -1
- package/dist/addon-mv3/popup.bundle.js +1 -0
- package/dist/addon-mv3/rule-index.json +1 -1
- package/dist/addon-mv3/rules.json +1 -1
- package/dist/autoconsent.cjs.js +162 -35
- package/dist/autoconsent.esm.js +162 -35
- package/dist/autoconsent.playwright.js +165 -35
- package/dist/autoconsent.standalone.js +163 -36
- package/dist/types/heuristics.d.ts +12 -3
- package/dist/types/types.d.ts +12 -1
- package/lib/cmps/base.ts +5 -1
- package/lib/heuristics.ts +240 -45
- package/lib/types.ts +16 -1
- package/lib/utils.ts +1 -0
- package/package.json +1 -1
- package/readme.md +1 -1
- package/rules/autoconsent/asrock.json +44 -0
- package/rules/autoconsent/consentmo.json +5 -2
- package/rules/autoconsent/cookie-script.json +34 -15
- package/rules/autoconsent/epaas.json +18 -1
- package/rules/autoconsent/fnsp-cookieconsent.json +30 -0
- package/rules/autoconsent/fod-fujitv.json +13 -0
- package/rules/autoconsent/hkdir.json +33 -0
- package/rules/autoconsent/jokul-cookie-consent.json +28 -0
- package/rules/autoconsent/lego.json +27 -5
- package/rules/autoconsent/mars-cookies-prompt.json +20 -0
- package/rules/autoconsent/niedziela-pl.json +35 -0
- package/rules/autoconsent/nikkeibp.json +10 -0
- package/rules/autoconsent/onecom-cookie-banner.json +25 -0
- package/rules/autoconsent/oslosportslager.no.json +34 -0
- package/rules/autoconsent/plex.tv.json +52 -0
- package/rules/autoconsent/radio-canada.json +28 -0
- package/rules/autoconsent/regjeringen.no.json +13 -0
- package/rules/autoconsent/roll20.json +50 -0
- package/rules/autoconsent/sirdata.json +8 -3
- package/rules/autoconsent/site-notifications-cookie-usage.json +25 -0
- package/rules/autoconsent/sparebank1-optin.json +10 -0
- package/rules/autoconsent/tri-table.json +6 -2
- package/rules/autoconsent/vinnova-se.json +50 -0
- package/rules/autoconsent/vortex-consent.json +33 -0
- package/rules/compact-rules.json +1 -1
- package/rules/rule-index.json +1 -1
- package/rules/rules.json +1 -1
- package/tests/asrock.spec.ts +3 -0
- package/tests/consentmo.spec.ts +2 -0
- package/tests/cookie-script.spec.ts +5 -0
- package/tests/epaas.spec.ts +3 -0
- package/tests/fnsp-cookieconsent.spec.ts +9 -0
- package/tests/fod-fujitv.spec.ts +3 -0
- package/tests/hkdir.spec.ts +10 -0
- package/tests/jokul-cookie-consent.spec.ts +3 -0
- package/tests/mars-cookies-prompt.spec.ts +3 -0
- package/tests/niedziela-pl.spec.ts +3 -0
- package/tests/nikkeibp.spec.ts +8 -0
- package/tests/onecom-cookie-banner.spec.ts +5 -0
- package/tests/oslosportslager.no.spec.ts +3 -0
- package/tests/plex.tv.spec.ts +5 -0
- package/tests/radio-canada.spec.ts +15 -0
- package/tests/regjeringen.no.spec.ts +3 -0
- package/tests/roll20.spec.ts +3 -0
- package/tests/sirdata.spec.ts +13 -1
- package/tests/site-notifications-cookie-usage.spec.ts +3 -0
- package/tests/sparebank1-optin.spec.ts +8 -0
- package/tests/tri-table.spec.ts +1 -1
- package/tests/vinnova-se.spec.ts +6 -0
- package/tests/vortex-consent.spec.ts +18 -0
- package/tests-wtr/heuristics/get-actionable-popups.ts +41 -7
- package/tests-wtr/heuristics/get-potential-popups.test.ts +235 -4
- package/tests-wtr/heuristics/heuristic-cmp.ts +16 -1
- package/rules/generated/auto_CA_plex.tv_ee4.json +0 -40
- package/rules/generated/auto_CA_support.plex.tv_gn3.json +0 -40
- package/tests/generated/auto_CA_plex.tv_ee4.spec.ts +0 -2
- package/tests/generated/auto_CA_support.plex.tv_gn3.spec.ts +0 -6
|
@@ -151,6 +151,7 @@
|
|
|
151
151
|
},
|
|
152
152
|
performanceLoggingEnabled: false,
|
|
153
153
|
heuristicPopupSearchTimeout: 100,
|
|
154
|
+
enableHeuristicHitTest: false,
|
|
154
155
|
heuristicMode: "off"
|
|
155
156
|
// heuristic disabled by default
|
|
156
157
|
};
|
|
@@ -1326,28 +1327,70 @@
|
|
|
1326
1327
|
// lib/heuristics.ts
|
|
1327
1328
|
var BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
|
|
1328
1329
|
var POPUP_SEARCH_MAX_TIME = 100;
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
return popups.sort((a, b) => (a
|
|
1330
|
+
var ACCEPTED_LEVELS = {
|
|
1331
|
+
off: [],
|
|
1332
|
+
reject: ["reject"],
|
|
1333
|
+
tier1: ["reject", "tier1"],
|
|
1334
|
+
tier2: ["reject", "tier1", "tier2"]
|
|
1335
|
+
};
|
|
1336
|
+
function getActionablePopups(mode = "reject", timeout = POPUP_SEARCH_MAX_TIME, hitTest = false) {
|
|
1337
|
+
const acceptedLevels = ACCEPTED_LEVELS[mode];
|
|
1338
|
+
const isActionable = (popup) => acceptedLevels.includes(popup.regexClassification) && isConsentPopup(popup);
|
|
1339
|
+
const popups = searchPopups(timeout, hitTest, DEFAULT_HIT_TEST_GRID, true).filter(isActionable);
|
|
1340
|
+
return popups.sort((a, b) => classificationRank(a) - classificationRank(b));
|
|
1340
1341
|
}
|
|
1341
|
-
function
|
|
1342
|
+
function searchPopups(timeout, hitTest, hitTestGrid, stopOnReject = false) {
|
|
1342
1343
|
if (!isTopFrame()) {
|
|
1343
|
-
|
|
1344
|
-
return [];
|
|
1345
|
-
}
|
|
1346
|
-
const frameElement = getFramePopupElement(document);
|
|
1347
|
-
const popup = frameElement && toPopupData(frameElement);
|
|
1348
|
-
return popup ? [popup] : [];
|
|
1344
|
+
return getFramePopups();
|
|
1349
1345
|
}
|
|
1350
1346
|
const deadline = performance.now() + timeout;
|
|
1347
|
+
const hitTestPopups = hitTest ? findPopupsByHitTest(deadline, hitTestGrid) : [];
|
|
1348
|
+
if (stopOnReject && hitTestPopups.some((p) => p.regexClassification === "reject" && isConsentPopup(p))) {
|
|
1349
|
+
return hitTestPopups;
|
|
1350
|
+
}
|
|
1351
|
+
const walkPopups = collectPositionedPopups(deadline);
|
|
1352
|
+
return [...walkPopups, ...mergeWalkOverlaps(hitTestPopups, walkPopups)];
|
|
1353
|
+
}
|
|
1354
|
+
var consentPopups = /* @__PURE__ */ new WeakMap();
|
|
1355
|
+
function classificationRank(popup) {
|
|
1356
|
+
return ACCEPTED_LEVELS.tier2.indexOf(popup.regexClassification);
|
|
1357
|
+
}
|
|
1358
|
+
function isConsentPopup(popup) {
|
|
1359
|
+
if (!ACCEPTED_LEVELS.tier2.includes(popup.regexClassification)) {
|
|
1360
|
+
return false;
|
|
1361
|
+
}
|
|
1362
|
+
let result = consentPopups.get(popup);
|
|
1363
|
+
if (result === void 0) {
|
|
1364
|
+
const popupText = popup.text.trim();
|
|
1365
|
+
result = !!popupText && !isExcludedPopup(popupText) && hasHeuristicPatterns(popupText);
|
|
1366
|
+
consentPopups.set(popup, result);
|
|
1367
|
+
}
|
|
1368
|
+
return result;
|
|
1369
|
+
}
|
|
1370
|
+
function mergeWalkOverlaps(hitTestPopups, walkPopups) {
|
|
1371
|
+
return hitTestPopups.filter((h) => {
|
|
1372
|
+
const hitIsConsent = isConsentPopup(h);
|
|
1373
|
+
const absorbs = (p) => !hitIsConsent || isConsentPopup(p) && classificationRank(p) <= classificationRank(h);
|
|
1374
|
+
const walkPopup = walkPopups.find((p) => composedOverlaps(p.element, h.element) && absorbs(p));
|
|
1375
|
+
if (walkPopup) {
|
|
1376
|
+
(walkPopup.hitPoints ??= []).push(...h.hitPoints ?? []);
|
|
1377
|
+
}
|
|
1378
|
+
return !walkPopup;
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
function getFramePopups() {
|
|
1382
|
+
if (window.parent && window.parent !== window.top) {
|
|
1383
|
+
return [];
|
|
1384
|
+
}
|
|
1385
|
+
const frameElement = getFramePopupElement(document);
|
|
1386
|
+
const popup = frameElement && toPopupData(frameElement);
|
|
1387
|
+
if (!popup) {
|
|
1388
|
+
return [];
|
|
1389
|
+
}
|
|
1390
|
+
popup.source = "frame";
|
|
1391
|
+
return [popup];
|
|
1392
|
+
}
|
|
1393
|
+
function collectPositionedPopups(deadline) {
|
|
1351
1394
|
const { elements, shadowHosts } = getPopupLikeElements(deadline);
|
|
1352
1395
|
const potentialPopups = [];
|
|
1353
1396
|
const expanded = [];
|
|
@@ -1363,17 +1406,86 @@
|
|
|
1363
1406
|
if (root !== popup) {
|
|
1364
1407
|
expanded.push(root.element);
|
|
1365
1408
|
}
|
|
1409
|
+
root.source = "walk";
|
|
1366
1410
|
potentialPopups.push(root);
|
|
1367
1411
|
}
|
|
1368
1412
|
return potentialPopups.filter((p) => !expanded.some((root) => root !== p.element && composedContains(root, p.element)));
|
|
1369
1413
|
}
|
|
1414
|
+
var DEFAULT_HIT_TEST_GRID = {
|
|
1415
|
+
columns: [0.1, 0.5, 0.9],
|
|
1416
|
+
// banners sit along the top and bottom edges and modals in the middle; rows 150px in added almost nothing in crawls
|
|
1417
|
+
edgeRows: [30, 90]
|
|
1418
|
+
};
|
|
1419
|
+
function findPopupsByHitTest(deadline, grid) {
|
|
1420
|
+
const climbed = /* @__PURE__ */ new Map();
|
|
1421
|
+
const found = [];
|
|
1422
|
+
const toPopup = (el) => el.querySelector(TEXT_ENTRY_SELECTOR) ? null : toPopupRoot(el);
|
|
1423
|
+
for (const [x, y] of getHitTestPoints(grid)) {
|
|
1424
|
+
if (performance.now() > deadline) {
|
|
1425
|
+
return found;
|
|
1426
|
+
}
|
|
1427
|
+
const path = [];
|
|
1428
|
+
let reached = null;
|
|
1429
|
+
const shouldStop = (el) => {
|
|
1430
|
+
if (climbed.has(el)) {
|
|
1431
|
+
reached = climbed.get(el) ?? null;
|
|
1432
|
+
return true;
|
|
1433
|
+
}
|
|
1434
|
+
const overlapped = found.find((p) => composedOverlaps(p.element, el));
|
|
1435
|
+
if (overlapped) {
|
|
1436
|
+
reached = composedContains(overlapped.element, el) ? overlapped : null;
|
|
1437
|
+
return true;
|
|
1438
|
+
}
|
|
1439
|
+
path.push(el);
|
|
1440
|
+
return false;
|
|
1441
|
+
};
|
|
1442
|
+
const hit = closestHTMLElement(deepElementFromPoint(x, y));
|
|
1443
|
+
const popup = climbToPopup(hit, MAX_HIT_TEST_CLIMB_DEPTH, toPopup, shouldStop);
|
|
1444
|
+
if (popup) {
|
|
1445
|
+
popup.source = "hitTest";
|
|
1446
|
+
found.push(popup);
|
|
1447
|
+
reached = popup;
|
|
1448
|
+
}
|
|
1449
|
+
for (const el of path) {
|
|
1450
|
+
climbed.set(el, reached);
|
|
1451
|
+
}
|
|
1452
|
+
if (reached) {
|
|
1453
|
+
(reached.hitPoints ??= []).push([x, y]);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
return found;
|
|
1457
|
+
}
|
|
1458
|
+
function getHitTestPoints(grid) {
|
|
1459
|
+
const { innerWidth: width, innerHeight: height } = window;
|
|
1460
|
+
const rows = [...grid.edgeRows, height / 2, ...grid.edgeRows.map((y) => height - y)].map((y) => Math.round(y));
|
|
1461
|
+
const ys = [...new Set(rows)].filter((y) => y >= 0 && y < height);
|
|
1462
|
+
return ys.flatMap((y) => grid.columns.map((column) => [Math.round(column * width), y]));
|
|
1463
|
+
}
|
|
1464
|
+
var TEXT_ENTRY_SELECTOR = 'textarea, input:not([type]), input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="tel"], input[type="number"], input[type="url"]';
|
|
1465
|
+
function deepElementFromPoint(x, y) {
|
|
1466
|
+
let el = document.elementFromPoint(x, y);
|
|
1467
|
+
while (el?.shadowRoot) {
|
|
1468
|
+
const inner = el.shadowRoot.elementFromPoint(x, y);
|
|
1469
|
+
if (!inner || inner === el) {
|
|
1470
|
+
break;
|
|
1471
|
+
}
|
|
1472
|
+
el = inner;
|
|
1473
|
+
}
|
|
1474
|
+
return el;
|
|
1475
|
+
}
|
|
1476
|
+
function closestHTMLElement(el) {
|
|
1477
|
+
while (el && !(el instanceof HTMLElement)) {
|
|
1478
|
+
el = el.parentElement;
|
|
1479
|
+
}
|
|
1480
|
+
return el;
|
|
1481
|
+
}
|
|
1370
1482
|
function toPopupData(el, shadowHosts, text = getElementText(el)) {
|
|
1371
1483
|
if (!text) {
|
|
1372
1484
|
return null;
|
|
1373
1485
|
}
|
|
1374
1486
|
const buttons = getButtonData(el, shadowHosts);
|
|
1375
1487
|
classifyButtons(buttons);
|
|
1376
|
-
return { text, element: el, buttons };
|
|
1488
|
+
return { text, element: el, buttons, regexClassification: classifyPopup(buttons) };
|
|
1377
1489
|
}
|
|
1378
1490
|
function hasPopupButtons(text, buttons) {
|
|
1379
1491
|
return !isOnlyButtonText(text, buttons) && buttons.some((b) => b.regexClassification !== "other");
|
|
@@ -1385,26 +1497,34 @@
|
|
|
1385
1497
|
}
|
|
1386
1498
|
return rest.replace(/\s+/g, "").length < 10;
|
|
1387
1499
|
}
|
|
1500
|
+
function toPopupRoot(el, shadowHosts) {
|
|
1501
|
+
const text = getElementText(el);
|
|
1502
|
+
if (!text || !hasHeuristicPatterns(text)) {
|
|
1503
|
+
return null;
|
|
1504
|
+
}
|
|
1505
|
+
const popup = toPopupData(el, shadowHosts, text);
|
|
1506
|
+
return popup && hasPopupButtons(text, popup.buttons) ? popup : null;
|
|
1507
|
+
}
|
|
1388
1508
|
var MAX_FRAGMENT_EXPANSION_DEPTH = 6;
|
|
1509
|
+
var MAX_HIT_TEST_CLIMB_DEPTH = 8;
|
|
1510
|
+
function climbToPopup(el, maxDepth, toPopup, shouldStop) {
|
|
1511
|
+
for (let depth = 0; el && depth < maxDepth; depth++, el = getComposedParent(el)) {
|
|
1512
|
+
if (shouldStop?.(el) || el === document.body || el === document.documentElement || el.getBoundingClientRect().height > window.innerHeight) {
|
|
1513
|
+
return null;
|
|
1514
|
+
}
|
|
1515
|
+
const popup = toPopup(el);
|
|
1516
|
+
if (popup) {
|
|
1517
|
+
return popup;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
return null;
|
|
1521
|
+
}
|
|
1389
1522
|
function expandPopupFragment(popup, shadowHosts) {
|
|
1390
1523
|
if (hasHeuristicPatterns(popup.text) && hasPopupButtons(popup.text, popup.buttons)) {
|
|
1391
1524
|
return popup;
|
|
1392
1525
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
if (el === document.body || el === document.documentElement || el.getBoundingClientRect().height > window.innerHeight) {
|
|
1396
|
-
break;
|
|
1397
|
-
}
|
|
1398
|
-
const text = getElementText(el);
|
|
1399
|
-
if (!text || !hasHeuristicPatterns(text)) {
|
|
1400
|
-
continue;
|
|
1401
|
-
}
|
|
1402
|
-
const root = toPopupData(el, shadowHosts, text);
|
|
1403
|
-
if (root && hasPopupButtons(text, root.buttons)) {
|
|
1404
|
-
return root;
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
return popup;
|
|
1526
|
+
const parent = getComposedParent(popup.element);
|
|
1527
|
+
return climbToPopup(parent, MAX_FRAGMENT_EXPANSION_DEPTH, (el) => toPopupRoot(el, shadowHosts)) ?? popup;
|
|
1408
1528
|
}
|
|
1409
1529
|
function getComposedParent(el) {
|
|
1410
1530
|
if (el.parentElement) {
|
|
@@ -1435,6 +1555,9 @@
|
|
|
1435
1555
|
function isRenderedTextElement(el) {
|
|
1436
1556
|
return el instanceof HTMLElement && !["STYLE", "SCRIPT", "TEMPLATE", "NOSCRIPT", "LINK", "SLOT"].includes(el.tagName) && window.getComputedStyle(el).display !== "none";
|
|
1437
1557
|
}
|
|
1558
|
+
function composedOverlaps(a, b) {
|
|
1559
|
+
return composedContains(a, b) || composedContains(b, a);
|
|
1560
|
+
}
|
|
1438
1561
|
function composedContains(container, node) {
|
|
1439
1562
|
if (container.contains(node)) {
|
|
1440
1563
|
return true;
|
|
@@ -1926,7 +2049,11 @@
|
|
|
1926
2049
|
async detectCmp() {
|
|
1927
2050
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1928
2051
|
this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorStart");
|
|
1929
|
-
this.popups = getActionablePopups(
|
|
2052
|
+
this.popups = getActionablePopups(
|
|
2053
|
+
this.mode,
|
|
2054
|
+
this.autoconsent.config.heuristicPopupSearchTimeout,
|
|
2055
|
+
this.autoconsent.config.enableHeuristicHitTest
|
|
2056
|
+
);
|
|
1930
2057
|
this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorEnd");
|
|
1931
2058
|
this.autoconsent.config.performanceLoggingEnabled && performance.measure("heuristicDetector", "heuristicDetectorStart", "heuristicDetectorEnd");
|
|
1932
2059
|
if (this.popups.length > 0) {
|