From 12dc04a304287c5054559facd0a16ab0edf29292 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Thu, 10 Dec 2020 13:53:19 -0800 Subject: [PATCH] feat(selectors): optimize old->new selectors conversion for text (#4671) --- src/server/common/cssParser.ts | 3 ++- src/server/common/selectorParser.ts | 22 ++++------------------ src/server/injected/selectorEvaluator.ts | 13 +++++++++++++ 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/server/common/cssParser.ts b/src/server/common/cssParser.ts index f98da9fb67..d54c894ee7 100644 --- a/src/server/common/cssParser.ts +++ b/src/server/common/cssParser.ts @@ -16,7 +16,8 @@ import * as css from './cssTokenizer'; -type ClauseCombinator = '' | '>' | '+' | '~'; +// Note: '>=' is used internally for text engine to preserve backwards compatibility. +type ClauseCombinator = '' | '>' | '+' | '~' | '>='; // TODO: consider // - key=value // - operators like `=`, `|=`, `~=`, `*=`, `/` diff --git a/src/server/common/selectorParser.ts b/src/server/common/selectorParser.ts index f40c657121..b22c9a6474 100644 --- a/src/server/common/selectorParser.ts +++ b/src/server/common/selectorParser.ts @@ -55,7 +55,7 @@ export function parseSelector(selector: string, customNames: Set): Parse } const chain = (from: number, to: number): CSSComplexSelector => { - let result: CSSComplexSelector = { simples: [] }; + const result: CSSComplexSelector = { simples: [] }; for (const part of v1.parts.slice(from, to)) { let name = part.name; let wrapInLight = false; @@ -70,24 +70,14 @@ export function parseSelector(selector: string, customNames: Set): Parse simple = callWith('is', parsed.selector); } else if (name === 'text') { simple = textSelectorToSimple(part.body); + if (result.simples.length) + result.simples[result.simples.length - 1].combinator = '>='; } else { simple = callWith(name, [part.body]); } if (wrapInLight) simple = callWith('light', [simpleToComplex(simple)]); - if (name === 'text') { - const copy = result.simples.map(one => { - return { selector: copySimple(one.selector), combinator: one.combinator }; - }); - copy.push({ selector: simple, combinator: '' }); - if (!result.simples.length) - result.simples.push({ selector: callWith('scope', []), combinator: '' }); - const last = result.simples[result.simples.length - 1]; - last.selector.functions.push({ name: 'is', args: [simpleToComplex(simple)] }); - result = simpleToComplex(callWith('is', [{ simples: copy }, result])); - } else { - result.simples.push({ selector: simple, combinator: '' }); - } + result.simples.push({ selector: simple, combinator: '' }); } return result; }; @@ -110,10 +100,6 @@ function simpleToComplex(simple: CSSSimpleSelector): CSSComplexSelector { return { simples: [{ selector: simple, combinator: '' }]}; } -function copySimple(simple: CSSSimpleSelector): CSSSimpleSelector { - return { css: simple.css, functions: simple.functions.slice() }; -} - function textSelectorToSimple(selector: string): CSSSimpleSelector { function unescape(s: string): string { if (!s.includes('\\')) diff --git a/src/server/injected/selectorEvaluator.ts b/src/server/injected/selectorEvaluator.ts index 2befe53c70..4b715acd2d 100644 --- a/src/server/injected/selectorEvaluator.ts +++ b/src/server/injected/selectorEvaluator.ts @@ -214,6 +214,19 @@ export class SelectorEvaluatorImpl implements SelectorEvaluator { } return false; } + if (combinator === '>=') { + let parent: Element | undefined = element; + while (parent) { + if (this._matchesSimple(parent, simple, context)) { + if (this._matchesParents(parent, complex, index - 1, context)) + return true; + if (complex.simples[index - 1].combinator === '') + break; + } + parent = parentElementOrShadowHostInContext(parent, context); + } + return false; + } throw new Error(`Unsupported combinator "${combinator}"`); }); }