From 19ebeedf6af6c341d5e1ae2ef7281914435c2e67 Mon Sep 17 00:00:00 2001 From: tophf Date: Sun, 14 Nov 2021 11:00:12 +0300 Subject: [PATCH] add vars after `@import` in compiled code (#1348) fixes #1347 --- background/style-manager.js | 18 ++++++++++++++---- js/csslint/parserlib.js | 1 + js/sections-util.js | 5 ++++- js/usercss-compiler.js | 22 +++++++++++++++++++++- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/background/style-manager.js b/background/style-manager.js index b0e3b649..8bbf3d69 100644 --- a/background/style-manager.js +++ b/background/style-manager.js @@ -1,5 +1,5 @@ /* global API msg */// msg.js -/* global CHROME URLS stringAsRegExp tryRegExp tryURL */// toolbox.js +/* global CHROME URLS isEmptyObj stringAsRegExp tryRegExp tryURL */// toolbox.js /* global bgReady compareRevision */// common.js /* global calcStyleDigest styleCodeEmpty styleSectionGlobal */// sections-util.js /* global db */ @@ -467,7 +467,7 @@ const styleMan = (() => { async function init() { const styles = await db.exec('getAll') || []; - const updated = styles.filter(fixKnownProblems); + const updated = await Promise.all(styles.map(fixKnownProblems).filter(Boolean)); if (updated.length) { await db.exec('putMany', updated); } @@ -479,7 +479,7 @@ const styleMan = (() => { bgReady._resolveStyles(); } - function fixKnownProblems(style) { + function fixKnownProblems(style, initIndex, initArray) { let res = 0; for (const key in MISSING_PROPS) { if (!style[key]) { @@ -523,7 +523,17 @@ const styleMan = (() => { if (!style.url) res = style.url = url; if (!style.installationUrl) res = style.installationUrl = url; } - return Boolean(res); + /* @import must precede `vars` that we add at beginning */ + if ( + initArray && + !isEmptyObj((style.usercssData || {}).vars) && + style.sections.some(({code}) => + code.startsWith(':root {\n --') && + /@import\s/i.test(code)) + ) { + return usercssMan.buildCode(style); + } + return res && style; } function urlMatchStyle(query, style) { diff --git a/js/csslint/parserlib.js b/js/csslint/parserlib.js index de5a9cf9..cdef79a3 100644 --- a/js/csslint/parserlib.js +++ b/js/csslint/parserlib.js @@ -4678,6 +4678,7 @@ self.parserlib = (() => { //#endregion //#region PUBLIC API + /** @namespace parserlib */ return { css: { Colors, diff --git a/js/sections-util.js b/js/sections-util.js index 3ef5e7d4..f9e679ab 100644 --- a/js/sections-util.js +++ b/js/sections-util.js @@ -71,12 +71,15 @@ function styleCodeEmpty(code) { if (!code) { return true; } + let lastIndex = 0; const rx = /\s+|\/\*([^*]|\*(?!\/))*(\*\/|$)|@namespace[^;]+;|@charset[^;]+;/giyu; while (rx.exec(code)) { - if (rx.lastIndex === code.length) { + lastIndex = rx.lastIndex; + if (lastIndex === code.length) { return true; } } + styleCodeEmpty.lastIndex = lastIndex; return false; } diff --git a/js/usercss-compiler.js b/js/usercss-compiler.js index 9440a44a..764afb38 100644 --- a/js/usercss-compiler.js +++ b/js/usercss-compiler.js @@ -12,7 +12,7 @@ const BUILDERS = Object.assign(Object.create(null), { varDef = ':root {\n' + varDef + '}\n'; for (const section of sections) { if (!styleCodeEmpty(section.code)) { - section.code = varDef + section.code; + spliceCssAfterGlobals(section, varDef, styleCodeEmpty.lastIndex); } } }, @@ -165,3 +165,23 @@ function simplifyUsercssVars(vars) { va.value = value; } } + +function spliceCssAfterGlobals(section, newText, after) { + const {code} = section; + const RX_IMPORT = /@import\s/gi; + RX_IMPORT.lastIndex = after; + if (RX_IMPORT.test(code)) { + require(['/js/csslint/parserlib']); /* global parserlib */ + const parser = new parserlib.css.Parser(); + parser._tokenStream = new parserlib.css.TokenStream(code); + parser._sheetGlobals(); + const {col, line, offset} = parser._tokenStream._token; + // normalizing newlines in non-usercss to match line:col from parserlib + if ((code.indexOf('\r') + 1 || 1e99) - 1 < offset) { + after = col + code.split('\n', line).reduce((len, s) => len + s.length + 1, 0); + } else { + after = offset + 1; + } + } + section.code = (after ? code.slice(0, after) + '\n' : '') + newText + code.slice(after); +}