warn when paste-importing usercss with @preprocessor, #1082

This commit is contained in:
tophf 2020-10-26 17:37:31 +03:00 committed by GitHub
commit 1a7b51be6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 64 additions and 48 deletions

View File

@ -468,6 +468,12 @@
"message": "Import", "message": "Import",
"description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)" "description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)"
}, },
"importPreprocessor": {
"message": "Style with a <code>@preprocessor</code> won't work in the classic mode. You can switch the editor to Usercss mode: 1) open the style manager, 2) enable \"as Usercss\" checkbox, 3) click \"Write new style\"\n\nImport now anyway?"
},
"importPreprocessorTitle": {
"message": "Potential problem due to @preprocessor"
},
"importReplaceLabel": { "importReplaceLabel": {
"message": "Overwrite style", "message": "Overwrite style",
"description": "Label for the button to import and overwrite current style" "description": "Label for the button to import and overwrite current style"

View File

@ -8,6 +8,7 @@ const usercssHelper = (() => {
API_METHODS.configUsercssVars = configUsercssVars; API_METHODS.configUsercssVars = configUsercssVars;
API_METHODS.buildUsercss = build; API_METHODS.buildUsercss = build;
API_METHODS.buildUsercssMeta = buildMeta;
API_METHODS.findUsercss = find; API_METHODS.findUsercss = find;
function buildMeta(style) { function buildMeta(style) {

View File

@ -176,10 +176,9 @@ function createSection({
}); });
cm.on('paste', (cm, event) => { cm.on('paste', (cm, event) => {
const text = event.clipboardData.getData('text') || ''; const text = event.clipboardData.getData('text') || '';
if ( if (/@-moz-document/i.test(text) &&
text.includes('@-moz-document') && /@-moz-document\s+(url|url-prefix|domain|regexp)\(/i
text.replace(/\/\*[\s\S]*?(?:\*\/|$)/g, '') .test(text.replace(/\/\*([^*]|\*(?!\/))*(\*\/|$)/g, ''))
.match(/@-moz-document[\s\r\n]+(url|url-prefix|domain|regexp)\(/)
) { ) {
event.preventDefault(); event.preventDefault();
showMozillaFormatImport(text); showMozillaFormatImport(text);

View File

@ -48,7 +48,7 @@ function createSectionsEditor(editorBase) {
let sectionOrder = ''; let sectionOrder = '';
let headerOffset; // in compact mode the header is at the top so it reduces the available height let headerOffset; // in compact mode the header is at the top so it reduces the available height
const ready = initSections(style.sections, {isFirstInit: true}); const ready = initSections(style.sections, {pristine: true});
const livePreview = createLivePreview(); const livePreview = createLivePreview();
livePreview.show(Boolean(style.id)); livePreview.show(Boolean(style.id));
@ -333,24 +333,37 @@ function createSectionsEditor(editorBase) {
'Shift-Ctrl-Enter': () => doImport({replaceOldStyle: true}), 'Shift-Ctrl-Enter': () => doImport({replaceOldStyle: true}),
}; };
function doImport({replaceOldStyle = false}) { async function doImport({replaceOldStyle = false}) {
lockPageUI(true); lockPageUI(true);
API.parseCss({code: popup.codebox.getValue().trim()}) try {
.then(({sections, errors}) => { const code = popup.codebox.getValue().trim();
if (!/==userstyle==/i.test(code) ||
!await getPreprocessor(code) ||
await messageBox.confirm(
t('importPreprocessor'), 'pre-line',
t('importPreprocessorTitle'))
) {
const {sections, errors} = await API.parseCss({code});
// shouldn't happen but just in case // shouldn't happen but just in case
if (!sections.length || errors.length) { if (!sections.length || errors.length) {
throw errors; throw errors;
} }
if (replaceOldStyle) { await initSections(sections, {
return replaceSections(sections); replace: replaceOldStyle,
} focusOn: replaceOldStyle ? 0 : false,
return initSections(sections, {focusOn: false}); });
})
.then(() => {
$('.dismiss').dispatchEvent(new Event('click')); $('.dismiss').dispatchEvent(new Event('click'));
}) }
.catch(showError) } catch (err) {
.then(() => lockPageUI(false)); showError(err);
}
lockPageUI(false);
}
async function getPreprocessor(code) {
try {
return (await API.buildUsercssMeta({sourceCode: code})).usercssData.preprocessor;
} catch (e) {}
} }
function lockPageUI(locked) { function lockPageUI(locked) {
@ -451,8 +464,14 @@ function createSectionsEditor(editorBase) {
function initSections(originalSections, { function initSections(originalSections, {
focusOn = 0, focusOn = 0,
isFirstInit, replace = false,
pristine = false,
} = {}) { } = {}) {
if (replace) {
sections.forEach(s => s.remove(true));
sections.length = 0;
container.textContent = '';
}
let done; let done;
const total = originalSections.length; const total = originalSections.length;
originalSections = originalSections.slice(); originalSections = originalSections.slice();
@ -464,7 +483,7 @@ function createSectionsEditor(editorBase) {
const t0 = performance.now(); const t0 = performance.now();
while (originalSections.length && performance.now() - t0 < 100) { while (originalSections.length && performance.now() - t0 < 100) {
insertSectionAfter(originalSections.shift(), undefined, forceRefresh); insertSectionAfter(originalSections.shift(), undefined, forceRefresh);
if (isFirstInit) dirty.clear(); if (pristine) dirty.clear();
if (focusOn !== false && sections[focusOn]) { if (focusOn !== false && sections[focusOn]) {
sections[focusOn].cm.focus(); sections[focusOn].cm.focus();
focusOn = false; focusOn = false;
@ -572,36 +591,21 @@ function createSectionsEditor(editorBase) {
updateSectionOrder(); updateSectionOrder();
} }
function replaceSections(...args) { async function replaceStyle(newStyle, codeIsUpdated) {
for (const section of sections) {
section.remove(true);
}
sections.length = 0;
container.textContent = '';
return initSections(...args);
}
function replaceStyle(newStyle, codeIsUpdated) {
dirty.clear('name'); dirty.clear('name');
// FIXME: avoid recreating all editors? // FIXME: avoid recreating all editors?
reinit().then(() => { if (codeIsUpdated !== false) {
Object.assign(style, newStyle); await initSections(newStyle.sections, {replace: true, pristine: true});
updateHeader();
dirty.clear();
// Go from new style URL to edit style URL
if (location.href.indexOf('id=') === -1 && style.id) {
history.replaceState({}, document.title, 'edit.html?id=' + style.id);
$('#heading').textContent = t('editStyleHeading');
}
livePreview.show(Boolean(style.id));
updateLivePreview();
});
function reinit() {
if (codeIsUpdated !== false) {
return replaceSections(newStyle.sections, {isFirstInit: true});
}
return Promise.resolve();
} }
Object.assign(style, newStyle);
updateHeader();
dirty.clear();
// Go from new style URL to edit style URL
if (location.href.indexOf('id=') === -1 && style.id) {
history.replaceState({}, document.title, 'edit.html?id=' + style.id);
$('#heading').textContent = t('editStyleHeading');
}
livePreview.show(Boolean(style.id));
updateLivePreview();
} }
} }

View File

@ -56,6 +56,10 @@
text-align: left; text-align: left;
} }
#message-box.pre-line #message-box-contents {
white-space: pre-line;
}
#message-box-title { #message-box-title {
font-weight: bold; font-weight: bold;
background-color: rgb(145, 208, 198); background-color: rgb(145, 208, 198);

View File

@ -168,10 +168,12 @@ messageBox.alert = (contents, className, title) =>
/** /**
* @param {String|Node|Array<String|Node>} contents * @param {String|Node|Array<String|Node>} contents
* @param {String} [className] like 'pre' for monospace font * @param {String} [className] like 'pre' for monospace font
* @param {String} [title]
* @returns {Promise<Boolean>} resolves to true when confirmed * @returns {Promise<Boolean>} resolves to true when confirmed
*/ */
messageBox.confirm = (contents, className) => messageBox.confirm = (contents, className, title) =>
messageBox({ messageBox({
title,
contents, contents,
className: `center ${className || ''}`, className: `center ${className || ''}`,
buttons: [t('confirmYes'), t('confirmNo')] buttons: [t('confirmYes'), t('confirmNo')]