diff --git a/src/scripts/darkreader.js b/src/scripts/darkreader.js
index 080a68869..0daefcf2f 100644
--- a/src/scripts/darkreader.js
+++ b/src/scripts/darkreader.js
@@ -1,5 +1,5 @@
/**
- * Dark Reader v4.9.86
+ * Dark Reader v4.9.100
* https://darkreader.org/
*/
@@ -7,12 +7,12 @@
typeof exports === "object" && typeof module !== "undefined"
? factory(exports)
: typeof define === "function" && define.amd
- ? define(["exports"], factory)
- : ((global =
+ ? define(["exports"], factory)
+ : ((global =
typeof globalThis !== "undefined"
? globalThis
: global || self),
- factory((global.DarkReader = {})));
+ factory((global.DarkReader = {})));
})(this, function (exports) {
"use strict";
@@ -97,17 +97,17 @@
const isNavigatorDefined = typeof navigator !== "undefined";
const userAgent = isNavigatorDefined
? navigator.userAgentData &&
- Array.isArray(navigator.userAgentData.brands)
+ Array.isArray(navigator.userAgentData.brands)
? navigator.userAgentData.brands
- .map(
- (brand) => `${brand.brand.toLowerCase()} ${brand.version}`
- )
- .join(" ")
+ .map(
+ (brand) => `${brand.brand.toLowerCase()} ${brand.version}`
+ )
+ .join(" ")
: navigator.userAgent.toLowerCase()
: "some useragent";
const platform = isNavigatorDefined
? navigator.userAgentData &&
- typeof navigator.userAgentData.platform === "string"
+ typeof navigator.userAgentData.platform === "string"
? navigator.userAgentData.platform.toLowerCase()
: navigator.platform.toLowerCase()
: "some platform";
@@ -170,9 +170,11 @@
})();
async function getOKResponse(url, mimeType, origin) {
+ const credentials =
+ origin && url.startsWith(`${origin}/`) ? undefined : "omit";
const response = await fetch(url, {
cache: "force-cache",
- credentials: "omit",
+ credentials,
referrer: origin
});
if (
@@ -253,9 +255,9 @@
const messageListeners = new Set();
async function sendMessage(...args) {
if (args[0] && args[0].type === MessageTypeCStoBG.FETCH) {
- const { id } = args[0];
+ const {id} = args[0];
try {
- const { url, responseType } = args[0].data;
+ const {url, responseType} = args[0].data;
const response = await callFetchMethod(url);
let text;
if (responseType === "data-url") {
@@ -346,8 +348,8 @@
fontFamily: isMacOS
? "Helvetica Neue"
: isWindows
- ? "Segoe UI"
- : "Open Sans",
+ ? "Segoe UI"
+ : "Open Sans",
textStroke: 0,
engine: ThemeEngine.dynamicTheme,
stylesheet: "",
@@ -355,20 +357,35 @@
darkSchemeTextColor: DEFAULT_COLORS.darkScheme.text,
lightSchemeBackgroundColor: DEFAULT_COLORS.lightScheme.background,
lightSchemeTextColor: DEFAULT_COLORS.lightScheme.text,
- scrollbarColor: isMacOS ? "" : "auto",
+ scrollbarColor: "",
selectionColor: "auto",
styleSystemControls: !isCSSColorSchemePropSupported,
lightColorScheme: "Default",
darkColorScheme: "Default",
immediateModify: false
};
+ const filterModeSites = [
+ "*.officeapps.live.com",
+ "*.sharepoint.com",
+ "docs.google.com",
+ "onedrive.live.com"
+ ];
({
schemeVersion: 0,
enabled: true,
fetchNews: true,
theme: DEFAULT_THEME,
presets: [],
- customThemes: [],
+ customThemes: filterModeSites.map((url) => {
+ const engine = isChromium
+ ? ThemeEngine.svgFilter
+ : ThemeEngine.cssFilter;
+ return {
+ url: [url],
+ theme: {...DEFAULT_THEME, engine},
+ builtIn: true
+ };
+ }),
enabledByDefault: true,
enabledFor: [],
disabledFor: [],
@@ -389,12 +406,218 @@
longitude: null
},
previewNewDesign: false,
+ previewNewestDesign: false,
enableForPDF: true,
enableForProtectedPages: false,
enableContextMenus: false,
- detectDarkTheme: false
+ detectDarkTheme: true
});
+ function getMatches(regex, input, group = 0) {
+ const matches = [];
+ let m;
+ while ((m = regex.exec(input))) {
+ matches.push(m[group]);
+ }
+ return matches;
+ }
+ function getMatchesWithOffsets(regex, input, group = 0) {
+ const matches = [];
+ let m;
+ while ((m = regex.exec(input))) {
+ matches.push({text: m[group], offset: m.index});
+ }
+ return matches;
+ }
+ function getHashCode(text) {
+ const len = text.length;
+ let hash = 0;
+ for (let i = 0; i < len; i++) {
+ const c = text.charCodeAt(i);
+ hash = ((hash << 5) - hash + c) & 4294967295;
+ }
+ return hash;
+ }
+ function escapeRegExpSpecialChars(input) {
+ return input.replaceAll(/[\^$.*+?\(\)\[\]{}|\-\\]/g, "\\$&");
+ }
+ function getParenthesesRange(input, searchStartIndex = 0) {
+ return getOpenCloseRange(input, searchStartIndex, "(", ")", []);
+ }
+ function getOpenCloseRange(
+ input,
+ searchStartIndex,
+ openToken,
+ closeToken,
+ excludeRanges
+ ) {
+ let indexOf;
+ if (excludeRanges.length === 0) {
+ indexOf = (token, pos) => input.indexOf(token, pos);
+ } else {
+ indexOf = (token, pos) =>
+ indexOfExcluding(input, token, pos, excludeRanges);
+ }
+ const {length} = input;
+ let depth = 0;
+ let firstOpenIndex = -1;
+ for (let i = searchStartIndex; i < length; i++) {
+ if (depth === 0) {
+ const openIndex = indexOf(openToken, i);
+ if (openIndex < 0) {
+ break;
+ }
+ firstOpenIndex = openIndex;
+ depth++;
+ i = openIndex;
+ } else {
+ const closeIndex = indexOf(closeToken, i);
+ if (closeIndex < 0) {
+ break;
+ }
+ const openIndex = indexOf(openToken, i);
+ if (openIndex < 0 || closeIndex <= openIndex) {
+ depth--;
+ if (depth === 0) {
+ return {start: firstOpenIndex, end: closeIndex + 1};
+ }
+ i = closeIndex;
+ } else {
+ depth++;
+ i = openIndex;
+ }
+ }
+ }
+ return null;
+ }
+ function indexOfExcluding(input, search, position, excludeRanges) {
+ const i = input.indexOf(search, position);
+ const exclusion = excludeRanges.find((r) => i >= r.start && i < r.end);
+ if (exclusion) {
+ return indexOfExcluding(
+ input,
+ search,
+ exclusion.end,
+ excludeRanges
+ );
+ }
+ return i;
+ }
+ function splitExcluding(input, separator, excludeRanges) {
+ const parts = [];
+ let commaIndex = -1;
+ let currIndex = 0;
+ while (
+ (commaIndex = indexOfExcluding(
+ input,
+ separator,
+ currIndex,
+ excludeRanges
+ )) >= 0
+ ) {
+ parts.push(input.substring(currIndex, commaIndex).trim());
+ currIndex = commaIndex + 1;
+ }
+ parts.push(input.substring(currIndex).trim());
+ return parts;
+ }
+
+ let anchor;
+ const parsedURLCache = new Map();
+ function fixBaseURL($url) {
+ if (!anchor) {
+ anchor = document.createElement("a");
+ }
+ anchor.href = $url;
+ return anchor.href;
+ }
+ function parseURL($url, $base = null) {
+ const key = `${$url}${$base ? `;${$base}` : ""}`;
+ if (parsedURLCache.has(key)) {
+ return parsedURLCache.get(key);
+ }
+ if ($base) {
+ const parsedURL = new URL($url, fixBaseURL($base));
+ parsedURLCache.set(key, parsedURL);
+ return parsedURL;
+ }
+ const parsedURL = new URL(fixBaseURL($url));
+ parsedURLCache.set($url, parsedURL);
+ return parsedURL;
+ }
+ function getAbsoluteURL($base, $relative) {
+ if ($relative.match(/^data\\?\:/)) {
+ return $relative;
+ }
+ if (/^\/\//.test($relative)) {
+ return `${location.protocol}${$relative}`;
+ }
+ const b = parseURL($base);
+ const a = parseURL($relative, b.href);
+ return a.href;
+ }
+ function isRelativeHrefOnAbsolutePath(href) {
+ if (href.startsWith("data:")) {
+ return true;
+ }
+ const url = parseURL(href);
+ if (url.protocol !== location.protocol) {
+ return false;
+ }
+ if (url.hostname !== location.hostname) {
+ return false;
+ }
+ if (url.port !== location.port) {
+ return false;
+ }
+ return url.pathname === location.pathname;
+ }
+
+ const excludedSelectors = [
+ "pre",
+ "pre *",
+ "code",
+ '[aria-hidden="true"]',
+ '[class*="fa-"]',
+ ".fa",
+ ".fab",
+ ".fad",
+ ".fal",
+ ".far",
+ ".fas",
+ ".fass",
+ ".fasr",
+ ".fat",
+ ".icofont",
+ '[style*="font-"]',
+ '[class*="icon"]',
+ '[class*="Icon"]',
+ '[class*="symbol"]',
+ '[class*="Symbol"]',
+ ".glyphicon",
+ '[class*="material-symbol"]',
+ '[class*="material-icon"]',
+ "mu",
+ '[class*="mu-"]',
+ ".typcn",
+ '[class*="vjs-"]'
+ ];
+ function createTextStyle(config) {
+ const lines = [];
+ lines.push(`*:not(${excludedSelectors.join(", ")}) {`);
+ if (config.useFont && config.fontFamily) {
+ lines.push(` font-family: ${config.fontFamily} !important;`);
+ }
+ if (config.textStroke > 0) {
+ lines.push(
+ ` -webkit-text-stroke: ${config.textStroke}px !important;`
+ );
+ lines.push(` text-stroke: ${config.textStroke}px !important;`);
+ }
+ lines.push("}");
+ return lines.join("\n");
+ }
+
function isArrayLike(items) {
return items.length != null;
}
@@ -420,1766 +643,1573 @@
return results;
}
- function logInfo(...args) { }
- function logWarn(...args) { }
-
- function throttle(callback) {
- let pending = false;
- let frameId = null;
- let lastArgs;
- const throttled = (...args) => {
- lastArgs = args;
- if (frameId) {
- pending = true;
- } else {
- callback(...lastArgs);
- frameId = requestAnimationFrame(() => {
- frameId = null;
- if (pending) {
- callback(...lastArgs);
- pending = false;
- }
- });
- }
- };
- const cancel = () => {
- cancelAnimationFrame(frameId);
- pending = false;
- frameId = null;
- };
- return Object.assign(throttled, { cancel });
+ function scale(x, inLow, inHigh, outLow, outHigh) {
+ return ((x - inLow) * (outHigh - outLow)) / (inHigh - inLow) + outLow;
}
- function createAsyncTasksQueue() {
- const tasks = [];
- let frameId = null;
- function runTasks() {
- let task;
- while ((task = tasks.shift())) {
- task();
+ function clamp(x, min, max) {
+ return Math.min(max, Math.max(min, x));
+ }
+ function multiplyMatrices(m1, m2) {
+ const result = [];
+ for (let i = 0, len = m1.length; i < len; i++) {
+ result[i] = [];
+ for (let j = 0, len2 = m2[0].length; j < len2; j++) {
+ let sum = 0;
+ for (let k = 0, len3 = m1[0].length; k < len3; k++) {
+ sum += m1[i][k] * m2[k][j];
+ }
+ result[i][j] = sum;
}
- frameId = null;
}
- function add(task) {
- tasks.push(task);
- if (!frameId) {
- frameId = requestAnimationFrame(runTasks);
- }
+ return result;
+ }
+
+ function createFilterMatrix(config) {
+ let m = Matrix.identity();
+ if (config.sepia !== 0) {
+ m = multiplyMatrices(m, Matrix.sepia(config.sepia / 100));
}
- function cancel() {
- tasks.splice(0);
- cancelAnimationFrame(frameId);
- frameId = null;
+ if (config.grayscale !== 0) {
+ m = multiplyMatrices(m, Matrix.grayscale(config.grayscale / 100));
}
- return { add, cancel };
- }
- const delayTokens = new Set();
- function requestAnimationFrameOnce(token, callback) {
- if (delayTokens.has(token)) {
- return;
+ if (config.contrast !== 100) {
+ m = multiplyMatrices(m, Matrix.contrast(config.contrast / 100));
}
- delayTokens.add(token);
- requestAnimationFrame(() => {
- delayTokens.delete(token);
- callback();
- });
+ if (config.brightness !== 100) {
+ m = multiplyMatrices(m, Matrix.brightness(config.brightness / 100));
+ }
+ if (config.mode === 1) {
+ m = multiplyMatrices(m, Matrix.invertNHue());
+ }
+ return m;
+ }
+ function applyColorMatrix([r, g, b], matrix) {
+ const rgb = [[r / 255], [g / 255], [b / 255], [1], [1]];
+ const result = multiplyMatrices(matrix, rgb);
+ return [0, 1, 2].map((i) =>
+ clamp(Math.round(result[i][0] * 255), 0, 255)
+ );
}
+ const Matrix = {
+ identity() {
+ return [
+ [1, 0, 0, 0, 0],
+ [0, 1, 0, 0, 0],
+ [0, 0, 1, 0, 0],
+ [0, 0, 0, 1, 0],
+ [0, 0, 0, 0, 1]
+ ];
+ },
+ invertNHue() {
+ return [
+ [0.333, -0.667, -0.667, 0, 1],
+ [-0.667, 0.333, -0.667, 0, 1],
+ [-0.667, -0.667, 0.333, 0, 1],
+ [0, 0, 0, 1, 0],
+ [0, 0, 0, 0, 1]
+ ];
+ },
+ brightness(v) {
+ return [
+ [v, 0, 0, 0, 0],
+ [0, v, 0, 0, 0],
+ [0, 0, v, 0, 0],
+ [0, 0, 0, 1, 0],
+ [0, 0, 0, 0, 1]
+ ];
+ },
+ contrast(v) {
+ const t = (1 - v) / 2;
+ return [
+ [v, 0, 0, 0, t],
+ [0, v, 0, 0, t],
+ [0, 0, v, 0, t],
+ [0, 0, 0, 1, 0],
+ [0, 0, 0, 0, 1]
+ ];
+ },
+ sepia(v) {
+ return [
+ [
+ 0.393 + 0.607 * (1 - v),
+ 0.769 - 0.769 * (1 - v),
+ 0.189 - 0.189 * (1 - v),
+ 0,
+ 0
+ ],
+ [
+ 0.349 - 0.349 * (1 - v),
+ 0.686 + 0.314 * (1 - v),
+ 0.168 - 0.168 * (1 - v),
+ 0,
+ 0
+ ],
+ [
+ 0.272 - 0.272 * (1 - v),
+ 0.534 - 0.534 * (1 - v),
+ 0.131 + 0.869 * (1 - v),
+ 0,
+ 0
+ ],
+ [0, 0, 0, 1, 0],
+ [0, 0, 0, 0, 1]
+ ];
+ },
+ grayscale(v) {
+ return [
+ [
+ 0.2126 + 0.7874 * (1 - v),
+ 0.7152 - 0.7152 * (1 - v),
+ 0.0722 - 0.0722 * (1 - v),
+ 0,
+ 0
+ ],
+ [
+ 0.2126 - 0.2126 * (1 - v),
+ 0.7152 + 0.2848 * (1 - v),
+ 0.0722 - 0.0722 * (1 - v),
+ 0,
+ 0
+ ],
+ [
+ 0.2126 - 0.2126 * (1 - v),
+ 0.7152 - 0.7152 * (1 - v),
+ 0.0722 + 0.9278 * (1 - v),
+ 0,
+ 0
+ ],
+ [0, 0, 0, 1, 0],
+ [0, 0, 0, 0, 1]
+ ];
+ }
+ };
- function getDuration(time) {
- let duration = 0;
- if (time.seconds) {
- duration += time.seconds * 1000;
+ var FilterMode;
+ (function (FilterMode) {
+ FilterMode[(FilterMode["light"] = 0)] = "light";
+ FilterMode[(FilterMode["dark"] = 1)] = "dark";
+ })(FilterMode || (FilterMode = {}));
+ function getCSSFilterValue(config) {
+ const filters = [];
+ if (config.mode === FilterMode.dark) {
+ filters.push("invert(100%) hue-rotate(180deg)");
}
- if (time.minutes) {
- duration += time.minutes * 60 * 1000;
+ if (config.brightness !== 100) {
+ filters.push(`brightness(${config.brightness}%)`);
}
- if (time.hours) {
- duration += time.hours * 60 * 60 * 1000;
+ if (config.contrast !== 100) {
+ filters.push(`contrast(${config.contrast}%)`);
}
- if (time.days) {
- duration += time.days * 24 * 60 * 60 * 1000;
+ if (config.grayscale !== 0) {
+ filters.push(`grayscale(${config.grayscale}%)`);
}
- return duration;
- }
-
- function removeNode(node) {
- node && node.parentNode && node.parentNode.removeChild(node);
- }
- function watchForNodePosition(node, mode, onRestore = Function.prototype) {
- const MAX_ATTEMPTS_COUNT = 10;
- const RETRY_TIMEOUT = getDuration({ seconds: 2 });
- const ATTEMPTS_INTERVAL = getDuration({ seconds: 10 });
- const prevSibling = node.previousSibling;
- let parent = node.parentNode;
- if (!parent) {
- throw new Error(
- "Unable to watch for node position: parent element not found"
- );
+ if (config.sepia !== 0) {
+ filters.push(`sepia(${config.sepia}%)`);
}
- if (mode === "prev-sibling" && !prevSibling) {
- throw new Error(
- "Unable to watch for node position: there is no previous sibling"
- );
+ if (filters.length === 0) {
+ return null;
}
- let attempts = 0;
- let start = null;
- let timeoutId = null;
- const restore = throttle(() => {
- if (timeoutId) {
- return;
- }
- attempts++;
- const now = Date.now();
- if (start == null) {
- start = now;
- } else if (attempts >= MAX_ATTEMPTS_COUNT) {
- if (now - start < ATTEMPTS_INTERVAL) {
- timeoutId = setTimeout(() => {
- start = null;
- attempts = 0;
- timeoutId = null;
- restore();
- }, RETRY_TIMEOUT);
- return;
- }
- start = now;
- attempts = 1;
+ return filters.join(" ");
+ }
+
+ function evalMath(expression) {
+ const rpnStack = [];
+ const workingStack = [];
+ let lastToken;
+ for (let i = 0, len = expression.length; i < len; i++) {
+ const token = expression[i];
+ if (!token || token === " ") {
+ continue;
}
- if (mode === "head") {
- if (prevSibling && prevSibling.parentNode !== parent) {
- stop();
- return;
+ if (operators.has(token)) {
+ const op = operators.get(token);
+ while (workingStack.length) {
+ const currentOp = operators.get(workingStack[0]);
+ if (!currentOp) {
+ break;
+ }
+ if (op.lessOrEqualThan(currentOp)) {
+ rpnStack.push(workingStack.shift());
+ } else {
+ break;
+ }
}
+ workingStack.unshift(token);
+ } else if (!lastToken || operators.has(lastToken)) {
+ rpnStack.push(token);
+ } else {
+ rpnStack[rpnStack.length - 1] += token;
}
- if (mode === "prev-sibling") {
- if (prevSibling.parentNode == null) {
- stop();
- return;
- }
- if (prevSibling.parentNode !== parent) {
- updateParent(prevSibling.parentNode);
- }
+ lastToken = token;
+ }
+ rpnStack.push(...workingStack);
+ const stack = [];
+ for (let i = 0, len = rpnStack.length; i < len; i++) {
+ const op = operators.get(rpnStack[i]);
+ if (op) {
+ const args = stack.splice(0, 2);
+ stack.push(op.exec(args[1], args[0]));
+ } else {
+ stack.unshift(parseFloat(rpnStack[i]));
}
- if (mode === "head" && !parent.isConnected) {
- parent = document.head;
- }
- parent.insertBefore(
- node,
- prevSibling && prevSibling.isConnected
- ? prevSibling.nextSibling
- : parent.firstChild
- );
- observer.takeRecords();
- onRestore && onRestore();
- });
- const observer = new MutationObserver(() => {
- if (
- (mode === "head" &&
- (node.parentNode !== parent ||
- !node.parentNode.isConnected)) ||
- (mode === "prev-sibling" &&
- node.previousSibling !== prevSibling)
- ) {
- restore();
- }
- });
- const run = () => {
- observer.observe(parent, { childList: true });
- };
- const stop = () => {
- clearTimeout(timeoutId);
- observer.disconnect();
- restore.cancel();
- };
- const skip = () => {
- observer.takeRecords();
- };
- const updateParent = (parentNode) => {
- parent = parentNode;
- stop();
- run();
- };
- run();
- return { run, stop, skip };
+ }
+ return stack[0];
}
- function iterateShadowHosts(root, iterator) {
- if (root == null) {
- return;
+ class Operator {
+ constructor(precedence, method) {
+ this.precendce = precedence;
+ this.execMethod = method;
}
- const walker = document.createTreeWalker(
- root,
- NodeFilter.SHOW_ELEMENT,
- {
- acceptNode(node) {
- return node.shadowRoot == null
- ? NodeFilter.FILTER_SKIP
- : NodeFilter.FILTER_ACCEPT;
- }
- }
- );
- for (
- let node = root.shadowRoot ? walker.currentNode : walker.nextNode();
- node != null;
- node = walker.nextNode()
- ) {
- if (node.classList.contains("surfingkeys_hints_host")) {
- continue;
- }
- iterator(node);
- iterateShadowHosts(node.shadowRoot, iterator);
+ exec(left, right) {
+ return this.execMethod(left, right);
+ }
+ lessOrEqualThan(op) {
+ return this.precendce <= op.precendce;
}
}
- let isDOMReady = () => {
- return (
- document.readyState === "complete" ||
- document.readyState === "interactive"
- );
- };
- function setIsDOMReady(newFunc) {
- isDOMReady = newFunc;
- }
- const readyStateListeners = new Set();
- function addDOMReadyListener(listener) {
- isDOMReady() ? listener() : readyStateListeners.add(listener);
+ const operators = new Map([
+ ["+", new Operator(1, (left, right) => left + right)],
+ ["-", new Operator(1, (left, right) => left - right)],
+ ["*", new Operator(2, (left, right) => left * right)],
+ ["/", new Operator(2, (left, right) => left / right)]
+ ]);
+
+ const isSystemDarkModeEnabled = () =>
+ matchMedia("(prefers-color-scheme: dark)").matches;
+
+ const hslaParseCache = new Map();
+ const rgbaParseCache = new Map();
+ function parseColorWithCache($color) {
+ $color = $color.trim();
+ if (rgbaParseCache.has($color)) {
+ return rgbaParseCache.get($color);
+ }
+ if ($color.includes("calc(")) {
+ $color = lowerCalcExpression($color);
+ }
+ const color = parse($color);
+ if (color) {
+ rgbaParseCache.set($color, color);
+ return color;
+ }
+ return null;
}
- function removeDOMReadyListener(listener) {
- readyStateListeners.delete(listener);
+ function parseToHSLWithCache(color) {
+ if (hslaParseCache.has(color)) {
+ return hslaParseCache.get(color);
+ }
+ const rgb = parseColorWithCache(color);
+ if (!rgb) {
+ return null;
+ }
+ const hsl = rgbToHSL(rgb);
+ hslaParseCache.set(color, hsl);
+ return hsl;
}
- function isReadyStateComplete() {
- return document.readyState === "complete";
+ function clearColorCache() {
+ hslaParseCache.clear();
+ rgbaParseCache.clear();
}
- const readyStateCompleteListeners = new Set();
- function addReadyStateCompleteListener(listener) {
- isReadyStateComplete()
- ? listener()
- : readyStateCompleteListeners.add(listener);
+ function hslToRGB({h, s, l, a = 1}) {
+ if (s === 0) {
+ const [r, b, g] = [l, l, l].map((x) => Math.round(x * 255));
+ return {r, g, b, a};
+ }
+ const c = (1 - Math.abs(2 * l - 1)) * s;
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
+ const m = l - c / 2;
+ const [r, g, b] = (
+ h < 60
+ ? [c, x, 0]
+ : h < 120
+ ? [x, c, 0]
+ : h < 180
+ ? [0, c, x]
+ : h < 240
+ ? [0, x, c]
+ : h < 300
+ ? [x, 0, c]
+ : [c, 0, x]
+ ).map((n) => Math.round((n + m) * 255));
+ return {r, g, b, a};
}
- function cleanReadyStateCompleteListeners() {
- readyStateCompleteListeners.clear();
+ function rgbToHSL({r: r255, g: g255, b: b255, a = 1}) {
+ const r = r255 / 255;
+ const g = g255 / 255;
+ const b = b255 / 255;
+ const max = Math.max(r, g, b);
+ const min = Math.min(r, g, b);
+ const c = max - min;
+ const l = (max + min) / 2;
+ if (c === 0) {
+ return {h: 0, s: 0, l, a};
+ }
+ let h =
+ (max === r
+ ? ((g - b) / c) % 6
+ : max === g
+ ? (b - r) / c + 2
+ : (r - g) / c + 4) * 60;
+ if (h < 0) {
+ h += 360;
+ }
+ const s = c / (1 - Math.abs(2 * l - 1));
+ return {h, s, l, a};
}
- if (!isDOMReady()) {
- const onReadyStateChange = () => {
- if (isDOMReady()) {
- readyStateListeners.forEach((listener) => listener());
- readyStateListeners.clear();
- if (isReadyStateComplete()) {
- document.removeEventListener(
- "readystatechange",
- onReadyStateChange
- );
- readyStateCompleteListeners.forEach((listener) =>
- listener()
- );
- readyStateCompleteListeners.clear();
+ function toFixed(n, digits = 0) {
+ const fixed = n.toFixed(digits);
+ if (digits === 0) {
+ return fixed;
+ }
+ const dot = fixed.indexOf(".");
+ if (dot >= 0) {
+ const zerosMatch = fixed.match(/0+$/);
+ if (zerosMatch) {
+ if (zerosMatch.index === dot + 1) {
+ return fixed.substring(0, dot);
}
+ return fixed.substring(0, zerosMatch.index);
}
- };
- document.addEventListener("readystatechange", onReadyStateChange);
+ }
+ return fixed;
}
- const HUGE_MUTATIONS_COUNT = 1000;
- function isHugeMutation(mutations) {
- if (mutations.length > HUGE_MUTATIONS_COUNT) {
- return true;
+ function rgbToString(rgb) {
+ const {r, g, b, a} = rgb;
+ if (a != null && a < 1) {
+ return `rgba(${toFixed(r)}, ${toFixed(g)}, ${toFixed(b)}, ${toFixed(a, 2)})`;
}
- let addedNodesCount = 0;
- for (let i = 0; i < mutations.length; i++) {
- addedNodesCount += mutations[i].addedNodes.length;
- if (addedNodesCount > HUGE_MUTATIONS_COUNT) {
- return true;
- }
+ return `rgb(${toFixed(r)}, ${toFixed(g)}, ${toFixed(b)})`;
+ }
+ function rgbToHexString({r, g, b, a}) {
+ return `#${(a != null && a < 1
+ ? [r, g, b, Math.round(a * 255)]
+ : [r, g, b]
+ )
+ .map((x) => {
+ return `${x < 16 ? "0" : ""}${x.toString(16)}`;
+ })
+ .join("")}`;
+ }
+ function hslToString(hsl) {
+ const {h, s, l, a} = hsl;
+ if (a != null && a < 1) {
+ return `hsla(${toFixed(h)}, ${toFixed(s * 100)}%, ${toFixed(l * 100)}%, ${toFixed(a, 2)})`;
}
- return false;
+ return `hsl(${toFixed(h)}, ${toFixed(s * 100)}%, ${toFixed(l * 100)}%)`;
}
- function getElementsTreeOperations(mutations) {
- const additions = new Set();
- const deletions = new Set();
- const moves = new Set();
- mutations.forEach((m) => {
- forEach(m.addedNodes, (n) => {
- if (n instanceof Element && n.isConnected) {
- additions.add(n);
- }
- });
- forEach(m.removedNodes, (n) => {
- if (n instanceof Element) {
- if (n.isConnected) {
- moves.add(n);
- additions.delete(n);
- } else {
- deletions.add(n);
- }
- }
- });
- });
- const duplicateAdditions = [];
- const duplicateDeletions = [];
- additions.forEach((node) => {
- if (additions.has(node.parentElement)) {
- duplicateAdditions.push(node);
+ const rgbMatch = /^rgba?\([^\(\)]+\)$/;
+ const hslMatch = /^hsla?\([^\(\)]+\)$/;
+ const hexMatch = /^#[0-9a-f]+$/i;
+ function parse($color) {
+ const c = $color.trim().toLowerCase();
+ if ($color.includes("(from ")) {
+ return domParseColor(c);
+ }
+ if (c.match(rgbMatch)) {
+ if (c.startsWith("rgb(#") || c.startsWith("rgba(#")) {
+ return domParseColor(c);
}
- });
- deletions.forEach((node) => {
- if (deletions.has(node.parentElement)) {
- duplicateDeletions.push(node);
- }
- });
- duplicateAdditions.forEach((node) => additions.delete(node));
- duplicateDeletions.forEach((node) => deletions.delete(node));
- return { additions, moves, deletions };
- }
- const optimizedTreeObservers = new Map();
- const optimizedTreeCallbacks = new WeakMap();
- function createOptimizedTreeObserver(root, callbacks) {
- let observer;
- let observerCallbacks;
- let domReadyListener;
- if (optimizedTreeObservers.has(root)) {
- observer = optimizedTreeObservers.get(root);
- observerCallbacks = optimizedTreeCallbacks.get(observer);
- } else {
- let hadHugeMutationsBefore = false;
- let subscribedForReadyState = false;
- observer = new MutationObserver((mutations) => {
- if (isHugeMutation(mutations)) {
- if (!hadHugeMutationsBefore || isDOMReady()) {
- observerCallbacks.forEach(({ onHugeMutations }) =>
- onHugeMutations(root)
- );
- } else if (!subscribedForReadyState) {
- domReadyListener = () =>
- observerCallbacks.forEach(({ onHugeMutations }) =>
- onHugeMutations(root)
- );
- addDOMReadyListener(domReadyListener);
- subscribedForReadyState = true;
- }
- hadHugeMutationsBefore = true;
- } else {
- const elementsOperations =
- getElementsTreeOperations(mutations);
- observerCallbacks.forEach(({ onMinorMutations }) =>
- onMinorMutations(root, elementsOperations)
- );
- }
- });
- observer.observe(root, { childList: true, subtree: true });
- optimizedTreeObservers.set(root, observer);
- observerCallbacks = new Set();
- optimizedTreeCallbacks.set(observer, observerCallbacks);
+ return parseRGB(c);
}
- observerCallbacks.add(callbacks);
- return {
- disconnect() {
- observerCallbacks.delete(callbacks);
- if (domReadyListener) {
- removeDOMReadyListener(domReadyListener);
- }
- if (observerCallbacks.size === 0) {
- observer.disconnect();
- optimizedTreeCallbacks.delete(observer);
- optimizedTreeObservers.delete(root);
- }
- }
- };
- }
-
- function getMatches(regex, input, group = 0) {
- const matches = [];
- let m;
- while ((m = regex.exec(input))) {
- matches.push(m[group]);
+ if (c.match(hslMatch)) {
+ return parseHSL(c);
}
- return matches;
- }
- function getHashCode(text) {
- const len = text.length;
- let hash = 0;
- for (let i = 0; i < len; i++) {
- const c = text.charCodeAt(i);
- hash = ((hash << 5) - hash + c) & 4294967295;
+ if (c.match(hexMatch)) {
+ return parseHex(c);
}
- return hash;
- }
- function escapeRegExpSpecialChars(input) {
- return input.replaceAll(/[\^$.*+?\(\)\[\]{}|\-\\]/g, "\\$&");
- }
- function getParenthesesRange(input, searchStartIndex = 0) {
- return getOpenCloseRange(input, searchStartIndex, "(", ")", []);
- }
- function getOpenCloseRange(
- input,
- searchStartIndex,
- openToken,
- closeToken,
- excludeRanges
- ) {
- let indexOf;
- if (excludeRanges.length === 0) {
- indexOf = (token, pos) => input.indexOf(token, pos);
- } else {
- indexOf = (token, pos) =>
- indexOfExcluding(input, token, pos, excludeRanges);
+ if (knownColors.has(c)) {
+ return getColorByName(c);
}
- const { length } = input;
- let depth = 0;
- let firstOpenIndex = -1;
- for (let i = searchStartIndex; i < length; i++) {
- if (depth === 0) {
- const openIndex = indexOf(openToken, i);
- if (openIndex < 0) {
- break;
- }
- firstOpenIndex = openIndex;
- depth++;
- i = openIndex;
- } else {
- const closeIndex = indexOf(closeToken, i);
- if (closeIndex < 0) {
- break;
- }
- const openIndex = indexOf(openToken, i);
- if (openIndex < 0 || closeIndex <= openIndex) {
- depth--;
- if (depth === 0) {
- return { start: firstOpenIndex, end: closeIndex + 1 };
- }
- i = closeIndex;
- } else {
- depth++;
- i = openIndex;
- }
- }
+ if (systemColors.has(c)) {
+ return getSystemColor(c);
}
- return null;
- }
- function indexOfExcluding(input, search, position, excludeRanges) {
- const i = input.indexOf(search, position);
- const exclusion = excludeRanges.find((r) => i >= r.start && i < r.end);
- if (exclusion) {
- return indexOfExcluding(
- input,
- search,
- exclusion.end,
- excludeRanges
- );
+ if ($color === "transparent") {
+ return {r: 0, g: 0, b: 0, a: 0};
}
- return i;
- }
- function splitExcluding(input, separator, excludeRanges) {
- const parts = [];
- let commaIndex = -1;
- let currIndex = 0;
- while (
- (commaIndex = indexOfExcluding(
- input,
- separator,
- currIndex,
- excludeRanges
- )) >= 0
+ if (
+ (c.startsWith("color(") || c.startsWith("color-mix(")) &&
+ c.endsWith(")")
) {
- parts.push(input.substring(currIndex, commaIndex).trim());
- currIndex = commaIndex + 1;
+ return domParseColor(c);
}
- parts.push(input.substring(currIndex).trim());
- return parts;
- }
-
- let anchor;
- const parsedURLCache = new Map();
- function fixBaseURL($url) {
- if (!anchor) {
- anchor = document.createElement("a");
+ if (c.startsWith("light-dark(") && c.endsWith(")")) {
+ const match = c.match(
+ /^light-dark\(\s*([a-z]+(\(.*\))?),\s*([a-z]+(\(.*\))?)\s*\)$/
+ );
+ if (match) {
+ const schemeColor = isSystemDarkModeEnabled()
+ ? match[3]
+ : match[1];
+ return parse(schemeColor);
+ }
}
- anchor.href = $url;
- return anchor.href;
+ return null;
}
- function parseURL($url, $base = null) {
- const key = `${$url}${$base ? `;${$base}` : ""}`;
- if (parsedURLCache.has(key)) {
- return parsedURLCache.get(key);
+ function getNumbers($color) {
+ const numbers = [];
+ let prevPos = 0;
+ let isMining = false;
+ const startIndex = $color.indexOf("(");
+ $color = $color.substring(startIndex + 1, $color.length - 1);
+ for (let i = 0; i < $color.length; i++) {
+ const c = $color[i];
+ if ((c >= "0" && c <= "9") || c === "." || c === "+" || c === "-") {
+ isMining = true;
+ } else if (isMining && (c === " " || c === "," || c === "/")) {
+ numbers.push($color.substring(prevPos, i));
+ isMining = false;
+ prevPos = i + 1;
+ } else if (!isMining) {
+ prevPos = i + 1;
+ }
}
- if ($base) {
- const parsedURL = new URL($url, fixBaseURL($base));
- parsedURLCache.set(key, parsedURL);
- return parsedURL;
+ if (isMining) {
+ numbers.push($color.substring(prevPos, $color.length));
}
- const parsedURL = new URL(fixBaseURL($url));
- parsedURLCache.set($url, parsedURL);
- return parsedURL;
+ return numbers;
}
- function getAbsoluteURL($base, $relative) {
- if ($relative.match(/^data\\?\:/)) {
- return $relative;
- }
- if (/^\/\//.test($relative)) {
- return `${location.protocol}${$relative}`;
+ function getNumbersFromString(str, range, units) {
+ const raw = getNumbers(str);
+ const unitsList = Object.entries(units);
+ const numbers = raw
+ .map((r) => r.trim())
+ .map((r, i) => {
+ let n;
+ const unit = unitsList.find(([u]) => r.endsWith(u));
+ if (unit) {
+ n =
+ (parseFloat(r.substring(0, r.length - unit[0].length)) /
+ unit[1]) *
+ range[i];
+ } else {
+ n = parseFloat(r);
+ }
+ if (range[i] > 1) {
+ return Math.round(n);
+ }
+ return n;
+ });
+ return numbers;
+ }
+ const rgbRange = [255, 255, 255, 1];
+ const rgbUnits = {"%": 100};
+ function parseRGB($rgb) {
+ const [r, g, b, a = 1] = getNumbersFromString($rgb, rgbRange, rgbUnits);
+ if (r == null || g == null || b == null || a == null) {
+ return null;
}
- const b = parseURL($base);
- const a = parseURL($relative, b.href);
- return a.href;
+ return {r, g, b, a};
}
- function isRelativeHrefOnAbsolutePath(href) {
- if (href.startsWith("data:")) {
- return true;
+ const hslRange = [360, 1, 1, 1];
+ const hslUnits = {"%": 100, "deg": 360, "rad": 2 * Math.PI, "turn": 1};
+ function parseHSL($hsl) {
+ const [h, s, l, a = 1] = getNumbersFromString($hsl, hslRange, hslUnits);
+ if (h == null || s == null || l == null || a == null) {
+ return null;
}
- const url = parseURL(href);
- if (url.protocol !== location.protocol) {
- return false;
- }
- if (url.hostname !== location.hostname) {
- return false;
- }
- if (url.port !== location.port) {
- return false;
- }
- return url.pathname === location.pathname;
+ return hslToRGB({h, s, l, a});
}
-
- function iterateCSSRules(rules, iterate, onImportError) {
- forEach(rules, (rule) => {
- if (isStyleRule(rule)) {
- iterate(rule);
- } else if (isImportRule(rule)) {
- try {
- iterateCSSRules(
- rule.styleSheet.cssRules,
- iterate,
- onImportError
- );
- } catch (err) {
- onImportError?.();
- }
- } else if (isMediaRule(rule)) {
- const media = Array.from(rule.media);
- const isScreenOrAllOrQuery = media.some(
- (m) =>
- m.startsWith("screen") ||
- m.startsWith("all") ||
- m.startsWith("(")
- );
- const isPrintOrSpeech = media.some(
- (m) => m.startsWith("print") || m.startsWith("speech")
+ function parseHex($hex) {
+ const h = $hex.substring(1);
+ switch (h.length) {
+ case 3:
+ case 4: {
+ const [r, g, b] = [0, 1, 2].map((i) =>
+ parseInt(`${h[i]}${h[i]}`, 16)
);
- if (isScreenOrAllOrQuery || !isPrintOrSpeech) {
- iterateCSSRules(rule.cssRules, iterate, onImportError);
- }
- } else if (isSupportsRule(rule)) {
- if (CSS.supports(rule.conditionText)) {
- iterateCSSRules(rule.cssRules, iterate, onImportError);
- }
- } else if (isLayerRule(rule)) {
- iterateCSSRules(rule.cssRules, iterate, onImportError);
- } else;
- });
- }
- const shorthandVarDependantProperties = [
- "background",
- "border",
- "border-color",
- "border-bottom",
- "border-left",
- "border-right",
- "border-top",
- "outline",
- "outline-color"
- ];
- const shorthandVarDepPropRegexps = isSafari
- ? shorthandVarDependantProperties.map((prop) => {
- const regexp = new RegExp(`${prop}:\\s*(.*?)\\s*;`);
- return [prop, regexp];
- })
- : null;
- function iterateCSSDeclarations(style, iterate) {
- forEach(style, (property) => {
- const value = style.getPropertyValue(property).trim();
- if (!value) {
- return;
- }
- iterate(property, value);
- });
- const cssText = style.cssText;
- if (cssText.includes("var(")) {
- if (isSafari) {
- shorthandVarDepPropRegexps.forEach(([prop, regexp]) => {
- const match = cssText.match(regexp);
- if (match && match[1]) {
- const val = match[1].trim();
- iterate(prop, val);
- }
- });
- } else {
- shorthandVarDependantProperties.forEach((prop) => {
- const val = style.getPropertyValue(prop);
- if (val && val.includes("var(")) {
- iterate(prop, val);
- }
- });
+ const a =
+ h.length === 3 ? 1 : parseInt(`${h[3]}${h[3]}`, 16) / 255;
+ return {r, g, b, a};
}
- }
- if (
- cssText.includes("background-color: ;") &&
- !style.getPropertyValue("background")
- ) {
- handleEmptyShorthand("background", style, iterate);
- }
- if (
- cssText.includes("border-") &&
- cssText.includes("-color: ;") &&
- !style.getPropertyValue("border")
- ) {
- handleEmptyShorthand("border", style, iterate);
- }
- }
- function handleEmptyShorthand(shorthand, style, iterate) {
- const parentRule = style.parentRule;
- if (isStyleRule(parentRule)) {
- const sourceCSSText =
- parentRule.parentStyleSheet?.ownerNode?.textContent;
- if (sourceCSSText) {
- let escapedSelector = escapeRegExpSpecialChars(
- parentRule.selectorText
- );
- escapedSelector = escapedSelector.replaceAll(/\s+/g, "\\s*");
- escapedSelector = escapedSelector.replaceAll(/::/g, "::?");
- const regexp = new RegExp(
- `${escapedSelector}\\s*{[^}]*${shorthand}:\\s*([^;}]+)`
+ case 6:
+ case 8: {
+ const [r, g, b] = [0, 2, 4].map((i) =>
+ parseInt(h.substring(i, i + 2), 16)
);
- const match = sourceCSSText.match(regexp);
- if (match) {
- iterate(shorthand, match[1]);
- }
- } else if (shorthand === "background") {
- iterate("background-color", "#ffffff");
+ const a =
+ h.length === 6 ? 1 : parseInt(h.substring(6, 8), 16) / 255;
+ return {r, g, b, a};
}
}
+ return null;
}
- const cssURLRegex = /url\((('.*?')|(".*?")|([^\)]*?))\)/g;
- const cssImportRegex =
- /@import\s*(url\()?(('.+?')|(".+?")|([^\)]*?))\)? ?(screen)?;?/gi;
- function getCSSURLValue(cssURL) {
- return cssURL
- .trim()
- .replace(/[\n\r\\]+/g, "")
- .replace(/^url\((.*)\)$/, "$1")
- .trim()
- .replace(/^"(.*)"$/, "$1")
- .replace(/^'(.*)'$/, "$1")
- .replace(/(?:\\(.))/g, "$1");
+ function getColorByName($color) {
+ const n = knownColors.get($color);
+ return {
+ r: (n >> 16) & 255,
+ g: (n >> 8) & 255,
+ b: (n >> 0) & 255,
+ a: 1
+ };
}
- function getCSSBaseBath(url) {
- const cssURL = parseURL(url);
- return `${cssURL.origin}${cssURL.pathname.replace(/\?.*$/, "").replace(/(\/)([^\/]+)$/i, "$1")}`;
+ function getSystemColor($color) {
+ const n = systemColors.get($color);
+ return {
+ r: (n >> 16) & 255,
+ g: (n >> 8) & 255,
+ b: (n >> 0) & 255,
+ a: 1
+ };
}
- function replaceCSSRelativeURLsWithAbsolute($css, cssBasePath) {
- return $css.replace(cssURLRegex, (match) => {
- try {
- const url = getCSSURLValue(match);
- const absoluteURL = getAbsoluteURL(cssBasePath, url);
- const escapedURL = absoluteURL.replaceAll("'", "\\'");
- return `url('${escapedURL}')`;
- } catch (err) {
- return match;
+ function lowerCalcExpression(color) {
+ let searchIndex = 0;
+ const replaceBetweenIndices = (start, end, replacement) => {
+ color =
+ color.substring(0, start) + replacement + color.substring(end);
+ };
+ while ((searchIndex = color.indexOf("calc(")) !== -1) {
+ const range = getParenthesesRange(color, searchIndex);
+ if (!range) {
+ break;
}
- });
- }
- const fontFaceRegex = /@font-face\s*{[^}]*}/g;
- function replaceCSSFontFace($css) {
- return $css.replace(fontFaceRegex, "");
- }
- const styleRules = new WeakSet();
- const importRules = new WeakSet();
- const mediaRules = new WeakSet();
- const supportsRules = new WeakSet();
- const layerRules = new WeakSet();
- function isStyleRule(rule) {
- if (!rule) {
- return false;
- }
- if (styleRules.has(rule)) {
- return true;
- }
- if (rule.selectorText) {
- styleRules.add(rule);
- return true;
- }
- return false;
- }
- function isImportRule(rule) {
- if (!rule) {
- return false;
- }
- if (styleRules.has(rule)) {
- return false;
- }
- if (importRules.has(rule)) {
- return true;
- }
- if (rule.href) {
- importRules.add(rule);
- return true;
- }
- return false;
- }
- function isMediaRule(rule) {
- if (!rule) {
- return false;
- }
- if (styleRules.has(rule)) {
- return false;
- }
- if (mediaRules.has(rule)) {
- return true;
- }
- if (rule.media) {
- mediaRules.add(rule);
- return true;
+ let slice = color.slice(range.start + 1, range.end - 1);
+ const includesPercentage = slice.includes("%");
+ slice = slice.split("%").join("");
+ const output = Math.round(evalMath(slice));
+ replaceBetweenIndices(
+ range.start - 4,
+ range.end,
+ output + (includesPercentage ? "%" : "")
+ );
}
- return false;
+ return color;
}
- function isSupportsRule(rule) {
- if (!rule) {
- return false;
- }
- if (styleRules.has(rule)) {
- return false;
- }
- if (supportsRules.has(rule)) {
- return true;
- }
- if (rule instanceof CSSSupportsRule) {
- supportsRules.add(rule);
- return true;
- }
- return false;
+ const knownColors = new Map(
+ Object.entries({
+ aliceblue: 0xf0f8ff,
+ antiquewhite: 0xfaebd7,
+ aqua: 0x00ffff,
+ aquamarine: 0x7fffd4,
+ azure: 0xf0ffff,
+ beige: 0xf5f5dc,
+ bisque: 0xffe4c4,
+ black: 0x000000,
+ blanchedalmond: 0xffebcd,
+ blue: 0x0000ff,
+ blueviolet: 0x8a2be2,
+ brown: 0xa52a2a,
+ burlywood: 0xdeb887,
+ cadetblue: 0x5f9ea0,
+ chartreuse: 0x7fff00,
+ chocolate: 0xd2691e,
+ coral: 0xff7f50,
+ cornflowerblue: 0x6495ed,
+ cornsilk: 0xfff8dc,
+ crimson: 0xdc143c,
+ cyan: 0x00ffff,
+ darkblue: 0x00008b,
+ darkcyan: 0x008b8b,
+ darkgoldenrod: 0xb8860b,
+ darkgray: 0xa9a9a9,
+ darkgrey: 0xa9a9a9,
+ darkgreen: 0x006400,
+ darkkhaki: 0xbdb76b,
+ darkmagenta: 0x8b008b,
+ darkolivegreen: 0x556b2f,
+ darkorange: 0xff8c00,
+ darkorchid: 0x9932cc,
+ darkred: 0x8b0000,
+ darksalmon: 0xe9967a,
+ darkseagreen: 0x8fbc8f,
+ darkslateblue: 0x483d8b,
+ darkslategray: 0x2f4f4f,
+ darkslategrey: 0x2f4f4f,
+ darkturquoise: 0x00ced1,
+ darkviolet: 0x9400d3,
+ deeppink: 0xff1493,
+ deepskyblue: 0x00bfff,
+ dimgray: 0x696969,
+ dimgrey: 0x696969,
+ dodgerblue: 0x1e90ff,
+ firebrick: 0xb22222,
+ floralwhite: 0xfffaf0,
+ forestgreen: 0x228b22,
+ fuchsia: 0xff00ff,
+ gainsboro: 0xdcdcdc,
+ ghostwhite: 0xf8f8ff,
+ gold: 0xffd700,
+ goldenrod: 0xdaa520,
+ gray: 0x808080,
+ grey: 0x808080,
+ green: 0x008000,
+ greenyellow: 0xadff2f,
+ honeydew: 0xf0fff0,
+ hotpink: 0xff69b4,
+ indianred: 0xcd5c5c,
+ indigo: 0x4b0082,
+ ivory: 0xfffff0,
+ khaki: 0xf0e68c,
+ lavender: 0xe6e6fa,
+ lavenderblush: 0xfff0f5,
+ lawngreen: 0x7cfc00,
+ lemonchiffon: 0xfffacd,
+ lightblue: 0xadd8e6,
+ lightcoral: 0xf08080,
+ lightcyan: 0xe0ffff,
+ lightgoldenrodyellow: 0xfafad2,
+ lightgray: 0xd3d3d3,
+ lightgrey: 0xd3d3d3,
+ lightgreen: 0x90ee90,
+ lightpink: 0xffb6c1,
+ lightsalmon: 0xffa07a,
+ lightseagreen: 0x20b2aa,
+ lightskyblue: 0x87cefa,
+ lightslategray: 0x778899,
+ lightslategrey: 0x778899,
+ lightsteelblue: 0xb0c4de,
+ lightyellow: 0xffffe0,
+ lime: 0x00ff00,
+ limegreen: 0x32cd32,
+ linen: 0xfaf0e6,
+ magenta: 0xff00ff,
+ maroon: 0x800000,
+ mediumaquamarine: 0x66cdaa,
+ mediumblue: 0x0000cd,
+ mediumorchid: 0xba55d3,
+ mediumpurple: 0x9370db,
+ mediumseagreen: 0x3cb371,
+ mediumslateblue: 0x7b68ee,
+ mediumspringgreen: 0x00fa9a,
+ mediumturquoise: 0x48d1cc,
+ mediumvioletred: 0xc71585,
+ midnightblue: 0x191970,
+ mintcream: 0xf5fffa,
+ mistyrose: 0xffe4e1,
+ moccasin: 0xffe4b5,
+ navajowhite: 0xffdead,
+ navy: 0x000080,
+ oldlace: 0xfdf5e6,
+ olive: 0x808000,
+ olivedrab: 0x6b8e23,
+ orange: 0xffa500,
+ orangered: 0xff4500,
+ orchid: 0xda70d6,
+ palegoldenrod: 0xeee8aa,
+ palegreen: 0x98fb98,
+ paleturquoise: 0xafeeee,
+ palevioletred: 0xdb7093,
+ papayawhip: 0xffefd5,
+ peachpuff: 0xffdab9,
+ peru: 0xcd853f,
+ pink: 0xffc0cb,
+ plum: 0xdda0dd,
+ powderblue: 0xb0e0e6,
+ purple: 0x800080,
+ rebeccapurple: 0x663399,
+ red: 0xff0000,
+ rosybrown: 0xbc8f8f,
+ royalblue: 0x4169e1,
+ saddlebrown: 0x8b4513,
+ salmon: 0xfa8072,
+ sandybrown: 0xf4a460,
+ seagreen: 0x2e8b57,
+ seashell: 0xfff5ee,
+ sienna: 0xa0522d,
+ silver: 0xc0c0c0,
+ skyblue: 0x87ceeb,
+ slateblue: 0x6a5acd,
+ slategray: 0x708090,
+ slategrey: 0x708090,
+ snow: 0xfffafa,
+ springgreen: 0x00ff7f,
+ steelblue: 0x4682b4,
+ tan: 0xd2b48c,
+ teal: 0x008080,
+ thistle: 0xd8bfd8,
+ tomato: 0xff6347,
+ turquoise: 0x40e0d0,
+ violet: 0xee82ee,
+ wheat: 0xf5deb3,
+ white: 0xffffff,
+ whitesmoke: 0xf5f5f5,
+ yellow: 0xffff00,
+ yellowgreen: 0x9acd32
+ })
+ );
+ const systemColors = new Map(
+ Object.entries({
+ "ActiveBorder": 0x3b99fc,
+ "ActiveCaption": 0x000000,
+ "AppWorkspace": 0xaaaaaa,
+ "Background": 0x6363ce,
+ "ButtonFace": 0xffffff,
+ "ButtonHighlight": 0xe9e9e9,
+ "ButtonShadow": 0x9fa09f,
+ "ButtonText": 0x000000,
+ "CaptionText": 0x000000,
+ "GrayText": 0x7f7f7f,
+ "Highlight": 0xb2d7ff,
+ "HighlightText": 0x000000,
+ "InactiveBorder": 0xffffff,
+ "InactiveCaption": 0xffffff,
+ "InactiveCaptionText": 0x000000,
+ "InfoBackground": 0xfbfcc5,
+ "InfoText": 0x000000,
+ "Menu": 0xf6f6f6,
+ "MenuText": 0xffffff,
+ "Scrollbar": 0xaaaaaa,
+ "ThreeDDarkShadow": 0x000000,
+ "ThreeDFace": 0xc0c0c0,
+ "ThreeDHighlight": 0xffffff,
+ "ThreeDLightShadow": 0xffffff,
+ "ThreeDShadow": 0x000000,
+ "Window": 0xececec,
+ "WindowFrame": 0xaaaaaa,
+ "WindowText": 0x000000,
+ "-webkit-focus-ring-color": 0xe59700
+ }).map(([key, value]) => [key.toLowerCase(), value])
+ );
+ function getSRGBLightness(r, g, b) {
+ return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
}
- function isLayerRule(rule) {
- if (!rule) {
- return false;
- }
- if (styleRules.has(rule)) {
- return false;
- }
- if (layerRules.has(rule)) {
- return true;
- }
- if (isLayerRuleSupported && rule instanceof CSSLayerBlockRule) {
- layerRules.add(rule);
- return true;
+ let canvas$1;
+ let context$1;
+ function domParseColor($color) {
+ if (!context$1) {
+ canvas$1 = document.createElement("canvas");
+ canvas$1.width = 1;
+ canvas$1.height = 1;
+ context$1 = canvas$1.getContext("2d", {willReadFrequently: true});
}
- return false;
+ context$1.fillStyle = $color;
+ context$1.fillRect(0, 0, 1, 1);
+ const d = context$1.getImageData(0, 0, 1, 1).data;
+ const color = `rgba(${d[0]}, ${d[1]}, ${d[2]}, ${(d[3] / 255).toFixed(2)})`;
+ return parseRGB(color);
}
- function evalMath(expression) {
- const rpnStack = [];
- const workingStack = [];
- let lastToken;
- for (let i = 0, len = expression.length; i < len; i++) {
- const token = expression[i];
- if (!token || token === " ") {
- continue;
- }
- if (operators.has(token)) {
- const op = operators.get(token);
- while (workingStack.length) {
- const currentOp = operators.get(workingStack[0]);
- if (!currentOp) {
- break;
+ function throttle(callback) {
+ let pending = false;
+ let frameId = null;
+ let lastArgs;
+ const throttled = (...args) => {
+ lastArgs = args;
+ if (frameId) {
+ pending = true;
+ } else {
+ callback(...lastArgs);
+ frameId = requestAnimationFrame(() => {
+ frameId = null;
+ if (pending) {
+ callback(...lastArgs);
+ pending = false;
}
- if (op.lessOrEqualThan(currentOp)) {
- rpnStack.push(workingStack.shift());
- } else {
- break;
- }
- }
- workingStack.unshift(token);
- } else if (!lastToken || operators.has(lastToken)) {
- rpnStack.push(token);
- } else {
- rpnStack[rpnStack.length - 1] += token;
+ });
}
- lastToken = token;
- }
- rpnStack.push(...workingStack);
- const stack = [];
- for (let i = 0, len = rpnStack.length; i < len; i++) {
- const op = operators.get(rpnStack[i]);
- if (op) {
- const args = stack.splice(0, 2);
- stack.push(op.exec(args[1], args[0]));
- } else {
- stack.unshift(parseFloat(rpnStack[i]));
+ };
+ const cancel = () => {
+ cancelAnimationFrame(frameId);
+ pending = false;
+ frameId = null;
+ };
+ return Object.assign(throttled, {cancel});
+ }
+ function createAsyncTasksQueue() {
+ const tasks = [];
+ let frameId = null;
+ function runTasks() {
+ let task;
+ while ((task = tasks.shift())) {
+ task();
}
+ frameId = null;
}
- return stack[0];
- }
- class Operator {
- constructor(precedence, method) {
- this.precendce = precedence;
- this.execMethod = method;
+ function add(task) {
+ tasks.push(task);
+ if (!frameId) {
+ frameId = requestAnimationFrame(runTasks);
+ }
}
- exec(left, right) {
- return this.execMethod(left, right);
+ function cancel() {
+ tasks.splice(0);
+ cancelAnimationFrame(frameId);
+ frameId = null;
}
- lessOrEqualThan(op) {
- return this.precendce <= op.precendce;
+ return {add, cancel};
+ }
+ const delayTokens = new Set();
+ function requestAnimationFrameOnce(token, callback) {
+ if (delayTokens.has(token)) {
+ return;
}
+ delayTokens.add(token);
+ requestAnimationFrame(() => {
+ delayTokens.delete(token);
+ callback();
+ });
}
- const operators = new Map([
- ["+", new Operator(1, (left, right) => left + right)],
- ["-", new Operator(1, (left, right) => left - right)],
- ["*", new Operator(2, (left, right) => left * right)],
- ["/", new Operator(2, (left, right) => left / right)]
- ]);
-
- const isSystemDarkModeEnabled = () =>
- matchMedia("(prefers-color-scheme: dark)").matches;
- const hslaParseCache = new Map();
- const rgbaParseCache = new Map();
- function parseColorWithCache($color) {
- $color = $color.trim();
- if (rgbaParseCache.has($color)) {
- return rgbaParseCache.get($color);
- }
- if ($color.includes("calc(")) {
- $color = lowerCalcExpression($color);
- }
- const color = parse($color);
- color && rgbaParseCache.set($color, color);
- return color;
+ function hexify(number) {
+ return (number < 16 ? "0" : "") + number.toString(16);
}
- function parseToHSLWithCache(color) {
- if (hslaParseCache.has(color)) {
- return hslaParseCache.get(color);
+ function generateUID() {
+ if ("randomUUID" in crypto) {
+ const uuid = crypto.randomUUID();
+ return (
+ uuid.substring(0, 8) +
+ uuid.substring(9, 13) +
+ uuid.substring(14, 18) +
+ uuid.substring(19, 23) +
+ uuid.substring(24)
+ );
}
- const rgb = parseColorWithCache(color);
- if (!rgb) {
- return null;
+ if ("getRandomValues" in crypto) {
+ return Array.from(crypto.getRandomValues(new Uint8Array(16)))
+ .map((x) => hexify(x))
+ .join("");
}
- const hsl = rgbToHSL(rgb);
- hslaParseCache.set(color, hsl);
- return hsl;
- }
- function clearColorCache() {
- hslaParseCache.clear();
- rgbaParseCache.clear();
+ return Math.floor(Math.random() * 2 ** 55).toString(36);
}
- function hslToRGB({ h, s, l, a = 1 }) {
- if (s === 0) {
- const [r, b, g] = [l, l, l].map((x) => Math.round(x * 255));
- return { r, g, b, a };
- }
- const c = (1 - Math.abs(2 * l - 1)) * s;
- const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
- const m = l - c / 2;
- const [r, g, b] = (
- h < 60
- ? [c, x, 0]
- : h < 120
- ? [x, c, 0]
- : h < 180
- ? [0, c, x]
- : h < 240
- ? [0, x, c]
- : h < 300
- ? [x, 0, c]
- : [c, 0, x]
- ).map((n) => Math.round((n + m) * 255));
- return { r, g, b, a };
+
+ let documentVisibilityListener = null;
+ let documentIsVisible_ = !document.hidden;
+ const listenerOptions = {
+ capture: true,
+ passive: true
+ };
+ function watchForDocumentVisibility() {
+ document.addEventListener(
+ "visibilitychange",
+ documentVisibilityListener,
+ listenerOptions
+ );
+ window.addEventListener(
+ "pageshow",
+ documentVisibilityListener,
+ listenerOptions
+ );
+ window.addEventListener(
+ "focus",
+ documentVisibilityListener,
+ listenerOptions
+ );
}
- function rgbToHSL({ r: r255, g: g255, b: b255, a = 1 }) {
- const r = r255 / 255;
- const g = g255 / 255;
- const b = b255 / 255;
- const max = Math.max(r, g, b);
- const min = Math.min(r, g, b);
- const c = max - min;
- const l = (max + min) / 2;
- if (c === 0) {
- return { h: 0, s: 0, l, a };
- }
- let h =
- (max === r
- ? ((g - b) / c) % 6
- : max === g
- ? (b - r) / c + 2
- : (r - g) / c + 4) * 60;
- if (h < 0) {
- h += 360;
- }
- const s = c / (1 - Math.abs(2 * l - 1));
- return { h, s, l, a };
+ function stopWatchingForDocumentVisibility() {
+ document.removeEventListener(
+ "visibilitychange",
+ documentVisibilityListener,
+ listenerOptions
+ );
+ window.removeEventListener(
+ "pageshow",
+ documentVisibilityListener,
+ listenerOptions
+ );
+ window.removeEventListener(
+ "focus",
+ documentVisibilityListener,
+ listenerOptions
+ );
}
- function toFixed(n, digits = 0) {
- const fixed = n.toFixed(digits);
- if (digits === 0) {
- return fixed;
- }
- const dot = fixed.indexOf(".");
- if (dot >= 0) {
- const zerosMatch = fixed.match(/0+$/);
- if (zerosMatch) {
- if (zerosMatch.index === dot + 1) {
- return fixed.substring(0, dot);
- }
- return fixed.substring(0, zerosMatch.index);
+ function setDocumentVisibilityListener(callback) {
+ const alreadyWatching = Boolean(documentVisibilityListener);
+ documentVisibilityListener = () => {
+ if (!document.hidden) {
+ removeDocumentVisibilityListener();
+ callback();
+ documentIsVisible_ = true;
}
+ };
+ if (!alreadyWatching) {
+ watchForDocumentVisibility();
}
- return fixed;
- }
- function rgbToString(rgb) {
- const { r, g, b, a } = rgb;
- if (a != null && a < 1) {
- return `rgba(${toFixed(r)}, ${toFixed(g)}, ${toFixed(b)}, ${toFixed(a, 2)})`;
- }
- return `rgb(${toFixed(r)}, ${toFixed(g)}, ${toFixed(b)})`;
}
- function rgbToHexString({ r, g, b, a }) {
- return `#${(a != null && a < 1
- ? [r, g, b, Math.round(a * 255)]
- : [r, g, b]
- )
- .map((x) => {
- return `${x < 16 ? "0" : ""}${x.toString(16)}`;
- })
- .join("")}`;
+ function removeDocumentVisibilityListener() {
+ stopWatchingForDocumentVisibility();
+ documentVisibilityListener = null;
}
- function hslToString(hsl) {
- const { h, s, l, a } = hsl;
- if (a != null && a < 1) {
- return `hsla(${toFixed(h)}, ${toFixed(s * 100)}%, ${toFixed(l * 100)}%, ${toFixed(a, 2)})`;
- }
- return `hsl(${toFixed(h)}, ${toFixed(s * 100)}%, ${toFixed(l * 100)}%)`;
+ function documentIsVisible() {
+ return documentIsVisible_;
}
- const rgbMatch = /^rgba?\([^\(\)]+\)$/;
- const hslMatch = /^hsla?\([^\(\)]+\)$/;
- const hexMatch = /^#[0-9a-f]+$/i;
- function parse($color) {
- const c = $color.trim().toLowerCase();
- if (c.match(rgbMatch)) {
- return parseRGB(c);
+
+ function getDuration(time) {
+ let duration = 0;
+ if (time.seconds) {
+ duration += time.seconds * 1000;
}
- if (c.match(hslMatch)) {
- return parseHSL(c);
+ if (time.minutes) {
+ duration += time.minutes * 60 * 1000;
}
- if (c.match(hexMatch)) {
- return parseHex(c);
+ if (time.hours) {
+ duration += time.hours * 60 * 60 * 1000;
}
- if (knownColors.has(c)) {
- return getColorByName(c);
+ if (time.days) {
+ duration += time.days * 24 * 60 * 60 * 1000;
}
- if (systemColors.has(c)) {
- return getSystemColor(c);
- }
- if ($color === "transparent") {
- return { r: 0, g: 0, b: 0, a: 0 };
- }
- if (
- (c.startsWith("color(") || c.startsWith("color-mix(")) &&
- c.endsWith(")")
- ) {
- return domParseColor(c);
+ return duration;
+ }
+
+ function logInfo(...args) {}
+ function logWarn(...args) {}
+
+ function removeNode(node) {
+ node && node.parentNode && node.parentNode.removeChild(node);
+ }
+ function watchForNodePosition(node, mode, onRestore = Function.prototype) {
+ const MAX_ATTEMPTS_COUNT = 10;
+ const RETRY_TIMEOUT = getDuration({seconds: 2});
+ const ATTEMPTS_INTERVAL = getDuration({seconds: 10});
+ const prevSibling = node.previousSibling;
+ let parent = node.parentNode;
+ if (!parent) {
+ throw new Error(
+ "Unable to watch for node position: parent element not found"
+ );
}
- if (c.startsWith("light-dark(") && c.endsWith(")")) {
- const match = c.match(
- /^light-dark\(\s*([a-z]+(\(.*\))?),\s*([a-z]+(\(.*\))?)\s*\)$/
+ if (mode === "prev-sibling" && !prevSibling) {
+ throw new Error(
+ "Unable to watch for node position: there is no previous sibling"
);
- if (match) {
- const schemeColor = isSystemDarkModeEnabled()
- ? match[3]
- : match[1];
- return parse(schemeColor);
- }
}
- return null;
- }
- function getNumbers($color) {
- const numbers = [];
- let prevPos = 0;
- let isMining = false;
- const startIndex = $color.indexOf("(");
- $color = $color.substring(startIndex + 1, $color.length - 1);
- for (let i = 0; i < $color.length; i++) {
- const c = $color[i];
- if ((c >= "0" && c <= "9") || c === "." || c === "+" || c === "-") {
- isMining = true;
- } else if (isMining && (c === " " || c === "," || c === "/")) {
- numbers.push($color.substring(prevPos, i));
- isMining = false;
- prevPos = i + 1;
- } else if (!isMining) {
- prevPos = i + 1;
+ let attempts = 0;
+ let start = null;
+ let timeoutId = null;
+ const restore = throttle(() => {
+ if (timeoutId) {
+ return;
}
- }
- if (isMining) {
- numbers.push($color.substring(prevPos, $color.length));
- }
- return numbers;
- }
- function getNumbersFromString(str, range, units) {
- const raw = getNumbers(str);
- const unitsList = Object.entries(units);
- const numbers = raw
- .map((r) => r.trim())
- .map((r, i) => {
- let n;
- const unit = unitsList.find(([u]) => r.endsWith(u));
- if (unit) {
- n =
- (parseFloat(r.substring(0, r.length - unit[0].length)) /
- unit[1]) *
- range[i];
- } else {
- n = parseFloat(r);
+ attempts++;
+ const now = Date.now();
+ if (start == null) {
+ start = now;
+ } else if (attempts >= MAX_ATTEMPTS_COUNT) {
+ if (now - start < ATTEMPTS_INTERVAL) {
+ timeoutId = setTimeout(() => {
+ start = null;
+ attempts = 0;
+ timeoutId = null;
+ restore();
+ }, RETRY_TIMEOUT);
+ return;
}
- if (range[i] > 1) {
- return Math.round(n);
+ start = now;
+ attempts = 1;
+ }
+ if (mode === "head") {
+ if (prevSibling && prevSibling.parentNode !== parent) {
+ stop();
+ return;
}
- return n;
- });
- return numbers;
- }
- const rgbRange = [255, 255, 255, 1];
- const rgbUnits = { "%": 100 };
- function parseRGB($rgb) {
- const [r, g, b, a = 1] = getNumbersFromString($rgb, rgbRange, rgbUnits);
- return { r, g, b, a };
- }
- const hslRange = [360, 1, 1, 1];
- const hslUnits = { "%": 100, "deg": 360, "rad": 2 * Math.PI, "turn": 1 };
- function parseHSL($hsl) {
- const [h, s, l, a = 1] = getNumbersFromString($hsl, hslRange, hslUnits);
- return hslToRGB({ h, s, l, a });
- }
- function parseHex($hex) {
- const h = $hex.substring(1);
- switch (h.length) {
- case 3:
- case 4: {
- const [r, g, b] = [0, 1, 2].map((i) =>
- parseInt(`${h[i]}${h[i]}`, 16)
- );
- const a =
- h.length === 3 ? 1 : parseInt(`${h[3]}${h[3]}`, 16) / 255;
- return { r, g, b, a };
}
- case 6:
- case 8: {
- const [r, g, b] = [0, 2, 4].map((i) =>
- parseInt(h.substring(i, i + 2), 16)
- );
- const a =
- h.length === 6 ? 1 : parseInt(h.substring(6, 8), 16) / 255;
- return { r, g, b, a };
+ if (mode === "prev-sibling") {
+ if (prevSibling.parentNode == null) {
+ stop();
+ return;
+ }
+ if (prevSibling.parentNode !== parent) {
+ updateParent(prevSibling.parentNode);
+ }
}
- }
- return null;
- }
- function getColorByName($color) {
- const n = knownColors.get($color);
- return {
- r: (n >> 16) & 255,
- g: (n >> 8) & 255,
- b: (n >> 0) & 255,
- a: 1
+ if (mode === "head" && !parent.isConnected) {
+ parent = document.head;
+ }
+ parent.insertBefore(
+ node,
+ prevSibling && prevSibling.isConnected
+ ? prevSibling.nextSibling
+ : parent.firstChild
+ );
+ observer.takeRecords();
+ onRestore && onRestore();
+ });
+ const observer = new MutationObserver(() => {
+ if (
+ (mode === "head" &&
+ (node.parentNode !== parent ||
+ !node.parentNode.isConnected)) ||
+ (mode === "prev-sibling" &&
+ node.previousSibling !== prevSibling)
+ ) {
+ restore();
+ }
+ });
+ const run = () => {
+ observer.observe(parent, {childList: true});
};
- }
- function getSystemColor($color) {
- const n = systemColors.get($color);
- return {
- r: (n >> 16) & 255,
- g: (n >> 8) & 255,
- b: (n >> 0) & 255,
- a: 1
+ const stop = () => {
+ clearTimeout(timeoutId);
+ observer.disconnect();
+ restore.cancel();
};
- }
- function lowerCalcExpression(color) {
- let searchIndex = 0;
- const replaceBetweenIndices = (start, end, replacement) => {
- color =
- color.substring(0, start) + replacement + color.substring(end);
+ const skip = () => {
+ observer.takeRecords();
};
- while ((searchIndex = color.indexOf("calc(")) !== -1) {
- const range = getParenthesesRange(color, searchIndex);
- if (!range) {
- break;
+ const updateParent = (parentNode) => {
+ parent = parentNode;
+ stop();
+ run();
+ };
+ run();
+ return {run, stop, skip};
+ }
+ function iterateShadowHosts(root, iterator) {
+ if (root == null) {
+ return;
+ }
+ const walker = document.createTreeWalker(
+ root,
+ NodeFilter.SHOW_ELEMENT,
+ {
+ acceptNode(node) {
+ return node.shadowRoot == null
+ ? NodeFilter.FILTER_SKIP
+ : NodeFilter.FILTER_ACCEPT;
+ }
}
- let slice = color.slice(range.start + 1, range.end - 1);
- const includesPercentage = slice.includes("%");
- slice = slice.split("%").join("");
- const output = Math.round(evalMath(slice));
- replaceBetweenIndices(
- range.start - 4,
- range.end,
- output + (includesPercentage ? "%" : "")
- );
+ );
+ for (
+ let node = root.shadowRoot ? walker.currentNode : walker.nextNode();
+ node != null;
+ node = walker.nextNode()
+ ) {
+ if (node.classList.contains("surfingkeys_hints_host")) {
+ continue;
+ }
+ iterator(node);
+ iterateShadowHosts(node.shadowRoot, iterator);
}
- return color;
}
- const knownColors = new Map(
- Object.entries({
- aliceblue: 0xf0f8ff,
- antiquewhite: 0xfaebd7,
- aqua: 0x00ffff,
- aquamarine: 0x7fffd4,
- azure: 0xf0ffff,
- beige: 0xf5f5dc,
- bisque: 0xffe4c4,
- black: 0x000000,
- blanchedalmond: 0xffebcd,
- blue: 0x0000ff,
- blueviolet: 0x8a2be2,
- brown: 0xa52a2a,
- burlywood: 0xdeb887,
- cadetblue: 0x5f9ea0,
- chartreuse: 0x7fff00,
- chocolate: 0xd2691e,
- coral: 0xff7f50,
- cornflowerblue: 0x6495ed,
- cornsilk: 0xfff8dc,
- crimson: 0xdc143c,
- cyan: 0x00ffff,
- darkblue: 0x00008b,
- darkcyan: 0x008b8b,
- darkgoldenrod: 0xb8860b,
- darkgray: 0xa9a9a9,
- darkgrey: 0xa9a9a9,
- darkgreen: 0x006400,
- darkkhaki: 0xbdb76b,
- darkmagenta: 0x8b008b,
- darkolivegreen: 0x556b2f,
- darkorange: 0xff8c00,
- darkorchid: 0x9932cc,
- darkred: 0x8b0000,
- darksalmon: 0xe9967a,
- darkseagreen: 0x8fbc8f,
- darkslateblue: 0x483d8b,
- darkslategray: 0x2f4f4f,
- darkslategrey: 0x2f4f4f,
- darkturquoise: 0x00ced1,
- darkviolet: 0x9400d3,
- deeppink: 0xff1493,
- deepskyblue: 0x00bfff,
- dimgray: 0x696969,
- dimgrey: 0x696969,
- dodgerblue: 0x1e90ff,
- firebrick: 0xb22222,
- floralwhite: 0xfffaf0,
- forestgreen: 0x228b22,
- fuchsia: 0xff00ff,
- gainsboro: 0xdcdcdc,
- ghostwhite: 0xf8f8ff,
- gold: 0xffd700,
- goldenrod: 0xdaa520,
- gray: 0x808080,
- grey: 0x808080,
- green: 0x008000,
- greenyellow: 0xadff2f,
- honeydew: 0xf0fff0,
- hotpink: 0xff69b4,
- indianred: 0xcd5c5c,
- indigo: 0x4b0082,
- ivory: 0xfffff0,
- khaki: 0xf0e68c,
- lavender: 0xe6e6fa,
- lavenderblush: 0xfff0f5,
- lawngreen: 0x7cfc00,
- lemonchiffon: 0xfffacd,
- lightblue: 0xadd8e6,
- lightcoral: 0xf08080,
- lightcyan: 0xe0ffff,
- lightgoldenrodyellow: 0xfafad2,
- lightgray: 0xd3d3d3,
- lightgrey: 0xd3d3d3,
- lightgreen: 0x90ee90,
- lightpink: 0xffb6c1,
- lightsalmon: 0xffa07a,
- lightseagreen: 0x20b2aa,
- lightskyblue: 0x87cefa,
- lightslategray: 0x778899,
- lightslategrey: 0x778899,
- lightsteelblue: 0xb0c4de,
- lightyellow: 0xffffe0,
- lime: 0x00ff00,
- limegreen: 0x32cd32,
- linen: 0xfaf0e6,
- magenta: 0xff00ff,
- maroon: 0x800000,
- mediumaquamarine: 0x66cdaa,
- mediumblue: 0x0000cd,
- mediumorchid: 0xba55d3,
- mediumpurple: 0x9370db,
- mediumseagreen: 0x3cb371,
- mediumslateblue: 0x7b68ee,
- mediumspringgreen: 0x00fa9a,
- mediumturquoise: 0x48d1cc,
- mediumvioletred: 0xc71585,
- midnightblue: 0x191970,
- mintcream: 0xf5fffa,
- mistyrose: 0xffe4e1,
- moccasin: 0xffe4b5,
- navajowhite: 0xffdead,
- navy: 0x000080,
- oldlace: 0xfdf5e6,
- olive: 0x808000,
- olivedrab: 0x6b8e23,
- orange: 0xffa500,
- orangered: 0xff4500,
- orchid: 0xda70d6,
- palegoldenrod: 0xeee8aa,
- palegreen: 0x98fb98,
- paleturquoise: 0xafeeee,
- palevioletred: 0xdb7093,
- papayawhip: 0xffefd5,
- peachpuff: 0xffdab9,
- peru: 0xcd853f,
- pink: 0xffc0cb,
- plum: 0xdda0dd,
- powderblue: 0xb0e0e6,
- purple: 0x800080,
- rebeccapurple: 0x663399,
- red: 0xff0000,
- rosybrown: 0xbc8f8f,
- royalblue: 0x4169e1,
- saddlebrown: 0x8b4513,
- salmon: 0xfa8072,
- sandybrown: 0xf4a460,
- seagreen: 0x2e8b57,
- seashell: 0xfff5ee,
- sienna: 0xa0522d,
- silver: 0xc0c0c0,
- skyblue: 0x87ceeb,
- slateblue: 0x6a5acd,
- slategray: 0x708090,
- slategrey: 0x708090,
- snow: 0xfffafa,
- springgreen: 0x00ff7f,
- steelblue: 0x4682b4,
- tan: 0xd2b48c,
- teal: 0x008080,
- thistle: 0xd8bfd8,
- tomato: 0xff6347,
- turquoise: 0x40e0d0,
- violet: 0xee82ee,
- wheat: 0xf5deb3,
- white: 0xffffff,
- whitesmoke: 0xf5f5f5,
- yellow: 0xffff00,
- yellowgreen: 0x9acd32
- })
- );
- const systemColors = new Map(
- Object.entries({
- "ActiveBorder": 0x3b99fc,
- "ActiveCaption": 0x000000,
- "AppWorkspace": 0xaaaaaa,
- "Background": 0x6363ce,
- "ButtonFace": 0xffffff,
- "ButtonHighlight": 0xe9e9e9,
- "ButtonShadow": 0x9fa09f,
- "ButtonText": 0x000000,
- "CaptionText": 0x000000,
- "GrayText": 0x7f7f7f,
- "Highlight": 0xb2d7ff,
- "HighlightText": 0x000000,
- "InactiveBorder": 0xffffff,
- "InactiveCaption": 0xffffff,
- "InactiveCaptionText": 0x000000,
- "InfoBackground": 0xfbfcc5,
- "InfoText": 0x000000,
- "Menu": 0xf6f6f6,
- "MenuText": 0xffffff,
- "Scrollbar": 0xaaaaaa,
- "ThreeDDarkShadow": 0x000000,
- "ThreeDFace": 0xc0c0c0,
- "ThreeDHighlight": 0xffffff,
- "ThreeDLightShadow": 0xffffff,
- "ThreeDShadow": 0x000000,
- "Window": 0xececec,
- "WindowFrame": 0xaaaaaa,
- "WindowText": 0x000000,
- "-webkit-focus-ring-color": 0xe59700
- }).map(([key, value]) => [key.toLowerCase(), value])
- );
- function getSRGBLightness(r, g, b) {
- return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
+ let isDOMReady = () => {
+ return (
+ document.readyState === "complete" ||
+ document.readyState === "interactive"
+ );
+ };
+ function setIsDOMReady(newFunc) {
+ isDOMReady = newFunc;
}
- let canvas$1;
- let context$1;
- function domParseColor($color) {
- if (!context$1) {
- canvas$1 = document.createElement("canvas");
- canvas$1.width = 1;
- canvas$1.height = 1;
- context$1 = canvas$1.getContext("2d", { willReadFrequently: true });
- }
- context$1.fillStyle = $color;
- context$1.fillRect(0, 0, 1, 1);
- const d = context$1.getImageData(0, 0, 1, 1).data;
- const color = `rgba(${d[0]}, ${d[1]}, ${d[2]}, ${(d[3] / 255).toFixed(2)})`;
- return parseRGB(color);
+ const readyStateListeners = new Set();
+ function addDOMReadyListener(listener) {
+ isDOMReady() ? listener() : readyStateListeners.add(listener);
}
-
- function scale(x, inLow, inHigh, outLow, outHigh) {
- return ((x - inLow) * (outHigh - outLow)) / (inHigh - inLow) + outLow;
+ function removeDOMReadyListener(listener) {
+ readyStateListeners.delete(listener);
}
- function clamp(x, min, max) {
- return Math.min(max, Math.max(min, x));
+ function isReadyStateComplete() {
+ return document.readyState === "complete";
}
- function multiplyMatrices(m1, m2) {
- const result = [];
- for (let i = 0, len = m1.length; i < len; i++) {
- result[i] = [];
- for (let j = 0, len2 = m2[0].length; j < len2; j++) {
- let sum = 0;
- for (let k = 0, len3 = m1[0].length; k < len3; k++) {
- sum += m1[i][k] * m2[k][j];
+ const readyStateCompleteListeners = new Set();
+ function addReadyStateCompleteListener(listener) {
+ isReadyStateComplete()
+ ? listener()
+ : readyStateCompleteListeners.add(listener);
+ }
+ function cleanReadyStateCompleteListeners() {
+ readyStateCompleteListeners.clear();
+ }
+ if (!isDOMReady()) {
+ const onReadyStateChange = () => {
+ if (isDOMReady()) {
+ readyStateListeners.forEach((listener) => listener());
+ readyStateListeners.clear();
+ if (isReadyStateComplete()) {
+ document.removeEventListener(
+ "readystatechange",
+ onReadyStateChange
+ );
+ readyStateCompleteListeners.forEach((listener) =>
+ listener()
+ );
+ readyStateCompleteListeners.clear();
}
- result[i][j] = sum;
}
- }
- return result;
+ };
+ document.addEventListener("readystatechange", onReadyStateChange);
}
-
- function createFilterMatrix(config) {
- let m = Matrix.identity();
- if (config.sepia !== 0) {
- m = multiplyMatrices(m, Matrix.sepia(config.sepia / 100));
+ const HUGE_MUTATIONS_COUNT = 1000;
+ function isHugeMutation(mutations) {
+ if (mutations.length > HUGE_MUTATIONS_COUNT) {
+ return true;
}
- if (config.grayscale !== 0) {
- m = multiplyMatrices(m, Matrix.grayscale(config.grayscale / 100));
+ let addedNodesCount = 0;
+ for (let i = 0; i < mutations.length; i++) {
+ addedNodesCount += mutations[i].addedNodes.length;
+ if (addedNodesCount > HUGE_MUTATIONS_COUNT) {
+ return true;
+ }
}
- if (config.contrast !== 100) {
- m = multiplyMatrices(m, Matrix.contrast(config.contrast / 100));
- }
- if (config.brightness !== 100) {
- m = multiplyMatrices(m, Matrix.brightness(config.brightness / 100));
- }
- if (config.mode === 1) {
- m = multiplyMatrices(m, Matrix.invertNHue());
- }
- return m;
- }
- function applyColorMatrix([r, g, b], matrix) {
- const rgb = [[r / 255], [g / 255], [b / 255], [1], [1]];
- const result = multiplyMatrices(matrix, rgb);
- return [0, 1, 2].map((i) =>
- clamp(Math.round(result[i][0] * 255), 0, 255)
- );
- }
- const Matrix = {
- identity() {
- return [
- [1, 0, 0, 0, 0],
- [0, 1, 0, 0, 0],
- [0, 0, 1, 0, 0],
- [0, 0, 0, 1, 0],
- [0, 0, 0, 0, 1]
- ];
- },
- invertNHue() {
- return [
- [0.333, -0.667, -0.667, 0, 1],
- [-0.667, 0.333, -0.667, 0, 1],
- [-0.667, -0.667, 0.333, 0, 1],
- [0, 0, 0, 1, 0],
- [0, 0, 0, 0, 1]
- ];
- },
- brightness(v) {
- return [
- [v, 0, 0, 0, 0],
- [0, v, 0, 0, 0],
- [0, 0, v, 0, 0],
- [0, 0, 0, 1, 0],
- [0, 0, 0, 0, 1]
- ];
- },
- contrast(v) {
- const t = (1 - v) / 2;
- return [
- [v, 0, 0, 0, t],
- [0, v, 0, 0, t],
- [0, 0, v, 0, t],
- [0, 0, 0, 1, 0],
- [0, 0, 0, 0, 1]
- ];
- },
- sepia(v) {
- return [
- [
- 0.393 + 0.607 * (1 - v),
- 0.769 - 0.769 * (1 - v),
- 0.189 - 0.189 * (1 - v),
- 0,
- 0
- ],
- [
- 0.349 - 0.349 * (1 - v),
- 0.686 + 0.314 * (1 - v),
- 0.168 - 0.168 * (1 - v),
- 0,
- 0
- ],
- [
- 0.272 - 0.272 * (1 - v),
- 0.534 - 0.534 * (1 - v),
- 0.131 + 0.869 * (1 - v),
- 0,
- 0
- ],
- [0, 0, 0, 1, 0],
- [0, 0, 0, 0, 1]
- ];
- },
- grayscale(v) {
- return [
- [
- 0.2126 + 0.7874 * (1 - v),
- 0.7152 - 0.7152 * (1 - v),
- 0.0722 - 0.0722 * (1 - v),
- 0,
- 0
- ],
- [
- 0.2126 - 0.2126 * (1 - v),
- 0.7152 + 0.2848 * (1 - v),
- 0.0722 - 0.0722 * (1 - v),
- 0,
- 0
- ],
- [
- 0.2126 - 0.2126 * (1 - v),
- 0.7152 - 0.7152 * (1 - v),
- 0.0722 + 0.9278 * (1 - v),
- 0,
- 0
- ],
- [0, 0, 0, 1, 0],
- [0, 0, 0, 0, 1]
- ];
- }
- };
-
- function getBgPole(theme) {
- const isDarkScheme = theme.mode === 1;
- const prop = isDarkScheme
- ? "darkSchemeBackgroundColor"
- : "lightSchemeBackgroundColor";
- return theme[prop];
- }
- function getFgPole(theme) {
- const isDarkScheme = theme.mode === 1;
- const prop = isDarkScheme
- ? "darkSchemeTextColor"
- : "lightSchemeTextColor";
- return theme[prop];
- }
- const colorModificationCache = new Map();
- function clearColorModificationCache() {
- colorModificationCache.clear();
+ return false;
}
- const rgbCacheKeys = ["r", "g", "b", "a"];
- const themeCacheKeys$1 = [
- "mode",
- "brightness",
- "contrast",
- "grayscale",
- "sepia",
- "darkSchemeBackgroundColor",
- "darkSchemeTextColor",
- "lightSchemeBackgroundColor",
- "lightSchemeTextColor"
- ];
- function getCacheId(rgb, theme) {
- let resultId = "";
- rgbCacheKeys.forEach((key) => {
- resultId += `${rgb[key]};`;
+ function getElementsTreeOperations(mutations) {
+ const additions = new Set();
+ const deletions = new Set();
+ const moves = new Set();
+ mutations.forEach((m) => {
+ forEach(m.addedNodes, (n) => {
+ if (n instanceof Element && n.isConnected) {
+ additions.add(n);
+ }
+ });
+ forEach(m.removedNodes, (n) => {
+ if (n instanceof Element) {
+ if (n.isConnected) {
+ moves.add(n);
+ additions.delete(n);
+ } else {
+ deletions.add(n);
+ }
+ }
+ });
});
- themeCacheKeys$1.forEach((key) => {
- resultId += `${theme[key]};`;
+ const duplicateAdditions = [];
+ const duplicateDeletions = [];
+ additions.forEach((node) => {
+ if (additions.has(node.parentElement)) {
+ duplicateAdditions.push(node);
+ }
});
- return resultId;
+ deletions.forEach((node) => {
+ if (deletions.has(node.parentElement)) {
+ duplicateDeletions.push(node);
+ }
+ });
+ duplicateAdditions.forEach((node) => additions.delete(node));
+ duplicateDeletions.forEach((node) => deletions.delete(node));
+ return {additions, moves, deletions};
}
- function modifyColorWithCache(
- rgb,
- theme,
- modifyHSL,
- poleColor,
- anotherPoleColor
- ) {
- let fnCache;
- if (colorModificationCache.has(modifyHSL)) {
- fnCache = colorModificationCache.get(modifyHSL);
+ const optimizedTreeObservers = new Map();
+ const optimizedTreeCallbacks = new WeakMap();
+ function createOptimizedTreeObserver(root, callbacks) {
+ let observer;
+ let observerCallbacks;
+ let domReadyListener;
+ if (optimizedTreeObservers.has(root)) {
+ observer = optimizedTreeObservers.get(root);
+ observerCallbacks = optimizedTreeCallbacks.get(observer);
} else {
- fnCache = new Map();
- colorModificationCache.set(modifyHSL, fnCache);
- }
- const id = getCacheId(rgb, theme);
- if (fnCache.has(id)) {
- return fnCache.get(id);
+ let hadHugeMutationsBefore = false;
+ let subscribedForReadyState = false;
+ observer = new MutationObserver((mutations) => {
+ if (isHugeMutation(mutations)) {
+ if (!hadHugeMutationsBefore || isDOMReady()) {
+ observerCallbacks.forEach(({onHugeMutations}) =>
+ onHugeMutations(root)
+ );
+ } else if (!subscribedForReadyState) {
+ domReadyListener = () =>
+ observerCallbacks.forEach(({onHugeMutations}) =>
+ onHugeMutations(root)
+ );
+ addDOMReadyListener(domReadyListener);
+ subscribedForReadyState = true;
+ }
+ hadHugeMutationsBefore = true;
+ } else {
+ const elementsOperations =
+ getElementsTreeOperations(mutations);
+ observerCallbacks.forEach(({onMinorMutations}) =>
+ onMinorMutations(root, elementsOperations)
+ );
+ }
+ });
+ observer.observe(root, {childList: true, subtree: true});
+ optimizedTreeObservers.set(root, observer);
+ observerCallbacks = new Set();
+ optimizedTreeCallbacks.set(observer, observerCallbacks);
}
- const hsl = rgbToHSL(rgb);
- const pole = poleColor == null ? null : parseToHSLWithCache(poleColor);
- const anotherPole =
- anotherPoleColor == null
- ? null
- : parseToHSLWithCache(anotherPoleColor);
- const modified = modifyHSL(hsl, pole, anotherPole);
- const { r, g, b, a } = hslToRGB(modified);
- const matrix = createFilterMatrix(theme);
- const [rf, gf, bf] = applyColorMatrix([r, g, b], matrix);
- const color =
- a === 1
- ? rgbToHexString({ r: rf, g: gf, b: bf })
- : rgbToString({ r: rf, g: gf, b: bf, a });
- fnCache.set(id, color);
- return color;
- }
- function noopHSL(hsl) {
- return hsl;
- }
- function modifyColor(rgb, theme) {
- return modifyColorWithCache(rgb, theme, noopHSL);
- }
- function modifyLightSchemeColor(rgb, theme) {
- const poleBg = getBgPole(theme);
- const poleFg = getFgPole(theme);
- return modifyColorWithCache(
- rgb,
- theme,
- modifyLightModeHSL,
- poleFg,
- poleBg
- );
- }
- function modifyLightModeHSL({ h, s, l, a }, poleFg, poleBg) {
- const isDark = l < 0.5;
- let isNeutral;
- if (isDark) {
- isNeutral = l < 0.2 || s < 0.12;
- } else {
- const isBlue = h > 200 && h < 280;
- isNeutral = s < 0.24 || (l > 0.8 && isBlue);
- }
- let hx = h;
- let sx = l;
- if (isNeutral) {
- if (isDark) {
- hx = poleFg.h;
- sx = poleFg.s;
- } else {
- hx = poleBg.h;
- sx = poleBg.s;
+ observerCallbacks.add(callbacks);
+ return {
+ disconnect() {
+ observerCallbacks.delete(callbacks);
+ if (domReadyListener) {
+ removeDOMReadyListener(domReadyListener);
+ }
+ if (observerCallbacks.size === 0) {
+ observer.disconnect();
+ optimizedTreeCallbacks.delete(observer);
+ optimizedTreeObservers.delete(root);
+ }
}
- }
- const lx = scale(l, 0, 1, poleFg.l, poleBg.l);
- return { h: hx, s: sx, l: lx, a };
+ };
}
- const MAX_BG_LIGHTNESS = 0.4;
- function modifyBgHSL({ h, s, l, a }, pole) {
- const isDark = l < 0.5;
- const isBlue = h > 200 && h < 280;
- const isNeutral = s < 0.12 || (l > 0.8 && isBlue);
- if (isDark) {
- const lx = scale(l, 0, 0.5, 0, MAX_BG_LIGHTNESS);
- if (isNeutral) {
- const hx = pole.h;
- const sx = pole.s;
- return { h: hx, s: sx, l: lx, a };
+
+ function iterateCSSRules(rules, iterate, onImportError) {
+ forEach(rules, (rule) => {
+ if (isStyleRule(rule)) {
+ iterate(rule);
+ } else if (isImportRule(rule)) {
+ try {
+ iterateCSSRules(
+ rule.styleSheet.cssRules,
+ iterate,
+ onImportError
+ );
+ } catch (err) {
+ onImportError?.();
+ }
+ } else if (isMediaRule(rule)) {
+ const media = Array.from(rule.media);
+ const isScreenOrAllOrQuery = media.some(
+ (m) =>
+ m.startsWith("screen") ||
+ m.startsWith("all") ||
+ m.startsWith("(")
+ );
+ const isPrintOrSpeech = media.some(
+ (m) => m.startsWith("print") || m.startsWith("speech")
+ );
+ if (isScreenOrAllOrQuery || !isPrintOrSpeech) {
+ iterateCSSRules(rule.cssRules, iterate, onImportError);
+ }
+ } else if (isSupportsRule(rule)) {
+ if (CSS.supports(rule.conditionText)) {
+ iterateCSSRules(rule.cssRules, iterate, onImportError);
+ }
+ } else if (isLayerRule(rule)) {
+ iterateCSSRules(rule.cssRules, iterate, onImportError);
+ } else;
+ });
+ }
+ const shorthandVarDependantProperties = [
+ "background",
+ "border",
+ "border-color",
+ "border-bottom",
+ "border-left",
+ "border-right",
+ "border-top",
+ "outline",
+ "outline-color"
+ ];
+ const shorthandVarDepPropRegexps = isSafari
+ ? shorthandVarDependantProperties.map((prop) => {
+ const regexp = new RegExp(`${prop}:\\s*(.*?)\\s*;`);
+ return [prop, regexp];
+ })
+ : null;
+ function iterateCSSDeclarations(style, iterate) {
+ forEach(style, (property) => {
+ const value = style.getPropertyValue(property).trim();
+ if (!value) {
+ return;
}
- return { h, s, l: lx, a };
- }
- let lx = scale(l, 0.5, 1, MAX_BG_LIGHTNESS, pole.l);
- if (isNeutral) {
- const hx = pole.h;
- const sx = pole.s;
- return { h: hx, s: sx, l: lx, a };
- }
- let hx = h;
- const isYellow = h > 60 && h < 180;
- if (isYellow) {
- const isCloserToGreen = h > 120;
- if (isCloserToGreen) {
- hx = scale(h, 120, 180, 135, 180);
+ iterate(property, value);
+ });
+ const cssText = style.cssText;
+ if (cssText.includes("var(")) {
+ if (isSafari) {
+ shorthandVarDepPropRegexps.forEach(([prop, regexp]) => {
+ const match = cssText.match(regexp);
+ if (match && match[1]) {
+ const val = match[1].trim();
+ iterate(prop, val);
+ }
+ });
} else {
- hx = scale(h, 60, 120, 60, 105);
+ shorthandVarDependantProperties.forEach((prop) => {
+ const val = style.getPropertyValue(prop);
+ if (val && val.includes("var(")) {
+ iterate(prop, val);
+ }
+ });
}
}
- if (hx > 40 && hx < 80) {
- lx *= 0.75;
+ if (
+ cssText.includes("background-color: ;") &&
+ !style.getPropertyValue("background")
+ ) {
+ handleEmptyShorthand("background", style, iterate);
+ }
+ if (
+ cssText.includes("border-") &&
+ cssText.includes("-color: ;") &&
+ !style.getPropertyValue("border")
+ ) {
+ handleEmptyShorthand("border", style, iterate);
}
- return { h: hx, s, l: lx, a };
}
- function modifyBackgroundColor(rgb, theme) {
- if (theme.mode === 0) {
- return modifyLightSchemeColor(rgb, theme);
+ function handleEmptyShorthand(shorthand, style, iterate) {
+ const parentRule = style.parentRule;
+ if (isStyleRule(parentRule)) {
+ const sourceCSSText =
+ parentRule.parentStyleSheet?.ownerNode?.textContent;
+ if (sourceCSSText) {
+ let escapedSelector = escapeRegExpSpecialChars(
+ parentRule.selectorText
+ );
+ escapedSelector = escapedSelector.replaceAll(/\s+/g, "\\s*");
+ escapedSelector = escapedSelector.replaceAll(/::/g, "::?");
+ const regexp = new RegExp(
+ `${escapedSelector}\\s*{[^}]*${shorthand}:\\s*([^;}]+)`
+ );
+ const match = sourceCSSText.match(regexp);
+ if (match) {
+ iterate(shorthand, match[1]);
+ }
+ } else if (shorthand === "background") {
+ iterate("background-color", "#ffffff");
+ }
}
- const pole = getBgPole(theme);
- return modifyColorWithCache(
- rgb,
- { ...theme, mode: 0 },
- modifyBgHSL,
- pole
- );
}
- const MIN_FG_LIGHTNESS = 0.55;
- function modifyBlueFgHue(hue) {
- return scale(hue, 205, 245, 205, 220);
+ const cssURLRegex = /url\((('.*?')|(".*?")|([^\)]*?))\)/g;
+ const cssImportRegex =
+ /@import\s*(url\()?(('.+?')|(".+?")|([^\)]*?))\)? ?(screen)?;?/gi;
+ function getCSSURLValue(cssURL) {
+ return cssURL
+ .trim()
+ .replace(/[\n\r\\]+/g, "")
+ .replace(/^url\((.*)\)$/, "$1")
+ .trim()
+ .replace(/^"(.*)"$/, "$1")
+ .replace(/^'(.*)'$/, "$1")
+ .replace(/(?:\\(.))/g, "$1");
}
- function modifyFgHSL({ h, s, l, a }, pole) {
- const isLight = l > 0.5;
- const isNeutral = l < 0.2 || s < 0.24;
- const isBlue = !isNeutral && h > 205 && h < 245;
- if (isLight) {
- const lx = scale(l, 0.5, 1, MIN_FG_LIGHTNESS, pole.l);
- if (isNeutral) {
- const hx = pole.h;
- const sx = pole.s;
- return { h: hx, s: sx, l: lx, a };
- }
- let hx = h;
- if (isBlue) {
- hx = modifyBlueFgHue(h);
+ function getCSSBaseBath(url) {
+ const cssURL = parseURL(url);
+ return `${cssURL.origin}${cssURL.pathname.replace(/\?.*$/, "").replace(/(\/)([^\/]+)$/i, "$1")}`;
+ }
+ function replaceCSSRelativeURLsWithAbsolute($css, cssBasePath) {
+ return $css.replace(cssURLRegex, (match) => {
+ try {
+ const url = getCSSURLValue(match);
+ const absoluteURL = getAbsoluteURL(cssBasePath, url);
+ const escapedURL = absoluteURL.replaceAll("'", "\\'");
+ return `url('${escapedURL}')`;
+ } catch (err) {
+ return match;
}
- return { h: hx, s, l: lx, a };
+ });
+ }
+ const fontFaceRegex = /@font-face\s*{[^}]*}/g;
+ function replaceCSSFontFace($css) {
+ return $css.replace(fontFaceRegex, "");
+ }
+ const styleRules = new WeakSet();
+ const importRules = new WeakSet();
+ const mediaRules = new WeakSet();
+ const supportsRules = new WeakSet();
+ const layerRules = new WeakSet();
+ function isStyleRule(rule) {
+ if (!rule) {
+ return false;
}
- if (isNeutral) {
- const hx = pole.h;
- const sx = pole.s;
- const lx = scale(l, 0, 0.5, pole.l, MIN_FG_LIGHTNESS);
- return { h: hx, s: sx, l: lx, a };
+ if (styleRules.has(rule)) {
+ return true;
}
- let hx = h;
- let lx;
- if (isBlue) {
- hx = modifyBlueFgHue(h);
- lx = scale(l, 0, 0.5, pole.l, Math.min(1, MIN_FG_LIGHTNESS + 0.05));
- } else {
- lx = scale(l, 0, 0.5, pole.l, MIN_FG_LIGHTNESS);
+ if (rule.selectorText) {
+ styleRules.add(rule);
+ return true;
}
- return { h: hx, s, l: lx, a };
+ return false;
}
- function modifyForegroundColor(rgb, theme) {
- if (theme.mode === 0) {
- return modifyLightSchemeColor(rgb, theme);
+ function isImportRule(rule) {
+ if (!rule) {
+ return false;
}
- const pole = getFgPole(theme);
- return modifyColorWithCache(
- rgb,
- { ...theme, mode: 0 },
- modifyFgHSL,
- pole
- );
- }
- function modifyBorderHSL({ h, s, l, a }, poleFg, poleBg) {
- const isDark = l < 0.5;
- const isNeutral = l < 0.2 || s < 0.24;
- let hx = h;
- let sx = s;
- if (isNeutral) {
- if (isDark) {
- hx = poleFg.h;
- sx = poleFg.s;
- } else {
- hx = poleBg.h;
- sx = poleBg.s;
- }
+ if (styleRules.has(rule)) {
+ return false;
}
- const lx = scale(l, 0, 1, 0.5, 0.2);
- return { h: hx, s: sx, l: lx, a };
- }
- function modifyBorderColor(rgb, theme) {
- if (theme.mode === 0) {
- return modifyLightSchemeColor(rgb, theme);
+ if (importRules.has(rule)) {
+ return true;
}
- const poleFg = getFgPole(theme);
- const poleBg = getBgPole(theme);
- return modifyColorWithCache(
- rgb,
- { ...theme, mode: 0 },
- modifyBorderHSL,
- poleFg,
- poleBg
- );
+ if (rule.href) {
+ importRules.add(rule);
+ return true;
+ }
+ return false;
}
- function modifyShadowColor(rgb, theme) {
- return modifyBackgroundColor(rgb, theme);
+ function isMediaRule(rule) {
+ if (!rule) {
+ return false;
+ }
+ if (styleRules.has(rule)) {
+ return false;
+ }
+ if (mediaRules.has(rule)) {
+ return true;
+ }
+ if (rule.media) {
+ mediaRules.add(rule);
+ return true;
+ }
+ return false;
}
- function modifyGradientColor(rgb, theme) {
- return modifyBackgroundColor(rgb, theme);
+ function isSupportsRule(rule) {
+ if (!rule) {
+ return false;
+ }
+ if (styleRules.has(rule)) {
+ return false;
+ }
+ if (supportsRules.has(rule)) {
+ return true;
+ }
+ if (rule instanceof CSSSupportsRule) {
+ supportsRules.add(rule);
+ return true;
+ }
+ return false;
}
-
- const excludedSelectors = [
- "pre",
- "pre *",
- "code",
- '[aria-hidden="true"]',
- '[class*="fa-"]',
- ".fa",
- ".fab",
- ".fad",
- ".fal",
- ".far",
- ".fas",
- ".fass",
- ".fasr",
- ".fat",
- ".icofont",
- '[style*="font-"]',
- '[class*="icon"]',
- '[class*="Icon"]',
- '[class*="symbol"]',
- '[class*="Symbol"]',
- ".glyphicon",
- '[class*="material-symbol"]',
- '[class*="material-icon"]',
- "mu",
- '[class*="mu-"]',
- ".typcn",
- '[class*="vjs-"]'
- ];
- function createTextStyle(config) {
- const lines = [];
- lines.push(`*:not(${excludedSelectors.join(", ")}) {`);
- if (config.useFont && config.fontFamily) {
- lines.push(` font-family: ${config.fontFamily} !important;`);
+ function isLayerRule(rule) {
+ if (!rule) {
+ return false;
}
- if (config.textStroke > 0) {
- lines.push(
- ` -webkit-text-stroke: ${config.textStroke}px !important;`
- );
- lines.push(` text-stroke: ${config.textStroke}px !important;`);
+ if (styleRules.has(rule)) {
+ return false;
}
- lines.push("}");
- return lines.join("\n");
+ if (layerRules.has(rule)) {
+ return true;
+ }
+ if (isLayerRuleSupported && rule instanceof CSSLayerBlockRule) {
+ layerRules.add(rule);
+ return true;
+ }
+ return false;
}
- var FilterMode;
- (function (FilterMode) {
- FilterMode[(FilterMode["light"] = 0)] = "light";
- FilterMode[(FilterMode["dark"] = 1)] = "dark";
- })(FilterMode || (FilterMode = {}));
- function getCSSFilterValue(config) {
- const filters = [];
- if (config.mode === FilterMode.dark) {
- filters.push("invert(100%) hue-rotate(180deg)");
+ const sheetsScopes = new WeakMap();
+ function defineSheetScope(sheet, node) {
+ sheetsScopes.set(sheet, node);
+ }
+ function getSheetScope(sheet) {
+ if (!sheet.ownerNode) {
+ return null;
}
- if (config.brightness !== 100) {
- filters.push(`brightness(${config.brightness}%)`);
+ if (sheetsScopes.has(sheet)) {
+ return sheetsScopes.get(sheet);
}
- if (config.contrast !== 100) {
- filters.push(`contrast(${config.contrast}%)`);
+ let node = sheet.ownerNode;
+ while (node) {
+ if (node instanceof ShadowRoot || node instanceof Document) {
+ defineSheetScope(sheet, node);
+ return node;
+ }
+ node = node.parentNode;
}
- if (config.grayscale !== 0) {
- filters.push(`grayscale(${config.grayscale}%)`);
+ return null;
+ }
+
+ const gradientLength = "gradient".length;
+ const conicGradient = "conic-";
+ const conicGradientLength = conicGradient.length;
+ const radialGradient = "radial-";
+ const linearGradient = "linear-";
+ function parseGradient(value) {
+ const result = [];
+ let index = 0;
+ let startIndex = conicGradient.length;
+ while ((index = value.indexOf("gradient", startIndex)) !== -1) {
+ let typeGradient;
+ [linearGradient, radialGradient, conicGradient].find(
+ (possibleType) => {
+ if (index - possibleType.length >= 0) {
+ const possibleGradient = value.substring(
+ index - possibleType.length,
+ index
+ );
+ if (possibleGradient === possibleType) {
+ if (
+ value.slice(
+ index - possibleType.length - 10,
+ index - possibleType.length - 1
+ ) === "repeating"
+ ) {
+ typeGradient = `repeating-${possibleType}gradient`;
+ return true;
+ }
+ if (
+ value.slice(
+ index - possibleType.length - 8,
+ index - possibleType.length - 1
+ ) === "-webkit"
+ ) {
+ typeGradient = `-webkit-${possibleType}gradient`;
+ return true;
+ }
+ typeGradient = `${possibleType}gradient`;
+ return true;
+ }
+ }
+ }
+ );
+ if (!typeGradient) {
+ break;
+ }
+ const {start, end} = getParenthesesRange(
+ value,
+ index + gradientLength
+ );
+ const match = value.substring(start + 1, end - 1);
+ startIndex = end + 1 + conicGradientLength;
+ result.push({
+ typeGradient,
+ match,
+ offset: typeGradient.length + 2,
+ index: index - typeGradient.length + gradientLength,
+ hasComma: true
+ });
}
- if (config.sepia !== 0) {
- filters.push(`sepia(${config.sepia}%)`);
+ if (result.length) {
+ result[result.length - 1].hasComma = false;
}
- if (filters.length === 0) {
- return null;
+ return result;
+ }
+
+ const STORAGE_KEY_IMAGE_DETAILS_LIST = "__darkreader__imageDetails_v2_list";
+ const STORAGE_KEY_IMAGE_DETAILS_PREFIX = "__darkreader__imageDetails_v2_";
+ const STORAGE_KEY_CSS_FETCH_PREFIX = "__darkreader__cssFetch_";
+ let imageCacheTimeout = 0;
+ const imageDetailsCacheQueue = new Map();
+ const cachedImageUrls = [];
+ function writeImageDetailsQueue() {
+ imageDetailsCacheQueue.forEach((details, url) => {
+ if (url && url.startsWith("https://")) {
+ try {
+ const json = JSON.stringify(details);
+ sessionStorage.setItem(
+ `${STORAGE_KEY_IMAGE_DETAILS_PREFIX}${url}`,
+ json
+ );
+ cachedImageUrls.push(url);
+ } catch (err) {}
+ }
+ });
+ imageDetailsCacheQueue.clear();
+ sessionStorage.setItem(
+ STORAGE_KEY_IMAGE_DETAILS_LIST,
+ JSON.stringify(cachedImageUrls)
+ );
+ }
+ function writeImageDetailsCache(url, imageDetails) {
+ if (!url || !url.startsWith("https://")) {
+ return;
}
- return filters.join(" ");
+ imageDetailsCacheQueue.set(url, imageDetails);
+ clearTimeout(imageCacheTimeout);
+ imageCacheTimeout = setTimeout(writeImageDetailsQueue, 1000);
+ }
+ function readImageDetailsCache(targetMap) {
+ try {
+ const jsonList = sessionStorage.getItem(
+ STORAGE_KEY_IMAGE_DETAILS_LIST
+ );
+ if (!jsonList) {
+ return;
+ }
+ const list = JSON.parse(jsonList);
+ list.forEach((url) => {
+ const json = sessionStorage.getItem(
+ `${STORAGE_KEY_IMAGE_DETAILS_PREFIX}${url}`
+ );
+ if (json) {
+ const details = JSON.parse(json);
+ targetMap.set(url, details);
+ }
+ });
+ } catch (err) {}
+ }
+ function writeCSSFetchCache(url, cssText) {
+ const key = `${STORAGE_KEY_CSS_FETCH_PREFIX}${url}`;
+ try {
+ sessionStorage.setItem(key, cssText);
+ } catch (err) {}
+ }
+ function readCSSFetchCache(url) {
+ const key = `${STORAGE_KEY_CSS_FETCH_PREFIX}${url}`;
+ try {
+ return sessionStorage.getItem(key) ?? null;
+ } catch (err) {}
+ return null;
}
function toSVGMatrix(matrix) {
@@ -2192,26 +2222,40 @@
return toSVGMatrix(createFilterMatrix(config));
}
- function hexify(number) {
- return (number < 16 ? "0" : "") + number.toString(16);
- }
- function generateUID() {
- if ("randomUUID" in crypto) {
- const uuid = crypto.randomUUID();
- return (
- uuid.substring(0, 8) +
- uuid.substring(9, 13) +
- uuid.substring(14, 18) +
- uuid.substring(19, 23) +
- uuid.substring(24)
- );
+ const MAX_FRAME_DURATION = 1000 / 60;
+ class AsyncQueue {
+ constructor() {
+ this.queue = [];
+ this.timerId = null;
}
- if ("getRandomValues" in crypto) {
- return Array.from(crypto.getRandomValues(new Uint8Array(16)))
- .map((x) => hexify(x))
- .join("");
+ addTask(task) {
+ this.queue.push(task);
+ this.scheduleFrame();
+ }
+ stop() {
+ if (this.timerId !== null) {
+ cancelAnimationFrame(this.timerId);
+ this.timerId = null;
+ }
+ this.queue = [];
+ }
+ scheduleFrame() {
+ if (this.timerId) {
+ return;
+ }
+ this.timerId = requestAnimationFrame(() => {
+ this.timerId = null;
+ const start = Date.now();
+ let cb;
+ while ((cb = this.queue.shift())) {
+ cb();
+ if (Date.now() - start >= MAX_FRAME_DURATION) {
+ this.scheduleFrame();
+ break;
+ }
+ }
+ });
}
- return Math.floor(Math.random() * 2 ** 55).toString(36);
}
const resolvers$1 = new Map();
@@ -2231,7 +2275,7 @@
});
});
}
- chrome.runtime.onMessage.addListener(({ type, data, error, id }) => {
+ chrome.runtime.onMessage.addListener(({type, data, error, id}) => {
if (type === MessageTypeBGtoCS.FETCH_RESPONSE) {
const resolve = resolvers$1.get(id);
const reject = rejectors.get(id);
@@ -2245,42 +2289,6 @@
}
});
- const MAX_FRAME_DURATION = 1000 / 60;
- class AsyncQueue {
- constructor() {
- this.queue = [];
- this.timerId = null;
- }
- addTask(task) {
- this.queue.push(task);
- this.scheduleFrame();
- }
- stop() {
- if (this.timerId !== null) {
- cancelAnimationFrame(this.timerId);
- this.timerId = null;
- }
- this.queue = [];
- }
- scheduleFrame() {
- if (this.timerId) {
- return;
- }
- this.timerId = requestAnimationFrame(() => {
- this.timerId = null;
- const start = Date.now();
- let cb;
- while ((cb = this.queue.shift())) {
- cb();
- if (Date.now() - start >= MAX_FRAME_DURATION) {
- this.scheduleFrame();
- break;
- }
- }
- });
- }
- }
-
const imageManager = new AsyncQueue();
async function getImageDetails(url) {
return new Promise(async (resolve, reject) => {
@@ -2319,7 +2327,7 @@
if (parsedURL.origin === location.origin) {
return await loadAsDataURL(url);
}
- return await bgFetch({ url, responseType: "data-url" });
+ return await bgFetch({url, responseType: "data-url"});
}
async function tryCreateImageBitmap(blob) {
try {
@@ -2357,7 +2365,7 @@
canvas = document.createElement("canvas");
canvas.width = maxWidth;
canvas.height = maxHeight;
- context = canvas.getContext("2d", { willReadFrequently: true });
+ context = canvas.getContext("2d", {willReadFrequently: true});
context.imageSmoothingEnabled = false;
}
function removeCanvas() {
@@ -2449,7 +2457,7 @@
document.addEventListener(
"__darkreader__inlineScriptsAllowed",
() => (canUseProxy = true),
- { once: true }
+ {once: true}
);
async function requestBlobURLCheck() {
if (!canUseProxy) {
@@ -2470,7 +2478,7 @@
blobURLCheckAwaiters.forEach((r) => r());
blobURLCheckAwaiters.splice(0);
},
- { once: true }
+ {once: true}
);
document.dispatchEvent(
new CustomEvent("__darkreader__blobURLCheckRequest")
@@ -2488,7 +2496,7 @@
}
document.addEventListener("securitypolicyviolation", onCSPError);
const objectURLs = new Set();
- function getFilteredImageURL({ dataURL, width, height }, theme) {
+ function getFilteredImageURL({dataURL, width, height}, theme) {
if (dataURL.startsWith("data:image/svg+xml")) {
dataURL = escapeXML(dataURL);
}
@@ -2510,7 +2518,7 @@
for (let i = 0; i < svg.length; i++) {
bytes[i] = svg.charCodeAt(i);
}
- const blob = new Blob([bytes], { type: "image/svg+xml" });
+ const blob = new Blob([bytes], {type: "image/svg+xml"});
const objectURL = URL.createObjectURL(blob);
objectURLs.add(objectURL);
return objectURL;
@@ -2542,7 +2550,7 @@
for (let i = 0; i < characters.length; i++) {
bytes[i] = characters.charCodeAt(i);
}
- return new Blob([bytes], { type: mediaType });
+ return new Blob([bytes], {type: mediaType});
}
async function tryConvertDataURLToBlobURL(dataURL) {
if (!isBlobURLSupported) {
@@ -2571,2156 +2579,1891 @@
dataURLBlobURLs.clear();
}
- const gradientLength = "gradient".length;
- const conicGradient = "conic-";
- const conicGradientLength = conicGradient.length;
- const radialGradient = "radial-";
- const linearGradient = "linear-";
- function parseGradient(value) {
- const result = [];
- let index = 0;
- let startIndex = conicGradient.length;
- while ((index = value.indexOf("gradient", startIndex)) !== -1) {
- let typeGradient;
- [linearGradient, radialGradient, conicGradient].find(
- (possibleType) => {
- if (index - possibleType.length >= 0) {
- const possibleGradient = value.substring(
- index - possibleType.length,
- index
- );
- if (possibleGradient === possibleType) {
- if (
- value.slice(
- index - possibleType.length - 10,
- index - possibleType.length - 1
- ) === "repeating"
- ) {
- typeGradient = `repeating-${possibleType}gradient`;
- return true;
- }
- if (
- value.slice(
- index - possibleType.length - 8,
- index - possibleType.length - 1
- ) === "-webkit"
- ) {
- typeGradient = `-webkit-${possibleType}gradient`;
- return true;
- }
- typeGradient = `${possibleType}gradient`;
- return true;
- }
- }
+ let variablesSheet;
+ const registeredColors = new Map();
+ function registerVariablesSheet(sheet) {
+ variablesSheet = sheet;
+ const types = ["background", "text", "border"];
+ registeredColors.forEach((registered) => {
+ types.forEach((type) => {
+ if (registered[type]) {
+ const {variable, value} = registered[type];
+ variablesSheet?.cssRules[0].style.setProperty(
+ variable,
+ value
+ );
}
- );
- if (!typeGradient) {
- break;
- }
- const { start, end } = getParenthesesRange(
- value,
- index + gradientLength
- );
- const match = value.substring(start + 1, end - 1);
- startIndex = end + 1 + conicGradientLength;
- result.push({
- typeGradient,
- match,
- offset: typeGradient.length + 2,
- index: index - typeGradient.length + gradientLength,
- hasComma: true
});
+ });
+ }
+ function releaseVariablesSheet() {
+ variablesSheet = null;
+ clearColorPalette();
+ }
+ function getRegisteredVariableValue(type, registered) {
+ return `var(${registered[type].variable}, ${registered[type].value})`;
+ }
+ function getRegisteredColor(type, parsed) {
+ const hex = rgbToHexString(parsed);
+ const registered = registeredColors.get(hex);
+ if (registered?.[type]) {
+ return getRegisteredVariableValue(type, registered);
}
- if (result.length) {
- result[result.length - 1].hasComma = false;
+ return null;
+ }
+ function registerColor(type, parsed, value) {
+ const hex = rgbToHexString(parsed);
+ let registered;
+ if (registeredColors.has(hex)) {
+ registered = registeredColors.get(hex);
+ } else {
+ const parsed = parseColorWithCache(hex);
+ registered = {parsed};
+ registeredColors.set(hex, registered);
}
- return result;
+ const variable = `--darkreader-${type}-${hex.replace("#", "")}`;
+ registered[type] = {variable, value};
+ if (variablesSheet?.cssRules[0]?.style) {
+ variablesSheet?.cssRules[0].style.setProperty(variable, value);
+ }
+ return getRegisteredVariableValue(type, registered);
+ }
+ function getColorPalette() {
+ const background = [];
+ const border = [];
+ const text = [];
+ registeredColors.forEach((registered) => {
+ if (registered.background) {
+ background.push(registered.parsed);
+ }
+ if (registered.border) {
+ border.push(registered.parsed);
+ }
+ if (registered.text) {
+ text.push(registered.parsed);
+ }
+ });
+ return {background, border, text};
+ }
+ function clearColorPalette() {
+ registeredColors.clear();
}
- function getPriority(ruleStyle, property) {
- return Boolean(ruleStyle && ruleStyle.getPropertyPriority(property));
+ function getBgPole(theme) {
+ const isDarkScheme = theme.mode === 1;
+ const prop = isDarkScheme
+ ? "darkSchemeBackgroundColor"
+ : "lightSchemeBackgroundColor";
+ return theme[prop];
}
- function getModifiableCSSDeclaration(
- property,
- value,
- rule,
- variablesStore,
- ignoreImageSelectors,
- isCancelled
- ) {
- let modifier = null;
- if (property.startsWith("--")) {
- modifier = getVariableModifier(
- variablesStore,
- property,
- value,
- rule,
- ignoreImageSelectors,
- isCancelled
- );
- } else if (value.includes("var(")) {
- modifier = getVariableDependantModifier(
- variablesStore,
- property,
- value
- );
- } else if (property === "color-scheme") {
- modifier = getColorSchemeModifier();
- } else if (property === "scrollbar-color") {
- modifier = getScrollbarColorModifier(value);
- } else if (
- (property.includes("color") &&
- property !== "-webkit-print-color-adjust") ||
- property === "fill" ||
- property === "stroke" ||
- property === "stop-color"
- ) {
- if (
- property.startsWith("border") &&
- property !== "border-color" &&
- value === "initial"
- ) {
- const borderSideProp = property.substring(
- 0,
- property.length - 6
- );
- const borderSideVal =
- rule.style.getPropertyValue(borderSideProp);
- if (
- borderSideVal.startsWith("0px") ||
- borderSideVal === "none"
- ) {
- property = borderSideProp;
- modifier = borderSideVal;
- } else {
- modifier = value;
- }
- } else {
- modifier = getColorModifier(property, value, rule);
- }
- } else if (
- property === "background-image" ||
- property === "list-style-image"
- ) {
- modifier = getBgImageModifier(
- value,
- rule,
- ignoreImageSelectors,
- isCancelled
- );
- } else if (property.includes("shadow")) {
- modifier = getShadowModifier(value);
- }
- if (!modifier) {
- return null;
- }
- return {
- property,
- value: modifier,
- important: getPriority(rule.style, property),
- sourceValue: value
- };
+ function getFgPole(theme) {
+ const isDarkScheme = theme.mode === 1;
+ const prop = isDarkScheme
+ ? "darkSchemeTextColor"
+ : "lightSchemeTextColor";
+ return theme[prop];
}
- function joinSelectors(...selectors) {
- return selectors.filter(Boolean).join(", ");
+ const colorModificationCache = new Map();
+ function clearColorModificationCache() {
+ colorModificationCache.clear();
}
- function getModifiedUserAgentStyle(theme, isIFrame, styleSystemControls) {
- const lines = [];
- if (!isIFrame) {
- lines.push("html {");
- lines.push(
- ` background-color: ${modifyBackgroundColor({ r: 255, g: 255, b: 255 }, theme)} !important;`
- );
- lines.push("}");
- }
- if (isCSSColorSchemePropSupported && theme.mode === 1) {
- lines.push("html {");
- lines.push(` color-scheme: dark !important;`);
- lines.push("}");
- lines.push("iframe {");
- lines.push(` color-scheme: initial;`);
- lines.push("}");
- }
- const bgSelectors = joinSelectors(
- isIFrame ? "" : "html, body",
- styleSystemControls ? "input, textarea, select, button, dialog" : ""
- );
- if (bgSelectors) {
- lines.push(`${bgSelectors} {`);
- lines.push(
- ` background-color: ${modifyBackgroundColor({ r: 255, g: 255, b: 255 }, theme)};`
- );
- lines.push("}");
- }
- lines.push(
- `${joinSelectors("html, body", styleSystemControls ? "input, textarea, select, button" : "")} {`
- );
- lines.push(
- ` border-color: ${modifyBorderColor({ r: 76, g: 76, b: 76 }, theme)};`
- );
- lines.push(
- ` color: ${modifyForegroundColor({ r: 0, g: 0, b: 0 }, theme)};`
- );
- lines.push("}");
- lines.push("a {");
- lines.push(
- ` color: ${modifyForegroundColor({ r: 0, g: 64, b: 255 }, theme)};`
- );
- lines.push("}");
- lines.push("table {");
- lines.push(
- ` border-color: ${modifyBorderColor({ r: 128, g: 128, b: 128 }, theme)};`
- );
- lines.push("}");
- lines.push("mark {");
- lines.push(
- ` color: ${modifyForegroundColor({ r: 0, g: 0, b: 0 }, theme)};`
- );
- lines.push("}");
- lines.push("::placeholder {");
- lines.push(
- ` color: ${modifyForegroundColor({ r: 169, g: 169, b: 169 }, theme)};`
- );
- lines.push("}");
- lines.push("input:-webkit-autofill,");
- lines.push("textarea:-webkit-autofill,");
- lines.push("select:-webkit-autofill {");
- lines.push(
- ` background-color: ${modifyBackgroundColor({ r: 250, g: 255, b: 189 }, theme)} !important;`
- );
- lines.push(
- ` color: ${modifyForegroundColor({ r: 0, g: 0, b: 0 }, theme)} !important;`
- );
- lines.push("}");
- if (theme.scrollbarColor) {
- lines.push(getModifiedScrollbarStyle(theme));
- }
- if (theme.selectionColor) {
- lines.push(getModifiedSelectionStyle(theme));
+ const rgbCacheKeys = ["r", "g", "b", "a"];
+ const themeCacheKeys$1 = [
+ "mode",
+ "brightness",
+ "contrast",
+ "grayscale",
+ "sepia",
+ "darkSchemeBackgroundColor",
+ "darkSchemeTextColor",
+ "lightSchemeBackgroundColor",
+ "lightSchemeTextColor"
+ ];
+ function getCacheId(rgb, theme) {
+ let resultId = "";
+ rgbCacheKeys.forEach((key) => {
+ resultId += `${rgb[key]};`;
+ });
+ themeCacheKeys$1.forEach((key) => {
+ resultId += `${theme[key]};`;
+ });
+ return resultId;
+ }
+ function modifyColorWithCache(
+ rgb,
+ theme,
+ modifyHSL,
+ poleColor,
+ anotherPoleColor
+ ) {
+ let fnCache;
+ if (colorModificationCache.has(modifyHSL)) {
+ fnCache = colorModificationCache.get(modifyHSL);
+ } else {
+ fnCache = new Map();
+ colorModificationCache.set(modifyHSL, fnCache);
}
- if (isLayerRuleSupported) {
- lines.unshift("@layer {");
- lines.push("}");
+ const id = getCacheId(rgb, theme);
+ if (fnCache.has(id)) {
+ return fnCache.get(id);
}
- return lines.join("\n");
+ const hsl = rgbToHSL(rgb);
+ const pole = poleColor == null ? null : parseToHSLWithCache(poleColor);
+ const anotherPole =
+ anotherPoleColor == null
+ ? null
+ : parseToHSLWithCache(anotherPoleColor);
+ const modified = modifyHSL(hsl, pole, anotherPole);
+ const {r, g, b, a} = hslToRGB(modified);
+ const matrix = createFilterMatrix(theme);
+ const [rf, gf, bf] = applyColorMatrix([r, g, b], matrix);
+ const color =
+ a === 1
+ ? rgbToHexString({r: rf, g: gf, b: bf})
+ : rgbToString({r: rf, g: gf, b: bf, a});
+ fnCache.set(id, color);
+ return color;
}
- function getSelectionColor(theme) {
- let backgroundColorSelection;
- let foregroundColorSelection;
- if (theme.selectionColor === "auto") {
- backgroundColorSelection = modifyBackgroundColor(
- { r: 0, g: 96, b: 212 },
- { ...theme, grayscale: 0 }
- );
- foregroundColorSelection = modifyForegroundColor(
- { r: 255, g: 255, b: 255 },
- { ...theme, grayscale: 0 }
- );
- } else {
- const rgb = parseColorWithCache(theme.selectionColor);
- const hsl = rgbToHSL(rgb);
- backgroundColorSelection = theme.selectionColor;
- if (hsl.l < 0.5) {
- foregroundColorSelection = "#FFF";
- } else {
- foregroundColorSelection = "#000";
- }
+ function modifyAndRegisterColor(type, rgb, theme, modifier) {
+ const registered = getRegisteredColor(type, rgb);
+ if (registered) {
+ return registered;
}
- return { backgroundColorSelection, foregroundColorSelection };
+ const value = modifier(rgb, theme);
+ return registerColor(type, rgb, value);
}
- function getModifiedSelectionStyle(theme) {
- const lines = [];
- const modifiedSelectionColor = getSelectionColor(theme);
- const backgroundColorSelection =
- modifiedSelectionColor.backgroundColorSelection;
- const foregroundColorSelection =
- modifiedSelectionColor.foregroundColorSelection;
- ["::selection", "::-moz-selection"].forEach((selection) => {
- lines.push(`${selection} {`);
- lines.push(
- ` background-color: ${backgroundColorSelection} !important;`
- );
- lines.push(` color: ${foregroundColorSelection} !important;`);
- lines.push("}");
- });
- return lines.join("\n");
+ function modifyLightSchemeColor(rgb, theme) {
+ const poleBg = getBgPole(theme);
+ const poleFg = getFgPole(theme);
+ return modifyColorWithCache(
+ rgb,
+ theme,
+ modifyLightModeHSL,
+ poleFg,
+ poleBg
+ );
}
- function getModifiedScrollbarStyle(theme) {
- const lines = [];
- let colorTrack;
- let colorIcons;
- let colorThumb;
- let colorThumbHover;
- let colorThumbActive;
- let colorCorner;
- if (theme.scrollbarColor === "auto") {
- colorTrack = modifyBackgroundColor({ r: 241, g: 241, b: 241 }, theme);
- colorIcons = modifyForegroundColor({ r: 96, g: 96, b: 96 }, theme);
- colorThumb = modifyBackgroundColor({ r: 176, g: 176, b: 176 }, theme);
- colorThumbHover = modifyBackgroundColor(
- { r: 144, g: 144, b: 144 },
- theme
- );
- colorThumbActive = modifyBackgroundColor(
- { r: 96, g: 96, b: 96 },
- theme
- );
- colorCorner = modifyBackgroundColor(
- { r: 255, g: 255, b: 255 },
- theme
- );
+ function modifyLightModeHSL({h, s, l, a}, poleFg, poleBg) {
+ const isDark = l < 0.5;
+ let isNeutral;
+ if (isDark) {
+ isNeutral = l < 0.2 || s < 0.12;
} else {
- const rgb = parseColorWithCache(theme.scrollbarColor);
- const hsl = rgbToHSL(rgb);
- const isLight = hsl.l > 0.5;
- const lighten = (lighter) => ({
- ...hsl,
- l: clamp(hsl.l + lighter, 0, 1)
- });
- const darken = (darker) => ({
- ...hsl,
- l: clamp(hsl.l - darker, 0, 1)
- });
- colorTrack = hslToString(darken(0.4));
- colorIcons = hslToString(isLight ? darken(0.4) : lighten(0.4));
- colorThumb = hslToString(hsl);
- colorThumbHover = hslToString(lighten(0.1));
- colorThumbActive = hslToString(lighten(0.2));
- colorCorner = hslToString(darken(0.5));
+ const isBlue = h > 200 && h < 280;
+ isNeutral = s < 0.24 || (l > 0.8 && isBlue);
}
- lines.push("::-webkit-scrollbar {");
- lines.push(` background-color: ${colorTrack};`);
- lines.push(` color: ${colorIcons};`);
- lines.push("}");
- lines.push("::-webkit-scrollbar-thumb {");
- lines.push(` background-color: ${colorThumb};`);
- lines.push("}");
- lines.push("::-webkit-scrollbar-thumb:hover {");
- lines.push(` background-color: ${colorThumbHover};`);
- lines.push("}");
- lines.push("::-webkit-scrollbar-thumb:active {");
- lines.push(` background-color: ${colorThumbActive};`);
- lines.push("}");
- lines.push("::-webkit-scrollbar-corner {");
- lines.push(` background-color: ${colorCorner};`);
- lines.push("}");
- if (isFirefox) {
- lines.push("* {");
- lines.push(` scrollbar-color: ${colorThumb} ${colorTrack};`);
- lines.push("}");
+ let hx = h;
+ let sx = l;
+ if (isNeutral) {
+ if (isDark) {
+ hx = poleFg.h;
+ sx = poleFg.s;
+ } else {
+ hx = poleBg.h;
+ sx = poleBg.s;
+ }
}
- return lines.join("\n");
- }
- function getModifiedFallbackStyle(theme, { strict }) {
- const factory = defaultFallbackFactory;
- return factory(theme, { strict });
- }
- function defaultFallbackFactory(theme, { strict }) {
- const lines = [];
- lines.push(
- `html, body, ${strict ? "body :not(iframe)" : "body > :not(iframe)"} {`
- );
- lines.push(
- ` background-color: ${modifyBackgroundColor({ r: 255, g: 255, b: 255 }, theme)} !important;`
- );
- lines.push(
- ` border-color: ${modifyBorderColor({ r: 64, g: 64, b: 64 }, theme)} !important;`
- );
- lines.push(
- ` color: ${modifyForegroundColor({ r: 0, g: 0, b: 0 }, theme)} !important;`
- );
- lines.push("}");
- lines.push(`div[style*="background-color: rgb(135, 135, 135)"] {`);
- lines.push(` background-color: #878787 !important;`);
- lines.push("}");
- return lines.join("\n");
+ const lx = scale(l, 0, 1, poleFg.l, poleBg.l);
+ return {h: hx, s: sx, l: lx, a};
}
- const unparsableColors = new Set([
- "inherit",
- "transparent",
- "initial",
- "currentcolor",
- "none",
- "unset",
- "auto"
- ]);
- function getColorModifier(prop, value, rule) {
- if (unparsableColors.has(value.toLowerCase())) {
- return value;
+ const MAX_BG_LIGHTNESS = 0.4;
+ function modifyBgHSL({h, s, l, a}, pole) {
+ const isDark = l < 0.5;
+ const isBlue = h > 200 && h < 280;
+ const isNeutral = s < 0.12 || (l > 0.8 && isBlue);
+ if (isDark) {
+ const lx = scale(l, 0, 0.5, 0, MAX_BG_LIGHTNESS);
+ if (isNeutral) {
+ const hx = pole.h;
+ const sx = pole.s;
+ return {h: hx, s: sx, l: lx, a};
+ }
+ return {h, s, l: lx, a};
}
- const rgb = parseColorWithCache(value);
- if (!rgb) {
- return null;
+ let lx = scale(l, 0.5, 1, MAX_BG_LIGHTNESS, pole.l);
+ if (isNeutral) {
+ const hx = pole.h;
+ const sx = pole.s;
+ return {h: hx, s: sx, l: lx, a};
}
- if (prop.includes("background")) {
- if (
- (rule.style.webkitMaskImage &&
- rule.style.webkitMaskImage !== "none") ||
- (rule.style.webkitMask &&
- !rule.style.webkitMask.startsWith("none")) ||
- (rule.style.mask && rule.style.mask !== "none") ||
- (rule.style.getPropertyValue("mask-image") &&
- rule.style.getPropertyValue("mask-image") !== "none")
- ) {
- return (theme) => modifyForegroundColor(rgb, theme);
+ let hx = h;
+ const isYellow = h > 60 && h < 180;
+ if (isYellow) {
+ const isCloserToGreen = h > 120;
+ if (isCloserToGreen) {
+ hx = scale(h, 120, 180, 135, 180);
+ } else {
+ hx = scale(h, 60, 120, 60, 105);
}
- return (theme) => modifyBackgroundColor(rgb, theme);
}
- if (prop.includes("border") || prop.includes("outline")) {
- return (theme) => modifyBorderColor(rgb, theme);
+ if (hx > 40 && hx < 80) {
+ lx *= 0.75;
}
- return (theme) => modifyForegroundColor(rgb, theme);
+ return {h: hx, s, l: lx, a};
}
- const imageDetailsCache = new Map();
- const awaitingForImageLoading = new Map();
- function shouldIgnoreImage(selectorText, selectors) {
- if (!selectorText || selectors.length === 0) {
- return false;
+ function _modifyBackgroundColor(rgb, theme) {
+ if (theme.mode === 0) {
+ return modifyLightSchemeColor(rgb, theme);
}
- if (selectors.some((s) => s === "*")) {
- return true;
+ const pole = getBgPole(theme);
+ return modifyColorWithCache(
+ rgb,
+ {...theme, mode: 0},
+ modifyBgHSL,
+ pole
+ );
+ }
+ function modifyBackgroundColor(
+ rgb,
+ theme,
+ shouldRegisterColorVariable = true
+ ) {
+ if (!shouldRegisterColorVariable) {
+ return _modifyBackgroundColor(rgb, theme);
}
- const ruleSelectors = selectorText.split(/,\s*/g);
- for (let i = 0; i < selectors.length; i++) {
- const ignoredSelector = selectors[i];
- if (ruleSelectors.some((s) => s === ignoredSelector)) {
- return true;
+ return modifyAndRegisterColor(
+ "background",
+ rgb,
+ theme,
+ _modifyBackgroundColor
+ );
+ }
+ const MIN_FG_LIGHTNESS = 0.55;
+ function modifyBlueFgHue(hue) {
+ return scale(hue, 205, 245, 205, 220);
+ }
+ function modifyFgHSL({h, s, l, a}, pole) {
+ const isLight = l > 0.5;
+ const isNeutral = l < 0.2 || s < 0.24;
+ const isBlue = !isNeutral && h > 205 && h < 245;
+ if (isLight) {
+ const lx = scale(l, 0.5, 1, MIN_FG_LIGHTNESS, pole.l);
+ if (isNeutral) {
+ const hx = pole.h;
+ const sx = pole.s;
+ return {h: hx, s: sx, l: lx, a};
+ }
+ let hx = h;
+ if (isBlue) {
+ hx = modifyBlueFgHue(h);
}
+ return {h: hx, s, l: lx, a};
}
- return false;
+ if (isNeutral) {
+ const hx = pole.h;
+ const sx = pole.s;
+ const lx = scale(l, 0, 0.5, pole.l, MIN_FG_LIGHTNESS);
+ return {h: hx, s: sx, l: lx, a};
+ }
+ let hx = h;
+ let lx;
+ if (isBlue) {
+ hx = modifyBlueFgHue(h);
+ lx = scale(l, 0, 0.5, pole.l, Math.min(1, MIN_FG_LIGHTNESS + 0.05));
+ } else {
+ lx = scale(l, 0, 0.5, pole.l, MIN_FG_LIGHTNESS);
+ }
+ return {h: hx, s, l: lx, a};
}
- function getBgImageModifier(
+ function _modifyForegroundColor(rgb, theme) {
+ if (theme.mode === 0) {
+ return modifyLightSchemeColor(rgb, theme);
+ }
+ const pole = getFgPole(theme);
+ return modifyColorWithCache(
+ rgb,
+ {...theme, mode: 0},
+ modifyFgHSL,
+ pole
+ );
+ }
+ function modifyForegroundColor(
+ rgb,
+ theme,
+ shouldRegisterColorVariable = true
+ ) {
+ if (!shouldRegisterColorVariable) {
+ return _modifyForegroundColor(rgb, theme);
+ }
+ return modifyAndRegisterColor(
+ "text",
+ rgb,
+ theme,
+ _modifyForegroundColor
+ );
+ }
+ function modifyBorderHSL({h, s, l, a}, poleFg, poleBg) {
+ const isDark = l < 0.5;
+ const isNeutral = l < 0.2 || s < 0.24;
+ let hx = h;
+ let sx = s;
+ if (isNeutral) {
+ if (isDark) {
+ hx = poleFg.h;
+ sx = poleFg.s;
+ } else {
+ hx = poleBg.h;
+ sx = poleBg.s;
+ }
+ }
+ const lx = scale(l, 0, 1, 0.5, 0.2);
+ return {h: hx, s: sx, l: lx, a};
+ }
+ function _modifyBorderColor(rgb, theme) {
+ if (theme.mode === 0) {
+ return modifyLightSchemeColor(rgb, theme);
+ }
+ const poleFg = getFgPole(theme);
+ const poleBg = getBgPole(theme);
+ return modifyColorWithCache(
+ rgb,
+ {...theme, mode: 0},
+ modifyBorderHSL,
+ poleFg,
+ poleBg
+ );
+ }
+ function modifyBorderColor(rgb, theme, shouldRegisterColorVariable = true) {
+ if (!shouldRegisterColorVariable) {
+ return _modifyBorderColor(rgb, theme);
+ }
+ return modifyAndRegisterColor("border", rgb, theme, _modifyBorderColor);
+ }
+ function modifyShadowColor(rgb, theme) {
+ return modifyBackgroundColor(rgb, theme);
+ }
+ function modifyGradientColor(rgb, theme) {
+ return modifyBackgroundColor(rgb, theme);
+ }
+
+ function getPriority(ruleStyle, property) {
+ return Boolean(ruleStyle && ruleStyle.getPropertyPriority(property));
+ }
+ function getModifiableCSSDeclaration(
+ property,
value,
rule,
+ variablesStore,
ignoreImageSelectors,
isCancelled
) {
- try {
- const gradients = parseGradient(value);
- const urls = getMatches(cssURLRegex, value);
- if (urls.length === 0 && gradients.length === 0) {
- return value;
- }
- const getIndices = (matches) => {
- let index = 0;
- return matches.map((match) => {
- const valueIndex = value.indexOf(match, index);
- index = valueIndex + match.length;
- return { match, index: valueIndex };
- });
- };
- const matches = gradients
- .map((i) => ({ type: "gradient", ...i }))
- .concat(
- getIndices(urls).map((i) => ({
- type: "url",
- offset: 0,
- ...i
- }))
- )
- .sort((a, b) => (a.index > b.index ? 1 : -1));
- const getGradientModifier = (gradient) => {
- const { typeGradient, match, hasComma } = gradient;
- const partsRegex =
- /([^\(\),]+(\([^\(\)]*(\([^\(\)]*\)*[^\(\)]*)?\))?([^\(\), ]|( (?!calc)))*),?/g;
- const colorStopRegex =
- /^(from|color-stop|to)\(([^\(\)]*?,\s*)?(.*?)\)$/;
- const parts = getMatches(partsRegex, match, 1).map((part) => {
- part = part.trim();
- let rgb = parseColorWithCache(part);
- if (rgb) {
- return (theme) => modifyGradientColor(rgb, theme);
- }
- const space = part.lastIndexOf(" ");
- rgb = parseColorWithCache(part.substring(0, space));
- if (rgb) {
- return (theme) =>
- `${modifyGradientColor(rgb, theme)} ${part.substring(space + 1)}`;
- }
- const colorStopMatch = part.match(colorStopRegex);
- if (colorStopMatch) {
- rgb = parseColorWithCache(colorStopMatch[3]);
- if (rgb) {
- return (theme) =>
- `${colorStopMatch[1]}(${colorStopMatch[2] ? `${colorStopMatch[2]}, ` : ""}${modifyGradientColor(rgb, theme)})`;
- }
- }
- return () => part;
- });
- return (theme) => {
- return `${typeGradient}(${parts.map((modify) => modify(theme)).join(", ")})${hasComma ? ", " : ""}`;
- };
- };
- const getURLModifier = (urlValue) => {
+ let modifier = null;
+ if (property.startsWith("--")) {
+ modifier = getVariableModifier(
+ variablesStore,
+ property,
+ value,
+ rule,
+ ignoreImageSelectors,
+ isCancelled
+ );
+ } else if (value.includes("var(")) {
+ modifier = getVariableDependantModifier(
+ variablesStore,
+ property,
+ value
+ );
+ } else if (property === "color-scheme") {
+ modifier = getColorSchemeModifier();
+ } else if (property === "scrollbar-color") {
+ modifier = getScrollbarColorModifier(value);
+ } else if (
+ (property.includes("color") &&
+ property !== "-webkit-print-color-adjust") ||
+ property === "fill" ||
+ property === "stroke" ||
+ property === "stop-color"
+ ) {
+ if (
+ property.startsWith("border") &&
+ property !== "border-color" &&
+ value === "initial"
+ ) {
+ const borderSideProp = property.substring(
+ 0,
+ property.length - 6
+ );
+ const borderSideVal =
+ rule.style.getPropertyValue(borderSideProp);
if (
- shouldIgnoreImage(rule.selectorText, ignoreImageSelectors)
+ borderSideVal.startsWith("0px") ||
+ borderSideVal === "none"
) {
- return null;
- }
- let url = getCSSURLValue(urlValue);
- const isURLEmpty = url.length === 0;
- const { parentStyleSheet } = rule;
- const baseURL =
- parentStyleSheet && parentStyleSheet.href
- ? getCSSBaseBath(parentStyleSheet.href)
- : parentStyleSheet?.ownerNode?.baseURI ||
- location.origin;
- url = getAbsoluteURL(baseURL, url);
- return async (theme) => {
- if (isURLEmpty) {
- return "url('')";
- }
- let imageDetails = null;
- if (imageDetailsCache.has(url)) {
- imageDetails = imageDetailsCache.get(url);
- } else {
- try {
- if (!isBlobURLCheckResultReady()) {
- await requestBlobURLCheck();
- }
- if (awaitingForImageLoading.has(url)) {
- const awaiters =
- awaitingForImageLoading.get(url);
- imageDetails = await new Promise((resolve) =>
- awaiters.push(resolve)
- );
- if (!imageDetails) {
- return null;
- }
- } else {
- awaitingForImageLoading.set(url, []);
- imageDetails = await getImageDetails(url);
- imageDetailsCache.set(url, imageDetails);
- awaitingForImageLoading
- .get(url)
- .forEach((resolve) =>
- resolve(imageDetails)
- );
- awaitingForImageLoading.delete(url);
- }
- if (isCancelled()) {
- return null;
- }
- } catch (err) {
- logWarn(err);
- if (awaitingForImageLoading.has(url)) {
- awaitingForImageLoading
- .get(url)
- .forEach((resolve) => resolve(null));
- awaitingForImageLoading.delete(url);
- }
- }
- }
- if (imageDetails) {
- const bgImageValue = getBgImageValue(
- imageDetails,
- theme
- );
- if (bgImageValue) {
- return bgImageValue;
- }
- }
- if (url.startsWith("data:")) {
- const blobURL = await tryConvertDataURLToBlobURL(url);
- if (blobURL) {
- return `url("${blobURL}")`;
- }
- }
- return `url("${url}")`;
- };
- };
- const getBgImageValue = (imageDetails, theme) => {
- const { isDark, isLight, isTransparent, isLarge, width } =
- imageDetails;
- let result;
- const logSrc = imageDetails.src.startsWith("data:")
- ? "data:"
- : imageDetails.src;
- if (isLarge && isLight && !isTransparent && theme.mode === 1) {
- logInfo(`Hiding large light image ${logSrc}`);
- result = "none";
- } else if (
- isDark &&
- isTransparent &&
- theme.mode === 1 &&
- width > 2
- ) {
- logInfo(`Inverting dark image ${logSrc}`);
- const inverted = getFilteredImageURL(imageDetails, {
- ...theme,
- sepia: clamp(theme.sepia + 10, 0, 100)
- });
- result = `url("${inverted}")`;
- } else if (isLight && !isTransparent && theme.mode === 1) {
- logInfo(`Dimming light image ${logSrc}`);
- const dimmed = getFilteredImageURL(imageDetails, theme);
- result = `url("${dimmed}")`;
- } else if (theme.mode === 0 && isLight) {
- logInfo(`Applying filter to image ${logSrc}`);
- const filtered = getFilteredImageURL(imageDetails, {
- ...theme,
- brightness: clamp(theme.brightness - 10, 5, 200),
- sepia: clamp(theme.sepia + 10, 0, 100)
- });
- result = `url("${filtered}")`;
- } else {
- logInfo(`Not modifying the image ${logSrc}`);
- result = null;
- }
- return result;
- };
- const modifiers = [];
- let matchIndex = 0;
- let prevHasComma = false;
- matches.forEach(
- ({ type, match, index, typeGradient, hasComma, offset }, i) => {
- const matchStart = index;
- const prefixStart = matchIndex;
- const matchEnd = matchStart + match.length + offset;
- matchIndex = matchEnd;
- if (prefixStart !== matchStart) {
- if (prevHasComma) {
- modifiers.push(() => {
- let betweenValue = value.substring(
- prefixStart,
- matchStart
- );
- if (betweenValue[0] === ",") {
- betweenValue = betweenValue.substring(1);
- }
- return betweenValue;
- });
- } else {
- modifiers.push(() =>
- value.substring(prefixStart, matchStart)
- );
- }
- }
- prevHasComma = hasComma || false;
- if (type === "url") {
- modifiers.push(getURLModifier(match));
- } else if (type === "gradient") {
- modifiers.push(
- getGradientModifier({
- match,
- index,
- typeGradient: typeGradient,
- hasComma: hasComma || false,
- offset
- })
- );
- }
- if (i === matches.length - 1) {
- modifiers.push(() => value.substring(matchEnd));
- }
+ property = borderSideProp;
+ modifier = borderSideVal;
+ } else {
+ modifier = value;
}
+ } else {
+ modifier = getColorModifier(property, value, rule);
+ }
+ } else if (
+ property === "background-image" ||
+ property === "list-style-image"
+ ) {
+ modifier = getBgImageModifier(
+ value,
+ rule,
+ ignoreImageSelectors,
+ isCancelled
);
- return (theme) => {
- const results = modifiers
- .filter(Boolean)
- .map((modify) => modify(theme));
- if (results.some((r) => r instanceof Promise)) {
- return Promise.all(results).then((asyncResults) => {
- return asyncResults.filter(Boolean).join("");
- });
- }
- const combinedResult = results.join("");
- if (combinedResult.endsWith(", initial")) {
- return combinedResult.slice(0, -9);
- }
- return combinedResult;
- };
- } catch (err) {
+ } else if (property.includes("shadow")) {
+ modifier = getShadowModifier(value);
+ }
+ if (!modifier) {
return null;
}
+ return {
+ property,
+ value: modifier,
+ important: getPriority(rule.style, property),
+ sourceValue: value
+ };
}
- function getShadowModifierWithInfo(value) {
- try {
- let index = 0;
- const colorMatches = getMatches(
- /(^|\s)(?!calc)([a-z]+\(.+?\)|#[0-9a-f]+|[a-z]+)(.*?(inset|outset)?($|,))/gi,
- value,
- 2
+ function joinSelectors(...selectors) {
+ return selectors.filter(Boolean).join(", ");
+ }
+ const hostsWithOddScrollbars = ["calendar.google.com"];
+ function getModifiedUserAgentStyle(theme, isIFrame, styleSystemControls) {
+ const lines = [];
+ if (!isIFrame) {
+ lines.push("html {");
+ lines.push(
+ ` background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, theme)} !important;`
);
- let notParsed = 0;
- const modifiers = colorMatches.map((match, i) => {
- const prefixIndex = index;
- const matchIndex = value.indexOf(match, index);
- const matchEnd = matchIndex + match.length;
- index = matchEnd;
- const rgb = parseColorWithCache(match);
- if (!rgb) {
- notParsed++;
- return () => value.substring(prefixIndex, matchEnd);
- }
- return (theme) =>
- `${value.substring(prefixIndex, matchIndex)}${modifyShadowColor(rgb, theme)}${i === colorMatches.length - 1 ? value.substring(matchEnd) : ""}`;
- });
- return (theme) => {
- const modified = modifiers
- .map((modify) => modify(theme))
- .join("");
- return {
- matchesLength: colorMatches.length,
- unparseableMatchesLength: notParsed,
- result: modified
- };
- };
- } catch (err) {
- return null;
+ lines.push("}");
}
- }
- function getShadowModifier(value) {
- const shadowModifier = getShadowModifierWithInfo(value);
- if (!shadowModifier) {
- return null;
+ if (isCSSColorSchemePropSupported && theme.mode === 1) {
+ lines.push("html {");
+ lines.push(` color-scheme: dark !important;`);
+ lines.push("}");
+ lines.push("iframe {");
+ lines.push(` color-scheme: dark !important;`);
+ lines.push("}");
}
- return (theme) => shadowModifier(theme).result;
- }
- function getScrollbarColorModifier(value) {
- const colorsMatch = value.match(
- /^\s*([a-z]+(\(.*\))?)\s+([a-z]+(\(.*\))?)\s*$/
+ const bgSelectors = joinSelectors(
+ isIFrame ? "" : "html, body",
+ styleSystemControls ? "input, textarea, select, button, dialog" : ""
);
- if (!colorsMatch) {
- return value;
- }
- const thumb = parseColorWithCache(colorsMatch[1]);
- const track = parseColorWithCache(colorsMatch[3]);
- if (!thumb || !track) {
- return null;
+ if (bgSelectors) {
+ lines.push(`${bgSelectors} {`);
+ lines.push(
+ ` background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, theme)};`
+ );
+ lines.push("}");
}
- return (theme) =>
- `${modifyForegroundColor(thumb, theme)} ${modifyBackgroundColor(thumb, theme)}`;
- }
- function getColorSchemeModifier() {
- return (theme) => (theme.mode === 0 ? "dark light" : "dark");
- }
- function getVariableModifier(
- variablesStore,
- prop,
- value,
- rule,
- ignoredImgSelectors,
- isCancelled
- ) {
- return variablesStore.getModifierForVariable({
- varName: prop,
- sourceValue: value,
- rule,
- ignoredImgSelectors,
- isCancelled
- });
- }
- function getVariableDependantModifier(variablesStore, prop, value) {
- return variablesStore.getModifierForVarDependant(prop, value);
- }
- function cleanModificationCache() {
- clearColorModificationCache();
- imageDetailsCache.clear();
- cleanImageProcessingCache();
- awaitingForImageLoading.clear();
- }
-
- const VAR_TYPE_BGCOLOR = 1 << 0;
- const VAR_TYPE_TEXTCOLOR = 1 << 1;
- const VAR_TYPE_BORDERCOLOR = 1 << 2;
- const VAR_TYPE_BGIMG = 1 << 3;
- class VariablesStore {
- constructor() {
- this.varTypes = new Map();
- this.rulesQueue = new Set();
- this.inlineStyleQueue = [];
- this.definedVars = new Set();
- this.varRefs = new Map();
- this.unknownColorVars = new Set();
- this.unknownBgVars = new Set();
- this.undefinedVars = new Set();
- this.initialVarTypes = new Map();
- this.changedTypeVars = new Set();
- this.typeChangeSubscriptions = new Map();
- this.unstableVarValues = new Map();
+ lines.push(
+ `${joinSelectors("html, body", styleSystemControls ? "input, textarea, select, button" : "")} {`
+ );
+ lines.push(
+ ` border-color: ${modifyBorderColor({r: 76, g: 76, b: 76}, theme)};`
+ );
+ lines.push(
+ ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)};`
+ );
+ lines.push("}");
+ lines.push("a {");
+ lines.push(
+ ` color: ${modifyForegroundColor({r: 0, g: 64, b: 255}, theme)};`
+ );
+ lines.push("}");
+ lines.push("table {");
+ lines.push(
+ ` border-color: ${modifyBorderColor({r: 128, g: 128, b: 128}, theme)};`
+ );
+ lines.push("}");
+ lines.push("mark {");
+ lines.push(
+ ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)};`
+ );
+ lines.push("}");
+ lines.push("::placeholder {");
+ lines.push(
+ ` color: ${modifyForegroundColor({r: 169, g: 169, b: 169}, theme)};`
+ );
+ lines.push("}");
+ lines.push("input:-webkit-autofill,");
+ lines.push("textarea:-webkit-autofill,");
+ lines.push("select:-webkit-autofill {");
+ lines.push(
+ ` background-color: ${modifyBackgroundColor({r: 250, g: 255, b: 189}, theme)} !important;`
+ );
+ lines.push(
+ ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)} !important;`
+ );
+ lines.push("}");
+ if (
+ theme.scrollbarColor &&
+ !hostsWithOddScrollbars.includes(location.hostname)
+ ) {
+ lines.push(getModifiedScrollbarStyle(theme));
}
- clear() {
- this.varTypes.clear();
- this.rulesQueue.clear();
- this.inlineStyleQueue.splice(0);
- this.definedVars.clear();
- this.varRefs.clear();
- this.unknownColorVars.clear();
- this.unknownBgVars.clear();
- this.undefinedVars.clear();
- this.initialVarTypes.clear();
- this.changedTypeVars.clear();
- this.typeChangeSubscriptions.clear();
- this.unstableVarValues.clear();
+ if (theme.selectionColor) {
+ lines.push(getModifiedSelectionStyle(theme));
}
- isVarType(varName, typeNum) {
- return (
- this.varTypes.has(varName) &&
- (this.varTypes.get(varName) & typeNum) > 0
+ if (isLayerRuleSupported) {
+ lines.unshift("@layer {");
+ lines.push("}");
+ }
+ return lines.join("\n");
+ }
+ function getSelectionColor(theme) {
+ let backgroundColorSelection;
+ let foregroundColorSelection;
+ if (theme.selectionColor === "auto") {
+ backgroundColorSelection = modifyBackgroundColor(
+ {r: 0, g: 96, b: 212},
+ {...theme, grayscale: 0}
+ );
+ foregroundColorSelection = modifyForegroundColor(
+ {r: 255, g: 255, b: 255},
+ {...theme, grayscale: 0}
);
+ } else {
+ const rgb = parseColorWithCache(theme.selectionColor);
+ const hsl = rgbToHSL(rgb);
+ backgroundColorSelection = theme.selectionColor;
+ if (hsl.l < 0.5) {
+ foregroundColorSelection = "#FFF";
+ } else {
+ foregroundColorSelection = "#000";
+ }
}
- addRulesForMatching(rules) {
- this.rulesQueue.add(rules);
+ return {backgroundColorSelection, foregroundColorSelection};
+ }
+ function getModifiedSelectionStyle(theme) {
+ const lines = [];
+ const modifiedSelectionColor = getSelectionColor(theme);
+ const backgroundColorSelection =
+ modifiedSelectionColor.backgroundColorSelection;
+ const foregroundColorSelection =
+ modifiedSelectionColor.foregroundColorSelection;
+ ["::selection", "::-moz-selection"].forEach((selection) => {
+ lines.push(`${selection} {`);
+ lines.push(
+ ` background-color: ${backgroundColorSelection} !important;`
+ );
+ lines.push(` color: ${foregroundColorSelection} !important;`);
+ lines.push("}");
+ });
+ return lines.join("\n");
+ }
+ function getModifiedScrollbarStyle(theme) {
+ let colorTrack;
+ let colorThumb;
+ if (theme.scrollbarColor === "auto") {
+ colorTrack = modifyBackgroundColor({r: 241, g: 241, b: 241}, theme);
+ colorThumb = modifyBackgroundColor({r: 176, g: 176, b: 176}, theme);
+ } else {
+ const rgb = parseColorWithCache(theme.scrollbarColor);
+ const hsl = rgbToHSL(rgb);
+ const darken = (darker) => ({
+ ...hsl,
+ l: clamp(hsl.l - darker, 0, 1)
+ });
+ colorTrack = hslToString(darken(0.4));
+ colorThumb = hslToString(hsl);
}
- addInlineStyleForMatching(style) {
- this.inlineStyleQueue.push(style);
+ return [
+ `* {`,
+ ` scrollbar-color: ${colorThumb} ${colorTrack};`,
+ `}`
+ ].join("\n");
+ }
+ function getModifiedFallbackStyle(theme, {strict}) {
+ const factory = defaultFallbackFactory;
+ return factory(theme, {strict});
+ }
+ function defaultFallbackFactory(theme, {strict}) {
+ const lines = [];
+ lines.push(
+ `html, body, ${strict ? "body :not(iframe)" : "body > :not(iframe)"} {`
+ );
+ lines.push(
+ ` background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, theme)} !important;`
+ );
+ lines.push(
+ ` border-color: ${modifyBorderColor({r: 64, g: 64, b: 64}, theme)} !important;`
+ );
+ lines.push(
+ ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)} !important;`
+ );
+ lines.push("}");
+ lines.push(`div[style*="background-color: rgb(135, 135, 135)"] {`);
+ lines.push(` background-color: #878787 !important;`);
+ lines.push("}");
+ return lines.join("\n");
+ }
+ const unparsableColors = new Set([
+ "inherit",
+ "transparent",
+ "initial",
+ "currentcolor",
+ "none",
+ "unset",
+ "auto"
+ ]);
+ function getColorModifier(prop, value, rule) {
+ if (unparsableColors.has(value.toLowerCase())) {
+ return value;
}
- matchVariablesAndDependents() {
+ const rgb = parseColorWithCache(value);
+ if (!rgb) {
+ return null;
+ }
+ if (prop.includes("background")) {
if (
- this.rulesQueue.size === 0 &&
- this.inlineStyleQueue.length === 0
+ (rule.style.webkitMaskImage &&
+ rule.style.webkitMaskImage !== "none") ||
+ (rule.style.webkitMask &&
+ !rule.style.webkitMask.startsWith("none")) ||
+ (rule.style.mask && rule.style.mask !== "none") ||
+ (rule.style.getPropertyValue("mask-image") &&
+ rule.style.getPropertyValue("mask-image") !== "none")
) {
- return;
+ return (theme) => modifyForegroundColor(rgb, theme);
}
- this.changedTypeVars.clear();
- this.initialVarTypes = new Map(this.varTypes);
- this.collectRootVariables();
- this.collectVariablesAndVarDep();
- this.collectRootVarDependents();
- this.varRefs.forEach((refs, v) => {
- refs.forEach((r) => {
- if (this.varTypes.has(v)) {
- this.resolveVariableType(r, this.varTypes.get(v));
+ return (theme) => modifyBackgroundColor(rgb, theme);
+ }
+ if (prop.includes("border") || prop.includes("outline")) {
+ return (theme) => modifyBorderColor(rgb, theme);
+ }
+ return (theme) => modifyForegroundColor(rgb, theme);
+ }
+ const imageDetailsCache = new Map();
+ const awaitingForImageLoading = new Map();
+ let didTryLoadCache = false;
+ function shouldIgnoreImage(selectorText, selectors) {
+ if (!selectorText || selectors.length === 0) {
+ return false;
+ }
+ if (selectors.some((s) => s === "*")) {
+ return true;
+ }
+ const ruleSelectors = selectorText.split(/,\s*/g);
+ for (let i = 0; i < selectors.length; i++) {
+ const ignoredSelector = selectors[i];
+ if (ignoredSelector.startsWith("^")) {
+ const beginning = ignoredSelector.slice(1);
+ if (ruleSelectors.some((s) => s.startsWith(beginning))) {
+ return true;
+ }
+ } else if (ignoredSelector.endsWith("$")) {
+ const ending = ignoredSelector.slice(
+ 0,
+ ignoredSelector.length - 1
+ );
+ if (ruleSelectors.some((s) => s.endsWith(ending))) {
+ return true;
+ }
+ } else if (ruleSelectors.some((s) => s === ignoredSelector)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ const imageSelectorQueue = new Map();
+ const imageSelectorValues = new Map();
+ const imageSelectorNodeQueue = new Set();
+ let imageSelectorQueueFrameId = null;
+ let classObserver = null;
+ function checkImageSelectors(node) {
+ for (const [selector, callbacks] of imageSelectorQueue) {
+ if (
+ node.querySelector(selector) ||
+ (node instanceof Element && node.matches(selector))
+ ) {
+ imageSelectorQueue.delete(selector);
+ callbacks.forEach((cb) => cb());
+ }
+ }
+ if (!classObserver) {
+ classObserver = new MutationObserver((mutations) => {
+ mutations.forEach((mutation) => {
+ imageSelectorNodeQueue.add(mutation.target);
+ if (!imageSelectorQueueFrameId) {
+ imageSelectorQueueFrameId = requestAnimationFrame(
+ () => {
+ imageSelectorNodeQueue.forEach((element) => {
+ checkImageSelectors(element);
+ });
+ imageSelectorNodeQueue.clear();
+ imageSelectorQueueFrameId = null;
+ }
+ );
}
});
});
- this.unknownColorVars.forEach((v) => {
- if (this.unknownBgVars.has(v)) {
- this.unknownColorVars.delete(v);
- this.unknownBgVars.delete(v);
- this.resolveVariableType(v, VAR_TYPE_BGCOLOR);
- } else if (
- this.isVarType(
- v,
- VAR_TYPE_BGCOLOR |
- VAR_TYPE_TEXTCOLOR |
- VAR_TYPE_BORDERCOLOR
- )
- ) {
- this.unknownColorVars.delete(v);
- } else {
- this.undefinedVars.add(v);
- }
- });
- this.unknownBgVars.forEach((v) => {
- const hasColor =
- this.findVarRef(v, (ref) => {
- return (
- this.unknownColorVars.has(ref) ||
- this.isVarType(
- ref,
- VAR_TYPE_BGCOLOR |
- VAR_TYPE_TEXTCOLOR |
- VAR_TYPE_BORDERCOLOR
- )
- );
- }) != null;
- if (hasColor) {
- this.iterateVarRefs(v, (ref) => {
- this.resolveVariableType(ref, VAR_TYPE_BGCOLOR);
- });
- } else if (
- this.isVarType(v, VAR_TYPE_BGCOLOR | VAR_TYPE_BGIMG)
- ) {
- this.unknownBgVars.delete(v);
- } else {
- this.undefinedVars.add(v);
- }
+ classObserver.observe(document.documentElement, {
+ attributes: true,
+ attributeFilter: ["class"],
+ subtree: true
});
- this.changedTypeVars.forEach((varName) => {
- if (this.typeChangeSubscriptions.has(varName)) {
- this.typeChangeSubscriptions
- .get(varName)
- .forEach((callback) => {
- callback();
- });
- }
- });
- this.changedTypeVars.clear();
}
- getModifierForVariable(options) {
- return (theme) => {
- const {
- varName,
- sourceValue,
- rule,
- ignoredImgSelectors,
- isCancelled
- } = options;
- const getDeclarations = () => {
- const declarations = [];
- const addModifiedValue = (
- typeNum,
- varNameWrapper,
- colorModifier
- ) => {
- if (!this.isVarType(varName, typeNum)) {
- return;
+ }
+ function getBgImageModifier(
+ value,
+ rule,
+ ignoreImageSelectors,
+ isCancelled
+ ) {
+ try {
+ if (shouldIgnoreImage(rule.selectorText, ignoreImageSelectors)) {
+ return value;
+ }
+ const gradients = parseGradient(value);
+ const urls = getMatches(cssURLRegex, value);
+ if (urls.length === 0 && gradients.length === 0) {
+ return value;
+ }
+ const getIndices = (matches) => {
+ let index = 0;
+ return matches.map((match) => {
+ const valueIndex = value.indexOf(match, index);
+ index = valueIndex + match.length;
+ return {match, index: valueIndex};
+ });
+ };
+ const matches = gradients
+ .map((i) => ({type: "gradient", ...i}))
+ .concat(
+ getIndices(urls).map((i) => ({
+ type: "url",
+ offset: 0,
+ ...i
+ }))
+ )
+ .sort((a, b) => (a.index > b.index ? 1 : -1));
+ const getGradientModifier = (gradient) => {
+ const {typeGradient, match, hasComma} = gradient;
+ const partsRegex =
+ /([^\(\),]+(\([^\(\)]*(\([^\(\)]*\)*[^\(\)]*)?\))?([^\(\), ]|( (?!calc)))*),?/g;
+ const colorStopRegex =
+ /^(from|color-stop|to)\(([^\(\)]*?,\s*)?(.*?)\)$/;
+ const parts = getMatches(partsRegex, match, 1).map((part) => {
+ part = part.trim();
+ let rgb = parseColorWithCache(part);
+ if (rgb) {
+ return (theme) => modifyGradientColor(rgb, theme);
+ }
+ const space = part.lastIndexOf(" ");
+ rgb = parseColorWithCache(part.substring(0, space));
+ if (rgb) {
+ return (theme) =>
+ `${modifyGradientColor(rgb, theme)} ${part.substring(space + 1)}`;
+ }
+ const colorStopMatch = part.match(colorStopRegex);
+ if (colorStopMatch) {
+ rgb = parseColorWithCache(colorStopMatch[3]);
+ if (rgb) {
+ return (theme) =>
+ `${colorStopMatch[1]}(${colorStopMatch[2] ? `${colorStopMatch[2]}, ` : ""}${modifyGradientColor(rgb, theme)})`;
}
- const property = varNameWrapper(varName);
- let modifiedValue;
- if (isVarDependant(sourceValue)) {
- if (isConstructedColorVar(sourceValue)) {
- let value = insertVarValues(
- sourceValue,
- this.unstableVarValues
+ }
+ return () => part;
+ });
+ return (theme) => {
+ return `${typeGradient}(${parts.map((modify) => modify(theme)).join(", ")})${hasComma ? ", " : ""}`;
+ };
+ };
+ const getURLModifier = (urlValue) => {
+ if (!didTryLoadCache) {
+ didTryLoadCache = true;
+ readImageDetailsCache(imageDetailsCache);
+ }
+ let url = getCSSURLValue(urlValue);
+ const isURLEmpty = url.length === 0;
+ const {parentStyleSheet} = rule;
+ const ownerNode = parentStyleSheet?.ownerNode;
+ const scope =
+ (parentStyleSheet && getSheetScope(parentStyleSheet)) ??
+ document;
+ const baseURL =
+ parentStyleSheet && parentStyleSheet.href
+ ? getCSSBaseBath(parentStyleSheet.href)
+ : ownerNode?.baseURI || location.origin;
+ url = getAbsoluteURL(baseURL, url);
+ return async (theme) => {
+ if (isURLEmpty) {
+ return "url('')";
+ }
+ let selector = rule.selectorText;
+ if (selector) {
+ if (selector.includes("::before")) {
+ selector = selector.replaceAll("::before", "");
+ }
+ if (selector.includes("::after")) {
+ selector = selector.replaceAll("::after", "");
+ }
+ if (!scope.querySelector(selector)) {
+ await new Promise((resolve) => {
+ if (imageSelectorQueue.has(selector)) {
+ imageSelectorQueue
+ .get(selector)
+ .push(resolve);
+ } else {
+ imageSelectorQueue.set(selector, [resolve]);
+ imageSelectorValues.set(selector, urlValue);
+ }
+ });
+ }
+ }
+ let imageDetails = null;
+ if (imageDetailsCache.has(url)) {
+ imageDetails = imageDetailsCache.get(url);
+ } else {
+ try {
+ if (!isBlobURLCheckResultReady()) {
+ await requestBlobURLCheck();
+ }
+ if (awaitingForImageLoading.has(url)) {
+ const awaiters =
+ awaitingForImageLoading.get(url);
+ imageDetails = await new Promise((resolve) =>
+ awaiters.push(resolve)
);
- if (!value) {
- value =
- typeNum === VAR_TYPE_BGCOLOR
- ? "#ffffff"
- : "#000000";
+ if (!imageDetails) {
+ return null;
}
- modifiedValue = colorModifier(value, theme);
} else {
- modifiedValue = replaceCSSVariablesNames(
- sourceValue,
- (v) => varNameWrapper(v),
- (fallback) => colorModifier(fallback, theme)
- );
+ awaitingForImageLoading.set(url, []);
+ imageDetails = await getImageDetails(url);
+ imageDetailsCache.set(url, imageDetails);
+ writeImageDetailsCache(url, imageDetails);
+ awaitingForImageLoading
+ .get(url)
+ .forEach((resolve) =>
+ resolve(imageDetails)
+ );
+ awaitingForImageLoading.delete(url);
+ }
+ if (isCancelled()) {
+ return null;
+ }
+ } catch (err) {
+ logWarn(err);
+ if (awaitingForImageLoading.has(url)) {
+ awaitingForImageLoading
+ .get(url)
+ .forEach((resolve) => resolve(null));
+ awaitingForImageLoading.delete(url);
}
- } else {
- modifiedValue = colorModifier(sourceValue, theme);
- }
- declarations.push({
- property,
- value: modifiedValue
- });
- };
- addModifiedValue(
- VAR_TYPE_BGCOLOR,
- wrapBgColorVariableName,
- tryModifyBgColor
- );
- addModifiedValue(
- VAR_TYPE_TEXTCOLOR,
- wrapTextColorVariableName,
- tryModifyTextColor
- );
- addModifiedValue(
- VAR_TYPE_BORDERCOLOR,
- wrapBorderColorVariableName,
- tryModifyBorderColor
- );
- if (this.isVarType(varName, VAR_TYPE_BGIMG)) {
- const property = wrapBgImgVariableName(varName);
- let modifiedValue = sourceValue;
- if (isVarDependant(sourceValue)) {
- modifiedValue = replaceCSSVariablesNames(
- sourceValue,
- (v) => wrapBgColorVariableName(v),
- (fallback) => tryModifyBgColor(fallback, theme)
- );
}
- const bgModifier = getBgImageModifier(
- modifiedValue,
- rule,
- ignoredImgSelectors,
- isCancelled
- );
- modifiedValue =
- typeof bgModifier === "function"
- ? bgModifier(theme)
- : bgModifier;
- declarations.push({
- property,
- value: modifiedValue
- });
}
- return declarations;
- };
- const callbacks = new Set();
- const addListener = (onTypeChange) => {
- const callback = () => {
- const decs = getDeclarations();
- onTypeChange(decs);
- };
- callbacks.add(callback);
- this.subscribeForVarTypeChange(varName, callback);
- };
- const removeListeners = () => {
- callbacks.forEach((callback) => {
- this.unsubscribeFromVariableTypeChanges(
- varName,
- callback
+ if (imageDetails) {
+ const bgImageValue = getBgImageValue(
+ imageDetails,
+ theme
);
- });
- };
- return {
- declarations: getDeclarations(),
- onTypeChange: { addListener, removeListeners }
- };
- };
- }
- getModifierForVarDependant(property, sourceValue) {
- const isConstructedColor = sourceValue.match(/^\s*(rgb|hsl)a?\(/);
- const isSimpleConstructedColor = sourceValue.match(
- /^rgba?\(var\(--[\-_A-Za-z0-9]+\)(\s*,?\/?\s*0?\.\d+)?\)$/
- );
- if (isConstructedColor && !isSimpleConstructedColor) {
- const isBg = property.startsWith("background");
- const isText = isTextColorProperty(property);
- return (theme) => {
- let value = insertVarValues(
- sourceValue,
- this.unstableVarValues
- );
- if (!value) {
- value = isBg ? "#ffffff" : "#000000";
+ if (bgImageValue) {
+ return bgImageValue;
+ }
}
- const modifier = isBg
- ? tryModifyBgColor
- : isText
- ? tryModifyTextColor
- : tryModifyBorderColor;
- return modifier(value, theme);
+ if (url.startsWith("data:")) {
+ const blobURL = await tryConvertDataURLToBlobURL(url);
+ if (blobURL) {
+ return `url("${blobURL}")`;
+ }
+ }
+ return `url("${url}")`;
};
- }
- if (
- property === "background-color" ||
- (isSimpleConstructedColor && property === "background")
- ) {
- return (theme) => {
- const defaultFallback = tryModifyBgColor(
- isConstructedColor ? "255, 255, 255" : "#ffffff",
- theme
- );
- return replaceCSSVariablesNames(
- sourceValue,
- (v) => wrapBgColorVariableName(v),
- (fallback) => tryModifyBgColor(fallback, theme),
- defaultFallback
- );
- };
- }
- if (isTextColorProperty(property)) {
- return (theme) => {
- const defaultFallback = tryModifyTextColor(
- isConstructedColor ? "0, 0, 0" : "#000000",
- theme
- );
- return replaceCSSVariablesNames(
- sourceValue,
- (v) => wrapTextColorVariableName(v),
- (fallback) => tryModifyTextColor(fallback, theme),
- defaultFallback
- );
- };
- }
- if (
- property === "background" ||
- property === "background-image" ||
- property === "box-shadow"
- ) {
- return (theme) => {
- const unknownVars = new Set();
- const modify = () => {
- const variableReplaced = replaceCSSVariablesNames(
- sourceValue,
- (v) => {
- if (this.isVarType(v, VAR_TYPE_BGCOLOR)) {
- return wrapBgColorVariableName(v);
- }
- if (this.isVarType(v, VAR_TYPE_BGIMG)) {
- return wrapBgImgVariableName(v);
+ };
+ const getBgImageValue = (imageDetails, theme) => {
+ const {isDark, isLight, isTransparent, isLarge, width} =
+ imageDetails;
+ let result;
+ const logSrc = imageDetails.src.startsWith("data:")
+ ? "data:"
+ : imageDetails.src;
+ if (isLarge && isLight && !isTransparent && theme.mode === 1) {
+ logInfo(`Hiding large light image ${logSrc}`);
+ result = "none";
+ } else if (
+ isDark &&
+ isTransparent &&
+ theme.mode === 1 &&
+ width > 2
+ ) {
+ logInfo(`Inverting dark image ${logSrc}`);
+ const inverted = getFilteredImageURL(imageDetails, {
+ ...theme,
+ sepia: clamp(theme.sepia + 10, 0, 100)
+ });
+ result = `url("${inverted}")`;
+ } else if (isLight && !isTransparent && theme.mode === 1) {
+ logInfo(`Dimming light image ${logSrc}`);
+ const dimmed = getFilteredImageURL(imageDetails, theme);
+ result = `url("${dimmed}")`;
+ } else if (theme.mode === 0 && isLight) {
+ logInfo(`Applying filter to image ${logSrc}`);
+ const filtered = getFilteredImageURL(imageDetails, {
+ ...theme,
+ brightness: clamp(theme.brightness - 10, 5, 200),
+ sepia: clamp(theme.sepia + 10, 0, 100)
+ });
+ result = `url("${filtered}")`;
+ } else {
+ logInfo(`Not modifying the image ${logSrc}`);
+ result = null;
+ }
+ return result;
+ };
+ const modifiers = [];
+ let matchIndex = 0;
+ let prevHasComma = false;
+ matches.forEach(
+ ({type, match, index, typeGradient, hasComma, offset}, i) => {
+ const matchStart = index;
+ const prefixStart = matchIndex;
+ const matchEnd = matchStart + match.length + offset;
+ matchIndex = matchEnd;
+ if (prefixStart !== matchStart) {
+ if (prevHasComma) {
+ modifiers.push(() => {
+ let betweenValue = value.substring(
+ prefixStart,
+ matchStart
+ );
+ if (betweenValue[0] === ",") {
+ betweenValue = betweenValue.substring(1);
}
- unknownVars.add(v);
- return v;
- },
- (fallback) => tryModifyBgColor(fallback, theme)
- );
- if (property === "box-shadow") {
- const shadowModifier =
- getShadowModifierWithInfo(variableReplaced);
- const modifiedShadow = shadowModifier(theme);
- if (
- modifiedShadow.unparseableMatchesLength !==
- modifiedShadow.matchesLength
- ) {
- return modifiedShadow.result;
- }
+ return betweenValue;
+ });
+ } else {
+ modifiers.push(() =>
+ value.substring(prefixStart, matchStart)
+ );
}
- return variableReplaced;
- };
- const modified = modify();
- if (unknownVars.size > 0) {
- const isFallbackResolved = modified.match(
- /^var\(.*?, var\(--darkreader-bg--.*\)\)$/
+ }
+ prevHasComma = hasComma || false;
+ if (type === "url") {
+ modifiers.push(getURLModifier(match));
+ } else if (type === "gradient") {
+ modifiers.push(
+ getGradientModifier({
+ match,
+ index,
+ typeGradient: typeGradient,
+ hasComma: hasComma || false,
+ offset
+ })
);
- if (isFallbackResolved) {
- return modified;
- }
- return new Promise((resolve) => {
- for (const unknownVar of unknownVars.values()) {
- const callback = () => {
- this.unsubscribeFromVariableTypeChanges(
- unknownVar,
- callback
- );
- const newValue = modify();
- resolve(newValue);
- };
- this.subscribeForVarTypeChange(
- unknownVar,
- callback
- );
- }
- });
}
- return modified;
- };
- }
- if (
- property.startsWith("border") ||
- property.startsWith("outline")
- ) {
- return (theme) => {
- return replaceCSSVariablesNames(
- sourceValue,
- (v) => wrapBorderColorVariableName(v),
- (fallback) => tryModifyBorderColor(fallback, theme)
- );
- };
- }
- return null;
- }
- subscribeForVarTypeChange(varName, callback) {
- if (!this.typeChangeSubscriptions.has(varName)) {
- this.typeChangeSubscriptions.set(varName, new Set());
- }
- const rootStore = this.typeChangeSubscriptions.get(varName);
- if (!rootStore.has(callback)) {
- rootStore.add(callback);
- }
- }
- unsubscribeFromVariableTypeChanges(varName, callback) {
- if (this.typeChangeSubscriptions.has(varName)) {
- this.typeChangeSubscriptions.get(varName).delete(callback);
- }
- }
- collectVariablesAndVarDep() {
- this.rulesQueue.forEach((rules) => {
- iterateCSSRules(rules, (rule) => {
- if (rule.style) {
- this.collectVarsFromCSSDeclarations(rule.style);
+ if (i === matches.length - 1) {
+ modifiers.push(() => value.substring(matchEnd));
}
- });
- });
- this.inlineStyleQueue.forEach((style) => {
- this.collectVarsFromCSSDeclarations(style);
- });
- this.rulesQueue.clear();
- this.inlineStyleQueue.splice(0);
- }
- collectVarsFromCSSDeclarations(style) {
- iterateCSSDeclarations(style, (property, value) => {
- if (isVariable(property)) {
- this.inspectVariable(property, value);
}
- if (isVarDependant(value)) {
- this.inspectVarDependant(property, value);
- }
- });
- }
- shouldProcessRootVariables() {
- return (
- this.rulesQueue.size > 0 &&
- document.documentElement.getAttribute("style")?.includes("--")
);
- }
- collectRootVariables() {
- if (!this.shouldProcessRootVariables()) {
- return;
- }
- iterateCSSDeclarations(
- document.documentElement.style,
- (property, value) => {
- if (isVariable(property)) {
- this.inspectVariable(property, value);
- }
+ return (theme) => {
+ const results = modifiers
+ .filter(Boolean)
+ .map((modify) => modify(theme));
+ if (results.some((r) => r instanceof Promise)) {
+ return Promise.all(results).then((asyncResults) => {
+ return asyncResults.filter(Boolean).join("");
+ });
}
- );
+ const combinedResult = results.join("");
+ if (combinedResult.endsWith(", initial")) {
+ return combinedResult.slice(0, -9);
+ }
+ return combinedResult;
+ };
+ } catch (err) {
+ return null;
}
- inspectVariable(varName, value) {
- this.unstableVarValues.set(varName, value);
- if (isVarDependant(value) && isConstructedColorVar(value)) {
- this.unknownColorVars.add(varName);
- this.definedVars.add(varName);
- }
- if (this.definedVars.has(varName)) {
- return;
- }
- this.definedVars.add(varName);
- const isColor = Boolean(
- value.match(rawRGBSpaceRegex) ||
- value.match(rawRGBCommaRegex) ||
- parseColorWithCache(value)
- );
- if (isColor) {
- this.unknownColorVars.add(varName);
- } else if (
- value.includes("url(") ||
- value.includes("linear-gradient(") ||
- value.includes("radial-gradient(")
- ) {
- this.resolveVariableType(varName, VAR_TYPE_BGIMG);
- }
- }
- resolveVariableType(varName, typeNum) {
- const initialType = this.initialVarTypes.get(varName) || 0;
- const currentType = this.varTypes.get(varName) || 0;
- const newType = currentType | typeNum;
- this.varTypes.set(varName, newType);
- if (newType !== initialType || this.undefinedVars.has(varName)) {
- this.changedTypeVars.add(varName);
- this.undefinedVars.delete(varName);
- }
- this.unknownColorVars.delete(varName);
- this.unknownBgVars.delete(varName);
- }
- collectRootVarDependents() {
- if (!this.shouldProcessRootVariables()) {
- return;
- }
- iterateCSSDeclarations(
- document.documentElement.style,
- (property, value) => {
- if (isVarDependant(value)) {
- this.inspectVarDependant(property, value);
- }
- }
+ }
+ function getShadowModifierWithInfo(value) {
+ try {
+ let index = 0;
+ const colorMatches = getMatches(
+ /(^|\s)(?!calc)([a-z]+\(.+?\)|#[0-9a-f]+|[a-z]+)(.*?(inset|outset)?($|,))/gi,
+ value,
+ 2
);
- }
- inspectVarDependant(property, value) {
- if (isVariable(property)) {
- this.iterateVarDeps(value, (ref) => {
- if (!this.varRefs.has(property)) {
- this.varRefs.set(property, new Set());
- }
- this.varRefs.get(property).add(ref);
- });
- } else if (
- property === "background-color" ||
- property === "box-shadow"
- ) {
- this.iterateVarDeps(value, (v) =>
- this.resolveVariableType(v, VAR_TYPE_BGCOLOR)
- );
- } else if (isTextColorProperty(property)) {
- this.iterateVarDeps(value, (v) =>
- this.resolveVariableType(v, VAR_TYPE_TEXTCOLOR)
- );
- } else if (
- property.startsWith("border") ||
- property.startsWith("outline")
- ) {
- this.iterateVarDeps(value, (v) =>
- this.resolveVariableType(v, VAR_TYPE_BORDERCOLOR)
- );
- } else if (
- property === "background" ||
- property === "background-image"
- ) {
- this.iterateVarDeps(value, (v) => {
- if (this.isVarType(v, VAR_TYPE_BGCOLOR | VAR_TYPE_BGIMG)) {
- return;
- }
- const isBgColor =
- this.findVarRef(v, (ref) => {
- return (
- this.unknownColorVars.has(ref) ||
- this.isVarType(
- ref,
- VAR_TYPE_BGCOLOR |
- VAR_TYPE_TEXTCOLOR |
- VAR_TYPE_BORDERCOLOR
- )
- );
- }) != null;
- this.iterateVarRefs(v, (ref) => {
- if (isBgColor) {
- this.resolveVariableType(ref, VAR_TYPE_BGCOLOR);
- } else {
- this.unknownBgVars.add(ref);
- }
- });
- });
- }
- }
- iterateVarDeps(value, iterator) {
- const varDeps = new Set();
- iterateVarDependencies(value, (v) => varDeps.add(v));
- varDeps.forEach((v) => iterator(v));
- }
- findVarRef(varName, iterator, stack = new Set()) {
- if (stack.has(varName)) {
- return null;
- }
- stack.add(varName);
- const result = iterator(varName);
- if (result) {
- return varName;
- }
- const refs = this.varRefs.get(varName);
- if (!refs || refs.size === 0) {
- return null;
- }
- for (const ref of refs) {
- const found = this.findVarRef(ref, iterator, stack);
- if (found) {
- return found;
+ let notParsed = 0;
+ const modifiers = colorMatches.map((match, i) => {
+ const prefixIndex = index;
+ const matchIndex = value.indexOf(match, index);
+ const matchEnd = matchIndex + match.length;
+ index = matchEnd;
+ const rgb = parseColorWithCache(match);
+ if (!rgb) {
+ notParsed++;
+ return () => value.substring(prefixIndex, matchEnd);
}
- }
- return null;
- }
- iterateVarRefs(varName, iterator) {
- this.findVarRef(varName, (ref) => {
- iterator(ref);
- return false;
+ return (theme) =>
+ `${value.substring(prefixIndex, matchIndex)}${modifyShadowColor(rgb, theme)}${i === colorMatches.length - 1 ? value.substring(matchEnd) : ""}`;
});
+ return (theme) => {
+ const modified = modifiers
+ .map((modify) => modify(theme))
+ .join("");
+ return {
+ matchesLength: colorMatches.length,
+ unparsableMatchesLength: notParsed,
+ result: modified
+ };
+ };
+ } catch (err) {
+ return null;
}
- setOnRootVariableChange(callback) {
- this.onRootVariableDefined = callback;
- }
- putRootVars(styleElement, theme) {
- const sheet = styleElement.sheet;
- if (sheet.cssRules.length > 0) {
- sheet.deleteRule(0);
- }
- const declarations = new Map();
- iterateCSSDeclarations(
- document.documentElement.style,
- (property, value) => {
- if (isVariable(property)) {
- if (this.isVarType(property, VAR_TYPE_BGCOLOR)) {
- declarations.set(
- wrapBgColorVariableName(property),
- tryModifyBgColor(value, theme)
- );
- }
- if (this.isVarType(property, VAR_TYPE_TEXTCOLOR)) {
- declarations.set(
- wrapTextColorVariableName(property),
- tryModifyTextColor(value, theme)
- );
- }
- if (this.isVarType(property, VAR_TYPE_BORDERCOLOR)) {
- declarations.set(
- wrapBorderColorVariableName(property),
- tryModifyBorderColor(value, theme)
- );
- }
- this.subscribeForVarTypeChange(
- property,
- this.onRootVariableDefined
- );
- }
- }
- );
- const cssLines = [];
- cssLines.push(":root {");
- for (const [property, value] of declarations) {
- cssLines.push(` ${property}: ${value};`);
- }
- cssLines.push("}");
- const cssText = cssLines.join("\n");
- sheet.insertRule(cssText);
- }
- }
- const variablesStore = new VariablesStore();
- function getVariableRange(input, searchStart = 0) {
- const start = input.indexOf("var(", searchStart);
- if (start >= 0) {
- const range = getParenthesesRange(input, start + 3);
- if (range) {
- return { start, end: range.end };
- }
- }
- return null;
}
- function getVariablesMatches(input) {
- const ranges = [];
- let i = 0;
- let range;
- while ((range = getVariableRange(input, i))) {
- const { start, end } = range;
- ranges.push({ start, end, value: input.substring(start, end) });
- i = range.end + 1;
+ function getShadowModifier(value) {
+ const shadowModifier = getShadowModifierWithInfo(value);
+ if (!shadowModifier) {
+ return null;
}
- return ranges;
+ return (theme) => shadowModifier(theme).result;
}
- function replaceVariablesMatches(input, replacer) {
- const matches = getVariablesMatches(input);
- const matchesCount = matches.length;
- if (matchesCount === 0) {
- return input;
- }
- const inputLength = input.length;
- const replacements = matches.map((m) =>
- replacer(m.value, matches.length)
+ function getScrollbarColorModifier(value) {
+ const colorsMatch = value.match(
+ /^\s*([a-z]+(\(.*\))?)\s+([a-z]+(\(.*\))?)\s*$/
);
- const parts = [];
- parts.push(input.substring(0, matches[0].start));
- for (let i = 0; i < matchesCount; i++) {
- parts.push(replacements[i]);
- const start = matches[i].end;
- const end =
- i < matchesCount - 1 ? matches[i + 1].start : inputLength;
- parts.push(input.substring(start, end));
+ if (!colorsMatch) {
+ return value;
}
- return parts.join("");
- }
- function getVariableNameAndFallback(match) {
- const commaIndex = match.indexOf(",");
- let name;
- let fallback;
- if (commaIndex >= 0) {
- name = match.substring(4, commaIndex).trim();
- fallback = match.substring(commaIndex + 1, match.length - 1).trim();
- } else {
- name = match.substring(4, match.length - 1).trim();
- fallback = "";
+ const thumb = parseColorWithCache(colorsMatch[1]);
+ const track = parseColorWithCache(colorsMatch[3]);
+ if (!thumb || !track) {
+ return null;
}
- return { name, fallback };
+ return (theme) =>
+ `${modifyForegroundColor(thumb, theme)} ${modifyBackgroundColor(thumb, theme)}`;
}
- function replaceCSSVariablesNames(
+ function getColorSchemeModifier() {
+ return (theme) => (theme.mode === 0 ? "dark light" : "dark");
+ }
+ function getVariableModifier(
+ variablesStore,
+ prop,
value,
- nameReplacer,
- fallbackReplacer,
- finalFallback
+ rule,
+ ignoredImgSelectors,
+ isCancelled
) {
- const matchReplacer = (match) => {
- const { name, fallback } = getVariableNameAndFallback(match);
- const newName = nameReplacer(name);
- if (!fallback) {
- if (finalFallback) {
- return `var(${newName}, ${finalFallback})`;
- }
- return `var(${newName})`;
- }
- let newFallback;
- if (isVarDependant(fallback)) {
- newFallback = replaceCSSVariablesNames(
- fallback,
- nameReplacer,
- fallbackReplacer
- );
- } else if (fallbackReplacer) {
- newFallback = fallbackReplacer(fallback);
- } else {
- newFallback = fallback;
- }
- return `var(${newName}, ${newFallback})`;
- };
- return replaceVariablesMatches(value, matchReplacer);
- }
- function iterateVarDependencies(value, iterator) {
- replaceCSSVariablesNames(value, (varName) => {
- iterator(varName);
- return varName;
+ return variablesStore.getModifierForVariable({
+ varName: prop,
+ sourceValue: value,
+ rule,
+ ignoredImgSelectors,
+ isCancelled
});
}
- function wrapBgColorVariableName(name) {
- return `--darkreader-bg${name}`;
- }
- function wrapTextColorVariableName(name) {
- return `--darkreader-text${name}`;
- }
- function wrapBorderColorVariableName(name) {
- return `--darkreader-border${name}`;
- }
- function wrapBgImgVariableName(name) {
- return `--darkreader-bgimg${name}`;
- }
- function isVariable(property) {
- return property.startsWith("--");
- }
- function isVarDependant(value) {
- return value.includes("var(");
- }
- function isConstructedColorVar(value) {
- return (
- value.match(/^\s*(rgb|hsl)a?\(/) ||
- value.match(/^(((\d{1,3})|(var\([\-_A-Za-z0-9]+\))),?\s*?){3}$/)
- );
+ function getVariableDependantModifier(variablesStore, prop, value) {
+ return variablesStore.getModifierForVarDependant(prop, value);
}
- function isTextColorProperty(property) {
- return (
- property === "color" ||
- property === "caret-color" ||
- property === "-webkit-text-fill-color"
- );
+ function cleanModificationCache() {
+ clearColorModificationCache();
+ imageDetailsCache.clear();
+ cleanImageProcessingCache();
+ awaitingForImageLoading.clear();
+ imageSelectorQueue.clear();
+ classObserver?.disconnect();
+ classObserver = null;
}
- const rawRGBSpaceRegex = /^(\d{1,3})\s+(\d{1,3})\s+(\d{1,3})$/;
- const rawRGBCommaRegex = /^(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})$/;
- function parseRawColorValue(input) {
- const match =
- input.match(rawRGBSpaceRegex) ?? input.match(rawRGBCommaRegex);
- if (match) {
- const color = `rgb(${match[1]}, ${match[2]}, ${match[3]})`;
- return { isRaw: true, color };
+
+ const VAR_TYPE_BG_COLOR = 1 << 0;
+ const VAR_TYPE_TEXT_COLOR = 1 << 1;
+ const VAR_TYPE_BORDER_COLOR = 1 << 2;
+ const VAR_TYPE_BG_IMG = 1 << 3;
+ class VariablesStore {
+ constructor() {
+ this.varTypes = new Map();
+ this.rulesQueue = new Set();
+ this.inlineStyleQueue = [];
+ this.definedVars = new Set();
+ this.varRefs = new Map();
+ this.unknownColorVars = new Set();
+ this.unknownBgVars = new Set();
+ this.undefinedVars = new Set();
+ this.initialVarTypes = new Map();
+ this.changedTypeVars = new Set();
+ this.typeChangeSubscriptions = new Map();
+ this.unstableVarValues = new Map();
}
- return { isRaw: false, color: input };
- }
- function handleRawColorValue(input, theme, modifyFunction) {
- const { isRaw, color } = parseRawColorValue(input);
- const rgb = parseColorWithCache(color);
- if (rgb) {
- const outputColor = modifyFunction(rgb, theme);
- if (isRaw) {
- const outputInRGB = parseColorWithCache(outputColor);
- return outputInRGB
- ? `${outputInRGB.r}, ${outputInRGB.g}, ${outputInRGB.b}`
- : outputColor;
- }
- return outputColor;
+ clear() {
+ this.varTypes.clear();
+ this.rulesQueue.clear();
+ this.inlineStyleQueue.splice(0);
+ this.definedVars.clear();
+ this.varRefs.clear();
+ this.unknownColorVars.clear();
+ this.unknownBgVars.clear();
+ this.undefinedVars.clear();
+ this.initialVarTypes.clear();
+ this.changedTypeVars.clear();
+ this.typeChangeSubscriptions.clear();
+ this.unstableVarValues.clear();
}
- return color;
- }
- function tryModifyBgColor(color, theme) {
- return handleRawColorValue(color, theme, modifyBackgroundColor);
- }
- function tryModifyTextColor(color, theme) {
- return handleRawColorValue(color, theme, modifyForegroundColor);
- }
- function tryModifyBorderColor(color, theme) {
- return handleRawColorValue(color, theme, modifyBorderColor);
- }
- function insertVarValues(source, varValues, fullStack = new Set()) {
- let containsUnresolvedVar = false;
- const matchReplacer = (match, count) => {
- const { name, fallback } = getVariableNameAndFallback(match);
- const stack = count > 1 ? new Set(fullStack) : fullStack;
- if (stack.has(name)) {
- containsUnresolvedVar = true;
- return null;
+ isVarType(varName, typeNum) {
+ return (
+ this.varTypes.has(varName) &&
+ (this.varTypes.get(varName) & typeNum) > 0
+ );
+ }
+ addRulesForMatching(rules) {
+ this.rulesQueue.add(rules);
+ }
+ addInlineStyleForMatching(style) {
+ this.inlineStyleQueue.push(style);
+ }
+ matchVariablesAndDependents() {
+ if (
+ this.rulesQueue.size === 0 &&
+ this.inlineStyleQueue.length === 0
+ ) {
+ return;
}
- stack.add(name);
- const varValue = varValues.get(name) || fallback;
- let inserted = null;
- if (varValue) {
- if (isVarDependant(varValue)) {
- inserted = insertVarValues(varValue, varValues, stack);
+ this.changedTypeVars.clear();
+ this.initialVarTypes = new Map(this.varTypes);
+ this.collectRootVariables();
+ this.collectVariablesAndVarDep();
+ this.collectRootVarDependents();
+ this.varRefs.forEach((refs, v) => {
+ refs.forEach((r) => {
+ if (this.varTypes.has(v)) {
+ this.resolveVariableType(r, this.varTypes.get(v));
+ }
+ });
+ });
+ this.unknownColorVars.forEach((v) => {
+ if (this.unknownBgVars.has(v)) {
+ this.unknownColorVars.delete(v);
+ this.unknownBgVars.delete(v);
+ this.resolveVariableType(v, VAR_TYPE_BG_COLOR);
+ } else if (
+ this.isVarType(
+ v,
+ VAR_TYPE_BG_COLOR |
+ VAR_TYPE_TEXT_COLOR |
+ VAR_TYPE_BORDER_COLOR
+ )
+ ) {
+ this.unknownColorVars.delete(v);
} else {
- inserted = varValue;
+ this.undefinedVars.add(v);
}
- }
- if (!inserted) {
- containsUnresolvedVar = true;
- return null;
- }
- return inserted;
- };
- const replaced = replaceVariablesMatches(source, matchReplacer);
- if (containsUnresolvedVar) {
- return null;
+ });
+ this.unknownBgVars.forEach((v) => {
+ const hasColor =
+ this.findVarRef(v, (ref) => {
+ return (
+ this.unknownColorVars.has(ref) ||
+ this.isVarType(
+ ref,
+ VAR_TYPE_BG_COLOR |
+ VAR_TYPE_TEXT_COLOR |
+ VAR_TYPE_BORDER_COLOR
+ )
+ );
+ }) != null;
+ if (hasColor) {
+ this.iterateVarRefs(v, (ref) => {
+ this.resolveVariableType(ref, VAR_TYPE_BG_COLOR);
+ });
+ } else if (
+ this.isVarType(v, VAR_TYPE_BG_COLOR | VAR_TYPE_BG_IMG)
+ ) {
+ this.unknownBgVars.delete(v);
+ } else {
+ this.undefinedVars.add(v);
+ }
+ });
+ this.changedTypeVars.forEach((varName) => {
+ if (this.typeChangeSubscriptions.has(varName)) {
+ this.typeChangeSubscriptions
+ .get(varName)
+ .forEach((callback) => {
+ callback();
+ });
+ }
+ });
+ this.changedTypeVars.clear();
}
- return replaced;
- }
-
- const overrides$1 = {
- "background-color": {
- customProp: "--darkreader-inline-bgcolor",
- cssProp: "background-color",
- dataAttr: "data-darkreader-inline-bgcolor"
- },
- "background-image": {
- customProp: "--darkreader-inline-bgimage",
- cssProp: "background-image",
- dataAttr: "data-darkreader-inline-bgimage"
- },
- "border-color": {
- customProp: "--darkreader-inline-border",
- cssProp: "border-color",
- dataAttr: "data-darkreader-inline-border"
- },
- "border-bottom-color": {
- customProp: "--darkreader-inline-border-bottom",
- cssProp: "border-bottom-color",
- dataAttr: "data-darkreader-inline-border-bottom"
- },
- "border-left-color": {
- customProp: "--darkreader-inline-border-left",
- cssProp: "border-left-color",
- dataAttr: "data-darkreader-inline-border-left"
- },
- "border-right-color": {
- customProp: "--darkreader-inline-border-right",
- cssProp: "border-right-color",
- dataAttr: "data-darkreader-inline-border-right"
- },
- "border-top-color": {
- customProp: "--darkreader-inline-border-top",
- cssProp: "border-top-color",
- dataAttr: "data-darkreader-inline-border-top"
- },
- "box-shadow": {
- customProp: "--darkreader-inline-boxshadow",
- cssProp: "box-shadow",
- dataAttr: "data-darkreader-inline-boxshadow"
- },
- "color": {
- customProp: "--darkreader-inline-color",
- cssProp: "color",
- dataAttr: "data-darkreader-inline-color"
- },
- "fill": {
- customProp: "--darkreader-inline-fill",
- cssProp: "fill",
- dataAttr: "data-darkreader-inline-fill"
- },
- "stroke": {
- customProp: "--darkreader-inline-stroke",
- cssProp: "stroke",
- dataAttr: "data-darkreader-inline-stroke"
- },
- "outline-color": {
- customProp: "--darkreader-inline-outline",
- cssProp: "outline-color",
- dataAttr: "data-darkreader-inline-outline"
- },
- "stop-color": {
- customProp: "--darkreader-inline-stopcolor",
- cssProp: "stop-color",
- dataAttr: "data-darkreader-inline-stopcolor"
- }
- };
- const shorthandOverrides = {
- background: {
- customProp: "--darkreader-inline-bg",
- cssProp: "background",
- dataAttr: "data-darkreader-inline-bg"
- }
- };
- const overridesList = Object.values(overrides$1);
- const normalizedPropList = {};
- overridesList.forEach(
- ({ cssProp, customProp }) => (normalizedPropList[customProp] = cssProp)
- );
- const INLINE_STYLE_ATTRS = [
- "style",
- "fill",
- "stop-color",
- "stroke",
- "bgcolor",
- "color",
- "background"
- ];
- const INLINE_STYLE_SELECTOR = INLINE_STYLE_ATTRS.map(
- (attr) => `[${attr}]`
- ).join(", ");
- function getInlineOverrideStyle() {
- const allOverrides = overridesList.concat(
- Object.values(shorthandOverrides)
- );
- return allOverrides
- .map(({ dataAttr, customProp, cssProp }) => {
- return [
- `[${dataAttr}] {`,
- ` ${cssProp}: var(${customProp}) !important;`,
- "}"
- ].join("\n");
- })
- .concat([
- "[data-darkreader-inline-invert] {",
- " filter: invert(100%) hue-rotate(180deg);",
- "}"
- ])
- .join("\n");
- }
- function getInlineStyleElements(root) {
- const results = [];
- if (root instanceof Element && root.matches(INLINE_STYLE_SELECTOR)) {
- results.push(root);
- }
- if (
- root instanceof Element ||
- (isShadowDomSupported && root instanceof ShadowRoot) ||
- root instanceof Document
- ) {
- push(results, root.querySelectorAll(INLINE_STYLE_SELECTOR));
+ getModifierForVariable(options) {
+ return (theme) => {
+ const {
+ varName,
+ sourceValue,
+ rule,
+ ignoredImgSelectors,
+ isCancelled
+ } = options;
+ const getDeclarations = () => {
+ const declarations = [];
+ const addModifiedValue = (
+ typeNum,
+ varNameWrapper,
+ colorModifier
+ ) => {
+ if (!this.isVarType(varName, typeNum)) {
+ return;
+ }
+ const property = varNameWrapper(varName);
+ let modifiedValue;
+ if (isVarDependant(sourceValue)) {
+ if (isConstructedColorVar(sourceValue)) {
+ let value = insertVarValues(
+ sourceValue,
+ this.unstableVarValues
+ );
+ if (!value) {
+ value =
+ typeNum === VAR_TYPE_BG_COLOR
+ ? "#ffffff"
+ : "#000000";
+ }
+ modifiedValue = colorModifier(value, theme);
+ } else {
+ modifiedValue = replaceCSSVariablesNames(
+ sourceValue,
+ (v) => varNameWrapper(v),
+ (fallback) => colorModifier(fallback, theme)
+ );
+ }
+ } else {
+ modifiedValue = colorModifier(sourceValue, theme);
+ }
+ declarations.push({
+ property,
+ value: modifiedValue
+ });
+ };
+ addModifiedValue(
+ VAR_TYPE_BG_COLOR,
+ wrapBgColorVariableName,
+ tryModifyBgColor
+ );
+ addModifiedValue(
+ VAR_TYPE_TEXT_COLOR,
+ wrapTextColorVariableName,
+ tryModifyTextColor
+ );
+ addModifiedValue(
+ VAR_TYPE_BORDER_COLOR,
+ wrapBorderColorVariableName,
+ tryModifyBorderColor
+ );
+ if (this.isVarType(varName, VAR_TYPE_BG_IMG)) {
+ const property = wrapBgImgVariableName(varName);
+ let modifiedValue = sourceValue;
+ if (isVarDependant(sourceValue)) {
+ modifiedValue = replaceCSSVariablesNames(
+ sourceValue,
+ (v) => wrapBgColorVariableName(v),
+ (fallback) => tryModifyBgColor(fallback, theme)
+ );
+ }
+ const bgModifier = getBgImageModifier(
+ modifiedValue,
+ rule,
+ ignoredImgSelectors,
+ isCancelled
+ );
+ modifiedValue =
+ typeof bgModifier === "function"
+ ? bgModifier(theme)
+ : bgModifier;
+ declarations.push({
+ property,
+ value: modifiedValue
+ });
+ }
+ return declarations;
+ };
+ const callbacks = new Set();
+ const addListener = (onTypeChange) => {
+ const callback = () => {
+ const decs = getDeclarations();
+ onTypeChange(decs);
+ };
+ callbacks.add(callback);
+ this.subscribeForVarTypeChange(varName, callback);
+ };
+ const removeListeners = () => {
+ callbacks.forEach((callback) => {
+ this.unsubscribeFromVariableTypeChanges(
+ varName,
+ callback
+ );
+ });
+ };
+ return {
+ declarations: getDeclarations(),
+ onTypeChange: {addListener, removeListeners}
+ };
+ };
}
- return results;
- }
- const treeObservers = new Map();
- const attrObservers = new Map();
- function watchForInlineStyles(elementStyleDidChange, shadowRootDiscovered) {
- deepWatchForInlineStyles(
- document,
- elementStyleDidChange,
- shadowRootDiscovered
- );
- iterateShadowHosts(document.documentElement, (host) => {
- deepWatchForInlineStyles(
- host.shadowRoot,
- elementStyleDidChange,
- shadowRootDiscovered
+ getModifierForVarDependant(property, sourceValue) {
+ const isConstructedColor = sourceValue.match(/^\s*(rgb|hsl)a?\(/);
+ const isSimpleConstructedColor = sourceValue.match(
+ /^rgba?\(var\(--[\-_A-Za-z0-9]+\)(\s*,?\/?\s*0?\.\d+)?\)$/
);
- });
- }
- function deepWatchForInlineStyles(
- root,
- elementStyleDidChange,
- shadowRootDiscovered
- ) {
- if (treeObservers.has(root)) {
- treeObservers.get(root).disconnect();
- attrObservers.get(root).disconnect();
- }
- const discoveredNodes = new WeakSet();
- function discoverNodes(node) {
- getInlineStyleElements(node).forEach((el) => {
- if (discoveredNodes.has(el)) {
- return;
- }
- discoveredNodes.add(el);
- elementStyleDidChange(el);
- });
- iterateShadowHosts(node, (n) => {
- if (discoveredNodes.has(node)) {
- return;
- }
- discoveredNodes.add(node);
- shadowRootDiscovered(n.shadowRoot);
- deepWatchForInlineStyles(
- n.shadowRoot,
- elementStyleDidChange,
- shadowRootDiscovered
- );
- });
- variablesStore.matchVariablesAndDependents();
- }
- const treeObserver = createOptimizedTreeObserver(root, {
- onMinorMutations: (_root, { additions }) => {
- additions.forEach((added) => discoverNodes(added));
- },
- onHugeMutations: () => {
- discoverNodes(root);
+ if (isConstructedColor && !isSimpleConstructedColor) {
+ const isBg = property.startsWith("background");
+ const isText = isTextColorProperty(property);
+ return (theme) => {
+ let value = insertVarValues(
+ sourceValue,
+ this.unstableVarValues
+ );
+ if (!value) {
+ value = isBg ? "#ffffff" : "#000000";
+ }
+ const modifier = isBg
+ ? tryModifyBgColor
+ : isText
+ ? tryModifyTextColor
+ : tryModifyBorderColor;
+ return modifier(value, theme);
+ };
}
- });
- treeObservers.set(root, treeObserver);
- let attemptCount = 0;
- let start = null;
- const ATTEMPTS_INTERVAL = getDuration({ seconds: 10 });
- const RETRY_TIMEOUT = getDuration({ seconds: 2 });
- const MAX_ATTEMPTS_COUNT = 50;
- let cache = [];
- let timeoutId = null;
- const handleAttributeMutations = throttle((mutations) => {
- const handledTargets = new Set();
- mutations.forEach((m) => {
- const target = m.target;
- if (handledTargets.has(target)) {
- return;
- }
- if (INLINE_STYLE_ATTRS.includes(m.attributeName)) {
- handledTargets.add(target);
- elementStyleDidChange(target);
- }
- });
- variablesStore.matchVariablesAndDependents();
- });
- const attrObserver = new MutationObserver((mutations) => {
- if (timeoutId) {
- cache.push(...mutations);
- return;
+ if (
+ property === "background-color" ||
+ (isSimpleConstructedColor && property === "background")
+ ) {
+ return (theme) => {
+ const defaultFallback = tryModifyBgColor(
+ isConstructedColor ? "255, 255, 255" : "#ffffff",
+ theme
+ );
+ return replaceCSSVariablesNames(
+ sourceValue,
+ (v) => wrapBgColorVariableName(v),
+ (fallback) => tryModifyBgColor(fallback, theme),
+ defaultFallback
+ );
+ };
}
- attemptCount++;
- const now = Date.now();
- if (start == null) {
- start = now;
- } else if (attemptCount >= MAX_ATTEMPTS_COUNT) {
- if (now - start < ATTEMPTS_INTERVAL) {
- timeoutId = setTimeout(() => {
- start = null;
- attemptCount = 0;
- timeoutId = null;
- const attributeCache = cache;
- cache = [];
- handleAttributeMutations(attributeCache);
- }, RETRY_TIMEOUT);
- cache.push(...mutations);
- return;
- }
- start = now;
- attemptCount = 1;
+ if (isTextColorProperty(property)) {
+ return (theme) => {
+ const defaultFallback = tryModifyTextColor(
+ isConstructedColor ? "0, 0, 0" : "#000000",
+ theme
+ );
+ return replaceCSSVariablesNames(
+ sourceValue,
+ (v) => wrapTextColorVariableName(v),
+ (fallback) => tryModifyTextColor(fallback, theme),
+ defaultFallback
+ );
+ };
+ }
+ if (
+ property === "background" ||
+ property === "background-image" ||
+ property === "box-shadow"
+ ) {
+ return (theme) => {
+ const unknownVars = new Set();
+ const modify = () => {
+ const variableReplaced = replaceCSSVariablesNames(
+ sourceValue,
+ (v) => {
+ if (this.isVarType(v, VAR_TYPE_BG_COLOR)) {
+ return wrapBgColorVariableName(v);
+ }
+ if (this.isVarType(v, VAR_TYPE_BG_IMG)) {
+ return wrapBgImgVariableName(v);
+ }
+ unknownVars.add(v);
+ return v;
+ },
+ (fallback) => tryModifyBgColor(fallback, theme)
+ );
+ if (property === "box-shadow") {
+ const shadowModifier =
+ getShadowModifierWithInfo(variableReplaced);
+ const modifiedShadow = shadowModifier(theme);
+ if (
+ modifiedShadow.unparsableMatchesLength !==
+ modifiedShadow.matchesLength
+ ) {
+ return modifiedShadow.result;
+ }
+ }
+ return variableReplaced;
+ };
+ const modified = modify();
+ if (unknownVars.size > 0) {
+ const isFallbackResolved = modified.match(
+ /^var\(.*?, (var\(--darkreader-bg--.*\))|(#[0-9A-Fa-f]+)|([a-z]+)|(rgba?\(.+\))|(hsla?\(.+\))\)$/
+ );
+ if (isFallbackResolved) {
+ return modified;
+ }
+ return new Promise((resolve) => {
+ for (const unknownVar of unknownVars.values()) {
+ const callback = () => {
+ this.unsubscribeFromVariableTypeChanges(
+ unknownVar,
+ callback
+ );
+ const newValue = modify();
+ resolve(newValue);
+ };
+ this.subscribeForVarTypeChange(
+ unknownVar,
+ callback
+ );
+ }
+ });
+ }
+ return modified;
+ };
+ }
+ if (
+ property.startsWith("border") ||
+ property.startsWith("outline")
+ ) {
+ return (theme) => {
+ return replaceCSSVariablesNames(
+ sourceValue,
+ (v) => wrapBorderColorVariableName(v),
+ (fallback) => tryModifyBorderColor(fallback, theme)
+ );
+ };
}
- handleAttributeMutations(mutations);
- });
- attrObserver.observe(root, {
- attributes: true,
- attributeFilter: INLINE_STYLE_ATTRS.concat(
- overridesList.map(({ dataAttr }) => dataAttr)
- ),
- subtree: true
- });
- attrObservers.set(root, attrObserver);
- }
- function stopWatchingForInlineStyles() {
- treeObservers.forEach((o) => o.disconnect());
- attrObservers.forEach((o) => o.disconnect());
- treeObservers.clear();
- attrObservers.clear();
- }
- const inlineStyleCache = new WeakMap();
- const svgInversionCache = new WeakSet();
- const svgAnalysisConditionCache = new WeakMap();
- const themeProps = ["brightness", "contrast", "grayscale", "sepia", "mode"];
- function shouldAnalyzeSVGAsImage(svg) {
- if (svgAnalysisConditionCache.has(svg)) {
- return svgAnalysisConditionCache.get(svg);
+ return null;
}
- const shouldAnalyze = Boolean(
- svg &&
- (svg.getAttribute("class")?.includes("logo") ||
- svg.parentElement?.getAttribute("class")?.includes("logo"))
- );
- svgAnalysisConditionCache.set(svg, shouldAnalyze);
- return shouldAnalyze;
- }
- function getInlineStyleCacheKey(el, theme) {
- return INLINE_STYLE_ATTRS.map(
- (attr) => `${attr}="${el.getAttribute(attr)}"`
- )
- .concat(themeProps.map((prop) => `${prop}="${theme[prop]}"`))
- .join(" ");
- }
- function shouldIgnoreInlineStyle(element, selectors) {
- for (let i = 0, len = selectors.length; i < len; i++) {
- const ingnoredSelector = selectors[i];
- if (element.matches(ingnoredSelector)) {
- return true;
+ subscribeForVarTypeChange(varName, callback) {
+ if (!this.typeChangeSubscriptions.has(varName)) {
+ this.typeChangeSubscriptions.set(varName, new Set());
+ }
+ const rootStore = this.typeChangeSubscriptions.get(varName);
+ if (!rootStore.has(callback)) {
+ rootStore.add(callback);
}
}
- return false;
- }
- function overrideInlineStyle(
- element,
- theme,
- ignoreInlineSelectors,
- ignoreImageSelectors
- ) {
- const cacheKey = getInlineStyleCacheKey(element, theme);
- if (cacheKey === inlineStyleCache.get(element)) {
- return;
+ unsubscribeFromVariableTypeChanges(varName, callback) {
+ if (this.typeChangeSubscriptions.has(varName)) {
+ this.typeChangeSubscriptions.get(varName).delete(callback);
+ }
}
- const unsetProps = new Set(Object.keys(overrides$1));
- function setCustomProp(targetCSSProp, modifierCSSProp, cssVal) {
- const mod = getModifiableCSSDeclaration(
- modifierCSSProp,
- cssVal,
- { style: element.style },
- variablesStore,
- ignoreImageSelectors,
- null
+ collectVariablesAndVarDep() {
+ this.rulesQueue.forEach((rules) => {
+ iterateCSSRules(rules, (rule) => {
+ if (rule.style) {
+ this.collectVarsFromCSSDeclarations(rule.style);
+ }
+ });
+ });
+ this.inlineStyleQueue.forEach((style) => {
+ this.collectVarsFromCSSDeclarations(style);
+ });
+ this.rulesQueue.clear();
+ this.inlineStyleQueue.splice(0);
+ }
+ collectVarsFromCSSDeclarations(style) {
+ iterateCSSDeclarations(style, (property, value) => {
+ if (isVariable(property)) {
+ this.inspectVariable(property, value);
+ }
+ if (isVarDependant(value)) {
+ this.inspectVarDependant(property, value);
+ }
+ });
+ }
+ shouldProcessRootVariables() {
+ return (
+ this.rulesQueue.size > 0 &&
+ document.documentElement.getAttribute("style")?.includes("--")
);
- if (!mod) {
+ }
+ collectRootVariables() {
+ if (!this.shouldProcessRootVariables()) {
return;
}
- function setStaticValue(value) {
- const { customProp, dataAttr } =
- overrides$1[targetCSSProp] ??
- shorthandOverrides[targetCSSProp];
- element.style.setProperty(customProp, value);
- if (!element.hasAttribute(dataAttr)) {
- element.setAttribute(dataAttr, "");
- }
- unsetProps.delete(targetCSSProp);
- }
- function setVarDeclaration(mod) {
- let prevDeclarations = [];
- function setProps(declarations) {
- prevDeclarations.forEach(({ property }) => {
- element.style.removeProperty(property);
- });
- declarations.forEach(({ property, value }) => {
- if (!(value instanceof Promise)) {
- element.style.setProperty(property, value);
- }
- });
- prevDeclarations = declarations;
+ iterateCSSDeclarations(
+ document.documentElement.style,
+ (property, value) => {
+ if (isVariable(property)) {
+ this.inspectVariable(property, value);
+ }
}
- setProps(mod.declarations);
- mod.onTypeChange.addListener(setProps);
+ );
+ }
+ inspectVariable(varName, value) {
+ this.unstableVarValues.set(varName, value);
+ if (isVarDependant(value) && isConstructedColorVar(value)) {
+ this.unknownColorVars.add(varName);
+ this.definedVars.add(varName);
}
- function setAsyncValue(promise, sourceValue) {
- promise.then((value) => {
- if (
- value &&
- targetCSSProp === "background" &&
- value.startsWith("var(--darkreader-bg--")
- ) {
- setStaticValue(value);
- }
- if (value && targetCSSProp === "background-image") {
- if (
- (element === document.documentElement ||
- element === document.body) &&
- value === sourceValue
- ) {
- value = "none";
- }
- setStaticValue(value);
- }
- inlineStyleCache.set(
- element,
- getInlineStyleCacheKey(element, theme)
- );
- });
+ if (this.definedVars.has(varName)) {
+ return;
}
- const value =
- typeof mod.value === "function" ? mod.value(theme) : mod.value;
- if (typeof value === "string") {
- setStaticValue(value);
- } else if (value instanceof Promise) {
- setAsyncValue(value, cssVal);
- } else if (typeof value === "object") {
- setVarDeclaration(value);
+ this.definedVars.add(varName);
+ const isColor = Boolean(
+ value.match(rawRGBSpaceRegex) ||
+ value.match(rawRGBCommaRegex) ||
+ parseColorWithCache(value)
+ );
+ if (isColor) {
+ this.unknownColorVars.add(varName);
+ } else if (
+ value.includes("url(") ||
+ value.includes("linear-gradient(") ||
+ value.includes("radial-gradient(")
+ ) {
+ this.resolveVariableType(varName, VAR_TYPE_BG_IMG);
}
}
- if (ignoreInlineSelectors.length > 0) {
- if (shouldIgnoreInlineStyle(element, ignoreInlineSelectors)) {
- unsetProps.forEach((cssProp) => {
- element.removeAttribute(overrides$1[cssProp].dataAttr);
- });
- return;
+ resolveVariableType(varName, typeNum) {
+ const initialType = this.initialVarTypes.get(varName) || 0;
+ const currentType = this.varTypes.get(varName) || 0;
+ const newType = currentType | typeNum;
+ this.varTypes.set(varName, newType);
+ if (newType !== initialType || this.undefinedVars.has(varName)) {
+ this.changedTypeVars.add(varName);
+ this.undefinedVars.delete(varName);
}
+ this.unknownColorVars.delete(varName);
+ this.unknownBgVars.delete(varName);
}
- const isSVGElement = element instanceof SVGElement;
- const svg = isSVGElement
- ? element.ownerSVGElement ??
- (element instanceof SVGSVGElement ? element : null)
- : null;
- if (isSVGElement && theme.mode === 1 && svg) {
- if (svgInversionCache.has(svg)) {
+ collectRootVarDependents() {
+ if (!this.shouldProcessRootVariables()) {
return;
}
- if (shouldAnalyzeSVGAsImage(svg)) {
- svgInversionCache.add(svg);
- const analyzeSVGAsImage = () => {
- let svgString = svg.outerHTML;
- svgString = svgString.replaceAll(
- '',
- ""
- );
- const dataURL = `data:image/svg+xml;base64,${btoa(svgString)}`;
- getImageDetails(dataURL).then((details) => {
- if (
- (details.isDark && details.isTransparent) ||
- (details.isLarge &&
- details.isLight &&
- !details.isTransparent)
- ) {
- svg.setAttribute(
- "data-darkreader-inline-invert",
- ""
+ iterateCSSDeclarations(
+ document.documentElement.style,
+ (property, value) => {
+ if (isVarDependant(value)) {
+ this.inspectVarDependant(property, value);
+ }
+ }
+ );
+ }
+ inspectVarDependant(property, value) {
+ if (isVariable(property)) {
+ this.iterateVarDeps(value, (ref) => {
+ if (!this.varRefs.has(property)) {
+ this.varRefs.set(property, new Set());
+ }
+ this.varRefs.get(property).add(ref);
+ });
+ } else if (
+ property === "background-color" ||
+ property === "box-shadow"
+ ) {
+ this.iterateVarDeps(value, (v) =>
+ this.resolveVariableType(v, VAR_TYPE_BG_COLOR)
+ );
+ } else if (isTextColorProperty(property)) {
+ this.iterateVarDeps(value, (v) =>
+ this.resolveVariableType(v, VAR_TYPE_TEXT_COLOR)
+ );
+ } else if (
+ property.startsWith("border") ||
+ property.startsWith("outline")
+ ) {
+ this.iterateVarDeps(value, (v) =>
+ this.resolveVariableType(v, VAR_TYPE_BORDER_COLOR)
+ );
+ } else if (
+ property === "background" ||
+ property === "background-image"
+ ) {
+ this.iterateVarDeps(value, (v) => {
+ if (
+ this.isVarType(v, VAR_TYPE_BG_COLOR | VAR_TYPE_BG_IMG)
+ ) {
+ return;
+ }
+ const isBgColor =
+ this.findVarRef(v, (ref) => {
+ return (
+ this.unknownColorVars.has(ref) ||
+ this.isVarType(
+ ref,
+ VAR_TYPE_BG_COLOR |
+ VAR_TYPE_TEXT_COLOR |
+ VAR_TYPE_BORDER_COLOR
+ )
);
+ }) != null;
+ this.iterateVarRefs(v, (ref) => {
+ if (isBgColor) {
+ this.resolveVariableType(ref, VAR_TYPE_BG_COLOR);
} else {
- svg.removeAttribute(
- "data-darkreader-inline-invert"
- );
+ this.unknownBgVars.add(ref);
}
});
- };
- analyzeSVGAsImage();
- if (!isDOMReady()) {
- addDOMReadyListener(analyzeSVGAsImage);
- }
- return;
+ });
}
}
- if (element.hasAttribute("bgcolor")) {
- let value = element.getAttribute("bgcolor");
- if (
- value.match(/^[0-9a-f]{3}$/i) ||
- value.match(/^[0-9a-f]{6}$/i)
- ) {
- value = `#${value}`;
+ iterateVarDeps(value, iterator) {
+ const varDeps = new Set();
+ iterateVarDependencies(value, (v) => varDeps.add(v));
+ varDeps.forEach((v) => iterator(v));
+ }
+ findVarRef(varName, iterator, stack = new Set()) {
+ if (stack.has(varName)) {
+ return null;
}
- setCustomProp("background-color", "background-color", value);
+ stack.add(varName);
+ const result = iterator(varName);
+ if (result) {
+ return varName;
+ }
+ const refs = this.varRefs.get(varName);
+ if (!refs || refs.size === 0) {
+ return null;
+ }
+ for (const ref of refs) {
+ const found = this.findVarRef(ref, iterator, stack);
+ if (found) {
+ return found;
+ }
+ }
+ return null;
}
- if (
- (element === document.documentElement ||
- element === document.body) &&
- element.hasAttribute("background")
- ) {
- const url = getAbsoluteURL(
- location.href,
- element.getAttribute("background") ?? ""
- );
- const value = `url("${url}")`;
- setCustomProp("background-image", "background-image", value);
+ iterateVarRefs(varName, iterator) {
+ this.findVarRef(varName, (ref) => {
+ iterator(ref);
+ return false;
+ });
}
- if (element.hasAttribute("color") && element.rel !== "mask-icon") {
- let value = element.getAttribute("color");
- if (
- value.match(/^[0-9a-f]{3}$/i) ||
- value.match(/^[0-9a-f]{6}$/i)
- ) {
- value = `#${value}`;
- }
- setCustomProp("color", "color", value);
+ setOnRootVariableChange(callback) {
+ this.onRootVariableDefined = callback;
}
- if (isSVGElement) {
- if (element.hasAttribute("fill")) {
- const SMALL_SVG_LIMIT = 32;
- const value = element.getAttribute("fill");
- if (value !== "none") {
- if (!(element instanceof SVGTextElement)) {
- const handleSVGElement = () => {
- const { width, height } =
- element.getBoundingClientRect();
- const isBg =
- width > SMALL_SVG_LIMIT ||
- height > SMALL_SVG_LIMIT;
- setCustomProp(
- "fill",
- isBg ? "background-color" : "color",
- value
+ putRootVars(styleElement, theme) {
+ const sheet = styleElement.sheet;
+ if (sheet.cssRules.length > 0) {
+ sheet.deleteRule(0);
+ }
+ const declarations = new Map();
+ iterateCSSDeclarations(
+ document.documentElement.style,
+ (property, value) => {
+ if (isVariable(property)) {
+ if (this.isVarType(property, VAR_TYPE_BG_COLOR)) {
+ declarations.set(
+ wrapBgColorVariableName(property),
+ tryModifyBgColor(value, theme)
);
- };
- if (isReadyStateComplete()) {
- handleSVGElement();
- } else {
- addReadyStateCompleteListener(handleSVGElement);
}
- } else {
- setCustomProp("fill", "color", value);
+ if (this.isVarType(property, VAR_TYPE_TEXT_COLOR)) {
+ declarations.set(
+ wrapTextColorVariableName(property),
+ tryModifyTextColor(value, theme)
+ );
+ }
+ if (this.isVarType(property, VAR_TYPE_BORDER_COLOR)) {
+ declarations.set(
+ wrapBorderColorVariableName(property),
+ tryModifyBorderColor(value, theme)
+ );
+ }
+ this.subscribeForVarTypeChange(
+ property,
+ this.onRootVariableDefined
+ );
}
}
+ );
+ const cssLines = [];
+ cssLines.push(":root {");
+ for (const [property, value] of declarations) {
+ cssLines.push(` ${property}: ${value};`);
}
- if (element.hasAttribute("stop-color")) {
- setCustomProp(
- "stop-color",
- "background-color",
- element.getAttribute("stop-color")
- );
+ cssLines.push("}");
+ const cssText = cssLines.join("\n");
+ sheet.insertRule(cssText);
+ }
+ }
+ const variablesStore = new VariablesStore();
+ function getVariableRange(input, searchStart = 0) {
+ const start = input.indexOf("var(", searchStart);
+ if (start >= 0) {
+ const range = getParenthesesRange(input, start + 3);
+ if (range) {
+ return {start, end: range.end};
}
}
- if (element.hasAttribute("stroke")) {
- const value = element.getAttribute("stroke");
- setCustomProp(
- "stroke",
- element instanceof SVGLineElement ||
- element instanceof SVGTextElement
- ? "border-color"
- : "color",
- value
- );
+ return null;
+ }
+ function getVariablesMatches(input) {
+ const ranges = [];
+ let i = 0;
+ let range;
+ while ((range = getVariableRange(input, i))) {
+ const {start, end} = range;
+ ranges.push({start, end, value: input.substring(start, end)});
+ i = range.end + 1;
}
- element.style &&
- iterateCSSDeclarations(element.style, (property, value) => {
- if (property === "background-image" && value.includes("url")) {
- if (
- element === document.documentElement ||
- element === document.body
- ) {
- setCustomProp(property, property, value);
- }
- return;
- }
- if (
- overrides$1.hasOwnProperty(property) ||
- (property.startsWith("--") && !normalizedPropList[property])
- ) {
- setCustomProp(property, property, value);
- } else if (
- property === "background" &&
- value.includes("var(")
- ) {
- setCustomProp("background", "background", value);
- } else {
- const overriddenProp = normalizedPropList[property];
- if (
- overriddenProp &&
- !element.style.getPropertyValue(overriddenProp) &&
- !element.hasAttribute(overriddenProp)
- ) {
- if (
- overriddenProp === "background-color" &&
- element.hasAttribute("bgcolor")
- ) {
- return;
- }
- element.style.setProperty(property, "");
- }
- }
- });
- if (
- element.style &&
- element instanceof SVGTextElement &&
- element.style.fill
- ) {
- setCustomProp(
- "fill",
- "color",
- element.style.getPropertyValue("fill")
- );
+ return ranges;
+ }
+ function replaceVariablesMatches(input, replacer) {
+ const matches = getVariablesMatches(input);
+ const matchesCount = matches.length;
+ if (matchesCount === 0) {
+ return input;
}
- if (element.getAttribute("style")?.includes("--")) {
- variablesStore.addInlineStyleForMatching(element.style);
+ const inputLength = input.length;
+ const replacements = matches.map((m) =>
+ replacer(m.value, matches.length)
+ );
+ const parts = [];
+ parts.push(input.substring(0, matches[0].start));
+ for (let i = 0; i < matchesCount; i++) {
+ parts.push(replacements[i]);
+ const start = matches[i].end;
+ const end =
+ i < matchesCount - 1 ? matches[i + 1].start : inputLength;
+ parts.push(input.substring(start, end));
+ }
+ return parts.join("");
+ }
+ function getVariableNameAndFallback(match) {
+ const commaIndex = match.indexOf(",");
+ let name;
+ let fallback;
+ if (commaIndex >= 0) {
+ name = match.substring(4, commaIndex).trim();
+ fallback = match.substring(commaIndex + 1, match.length - 1).trim();
+ } else {
+ name = match.substring(4, match.length - 1).trim();
+ fallback = "";
}
- forEach(unsetProps, (cssProp) => {
- element.removeAttribute(overrides$1[cssProp].dataAttr);
+ return {name, fallback};
+ }
+ function replaceCSSVariablesNames(
+ value,
+ nameReplacer,
+ fallbackReplacer,
+ finalFallback
+ ) {
+ const matchReplacer = (match) => {
+ const {name, fallback} = getVariableNameAndFallback(match);
+ const newName = nameReplacer(name);
+ if (!fallback) {
+ if (finalFallback) {
+ return `var(${newName}, ${finalFallback})`;
+ }
+ return `var(${newName})`;
+ }
+ let newFallback;
+ if (isVarDependant(fallback)) {
+ newFallback = replaceCSSVariablesNames(
+ fallback,
+ nameReplacer,
+ fallbackReplacer
+ );
+ } else if (fallbackReplacer) {
+ newFallback = fallbackReplacer(fallback);
+ } else {
+ newFallback = fallback;
+ }
+ return `var(${newName}, ${newFallback})`;
+ };
+ return replaceVariablesMatches(value, matchReplacer);
+ }
+ function iterateVarDependencies(value, iterator) {
+ replaceCSSVariablesNames(value, (varName) => {
+ iterator(varName);
+ return varName;
});
- inlineStyleCache.set(element, getInlineStyleCacheKey(element, theme));
}
-
- const metaThemeColorName = "theme-color";
- const metaThemeColorSelector = `meta[name="${metaThemeColorName}"]`;
- let srcMetaThemeColor = null;
- let observer = null;
- function changeMetaThemeColor(meta, theme) {
- srcMetaThemeColor = srcMetaThemeColor || meta.content;
- const color = parseColorWithCache(srcMetaThemeColor);
- if (!color) {
- return;
+ function wrapBgColorVariableName(name) {
+ return `--darkreader-bg${name}`;
+ }
+ function wrapTextColorVariableName(name) {
+ return `--darkreader-text${name}`;
+ }
+ function wrapBorderColorVariableName(name) {
+ return `--darkreader-border${name}`;
+ }
+ function wrapBgImgVariableName(name) {
+ return `--darkreader-bgimg${name}`;
+ }
+ function isVariable(property) {
+ return property.startsWith("--");
+ }
+ function isVarDependant(value) {
+ return value.includes("var(");
+ }
+ function isConstructedColorVar(value) {
+ return (
+ value.match(/^\s*(rgb|hsl)a?\(/) ||
+ value.match(/^(((\d{1,3})|(var\([\-_A-Za-z0-9]+\))),?\s*?){3}$/)
+ );
+ }
+ const textColorProps = [
+ "color",
+ "caret-color",
+ "-webkit-text-fill-color",
+ "fill",
+ "stroke"
+ ];
+ function isTextColorProperty(property) {
+ return textColorProps.includes(property);
+ }
+ const rawRGBSpaceRegex = /^(\d{1,3})\s+(\d{1,3})\s+(\d{1,3})$/;
+ const rawRGBCommaRegex = /^(\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})$/;
+ function parseRawColorValue(input) {
+ const match =
+ input.match(rawRGBSpaceRegex) ?? input.match(rawRGBCommaRegex);
+ if (match) {
+ const color = `rgb(${match[1]}, ${match[2]}, ${match[3]})`;
+ return {isRaw: true, color};
}
- meta.content = modifyBackgroundColor(color, theme);
+ return {isRaw: false, color: input};
}
- function changeMetaThemeColorWhenAvailable(theme) {
- const meta = document.querySelector(metaThemeColorSelector);
- if (meta) {
- changeMetaThemeColor(meta, theme);
- } else {
- if (observer) {
- observer.disconnect();
+ function handleRawColorValue(input, theme, modifyFunction) {
+ const {isRaw, color} = parseRawColorValue(input);
+ const rgb = parseColorWithCache(color);
+ if (rgb) {
+ const outputColor = modifyFunction(rgb, theme);
+ if (isRaw) {
+ const outputInRGB = parseColorWithCache(outputColor);
+ return outputInRGB
+ ? `${outputInRGB.r}, ${outputInRGB.g}, ${outputInRGB.b}`
+ : outputColor;
}
- observer = new MutationObserver((mutations) => {
- loop: for (let i = 0; i < mutations.length; i++) {
- const { addedNodes } = mutations[i];
- for (let j = 0; j < addedNodes.length; j++) {
- const node = addedNodes[j];
- if (
- node instanceof HTMLMetaElement &&
- node.name === metaThemeColorName
- ) {
- observer.disconnect();
- observer = null;
- changeMetaThemeColor(node, theme);
- break loop;
- }
- }
- }
- });
- observer.observe(document.head, { childList: true });
+ return outputColor;
}
+ return color;
}
- function restoreMetaThemeColor() {
- if (observer) {
- observer.disconnect();
- observer = null;
- }
- const meta = document.querySelector(metaThemeColorSelector);
- if (meta && srcMetaThemeColor) {
- meta.content = srcMetaThemeColor;
- }
+ function tryModifyBgColor(color, theme) {
+ return handleRawColorValue(color, theme, modifyBackgroundColor);
}
-
- const cssCommentsRegex = /\/\*[\s\S]*?\*\//g;
- function removeCSSComments(cssText) {
- return cssText.replace(cssCommentsRegex, "");
+ function tryModifyTextColor(color, theme) {
+ return handleRawColorValue(color, theme, modifyForegroundColor);
+ }
+ function tryModifyBorderColor(color, theme) {
+ return handleRawColorValue(color, theme, modifyBorderColor);
+ }
+ function insertVarValues(source, varValues, fullStack = new Set()) {
+ let containsUnresolvedVar = false;
+ const matchReplacer = (match, count) => {
+ const {name, fallback} = getVariableNameAndFallback(match);
+ const stack = count > 1 ? new Set(fullStack) : fullStack;
+ if (stack.has(name)) {
+ containsUnresolvedVar = true;
+ return null;
+ }
+ stack.add(name);
+ const varValue = varValues.get(name) || fallback;
+ let inserted = null;
+ if (varValue) {
+ if (isVarDependant(varValue)) {
+ inserted = insertVarValues(varValue, varValues, stack);
+ } else {
+ inserted = varValue;
+ }
+ }
+ if (!inserted) {
+ containsUnresolvedVar = true;
+ return null;
+ }
+ return inserted;
+ };
+ const replaced = replaceVariablesMatches(source, matchReplacer);
+ if (containsUnresolvedVar) {
+ return null;
+ }
+ return replaced;
}
const themeCacheKeys = [
@@ -4840,7 +4583,7 @@
}
renderId++;
function setRule(target, index, rule) {
- const { selector, declarations } = rule;
+ const {selector, declarations} = rule;
let selectorText = selector;
const emptyIsWhereSelector =
isChromium &&
@@ -4856,7 +4599,7 @@
}
let ruleText = `${selectorText} {`;
for (const dec of declarations) {
- const { property, value, important } = dec;
+ const {property, value, important} = dec;
if (value) {
ruleText += ` ${property}: ${value}${important ? " !important" : ""};`;
}
@@ -4868,7 +4611,7 @@
const varDeclarations = new Map();
let asyncDeclarationCounter = 0;
let varDeclarationCounter = 0;
- const rootReadyGroup = { rule: null, rules: [], isGroup: true };
+ const rootReadyGroup = {rule: null, rules: [], isGroup: true};
const groupRefs = new WeakMap();
function getGroup(rule) {
if (rule == null) {
@@ -4877,7 +4620,7 @@
if (groupRefs.has(rule)) {
return groupRefs.get(rule);
}
- const group = { rule, rules: [], isGroup: true };
+ const group = {rule, rules: [], isGroup: true};
groupRefs.set(rule, group);
const parentGroup = getGroup(rule.parentRule);
parentGroup.rules.push(group);
@@ -4887,7 +4630,7 @@
varTypeChangeCleaners.clear();
modRules
.filter((r) => r)
- .forEach(({ selector, declarations, parentRule }) => {
+ .forEach(({selector, declarations, parentRule}) => {
const group = getGroup(parentRule);
const readyStyleRule = {
selector,
@@ -4938,7 +4681,7 @@
important,
sourceValue
) {
- const { declarations: varDecs, onTypeChange } = modified;
+ const {declarations: varDecs, onTypeChange} = modified;
const varKey = ++varDeclarationCounter;
const currentRenderId = renderId;
const initialIndex = readyDeclarations.length;
@@ -5007,7 +4750,7 @@
);
}
declarations.forEach(
- ({ property, value, important, sourceValue }) => {
+ ({property, value, important, sourceValue}) => {
if (typeof value === "function") {
const modified = value(theme);
if (modified instanceof Promise) {
@@ -5046,9 +4789,9 @@
const sheet = prepareSheet();
function buildStyleSheet() {
function createTarget(group, parent) {
- const { rule } = group;
+ const {rule} = group;
if (isMediaRule(rule)) {
- const { media } = rule;
+ const {media} = rule;
const index = parent.cssRules.length;
parent.insertRule(
`@media ${media.mediaText} {}`,
@@ -5057,7 +4800,7 @@
return parent.cssRules[index];
}
if (isLayerRule(rule)) {
- const { name } = rule;
+ const {name} = rule;
const index = parent.cssRules.length;
parent.insertRule(`@layer ${name} {}`, index);
return parent.cssRules[index];
@@ -5076,7 +4819,7 @@
}
iterateReadyRules(rootReadyGroup, sheet, (rule, target) => {
const index = target.cssRules.length;
- rule.declarations.forEach(({ asyncKey, varKey }) => {
+ rule.declarations.forEach(({asyncKey, varKey}) => {
if (asyncKey != null) {
asyncDeclarations.set(asyncKey, {
rule,
@@ -5085,1708 +4828,2321 @@
});
}
if (varKey != null) {
- varDeclarations.set(varKey, { rule, target, index });
+ varDeclarations.set(varKey, {rule, target, index});
}
});
setRule(target, index, rule);
});
}
function rebuildAsyncRule(key) {
- const { rule, target, index } = asyncDeclarations.get(key);
+ const {rule, target, index} = asyncDeclarations.get(key);
target.deleteRule(index);
setRule(target, index, rule);
asyncDeclarations.delete(key);
}
function rebuildVarRule(key) {
- const { rule, target, index } = varDeclarations.get(key);
+ const {rule, target, index} = varDeclarations.get(key);
target.deleteRule(index);
setRule(target, index, rule);
}
buildStyleSheet();
}
- return { modifySheet, shouldRebuildStyle };
+ return {modifySheet, shouldRebuildStyle};
}
let canUseSheetProxy$1 = false;
document.addEventListener(
"__darkreader__inlineScriptsAllowed",
() => (canUseSheetProxy$1 = true),
- { once: true }
+ {once: true}
);
- function createSheetWatcher(
- element,
- safeGetSheetRules,
- callback,
- isCancelled
- ) {
- let rafSheetWatcher = null;
- function watchForSheetChanges() {
- watchForSheetChangesUsingProxy();
- if (!(canUseSheetProxy$1 && element.sheet)) {
- rafSheetWatcher = createRAFSheetWatcher(
- element,
- safeGetSheetRules,
- callback,
- isCancelled
+ const overrides$1 = new WeakSet();
+ const overridesBySource = new WeakMap();
+ function canHaveAdoptedStyleSheets(node) {
+ return Array.isArray(node.adoptedStyleSheets);
+ }
+ function createAdoptedStyleSheetOverride(node) {
+ let cancelAsyncOperations = false;
+ function iterateSourceSheets(iterator) {
+ node.adoptedStyleSheets.forEach((sheet) => {
+ if (!overrides$1.has(sheet)) {
+ iterator(sheet);
+ }
+ defineSheetScope(sheet, node);
+ });
+ }
+ function injectSheet(sheet, override) {
+ const newSheets = [...node.adoptedStyleSheets];
+ const sheetIndex = newSheets.indexOf(sheet);
+ const overrideIndex = newSheets.indexOf(override);
+ if (overrideIndex >= 0) {
+ newSheets.splice(overrideIndex, 1);
+ }
+ newSheets.splice(sheetIndex + 1, 0, override);
+ node.adoptedStyleSheets = newSheets;
+ }
+ function clear() {
+ const newSheets = [...node.adoptedStyleSheets];
+ for (let i = newSheets.length - 1; i >= 0; i--) {
+ const sheet = newSheets[i];
+ if (overrides$1.has(sheet)) {
+ newSheets.splice(i, 1);
+ }
+ }
+ if (node.adoptedStyleSheets.length !== newSheets.length) {
+ node.adoptedStyleSheets = newSheets;
+ }
+ sourceSheets = new WeakSet();
+ sourceDeclarations = new WeakSet();
+ }
+ const cleaners = [];
+ function destroy() {
+ cleaners.forEach((c) => c());
+ cleaners.splice(0);
+ cancelAsyncOperations = true;
+ clear();
+ if (frameId) {
+ cancelAnimationFrame(frameId);
+ frameId = null;
+ }
+ }
+ let rulesChangeKey = 0;
+ function getRulesChangeKey() {
+ let count = 0;
+ iterateSourceSheets((sheet) => {
+ count += sheet.cssRules.length;
+ });
+ if (count === 1) {
+ const rule = node.adoptedStyleSheets[0].cssRules[0];
+ return rule instanceof CSSStyleRule ? rule.style.length : count;
+ }
+ return count;
+ }
+ let sourceSheets = new WeakSet();
+ let sourceDeclarations = new WeakSet();
+ function render(theme, ignoreImageAnalysis) {
+ clear();
+ for (let i = node.adoptedStyleSheets.length - 1; i >= 0; i--) {
+ const sheet = node.adoptedStyleSheets[i];
+ if (overrides$1.has(sheet)) {
+ continue;
+ }
+ sourceSheets.add(sheet);
+ const readyOverride = overridesBySource.get(sheet);
+ if (readyOverride) {
+ rulesChangeKey = getRulesChangeKey();
+ injectSheet(sheet, readyOverride);
+ continue;
+ }
+ const rules = sheet.cssRules;
+ const override = new CSSStyleSheet();
+ overridesBySource.set(sheet, override);
+ iterateCSSRules(rules, (rule) =>
+ sourceDeclarations.add(rule.style)
);
- rafSheetWatcher.start();
+ const prepareSheet = () => {
+ for (let i = override.cssRules.length - 1; i >= 0; i--) {
+ override.deleteRule(i);
+ }
+ override.insertRule("#__darkreader__adoptedOverride {}");
+ injectSheet(sheet, override);
+ overrides$1.add(override);
+ return override;
+ };
+ const sheetModifier = createStyleSheetModifier();
+ sheetModifier.modifySheet({
+ prepareSheet,
+ sourceCSSRules: rules,
+ theme,
+ ignoreImageAnalysis,
+ force: false,
+ isAsyncCancelled: () => cancelAsyncOperations
+ });
}
+ rulesChangeKey = getRulesChangeKey();
}
- let areSheetChangesPending = false;
- function onSheetChange() {
- canUseSheetProxy$1 = true;
- rafSheetWatcher?.stop();
- if (areSheetChangesPending) {
+ let callbackRequested = false;
+ function handleArrayChange(callback) {
+ if (callbackRequested) {
return;
}
- function handleSheetChanges() {
- areSheetChangesPending = false;
- if (isCancelled()) {
+ callbackRequested = true;
+ queueMicrotask(() => {
+ callbackRequested = false;
+ const sheets = node.adoptedStyleSheets.filter(
+ (s) => !overrides$1.has(s)
+ );
+ sheets.forEach((sheet) => overridesBySource.delete(sheet));
+ callback(sheets);
+ });
+ }
+ function checkForUpdates() {
+ return getRulesChangeKey() !== rulesChangeKey;
+ }
+ let frameId = null;
+ function watchUsingRAF(callback) {
+ frameId = requestAnimationFrame(() => {
+ if (canUseSheetProxy$1) {
return;
}
- callback();
+ if (checkForUpdates()) {
+ handleArrayChange(callback);
+ }
+ watchUsingRAF(callback);
+ });
+ }
+ function addSheetChangeEventListener(type, listener) {
+ node.addEventListener(type, listener);
+ cleaners.push(() => node.removeEventListener(type, listener));
+ }
+ function watch(callback) {
+ const onAdoptedSheetsChange = () => {
+ canUseSheetProxy$1 = true;
+ handleArrayChange(callback);
+ };
+ addSheetChangeEventListener(
+ "__darkreader__adoptedStyleSheetsChange",
+ onAdoptedSheetsChange
+ );
+ addSheetChangeEventListener(
+ "__darkreader__adoptedStyleSheetChange",
+ onAdoptedSheetsChange
+ );
+ addSheetChangeEventListener(
+ "__darkreader__adoptedStyleDeclarationChange",
+ onAdoptedSheetsChange
+ );
+ if (canUseSheetProxy$1) {
+ return;
}
- areSheetChangesPending = true;
- queueMicrotask(handleSheetChanges);
+ watchUsingRAF(callback);
+ }
+ return {
+ render,
+ destroy,
+ watch
+ };
+ }
+ class StyleSheetCommandBuilder {
+ constructor() {
+ this.cssRules = [];
+ this.commands = [];
+ }
+ insertRule(cssText, index = 0) {
+ this.commands.push({type: "insert", index, cssText});
+ this.cssRules.splice(index, 0, new StyleSheetCommandBuilder());
+ return index;
+ }
+ deleteRule(index) {
+ this.commands.push({type: "delete", index});
+ this.cssRules.splice(index, 1);
+ }
+ replaceSync(cssText) {
+ this.commands.splice(0);
+ this.commands.push({type: "replace", cssText});
+ if (cssText === "") {
+ this.cssRules.splice(0);
+ } else {
+ throw new Error(
+ "StyleSheetCommandBuilder.replaceSync() is not fully supported"
+ );
+ }
+ }
+ getDeepCSSCommands() {
+ const deep = [];
+ this.commands.forEach((command) => {
+ deep.push({
+ type: command.type,
+ cssText: command.type !== "delete" ? command.cssText : "",
+ path: command.type === "replace" ? [] : [command.index]
+ });
+ });
+ this.cssRules.forEach((rule, i) => {
+ const childCommands = rule.getDeepCSSCommands();
+ childCommands.forEach((c) => c.path.unshift(i));
+ });
+ return deep;
+ }
+ clearDeepCSSCommands() {
+ this.commands.splice(0);
+ this.cssRules.forEach((rule) => rule.clearDeepCSSCommands());
+ }
+ }
+ function createAdoptedStyleSheetFallback() {
+ let cancelAsyncOperations = false;
+ const builder = new StyleSheetCommandBuilder();
+ function render(options) {
+ const prepareSheet = () => {
+ builder.replaceSync("");
+ return builder;
+ };
+ const sheetModifier = createStyleSheetModifier();
+ sheetModifier.modifySheet({
+ prepareSheet,
+ sourceCSSRules: options.cssRules,
+ theme: options.theme,
+ ignoreImageAnalysis: options.ignoreImageAnalysis,
+ force: false,
+ isAsyncCancelled: () => cancelAsyncOperations
+ });
+ }
+ function commands() {
+ const commands = builder.getDeepCSSCommands();
+ builder.clearDeepCSSCommands();
+ return commands;
+ }
+ function destroy() {
+ cancelAsyncOperations = true;
+ }
+ return {render, destroy, commands};
+ }
+
+ const overrides = {
+ "background-color": {
+ customProp: "--darkreader-inline-bgcolor",
+ cssProp: "background-color",
+ dataAttr: "data-darkreader-inline-bgcolor"
+ },
+ "background-image": {
+ customProp: "--darkreader-inline-bgimage",
+ cssProp: "background-image",
+ dataAttr: "data-darkreader-inline-bgimage"
+ },
+ "border-color": {
+ customProp: "--darkreader-inline-border",
+ cssProp: "border-color",
+ dataAttr: "data-darkreader-inline-border"
+ },
+ "border-bottom-color": {
+ customProp: "--darkreader-inline-border-bottom",
+ cssProp: "border-bottom-color",
+ dataAttr: "data-darkreader-inline-border-bottom"
+ },
+ "border-left-color": {
+ customProp: "--darkreader-inline-border-left",
+ cssProp: "border-left-color",
+ dataAttr: "data-darkreader-inline-border-left"
+ },
+ "border-right-color": {
+ customProp: "--darkreader-inline-border-right",
+ cssProp: "border-right-color",
+ dataAttr: "data-darkreader-inline-border-right"
+ },
+ "border-top-color": {
+ customProp: "--darkreader-inline-border-top",
+ cssProp: "border-top-color",
+ dataAttr: "data-darkreader-inline-border-top"
+ },
+ "box-shadow": {
+ customProp: "--darkreader-inline-boxshadow",
+ cssProp: "box-shadow",
+ dataAttr: "data-darkreader-inline-boxshadow"
+ },
+ "color": {
+ customProp: "--darkreader-inline-color",
+ cssProp: "color",
+ dataAttr: "data-darkreader-inline-color"
+ },
+ "fill": {
+ customProp: "--darkreader-inline-fill",
+ cssProp: "fill",
+ dataAttr: "data-darkreader-inline-fill"
+ },
+ "stroke": {
+ customProp: "--darkreader-inline-stroke",
+ cssProp: "stroke",
+ dataAttr: "data-darkreader-inline-stroke"
+ },
+ "outline-color": {
+ customProp: "--darkreader-inline-outline",
+ cssProp: "outline-color",
+ dataAttr: "data-darkreader-inline-outline"
+ },
+ "stop-color": {
+ customProp: "--darkreader-inline-stopcolor",
+ cssProp: "stop-color",
+ dataAttr: "data-darkreader-inline-stopcolor"
+ }
+ };
+ const shorthandOverrides = {
+ background: {
+ customProp: "--darkreader-inline-bg",
+ cssProp: "background",
+ dataAttr: "data-darkreader-inline-bg"
+ }
+ };
+ const overridesList = Object.values(overrides);
+ const normalizedPropList = {};
+ overridesList.forEach(
+ ({cssProp, customProp}) => (normalizedPropList[customProp] = cssProp)
+ );
+ const INLINE_STYLE_ATTRS = [
+ "style",
+ "fill",
+ "stop-color",
+ "stroke",
+ "bgcolor",
+ "color",
+ "background"
+ ];
+ const INLINE_STYLE_SELECTOR = INLINE_STYLE_ATTRS.map(
+ (attr) => `[${attr}]`
+ ).join(", ");
+ function getInlineOverrideStyle() {
+ const allOverrides = overridesList.concat(
+ Object.values(shorthandOverrides)
+ );
+ return allOverrides
+ .map(({dataAttr, customProp, cssProp}) => {
+ return [
+ `[${dataAttr}] {`,
+ ` ${cssProp}: var(${customProp}) !important;`,
+ "}"
+ ].join("\n");
+ })
+ .concat([
+ "[data-darkreader-inline-invert] {",
+ " filter: invert(100%) hue-rotate(180deg);",
+ "}"
+ ])
+ .join("\n");
+ }
+ function getInlineStyleElements(root) {
+ const results = [];
+ if (root instanceof Element && root.matches(INLINE_STYLE_SELECTOR)) {
+ results.push(root);
}
- function watchForSheetChangesUsingProxy() {
- element.addEventListener(
- "__darkreader__updateSheet",
- onSheetChange
- );
+ if (
+ root instanceof Element ||
+ (isShadowDomSupported && root instanceof ShadowRoot) ||
+ root instanceof Document
+ ) {
+ push(results, root.querySelectorAll(INLINE_STYLE_SELECTOR));
}
- function stopWatchingForSheetChangesUsingProxy() {
- element.removeEventListener(
- "__darkreader__updateSheet",
- onSheetChange
+ return results;
+ }
+ const treeObservers = new Map();
+ const attrObservers = new Map();
+ function watchForInlineStyles(elementStyleDidChange, shadowRootDiscovered) {
+ deepWatchForInlineStyles(
+ document,
+ elementStyleDidChange,
+ shadowRootDiscovered
+ );
+ iterateShadowHosts(document.documentElement, (host) => {
+ deepWatchForInlineStyles(
+ host.shadowRoot,
+ elementStyleDidChange,
+ shadowRootDiscovered
);
- }
- function stopWatchingForSheetChanges() {
- stopWatchingForSheetChangesUsingProxy();
- rafSheetWatcher?.stop();
- }
- return {
- start: watchForSheetChanges,
- stop: stopWatchingForSheetChanges
- };
+ });
}
- function createRAFSheetWatcher(
- element,
- safeGetSheetRules,
- callback,
- isCancelled
+ function deepWatchForInlineStyles(
+ root,
+ elementStyleDidChange,
+ shadowRootDiscovered
) {
- let rulesChangeKey = null;
- let rulesCheckFrameId = null;
- function getRulesChangeKey() {
- const rules = safeGetSheetRules();
- return rules ? rules.length : null;
- }
- function didRulesKeyChange() {
- return getRulesChangeKey() !== rulesChangeKey;
+ if (treeObservers.has(root)) {
+ treeObservers.get(root).disconnect();
+ attrObservers.get(root).disconnect();
}
- function watchForSheetChangesUsingRAF() {
- rulesChangeKey = getRulesChangeKey();
- stopWatchingForSheetChangesUsingRAF();
- const checkForUpdate = () => {
- const cancelled = isCancelled();
- if (!cancelled && didRulesKeyChange()) {
- rulesChangeKey = getRulesChangeKey();
- callback();
+ const discoveredNodes = new WeakSet();
+ function discoverNodes(node) {
+ getInlineStyleElements(node).forEach((el) => {
+ if (discoveredNodes.has(el)) {
+ return;
}
- if (cancelled || (canUseSheetProxy$1 && element.sheet)) {
- stopWatchingForSheetChangesUsingRAF();
+ discoveredNodes.add(el);
+ elementStyleDidChange(el);
+ });
+ iterateShadowHosts(node, (n) => {
+ if (discoveredNodes.has(node)) {
return;
}
- rulesCheckFrameId = requestAnimationFrame(checkForUpdate);
- };
- checkForUpdate();
- }
- function stopWatchingForSheetChangesUsingRAF() {
- rulesCheckFrameId && cancelAnimationFrame(rulesCheckFrameId);
- }
- return {
- start: watchForSheetChangesUsingRAF,
- stop: stopWatchingForSheetChangesUsingRAF
- };
- }
-
- const STYLE_SELECTOR = 'style, link[rel*="stylesheet" i]:not([disabled])';
- function isFontsGoogleApiStyle(element) {
- if (!element.href) {
- return false;
- }
- try {
- const elementURL = new URL(element.href);
- return elementURL.hostname === "fonts.googleapis.com";
- } catch (err) {
- logInfo(`Couldn't construct ${element.href} as URL`);
- return false;
- }
- }
- const hostsBreakingOnSVGStyleOverride = ["www.onet.pl"];
- function shouldManageStyle(element) {
- return (
- (element instanceof HTMLStyleElement ||
- (element instanceof SVGStyleElement &&
- !hostsBreakingOnSVGStyleOverride.includes(
- location.hostname
- )) ||
- (element instanceof HTMLLinkElement &&
- Boolean(element.rel) &&
- element.rel.toLowerCase().includes("stylesheet") &&
- Boolean(element.href) &&
- !element.disabled &&
- (isFirefox
- ? !element.href.startsWith("moz-extension://")
- : true) &&
- !isFontsGoogleApiStyle(element))) &&
- !element.classList.contains("darkreader") &&
- element.media.toLowerCase() !== "print" &&
- !element.classList.contains("stylus")
- );
- }
- function getManageableStyles(node, results = [], deep = true) {
- if (shouldManageStyle(node)) {
- results.push(node);
- } else if (
- node instanceof Element ||
- (isShadowDomSupported && node instanceof ShadowRoot) ||
- node === document
- ) {
- forEach(node.querySelectorAll(STYLE_SELECTOR), (style) =>
- getManageableStyles(style, results, false)
- );
- if (deep) {
- iterateShadowHosts(node, (host) =>
- getManageableStyles(host.shadowRoot, results, false)
+ discoveredNodes.add(node);
+ shadowRootDiscovered(n.shadowRoot);
+ deepWatchForInlineStyles(
+ n.shadowRoot,
+ elementStyleDidChange,
+ shadowRootDiscovered
);
- }
- }
- return results;
- }
- const syncStyleSet = new WeakSet();
- const corsStyleSet = new WeakSet();
- let loadingLinkCounter = 0;
- const rejectorsForLoadingLinks = new Map();
- function cleanLoadingLinks() {
- rejectorsForLoadingLinks.clear();
- }
- function manageStyle(element, { update, loadingStart, loadingEnd }) {
- const prevStyles = [];
- let next = element;
- while (
- (next = next.nextElementSibling) &&
- next.matches(".darkreader")
- ) {
- prevStyles.push(next);
+ });
+ variablesStore.matchVariablesAndDependents();
}
- let corsCopy =
- prevStyles.find(
- (el) => el.matches(".darkreader--cors") && !corsStyleSet.has(el)
- ) || null;
- let syncStyle =
- prevStyles.find(
- (el) => el.matches(".darkreader--sync") && !syncStyleSet.has(el)
- ) || null;
- let corsCopyPositionWatcher = null;
- let syncStylePositionWatcher = null;
- let cancelAsyncOperations = false;
- let isOverrideEmpty = true;
- const isAsyncCancelled = () => cancelAsyncOperations;
- const sheetModifier = createStyleSheetModifier();
- const observer = new MutationObserver((mutations) => {
- if (
- mutations.some((m) => m.type === "characterData") &&
- containsCSSImport()
- ) {
- const cssText = (element.textContent ?? "").trim();
- createOrUpdateCORSCopy(cssText, location.href).then(update);
- } else {
- update();
+ const treeObserver = createOptimizedTreeObserver(root, {
+ onMinorMutations: (_root, {additions}) => {
+ additions.forEach((added) => discoverNodes(added));
+ },
+ onHugeMutations: () => {
+ discoverNodes(root);
}
});
- const observerOptions = {
- attributes: true,
- childList: true,
- subtree: true,
- characterData: true
- };
- function containsCSSImport() {
- if (!(element instanceof HTMLStyleElement)) {
- return false;
- }
- const cssText = removeCSSComments(element.textContent ?? "").trim();
- return cssText.match(cssImportRegex);
- }
- function hasImports(cssRules, checkCrossOrigin) {
- let result = false;
- if (cssRules) {
- let rule;
- cssRulesLoop: for (
- let i = 0, len = cssRules.length;
- i < len;
- i++
- ) {
- rule = cssRules[i];
- if (rule.href) {
- if (checkCrossOrigin) {
- if (
- !rule.href.startsWith(
- "https://fonts.googleapis.com/"
- ) &&
- rule.href.startsWith("http") &&
- !rule.href.startsWith(location.origin)
- ) {
- result = true;
- break cssRulesLoop;
- }
- } else {
- result = true;
- break cssRulesLoop;
- }
- }
+ treeObservers.set(root, treeObserver);
+ let attemptCount = 0;
+ let start = null;
+ const ATTEMPTS_INTERVAL = getDuration({seconds: 10});
+ const RETRY_TIMEOUT = getDuration({seconds: 2});
+ const MAX_ATTEMPTS_COUNT = 50;
+ let cache = [];
+ let timeoutId = null;
+ const handleAttributeMutations = throttle((mutations) => {
+ const handledTargets = new Set();
+ mutations.forEach((m) => {
+ const target = m.target;
+ if (handledTargets.has(target)) {
+ return;
}
+ if (INLINE_STYLE_ATTRS.includes(m.attributeName)) {
+ handledTargets.add(target);
+ elementStyleDidChange(target);
+ }
+ });
+ variablesStore.matchVariablesAndDependents();
+ });
+ const attrObserver = new MutationObserver((mutations) => {
+ if (timeoutId) {
+ cache.push(...mutations);
+ return;
}
- return result;
- }
- function getRulesSync() {
- if (corsCopy) {
- return corsCopy.sheet.cssRules;
- }
- if (containsCSSImport()) {
- return null;
- }
- const cssRules = safeGetSheetRules();
- if (
- element instanceof HTMLLinkElement &&
- !isRelativeHrefOnAbsolutePath(element.href) &&
- hasImports(cssRules, false)
- ) {
- return null;
+ attemptCount++;
+ const now = Date.now();
+ if (start == null) {
+ start = now;
+ } else if (attemptCount >= MAX_ATTEMPTS_COUNT) {
+ if (now - start < ATTEMPTS_INTERVAL) {
+ timeoutId = setTimeout(() => {
+ start = null;
+ attemptCount = 0;
+ timeoutId = null;
+ const attributeCache = cache;
+ cache = [];
+ handleAttributeMutations(attributeCache);
+ }, RETRY_TIMEOUT);
+ cache.push(...mutations);
+ return;
+ }
+ start = now;
+ attemptCount = 1;
}
- if (hasImports(cssRules, true)) {
- return null;
+ handleAttributeMutations(mutations);
+ });
+ attrObserver.observe(root, {
+ attributes: true,
+ attributeFilter: INLINE_STYLE_ATTRS.concat(
+ overridesList.map(({dataAttr}) => dataAttr)
+ ),
+ subtree: true
+ });
+ attrObservers.set(root, attrObserver);
+ }
+ function stopWatchingForInlineStyles() {
+ treeObservers.forEach((o) => o.disconnect());
+ attrObservers.forEach((o) => o.disconnect());
+ treeObservers.clear();
+ attrObservers.clear();
+ }
+ const inlineStyleCache = new WeakMap();
+ const svgInversionCache = new WeakSet();
+ const svgAnalysisConditionCache = new WeakMap();
+ const themeProps = ["brightness", "contrast", "grayscale", "sepia", "mode"];
+ function shouldAnalyzeSVGAsImage(svg) {
+ if (svgAnalysisConditionCache.has(svg)) {
+ return svgAnalysisConditionCache.get(svg);
+ }
+ const shouldAnalyze = Boolean(
+ svg &&
+ (svg.getAttribute("class")?.includes("logo") ||
+ svg.parentElement?.getAttribute("class")?.includes("logo"))
+ );
+ svgAnalysisConditionCache.set(svg, shouldAnalyze);
+ return shouldAnalyze;
+ }
+ function getInlineStyleCacheKey(el, theme) {
+ return INLINE_STYLE_ATTRS.map(
+ (attr) => `${attr}="${el.getAttribute(attr)}"`
+ )
+ .concat(themeProps.map((prop) => `${prop}="${theme[prop]}"`))
+ .join(" ");
+ }
+ function shouldIgnoreInlineStyle(element, selectors) {
+ for (let i = 0, len = selectors.length; i < len; i++) {
+ const ingnoredSelector = selectors[i];
+ if (element.matches(ingnoredSelector)) {
+ return true;
}
- return cssRules;
}
- function insertStyle() {
- if (corsCopy) {
- if (element.nextSibling !== corsCopy) {
- element.parentNode.insertBefore(
- corsCopy,
- element.nextSibling
- );
- }
- if (corsCopy.nextSibling !== syncStyle) {
- element.parentNode.insertBefore(
- syncStyle,
- corsCopy.nextSibling
- );
+ return false;
+ }
+ function overrideInlineStyle(
+ element,
+ theme,
+ ignoreInlineSelectors,
+ ignoreImageSelectors
+ ) {
+ const cacheKey = getInlineStyleCacheKey(element, theme);
+ if (cacheKey === inlineStyleCache.get(element)) {
+ return;
+ }
+ const unsetProps = new Set(Object.keys(overrides));
+ function setCustomProp(targetCSSProp, modifierCSSProp, cssVal) {
+ const mod = getModifiableCSSDeclaration(
+ modifierCSSProp,
+ cssVal,
+ {style: element.style},
+ variablesStore,
+ ignoreImageSelectors,
+ null
+ );
+ if (!mod) {
+ return;
+ }
+ function setStaticValue(value) {
+ const {customProp, dataAttr} =
+ overrides[targetCSSProp] ??
+ shorthandOverrides[targetCSSProp];
+ element.style.setProperty(customProp, value);
+ if (!element.hasAttribute(dataAttr)) {
+ element.setAttribute(dataAttr, "");
}
- } else if (element.nextSibling !== syncStyle) {
- element.parentNode.insertBefore(syncStyle, element.nextSibling);
+ unsetProps.delete(targetCSSProp);
}
- }
- function createSyncStyle() {
- syncStyle =
- element instanceof SVGStyleElement
- ? document.createElementNS(
- "http://www.w3.org/2000/svg",
- "style"
- )
- : document.createElement("style");
- syncStyle.classList.add("darkreader");
- syncStyle.classList.add("darkreader--sync");
- syncStyle.media = "screen";
- if (element.title) {
- syncStyle.title = element.title;
+ function setVarDeclaration(mod) {
+ let prevDeclarations = [];
+ function setProps(declarations) {
+ prevDeclarations.forEach(({property}) => {
+ element.style.removeProperty(property);
+ });
+ declarations.forEach(({property, value}) => {
+ if (!(value instanceof Promise)) {
+ element.style.setProperty(property, value);
+ }
+ });
+ prevDeclarations = declarations;
+ }
+ setProps(mod.declarations);
+ mod.onTypeChange.addListener(setProps);
}
- syncStyleSet.add(syncStyle);
- }
- let isLoadingRules = false;
- let wasLoadingError = false;
- const loadingLinkId = ++loadingLinkCounter;
- async function getRulesAsync() {
- let cssText;
- let cssBasePath;
- if (element instanceof HTMLLinkElement) {
- let [cssRules, accessError] = getRulesOrError();
- if (
- (isSafari && !element.sheet) ||
- (!isSafari && !cssRules && !accessError) ||
- isStillLoadingError(accessError)
- ) {
- try {
- logInfo(
- `Linkelement ${loadingLinkId} is not loaded yet and thus will be await for`,
- element
- );
- await linkLoading(element, loadingLinkId);
- } catch (err) {
- wasLoadingError = true;
- }
- if (cancelAsyncOperations) {
- return null;
+ function setAsyncValue(promise, sourceValue) {
+ promise.then((value) => {
+ if (
+ value &&
+ targetCSSProp === "background" &&
+ value.startsWith("var(--darkreader-bg--")
+ ) {
+ setStaticValue(value);
}
- [cssRules, accessError] = getRulesOrError();
- }
- if (cssRules) {
- if (!hasImports(cssRules, false)) {
- return cssRules;
+ if (value && targetCSSProp === "background-image") {
+ if (
+ (element === document.documentElement ||
+ element === document.body) &&
+ value === sourceValue
+ ) {
+ value = "none";
+ }
+ setStaticValue(value);
}
- }
- cssText = await loadText(element.href);
- cssBasePath = getCSSBaseBath(element.href);
- if (cancelAsyncOperations) {
- return null;
- }
- } else if (containsCSSImport()) {
- cssText = element.textContent.trim();
- cssBasePath = getCSSBaseBath(location.href);
- } else {
- return null;
+ inlineStyleCache.set(
+ element,
+ getInlineStyleCacheKey(element, theme)
+ );
+ });
}
- await createOrUpdateCORSCopy(cssText, cssBasePath);
- if (corsCopy) {
- return corsCopy.sheet.cssRules;
+ const value =
+ typeof mod.value === "function" ? mod.value(theme) : mod.value;
+ if (typeof value === "string") {
+ setStaticValue(value);
+ } else if (value instanceof Promise) {
+ setAsyncValue(value, cssVal);
+ } else if (typeof value === "object") {
+ setVarDeclaration(value);
}
- return null;
}
- async function createOrUpdateCORSCopy(cssText, cssBasePath) {
- if (cssText) {
- try {
- const fullCSSText = await replaceCSSImports(
- cssText,
- cssBasePath
+ if (ignoreInlineSelectors.length > 0) {
+ if (shouldIgnoreInlineStyle(element, ignoreInlineSelectors)) {
+ unsetProps.forEach((cssProp) => {
+ element.removeAttribute(overrides[cssProp].dataAttr);
+ });
+ return;
+ }
+ }
+ const isSVGElement = element instanceof SVGElement;
+ const svg = isSVGElement
+ ? (element.ownerSVGElement ??
+ (element instanceof SVGSVGElement ? element : null))
+ : null;
+ if (isSVGElement && theme.mode === 1 && svg) {
+ if (svgInversionCache.has(svg)) {
+ return;
+ }
+ if (shouldAnalyzeSVGAsImage(svg)) {
+ svgInversionCache.add(svg);
+ const analyzeSVGAsImage = () => {
+ let svgString = svg.outerHTML;
+ svgString = svgString.replaceAll(
+ '',
+ ""
);
- if (corsCopy) {
+ const dataURL = `data:image/svg+xml;base64,${btoa(svgString)}`;
+ getImageDetails(dataURL).then((details) => {
if (
- (corsCopy.textContent?.length ?? 0) <
- fullCSSText.length
+ (details.isDark && details.isTransparent) ||
+ (details.isLarge &&
+ details.isLight &&
+ !details.isTransparent)
) {
- corsCopy.textContent = fullCSSText;
+ svg.setAttribute(
+ "data-darkreader-inline-invert",
+ ""
+ );
+ } else {
+ svg.removeAttribute(
+ "data-darkreader-inline-invert"
+ );
}
- } else {
- corsCopy = createCORSCopy(element, fullCSSText);
- }
- } catch (err) { }
- if (corsCopy) {
- corsCopyPositionWatcher = watchForNodePosition(
- corsCopy,
- "prev-sibling"
- );
+ });
+ };
+ analyzeSVGAsImage();
+ if (!isDOMReady()) {
+ addDOMReadyListener(analyzeSVGAsImage);
}
+ return;
}
}
- function details(options) {
- const rules = getRulesSync();
- if (!rules) {
- if (options.secondRound) {
- return null;
- }
- if (isLoadingRules || wasLoadingError) {
- return null;
- }
- isLoadingRules = true;
- loadingStart();
- getRulesAsync()
- .then((results) => {
- isLoadingRules = false;
- loadingEnd();
- if (results) {
- update();
- }
- })
- .catch((err) => {
- isLoadingRules = false;
- loadingEnd();
- });
- return null;
+ if (element.hasAttribute("bgcolor")) {
+ let value = element.getAttribute("bgcolor");
+ if (
+ value.match(/^[0-9a-f]{3}$/i) ||
+ value.match(/^[0-9a-f]{6}$/i)
+ ) {
+ value = `#${value}`;
}
- return { rules };
+ setCustomProp("background-color", "background-color", value);
}
- let forceRenderStyle = false;
- function render(theme, ignoreImageAnalysis) {
- const rules = getRulesSync();
- if (!rules) {
- return;
+ if (
+ (element === document.documentElement ||
+ element === document.body) &&
+ element.hasAttribute("background")
+ ) {
+ const url = getAbsoluteURL(
+ location.href,
+ element.getAttribute("background") ?? ""
+ );
+ const value = `url("${url}")`;
+ setCustomProp("background-image", "background-image", value);
+ }
+ if (element.hasAttribute("color") && element.rel !== "mask-icon") {
+ let value = element.getAttribute("color");
+ if (
+ value.match(/^[0-9a-f]{3}$/i) ||
+ value.match(/^[0-9a-f]{6}$/i)
+ ) {
+ value = `#${value}`;
}
- cancelAsyncOperations = false;
- function removeCSSRulesFromSheet(sheet) {
- if (!sheet) {
- return;
- }
- for (let i = sheet.cssRules.length - 1; i >= 0; i--) {
- sheet.deleteRule(i);
+ setCustomProp("color", "color", value);
+ }
+ if (isSVGElement) {
+ if (element.hasAttribute("fill")) {
+ const SMALL_SVG_LIMIT = 32;
+ const value = element.getAttribute("fill");
+ if (value !== "none") {
+ if (!(element instanceof SVGTextElement)) {
+ const handleSVGElement = () => {
+ const {width, height} =
+ element.getBoundingClientRect();
+ const isBg =
+ width > SMALL_SVG_LIMIT ||
+ height > SMALL_SVG_LIMIT;
+ setCustomProp(
+ "fill",
+ isBg ? "background-color" : "color",
+ value
+ );
+ };
+ if (isReadyStateComplete()) {
+ handleSVGElement();
+ } else {
+ addReadyStateCompleteListener(handleSVGElement);
+ }
+ } else {
+ setCustomProp("fill", "color", value);
+ }
}
}
- function prepareOverridesSheet() {
- if (!syncStyle) {
- createSyncStyle();
- }
- syncStylePositionWatcher && syncStylePositionWatcher.stop();
- insertStyle();
- if (syncStyle.sheet == null) {
- syncStyle.textContent = "";
+ if (element.hasAttribute("stop-color")) {
+ setCustomProp(
+ "stop-color",
+ "background-color",
+ element.getAttribute("stop-color")
+ );
+ }
+ }
+ if (element.hasAttribute("stroke")) {
+ const value = element.getAttribute("stroke");
+ setCustomProp(
+ "stroke",
+ element instanceof SVGLineElement ||
+ element instanceof SVGTextElement
+ ? "border-color"
+ : "color",
+ value
+ );
+ }
+ element.style &&
+ iterateCSSDeclarations(element.style, (property, value) => {
+ if (property === "background-image" && value.includes("url")) {
+ if (
+ element === document.documentElement ||
+ element === document.body
+ ) {
+ setCustomProp(property, property, value);
+ }
+ return;
}
- const sheet = syncStyle.sheet;
- removeCSSRulesFromSheet(sheet);
- if (syncStylePositionWatcher) {
- syncStylePositionWatcher.run();
+ if (
+ overrides.hasOwnProperty(property) ||
+ (property.startsWith("--") && !normalizedPropList[property])
+ ) {
+ setCustomProp(property, property, value);
+ } else if (
+ property === "background" &&
+ value.includes("var(")
+ ) {
+ setCustomProp("background", "background", value);
} else {
- syncStylePositionWatcher = watchForNodePosition(
- syncStyle,
- "prev-sibling",
- () => {
- forceRenderStyle = true;
- buildOverrides();
+ const overriddenProp = normalizedPropList[property];
+ if (
+ overriddenProp &&
+ !element.style.getPropertyValue(overriddenProp) &&
+ !element.hasAttribute(overriddenProp)
+ ) {
+ if (
+ overriddenProp === "background-color" &&
+ element.hasAttribute("bgcolor")
+ ) {
+ return;
}
- );
- }
- return syncStyle.sheet;
- }
- function buildOverrides() {
- const force = forceRenderStyle;
- forceRenderStyle = false;
- sheetModifier.modifySheet({
- prepareSheet: prepareOverridesSheet,
- sourceCSSRules: rules,
- theme,
- ignoreImageAnalysis,
- force,
- isAsyncCancelled
- });
- isOverrideEmpty = syncStyle.sheet.cssRules.length === 0;
- if (sheetModifier.shouldRebuildStyle()) {
- addReadyStateCompleteListener(() => update());
+ element.style.setProperty(property, "");
+ }
}
- }
- buildOverrides();
+ });
+ if (
+ element.style &&
+ element instanceof SVGTextElement &&
+ element.style.fill
+ ) {
+ setCustomProp(
+ "fill",
+ "color",
+ element.style.getPropertyValue("fill")
+ );
}
- function getRulesOrError() {
- try {
- if (element.sheet == null) {
- return [null, null];
- }
- return [element.sheet.cssRules, null];
- } catch (err) {
- return [null, err];
- }
+ if (element.getAttribute("style")?.includes("--")) {
+ variablesStore.addInlineStyleForMatching(element.style);
}
- function isStillLoadingError(error) {
- return error && error.message && error.message.includes("loading");
+ forEach(unsetProps, (cssProp) => {
+ element.removeAttribute(overrides[cssProp].dataAttr);
+ });
+ inlineStyleCache.set(element, getInlineStyleCacheKey(element, theme));
+ }
+
+ const metaThemeColorName = "theme-color";
+ const metaThemeColorSelector = `meta[name="${metaThemeColorName}"]`;
+ let srcMetaThemeColor = null;
+ let observer = null;
+ function changeMetaThemeColor(meta, theme) {
+ srcMetaThemeColor = srcMetaThemeColor || meta.content;
+ const color = parseColorWithCache(srcMetaThemeColor);
+ if (!color) {
+ return;
}
- function safeGetSheetRules() {
- const [cssRules, err] = getRulesOrError();
- if (err) {
- return null;
+ meta.content = modifyBackgroundColor(color, theme, false);
+ }
+ function changeMetaThemeColorWhenAvailable(theme) {
+ const meta = document.querySelector(metaThemeColorSelector);
+ if (meta) {
+ changeMetaThemeColor(meta, theme);
+ } else {
+ if (observer) {
+ observer.disconnect();
}
- return cssRules;
+ observer = new MutationObserver((mutations) => {
+ loop: for (let i = 0; i < mutations.length; i++) {
+ const {addedNodes} = mutations[i];
+ for (let j = 0; j < addedNodes.length; j++) {
+ const node = addedNodes[j];
+ if (
+ node instanceof HTMLMetaElement &&
+ node.name === metaThemeColorName
+ ) {
+ observer.disconnect();
+ observer = null;
+ changeMetaThemeColor(node, theme);
+ break loop;
+ }
+ }
+ }
+ });
+ observer.observe(document.head, {childList: true});
}
- const sheetChangeWatcher = createSheetWatcher(
- element,
- safeGetSheetRules,
- update,
- isAsyncCancelled
- );
- function pause() {
+ }
+ function restoreMetaThemeColor() {
+ if (observer) {
observer.disconnect();
- cancelAsyncOperations = true;
- corsCopyPositionWatcher && corsCopyPositionWatcher.stop();
- syncStylePositionWatcher && syncStylePositionWatcher.stop();
- sheetChangeWatcher.stop();
+ observer = null;
}
- function destroy() {
- pause();
- removeNode(corsCopy);
- removeNode(syncStyle);
- loadingEnd();
- if (rejectorsForLoadingLinks.has(loadingLinkId)) {
- const reject = rejectorsForLoadingLinks.get(loadingLinkId);
- rejectorsForLoadingLinks.delete(loadingLinkId);
- reject && reject();
- }
+ const meta = document.querySelector(metaThemeColorSelector);
+ if (meta && srcMetaThemeColor) {
+ meta.content = srcMetaThemeColor;
}
- function watch() {
- observer.observe(element, observerOptions);
- if (element instanceof HTMLStyleElement) {
- sheetChangeWatcher.start();
+ }
+
+ const cssCommentsRegex = /\/\*[\s\S]*?\*\//g;
+ function removeCSSComments(cssText) {
+ return cssText.replace(cssCommentsRegex, "");
+ }
+
+ let canUseSheetProxy = false;
+ document.addEventListener(
+ "__darkreader__inlineScriptsAllowed",
+ () => (canUseSheetProxy = true),
+ {once: true}
+ );
+ function createSheetWatcher(
+ element,
+ safeGetSheetRules,
+ callback,
+ isCancelled
+ ) {
+ let rafSheetWatcher = null;
+ function watchForSheetChanges() {
+ watchForSheetChangesUsingProxy();
+ if (!(canUseSheetProxy && element.sheet)) {
+ rafSheetWatcher = createRAFSheetWatcher(
+ element,
+ safeGetSheetRules,
+ callback,
+ isCancelled
+ );
+ rafSheetWatcher.start();
}
}
- const maxMoveCount = 10;
- let moveCount = 0;
- function restore() {
- if (!syncStyle) {
- return;
- }
- moveCount++;
- if (moveCount > maxMoveCount) {
+ let areSheetChangesPending = false;
+ function onSheetChange() {
+ canUseSheetProxy = true;
+ rafSheetWatcher?.stop();
+ if (areSheetChangesPending) {
return;
}
- insertStyle();
- corsCopyPositionWatcher && corsCopyPositionWatcher.skip();
- syncStylePositionWatcher && syncStylePositionWatcher.skip();
- if (!isOverrideEmpty) {
- forceRenderStyle = true;
- update();
+ function handleSheetChanges() {
+ areSheetChangesPending = false;
+ if (isCancelled()) {
+ return;
+ }
+ callback();
}
+ areSheetChangesPending = true;
+ queueMicrotask(handleSheetChanges);
+ }
+ function watchForSheetChangesUsingProxy() {
+ element.addEventListener(
+ "__darkreader__updateSheet",
+ onSheetChange
+ );
+ }
+ function stopWatchingForSheetChangesUsingProxy() {
+ element.removeEventListener(
+ "__darkreader__updateSheet",
+ onSheetChange
+ );
+ }
+ function stopWatchingForSheetChanges() {
+ stopWatchingForSheetChangesUsingProxy();
+ rafSheetWatcher?.stop();
}
return {
- details,
- render,
- pause,
- destroy,
- watch,
- restore
+ start: watchForSheetChanges,
+ stop: stopWatchingForSheetChanges
};
}
- async function linkLoading(link, loadingId) {
- return new Promise((resolve, reject) => {
- const cleanUp = () => {
- link.removeEventListener("load", onLoad);
- link.removeEventListener("error", onError);
- rejectorsForLoadingLinks.delete(loadingId);
- };
- const onLoad = () => {
- cleanUp();
- resolve();
- };
- const onError = () => {
- cleanUp();
- reject(
- `Linkelement ${loadingId} couldn't be loaded. ${link.href}`
- );
+ function createRAFSheetWatcher(
+ element,
+ safeGetSheetRules,
+ callback,
+ isCancelled
+ ) {
+ let rulesChangeKey = null;
+ let rulesCheckFrameId = null;
+ function getRulesChangeKey() {
+ const rules = safeGetSheetRules();
+ return rules ? rules.length : null;
+ }
+ function didRulesKeyChange() {
+ return getRulesChangeKey() !== rulesChangeKey;
+ }
+ function watchForSheetChangesUsingRAF() {
+ rulesChangeKey = getRulesChangeKey();
+ stopWatchingForSheetChangesUsingRAF();
+ const checkForUpdate = () => {
+ const cancelled = isCancelled();
+ if (!cancelled && didRulesKeyChange()) {
+ rulesChangeKey = getRulesChangeKey();
+ callback();
+ }
+ if (cancelled || (canUseSheetProxy && element.sheet)) {
+ stopWatchingForSheetChangesUsingRAF();
+ return;
+ }
+ rulesCheckFrameId = requestAnimationFrame(checkForUpdate);
};
- rejectorsForLoadingLinks.set(loadingId, () => {
- cleanUp();
- reject();
- });
- link.addEventListener("load", onLoad, { passive: true });
- link.addEventListener("error", onError, { passive: true });
- if (!link.href) {
- onError();
- }
- });
- }
- function getCSSImportURL(importDeclaration) {
- return getCSSURLValue(
- importDeclaration
- .substring(7)
- .trim()
- .replace(/;$/, "")
- .replace(/screen$/, "")
- );
+ checkForUpdate();
+ }
+ function stopWatchingForSheetChangesUsingRAF() {
+ rulesCheckFrameId && cancelAnimationFrame(rulesCheckFrameId);
+ }
+ return {
+ start: watchForSheetChangesUsingRAF,
+ stop: stopWatchingForSheetChangesUsingRAF
+ };
}
- async function loadText(url) {
- if (url.startsWith("data:")) {
- return await (await fetch(url)).text();
+
+ const STYLE_SELECTOR = 'style, link[rel*="stylesheet" i]:not([disabled])';
+ function isFontsGoogleApiStyle(element) {
+ if (!element.href) {
+ return false;
}
- const parsedURL = new URL(url);
- if (parsedURL.origin === location.origin) {
- return await loadAsText(url, "text/css", location.origin);
+ try {
+ const elementURL = new URL(element.href);
+ return elementURL.hostname === "fonts.googleapis.com";
+ } catch (err) {
+ logInfo(`Couldn't construct ${element.href} as URL`);
+ return false;
}
- return await bgFetch({
- url,
- responseType: "text",
- mimeType: "text/css",
- origin: location.origin
- });
}
- async function replaceCSSImports(cssText, basePath, cache = new Map()) {
- cssText = removeCSSComments(cssText);
- cssText = replaceCSSFontFace(cssText);
- cssText = replaceCSSRelativeURLsWithAbsolute(cssText, basePath);
- const importMatches = getMatches(cssImportRegex, cssText);
- for (const match of importMatches) {
- const importURL = getCSSImportURL(match);
- const absoluteURL = getAbsoluteURL(basePath, importURL);
- let importedCSS;
- if (cache.has(absoluteURL)) {
- importedCSS = cache.get(absoluteURL);
- } else {
- try {
- importedCSS = await loadText(absoluteURL);
- cache.set(absoluteURL, importedCSS);
- importedCSS = await replaceCSSImports(
- importedCSS,
- getCSSBaseBath(absoluteURL),
- cache
- );
- } catch (err) {
- importedCSS = "";
- }
+ const hostsBreakingOnSVGStyleOverride = [
+ "account.containerstore.com",
+ "containerstore.com",
+ "www.onet.pl"
+ ];
+ function shouldManageStyle(element) {
+ return (
+ (element instanceof HTMLStyleElement ||
+ (element instanceof SVGStyleElement &&
+ !hostsBreakingOnSVGStyleOverride.includes(
+ location.hostname
+ )) ||
+ (element instanceof HTMLLinkElement &&
+ Boolean(element.rel) &&
+ element.rel.toLowerCase().includes("stylesheet") &&
+ Boolean(element.href) &&
+ !element.disabled &&
+ (isFirefox
+ ? !element.href.startsWith("moz-extension://")
+ : true) &&
+ !isFontsGoogleApiStyle(element))) &&
+ !element.classList.contains("darkreader") &&
+ element.media.toLowerCase() !== "print" &&
+ !element.classList.contains("stylus")
+ );
+ }
+ function getManageableStyles(node, results = [], deep = true) {
+ if (shouldManageStyle(node)) {
+ results.push(node);
+ } else if (
+ node instanceof Element ||
+ (isShadowDomSupported && node instanceof ShadowRoot) ||
+ node === document
+ ) {
+ forEach(node.querySelectorAll(STYLE_SELECTOR), (style) =>
+ getManageableStyles(style, results, false)
+ );
+ if (deep) {
+ iterateShadowHosts(node, (host) =>
+ getManageableStyles(host.shadowRoot, results, false)
+ );
}
- cssText = cssText.split(match).join(importedCSS);
}
- cssText = cssText.trim();
- return cssText;
+ return results;
}
- function createCORSCopy(srcElement, cssText) {
- if (!cssText) {
- return null;
- }
- const cors = document.createElement("style");
- cors.classList.add("darkreader");
- cors.classList.add("darkreader--cors");
- cors.media = "screen";
- cors.textContent = cssText;
- srcElement.parentNode.insertBefore(cors, srcElement.nextSibling);
- cors.sheet.disabled = true;
- corsStyleSet.add(cors);
- return cors;
+ const syncStyleSet = new WeakSet();
+ const corsStyleSet = new WeakSet();
+ let loadingLinkCounter = 0;
+ const rejectorsForLoadingLinks = new Map();
+ function cleanLoadingLinks() {
+ rejectorsForLoadingLinks.clear();
}
-
- const definedCustomElements = new Set();
- const undefinedGroups = new Map();
- let elementsDefinitionCallback;
- function isCustomElement(element) {
- if (element.tagName.includes("-") || element.getAttribute("is")) {
- return true;
+ function manageStyle(element, {update, loadingStart, loadingEnd}) {
+ const prevStyles = [];
+ let next = element;
+ while (
+ (next = next.nextElementSibling) &&
+ next.matches(".darkreader")
+ ) {
+ prevStyles.push(next);
}
- return false;
- }
- function recordUndefinedElement(element) {
- let tag = element.tagName.toLowerCase();
- if (!tag.includes("-")) {
- const extendedTag = element.getAttribute("is");
- if (extendedTag) {
- tag = extendedTag;
+ let corsCopy =
+ prevStyles.find(
+ (el) => el.matches(".darkreader--cors") && !corsStyleSet.has(el)
+ ) || null;
+ let syncStyle =
+ prevStyles.find(
+ (el) => el.matches(".darkreader--sync") && !syncStyleSet.has(el)
+ ) || null;
+ let corsCopyPositionWatcher = null;
+ let syncStylePositionWatcher = null;
+ let cancelAsyncOperations = false;
+ let isOverrideEmpty = true;
+ const isAsyncCancelled = () => cancelAsyncOperations;
+ const sheetModifier = createStyleSheetModifier();
+ const observer = new MutationObserver((mutations) => {
+ if (
+ mutations.some((m) => m.type === "characterData") &&
+ containsCSSImport()
+ ) {
+ const cssText = (element.textContent ?? "").trim();
+ createOrUpdateCORSCopy(cssText, location.href).then(update);
} else {
- return;
+ update();
+ }
+ });
+ const observerOptions = {
+ attributes: true,
+ childList: true,
+ subtree: true,
+ characterData: true
+ };
+ function containsCSSImport() {
+ if (!(element instanceof HTMLStyleElement)) {
+ return false;
}
+ const cssText = removeCSSComments(element.textContent ?? "").trim();
+ return cssText.match(cssImportRegex);
}
- if (!undefinedGroups.has(tag)) {
- undefinedGroups.set(tag, new Set());
- customElementsWhenDefined(tag).then(() => {
- if (elementsDefinitionCallback) {
- const elements = undefinedGroups.get(tag);
- undefinedGroups.delete(tag);
- elementsDefinitionCallback(Array.from(elements));
+ function hasImports(cssRules, checkCrossOrigin) {
+ let result = false;
+ if (cssRules) {
+ let rule;
+ cssRulesLoop: for (
+ let i = 0, len = cssRules.length;
+ i < len;
+ i++
+ ) {
+ rule = cssRules[i];
+ if (rule.href) {
+ if (checkCrossOrigin) {
+ if (
+ !rule.href.startsWith(
+ "https://fonts.googleapis.com/"
+ ) &&
+ rule.href.startsWith("http") &&
+ !rule.href.startsWith(location.origin)
+ ) {
+ result = true;
+ break cssRulesLoop;
+ }
+ } else {
+ result = true;
+ break cssRulesLoop;
+ }
+ }
}
- });
+ }
+ return result;
}
- undefinedGroups.get(tag).add(element);
- }
- function collectUndefinedElements(root) {
- if (!isDefinedSelectorSupported) {
- return;
+ function getRulesSync() {
+ if (corsCopy) {
+ return corsCopy.sheet.cssRules;
+ }
+ if (containsCSSImport()) {
+ return null;
+ }
+ const cssRules = safeGetSheetRules();
+ if (
+ element instanceof HTMLLinkElement &&
+ !isRelativeHrefOnAbsolutePath(element.href) &&
+ hasImports(cssRules, false)
+ ) {
+ return null;
+ }
+ if (hasImports(cssRules, true)) {
+ return null;
+ }
+ return cssRules;
}
- forEach(
- root.querySelectorAll(":not(:defined)"),
- recordUndefinedElement
- );
- }
- let canOptimizeUsingProxy = false;
- document.addEventListener(
- "__darkreader__inlineScriptsAllowed",
- () => {
- canOptimizeUsingProxy = true;
- },
- { once: true, passive: true }
- );
- const resolvers = new Map();
- function handleIsDefined(e) {
- canOptimizeUsingProxy = true;
- const tag = e.detail.tag;
- definedCustomElements.add(tag);
- if (resolvers.has(tag)) {
- const r = resolvers.get(tag);
- resolvers.delete(tag);
- r.forEach((r) => r());
+ function insertStyle() {
+ if (corsCopy) {
+ if (element.nextSibling !== corsCopy) {
+ element.parentNode.insertBefore(
+ corsCopy,
+ element.nextSibling
+ );
+ }
+ if (corsCopy.nextSibling !== syncStyle) {
+ element.parentNode.insertBefore(
+ syncStyle,
+ corsCopy.nextSibling
+ );
+ }
+ } else if (element.nextSibling !== syncStyle) {
+ element.parentNode.insertBefore(syncStyle, element.nextSibling);
+ }
}
- }
- async function customElementsWhenDefined(tag) {
- if (definedCustomElements.has(tag)) {
- return;
+ function createSyncStyle() {
+ syncStyle =
+ element instanceof SVGStyleElement
+ ? document.createElementNS(
+ "http://www.w3.org/2000/svg",
+ "style"
+ )
+ : document.createElement("style");
+ syncStyle.classList.add("darkreader");
+ syncStyle.classList.add("darkreader--sync");
+ syncStyle.media = "screen";
+ if (element.title) {
+ syncStyle.title = element.title;
+ }
+ syncStyleSet.add(syncStyle);
}
- return new Promise((resolve) => {
- if (
- window.customElements &&
- typeof customElements.whenDefined === "function"
- ) {
- customElements.whenDefined(tag).then(() => resolve());
- } else if (canOptimizeUsingProxy) {
- if (resolvers.has(tag)) {
- resolvers.get(tag).push(resolve);
- } else {
- resolvers.set(tag, [resolve]);
+ let isLoadingRules = false;
+ let wasLoadingError = false;
+ const loadingLinkId = ++loadingLinkCounter;
+ async function getRulesAsync() {
+ let cssText;
+ let cssBasePath;
+ if (element instanceof HTMLLinkElement) {
+ let [cssRules, accessError] = getRulesOrError();
+ if (
+ (isSafari && !element.sheet) ||
+ (!isSafari && !cssRules && !accessError) ||
+ isStillLoadingError(accessError)
+ ) {
+ try {
+ logInfo(
+ `Linkelement ${loadingLinkId} is not loaded yet and thus will be await for`,
+ element
+ );
+ await linkLoading(element, loadingLinkId);
+ } catch (err) {
+ wasLoadingError = true;
+ }
+ if (cancelAsyncOperations) {
+ return null;
+ }
+ [cssRules, accessError] = getRulesOrError();
}
- document.dispatchEvent(
- new CustomEvent("__darkreader__addUndefinedResolver", {
- detail: { tag }
- })
- );
+ if (cssRules) {
+ if (!hasImports(cssRules, false)) {
+ return cssRules;
+ }
+ }
+ cssText = await loadText(element.href);
+ cssBasePath = getCSSBaseBath(element.href);
+ if (cancelAsyncOperations) {
+ return null;
+ }
+ } else if (containsCSSImport()) {
+ cssText = element.textContent.trim();
+ cssBasePath = getCSSBaseBath(location.href);
} else {
- const checkIfDefined = () => {
- const elements = undefinedGroups.get(tag);
- if (elements && elements.size > 0) {
+ return null;
+ }
+ await createOrUpdateCORSCopy(cssText, cssBasePath);
+ if (corsCopy) {
+ return corsCopy.sheet.cssRules;
+ }
+ return null;
+ }
+ async function createOrUpdateCORSCopy(cssText, cssBasePath) {
+ if (cssText) {
+ try {
+ const fullCSSText = await replaceCSSImports(
+ cssText,
+ cssBasePath
+ );
+ if (corsCopy) {
if (
- elements.values().next().value.matches(":defined")
+ (corsCopy.textContent?.length ?? 0) <
+ fullCSSText.length
) {
- resolve();
- } else {
- requestAnimationFrame(checkIfDefined);
+ corsCopy.textContent = fullCSSText;
}
+ } else {
+ corsCopy = createCORSCopy(element, fullCSSText);
}
- };
- requestAnimationFrame(checkIfDefined);
- }
- });
- }
- function watchWhenCustomElementsDefined(callback) {
- elementsDefinitionCallback = callback;
- }
- function unsubscribeFromDefineCustomElements() {
- elementsDefinitionCallback = null;
- undefinedGroups.clear();
- document.removeEventListener(
- "__darkreader__isDefined",
- handleIsDefined
- );
- }
-
- const observers = [];
- let observedRoots;
- function watchForStylePositions(
- currentStyles,
- update,
- shadowRootDiscovered
- ) {
- stopWatchingForStylePositions();
- const prevStylesByRoot = new WeakMap();
- const getPrevStyles = (root) => {
- if (!prevStylesByRoot.has(root)) {
- prevStylesByRoot.set(root, new Set());
- }
- return prevStylesByRoot.get(root);
- };
- currentStyles.forEach((node) => {
- let root = node;
- while ((root = root.parentNode)) {
- if (
- root === document ||
- root.nodeType === Node.DOCUMENT_FRAGMENT_NODE
- ) {
- const prevStyles = getPrevStyles(root);
- prevStyles.add(node);
- break;
+ } catch (err) {}
+ if (corsCopy) {
+ corsCopyPositionWatcher = watchForNodePosition(
+ corsCopy,
+ "prev-sibling"
+ );
}
}
- });
- const prevStyleSiblings = new WeakMap();
- const nextStyleSiblings = new WeakMap();
- function saveStylePosition(style) {
- prevStyleSiblings.set(style, style.previousElementSibling);
- nextStyleSiblings.set(style, style.nextElementSibling);
- }
- function forgetStylePosition(style) {
- prevStyleSiblings.delete(style);
- nextStyleSiblings.delete(style);
- }
- function didStylePositionChange(style) {
- return (
- style.previousElementSibling !== prevStyleSiblings.get(style) ||
- style.nextElementSibling !== nextStyleSiblings.get(style)
- );
}
- currentStyles.forEach(saveStylePosition);
- function handleStyleOperations(root, operations) {
- const { createdStyles, removedStyles, movedStyles } = operations;
- createdStyles.forEach((s) => saveStylePosition(s));
- movedStyles.forEach((s) => saveStylePosition(s));
- removedStyles.forEach((s) => forgetStylePosition(s));
- const prevStyles = getPrevStyles(root);
- createdStyles.forEach((s) => prevStyles.add(s));
- removedStyles.forEach((s) => prevStyles.delete(s));
- if (
- createdStyles.size + removedStyles.size + movedStyles.size >
- 0
- ) {
- update({
- created: Array.from(createdStyles),
- removed: Array.from(removedStyles),
- moved: Array.from(movedStyles),
- updated: []
- });
+ function details(options) {
+ const rules = getRulesSync();
+ if (!rules) {
+ if (options.secondRound) {
+ return null;
+ }
+ if (isLoadingRules || wasLoadingError) {
+ return null;
+ }
+ isLoadingRules = true;
+ loadingStart();
+ getRulesAsync()
+ .then((results) => {
+ isLoadingRules = false;
+ loadingEnd();
+ if (results) {
+ update();
+ }
+ })
+ .catch((err) => {
+ isLoadingRules = false;
+ loadingEnd();
+ });
+ return null;
}
+ return {rules};
}
- function handleMinorTreeMutations(root, { additions, moves, deletions }) {
- const createdStyles = new Set();
- const removedStyles = new Set();
- const movedStyles = new Set();
- additions.forEach((node) =>
- getManageableStyles(node).forEach((style) =>
- createdStyles.add(style)
- )
- );
- deletions.forEach((node) =>
- getManageableStyles(node).forEach((style) =>
- removedStyles.add(style)
- )
- );
- moves.forEach((node) =>
- getManageableStyles(node).forEach((style) =>
- movedStyles.add(style)
- )
- );
- handleStyleOperations(root, {
- createdStyles,
- removedStyles,
- movedStyles
- });
- additions.forEach((n) => {
- deepObserve(n);
- collectUndefinedElements(n);
- });
- additions.forEach(
- (node) => isCustomElement(node) && recordUndefinedElement(node)
- );
- }
- function handleHugeTreeMutations(root) {
- const styles = new Set(getManageableStyles(root));
- const createdStyles = new Set();
- const removedStyles = new Set();
- const movedStyles = new Set();
- const prevStyles = getPrevStyles(root);
- styles.forEach((s) => {
- if (!prevStyles.has(s)) {
- createdStyles.add(s);
+ let forceRenderStyle = false;
+ function render(theme, ignoreImageAnalysis) {
+ const rules = getRulesSync();
+ if (!rules) {
+ return;
+ }
+ cancelAsyncOperations = false;
+ function removeCSSRulesFromSheet(sheet) {
+ if (!sheet) {
+ return;
}
- });
- prevStyles.forEach((s) => {
- if (!styles.has(s)) {
- removedStyles.add(s);
+ for (let i = sheet.cssRules.length - 1; i >= 0; i--) {
+ sheet.deleteRule(i);
}
- });
- styles.forEach((s) => {
- if (
- !createdStyles.has(s) &&
- !removedStyles.has(s) &&
- didStylePositionChange(s)
- ) {
- movedStyles.add(s);
+ }
+ function prepareOverridesSheet() {
+ if (!syncStyle) {
+ createSyncStyle();
}
- });
- handleStyleOperations(root, {
- createdStyles,
- removedStyles,
- movedStyles
- });
- deepObserve(root);
- collectUndefinedElements(root);
- }
- function handleAttributeMutations(mutations) {
- const updatedStyles = new Set();
- const removedStyles = new Set();
- mutations.forEach((m) => {
- const { target } = m;
- if (target.isConnected) {
- if (shouldManageStyle(target)) {
- updatedStyles.add(target);
- } else if (
- target instanceof HTMLLinkElement &&
- target.disabled
- ) {
- removedStyles.add(target);
- }
+ syncStylePositionWatcher && syncStylePositionWatcher.stop();
+ insertStyle();
+ if (syncStyle.sheet == null) {
+ syncStyle.textContent = "";
+ }
+ const sheet = syncStyle.sheet;
+ removeCSSRulesFromSheet(sheet);
+ if (syncStylePositionWatcher) {
+ syncStylePositionWatcher.run();
+ } else {
+ syncStylePositionWatcher = watchForNodePosition(
+ syncStyle,
+ "prev-sibling",
+ () => {
+ forceRenderStyle = true;
+ buildOverrides();
+ }
+ );
}
- });
- if (updatedStyles.size + removedStyles.size > 0) {
- update({
- updated: Array.from(updatedStyles),
- created: [],
- removed: Array.from(removedStyles),
- moved: []
+ return syncStyle.sheet;
+ }
+ function buildOverrides() {
+ const force = forceRenderStyle;
+ forceRenderStyle = false;
+ sheetModifier.modifySheet({
+ prepareSheet: prepareOverridesSheet,
+ sourceCSSRules: rules,
+ theme,
+ ignoreImageAnalysis,
+ force,
+ isAsyncCancelled
});
+ isOverrideEmpty = syncStyle.sheet.cssRules.length === 0;
+ if (sheetModifier.shouldRebuildStyle()) {
+ addReadyStateCompleteListener(() => update());
+ }
}
+ buildOverrides();
}
- function observe(root) {
- if (observedRoots.has(root)) {
- return;
+ function getRulesOrError() {
+ try {
+ if (element.sheet == null) {
+ return [null, null];
+ }
+ return [element.sheet.cssRules, null];
+ } catch (err) {
+ return [null, err];
}
- const treeObserver = createOptimizedTreeObserver(root, {
- onMinorMutations: handleMinorTreeMutations,
- onHugeMutations: handleHugeTreeMutations
- });
- const attrObserver = new MutationObserver(handleAttributeMutations);
- attrObserver.observe(root, {
- attributeFilter: ["rel", "disabled", "media", "href"],
- subtree: true
- });
- observers.push(treeObserver, attrObserver);
- observedRoots.add(root);
}
- function subscribeForShadowRootChanges(node) {
- const { shadowRoot } = node;
- if (shadowRoot == null || observedRoots.has(shadowRoot)) {
- return;
+ function isStillLoadingError(error) {
+ return error && error.message && error.message.includes("loading");
+ }
+ function safeGetSheetRules() {
+ const [cssRules, err] = getRulesOrError();
+ if (err) {
+ return null;
}
- observe(shadowRoot);
- shadowRootDiscovered(shadowRoot);
+ return cssRules;
}
- function deepObserve(node) {
- iterateShadowHosts(node, subscribeForShadowRootChanges);
+ const sheetChangeWatcher = createSheetWatcher(
+ element,
+ safeGetSheetRules,
+ update,
+ isAsyncCancelled
+ );
+ function pause() {
+ observer.disconnect();
+ cancelAsyncOperations = true;
+ corsCopyPositionWatcher && corsCopyPositionWatcher.stop();
+ syncStylePositionWatcher && syncStylePositionWatcher.stop();
+ sheetChangeWatcher.stop();
}
- observe(document);
- deepObserve(document.documentElement);
- watchWhenCustomElementsDefined((hosts) => {
- const newStyles = [];
- hosts.forEach((host) =>
- push(newStyles, getManageableStyles(host.shadowRoot))
- );
- update({ created: newStyles, updated: [], removed: [], moved: [] });
- hosts.forEach((host) => {
- const { shadowRoot } = host;
- if (shadowRoot == null) {
- return;
- }
- subscribeForShadowRootChanges(host);
- deepObserve(shadowRoot);
- collectUndefinedElements(shadowRoot);
+ function destroy() {
+ pause();
+ removeNode(corsCopy);
+ removeNode(syncStyle);
+ loadingEnd();
+ if (rejectorsForLoadingLinks.has(loadingLinkId)) {
+ const reject = rejectorsForLoadingLinks.get(loadingLinkId);
+ rejectorsForLoadingLinks.delete(loadingLinkId);
+ reject && reject();
+ }
+ }
+ function watch() {
+ observer.observe(element, observerOptions);
+ if (element instanceof HTMLStyleElement) {
+ sheetChangeWatcher.start();
+ }
+ }
+ const maxMoveCount = 10;
+ let moveCount = 0;
+ function restore() {
+ if (!syncStyle) {
+ return;
+ }
+ moveCount++;
+ if (moveCount > maxMoveCount) {
+ return;
+ }
+ insertStyle();
+ corsCopyPositionWatcher && corsCopyPositionWatcher.skip();
+ syncStylePositionWatcher && syncStylePositionWatcher.skip();
+ if (!isOverrideEmpty) {
+ forceRenderStyle = true;
+ update();
+ }
+ }
+ return {
+ details,
+ render,
+ pause,
+ destroy,
+ watch,
+ restore
+ };
+ }
+ async function linkLoading(link, loadingId) {
+ return new Promise((resolve, reject) => {
+ const cleanUp = () => {
+ link.removeEventListener("load", onLoad);
+ link.removeEventListener("error", onError);
+ rejectorsForLoadingLinks.delete(loadingId);
+ };
+ const onLoad = () => {
+ cleanUp();
+ resolve();
+ };
+ const onError = () => {
+ cleanUp();
+ reject(
+ `Linkelement ${loadingId} couldn't be loaded. ${link.href}`
+ );
+ };
+ rejectorsForLoadingLinks.set(loadingId, () => {
+ cleanUp();
+ reject();
});
+ link.addEventListener("load", onLoad, {passive: true});
+ link.addEventListener("error", onError, {passive: true});
+ if (!link.href) {
+ onError();
+ }
});
- document.addEventListener("__darkreader__isDefined", handleIsDefined);
- collectUndefinedElements(document);
}
- function resetObservers() {
- observers.forEach((o) => o.disconnect());
- observers.splice(0, observers.length);
- observedRoots = new WeakSet();
+ function getCSSImportURL(importDeclaration) {
+ return getCSSURLValue(
+ importDeclaration
+ .substring(7)
+ .trim()
+ .replace(/;$/, "")
+ .replace(/screen$/, "")
+ );
}
- function stopWatchingForStylePositions() {
- resetObservers();
- unsubscribeFromDefineCustomElements();
+ async function loadText(url) {
+ if (url.startsWith("data:")) {
+ return await (await fetch(url)).text();
+ }
+ const cache = readCSSFetchCache(url);
+ if (cache) {
+ return cache;
+ }
+ const parsedURL = new URL(url);
+ let text;
+ if (parsedURL.origin === location.origin) {
+ text = await loadAsText(url, "text/css", location.origin);
+ }
+ text = await bgFetch({
+ url,
+ responseType: "text",
+ mimeType: "text/css",
+ origin: location.origin
+ });
+ writeCSSFetchCache(url, text);
+ return text;
}
-
- function watchForStyleChanges(currentStyles, update, shadowRootDiscovered) {
- watchForStylePositions(currentStyles, update, shadowRootDiscovered);
+ async function replaceCSSImports(cssText, basePath, cache = new Map()) {
+ cssText = removeCSSComments(cssText);
+ cssText = replaceCSSFontFace(cssText);
+ cssText = replaceCSSRelativeURLsWithAbsolute(cssText, basePath);
+ const importMatches = getMatchesWithOffsets(cssImportRegex, cssText);
+ let prev = null;
+ let shouldIgnoreImportsInBetween = false;
+ let diff = 0;
+ for (const match of importMatches) {
+ let importedCSS;
+ const prevImportEnd = prev ? prev.offset + prev.text.length : 0;
+ const nextImportStart = match.offset;
+ const openBraceIndex = cssText.indexOf("{", prevImportEnd);
+ const closeBraceIndex = cssText.indexOf("}", prevImportEnd);
+ if (
+ shouldIgnoreImportsInBetween ||
+ (openBraceIndex >= 0 &&
+ openBraceIndex < nextImportStart &&
+ closeBraceIndex >= 0 &&
+ closeBraceIndex < nextImportStart)
+ ) {
+ shouldIgnoreImportsInBetween = true;
+ importedCSS = "";
+ } else {
+ const importURL = getCSSImportURL(match.text);
+ const absoluteURL = getAbsoluteURL(basePath, importURL);
+ if (cache.has(absoluteURL)) {
+ importedCSS = cache.get(absoluteURL);
+ } else {
+ try {
+ importedCSS = await loadText(absoluteURL);
+ cache.set(absoluteURL, importedCSS);
+ importedCSS = await replaceCSSImports(
+ importedCSS,
+ getCSSBaseBath(absoluteURL),
+ cache
+ );
+ } catch (err) {
+ importedCSS = "";
+ }
+ }
+ }
+ cssText =
+ cssText.substring(0, match.offset + diff) +
+ importedCSS +
+ cssText.substring(match.offset + match.text.length + diff);
+ diff = importedCSS.length - match.text.length;
+ prev = match;
+ }
+ cssText = cssText.trim();
+ return cssText;
}
- function stopWatchingForStyleChanges() {
- stopWatchingForStylePositions();
+ function createCORSCopy(srcElement, cssText) {
+ if (!cssText) {
+ return null;
+ }
+ const cors = document.createElement("style");
+ cors.classList.add("darkreader");
+ cors.classList.add("darkreader--cors");
+ cors.media = "screen";
+ cors.textContent = cssText;
+ srcElement.parentNode.insertBefore(cors, srcElement.nextSibling);
+ cors.sheet.disabled = true;
+ corsStyleSet.add(cors);
+ return cors;
}
- let canUseSheetProxy = false;
- document.addEventListener(
- "__darkreader__inlineScriptsAllowed",
- () => (canUseSheetProxy = true),
- { once: true }
- );
- const overrides = new WeakSet();
- const overridesBySource = new WeakMap();
- function canHaveAdoptedStyleSheets(node) {
- return Array.isArray(node.adoptedStyleSheets);
- }
- function createAdoptedStyleSheetOverride(node) {
- let cancelAsyncOperations = false;
- function iterateSourceSheets(iterator) {
- node.adoptedStyleSheets.forEach((sheet) => {
- if (!overrides.has(sheet)) {
- iterator(sheet);
+ function injectProxy(
+ enableStyleSheetsProxy,
+ enableCustomElementRegistryProxy
+ ) {
+ document.dispatchEvent(
+ new CustomEvent("__darkreader__inlineScriptsAllowed")
+ );
+ const cleaners = [];
+ function cleanUp() {
+ cleaners.forEach((clean) => clean());
+ cleaners.splice(0);
+ }
+ function documentEventListener(type, listener, options) {
+ document.addEventListener(type, listener, options);
+ cleaners.push(() => document.removeEventListener(type, listener));
+ }
+ function disableConflictingPlugins() {
+ const disableWPDarkMode = () => {
+ if (window?.WPDarkMode?.deactivate) {
+ window.WPDarkMode.deactivate();
}
- });
+ };
+ disableWPDarkMode();
}
- function injectSheet(sheet, override) {
- const newSheets = [...node.adoptedStyleSheets];
- const sheetIndex = newSheets.indexOf(sheet);
- const overrideIndex = newSheets.indexOf(override);
- if (overrideIndex >= 0) {
- newSheets.splice(overrideIndex, 1);
+ documentEventListener("__darkreader__cleanUp", cleanUp);
+ documentEventListener(
+ "__darkreader__disableConflictingPlugins",
+ disableConflictingPlugins
+ );
+ function overrideProperty(cls, prop, overrides) {
+ const proto = cls.prototype;
+ const oldDescriptor = Object.getOwnPropertyDescriptor(proto, prop);
+ if (!oldDescriptor) {
+ return;
}
- newSheets.splice(sheetIndex + 1, 0, override);
- node.adoptedStyleSheets = newSheets;
+ const newDescriptor = {...oldDescriptor};
+ Object.keys(overrides).forEach((key) => {
+ const factory = overrides[key];
+ newDescriptor[key] = factory(oldDescriptor[key]);
+ });
+ Object.defineProperty(proto, prop, newDescriptor);
+ cleaners.push(() =>
+ Object.defineProperty(proto, prop, oldDescriptor)
+ );
}
- function clear() {
- const newSheets = [...node.adoptedStyleSheets];
- for (let i = newSheets.length - 1; i >= 0; i--) {
- const sheet = newSheets[i];
- if (overrides.has(sheet)) {
- newSheets.splice(i, 1);
+ function override(cls, prop, factory) {
+ overrideProperty(cls, prop, {value: factory});
+ }
+ function isDRElement(element) {
+ return element?.classList?.contains("darkreader");
+ }
+ function isDRSheet(sheet) {
+ return isDRElement(sheet.ownerNode);
+ }
+ const updateSheetEvent = new CustomEvent("__darkreader__updateSheet");
+ const adoptedSheetChangeEvent = new CustomEvent(
+ "__darkreader__adoptedStyleSheetChange"
+ );
+ const shadowDomAttachingEvent = new CustomEvent(
+ "__darkreader__shadowDomAttaching",
+ {bubbles: true}
+ );
+ const adoptedSheetOwners = new WeakMap();
+ const adoptedDeclarationSheets = new WeakMap();
+ function onAdoptedSheetChange(sheet) {
+ const owners = adoptedSheetOwners.get(sheet);
+ owners?.forEach((node) => {
+ if (node.isConnected) {
+ node.dispatchEvent(adoptedSheetChangeEvent);
+ } else {
+ owners.delete(node);
}
+ });
+ }
+ function reportSheetChange(sheet) {
+ if (sheet.ownerNode && !isDRSheet(sheet)) {
+ sheet.ownerNode.dispatchEvent(updateSheetEvent);
}
- if (node.adoptedStyleSheets.length !== newSheets.length) {
- node.adoptedStyleSheets = newSheets;
+ if (adoptedSheetOwners.has(sheet)) {
+ onAdoptedSheetChange(sheet);
}
- sourceSheets = new WeakSet();
- sourceDeclarations = new WeakSet();
}
- const cleaners = [];
- function destroy() {
- cleaners.forEach((c) => c());
- cleaners.splice(0);
- cancelAsyncOperations = true;
- clear();
- if (frameId) {
- cancelAnimationFrame(frameId);
- frameId = null;
+ function reportSheetChangeAsync(sheet, promise) {
+ const {ownerNode} = sheet;
+ if (
+ ownerNode &&
+ !isDRSheet(sheet) &&
+ promise &&
+ promise instanceof Promise
+ ) {
+ promise.then(() => ownerNode.dispatchEvent(updateSheetEvent));
}
- }
- let rulesChangeKey = 0;
- function getRulesChangeKey() {
- let count = 0;
- iterateSourceSheets((sheet) => {
- count += sheet.cssRules.length;
- });
- if (count === 1) {
- const rule = node.adoptedStyleSheets[0].cssRules[0];
- return rule instanceof CSSStyleRule ? rule.style.length : count;
+ if (adoptedSheetOwners.has(sheet)) {
+ if (promise && promise instanceof Promise) {
+ promise.then(() => onAdoptedSheetChange(sheet));
+ }
}
- return count;
}
- let sourceSheets = new WeakSet();
- let sourceDeclarations = new WeakSet();
- function render(theme, ignoreImageAnalysis) {
- clear();
- for (let i = node.adoptedStyleSheets.length - 1; i >= 0; i--) {
- const sheet = node.adoptedStyleSheets[i];
- if (overrides.has(sheet)) {
- continue;
+ override(
+ CSSStyleSheet,
+ "addRule",
+ (native) =>
+ function (selector, style, index) {
+ native.call(this, selector, style, index);
+ reportSheetChange(this);
+ return -1;
}
- sourceSheets.add(sheet);
- const readyOverride = overridesBySource.get(sheet);
- if (readyOverride) {
- rulesChangeKey = getRulesChangeKey();
- injectSheet(sheet, readyOverride);
- continue;
+ );
+ override(
+ CSSStyleSheet,
+ "insertRule",
+ (native) =>
+ function (rule, index) {
+ const returnValue = native.call(this, rule, index);
+ reportSheetChange(this);
+ return returnValue;
}
- const rules = sheet.cssRules;
- const override = new CSSStyleSheet();
- overridesBySource.set(sheet, override);
- iterateCSSRules(rules, (rule) =>
- sourceDeclarations.add(rule.style)
- );
- const prepareSheet = () => {
- for (let i = override.cssRules.length - 1; i >= 0; i--) {
- override.deleteRule(i);
- }
- override.insertRule("#__darkreader__adoptedOverride {}");
- injectSheet(sheet, override);
- overrides.add(override);
- return override;
- };
- const sheetModifier = createStyleSheetModifier();
- sheetModifier.modifySheet({
- prepareSheet,
- sourceCSSRules: rules,
- theme,
- ignoreImageAnalysis,
- force: false,
- isAsyncCancelled: () => cancelAsyncOperations
- });
- }
- rulesChangeKey = getRulesChangeKey();
- }
- let callbackRequested = false;
- function handleArrayChange(callback) {
- if (callbackRequested) {
- return;
- }
- callbackRequested = true;
- queueMicrotask(() => {
- callbackRequested = false;
- const sheets = node.adoptedStyleSheets.filter(
- (s) => !overrides.has(s)
- );
- sheets.forEach((sheet) => overridesBySource.delete(sheet));
- callback(sheets);
- });
- }
- function checkForUpdates() {
- return getRulesChangeKey() !== rulesChangeKey;
- }
- let frameId = null;
- function watchUsingRAF(callback) {
- frameId = requestAnimationFrame(() => {
- if (canUseSheetProxy) {
- return;
+ );
+ override(
+ CSSStyleSheet,
+ "deleteRule",
+ (native) =>
+ function (index) {
+ native.call(this, index);
+ reportSheetChange(this);
}
- if (checkForUpdates()) {
- handleArrayChange(callback);
+ );
+ override(
+ CSSStyleSheet,
+ "removeRule",
+ (native) =>
+ function (index) {
+ native.call(this, index);
+ reportSheetChange(this);
}
- watchUsingRAF(callback);
+ );
+ override(
+ CSSStyleSheet,
+ "replace",
+ (native) =>
+ function (cssText) {
+ const returnValue = native.call(this, cssText);
+ reportSheetChangeAsync(this, returnValue);
+ return returnValue;
+ }
+ );
+ override(
+ CSSStyleSheet,
+ "replaceSync",
+ (native) =>
+ function (cssText) {
+ native.call(this, cssText);
+ reportSheetChange(this);
+ }
+ );
+ override(
+ Element,
+ "attachShadow",
+ (native) =>
+ function (options) {
+ this.dispatchEvent(shadowDomAttachingEvent);
+ return native.call(this, options);
+ }
+ );
+ const shouldWrapHTMLElement =
+ location.hostname === "baidu.com" ||
+ location.hostname.endsWith(".baidu.com");
+ if (shouldWrapHTMLElement) {
+ override(
+ Element,
+ "getElementsByTagName",
+ (native) =>
+ function (tagName) {
+ if (tagName !== "style") {
+ return native.call(this, tagName);
+ }
+ const getCurrentElementValue = () => {
+ const elements = native.call(this, tagName);
+ return Object.setPrototypeOf(
+ [...elements].filter(
+ (element) =>
+ element && !isDRElement(element)
+ ),
+ NodeList.prototype
+ );
+ };
+ let elements = getCurrentElementValue();
+ const nodeListBehavior = {
+ get: function (_, property) {
+ return getCurrentElementValue()[
+ Number(property) || property
+ ];
+ }
+ };
+ elements = new Proxy(elements, nodeListBehavior);
+ return elements;
+ }
+ );
+ }
+ const shouldProxyChildNodes = ["brilliant.org", "www.vy.no"].includes(
+ location.hostname
+ );
+ if (shouldProxyChildNodes) {
+ overrideProperty(Node, "childNodes", {
+ get: (native) =>
+ function () {
+ const childNodes = native.call(this);
+ return Object.setPrototypeOf(
+ [...childNodes].filter((element) => {
+ return !isDRElement(element);
+ }),
+ NodeList.prototype
+ );
+ }
});
}
- function addSheetChangeEventListener(type, listener) {
- node.addEventListener(type, listener);
- cleaners.push(() => node.removeEventListener(type, listener));
+ function resolveCustomElement(tag) {
+ customElements.whenDefined(tag).then(() => {
+ document.dispatchEvent(
+ new CustomEvent("__darkreader__isDefined", {detail: {tag}})
+ );
+ });
}
- function watch(callback) {
- const onAdoptedSheetsChange = () => {
- canUseSheetProxy = true;
- handleArrayChange(callback);
- };
- addSheetChangeEventListener(
- "__darkreader__adoptedStyleSheetsChange",
- onAdoptedSheetsChange
- );
- addSheetChangeEventListener(
- "__darkreader__adoptedStyleSheetChange",
- onAdoptedSheetsChange
- );
- addSheetChangeEventListener(
- "__darkreader__adoptedStyleDeclarationChange",
- onAdoptedSheetsChange
+ documentEventListener("__darkreader__addUndefinedResolver", (e) =>
+ resolveCustomElement(e.detail.tag)
+ );
+ if (enableCustomElementRegistryProxy) {
+ override(
+ CustomElementRegistry,
+ "define",
+ (native) =>
+ function (name, constructor, options) {
+ resolveCustomElement(name);
+ native.call(this, name, constructor, options);
+ }
);
- if (canUseSheetProxy) {
+ }
+ let blobURLAllowed = null;
+ async function checkBlobURLSupport() {
+ if (blobURLAllowed != null) {
+ document.dispatchEvent(
+ new CustomEvent("__darkreader__blobURLCheckResponse", {
+ detail: {blobURLAllowed}
+ })
+ );
return;
}
- watchUsingRAF(callback);
- }
- return {
- render,
- destroy,
- watch
- };
- }
- class StyleSheetCommandBuilder {
- constructor(onChange) {
- this.cssRules = [];
- this.commands = [];
- this.onChange = onChange;
- }
- insertRule(cssText, index = 0) {
- this.commands.push({ type: "insert", index, cssText });
- this.cssRules.splice(
- index,
- 0,
- new StyleSheetCommandBuilder(this.onChange)
+ const svg =
+ '';
+ const bytes = new Uint8Array(svg.length);
+ for (let i = 0; i < svg.length; i++) {
+ bytes[i] = svg.charCodeAt(i);
+ }
+ const blob = new Blob([bytes], {type: "image/svg+xml"});
+ const objectURL = URL.createObjectURL(blob);
+ try {
+ const image = new Image();
+ await new Promise((resolve, reject) => {
+ image.onload = () => resolve();
+ image.onerror = () => reject();
+ image.src = objectURL;
+ });
+ blobURLAllowed = true;
+ } catch (err) {
+ blobURLAllowed = false;
+ }
+ document.dispatchEvent(
+ new CustomEvent("__darkreader__blobURLCheckResponse", {
+ detail: {blobURLAllowed}
+ })
);
- this.onChange();
- return index;
}
- deleteRule(index) {
- this.commands.push({ type: "delete", index });
- this.cssRules.splice(index, 1);
- this.onChange();
+ documentEventListener(
+ "__darkreader__blobURLCheckRequest",
+ checkBlobURLSupport
+ );
+ if (enableStyleSheetsProxy) {
+ overrideProperty(Document, "styleSheets", {
+ get: (native) =>
+ function () {
+ const getCurrentValue = () => {
+ const docSheets = native.call(this);
+ const filteredSheets = [...docSheets].filter(
+ (styleSheet) =>
+ styleSheet.ownerNode &&
+ !isDRSheet(styleSheet)
+ );
+ filteredSheets.item = (item) =>
+ filteredSheets[item];
+ return Object.setPrototypeOf(
+ filteredSheets,
+ StyleSheetList.prototype
+ );
+ };
+ let elements = getCurrentValue();
+ const styleSheetListBehavior = {
+ get: function (_, property) {
+ return getCurrentValue()[property];
+ }
+ };
+ elements = new Proxy(elements, styleSheetListBehavior);
+ return elements;
+ }
+ });
}
- replaceSync(cssText) {
- this.commands.splice(0);
- this.commands.push({ type: "replace", cssText });
- if (cssText === "") {
- this.cssRules.splice(0);
- } else {
- throw new Error(
- "StyleSheetCommandBuilder.replaceSync() is not fully supported"
+ {
+ const adoptedSheetsSourceProxies = new WeakMap();
+ const adoptedSheetsProxySources = new WeakMap();
+ const adoptedSheetsChangeEvent = new CustomEvent(
+ "__darkreader__adoptedStyleSheetsChange"
+ );
+ const adoptedSheetOverrideCache = new WeakSet();
+ const adoptedSheetsSnapshots = new WeakMap();
+ const isDRAdoptedSheetOverride = (sheet) => {
+ if (!sheet || !sheet.cssRules) {
+ return false;
+ }
+ if (adoptedSheetOverrideCache.has(sheet)) {
+ return true;
+ }
+ if (
+ sheet.cssRules.length > 0 &&
+ sheet.cssRules[0].cssText.startsWith(
+ "#__darkreader__adoptedOverride"
+ )
+ ) {
+ adoptedSheetOverrideCache.add(sheet);
+ return true;
+ }
+ return false;
+ };
+ const areArraysEqual = (a, b) => {
+ return a.length === b.length && a.every((x, i) => x === b[i]);
+ };
+ const onAdoptedSheetsChange = (node) => {
+ const prev = adoptedSheetsSnapshots.get(node);
+ const curr = (node.adoptedStyleSheets || []).filter(
+ (s) => !isDRAdoptedSheetOverride(s)
);
- }
- this.onChange();
- }
- getDeepCSSCommands() {
- const deep = [];
- this.commands.forEach((command) => {
- deep.push({
- type: command.type,
- cssText: command.type !== "delete" ? command.cssText : "",
- path: command.type === "replace" ? [] : [command.index]
+ adoptedSheetsSnapshots.set(node, curr);
+ if (!prev || !areArraysEqual(prev, curr)) {
+ curr.forEach((sheet) => {
+ if (!adoptedSheetOwners.has(sheet)) {
+ adoptedSheetOwners.set(sheet, new Set());
+ }
+ adoptedSheetOwners.get(sheet).add(node);
+ for (const rule of sheet.cssRules) {
+ const declaration = rule.style;
+ if (declaration) {
+ adoptedDeclarationSheets.set(
+ declaration,
+ sheet
+ );
+ }
+ }
+ });
+ node.dispatchEvent(adoptedSheetsChangeEvent);
+ }
+ };
+ const proxyAdoptedSheetsArray = (node, source) => {
+ if (adoptedSheetsProxySources.has(source)) {
+ return source;
+ }
+ if (adoptedSheetsSourceProxies.has(source)) {
+ return adoptedSheetsSourceProxies.get(source);
+ }
+ const proxy = new Proxy(source, {
+ deleteProperty(target, property) {
+ delete target[property];
+ return true;
+ },
+ set(target, property, value) {
+ target[property] = value;
+ if (property === "length") {
+ onAdoptedSheetsChange(node);
+ }
+ return true;
+ }
});
- });
- this.cssRules.forEach((rule, i) => {
- const childCommands = rule.getDeepCSSCommands();
- childCommands.forEach((c) => c.path.unshift(i));
- });
- return deep;
- }
- clearDeepCSSCommands() {
- this.commands.splice(0);
- this.cssRules.forEach((rule) => rule.clearDeepCSSCommands());
- }
- }
- function createAdoptedStyleSheetFallback(onChange) {
- let cancelAsyncOperations = false;
- let sourceCSSRules = [];
- let lastTheme;
- let lastIgnoreImageAnalysis;
- function updateCSS(cssRules) {
- sourceCSSRules = cssRules;
- if (lastTheme && lastIgnoreImageAnalysis) {
- render(lastTheme, lastIgnoreImageAnalysis);
- }
- }
- const builder = new StyleSheetCommandBuilder(onChange);
- function render(theme, ignoreImageAnalysis) {
- lastTheme = theme;
- lastIgnoreImageAnalysis = ignoreImageAnalysis;
- const prepareSheet = () => {
- builder.replaceSync("");
- return builder;
+ adoptedSheetsSourceProxies.set(source, proxy);
+ adoptedSheetsProxySources.set(proxy, source);
+ return proxy;
};
- const sheetModifier = createStyleSheetModifier();
- sheetModifier.modifySheet({
- prepareSheet,
- sourceCSSRules,
- theme,
- ignoreImageAnalysis,
- force: false,
- isAsyncCancelled: () => cancelAsyncOperations
+ [Document, ShadowRoot].forEach((ctor) => {
+ overrideProperty(ctor, "adoptedStyleSheets", {
+ get: (native) =>
+ function () {
+ const source = native.call(this);
+ return proxyAdoptedSheetsArray(this, source);
+ },
+ set: (native) =>
+ function (source) {
+ if (adoptedSheetsProxySources.has(source)) {
+ source = adoptedSheetsProxySources.get(source);
+ }
+ native.call(this, source);
+ onAdoptedSheetsChange(this);
+ }
+ });
+ });
+ const adoptedDeclarationChangeEvent = new CustomEvent(
+ "__darkreader__adoptedStyleDeclarationChange"
+ );
+ ["setProperty", "removeProperty"].forEach((key) => {
+ override(CSSStyleDeclaration, key, (native) => {
+ return function (...args) {
+ const returnValue = native.apply(this, args);
+ const sheet = adoptedDeclarationSheets.get(this);
+ if (sheet) {
+ const owners = adoptedSheetOwners.get(sheet);
+ if (owners) {
+ owners.forEach((node) => {
+ node.dispatchEvent(
+ adoptedDeclarationChangeEvent
+ );
+ });
+ }
+ }
+ return returnValue;
+ };
+ });
});
}
- function commands() {
- const commands = builder.getDeepCSSCommands();
- builder.clearDeepCSSCommands();
- return commands;
- }
- function destroy() {
- cancelAsyncOperations = true;
- }
- return { render, destroy, updateCSS, commands };
}
- function injectProxy(
- enableStyleSheetsProxy,
- enableCustomElementRegistryProxy
- ) {
- document.dispatchEvent(
- new CustomEvent("__darkreader__inlineScriptsAllowed")
- );
- const cleaners = [];
- function cleanUp() {
- cleaners.forEach((clean) => clean());
- cleaners.splice(0);
+ const definedCustomElements = new Set();
+ const undefinedGroups = new Map();
+ let elementsDefinitionCallback;
+ function isCustomElement(element) {
+ if (element.tagName.includes("-") || element.getAttribute("is")) {
+ return true;
}
- function documentEventListener(type, listener, options) {
- document.addEventListener(type, listener, options);
- cleaners.push(() => document.removeEventListener(type, listener));
+ return false;
+ }
+ function recordUndefinedElement(element) {
+ let tag = element.tagName.toLowerCase();
+ if (!tag.includes("-")) {
+ const extendedTag = element.getAttribute("is");
+ if (extendedTag) {
+ tag = extendedTag;
+ } else {
+ return;
+ }
}
- documentEventListener("__darkreader__cleanUp", cleanUp);
- function overrideProperty(cls, prop, overrides) {
- const proto = cls.prototype;
- const oldDescriptor = Object.getOwnPropertyDescriptor(proto, prop);
- const newDescriptor = { ...oldDescriptor };
- Object.keys(overrides).forEach((key) => {
- const factory = overrides[key];
- newDescriptor[key] = factory(oldDescriptor[key]);
+ if (!undefinedGroups.has(tag)) {
+ undefinedGroups.set(tag, new Set());
+ customElementsWhenDefined(tag).then(() => {
+ if (elementsDefinitionCallback) {
+ const elements = undefinedGroups.get(tag);
+ undefinedGroups.delete(tag);
+ elementsDefinitionCallback(Array.from(elements));
+ }
});
- Object.defineProperty(proto, prop, newDescriptor);
- cleaners.push(() =>
- Object.defineProperty(proto, prop, oldDescriptor)
- );
}
- function override(cls, prop, factory) {
- overrideProperty(cls, prop, { value: factory });
- }
- function isDRElement(element) {
- return element?.classList?.contains("darkreader");
- }
- function isDRSheet(sheet) {
- return isDRElement(sheet.ownerNode);
+ undefinedGroups.get(tag).add(element);
+ }
+ function collectUndefinedElements(root) {
+ if (!isDefinedSelectorSupported) {
+ return;
}
- const updateSheetEvent = new CustomEvent("__darkreader__updateSheet");
- const adoptedSheetChangeEvent = new CustomEvent(
- "__darkreader__adoptedStyleSheetChange"
+ forEach(
+ root.querySelectorAll(":not(:defined)"),
+ recordUndefinedElement
);
- const adoptedSheetOwners = new WeakMap();
- const adoptedDeclarationSheets = new WeakMap();
- function onAdoptedSheetChange(sheet) {
- const owners = adoptedSheetOwners.get(sheet);
- owners?.forEach((node) => {
- if (node.isConnected) {
- node.dispatchEvent(adoptedSheetChangeEvent);
- } else {
- owners.delete(node);
- }
+ }
+ let canOptimizeUsingProxy = false;
+ document.addEventListener(
+ "__darkreader__inlineScriptsAllowed",
+ () => {
+ canOptimizeUsingProxy = true;
+ },
+ {once: true, passive: true}
+ );
+ const unhandledShadowHosts = new Set();
+ document.addEventListener("__darkreader__shadowDomAttaching", (e) => {
+ const host = e.target;
+ if (unhandledShadowHosts.size === 0) {
+ queueMicrotask(() => {
+ const hosts = [...unhandledShadowHosts].filter(
+ (el) => el.shadowRoot
+ );
+ elementsDefinitionCallback?.(hosts);
+ unhandledShadowHosts.clear();
});
}
- function reportSheetChange(sheet) {
- if (sheet.ownerNode && !isDRSheet(sheet)) {
- sheet.ownerNode.dispatchEvent(updateSheetEvent);
- }
- if (adoptedSheetOwners.has(sheet)) {
- onAdoptedSheetChange(sheet);
- }
+ unhandledShadowHosts.add(host);
+ });
+ const resolvers = new Map();
+ function handleIsDefined(e) {
+ canOptimizeUsingProxy = true;
+ const tag = e.detail.tag;
+ definedCustomElements.add(tag);
+ if (resolvers.has(tag)) {
+ const r = resolvers.get(tag);
+ resolvers.delete(tag);
+ r.forEach((r) => r());
}
- function reportSheetChangeAsync(sheet, promise) {
- const { ownerNode } = sheet;
+ }
+ async function customElementsWhenDefined(tag) {
+ if (definedCustomElements.has(tag)) {
+ return;
+ }
+ return new Promise((resolve) => {
if (
- ownerNode &&
- !isDRSheet(sheet) &&
- promise &&
- promise instanceof Promise
+ window.customElements &&
+ typeof customElements.whenDefined === "function"
) {
- promise.then(() => ownerNode.dispatchEvent(updateSheetEvent));
- }
- if (adoptedSheetOwners.has(sheet)) {
- if (promise && promise instanceof Promise) {
- promise.then(() => onAdoptedSheetChange(sheet));
- }
- }
- }
- override(
- CSSStyleSheet,
- "addRule",
- (native) =>
- function (selector, style, index) {
- native.call(this, selector, style, index);
- reportSheetChange(this);
- return -1;
- }
- );
- override(
- CSSStyleSheet,
- "insertRule",
- (native) =>
- function (rule, index) {
- const returnValue = native.call(this, rule, index);
- reportSheetChange(this);
- return returnValue;
- }
- );
- override(
- CSSStyleSheet,
- "deleteRule",
- (native) =>
- function (index) {
- native.call(this, index);
- reportSheetChange(this);
- }
- );
- override(
- CSSStyleSheet,
- "removeRule",
- (native) =>
- function (index) {
- native.call(this, index);
- reportSheetChange(this);
- }
- );
- override(
- CSSStyleSheet,
- "replace",
- (native) =>
- function (cssText) {
- const returnValue = native.call(this, cssText);
- reportSheetChangeAsync(this, returnValue);
- return returnValue;
- }
- );
- override(
- CSSStyleSheet,
- "replaceSync",
- (native) =>
- function (cssText) {
- native.call(this, cssText);
- reportSheetChange(this);
+ customElements.whenDefined(tag).then(() => resolve());
+ } else if (canOptimizeUsingProxy) {
+ if (resolvers.has(tag)) {
+ resolvers.get(tag).push(resolve);
+ } else {
+ resolvers.set(tag, [resolve]);
}
- );
- const shouldWrapHTMLElement =
- location.hostname === "baidu.com" ||
- location.hostname.endsWith(".baidu.com");
- if (shouldWrapHTMLElement) {
- override(
- Element,
- "getElementsByTagName",
- (native) =>
- function (tagName) {
- if (tagName !== "style") {
- return native.call(this, tagName);
- }
- const getCurrentElementValue = () => {
- const elements = native.call(this, tagName);
- return Object.setPrototypeOf(
- [...elements].filter(
- (element) =>
- element && !isDRElement(element)
- ),
- NodeList.prototype
- );
- };
- let elements = getCurrentElementValue();
- const nodeListBehavior = {
- get: function (_, property) {
- return getCurrentElementValue()[
- Number(property) || property
- ];
- }
- };
- elements = new Proxy(elements, nodeListBehavior);
- return elements;
+ document.dispatchEvent(
+ new CustomEvent("__darkreader__addUndefinedResolver", {
+ detail: {tag}
+ })
+ );
+ } else {
+ const checkIfDefined = () => {
+ const elements = undefinedGroups.get(tag);
+ if (elements && elements.size > 0) {
+ if (
+ elements.values().next().value.matches(":defined")
+ ) {
+ resolve();
+ } else {
+ requestAnimationFrame(checkIfDefined);
+ }
}
- );
- }
- const shouldProxyChildNodes = ["brilliant.org", "www.vy.no"].includes(
- location.hostname
+ };
+ requestAnimationFrame(checkIfDefined);
+ }
+ });
+ }
+ function watchWhenCustomElementsDefined(callback) {
+ elementsDefinitionCallback = callback;
+ }
+ function unsubscribeFromDefineCustomElements() {
+ elementsDefinitionCallback = null;
+ undefinedGroups.clear();
+ document.removeEventListener(
+ "__darkreader__isDefined",
+ handleIsDefined
);
- if (shouldProxyChildNodes) {
- overrideProperty(Node, "childNodes", {
- get: (native) =>
- function () {
- const childNodes = native.call(this);
- return Object.setPrototypeOf(
- [...childNodes].filter((element) => {
- return !isDRElement(element);
- }),
- NodeList.prototype
- );
- }
- });
+ }
+
+ const observers = [];
+ let observedRoots;
+ let handledShadowHosts;
+ function watchForStylePositions(
+ currentStyles,
+ update,
+ shadowRootDiscovered
+ ) {
+ stopWatchingForStylePositions();
+ const prevStylesByRoot = new WeakMap();
+ const getPrevStyles = (root) => {
+ if (!prevStylesByRoot.has(root)) {
+ prevStylesByRoot.set(root, new Set());
+ }
+ return prevStylesByRoot.get(root);
+ };
+ currentStyles.forEach((node) => {
+ let root = node;
+ while ((root = root.parentNode)) {
+ if (
+ root === document ||
+ root.nodeType === Node.DOCUMENT_FRAGMENT_NODE
+ ) {
+ const prevStyles = getPrevStyles(root);
+ prevStyles.add(node);
+ break;
+ }
+ }
+ });
+ const prevStyleSiblings = new WeakMap();
+ const nextStyleSiblings = new WeakMap();
+ function saveStylePosition(style) {
+ prevStyleSiblings.set(style, style.previousElementSibling);
+ nextStyleSiblings.set(style, style.nextElementSibling);
}
- function resolveCustomElement(tag) {
- customElements.whenDefined(tag).then(() => {
- document.dispatchEvent(
- new CustomEvent("__darkreader__isDefined", { detail: { tag } })
- );
- });
+ function forgetStylePosition(style) {
+ prevStyleSiblings.delete(style);
+ nextStyleSiblings.delete(style);
}
- documentEventListener("__darkreader__addUndefinedResolver", (e) =>
- resolveCustomElement(e.detail.tag)
- );
- if (enableCustomElementRegistryProxy) {
- override(
- CustomElementRegistry,
- "define",
- (native) =>
- function (name, constructor, options) {
- resolveCustomElement(name);
- native.call(this, name, constructor, options);
- }
+ function didStylePositionChange(style) {
+ return (
+ style.previousElementSibling !== prevStyleSiblings.get(style) ||
+ style.nextElementSibling !== nextStyleSiblings.get(style)
);
}
- async function checkBlobURLSupport() {
- const svg =
- '';
- const bytes = new Uint8Array(svg.length);
- for (let i = 0; i < svg.length; i++) {
- bytes[i] = svg.charCodeAt(i);
- }
- const blob = new Blob([bytes], { type: "image/svg+xml" });
- const objectURL = URL.createObjectURL(blob);
- let blobURLAllowed;
- try {
- const image = new Image();
- await new Promise((resolve, reject) => {
- image.onload = () => resolve();
- image.onerror = () => reject();
- image.src = objectURL;
+ currentStyles.forEach(saveStylePosition);
+ function handleStyleOperations(root, operations) {
+ const {createdStyles, removedStyles, movedStyles} = operations;
+ createdStyles.forEach((s) => saveStylePosition(s));
+ movedStyles.forEach((s) => saveStylePosition(s));
+ removedStyles.forEach((s) => forgetStylePosition(s));
+ const prevStyles = getPrevStyles(root);
+ createdStyles.forEach((s) => prevStyles.add(s));
+ removedStyles.forEach((s) => prevStyles.delete(s));
+ if (
+ createdStyles.size + removedStyles.size + movedStyles.size >
+ 0
+ ) {
+ update({
+ created: Array.from(createdStyles),
+ removed: Array.from(removedStyles),
+ moved: Array.from(movedStyles),
+ updated: []
});
- blobURLAllowed = true;
- } catch (err) {
- blobURLAllowed = false;
}
- document.dispatchEvent(
- new CustomEvent("__darkreader__blobURLCheckResponse", {
- detail: { blobURLAllowed }
- })
- );
}
- documentEventListener(
- "__darkreader__blobURLCheckRequest",
- checkBlobURLSupport,
- { once: true }
- );
- if (enableStyleSheetsProxy) {
- overrideProperty(Document, "styleSheets", {
- get: (native) =>
- function () {
- const getCurrentValue = () => {
- const docSheets = native.call(this);
- const filteredSheets = [...docSheets].filter(
- (styleSheet) =>
- styleSheet.ownerNode &&
- !isDRSheet(styleSheet)
- );
- filteredSheets.item = (item) =>
- filteredSheets[item];
- return Object.setPrototypeOf(
- filteredSheets,
- StyleSheetList.prototype
- );
- };
- let elements = getCurrentValue();
- const styleSheetListBehavior = {
- get: function (_, property) {
- return getCurrentValue()[property];
- }
- };
- elements = new Proxy(elements, styleSheetListBehavior);
- return elements;
- }
+ function handleMinorTreeMutations(root, {additions, moves, deletions}) {
+ const createdStyles = new Set();
+ const removedStyles = new Set();
+ const movedStyles = new Set();
+ additions.forEach((node) =>
+ getManageableStyles(node).forEach((style) =>
+ createdStyles.add(style)
+ )
+ );
+ deletions.forEach((node) =>
+ getManageableStyles(node).forEach((style) =>
+ removedStyles.add(style)
+ )
+ );
+ moves.forEach((node) =>
+ getManageableStyles(node).forEach((style) =>
+ movedStyles.add(style)
+ )
+ );
+ handleStyleOperations(root, {
+ createdStyles,
+ removedStyles,
+ movedStyles
});
- }
- {
- const adoptedSheetsSourceProxies = new WeakMap();
- const adoptedSheetsProxySources = new WeakMap();
- const adoptedSheetsChangeEvent = new CustomEvent(
- "__darkreader__adoptedStyleSheetsChange"
+ additions.forEach((n) => {
+ deepObserve(n);
+ collectUndefinedElements(n);
+ });
+ additions.forEach(
+ (node) => isCustomElement(node) && recordUndefinedElement(node)
);
- const adoptedSheetOverrideCache = new WeakSet();
- const adoptedSheetsSnapshots = new WeakMap();
- const isDRAdoptedSheetOverride = (sheet) => {
- if (!sheet || !sheet.cssRules) {
- return false;
- }
- if (adoptedSheetOverrideCache.has(sheet)) {
- return true;
- }
- if (
- sheet.cssRules.length > 0 &&
- sheet.cssRules[0].cssText.startsWith(
- "#__darkreader__adoptedOverride"
- )
- ) {
- adoptedSheetOverrideCache.add(sheet);
- return true;
- }
- return false;
- };
- const areArraysEqual = (a, b) => {
- return a.length === b.length && a.every((x, i) => x === b[i]);
- };
- const onAdoptedSheetsChange = (node) => {
- const prev = adoptedSheetsSnapshots.get(node);
- const curr = (node.adoptedStyleSheets || []).filter(
- (s) => !isDRAdoptedSheetOverride(s)
- );
- adoptedSheetsSnapshots.set(node, curr);
- if (!prev || !areArraysEqual(prev, curr)) {
- curr.forEach((sheet) => {
- if (!adoptedSheetOwners.has(sheet)) {
- adoptedSheetOwners.set(sheet, new Set());
- }
- adoptedSheetOwners.get(sheet).add(node);
- for (const rule of sheet.cssRules) {
- const declaration = rule.style;
- if (declaration) {
- adoptedDeclarationSheets.set(
- declaration,
- sheet
- );
- }
- }
- });
- node.dispatchEvent(adoptedSheetsChangeEvent);
+ additions.forEach((node) => checkImageSelectors(node));
+ }
+ function handleHugeTreeMutations(root) {
+ const styles = new Set(getManageableStyles(root));
+ const createdStyles = new Set();
+ const removedStyles = new Set();
+ const movedStyles = new Set();
+ const prevStyles = getPrevStyles(root);
+ styles.forEach((s) => {
+ if (!prevStyles.has(s)) {
+ createdStyles.add(s);
}
- };
- const proxyAdoptedSheetsArray = (node, source) => {
- if (adoptedSheetsProxySources.has(source)) {
- return source;
+ });
+ prevStyles.forEach((s) => {
+ if (!styles.has(s)) {
+ removedStyles.add(s);
}
- if (adoptedSheetsSourceProxies.has(source)) {
- return adoptedSheetsSourceProxies.get(source);
+ });
+ styles.forEach((s) => {
+ if (
+ !createdStyles.has(s) &&
+ !removedStyles.has(s) &&
+ didStylePositionChange(s)
+ ) {
+ movedStyles.add(s);
}
- const proxy = new Proxy(source, {
- deleteProperty(target, property) {
- delete target[property];
- return true;
- },
- set(target, property, value) {
- target[property] = value;
- if (property === "length") {
- onAdoptedSheetsChange(node);
- }
- return true;
+ });
+ handleStyleOperations(root, {
+ createdStyles,
+ removedStyles,
+ movedStyles
+ });
+ deepObserve(root);
+ collectUndefinedElements(root);
+ checkImageSelectors(root);
+ }
+ function handleAttributeMutations(mutations) {
+ const updatedStyles = new Set();
+ const removedStyles = new Set();
+ mutations.forEach((m) => {
+ const {target} = m;
+ if (target.isConnected) {
+ if (shouldManageStyle(target)) {
+ updatedStyles.add(target);
+ } else if (
+ target instanceof HTMLLinkElement &&
+ target.disabled
+ ) {
+ removedStyles.add(target);
}
- });
- adoptedSheetsSourceProxies.set(source, proxy);
- adoptedSheetsProxySources.set(proxy, source);
- return proxy;
- };
- [Document, ShadowRoot].forEach((ctor) => {
- overrideProperty(ctor, "adoptedStyleSheets", {
- get: (native) =>
- function () {
- const source = native.call(this);
- return proxyAdoptedSheetsArray(this, source);
- },
- set: (native) =>
- function (source) {
- if (adoptedSheetsProxySources.has(source)) {
- source = adoptedSheetsProxySources.get(source);
- }
- native.call(this, source);
- onAdoptedSheetsChange(this);
- }
- });
+ }
});
- const adoptedDeclarationChangeEvent = new CustomEvent(
- "__darkreader__adoptedStyleDeclarationChange"
- );
- ["setProperty", "removeProperty"].forEach((key) => {
- override(CSSStyleDeclaration, key, (native) => {
- return function (...args) {
- const returnValue = native.apply(this, args);
- const sheet = adoptedDeclarationSheets.get(this);
- if (sheet) {
- const owners = adoptedSheetOwners.get(sheet);
- if (owners) {
- owners.forEach((node) => {
- node.dispatchEvent(
- adoptedDeclarationChangeEvent
- );
- });
- }
- }
- return returnValue;
- };
+ if (updatedStyles.size + removedStyles.size > 0) {
+ update({
+ updated: Array.from(updatedStyles),
+ created: [],
+ removed: Array.from(removedStyles),
+ moved: []
});
+ }
+ }
+ function observe(root) {
+ if (observedRoots.has(root)) {
+ return;
+ }
+ const treeObserver = createOptimizedTreeObserver(root, {
+ onMinorMutations: handleMinorTreeMutations,
+ onHugeMutations: handleHugeTreeMutations
+ });
+ const attrObserver = new MutationObserver(handleAttributeMutations);
+ attrObserver.observe(root, {
+ attributeFilter: ["rel", "disabled", "media", "href"],
+ subtree: true
});
+ observers.push(treeObserver, attrObserver);
+ observedRoots.add(root);
}
- }
-
- let documentVisibilityListener = null;
- let documentIsVisible_ = !document.hidden;
- const listenerOptions = {
- capture: true,
- passive: true
- };
- function watchForDocumentVisibility() {
- document.addEventListener(
- "visibilitychange",
- documentVisibilityListener,
- listenerOptions
- );
- window.addEventListener(
- "pageshow",
- documentVisibilityListener,
- listenerOptions
- );
- window.addEventListener(
- "focus",
- documentVisibilityListener,
- listenerOptions
- );
- }
- function stopWatchingForDocumentVisibility() {
- document.removeEventListener(
- "visibilitychange",
- documentVisibilityListener,
- listenerOptions
- );
- window.removeEventListener(
- "pageshow",
- documentVisibilityListener,
- listenerOptions
- );
- window.removeEventListener(
- "focus",
- documentVisibilityListener,
- listenerOptions
- );
- }
- function setDocumentVisibilityListener(callback) {
- const alreadyWatching = Boolean(documentVisibilityListener);
- documentVisibilityListener = () => {
- if (!document.hidden) {
- removeDocumentVisibilityListener();
- callback();
- documentIsVisible_ = true;
+ function subscribeForShadowRootChanges(node) {
+ const {shadowRoot} = node;
+ if (shadowRoot == null || observedRoots.has(shadowRoot)) {
+ return;
}
- };
- if (!alreadyWatching) {
- watchForDocumentVisibility();
+ observe(shadowRoot);
+ shadowRootDiscovered(shadowRoot);
}
+ function deepObserve(node) {
+ iterateShadowHosts(node, subscribeForShadowRootChanges);
+ }
+ observe(document);
+ deepObserve(document.documentElement);
+ watchWhenCustomElementsDefined((hosts) => {
+ hosts = hosts.filter((node) => !handledShadowHosts.has(node));
+ const newStyles = [];
+ hosts.forEach((host) =>
+ push(newStyles, getManageableStyles(host.shadowRoot))
+ );
+ update({created: newStyles, updated: [], removed: [], moved: []});
+ hosts.forEach((host) => {
+ const {shadowRoot} = host;
+ if (shadowRoot == null) {
+ return;
+ }
+ subscribeForShadowRootChanges(host);
+ deepObserve(shadowRoot);
+ collectUndefinedElements(shadowRoot);
+ });
+ hosts.forEach((node) => handledShadowHosts.add(node));
+ });
+ document.addEventListener("__darkreader__isDefined", handleIsDefined);
+ collectUndefinedElements(document);
}
- function removeDocumentVisibilityListener() {
- stopWatchingForDocumentVisibility();
- documentVisibilityListener = null;
+ function resetObservers() {
+ observers.forEach((o) => o.disconnect());
+ observers.splice(0, observers.length);
+ observedRoots = new WeakSet();
+ handledShadowHosts = new WeakSet();
}
- function documentIsVisible() {
- return documentIsVisible_;
+ function stopWatchingForStylePositions() {
+ resetObservers();
+ unsubscribeFromDefineCustomElements();
+ }
+
+ function watchForStyleChanges(currentStyles, update, shadowRootDiscovered) {
+ watchForStylePositions(currentStyles, update, shadowRootDiscovered);
+ }
+ function stopWatchingForStyleChanges() {
+ stopWatchingForStylePositions();
}
const INSTANCE_ID = generateUID();
const styleManagers = new Map();
const adoptedStyleManagers = [];
const adoptedStyleFallbacks = new Map();
- const adoptedStyleNodeIds = new WeakMap();
const adoptedStyleChangeTokens = new WeakMap();
let theme = null;
let fixes = null;
@@ -6877,36 +7233,28 @@
document.head.appendChild(overrideStyle);
setupNodePositionWatcher(overrideStyle, "override");
const variableStyle = createOrUpdateStyle("darkreader--variables");
- const selectionColors = getSelectionColor(theme);
- const {
- darkSchemeBackgroundColor,
- darkSchemeTextColor,
- lightSchemeBackgroundColor,
- lightSchemeTextColor,
- mode
- } = theme;
- let schemeBackgroundColor =
- mode === 0 ? lightSchemeBackgroundColor : darkSchemeBackgroundColor;
- let schemeTextColor =
- mode === 0 ? lightSchemeTextColor : darkSchemeTextColor;
- schemeBackgroundColor = modifyBackgroundColor(
- parseColorWithCache(schemeBackgroundColor),
+ const selectionColors = theme?.selectionColor
+ ? getSelectionColor(theme)
+ : null;
+ const neutralBackgroundColor = modifyBackgroundColor(
+ parseColorWithCache("#ffffff"),
theme
);
- schemeTextColor = modifyForegroundColor(
- parseColorWithCache(schemeTextColor),
+ const neutralTextColor = modifyForegroundColor(
+ parseColorWithCache("#000000"),
theme
);
variableStyle.textContent = [
`:root {`,
- ` --darkreader-neutral-background: ${schemeBackgroundColor};`,
- ` --darkreader-neutral-text: ${schemeTextColor};`,
- ` --darkreader-selection-background: ${selectionColors.backgroundColorSelection};`,
- ` --darkreader-selection-text: ${selectionColors.foregroundColorSelection};`,
+ ` --darkreader-neutral-background: ${neutralBackgroundColor};`,
+ ` --darkreader-neutral-text: ${neutralTextColor};`,
+ ` --darkreader-selection-background: ${selectionColors?.backgroundColorSelection ?? "initial"};`,
+ ` --darkreader-selection-text: ${selectionColors?.foregroundColorSelection ?? "initial"};`,
`}`
].join("\n");
document.head.insertBefore(variableStyle, inlineStyle.nextSibling);
setupNodePositionWatcher(variableStyle, "variables");
+ registerVariablesSheet(variableStyle.sheet);
const rootVarsStyle = createOrUpdateStyle("darkreader--root-vars");
document.head.insertBefore(rootVarsStyle, variableStyle.nextSibling);
const enableStyleSheetsProxy = !(
@@ -6956,9 +7304,9 @@
function delayedCreateShadowStaticStyleOverrides(root) {
const observer = new MutationObserver((mutations, observer) => {
observer.disconnect();
- for (const { type, removedNodes } of mutations) {
+ for (const {type, removedNodes} of mutations) {
if (type === "childList") {
- for (const { nodeName, className } of removedNodes) {
+ for (const {nodeName, className} of removedNodes) {
if (
nodeName === "STYLE" &&
[
@@ -6974,7 +7322,7 @@
}
}
});
- observer.observe(root, { childList: true });
+ observer.observe(root, {childList: true});
}
function createShadowStaticStyleOverrides(root) {
const delayed = root.firstChild === null;
@@ -6987,7 +7335,11 @@
return $cssText.replace(/\${(.+?)}/g, (_, $color) => {
const color = parseColorWithCache($color);
if (color) {
- return modifyColor(color, theme);
+ const lightness = getSRGBLightness(color.r, color.g, color.b);
+ if (lightness > 0.5) {
+ return modifyBackgroundColor(color, theme);
+ }
+ return modifyForegroundColor(color, theme);
}
return $color;
});
@@ -7005,7 +7357,7 @@
.filter((style) => !styleManagers.has(style))
.map((style) => createManager(style));
newManagers
- .map((manager) => manager.details({ secondRound: false }))
+ .map((manager) => manager.details({secondRound: false}))
.filter((detail) => detail && detail.rules.length > 0)
.forEach((detail) => {
variablesStore.addRulesForMatching(detail.rules);
@@ -7047,40 +7399,48 @@
handleAdoptedStyleSheets(document);
variablesStore.matchVariablesAndDependents();
if (isFirefox) {
- const MATCH_VAR = Symbol();
- const onAdoptedCSSChange = (e) => {
- const { node, id, cssRules, entries } = e.detail;
- if (Array.isArray(entries)) {
- entries.forEach((e) => {
- const cssRules = e[2];
- variablesStore.addRulesForMatching(cssRules);
- });
- variablesStore.matchVariablesAndDependents();
- } else if (cssRules) {
- variablesStore.addRulesForMatching(cssRules);
- requestAnimationFrameOnce(MATCH_VAR, () =>
- variablesStore.matchVariablesAndDependents()
- );
+ const onAdoptedCssChange = (e) => {
+ const {sheets} = e.detail;
+ if (!Array.isArray(sheets) || sheets.length === 0) {
+ return;
}
- const tuples = Array.isArray(entries)
- ? entries
- : node && cssRules
- ? [[node, id, cssRules]]
- : [];
- tuples.forEach(([node, id, cssRules]) => {
- adoptedStyleNodeIds.set(node, id);
- const fallback = getAdoptedStyleSheetFallback(node);
- fallback.updateCSS(cssRules);
+ sheets.forEach(({sheet}) => {
+ const {cssRules} = sheet;
+ variablesStore.addRulesForMatching(cssRules);
+ });
+ variablesStore.matchVariablesAndDependents();
+ const response = [];
+ sheets.forEach(({sheetId, sheet}) => {
+ const fallback = getAdoptedStyleSheetFallback(sheet);
+ const cssRules = sheet.cssRules;
+ fallback.render({
+ theme: theme,
+ ignoreImageAnalysis: ignoredImageAnalysisSelectors,
+ cssRules
+ });
+ const commands = fallback.commands();
+ response.push({sheetId, commands});
});
+ requestAnimationFrameOnce(
+ getAdoptedStyleChangeToken(sheets[0].sheet),
+ () => {
+ document.dispatchEvent(
+ new CustomEvent(
+ "__darkreader__adoptedStyleSheetCommands",
+ {detail: JSON.stringify(response)}
+ )
+ );
+ }
+ );
};
document.addEventListener(
"__darkreader__adoptedStyleSheetsChange",
- onAdoptedCSSChange
+ onAdoptedCssChange
);
cleaners.push(() =>
document.removeEventListener(
"__darkreader__adoptedStyleSheetsChange",
- onAdoptedCSSChange
+ onAdoptedCssChange
)
);
document.dispatchEvent(
@@ -7104,7 +7464,7 @@
if (!fallbackStyle.textContent) {
fallbackStyle.textContent = getModifiedFallbackStyle(
theme,
- { strict: false }
+ {strict: false}
);
}
}
@@ -7119,7 +7479,7 @@
}
}
function update() {
- const details = manager.details({ secondRound: true });
+ const details = manager.details({secondRound: true});
if (!details) {
return;
}
@@ -7175,8 +7535,6 @@
}
function handleAdoptedStyleSheets(node) {
if (isFirefox) {
- const fallback = getAdoptedStyleSheetFallback(node);
- fallback.render(theme, ignoredImageAnalysisSelectors);
return;
}
if (canHaveAdoptedStyleSheets(node)) {
@@ -7195,35 +7553,19 @@
});
}
}
- function getAdoptedStyleChangeToken(node) {
- if (adoptedStyleChangeTokens.has(node)) {
- return adoptedStyleChangeTokens.get(node);
+ function getAdoptedStyleChangeToken(sheet) {
+ if (adoptedStyleChangeTokens.has(sheet)) {
+ return adoptedStyleChangeTokens.get(sheet);
}
const token = Symbol();
- adoptedStyleChangeTokens.set(node, token);
+ adoptedStyleChangeTokens.set(sheet, token);
return token;
}
- function getAdoptedStyleSheetFallback(node) {
- let fallback = adoptedStyleFallbacks.get(node);
+ function getAdoptedStyleSheetFallback(sheet) {
+ let fallback = adoptedStyleFallbacks.get(sheet);
if (!fallback) {
- fallback = createAdoptedStyleSheetFallback(() => {
- const token = getAdoptedStyleChangeToken(node);
- requestAnimationFrameOnce(token, () => {
- const id = adoptedStyleNodeIds.get(node);
- const commands = fallback?.commands();
- if (!id || !commands) {
- return;
- }
- const data = { id, commands };
- document.dispatchEvent(
- new CustomEvent(
- "__darkreader__adoptedStyleSheetCommands",
- { detail: JSON.stringify(data) }
- )
- );
- });
- });
- adoptedStyleFallbacks.set(node, fallback);
+ fallback = createAdoptedStyleSheetFallback();
+ adoptedStyleFallbacks.set(sheet, fallback);
}
return fallback;
}
@@ -7231,7 +7573,7 @@
const managedStyles = Array.from(styleManagers.keys());
watchForStyleChanges(
managedStyles,
- ({ created, updated, removed, moved }) => {
+ ({created, updated, removed, moved}) => {
const stylesToRemove = removed;
const stylesToManage = created
.concat(updated)
@@ -7245,7 +7587,7 @@
createManager(style)
);
newManagers
- .map((manager) => manager.details({ secondRound: false }))
+ .map((manager) => manager.details({secondRound: false}))
.filter((detail) => detail && detail.rules.length > 0)
.forEach((detail) => {
variablesStore.addRulesForMatching(detail.rules);
@@ -7318,7 +7660,7 @@
removeDynamicTheme();
}
});
- metaObserver.observe(document.head, { childList: true, subtree: true });
+ metaObserver.observe(document.head, {childList: true, subtree: true});
}
function createDarkReaderInstanceMarker() {
const metaElement = document.createElement("meta");
@@ -7326,10 +7668,10 @@
metaElement.content = INSTANCE_ID;
document.head.appendChild(metaElement);
}
+ function isDRLocked() {
+ return document.querySelector('meta[name="darkreader-lock"]') != null;
+ }
function isAnotherDarkReaderInstanceActive() {
- if (document.querySelector('meta[name="darkreader-lock"]')) {
- return true;
- }
const meta = document.querySelector('meta[name="darkreader"]');
if (meta) {
if (meta.content !== INSTANCE_ID) {
@@ -7342,7 +7684,7 @@
return false;
}
let interceptorAttempts = 2;
- function interceptOldScript({ success, failure }) {
+ function interceptOldScript({success, failure}) {
if (--interceptorAttempts <= 0) {
failure();
return;
@@ -7359,6 +7701,40 @@
success();
});
}
+ function disableConflictingPlugins() {
+ if (document.documentElement.hasAttribute("data-wp-dark-mode-preset")) {
+ const disableWPDarkMode = () => {
+ document.dispatchEvent(
+ new CustomEvent("__darkreader__disableConflictingPlugins")
+ );
+ document.documentElement.classList.remove(
+ "wp-dark-mode-active"
+ );
+ document.documentElement.removeAttribute(
+ "data-wp-dark-mode-active"
+ );
+ };
+ disableWPDarkMode();
+ const observer = new MutationObserver(() => {
+ if (
+ document.documentElement.classList.contains(
+ "wp-dark-mode-active"
+ ) ||
+ document.documentElement.hasAttribute(
+ "data-wp-dark-mode-active"
+ )
+ ) {
+ disableWPDarkMode();
+ }
+ });
+ observer.observe(document.documentElement, {
+ attributes: true,
+ attributeFilter: ["class", "data-wp-dark-mode-active"]
+ });
+ }
+ }
+ let prevTheme = null;
+ let prevFixes = null;
function createOrUpdateDynamicThemeInternal(
themeConfig,
dynamicThemeFixes,
@@ -7366,6 +7742,53 @@
) {
theme = themeConfig;
fixes = dynamicThemeFixes;
+ const colorAffectingKeys = [
+ "brightness",
+ "contrast",
+ "darkSchemeBackgroundColor",
+ "darkSchemeTextColor",
+ "grayscale",
+ "lightSchemeBackgroundColor",
+ "lightSchemeTextColor",
+ "sepia"
+ ];
+ if (prevTheme && prevFixes) {
+ const themeKeys = new Set([
+ ...Object.keys(theme),
+ ...Object.keys(prevTheme)
+ ]);
+ let onlyColorsChanged = true;
+ for (const key of themeKeys) {
+ if (
+ theme[key] !== prevTheme[key] &&
+ !colorAffectingKeys.includes(key)
+ ) {
+ onlyColorsChanged = false;
+ break;
+ }
+ }
+ if (
+ onlyColorsChanged &&
+ JSON.stringify(fixes) !== JSON.stringify(prevFixes)
+ ) {
+ onlyColorsChanged = false;
+ }
+ if (onlyColorsChanged) {
+ const palette = getColorPalette();
+ clearColorPalette();
+ palette.background.forEach((color) =>
+ modifyBackgroundColor(color, theme)
+ );
+ palette.text.forEach((color) =>
+ modifyForegroundColor(color, theme)
+ );
+ palette.border.forEach((color) =>
+ modifyBorderColor(color, theme)
+ );
+ return;
+ }
+ clearColorPalette();
+ }
if (fixes) {
ignoredImageAnalysisSelectors = Array.isArray(
fixes.ignoreImageAnalysis
@@ -7387,6 +7810,7 @@
isIFrame$1 = iframe;
const ready = () => {
const success = () => {
+ disableConflictingPlugins();
document.documentElement.setAttribute(
"data-darkreader-mode",
"dynamic"
@@ -7400,7 +7824,9 @@
const failure = () => {
removeDynamicTheme();
};
- if (isAnotherDarkReaderInstanceActive()) {
+ if (isDRLocked()) {
+ removeNode(document.querySelector(".darkreader--fallback"));
+ } else if (isAnotherDarkReaderInstanceActive()) {
interceptOldScript({
success,
failure
@@ -7427,8 +7853,10 @@
ready();
}
});
- headObserver.observe(document, { childList: true, subtree: true });
+ headObserver.observe(document, {childList: true, subtree: true});
}
+ prevTheme = theme;
+ prevFixes = fixes;
}
function removeProxy() {
document.dispatchEvent(new CustomEvent("__darkreader__cleanUp"));
@@ -7441,15 +7869,20 @@
cleanDynamicThemeCache();
removeNode(document.querySelector(".darkreader--fallback"));
if (document.head) {
+ const selectors = [
+ ".darkreader--user-agent",
+ ".darkreader--text",
+ ".darkreader--invert",
+ ".darkreader--inline",
+ ".darkreader--override",
+ ".darkreader--variables",
+ ".darkreader--root-vars",
+ 'meta[name="darkreader"]'
+ ];
restoreMetaThemeColor();
- removeNode(document.head.querySelector(".darkreader--user-agent"));
- removeNode(document.head.querySelector(".darkreader--text"));
- removeNode(document.head.querySelector(".darkreader--invert"));
- removeNode(document.head.querySelector(".darkreader--inline"));
- removeNode(document.head.querySelector(".darkreader--override"));
- removeNode(document.head.querySelector(".darkreader--variables"));
- removeNode(document.head.querySelector(".darkreader--root-vars"));
- removeNode(document.head.querySelector('meta[name="darkreader"]'));
+ selectors.forEach((selector) =>
+ removeNode(document.head.querySelector(selector))
+ );
removeProxy();
}
shadowRootsWithOverrides.forEach((root) => {
@@ -7477,6 +7910,9 @@
stopWatchingForUpdates();
cleanModificationCache();
clearColorCache();
+ releaseVariablesSheet();
+ prevTheme = null;
+ prevFixes = null;
}
function parseCSS(cssText) {
@@ -7619,12 +8055,12 @@
formatAtRule(rule, indent);
}
}
- function formatAtRule({ type, query, rules }, indent) {
+ function formatAtRule({type, query, rules}, indent) {
lines.push(`${indent}${type} ${query} {`);
rules.forEach((child) => formatRule(child, `${indent}${tab}`));
lines.push(`${indent}}`);
}
- function formatStyleRule({ selectors, declarations }, indent) {
+ function formatStyleRule({selectors, declarations}, indent) {
const lastSelectorIndex = selectors.length - 1;
selectors.forEach((selector, i) => {
lines.push(
@@ -7632,7 +8068,7 @@
);
});
const sorted = sortDeclarations(declarations);
- sorted.forEach(({ property, value, important }) => {
+ sorted.forEach(({property, value, important}) => {
lines.push(
`${indent}${tab}${property}: ${value}${important ? " !important" : ""};`
);
@@ -7748,7 +8184,7 @@ _______|_______/__/ ____ \\__\\__|___\\__\\__|___\\__\\____
}
})();
function enable(themeOptions = {}, fixes = null) {
- const theme = { ...DEFAULT_THEME, ...themeOptions };
+ const theme = {...DEFAULT_THEME, ...themeOptions};
if (theme.engine !== ThemeEngine.dynamicTheme) {
throw new Error("Theme engine is not supported.");
}
@@ -7762,13 +8198,16 @@ _______|_______/__/ ____ \\__\\__|___\\__\\__|___\\__\\____
removeDynamicTheme();
isDarkReaderEnabled = false;
}
- const darkScheme = matchMedia("(prefers-color-scheme: dark)");
+ const darkScheme =
+ typeof matchMedia === "function"
+ ? matchMedia("(prefers-color-scheme: dark)")
+ : undefined;
let store = {
themeOptions: null,
fixes: null
};
function handleColorScheme() {
- if (darkScheme.matches) {
+ if (darkScheme?.matches) {
enable(store.themeOptions, store.fixes);
} else {
disable();
@@ -7776,18 +8215,18 @@ _______|_______/__/ ____ \\__\\__|___\\__\\__|___\\__\\____
}
function auto(themeOptions = {}, fixes = null) {
if (themeOptions) {
- store = { themeOptions, fixes };
+ store = {themeOptions, fixes};
handleColorScheme();
if (isMatchMediaChangeEventListenerSupported) {
- darkScheme.addEventListener("change", handleColorScheme);
+ darkScheme?.addEventListener("change", handleColorScheme);
} else {
- darkScheme.addListener(handleColorScheme);
+ darkScheme?.addListener(handleColorScheme);
}
} else {
if (isMatchMediaChangeEventListenerSupported) {
- darkScheme.removeEventListener("change", handleColorScheme);
+ darkScheme?.removeEventListener("change", handleColorScheme);
} else {
- darkScheme.removeListener(handleColorScheme);
+ darkScheme?.removeListener(handleColorScheme);
}
disable();
}
@@ -7804,5 +8243,5 @@ _______|_______/__/ ____ \\__\\__|___\\__\\__|___\\__\\____
exports.isEnabled = isEnabled;
exports.setFetchMethod = setFetchMethod;
- Object.defineProperty(exports, "__esModule", { value: true });
+ Object.defineProperty(exports, "__esModule", {value: true});
});
\ No newline at end of file