mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-31 05:09:12 +00:00
LibWeb/HTML: Implement valueAsNumber for 'date' input type
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, 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
Some checks are pending
CI / Lagom (arm64, Sanitizer_CI, false, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (x86_64, Fuzzers_CI, false, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, false, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (x86_64, Sanitizer_CI, true, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, 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 commit is contained in:
parent
12a07b4fad
commit
9585c6c0c7
Notes:
github-actions[bot]
2025-02-26 10:50:33 +00:00
Author: https://github.com/shannonbooth
Commit: 9585c6c0c7
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3701
5 changed files with 108 additions and 29 deletions
|
@ -129,22 +129,6 @@ bool is_valid_date_string(StringView value)
|
||||||
return day >= 1 && day <= AK::days_in_month(year, month);
|
return day >= 1 && day <= AK::days_in_month(year, month);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-date-string
|
|
||||||
WebIDL::ExceptionOr<GC::Ref<JS::Date>> parse_date_string(JS::Realm& realm, StringView value)
|
|
||||||
{
|
|
||||||
// FIXME: Implement spec compliant date string parsing
|
|
||||||
auto parts = value.split_view('-', SplitBehavior::KeepEmpty);
|
|
||||||
if (parts.size() >= 3) {
|
|
||||||
if (auto year = parts.at(0).to_number<u32>(); year.has_value()) {
|
|
||||||
if (auto month = parts.at(1).to_number<u32>(); month.has_value()) {
|
|
||||||
if (auto day_of_month = parts.at(2).to_number<u32>(); day_of_month.has_value())
|
|
||||||
return JS::Date::create(realm, JS::make_date(JS::make_day(*year, *month - 1, *day_of_month), 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Can't parse date string"sv };
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-local-date-and-time-string
|
// 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(StringView value)
|
||||||
{
|
{
|
||||||
|
@ -363,4 +347,58 @@ Optional<WeekYearAndWeek> parse_a_week_string(StringView input_view)
|
||||||
return WeekYearAndWeek { year, week };
|
return WeekYearAndWeek { year, week };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-date-component
|
||||||
|
static Optional<YearMonthDay> parse_a_date_component(GenericLexer& input)
|
||||||
|
{
|
||||||
|
// 1. Parse a month component to obtain year and month. If this returns nothing, then fail.
|
||||||
|
auto maybe_month_component = parse_a_month_component(input);
|
||||||
|
if (!maybe_month_component.has_value())
|
||||||
|
return {};
|
||||||
|
auto month_component = maybe_month_component.value();
|
||||||
|
|
||||||
|
// 2. Let maxday be the number of days in month month of year year.
|
||||||
|
u32 maxday = AK::days_in_month(month_component.year, month_component.month);
|
||||||
|
|
||||||
|
// 3. If position is beyond the end of input or if the character at position is not a U+002D HYPHEN-MINUS character, then fail.
|
||||||
|
// Otherwise, move position forwards one character.
|
||||||
|
if (!input.consume_specific('-'))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// 4. Collect a sequence of code points that are ASCII digits from input given position. If the collected sequence is not
|
||||||
|
// exactly two characters long, then fail. Otherwise, interpret the resulting sequence as a base-ten integer. Let that
|
||||||
|
// number be the day.
|
||||||
|
auto day_string = input.consume_while(is_ascii_digit);
|
||||||
|
if (day_string.length() != 2)
|
||||||
|
return {};
|
||||||
|
auto day = day_string.to_number<u32>().value();
|
||||||
|
|
||||||
|
// 5. If day is not a number in the range 1 ≤ day ≤ maxday, then fail.
|
||||||
|
if (day < 1 || day > maxday)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// 6. Return year, month, and day.
|
||||||
|
return YearMonthDay { month_component.year, month_component.month, day };
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-date-string
|
||||||
|
Optional<YearMonthDay> parse_a_date_string(StringView input_view)
|
||||||
|
{
|
||||||
|
// 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 { input_view };
|
||||||
|
|
||||||
|
// 3. Parse a date component to obtain year, month, and day. If this returns nothing, then fail.
|
||||||
|
auto year_month_day = parse_a_date_component(input);
|
||||||
|
if (!year_month_day.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// 4. If position is not beyond the end of input, then fail.
|
||||||
|
if (!input.is_eof())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// 5. Let date be the date with year year, month month, and day day.
|
||||||
|
// 6. Return date.
|
||||||
|
return year_month_day.release_value();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ u32 week_number_of_the_last_day(u64 year);
|
||||||
bool is_valid_week_string(StringView value);
|
bool is_valid_week_string(StringView value);
|
||||||
bool is_valid_month_string(StringView value);
|
bool is_valid_month_string(StringView value);
|
||||||
bool is_valid_date_string(StringView value);
|
bool is_valid_date_string(StringView value);
|
||||||
WebIDL::ExceptionOr<GC::Ref<JS::Date>> parse_date_string(JS::Realm& realm, StringView value);
|
|
||||||
bool is_valid_local_date_and_time_string(StringView value);
|
bool is_valid_local_date_and_time_string(StringView value);
|
||||||
String normalize_local_date_and_time_string(String const& value);
|
String normalize_local_date_and_time_string(String const& value);
|
||||||
bool is_valid_time_string(StringView value);
|
bool is_valid_time_string(StringView value);
|
||||||
|
@ -35,6 +34,14 @@ struct WeekYearAndWeek {
|
||||||
};
|
};
|
||||||
Optional<WeekYearAndWeek> parse_a_week_string(StringView);
|
Optional<WeekYearAndWeek> parse_a_week_string(StringView);
|
||||||
|
|
||||||
|
struct YearMonthDay {
|
||||||
|
u32 year;
|
||||||
|
u32 month;
|
||||||
|
u32 day;
|
||||||
|
};
|
||||||
|
|
||||||
|
Optional<YearMonthDay> parse_a_date_string(StringView);
|
||||||
|
|
||||||
i32 number_of_months_since_unix_epoch(YearAndMonth);
|
i32 number_of_months_since_unix_epoch(YearAndMonth);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <LibCore/DateTime.h>
|
||||||
#include <LibJS/Runtime/Date.h>
|
#include <LibJS/Runtime/Date.h>
|
||||||
#include <LibJS/Runtime/NativeFunction.h>
|
#include <LibJS/Runtime/NativeFunction.h>
|
||||||
#include <LibWeb/Bindings/HTMLInputElementPrototype.h>
|
#include <LibWeb/Bindings/HTMLInputElementPrototype.h>
|
||||||
|
@ -2094,6 +2095,22 @@ static Optional<double> convert_week_string_to_number(StringView input)
|
||||||
return UnixDateTime::from_iso8601_week(parsed_week->week_year, parsed_week->week).milliseconds_since_epoch();
|
return UnixDateTime::from_iso8601_week(parsed_week->week_year, parsed_week->week).milliseconds_since_epoch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):concept-input-value-number-string
|
||||||
|
static Optional<double> convert_date_string_to_number(StringView input)
|
||||||
|
{
|
||||||
|
// The algorithm to convert a string to a number, given a string input, is as follows: If parsing a date
|
||||||
|
// from input results in an error, then return an error; otherwise, return the number of milliseconds
|
||||||
|
// elapsed from midnight UTC on the morning of 1970-01-01 (the time represented by the value
|
||||||
|
// "1970-01-01T00:00:00.0Z") to midnight UTC on the morning of the parsed date, ignoring leap seconds.
|
||||||
|
auto maybe_date = parse_a_date_string(input);
|
||||||
|
if (!maybe_date.has_value())
|
||||||
|
return {};
|
||||||
|
auto date = maybe_date.value();
|
||||||
|
|
||||||
|
auto date_time = UnixDateTime::from_unix_time_parts(date.year, date.month, date.day, 0, 0, 0, 0);
|
||||||
|
return date_time.milliseconds_since_epoch();
|
||||||
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/input.html#concept-input-value-string-number
|
// https://html.spec.whatwg.org/multipage/input.html#concept-input-value-string-number
|
||||||
Optional<double> HTMLInputElement::convert_string_to_number(StringView input) const
|
Optional<double> HTMLInputElement::convert_string_to_number(StringView input) const
|
||||||
{
|
{
|
||||||
|
@ -2111,6 +2128,9 @@ Optional<double> HTMLInputElement::convert_string_to_number(StringView input) co
|
||||||
if (type_state() == TypeAttributeState::Week)
|
if (type_state() == TypeAttributeState::Week)
|
||||||
return convert_week_string_to_number(input);
|
return convert_week_string_to_number(input);
|
||||||
|
|
||||||
|
if (type_state() == TypeAttributeState::Date)
|
||||||
|
return convert_date_string_to_number(input);
|
||||||
|
|
||||||
dbgln("HTMLInputElement::convert_string_to_number() not implemented for input type {}", type());
|
dbgln("HTMLInputElement::convert_string_to_number() not implemented for input type {}", type());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -2156,6 +2176,16 @@ static String convert_number_to_week_string(double input)
|
||||||
return MUST(String::formatted("{:04d}-W{:02d}", year, week));
|
return MUST(String::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)
|
||||||
|
{
|
||||||
|
// 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 = Core::DateTime::from_timestamp(input / 1000.);
|
||||||
|
return MUST(date.to_string("%Y-%m-%d"sv, Core::DateTime::LocalTime::No));
|
||||||
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/input.html#concept-input-value-string-number
|
// https://html.spec.whatwg.org/multipage/input.html#concept-input-value-string-number
|
||||||
String HTMLInputElement::convert_number_to_string(double input) const
|
String HTMLInputElement::convert_number_to_string(double input) const
|
||||||
{
|
{
|
||||||
|
@ -2173,6 +2203,9 @@ String HTMLInputElement::convert_number_to_string(double input) const
|
||||||
if (type_state() == TypeAttributeState::Week)
|
if (type_state() == TypeAttributeState::Week)
|
||||||
return convert_number_to_week_string(input);
|
return convert_number_to_week_string(input);
|
||||||
|
|
||||||
|
if (type_state() == TypeAttributeState::Date)
|
||||||
|
return convert_number_to_date_string(input);
|
||||||
|
|
||||||
dbgln("HTMLInputElement::convert_number_to_string() not implemented for input type {}", type());
|
dbgln("HTMLInputElement::convert_number_to_string() not implemented for input type {}", type());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -2183,12 +2216,13 @@ WebIDL::ExceptionOr<GC::Ptr<JS::Date>> HTMLInputElement::convert_string_to_date(
|
||||||
// https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):concept-input-value-string-date
|
// https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):concept-input-value-string-date
|
||||||
if (type_state() == TypeAttributeState::Date) {
|
if (type_state() == TypeAttributeState::Date) {
|
||||||
// If parsing a date from input results in an error, then return an error;
|
// If parsing a date from input results in an error, then return an error;
|
||||||
auto maybe_date = parse_date_string(realm(), input);
|
auto maybe_date = parse_a_date_string(input);
|
||||||
if (maybe_date.is_exception())
|
if (!maybe_date.has_value())
|
||||||
return maybe_date.exception();
|
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Can't parse date string"sv };
|
||||||
|
auto date = maybe_date.value();
|
||||||
|
|
||||||
// otherwise, return a new Date object representing midnight UTC on the morning of the parsed date.
|
// otherwise, return a new Date object representing midnight UTC on the morning of the parsed date.
|
||||||
return maybe_date.value();
|
return JS::Date::create(realm(), JS::make_date(JS::make_day(date.year, date.month - 1, date.day), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):concept-input-value-string-date
|
// https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):concept-input-value-string-date
|
||||||
|
|
|
@ -31,7 +31,7 @@ tel threw exception: InvalidStateError: valueAsNumber: Invalid input type used
|
||||||
url threw exception: InvalidStateError: valueAsNumber: Invalid input type used
|
url threw exception: InvalidStateError: valueAsNumber: Invalid input type used
|
||||||
email threw exception: InvalidStateError: valueAsNumber: Invalid input type used
|
email threw exception: InvalidStateError: valueAsNumber: Invalid input type used
|
||||||
password threw exception: InvalidStateError: valueAsNumber: Invalid input type used
|
password threw exception: InvalidStateError: valueAsNumber: Invalid input type used
|
||||||
date did not throw: NaN
|
date did not throw: 0
|
||||||
month did not throw: 100
|
month did not throw: 100
|
||||||
week did not throw: 345600000
|
week did not throw: 345600000
|
||||||
time did not throw: NaN
|
time did not throw: NaN
|
||||||
|
|
|
@ -2,19 +2,19 @@ Harness status: OK
|
||||||
|
|
||||||
Found 60 tests
|
Found 60 tests
|
||||||
|
|
||||||
43 Pass
|
48 Pass
|
||||||
17 Fail
|
12 Fail
|
||||||
Pass valueAsNumber getter on type date (actual value: , expected valueAsNumber: NaN)
|
Pass valueAsNumber getter on type date (actual value: , expected valueAsNumber: NaN)
|
||||||
Pass valueAsNumber getter on type date (actual value: 0000-12-10, expected valueAsNumber: NaN)
|
Pass valueAsNumber getter on type date (actual value: 0000-12-10, expected valueAsNumber: NaN)
|
||||||
Pass valueAsNumber getter on type date (actual value: 2019-00-12, expected valueAsNumber: NaN)
|
Pass valueAsNumber getter on type date (actual value: 2019-00-12, expected valueAsNumber: NaN)
|
||||||
Pass valueAsNumber getter on type date (actual value: 2019-12-00, expected valueAsNumber: NaN)
|
Pass valueAsNumber getter on type date (actual value: 2019-12-00, expected valueAsNumber: NaN)
|
||||||
Pass valueAsNumber getter on type date (actual value: 2019-13-10, expected valueAsNumber: NaN)
|
Pass valueAsNumber getter on type date (actual value: 2019-13-10, expected valueAsNumber: NaN)
|
||||||
Pass valueAsNumber getter on type date (actual value: 2019-02-29, expected valueAsNumber: NaN)
|
Pass valueAsNumber getter on type date (actual value: 2019-02-29, expected valueAsNumber: NaN)
|
||||||
Fail valueAsNumber getter on type date (actual value: 2019-12-10, expected valueAsNumber: 1575936000000)
|
Pass valueAsNumber getter on type date (actual value: 2019-12-10, expected valueAsNumber: 1575936000000)
|
||||||
Fail valueAsNumber getter on type date (actual value: 2016-02-29, expected valueAsNumber: 1456704000000)
|
Pass valueAsNumber getter on type date (actual value: 2016-02-29, expected valueAsNumber: 1456704000000)
|
||||||
Fail valueAsNumber setter on type date (actual valueAsNumber: 0, expected value: 1970-01-01)
|
Pass valueAsNumber setter on type date (actual valueAsNumber: 0, expected value: 1970-01-01)
|
||||||
Fail valueAsNumber setter on type date (actual valueAsNumber: 1575936000000, expected value: 2019-12-10)
|
Pass valueAsNumber setter on type date (actual valueAsNumber: 1575936000000, expected value: 2019-12-10)
|
||||||
Fail valueAsNumber setter on type date (actual valueAsNumber: 1456704000000, expected value: 2016-02-29)
|
Pass valueAsNumber setter on type date (actual valueAsNumber: 1456704000000, expected value: 2016-02-29)
|
||||||
Pass valueAsNumber getter on type month (actual value: , expected valueAsNumber: NaN)
|
Pass valueAsNumber getter on type month (actual value: , expected valueAsNumber: NaN)
|
||||||
Pass valueAsNumber getter on type month (actual value: 0000-12, expected valueAsNumber: NaN)
|
Pass valueAsNumber getter on type month (actual value: 0000-12, expected valueAsNumber: NaN)
|
||||||
Pass valueAsNumber getter on type month (actual value: 2019-00, expected valueAsNumber: NaN)
|
Pass valueAsNumber getter on type month (actual value: 2019-00, expected valueAsNumber: NaN)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue