darkreader 4.9.118 → 4.9.120

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/darkreader.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Dark Reader v4.9.118
2
+ * Dark Reader v4.9.120
3
3
  * https://darkreader.org/
4
4
  */
5
5
 
@@ -1010,54 +1010,155 @@ function parse($color) {
1010
1010
  }
1011
1011
  return null;
1012
1012
  }
1013
- function getNumbers($color) {
1013
+ const C_0 = "0".charCodeAt(0);
1014
+ const C_9 = "9".charCodeAt(0);
1015
+ const C_e = "e".charCodeAt(0);
1016
+ const C_DOT = ".".charCodeAt(0);
1017
+ const C_PLUS = "+".charCodeAt(0);
1018
+ const C_MINUS = "-".charCodeAt(0);
1019
+ const C_SPACE = " ".charCodeAt(0);
1020
+ const C_COMMA = ",".charCodeAt(0);
1021
+ const C_SLASH = "/".charCodeAt(0);
1022
+ const C_PERCENT = "%".charCodeAt(0);
1023
+ function getNumbersFromString(input, range, units) {
1014
1024
  const numbers = [];
1015
- let prevPos = 0;
1016
- let isMining = false;
1017
- const startIndex = $color.indexOf("(");
1018
- $color = $color.substring(startIndex + 1, $color.length - 1);
1019
- for (let i = 0; i < $color.length; i++) {
1020
- const c = $color[i];
1021
- if ((c >= "0" && c <= "9") || c === "." || c === "+" || c === "-") {
1022
- isMining = true;
1023
- } else if (isMining && (c === " " || c === "," || c === "/")) {
1024
- numbers.push($color.substring(prevPos, i));
1025
- isMining = false;
1026
- prevPos = i + 1;
1027
- } else if (!isMining) {
1028
- prevPos = i + 1;
1029
- }
1030
- }
1031
- if (isMining) {
1032
- numbers.push($color.substring(prevPos, $color.length));
1025
+ const searchStart = input.indexOf("(") + 1;
1026
+ const searchEnd = input.length - 1;
1027
+ let numStart = -1;
1028
+ let unitStart = -1;
1029
+ const push = (matchEnd) => {
1030
+ const numEnd = unitStart > -1 ? unitStart : matchEnd;
1031
+ const $num = input.slice(numStart, numEnd);
1032
+ let n = parseFloat($num);
1033
+ const r = range[numbers.length];
1034
+ if (unitStart > -1) {
1035
+ const unit = input.slice(unitStart, matchEnd);
1036
+ const u = units[unit];
1037
+ if (u != null) {
1038
+ n *= r / u;
1039
+ }
1040
+ }
1041
+ if (r > 1) {
1042
+ n = Math.round(n);
1043
+ }
1044
+ numbers.push(n);
1045
+ numStart = -1;
1046
+ unitStart = -1;
1047
+ };
1048
+ for (let i = searchStart; i < searchEnd; i++) {
1049
+ const c = input.charCodeAt(i);
1050
+ const isNumChar =
1051
+ (c >= C_0 && c <= C_9) ||
1052
+ c === C_DOT ||
1053
+ c === C_PLUS ||
1054
+ c === C_MINUS ||
1055
+ c === C_e;
1056
+ const isDelimiter = c === C_SPACE || c === C_COMMA || c === C_SLASH;
1057
+ if (isNumChar) {
1058
+ if (numStart === -1) {
1059
+ numStart = i;
1060
+ }
1061
+ } else if (numStart > -1) {
1062
+ if (isDelimiter) {
1063
+ push(i);
1064
+ } else if (unitStart === -1) {
1065
+ unitStart = i;
1066
+ }
1067
+ }
1068
+ }
1069
+ if (numStart > -1) {
1070
+ push(searchEnd);
1033
1071
  }
1034
1072
  return numbers;
1035
1073
  }
1036
- function getNumbersFromString(str, range, units) {
1037
- const raw = getNumbers(str);
1038
- const unitsList = Object.entries(units);
1039
- const numbers = raw
1040
- .map((r) => r.trim())
1041
- .map((r, i) => {
1042
- let n;
1043
- const unit = unitsList.find(([u]) => r.endsWith(u));
1044
- if (unit) {
1045
- n =
1046
- (parseFloat(r.substring(0, r.length - unit[0].length)) /
1047
- unit[1]) *
1048
- range[i];
1074
+ const rgbRange = [255, 255, 255, 1];
1075
+ const rgbUnits = {"%": 100};
1076
+ function getRGBValues(input) {
1077
+ const CHAR_CODE_0 = 48;
1078
+ const length = input.length;
1079
+ let i = 0;
1080
+ let digitsCount = 0;
1081
+ let digitSequence = false;
1082
+ let floatDigitsCount = -1;
1083
+ let delimiter = C_SPACE;
1084
+ let channel = -1;
1085
+ let result = null;
1086
+ while (i < length) {
1087
+ const c = input.charCodeAt(i);
1088
+ if ((c >= C_0 && c <= C_9) || c === C_DOT) {
1089
+ if (!digitSequence) {
1090
+ digitSequence = true;
1091
+ digitsCount = 0;
1092
+ floatDigitsCount = -1;
1093
+ channel++;
1094
+ if (channel === 3 && result) {
1095
+ result[3] = 0;
1096
+ }
1097
+ if (channel > 3) {
1098
+ return null;
1099
+ }
1100
+ }
1101
+ if (c === C_DOT) {
1102
+ if (floatDigitsCount > 0) {
1103
+ return null;
1104
+ }
1105
+ floatDigitsCount = 0;
1049
1106
  } else {
1050
- n = parseFloat(r);
1107
+ const d = c - CHAR_CODE_0;
1108
+ if (!result) {
1109
+ result = [0, 0, 0, 1];
1110
+ }
1111
+ if (floatDigitsCount > -1) {
1112
+ floatDigitsCount++;
1113
+ result[channel] += d / 10 ** floatDigitsCount;
1114
+ } else {
1115
+ digitsCount++;
1116
+ if (digitsCount > 3) {
1117
+ return null;
1118
+ }
1119
+ result[channel] = result[channel] * 10 + d;
1120
+ }
1051
1121
  }
1052
- if (range[i] > 1) {
1053
- return Math.round(n);
1122
+ } else if (c === C_PERCENT) {
1123
+ if (
1124
+ channel < 0 ||
1125
+ channel > 3 ||
1126
+ delimiter !== C_SPACE ||
1127
+ !result
1128
+ ) {
1129
+ return null;
1054
1130
  }
1055
- return n;
1056
- });
1057
- return numbers;
1131
+ result[channel] =
1132
+ channel < 3
1133
+ ? Math.round((result[channel] * 255) / 100)
1134
+ : result[channel] / 100;
1135
+ digitSequence = false;
1136
+ } else {
1137
+ digitSequence = false;
1138
+ if (c === C_SPACE) {
1139
+ if (channel === 0) {
1140
+ delimiter = c;
1141
+ }
1142
+ } else if (c === C_COMMA) {
1143
+ if (channel === -1) {
1144
+ return null;
1145
+ }
1146
+ delimiter = C_COMMA;
1147
+ } else if (c === C_SLASH) {
1148
+ if (channel !== 2 || delimiter !== C_SPACE) {
1149
+ return null;
1150
+ }
1151
+ } else {
1152
+ return null;
1153
+ }
1154
+ }
1155
+ i++;
1156
+ }
1157
+ if (channel < 2 || channel > 3) {
1158
+ return null;
1159
+ }
1160
+ return result;
1058
1161
  }
1059
- const rgbRange = [255, 255, 255, 1];
1060
- const rgbUnits = {"%": 100};
1061
1162
  function parseRGB($rgb) {
1062
1163
  const [r, g, b, a = 1] = getNumbersFromString($rgb, rgbRange, rgbUnits);
1063
1164
  if (r == null || g == null || b == null || a == null) {
@@ -1074,28 +1175,48 @@ function parseHSL($hsl) {
1074
1175
  }
1075
1176
  return hslToRGB({h, s, l, a});
1076
1177
  }
1178
+ const C_A = "A".charCodeAt(0);
1179
+ const C_F = "F".charCodeAt(0);
1180
+ const C_a = "a".charCodeAt(0);
1181
+ const C_f = "f".charCodeAt(0);
1077
1182
  function parseHex($hex) {
1078
- const h = $hex.substring(1);
1079
- switch (h.length) {
1080
- case 3:
1081
- case 4: {
1082
- const [r, g, b] = [0, 1, 2].map((i) =>
1083
- parseInt(`${h[i]}${h[i]}`, 16)
1084
- );
1085
- const a = h.length === 3 ? 1 : parseInt(`${h[3]}${h[3]}`, 16) / 255;
1086
- return {r, g, b, a};
1183
+ const length = $hex.length;
1184
+ const digitCount = length - 1;
1185
+ const isShort = digitCount === 3 || digitCount === 4;
1186
+ const isLong = digitCount === 6 || digitCount === 8;
1187
+ if (!isShort && !isLong) {
1188
+ return null;
1189
+ }
1190
+ const hex = (i) => {
1191
+ const c = $hex.charCodeAt(i);
1192
+ if (c >= C_A && c <= C_F) {
1193
+ return c + 10 - C_A;
1087
1194
  }
1088
- case 6:
1089
- case 8: {
1090
- const [r, g, b] = [0, 2, 4].map((i) =>
1091
- parseInt(h.substring(i, i + 2), 16)
1092
- );
1093
- const a =
1094
- h.length === 6 ? 1 : parseInt(h.substring(6, 8), 16) / 255;
1095
- return {r, g, b, a};
1195
+ if (c >= C_a && c <= C_f) {
1196
+ return c + 10 - C_a;
1197
+ }
1198
+ return c - C_0;
1199
+ };
1200
+ let r;
1201
+ let g;
1202
+ let b;
1203
+ let a = 1;
1204
+ if (isShort) {
1205
+ r = hex(1) * 17;
1206
+ g = hex(2) * 17;
1207
+ b = hex(3) * 17;
1208
+ if (digitCount === 4) {
1209
+ a = (hex(4) * 17) / 255;
1210
+ }
1211
+ } else {
1212
+ r = hex(1) * 16 + hex(2);
1213
+ g = hex(3) * 16 + hex(4);
1214
+ b = hex(5) * 16 + hex(6);
1215
+ if (digitCount === 8) {
1216
+ a = (hex(7) * 16 + hex(8)) / 255;
1096
1217
  }
1097
1218
  }
1098
- return null;
1219
+ return {r, g, b, a};
1099
1220
  }
1100
1221
  function getColorByName($color) {
1101
1222
  const n = knownColors.get($color);
@@ -1877,13 +1998,6 @@ const shorthandVarDepPropRegexps = isSafari
1877
1998
  })
1878
1999
  : null;
1879
2000
  function iterateCSSDeclarations(style, iterate) {
1880
- forEach(style, (property) => {
1881
- const value = style.getPropertyValue(property).trim();
1882
- if (!value) {
1883
- return;
1884
- }
1885
- iterate(property, value);
1886
- });
1887
2001
  const cssText = style.cssText;
1888
2002
  if (cssText.includes("var(")) {
1889
2003
  if (isSafari) {
@@ -1917,6 +2031,13 @@ function iterateCSSDeclarations(style, iterate) {
1917
2031
  ) {
1918
2032
  handleEmptyShorthand("border", style, iterate);
1919
2033
  }
2034
+ forEach(style, (property) => {
2035
+ const value = style.getPropertyValue(property).trim();
2036
+ if (!value) {
2037
+ return;
2038
+ }
2039
+ iterate(property, value);
2040
+ });
1920
2041
  }
1921
2042
  function handleEmptyShorthand(shorthand, style, iterate) {
1922
2043
  const parentRule = style.parentRule;
@@ -2671,13 +2792,56 @@ const imageManager = new AsyncQueue();
2671
2792
  async function getImageDetails(url) {
2672
2793
  return new Promise(async (resolve, reject) => {
2673
2794
  try {
2674
- const dataURL = url.startsWith("data:")
2675
- ? url
2676
- : await getDataURL(url);
2795
+ let dataURL = url.startsWith("data:") ? url : await getDataURL(url);
2677
2796
  const blob =
2678
2797
  tryConvertDataURLToBlobSync(dataURL) ?? (await loadAsBlob(url));
2679
2798
  let image;
2799
+ let useViewBox = false;
2680
2800
  if (dataURL.startsWith("data:image/svg+xml")) {
2801
+ const commaIndex = dataURL.indexOf(",");
2802
+ if (commaIndex >= 0) {
2803
+ let svgText = dataURL.slice(commaIndex + 1);
2804
+ const encoding = dataURL.slice(0, commaIndex).split(";")[1];
2805
+ if (encoding === "base64") {
2806
+ if (svgText.includes("%")) {
2807
+ svgText = decodeURIComponent(svgText);
2808
+ }
2809
+ svgText = atob(svgText);
2810
+ } else if (svgText.startsWith("%3c")) {
2811
+ svgText = decodeURIComponent(svgText);
2812
+ }
2813
+ if (svgText.startsWith("<svg ")) {
2814
+ const closingIndex = svgText.indexOf(">");
2815
+ const svgOpening = svgText
2816
+ .slice(0, closingIndex + 1)
2817
+ .toLocaleLowerCase();
2818
+ if (
2819
+ svgOpening.includes("viewbox=") &&
2820
+ !svgOpening.includes("width=") &&
2821
+ !svgOpening.includes("height=")
2822
+ ) {
2823
+ useViewBox = true;
2824
+ const viewboxIndex = svgOpening.indexOf("viewbox=");
2825
+ const quote = svgOpening[viewboxIndex + 8];
2826
+ const viewboxCloseIndex = svgOpening.indexOf(
2827
+ quote,
2828
+ viewboxIndex + 9
2829
+ );
2830
+ const viewBox = svgOpening
2831
+ .slice(viewboxIndex + 9, viewboxCloseIndex)
2832
+ .split(" ")
2833
+ .map((x) => parseFloat(x));
2834
+ if (
2835
+ viewBox.length === 4 &&
2836
+ !viewBox.some((x) => isNaN(x))
2837
+ ) {
2838
+ const width = viewBox[2] - viewBox[0];
2839
+ const height = viewBox[3] - viewBox[1];
2840
+ dataURL = `data:image/svg+xml;base64,${btoa(`<svg width="${width}" height="${height}" ${svgText.slice(5)}`)}`;
2841
+ }
2842
+ }
2843
+ }
2844
+ }
2681
2845
  image = await loadImage(dataURL);
2682
2846
  } else {
2683
2847
  image =
@@ -2691,6 +2855,7 @@ async function getImageDetails(url) {
2691
2855
  dataURL: analysis.isLarge ? "" : dataURL,
2692
2856
  width: image.width,
2693
2857
  height: image.height,
2858
+ useViewBox,
2694
2859
  ...analysis
2695
2860
  });
2696
2861
  });
@@ -2877,13 +3042,16 @@ function onCSPError(err) {
2877
3042
  }
2878
3043
  document.addEventListener("securitypolicyviolation", onCSPError);
2879
3044
  const objectURLs = new Set();
2880
- function getFilteredImageURL({dataURL, width, height}, theme) {
3045
+ function getFilteredImageURL({dataURL, width, height, useViewBox}, theme) {
2881
3046
  if (dataURL.startsWith("data:image/svg+xml")) {
2882
3047
  dataURL = escapeXML(dataURL);
2883
3048
  }
2884
3049
  const matrix = getSVGFilterMatrixValue(theme);
3050
+ const size = useViewBox
3051
+ ? `viewBox="0 0 ${width} ${height}"`
3052
+ : `width="${width}" height="${height}"`;
2885
3053
  const svg = [
2886
- `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${width}" height="${height}">`,
3054
+ `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ${size}>`,
2887
3055
  "<defs>",
2888
3056
  '<filter id="darkreader-image-filter">',
2889
3057
  `<feColorMatrix type="matrix" values="${matrix}" />`,
@@ -2926,7 +3094,11 @@ function tryConvertDataURLToBlobSync(dataURL) {
2926
3094
  if (encoding !== "base64" || !mediaType) {
2927
3095
  return null;
2928
3096
  }
2929
- const characters = atob(dataURL.substring(commaIndex + 1));
3097
+ let base64Content = dataURL.substring(commaIndex + 1);
3098
+ if (base64Content.includes("%")) {
3099
+ base64Content = decodeURIComponent(base64Content);
3100
+ }
3101
+ const characters = atob(base64Content);
2930
3102
  const bytes = new Uint8Array(characters.length);
2931
3103
  for (let i = 0; i < characters.length; i++) {
2932
3104
  bytes[i] = characters.charCodeAt(i);
@@ -2963,6 +3135,12 @@ function cleanImageProcessingCache() {
2963
3135
  function getPriority(ruleStyle, property) {
2964
3136
  return Boolean(ruleStyle && ruleStyle.getPropertyPriority(property));
2965
3137
  }
3138
+ const bgPropsToCopy = [
3139
+ "background-clip",
3140
+ "background-position",
3141
+ "background-repeat",
3142
+ "background-size"
3143
+ ];
2966
3144
  function getModifiableCSSDeclaration(
2967
3145
  property,
2968
3146
  value,
@@ -3026,6 +3204,8 @@ function getModifiableCSSDeclaration(
3026
3204
  );
3027
3205
  } else if (property.includes("shadow")) {
3028
3206
  modifier = getShadowModifier(value);
3207
+ } else if (bgPropsToCopy.includes(property) && value !== "initial") {
3208
+ modifier = value;
3029
3209
  }
3030
3210
  if (!modifier) {
3031
3211
  return null;
@@ -4093,9 +4273,7 @@ class VariablesStore {
4093
4273
  }
4094
4274
  this.definedVars.add(varName);
4095
4275
  const isColor = Boolean(
4096
- value.match(rawRGBSpaceRegex) ||
4097
- value.match(rawRGBCommaRegex) ||
4098
- parseColorWithCache(value)
4276
+ getRGBValues(value) || parseColorWithCache(value)
4099
4277
  );
4100
4278
  if (isColor) {
4101
4279
  this.unknownColorVars.add(varName);
@@ -4426,16 +4604,13 @@ const textColorProps = [
4426
4604
  function isTextColorProperty(property) {
4427
4605
  return textColorProps.includes(property);
4428
4606
  }
4429
- const rawRGBSpaceRegex =
4430
- /^(\d{1,3})\s+(\d{1,3})\s+(\d{1,3})\s*(\/\s*\d+\.?\d*)?$/;
4431
- const rawRGBCommaRegex = /^(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})$/;
4432
4607
  function parseRawColorValue(input) {
4433
- const match =
4434
- input.match(rawRGBSpaceRegex) ?? input.match(rawRGBCommaRegex);
4435
- if (match) {
4436
- const color = match[4]
4437
- ? `rgb(${match[1]} ${match[2]} ${match[3]} / ${match[4]})`
4438
- : `rgb(${match[1]}, ${match[2]}, ${match[3]})`;
4608
+ const v = getRGBValues(input);
4609
+ if (v) {
4610
+ const color =
4611
+ v[3] < 1
4612
+ ? `rgb(${v[0]} ${v[1]} ${v[2]} / ${v[3]})`
4613
+ : `rgb(${v[0]} ${v[1]} ${v[2]})`;
4439
4614
  return {isRaw: true, color};
4440
4615
  }
4441
4616
  return {isRaw: false, color: input};
@@ -4448,7 +4623,9 @@ function handleRawColorValue(input, theme, modifyFunction) {
4448
4623
  if (isRaw) {
4449
4624
  const outputInRGB = parseColorWithCache(outputColor);
4450
4625
  return outputInRGB
4451
- ? `${outputInRGB.r}, ${outputInRGB.g}, ${outputInRGB.b}`
4626
+ ? Number.isNaN(outputInRGB.a) || outputInRGB.a === 1
4627
+ ? `${outputInRGB.r}, ${outputInRGB.g}, ${outputInRGB.b}`
4628
+ : `${outputInRGB.r}, ${outputInRGB.g}, ${outputInRGB.b}, ${outputInRGB.a}`
4452
4629
  : outputColor;
4453
4630
  }
4454
4631
  return outputColor;
@@ -5466,8 +5643,8 @@ function shouldAnalyzeSVGAsImage(svg) {
5466
5643
  }
5467
5644
  const shouldAnalyze = Boolean(
5468
5645
  svg &&
5469
- (svg.getAttribute("class")?.includes("logo") ||
5470
- svg.parentElement?.getAttribute("class")?.includes("logo"))
5646
+ (svg.getAttribute("class")?.includes("logo") ||
5647
+ svg.parentElement?.getAttribute("class")?.includes("logo"))
5471
5648
  );
5472
5649
  svgAnalysisConditionCache.set(svg, shouldAnalyze);
5473
5650
  return shouldAnalyze;
@@ -5510,6 +5687,7 @@ function getSVGElementRoot(svgElement) {
5510
5687
  svgNodesRoots.set(svgElement, root);
5511
5688
  return root;
5512
5689
  }
5690
+ const inlineStringValueCache = new Map();
5513
5691
  function overrideInlineStyle(
5514
5692
  element,
5515
5693
  theme,
@@ -5537,6 +5715,13 @@ function overrideInlineStyle(
5537
5715
  }
5538
5716
  const unsetProps = new Set(Object.keys(overrides));
5539
5717
  function setCustomProp(targetCSSProp, modifierCSSProp, cssVal) {
5718
+ const cachedStringValue = inlineStringValueCache
5719
+ .get(modifierCSSProp)
5720
+ ?.get(cssVal);
5721
+ if (cachedStringValue) {
5722
+ setStaticValue(cachedStringValue);
5723
+ return;
5724
+ }
5540
5725
  const mod = getModifiableCSSDeclaration(
5541
5726
  modifierCSSProp,
5542
5727
  cssVal,
@@ -5602,6 +5787,10 @@ function overrideInlineStyle(
5602
5787
  typeof mod.value === "function" ? mod.value(theme) : mod.value;
5603
5788
  if (typeof value === "string") {
5604
5789
  setStaticValue(value);
5790
+ if (!inlineStringValueCache.has(modifierCSSProp)) {
5791
+ inlineStringValueCache.set(modifierCSSProp, new Map());
5792
+ }
5793
+ inlineStringValueCache.get(modifierCSSProp).set(cssVal, value);
5605
5794
  } else if (value instanceof Promise) {
5606
5795
  setAsyncValue(value, cssVal);
5607
5796
  } else if (typeof value === "object") {
@@ -5676,13 +5865,16 @@ function overrideInlineStyle(
5676
5865
  let value = element.getAttribute("color");
5677
5866
  if (value.match(/^[0-9a-f]{3}$/i) || value.match(/^[0-9a-f]{6}$/i)) {
5678
5867
  value = `#${value}`;
5868
+ } else if (value.match(/^#?[0-9a-f]{4}$/i)) {
5869
+ const hex = value.startsWith("#") ? value.substring(1) : value;
5870
+ value = `#${hex}00`;
5679
5871
  }
5680
5872
  setCustomProp("color", "color", value);
5681
5873
  }
5682
5874
  if (isSVGElement) {
5683
5875
  if (element.hasAttribute("fill")) {
5684
5876
  const value = element.getAttribute("fill");
5685
- if (value !== "none") {
5877
+ if (value !== "none" && value !== "currentColor") {
5686
5878
  if (!(element instanceof SVGTextElement)) {
5687
5879
  const handleSVGElement = () => {
5688
5880
  let isSVGSmall = false;
@@ -5947,17 +6139,28 @@ function createRAFSheetWatcher(
5947
6139
  }
5948
6140
 
5949
6141
  const STYLE_SELECTOR = 'style, link[rel*="stylesheet" i]:not([disabled])';
5950
- function isFontsGoogleApiStyle(element) {
5951
- if (!element.href) {
6142
+ let ignoredCSSURLPatterns = [];
6143
+ function setIgnoredCSSURLs(patterns) {
6144
+ ignoredCSSURLPatterns = patterns || [];
6145
+ }
6146
+ function shouldIgnoreCSSURL(url) {
6147
+ if (!url || ignoredCSSURLPatterns.length === 0) {
5952
6148
  return false;
5953
6149
  }
5954
- try {
5955
- const elementURL = new URL(element.href);
5956
- return elementURL.hostname === "fonts.googleapis.com";
5957
- } catch (err) {
5958
- logInfo(`Couldn't construct ${element.href} as URL`);
5959
- return false;
6150
+ for (const pattern of ignoredCSSURLPatterns) {
6151
+ if (pattern.startsWith("^")) {
6152
+ if (url.startsWith(pattern.slice(1))) {
6153
+ return true;
6154
+ }
6155
+ } else if (pattern.endsWith("$")) {
6156
+ if (url.endsWith(pattern.slice(0, -1))) {
6157
+ return true;
6158
+ }
6159
+ } else if (url.includes(pattern)) {
6160
+ return true;
6161
+ }
5960
6162
  }
6163
+ return false;
5961
6164
  }
5962
6165
  const hostsBreakingOnSVGStyleOverride = [
5963
6166
  "account.containerstore.com",
@@ -5977,7 +6180,7 @@ function shouldManageStyle(element) {
5977
6180
  (isFirefox
5978
6181
  ? !element.href.startsWith("moz-extension://")
5979
6182
  : true) &&
5980
- !isFontsGoogleApiStyle(element))) &&
6183
+ !shouldIgnoreCSSURL(element.href))) &&
5981
6184
  !element.classList.contains("darkreader") &&
5982
6185
  !ignoredMedia.includes(element.media.toLowerCase()) &&
5983
6186
  !element.classList.contains("stylus")
@@ -5994,7 +6197,7 @@ function getManageableStyles(node, results = [], deep = true) {
5994
6197
  forEach(node.querySelectorAll(STYLE_SELECTOR), (style) =>
5995
6198
  getManageableStyles(style, results, false)
5996
6199
  );
5997
- if (deep) {
6200
+ if (deep && (node.children?.length > 0 || node.shadowRoot)) {
5998
6201
  iterateShadowHosts(node, (host) =>
5999
6202
  getManageableStyles(host.shadowRoot, results, false)
6000
6203
  );
@@ -7120,10 +7323,23 @@ function watchForStylePositions(currentStyles, update, shadowRootDiscovered) {
7120
7323
  removedStyles,
7121
7324
  movedStyles
7122
7325
  });
7326
+ const potentialHosts = new Set();
7123
7327
  additions.forEach((n) => {
7328
+ if (n.parentElement) {
7329
+ potentialHosts.add(n.parentElement);
7330
+ }
7331
+ if (n.previousElementSibling) {
7332
+ potentialHosts.add(n.previousElementSibling);
7333
+ }
7124
7334
  deepObserve(n);
7125
7335
  collectUndefinedElements(n);
7126
7336
  });
7337
+ potentialHosts.forEach((el) => {
7338
+ if (el.shadowRoot && !observedRoots.has(el)) {
7339
+ subscribeForShadowRootChanges(el);
7340
+ deepObserve(el.shadowRoot);
7341
+ }
7342
+ });
7127
7343
  additions.forEach(
7128
7344
  (node) => isCustomElement(node) && recordUndefinedElement(node)
7129
7345
  );
@@ -7237,6 +7453,14 @@ function watchForStylePositions(currentStyles, update, shadowRootDiscovered) {
7237
7453
  });
7238
7454
  document.addEventListener("__darkreader__isDefined", handleIsDefined);
7239
7455
  collectUndefinedElements(document);
7456
+ addDOMReadyListener(() => {
7457
+ forEach(document.body.children, (el) => {
7458
+ if (el.shadowRoot && !observedRoots.has(el)) {
7459
+ subscribeForShadowRootChanges(el);
7460
+ deepObserve(el.shadowRoot);
7461
+ }
7462
+ });
7463
+ });
7240
7464
  }
7241
7465
  function resetObservers() {
7242
7466
  observers.forEach((o) => o.disconnect());
@@ -7918,9 +8142,13 @@ function createOrUpdateDynamicThemeInternal(
7918
8142
  ignoredInlineSelectors = Array.isArray(fixes.ignoreInlineStyle)
7919
8143
  ? fixes.ignoreInlineStyle
7920
8144
  : [];
8145
+ setIgnoredCSSURLs(
8146
+ Array.isArray(fixes.ignoreCSSUrl) ? fixes.ignoreCSSUrl : []
8147
+ );
7921
8148
  } else {
7922
8149
  ignoredImageAnalysisSelectors = [];
7923
8150
  ignoredInlineSelectors = [];
8151
+ setIgnoredCSSURLs([]);
7924
8152
  }
7925
8153
  if (theme.immediateModify) {
7926
8154
  setIsDOMReady(() => {
package/index.d.ts CHANGED
@@ -157,6 +157,14 @@ declare namespace DarkReader {
157
157
  * who are using the Dark Reader API.
158
158
  */
159
159
  disableStyleSheetsProxy: boolean;
160
+ /**
161
+ * List of stylesheet URL patterns to ignore.
162
+ * Stylesheets matching these patterns will not be processed by Dark Reader.
163
+ * - Simple string: matches if URL contains the string (e.g., "crisp")
164
+ * - Prefix with ^: matches if URL starts with pattern (e.g., "^https://client.crisp")
165
+ * - Suffix with $: matches if URL ends with pattern (e.g., ".css$")
166
+ */
167
+ ignoreCSSUrl: string[];
160
168
  }
161
169
  }
162
170