Commit Graph

983 Commits

Author SHA1 Message Date
Jelle Raaijmakers
1aeb080250 LibCompress: Treat LZW decoding errors as end of stream
The LZW data for both GIF and TIFF images is sometimes intentionally
missing an end-of-information (EOI) code, which technically is a
decoding error, but in practive is handled gracefully by Firefox, Safari
and Chrome for GIFs and Safari for TIFFs. Let's mirror their behavior.

The included WPT test exposes the fact that trailing garbage bytes can
also result in decoding errors. We handle this in the LZW logic rather
than in the image decoding since our LZW implementation is currently
only used by GIF and TIFF decoding. The error is logged behind the
LZW_DEBUG flag.
2026-04-29 20:28:15 +02:00
Andreas Kling
d1d739b533 LibWeb: Clear stale layout descendants for skipped subtrees
When content-visibility:hidden starts skipping a subtree, clear the
stale layout and paint nodes for its descendants.

Preserve SVG mask, clipPath, and pattern resource boxes only when
they are attached to a referencing layout subtree outside the subtree
being hidden. Resources used inside the hidden subtree are cleared
with that subtree, so they rebuild under the live referencing layout
node when the subtree becomes visible again.

Otherwise observed descendants can keep old paintables around after
the current paint tree has been rebuilt without them.
2026-04-27 16:22:11 +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
Shannon Booth
b0acc18f9b LibWeb: Clamp limited grid item contributions by fixed max tracks
When resolving grid track sizes, limited min/max-content contributions
should be capped by fixed max track sizing functions, including the
argument to fit-content(). We were instead falling back to the grid
container maximum size, which allowed a grid item with overflowing
contents in a fit-content(0) row to inflate the intrinsic block size of
a nested grid.

That bogus intrinsic height could then be used for the grid's second row
sizing pass, causing unrelated flexible rows to absorb the extra space.
2026-04-23 09:23:02 +01:00
Tim Ledbetter
d8825c89af LibWeb: Use caption content width for inline layout in table captions
Previously, `run_caption_layout()` passed the table's border-box width
as the available space to the caption's formatting context. The BFC then
used this width directly for inline line breaking, causing text to
overflow the caption's content box by the size of the caption's own
border and padding.
2026-04-22 15:35:07 +01:00
Tim Ledbetter
da5e002db1 LibWeb: Keep select button text in sync with the selected option
Previously, the select button's text was only refreshed inside the
two non-trivial branches of the selectedness setting algorithm.
Paths that left the select with exactly one selected option hit a
no-op branch and skipped the refresh.

Fix this by implementing the "clone selected option into select
button" algorithm and invoking it whenever the set of selected options
may have changed.
2026-04-22 09:15:29 -04:00
Shannon Booth
4a460b1dda LibWeb: Apply calculated vertical-align offsets in LineBuilder
Fixes the alignment of the arrow for "Get in touch" on canonical.com
2026-04-15 21:49:40 +02:00
Shannon Booth
482e5e770f LibWeb: Let flexbox stretch auto-width text inputs
Don't rewrite text input 'width: auto' to 'size()ch' in
'HTMLInputElement::adjust_computed_style()'. That turns the control into
a definite-width flex item and prevents 'align-items: stretch' from
expanding it across the flex container.

Fixes the layout of the input element on the element matrix login page.
2026-04-13 09:57:01 +02:00
Callum Law
919dba87b9 LibWeb: Support calculated <flex> values
This spec note reads to me as explicitly disallowing combining
`<length>` and `<flex>` (i.e. `calc(1px + 1fr)`). This behavior is
already implemented (as it is for all other combination of units i.e.
`<length>` and `<time>`).
2026-04-09 21:41:49 +01:00
Shannon Booth
af03e07f90 LibWeb: Clamp replaced element min/max sizes in the content box space
The replaced element sizing code was comparing tentative used sizes
min-width/min-height and max-width/max-height. For box-sizing:
border-box, that mixes content-box and border-box measurements, which
can clamp replaced-like elements incorrectly.

This could make, for example search/text inputs with explicit height
and padding render too short.

Resolve min/max constraints with calculate_inner_width() and
calculate_inner_height() before clamping so the comparison uses
the same inner sizing space as the tentative replaced size.

Fixes the sizing of the search bar on:

https://tv.apple.com/se
2026-04-06 12:35:12 +02:00
Andreas Kling
e2e3c7fcdf LibWeb: Rebuild counter style cache lazily
Stop rebuilding the counter style cache from every style update.
That made unrelated restyles pay the full counter-style cost even when
no relevant stylesheet state had changed.

Dirty the cache when stylesheet rule caches are invalidated and rebuild
it on the first counter-style lookup instead. Also make cold cache
rebuilds include user stylesheets.

Add regression tests covering insertRule() and replaceSync() updates
that should make newly defined counter styles take effect.
2026-04-05 12:34:28 +02:00
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
Tim Ledbetter
d9079d2078 LibWeb: Apply z-index only to positioned elements
`z-index` values other than `auto` also apply to grid and flex items
because they behave as if their `position` were `relative`.
2026-04-03 23:28:39 +02:00
Andreas Kling
13a7459e61 LibWeb: Add tests for percentage sizes in abspos non-BFC containers
Comprehensive coverage for percentage size resolution when the
containing block is an absolutely positioned non-BFC element (flex,
inline-flex, grid, inline-grid) with auto height.

Tests cover: flex row/column, grid, inline-flex, inline-grid, explicit
height (positive test), inset-derived height (positive test), nested
percentages, calc() with percentage, min-height percentage, multiple
items, both-axes-auto, and mixed fixed/percentage children.
2026-04-03 14:29:44 +02:00
Psychpsyo
ca99e65d83 LibWeb: Establish formatting context due to container-type 2026-03-30 15:43:39 +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
0b95f64ee9 Tests: Add right-float BFC avoidance coverage
Add a right-side mirror of the existing overflow:hidden test with a
nested block and margin-right on the containing block.

This exercises the float-avoidance bookkeeping for a BFC that has to
sit beside a right float in nested coordinates.
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
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
Andreas Kling
3730417b6d Tests: Add layout tests for percentage sizing in auto-height tables
These tests demonstrate incorrect behavior where percentage heights
and max-heights resolve against the viewport instead of being treated
as auto/none inside auto-height tables.
2026-03-23 16:40:38 +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
c0d65b7cda Tests: Add test for grid item with aspect-ratio and place-items: center 2026-03-21 23:16:32 -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
Rob Ryan
1b508c4e42 LibWeb: Remove label default inline-block style
Let labels fall back to their default inline display so empty
labels do not create extra line height above following
block content.

Add a regression for the empty-label case and update the
file input layout expectation for the UA shadow label.
2026-03-21 07:40:25 +00: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
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
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
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
MichielN19
98f1813f5d LibWeb: Fix input field placeholder vertical alignment
Input elements with a placeholder were positioned lower than inputs
without a placeholder or inputs with text. This was caused by the
placeholder element missing height height and overflow properties that
the inner text element had.

This fix makes sure both the placeholder and inner text elements let
show the same position of the input field.
2026-03-02 13:24:20 +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
Callum Law
f6eccc629c LibWeb: Default @counter-style system descriptor to symbolic
The spec says that the `system` descriptor defaults to `symbolic` but
previously we just ignored any `@counter-style` rules without a `system`
descriptor
2026-02-27 16:25:53 +00:00
Callum Law
d0eabada0b LibWeb: Support extended Korean counter styles 2026-02-27 12:10:44 +00:00
Callum Law
1877c20c7b LibWeb: Support extended Japanese counter styles 2026-02-27 12:10:44 +00:00
Callum Law
08a5ed7ec6 LibWeb: Support Chinese counter styles
We implement the extended version of this algorithm but don't take full
advantage of it since we are limited to an i32 for our counter values.
2026-02-27 12:10:44 +00:00
Tim Ledbetter
f05bc7c0cd LibWeb: Implement dominant-baseline for SVG text
This property determines the default baseline used to align content
within the given box.
2026-02-26 09:23:23 +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
6b3d3468a1 LibWeb: Account for <col> span attribute during table grid formation
Previously, the column count was always incremented by 1. This led to a
mismatch with `compute_outer_content_sizes()`, which did use the `span`
attribute to advance the column index. This mismatch caused an
out-of-bounds access when the column index was greater than the
expected number of columns.
2026-02-24 12:14:54 +01:00
Zaggy1024
21019c2fa9 LibWeb: Use UA shadow DOM for media elements' controls
Instead of using a custom paintable to draw the controls for video and
audio elements, we build them out of plain old HTML elements within a
shadow root.

This required a few hacks in the previous commits in order to allow a
replaced element to host children within a shadow root, but it's
fairly self-contained.

A big benefit is that we can drive all the UI updates off of plain old
DOM events (except the play button overlay on videos, which uses the
video element representation), so we can test our media and input event
handling more thoroughly. :^)

The control bar visibility is now more similar to how other browsers
handle it. It will show upon hovering over the element, but if the
cursor is kept still for more than a second, it will hide again. While
dragging, the controls remain visible, and will then hide after the
mouse button is released.

The icons have been redesigned from scratch, and the mute icon now
visualizes the volume level along with indicating the mute state.
2026-02-23 07:27:31 +01:00
Zaggy1024
bc60768cb0 LibWeb: Rewrite audio elements without controls to display:none
This is part of the rendering spec, but we had neglected to do this
before. It causes one WPT check to fail, but other browsers get the
same result on that check, so I guess we can call that a win. :^)
2026-02-23 07:27:31 +01:00
Tim Ledbetter
83bd30b957 LibWeb: Stop inline elements after table-cell being swallowed into table
The `is_ignorable_whitespace()` check in table fixup traverses anonymous
block wrappers to see if they contain only whitespace. It rejected
out-of-flow and text descendants but silently skipped in-flow non-text
elements like `<span>`, misclassifying wrappers with real content as
ignorable and absorbing them into the table structure.
2026-02-21 05:48:53 +00:00
Tim Ledbetter
8240fa0dc8 LibWeb: Resolve percentage table widths in wrapper width computation
Previously, a table with `width: 100%` and `margin: auto` whose content
was narrower than the viewport would be centered based on content
width rather than filling the containing block. Resizing the viewport
wider than the content would shift the table progressively further to
the right.

Co-authored-by: Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
2026-02-21 05:16:56 +01:00
Aliaksandr Kalenik
e87fece8c6 LibWeb: Force position:static on non-root SVG elements
SVG elements (except the outermost <svg>) use SVG's coordinate system,
not the CSS box model, so CSS positioning doesn't apply to them.

This adds SVGElement::adjust_computed_style() to force position:static
on all SVG elements except the outermost <svg> element (which has no
owner_svg_element()). SVGSymbolElement's existing override now calls
Base::adjust_computed_style() to inherit this behavior.

With this in place, the FIXME in layout_absolutely_positioned_element()
for SVG boxes becomes unreachable and is replaced with
VERIFY_NOT_REACHED().
2026-02-21 05:12:55 +01:00
Aliaksandr Kalenik
b3231ea2a0 LibWeb: Make foreignObject establish a containing block for abspos
Absolutely positioned elements inside SVG foreignObject were being
positioned relative to an ancestor containing block outside the SVG,
instead of relative to the foreignObject itself. Per a W3C resolution
and the behavior of other browsers, foreignObject should establish a
containing block for absolutely and fixed positioned elements.

With this fix, the `has_abspos_with_external_containing_block` check
in `set_needs_layout_update()` and the abspos preservation loop in
`relayout_svg_root()` become dead code — remove both and simplify the
ancestor loops. Rename related tests to reflect the new behavior.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/3241
2026-02-17 15:59:59 +01:00
Aliaksandr Kalenik
397fe9c58e LibWeb: Dump all paintables attached to a layout node in dump_tree
Instead of dumping only the single paintable passed into the function,
iterate over all paintables attached to its layout node. This makes
detached/disconnected paintables visible in the dump output, which is
useful for debugging incremental layout issues.

Rebaseline content-image-simple layout test: the anonymous InlineNode
for ::before content spanning multiple lines legitimately has a
separate PaintableWithLines per line, which now all appear in the dump.
2026-02-15 17:47:30 +01:00