Change style structure

This commit is contained in:
eight 2017-09-16 09:24:50 +08:00
parent dc988a413e
commit a0495f466f
8 changed files with 156 additions and 118 deletions

View File

@ -370,7 +370,7 @@ function filterStylesInternal({
// Parse the source and find the duplication // Parse the source and find the duplication
// {id: int, style: object, source: string, checkDup: boolean} // {id: int, style: object, sourceCode: string, checkDup: boolean}
function filterUsercss(req) { function filterUsercss(req) {
let style; let style;
let pendingBuild; let pendingBuild;
@ -381,8 +381,8 @@ function filterUsercss(req) {
function buildMeta() { function buildMeta() {
return new Promise(resolve => { return new Promise(resolve => {
if (req.source) { if (req.sourceCode) {
style = usercss.buildMeta(req.source); style = usercss.buildMeta(req.sourceCode);
} else { } else {
style = req.style; style = req.style;
} }
@ -426,8 +426,8 @@ function saveUsercss(style) {
function buildMeta() { function buildMeta() {
return new Promise(resolve => { return new Promise(resolve => {
if (!style.name || !style.namespace) { if (!style.usercssData) {
resolve(Object.assign(usercss.buildMeta(style.source), style)); resolve(Object.assign(usercss.buildMeta(style.sourceCode), style));
return; return;
} }
resolve(style); resolve(style);
@ -449,16 +449,22 @@ function saveStyle(style) {
let existed; let existed;
let codeIsUpdated; let codeIsUpdated;
if (style.usercss) { const maybeProcess = style.usercssData ? processUsercss() : Promise.resolve();
return processUsercss(style).then(decide);
}
return maybeProcess
.then(maybeUpdate)
.then(maybeImportFix)
.then(decide);
function maybeUpdate() {
if (reason === 'update' || reason === 'update-digest') { if (reason === 'update' || reason === 'update-digest') {
return calcStyleDigest(style).then(digest => { return calcStyleDigest(style).then(digest => {
style.originalDigest = digest; style.originalDigest = digest;
return decide();
}); });
} }
}
function maybeImportFix() {
if (reason === 'import') { if (reason === 'import') {
style.originalDigest = style.originalDigest || style.styleDigest; // TODO: remove in the future style.originalDigest = style.originalDigest || style.styleDigest; // TODO: remove in the future
delete style.styleDigest; // TODO: remove in the future delete style.styleDigest; // TODO: remove in the future
@ -466,9 +472,9 @@ function saveStyle(style) {
delete style.originalDigest; delete style.originalDigest;
} }
} }
return decide(); }
function processUsercss(style) { function processUsercss() {
return findDupUsercss(style) return findDupUsercss(style)
.then(dup => { .then(dup => {
if (!dup) { if (!dup) {
@ -477,11 +483,10 @@ function saveStyle(style) {
if (!id) { if (!id) {
id = dup.id; id = dup.id;
} }
if (reason === 'config') { if (reason !== 'config') {
return;
}
// preserve style.vars during update // preserve style.vars during update
usercss.assignVars(style, dup); usercss.assignVars(style, dup);
}
}) })
.then(() => usercss.buildCode(style)); .then(() => usercss.buildCode(style));
} }
@ -538,7 +543,7 @@ function saveStyle(style) {
style, codeIsUpdated, reason, style, codeIsUpdated, reason,
}); });
} }
if (style.usercss && !existed && reason === 'install') { if (style.usercssData && !existed && reason === 'update') {
// open the editor for usercss with the first install? // open the editor for usercss with the first install?
openEditor(style.id); openEditor(style.id);
} }
@ -563,7 +568,13 @@ function findDupUsercss(style) {
return getStyles({id: style.id}).then(s => s[0]); return getStyles({id: style.id}).then(s => s[0]);
} }
return getStyles().then(styles => return getStyles().then(styles =>
styles.find(s => s.name === style.name && s.namespace === style.namespace) styles.find(target => {
if (!target.usercssData) {
return false;
}
return target.usercssData.name === style.usercssData.name &&
target.usercssData.namespace === style.usercssData.namespace;
})
); );
} }
@ -815,7 +826,8 @@ function normalizeStyleSections({sections}) {
function calcStyleDigest(style) { function calcStyleDigest(style) {
const jsonString = JSON.stringify(normalizeStyleSections(style)); const jsonString = style.usercssData ?
style.sourceCode : JSON.stringify(normalizeStyleSections(style));
const text = new TextEncoder('utf-8').encode(jsonString); const text = new TextEncoder('utf-8').encode(jsonString);
return crypto.subtle.digest('SHA-1', text).then(hex); return crypto.subtle.digest('SHA-1', text).then(hex);

View File

@ -64,7 +64,7 @@ var updater = {
'ignoreDigest' option is set on the second manual individual update check on the manage page. 'ignoreDigest' option is set on the second manual individual update check on the manage page.
*/ */
const maybeUpdate = style.usercss ? maybeUpdateUsercss : maybeUpdateUSO; const maybeUpdate = style.usercssData ? maybeUpdateUsercss : maybeUpdateUSO;
return (ignoreDigest ? Promise.resolve() : calcStyleDigest(style)) return (ignoreDigest ? Promise.resolve() : calcStyleDigest(style))
.then(checkIfEdited) .then(checkIfEdited)
.then(maybeUpdate) .then(maybeUpdate)
@ -83,9 +83,7 @@ var updater = {
if (ignoreDigest) { if (ignoreDigest) {
return; return;
} }
if (style.usercss && style.edited) { if (style.originalDigest && style.originalDigest !== digest) {
return Promise.reject(updater.EDITED);
} else if (style.originalDigest && style.originalDigest !== digest) {
return Promise.reject(updater.EDITED); return Promise.reject(updater.EDITED);
} }
} }
@ -106,12 +104,14 @@ var updater = {
function maybeUpdateUsercss() { function maybeUpdateUsercss() {
return download(style.updateUrl).then(text => { return download(style.updateUrl).then(text => {
const json = usercss.buildMeta(text); const json = usercss.buildMeta(text);
const {usercssData: {version}} = style;
const {usercssData: {version: newVersion}} = json;
// re-install is invalid in a soft upgrade // re-install is invalid in a soft upgrade
if (semverCompare(style.version, json.version) === 0 && !ignoreDigest) { if (semverCompare(version, newVersion) === 0 && !ignoreDigest) {
return Promise.reject(updater.SAME_VERSION); return Promise.reject(updater.SAME_VERSION);
} }
// downgrade is always invalid // downgrade is always invalid
if (semverCompare(style.version, json.version) > 0) { if (semverCompare(version, newVersion) > 0) {
return Promise.reject(updater.ERROR_VERSION); return Promise.reject(updater.ERROR_VERSION);
} }
return json; return json;
@ -121,7 +121,7 @@ var updater = {
function maybeSave(json) { function maybeSave(json) {
json.id = style.id; json.id = style.id;
// no need to compare section code for usercss, they are built dynamically // no need to compare section code for usercss, they are built dynamically
if (!json.usercss) { if (!json.usercssData) {
if (!styleJSONseemsValid(json)) { if (!styleJSONseemsValid(json)) {
return Promise.reject(updater.ERROR_JSON); return Promise.reject(updater.ERROR_JSON);
} }

View File

@ -7,7 +7,7 @@ let pendingResource;
function install(style) { function install(style) {
const request = Object.assign(style, { const request = Object.assign(style, {
method: 'saveUsercss', method: 'saveUsercss',
reason: 'install', reason: 'update',
updateUrl: location.href updateUrl: location.href
}); });
return runtimeSend(request) return runtimeSend(request)
@ -55,7 +55,9 @@ function getAppliesTo(style) {
function initInstallPage({style, dup}, sourceLoader) { function initInstallPage({style, dup}, sourceLoader) {
return pendingResource.then(() => { return pendingResource.then(() => {
const versionTest = dup && semverCompare(style.version, dup.version); const data = style.usercssData;
const dupData = dup && dup.usercssData;
const versionTest = dup && semverCompare(data.version, dupData.version);
document.body.textContent = ''; document.body.textContent = '';
document.body.appendChild(buildPage()); document.body.appendChild(buildPage());
@ -65,13 +67,15 @@ function initInstallPage({style, dup}, sourceLoader) {
$('.actions') $('.actions')
); );
} }
$('.code').textContent = style.source; $('.code').textContent = style.sourceCode;
$('button.install').onclick = () => { $('button.install').onclick = () => {
if (dup) { if (dup) {
if (confirm(chrome.i18n.getMessage('styleInstallOverwrite', [style.name, dup.version, style.version]))) { if (confirm(chrome.i18n.getMessage('styleInstallOverwrite', [
data.name, dupData.version, data.version
]))) {
install(style); install(style);
} }
} else if (confirm(chrome.i18n.getMessage('styleInstall', [style.name]))) { } else if (confirm(chrome.i18n.getMessage('styleInstall', [data.name]))) {
install(style); install(style);
} }
}; };
@ -87,19 +91,19 @@ function initInstallPage({style, dup}, sourceLoader) {
$element({tag: 'button', className: 'install', textContent: installButtonLabel()}) $element({tag: 'button', className: 'install', textContent: installButtonLabel()})
]}), ]}),
$element({tag: 'h1', appendChild: [ $element({tag: 'h1', appendChild: [
style.name, data.name,
$element({tag: 'small', className: 'meta-version', textContent: style.version}) $element({tag: 'small', className: 'meta-version', textContent: data.version})
]}), ]}),
$element({tag: 'p', textContent: style.description}), $element({tag: 'p', textContent: data.description}),
style.author && $element({tag: 'h3', textContent: t('author')}), data.author && $element({tag: 'h3', textContent: t('author')}),
style.author, data.author,
style.license && $element({tag: 'h3', textContent: t('license')}), data.license && $element({tag: 'h3', textContent: t('license')}),
style.license, data.license,
$element({tag: 'h3', textContent: t('appliesLabel')}), $element({tag: 'h3', textContent: t('appliesLabel')}),
$element({tag: 'ul', appendChild: getAppliesTo(style).map( $element({tag: 'ul', appendChild: getAppliesTo(style).map(
pattern => $element({tag: 'li', textContent: pattern}) pattern => $element({tag: 'li', textContent: pattern})
)}), )}),
externalLink(style), externalLink(),
]}), ]}),
$element({className: 'main', appendChild: [ $element({className: 'main', appendChild: [
$element({className: 'code'}) $element({className: 'code'})
@ -107,13 +111,13 @@ function initInstallPage({style, dup}, sourceLoader) {
]}); ]});
} }
function externalLink(style) { function externalLink() {
const urls = []; const urls = [];
if (style.url) { if (data.homepageURL) {
urls.push([style.url, t('externalHomepage')]); urls.push([data.homepageURL, t('externalHomepage')]);
} }
if (style.supportURL) { if (data.supportURL) {
urls.push([style.supportURL, t('externalSupport')]); urls.push([data.supportURL, t('externalSupport')]);
} }
if (urls.length) { if (urls.length) {
return $element({appendChild: [ return $element({appendChild: [
@ -139,7 +143,7 @@ function initLiveReload(sourceLoader) {
return runtimeSend({ return runtimeSend({
method: 'saveUsercss', method: 'saveUsercss',
id: installed.id, id: installed.id,
source: source sourceCode: source
}).then(() => { }).then(() => {
$$('.main .warning').forEach(e => e.remove()); $$('.main .warning').forEach(e => e.remove());
}).catch(err => { }).catch(err => {
@ -267,7 +271,7 @@ function initUsercssInstall() {
.then(() => .then(() =>
runtimeSend({ runtimeSend({
method: 'filterUsercss', method: 'filterUsercss',
source: sourceLoader.source(), sourceCode: sourceLoader.source(),
checkDup: true checkDup: true
}) })
) )

View File

@ -1352,7 +1352,7 @@ function setStyleMeta(style) {
function initWithStyle(request) { function initWithStyle(request) {
if (!editor) { if (!editor) {
if (!request.style.usercss) { if (!request.style.usercssData) {
initWithSectionStyle(request); initWithSectionStyle(request);
} else { } else {
editor = createSourceEditor(request.style); editor = createSourceEditor(request.style);

View File

@ -26,7 +26,7 @@ function createSourceEditor(style) {
); );
// draw CodeMirror // draw CodeMirror
$('#sections textarea').value = style.source; $('#sections textarea').value = style.sourceCode;
const cm = CodeMirror.fromTextArea($('#sections textarea')); const cm = CodeMirror.fromTextArea($('#sections textarea'));
// too many functions depend on this global // too many functions depend on this global
editors.push(cm); editors.push(cm);
@ -378,8 +378,8 @@ function createSourceEditor(style) {
// source // source
cm.on('change', () => { cm.on('change', () => {
const value = cm.getValue(); const value = cm.getValue();
dirty.modify('source', style.source, value); dirty.modify('source', style.sourceCode, value);
style.source = value; style.sourceCode = value;
updateLintReportIfEnabled(cm); updateLintReportIfEnabled(cm);
}); });
@ -402,10 +402,11 @@ function createSourceEditor(style) {
$('#name').value = style.name; $('#name').value = style.name;
$('#enabled').checked = style.enabled; $('#enabled').checked = style.enabled;
$('#url').href = style.url; $('#url').href = style.url;
cm.setOption('mode', MODE[style.preprocessor] || 'css'); const {usercssData: {preprocessor}} = style;
CodeMirror.autoLoadMode(cm, style.preprocessor || 'css'); cm.setOption('mode', MODE[preprocessor] || 'css');
CodeMirror.autoLoadMode(cm, MODE[preprocessor] || 'css');
// beautify only works with regular CSS // beautify only works with regular CSS
$('#beautify').disabled = Boolean(style.preprocessor); $('#beautify').disabled = MODE[preprocessor] && MODE[preprocessor] !== 'css';
updateTitle(); updateTitle();
} }
@ -417,9 +418,9 @@ function createSourceEditor(style) {
function replaceStyle(newStyle) { function replaceStyle(newStyle) {
style = deepCopy(newStyle); style = deepCopy(newStyle);
updateMetas(); updateMetas();
if (style.source !== cm.getValue()) { if (style.sourceCode !== cm.getValue()) {
const cursor = cm.getCursor(); const cursor = cm.getCursor();
cm.setValue(style.source); cm.setValue(style.sourceCode);
cm.setCursor(cursor); cm.setCursor(cursor);
} }
dirty.clear(); dirty.clear();
@ -448,8 +449,7 @@ function createSourceEditor(style) {
reason: 'editSave', reason: 'editSave',
id: style.id, id: style.id,
enabled: style.enabled, enabled: style.enabled,
edited: dirty.has('source'), sourceCode: style.sourceCode
source: style.source
}; };
return onBackgroundReady().then(() => BG.saveUsercss(req)) return onBackgroundReady().then(() => BG.saveUsercss(req))
.then(result => { .then(result => {

View File

@ -4,10 +4,23 @@
// eslint-disable-next-line no-var // eslint-disable-next-line no-var
var usercss = (function () { var usercss = (function () {
const METAS = [ // true for global, false for private
'author', 'advanced', 'description', 'homepageURL', 'icon', 'license', 'name', const METAS = {
'namespace', 'noframes', 'preprocessor', 'supportURL', 'var', 'version' __proto__: null,
]; author: true,
advanced: false,
description: true,
homepageURL: false,
// icon: false,
license: false,
name: true,
namespace: false,
// noframes: false,
preprocessor: false,
supportURL: false,
'var': false,
version: false
};
const META_VARS = ['text', 'color', 'checkbox', 'select', 'dropdown', 'image']; const META_VARS = ['text', 'color', 'checkbox', 'select', 'dropdown', 'image'];
@ -222,7 +235,7 @@ var usercss = (function () {
parseStringToEnd(state); parseStringToEnd(state);
result.default = state.value; result.default = state.value;
} }
state.style.vars[result.name] = result; state.usercssData.vars[result.name] = result;
} }
function parseEOT(state) { function parseEOT(state) {
@ -319,72 +332,71 @@ var usercss = (function () {
return s; return s;
} }
function _buildMeta(source) { function _buildMeta(sourceCode) {
const style = { const usercssData = {
name: null, vars: {}
usercss: true,
version: null,
source: source,
edited: false,
enabled: true,
sections: [],
vars: {},
preprocessor: null,
noframes: false
}; };
const text = getMetaSource(source); const style = {
enabled: true,
sourceCode,
sections: [],
usercssData
};
const text = getMetaSource(sourceCode);
const re = /@(\w+)\s+/mg; const re = /@(\w+)\s+/mg;
const state = {style, re, text}; const state = {style, re, text, usercssData};
let match; let match;
while ((match = re.exec(text))) { while ((match = re.exec(text))) {
state.key = match[1]; state.key = match[1];
if (!METAS.includes(state.key)) { if (!(state.key in METAS)) {
continue; continue;
} }
if (state.key === 'noframes') { if (state.key === 'var' || state.key === 'advanced') {
style.noframes = true;
} else if (state.key === 'var' || state.key === 'advanced') {
if (state.key === 'advanced') { if (state.key === 'advanced') {
state.maybeUSO = true; state.maybeUSO = true;
} }
parseVar(state); parseVar(state);
} else { } else {
parseStringToEnd(state); parseStringToEnd(state);
if (state.key === 'homepageURL') { usercssData[state.key] = state.value;
style.url = state.value; }
} else { if (METAS[state.key]) {
style[state.key] = state.value; style[state.key] = usercssData[state.key];
} }
} }
if (state.maybeUSO && !usercssData.preprocessor) {
usercssData.preprocessor = 'uso';
} }
if (state.maybeUSO && !style.preprocessor) { if (usercssData.homepageURL) {
style.preprocessor = 'uso'; style.url = usercssData.homepageURL;
} }
return style; return style;
} }
function buildCode(style) { function buildCode(style) {
const {usercssData: {preprocessor, vars}, sourceCode} = style;
let builder; let builder;
if (style.preprocessor) { if (preprocessor) {
if (!BUILDER.hasOwnProperty(style.preprocessor)) { if (!BUILDER[preprocessor]) {
return Promise.reject(new Error(`Unsupported preprocessor: ${style.preprocessor}`)); return Promise.reject(new Error(`Unsupported preprocessor: ${preprocessor}`));
} }
builder = BUILDER[style.preprocessor]; builder = BUILDER[preprocessor];
} else { } else {
builder = BUILDER.default; builder = BUILDER.default;
} }
const vars = simpleVars(style.vars); const sVars = simpleVars(vars);
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
// preprocess // preprocess
if (builder.preprocess) { if (builder.preprocess) {
return builder.preprocess(style.source, vars); return builder.preprocess(sourceCode, sVars);
} }
return style.source; return sourceCode;
}).then(mozStyle => }).then(mozStyle =>
// moz-parser // moz-parser
loadScript('/js/moz-parser.js').then(() => loadScript('/js/moz-parser.js').then(() =>
@ -395,7 +407,7 @@ var usercss = (function () {
).then(() => { ).then(() => {
// postprocess // postprocess
if (builder.postprocess) { if (builder.postprocess) {
return builder.postprocess(style.sections, vars); return builder.postprocess(style.sections, sVars);
} }
}).then(() => style); }).then(() => style);
} }
@ -414,22 +426,23 @@ var usercss = (function () {
} }
function validate(style) { function validate(style) {
const {usercssData: data} = style;
// mandatory fields // mandatory fields
for (const prop of ['name', 'namespace', 'version']) { for (const prop of ['name', 'namespace', 'version']) {
if (!style[prop]) { if (!data[prop]) {
throw new Error(chrome.i18n.getMessage('styleMissingMeta', prop)); throw new Error(chrome.i18n.getMessage('styleMissingMeta', prop));
} }
} }
// validate version // validate version
semverCompare(style.version, '0.0.0'); semverCompare(data.version, '0.0.0');
// validate URLs // validate URLs
validUrl(style.url); validUrl(data.homepageURL);
validUrl(style.supportURL); validUrl(data.supportURL);
// validate vars // validate vars
for (const key of Object.keys(style.vars)) { for (const key of Object.keys(data.vars)) {
validVar(style.vars[key]); validVar(data.vars[key]);
} }
} }
@ -456,14 +469,16 @@ var usercss = (function () {
} }
function assignVars(style, old) { function assignVars(style, old) {
const {usercssData: {vars}} = style;
const {usercssData: {vars: oldVars}} = old;
// The type of var might be changed during the update. Set value to null if the value is invalid. // 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(style.vars)) { for (const key of Object.keys(vars)) {
if (old.vars[key] && old.vars[key].value) { if (oldVars[key] && oldVars[key].value) {
style.vars[key].value = old.vars[key].value; vars[key].value = oldVars[key].value;
try { try {
validVar(style.vars[key], 'value'); validVar(vars[key], 'value');
} catch (e) { } catch (e) {
style.vars[key].value = null; vars[key].value = null;
} }
} }
} }

View File

@ -27,7 +27,7 @@ function configDialog(style) {
function buildConfigForm() { function buildConfigForm() {
const labels = []; const labels = [];
const vars = deepCopy(style.vars); const vars = deepCopy(style.usercssData.vars);
for (const key of Object.keys(vars)) { for (const key of Object.keys(vars)) {
const va = vars[key]; const va = vars[key];
let appendChild; let appendChild;

View File

@ -193,7 +193,7 @@ function createStyleElement({style, name}) {
if (style.updateUrl && newUI.enabled) { if (style.updateUrl && newUI.enabled) {
$('.actions', entry).appendChild(template.updaterIcons.cloneNode(true)); $('.actions', entry).appendChild(template.updaterIcons.cloneNode(true));
} }
if (style.vars && Object.keys(style.vars).length && newUI.enabled) { if (shouldShowConfig() && newUI.enabled) {
$('.actions', entry).appendChild(template.configureIcon.cloneNode(true)); $('.actions', entry).appendChild(template.configureIcon.cloneNode(true));
} }
@ -202,6 +202,13 @@ function createStyleElement({style, name}) {
createStyleTargetsElement({entry, style, postponeFavicons: name}); createStyleTargetsElement({entry, style, postponeFavicons: name});
return entry; return entry;
function shouldShowConfig() {
if (!style.usercssData) {
return false;
}
return Object.keys(style.usercssData.vars).length > 0;
}
} }
@ -293,7 +300,7 @@ Object.assign(handleEvent, {
} }
style.reason = 'config'; style.reason = 'config';
for (const key of keys) { for (const key of keys) {
style.vars[key].value = vars[key].value; style.usercssData.vars[key].value = vars[key].value;
} }
saveStyleSafe(style); saveStyleSafe(style);
}); });