Commit Graph

875 Commits

Author SHA1 Message Date
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
Michael Watt
1243dbd72f LibWeb: Create absolute container for will-change: position
This fixes:
http://wpt.live/css/css-will-change/will-change-abspos-cb-001.html
2026-03-03 16:52:57 +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
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
858989e006 LibWeb: Make CounterStyle ref counted
Previously we just passed around a reference to the `CounterStyle`
stored on `Document::registered_counter_styles` but this won't be
possible for anonymous counter styles (i.e. those created by the
`<symbols()>` function)
2026-02-27 16:25:53 +00:00
Tim Ledbetter
a762f623bd LibWeb: Implement SVGPatternElement 2026-02-27 17:14:50 +01:00
Tim Ledbetter
000a746b78 LibWeb: Apply SVG transforms to image elements during layout 2026-02-27 17:14:50 +01:00
Tim Ledbetter
804287847a LibWeb: Add SVG paint fallback color support to CSS parsing 2026-02-27 17:14:50 +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
Psychpsyo
f7f95d2611 LibWeb: Do not remove inline start padding when collapsing whitespace 2026-02-27 12:05:03 +01:00
Aliaksandr Kalenik
d163c11612 LibWeb: Skip IFC abspos static position in intrinsic sizing
BFC, TFC, GFC, and FFC all skip absolutely positioned element handling
during intrinsic sizing. IFC was the only formatting context that
unconditionally calculated static positions for abspos elements.

This is unnecessary during intrinsic sizing because the positions are
based on intermediate state and will be recalculated during normal
layout. It also avoids creating UsedValues entries for abspos boxes
that serve no purpose during intrinsic sizing.
2026-02-27 11:43:53 +01:00
Andreas Kling
a146225331 LibWeb: Use unsafe layout/paintable accessors where appropriate
Add unsafe_layout_node(), unsafe_paintable(), and unsafe_paintable_box()
accessors that skip layout-staleness verification. These are for use in
contexts where accessing layout/paintable data is legitimate despite
layout not being up to date: tree construction, style recalculation,
painting, animation interpolation, DOM mutation, and invalidation
propagation.

Also add wrapper APIs on Node to centralize common patterns:
- set_needs_display() wraps if (unsafe_paintable()) ...set_needs_display
- set_needs_paint_only_properties_update() wraps similar
- set_needs_layout_update() wraps if (unsafe_layout_node()) ...

And add Document::layout_is_up_to_date() which checks whether layout
tree update flags are all clear.
2026-02-26 21:09:08 +01:00
Andreas Kling
ed26fdaa81 LibWeb: Remove ViewportClient from ImagePaintable and VideoBox
Neither of these classes did anything useful as ViewportClients:

- ImagePaintable called set_visible_in_viewport() which is a FIXME
  no-op in every ImageProvider implementation.
- VideoBox had an empty did_set_viewport_rect() with a FIXME comment.

More importantly, they caused a crash when a DOM node was adopted into
a different document: the old ImagePaintable/VideoBox would still be
registered with the old document, but their document() accessor (which
goes through the DOM node) would return the new document. During GC
finalization, unregister_viewport_client() would fail because it was
trying to unregister from the wrong document.

The only meaningful ViewportClient is HTMLImageElement (for responsive
image source selection), which already handles document adoption
correctly in adopted_from().

Fixes crash when loading https://msn.com/
2026-02-26 09:25:25 +01: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
Callum Law
8d4084261a LibWeb: Resolve list item marker using registered counter styles 2026-02-23 11:21:09 +00:00
Callum Law
32b9ff21df LibWeb: Add generic int_from_style_value method
Reduces duplication in line with `number_from_style_value`,
`string_from_style_value` etc
2026-02-23 11:21:09 +00: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
dd0c821291 LibWeb: Allow replaced elements to host children
We add a new formatting context that simply runs layout for an
anonymous block formatting context within it. This allows replaced
elements to contain children, if the parent rewrites inline-flow to
inline-block.
2026-02-23 07:27:31 +01:00
Zaggy1024
7bc9824578 LibWeb: Remove useless moves of computed styles in AudioBox/VideoBox
Fixes a clang-tidy warning.
2026-02-23 07:27:31 +01:00
Aliaksandr Kalenik
eb210bb3af LibWeb: Replace OrderedHashMap with page-table Vector in LayoutState
Each NodeWithStyle is assigned a sequential layout index during the
pre-layout tree traversal. LayoutState stores UsedValues in a
PagedStore — a two-level page table indexed by layout_index that
gives O(1) lookup via two array accesses, with pages allocated
lazily on first write. UsedValues are stored directly in pages
(Optional<T>) rather than behind heap pointers, eliminating
per-entry malloc/free calls and improving cache locality.

This cuts ensure_used_values_for() from ~14% to ~7% in profiles
on https://www.nyan.cat/.
2026-02-23 01:13:35 +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
9f36972e06 LibWeb: Include zero-area boxes when measuring scrollable overflow
The CSS Overflow spec says scrollable overflow should include "the
scrollable overflow areas of all of the above boxes (including
zero-area boxes)", but we were skipping zero-area boxes entirely via
an early return. This meant elements like a position:relative container
that collapses to zero height (because its only child is absolutely
positioned) would never have their children's overflow counted.
2026-02-22 14:23:44 +01:00
Aliaksandr Kalenik
795222fab3 LibWeb: Validate grid-template-areas rectangles at parse time
Move grid area rectangle computation and validation from layout to the
CSS parser. Named grid areas that don't form filled-in rectangles now
correctly invalidate the declaration per spec.
2026-02-21 21:46:34 +01:00
Andreas Kling
1b41c9109d LibWeb: Cache is_body() on layout nodes
Cache the result of the body element check as a bool set once during
NodeWithStyle construction, instead of calling document().body() (which
walks the children of <html>) on every call. This is called from
PaintableBox::paint_background() for every box on every frame.

This was 0.9% of CPU time while playing a YouTube video.
2026-02-21 15:53:22 +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
65704e57fb LibWeb: Fix abspos replaced element constraint equations
The constraint equations for absolutely positioned replaced elements
only subtracted content width/height from the containing block size,
omitting padding and border.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/7820
2026-02-19 12:56:40 +01:00
Aliaksandr Kalenik
a19db375b5 LibWeb: Only zero scroll container min-content in columns
The previous fix for scroll containers in grid unconditionally set the
min-content contribution to 0 in both dimensions. This caused grids with
height:min-content to collapse rows containing scroll container items to
0 height. Restrict the check to the column dimension only, since scroll
containers can overflow and scroll horizontally but must still
contribute their content height for correct row sizing.
2026-02-19 03:07:15 +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
3c73457b26 LibWeb: Implement CSS Grid dense packing and fix span placement
Implement the `dense` keyword for `grid-auto-flow` so auto-placed items
backfill earlier gaps in the grid. The sparse/dense cursor logic is now
centralized in `place_grid_items()` step 4: dense resets the cursor to
the grid start before each search, while sparse keeps advancing forward.

Also fix a pre-existing bug where `find_unoccupied_place()` and several
placement helpers only checked if a single cell was unoccupied, ignoring
multi-cell spans. Add `OccupationGrid::is_area_occupied()` and use it
throughout to correctly verify the entire rectangular area is available.
2026-02-16 20:00:17 +01:00
Callum Law
3d3d0a50b6 LibWeb: Add generic Length::from_style_value method 2026-02-16 12:09:23 +00:00
Callum Law
846493831d LibWeb: Add generic number_from_style_value method 2026-02-16 12:09:23 +00:00
Aliaksandr Kalenik
d61fe8622c LibWeb: Skip outside-subtree paintable creation during partial relayout
During partial subtree relayout, `LayoutState` pre-populates
`used_values_per_layout_node` with nodes outside the relayout subtree
(e.g. ancestor SVG graphics boxes) so dependent data can be resolved.
`LayoutState::commit()` was still creating paintables for all entries,
including those pre-populated outside-subtree nodes.

That produced extra disconnected paintables on ancestors across repeated
SVG partial relayouts.

Guard the main paintable-creation loop with
`m_subtree_root->is_inclusive_ancestor_of(node)` so only subtree nodes
get new paintables, while outside-subtree nodes keep their existing
state.
2026-02-15 17:47:30 +01:00
Psychpsyo
a7267f711b LibWeb: Add overflow-clip-margin-* properties
The corner radius isn't quite right yet, but this gives us
another couple WPT passes for these.
2026-02-14 22:58:21 +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
Tim Ledbetter
d49a2dcfa1 LibWeb: Implement word-break functionality using UAX#14 line breaking
We now use ICU's line break iterator to determine soft wrap
opportunities, enabling correct line breaking for CJK text.
2026-02-14 16:23:18 -05:00