diff --git a/Base/res/html/misc/textarea.html b/Base/res/html/misc/textarea.html index f6b9912e59e..9bb5cfea56b 100644 --- a/Base/res/html/misc/textarea.html +++ b/Base/res/html/misc/textarea.html @@ -1,5 +1,8 @@ - + + diff --git a/Tests/LibWeb/Layout/expected/textarea-content.txt b/Tests/LibWeb/Layout/expected/textarea-content.txt index a5ad26030fa..78909d52fc9 100644 --- a/Tests/LibWeb/Layout/expected/textarea-content.txt +++ b/Tests/LibWeb/Layout/expected/textarea-content.txt @@ -1,38 +1,38 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline - BlockContainer at (0,0) content-size 800x56.9375 [BFC] children: not-inline - BlockContainer at (8,8) content-size 784x40.9375 children: inline - line 0 width: 403.75, height: 40.9375, bottom: 40.9375, baseline: 13.53125 - frag 0 from BlockContainer start: 0, length: 0, rect: [11,11 191.875x34.9375] - frag 1 from TextNode start: 0, length: 1, rect: [206,8 8x17.46875] + BlockContainer at (0,0) content-size 800x65.6875 [BFC] children: not-inline + BlockContainer at (8,8) content-size 784x49.6875 children: inline + line 0 width: 502, height: 49.6875, bottom: 49.6875, baseline: 16.921875 + frag 0 from BlockContainer start: 0, length: 0, rect: [11,11 240x43.6875] + frag 1 from TextNode start: 0, length: 1, rect: [254,8 10x21.84375] " " - frag 2 from BlockContainer start: 0, length: 0, rect: [217,11 191.875x34.9375] + frag 2 from BlockContainer start: 0, length: 0, rect: [267,11 240x43.6875] TextNode <#text> - BlockContainer - diff --git a/Userland/Libraries/LibWeb/CSS/Default.css b/Userland/Libraries/LibWeb/CSS/Default.css index 55db64d477e..f61d198adb1 100644 --- a/Userland/Libraries/LibWeb/CSS/Default.css +++ b/Userland/Libraries/LibWeb/CSS/Default.css @@ -43,7 +43,7 @@ textarea { height: attr(rows lh, 2lh); } -input::placeholder { +input::placeholder, textarea::placeholder { color: GrayText; } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp index 2107379bf79..98780faaf53 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp @@ -47,6 +47,8 @@ void HTMLTextAreaElement::initialize(JS::Realm& realm) void HTMLTextAreaElement::visit_edges(Cell::Visitor& visitor) { Base::visit_edges(visitor); + visitor.visit(m_placeholder_element); + visitor.visit(m_placeholder_text_node); visitor.visit(m_inner_text_element); visitor.visit(m_text_node); } @@ -86,6 +88,8 @@ void HTMLTextAreaElement::reset_algorithm() m_dirty = false; // and set the raw value of element to its child text content. m_raw_value = child_text_content(); + + update_placeholder_visibility(); } void HTMLTextAreaElement::form_associated_element_was_inserted() @@ -181,9 +185,22 @@ void HTMLTextAreaElement::create_shadow_tree_if_needed() return; auto shadow_root = heap().allocate(realm(), document(), *this, Bindings::ShadowRootMode::Closed); + set_shadow_root(shadow_root); + auto element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); + MUST(shadow_root->append_child(element)); + + m_placeholder_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); + m_placeholder_element->set_use_pseudo_element(CSS::Selector::PseudoElement::Type::Placeholder); + MUST(element->append_child(*m_placeholder_element)); + + m_placeholder_text_node = heap().allocate(realm(), document(), String {}); + m_placeholder_text_node->set_data(get_attribute(HTML::AttributeNames::placeholder).value_or(String {})); + m_placeholder_text_node->set_editable_text_node_owner(Badge {}, *this); + MUST(m_placeholder_element->append_child(*m_placeholder_text_node)); m_inner_text_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); + MUST(element->append_child(*m_inner_text_element)); m_text_node = heap().allocate(realm(), document(), String {}); m_text_node->set_always_editable(true); @@ -191,11 +208,23 @@ void HTMLTextAreaElement::create_shadow_tree_if_needed() // NOTE: If `children_changed()` was called before now, `m_raw_value` will hold the text content. // Otherwise, it will get filled in whenever that does get called. m_text_node->set_text_content(m_raw_value); - MUST(m_inner_text_element->append_child(*m_text_node)); - MUST(element->append_child(*m_inner_text_element)); - MUST(shadow_root->append_child(element)); - set_shadow_root(shadow_root); + + update_placeholder_visibility(); +} + +void HTMLTextAreaElement::update_placeholder_visibility() +{ + if (!m_placeholder_element) + return; + if (!m_text_node) + return; + auto placeholder_text = get_attribute(AttributeNames::placeholder); + if (placeholder_text.has_value() && m_text_node->data().is_empty()) { + MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "block"sv)); + } else { + MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Display, "none"sv)); + } } // https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element:children-changed-steps @@ -207,6 +236,16 @@ void HTMLTextAreaElement::children_changed() m_raw_value = child_text_content(); if (m_text_node) m_text_node->set_text_content(m_raw_value); + update_placeholder_visibility(); + } +} + +void HTMLTextAreaElement::attribute_changed(FlyString const& name, Optional const& value) +{ + HTMLElement::attribute_changed(name, value); + if (name == HTML::AttributeNames::placeholder) { + if (m_placeholder_text_node) + m_placeholder_text_node->set_data(value.value_or(String {})); } } @@ -214,6 +253,8 @@ void HTMLTextAreaElement::did_edit_text_node(Badge) { // A textarea element's dirty value flag must be set to true whenever the user interacts with the control in a way that changes the raw value. m_dirty = true; + + update_placeholder_visibility(); } } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h index 359aaacbc8a..6d38ff9ef76 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h @@ -56,6 +56,8 @@ public: virtual bool is_auto_capitalize_inheriting() const override { return true; } // ^HTMLElement + virtual void attribute_changed(FlyString const&, Optional const&) override; + // https://html.spec.whatwg.org/multipage/forms.html#category-label virtual bool is_labelable() const override { return true; } @@ -94,6 +96,10 @@ private: void create_shadow_tree_if_needed(); + void update_placeholder_visibility(); + JS::GCPtr m_placeholder_element; + JS::GCPtr m_placeholder_text_node; + JS::GCPtr m_inner_text_element; JS::GCPtr m_text_node;