@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.
Files changed (93) hide show
  1. package/dist/autoconsent.cjs.js +97 -30
  2. package/dist/autoconsent.esm.js +97 -30
  3. package/dist/autoconsent.puppet.js +101 -7
  4. package/lib/cmps/all.js +2 -0
  5. package/lib/cmps/all.ts +2 -0
  6. package/lib/cmps/base.js +5 -1
  7. package/lib/cmps/base.ts +5 -1
  8. package/lib/cmps/onetrust.js +34 -0
  9. package/lib/cmps/onetrust.ts +47 -0
  10. package/lib/config.js +1 -0
  11. package/lib/config.ts +1 -0
  12. package/lib/detector.js +4 -0
  13. package/lib/detector.ts +4 -0
  14. package/lib/hider.js +1 -1
  15. package/lib/hider.ts +1 -1
  16. package/lib/messages.d.ts +3 -0
  17. package/lib/node.js +8 -3
  18. package/lib/node.ts +10 -5
  19. package/lib/puppet/tab.js +10 -3
  20. package/lib/puppet/tab.ts +13 -3
  21. package/lib/tabwrapper.js +6 -0
  22. package/lib/tabwrapper.ts +6 -0
  23. package/lib/types.d.ts +2 -2
  24. package/lib/web/content-utils.js +29 -0
  25. package/lib/web/content-utils.ts +31 -0
  26. package/lib/web/content.js +20 -22
  27. package/lib/web/content.ts +19 -22
  28. package/lib/web/tab.js +12 -6
  29. package/lib/web/tab.ts +13 -6
  30. package/lib/web.js +5 -0
  31. package/lib/web.ts +5 -0
  32. package/package.json +1 -1
  33. package/readme.md +1 -1
  34. package/rules/autoconsent/arzt-auskunft-de.json +1 -1
  35. package/rules/autoconsent/aws-amazon.json +1 -1
  36. package/rules/autoconsent/bundesregierung-de.json +1 -1
  37. package/rules/autoconsent/corona-in-zahlen-de.json +1 -1
  38. package/rules/autoconsent/deepl.json +1 -1
  39. package/rules/autoconsent/etsy.json +13 -0
  40. package/rules/autoconsent/hl-co-uk.json +1 -1
  41. package/rules/autoconsent/johnlewis.json +1 -1
  42. package/rules/autoconsent/metoffice-gov-uk.json +1 -1
  43. package/rules/autoconsent/microsoft.json +1 -1
  44. package/rules/autoconsent/moneysavingexpert.json +1 -1
  45. package/rules/autoconsent/national-lottery-co-uk.json +2 -3
  46. package/rules/autoconsent/netflix-de.json +1 -1
  47. package/rules/autoconsent/nhs.json +1 -1
  48. package/rules/autoconsent/steampowered.json +14 -7
  49. package/rules/autoconsent/thefreedictionary.json +1 -1
  50. package/rules/autoconsent/usercentrics-1.json +16 -5
  51. package/rules/autoconsent/vodafone-de.json +12 -9
  52. package/rules/autoconsent/xing.json +2 -2
  53. package/rules/rules.json +73 -73
  54. package/tests/arzt-auskunft.spec.ts +7 -0
  55. package/tests/aws.amazon.spec.ts +7 -0
  56. package/tests/baden-wuerttemberg.spec.ts +7 -0
  57. package/tests/borlabs.spec.ts +7 -0
  58. package/tests/bundesregierung.spec.ts +7 -0
  59. package/tests/consentmanager.spec.ts +2 -1
  60. package/tests/cookiebot.spec.ts +3 -3
  61. package/tests/cookieconsent.spec.ts +2 -1
  62. package/tests/cookielawinfo.spec.ts +0 -1
  63. package/tests/corona-in-zahlen.spec.ts +7 -0
  64. package/tests/deepl.spec.ts +7 -0
  65. package/tests/destatis.spec.ts +7 -0
  66. package/tests/etsy.spec.ts +7 -0
  67. package/tests/eu-cookie-compliance-banner.spec.ts +0 -1
  68. package/tests/hl-co-uk.spec.ts +7 -0
  69. package/tests/ionos.spec.ts +7 -0
  70. package/tests/johnlewis.spec.ts +7 -0
  71. package/tests/mediamarkt.spec.ts +7 -0
  72. package/tests/metoffice-gov-uk.spec.ts +7 -0
  73. package/tests/microsoft.spec.ts +9 -0
  74. package/tests/moneysavingexpert.spec.ts +7 -0
  75. package/tests/motor-talk.spec.ts +7 -0
  76. package/tests/national-lottery.spec.ts +7 -0
  77. package/tests/netflix.spec.ts +7 -0
  78. package/tests/nhs.spec.ts +7 -0
  79. package/tests/obi.spec.ts +7 -0
  80. package/tests/onetrust.spec.ts +6 -1
  81. package/tests/otto.spec.ts +7 -0
  82. package/tests/paypal.spec.ts +7 -0
  83. package/tests/runner.ts +2 -2
  84. package/tests/snigel.spec.ts +7 -0
  85. package/tests/steampowered.spec.ts +7 -0
  86. package/tests/thalia.spec.ts +7 -0
  87. package/tests/thefreedictionary.spec.ts +7 -0
  88. package/tests/usercentrics-1.spec.ts +9 -0
  89. package/tests/vodafone.spec.ts +7 -0
  90. package/tests/wordpressgdpr.spec.ts +0 -1
  91. package/tests/xing.spec.ts +8 -0
  92. package/rules/autoconsent/onetrust.json +0 -24
  93. 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
- // TODO implement this
445
- return Promise.resolve(true);
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
@@ -9,5 +9,5 @@ export default async function prehideElements(tab, rules) {
9
9
  }
10
10
  return selectorList;
11
11
  }, globalHidden);
12
- await tab.hideElements(selectors);
12
+ await tab.hideElements(selectors, undefined, 'opacity');
13
13
  }
package/lib/hider.ts CHANGED
@@ -12,5 +12,5 @@ export default async function prehideElements(tab: TabActor, rules: AutoCMP[]):
12
12
  }
13
13
  return selectorList;
14
14
  }, globalHidden);
15
- await tab.hideElements(selectors);
15
+ await tab.hideElements(selectors, undefined, 'opacity');
16
16
  }
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
- frames[0] = page.mainFrame();
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
- return new TabConsent(tab, detectDialog(tab, retries, rules))
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
- // TODO implement this
89
- return Promise.resolve(true);
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
- // TODO implement this
106
- return Promise.resolve(true)
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 {