@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
package/dist/autoconsent.cjs.js
CHANGED
|
@@ -187,6 +187,7 @@ function normalizeConfig(providedConfig) {
|
|
|
187
187
|
},
|
|
188
188
|
performanceLoggingEnabled: false,
|
|
189
189
|
heuristicPopupSearchTimeout: 100,
|
|
190
|
+
enableHeuristicHitTest: false,
|
|
190
191
|
heuristicMode: "off"
|
|
191
192
|
// heuristic disabled by default
|
|
192
193
|
};
|
|
@@ -1362,28 +1363,70 @@ function classifyButtonTextRegex(buttonText) {
|
|
|
1362
1363
|
// lib/heuristics.ts
|
|
1363
1364
|
var BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
|
|
1364
1365
|
var POPUP_SEARCH_MAX_TIME = 100;
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
return popups.sort((a, b) => (a
|
|
1366
|
+
var ACCEPTED_LEVELS = {
|
|
1367
|
+
off: [],
|
|
1368
|
+
reject: ["reject"],
|
|
1369
|
+
tier1: ["reject", "tier1"],
|
|
1370
|
+
tier2: ["reject", "tier1", "tier2"]
|
|
1371
|
+
};
|
|
1372
|
+
function getActionablePopups(mode = "reject", timeout = POPUP_SEARCH_MAX_TIME, hitTest = false) {
|
|
1373
|
+
const acceptedLevels = ACCEPTED_LEVELS[mode];
|
|
1374
|
+
const isActionable = (popup) => acceptedLevels.includes(popup.regexClassification) && isConsentPopup(popup);
|
|
1375
|
+
const popups = searchPopups(timeout, hitTest, DEFAULT_HIT_TEST_GRID, true).filter(isActionable);
|
|
1376
|
+
return popups.sort((a, b) => classificationRank(a) - classificationRank(b));
|
|
1376
1377
|
}
|
|
1377
|
-
function
|
|
1378
|
+
function searchPopups(timeout, hitTest, hitTestGrid, stopOnReject = false) {
|
|
1378
1379
|
if (!isTopFrame()) {
|
|
1379
|
-
|
|
1380
|
-
return [];
|
|
1381
|
-
}
|
|
1382
|
-
const frameElement = getFramePopupElement(document);
|
|
1383
|
-
const popup = frameElement && toPopupData(frameElement);
|
|
1384
|
-
return popup ? [popup] : [];
|
|
1380
|
+
return getFramePopups();
|
|
1385
1381
|
}
|
|
1386
1382
|
const deadline = performance.now() + timeout;
|
|
1383
|
+
const hitTestPopups = hitTest ? findPopupsByHitTest(deadline, hitTestGrid) : [];
|
|
1384
|
+
if (stopOnReject && hitTestPopups.some((p) => p.regexClassification === "reject" && isConsentPopup(p))) {
|
|
1385
|
+
return hitTestPopups;
|
|
1386
|
+
}
|
|
1387
|
+
const walkPopups = collectPositionedPopups(deadline);
|
|
1388
|
+
return [...walkPopups, ...mergeWalkOverlaps(hitTestPopups, walkPopups)];
|
|
1389
|
+
}
|
|
1390
|
+
var consentPopups = /* @__PURE__ */ new WeakMap();
|
|
1391
|
+
function classificationRank(popup) {
|
|
1392
|
+
return ACCEPTED_LEVELS.tier2.indexOf(popup.regexClassification);
|
|
1393
|
+
}
|
|
1394
|
+
function isConsentPopup(popup) {
|
|
1395
|
+
if (!ACCEPTED_LEVELS.tier2.includes(popup.regexClassification)) {
|
|
1396
|
+
return false;
|
|
1397
|
+
}
|
|
1398
|
+
let result = consentPopups.get(popup);
|
|
1399
|
+
if (result === void 0) {
|
|
1400
|
+
const popupText = popup.text.trim();
|
|
1401
|
+
result = !!popupText && !isExcludedPopup(popupText) && hasHeuristicPatterns(popupText);
|
|
1402
|
+
consentPopups.set(popup, result);
|
|
1403
|
+
}
|
|
1404
|
+
return result;
|
|
1405
|
+
}
|
|
1406
|
+
function mergeWalkOverlaps(hitTestPopups, walkPopups) {
|
|
1407
|
+
return hitTestPopups.filter((h) => {
|
|
1408
|
+
const hitIsConsent = isConsentPopup(h);
|
|
1409
|
+
const absorbs = (p) => !hitIsConsent || isConsentPopup(p) && classificationRank(p) <= classificationRank(h);
|
|
1410
|
+
const walkPopup = walkPopups.find((p) => composedOverlaps(p.element, h.element) && absorbs(p));
|
|
1411
|
+
if (walkPopup) {
|
|
1412
|
+
(walkPopup.hitPoints ??= []).push(...h.hitPoints ?? []);
|
|
1413
|
+
}
|
|
1414
|
+
return !walkPopup;
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
function getFramePopups() {
|
|
1418
|
+
if (window.parent && window.parent !== window.top) {
|
|
1419
|
+
return [];
|
|
1420
|
+
}
|
|
1421
|
+
const frameElement = getFramePopupElement(document);
|
|
1422
|
+
const popup = frameElement && toPopupData(frameElement);
|
|
1423
|
+
if (!popup) {
|
|
1424
|
+
return [];
|
|
1425
|
+
}
|
|
1426
|
+
popup.source = "frame";
|
|
1427
|
+
return [popup];
|
|
1428
|
+
}
|
|
1429
|
+
function collectPositionedPopups(deadline) {
|
|
1387
1430
|
const { elements, shadowHosts } = getPopupLikeElements(deadline);
|
|
1388
1431
|
const potentialPopups = [];
|
|
1389
1432
|
const expanded = [];
|
|
@@ -1399,17 +1442,86 @@ function getPotentialPopups(timeout = POPUP_SEARCH_MAX_TIME) {
|
|
|
1399
1442
|
if (root !== popup) {
|
|
1400
1443
|
expanded.push(root.element);
|
|
1401
1444
|
}
|
|
1445
|
+
root.source = "walk";
|
|
1402
1446
|
potentialPopups.push(root);
|
|
1403
1447
|
}
|
|
1404
1448
|
return potentialPopups.filter((p) => !expanded.some((root) => root !== p.element && composedContains(root, p.element)));
|
|
1405
1449
|
}
|
|
1450
|
+
var DEFAULT_HIT_TEST_GRID = {
|
|
1451
|
+
columns: [0.1, 0.5, 0.9],
|
|
1452
|
+
// banners sit along the top and bottom edges and modals in the middle; rows 150px in added almost nothing in crawls
|
|
1453
|
+
edgeRows: [30, 90]
|
|
1454
|
+
};
|
|
1455
|
+
function findPopupsByHitTest(deadline, grid) {
|
|
1456
|
+
const climbed = /* @__PURE__ */ new Map();
|
|
1457
|
+
const found = [];
|
|
1458
|
+
const toPopup = (el) => el.querySelector(TEXT_ENTRY_SELECTOR) ? null : toPopupRoot(el);
|
|
1459
|
+
for (const [x, y] of getHitTestPoints(grid)) {
|
|
1460
|
+
if (performance.now() > deadline) {
|
|
1461
|
+
return found;
|
|
1462
|
+
}
|
|
1463
|
+
const path = [];
|
|
1464
|
+
let reached = null;
|
|
1465
|
+
const shouldStop = (el) => {
|
|
1466
|
+
if (climbed.has(el)) {
|
|
1467
|
+
reached = climbed.get(el) ?? null;
|
|
1468
|
+
return true;
|
|
1469
|
+
}
|
|
1470
|
+
const overlapped = found.find((p) => composedOverlaps(p.element, el));
|
|
1471
|
+
if (overlapped) {
|
|
1472
|
+
reached = composedContains(overlapped.element, el) ? overlapped : null;
|
|
1473
|
+
return true;
|
|
1474
|
+
}
|
|
1475
|
+
path.push(el);
|
|
1476
|
+
return false;
|
|
1477
|
+
};
|
|
1478
|
+
const hit = closestHTMLElement(deepElementFromPoint(x, y));
|
|
1479
|
+
const popup = climbToPopup(hit, MAX_HIT_TEST_CLIMB_DEPTH, toPopup, shouldStop);
|
|
1480
|
+
if (popup) {
|
|
1481
|
+
popup.source = "hitTest";
|
|
1482
|
+
found.push(popup);
|
|
1483
|
+
reached = popup;
|
|
1484
|
+
}
|
|
1485
|
+
for (const el of path) {
|
|
1486
|
+
climbed.set(el, reached);
|
|
1487
|
+
}
|
|
1488
|
+
if (reached) {
|
|
1489
|
+
(reached.hitPoints ??= []).push([x, y]);
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
return found;
|
|
1493
|
+
}
|
|
1494
|
+
function getHitTestPoints(grid) {
|
|
1495
|
+
const { innerWidth: width, innerHeight: height } = window;
|
|
1496
|
+
const rows = [...grid.edgeRows, height / 2, ...grid.edgeRows.map((y) => height - y)].map((y) => Math.round(y));
|
|
1497
|
+
const ys = [...new Set(rows)].filter((y) => y >= 0 && y < height);
|
|
1498
|
+
return ys.flatMap((y) => grid.columns.map((column) => [Math.round(column * width), y]));
|
|
1499
|
+
}
|
|
1500
|
+
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"]';
|
|
1501
|
+
function deepElementFromPoint(x, y) {
|
|
1502
|
+
let el = document.elementFromPoint(x, y);
|
|
1503
|
+
while (el?.shadowRoot) {
|
|
1504
|
+
const inner = el.shadowRoot.elementFromPoint(x, y);
|
|
1505
|
+
if (!inner || inner === el) {
|
|
1506
|
+
break;
|
|
1507
|
+
}
|
|
1508
|
+
el = inner;
|
|
1509
|
+
}
|
|
1510
|
+
return el;
|
|
1511
|
+
}
|
|
1512
|
+
function closestHTMLElement(el) {
|
|
1513
|
+
while (el && !(el instanceof HTMLElement)) {
|
|
1514
|
+
el = el.parentElement;
|
|
1515
|
+
}
|
|
1516
|
+
return el;
|
|
1517
|
+
}
|
|
1406
1518
|
function toPopupData(el, shadowHosts, text = getElementText(el)) {
|
|
1407
1519
|
if (!text) {
|
|
1408
1520
|
return null;
|
|
1409
1521
|
}
|
|
1410
1522
|
const buttons = getButtonData(el, shadowHosts);
|
|
1411
1523
|
classifyButtons(buttons);
|
|
1412
|
-
return { text, element: el, buttons };
|
|
1524
|
+
return { text, element: el, buttons, regexClassification: classifyPopup(buttons) };
|
|
1413
1525
|
}
|
|
1414
1526
|
function hasPopupButtons(text, buttons) {
|
|
1415
1527
|
return !isOnlyButtonText(text, buttons) && buttons.some((b) => b.regexClassification !== "other");
|
|
@@ -1421,26 +1533,34 @@ function isOnlyButtonText(text, buttons) {
|
|
|
1421
1533
|
}
|
|
1422
1534
|
return rest.replace(/\s+/g, "").length < 10;
|
|
1423
1535
|
}
|
|
1536
|
+
function toPopupRoot(el, shadowHosts) {
|
|
1537
|
+
const text = getElementText(el);
|
|
1538
|
+
if (!text || !hasHeuristicPatterns(text)) {
|
|
1539
|
+
return null;
|
|
1540
|
+
}
|
|
1541
|
+
const popup = toPopupData(el, shadowHosts, text);
|
|
1542
|
+
return popup && hasPopupButtons(text, popup.buttons) ? popup : null;
|
|
1543
|
+
}
|
|
1424
1544
|
var MAX_FRAGMENT_EXPANSION_DEPTH = 6;
|
|
1545
|
+
var MAX_HIT_TEST_CLIMB_DEPTH = 8;
|
|
1546
|
+
function climbToPopup(el, maxDepth, toPopup, shouldStop) {
|
|
1547
|
+
for (let depth = 0; el && depth < maxDepth; depth++, el = getComposedParent(el)) {
|
|
1548
|
+
if (shouldStop?.(el) || el === document.body || el === document.documentElement || el.getBoundingClientRect().height > window.innerHeight) {
|
|
1549
|
+
return null;
|
|
1550
|
+
}
|
|
1551
|
+
const popup = toPopup(el);
|
|
1552
|
+
if (popup) {
|
|
1553
|
+
return popup;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
return null;
|
|
1557
|
+
}
|
|
1425
1558
|
function expandPopupFragment(popup, shadowHosts) {
|
|
1426
1559
|
if (hasHeuristicPatterns(popup.text) && hasPopupButtons(popup.text, popup.buttons)) {
|
|
1427
1560
|
return popup;
|
|
1428
1561
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
if (el === document.body || el === document.documentElement || el.getBoundingClientRect().height > window.innerHeight) {
|
|
1432
|
-
break;
|
|
1433
|
-
}
|
|
1434
|
-
const text = getElementText(el);
|
|
1435
|
-
if (!text || !hasHeuristicPatterns(text)) {
|
|
1436
|
-
continue;
|
|
1437
|
-
}
|
|
1438
|
-
const root = toPopupData(el, shadowHosts, text);
|
|
1439
|
-
if (root && hasPopupButtons(text, root.buttons)) {
|
|
1440
|
-
return root;
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
return popup;
|
|
1562
|
+
const parent = getComposedParent(popup.element);
|
|
1563
|
+
return climbToPopup(parent, MAX_FRAGMENT_EXPANSION_DEPTH, (el) => toPopupRoot(el, shadowHosts)) ?? popup;
|
|
1444
1564
|
}
|
|
1445
1565
|
function getComposedParent(el) {
|
|
1446
1566
|
if (el.parentElement) {
|
|
@@ -1471,6 +1591,9 @@ function getElementText(el) {
|
|
|
1471
1591
|
function isRenderedTextElement(el) {
|
|
1472
1592
|
return el instanceof HTMLElement && !["STYLE", "SCRIPT", "TEMPLATE", "NOSCRIPT", "LINK", "SLOT"].includes(el.tagName) && window.getComputedStyle(el).display !== "none";
|
|
1473
1593
|
}
|
|
1594
|
+
function composedOverlaps(a, b) {
|
|
1595
|
+
return composedContains(a, b) || composedContains(b, a);
|
|
1596
|
+
}
|
|
1474
1597
|
function composedContains(container, node) {
|
|
1475
1598
|
if (container.contains(node)) {
|
|
1476
1599
|
return true;
|
|
@@ -1962,7 +2085,11 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
|
|
|
1962
2085
|
async detectCmp() {
|
|
1963
2086
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1964
2087
|
this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorStart");
|
|
1965
|
-
this.popups = getActionablePopups(
|
|
2088
|
+
this.popups = getActionablePopups(
|
|
2089
|
+
this.mode,
|
|
2090
|
+
this.autoconsent.config.heuristicPopupSearchTimeout,
|
|
2091
|
+
this.autoconsent.config.enableHeuristicHitTest
|
|
2092
|
+
);
|
|
1966
2093
|
this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorEnd");
|
|
1967
2094
|
this.autoconsent.config.performanceLoggingEnabled && performance.measure("heuristicDetector", "heuristicDetectorStart", "heuristicDetectorEnd");
|
|
1968
2095
|
if (this.popups.length > 0) {
|
package/dist/autoconsent.esm.js
CHANGED
|
@@ -157,6 +157,7 @@ function normalizeConfig(providedConfig) {
|
|
|
157
157
|
},
|
|
158
158
|
performanceLoggingEnabled: false,
|
|
159
159
|
heuristicPopupSearchTimeout: 100,
|
|
160
|
+
enableHeuristicHitTest: false,
|
|
160
161
|
heuristicMode: "off"
|
|
161
162
|
// heuristic disabled by default
|
|
162
163
|
};
|
|
@@ -1332,28 +1333,70 @@ function classifyButtonTextRegex(buttonText) {
|
|
|
1332
1333
|
// lib/heuristics.ts
|
|
1333
1334
|
var BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
|
|
1334
1335
|
var POPUP_SEARCH_MAX_TIME = 100;
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
return popups.sort((a, b) => (a
|
|
1336
|
+
var ACCEPTED_LEVELS = {
|
|
1337
|
+
off: [],
|
|
1338
|
+
reject: ["reject"],
|
|
1339
|
+
tier1: ["reject", "tier1"],
|
|
1340
|
+
tier2: ["reject", "tier1", "tier2"]
|
|
1341
|
+
};
|
|
1342
|
+
function getActionablePopups(mode = "reject", timeout = POPUP_SEARCH_MAX_TIME, hitTest = false) {
|
|
1343
|
+
const acceptedLevels = ACCEPTED_LEVELS[mode];
|
|
1344
|
+
const isActionable = (popup) => acceptedLevels.includes(popup.regexClassification) && isConsentPopup(popup);
|
|
1345
|
+
const popups = searchPopups(timeout, hitTest, DEFAULT_HIT_TEST_GRID, true).filter(isActionable);
|
|
1346
|
+
return popups.sort((a, b) => classificationRank(a) - classificationRank(b));
|
|
1346
1347
|
}
|
|
1347
|
-
function
|
|
1348
|
+
function searchPopups(timeout, hitTest, hitTestGrid, stopOnReject = false) {
|
|
1348
1349
|
if (!isTopFrame()) {
|
|
1349
|
-
|
|
1350
|
-
return [];
|
|
1351
|
-
}
|
|
1352
|
-
const frameElement = getFramePopupElement(document);
|
|
1353
|
-
const popup = frameElement && toPopupData(frameElement);
|
|
1354
|
-
return popup ? [popup] : [];
|
|
1350
|
+
return getFramePopups();
|
|
1355
1351
|
}
|
|
1356
1352
|
const deadline = performance.now() + timeout;
|
|
1353
|
+
const hitTestPopups = hitTest ? findPopupsByHitTest(deadline, hitTestGrid) : [];
|
|
1354
|
+
if (stopOnReject && hitTestPopups.some((p) => p.regexClassification === "reject" && isConsentPopup(p))) {
|
|
1355
|
+
return hitTestPopups;
|
|
1356
|
+
}
|
|
1357
|
+
const walkPopups = collectPositionedPopups(deadline);
|
|
1358
|
+
return [...walkPopups, ...mergeWalkOverlaps(hitTestPopups, walkPopups)];
|
|
1359
|
+
}
|
|
1360
|
+
var consentPopups = /* @__PURE__ */ new WeakMap();
|
|
1361
|
+
function classificationRank(popup) {
|
|
1362
|
+
return ACCEPTED_LEVELS.tier2.indexOf(popup.regexClassification);
|
|
1363
|
+
}
|
|
1364
|
+
function isConsentPopup(popup) {
|
|
1365
|
+
if (!ACCEPTED_LEVELS.tier2.includes(popup.regexClassification)) {
|
|
1366
|
+
return false;
|
|
1367
|
+
}
|
|
1368
|
+
let result = consentPopups.get(popup);
|
|
1369
|
+
if (result === void 0) {
|
|
1370
|
+
const popupText = popup.text.trim();
|
|
1371
|
+
result = !!popupText && !isExcludedPopup(popupText) && hasHeuristicPatterns(popupText);
|
|
1372
|
+
consentPopups.set(popup, result);
|
|
1373
|
+
}
|
|
1374
|
+
return result;
|
|
1375
|
+
}
|
|
1376
|
+
function mergeWalkOverlaps(hitTestPopups, walkPopups) {
|
|
1377
|
+
return hitTestPopups.filter((h) => {
|
|
1378
|
+
const hitIsConsent = isConsentPopup(h);
|
|
1379
|
+
const absorbs = (p) => !hitIsConsent || isConsentPopup(p) && classificationRank(p) <= classificationRank(h);
|
|
1380
|
+
const walkPopup = walkPopups.find((p) => composedOverlaps(p.element, h.element) && absorbs(p));
|
|
1381
|
+
if (walkPopup) {
|
|
1382
|
+
(walkPopup.hitPoints ??= []).push(...h.hitPoints ?? []);
|
|
1383
|
+
}
|
|
1384
|
+
return !walkPopup;
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
function getFramePopups() {
|
|
1388
|
+
if (window.parent && window.parent !== window.top) {
|
|
1389
|
+
return [];
|
|
1390
|
+
}
|
|
1391
|
+
const frameElement = getFramePopupElement(document);
|
|
1392
|
+
const popup = frameElement && toPopupData(frameElement);
|
|
1393
|
+
if (!popup) {
|
|
1394
|
+
return [];
|
|
1395
|
+
}
|
|
1396
|
+
popup.source = "frame";
|
|
1397
|
+
return [popup];
|
|
1398
|
+
}
|
|
1399
|
+
function collectPositionedPopups(deadline) {
|
|
1357
1400
|
const { elements, shadowHosts } = getPopupLikeElements(deadline);
|
|
1358
1401
|
const potentialPopups = [];
|
|
1359
1402
|
const expanded = [];
|
|
@@ -1369,17 +1412,86 @@ function getPotentialPopups(timeout = POPUP_SEARCH_MAX_TIME) {
|
|
|
1369
1412
|
if (root !== popup) {
|
|
1370
1413
|
expanded.push(root.element);
|
|
1371
1414
|
}
|
|
1415
|
+
root.source = "walk";
|
|
1372
1416
|
potentialPopups.push(root);
|
|
1373
1417
|
}
|
|
1374
1418
|
return potentialPopups.filter((p) => !expanded.some((root) => root !== p.element && composedContains(root, p.element)));
|
|
1375
1419
|
}
|
|
1420
|
+
var DEFAULT_HIT_TEST_GRID = {
|
|
1421
|
+
columns: [0.1, 0.5, 0.9],
|
|
1422
|
+
// banners sit along the top and bottom edges and modals in the middle; rows 150px in added almost nothing in crawls
|
|
1423
|
+
edgeRows: [30, 90]
|
|
1424
|
+
};
|
|
1425
|
+
function findPopupsByHitTest(deadline, grid) {
|
|
1426
|
+
const climbed = /* @__PURE__ */ new Map();
|
|
1427
|
+
const found = [];
|
|
1428
|
+
const toPopup = (el) => el.querySelector(TEXT_ENTRY_SELECTOR) ? null : toPopupRoot(el);
|
|
1429
|
+
for (const [x, y] of getHitTestPoints(grid)) {
|
|
1430
|
+
if (performance.now() > deadline) {
|
|
1431
|
+
return found;
|
|
1432
|
+
}
|
|
1433
|
+
const path = [];
|
|
1434
|
+
let reached = null;
|
|
1435
|
+
const shouldStop = (el) => {
|
|
1436
|
+
if (climbed.has(el)) {
|
|
1437
|
+
reached = climbed.get(el) ?? null;
|
|
1438
|
+
return true;
|
|
1439
|
+
}
|
|
1440
|
+
const overlapped = found.find((p) => composedOverlaps(p.element, el));
|
|
1441
|
+
if (overlapped) {
|
|
1442
|
+
reached = composedContains(overlapped.element, el) ? overlapped : null;
|
|
1443
|
+
return true;
|
|
1444
|
+
}
|
|
1445
|
+
path.push(el);
|
|
1446
|
+
return false;
|
|
1447
|
+
};
|
|
1448
|
+
const hit = closestHTMLElement(deepElementFromPoint(x, y));
|
|
1449
|
+
const popup = climbToPopup(hit, MAX_HIT_TEST_CLIMB_DEPTH, toPopup, shouldStop);
|
|
1450
|
+
if (popup) {
|
|
1451
|
+
popup.source = "hitTest";
|
|
1452
|
+
found.push(popup);
|
|
1453
|
+
reached = popup;
|
|
1454
|
+
}
|
|
1455
|
+
for (const el of path) {
|
|
1456
|
+
climbed.set(el, reached);
|
|
1457
|
+
}
|
|
1458
|
+
if (reached) {
|
|
1459
|
+
(reached.hitPoints ??= []).push([x, y]);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
return found;
|
|
1463
|
+
}
|
|
1464
|
+
function getHitTestPoints(grid) {
|
|
1465
|
+
const { innerWidth: width, innerHeight: height } = window;
|
|
1466
|
+
const rows = [...grid.edgeRows, height / 2, ...grid.edgeRows.map((y) => height - y)].map((y) => Math.round(y));
|
|
1467
|
+
const ys = [...new Set(rows)].filter((y) => y >= 0 && y < height);
|
|
1468
|
+
return ys.flatMap((y) => grid.columns.map((column) => [Math.round(column * width), y]));
|
|
1469
|
+
}
|
|
1470
|
+
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"]';
|
|
1471
|
+
function deepElementFromPoint(x, y) {
|
|
1472
|
+
let el = document.elementFromPoint(x, y);
|
|
1473
|
+
while (el?.shadowRoot) {
|
|
1474
|
+
const inner = el.shadowRoot.elementFromPoint(x, y);
|
|
1475
|
+
if (!inner || inner === el) {
|
|
1476
|
+
break;
|
|
1477
|
+
}
|
|
1478
|
+
el = inner;
|
|
1479
|
+
}
|
|
1480
|
+
return el;
|
|
1481
|
+
}
|
|
1482
|
+
function closestHTMLElement(el) {
|
|
1483
|
+
while (el && !(el instanceof HTMLElement)) {
|
|
1484
|
+
el = el.parentElement;
|
|
1485
|
+
}
|
|
1486
|
+
return el;
|
|
1487
|
+
}
|
|
1376
1488
|
function toPopupData(el, shadowHosts, text = getElementText(el)) {
|
|
1377
1489
|
if (!text) {
|
|
1378
1490
|
return null;
|
|
1379
1491
|
}
|
|
1380
1492
|
const buttons = getButtonData(el, shadowHosts);
|
|
1381
1493
|
classifyButtons(buttons);
|
|
1382
|
-
return { text, element: el, buttons };
|
|
1494
|
+
return { text, element: el, buttons, regexClassification: classifyPopup(buttons) };
|
|
1383
1495
|
}
|
|
1384
1496
|
function hasPopupButtons(text, buttons) {
|
|
1385
1497
|
return !isOnlyButtonText(text, buttons) && buttons.some((b) => b.regexClassification !== "other");
|
|
@@ -1391,26 +1503,34 @@ function isOnlyButtonText(text, buttons) {
|
|
|
1391
1503
|
}
|
|
1392
1504
|
return rest.replace(/\s+/g, "").length < 10;
|
|
1393
1505
|
}
|
|
1506
|
+
function toPopupRoot(el, shadowHosts) {
|
|
1507
|
+
const text = getElementText(el);
|
|
1508
|
+
if (!text || !hasHeuristicPatterns(text)) {
|
|
1509
|
+
return null;
|
|
1510
|
+
}
|
|
1511
|
+
const popup = toPopupData(el, shadowHosts, text);
|
|
1512
|
+
return popup && hasPopupButtons(text, popup.buttons) ? popup : null;
|
|
1513
|
+
}
|
|
1394
1514
|
var MAX_FRAGMENT_EXPANSION_DEPTH = 6;
|
|
1515
|
+
var MAX_HIT_TEST_CLIMB_DEPTH = 8;
|
|
1516
|
+
function climbToPopup(el, maxDepth, toPopup, shouldStop) {
|
|
1517
|
+
for (let depth = 0; el && depth < maxDepth; depth++, el = getComposedParent(el)) {
|
|
1518
|
+
if (shouldStop?.(el) || el === document.body || el === document.documentElement || el.getBoundingClientRect().height > window.innerHeight) {
|
|
1519
|
+
return null;
|
|
1520
|
+
}
|
|
1521
|
+
const popup = toPopup(el);
|
|
1522
|
+
if (popup) {
|
|
1523
|
+
return popup;
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
return null;
|
|
1527
|
+
}
|
|
1395
1528
|
function expandPopupFragment(popup, shadowHosts) {
|
|
1396
1529
|
if (hasHeuristicPatterns(popup.text) && hasPopupButtons(popup.text, popup.buttons)) {
|
|
1397
1530
|
return popup;
|
|
1398
1531
|
}
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
if (el === document.body || el === document.documentElement || el.getBoundingClientRect().height > window.innerHeight) {
|
|
1402
|
-
break;
|
|
1403
|
-
}
|
|
1404
|
-
const text = getElementText(el);
|
|
1405
|
-
if (!text || !hasHeuristicPatterns(text)) {
|
|
1406
|
-
continue;
|
|
1407
|
-
}
|
|
1408
|
-
const root = toPopupData(el, shadowHosts, text);
|
|
1409
|
-
if (root && hasPopupButtons(text, root.buttons)) {
|
|
1410
|
-
return root;
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
return popup;
|
|
1532
|
+
const parent = getComposedParent(popup.element);
|
|
1533
|
+
return climbToPopup(parent, MAX_FRAGMENT_EXPANSION_DEPTH, (el) => toPopupRoot(el, shadowHosts)) ?? popup;
|
|
1414
1534
|
}
|
|
1415
1535
|
function getComposedParent(el) {
|
|
1416
1536
|
if (el.parentElement) {
|
|
@@ -1441,6 +1561,9 @@ function getElementText(el) {
|
|
|
1441
1561
|
function isRenderedTextElement(el) {
|
|
1442
1562
|
return el instanceof HTMLElement && !["STYLE", "SCRIPT", "TEMPLATE", "NOSCRIPT", "LINK", "SLOT"].includes(el.tagName) && window.getComputedStyle(el).display !== "none";
|
|
1443
1563
|
}
|
|
1564
|
+
function composedOverlaps(a, b) {
|
|
1565
|
+
return composedContains(a, b) || composedContains(b, a);
|
|
1566
|
+
}
|
|
1444
1567
|
function composedContains(container, node) {
|
|
1445
1568
|
if (container.contains(node)) {
|
|
1446
1569
|
return true;
|
|
@@ -1932,7 +2055,11 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
|
|
|
1932
2055
|
async detectCmp() {
|
|
1933
2056
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1934
2057
|
this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorStart");
|
|
1935
|
-
this.popups = getActionablePopups(
|
|
2058
|
+
this.popups = getActionablePopups(
|
|
2059
|
+
this.mode,
|
|
2060
|
+
this.autoconsent.config.heuristicPopupSearchTimeout,
|
|
2061
|
+
this.autoconsent.config.enableHeuristicHitTest
|
|
2062
|
+
);
|
|
1936
2063
|
this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorEnd");
|
|
1937
2064
|
this.autoconsent.config.performanceLoggingEnabled && performance.measure("heuristicDetector", "heuristicDetectorStart", "heuristicDetectorEnd");
|
|
1938
2065
|
if (this.popups.length > 0) {
|