diff --git a/Userland/Libraries/LibWeb/CSS/Default.css b/Userland/Libraries/LibWeb/CSS/Default.css index 8074793c2b..ac5b178688 100644 --- a/Userland/Libraries/LibWeb/CSS/Default.css +++ b/Userland/Libraries/LibWeb/CSS/Default.css @@ -38,7 +38,10 @@ input, textarea { textarea { padding: 2px; display: inline-block; - overflow: scroll; + overflow: auto; + font-family: monospace; + width: attr(cols ch, 20ch); + height: attr(rows lh, 2lh); } input[type=submit], input[type=button], input[type=reset], input[type=checkbox], input[type=radio] { diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp index ac1443b100..af348fdf95 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp @@ -1,27 +1,75 @@ /* * Copyright (c) 2020, the SerenityOS developers. + * Copyright (c) 2023, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ #include +#include +#include +#include +#include +#include +#include +#include #include +#include namespace Web::HTML { HTMLTextAreaElement::HTMLTextAreaElement(DOM::Document& document, DOM::QualifiedName qualified_name) : HTMLElement(document, move(qualified_name)) + , m_raw_value(DeprecatedString::empty()) { } HTMLTextAreaElement::~HTMLTextAreaElement() = default; +JS::GCPtr HTMLTextAreaElement::create_layout_node(NonnullRefPtr style) +{ + // AD-HOC: We rewrite `display: inline` to `display: inline-block`. + // This is required for the internal shadow tree to work correctly in layout. + if (style->display().is_inline_outside() && style->display().is_flow_inside()) + style->set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::InlineBlock))); + + return Element::create_layout_node_for_display_type(document(), style->display(), style, this); +} + void HTMLTextAreaElement::initialize(JS::Realm& realm) { Base::initialize(realm); set_prototype(&Bindings::ensure_web_prototype(realm, "HTMLTextAreaElement")); } +void HTMLTextAreaElement::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_inner_text_element); + visitor.visit(m_text_node); +} + +void HTMLTextAreaElement::did_receive_focus() +{ + auto* browsing_context = document().browsing_context(); + if (!browsing_context) + return; + if (!m_text_node) + return; + browsing_context->set_cursor_position(DOM::Position { *m_text_node, 0 }); +} + +void HTMLTextAreaElement::did_lose_focus() +{ + // The change event fires when the value is committed, if that makes sense for the control, + // or else when the control loses focus + queue_an_element_task(HTML::Task::Source::UserInteraction, [this] { + auto change_event = DOM::Event::create(realm(), HTML::EventNames::change); + change_event->set_bubbles(true); + dispatch_event(change_event); + }); +} + // https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex i32 HTMLTextAreaElement::default_tab_index_value() const { @@ -32,7 +80,54 @@ i32 HTMLTextAreaElement::default_tab_index_value() const // https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element:concept-form-reset-control void HTMLTextAreaElement::reset_algorithm() { - // FIXME: The reset algorithm for textarea elements is to set the dirty value flag back to false, and set the raw value of element to its child text content. + // The reset algorithm for textarea elements is to set the dirty value flag back to false, + m_dirty = false; + // and set the raw value of element to its child text content. + m_raw_value = child_text_content(); +} + +void HTMLTextAreaElement::form_associated_element_was_inserted() +{ + create_shadow_tree_if_needed(); +} + +void HTMLTextAreaElement::create_shadow_tree_if_needed() +{ + if (shadow_root_internal()) + return; + + auto shadow_root = heap().allocate(realm(), document(), *this, Bindings::ShadowRootMode::Closed); + auto element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); + + m_inner_text_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML)); + + // NOTE: The text content of the