diff --git a/Tests/LibWeb/TestNumbers.cpp b/Tests/LibWeb/TestNumbers.cpp index 56021212cc3..3ba0c4df2cd 100644 --- a/Tests/LibWeb/TestNumbers.cpp +++ b/Tests/LibWeb/TestNumbers.cpp @@ -103,4 +103,31 @@ TEST_CASE(parse_non_negative_integer) optional_value = Web::HTML::parse_non_negative_integer("-3"sv); EXPECT(!optional_value.has_value()); + + EXPECT(Web::HTML::is_valid_floating_point_number("11"sv)); + EXPECT(Web::HTML::is_valid_floating_point_number("11.12"sv)); + EXPECT(Web::HTML::is_valid_floating_point_number("-11111"sv)); + EXPECT(Web::HTML::is_valid_floating_point_number("-11111.123"sv)); + EXPECT(Web::HTML::is_valid_floating_point_number("1e2"sv)); + EXPECT(Web::HTML::is_valid_floating_point_number("1E2"sv)); + EXPECT(Web::HTML::is_valid_floating_point_number("1e+2"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("1d+2"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("foobar"sv)); + EXPECT(Web::HTML::is_valid_floating_point_number(".1"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("1."sv)); + EXPECT(Web::HTML::is_valid_floating_point_number("-0"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("Infinity"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("-Infinity"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("NaN"sv)); + EXPECT(Web::HTML::is_valid_floating_point_number("9007199254740993"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("1e"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("+1"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("+"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("-"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("\t1"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("\n1"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("\f1"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("\r1"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number(" 1"sv)); + EXPECT(!Web::HTML::is_valid_floating_point_number("1trailing junk"sv)); } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 59759bcc1ca..393952a9875 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -1373,8 +1373,14 @@ String HTMLInputElement::value_sanitization_algorithm(String const& value) const return MUST(String::from_utf8(builder.string_view().trim(Infra::ASCII_WHITESPACE))); } } else 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. + // 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. + if (!is_valid_floating_point_number(value)) + return String {}; auto maybe_value = parse_floating_point_number(value); + // 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 (!maybe_value.has_value() || !isfinite(maybe_value.value())) return String {}; } else if (type_state() == HTMLInputElement::TypeAttributeState::Date) { @@ -1402,7 +1408,9 @@ String HTMLInputElement::value_sanitization_algorithm(String const& value) const // 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 (!maybe_value.has_value() || !isfinite(maybe_value.value())) { + if (!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. auto minimum = *min(); auto maximum = *max(); diff --git a/Userland/Libraries/LibWeb/HTML/Numbers.cpp b/Userland/Libraries/LibWeb/HTML/Numbers.cpp index 58d18ccefea..edf5f7b81a7 100644 --- a/Userland/Libraries/LibWeb/HTML/Numbers.cpp +++ b/Userland/Libraries/LibWeb/HTML/Numbers.cpp @@ -92,6 +92,39 @@ Optional parse_floating_point_number(StringView string) return maybe_double.value(); } +// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-floating-point-number +bool is_valid_floating_point_number(StringView string) +{ + GenericLexer lexer { string }; + // 1. Optionally, a U+002D HYPHEN-MINUS character (-). + lexer.consume_specific('-'); + // 2. One or both of the following, in the given order: + // 2.1. A series of one or more ASCII digits. + bool has_leading_digits = !lexer.consume_while(is_ascii_digit).is_empty(); + // 2.2. Both of the following, in the given order: + // 2.2.1. A single U+002E FULL STOP character (.). + if (lexer.consume_specific('.')) { + // 2.2.2. A series of one or more ASCII digits. + if (lexer.consume_while(is_ascii_digit).is_empty()) + return false; + } else if (!has_leading_digits) { + // Doesn’t begin with digits, doesn’t begin with a full stop followed by digits. + return false; + } + // 3. Optionally: + // 3.1. Either a U+0065 LATIN SMALL LETTER E character (e) or a U+0045 LATIN CAPITAL + // LETTER E character (E). + if (lexer.consume_specific('e') || lexer.consume_specific('E')) { + // 3.2. Optionally, a U+002D HYPHEN-MINUS character (-) or U+002B PLUS SIGN + // character (+). + lexer.consume_specific('-') || lexer.consume_specific('+'); + // 3.3. A series of one or more ASCII digits. + if (lexer.consume_while(is_ascii_digit).is_empty()) + return false; + } + return lexer.tell_remaining() == 0; +} + WebIDL::ExceptionOr convert_non_negative_integer_to_string(JS::Realm& realm, WebIDL::Long value) { if (value < 0) diff --git a/Userland/Libraries/LibWeb/HTML/Numbers.h b/Userland/Libraries/LibWeb/HTML/Numbers.h index 28d9be20e57..a3b608e859a 100644 --- a/Userland/Libraries/LibWeb/HTML/Numbers.h +++ b/Userland/Libraries/LibWeb/HTML/Numbers.h @@ -19,6 +19,8 @@ Optional parse_non_negative_integer(StringView string); Optional parse_floating_point_number(StringView string); +bool is_valid_floating_point_number(StringView string); + WebIDL::ExceptionOr convert_non_negative_integer_to_string(JS::Realm&, WebIDL::Long); }