From ff5f80a196405c1797a3e7bd2c85771e172d9640 Mon Sep 17 00:00:00 2001 From: Jelle Raaijmakers Date: Fri, 15 Aug 2025 09:42:52 +0200 Subject: [PATCH] LibWeb: Add HTMLElement::uses_button_layout() This suits the spec a bit better, and exposes the fact that we were allowing `::ImageButton` to use the button layout although it is never specified that it should do so. Tests were rebaselined for this. --- Libraries/LibWeb/HTML/HTMLButtonElement.h | 3 + Libraries/LibWeb/HTML/HTMLElement.h | 3 + Libraries/LibWeb/HTML/HTMLInputElement.cpp | 23 +++ Libraries/LibWeb/HTML/HTMLInputElement.h | 1 + Libraries/LibWeb/Layout/TreeBuilder.cpp | 29 ++-- Tests/LibWeb/Layout/expected/input-file.txt | 136 ++++++++++-------- Tests/LibWeb/Layout/expected/input-image.txt | 12 -- .../Layout/expected/input-text-to-image.txt | 4 - 8 files changed, 112 insertions(+), 99 deletions(-) diff --git a/Libraries/LibWeb/HTML/HTMLButtonElement.h b/Libraries/LibWeb/HTML/HTMLButtonElement.h index 2dafbc81ad8..2a2ee516cae 100644 --- a/Libraries/LibWeb/HTML/HTMLButtonElement.h +++ b/Libraries/LibWeb/HTML/HTMLButtonElement.h @@ -87,6 +87,9 @@ public: GC::Ptr command_for_element() { return m_command_for_element; } void set_command_for_element(GC::Ptr value) { m_command_for_element = value; } + // https://html.spec.whatwg.org/multipage/rendering.html#the-button-element-2:button-layout-2 + virtual bool uses_button_layout() const override { return true; } + private: virtual void visit_edges(Visitor&) override; diff --git a/Libraries/LibWeb/HTML/HTMLElement.h b/Libraries/LibWeb/HTML/HTMLElement.h index be840e4771e..162c3c09ee2 100644 --- a/Libraries/LibWeb/HTML/HTMLElement.h +++ b/Libraries/LibWeb/HTML/HTMLElement.h @@ -165,6 +165,9 @@ public: bool is_form_associated_custom_element(); + // https://html.spec.whatwg.org/multipage/rendering.html#button-layout + virtual bool uses_button_layout() const { return false; } + protected: HTMLElement(DOM::Document&, DOM::QualifiedName); diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 2f28da4dfb7..9c3125de172 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -3622,4 +3622,27 @@ bool HTMLInputElement::is_mutable() const && !(has_attribute(AttributeNames::readonly) && is_allowed_to_be_readonly(m_type)); } +// https://html.spec.whatwg.org/multipage/rendering.html#button-layout +bool HTMLInputElement::uses_button_layout() const +{ + // https://html.spec.whatwg.org/multipage/rendering.html#the-input-element-as-a-button:button-layout-2 + // An input element whose type attribute is in the Submit Button, Reset Button, or Button state, when it generates a + // CSS box, is expected to depict a button and use button layout [..] + + // https://html.spec.whatwg.org/multipage/rendering.html#the-input-element-as-a-colour-well:button-layout-2 + // The element, when it generates a CSS box, is expected to use button layout, that has no child boxes of the + // anonymous button content box. + + // https://html.spec.whatwg.org/multipage/rendering.html#the-input-element-as-a-file-upload-control:button-layout-2 + // The button is expected to use button layout and match the '::file-selector-button' pseudo-element. + + // https://html.spec.whatwg.org/multipage/input.html#image-button-state-(type=image):concept-button + // The element is a button, specifically a submit button. + // NOTE: Although type=image is specified to be a submit button, that does not mean that the type attribute is + // Submit Button, so we don't include ::ImageButton below. + + return first_is_one_of(type_state(), TypeAttributeState::SubmitButton, TypeAttributeState::ResetButton, + TypeAttributeState::Button, TypeAttributeState::Color, TypeAttributeState::FileUpload); +} + } diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.h b/Libraries/LibWeb/HTML/HTMLInputElement.h index 5662eee295f..69dde7a2b01 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -253,6 +253,7 @@ public: virtual bool suffering_from_bad_input() const override; virtual bool is_mutable() const override; + virtual bool uses_button_layout() const override; private: HTMLInputElement(DOM::Document&, DOM::QualifiedName); diff --git a/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Libraries/LibWeb/Layout/TreeBuilder.cpp index 776177caefc..3fbd2c6d75e 100644 --- a/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -724,36 +724,23 @@ void TreeBuilder::update_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& void TreeBuilder::wrap_in_button_layout_tree_if_needed(DOM::Node& dom_node, GC::Ref layout_node) { - auto is_button_layout = [&] { - if (dom_node.is_html_button_element()) - return true; - if (!dom_node.is_html_input_element()) - return false; - // https://html.spec.whatwg.org/multipage/rendering.html#the-input-element-as-a-button - // An input element whose type attribute is in the Submit Button, Reset Button, or Button state, when it generates a CSS box, is expected to depict a button and use button layout - auto const& input_element = static_cast(dom_node); - if (input_element.is_button()) - return true; - return false; - }(); - - if (!is_button_layout) + auto const* html_element = as_if(dom_node); + if (!html_element || !html_element->uses_button_layout()) return; - auto display = layout_node->display(); - // 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 (is_button_layout && dom_node.layout_node()->computed_values().width().is_auto()) { + if (dom_node.layout_node()->computed_values().width().is_auto()) { auto& computed_values = as(*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 with the following behaviors: - if (is_button_layout && !display.is_grid_inside() && !display.is_flex_inside()) { + // 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 + // with the following behaviors: + auto display = layout_node->display(); + if (!display.is_grid_inside() && !display.is_flex_inside()) { auto& parent = *layout_node; // If the box does not overflow in the vertical axis, then it is centered vertically. diff --git a/Tests/LibWeb/Layout/expected/input-file.txt b/Tests/LibWeb/Layout/expected/input-file.txt index 427db14a0bc..fa060c392b0 100644 --- a/Tests/LibWeb/Layout/expected/input-file.txt +++ b/Tests/LibWeb/Layout/expected/input-file.txt @@ -1,83 +1,95 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline - BlockContainer at (0,0) content-size 800x38 [BFC] children: not-inline - BlockContainer at (8,8) content-size 784x22 children: inline - frag 0 from BlockContainer start: 0, length: 0, rect: [8,8 236.65625x22] baseline: 15.796875 + BlockContainer at (0,0) content-size 800x42 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x26 children: inline + frag 0 from BlockContainer start: 0, length: 0, rect: [8,8 236.65625x26] baseline: 15.796875 frag 1 from TextNode start: 0, length: 1, rect: [244.65625,10 8x18] baseline: 13.796875 " " - frag 2 from BlockContainer start: 0, length: 0, rect: [252.65625,8 255.34375x22] baseline: 15.796875 + frag 2 from BlockContainer start: 0, length: 0, rect: [252.65625,8 255.34375x26] baseline: 15.796875 frag 3 from TextNode start: 0, length: 1, rect: [508,10 8x18] baseline: 13.796875 " " - frag 4 from BlockContainer start: 0, length: 0, rect: [516,8 255.34375x22] baseline: 15.796875 - BlockContainer at (8,8) content-size 236.65625x22 inline-block [BFC] children: inline - frag 0 from BlockContainer start: 0, length: 0, rect: [13,10 94.375x18] baseline: 15.796875 - frag 1 from Label start: 0, length: 0, rect: [116.375,10 128.28125x18] baseline: 13.796875 - BlockContainer