We add a new formatting context that simply runs layout for an
anonymous block formatting context within it. This allows replaced
elements to contain children, if the parent rewrites inline-flow to
inline-block.
Each NodeWithStyle is assigned a sequential layout index during the
pre-layout tree traversal. LayoutState stores UsedValues in a
PagedStore — a two-level page table indexed by layout_index that
gives O(1) lookup via two array accesses, with pages allocated
lazily on first write. UsedValues are stored directly in pages
(Optional<T>) rather than behind heap pointers, eliminating
per-entry malloc/free calls and improving cache locality.
This cuts ensure_used_values_for() from ~14% to ~7% in profiles
on https://www.nyan.cat/.
Cache the result of the body element check as a bool set once during
NodeWithStyle construction, instead of calling document().body() (which
walks the children of <html>) on every call. This is called from
PaintableBox::paint_background() for every box on every frame.
This was 0.9% of CPU time while playing a YouTube video.
CSS allows inline elements with `position: relative` (or other
containing-block-establishing properties) to serve as the containing
block for their absolutely positioned descendants. However, our layout
system stores containing blocks as `Box*`, which cannot represent
inline elements (they are `InlineNode`, not `Box`).
This patch adds a workaround: when computing containing blocks, we
also check if there's an inline element between the abspos element
and its Box containing block that should actually be the CSS
containing block. If found, we store it in a new member called
`m_inline_containing_block_if_applicable` and use it during abspos
layout to:
1. Compute the inline's fragment bounding box as the containing
block rectangle (including padding, per CSS spec)
2. Resolve percentage-based insets against the inline's dimensions
3. Position the abspos element relative to the inline's location
Some details to be aware of:
- The inline containing block search happens in the function
`recompute_containing_block()` by walking DOM ancestors (not layout
tree ancestors, since the layout tree restructures blocks inside
inlines as siblings)
- For pseudo-elements like `::after`, we start the search from the
generating element itself, since it may be the inline containing
block
- Fragment offsets are relative to their block container, so we
translate the computed rect to the abspos element's containing
block coordinate system by accumulating offsets up the ancestor
chain
- When the abspos element uses static position (auto insets), we
don't apply the inline rect translation since static position is
already computed in the correct coordinate system
Long term, we want to refactor our "containing block" concept to
map more cleanly to the spec concept. That means turning it into
a rectangle instead of the box this rectangle was derived from.
That's an invasive change for another day though.
Previously we only allowed the viewport itself to be the containing
block for fixed-position elements, but the specs give us a few other
situations. Many of these are the same as for absolute-positioned ones.
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.
The style propagation logic in `NodeWithStyle::apply_style()`
was incomplete for anonymous nodes created during layout
(e.g., within `wrap_in_button_layout_tree_if_needed`).
1. **Non-inherited CSS values** were not being propagated to the
anonymous wrappers.
2. Propagation did not recurse into **nested anonymous wrappers**
(descendants).
This fix adds calls to `propagate_non_inherit_values(child)` and
`child.propagate_style_to_anonymous_wrappers()`, ensuring all computed
styles reach the entire anonymous wrapper hierarchy.
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.
Gains us 112 new passes since we now interpolate keywords (thick, thin,
etc) correctly.
Also loses us 4 WPT tests as we longer clamp negative values produced by
interpolation from the point of view of getComputedStyle (although the
'used' value is still clamped).
All fragments inside an atomic inline box should stay within that box,
otherwise we'll screw up the paint order and paint them behind things
that they're supposed to be on top of.
This fixes an issue with inline-block content not appearing on sites
like Google Docs and Reddit, among others.
PaintContext dates back to a time when display lists didn't exist and it
truly represented "paint context". Renaming it to better align with its
current role.
We now cache the containing block (box) once at the start of layout,
which allows Layout::Node::containing_block() to return instantly
instead of doing tree traversal.
Removes a 0.7% profile item on Speedometer 3.
This reduces the number of `.cpp` files that need to be recompiled when
one of the below header files changes as follows:
CSS/ComputedProperties.h: 1113 -> 49
CSS/ComputedValues.h: 1120 -> 209
Having one big `if` to determine whether or not we should restructure an
inline layout node became a bit unwieldy. This extracts the logic into a
separate method.
Our layout tree requires that all containers either have inline or
non-inline children. In order to support the layout of non-inline
elements inside inline elements, we need to do a bit of tree
restructuring. It effectively simulates temporarily closing all inline
nodes, appending the block element, and resumes appending to the last
open inline node.
The acid1.txt expectation needed to be updated to reflect the fact that
we now hoist its <p> elements out of the inline <form> they were in.
Visually, the before and after situations for acid1.html are identical.
Instead just update the existing wrapper with computed values from the
table box, to insure that upside-down "inheritance" works as expected.
This allows table fixup to run on partially updated layout trees without
adding a new layer of unnecessary wrappers every time.
This improves the quality of our font rendering, especially when
animations are involved. Relevant changes:
* Skia fonts have their subpixel flag set, which means that individual
glyphs are rendered at subpixel offsets causing glyph runs as a
whole to look better.
* Fragment offsets are no longer rounded to whole device pixels, and
instead the floating point offset is kept. This allows us to pass
through the floating point baseline position all the way to the Skia
calls, which already expected that to be a float position.
The `scrollable-contains-table.html` ref test needed different table
headings since they would slightly inflate the column size in the test
file, but not the reference.
Resulting in a massive rename across almost everywhere! Alongside the
namespace change, we now have the following names:
* JS::NonnullGCPtr -> GC::Ref
* JS::GCPtr -> GC::Ptr
* JS::HeapFunction -> GC::Function
* JS::CellImpl -> GC::Cell
* JS::Handle -> GC::Root
Layout nodes now only carry CSS computer values with them. The main
idea here is to give them only what they need to perform layout, and
leave the rest back in the DOM.