From ce9ad3a236d48b2f31f901acbe6b4414707c4d28 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sat, 17 Feb 2024 16:27:41 -0500 Subject: [PATCH] LibWeb: Fully implement the HTMLInputElement `value` setter and getter The setter was missing an implementation for the default and default/on value attribute modes. This patch adds a method to get the current value attribute mode, and implements the value setter and getter based on that mode according to the spec. --- Tests/LibWeb/Text/expected/input-value.txt | 5 + Tests/LibWeb/Text/input/input-value.html | 26 +++ .../LibWeb/HTML/HTMLInputElement.cpp | 178 +++++++++++------- .../Libraries/LibWeb/HTML/HTMLInputElement.h | 8 + 4 files changed, 147 insertions(+), 70 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/input-value.txt create mode 100644 Tests/LibWeb/Text/input/input-value.html diff --git a/Tests/LibWeb/Text/expected/input-value.txt b/Tests/LibWeb/Text/expected/input-value.txt new file mode 100644 index 0000000000..40cd1fec9e --- /dev/null +++ b/Tests/LibWeb/Text/expected/input-value.txt @@ -0,0 +1,5 @@ +pass text: "pass" +hidden: "pass" +button: "pass" +checkbox: "pass" +file: "" (threw exception) diff --git a/Tests/LibWeb/Text/input/input-value.html b/Tests/LibWeb/Text/input/input-value.html new file mode 100644 index 0000000000..f47b6ae137 --- /dev/null +++ b/Tests/LibWeb/Text/input/input-value.html @@ -0,0 +1,26 @@ + + + + + + + diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index b421f77b33..65195edb1e 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -346,93 +346,93 @@ void HTMLInputElement::did_pick_color(Optional picked_color) String HTMLInputElement::value() const { + switch (value_attribute_mode()) { + // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-value + case ValueAttributeMode::Value: + // Return the current value of the element. + return m_value; + + // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default + case ValueAttributeMode::Default: + // On getting, if the element has a value content attribute, return that attribute's value; otherwise, return + // the empty string. + return get_attribute_value(AttributeNames::value); + + // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default-on + case ValueAttributeMode::DefaultOn: + // On getting, if the element has a value content attribute, return that attribute's value; otherwise, return + // the string "on". + return get_attribute(AttributeNames::value).value_or("on"_string); + // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-filename - if (type_state() == TypeAttributeState::FileUpload) { - // NOTE: This "fakepath" requirement is a sad accident of history. See the example in the File Upload state section for more information. - // NOTE: Since path components are not permitted in filenames in the list of selected files, the "\fakepath\" cannot be mistaken for a path component. - // On getting, return the string "C:\fakepath\" followed by the name of the first file in the list of selected files, if any, or the empty string if the list is empty. + case ValueAttributeMode::Filename: + // On getting, return the string "C:\fakepath\" followed by the name of the first file in the list of selected + // files, if any, or the empty string if the list is empty. if (m_selected_files && m_selected_files->item(0)) return MUST(String::formatted("C:\\fakepath\\{}", m_selected_files->item(0)->name())); return String {}; } - // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default-on - if (type_state() == TypeAttributeState::Checkbox || type_state() == TypeAttributeState::RadioButton) { - // On getting, if the element has a value content attribute, return that attribute's value; otherwise, return the string "on". - return get_attribute(AttributeNames::value).value_or("on"_string); - } - - // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default - if (type_state() == TypeAttributeState::Hidden - || type_state() == TypeAttributeState::SubmitButton - || type_state() == TypeAttributeState::ImageButton - || type_state() == TypeAttributeState::ResetButton - || type_state() == TypeAttributeState::Button) { - // On getting, if the element has a value content attribute, return that attribute's value; otherwise, return the empty string. - return get_attribute_value(AttributeNames::value); - } - - // https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range):attr-input-value - if (type_state() == TypeAttributeState::Range) { - // https://html.spec.whatwg.org/multipage/input.html#concept-input-value-default-range - double minimum = *min(); - double maximum = *max(); - double default_value = minimum + (maximum - minimum) / 2; - if (maximum < minimum) - default_value = minimum; - - if (!parse_floating_point_number(m_value).has_value()) - return MUST(String::number(default_value)); - } - - // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-value - // Return the current value of the element. - return m_value; + VERIFY_NOT_REACHED(); } WebIDL::ExceptionOr HTMLInputElement::set_value(String const& value) { auto& realm = this->realm(); + switch (value_attribute_mode()) { + // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-value + case ValueAttributeMode::Value: { + // 1. Let oldValue be the element's value. + auto old_value = move(m_value); + + // 2. Set the element's value to the new value. + // NOTE: For the TextNode this is done as part of step 4 below. + + // 3. Set the element's dirty value flag to true. + m_dirty_value = true; + + // 4. Invoke the value sanitization algorithm, if the element's type attribute's current state defines one. + m_value = value_sanitization_algorithm(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_value != old_value) { + if (m_text_node) { + m_text_node->set_data(m_value); + update_placeholder_visibility(); + + if (auto* browsing_context = document().browsing_context()) + browsing_context->set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size())); + } + + if (type_state() == TypeAttributeState::Color && m_color_well_element) + update_color_well_element(); + + if (type_state() == TypeAttributeState::Range && m_slider_thumb) + update_slider_thumb_element(); + } + + break; + } + + // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default + // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default-on + case ValueAttributeMode::Default: + case ValueAttributeMode::DefaultOn: + // On setting, set the value of the element's value content attribute to the new value. + TRY(set_attribute(HTML::AttributeNames::value, value)); + break; + // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-filename - if (type_state() == TypeAttributeState::FileUpload) { + case ValueAttributeMode::Filename: // On setting, if the new value is the empty string, empty the list of selected files; otherwise, throw an "InvalidStateError" DOMException. if (!value.is_empty()) return WebIDL::InvalidStateError::create(realm, "Setting value of input type file to non-empty string"_fly_string); + m_selected_files = nullptr; - return {}; - } - - // https://html.spec.whatwg.org/multipage/input.html#dom-input-value-value - // 1. Let oldValue be the element's value. - auto old_value = move(m_value); - - // 2. Set the element's value to the new value. - // NOTE: For the TextNode this is done as part of step 4 below. - - // 3. Set the element's dirty value flag to true. - m_dirty_value = true; - - // 4. Invoke the value sanitization algorithm, if the element's type attribute's current state defines one. - m_value = value_sanitization_algorithm(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_value != old_value) { - if (m_text_node) { - m_text_node->set_data(m_value); - update_placeholder_visibility(); - - if (auto* browsing_context = document().browsing_context()) - browsing_context->set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size())); - } - - if (type_state() == TypeAttributeState::Color && m_color_well_element) - update_color_well_element(); - - if (type_state() == TypeAttributeState::Range && m_slider_thumb) - update_slider_thumb_element(); + break; } return {}; @@ -1776,4 +1776,42 @@ bool HTMLInputElement::step_up_or_down_applies() const return value_as_number_applies(); } +// https://html.spec.whatwg.org/multipage/input.html#the-input-element:dom-input-value-2 +HTMLInputElement::ValueAttributeMode HTMLInputElement::value_attribute_mode() const +{ + switch (type_state()) { + case TypeAttributeState::Text: + case TypeAttributeState::Search: + case TypeAttributeState::Telephone: + case TypeAttributeState::URL: + case TypeAttributeState::Email: + case TypeAttributeState::Password: + case TypeAttributeState::Date: + case TypeAttributeState::Month: + case TypeAttributeState::Week: + case TypeAttributeState::Time: + case TypeAttributeState::LocalDateAndTime: + case TypeAttributeState::Number: + case TypeAttributeState::Range: + case TypeAttributeState::Color: + return ValueAttributeMode::Value; + + case TypeAttributeState::Hidden: + case TypeAttributeState::SubmitButton: + case TypeAttributeState::ImageButton: + case TypeAttributeState::ResetButton: + case TypeAttributeState::Button: + return ValueAttributeMode::Default; + + case TypeAttributeState::Checkbox: + case TypeAttributeState::RadioButton: + return ValueAttributeMode::DefaultOn; + + case TypeAttributeState::FileUpload: + return ValueAttributeMode::Filename; + } + + VERIFY_NOT_REACHED(); +} + } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h index 236a3b68d4..f026cd8107 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -216,6 +216,14 @@ private: // https://html.spec.whatwg.org/multipage/input.html#value-sanitization-algorithm String value_sanitization_algorithm(String const&) const; + enum class ValueAttributeMode { + Value, + Default, + DefaultOn, + Filename, + }; + ValueAttributeMode value_attribute_mode() const; + void update_placeholder_visibility(); JS::GCPtr m_placeholder_element; JS::GCPtr m_placeholder_text_node;