@duckduckgo/autoconsent 1.0.5 → 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.
@@ -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
  }
@@ -181,7 +183,6 @@ class TabActions {
181
183
  this.id = tabId;
182
184
  }
183
185
  async elementExists(selector, frameId = 0) {
184
- console.log(`check for ${selector} in tab ${this.id}, frame ${frameId}`);
185
186
  return this.sendContentMessage(this.id, {
186
187
  type: "elemExists",
187
188
  selector
@@ -190,7 +191,6 @@ class TabActions {
190
191
  });
191
192
  }
192
193
  async clickElement(selector, frameId = 0) {
193
- console.log(`click element ${selector} in tab ${this.id}`);
194
194
  return this.sendContentMessage(this.id, {
195
195
  type: "click",
196
196
  selector
@@ -199,7 +199,6 @@ class TabActions {
199
199
  });
200
200
  }
201
201
  async clickElements(selector, frameId = 0) {
202
- console.log(`click elements ${selector} in tab ${this.id}`);
203
202
  return this.sendContentMessage(this.id, {
204
203
  type: "click",
205
204
  all: true,
@@ -242,10 +241,11 @@ class TabActions {
242
241
  }
243
242
  return false;
244
243
  }
245
- async hideElements(selectors, frameId = 0) {
244
+ async hideElements(selectors, frameId = 0, method = 'display') {
246
245
  return this.sendContentMessage(this.id, {
247
246
  type: "hide",
248
- selectors
247
+ selectors,
248
+ method,
249
249
  }, { frameId });
250
250
  }
251
251
  async undoHideElements(frameId = 0) {
@@ -261,7 +261,9 @@ class TabActions {
261
261
  }
262
262
  wait(ms) {
263
263
  return new Promise(resolve => {
264
- setTimeout(() => resolve(true), ms);
264
+ setTimeout(() => {
265
+ resolve(true);
266
+ }, ms);
265
267
  });
266
268
  }
267
269
  matches(matcherConfig) {
@@ -645,9 +647,37 @@ async function evalAction(config) {
645
647
  });
646
648
  }
647
649
 
650
+ // get or create a style container for CSS overrides
651
+ function getStyleElementUtil() {
652
+ const styleOverrideElementId = "autoconsent-css-rules";
653
+ const styleSelector = `style#${styleOverrideElementId}`;
654
+ const existingElement = document.querySelector(styleSelector);
655
+ if (existingElement && existingElement instanceof HTMLStyleElement) {
656
+ return existingElement;
657
+ }
658
+ else {
659
+ const parent = document.head ||
660
+ document.getElementsByTagName("head")[0] ||
661
+ document.documentElement;
662
+ const css = document.createElement("style");
663
+ css.id = styleOverrideElementId;
664
+ parent.appendChild(css);
665
+ return css;
666
+ }
667
+ }
668
+ // hide elements with a CSS rule
669
+ function hideElementsUtil(selectors, method) {
670
+ const hidingSnippet = method === 'display' ? `display: none` : `opacity: 0`;
671
+ const rule = `${selectors.join(",")} { ${hidingSnippet} !important; z-index: -1 !important; pointer-events: none !important; } `;
672
+ const styleEl = getStyleElementUtil();
673
+ if (styleEl instanceof HTMLStyleElement) {
674
+ styleEl.innerText += rule;
675
+ return selectors.length > 0;
676
+ }
677
+ return false;
678
+ }
679
+
648
680
  let actionQueue = Promise.resolve(null);
649
- const styleOverrideElementId = "autoconsent-css-rules";
650
- const styleSelector = `style#${styleOverrideElementId}`;
651
681
  function handleMessage(message, debug = false) {
652
682
  if (message.type === "click") {
653
683
  const elem = document.querySelectorAll(message.selector);
@@ -671,8 +701,9 @@ function handleMessage(message, debug = false) {
671
701
  const elem = document.querySelectorAll(message.selector);
672
702
  const results = new Array(elem.length);
673
703
  elem.forEach((e, i) => {
674
- results[i] = e.offsetParent !== null || window.getComputedStyle(e).display !== "none" || e.style?.display !== "none";
704
+ results[i] = e.offsetParent !== null || window.getComputedStyle(e).display !== "none"; // TODO: handle visibility and z-index?
675
705
  });
706
+ debug && console.log("[visible?]", message.selector, elem, results);
676
707
  if (results.length === 0) {
677
708
  return false;
678
709
  }
@@ -687,6 +718,7 @@ function handleMessage(message, debug = false) {
687
718
  }
688
719
  else if (message.type === "getAttribute") {
689
720
  const elem = document.querySelector(message.selector);
721
+ debug && console.log("[getAttribute]", message.selector, elem);
690
722
  if (!elem) {
691
723
  return false;
692
724
  }
@@ -694,31 +726,16 @@ function handleMessage(message, debug = false) {
694
726
  }
695
727
  else if (message.type === "eval") {
696
728
  // TODO: chrome support
729
+ debug && console.log("about to [eval]", message.script); // this will not show in Webkit console
697
730
  const result = window.eval(message.script); // eslint-disable-line no-eval
698
- debug && console.log("[eval]", message.script, result);
699
731
  return result;
700
732
  }
701
733
  else if (message.type === "hide") {
702
- const parent = document.head ||
703
- document.getElementsByTagName("head")[0] ||
704
- document.documentElement;
705
- const rule = `${message.selectors.join(",")} { display: none !important; z-index: -1 !important; } `;
706
- const existingElement = document.querySelector(styleSelector);
707
- debug && console.log("[hide]", message.selectors, !!existingElement);
708
- if (existingElement && existingElement instanceof HTMLStyleElement) {
709
- existingElement.innerText += rule;
710
- }
711
- else {
712
- const css = document.createElement("style");
713
- css.type = "text/css";
714
- css.id = styleOverrideElementId;
715
- css.appendChild(document.createTextNode(rule));
716
- parent.appendChild(css);
717
- }
718
- return message.selectors.length > 0;
734
+ debug && console.log("[hide]", message.selectors);
735
+ return hideElementsUtil(message.selectors, message.method);
719
736
  }
720
737
  else if (message.type === "undohide") {
721
- const existingElement = document.querySelector(styleSelector);
738
+ const existingElement = getStyleElementUtil();
722
739
  debug && console.log("[unhide]", !!existingElement);
723
740
  if (existingElement) {
724
741
  existingElement.remove();
@@ -727,9 +744,11 @@ function handleMessage(message, debug = false) {
727
744
  }
728
745
  else if (message.type === "matches") {
729
746
  const matched = matches(message.config);
747
+ debug && console.log("[matches?]", message.config.type, JSON.stringify(message.config), matched);
730
748
  return matched;
731
749
  }
732
750
  else if (message.type === "executeAction") {
751
+ debug && console.log("[executeAction]", message);
733
752
  actionQueue = actionQueue.then(() => executeAction(message.config, message.param));
734
753
  return true;
735
754
  }
@@ -758,10 +777,12 @@ class TabConsent {
758
777
  }
759
778
  async doOptOut() {
760
779
  try {
780
+ enableLogs && console.log(`doing opt out ${this.getCMPName()} in tab ${this.tab.id}`);
761
781
  this.optOutStatus = await this.rule.optOut(this.tab);
762
782
  return this.optOutStatus;
763
783
  }
764
784
  catch (e) {
785
+ console.error('error during opt out', e);
765
786
  this.optOutStatus = e;
766
787
  throw e;
767
788
  }
@@ -806,6 +827,7 @@ async function detectDialog(tab, retries, rules) {
806
827
  try {
807
828
  if (await r.detectCmp(tab)) {
808
829
  earlyReturn = true;
830
+ enableLogs && console.log(`Found CMP in [${tab.id}]: ${r.name}`);
809
831
  resolve(index);
810
832
  }
811
833
  }
@@ -1146,12 +1168,45 @@ class Evidon extends AutoConsentBase {
1146
1168
  }
1147
1169
  }
1148
1170
 
1171
+ class Onetrust extends AutoConsentBase {
1172
+ constructor() {
1173
+ super("Onetrust");
1174
+ this.prehideSelectors = ["#onetrust-banner-sdk,#onetrust-consent-sdk,.optanon-alert-box-wrapper,.onetrust-pc-dark-filter,.js-consent-banner"];
1175
+ }
1176
+ detectCmp(tab) {
1177
+ return tab.elementExists("#onetrust-banner-sdk,.optanon-alert-box-wrapper");
1178
+ }
1179
+ detectPopup(tab) {
1180
+ return tab.elementsAreVisible("#onetrust-banner-sdk,.optanon-alert-box-wrapper");
1181
+ }
1182
+ async optOut(tab) {
1183
+ if (await tab.elementExists("#onetrust-pc-btn-handler")) { // "show purposes" button inside a popup
1184
+ await success(tab.clickElement("#onetrust-pc-btn-handler"));
1185
+ }
1186
+ else { // otherwise look for a generic "show settings" button
1187
+ await success(tab.clickElement(".ot-sdk-show-settings,button.js-cookie-settings"));
1188
+ }
1189
+ await success(tab.waitForElement("#onetrust-consent-sdk", 2000));
1190
+ await success(tab.wait(1000));
1191
+ await tab.clickElements("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked"); // optional step
1192
+ await success(tab.waitForThenClick(".save-preference-btn-handler,.js-consent-save", 1000));
1193
+ return true;
1194
+ }
1195
+ async optIn(tab) {
1196
+ return tab.clickElement("onetrust-accept-btn-handler,js-accept-cookies");
1197
+ }
1198
+ async test(tab) {
1199
+ return tab.eval("window.OnetrustActiveGroups.split(',').filter(s => s.length > 0).length <= 1");
1200
+ }
1201
+ }
1202
+
1149
1203
  const rules = [
1150
1204
  new TrustArc(),
1151
1205
  new Cookiebot(),
1152
1206
  new SourcePoint(),
1153
1207
  new ConsentManager(),
1154
1208
  new Evidon(),
1209
+ new Onetrust(),
1155
1210
  ];
1156
1211
  function createAutoCMP(config) {
1157
1212
  return new AutoConsent(config);
@@ -1223,7 +1278,7 @@ async function prehideElements(tab, rules) {
1223
1278
  }
1224
1279
  return selectorList;
1225
1280
  }, globalHidden);
1226
- await tab.hideElements(selectors);
1281
+ await tab.hideElements(selectors, undefined, 'opacity');
1227
1282
  }
1228
1283
 
1229
1284
  class AutoConsent$1 {
@@ -1,3 +1,5 @@
1
+ const enableLogs = false; // change this to enable debug logs
2
+
1
3
  /* eslint-disable no-restricted-syntax,no-await-in-loop,no-underscore-dangle */
2
4
  async function waitFor(predicate, maxTimes, interval) {
3
5
  let result = await predicate();
@@ -13,7 +15,7 @@ async function waitFor(predicate, maxTimes, interval) {
13
15
  async function success(action) {
14
16
  const result = await action;
15
17
  if (!result) {
16
- throw new Error(`Action failed: ${action}`);
18
+ throw new Error(`Action failed: ${action} ${result}`);
17
19
  }
18
20
  return result;
19
21
  }
@@ -177,7 +179,6 @@ class TabActions {
177
179
  this.id = tabId;
178
180
  }
179
181
  async elementExists(selector, frameId = 0) {
180
- console.log(`check for ${selector} in tab ${this.id}, frame ${frameId}`);
181
182
  return this.sendContentMessage(this.id, {
182
183
  type: "elemExists",
183
184
  selector
@@ -186,7 +187,6 @@ class TabActions {
186
187
  });
187
188
  }
188
189
  async clickElement(selector, frameId = 0) {
189
- console.log(`click element ${selector} in tab ${this.id}`);
190
190
  return this.sendContentMessage(this.id, {
191
191
  type: "click",
192
192
  selector
@@ -195,7 +195,6 @@ class TabActions {
195
195
  });
196
196
  }
197
197
  async clickElements(selector, frameId = 0) {
198
- console.log(`click elements ${selector} in tab ${this.id}`);
199
198
  return this.sendContentMessage(this.id, {
200
199
  type: "click",
201
200
  all: true,
@@ -238,10 +237,11 @@ class TabActions {
238
237
  }
239
238
  return false;
240
239
  }
241
- async hideElements(selectors, frameId = 0) {
240
+ async hideElements(selectors, frameId = 0, method = 'display') {
242
241
  return this.sendContentMessage(this.id, {
243
242
  type: "hide",
244
- selectors
243
+ selectors,
244
+ method,
245
245
  }, { frameId });
246
246
  }
247
247
  async undoHideElements(frameId = 0) {
@@ -257,7 +257,9 @@ class TabActions {
257
257
  }
258
258
  wait(ms) {
259
259
  return new Promise(resolve => {
260
- setTimeout(() => resolve(true), ms);
260
+ setTimeout(() => {
261
+ resolve(true);
262
+ }, ms);
261
263
  });
262
264
  }
263
265
  matches(matcherConfig) {
@@ -641,9 +643,37 @@ async function evalAction(config) {
641
643
  });
642
644
  }
643
645
 
646
+ // get or create a style container for CSS overrides
647
+ function getStyleElementUtil() {
648
+ const styleOverrideElementId = "autoconsent-css-rules";
649
+ const styleSelector = `style#${styleOverrideElementId}`;
650
+ const existingElement = document.querySelector(styleSelector);
651
+ if (existingElement && existingElement instanceof HTMLStyleElement) {
652
+ return existingElement;
653
+ }
654
+ else {
655
+ const parent = document.head ||
656
+ document.getElementsByTagName("head")[0] ||
657
+ document.documentElement;
658
+ const css = document.createElement("style");
659
+ css.id = styleOverrideElementId;
660
+ parent.appendChild(css);
661
+ return css;
662
+ }
663
+ }
664
+ // hide elements with a CSS rule
665
+ function hideElementsUtil(selectors, method) {
666
+ const hidingSnippet = method === 'display' ? `display: none` : `opacity: 0`;
667
+ const rule = `${selectors.join(",")} { ${hidingSnippet} !important; z-index: -1 !important; pointer-events: none !important; } `;
668
+ const styleEl = getStyleElementUtil();
669
+ if (styleEl instanceof HTMLStyleElement) {
670
+ styleEl.innerText += rule;
671
+ return selectors.length > 0;
672
+ }
673
+ return false;
674
+ }
675
+
644
676
  let actionQueue = Promise.resolve(null);
645
- const styleOverrideElementId = "autoconsent-css-rules";
646
- const styleSelector = `style#${styleOverrideElementId}`;
647
677
  function handleMessage(message, debug = false) {
648
678
  if (message.type === "click") {
649
679
  const elem = document.querySelectorAll(message.selector);
@@ -667,8 +697,9 @@ function handleMessage(message, debug = false) {
667
697
  const elem = document.querySelectorAll(message.selector);
668
698
  const results = new Array(elem.length);
669
699
  elem.forEach((e, i) => {
670
- results[i] = e.offsetParent !== null || window.getComputedStyle(e).display !== "none" || e.style?.display !== "none";
700
+ results[i] = e.offsetParent !== null || window.getComputedStyle(e).display !== "none"; // TODO: handle visibility and z-index?
671
701
  });
702
+ debug && console.log("[visible?]", message.selector, elem, results);
672
703
  if (results.length === 0) {
673
704
  return false;
674
705
  }
@@ -683,6 +714,7 @@ function handleMessage(message, debug = false) {
683
714
  }
684
715
  else if (message.type === "getAttribute") {
685
716
  const elem = document.querySelector(message.selector);
717
+ debug && console.log("[getAttribute]", message.selector, elem);
686
718
  if (!elem) {
687
719
  return false;
688
720
  }
@@ -690,31 +722,16 @@ function handleMessage(message, debug = false) {
690
722
  }
691
723
  else if (message.type === "eval") {
692
724
  // TODO: chrome support
725
+ debug && console.log("about to [eval]", message.script); // this will not show in Webkit console
693
726
  const result = window.eval(message.script); // eslint-disable-line no-eval
694
- debug && console.log("[eval]", message.script, result);
695
727
  return result;
696
728
  }
697
729
  else if (message.type === "hide") {
698
- const parent = document.head ||
699
- document.getElementsByTagName("head")[0] ||
700
- document.documentElement;
701
- const rule = `${message.selectors.join(",")} { display: none !important; z-index: -1 !important; } `;
702
- const existingElement = document.querySelector(styleSelector);
703
- debug && console.log("[hide]", message.selectors, !!existingElement);
704
- if (existingElement && existingElement instanceof HTMLStyleElement) {
705
- existingElement.innerText += rule;
706
- }
707
- else {
708
- const css = document.createElement("style");
709
- css.type = "text/css";
710
- css.id = styleOverrideElementId;
711
- css.appendChild(document.createTextNode(rule));
712
- parent.appendChild(css);
713
- }
714
- return message.selectors.length > 0;
730
+ debug && console.log("[hide]", message.selectors);
731
+ return hideElementsUtil(message.selectors, message.method);
715
732
  }
716
733
  else if (message.type === "undohide") {
717
- const existingElement = document.querySelector(styleSelector);
734
+ const existingElement = getStyleElementUtil();
718
735
  debug && console.log("[unhide]", !!existingElement);
719
736
  if (existingElement) {
720
737
  existingElement.remove();
@@ -723,9 +740,11 @@ function handleMessage(message, debug = false) {
723
740
  }
724
741
  else if (message.type === "matches") {
725
742
  const matched = matches(message.config);
743
+ debug && console.log("[matches?]", message.config.type, JSON.stringify(message.config), matched);
726
744
  return matched;
727
745
  }
728
746
  else if (message.type === "executeAction") {
747
+ debug && console.log("[executeAction]", message);
729
748
  actionQueue = actionQueue.then(() => executeAction(message.config, message.param));
730
749
  return true;
731
750
  }
@@ -754,10 +773,12 @@ class TabConsent {
754
773
  }
755
774
  async doOptOut() {
756
775
  try {
776
+ enableLogs && console.log(`doing opt out ${this.getCMPName()} in tab ${this.tab.id}`);
757
777
  this.optOutStatus = await this.rule.optOut(this.tab);
758
778
  return this.optOutStatus;
759
779
  }
760
780
  catch (e) {
781
+ console.error('error during opt out', e);
761
782
  this.optOutStatus = e;
762
783
  throw e;
763
784
  }
@@ -802,6 +823,7 @@ async function detectDialog(tab, retries, rules) {
802
823
  try {
803
824
  if (await r.detectCmp(tab)) {
804
825
  earlyReturn = true;
826
+ enableLogs && console.log(`Found CMP in [${tab.id}]: ${r.name}`);
805
827
  resolve(index);
806
828
  }
807
829
  }
@@ -1142,12 +1164,45 @@ class Evidon extends AutoConsentBase {
1142
1164
  }
1143
1165
  }
1144
1166
 
1167
+ class Onetrust extends AutoConsentBase {
1168
+ constructor() {
1169
+ super("Onetrust");
1170
+ this.prehideSelectors = ["#onetrust-banner-sdk,#onetrust-consent-sdk,.optanon-alert-box-wrapper,.onetrust-pc-dark-filter,.js-consent-banner"];
1171
+ }
1172
+ detectCmp(tab) {
1173
+ return tab.elementExists("#onetrust-banner-sdk,.optanon-alert-box-wrapper");
1174
+ }
1175
+ detectPopup(tab) {
1176
+ return tab.elementsAreVisible("#onetrust-banner-sdk,.optanon-alert-box-wrapper");
1177
+ }
1178
+ async optOut(tab) {
1179
+ if (await tab.elementExists("#onetrust-pc-btn-handler")) { // "show purposes" button inside a popup
1180
+ await success(tab.clickElement("#onetrust-pc-btn-handler"));
1181
+ }
1182
+ else { // otherwise look for a generic "show settings" button
1183
+ await success(tab.clickElement(".ot-sdk-show-settings,button.js-cookie-settings"));
1184
+ }
1185
+ await success(tab.waitForElement("#onetrust-consent-sdk", 2000));
1186
+ await success(tab.wait(1000));
1187
+ await tab.clickElements("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked"); // optional step
1188
+ await success(tab.waitForThenClick(".save-preference-btn-handler,.js-consent-save", 1000));
1189
+ return true;
1190
+ }
1191
+ async optIn(tab) {
1192
+ return tab.clickElement("onetrust-accept-btn-handler,js-accept-cookies");
1193
+ }
1194
+ async test(tab) {
1195
+ return tab.eval("window.OnetrustActiveGroups.split(',').filter(s => s.length > 0).length <= 1");
1196
+ }
1197
+ }
1198
+
1145
1199
  const rules = [
1146
1200
  new TrustArc(),
1147
1201
  new Cookiebot(),
1148
1202
  new SourcePoint(),
1149
1203
  new ConsentManager(),
1150
1204
  new Evidon(),
1205
+ new Onetrust(),
1151
1206
  ];
1152
1207
  function createAutoCMP(config) {
1153
1208
  return new AutoConsent(config);
@@ -1219,7 +1274,7 @@ async function prehideElements(tab, rules) {
1219
1274
  }
1220
1275
  return selectorList;
1221
1276
  }, globalHidden);
1222
- await tab.hideElements(selectors);
1277
+ await tab.hideElements(selectors, undefined, 'opacity');
1223
1278
  }
1224
1279
 
1225
1280
  class AutoConsent$1 {
@@ -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,45 @@ 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
+ return true;
960
+ }
961
+ async optIn(tab) {
962
+ return tab.clickElement("onetrust-accept-btn-handler,js-accept-cookies");
963
+ }
964
+ async test(tab) {
965
+ return tab.eval("window.OnetrustActiveGroups.split(',').filter(s => s.length > 0).length <= 1");
966
+ }
967
+ }
968
+
882
969
  const rules = [
883
970
  new TrustArc(),
884
971
  new Cookiebot(),
885
972
  new SourcePoint(),
886
973
  new ConsentManager(),
887
974
  new Evidon(),
975
+ new Onetrust(),
888
976
  ];
889
977
  function createAutoCMP(config) {
890
978
  return new AutoConsent(config);
@@ -945,10 +1033,11 @@ class ConsentOMaticCMP {
945
1033
  }
946
1034
  }
947
1035
 
948
- function attachToPage(page, url, rules, retries = 1) {
949
- const frames = {};
1036
+ function attachToPage(page, url, rules, retries = 1, prehide = true) {
1037
+ const frames = {
1038
+ 0: page.mainFrame(),
1039
+ };
950
1040
  const tab = new Tab(page, url, frames);
951
- frames[0] = page.mainFrame();
952
1041
  async function onFrame(frame) {
953
1042
  const allFrames = await page.frames();
954
1043
  allFrames.forEach((frame, frameId) => {
@@ -967,6 +1056,9 @@ function attachToPage(page, url, rules, retries = 1) {
967
1056
  }
968
1057
  page.on('framenavigated', onFrame);
969
1058
  page.frames().forEach(onFrame);
1059
+ if (prehide) {
1060
+ prehideElements(tab, rules);
1061
+ }
970
1062
  return new TabConsent(tab, detectDialog(tab, retries, rules));
971
1063
  }
972
1064
 
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);