Commit Graph

914 Commits

Author SHA1 Message Date
Andreas Kling
1bfcf49ffe LibWeb: Honor reversed axes for flex overflow
Scrollable overflow still assumed a top-left scroll origin and only
added trailing padding on the physical bottom edge. That broke
scrollWidth and scrollHeight for flex containers whose main or cross
axis was reversed by writing-mode, direction, flex-direction, or
wrap-reverse.

Teach flex layout to place wrapped lines using the computed cross-axis
direction and to measure scrollable overflow from the container's
actual scroll origin so reachable reversed overflow is preserved, the
unreachable side is clipped, and end padding is added on the correct
physical edge.

Keep per-item cross-axis placement using the existing behavior.
Applying full cross-axis reversal there regressed baseline alignment
tests, and zero-sized boxes exactly at the scroll origin must still
contribute descendant overflow, so the unreachable-overflow checks
need strict comparisons.

This makes negative-overflow-002 and negative-overflow-003 pass and
improves negative-overflow, align-content-wrap-003, and
overflow-with-padding.
2026-04-05 10:42:32 +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
Andreas Kling
9450fcb2b0 LibWeb: Fix grid abspos descendant placement
Use the absolutely positioned box's own grid placement to resolve the
grid-area containing block rectangle instead of inheriting the nearest
in-flow grid item's area.

Keep the grid-specific static-position handling for axes with both
insets auto, but resolve mixed auto and explicit grid placement axes
against the augmented grid for direct children as well as descendants
inside grid items.

This fixes the imported abspos and alignment WPTs for values like
grid-row: 1 and grid-column: auto / 1 while keeping the reduced
descendant regressions passing.
2026-04-04 14:35:50 +02:00
Andreas Kling
6994463b1c LibWeb: Rerun row sizing for auto-height grids
Percentage row tracks in auto-height grids only participated in our
intrinsic sizing pass. We computed the grid container's intrinsic
height from that pass, but never reran row track sizing with the
now-known height, so percentage rows stayed content-sized and the
imported WPTs had recorded failures.

Reset the mutable track sizing state before rerunning the row pass,
rebuild row gap tracks against the resolved container height, and keep
the automatic content height from the intrinsic pass so parent layout
still resolves auto height correctly. Rebaseline the percentage-row
imports and the grid track parsing import that now improve with the
corrected percentage resolution.
2026-04-04 14:35:50 +02:00
Andreas Kling
5de6382d11 LibWeb: Fix flex abspos static positions on reversed cross axes
The static-position rectangle for absolutely positioned flex children
still mapped cross-axis flex-start and flex-end directly to physical
start and end. That broke cases where the flex cross axis is reversed
by writing mode, direction, or wrap-reverse.

Teach the flex formatting context to derive the cross-axis direction
from its logical axes and wrap mode, and use that when resolving
cross-axis alignment for abspos static positioning. This clears the
newly imported position-absolute-013 test and improves a broader set
of existing flex abspos WPT baselines.
2026-04-03 14:29:44 +02:00
Andreas Kling
0e41072db7 LibWeb: Fix remaining flex percentage-height WPTs
Handle flex main and cross axes in logical terms so percentage
height resolution keeps working in vertical writing modes and
other orthogonal cases. Also let resolvable percentage max
cross sizes clamp stretched flex items.

The same change improves a broader set of imported css-flexbox
tests than the original percentage-heights cases. Update the
affected expectations in the same change so the resulting
behavior stays documented and the suite remains green.
2026-04-03 14:29:44 +02:00
Andreas Kling
99dd13f7f7 LibWeb: Resolve grid item percentages against definite areas
Final grid item alignment runs after the grid area size has already
been resolved from the spanned tracks. Reusing
should_treat_height/width_as_auto() there misclassified percentage
preferred sizes as auto based on the outer grid container's own
definiteness, which broke fixed-track cases such as height:50% in a
100px row.

Restore the final alignment fast path to only special-case literal
auto sizes, so percentage preferred sizes resolve through
calculate_inner_width/height() against the computed grid area.

Add a text test that covers start/stretch behavior in both axes and
across single-track and spanning definite grid areas.
2026-04-03 14:29:44 +02:00
Andreas Kling
d75934edf5 LibWeb: Don't mark auto height as definite for abspos non-BFC elements
When an absolutely positioned non-BFC element (flex, grid, etc.) has
auto height, we pre-compute its height from content before running
inside layout. Previously, this content-derived height was marked as
"definite", which incorrectly allowed descendants to resolve percentage
heights against it. Per CSS 2.1 section 10.5, percentage heights should
only resolve when the containing block's height is specified explicitly.

The fix is to simply not set has_definite_height when the CSS height is
auto. This naturally prevents percentage resolution through all existing
paths (set_node, should_treat_height_as_auto, calculate_inner_height)
without needing any new flags or per-site checks.

Two additional fixes enable this:

- In flex line cross-size clamping, remove the contains_percentage()
  guard that prevented percentage min/max-height from resolving. These
  percentages resolve correctly via calculate_inner_height's containing
  block lookup, since the abspos element's containing block always has
  a definite height.

- In grid item alignment, check should_treat_height/width_as_auto for
  percentage preferred sizes, so they're treated as auto when the grid
  container's height is indefinite (CSS Grid section 6.6).
2026-04-03 14:29:44 +02:00
Jelle Raaijmakers
a3df1b42c9 LibWeb: Consider Size::is_none() in FFC as an unresolvable size
Prevents the WPT test
`css/css-anchor-position/anchor-size-in-flex-basis-crash.html` from
crashing.
2026-04-01 19:41:46 +01:00
Jelle Raaijmakers
4293c841f3 LibWeb: Implement CSS anchor positioning
When `position-anchor` is used, resolve the insets for that absolutely
positioned box.

Co-authored-by: Rob Ryan <rob@affclicks.com>
2026-04-01 19:41:46 +01:00
Jelle Raaijmakers
6068d94752 LibWeb: Update/remove unused includes
No functional changes.
2026-04-01 19:41:46 +01:00
Shannon Booth
c6809eb53d LibWeb/Layout: Use is_empty() when fragment text has nothing to trim
Treat an empty fragment text view as equivalent to is_null, as we
only need to determine that there is no text to trim.

This does not change trimming behavior as an empty text view reaches
the same result as the previous null only check, since the trimming loop
would not run.
2026-03-31 13:48:50 +01: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
Psychpsyo
ca99e65d83 LibWeb: Establish formatting context due to container-type 2026-03-30 15:43:39 +01:00
Callum Law
fe5d6471f0 LibWeb: Store GridTrackPlacement sub-values as StyleValues
Gets us one step closer to removing the `FooOrCalculated` classes
2026-03-30 14:05:10 +01:00
Andreas Kling
0e438c01d8 LibWeb: Compute CAPMIN from caption outer widths
Fixing float-related intrinsic width bookkeeping exposed that table
captions only contributed their raw min-content width to CAPMIN.
That ignored caption padding, borders, margins, and definite widths,
so captions could be laid out too narrow and wrap content that should
stay beside floats.

Compute each caption's min-content contribution as an outer width and
clamp it against the preferred outer width when the caption has a
definite width. Add a test for a fixed-width caption with a float and
rebaseline the existing padded-caption layout test.
2026-03-29 12:05:25 +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
Jelle Raaijmakers
3d2571b46e LibWeb: Implement text-decoration-skip-ink painting
Use Skia's SkTextBlob::getIntercepts() to find where glyph outlines
cross the underline/overline band, then split the decoration line into
segments with gaps around those intersections.
2026-03-26 12:15:36 +00:00
Callum Law
b86377b9dc LibWeb: Clamp CSS <integer> value to i32 at parse time
This matches the behavior of other browsers. Previously we implemented
this at used-value time for z-index specifically.
2026-03-26 12:30:01 +01:00
Jelle Raaijmakers
c07e92fc7e LibWeb: Apply text-overflow ellipsis as line box post-processing
The previous implementation checked text-overflow and overflow-x
on the text node's direct parent during inline item iteration.
Since these are non-inherited properties, ellipsis only worked
for text directly inside the block container, not when wrapped
in inline elements like <span> or <a>.

Move ellipsis truncation to a post-processing step after line
boxes are constructed, checking the containing block instead.
2026-03-26 00:19:50 +00:00
Tim Ledbetter
d69d74e7ce LibWeb: Always shift line boxes below floats when content does not fit
Previously, this wasn't done when placing the first inline content in a
block, which caused long unbreakable words to overlap with floats
instead of being moved below them.
2026-03-25 21:28:57 +01:00
Luke Wilde
df32da5e86 LibWeb: Make every HTMLElement potentially form-associated
This can be the case for form-associated custom elements, where any
HTML element can be form-associated.
2026-03-25 13:18:15 +00:00
Callum Law
f347be8206 LibWeb: Store underlying RatioStyleValue values as StyleValues
Previously we stored a fully formed `Ratio` - this restricted us from
supporting calculated values which will be implemented in a later commit
2026-03-24 14:00:01 +00:00
Callum Law
b07ef9435e LibWeb: Remove fallback behavior from color_or_fallback
All of the properties using this method only accept color values so the
fallback behavior wasn't required
2026-03-24 13:56:01 +01:00
Callum Law
e2f2f813b6 LibWeb: Handle accent-color auto value explicitly
`accent-color` is the only user of the fallback functionality of
`color_or_fallback`, by handling this explicitly we can remove that
fallback functionality in a later commit.

Also includes a couple of improvements for `accent-color` specifically:
 - We don't set it in `ComputedValues` twice.
 - `ComputedProperties::accent_color` returns a non-optional value
   (since we always have one)
 - `ComputedProperties::accent_color` takes a `ColorResolutionContext`
   instead of generating one itself from a `LayoutNode`, this will allow
   us to reuse shared resolution contexts in the future
2026-03-24 13:56:01 +01:00
Jelle Raaijmakers
277512ce01 LibWeb: Implement CSS flexbox baseline aligned items 2026-03-24 05:13:49 +01:00
Andreas Kling
8836f28267 LibWeb: Don't walk past anonymous table cells for percentage resolution
When resolving percentage heights/widths against the containing block,
we walk past anonymous boxes to find the relevant ancestor. However,
anonymous table cells are proper containing blocks with their own
sizing semantics. Walking past them caused us to reach the viewport
and incorrectly resolve percentages against the viewport size.

Fix this in all affected places in FormattingContext:
- should_treat_height_as_auto()
- calculate_inner_height()
- should_treat_max_height_as_none()
- should_treat_max_width_as_none()
- compute_inset() percentage-as-auto check
- solve_replaced_size_constraint()
- compute_height_for_replaced_element()
2026-03-23 16:40:38 +01: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
c5d7898b41 LibWeb: Resolve grid item height via aspect-ratio for non-stretch items
When a grid item has a preferred aspect-ratio, a definite width, and
non-stretch alignment, resolve its height through the aspect-ratio
instead of using fit-content sizing.

The fit-content height calculation was incorrectly using the available
width (grid area width) to compute the aspect-ratio-based height,
rather than using the item's actual resolved width. This caused items
like a 50px wide div with aspect-ratio: 1/1 to get a height of 784px
(the grid area width) instead of 50px.
2026-03-21 23:16:32 -05: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
Andreas Kling
3b1bbd7360 LibWeb: Respect box-sizing when computing grid item minimum contribution
When computing the minimum contribution of a grid item with a definite
minimum size (e.g. min-height: 3rem), we must convert the CSS value to
content-box size before adding margin-box sizes. With box-sizing:
border-box, the CSS value already includes padding and border, so
passing it directly to add_margin_box_sizes() double-counted them.

Use calculate_inner_width/calculate_inner_height to properly convert
the minimum size to content-box dimensions, consistent with how
preferred and maximum sizes are already handled in the grid track
sizing algorithm.
2026-03-21 21:42:44 -05:00
Andreas Kling
b07a19e35f LibWeb: Account for letter-spacing when trimming trailing whitespace
When trimming trailing whitespace from line boxes, we were only
subtracting the glyph width of each trimmed character. However, during
text shaping, letter-spacing is added to each glyph's advance width.
We must subtract the full advance (glyph width + letter-spacing) to
get an accurate line width after trimming.

This also fixes a pre-existing precision bug where the glyph width
(a float) was truncated to int, leaving fractional phantom width in
the line box.

Together, these issues caused text with letter-spacing and trailing
HTML whitespace to have an underestimated intrinsic width, leading to
unexpected text wrapping in flex layouts.
2026-03-21 21:42:44 -05:00
Andreas Kling
b102a68746 LibWeb: Precompute contained boxes map for measure_scrollable_overflow
Build a HashMap from each containing block to the boxes it contains
before measuring scrollable overflow. This allows
measure_scrollable_overflow() to iterate only the relevant boxes
directly, rather than walking the entire layout subtree and filtering
by containing_block() at each node.
2026-03-21 18:20:09 -05:00
Tim Ledbetter
cd9a76902b LibWeb: Avoid applying viewbox transform multiple times for nested SVGs
The parent's viewBox transform includes both scaling and a centering
offset. For nested <svg> elements, this transform was applied once to
position the nested SVG's box, then again to position children inside
it, double-counting the centering offset.

We now apply the parent's transform to the nested SVG box upfront to
ensure the transform is only applied once.
2026-03-20 15:59:44 -05:00
Tim Ledbetter
60334b3464 LibWeb: Treat percentage insets as auto for indefinite containing blocks 2026-03-20 15:58:50 -05:00
Zaggy1024
44ed698d4f LibWeb: Separate the active element and the element being activated
We were conflating elements being the active element and elements being
activated. The :active pseudo class is supposed to be based on whether
an element will have its activation behavior run upon a button being
released.

Store whether an element is being activated as a flag that is set/reset
by EventHandler.

Doing this allows label elements to visually activate their control
without doing a weird paintable hack, so the Labelable classes have
been yeeted.
2026-03-17 04:01:29 -05:00
dominick038
76c65eca57 LibWeb: Clamp saturated CSSPixels values before make_definite() 2026-03-11 17:23:35 +01:00
Tim Ledbetter
2b8967d55f LibWeb: Distribute grid track free space among unfrozen tracks only
Previously, during grid track maximization we used the total number of
grid tracks as the denominator when distributing the available free
space. Using a larger denominator than expected meant that the
algorithm needed a larger number of iterations than necessary to
converge leading to rounding errors in the final track sizes.
2026-03-10 22:39:55 +01: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
f61528238e LibWeb: Replace is<T> + as<T> with as_if<T>
Doing so results in a single fast_if<T> or dynamic_cast<T> call instead
of two. No functional changes.
2026-03-10 15:17:51 +01:00
Tim Ledbetter
483ea12914 LibWeb: Reinitialize TextNode chunk state after collapsing whitespace
When collapsible whitespace is skipped without committing a prior
chunk, the loop would fall through and continue with font information
derived from the last whitespace code point rather than the next
non-whitespace character. Restart the function via recursion so that
these values are reinitialized from the correct code point.
2026-03-09 15:34:34 +01:00
Psychpsyo
f7e1c8cf6b LibWeb: Add spec comment to algorithm 2026-03-07 17:17:18 +01:00
Tim Ledbetter
2fbc4d26ff LibWeb: Allow SVG root elements to participate in inline continuation 2026-03-05 17:16:42 +01:00
Jelle Raaijmakers
344b1a1ddc LibWeb: Hoist fieldset contents into anonymous wrapper
Make an anonymous wrapper for a fieldset's contents, excluding its
legend, as the spec asks us to do. This will make sure we can apply
certain CSS properties to the correct box.

Fixes #6747
2026-03-04 20:39:01 +01: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