stylus/background/usercss-helper.js

195 lines
5.3 KiB
JavaScript
Raw Normal View History

2018-10-10 16:54:38 +00:00
/* global API_METHODS usercss chromeLocal styleManager FIREFOX deepCopy openURL
download */
'use strict';
2018-01-01 17:02:49 +00:00
(() => {
2018-10-07 15:28:41 +00:00
API_METHODS.installUsercss = installUsercss;
API_METHODS.editSaveUsercss = editSaveUsercss;
API_METHODS.configUsercssVars = configUsercssVars;
2018-10-07 14:59:31 +00:00
2018-01-01 17:02:49 +00:00
API_METHODS.buildUsercss = build;
2018-10-07 15:28:41 +00:00
API_METHODS.openUsercssInstallPage = install;
API_METHODS.findUsercss = find;
const TEMP_CODE_PREFIX = 'tempUsercssCode';
const TEMP_CODE_CLEANUP_DELAY = 60e3;
let tempCodeLastWriteDate = 0;
if (FIREFOX) {
// the temp code is created on direct installation of usercss URLs in FF
// and can be left behind in case the install page didn't open in time before
// the extension was updated/reloaded/disabled or the browser was closed
setTimeout(function poll() {
if (Date.now() - tempCodeLastWriteDate < TEMP_CODE_CLEANUP_DELAY) {
setTimeout(poll, TEMP_CODE_CLEANUP_DELAY);
return;
}
chrome.storage.local.get(null, storage => {
const leftovers = [];
for (const key in storage) {
if (key.startsWith(TEMP_CODE_PREFIX)) {
leftovers.push(key);
}
}
if (leftovers.length) {
chrome.storage.local.remove(leftovers);
}
});
}, TEMP_CODE_CLEANUP_DELAY);
}
function buildMeta(style) {
if (style.usercssData) {
return Promise.resolve(style);
}
try {
const {sourceCode} = style;
// allow sourceCode to be normalized
delete style.sourceCode;
return Promise.resolve(Object.assign(usercss.buildMeta(sourceCode), style));
} catch (e) {
return Promise.reject(e);
}
}
function assignVars(style) {
if (style.reason === 'config' && style.id) {
return style;
}
2018-10-07 15:28:41 +00:00
return find(style)
.then(dup => {
if (dup) {
style.id = dup.id;
if (style.reason !== 'config') {
// preserve style.vars during update
usercss.assignVars(style, dup);
}
}
return style;
});
}
/**
* Parse the source and find the duplication
* @param _
* @param {String} _.sourceCode
* @param {Boolean=} _.checkDup
* @param {Boolean=} _.metaOnly
* @returns {Promise<{style, dup:Boolean?}>}
*/
function build({
sourceCode,
checkDup,
metaOnly,
2018-10-08 10:16:45 +00:00
vars,
}) {
2018-10-10 15:05:20 +00:00
return Promise.resolve(usercss.buildMeta(sourceCode))
2018-10-08 10:16:45 +00:00
.then(style =>
Promise.all([
metaOnly ? style : doBuild(style),
checkDup ? find(style) : undefined
])
)
.then(([style, dup]) => ({style, dup}));
function doBuild(style) {
if (vars) {
const oldStyle = {usercssData: {vars}};
usercss.assignVars(style, oldStyle);
}
return usercss.buildCode(style);
}
}
// Parse the source, apply customizations, report fatal/syntax errors
2018-10-07 15:28:41 +00:00
function parse(style) {
return fetchStyle()
.then(buildMeta)
.then(assignVars)
2018-10-07 15:28:41 +00:00
.then(usercss.buildCode);
function fetchStyle() {
// restore if stripped by getStyleWithNoCode
if (typeof style.sourceCode !== 'string') {
return styleManager.get(style.id)
.then(oldStyle => {
style.sourceCode = oldStyle.sourceCode;
return style;
});
}
return Promise.resolve(style);
}
}
2018-10-07 15:28:41 +00:00
function installUsercss(style) {
return parse(style)
.then(styleManager.installStyle);
}
function editSaveUsercss(style) {
return parse(style)
.then(styleManager.editSave);
}
function configUsercssVars(id, vars) {
return styleManager.get(id)
.then(style => {
const newStyle = deepCopy(style);
newStyle.usercssData.vars = vars;
return usercss.buildCode(newStyle);
})
.then(style => styleManager.installStyle(style, 'config'))
.then(style => style.usercssData.vars);
}
2018-01-09 16:13:37 +00:00
/**
* @param {Style|{name:string, namespace:string}} styleOrData
* @returns {Style}
*/
function find(styleOrData) {
2018-10-07 15:28:41 +00:00
if (styleOrData.id) {
return styleManager.get(styleOrData.id);
}
2018-01-09 16:13:37 +00:00
const {name, namespace} = styleOrData.usercssData || styleOrData;
2018-10-10 15:05:20 +00:00
return styleManager.getAllStyles().then(styleList => {
2018-10-07 15:28:41 +00:00
for (const dup of styleList) {
const data = dup.usercssData;
if (!data) continue;
if (data.name === name &&
data.namespace === namespace) {
return dup;
}
}
2018-10-07 15:28:41 +00:00
});
}
2018-10-11 11:29:17 +00:00
function install({url, direct, downloaded, tab}, sender = this.sender) {
2018-01-03 15:26:31 +00:00
tab = tab !== undefined ? tab : sender.tab;
2018-01-01 17:02:49 +00:00
url = url || tab.url;
if (direct && !downloaded) {
prefetchCodeForInstallation(tab.id, url);
}
2018-01-01 17:02:49 +00:00
return openURL({
url: '/install-usercss.html' +
'?updateUrl=' + encodeURIComponent(url) +
2017-11-25 17:24:15 +00:00
'&tabId=' + tab.id +
(direct ? '&direct=yes' : ''),
index: tab.index + 1,
openerTabId: tab.id,
currentWindow: null,
2018-01-01 17:02:49 +00:00
});
2017-09-24 03:39:04 +00:00
}
function prefetchCodeForInstallation(tabId, url) {
const key = TEMP_CODE_PREFIX + tabId;
tempCodeLastWriteDate = Date.now();
Promise.all([
download(url),
chromeLocal.setValue(key, {loading: true}),
]).then(([code]) => {
chromeLocal.setValue(key, code);
setTimeout(() => chromeLocal.remove(key), TEMP_CODE_CLEANUP_DELAY);
});
}
})();