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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Dark Reader Ltd.
3
+ Copyright (c) 2026 Dark Reader Ltd.
4
4
 
5
5
  All rights reserved.
6
6
 
package/darkreader.js 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
 
@@ -1041,54 +1041,155 @@
1041
1041
  }
1042
1042
  return null;
1043
1043
  }
1044
- function getNumbers($color) {
1044
+ const C_0 = "0".charCodeAt(0);
1045
+ const C_9 = "9".charCodeAt(0);
1046
+ const C_e = "e".charCodeAt(0);
1047
+ const C_DOT = ".".charCodeAt(0);
1048
+ const C_PLUS = "+".charCodeAt(0);
1049
+ const C_MINUS = "-".charCodeAt(0);
1050
+ const C_SPACE = " ".charCodeAt(0);
1051
+ const C_COMMA = ",".charCodeAt(0);
1052
+ const C_SLASH = "/".charCodeAt(0);
1053
+ const C_PERCENT = "%".charCodeAt(0);
1054
+ function getNumbersFromString(input, range, units) {
1045
1055
  const numbers = [];
1046
- let prevPos = 0;
1047
- let isMining = false;
1048
- const startIndex = $color.indexOf("(");
1049
- $color = $color.substring(startIndex + 1, $color.length - 1);
1050
- for (let i = 0; i < $color.length; i++) {
1051
- const c = $color[i];
1052
- if ((c >= "0" && c <= "9") || c === "." || c === "+" || c === "-") {
1053
- isMining = true;
1054
- } else if (isMining && (c === " " || c === "," || c === "/")) {
1055
- numbers.push($color.substring(prevPos, i));
1056
- isMining = false;
1057
- prevPos = i + 1;
1058
- } else if (!isMining) {
1059
- prevPos = i + 1;
1060
- }
1061
- }
1062
- if (isMining) {
1063
- numbers.push($color.substring(prevPos, $color.length));
1056
+ const searchStart = input.indexOf("(") + 1;
1057
+ const searchEnd = input.length - 1;
1058
+ let numStart = -1;
1059
+ let unitStart = -1;
1060
+ const push = (matchEnd) => {
1061
+ const numEnd = unitStart > -1 ? unitStart : matchEnd;
1062
+ const $num = input.slice(numStart, numEnd);
1063
+ let n = parseFloat($num);
1064
+ const r = range[numbers.length];
1065
+ if (unitStart > -1) {
1066
+ const unit = input.slice(unitStart, matchEnd);
1067
+ const u = units[unit];
1068
+ if (u != null) {
1069
+ n *= r / u;
1070
+ }
1071
+ }
1072
+ if (r > 1) {
1073
+ n = Math.round(n);
1074
+ }
1075
+ numbers.push(n);
1076
+ numStart = -1;
1077
+ unitStart = -1;
1078
+ };
1079
+ for (let i = searchStart; i < searchEnd; i++) {
1080
+ const c = input.charCodeAt(i);
1081
+ const isNumChar =
1082
+ (c >= C_0 && c <= C_9) ||
1083
+ c === C_DOT ||
1084
+ c === C_PLUS ||
1085
+ c === C_MINUS ||
1086
+ c === C_e;
1087
+ const isDelimiter = c === C_SPACE || c === C_COMMA || c === C_SLASH;
1088
+ if (isNumChar) {
1089
+ if (numStart === -1) {
1090
+ numStart = i;
1091
+ }
1092
+ } else if (numStart > -1) {
1093
+ if (isDelimiter) {
1094
+ push(i);
1095
+ } else if (unitStart === -1) {
1096
+ unitStart = i;
1097
+ }
1098
+ }
1099
+ }
1100
+ if (numStart > -1) {
1101
+ push(searchEnd);
1064
1102
  }
1065
1103
  return numbers;
1066
1104
  }
1067
- function getNumbersFromString(str, range, units) {
1068
- const raw = getNumbers(str);
1069
- const unitsList = Object.entries(units);
1070
- const numbers = raw
1071
- .map((r) => r.trim())
1072
- .map((r, i) => {
1073
- let n;
1074
- const unit = unitsList.find(([u]) => r.endsWith(u));
1075
- if (unit) {
1076
- n =
1077
- (parseFloat(r.substring(0, r.length - unit[0].length)) /
1078
- unit[1]) *
1079
- range[i];
1105
+ const rgbRange = [255, 255, 255, 1];
1106
+ const rgbUnits = {"%": 100};
1107
+ function getRGBValues(input) {
1108
+ const CHAR_CODE_0 = 48;
1109
+ const length = input.length;
1110
+ let i = 0;
1111
+ let digitsCount = 0;
1112
+ let digitSequence = false;
1113
+ let floatDigitsCount = -1;
1114
+ let delimiter = C_SPACE;
1115
+ let channel = -1;
1116
+ let result = null;
1117
+ while (i < length) {
1118
+ const c = input.charCodeAt(i);
1119
+ if ((c >= C_0 && c <= C_9) || c === C_DOT) {
1120
+ if (!digitSequence) {
1121
+ digitSequence = true;
1122
+ digitsCount = 0;
1123
+ floatDigitsCount = -1;
1124
+ channel++;
1125
+ if (channel === 3 && result) {
1126
+ result[3] = 0;
1127
+ }
1128
+ if (channel > 3) {
1129
+ return null;
1130
+ }
1131
+ }
1132
+ if (c === C_DOT) {
1133
+ if (floatDigitsCount > 0) {
1134
+ return null;
1135
+ }
1136
+ floatDigitsCount = 0;
1080
1137
  } else {
1081
- n = parseFloat(r);
1138
+ const d = c - CHAR_CODE_0;
1139
+ if (!result) {
1140
+ result = [0, 0, 0, 1];
1141
+ }
1142
+ if (floatDigitsCount > -1) {
1143
+ floatDigitsCount++;
1144
+ result[channel] += d / 10 ** floatDigitsCount;
1145
+ } else {
1146
+ digitsCount++;
1147
+ if (digitsCount > 3) {
1148
+ return null;
1149
+ }
1150
+ result[channel] = result[channel] * 10 + d;
1151
+ }
1082
1152
  }
1083
- if (range[i] > 1) {
1084
- return Math.round(n);
1153
+ } else if (c === C_PERCENT) {
1154
+ if (
1155
+ channel < 0 ||
1156
+ channel > 3 ||
1157
+ delimiter !== C_SPACE ||
1158
+ !result
1159
+ ) {
1160
+ return null;
1085
1161
  }
1086
- return n;
1087
- });
1088
- return numbers;
1162
+ result[channel] =
1163
+ channel < 3
1164
+ ? Math.round((result[channel] * 255) / 100)
1165
+ : result[channel] / 100;
1166
+ digitSequence = false;
1167
+ } else {
1168
+ digitSequence = false;
1169
+ if (c === C_SPACE) {
1170
+ if (channel === 0) {
1171
+ delimiter = c;
1172
+ }
1173
+ } else if (c === C_COMMA) {
1174
+ if (channel === -1) {
1175
+ return null;
1176
+ }
1177
+ delimiter = C_COMMA;
1178
+ } else if (c === C_SLASH) {
1179
+ if (channel !== 2 || delimiter !== C_SPACE) {
1180
+ return null;
1181
+ }
1182
+ } else {
1183
+ return null;
1184
+ }
1185
+ }
1186
+ i++;
1187
+ }
1188
+ if (channel < 2 || channel > 3) {
1189
+ return null;
1190
+ }
1191
+ return result;
1089
1192
  }
1090
- const rgbRange = [255, 255, 255, 1];
1091
- const rgbUnits = {"%": 100};
1092
1193
  function parseRGB($rgb) {
1093
1194
  const [r, g, b, a = 1] = getNumbersFromString($rgb, rgbRange, rgbUnits);
1094
1195
  if (r == null || g == null || b == null || a == null) {
@@ -1105,29 +1206,48 @@
1105
1206
  }
1106
1207
  return hslToRGB({h, s, l, a});
1107
1208
  }
1209
+ const C_A = "A".charCodeAt(0);
1210
+ const C_F = "F".charCodeAt(0);
1211
+ const C_a = "a".charCodeAt(0);
1212
+ const C_f = "f".charCodeAt(0);
1108
1213
  function parseHex($hex) {
1109
- const h = $hex.substring(1);
1110
- switch (h.length) {
1111
- case 3:
1112
- case 4: {
1113
- const [r, g, b] = [0, 1, 2].map((i) =>
1114
- parseInt(`${h[i]}${h[i]}`, 16)
1115
- );
1116
- const a =
1117
- h.length === 3 ? 1 : parseInt(`${h[3]}${h[3]}`, 16) / 255;
1118
- return {r, g, b, a};
1119
- }
1120
- case 6:
1121
- case 8: {
1122
- const [r, g, b] = [0, 2, 4].map((i) =>
1123
- parseInt(h.substring(i, i + 2), 16)
1124
- );
1125
- const a =
1126
- h.length === 6 ? 1 : parseInt(h.substring(6, 8), 16) / 255;
1127
- return {r, g, b, a};
1214
+ const length = $hex.length;
1215
+ const digitCount = length - 1;
1216
+ const isShort = digitCount === 3 || digitCount === 4;
1217
+ const isLong = digitCount === 6 || digitCount === 8;
1218
+ if (!isShort && !isLong) {
1219
+ return null;
1220
+ }
1221
+ const hex = (i) => {
1222
+ const c = $hex.charCodeAt(i);
1223
+ if (c >= C_A && c <= C_F) {
1224
+ return c + 10 - C_A;
1225
+ }
1226
+ if (c >= C_a && c <= C_f) {
1227
+ return c + 10 - C_a;
1228
+ }
1229
+ return c - C_0;
1230
+ };
1231
+ let r;
1232
+ let g;
1233
+ let b;
1234
+ let a = 1;
1235
+ if (isShort) {
1236
+ r = hex(1) * 17;
1237
+ g = hex(2) * 17;
1238
+ b = hex(3) * 17;
1239
+ if (digitCount === 4) {
1240
+ a = (hex(4) * 17) / 255;
1241
+ }
1242
+ } else {
1243
+ r = hex(1) * 16 + hex(2);
1244
+ g = hex(3) * 16 + hex(4);
1245
+ b = hex(5) * 16 + hex(6);
1246
+ if (digitCount === 8) {
1247
+ a = (hex(7) * 16 + hex(8)) / 255;
1128
1248
  }
1129
1249
  }
1130
- return null;
1250
+ return {r, g, b, a};
1131
1251
  }
1132
1252
  function getColorByName($color) {
1133
1253
  const n = knownColors.get($color);
@@ -1921,13 +2041,6 @@
1921
2041
  })
1922
2042
  : null;
1923
2043
  function iterateCSSDeclarations(style, iterate) {
1924
- forEach(style, (property) => {
1925
- const value = style.getPropertyValue(property).trim();
1926
- if (!value) {
1927
- return;
1928
- }
1929
- iterate(property, value);
1930
- });
1931
2044
  const cssText = style.cssText;
1932
2045
  if (cssText.includes("var(")) {
1933
2046
  if (isSafari) {
@@ -1961,6 +2074,13 @@
1961
2074
  ) {
1962
2075
  handleEmptyShorthand("border", style, iterate);
1963
2076
  }
2077
+ forEach(style, (property) => {
2078
+ const value = style.getPropertyValue(property).trim();
2079
+ if (!value) {
2080
+ return;
2081
+ }
2082
+ iterate(property, value);
2083
+ });
1964
2084
  }
1965
2085
  function handleEmptyShorthand(shorthand, style, iterate) {
1966
2086
  const parentRule = style.parentRule;
@@ -2759,14 +2879,62 @@
2759
2879
  async function getImageDetails(url) {
2760
2880
  return new Promise(async (resolve, reject) => {
2761
2881
  try {
2762
- const dataURL = url.startsWith("data:")
2882
+ let dataURL = url.startsWith("data:")
2763
2883
  ? url
2764
2884
  : await getDataURL(url);
2765
2885
  const blob =
2766
2886
  tryConvertDataURLToBlobSync(dataURL) ??
2767
2887
  (await loadAsBlob(url));
2768
2888
  let image;
2889
+ let useViewBox = false;
2769
2890
  if (dataURL.startsWith("data:image/svg+xml")) {
2891
+ const commaIndex = dataURL.indexOf(",");
2892
+ if (commaIndex >= 0) {
2893
+ let svgText = dataURL.slice(commaIndex + 1);
2894
+ const encoding = dataURL
2895
+ .slice(0, commaIndex)
2896
+ .split(";")[1];
2897
+ if (encoding === "base64") {
2898
+ if (svgText.includes("%")) {
2899
+ svgText = decodeURIComponent(svgText);
2900
+ }
2901
+ svgText = atob(svgText);
2902
+ } else if (svgText.startsWith("%3c")) {
2903
+ svgText = decodeURIComponent(svgText);
2904
+ }
2905
+ if (svgText.startsWith("<svg ")) {
2906
+ const closingIndex = svgText.indexOf(">");
2907
+ const svgOpening = svgText
2908
+ .slice(0, closingIndex + 1)
2909
+ .toLocaleLowerCase();
2910
+ if (
2911
+ svgOpening.includes("viewbox=") &&
2912
+ !svgOpening.includes("width=") &&
2913
+ !svgOpening.includes("height=")
2914
+ ) {
2915
+ useViewBox = true;
2916
+ const viewboxIndex =
2917
+ svgOpening.indexOf("viewbox=");
2918
+ const quote = svgOpening[viewboxIndex + 8];
2919
+ const viewboxCloseIndex = svgOpening.indexOf(
2920
+ quote,
2921
+ viewboxIndex + 9
2922
+ );
2923
+ const viewBox = svgOpening
2924
+ .slice(viewboxIndex + 9, viewboxCloseIndex)
2925
+ .split(" ")
2926
+ .map((x) => parseFloat(x));
2927
+ if (
2928
+ viewBox.length === 4 &&
2929
+ !viewBox.some((x) => isNaN(x))
2930
+ ) {
2931
+ const width = viewBox[2] - viewBox[0];
2932
+ const height = viewBox[3] - viewBox[1];
2933
+ dataURL = `data:image/svg+xml;base64,${btoa(`<svg width="${width}" height="${height}" ${svgText.slice(5)}`)}`;
2934
+ }
2935
+ }
2936
+ }
2937
+ }
2770
2938
  image = await loadImage(dataURL);
2771
2939
  } else {
2772
2940
  image =
@@ -2780,6 +2948,7 @@
2780
2948
  dataURL: analysis.isLarge ? "" : dataURL,
2781
2949
  width: image.width,
2782
2950
  height: image.height,
2951
+ useViewBox,
2783
2952
  ...analysis
2784
2953
  });
2785
2954
  });
@@ -2967,13 +3136,16 @@
2967
3136
  }
2968
3137
  document.addEventListener("securitypolicyviolation", onCSPError);
2969
3138
  const objectURLs = new Set();
2970
- function getFilteredImageURL({dataURL, width, height}, theme) {
3139
+ function getFilteredImageURL({dataURL, width, height, useViewBox}, theme) {
2971
3140
  if (dataURL.startsWith("data:image/svg+xml")) {
2972
3141
  dataURL = escapeXML(dataURL);
2973
3142
  }
2974
3143
  const matrix = getSVGFilterMatrixValue(theme);
3144
+ const size = useViewBox
3145
+ ? `viewBox="0 0 ${width} ${height}"`
3146
+ : `width="${width}" height="${height}"`;
2975
3147
  const svg = [
2976
- `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${width}" height="${height}">`,
3148
+ `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" ${size}>`,
2977
3149
  "<defs>",
2978
3150
  '<filter id="darkreader-image-filter">',
2979
3151
  `<feColorMatrix type="matrix" values="${matrix}" />`,
@@ -3016,7 +3188,11 @@
3016
3188
  if (encoding !== "base64" || !mediaType) {
3017
3189
  return null;
3018
3190
  }
3019
- const characters = atob(dataURL.substring(commaIndex + 1));
3191
+ let base64Content = dataURL.substring(commaIndex + 1);
3192
+ if (base64Content.includes("%")) {
3193
+ base64Content = decodeURIComponent(base64Content);
3194
+ }
3195
+ const characters = atob(base64Content);
3020
3196
  const bytes = new Uint8Array(characters.length);
3021
3197
  for (let i = 0; i < characters.length; i++) {
3022
3198
  bytes[i] = characters.charCodeAt(i);
@@ -3053,6 +3229,12 @@
3053
3229
  function getPriority(ruleStyle, property) {
3054
3230
  return Boolean(ruleStyle && ruleStyle.getPropertyPriority(property));
3055
3231
  }
3232
+ const bgPropsToCopy = [
3233
+ "background-clip",
3234
+ "background-position",
3235
+ "background-repeat",
3236
+ "background-size"
3237
+ ];
3056
3238
  function getModifiableCSSDeclaration(
3057
3239
  property,
3058
3240
  value,
@@ -3123,6 +3305,8 @@
3123
3305
  );
3124
3306
  } else if (property.includes("shadow")) {
3125
3307
  modifier = getShadowModifier(value);
3308
+ } else if (bgPropsToCopy.includes(property) && value !== "initial") {
3309
+ modifier = value;
3126
3310
  }
3127
3311
  if (!modifier) {
3128
3312
  return null;
@@ -4234,9 +4418,7 @@
4234
4418
  }
4235
4419
  this.definedVars.add(varName);
4236
4420
  const isColor = Boolean(
4237
- value.match(rawRGBSpaceRegex) ||
4238
- value.match(rawRGBCommaRegex) ||
4239
- parseColorWithCache(value)
4421
+ getRGBValues(value) || parseColorWithCache(value)
4240
4422
  );
4241
4423
  if (isColor) {
4242
4424
  this.unknownColorVars.add(varName);
@@ -4575,16 +4757,13 @@
4575
4757
  function isTextColorProperty(property) {
4576
4758
  return textColorProps.includes(property);
4577
4759
  }
4578
- const rawRGBSpaceRegex =
4579
- /^(\d{1,3})\s+(\d{1,3})\s+(\d{1,3})\s*(\/\s*\d+\.?\d*)?$/;
4580
- const rawRGBCommaRegex = /^(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})$/;
4581
4760
  function parseRawColorValue(input) {
4582
- const match =
4583
- input.match(rawRGBSpaceRegex) ?? input.match(rawRGBCommaRegex);
4584
- if (match) {
4585
- const color = match[4]
4586
- ? `rgb(${match[1]} ${match[2]} ${match[3]} / ${match[4]})`
4587
- : `rgb(${match[1]}, ${match[2]}, ${match[3]})`;
4761
+ const v = getRGBValues(input);
4762
+ if (v) {
4763
+ const color =
4764
+ v[3] < 1
4765
+ ? `rgb(${v[0]} ${v[1]} ${v[2]} / ${v[3]})`
4766
+ : `rgb(${v[0]} ${v[1]} ${v[2]})`;
4588
4767
  return {isRaw: true, color};
4589
4768
  }
4590
4769
  return {isRaw: false, color: input};
@@ -4597,7 +4776,9 @@
4597
4776
  if (isRaw) {
4598
4777
  const outputInRGB = parseColorWithCache(outputColor);
4599
4778
  return outputInRGB
4600
- ? `${outputInRGB.r}, ${outputInRGB.g}, ${outputInRGB.b}`
4779
+ ? Number.isNaN(outputInRGB.a) || outputInRGB.a === 1
4780
+ ? `${outputInRGB.r}, ${outputInRGB.g}, ${outputInRGB.b}`
4781
+ : `${outputInRGB.r}, ${outputInRGB.g}, ${outputInRGB.b}, ${outputInRGB.a}`
4601
4782
  : outputColor;
4602
4783
  }
4603
4784
  return outputColor;
@@ -5629,8 +5810,8 @@
5629
5810
  }
5630
5811
  const shouldAnalyze = Boolean(
5631
5812
  svg &&
5632
- (svg.getAttribute("class")?.includes("logo") ||
5633
- svg.parentElement?.getAttribute("class")?.includes("logo"))
5813
+ (svg.getAttribute("class")?.includes("logo") ||
5814
+ svg.parentElement?.getAttribute("class")?.includes("logo"))
5634
5815
  );
5635
5816
  svgAnalysisConditionCache.set(svg, shouldAnalyze);
5636
5817
  return shouldAnalyze;
@@ -5673,6 +5854,7 @@
5673
5854
  svgNodesRoots.set(svgElement, root);
5674
5855
  return root;
5675
5856
  }
5857
+ const inlineStringValueCache = new Map();
5676
5858
  function overrideInlineStyle(
5677
5859
  element,
5678
5860
  theme,
@@ -5700,6 +5882,13 @@
5700
5882
  }
5701
5883
  const unsetProps = new Set(Object.keys(overrides));
5702
5884
  function setCustomProp(targetCSSProp, modifierCSSProp, cssVal) {
5885
+ const cachedStringValue = inlineStringValueCache
5886
+ .get(modifierCSSProp)
5887
+ ?.get(cssVal);
5888
+ if (cachedStringValue) {
5889
+ setStaticValue(cachedStringValue);
5890
+ return;
5891
+ }
5703
5892
  const mod = getModifiableCSSDeclaration(
5704
5893
  modifierCSSProp,
5705
5894
  cssVal,
@@ -5766,6 +5955,10 @@
5766
5955
  typeof mod.value === "function" ? mod.value(theme) : mod.value;
5767
5956
  if (typeof value === "string") {
5768
5957
  setStaticValue(value);
5958
+ if (!inlineStringValueCache.has(modifierCSSProp)) {
5959
+ inlineStringValueCache.set(modifierCSSProp, new Map());
5960
+ }
5961
+ inlineStringValueCache.get(modifierCSSProp).set(cssVal, value);
5769
5962
  } else if (value instanceof Promise) {
5770
5963
  setAsyncValue(value, cssVal);
5771
5964
  } else if (typeof value === "object") {
@@ -5852,13 +6045,16 @@
5852
6045
  value.match(/^[0-9a-f]{6}$/i)
5853
6046
  ) {
5854
6047
  value = `#${value}`;
6048
+ } else if (value.match(/^#?[0-9a-f]{4}$/i)) {
6049
+ const hex = value.startsWith("#") ? value.substring(1) : value;
6050
+ value = `#${hex}00`;
5855
6051
  }
5856
6052
  setCustomProp("color", "color", value);
5857
6053
  }
5858
6054
  if (isSVGElement) {
5859
6055
  if (element.hasAttribute("fill")) {
5860
6056
  const value = element.getAttribute("fill");
5861
- if (value !== "none") {
6057
+ if (value !== "none" && value !== "currentColor") {
5862
6058
  if (!(element instanceof SVGTextElement)) {
5863
6059
  const handleSVGElement = () => {
5864
6060
  let isSVGSmall = false;
@@ -6141,17 +6337,28 @@
6141
6337
  }
6142
6338
 
6143
6339
  const STYLE_SELECTOR = 'style, link[rel*="stylesheet" i]:not([disabled])';
6144
- function isFontsGoogleApiStyle(element) {
6145
- if (!element.href) {
6340
+ let ignoredCSSURLPatterns = [];
6341
+ function setIgnoredCSSURLs(patterns) {
6342
+ ignoredCSSURLPatterns = patterns || [];
6343
+ }
6344
+ function shouldIgnoreCSSURL(url) {
6345
+ if (!url || ignoredCSSURLPatterns.length === 0) {
6146
6346
  return false;
6147
6347
  }
6148
- try {
6149
- const elementURL = new URL(element.href);
6150
- return elementURL.hostname === "fonts.googleapis.com";
6151
- } catch (err) {
6152
- logInfo(`Couldn't construct ${element.href} as URL`);
6153
- return false;
6348
+ for (const pattern of ignoredCSSURLPatterns) {
6349
+ if (pattern.startsWith("^")) {
6350
+ if (url.startsWith(pattern.slice(1))) {
6351
+ return true;
6352
+ }
6353
+ } else if (pattern.endsWith("$")) {
6354
+ if (url.endsWith(pattern.slice(0, -1))) {
6355
+ return true;
6356
+ }
6357
+ } else if (url.includes(pattern)) {
6358
+ return true;
6359
+ }
6154
6360
  }
6361
+ return false;
6155
6362
  }
6156
6363
  const hostsBreakingOnSVGStyleOverride = [
6157
6364
  "account.containerstore.com",
@@ -6173,7 +6380,7 @@
6173
6380
  (isFirefox
6174
6381
  ? !element.href.startsWith("moz-extension://")
6175
6382
  : true) &&
6176
- !isFontsGoogleApiStyle(element))) &&
6383
+ !shouldIgnoreCSSURL(element.href))) &&
6177
6384
  !element.classList.contains("darkreader") &&
6178
6385
  !ignoredMedia.includes(element.media.toLowerCase()) &&
6179
6386
  !element.classList.contains("stylus")
@@ -6190,7 +6397,7 @@
6190
6397
  forEach(node.querySelectorAll(STYLE_SELECTOR), (style) =>
6191
6398
  getManageableStyles(style, results, false)
6192
6399
  );
6193
- if (deep) {
6400
+ if (deep && (node.children?.length > 0 || node.shadowRoot)) {
6194
6401
  iterateShadowHosts(node, (host) =>
6195
6402
  getManageableStyles(host.shadowRoot, results, false)
6196
6403
  );
@@ -7358,10 +7565,23 @@
7358
7565
  removedStyles,
7359
7566
  movedStyles
7360
7567
  });
7568
+ const potentialHosts = new Set();
7361
7569
  additions.forEach((n) => {
7570
+ if (n.parentElement) {
7571
+ potentialHosts.add(n.parentElement);
7572
+ }
7573
+ if (n.previousElementSibling) {
7574
+ potentialHosts.add(n.previousElementSibling);
7575
+ }
7362
7576
  deepObserve(n);
7363
7577
  collectUndefinedElements(n);
7364
7578
  });
7579
+ potentialHosts.forEach((el) => {
7580
+ if (el.shadowRoot && !observedRoots.has(el)) {
7581
+ subscribeForShadowRootChanges(el);
7582
+ deepObserve(el.shadowRoot);
7583
+ }
7584
+ });
7365
7585
  additions.forEach(
7366
7586
  (node) => isCustomElement(node) && recordUndefinedElement(node)
7367
7587
  );
@@ -7475,6 +7695,14 @@
7475
7695
  });
7476
7696
  document.addEventListener("__darkreader__isDefined", handleIsDefined);
7477
7697
  collectUndefinedElements(document);
7698
+ addDOMReadyListener(() => {
7699
+ forEach(document.body.children, (el) => {
7700
+ if (el.shadowRoot && !observedRoots.has(el)) {
7701
+ subscribeForShadowRootChanges(el);
7702
+ deepObserve(el.shadowRoot);
7703
+ }
7704
+ });
7705
+ });
7478
7706
  }
7479
7707
  function resetObservers() {
7480
7708
  observers.forEach((o) => o.disconnect());
@@ -8182,9 +8410,13 @@
8182
8410
  ignoredInlineSelectors = Array.isArray(fixes.ignoreInlineStyle)
8183
8411
  ? fixes.ignoreInlineStyle
8184
8412
  : [];
8413
+ setIgnoredCSSURLs(
8414
+ Array.isArray(fixes.ignoreCSSUrl) ? fixes.ignoreCSSUrl : []
8415
+ );
8185
8416
  } else {
8186
8417
  ignoredImageAnalysisSelectors = [];
8187
8418
  ignoredInlineSelectors = [];
8419
+ setIgnoredCSSURLs([]);
8188
8420
  }
8189
8421
  if (theme.immediateModify) {
8190
8422
  setIsDOMReady(() => {