Commit Graph

2105 Commits

Author SHA1 Message Date
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
Tim Ledbetter
e49b643f86 LibWeb: Allow custom properties on pseudo-elements with property lists 2026-03-04 13:36:53 +01:00
Tim Ledbetter
80977b5fea LibWeb: Apply contain constraint in default image sizing algorithm
When an image has no intrinsic dimensions but has an intrinsic aspect
ratio, the CSS default sizing algorithm should resolve its size as a
contain constraint against the default object size. Previously, we
returned the default size directly, which caused such images to stretch
to fill the entire background positioning area. The SVG's default
`preserveAspectRatio` would then center the content within that
oversized viewport, making the image appear horizontally mispositioned.
2026-03-03 17:14:19 +01:00
Tim Ledbetter
a7b70b0042 LibWeb: Ensure execution context exists for FontFace loading task 2026-03-03 11:25:49 +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
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
Shannon Booth
46cd47753f LibWeb: Make more use of Value::{as,as_if,is} in LibWeb 2026-02-28 10:24:37 -05:00
Callum Law
81cb968beb LibWeb: Support symbols() function in <counter-style> 2026-02-27 16:25:53 +00:00
Callum Law
f6eccc629c LibWeb: Default @counter-style system descriptor to symbolic
The spec says that the `system` descriptor defaults to `symbolic` but
previously we just ignored any `@counter-style` rules without a `system`
descriptor
2026-02-27 16:25:53 +00:00
Callum Law
858989e006 LibWeb: Make CounterStyle ref counted
Previously we just passed around a reference to the `CounterStyle`
stored on `Document::registered_counter_styles` but this won't be
possible for anonymous counter styles (i.e. those created by the
`<symbols()>` function)
2026-02-27 16:25:53 +00:00
Callum Law
5b4b3b305b LibWeb: Remove unused to_counter_style_name_keyword()
The last user of this was removed in 8d40842 but this wasn't removed.
2026-02-27 16:25:53 +00:00
Tim Ledbetter
804287847a LibWeb: Add SVG paint fallback color support to CSS parsing 2026-02-27 17:14:50 +01:00
Callum Law
b683568556 LibWeb: Add missing predefined counter style keywords
This means that we properly lowercase these keywords when used as
counter style names
2026-02-27 12:10:44 +00:00
Callum Law
d0eabada0b LibWeb: Support extended Korean counter styles 2026-02-27 12:10:44 +00:00
Callum Law
1877c20c7b LibWeb: Support extended Japanese counter styles 2026-02-27 12:10:44 +00:00
Callum Law
08a5ed7ec6 LibWeb: Support Chinese counter styles
We implement the extended version of this algorithm but don't take full
advantage of it since we are limited to an i32 for our counter values.
2026-02-27 12:10:44 +00:00
Callum Law
120cabd35c LibWeb: Support ethiopic-numeric counter style 2026-02-27 12:10:44 +00:00
Callum Law
66dee23f6e LibWeb: Use i64 when generating initial counter representation
This means that we can correctly represent the absolute value of INT_MIN
(2147483648) rather than clamping it to an i32 (2147483647)
2026-02-27 12:10:44 +00:00
Callum Law
bd1ebdb8bb LibWeb: Add reusable CounterStyleAlgorithmOrExtends type
We already use this in a few places and will use it even more in later
commits
2026-02-27 12:10:44 +00:00
Callum Law
c467c2f9ca LibWeb: Make CounterStyleDefinition::create return type non-optional
And also use it within `from_counter_style`
2026-02-27 12:10:44 +00:00
Andreas Kling
a146225331 LibWeb: Use unsafe layout/paintable accessors where appropriate
Add unsafe_layout_node(), unsafe_paintable(), and unsafe_paintable_box()
accessors that skip layout-staleness verification. These are for use in
contexts where accessing layout/paintable data is legitimate despite
layout not being up to date: tree construction, style recalculation,
painting, animation interpolation, DOM mutation, and invalidation
propagation.

Also add wrapper APIs on Node to centralize common patterns:
- set_needs_display() wraps if (unsafe_paintable()) ...set_needs_display
- set_needs_paint_only_properties_update() wraps similar
- set_needs_layout_update() wraps if (unsafe_layout_node()) ...

And add Document::layout_is_up_to_date() which checks whether layout
tree update flags are all clear.
2026-02-26 21:09:08 +01:00
Jelle Raaijmakers
2b78b84979 AK+Everywhere: Add and use weak_callback()
We have a common pattern of creating a `WeakPtr<T>` from a reference and
passing that into a lambda, to then take the strong ref when the lambda
is executed. Add `weak_callback(Weakable, lambda)` that returns a lambda
that only invokes the callback if a strong ref exists, and passes it as
the first argument.
2026-02-26 08:03:50 -05:00
Tim Ledbetter
f05bc7c0cd LibWeb: Implement dominant-baseline for SVG text
This property determines the default baseline used to align content
within the given box.
2026-02-26 09:23:23 +01:00
Jelle Raaijmakers
90a211bf47 LibWeb: Use device-pixel coordinates in display list and AVC
Stop converting between CSS and device pixels as part of rendering - the
display list should be as simple as possible, so convert to DevicePixels
once when constructing the display list.
2026-02-26 07:43:00 +01:00
Andreas Kling
f1573dfc63 LibWeb: Use GC::WeakHashSet for StyleScope's pending :has() nodes
Replace the unsafe HashTable<GC::Weak<DOM::Node>> with
GC::WeakHashSet<DOM::Node>. The null check in the iteration loop is
no longer needed since WeakHashSet's iterator skips dead entries.
2026-02-24 22:35:03 +01:00
Sam Atkins
1ac3ede046 LibWeb/CSS: Make @font-face a critical style subresource
This causes `@font-face`'s loading state to propagate to its style sheet
and upwards.
2026-02-24 15:44:32 +00:00
Sam Atkins
5e3f4d71cc LibWeb/CSS: Remove number_of_css_font_faces_with_loading_in_progress()
This is now unused as of the previous commit.
2026-02-24 15:44:32 +00:00
Sam Atkins
d9f12da712 LibWeb: Implement missing code for FontFace/FontFaceSet loading
As FontFaces are added or removed from a FontFaceSet, and as they load
or fail, the FontFaceSet moves them between a few different lists, and
updates its loading/loaded status. In the spec, this is how the
FontFaceSet.[[ReadyPromise]] gets fulfilled: When the document has
finished loading and FontFaceSet.[[LoadingFonts]] is empty, it resolves
the promise.

To support this, FontFace now keeps a set of FontFaceSets that it is
contained in.

This lets us remove the non-spec resolve_ready_promise() call in
EventLoop which was sometimes triggering before any fonts had attempted
to load.

As noted, there's a spec issue with the ready promise: If nothing
modifies the document's fonts, then it would never resolve. My ad-hoc
fix is to also switch the FontFaceSet to the loaded state if it is
empty, which appears to solve the issues but is not ideal.
2026-02-24 15:44:32 +00:00
Sam Atkins
f82201e908 LibWeb/CSS: Remove unnecessary FontFaceSet::construct_impl()
The current css-font-loading spec doesn't define a constructor for this,
and from our own code we can rely on initializing things in the C++
constructor.
2026-02-24 15:44:32 +00:00
Sam Atkins
4ea87db0a8 LibWeb/CSS: Load all fonts through FontFace::load()
The spec requires us to follow the steps in FontFace::load() whenever a
font is loaded. The simplest way to do so, is to make that the only way
we load fonts. :^)

To support this, FontFace::load() uses its connected CSSFontFaceRule's
ParsedFontFace if it's available.

The 1 regression in generic-family-keywords-003.html seems to be a false
positive: we don't support the system-ui font keyword.
2026-02-24 15:44:32 +00:00
Sam Atkins
86ae5022c2 LibWeb/CSS: Mark CSSStyleSheetInit::baseURL as nullable
Corresponds to:
f0635e6e10

Assigning null to a non-nullable DOMString didn't make sense.
Apparently our bindings generator didn't care though. It's already
nullable on the C++ side by being an Optional<String>.
2026-02-24 14:36:40 +01:00
Sam Atkins
c9350374a1 LibWeb/CSS: Update a couple of font-related comments 2026-02-24 10:05:09 +00:00