Skip to content

Commit ddc108e

Browse files
committed
Optimize :has() matching
The result from matching an element against each :has() argument is now stored in the selector cache, which is now created by default, not just when resolving style values. This allows passing the WPT test related to :has() complexity, which includes `main:has(nonexistent) span`, which matches `main:has(...)` for each of the many `span` descendants of `main`.
1 parent d1d787e commit ddc108e

1 file changed

Lines changed: 17 additions & 3 deletions

File tree

lib/match/selector.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,12 @@ function createContext(tree, { elementCache = new Map, namespaces = new Map, roo
179179
* @param {object} [options]
180180
* @returns {object}
181181
*/
182-
function createOptions({ root, scopes }, { pseudos = [], ...options } = {}) {
182+
function createOptions({ root, scopes }, { pseudos = [], selectorCache = new Map, ...options } = {}) {
183183
return {
184184
inclusive: scopes ? scopes.inclusive : root && isShadowRoot(root),
185185
limits: scopes?.limits,
186186
pseudos,
187+
selectorCache,
187188
...options,
188189
}
189190
}
@@ -339,7 +340,18 @@ function hasMeterValue(element, value) {
339340
* @see {@link https://drafts.csswg.org/selectors-4/#has-pseudo}
340341
*/
341342
function hasRelativeMatching(element, selectors, context, options) {
342-
return selectors.some(([combinator, [head, tail]]) => {
343+
const { selectorCache } = options
344+
let cache
345+
if (selectorCache.has(element)) {
346+
cache = selectorCache.get(element)
347+
} else {
348+
selectorCache.set(element, cache = new Map)
349+
}
350+
return selectors.some(([combinator, selector]) => {
351+
if (cache.has(selector)) {
352+
return cache.get(selector)
353+
}
354+
const [head, tail] = selector
343355
function matchRight(left, combinator, compound, index = 0) {
344356
for (const right of rightCombined(left, combinator)) {
345357
if (
@@ -351,7 +363,9 @@ function hasRelativeMatching(element, selectors, context, options) {
351363
}
352364
return false
353365
}
354-
return matchRight(element, combinator, head)
366+
const result = matchRight(element, combinator, head)
367+
cache.set(selector, result)
368+
return result
355369
})
356370
}
357371

0 commit comments

Comments
 (0)