Commit Graph

2320 Commits

Author SHA1 Message Date
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
Tim Ledbetter
53a0991ff5 LibWeb: Remove LightDark and ColorMix from the ColorType enum 2026-04-22 11:52:56 +01:00
Tim Ledbetter
e4c29811be LibWeb: Generalize ColorFunctionStyleValue to cover every color space
Introduce a descriptor table keyed by `ColorType` that encodes
per-space metadata: channel kind, percent reference value, clamp
bounds, function name and serialization behavior.

This descriptor table is then used so that a single class can back
every CSS color type. Resolution, serialization, and absolutization
are driven by the descriptor.
2026-04-22 11:52:56 +01:00
Shannon Booth
fd44da6829 LibWeb/Bindings: Emit one bindings header and cpp per IDL
Previously, the LibWeb bindings generator would output multiple per
interface files like Prototype/Constructor/Namespace/GlobalMixin
depending on the contents of that IDL file.

This complicates the build system as it means that it does not know
what files will be generated without knowledge of the contents of that
IDL file.

Instead, for each IDL file only generate a single Bindings/<IDLFile>.h
and Bindings/<IDLFile>.cpp.
2026-04-21 07:36:13 +02:00
Andreas Kling
7a5b1d9de1 LibWeb: Delay generic :has() sibling scans until sibling roots
Mark elements reached by stepping through sibling combinators inside
:has() and use that breadcrumb during generic invalidation walks.

Keep the existing conservative sibling scans for mutations outside
those marked subtrees so nested :is(), :not(), and nesting cases
continue to invalidate correctly.

Also keep :has() eager within compounds that contain ::part(). Those
selectors retarget the remaining simple selectors to the part host, so
deferring :has() there changes which element the pseudo-class runs
against and can make ::part(foo):has(.match) spuriously match.

Add a counter-based sibling-scan test and a regression test covering
the ::part()/ :has() selector orderings.
2026-04-20 13:20:41 +02:00
Andreas Kling
85ff13870f LibWeb: Stop :has() invalidation walk when out of :has() scope
A DOM mutation under a document that uses any :has() rule currently
walks every ancestor up to the root, invoking invalidate_style_if_
affected_by_has() on each. Most of those ancestors have nothing to
do with :has(), so the work scales linearly with DOM depth.

Introduce an in_has_scope flag on Element, set while evaluating :has()
arguments for invalidation metadata. StyleScope's upward invalidation
walk now terminates at the first element that is neither in :has()
scope nor a :has() anchor, so it only traverses the region where some
:has() rule might actually care about the change.

Keep the existing fast :has() matching paths for normal selector
matching, but bypass them while collecting per-element metadata so the
scope markers still get populated. Node insertion also schedules the
parent for the :has() walk so newly inserted nodes still reach the real
anchor.

The css-has-invalidation suite adds focused coverage for these shapes
and updates the expected counters to reflect the shorter walks.
2026-04-20 13:20:41 +02:00
Andreas Kling
029b4998e5 LibWeb: Skip useless sibling scans in generic :has() walks
Track whether any :has() relative selector in a style scope uses a
sibling combinator and let the generic ancestor walk consult that
before scanning ancestor siblings.

This keeps descendant-only :has() invalidations from walking unrelated
siblings while preserving the existing behavior for selectors that use
+ or ~. Add counter-based test coverage so the reduced sibling scans
stay visible through the invalidation counters.
2026-04-20 13:20:41 +02:00
Andreas Kling
e1d62eaf85 LibWeb: Bucket :has() invalidation metadata by feature
Record per-feature :has() invalidation metadata instead of only tracking
whether some selector somewhere mentions a class, id, attribute, tag,
or pseudo-class. The new buckets preserve the relative selector and a
coarse scope classification for each :has() argument, which gives the
next invalidation step enough information to route mutations more
precisely.

Keep this commit behavior-preserving for mutation handling by only
switching the lookup path over to the new metadata buckets. Expose a
test-only counter for the number of candidate :has() metadata entries a
mutation matched, and add coverage showing that one feature can map to
one or multiple :has() buckets without forcing a document-wide yes/no
answer.
2026-04-20 13:20:41 +02:00
Andreas Kling
55ebd8a589 LibWeb: Ignore invalidation rule order when merging plans
Treat structurally equivalent invalidation plans as equal even when
their descendant or sibling rules were accumulated in a different
order. This lets :has() invalidation merge more of the repeated
descendant-only payloads that still showed up after the earlier
structural dedup.

Add a :has() invalidation counter test that exercises equivalent
selector permutations so this shape stays covered.
2026-04-20 13:20:41 +02:00
Andreas Kling
04fd26a02b LibWeb: Fast-path simple descendant compound :has()
Teach :has() matching to recognize the common case of a single
descendant compound made only of tag and class selectors. This lets us
stay on the cheap per-element matcher instead of recursing through the
full relative-selector machinery for each candidate descendant.

Keep the optimization limited to that simple selector shape and fall
back to the generic matcher for everything else.
2026-04-20 13:20:41 +02:00
Andreas Kling
b20bbbc6b3 LibWeb: Fast-path simple child-tag :has() matching
Avoid the generic relative-selector matcher for child-only tag
selectors inside :has(). These selectors can be answered by walking the
anchor's direct children and checking the tag match directly, which
keeps a very hot path cheaper.

Preserve the existing ancestor cache behavior and fall back to the
generic matcher for all other selector shapes.
2026-04-20 13:20:41 +02:00
Andreas Kling
97e2b05004 LibWeb: Merge equivalent style invalidation plans
Compare invalidation sets, rules, and plans structurally so repeated
descendant and sibling invalidation entries can be merged even when
they were built as separate payload objects.

Also deduplicate pending and active descendant invalidations in the
style invalidator so equivalent rules are not re-applied as the DOM
walk descends. This reduces :has() invalidation fanout while keeping
behavior the same.
2026-04-20 13:20:41 +02:00
Andreas Kling
a72fae8d36 LibWeb: Add test-only counters for :has() invalidation work
Introduce a small set of counters on Document that track the work done
while processing :has() invalidation: how often the upward walk runs,
how many elements it visits, how often matches_has_pseudo_class() is
invoked, how well the per-pass result cache performs, and how many
elements transition from clean to needs-style-update.

Expose the counters through internals so tests can assert precise bounds
on the invalidation work triggered by a mutation, which regular
reference tests cannot express.

Add a css-has-invalidation test suite that covers subject-position,
non-subject-position, sibling-combinator, and no-:has() cases. The
baseline tests share a helper script so later coverage can reuse the
same counter-printing path.

The counters are test-only observation; they do not affect style
computation itself.
2026-04-20 13:20:41 +02:00
Andreas Kling
e1d35d7da9 LibWeb+LibGfx: Decompress WOFF2 fonts off the main thread
Add an off-thread preparation step for downloaded vector fonts so
WOFF2 resources can be decompressed before LibWeb tries to create a
typeface from them. This avoids doing the conversion work on the main
thread during @font-face loading.

Expose raw WOFF2-to-TTF conversion from LibGfx's WOFF2 loader and use
that from the new preparation path. Keeping the libwoff2 integration
in LibGfx preserves the layering between LibWeb and the third-party
decoder while still letting LibWeb schedule the work off-thread.
2026-04-18 23:46:20 +02:00
Sam Atkins
5d965d6d37 LibWeb/CSS: Simplify parse_as_pseudo_element_selector()
Now that we have a method that parses a single pseudo-element selector,
use that. This actually fixes a bug too.
2026-04-18 08:56:25 +02:00
Andreas Kling
bc01fc3280 LibWeb: Use pre-cached specificity when sorting matching CSS rules
sort_matching_rules() was calling Selector::specificity() inside the
sort comparator. MatchingRule already caches the specificity value
at rule insertion time, so use that directly instead.
2026-04-17 16:23:15 +02:00
Sam Atkins
2b47098301 LibWeb/CSS: Un-deprecate TokenStream::peek_token()
This is sometimes the best tool for the job. To try and make it less of
a footgun that peek_token() is identical to next_token(), make the
offset required, and note the shared behaviour. Also move them
together, and make the internal code match.
2026-04-16 14:52:22 +01:00
Sam Atkins
1f8a8870c3 LibWeb/CSS: Replace trivial peek_token() users with next_token()
`next_token()` does the same thing as `peek_token()` or `peek_token(0)`,
but is more idiomatic.
2026-04-16 14:52:22 +01:00
Sam Atkins
f938829619 LibWeb/CSS: Remove a couple of deprecated TokenStream methods 2026-04-16 14:52:22 +01:00
Sam Atkins
e7e0a48144 LibWeb/CSS: Stop reconsuming tokens in an+b parsing
The main change is that we now wait to consume the token until we know
we want it, instead of consuming it and then later putting it back.

While I was here, I spec-commented some parts and updated the URL.
2026-04-16 14:52:22 +01:00
Sam Atkins
d7c2524311 LibWeb: Stop reconsuming tokens to parse shorthands
Reconsuming the current token relies on parse_css_value_for_properties()
only consuming a single token, which may be true now but isn't safe to
assume. Wrapping that in a Transaction and reverting it if we need to
run manual parsing, is a much safer pattern.

Ideally we'll get parse_css_value_for_properties() to run the
per-property bespoke parsing code eventually.
2026-04-16 14:52:22 +01:00
Sam Atkins
b661c17117 LibWeb/CSS: Stop reconsuming tokens in parse_simple_selector()
No behaviour change, just a refactor to stop using a deprecated API.
2026-04-16 14:52:22 +01:00
Sam Atkins
7eabbfaa4b LibWeb/CSS: Use Transaction for checking tokens' substitution presence
Equivalent to using mark() and restore_a_mark() from before, except it
runs even if we return a SyntaxError.

Also removes the deprecated reconsume_current_input_token() call - this
never actually did anything useful anyway.
2026-04-16 14:52:22 +01:00
Sam Atkins
1239ebad29 LibWeb/CSS: Use transaction for attribute selector match-type parsing
No behaviour change, just a refactor to stop using a deprecated API -
reconsuming tokens.
2026-04-16 14:52:22 +01:00
Sam Atkins
3681452386 LibWeb/CSS: Stop reconsuming tokens in convert_to_keyframes_rule()
No behaviour change, just a refactor to stop using a deprecated API.
2026-04-16 14:52:22 +01:00
Sam Atkins
3342d880e8 LibWeb/CSS: Use a transaction for simple comma-separated list parsing
No behaviour change, just a refactor to stop using a deprecated API.
2026-04-16 14:52:22 +01:00
Tim Ledbetter
176e1a5a7c LibWeb: Don't clamp LCH/OKLCH chroma max value during serialization 2026-04-16 12:06:10 +02:00
Tim Ledbetter
49fa4d702d LibWeb: Apply color-mix() alpha multiplier in the absolutized path
When the two component percentages of color-mix() sum
to less than 100%, the remainder is used as an alpha multiplier applied
to the interpolated result's alpha. Previously, this was only applied
in the runtime `to_color()` path, not when computing the absolutized
value used by `getComputedStyle()`.

Switch both paths to use `perform_color_interpolation()` directly so the
alpha multiplier can be applied to the interpolated components before
they are converted to a style value.
2026-04-16 12:06:10 +02:00
Tim Ledbetter
094acbdcf1 LibWeb: Default color-mix() interpolation method to Oklab 2026-04-16 12:06:10 +02:00
Tim Ledbetter
d8e150421f LibWeb: Align color interpolation with the css-color specification 2026-04-16 12:06:10 +02:00
Sam Atkins
08dbbf24c2 LibWeb/CSS: Tokenize custom property values when substituting var()
Co-authored-by: Shannon Booth <shannon@serenityos.org>
2026-04-15 16:42:15 +02:00
Sam Atkins
7f2ff6df4e LibWeb/CSS: Tokenize PercentageStyleValues properly
- These should be Percentage tokens, not Dimensions.
- The `unit_name` is "percent", so the serialization also came out wrong
  as e.g. `10percent` instead of `10%`.
2026-04-15 16:42:15 +02:00
Callum Law
8a547a3f31 LibWeb: Clear counter style cache when CSSCounterStyleRule modified 2026-04-15 11:07:38 +01:00
Callum Law
a1cc560caa LibWeb: Add CSSStyleSheet::for_each_owning_style_scope 2026-04-15 11:07:38 +01:00
Callum Law
8d2995c785 LibWeb: Apply correct invalidations when effective counter style changes
The counter style used for an element (in either the `content` or
`list-style-type`) may change despite the computed values of properties
on that element remaining the same (e.g. if a new rule is inserted with
higher cascade precedence).
2026-04-15 11:07:38 +01:00
Callum Law
8b69e2fc81 LibWeb: Simplify compute_property_invalidation 2026-04-15 11:07:38 +01:00
Callum Law
2313460ab6 LibWeb: Make null content on ComputedValues more explicit
In a later commit we can use the null state for an optimization.
2026-04-15 11:07:38 +01:00
Callum Law
595848b0fa LibWeb: Implement CounterStyle::equals
This makes `ValueComparingRefPtr<CounterStyle>` work
2026-04-15 11:07:38 +01:00
Callum Law
64ccb9a015 LibWeb: Make @counter-style tree-scoped
This was a pretty straightforward change of storing registered counter
styles on the relevant `StyleScope`s and resolving by following the
process to dereference a global tree-scoped name, the only things of
note are:
 - We only define predefined counter styles (e.g. decimal) on the
   document's scope (since otherwise overrides in outer scopes would
   themselves be overriden).
 - When registering counter styles we don't have the full list of
   extendable styles so we defer fallback to "decimal" for undefined
   styles until `CounterStyle::from_counter_style_definition`.
2026-04-15 11:07:38 +01:00
Callum Law
8d117725e5 LibWeb: Add helper method to dereference global tree-scoped references 2026-04-15 11:07:38 +01:00
Sam Atkins
5385d90e3c LibWeb/CSS: Output more details in Token::to_debug_string()
Include all the relevant fields for the type of Token, so that the
upcoming Tokenizer tests can verify that the produced Tokens are
correct.
2026-04-15 10:23:09 +01:00
Sam Atkins
d54305eb06 LibWeb/CSS: Ensure at-keyword name is consumed before input_since()
This relied on input_since() being called after
consume_an_ident_sequence(), which is not at all guaranteed. GCC
evidently evaluates the arguments in the opposite order.

This caused AtKeyword tokens to have their original source text be `@`
instead of the full `@whatever`, which apparently has no consequences
in our current code, but does mess up the debug output introduced in
the following commit.
2026-04-15 10:23:09 +01:00
Callum Law
76f0416b75 LibWeb: Resolve keywords in basic shape position at compute time
Previously we resolved these at parse time since that's what WPT
expected, but WPT was updated in web-platform-tests/wpt#59022 and
web-platform-tests/wpt#59176
2026-04-15 08:04:02 +02:00
Callum Law
de10db6da3 LibWeb: Remove unused interpolate_length_percentage{_auto} methods 2026-04-13 19:24:43 +02:00
Callum Law
1e36ccad0d LibWeb: Remove unused PercentageOr classes
These are only used at used value time (since we store as `StyleValue`s
before) by which time we resolve all percentages except for layout
relative length percentages.
2026-04-13 19:24:43 +02:00
Callum Law
d25f86da66 LibWeb: Remove unused CSS::Parser::Dimension 2026-04-13 19:24:43 +02:00