This requires us to front load computation of writing-mode and direction
before we encounter any logical aliases or their physical counterparts
so that we can create a mapping context.
Doing this at compute rather than cascade time achieves a few things:
1) Brings us into line with the spec
2) Avoids the double cascade that was previously required to compute
mapping contexts
3) We now compute values of logical aliases, while
`style_value_for_computed_property` maps logical aliases to their
physical counterparts, this didn't account for all cases (i.e. if
there was no layout node, Typed OM, etc).
4) Removes a hurdle to moving other upstream processes (i.e. arbitrary
substitution function resolution, custom property computation) to
compute time as the spec requires.
The hover invalidation code only tried matching ::before/::after
selectors when has_pseudo_element() returned true, which requires an
existing layout node. A pseudo-element that doesn't exist yet (because
its content is only set by a hover rule) has no layout node, so the
match was skipped and hovering never triggered a style recompute.
Always try ::before/::after selectors during hover invalidation.
We no longer try to resolve calculated values at parse time which means
we support relative lengths.
We now clamp negative values rather than rejecting them at parse time.
Parsing has been inlined into `parse_ratio_value` and `parse_ratio` has
been removed since `parse_ratio_value` was the only caller
Avoid comparing exact matrix3d values which contain floating point
noise that could differ across platforms. Instead, check whether the
transform is or isn't the identity matrix.
When interpolating between rotateY(0deg) and rotateY(3600deg), the
angle should be interpolated numerically. Currently we use quaternion
slerp which sees these as identical rotations (3600 mod 360 = 0),
producing no visual animation. A following commit will fix this.
The CSS spec says animation-timing-function is applied per keyframe
interval, not as an overall effect-level timing function. Currently we
apply it globally, causing wrong intermediate values and ignoring
per-keyframe animation-timing-function declarations in @keyframes
rules. A following commit will fix this.
When @keyframes rules are defined in a <style> element inside a shadow
root, animations referencing them should work. Currently the animation
is created but has no keyframes, producing "none" for animated
transform values. A following commit will fix this.
When a transform animation involves a non-invertible matrix (e.g. a
matrix3d with zero Z-scale), the spec requires falling back to discrete
interpolation. Currently we drop the transform entirely, producing
"none" at all progress values. A following commit will fix this.
This test shows that var() fallback values with trailing whitespace
(e.g. `var(--foo, flex )`) fail to parse correctly for the display
property, falling back to initial values instead.
When a CSS animation keyframe uses var() referencing a nonexistent or
invalid custom property, variable substitution produces a
guaranteed-invalid value. The animation keyframe processing code did not
handle this case, allowing the value to reach compute_opacity() (and
similar functions) which would hit VERIFY_NOT_REACHED().
Fix this by skipping guaranteed-invalid values in
compute_keyframe_values, matching how the regular cascading code treats
them.
This fixes a crash on chess.com.
See previous commit for details
We now support parsing of `display-p3-linear` (although it just falls
back to using Oklab since Skia doesn't support it)
Replace flat InvalidationSet with recursive InvalidationPlan trees
that preserve selector combinator structure. Previously, selectors
with sibling combinators (+ and ~) fell back to whole-subtree
invalidation. Now the StyleInvalidator walks the DOM following
combinator-specific rules, so ".a + .b" only invalidates the
adjacent sibling matching ".b" rather than the entire subtree.
Plans are compiled at stylesheet parse time by walking selector
compounds right-to-left. For ".a .b + .c":
```
[.c]: plan = { invalidate_self }
register: "c" → plan
[.b]: wrap("+", righthand)
plan = { sibling_rules: [match ".c", adjacent, {self}] }
register: "b" → plan
[.a]: wrap(" ", righthand)
plan = { descendant_rules: [match ".b", <sibling plan>] }
register: "a" → plan
```
Changing class "a" produces a plan that walks descendants for ".b",
checks ".b"'s adjacent sibling for ".c", and invalidates only that
element.
The style of an element depends on it's navigable's viewport size which
in turn depends on the navigable's container's style - so if requires a
style update then so does the original element.
When CSSRule.cssText is accessed, shorthands are recomposed from
individual longhand declarations. For coordinating-list shorthands, the
`serialize()` function always assumed that each sub-property was a
value list. This caused a crash for longhands containing `var()`. We
now fall back to serializing properties individually if any sub
property contains `var()`. This matches the behavior of other engines.
The selector matching code bypassed the pseudo-element type check for
`::part()` selectors to support compound selectors like
`::part(foo)::before`. This caused bare ::part() declarations to leak
into unrelated pseudo-elements like ::selection.
Fix this by finding any additional pseudo-element beyond ::part() in the
selector and verifying it matches the target.
The grammar groups this component together meaning that all
sub-components must occur together i.e.
`ordinal slashed-zero small-caps` is valid but
`ordinal small-caps slashed-zero` is not.
We also reuse the logic for parsing from the longhand
`font-variant-numeric` property for simplicity.
The grammar groups this component together meaning that all
sub-components must occur together i.e.
`common-ligatures no-contextual small-caps` is valid but
`common-ligatures small-caps no-contextual` is not.
We also reuse the logic for parsing from the longhand
`font-variant-ligatures` property for simplicity.
The grammar groups this component together meaning that all
sub-components must occur together i.e. `jis78 full-width small-caps` is
valid but `jis78 small-caps full-width` is not.
We also reuse the logic for parsing from the longhand
`font-variant-east-asian` property for simplicity.
Previously we didn't clear the computation context caches after:
- Recomputing inherited style
- Computing keyframe values
We now clear the caches in those two cases and verify it has been
cleared before using it.
Fixes#7959
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.
Test that slotted elements correctly inherit styles from their
assigned slot in various scenarios: inline styles, CSS rules,
named slots, nested shadow hosts, own style overrides, deep
inheritance chains, dynamic slot reassignment, JS-created shadow
DOM, class toggles on slots, and moving elements between hosts.
Two issues prevented slotted elements from correctly inheriting
styles from their assigned slot:
1. Element::element_to_inherit_style_from() was skipping the slot
element and returning the shadow host instead. This meant slotted
elements inherited from the host, completely ignoring any styles
on the slot itself.
2. When a slot element's style changed during the style tree walk,
its assigned (slotted) nodes were never marked for recomputation.
The tree walk follows the DOM tree, but slotted elements are DOM
children of the shadow host, not the slot, so they were missed.
Fix (1) by returning the slot directly as the inheritance parent.
Fix (2) by marking assigned nodes dirty in update_style_recursively
when a slot's style changes.
Cover various scenarios: element's own style change, ancestor style
change affecting inheritance, sibling changes not affecting our element,
shadow DOM elements, pseudo-elements, and repeated reads.
Also documents a pre-existing bug where slotted elements don't pick up
inherited style changes from their assigned slot.
Test all major code paths in the CSS2 10.6.4 algorithm for computing
height of absolutely positioned non-replaced elements:
- Rules 4, 5, 6 (one auto among top/height/bottom)
- Over-constrained case (none auto)
- Auto margin solving (one auto, both auto)
- Min-height and max-height re-solve for rules 4, 5, 6
- Min/max with auto margins
- Borders and padding interaction with min-height
Per the CSSOM specification, throw a NotFoundError DOMException when
the specified medium is not found in the collection. Invalid input
that fails to parse continues to return silently per step 2.
Instead of defining somewhat high level mouse actions, allow granular
control of mouse clicks and mouse down/up/move events. We will want to
simulate things like holding down a mouse button after double clicking
and then dragging the mouse to another position in the future, and this
enables that.
Instead of doing a full document style invalidation when a stylesheet is
dynamically added, we now analyze the new sheet's selectors to determine
which elements could potentially be affected, and only invalidate those.
This works by building an InvalidationSet from the rightmost compound
selector (the "subject") of each rule in the new stylesheet, extracting
class, ID, tag name, attribute, and pseudo-class features. We then walk
the DOM tree and only mark elements matching those features as needing a
style update.
If any selector has a rightmost compound that is purely universal (no
identifying features), or uses a pseudo-class not supported by the
invalidation set matching logic, we fall back to full invalidation.
When a pseudo-class state changed, we always walked the entire
document (or shadow root) tree to find affected elements, even
though only the subtree rooted at the old/new common ancestor
can be affected.
Narrow the tree walk to start from old_new_common_ancestor
instead of the root. To ensure ancestor-dependent selectors are
still correctly evaluated, we seed the style computer's ancestor
filter by walking up from the common ancestor to the root before
the invalidation walk.
This reduces the work from O(total elements) to
O(subtree elements) + O(tree depth), which is a large improvement
on pages where pseudo-class changes (hover, focus, active, target)
occur deep in the DOM.
This was extremely hot (10%+) when hovering mailboxes on GMail.
When a parent element's display property changes (e.g., to flex or
grid), children may need to be blockified or un-blockified.
Previously, children only received a recompute_inherited_style() call
which doesn't run the blockification logic.
This patch adds a parent_display_changed flag to the recursive style
update that forces children to get a full style recompute when their
parent's display change triggers a layout tree rebuild.
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 would just set the attributes to the serialized
descriptors, even if they were the empty string.
We now apply defaults when we have empty descriptors and apply parsing
logic from the various `set_*` methods (only applicable to `font-family`
so far where we now extract the value from either a string or a
custom-ident)
Fixes an issue in some css/css-shapes WPT tests where we weren't
properly matching fonts.
Previously we would always try to load the font URL relative to the
document's base URL. This commit means that for CSS-connected
`FontFace`s we now try to load relative to the URL of the stylesheet
that contains the associated `CSSFontFaceRule`