summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp')
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp80
1 files changed, 72 insertions, 8 deletions
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
index 7cc0d5ba35..2ec5e4c762 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
+++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022, Adam Hodgen <ant1441@gmail.com>
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
*
@@ -15,6 +15,7 @@
#include <LibWeb/DOM/Text.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/EventNames.h>
+#include <LibWeb/HTML/HTMLDivElement.h>
#include <LibWeb/HTML/HTMLFormElement.h>
#include <LibWeb/HTML/HTMLInputElement.h>
#include <LibWeb/HTML/Scripting/Environments.h>
@@ -56,7 +57,10 @@ JS::ThrowCompletionOr<void> HTMLInputElement::initialize(JS::Realm& realm)
void HTMLInputElement::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
- visitor.visit(m_text_node.ptr());
+ visitor.visit(m_inner_text_element);
+ visitor.visit(m_text_node);
+ visitor.visit(m_placeholder_element);
+ visitor.visit(m_placeholder_text_node);
visitor.visit(m_legacy_pre_activation_behavior_checked_element_in_group.ptr());
visitor.visit(m_selected_files);
}
@@ -284,6 +288,8 @@ void HTMLInputElement::did_edit_text_node(Badge<BrowsingContext>)
m_value = value_sanitization_algorithm(m_text_node->data());
m_dirty_value = true;
+ update_placeholder_visibility();
+
// NOTE: This is a bit ad-hoc, but basically implements part of "4.10.5.5 Common event behaviors"
// https://html.spec.whatwg.org/multipage/input.html#common-input-element-events
queue_an_element_task(HTML::Task::Source::UserInteraction, [this] {
@@ -342,12 +348,26 @@ WebIDL::ExceptionOr<void> HTMLInputElement::set_value(DeprecatedString value)
// 5. If the element's value (after applying the value sanitization algorithm) is different from oldValue,
// and the element has a text entry cursor position, move the text entry cursor position to the end of the
// text control, unselecting any selected text and resetting the selection direction to "none".
- if (m_text_node && (m_value != old_value))
+ if (m_text_node && (m_value != old_value)) {
m_text_node->set_data(m_value);
+ update_placeholder_visibility();
+ }
return {};
}
+void HTMLInputElement::update_placeholder_visibility()
+{
+ if (!m_placeholder_element)
+ return;
+ auto placeholder_text = this->placeholder_value();
+ if (placeholder_text.has_value()) {
+ 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/input.html#the-input-element:attr-input-placeholder-3
static bool is_allowed_to_have_placeholder(HTML::HTMLInputElement::TypeAttributeState state)
{
@@ -389,6 +409,17 @@ Optional<DeprecatedString> HTMLInputElement::placeholder_value() const
return placeholder;
}
+class PlaceholderElement final : public HTMLDivElement {
+ JS_CELL(PlaceholderElement, HTMLDivElement);
+
+public:
+ PlaceholderElement(DOM::Document& document)
+ : HTMLDivElement(document, DOM::QualifiedName { HTML::TagNames::div, ""sv, Namespace::HTML })
+ {
+ }
+ virtual Optional<CSS::Selector::PseudoElement> pseudo_element() const override { return CSS::Selector::PseudoElement::Placeholder; }
+};
+
void HTMLInputElement::create_shadow_tree_if_needed()
{
if (shadow_root_internal())
@@ -412,7 +443,27 @@ void HTMLInputElement::create_shadow_tree_if_needed()
if (initial_value.is_null())
initial_value = DeprecatedString::empty();
auto element = DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
- MUST(element->set_attribute(HTML::AttributeNames::style, "white-space: pre; padding-top: 1px; padding-bottom: 1px; padding-left: 2px; padding-right: 2px; height: 1lh;"));
+ MUST(element->set_attribute(HTML::AttributeNames::style, R"~~~(
+ display: flex;
+ height: 100%;
+ align-items: center;
+ white-space: pre;
+ border: none;
+ padding: 1px 2px;
+)~~~"));
+
+ m_placeholder_element = heap().allocate<PlaceholderElement>(realm(), document()).release_allocated_value_but_fixme_should_propagate_errors();
+ MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Height, "1lh"sv));
+
+ m_placeholder_text_node = heap().allocate<DOM::Text>(realm(), document(), initial_value).release_allocated_value_but_fixme_should_propagate_errors();
+ m_placeholder_text_node->set_data(attribute(HTML::AttributeNames::placeholder));
+ m_placeholder_text_node->set_owner_input_element({}, *this);
+ MUST(m_placeholder_element->append_child(*m_placeholder_text_node));
+ MUST(element->append_child(*m_placeholder_element));
+
+ m_inner_text_element = DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
+ MUST(m_inner_text_element->style_for_bindings()->set_property(CSS::PropertyID::Height, "1lh"sv));
+
m_text_node = heap().allocate<DOM::Text>(realm(), document(), initial_value).release_allocated_value_but_fixme_should_propagate_errors();
m_text_node->set_always_editable(m_type != TypeAttributeState::FileUpload);
m_text_node->set_owner_input_element({}, *this);
@@ -420,7 +471,8 @@ void HTMLInputElement::create_shadow_tree_if_needed()
if (m_type == TypeAttributeState::Password)
m_text_node->set_is_password_input({}, true);
- MUST(element->append_child(*m_text_node));
+ 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);
}
@@ -446,8 +498,13 @@ void HTMLInputElement::parse_attribute(DeprecatedFlyString const& name, Deprecat
} else if (name == HTML::AttributeNames::type) {
m_type = parse_type_attribute(value);
} else if (name == HTML::AttributeNames::value) {
- if (!m_dirty_value)
+ if (!m_dirty_value) {
m_value = value_sanitization_algorithm(value);
+ update_placeholder_visibility();
+ }
+ } else if (name == HTML::AttributeNames::placeholder) {
+ if (m_placeholder_text_node)
+ m_placeholder_text_node->set_data(value);
}
}
@@ -474,8 +531,13 @@ void HTMLInputElement::did_remove_attribute(DeprecatedFlyString const& name)
if (!m_dirty_checkedness)
set_checked(false, ChangeSource::Programmatic);
} else if (name == HTML::AttributeNames::value) {
- if (!m_dirty_value)
+ if (!m_dirty_value) {
m_value = DeprecatedString::empty();
+ update_placeholder_visibility();
+ }
+ } else if (name == HTML::AttributeNames::placeholder) {
+ if (m_placeholder_text_node)
+ m_placeholder_text_node->set_data({});
}
}
@@ -785,8 +847,10 @@ void HTMLInputElement::reset_algorithm()
// and then invoke the value sanitization algorithm, if the type attribute's current state defines one.
m_value = value_sanitization_algorithm(m_value);
- if (m_text_node)
+ if (m_text_node) {
m_text_node->set_data(m_value);
+ update_placeholder_visibility();
+ }
}
void HTMLInputElement::form_associated_element_was_inserted()