@duckduckgo/autoconsent 1.0.3 → 1.0.6
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 +85 -30
- package/dist/autoconsent.esm.js +85 -30
- package/dist/autoconsent.puppet.js +99 -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 +32 -0
- package/lib/cmps/onetrust.ts +41 -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 +10 -22
- package/lib/web/content.ts +10 -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 +2 -1
- package/readme.md +1 -1
- package/rules/autoconsent/arzt-auskunft-de.json +1 -1
- package/rules/autoconsent/aws-amazon.json +18 -0
- package/rules/autoconsent/baden-wuerttemberg-de.json +9 -0
- package/rules/autoconsent/borlabs.json +12 -0
- package/rules/autoconsent/bundesregierung-de.json +15 -0
- package/rules/autoconsent/corona-in-zahlen-de.json +1 -1
- package/rules/autoconsent/deepl.json +1 -1
- package/rules/autoconsent/destatis-de.json +8 -0
- package/rules/autoconsent/etsy.json +13 -0
- package/rules/autoconsent/hl-co-uk.json +1 -1
- package/rules/autoconsent/ionos-de.json +15 -0
- package/rules/autoconsent/johnlewis.json +1 -1
- package/rules/autoconsent/mediamarkt-de.json +3 -3
- 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/motor-talk-de.json +24 -0
- package/rules/autoconsent/national-lottery-co-uk.json +2 -3
- package/rules/autoconsent/netflix-de.json +7 -0
- package/rules/autoconsent/nhs.json +1 -1
- package/rules/autoconsent/obi-de.json +12 -0
- package/rules/autoconsent/otto-de.json +12 -0
- package/rules/autoconsent/paypal-de.json +12 -0
- package/rules/autoconsent/steampowered.json +14 -7
- package/rules/autoconsent/thalia-de.json +24 -0
- package/rules/autoconsent/thefreedictionary.json +1 -1
- package/rules/autoconsent/usercentrics-1.json +19 -0
- package/rules/autoconsent/vodafone-de.json +13 -0
- package/rules/autoconsent/xing.json +2 -2
- package/rules/rules.json +435 -49
- 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
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,32 @@
|
|
|
1
|
+
import AutoConsentBase, { success } 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
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
async optIn(tab) {
|
|
27
|
+
return tab.clickElement("onetrust-accept-btn-handler,js-accept-cookies");
|
|
28
|
+
}
|
|
29
|
+
async test(tab) {
|
|
30
|
+
return tab.eval("window.OnetrustActiveGroups.split(',').filter(s => s.length > 0).length <= 1");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import AutoConsentBase, { success } 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
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async optIn(tab: TabActor) {
|
|
35
|
+
return tab.clickElement("onetrust-accept-btn-handler,js-accept-cookies");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async test(tab: TabActor){
|
|
39
|
+
return tab.eval("window.OnetrustActiveGroups.split(',').filter(s => s.length > 0).length <= 1");
|
|
40
|
+
}
|
|
41
|
+
}
|
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 {
|
package/lib/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ContentScriptMessage } from "./messages";
|
|
1
|
+
import { ContentScriptMessage, HideMethod } from "./messages";
|
|
2
2
|
|
|
3
3
|
type Tab = {
|
|
4
4
|
url: string
|
|
@@ -27,7 +27,7 @@ export interface TabActor {
|
|
|
27
27
|
eval(script: string, frameId?: number): Promise<boolean>
|
|
28
28
|
waitForElement(selector: string, timeout: number, frameId?: number): Promise<boolean>
|
|
29
29
|
waitForThenClick(selector: string, timeout?: number, frameId?: number): Promise<boolean>
|
|
30
|
-
hideElements(selectors: string[], frameId?: number): Promise<boolean>
|
|
30
|
+
hideElements(selectors: string[], frameId?: number, method?: HideMethod): Promise<boolean>
|
|
31
31
|
undoHideElements(frameId?: number): Promise<boolean>
|
|
32
32
|
goto(url: string): Promise<void>
|
|
33
33
|
wait(ms: number): Promise<true>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// get or create a style container for CSS overrides
|
|
2
|
+
export function getStyleElementUtil() {
|
|
3
|
+
const styleOverrideElementId = "autoconsent-css-rules";
|
|
4
|
+
const styleSelector = `style#${styleOverrideElementId}`;
|
|
5
|
+
const existingElement = document.querySelector(styleSelector);
|
|
6
|
+
if (existingElement && existingElement instanceof HTMLStyleElement) {
|
|
7
|
+
return existingElement;
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
const parent = document.head ||
|
|
11
|
+
document.getElementsByTagName("head")[0] ||
|
|
12
|
+
document.documentElement;
|
|
13
|
+
const css = document.createElement("style");
|
|
14
|
+
css.id = styleOverrideElementId;
|
|
15
|
+
parent.appendChild(css);
|
|
16
|
+
return css;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// hide elements with a CSS rule
|
|
20
|
+
export function hideElementsUtil(selectors, method) {
|
|
21
|
+
const hidingSnippet = method === 'display' ? `display: none` : `opacity: 0`;
|
|
22
|
+
const rule = `${selectors.join(",")} { ${hidingSnippet} !important; z-index: -1 !important; pointer-events: none !important; } `;
|
|
23
|
+
const styleEl = getStyleElementUtil();
|
|
24
|
+
if (styleEl instanceof HTMLStyleElement) {
|
|
25
|
+
styleEl.innerText += rule;
|
|
26
|
+
return selectors.length > 0;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { HideMethod } from "../messages";
|
|
2
|
+
|
|
3
|
+
// get or create a style container for CSS overrides
|
|
4
|
+
export function getStyleElementUtil(): HTMLStyleElement {
|
|
5
|
+
const styleOverrideElementId = "autoconsent-css-rules";
|
|
6
|
+
const styleSelector = `style#${styleOverrideElementId}`;
|
|
7
|
+
const existingElement = document.querySelector(styleSelector);
|
|
8
|
+
if (existingElement && existingElement instanceof HTMLStyleElement) {
|
|
9
|
+
return existingElement;
|
|
10
|
+
} else {
|
|
11
|
+
const parent = document.head ||
|
|
12
|
+
document.getElementsByTagName("head")[0] ||
|
|
13
|
+
document.documentElement;
|
|
14
|
+
const css = document.createElement("style");
|
|
15
|
+
css.id = styleOverrideElementId;
|
|
16
|
+
parent.appendChild(css);
|
|
17
|
+
return css;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// hide elements with a CSS rule
|
|
22
|
+
export function hideElementsUtil(selectors: string[], method: HideMethod): boolean {
|
|
23
|
+
const hidingSnippet = method === 'display' ? `display: none` : `opacity: 0`;
|
|
24
|
+
const rule = `${selectors.join(",")} { ${hidingSnippet} !important; z-index: -1 !important; pointer-events: none !important; } `;
|
|
25
|
+
const styleEl = getStyleElementUtil();
|
|
26
|
+
if (styleEl instanceof HTMLStyleElement) {
|
|
27
|
+
styleEl.innerText += rule;
|
|
28
|
+
return selectors.length > 0;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
package/lib/web/content.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { matches, executeAction } from "./consentomatic/index";
|
|
2
|
+
import { hideElementsUtil, getStyleElementUtil } from "./content-utils";
|
|
2
3
|
let actionQueue = Promise.resolve(null);
|
|
3
|
-
const styleOverrideElementId = "autoconsent-css-rules";
|
|
4
|
-
const styleSelector = `style#${styleOverrideElementId}`;
|
|
5
4
|
export default function handleMessage(message, debug = false) {
|
|
6
5
|
if (message.type === "click") {
|
|
7
6
|
const elem = document.querySelectorAll(message.selector);
|
|
@@ -25,8 +24,9 @@ export default function handleMessage(message, debug = false) {
|
|
|
25
24
|
const elem = document.querySelectorAll(message.selector);
|
|
26
25
|
const results = new Array(elem.length);
|
|
27
26
|
elem.forEach((e, i) => {
|
|
28
|
-
results[i] = e.offsetParent !== null || window.getComputedStyle(e).display !== "none"
|
|
27
|
+
results[i] = e.offsetParent !== null || window.getComputedStyle(e).display !== "none"; // TODO: handle visibility and z-index?
|
|
29
28
|
});
|
|
29
|
+
debug && console.log("[visible?]", message.selector, elem, results);
|
|
30
30
|
if (results.length === 0) {
|
|
31
31
|
return false;
|
|
32
32
|
}
|
|
@@ -41,6 +41,7 @@ export default function handleMessage(message, debug = false) {
|
|
|
41
41
|
}
|
|
42
42
|
else if (message.type === "getAttribute") {
|
|
43
43
|
const elem = document.querySelector(message.selector);
|
|
44
|
+
debug && console.log("[getAttribute]", message.selector, elem);
|
|
44
45
|
if (!elem) {
|
|
45
46
|
return false;
|
|
46
47
|
}
|
|
@@ -48,31 +49,16 @@ export default function handleMessage(message, debug = false) {
|
|
|
48
49
|
}
|
|
49
50
|
else if (message.type === "eval") {
|
|
50
51
|
// TODO: chrome support
|
|
52
|
+
debug && console.log("about to [eval]", message.script); // this will not show in Webkit console
|
|
51
53
|
const result = window.eval(message.script); // eslint-disable-line no-eval
|
|
52
|
-
debug && console.log("[eval]", message.script, result);
|
|
53
54
|
return result;
|
|
54
55
|
}
|
|
55
56
|
else if (message.type === "hide") {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
document.documentElement;
|
|
59
|
-
const rule = `${message.selectors.join(",")} { display: none !important; z-index: -1 !important; } `;
|
|
60
|
-
const existingElement = document.querySelector(styleSelector);
|
|
61
|
-
debug && console.log("[hide]", message.selectors, !!existingElement);
|
|
62
|
-
if (existingElement && existingElement instanceof HTMLStyleElement) {
|
|
63
|
-
existingElement.innerText += rule;
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
const css = document.createElement("style");
|
|
67
|
-
css.type = "text/css";
|
|
68
|
-
css.id = styleOverrideElementId;
|
|
69
|
-
css.appendChild(document.createTextNode(rule));
|
|
70
|
-
parent.appendChild(css);
|
|
71
|
-
}
|
|
72
|
-
return message.selectors.length > 0;
|
|
57
|
+
debug && console.log("[hide]", message.selectors);
|
|
58
|
+
return hideElementsUtil(message.selectors, message.method);
|
|
73
59
|
}
|
|
74
60
|
else if (message.type === "undohide") {
|
|
75
|
-
const existingElement =
|
|
61
|
+
const existingElement = getStyleElementUtil();
|
|
76
62
|
debug && console.log("[unhide]", !!existingElement);
|
|
77
63
|
if (existingElement) {
|
|
78
64
|
existingElement.remove();
|
|
@@ -81,9 +67,11 @@ export default function handleMessage(message, debug = false) {
|
|
|
81
67
|
}
|
|
82
68
|
else if (message.type === "matches") {
|
|
83
69
|
const matched = matches(message.config);
|
|
70
|
+
debug && console.log("[matches?]", message.config.type, JSON.stringify(message.config), matched);
|
|
84
71
|
return matched;
|
|
85
72
|
}
|
|
86
73
|
else if (message.type === "executeAction") {
|
|
74
|
+
debug && console.log("[executeAction]", message);
|
|
87
75
|
actionQueue = actionQueue.then(() => executeAction(message.config, message.param));
|
|
88
76
|
return true;
|
|
89
77
|
}
|