@duckduckgo/autoconsent 3.0.4 → 4.1.0

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 (183) hide show
  1. package/.eslintrc +21 -0
  2. package/.github/ISSUE_TEMPLATE/broken-site-issue.md +29 -0
  3. package/.github/dependabot.yml +6 -0
  4. package/.github/workflows/checks.yml +19 -0
  5. package/.prettierrc +4 -0
  6. package/CHANGELOG.md +65 -0
  7. package/Jenkinsfile +4 -4
  8. package/api.md +10 -1
  9. package/dist/addon-firefox/background.bundle.js +312 -1
  10. package/dist/addon-firefox/content.bundle.js +1912 -1
  11. package/dist/addon-firefox/manifest.json +2 -2
  12. package/dist/addon-firefox/rules.json +183 -42
  13. package/dist/addon-mv3/background.bundle.js +312 -1
  14. package/dist/addon-mv3/content.bundle.js +1912 -1
  15. package/dist/addon-mv3/devtools/background.html +10 -0
  16. package/dist/addon-mv3/devtools/bulma.min.css +1 -0
  17. package/dist/addon-mv3/devtools/loader.js +2 -0
  18. package/dist/addon-mv3/devtools/panel.html +88 -0
  19. package/dist/addon-mv3/devtools/panel.js +148 -0
  20. package/dist/addon-mv3/devtools/panel.ts +145 -0
  21. package/dist/addon-mv3/manifest.json +5 -3
  22. package/dist/addon-mv3/popup.bundle.js +173 -1
  23. package/dist/addon-mv3/popup.html +14 -0
  24. package/dist/addon-mv3/rules.json +183 -42
  25. package/dist/autoconsent.cjs.js +1 -1
  26. package/dist/autoconsent.esm.js +1 -1
  27. package/dist/autoconsent.playwright.js +1 -1
  28. package/lib/cmps/airbnb.ts +4 -0
  29. package/lib/cmps/base.ts +8 -0
  30. package/lib/cmps/consentmanager.ts +4 -0
  31. package/lib/cmps/consentomatic.ts +1 -0
  32. package/lib/cmps/conversant.ts +4 -0
  33. package/lib/cmps/cookiebot.ts +4 -0
  34. package/lib/cmps/evidon.ts +4 -0
  35. package/lib/cmps/klaro.ts +4 -0
  36. package/lib/cmps/onetrust.ts +4 -0
  37. package/lib/cmps/sourcepoint-frame.ts +35 -7
  38. package/lib/cmps/tiktok.ts +4 -0
  39. package/lib/cmps/trustarc-frame.ts +4 -0
  40. package/lib/cmps/trustarc-top.ts +4 -0
  41. package/lib/cmps/uniconsent.ts +4 -0
  42. package/lib/consentomatic/index.ts +12 -12
  43. package/lib/consentomatic/tools.ts +13 -13
  44. package/lib/eval-handler.ts +2 -6
  45. package/lib/messages.ts +44 -13
  46. package/lib/random.ts +6 -0
  47. package/lib/rule-executors.ts +8 -3
  48. package/lib/rules.ts +2 -1
  49. package/lib/types.ts +27 -0
  50. package/lib/web.ts +127 -82
  51. package/package.json +11 -8
  52. package/playwright/runner.ts +1 -0
  53. package/rollup.config.js +18 -5
  54. package/rules/autoconsent/affinity-serif-com.json +1 -4
  55. package/rules/autoconsent/ausopen.json +2 -2
  56. package/rules/autoconsent/baden-wuerttemberg-de.json +1 -0
  57. package/rules/autoconsent/cc-banner.json +1 -0
  58. package/rules/autoconsent/complianz-notice.json +1 -0
  59. package/rules/autoconsent/cookie-notice.json +2 -1
  60. package/rules/autoconsent/cookiealert.json +48 -0
  61. package/rules/autoconsent/dsgvo.json +1 -0
  62. package/rules/autoconsent/eu-cookie-compliance.json +1 -1
  63. package/rules/autoconsent/eu-cookie-law.json +1 -0
  64. package/rules/autoconsent/ezoic.json +1 -0
  65. package/rules/autoconsent/generic-cosmetic.json +9 -0
  66. package/rules/autoconsent/indeed-com.json +9 -0
  67. package/rules/autoconsent/jquery-cookiebar.json +1 -0
  68. package/rules/autoconsent/marksandspencer.json +2 -2
  69. package/rules/autoconsent/notice-cookie.json +1 -0
  70. package/rules/autoconsent/osano.json +1 -0
  71. package/rules/autoconsent/pornhub.json +12 -0
  72. package/rules/autoconsent/uk-cookie-consent.json +1 -0
  73. package/rules/autoconsent/xnxx-com.json +9 -0
  74. package/rules/rules.json +183 -42
  75. package/tests/192.spec.ts +3 -3
  76. package/tests/adroll.spec.ts +7 -7
  77. package/tests/affinity-serif-com.spec.ts +1 -1
  78. package/tests/airbnb.spec.ts +3 -3
  79. package/tests/amazon.spec.ts +8 -8
  80. package/tests/arzt-auskunft.spec.ts +3 -3
  81. package/tests/ausopen.spec.ts +2 -2
  82. package/tests/aws.amazon.spec.ts +3 -3
  83. package/tests/axeptio.spec.ts +3 -3
  84. package/tests/baden-wuerttemberg.spec.ts +3 -3
  85. package/tests/borlabs.spec.ts +4 -4
  86. package/tests/bundesregierung.spec.ts +6 -6
  87. package/tests/ccbanner.spec.ts +4 -4
  88. package/tests/clickio.spec.ts +5 -5
  89. package/tests/complianz-banner.spec.ts +2 -2
  90. package/tests/complianz-categories.spec.ts +5 -5
  91. package/tests/complianz-notice.spec.ts +3 -3
  92. package/tests/complianz-optin.spec.ts +4 -4
  93. package/tests/consentmanager.spec.ts +8 -8
  94. package/tests/conversant.spec.ts +5 -5
  95. package/tests/cookie-notice.spec.ts +5 -3
  96. package/tests/cookiealert.spec.ts +9 -0
  97. package/tests/cookiebot.spec.ts +7 -11
  98. package/tests/cookieinformation.spec.ts +4 -5
  99. package/tests/cookielawinfo.spec.ts +3 -3
  100. package/tests/corona-in-zahlen.spec.ts +3 -3
  101. package/tests/dailymotion.spec.ts +5 -5
  102. package/tests/deepl.spec.ts +3 -3
  103. package/tests/didomi.spec.ts +8 -8
  104. package/tests/dmgmedia.spec.ts +7 -7
  105. package/tests/drupal.spec.ts +3 -3
  106. package/tests/dsgvo.spec.ts +4 -1
  107. package/tests/dunelm.spec.ts +3 -3
  108. package/tests/etsy.spec.ts +2 -2
  109. package/tests/eu-cookie-compliance-banner.spec.ts +2 -2
  110. package/tests/eu-cookie-law.spec.ts +2 -2
  111. package/tests/evidon.spec.ts +4 -6
  112. package/tests/ezoic.spec.ts +2 -3
  113. package/tests/facebook.spec.ts +5 -5
  114. package/tests/fundingchoices.spec.ts +5 -6
  115. package/tests/generic-cosmetic.spec.ts +11 -0
  116. package/tests/google.spec.ts +2 -2
  117. package/tests/gov-uk.spec.ts +4 -4
  118. package/tests/hl-co-uk.spec.ts +3 -3
  119. package/tests/hubspot.spec.ts +2 -2
  120. package/tests/indeed.spec.ts +7 -0
  121. package/tests/ionos.spec.ts +3 -3
  122. package/tests/iubenda.spec.ts +3 -3
  123. package/tests/johnlewis.spec.ts +3 -3
  124. package/tests/jquery-cookiebar.spec.ts +2 -2
  125. package/tests/klaro.spec.ts +5 -5
  126. package/tests/linkedin.spec.ts +2 -2
  127. package/tests/marksandspencer.spec.ts +2 -2
  128. package/tests/mediamarkt.spec.ts +3 -3
  129. package/tests/mediavine.spec.ts +3 -3
  130. package/tests/metoffice-gov-uk.spec.ts +3 -3
  131. package/tests/microsoft.spec.ts +4 -4
  132. package/tests/moneysavingexpert.spec.ts +3 -3
  133. package/tests/monzo-com.spec.ts +1 -1
  134. package/tests/moove.spec.ts +4 -10
  135. package/tests/national-lottery.spec.ts +3 -3
  136. package/tests/netflix.spec.ts +3 -3
  137. package/tests/nhs.spec.ts +3 -3
  138. package/tests/notice-cookie.spec.ts +2 -2
  139. package/tests/obi.spec.ts +3 -3
  140. package/tests/oil.spec.ts +6 -6
  141. package/tests/onetrust.spec.ts +24 -24
  142. package/tests/osano.spec.ts +1 -1
  143. package/tests/otto.spec.ts +3 -3
  144. package/tests/paypal.spec.ts +4 -4
  145. package/tests/pornhub.spec.ts +7 -0
  146. package/tests/primebox.spec.ts +2 -2
  147. package/tests/privacymanager.spec.ts +3 -3
  148. package/tests/pubtech.spec.ts +8 -9
  149. package/tests/quantcast.spec.ts +10 -10
  150. package/tests/reddit.spec.ts +3 -3
  151. package/tests/sibbo.spec.ts +9 -10
  152. package/tests/sirdata.spec.ts +2 -3
  153. package/tests/snigel.spec.ts +3 -3
  154. package/tests/sourcepoint.spec.ts +17 -17
  155. package/tests/springer.spec.ts +6 -6
  156. package/tests/steampowered.spec.ts +3 -3
  157. package/tests/tarteaucitron.spec.ts +4 -4
  158. package/tests/tealium.spec.ts +2 -2
  159. package/tests/termly.spec.ts +4 -4
  160. package/tests/testcmp.spec.ts +1 -1
  161. package/tests/thalia.spec.ts +3 -3
  162. package/tests/thefreedictionary.spec.ts +3 -3
  163. package/tests/tiktok.spec.ts +5 -5
  164. package/tests/trustarc.spec.ts +13 -15
  165. package/tests/twitter.spec.ts +7 -7
  166. package/tests/uk-cookie-consent.spec.ts +3 -3
  167. package/tests/uniconsent.spec.ts +4 -4
  168. package/tests/usercentrics-api.spec.ts +6 -5
  169. package/tests/usercentrics-button.spec.ts +2 -3
  170. package/tests/uswitch.spec.ts +3 -3
  171. package/tests/vodafone.spec.ts +3 -3
  172. package/tests/waitrose.spec.ts +3 -3
  173. package/tests/wetransfer.spec.ts +3 -3
  174. package/tests/wordpressgdpr.spec.ts +3 -3
  175. package/tests/wp-cookie-notice.spec.ts +3 -3
  176. package/tests/xing.spec.ts +4 -4
  177. package/tests/xnxx.spec.ts +8 -0
  178. package/tests/youtube-desktop.spec.ts +2 -4
  179. package/tests/youtube-mobile.spec.ts +3 -3
  180. package/tsconfig.json +2 -1
  181. package/.eslintrc.cjs +0 -14
  182. package/rules/autoconsent/motor-talk-de.json +0 -24
  183. package/tests/motor-talk.spec.ts +0 -7
@@ -21,6 +21,7 @@ export class ConsentOMaticCMP implements AutoCMP {
21
21
  methods = new Map<string, any>();
22
22
  hasSelfTest: boolean;
23
23
  runContext: RunContext = defaultRunContext;
24
+ isCosmetic = false;
24
25
 
25
26
  constructor(public name: string, public config: ConsentOMaticConfig) {
26
27
  config.methods.forEach(methodConfig => {
@@ -18,6 +18,10 @@ export default class Conversant extends AutoConsentCMPBase {
18
18
  return false;
19
19
  }
20
20
 
21
+ get isCosmetic(): boolean {
22
+ return false;
23
+ }
24
+
21
25
  async detectCmp() {
22
26
  return elementExists(".cmp-root .cmp-receptacle");
23
27
  }
@@ -17,6 +17,10 @@ export default class Cookiebot extends AutoConsentCMPBase {
17
17
  return false;
18
18
  }
19
19
 
20
+ get isCosmetic(): boolean {
21
+ return false;
22
+ }
23
+
20
24
  async detectCmp() {
21
25
  return elementExists('#CybotCookiebotDialogBodyLevelButtonPreferences');
22
26
  }
@@ -15,6 +15,10 @@ export default class Evidon extends AutoConsentCMPBase {
15
15
  return false;
16
16
  }
17
17
 
18
+ get isCosmetic(): boolean {
19
+ return false;
20
+ }
21
+
18
22
  async detectCmp() {
19
23
  return elementExists("#_evidon_banner");
20
24
  }
package/lib/cmps/klaro.ts CHANGED
@@ -18,6 +18,10 @@ export default class Klaro extends AutoConsentCMPBase {
18
18
  return false;
19
19
  }
20
20
 
21
+ get isCosmetic(): boolean {
22
+ return false;
23
+ }
24
+
21
25
  async detectCmp() {
22
26
  if (elementExists('.klaro > .cookie-modal')) {
23
27
  this.settingsOpen = true;
@@ -18,6 +18,10 @@ export default class Onetrust extends AutoConsentCMPBase {
18
18
  return false;
19
19
  }
20
20
 
21
+ get isCosmetic(): boolean {
22
+ return false;
23
+ }
24
+
21
25
  async detectCmp() {
22
26
  return elementExists("#onetrust-banner-sdk");
23
27
  }
@@ -5,9 +5,10 @@ import { waitFor } from "../utils";
5
5
  import AutoConsentCMPBase from "./base";
6
6
 
7
7
  export default class SourcePoint extends AutoConsentCMPBase {
8
- prehideSelectors = ["div[id^='sp_message_container_'],.message-overlay"]
8
+ prehideSelectors = ["div[id^='sp_message_container_'],.message-overlay",'#sp_privacy_manager_container']
9
9
 
10
- ccpaMode = false;
10
+ ccpaNotice = false;
11
+ ccpaPopup = false;
11
12
 
12
13
  runContext: RunContext = {
13
14
  main: false,
@@ -26,10 +27,18 @@ export default class SourcePoint extends AutoConsentCMPBase {
26
27
  return false;
27
28
  }
28
29
 
30
+ get isCosmetic(): boolean {
31
+ return false;
32
+ }
33
+
29
34
  async detectCmp() {
30
35
  const url = new URL(location.href);
31
36
  if (url.searchParams.has('message_id') && url.hostname === 'ccpa-notice.sp-prod.net') {
32
- this.ccpaMode = true;
37
+ this.ccpaNotice = true;
38
+ return true;
39
+ }
40
+ if (url.hostname === 'ccpa-pm.sp-prod.net') {
41
+ this.ccpaPopup = true;
33
42
  return true;
34
43
  }
35
44
  return (url.pathname === '/index.html' || url.pathname === '/privacy-manager/index.html')
@@ -37,6 +46,12 @@ export default class SourcePoint extends AutoConsentCMPBase {
37
46
  }
38
47
 
39
48
  async detectPopup() {
49
+ if (this.ccpaNotice) {
50
+ return true;
51
+ }
52
+ if (this.ccpaPopup) {
53
+ return await waitForElement('.priv-save-btn', 2000);
54
+ }
40
55
  // check for the paywall button, and bail if it exists to prevent broken opt out
41
56
  await waitForElement(".sp_choice_type_11,.sp_choice_type_12,.sp_choice_type_13,.sp_choice_type_ACCEPT_ALL", 2000);
42
57
  return !elementExists('.sp_choice_type_9');
@@ -55,10 +70,23 @@ export default class SourcePoint extends AutoConsentCMPBase {
55
70
  }
56
71
 
57
72
  isManagerOpen() {
58
- return (new URL(location.href)).pathname === "/privacy-manager/index.html";
73
+ return location.pathname === "/privacy-manager/index.html";
59
74
  }
60
75
 
61
76
  async optOut() {
77
+ if (this.ccpaPopup) {
78
+ // toggles with 2 buttons
79
+ const toggles = document.querySelectorAll('.priv-purpose-container .sp-switch-arrow-block a.neutral.on .right') as NodeListOf<HTMLElement>;
80
+ for (const t of toggles) {
81
+ click([t]);
82
+ }
83
+ // switch toggles
84
+ const switches = document.querySelectorAll('.priv-purpose-container .sp-switch-arrow-block a.switch-bg.on') as NodeListOf<HTMLElement>;
85
+ for (const t of switches) {
86
+ click([t]);
87
+ }
88
+ return click('.priv-save-btn');
89
+ }
62
90
  if (!this.isManagerOpen()) {
63
91
  const actionable = await waitForElement('.sp_choice_type_12,.sp_choice_type_13');
64
92
  if (!actionable) {
@@ -72,7 +100,7 @@ export default class SourcePoint extends AutoConsentCMPBase {
72
100
  click(".sp_choice_type_12");
73
101
  // the page may navigate at this point but that's okay
74
102
  await waitFor(
75
- () => location.pathname === "/privacy-manager/index.html",
103
+ () => this.isManagerOpen(),
76
104
  200,
77
105
  100
78
106
  );
@@ -102,7 +130,7 @@ export default class SourcePoint extends AutoConsentCMPBase {
102
130
  } catch (e) {
103
131
  enableLogs && console.warn(e);
104
132
  }
105
- click('.sp_choice_type_SAVE_AND_EXIT');
106
- return true;
133
+ // TODO: race condition: the popup disappears very quickly, so the background script may not receive a success report.
134
+ return click('.sp_choice_type_SAVE_AND_EXIT');
107
135
  }
108
136
  }
@@ -21,6 +21,10 @@ export default class Tiktok extends AutoConsentCMPBase {
21
21
  return false;
22
22
  }
23
23
 
24
+ get isCosmetic(): boolean {
25
+ return false;
26
+ }
27
+
24
28
  getShadowRoot() {
25
29
  const container = document.querySelector('tiktok-cookie-banner');
26
30
  if (!container) {
@@ -22,6 +22,10 @@ export default class TrustArcFrame extends AutoConsentCMPBase {
22
22
  return false;
23
23
  }
24
24
 
25
+ get isCosmetic(): boolean {
26
+ return false;
27
+ }
28
+
25
29
  async detectCmp() {
26
30
  return true;
27
31
  }
@@ -41,6 +41,10 @@ export default class TrustArcTop extends AutoConsentCMPBase {
41
41
  return !this._shortcutButton;
42
42
  }
43
43
 
44
+ get isCosmetic(): boolean {
45
+ return false;
46
+ }
47
+
44
48
  async detectCmp() {
45
49
  const result = elementExists(`${cookieSettingsButton},${bannerContainer}`);
46
50
  if (result) {
@@ -18,6 +18,10 @@ export default class Uniconsent extends AutoConsentCMPBase {
18
18
  return false;
19
19
  }
20
20
 
21
+ get isCosmetic(): boolean {
22
+ return false;
23
+ }
24
+
21
25
  async detectCmp() {
22
26
  return elementExists(".unic .unic-box,.unic .unic-bar");
23
27
  }
@@ -28,7 +28,7 @@ export async function executeAction(config: any, param?: any): Promise<boolean |
28
28
  case "slide":
29
29
  return slideAction(config);
30
30
  case "close":
31
- return closeAction(config);
31
+ return closeAction();
32
32
  case "wait":
33
33
  return waitAction(config);
34
34
  case "eval":
@@ -57,7 +57,7 @@ async function clickAction(config: any) {
57
57
  }
58
58
 
59
59
  async function listAction(config: any, param: any) {
60
- for (let action of config.actions) {
60
+ for (const action of config.actions) {
61
61
  await executeAction(action, param);
62
62
  }
63
63
  }
@@ -141,8 +141,8 @@ async function slideAction(config: any) {
141
141
  const result = Tools.find(config);
142
142
  const dragResult = Tools.find(config.dragTarget);
143
143
  if (result.target) {
144
- let targetBounds = result.target.getBoundingClientRect();
145
- let dragTargetBounds = dragResult.target.getBoundingClientRect();
144
+ const targetBounds = result.target.getBoundingClientRect();
145
+ const dragTargetBounds = dragResult.target.getBoundingClientRect();
146
146
 
147
147
  let yDiff = dragTargetBounds.top - targetBounds.top;
148
148
  let xDiff = dragTargetBounds.left - targetBounds.left;
@@ -154,12 +154,12 @@ async function slideAction(config: any) {
154
154
  yDiff = 0;
155
155
  }
156
156
 
157
- let screenX = window.screenX + targetBounds.left + targetBounds.width / 2.0;
158
- let screenY = window.screenY + targetBounds.top + targetBounds.height / 2.0;
159
- let clientX = targetBounds.left + targetBounds.width / 2.0;
160
- let clientY = targetBounds.top + targetBounds.height / 2.0;
157
+ const screenX = window.screenX + targetBounds.left + targetBounds.width / 2.0;
158
+ const screenY = window.screenY + targetBounds.top + targetBounds.height / 2.0;
159
+ const clientX = targetBounds.left + targetBounds.width / 2.0;
160
+ const clientY = targetBounds.top + targetBounds.height / 2.0;
161
161
 
162
- let mouseDown = document.createEvent("MouseEvents");
162
+ const mouseDown = document.createEvent("MouseEvents");
163
163
  mouseDown.initMouseEvent(
164
164
  "mousedown",
165
165
  true,
@@ -177,7 +177,7 @@ async function slideAction(config: any) {
177
177
  0,
178
178
  result.target
179
179
  );
180
- let mouseMove = document.createEvent("MouseEvents");
180
+ const mouseMove = document.createEvent("MouseEvents");
181
181
  mouseMove.initMouseEvent(
182
182
  "mousemove",
183
183
  true,
@@ -195,7 +195,7 @@ async function slideAction(config: any) {
195
195
  0,
196
196
  result.target
197
197
  );
198
- let mouseUp = document.createEvent("MouseEvents");
198
+ const mouseUp = document.createEvent("MouseEvents");
199
199
  mouseUp.initMouseEvent(
200
200
  "mouseup",
201
201
  true,
@@ -225,7 +225,7 @@ async function waitAction(config: any) {
225
225
  await waitTimeout(config.waitTime);
226
226
  }
227
227
 
228
- async function closeAction(config: any) {
228
+ async function closeAction() {
229
229
  window.close();
230
230
  }
231
231
 
@@ -28,12 +28,12 @@ export default class Tools {
28
28
 
29
29
  if (options.textFilter != null) {
30
30
  possibleTargets = possibleTargets.filter(possibleTarget => {
31
- let textContent = possibleTarget.textContent.toLowerCase();
31
+ const textContent = possibleTarget.textContent.toLowerCase();
32
32
 
33
33
  if (Array.isArray(options.textFilter)) {
34
34
  let foundText = false;
35
35
 
36
- for (let text of options.textFilter) {
36
+ for (const text of options.textFilter) {
37
37
  if (textContent.indexOf(text.toLowerCase()) !== -1) {
38
38
  foundText = true;
39
39
  break;
@@ -49,12 +49,12 @@ export default class Tools {
49
49
 
50
50
  if (options.styleFilters != null) {
51
51
  possibleTargets = possibleTargets.filter(possibleTarget => {
52
- let styles = window.getComputedStyle(possibleTarget);
52
+ const styles = window.getComputedStyle(possibleTarget);
53
53
 
54
54
  let keep = true;
55
55
 
56
- for (let styleFilter of options.styleFilters) {
57
- let option = styles[styleFilter.option];
56
+ for (const styleFilter of options.styleFilters) {
57
+ const option = styles[styleFilter.option];
58
58
 
59
59
  if (styleFilter.negated) {
60
60
  keep = keep && option !== styleFilter.value;
@@ -80,7 +80,7 @@ export default class Tools {
80
80
  }
81
81
 
82
82
  if (options.iframeFilter != null) {
83
- possibleTargets = possibleTargets.filter(possibleTarget => {
83
+ possibleTargets = possibleTargets.filter((/* possibleTarget */) => {
84
84
  if (options.iframeFilter) {
85
85
  //We should be inside an iframe
86
86
  return window.location !== window.parent.location;
@@ -93,9 +93,9 @@ export default class Tools {
93
93
 
94
94
  if (options.childFilter != null) {
95
95
  possibleTargets = possibleTargets.filter(possibleTarget => {
96
- let oldBase = Tools.base;
96
+ const oldBase = Tools.base;
97
97
  Tools.setBase(possibleTarget);
98
- let childResults = Tools.find(options.childFilter);
98
+ const childResults = Tools.find(options.childFilter);
99
99
  Tools.setBase(oldBase);
100
100
  return childResults.target != null;
101
101
  });
@@ -118,13 +118,13 @@ export default class Tools {
118
118
  }
119
119
 
120
120
  static find(options: any, multiple = false) {
121
- let results: any[] = [];
121
+ const results: any[] = [];
122
122
  if (options.parent != null) {
123
- let parent = Tools.findElement(options.parent, null, multiple);
123
+ const parent = Tools.findElement(options.parent, null, multiple);
124
124
  if (parent != null) {
125
125
  if (parent instanceof Array) {
126
126
  parent.forEach(p => {
127
- let targets = Tools.findElement(options.target, p, multiple);
127
+ const targets = Tools.findElement(options.target, p, multiple);
128
128
  if (targets instanceof Array) {
129
129
  targets.forEach(target => {
130
130
  results.push({
@@ -142,7 +142,7 @@ export default class Tools {
142
142
 
143
143
  return results;
144
144
  } else {
145
- let targets = Tools.findElement(options.target, parent, multiple);
145
+ const targets = Tools.findElement(options.target, parent, multiple);
146
146
  if (targets instanceof Array) {
147
147
  targets.forEach(target => {
148
148
  results.push({
@@ -159,7 +159,7 @@ export default class Tools {
159
159
  }
160
160
  }
161
161
  } else {
162
- let targets = Tools.findElement(options.target, null, multiple);
162
+ const targets = Tools.findElement(options.target, null, multiple);
163
163
  if (targets instanceof Array) {
164
164
  targets.forEach(target => {
165
165
  results.push({
@@ -1,4 +1,5 @@
1
1
  import { ContentScriptMessage } from "./messages";
2
+ import { getRandomID } from "./random";
2
3
 
3
4
  class Deferred<T> {
4
5
  id: string;
@@ -30,12 +31,7 @@ export const evalState: EvalState = {
30
31
  }
31
32
 
32
33
  export function requestEval(code: string): Promise<boolean> {
33
- let id;
34
- if (crypto && typeof crypto.randomUUID !== 'undefined') {
35
- id = crypto.randomUUID();
36
- } else {
37
- id = Math.random().toString();
38
- }
34
+ const id = getRandomID();
39
35
  evalState.sendContentMessage({
40
36
  type: 'eval',
41
37
  id,
package/lib/messages.ts CHANGED
@@ -1,14 +1,14 @@
1
- import { Config, RuleBundle } from "./types";
1
+ import { Config, ConsentState, RuleBundle } from "./types";
2
2
 
3
3
  export type BackgroundMessage =
4
- InitResponseMessage
4
+ | InitResponseMessage
5
5
  | EvalResponseMessage
6
6
  | OptOutMessage
7
7
  | OptInMessage
8
- | SelfTestMessage;
8
+ | SelfTestMessage
9
9
 
10
10
  export type ContentScriptMessage =
11
- InitMessage
11
+ | InitMessage
12
12
  | EvalMessage
13
13
  | DetectedMessage
14
14
  | FoundMessage
@@ -16,7 +16,16 @@ export type ContentScriptMessage =
16
16
  | OptInResultMessage
17
17
  | SelfTestResultMessage
18
18
  | DoneMessage
19
- | ErrorMessage;
19
+ | ErrorMessage
20
+ | ReportMessage;
21
+
22
+ export type BackgroundDevtoolsMessage =
23
+ | DevtoolsAuditMessage
24
+ | InstanceTerminatedMessage
25
+ | InitResponseMessage;
26
+
27
+ export type DevtoolsMessage =
28
+ | DevtoolsInitMessage;
20
29
 
21
30
  export type InitMessage = {
22
31
  type: "init";
@@ -33,13 +42,13 @@ export type DetectedMessage = {
33
42
  type: "cmpDetected";
34
43
  cmp: string;
35
44
  url: string;
36
- }
45
+ };
37
46
 
38
47
  export type FoundMessage = {
39
48
  type: "popupFound";
40
49
  cmp: string;
41
50
  url: string;
42
- }
51
+ };
43
52
 
44
53
  export type OptOutResultMessage = {
45
54
  type: "optOutResult";
@@ -67,34 +76,56 @@ export type SelfTestResultMessage = {
67
76
  export type DoneMessage = {
68
77
  type: "autoconsentDone";
69
78
  cmp: string;
79
+ isCosmetic: boolean;
70
80
  url: string;
71
- }
81
+ };
72
82
 
73
83
  export type ErrorMessage = {
74
84
  type: "autoconsentError";
75
85
  details: any;
76
- }
86
+ };
77
87
 
78
88
  export type InitResponseMessage = {
79
89
  type: "initResp";
80
90
  rules: RuleBundle;
81
91
  config: Config;
82
- }
92
+ };
83
93
 
84
94
  export type EvalResponseMessage = {
85
95
  type: "evalResp";
86
96
  id: string;
87
97
  result: any;
88
- }
98
+ };
89
99
 
90
100
  export type OptOutMessage = {
91
101
  type: "optOut";
92
- }
102
+ };
93
103
 
94
104
  export type OptInMessage = {
95
105
  type: "optIn";
96
- }
106
+ };
97
107
 
98
108
  export type SelfTestMessage = {
99
109
  type: "selfTest";
110
+ };
111
+
112
+ export type ReportMessage = {
113
+ type: "report";
114
+ instanceId: string; // A unique identifier for the frame.
115
+ url: string; // Current frame URL
116
+ mainFrame: boolean; // `true` iff this frame is the top frame.
117
+ state: ConsentState;
118
+ };
119
+
120
+ export type DevtoolsAuditMessage = ReportMessage & { tabId: number, frameId: number }
121
+
122
+ export type InstanceTerminatedMessage = {
123
+ type: 'instanceTerminated';
124
+ tabId: number;
125
+ instanceId: string;
126
+ }
127
+
128
+ export type DevtoolsInitMessage = {
129
+ type: 'init';
130
+ tabId: number;
100
131
  }
package/lib/random.ts ADDED
@@ -0,0 +1,6 @@
1
+ export function getRandomID() {
2
+ if (crypto && typeof crypto.randomUUID !== "undefined") {
3
+ return crypto.randomUUID();
4
+ }
5
+ return Math.random().toString();
6
+ }
@@ -10,9 +10,14 @@ export function doEval(expr: string): Promise<boolean> {
10
10
  });
11
11
  }
12
12
 
13
- export function click(selector: string, all = false): boolean {
14
- const elem = document.querySelectorAll<HTMLElement>(selector);
15
- enableLogs && console.log("[click]", selector, all, elem);
13
+ export function click(selectorOrElements: string | HTMLElement[], all = false): boolean {
14
+ let elem: HTMLElement[] = [];
15
+ if (typeof selectorOrElements === 'string') {
16
+ elem = Array.from(document.querySelectorAll<HTMLElement>(selectorOrElements));
17
+ } else {
18
+ elem = selectorOrElements;
19
+ }
20
+ enableLogs && console.log("[click]", selectorOrElements, all, elem);
16
21
  if (elem.length > 0) {
17
22
  if (all) {
18
23
  elem.forEach((e) => e.click());
package/lib/rules.ts CHANGED
@@ -2,7 +2,8 @@ export type AutoConsentCMPRule = {
2
2
  name: string
3
3
  prehideSelectors?: string[]
4
4
  runContext?: RunContext,
5
- intermediate?: boolean
5
+ intermediate?: boolean,
6
+ cosmetic?: boolean,
6
7
  detectCmp: AutoConsentRuleStep[]
7
8
  detectPopup: AutoConsentRuleStep[]
8
9
  optOut: AutoConsentRuleStep[]
package/lib/types.ts CHANGED
@@ -8,6 +8,7 @@ export interface AutoCMP {
8
8
  name: string
9
9
  hasSelfTest: boolean
10
10
  isIntermediate: boolean;
11
+ isCosmetic: boolean;
11
12
  prehideSelectors?: string[];
12
13
  runContext: RunContext;
13
14
  checkRunContext(): boolean;
@@ -31,5 +32,31 @@ export type Config = {
31
32
  autoAction: AutoAction;
32
33
  disabledCmps: string[];
33
34
  enablePrehide: boolean;
35
+ enableCosmeticRules: boolean;
34
36
  detectRetries: number;
35
37
  }
38
+
39
+ export type LifecycleState = 'loading' |
40
+ 'initialized' |
41
+ 'waitingForInitResponse' |
42
+ 'started' |
43
+ 'nothingDetected' |
44
+ 'cmpDetected' |
45
+ 'openPopupDetected' |
46
+ 'runningOptOut' |
47
+ 'runningOptIn' |
48
+ 'optOutSucceeded' |
49
+ 'optOutFailed' |
50
+ 'optInSucceeded' |
51
+ 'optInFailed' |
52
+ 'done';
53
+
54
+ export type ConsentState = {
55
+ lifecycle: LifecycleState; // What point in the autoconsent lifecycle this script is at.
56
+ prehideOn: boolean; // If the script is currently hiding preHide elements.
57
+ findCmpAttempts: number; // Number of times we tried to find CMPs in this frame.
58
+ detectedCmps: string[]; // Names of CMP rules where `detectCmp` returned true.
59
+ detectedPopups: string[]; // Names of CMP rules where `detectPopup` returned true.
60
+ selfTest: boolean; // null if no self test was run, otherwise it holds the result of the self test.
61
+ }
62
+