faster install from known sites

This commit is contained in:
tophf 2020-10-06 13:49:43 +03:00
parent 0162f39163
commit 78b0e33ba4
6 changed files with 93 additions and 63 deletions

View File

@ -1,8 +1,8 @@
/* global download prefs openURL FIREFOX CHROME /* global download prefs openURL FIREFOX CHROME
URLS ignoreChromeError usercssHelper chromeLocal semverCompare URLS ignoreChromeError chromeLocal semverCompare
styleManager msg navigatorUtil workerUtil contentScripts sync styleManager msg navigatorUtil workerUtil contentScripts sync
findExistingTab activateTab isTabReplaceable getActiveTab findExistingTab activateTab isTabReplaceable getActiveTab
tabManager */ */
'use strict'; 'use strict';
@ -111,14 +111,6 @@ navigatorUtil.onUrlChange(({tabId, frameId}, type) => {
} }
}); });
tabManager.onUpdate(({tabId, url, oldUrl = ''}) => {
if (usercssHelper.testUrl(url) && !oldUrl.startsWith(URLS.installUsercss)) {
usercssHelper.testContents(tabId, url).then(data => {
if (data.code) usercssHelper.openInstallerPage(tabId, url, data);
});
}
});
if (FIREFOX) { if (FIREFOX) {
// FF misses some about:blank iframes so we inject our content script explicitly // FF misses some about:blank iframes so we inject our content script explicitly
navigatorUtil.onDOMContentLoaded(webNavIframeHelperFF, { navigatorUtil.onDOMContentLoaded(webNavIframeHelperFF, {

View File

@ -1,15 +1,8 @@
/* global API_METHODS usercss styleManager deepCopy openURL download URLS */ /* global API_METHODS usercss styleManager deepCopy */
/* exported usercssHelper */ /* exported usercssHelper */
'use strict'; 'use strict';
const usercssHelper = (() => { const usercssHelper = (() => {
const installCodeCache = {};
const clearInstallCode = url => delete installCodeCache[url];
const isResponseText = r => /^text\/(css|plain)(;.*?)?$/i.test(r.headers.get('content-type'));
// in Firefox we have to use a content script to read file://
const fileLoader = !chrome.app && // not relying on navigator.ua which can be spoofed
(tabId => browser.tabs.executeScript(tabId, {file: '/content/install-hook-usercss.js'}).then(r => r[0]));
API_METHODS.installUsercss = installUsercss; API_METHODS.installUsercss = installUsercss;
API_METHODS.editSaveUsercss = editSaveUsercss; API_METHODS.editSaveUsercss = editSaveUsercss;
API_METHODS.configUsercssVars = configUsercssVars; API_METHODS.configUsercssVars = configUsercssVars;
@ -17,50 +10,6 @@ const usercssHelper = (() => {
API_METHODS.buildUsercss = build; API_METHODS.buildUsercss = build;
API_METHODS.findUsercss = find; API_METHODS.findUsercss = find;
API_METHODS.getUsercssInstallCode = url => {
// when the installer tab is reloaded after the cache is expired, this will throw intentionally
const {code, timer} = installCodeCache[url];
clearInstallCode(url);
clearTimeout(timer);
return code;
};
return {
testUrl(url) {
return url.includes('.user.') &&
/^(https?|file|ftps?):/.test(url) &&
/\.user\.(css|styl)$/.test(url.split(/[#?]/, 1)[0]);
},
/** @return {Promise<{ code:string, inTab:boolean } | false>} */
testContents(tabId, url) {
const isFile = url.startsWith('file:');
const inTab = isFile && Boolean(fileLoader);
return Promise.resolve(isFile || fetch(url, {method: 'HEAD'}).then(isResponseText))
.then(ok => ok && (inTab ? fileLoader(tabId) : download(url)))
.then(code => /==userstyle==/i.test(code) && {code, inTab});
},
openInstallerPage(tabId, url, {code, inTab} = {}) {
const newUrl = `${URLS.installUsercss}?updateUrl=${encodeURIComponent(url)}`;
if (inTab) {
browser.tabs.get(tabId).then(tab =>
openURL({
url: `${newUrl}&tabId=${tabId}`,
active: tab.active,
index: tab.index + 1,
openerTabId: tabId,
currentWindow: null,
}));
} else {
const timer = setTimeout(clearInstallCode, 10e3, url);
installCodeCache[url] = {code, timer};
chrome.tabs.update(tabId, {url: newUrl});
}
},
};
function buildMeta(style) { function buildMeta(style) {
if (style.usercssData) { if (style.usercssData) {
return Promise.resolve(style); return Promise.resolve(style);

View File

@ -0,0 +1,82 @@
/* global API_METHODS openURL download URLS tabManager */
'use strict';
(() => {
const installCodeCache = {};
const clearInstallCode = url => delete installCodeCache[url];
const isContentTypeText = type => /^text\/(css|plain)(;.*?)?$/i.test(type);
// in Firefox we have to use a content script to read file://
const fileLoader = !chrome.app && (
async tabId =>
(await browser.tabs.executeScript(tabId, {file: '/content/install-hook-usercss.js'}))[0]);
const urlLoader =
async (tabId, url) => (
url.startsWith('file:') ||
tabManager.get(tabId, isContentTypeText.name) ||
isContentTypeText((await fetch(url, {method: 'HEAD'})).headers.get('content-type'))
) && download(url);
API_METHODS.getUsercssInstallCode = url => {
// when the installer tab is reloaded after the cache is expired, this will throw intentionally
const {code, timer} = installCodeCache[url];
clearInstallCode(url);
clearTimeout(timer);
return code;
};
// Faster installation on known distribution sites to avoid flicker of css text
chrome.webRequest.onBeforeSendHeaders.addListener(({tabId, url}) => {
openInstallerPage(tabId, url, {});
// Silently suppressing navigation like it never happened
return {redirectUrl: 'javascript:void 0'}; // eslint-disable-line no-script-url
}, {
urls: [
URLS.usoArchiveRaw + 'usercss/*.user.css',
'*://greasyfork.org/scripts/*/code/*.user.css',
'*://sleazyfork.org/scripts/*/code/*.user.css',
],
types: ['main_frame'],
}, ['blocking']);
// Remember Content-Type to avoid re-fetching of the headers in urlLoader as it can be very slow
chrome.webRequest.onHeadersReceived.addListener(({tabId, responseHeaders}) => {
const h = responseHeaders.find(h => h.name.toLowerCase() === 'content-type');
tabManager.set(tabId, isContentTypeText.name, h && isContentTypeText(h.value) || undefined);
}, {
urls: '%css,%css?*,%styl,%styl?*'.replace(/%/g, '*://*/*.user.').split(','),
types: ['main_frame'],
}, ['responseHeaders']);
tabManager.onUpdate(async ({tabId, url, oldUrl = ''}) => {
if (url.includes('.user.') &&
/^(https?|file|ftps?):/.test(url) &&
/\.user\.(css|styl)$/.test(url.split(/[#?]/, 1)[0]) &&
!oldUrl.startsWith(URLS.installUsercss)) {
const inTab = url.startsWith('file:') && Boolean(fileLoader);
const code = await (inTab ? fileLoader : urlLoader)(tabId, url);
if (/==userstyle==/i.test(code)) {
openInstallerPage(tabId, url, {code, inTab});
}
}
});
function openInstallerPage(tabId, url, {code, inTab} = {}) {
const newUrl = `${URLS.installUsercss}?updateUrl=${encodeURIComponent(url)}`;
if (inTab) {
browser.tabs.get(tabId).then(tab =>
openURL({
url: `${newUrl}&tabId=${tabId}`,
active: tab.active,
index: tab.index + 1,
openerTabId: tabId,
currentWindow: null,
}));
} else {
const timer = setTimeout(clearInstallCode, 10e3, url);
installCodeCache[url] = {code, timer};
chrome.tabs.update(tabId, {url: newUrl});
}
}
})();

View File

@ -318,7 +318,9 @@
let sequence = null; let sequence = null;
if (tabId < 0) { if (tabId < 0) {
getData = DirectDownloader(); getData = DirectDownloader();
sequence = API.getUsercssInstallCode(initialUrl).catch(getData); sequence = API.getUsercssInstallCode(initialUrl)
.then(code => code || getData())
.catch(getData);
} else { } else {
getData = PortDownloader(); getData = PortDownloader();
sequence = getData({timer: false}); sequence = getData({timer: false});

View File

@ -72,6 +72,10 @@ const URLS = {
url.startsWith(URLS.usoArchiveRaw) && url.startsWith(URLS.usoArchiveRaw) &&
parseInt(url.match(/\/(\d+)\.user\.css|$/)[1]), parseInt(url.match(/\/(\d+)\.user\.css|$/)[1]),
extractGreasyForkId: url =>
/^https:\/\/(?:greasy|sleazy)fork\.org\/scripts\/(\d+)[^/]*\/code\/[^/]*\.user\.css$/.test(url) &&
RegExp.$1,
supported: url => ( supported: url => (
url.startsWith('http') && (FIREFOX || !url.startsWith(URLS.browserWebStore)) || url.startsWith('http') && (FIREFOX || !url.startsWith(URLS.browserWebStore)) ||
url.startsWith('ftp') || url.startsWith('ftp') ||

View File

@ -51,6 +51,7 @@
"background/icon-manager.js", "background/icon-manager.js",
"background/background.js", "background/background.js",
"background/usercss-helper.js", "background/usercss-helper.js",
"background/usercss-install-helper.js",
"background/style-via-api.js", "background/style-via-api.js",
"background/search-db.js", "background/search-db.js",
"background/update.js", "background/update.js",