Commit graph

120 commits

Author SHA1 Message Date
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
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