Include tag and attribute selectors in guarded pseudo-class invalidation
plans. This keeps selectors like a:hover .target from applying work to
unrelated descendants when :hover changes on other elements.
Store both original and lowercase attribute names for invalidation keys.
HTML attribute mutations still hit lowercase selector buckets, while SVG
and MathML selectors such as [viewBox] keep their case-sensitive guards.
Use the same names for :has() metadata and mutation feature filtering so
attribute changes do not get filtered out after metadata lookup.
Match tag invalidation properties against lowercased local names, like
invalidation data and rule cache buckets do. The regression tests cover
SVG hover fanout and case-sensitive :has() attribute mutation.
Only mark an element itself dirty for interaction pseudo-class changes
when matching rules can plausibly match that element. Check every style
scope observing the element, and keep the normal invalidation plan for
descendants and siblings.
Treat pseudo-elements as neutral during the plausibility check so rules
like a:hover::before still invalidate the originating element. Add text
coverage for non-matching hover rules and pseudo-element hover changes.
Compound selectors with a non-rightmost pseudo-class used to register
their descendant and sibling invalidation plans directly under the
pseudo-class property. A selector like `.item:hover * .target` ran
the descendant plan for every hovered element, even when that element
could not match `.item`.
Store those non-rightmost plans behind a guard made from stable class
and id subject features in the same compound selector. Deliberately
leave pseudo-classes out of guards, since one mutation can change
multiple state pseudo-classes at once. Also leave tag and attribute
selectors out, since their matching depends on document and namespace
case-sensitivity that the guard property does not carry.
The new style-invalidation test covers the GitHub-shaped
`:hover * :not(...)` case, co-invalidated `:link` and `:any-link`,
partial or complex `:is()`/`:where()` alternatives, and case-sensitive
SVG tag and attribute selectors.
Match :focus-within by walking flat-tree parents so shadow hosts and
slotted content follow the Selectors definition instead of DOM ancestry.
When focus state changes, also run pseudo-class invalidation in shadow
style scopes that can observe the focused node. This lets shadow-root
rules such as :host(:focus-within)::before and ::slotted(:focus) update
without waiting for an unrelated DOM mutation to restyle the host.
Add focused coverage for host pseudo-elements, shadow descendants, and
slotted controls, and update the style-invalidation counter baseline for
the lower focus-family no-op restyle counts.
When a sheet's rightmost compound carries :first-child, :last-child,
or :only-child but no other class/tag/id/attr feature, the sheet
add/remove path can target the boundary children directly instead of
falling back to a whole-subtree invalidation.
The narrowing in extend_style_sheet_invalidation_set_with_style_rule
deliberately only kicks in when the structural pseudo is the only
feature in the rightmost compound. A selector like `.foo:first-child`
keeps targeting `.foo` rather than every first-child in the
document.
Adds matchers for :first-child / :last-child / :only-child in
InvalidationSetMatcher so the targeted walk can recognize them on
each candidate element.
Stylesheet add/remove previously fell back to a whole-subtree
invalidation whenever a sheet's rightmost compound carried one of
these pseudo-classes. They each match a small, knowable set of
elements at any moment, so the invalidation walk can target them
directly:
- :host matches one element per shadow root and :root matches the
html element. Mark them targetable.
- :hover, :focus, :focus-visible, :focus-within, :active, and
:target all match at most a handful of elements at a time. Mark
them targetable and add matchers in InvalidationSetMatcher that
consult Document::hovered_node, focused_area, target_element,
and the element's own focused/active state.
Element.cpp still contained the CSS logic for deciding whether an
invalidation set references features present on an element. Move that
matcher into CSS::Invalidation::InvalidationSetMatcher.
The helper uses Element's public API for classes, id, attributes,
pseudo-class state, and removed-attribute tracking. This keeps Element
focused on DOM state while CSS::Invalidation owns selector feature
matching.