Commit graph

107 commits

Author SHA1 Message Date
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
Andreas Kling
4b281fa880 LibWeb: Make Length ctor/dtor/factory inline
These functions are trivial, and we were actually bleeding a lot of time
in profiles to just function entry/exit.

By marking Length::make_px() as [[nodiscard]], we also exposed some
places that were creating a Length and not using it for anything.
2025-09-24 23:59:41 +02:00
Andreas Kling
b634918ff6 LibWeb: Remove Font::width() overloads for UTF-8
This is prep work for getting rid of UTF-8 text shaping.
2025-09-21 13:22:38 +02:00
Aliaksandr Kalenik
e2eb8716e5 LibWeb: Fix incorrect margin collapsing behavior [BFC]
This change removes premature reset of
`block_container_y_position_update_callback`. Also makes callback
private in `BlockMarginState`, because resetting it independently of
currently accumulated margins is incorrect.

Lots of test expectations are updated, but there is no visual
difference.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/6074
2025-09-08 14:09:06 +02:00
Aliaksandr Kalenik
01bfb5bc81 LibWeb: Capture y by value in position update callback [BFC]
Callback registered by
`register_block_container_y_position_update_callback()` is executed
after `layout_block_level_box()` returned, so capturing stack variable
`y` by reference is UB.
2025-09-08 14:09:06 +02:00
Sam Atkins
dd122e2f74 LibWeb: Make LengthBox hold LengthPercentageOrAuto
Not every user of this requires an `auto` state, but most do.

This has quite a big diff but most of that is mechanical:
LengthPercentageOrAuto has `resolved_or_auto()` instead of `resolved()`,
and `to_px_or_zero()` instead of `to_px()`, to make their output
clearer.
2025-09-04 13:31:24 +01:00
Sam Atkins
7244c6ddf9 LibWeb/Layout: Use LengthOrAuto in BFC::compute_width() 2025-09-04 13:31:24 +01:00
Sam Atkins
64da3b8f7d LibWeb/Layout: Use LengthOrAuto in BFC::compute_width_for_floating_box() 2025-09-04 13:31:24 +01:00
Tim Ledbetter
cb1a1a5cb5 LibWeb: Replace is<T>() with as_if<T>() where possible 2025-08-25 18:45:00 +02:00
Jelle Raaijmakers
087601832a LibWeb: Set fit-content width for buttons in used values, not computed
For button layouts, we were overriding the computed `width` value with
`fit-content` in `TreeBuilder::wrap_in_button_layout_if_needed()`. But
the spec asks us to set the _used value_ instead, so we now actually
calculate the fit-content width and set the box' content width to it.

Fixes #2516.
2025-08-18 11:04:34 +01:00
Andreas Kling
41e8211405 LibWeb: Honor "should treat as auto" cases in aspect-ratio decisions 2025-08-05 21:33:41 +02:00
Jelle Raaijmakers
af8b256832 LibWeb: Avoid floats for BFC/FFC/GFCs with a definite width
Applicable FCs with an indefinite width simply shrink in their available
space as long as floats are intruding, but as soon as we have a definite
width we must push the box down until it it has enough space again.

Fixes #4136.
2025-08-01 14:26:12 +02:00
Jelle Raaijmakers
d27395cf1f LibWeb: Add spec links and text for BFC/FFC/GFC float intrusion behavior
No functional changes.
2025-08-01 14:26:12 +02:00
Jelle Raaijmakers
b6732240c7 LibWeb: Avoid float intrusions for BFCs, FFCs and GFCs
We were only avoiding float intrusions for BFCs, but FFCs and GFCs
should also accommodate for any floats present.

Work towards #4136.
2025-07-28 12:13:06 +02:00
Jelle Raaijmakers
ab3e9799d5 LibWeb: Allow negative margins to influence inline offset after float
In 89ba00304c, the box' X position was
capped at 0 to prevent negative X positions to act as if there were
intruding floats on the left side. Instead, we need to check whether the
left side float intrusion we are going to calculate matters at all -
because if there's no matching float box, the intrusion is always going
to be 0 and we don't need to take the box' X position into account.

Fixes the floating publication images on https://lexfridman.com/.
2025-07-28 12:11:56 +02:00
Jelle Raaijmakers
115e5f42af LibWeb: Improve graphical list item marker positioning
While 788d5368a7 took care of better text
marker positioning, this improves graphical marker positioning instead.

By looking at how Firefox and Chrome render markers, it's clear that
there are three parts to positioning a graphical marker:

  * The containing space that the marker resides in;
  * The marker dimensions;
  * The distance between the marker and the start of the list item.

The space that the marker can be contained in, is the area to the left
of the list item with a height of the marker's line-height. The marker
dimensions are relative to the marker's font's pixel size: most of them
are a square at 35% of the font size, but the disclosure markers are
sized at 50% instead. Finally, the marker distance is always gauged at
50% of the font size.

So for example, a list item with `list-style-type: disc` and `font-size:
20px`, has 10px between its start and the right side of the marker, and
the marker's dimensions are 7x7.

The percentages I've chosen closely resemble how Firefox lays out its
list item markers.
2025-07-17 09:35:09 +01:00
Jelle Raaijmakers
788d5368a7 LibWeb: Improve list item marker positioning and alpha/roman text
This commit is a three-parter that is hard to separate without breaking
marker rendering:

  1. Any marker style that results in a string, except for a literal
     string (e.g. `list-style-type: "@"`), should get the string ". "
     appended. We forgot to do this for the alpha and roman types.

  2. Instead of using the "pixel size rounded up" from a font and adding
     an arbitrary 1 to that, we now use the exact pixel size for as long
     as possible to improve our vertical positioning of markers.

  3. Instead of always adding a "default marker width" to the marker
     content width, we now only do this if we did not have text metrics
     available (i.e. the marker style is not a text type). This greatly
     improves horizontal positioning of text markers.
2025-07-15 19:05:36 +01:00
Andreas Kling
11fa5fdd47 LibWeb: Honor box-sizing for block-level replaced element widths
Before this change, we were always behaving as if box-sizing were
content-box for block-level replaced element widths.

This fixes the squishy logo on https://videolan.org/
2025-07-14 11:16:13 +02:00
Tim Ledbetter
30cdacc05a LibWeb: Add flow relative values for the clear property
Currently `inline-start` and `inline-end` are always treated as `left`
and `right` respectively.
2025-06-17 09:26:26 +02:00
Tim Ledbetter
28b24b72bc LibWeb: Don't resolve flow-relative values for float too early
This allows `getComputedStyle()` to return the correct value if `float`
is set to `inline-start` or `inline-end`
2025-06-17 09:26:26 +02:00
InvalidUsernameException
1d0cfdc839 LibWeb: Consider margins during fit-content sizing in BFC
`BlockFormattingContext::compute_width()` stores the left and right
margins in the layout state at the very end of the function. However,
before doing so, it calls `FormattingContext::calculate_inner_width()`
which ends up calling `FormattingContext::calculate_stretch_fit_width()`
if the current box has `width: fit-content`.

Due to this, `calculate_stretch_fit_width()` would always see the
margins from the layout state as zero and therefore not take them into
account. Subsequently, the calculated width ended up being wrong.

Saving margins on the layout state earlier, before calling
`calculate_inner_width()`, makes sure that the width is calculated
correctly.
2025-06-05 17:56:19 +02:00
Ruben
3263b629c6 LibWeb: Correct the calculation for right float intrusion
We used to subtract the maximum right margin of any containing box,
but we want to subtract the entire right margin instead. This yielded
incorrect intrusions for right floats not placed in the root.
2025-05-27 17:31:47 +02:00
Ruben
09f5ce42f6 LibWeb: Ensure floats are correctly placed under the preceding float
Previously floats would be placed next to the highest float that
fitted the new float on its line. However, this violates the rule
that floats should be placed under the preceding float if it does
not fit next to it.
2025-05-23 10:58:15 +02:00
Jelle Raaijmakers
170e599bd9 LibWeb: Always set the Y offset of the current block in BFC
Originally part of a fix in 15103d172c, it
appears that this is no longer necessary and received a better fix in a
more recent commit. Resolves a visual regression with the ACID3 test.
2025-05-16 20:33:27 +02:00
Jelle Raaijmakers
123abe0e79 LibWeb: Overlap float space and left margins for all shared ancestors
We would only correct for the left margin of the containing block of the
child box that we are laying out, but we actually need to correct for
all left margins up until the containing block that contains both the
float and the child box.

Fixes #4639.
2025-05-09 14:13:02 +02:00
Jelle Raaijmakers
79352ad725 LibWeb: Remove application of vertical float clearance to BFC Y offset
In 15103d172c we applied any remaining vertical float clearance to the
BFC's current Y offset for the next block to layout, because a `<br
style="clear: left">` could introduce clearance that would otherwise be
ignored. However, a `<div>` that floats _and_ clears `right` also
introduces this clearance and it is obvious that this should not push
down any subsequent blocks to layout in the current BFC.

Turns out, we don't need this change anymore. Some other later change
also fixed the underlying issue, and by getting rid of the original fix
we can now render https://en.wikipedia.org/wiki/SerenityOS correctly
again.

Fixes #4418.
2025-04-23 13:24:58 +02:00
Jelle Raaijmakers
1731389bfe LibWeb: Return early in clear_floating_boxes()
Get rid of an indentation level. No functional changes.
2025-04-23 13:24:58 +02:00
Jelle Raaijmakers
47ed39a790 LibWeb: Reuse floating box' margin box rect for clear: ...
We already store the resulting margin box rect for floating boxes
relative to their root coordinate space, so don't calculate them again.
No functional changes.
2025-04-23 13:24:58 +02:00