Remove the visit_edges hook from CSS::StyleValue and stop asking CSS
properties, descriptors, computed values, and layout nodes to trace
through their style values.
Style values are refcounted data objects, so they should not be part of
the GC graph. Keeping this cleanup separate makes the later layout tree
ownership change smaller and easier to review.
Resolved grid-template-columns and grid-template-rows values are needed
for getComputedStyle(), even when DevTools is not connected. Store them
separately from GridLayoutData again so the later lazy DevTools data
work can skip inspector-only geometry without changing CSSOM behavior.
Partly reverts 540b53ac1a.
When a query container changes size or type, descendants that use
container-relative units need the same post-layout style refresh as
elements matched by `@container` rules.
Drop the style-scope gate from the invalidation and getComputedStyle()
paths so any element flagged as depending on a size query container gets
restyled after layout.
Store grid container layout data on the grid container's paintable box
during layout.
The data includes computed track, line, and named-area geometry for
Firefox DevTools protocol serialization. It also carries the resolved
grid-template-columns and grid-template-rows values used by
getComputedStyle(), so PaintableBox does not need separate grid
template storage.
Avoid rewriting CSSOM custom properties when the parsed value and
priority match the existing declaration. This mirrors the longhand
setProperty path and prevents repeated identical root custom property
writes from invalidating inherited style across the whole document.
Extend the style invalidation counter test to cover identical and
changed custom property assignments.
Parameter values are absolutized at style computation time (or are
confirmed to be resolvable in the case of reification or DOMMatrix) so
there is no reason this function should fail.
e.g., `@container (width >= 300px) {}` and similar.
During style computation, flag any elements whose style depends on a
size container. Then re-evaluate their style after the initial layout
has been computed and size containers have a size. This may take
multiple passes, as these may have further descendants that depend on
their size, etc. We limit this to 8 passes currently.
SizeFeature itself is very similar to MediaFeature, but queries the
container element instead. There are only 6 size features specified, so
they're hard-coded instead of generated from JSON.
Also add a counter test for the narrower restyle path.
Descendants of a display:none element are not rendered and their
computed style is only observable through on-demand reads. Skip the
recursive style descent at display:none ancestors during the
top-down traversal in `update_style_recursively()`.
Previously, and according to the spec, `a::part(foo)::before` would be a
single CompoundSelector, even though it matches against 3 different
targets. This meant some awkward swapping of targets in the middle of
matching, and in particular it made `::part()` and `::slotted()` quite
hacky, requiring them to track extra data on the MatchContext to then
use later. This was scattered around and difficult to follow.
Partly inspired by Gecko, this commit instead introduces an invisible
PseudoElement combinator. After parsing a selector, we find any
CompoundSelectors that contain a pseudo-element and split them up, so
that each CompoundSelector only has a single target in the end. Where
the pseudo-element was at the start of a CompoundSelector, we insert an
invisible universal selector before it to represent its originating
element.
So now, a CompoundSelector deals with one target, and switching targets
is done at the combinator.
The one inconsistency is that we match the target of ::slotted()
and ::part() in pseudo_element_transition_target(), instead of before
then when processing the SimpleSelector. This is to avoid repeating the
same computations twice.
No outward-facing behaviour changes, though the invalidation metrics
have changed.
The Paintable tree and its supplemental painting data structures were
GC allocated because that was the easiest way to manage it and avoid
leaks introduced by ref cycles. This included the Paintable subclasses
themselves plus StackingContext, ChromeWidget, Scrollbar, ResizeHandle,
and scroll-frame state.
We are now trying to reduce GC allocation churn on layout and painting
updates, so keeping this short-lived rendering tree outside the JS heap
is a better fit. Move Paintable to RefCountedTreeNode, make painting
helpers ref-counted or weakly reference Paintables, and update the
layout and event-handler call sites to use RefPtr/WeakPtr ownership.
CSSOM declaration mutations on style rules and nested declarations
do not change selector buckets, layer ordering, or keyframe sets stored
in rule caches. Keep those caches valid and only invalidate affected
styles, while leaving keyframe declaration mutations on the existing
cache-invalidating path.
Add coverage showing a CSSOM style declaration mutation is observed
through an already-built rule cache.
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.
Previously we waited until used-value time to apply handling for the
z-index value that we should have applied at declared or computed-value
time, which was papered over by returning the used rather than computed
value as the resolved value. This is no longer required.
This allows us to unmark z-index as requiring a layout node to get
resolved value.
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.
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.
Before calling update_style() for a getComputedStyle property access,
we now check whether the target element actually needs a style update
by walking the flat tree ancestor chain. If neither the element nor any
of its ancestors have dirty style bits, and there are no document-level
reasons to recalculate style, we skip the update_style() call entirely.
We walk the flat tree (not the DOM tree) because style inheritance
follows slot assignment -- slotted elements inherit from their assigned
slot, not their DOM parent.
This avoids unnecessary work when scripts access computed style
properties on elements whose styles are already up-to-date, which is a
common pattern on the web.
Remove 11 heavy includes from Document.h that were only needed for
pointer/reference types (already forward-declared in Forward.h), and
extract the nested ViewportClient interface to a standalone header.
This reduces Document.h's recompilation cascade from ~1228 files to
~717 files (42% reduction). Headers like BrowsingContext.h that were
previously transitively included see even larger improvements (from
~1228 down to ~73 dependents).
Previously, getComputedStyle() would always call update_layout() for
most properties. This was expensive since layout involves a full tree
traversal even when only style information is needed.
This change introduces a more granular approach:
- Properties needing layout computation (used values like width/height)
still call update_layout()
- Properties needing a layout node for resolved value computation
(colors, border widths, etc.) also call update_layout()
- All other properties now only call update_style()
The set of properties needing layout node for resolution is now defined
in Properties.json via the "needs-layout-node-for-resolved-value" flag,
rather than being hardcoded. This is generated into a new function
property_needs_layout_node_for_resolved_value().
If a custom property is registered, then it may have an initial value
which we should fall back to here.
This is a temporary hack until we cascade our custom properties (see
https://github.com/LadybirdBrowser/ladybird/pull/6608).
The Transformation class wasn't really accomplishing anything. It still
had to store StyleValues, so it was basically the same as
TransformationStyleValue, with extra steps to convert from one to the
other. So... let's just use TransformationStyleValue instead!
Apart from moving code around, the behavior has changed a bit. We now
actually acknowledge unresolvable parameters and return an error when
we try to produce a matrix from them. Previously we just skipped over
them, which was pretty wrong. This gets us an extra pass in the
typed-om test.
We also get some slightly different results with our transform
serialization, because we're not converting to CSSPixels and back.
...when the style is `none` or `hidden`. `outline-width` is not affected
by `outline-style: none` at all.
In our codebase, that means doing the border-width conversion when
assigning to ComputedValues.
Corresponds to:
2a3d1e4d1009f11f2ef9
Previously we would either parse these as `StyleValueList<T>` or `T`
depending on whether or not there was more than one value, this meant we
always had to handle both cases anywhere we used these values.
We were doing this manually within `Document::update_layout()` and
`CSSStyleProperties::get_direct_property()` but we should do it for all
callers of `Document::update_style()`
Doing trigonometric calculations with floating point numbers can
introduce small inaccuracies. This meant that we would sometimes
incorrectly generate a 3d rather than 2d matrix for the resolved value
of `transform`.
Gains us 3 WPT tests.
Shorthands should be broken up into their longhands, instead of setting
them directly.
There's a catch here with our "positional value list shorthands" like
`margin`: Setting margin to a single value like `CSSUnitValue(10, "px")`
is supposed to fail here, but our type-checking code thinks it's valid
because our JSON for `margin` says it accepts lengths. This is the same
kind of issue that we had for `cursor` discussed in the
"LibWeb/CSS: Support converting CSSUnitValue to a StyleValue" commit.
Will get us a few subtest passes for every shorthand that's tested.
Pending changes to an ancestor document's layout can affect an element's
computed style e.g. an IFrame's width being changed can affect media
query evaluation and the value of the `vw` unit.
StylePropertyMap only ever works with style properties - never
descriptors. Switching `has_property()` and `get_property_style_value()`
to taking PropertyNameAndID skips some duplicate work.
This awkwardly sat as the internal final API for getting a StyleProperty
directly for a given PropertyID, and also the external API for getting
the StyleProperty for a PropertyID. For the latter, it lacked support
for shorthands, and for both it lacked support for custom properties.
This commit:
- Moves the code from get_property() into get_direct_property()
- Makes get_property() call get_property_internal() to support
shorthands
- Adds custom property support for get_direct_property()
This also wins us some WPT points for StylePropertyMap.