@duckduckgo/autoconsent 1.0.5 → 1.0.8

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 (66) hide show
  1. package/dist/autoconsent.cjs.js +105 -34
  2. package/dist/autoconsent.esm.js +105 -34
  3. package/dist/autoconsent.puppet.js +109 -11
  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/cookiebot.js +6 -2
  9. package/lib/cmps/cookiebot.ts +8 -2
  10. package/lib/cmps/onetrust.js +34 -0
  11. package/lib/cmps/onetrust.ts +47 -0
  12. package/lib/cmps/trustarc.js +2 -2
  13. package/lib/cmps/trustarc.ts +2 -2
  14. package/lib/config.js +1 -0
  15. package/lib/config.ts +1 -0
  16. package/lib/detector.js +4 -0
  17. package/lib/detector.ts +4 -0
  18. package/lib/hider.js +1 -1
  19. package/lib/hider.ts +1 -1
  20. package/lib/messages.d.ts +3 -0
  21. package/lib/node.js +8 -3
  22. package/lib/node.ts +10 -5
  23. package/lib/puppet/tab.js +10 -3
  24. package/lib/puppet/tab.ts +13 -3
  25. package/lib/tabwrapper.js +6 -0
  26. package/lib/tabwrapper.ts +6 -0
  27. package/lib/types.d.ts +2 -2
  28. package/lib/web/content-utils.js +29 -0
  29. package/lib/web/content-utils.ts +31 -0
  30. package/lib/web/content.js +20 -22
  31. package/lib/web/content.ts +19 -22
  32. package/lib/web/tab.js +12 -6
  33. package/lib/web/tab.ts +13 -6
  34. package/lib/web.js +5 -0
  35. package/lib/web.ts +5 -0
  36. package/package.json +1 -1
  37. package/readme.md +1 -1
  38. package/rules/autoconsent/192.json +17 -0
  39. package/rules/autoconsent/ausopen.json +7 -0
  40. package/rules/autoconsent/aws-amazon.json +1 -1
  41. package/rules/autoconsent/bing.json +14 -0
  42. package/rules/autoconsent/bundesregierung-de.json +1 -1
  43. package/rules/autoconsent/dunelm.json +18 -0
  44. package/rules/autoconsent/etsy.json +13 -0
  45. package/rules/autoconsent/gov-uk.json +10 -0
  46. package/rules/autoconsent/marksandspencer.json +7 -0
  47. package/rules/autoconsent/{paypal-de.json → paypal.json} +6 -2
  48. package/rules/autoconsent/uswitch.json +8 -0
  49. package/rules/autoconsent/waitrose.json +28 -0
  50. package/rules/autoconsent/wetransfer.json +7 -0
  51. package/rules/rules.json +308 -50
  52. package/tests/192.spec.ts +7 -0
  53. package/tests/ausopen.spec.ts +7 -0
  54. package/tests/cookiebot.spec.ts +1 -0
  55. package/tests/dunelm.spec.ts +7 -0
  56. package/tests/etsy.spec.ts +7 -0
  57. package/tests/gov-uk.spec.ts +9 -0
  58. package/tests/marksandspencer.spec.ts +7 -0
  59. package/tests/onetrust.spec.ts +6 -0
  60. package/tests/paypal.spec.ts +7 -5
  61. package/tests/runner.ts +1 -1
  62. package/tests/trustarc.spec.ts +1 -0
  63. package/tests/uswitch.spec.ts +7 -0
  64. package/tests/waitrose.spec.ts +7 -0
  65. package/tests/wetransfer.spec.ts +7 -0
  66. package/rules/autoconsent/onetrust.json +0 -24
@@ -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");
@@ -577,10 +632,10 @@ class TrustArc extends AutoConsentBase {
577
632
  tab.frame.url.startsWith("https://consent-pref.trustarc.com/?")) {
578
633
  return true;
579
634
  }
580
- return tab.elementExists("#truste-show-consent");
635
+ return tab.elementExists("#truste-show-consent,#truste-consent-track");
581
636
  }
582
637
  async detectPopup(tab) {
583
- return ((await tab.elementsAreVisible("#truste-consent-content,#trustarc-banner-overlay")) ||
638
+ return ((await tab.elementsAreVisible("#truste-consent-content,.truste-consent-content,#trustarc-banner-overlay")) ||
584
639
  (tab.frame &&
585
640
  (await tab.waitForElement("#defaultpreferencemanager", 5000, tab.frame.id))));
586
641
  }
@@ -670,7 +725,7 @@ class TrustArc extends AutoConsentBase {
670
725
  class Cookiebot extends AutoConsentBase {
671
726
  constructor() {
672
727
  super('Cybotcookiebot');
673
- this.prehideSelectors = ["#CybotCookiebotDialog,#dtcookie-container,#cookiebanner"];
728
+ this.prehideSelectors = ["#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookieoverlay"];
674
729
  }
675
730
  async detectCmp(tab) {
676
731
  try {
@@ -681,7 +736,7 @@ class Cookiebot extends AutoConsentBase {
681
736
  }
682
737
  }
683
738
  detectPopup(tab) {
684
- return tab.elementExists('#CybotCookiebotDialog,#dtcookie-container,#cookiebanner');
739
+ return tab.elementExists('#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookiebanner');
685
740
  }
686
741
  async optOut(tab) {
687
742
  if (await tab.elementExists('.cookie-alert-extended-detail-link')) {
@@ -720,6 +775,10 @@ class Cookiebot extends AutoConsentBase {
720
775
  await tab.eval('Cookiebot.dialog.submitConsent() || true');
721
776
  await tab.wait(500);
722
777
  }
778
+ // site with 3rd confirm settings modal
779
+ if (await tab.elementExists('#cb-confirmedSettings')) {
780
+ await tab.eval('endCookieProcess()');
781
+ }
723
782
  return true;
724
783
  }
725
784
  async optIn(tab) {
@@ -879,12 +938,47 @@ class Evidon extends AutoConsentBase {
879
938
  }
880
939
  }
881
940
 
941
+ class Onetrust extends AutoConsentBase {
942
+ constructor() {
943
+ super("Onetrust");
944
+ this.prehideSelectors = ["#onetrust-banner-sdk,#onetrust-consent-sdk,.optanon-alert-box-wrapper,.onetrust-pc-dark-filter,.js-consent-banner"];
945
+ }
946
+ detectCmp(tab) {
947
+ return tab.elementExists("#onetrust-banner-sdk,.optanon-alert-box-wrapper");
948
+ }
949
+ detectPopup(tab) {
950
+ return tab.elementsAreVisible("#onetrust-banner-sdk,.optanon-alert-box-wrapper");
951
+ }
952
+ async optOut(tab) {
953
+ if (await tab.elementExists("#onetrust-pc-btn-handler")) { // "show purposes" button inside a popup
954
+ await success(tab.clickElement("#onetrust-pc-btn-handler"));
955
+ }
956
+ else { // otherwise look for a generic "show settings" button
957
+ await success(tab.clickElement(".ot-sdk-show-settings,button.js-cookie-settings"));
958
+ }
959
+ await success(tab.waitForElement("#onetrust-consent-sdk", 2000));
960
+ await success(tab.wait(1000));
961
+ await tab.clickElements("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked"); // optional step
962
+ await success(tab.waitForThenClick(".save-preference-btn-handler,.js-consent-save", 1000));
963
+ // popup doesn't disappear immediately
964
+ await waitFor(async () => !(await tab.elementsAreVisible("#onetrust-banner-sdk")), 10, 500);
965
+ return true;
966
+ }
967
+ async optIn(tab) {
968
+ return tab.clickElement("onetrust-accept-btn-handler,js-accept-cookies");
969
+ }
970
+ async test(tab) {
971
+ return tab.eval("window.OnetrustActiveGroups.split(',').filter(s => s.length > 0).length <= 1");
972
+ }
973
+ }
974
+
882
975
  const rules = [
883
976
  new TrustArc(),
884
977
  new Cookiebot(),
885
978
  new SourcePoint(),
886
979
  new ConsentManager(),
887
980
  new Evidon(),
981
+ new Onetrust(),
888
982
  ];
889
983
  function createAutoCMP(config) {
890
984
  return new AutoConsent(config);
@@ -945,10 +1039,11 @@ class ConsentOMaticCMP {
945
1039
  }
946
1040
  }
947
1041
 
948
- function attachToPage(page, url, rules, retries = 1) {
949
- const frames = {};
1042
+ function attachToPage(page, url, rules, retries = 1, prehide = true) {
1043
+ const frames = {
1044
+ 0: page.mainFrame(),
1045
+ };
950
1046
  const tab = new Tab(page, url, frames);
951
- frames[0] = page.mainFrame();
952
1047
  async function onFrame(frame) {
953
1048
  const allFrames = await page.frames();
954
1049
  allFrames.forEach((frame, frameId) => {
@@ -967,6 +1062,9 @@ function attachToPage(page, url, rules, retries = 1) {
967
1062
  }
968
1063
  page.on('framenavigated', onFrame);
969
1064
  page.frames().forEach(onFrame);
1065
+ if (prehide) {
1066
+ prehideElements(tab, rules);
1067
+ }
970
1068
  return new TabConsent(tab, detectDialog(tab, retries, rules));
971
1069
  }
972
1070
 
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;
@@ -2,7 +2,7 @@ import AutoConsentBase from './base';
2
2
  export default class Cookiebot extends AutoConsentBase {
3
3
  constructor() {
4
4
  super('Cybotcookiebot');
5
- this.prehideSelectors = ["#CybotCookiebotDialog,#dtcookie-container,#cookiebanner"];
5
+ this.prehideSelectors = ["#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookieoverlay"];
6
6
  }
7
7
  async detectCmp(tab) {
8
8
  try {
@@ -13,7 +13,7 @@ export default class Cookiebot extends AutoConsentBase {
13
13
  }
14
14
  }
15
15
  detectPopup(tab) {
16
- return tab.elementExists('#CybotCookiebotDialog,#dtcookie-container,#cookiebanner');
16
+ return tab.elementExists('#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookiebanner');
17
17
  }
18
18
  async optOut(tab) {
19
19
  if (await tab.elementExists('.cookie-alert-extended-detail-link')) {
@@ -52,6 +52,10 @@ export default class Cookiebot extends AutoConsentBase {
52
52
  await tab.eval('Cookiebot.dialog.submitConsent() || true');
53
53
  await tab.wait(500);
54
54
  }
55
+ // site with 3rd confirm settings modal
56
+ if (await tab.elementExists('#cb-confirmedSettings')) {
57
+ await tab.eval('endCookieProcess()');
58
+ }
55
59
  return true;
56
60
  }
57
61
  async optIn(tab) {
@@ -3,7 +3,7 @@ import { TabActor } from '../types';
3
3
 
4
4
  export default class Cookiebot extends AutoConsentBase {
5
5
 
6
- prehideSelectors = ["#CybotCookiebotDialog,#dtcookie-container,#cookiebanner"]
6
+ prehideSelectors = ["#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookieoverlay"]
7
7
 
8
8
  constructor() {
9
9
  super('Cybotcookiebot');
@@ -18,7 +18,7 @@ export default class Cookiebot extends AutoConsentBase {
18
18
  }
19
19
 
20
20
  detectPopup(tab: TabActor) {
21
- return tab.elementExists('#CybotCookiebotDialog,#dtcookie-container,#cookiebanner');
21
+ return tab.elementExists('#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookiebanner');
22
22
  }
23
23
 
24
24
  async optOut(tab: TabActor) {
@@ -57,6 +57,12 @@ export default class Cookiebot extends AutoConsentBase {
57
57
  await tab.eval('Cookiebot.dialog.submitConsent() || true');
58
58
  await tab.wait(500);
59
59
  }
60
+
61
+ // site with 3rd confirm settings modal
62
+ if (await tab.elementExists('#cb-confirmedSettings')) {
63
+ await tab.eval('endCookieProcess()')
64
+ }
65
+
60
66
  return true;
61
67
  }
62
68
 
@@ -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
+ }
@@ -15,10 +15,10 @@ export default class TrustArc extends AutoConsentBase {
15
15
  tab.frame.url.startsWith("https://consent-pref.trustarc.com/?")) {
16
16
  return true;
17
17
  }
18
- return tab.elementExists("#truste-show-consent");
18
+ return tab.elementExists("#truste-show-consent,#truste-consent-track");
19
19
  }
20
20
  async detectPopup(tab) {
21
- return ((await tab.elementsAreVisible("#truste-consent-content,#trustarc-banner-overlay")) ||
21
+ return ((await tab.elementsAreVisible("#truste-consent-content,.truste-consent-content,#trustarc-banner-overlay")) ||
22
22
  (tab.frame &&
23
23
  (await tab.waitForElement("#defaultpreferencemanager", 5000, tab.frame.id))));
24
24
  }
@@ -23,12 +23,12 @@ export default class TrustArc extends AutoConsentBase {
23
23
  ) {
24
24
  return true;
25
25
  }
26
- return tab.elementExists("#truste-show-consent");
26
+ return tab.elementExists("#truste-show-consent,#truste-consent-track");
27
27
  }
28
28
 
29
29
  async detectPopup(tab: TabActor) {
30
30
  return (
31
- (await tab.elementsAreVisible("#truste-consent-content,#trustarc-banner-overlay")) ||
31
+ (await tab.elementsAreVisible("#truste-consent-content,.truste-consent-content,#trustarc-banner-overlay")) ||
32
32
  (tab.frame &&
33
33
  (await tab.waitForElement(
34
34
  "#defaultpreferencemanager",
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
  }