From a71b71e4c8a2037fc68970bc8912a76732edaade Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 23 Sep 2022 16:03:13 -0400 Subject: [PATCH] New cosmetic filter parser using CSSTree library The new parser no longer uses the browser DOM to validate that a cosmetic filter is valid or not, this is now done through a JS library, CSSTree. This means filter list authors will have to be more careful to ensure that a cosmetic filter is really valid, as there is no more guarantee that a cosmetic filter which works for a given browser/version will still work properly on another browser, or different version of the same browser. This change has become necessary because of many reasons, one of them being the flakiness of the previous parser as exposed by many issues lately: - https://github.com/uBlockOrigin/uBlock-issues/issues/2262 - https://github.com/uBlockOrigin/uBlock-issues/issues/2228 The new parser introduces breaking changes, there was no way to do otherwise. Some current procedural cosmetic filters will be shown as invalid with this change. This occurs because the CSSTree library gets confused with some syntax which was previously allowed by the previous parser because it was more permissive. Mainly the issue is with the arguments passed to some procedural cosmetic filters, and these issues can be solved as follow: Use quotes around the argument. You can use either single or double-quotes, whichever is most convenient. If your argument contains a single quote, use double-quotes, and vice versa. Additionally, try to escape a quote inside an argument using backslash. THis may work, but if not, use quotes around the argument. When the parser encounter quotes around an argument, it will discard them before trying to process the argument, same with escaped quotes inside the argument. Examples: Breakage: ...##^script:has-text(toscr') Fix: ...##^script:has-text(toscr\') Breakage: ...##:xpath(//*[contains(text(),"VPN")]):upward(2) Fix: ...##:xpath('//*[contains(text(),"VPN")]'):upward(2) There are not many filters which break in the default set of filter lists, so this should be workable for default lists. Unfortunately those fixes will break the filter for previous versions of uBO since these to not deal with quoted argument. In such case, it may be necessary to keep the previous filter, which will be discarded as broken on newer version of uBO. THis was a necessary change as the old parser was becoming more and more flaky after being constantly patched for new cases arising, The new parser should be far more robust and stay robist through expanding procedural cosmetic filter syntax. Additionally, in the MV3 version, filters are pre-compiled using a Nodejs script, i.e. outside the browser, so validating cosmetic filters using a live DOM no longer made sense. This new parser will have to be tested throughly before stable release. --- platform/common/vapi-common.js | 5 + platform/mv3/make-rulesets.js | 2 +- src/about.html | 1 + src/js/contentscript-extra.js | 67 ++- src/js/cosmetic-filtering.js | 6 +- src/js/epicker-ui.js | 5 +- src/js/messaging.js | 4 +- src/js/reverselookup.js | 4 +- src/js/static-dnr-filtering.js | 16 +- src/js/static-filtering-parser.js | 956 ++++++++++++++---------------- src/js/storage.js | 7 +- src/lib/csstree/LICENSE | 19 + src/lib/csstree/css-tree.js | 17 + tools/make-nodejs.sh | 1 + 14 files changed, 558 insertions(+), 552 deletions(-) create mode 100644 src/lib/csstree/LICENSE create mode 100644 src/lib/csstree/css-tree.js diff --git a/platform/common/vapi-common.js b/platform/common/vapi-common.js index 9dde268df..290837584 100644 --- a/platform/common/vapi-common.js +++ b/platform/common/vapi-common.js @@ -22,6 +22,8 @@ // For background page or non-background pages +/* global browser */ + 'use strict'; /******************************************************************************/ @@ -89,6 +91,9 @@ vAPI.webextFlavor = { soup.add('chromium') .add('user_stylesheet'); flavor.major = parseInt(match[1], 10) || 0; + if ( flavor.major >= 105 ) { + soup.add('native_css_has'); + } } // Don't starve potential listeners diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 9e5b9e9d4..99fb646bb 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -55,7 +55,7 @@ const outputDir = commandLineArgs.get('output') || '.'; const cacheDir = `${outputDir}/../mv3-data`; const rulesetDir = `${outputDir}/rulesets`; const scriptletDir = `${rulesetDir}/js`; -const env = [ 'chromium', 'ubol' ]; +const env = [ 'chromium', 'ubol', 'native_css_has' ]; /******************************************************************************/ diff --git a/src/about.html b/src/about.html index a7c701ec1..12b130cee 100644 --- a/src/about.html +++ b/src/about.html @@ -37,6 +37,7 @@
Regular Expression Analyzer by Nikos M.
HSLuv - Human-friendly HSL by Alexei Boronine
google-ima.js by Mozilla
+
CSSTree by Roman Dvornov
diff --git a/src/js/contentscript-extra.js b/src/js/contentscript-extra.js index 5130abcd8..5be4af1ff 100644 --- a/src/js/contentscript-extra.js +++ b/src/js/contentscript-extra.js @@ -95,19 +95,6 @@ class PSelectorMatchesCSSTask extends PSelectorTask { } } } -class PSelectorMatchesCSSAfterTask extends PSelectorMatchesCSSTask { - constructor(task) { - super(task); - this.pseudo = '::after'; - } -} - -class PSelectorMatchesCSSBeforeTask extends PSelectorMatchesCSSTask { - constructor(task) { - super(task); - this.pseudo = '::before'; - } -} class PSelectorMatchesMediaTask extends PSelectorTask { constructor(task) { @@ -247,6 +234,20 @@ class PSelectorSpathTask extends PSelectorTask { output.push(node); } } + // Helper method for other operators. + static qsa(node, selector) { + const parent = node.parentElement; + if ( parent === null ) { return []; } + let pos = 1; + for (;;) { + node = node.previousElementSibling; + if ( node === null ) { break; } + pos += 1; + } + return parent.querySelectorAll( + `:scope > :nth-child(${pos})${selector}` + ); + } } class PSelectorUpwardTask extends PSelectorTask { @@ -339,23 +340,20 @@ class PSelector { constructor(o) { if ( PSelector.prototype.operatorToTaskMap === undefined ) { PSelector.prototype.operatorToTaskMap = new Map([ - [ ':has', PSelectorIfTask ], - [ ':has-text', PSelectorHasTextTask ], - [ ':if', PSelectorIfTask ], - [ ':if-not', PSelectorIfNotTask ], - [ ':matches-css', PSelectorMatchesCSSTask ], - [ ':matches-css-after', PSelectorMatchesCSSAfterTask ], - [ ':matches-css-before', PSelectorMatchesCSSBeforeTask ], - [ ':matches-media', PSelectorMatchesMediaTask ], - [ ':matches-path', PSelectorMatchesPathTask ], - [ ':min-text-length', PSelectorMinTextLengthTask ], - [ ':not', PSelectorIfNotTask ], - [ ':nth-ancestor', PSelectorUpwardTask ], - [ ':others', PSelectorOthersTask ], - [ ':spath', PSelectorSpathTask ], - [ ':upward', PSelectorUpwardTask ], - [ ':watch-attr', PSelectorWatchAttrs ], - [ ':xpath', PSelectorXpathTask ], + [ 'has', PSelectorIfTask ], + [ 'has-text', PSelectorHasTextTask ], + [ 'if', PSelectorIfTask ], + [ 'if-not', PSelectorIfNotTask ], + [ 'matches-css', PSelectorMatchesCSSTask ], + [ 'matches-media', PSelectorMatchesMediaTask ], + [ 'matches-path', PSelectorMatchesPathTask ], + [ 'min-text-length', PSelectorMinTextLengthTask ], + [ 'not', PSelectorIfNotTask ], + [ 'others', PSelectorOthersTask ], + [ 'spath', PSelectorSpathTask ], + [ 'upward', PSelectorUpwardTask ], + [ 'watch-attr', PSelectorWatchAttrs ], + [ 'xpath', PSelectorXpathTask ], ]); } this.raw = o.raw; @@ -374,7 +372,12 @@ class PSelector { prime(input) { const root = input || document; if ( this.selector === '' ) { return [ root ]; } - return Array.from(root.querySelectorAll(this.selector)); + let selector = this.selector; + if ( input !== document && /^ [>+~]/.test(this.selector) ) { + return Array.from(PSelectorSpathTask.qsa(input, this.selector)); + } + const elems = root.querySelectorAll(selector); + return Array.from(elems); } exec(input) { let nodes = this.prime(input); @@ -453,7 +456,7 @@ class ProceduralFilterer { let style, styleToken; if ( selector.action === undefined ) { style = vAPI.hideStyle; - } else if ( selector.action[0] === ':style' ) { + } else if ( selector.action[0] === 'style' ) { style = selector.action[1]; } if ( style !== undefined ) { diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index de3b8c720..2c1d941cd 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -429,7 +429,7 @@ FilterContainer.prototype.compileGenericHideSelector = function( // https://github.com/uBlockOrigin/uBlock-issues/issues/131 // Support generic procedural filters as per advanced settings. // TODO: prevent double compilation. - if ( compiled !== raw ) { + if ( compiled.charCodeAt(0) === 0x7B /* '{' */ ) { if ( µb.hiddenSettings.allowGenericProceduralFilters === true ) { return this.compileSpecificSelector(parser, '', false, writer); } @@ -830,12 +830,12 @@ FilterContainer.prototype.cssRuleFromProcedural = function(json) { let mq; if ( tasks !== undefined ) { if ( tasks.length > 1 ) { return; } - if ( tasks[0][0] !== ':matches-media' ) { return; } + if ( tasks[0][0] !== 'matches-media' ) { return; } mq = tasks[0][1]; } let style; if ( Array.isArray(action) ) { - if ( action[0] !== ':style' ) { return; } + if ( action[0] !== 'style' ) { return; } style = action[1]; } if ( mq === undefined && style === undefined ) { return; } diff --git a/src/js/epicker-ui.js b/src/js/epicker-ui.js index 08476aedc..fda45838b 100644 --- a/src/js/epicker-ui.js +++ b/src/js/epicker-ui.js @@ -834,7 +834,10 @@ const startPicker = function() { $id('candidateFilters').addEventListener('click', onCandidateClicked); $stor('#resultsetDepth input').addEventListener('input', onDepthChanged); $stor('#resultsetSpecificity input').addEventListener('input', onSpecificityChanged); - staticFilteringParser = new StaticFilteringParser({ interactive: true }); + staticFilteringParser = new StaticFilteringParser({ + interactive: true, + nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), + }); }; /******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index 6c83d5555..90a57ed58 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1721,7 +1721,9 @@ const getURLFilteringData = function(details) { }; const compileTemporaryException = function(filter) { - const parser = new StaticFilteringParser(); + const parser = new StaticFilteringParser({ + nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), + }); parser.analyze(filter); if ( parser.shouldDiscard() ) { return; } return staticExtFilteringEngine.compileTemporary(parser); diff --git a/src/js/reverselookup.js b/src/js/reverselookup.js index 69e4549cb..1a0da3fe6 100644 --- a/src/js/reverselookup.js +++ b/src/js/reverselookup.js @@ -134,7 +134,9 @@ const fromNetFilter = async function(rawFilter) { if ( typeof rawFilter !== 'string' || rawFilter === '' ) { return; } const writer = new CompiledListWriter(); - const parser = new StaticFilteringParser(); + const parser = new StaticFilteringParser({ + nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), + }); parser.setMaxTokenLength(staticNetFilteringEngine.MAX_TOKEN_LENGTH); parser.analyze(rawFilter); diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index 91ed11819..9aecf42b1 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -97,6 +97,7 @@ function addExtendedToDNR(context, parser) { if ( bad ) { continue; } if ( hn.endsWith('.*') ) { continue; } const { compiled, exception } = parser.result; + if ( typeof compiled !== 'string' ) { continue; } if ( compiled.startsWith('{') ) { continue; } if ( exception ) { continue; } let details = context.cosmeticFilters.get(compiled); @@ -126,14 +127,14 @@ function addExtendedToDNR(context, parser) { /******************************************************************************/ function addToDNR(context, list) { + const env = context.env || []; const writer = new CompiledListWriter(); const lineIter = new LineIterator( - StaticFilteringParser.utils.preparser.prune( - list.text, - context.env || [] - ) + StaticFilteringParser.utils.preparser.prune(list.text, env) ); - const parser = new StaticFilteringParser(); + const parser = new StaticFilteringParser({ + nativeCssHas: env.includes('native_css_has'), + }); const compiler = staticNetFilteringEngine.createCompiler(parser); writer.properties.set('name', list.name); @@ -180,10 +181,9 @@ function addToDNR(context, list) { /******************************************************************************/ async function dnrRulesetFromRawLists(lists, options = {}) { - const context = {}; + const context = Object.assign({}, options); staticNetFilteringEngine.dnrFromCompiled('begin', context); - context.extensionPaths = new Map(options.extensionPaths || []); - context.env = options.env; + context.extensionPaths = new Map(context.extensionPaths || []); const toLoad = []; const toDNR = (context, list) => addToDNR(context, list); for ( const list of lists ) { diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 1be5e41e8..fde3e7d92 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -26,6 +26,7 @@ /******************************************************************************/ import Regex from '../lib/regexanalyzer/regex.js'; +import * as cssTree from '../lib/csstree/css-tree.js'; /******************************************************************************* @@ -75,8 +76,8 @@ import Regex from '../lib/regexanalyzer/regex.js'; /******************************************************************************/ const Parser = class { - constructor(options = {}) { - this.interactive = options.interactive === true; + constructor(instanceOptions = {}) { + this.interactive = instanceOptions.interactive === true; this.raw = ''; this.slices = []; this.leftSpaceSpan = new Span(); @@ -105,7 +106,7 @@ const Parser = class { this.netOptionsIterator = new NetOptionsIterator(this); this.extOptionsIterator = new ExtOptionsIterator(this); this.maxTokenLength = Number.MAX_SAFE_INTEGER; - this.expertMode = options.expertMode !== false; + this.expertMode = instanceOptions.expertMode !== false; this.reIsLocalhostRedirect = /(?:0\.0\.0\.0|broadcasthost|local|localhost(?:\.localdomain)?|ip6-\w+)(?:[^\w.-]|$)/; this.reHostname = /^[^\x00-\x24\x26-\x29\x2B\x2C\x2F\x3A-\x40\x5B-\x5E\x60\x7B-\x7F]+/; this.reHostsSink = /^[\w%.:\[\]-]+$/; @@ -124,7 +125,7 @@ const Parser = class { // TODO: mind maxTokenLength this.reGoodRegexToken = /[^\x01%0-9A-Za-z][%0-9A-Za-z]{7,}|[^\x01%0-9A-Za-z][%0-9A-Za-z]{1,6}[^\x01%0-9A-Za-z]/; - this.selectorCompiler = new this.SelectorCompiler(this); + this.selectorCompiler = new this.SelectorCompiler(this, instanceOptions); // TODO: reuse for network filtering analysis this.result = { exception: false, @@ -263,9 +264,9 @@ const Parser = class { // ^^ else if ( hasBits(this.slices[to], BITPercent | BITQuestion) ) { if ( this.slices[to+2] !== 1 ) { return; } - flavorBits |= hasBits(this.slices[to], BITQuestion) - ? BITFlavorExtStrong - : BITFlavorUnsupported; + flavorBits |= hasBits(this.slices[to], BITPercent) + ? BITFlavorUnsupported + : BITFlavorExtStrong; to += 3; if ( to === end ) { return; } } // ##... @@ -290,6 +291,8 @@ const Parser = class { this.result.exception = this.isException(); this.result.compiled = undefined; + if ( hasBits(this.flavorBits, BITFlavorUnsupported) ) { return; } + let selector = this.strFromSpan(this.patternSpan); if ( selector === '' ) { this.flavorBits |= BITFlavorUnsupported; @@ -335,11 +338,9 @@ const Parser = class { } this.result.raw = selector; if ( - this.selectorCompiler.compile( - selector, - hasBits(this.flavorBits, BITFlavorExtStrong | BITFlavorExtStyle), - this.result - ) === false + this.selectorCompiler.compile(selector, this.result, { + asProcedural: hasBits(this.flavorBits, BITFlavorExtStrong | BITFlavorExtStyle), + }) === false ) { this.flavorBits |= BITFlavorUnsupported; } @@ -1320,24 +1321,15 @@ Parser.removableHTTPHeaders = Parser.prototype.removableHTTPHeaders = new Set([ // Do not discard unknown pseudo-elements. Parser.prototype.SelectorCompiler = class { - constructor(parser) { + constructor(parser, instanceOptions) { this.parser = parser; - this.reExtendedSyntax = /\[-(?:abp|ext)-[a-z-]+=(['"])(?:.+?)(?:\1)\]/; - this.reExtendedSyntaxParser = /\[-(?:abp|ext)-([a-z-]+)=(['"])(.+?)\2\]/; this.reParseRegexLiteral = /^\/(.+)\/([imu]+)?$/; - this.normalizedExtendedSyntaxOperators = new Map([ - [ 'contains', ':has-text' ], - [ 'has', ':has' ], - [ 'matches-css', ':matches-css' ], - [ 'matches-css-after', ':matches-css-after' ], - [ 'matches-css-before', ':matches-css-before' ], - ]); // Use a regex for most common CSS selectors known to be valid in any // context. const cssIdentifier = '[A-Za-z_][\\w-]*'; const cssClassOrId = `[.#]${cssIdentifier}`; - const cssAttribute = `\\[${cssIdentifier}(?:[*^$]?="[^"\\]\\\\]+")\\]`; + const cssAttribute = `\\[${cssIdentifier}(?:[*^$]?="[^"\\]\\\\]+")?\\]`; const cssSimple = '(?:' + `${cssIdentifier}(?:${cssClassOrId})*(?:${cssAttribute})*` + '|' + @@ -1349,62 +1341,60 @@ Parser.prototype.SelectorCompiler = class { `^${cssSimple}(?:${cssCombinator}${cssSimple})*$` ); // Resulting regex literal: - // ^(?:[A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*[*^$]?="[^"\]\\]+"\])*|[.#][A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*[*^$]?="[^"\]\\]+"\])*|\[[A-Za-z_][\w-]*[*^$]?="[^"\]\\]+"\](?:\[[A-Za-z_][\w-]*[*^$]?="[^"\]\\]+"\])*)(?:(?:\s+|\s*[>+~]\s*)(?:[A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*[*^$]?="[^"\]\\]+"\])*|[.#][A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*[*^$]?="[^"\]\\]+"\])*|\[[A-Za-z_][\w-]*[*^$]?="[^"\]\\]+"\](?:\[[A-Za-z_][\w-]*[*^$]?="[^"\]\\]+"\])*))*$ + // /^(?:[A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*|[.#][A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*|\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\](?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*)(?:(?:\s+|\s*[>+~]\s*)(?:[A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*|[.#][A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*|\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\](?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*))*$/ - // We use an actual stylesheet to validate uncommon CSS selectors and - // CSS properties which can be used in a CSS declaration. - this.cssValidatorElement = null; - (( ) => { - if ( typeof document !== 'object' ) { return; } - if ( document === null ) { return; } - try { - const styleElement = document.createElement('style'); - styleElement.appendChild(document.createTextNode(' ')); - document.body.append(styleElement); - this.cssValidatorElement = styleElement; - } catch(ex) { - } - })(); - - // We use an HTML element to validate selectors which are - // querySelector-able. - this.div = (( ) => { - if ( typeof document !== 'object' ) { return null; } - if ( document instanceof Object === false ) { return null; } - return document.createElement('div'); - })(); - - const allProceduralOperators = Array.from( - parser.proceduralOperatorTokens.keys() - ); - this.reProceduralOperator = new RegExp( - `^(?:${allProceduralOperators.join('|')})\\(` - ); - this.reHasProceduralOperator = new RegExp( - `:(?:${allProceduralOperators.filter(s => s !== 'not').join('|')})\\(` - ); this.reEatBackslashes = /\\([()])/g; this.reEscapeRegex = /[.*+?^${}()|[\]\\]/g; - this.reDropScope = /^\s*:scope\s*(?=[+>~])/; - this.reIsDanglingSelector = /[+>~\s]\s*$/; - this.reIsCombinator = /^\s*[+>~]/; - this.reForgivingOps = /:has\(/; this.regexToRawValue = new Map(); // https://github.com/gorhill/uBlock/issues/2793 this.normalizedOperators = new Map([ - [ ':-abp-contains', ':has-text' ], - [ ':-abp-has', ':has' ], - [ ':contains', ':has-text' ], - [ ':nth-ancestor', ':upward' ], - [ ':watch-attrs', ':watch-attr' ], + [ '-abp-has', 'has' ], + [ '-abp-contains', 'has-text' ], + [ 'contains', 'has-text' ], + [ 'nth-ancestor', 'upward' ], + [ 'watch-attrs', 'watch-attr' ], ]); this.actionOperators = new Set([ ':remove', ':style', ]); + + this.proceduralOperatorNames = new Set([ + 'has-text', + 'if', + 'if-not', + 'matches-css', + 'matches-css-after', + 'matches-css-before', + 'matches-media', + 'matches-path', + 'min-text-length', + 'others', + 'upward', + 'watch-attr', + 'xpath', + ]); + this.maybeProceduralOperatorNames = new Set([ + 'has', + 'not', + ]); + this.proceduralActionNames = new Set([ + 'remove', + 'style', + ]); + this.normalizedExtendedSyntaxOperators = new Map([ + [ 'contains', 'has-text' ], + [ 'has', 'has' ], + ]); + this.reExtendedSyntax = /\[-(?:abp|ext)-[a-z-]+=(['"])(?:.+?)(?:\1)\]/; + this.reExtendedSyntaxReplacer = /\[-(?:abp|ext)-([a-z-]+)=(['"])(.+?)\2\]/g; + this.abpProceduralOpReplacer = /:-abp-(?:contains|has)\(/g; + this.nativeCssHas = instanceOptions.nativeCssHas === true; } - compile(raw, asProcedural, out) { + compile(raw, out, compileOptions = {}) { + this.asProcedural = compileOptions.asProcedural === true; + // https://github.com/gorhill/uBlock/issues/952 // Find out whether we are dealing with an Adguard-specific cosmetic // filter, and if so, translate it if supported, or discard it if not @@ -1417,56 +1407,341 @@ Parser.prototype.SelectorCompiler = class { raw = this.translateAdguardCSSInjectionFilter(raw); if ( raw === '' ) { return false; } this.parser.flavorBits &= ~BITFlavorExtStyle; - out.raw = raw; } - // Can be used in a declarative CSS rule? - // https://github.com/uBlockOrigin/uBlock-issues/issues/2228 - // Some operators are forgiving, so we need to exclude them for now - // as potentially declarative selectors until we validate that their - // arguments are themselves valid plain CSS selector. - if ( - asProcedural === false && - this.reForgivingOps.test(raw) === false && - this.sheetSelectable(raw) - ) { + // Normalize AdGuard's attribute-based procedural operators. + // Normalize ABP's procedural operator names + if ( this.asProcedural ) { + if ( this.reExtendedSyntax.test(raw) ) { + raw = raw.replace(this.reExtendedSyntaxReplacer, (a, a1, a2, a3) => { + const op = this.normalizedExtendedSyntaxOperators.get(a1); + if ( op === undefined ) { return a; } + return `:${op}(${a3})`; + }); + } else { + raw = raw.replace(this.abpProceduralOpReplacer, match => { + if ( match === ':-abp-contains(' ) { + return ':has-text('; + } else if ( match === ':-abp-has(' ) { + return ':has('; + } + return match; + }); + } + } + + if ( this.reCommonSelector.test(raw) ) { out.compiled = raw; return true; } - // We rarely reach this point -- majority of selectors are plain - // CSS selectors. + out.compiled = this.compileSelector(raw); + if ( out.compiled === undefined ) { + console.log('Error:', raw); + return false; + } + if ( out.compiled instanceof Object ) { + out.compiled.raw = raw; + out.compiled = JSON.stringify(out.compiled); + } + return true; + } - // Supported Adguard/ABP advanced selector syntax: will translate - // into uBO's syntax before further processing. - // Mind unsupported advanced selector syntax, such as ABP's - // `-abp-properties`. - // Note: extended selector syntax has been deprecated in ABP, in - // favor of the procedural one (i.e. `:operator(...)`). - // See https://issues.adblockplus.org/ticket/5287 - if ( asProcedural && this.reExtendedSyntax.test(raw) ) { - let matches; - while ( (matches = this.reExtendedSyntaxParser.exec(raw)) !== null ) { - const operator = this.normalizedExtendedSyntaxOperators.get(matches[1]); - if ( operator === undefined ) { return false; } - raw = raw.slice(0, matches.index) + - operator + '(' + matches[3] + ')' + - raw.slice(matches.index + matches[0].length); + compileSelector(raw) { + const parts = this.astFromRaw(raw, 'selectorList'); + if ( parts === undefined ) { return; } + if ( this.astHasType(parts, 'Error') ) { return; } + if ( this.astHasType(parts, 'Selector') === false ) { return; } + if ( + this.astHasType(parts, 'ProceduralSelector') === false && + this.astHasType(parts, 'ActionSelector') === false + ) { + return this.astSerialize(parts); + } + const r = this.astCompile(parts); + if ( this.isCssable(r) ) { + r.cssable = true; + } + return r; + } + + isCssable(r) { + if ( r instanceof Object === false ) { return false; } + if ( Array.isArray(r.action) && r.action[0] !== 'style' ) { return false; } + if ( r.tasks === undefined ) { return true; } + if ( r.tasks.length > 1 ) { return false; } + if ( r.tasks[0][0] === 'matches-media' ) { return true; } + return false; + } + + astFromRaw(raw, type) { + let ast; + try { + ast = cssTree.parse(raw, { context: type }); + } catch(reason) { + return; + } + const parts = []; + this.astFlatten(ast, parts); + return parts; + } + + astFlatten(data, out) { + const head = data.children && data.children.head; + let args; + switch ( data.type ) { + case 'AttributeSelector': + case 'ClassSelector': + case 'Combinator': + case 'IdSelector': + case 'TypeSelector': + out.push({ data }); + break; + case 'Declaration': { + if ( data.value ) { + this.astFlatten(data.value, args = []); } - return this.compile(raw, true, out); + out.push({ data, args }); + args = undefined; + break; + } + case 'DeclarationList': + args = out; + out.push({ data }); + break; + case 'Identifier': + args = out; + out.push({ data }); + break; + case 'PseudoClassSelector': + case 'PseudoElementSelector': + if ( head ) { args = []; } + out.push({ data, args }); + break; + case 'Raw': + if ( head ) { args = []; } + out.push({ data, args }); + break; + case 'Selector': + args = out; + out.push({ data }); + break; + case 'SelectorList': + args = out; + out.push({ data }); + break; + case 'Value': + args = out; + break; + default: + break; } - // Procedural selector? - const compiled = this.compileProceduralSelector(raw, asProcedural); - if ( compiled === undefined ) { return false; } + if ( head ) { + if ( args ) { + this.astFlatten(head.data, args); + } + let next = head.next; + while ( next ) { + this.astFlatten(next.data, out); + next = next.next; + } + } + if ( data.type !== 'PseudoClassSelector' ) { return; } - out.compiled = - compiled.selector !== compiled.raw || - this.sheetSelectable(compiled.selector) === false - ? JSON.stringify(compiled) - : compiled.selector; + // Post-analysis + // Mind https://w3c.github.io/csswg-drafts/selectors-4/#has-pseudo + data.name = this.normalizedOperators.get(data.name) || data.name; + if ( this.proceduralOperatorNames.has(data.name) ) { + data.type = 'ProceduralSelector'; + } else if ( this.proceduralActionNames.has(data.name) ) { + data.type = 'ActionSelector'; + } + if ( this.maybeProceduralOperatorNames.has(data.name) ) { + if ( this.astHasType(args, 'ProceduralSelector') ) { + data.type = 'ProceduralSelector'; + } else if ( data.name === 'has' ) { + if ( + this.asProcedural || + this.nativeCssHas !== true || + this.astHasName(args, 'has') + ) { + data.type = 'ProceduralSelector'; + } else if ( this.astHasType(args, 'PseudoElementSelector') ) { + data.type = 'Error'; + } + } + } + } - return true; + astSerialize(parts) { + const out = []; + for ( const part of parts ) { + const { data } = part; + switch ( data.type ) { + case 'AttributeSelector': + out.push( + data.matcher + ? `[${data.name.name}${data.matcher}"${data.value.value}"]` + : `[${data.name.name}]` + ); + break; + case 'ClassSelector': + out.push(`.${data.name}`); + break; + case 'Combinator': + out.push(data.name === ' ' ? ' ' : ` ${data.name} `); + break; + case 'IdSelector': + out.push(`#${data.name}`); + break; + case 'ActionSelector': + case 'PseudoClassSelector': + case 'PseudoElementSelector': + out.push(`:${data.name}`); + if ( Array.isArray(part.args) ) { + out.push(`(${this.astSerialize(part.args)})`); + } + break; + case 'Raw': + out.push(data.value); + break; + case 'Selector': + if ( out.length !== 0 ) { out.push(','); } + break; + case 'TypeSelector': + out.push(data.name); + break; + default: + break; + } + } + return out.join(''); + } + + astCompile(parts) { + if ( Array.isArray(parts) === false ) { return; } + if ( parts.length === 0 ) { return; } + if ( parts[0].data.type !== 'SelectorList' ) { return; } + const out = { selector: '' }; + const prelude = []; + const tasks = []; + for ( const part of parts ) { + const { data } = part; + switch ( data.type ) { + case 'ActionSelector': { + if ( prelude.length !== 0 ) { + if ( tasks.length === 0 ) { + out.selector = prelude.join(''); + } else { + tasks.push(this.createSpathTask(prelude.join(''))); + } + prelude.length = 0; + } + const args = this.compileArgumentAst(data.name, part.args); + if ( args === undefined ) { return; } + out.action = [ data.name, args ]; + break; + } + case 'AttributeSelector': { + const s = data.matcher + ? `[${data.name.name}${data.matcher}"${data.value.value}"]` + : `[${data.name.name}]`; + prelude.push(s); + break; + } + case 'ClassSelector': { + const s = `.${data.name}`; + prelude.push(s); + break; + } + case 'Combinator': { + const s = data.name === ' ' ? ' ' : ` ${data.name} `; + prelude.push(s); + break; + } + case 'IdSelector': { + const s = `#${data.name}`; + prelude.push(s); + break; + } + case 'PseudoClassSelector': { + prelude.push(`:${data.name}`); + if ( Array.isArray(part.args) ) { + prelude.push(`(${this.astSerialize(part.args)})`); + } + break; + } + case 'PseudoElementSelector': { + prelude.push(`::${data.name}`); + if ( Array.isArray(part.args) ) { + prelude.push(`(${this.astSerialize(part.args)})`); + } + break; + } + case 'SelectorList': + break; + case 'Selector': + if ( prelude.length !== 0 ) { + prelude.push(', '); + } + break; + case 'SelectorList': + break; + case 'TypeSelector': { + prelude.push(data.name); + break; + } + case 'ProceduralSelector': + if ( prelude.length !== 0 ) { + if ( tasks.length === 0 ) { + out.selector = prelude.join(''); + } else { + tasks.push(this.createSpathTask(prelude.join(''))); + } + prelude.length = 0; + } + const args = this.compileArgumentAst(data.name, part.args); + if ( args === undefined ) { return; } + tasks.push([ data.name, args ]); + break; + default: + break; + } + } + if ( tasks.length === 0 && out.action === undefined ) { + if ( prelude.length === 0 ) { return; } + return prelude.join(''); + } + if ( prelude.length !== 0 ) { + tasks.push(this.createSpathTask(prelude.join(''))); + } + if ( tasks.length !== 0 ) { + out.tasks = tasks; + } + return out; + } + + astHasType(parts, type) { + if ( Array.isArray(parts) === false ) { return false; } + for ( const part of parts ) { + if ( part.data.type === type ) { return true; } + if ( Array.isArray(part.args) && this.astHasType(part.args, type) ) { + return true; + } + } + return false; + } + + astHasName(parts, name) { + if ( Array.isArray(parts) === false ) { return false; } + for ( const part of parts ) { + if ( part.data.name === name ) { return true; } + if ( Array.isArray(part.args) && this.astHasName(part.args, name) ) { + return true; + } + } + return false; } translateAdguardCSSInjectionFilter(suffix) { @@ -1487,67 +1762,64 @@ Parser.prototype.SelectorCompiler = class { : `${selector}:style(${style})`; } - // Quick regex-based validation -- most cosmetic filters are of the - // simple form and in such case a regex is much faster. - // Keep in mind: - // https://github.com/gorhill/uBlock/issues/693 - // https://github.com/gorhill/uBlock/issues/1955 - // https://github.com/gorhill/uBlock/issues/3111 - // Workaround until https://bugzilla.mozilla.org/show_bug.cgi?id=1406817 - // is fixed. - // https://github.com/uBlockOrigin/uBlock-issues/issues/1751 - // Do not rely on matches() or querySelector() to test whether a - // selector is declarative or not. - // https://github.com/uBlockOrigin/uBlock-issues/issues/1806#issuecomment-963278382 - // Forbid multiple and unexpected CSS style declarations. - // https://github.com/uBlockOrigin/uBlock-issues/issues/2170#issuecomment-1207921464 - // Assigning text content to the `style` element resets the `disabled` - // state of the sheet, so we need to explicitly disable it each time we - // assign new text. - sheetSelectable(s) { - if ( this.reCommonSelector.test(s) ) { return true; } - if ( this.cssValidatorElement === null ) { - return this.reHasProceduralOperator.test(s) === false; - } - let valid = false; - try { - this.cssValidatorElement.childNodes[0].nodeValue = `_z + ${s}{color:red;} _z{color:red;}`; - this.cssValidatorElement.sheet.disabled = true; - const rules = this.cssValidatorElement.sheet.cssRules; - valid = rules.length === 2 && - rules[0].style.cssText !== '' && - rules[1].style.cssText !== ''; - } catch (ex) { - } - return valid; + createSpathTask(selector) { + return [ 'spath', selector ]; } - // https://github.com/uBlockOrigin/uBlock-issues/issues/1806 - // Forbid instances of: - // - opening comment `/*` - querySelectable(s) { - if ( this.reCommonSelector.test(s) ) { return true; } - if ( this.div === null ) { - return this.reHasProceduralOperator.test(s) === false; - } - try { - this.div.querySelector(`${s},${s}:not(#foo)`); - if ( s.includes('/*') ) { return false; } - } catch (ex) { - return false; - } - return true; - } - - compileProceduralSelector(raw, asProcedural = false) { - const compiled = this.compileProcedural(raw, true, asProcedural); - if ( compiled !== undefined ) { - if ( asProcedural ) { - this.optimizeCompiledProcedural(compiled); + compileArgumentAst(operator, parts) { + switch ( operator ) { + case 'has': { + let r = this.astCompile(parts); + if ( typeof r === 'string' ) { + r = { selector: r.replace(/^\s*:scope\s*/, ' ') }; } - compiled.raw = this.decompileProcedural(compiled); + return r; + } + case 'not': { + return this.astCompile(parts); + } + default: + break; + } + + let arg; + if ( Array.isArray(parts) && parts.length !== 0 ) { + arg = this.astSerialize(parts); + } + switch ( operator ) { + case 'has-text': + return this.compileText(arg); + case 'if': + return this.compileSelector(arg); + case 'if-not': + return this.compileSelector(arg); + case 'matches-css': + return this.compileCSSDeclaration(arg); + case 'matches-css-after': + return this.compileCSSDeclaration(`after, ${arg}`); + case 'matches-css-before': + return this.compileCSSDeclaration(`before, ${arg}`); + case 'matches-media': + return this.compileMediaQuery(arg); + case 'matches-path': + return this.compileText(arg); + case 'min-text-length': + return this.compileInteger(arg); + case 'others': + return this.compileNoArgument(arg); + case 'remove': + return this.compileNoArgument(arg); + case 'style': + return this.compileStyleProperties(arg); + case 'upward': + return this.compileUpwardArgument(arg); + case 'watch-attr': + return this.compileAttrList(arg); + case 'xpath': + return this.compileXpathExpression(arg); + default: + break; } - return compiled; } isBadRegex(s) { @@ -1560,9 +1832,18 @@ Parser.prototype.SelectorCompiler = class { return false; } + extractArg(s) { + if ( /^(['"]).+\1$/.test(s) ) { + s = s.slice(1, -1); + } + return s.replace(/\\(['"])/g, '$1'); + } + // When dealing with literal text, we must first eat _some_ // backslash characters. + // Remove potentially present quotes before processing. compileText(s) { + s = this.extractArg(s); const match = this.reParseRegexLiteral.exec(s); let regexDetails; if ( match !== null ) { @@ -1625,35 +1906,20 @@ Parser.prototype.SelectorCompiler = class { } } - // https://github.com/uBlockOrigin/uBlock-issues/issues/341#issuecomment-447603588 - // Reject instances of :not() filters for which the argument is - // a valid CSS selector, otherwise we would be adversely changing the - // behavior of CSS4's :not(). - compileNotSelector(s) { - if ( this.querySelectable(s) === false ) { - return this.compileProcedural(s); - } - return s; - } - compileUpwardArgument(s) { const i = this.compileInteger(s, 1, 256); if ( i !== undefined ) { return i; } - if ( this.querySelectable(s) ) { return s; } + const parts = this.astFromRaw(s, 'selectorList' ); + if ( this.astHasType(parts, 'ProceduralSelector') ) { return; } + if ( this.astHasType(parts, 'ActionSelector') ) { return; } + if ( this.astHasType(parts, 'Error') ) { return; } + return s; } compileNoArgument(s) { if ( s === '' ) { return s; } } - // https://github.com/uBlockOrigin/uBlock-issues/issues/382#issuecomment-703725346 - // Prepend `:scope` only when it can be deemed implicit. - compileSpathExpression(s) { - if ( this.querySelectable(/^\s*[+:>~]/.test(s) ? `:scope${s}` : s) ) { - return s; - } - } - // https://github.com/uBlockOrigin/uBlock-issues/issues/668 // https://github.com/uBlockOrigin/uBlock-issues/issues/1693 // https://github.com/uBlockOrigin/uBlock-issues/issues/1811 @@ -1665,18 +1931,11 @@ Parser.prototype.SelectorCompiler = class { // - opening comment `/*` compileStyleProperties(s) { if ( /image-set\(|url\(|\/\s*\/|\\|\/\*/i.test(s) ) { return; } - if ( this.cssValidatorElement === null ) { return s; } - let valid = false; - try { - this.cssValidatorElement.childNodes[0].nodeValue = `_z{${s}} _z{color:red;}`; - this.cssValidatorElement.sheet.disabled = true; - const rules = this.cssValidatorElement.sheet.cssRules; - valid = rules.length >= 2 && - rules[0].style.cssText !== '' && - rules[1].style.cssText !== ''; - } catch(ex) { - } - if ( valid ) { return s; } + const parts = this.astFromRaw(s, 'declarationList'); + if ( parts === undefined ) { return; } + if ( this.astHasType(parts, 'Raw') ) { return; } + if ( this.astHasType(parts, 'Declaration') === false ) { return; } + return s; } compileAttrList(s) { @@ -1691,6 +1950,7 @@ Parser.prototype.SelectorCompiler = class { } compileXpathExpression(s) { + s = this.extractArg(s); try { document.createExpression(s, null); } catch (e) { @@ -1698,314 +1958,6 @@ Parser.prototype.SelectorCompiler = class { } return s; } - - optimizeCompiledProcedural(compiled) { - if ( typeof compiled === 'string' ) { return; } - if ( Array.isArray(compiled.tasks) === false ) { return; } - const tasks = []; - let selector = compiled.selector; - for ( const task of compiled.tasks ) { - switch ( task[0] ) { - case ':not': - case ':if-not': - this.optimizeCompiledProcedural(task[1]); - if ( tasks.length === 0 && typeof task[1] === 'string' ) { - selector += `:not(${task[1]})`; - break; - } - tasks.push(task); - break; - default: - tasks.push(task); - break; - } - } - compiled.selector = selector; - compiled.tasks = tasks.length !== 0 ? tasks : undefined; - } - - // https://github.com/gorhill/uBlock/issues/2793#issuecomment-333269387 - // Normalize (somewhat) the stringified version of procedural - // cosmetic filters -- this increase the likelihood of detecting - // duplicates given that uBO is able to understand syntax specific - // to other blockers. - // The normalized string version is what is reported in the logger, - // by design. - decompileProcedural(compiled) { - if ( typeof compiled === 'string' ) { return compiled; } - const tasks = compiled.tasks || []; - const raw = [ compiled.selector ]; - for ( const task of tasks ) { - let value; - switch ( task[0] ) { - case ':has': - case ':if': - raw.push(`:has(${this.decompileProcedural(task[1])})`); - break; - case ':has-text': - case ':matches-path': - if ( Array.isArray(task[1]) ) { - value = `/${task[1][0]}/${task[1][1]}`; - } else { - value = this.regexToRawValue.get(task[1]); - if ( value === undefined ) { - value = `/${task[1]}/`; - } - } - raw.push(`${task[0]}(${value})`); - break; - case ':matches-css': - case ':matches-css-after': - case ':matches-css-before': - if ( Array.isArray(task[1].value) ) { - value = `/${task[1].value[0]}/${task[1].value[1]}`; - } else { - value = this.regexToRawValue.get(task[1].value); - if ( value === undefined ) { - value = `/${task[1].value}/`; - } - } - if ( task[1].pseudo ) { - raw.push(`:matches-css(${task[1].pseudo}, ${task[1].name}: ${value})`); - } else { - raw.push(`:matches-css(${task[1].name}: ${value})`); - } - break; - case ':not': - case ':if-not': - if ( typeof(task[1]) === 'string' ) { - raw.push(`:not(${task[1]})`); - break; - } - raw.push(`:not(${this.decompileProcedural(task[1])})`); - break; - case ':spath': - raw.push(task[1]); - break; - case ':matches-media': - case ':min-text-length': - case ':others': - case ':upward': - case ':watch-attr': - case ':xpath': - raw.push(`${task[0]}(${task[1]})`); - break; - } - } - if ( Array.isArray(compiled.action) ) { - const [ op, arg ] = compiled.action; - raw.push(`${op}(${arg})`); - } - return raw.join(''); - } - - compileProcedural(raw, root = false, asProcedural = false) { - if ( raw === '' ) { return; } - - const tasks = []; - const n = raw.length; - let prefix = ''; - let i = 0; - let opPrefixBeg = 0; - let action; - - // TODO: use slices instead of charCodeAt() - for (;;) { - let c, match; - // Advance to next operator. - while ( i < n ) { - c = raw.charCodeAt(i++); - if ( c === 0x3A /* ':' */ ) { - match = this.reProceduralOperator.exec(raw.slice(i)); - if ( match !== null ) { break; } - } - } - if ( i === n ) { break; } - const opNameBeg = i - 1; - const opNameEnd = i + match[0].length - 1; - i += match[0].length; - // Find end of argument: first balanced closing parenthesis. - // Note: unbalanced parenthesis can be used in a regex literal - // when they are escaped using `\`. - // TODO: need to handle quoted parentheses. - let pcnt = 1; - while ( i < n ) { - c = raw.charCodeAt(i++); - if ( c === 0x5C /* '\\' */ ) { - if ( i < n ) { i += 1; } - } else if ( c === 0x28 /* '(' */ ) { - pcnt +=1 ; - } else if ( c === 0x29 /* ')' */ ) { - pcnt -= 1; - if ( pcnt === 0 ) { break; } - } - } - // Unbalanced parenthesis? An unbalanced parenthesis is fine - // as long as the last character is a closing parenthesis. - if ( pcnt !== 0 && c !== 0x29 ) { return; } - // Extract and remember operator/argument details. - const opname = raw.slice(opNameBeg, opNameEnd); - const oparg = raw.slice(opNameEnd + 1, i - 1); - const operator = this.normalizedOperators.get(opname) || opname; - // https://github.com/uBlockOrigin/uBlock-issues/issues/341#issuecomment-447603588 - // Maybe that one operator is a valid CSS selector and if so, - // then consider it to be part of the prefix. - // https://github.com/uBlockOrigin/uBlock-issues/issues/2228 - // Maybe an operator is a valid CSS selector, but if it is - // "forgiving", we also need to validate that the argument itself - // is also a valid CSS selector. - if ( - asProcedural === false && - this.querySelectable(raw.slice(opNameBeg, i)) && - this.querySelectable(oparg) - ) { - continue; - } - // Action operator can only be used as trailing operator in the - // root task list. - // Per-operator arguments validation - const args = this.compileArgument(operator, oparg); - if ( args === undefined ) { return; } - if ( opPrefixBeg === 0 ) { - prefix = raw.slice(0, opNameBeg); - } else if ( opNameBeg !== opPrefixBeg ) { - if ( action !== undefined ) { return; } - const spath = this.compileSpathExpression( - raw.slice(opPrefixBeg, opNameBeg) - ); - if ( spath === undefined ) { return; } - tasks.push([ ':spath', spath ]); - } - if ( action !== undefined ) { return; } - const task = [ operator, args ]; - if ( this.actionOperators.has(operator) ) { - if ( root === false ) { return; } - action = task; - } else { - tasks.push(task); - } - opPrefixBeg = i; - if ( i === n ) { break; } - } - - // When there is an explicit action, nothing should be left to parse. - if ( action !== undefined && opPrefixBeg < n ) { return; } - - // No task found: then we have a CSS selector. - // At least one task found: either nothing or a valid plain CSS selector - // should be left to parse - if ( tasks.length === 0 ) { - if ( action === undefined ) { - prefix = raw; - } - if ( root && this.sheetSelectable(prefix) ) { - if ( action === undefined ) { - return { selector: prefix, cssable: true }; - } else if ( action[0] === ':style' ) { - return { selector: prefix, cssable: true, action }; - } - } - - } else if ( opPrefixBeg < n ) { - const spath = this.compileSpathExpression(raw.slice(opPrefixBeg)); - if ( spath === undefined ) { return; } - tasks.push([ ':spath', spath ]); - } - - // https://github.com/NanoAdblocker/NanoCore/issues/1#issuecomment-354394894 - // https://www.reddit.com/r/uBlockOrigin/comments/c6iem5/ - // Convert sibling-selector prefix into :spath operator, but - // only if context is not the root. - // https://github.com/uBlockOrigin/uBlock-issues/issues/1011#issuecomment-884806241 - // Drop explicit `:scope` in case of leading combinator, all such - // cases are normalized to implicit `:scope`. - if ( prefix !== '' ) { - if ( this.reIsDanglingSelector.test(prefix) && tasks.length !== 0 ) { - prefix += ' *'; - } - prefix = prefix.replace(this.reDropScope, ''); - if ( this.querySelectable(prefix) === false ) { - if ( - root || - this.reIsCombinator.test(prefix) === false || - this.compileSpathExpression(prefix) === undefined - ) { - return; - } - tasks.unshift([ ':spath', prefix ]); - prefix = ''; - } - } - - const out = { selector: prefix }; - - if ( tasks.length !== 0 ) { - out.tasks = tasks; - } - - // Expose action to take in root descriptor. - if ( action !== undefined ) { - out.action = action; - } - - // Flag to quickly find out whether the filter can be converted into - // a declarative CSS rule. - if ( - root && - (action === undefined || action[0] === ':style') && - ( - tasks.length === 0 || - tasks.length === 1 && tasks[0][0] === ':matches-media' - ) - ) { - out.cssable = this.sheetSelectable(prefix); - } - - return out; - } - - compileArgument(operator, args) { - switch ( operator ) { - case ':has': - return this.compileProcedural(args); - case ':has-text': - return this.compileText(args); - case ':if': - return this.compileProcedural(args); - case ':if-not': - return this.compileProcedural(args); - case ':matches-css': - return this.compileCSSDeclaration(args); - case ':matches-css-after': - return this.compileCSSDeclaration(`after, ${args}`); - case ':matches-css-before': - return this.compileCSSDeclaration(`before, ${args}`); - case ':matches-media': - return this.compileMediaQuery(args); - case ':matches-path': - return this.compileText(args); - case ':min-text-length': - return this.compileInteger(args); - case ':not': - return this.compileNotSelector(args); - case ':others': - return this.compileNoArgument(args); - case ':remove': - return this.compileNoArgument(args); - case ':spath': - return this.compileSpathExpression(args); - case ':style': - return this.compileStyleProperties(args); - case ':upward': - return this.compileUpwardArgument(args); - case ':watch-attr': - return this.compileAttrList(args); - case ':xpath': - return this.compileXpathExpression(args); - default: - break; - } - } }; // bit 0: can be used as auto-completion hint @@ -2020,8 +1972,6 @@ Parser.prototype.proceduralOperatorTokens = new Map([ [ 'if', 0b00 ], [ 'if-not', 0b00 ], [ 'matches-css', 0b11 ], - [ 'matches-css-after', 0b11 ], - [ 'matches-css-before', 0b11 ], [ 'matches-media', 0b11 ], [ 'matches-path', 0b11 ], [ 'min-text-length', 0b01 ], diff --git a/src/js/storage.js b/src/js/storage.js index 058a99d9c..7b80c5865 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -19,7 +19,7 @@ Home: https://github.com/gorhill/uBlock */ -/* globals WebAssembly */ +/* globals browser, WebAssembly */ 'use strict'; @@ -973,7 +973,10 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { // Useful references: // https://adblockplus.org/en/filter-cheatsheet // https://adblockplus.org/en/filters - const parser = new StaticFilteringParser({ expertMode }); + const parser = new StaticFilteringParser({ + expertMode, + nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), + }); const compiler = staticNetFilteringEngine.createCompiler(parser); const lineIter = new LineIterator( parser.utils.preparser.prune(rawText, vAPI.webextFlavor.env) diff --git a/src/lib/csstree/LICENSE b/src/lib/csstree/LICENSE new file mode 100644 index 000000000..b46f3c4a6 --- /dev/null +++ b/src/lib/csstree/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2016-2022 by Roman Dvornov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/lib/csstree/css-tree.js b/src/lib/csstree/css-tree.js new file mode 100644 index 000000000..90e8bad6f --- /dev/null +++ b/src/lib/csstree/css-tree.js @@ -0,0 +1,17 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/css-tree@2.2.1/dist/csstree.esm.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +var ts=Object.create;var nr=Object.defineProperty;var rs=Object.getOwnPropertyDescriptor;var ns=Object.getOwnPropertyNames;var os=Object.getPrototypeOf,is=Object.prototype.hasOwnProperty;var Oe=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),b=(e,t)=>{for(var r in t)nr(e,r,{get:t[r],enumerable:!0})},as=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ns(t))!is.call(e,o)&&o!==r&&nr(e,o,{get:()=>t[o],enumerable:!(n=rs(t,o))||n.enumerable});return e};var ss=(e,t,r)=>(r=e!=null?ts(os(e)):{},as(t||!e||!e.__esModule?nr(r,"default",{value:e,enumerable:!0}):r,e));var Zo=Oe(hr=>{var $o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");hr.encode=function(e){if(0<=e&&e<$o.length)return $o[e];throw new TypeError("Must be between 0 and 63: "+e)};hr.decode=function(e){var t=65,r=90,n=97,o=122,i=48,s=57,u=43,c=47,a=26,l=52;return t<=e&&e<=r?e-t:n<=e&&e<=o?e-n+a:i<=e&&e<=s?e-i+l:e==u?62:e==c?63:-1}});var ni=Oe(fr=>{var Jo=Zo(),mr=5,ei=1<>1;return t?-r:r}fr.encode=function(t){var r="",n,o=ys(t);do n=o&ti,o>>>=mr,o>0&&(n|=ri),r+=Jo.encode(n);while(o>0);return r};fr.decode=function(t,r,n){var o=t.length,i=0,s=0,u,c;do{if(r>=o)throw new Error("Expected more digits in base 64 VLQ value.");if(c=Jo.decode(t.charCodeAt(r++)),c===-1)throw new Error("Invalid base64 digit: "+t.charAt(r-1));u=!!(c&ri),c&=ti,i=i+(c<{function ws(e,t,r){if(t in e)return e[t];if(arguments.length===3)return r;throw new Error('"'+t+'" is a required argument.')}K.getArg=ws;var oi=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/,vs=/^data:.+\,.+$/;function ot(e){var t=e.match(oi);return t?{scheme:t[1],auth:t[2],host:t[3],port:t[4],path:t[5]}:null}K.urlParse=ot;function Ue(e){var t="";return e.scheme&&(t+=e.scheme+":"),t+="//",e.auth&&(t+=e.auth+"@"),e.host&&(t+=e.host),e.port&&(t+=":"+e.port),e.path&&(t+=e.path),t}K.urlGenerate=Ue;var Ss=32;function Cs(e){var t=[];return function(r){for(var n=0;nSs&&t.pop(),i}}var dr=Cs(function(t){var r=t,n=ot(t);if(n){if(!n.path)return t;r=n.path}for(var o=K.isAbsolute(r),i=[],s=0,u=0;;)if(s=u,u=r.indexOf("/",s),u===-1){i.push(r.slice(s));break}else for(i.push(r.slice(s,u));u=0;u--)c=i[u],c==="."?i.splice(u,1):c===".."?a++:a>0&&(c===""?(i.splice(u+1,a),a=0):(i.splice(u,2),a--));return r=i.join("/"),r===""&&(r=o?"/":"."),n?(n.path=r,Ue(n)):r});K.normalize=dr;function ii(e,t){e===""&&(e="."),t===""&&(t=".");var r=ot(t),n=ot(e);if(n&&(e=n.path||"/"),r&&!r.scheme)return n&&(r.scheme=n.scheme),Ue(r);if(r||t.match(vs))return t;if(n&&!n.host&&!n.path)return n.host=t,Ue(n);var o=t.charAt(0)==="/"?t:dr(e.replace(/\/+$/,"")+"/"+t);return n?(n.path=o,Ue(n)):o}K.join=ii;K.isAbsolute=function(e){return e.charAt(0)==="/"||oi.test(e)};function As(e,t){e===""&&(e="."),e=e.replace(/\/$/,"");for(var r=0;t.indexOf(e+"/")!==0;){var n=e.lastIndexOf("/");if(n<0||(e=e.slice(0,n),e.match(/^([^\/]+:\/)?\/*$/)))return t;++r}return Array(r+1).join("../")+t.substr(e.length+1)}K.relative=As;var ai=function(){var e=Object.create(null);return!("__proto__"in e)}();function si(e){return e}function Ts(e){return li(e)?"$"+e:e}K.toSetString=ai?si:Ts;function Es(e){return li(e)?e.slice(1):e}K.fromSetString=ai?si:Es;function li(e){if(!e)return!1;var t=e.length;if(t<9||e.charCodeAt(t-1)!==95||e.charCodeAt(t-2)!==95||e.charCodeAt(t-3)!==111||e.charCodeAt(t-4)!==116||e.charCodeAt(t-5)!==111||e.charCodeAt(t-6)!==114||e.charCodeAt(t-7)!==112||e.charCodeAt(t-8)!==95||e.charCodeAt(t-9)!==95)return!1;for(var r=t-10;r>=0;r--)if(e.charCodeAt(r)!==36)return!1;return!0}function Ls(e,t,r){var n=be(e.source,t.source);return n!==0||(n=e.originalLine-t.originalLine,n!==0)||(n=e.originalColumn-t.originalColumn,n!==0||r)||(n=e.generatedColumn-t.generatedColumn,n!==0)||(n=e.generatedLine-t.generatedLine,n!==0)?n:be(e.name,t.name)}K.compareByOriginalPositions=Ls;function Ps(e,t,r){var n;return n=e.originalLine-t.originalLine,n!==0||(n=e.originalColumn-t.originalColumn,n!==0||r)||(n=e.generatedColumn-t.generatedColumn,n!==0)||(n=e.generatedLine-t.generatedLine,n!==0)?n:be(e.name,t.name)}K.compareByOriginalPositionsNoSource=Ps;function Is(e,t,r){var n=e.generatedLine-t.generatedLine;return n!==0||(n=e.generatedColumn-t.generatedColumn,n!==0||r)||(n=be(e.source,t.source),n!==0)||(n=e.originalLine-t.originalLine,n!==0)||(n=e.originalColumn-t.originalColumn,n!==0)?n:be(e.name,t.name)}K.compareByGeneratedPositionsDeflated=Is;function Ds(e,t,r){var n=e.generatedColumn-t.generatedColumn;return n!==0||r||(n=be(e.source,t.source),n!==0)||(n=e.originalLine-t.originalLine,n!==0)||(n=e.originalColumn-t.originalColumn,n!==0)?n:be(e.name,t.name)}K.compareByGeneratedPositionsDeflatedNoLine=Ds;function be(e,t){return e===t?0:e===null?1:t===null?-1:e>t?1:-1}function Os(e,t){var r=e.generatedLine-t.generatedLine;return r!==0||(r=e.generatedColumn-t.generatedColumn,r!==0)||(r=be(e.source,t.source),r!==0)||(r=e.originalLine-t.originalLine,r!==0)||(r=e.originalColumn-t.originalColumn,r!==0)?r:be(e.name,t.name)}K.compareByGeneratedPositionsInflated=Os;function Ns(e){return JSON.parse(e.replace(/^\)]}'[^\n]*\n/,""))}K.parseSourceMapInput=Ns;function zs(e,t,r){if(t=t||"",e&&(e[e.length-1]!=="/"&&t[0]!=="/"&&(e+="/"),t=e+t),r){var n=ot(r);if(!n)throw new Error("sourceMapURL could not be parsed");if(n.path){var o=n.path.lastIndexOf("/");o>=0&&(n.path=n.path.substring(0,o+1))}t=ii(Ue(n),t)}return dr(t)}K.computeSourceURL=zs});var ui=Oe(ci=>{var gr=Lt(),br=Object.prototype.hasOwnProperty,Le=typeof Map<"u";function xe(){this._array=[],this._set=Le?new Map:Object.create(null)}xe.fromArray=function(t,r){for(var n=new xe,o=0,i=t.length;o=0)return r}else{var n=gr.toSetString(t);if(br.call(this._set,n))return this._set[n]}throw new Error('"'+t+'" is not in the set.')};xe.prototype.at=function(t){if(t>=0&&t{var pi=Lt();function Ms(e,t){var r=e.generatedLine,n=t.generatedLine,o=e.generatedColumn,i=t.generatedColumn;return n>r||n==r&&i>=o||pi.compareByGeneratedPositionsInflated(e,t)<=0}function Pt(){this._array=[],this._sorted=!0,this._last={generatedLine:-1,generatedColumn:0}}Pt.prototype.unsortedForEach=function(t,r){this._array.forEach(t,r)};Pt.prototype.add=function(t){Ms(this._last,t)?(this._last=t,this._array.push(t)):(this._sorted=!1,this._array.push(t))};Pt.prototype.toArray=function(){return this._sorted||(this._array.sort(pi.compareByGeneratedPositionsInflated),this._sorted=!0),this._array};hi.MappingList=Pt});var di=Oe(fi=>{var it=ni(),H=Lt(),It=ui().ArraySet,Rs=mi().MappingList;function oe(e){e||(e={}),this._file=H.getArg(e,"file",null),this._sourceRoot=H.getArg(e,"sourceRoot",null),this._skipValidation=H.getArg(e,"skipValidation",!1),this._sources=new It,this._names=new It,this._mappings=new Rs,this._sourcesContents=null}oe.prototype._version=3;oe.fromSourceMap=function(t){var r=t.sourceRoot,n=new oe({file:t.file,sourceRoot:r});return t.eachMapping(function(o){var i={generated:{line:o.generatedLine,column:o.generatedColumn}};o.source!=null&&(i.source=o.source,r!=null&&(i.source=H.relative(r,i.source)),i.original={line:o.originalLine,column:o.originalColumn},o.name!=null&&(i.name=o.name)),n.addMapping(i)}),t.sources.forEach(function(o){var i=o;r!==null&&(i=H.relative(r,o)),n._sources.has(i)||n._sources.add(i);var s=t.sourceContentFor(o);s!=null&&n.setSourceContent(o,s)}),n};oe.prototype.addMapping=function(t){var r=H.getArg(t,"generated"),n=H.getArg(t,"original",null),o=H.getArg(t,"source",null),i=H.getArg(t,"name",null);this._skipValidation||this._validateMapping(r,n,o,i),o!=null&&(o=String(o),this._sources.has(o)||this._sources.add(o)),i!=null&&(i=String(i),this._names.has(i)||this._names.add(i)),this._mappings.add({generatedLine:r.line,generatedColumn:r.column,originalLine:n!=null&&n.line,originalColumn:n!=null&&n.column,source:o,name:i})};oe.prototype.setSourceContent=function(t,r){var n=t;this._sourceRoot!=null&&(n=H.relative(this._sourceRoot,n)),r!=null?(this._sourcesContents||(this._sourcesContents=Object.create(null)),this._sourcesContents[H.toSetString(n)]=r):this._sourcesContents&&(delete this._sourcesContents[H.toSetString(n)],Object.keys(this._sourcesContents).length===0&&(this._sourcesContents=null))};oe.prototype.applySourceMap=function(t,r,n){var o=r;if(r==null){if(t.file==null)throw new Error(`SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, or the source map's "file" property. Both were omitted.`);o=t.file}var i=this._sourceRoot;i!=null&&(o=H.relative(i,o));var s=new It,u=new It;this._mappings.unsortedForEach(function(c){if(c.source===o&&c.originalLine!=null){var a=t.originalPositionFor({line:c.originalLine,column:c.originalColumn});a.source!=null&&(c.source=a.source,n!=null&&(c.source=H.join(n,c.source)),i!=null&&(c.source=H.relative(i,c.source)),c.originalLine=a.line,c.originalColumn=a.column,a.name!=null&&(c.name=a.name))}var l=c.source;l!=null&&!s.has(l)&&s.add(l);var p=c.name;p!=null&&!u.has(p)&&u.add(p)},this),this._sources=s,this._names=u,t.sources.forEach(function(c){var a=t.sourceContentFor(c);a!=null&&(n!=null&&(c=H.join(n,c)),i!=null&&(c=H.relative(i,c)),this.setSourceContent(c,a))},this)};oe.prototype._validateMapping=function(t,r,n,o){if(r&&typeof r.line!="number"&&typeof r.column!="number")throw new Error("original.line and original.column are not numbers -- you probably meant to omit the original mapping entirely and only map the generated position. If so, pass null for the original mapping instead of an object with empty or null values.");if(!(t&&"line"in t&&"column"in t&&t.line>0&&t.column>=0&&!r&&!n&&!o)){if(t&&"line"in t&&"column"in t&&r&&"line"in r&&"column"in r&&t.line>0&&t.column>=0&&r.line>0&&r.column>=0&&n)return;throw new Error("Invalid mapping: "+JSON.stringify({generated:t,source:n,original:r,name:o}))}};oe.prototype._serializeMappings=function(){for(var t=0,r=1,n=0,o=0,i=0,s=0,u="",c,a,l,p,m=this._mappings.toArray(),f=0,P=m.length;f0){if(!H.compareByGeneratedPositionsInflated(a,m[f-1]))continue;c+=","}c+=it.encode(a.generatedColumn-t),t=a.generatedColumn,a.source!=null&&(p=this._sources.indexOf(a.source),c+=it.encode(p-s),s=p,c+=it.encode(a.originalLine-1-o),o=a.originalLine-1,c+=it.encode(a.originalColumn-n),n=a.originalColumn,a.name!=null&&(l=this._names.indexOf(a.name),c+=it.encode(l-i),i=l)),u+=c}return u};oe.prototype._generateSourcesContent=function(t,r){return t.map(function(n){if(!this._sourcesContents)return null;r!=null&&(n=H.relative(r,n));var o=H.toSetString(n);return Object.prototype.hasOwnProperty.call(this._sourcesContents,o)?this._sourcesContents[o]:null},this)};oe.prototype.toJSON=function(){var t={version:this._version,sources:this._sources.toArray(),names:this._names.toArray(),mappings:this._serializeMappings()};return this._file!=null&&(t.file=this._file),this._sourceRoot!=null&&(t.sourceRoot=this._sourceRoot),this._sourcesContents&&(t.sourcesContent=this._generateSourcesContent(t.sources,t.sourceRoot)),t};oe.prototype.toString=function(){return JSON.stringify(this.toJSON())};fi.SourceMapGenerator=oe});var Ze={};b(Ze,{AtKeyword:()=>D,BadString:()=>Ae,BadUrl:()=>Y,CDC:()=>j,CDO:()=>ue,Colon:()=>O,Comma:()=>G,Comment:()=>E,Delim:()=>g,Dimension:()=>y,EOF:()=>$e,Function:()=>x,Hash:()=>v,Ident:()=>h,LeftCurlyBracket:()=>M,LeftParenthesis:()=>T,LeftSquareBracket:()=>U,Number:()=>d,Percentage:()=>A,RightCurlyBracket:()=>W,RightParenthesis:()=>w,RightSquareBracket:()=>V,Semicolon:()=>_,String:()=>q,Url:()=>F,WhiteSpace:()=>k});var $e=0,h=1,x=2,D=3,v=4,q=5,Ae=6,F=7,Y=8,g=9,d=10,A=11,y=12,k=13,ue=14,j=15,O=16,_=17,G=18,U=19,V=20,T=21,w=22,M=23,W=24,E=25;function B(e){return e>=48&&e<=57}function ee(e){return B(e)||e>=65&&e<=70||e>=97&&e<=102}function kt(e){return e>=65&&e<=90}function ls(e){return e>=97&&e<=122}function cs(e){return kt(e)||ls(e)}function us(e){return e>=128}function yt(e){return cs(e)||us(e)||e===95}function Ne(e){return yt(e)||B(e)||e===45}function ps(e){return e>=0&&e<=8||e===11||e>=14&&e<=31||e===127}function Je(e){return e===10||e===13||e===12}function pe(e){return Je(e)||e===32||e===9}function $(e,t){return!(e!==92||Je(t)||t===0)}function ze(e,t,r){return e===45?yt(t)||t===45||$(t,r):yt(e)?!0:e===92?$(e,t):!1}function wt(e,t,r){return e===43||e===45?B(t)?2:t===46&&B(r)?3:0:e===46?B(t)?2:0:B(e)?1:0}function vt(e){return e===65279||e===65534?1:0}var or=new Array(128),hs=128,et=130,ir=131,St=132,ar=133;for(let e=0;ee.length)return!1;for(let o=t;o=0&&pe(e.charCodeAt(t));t--);return t+1}function tt(e,t){for(;t=55296&&t<=57343||t>1114111)&&(t=65533),String.fromCodePoint(t)}var Fe=["EOF-token","ident-token","function-token","at-keyword-token","hash-token","string-token","bad-string-token","url-token","bad-url-token","delim-token","number-token","percentage-token","dimension-token","whitespace-token","CDO-token","CDC-token","colon-token","semicolon-token","comma-token","[-token","]-token","(-token",")-token","{-token","}-token"];function Be(e=null,t){return e===null||e.length0?vt(t.charCodeAt(0)):0,o=Be(e.lines,r),i=Be(e.columns,r),s=e.startLine,u=e.startColumn;for(let c=n;c{}){t=String(t||"");let n=t.length,o=Be(this.offsetAndType,t.length+1),i=Be(this.balance,t.length+1),s=0,u=0,c=0,a=-1;for(this.offsetAndType=null,this.balance=null,r(t,(l,p,m)=>{switch(l){default:i[s]=n;break;case u:{let f=c≠for(c=i[f],u=c>>we,i[s]=f,i[f++]=s;f>we:0}lookupOffset(t){return t+=this.tokenIndex,t0?t>we,this.tokenEnd=r&ne):(this.tokenIndex=this.tokenCount,this.next())}next(){let t=this.tokenIndex+1;t>we,this.tokenEnd=t&ne):(this.eof=!0,this.tokenIndex=this.tokenCount,this.tokenType=0,this.tokenStart=this.tokenEnd=this.source.length)}skipSC(){for(;this.tokenType===13||this.tokenType===25;)this.next()}skipUntilBalanced(t,r){let n=t,o,i;e:for(;n0?this.offsetAndType[n-1]&ne:this.firstCharOffset,r(this.source.charCodeAt(i))){case 1:break e;case 2:n++;break e;default:this.balance[o]===n&&(n=o)}}this.skip(n-this.tokenIndex)}forEachToken(t){for(let r=0,n=this.firstCharOffset;r>we;n=s,t(u,o,s,r)}}dump(){let t=new Array(this.tokenCount);return this.forEachToken((r,n,o,i)=>{t[i]={idx:i,type:Fe[r],chunk:this.source.substring(n,o),balance:this.balance[i]}}),t}};function ve(e,t){function r(p){return p=e.length){aString(l+f+1).padStart(c)+" |"+m).join(` +`)}let i=e.split(/\r\n?|\n|\f/),s=Math.max(1,t-n)-1,u=Math.min(t+n,i.length+1),c=Math.max(4,String(u).length)+1,a=0;r+=(Wo.length-1)*(i[t-1].substr(0,r-1).match(/\t/g)||[]).length,r>lr&&(a=r-qo+3,r=qo-2);for(let l=s;l<=u;l++)l>=0&&l0&&i[l].length>a?"\u2026":"")+i[l].substr(a,lr-2)+(i[l].length>a+lr-1?"\u2026":""));return[o(s,t),new Array(r+c+2).join("-")+"^",o(t,u)].filter(Boolean).join(` +`)}function cr(e,t,r,n,o){return Object.assign(Ee("SyntaxError",e),{source:t,offset:r,line:n,column:o,sourceFragment(s){return Yo({source:t,line:n,column:o},isNaN(s)?0:s)},get formattedMessage(){return`Parse error: ${e} +`+Yo({source:t,line:n,column:o},2)}})}function Go(e){let t=this.createList(),r=!1,n={recognizer:e};for(;!this.eof;){switch(this.tokenType){case 25:this.next();continue;case 13:r=!0,this.next();continue}let o=e.getNode.call(this,n);if(o===void 0)break;r&&(e.onWhiteSpace&&e.onWhiteSpace.call(this,o,t,n),r=!1),t.push(o)}return r&&e.onWhiteSpace&&e.onWhiteSpace.call(this,null,t,n),t}var Vo=()=>{},ds=33,gs=35,ur=59,Ko=123,Qo=0;function bs(e){return function(){return this[e]()}}function pr(e){let t=Object.create(null);for(let r in e){let n=e[r],o=n.parse||n;o&&(t[r]=o)}return t}function xs(e){let t={context:Object.create(null),scope:Object.assign(Object.create(null),e.scope),atrule:pr(e.atrule),pseudo:pr(e.pseudo),node:pr(e.node)};for(let r in e.parseContext)switch(typeof e.parseContext[r]){case"function":t.context[r]=e.parseContext[r];break;case"string":t.context[r]=bs(e.parseContext[r]);break}return{config:t,...t,...t.node}}function Xo(e){let t="",r="",n=!1,o=Vo,i=!1,s=new Et,u=Object.assign(new nt,xs(e||{}),{parseAtrulePrelude:!0,parseRulePrelude:!0,parseValue:!0,parseCustomProperty:!1,readSequence:Go,consumeUntilBalanceEnd:()=>0,consumeUntilLeftCurlyBracket(a){return a===Ko?1:0},consumeUntilLeftCurlyBracketOrSemicolon(a){return a===Ko||a===ur?1:0},consumeUntilExclamationMarkOrSemicolon(a){return a===ds||a===ur?1:0},consumeUntilSemicolonIncluded(a){return a===ur?2:0},createList(){return new I},createSingleNodeList(a){return new I().appendData(a)},getFirstListNode(a){return a&&a.first},getLastListNode(a){return a&&a.last},parseWithFallback(a,l){let p=this.tokenIndex;try{return a.call(this)}catch(m){if(i)throw m;let f=l.call(this,p);return i=!0,o(m,f),i=!1,f}},lookupNonWSType(a){let l;do if(l=this.lookupType(a++),l!==13)return l;while(l!==Qo);return Qo},charCodeAt(a){return a>=0&&af.toUpperCase()),p=`${/[[\](){}]/.test(l)?`"${l}"`:l} is expected`,m=this.tokenStart;switch(a){case 1:this.tokenType===2||this.tokenType===7?(m=this.tokenEnd-1,p="Identifier is expected but function found"):p="Identifier is expected";break;case 4:this.isDelim(gs)&&(this.next(),m++,p="Name is expected");break;case 11:this.tokenType===10&&(m=this.tokenEnd,p="Percent sign is expected");break}this.error(p,m)}this.next()},eatIdent(a){(this.tokenType!==1||this.lookupValue(0,a)===!1)&&this.error(`Identifier "${a}" is expected`),this.next()},eatDelim(a){this.isDelim(a)||this.error(`Delim "${String.fromCharCode(a)}" is expected`),this.next()},getLocation(a,l){return n?s.getLocationRange(a,l,r):null},getLocationFromList(a){if(n){let l=this.getFirstListNode(a),p=this.getLastListNode(a);return s.getLocationRange(l!==null?l.loc.start.offset-s.startOffset:this.tokenStart,p!==null?p.loc.end.offset-s.startOffset:this.tokenStart,r)}return null},error(a,l){let p=typeof l<"u"&&l",n=Boolean(l.positions),o=typeof l.onParseError=="function"?l.onParseError:Vo,i=!1,u.parseAtrulePrelude="parseAtrulePrelude"in l?Boolean(l.parseAtrulePrelude):!0,u.parseRulePrelude="parseRulePrelude"in l?Boolean(l.parseRulePrelude):!0,u.parseValue="parseValue"in l?Boolean(l.parseValue):!0,u.parseCustomProperty="parseCustomProperty"in l?Boolean(l.parseCustomProperty):!1;let{context:p="default",onComment:m}=l;if(!(p in u.context))throw new Error("Unknown context `"+p+"`");typeof m=="function"&&u.forEachToken((P,te,X)=>{if(P===25){let S=u.getLocation(te,X),R=ge(t,X-2,X,"*/")?t.slice(te+2,X-2):t.slice(te+2,X);m(R,S)}});let f=u.context[p].call(u,l);return u.eof||u.error(),f},{SyntaxError:cr,config:u.config})}var bi=ss(di(),1),gi=new Set(["Atrule","Selector","Declaration"]);function xi(e){let t=new bi.SourceMapGenerator,r={line:1,column:0},n={line:0,column:0},o={line:1,column:0},i={generated:o},s=1,u=0,c=!1,a=e.node;e.node=function(m){if(m.loc&&m.loc.start&&gi.has(m.type)){let f=m.loc.start.line,P=m.loc.start.column-1;(n.line!==f||n.column!==P)&&(n.line=f,n.column=P,r.line=s,r.column=u,c&&(c=!1,(r.line!==o.line||r.column!==o.column)&&t.addMapping(i)),c=!0,t.addMapping({source:m.loc.source,original:n,generated:r}))}a.call(this,m),c&&gi.has(m.type)&&(o.line=s,o.column=u)};let l=e.emit;e.emit=function(m,f,P){for(let te=0;teyr,spec:()=>Us});var Fs=43,Bs=45,xr=(e,t)=>{if(e===9&&(e=t),typeof e=="string"){let r=e.charCodeAt(0);return r>127?32768:r<<8}return e},yi=[[1,1],[1,2],[1,7],[1,8],[1,"-"],[1,10],[1,11],[1,12],[1,15],[1,21],[3,1],[3,2],[3,7],[3,8],[3,"-"],[3,10],[3,11],[3,12],[3,15],[4,1],[4,2],[4,7],[4,8],[4,"-"],[4,10],[4,11],[4,12],[4,15],[12,1],[12,2],[12,7],[12,8],[12,"-"],[12,10],[12,11],[12,12],[12,15],["#",1],["#",2],["#",7],["#",8],["#","-"],["#",10],["#",11],["#",12],["#",15],["-",1],["-",2],["-",7],["-",8],["-","-"],["-",10],["-",11],["-",12],["-",15],[10,1],[10,2],[10,7],[10,8],[10,10],[10,11],[10,12],[10,"%"],[10,15],["@",1],["@",2],["@",7],["@",8],["@","-"],["@",15],[".",10],[".",11],[".",12],["+",10],["+",11],["+",12],["/","*"]],_s=yi.concat([[1,4],[12,4],[4,4],[3,21],[3,5],[3,16],[11,11],[11,12],[11,2],[11,"-"],[22,1],[22,2],[22,11],[22,12],[22,4],[22,"-"]]);function ki(e){let t=new Set(e.map(([r,n])=>xr(r)<<16|xr(n)));return function(r,n,o){let i=xr(n,o),s=o.charCodeAt(0);return(s===Bs&&n!==1&&n!==2&&n!==15||s===Fs?t.has(r<<16|s<<8):t.has(r<<16|i))&&this.emit(" ",13,!0),i}}var Us=ki(yi),yr=ki(_s);var js=92;function Hs(e,t){if(typeof t=="function"){let r=null;e.children.forEach(n=>{r!==null&&t.call(this,r),this.node(n),r=n});return}e.children.forEach(this.node,this)}function qs(e){ve(e,(t,r,n)=>{this.token(t,e.slice(r,n))})}function wi(e){let t=new Map;for(let r in e.node){let n=e.node[r];typeof(n.generate||n)=="function"&&t.set(r,n.generate||n)}return function(r,n){let o="",i=0,s={node(c){if(t.has(c.type))t.get(c.type).call(u,c);else throw new Error("Unknown node type: "+c.type)},tokenBefore:yr,token(c,a){i=this.tokenBefore(i,c,a),this.emit(a,c,!1),c===9&&a.charCodeAt(0)===js&&this.emit(` +`,13,!0)},emit(c){o+=c},result(){return o}};n&&(typeof n.decorator=="function"&&(s=n.decorator(s)),n.sourceMap&&(s=xi(s)),n.mode in Dt&&(s.tokenBefore=Dt[n.mode]));let u={node:c=>s.node(c),children:Hs,token:(c,a)=>s.token(c,a),tokenize:qs};return s.node(r),s.result()}}function vi(e){return{fromPlainObject(t){return e(t,{enter(r){r.children&&!(r.children instanceof I)&&(r.children=new I().fromArray(r.children))}}),t},toPlainObject(t){return e(t,{leave(r){r.children&&r.children instanceof I&&(r.children=r.children.toArray())}}),t}}}var{hasOwnProperty:kr}=Object.prototype,at=function(){};function Si(e){return typeof e=="function"?e:at}function Ci(e,t){return function(r,n,o){r.type===t&&e.call(this,r,n,o)}}function Ws(e,t){let r=t.structure,n=[];for(let o in r){if(kr.call(r,o)===!1)continue;let i=r[o],s={name:o,type:!1,nullable:!1};Array.isArray(i)||(i=[i]);for(let u of i)u===null?s.nullable=!0:typeof u=="string"?s.type="node":Array.isArray(u)&&(s.type="list");s.type&&n.push(s)}return n.length?{context:t.walkContext,fields:n}:null}function Ys(e){let t={};for(let r in e.node)if(kr.call(e.node,r)){let n=e.node[r];if(!n.structure)throw new Error("Missed `structure` field in `"+r+"` node type definition");t[r]=Ws(r,n)}return t}function Ai(e,t){let r=e.fields.slice(),n=e.context,o=typeof n=="string";return t&&r.reverse(),function(i,s,u,c){let a;o&&(a=s[n],s[n]=i);for(let l of r){let p=i[l.name];if(!l.nullable||p){if(l.type==="list"){if(t?p.reduceRight(c,!1):p.reduce(c,!1))return!0}else if(u(p))return!0}}o&&(s[n]=a)}}function Ti({StyleSheet:e,Atrule:t,Rule:r,Block:n,DeclarationList:o}){return{Atrule:{StyleSheet:e,Atrule:t,Rule:r,Block:n},Rule:{StyleSheet:e,Atrule:t,Rule:r,Block:n},Declaration:{StyleSheet:e,Atrule:t,Rule:r,Block:n,DeclarationList:o}}}function Ei(e){let t=Ys(e),r={},n={},o=Symbol("break-walk"),i=Symbol("skip-node");for(let a in t)kr.call(t,a)&&t[a]!==null&&(r[a]=Ai(t[a],!1),n[a]=Ai(t[a],!0));let s=Ti(r),u=Ti(n),c=function(a,l){function p(S,R,ke){let z=m.call(X,S,R,ke);return z===o?!0:z===i?!1:!!(P.hasOwnProperty(S.type)&&P[S.type](S,X,p,te)||f.call(X,S,R,ke)===o)}let m=at,f=at,P=r,te=(S,R,ke,z)=>S||p(R,ke,z),X={break:o,skip:i,root:a,stylesheet:null,atrule:null,atrulePrelude:null,rule:null,selector:null,block:null,declaration:null,function:null};if(typeof l=="function")m=l;else if(l&&(m=Si(l.enter),f=Si(l.leave),l.reverse&&(P=n),l.visit)){if(s.hasOwnProperty(l.visit))P=l.reverse?u[l.visit]:s[l.visit];else if(!t.hasOwnProperty(l.visit))throw new Error("Bad value `"+l.visit+"` for `visit` option (should be: "+Object.keys(t).sort().join(", ")+")");m=Ci(m,l.visit),f=Ci(f,l.visit)}if(m===at&&f===at)throw new Error("Neither `enter` nor `leave` walker handler is set or both aren't a function");p(a)};return c.break=o,c.skip=i,c.find=function(a,l){let p=null;return c(a,function(m,f,P){if(l.call(this,m,f,P))return p=m,o}),p},c.findLast=function(a,l){let p=null;return c(a,{reverse:!0,enter(m,f,P){if(l.call(this,m,f,P))return p=m,o}}),p},c.findAll=function(a,l){let p=[];return c(a,function(m,f,P){l.call(this,m,f,P)&&p.push(m)}),p},c}function Gs(e){return e}function Vs(e){let{min:t,max:r,comma:n}=e;return t===0&&r===0?n?"#?":"*":t===0&&r===1?"?":t===1&&r===0?n?"#":"+":t===1&&r===1?"":(n?"#":"")+(t===r?"{"+t+"}":"{"+t+","+(r!==0?r:"")+"}")}function Ks(e){switch(e.type){case"Range":return" ["+(e.min===null?"-\u221E":e.min)+","+(e.max===null?"\u221E":e.max)+"]";default:throw new Error("Unknown node type `"+e.type+"`")}}function Qs(e,t,r,n){let o=e.combinator===" "||n?e.combinator:" "+e.combinator+" ",i=e.terms.map(s=>wr(s,t,r,n)).join(o);return e.explicit||r?(n||i[0]===","?"[":"[ ")+i+(n?"]":" ]"):i}function wr(e,t,r,n){let o;switch(e.type){case"Group":o=Qs(e,t,r,n)+(e.disallowEmpty?"!":"");break;case"Multiplier":return wr(e.term,t,r,n)+t(Vs(e),e);case"Type":o="<"+e.name+(e.opts?t(Ks(e.opts),e.opts):"")+">";break;case"Property":o="<'"+e.name+"'>";break;case"Keyword":o=e.name;break;case"AtKeyword":o="@"+e.name;break;case"Function":o=e.name+"(";break;case"String":case"Token":o=e.value;break;case"Comma":o=",";break;default:throw new Error("Unknown node type `"+e.type+"`")}return t(o,e)}function Pe(e,t){let r=Gs,n=!1,o=!1;return typeof t=="function"?r=t:t&&(n=Boolean(t.forceBraces),o=Boolean(t.compact),typeof t.decorate=="function"&&(r=t.decorate)),wr(e,r,n,o)}var Li={offset:0,line:1,column:1};function Xs(e,t){let r=e.tokens,n=e.longestMatch,o=n1?(l=Ot(i||t,"end")||st(Li,a),p=st(l)):(l=Ot(i,"start")||st(Ot(t,"start")||Li,a.slice(0,s)),p=Ot(i,"end")||st(l,a.substr(s,u))),{css:a,mismatchOffset:s,mismatchLength:u,start:l,end:p}}function Ot(e,t){let r=e&&e.loc&&e.loc[t];return r?"line"in r?st(r):r:null}function st({offset:e,line:t,column:r},n){let o={offset:e,line:t,column:r};if(n){let i=n.split(/\n|\r\n?|\f/);o.offset+=n.length,o.line+=i.length-1,o.column=i.length===1?o.column+n.length:i.pop().length+1}return o}var je=function(e,t){let r=Ee("SyntaxReferenceError",e+(t?" `"+t+"`":""));return r.reference=t,r},Pi=function(e,t,r,n){let o=Ee("SyntaxMatchError",e),{css:i,mismatchOffset:s,mismatchLength:u,start:c,end:a}=Xs(n,r);return o.rawMessage=e,o.syntax=t?Pe(t):"",o.css=i,o.mismatchOffset=s,o.mismatchLength=u,o.message=e+` + syntax: `+o.syntax+` + value: `+(i||"")+` + --------`+new Array(o.mismatchOffset+1).join("-")+"^",Object.assign(o,c),o.loc={source:r&&r.loc&&r.loc.source||"",start:c,end:a},o};var Nt=new Map,He=new Map,zt=45,Mt=$s,vr=Zs,_m=Sr;function Rt(e,t){return t=t||0,e.length-t>=2&&e.charCodeAt(t)===zt&&e.charCodeAt(t+1)===zt}function Sr(e,t){if(t=t||0,e.length-t>=3&&e.charCodeAt(t)===zt&&e.charCodeAt(t+1)!==zt){let r=e.indexOf("-",t+2);if(r!==-1)return e.substring(t,r+1)}return""}function $s(e){if(Nt.has(e))return Nt.get(e);let t=e.toLowerCase(),r=Nt.get(t);if(r===void 0){let n=Rt(t,0),o=n?"":Sr(t,0);r=Object.freeze({basename:t.substr(o.length),name:t,prefix:o,vendor:o,custom:n})}return Nt.set(e,r),r}function Zs(e){if(He.has(e))return He.get(e);let t=e,r=e[0];r==="/"?r=e[1]==="/"?"//":"/":r!=="_"&&r!=="*"&&r!=="$"&&r!=="#"&&r!=="+"&&r!=="&"&&(r="");let n=Rt(t,r.length);if(!n&&(t=t.toLowerCase(),He.has(t))){let u=He.get(t);return He.set(e,u),u}let o=n?"":Sr(t,r.length),i=t.substr(0,r.length+o.length),s=Object.freeze({basename:t.substr(i.length),name:t.substr(r.length),hack:r,vendor:o,prefix:i,custom:n});return He.set(e,s),s}var Ft=["initial","inherit","unset","revert","revert-layer"];var ct=43,he=45,Cr=110,qe=!0,el=!1;function Tr(e,t){return e!==null&&e.type===9&&e.value.charCodeAt(0)===t}function lt(e,t,r){for(;e!==null&&(e.type===13||e.type===25);)e=r(++t);return t}function Se(e,t,r,n){if(!e)return 0;let o=e.value.charCodeAt(t);if(o===ct||o===he){if(r)return 0;t++}for(;t6)return 0}return n}function Bt(e,t,r){if(!e)return 0;for(;Lr(r(t),Di);){if(++e>6)return 0;t++}return t}function Pr(e,t){let r=0;if(e===null||e.type!==1||!de(e.value,0,rl)||(e=t(++r),e===null))return 0;if(Lr(e,tl))return e=t(++r),e===null?0:e.type===1?Bt(ut(e,0,!0),++r,t):Lr(e,Di)?Bt(1,++r,t):0;if(e.type===10){let n=ut(e,1,!0);return n===0?0:(e=t(++r),e===null?r:e.type===12||e.type===10?!nl(e,Ii)||!ut(e,1,!1)?0:r+1:Bt(n,r,t))}return e.type===12?Bt(ut(e,1,!0),++r,t):0}var ol=["calc(","-moz-calc(","-webkit-calc("],Ir=new Map([[2,22],[21,22],[19,20],[23,24]]),il=["cm","mm","q","in","pt","pc","px","em","rem","ex","rex","cap","rcap","ch","rch","ic","ric","lh","rlh","vw","svw","lvw","dvw","vh","svh","lvh","dvh","vi","svi","lvi","dvi","vb","svb","lvb","dvb","vmin","svmin","lvmin","dvmin","vmax","svmax","lvmax","dvmax","cqw","cqh","cqi","cqb","cqmin","cqmax"],al=["deg","grad","rad","turn"],sl=["s","ms"],ll=["hz","khz"],cl=["dpi","dpcm","dppx","x"],ul=["fr"],pl=["db"],hl=["st"];function le(e,t){return te.max&&typeof e.max!="string")return!0}return!1}function ml(e,t){let r=0,n=[],o=0;e:do{switch(e.type){case 24:case 22:case 20:if(e.type!==r)break e;if(r=n.pop(),n.length===0){o++;break e}break;case 2:case 21:case 19:case 23:n.push(r),r=Ir.get(e.type);break}o++}while(e=t(o));return o}function ie(e){return function(t,r,n){return t===null?0:t.type===2&&zi(t.value,ol)?ml(t,r):e(t,r,n)}}function N(e){return function(t){return t===null||t.type!==e?0:1}}function fl(e){if(e===null||e.type!==1)return 0;let t=e.value.toLowerCase();return zi(t,Ft)||Ni(t,"default")?0:1}function dl(e){return e===null||e.type!==1||le(e.value,0)!==45||le(e.value,1)!==45?0:1}function gl(e){if(e===null||e.type!==4)return 0;let t=e.value.length;if(t!==4&&t!==5&&t!==7&&t!==9)return 0;for(let r=1;rUt,generate:()=>Pe,parse:()=>Ge,walk:()=>Vt});function Ut(e,t,r){return Object.assign(Ee("SyntaxError",e),{input:t,offset:r,rawMessage:e,message:e+` + `+t+` +--`+new Array((r||t.length)+1).join("-")+"^"})}var Sl=9,Cl=10,Al=12,Tl=13,El=32,jt=class{constructor(t){this.str=t,this.pos=0}charCodeAt(t){return t/[a-zA-Z0-9\-]/.test(String.fromCharCode(t))?1:0),ji={" ":1,"&&":2,"||":3,"|":4};function Wt(e){return e.substringToPos(e.findWsEnd(e.pos))}function We(e){let t=e.pos;for(;t=128||pt[r]===0)break}return e.pos===t&&e.error("Expect a keyword"),e.substringToPos(t)}function Yt(e){let t=e.pos;for(;t57)break}return e.pos===t&&e.error("Expect a number"),e.substringToPos(t)}function Ml(e){let t=e.str.indexOf("'",e.pos+1);return t===-1&&(e.pos=e.str.length,e.error("Expect an apostrophe")),e.substringToPos(t+1)}function Hi(e){let t=null,r=null;return e.eat(qt),t=Yt(e),e.charCode()===Rr?(e.pos++,e.charCode()!==_i&&(r=Yt(e))):r=t,e.eat(_i),{min:Number(t),max:r?Number(r):0}}function Rl(e){let t=null,r=!1;switch(e.charCode()){case Yi:e.pos++,t={min:0,max:0};break;case Mr:e.pos++,t={min:1,max:0};break;case Nr:e.pos++,t={min:0,max:1};break;case zr:e.pos++,r=!0,e.charCode()===qt?t=Hi(e):e.charCode()===Nr?(e.pos++,t={min:0,max:0}):t={min:1,max:0};break;case qt:t=Hi(e);break;default:return null}return{type:"Multiplier",comma:r,min:t.min,max:t.max,term:null}}function Ye(e,t){let r=Rl(e);return r!==null?(r.term=t,e.charCode()===zr&&e.charCodeAt(e.pos-1)===Mr?Ye(e,r):r):t}function Or(e){let t=e.peek();return t===""?null:{type:"Token",value:t}}function Fl(e){let t;return e.eat(Fr),e.eat(Ht),t=We(e),e.eat(Ht),e.eat(Gi),Ye(e,{type:"Property",name:t})}function Bl(e){let t=null,r=null,n=1;return e.eat(Gt),e.charCode()===Fi&&(e.peek(),n=-1),n==-1&&e.charCode()===Ui?e.peek():(t=n*Number(Yt(e)),pt[e.charCode()]!==0&&(t+=We(e))),Wt(e),e.eat(Rr),Wt(e),e.charCode()===Ui?e.peek():(n=1,e.charCode()===Fi&&(e.peek(),n=-1),r=n*Number(Yt(e)),pt[e.charCode()]!==0&&(r+=We(e))),e.eat(Br),{type:"Range",min:t,max:r}}function _l(e){let t,r=null;return e.eat(Fr),t=We(e),e.charCode()===Wi&&e.nextCharCode()===Nl&&(e.pos+=2,t+="()"),e.charCodeAt(e.findWsEnd(e.pos))===Gt&&(Wt(e),r=Bl(e)),e.eat(Gi),Ye(e,{type:"Type",name:t,opts:r})}function Ul(e){let t=We(e);return e.charCode()===Wi?(e.pos++,{type:"Function",name:t}):Ye(e,{type:"Keyword",name:t})}function jl(e,t){function r(o,i){return{type:"Group",terms:o,combinator:i,disallowEmpty:!1,explicit:!1}}let n;for(t=Object.keys(t).sort((o,i)=>ji[o]-ji[i]);t.length>0;){n=t.shift();let o=0,i=0;for(;o1&&(e.splice(i,o-i,r(e.slice(i,o),n)),o=i+1),i=-1))}i!==-1&&t.length&&e.splice(i,o-i,r(e.slice(i,o),n))}return n}function Vi(e){let t=[],r={},n,o=null,i=e.pos;for(;n=ql(e);)n.type!=="Spaces"&&(n.type==="Combinator"?((o===null||o.type==="Combinator")&&(e.pos=i,e.error("Unexpected combinator")),r[n.value]=!0):o!==null&&o.type!=="Combinator"&&(r[" "]=!0,t.push({type:"Combinator",value:" "})),t.push(n),o=n,i=e.pos);return o!==null&&o.type==="Combinator"&&(e.pos-=i,e.error("Unexpected combinator")),{type:"Group",terms:t,combinator:jl(t,r)||" ",disallowEmpty:!1,explicit:!1}}function Hl(e){let t;return e.eat(Gt),t=Vi(e),e.eat(Br),t.explicit=!0,e.charCode()===qi&&(e.pos++,t.disallowEmpty=!0),t}function ql(e){let t=e.charCode();if(t<128&&pt[t]===1)return Ul(e);switch(t){case Br:break;case Gt:return Ye(e,Hl(e));case Fr:return e.nextCharCode()===Ht?Fl(e):_l(e);case Bi:return{type:"Combinator",value:e.substringToPos(e.pos+(e.nextCharCode()===Bi?2:1))};case Ri:return e.pos++,e.eat(Ri),{type:"Combinator",value:"&&"};case Rr:return e.pos++,{type:"Comma"};case Ht:return Ye(e,{type:"String",value:Ml(e)});case Ol:case Ll:case Pl:case Dl:case Il:return{type:"Spaces",value:Wt(e)};case zl:return t=e.nextCharCode(),t<128&&pt[t]===1?(e.pos++,{type:"AtKeyword",name:We(e)}):Or(e);case Yi:case Mr:case Nr:case zr:case qi:break;case qt:if(t=e.nextCharCode(),t<48||t>57)return Or(e);break;default:return Or(e)}}function Ge(e){let t=new jt(e),r=Vi(t);return t.pos!==e.length&&t.error("Unexpected input"),r.terms.length===1&&r.terms[0].type==="Group"?r.terms[0]:r}var ht=function(){};function Ki(e){return typeof e=="function"?e:ht}function Vt(e,t,r){function n(s){switch(o.call(r,s),s.type){case"Group":s.terms.forEach(n);break;case"Multiplier":n(s.term);break;case"Type":case"Property":case"Keyword":case"AtKeyword":case"Function":case"String":case"Token":case"Comma":break;default:throw new Error("Unknown type: "+s.type)}i.call(r,s)}let o=ht,i=ht;if(typeof t=="function"?o=t:t&&(o=Ki(t.enter),i=Ki(t.leave)),o===ht&&i===ht)throw new Error("Neither `enter` nor `leave` walker handler is set or both aren't a function");n(e,r)}var Wl={decorator(e){let t=[],r=null;return{...e,node(n){let o=r;r=n,e.node.call(this,n),r=o},emit(n,o,i){t.push({type:o,value:n,node:i?null:r})},result(){return t}}}};function Yl(e){let t=[];return ve(e,(r,n,o)=>t.push({type:r,value:e.slice(n,o),node:null})),t}function Xi(e,t){return typeof e=="string"?Yl(e):t.generate(e,Wl)}var C={type:"Match"},L={type:"Mismatch"},Kt={type:"DisallowEmpty"},Gl=40,Vl=41;function Z(e,t,r){return t===C&&r===L||e===C&&t===C&&r===C?e:(e.type==="If"&&e.else===L&&t===C&&(t=e.then,e=e.match),{type:"If",match:e,then:t,else:r})}function Zi(e){return e.length>2&&e.charCodeAt(e.length-2)===Gl&&e.charCodeAt(e.length-1)===Vl}function $i(e){return e.type==="Keyword"||e.type==="AtKeyword"||e.type==="Function"||e.type==="Type"&&Zi(e.name)}function _r(e,t,r){switch(e){case" ":{let n=C;for(let o=t.length-1;o>=0;o--){let i=t[o];n=Z(i,n,L)}return n}case"|":{let n=L,o=null;for(let i=t.length-1;i>=0;i--){let s=t[i];if($i(s)&&(o===null&&i>0&&$i(t[i-1])&&(o=Object.create(null),n=Z({type:"Enum",map:o},C,n)),o!==null)){let u=(Zi(s.name)?s.name.slice(0,-1):s.name).toLowerCase();if(!(u in o)){o[u]=s;continue}}o=null,n=Z(s,C,n)}return n}case"&&":{if(t.length>5)return{type:"MatchOnce",terms:t,all:!0};let n=L;for(let o=t.length-1;o>=0;o--){let i=t[o],s;t.length>1?s=_r(e,t.filter(function(u){return u!==i}),!1):s=C,n=Z(i,s,n)}return n}case"||":{if(t.length>5)return{type:"MatchOnce",terms:t,all:!1};let n=r?C:L;for(let o=t.length-1;o>=0;o--){let i=t[o],s;t.length>1?s=_r(e,t.filter(function(u){return u!==i}),!0):s=C,n=Z(i,s,n)}return n}}}function Kl(e){let t=C,r=Ur(e.term);if(e.max===0)r=Z(r,Kt,L),t=Z(r,null,L),t.then=Z(C,C,t),e.comma&&(t.then.else=Z({type:"Comma",syntax:e},t,L));else for(let n=e.min||1;n<=e.max;n++)e.comma&&t!==C&&(t=Z({type:"Comma",syntax:e},t,L)),t=Z(r,Z(C,C,t),L);if(e.min===0)t=Z(C,C,t);else for(let n=0;n=65&&o<=90&&(o=o|32),o!==n)return!1}return!0}function tc(e){return e.type!==9?!1:e.value!=="?"}function ra(e){return e===null?!0:e.type===18||e.type===2||e.type===21||e.type===19||e.type===23||tc(e)}function na(e){return e===null?!0:e.type===22||e.type===20||e.type===24||e.type===9&&e.value==="/"}function rc(e,t,r){function n(){do R++,S=Rke&&(ke=R)}function a(){p={syntax:t.syntax,opts:t.syntax.opts||p!==null&&p.opts||null,prev:p},z={type:Hr,syntax:t.syntax,token:z.token,prev:z}}function l(){z.type===Hr?z=z.prev:z={type:oa,syntax:p.syntax,token:z.token,prev:z},p=p.prev}let p=null,m=null,f=null,P=null,te=0,X=null,S=null,R=-1,ke=0,z={type:Ql,syntax:null,token:null,prev:null};for(n();X===null&&++tef.tokenIndex)&&(f=P,P=!1);else if(f===null){X=$l;break}t=f.nextState,m=f.thenStack,p=f.syntaxStack,z=f.matchStack,R=f.tokenIndex,S=RR){for(;R":"<'"+t.name+"'>"));if(P!==!1&&S!==null&&t.type==="Type"&&(t.name==="custom-ident"&&S.type===1||t.name==="length"&&S.value==="0")){P===null&&(P=i(t,f)),t=L;break}a(),t=J.match;break}case"Keyword":{let Q=t.name;if(S!==null){let J=S.value;if(J.indexOf("\\")!==-1&&(J=J.replace(/\\[09].*$/,"")),jr(J,Q)){c(),t=C;break}}t=L;break}case"AtKeyword":case"Function":if(S!==null&&jr(S.value,t.name)){c(),t=C;break}t=L;break;case"Token":if(S!==null&&S.value===t.value){c(),t=C;break}t=L;break;case"Comma":S!==null&&S.type===18?ra(z.token)?t=L:(c(),t=na(S)?L:C):t=ra(z.token)||na(S)?C:L;break;case"String":let ae="",fe=R;for(;feia,isKeyword:()=>ic,isProperty:()=>oc,isType:()=>nc});function ia(e){function t(o){return o===null?!1:o.type==="Type"||o.type==="Property"||o.type==="Keyword"}function r(o){if(Array.isArray(o.match)){for(let i=0;ir.type==="Type"&&r.name===t)}function oc(e,t){return Wr(this,e,r=>r.type==="Property"&&r.name===t)}function ic(e){return Wr(this,e,t=>t.type==="Keyword")}function Wr(e,t,r){let n=ia.call(e,t);return n===null?!1:n.some(r)}function aa(e){return"node"in e?e.node:aa(e.match[0])}function sa(e){return"node"in e?e.node:sa(e.match[e.match.length-1])}function Gr(e,t,r,n,o){function i(u){if(u.syntax!==null&&u.syntax.type===n&&u.syntax.name===o){let c=aa(u),a=sa(u);e.syntax.walk(t,function(l,p,m){if(l===c){let f=new I;do{if(f.appendData(p.data),p.data===a)break;p=p.next}while(p!==null);s.push({parent:m,nodes:f})}})}Array.isArray(u.match)&&u.match.forEach(i)}let s=[];return r.matched!==null&&i(r.matched),s}var{hasOwnProperty:mt}=Object.prototype;function Vr(e){return typeof e=="number"&&isFinite(e)&&Math.floor(e)===e&&e>=0}function la(e){return Boolean(e)&&Vr(e.offset)&&Vr(e.line)&&Vr(e.column)}function ac(e,t){return function(n,o){if(!n||n.constructor!==Object)return o(n,"Type of node should be an Object");for(let i in n){let s=!0;if(mt.call(n,i)!==!1){if(i==="type")n.type!==e&&o(n,"Wrong node type `"+n.type+"`, expected `"+e+"`");else if(i==="loc"){if(n.loc===null)continue;if(n.loc&&n.loc.constructor===Object)if(typeof n.loc.source!="string")i+=".source";else if(!la(n.loc.start))i+=".start";else if(!la(n.loc.end))i+=".end";else continue;s=!1}else if(t.hasOwnProperty(i)){s=!1;for(let u=0;!s&&u");else if(Array.isArray(a))s.push("List");else throw new Error("Wrong value `"+a+"` in `"+e+"."+i+"` structure definition")}o[i]=s.join(" | ")}return{docs:o,check:ac(e,n)}}function ca(e){let t={};if(e.node){for(let r in e.node)if(mt.call(e.node,r)){let n=e.node[r];if(n.structure)t[r]=sc(r,n);else throw new Error("Missed `structure` field in `"+r+"` node type definition")}}return t}var lc=Qt(Ft.join(" | "));function Kr(e,t,r){let n={};for(let o in e)e[o].syntax&&(n[o]=r?e[o].syntax:Pe(e[o].syntax,{compact:t}));return n}function cc(e,t,r){let n={};for(let[o,i]of Object.entries(e))n[o]={prelude:i.prelude&&(r?i.prelude.syntax:Pe(i.prelude.syntax,{compact:t})),descriptors:i.descriptors&&Kr(i.descriptors,t,r)};return n}function uc(e){for(let t=0;t(n[o]=this.createDescriptor(r.descriptors[o],"AtruleDescriptor",o,t),n),Object.create(null)):null})}addProperty_(t,r){!r||(this.properties[t]=this.createDescriptor(r,"Property",t))}addType_(t,r){!r||(this.types[t]=this.createDescriptor(r,"Type",t))}checkAtruleName(t){if(!this.getAtrule(t))return new je("Unknown at-rule","@"+t)}checkAtrulePrelude(t,r){let n=this.checkAtruleName(t);if(n)return n;let o=this.getAtrule(t);if(!o.prelude&&r)return new SyntaxError("At-rule `@"+t+"` should not contain a prelude");if(o.prelude&&!r&&!Ve(this,o.prelude,"",!1).matched)return new SyntaxError("At-rule `@"+t+"` should contain a prelude")}checkAtruleDescriptorName(t,r){let n=this.checkAtruleName(t);if(n)return n;let o=this.getAtrule(t),i=Mt(r);if(!o.descriptors)return new SyntaxError("At-rule `@"+t+"` has no known descriptors");if(!o.descriptors[i.name]&&!o.descriptors[i.basename])return new je("Unknown at-rule descriptor",r)}checkPropertyName(t){if(!this.getProperty(t))return new je("Unknown property",t)}matchAtrulePrelude(t,r){let n=this.checkAtrulePrelude(t,r);if(n)return ce(null,n);let o=this.getAtrule(t);return o.prelude?Ve(this,o.prelude,r||"",!1):ce(null,null)}matchAtruleDescriptor(t,r,n){let o=this.checkAtruleDescriptorName(t,r);if(o)return ce(null,o);let i=this.getAtrule(t),s=Mt(r);return Ve(this,i.descriptors[s.name]||i.descriptors[s.basename],n,!1)}matchDeclaration(t){return t.type!=="Declaration"?ce(null,new Error("Not a Declaration node")):this.matchProperty(t.property,t.value)}matchProperty(t,r){if(vr(t).custom)return ce(null,new Error("Lexer matching doesn't applicable for custom properties"));let n=this.checkPropertyName(t);return n?ce(null,n):Ve(this,this.getProperty(t),r,!0)}matchType(t,r){let n=this.getType(t);return n?Ve(this,n,r,!1):ce(null,new je("Unknown type",t))}match(t,r){return typeof t!="string"&&(!t||!t.type)?ce(null,new je("Bad syntax")):((typeof t=="string"||!t.match)&&(t=this.createDescriptor(t,"Type","anonymous")),Ve(this,t,r,!1))}findValueFragments(t,r,n,o){return Gr(this,r,this.matchProperty(t,r),n,o)}findDeclarationValueFragments(t,r,n){return Gr(this,t.value,this.matchDeclaration(t),r,n)}findAllFragments(t,r,n){let o=[];return this.syntax.walk(t,{visit:"Declaration",enter:i=>{o.push.apply(o,this.findDeclarationValueFragments(i,r,n))}}),o}getAtrule(t,r=!0){let n=Mt(t);return(n.vendor&&r?this.atrules[n.name]||this.atrules[n.basename]:this.atrules[n.name])||null}getAtrulePrelude(t,r=!0){let n=this.getAtrule(t,r);return n&&n.prelude||null}getAtruleDescriptor(t,r){return this.atrules.hasOwnProperty(t)&&this.atrules.declarators&&this.atrules[t].declarators[r]||null}getProperty(t,r=!0){let n=vr(t);return(n.vendor&&r?this.properties[n.name]||this.properties[n.basename]:this.properties[n.name])||null}getType(t){return hasOwnProperty.call(this.types,t)?this.types[t]:null}validate(){function t(o,i,s,u){if(s.has(i))return s.get(i);s.set(i,!1),u.syntax!==null&&Vt(u.syntax,function(c){if(c.type!=="Type"&&c.type!=="Property")return;let a=c.type==="Type"?o.types:o.properties,l=c.type==="Type"?r:n;(!hasOwnProperty.call(a,c.name)||t(o,c.name,l,a[c.name]))&&s.set(i,!0)},this)}let r=new Map,n=new Map;for(let o in this.types)t(this,o,r,this.types[o]);for(let o in this.properties)t(this,o,n,this.properties[o]);return r=[...r.keys()].filter(o=>r.get(o)),n=[...n.keys()].filter(o=>n.get(o)),r.length||n.length?{types:r,properties:n}:null}dump(t,r){return{generic:this.generic,types:Kr(this.types,!r,t),properties:Kr(this.properties,!r,t),atrules:cc(this.atrules,!r,t)}}toString(){return JSON.stringify(this.dump())}};var{hasOwnProperty:Qe}=Object.prototype,pc={generic:!0,types:Qr,atrules:{prelude:pa,descriptors:pa},properties:Qr,parseContext:hc,scope:ma,atrule:["parse"],pseudo:["parse"],node:["name","structure","parse","generate","walkContext"]};function Xt(e){return e&&e.constructor===Object}function ha(e){return Xt(e)?{...e}:e}function hc(e,t){return Object.assign(e,t)}function ma(e,t){for(let r in t)Qe.call(t,r)&&(Xt(e[r])?ma(e[r],t[r]):e[r]=ha(t[r]));return e}function ua(e,t){return typeof t=="string"&&/^\s*\|/.test(t)?typeof e=="string"?e+t:t.replace(/^\s*\|\s*/,""):t||null}function Qr(e,t){if(typeof t=="string")return ua(e,t);let r={...e};for(let n in t)Qe.call(t,n)&&(r[n]=ua(Qe.call(e,n)?e[n]:void 0,t[n]));return r}function pa(e,t){let r=Qr(e,t);return!Xt(r)||Object.keys(r).length?r:null}function ft(e,t,r){for(let n in r)if(Qe.call(r,n)!==!1){if(r[n]===!0)Qe.call(t,n)&&(e[n]=ha(t[n]));else if(r[n]){if(typeof r[n]=="function"){let o=r[n];e[n]=o({},e[n]),e[n]=o(e[n]||{},t[n])}else if(Xt(r[n])){let o={};for(let i in e[n])o[i]=ft({},e[n][i],r[n]);for(let i in t[n])o[i]=ft(o[i]||{},t[n][i],r[n]);e[n]=o}else if(Array.isArray(r[n])){let o={},i=r[n].reduce(function(s,u){return s[u]=!0,s},{});for(let[s,u]of Object.entries(e[n]||{}))o[s]={},u&&ft(o[s],u,i);for(let s in t[n])Qe.call(t[n],s)&&(o[s]||(o[s]={}),t[n]&&t[n][s]&&ft(o[s],t[n][s],i));e[n]=o}}}return e}var $t=(e,t)=>ft(e,t,pc);function fa(e){let t=Xo(e),r=Ei(e),n=wi(e),{fromPlainObject:o,toPlainObject:i}=vi(r),s={lexer:null,createLexer:u=>new Ke(u,s,s.lexer.structure),tokenize:ve,parse:t,generate:n,walk:r,find:r.find,findLast:r.findLast,findAll:r.findAll,fromPlainObject:o,toPlainObject:i,fork(u){let c=$t({},e);return fa(typeof u=="function"?u(c,Object.assign):$t(c,u))}};return s.lexer=new Ke({generic:!0,types:e.types,atrules:e.atrules,properties:e.properties,node:e.node},s),s}var Xr=e=>fa($t({},e));var da={generic:!0,types:{"absolute-size":"xx-small|x-small|small|medium|large|x-large|xx-large|xxx-large","alpha-value":"|","angle-percentage":"|","angular-color-hint":"","angular-color-stop":"&&?","angular-color-stop-list":"[ [, ]?]# , ","animateable-feature":"scroll-position|contents|",attachment:"scroll|fixed|local","attr()":"attr( ? [, ]? )","attr-matcher":"['~'|'|'|'^'|'$'|'*']? '='","attr-modifier":"i|s","attribute-selector":"'[' ']'|'[' [|] ? ']'","auto-repeat":"repeat( [auto-fill|auto-fit] , [? ]+ ? )","auto-track-list":"[? [|]]* ? [? [|]]* ?","baseline-position":"[first|last]? baseline","basic-shape":"||||","bg-image":"none|","bg-layer":"|| [/ ]?||||||||","bg-position":"[[left|center|right|top|bottom|]|[left|center|right|] [top|center|bottom|]|[center|[left|right] ?]&&[center|[top|bottom] ?]]","bg-size":"[|auto]{1,2}|cover|contain","blur()":"blur( )","blend-mode":"normal|multiply|screen|overlay|darken|lighten|color-dodge|color-burn|hard-light|soft-light|difference|exclusion|hue|saturation|color|luminosity",box:"border-box|padding-box|content-box","brightness()":"brightness( )","calc()":"calc( )","calc-sum":" [['+'|'-'] ]*","calc-product":" ['*' |'/' ]*","calc-value":"|||( )","cf-final-image":"|","cf-mixing-image":"?&&","circle()":"circle( []? [at ]? )","clamp()":"clamp( #{3} )","class-selector":"'.' ","clip-source":"",color:"|||||||||currentcolor|","color-stop":"|","color-stop-angle":"{1,2}","color-stop-length":"{1,2}","color-stop-list":"[ [, ]?]# , ",combinator:"'>'|'+'|'~'|['||']","common-lig-values":"[common-ligatures|no-common-ligatures]","compat-auto":"searchfield|textarea|push-button|slider-horizontal|checkbox|radio|square-button|menulist|listbox|meter|progress-bar|button","composite-style":"clear|copy|source-over|source-in|source-out|source-atop|destination-over|destination-in|destination-out|destination-atop|xor","compositing-operator":"add|subtract|intersect|exclude","compound-selector":"[? * [ *]*]!","compound-selector-list":"#","complex-selector":" [? ]*","complex-selector-list":"#","conic-gradient()":"conic-gradient( [from ]? [at ]? , )","contextual-alt-values":"[contextual|no-contextual]","content-distribution":"space-between|space-around|space-evenly|stretch","content-list":"[|contents||||||]+","content-position":"center|start|end|flex-start|flex-end","content-replacement":"","contrast()":"contrast( [] )",counter:"|","counter()":"counter( , ? )","counter-name":"","counter-style":"|symbols( )","counter-style-name":"","counters()":"counters( , , ? )","cross-fade()":"cross-fade( , ? )","cubic-bezier-timing-function":"ease|ease-in|ease-out|ease-in-out|cubic-bezier( , , , )","deprecated-system-color":"ActiveBorder|ActiveCaption|AppWorkspace|Background|ButtonFace|ButtonHighlight|ButtonShadow|ButtonText|CaptionText|GrayText|Highlight|HighlightText|InactiveBorder|InactiveCaption|InactiveCaptionText|InfoBackground|InfoText|Menu|MenuText|Scrollbar|ThreeDDarkShadow|ThreeDFace|ThreeDHighlight|ThreeDLightShadow|ThreeDShadow|Window|WindowFrame|WindowText","discretionary-lig-values":"[discretionary-ligatures|no-discretionary-ligatures]","display-box":"contents|none","display-inside":"flow|flow-root|table|flex|grid|ruby","display-internal":"table-row-group|table-header-group|table-footer-group|table-row|table-cell|table-column-group|table-column|table-caption|ruby-base|ruby-text|ruby-base-container|ruby-text-container","display-legacy":"inline-block|inline-list-item|inline-table|inline-flex|inline-grid","display-listitem":"?&&[flow|flow-root]?&&list-item","display-outside":"block|inline|run-in","drop-shadow()":"drop-shadow( {2,3} ? )","east-asian-variant-values":"[jis78|jis83|jis90|jis04|simplified|traditional]","east-asian-width-values":"[full-width|proportional-width]","element()":"element( , [first|start|last|first-except]? )|element( )","ellipse()":"ellipse( [{2}]? [at ]? )","ending-shape":"circle|ellipse","env()":"env( , ? )","explicit-track-list":"[? ]+ ?","family-name":"|+","feature-tag-value":" [|on|off]?","feature-type":"@stylistic|@historical-forms|@styleset|@character-variant|@swash|@ornaments|@annotation","feature-value-block":" '{' '}'","feature-value-block-list":"+","feature-value-declaration":" : + ;","feature-value-declaration-list":"","feature-value-name":"","fill-rule":"nonzero|evenodd","filter-function":"|||||||||","filter-function-list":"[|]+","final-bg-layer":"<'background-color'>|||| [/ ]?||||||||","fit-content()":"fit-content( [|] )","fixed-breadth":"","fixed-repeat":"repeat( [] , [? ]+ ? )","fixed-size":"|minmax( , )|minmax( , )","font-stretch-absolute":"normal|ultra-condensed|extra-condensed|condensed|semi-condensed|semi-expanded|expanded|extra-expanded|ultra-expanded|","font-variant-css21":"[normal|small-caps]","font-weight-absolute":"normal|bold|","frequency-percentage":"|","general-enclosed":"[ )]|( )","generic-family":"serif|sans-serif|cursive|fantasy|monospace|-apple-system","generic-name":"serif|sans-serif|cursive|fantasy|monospace","geometry-box":"|fill-box|stroke-box|view-box",gradient:"||||||<-legacy-gradient>","grayscale()":"grayscale( )","grid-line":"auto||[&&?]|[span&&[||]]","historical-lig-values":"[historical-ligatures|no-historical-ligatures]","hsl()":"hsl( [/ ]? )|hsl( , , , ? )","hsla()":"hsla( [/ ]? )|hsla( , , , ? )",hue:"|","hue-rotate()":"hue-rotate( )","hwb()":"hwb( [|none] [|none] [|none] [/ [|none]]? )",image:"||||||","image()":"image( ? [? , ?]! )","image-set()":"image-set( # )","image-set-option":"[|] [||type( )]","image-src":"|","image-tags":"ltr|rtl","inflexible-breadth":"||min-content|max-content|auto","inset()":"inset( {1,4} [round <'border-radius'>]? )","invert()":"invert( )","keyframes-name":"|","keyframe-block":"# { }","keyframe-block-list":"+","keyframe-selector":"from|to|","layer()":"layer( )","layer-name":" ['.' ]*","leader()":"leader( )","leader-type":"dotted|solid|space|","length-percentage":"|","line-names":"'[' * ']'","line-name-list":"[|]+","line-style":"none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset","line-width":"|thin|medium|thick","linear-color-hint":"","linear-color-stop":" ?","linear-gradient()":"linear-gradient( [|to ]? , )","mask-layer":"|| [/ ]?||||||[|no-clip]||||","mask-position":"[|left|center|right] [|top|center|bottom]?","mask-reference":"none||","mask-source":"","masking-mode":"alpha|luminance|match-source","matrix()":"matrix( #{6} )","matrix3d()":"matrix3d( #{16} )","max()":"max( # )","media-and":" [and ]+","media-condition":"|||","media-condition-without-or":"||","media-feature":"( [||] )","media-in-parens":"( )||","media-not":"not ","media-or":" [or ]+","media-query":"|[not|only]? [and ]?","media-query-list":"#","media-type":"","mf-boolean":"","mf-name":"","mf-plain":" : ","mf-range":" ['<'|'>']? '='? | ['<'|'>']? '='? | '<' '='? '<' '='? | '>' '='? '>' '='? ","mf-value":"|||","min()":"min( # )","minmax()":"minmax( [||min-content|max-content|auto] , [|||min-content|max-content|auto] )","name-repeat":"repeat( [|auto-fill] , + )","named-color":"transparent|aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|rebeccapurple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen|<-non-standard-color>","namespace-prefix":"","ns-prefix":"[|'*']? '|'","number-percentage":"|","numeric-figure-values":"[lining-nums|oldstyle-nums]","numeric-fraction-values":"[diagonal-fractions|stacked-fractions]","numeric-spacing-values":"[proportional-nums|tabular-nums]",nth:"|even|odd","opacity()":"opacity( [] )","overflow-position":"unsafe|safe","outline-radius":"|","page-body":"? [; ]?| ","page-margin-box":" '{' '}'","page-margin-box-type":"@top-left-corner|@top-left|@top-center|@top-right|@top-right-corner|@bottom-left-corner|@bottom-left|@bottom-center|@bottom-right|@bottom-right-corner|@left-top|@left-middle|@left-bottom|@right-top|@right-middle|@right-bottom","page-selector-list":"[#]?","page-selector":"+| *","page-size":"A5|A4|A3|B5|B4|JIS-B5|JIS-B4|letter|legal|ledger","path()":"path( [ ,]? )","paint()":"paint( , ? )","perspective()":"perspective( )","polygon()":"polygon( ? , [ ]# )",position:"[[left|center|right]||[top|center|bottom]|[left|center|right|] [top|center|bottom|]?|[[left|right] ]&&[[top|bottom] ]]","pseudo-class-selector":"':' |':' ')'","pseudo-element-selector":"':' ","pseudo-page":": [left|right|first|blank]",quote:"open-quote|close-quote|no-open-quote|no-close-quote","radial-gradient()":"radial-gradient( [||]? [at ]? , )","relative-selector":"? ","relative-selector-list":"#","relative-size":"larger|smaller","repeat-style":"repeat-x|repeat-y|[repeat|space|round|no-repeat]{1,2}","repeating-conic-gradient()":"repeating-conic-gradient( [from ]? [at ]? , )","repeating-linear-gradient()":"repeating-linear-gradient( [|to ]? , )","repeating-radial-gradient()":"repeating-radial-gradient( [||]? [at ]? , )","rgb()":"rgb( {3} [/ ]? )|rgb( {3} [/ ]? )|rgb( #{3} , ? )|rgb( #{3} , ? )","rgba()":"rgba( {3} [/ ]? )|rgba( {3} [/ ]? )|rgba( #{3} , ? )|rgba( #{3} , ? )","rotate()":"rotate( [|] )","rotate3d()":"rotate3d( , , , [|] )","rotateX()":"rotateX( [|] )","rotateY()":"rotateY( [|] )","rotateZ()":"rotateZ( [|] )","saturate()":"saturate( )","scale()":"scale( , ? )","scale3d()":"scale3d( , , )","scaleX()":"scaleX( )","scaleY()":"scaleY( )","scaleZ()":"scaleZ( )","self-position":"center|start|end|self-start|self-end|flex-start|flex-end","shape-radius":"|closest-side|farthest-side","skew()":"skew( [|] , [|]? )","skewX()":"skewX( [|] )","skewY()":"skewY( [|] )","sepia()":"sepia( )",shadow:"inset?&&{2,4}&&?","shadow-t":"[{2,3}&&?]",shape:"rect( , , , )|rect( )","shape-box":"|margin-box","side-or-corner":"[left|right]||[top|bottom]","single-animation":"