diff --git a/Libraries/LibWeb/CSS/ComputedProperties.cpp b/Libraries/LibWeb/CSS/ComputedProperties.cpp index 8cb35327fce..bcb1205aea9 100644 --- a/Libraries/LibWeb/CSS/ComputedProperties.cpp +++ b/Libraries/LibWeb/CSS/ComputedProperties.cpp @@ -833,8 +833,6 @@ Appearance ComputedProperties::appearance() const auto appearance = keyword_to_appearance(value.to_keyword()).release_value(); switch (appearance) { // Note: All these compatibility values can be treated as 'auto' - case Appearance::Textfield: - case Appearance::MenulistButton: case Appearance::Searchfield: case Appearance::Textarea: case Appearance::PushButton: @@ -849,6 +847,10 @@ Appearance ComputedProperties::appearance() const case Appearance::Button: appearance = Appearance::Auto; break; + // NB: values behave like auto but can also have an effect. Preserve them. + case Appearance::Textfield: + case Appearance::MenulistButton: + break; default: break; } diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp index ad459796c11..ca083de0542 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -86,6 +86,8 @@ void HTMLInputElement::visit_edges(Cell::Visitor& visitor) visitor.visit(m_text_node); visitor.visit(m_placeholder_element); visitor.visit(m_placeholder_text_node); + visitor.visit(m_up_button_element); + visitor.visit(m_down_button_element); visitor.visit(m_color_well_element); visitor.visit(m_file_button); visitor.visit(m_file_label); @@ -793,6 +795,31 @@ static GC::Ref inner_text_style_when_hidden() return *style; } +static GC::Ref stepper_button_style_when_visible() +{ + static GC::Root style; + if (!style) { + style = CSS::CSSStyleProperties::create(internal_css_realm(), {}, {}); + style->set_declarations_from_text(R"~~~( + padding: 0; + cursor: default; + )~~~"sv); + } + return *style; +} + +static GC::Ref stepper_button_style_when_hidden() +{ + static GC::Root style; + if (!style) { + style = CSS::CSSStyleProperties::create(internal_css_realm(), {}, {}); + style->set_declarations_from_text(R"~~~( + display: none; + )~~~"sv); + } + return *style; +} + static GC::Ref placeholder_style_when_visible() { static GC::Root style; @@ -871,6 +898,17 @@ void HTMLInputElement::update_text_input_shadow_tree() m_text_node->set_data(m_value); update_placeholder_visibility(); } + + if (m_type == TypeAttributeState::Number) { + // The `textfield` appearance is used to hide the stepper buttons. + if (auto style = computed_properties(); style && style->appearance() == CSS::Appearance::Textfield) { + m_up_button_element->set_inline_style(stepper_button_style_when_hidden()); + m_down_button_element->set_inline_style(stepper_button_style_when_hidden()); + } else { + m_up_button_element->set_inline_style(stepper_button_style_when_visible()); + m_down_button_element->set_inline_style(stepper_button_style_when_visible()); + } + } } // https://html.spec.whatwg.org/multipage/input.html#the-input-element:attr-input-readonly-3 @@ -1093,29 +1131,22 @@ void HTMLInputElement::create_text_input_shadow_tree() m_placeholder_text_node = realm().create(document(), Utf16String::from_utf8(placeholder())); MUST(m_placeholder_element->append_child(*m_placeholder_text_node)); - update_placeholder_visibility(); - if (type_state() == TypeAttributeState::Number) { // Up button - auto up_button = MUST(DOM::create_element(document(), HTML::TagNames::button, Namespace::HTML)); - // FIXME: This cursor property doesn't work - up_button->set_attribute_value(HTML::AttributeNames::style, R"~~~( - padding: 0; - cursor: default; - )~~~"_string); + m_up_button_element = MUST(DOM::create_element(document(), HTML::TagNames::button, Namespace::HTML)); auto up_button_svg = MUST(DOM::create_element(document(), SVG::TagNames::svg, Namespace::SVG)); up_button_svg->set_attribute_value(HTML::AttributeNames::style, "width: 1em; height: 1em;"_string); up_button_svg->set_attribute_value(SVG::AttributeNames::xmlns, Namespace::SVG.to_string()); up_button_svg->set_attribute_value(SVG::AttributeNames::viewBox, "0 0 24 24"_string); - MUST(up_button->append_child(up_button_svg)); + MUST(m_up_button_element->append_child(up_button_svg)); auto up_button_svg_path = MUST(DOM::create_element(document(), SVG::TagNames::path, Namespace::SVG)); up_button_svg_path->set_attribute_value(SVG::AttributeNames::fill, "currentColor"_string); up_button_svg_path->set_attribute_value(SVG::AttributeNames::d, "M7.41,15.41L12,10.83L16.59,15.41L18,14L12,8L6,14L7.41,15.41Z"_string); MUST(up_button_svg->append_child(up_button_svg_path)); - MUST(element->append_child(up_button)); + MUST(element->append_child(*m_up_button_element)); auto mouseup_callback_function = JS::NativeFunction::create( realm(), [this](JS::VM&) { @@ -1137,28 +1168,24 @@ void HTMLInputElement::create_text_input_shadow_tree() }, 0, Utf16FlyString {}, &realm()); auto step_up_callback = realm().heap().allocate(*up_callback_function, realm()); - up_button->add_event_listener_without_options(UIEvents::EventNames::mousedown, DOM::IDLEventListener::create(realm(), step_up_callback)); - up_button->add_event_listener_without_options(UIEvents::EventNames::mouseup, DOM::IDLEventListener::create(realm(), mouseup_callback)); + m_up_button_element->add_event_listener_without_options(UIEvents::EventNames::mousedown, DOM::IDLEventListener::create(realm(), step_up_callback)); + m_up_button_element->add_event_listener_without_options(UIEvents::EventNames::mouseup, DOM::IDLEventListener::create(realm(), mouseup_callback)); // Down button - auto down_button = MUST(DOM::create_element(document(), HTML::TagNames::button, Namespace::HTML)); - down_button->set_attribute_value(HTML::AttributeNames::style, R"~~~( - padding: 0; - cursor: default; - )~~~"_string); + m_down_button_element = MUST(DOM::create_element(document(), HTML::TagNames::button, Namespace::HTML)); auto down_button_svg = MUST(DOM::create_element(document(), SVG::TagNames::svg, Namespace::SVG)); down_button_svg->set_attribute_value(HTML::AttributeNames::style, "width: 1em; height: 1em;"_string); down_button_svg->set_attribute_value(SVG::AttributeNames::xmlns, Namespace::SVG.to_string()); down_button_svg->set_attribute_value(SVG::AttributeNames::viewBox, "0 0 24 24"_string); - MUST(down_button->append_child(down_button_svg)); + MUST(m_down_button_element->append_child(down_button_svg)); auto down_button_svg_path = MUST(DOM::create_element(document(), SVG::TagNames::path, Namespace::SVG)); down_button_svg_path->set_attribute_value(SVG::AttributeNames::fill, "currentColor"_string); down_button_svg_path->set_attribute_value(SVG::AttributeNames::d, "M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"_string); MUST(down_button_svg->append_child(down_button_svg_path)); - MUST(element->append_child(down_button)); + MUST(element->append_child(*m_down_button_element)); auto down_callback_function = JS::NativeFunction::create( realm(), [this](JS::VM&) { @@ -1170,9 +1197,11 @@ void HTMLInputElement::create_text_input_shadow_tree() }, 0, Utf16FlyString {}, &realm()); auto step_down_callback = realm().heap().allocate(*down_callback_function, realm()); - down_button->add_event_listener_without_options(UIEvents::EventNames::mousedown, DOM::IDLEventListener::create(realm(), step_down_callback)); - down_button->add_event_listener_without_options(UIEvents::EventNames::mouseup, DOM::IDLEventListener::create(realm(), mouseup_callback)); + m_down_button_element->add_event_listener_without_options(UIEvents::EventNames::mousedown, DOM::IDLEventListener::create(realm(), step_down_callback)); + m_down_button_element->add_event_listener_without_options(UIEvents::EventNames::mouseup, DOM::IDLEventListener::create(realm(), mouseup_callback)); } + + update_text_input_shadow_tree(); } void HTMLInputElement::create_color_input_shadow_tree() @@ -1531,6 +1560,12 @@ void HTMLInputElement::type_attribute_changed(TypeAttributeState old_state, Type } } +void HTMLInputElement::computed_properties_changed() +{ + create_shadow_tree_if_needed(); + update_shadow_tree(); +} + // https://html.spec.whatwg.org/multipage/input.html#radio-button-state-(type=radio):signal-a-type-change void HTMLInputElement::signal_a_type_change() { diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.h b/Libraries/LibWeb/HTML/HTMLInputElement.h index 88bc61bb415..c2f8c899a0d 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -257,6 +257,7 @@ private: HTMLInputElement(DOM::Document&, DOM::QualifiedName); void type_attribute_changed(TypeAttributeState old_state, TypeAttributeState new_state); + virtual void computed_properties_changed() override; virtual bool is_presentational_hint(FlyString const&) const override; virtual void apply_presentational_hints(GC::Ref) const override; @@ -349,6 +350,8 @@ private: GC::Ptr m_inner_text_element; GC::Ptr m_text_node; bool m_checked { false }; + GC::Ptr m_up_button_element; + GC::Ptr m_down_button_element; void update_color_well_element(); GC::Ptr m_color_well_element; diff --git a/Tests/LibWeb/Layout/expected/number-input-appearance.txt b/Tests/LibWeb/Layout/expected/number-input-appearance.txt new file mode 100644 index 00000000000..ac304ad860b --- /dev/null +++ b/Tests/LibWeb/Layout/expected/number-input-appearance.txt @@ -0,0 +1,96 @@ +Viewport <#document> at [0,0] [0+0+0 800 0+0+0] [0+0+0 600 0+0+0] children: not-inline + BlockContainer at [0,0] [0+0+0 800 0+0+0] [0+0+0 40 0+0+0] [BFC] children: not-inline + BlockContainer at [8,8] [8+0+0 784 0+0+8] [8+0+0 24 0+0+8] children: inline + frag 0 from BlockContainer start: 0, length: 0, rect: [9,9 200x22] baseline: 17 + frag 1 from TextNode start: 0, length: 1, rect: [210,11 8x18] baseline: 13.796875 + " " + frag 2 from BlockContainer start: 0, length: 0, rect: [219,9 200x22] baseline: 17 + frag 3 from TextNode start: 0, length: 1, rect: [420,11 8x18] baseline: 13.796875 + " " + frag 4 from BlockContainer start: 0, length: 0, rect: [429,11 200x20] baseline: 14.796875 + BlockContainer at [9,9] inline-block [0+1+0 200 0+1+0] [0+1+0 22 0+1+0] [BFC] children: not-inline + Box
at [11,10] flex-container(row) [0+0+2 196 2+0+0] [0+0+1 20 1+0+0] [FFC] children: not-inline + BlockContainer
at [11,11] flex-item [0+0+0 160 0+0+0] [0+0+0 18 0+0+0] [BFC] children: inline + frag 0 from TextNode start: 0, length: 1, rect: [11,11 6.34375x18] baseline: 13.796875 + "1" + TextNode <#text> (not painted) + BlockContainer