diff --git a/Libraries/LibWeb/HTML/FormAssociatedElement.cpp b/Libraries/LibWeb/HTML/FormAssociatedElement.cpp
index c2a3444bf2e..0887c8824e7 100644
--- a/Libraries/LibWeb/HTML/FormAssociatedElement.cpp
+++ b/Libraries/LibWeb/HTML/FormAssociatedElement.cpp
@@ -226,6 +226,20 @@ WebIDL::ExceptionOr FormAssociatedElement::set_form_action(String const& v
return html_element.set_attribute(HTML::AttributeNames::formaction, value);
}
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#check-validity-steps
+bool FormAssociatedElement::check_validity_steps()
+{
+ // 1. If element is a candidate for constraint validation and does not satisfy its constraints
+ if (is_candidate_for_constraint_validation() && !satisfies_its_constraints()) {
+ auto& element = form_associated_element_to_html_element();
+ // 1. Fire an event named invalid at element, with the cancelable attribute initialized to true
+ element.dispatch_event(DOM::Event::create(element.realm(), EventNames::invalid, { .cancelable = true }));
+ // 2. Return false.
+ return false;
+ }
+ 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 938e809a1e5..6536283174e 100644
--- a/Libraries/LibWeb/HTML/FormAssociatedElement.h
+++ b/Libraries/LibWeb/HTML/FormAssociatedElement.h
@@ -91,6 +91,9 @@ public:
// https://html.spec.whatwg.org/multipage/forms.html#concept-submit-button
virtual bool is_submit_button() const { return false; }
+ // 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#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 7d17382c896..4452a10107a 100644
--- a/Libraries/LibWeb/HTML/HTMLFormElement.cpp
+++ b/Libraries/LibWeb/HTML/HTMLFormElement.cpp
@@ -561,11 +561,48 @@ unsigned HTMLFormElement::length() const
return elements()->length();
}
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#statically-validate-the-constraints
+HTMLFormElement::StaticValidationResult HTMLFormElement::statically_validate_constraints()
+{
+ // 1. Let controls be a list of all the submittable elements whose form owner is form, in tree order.
+ auto controls = get_submittable_elements();
+ // 2. Let invalid controls be an initially empty list of elements.
+ GC::RootVector> invalid_controls(realm().heap());
+ // 3. For each element field in controls, in tree order:
+ for (auto& element : controls) {
+ auto& field = as(*element);
+ // 1. If field is not a candidate for constraint validation, then move on to the next element.
+ if (!field.is_candidate_for_constraint_validation())
+ continue;
+ // 2. Otherwise, if field satisfies its constraints, then move on to the next element.
+ if (field.satisfies_its_constraints())
+ continue;
+ // 3. Otherwise, add field to invalid controls.
+ invalid_controls.append(field.form_associated_element_to_html_element());
+ }
+ // 4. If invalid controls is empty, then return a positive result.
+ if (invalid_controls.is_empty())
+ return { true, invalid_controls };
+ // 5. Let unhandled invalid controls be an initially empty list of elements.
+ GC::RootVector> unhandled_invalid_controls(realm().heap());
+ // 6. For each element field in invalid controls, if any, in tree order:
+ for (auto& field : invalid_controls) {
+ // 1. Let notCanceled be the result of firing an event named invalid at field, with the cancelable attribute
+ // initialized to true.
+ auto not_canceled = field->dispatch_event(DOM::Event::create(this->realm(),
+ EventNames::invalid, { .cancelable = true }));
+ // 2. If notCanceled is true, then add field to unhandled invalid controls.
+ if (not_canceled)
+ unhandled_invalid_controls.append(field);
+ }
+ // 7. Return a negative result with the list of elements in the unhandled invalid controls list.
+ return { false, unhandled_invalid_controls };
+}
+
// https://html.spec.whatwg.org/multipage/forms.html#dom-form-checkvalidity
WebIDL::ExceptionOr HTMLFormElement::check_validity()
{
- dbgln("(STUBBED) HTMLFormElement::check_validity(). Called on: {}", debug_description());
- return true;
+ return statically_validate_constraints().result;
}
// https://html.spec.whatwg.org/multipage/forms.html#dom-form-reportvalidity
diff --git a/Libraries/LibWeb/HTML/HTMLFormElement.h b/Libraries/LibWeb/HTML/HTMLFormElement.h
index 7795142167e..c24fed3253c 100644
--- a/Libraries/LibWeb/HTML/HTMLFormElement.h
+++ b/Libraries/LibWeb/HTML/HTMLFormElement.h
@@ -78,6 +78,12 @@ public:
GC::Ref elements() const;
unsigned length() const;
+ struct StaticValidationResult {
+ bool result;
+ GC::RootVector> unhandled_invalid_controls;
+ };
+
+ StaticValidationResult statically_validate_constraints();
WebIDL::ExceptionOr check_validity();
WebIDL::ExceptionOr report_validity();