diff --git a/Libraries/LibWeb/HTML/FormAssociatedElement.cpp b/Libraries/LibWeb/HTML/FormAssociatedElement.cpp index 29b9e3c0eae..4803015e4b8 100644 --- a/Libraries/LibWeb/HTML/FormAssociatedElement.cpp +++ b/Libraries/LibWeb/HTML/FormAssociatedElement.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -248,6 +249,36 @@ bool FormAssociatedElement::check_validity_steps() return true; } +// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#report-validity-steps +bool FormAssociatedElement::report_validity_steps() +{ + // 1. If element is a candidate for constraint validation and does not satisfy its constraints, then: + if (is_candidate_for_constraint_validation() && !satisfies_its_constraints()) { + auto& element = form_associated_element_to_html_element(); + // 1. Let report be the result of firing an event named invalid at element, with the cancelable attribute initialized to true. + auto report = element.dispatch_event(DOM::Event::create(element.realm(), EventNames::invalid, { .cancelable = true })); + + // 2. If report is true, then report the problems with the constraints of this element to the user. When reporting the problem with the constraints to the user, + // the user agent may run the focusing steps for element, and may change the scrolling position of the document, or perform some other action that brings + // element to the user's attention. User agents may report more than one constraint violation, if element suffers from multiple problems at once. + // FIXME: Does this align with other browsers? + if (report && element.check_visibility({})) { + run_focusing_steps(&element); + DOM::ScrollIntoViewOptions scroll_options; + scroll_options.block = Bindings::ScrollLogicalPosition::Nearest; + scroll_options.inline_ = Bindings::ScrollLogicalPosition::Nearest; + scroll_options.behavior = Bindings::ScrollBehavior::Instant; + (void)element.scroll_into_view(scroll_options); + } + + // 3. Return false. + return false; + } + + // 2. Return true. + return true; +} + // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#candidate-for-constraint-validation bool FormAssociatedElement::is_candidate_for_constraint_validation() const { diff --git a/Libraries/LibWeb/HTML/FormAssociatedElement.h b/Libraries/LibWeb/HTML/FormAssociatedElement.h index ecba6f963a5..250a8ddf4cd 100644 --- a/Libraries/LibWeb/HTML/FormAssociatedElement.h +++ b/Libraries/LibWeb/HTML/FormAssociatedElement.h @@ -101,6 +101,9 @@ public: // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#check-validity-steps bool check_validity_steps(); + // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#report-validity-steps + bool report_validity_steps(); + // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#candidate-for-constraint-validation bool is_candidate_for_constraint_validation() const; diff --git a/Libraries/LibWeb/HTML/HTMLFormElement.cpp b/Libraries/LibWeb/HTML/HTMLFormElement.cpp index 373e1c5c4fb..5db36cd14a5 100644 --- a/Libraries/LibWeb/HTML/HTMLFormElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLFormElement.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -599,6 +600,41 @@ HTMLFormElement::StaticValidationResult HTMLFormElement::statically_validate_con return { false, unhandled_invalid_controls }; } +// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#interactively-validate-the-constraints +bool HTMLFormElement::interactively_validate_constraints() +{ + // 1. Statically validate the constraints of form, and let unhandled invalid controls be the list of elements returned if the result was negative. + // 2. If the result was positive, then return that result. + auto static_validation_result = statically_validate_constraints(); + if (static_validation_result.result) + return true; + auto unhandled_invalid_controls = static_validation_result.unhandled_invalid_controls; + + // 3. Report the problems with the constraints of at least one of the elements given in unhandled invalid controls to the user. + // - User agents may focus one of those elements in the process, by running the focusing steps for that element, and may change the + // scrolling position of the document, or perform some other action that brings the element to the user's attention. + // For elements that are form-associated custom elements, user agents should use their validation anchor instead, for the purposes of these actions. + // - User agents may report more than one constraint violation. + // - User agents may coalesce related constraint violation reports if appropriate (e.g. if multiple radio buttons in a group are marked as required, only one error need be reported). + // - If one of the controls is not being rendered (e.g. it has the hidden attribute set) then user agents may report a script error. + // FIXME: Does this align with other browsers? + auto first_invalid_control = unhandled_invalid_controls.first_matching([](auto control) { + return control->check_visibility({}); + }); + if (first_invalid_control.has_value()) { + auto control = first_invalid_control.release_value(); + run_focusing_steps(control); + DOM::ScrollIntoViewOptions scroll_options; + scroll_options.block = Bindings::ScrollLogicalPosition::Nearest; + scroll_options.inline_ = Bindings::ScrollLogicalPosition::Nearest; + scroll_options.behavior = Bindings::ScrollBehavior::Instant; + (void)control->scroll_into_view(scroll_options); + } + + // 4. Return a negative result. + return false; +} + // https://html.spec.whatwg.org/multipage/forms.html#dom-form-checkvalidity WebIDL::ExceptionOr HTMLFormElement::check_validity() { @@ -608,8 +644,7 @@ WebIDL::ExceptionOr HTMLFormElement::check_validity() // https://html.spec.whatwg.org/multipage/forms.html#dom-form-reportvalidity WebIDL::ExceptionOr HTMLFormElement::report_validity() { - dbgln("(STUBBED) HTMLFormElement::report_validity(). Called on: {}", debug_description()); - return true; + return interactively_validate_constraints(); } // https://html.spec.whatwg.org/multipage/forms.html#category-submit diff --git a/Libraries/LibWeb/HTML/HTMLFormElement.h b/Libraries/LibWeb/HTML/HTMLFormElement.h index 1663b86666f..5861ba44811 100644 --- a/Libraries/LibWeb/HTML/HTMLFormElement.h +++ b/Libraries/LibWeb/HTML/HTMLFormElement.h @@ -84,6 +84,7 @@ public: }; StaticValidationResult statically_validate_constraints(); + bool interactively_validate_constraints(); WebIDL::ExceptionOr check_validity(); WebIDL::ExceptionOr report_validity(); diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 48359ac59d2..81824030cb0 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -2785,8 +2785,7 @@ WebIDL::ExceptionOr HTMLInputElement::check_validity() // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-reportvalidity WebIDL::ExceptionOr HTMLInputElement::report_validity() { - dbgln("(STUBBED) HTMLInputElement::report_validity(). Called on: {}", debug_description()); - return true; + return report_validity_steps(); } Optional HTMLInputElement::default_role() const diff --git a/Libraries/LibWeb/HTML/HTMLSelectElement.cpp b/Libraries/LibWeb/HTML/HTMLSelectElement.cpp index e32b3536f13..bb814b004a6 100644 --- a/Libraries/LibWeb/HTML/HTMLSelectElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLSelectElement.cpp @@ -711,6 +711,12 @@ bool HTMLSelectElement::check_validity() return check_validity_steps(); } +// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-reportvalidity +bool HTMLSelectElement::report_validity() +{ + return report_validity_steps(); +} + bool HTMLSelectElement::is_focusable() const { return enabled(); diff --git a/Libraries/LibWeb/HTML/HTMLSelectElement.h b/Libraries/LibWeb/HTML/HTMLSelectElement.h index dede0996a6f..f43abea356a 100644 --- a/Libraries/LibWeb/HTML/HTMLSelectElement.h +++ b/Libraries/LibWeb/HTML/HTMLSelectElement.h @@ -63,6 +63,7 @@ public: bool will_validate(); bool check_validity(); + bool report_validity(); // ^EventTarget // https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-select-element diff --git a/Libraries/LibWeb/HTML/HTMLSelectElement.idl b/Libraries/LibWeb/HTML/HTMLSelectElement.idl index 99655e3793b..cc53df48bc5 100644 --- a/Libraries/LibWeb/HTML/HTMLSelectElement.idl +++ b/Libraries/LibWeb/HTML/HTMLSelectElement.idl @@ -35,7 +35,7 @@ interface HTMLSelectElement : HTMLElement { readonly attribute ValidityState validity; [FIXME] readonly attribute DOMString validationMessage; boolean checkValidity(); - [FIXME] boolean reportValidity(); + boolean reportValidity(); undefined setCustomValidity(DOMString error); undefined showPicker(); diff --git a/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp index f079a07b474..79da9a61c73 100644 --- a/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp @@ -248,8 +248,7 @@ bool HTMLTextAreaElement::check_validity() // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-reportvalidity bool HTMLTextAreaElement::report_validity() { - dbgln("(STUBBED) HTMLTextAreaElement::report_validity(). Called on: {}", debug_description()); - return true; + return report_validity_steps(); } // https://html.spec.whatwg.org/multipage/form-elements.html#dom-textarea-maxlength diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-reportValidity.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-reportValidity.txt new file mode 100644 index 00000000000..ac689131426 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/forms/constraints/form-validation-reportValidity.txt @@ -0,0 +1,135 @@ +Harness status: OK + +Found 130 tests + +130 Pass +Pass [INPUT in TEXT status] no constraint +Pass [INPUT in TEXT status] no constraint (in a form) +Pass [INPUT in TEXT status] not suffering from being too long +Pass [INPUT in TEXT status] not suffering from being too long (in a form) +Pass [INPUT in TEXT status] suffering from a pattern mismatch +Pass [INPUT in TEXT status] suffering from a pattern mismatch (in a form) +Pass [INPUT in TEXT status] suffering from being missing +Pass [INPUT in TEXT status] suffering from being missing (in a form) +Pass [INPUT in SEARCH status] no constraint +Pass [INPUT in SEARCH status] no constraint (in a form) +Pass [INPUT in SEARCH status] not suffering from being too long +Pass [INPUT in SEARCH status] not suffering from being too long (in a form) +Pass [INPUT in SEARCH status] suffering from a pattern mismatch +Pass [INPUT in SEARCH status] suffering from a pattern mismatch (in a form) +Pass [INPUT in SEARCH status] suffering from being missing +Pass [INPUT in SEARCH status] suffering from being missing (in a form) +Pass [INPUT in TEL status] no constraint +Pass [INPUT in TEL status] no constraint (in a form) +Pass [INPUT in TEL status] not suffering from being too long +Pass [INPUT in TEL status] not suffering from being too long (in a form) +Pass [INPUT in TEL status] suffering from a pattern mismatch +Pass [INPUT in TEL status] suffering from a pattern mismatch (in a form) +Pass [INPUT in TEL status] suffering from being missing +Pass [INPUT in TEL status] suffering from being missing (in a form) +Pass [INPUT in PASSWORD status] no constraint +Pass [INPUT in PASSWORD status] no constraint (in a form) +Pass [INPUT in PASSWORD status] not suffering from being too long +Pass [INPUT in PASSWORD status] not suffering from being too long (in a form) +Pass [INPUT in PASSWORD status] suffering from a pattern mismatch +Pass [INPUT in PASSWORD status] suffering from a pattern mismatch (in a form) +Pass [INPUT in PASSWORD status] suffering from being missing +Pass [INPUT in PASSWORD status] suffering from being missing (in a form) +Pass [INPUT in URL status] no constraint +Pass [INPUT in URL status] no constraint (in a form) +Pass [INPUT in URL status] not suffering from being too long +Pass [INPUT in URL status] not suffering from being too long (in a form) +Pass [INPUT in URL status] suffering from a pattern mismatch +Pass [INPUT in URL status] suffering from a pattern mismatch (in a form) +Pass [INPUT in URL status] suffering from a type mismatch +Pass [INPUT in URL status] suffering from a type mismatch (in a form) +Pass [INPUT in URL status] suffering from being missing +Pass [INPUT in URL status] suffering from being missing (in a form) +Pass [INPUT in EMAIL status] no constraint +Pass [INPUT in EMAIL status] no constraint (in a form) +Pass [INPUT in EMAIL status] not suffering from being too long +Pass [INPUT in EMAIL status] not suffering from being too long (in a form) +Pass [INPUT in EMAIL status] suffering from a pattern mismatch +Pass [INPUT in EMAIL status] suffering from a pattern mismatch (in a form) +Pass [INPUT in EMAIL status] suffering from a type mismatch +Pass [INPUT in EMAIL status] suffering from a type mismatch (in a form) +Pass [INPUT in EMAIL status] suffering from being missing +Pass [INPUT in EMAIL status] suffering from being missing (in a form) +Pass [INPUT in DATETIME-LOCAL status] no constraint +Pass [INPUT in DATETIME-LOCAL status] no constraint (in a form) +Pass [INPUT in DATETIME-LOCAL status] suffering from an overflow +Pass [INPUT in DATETIME-LOCAL status] suffering from an overflow (in a form) +Pass [INPUT in DATETIME-LOCAL status] suffering from an underflow +Pass [INPUT in DATETIME-LOCAL status] suffering from an underflow (in a form) +Pass [INPUT in DATETIME-LOCAL status] suffering from a step mismatch +Pass [INPUT in DATETIME-LOCAL status] suffering from a step mismatch (in a form) +Pass [INPUT in DATETIME-LOCAL status] suffering from being missing +Pass [INPUT in DATETIME-LOCAL status] suffering from being missing (in a form) +Pass [INPUT in DATE status] no constraint +Pass [INPUT in DATE status] no constraint (in a form) +Pass [INPUT in DATE status] suffering from an overflow +Pass [INPUT in DATE status] suffering from an overflow (in a form) +Pass [INPUT in DATE status] suffering from an underflow +Pass [INPUT in DATE status] suffering from an underflow (in a form) +Pass [INPUT in DATE status] suffering from a step mismatch +Pass [INPUT in DATE status] suffering from a step mismatch (in a form) +Pass [INPUT in DATE status] suffering from being missing +Pass [INPUT in DATE status] suffering from being missing (in a form) +Pass [INPUT in MONTH status] no constraint +Pass [INPUT in MONTH status] no constraint (in a form) +Pass [INPUT in MONTH status] suffering from an overflow +Pass [INPUT in MONTH status] suffering from an overflow (in a form) +Pass [INPUT in MONTH status] suffering from an underflow +Pass [INPUT in MONTH status] suffering from an underflow (in a form) +Pass [INPUT in MONTH status] suffering from a step mismatch +Pass [INPUT in MONTH status] suffering from a step mismatch (in a form) +Pass [INPUT in MONTH status] suffering from being missing +Pass [INPUT in MONTH status] suffering from being missing (in a form) +Pass [INPUT in WEEK status] no constraint +Pass [INPUT in WEEK status] no constraint (in a form) +Pass [INPUT in WEEK status] suffering from an overflow +Pass [INPUT in WEEK status] suffering from an overflow (in a form) +Pass [INPUT in WEEK status] suffering from an underflow +Pass [INPUT in WEEK status] suffering from an underflow (in a form) +Pass [INPUT in WEEK status] suffering from a step mismatch +Pass [INPUT in WEEK status] suffering from a step mismatch (in a form) +Pass [INPUT in WEEK status] suffering from being missing +Pass [INPUT in WEEK status] suffering from being missing (in a form) +Pass [INPUT in TIME status] no constraint +Pass [INPUT in TIME status] no constraint (in a form) +Pass [INPUT in TIME status] suffering from an overflow +Pass [INPUT in TIME status] suffering from an overflow (in a form) +Pass [INPUT in TIME status] suffering from an underflow +Pass [INPUT in TIME status] suffering from an underflow (in a form) +Pass [INPUT in TIME status] suffering from a step mismatch +Pass [INPUT in TIME status] suffering from a step mismatch (in a form) +Pass [INPUT in TIME status] suffering from being missing +Pass [INPUT in TIME status] suffering from being missing (in a form) +Pass [INPUT in NUMBER status] suffering from an overflow +Pass [INPUT in NUMBER status] suffering from an overflow (in a form) +Pass [INPUT in NUMBER status] suffering from an underflow +Pass [INPUT in NUMBER status] suffering from an underflow (in a form) +Pass [INPUT in NUMBER status] suffering from a step mismatch +Pass [INPUT in NUMBER status] suffering from a step mismatch (in a form) +Pass [INPUT in NUMBER status] suffering from being missing +Pass [INPUT in NUMBER status] suffering from being missing (in a form) +Pass [INPUT in CHECKBOX status] no constraint +Pass [INPUT in CHECKBOX status] no constraint (in a form) +Pass [INPUT in CHECKBOX status] suffering from being missing +Pass [INPUT in CHECKBOX status] suffering from being missing (in a form) +Pass [INPUT in RADIO status] no constraint +Pass [INPUT in RADIO status] no constraint (in a form) +Pass [INPUT in RADIO status] suffering from being missing +Pass [INPUT in RADIO status] suffering from being missing (in a form) +Pass [INPUT in FILE status] no constraint +Pass [INPUT in FILE status] no constraint (in a form) +Pass [INPUT in FILE status] suffering from being missing +Pass [INPUT in FILE status] suffering from being missing (in a form) +Pass [select] no constraint +Pass [select] no constraint (in a form) +Pass [select] suffering from being missing +Pass [select] suffering from being missing (in a form) +Pass [textarea] no constraint +Pass [textarea] no constraint (in a form) +Pass [textarea] suffering from being missing +Pass [textarea] suffering from being missing (in a form) \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-reportValidity.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-reportValidity.html new file mode 100644 index 00000000000..d70588ad19f --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/forms/constraints/form-validation-reportValidity.html @@ -0,0 +1,146 @@ + + + +The constraint validation API Test: element.reportValidity() + + + + + + +
+