Commit graph

988 commits

Author SHA1 Message Date
Andreas Kling
c90b79a282 LibWeb: Cache TextNode chunks between layouts
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.
2026-05-24 23:41:57 +02:00
Andreas Kling
17902b02ab LibWeb: Stop idle animated CSS image timers
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.
2026-05-23 11:36:53 +02:00
Andreas Kling
ccd386d7aa LibWeb: Guard inline fragment lookups during layout commit
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.
2026-05-23 11:36:45 +02:00
Andreas Kling
e112514681 LibWeb: Walk layout tree for table column groups
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.
2026-05-23 11:36:45 +02:00
Andreas Kling
d98cea80e2 LibWeb: Let IFC publish inline-end static position
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.
2026-05-23 11:01:58 +02:00
Andreas Kling
d05b30a8dc LibWeb: Preserve inline-flex strut descent
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.
2026-05-23 11:01:58 +02:00
Aliaksandr Kalenik
640965d052 LibWeb: Allow button content boxes to shrink below intrinsic height
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.
2026-05-21 19:34:45 +01:00
sideshowbarker
e4b05ccde7 LibWeb: Apply the CSS clip property to display:table elements
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
2026-05-21 17:06:35 +02:00
Callum Law
0f92554427 LibWeb: Introduce Element::pseudo_element_unsafe_layout_node
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`
2026-05-21 14:26:22 +01:00
Callum Law
28afdb327a LibWeb: Limit some pseudo-element functionality to synthetic only
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.
2026-05-21 14:26:22 +01:00
Callum Law
468c696ec7 LibWeb: Combine pseudo element clearing methods
These two methods were the same just with slightly different names and
badges so let's combine them.
2026-05-21 14:26:22 +01:00
Andreas Kling
ec29db716c LibWeb: Size appearance-none text inputs for max-content
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.
2026-05-20 22:31:38 +02:00
Andreas Kling
0304b8d76b LibWeb: Preserve overflowing grid content center alignment
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.
2026-05-20 21:09:32 +02:00
Tim Ledbetter
b67d73a661 LibWeb: Apply ::first-letter pseudo-element styles
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`.
2026-05-20 12:09:19 +01:00
Tim Ledbetter
ca7a09d0ef LibWeb: Track PaintableFragment offsets relative to the owning DOM::Text 2026-05-20 12:09:19 +01:00
Tim Ledbetter
d69c3c26b1 LibWeb: Make TextNode subclassable 2026-05-20 12:09:19 +01:00
Tim Ledbetter
785f2881f9 LibWeb: Add AttachToDOMNode parameter to Layout::Node constructor
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.
2026-05-20 12:09:19 +01:00
Callum Law
a6f24e91cc LibWeb: Simplify is_definite_size 2026-05-20 09:47:17 +01:00
Callum Law
c19a4882a8 LibWeb: Don't store transition-delay on ComputedValues
This is never used (since transitions are applied during style
computation) so is not needed.
2026-05-20 09:47:17 +01:00
Andreas Kling
09a2c6b153 LibWeb: Resolve table width constraints as content widths
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.
2026-05-19 10:41:52 +02:00
Andreas Kling
4cdad196ed LibWeb: Resolve table wrappers from grid areas
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.
2026-05-19 10:41:52 +02:00
Andreas Kling
9d8d0883d6 LibWeb: Move table grid item properties to wrappers
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.
2026-05-19 10:41:52 +02:00
Andreas Kling
90218c8dd6 LibWeb: Align auto-height grid rows with used height
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.
2026-05-19 10:41:52 +02:00
Tim Ledbetter
52e082c01f LibWeb: Skip style update inside display:none subtrees
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()`.
2026-05-18 11:31:26 +02:00
Andreas Kling
a0ba84975b LibWeb: Treat cyclic grid percentages as auto
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.
2026-05-18 08:20:28 +02:00
Tim Ledbetter
e27260450e LibWeb: Clamp the size of the implicit grid to the range [-10000, 10000]
The grid specification allows UAs to clamp the possible size of the
implicit grid to a UA defined value that must be in the range
[-10000, 10000].
2026-05-17 21:26:57 +02:00
Andreas Kling
6fcdd74b38 LibWeb: Resolve SVG max-content width from CSS height
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.
2026-05-17 15:20:36 +02:00
Andreas Kling
1eac2fc088 LibWeb: Add scrollable overflow padding per axis
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.
2026-05-17 14:19:36 +02:00
Shannon Booth
7afb4ca328 LibWeb: Place single-line flex lines at cross-start
For single-line flex containers, the flex line should not be centered in
the container’s cross axis. Instead, it should be placed at cross-start.
2026-05-17 10:15:38 +02:00
Shannon Booth
825a37cc8e LibWeb: Keep intrinsic heights indefinite for percentage children
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.
2026-05-16 17:06:03 +02:00
Shannon Booth
cb1b5aa46b LibWeb: Account for SVG partial intrinsic size in max-content sizing
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
2026-05-16 17:05:01 +02:00
Shannon Booth
296b561b98 LibWeb: Handle replaced min/max-content sizes without natural dimensions
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
2026-05-16 17:05:01 +02:00
Psychpsyo
8ee99a9411 LibWeb/Painting: Only paint left/right borders of inlines when we should 2026-05-15 13:26:24 +01:00
Pavel Shliak
166c02089d LibWeb: Preserve column indices across colgroups
Keep the collected column index across table-column-group siblings, so
columns in later colgroups are not recorded as column 0.
2026-05-14 12:41:54 +01:00
Pavel Shliak
f50165dcde LibWeb: Correct right colgroup border lookup
When collecting the left edge of the column group to the right, look up
the column at column_index + column_span.
2026-05-14 12:41:54 +01:00
Tim Ledbetter
4f33ab15e0 LibWeb: Don't apply self-alignment to grid items with auto margins
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`.
2026-05-14 13:29:40 +02:00
Andreas Kling
3f1be9d93c LibWeb: Clamp grid item contributions by calculated max size
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.
2026-05-13 20:54:10 +02:00
Andreas Kling
cc381d2dc8 LibWeb: Avoid inflating grid min-content flex tracks
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.
2026-05-13 20:54:10 +02:00
mikiubo
b9ed64d1ce LibWeb: Add dedicated overflow-clip-margin style value
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.
2026-05-12 14:09:13 +02:00
Andreas Kling
b95b3ecd6d LibWeb: Unregister image observers when finalizing layout nodes
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.
2026-05-10 10:58:11 +02:00
Sam Atkins
d7344a96bb LibWeb: Parse and propagate container and container-name properties 2026-05-07 17:48:53 +02:00
Aliaksandr Kalenik
f8640d813a LibGfx+LibWeb: Make DecodedImageFrame a value type
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.
2026-05-07 16:08:13 +02:00
Aliaksandr Kalenik
568b7ce7ea LibWeb: Make Paintable tree ref-counted
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.
2026-05-07 15:03:44 +02:00
Tim Ledbetter
cee28a2af5 LibWeb: Skip BlockContainer children when sizing inline paintables
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.
2026-05-07 12:01:04 +02:00
Callum Law
23e5a4ed64 LibWeb: Always apply CSS stroke/fill property changes
Previously changes to `none` from any other value would be ignored. This
also moves handling into `ComputedProperties` in line with other
properties.
2026-05-07 10:07:09 +01:00
Aliaksandr Kalenik
76c79ee522 LibGfx: Remove ImmutableBitmap
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.
2026-05-05 14:39:17 -05:00
Aliaksandr Kalenik
40f2abb7fe LibGfx+LibWeb: Add DecodedImageFrame
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.
2026-05-05 14:39:17 -05:00
Tim Ledbetter
78b47109af LibWeb: Use the ASCII line segmenter for eligible text 2026-05-05 18:59:07 +02:00
Aliaksandr Kalenik
63c8f6761b LibWeb: Clear stale layout state for shadow descendants
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.
2026-05-04 11:57:33 +02:00
Aliaksandr Kalenik
8c55c705d5 LibWeb: Clear stale layout state for hidden slot slottables
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.
2026-05-04 11:57:33 +02:00