stylus/js/usercss.js
tophf 420733b93a
PatchCSP + tweaks/fixes/features (#1107)
* add Patch CSP option
* show style version, size, and update age in manager
* add scope selector to style search in manager
* keep scroll position and selections in tab's session
* directly install usercss from raw github links
* ditch localStorage, use on-demand SessionStore proxy
* simplify localization
* allow <code> tag in i18n-html
* keep &nbsp; nodes in HTML templates
* API.getAllStyles is actually faster with code untouched
* fix fitToContent when applies-to is taller than window
* dedupe linter.enableForEditor calls
* prioritize visible CMs in refreshOnViewListener
* don't scroll to last style on editing a new one
* delay colorview for invisible CMs
* eslint comma-dangle error + autofix files
* styleViaXhr: also toggle for disableAll pref
* styleViaXhr: allow cookies for sandbox CSP
* simplify notes in options
* simplify getStylesViaXhr
* oldUI fixups:
  * remove separator before 1st applies-to
  * center name bubbles
* fix updateToc focus on a newly added section
* fix fitToContent when cloning section
* remove CSS `contain` as it makes no difference
* replace overrides with declarative CSS + code cosmetics
* simplify adjustWidth and make it work in FF
2020-11-18 14:17:15 +03:00

104 lines
3.0 KiB
JavaScript

/* global backgroundWorker */
/* exported usercss */
'use strict';
const usercss = (() => {
const GLOBAL_METAS = {
author: undefined,
description: undefined,
homepageURL: 'url',
updateURL: 'updateUrl',
name: undefined,
};
const RX_META = /\/\*!?\s*==userstyle==[\s\S]*?==\/userstyle==\s*\*\//i;
const ERR_ARGS_IS_LIST = new Set(['missingMandatory', 'missingChar']);
return {
RX_META,
buildMeta,
buildCode,
assignVars,
};
function buildMeta(sourceCode) {
sourceCode = sourceCode.replace(/\r\n?/g, '\n');
const style = {
enabled: true,
sourceCode,
sections: [],
};
const match = sourceCode.match(RX_META);
if (!match) {
throw new Error('can not find metadata');
}
return backgroundWorker.parseUsercssMeta(match[0], match.index)
.catch(err => {
if (err.code) {
const args = ERR_ARGS_IS_LIST.has(err.code) ? drawList(err.args) : err.args;
const message = chrome.i18n.getMessage(`meta_${err.code}`, args);
if (message) {
err.message = message;
}
}
throw err;
})
.then(({metadata}) => {
style.usercssData = metadata;
// https://github.com/openstyles/stylus/issues/560#issuecomment-440561196
for (const [key, value] of Object.entries(GLOBAL_METAS)) {
if (metadata[key] !== undefined) {
style[value || key] = metadata[key];
}
}
return style;
});
}
function drawList(items) {
return items.map(i => i.length === 1 ? JSON.stringify(i) : i).join(', ');
}
/**
* @param {Object} style
* @param {Boolean} [allowErrors=false]
* @returns {(Style | {style: Style, errors: (false|String[])})} - style object
* when allowErrors is falsy or {style, errors} object when allowErrors is truthy
*/
function buildCode(style, allowErrors) {
const match = style.sourceCode.match(RX_META);
return backgroundWorker.compileUsercss(
style.usercssData.preprocessor,
style.sourceCode.slice(0, match.index) + style.sourceCode.slice(match.index + match[0].length),
style.usercssData.vars
)
.then(({sections, errors}) => {
if (!errors.length) errors = false;
if (!sections.length || errors && !allowErrors) {
throw errors || 'Style does not contain any actual CSS to apply.';
}
style.sections = sections;
return allowErrors ? {style, errors} : style;
});
}
function assignVars(style, oldStyle) {
const {usercssData: {vars}} = style;
const {usercssData: {vars: oldVars}} = oldStyle;
if (!vars || !oldVars) {
return Promise.resolve();
}
// The type of var might be changed during the update. Set value to null if the value is invalid.
for (const key of Object.keys(vars)) {
if (oldVars[key] && oldVars[key].value) {
vars[key].value = oldVars[key].value;
}
}
return backgroundWorker.nullifyInvalidVars(vars)
.then(vars => {
style.usercssData.vars = vars;
});
}
})();