Commit Graph

2236 Commits

Author SHA1 Message Date
Andreas Kling
0c58a6f322 LibWeb: Use slot's shadow host when matching ::slotted() selectors
When matching selectors like `:host ::slotted(div)`, the selector
engine needs to traverse up from the slot element to reach the shadow
host for `:host` matching. Previously, we passed shadow_host_to_use
which was derived from the *slotted element's* DOM position. For
elements in the light DOM (not inside any shadow root), this was null,
causing traverse_up() to use parent() instead of
parent_or_shadow_host_element(). This meant the traversal could never
cross the shadow boundary from inside the shadow tree to reach the
host element.

Fix this by deriving the shadow host from the slot's containing shadow
root, which is the correct scope for combinator traversal within
::slotted() rule matching.
2026-03-21 21:42:44 -05:00
Andreas Kling
717f18909b LibWeb: Handle trailing whitespace in display value parsing
When a var() fallback value contained trailing whitespace (e.g.
`var(--foo, flex )`), parse_display_value() miscounted the tokens.
The remaining_token_count() check included whitespace tokens, routing
single-keyword values like "flex" to the multi-component path. The
multi-component parser then failed when encountering the trailing
whitespace token.

Fix this by counting only non-whitespace tokens for the single vs
multi-component routing decision, and by skipping whitespace in the
multi-component parsing loop.
2026-03-21 21:42:44 -05:00
Andreas Kling
bc3bd28378 LibWeb: Use Newton-Raphson for cubic-bezier easing evaluation
Replace the previous caching/binary-search approach with
Newton-Raphson iteration and bisection fallback. This is the
same algorithm used by WebKit, Chromium, and Firefox.

The old code had a broken binary search comparator that could never
return 0 (the second condition was always true when the first was
false), leading to out-of-bounds vector accesses and crashes.

Fixes #3628.
2026-03-21 18:21:31 -05:00
Andreas Kling
eb789e790e Everywhere: Use AK::SaturatingMath and remove Checked saturating APIs
Port all callers of Checked<T>::saturating_add/sub/mul to the new
standalone functions in AK/SaturatingMath.h, and remove the old
APIs from Checked.
2026-03-21 18:20:09 -05:00
Andreas Kling
be56df8d4f LibWeb: Handle guaranteed-invalid values in animation keyframes
When a CSS animation keyframe uses var() referencing a nonexistent or
invalid custom property, variable substitution produces a
guaranteed-invalid value. The animation keyframe processing code did not
handle this case, allowing the value to reach compute_opacity() (and
similar functions) which would hit VERIFY_NOT_REACHED().

Fix this by skipping guaranteed-invalid values in
compute_keyframe_values, matching how the regular cascading code treats
them.

This fixes a crash on chess.com.
2026-03-21 08:41:13 -05:00
Jelle Raaijmakers
7fed3f9801 LibWeb: Start fetching CSS image resources before document load event
Both Chromium and Gecko delay the document's load event for CSS image
resource requests (background-image, mask-image, etc). We now start
fetching CSS image resources as soon as their stylesheet is associated
with a document, rather than deferring until layout. This is done by
collecting ImageStyleValues during stylesheet parsing and initiating
their fetches when the stylesheet is added to the document.

Fixes #3448
2026-03-21 10:29:54 +01:00
Rob Ryan
d9f909c911 LibWeb: Add label cursor default CSS to match Chromium and Gecko 2026-03-21 07:40:25 +00:00
Rob Ryan
1b508c4e42 LibWeb: Remove label default inline-block style
Let labels fall back to their default inline display so empty
labels do not create extra line height above following
block content.

Add a regression for the empty-label case and update the
file input layout expectation for the UA shadow label.
2026-03-21 07:40:25 +00:00
mikiubo
71c4bb1aa8 LibWeb: Support border-radius in clip-path: inset()
Implement support for the 'round' radii in 'clip-path: inset()'
by resolving and normalizing corner radii and generating a path
with elliptical arcs.

Add a screenshot test.
2026-03-21 02:44:49 +00:00
mikiubo
b5e90e0350 LibWeb: Remove outdated FIXME about path() in basic-shape parser
Remove a FIXME stating that path() was not implemented in
basic-shape parsing, as it is now supported.
2026-03-21 02:44:49 +00:00
Callum Law
5b07dcbd5d LibWeb: Respect @font-feature-values rules 2026-03-20 16:08:32 -05:00
Callum Law
4086e053ae LibWeb: Use FlyString for key in to_shape_features
This fixes an issue where we couldn't use `String::formatted` within
lambdas
2026-03-20 16:08:32 -05:00
Callum Law
31b5307bec LibWeb: Respect font-variant-alternates: historical-forms 2026-03-20 16:08:32 -05:00
Callum Law
8fb55a31a9 LibWeb: Pass @font-feature-values data when resolving shape features 2026-03-20 16:08:32 -05:00
Callum Law
02c572c21e LibWeb: Clear font cache when @font-feature-values rule modified
We now clear the computed font cache whenever a `@font-feature-values`
rule is added, removed, or has one of it's descriptors modified.

This isn't observable yet since we don't actually respect
`@font-feature-values` rules, but that will come in a later commit
2026-03-20 16:08:32 -05:00
Callum Law
dae2f4df0d LibWeb: Clear rule caches before setting parent rule/stylesheet
In a later commit the caches to clear will be stored against a document
so the rule will require still having reference to that document to
clear it.

Also take this opportunity to mark `set_parent_style_sheet` as
`MUST_UPCALL`
2026-03-20 16:08:32 -05:00
Callum Law
cc5d1a9b4d LibWeb: Rename did_load_font to clear_computed_font_cache
This will be used in cases other than font loading in the future (i.e.
changes to `@font-feature-values` rules)
2026-03-20 16:08:32 -05:00
Callum Law
2d0144f265 LibWeb: Include font-variant-alternates functions in FontFeatureData 2026-03-20 16:08:32 -05:00
Johan Dahlin
dfe5d009d0 LibWeb: Fix CSS style computation crashes on detached documents
Replace VERIFY assertions with fallbacks in Length::for_element()
when computed_properties or root element is null. Guard
inheritance_parent->computed_properties() in StyleComputer.
2026-03-20 15:56:50 -05:00
Jelle Raaijmakers
5bffb5e003 LibWeb: Prevent CascadedProperties churn for pseudo elements
We can bail earlier in `StyleComputer::compute_style_impl()` when we
know no pseudo-element rules matched, preventing quite a lot of
CascadedProperties churn. This was especially visible in WPT's
`encoding` tests, for which this is a hot path on account of all the
`<span>`s they create.
2026-03-20 19:33:06 +01:00
Callum Law
6f226f3d2e LibWeb: Add missing AD-HOC comment to parse_family_name_value 2026-03-20 14:06:39 +00:00
Tim Ledbetter
c173a66754 LibWeb: Avoid style computation if document is detached 2026-03-19 14:17:46 +01:00
Callum Law
915fc4602b LibWeb: Implement CSS inherit() function
The remaining failing imported tests are due to wider issues which are
covered by FIXMEs (both existing and added in this commit)
2026-03-19 10:25:37 +01:00
Callum Law
2300ba41fb LibWeb: Use generic <color-interpolation-method> parsing for color-mix
This also fixes an issue where we would allow arbitrary idents as color
spaces
2026-03-18 13:21:57 +00:00
Callum Law
9db607b1a7 LibWeb: Use generic <color-interpolation-method> parsing for gradients
See previous commit for details

We now support parsing of `display-p3-linear` (although it just falls
back to using Oklab since Skia doesn't support it)
2026-03-18 13:21:57 +00:00
Callum Law
d8c38a294c LibWeb: Add a generic <color-interpolation-method> parsing method
Previously we had two implementations for parsing
`<color-interpolation-method>`, one for gradients and one for
`color-mix()` - this commit adds another which will unify the existing
ones in following commits.

This implementation has a couple of advantages over the existing ones:
 - It is simpler in that it uses global CSS enums and their helper
   functions
 - It is spec compliant (unlike the `color-mix()` one which allows
   arbitrary idents)
 - It parses as a `StyleValue` which will be required once we support
   `<custom-color-space>` since that can be an `ident()` which isn't
   resolvable at parse time
2026-03-18 13:21:57 +00:00
Zaggy1024
44ed698d4f LibWeb: Separate the active element and the element being activated
We were conflating elements being the active element and elements being
activated. The :active pseudo class is supposed to be based on whether
an element will have its activation behavior run upon a button being
released.

Store whether an element is being activated as a flag that is set/reset
by EventHandler.

Doing this allows label elements to visually activate their control
without doing a weird paintable hack, so the Labelable classes have
been yeeted.
2026-03-17 04:01:29 -05:00
Psychpsyo
80a0a39b79 LibWeb: Add scroll-behavior CSS property
To quote the spec:
"User agents may ignore this property."
So that is what we do.
2026-03-16 08:44:50 +00:00
Shannon Booth
e555edd770 LibWeb/Bindings: Implement callback interface object bindings
Generate correct bindings for callback interfaces: only create an
interface object when the interface declares constants, and set up
the prototype correctly.

This also lets us tidy up some IDL for these callback interfaces.
2026-03-11 21:16:44 +01:00
Tim Ledbetter
8179efb38e LibWeb: Respect box-sizing value when getting width/height used value 2026-03-11 11:33:25 +01:00
Aliaksandr Kalenik
9d2ebe90ed LibWeb: Store visual context nodes in arena-based tree
Replace per-node heap-allocated AtomicRefCounted
AccumulatedVisualContext objects with a single contiguous Vector inside
AccumulatedVisualContextTree. All nodes for a frame are now stored in
one allocation, using type-safe VisualContextIndex instead of RefPtr
pointers.

This reduces allocation churn, improves cache locality, and opens the
door for future snapshotting of visual context state — similar to how
scroll offsets are snapshotted today.
2026-03-11 11:16:36 +01:00
Aliaksandr Kalenik
d8b8a7d3f3 LibWeb: Mark only font-using elements for style update on font load
Instead of calling invalidate_style(CSSFontLoaded) which marks the
entire subtree for style recomputation, use set_needs_style_update(true)
to mark only individual elements that reference the loaded font family.

This is correct because element_uses_font_family() checks the computed
(inherited) font-family value, so descendants inheriting the font will
match individually, while descendants that override font-family to a
different font are skipped entirely.
2026-03-11 02:10:15 +01:00
Aliaksandr Kalenik
345cc022a2 LibWeb: Deduplicate :has() ancestor invalidation
When multiple descendant nodes change in one style invalidation cycle,
invalidate_style_of_elements_affected_by_has() walks from each pending
node up to all ancestors. Since ancestor paths converge going up, the
same ancestor elements get processed repeatedly, causing redundant
invalidate_style_if_affected_by_has() calls.
2026-03-10 21:37:17 +01:00
Aliaksandr Kalenik
f2cd23659c LibWeb: Fix :has() invalidation with nested :is() selectors
Replace the broad whole-subtree fallback for :has() invalidation with
a more targeted approach. The old code unconditionally overwrote
fine-grained :has() invalidation plans with invalidate_whole_subtree
for every non-rightmost compound containing :has(). This prevented
optimization for direct cases like `.a:has(.b) .c`.

The new approach propagates pseudo_class:Has through :is()/:where()
argument processing when :has() appears in non-rightmost compounds of
the inner selector. For complex :is() arguments (multiple compounds),
it falls back to whole-subtree invalidation since the outer plan can't
correctly capture the nested combinator structure.
2026-03-10 17:56:42 +01:00
Adam Colvin
1916b6cfc1 LibWeb: Match nth-child pseudo-classes on elements without a parent
The :nth-child(), :nth-last-child(), :nth-of-type(), and
:nth-last-of-type() pseudo-classes previously returned false for
elements that have no parent node. This meant that calling
element.matches(":nth-child(1)") on a standalone element created
via document.createElement() would incorrectly return false.

An element without a parent has no siblings, so its index is 1. The
simpler child-indexed pseudo-classes (:first-child, :last-child, etc.)
already handled this case correctly by checking for sibling presence
without requiring a parent. This change brings the nth-* variants in
line with that behavior by guarding the sibling iteration loops on
parent existence rather than returning false early.
2026-03-10 09:58:54 +00:00
Aliaksandr Kalenik
9df1372452 LibWeb: Implement sibling invalidation sets
Replace flat InvalidationSet with recursive InvalidationPlan trees
that preserve selector combinator structure. Previously, selectors
with sibling combinators (+ and ~) fell back to whole-subtree
invalidation. Now the StyleInvalidator walks the DOM following
combinator-specific rules, so ".a + .b" only invalidates the
adjacent sibling matching ".b" rather than the entire subtree.

Plans are compiled at stylesheet parse time by walking selector
compounds right-to-left. For ".a .b + .c":
```
  [.c]: plan = { invalidate_self }
        register: "c" → plan

  [.b]: wrap("+", righthand)
        plan = { sibling_rules: [match ".c", adjacent, {self}] }
        register: "b" → plan

  [.a]: wrap(" ", righthand)
        plan = { descendant_rules: [match ".b", <sibling plan>] }
        register: "a" → plan
```

Changing class "a" produces a plan that walks descendants for ".b",
checks ".b"'s adjacent sibling for ".c", and invalidates only that
element.
2026-03-09 18:35:46 +01:00
Aliaksandr Kalenik
201e985bea LibWeb: Track :required/:optional in :has() invalidation data
These pseudo-classes were missing from collect_properties_used_in_has,
which meant changes to the `required` attribute did not trigger
:has()-based ancestor invalidation.
2026-03-09 18:35:46 +01:00
Callum Law
c47f226225 LibWeb: Support CSS if() function
We don't yet support style queries
2026-03-09 14:36:18 +00:00
Callum Law
b9ebd50cf3 LibWeb: Add ConstantBooleanExpression
This is useful for representing things such as the `else` keyword in
`if()` functions
2026-03-09 14:36:18 +00:00
Callum Law
7f3b8bb587 LibWeb: Make parsing of <supports-condition> reusable
This will also be used for parsing `if()` (for which we want to return a
`BooleanExpression`) as well as `@supports`
2026-03-09 14:36:18 +00:00
Callum Law
ce43e81933 LibWeb: Lift parentheses parsing out of <media-feature>
Having this as part of `<media-feature>` seemed to be a spec bug, see
https://github.com/w3c/csswg-drafts/pull/13575
2026-03-09 14:36:18 +00:00
Callum Law
87b0b9ad31 LibWeb: Support other ASF argument types
While all parsed argument grammars can be represented as
`Vector<Vector<ComponentValue>>`, we can save some redundant work by
storing them in their argument-grammar-parsed format.

Note that for all currently implemented ASFs this is actually
`Vector<Vector<ComponentValue>>` and thus this change will only be
relevant for ASFs we haven't implemented yet
2026-03-09 14:36:18 +00:00
Callum Law
11d5898915 LibWeb: Make ASF grammar parsing method more reusable
These changes make it possible to use this function to parse
`<if-args-branch>` as part of `<if-args>`
2026-03-09 14:36:18 +00:00
Tim Ledbetter
815e9db05d LibWeb: Take namespace into account when matching *-of-type selectors 2026-03-09 11:48:19 +01:00
Callum Law
8a6d902d4c LibWeb: Rename for_each_counter_style_at_rule
This only iterates effective rules which should be reflected in the name
2026-03-07 12:37:10 +01:00
Aliaksandr Kalenik
d17b7fe70d LibWeb: Track structural invalidation dependencies by direction
Split the structural-change selector metadata into directional bits for
first/last-child and forward/backward positional selectors.

This gives sibling invalidation enough information to distinguish which
side of a mutation can affect an element, instead of treating all
structural selectors as bidirectional.
2026-03-07 00:34:00 +01:00
Tim Ledbetter
30d528d3c0 LibWeb: Skip shorthand serialization for var() longhands
When CSSRule.cssText is accessed, shorthands are recomposed from
individual longhand declarations. For coordinating-list shorthands, the
`serialize()` function always assumed that each  sub-property was a
value list. This caused a  crash for longhands containing `var()`. We
now fall back to serializing properties individually if any sub
property contains `var()`. This matches the behavior of other engines.
2026-03-06 13:43:33 +01:00
Tim Ledbetter
710c44a021 LibWeb: Add vendor-specific aliases for the placeholder pseudoelement 2026-03-06 13:13:44 +01:00
Aliaksandr Kalenik
5bfc4a3c41 LibWeb: Cache display list commands per paintable
Cache the display list commands produced by each PaintableBox's paint()
on a per-phase basis. On subsequent display list rebuilds, if a
paintable's cache is still valid, replay the recorded commands directly
— skipping paint() and all the property resolution it entails.

Besides saving time on property resolution, this also enables Skia to
reuse path tessellation results across frames — e.g. border paths are
preserved in the cache and don't need to be re-tessellated on every
repaint.
2026-03-04 19:35:45 +01:00
Aliaksandr Kalenik
eae94a8a46 LibWeb: Route repaint requests through paintables, not Document
Rename Document::set_needs_display() to set_needs_repaint() and make it
private. External callers must now go through Node/Paintable which
route the request to the document internally.

Fix one existing misuse in AnimationEffect that was calling
document-level set_needs_display() instead of routing through the
target element's paintable.

This is preparation for per-paintable display list command caching:
repaint requests must go through specific paintables so their cached
command lists can be invalidated.
2026-03-04 19:35:45 +01:00