Commit graph

354 commits

Author SHA1 Message Date
Callum Law
42b93a85fe LibWeb: Remove fallbacks for computing style on disconnected elements
This were introduced in dfe5d00 but only papered over the underlying
issue that we were computing style for element belonging to detached
documents - this underlying fix was implemented in c173a66 so these
fallbacks/guards are no longer needed
2026-04-08 14:31:43 +01:00
Callum Law
8bd1b383ea LibWeb: Use correct inherited font size when canvas is not connected
The default font size for a canvas context is 10px as opposed to 16px
for the document as a whole.
2026-04-08 14:31:43 +01:00
Callum Law
df73c0e9b5 LibWeb: Don't unnecessarily absolutize style value
We already do this in the caller
2026-04-08 14:31:43 +01:00
Sam Atkins
f11207fee1 LibWeb/CSS: Pass AbstractElement to SelectorEngine::matches
...instead of separate Element and PseudoElement arguments.

As noted, AbstractElement's constness is weird currently, but that's a
tangent I don't want to go on right now.
2026-04-08 10:37:05 +01:00
Tim Ledbetter
af6bc07c4f LibWeb/CSS: Resolve var() in keyframe animation-timing-function
When a `@keyframes` rule contains `animation-timing-function` with a
`var()`, we cannot eagerly resolve it to an `EasingFunction` at rule
cache build time because there is no element context available. We now
store the unresolved `StyleValue` and defer resolution to
`collect_animation_into()`, where the animated element's custom
properties can be used to substitute the variable. Previously, an
`animation-timing-function` with a `var()` in a `@keyframe` would cause
a crash.
2026-04-01 11:38:48 +01:00
Callum Law
90e0e5a0f2 LibWeb: Use correct viewport units for font properties in iframe root el 2026-03-31 10:06:18 +02:00
Dylan Hart
1354eb1ac2 LibWeb: Resolve var() in shorthands before pseudo-element filtering
When a shorthand like `background` containing `var()` is used in
a `::selection` rule, the shorthand was filtered out by the pseudo-
element property whitelist before variable resolution could occur.
This left PendingSubstitutionStyleValue longhands unresolved,
causing either a crash or incorrect computed values.

Allow unresolved shorthands to bypass the pseudo-element filter so
variable resolution can proceed. After resolution and expansion
into longhands, filter out any that the pseudo-element does not
support.

Fixes #8625.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 12:46:20 +01:00
Callum Law
cfc2e64b4b LibWeb: Add is_valid_custom_ident function
We repeat this pattern in a couple of places so let's add a single
helper method
2026-03-26 01:11:39 +00:00
Callum Law
500ca417ce LibWeb: Map logical aliases at compute rather than cascade time
This requires us to front load computation of writing-mode and direction
before we encounter any logical aliases or their physical counterparts
so that we can create a mapping context.

Doing this at compute rather than cascade time achieves a few things:
 1) Brings us into line with the spec
 2) Avoids the double cascade that was previously required to compute
    mapping contexts
 3) We now compute values of logical aliases, while
    `style_value_for_computed_property` maps logical aliases to their
    physical counterparts, this didn't account for all cases (i.e. if
    there was no layout node, Typed OM, etc).
 4) Removes a hurdle to moving other upstream processes (i.e. arbitrary
    substitution function resolution, custom property computation) to
    compute time as the spec requires.
2026-03-25 12:53:22 +00:00
Tim Ledbetter
77b9fcf7f9 LibWeb+LibWebView: Select generic font families based on requested style
Pass the requested font weight and slope to generic_font_name() and use
them to select the most suitable font family. Families that have the
exact requested weight are strongly preferred, followed by families with
more style variety.

This ensures that when CSS requests bold text with a generic font family
like `serif`, we select a family that actually supports bold weights
rather than one that only has regular weight.

Co-authored-by: Jelle Raaijmakers <jelle@ladybird.org>
2026-03-24 15:08:24 +01:00
Charlie Tonneslan
e564eff402 LibWeb/CSS: Fix typo overriden -> overridden in comments 2026-03-24 11:08:23 +01:00
Rob Ryan
281c2ce545 LibWeb: Seed ancestor context for computing style with no layout node 2026-03-24 05:02:48 +01:00
Andreas Kling
42bf301acd LibWeb: Apply animation-timing-function per keyframe interval
Per the CSS Animations spec, the animation-timing-function property
describes how the animation progresses between each pair of keyframes,
not as an overall effect-level timing function.

Previously we set it as the effect-level timing function on the
AnimationEffect, which caused easing to be applied to the global
animation progress. This made animations with multiple keyframes
"pause" at the start and end of the full animation cycle instead of
easing smoothly between each pair of keyframes.

Now we:
- Store per-keyframe easing in ResolvedKeyFrame from @keyframes rules
- Store the default easing on CSSAnimation instead of on the effect
- Apply per-keyframe easing to the interval progress during
  interpolation, falling back to the CSS animation's default easing
- Also store per-keyframe easing from JS-created KeyframeEffects to
  avoid incorrectly applying CSS default easing to replaced effects
2026-03-21 23:16:32 -05:00
Andreas Kling
6d6e15f012 LibWeb: Look up @keyframes rules from shadow root stylesheets
When processing CSS animations, we were only looking up @keyframes
rules from the document-level rule cache, passing nullptr for the
shadow root parameter. This meant that @keyframes defined inside
shadow DOM <style> elements were never found, and animations
referencing them would silently have no keyframes.

Fix this by first checking the element's containing shadow root for
@keyframes rules, then falling back to the document-level rules.
2026-03-21 23:16:32 -05:00
Andreas Kling
63748758ea LibWeb: Compute float to none for absolutely positioned elements
Per CSS2 section 9.7, if position has the value absolute or fixed,
the computed value of float is none. We were already blockifying the
display for such elements, but not resetting the computed value of
float. This made the wrong value observable via getComputedStyle().
2026-03-21 21:42:44 -05:00
Andreas Kling
223ca14abc LibWeb: Support :host::part() selectors within shadow DOM stylesheets
The :host::part() pattern allows a shadow DOM's own stylesheet to
style its internal elements that have been exposed via the part
attribute. Previously, ::part() rules were only collected from
ancestor shadow roots (for external part styling), but never from the
element's own containing shadow root.

Fix this by also collecting ::part() rules from the element's own
shadow root in collect_matching_rules(). Additionally, fix the
selector engine's ::part() compound matching to preserve the
shadow_host when the rule originates from the part element's own
shadow root, allowing :host to match correctly in the same compound
selector.

This fixes 2 previously failing WPT tests:
- css/css-shadow-parts/host-part-002.html
- css/css-shadow-parts/host-part-nesting.html
2026-03-21 21:42:44 -05:00
Andreas Kling
d1acb6e157 LibWeb: Fix :host() descendant matching for nested shadow hosts
When an element inside a shadow DOM is itself a shadow host, and a
rule from the parent shadow DOM uses a selector like
`:host([attr]) .child`, the combinator traversal needs to reach the
outer shadow host to match `:host([attr])`.

Previously, when the styled element was a shadow host and the rule
came from outside its own shadow root, we set shadow_host_to_use to
nullptr. This caused traverse_up() to use parent() which cannot
cross the shadow boundary, preventing the selector from reaching the
outer :host.

Fix this by using the rule's shadow root's host as the traversal
boundary instead of nullptr. This allows the descendant combinator
to traverse from the element up through the enclosing shadow DOM to
reach the outer shadow host for :host() matching.
2026-03-21 21:42:44 -05:00
Andreas Kling
6eeafd3d7a LibWeb: Support ::slotted() matching through nested slot chains
The CSS Scoping spec says ::slotted() represents elements assigned
"after flattening" to a slot. When a slot element is itself slotted
into another slot (nested slots), the flattened tree resolves the
chain so that the inner content appears in the outermost slot.

Previously, we only collected ::slotted() rules from the directly
assigned slot's shadow root. This meant that styles defined in an
outer shadow DOM's ::slotted() rules would not apply to elements
that were transitively slotted through intermediate slots.

Fix this by walking up the slot assignment chain when collecting
and matching ::slotted() rules, so that rules from every shadow
root in the chain are considered.
2026-03-21 21:42:44 -05:00
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
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
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
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
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
Tim Ledbetter
936fa1bd60 LibWeb: Apply document stylesheets to SVG use element shadow trees
The SVG spec says document stylesheets should apply inside `<use>`
element shadow trees if the referenced element is from the same
document.
2026-03-02 10:55:07 +01:00
Callum Law
32b9ff21df LibWeb: Add generic int_from_style_value method
Reduces duplication in line with `number_from_style_value`,
`string_from_style_value` etc
2026-02-23 11:21:09 +00:00
Callum Law
b8f2989ccb LibWeb: Reduce recompilation from editing CascadedProperties.h
This reduces the recompilation of editing `Properties.json` from ~1429
to ~158
2026-02-19 11:27:06 +00:00
Callum Law
3d3d0a50b6 LibWeb: Add generic Length::from_style_value method 2026-02-16 12:09:23 +00:00
Callum Law
33e590dddb LibWeb: Ensure that computation context cache is always cleared
Previously we didn't clear the computation context caches after:
 - Recomputing inherited style
 - Computing keyframe values

We now clear the caches in those two cases and verify it has been
cleared before using it.

Fixes #7959
2026-02-15 17:52:11 +01:00
Andreas Kling
2b6e6e4ea2 LibWeb: Fix crash in style inheritance for pseudo-element slots
Elements in internal shadow trees that represent CSS pseudo-elements
(e.g. the DetailsContent slot in <details>) store their cascaded
properties on the host element's pseudo-element data, not on the
element itself. This meant that when slotted elements walked the
inheritance chain and encountered such a slot, they would dereference
null cascaded properties and crash.

Fix this by copying the cascaded properties onto the slot element
itself after computing its style, keeping both cascaded and computed
properties accessible in the same place.
2026-02-14 14:36:21 -05:00
Callum Law
498e501ea3 LibWeb: Mark StyleComputer cache members as mutable
This avoids some ugly `const_cast`s
2026-02-14 14:35:30 -05:00
Callum Law
daf8d90579 LibWeb: Only set property flags when required in compute_properties
When we initialize `ComputedProperties` all flags are initially set to
false so we only need to update them if they are true
2026-02-13 21:54:06 +01:00
Callum Law
08ef225d0f LibWeb: Only compute inheritance parent once in compute_properties
Previously we were doing this multiple times per property which was
quite expensive
2026-02-13 21:54:06 +01:00
Callum Law
b949fe087a LibWeb: Cache the computed value of corner-*-*-shape initial value 2026-02-13 21:54:06 +01:00
Callum Law
63584321fe LibWeb: Skip property computation in some cases
There are cases where we can skip the property value computation process
because we know that the computed value will be equal to the specified
value
2026-02-13 21:54:06 +01:00
Callum Law
9195f26003 LibWeb: Immediately compute property values in compute_properties
We can avoid some overhead by computing the values immediately instead
of setting them within `ComputedProperties` and then calling
`compute_property_values`
2026-02-13 21:54:06 +01:00
Callum Law
f490227ee0 LibWeb: Set "display before box type transformation" in relevant place
In a future commit we will update `compute_properties` to avoid calling
`compute_property_values` but we still need to store this value
2026-02-13 21:54:06 +01:00
Callum Law
0ced4b5bb8 LibWeb: Don't coordinate background-image with itself
This is always a no-op
2026-02-13 21:54:06 +01:00
Callum Law
32da7edf5e LibWeb: Compute font properties the same as other properties
Previously we computed font properties separately from other properties
for two reasons:
  1) These font properties were computed using a different length
     resolution context than the rest of the properties.
  2) These properties were required to be computed before creating the
     length resolution context for the rest of the properties.

The first issue was solved in the previous commit by introducing a
generic method to get the computation context for a property, and
the second is solved in this commit by computing properties in the
required order.

This simplifies the code a bit and opens up some opportunities for
optimization.
2026-02-13 21:54:06 +01:00
Callum Law
5b635a2135 LibWeb: Don't number steps in compute_properties comments
Having these numbered doesn't add anything and makes diffs ugly when we
add/remove a step
2026-02-13 21:54:06 +01:00
Callum Law
f1e7743989 LibWeb: Add generic method to get computation context for property
The computation context used is the main thing distinguishing the
computation of font/non-font properties so having a generic method to
handle this will allow us to consolidate logic between the two.
2026-02-13 21:54:06 +01:00
Andreas Kling
9e8e568b43 LibWeb: Use structural sharing for CSS custom properties
Replace per-element OrderedHashMap storage for custom properties with
a RefCounted chain (CustomPropertyData) that enables structural
sharing. Each chain node stores only the properties declared directly
on its element, with a parent pointer to the inherited chain.

Elements that don't override any custom properties share the parent's
data directly (just a RefPtr copy). During cascade, only entries that
actually differ from the parent are stored in own_values - the rest
are inherited through the chain. During var() resolution, resolved
values are compared against the parent's and matching entries are
dropped, enabling further sharing.

The chain uses a depth limit (max 32) with flattening, plus
absorption of small parent nodes (threshold 8) to keep lookups fast.

This reduces custom property memory from ~79 MB to ~5.7 MB on
cloudflare.com.
2026-02-13 14:57:15 +01:00
Chase Knowlden
b8f31179b2 LibWeb: Use dimension image source for images
Fixes tiny images on Wikipedia
2026-02-13 10:42:38 +00:00
Callum Law
61b0b20f11 LibWeb: Respect animation-timeline: none 2026-02-11 10:49:34 +01:00
Callum Law
ea2755d4f2 LibWeb: Move animation property application to CSSAnimation
In a future commit this method will be edited to use information stored
within the `CSSAnimation` and it makes more sense to have that data
private
2026-02-11 10:49:34 +01:00
Jelle Raaijmakers
726fe8284c LibWeb: Check if author rule layers are non-empty for pseudo-elements
The author_rules vector always contains at least one layer (the
unlayered entry), so checking is_empty() was always false. Instead,
check whether any layer actually contains rules.
2026-02-06 10:47:50 +00:00
Jelle Raaijmakers
ab82b0147d LibWeb: Compute styles for pseudo-elements with implicit UA styles
Some pseudo-elements have implicit UA styles that need to be computed
even when no CSS rules matched.
2026-02-06 10:47:50 +00:00
Sam Atkins
92897a1dec LibWeb/CSS: Absolutize color KeywordStyleValues
These get computed to an equivalent RGBColorStyleValue.

To support this, we now store the computed color-scheme on the
ComputationContext when computing properties that might contain a color.

This has a nice bonus of correcting the css-accent-color test's result.
2026-02-05 13:48:10 +00:00
Sam Atkins
2994a7532d LibWeb: Make shadow_including_first_ancestor_of_type() use the flat tree
Every user of this actually wants an ancestor in the flat tree - taking
things like `<slot>` into account. So rename it and adjust its behavior
to use that.
2026-02-05 11:21:08 +01:00
Callum Law
2310aaa5b3 LibWeb: Compute font-feature-settings before computing font
In a future commit we will require this to be in it's computed form
before calling `ComputedProperties::computed_font_list()`.

We will also depend on other properties (e.g. `font-kerning`,
`font-variant-*`) but these are unaffected by computation.
2026-02-02 14:11:43 +00:00