Keep one per-TextNode chunk list so repeated inline layouts can skip
segmenting text and resolving fonts for each chunk. The cache entry is
keyed by the wrapping mode, whitespace behavior, word-break value, and
font cascade used to produce it.
Store rendered text, segmenters, and chunks behind one text-dependent
cache key. This keeps cached Utf16Views and segmenter state anchored to
current text when inherited text style changes without rebuilding the
layout tree.
Keep the cached font cascade alive while chunks are cached so a later
font-list allocation cannot compare equal by reusing the same address.
Add innerText coverage for style-only text-transform and white-space
changes, since that path constructs ChunkIterator directly.
Keep animated ImageStyleValue frame advancement owned by the
style value. The current frame and loop state live there, so a
separate document scheduler would duplicate ownership of that state.
Start the ImageStyleValue timer only while it has layout clients.
Stop it when the last client unregisters, or when a finite animation
completes. Expose a document-scoped active timer count through
internals for focused regression tests.
Clear image observers when layout nodes detach. Use current-node
cleanup for per-DOM-node clearing, and explicit subtree cleanup for
tree replacement, full tree teardown, and synthetic pseudo-elements.
This keeps large document clearing linear.
Unregister generated-content image providers during layout detach
instead of waiting for GC to finalize the provider.
Cover hidden animated background images, generated content images,
layout node replacement, full layout tree teardown, and document
scoping for the internals counter.
Store containing line fragment coordinates only for atomic inline boxes.
Inline list items are inline-level boxes, but they are not atomic
inline boxes, so commit must not treat their fragments as atomic
positioning data.
The saved coordinate is a cache into mutable line boxes. Line cleanup
can remove fragments after the coordinate is recorded, so fall back to
the box's stored offset instead of indexing past the line.
Add crash coverage with inline list items and an inline table child.
Count table-column descendants from the layout tree when forming a table
grid. Generated table-column-group boxes can be layout-only and do not
have a DOM node, so walking through the DOM subtree can crash during the
table fixup pass.
Add a crash test covering a generated table-column-group pseudo-element.
Store the static-position insertion point produced by an inline
formatting context and let block layout consume it for originally-inline
absolute boxes that follow an anonymous inline wrapper. This removes the
block formatting context's fragment-walking reconstruction of the same
position.
Keep the calculation in IFC aware of forced breaks, aligned inline
content, trailing inline margins, wrapped lines, RTL inline content, and
vertical writing modes.
Keep regression coverage for the static-position cases covered while
building this model.
When an inline flex fragment raises the line box baseline, include the
parent strut descent in the line box bottom and advance the next line
from that enlarged line box. This keeps following wrapped or broken
lines from overlapping the extra descent.
Add ref and text coverage for inline-flex line height and line
advancement.
Button layout wraps non-flex/grid button contents in an anonymous flex
item. That flex item kept the default automatic minimum height, so
a descendant with height: 100% and width: auto could make a fixed-height
button grow to the descendant's intrinsic height. This made image-only
controls, such as site header logos, render much taller than the height
specified by author styles.
Set min-height: 0 on the anonymous button content box so
percentage-height descendants can resolve against and shrink to the
button's used height.
Problem: An absolutely-positioned element with “clip: rect(...)” stays
visible when “display: table” is also applied to it.
Cause: “display: table” elements generate an anonymous table-wrapper
box — and the “position” property is used on that wrapper rather than on
the table box itself. PaintableBox::get_clip_rect() only applies “clip”
to absolutely-positioned boxes — so it checks the table box, finds it’s
not absolutely positioned, and never applies the clip.
Fix: Transfer the “clip” property from the table box to the table-
wrapper box. That wrapper is the absolutely-positioned box — so
get_clip_rect() applies the clip correctly.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/9349
This is used whereever we use `unsafe_layout_node`. There's no
difference in behavior for `SyntheticPseudoElement` but once we
implement element-reference pseudo-elements it will call
`unsafe_layout_node` on the referenced element rather than `layout_node`
Most of this functionality was already implicitly disallowed for
element-reference pseudo-elements by the fact that we weren't creating
entries in `m_pseudo_element_data` for them, but we need to explicitly
limit it in preparation of creating those entries.
Due to the above this is mostly non-functional apart from a regression
where we no longer support custom properties on element-reference
pseudo-elements. Previously when setting custom properties for an
element-reference pseudo-element we would call `ensure_pseudo_element()`
which created a synthetic pseudo-element entry distinct from the
referenced element which only stored custom property data - this was
clearly wrong and will be implemented properly in a future commit.
Use the text-control auto content size when calculating max-content
width for primitive text input widgets. CSS Sizing treats cyclic
percentage preferred sizes as initial values for non-replaced
max-content contributions, so an `appearance: none` input with
`width: 100%` should contribute its `width: auto` size instead of
collapsing to zero.
Keep the min-content path unchanged so the percentage-sized replaced
element rule can still compress non-button-like input controls for
flex automatic minimum sizing. Add coverage for an inline-flex
control matching the GitHub file search layout.
Let centered grid content alignment use signed free space when grid
tracks overflow the grid container content box. CSS Align defaults this
case to unsafe overflow alignment, while distributed alignment values
keep their safe fallback behavior.
Add a text test covering an oversized centered grid inside a padded
grid container.
We now apply first letter styles by splitting text with a first-letter
style applied into 2 `TextSliceNode` objects. The
`DOM::Text` layout node always points at the non first-letter slice
and the first-letter slice is reachable via
`TextSliceNode::first_letter_slice()`.
First letter splitting works by `TreeBuilder` walking a block
container's inline descendants to find the first typographic letter
unit per the pattern given in css-pseudo level 4, which is then
wrapped in an anonymous inline box styled with the `::first-letter`
computed properties.
Consumers that map between DOM offsets and layout geometry
are updated to visit all slices of a `DOM::Text` through
`TextOffsetMapping`.
Add an `AttachToDOMNode` parameter to the Layout::Node constructor that
controls whether the newly-constructed node calls `set_layout_node()`
on its backing DOM node.
Convert table width, min-width, and max-width constraints into the
table content-width coordinate space before comparing them with GRIDMIN,
GRIDMAX, or a stretched grid-area width. Length and percentage
constraints honor box-sizing, while intrinsic constraints map to the
table GRIDMIN, GRIDMAX, or fit-content width instead of going through
to_px.
This also feeds stretched table wrapper widths back into auto-width
table layout so the inner table remains consistent with its wrapper.
Measure anonymous table wrapper grid items against their grid-area
containing block instead of the layout-tree containing block. Recompute
the wrapper width after track spacing and store the grid-area size so
table layout resolves table-root percentages against the same basis.
Move grid placement, self-alignment, and order from a display:table
root to its anonymous table wrapper. The wrapper is the box grid layout
places and aligns, so these grid item properties need to live there
instead of on the inner table box.
Track the resolved row-axis size used for auto-height grid layout and
use it when resolving row gaps and align-content offsets. This keeps
percentage row gaps and row alignment based on the used grid height
after min/max-height or parent stretch constraints are applied.
Add text coverage for auto-height row gaps and align-content cases.
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()`.
Treat non-replaced grid item percentage preferred sizes as auto when
computing intrinsic grid track contributions. This matches the CSS
Sizing cyclic percentage contribution rule and avoids inflating auto
rows before the final grid area size is known.
Keep resolving those percentages during final item layout. Update the
new layout tests to cover row inflation and nested overlap.
When a replaced element has no natural width or height but has a
preferred aspect ratio, max-content width fell through to the CSS
Sizing fallback that uses the initial containing block width.
Use a definite used height to derive max-content width through the
aspect ratio before the fallback. Keep the existing natural-height SVG
path first by only doing this for elements without natural height. Add
a focused layout test for the nested flex item case.
Only add extra end padding to the scrollable overflow rectangle along
axes where content actually overflows the content box. For positive
overflow directions, require content to cross the positive content edge
so start-side negative overflow does not create a fake scroll range.
Add coverage for orthogonal padding, clipped child overflow, and
negative start-side overflow that must not make a box scrollable.
A used height computed from an intrinsic sizing keyword like fit-content
does not necessarily establish a definite containing-block height for
percentage descendants.
Preserve indefinite percentage-height resolution both in normal block
layout and in absolutely positioned/fixed layout, including cases where
non-auto top/bottom insets resolve the box’s own used height.
Add regressions for a regular fit-content block container and fixed
fit-content containers with min-height and top/bottom insets.
When an outer <svg> has a viewBox and only one sizing attribute, SVG
Integration keeps the missing dimension as auto while exposing the
specified dimension and intrinsic aspect ratio to CSS sizing.
Teach replaced max-content sizing to resolve that SVG-specific partial
intrinsic size through CSS2 replaced sizing before falling back to the
CSS Sizing rules for replaced elements without natural sizes. This keeps
the generic fallback path intact while avoiding viewport-sized
max-content contributions for SVGs such as height+viewBox logos.
In combination with the previous commit, fixes the layout of logos
on polar.sh
CSS Sizing defines the max-content size of a replaced element without
natural dimensions in terms of the size it would have as a float with an
auto preferred size.
Change max-content width/height calculation to use those fallback rules
for replaced elements that lack a natural size in the queried axis.
Reduced from bad layout of the logo section on polar.sh
An auto margin on a grid item absorbs positive free space prior to
alignment via the box alignment properties, disabling the effects
of any self-alignment in that axis. We were applying both
auto-margin absorption and alignment additively, which doubled the
offset and pushed the item outside its grid area when, for example,
`margin-top: auto` was combined with `align-items: center`.
Grid item intrinsic size contributions only honored maximum sizes
written as plain lengths. Definite calculated sizes should clamp those
contributions as well. Otherwise, an item with a larger preferred width
can inflate a flexible grid track before its used size is later capped.
This made Reddit's main page grid compute a 1120px content column inside
a 1280px viewport, leaving the capped content centered in an oversized
track and making the page horizontally scrollable.
Flexible grid tracks with fixed minimum sizing functions should not grow
from min-content constraints. A nested grid using minmax(0, 1fr)
could otherwise bubble aspect-ratio overflow up into an ancestor auto
track, making Shopify horizontally scrollable.
Keep those tracks at their fixed minimum during min-content sizing so
the overflow remains inside the intended scroll container. Add a
test covering the nested grid shape from Shopify's homepage.
Store overflow-clip-margin as a structured style value instead of
collapsing it into generic keyword/length lists during parsing.
Keep omitted visual-box as a distinct state so painting can apply
the spec default per element type (content-box for replaced,
padding-box otherwise), while preserving canonical serialization.
Clear NodeWithStyle image observers during finalization so pending image
loads cannot call back into observers owned by unreachable layout nodes.
Incremental sweep leaves finalized cells allocated until their block is
swept, so waiting for the C++ destructor is too late.
DecodedImageFrame only wraps a ref-counted Bitmap and color-space
metadata. The frame object itself does not provide shared mutable
state or lifetime ownership beyond those members, so ref-counting it
adds an unnecessary layer of indirection.
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.
Inline paintables are sized by walking their paintable subtree. The
traversal already ignored `BlockContainer` paintables, but it still
visited their children, so an inline element could include internal
fragments from an inline-block child when computing its own geometry.
We now skip the entire `BlockContainer` subtree, so widths are not
double counted and offsets are not pulled from a different containing
block.
DecodedImageFrame now owns decoded bitmap pixels directly, so the
separate ImmutableBitmap wrapper no longer carries useful semantics.
Remove the class and pass decoded image frames or bitmaps at the
boundaries where pixels are actually required.
The Skia image cache now keys off DecodedImageFrame, matching the
display-list commands that paint decoded images. Video frames stay
owned by LibMedia, with the explicit YUV-to-bitmap conversion living
at HTMLVideoElement's decoded-frame entry point for canvas and WebGL
callers.
Decoded image data should not continue to traffic in ImmutableBitmap now
that the bitmap wrapper is being retired. Introduce DecodedImageFrame as
the paintable decoded-image unit and store a Bitmap plus ColorSpace in
it directly.
Thread the new frame type through decoded image data, display-list
image commands, filters, canvas drawImage, patterns, WebGL texture
upload, and CSS/SVG image consumers. ImmutableBitmap remains only at
the legacy boundaries that still need it, such as HTML video snapshots
and callers that explicitly ask for a bitmap snapshot.
This keeps color-space ownership with the decoded frame while making
the expensive or legacy ImmutableBitmap path explicit at the few call
sites that still need it.
When an element stops generating a layout node, subtree cleanup
does not reach its shadow descendants.
Walk the shadow-including subtree instead, so IntersectionObserver
cannot later query geometry through stale paintables.
When a slot gets content-visibility: hidden, the existing subtree
cleanup does not reach assigned slottables, since they are not DOM
descendants of the slot.
Clear layout and paint nodes from those assigned subtrees as well,
so IntersectionObserver cannot later query geometry through stale
paintables.