2018-10-07 15:28:41 +00:00
|
|
|
/* global API_METHODS usercss chromeLocal styleManager */
|
2017-09-18 03:34:12 +00:00
|
|
|
'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;
|
|
|
|
|
2018-01-10 18:56:14 +00:00
|
|
|
API_METHODS.parseUsercss = parse;
|
|
|
|
API_METHODS.findUsercss = find;
|
2017-12-10 07:04:13 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-09-18 03:34:12 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-10 18:56:14 +00:00
|
|
|
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;
|
|
|
|
});
|
2018-01-10 18:56:14 +00:00
|
|
|
}
|
|
|
|
|
2018-08-18 20:17:20 +00:00
|
|
|
/**
|
|
|
|
* 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-08-18 20:17:20 +00:00
|
|
|
}) {
|
2018-10-08 10:16:45 +00:00
|
|
|
return usercss.buildMeta(sourceCode)
|
|
|
|
.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);
|
|
|
|
}
|
2017-09-18 03:34:12 +00:00
|
|
|
}
|
|
|
|
|
2018-01-10 18:56:14 +00:00
|
|
|
// Parse the source, apply customizations, report fatal/syntax errors
|
2018-10-07 15:28:41 +00:00
|
|
|
function parse(style) {
|
|
|
|
return fetchStyle()
|
|
|
|
.then(buildMeta)
|
2017-09-18 03:34:12 +00:00
|
|
|
.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-01-10 18:56:14 +00:00
|
|
|
}
|
|
|
|
|
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);
|
2017-09-18 03:34:12 +00:00
|
|
|
}
|
|
|
|
|
2018-01-09 16:13:37 +00:00
|
|
|
/**
|
|
|
|
* @param {Style|{name:string, namespace:string}} styleOrData
|
|
|
|
* @returns {Style}
|
|
|
|
*/
|
2018-01-10 18:56:14 +00:00
|
|
|
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-07 15:28:41 +00:00
|
|
|
return styleManager.getAllStyles(styleList => {
|
|
|
|
for (const dup of styleList) {
|
|
|
|
const data = dup.usercssData;
|
|
|
|
if (!data) continue;
|
|
|
|
if (data.name === name &&
|
|
|
|
data.namespace === namespace) {
|
|
|
|
return dup;
|
|
|
|
}
|
2018-01-07 13:36:30 +00:00
|
|
|
}
|
2018-10-07 15:28:41 +00:00
|
|
|
});
|
2017-09-18 03:34:12 +00:00
|
|
|
}
|
|
|
|
|
2018-01-03 15:26:31 +00:00
|
|
|
function install({url, direct, downloaded, tab}, sender) {
|
|
|
|
tab = tab !== undefined ? tab : sender.tab;
|
2018-01-01 17:02:49 +00:00
|
|
|
url = url || tab.url;
|
2017-12-06 19:35:19 +00:00
|
|
|
if (direct && !downloaded) {
|
2017-11-24 16:33:50 +00:00
|
|
|
prefetchCodeForInstallation(tab.id, url);
|
|
|
|
}
|
2018-01-01 17:02:49 +00:00
|
|
|
return openURL({
|
2017-11-24 16:33:50 +00:00
|
|
|
url: '/install-usercss.html' +
|
|
|
|
'?updateUrl=' + encodeURIComponent(url) +
|
2017-11-25 17:24:15 +00:00
|
|
|
'&tabId=' + tab.id +
|
|
|
|
(direct ? '&direct=yes' : ''),
|
2017-10-01 12:34:29 +00:00
|
|
|
index: tab.index + 1,
|
|
|
|
openerTabId: tab.id,
|
2017-12-31 14:58:35 +00:00
|
|
|
currentWindow: null,
|
2018-01-01 17:02:49 +00:00
|
|
|
});
|
2017-09-24 03:39:04 +00:00
|
|
|
}
|
|
|
|
|
2017-11-24 16:33:50 +00:00
|
|
|
function prefetchCodeForInstallation(tabId, url) {
|
2017-12-10 07:04:13 +00:00
|
|
|
const key = TEMP_CODE_PREFIX + tabId;
|
|
|
|
tempCodeLastWriteDate = Date.now();
|
2017-11-24 16:33:50 +00:00
|
|
|
Promise.all([
|
|
|
|
download(url),
|
|
|
|
chromeLocal.setValue(key, {loading: true}),
|
|
|
|
]).then(([code]) => {
|
|
|
|
chromeLocal.setValue(key, code);
|
2017-12-10 07:04:13 +00:00
|
|
|
setTimeout(() => chromeLocal.remove(key), TEMP_CODE_CLEANUP_DELAY);
|
2017-11-24 16:33:50 +00:00
|
|
|
});
|
|
|
|
}
|
2017-09-18 03:34:12 +00:00
|
|
|
})();
|