1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 22:48:11 +00:00

LibWeb: Add input element valueAsDate property

This commit is contained in:
Bastiaan van der Plaat 2023-12-15 18:23:29 +01:00 committed by Andrew Kaster
parent be511fdcf7
commit cf69fd0a09
10 changed files with 314 additions and 9 deletions

View file

@ -128,6 +128,22 @@ bool is_valid_date_string(StringView value)
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<JS::NonnullGCPtr<JS::Date>> parse_date_string(JS::Realm& realm, StringView value)
{
// FIXME: Implement spec compliant date string parsing
auto parts = value.split_view('-');
if (parts.size() >= 3) {
if (auto year = parts.at(0).to_uint(); year.has_value()) {
if (auto month = parts.at(1).to_uint(); month.has_value()) {
if (auto day_of_month = parts.at(2).to_uint(); 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
bool is_valid_local_date_and_time_string(StringView value)
{
@ -197,4 +213,23 @@ bool is_valid_time_string(StringView value)
return true;
}
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#parse-a-time-string
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Date>> parse_time_string(JS::Realm& realm, StringView value)
{
// FIXME: Implement spec compliant time string parsing
auto parts = value.split_view(':');
if (parts.size() >= 2) {
if (auto hours = parts.at(0).to_uint(); hours.has_value()) {
if (auto minutes = parts.at(1).to_uint(); minutes.has_value()) {
if (parts.size() >= 3) {
if (auto seconds = parts.at(2).to_uint(); 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 };
}
}

View file

@ -8,6 +8,8 @@
#include <AK/Forward.h>
#include <AK/String.h>
#include <LibJS/Runtime/Date.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::HTML {
@ -15,8 +17,10 @@ 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);
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Date>> parse_date_string(JS::Realm& realm, 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);
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Date>> parse_time_string(JS::Realm& realm, StringView value);
}

View file

@ -8,6 +8,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
#include <LibWeb/CSS/StyleValues/IdentifierStyleValue.h>
@ -1117,6 +1118,63 @@ String HTMLInputElement::covert_number_to_string(double input) const
return {};
}
// https://html.spec.whatwg.org/multipage/input.html#concept-input-value-string-date
WebIDL::ExceptionOr<JS::GCPtr<JS::Date>> HTMLInputElement::convert_string_to_date(StringView input) const
{
// https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):concept-input-value-string-date
if (type_state() == TypeAttributeState::Date) {
// If parsing a date from input results in an error, then return an error;
auto maybe_date = parse_date_string(realm(), input);
if (maybe_date.is_exception())
return maybe_date.exception();
// otherwise, return a new Date object representing midnight UTC on the morning of the parsed date.
return maybe_date.value();
}
// https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):concept-input-value-string-date
if (type_state() == TypeAttributeState::Time) {
// If parsing a time from input results in an error, then return an error;
auto maybe_time = parse_time_string(realm(), input);
if (maybe_time.is_exception())
return maybe_time.exception();
// otherwise, return a new Date object representing the parsed time in UTC on 1970-01-01.
return maybe_time.value();
}
dbgln("HTMLInputElement::convert_string_to_date() not implemented for input type {}", type());
return nullptr;
}
// https://html.spec.whatwg.org/multipage/input.html#concept-input-value-date-string
String HTMLInputElement::covert_date_to_string(JS::NonnullGCPtr<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) {
// Return a valid date string that represents the date current at the time represented by input in the UTC time zone.
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string
return MUST(String::formatted("{:04d}-{:02d}-{:02d}", JS::year_from_time(input->date_value()), JS::month_from_time(input->date_value()) + 1, JS::date_from_time(input->date_value())));
}
// https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):concept-input-value-string-date
if (type_state() == TypeAttributeState::Time) {
// Return a valid time string that represents the UTC time component that is represented by input.
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-time-string
auto seconds = JS::sec_from_time(input->date_value());
auto milliseconds = JS::ms_from_time(input->date_value());
if (seconds > 0) {
if (milliseconds > 0)
return MUST(String::formatted("{:02d}:{:02d}:{:02d}.{:3d}", JS::hour_from_time(input->date_value()), JS::min_from_time(input->date_value()), seconds, milliseconds));
return MUST(String::formatted("{:02d}:{:02d}:{:02d}", JS::hour_from_time(input->date_value()), JS::min_from_time(input->date_value()), seconds));
}
return MUST(String::formatted("{:02d}:{:02d}", JS::hour_from_time(input->date_value()), JS::min_from_time(input->date_value())));
}
dbgln("HTMLInputElement::covert_date_to_string() not implemented for input type {}", type());
return {};
}
// https://html.spec.whatwg.org/multipage/input.html#attr-input-min
Optional<double> HTMLInputElement::min() const
{
@ -1234,8 +1292,50 @@ double HTMLInputElement::step_base() const
return 0;
}
// https://html.spec.whatwg.org/multipage/input.html#dom-input-valueasdate
JS::Object* HTMLInputElement::value_as_date() const
{
// On getting, if the valueAsDate attribute does not apply, as defined for the input element's type attribute's current state, then return null.
if (!value_as_date_applies())
return nullptr;
// Otherwise, run the algorithm to convert a string to a Date object defined for that state to the element's value;
// if the algorithm returned a Date object, then return it, otherwise, return null.
auto maybe_date = convert_string_to_date(value());
if (!maybe_date.is_exception())
return maybe_date.value().ptr();
return nullptr;
}
// https://html.spec.whatwg.org/multipage/input.html#dom-input-valueasdate
WebIDL::ExceptionOr<void> HTMLInputElement::set_value_as_date(Optional<JS::Handle<JS::Object>> const& value)
{
// On setting, if the valueAsDate attribute does not apply, as defined for the input element's type attribute's current state, then throw an "InvalidStateError" DOMException;
if (!value_as_date_applies())
return WebIDL::InvalidStateError::create(realm(), "valueAsDate: Invalid input type used"_fly_string);
// otherwise, if the new value is not null and not a Date object throw a TypeError exception;
if (value.has_value() && !is<JS::Date>(**value))
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "valueAsDate: input is not a Date"sv };
// 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 {}));
return {};
}
auto& date = static_cast<JS::Date&>(**value);
if (!isfinite(date.date_value())) {
TRY(set_value(String {}));
return {};
}
// otherwise, run the algorithm to convert a Date object to a string, as defined for that state, on the new value, and set the value of the element to the resulting string.
TRY(set_value(covert_date_to_string(date)));
return {};
}
// https://html.spec.whatwg.org/multipage/input.html#dom-input-valueasnumber
WebIDL::ExceptionOr<double> HTMLInputElement::value_as_number() const
double HTMLInputElement::value_as_number() const
{
// On getting, if the valueAsNumber attribute does not apply, as defined for the input element's type attribute's current state, then return a Not-a-Number (NaN) value.
if (!value_as_number_applies())
@ -1537,6 +1637,20 @@ bool HTMLInputElement::change_event_applies() const
}
}
// https://html.spec.whatwg.org/multipage/input.html#the-input-element:dom-input-valueasdate-3
bool HTMLInputElement::value_as_date_applies() const
{
switch (type_state()) {
case TypeAttributeState::Date:
case TypeAttributeState::Month:
case TypeAttributeState::Week:
case TypeAttributeState::Time:
return true;
default:
return false;
}
}
// https://html.spec.whatwg.org/multipage/input.html#the-input-element:dom-input-valueasnumber-3
bool HTMLInputElement::value_as_number_applies() const
{

View file

@ -102,7 +102,10 @@ public:
unsigned size() const;
WebIDL::ExceptionOr<void> set_size(unsigned value);
WebIDL::ExceptionOr<double> value_as_number() const;
JS::Object* value_as_date() const;
WebIDL::ExceptionOr<void> set_value_as_date(Optional<JS::Handle<JS::Object>> const&);
double value_as_number() const;
WebIDL::ExceptionOr<void> set_value_as_number(double value);
WebIDL::ExceptionOr<void> step_up(long n = 1);
@ -165,6 +168,7 @@ public:
bool has_input_activation_behavior() const;
bool change_event_applies() const;
bool value_as_date_applies() const;
bool value_as_number_applies() const;
bool step_applies() const;
bool step_up_or_down_applies() const;
@ -191,6 +195,9 @@ private:
Optional<double> convert_string_to_number(StringView input) const;
String covert_number_to_string(double input) const;
WebIDL::ExceptionOr<JS::GCPtr<JS::Date>> convert_string_to_date(StringView input) const;
String covert_date_to_string(JS::NonnullGCPtr<JS::Date> input) const;
Optional<double> min() const;
Optional<double> max() const;
double default_step() const;

View file

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