diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index ea18706f..51dddb1a 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -468,6 +468,12 @@
"message": "Import",
"description": "Label for the button to import a style ('edit' page) or all styles ('manage' page)"
},
+ "importPreprocessor": {
+ "message": "Style with a @preprocessor
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": {
"message": "Overwrite style",
"description": "Label for the button to import and overwrite current style"
diff --git a/background/usercss-helper.js b/background/usercss-helper.js
index 00b3a99b..58dc2233 100644
--- a/background/usercss-helper.js
+++ b/background/usercss-helper.js
@@ -8,6 +8,7 @@ const usercssHelper = (() => {
API_METHODS.configUsercssVars = configUsercssVars;
API_METHODS.buildUsercss = build;
+ API_METHODS.buildUsercssMeta = buildMeta;
API_METHODS.findUsercss = find;
function buildMeta(style) {
diff --git a/edit/sections-editor-section.js b/edit/sections-editor-section.js
index 5b07875e..dce07e93 100644
--- a/edit/sections-editor-section.js
+++ b/edit/sections-editor-section.js
@@ -176,10 +176,9 @@ function createSection({
});
cm.on('paste', (cm, event) => {
const text = event.clipboardData.getData('text') || '';
- if (
- text.includes('@-moz-document') &&
- text.replace(/\/\*[\s\S]*?(?:\*\/|$)/g, '')
- .match(/@-moz-document[\s\r\n]+(url|url-prefix|domain|regexp)\(/)
+ if (/@-moz-document/i.test(text) &&
+ /@-moz-document\s+(url|url-prefix|domain|regexp)\(/i
+ .test(text.replace(/\/\*([^*]|\*(?!\/))*(\*\/|$)/g, ''))
) {
event.preventDefault();
showMozillaFormatImport(text);
diff --git a/edit/sections-editor.js b/edit/sections-editor.js
index e2e3678d..567a5bc4 100644
--- a/edit/sections-editor.js
+++ b/edit/sections-editor.js
@@ -48,7 +48,7 @@ function createSectionsEditor(editorBase) {
let sectionOrder = '';
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();
livePreview.show(Boolean(style.id));
@@ -333,24 +333,37 @@ function createSectionsEditor(editorBase) {
'Shift-Ctrl-Enter': () => doImport({replaceOldStyle: true}),
};
- function doImport({replaceOldStyle = false}) {
+ async function doImport({replaceOldStyle = false}) {
lockPageUI(true);
- API.parseCss({code: popup.codebox.getValue().trim()})
- .then(({sections, errors}) => {
+ try {
+ 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
if (!sections.length || errors.length) {
throw errors;
}
- if (replaceOldStyle) {
- return replaceSections(sections);
- }
- return initSections(sections, {focusOn: false});
- })
- .then(() => {
+ await initSections(sections, {
+ replace: replaceOldStyle,
+ focusOn: replaceOldStyle ? 0 : false,
+ });
$('.dismiss').dispatchEvent(new Event('click'));
- })
- .catch(showError)
- .then(() => lockPageUI(false));
+ }
+ } catch (err) {
+ showError(err);
+ }
+ lockPageUI(false);
+ }
+
+ async function getPreprocessor(code) {
+ try {
+ return (await API.buildUsercssMeta({sourceCode: code})).usercssData.preprocessor;
+ } catch (e) {}
}
function lockPageUI(locked) {
@@ -451,8 +464,14 @@ function createSectionsEditor(editorBase) {
function initSections(originalSections, {
focusOn = 0,
- isFirstInit,
+ replace = false,
+ pristine = false,
} = {}) {
+ if (replace) {
+ sections.forEach(s => s.remove(true));
+ sections.length = 0;
+ container.textContent = '';
+ }
let done;
const total = originalSections.length;
originalSections = originalSections.slice();
@@ -464,7 +483,7 @@ function createSectionsEditor(editorBase) {
const t0 = performance.now();
while (originalSections.length && performance.now() - t0 < 100) {
insertSectionAfter(originalSections.shift(), undefined, forceRefresh);
- if (isFirstInit) dirty.clear();
+ if (pristine) dirty.clear();
if (focusOn !== false && sections[focusOn]) {
sections[focusOn].cm.focus();
focusOn = false;
@@ -572,36 +591,21 @@ function createSectionsEditor(editorBase) {
updateSectionOrder();
}
- function replaceSections(...args) {
- for (const section of sections) {
- section.remove(true);
- }
- sections.length = 0;
- container.textContent = '';
- return initSections(...args);
- }
-
- function replaceStyle(newStyle, codeIsUpdated) {
+ async function replaceStyle(newStyle, codeIsUpdated) {
dirty.clear('name');
// FIXME: avoid recreating all editors?
- reinit().then(() => {
- 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();
- });
-
- function reinit() {
- if (codeIsUpdated !== false) {
- return replaceSections(newStyle.sections, {isFirstInit: true});
- }
- return Promise.resolve();
+ if (codeIsUpdated !== false) {
+ await initSections(newStyle.sections, {replace: true, pristine: true});
}
+ 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();
}
}
diff --git a/msgbox/msgbox.css b/msgbox/msgbox.css
index bbc70622..4f349613 100644
--- a/msgbox/msgbox.css
+++ b/msgbox/msgbox.css
@@ -56,6 +56,10 @@
text-align: left;
}
+#message-box.pre-line #message-box-contents {
+ white-space: pre-line;
+}
+
#message-box-title {
font-weight: bold;
background-color: rgb(145, 208, 198);
diff --git a/msgbox/msgbox.js b/msgbox/msgbox.js
index a537da32..6a409f9f 100644
--- a/msgbox/msgbox.js
+++ b/msgbox/msgbox.js
@@ -168,10 +168,12 @@ messageBox.alert = (contents, className, title) =>
/**
* @param {String|Node|Array} contents
* @param {String} [className] like 'pre' for monospace font
+ * @param {String} [title]
* @returns {Promise} resolves to true when confirmed
*/
-messageBox.confirm = (contents, className) =>
+messageBox.confirm = (contents, className, title) =>
messageBox({
+ title,
contents,
className: `center ${className || ''}`,
buttons: [t('confirmYes'), t('confirmNo')]