Commit Graph

2365 Commits

Author SHA1 Message Date
Aliaksandr Kalenik
0749a65a78 LibWeb: Delay document load event for image-set() candidate fetches
ImageSetStyleValue::set_style_sheet() previously stored the style sheet
on itself but didn't propagate to its candidate images. As a result,
candidates were never registered as pending image resources, so their
fetches didn't start until layout time and didn't delay the document's
load event.

This caused css-image-set-background-type.html to be flaky: under
load, the screenshot could be captured before the selected SVG
candidate had finished decoding, producing an empty box instead of
the expected color.

Propagate set_style_sheet() to each candidate image whose type()
filter does not exclude it, mirroring StyleValueList and
ShorthandStyleValue. The candidates now register themselves as
pending so SharedResourceRequest's load event delayer correctly
delays the load event until decoding completes.
2026-04-26 18:33:15 +02:00
Andreas Kling
09c0caf986 LibWeb: Build invalidation sets from ::slotted() simple selectors
build_invalidation_sets_for_simple_selector ignored ::slotted()
pseudo-element selectors entirely. As a result, the invalidation
plans built for a shadow scope didn't list class, attribute, or
pseudo-class properties referenced inside ::slotted(), so changing
those properties on a slottable couldn't enqueue an invalidation
plan for the rule.

Recurse into ::slotted()'s compound argument and feed each simple
selector through the same invalidation-set builder.
2026-04-26 10:40:58 +02:00
Andreas Kling
fead65d9b2 LibWeb: Recurse into ::slotted() args when collecting :has() metadata
collect_properties_used_in_has only inspected pseudo-class argument
selectors. With ::slotted(.x:has(.descendant)) rules, the property
references inside the :has() argument were therefore never recorded
as :has()-affecting, so attribute and state changes on a slottable's
descendants couldn't enqueue an invalidation plan that covered the
::slotted() rule.

Recurse into the ::slotted() compound argument so the :has() metadata
maps include the properties used inside.
2026-04-26 10:40:58 +02:00
Andreas Kling
85a9239faf LibWeb: Make :has() invalidation reach ::slotted/:host rules
Two related fixes that together let :host(...:has(...)) and
::slotted(.x:has(...)) rules re-evaluate when their light-DOM input
changes:

* StyleScope::collect_selector_insights only recursed into
  pseudo-class argument selectors. The :has() inside a
  ::slotted(...) compound argument was therefore invisible to a
  shadow scope's insights, so may_have_has_selectors() reported
  false and Node::invalidate_style skipped the :has() walk for that
  scope. Walk the ::slotted() argument selector through the same
  insight collection.

* Element::invalidate_style_if_affected_by_has only set
  needs_style_update for elements in subject position. Inside
  ::slotted(.x:has(...)) and :host(...:has(...)) the rule's
  selector subject is the slot or host, but the styled element is
  this element; when the :has() result flips, this element's own
  computed style must be recomputed. Mark it dirty in the
  non-subject branch as well.
2026-04-26 10:40:58 +02:00
Andreas Kling
5904a21a56 LibWeb: Clear pseudo-element style data when no rule matches
When computing pseudo-element style and no pseudo-element rules match,
StyleComputer was returning early without clearing the cascaded and
custom property data on the AbstractElement. As a result,
getComputedStyle() on the pseudo-element kept exposing values from a
previous matching state.

Clear both before bailing so a transition from matched to unmatched
leaves the pseudo-element in a clean state.
2026-04-26 10:40:58 +02:00
Johan Dahlin
b27c6d68e8 LibWeb: Dedup @font-face fetches by source URL 2026-04-25 17:06:28 +02:00
Johan Dahlin
d90c5b295a LibWeb: Honor text argument of FontFaceSet.load() / .check() 2026-04-25 17:06:28 +02:00
Johan Dahlin
acabf765c1 LibWeb+LibGfx: Defer @font-face fetches until a codepoint renders 2026-04-25 17:06:28 +02:00
Andreas Kling
61d79a1e47 LibWeb: Parse CSS image-set()
Add an abstract image style value for image-set() and parse both the
standard and -webkit-prefixed spellings through the existing <image>
value path. The parser accepts URL and string image candidates,
optional resolution descriptors, and type() filters.

Track attr-taint through substituted component values so image-set()
candidates using attr()-derived URL-producing tokens are rejected when
resolved for URL-using properties.

Update the relevant WPT baselines now that image-set() parsing is
supported in additional value contexts.
2026-04-25 14:54:10 +02:00
Tim Ledbetter
eef11001ec LibWeb: Avoid ref-count churn when diffing computed styles 2026-04-25 08:47:47 +02:00
Tim Ledbetter
5d69c6d2b7 LibWeb: Filter by font width before weight in font matching
Implement the width filtering step of the font matching algorithm.
Without it, system font providers that group all widths under one
family could return a condensed variant for font-width: normal,
producing visibly narrower text.
2026-04-24 20:19:38 +02:00
Tim Ledbetter
2ff967fd6f LibWeb: Track declared font width on @font-face rules 2026-04-24 20:19:38 +02:00
Tim Ledbetter
5cefb14707 LibWeb: Switch font to loading in FontFace::load()
FontFace.load() set the face's status to "loading" but never switched
the containing FontFaceSets to "loading" or appended to their
`[[LoadingFonts]]` lists. The load-completion handler then found
`[[LoadingFonts]]` already empty and fired switch-to-loaded after the
first face finished, resolving `document.fonts.ready` while faces in
the same set were potentially still loading.
2026-04-24 20:19:38 +02:00
Shannon Booth
cc6e048bd6 LibWeb+LibIDL: Remove support for #import directives during parsing
These no longer serve any purpose now that we run the IDLGenerator
on all of these files at once.
2026-04-24 20:08:29 +02:00
Tim Ledbetter
b1501dcb45 LibWeb: Avoid copying custom property maps in StyleComputer
Previously, we were accidentally creating temporary copies of custom
property maps on both sides of a ternary in `compute_style_impl()`.  We
now bind to a static empty sentinel instead so the reference binds
directly to `own_values()` without copying.
2026-04-24 17:25:29 +01:00
Callum Law
6b7fb4b273 LibWeb: Replace ViewFunctionStyleValue with FunctionStyleValue 2026-04-24 07:34:54 +01:00
Callum Law
6aeaf69cd3 LibWeb: Replace ScrollFunctionStyleValue with FunctionStyleValue 2026-04-24 07:34:54 +01:00
Callum Law
4faab1aa57 LibWeb: Replace FitContentSV with KeywordSV and FunctionSV
The `fit-content` keyword is stored as a `KeywordStyleValue` and
`fit-content()` function is stored as `FunctionStyleValue`
2026-04-24 07:34:54 +01:00
Callum Law
3bfebf862b LibWeb: Replace AddFunctionStyleValue with FunctionStyleValue 2026-04-24 07:34:54 +01:00
Callum Law
c846b289a9 LibWeb: Replace FontVariantAlternatesFunctionSV with FunctionSV 2026-04-24 07:34:54 +01:00
Callum Law
c558d67ba9 LibWeb: Add generic FunctionStyleValue
Currently there are multiple style values which are essentially the same
thing, a function holding a value, just with different names. This
commit adds a generic style value to replace them with and the following
commits will do so.
2026-04-24 07:34:54 +01:00
Andreas Kling
3cf24872c4 LibWeb: Fix crash removing link stylesheet nested in a shadow tree
HTMLLinkElement::removed_from() used `old_root` to find the
StyleSheetList to remove the link's stylesheet from. That's wrong
when the link element lives inside a shadow tree that is itself
nested within a larger removed subtree: Node::remove() hands every
shadow-including descendant the outer subtree's root as `old_root`,
not the descendant's own containing root. So we'd look in the
document's list while the sheet was actually in the shadow root's
list, failing the did_remove VERIFY in StyleSheetList::remove_sheet.

Fix by using the sheet's own owning-root tracking. A link-owned sheet
always has exactly one owning document or shadow root (only constructed
stylesheets can be adopted, and link sheets are never constructed), so
we can just read that entry.

Also make owning_documents_or_shadow_roots() return by const reference
instead of copying the HashTable on every call, which benefits existing
iterating callers too.

Fixes a crash on https://nytimes.com/.
2026-04-23 22:37:06 +02:00
Andreas Kling
928a5247ff LibWeb: Narrow stylesheet add/remove invalidation
Avoid broad document invalidation when adding or removing ordinary
document-owned or shadow-owned stylesheets. Reuse the targeted
StyleSheetInvalidation path for style rules, including shadow-host
escapes, pseudo-element-only selectors, and trailing-universal cases.

Keep the broad path for sheet contents whose effects are not captured
by selector invalidation alone, including @property, @font-face,
@font-feature-values, @keyframes, imported sheets, and top-level @layer
blocks. Broad-path shadow-root sheets still reach host-side consumers
through their active-scope effects.
2026-04-23 16:45:22 +02:00
Andreas Kling
cfa75e6eb4 LibWeb: Invalidate stylesheet owners when disabled state changes
Toggling CSSStyleSheet::disabled previously cleared the cached media
match bits and reloaded fonts, but never informed the owning documents
or shadow roots that style resolution was now stale. Worse, the IDL
binding for the disabled attribute dispatches through a non-virtual
setter on StyleSheet, so any override on CSSStyleSheet was bypassed
entirely.

Make set_disabled() virtual so the CSSStyleSheet override actually runs,
snapshot the pre-mutation shadow-root stylesheet effects before flipping
the flag, and hand them to invalidate_owners() so a disable that strips
the last host-reaching rule still tears down host-side style correctly.
2026-04-23 16:45:22 +02:00
Andreas Kling
a0dc0c61f4 LibWeb: Scope broad shadow-root stylesheet invalidation
When invalidate_owners() runs on a stylesheet scoped to a shadow root,
we previously dirtied the host and its light-DOM side too broadly. That
forced restyles on nodes the shadow-scoped stylesheet cannot match.

Inspect the sheet's effective selectors and dependent features up front.
Only dirty assigned nodes, the host, the host root, or host-side
animation consumers when the sheet can actually reach them, while
keeping purely shadow-local mutations inside the shadow tree.
2026-04-23 16:45:22 +02:00
Andreas Kling
4e92765211 LibWeb: Narrow @keyframes insertRule() invalidation
Handle inline stylesheet @keyframes insertions without falling back to
broad owner invalidation. Recompute only elements whose computed
animation-name already references the inserted keyframes name.

Document-scoped insertions still walk the shadow-including tree so
existing shadow trees pick up inherited animations, and shadow-root
stylesheets fan out through the host root so :host combinators can
refresh host-side consumers as well. Also introduce the shared
ShadowRootStylesheetEffects analysis so later stylesheet mutation paths
can reuse the same per-scope escape classification.
2026-04-23 16:45:22 +02:00
Andreas Kling
b6559d3846 LibWeb: Narrow inline stylesheet insertRule() invalidation
Avoid forcing a full style update when a connected inline <style> sheet
inserts an ordinary style rule. Build a targeted invalidation set from
the inserted rule and walk only the affected roots instead.

Introduce the shared StyleSheetInvalidation helper so later stylesheet
mutation paths can reuse the same selector analysis and root application
logic. It handles trailing-universal selectors, pseudo-element-only
rightmost compounds, and shadow-host escapes through ::slotted(...) and
:host combinators.

Keep the broad invalidate_owners() path for constructed stylesheets and
other sheet kinds whose TreeScope interactions still require it.
2026-04-23 16:45:22 +02:00
Andreas Kling
a94f9aa4c7 LibWeb: Filter non-inheriting registered custom properties on inherit
When inheriting custom-property data from a parent element, we were
copying the parent's full CustomPropertyData regardless of whether
each property was registered with `inherits: false`. That caused
non-inheriting registered properties to leak from the parent,
contrary to the @property spec.

Wrap the parent-side lookup so we strip any custom property whose
registration says it should not inherit, and only build a fresh
CustomPropertyData when at least one property was actually filtered.

Key the filtered view's cache on both the destination document's
identity and its custom-property registration generation. The
generation counter is local to each document, so a subtree adopted
into another document (or queried via getComputedStyle from another
window) could otherwise pick up a cached view computed under an
unrelated registration set and silently skip non-inheriting filtering
in the new document.
2026-04-22 20:59:00 +02:00
Andreas Kling
11c75a2ffb LibWeb: Fix @keyframes resolution for slotted elements
A @keyframes rule scoped to a shadow root was not reliably reached
from an animated slotted light-DOM element: the keyframes lookup
walked the element's own root first, then fell back to the document,
but slotted elements can pick up animation-name from a ::slotted(...)
rule that lives in an ancestor shadow root rather than in the
element's own tree.

Track the shadow-root scope that supplied each winning cascaded
declaration, and use that scope to resolve the matching @keyframes
when processing animation definitions. A shared constructable
stylesheet can be adopted into several scopes at once, so the
declaration object alone is too weak as a key; the per-entry
shadow-root pointer disambiguates which adoption actually contributed.

Also refresh running CSS animations' keyframe sets when style is
recomputed. Previously only the first animation creation path set a
keyframe set, so an existing animation never picked up newly inserted
@keyframes rules.
2026-04-22 20:59:00 +02:00
Andreas Kling
654e1efacc LibWeb: Refresh CascadedProperties source on same-priority replacement
When CascadedProperties::set_property overwrites an existing entry for
the same origin and layer, it bumped cascade_index but kept the old
source pointer. That left source stale after a later declaration
overrode an earlier one at equal priority, so property_source() could
return a CSSStyleDeclaration that no longer supplied the winning
value.

Refresh source alongside the rewritten property and cascade_index so
the entry consistently describes its current contributor.
2026-04-22 20:59:00 +02:00
Andreas Kling
e4800b2498 LibWeb: Parse @keyframes name as logical string, not token text
The @keyframes parser was storing the keyframes name via
Token::to_string(), which keeps a string token in its quoted,
serialized form. That meant @keyframes "foo" was stored as
"\"foo\"" while animation-name: "foo" resolved to "foo",
and the two never matched.

Store the unquoted string or identifier value so the @keyframes name
and the animation-name reference compare on the same string.
2026-04-22 20:59:00 +02:00
Andreas Kling
a941b8a2a1 LibWeb: Skip font/image activation while a stylesheet is disabled
CSSOM's "add a CSS style sheet" steps bail out once the disabled flag
is set, so ownership alone should not make a disabled sheet observable
in the destination document. Delay CSS-connected font activation in
add_owning_document_or_shadow_root() until the sheet actually becomes
enabled, and refuse pending image-resource loads on a disabled sheet
for the same reason.

Also extend set_disabled() to drive the font/image lifecycle around the
transition: loading fonts and pending images when the sheet becomes
enabled, and unloading fonts when it goes back to disabled.
2026-04-22 20:59:00 +02:00
Callum Law
eab7935686 LibWeb: Explicitly pass calculation context when parsing
Previously we would generate the calculation context based on the
current value parsing context. The main problem with this was that
contexts were defined per property by default and had to be overriden
per component value using "special" contexts, which was easy to forget.

We now generate the calculation context per component value in the
relevant `parse_foo_value` methods.

The new failures in `typed_arithmetic.html` are because we no longer
treat percentages as resolving to their property-level type when
computing what the resolved type of a calculation is i.e. when we are
parsing the `<number>` portion of `line-height` we treat percentages as
raw percentages, not lengths. This brings us in line with WebKit but no
longer with Chrome and WPT, I am not sure what the correct behavior is.
2026-04-22 14:24:12 +01:00
Callum Law
76250ba142 LibWeb: Validate literal numeric values at parse time
This brings a couple of advantages:
 - Previously we relied on the caller validating the parsed value was in
   bounds after the fact - this was usually fine but there are a couple
   of places that it was forgotten (see the tests added in this commit),
   requiring the bounds to be passed as arguments makes us consider the
   desired range more explicitly.
 - In a future commit we will use the passed bounds as the clamping
   bounds for computed values, removing the need for the existing
   `ValueParsingContext` based method we have at the moment.
 - Generating code is easier with this approach
2026-04-22 14:24:12 +01:00
Callum Law
ca1b433d27 LibWeb: Restrict CSS dimension-percentage mix parsing
We should only try to parse a dimension-percentage mix in
`parse_css_value_for_properties` if percentages resolve relative to that
dimension, not simply because percentages are allowed in general.

This doesn't currently cause any issues since we check that percentages
are resolved relative to the relevant dimension within the
`parse_foo_percentage_value` functions
2026-04-22 14:24:12 +01:00
Callum Law
76caf4d041 LibWeb: Deduplicate dimension literal parsing
This extracts the shared logic between <foo> and <foo-percentage>
parsing into helper methods
2026-04-22 14:24:12 +01:00
Callum Law
aa9f2ed83f LibWeb: Rename AcceptedTypeRange to NumericRange
This name wasn't very clear, also updates associated type and variable
names.
2026-04-22 14:24:12 +01:00
Callum Law
18c43bed39 LibWeb: Add missing bounds for numeric types 2026-04-22 14:24:12 +01:00
Callum Law
fc5dae8615 LibWeb: Clamp calculated values in FiltervalueListSV ::absolutized()
In a future commit we will use the allowed literal values as the bounds
for calculated values rather than the existing `ValueParsingContext`
based system. Since the computed bounds are different from the allowed
literal values here we need to handle clamping them manually.
2026-04-22 14:24:12 +01:00
Callum Law
83ed7e7419 LibWeb: Add spec comments for <filter-value-list> parsing 2026-04-22 14:24:12 +01:00
Callum Law
8a7332b7b9 LibWeb: Add OpacityValueStyleValue
This allows us to avoid the ugly hack in
`property_accepted_type_ranges()`.

This also updates the `ValueType` to be `opacity-value` rather than
`opacity` to match the spec.
2026-04-22 14:24:12 +01:00
Callum Law
d6dbabdf0e LibWeb: Clamp CSS numeric token values to float range
This matches the behavior of other browsers. We did the equivalent
change for <integer> in b86377b

We continue to store these as doubles for the extra precision.
2026-04-22 14:24:12 +01:00
Callum Law
dbe0cb4b1d LibWeb: Use number_value() for Number token debug string
This brings us in line with the other numeric types (percentage and
dimension) and allows us to test the clamping behavior that will be
added in a future commit.
2026-04-22 14:24:12 +01:00
Callum Law
cbf6e4454d LibWeb: Remove Token::number()
In a future commit we will be applying clamping to numeric (i.e.
number, percentage, dimension) values which will be done in the
appropriate `Token` getters so accessing the underlying number value
would be a potential footgun
2026-04-22 14:24:12 +01:00
Tim Ledbetter
882bd57b83 LibWeb: Drop unnecessary ColorFunctionStyleValue constructor 2026-04-22 11:52:56 +01:00
Tim Ledbetter
8d4f8a2d7f LibWeb: Route lch()/oklch() through unified ColorFunctionStyleValue 2026-04-22 11:52:56 +01:00
Tim Ledbetter
43ba21a45b LibWeb: Route lab()/oklab() through unified ColorFunctionStyleValue 2026-04-22 11:52:56 +01:00
Tim Ledbetter
e358cf1f0d LibWeb: Route hwb() through unified ColorFunctionStyleValue 2026-04-22 11:52:56 +01:00
Tim Ledbetter
ebe12a8766 LibWeb: Route hsl()/hsla() through unified ColorFunctionStyleValue 2026-04-22 11:52:56 +01:00
Tim Ledbetter
9ea880dcf0 LibWeb: Route rgb()/rgba() through unified ColorFunctionStyleValue 2026-04-22 11:52:56 +01:00