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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
- 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%`.
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).
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`.
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.
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.