From 25b9d7edb11cc032baf50cfa4ccde6dbbcf32cbc Mon Sep 17 00:00:00 2001 From: ThomazPom Date: Mon, 18 Nov 2024 22:53:03 +0100 Subject: [PATCH] Fixing small regressions on 1.6.x & Reenablig JS core where it performs the best. --- Listeners.js | 508 +++++++++-------- background.js | 1198 +++++++++++++++++++++-------------------- backgroundClass.js | 32 +- contentScriptClass.js | 14 +- uDarkEncoder.js | 18 +- 5 files changed, 888 insertions(+), 882 deletions(-) diff --git a/Listeners.js b/Listeners.js index e96a656..c4839c0 100644 --- a/Listeners.js +++ b/Listeners.js @@ -1,287 +1,283 @@ class Listeners { - static editOnHeadersImage(details) { - // Util 2024 jan 02 we were checking details.documentUrl, or details.url to know if a stylesheet was loaded in a excluded page - // Since only CS ports that matches blaclist and whitelist are connected, we can simply check if this resource has a corresponding CS port - if (!uDark.getPort(details)) { - // console.log("Image","No port found for",details.url,"loaded by webpage:",details.originUrl,"Assuming it is not an eligible webpage, or even blocked by another extension"); - return {} - } - - let imageURLObject = new URL(details.url); - details.headersLow = {} - - uDark.extractCharsetFromHeaders(details, "image/png",); - details.isSVGImage = details.contentType.includes("image/svg"); - - // Determine if the image deserves to be edited - if (imageURLObject.pathname.startsWith("/favicon.ico") || imageURLObject.hash.endsWith("#ud_favicon")) { - return {}; + static editOnHeadersImage(details) { + // Util 2024 jan 02 we were checking details.documentUrl, or details.url to know if a stylesheet was loaded in a excluded page + // Since only CS ports that matches blaclist and whitelist are connected, we can simply check if this resource has a corresponding CS port + if (!uDark.getPort(details)) { + // uDark.log("Image","No port found for",details.url,"loaded by webpage:",details.originUrl,"Assuming it is not an eligible webpage, or even blocked by another extension"); + return {} + } + + let imageURLObject = new URL(details.url); + details.headersLow = {} + + uDark.extractCharsetFromHeaders(details, "image/png",); + details.isSVGImage = details.contentType.includes("image/svg"); + + // Determine if the image deserves to be edited + if (imageURLObject.pathname.startsWith("/favicon.ico") || imageURLObject.hash.endsWith("#ud_favicon")) { + return {}; + } + + let filter = globalThis.browser.webRequest.filterResponseData(details.requestId); // After this instruction, browser espect us to write data to the filter and close it + let imageWorker; + let secureTimeout = setTimeout(() => { + try { + filter.disconnect(); + imageWorker && imageWorker.terminate(); + } catch (e) {} + }, 30000) // Take care of very big images + details.buffers = details.buffers || []; + if (details.isSVGImage) { + filter.ondata = event => details.buffers.push(event.data); + let svgURLObject = new URL(details.url); + { // Sometimes the website reencodes as html chars the data + let HTMLDecoderOption = new Option(); + HTMLDecoderOption.p_ud_innerHTML = svgURLObject.hash; + svgURLObject.hash = HTMLDecoderOption.textContent; } - - let filter = globalThis.browser.webRequest.filterResponseData(details.requestId); // After this instruction, browser espect us to write data to the filter and close it - let imageWorker; - let secureTimeout = setTimeout(() => { - try { - filter.disconnect(); - imageWorker && imageWorker.terminate(); - } catch (e) {} - }, 30000) // Take care of very big images - details.buffers = details.buffers || []; - if (details.isSVGImage) { - filter.ondata = event => details.buffers.push(event.data); - let svgURLObject = new URL(details.url); - { // Sometimes the website reencodes as html chars the data - let HTMLDecoderOption = new Option(); - HTMLDecoderOption.p_ud_innerHTML = svgURLObject.hash; - svgURLObject.hash = HTMLDecoderOption.textContent; - } - let complementIndex = svgURLObject.hash.indexOf(uDark.imageSrcInfoMarker); - let notableInfos = new URLSearchParams(complementIndex == -1 ? "" : svgURLObject.hash.slice(complementIndex + 6)) - notableInfos = Object.fromEntries(notableInfos.entries()); - notableInfos.remoteSVG = true; - filter.onstop = event => { - new Blob(details.buffers).arrayBuffer().then((buffer) => { - let svgString = uDarkDecode(details.charset,buffer, { - stream: true - }) - - let svgStringEdited = uDark.frontEditHTML(false, svgString, details, { - notableInfos, - svgImage: true, - remoteSVG: true, - remoteSVGURL: svgURLObject.href, - }); - filter.write(uDarkEncode(details.charset,svgStringEdited)); - filter.disconnect(); - clearInterval(secureTimeout); - }); - } - } else { - imageWorker = new uDark.LoggingWorker(uDark.imageWorkerJsFile); - imageWorker.addEventListener("message", event => { - if (event.data.editionComplete) { - for (let buffer of event.data.buffers) { - try { - filter.write(buffer); - } catch (e) { - console.log("Error", e.message) - } - } - filter.disconnect(); - imageWorker.terminate(); - clearInterval(secureTimeout); - } - }) - filter.ondata = event => { - imageWorker.postMessage({ - oneImageBuffer: event.data - }, [event.data]) // Explicityly transfer the ArrayBuffer to the worker + let complementIndex = svgURLObject.hash.indexOf(uDark.imageSrcInfoMarker); + let notableInfos = new URLSearchParams(complementIndex == -1 ? "" : svgURLObject.hash.slice(complementIndex + 6)) + notableInfos = Object.fromEntries(notableInfos.entries()); + notableInfos.remoteSVG = true; + filter.onstop = event => { + new Blob(details.buffers).arrayBuffer().then((buffer) => { + let svgString = uDarkDecode(details.charset,buffer, { + stream: true + }) - } - filter.onstop = event => { - imageWorker.postMessage({ - filterStopped: 1, - details + let svgStringEdited = uDark.frontEditHTML(false, svgString, details, { + notableInfos, + svgImage: true, + remoteSVG: true, + remoteSVGURL: svgURLObject.href, }); - } - } - // Here we catch any image, including data:images <3 ( in the form of https://data-image/data:image/png;base64,....) - let resultEdit = {} - - // If resultEdit is a promise, image will be edited (foreground or background), otherwise it may be a big background image to include under text - // Lets inform the content script about it - if (uDark.enable_registering_background_images && (!resultEdit.then || !resultEdit.edited)) { - // uDark.registerBackgroundItem(false,{selectorText:`img[src='${details.url}']`},details); - let imageURLObject = new URL(details.url); - if (imageURLObject.searchParams.has("uDark_cssClass")) { - let cssClass = decodeURIComponent(imageURLObject.searchParams.get("uDark_cssClass")); - // console.log("Found a background image via property",cssClass); - uDark.registerBackgroundItem(false, { - selectorText: cssClass - }, details); - imageURLObject.searchParams.delete("uDark_cssClass"); - imageURLObject.searchParams.set("c", uDark.fixedRandom); - return { - redirectUrl: imageURLObject.href - }; - } else if (!imageURLObject.searchParams.has("c")) { - // console.log("Found an img element",details.url) - // console.log(details.url,"is not a background image, but an img element",details) - uDark.registerBackgroundItem(false, { - selectorText: `img[src='${details.url}']` - }, details); - } + filter.write(uDarkEncode(details.charset,svgStringEdited)); + filter.disconnect(); + clearInterval(secureTimeout); + }); } - return resultEdit; - - } - static async editBeforeRequestImage(details) { - if (details.url.startsWith("https://data-image/?base64IMG=")) { - // console.log(details); - // console.log("PASSING",(globalThis["passing"+details.url+details.requestId]=(globalThis["passing"+details.url+details.requestId]||0)+1),details); - const dataUrl = details.url.slice(30); - // console.log("PASSING",(globalThis["passing"+details.url+details.requestId]=(globalThis["passing"+details.url+details.requestId]||0)+1),details); - - const arrayBuffer = await (await fetch(dataUrl)).arrayBuffer(); - const reader = new FileReader() // Faster but ad what cost later ? - - const imageWorker = new uDark.LoggingWorker(uDark.imageWorkerJsFile); - - imageWorker.addEventListener("message", event => { - if (event.data.editionComplete) { - // console.log("PASSING",(globalThis["passing"+details.url+details.requestId]=(globalThis["passing"+details.url+details.requestId]||0)+1),details); - - reader.readAsDataURL(new Blob(event.data.buffers)); + } else { + imageWorker = new uDark.LoggingWorker(uDark.imageWorkerJsFile); + imageWorker.addEventListener("message", event => { + if (event.data.editionComplete) { + for (let buffer of event.data.buffers) { + try { + filter.write(buffer); + } catch (e) { + uDark.error(e.message) + } } - }) + filter.disconnect(); + imageWorker.terminate(); + clearInterval(secureTimeout); + } + }) + filter.ondata = event => { + imageWorker.postMessage({ + oneImageBuffer: event.data + }, [event.data]) // Explicityly transfer the ArrayBuffer to the worker + } + filter.onstop = event => { imageWorker.postMessage({ - oneImageBuffer: arrayBuffer, filterStopped: 1, - details: details - }, [arrayBuffer]) // Explicityly transfer the ArrayBuffer to the worker - - let to_return = new Promise(resolve => reader.onload = (e) => resolve({ - redirectUrl: reader.result - })); - - to_return.then(x => imageWorker.terminate()); // Very needed : non terminated workers will avoid new workers to reveive messages - - // console.log("PASSING",(globalThis["passing"+details.url+details.requestId]=(globalThis["passing"+details.url+details.requestId]||0)+1),details); - - return to_return; + details + }); } } - static editBeforeRequestStyleSheet_sync(details) { - let options = {}; - - // console.log("Loading CSS", details.url, details.requestId, details.fromCache) - - // Util 2024 jan 02 we were checking details.documentUrl, or details.url to know if a stylesheet was loaded in a excluded page - // Since only CS ports that matches blaclist and whitelist are connected, we can simply check if this resource has a corresponding CS port - if (!uDark.getPort(details)) { - // console.log("CSS", "No port found for", details.url, "loaded by webpage:", details.originUrl, "Assuming it is not an eligible webpage, or even blocked by another extension"); - // console.log("If i'm lacking of knowledge, here is what i know about this request", details.tabId, details.frameId); - return {} + // Here we catch any image, including data:images <3 ( in the form of https://data-image/data:image/png;base64,....) + let resultEdit = {} + + // If resultEdit is a promise, image will be edited (foreground or background), otherwise it may be a big background image to include under text + // Lets inform the content script about it + if (uDark.enable_registering_background_images && (!resultEdit.then || !resultEdit.edited)) { + // uDark.registerBackgroundItem(false,{selectorText:`img[src='${details.url}']`},details); + let imageURLObject = new URL(details.url); + if (imageURLObject.searchParams.has("uDark_cssClass")) { + let cssClass = decodeURIComponent(imageURLObject.searchParams.get("uDark_cssClass")); + // console.log("Found a background image via property",cssClass); + uDark.registerBackgroundItem(false, { + selectorText: cssClass + }, details); + imageURLObject.searchParams.delete("uDark_cssClass"); + imageURLObject.searchParams.set("c", uDark.fixedRandom); + return { + redirectUrl: imageURLObject.href + }; + } else if (!imageURLObject.searchParams.has("c")) { + // uDark.log("Found an img element",details.url) + // uDark.log(details.url,"is not a background image, but an img element",details) + uDark.registerBackgroundItem(false, { + selectorText: `img[src='${details.url}']` + }, details); } + } + return resultEdit; + + } + static async editBeforeRequestImage(details) { + if (details.url.startsWith("https://data-image/?base64IMG=")) { + const dataUrl = details.url.slice(30); - uDark.extractCharsetFromHeaders(details, "text/css"); + const arrayBuffer = await (await fetch(dataUrl)).arrayBuffer(); + const reader = new FileReader() // Faster but ad what cost later ? - let filter = globalThis.browser.webRequest.filterResponseData(details.requestId); // After this instruction, browser espect us to write data to the filter and close it + const imageWorker = new uDark.LoggingWorker(uDark.imageWorkerJsFile); - details.dataCount = 0; - details.rejectedValues = ""; + imageWorker.addEventListener("message", event => { + if (event.data.editionComplete) { + + reader.readAsDataURL(new Blob(event.data.buffers)); + } + }) + imageWorker.postMessage({ + oneImageBuffer: arrayBuffer, + filterStopped: 1, + details: details + }, [arrayBuffer]) // Explicityly transfer the ArrayBuffer to the worker + let to_return = new Promise(resolve => reader.onload = (e) => resolve({ + redirectUrl: reader.result + })); - // // ondata event handler - filter.ondata = event => { - details.dataCount++; - uDark.handleCSSChunk_sync(event.data, true, details, filter); - }; + to_return.then(x => imageWorker.terminate()); // Very needed : non terminated workers will avoid new workers to reveive messages - // onstop event handler - filter.onstop = event => { - if (details.rejectedValues.length > 0) { - uDark.handleCSSChunk_sync(null, false, details, filter); - } - filter.disconnect(); // Ensure disconnection after completion - }; - // return {redirectUrl:details.url}; - // return {responseHeaders:[{name:"Vary",value:"*"},{name:"Location",value:details.url}]}; - return {}; - // must not return this closes filter// + // console.log("PASSING",(globalThis["passing"+details.url+details.requestId]=(globalThis["passing"+details.url+details.requestId]||0)+1),details); + + return to_return; + } + } + static editBeforeRequestStyleSheet_sync(details) { + let options = {}; + + // console.log("Loading CSS", details.url, details.requestId, details.fromCache) + + // Util 2024 jan 02 we were checking details.documentUrl, or details.url to know if a stylesheet was loaded in a excluded page + // Since only CS ports that matches blaclist and whitelist are connected, we can simply check if this resource has a corresponding CS port + if (!uDark.getPort(details)) { + // console.log("CSS", "No port found for", details.url, "loaded by webpage:", details.originUrl, "Assuming it is not an eligible webpage, or even blocked by another extension"); + // console.log("If i'm lacking of knowledge, here is what i know about this request", details.tabId, details.frameId); + return {} } + + uDark.extractCharsetFromHeaders(details, "text/css"); + + let filter = globalThis.browser.webRequest.filterResponseData(details.requestId); // After this instruction, browser espect us to write data to the filter and close it + + details.dataCount = 0; + details.rejectedValues = ""; + + + + // // ondata event handler + filter.ondata = event => { + details.dataCount++; + uDark.handleCSSChunk_sync(event.data, true, details, filter); + }; + + // onstop event handler + filter.onstop = event => { + if (details.rejectedValues.length > 0) { + uDark.handleCSSChunk_sync(null, false, details, filter); + } + filter.disconnect(); // Ensure disconnection after completion + }; + // return {redirectUrl:details.url}; + // return {responseHeaders:[{name:"Vary",value:"*"},{name:"Location",value:details.url}]}; + return {}; + // must not return this closes filter// + } - static setEligibleRequestBeforeData(details){ - details.unEligibleRequest=(details.documentUrl || details.url).match(uDark.userSettings.exclude_regex); - details.eligibleRequest=!details.unEligibleRequest; - // Here we have to check the url or the documentUrl to know if this webpage is excluded - // It already has passed the whitelist check, this is why we only check the blacklist - // However this code executes before the content script is connected, so we can't check if it will connect or not - // Even if we could do this, like sending some bytes and waiting for he content script to connect, - // and it would be not so musch costly in terms of time, some pages as YouTube as the time i write this, somehow manages - // to send in this very first request tabID -1 and frameID 0, which is not a valid combination, and the content script will never be found - // stackoverflow says it might be related to worker threads. It's probably true with serviceWorkers + static setEligibleRequestBeforeData(details){ + details.unEligibleRequest=(details.documentUrl || details.url).match(uDark.userSettings.exclude_regex); + details.eligibleRequest=!details.unEligibleRequest; + // Here we have to check the url or the documentUrl to know if this webpage is excluded + // It already has passed the whitelist check, this is why we only check the blacklist + // However this code executes before the content script is connected, so we can't check if it will connect or not + // Even if we could do this, like sending some bytes and waiting for he content script to connect, + // and it would be not so musch costly in terms of time, some pages as YouTube as the time i write this, somehow manages + // to send in this very first request tabID -1 and frameID 0, which is not a valid combination, and the content script will never be found + // stackoverflow says it might be related to worker threads. It's probably true with serviceWorkers + + // console.log("Will check",details.url,"made by",details.documentUrl || details.url,0) + // console.log("Is eligible for uDark",details.eligibleRequest) + if (details.unEligibleRequest) { - // console.log("Will check",details.url,"made by",details.documentUrl || details.url,0) - // console.log("Is eligible for uDark",details.eligibleRequest) - if (details.unEligibleRequest) { - - uDark.deletePort(details) - // As bellow is marking as arriving soon - // It is possible to have a page that starts loading, we mark it as arriving soon - // loading stops, for whatever reason, and the content script does not connect and therefore does not disconnects and get not deleted. - // In this case, the port will not be erased, and all resources will darkened, even if the page is not eligible for uDark - // It is testable by disablising the content script, assignation and line above; loading a darkened page, in a tab, to set the arriving soon flag, - // then loading an uneligible page in the same tab, and see if it not dakening. - // A simple delete when the page is not eligible is enough and very low cost. - // In the end we need to be in this if to avoid darkening the page, we wont be lazy and delete the port. - return; - - } - if (details.tabId != -1) { - // Lets be the MVP here, sometimes the content script is not connected yet, and the CSS will arrive in few milliseconds. - // This page is eligible for uDark - // console.log("I'm telling the world that",details.url,"is eligible for uDark", "on", details.tabId,details.frameId) - // This code must absolutely eb executed before the parsing of headers of the page since the page can have link header wich will be considered as a tag - // See https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Link for details - uDark.setPort(details,{arrivingSoon:true},0); - - } + uDark.deletePort(details) + // As bellow is marking as arriving soon + // It is possible to have a page that starts loading, we mark it as arriving soon + // loading stops, for whatever reason, and the content script does not connect and therefore does not disconnects and get not deleted. + // In this case, the port will not be erased, and all resources will darkened, even if the page is not eligible for uDark + // It is testable by disablising the content script, assignation and line above; loading a darkened page, in a tab, to set the arriving soon flag, + // then loading an uneligible page in the same tab, and see if it not dakening. + // A simple delete when the page is not eligible is enough and very low cost. + // In the end we need to be in this if to avoid darkening the page, we wont be lazy and delete the port. + return; } - static async editBeforeData(details) { - if (details.tabId == -1 && uDark.connected_options_ports_count || uDark.connected_cs_ports["port-from-popup-" + details.tabId]) { // -1 Happens sometimes, like on https://www.youtube.com/ at the time i write this, stackoverflow talks about worker threads - - // Here we are covering the needs of the option page: Be able to frame any page - let removeHeaders = ["content-security-policy", "x-frame-options", "content-security-policy-report-only"] - details.responseHeaders = details.responseHeaders.filter(x => !removeHeaders.includes(x.name.toLowerCase())) - } - if(!uDark.getPort(details)){ // If setEligibleRequestBeforeData removed the port, we don't want to darken the page - return {responseHeaders:details.responseHeaders}; - } + if (details.tabId != -1) { + // Lets be the MVP here, sometimes the content script is not connected yet, and the CSS will arrive in few milliseconds. + // This page is eligible for uDark + // console.log("I'm telling the world that",details.url,"is eligible for uDark", "on", details.tabId,details.frameId) + // This code must absolutely eb executed before the parsing of headers of the page since the page can have link header wich will be considered as a tag + // See https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Link for details + uDark.setPort(details,{arrivingSoon:true},0); - uDark.extractCharsetFromHeaders(details); + } + + } + static async editBeforeData(details) { + if (details.tabId == -1 && uDark.connected_options_ports_count || uDark.connected_cs_ports["port-from-popup-" + details.tabId]) { // -1 Happens sometimes, like on https://www.youtube.com/ at the time i write this, stackoverflow talks about worker threads - if(!details.contentType.includes("text/html")){ - return {responseHeaders:details.responseHeaders}; - } - details.responseHeaders = details.responseHeaders.filter(x => { - var a_filter = uDark.headersDo[x.name.toLowerCase()]; - return a_filter ? a_filter(x) : true; - }) - // console.log("Editing", details.url, details.requestId, details.fromCache) - let filter = globalThis.browser.webRequest.filterResponseData(details.requestId); + // Here we are covering the needs of the option page: Be able to frame any page + let removeHeaders = ["content-security-policy", "x-frame-options", "content-security-policy-report-only"] + details.responseHeaders = details.responseHeaders.filter(x => !removeHeaders.includes(x.name.toLowerCase())) + } + if(!uDark.getPort(details)){ // If setEligibleRequestBeforeData removed the port, we don't want to darken the page + return {responseHeaders:details.responseHeaders}; + } + + uDark.extractCharsetFromHeaders(details); + + if(!details.contentType.includes("text/html")){ + return {responseHeaders:details.responseHeaders}; + } + details.responseHeaders = details.responseHeaders.filter(x => { + var a_filter = uDark.headersDo[x.name.toLowerCase()]; + return a_filter ? a_filter(x) : true; + }) + // console.log("Editing", details.url, details.requestId, details.fromCache) + let filter = globalThis.browser.webRequest.filterResponseData(details.requestId); + + details.dataCount = 0; + details.writeEnd = []; + filter.ondata = event => { + details.dataCount++ + details.writeEnd.push(event.data); - details.dataCount = 0; - details.writeEnd = []; - filter.ondata = event => { - details.dataCount++ - details.writeEnd.push(event.data); - + } + filter.onstop = async event => { + + // Note the headers are already returned since a long time, so we can't edit them here. Fortunately we don't need to, and if we realy need.. use http equiv. + details.dataCount = 1; + details.writeEnd = await new Blob(details.writeEnd).arrayBuffer(); + + + let decodedValue= uDarkDecode(details.charset,details.writeEnd,{stream:true}); + if(details.debugParsing){ // debug + details.writeEnd = decodedValue } - filter.onstop = async event => { - - // Note the headers are already returned since a long time, so we can't edit them here. Fortunately we don't need to, and if we realy need.. use http equiv. - details.dataCount = 1; - details.writeEnd = await new Blob(details.writeEnd).arrayBuffer(); - - - let decodedValue= uDarkDecode(details.charset,details.writeEnd,{stream:true}); - if(details.debugParsing){ // debug - details.writeEnd = decodedValue - } - else - { - details.writeEnd = uDark.parseAndEditHtmlContentBackend4(decodedValue, details) - } - - filter.write(uDarkEncode(details.charset,details.writeEnd)); - - filter.disconnect(); // Low perf if not disconnected ! + else + { + details.writeEnd = uDark.parseAndEditHtmlContentBackend4(decodedValue, details) } - return {responseHeaders:details.responseHeaders} + + filter.write(uDarkEncode(details.charset,details.writeEnd)); + + filter.disconnect(); // Low perf if not disconnected ! } - } \ No newline at end of file + return {responseHeaders:details.responseHeaders} + } +} \ No newline at end of file diff --git a/background.js b/background.js index f97db1e..e692d1e 100644 --- a/background.js +++ b/background.js @@ -1,6 +1,6 @@ class Common { static appCompat(res) { - + if (uDark.browserInfo.version < 105 && uDark.browserInfo.name == "Firefox") { res.disable_image_edition = true; console.warn("UltimaDark", "Image edition is disabled on Firefox versions below 105, as it is not supported"); @@ -8,12 +8,34 @@ class Common { } } static install() { - + } }; class uDarkC extends uDarkExtended { + logPrefix = "UltimaDark:"; + log(...args) { + console.log("%c"+this.logPrefix, "color:white;font-weight:bolder",...args); + }; + warn(...args) { + console.warn("%c"+this.logPrefix, "color:yellow;font-weight:bolder",...args); + } + error(text,...args) { + console.error("%c"+this.logPrefix, "color:red;font-weight:bolder",...args,new Error(text)); + } + info(...args) { + console.info("%c"+this.logPrefix, "color:lightblue;font-weight:bolder",...args); + } + + success(...args) { + console.log("%c"+this.logPrefix, "color:lightgreen;font-weight:bolder",...args); + } + keypoint(...args) { + console.info("%c"+this.logPrefix, "color:lime;font-weight:bolder;font-size:14px",...args); + } - static CSS_COLOR_NAMES = ["currentcolor", "AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", "BlueViolet", "Brown", "BurlyWood", "CadetBlue", "Chartreuse", "Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "Cyan", "DarkBlue", "DarkCyan", "DarkGoldenRod", "DarkGray", "DarkGrey", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", "DarkOrange", "DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen", "DarkSlateBlue", "DarkSlateGray", "DarkSlateGrey", "DarkTurquoise", "DarkViolet", "DeepPink", "DeepSkyBlue", "DimGray", "DimGrey", "DodgerBlue", "FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro", "GhostWhite", "Gold", "GoldenRod", "Gray", "Grey", "Green", "GreenYellow", "HoneyDew", "HotPink", "IndianRed", "Indigo", "Ivory", "Khaki", "Lavender", "LavenderBlush", "LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan", "LightGoldenRodYellow", "LightGray", "LightGrey", "LightGreen", "LightPink", "LightSalmon", "LightSeaGreen", "LightSkyBlue", "LightSlateGray", "LightSlateGrey", "LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "Magenta", "Maroon", "MediumAquaMarine", "MediumBlue", "MediumOrchid", "MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise", "MediumVioletRed", "MidnightBlue", "MintCream", "MistyRose", "Moccasin", "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab", "Orange", "OrangeRed", "Orchid", "PaleGoldenRod", "PaleGreen", "PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff", "Peru", "Pink", "Plum", "PowderBlue", "Purple", "RebeccaPurple", "Red", "RosyBrown", "RoyalBlue", "SaddleBrown", "Salmon", "SandyBrown", "SeaGreen", "SeaShell", "Sienna", "Silver", "SkyBlue", "SlateBlue", "SlateGray", "SlateGrey", "Snow", "SpringGreen", "SteelBlue", "Tan", "Teal", "Thistle", "Tomato", "Turquoise", "Violet", "Wheat", "White", "WhiteSmoke", "Yellow", "YellowGreen"] + static CSS_COLOR_NAMES = [ + //"currentcolor", + "AliceBlue", "AntiqueWhite", "Aqua", "Aquamarine", "Azure", "Beige", "Bisque", "Black", "BlanchedAlmond", "Blue", "BlueViolet", "Brown", "BurlyWood", "CadetBlue", "Chartreuse", "Chocolate", "Coral", "CornflowerBlue", "Cornsilk", "Crimson", "Cyan", "DarkBlue", "DarkCyan", "DarkGoldenRod", "DarkGray", "DarkGrey", "DarkGreen", "DarkKhaki", "DarkMagenta", "DarkOliveGreen", "DarkOrange", "DarkOrchid", "DarkRed", "DarkSalmon", "DarkSeaGreen", "DarkSlateBlue", "DarkSlateGray", "DarkSlateGrey", "DarkTurquoise", "DarkViolet", "DeepPink", "DeepSkyBlue", "DimGray", "DimGrey", "DodgerBlue", "FireBrick", "FloralWhite", "ForestGreen", "Fuchsia", "Gainsboro", "GhostWhite", "Gold", "GoldenRod", "Gray", "Grey", "Green", "GreenYellow", "HoneyDew", "HotPink", "IndianRed", "Indigo", "Ivory", "Khaki", "Lavender", "LavenderBlush", "LawnGreen", "LemonChiffon", "LightBlue", "LightCoral", "LightCyan", "LightGoldenRodYellow", "LightGray", "LightGrey", "LightGreen", "LightPink", "LightSalmon", "LightSeaGreen", "LightSkyBlue", "LightSlateGray", "LightSlateGrey", "LightSteelBlue", "LightYellow", "Lime", "LimeGreen", "Linen", "Magenta", "Maroon", "MediumAquaMarine", "MediumBlue", "MediumOrchid", "MediumPurple", "MediumSeaGreen", "MediumSlateBlue", "MediumSpringGreen", "MediumTurquoise", "MediumVioletRed", "MidnightBlue", "MintCream", "MistyRose", "Moccasin", "NavajoWhite", "Navy", "OldLace", "Olive", "OliveDrab", "Orange", "OrangeRed", "Orchid", "PaleGoldenRod", "PaleGreen", "PaleTurquoise", "PaleVioletRed", "PapayaWhip", "PeachPuff", "Peru", "Pink", "Plum", "PowderBlue", "Purple", "RebeccaPurple", "Red", "RosyBrown", "RoyalBlue", "SaddleBrown", "Salmon", "SandyBrown", "SeaGreen", "SeaShell", "Sienna", "Silver", "SkyBlue", "SlateBlue", "SlateGray", "SlateGrey", "Snow", "SpringGreen", "SteelBlue", "Tan", "Teal", "Thistle", "Tomato", "Turquoise", "Violet", "Wheat", "White", "WhiteSmoke", "Yellow", "YellowGreen"] static SHORTHANDS = ["all", "animation", "animation-range", "background", "border", "border-block", "border-block-end", "border-block-start", "border-bottom", "border-color", "border-image", "border-inline", "border-inline-end", "border-inline-start", "border-left", "border-radius", "border-right", "border-style", "border-top", "border-width", "column-rule", "columns", "contain-intrinsic-size", "container", "flex", "flex-flow", "font", "font-synthesis", "font-variant", "gap", "grid", "grid-area", "grid-column", "grid-row", "grid-template", "inset", "inset-block", "inset-inline", "list-style", "margin", "margin-block", "margin-inline", "mask", "mask-border", "offset", "outline", "overflow", "overscroll-behavior", "padding", "padding-block", "padding-inline", "place-content", "place-items", "place-self", "position-try", "scroll-margin", "scroll-margin-block", "scroll-margin-inline", "scroll-padding", "scroll-padding-block", "scroll-padding-inline", "scroll-timeline", "text-decoration", "text-emphasis", "text-wrap", "transition"] static TAGS_TO_PROTECT = ["head", "html", "body", "frameset", "frame"] static CSS_COLOR_FUNCTIONS = ["rgb", "rgba", "hsl", "hsla", "hwb", "lab", "lch", "color", "color-mix", "oklch", "oklab"] @@ -21,7 +43,7 @@ class uDarkC extends uDarkExtended { tagsToProtectRegex = new RegExp(`(? { @@ -44,24 +66,24 @@ class uDarkC extends uDarkExtended { "text": "color", "bgcolor": this.rgba } - + colorRegex = new RegExp(`(? { - + let value = cssStyle.getPropertyValue("fill"); this.edit_all_cssRule_colors_cb(cssRule, "color", value, options, { key_prefix:"--ud-fg--fill-", l_var: "--uDark_transform_lighten", fastValue0:true }) - + }] }, "mask-image": { @@ -147,7 +169,7 @@ class uDarkC extends uDarkExtended { }, callBacks: [this.edit_css_urls] }, - + // Not good for wayback machine time selector // "color":{ stickConcatToPropery: {sValue:"(",rKey:"mix-blend-mode", stick:"difference"}}, // Not good for wayback machine time selector // "position":{ stickConcatToPropery: {sValue:"fixed",rKey:"filter", stick:"contrast(110%)"}}, // Not good for wayback machine time selector @@ -167,7 +189,7 @@ class uDarkC extends uDarkExtended { } uDark.edit_cssRules(cssStyleSheet.cssRules, details, options); } - + str_protect(str, regexSearch, protectWith) { // sore values into an array: var values = str.match(regexSearch); @@ -217,7 +239,7 @@ class uDarkC extends uDarkExtended { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#specifying_a_string_as_the_replacement // This is why we use a function to replace the protected value // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#specifying_a_function_as_the_replacement - + str = str.replace(protection.protectWith.replace("{index}", index), () => value); }) } @@ -226,7 +248,7 @@ class uDarkC extends uDarkExtended { sRGBtoLin(colorChannel) { // Send this function a decimal sRGB gamma encoded color value // between 0.0 and 1.0, and it returns a linearized value. - + if (colorChannel <= 0.04045) { return colorChannel / 12.92; } else { @@ -242,7 +264,7 @@ class uDarkC extends uDarkExtended { YtoLstar(Y) { // Send this function a luminance value between 0.0 and 1.0, // and it returns L* which is "perceptual lightness" - + if (Y <= (216 / 24389)) { // The CIE standard states 0.008856 but 216/24389 is the intent for 0.008856451679036 return Y * (24389 / 27); // The CIE standard states 903.3, but 24389/27 is the intent, making 903.296296296296296 } else { @@ -250,7 +272,7 @@ class uDarkC extends uDarkExtended { } } search_container_logo(element, notableInfos) { - + let parent = (element.parentNode || element) parent = (parent.parentNode || parent) return uDark.logo_match.test(parent.outerHTML + notableInfos.uDark_cssClass) @@ -262,12 +284,12 @@ class uDarkC extends uDarkExtended { { // Do not parse url preventing adding context to it or interpreting it as a relative url or correcting its content by any way let imageTrueSrc = src_override || image.getAttribute("src") - + if (uDark.userSettings.disable_image_edition || !imageTrueSrc) { - + return imageTrueSrc; } - + if (!image.hasAttribute("data-ud-selector")) { image.setAttribute("data-ud-selector", Math.random()); } @@ -296,29 +318,27 @@ class uDarkC extends uDarkExtended { notableInfos, image }); - + return imageTrueSrc + usedChar + new URLSearchParams(notableInfos).toString(); } valuePrototypeEditor(leType, atName, setter = false, conditon = false, aftermath = false, getter = false) { + + uDark.info("Editing property :", leType, atName) - console.log("Editing property :", leType, atName) - // if (conditon) { - // console.log("VAdding condtition to", leType, leType.name, conditon, conditon.toString()) - // } if (leType.concat) { return leType.forEach(aType => uDark.valuePrototypeEditor(aType, atName, setter, conditon, aftermath, getter)) } - + if (leType.wrappedJSObject) { // Cross compatibilty with content script leType = leType.wrappedJSObject; } - + var originalProperty = Object.getOwnPropertyDescriptor(leType.prototype, atName); if (!originalProperty) { console.error("No existing property for '", atName, "'", leType, leType.name, leType.prototype) return; } - + Object.defineProperty(leType.prototype, "o_ud_" + atName, originalProperty); let override_get_set = {}; if (setter) { @@ -331,7 +351,6 @@ class uDarkC extends uDarkExtended { } if (getter) { override_get_set.get = exportFunction(function() { // getters must be exported like regular functions - // console.log("Getting", this, atName) let call_result = originalProperty.get.call(this); return getter(this, call_result); }, window); @@ -339,9 +358,9 @@ class uDarkC extends uDarkExtended { // uDark.general_cache["o_ud_"+atName]=originalSet Object.defineProperty(leType.prototype, atName, override_get_set); } - + functionWrapper(leType, laFonction, fName, watcher = x => x, conditon = true, result_editor = x => x) { - console.log("Wrapping function :", leType, laFonction, fName) + uDark.info("Wrapping function :", leType, laFonction, fName) let originalFunction = leType.prototype["o_ud_wrap_" + fName] = laFonction; leType.prototype[fName] = function(...args) { if (conditon === true || conditon(this, arguments) === true) { @@ -354,7 +373,6 @@ class uDarkC extends uDarkExtended { } } functionPrototypeEditor(leType, laFonction, watcher = x => x, conditon = x => x, result_editor = x => x) { - // console.log(leType,leType.name,leType.prototype,laFonction,laFonction.name) if (laFonction.concat) { return laFonction.forEach(aFonction => { uDark.functionPrototypeEditor(leType, aFonction, watcher, conditon, result_editor) @@ -363,13 +381,10 @@ class uDarkC extends uDarkExtended { if (leType.wrappedJSObject) { // Cross compatibilty with content script leType = leType.wrappedJSObject; } - console.log("Editing function :", leType, laFonction) - // if (conditon) { - // console.log("Adding condtition to", leType, leType.name, laFonction, conditon, conditon.toString()) - // } + uDark.info("Editing function :", leType, laFonction) leType.prototype.count = (leType.prototype.count || 0) + 1; if (!Object.getOwnPropertyDescriptor(leType.prototype, laFonction.name)) { - console.log("No getter for '", leType, laFonction, new Error(), leType.prototype.count, document.location.href) + uDark.error("No getter for '", leType, laFonction, new Error(), leType.prototype.count, document.location.href) return; } let originalFunctionKey = "o_ud_" + laFonction.name @@ -382,9 +397,7 @@ class uDarkC extends uDarkExtended { value: { [laFonction.name]: exportFunction(function() { if (conditon === true || conditon.apply(this, arguments)) { // if a standard function is provided, it will will be able to use the 'this' keyword - // console.log("Setting",leType,laFonction,this,arguments[0],watcher(this, arguments)[0]) let watcher_result = watcher(this, arguments); - // console.log("watcher_result", this,originalFunction,watcher_result,originalFunctionKey,leType.prototype[originalFunctionKey],this[originalFunctionKey],this.getP); let result = originalFunction.apply(this, watcher_result) // if a standard function is provided, it will will be able to use the 'this' keyword return result_editor(result, this, arguments, watcher_result); } else { @@ -394,25 +407,25 @@ class uDarkC extends uDarkExtended { } [laFonction.name] }); } - + edit_str_restore_imports_all_way(str, rules) { // This regexp seems a bit complex // because @import url("") can includes ";" which is also the css instruction separator like in following example // @charset "UTF-8";@import url("https://use.typekit.net/lls1fmf.css"); // @import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap"); // .primary-1{ color: rgb(133, 175, 255); } - + // This code is sensible to some edge cases, like @rules put in a comment, or in a string, and this is why i now use the protect system // It was breaking https://www.pascalgamedevelopment.com/content.php . // It would be possible to fix this by adding a condition to the regex to avoid matching @rules in comments or strings, but it would be a bit more complex // Or even protecting the @rules individually wit a numbered css class, but it would be a bit more complex too regarding the occurences of the @rules in strings or comments - + let imports = str.match(uDark.cssAtRulesRegex) || []; rules.unshift(...imports); - + } send_data_image_to_parser(str, details, options) { - + // uDark.disable_data_image_edition=true; if (str.trim().toLowerCase().startsWith('data:') && !uDark.userSettings.disable_image_edition && !uDark.disable_data_image_edition) { let { @@ -424,7 +437,7 @@ class uDarkC extends uDarkExtended { let imageData = data; options.changed = true; // We have changed the image, notify calle, like edit_css_url action options.is_data_image = true; - + if (!failure && dataHeader.includes('svg')) // Synchronous edit for data SVGs images, we have some nice context and functions to work with { // This avoids loosing svg data including the size of the image, and the tags in the image uDark.disable_svg_data_url_edition = false; @@ -434,9 +447,9 @@ class uDarkC extends uDarkExtended { return str; } options.get_document = true; - + if (!b64) { - + // This replaces searches for unencoded % in image data, and replaces them by the equivalent %25. // Some websites uses % in their svg data eg for percentage value, while not encoding them. // This results in a probalby broken image, since we are in a dataURL image. @@ -444,9 +457,9 @@ class uDarkC extends uDarkExtended { imageData = imageData.replace(/%(?![0-9a-z]{2})/gi, "%25") imageData = decodeURIComponent(imageData); } - + imageData = uDark.frontEditHTML(false, imageData, details, options).innerHTML; - + let encoded = undefined; // uDark.disable_reencode_data_svg_to_base64=true; if (uDark.disable_reencode_data_svg_to_base64) { @@ -475,9 +488,9 @@ class uDarkC extends uDarkExtended { encoded = encodeURIComponent(imageData); encoded = uDark.rencodeToURI(encoded, dataHeader.replace("base64", ""), false); } - + } - + str = encoded; } else { str = "https://data-image?base64IMG=" + str; // Sending other images to the parser via the worker, @@ -497,12 +510,12 @@ class uDarkC extends uDarkExtended { return fillValue } // fill has another meaning for animate let is_text = options.notableInfos.guessed_type == "logo" || ["text", "tspan"].includes(fillElem.tagName); - + if (!is_text && ["path"].includes(fillElem.tagName)) { let draw_path = fillElem.getAttribute("d"); // Lot of stop path in in path, it's probably a text is_text = draw_path && ([...draw_path.matchAll(/Z/ig)].length >= 2 || draw_path.length > 170) - + } fillElem.setAttribute("udark-edit", true); fillElem.setAttribute(class_name, `${options.notableInfos.guessed_type}${is_text?"-text":""}`); @@ -534,7 +547,7 @@ class uDarkC extends uDarkExtended { options.notableInfos.inside_clickable = true; } } - + if (!options.notableInfos.logo_match) { if (uDark.search_container_logo(svg, options.notableInfos)) { options.notableInfos.logo_match = true; @@ -543,23 +556,23 @@ class uDarkC extends uDarkExtended { if (options.notableInfos.logo_match || options.notableInfos.inside_clickable) { options.notableInfos.guessed_type = "logo"; } - + if (options.notableInfos.guessed_type == "logo") { - + svg.setAttribute("fill", "white"); // svg.removeAttribute("fill"); // svg.setAttribute("fill", "currentColor"); - + if (options.remoteSVG || options.svgDataImage) // If there is no style element, we don't need to create one { let styleElem = document.createElement("style"); styleElem.id = "udark-styled"; styleElem.append(document.createTextNode(uDark.inject_css_override)) styleElem.append(document.createTextNode("svg{color:white}")) // Allows "currentColor" to take effect - + svg.append(styleElem); } - + } svg.querySelectorAll("[fill]:not([udark-fill])").forEach(fillElem => { fillElem.setAttribute("fill", uDark.get_fill_for_svg_elem(fillElem, false, options)) @@ -567,10 +580,10 @@ class uDarkC extends uDarkExtended { svg.querySelectorAll("[stroke]:not([udark-stroke])").forEach(fillElem => { fillElem.setAttribute("stroke", uDark.get_fill_for_svg_elem(fillElem, fillElem.getAttribute("stroke"), options).replace(/currentColor/i, "white"), "udark-stroke") }) - + svg.setAttribute("udark-guess", options.notableInfos.guessed_type); svg.setAttribute("udark-infos", new URLSearchParams(options.notableInfos).toString()); - + } edit_styles_elements(parentElement, details, add_class = "ud-edited-background", options = {}) { parentElement.querySelectorAll(`style:not(.${add_class})`).forEach(astyle => { @@ -579,7 +592,7 @@ class uDarkC extends uDarkExtended { // According to https://stackoverflow.com/questions/55895361/how-do-i-change-the-innerhtml-of-a-global-style-element-with-cssrule , // it is not possible to edit a style element innerHTML with its cssStyleSheet alone // As long as we are returing a STR, we have to edit the style element innerHTML; - + astyle.classList.add(add_class) }); } @@ -591,22 +604,22 @@ class uDarkC extends uDarkExtended { str = str.protect_simple(uDark.tagsToProtectRegex, "ud-tag-ptd-$1" // use word boundaries to avoid matching tags like "headings" or tbody or texts like innerHTML // Frame and frameset are obsolete, but there were meant to be in head, and will be removed from body, they need to be protected - + ); - + let parsedDocument = uDark.createDocumentFromHtml("" + str + "" /* Re encapsulate str into a is not an overkill : Exists something called unsafeHTML clid binding. I did not understood what it is, but it needs a body tag for proper parsing*/ ); - + const aDocument = parsedDocument.body; - + if (details.unspecifiedCharset) { // As we seen document.characterSet will default we need to account the meta tag charset and re decode the document properly // It Falbacks from http header charset to meta tag charset, then to http-equiv charset then to OS charset. // We can use queryselector safely as it takes the first meta tag it finds let metaContentType = aDocument.querySelector("meta[charset],meta[http-equiv='Content-Type']"); // If both charset and http-equiv attributes are set in the same tag , charset wins - + if (metaContentType) { let usedContentType = metaContentType.getAttribute("content"); if (metaContentType.hasAttribute("charset")) { @@ -633,41 +646,41 @@ class uDarkC extends uDarkExtended { } aDocument.querySelectorAll("meta[http-equiv=content-security-policy]").forEach(m => m.remove()); if (!details.debugParsing) { - + // 4. Temporarily replace all SVG elements to avoid accidental style modifications const svgElements = uDark.processSvgElements(aDocument, details); // 5. Edit styles and attributes inline for background elements uDark.edit_styles_attributes(aDocument, details); uDark.edit_styles_elements(aDocument, details, "ud-edited-background"); - + // 8. Add a custom identifier to favicon links to manage cache uDark.processLinks(aDocument); - + // 9. Process image sources and prepare them for custom modifications uDark.processImages(aDocument); - + // 10. Recursively process iframes using the "srcdoc" attribute by applying the same editing logic uDark.processIframes(aDocument, details, {}); - + // 11. Handle elements with color attributes (color, bgcolor) and ensure proper color handling uDark.processColoredItems(aDocument); - + // 12. Inject custom CSS and dark color scheme if required (only for the first data load) uDark.injectStylesIfNeeded(aDocument, details); // Only benefit of this ; avoids page being white on uDark refresh - + // 13. Restore the original SVG elements that were temporarily replaced uDark.restoreSvgElements(svgElements); } - + // 15. Remove the integrity attribute from elements and replace it with a custom attribute uDark.restoreIntegrityAttributes(aDocument); - + // 16. Return the final edited HTML const outerEdited = aDocument.innerHTML.trim().unprotect_simple("ud-tag-ptd-"); return "" + outerEdited; // Once i tried to be funny and personalized the doctype, but it was a bad idea, it broke everything ! Doctype is a serious thing, very sensitive to any change outside of the standard - + } - + frontEditHTML(elem, strO, details, options = {}) { // 0. Return the original value if it's not a string if(!(strO instanceof String || typeof strO === "string")){ @@ -690,49 +703,49 @@ class uDarkC extends uDarkExtended { /* Re encapsulate str into a is not an overkill : Exists something called unsafeHTML clid binding. I did not understood what it is, but it needs a body tag for proper parsing*/ ); const aDocument = parsedDocument.body; - - + + // 4. Temporarily replace all SVG elements to avoid accidental style modifications const svgElements = uDark.processSvgElements(aDocument, details); - + // 5. Edit styles and attributes inline for background elements uDark.edit_styles_attributes(aDocument, details); uDark.edit_styles_elements(aDocument, details, "ud-edited-background"); - + // 8. Add a custom identifier to favicon links to manage cache uDark.processLinks(aDocument); - + // 9. Process image sources and prepare them for custom modifications uDark.processImages(aDocument); - + // 10. Recursively process iframes using the "srcdoc" attribute by applying the same editing logic uDark.processIframes(aDocument, details, options); - + // 11. Handle elements with color attributes (color, bgcolor) and ensure proper color handling uDark.processColoredItems(aDocument); - + // 13. Restore the original SVG elements that were temporarily replaced uDark.restoreSvgElements(svgElements); - + // 15. Remove the integrity attribute from elements and replace it with a custom attribute uDark.restoreIntegrityAttributes(aDocument); - + // 18. After all the edits, return the final HTML output - + if (options.get_document) { return aDocument; } - + let resultEdited = aDocument.innerHTML.unprotect_simple("ud-tag-ptd-"); return resultEdited; } - + createDocumentFromHtml(html) { // Use DOMParser to convert the HTML string into a DOM document const parser = new DOMParser(); return parser.p_ud_parseFromString(html, "text/html"); } - + processSvgElements(documentElement, details) { let svgElements = []; // Temporarily replace all SVG elements to avoid accidental style modifications @@ -745,7 +758,7 @@ class uDarkC extends uDarkExtended { }); return svgElements; } - + processMetaTags(documentElement) { // Ensure that content-type meta tags are properly set to avoid charset issues documentElement.querySelectorAll("meta[http-equiv]").forEach(m => { @@ -754,18 +767,17 @@ class uDarkC extends uDarkExtended { } }); } - + edit_styles_attributes(parentElement, details, options = {}) { parentElement.querySelectorAll("[style]").forEach(astyle => { - // console.log(details,astyle,astyle.innerHTML,astyle.innerHTML.includes(`button,[type="reset"],[type="button"],button:hover,[type="button"],[type="submit"],button:active:hover,[type="button"],[type="submi`)) - astyle.setAttribute("style", uDark.edit_str(astyle.getAttribute("style"), false, false, details, false, { + astyle.setAttribute("style", uDark.edit_str(astyle.getAttribute("style"), false, false, details, false, { ...options, nochunk: true })); }); - + } - + processLinks(documentElement) { // Append a custom identifier to favicon links to manage cache more effectively documentElement.querySelectorAll("link[rel*='icon' i][href]").forEach(link => { @@ -822,13 +834,13 @@ class uDarkC extends uDarkExtended { processImages(documentElement) { // Process image sources to prepare them for custom modifications documentElement.querySelectorAll("img[src]").forEach(image => { - + image.setAttribute("src", uDark.image_element_prepare_href(image, documentElement)); }); - + } frontEditHTMLPossibleDataURL(elem, value, details, options, documentElement) { - + let { b64, dataHeader, @@ -857,16 +869,16 @@ class uDarkC extends uDarkExtended { documentElement.querySelectorAll("iframe[srcdoc]").forEach(iframe => { iframe.setAttribute("srcdoc", uDark.frontEditHTML(false, iframe.srcdoc, details)); }); - + documentElement.querySelectorAll("object[data],embed[src],iframe[src]").forEach(object => { // Use GetAttribute to get the original value, as the src attribute may be changed by the context let src = object.getAttribute("src"); let usedData = src ? src : object.getAttribute("data"); - + object.setAttribute(src ? "src" : "data", uDark.frontEditHTMLPossibleDataURL(object, usedData, details, options, documentElement)); }); } - + processColoredItems(documentElement) { // Process elements with color or bgcolor attributes and ensure proper color handling documentElement.querySelectorAll("[color],[bgcolor]").forEach(coloredItem => { @@ -879,10 +891,10 @@ class uDarkC extends uDarkExtended { attributeValue += "0"; // Ensure colors are properly formatted } const possibleColor = uDark.is_color(attributeValue, true, true); - + if (possibleColor) { let callResult = afunction(...possibleColor, uDark.hex_val /* this kind of html4 attributes does not fully supports rgba vals, prefer use hex vals */ , coloredItem); - + if (callResult) { coloredItem.setAttribute(key, callResult); } @@ -890,13 +902,13 @@ class uDarkC extends uDarkExtended { } }); } - + injectStylesIfNeeded(aDocument, details) { // Inject custom CSS and the dark color scheme meta tag if this is the first data load if (details.dataCount === 1) { - + // Stopped using inject_css_suggested, as it was causing issues with some websites, like react ones that stats with a minimal body - + const udMetaDark = aDocument.querySelector("meta[name='color-scheme']") || document.createElement("meta"); udMetaDark.id = "ud-meta-dark"; udMetaDark.name = "color-scheme"; @@ -905,7 +917,7 @@ class uDarkC extends uDarkExtended { headElem.prepend(udMetaDark); } } - + restoreSvgElements(svgElements) { // Restore the original SVG elements that were temporarily replaced svgElements.forEach(([svg, tempReplace]) => { @@ -923,12 +935,12 @@ class uDarkC extends uDarkExtended { template.innerHTML = script.innerHTML; noScript.append(template.content); // We cant put innerHTML directly in a noscript element, it would be html encoded. The template element is used to avoid this, it parse elements for us. script.replaceWith(noScript); - + }); } restoreTemplateElements(aDocument) { // Restore