Commit graph

955 commits

Author SHA1 Message Date
Pavel Shliak
166c02089d LibWeb: Preserve column indices across colgroups
Keep the collected column index across table-column-group siblings, so
columns in later colgroups are not recorded as column 0.
2026-05-14 12:41:54 +01:00
Pavel Shliak
f50165dcde LibWeb: Correct right colgroup border lookup
When collecting the left edge of the column group to the right, look up
the column at column_index + column_span.
2026-05-14 12:41:54 +01:00
Tim Ledbetter
4f33ab15e0 LibWeb: Don't apply self-alignment to grid items with auto margins
An auto margin on a grid item absorbs positive free space prior to
alignment via the box alignment properties, disabling the effects
of any self-alignment in that axis. We were applying both
auto-margin absorption and alignment additively, which doubled the
offset and pushed the item outside its grid area when, for example,
`margin-top: auto` was combined with `align-items: center`.
2026-05-14 13:29:40 +02:00
Andreas Kling
3f1be9d93c LibWeb: Clamp grid item contributions by calculated max size
Grid item intrinsic size contributions only honored maximum sizes
written as plain lengths. Definite calculated sizes should clamp those
contributions as well. Otherwise, an item with a larger preferred width
can inflate a flexible grid track before its used size is later capped.

This made Reddit's main page grid compute a 1120px content column inside
a 1280px viewport, leaving the capped content centered in an oversized
track and making the page horizontally scrollable.
2026-05-13 20:54:10 +02:00
Andreas Kling
cc381d2dc8 LibWeb: Avoid inflating grid min-content flex tracks
Flexible grid tracks with fixed minimum sizing functions should not grow
from min-content constraints. A nested grid using minmax(0, 1fr)
could otherwise bubble aspect-ratio overflow up into an ancestor auto
track, making Shopify horizontally scrollable.

Keep those tracks at their fixed minimum during min-content sizing so
the overflow remains inside the intended scroll container. Add a
test covering the nested grid shape from Shopify's homepage.
2026-05-13 20:54:10 +02:00
mikiubo
b9ed64d1ce LibWeb: Add dedicated overflow-clip-margin style value
Store overflow-clip-margin as a structured style value instead of
collapsing it into generic keyword/length lists during parsing.

Keep omitted visual-box as a distinct state so painting can apply
the spec default per element type (content-box for replaced,
padding-box otherwise), while preserving canonical serialization.
2026-05-12 14:09:13 +02:00
Andreas Kling
b95b3ecd6d LibWeb: Unregister image observers when finalizing layout nodes
Clear NodeWithStyle image observers during finalization so pending image
loads cannot call back into observers owned by unreachable layout nodes.
Incremental sweep leaves finalized cells allocated until their block is
swept, so waiting for the C++ destructor is too late.
2026-05-10 10:58:11 +02:00
Sam Atkins
d7344a96bb LibWeb: Parse and propagate container and container-name properties 2026-05-07 17:48:53 +02:00
Aliaksandr Kalenik
f8640d813a LibGfx+LibWeb: Make DecodedImageFrame a value type
DecodedImageFrame only wraps a ref-counted Bitmap and color-space
metadata. The frame object itself does not provide shared mutable
state or lifetime ownership beyond those members, so ref-counting it
adds an unnecessary layer of indirection.
2026-05-07 16:08:13 +02:00
Aliaksandr Kalenik
568b7ce7ea LibWeb: Make Paintable tree ref-counted
The Paintable tree and its supplemental painting data structures were
GC allocated because that was the easiest way to manage it and avoid
leaks introduced by ref cycles. This included the Paintable subclasses
themselves plus StackingContext, ChromeWidget, Scrollbar, ResizeHandle,
and scroll-frame state.

We are now trying to reduce GC allocation churn on layout and painting
updates, so keeping this short-lived rendering tree outside the JS heap
is a better fit. Move Paintable to RefCountedTreeNode, make painting
helpers ref-counted or weakly reference Paintables, and update the
layout and event-handler call sites to use RefPtr/WeakPtr ownership.
2026-05-07 15:03:44 +02:00
Tim Ledbetter
cee28a2af5 LibWeb: Skip BlockContainer children when sizing inline paintables
Inline paintables are sized by walking their paintable subtree. The
traversal already ignored `BlockContainer` paintables, but it still
visited their children, so an inline element could include internal
fragments from an inline-block child when computing its own geometry.
We now skip the entire `BlockContainer` subtree, so widths are not
double counted and offsets are not pulled from a different containing
block.
2026-05-07 12:01:04 +02:00
Callum Law
23e5a4ed64 LibWeb: Always apply CSS stroke/fill property changes
Previously changes to `none` from any other value would be ignored. This
also moves handling into `ComputedProperties` in line with other
properties.
2026-05-07 10:07:09 +01:00
Aliaksandr Kalenik
76c79ee522 LibGfx: Remove ImmutableBitmap
DecodedImageFrame now owns decoded bitmap pixels directly, so the
separate ImmutableBitmap wrapper no longer carries useful semantics.
Remove the class and pass decoded image frames or bitmaps at the
boundaries where pixels are actually required.

The Skia image cache now keys off DecodedImageFrame, matching the
display-list commands that paint decoded images. Video frames stay
owned by LibMedia, with the explicit YUV-to-bitmap conversion living
at HTMLVideoElement's decoded-frame entry point for canvas and WebGL
callers.
2026-05-05 14:39:17 -05:00
Aliaksandr Kalenik
40f2abb7fe LibGfx+LibWeb: Add DecodedImageFrame
Decoded image data should not continue to traffic in ImmutableBitmap now
that the bitmap wrapper is being retired. Introduce DecodedImageFrame as
the paintable decoded-image unit and store a Bitmap plus ColorSpace in
it directly.

Thread the new frame type through decoded image data, display-list
image commands, filters, canvas drawImage, patterns, WebGL texture
upload, and CSS/SVG image consumers. ImmutableBitmap remains only at
the legacy boundaries that still need it, such as HTML video snapshots
and callers that explicitly ask for a bitmap snapshot.

This keeps color-space ownership with the decoded frame while making
the expensive or legacy ImmutableBitmap path explicit at the few call
sites that still need it.
2026-05-05 14:39:17 -05:00
Tim Ledbetter
78b47109af LibWeb: Use the ASCII line segmenter for eligible text 2026-05-05 18:59:07 +02:00
Aliaksandr Kalenik
63c8f6761b LibWeb: Clear stale layout state for shadow descendants
When an element stops generating a layout node, subtree cleanup
does not reach its shadow descendants.

Walk the shadow-including subtree instead, so IntersectionObserver
cannot later query geometry through stale paintables.
2026-05-04 11:57:33 +02:00
Aliaksandr Kalenik
8c55c705d5 LibWeb: Clear stale layout state for hidden slot slottables
When a slot gets content-visibility: hidden, the existing subtree
cleanup does not reach assigned slottables, since they are not DOM
descendants of the slot.

Clear layout and paint nodes from those assigned subtrees as well,
so IntersectionObserver cannot later query geometry through stale
paintables.
2026-05-04 11:57:33 +02:00
Jelle Raaijmakers
7f0ae9bd4c LibWeb: Derive inline flex/grid baselines from the first child line
When an inline flex/grid container has no line boxes of its own, we
derive its baseline from a child box. The generic recursive path can
then use the child's last wrapped line, even though the flex/grid
baseline should come from the child item's alignment baseline.

Use the selected child's first line box directly for inline flex/grid
containers, avoiding an incorrect baseline when that child wraps.
2026-04-30 17:34:25 +02:00
Psychpsyo
d74aa418f7 LibWeb: Position list marker to the right of right-to-left text 2026-04-30 16:20:10 +02:00
Tim Ledbetter
4919363a37 LibWeb: Move rarely-used UsedValues properties behind a lazy pointer
This reduces the size of the `UsedValues` struct from 424 to 176 bytes.
2026-04-30 12:30:16 +02:00
Andreas Kling
ba23d6a8ca LibWeb: Fix abspos containing block for split inline ancestors
When an inline-relative is split by block-level descendants, the rect
computation only looked at one anonymous wrapper and returned empty
for everything else, sending abspos placement back to the initial
containing block. On Reddit this let an inline-relative ad host's <a>
overlay the entire viewport and steal clicks from the post gallery's
pager buttons.

Walk every descendant of the inline's real block container instead,
collecting fragments from any InlineNode part of the inline plus the
border-box rects of its in-flow Box descendants. Matches what other
engines expose via getClientRects() for split inlines.

While here, narrow the inline-CB detection to the triggers that
actually apply on non-atomic inlines: `position`, `filter`,
`backdrop-filter` (and their will-change hints). transform, contain
and the rest don't apply per their specs - the broader check would
have started routing the WPT contain-paint/contain-layout ib-split
tests through the inline path once the rect computation began
returning non-empty results.
2026-04-29 04:54:11 +02:00
Tim Ledbetter
e495db44d5 LibWeb: Notify only affected layout nodes when a CSS image loads
Previously, `Document::notify_css_background_image_loaded()` walked the
entire `PaintableBox` subtree and cleared each box's paintable cache
whenever any CSS image finished loading.

Replace this with per-image observers owned by the layout node. During
`apply_style`, each node registers as an `ImageStyleValue::Client` for
the images its style references. On load, only the affected layout
node's paintables are invalidated.
2026-04-29 04:33:35 +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
Andreas Kling
89e5deb7b9 LibWeb: Centralize style scope lookup for invalidation
Several invalidation paths need to consider not only a node's own root
scope, but also shadow scopes that can observe the node through :host(),
::slotted(), or ::part() selectors. Each caller open-coded that
traversal, which made the dir/lang and dir=auto fixes carry the same
shadow-boundary logic in multiple places.

Add Node helpers for resolving a node's style scope and for visiting
every style scope that may observe that node. Use them from the
property, child-list, dir/lang, and dir=auto invalidation paths, and
share the same style-scope lookup with DOM::AbstractElement and
Layout::NodeWithStyle.
2026-04-26 10:40:58 +02:00
Johan Dahlin
8c49029bb7 LibGfx+LibWeb: Only trigger @font-face loads from text shaping 2026-04-25 17:06:28 +02:00
Andreas Kling
a226d778e6 LibWeb: Visit abstract list-style images
List-style images can now be abstract image values such as image-set(),
not just plain URL images. Visit the stored abstract image directly so
GC-backed resources held by those image values stay reachable.
2026-04-25 14:54:10 +02:00
Andreas Kling
61d79a1e47 LibWeb: Parse CSS image-set()
Add an abstract image style value for image-set() and parse both the
standard and -webkit-prefixed spellings through the existing <image>
value path. The parser accepts URL and string image candidates,
optional resolution descriptors, and type() filters.

Track attr-taint through substituted component values so image-set()
candidates using attr()-derived URL-producing tokens are rejected when
resolved for URL-using properties.

Update the relevant WPT baselines now that image-set() parsing is
supported in additional value contexts.
2026-04-25 14:54:10 +02:00
Tim Ledbetter
30f8e7f80c LibWeb: Include non-auto cross margins in auto-margin resolution
When resolving cross-axis auto margins on a flex item, the outer cross
size calculation omitted all cross-axis margins. We now include
non-auto margins as part of the outer cross size treating auto margins
as zero.
2026-04-25 14:46:12 +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
Aliaksandr Kalenik
0bfe4677ae LibWeb: Verify whitespace font has the glyph in font_for_space()
When inline layout emits a whitespace chunk, it previously selected the
surrounding text's font without checking whether that font actually
contains a glyph for the whitespace codepoint. On pages that use
`@font-face` rules sharded by `unicode-range` (e.g. a Roboto webfont
split across one file for Cyrillic letters and another for basic Latin),
the shard covering the letters is picked for an adjacent space even
though the space codepoint lives in a different shard. HarfBuzz then
shapes the space with a font that has no glyph for it and emits
`.notdef`, rendering spaces as tofu boxes.

Check `contains_glyph(space_code_point)` on each candidate in
`font_for_space()` and fall through to
`FontCascadeList::font_for_code_point()` for the whitespace codepoint
when no surrounding font has the glyph.

Fixes whitespace rendering on web.telegram.org/a.
2026-04-22 20:27:41 +02: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
Zaggy1024
04cc5bced9 LibWeb: Update video elements' natural dimensions during playback
This tightens the implementation of video element sizing to the spec by
implementing two spec concepts:
- The media resource's natural width and height, and
- The video element's natural width and height.
The element's natural dimensions change based on the representation,
which has many inputs, so update checks are triggered from many
locations.

The resize event is fired when the media resource's natural dimensions
change, and the layout is invalidated if the element's natural
dimensions change.

Tests for a few important resize triggers have been added.
2026-04-21 19:11:24 -05:00
Tim Ledbetter
294a0efe2a LibWeb: Mark ellipsized fragments as hidden rather than removing them
Previously, when applying `text-overflow: ellipsis`, line box fragments
after the  ellipsis point were removed from the line box. This
invalidated fragment indices stored in `containing_line_box_fragment`,
causing an out-of-bounds access in `LayoutState::commit()` when
resolving atomic inline positions.

Instead of removing fragments, mark them as hidden. This preserves
index stability while preventing hidden fragments from being displayed.
2026-04-17 08:03:26 +02: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
Callum Law
64ccb9a015 LibWeb: Make @counter-style tree-scoped
This was a pretty straightforward change of storing registered counter
styles on the relevant `StyleScope`s and resolving by following the
process to dereference a global tree-scoped name, the only things of
note are:
 - We only define predefined counter styles (e.g. decimal) on the
   document's scope (since otherwise overrides in outer scopes would
   themselves be overriden).
 - When registering counter styles we don't have the full list of
   extendable styles so we defer fallback to "decimal" for undefined
   styles until `CounterStyle::from_counter_style_definition`.
2026-04-15 11:07:38 +01:00
Callum Law
fedd1f8d55 LibWeb: Add method to get style scope from layout node 2026-04-15 11:07:38 +01:00
Callum Law
281028944e LibWeb: Store GridTrackSize::m_value as a StyleValue
This simplifies handling (particularly around absolutization and
interpolation) and allows us to support calculated flex values (which
will come in a later commit).
2026-04-09 21:41:49 +01:00
R-Goc
094e4bacf8 LibWeb: Deduplicate gap_to_px
This commit creates one common gap_to_px function. This resolves problem
in the unity build.
2026-04-09 08:33:40 -04:00
R-Goc
d1cd391017 LibWeb: Deduplicate inline_axis functions
This commit deduplicates inline_axis_is_reversed and
block_axis_is_reversed.
2026-04-09 08:33:40 -04: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
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