recognize usercss @updateURL
* installation URL is preferred - same behavior as before * @updateURL is used when the style was drag'n'dropped into the manage page because there's no real URL in this case * install-usercss page shows the new update URL, which is set as per the above, under the checkbox that enables updates
This commit is contained in:
parent
0659ff6233
commit
f337e18515
|
@ -78,8 +78,8 @@ var usercssHelper = (() => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openInstallPage(tab, {url = tab.url, direct} = {}) {
|
function openInstallPage(tab, {url = tab.url, direct, downloaded} = {}) {
|
||||||
if (direct) {
|
if (direct && !downloaded) {
|
||||||
prefetchCodeForInstallation(tab.id, url);
|
prefetchCodeForInstallation(tab.id, url);
|
||||||
}
|
}
|
||||||
return wrapReject(openURL({
|
return wrapReject(openURL({
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
<input type="checkbox">
|
<input type="checkbox">
|
||||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||||
<span></span>
|
<span></span>
|
||||||
|
<p></p>
|
||||||
</label>
|
</label>
|
||||||
<label class="live-reload">
|
<label class="live-reload">
|
||||||
<input type="checkbox">
|
<input type="checkbox">
|
||||||
|
|
|
@ -193,6 +193,17 @@ h2.installed.active {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.set-update-url {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.set-update-url p {
|
||||||
|
word-break: break-all;
|
||||||
|
opacity: .5;
|
||||||
|
width: 100%;
|
||||||
|
margin: .25em 0 .25em;
|
||||||
|
}
|
||||||
|
|
||||||
.external {
|
.external {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -283,24 +283,28 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
// set updateUrl
|
// set updateUrl
|
||||||
const setUpdate = $('.set-update-url input[type=checkbox]');
|
const checker = $('.set-update-url input[type=checkbox]');
|
||||||
const updateUrl = new URL(params.get('updateUrl'));
|
// prefer the installation URL unless drag'n'dropped on the manage page
|
||||||
|
const installationUrl = (params.get('updateUrl') || '').replace(/^blob.+/, '');
|
||||||
|
const updateUrl = new URL(installationUrl || style.updateUrl || 'foo:bar');
|
||||||
$('.set-update-url > span').textContent = t('installUpdateFromLabel');
|
$('.set-update-url > span').textContent = t('installUpdateFromLabel');
|
||||||
if (dup && dup.updateUrl === updateUrl.href) {
|
if (dup && dup.updateUrl === updateUrl.href) {
|
||||||
setUpdate.checked = true;
|
checker.checked = true;
|
||||||
// there is no way to "unset" updateUrl, you can only overwrite it.
|
// there is no way to "unset" updateUrl, you can only overwrite it.
|
||||||
setUpdate.disabled = true;
|
checker.disabled = true;
|
||||||
|
} else if (updateUrl.protocol === 'foo:') {
|
||||||
|
// drag'n'dropped on the manage page and the style doesn't have @updateURL
|
||||||
|
checker.disabled = true;
|
||||||
} else if (updateUrl.protocol !== 'file:') {
|
} else if (updateUrl.protocol !== 'file:') {
|
||||||
setUpdate.checked = true;
|
checker.checked = true;
|
||||||
style.updateUrl = updateUrl.href;
|
style.updateUrl = updateUrl.href;
|
||||||
}
|
}
|
||||||
setUpdate.onchange = e => {
|
checker.onchange = () => {
|
||||||
if (e.target.checked) {
|
style.updateUrl = checker.checked ? updateUrl.href : null;
|
||||||
style.updateUrl = updateUrl.href;
|
|
||||||
} else {
|
|
||||||
delete style.updateUrl;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
checker.onchange();
|
||||||
|
$('.set-update-url p').textContent = updateUrl.href.length < 300 ? updateUrl.href :
|
||||||
|
updateUrl.href.slice(0, 300) + '...';
|
||||||
|
|
||||||
if (!port) {
|
if (!port) {
|
||||||
return;
|
return;
|
||||||
|
|
120
js/usercss.js
120
js/usercss.js
|
@ -3,25 +3,34 @@
|
||||||
|
|
||||||
// eslint-disable-next-line no-var
|
// eslint-disable-next-line no-var
|
||||||
var usercss = (() => {
|
var usercss = (() => {
|
||||||
// true for global, false for private
|
// true = global
|
||||||
const METAS = {
|
// false or 0 = private
|
||||||
__proto__: null,
|
// <string> = global key name
|
||||||
author: true,
|
// <function> = (style, newValue)
|
||||||
advanced: false,
|
const KNOWN_META = new Map([
|
||||||
description: true,
|
['author', true],
|
||||||
homepageURL: false,
|
['advanced', 0],
|
||||||
// icon: false,
|
['description', true],
|
||||||
license: false,
|
['homepageURL', 'url'],
|
||||||
name: true,
|
['icon', 0],
|
||||||
namespace: false,
|
['license', 0],
|
||||||
// noframes: false,
|
['name', true],
|
||||||
preprocessor: false,
|
['namespace', 0],
|
||||||
supportURL: false,
|
//['noframes', 0],
|
||||||
'var': false,
|
['preprocessor', 0],
|
||||||
version: false
|
['supportURL', 0],
|
||||||
};
|
['updateURL', (style, newValue) => {
|
||||||
|
// always preserve locally installed style's updateUrl
|
||||||
|
if (!/^file:/.test(style.updateUrl)) {
|
||||||
|
style.updateUrl = newValue;
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
['var', 0],
|
||||||
|
['version', 0],
|
||||||
|
]);
|
||||||
|
const MANDATORY_META = ['name', 'namespace', 'version'];
|
||||||
const META_VARS = ['text', 'color', 'checkbox', 'select', 'dropdown', 'image'];
|
const META_VARS = ['text', 'color', 'checkbox', 'select', 'dropdown', 'image'];
|
||||||
|
const META_URLS = [...KNOWN_META.keys()].filter(k => k.endsWith('URL'));
|
||||||
|
|
||||||
const BUILDER = {
|
const BUILDER = {
|
||||||
default: {
|
default: {
|
||||||
|
@ -221,7 +230,7 @@ var usercss = (() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.usercssData.vars[result.name] = result;
|
state.usercssData.vars[result.name] = result;
|
||||||
validVar(result);
|
validateVar(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOption(label, value) {
|
function createOption(label, value) {
|
||||||
|
@ -407,28 +416,39 @@ var usercss = (() => {
|
||||||
function doParse() {
|
function doParse() {
|
||||||
let match;
|
let match;
|
||||||
while ((match = re.exec(text))) {
|
while ((match = re.exec(text))) {
|
||||||
state.key = match[1];
|
const key = state.key = match[1];
|
||||||
if (!(state.key in METAS)) {
|
const route = KNOWN_META.get(key);
|
||||||
|
if (route === undefined) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (state.key === 'var' || state.key === 'advanced') {
|
if (key === 'var' || key === 'advanced') {
|
||||||
if (state.key === 'advanced') {
|
if (key === 'advanced') {
|
||||||
state.maybeUSO = true;
|
state.maybeUSO = true;
|
||||||
}
|
}
|
||||||
parseVar(state);
|
parseVar(state);
|
||||||
} else {
|
} else {
|
||||||
parseStringToEnd(state);
|
parseStringToEnd(state);
|
||||||
usercssData[state.key] = state.value;
|
usercssData[key] = state.value;
|
||||||
}
|
}
|
||||||
if (state.key === 'version') {
|
let value = state.value;
|
||||||
usercssData[state.key] = normalizeVersion(usercssData[state.key]);
|
if (key === 'version') {
|
||||||
validVersion(usercssData[state.key]);
|
value = usercssData[key] = normalizeVersion(value);
|
||||||
|
validateVersion(value);
|
||||||
}
|
}
|
||||||
if (METAS[state.key]) {
|
if (META_URLS.includes(key)) {
|
||||||
style[state.key] = usercssData[state.key];
|
validateUrl(key, value);
|
||||||
}
|
}
|
||||||
if (state.key === 'homepageURL' || state.key === 'supportURL') {
|
switch (typeof route) {
|
||||||
validUrl(usercssData[state.key]);
|
case 'function':
|
||||||
|
route(style, value);
|
||||||
|
break;
|
||||||
|
case 'string':
|
||||||
|
style[route] = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (route) {
|
||||||
|
style[key] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -446,12 +466,8 @@ var usercss = (() => {
|
||||||
if (state.maybeUSO && !usercssData.preprocessor) {
|
if (state.maybeUSO && !usercssData.preprocessor) {
|
||||||
usercssData.preprocessor = 'uso';
|
usercssData.preprocessor = 'uso';
|
||||||
}
|
}
|
||||||
if (usercssData.homepageURL) {
|
|
||||||
style.url = usercssData.homepageURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
validate(style);
|
|
||||||
|
|
||||||
|
validateStyle(style);
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,42 +521,32 @@ var usercss = (() => {
|
||||||
return va[prop];
|
return va[prop];
|
||||||
}
|
}
|
||||||
|
|
||||||
function validate(style) {
|
function validateStyle({usercssData: data}) {
|
||||||
const {usercssData: data} = style;
|
for (const prop of MANDATORY_META) {
|
||||||
// mandatory fields
|
|
||||||
for (const prop of ['name', 'namespace', 'version']) {
|
|
||||||
if (!data[prop]) {
|
if (!data[prop]) {
|
||||||
throw new Error(chrome.i18n.getMessage('styleMissingMeta', prop));
|
throw new Error(chrome.i18n.getMessage('styleMissingMeta', prop));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// validate version
|
validateVersion(data.version);
|
||||||
validVersion(data.version);
|
META_URLS.forEach(k => validateUrl(k, data[k]));
|
||||||
|
Object.keys(data.vars).forEach(k => validateVar(data.vars[k]));
|
||||||
// validate URLs
|
|
||||||
validUrl(data.homepageURL);
|
|
||||||
validUrl(data.supportURL);
|
|
||||||
|
|
||||||
// validate vars
|
|
||||||
for (const key of Object.keys(data.vars)) {
|
|
||||||
validVar(data.vars[key]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function validVersion(version) {
|
function validateVersion(version) {
|
||||||
semverCompare(version, '0.0.0');
|
semverCompare(version, '0.0.0');
|
||||||
}
|
}
|
||||||
|
|
||||||
function validUrl(url) {
|
function validateUrl(key, url) {
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
url = new URL(url);
|
url = new URL(url);
|
||||||
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
if (!/^https?:/.test(url.protocol)) {
|
||||||
throw new Error(`${url.protocol} is not a valid protocol`);
|
throw new Error(`${url.protocol} is not a valid protocol in ${key}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validVar(va, value = 'default') {
|
function validateVar(va, value = 'default') {
|
||||||
if (va.type === 'select' || va.type === 'dropdown') {
|
if (va.type === 'select' || va.type === 'dropdown') {
|
||||||
if (va.options.every(o => o.name !== va[value])) {
|
if (va.options.every(o => o.name !== va[value])) {
|
||||||
throw new Error(chrome.i18n.getMessage('styleMetaErrorSelectValueMismatch'));
|
throw new Error(chrome.i18n.getMessage('styleMetaErrorSelectValueMismatch'));
|
||||||
|
@ -560,7 +566,7 @@ var usercss = (() => {
|
||||||
if (oldVars[key] && oldVars[key].value) {
|
if (oldVars[key] && oldVars[key].value) {
|
||||||
vars[key].value = oldVars[key].value;
|
vars[key].value = oldVars[key].value;
|
||||||
try {
|
try {
|
||||||
validVar(vars[key], 'value');
|
validateVar(vars[key], 'value');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
vars[key].value = null;
|
vars[key].value = null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user