diff --git a/Libraries/LibWeb/HTML/Dates.cpp b/Libraries/LibWeb/HTML/Dates.cpp index e898898d9a5..684088a4591 100644 --- a/Libraries/LibWeb/HTML/Dates.cpp +++ b/Libraries/LibWeb/HTML/Dates.cpp @@ -209,25 +209,6 @@ bool is_valid_time_string(StringView value) return true; } -// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-time-string -WebIDL::ExceptionOr> parse_time_string(JS::Realm& realm, StringView value) -{ - // FIXME: Implement spec compliant time string parsing - auto parts = value.split_view(':', SplitBehavior::KeepEmpty); - if (parts.size() >= 2) { - if (auto hours = parts.at(0).to_number(); hours.has_value()) { - if (auto minutes = parts.at(1).to_number(); minutes.has_value()) { - if (parts.size() >= 3) { - if (auto seconds = parts.at(2).to_number(); seconds.has_value()) - return JS::Date::create(realm, JS::make_time(*hours, *minutes, *seconds, 0)); - } - return JS::Date::create(realm, JS::make_date(0, JS::make_time(*hours, *minutes, 0, 0))); - } - } - } - return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Can't parse time string"sv }; -} - // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-month-component static Optional parse_a_month_component(GenericLexer& input) { @@ -440,7 +421,7 @@ static Optional parse_a_time_component(GenericLexer& input) return {}; // 6. Let second be 0. - i32 second = 0; + f32 second = 0; // 7. If position is not beyond the end of input and the character at position is U+003A (:), then: if (input.consume_specific(':')) { @@ -463,18 +444,13 @@ static Optional parse_a_time_component(GenericLexer& input) return {}; // Otherwise, interpret the resulting sequence as a base-ten number (possibly with a fractional part). Set second // to that number. - // NB: The spec doesn't state requirements for what we must do with the fractional part of the second(s) string. - // The spec neither requires that we separately preserve it nor requires that we completely discard it. If we - // did have any reason at all to preserve it, we could parse the string into a float here. But there doesn't - // seem to be any point in doing that, because there’s nothing in the corresponding calling algorithm(s) in the - // spec that takes a milliseconds field and does anything with it anyway. - auto maybe_second = second_string.to_number(); + auto maybe_second = second_string.to_number(); if (!maybe_second.has_value()) return {}; second = maybe_second.value(); // 4. If second is not a number in the range 0 ≤ second < 60, then fail. - if (second < 0 || second > 60) + if (second < 0 || second >= 60) return {}; } @@ -482,6 +458,27 @@ static Optional parse_a_time_component(GenericLexer& input) return HourMinuteSecond { hour, minute, second }; } +// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-time-string +WebIDL::ExceptionOr> parse_time_string(JS::Realm& realm, StringView value) +{ + // 1. Let input be the string being parsed. + // 2. Let position be a pointer into input, initially pointing at the start of the string. + GenericLexer input { value }; + + // 3. Parse a time component to obtain hour, minute, and second. If this returns nothing, then fail. + auto hour_minute_second = parse_a_time_component(input); + if (!hour_minute_second.has_value()) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Can't parse time string"sv }; + + // 4. If position is not beyond the end of input, then fail. + if (!input.is_eof()) + return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Can't parse time string"sv }; + + // 5. Let time be the time with hour hour, minute minute, and second second. + // 6. Return time. + return JS::Date::create(realm, JS::make_time(hour_minute_second->hour, hour_minute_second->minute, hour_minute_second->second, static_cast(hour_minute_second->second * 1000) % 1000)); +} + // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-local-date-and-time-string Optional parse_a_local_date_and_time_string(StringView input_view) { diff --git a/Libraries/LibWeb/HTML/Dates.h b/Libraries/LibWeb/HTML/Dates.h index a3186299452..05bbcebfca9 100644 --- a/Libraries/LibWeb/HTML/Dates.h +++ b/Libraries/LibWeb/HTML/Dates.h @@ -43,7 +43,7 @@ struct YearMonthDay { struct HourMinuteSecond { i32 hour; i32 minute; - i32 second; + f32 second; }; struct DateAndTime { diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 199a0ea76db..6cb987e4270 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -2209,7 +2209,7 @@ static Optional convert_local_date_and_time_string_to_number(StringView auto date = date_and_time.date; auto time = date_and_time.time; - auto date_time = UnixDateTime::from_unix_time_parts(date.year, date.month, date.day, time.hour, time.minute, time.second, 0); + auto date_time = UnixDateTime::from_unix_time_parts(date.year, date.month, date.day, time.hour, time.minute, time.second, static_cast(time.second * 1000) % 1000); return date_time.milliseconds_since_epoch(); } diff --git a/Tests/LibWeb/Text/expected/HTML/HTMLInputElement-invalid-time-digits.txt b/Tests/LibWeb/Text/expected/HTML/HTMLInputElement-invalid-time-digits.txt index aaecaf93c4a..bf93285b1bb 100644 --- a/Tests/LibWeb/Text/expected/HTML/HTMLInputElement-invalid-time-digits.txt +++ b/Tests/LibWeb/Text/expected/HTML/HTMLInputElement-invalid-time-digits.txt @@ -1 +1,6 @@ +Parsed invalid as NaN +Parsed xx:34:56 as NaN +Parsed 12:xx:56 as NaN +Parsed 12:34:xx as NaN +Parsed 12:34:60 as NaN PASS (didn't crash) diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.txt index 897e3e2ad25..04ffd2307f9 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeOverflow.txt @@ -2,8 +2,7 @@ Harness status: OK Found 49 tests -43 Pass -6 Fail +49 Pass Pass [INPUT in DATETIME-LOCAL status] The max attribute is not set Pass [INPUT in DATETIME-LOCAL status] Value is empty string Pass [INPUT in DATETIME-LOCAL status] The max attribute is an invalid local date time string @@ -11,9 +10,9 @@ Pass [INPUT in DATETIME-LOCAL status] The max attribute is greater than the valu Pass [INPUT in DATETIME-LOCAL status] The value is an invalid local date time string(hour is greater than 23) Pass [INPUT in DATETIME-LOCAL status] The value if an invalid local date time string(year is two digits) Pass [INPUT in DATETIME-LOCAL status] The value is greater than max -Fail [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 1 digit) -Fail [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 2 digits) -Fail [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 3 digits) +Pass [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 1 digit) +Pass [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 2 digits) +Pass [INPUT in DATETIME-LOCAL status] The value is greater than max(with millisecond in 3 digits) Pass [INPUT in DATETIME-LOCAL status] The value is greater than max(Year is 10000 should be valid) Pass [INPUT in DATE status] The max attribute is not set Pass [INPUT in DATE status] Value is empty string @@ -35,9 +34,9 @@ Pass [INPUT in TIME status] The value attribute is an invalid time string(second Pass [INPUT in TIME status] The max attribute is greater than value attribute Pass [INPUT in TIME status] The time missing second and minute parts is invalid Pass [INPUT in TIME status] The value attribute is greater than max attribute -Fail [INPUT in TIME status] The value is greater than max(with millisecond in 1 digit) -Fail [INPUT in TIME status] The value is greater than max(with millisecond in 2 digit) -Fail [INPUT in TIME status] The value is greater than max(with millisecond in 3 digit) +Pass [INPUT in TIME status] The value is greater than max(with millisecond in 1 digit) +Pass [INPUT in TIME status] The value is greater than max(with millisecond in 2 digit) +Pass [INPUT in TIME status] The value is greater than max(with millisecond in 3 digit) Pass [INPUT in TIME status] The time missing second part is valid Pass [INPUT in TIME status] The time is max for reversed range Pass [INPUT in TIME status] The time is outside the accepted range for reversed range diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.txt index b75209dfccf..d21e6aa3569 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-validity-rangeUnderflow.txt @@ -2,8 +2,7 @@ Harness status: OK Found 47 tests -41 Pass -6 Fail +47 Pass Pass [INPUT in DATETIME-LOCAL status] The min attribute is not set Pass [INPUT in DATETIME-LOCAL status] Value is empty string Pass [INPUT in DATETIME-LOCAL status] The min attribute is an invalid local date time string @@ -11,9 +10,9 @@ Pass [INPUT in DATETIME-LOCAL status] The min attribute is less than the value a Pass [INPUT in DATETIME-LOCAL status] The value is an invalid local date time string(hour is greater than 23) Pass [INPUT in DATETIME-LOCAL status] The value is an invalid local date time string(year is two digits) Pass [INPUT in DATETIME-LOCAL status] The value is less than min -Fail [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 1 digit) -Fail [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 2 digits) -Fail [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 3 digits) +Pass [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 1 digit) +Pass [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 2 digits) +Pass [INPUT in DATETIME-LOCAL status] The value is less than min(with millisecond in 3 digits) Pass [INPUT in DATETIME-LOCAL status] The value is less than min(Year is 10000 should be valid) Pass [INPUT in DATETIME-LOCAL status] The value is greater than max Pass [INPUT in DATE status] The min attribute is not set @@ -33,9 +32,9 @@ Pass [INPUT in TIME status] The value attribute is an invalid time string Pass [INPUT in TIME status] The min attribute is less than value attribute Pass [INPUT in TIME status] The time missing second and minute parts is invalid Pass [INPUT in TIME status] The value attribute is less than min attribute -Fail [INPUT in TIME status] The value is less than min(with millisecond in 1 digit) -Fail [INPUT in TIME status] The value is less than min(with millisecond in 2 digit) -Fail [INPUT in TIME status] The value is less than min(with millisecond in 3 digit) +Pass [INPUT in TIME status] The value is less than min(with millisecond in 1 digit) +Pass [INPUT in TIME status] The value is less than min(with millisecond in 2 digit) +Pass [INPUT in TIME status] The value is less than min(with millisecond in 3 digit) Pass [INPUT in TIME status] The time missing second part is valid Pass [INPUT in TIME status] The time is max for reversed range Pass [INPUT in TIME status] The time is outside the accepted range for reversed range diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.txt new file mode 100644 index 00000000000..f13b515438a --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.txt @@ -0,0 +1,17 @@ +Harness status: OK + +Found 12 tests + +12 Pass +Pass Expected valueAsNumber=946729801000 from 2000-01-01T12:30:01 +Pass Expected digits unchanged in round-trip of 2000-01-01T12:30:01 +Pass Expected valueAsNumber=946729800500 from 2000-01-01T12:30:00.5 +Pass Expected digits unchanged in round-trip of 2000-01-01T12:30:00.5 +Pass Expected valueAsNumber=946729800040 from 2000-01-01T12:30:00.04 +Pass Expected digits unchanged in round-trip of 2000-01-01T12:30:00.04 +Pass Expected valueAsNumber=946729800003 from 2000-01-01T12:30:00.003 +Pass Expected digits unchanged in round-trip of 2000-01-01T12:30:00.003 +Pass Expected valueAsNumber=45001000 from 12:30:01 +Pass Expected valueAsNumber=45000500 from 12:30:00.5 +Pass Expected valueAsNumber=45000040 from 12:30:00.04 +Pass Expected valueAsNumber=45000003 from 12:30:00.003 \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/HTML/HTMLInputElement-invalid-time-digits.html b/Tests/LibWeb/Text/input/HTML/HTMLInputElement-invalid-time-digits.html index f4e7cf19cc8..4160f0e378e 100644 --- a/Tests/LibWeb/Text/input/HTML/HTMLInputElement-invalid-time-digits.html +++ b/Tests/LibWeb/Text/input/HTML/HTMLInputElement-invalid-time-digits.html @@ -4,10 +4,10 @@ test(() => { const inputElement = document.createElement("input"); inputElement.type = "time"; - inputElement.value = "invalid"; - inputElement.value = "xx:34:56"; - inputElement.value = "12:xx:56"; - inputElement.value = "12:34:xx"; + ["invalid", "xx:34:56", "12:xx:56", "12:34:xx", "12:34:60"].forEach(value => { + inputElement.value = value; + println(`Parsed ${value} as ${inputElement.valueAsNumber}`); + }); println("PASS (didn't crash)"); }); diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.html new file mode 100644 index 00000000000..08beb222f78 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/the-input-element/input-seconds-leading-zeroes.html @@ -0,0 +1,52 @@ + + + + + HTMLInputElement leading zeroes in seconds/millis + + + + +

input times and datetimes with leading zeroes in seconds/millis

+ +
+
+ + + + +