Commit Graph

255 Commits

Author SHA1 Message Date
Tim Ledbetter
2c728abd9f Tests: Use wider test string in FontFace arraybuffer matching test
This test relies on the width of the test font and the fallback font
being different to determine whether they matched or not. The
`offsetWidth` rounding change introduced in 51c7afdf5f caused these
widths to appear the same, meaning the test failed. This change avoids
the issue by using a longer string with a deliberately wide glyph.
2026-04-05 09:19:37 +02:00
Tim Ledbetter
5b584fde1d LibWeb: Register JS-created FontFace objects for font matching
Previously, FontFace objects created via the JS and added to
`document.fonts` were stored in the FontFaceSet but never participated
in font matching during style resolution. We now store both
CSS-connected and JS-created font faces in a unified map on
`FontComputer`, keyed by family name, and include them all as
candidates in the font matching algorithm.
2026-04-05 00:13:35 +02: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
Aliaksandr Kalenik
b36f2361f1 Tests: Fix flaky iframe srcdoc test to wait for actual content
The test checked iframe.contentDocument?.readyState !== "complete" to
decide whether to wait for the iframe's load event. However, the
initial about:blank document has readyState "complete", so this check
passes immediately even when the srcdoc navigation hasn't activated
yet. Under heavy load with sanitizers, the srcdoc document activation
is delayed long enough for the test to proceed with the about:blank
document, causing a TypeError when querySelector("#target") returns
null.

Fix by waiting for the actual srcdoc content to appear rather than
relying on readyState. Use a while loop with { once: true } load
event listeners to handle the case where multiple load events fire
(one for about:blank, one for srcdoc).
2026-03-31 09:47:59 +02:00
Callum Law
03d479c1da LibWeb: Validate ASF syntax at parse time 2026-03-30 19:57:36 +01:00
Callum Law
071b000d9f LibWeb: Only allow ASFs in descriptor values if explicitly supported
`@function` descriptors are the only ones that support ASFs, while most
descriptors enforce this through their syntaxes implicitly disallowing
ASFs, this wasn't the case for `@property/initial-value`.

We now explictly disallow ASFs unless they are marked as allowed within
`Descriptors.json`.
2026-03-30 19:57:36 +01:00
Callum Law
8b66e7f463 LibWeb: Consider semicolon in parse_descriptor_value a SyntaxError
Everywhere we use this expects us to parse the whole value, either
because we are parsing the value of a declaration (in which case there
will be no semicolons), or because it is called from a JS setter which
takes whole values and semicolons make the value invalid.

Previously we would just ignore everything after a semicolon.

This also allows us to avoid creating a new `Vector` and copying all the
component values
2026-03-30 19:57:36 +01:00
Callum Law
3e58e15217 LibWeb: Support relative lengths within color-mix percentage calc()s 2026-03-30 14:05:10 +01:00
Callum Law
fe5d6471f0 LibWeb: Store GridTrackPlacement sub-values as StyleValues
Gets us one step closer to removing the `FooOrCalculated` classes
2026-03-30 14:05:10 +01: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
Sam Atkins
7f322da78c Tests: Update our in-house @supports test to be clearer and add env()
The output format for this was confusing. When "FAIL" becomes "PASS" the
natural assumption is that's good. So, make it always output pass for
correct results, and fail for incorrect. Also, replace the
supposed-to-fail pseudo-element name with one that will never be
supported, instead of a webkit one that we did end up adding support
for! 😅

Added a couple of env() cases which will pass with the following commit.
2026-03-29 21:27:20 +01:00
Tim Ledbetter
657060ccc2 LibWeb: Implement FontFaceSet.check()
This returns true if the given text can be rendered with the fonts in
the set that are fully loaded.
2026-03-27 15:28:59 +00:00
Callum Law
b86377b9dc LibWeb: Clamp CSS <integer> value to i32 at parse time
This matches the behavior of other browsers. Previously we implemented
this at used-value time for z-index specifically.
2026-03-26 12:30:01 +01:00
Callum Law
0ab06e119e LibWeb: Use i32 max for clamping 'infinite' calculated integers
This required us to change our range values from `float`s to `double`s
since `float` can't exactly represent i32 max
2026-03-26 12:30:01 +01:00
Callum Law
0e8956ee30 LibWeb: Round up on half when rounding to nearest integer in CSS 2026-03-26 12:30:01 +01:00
Callum Law
93bd066639 LibWeb: Ensure <declaration-value>? descriptors are valid
Currently this only applies to the `@property` `syntax` descriptor.

As with custom properties in the previous commit we assumed that any
consumed values were valid but that's not the case.
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
Jelle Raaijmakers
16dffe39a0 LibWeb: Invalidate pseudo-elements on hover even when not yet created
The hover invalidation code only tried matching ::before/::after
selectors when has_pseudo_element() returned true, which requires an
existing layout node. A pseudo-element that doesn't exist yet (because
its content is only set by a hover rule) has no layout node, so the
match was skipped and hovering never triggered a style recompute.
Always try ::before/::after selectors during hover invalidation.
2026-03-24 10:23:35 -04:00
Callum Law
02db1b942b LibWeb: Improve handling of calculated values in <ratio>
We no longer try to resolve calculated values at parse time which means
we support relative lengths.

We now clamp negative values rather than rejecting them at parse time.

Parsing has been inlined into `parse_ratio_value` and `parse_ratio` has
been removed since `parse_ratio_value` was the only caller
2026-03-24 14:00:01 +00:00
Andreas Kling
593fd69cdb Tests: Make rotation interpolation test use stable boolean checks
Avoid comparing exact matrix3d values which contain floating point
noise that could differ across platforms. Instead, check whether the
transform is or isn't the identity matrix.
2026-03-21 23:16:32 -05:00
Andreas Kling
ed4b5d7b72 Tests: Add test for rotation interpolation with same-axis transforms
When interpolating between rotateY(0deg) and rotateY(3600deg), the
angle should be interpolated numerically. Currently we use quaternion
slerp which sees these as identical rotations (3600 mod 360 = 0),
producing no visual animation. A following commit will fix this.
2026-03-21 23:16:32 -05:00
Andreas Kling
fd01178b6c Tests: Add test for per-keyframe animation-timing-function
The CSS spec says animation-timing-function is applied per keyframe
interval, not as an overall effect-level timing function. Currently we
apply it globally, causing wrong intermediate values and ignoring
per-keyframe animation-timing-function declarations in @keyframes
rules. A following commit will fix this.
2026-03-21 23:16:32 -05:00
Andreas Kling
893fe62588 Tests: Add test for CSS animations defined in shadow DOM
When @keyframes rules are defined in a <style> element inside a shadow
root, animations referencing them should work. Currently the animation
is created but has no keyframes, producing "none" for animated
transform values. A following commit will fix this.
2026-03-21 23:16:32 -05:00
Andreas Kling
fa0a7d4a4a Tests: Add test for non-invertible transform animation fallback
When a transform animation involves a non-invertible matrix (e.g. a
matrix3d with zero Z-scale), the spec requires falling back to discrete
interpolation. Currently we drop the transform entirely, producing
"none" at all progress values. A following commit will fix this.
2026-03-21 23:16:32 -05:00
Andreas Kling
86b3c37222 Tests: Add test for var() fallback values with trailing whitespace
This test shows that var() fallback values with trailing whitespace
(e.g. `var(--foo, flex )`) fail to parse correctly for the display
property, falling back to initial values instead.
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
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
9b4cd4fe62 Tests: Fix flaky test timeout
If the iframe had loaded before the test was executed this would timeout
2026-03-12 12:00: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
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
Callum Law
33c0e55762 LibWeb: Check navigable container for whether element needs style update
The style of an element depends on it's navigable's viewport size which
in turn depends on the navigable's container's style - so if requires a
style update then so does the original element.
2026-03-09 14:36:18 +00: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
e49b643f86 LibWeb: Allow custom properties on pseudo-elements with property lists 2026-03-04 13:36:53 +01:00
Jelle Raaijmakers
e1ba577ab7 LibWeb: Don't match ::part() selectors against unrelated pseudo-elements
The selector matching code bypassed the pseudo-element type check for
`::part()` selectors to support compound selectors like
`::part(foo)::before`. This caused bare ::part() declarations to leak
into unrelated pseudo-elements like ::selection.

Fix this by finding any additional pseudo-element beyond ::part() in the
selector and verifying it matches the target.
2026-03-03 10:03:03 +01:00
Sam Atkins
f657a4b71b LibWeb/CSS: Parse @font-face { font-weight } with two values
WPT has coverage for matching but not parsing, so the test is homemade.
2026-02-24 10:05:09 +00:00
Callum Law
e6669482e6 LibWeb: Parse font-variant-alternates functions 2026-02-20 22:01:44 +00:00
Callum Law
04fd7e00e9 LibWeb: Disallow disjointed numeric component of font-variant
The grammar groups this component together meaning that all
sub-components must occur together i.e.
`ordinal slashed-zero small-caps` is valid but
`ordinal small-caps slashed-zero` is not.

We also reuse the logic for parsing from the longhand
`font-variant-numeric` property for simplicity.
2026-02-20 22:01:44 +00:00
Callum Law
d97098ec80 LibWeb: Disallow disjointed ligatures component of font-variant
The grammar groups this component together meaning that all
sub-components must occur together i.e.
`common-ligatures no-contextual small-caps` is valid but
`common-ligatures small-caps no-contextual` is not.

We also reuse the logic for parsing from the longhand
`font-variant-ligatures` property for simplicity.
2026-02-20 22:01:44 +00:00
Callum Law
75dd7b767f LibWeb: Disallow disjointed east asian component of font-variant
The grammar groups this component together meaning that all
sub-components must occur together i.e. `jis78 full-width small-caps` is
valid but `jis78 small-caps full-width` is not.

We also reuse the logic for parsing from the longhand
`font-variant-east-asian` property for simplicity.
2026-02-20 22:01:44 +00:00
Tim Ledbetter
fd24ca898c LibWeb: Skip hit-testing for elements with non-invertible transforms 2026-02-19 14:33:31 +00:00
Callum Law
caa5705edf LibWeb: Clamp blur() calculated values 2026-02-16 12:09:23 +00:00
Callum Law
68c596942c LibWeb: Store FilterOperation::Color::amount as StyleValue
This simplifies handling and means we now support tree counting
functions.
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
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
Andreas Kling
31cbe2061a LibWeb: Add comprehensive tests for slot style inheritance
Test that slotted elements correctly inherit styles from their
assigned slot in various scenarios: inline styles, CSS rules,
named slots, nested shadow hosts, own style overrides, deep
inheritance chains, dynamic slot reassignment, JS-created shadow
DOM, class toggles on slots, and moving elements between hosts.
2026-02-13 10:22:30 +01:00
Andreas Kling
fb11732526 LibWeb: Fix style inheritance for slotted elements
Two issues prevented slotted elements from correctly inheriting
styles from their assigned slot:

1. Element::element_to_inherit_style_from() was skipping the slot
   element and returning the shadow host instead. This meant slotted
   elements inherited from the host, completely ignoring any styles
   on the slot itself.

2. When a slot element's style changed during the style tree walk,
   its assigned (slotted) nodes were never marked for recomputation.
   The tree walk follows the DOM tree, but slotted elements are DOM
   children of the shadow host, not the slot, so they were missed.

Fix (1) by returning the slot directly as the inheritance parent.
Fix (2) by marking assigned nodes dirty in update_style_recursively
when a slot's style changes.
2026-02-13 10:22:30 +01:00
Andreas Kling
8804eb89e0 LibWeb: Add test for targeted style update in getComputedStyle
Cover various scenarios: element's own style change, ancestor style
change affecting inheritance, sibling changes not affecting our element,
shadow DOM elements, pseudo-elements, and repeated reads.

Also documents a pre-existing bug where slotted elements don't pick up
inherited style changes from their assigned slot.
2026-02-13 10:22:30 +01:00
Callum Law
b0274268f8 LibWeb: Support calculated values in @counter-style range descriptor
Calculated values are resolved and confirmed to be valid (in line with
non-calculated values) at parse time
2026-02-12 01:26:52 +01:00
Callum Law
a541c09d61 LibWeb: Support calculated value in additive-symbols descriptor
Calculated weight values are resolved and verified to be in strictly
decreasing order (as with non-calculated values) at parse time.
2026-02-12 01:26:52 +01:00