LibWeb: Make button layout wrappers inherit styles correctly

There are some nuances to creating these wrappers, such as manually
propagating certain text styles that are not inherited by default. We
already have the logic for this in
`NodeWithStyle::create_anonymous_wrapper()`, so reuse that method in our
implementation of the button layout.

Fixes applying certain text styles (such as `text-decoration`) to the
text of a `<button>`.
This commit is contained in:
Jelle Raaijmakers 2025-08-18 12:45:04 +02:00 committed by Jelle Raaijmakers
commit 9e29d0c040
Notes: github-actions[bot] 2025-08-19 09:13:29 +00:00
5 changed files with 36 additions and 15 deletions

View file

@ -734,27 +734,24 @@ void TreeBuilder::wrap_in_button_layout_tree_if_needed(DOM::Node& dom_node, GC::
// with the following behaviors: // with the following behaviors:
auto display = layout_node->display(); auto display = layout_node->display();
if (!display.is_grid_inside() && !display.is_flex_inside()) { if (!display.is_grid_inside() && !display.is_flex_inside()) {
auto& parent = *layout_node; auto& parent = as<NodeWithStyle>(*layout_node);
// If the box does not overflow in the vertical axis, then it is centered vertically. // If the box does not overflow in the vertical axis, then it is centered vertically.
// FIXME: Only apply alignment when box overflows // FIXME: Only apply alignment when box overflows
auto flex_computed_values = parent.computed_values().clone_inherited_values(); auto flex_wrapper = parent.create_anonymous_wrapper();
auto& mutable_flex_computed_values = static_cast<CSS::MutableComputedValues&>(*flex_computed_values); auto& flex_computed_values = flex_wrapper->mutable_computed_values();
mutable_flex_computed_values.set_display(CSS::Display { CSS::DisplayOutside::Block, CSS::DisplayInside::Flex }); flex_computed_values.set_display(CSS::Display { CSS::DisplayOutside::Block, CSS::DisplayInside::Flex });
mutable_flex_computed_values.set_justify_content(CSS::JustifyContent::Center); flex_computed_values.set_justify_content(CSS::JustifyContent::Center);
mutable_flex_computed_values.set_flex_direction(CSS::FlexDirection::Column); flex_computed_values.set_flex_direction(CSS::FlexDirection::Column);
mutable_flex_computed_values.set_height(CSS::Size::make_percentage(CSS::Percentage(100))); flex_computed_values.set_height(CSS::Size::make_percentage(CSS::Percentage(100)));
mutable_flex_computed_values.set_min_height(parent.computed_values().min_height()); flex_computed_values.set_min_height(parent.computed_values().min_height());
auto flex_wrapper = parent.heap().template allocate<BlockContainer>(parent.document(), nullptr, move(flex_computed_values));
auto content_box_computed_values = parent.computed_values().clone_inherited_values(); auto content_box_wrapper = parent.create_anonymous_wrapper();
auto content_box_wrapper = parent.heap().template allocate<BlockContainer>(parent.document(), nullptr, move(content_box_computed_values));
content_box_wrapper->set_children_are_inline(parent.children_are_inline()); content_box_wrapper->set_children_are_inline(parent.children_are_inline());
Vector<GC::Root<Node>> sequence; Vector<GC::Root<Node>> sequence;
for (auto child = parent.first_child(); child; child = child->next_sibling()) { for (auto child = parent.first_child(); child; child = child->next_sibling())
sequence.append(*child); sequence.append(*child);
}
for (auto& node : sequence) { for (auto& node : sequence) {
parent.remove_child(*node); parent.remove_child(*node);

View file

@ -4,7 +4,7 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline
frag 0 from BlockContainer start: 0, length: 0, rect: [13,19 0x0] baseline: 4 frag 0 from BlockContainer start: 0, length: 0, rect: [13,19 0x0] baseline: 4
BlockContainer <button> at (13,19) content-size 0x0 inline-block [BFC] children: not-inline BlockContainer <button> at (13,19) content-size 0x0 inline-block [BFC] children: not-inline
BlockContainer <(anonymous)> at (13,19) content-size 0x0 flex-container(column) [FFC] children: not-inline BlockContainer <(anonymous)> at (13,19) content-size 0x0 flex-container(column) [FFC] children: not-inline
BlockContainer <(anonymous)> at (13,19) content-size 0x0 [BFC] children: not-inline BlockContainer <(anonymous)> at (13,19) content-size 0x0 inline-block [BFC] children: not-inline
ViewportPaintable (Viewport<#document>) [0,0 800x600] ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x34] PaintableWithLines (BlockContainer<HTML>) [0,0 800x34]

View file

@ -4,7 +4,7 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline
frag 0 from BlockContainer start: 0, length: 0, rect: [13,19 0x0] baseline: 4 frag 0 from BlockContainer start: 0, length: 0, rect: [13,19 0x0] baseline: 4
BlockContainer <button#button> at (13,19) content-size 0x0 inline-block [BFC] children: not-inline BlockContainer <button#button> at (13,19) content-size 0x0 inline-block [BFC] children: not-inline
BlockContainer <(anonymous)> at (13,19) content-size 0x0 flex-container(column) [FFC] children: not-inline BlockContainer <(anonymous)> at (13,19) content-size 0x0 flex-container(column) [FFC] children: not-inline
BlockContainer <(anonymous)> at (13,19) content-size 0x0 [BFC] children: not-inline BlockContainer <(anonymous)> at (13,19) content-size 0x0 inline-block [BFC] children: not-inline
TextNode <#text> TextNode <#text>
TextNode <#text> TextNode <#text>
TextNode <#text> TextNode <#text>

View file

@ -0,0 +1,11 @@
SaveLayer
PushStackingContext opacity=1 isolate=false has_clip_path=false transform=[1 0 0 1 0 0]
PushStackingContext opacity=1 isolate=false has_clip_path=false transform=[1 0 0 1 0 0]
FillRect rect=[8,8 106x22] color=rgb(212, 208, 200)
FillPath
DrawGlyphRun rect=[13,10 96x18] translation=[13,23.796875] color=rgb(0, 0, 0) scale=1
DrawLine from=[13,26] to=[108,26] color=rgb(0, 0, 0) thickness=2
PopStackingContext
PopStackingContext
Restore

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<style>
button {
text-decoration: underline;
}
</style>
<button>foo the bar</button>
<script>
test(() => {
println(internals.dumpDisplayList());
});
</script>