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()
+
+
+
+
+
+
+
+