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.
We shouldn't include spread distance when serializing `text-shadow` as
it is not supported unlike `box-shadow` - to achieve this we store
whether this is a text or box shadow within the ShadowStyleValue and
serialize appropriately.
Some properties like `justify-items`, `grid`, or `display` do affect
layout, but their used values can be obtained without performing a
layout calculation.
This change introduces a new helper,
`property_needs_layout_for_getcomputedstyle()`, specifically for use by
`CSSStyleProperties::property()`. It returns true only for properties
such as `width`, `height`, `margin`, `padding`, `top`, and `left`, where
an up-to-date layout is required to return the correct used value.
This has always been a bit of a hack. Initially it made sense as a lot
of properties that accept a length also accept `auto`, but while
convenient, that leads to problems: It's easy to forget to check if a
length is `auto`, and places that don't accept it end up with an
invalid state lurking in the type system, which makes things unclear.
Not every user of this requires an `auto` state, but most do.
This has quite a big diff but most of that is mechanical:
LengthPercentageOrAuto has `resolved_or_auto()` instead of `resolved()`,
and `to_px_or_zero()` instead of `to_px()`, to make their output
clearer.
Any type of Size which has no LengthPercentage value now uses an empty
optional instead of making an auto Length as before.
We also now serialize a `fit-content` Size as `fit-content` instead of
`fit-content(auto)`, though this doesn't affect test results and I
didn't identify where it's actually used.