@duckduckgo/autoconsent 16.62.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 +84 -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 +176 -36
- 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 +176 -36
- 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 +176 -36
- package/dist/autoconsent.esm.js +176 -36
- package/dist/autoconsent.playwright.js +179 -36
- package/dist/autoconsent.standalone.js +177 -37
- package/dist/heuristics.cjs.js +14 -1
- package/dist/heuristics.mjs +14 -1
- 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/heuristic-patterns.ts +17 -1
- package/lib/heuristics.ts +240 -45
- package/lib/types.ts +16 -1
- package/lib/utils.ts +1 -0
- package/package.json +2 -2
- package/readme.md +1 -1
- package/rules/autoconsent/appconsent-legacy.json +22 -1
- package/rules/autoconsent/asrock.json +44 -0
- package/rules/autoconsent/beetle-tracking.json +22 -0
- package/rules/autoconsent/casier-judiciaire-justice-gouv-fr.json +40 -0
- package/rules/autoconsent/civilservicejobs-service-gov-uk.json +38 -0
- package/rules/autoconsent/ckies.json +1 -1
- package/rules/autoconsent/consentmo.json +5 -2
- package/rules/autoconsent/cookie-script.json +34 -15
- package/rules/autoconsent/cookiesinfo-closecookies.json +9 -0
- package/rules/autoconsent/dack365.json +35 -0
- package/rules/autoconsent/epaas.json +18 -1
- package/rules/autoconsent/eu-cookie-law-popup.json +33 -0
- package/rules/autoconsent/fascicolosanitario-lombardia.json +14 -0
- package/rules/autoconsent/fnsp-cookieconsent.json +30 -0
- package/rules/autoconsent/fod-fujitv.json +13 -0
- package/rules/autoconsent/fundingchoices.json +12 -2
- package/rules/autoconsent/hkdir.json +33 -0
- package/rules/autoconsent/hyrabostad.json +21 -0
- package/rules/autoconsent/insided.json +15 -0
- package/rules/autoconsent/jokul-cookie-consent.json +28 -0
- package/rules/autoconsent/kartverket.json +46 -0
- package/rules/autoconsent/lego.json +27 -5
- package/rules/autoconsent/luxmed.json +33 -0
- package/rules/autoconsent/mars-cookies-prompt.json +20 -0
- package/rules/autoconsent/meta-cookie-dialog.json +37 -0
- package/rules/autoconsent/mittanbud.json +33 -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/poki.json +37 -0
- package/rules/autoconsent/polisen-se.json +26 -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/romeo.json +47 -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/stockholm-stad.json +42 -0
- package/rules/autoconsent/stright.json +19 -1
- package/rules/autoconsent/stripchat.com.json +26 -5
- package/rules/autoconsent/tri-table.json +6 -2
- package/rules/autoconsent/tvp.json +27 -0
- package/rules/autoconsent/urvaerket.json +33 -0
- package/rules/autoconsent/vinnova-se.json +50 -0
- package/rules/autoconsent/volvocars-com.json +23 -2
- package/rules/autoconsent/vortex-consent.json +33 -0
- package/rules/autoconsent/vue-cookie-accept-decline.json +42 -0
- package/rules/compact-rules.json +1 -1
- package/rules/rule-index.json +1 -1
- package/rules/rules.json +1 -1
- package/tests/appconsent-legacy.spec.ts +1 -1
- package/tests/asrock.spec.ts +3 -0
- package/tests/beetle-tracking.spec.ts +3 -0
- package/tests/casier-judiciaire-justice-gouv-fr.spec.ts +3 -0
- package/tests/civilservicejobs-service-gov-uk.spec.ts +3 -0
- package/tests/consentmo.spec.ts +2 -0
- package/tests/cookie-script.spec.ts +5 -0
- package/tests/cookiesinfo-closecookies.spec.ts +8 -0
- package/tests/dack365.spec.ts +3 -0
- package/tests/epaas.spec.ts +3 -0
- package/tests/eu-cookie-law-popup.spec.ts +8 -0
- package/tests/fascicolosanitario-lombardia.spec.ts +3 -0
- package/tests/fnsp-cookieconsent.spec.ts +9 -0
- package/tests/fod-fujitv.spec.ts +3 -0
- package/tests/fundingchoices.spec.ts +7 -1
- package/tests/hkdir.spec.ts +10 -0
- package/tests/hyrabostad.spec.ts +3 -0
- package/tests/insided.spec.ts +9 -0
- package/tests/jokul-cookie-consent.spec.ts +3 -0
- package/tests/kartverket.spec.ts +3 -0
- package/tests/luxmed.spec.ts +3 -0
- package/tests/mars-cookies-prompt.spec.ts +3 -0
- package/tests/meta-cookie-dialog.spec.ts +14 -0
- package/tests/mittanbud.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/poki.spec.ts +6 -0
- package/tests/polisen-se.spec.ts +3 -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/romeo.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/stockholm-stad.spec.ts +7 -0
- package/tests/stright.spec.ts +11 -0
- package/tests/stripchat.com.spec.ts +4 -1
- package/tests/tri-table.spec.ts +1 -1
- package/tests/tvp.spec.ts +3 -0
- package/tests/urvaerket.spec.ts +13 -0
- package/tests/vinnova-se.spec.ts +6 -0
- package/tests/volvocars-com.spec.ts +1 -1
- package/tests/vortex-consent.spec.ts +18 -0
- package/tests/vue-cookie-accept-decline.spec.ts +8 -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/tests-wtr/heuristics/heuristics-utils.test.ts +49 -0
- 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
|
};
|
|
@@ -798,7 +799,15 @@ var REJECT_PATTERNS_DANISH = [
|
|
|
798
799
|
// "nej tak" (no thanks)
|
|
799
800
|
/^nej(,?\s+tak)?$/is
|
|
800
801
|
];
|
|
801
|
-
var REJECT_PATTERNS_CATALAN = [
|
|
802
|
+
var REJECT_PATTERNS_CATALAN = [
|
|
803
|
+
/(^|\s)rebutj/is,
|
|
804
|
+
"no accepto",
|
|
805
|
+
"no, gr\xE0cies",
|
|
806
|
+
// accept/allow/use (only) necessary / essential / technical / functional
|
|
807
|
+
/^\s*(acceptar?|accepto|permetre|utilitzar|usar)?\s*(només|sols|únicament)?\s*(les\s+|els\s+)?(galetes\s+|cookies?\s+)?(estrictament\s+)?(necess[aà]ri\w*|essencial\w*|tècni\w*|funcional\w*|imprescindibles)\s*$/is,
|
|
808
|
+
// continue without accepting
|
|
809
|
+
/^continuar sense acceptar/is
|
|
810
|
+
];
|
|
802
811
|
var REJECT_PATTERNS_GALICIAN = [/(^|\s)rexeitar/is];
|
|
803
812
|
var REJECT_PATTERNS_BASQUE = [/(^|\s)(baztertu|ukatu)/is];
|
|
804
813
|
var REJECT_PATTERNS_PORTUGUESE = [/^aceitar apenas cookies essenciais\.$/];
|
|
@@ -956,6 +965,9 @@ var SETTINGS_PATTERNS = [
|
|
|
956
965
|
"configura-les",
|
|
957
966
|
"personalitza",
|
|
958
967
|
"veure prefer\xE8ncies",
|
|
968
|
+
/^(configuració|personalitzar|preferències|més opcions)$/,
|
|
969
|
+
/^(configuració|preferències|opcions)( de( les)?)? (galetes|cookies?)$/is,
|
|
970
|
+
/^(gestiona|gestionar|configura|configurar|personalitza|personalitzar|veure|establir|administrar)( les)?( meves)? (preferències|galetes|cookies?|opcions)$/is,
|
|
959
971
|
// Galician (GL)
|
|
960
972
|
"xestionar preferencias",
|
|
961
973
|
// Basque (EU)
|
|
@@ -1229,6 +1241,8 @@ var ACKNOWLEDGE_PATTERNS = [
|
|
|
1229
1241
|
"seguir",
|
|
1230
1242
|
"vale",
|
|
1231
1243
|
"\xA1vamos!",
|
|
1244
|
+
// Catalan (CA)
|
|
1245
|
+
/^(ho )?(entenc|entès|entesos)[ !.]*$/s,
|
|
1232
1246
|
// Polish (PL)
|
|
1233
1247
|
"kontynuuj",
|
|
1234
1248
|
"ok, zrozumia\u0142em",
|
|
@@ -1349,28 +1363,70 @@ function classifyButtonTextRegex(buttonText) {
|
|
|
1349
1363
|
// lib/heuristics.ts
|
|
1350
1364
|
var BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
|
|
1351
1365
|
var POPUP_SEARCH_MAX_TIME = 100;
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
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));
|
|
1363
1377
|
}
|
|
1364
|
-
function
|
|
1378
|
+
function searchPopups(timeout, hitTest, hitTestGrid, stopOnReject = false) {
|
|
1365
1379
|
if (!isTopFrame()) {
|
|
1366
|
-
|
|
1367
|
-
return [];
|
|
1368
|
-
}
|
|
1369
|
-
const frameElement = getFramePopupElement(document);
|
|
1370
|
-
const popup = frameElement && toPopupData(frameElement);
|
|
1371
|
-
return popup ? [popup] : [];
|
|
1380
|
+
return getFramePopups();
|
|
1372
1381
|
}
|
|
1373
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) {
|
|
1374
1430
|
const { elements, shadowHosts } = getPopupLikeElements(deadline);
|
|
1375
1431
|
const potentialPopups = [];
|
|
1376
1432
|
const expanded = [];
|
|
@@ -1386,17 +1442,86 @@ function getPotentialPopups(timeout = POPUP_SEARCH_MAX_TIME) {
|
|
|
1386
1442
|
if (root !== popup) {
|
|
1387
1443
|
expanded.push(root.element);
|
|
1388
1444
|
}
|
|
1445
|
+
root.source = "walk";
|
|
1389
1446
|
potentialPopups.push(root);
|
|
1390
1447
|
}
|
|
1391
1448
|
return potentialPopups.filter((p) => !expanded.some((root) => root !== p.element && composedContains(root, p.element)));
|
|
1392
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
|
+
}
|
|
1393
1518
|
function toPopupData(el, shadowHosts, text = getElementText(el)) {
|
|
1394
1519
|
if (!text) {
|
|
1395
1520
|
return null;
|
|
1396
1521
|
}
|
|
1397
1522
|
const buttons = getButtonData(el, shadowHosts);
|
|
1398
1523
|
classifyButtons(buttons);
|
|
1399
|
-
return { text, element: el, buttons };
|
|
1524
|
+
return { text, element: el, buttons, regexClassification: classifyPopup(buttons) };
|
|
1400
1525
|
}
|
|
1401
1526
|
function hasPopupButtons(text, buttons) {
|
|
1402
1527
|
return !isOnlyButtonText(text, buttons) && buttons.some((b) => b.regexClassification !== "other");
|
|
@@ -1408,26 +1533,34 @@ function isOnlyButtonText(text, buttons) {
|
|
|
1408
1533
|
}
|
|
1409
1534
|
return rest.replace(/\s+/g, "").length < 10;
|
|
1410
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
|
+
}
|
|
1411
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
|
+
}
|
|
1412
1558
|
function expandPopupFragment(popup, shadowHosts) {
|
|
1413
1559
|
if (hasHeuristicPatterns(popup.text) && hasPopupButtons(popup.text, popup.buttons)) {
|
|
1414
1560
|
return popup;
|
|
1415
1561
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
if (el === document.body || el === document.documentElement || el.getBoundingClientRect().height > window.innerHeight) {
|
|
1419
|
-
break;
|
|
1420
|
-
}
|
|
1421
|
-
const text = getElementText(el);
|
|
1422
|
-
if (!text || !hasHeuristicPatterns(text)) {
|
|
1423
|
-
continue;
|
|
1424
|
-
}
|
|
1425
|
-
const root = toPopupData(el, shadowHosts, text);
|
|
1426
|
-
if (root && hasPopupButtons(text, root.buttons)) {
|
|
1427
|
-
return root;
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
return popup;
|
|
1562
|
+
const parent = getComposedParent(popup.element);
|
|
1563
|
+
return climbToPopup(parent, MAX_FRAGMENT_EXPANSION_DEPTH, (el) => toPopupRoot(el, shadowHosts)) ?? popup;
|
|
1431
1564
|
}
|
|
1432
1565
|
function getComposedParent(el) {
|
|
1433
1566
|
if (el.parentElement) {
|
|
@@ -1458,6 +1591,9 @@ function getElementText(el) {
|
|
|
1458
1591
|
function isRenderedTextElement(el) {
|
|
1459
1592
|
return el instanceof HTMLElement && !["STYLE", "SCRIPT", "TEMPLATE", "NOSCRIPT", "LINK", "SLOT"].includes(el.tagName) && window.getComputedStyle(el).display !== "none";
|
|
1460
1593
|
}
|
|
1594
|
+
function composedOverlaps(a, b) {
|
|
1595
|
+
return composedContains(a, b) || composedContains(b, a);
|
|
1596
|
+
}
|
|
1461
1597
|
function composedContains(container, node) {
|
|
1462
1598
|
if (container.contains(node)) {
|
|
1463
1599
|
return true;
|
|
@@ -1949,7 +2085,11 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
|
|
|
1949
2085
|
async detectCmp() {
|
|
1950
2086
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1951
2087
|
this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorStart");
|
|
1952
|
-
this.popups = getActionablePopups(
|
|
2088
|
+
this.popups = getActionablePopups(
|
|
2089
|
+
this.mode,
|
|
2090
|
+
this.autoconsent.config.heuristicPopupSearchTimeout,
|
|
2091
|
+
this.autoconsent.config.enableHeuristicHitTest
|
|
2092
|
+
);
|
|
1953
2093
|
this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorEnd");
|
|
1954
2094
|
this.autoconsent.config.performanceLoggingEnabled && performance.measure("heuristicDetector", "heuristicDetectorStart", "heuristicDetectorEnd");
|
|
1955
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
|
};
|
|
@@ -768,7 +769,15 @@ var REJECT_PATTERNS_DANISH = [
|
|
|
768
769
|
// "nej tak" (no thanks)
|
|
769
770
|
/^nej(,?\s+tak)?$/is
|
|
770
771
|
];
|
|
771
|
-
var REJECT_PATTERNS_CATALAN = [
|
|
772
|
+
var REJECT_PATTERNS_CATALAN = [
|
|
773
|
+
/(^|\s)rebutj/is,
|
|
774
|
+
"no accepto",
|
|
775
|
+
"no, gr\xE0cies",
|
|
776
|
+
// accept/allow/use (only) necessary / essential / technical / functional
|
|
777
|
+
/^\s*(acceptar?|accepto|permetre|utilitzar|usar)?\s*(només|sols|únicament)?\s*(les\s+|els\s+)?(galetes\s+|cookies?\s+)?(estrictament\s+)?(necess[aà]ri\w*|essencial\w*|tècni\w*|funcional\w*|imprescindibles)\s*$/is,
|
|
778
|
+
// continue without accepting
|
|
779
|
+
/^continuar sense acceptar/is
|
|
780
|
+
];
|
|
772
781
|
var REJECT_PATTERNS_GALICIAN = [/(^|\s)rexeitar/is];
|
|
773
782
|
var REJECT_PATTERNS_BASQUE = [/(^|\s)(baztertu|ukatu)/is];
|
|
774
783
|
var REJECT_PATTERNS_PORTUGUESE = [/^aceitar apenas cookies essenciais\.$/];
|
|
@@ -926,6 +935,9 @@ var SETTINGS_PATTERNS = [
|
|
|
926
935
|
"configura-les",
|
|
927
936
|
"personalitza",
|
|
928
937
|
"veure prefer\xE8ncies",
|
|
938
|
+
/^(configuració|personalitzar|preferències|més opcions)$/,
|
|
939
|
+
/^(configuració|preferències|opcions)( de( les)?)? (galetes|cookies?)$/is,
|
|
940
|
+
/^(gestiona|gestionar|configura|configurar|personalitza|personalitzar|veure|establir|administrar)( les)?( meves)? (preferències|galetes|cookies?|opcions)$/is,
|
|
929
941
|
// Galician (GL)
|
|
930
942
|
"xestionar preferencias",
|
|
931
943
|
// Basque (EU)
|
|
@@ -1199,6 +1211,8 @@ var ACKNOWLEDGE_PATTERNS = [
|
|
|
1199
1211
|
"seguir",
|
|
1200
1212
|
"vale",
|
|
1201
1213
|
"\xA1vamos!",
|
|
1214
|
+
// Catalan (CA)
|
|
1215
|
+
/^(ho )?(entenc|entès|entesos)[ !.]*$/s,
|
|
1202
1216
|
// Polish (PL)
|
|
1203
1217
|
"kontynuuj",
|
|
1204
1218
|
"ok, zrozumia\u0142em",
|
|
@@ -1319,28 +1333,70 @@ function classifyButtonTextRegex(buttonText) {
|
|
|
1319
1333
|
// lib/heuristics.ts
|
|
1320
1334
|
var BUTTON_LIKE_ELEMENT_SELECTOR = 'button, input[type="button"], input[type="submit"], a, [role="button"], [class*="button"]';
|
|
1321
1335
|
var POPUP_SEARCH_MAX_TIME = 100;
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
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));
|
|
1333
1347
|
}
|
|
1334
|
-
function
|
|
1348
|
+
function searchPopups(timeout, hitTest, hitTestGrid, stopOnReject = false) {
|
|
1335
1349
|
if (!isTopFrame()) {
|
|
1336
|
-
|
|
1337
|
-
return [];
|
|
1338
|
-
}
|
|
1339
|
-
const frameElement = getFramePopupElement(document);
|
|
1340
|
-
const popup = frameElement && toPopupData(frameElement);
|
|
1341
|
-
return popup ? [popup] : [];
|
|
1350
|
+
return getFramePopups();
|
|
1342
1351
|
}
|
|
1343
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) {
|
|
1344
1400
|
const { elements, shadowHosts } = getPopupLikeElements(deadline);
|
|
1345
1401
|
const potentialPopups = [];
|
|
1346
1402
|
const expanded = [];
|
|
@@ -1356,17 +1412,86 @@ function getPotentialPopups(timeout = POPUP_SEARCH_MAX_TIME) {
|
|
|
1356
1412
|
if (root !== popup) {
|
|
1357
1413
|
expanded.push(root.element);
|
|
1358
1414
|
}
|
|
1415
|
+
root.source = "walk";
|
|
1359
1416
|
potentialPopups.push(root);
|
|
1360
1417
|
}
|
|
1361
1418
|
return potentialPopups.filter((p) => !expanded.some((root) => root !== p.element && composedContains(root, p.element)));
|
|
1362
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
|
+
}
|
|
1363
1488
|
function toPopupData(el, shadowHosts, text = getElementText(el)) {
|
|
1364
1489
|
if (!text) {
|
|
1365
1490
|
return null;
|
|
1366
1491
|
}
|
|
1367
1492
|
const buttons = getButtonData(el, shadowHosts);
|
|
1368
1493
|
classifyButtons(buttons);
|
|
1369
|
-
return { text, element: el, buttons };
|
|
1494
|
+
return { text, element: el, buttons, regexClassification: classifyPopup(buttons) };
|
|
1370
1495
|
}
|
|
1371
1496
|
function hasPopupButtons(text, buttons) {
|
|
1372
1497
|
return !isOnlyButtonText(text, buttons) && buttons.some((b) => b.regexClassification !== "other");
|
|
@@ -1378,26 +1503,34 @@ function isOnlyButtonText(text, buttons) {
|
|
|
1378
1503
|
}
|
|
1379
1504
|
return rest.replace(/\s+/g, "").length < 10;
|
|
1380
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
|
+
}
|
|
1381
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
|
+
}
|
|
1382
1528
|
function expandPopupFragment(popup, shadowHosts) {
|
|
1383
1529
|
if (hasHeuristicPatterns(popup.text) && hasPopupButtons(popup.text, popup.buttons)) {
|
|
1384
1530
|
return popup;
|
|
1385
1531
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
if (el === document.body || el === document.documentElement || el.getBoundingClientRect().height > window.innerHeight) {
|
|
1389
|
-
break;
|
|
1390
|
-
}
|
|
1391
|
-
const text = getElementText(el);
|
|
1392
|
-
if (!text || !hasHeuristicPatterns(text)) {
|
|
1393
|
-
continue;
|
|
1394
|
-
}
|
|
1395
|
-
const root = toPopupData(el, shadowHosts, text);
|
|
1396
|
-
if (root && hasPopupButtons(text, root.buttons)) {
|
|
1397
|
-
return root;
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
return popup;
|
|
1532
|
+
const parent = getComposedParent(popup.element);
|
|
1533
|
+
return climbToPopup(parent, MAX_FRAGMENT_EXPANSION_DEPTH, (el) => toPopupRoot(el, shadowHosts)) ?? popup;
|
|
1401
1534
|
}
|
|
1402
1535
|
function getComposedParent(el) {
|
|
1403
1536
|
if (el.parentElement) {
|
|
@@ -1428,6 +1561,9 @@ function getElementText(el) {
|
|
|
1428
1561
|
function isRenderedTextElement(el) {
|
|
1429
1562
|
return el instanceof HTMLElement && !["STYLE", "SCRIPT", "TEMPLATE", "NOSCRIPT", "LINK", "SLOT"].includes(el.tagName) && window.getComputedStyle(el).display !== "none";
|
|
1430
1563
|
}
|
|
1564
|
+
function composedOverlaps(a, b) {
|
|
1565
|
+
return composedContains(a, b) || composedContains(b, a);
|
|
1566
|
+
}
|
|
1431
1567
|
function composedContains(container, node) {
|
|
1432
1568
|
if (container.contains(node)) {
|
|
1433
1569
|
return true;
|
|
@@ -1919,7 +2055,11 @@ var AutoConsentHeuristicCMP = class extends AutoConsentCMPBase {
|
|
|
1919
2055
|
async detectCmp() {
|
|
1920
2056
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1921
2057
|
this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorStart");
|
|
1922
|
-
this.popups = getActionablePopups(
|
|
2058
|
+
this.popups = getActionablePopups(
|
|
2059
|
+
this.mode,
|
|
2060
|
+
this.autoconsent.config.heuristicPopupSearchTimeout,
|
|
2061
|
+
this.autoconsent.config.enableHeuristicHitTest
|
|
2062
|
+
);
|
|
1923
2063
|
this.autoconsent.config.performanceLoggingEnabled && performance.mark("heuristicDetectorEnd");
|
|
1924
2064
|
this.autoconsent.config.performanceLoggingEnabled && performance.measure("heuristicDetector", "heuristicDetectorStart", "heuristicDetectorEnd");
|
|
1925
2065
|
if (this.popups.length > 0) {
|