Commit Graph

935 Commits

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