LibWeb: Propagate style values in deep anonymous wrappers

The style propagation logic in `NodeWithStyle::apply_style()`
was incomplete for anonymous nodes created during layout
(e.g., within `wrap_in_button_layout_tree_if_needed`).

1.  **Non-inherited CSS values** were not being propagated to the
    anonymous wrappers.
2.  Propagation did not recurse into **nested anonymous wrappers**
    (descendants).

This fix adds calls to `propagate_non_inherit_values(child)` and
`child.propagate_style_to_anonymous_wrappers()`, ensuring all computed
styles reach the entire anonymous wrapper hierarchy.
This commit is contained in:
Lorenz A 2025-11-04 02:01:36 +01:00 committed by Jelle Raaijmakers
parent 46acdbd10a
commit 010b0b00ff
Notes: github-actions[bot] 2025-11-23 20:39:34 +00:00
4 changed files with 37 additions and 7 deletions

View file

@ -922,6 +922,15 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
box_node->propagate_style_along_continuation(computed_style);
}
void NodeWithStyle::propagate_non_inherit_values(NodeWithStyle& target_node) const
{
// NOTE: These properties are not inherited, but we still have to propagate them to anonymous wrappers.
target_node.mutable_computed_values().set_text_decoration_line(computed_values().text_decoration_line());
target_node.mutable_computed_values().set_text_decoration_thickness(computed_values().text_decoration_thickness());
target_node.mutable_computed_values().set_text_decoration_color(computed_values().text_decoration_color());
target_node.mutable_computed_values().set_text_decoration_style(computed_values().text_decoration_style());
}
void NodeWithStyle::propagate_style_to_anonymous_wrappers()
{
// Update the style of any anonymous wrappers that inherit from this node.
@ -940,6 +949,8 @@ void NodeWithStyle::propagate_style_to_anonymous_wrappers()
if (child.is_anonymous() && !is<TableWrapper>(child)) {
auto& child_computed_values = static_cast<CSS::MutableComputedValues&>(static_cast<CSS::ComputedValues&>(const_cast<CSS::ImmutableComputedValues&>(child.computed_values())));
child_computed_values.inherit_from(computed_values());
propagate_non_inherit_values(child);
child.propagate_style_to_anonymous_wrappers();
}
return IterationDecision::Continue;
});
@ -1019,13 +1030,7 @@ GC::Ref<NodeWithStyle> NodeWithStyle::create_anonymous_wrapper() const
{
auto wrapper = heap().allocate<BlockContainer>(const_cast<DOM::Document&>(document()), nullptr, computed_values().clone_inherited_values());
wrapper->mutable_computed_values().set_display(CSS::Display(CSS::DisplayOutside::Block, CSS::DisplayInside::Flow));
// NOTE: These properties are not inherited, but we still have to propagate them to anonymous wrappers.
wrapper->mutable_computed_values().set_text_decoration_line(computed_values().text_decoration_line());
wrapper->mutable_computed_values().set_text_decoration_thickness(computed_values().text_decoration_thickness());
wrapper->mutable_computed_values().set_text_decoration_color(computed_values().text_decoration_color());
wrapper->mutable_computed_values().set_text_decoration_style(computed_values().text_decoration_style());
propagate_non_inherit_values(*wrapper);
// CSS 2.2 9.2.1.1 creates anonymous block boxes, but 9.4.1 states inline-block creates a BFC.
// Set wrapper to inline-block to participate correctly in the IFC within the parent inline-block.
if (display().is_inline_block() && !has_children()) {

View file

@ -271,6 +271,7 @@ private:
virtual bool is_node_with_style() const final { return true; }
void reset_table_box_computed_values_used_by_wrapper_to_init_values();
void propagate_non_inherit_values(NodeWithStyle& target_node) const;
void propagate_style_to_anonymous_wrappers();
NonnullOwnPtr<CSS::ComputedValues> m_computed_values;

View file

@ -0,0 +1,8 @@
<!DOCTYPE html>
<style>
.btn {
color: white;
background: black;
}
</style>
<button type="button" class="btn">BUTTON</button>

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<link rel="match" href="../expected/button-hover-text-color-ref.html" />
<style>
.btn {
background: black;
color: red;
}
.btn:hover {
color: white;
}
</style>
<button type="button" class="btn">BUTTON</button>
<script>
requestAnimationFrame(() => internals.movePointerTo(15, 15));
</script>