The computation context used is the main thing distinguishing the
computation of font/non-font properties so having a generic method to
handle this will allow us to consolidate logic between the two.
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.
The author_rules vector always contains at least one layer (the
unlayered entry), so checking is_empty() was always false. Instead,
check whether any layer actually contains rules.
These get computed to an equivalent RGBColorStyleValue.
To support this, we now store the computed color-scheme on the
ComputationContext when computing properties that might contain a color.
This has a nice bonus of correcting the css-accent-color test's result.
Every user of this actually wants an ancestor in the flat tree - taking
things like `<slot>` into account. So rename it and adjust its behavior
to use that.
In a future commit we will require this to be in it's computed form
before calling `ComputedProperties::computed_font_list()`.
We will also depend on other properties (e.g. `font-kerning`,
`font-variant-*`) but these are unaffected by computation.
The main change here is that we now properly absolutize values which
means we now support `random()` and `sibling-{count,index}()`
We are also more consistent with how we handle computation for the other
font properties
Previously we implemented an all encompassing `MathDepthStyleValue`
specifically for the `math-depth` property, this was unnecessary since
we can represent `auto-add` and `<integer>` using existing `StyleValue`
classes.
This brings the values created from parsing in line with those set via
`StylePropertyMap` which allows us to simplify computation
Replace two uses of heap-allocated Bitmap with stack-allocated
FixedBitmap. The sizes are known at compile time, so there's no need
to hit malloc for these.
This means we now allow oblique angles when parsing the `font`
shorthand.
This also required us to rename the existing `FontStyle` enum to
`FontStyleKeyword`
Previously we would do two cascaded property lookups per property during
style computation - the first to get the value of the property and the
second to check whether it was marked as important. We now do this as a
single lookup to save some unnecessary work.
This adds visit_edges(Cell::Visitor&) methods to various helper structs
that contain GC pointers, and makes sure they are called from owning
GC-heap-allocated objects as needed.
These were found by our Clang plugin after expanding its capabilities.
The added rules will be enforced by CI going forward.
Previously the logic to compute transitions was split across
`ComputedProperties`, `StyleComputer`, and `Animatable` - this commit
consolidates it all in `ComputedProperties`
The `:has()` pseudo-class requires traversing descendants (or siblings)
to find matches.
With this change we cache results keyed by `(Selector*, Element*)`
pairs. The cache is stored in `StyleComputer` and cleared at the start
of each style computation pass in `Document::update_style()`.
When `:has()` uses a descendant combinator and we find a match, we also
cache that all ancestors between the matching descendant and the
anchor match. For example with `div:has(.target)`:
```html
<div id="A"> <!-- checking :has(.target) here -->
<div id="B">
<div id="C">
<span class="target"/>
</div>
</div>
</div>
```
When we find `.target` while checking `div#A`, we also cache that
`div#B` and `div#C` match `:has(.target)` since they also contain
`.target`. Later when styling these elements, we get cache hits and skip
traversal.
...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
In level 2 of the web animations spec, times are no longer always
measures in milliseconds, they can also be percents when dealing with
progress-based (i.e. scroll-based) timelines.
We don't actually support percent times yet but this change will make it
easier to implement when we do.
Web Animations Level 2 disallows setting some `AnimationEffect` timing
values (start delay, end delay, iteration duration) directly and instead
allows authors to set the specified values which are then normalized
into the actual used values taking into account the type of the
associated timeline (i.e. progress- vs time-based)
This makes us consistent with how we handle this value within the
`background` shorthand and allows us to remove the special handling in
`StyleComputer::for_each_property_expanding_shorthands`
Computing the font for an element in `compute_font` is premature since
we are yet to apply animated properties - instead we should compute the
value on the fly (with a cache to avoid unnecessary work) to ensure we
are respecting the latest values
Font computation and loading is distinct enough from style computation
that it makes more sense to have this in it's own class.
This will be useful later when we move the font loading process to
`ComputedProperties` in order to respect animated values.
Since we resolve any relative lengths at compute time there's no need
for the value to be passed around as a `NumberOrCalculated` and we can
just resolve it within `ComputedProperties::font_variation_settings`.
The only place this is used it is used with value_or so there's no need
to return it is an `Optional`.
This is only used for loading fonts (which occurs during style
computation) so there's no need to store it in `ComputedValues`
From the spec:
> The owning element of a transition refers to the element or
pseudo-element to which the transition-property property was applied
that generated the animation.
https://drafts.csswg.org/css-transitions-2/#owning-element
Previously we only stored the element.
An upcoming commit will migrate the contents of Headers.h/cpp to LibHTTP
for use outside of LibWeb. These CORS and MIME helpers depend on other
LibWeb facilities, however, so they cannot be moved.
Previously we would only update these if:
a) We had a cascaded value for `transition-property`
b) The source of that cascaded value had changed since we last
registered transitions
This meant that there were a lot of changes we didn't apply:
- Changes exclusively to properties other than `transition-property`
(e.g. `transition-duration`, `transition-behavior`, etc)
- Removing the `transition-property` property
- Updating the `transition-property` property in a way that didn't
change it's source (e.g. setting it within inline-style)
Unfortunately this does mean that we now register transitions for all
properties on most elements since "all" is the initial value for
"transition-property" which isn't great for performance, but that can be
looked at in later commits.
Also renames the `clear_transitions` function to clarify this doesn't
affect the associated transition animations.
This fixes an issue where transitions weren't being cancelled when the
relevant transition-property entry was no longer present
Before this change, we've been maintaining various StyleComputer caches
at the document level.
This made sense for old-school documents without shadow trees, since
all the style information was document-wide anyway. However, documents
with many shadow trees ended up suffering since any time you mutated
a style sheet inside a shadow tree, *all* style caches for the entire
document would get invalidated.
This was particularly expensive on Reddit, which has tons of shadow
trees with their own style elements. Every time we'd create one of their
custom elements, we'd invalidate the document-level "rule cache" and
have to rebuild it, taking about ~60ms each time (ouch).
This commit introduces a new object called StyleScope.
Every Document and ShadowRoot has its own StyleScope. Rule caches etc
are moved from StyleComputer to StyleScope.
Rule cache invalidation now happens at StyleScope level. As an example,
rule cache rebuilds now take ~1ms on Reddit instead of ~60ms.
This is largely a mechanical change, moving things around, but there's
one key detail to be aware of: due to the :host selector, which works
across the shadow DOM boundary and reaches from inside a shadow tree out
into the light tree, there are various places where we have to check
both the shadow tree's StyleScope *and* the document-level StyleScope
in order to get all rules that may apply.
The exception returned here is never used or logged. Let's just return
a null request, to make it clearer that these are not exceptions shown
to the user (thus not observable).
`play_or_cancel_animations_after_display_property_change` is called
whenever an element is inserted or removed, or it's display property
changes, but it is only required to run if we actually have animations
to play or cancel.
Reduces time spent in the aforementioned function from ~2% to ~0.03%
when loading https://en.wikipedia.org/wiki/2023_in_American_television
We don't need to iterate every property in start_needed_transitions,
only those that appear in transition-property or have an existing
transition
Reduces the time spent in start_needed_transitions from ~5% to ~0.03%
when loading https://en.wikipedia.org/wiki/2023_in_American_television
The majority of time in `compute_font()` was spent in
`font_matching_algorithm()` repeatedly computing the same values. We
now cache these values to avoid unnecessary work.
Makes the `compute_font_variation_settings` function match other
`compute_font_*` by absolutizing values passed into them. It resolves a
crash related to using `siblings-count` inside `font-variation-settings`
Co-authored-by: Sam Atkins <sam@ladybird.org>
Integrates the new `FontVariationSettings` from LibGfx into LibWeb to
enable initial variable font functionality. Currently, only the `wght`
(weight) axis is fully supported and tested. This update also introduces
support for the CSS `font-variation-settings` property.
There were a couple places that we had special handling for the `all`
property but since d31a58a was merged we can treat it the same as any
other shorthand
There are a few places in style computation where we want to update the
value of a property without modifying the inherited or important flags.
Previously we would look up these flags and pass them to the normal
`set_property` method but this is unnecessary and was easy to forget to
do.
We don't need all this specific logic for parsing the `transition`
property - we also now maintain `none` as such until use time which
gains us a couple extra tests
Unrecognized property names should still be kept in the list to preserve
matching of indices (e.g. that the Nth property should associate with
the Nth duration)
Since we now have access to the `AbstractElement` through the
`ComputationContext` we can just set the flag that this element relies
on tree counting functions directly, no need to pass this struct around.
Passing the `AbstractElement` rather than the
`TreeCountingFunctionResolutionContext` allows us to only compute the
resolution context when necessary (i.e. when we actually need to resolve
a tree counting function)
When we compute style for elements inside a UA-internal shadow tree that
represent a pseudo-element (e.g ::placeholder), we actually run the
StyleComputer machinery for (host element :: pseudo-element).
While that lets us match the correct selectors, it was incorrectly
applying CSS inheritance, since we'd also then inherit from whatever was
above the host element in the tree.
This patch fixes the issue by introducing an inheritance override in
AbstractElement and then using that to force inheritance from whatever
is actually directly above in the DOM for these elements instead of
jumping all the way up past the host.
This fixes an issue where `text-align: center` on input type=text
elements would render the main text centered but placeholder text was
still left-aligned.
Tree counting functions should be resolved at style computation time -
to do this we will need to know the element's sibling count and index.
This commit computes that information and propagates it to the various
`StyleValue::to_computed_value` methods.
This excludes `step-end` and `step-start` which are expected to be
converted to the equivalent function at parse time.
We are expected to serialize these as the explicit keywords - previously
we would parse as `EasingStyleValue` and serialize equivalent functions
as the keywords. This caused issues as we would incorrectly serialize
even explicit functions as the keyword.
This also allows us to move the magic easing functions to
`EasingFunction` rather than `EasingStyleValue` which is a bit tidier
This struct will in the future hold information other than a length
resolution context (e.g. context for tree counting functions) and a
single struct is easier to work with than multiple parameters.
This commit modifies the `compute_foo()` code for `font-style` and
`math-depth`. They previously assumed that their StyleValue was always
a special kind: FontStyleStyleValue or MathDepthStyleValue. This was
always true, because that's how we parse them, but it stops being true
once StylePropertyMap is involved: An author can set font-style to a
CSSKeywordValue of "italic", and this should work.
There are multiple ways that we could solve this, but the simplest and
easiest to maintain seems to be to handle those more basic StyleValues
in this computation code. Going forward, we'll have to be aware that
similar properties could have a basic StyleValue from the typed-OM
instead of the property-specific one we'd expect.
Whether an absbox is positioned below or to the right of its previous
sibling in an `InlineFormattingContext` is determined by the
display-outside value before blockification, so we store the
pre-blockification `display` value in `ComputedValues` to access it in
`InlineFormattingContext` and position the box accordingly.
Opacity values are unique in that the range which calculated and
interpolated values should be clamped to [0,1] is different from the
range of allowed values [-∞,∞].
This fixes 28 WPT tests that were regressed in #6112
We were unnecessarily computing property values within
`collect_animation_into` which we can avoid by ensuring that all calls
to `collect_animation_into` happen after property values are computed.
We now fail a few more tests in properties-value-inherit-001.txt as we
no longer overwrite the non-animated value of `line-height` with the
animated value, this is in line with other major browsers.
We were already doing this within `compute_property_values` where
we resolved most relative lengths but the remainder was instead
incorrectly using the font's line-spacing
Previously if we would overwrite the non-animated font-size with the
animated font-size if it was set.
Loses us 2 WPT tests but this is in line with other browsers
We now also more closely follow the spec when computing values for
font-weight and we now:
- Support relative lengths in `calc()`s
- Properly clamp `calc()`s
- Support relative keywords (e.g. lighter, bolder)
- Respect that font-weight can be a non-integer number.
This does expose a few false positives in the font-weight-computed.html
WPT test. This is because we don't recompute non-inherited font-weight
within `recompute_inherited_style` which means that relative keyword
values can fall out of sync with their parent's value. These previously
passed as we treated `bolder` and `lighter` as aliases for `bold` and
`normal` respectively.
Remaining test failures in font-size-interpolation-00* are either:
- Rounding of font-size to CSSPixels when setting the expected value
- Not clamping negative values from the point of view of
getComputedStyle (used values are still clamped)
Previously if we would overwrite the non-animated font-size with the
animated font-size if it was set.
Gains us 8 WPT tests and means we now fail 9 others in line with other
browsers.
There are other places we want to convert font-size into it's computed
form than just when we are loading the font (e.g. computing keyframes).
Gains us 36 WPT passes as we now correctly clamp negative calc values.
A new parameter was added to Web::CSS::StyleValue::to_string() in
PR #2820 but this debug message was never updated. If
CSS_TRANSITIONS_DEBUG was enabled, compilation would fail.
As noted, the chunk of this method that deals with animations could do
with some helpers on AbstractElement, but I'm leaving that until it's
clearer how animations and pseudo-elements should interact.
Transitions apply after logical properties are mapped to their physical
counterparts so we should apply this mapping to `transition` properties
Gains us 20 WPT tests.
Using longhands rather than expanded longhands meant for instance that
we wouldn't transition `border-top-width` if we had `border` as the
transition property.
Continues the work started in #5386 to simplify handling of positional
value list shorthand properties.
Previously we would parse these as `StyleValueList`s and then rely on
`StyleComputer::for_each_property_expanding_shorthands` to expand them
into longhands.
This required a bit of work to handle `ShorthandStyleValue`s for the
`@page` `margin` descriptor.
Implements `::slotted()` to enough extent we could pass the imported WPT
test and make substantial layout correctness improvement on
https://www.rottentomatoes.com/