LibWeb: Add result caching for :has() pseudo-class matching

The `:has()` pseudo-class requires traversing descendants (or siblings)
to find matches.

With this change we cache results keyed by `(Selector*, Element*)`
pairs. The cache is stored in `StyleComputer` and cleared at the start
of each style computation pass in `Document::update_style()`.

When `:has()` uses a descendant combinator and we find a match, we also
cache that all ancestors between the matching descendant and the
anchor match. For example with `div:has(.target)`:

```html
<div id="A">  <!-- checking :has(.target) here -->
  <div id="B">
    <div id="C">
      <span class="target"/>
    </div>
  </div>
</div>
```

When we find `.target` while checking `div#A`, we also cache that
`div#B` and `div#C` match `:has(.target)` since they also contain
`.target`. Later when styling these elements, we get cache hits and skip
traversal.
This commit is contained in:
Aliaksandr Kalenik
2026-01-04 10:59:52 +01:00
committed by Andreas Kling
parent 23c39298da
commit 3353ec9663
Notes: github-actions[bot] 2026-01-04 18:37:35 +00:00
7 changed files with 91 additions and 1 deletions

View File

@@ -1611,6 +1611,7 @@ void Document::update_style()
evaluate_media_rules();
style_computer().reset_has_result_cache();
style_computer().reset_ancestor_filter();
auto invalidation = update_style_recursively(*this, style_computer(), false, false);