From ed2fbde27a7b2798f49597fd4bca6ac82b8d145d Mon Sep 17 00:00:00 2001 From: blankie Date: Tue, 14 Nov 2023 12:50:23 +1100 Subject: [PATCH] Update Image Loader Placeholder Remover to 1.23.2 - *Actually* fix page slowdowns by ferociously caching CSS properties --- Image Loader Placeholder Remover.user.js | 106 +++++++++++++++-------- 1 file changed, 69 insertions(+), 37 deletions(-) diff --git a/Image Loader Placeholder Remover.user.js b/Image Loader Placeholder Remover.user.js index b217377..e02c030 100644 --- a/Image Loader Placeholder Remover.user.js +++ b/Image Loader Placeholder Remover.user.js @@ -4,7 +4,7 @@ // @match http*://*/* // @exclude-match http*://solar.lowtechmagazine.com/* // @grant none -// @version 1.23.1 +// @version 1.23.2 // @author blankie // @run-at document-end // @description Removes image loading placeholders @@ -18,25 +18,21 @@ const URL_RE = new RegExp(`^${URL_RE_STR}$`); const SRCSET_COMPONENT = `${URL_RE_STR}(?:\\s+\\d+w|\\s+\\d+(?:\\.\\d+)?x)?`; const SRCSET_RE = new RegExp(`^${SRCSET_COMPONENT}(?:,\\s*${SRCSET_COMPONENT})*$`); +let cachedCSSProperties = new Map(); + function isUrlLike(url) { // Example of ./path: // - https://projects.apnews.com/features/2023/missing-students-chronic-absenteeism/index.html return url && URL_RE.test(url); } -function findUrl(element) { - if (window.location.host === "www.vice.com") { - let picture = element.parentElement; - let source = picture && picture.localName === "picture" ? picture.querySelector("source") : null; - if (source) { - return source.srcset; - } - } + +function getUrlAttributes(element) { + let attributes = []; // https://blog.google/ // https://blog.google/threat-analysis-group/active-north-korean-campaign-targeting-security-researchers/ if (window.location.host === "blog.google" && element.hasAttribute("data-loading")) { - let data = JSON.parse(element.getAttribute("data-loading")); - return data.desktop || data.mobile; + attributes.push(element.getAttributeNode("data-loading")); } // Examples of data-src: @@ -51,7 +47,7 @@ function findUrl(element) { // - https://knowyourmeme.com/ // - https://knowyourmeme.com/memes/saddam-husseins-hiding-place if (isUrlLike(element.dataset.src)) { - return element.dataset.src; + attributes.push(element.getAttributeNode("data-src")); } for (let attr of element.attributes) { @@ -79,7 +75,31 @@ function findUrl(element) { if (!isUrlLike(attr.value)) { continue; } - return attr.value; + attributes.push(attr); + } + + return attributes; +} + +function findUrl(element) { + if (window.location.host === "www.vice.com") { + let picture = element.parentElement; + let source = picture && picture.localName === "picture" ? picture.querySelector("source") : null; + if (source) { + return source.srcset; + } + } + + // https://blog.google/ + // https://blog.google/threat-analysis-group/active-north-korean-campaign-targeting-security-researchers/ + if (window.location.host === "blog.google" && element.hasAttribute("data-loading")) { + let data = JSON.parse(element.getAttribute("data-loading")); + return data.desktop || data.mobile; + } + + let urlAttributes = getUrlAttributes(element); + if (urlAttributes.length !== 0) { + return urlAttributes[0].value; } return null; @@ -98,6 +118,17 @@ function findSrcset(element) { return null; } +function getCachedCSSProperties(element, key) { + if (cachedCSSProperties.has(key)) { + return cachedCSSProperties.get(key); + } + + let {opacity, display, visibility, filter} = getComputedStyle(element); + let ret = {opacity, display, visibility, filter}; + cachedCSSProperties.set(key, ret); + return ret; +} + function getLazyloaderClasses(element) { let classes = []; @@ -172,6 +203,7 @@ function cloneLazyloaderTree(element, elementLazyLoaderClasses) { for (let attr of getLazyloaderAttributes(followingTopMostParent)) { clone.setAttribute(attr.name, attr.value); } + if (topMostParent) { clone.append(topMostParent); } @@ -185,8 +217,12 @@ function cloneLazyloaderTree(element, elementLazyLoaderClasses) { let clone = document.createElement(element.localName); clone.classList.add(...elementLazyLoaderClasses); + let urlAttributeNames = new Set(["src", "href"]); + for (let attr of getUrlAttributes(element)) { + urlAttributeNames.add(attr.name); + } for (let attr of getLazyloaderAttributes(element)) { - clone.setAttribute(attr.name, attr.value); + clone.setAttribute(attr.name, !urlAttributeNames.has(attr.name) ? attr.value : ""); } if (bottomMostChild) { @@ -200,9 +236,19 @@ function cloneLazyloaderTree(element, elementLazyLoaderClasses) { } function unhideElement(element, lazyLoaderClasses) { - let style = getComputedStyle(element); + // We no longer/don't check the styles of each element individually, but rather their "generified" version + // (i.e., only lazy classes), because checking each actual element causes a lot of cache busts - // Examples of opacity: + // We check if the classes discriminate lazyloaded images, just in case + // We can't remove the lazyload classes from the image, because: + // - https://legendsoflocalization.com/japans-mysterious-love-of-the-word-lets/#why-lets-is-so-common + // - https://www.wired.com/story/why-do-printers-still-suck/ + // However, some sites use the classes to blur and/or change the opacity of images + let [toRemove, toExamine] = cloneLazyloaderTree(element, lazyLoaderClasses); + document.body.append(toRemove); + let classStyle = getCachedCSSProperties(toExamine, toRemove.outerHTML); + + // Examples of opacity === "0": // - https://closeronline.co.uk // - https://closeronline.co.uk/real-life/news/ever-used-excuses-documented-spreadsheet-man-used-expose-wife-s-lack-sex // - https://www.vice.com/en/article/dy73n7/ehallpass-1000-thousand-schools-monitor-bathroom @@ -210,14 +256,16 @@ function unhideElement(element, lazyLoaderClasses) { // - https://daramiblog.com/how-to-group-irregular-verbs-for-more-efficient-learning/ // - https://www.wired.com/ // - https://www.wired.com/story/researcher-fooled-a-google-ai-into-thinking-a-rifle-was-a-helicopter/ - if (style.opacity === "0") { + // Example of opacity === "0.75": + // https://www.404media.co/welcome-to-404-media/ + if (classStyle.opacity !== "1") { element.style.opacity = 1; } // Examples of display: // - https://closeronline.co.uk // - https://closeronline.co.uk/real-life/news/ever-used-excuses-documented-spreadsheet-man-used-expose-wife-s-lack-sex - if (style.display === "none") { + if (classStyle.display === "none") { element.style.display = "block"; } @@ -226,16 +274,6 @@ function unhideElement(element, lazyLoaderClasses) { element.style.paddingBottom = 0; } - // We check if the classes discriminate lazyloaded images, just in case - // We can't remove the lazyload classes from the image, because: - // - https://legendsoflocalization.com/japans-mysterious-love-of-the-word-lets/#why-lets-is-so-common - // - https://www.wired.com/story/why-do-printers-still-suck/ - // However, some sites use the classes to blur and/or change the opacity of images - - let [toRemove, toExamine] = cloneLazyloaderTree(element, lazyLoaderClasses); - document.body.append(toRemove); - let classStyle = getComputedStyle(toExamine); - // Examples of visibility: // - https://www.edinburghlive.co.uk/ // - https://www.edinburghlive.co.uk/news/edinburgh-news/edinburgh-couple-fume-handed-17k-27906242 @@ -243,12 +281,6 @@ function unhideElement(element, lazyLoaderClasses) { element.style.visibility = "visible"; } - // Example of opacity === "0.75": - // https://www.404media.co/welcome-to-404-media/ - if (classStyle.opacity !== "1") { - element.style.opacity = 1; - } - // Examples of blur: // - https://www.404media.co/welcome-to-404-media/ // - https://restofworld.org/ @@ -350,15 +382,15 @@ function removePlaceholder(img) { } - -for (let img of Array.from(document.querySelectorAll("img"))) { +for (let img of document.querySelectorAll("img")) { removePlaceholder(img); } // the reason we reunhide images after 1s after the page loads is because of