Commit graph

2542 commits

Author SHA1 Message Date
Andreas Kling
b01ce45c14 LibWeb: Guard non-subject pseudo-class invalidations
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.
2026-05-24 15:32:39 +02:00
Andreas Kling
fa579754bf LibWeb: Use invalidation plans for pseudo-class changes
Replace PseudoClassInvalidator's subtree scan with targeted
invalidation for elements whose pseudo-class state changes. This scopes
state changes to affected selectors instead of rechecking a whole
common-ancestor subtree.

Use the ancestor chain that matches each pseudo-class. Hover walks the
shadow-including chain. :focus-within walks the flat-tree chain, so
slotted content invalidates its assigned slot and relevant shadow-tree
descendants. Focus, FocusVisible, and Target invalidate just the state
node.

Route each affected element through Element::invalidate_style with the
pseudo-class property. This uses the same invalidation-plan machinery as
Disabled, Checked, and other pseudo-class state changes.

Interaction state pseudo classes are not tracked in :has() metadata, so
schedule :has() ancestor invalidation explicitly when the state flips.
The callers no longer need cross-scope branching. The chain walk handles
shadow boundaries, and property invalidation already visits every
observer style scope.
2026-05-23 23:37:36 +02:00
Andreas Kling
817cb7432f LibWeb: Keep shadow rule caches for user styles
Do not invalidate shadow-root rule caches when the document user style
sheet changes. Shadow trees still need style invalidation because
document user rules can match their descendants, but their local author
rule caches do not include the document user sheet.

Since user and content-blocker sheets now live only in the document
scope, shadow-DOM state changes also have to consult the document user
selector insights for :has() and pseudo-class invalidation.

Add content blocker coverage for user-style refreshes, shadow :has()
state changes, and shadow pseudo-class invalidation.
2026-05-23 22:03:46 +02:00
Andreas Kling
44a6da5165 LibWeb: Avoid cold rule cache builds for :has() checks
Cache lightweight selector insights on each stylesheet so cold style
scopes can answer whether :has() invalidation is relevant without
building a complete rule cache. This avoids forcing rule caches for
shadow roots whose active sheets do not contain :has() selectors.

Imported sheets contribute to their parent sheet's effective rules, so
an imported sheet load or CSSOM change also clears ancestor selector
insight caches.

Add test-only counters and regression coverage for cold shadow roots
with and without :has() selectors, plus delayed imported :has() rules.
2026-05-23 22:03:46 +02:00
Andreas Kling
2118396905 LibWeb: Skip :has() invalidation for unstyled shadow roots
Do not process pending :has() mutation invalidation for shadow roots
that have no active style sheets. Such scopes cannot contain shadow
rules with :has(), and document-level user rules are handled by the
document style scope before shadow roots are visited.

Add a text test that mutates an unstyled shadow root while document
:has() invalidation is active, covering the safety boundary for this
shortcut.
2026-05-23 22:03:46 +02:00
Andreas Kling
56077a7c88 LibWeb: Share constructed shadow style caches with ad blocking
Content blocker cosmetic rules now live in the document style scope, so
single constructed stylesheet shadow roots can still reuse their shared
style cache when cosmetic rules are enabled. This keeps the existing
sharing fast path active on pages with many constructed shadow roots.
2026-05-23 22:03:46 +02:00
Andreas Kling
5cc6cb106d LibWeb: Cache content blocker styles per document
Build the user stylesheet only for document style scopes, since user
rules are already considered relevant across shadow boundaries during
rule matching. This avoids regenerating and reparsing the same cosmetic
content blocker stylesheet for every shadow root in the document.

Keep the generated cosmetic stylesheet cached on the Document and clear
it whenever user style is invalidated, so content blocker changes still
produce fresh CSS for the next style update.
2026-05-23 22:03:46 +02:00
Tim Ledbetter
988dce3e7a LibWeb: Drop :local-link rule-cache gate from base URL change handler
The base URL change handler checked whether any style scope contained
a `:local-link` rule before walking the document's links, this forced
a rule cache rebuild, which is generally slower than the link walk we
were trying to avoid.

On Speedometer2 the duplicated rule-cache and invalidation-set
construction accounted for roughly 4% of total samples. Removing the
gate lets the URL-unchanged early-exit handle the no-op case and runs
the link walk only when the URL actually changes, which the profile
showed to be inexpensive on its own.
2026-05-23 19:43:42 +01:00
Shannon Booth
637fd51595 LibWeb: Unify WebIDL C++ type generation
Represent WebIDL C++ types with a single CppType model that tracks
nullability, optional presence, and contained storage.

GC-like values now use GC::Ref/GC::Ptr directly, while containers choose
"plain", "Root", or "Conservative" container types depending on what
they contain. For example, sequence<Element> becomes a RootVector of
GC::Ref values, while sequence<SomeDictionary> becomes a
ConservativeVector only when the dictionary contains GC-like values.
This moves the generated bindings away from wrapping GC values in
GC::Root by default.

This has broad fallout as the types passed to interfaces for GC
objects changes almost fully across the board.
2026-05-23 18:26:12 +02:00
Tim Ledbetter
c64c60b020 LibWeb: Absolutize @font-face font-style descriptor before reading
This change matches the style used in the previous commits. No
regression tests have been added because this change is not observable,
as `FontStyleStyleValue` currently ignores the oblique angle.
2026-05-23 15:44:57 +02:00
Tim Ledbetter
8cd82627ff LibWeb: Absolutize @font-face font-weight descriptor before reading 2026-05-23 15:44:57 +02:00
Tim Ledbetter
5079324f95 LibWeb: Absolutize @font-face font-width descriptor before reading
Previously, a font width/stretch descriptor with a calc() expression
containing font-relative units cause a crash when read because
`set_stretch_impl()` expects values to be absolutized. This change
moves absolutization into `set_stretch_impl()`, sourcing the length
resolution context from the connected stylesheet's owning document.
2026-05-23 15:44:57 +02:00
Andreas Kling
17902b02ab LibWeb: Stop idle animated CSS image timers
Keep animated ImageStyleValue frame advancement owned by the
style value. The current frame and loop state live there, so a
separate document scheduler would duplicate ownership of that state.

Start the ImageStyleValue timer only while it has layout clients.
Stop it when the last client unregisters, or when a finite animation
completes. Expose a document-scoped active timer count through
internals for focused regression tests.

Clear image observers when layout nodes detach. Use current-node
cleanup for per-DOM-node clearing, and explicit subtree cleanup for
tree replacement, full tree teardown, and synthetic pseudo-elements.
This keeps large document clearing linear.

Unregister generated-content image providers during layout detach
instead of waiting for GC to finalize the provider.

Cover hidden animated background images, generated content images,
layout node replacement, full layout tree teardown, and document
scoping for the internals counter.
2026-05-23 11:36:53 +02:00
Andreas Kling
9abdc9fb62 LibWeb: Handle inserts into detached grouping rules
Use an empty namespace table when parsing a nested rule through a
grouping rule that no longer has a parent stylesheet, and skip owner
invalidation in that detached case.

Add a crash test covering selectorText mutation and nested insertRule
after a top-level style rule has been deleted from its constructable
stylesheet.
2026-05-23 11:36:45 +02:00
Andreas Kling
2a46037628 LibWeb: Avoid transform interpolation without a reference box
Fall back to discrete transform interpolation when a remaining
transform list needs a reference box to become a matrix and none is
available. Percentage translate values otherwise reached
Length::from_style_value() without a percentage basis.

Add a crash test covering computed Typed OM access to an interpolated
transform that mixes percentage translation with rotation.
2026-05-23 11:36:45 +02:00
Andreas Kling
bcdd4cfaa3 LibWeb: Normalize overflow-clip-margin Typed OM values
Allow overflow-clip-margin's object-valued Typed OM path to use the
property parser before the value reaches CSSStyleProperties.
CSSKeywordValue("border-box") otherwise arrived as a bare keyword for
a shorthand, tripping the shorthand expansion guard.

Keep the normalization scoped to the overflow-clip-margin shorthand
family so existing Typed OM shorthand behavior remains unchanged. Add
a crash test for the keyword and length forms from the WPT coverage.
2026-05-23 11:36:45 +02:00
Andreas Kling
16dcd2ab23 LibWeb: Absolutize connected font-width descriptors
Resolve connected @font-face font-width descriptors with the rule's
document context before updating the cached width used for font
selection. This matches the font-style and font-weight descriptor
handling and lets relative units inside calculations simplify safely.

Add crash coverage for an @font-face font-width descriptor using
sign() with rem units.
2026-05-23 11:36:45 +02:00
Pavel Shliak
fed6186201 LibWeb: Remove unused ReportTime include from StyleScope 2026-05-23 09:16:09 +02:00
Sam Atkins
f6b1625e12 LibWeb/CSS: Mark @scope as a supported at-rule
Also add a test that covers the various at-rules we support which are
missed by the WPT test.
2026-05-22 13:12:27 +01:00
mikiubo
fab4292711 LibWeb: Reject safe/unsafe as standalone self-alignment values
safe and unsafe are <overflow-position> modifiers per css-align-3
and must accompany an alignment value. Reject them as standalone
keywords for align-items, align-self, justify-items and
justify-self.

The check only applies when these properties are parsed as
longhands; their use inside the place-items and place-self
shorthands goes through a separate code path and is unaffected.
2026-05-22 10:36:04 +01:00
Sam Atkins
5c928eb7eb LibWeb/CSS: Implement the @scope rule
`@scope (a) to (b) {}` applies its contained style rules to elements
that have `a` as a parent, and do not have `a b` as a parent. Both the
`a` and `b` selector lists are optional.

Because it's situational whether a `@scope` will apply to a given
element, we store the ancestor scope on the `MatchingRule`, similar to
`@container`, and then determine during matching whether all the parent
`@scope`s match or not.

The rules for how selectors inside `@scope` are adjusted and interpreted
are a bit confusing. Unlike for other at-rules, nested style rules
inside `@scope` do not get a leading `&` added during parsing. To
support this, `adapt_nested_relative_selector_list()` now takes a flag
for whether its parent is a `@scope` or not.

`@scope` can also contain nested declarations without itself being
nested inside a style rule.

When determining their selectors, nested declarations rules adopt the
`@scope`'s scoping root if it has one, or otherwise fall back to the
parent element of the `<style>` element (not implemented here,) or the
`:root`. These are required to have zero specificity, so we wrap the
selector in `:where()`.
2026-05-22 10:00:42 +01:00
Sam Atkins
14a06add85 LibWeb/CSS: Invalidate style sheet owners on rule insertion/deletion 2026-05-22 10:00:42 +01:00
Sam Atkins
c67172f368 LibWeb/CSS: Ask CSSNestedDeclarations for selectors and layer directly
This is preparation for nested declarations inside `@scope`. User code
no longer makes assumptions about there being a style rule parent, as
there may not be one.

We cache the absolutized selectors because `@scope` will require us to
modify the parent's selectors instead of using them directly.
2026-05-22 10:00:42 +01:00
Sam Atkins
43f4ecde5c LibWeb/CSS: Simplify absolutize_selectors_relative_to nesting check 2026-05-22 10:00:42 +01:00
Sam Atkins
f88c093f93 LibWeb/CSS: Maintain rule-parsing context when converting to CSSRules
Specifically we will need to know the parent rules for a nested style
rule inside `@scope` in order to resolve its selectors correctly.
Reusing our existing m_rule_context stack is the simplest option - once
we reach the point of converting rules, this context stack is empty, so
we can populate it as we go.
2026-05-22 10:00:42 +01:00
Sam Atkins
1b97f83075 LibWeb/CSS: Replace & selectors with :where(:scope) when appropriate
We previously just ignored `&` when absolutizing it would become
`:scope`, because of specificity, but we can actually directly replace
it with `:where(:scope)` in that situation instead.

This commit also renames parent_style_rule() to nesting_parent_rule()
and it doesn't just return CSSStyleRules - in a later commit, we'll
also detect CSSScopeRules here.
2026-05-22 10:00:42 +01:00
Sam Atkins
c408a7165a LibWeb: Adapt CSSStyleRule::parent_style_rule() -> nesting_parent_rule()
Once we implement `@scope`, the rule used as the parent for nesting
selectors won't always be a style rule, but could be a `@scope` rule.
This will get adjusted once those are added.
2026-05-22 10:00:42 +01:00
Sam Atkins
e83478f21d LibWeb/CSS: Move Selector absolutization into Selector.[h,cpp]
Just a move with very minor adjustments. We're going to need to run the
absolutization process on other selectors besides those in a
CSSStyleRule - specifically the scope target of CSSScopeRule. So put the
code where it's accessible.
2026-05-22 10:00:42 +01:00
Sam Atkins
03f9ab7602 LibWeb/CSS: Include absolutized selectors in CSSStyleRule dump
Useful when debugging absolutization.
2026-05-22 10:00:42 +01:00
Sam Atkins
b61227d540 LibWeb/CSS: Implement @supports at-rule(@foo)
Matches if we support the at-rule in some form.

Keeping this list up to date is a bit awkward, but we don't add at-rules
too often, and having all at-rules defined in JSON would just move the
awkward-to-maintain list somewhere else.
2026-05-22 09:59:52 +01:00
Andreas Kling
e548c97b6d LibWeb: Limit non-inherited inherit tracking to direct children
Track explicit inherit of non-inherited properties only on the direct
parent shadow root. A deeper descendant with margin-left: inherit still
inherits from its own parent, so a host margin change does not require
marking every ancestor as possibly affected.

Extend the shadow-root inherited style test to cover both the direct
child case that must still update and the deeper descendant case that
must not trigger broad inherited-style recomputation.
2026-05-22 09:38:59 +02:00
Andreas Kling
ddce686ed0 LibWeb: Propagate inherited style across slot boundaries
Make style updates reach a fixed point when slot invalidation dirties
assigned nodes after their traversal point. During inherited-style
cascades, only the topmost changed element scans for descendant slots.

Animation inherited-style updates now include the target slot and walk
shadow-including descendants, so host animations propagate inherited
values through shadow trees and assigned slottables. Animated inherited
longhands also carry the same inherited-style invalidation signal as
regular style changes.

Mark custom elements dirty when their :defined state flips, so upgraded
elements do not keep stale :not(:defined) computed style. Add coverage
for slotted menu invalidation, descendant-slot scan counts, target slot
animations, host shadow-tree animations, and explicit inherit from an
animated non-inherited longhand.
2026-05-22 09:38:59 +02:00
Andreas Kling
d0b47e32c9 LibWeb: Propagate inherited style into shadow roots
Track when style recomputation may require inherited-style work in a
shadow tree, and use that signal when crossing from a shadow host into
its shadow root. Shadow descendants can explicitly inherit normally
non-inherited host properties, so any host style change may need
inherited-style recomputation there.

Use the CSS property definition to tell whether changed longhands need
to propagate to shadow descendants. Do the same after recomputing
inheritance-dependent values, such as host font-size: 2em after a parent
font-size change.

The shadow DOM tests cover inline and class-driven inherited host style
changes, explicit inherit for non-inherited host properties, relative
units on hosts, and nested slotted inheritance updates.
2026-05-22 09:38:59 +02:00
Andreas Kling
c974e616c0 LibWeb: Add cosmetic rules to ContentBlocker
Split cosmetic blocker rules out from network patterns. Expose matching
rules as user CSS through StyleScope.

Invalidate affected user style caches when blocker state changes.
Generated cosmetic CSS now respects disabled content blocking.
2026-05-21 21:16:56 +02:00
Callum Law
883199cb39 LibWeb: Make TransformationSV::to_matrix infallible
Parameter values are absolutized at style computation time (or are
confirmed to be resolvable in the case of reification or DOMMatrix) so
there is no reason this function should fail.
2026-05-21 17:07:58 +01:00
Callum Law
e07ba236d1 LibWeb: Don't generate LRC in TransformationSV::to_matrix
All relative lengths will have been absolutized at style computation
time (or disallowed at parse time in the case of DOMMatrix) so we don't
need to resolve them again here.
2026-05-21 17:07:58 +01:00
Callum Law
e9a89605fa LibWeb: Check transform function can be resolved before reification
This gets us another step closer to making `to_matrix` infallible
2026-05-21 17:07:58 +01:00
Callum Law
94788555ac LibWeb: Disallow relative lengths in DOMMatrix init string at parse
This will allow us to make matrix resolution non-fallible in a later
commit
2026-05-21 17:07:58 +01:00
Callum Law
03463c2651 LibWeb: Use the correct identity value for skew() interpolation 2026-05-21 17:07:58 +01:00
Callum Law
9db3c77c6e LibWeb: Apply "element-reference" pseudo inline styles correctly
Previously we applied them ad-hoc when computing the style for the
referenced element but we now apply it as part of the cascade. This
fixes a couple bugs:
 - Computing the style for the pseudo-element (rather than the
   referenced element itself) as we do in the case we don't have a
   layout node in `get_direct_property` now includes the inline style.
 - Inline style is applied according to the cascade (i.e. it can be
   overriden by non-inline `!important` styles).
 - Properties go through the computation process.
2026-05-21 14:26:22 +01:00
Callum Law
977c054e77 LibWeb: Rename use_pseudo_element
`associated_shadow_host_pseudo_element` is clearer on what this is.
2026-05-21 14:26:22 +01:00
Callum Law
79f6cc1d7a LibWeb: Make DOM::PseudoElement abstract
And add a new `SyntheticPseudoElement` sub-class.

This will allow us to implement `ElementReferencePseudoElement` in the
next commit.
2026-05-21 14:26:22 +01:00
Callum Law
28afdb327a LibWeb: Limit some pseudo-element functionality to synthetic only
Most of this functionality was already implicitly disallowed for
element-reference pseudo-elements by the fact that we weren't creating
entries in `m_pseudo_element_data` for them, but we need to explicitly
limit it in preparation of creating those entries.

Due to the above this is mostly non-functional apart from a regression
where we no longer support custom properties on element-reference
pseudo-elements. Previously when setting custom properties for an
element-reference pseudo-element we would call `ensure_pseudo_element()`
which created a synthetic pseudo-element entry distinct from the
referenced element which only stored custom property data - this was
clearly wrong and will be implemented properly in a future commit.
2026-05-21 14:26:22 +01:00
Callum Law
0c5d120d75 LibWeb: Mark pseudo-elements as either synthetic or element-reference
See new paragraph in `Documentation/CSSGeneratedFiles.md` for what this
signifies.
2026-05-21 14:26:22 +01:00
Aliaksandr Kalenik
6b912038d3 LibWeb+WebContent: Route compositor through in-process IPC
LibWeb still exposed the concrete CompositorThread to Page,
Navigable, and EventHandler, so compositor IPC would have leaked the
thread implementation into callers. The old thread APIs also bundled
page presentation callbacks and main-thread wakeups into the same
object, which made it awkward for WebContent to put an actor boundary
in between.

Introduce CompositorHost and context handles as the caller-facing API,
and move shared compositor protocol values out of CompositorThread. Add
WebContentCompositor IPC endpoints and route PageHost through a paired
in-process transport. The actor owns CompositorThread with explicit
main-thread and UI presentation clients, while screenshot completion is
serialized on the WebContent event loop using request IDs.

The intention for introducing IPC here is to prepare for moving the
compositor thread into a separate process.
2026-05-21 11:45:06 +01:00
Brandon Rothweiler
20285b23b3 LibWeb: Fix -webkit-linear-gradient angle direction 2026-05-21 09:13:04 +02:00
Andreas Kling
107e44e9c6 LibWeb: Invalidate focus state for shadow parts
Include ::part() rules when comparing pseudo-class rule matches during
focus state invalidation. For observer style scopes, also walk the
containing shadow roots of the old and new focused nodes so document and
ancestor shadow scopes can invalidate exported part targets.

Add coverage for direct and exported parts using :focus, :focus-visible,
and :focus-within.
2026-05-21 08:56:05 +02:00
Andreas Kling
c92ea86c30 LibWeb: Invalidate shadow focus-within styles
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.
2026-05-21 08:56:05 +02:00
Shannon Booth
a24af35ca1 LibGC: Default-construct RootHashMap from the global heap 2026-05-20 20:37:55 +02:00
Shannon Booth
78a4438cd8 LibGC: Default-construct RootHashTable from the global heap 2026-05-20 20:37:55 +02:00