@duckduckgo/autoconsent 1.0.4 → 1.0.7
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/dist/autoconsent.cjs.js +97 -30
- package/dist/autoconsent.esm.js +97 -30
- package/dist/autoconsent.puppet.js +101 -7
- package/lib/cmps/all.js +2 -0
- package/lib/cmps/all.ts +2 -0
- package/lib/cmps/base.js +5 -1
- package/lib/cmps/base.ts +5 -1
- package/lib/cmps/onetrust.js +34 -0
- package/lib/cmps/onetrust.ts +47 -0
- package/lib/config.js +1 -0
- package/lib/config.ts +1 -0
- package/lib/detector.js +4 -0
- package/lib/detector.ts +4 -0
- package/lib/hider.js +1 -1
- package/lib/hider.ts +1 -1
- package/lib/messages.d.ts +3 -0
- package/lib/node.js +8 -3
- package/lib/node.ts +10 -5
- package/lib/puppet/tab.js +10 -3
- package/lib/puppet/tab.ts +13 -3
- package/lib/tabwrapper.js +6 -0
- package/lib/tabwrapper.ts +6 -0
- package/lib/types.d.ts +2 -2
- package/lib/web/content-utils.js +29 -0
- package/lib/web/content-utils.ts +31 -0
- package/lib/web/content.js +20 -22
- package/lib/web/content.ts +19 -22
- package/lib/web/tab.js +12 -6
- package/lib/web/tab.ts +13 -6
- package/lib/web.js +5 -0
- package/lib/web.ts +5 -0
- package/package.json +1 -1
- package/readme.md +1 -1
- package/rules/autoconsent/arzt-auskunft-de.json +1 -1
- package/rules/autoconsent/aws-amazon.json +1 -1
- package/rules/autoconsent/bundesregierung-de.json +1 -1
- package/rules/autoconsent/corona-in-zahlen-de.json +1 -1
- package/rules/autoconsent/deepl.json +1 -1
- package/rules/autoconsent/etsy.json +13 -0
- package/rules/autoconsent/hl-co-uk.json +1 -1
- package/rules/autoconsent/johnlewis.json +1 -1
- package/rules/autoconsent/metoffice-gov-uk.json +1 -1
- package/rules/autoconsent/microsoft.json +1 -1
- package/rules/autoconsent/moneysavingexpert.json +1 -1
- package/rules/autoconsent/national-lottery-co-uk.json +2 -3
- package/rules/autoconsent/netflix-de.json +1 -1
- package/rules/autoconsent/nhs.json +1 -1
- package/rules/autoconsent/steampowered.json +14 -7
- package/rules/autoconsent/thefreedictionary.json +1 -1
- package/rules/autoconsent/usercentrics-1.json +16 -5
- package/rules/autoconsent/vodafone-de.json +12 -9
- package/rules/autoconsent/xing.json +2 -2
- package/rules/rules.json +73 -73
- package/tests/arzt-auskunft.spec.ts +7 -0
- package/tests/aws.amazon.spec.ts +7 -0
- package/tests/baden-wuerttemberg.spec.ts +7 -0
- package/tests/borlabs.spec.ts +7 -0
- package/tests/bundesregierung.spec.ts +7 -0
- package/tests/consentmanager.spec.ts +2 -1
- package/tests/cookiebot.spec.ts +3 -3
- package/tests/cookieconsent.spec.ts +2 -1
- package/tests/cookielawinfo.spec.ts +0 -1
- package/tests/corona-in-zahlen.spec.ts +7 -0
- package/tests/deepl.spec.ts +7 -0
- package/tests/destatis.spec.ts +7 -0
- package/tests/etsy.spec.ts +7 -0
- package/tests/eu-cookie-compliance-banner.spec.ts +0 -1
- package/tests/hl-co-uk.spec.ts +7 -0
- package/tests/ionos.spec.ts +7 -0
- package/tests/johnlewis.spec.ts +7 -0
- package/tests/mediamarkt.spec.ts +7 -0
- package/tests/metoffice-gov-uk.spec.ts +7 -0
- package/tests/microsoft.spec.ts +9 -0
- package/tests/moneysavingexpert.spec.ts +7 -0
- package/tests/motor-talk.spec.ts +7 -0
- package/tests/national-lottery.spec.ts +7 -0
- package/tests/netflix.spec.ts +7 -0
- package/tests/nhs.spec.ts +7 -0
- package/tests/obi.spec.ts +7 -0
- package/tests/onetrust.spec.ts +6 -1
- package/tests/otto.spec.ts +7 -0
- package/tests/paypal.spec.ts +7 -0
- package/tests/runner.ts +2 -2
- package/tests/snigel.spec.ts +7 -0
- package/tests/steampowered.spec.ts +7 -0
- package/tests/thalia.spec.ts +7 -0
- package/tests/thefreedictionary.spec.ts +7 -0
- package/tests/usercentrics-1.spec.ts +9 -0
- package/tests/vodafone.spec.ts +7 -0
- package/tests/wordpressgdpr.spec.ts +0 -1
- package/tests/xing.spec.ts +8 -0
- package/rules/autoconsent/onetrust.json +0 -24
- package/tests/optanon.spec.ts +0 -10
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
+
const enableLogs = false; // change this to enable debug logs
|
|
6
|
+
|
|
5
7
|
/* eslint-disable no-restricted-syntax,no-await-in-loop,no-underscore-dangle */
|
|
6
8
|
async function waitFor(predicate, maxTimes, interval) {
|
|
7
9
|
let result = await predicate();
|
|
@@ -17,7 +19,7 @@ async function waitFor(predicate, maxTimes, interval) {
|
|
|
17
19
|
async function success(action) {
|
|
18
20
|
const result = await action;
|
|
19
21
|
if (!result) {
|
|
20
|
-
throw new Error(`Action failed: ${action}`);
|
|
22
|
+
throw new Error(`Action failed: ${action} ${result}`);
|
|
21
23
|
}
|
|
22
24
|
return result;
|
|
23
25
|
}
|
|
@@ -361,6 +363,36 @@ function matches(config) {
|
|
|
361
363
|
}
|
|
362
364
|
}
|
|
363
365
|
|
|
366
|
+
// get or create a style container for CSS overrides
|
|
367
|
+
function getStyleElementUtil() {
|
|
368
|
+
const styleOverrideElementId = "autoconsent-css-rules";
|
|
369
|
+
const styleSelector = `style#${styleOverrideElementId}`;
|
|
370
|
+
const existingElement = document.querySelector(styleSelector);
|
|
371
|
+
if (existingElement && existingElement instanceof HTMLStyleElement) {
|
|
372
|
+
return existingElement;
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
const parent = document.head ||
|
|
376
|
+
document.getElementsByTagName("head")[0] ||
|
|
377
|
+
document.documentElement;
|
|
378
|
+
const css = document.createElement("style");
|
|
379
|
+
css.id = styleOverrideElementId;
|
|
380
|
+
parent.appendChild(css);
|
|
381
|
+
return css;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// hide elements with a CSS rule
|
|
385
|
+
function hideElementsUtil(selectors, method) {
|
|
386
|
+
const hidingSnippet = method === 'display' ? `display: none` : `opacity: 0`;
|
|
387
|
+
const rule = `${selectors.join(",")} { ${hidingSnippet} !important; z-index: -1 !important; pointer-events: none !important; } `;
|
|
388
|
+
const styleEl = getStyleElementUtil();
|
|
389
|
+
if (styleEl instanceof HTMLStyleElement) {
|
|
390
|
+
styleEl.innerText += rule;
|
|
391
|
+
return selectors.length > 0;
|
|
392
|
+
}
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
|
|
364
396
|
const DEBUG = false;
|
|
365
397
|
class Tab {
|
|
366
398
|
constructor(page, url, frames) {
|
|
@@ -369,6 +401,10 @@ class Tab {
|
|
|
369
401
|
this.page = page;
|
|
370
402
|
this.url = url;
|
|
371
403
|
this.frames = frames;
|
|
404
|
+
this._utilsSnippet = `
|
|
405
|
+
${getStyleElementUtil.toString()}
|
|
406
|
+
${hideElementsUtil.toString()}
|
|
407
|
+
`;
|
|
372
408
|
}
|
|
373
409
|
async elementExists(selector, frameId = 0) {
|
|
374
410
|
const elements = await this.frames[frameId].$$(selector);
|
|
@@ -440,9 +476,11 @@ class Tab {
|
|
|
440
476
|
}
|
|
441
477
|
return false;
|
|
442
478
|
}
|
|
443
|
-
async hideElements(selectors, frameId = 0) {
|
|
444
|
-
|
|
445
|
-
|
|
479
|
+
async hideElements(selectors, frameId = 0, method = 'display') {
|
|
480
|
+
return await this.frames[frameId].evaluate(`(() => {
|
|
481
|
+
${this._utilsSnippet}
|
|
482
|
+
return hideElementsUtil(${JSON.stringify(selectors)}, '${method}');
|
|
483
|
+
})()`);
|
|
446
484
|
}
|
|
447
485
|
undoHideElements(frameId) {
|
|
448
486
|
return Promise.resolve(true);
|
|
@@ -477,6 +515,7 @@ async function detectDialog(tab, retries, rules) {
|
|
|
477
515
|
try {
|
|
478
516
|
if (await r.detectCmp(tab)) {
|
|
479
517
|
earlyReturn = true;
|
|
518
|
+
enableLogs && console.log(`Found CMP in [${tab.id}]: ${r.name}`);
|
|
480
519
|
resolve(index);
|
|
481
520
|
}
|
|
482
521
|
}
|
|
@@ -521,10 +560,12 @@ class TabConsent {
|
|
|
521
560
|
}
|
|
522
561
|
async doOptOut() {
|
|
523
562
|
try {
|
|
563
|
+
enableLogs && console.log(`doing opt out ${this.getCMPName()} in tab ${this.tab.id}`);
|
|
524
564
|
this.optOutStatus = await this.rule.optOut(this.tab);
|
|
525
565
|
return this.optOutStatus;
|
|
526
566
|
}
|
|
527
567
|
catch (e) {
|
|
568
|
+
console.error('error during opt out', e);
|
|
528
569
|
this.optOutStatus = e;
|
|
529
570
|
throw e;
|
|
530
571
|
}
|
|
@@ -561,6 +602,20 @@ class TabConsent {
|
|
|
561
602
|
}
|
|
562
603
|
}
|
|
563
604
|
|
|
605
|
+
// hide rules not specific to a single CMP rule
|
|
606
|
+
const globalHidden = [
|
|
607
|
+
"#didomi-popup,.didomi-popup-container,.didomi-popup-notice,.didomi-consent-popup-preferences,#didomi-notice,.didomi-popup-backdrop,.didomi-screen-medium",
|
|
608
|
+
];
|
|
609
|
+
async function prehideElements(tab, rules) {
|
|
610
|
+
const selectors = rules.reduce((selectorList, rule) => {
|
|
611
|
+
if (rule.prehideSelectors) {
|
|
612
|
+
return [...selectorList, ...rule.prehideSelectors];
|
|
613
|
+
}
|
|
614
|
+
return selectorList;
|
|
615
|
+
}, globalHidden);
|
|
616
|
+
await tab.hideElements(selectors, undefined, 'opacity');
|
|
617
|
+
}
|
|
618
|
+
|
|
564
619
|
class TrustArc extends AutoConsentBase {
|
|
565
620
|
constructor() {
|
|
566
621
|
super("TrustArc");
|
|
@@ -879,12 +934,47 @@ class Evidon extends AutoConsentBase {
|
|
|
879
934
|
}
|
|
880
935
|
}
|
|
881
936
|
|
|
937
|
+
class Onetrust extends AutoConsentBase {
|
|
938
|
+
constructor() {
|
|
939
|
+
super("Onetrust");
|
|
940
|
+
this.prehideSelectors = ["#onetrust-banner-sdk,#onetrust-consent-sdk,.optanon-alert-box-wrapper,.onetrust-pc-dark-filter,.js-consent-banner"];
|
|
941
|
+
}
|
|
942
|
+
detectCmp(tab) {
|
|
943
|
+
return tab.elementExists("#onetrust-banner-sdk,.optanon-alert-box-wrapper");
|
|
944
|
+
}
|
|
945
|
+
detectPopup(tab) {
|
|
946
|
+
return tab.elementsAreVisible("#onetrust-banner-sdk,.optanon-alert-box-wrapper");
|
|
947
|
+
}
|
|
948
|
+
async optOut(tab) {
|
|
949
|
+
if (await tab.elementExists("#onetrust-pc-btn-handler")) { // "show purposes" button inside a popup
|
|
950
|
+
await success(tab.clickElement("#onetrust-pc-btn-handler"));
|
|
951
|
+
}
|
|
952
|
+
else { // otherwise look for a generic "show settings" button
|
|
953
|
+
await success(tab.clickElement(".ot-sdk-show-settings,button.js-cookie-settings"));
|
|
954
|
+
}
|
|
955
|
+
await success(tab.waitForElement("#onetrust-consent-sdk", 2000));
|
|
956
|
+
await success(tab.wait(1000));
|
|
957
|
+
await tab.clickElements("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked"); // optional step
|
|
958
|
+
await success(tab.waitForThenClick(".save-preference-btn-handler,.js-consent-save", 1000));
|
|
959
|
+
// popup doesn't disappear immediately
|
|
960
|
+
await waitFor(async () => !(await tab.elementsAreVisible("#onetrust-banner-sdk")), 10, 500);
|
|
961
|
+
return true;
|
|
962
|
+
}
|
|
963
|
+
async optIn(tab) {
|
|
964
|
+
return tab.clickElement("onetrust-accept-btn-handler,js-accept-cookies");
|
|
965
|
+
}
|
|
966
|
+
async test(tab) {
|
|
967
|
+
return tab.eval("window.OnetrustActiveGroups.split(',').filter(s => s.length > 0).length <= 1");
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
882
971
|
const rules = [
|
|
883
972
|
new TrustArc(),
|
|
884
973
|
new Cookiebot(),
|
|
885
974
|
new SourcePoint(),
|
|
886
975
|
new ConsentManager(),
|
|
887
976
|
new Evidon(),
|
|
977
|
+
new Onetrust(),
|
|
888
978
|
];
|
|
889
979
|
function createAutoCMP(config) {
|
|
890
980
|
return new AutoConsent(config);
|
|
@@ -945,10 +1035,11 @@ class ConsentOMaticCMP {
|
|
|
945
1035
|
}
|
|
946
1036
|
}
|
|
947
1037
|
|
|
948
|
-
function attachToPage(page, url, rules, retries = 1) {
|
|
949
|
-
const frames = {
|
|
1038
|
+
function attachToPage(page, url, rules, retries = 1, prehide = true) {
|
|
1039
|
+
const frames = {
|
|
1040
|
+
0: page.mainFrame(),
|
|
1041
|
+
};
|
|
950
1042
|
const tab = new Tab(page, url, frames);
|
|
951
|
-
frames[0] = page.mainFrame();
|
|
952
1043
|
async function onFrame(frame) {
|
|
953
1044
|
const allFrames = await page.frames();
|
|
954
1045
|
allFrames.forEach((frame, frameId) => {
|
|
@@ -967,6 +1058,9 @@ function attachToPage(page, url, rules, retries = 1) {
|
|
|
967
1058
|
}
|
|
968
1059
|
page.on('framenavigated', onFrame);
|
|
969
1060
|
page.frames().forEach(onFrame);
|
|
1061
|
+
if (prehide) {
|
|
1062
|
+
prehideElements(tab, rules);
|
|
1063
|
+
}
|
|
970
1064
|
return new TabConsent(tab, detectDialog(tab, retries, rules));
|
|
971
1065
|
}
|
|
972
1066
|
|
package/lib/cmps/all.js
CHANGED
|
@@ -4,12 +4,14 @@ import CookieBot from './cookiebot';
|
|
|
4
4
|
import SourcePoint from './sourcepoint';
|
|
5
5
|
import ContentManager from './consentmanager';
|
|
6
6
|
import Evidon from './evidon';
|
|
7
|
+
import Onetrust from './onetrust';
|
|
7
8
|
const rules = [
|
|
8
9
|
new TrustArc(),
|
|
9
10
|
new CookieBot(),
|
|
10
11
|
new SourcePoint(),
|
|
11
12
|
new ContentManager(),
|
|
12
13
|
new Evidon(),
|
|
14
|
+
new Onetrust(),
|
|
13
15
|
];
|
|
14
16
|
export function createAutoCMP(config) {
|
|
15
17
|
return new AutoConsent(config);
|
package/lib/cmps/all.ts
CHANGED
|
@@ -5,6 +5,7 @@ import SourcePoint from './sourcepoint';
|
|
|
5
5
|
import ContentManager from './consentmanager';
|
|
6
6
|
import Evidon from './evidon';
|
|
7
7
|
import { AutoConsentCMPRule } from '../rules';
|
|
8
|
+
import Onetrust from './onetrust';
|
|
8
9
|
|
|
9
10
|
const rules = [
|
|
10
11
|
new TrustArc(),
|
|
@@ -12,6 +13,7 @@ const rules = [
|
|
|
12
13
|
new SourcePoint(),
|
|
13
14
|
new ContentManager(),
|
|
14
15
|
new Evidon(),
|
|
16
|
+
new Onetrust(),
|
|
15
17
|
];
|
|
16
18
|
|
|
17
19
|
export function createAutoCMP(config: AutoConsentCMPRule): AutoConsent {
|
package/lib/cmps/base.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable no-restricted-syntax,no-await-in-loop,no-underscore-dangle */
|
|
2
|
+
import { enableLogs } from "../config";
|
|
2
3
|
export async function waitFor(predicate, maxTimes, interval) {
|
|
3
4
|
let result = await predicate();
|
|
4
5
|
if (!result && maxTimes > 0) {
|
|
@@ -13,7 +14,7 @@ export async function waitFor(predicate, maxTimes, interval) {
|
|
|
13
14
|
export async function success(action) {
|
|
14
15
|
const result = await action;
|
|
15
16
|
if (!result) {
|
|
16
|
-
throw new Error(`Action failed: ${action}`);
|
|
17
|
+
throw new Error(`Action failed: ${action} ${result}`);
|
|
17
18
|
}
|
|
18
19
|
return result;
|
|
19
20
|
}
|
|
@@ -118,7 +119,9 @@ export class AutoConsent extends AutoConsentBase {
|
|
|
118
119
|
}
|
|
119
120
|
async _runRulesSequentially(tab, rules) {
|
|
120
121
|
for (const rule of rules) {
|
|
122
|
+
enableLogs && console.log('Running rule...', rule, tab.id);
|
|
121
123
|
const result = await evaluateRule(rule, tab);
|
|
124
|
+
enableLogs && console.log('...rule result', result);
|
|
122
125
|
if (!result && !rule.optional) {
|
|
123
126
|
return false;
|
|
124
127
|
}
|
|
@@ -145,6 +148,7 @@ export class AutoConsent extends AutoConsentBase {
|
|
|
145
148
|
}
|
|
146
149
|
async optOut(tab) {
|
|
147
150
|
if (this.config.optOut) {
|
|
151
|
+
enableLogs && console.log('Initiated optOut()', this.config.optOut);
|
|
148
152
|
return this._runRulesSequentially(tab, this.config.optOut);
|
|
149
153
|
}
|
|
150
154
|
return false;
|
package/lib/cmps/base.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { AutoCMP, TabActor } from "../types";
|
|
4
4
|
import { AutoConsentCMPRule, AutoConsentRuleStep } from "../rules";
|
|
5
|
+
import { enableLogs } from "../config";
|
|
5
6
|
|
|
6
7
|
export async function waitFor(predicate: () => Promise<boolean> | boolean, maxTimes: number, interval: number): Promise<boolean> {
|
|
7
8
|
let result = await predicate();
|
|
@@ -19,7 +20,7 @@ export async function waitFor(predicate: () => Promise<boolean> | boolean, maxTi
|
|
|
19
20
|
export async function success(action: Promise<boolean>): Promise<boolean> {
|
|
20
21
|
const result = await action;
|
|
21
22
|
if (!result) {
|
|
22
|
-
throw new Error(`Action failed: ${action}`)
|
|
23
|
+
throw new Error(`Action failed: ${action} ${result}`)
|
|
23
24
|
}
|
|
24
25
|
return result
|
|
25
26
|
}
|
|
@@ -140,7 +141,9 @@ export class AutoConsent extends AutoConsentBase {
|
|
|
140
141
|
|
|
141
142
|
async _runRulesSequentially(tab: TabActor, rules: AutoConsentRuleStep[]): Promise<boolean> {
|
|
142
143
|
for (const rule of rules) {
|
|
144
|
+
enableLogs && console.log('Running rule...', rule, tab.id);
|
|
143
145
|
const result = await evaluateRule(rule, tab);
|
|
146
|
+
enableLogs && console.log('...rule result', result);
|
|
144
147
|
if (!result && !rule.optional) {
|
|
145
148
|
return false;
|
|
146
149
|
}
|
|
@@ -171,6 +174,7 @@ export class AutoConsent extends AutoConsentBase {
|
|
|
171
174
|
|
|
172
175
|
async optOut(tab: TabActor) {
|
|
173
176
|
if (this.config.optOut) {
|
|
177
|
+
enableLogs && console.log('Initiated optOut()', this.config.optOut);
|
|
174
178
|
return this._runRulesSequentially(tab, this.config.optOut);
|
|
175
179
|
}
|
|
176
180
|
return false;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import AutoConsentBase, { success, waitFor } from "./base";
|
|
2
|
+
export default class Onetrust extends AutoConsentBase {
|
|
3
|
+
constructor() {
|
|
4
|
+
super("Onetrust");
|
|
5
|
+
this.prehideSelectors = ["#onetrust-banner-sdk,#onetrust-consent-sdk,.optanon-alert-box-wrapper,.onetrust-pc-dark-filter,.js-consent-banner"];
|
|
6
|
+
}
|
|
7
|
+
detectCmp(tab) {
|
|
8
|
+
return tab.elementExists("#onetrust-banner-sdk,.optanon-alert-box-wrapper");
|
|
9
|
+
}
|
|
10
|
+
detectPopup(tab) {
|
|
11
|
+
return tab.elementsAreVisible("#onetrust-banner-sdk,.optanon-alert-box-wrapper");
|
|
12
|
+
}
|
|
13
|
+
async optOut(tab) {
|
|
14
|
+
if (await tab.elementExists("#onetrust-pc-btn-handler")) { // "show purposes" button inside a popup
|
|
15
|
+
await success(tab.clickElement("#onetrust-pc-btn-handler"));
|
|
16
|
+
}
|
|
17
|
+
else { // otherwise look for a generic "show settings" button
|
|
18
|
+
await success(tab.clickElement(".ot-sdk-show-settings,button.js-cookie-settings"));
|
|
19
|
+
}
|
|
20
|
+
await success(tab.waitForElement("#onetrust-consent-sdk", 2000));
|
|
21
|
+
await success(tab.wait(1000));
|
|
22
|
+
await tab.clickElements("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked"); // optional step
|
|
23
|
+
await success(tab.waitForThenClick(".save-preference-btn-handler,.js-consent-save", 1000));
|
|
24
|
+
// popup doesn't disappear immediately
|
|
25
|
+
await waitFor(async () => !(await tab.elementsAreVisible("#onetrust-banner-sdk")), 10, 500);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
async optIn(tab) {
|
|
29
|
+
return tab.clickElement("onetrust-accept-btn-handler,js-accept-cookies");
|
|
30
|
+
}
|
|
31
|
+
async test(tab) {
|
|
32
|
+
return tab.eval("window.OnetrustActiveGroups.split(',').filter(s => s.length > 0).length <= 1");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import AutoConsentBase, { success, waitFor } from "./base";
|
|
2
|
+
import { TabActor } from "../types";
|
|
3
|
+
|
|
4
|
+
export default class Onetrust extends AutoConsentBase {
|
|
5
|
+
|
|
6
|
+
prehideSelectors = ["#onetrust-banner-sdk,#onetrust-consent-sdk,.optanon-alert-box-wrapper,.onetrust-pc-dark-filter,.js-consent-banner"]
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
super("Onetrust");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
detectCmp(tab: TabActor) {
|
|
13
|
+
return tab.elementExists("#onetrust-banner-sdk,.optanon-alert-box-wrapper");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
detectPopup(tab: TabActor) {
|
|
17
|
+
return tab.elementsAreVisible("#onetrust-banner-sdk,.optanon-alert-box-wrapper");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async optOut(tab: TabActor) {
|
|
21
|
+
if (await tab.elementExists("#onetrust-pc-btn-handler")) { // "show purposes" button inside a popup
|
|
22
|
+
await success(tab.clickElement("#onetrust-pc-btn-handler"));
|
|
23
|
+
} else { // otherwise look for a generic "show settings" button
|
|
24
|
+
await success(tab.clickElement(".ot-sdk-show-settings,button.js-cookie-settings"));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
await success(tab.waitForElement("#onetrust-consent-sdk", 2000));
|
|
28
|
+
await success(tab.wait(1000));
|
|
29
|
+
await tab.clickElements("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked"); // optional step
|
|
30
|
+
await success(tab.waitForThenClick(".save-preference-btn-handler,.js-consent-save", 1000));
|
|
31
|
+
// popup doesn't disappear immediately
|
|
32
|
+
await waitFor(
|
|
33
|
+
async () => !(await tab.elementsAreVisible("#onetrust-banner-sdk")),
|
|
34
|
+
10,
|
|
35
|
+
500
|
|
36
|
+
);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async optIn(tab: TabActor) {
|
|
41
|
+
return tab.clickElement("onetrust-accept-btn-handler,js-accept-cookies");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async test(tab: TabActor){
|
|
45
|
+
return tab.eval("window.OnetrustActiveGroups.split(',').filter(s => s.length > 0).length <= 1");
|
|
46
|
+
}
|
|
47
|
+
}
|
package/lib/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const enableLogs = false; // change this to enable debug logs
|
package/lib/config.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const enableLogs = false; // change this to enable debug logs
|
package/lib/detector.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { enableLogs } from './config';
|
|
1
2
|
export default async function detectDialog(tab, retries, rules) {
|
|
2
3
|
let breakEarly = false;
|
|
3
4
|
const found = await new Promise(async (resolve) => {
|
|
4
5
|
let earlyReturn = false;
|
|
6
|
+
enableLogs && console.log(`${new Date()} [${tab.id}] Detecting CMPs (${rules.length} rules)`);
|
|
5
7
|
await Promise.all(rules.map(async (r, index) => {
|
|
6
8
|
try {
|
|
7
9
|
if (await r.detectCmp(tab)) {
|
|
8
10
|
earlyReturn = true;
|
|
11
|
+
enableLogs && console.log(`Found CMP in [${tab.id}]: ${r.name}`);
|
|
9
12
|
resolve(index);
|
|
10
13
|
}
|
|
11
14
|
}
|
|
@@ -17,6 +20,7 @@ export default async function detectDialog(tab, retries, rules) {
|
|
|
17
20
|
resolve(-1);
|
|
18
21
|
}
|
|
19
22
|
});
|
|
23
|
+
enableLogs && console.log(`${new Date()} CMP detection finished in [${tab.id}], found rule #${found}`);
|
|
20
24
|
if (found === -1 && retries > 0 && !breakEarly) {
|
|
21
25
|
return new Promise((resolve) => {
|
|
22
26
|
setTimeout(async () => {
|
package/lib/detector.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import { enableLogs } from './config';
|
|
1
2
|
import { AutoCMP, TabActor } from './types';
|
|
2
3
|
|
|
3
4
|
export default async function detectDialog(tab: TabActor, retries: number, rules: AutoCMP[]): Promise<AutoCMP> {
|
|
4
5
|
let breakEarly = false;
|
|
5
6
|
const found: number = await new Promise(async (resolve) => {
|
|
6
7
|
let earlyReturn = false;
|
|
8
|
+
enableLogs && console.log(`${new Date()} [${tab.id}] Detecting CMPs (${rules.length} rules)`);
|
|
7
9
|
await Promise.all(rules.map(async (r, index) => {
|
|
8
10
|
try {
|
|
9
11
|
if (await r.detectCmp(tab)) {
|
|
10
12
|
earlyReturn = true;
|
|
13
|
+
enableLogs && console.log(`Found CMP in [${tab.id}]: ${r.name}`);
|
|
11
14
|
resolve(index)
|
|
12
15
|
}
|
|
13
16
|
} catch (e) {
|
|
@@ -18,6 +21,7 @@ export default async function detectDialog(tab: TabActor, retries: number, rules
|
|
|
18
21
|
resolve(-1)
|
|
19
22
|
}
|
|
20
23
|
})
|
|
24
|
+
enableLogs && console.log(`${new Date()} CMP detection finished in [${tab.id}], found rule #${found}`);
|
|
21
25
|
if (found === -1 && retries > 0 && !breakEarly) {
|
|
22
26
|
return new Promise((resolve) => {
|
|
23
27
|
setTimeout(async () => {
|
package/lib/hider.js
CHANGED
package/lib/hider.ts
CHANGED
package/lib/messages.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export type ContentScriptMessage =
|
|
|
9
9
|
| MatchesMessage
|
|
10
10
|
| ActionMessage;
|
|
11
11
|
|
|
12
|
+
export type HideMethod = 'display' | 'opacity';
|
|
13
|
+
|
|
12
14
|
type ClickMessage = {
|
|
13
15
|
type: "click";
|
|
14
16
|
selector: string;
|
|
@@ -40,6 +42,7 @@ type EvalMessage = {
|
|
|
40
42
|
type HideMessage = {
|
|
41
43
|
type: "hide";
|
|
42
44
|
selectors: string[];
|
|
45
|
+
method: HideMethod;
|
|
43
46
|
};
|
|
44
47
|
|
|
45
48
|
type UndoHideMessage = {
|
package/lib/node.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import Tab from './puppet/tab';
|
|
2
2
|
import detectDialog from './detector';
|
|
3
3
|
import TabConsent from './tabwrapper';
|
|
4
|
+
import prehideElements from './hider';
|
|
4
5
|
export * from './index';
|
|
5
6
|
export { ConsentOMaticCMP } from './consentomatic/index';
|
|
6
7
|
export { Tab, detectDialog, TabConsent, };
|
|
7
|
-
export function attachToPage(page, url, rules, retries = 1) {
|
|
8
|
-
const frames = {
|
|
8
|
+
export function attachToPage(page, url, rules, retries = 1, prehide = true) {
|
|
9
|
+
const frames = {
|
|
10
|
+
0: page.mainFrame(),
|
|
11
|
+
};
|
|
9
12
|
const tab = new Tab(page, url, frames);
|
|
10
|
-
frames[0] = page.mainFrame();
|
|
11
13
|
async function onFrame(frame) {
|
|
12
14
|
const allFrames = await page.frames();
|
|
13
15
|
allFrames.forEach((frame, frameId) => {
|
|
@@ -26,5 +28,8 @@ export function attachToPage(page, url, rules, retries = 1) {
|
|
|
26
28
|
}
|
|
27
29
|
page.on('framenavigated', onFrame);
|
|
28
30
|
page.frames().forEach(onFrame);
|
|
31
|
+
if (prehide) {
|
|
32
|
+
prehideElements(tab, rules);
|
|
33
|
+
}
|
|
29
34
|
return new TabConsent(tab, detectDialog(tab, retries, rules));
|
|
30
35
|
}
|
package/lib/node.ts
CHANGED
|
@@ -2,6 +2,7 @@ import Tab from './puppet/tab';
|
|
|
2
2
|
import detectDialog from './detector';
|
|
3
3
|
import TabConsent from './tabwrapper';
|
|
4
4
|
import { AutoCMP } from './types';
|
|
5
|
+
import prehideElements from './hider';
|
|
5
6
|
|
|
6
7
|
export * from './index';
|
|
7
8
|
export { ConsentOMaticCMP } from './consentomatic/index';
|
|
@@ -11,11 +12,12 @@ export {
|
|
|
11
12
|
TabConsent,
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export function attachToPage(page: any, url: string, rules: AutoCMP[], retries = 1) {
|
|
15
|
-
const frames: { [id: number]: any } = {
|
|
15
|
+
export function attachToPage(page: any, url: string, rules: AutoCMP[], retries = 1, prehide = true) {
|
|
16
|
+
const frames: { [id: number]: any } = {
|
|
17
|
+
0: page.mainFrame(),
|
|
18
|
+
};
|
|
16
19
|
const tab = new Tab(page, url, frames);
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
|
|
19
21
|
async function onFrame(frame: any) {
|
|
20
22
|
const allFrames: any[] = await page.frames();
|
|
21
23
|
allFrames.forEach((frame, frameId) => {
|
|
@@ -34,5 +36,8 @@ export function attachToPage(page: any, url: string, rules: AutoCMP[], retries =
|
|
|
34
36
|
}
|
|
35
37
|
page.on('framenavigated', onFrame);
|
|
36
38
|
page.frames().forEach(onFrame);
|
|
37
|
-
|
|
39
|
+
if (prehide) {
|
|
40
|
+
prehideElements(tab, rules);
|
|
41
|
+
}
|
|
42
|
+
return new TabConsent(tab, detectDialog(tab, retries, rules));
|
|
38
43
|
}
|
package/lib/puppet/tab.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { waitFor } from '../cmps/base';
|
|
2
2
|
import Tools from '../web/consentomatic/tools';
|
|
3
3
|
import { matches } from '../web/consentomatic/index';
|
|
4
|
+
import { hideElementsUtil, getStyleElementUtil } from '../web/content-utils';
|
|
4
5
|
const DEBUG = false;
|
|
5
6
|
export default class Tab {
|
|
6
7
|
constructor(page, url, frames) {
|
|
@@ -9,6 +10,10 @@ export default class Tab {
|
|
|
9
10
|
this.page = page;
|
|
10
11
|
this.url = url;
|
|
11
12
|
this.frames = frames;
|
|
13
|
+
this._utilsSnippet = `
|
|
14
|
+
${getStyleElementUtil.toString()}
|
|
15
|
+
${hideElementsUtil.toString()}
|
|
16
|
+
`;
|
|
12
17
|
}
|
|
13
18
|
async elementExists(selector, frameId = 0) {
|
|
14
19
|
const elements = await this.frames[frameId].$$(selector);
|
|
@@ -84,9 +89,11 @@ export default class Tab {
|
|
|
84
89
|
}
|
|
85
90
|
return false;
|
|
86
91
|
}
|
|
87
|
-
async hideElements(selectors, frameId = 0) {
|
|
88
|
-
|
|
89
|
-
|
|
92
|
+
async hideElements(selectors, frameId = 0, method = 'display') {
|
|
93
|
+
return await this.frames[frameId].evaluate(`(() => {
|
|
94
|
+
${this._utilsSnippet}
|
|
95
|
+
return hideElementsUtil(${JSON.stringify(selectors)}, '${method}');
|
|
96
|
+
})()`);
|
|
90
97
|
}
|
|
91
98
|
undoHideElements(frameId) {
|
|
92
99
|
return Promise.resolve(true);
|
package/lib/puppet/tab.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { waitFor } from '../cmps/base';
|
|
|
2
2
|
import { TabActor } from '../types';
|
|
3
3
|
import Tools from '../web/consentomatic/tools';
|
|
4
4
|
import { matches } from '../web/consentomatic/index';
|
|
5
|
+
import { HideMethod } from '../messages';
|
|
6
|
+
import { hideElementsUtil, getStyleElementUtil } from '../web/content-utils';
|
|
5
7
|
|
|
6
8
|
const DEBUG = false;
|
|
7
9
|
|
|
@@ -17,10 +19,16 @@ export default class Tab implements TabActor {
|
|
|
17
19
|
url: string
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
_utilsSnippet: string // serialized RPC functions borrowed from content script
|
|
23
|
+
|
|
20
24
|
constructor(page: any, url: string, frames: { [id: number]: any }) {
|
|
21
25
|
this.page = page;
|
|
22
26
|
this.url = url;
|
|
23
27
|
this.frames = frames;
|
|
28
|
+
this._utilsSnippet = `
|
|
29
|
+
${getStyleElementUtil.toString()}
|
|
30
|
+
${hideElementsUtil.toString()}
|
|
31
|
+
`;
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
async elementExists(selector: string, frameId = 0) {
|
|
@@ -101,9 +109,11 @@ export default class Tab implements TabActor {
|
|
|
101
109
|
return false;
|
|
102
110
|
}
|
|
103
111
|
|
|
104
|
-
async hideElements(selectors: string[], frameId = 0) {
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
async hideElements(selectors: string[], frameId = 0, method: HideMethod = 'display') {
|
|
113
|
+
return await this.frames[frameId].evaluate(`(() => {
|
|
114
|
+
${this._utilsSnippet}
|
|
115
|
+
return hideElementsUtil(${JSON.stringify(selectors)}, '${method}');
|
|
116
|
+
})()`);
|
|
107
117
|
}
|
|
108
118
|
|
|
109
119
|
undoHideElements(frameId?: number): Promise<boolean> {
|
package/lib/tabwrapper.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { enableLogs } from './config';
|
|
1
2
|
export default class TabConsent {
|
|
2
3
|
constructor(tab, ruleCheckPromise) {
|
|
3
4
|
this.tab = tab;
|
|
@@ -12,23 +13,28 @@ export default class TabConsent {
|
|
|
12
13
|
return null;
|
|
13
14
|
}
|
|
14
15
|
async isPopupOpen(retries = 1, interval = 1000) {
|
|
16
|
+
enableLogs && console.log('checking if popup is open...', this.tab.id, this.rule.name);
|
|
15
17
|
const isOpen = await this.rule.detectPopup(this.tab);
|
|
16
18
|
if (!isOpen && retries > 0) {
|
|
17
19
|
return new Promise((resolve) => setTimeout(() => resolve(this.isPopupOpen(retries - 1, interval)), interval));
|
|
18
20
|
}
|
|
21
|
+
enableLogs && console.log(`popup is ${isOpen ? 'open' : 'not open'}`);
|
|
19
22
|
return isOpen;
|
|
20
23
|
}
|
|
21
24
|
async doOptOut() {
|
|
22
25
|
try {
|
|
26
|
+
enableLogs && console.log(`doing opt out ${this.getCMPName()} in tab ${this.tab.id}`);
|
|
23
27
|
this.optOutStatus = await this.rule.optOut(this.tab);
|
|
24
28
|
return this.optOutStatus;
|
|
25
29
|
}
|
|
26
30
|
catch (e) {
|
|
31
|
+
console.error('error during opt out', e);
|
|
27
32
|
this.optOutStatus = e;
|
|
28
33
|
throw e;
|
|
29
34
|
}
|
|
30
35
|
finally {
|
|
31
36
|
if (!this.rule.isHidingRule) {
|
|
37
|
+
enableLogs && console.log('unhiding elements');
|
|
32
38
|
if (this.getCMPName().startsWith('com_')) {
|
|
33
39
|
this.tab.wait(5000).then(() => this.tab.undoHideElements());
|
|
34
40
|
}
|
package/lib/tabwrapper.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { enableLogs } from './config';
|
|
1
2
|
import { AutoCMP, TabActor } from './types';
|
|
2
3
|
|
|
3
4
|
export default class TabConsent {
|
|
@@ -18,22 +19,27 @@ export default class TabConsent {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
async isPopupOpen(retries = 1, interval = 1000): Promise<boolean> {
|
|
22
|
+
enableLogs && console.log('checking if popup is open...', this.tab.id, this.rule.name);
|
|
21
23
|
const isOpen = await this.rule.detectPopup(this.tab);
|
|
22
24
|
if (!isOpen && retries > 0) {
|
|
23
25
|
return new Promise((resolve) => setTimeout(() => resolve(this.isPopupOpen(retries - 1, interval)), interval));
|
|
24
26
|
}
|
|
27
|
+
enableLogs && console.log(`popup is ${isOpen ? 'open' : 'not open'}`);
|
|
25
28
|
return isOpen;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
async doOptOut() {
|
|
29
32
|
try {
|
|
33
|
+
enableLogs && console.log(`doing opt out ${this.getCMPName()} in tab ${this.tab.id}`);
|
|
30
34
|
this.optOutStatus = await this.rule.optOut(this.tab);
|
|
31
35
|
return this.optOutStatus;
|
|
32
36
|
} catch (e) {
|
|
37
|
+
console.error('error during opt out', e);
|
|
33
38
|
this.optOutStatus = e;
|
|
34
39
|
throw e;
|
|
35
40
|
} finally {
|
|
36
41
|
if (!this.rule.isHidingRule) {
|
|
42
|
+
enableLogs && console.log('unhiding elements');
|
|
37
43
|
if (this.getCMPName().startsWith('com_')) {
|
|
38
44
|
this.tab.wait(5000).then(() => this.tab.undoHideElements())
|
|
39
45
|
} else {
|