darkreader 4.9.113 → 4.9.117

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 (3) hide show
  1. package/darkreader.js +162 -95
  2. package/darkreader.mjs +152 -92
  3. package/package.json +17 -17
package/darkreader.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Dark Reader v4.9.113
2
+ * Dark Reader v4.9.117
3
3
  * https://darkreader.org/
4
4
  */
5
5
 
@@ -1870,10 +1870,12 @@
1870
1870
  m.startsWith("all") ||
1871
1871
  m.startsWith("(")
1872
1872
  );
1873
- const isPrintOrSpeech = media.some(
1874
- (m) => m.startsWith("print") || m.startsWith("speech")
1875
- );
1876
- if (isScreenOrAllOrQuery || !isPrintOrSpeech) {
1873
+ const isNotScreen =
1874
+ !isScreenOrAllOrQuery &&
1875
+ media.some((m) =>
1876
+ ignoredMedia.some((i) => m.startsWith(i))
1877
+ );
1878
+ if (isScreenOrAllOrQuery || !isNotScreen) {
1877
1879
  iterateCSSRules(rule.cssRules, iterate, onImportError);
1878
1880
  }
1879
1881
  } else if (isSupportsRule(rule)) {
@@ -1887,6 +1889,17 @@
1887
1889
  }
1888
1890
  });
1889
1891
  }
1892
+ const ignoredMedia = [
1893
+ "aural",
1894
+ "braille",
1895
+ "embossed",
1896
+ "handheld",
1897
+ "print",
1898
+ "projection",
1899
+ "speech",
1900
+ "tty",
1901
+ "tv"
1902
+ ];
1890
1903
  const shorthandVarDependantProperties = [
1891
1904
  "background",
1892
1905
  "border",
@@ -1932,7 +1945,8 @@
1932
1945
  }
1933
1946
  }
1934
1947
  if (
1935
- cssText.includes("background-color: ;") &&
1948
+ (cssText.includes("background-color: ;") ||
1949
+ cssText.includes("background-image: ;")) &&
1936
1950
  !style.getPropertyValue("background")
1937
1951
  ) {
1938
1952
  handleEmptyShorthand("background", style, iterate);
@@ -1965,6 +1979,7 @@
1965
1979
  }
1966
1980
  } else if (shorthand === "background") {
1967
1981
  iterate("background-color", "#ffffff");
1982
+ iterate("background-image", "none");
1968
1983
  }
1969
1984
  }
1970
1985
  }
@@ -2641,6 +2656,13 @@
2641
2656
  if (window.DarkReader?.Plugins?.fetch) {
2642
2657
  return window.DarkReader.Plugins.fetch(request);
2643
2658
  }
2659
+ const parsedURL = new URL(request.url);
2660
+ if (
2661
+ parsedURL.origin !== request.origin &&
2662
+ shouldIgnoreCors(parsedURL)
2663
+ ) {
2664
+ throw new Error("Cross-origin limit reached");
2665
+ }
2644
2666
  return new Promise((resolve, reject) => {
2645
2667
  const id = generateUID();
2646
2668
  resolvers$1.set(id, resolve);
@@ -2668,6 +2690,25 @@
2668
2690
  }
2669
2691
  }
2670
2692
  });
2693
+ const ipV4RegExp = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
2694
+ const MAX_CORS_DOMAINS = 16;
2695
+ const corsDomains = new Set();
2696
+ function shouldIgnoreCors(url) {
2697
+ const host = url.hostname;
2698
+ if (!corsDomains.has(host)) {
2699
+ corsDomains.add(host);
2700
+ }
2701
+ if (
2702
+ corsDomains.size >= MAX_CORS_DOMAINS ||
2703
+ host === "localhost" ||
2704
+ host.startsWith("[") ||
2705
+ host.endsWith(".local") ||
2706
+ host.match(ipV4RegExp)
2707
+ ) {
2708
+ return true;
2709
+ }
2710
+ return false;
2711
+ }
2671
2712
 
2672
2713
  const imageManager = new AsyncQueue();
2673
2714
  async function getImageDetails(url) {
@@ -2707,7 +2748,11 @@
2707
2748
  if (parsedURL.origin === location.origin) {
2708
2749
  return await loadAsDataURL(url);
2709
2750
  }
2710
- return await bgFetch({url, responseType: "data-url"});
2751
+ return await bgFetch({
2752
+ url,
2753
+ responseType: "data-url",
2754
+ origin: location.origin
2755
+ });
2711
2756
  }
2712
2757
  async function tryCreateImageBitmap(blob) {
2713
2758
  try {
@@ -3462,7 +3507,15 @@
3462
3507
  awaitingForImageLoading.set(url, []);
3463
3508
  imageDetails = await getImageDetails(url);
3464
3509
  imageDetailsCache.set(url, imageDetails);
3465
- writeImageDetailsCache(url, imageDetails);
3510
+ if (!url.startsWith("data:")) {
3511
+ const parsedURL = new URL(url);
3512
+ if (parsedURL.origin === location.origin) {
3513
+ writeImageDetailsCache(
3514
+ url,
3515
+ imageDetails
3516
+ );
3517
+ }
3518
+ }
3466
3519
  awaitingForImageLoading
3467
3520
  .get(url)
3468
3521
  .forEach((resolve) =>
@@ -3706,6 +3759,9 @@
3706
3759
  const VAR_TYPE_TEXT_COLOR = 1 << 1;
3707
3760
  const VAR_TYPE_BORDER_COLOR = 1 << 2;
3708
3761
  const VAR_TYPE_BG_IMG = 1 << 3;
3762
+ const shouldSetDefaultColor =
3763
+ !location.hostname.startsWith("www.ebay.") &&
3764
+ !location.hostname.includes(".ebay.");
3709
3765
  class VariablesStore {
3710
3766
  constructor() {
3711
3767
  this.varTypes = new Map();
@@ -3963,10 +4019,12 @@
3963
4019
  (isSimpleConstructedColor && property === "background")
3964
4020
  ) {
3965
4021
  return (theme) => {
3966
- const defaultFallback = tryModifyBgColor(
3967
- isConstructedColor ? "255, 255, 255" : "#ffffff",
3968
- theme
3969
- );
4022
+ const defaultFallback = shouldSetDefaultColor
4023
+ ? tryModifyBgColor(
4024
+ isConstructedColor ? "255, 255, 255" : "#ffffff",
4025
+ theme
4026
+ )
4027
+ : "transparent";
3970
4028
  return replaceCSSVariablesNames(
3971
4029
  sourceValue,
3972
4030
  (v) => wrapBgColorVariableName(v),
@@ -4662,6 +4720,12 @@
4662
4720
  if (emptyIsWhereSelector || viewTransitionSelector) {
4663
4721
  selectorText = ".darkreader-unsupported-selector";
4664
4722
  }
4723
+ if (isChromium && selectorText.endsWith("::picker")) {
4724
+ selectorText = selectorText.replaceAll(
4725
+ "::picker",
4726
+ "::picker(select)"
4727
+ );
4728
+ }
4665
4729
  let ruleText = `${selectorText} {`;
4666
4730
  for (const dec of declarations) {
4667
4731
  const {property, value, important} = dec;
@@ -5169,6 +5233,8 @@
5169
5233
  }
5170
5234
 
5171
5235
  const hostsBreakingOnStylePosition = [
5236
+ "gogoprivate.com",
5237
+ "gprivate.com",
5172
5238
  "www.berlingske.dk",
5173
5239
  "www.bloomberg.com",
5174
5240
  "www.diffusioneshop.com",
@@ -5537,12 +5603,49 @@
5537
5603
  }
5538
5604
  return false;
5539
5605
  }
5606
+ const LOOP_DETECTION_THRESHOLD = 1000;
5607
+ const MAX_LOOP_CYCLES = 10;
5608
+ const elementsLastChanges = new WeakMap();
5609
+ const elementsLoopCycles = new WeakMap();
5610
+ const SMALL_SVG_THRESHOLD = 32;
5611
+ const svgNodesRoots = new WeakMap();
5612
+ const svgRootSizeTestResults = new WeakMap();
5613
+ function getSVGElementRoot(svgElement) {
5614
+ if (!svgElement) {
5615
+ return null;
5616
+ }
5617
+ if (svgNodesRoots.has(svgElement)) {
5618
+ return svgNodesRoots.get(svgElement);
5619
+ }
5620
+ if (svgElement instanceof SVGSVGElement) {
5621
+ return svgElement;
5622
+ }
5623
+ const parent = svgElement.parentNode;
5624
+ const root = getSVGElementRoot(parent);
5625
+ svgNodesRoots.set(svgElement, root);
5626
+ return root;
5627
+ }
5540
5628
  function overrideInlineStyle(
5541
5629
  element,
5542
5630
  theme,
5543
5631
  ignoreInlineSelectors,
5544
5632
  ignoreImageSelectors
5545
5633
  ) {
5634
+ if (elementsLastChanges.has(element)) {
5635
+ if (
5636
+ Date.now() - elementsLastChanges.get(element) <
5637
+ LOOP_DETECTION_THRESHOLD
5638
+ ) {
5639
+ const cycles = elementsLoopCycles.get(element) ?? 0;
5640
+ elementsLoopCycles.set(element, cycles + 1);
5641
+ }
5642
+ if ((elementsLoopCycles.get(element) ?? 0) >= MAX_LOOP_CYCLES) {
5643
+ return;
5644
+ }
5645
+ }
5646
+ if (element.parentElement?.dataset.nodeViewContent) {
5647
+ return;
5648
+ }
5546
5649
  const cacheKey = getInlineStyleCacheKey(element, theme);
5547
5650
  if (cacheKey === inlineStyleCache.get(element)) {
5548
5651
  return;
@@ -5706,16 +5809,34 @@
5706
5809
  }
5707
5810
  if (isSVGElement) {
5708
5811
  if (element.hasAttribute("fill")) {
5709
- const SMALL_SVG_LIMIT = 32;
5710
5812
  const value = element.getAttribute("fill");
5711
5813
  if (value !== "none") {
5712
5814
  if (!(element instanceof SVGTextElement)) {
5713
5815
  const handleSVGElement = () => {
5714
- const {width, height} =
5715
- element.getBoundingClientRect();
5716
- const isBg =
5717
- width > SMALL_SVG_LIMIT ||
5718
- height > SMALL_SVG_LIMIT;
5816
+ let isSVGSmall = false;
5817
+ const root = getSVGElementRoot(element);
5818
+ if (!root) {
5819
+ return;
5820
+ }
5821
+ if (svgRootSizeTestResults.has(root)) {
5822
+ isSVGSmall = svgRootSizeTestResults.get(root);
5823
+ } else {
5824
+ const svgBounds = root.getBoundingClientRect();
5825
+ isSVGSmall =
5826
+ svgBounds.width * svgBounds.height <=
5827
+ Math.pow(SMALL_SVG_THRESHOLD, 2);
5828
+ svgRootSizeTestResults.set(root, isSVGSmall);
5829
+ }
5830
+ let isBg;
5831
+ if (isSVGSmall) {
5832
+ isBg = false;
5833
+ } else {
5834
+ const {width, height} =
5835
+ element.getBoundingClientRect();
5836
+ isBg =
5837
+ width > SMALL_SVG_THRESHOLD ||
5838
+ height > SMALL_SVG_THRESHOLD;
5839
+ }
5719
5840
  setCustomProp(
5720
5841
  "fill",
5721
5842
  isBg ? "background-color" : "color",
@@ -5807,6 +5928,7 @@
5807
5928
  element.removeAttribute(overrides[cssProp].dataAttr);
5808
5929
  });
5809
5930
  inlineStyleCache.set(element, getInlineStyleCacheKey(element, theme));
5931
+ elementsLastChanges.set(element, Date.now());
5810
5932
  }
5811
5933
 
5812
5934
  const metaThemeColorName = "theme-color";
@@ -6005,7 +6127,7 @@
6005
6127
  : true) &&
6006
6128
  !isFontsGoogleApiStyle(element))) &&
6007
6129
  !element.classList.contains("darkreader") &&
6008
- element.media.toLowerCase() !== "print" &&
6130
+ !ignoredMedia.includes(element.media.toLowerCase()) &&
6009
6131
  !element.classList.contains("stylus")
6010
6132
  );
6011
6133
  }
@@ -6029,7 +6151,8 @@
6029
6151
  return results;
6030
6152
  }
6031
6153
  const syncStyleSet = new WeakSet();
6032
- const corsStyleSet = new WeakSet();
6154
+ const corsCopies = new WeakMap();
6155
+ const corsCopiesTextLengths = new WeakMap();
6033
6156
  let loadingLinkCounter = 0;
6034
6157
  const rejectorsForLoadingLinks = new Map();
6035
6158
  function cleanLoadingLinks() {
@@ -6037,7 +6160,6 @@
6037
6160
  }
6038
6161
  function manageStyle(element, {update, loadingStart, loadingEnd}) {
6039
6162
  const inMode = getStyleInjectionMode();
6040
- let corsCopy = null;
6041
6163
  let syncStyle = null;
6042
6164
  if (inMode === "next") {
6043
6165
  const prevStyles = [];
@@ -6048,18 +6170,12 @@
6048
6170
  ) {
6049
6171
  prevStyles.push(next);
6050
6172
  }
6051
- corsCopy =
6052
- prevStyles.find(
6053
- (el) =>
6054
- el.matches(".darkreader--cors") && !corsStyleSet.has(el)
6055
- ) || null;
6056
6173
  syncStyle =
6057
6174
  prevStyles.find(
6058
6175
  (el) =>
6059
6176
  el.matches(".darkreader--sync") && !syncStyleSet.has(el)
6060
6177
  ) || null;
6061
6178
  }
6062
- let corsCopyPositionWatcher = null;
6063
6179
  let syncStylePositionWatcher = null;
6064
6180
  let cancelAsyncOperations = false;
6065
6181
  let isOverrideEmpty = true;
@@ -6121,8 +6237,8 @@
6121
6237
  return result;
6122
6238
  }
6123
6239
  function getRulesSync() {
6124
- if (corsCopy) {
6125
- return corsCopy.sheet.cssRules;
6240
+ if (corsCopies.has(element)) {
6241
+ return corsCopies.get(element).cssRules;
6126
6242
  }
6127
6243
  if (containsCSSImport()) {
6128
6244
  return null;
@@ -6144,29 +6260,13 @@
6144
6260
  }
6145
6261
  function insertStyle() {
6146
6262
  if (inMode === "next") {
6147
- if (corsCopy) {
6148
- if (element.nextSibling !== corsCopy) {
6149
- element.parentNode.insertBefore(
6150
- corsCopy,
6151
- element.nextSibling
6152
- );
6153
- }
6154
- if (corsCopy.nextSibling !== syncStyle) {
6155
- element.parentNode.insertBefore(
6156
- syncStyle,
6157
- corsCopy.nextSibling
6158
- );
6159
- }
6160
- } else if (element.nextSibling !== syncStyle) {
6263
+ if (element.nextSibling !== syncStyle) {
6161
6264
  element.parentNode.insertBefore(
6162
6265
  syncStyle,
6163
6266
  element.nextSibling
6164
6267
  );
6165
6268
  }
6166
6269
  } else if (inMode === "away") {
6167
- if (corsCopy && !corsCopy.parentNode) {
6168
- injectStyleAway(corsCopy);
6169
- }
6170
6270
  injectStyleAway(syncStyle);
6171
6271
  }
6172
6272
  }
@@ -6242,8 +6342,8 @@
6242
6342
  return null;
6243
6343
  }
6244
6344
  await createOrUpdateCORSCopy(cssText, cssBasePath);
6245
- if (corsCopy) {
6246
- return corsCopy.sheet.cssRules;
6345
+ if (corsCopies.has(element)) {
6346
+ return corsCopies.get(element).cssRules;
6247
6347
  }
6248
6348
  return null;
6249
6349
  }
@@ -6254,44 +6354,25 @@
6254
6354
  cssText,
6255
6355
  cssBasePath
6256
6356
  );
6257
- if (corsCopy) {
6357
+ if (corsCopies.has(element)) {
6258
6358
  if (
6259
- (corsCopy.textContent?.length ?? 0) <
6359
+ (corsCopiesTextLengths.get(element) ?? 0) <
6260
6360
  fullCSSText.length
6261
6361
  ) {
6262
- corsCopy.textContent = fullCSSText;
6362
+ corsCopies.get(element).replaceSync(fullCSSText);
6363
+ corsCopiesTextLengths.set(
6364
+ element,
6365
+ fullCSSText.length
6366
+ );
6263
6367
  }
6264
6368
  } else {
6265
- corsCopy = createCORSCopy(
6266
- fullCSSText,
6267
- inMode === "next"
6268
- ? (cc) =>
6269
- element.parentNode.insertBefore(
6270
- cc,
6271
- element.nextSibling
6272
- )
6273
- : injectStyleAway
6274
- );
6275
- if (corsCopy) {
6276
- if (inMode === "next") {
6277
- element.parentNode.insertBefore(
6278
- corsCopy,
6279
- element.nextSibling
6280
- );
6281
- } else if (inMode === "away") {
6282
- injectStyleAway(corsCopy);
6283
- }
6284
- }
6369
+ const corsCopy = new CSSStyleSheet();
6370
+ corsCopy.replaceSync(fullCSSText);
6371
+ corsCopies.set(element, corsCopy);
6285
6372
  }
6286
6373
  } catch (err) {
6287
6374
  logWarn(err);
6288
6375
  }
6289
- if (corsCopy && inMode === "next") {
6290
- corsCopyPositionWatcher = watchForNodePosition(
6291
- corsCopy,
6292
- "prev-sibling"
6293
- );
6294
- }
6295
6376
  }
6296
6377
  }
6297
6378
  function details(options) {
@@ -6414,13 +6495,12 @@
6414
6495
  function pause() {
6415
6496
  observer.disconnect();
6416
6497
  cancelAsyncOperations = true;
6417
- corsCopyPositionWatcher && corsCopyPositionWatcher.stop();
6418
6498
  syncStylePositionWatcher && syncStylePositionWatcher.stop();
6419
6499
  sheetChangeWatcher.stop();
6420
6500
  }
6421
6501
  function destroy() {
6422
6502
  pause();
6423
- removeNode(corsCopy);
6503
+ corsCopies.delete(element);
6424
6504
  removeNode(syncStyle);
6425
6505
  loadingEnd();
6426
6506
  if (rejectorsForLoadingLinks.has(loadingLinkId)) {
@@ -6448,7 +6528,6 @@
6448
6528
  }
6449
6529
  logWarn("Restore style", syncStyle, element);
6450
6530
  insertStyle();
6451
- corsCopyPositionWatcher && corsCopyPositionWatcher.skip();
6452
6531
  syncStylePositionWatcher && syncStylePositionWatcher.skip();
6453
6532
  if (!isOverrideEmpty) {
6454
6533
  forceRenderStyle = true;
@@ -6521,7 +6600,9 @@
6521
6600
  origin: location.origin
6522
6601
  });
6523
6602
  }
6524
- writeCSSFetchCache(url, text);
6603
+ if (parsedURL.origin === location.origin) {
6604
+ writeCSSFetchCache(url, text);
6605
+ }
6525
6606
  return text;
6526
6607
  }
6527
6608
  async function replaceCSSImports(cssText, basePath, cache = new Map()) {
@@ -6577,20 +6658,6 @@
6577
6658
  cssText = cssText.trim();
6578
6659
  return cssText;
6579
6660
  }
6580
- function createCORSCopy(cssText, inject) {
6581
- if (!cssText) {
6582
- return null;
6583
- }
6584
- const cors = document.createElement("style");
6585
- cors.classList.add("darkreader");
6586
- cors.classList.add("darkreader--cors");
6587
- cors.media = "screen";
6588
- cors.textContent = cssText;
6589
- inject(cors);
6590
- cors.sheet.disabled = true;
6591
- corsStyleSet.add(cors);
6592
- return cors;
6593
- }
6594
6661
 
6595
6662
  function injectProxy(
6596
6663
  enableStyleSheetsProxy,
package/darkreader.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Dark Reader v4.9.113
2
+ * Dark Reader v4.9.117
3
3
  * https://darkreader.org/
4
4
  */
5
5
 
@@ -1828,10 +1828,10 @@ function iterateCSSRules(rules, iterate, onImportError) {
1828
1828
  m.startsWith("all") ||
1829
1829
  m.startsWith("(")
1830
1830
  );
1831
- const isPrintOrSpeech = media.some(
1832
- (m) => m.startsWith("print") || m.startsWith("speech")
1833
- );
1834
- if (isScreenOrAllOrQuery || !isPrintOrSpeech) {
1831
+ const isNotScreen =
1832
+ !isScreenOrAllOrQuery &&
1833
+ media.some((m) => ignoredMedia.some((i) => m.startsWith(i)));
1834
+ if (isScreenOrAllOrQuery || !isNotScreen) {
1835
1835
  iterateCSSRules(rule.cssRules, iterate, onImportError);
1836
1836
  }
1837
1837
  } else if (isSupportsRule(rule)) {
@@ -1845,6 +1845,17 @@ function iterateCSSRules(rules, iterate, onImportError) {
1845
1845
  }
1846
1846
  });
1847
1847
  }
1848
+ const ignoredMedia = [
1849
+ "aural",
1850
+ "braille",
1851
+ "embossed",
1852
+ "handheld",
1853
+ "print",
1854
+ "projection",
1855
+ "speech",
1856
+ "tty",
1857
+ "tv"
1858
+ ];
1848
1859
  const shorthandVarDependantProperties = [
1849
1860
  "background",
1850
1861
  "border",
@@ -1890,7 +1901,8 @@ function iterateCSSDeclarations(style, iterate) {
1890
1901
  }
1891
1902
  }
1892
1903
  if (
1893
- cssText.includes("background-color: ;") &&
1904
+ (cssText.includes("background-color: ;") ||
1905
+ cssText.includes("background-image: ;")) &&
1894
1906
  !style.getPropertyValue("background")
1895
1907
  ) {
1896
1908
  handleEmptyShorthand("background", style, iterate);
@@ -1923,6 +1935,7 @@ function handleEmptyShorthand(shorthand, style, iterate) {
1923
1935
  }
1924
1936
  } else if (shorthand === "background") {
1925
1937
  iterate("background-color", "#ffffff");
1938
+ iterate("background-image", "none");
1926
1939
  }
1927
1940
  }
1928
1941
  }
@@ -2560,6 +2573,10 @@ async function bgFetch(request) {
2560
2573
  if (window.DarkReader?.Plugins?.fetch) {
2561
2574
  return window.DarkReader.Plugins.fetch(request);
2562
2575
  }
2576
+ const parsedURL = new URL(request.url);
2577
+ if (parsedURL.origin !== request.origin && shouldIgnoreCors(parsedURL)) {
2578
+ throw new Error("Cross-origin limit reached");
2579
+ }
2563
2580
  return new Promise((resolve, reject) => {
2564
2581
  const id = generateUID();
2565
2582
  resolvers$1.set(id, resolve);
@@ -2585,6 +2602,25 @@ chrome.runtime.onMessage.addListener(({type, data, error, id}) => {
2585
2602
  }
2586
2603
  }
2587
2604
  });
2605
+ const ipV4RegExp = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
2606
+ const MAX_CORS_DOMAINS = 16;
2607
+ const corsDomains = new Set();
2608
+ function shouldIgnoreCors(url) {
2609
+ const host = url.hostname;
2610
+ if (!corsDomains.has(host)) {
2611
+ corsDomains.add(host);
2612
+ }
2613
+ if (
2614
+ corsDomains.size >= MAX_CORS_DOMAINS ||
2615
+ host === "localhost" ||
2616
+ host.startsWith("[") ||
2617
+ host.endsWith(".local") ||
2618
+ host.match(ipV4RegExp)
2619
+ ) {
2620
+ return true;
2621
+ }
2622
+ return false;
2623
+ }
2588
2624
 
2589
2625
  const imageManager = new AsyncQueue();
2590
2626
  async function getImageDetails(url) {
@@ -2623,7 +2659,11 @@ async function getDataURL(url) {
2623
2659
  if (parsedURL.origin === location.origin) {
2624
2660
  return await loadAsDataURL(url);
2625
2661
  }
2626
- return await bgFetch({url, responseType: "data-url"});
2662
+ return await bgFetch({
2663
+ url,
2664
+ responseType: "data-url",
2665
+ origin: location.origin
2666
+ });
2627
2667
  }
2628
2668
  async function tryCreateImageBitmap(blob) {
2629
2669
  try {
@@ -3350,7 +3390,12 @@ function getBgImageModifier(value, rule, ignoreImageSelectors, isCancelled) {
3350
3390
  awaitingForImageLoading.set(url, []);
3351
3391
  imageDetails = await getImageDetails(url);
3352
3392
  imageDetailsCache.set(url, imageDetails);
3353
- writeImageDetailsCache(url, imageDetails);
3393
+ if (!url.startsWith("data:")) {
3394
+ const parsedURL = new URL(url);
3395
+ if (parsedURL.origin === location.origin) {
3396
+ writeImageDetailsCache(url, imageDetails);
3397
+ }
3398
+ }
3354
3399
  awaitingForImageLoading
3355
3400
  .get(url)
3356
3401
  .forEach((resolve) => resolve(imageDetails));
@@ -3584,6 +3629,9 @@ const VAR_TYPE_BG_COLOR = 1 << 0;
3584
3629
  const VAR_TYPE_TEXT_COLOR = 1 << 1;
3585
3630
  const VAR_TYPE_BORDER_COLOR = 1 << 2;
3586
3631
  const VAR_TYPE_BG_IMG = 1 << 3;
3632
+ const shouldSetDefaultColor =
3633
+ !location.hostname.startsWith("www.ebay.") &&
3634
+ !location.hostname.includes(".ebay.");
3587
3635
  class VariablesStore {
3588
3636
  constructor() {
3589
3637
  this.varTypes = new Map();
@@ -3833,10 +3881,12 @@ class VariablesStore {
3833
3881
  (isSimpleConstructedColor && property === "background")
3834
3882
  ) {
3835
3883
  return (theme) => {
3836
- const defaultFallback = tryModifyBgColor(
3837
- isConstructedColor ? "255, 255, 255" : "#ffffff",
3838
- theme
3839
- );
3884
+ const defaultFallback = shouldSetDefaultColor
3885
+ ? tryModifyBgColor(
3886
+ isConstructedColor ? "255, 255, 255" : "#ffffff",
3887
+ theme
3888
+ )
3889
+ : "transparent";
3840
3890
  return replaceCSSVariablesNames(
3841
3891
  sourceValue,
3842
3892
  (v) => wrapBgColorVariableName(v),
@@ -4518,6 +4568,12 @@ function createStyleSheetModifier() {
4518
4568
  if (emptyIsWhereSelector || viewTransitionSelector) {
4519
4569
  selectorText = ".darkreader-unsupported-selector";
4520
4570
  }
4571
+ if (isChromium && selectorText.endsWith("::picker")) {
4572
+ selectorText = selectorText.replaceAll(
4573
+ "::picker",
4574
+ "::picker(select)"
4575
+ );
4576
+ }
4521
4577
  let ruleText = `${selectorText} {`;
4522
4578
  for (const dec of declarations) {
4523
4579
  const {property, value, important} = dec;
@@ -5018,6 +5074,8 @@ function createAdoptedStyleSheetFallback() {
5018
5074
  }
5019
5075
 
5020
5076
  const hostsBreakingOnStylePosition = [
5077
+ "gogoprivate.com",
5078
+ "gprivate.com",
5021
5079
  "www.berlingske.dk",
5022
5080
  "www.bloomberg.com",
5023
5081
  "www.diffusioneshop.com",
@@ -5382,12 +5440,49 @@ function shouldIgnoreInlineStyle(element, selectors) {
5382
5440
  }
5383
5441
  return false;
5384
5442
  }
5443
+ const LOOP_DETECTION_THRESHOLD = 1000;
5444
+ const MAX_LOOP_CYCLES = 10;
5445
+ const elementsLastChanges = new WeakMap();
5446
+ const elementsLoopCycles = new WeakMap();
5447
+ const SMALL_SVG_THRESHOLD = 32;
5448
+ const svgNodesRoots = new WeakMap();
5449
+ const svgRootSizeTestResults = new WeakMap();
5450
+ function getSVGElementRoot(svgElement) {
5451
+ if (!svgElement) {
5452
+ return null;
5453
+ }
5454
+ if (svgNodesRoots.has(svgElement)) {
5455
+ return svgNodesRoots.get(svgElement);
5456
+ }
5457
+ if (svgElement instanceof SVGSVGElement) {
5458
+ return svgElement;
5459
+ }
5460
+ const parent = svgElement.parentNode;
5461
+ const root = getSVGElementRoot(parent);
5462
+ svgNodesRoots.set(svgElement, root);
5463
+ return root;
5464
+ }
5385
5465
  function overrideInlineStyle(
5386
5466
  element,
5387
5467
  theme,
5388
5468
  ignoreInlineSelectors,
5389
5469
  ignoreImageSelectors
5390
5470
  ) {
5471
+ if (elementsLastChanges.has(element)) {
5472
+ if (
5473
+ Date.now() - elementsLastChanges.get(element) <
5474
+ LOOP_DETECTION_THRESHOLD
5475
+ ) {
5476
+ const cycles = elementsLoopCycles.get(element) ?? 0;
5477
+ elementsLoopCycles.set(element, cycles + 1);
5478
+ }
5479
+ if ((elementsLoopCycles.get(element) ?? 0) >= MAX_LOOP_CYCLES) {
5480
+ return;
5481
+ }
5482
+ }
5483
+ if (element.parentElement?.dataset.nodeViewContent) {
5484
+ return;
5485
+ }
5391
5486
  const cacheKey = getInlineStyleCacheKey(element, theme);
5392
5487
  if (cacheKey === inlineStyleCache.get(element)) {
5393
5488
  return;
@@ -5538,14 +5633,34 @@ function overrideInlineStyle(
5538
5633
  }
5539
5634
  if (isSVGElement) {
5540
5635
  if (element.hasAttribute("fill")) {
5541
- const SMALL_SVG_LIMIT = 32;
5542
5636
  const value = element.getAttribute("fill");
5543
5637
  if (value !== "none") {
5544
5638
  if (!(element instanceof SVGTextElement)) {
5545
5639
  const handleSVGElement = () => {
5546
- const {width, height} = element.getBoundingClientRect();
5547
- const isBg =
5548
- width > SMALL_SVG_LIMIT || height > SMALL_SVG_LIMIT;
5640
+ let isSVGSmall = false;
5641
+ const root = getSVGElementRoot(element);
5642
+ if (!root) {
5643
+ return;
5644
+ }
5645
+ if (svgRootSizeTestResults.has(root)) {
5646
+ isSVGSmall = svgRootSizeTestResults.get(root);
5647
+ } else {
5648
+ const svgBounds = root.getBoundingClientRect();
5649
+ isSVGSmall =
5650
+ svgBounds.width * svgBounds.height <=
5651
+ Math.pow(SMALL_SVG_THRESHOLD, 2);
5652
+ svgRootSizeTestResults.set(root, isSVGSmall);
5653
+ }
5654
+ let isBg;
5655
+ if (isSVGSmall) {
5656
+ isBg = false;
5657
+ } else {
5658
+ const {width, height} =
5659
+ element.getBoundingClientRect();
5660
+ isBg =
5661
+ width > SMALL_SVG_THRESHOLD ||
5662
+ height > SMALL_SVG_THRESHOLD;
5663
+ }
5549
5664
  setCustomProp(
5550
5665
  "fill",
5551
5666
  isBg ? "background-color" : "color",
@@ -5630,6 +5745,7 @@ function overrideInlineStyle(
5630
5745
  element.removeAttribute(overrides[cssProp].dataAttr);
5631
5746
  });
5632
5747
  inlineStyleCache.set(element, getInlineStyleCacheKey(element, theme));
5748
+ elementsLastChanges.set(element, Date.now());
5633
5749
  }
5634
5750
 
5635
5751
  const metaThemeColorName = "theme-color";
@@ -5815,7 +5931,7 @@ function shouldManageStyle(element) {
5815
5931
  : true) &&
5816
5932
  !isFontsGoogleApiStyle(element))) &&
5817
5933
  !element.classList.contains("darkreader") &&
5818
- element.media.toLowerCase() !== "print" &&
5934
+ !ignoredMedia.includes(element.media.toLowerCase()) &&
5819
5935
  !element.classList.contains("stylus")
5820
5936
  );
5821
5937
  }
@@ -5839,7 +5955,8 @@ function getManageableStyles(node, results = [], deep = true) {
5839
5955
  return results;
5840
5956
  }
5841
5957
  const syncStyleSet = new WeakSet();
5842
- const corsStyleSet = new WeakSet();
5958
+ const corsCopies = new WeakMap();
5959
+ const corsCopiesTextLengths = new WeakMap();
5843
5960
  let loadingLinkCounter = 0;
5844
5961
  const rejectorsForLoadingLinks = new Map();
5845
5962
  function cleanLoadingLinks() {
@@ -5847,7 +5964,6 @@ function cleanLoadingLinks() {
5847
5964
  }
5848
5965
  function manageStyle(element, {update, loadingStart, loadingEnd}) {
5849
5966
  const inMode = getStyleInjectionMode();
5850
- let corsCopy = null;
5851
5967
  let syncStyle = null;
5852
5968
  if (inMode === "next") {
5853
5969
  const prevStyles = [];
@@ -5858,16 +5974,11 @@ function manageStyle(element, {update, loadingStart, loadingEnd}) {
5858
5974
  ) {
5859
5975
  prevStyles.push(next);
5860
5976
  }
5861
- corsCopy =
5862
- prevStyles.find(
5863
- (el) => el.matches(".darkreader--cors") && !corsStyleSet.has(el)
5864
- ) || null;
5865
5977
  syncStyle =
5866
5978
  prevStyles.find(
5867
5979
  (el) => el.matches(".darkreader--sync") && !syncStyleSet.has(el)
5868
5980
  ) || null;
5869
5981
  }
5870
- let corsCopyPositionWatcher = null;
5871
5982
  let syncStylePositionWatcher = null;
5872
5983
  let cancelAsyncOperations = false;
5873
5984
  let isOverrideEmpty = true;
@@ -5925,8 +6036,8 @@ function manageStyle(element, {update, loadingStart, loadingEnd}) {
5925
6036
  return result;
5926
6037
  }
5927
6038
  function getRulesSync() {
5928
- if (corsCopy) {
5929
- return corsCopy.sheet.cssRules;
6039
+ if (corsCopies.has(element)) {
6040
+ return corsCopies.get(element).cssRules;
5930
6041
  }
5931
6042
  if (containsCSSImport()) {
5932
6043
  return null;
@@ -5947,26 +6058,10 @@ function manageStyle(element, {update, loadingStart, loadingEnd}) {
5947
6058
  }
5948
6059
  function insertStyle() {
5949
6060
  if (inMode === "next") {
5950
- if (corsCopy) {
5951
- if (element.nextSibling !== corsCopy) {
5952
- element.parentNode.insertBefore(
5953
- corsCopy,
5954
- element.nextSibling
5955
- );
5956
- }
5957
- if (corsCopy.nextSibling !== syncStyle) {
5958
- element.parentNode.insertBefore(
5959
- syncStyle,
5960
- corsCopy.nextSibling
5961
- );
5962
- }
5963
- } else if (element.nextSibling !== syncStyle) {
6061
+ if (element.nextSibling !== syncStyle) {
5964
6062
  element.parentNode.insertBefore(syncStyle, element.nextSibling);
5965
6063
  }
5966
6064
  } else if (inMode === "away") {
5967
- if (corsCopy && !corsCopy.parentNode) {
5968
- injectStyleAway(corsCopy);
5969
- }
5970
6065
  injectStyleAway(syncStyle);
5971
6066
  }
5972
6067
  }
@@ -6042,8 +6137,8 @@ function manageStyle(element, {update, loadingStart, loadingEnd}) {
6042
6137
  return null;
6043
6138
  }
6044
6139
  await createOrUpdateCORSCopy(cssText, cssBasePath);
6045
- if (corsCopy) {
6046
- return corsCopy.sheet.cssRules;
6140
+ if (corsCopies.has(element)) {
6141
+ return corsCopies.get(element).cssRules;
6047
6142
  }
6048
6143
  return null;
6049
6144
  }
@@ -6054,43 +6149,22 @@ function manageStyle(element, {update, loadingStart, loadingEnd}) {
6054
6149
  cssText,
6055
6150
  cssBasePath
6056
6151
  );
6057
- if (corsCopy) {
6152
+ if (corsCopies.has(element)) {
6058
6153
  if (
6059
- (corsCopy.textContent?.length ?? 0) < fullCSSText.length
6154
+ (corsCopiesTextLengths.get(element) ?? 0) <
6155
+ fullCSSText.length
6060
6156
  ) {
6061
- corsCopy.textContent = fullCSSText;
6157
+ corsCopies.get(element).replaceSync(fullCSSText);
6158
+ corsCopiesTextLengths.set(element, fullCSSText.length);
6062
6159
  }
6063
6160
  } else {
6064
- corsCopy = createCORSCopy(
6065
- fullCSSText,
6066
- inMode === "next"
6067
- ? (cc) =>
6068
- element.parentNode.insertBefore(
6069
- cc,
6070
- element.nextSibling
6071
- )
6072
- : injectStyleAway
6073
- );
6074
- if (corsCopy) {
6075
- if (inMode === "next") {
6076
- element.parentNode.insertBefore(
6077
- corsCopy,
6078
- element.nextSibling
6079
- );
6080
- } else if (inMode === "away") {
6081
- injectStyleAway(corsCopy);
6082
- }
6083
- }
6161
+ const corsCopy = new CSSStyleSheet();
6162
+ corsCopy.replaceSync(fullCSSText);
6163
+ corsCopies.set(element, corsCopy);
6084
6164
  }
6085
6165
  } catch (err) {
6086
6166
  logWarn(err);
6087
6167
  }
6088
- if (corsCopy && inMode === "next") {
6089
- corsCopyPositionWatcher = watchForNodePosition(
6090
- corsCopy,
6091
- "prev-sibling"
6092
- );
6093
- }
6094
6168
  }
6095
6169
  }
6096
6170
  function details(options) {
@@ -6213,13 +6287,12 @@ function manageStyle(element, {update, loadingStart, loadingEnd}) {
6213
6287
  function pause() {
6214
6288
  observer.disconnect();
6215
6289
  cancelAsyncOperations = true;
6216
- corsCopyPositionWatcher && corsCopyPositionWatcher.stop();
6217
6290
  syncStylePositionWatcher && syncStylePositionWatcher.stop();
6218
6291
  sheetChangeWatcher.stop();
6219
6292
  }
6220
6293
  function destroy() {
6221
6294
  pause();
6222
- removeNode(corsCopy);
6295
+ corsCopies.delete(element);
6223
6296
  removeNode(syncStyle);
6224
6297
  loadingEnd();
6225
6298
  if (rejectorsForLoadingLinks.has(loadingLinkId)) {
@@ -6247,7 +6320,6 @@ function manageStyle(element, {update, loadingStart, loadingEnd}) {
6247
6320
  }
6248
6321
  logWarn("Restore style", syncStyle, element);
6249
6322
  insertStyle();
6250
- corsCopyPositionWatcher && corsCopyPositionWatcher.skip();
6251
6323
  syncStylePositionWatcher && syncStylePositionWatcher.skip();
6252
6324
  if (!isOverrideEmpty) {
6253
6325
  forceRenderStyle = true;
@@ -6318,7 +6390,9 @@ async function loadText(url) {
6318
6390
  origin: location.origin
6319
6391
  });
6320
6392
  }
6321
- writeCSSFetchCache(url, text);
6393
+ if (parsedURL.origin === location.origin) {
6394
+ writeCSSFetchCache(url, text);
6395
+ }
6322
6396
  return text;
6323
6397
  }
6324
6398
  async function replaceCSSImports(cssText, basePath, cache = new Map()) {
@@ -6374,20 +6448,6 @@ async function replaceCSSImports(cssText, basePath, cache = new Map()) {
6374
6448
  cssText = cssText.trim();
6375
6449
  return cssText;
6376
6450
  }
6377
- function createCORSCopy(cssText, inject) {
6378
- if (!cssText) {
6379
- return null;
6380
- }
6381
- const cors = document.createElement("style");
6382
- cors.classList.add("darkreader");
6383
- cors.classList.add("darkreader--cors");
6384
- cors.media = "screen";
6385
- cors.textContent = cssText;
6386
- inject(cors);
6387
- cors.sheet.disabled = true;
6388
- corsStyleSet.add(cors);
6389
- return cors;
6390
- }
6391
6451
 
6392
6452
  function injectProxy(enableStyleSheetsProxy, enableCustomElementRegistryProxy) {
6393
6453
  document.dispatchEvent(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "darkreader",
3
- "version": "4.9.113",
3
+ "version": "4.9.117",
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",
@@ -66,29 +66,29 @@
66
66
  "malevic": "0.20.2"
67
67
  },
68
68
  "devDependencies": {
69
- "@eslint/compat": "1.4.0",
69
+ "@eslint/compat": "2.0.0",
70
70
  "@eslint/eslintrc": "3.3.1",
71
- "@eslint/js": "9.38.0",
71
+ "@eslint/js": "9.39.1",
72
72
  "@rollup/plugin-node-resolve": "16.0.3",
73
- "@rollup/plugin-replace": "6.0.2",
73
+ "@rollup/plugin-replace": "6.0.3",
74
74
  "@rollup/plugin-typescript": "12.3.0",
75
- "@stylistic/eslint-plugin": "5.5.0",
76
- "@types/chrome": "0.1.26",
75
+ "@stylistic/eslint-plugin": "5.6.1",
76
+ "@types/chrome": "0.1.31",
77
77
  "@types/eslint": "9.6.1",
78
- "@types/jasmine": "5.1.12",
78
+ "@types/jasmine": "5.1.13",
79
79
  "@types/jest": "30.0.0",
80
80
  "@types/karma": "6.3.9",
81
81
  "@types/karma-coverage": "2.0.3",
82
- "@types/node": "24.9.1",
82
+ "@types/node": "24.10.1",
83
83
  "@types/ws": "8.18.1",
84
84
  "chokidar": "4.0.3",
85
85
  "eslint-plugin-compat": "6.0.2",
86
86
  "eslint-plugin-import": "2.32.0",
87
- "globals": "16.4.0",
88
- "globby": "15.0.0",
89
- "jasmine-core": "5.12.0",
87
+ "globals": "16.5.0",
88
+ "globby": "16.0.0",
89
+ "jasmine-core": "5.12.1",
90
90
  "jest": "30.2.0",
91
- "jest-extended": "6.0.0",
91
+ "jest-extended": "7.0.0",
92
92
  "karma": "6.4.4",
93
93
  "karma-chrome-launcher": "3.2.0",
94
94
  "karma-coverage": "2.2.1",
@@ -99,18 +99,18 @@
99
99
  "karma-spec-reporter": "0.0.36",
100
100
  "less": "4.4.2",
101
101
  "prettier": "3.6.2",
102
- "puppeteer-core": "24.26.1",
103
- "rollup": "4.52.5",
102
+ "puppeteer-core": "24.30.0",
103
+ "rollup": "4.53.3",
104
104
  "rollup-plugin-istanbul": "5.0.0",
105
105
  "ts-jest": "29.4.5",
106
106
  "tslib": "2.8.1",
107
107
  "typescript": "5.9.3",
108
- "typescript-eslint": "8.46.2",
108
+ "typescript-eslint": "8.47.0",
109
109
  "ws": "8.18.3",
110
110
  "yazl": "3.3.1"
111
111
  },
112
112
  "optionalDependencies": {
113
- "@rollup/rollup-linux-x64-gnu": "4.52.5",
114
- "@rollup/rollup-win32-x64-msvc": "4.52.5"
113
+ "@rollup/rollup-linux-x64-gnu": "4.53.3",
114
+ "@rollup/rollup-win32-x64-msvc": "4.53.3"
115
115
  }
116
116
  }