darkreader 4.9.109 → 4.9.113

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.
package/README.md CHANGED
@@ -181,7 +181,7 @@ For Dark Reader, the option "Run on sites with restrictions" is not shown becaus
181
181
 
182
182
  The fact that it is a Recommended extension means that it meets the "highest standards of security, functionality, and user experience". The quarantined domains are only related to extension security. Because Dark Reader is considered secure by Mozilla, that option is not shown, meaning **it will always run even on quarantined domains** (but will still obey the "restricted domains" list if it is not empty).
183
183
 
184
- Regarding quarantined domains specifically, there is this [comment from Firefox's source code:](https://searchfox.org/mozilla-central/source/toolkit/components/extensions/Extension.sys.mjs#2937-2938)
184
+ Regarding quarantined domains specifically, there is this [comment from Firefox's source code](https://searchfox.org/mozilla-central/rev/1838f847aa3bf909c3d34a94a8f0cd7e37fca086/toolkit/components/extensions/Extension.sys.mjs#3470-3471):
185
185
 
186
186
  ```
187
187
  // Privileged extensions and any extensions with a recommendation state are
package/darkreader.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Dark Reader v4.9.109
2
+ * Dark Reader v4.9.113
3
3
  * https://darkreader.org/
4
4
  */
5
5
 
@@ -124,7 +124,7 @@
124
124
  const isMacOS = platform.startsWith("mac");
125
125
  isNavigatorDefined && navigator.userAgentData
126
126
  ? navigator.userAgentData.mobile
127
- : userAgent.includes("mobile");
127
+ : userAgent.includes("mobile") || false;
128
128
  const isShadowDomSupported = typeof ShadowRoot === "function";
129
129
  const isMatchMediaChangeEventListenerSupported =
130
130
  typeof MediaQueryList === "function" &&
@@ -4026,10 +4026,7 @@
4026
4026
  };
4027
4027
  const modified = modify();
4028
4028
  if (unknownVars.size > 0) {
4029
- const isFallbackResolved = modified.match(
4030
- /^var\(.*?, ((var\(--darkreader-bg--.*\))|(#[0-9A-Fa-f]+)|([a-z]+)|(rgba?\(.+\))|(hsla?\(.+\)))\)$/
4031
- );
4032
- if (isFallbackResolved) {
4029
+ if (isFallbackResolved(modified)) {
4033
4030
  return modified;
4034
4031
  }
4035
4032
  return new Promise((resolve) => {
@@ -4428,6 +4425,43 @@
4428
4425
  value.match(/^(((\d{1,3})|(var\([\-_A-Za-z0-9]+\))),?\s*?){3}$/)
4429
4426
  );
4430
4427
  }
4428
+ function isFallbackResolved(modified) {
4429
+ if (modified.startsWith("var(") && modified.endsWith(")")) {
4430
+ const hasNestedBrackets = modified.endsWith("))");
4431
+ const hasDoubleNestedBrackets = modified.endsWith(")))");
4432
+ const lastOpenBracketIndex = hasNestedBrackets
4433
+ ? modified.lastIndexOf("(")
4434
+ : -1;
4435
+ const firstOpenBracketIndex = hasDoubleNestedBrackets
4436
+ ? modified.lastIndexOf("(", lastOpenBracketIndex - 1)
4437
+ : lastOpenBracketIndex;
4438
+ const commaIndex = modified.lastIndexOf(
4439
+ ",",
4440
+ hasNestedBrackets ? firstOpenBracketIndex : modified.length
4441
+ );
4442
+ if (commaIndex < 0 || modified[commaIndex + 1] !== " ") {
4443
+ return false;
4444
+ }
4445
+ const fallback = modified.slice(
4446
+ commaIndex + 2,
4447
+ modified.length - 1
4448
+ );
4449
+ if (hasNestedBrackets) {
4450
+ return (
4451
+ fallback.startsWith("rgb(") ||
4452
+ fallback.startsWith("rgba(") ||
4453
+ fallback.startsWith("hsl(") ||
4454
+ fallback.startsWith("hsla(") ||
4455
+ fallback.startsWith("var(--darkreader-bg--") ||
4456
+ fallback.startsWith("var(--darkreader-background-") ||
4457
+ (hasDoubleNestedBrackets &&
4458
+ fallback.includes("var(--darkreader-background-"))
4459
+ );
4460
+ }
4461
+ return fallback.match(/^(#[0-9a-f]+)|([a-z]+)$/i);
4462
+ }
4463
+ return false;
4464
+ }
4431
4465
  const textColorProps = [
4432
4466
  "color",
4433
4467
  "caret-color",
@@ -4520,6 +4554,9 @@
4520
4554
  if (isMediaRule(rule.parentRule)) {
4521
4555
  cssText = `${rule.parentRule.media.mediaText} { ${cssText} }`;
4522
4556
  }
4557
+ if (isLayerRule(rule.parentRule)) {
4558
+ cssText = `${rule.parentRule.name} { ${cssText} }`;
4559
+ }
4523
4560
  return getHashCode(cssText);
4524
4561
  }
4525
4562
  const rulesTextCache = new Set();
@@ -5131,14 +5168,38 @@
5131
5168
  return {render, destroy, commands};
5132
5169
  }
5133
5170
 
5134
- const hostsBreakingOnStylePosition = ["www.diffusioneshop.com", "zhale.me"];
5171
+ const hostsBreakingOnStylePosition = [
5172
+ "www.berlingske.dk",
5173
+ "www.bloomberg.com",
5174
+ "www.diffusioneshop.com",
5175
+ "www.weekendavisen.dk",
5176
+ "zhale.me"
5177
+ ];
5135
5178
  const mode = hostsBreakingOnStylePosition.includes(location.hostname)
5136
5179
  ? "away"
5137
5180
  : "next";
5138
5181
  function getStyleInjectionMode() {
5139
5182
  return mode;
5140
5183
  }
5184
+ const stylesWaitingForBody = new Set();
5185
+ let bodyObserver;
5141
5186
  function injectStyleAway(styleElement) {
5187
+ if (!document.body) {
5188
+ stylesWaitingForBody.add(styleElement);
5189
+ if (!bodyObserver) {
5190
+ bodyObserver = new MutationObserver(() => {
5191
+ if (document.body) {
5192
+ bodyObserver.disconnect();
5193
+ bodyObserver = null;
5194
+ stylesWaitingForBody.forEach((el) =>
5195
+ injectStyleAway(el)
5196
+ );
5197
+ stylesWaitingForBody.clear();
5198
+ }
5199
+ });
5200
+ }
5201
+ return;
5202
+ }
5142
5203
  let container = document.body.querySelector(
5143
5204
  ".darkreader-style-container"
5144
5205
  );
@@ -5148,9 +5209,32 @@
5148
5209
  container.classList.add("darkreader-style-container");
5149
5210
  container.style.display = "none";
5150
5211
  document.body.append(container);
5212
+ containerObserver = new MutationObserver(() => {
5213
+ if (container?.nextElementSibling != null) {
5214
+ container
5215
+ .querySelectorAll(".darkreader--sync")
5216
+ .forEach((el) => {
5217
+ if (el.sheet.cssRules.length > 0) {
5218
+ let cssText = "";
5219
+ for (const rule of el.sheet.cssRules) {
5220
+ cssText += rule.cssText;
5221
+ }
5222
+ el.textContent = cssText;
5223
+ }
5224
+ });
5225
+ document.body.append(container);
5226
+ }
5227
+ });
5228
+ containerObserver.observe(document.body, {childList: true});
5151
5229
  }
5152
5230
  container.append(styleElement);
5153
5231
  }
5232
+ let containerObserver;
5233
+ function removeStyleContainer() {
5234
+ bodyObserver?.disconnect();
5235
+ containerObserver?.disconnect();
5236
+ document.querySelector(".darkreader-style-container")?.remove();
5237
+ }
5154
5238
 
5155
5239
  const overrides = {
5156
5240
  "background-color": {
@@ -6292,7 +6376,8 @@
6292
6376
  force,
6293
6377
  isAsyncCancelled
6294
6378
  });
6295
- isOverrideEmpty = syncStyle.sheet.cssRules.length === 0;
6379
+ isOverrideEmpty =
6380
+ !syncStyle.sheet || syncStyle.sheet.cssRules.length === 0;
6296
6381
  if (sheetModifier.shouldRebuildStyle()) {
6297
6382
  addReadyStateCompleteListener(() => update());
6298
6383
  }
@@ -8084,6 +8169,7 @@
8084
8169
  loadingStyles.clear();
8085
8170
  cleanLoadingLinks();
8086
8171
  forEach(document.querySelectorAll(".darkreader"), removeNode);
8172
+ removeStyleContainer();
8087
8173
  adoptedStyleManagers.forEach((manager) => manager.destroy());
8088
8174
  adoptedStyleManagers.splice(0);
8089
8175
  adoptedStyleFallbacks.forEach((fallback) => fallback.destroy());
package/darkreader.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Dark Reader v4.9.109
2
+ * Dark Reader v4.9.113
3
3
  * https://darkreader.org/
4
4
  */
5
5
 
@@ -107,7 +107,7 @@ const isWindows = platform.startsWith("win");
107
107
  const isMacOS = platform.startsWith("mac");
108
108
  isNavigatorDefined && navigator.userAgentData
109
109
  ? navigator.userAgentData.mobile
110
- : userAgent.includes("mobile");
110
+ : userAgent.includes("mobile") || false;
111
111
  const isShadowDomSupported = typeof ShadowRoot === "function";
112
112
  const isMatchMediaChangeEventListenerSupported =
113
113
  typeof MediaQueryList === "function" &&
@@ -3896,10 +3896,7 @@ class VariablesStore {
3896
3896
  };
3897
3897
  const modified = modify();
3898
3898
  if (unknownVars.size > 0) {
3899
- const isFallbackResolved = modified.match(
3900
- /^var\(.*?, ((var\(--darkreader-bg--.*\))|(#[0-9A-Fa-f]+)|([a-z]+)|(rgba?\(.+\))|(hsla?\(.+\)))\)$/
3901
- );
3902
- if (isFallbackResolved) {
3899
+ if (isFallbackResolved(modified)) {
3903
3900
  return modified;
3904
3901
  }
3905
3902
  return new Promise((resolve) => {
@@ -4290,6 +4287,40 @@ function isConstructedColorVar(value) {
4290
4287
  value.match(/^(((\d{1,3})|(var\([\-_A-Za-z0-9]+\))),?\s*?){3}$/)
4291
4288
  );
4292
4289
  }
4290
+ function isFallbackResolved(modified) {
4291
+ if (modified.startsWith("var(") && modified.endsWith(")")) {
4292
+ const hasNestedBrackets = modified.endsWith("))");
4293
+ const hasDoubleNestedBrackets = modified.endsWith(")))");
4294
+ const lastOpenBracketIndex = hasNestedBrackets
4295
+ ? modified.lastIndexOf("(")
4296
+ : -1;
4297
+ const firstOpenBracketIndex = hasDoubleNestedBrackets
4298
+ ? modified.lastIndexOf("(", lastOpenBracketIndex - 1)
4299
+ : lastOpenBracketIndex;
4300
+ const commaIndex = modified.lastIndexOf(
4301
+ ",",
4302
+ hasNestedBrackets ? firstOpenBracketIndex : modified.length
4303
+ );
4304
+ if (commaIndex < 0 || modified[commaIndex + 1] !== " ") {
4305
+ return false;
4306
+ }
4307
+ const fallback = modified.slice(commaIndex + 2, modified.length - 1);
4308
+ if (hasNestedBrackets) {
4309
+ return (
4310
+ fallback.startsWith("rgb(") ||
4311
+ fallback.startsWith("rgba(") ||
4312
+ fallback.startsWith("hsl(") ||
4313
+ fallback.startsWith("hsla(") ||
4314
+ fallback.startsWith("var(--darkreader-bg--") ||
4315
+ fallback.startsWith("var(--darkreader-background-") ||
4316
+ (hasDoubleNestedBrackets &&
4317
+ fallback.includes("var(--darkreader-background-"))
4318
+ );
4319
+ }
4320
+ return fallback.match(/^(#[0-9a-f]+)|([a-z]+)$/i);
4321
+ }
4322
+ return false;
4323
+ }
4293
4324
  const textColorProps = [
4294
4325
  "color",
4295
4326
  "caret-color",
@@ -4382,6 +4413,9 @@ function createStyleSheetModifier() {
4382
4413
  if (isMediaRule(rule.parentRule)) {
4383
4414
  cssText = `${rule.parentRule.media.mediaText} { ${cssText} }`;
4384
4415
  }
4416
+ if (isLayerRule(rule.parentRule)) {
4417
+ cssText = `${rule.parentRule.name} { ${cssText} }`;
4418
+ }
4385
4419
  return getHashCode(cssText);
4386
4420
  }
4387
4421
  const rulesTextCache = new Set();
@@ -4983,14 +5017,36 @@ function createAdoptedStyleSheetFallback() {
4983
5017
  return {render, destroy, commands};
4984
5018
  }
4985
5019
 
4986
- const hostsBreakingOnStylePosition = ["www.diffusioneshop.com", "zhale.me"];
5020
+ const hostsBreakingOnStylePosition = [
5021
+ "www.berlingske.dk",
5022
+ "www.bloomberg.com",
5023
+ "www.diffusioneshop.com",
5024
+ "www.weekendavisen.dk",
5025
+ "zhale.me"
5026
+ ];
4987
5027
  const mode = hostsBreakingOnStylePosition.includes(location.hostname)
4988
5028
  ? "away"
4989
5029
  : "next";
4990
5030
  function getStyleInjectionMode() {
4991
5031
  return mode;
4992
5032
  }
5033
+ const stylesWaitingForBody = new Set();
5034
+ let bodyObserver;
4993
5035
  function injectStyleAway(styleElement) {
5036
+ if (!document.body) {
5037
+ stylesWaitingForBody.add(styleElement);
5038
+ if (!bodyObserver) {
5039
+ bodyObserver = new MutationObserver(() => {
5040
+ if (document.body) {
5041
+ bodyObserver.disconnect();
5042
+ bodyObserver = null;
5043
+ stylesWaitingForBody.forEach((el) => injectStyleAway(el));
5044
+ stylesWaitingForBody.clear();
5045
+ }
5046
+ });
5047
+ }
5048
+ return;
5049
+ }
4994
5050
  let container = document.body.querySelector(".darkreader-style-container");
4995
5051
  if (!container) {
4996
5052
  container = document.createElement("div");
@@ -4998,9 +5054,32 @@ function injectStyleAway(styleElement) {
4998
5054
  container.classList.add("darkreader-style-container");
4999
5055
  container.style.display = "none";
5000
5056
  document.body.append(container);
5057
+ containerObserver = new MutationObserver(() => {
5058
+ if (container?.nextElementSibling != null) {
5059
+ container
5060
+ .querySelectorAll(".darkreader--sync")
5061
+ .forEach((el) => {
5062
+ if (el.sheet.cssRules.length > 0) {
5063
+ let cssText = "";
5064
+ for (const rule of el.sheet.cssRules) {
5065
+ cssText += rule.cssText;
5066
+ }
5067
+ el.textContent = cssText;
5068
+ }
5069
+ });
5070
+ document.body.append(container);
5071
+ }
5072
+ });
5073
+ containerObserver.observe(document.body, {childList: true});
5001
5074
  }
5002
5075
  container.append(styleElement);
5003
5076
  }
5077
+ let containerObserver;
5078
+ function removeStyleContainer() {
5079
+ bodyObserver?.disconnect();
5080
+ containerObserver?.disconnect();
5081
+ document.querySelector(".darkreader-style-container")?.remove();
5082
+ }
5004
5083
 
5005
5084
  const overrides = {
5006
5085
  "background-color": {
@@ -6096,7 +6175,8 @@ function manageStyle(element, {update, loadingStart, loadingEnd}) {
6096
6175
  force,
6097
6176
  isAsyncCancelled
6098
6177
  });
6099
- isOverrideEmpty = syncStyle.sheet.cssRules.length === 0;
6178
+ isOverrideEmpty =
6179
+ !syncStyle.sheet || syncStyle.sheet.cssRules.length === 0;
6100
6180
  if (sheetModifier.shouldRebuildStyle()) {
6101
6181
  addReadyStateCompleteListener(() => update());
6102
6182
  }
@@ -7830,6 +7910,7 @@ function removeDynamicTheme() {
7830
7910
  loadingStyles.clear();
7831
7911
  cleanLoadingLinks();
7832
7912
  forEach(document.querySelectorAll(".darkreader"), removeNode);
7913
+ removeStyleContainer();
7833
7914
  adoptedStyleManagers.forEach((manager) => manager.destroy());
7834
7915
  adoptedStyleManagers.splice(0);
7835
7916
  adoptedStyleFallbacks.forEach((fallback) => fallback.destroy());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "darkreader",
3
- "version": "4.9.109",
3
+ "version": "4.9.113",
4
4
  "description": "Dark mode for every website",
5
5
  "scripts": {
6
6
  "api": "node --max-old-space-size=3072 tasks/cli.js build --api",
@@ -26,6 +26,7 @@
26
26
  "test:chrome-mv3": "node tasks/cli.js build --debug --test --chrome-mv3 && jest --config=tests/browser/jest.config.chrome-mv3.mjs --runInBand",
27
27
  "test:ci": "npm run test:unit",
28
28
  "test:coverage": "jest --config=tests/unit/jest.config.mjs --coverage",
29
+ "test:edge": "node tasks/cli.js build --debug --test --plus && jest --config=tests/browser/jest.config.mjs --runInBand",
29
30
  "test:firefox": "node tasks/cli.js build --debug --test --firefox-mv2 && jest --config=tests/browser/jest.config.firefox.mjs --runInBand",
30
31
  "test:inject": "node --max-old-space-size=3072 node_modules/.bin/karma start ./tests/inject/karma.conf.cjs",
31
32
  "test:inject:debug": "node --max-old-space-size=3072 node_modules/.bin/karma start ./tests/inject/karma.conf.cjs --debug",
@@ -65,28 +66,28 @@
65
66
  "malevic": "0.20.2"
66
67
  },
67
68
  "devDependencies": {
68
- "@eslint/compat": "1.3.0",
69
+ "@eslint/compat": "1.4.0",
69
70
  "@eslint/eslintrc": "3.3.1",
70
- "@eslint/js": "9.29.0",
71
- "@rollup/plugin-node-resolve": "16.0.1",
71
+ "@eslint/js": "9.38.0",
72
+ "@rollup/plugin-node-resolve": "16.0.3",
72
73
  "@rollup/plugin-replace": "6.0.2",
73
- "@rollup/plugin-typescript": "12.1.3",
74
- "@stylistic/eslint-plugin": "5.0.0",
75
- "@types/chrome": "0.0.326",
74
+ "@rollup/plugin-typescript": "12.3.0",
75
+ "@stylistic/eslint-plugin": "5.5.0",
76
+ "@types/chrome": "0.1.26",
76
77
  "@types/eslint": "9.6.1",
77
- "@types/jasmine": "5.1.8",
78
+ "@types/jasmine": "5.1.12",
78
79
  "@types/jest": "30.0.0",
79
80
  "@types/karma": "6.3.9",
80
81
  "@types/karma-coverage": "2.0.3",
81
- "@types/node": "24.0.3",
82
+ "@types/node": "24.9.1",
82
83
  "@types/ws": "8.18.1",
83
84
  "chokidar": "4.0.3",
84
85
  "eslint-plugin-compat": "6.0.2",
85
86
  "eslint-plugin-import": "2.32.0",
86
- "globals": "16.2.0",
87
- "globby": "14.1.0",
88
- "jasmine-core": "5.8.0",
89
- "jest": "30.0.2",
87
+ "globals": "16.4.0",
88
+ "globby": "15.0.0",
89
+ "jasmine-core": "5.12.0",
90
+ "jest": "30.2.0",
90
91
  "jest-extended": "6.0.0",
91
92
  "karma": "6.4.4",
92
93
  "karma-chrome-launcher": "3.2.0",
@@ -96,20 +97,20 @@
96
97
  "karma-rollup-preprocessor": "7.0.8",
97
98
  "karma-safari-launcher": "1.0.0",
98
99
  "karma-spec-reporter": "0.0.36",
99
- "less": "4.3.0",
100
- "prettier": "3.6.0",
101
- "puppeteer-core": "24.10.2",
102
- "rollup": "4.44.0",
100
+ "less": "4.4.2",
101
+ "prettier": "3.6.2",
102
+ "puppeteer-core": "24.26.1",
103
+ "rollup": "4.52.5",
103
104
  "rollup-plugin-istanbul": "5.0.0",
104
- "ts-jest": "29.4.0",
105
+ "ts-jest": "29.4.5",
105
106
  "tslib": "2.8.1",
106
- "typescript": "5.8.3",
107
- "typescript-eslint": "8.35.0",
108
- "ws": "8.18.2",
107
+ "typescript": "5.9.3",
108
+ "typescript-eslint": "8.46.2",
109
+ "ws": "8.18.3",
109
110
  "yazl": "3.3.1"
110
111
  },
111
112
  "optionalDependencies": {
112
- "@rollup/rollup-linux-x64-gnu": "4.44.0",
113
- "@rollup/rollup-win32-x64-msvc": "4.44.0"
113
+ "@rollup/rollup-linux-x64-gnu": "4.52.5",
114
+ "@rollup/rollup-win32-x64-msvc": "4.52.5"
114
115
  }
115
116
  }