lazy-load favicons

This commit is contained in:
tophf 2022-01-29 18:53:49 +03:00
parent d048c480c3
commit 513ced6d57
2 changed files with 37 additions and 47 deletions

View File

@ -666,7 +666,7 @@ a:hover {
transition: opacity .5s, filter .5s; transition: opacity .5s, filter .5s;
/* workaround for the buggy CSS filter: images in the hidden overflow are shown on Mac */ /* workaround for the buggy CSS filter: images in the hidden overflow are shown on Mac */
backface-visibility: hidden; backface-visibility: hidden;
display: none; visibility: hidden;
} }
.newUI .favicons-grayed .target img { .newUI .favicons-grayed .target img {
@ -679,7 +679,7 @@ a:hover {
} }
.newUI .has-favicons .target img[src] { .newUI .has-favicons .target img[src] {
display: inline; visibility: visible;
} }
.newUI .entry:hover .target img { .newUI .entry:hover .target img {

View File

@ -1,4 +1,4 @@
/* global $ $$ animateElement scrollElementIntoView */// dom.js /* global $$ $ $create animateElement scrollElementIntoView */// dom.js
/* global API */// msg.js /* global API */// msg.js
/* global URLS debounce isEmptyObj sessionStore */// toolbox.js /* global URLS debounce isEmptyObj sessionStore */// toolbox.js
/* global filterAndAppend */// filters.js /* global filterAndAppend */// filters.js
@ -10,7 +10,7 @@
const ENTRY_ID_PREFIX_RAW = 'style-'; const ENTRY_ID_PREFIX_RAW = 'style-';
const TARGET_TYPES = ['domains', 'urls', 'urlPrefixes', 'regexps']; const TARGET_TYPES = ['domains', 'urls', 'urlPrefixes', 'regexps'];
const OWN_ICON = chrome.runtime.getManifest().icons['16']; const OWN_ICON = chrome.runtime.getURL(chrome.runtime.getManifest().icons['16']);
const AGES = [ const AGES = [
[24, 'h', t('dateAbbrHour', '\x01')], [24, 'h', t('dateAbbrHour', '\x01')],
[30, 'd', t('dateAbbrDay', '\x01')], [30, 'd', t('dateAbbrDay', '\x01')],
@ -18,6 +18,32 @@ const AGES = [
[Infinity, 'y', t('dateAbbrYear', '\x01')], [Infinity, 'y', t('dateAbbrYear', '\x01')],
]; ];
(() => {
const proto = HTMLImageElement.prototype;
if ('loading' in proto) return;
const pSrc = Object.getOwnPropertyDescriptor(proto, 'src');
const xo = new IntersectionObserver(entries => {
for (const e of entries) {
if (e.isIntersecting) {
const el = e.target;
pSrc.set.call(el, el.dataset.src);
xo.unobserve(el);
delete el.dataset.src;
}
}
});
Object.defineProperty(proto, 'src', Object.assign({}, pSrc, {
set(val) {
if (this.loading === 'lazy') {
this.dataset.src = val;
xo.observe(this);
} else {
pSrc.set.call(this, val);
}
},
}));
})();
let elementParts; let elementParts;
function $entry(styleOrId, root = installed) { function $entry(styleOrId, root = installed) {
@ -215,18 +241,13 @@ function getFaviconSrc(container = installed) {
favicon = targetValue.includes('://') && targetValue.match(regexpMatchDomain); favicon = targetValue.includes('://') && targetValue.match(regexpMatchDomain);
favicon = favicon ? URLS.favicon(favicon[1]) : ''; favicon = favicon ? URLS.favicon(favicon[1]) : '';
} }
if (favicon) { if (!favicon) continue;
const img = target.children[0]; const img = $(':scope > img:first-child', target) ||
if (!img || img.localName !== 'img') { target.insertAdjacentElement('afterbegin', $create('img', {loading: 'lazy'}));
target.insertAdjacentElement('afterbegin', document.createElement('img')) if ((img.dataset.src || img.src) !== favicon) {
.dataset.src = favicon; img.src = favicon;
} else if ((img.dataset.src || img.src) !== favicon) {
img.src = '';
img.dataset.src = favicon;
}
} }
} }
loadFavicons();
} }
function fitSelectBox(...elems) { function fitSelectBox(...elems) {
@ -281,37 +302,6 @@ function highlightEditedStyle() {
} }
} }
function loadFavicons({all = false} = {}) {
if (!installed.firstElementChild) return;
let favicons = [];
if (all) {
favicons = $$('img[data-src]', installed);
} else {
const {left, top} = installed.firstElementChild.getBoundingClientRect();
const x = Math.max(0, left);
const y = Math.max(0, top);
const first = document.elementFromPoint(x, y);
if (!first) return requestAnimationFrame(loadFavicons.bind(null, ...arguments));
const lastOffset = first.offsetTop + window.innerHeight;
const numTargets = newUI.targets;
let entry = first && first.closest('.entry') || installed.children[0];
while (entry && entry.offsetTop <= lastOffset) {
favicons.push(...$$('img', entry).slice(0, numTargets).filter(img => img.dataset.src));
entry = entry.nextElementSibling;
}
}
let i = 0;
for (const img of favicons) {
img.src = img.dataset.src;
delete img.dataset.src;
// loading too many icons at once will block the page while the new layout is recalculated
if (++i > 100) break;
}
if ($('img[data-src]', installed)) {
debounce(loadFavicons, 1, {all: true});
}
}
/** Adding spaces so CSS can detect "bigness" of a value via amount of spaces at the beginning */ /** Adding spaces so CSS can detect "bigness" of a value via amount of spaces at the beginning */
function padLeft(val, width) { function padLeft(val, width) {
val = `${val}`; val = `${val}`;
@ -354,11 +344,11 @@ function showStyles(styles = [], matchUrlIds) {
filterAndAppend({container: renderBin}).then(sorter.updateStripes); filterAndAppend({container: renderBin}).then(sorter.updateStripes);
if (index < sorted.length) { if (index < sorted.length) {
requestAnimationFrame(renderStyles); requestAnimationFrame(renderStyles);
if (firstRun) setTimeout(getFaviconSrc); if (firstRun) getFaviconSrc();
firstRun = false; firstRun = false;
return; return;
} }
setTimeout(getFaviconSrc); getFaviconSrc();
if (sessionStore.justEditedStyleId) { if (sessionStore.justEditedStyleId) {
setTimeout(highlightEditedStyle); // delaying to avoid forced layout setTimeout(highlightEditedStyle); // delaying to avoid forced layout
} else if ('scrollY' in (history.state || {})) { } else if ('scrollY' in (history.state || {})) {