LibWeb: Port the FormAssociatedElement value to UTF-16
Some checks are pending
CI / macOS, arm64, Sanitizer, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer, Clang (push) Waiting to run
Package the js repl as a binary artifact / Linux, arm64 (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run

This porting effort makes it pretty clear we will want a UTF-16-aware
GenericLexer. But for now, we can actually make ASCII assumptions about
what we are parsing, and act accordingly.
This commit is contained in:
Timothy Flynn 2025-07-26 12:19:56 -04:00 committed by Andreas Kling
commit c8888609f4
Notes: github-actions[bot] 2025-07-28 10:26:29 +00:00
30 changed files with 444 additions and 333 deletions

View file

@ -3600,7 +3600,7 @@ Optional<Element::Directionality> Element::auto_directionality() const
// 1. If element's value contains a character of bidirectional character type AL or R,
// and there is no character of bidirectional character type L anywhere before it in the element's value, then return 'rtl'.
for (auto code_point : Utf8View(value)) {
for (auto code_point : value) {
auto bidi_class = Unicode::bidirectional_class(code_point);
if (bidi_class == Unicode::BidiClass::LeftToRight)
break;

View file

@ -33,30 +33,33 @@ u32 week_number_of_the_last_day(u64 year)
}
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-week-string
bool is_valid_week_string(StringView value)
bool is_valid_week_string(Utf16View const& value)
{
// A string is a valid week string representing a week-year year and week week if it consists of the following components in the given order:
// 1. Four or more ASCII digits, representing year, where year > 0
// 2. A U+002D HYPHEN-MINUS character (-)
// 3. A U+0057 LATIN CAPITAL LETTER W character (W)
// 4. Two ASCII digits, representing the week week, in the range 1 ≤ week ≤ maxweek, where maxweek is the week number of the last day of week-year year
// 4. Two ASCII digits, representing the week week, in the range 1 ≤ week ≤ maxweek, where maxweek is the week number
// of the last day of week-year year
auto parts = value.split_view('-', SplitBehavior::KeepEmpty);
if (parts.size() != 2)
return false;
if (parts[0].length() < 4)
if (parts[0].length_in_code_units() < 4)
return false;
if (parts[1].length_in_code_units() != 3)
return false;
for (auto digit : parts[0])
if (!is_ascii_digit(digit))
return false;
if (parts[1].length() != 3)
return false;
if (!parts[1].starts_with('W'))
if (!parts[1].starts_with("W"sv))
return false;
if (!is_ascii_digit(parts[1][1]))
if (!is_ascii_digit(parts[1].code_unit_at(1)))
return false;
if (!is_ascii_digit(parts[1][2]))
if (!is_ascii_digit(parts[1].code_unit_at(2)))
return false;
u64 year = 0;
@ -64,13 +67,13 @@ bool is_valid_week_string(StringView value)
year *= 10;
year += parse_ascii_digit(d);
}
auto week = (parse_ascii_digit(parts[1][1]) * 10) + parse_ascii_digit(parts[1][2]);
auto week = (parse_ascii_digit(parts[1].code_unit_at(1)) * 10) + parse_ascii_digit(parts[1].code_unit_at(2));
return week >= 1 && week <= week_number_of_the_last_day(year);
}
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-month-string
bool is_valid_month_string(StringView value)
bool is_valid_month_string(Utf16View const& value)
{
// A string is a valid month string representing a year year and month month if it consists of the following components in the given order:
@ -82,40 +85,42 @@ bool is_valid_month_string(StringView value)
if (parts.size() != 2)
return false;
if (parts[0].length() < 4)
if (parts[0].length_in_code_units() < 4)
return false;
if (parts[1].length_in_code_units() != 2)
return false;
for (auto digit : parts[0])
if (!is_ascii_digit(digit))
return false;
if (parts[1].length() != 2)
if (!is_ascii_digit(parts[1].code_unit_at(0)))
return false;
if (!is_ascii_digit(parts[1].code_unit_at(1)))
return false;
if (!is_ascii_digit(parts[1][0]))
return false;
if (!is_ascii_digit(parts[1][1]))
return false;
auto month = (parse_ascii_digit(parts[1][0]) * 10) + parse_ascii_digit(parts[1][1]);
auto month = (parse_ascii_digit(parts[1].code_unit_at(0)) * 10) + parse_ascii_digit(parts[1].code_unit_at(1));
return month >= 1 && month <= 12;
}
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
bool is_valid_date_string(StringView value)
bool is_valid_date_string(Utf16View const& value)
{
// A string is a valid date string representing a year year, month month, and day day if it consists of the following components in the given order:
// 1. A valid month string, representing year and month
// 2. A U+002D HYPHEN-MINUS character (-)
// 3. Two ASCII digits, representing day, in the range 1 ≤ day ≤ maxday where maxday is the number of days in the month month and year year
// 3. Two ASCII digits, representing day, in the range 1 ≤ day ≤ maxday where maxday is the number of days in the
// month month and year year
auto parts = value.split_view('-', SplitBehavior::KeepEmpty);
if (parts.size() != 3)
return false;
if (!is_valid_month_string(ByteString::formatted("{}-{}", parts[0], parts[1])))
auto month_string = value.substring_view(0, parts[0].length_in_code_units() + 1 + parts[1].length_in_code_units());
if (!is_valid_month_string(month_string))
return false;
if (parts[2].length() != 2)
if (parts[2].length_in_code_units() != 2)
return false;
i64 year = 0;
@ -123,18 +128,20 @@ bool is_valid_date_string(StringView value)
year *= 10;
year += parse_ascii_digit(d);
}
auto month = (parse_ascii_digit(parts[1][0]) * 10) + parse_ascii_digit(parts[1][1]);
i64 day = (parse_ascii_digit(parts[2][0]) * 10) + parse_ascii_digit(parts[2][1]);
auto month = (parse_ascii_digit(parts[1].code_unit_at(0)) * 10) + parse_ascii_digit(parts[1].code_unit_at(1));
i64 day = (parse_ascii_digit(parts[2].code_unit_at(0)) * 10) + parse_ascii_digit(parts[2].code_unit_at(1));
return day >= 1 && day <= AK::days_in_month(year, month);
}
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-local-date-and-time-string
bool is_valid_local_date_and_time_string(StringView value)
bool is_valid_local_date_and_time_string(Utf16View const& value)
{
auto parts_split_by_T = value.split_view('T', SplitBehavior::KeepEmpty);
if (parts_split_by_T.size() == 2)
return is_valid_date_string(parts_split_by_T[0]) && is_valid_time_string(parts_split_by_T[1]);
auto parts_split_by_space = value.split_view(' ', SplitBehavior::KeepEmpty);
if (parts_split_by_space.size() == 2)
return is_valid_date_string(parts_split_by_space[0]) && is_valid_time_string(parts_split_by_space[1]);
@ -143,11 +150,11 @@ bool is_valid_local_date_and_time_string(StringView value)
}
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-normalised-local-date-and-time-string
String normalize_local_date_and_time_string(String const& value)
Utf16String normalize_local_date_and_time_string(Utf16String const& value)
{
if (auto spaces = value.count(" "sv); spaces > 0) {
VERIFY(spaces == 1);
return MUST(value.replace(" "sv, "T"sv, ReplaceMode::FirstOnly));
return value.replace(" "sv, "T"sv, ReplaceMode::FirstOnly);
}
VERIFY(value.count("T"sv) == 1);
@ -155,7 +162,7 @@ String normalize_local_date_and_time_string(String const& value)
}
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-time-string
bool is_valid_time_string(StringView value)
bool is_valid_time_string(Utf16View const& value)
{
// A string is a valid time string representing an hour hour, a minute minute, and a second second if it consists of the following components in the given order:
@ -163,49 +170,59 @@ bool is_valid_time_string(StringView value)
// 2. A U+003A COLON character (:)
// 3. Two ASCII digits, representing minute, in the range 0 ≤ minute ≤ 59
// 4. If second is nonzero, or optionally if second is zero:
// 1. A U+003A COLON character (:)
// 2. Two ASCII digits, representing the integer part of second, in the range 0 ≤ s ≤ 59
// 3. If second is not an integer, or optionally if second is an integer:
// 1. A U+002E FULL STOP character (.)
// 2. One, two, or three ASCII digits, representing the fractional part of second
// 1. A U+003A COLON character (:)
// 2. Two ASCII digits, representing the integer part of second, in the range 0 ≤ s ≤ 59
// 3. If second is not an integer, or optionally if second is an integer:
// 1. A U+002E FULL STOP character (.)
// 2. One, two, or three ASCII digits, representing the fractional part of second
auto parts = value.split_view(':', SplitBehavior::KeepEmpty);
if (parts.size() != 2 && parts.size() != 3)
return false;
if (parts[0].length() != 2)
if (parts[0].length_in_code_units() != 2)
return false;
if (!(is_ascii_digit(parts[0][0]) && is_ascii_digit(parts[0][1])))
if (parts[1].length_in_code_units() != 2)
return false;
auto hour = (parse_ascii_digit(parts[0][0]) * 10) + parse_ascii_digit(parts[0][1]);
if (!is_ascii_digit(parts[0].code_unit_at(0)) || !is_ascii_digit(parts[0].code_unit_at(1)))
return false;
auto hour = (parse_ascii_digit(parts[0].code_unit_at(0)) * 10) + parse_ascii_digit(parts[0].code_unit_at(1));
if (hour > 23)
return false;
if (parts[1].length() != 2)
if (!is_ascii_digit(parts[1].code_unit_at(0)) || !is_ascii_digit(parts[1].code_unit_at(1)))
return false;
if (!(is_ascii_digit(parts[1][0]) && is_ascii_digit(parts[1][1])))
return false;
auto minute = (parse_ascii_digit(parts[1][0]) * 10) + parse_ascii_digit(parts[1][1]);
auto minute = (parse_ascii_digit(parts[1].code_unit_at(0)) * 10) + parse_ascii_digit(parts[1].code_unit_at(1));
if (minute > 59)
return false;
if (parts.size() == 2)
return true;
if (parts[2].length() < 2)
return false;
if (!(is_ascii_digit(parts[2][0]) && is_ascii_digit(parts[2][1])))
return false;
auto second = (parse_ascii_digit(parts[2][0]) * 10) + parse_ascii_digit(parts[2][1]);
if (second > 59)
return false;
if (parts[2].length() == 2)
return true;
auto second_parts = parts[2].split_view('.', SplitBehavior::KeepEmpty);
if (second_parts.size() != 2)
return false;
if (second_parts[1].length() < 1 || second_parts[1].length() > 3)
return false;
for (auto digit : second_parts[1])
if (!is_ascii_digit(digit))
if (parts.size() == 3) {
if (parts[2].length_in_code_units() < 2)
return false;
if (!is_ascii_digit(parts[2].code_unit_at(0)) || !is_ascii_digit(parts[2].code_unit_at(1)))
return false;
auto second = (parse_ascii_digit(parts[2].code_unit_at(0)) * 10) + parse_ascii_digit(parts[2].code_unit_at(1));
if (second > 59)
return false;
if (parts[2].length_in_code_units() > 2) {
auto fractional = parts[2].split_view('.', SplitBehavior::KeepEmpty);
if (fractional.size() != 2)
return false;
if (fractional[1].length_in_code_units() < 1 || fractional[1].length_in_code_units() > 3)
return false;
for (auto digit : fractional[1])
if (!is_ascii_digit(digit))
return false;
}
}
return true;
}

View file

@ -7,19 +7,19 @@
#pragma once
#include <AK/Forward.h>
#include <AK/String.h>
#include <AK/Utf16String.h>
#include <LibJS/Runtime/Date.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::HTML {
u32 week_number_of_the_last_day(u64 year);
bool is_valid_week_string(StringView value);
bool is_valid_month_string(StringView value);
bool is_valid_date_string(StringView value);
bool is_valid_local_date_and_time_string(StringView value);
String normalize_local_date_and_time_string(String const& value);
bool is_valid_time_string(StringView value);
bool is_valid_week_string(Utf16View const& value);
bool is_valid_month_string(Utf16View const& value);
bool is_valid_date_string(Utf16View const& value);
bool is_valid_local_date_and_time_string(Utf16View const& value);
Utf16String normalize_local_date_and_time_string(Utf16String const& value);
bool is_valid_time_string(Utf16View const& value);
WebIDL::ExceptionOr<GC::Ref<JS::Date>> parse_time_string(JS::Realm& realm, StringView value);
struct YearAndMonth {

View file

@ -125,7 +125,7 @@ public:
virtual bool suffering_from_bad_input() const { return false; }
bool suffering_from_a_custom_error() const;
virtual String value() const { return String {}; }
virtual Utf16String value() const { return {}; }
virtual Optional<String> optional_value() const { VERIFY_NOT_REACHED(); }
virtual HTMLElement& form_associated_element_to_html_element() = 0;

View file

@ -144,7 +144,7 @@ WebIDL::ExceptionOr<Optional<Vector<XHR::FormDataEntry>>> construct_entry_list(J
if (auto* select_element = dynamic_cast<HTML::HTMLSelectElement*>(control.ptr())) {
for (auto const& option_element : select_element->list_of_options()) {
if (option_element->selected() && !option_element->disabled()) {
entry_list.append(TRY(create_entry(realm, name.to_string(), option_element->value())));
entry_list.append(TRY(create_entry(realm, name.to_string(), option_element->value().to_utf8_but_should_be_ported_to_utf16())));
}
}
}
@ -153,11 +153,11 @@ WebIDL::ExceptionOr<Optional<Vector<XHR::FormDataEntry>>> construct_entry_list(J
// 1. If the field element has a value attribute specified, then let value be the value of that attribute; otherwise, let value be the string "on".
auto value = checkbox_or_radio_element->value();
if (value.is_empty())
value = "on"_string;
value = "on"_utf16;
// 2. Create an entry with name and value, and append it to entry list.
auto checkbox_or_radio_element_name = checkbox_or_radio_element->name();
entry_list.append(TRY(create_entry(realm, checkbox_or_radio_element_name->to_string(), value)));
entry_list.append(TRY(create_entry(realm, checkbox_or_radio_element_name->to_string(), value.to_utf8_but_should_be_ported_to_utf16())));
}
// 8. Otherwise, if the field element is an input element whose type attribute is in the File Upload state, then:
else if (auto* file_element = dynamic_cast<HTML::HTMLInputElement*>(control.ptr()); file_element && file_element->type_state() == HTMLInputElement::TypeAttributeState::FileUpload) {
@ -186,7 +186,7 @@ WebIDL::ExceptionOr<Optional<Vector<XHR::FormDataEntry>>> construct_entry_list(J
}
// 10. Otherwise, create an entry with name and the value of the field element, and append it to entry list.
else {
entry_list.append(TRY(create_entry(realm, name.to_string(), control_as_form_associated_element->value())));
entry_list.append(TRY(create_entry(realm, name.to_string(), control_as_form_associated_element->value().to_utf8_but_should_be_ported_to_utf16())));
}
// 11. If the element has a dirname attribute, and that attribute's value is not the empty string, then:

View file

@ -117,10 +117,10 @@ bool HTMLButtonElement::is_submit_button() const
}
// https://html.spec.whatwg.org/multipage/form-elements.html#the-button-element:concept-fe-value
String HTMLButtonElement::value() const
Utf16String HTMLButtonElement::value() const
{
// The element's value is the value of the element's value attribute, if there is one; otherwise the empty string.
return attribute(AttributeNames::value).value_or(String {});
return Utf16String::from_utf8(attribute(AttributeNames::value).value_or(String {}));
}
// https://html.spec.whatwg.org/multipage/form-elements.html#the-button-element:concept-fe-optional-value

View file

@ -75,7 +75,7 @@ public:
// https://www.w3.org/TR/html-aria/#el-button
virtual Optional<ARIA::Role> default_role() const override { return ARIA::Role::button; }
virtual String value() const override;
virtual Utf16String value() const override;
virtual Optional<String> optional_value() const override;
virtual bool has_activation_behavior() const override;

View file

@ -26,7 +26,7 @@ interface HTMLButtonElement : HTMLElement {
[CEReactions, Reflect=formtarget] attribute DOMString formTarget;
[CEReactions, Reflect] attribute DOMString name;
[CEReactions, ImplementedAs=type_for_bindings, Enumerated=ButtonTypeState] attribute DOMString type;
[CEReactions, Reflect] attribute DOMString value;
[CEReactions, Reflect] attribute Utf16DOMString value;
readonly attribute boolean willValidate;
readonly attribute ValidityState validity;

View file

@ -404,7 +404,7 @@ static void show_the_picker_if_applicable(HTMLInputElement& element)
if (element.type_state() == HTMLInputElement::TypeAttributeState::Color) {
auto weak_element = element.make_weak_ptr<HTMLInputElement>();
element.set_is_open(true);
element.document().browsing_context()->top_level_browsing_context()->page().did_request_color_picker(weak_element, Color::from_string(element.value()).value_or(Color(0, 0, 0)));
element.document().browsing_context()->top_level_browsing_context()->page().did_request_color_picker(weak_element, Color::from_utf16_string(element.value()).value_or(Color(0, 0, 0)));
}
}
}
@ -530,7 +530,7 @@ void HTMLInputElement::did_edit_text_node()
{
// An input element's dirty value flag must be set to true whenever the user interacts with the control in a way that changes the value.
auto old_value = move(m_value);
m_value = value_sanitization_algorithm(m_text_node->data().to_utf8_but_should_be_ported_to_utf16());
m_value = value_sanitization_algorithm(m_text_node->data());
m_dirty_value = true;
m_has_uncommitted_changes = true;
@ -547,7 +547,7 @@ void HTMLInputElement::did_pick_color(Optional<Color> picked_color, ColorPickerU
{
if (type_state() == TypeAttributeState::Color && picked_color.has_value()) {
// then when the user changes the element's value
m_value = value_sanitization_algorithm(picked_color.value().to_string_without_alpha());
m_value = value_sanitization_algorithm(picked_color->to_utf16_string_without_alpha());
m_dirty_value = true;
update_color_well_element();
@ -630,7 +630,7 @@ void HTMLInputElement::did_select_files(Span<SelectedFile> selected_files, Multi
});
}
String HTMLInputElement::value() const
Utf16String HTMLInputElement::value() const
{
switch (value_attribute_mode()) {
// https://html.spec.whatwg.org/multipage/input.html#dom-input-value-value
@ -642,21 +642,23 @@ String HTMLInputElement::value() const
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);
return Utf16String::from_utf8(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);
if (auto value = get_attribute(AttributeNames::value); value.has_value())
return Utf16String::from_utf8(*value);
return "on"_utf16;
// https://html.spec.whatwg.org/multipage/input.html#dom-input-value-filename
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 {};
return Utf16String::formatted("C:\\fakepath\\{}", m_selected_files->item(0)->name());
return {};
}
VERIFY_NOT_REACHED();
@ -674,7 +676,7 @@ Optional<String> HTMLInputElement::optional_value() const
}
}
WebIDL::ExceptionOr<void> HTMLInputElement::set_value(String const& value)
WebIDL::ExceptionOr<void> HTMLInputElement::set_value(Utf16String const& value)
{
auto& realm = this->realm();
@ -700,7 +702,7 @@ WebIDL::ExceptionOr<void> HTMLInputElement::set_value(String const& value)
relevant_value_was_changed();
if (m_text_node) {
m_text_node->set_data(Utf16String::from_utf8(m_value));
m_text_node->set_data(m_value);
update_placeholder_visibility();
set_the_selection_range(m_text_node->length(), m_text_node->length());
@ -796,30 +798,36 @@ void HTMLInputElement::update_placeholder_visibility()
m_placeholder_element->set_inline_style(placeholder_style_when_hidden());
}
Utf16String HTMLInputElement::button_label() const
{
auto label = get_attribute(HTML::AttributeNames::value).map([](auto const& label) { return Utf16String::from_utf8(label); });
if (!label.has_value()) {
if (type_state() == TypeAttributeState::ResetButton) {
// https://html.spec.whatwg.org/multipage/input.html#reset-button-state-(type=reset)
// If the element has a value attribute, the button's label must be the value of that attribute;
// otherwise, it must be an implementation-defined string that means "Reset" or some such.
label = "Reset"_utf16;
} else if (type_state() == TypeAttributeState::SubmitButton) {
// https://html.spec.whatwg.org/multipage/input.html#submit-button-state-(type=submit)
// If the element has a value attribute, the button's label must be the value of that attribute;
// otherwise, it must be an implementation-defined string that means "Submit" or some such.
label = "Submit"_utf16;
} else {
// https://html.spec.whatwg.org/multipage/input.html#button-state-(type=button)
// If the element has a value attribute, the button's label must be the value of that attribute;
// otherwise, it must be the empty string.
label = value();
}
}
return label.release_value();
}
void HTMLInputElement::update_button_input_shadow_tree()
{
if (m_text_node) {
Optional<String> label = get_attribute(HTML::AttributeNames::value);
if (!label.has_value()) {
if (type_state() == TypeAttributeState::ResetButton) {
// https://html.spec.whatwg.org/multipage/input.html#reset-button-state-(type=reset)
// If the element has a value attribute, the button's label must be the value of that attribute;
// otherwise, it must be an implementation-defined string that means "Reset" or some such.
label = "Reset"_string;
} else if (type_state() == TypeAttributeState::SubmitButton) {
// https://html.spec.whatwg.org/multipage/input.html#submit-button-state-(type=submit)
// If the element has a value attribute, the button's label must be the value of that attribute;
// otherwise, it must be an implementation-defined string that means "Submit" or some such.
label = "Submit"_string;
} else {
// https://html.spec.whatwg.org/multipage/input.html#button-state-(type=button)
// If the element has a value attribute, the button's label must be the value of that attribute;
// otherwise, it must be the empty string.
label = value();
}
}
m_text_node->set_data(Utf16String::from_utf8(label.value()));
m_text_node->set_data(button_label());
update_placeholder_visibility();
}
}
@ -827,7 +835,7 @@ void HTMLInputElement::update_button_input_shadow_tree()
void HTMLInputElement::update_text_input_shadow_tree()
{
if (m_text_node) {
m_text_node->set_data(Utf16String::from_utf8(m_value));
m_text_node->set_data(m_value);
update_placeholder_visibility();
}
}
@ -999,26 +1007,8 @@ void HTMLInputElement::create_button_input_shadow_tree()
set_shadow_root(shadow_root);
auto text_container = MUST(DOM::create_element(document(), HTML::TagNames::span, Namespace::HTML));
MUST(text_container->set_attribute(HTML::AttributeNames::style, "display: inline-block; pointer-events: none;"_string));
Optional<String> label = get_attribute(HTML::AttributeNames::value);
if (!label.has_value()) {
if (type_state() == TypeAttributeState::ResetButton) {
// https://html.spec.whatwg.org/multipage/input.html#reset-button-state-(type=reset)
// If the element has a value attribute, the button's label must be the value of that attribute;
// otherwise, it must be an implementation-defined string that means "Reset" or some such.
label = "Reset"_string;
} else if (type_state() == TypeAttributeState::SubmitButton) {
// https://html.spec.whatwg.org/multipage/input.html#submit-button-state-(type=submit)
// If the element has a value attribute, the button's label must be the value of that attribute;
// otherwise, it must be an implementation-defined string that means "Submit" or some such.
label = "Submit"_string;
} else {
// https://html.spec.whatwg.org/multipage/input.html#button-state-(type=button)
// If the element has a value attribute, the button's label must be the value of that attribute;
// otherwise, it must be the empty string.
label = value();
}
}
m_text_node = realm().create<DOM::Text>(document(), Utf16String::from_utf8(label.value()));
m_text_node = realm().create<DOM::Text>(document(), button_label());
MUST(text_container->append_child(*m_text_node));
MUST(shadow_root->append_child(*text_container));
}
@ -1074,7 +1064,7 @@ void HTMLInputElement::create_text_input_shadow_tree()
}
MUST(element->append_child(*m_inner_text_element));
m_text_node = realm().create<DOM::Text>(document(), Utf16String::from_utf8(initial_value));
m_text_node = realm().create<DOM::Text>(document(), move(initial_value));
handle_readonly_attribute(attribute(HTML::AttributeNames::readonly));
if (type_state() == TypeAttributeState::Password)
m_text_node->set_is_password_input({}, true);
@ -1163,7 +1153,7 @@ void HTMLInputElement::create_color_input_shadow_tree()
border: 1px solid ButtonBorder;
box-sizing: border-box;
)~~~"_string));
MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, color));
MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, color.to_utf8_but_should_be_ported_to_utf16()));
MUST(border->append_child(*m_color_well_element));
MUST(shadow_root->append_child(border));
@ -1175,7 +1165,7 @@ void HTMLInputElement::update_color_well_element()
if (!m_color_well_element)
return;
MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, m_value));
MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, m_value.to_utf8_but_should_be_ported_to_utf16()));
}
void HTMLInputElement::create_file_input_shadow_tree()
@ -1407,11 +1397,11 @@ void HTMLInputElement::form_associated_element_attribute_changed(FlyString const
} else if (name == HTML::AttributeNames::value) {
if (!m_dirty_value) {
auto old_value = move(m_value);
if (!value.has_value()) {
m_value = String {};
} else {
m_value = value_sanitization_algorithm(*value);
}
if (value.has_value())
m_value = value_sanitization_algorithm(Utf16String::from_utf8(*value));
else
m_value = {};
if (m_value != old_value)
relevant_value_was_changed();
@ -1458,7 +1448,7 @@ void HTMLInputElement::type_attribute_changed(TypeAttributeState old_state, Type
// then set the value of the element to the value of the value content attribute, if there is one, or the empty string
// otherwise, and then set the control's dirty value flag to false.
else if (old_value_attribute_mode != ValueAttributeMode::Value && new_value_attribute_mode == ValueAttributeMode::Value) {
m_value = attribute(HTML::AttributeNames::value).value_or({});
m_value = Utf16String::from_utf8(attribute(HTML::AttributeNames::value).value_or({}));
m_dirty_value = false;
}
@ -1466,7 +1456,7 @@ void HTMLInputElement::type_attribute_changed(TypeAttributeState old_state, Type
// than the filename mode, and the new state of the element's type attribute puts the value IDL attribute in the filename mode,
// then set the value of the element to the empty string.
else if (old_value_attribute_mode != ValueAttributeMode::Filename && new_value_attribute_mode == ValueAttributeMode::Filename) {
m_value = String {};
m_value = {};
}
// 4. Update the element's rendering and behavior to the new state's.
@ -1618,129 +1608,147 @@ bool HTMLInputElement::can_have_text_editing_cursor() const
}
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-simple-colour
static bool is_valid_simple_color(StringView value)
static bool is_valid_simple_color(Utf16View const& value)
{
// if it is exactly seven characters long,
if (value.length() != 7)
if (value.length_in_code_units() != 7)
return false;
// and the first character is a U+0023 NUMBER SIGN character (#),
if (!value.starts_with('#'))
if (!value.starts_with("#"sv))
return false;
// and the remaining six characters are all ASCII hex digits
for (size_t i = 1; i < value.length(); i++)
if (!is_ascii_hex_digit(value[i]))
for (size_t i = 1; i < value.length_in_code_units(); i++)
if (!is_ascii_hex_digit(value.code_unit_at(i)))
return false;
return true;
}
// https://html.spec.whatwg.org/multipage/input.html#value-sanitization-algorithm
String HTMLInputElement::value_sanitization_algorithm(String const& value) const
Utf16String HTMLInputElement::value_sanitization_algorithm(Utf16String const& value) const
{
if (type_state() == HTMLInputElement::TypeAttributeState::Text || type_state() == HTMLInputElement::TypeAttributeState::Search || type_state() == HTMLInputElement::TypeAttributeState::Telephone || type_state() == HTMLInputElement::TypeAttributeState::Password) {
auto strip_newlines = [&]() {
if (!value.contains('\r') && !value.contains('\n'))
return value;
StringBuilder builder(StringBuilder::Mode::UTF16);
for (size_t i = 0; i < value.length_in_code_units(); ++i) {
auto code_unit = value.code_unit_at(i);
if (code_unit != '\r' && code_unit != '\n')
builder.append_code_unit(code_unit);
}
return builder.to_utf16_string();
};
auto strip_newlines_and_trim = [&]() {
auto value_without_newlines = strip_newlines();
return Utf16String::from_utf16_without_validation(value_without_newlines.utf16_view().trim(Infra::ASCII_WHITESPACE));
};
// https://html.spec.whatwg.org/multipage/input.html#text-(type=text)-state-and-search-state-(type=search):value-sanitization-algorithm
// https://html.spec.whatwg.org/multipage/input.html#telephone-state-(type=tel):value-sanitization-algorithm
// https://html.spec.whatwg.org/multipage/input.html#password-state-(type=password):value-sanitization-algorithm
if (first_is_one_of(type_state(), HTMLInputElement::TypeAttributeState::Text, HTMLInputElement::TypeAttributeState::Search, HTMLInputElement::TypeAttributeState::Telephone, HTMLInputElement::TypeAttributeState::Password)) {
// Strip newlines from the value.
if (value.bytes_as_string_view().contains('\r') || value.bytes_as_string_view().contains('\n')) {
StringBuilder builder;
for (auto c : value.bytes_as_string_view()) {
if (c != '\r' && c != '\n')
builder.append(c);
}
return MUST(builder.to_string());
}
} else if (type_state() == HTMLInputElement::TypeAttributeState::URL) {
return strip_newlines();
}
// https://html.spec.whatwg.org/multipage/input.html#url-state-(type=url):value-sanitization-algorithm
if (type_state() == HTMLInputElement::TypeAttributeState::URL) {
// Strip newlines from the value, then strip leading and trailing ASCII whitespace from the value.
if (value.bytes_as_string_view().contains('\r') || value.bytes_as_string_view().contains('\n')) {
StringBuilder builder;
for (auto c : value.bytes_as_string_view()) {
if (c != '\r' && c != '\n')
builder.append(c);
}
return MUST(String::from_utf8(builder.string_view().trim(Infra::ASCII_WHITESPACE)));
}
return MUST(value.trim(Infra::ASCII_WHITESPACE));
} else if (type_state() == HTMLInputElement::TypeAttributeState::Email) {
return strip_newlines_and_trim();
}
// https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email):value-sanitization-algorithm
// https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email):value-sanitization-algorithm-2
if (type_state() == HTMLInputElement::TypeAttributeState::Email) {
if (!has_attribute(AttributeNames::multiple)) {
// https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email):value-sanitization-algorithm
// Strip newlines from the value, then strip leading and trailing ASCII whitespace from the value.
if (value.bytes_as_string_view().contains('\r') || value.bytes_as_string_view().contains('\n')) {
StringBuilder builder;
for (auto c : value.bytes_as_string_view()) {
if (c != '\r' && c != '\n')
builder.append(c);
}
return MUST(String::from_utf8(builder.string_view().trim_whitespace()));
}
return MUST(value.trim_ascii_whitespace());
return strip_newlines_and_trim();
}
// https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email):value-sanitization-algorithm-2
// 1. Split on commas the element's value, strip leading and trailing ASCII whitespace from each resulting token, if any,
// and let the element's values be the (possibly empty) resulting list of (possibly empty) tokens, maintaining the original order.
Vector<String> values {};
for (auto const& token : MUST(value.split(',', SplitBehavior::KeepEmpty))) {
values.append(MUST(token.trim_ascii_whitespace()));
}
// 1. Split on commas the element's value, strip leading and trailing ASCII whitespace from each resulting token,
// if any, and let the element's values be the (possibly empty) resulting list of (possibly empty) tokens,
// maintaining the original order.
auto values = value.split_view(',', SplitBehavior::KeepEmpty);
// 2. Set the element's value to the result of concatenating the element's values, separating each value
// from the next by a single U+002C COMMA character (,), maintaining the list's order.
StringBuilder builder;
builder.join(',', values);
return MUST(builder.to_string());
for (auto& value : values)
value = value.trim(Infra::ASCII_WHITESPACE);
} else if (type_state() == HTMLInputElement::TypeAttributeState::Number) {
// https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number):value-sanitization-algorithm
// If the value of the element is not a valid floating-point number, then set it
// to the empty string instead.
// 2. Set the element's value to the result of concatenating the element's values, separating each value from
// the next by a single U+002C COMMA character (,), maintaining the list's order.
return Utf16String::join(',', values);
}
// https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number):value-sanitization-algorithm
if (type_state() == HTMLInputElement::TypeAttributeState::Number) {
// If the value of the element is not a valid floating-point number, then set it to the empty string instead.
if (!is_valid_floating_point_number(value))
return String {};
auto maybe_value = parse_floating_point_number(value);
// AD-HOC: The spec doesnt require these checks — but other engines do them, and
// theres a WPT case which tests that the value is less than Number.MAX_VALUE.
if (!maybe_value.has_value() || !isfinite(maybe_value.value()))
return String {};
} else if (type_state() == HTMLInputElement::TypeAttributeState::Date) {
// https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):value-sanitization-algorithm
return {};
// AD-HOC: The spec doesn't require these checks - but other engines do them, and there's a WPT case which tests
// that the value is less than Number.MAX_VALUE.
if (auto maybe_value = parse_floating_point_number(value); !maybe_value.has_value() || !isfinite(maybe_value.value()))
return {};
}
// https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):value-sanitization-algorithm
else if (type_state() == HTMLInputElement::TypeAttributeState::Date) {
// If the value of the element is not a valid date string, then set it to the empty string instead.
if (!is_valid_date_string(value))
return String {};
} else if (type_state() == HTMLInputElement::TypeAttributeState::Month) {
// https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):value-sanitization-algorithm
return {};
}
// https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):value-sanitization-algorithm
else if (type_state() == HTMLInputElement::TypeAttributeState::Month) {
// If the value of the element is not a valid month string, then set it to the empty string instead.
if (!is_valid_month_string(value))
return String {};
} else if (type_state() == HTMLInputElement::TypeAttributeState::Week) {
// https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week):value-sanitization-algorithm
return {};
}
// https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week):value-sanitization-algorithm
else if (type_state() == HTMLInputElement::TypeAttributeState::Week) {
if (!is_valid_week_string(value))
return String {};
} else if (type_state() == HTMLInputElement::TypeAttributeState::Time) {
// https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):value-sanitization-algorithm
return {};
}
// https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):value-sanitization-algorithm
else if (type_state() == HTMLInputElement::TypeAttributeState::Time) {
// If the value of the element is not a valid week string, then set it to the empty string instead.
if (!is_valid_time_string(value))
return String {};
} else if (type_state() == HTMLInputElement::TypeAttributeState::LocalDateAndTime) {
// https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local):value-sanitization-algorithm
return {};
}
// https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local):value-sanitization-algorithm
else if (type_state() == HTMLInputElement::TypeAttributeState::LocalDateAndTime) {
// If the value of the element is a valid local date and time string, then set it to a valid normalized local
// date and time string representing the same date and time; otherwise, set it to the empty string instead.
if (is_valid_local_date_and_time_string(value))
return normalize_local_date_and_time_string(value);
return String {};
} else if (type_state() == HTMLInputElement::TypeAttributeState::Range) {
// https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range):value-sanitization-algorithm
// If the value of the element is not a valid floating-point number, then set it to the best representation, as a floating-point number, of the default value.
auto maybe_value = parse_floating_point_number(value);
if (!is_valid_floating_point_number(value) ||
// AD-HOC: The spec doesnt require these checks — but other engines do them.
return {};
}
// https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range):value-sanitization-algorithm
else if (type_state() == HTMLInputElement::TypeAttributeState::Range) {
// If the value of the element is not a valid floating-point number, then set it to the best representation, as
// a floating-point number, of the default value.
if (auto maybe_value = parse_floating_point_number(value); !is_valid_floating_point_number(value) ||
// AD-HOC: The spec doesn't require these checks - but other engines do them.
!maybe_value.has_value() || !isfinite(maybe_value.value())) {
// The default value is the minimum plus half the difference between the minimum and the maximum, unless the maximum is less than the minimum, in which case the default value is the minimum.
// The default value is the minimum plus half the difference between the minimum and the maximum, unless the
// maximum is less than the minimum, in which case the default value is the minimum.
auto minimum = *min();
auto maximum = *max();
if (maximum < minimum)
return JS::number_to_string(minimum);
return JS::number_to_string(minimum + (maximum - minimum) / 2);
return JS::number_to_utf16_string(minimum);
return JS::number_to_utf16_string(minimum + ((maximum - minimum) / 2.0));
}
} else if (type_state() == HTMLInputElement::TypeAttributeState::Color) {
// https://html.spec.whatwg.org/multipage/input.html#color-state-(type=color):value-sanitization-algorithm
// If the value of the element is a valid simple color, then set it to the value of the element converted to ASCII lowercase;
}
// https://html.spec.whatwg.org/multipage/input.html#color-state-(type=color):value-sanitization-algorithm
else if (type_state() == HTMLInputElement::TypeAttributeState::Color) {
// If the value of the element is a valid simple color, then set it to the value of the element converted to
// ASCII lowercase; otherwise, set it to the string "#000000".
if (is_valid_simple_color(value))
return value.to_ascii_lowercase();
// otherwise, set it to the string "#000000".
return "#000000"_string;
return "#000000"_utf16;
}
return value;
}
@ -1754,7 +1762,7 @@ void HTMLInputElement::reset_algorithm()
// set the value of the element to the value of the value content attribute, if there is one, or the empty string otherwise,
auto old_value = move(m_value);
m_value = get_attribute_value(AttributeNames::value);
m_value = Utf16String::from_utf8(get_attribute_value(AttributeNames::value));
// set the checkedness of the element to true if the element has a checked content attribute and false if it does not,
m_checked = has_attribute(AttributeNames::checked);
@ -1770,7 +1778,7 @@ void HTMLInputElement::reset_algorithm()
relevant_value_was_changed();
if (m_text_node) {
m_text_node->set_data(Utf16String::from_utf8(m_value));
m_text_node->set_data(m_value);
update_placeholder_visibility();
}
@ -1786,7 +1794,7 @@ void HTMLInputElement::clear_algorithm()
// set the value of the element to an empty string,
auto old_value = move(m_value);
m_value = String {};
m_value = {};
// set the checkedness of the element to true if the element has a checked content attribute and false if it does not,
m_checked = has_attribute(AttributeNames::checked);
@ -1806,7 +1814,7 @@ void HTMLInputElement::clear_algorithm()
relevant_value_was_changed();
if (m_text_node) {
m_text_node->set_data(Utf16String::from_utf8(m_value));
m_text_node->set_data(m_value);
update_placeholder_visibility();
}
@ -2310,19 +2318,28 @@ Optional<double> HTMLInputElement::convert_string_to_number(StringView input) co
return {};
}
// https://html.spec.whatwg.org/multipage/input.html#concept-input-value-string-number
Optional<double> HTMLInputElement::convert_string_to_number(Utf16String const& input) const
{
// FIXME: Implement a UTF-16 GenericLexer.
if (!input.has_ascii_storage())
return {};
return convert_string_to_number(input.ascii_view());
}
// https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):concept-input-value-number-string
static String convert_number_to_month_string(double input)
static Utf16String convert_number_to_month_string(double input)
{
// The algorithm to convert a number to a string, given a number input, is as follows: Return a valid month
// string that represents the month that has input months between it and January 1970.
auto months = JS::modulo(input, 12);
auto year = 1970 + (input - months) / 12;
return MUST(String::formatted("{:04d}-{:02d}", static_cast<int>(year), static_cast<int>(months) + 1));
return Utf16String::formatted("{:04d}-{:02d}", static_cast<int>(year), static_cast<int>(months) + 1);
}
// https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week):concept-input-value-string-number
static String convert_number_to_week_string(double input)
static Utf16String convert_number_to_week_string(double input)
{
// The algorithm to convert a number to a string, given a number input, is as follows: Return a valid week string that
// that represents the week that, in UTC, is current input milliseconds after midnight UTC on the morning of 1970-01-01
@ -2348,21 +2365,21 @@ static String convert_number_to_week_string(double input)
week = weeks_in_year(year) + week;
}
return MUST(String::formatted("{:04d}-W{:02d}", year, week));
return Utf16String::formatted("{:04d}-W{:02d}", year, week);
}
// https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):concept-input-value-number-string
static String convert_number_to_date_string(double input)
static Utf16String convert_number_to_date_string(double input)
{
// The algorithm to convert a number to a string, given a number input, is as follows: Return a valid
// date string that represents the date that, in UTC, is current input milliseconds after midnight UTC
// on the morning of 1970-01-01 (the time represented by the value "1970-01-01T00:00:00.0Z").
auto date = AK::UnixDateTime::from_seconds_since_epoch(input / 1000.);
return MUST(date.to_string("%Y-%m-%d"sv, AK::UnixDateTime::LocalTime::No));
return date.to_utf16_string("%Y-%m-%d"sv, AK::UnixDateTime::LocalTime::No);
}
// https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):concept-input-value-number-string
static String convert_number_to_time_string(double input)
static Utf16String convert_number_to_time_string(double input)
{
// The algorithm to convert a number to a string, given a number input, is as follows: Return a valid time
// string that represents the time that is input milliseconds after midnight on a day with no time changes.
@ -2370,14 +2387,14 @@ static String convert_number_to_time_string(double input)
auto milliseconds = JS::ms_from_time(input);
if (seconds > 0) {
if (milliseconds > 0)
return MUST(String::formatted("{:02d}:{:02d}:{:02d}.{:3d}", JS::hour_from_time(input), JS::min_from_time(input), seconds, milliseconds));
return MUST(String::formatted("{:02d}:{:02d}:{:02d}", JS::hour_from_time(input), JS::min_from_time(input), seconds));
return Utf16String::formatted("{:02d}:{:02d}:{:02d}.{:3d}", JS::hour_from_time(input), JS::min_from_time(input), seconds, milliseconds);
return Utf16String::formatted("{:02d}:{:02d}:{:02d}", JS::hour_from_time(input), JS::min_from_time(input), seconds);
}
return MUST(String::formatted("{:02d}:{:02d}", JS::hour_from_time(input), JS::min_from_time(input)));
return Utf16String::formatted("{:02d}:{:02d}", JS::hour_from_time(input), JS::min_from_time(input));
}
// https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local):concept-input-value-number-string
static String convert_number_to_local_date_and_time_string(double input)
static Utf16String convert_number_to_local_date_and_time_string(double input)
{
// The algorithm to convert a number to a string, given a number input, is as follows: Return a valid
// normalized local date and time string that represents the date and time that is input milliseconds
@ -2392,23 +2409,23 @@ static String convert_number_to_local_date_and_time_string(double input)
if (seconds > 0) {
if (milliseconds > 0)
return MUST(String::formatted("{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}.{:03d}", year, month, day, hour, minutes, seconds, milliseconds));
return MUST(String::formatted("{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}", year, month, day, hour, minutes, seconds));
return Utf16String::formatted("{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}.{:03d}", year, month, day, hour, minutes, seconds, milliseconds);
return Utf16String::formatted("{:04d}-{:02d}-{:02d}T{:02d}:{:02d}:{:02d}", year, month, day, hour, minutes, seconds);
}
return MUST(String::formatted("{:04d}-{:02d}-{:02d}T{:02d}:{:02d}", year, month, day, hour, minutes));
return Utf16String::formatted("{:04d}-{:02d}-{:02d}T{:02d}:{:02d}", year, month, day, hour, minutes);
}
// https://html.spec.whatwg.org/multipage/input.html#concept-input-value-string-number
String HTMLInputElement::convert_number_to_string(double input) const
Utf16String HTMLInputElement::convert_number_to_string(double input) const
{
// https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number):concept-input-value-number-string
if (type_state() == TypeAttributeState::Number)
return String::number(input);
return Utf16String::number(input);
// https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range):concept-input-value-number-string
if (type_state() == TypeAttributeState::Range)
return String::number(input);
return Utf16String::number(input);
if (type_state() == TypeAttributeState::Month)
return convert_number_to_month_string(input);
@ -2459,8 +2476,17 @@ WebIDL::ExceptionOr<GC::Ptr<JS::Date>> HTMLInputElement::convert_string_to_date(
return nullptr;
}
// https://html.spec.whatwg.org/multipage/input.html#concept-input-value-string-date
WebIDL::ExceptionOr<GC::Ptr<JS::Date>> HTMLInputElement::convert_string_to_date(Utf16String const& input) const
{
// FIXME: Implement a UTF-16 GenericLexer.
if (!input.has_ascii_storage())
return nullptr;
return convert_string_to_date(input.ascii_view());
}
// https://html.spec.whatwg.org/multipage/input.html#concept-input-value-date-string
String HTMLInputElement::convert_date_to_string(GC::Ref<JS::Date> input) const
Utf16String HTMLInputElement::convert_date_to_string(GC::Ref<JS::Date> input) const
{
// https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):concept-input-value-date-string
if (type_state() == TypeAttributeState::Date) {
@ -2665,12 +2691,12 @@ WebIDL::ExceptionOr<void> HTMLInputElement::set_value_as_date(Optional<GC::Root<
// otherwise if the new value is null or a Date object representing the NaN time value, then set the value of the element to the empty string;
if (!value.has_value()) {
TRY(set_value(String {}));
TRY(set_value({}));
return {};
}
auto& date = static_cast<JS::Date&>(**value);
if (!isfinite(date.date_value())) {
TRY(set_value(String {}));
TRY(set_value({}));
return {};
}
@ -2704,7 +2730,7 @@ WebIDL::ExceptionOr<void> HTMLInputElement::set_value_as_number(double value)
// Otherwise, if the new value is a Not-a-Number (NaN) value, then set the value of the element to the empty string.
if (value == NAN) {
TRY(set_value(String {}));
TRY(set_value({}));
return {};
}
@ -3301,46 +3327,58 @@ bool HTMLInputElement::suffering_from_a_type_mismatch() const
{
auto input = value();
switch (type_state()) {
// https://html.spec.whatwg.org/multipage/input.html#url-state-(type%3Durl)%3Asuffering-from-a-type-mismatch
case TypeAttributeState::URL:
// https://html.spec.whatwg.org/multipage/input.html#url-state-(type%3Durl)%3Asuffering-from-a-type-mismatch
// While the value of the element is neither the empty string nor a valid absolute URL, the element is suffering from a type mismatch.
// AD-HOC: https://github.com/whatwg/html/issues/11083 and https://github.com/web-platform-tests/wpt/pull/51011
// We intentionally don't check if the value is a "valid absolute URL", because that's not what other
// engines actually do. So we instead just implement what matches the behavior in existing engines.
return !input.is_empty() && !URL::Parser::basic_parse(input).has_value();
case TypeAttributeState::Email:
// https://html.spec.whatwg.org/multipage/input.html#email-state-(type%3Demail)%3Asuffering-from-a-type-mismatch
return !input.is_empty() && !URL::Parser::basic_parse(input.to_utf8_but_should_be_ported_to_utf16()).has_value();
// https://html.spec.whatwg.org/multipage/input.html#email-state-(type%3Demail)%3Asuffering-from-a-type-mismatch
case TypeAttributeState::Email: {
// When the multiple attribute is not specified on the element: While the value of the element is neither the
// empty string nor a single valid email address, the element is suffering from a type mismatch.
if (!has_attribute(HTML::AttributeNames::multiple))
return !input.is_empty() && !valid_email_address_regex.match(input).success;
return !input.is_empty() && !valid_email_address_regex.match(input.utf16_view()).success;
// When the multiple attribute is specified on the element: While the value of the element is not a valid email
// address list, the element is suffering from a type mismatch.
// https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address-list
// A valid email address list is a set of comma-separated tokens, where each token is itself a valid email
// address. To obtain the list of tokens from a valid email address list, an implementation must split the
// string on commas.
for (auto& address : MUST(input.split(','))) {
if (!valid_email_address_regex.match(address).success)
return true;
}
break;
bool valid = true;
input.for_each_split_view(',', SplitBehavior::Nothing, [&](auto const& address) {
if (valid_email_address_regex.match(address).success)
return IterationDecision::Continue;
valid = false;
return IterationDecision::Break;
});
return !valid;
}
default:
break;
}
return false;
}
// https://html.spec.whatwg.org/multipage/input.html#the-pattern-attribute%3Asuffering-from-a-pattern-mismatch
bool HTMLInputElement::suffering_from_a_pattern_mismatch() const
{
// If the element's value is not the empty string, and either the element's multiple attribute is not specified or it does not apply to the input element given its
// type attribute's current state, and the element has a compiled pattern regular expression but that regular expression does not match the element's value, then the element is
// If the element's value is not the empty string, and either the element's multiple attribute is not specified or
// it does not apply to the input element given its type attribute's current state, and the element has a compiled
// pattern regular expression but that regular expression does not match the element's value, then the element is
// suffering from a pattern mismatch.
// If the element's value is not the empty string, and the element's multiple attribute is specified and applies to the input element,
// and the element has a compiled pattern regular expression but that regular expression does not match each of the element's values,
// then the element is suffering from a pattern mismatch.
// If the element's value is not the empty string, and the element's multiple attribute is specified and applies to
// the input element, and the element has a compiled pattern regular expression but that regular expression does not
// match each of the element's values, then the element is suffering from a pattern mismatch.
if (!pattern_applies())
return false;
@ -3355,13 +3393,20 @@ bool HTMLInputElement::suffering_from_a_pattern_mismatch() const
if (has_attribute(HTML::AttributeNames::multiple) && multiple_applies()) {
VERIFY(type_state() == HTMLInputElement::TypeAttributeState::Email);
bool valid = true;
return AK::any_of(MUST(value.split(',')), [&regexp_object](auto const& value) {
return !regexp_object->match(value).success;
value.for_each_split_view(',', SplitBehavior::Nothing, [&](auto const& value) {
if (regexp_object->match(value).success)
return IterationDecision::Continue;
valid = false;
return IterationDecision::Break;
});
return !valid;
}
return !regexp_object->match(value).success;
return !regexp_object->match(value.utf16_view()).success;
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#suffering-from-an-underflow

View file

@ -79,13 +79,13 @@ public:
String default_value() const { return get_attribute_value(HTML::AttributeNames::value); }
virtual String value() const override;
virtual Utf16String value() const override;
virtual Optional<String> optional_value() const override;
WebIDL::ExceptionOr<void> set_value(String const&);
WebIDL::ExceptionOr<void> set_value(Utf16String const&);
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-textarea/input-relevant-value
virtual Utf16String relevant_value() override { return Utf16String::from_utf8(value()); }
WebIDL::ExceptionOr<void> set_relevant_value(Utf16String const& value) override { return set_value(value.to_utf8_but_should_be_ported_to_utf16()); }
virtual Utf16String relevant_value() override { return value(); }
WebIDL::ExceptionOr<void> set_relevant_value(Utf16String const& value) override { return set_value(value); }
virtual void set_dirty_value_flag(bool flag) override { m_dirty_value = flag; }
@ -290,10 +290,12 @@ private:
Optional<double> convert_time_string_to_number(StringView input) const;
Optional<double> convert_string_to_number(StringView input) const;
String convert_number_to_string(double input) const;
Optional<double> convert_string_to_number(Utf16String const& input) const;
Utf16String convert_number_to_string(double input) const;
WebIDL::ExceptionOr<GC::Ptr<JS::Date>> convert_string_to_date(StringView input) const;
String convert_date_to_string(GC::Ref<JS::Date> input) const;
WebIDL::ExceptionOr<GC::Ptr<JS::Date>> convert_string_to_date(Utf16String const& input) const;
Utf16String convert_date_to_string(GC::Ref<JS::Date> input) const;
Optional<double> min() const;
Optional<double> max() const;
@ -304,6 +306,9 @@ private:
WebIDL::ExceptionOr<void> step_up_or_down(bool is_down, WebIDL::Long n);
static TypeAttributeState parse_type_attribute(StringView);
Utf16String button_label() const;
void create_shadow_tree_if_needed();
void update_shadow_tree();
void create_button_input_shadow_tree();
@ -321,7 +326,7 @@ private:
void user_interaction_did_change_input_value();
// https://html.spec.whatwg.org/multipage/input.html#value-sanitization-algorithm
String value_sanitization_algorithm(String const&) const;
Utf16String value_sanitization_algorithm(Utf16String const&) const;
enum class ValueAttributeMode {
Value,
@ -386,7 +391,7 @@ private:
GC::Ptr<FileAPI::FileList> m_selected_files;
TypeAttributeState m_type { TypeAttributeState::Text };
String m_value;
Utf16String m_value;
String m_last_src_value;

View file

@ -44,7 +44,7 @@ interface HTMLInputElement : HTMLElement {
[CEReactions, Reflect] attribute boolean switch;
[CEReactions] attribute DOMString type;
[CEReactions, Reflect=value] attribute DOMString defaultValue;
[CEReactions, LegacyNullToEmptyString] attribute DOMString value;
[CEReactions, LegacyNullToEmptyString] attribute Utf16DOMString value;
attribute object? valueAsDate;
attribute unrestricted double valueAsNumber;
[CEReactions] attribute unsigned long width;

View file

@ -89,15 +89,15 @@ void HTMLOptionElement::set_selected_internal(bool selected)
}
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-value
String HTMLOptionElement::value() const
Utf16String HTMLOptionElement::value() const
{
// The value of an option element is the value of the value content attribute, if there is one.
// ...or, if there is not, the value of the element's text IDL attribute.
return attribute(HTML::AttributeNames::value).value_or(text());
return Utf16String::from_utf8(attribute(HTML::AttributeNames::value).value_or(text()));
}
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-option-value
WebIDL::ExceptionOr<void> HTMLOptionElement::set_value(String const& value)
WebIDL::ExceptionOr<void> HTMLOptionElement::set_value(Utf16String const& value)
{
return set_attribute(HTML::AttributeNames::value, value);
}

View file

@ -23,8 +23,8 @@ public:
void set_selected_internal(bool);
[[nodiscard]] u64 selectedness_update_index() const { return m_selectedness_update_index; }
String value() const;
WebIDL::ExceptionOr<void> set_value(String const&);
Utf16String value() const;
WebIDL::ExceptionOr<void> set_value(Utf16String const&);
String text() const;
void set_text(String const&);

View file

@ -10,7 +10,7 @@ interface HTMLOptionElement : HTMLElement {
[CEReactions] attribute DOMString label;
[CEReactions, Reflect=selected] attribute boolean defaultSelected;
attribute boolean selected;
[CEReactions] attribute DOMString value;
[CEReactions] attribute Utf16DOMString value;
[CEReactions] attribute DOMString text;
readonly attribute long index;

View file

@ -74,20 +74,20 @@ void HTMLOutputElement::set_default_value(String const& default_value)
}
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-output-value
String HTMLOutputElement::value() const
Utf16String HTMLOutputElement::value() const
{
// The value getter steps are to return this's descendant text content.
return descendant_text_content();
return Utf16String::from_utf8(descendant_text_content());
}
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-output-value
void HTMLOutputElement::set_value(String const& value)
void HTMLOutputElement::set_value(Utf16String const& value)
{
// 1. Set this's default value override to its default value.
m_default_value_override = default_value();
// 2. String replace all with the given value within this.
string_replace_all(value);
string_replace_all(value.to_utf8_but_should_be_ported_to_utf16());
}
// https://html.spec.whatwg.org/multipage/form-elements.html#the-output-element:concept-form-reset-control

View file

@ -34,8 +34,8 @@ public:
String default_value() const;
void set_default_value(String const&);
String value() const override;
void set_value(String const&);
Utf16String value() const override;
void set_value(Utf16String const&);
// ^FormAssociatedElement
// https://html.spec.whatwg.org/multipage/forms.html#category-listed

View file

@ -13,7 +13,7 @@ interface HTMLOutputElement : HTMLElement {
readonly attribute DOMString type;
[CEReactions] attribute DOMString defaultValue;
[CEReactions] attribute DOMString value;
[CEReactions] attribute Utf16DOMString value;
readonly attribute boolean willValidate;
readonly attribute ValidityState validity;

View file

@ -365,16 +365,16 @@ Optional<ARIA::Role> HTMLSelectElement::default_role() const
return ARIA::Role::combobox;
}
String HTMLSelectElement::value() const
Utf16String HTMLSelectElement::value() const
{
update_cached_list_of_options();
for (auto const& option_element : m_cached_list_of_options)
if (option_element->selected())
return option_element->value();
return ""_string;
return {};
}
WebIDL::ExceptionOr<void> HTMLSelectElement::set_value(String const& value)
WebIDL::ExceptionOr<void> HTMLSelectElement::set_value(Utf16String const& value)
{
update_cached_list_of_options();
for (auto const& option_element : list_of_options())
@ -478,7 +478,7 @@ void HTMLSelectElement::show_the_picker_if_applicable()
for (auto const& child : opt_group_element->children_as_vector()) {
if (auto const& option_element = as_if<HTMLOptionElement>(*child)) {
if (!option_element->has_attribute(Web::HTML::AttributeNames::hidden))
option_group_items.append(SelectItemOption { id_counter++, option_element->selected(), option_element->disabled(), option_element, strip_newlines(option_element->label()), option_element->value() });
option_group_items.append(SelectItemOption { id_counter++, option_element->selected(), option_element->disabled(), option_element, strip_newlines(option_element->label()), option_element->value().to_utf8_but_should_be_ported_to_utf16() });
}
}
m_select_items.append(SelectItemOptionGroup { opt_group_element->get_attribute(AttributeNames::label).value_or(String {}), option_group_items });
@ -487,7 +487,7 @@ void HTMLSelectElement::show_the_picker_if_applicable()
if (auto const& option_element = as_if<HTMLOptionElement>(*child)) {
if (!option_element->has_attribute(Web::HTML::AttributeNames::hidden))
m_select_items.append(SelectItemOption { id_counter++, option_element->selected(), option_element->disabled(), option_element, strip_newlines(option_element->label()), option_element->value() });
m_select_items.append(SelectItemOption { id_counter++, option_element->selected(), option_element->disabled(), option_element, strip_newlines(option_element->label()), option_element->value().to_utf8_but_should_be_ported_to_utf16() });
}
if (auto const* hr_element = as_if<HTMLHRElement>(*child)) {

View file

@ -51,8 +51,8 @@ public:
WebIDL::Long selected_index() const;
void set_selected_index(WebIDL::Long);
virtual String value() const override;
WebIDL::ExceptionOr<void> set_value(String const&);
virtual Utf16String value() const override;
WebIDL::ExceptionOr<void> set_value(Utf16String const&);
bool is_open() const { return m_is_open; }
void set_is_open(bool);

View file

@ -29,7 +29,7 @@ interface HTMLSelectElement : HTMLElement {
[SameObject] readonly attribute HTMLCollection selectedOptions;
attribute long selectedIndex;
attribute DOMString value;
attribute Utf16DOMString value;
readonly attribute boolean willValidate;
readonly attribute ValidityState validity;

View file

@ -114,10 +114,10 @@ void HTMLTextAreaElement::reset_algorithm()
m_user_validity = false;
m_dirty_value = false;
// and the raw value to its child text content.
set_raw_value(child_text_content());
set_raw_value(Utf16String::from_utf8(child_text_content()));
if (m_text_node) {
m_text_node->set_text_content(m_raw_value);
m_text_node->set_text_content(m_raw_value.to_utf8_but_should_be_ported_to_utf16());
update_placeholder_visibility();
}
}
@ -129,7 +129,7 @@ void HTMLTextAreaElement::clear_algorithm()
m_dirty_value = false;
// and set the raw value of element to an empty string.
set_raw_value(child_text_content());
set_raw_value(Utf16String::from_utf8(child_text_content()));
// Unlike their associated reset algorithms, changes made to form controls as part of these algorithms do count as
// changes caused by the user (and thus, e.g. do cause input events to fire).
@ -169,14 +169,14 @@ void HTMLTextAreaElement::set_default_value(String const& default_value)
}
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-textarea-value
String HTMLTextAreaElement::value() const
Utf16String HTMLTextAreaElement::value() const
{
// The value IDL attribute must, on getting, return the element's API value.
return api_value();
}
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-textarea-value
void HTMLTextAreaElement::set_value(String const& value)
void HTMLTextAreaElement::set_value(Utf16String const& value)
{
// 1. Let oldAPIValue be this element's API value.
auto old_api_value = api_value();
@ -191,7 +191,7 @@ void HTMLTextAreaElement::set_value(String const& value)
// the text control, unselecting any selected text and resetting the selection direction to "none".
if (api_value() != old_api_value) {
if (m_text_node) {
m_text_node->set_data(Utf16String::from_utf8(m_raw_value));
m_text_node->set_data(m_raw_value);
update_placeholder_visibility();
set_the_selection_range(m_text_node->length(), m_text_node->length());
@ -199,7 +199,7 @@ void HTMLTextAreaElement::set_value(String const& value)
}
}
void HTMLTextAreaElement::set_raw_value(String value)
void HTMLTextAreaElement::set_raw_value(Utf16String value)
{
auto old_raw_value = move(m_raw_value);
m_raw_value = move(value);
@ -210,7 +210,7 @@ void HTMLTextAreaElement::set_raw_value(String value)
}
// https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element:concept-fe-api-value-3
String HTMLTextAreaElement::api_value() const
Utf16String HTMLTextAreaElement::api_value() const
{
// The algorithm for obtaining the element's API value is to return the element's raw value, with newlines normalized.
if (!m_api_value.has_value())
@ -221,7 +221,7 @@ String HTMLTextAreaElement::api_value() const
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-textarea/input-relevant-value
WebIDL::ExceptionOr<void> HTMLTextAreaElement::set_relevant_value(Utf16String const& value)
{
set_value(value.to_utf8_but_should_be_ported_to_utf16());
set_value(value);
return {};
}
@ -229,7 +229,7 @@ WebIDL::ExceptionOr<void> HTMLTextAreaElement::set_relevant_value(Utf16String co
u32 HTMLTextAreaElement::text_length() const
{
// The textLength IDL attribute must return the length of the element's API value.
return AK::utf16_code_unit_length_from_utf8(api_value());
return api_value().length_in_code_units();
}
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-willvalidate
@ -379,7 +379,7 @@ void HTMLTextAreaElement::create_shadow_tree_if_needed()
handle_readonly_attribute(attribute(HTML::AttributeNames::readonly));
// 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);
m_text_node->set_text_content(m_raw_value.to_utf8_but_should_be_ported_to_utf16());
handle_maxlength_attribute();
MUST(m_inner_text_element->append_child(*m_text_node));
@ -430,9 +430,9 @@ void HTMLTextAreaElement::children_changed(ChildrenChangedMetadata const* metada
// The children changed steps for textarea elements must, if the element's dirty value flag is false,
// set the element's raw value to its child text content.
if (!m_dirty_value) {
set_raw_value(child_text_content());
set_raw_value(Utf16String::from_utf8(child_text_content()));
if (m_text_node)
m_text_node->set_text_content(m_raw_value);
m_text_node->set_text_content(m_raw_value.to_utf8_but_should_be_ported_to_utf16());
update_placeholder_visibility();
}
}
@ -452,7 +452,7 @@ void HTMLTextAreaElement::form_associated_element_attribute_changed(FlyString co
void HTMLTextAreaElement::did_edit_text_node()
{
VERIFY(m_text_node);
set_raw_value(m_text_node->data().to_utf8_but_should_be_ported_to_utf16());
set_raw_value(m_text_node->data());
// Any time the user causes the element's raw value to change, the user agent must queue an element task on the user
// interaction task source given the textarea element to fire an event named input at the textarea element, with the

View file

@ -80,14 +80,14 @@ public:
String default_value() const;
void set_default_value(String const&);
String value() const override;
void set_value(String const&);
Utf16String value() const override;
void set_value(Utf16String const&);
// https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element:concept-fe-api-value-3
String api_value() const;
Utf16String api_value() const;
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-textarea/input-relevant-value
virtual Utf16String relevant_value() override { return Utf16String::from_utf8(api_value()); }
virtual Utf16String relevant_value() override { return api_value(); }
virtual WebIDL::ExceptionOr<void> set_relevant_value(Utf16String const& value) override;
virtual void set_dirty_value_flag(bool flag) override { m_dirty_value = flag; }
@ -140,7 +140,7 @@ private:
virtual void initialize(JS::Realm&) override;
virtual void visit_edges(Cell::Visitor&) override;
void set_raw_value(String);
void set_raw_value(Utf16String);
// ^DOM::Element
virtual i32 default_tab_index_value() const override;
@ -169,10 +169,10 @@ private:
bool m_user_validity { false };
// https://html.spec.whatwg.org/multipage/form-elements.html#concept-textarea-raw-value
String m_raw_value;
Utf16String m_raw_value;
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#concept-fe-api-value
mutable Optional<String> m_api_value;
mutable Optional<Utf16String> m_api_value;
};
}

View file

@ -23,7 +23,7 @@ interface HTMLTextAreaElement : HTMLElement {
readonly attribute DOMString type;
[CEReactions] attribute DOMString defaultValue;
[LegacyNullToEmptyString] attribute DOMString value;
[LegacyNullToEmptyString] attribute Utf16DOMString value;
readonly attribute unsigned long textLength;
readonly attribute boolean willValidate;

View file

@ -6,6 +6,7 @@
#include <AK/GenericLexer.h>
#include <AK/StringConversions.h>
#include <AK/Utf16String.h>
#include <LibWeb/HTML/Numbers.h>
#include <LibWeb/Infra/CharacterTypes.h>
#include <math.h>
@ -283,7 +284,7 @@ fraction_exit:
value *= pow(10, exponent);
}
conversion: {
conversion:
// 15. Conversion: Let S be the set of finite IEEE 754 double-precision floating-point values except 0,
// but with two special values added: 2^1024 and 2^1024.
if (!isfinite(value)) {
@ -305,6 +306,14 @@ conversion: {
// 18. Return rounded-value.
return rounded_value;
}
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-floating-point-number-values
Optional<double> parse_floating_point_number(Utf16String const& string)
{
// FIXME: Implement a UTF-16 GenericLexer.
if (!string.has_ascii_storage())
return {};
return parse_floating_point_number(string.ascii_view());
}
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-floating-point-number
@ -340,6 +349,15 @@ bool is_valid_floating_point_number(StringView string)
return lexer.tell_remaining() == 0;
}
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-floating-point-number
bool is_valid_floating_point_number(Utf16String const& string)
{
// FIXME: Implement a UTF-16 GenericLexer.
if (!string.has_ascii_storage())
return false;
return is_valid_floating_point_number(string.ascii_view());
}
WebIDL::ExceptionOr<String> convert_non_negative_integer_to_string(JS::Realm& realm, WebIDL::Long value)
{
if (value < 0)

View file

@ -20,8 +20,10 @@ Optional<u32> parse_non_negative_integer(StringView string);
Optional<StringView> parse_non_negative_integer_digits(StringView string);
Optional<double> parse_floating_point_number(StringView string);
Optional<double> parse_floating_point_number(Utf16String const& string);
bool is_valid_floating_point_number(StringView string);
bool is_valid_floating_point_number(Utf16String const& string);
WebIDL::ExceptionOr<String> convert_non_negative_integer_to_string(JS::Realm&, WebIDL::Long);

View file

@ -43,6 +43,30 @@ String normalize_newlines(String const& string)
return MUST(builder.to_string());
}
// https://infra.spec.whatwg.org/#normalize-newlines
Utf16String normalize_newlines(Utf16String const& string)
{
// To normalize newlines in a string, replace every U+000D CR U+000A LF code point pair with a single U+000A LF
// code point, and then replace every remaining U+000D CR code point with a U+000A LF code point.
if (!string.contains('\r'))
return string;
// FIXME: Implement a UTF-16 GenericLexer.
StringBuilder builder(StringBuilder::Mode::UTF16, string.length_in_code_units());
for (size_t i = 0; i < string.length_in_code_units(); ++i) {
if (auto code_unit = string.code_unit_at(i); code_unit == '\r') {
if (i + 1 < string.length_in_code_units() && string.code_unit_at(i + 1) == '\n')
++i;
builder.append('\n');
} else {
builder.append_code_unit(code_unit);
}
}
return builder.to_utf16_string();
}
// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
ErrorOr<String> strip_and_collapse_whitespace(StringView string)
{

View file

@ -14,6 +14,7 @@
namespace Web::Infra {
String normalize_newlines(String const&);
Utf16String normalize_newlines(Utf16String const&);
ErrorOr<String> strip_and_collapse_whitespace(StringView string);
bool is_code_unit_prefix(StringView potential_prefix, StringView input);
ErrorOr<String> convert_to_scalar_value_string(StringView string);

View file

@ -1951,7 +1951,7 @@ Web::WebDriver::Response WebDriverConnection::element_send_keys_impl(StringView
return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::ElementNotInteractable, "Element is immutable"sv);
// 3. Set a property value to text on element.
MUST(input_element.set_value(text));
MUST(input_element.set_value(Utf16String::from_utf8(text)));
// FIXME: 4. If element is suffering from bad input return an error with error code invalid argument.

View file

@ -2,8 +2,8 @@ Harness status: OK
Found 85 tests
71 Pass
14 Fail
78 Pass
7 Fail
Pass [INPUT in TEXT status] The pattern attribute is not set
Pass [INPUT in TEXT status] The value attibute is empty string
Pass [INPUT in TEXT status] The value attribute matches the pattern attribute
@ -13,7 +13,7 @@ Pass [INPUT in TEXT status] The value attribute mismatches the pattern attribute
Pass [INPUT in TEXT status] Invalid regular expression gets ignored
Pass [INPUT in TEXT status] Invalid `v` regular expression gets ignored
Pass [INPUT in TEXT status] The pattern attribute tries to escape a group
Fail [INPUT in TEXT status] The pattern attribute uses Unicode features
Pass [INPUT in TEXT status] The pattern attribute uses Unicode features
Pass [INPUT in TEXT status] The value attribute matches JavaScript-specific regular expression
Fail [INPUT in TEXT status] The value attribute mismatches JavaScript-specific regular expression
Pass [INPUT in SEARCH status] The pattern attribute is not set
@ -25,7 +25,7 @@ Pass [INPUT in SEARCH status] The value attribute mismatches the pattern attribu
Pass [INPUT in SEARCH status] Invalid regular expression gets ignored
Pass [INPUT in SEARCH status] Invalid `v` regular expression gets ignored
Pass [INPUT in SEARCH status] The pattern attribute tries to escape a group
Fail [INPUT in SEARCH status] The pattern attribute uses Unicode features
Pass [INPUT in SEARCH status] The pattern attribute uses Unicode features
Pass [INPUT in SEARCH status] The value attribute matches JavaScript-specific regular expression
Fail [INPUT in SEARCH status] The value attribute mismatches JavaScript-specific regular expression
Pass [INPUT in TEL status] The pattern attribute is not set
@ -37,7 +37,7 @@ Pass [INPUT in TEL status] The value attribute mismatches the pattern attribute
Pass [INPUT in TEL status] Invalid regular expression gets ignored
Pass [INPUT in TEL status] Invalid `v` regular expression gets ignored
Pass [INPUT in TEL status] The pattern attribute tries to escape a group
Fail [INPUT in TEL status] The pattern attribute uses Unicode features
Pass [INPUT in TEL status] The pattern attribute uses Unicode features
Pass [INPUT in TEL status] The value attribute matches JavaScript-specific regular expression
Fail [INPUT in TEL status] The value attribute mismatches JavaScript-specific regular expression
Pass [INPUT in URL status] The pattern attribute is not set
@ -49,7 +49,7 @@ Pass [INPUT in URL status] The value attribute mismatches the pattern attribute
Pass [INPUT in URL status] Invalid regular expression gets ignored
Pass [INPUT in URL status] Invalid `v` regular expression gets ignored
Pass [INPUT in URL status] The pattern attribute tries to escape a group
Fail [INPUT in URL status] The pattern attribute uses Unicode features
Pass [INPUT in URL status] The pattern attribute uses Unicode features
Pass [INPUT in URL status] The value attribute matches JavaScript-specific regular expression
Fail [INPUT in URL status] The value attribute mismatches JavaScript-specific regular expression
Pass [INPUT in EMAIL status] The pattern attribute is not set
@ -61,7 +61,7 @@ Pass [INPUT in EMAIL status] The value attribute mismatches the pattern attribut
Pass [INPUT in EMAIL status] Invalid regular expression gets ignored
Pass [INPUT in EMAIL status] Invalid `v` regular expression gets ignored
Pass [INPUT in EMAIL status] The pattern attribute tries to escape a group
Fail [INPUT in EMAIL status] The pattern attribute uses Unicode features
Pass [INPUT in EMAIL status] The pattern attribute uses Unicode features
Pass [INPUT in EMAIL status] The value attribute matches JavaScript-specific regular expression
Fail [INPUT in EMAIL status] The value attribute mismatches JavaScript-specific regular expression
Pass [INPUT in PASSWORD status] The pattern attribute is not set
@ -73,7 +73,7 @@ Pass [INPUT in PASSWORD status] The value attribute mismatches the pattern attri
Pass [INPUT in PASSWORD status] Invalid regular expression gets ignored
Pass [INPUT in PASSWORD status] Invalid `v` regular expression gets ignored
Pass [INPUT in PASSWORD status] The pattern attribute tries to escape a group
Fail [INPUT in PASSWORD status] The pattern attribute uses Unicode features
Pass [INPUT in PASSWORD status] The pattern attribute uses Unicode features
Pass [INPUT in PASSWORD status] The value attribute matches JavaScript-specific regular expression
Fail [INPUT in PASSWORD status] The value attribute mismatches JavaScript-specific regular expression
Pass [INPUT in EMAIL status] The pattern attribute is not set, if multiple is present
@ -85,7 +85,7 @@ Pass [INPUT in EMAIL status] The value attribute mismatches the pattern attribut
Pass [INPUT in EMAIL status] Invalid regular expression gets ignored, if multiple is present
Pass [INPUT in EMAIL status] Invalid `v` regular expression gets ignored, if multiple is present
Pass [INPUT in EMAIL status] The pattern attribute tries to escape a group, if multiple is present
Fail [INPUT in EMAIL status] The pattern attribute uses Unicode features, if multiple is present
Pass [INPUT in EMAIL status] The pattern attribute uses Unicode features, if multiple is present
Pass [INPUT in EMAIL status] The value attribute matches JavaScript-specific regular expression, if multiple is present
Fail [INPUT in EMAIL status] The value attribute mismatches JavaScript-specific regular expression, if multiple is present
Pass [INPUT in EMAIL status] Commas should be stripped from regex input, if multiple is present

View file

@ -2,9 +2,8 @@ Harness status: OK
Found 4 tests
3 Pass
1 Fail
4 Pass
Pass basic <input pattern> support
Fail <input pattern> is Unicode code point-aware
Pass <input pattern> is Unicode code point-aware
Pass <input pattern> supports Unicode property escape syntax
Pass <input pattern> supports Unicode property escape syntax for properties of strings