@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
package/lib/web.ts CHANGED
@@ -1,18 +1,38 @@
1
1
  import { rules as dynamicRules, createAutoCMP } from './index';
2
- import { MessageSender, AutoCMP, RuleBundle, Config } from './types';
2
+ import { MessageSender, AutoCMP, RuleBundle, Config, ConsentState } from './types';
3
3
  import { ConsentOMaticCMP, ConsentOMaticConfig } from './cmps/consentomatic';
4
4
  import { AutoConsentCMPRule } from './rules';
5
5
  import { enableLogs } from './config';
6
6
  import { BackgroundMessage, InitMessage } from './messages';
7
7
  import { prehide, undoPrehide } from './rule-executors';
8
8
  import { evalState, resolveEval } from './eval-handler';
9
+ import { getRandomID } from './random';
9
10
 
10
11
  export * from './index';
11
12
 
13
+ function filterCMPs(rules: AutoCMP[], config: Config) {
14
+ return rules.filter((cmp) => {
15
+ return (
16
+ (!config.disabledCmps || !config.disabledCmps.includes(cmp.name)) // CMP is not disabled
17
+ &&
18
+ (config.enableCosmeticRules || !cmp.isCosmetic) // CMP is not cosmetic or cosmetic rules are enabled
19
+ );
20
+ });
21
+ }
22
+
12
23
  export default class AutoConsent {
24
+ id = getRandomID();
13
25
  rules: AutoCMP[] = [];
14
26
  config: Config;
15
27
  foundCmp: AutoCMP = null;
28
+ state: ConsentState = {
29
+ lifecycle: 'loading',
30
+ prehideOn: false,
31
+ findCmpAttempts: 0,
32
+ detectedCmps: [],
33
+ detectedPopups: [],
34
+ selfTest: null,
35
+ };
16
36
  protected sendContentMessage: MessageSender;
17
37
 
18
38
  constructor(sendContentMessage: MessageSender, config: Config = null, declarativeRules: RuleBundle = null) {
@@ -21,6 +41,7 @@ export default class AutoConsent {
21
41
  this.rules = [...dynamicRules];
22
42
 
23
43
  enableLogs && console.log('autoconsent init', window.location.href);
44
+ this.updateState({ lifecycle: 'loading' });
24
45
  if (config) {
25
46
  this.initialize(config, declarativeRules);
26
47
  } else {
@@ -32,6 +53,7 @@ export default class AutoConsent {
32
53
  url: window.location.href,
33
54
  };
34
55
  sendContentMessage(initMsg);
56
+ this.updateState({ lifecycle: 'waitingForInitResponse' });
35
57
  }
36
58
  }
37
59
 
@@ -45,9 +67,8 @@ export default class AutoConsent {
45
67
  if (declarativeRules) {
46
68
  this.parseRules(declarativeRules);
47
69
  }
48
- if (config.disabledCmps?.length > 0) {
49
- this.disableCMPs(config.disabledCmps);
50
- }
70
+
71
+ this.rules = filterCMPs(this.rules, config);
51
72
 
52
73
  if (config.enablePrehide) {
53
74
  if (document.documentElement) {
@@ -72,6 +93,7 @@ export default class AutoConsent {
72
93
  } else {
73
94
  this.start();
74
95
  }
96
+ this.updateState({ lifecycle: 'initialized' });
75
97
  }
76
98
 
77
99
  parseRules(declarativeRules: RuleBundle) {
@@ -88,10 +110,6 @@ export default class AutoConsent {
88
110
  this.rules.push(createAutoCMP(config));
89
111
  }
90
112
 
91
- disableCMPs(cmpNames: string[]) {
92
- this.rules = this.rules.filter((cmp) => !cmpNames.includes(cmp.name))
93
- }
94
-
95
113
  addConsentomaticCMP(name: string, config: ConsentOMaticConfig) {
96
114
  this.rules.push(new ConsentOMaticCMP(`com_${name}`, config));
97
115
  }
@@ -106,73 +124,62 @@ export default class AutoConsent {
106
124
  }
107
125
 
108
126
  async _start() {
109
- enableLogs && console.log(`Detecting CMPs on ${window.location.href}`)
110
- const cmps = await this.findCmp(this.config.detectRetries);
111
- if (cmps.length > 0) {
112
- const popupLookups: Promise<boolean>[] = [];
113
- for (const cmp of cmps) {
114
- enableLogs && console.log("detected CMP:", cmp.name, window.location.href);
115
- this.sendContentMessage({
116
- type: 'cmpDetected',
117
- url: location.href,
118
- cmp: cmp.name,
119
- }); // notify the browser
120
- popupLookups.push(this.waitForPopup(cmp).then((isOpen) => {
121
- if (isOpen) {
122
- if (!this.foundCmp) {
123
- this.foundCmp = cmp;
124
- }
125
- this.sendContentMessage({
126
- type: 'popupFound',
127
- cmp: cmp.name,
128
- url: location.href,
129
- }); // notify the browser
130
- return true;
131
- } else {
132
- return Promise.reject(`${cmp.name} popup not found`);
133
- }
134
- }));
127
+ enableLogs && console.log(`Detecting CMPs on ${window.location.href}`);
128
+ this.updateState({ lifecycle: 'started' });
129
+ const foundCmps = await this.findCmp(this.config.detectRetries);
130
+ this.updateState({ detectedCmps: foundCmps.map(c => c.name) });
131
+ if (foundCmps.length === 0) {
132
+ enableLogs && console.log("no CMP found", location.href);
133
+ if (this.config.enablePrehide) {
134
+ this.undoPrehide();
135
135
  }
136
+ this.updateState({ lifecycle: 'nothingDetected' });
137
+ return false;
138
+ }
139
+ this.updateState({ lifecycle: 'cmpDetected' });
136
140
 
137
- // could use `somethingOpen = await Promise.any(popupLookups).catch(() => false)`, but Promise.any is often unavailable in polyfilled environments
138
- let somethingOpen = false;
139
- for (const popupLookup of popupLookups) {
140
- try {
141
- await popupLookup;
142
- somethingOpen = true;
143
- break;
144
- } catch (e) {
145
- continue;
146
- }
147
- }
141
+ // we resort to cosmetic rules only if no non-cosmetic rules are found
142
+ let foundPopups = await this.detectPopups(foundCmps.filter(r => !r.isCosmetic))
143
+ if (foundPopups.length === 0) {
144
+ foundPopups = await this.detectPopups(foundCmps.filter(r => r.isCosmetic))
145
+ }
148
146
 
149
- if (!somethingOpen) {
150
- enableLogs && console.log('no popup found');
151
- if (this.config.enablePrehide) {
152
- undoPrehide();
153
- }
154
- return false;
147
+ if (foundPopups.length === 0) {
148
+ enableLogs && console.log('no popup found');
149
+ if (this.config.enablePrehide) {
150
+ this.undoPrehide();
155
151
  }
152
+ return false;
153
+ }
154
+ this.updateState({ lifecycle: 'openPopupDetected' });
156
155
 
157
- if (this.config.autoAction === 'optOut') {
158
- return await this.doOptOut();
159
- } else if (this.config.autoAction === 'optIn') {
160
- return await this.doOptIn();
161
- }
156
+ if (foundPopups.length > 1) {
157
+ const errorDetails = {
158
+ msg: `Found multiple CMPs, check the detection rules.`,
159
+ cmps: foundPopups.map((cmp) => cmp.name),
160
+ };
161
+ enableLogs && console.warn(errorDetails.msg, errorDetails.cmps);
162
+ this.sendContentMessage({
163
+ type: 'autoconsentError',
164
+ details: errorDetails,
165
+ });
166
+ }
162
167
 
168
+ this.foundCmp = foundPopups[0];
169
+
170
+ if (this.config.autoAction === 'optOut') {
171
+ return await this.doOptOut();
172
+ } else if (this.config.autoAction === 'optIn') {
173
+ return await this.doOptIn();
174
+ } else {
163
175
  enableLogs && console.log("waiting for opt-out signal...", location.href);
164
176
  return true;
165
- } else {
166
- enableLogs && console.log("no CMP found", location.href);
167
- if (this.config.enablePrehide) {
168
- undoPrehide();
169
- }
170
- return false;
171
177
  }
172
178
  }
173
179
 
174
180
  async findCmp(retries: number): Promise<AutoCMP[]> {
175
- const allFoundCmps: AutoCMP[] = [];
181
+ this.updateState({ findCmpAttempts: this.state.findCmpAttempts + 1 })
182
+ const foundCMPs: AutoCMP[] = [];
176
183
 
177
184
  for (const cmp of this.rules) {
178
185
  try {
@@ -181,27 +188,20 @@ export default class AutoConsent {
181
188
  }
182
189
  const result = await cmp.detectCmp();
183
190
  if (result) {
184
- enableLogs && console.log(`Found CMP: ${cmp.name}`);
185
- allFoundCmps.push(cmp);
191
+ enableLogs && console.log(`Found CMP: ${cmp.name} ${window.location.href}`);
192
+ this.sendContentMessage({
193
+ type: 'cmpDetected',
194
+ url: location.href,
195
+ cmp: cmp.name,
196
+ }); // notify the browser
197
+ foundCMPs.push(cmp);
186
198
  }
187
199
  } catch (e) {
188
200
  enableLogs && console.warn(`error detecting ${cmp.name}`, e);
189
201
  }
190
202
  }
191
203
 
192
- if (allFoundCmps.length > 1) {
193
- const errorDetails = {
194
- msg: `Found multiple CMPs, check the detection rules.`,
195
- cmps: allFoundCmps.map((cmp) => cmp.name),
196
- };
197
- enableLogs && console.warn(errorDetails.msg, errorDetails.cmps);
198
- this.sendContentMessage({
199
- type: 'autoconsentError',
200
- details: errorDetails,
201
- });
202
- }
203
-
204
- if (allFoundCmps.length === 0 && retries > 0) {
204
+ if (foundCMPs.length === 0 && retries > 0) {
205
205
  return new Promise((resolve) => {
206
206
  setTimeout(async () => {
207
207
  const result = this.findCmp(retries - 1);
@@ -210,10 +210,28 @@ export default class AutoConsent {
210
210
  });
211
211
  }
212
212
 
213
- return allFoundCmps;
213
+ return foundCMPs;
214
+ }
215
+
216
+ async detectPopups(cmps: AutoCMP[]): Promise<AutoCMP[]> {
217
+ const result: AutoCMP[] = [];
218
+ const popupLookups = cmps.map((cmp) => this.waitForPopup(cmp).then((isOpen) => {
219
+ if (isOpen) {
220
+ this.updateState({ detectedPopups: this.state.detectedPopups.concat([cmp.name]) });
221
+ this.sendContentMessage({
222
+ type: 'popupFound',
223
+ cmp: cmp.name,
224
+ url: location.href,
225
+ }); // notify the browser
226
+ result.push(cmp);
227
+ }
228
+ }).catch(() => null));
229
+ await Promise.all(popupLookups);
230
+ return result;
214
231
  }
215
232
 
216
233
  async doOptOut(): Promise<boolean> {
234
+ this.updateState({ lifecycle: 'runningOptOut' })
217
235
  let optOutResult;
218
236
  if (!this.foundCmp) {
219
237
  enableLogs && console.log('no CMP to opt out');
@@ -225,7 +243,7 @@ export default class AutoConsent {
225
243
  }
226
244
 
227
245
  if (this.config.enablePrehide) {
228
- undoPrehide();
246
+ this.undoPrehide();
229
247
  }
230
248
 
231
249
  this.sendContentMessage({
@@ -240,14 +258,19 @@ export default class AutoConsent {
240
258
  this.sendContentMessage({
241
259
  type: 'autoconsentDone',
242
260
  cmp: this.foundCmp.name,
261
+ isCosmetic: this.foundCmp.isCosmetic,
243
262
  url: location.href,
244
263
  });
264
+ this.updateState({ lifecycle: 'done' })
265
+ } else {
266
+ this.updateState({ lifecycle: optOutResult ? 'optOutSucceeded' : 'optOutFailed' })
245
267
  }
246
268
 
247
269
  return optOutResult;
248
270
  }
249
271
 
250
272
  async doOptIn(): Promise<boolean> {
273
+ this.updateState({ lifecycle: 'runningOptIn' })
251
274
  let optInResult;
252
275
  if (!this.foundCmp) {
253
276
  enableLogs && console.log('no CMP to opt in');
@@ -259,7 +282,7 @@ export default class AutoConsent {
259
282
  }
260
283
 
261
284
  if (this.config.enablePrehide) {
262
- undoPrehide();
285
+ this.undoPrehide();
263
286
  }
264
287
 
265
288
  this.sendContentMessage({
@@ -274,8 +297,12 @@ export default class AutoConsent {
274
297
  this.sendContentMessage({
275
298
  type: 'autoconsentDone',
276
299
  cmp: this.foundCmp.name,
300
+ isCosmetic: this.foundCmp.isCosmetic,
277
301
  url: location.href,
278
302
  });
303
+ this.updateState({ lifecycle: 'done' })
304
+ } else {
305
+ this.updateState({ lifecycle: optInResult ? 'optInSucceeded' : 'optInFailed' })
279
306
  }
280
307
 
281
308
  return optInResult;
@@ -297,6 +324,7 @@ export default class AutoConsent {
297
324
  result: selfTestResult,
298
325
  url: location.href,
299
326
  });
327
+ this.updateState({ selfTest: selfTestResult })
300
328
  return selfTestResult;
301
329
  }
302
330
 
@@ -323,11 +351,28 @@ export default class AutoConsent {
323
351
  return selectorList;
324
352
  }, globalHidden);
325
353
 
354
+ this.updateState({ prehideOn: true })
326
355
  return prehide(selectors);
327
356
  }
328
357
 
358
+ undoPrehide(): boolean {
359
+ this.updateState({ prehideOn: false })
360
+ return undoPrehide();
361
+ }
362
+
363
+ updateState(change: Partial<ConsentState>) {
364
+ Object.assign(this.state, change)
365
+ this.sendContentMessage({
366
+ type: 'report',
367
+ instanceId: this.id,
368
+ url: window.location.href,
369
+ mainFrame: window.top === window.self,
370
+ state: this.state,
371
+ })
372
+ }
373
+
329
374
  async receiveMessageCallback(message: BackgroundMessage) {
330
- if (enableLogs && message.type !== 'evalResp' /* evals are noisy */) {
375
+ if (enableLogs && ['evalResp', 'report'].includes(message.type) /* evals are noisy */) {
331
376
  console.log('received from background', message, window.location.href);
332
377
  }
333
378
  switch (message.type) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duckduckgo/autoconsent",
3
- "version": "3.0.4",
3
+ "version": "4.1.0",
4
4
  "description": "",
5
5
  "main": "dist/autoconsent.cjs.js",
6
6
  "module": "dist/autoconsent.esm.js",
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "clean": "rm -r dist",
12
+ "lint": "eslint lib/ playwright/ tests/ rules/autoconsent/*.json",
12
13
  "bundle": "rollup -c",
13
14
  "watch": "rollup -c -w",
14
15
  "test": "playwright test",
@@ -28,16 +29,18 @@
28
29
  "license": "MPL-2.0",
29
30
  "devDependencies": {
30
31
  "@playwright/test": "^1.17.1",
31
- "@rollup/plugin-json": "^4.1.0",
32
- "@rollup/plugin-typescript": "^8.3.2",
32
+ "@rollup/plugin-json": "^6.0.0",
33
+ "@rollup/plugin-typescript": "^10.0.1",
33
34
  "@types/chai": "^4.3.1",
34
- "@types/chrome": "^0.0.188",
35
- "@types/mocha": "^9.1.1",
36
- "@typescript-eslint/eslint-plugin": "^5.25.0",
37
- "@typescript-eslint/parser": "^5.25.0",
35
+ "@types/chrome": "^0.0.206",
36
+ "@types/mocha": "^10.0.1",
37
+ "@typescript-eslint/eslint-plugin": "^5.42.1",
38
+ "@typescript-eslint/parser": "^5.42.1",
38
39
  "auto": "^10.37.6",
40
+ "bulma": "^0.9.4",
39
41
  "chai": "^4.2.0",
40
- "eslint-config-airbnb": "^19.0.4",
42
+ "eslint": "^8.27.0",
43
+ "eslint-plugin-json": "^3.1.0",
41
44
  "mocha": "^10.0.0",
42
45
  "rollup": "^2.79.1",
43
46
  "rollup-plugin-copy": "^3.4.0",
@@ -88,6 +88,7 @@ export function generateTest(
88
88
  disabledCmps: [],
89
89
  enablePrehide: true,
90
90
  detectRetries: 20,
91
+ enableCosmeticRules: true,
91
92
  })} })`);
92
93
  break;
93
94
  }
package/rollup.config.js CHANGED
@@ -40,15 +40,17 @@ export default [{
40
40
  }],
41
41
  plugins: [
42
42
  typescript(),
43
- terser(),
44
43
  copy({
45
44
  targets: [
46
45
  {
47
- src: ['./addon/icons', './rules/rules.json'],
46
+ src: [
47
+ './addon/icons',
48
+ './rules/rules.json'
49
+ ],
48
50
  dest: ['./dist/addon-firefox/', './dist/addon-mv3/']
49
51
  },
50
52
  {
51
- src: ['./addon/popup.html'],
53
+ src: ['./addon/popup.html', './addon/devtools'],
52
54
  dest: ['./dist/addon-mv3/']
53
55
  },
54
56
  {
@@ -61,6 +63,10 @@ export default [{
61
63
  dest: './dist/addon-firefox',
62
64
  rename: 'manifest.json',
63
65
  },
66
+ {
67
+ src: './node_modules/bulma/css/bulma.min.css',
68
+ dest: './dist/addon-mv3/devtools/'
69
+ }
64
70
  ]
65
71
  })
66
72
  ]
@@ -75,7 +81,6 @@ export default [{
75
81
  }],
76
82
  plugins: [
77
83
  typescript(),
78
- terser(),
79
84
  ],
80
85
  }, {
81
86
  input: './addon/popup.ts',
@@ -85,6 +90,14 @@ export default [{
85
90
  }],
86
91
  plugins: [
87
92
  typescript(),
88
- terser(),
93
+ ],
94
+ }, {
95
+ input: './addon/devtools/panel.ts',
96
+ output: [{
97
+ file: './dist/addon-mv3/devtools/panel.js',
98
+ format: 'iife',
99
+ }],
100
+ plugins: [
101
+ typescript(),
89
102
  ],
90
103
  }];
@@ -2,10 +2,7 @@
2
2
  "name": "affinity.serif.com",
3
3
  "detectCmp": [
4
4
  {
5
- "exists": ".c-cookie-banner"
6
- },
7
- {
8
- "exists": ".c-cookie-banner button"
5
+ "exists": ".c-cookie-banner button[data-qa='allow-all-cookies']"
9
6
  }
10
7
  ],
11
8
  "detectPopup": [
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "ausopen.com",
3
- "isHidingRule": true,
3
+ "cosmetic": true,
4
4
  "detectCmp": [{ "exists": ".gdpr-popup__message" }],
5
5
  "detectPopup": [{ "visible": ".gdpr-popup__message" }],
6
6
  "optOut": [{ "hide": [".gdpr-popup__message"]}],
7
- "optIn": [{ "click": [".gdpr-popup__message button"]}]
7
+ "optIn": [{ "click": ".gdpr-popup__message button"}]
8
8
  }
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "baden-wuerttemberg.de",
3
3
  "prehideSelectors": [".cookie-alert.t-dark"],
4
+ "cosmetic": true,
4
5
  "detectCmp": [{ "exists": ".cookie-alert.t-dark" }],
5
6
  "detectPopup": [{ "visible": ".cookie-alert.t-dark" }],
6
7
  "optIn": [
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "cc_banner",
3
+ "cosmetic": true,
3
4
  "prehideSelectors": [".cc_banner-wrapper"],
4
5
  "detectCmp": [{ "exists": ".cc_banner-wrapper" }],
5
6
  "detectPopup": [{ "visible": ".cc_banner" }],
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "Complianz notice",
3
3
  "prehideSelectors": [".cc-type-info[aria-describedby=\"cookieconsent:desc\"]"],
4
+ "cosmetic": true,
4
5
  "detectCmp": [{ "exists": ".cc-type-info[aria-describedby=\"cookieconsent:desc\"]" }],
5
6
  "detectPopup": [{ "visible": ".cc-type-info[aria-describedby=\"cookieconsent:desc\"]" }],
6
7
  "optIn": [
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "cookie-notice",
3
3
  "prehideSelectors": ["#cookie-notice"],
4
+ "cosmetic": true,
4
5
  "detectCmp": [
5
6
  { "visible": "#cookie-notice .cookie-notice-container" }
6
7
  ],
7
8
  "detectPopup": [{ "visible": "#cookie-notice" }],
8
- "optIn": [{ "click": ["#cn-accept-cookie"] }],
9
+ "optIn": [{ "click": "#cn-accept-cookie" }],
9
10
  "optOut": [{ "hide": ["#cookie-notice"] }]
10
11
  }
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "cookiealert",
3
+ "intermediate": false,
4
+ "prehideSelectors": [],
5
+ "runContext": {
6
+ "frame": true,
7
+ "main": true
8
+ },
9
+ "detectCmp": [
10
+ {
11
+ "exists": ".cookie-alert-extended"
12
+ }
13
+ ],
14
+ "detectPopup": [
15
+ {
16
+ "visible": ".cookie-alert-extended-modal"
17
+ }
18
+ ],
19
+ "optIn": [
20
+ {
21
+ "click": "button[data-controller='cookie-alert/extended/button/accept']"
22
+ },
23
+ {
24
+ "eval": "document.querySelector('body').removeAttribute('style') || true"
25
+ }
26
+ ],
27
+ "optOut": [
28
+ {
29
+ "click": "a[data-controller='cookie-alert/extended/detail-link']"
30
+ },
31
+ {
32
+ "click": ".cookie-alert-configuration-input:checked",
33
+ "all": true,
34
+ "optional": true
35
+ },
36
+ {
37
+ "click": "button[data-controller='cookie-alert/extended/button/configuration']"
38
+ },
39
+ {
40
+ "eval": "document.querySelector('body').removeAttribute('style') || true"
41
+ }
42
+ ],
43
+ "test": [
44
+ {
45
+ "eval": "window.CookieConsent.declined === true"
46
+ }
47
+ ]
48
+ }
@@ -2,6 +2,7 @@
2
2
  "name": "WP DSGVO Tools",
3
3
  "link": "https://wordpress.org/plugins/shapepress-dsgvo/",
4
4
  "prehideSelectors": [".sp-dsgvo"],
5
+ "cosmetic": true,
5
6
  "detectCmp": [{ "exists": ".sp-dsgvo.sp-dsgvo-popup-overlay" }],
6
7
  "detectPopup": [{ "visible": ".sp-dsgvo.sp-dsgvo-popup-overlay", "check": "any" }],
7
8
  "optIn": [
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eu-cookie-compliance-banner",
3
3
  "detectCmp": [{ "exists": ".eu-cookie-compliance-banner-info" }],
4
- "detectPopup": [{ "visible": ".eu-cookie-compliance-banner-info" }],
4
+ "detectPopup": [{ "exists": ".eu-cookie-compliance-popup-open" }],
5
5
  "optIn": [{ "click": ".agree-button" }],
6
6
  "optOut": [
7
7
  { "click": ".decline-button,.eu-cookie-compliance-save-preferences-button", "optional": true },
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "EU Cookie Law",
3
3
  "prehideSelectors": [".pea_cook_wrapper,.pea_cook_more_info_popover"],
4
+ "cosmetic": true,
4
5
  "detectCmp": [{ "exists": ".pea_cook_wrapper" }],
5
6
  "detectPopup": [
6
7
  { "wait": 500 },
@@ -8,6 +8,7 @@
8
8
  { "eval": "ezCMP.handleAcceptAllClick()", "optional": true }
9
9
  ],
10
10
  "optOut": [
11
+ { "wait": 500 },
11
12
  { "click": "#ez-manage-settings" },
12
13
  { "waitFor": "#ez-cookie-dialog input[type=checkbox]" },
13
14
  { "click": "#ez-cookie-dialog input[type=checkbox][checked]", "all": true },
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "generic-cosmetic",
3
+ "cosmetic": true,
4
+ "prehideSelectors": ["#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"],
5
+ "detectCmp": [{ "exists": "#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner" }],
6
+ "detectPopup": [{ "visible": "#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner" }],
7
+ "optIn": [],
8
+ "optOut": [{ "hide": ["#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"] }]
9
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "indeed.com",
3
+ "cosmetic": true,
4
+ "prehideSelectors": ["#CookiePrivacyNotice"],
5
+ "detectCmp": [{ "exists": "#CookiePrivacyNotice" }],
6
+ "detectPopup": [{ "visible": "#CookiePrivacyNotice" }],
7
+ "optIn": [{ "click": "#CookiePrivacyNotice button[data-gnav-element-name=CookiePrivacyNoticeOk]" }],
8
+ "optOut": [{ "hide": ["#CookiePrivacyNotice"] }]
9
+ }
@@ -2,6 +2,7 @@
2
2
  "name": "jquery.cookieBar",
3
3
  "comment": "https://github.com/kovarp/jquery.cookieBar",
4
4
  "prehideSelectors": [".cookie-bar"],
5
+ "cosmetic": true,
5
6
  "detectCmp": [{ "exists": ".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons" }],
6
7
  "detectPopup": [{
7
8
  "visible": ".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "marksandspencer.com",
3
- "isHidingRule": true,
3
+ "cosmetic": true,
4
4
  "detectCmp": [{ "exists": ".navigation-cookiebbanner" }],
5
5
  "detectPopup": [{ "visible": ".navigation-cookiebbanner" }],
6
6
  "optOut": [{ "hide": [".navigation-cookiebbanner"]}],
7
- "optIn": [{ "click": [".navigation-cookiebbanner__submit"]}]
7
+ "optIn": [{ "click": ".navigation-cookiebbanner__submit"}]
8
8
  }
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "notice-cookie",
3
3
  "prehideSelectors": [".button--notice"],
4
+ "cosmetic": true,
4
5
  "detectCmp": [{ "exists": ".notice--cookie" }],
5
6
  "detectPopup": [{ "visible": ".notice--cookie" }],
6
7
  "optIn": [{ "click": ".button--notice" }],