Update Image Loader Placeholder Remover to 1.23.2

- *Actually* fix page slowdowns by ferociously caching CSS properties
This commit is contained in:
blankie 2023-11-14 12:50:23 +11:00
parent 9a64f71505
commit ed2fbde27a
Signed by: blankie
GPG Key ID: CC15FC822C7F61F5
1 changed files with 69 additions and 37 deletions

View File

@ -4,7 +4,7 @@
// @match http*://*/* // @match http*://*/*
// @exclude-match http*://solar.lowtechmagazine.com/* // @exclude-match http*://solar.lowtechmagazine.com/*
// @grant none // @grant none
// @version 1.23.1 // @version 1.23.2
// @author blankie // @author blankie
// @run-at document-end // @run-at document-end
// @description Removes image loading placeholders // @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_COMPONENT = `${URL_RE_STR}(?:\\s+\\d+w|\\s+\\d+(?:\\.\\d+)?x)?`;
const SRCSET_RE = new RegExp(`^${SRCSET_COMPONENT}(?:,\\s*${SRCSET_COMPONENT})*$`); const SRCSET_RE = new RegExp(`^${SRCSET_COMPONENT}(?:,\\s*${SRCSET_COMPONENT})*$`);
let cachedCSSProperties = new Map();
function isUrlLike(url) { function isUrlLike(url) {
// Example of ./path: // Example of ./path:
// - https://projects.apnews.com/features/2023/missing-students-chronic-absenteeism/index.html // - https://projects.apnews.com/features/2023/missing-students-chronic-absenteeism/index.html
return url && URL_RE.test(url); return url && URL_RE.test(url);
} }
function findUrl(element) {
if (window.location.host === "www.vice.com") { function getUrlAttributes(element) {
let picture = element.parentElement; let attributes = [];
let source = picture && picture.localName === "picture" ? picture.querySelector("source") : null;
if (source) {
return source.srcset;
}
}
// https://blog.google/ // https://blog.google/
// https://blog.google/threat-analysis-group/active-north-korean-campaign-targeting-security-researchers/ // https://blog.google/threat-analysis-group/active-north-korean-campaign-targeting-security-researchers/
if (window.location.host === "blog.google" && element.hasAttribute("data-loading")) { if (window.location.host === "blog.google" && element.hasAttribute("data-loading")) {
let data = JSON.parse(element.getAttribute("data-loading")); attributes.push(element.getAttributeNode("data-loading"));
return data.desktop || data.mobile;
} }
// Examples of data-src: // Examples of data-src:
@ -51,7 +47,7 @@ function findUrl(element) {
// - https://knowyourmeme.com/ // - https://knowyourmeme.com/
// - https://knowyourmeme.com/memes/saddam-husseins-hiding-place // - https://knowyourmeme.com/memes/saddam-husseins-hiding-place
if (isUrlLike(element.dataset.src)) { if (isUrlLike(element.dataset.src)) {
return element.dataset.src; attributes.push(element.getAttributeNode("data-src"));
} }
for (let attr of element.attributes) { for (let attr of element.attributes) {
@ -79,7 +75,31 @@ function findUrl(element) {
if (!isUrlLike(attr.value)) { if (!isUrlLike(attr.value)) {
continue; 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; return null;
@ -98,6 +118,17 @@ function findSrcset(element) {
return null; 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) { function getLazyloaderClasses(element) {
let classes = []; let classes = [];
@ -172,6 +203,7 @@ function cloneLazyloaderTree(element, elementLazyLoaderClasses) {
for (let attr of getLazyloaderAttributes(followingTopMostParent)) { for (let attr of getLazyloaderAttributes(followingTopMostParent)) {
clone.setAttribute(attr.name, attr.value); clone.setAttribute(attr.name, attr.value);
} }
if (topMostParent) { if (topMostParent) {
clone.append(topMostParent); clone.append(topMostParent);
} }
@ -185,8 +217,12 @@ function cloneLazyloaderTree(element, elementLazyLoaderClasses) {
let clone = document.createElement(element.localName); let clone = document.createElement(element.localName);
clone.classList.add(...elementLazyLoaderClasses); 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)) { for (let attr of getLazyloaderAttributes(element)) {
clone.setAttribute(attr.name, attr.value); clone.setAttribute(attr.name, !urlAttributeNames.has(attr.name) ? attr.value : "");
} }
if (bottomMostChild) { if (bottomMostChild) {
@ -200,9 +236,19 @@ function cloneLazyloaderTree(element, elementLazyLoaderClasses) {
} }
function unhideElement(element, lazyLoaderClasses) { 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
// - https://closeronline.co.uk/real-life/news/ever-used-excuses-documented-spreadsheet-man-used-expose-wife-s-lack-sex // - 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 // - 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://daramiblog.com/how-to-group-irregular-verbs-for-more-efficient-learning/
// - https://www.wired.com/ // - https://www.wired.com/
// - https://www.wired.com/story/researcher-fooled-a-google-ai-into-thinking-a-rifle-was-a-helicopter/ // - 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; element.style.opacity = 1;
} }
// Examples of display: // Examples of display:
// - https://closeronline.co.uk // - https://closeronline.co.uk
// - https://closeronline.co.uk/real-life/news/ever-used-excuses-documented-spreadsheet-man-used-expose-wife-s-lack-sex // - 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"; element.style.display = "block";
} }
@ -226,16 +274,6 @@ function unhideElement(element, lazyLoaderClasses) {
element.style.paddingBottom = 0; 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: // Examples of visibility:
// - https://www.edinburghlive.co.uk/ // - https://www.edinburghlive.co.uk/
// - https://www.edinburghlive.co.uk/news/edinburgh-news/edinburgh-couple-fume-handed-17k-27906242 // - 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"; 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: // Examples of blur:
// - https://www.404media.co/welcome-to-404-media/ // - https://www.404media.co/welcome-to-404-media/
// - https://restofworld.org/ // - https://restofworld.org/
@ -350,15 +382,15 @@ function removePlaceholder(img) {
} }
for (let img of document.querySelectorAll("img")) {
for (let img of Array.from(document.querySelectorAll("img"))) {
removePlaceholder(img); removePlaceholder(img);
} }
// the reason we reunhide images after 1s after the page loads is because of <noscript> elements being faked by umatrix // the reason we reunhide images after 1s after the page loads is because of <noscript> elements being faked by umatrix
setTimeout(function() { setTimeout(function() {
for (let img of Array.from(document.querySelectorAll("img")) { for (let img of document.querySelectorAll("img")) {
removePlaceholder(img); removePlaceholder(img);
} }
cachedCSSProperties.clear();
}, 1000); }, 1000);