Commit graph

134 commits

Author SHA1 Message Date
Jelle Raaijmakers
e0ca142184 LibWeb: Prevent flex/grid margin collapsing with its contents 2026-06-10 13:11:47 +02:00
Andreas Kling
565df0da77 LibWeb: Avoid min-content sizing for fitting floats
Measure a floating auto-width box's max-content width before falling
back to its min-content width in the definite available-width branch.
If max-content already fits, shrink-to-fit resolves to that value and
the min-content contribution cannot affect the used width.

Also avoid the paired shrink-to-fit intrinsic measurement when an
intrinsic sizing constraint only needs one side of the contribution.
2026-06-08 18:10:13 +02:00
Andreas Kling
9340d2d1a3 LibWeb: Make layout nodes refcounted
Move the layout tree from GC allocation to refcounted ownership so
removed layout and paint subtrees are destroyed synchronously instead
of waiting for the next GC sweep. This dramatically reduces GC memory
usage peaks after layout tree churn and makes it easier for memory use
to fall back after large document updates.

Update layout factories, tree traversal, SVG layout node creation,
paintable back-pointers, and pseudo-element layout links to use RefPtr
ownership.

Make display: contents follow the same shape as Blink and WebKit: the
element itself does not create a layout node, and its children are
flattened into the nearest layout parent. Wrap direct non-whitespace
text in an anonymous inline node when the boxless element contributes
inherited style to that text.

Use an internal inline wrapper for display: contents pseudo-elements
so generated content can still participate in layout, painting, hit
testing, and pseudo-element queries. Keep CSSOM reporting the computed
display value from the pseudo style, not the internal wrapper.

Remove the retained out-of-tree layout node list and its testing hook,
since the flattened model does not need a side owner for boxless
elements. Add coverage for inherited text style, dynamic insertion
order, pseudo-element hit testing, and computed style queries.
2026-06-07 20:52:49 +02:00
Andreas Kling
222c1f7044 LibWeb: Move CSS image loading state to Document
Move the SharedResourceRequest, animation timer, and current frame
state out of ImageStyleValue and into a Document-owned table keyed
by resolved image URL. ImageStyleValue now keeps only URL metadata
and its client list, so image style values no longer need to trace
GC edges themselves.

Thread the Document through AbstractImageStyleValue APIs that need
decoded image data. CSS image fetches snapshot the stylesheet base URL,
referrer behavior, and origin-clean state instead of retaining the
stylesheet.

Remember each client's registered resolved URL when unregistering. This
keeps a later document base change from leaving an animated image
resource alive.

Add text coverage for inline relative image base URLs, stylesheet
referrers, imported stylesheet origin-clean behavior, inline @import
initiator type, and unregistering an animated background image after a
base element change.
2026-06-06 23:29:48 +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
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
Psychpsyo
d74aa418f7 LibWeb: Position list marker to the right of right-to-left text 2026-04-30 16:20:10 +02:00
Tim Ledbetter
4919363a37 LibWeb: Move rarely-used UsedValues properties behind a lazy pointer
This reduces the size of the `UsedValues` struct from 424 to 176 bytes.
2026-04-30 12:30:16 +02:00
Aliaksandr Kalenik
cd6672eee0 LibWeb: Fix min-content collapsing to 0 with min-width: 0 descendant
During intrinsic sizing, compute_width() ran on block descendants with
an intrinsic-sizing available space. For a non-FC-establishing block
with auto width, used_width stayed auto, and the min-width clamp then
compared AvailableSize::min-content against min-width via operator<,
which always returns true when the left side is min-content. The clamp
fired with min-width: 0 and set content_width to 0 permanently.

Skip the min-width clamp when used_width is still auto, mirroring the
max-width clamp a few lines above which already no-ops via
to_px_or_zero. The real width is then set by the IntrinsicSizing branch
in layout_block_level_children.
2026-04-24 19:08:28 +02:00
Andreas Kling
9526dfbef3 LibWeb: Use fragment extents for orthogonal inline widths
Inline formatting contexts in vertical writing modes were measuring
intrinsic width from the line box width. That width still tracks the
line-height-sized horizontal span, so shrink-to-fit abspos sizing could
stay at 50px even when the text fragments only covered 25px.

Measure the physical horizontal extent from the line box fragments
instead, including the float-aware block formatting context path. This
makes orthogonal inline content report the correct intrinsic width.
2026-04-05 00:03:22 +02:00
Psychpsyo
269aaeea21 LibWeb: Move standalone SVG document layout handling out of BFC
I'm simplifying the BFC code in preparation for multicol layout.
2026-03-31 10:49:15 +02:00
Andreas Kling
8c6786d70b LibWeb: Ignore non-overlapping floats in intrinsic widths
BlockFormattingContext::greatest_child_width() tried to decide whether
a line overlapped a float by comparing a line-relative baseline
against float edges stored in containing-block coordinates. That also
used `||`, so later lines could keep inheriting float width even after
the float had ended.

Measure overlap using each line box's top and bottom edges in the same
coordinate space as the float margins instead. This fixes
shrink-to-fit and max-content sizing for inline children after short
floats.

Add left and right tests where a later forced-break line is longer
than the line next to the float.
2026-03-29 12:05:25 +02:00
Andreas Kling
2614790a93 LibWeb: Lower floats past opposite-side blockers
CSS2 requires moving a float downward until it fits beside existing
floats. We already handled same-side blockers, but opposite-side
blockers could still overlap, such as matching left and right 100%
floats.

Recompute same-side bookkeeping after lowering and measure
opposite-side intrusion in the current containing block's coordinate
space. Also account for the full inline space already occupied on the
current side after same-side stacking, so a float shifted next to an
earlier same-side float does not still overlap an opposite-side float.

Add tests covering inline and nested lowering, stale same-side line
state, staggered opposite-side stacks, and the left/right/left 600px
overlap case. Update the stacked clear-both expectation to match the
corrected placement.
2026-03-29 12:05:25 +02:00
Andreas Kling
d53ceee7c3 LibWeb: Fix clear property not clearing past all stacked floats
When multiple floats are stacked vertically (e.g. multiple width:100%
left floats), the float placement code clears current_boxes as each
new "float line" begins. This meant that clear_floating_boxes() only
saw the last stacked float, not all preceding ones.

Fix this by iterating all_boxes instead of current_boxes when computing
clearance. Also move set_content_y() before the FloatingBox creation so
the root-space rect stored in all_boxes has the correct Y position.
2026-03-29 12:05:25 +02:00
Psychpsyo
98e1774eff LibWeb: Avoid division by zero in multicol calculations the right way
This got changed a while back to avoid a divide-by-0. That change was
not following the spec at all and would've resulted in incorrect values
being used, so this is the spec-compliant way of resolving that.

I originally didn't add spec comments to this algorithm since it felt
weird, but I hope these will demonstrate that this should not be changed
on a whim until we can import the relevant WPT tests for this behavior.
2026-03-23 12:09:29 +01:00
Psychpsyo
ce36e1f7df LibWeb: Do not allow margins collapsing through IFCs
Co-authored-by: ChaseKnowlden <haroldknowlden@gmail.com>
2026-03-23 11:34:33 +01:00
Andreas Kling
cf3d9c1bae LibWeb: Fix static position of inline-level abspos elements in BFC
When an absolutely positioned element's display was inline-level
before blockification, and its preceding sibling is an anonymous
wrapper with inline children, the element's static position should
be at the same vertical position as that wrapper. This matches where
the element would have been placed if it were still inline.

Previously, the static position was always set to the current block
offset (after the preceding block), which placed the element below
the inline content instead of beside it. This caused absolutely
positioned ::after pseudo-elements (like dropdown chevrons) to appear
on the line below the text instead of at the same vertical position.
2026-03-21 21:42:44 -05:00
Jelle Raaijmakers
09be72f9e6 LibWeb: Calculate a <fieldset>'s client height correctly
We were not properly including the impact of the fieldset's legend in
the client height, which caused us to report values that were different
than those from other browsers.
2026-03-10 19:08:15 +00:00
Jelle Raaijmakers
8aef896b11 LibWeb: Rework <fieldset> and <legend> rendering
We were misaligning <fieldset>'s bounds, painting and <legend> layout in
many cases. This reworks both the layout and painting code to the point
where we render many cases practically identical to Firefox and Chrome.
2026-03-04 20:39:01 +01:00
Aliaksandr Kalenik
490377b76e LibWeb: Populate containing block chain in BFC min-height measurement
The throwaway BFC for min-height measurement may encounter abspos
elements whose CSS containing block is an ancestor above the subtree
root. Populate the entire containing block chain (not just the immediate
parent) so that ensure_used_values_for() finds pre-existing entries
instead of hitting the subtree root VERIFY.

Stop walking when the source state lacks an entry, which handles the
nested throwaway state case (e.g. min-height inside intrinsic sizing).
2026-03-03 22:37:38 +00:00
Aliaksandr Kalenik
8527da249e LibWeb: Use parent layout mode for BFC min-height measurement
The throwaway min-height measurement in layout_block_level_box was
using LayoutMode::IntrinsicSizing, which caused flex/grid formatting
contexts to hit their early-exit optimization and return zero content
height. This incorrectly forced min-height as the available height.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/8249
2026-03-03 22:37:38 +00:00
mikiubo
bee9d1e19e LibWeb: Add synthetic height for contenteditable elements
Contenteditable elements that would otherwise have zero height now get a
minimum height equal to their line-height.
This ensures they remain clickable and usable for text editing.

This fixes the WPT test:
contenteditable/synthetic-height.html
2026-03-03 16:20:10 +01:00
Aliaksandr Kalenik
edf1ca8f19 LibWeb: Restrict throwaway layout states to their subtree
Throwaway LayoutState instances used for intrinsic sizing should not
access nodes outside the laid-out subtree. Make this explicit by setting
a subtree root on each throwaway state, pre-populating only the
immediate containing block, and using try_get() for any ancestor
lookups — treating unavailable ancestors as indefinite rather than
silently populating them with incorrectly-resolved values.
2026-02-27 19:11:41 +01:00
Aliaksandr Kalenik
a7993e240b LibWeb: Skip BFC abspos static position in intrinsic sizing
Avoid calculating static positions for absolutely positioned boxes
during intrinsic sizing, where the positions are based on intermediate
state and will be recalculated during normal layout. This is consistent
with how all other formatting contexts (IFC, TFC, GFC, FFC) handle
abspos boxes during intrinsic sizing.
2026-02-27 15:03:59 +01:00
Aliaksandr Kalenik
0c2212813c LibWeb: Use IntrinsicSizing mode for min-height measurement in BFC
When measuring content height to compare against min-height, a
throwaway formatting context was created with m_layout_mode (the
parent's layout mode, which could be Normal). This is a measurement
operation and should use LayoutMode::IntrinsicSizing instead,
consistent with the similar measurement in FormattingContext.cpp.
2026-02-27 12:48:27 +01:00
Tim Ledbetter
245eb7d91d LibWeb: Use margin box for vertical overlap checks in float placement
Previously, a float with `padding-top` would overlap a preceding float
instead of being placed beside it. The vertical overlap check was
comparing the new float's content box Y,which includes the padding
offset, against preceding floats' margin box rects, causing the check
to incorrectly conclude the floats do not overlap.
2026-02-25 12:03:31 +01:00
Tim Ledbetter
79d5fdc871 LibWeb: Avoid division by zero in multi-column column count calculation 2026-02-22 15:07:06 +01:00
Aliaksandr Kalenik
36ca93908a LibWeb: Unify abspos containing block resolution across FCs
Introduce AbsposContainingBlockInfo and a virtual
resolve_abspos_containing_block_info() method on FormattingContext that
computes the containing block rect, per-axis positioning mode (static
position vs inset-from-rect), and optional grid alignment — all in one
place.

The base implementation handles the common case: padding-box rect of
the containing block, with inline containing block support. GFC
overrides it to return the grid area rect with alignment info.

This replaces the per-context abspos loops that BFC, FFC, TFC, and GFC
each had, with a shared layout_absolutely_positioned_children() +
layout_absolutely_positioned_element() pair. The offset computation is
simplified from ~40 lines of branching to a data-driven dispatch on the
axis mode, and GFC's ~130-line custom layout method is replaced by a
~30-line resolve override.
2026-02-14 22:50:24 +01:00
Aliaksandr Kalenik
901cc28272 LibWeb: Reduce recompilation impact of DOM/Document.h
Remove 11 heavy includes from Document.h that were only needed for
pointer/reference types (already forward-declared in Forward.h), and
extract the nested ViewportClient interface to a standalone header.

This reduces Document.h's recompilation cascade from ~1228 files to
~717 files (42% reduction). Headers like BrowsingContext.h that were
previously transitively included see even larger improvements (from
~1228 down to ~73 dependents).
2026-02-11 20:02:28 +01:00
Callum Law
5b55c5051b LibWeb: Use marker line-height as minimum size for list item
Builds on 01518ba by using the marker's line height as the minimum
height for a list item in the case that all children are shorter, not
just if there are no children.
2026-02-11 11:04:32 +01:00
Callum Law
fd74fa024f LibWeb: Layout list item marker before setting offset in block container
Before this change we would only account for the size of the list item
itself rather than it's marker which if the marker was larger than it's
associated list item could lead to markers overlapping
2026-02-11 11:04:32 +01:00
Aliaksandr Kalenik
b41ed92505 LibWeb: Remove Document.h include from Layout/Viewport.h
Move the inline dom_node() method to Viewport.cpp so the header no
longer needs the full Document definition. Add explicit includes to
files that relied on the transitive dependency.
2026-02-08 18:51:13 +01:00
Psychpsyo
8ede5010fa LibWeb: Move some collapsing margin handling to more sensible places 2026-02-07 12:21:16 +01:00
Psychpsyo
12862bea93 LibWeb: Unregister block container y-position update callback 2026-02-07 12:21:16 +01:00
Tim Ledbetter
04ec4c0256 LibWeb: Resolve horizontal box model metrics for table captions
Previously, horizontal padding, border, and margin were not being
resolved for table captions. This was visible in layout tests where
captions with padding showed 0 in the width metrics.
2026-02-03 14:47:51 +01:00
Jonathan Gamble
ec50525675 LibWeb/Layout: Don't inject natural size in prepare_for_replaced_layout
Instead, compute them on demand. This affects ReplacedBox and its
subclasses.

This commit is centered around a new Box::auto_content_box_size
method. It returns a SizeWithAspectRatio representing the natural
size of a replaced element, or the size derived from attributes
for text input and textarea. These values are used when the
corresponding axis is auto or indefinite.

Although introducing this API choke-point for sizing replaced and
replaced-like elements was the main goal, it's notable that layout
becomes more robust in the face of dynamic changes due to reduced
potential for stale size values (at the cost of extra calculations
and allocations).
2026-02-02 14:36:49 +00:00
Tim Ledbetter
cf208c8e5e LibWeb: Exclude UA internal shadow roots from percentage height quirk 2026-02-02 12:28:05 +00:00
Andreas Kling
73c9d1a606 LibWeb: Implement the body element fills the html element quirk
In quirks mode, the body element expands to fill its parent (the html
element) when height is auto, per the quirks spec section 3.7.

This quirk applies when:
- The document is in quirks mode
- The body element has height: auto
- The body is not absolutely/fixed positioned
- The body is not floated
- The body is not inline-level
2026-01-26 16:48:21 +01:00
Andreas Kling
640ec0b64e LibWeb: Fix percentage height resolution in quirks mode
The quirks mode percentage height calculation quirk was incorrectly
applied to anonymous boxes (like the internal flex wrapper inside
buttons), causing buttons to collapse to zero height.

Per the quirks spec, the percentage height quirk:
- Only applies to DOM elements, not anonymous boxes
- Does not apply to flex/grid items (they resolve against their
  container)
- Does not apply to table-related display types

This patch:
1. Excludes anonymous boxes and flex/grid items from the quirk in
   should_treat_height_as_auto()
2. Adds quirks mode percentage height walk-up in
   calculate_inner_height() for inline-level boxes
3. Removes the incorrect flex/grid container exclusion from
   BlockFormattingContext (the quirk applies to containers, not items)
2026-01-26 16:48:21 +01:00
Jelle Raaijmakers
01518ba6a6 LibWeb: Use list item's line-height to provide space for marker
Empty list items should still have a default height if a marker is
present.

Fixes #3762.
2026-01-26 16:41:42 +01:00
Jelle Raaijmakers
19882e1ed6 LibWeb: Check available space for float: right boxes
For `float: left` boxes, we would take the available space into account
and push boxes down as required. This applies that same logic to `float:
right` boxes, where we would previously only compare their offset from
the edge using `>= 0`, which was almost always true.

Fixes #4750.
2026-01-26 16:41:42 +01:00
Tim Ledbetter
631e73676e LibWeb: Resolve margins for block-level buttons with width:auto
Previously, buttons with `display: block` and `width: auto` would take
an early return path in compute_width() that set the content width to
fit-content but skipped all margin resolution. This meant `margin: auto`
would not center the button horizontally.
2026-01-26 10:00:17 +01:00
Andreas Kling
8078d53d4f LibWeb: Avoid intrinsic sizing of boxes that don't create new FC
When computing the width of a box with `width: auto` in an intrinsic
sizing context (min-content or max-content), we previously called
calculate_min/max_content_width() for every such box.

This was wasteful for boxes that don't establish their own formatting
context (e.g. plain divs in a BFC), since their intrinsic width is
simply the intrinsic width of their contents, which we're already
computing as part of the current layout pass.

By only computing intrinsic widths separately for boxes that actually
create a new formatting context (BFC, flex, grid, table, etc.), we
avoid creating redundant throwaway LayoutState objects and formatting
contexts.

For deeply nested structures with `width: max-content` on an ancestor,
this reduces the number of formatting contexts created from O(n) to
O(1), where n is the nesting depth.
2026-01-14 13:52:22 +01:00
Aliaksandr Kalenik
ccffc42d6a LibWeb: Add debug tracing for FormattingContext::run() calls
Add optional tracing that prints a tree visualization of formatting
context `run()` invocations. This is useful for debugging layout issues
where you need to understand the nesting and order of layout passes,
or why a box receives unexpected available space.

Example output:
```
├─ BFC <Viewport<#document>> run(definite(800) x definite(600))
│ ├─ BFC <BlockContainer<HTML>> run(definite(800) x indefinite)
│ │ ├─ IFC <BlockContainer(anonymous)> run(definite(800) x indefinite)
│ │ ├─ GFC <Box<DIV.grid>> run(definite(800) x indefinite)
│ │ │ ├─ BFC <BlockContainer<DIV.item>> run(definite(400) x indefinite)
```
2026-01-09 08:12:21 +01:00
Aliaksandr Kalenik
bdf2dbff98 LibWeb: Move min-height handling for independent FCs to parent context
Previously, GridFormattingContext handled its own min-height constraints
internally, which caused infinite recursion when min-height was an
intrinsic sizing keyword (e.g., min-height: max-content).

This change moves the responsibility to the parent formatting context
(BFC). When any box establishing an independent formatting context has
auto height but non-auto min-height:
1. BFC measures content height using a throwaway LayoutState
2. If content height < min-height, BFC passes min-height as definite
   available height to the child formatting context
3. Child FC runs once with correct constraints, unaware of min-height

Fixes https://github.com/LadybirdBrowser/ladybird/issues/4261 which
is corresponding to stack overflow on https://claude.ai/
2026-01-05 16:09:51 +01:00
Psychpsyo
3760818d58 LibWeb: Implement CSS-Multicol's 'Pseudo-Algorithm' 2025-12-15 09:45:48 +00:00
Jelle Raaijmakers
7a693a9780 LibWeb: Use parent's space for anonymous wrappers with inline contents
We already dealt with this for block level children, but inline level
children should also look at the anonymous wrapper's parent's available
space to be able to properly resolve percentage values, for example.
2025-12-11 23:29:14 +00:00
Callum Law
12716dccf0 LibWeb: Avoid including ComputedProperties.h in Element.h
This reduces the size of the recompile when ComputedProperties.h is
modified from ~1200 to ~70
2025-10-27 14:50:54 +00:00
Tim Ledbetter
0bdb831c68 LibWeb: Avoid null dereference in ListItemBox specified content check 2025-10-14 10:27:11 +01:00
Manuel Zahariev
9d77221c4d LibWeb/CSS: Add support for content to the ::marker pseudo-element
A ::marker pseudo-element is created for list item nodes (nodes
with display:list-item).

Before:
    - The content of the ::marker element is created magically from
    the value of the ordinal (for <ol>) or from a template (for <ul>).
    The style `content` is ignored for ::marker pseudo-elements.

After:
    - If a "list item node" has CSS `content` specified for its ::marker
    pseudo-element, use this to layout the pseudo-element,
    https://drafts.csswg.org/css-lists-3/#content-property
    - Otherwise, layout the list item node as before.
2025-10-10 12:02:16 +01:00