LibWeb: Set fit-content width for buttons in used values, not computed

For button layouts, we were overriding the computed `width` value with
`fit-content` in `TreeBuilder::wrap_in_button_layout_if_needed()`. But
the spec asks us to set the _used value_ instead, so we now actually
calculate the fit-content width and set the box' content width to it.

Fixes #2516.
This commit is contained in:
Jelle Raaijmakers 2025-08-18 11:20:27 +02:00 committed by Sam Atkins
commit 087601832a
Notes: github-actions[bot] 2025-08-18 10:05:45 +00:00
5 changed files with 59 additions and 12 deletions

View file

@ -98,8 +98,7 @@ void BlockFormattingContext::run(AvailableSpace const& available_space)
auto new_y = -((legend_height) / 2) - fieldset_state.padding_top;
legend_state.set_content_y(new_y);
// If the computed value of 'inline-size' is 'auto',
// then the used value is the fit-content inline size.
// If the computed value of 'inline-size' is 'auto', then the used value is the fit-content inline size.
if (legend->computed_values().width().is_auto()) {
auto width = calculate_fit_content_width(*legend, available_space);
legend_state.set_content_width(width);
@ -224,6 +223,14 @@ void BlockFormattingContext::compute_width(Box const& box, AvailableSpace const&
box_state.padding_left = padding_left.to_px(box);
box_state.padding_right = padding_right.to_px(box);
// https://html.spec.whatwg.org/multipage/rendering.html#button-layout
// If the computed value of 'inline-size' is 'auto', then the used value is the fit-content inline size.
if (auto const* html_element = as_if<HTML::HTMLElement>(box.dom_node()); html_element
&& html_element->uses_button_layout() && computed_values.width().is_auto()) {
box_state.set_content_width(calculate_fit_content_width(box, available_space));
return;
}
// NOTE: If we are calculating the min-content or max-content width of this box,
// and the width should be treated as auto, then we can simply return here,
// as the preferred width and min/max constraints are irrelevant for intrinsic sizing.

View file

@ -1403,9 +1403,8 @@ void FormattingContext::compute_inset(NodeWithStyleAndBoxModelMetrics const& box
// https://drafts.csswg.org/css-sizing-3/#fit-content-size
CSSPixels FormattingContext::calculate_fit_content_width(Layout::Box const& box, AvailableSpace const& available_space) const
{
// If the available space in a given axis is definite,
// equal to clamp(min-content size, stretch-fit size, max-content size)
// (i.e. max(min-content size, min(max-content size, stretch-fit size))).
// If the available space in a given axis is definite, equal to clamp(min-content size, stretch-fit size,
// max-content size) (i.e. max(min-content size, min(max-content size, stretch-fit size))).
if (available_space.width.is_definite()) {
return max(calculate_min_content_width(box),
min(calculate_stretch_fit_width(box, available_space.width),

View file

@ -728,13 +728,6 @@ void TreeBuilder::wrap_in_button_layout_tree_if_needed(DOM::Node& dom_node, GC::
if (!html_element || !html_element->uses_button_layout())
return;
// https://html.spec.whatwg.org/multipage/rendering.html#button-layout
// If the computed value of 'inline-size' is 'auto', then the used value is the fit-content inline size.
if (dom_node.layout_node()->computed_values().width().is_auto()) {
auto& computed_values = as<NodeWithStyle>(*dom_node.layout_node()).mutable_computed_values();
computed_values.set_width(CSS::Size::make_fit_content());
}
// https://html.spec.whatwg.org/multipage/rendering.html#button-layout
// If the element is an input element, or if it is a button element and its computed value for 'display' is not
// 'inline-grid', 'grid', 'inline-flex', or 'flex', then the element's box has a child anonymous button content box

View file

@ -0,0 +1,36 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x56 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x40 children: not-inline
Box <div> at (8,8) content-size 784x22 flex-container(column) [FFC] children: not-inline
BlockContainer <button> at (13,10) content-size 774x18 flex-item [BFC] children: not-inline
BlockContainer <(anonymous)> at (13,10) content-size 774x18 flex-container(column) [FFC] children: not-inline
BlockContainer <(anonymous)> at (13,10) content-size 774x18 flex-item [BFC] children: inline
frag 0 from TextNode start: 0, length: 3, rect: [386.421875,10 27.15625x18] baseline: 13.796875
"foo"
TextNode <#text>
BlockContainer <(anonymous)> at (8,30) content-size 784x0 children: inline
TextNode <#text>
Box <div> at (8,30) content-size 784x18 flex-container(column) [FFC] children: not-inline
BlockContainer <span> at (8,30) content-size 784x18 flex-item [BFC] children: inline
frag 0 from TextNode start: 0, length: 3, rect: [8,30 27.640625x18] baseline: 13.796875
"bar"
TextNode <#text>
BlockContainer <(anonymous)> at (8,48) content-size 784x0 children: inline
TextNode <#text>
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x56]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x40]
PaintableBox (Box<DIV>) [8,8 784x22]
PaintableWithLines (BlockContainer<BUTTON>) [8,8 784x22]
PaintableWithLines (BlockContainer(anonymous)) [13,10 774x18]
PaintableWithLines (BlockContainer(anonymous)) [13,10 774x18]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer(anonymous)) [8,30 784x0]
PaintableBox (Box<DIV>) [8,30 784x18]
PaintableWithLines (BlockContainer<SPAN>) [8,30 784x18]
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer(anonymous)) [8,48 784x0]
SC for Viewport<#document> [0,0 800x600] [children: 1] (z-index: auto)
SC for BlockContainer<HTML> [0,0 800x56] [children: 0] (z-index: auto)

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<style>
div {
display: flex;
flex-direction: column;
}
span {
background-color: blue;
}
</style>
<div><button>foo</button></div>
<div><span>bar</span></div>