LibWeb/HTML: Implement report_validity
Some checks are pending
CI / Lagom (x86_64, Fuzzers_CI, false, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (arm64, Sanitizer_CI, false, macos-15, macOS, 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 (arm64, macos-15, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (x86_64, 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 implements the previously stubbed out `report_validity` method.

The specification is not very clear on how to exactly report the
validity. For now, we bring the first visible invalid control into
view and focus it. In the future, however, it would make sense to
support more complex scenarios and be more aligned with the other
implementations.
This commit is contained in:
Glenn Skrzypczak 2025-05-03 19:52:01 +02:00 committed by Shannon Booth
commit 5c578b6057
Notes: github-actions[bot] 2025-05-13 21:40:10 +00:00
11 changed files with 363 additions and 7 deletions

View file

@ -13,6 +13,7 @@
#include <LibWeb/DOM/Event.h>
#include <LibWeb/DOM/Position.h>
#include <LibWeb/DOM/SelectionchangeEventDispatching.h>
#include <LibWeb/HTML/Focus.h>
#include <LibWeb/HTML/FormAssociatedElement.h>
#include <LibWeb/HTML/HTMLButtonElement.h>
#include <LibWeb/HTML/HTMLDataListElement.h>
@ -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
{

View file

@ -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;

View file

@ -17,6 +17,7 @@
#include <LibWeb/DOMURL/DOMURL.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/EventNames.h>
#include <LibWeb/HTML/Focus.h>
#include <LibWeb/HTML/FormControlInfrastructure.h>
#include <LibWeb/HTML/HTMLButtonElement.h>
#include <LibWeb/HTML/HTMLDialogElement.h>
@ -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<bool> HTMLFormElement::check_validity()
{
@ -608,8 +644,7 @@ WebIDL::ExceptionOr<bool> HTMLFormElement::check_validity()
// https://html.spec.whatwg.org/multipage/forms.html#dom-form-reportvalidity
WebIDL::ExceptionOr<bool> 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

View file

@ -84,6 +84,7 @@ public:
};
StaticValidationResult statically_validate_constraints();
bool interactively_validate_constraints();
WebIDL::ExceptionOr<bool> check_validity();
WebIDL::ExceptionOr<bool> report_validity();

View file

@ -2785,8 +2785,7 @@ WebIDL::ExceptionOr<bool> HTMLInputElement::check_validity()
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-reportvalidity
WebIDL::ExceptionOr<bool> HTMLInputElement::report_validity()
{
dbgln("(STUBBED) HTMLInputElement::report_validity(). Called on: {}", debug_description());
return true;
return report_validity_steps();
}
Optional<ARIA::Role> HTMLInputElement::default_role() const

View file

@ -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();

View file

@ -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

View file

@ -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();

View file

@ -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