remember and skip bad favicons

This commit is contained in:
tophf 2022-02-22 01:27:41 +03:00
parent 5ff664f9b9
commit 38ac974d8a
2 changed files with 47 additions and 13 deletions

View File

@ -32,6 +32,13 @@ Object.assign(newUI, {
$.rootCL.toggle('newUI', newUI.enabled); $.rootCL.toggle('newUI', newUI.enabled);
$.rootCL.toggle('oldUI', !newUI.enabled); $.rootCL.toggle('oldUI', !newUI.enabled);
}, },
hasFavs: () => newUI.enabled && newUI.favicons,
badFavsKey: 'badFavs',
async readBadFavs() {
const key = newUI.badFavsKey;
const val = await API.prefsDb.get(key);
return (newUI[key] = Array.isArray(val) ? val : []);
},
}); });
// ...read the actual values // ...read the actual values
for (const id of newUI.ids) { for (const id of newUI.ids) {
@ -44,7 +51,7 @@ newUI.renderClass();
const [styles, ids] = await Promise.all([ const [styles, ids] = await Promise.all([
API.styles.getAll(), API.styles.getAll(),
query && API.styles.searchDB({query, mode: router.getSearch('searchMode')}), query && API.styles.searchDB({query, mode: router.getSearch('searchMode')}),
// needed to avoid flicker due to an extra frame and layout shift newUI.hasFavs() && newUI.readBadFavs(),
prefs.ready, prefs.ready,
]); ]);
installed.on('click', Events.entryClicked); installed.on('click', Events.entryClicked);

View File

@ -1,6 +1,6 @@
/* global $$ $ $create 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 getOwnTab isEmptyObj sessionStore stringAsRegExp */// toolbox.js
/* global filterAndAppend */// filters.js /* global filterAndAppend */// filters.js
/* global installed newUI */// manage.js /* global installed newUI */// manage.js
/* global prefs */ /* global prefs */
@ -45,6 +45,7 @@ const AGES = [
})(); })();
let elementParts; let elementParts;
let badFavs;
function $entry(styleOrId, root = installed) { function $entry(styleOrId, root = installed) {
return $(`#${ENTRY_ID_PREFIX_RAW}${styleOrId.id || styleOrId}`, root); return $(`#${ENTRY_ID_PREFIX_RAW}${styleOrId.id || styleOrId}`, root);
@ -214,20 +215,41 @@ function createTargetsElement({entry, expanded, style = entry.styleMeta}) {
entry._numTargets = numTargets; entry._numTargets = numTargets;
} }
function getFaviconSrc(container = installed) { async function getFaviconSrc(container = installed) {
if (!newUI.enabled || !newUI.favicons) return; if (!newUI.hasFavs()) return;
if (!badFavs) {
// API creates a new function each time so we save it for `debounce` which is keyed on function object
const {put} = API.prefsDb;
const key = newUI.badFavsKey;
const rxHost = new RegExp(`^${stringAsRegExp(URLS.favicon('\n'), '', true).replace('\n', '(.*)')}$`);
badFavs = newUI[key] || await newUI.readBadFavs();
const fn = e => {
const host = e.statusCode !== 200 && e.url.match(rxHost)[1];
if (host && !badFavs.includes(e)) {
badFavs.push(host);
debounce(put, 250, badFavs, key);
}
};
const filter = {
urls: [URLS.favicon('*')], // we assume there's no redirect
types: ['image'],
tabId: (await getOwnTab()).id,
};
chrome.webRequest.onCompleted.addListener(fn, filter); // works in Chrome
chrome.webRequest.onErrorOccurred.addListener(fn, filter); // works in FF
}
const regexpRemoveNegativeLookAhead = /(\?!([^)]+\))|\(\?![\w(]+[^)]+[\w|)]+)/g; const regexpRemoveNegativeLookAhead = /(\?!([^)]+\))|\(\?![\w(]+[^)]+[\w|)]+)/g;
// replace extra characters & all but the first group entry "(abc|def|ghi)xyz" => abcxyz // replace extra characters & all but the first group entry "(abc|def|ghi)xyz" => abcxyz
const regexpReplaceExtraCharacters = /[\\(]|((\|\w+)+\))/g; const regexpReplaceExtraCharacters = /[\\(]|((\|\w+)+\))/g;
const regexpMatchRegExp = /[\w-]+[.(]+(com|org|co|net|im|io|edu|gov|biz|info|de|cn|uk|nl|eu|ru)\b/g; const regexpMatchRegExp = /[\w-]+[.(]+(com|org|co|net|im|io|edu|gov|biz|info|de|cn|uk|nl|eu|ru)\b/g;
const regexpMatchDomain = /^.*?:\/\/([^/]+)/; const regexpMatchDomain = /^.*?:\/\/\W*([-.\w]+)/;
for (const target of $$('.target', container)) { for (const target of $$('.target', container)) {
const type = target.dataset.type; const type = target.dataset.type;
const targetValue = target.textContent; const targetValue = target.textContent;
if (!targetValue) continue; if (!targetValue) continue;
let favicon = ''; let favicon = '';
if (type === 'domains') { if (type === 'domains') {
favicon = URLS.favicon(targetValue); favicon = targetValue;
} else if (targetValue.includes('chrome-extension:') || targetValue.includes('moz-extension:')) { } else if (targetValue.includes('chrome-extension:') || targetValue.includes('moz-extension:')) {
favicon = OWN_ICON; favicon = OWN_ICON;
} else if (type === 'regexps') { } else if (type === 'regexps') {
@ -235,12 +257,17 @@ function getFaviconSrc(container = installed) {
.replace(regexpRemoveNegativeLookAhead, '') .replace(regexpRemoveNegativeLookAhead, '')
.replace(regexpReplaceExtraCharacters, '') .replace(regexpReplaceExtraCharacters, '')
.match(regexpMatchRegExp); .match(regexpMatchRegExp);
favicon = favicon ? URLS.favicon(favicon.shift()) : ''; favicon = favicon ? favicon.shift() : '';
} else { } else if (/^(f|ht)tps?:/.test(targetValue)) {
favicon = targetValue.includes('://') && targetValue.match(regexpMatchDomain); favicon = targetValue.match(regexpMatchDomain);
favicon = favicon ? URLS.favicon(favicon[1]) : ''; favicon = favicon ? favicon[1].replace(/\W+$/, '') : '';
}
if (!favicon || badFavs && badFavs.includes(favicon)) {
continue;
}
if (favicon !== OWN_ICON) {
favicon = URLS.favicon(favicon);
} }
if (!favicon) continue;
const img = $(':scope > img:first-child', target) || const img = $(':scope > img:first-child', target) ||
target.insertAdjacentElement('afterbegin', $create('img', {loading: 'lazy'})); target.insertAdjacentElement('afterbegin', $create('img', {loading: 'lazy'}));
if ((img.dataset.src || img.src) !== favicon) { if ((img.dataset.src || img.src) !== favicon) {
@ -376,7 +403,7 @@ function switchUI({styleOnly} = {}) {
Object.assign(newUI, current); Object.assign(newUI, current);
newUI.renderClass(); newUI.renderClass();
installed.classList.toggle('has-favicons', newUI.enabled && newUI.favicons); installed.classList.toggle('has-favicons', newUI.hasFavs());
installed.classList.toggle('favicons-grayed', newUI.enabled && newUI.faviconsGray); installed.classList.toggle('favicons-grayed', newUI.enabled && newUI.faviconsGray);
if (installed.style.getPropertyValue('--num-targets') !== `${newUI.targets}`) { if (installed.style.getPropertyValue('--num-targets') !== `${newUI.targets}`) {
installed.style.setProperty('--num-targets', newUI.targets); installed.style.setProperty('--num-targets', newUI.targets);
@ -386,7 +413,7 @@ function switchUI({styleOnly} = {}) {
return; return;
} }
const iconsEnabled = newUI.enabled && newUI.favicons; const iconsEnabled = newUI.hasFavs();
let iconsMissing = iconsEnabled && !$('.applies-to img'); let iconsMissing = iconsEnabled && !$('.applies-to img');
if (changed.enabled || (iconsMissing && !elementParts)) { if (changed.enabled || (iconsMissing && !elementParts)) {
installed.textContent = ''; installed.textContent = '';