mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-01 05:39:11 +00:00
LibWeb: Implement CSS validity pseudo-classes
This commit is contained in:
parent
39e17e83f9
commit
bad7324307
Notes:
github-actions[bot]
2025-02-05 12:52:56 +00:00
Author: https://github.com/Psychpsyo
Commit: bad7324307
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3436
Reviewed-by: https://github.com/AtkinsSJ ✅
15 changed files with 745 additions and 29 deletions
|
@ -21,6 +21,7 @@
|
|||
#include <LibWeb/HTML/HTMLDetailsElement.h>
|
||||
#include <LibWeb/HTML/HTMLDialogElement.h>
|
||||
#include <LibWeb/HTML/HTMLFieldSetElement.h>
|
||||
#include <LibWeb/HTML/HTMLFormElement.h>
|
||||
#include <LibWeb/HTML/HTMLHtmlElement.h>
|
||||
#include <LibWeb/HTML/HTMLInputElement.h>
|
||||
#include <LibWeb/HTML/HTMLMediaElement.h>
|
||||
|
@ -750,7 +751,7 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
|||
return false;
|
||||
}
|
||||
case CSS::PseudoClass::PopoverOpen: {
|
||||
// https://html.spec.whatwg.org/#selector-popover-open
|
||||
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-popover-open
|
||||
// The :popover-open pseudo-class is defined to match any HTML element whose popover attribute is not in the no popover state and whose popover visibility state is showing.
|
||||
if (is<HTML::HTMLElement>(element) && element.has_attribute(HTML::AttributeNames::popover)) {
|
||||
auto& html_element = static_cast<HTML::HTMLElement const&>(element);
|
||||
|
@ -759,6 +760,134 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
|
|||
|
||||
return false;
|
||||
}
|
||||
case CSS::PseudoClass::Valid: {
|
||||
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-valid
|
||||
// The :valid pseudo-class must match any element falling into one of the following categories:
|
||||
|
||||
// - elements that are candidates for constraint validation and that satisfy their constraints
|
||||
if (auto form_associated_element = as_if<Web::HTML::FormAssociatedElement>(element))
|
||||
if (form_associated_element->is_candidate_for_constraint_validation() && form_associated_element->satisfies_its_constraints())
|
||||
return true;
|
||||
|
||||
// - form elements that are not the form owner of any elements that themselves are candidates for constraint validation but do not satisfy their constraints
|
||||
if (auto form_element = as_if<Web::HTML::HTMLFormElement>(element)) {
|
||||
bool has_invalid_elements = false;
|
||||
element.for_each_in_subtree([&](auto& node) {
|
||||
if (auto form_associated_element = as_if<Web::HTML::FormAssociatedElement>(&node)) {
|
||||
if (form_associated_element->form() == form_element && form_associated_element->is_candidate_for_constraint_validation() && !form_associated_element->satisfies_its_constraints()) {
|
||||
has_invalid_elements = true;
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
if (!has_invalid_elements)
|
||||
return true;
|
||||
}
|
||||
|
||||
// - fieldset elements that have no descendant elements that themselves are candidates for constraint validation but do not satisfy their constraints
|
||||
if (is<Web::HTML::HTMLFieldSetElement>(element)) {
|
||||
bool has_invalid_children = false;
|
||||
element.for_each_in_subtree([&](auto& node) {
|
||||
if (auto form_associated_element = as_if<Web::HTML::FormAssociatedElement>(&node)) {
|
||||
if (form_associated_element->is_candidate_for_constraint_validation() && !form_associated_element->satisfies_its_constraints()) {
|
||||
has_invalid_children = true;
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
if (!has_invalid_children)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
case CSS::PseudoClass::Invalid: {
|
||||
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-invalid
|
||||
// The :invalid pseudo-class must match any element falling into one of the following categories:
|
||||
|
||||
// - elements that are candidates for constraint validation but that do not satisfy their constraints
|
||||
if (auto form_associated_element = as_if<Web::HTML::FormAssociatedElement>(element))
|
||||
if (form_associated_element->is_candidate_for_constraint_validation() && !form_associated_element->satisfies_its_constraints())
|
||||
return true;
|
||||
|
||||
// - form elements that are the form owner of one or more elements that themselves are candidates for constraint validation but do not satisfy their constraints
|
||||
if (auto form_element = as_if<Web::HTML::HTMLFormElement>(element)) {
|
||||
bool has_invalid_elements = false;
|
||||
element.for_each_in_subtree([&](auto& node) {
|
||||
if (auto form_associated_element = as_if<Web::HTML::FormAssociatedElement>(&node)) {
|
||||
if (form_associated_element->form() == form_element && form_associated_element->is_candidate_for_constraint_validation() && !form_associated_element->satisfies_its_constraints()) {
|
||||
has_invalid_elements = true;
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
if (has_invalid_elements)
|
||||
return true;
|
||||
}
|
||||
|
||||
// - fieldset elements that have of one or more descendant elements that themselves are candidates for constraint validation but do not satisfy their constraints
|
||||
if (is<Web::HTML::HTMLFieldSetElement>(element)) {
|
||||
bool has_invalid_children = false;
|
||||
element.for_each_in_subtree([&](auto& node) {
|
||||
if (auto form_associated_element = as_if<Web::HTML::FormAssociatedElement>(&node)) {
|
||||
if (form_associated_element->is_candidate_for_constraint_validation() && !form_associated_element->satisfies_its_constraints()) {
|
||||
has_invalid_children = true;
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
if (has_invalid_children)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
case CSS::PseudoClass::UserValid: {
|
||||
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-user-valid
|
||||
// The :user-valid pseudo-class must match input, textarea, and select elements whose user validity is true,
|
||||
bool user_validity = false;
|
||||
if (auto input_element = as_if<Web::HTML::HTMLInputElement>(element)) {
|
||||
user_validity = input_element->user_validity();
|
||||
} else if (auto select_element = as_if<Web::HTML::HTMLSelectElement>(element)) {
|
||||
user_validity = select_element->user_validity();
|
||||
} else if (auto text_area_element = as_if<Web::HTML::HTMLTextAreaElement>(element)) {
|
||||
user_validity = text_area_element->user_validity();
|
||||
}
|
||||
if (!user_validity)
|
||||
return false;
|
||||
|
||||
// are candidates for constraint validation, and that satisfy their constraints.
|
||||
auto& form_associated_element = as<Web::HTML::FormAssociatedElement>(element);
|
||||
if (form_associated_element.is_candidate_for_constraint_validation() && form_associated_element.satisfies_its_constraints())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
case CSS::PseudoClass::UserInvalid: {
|
||||
// https://html.spec.whatwg.org/multipage/semantics-other.html#selector-user-invalid
|
||||
// The :user-invalid pseudo-class must match input, textarea, and select elements whose user validity is true,
|
||||
bool user_validity = false;
|
||||
if (auto input_element = as_if<Web::HTML::HTMLInputElement>(element)) {
|
||||
user_validity = input_element->user_validity();
|
||||
} else if (auto select_element = as_if<Web::HTML::HTMLSelectElement>(element)) {
|
||||
user_validity = select_element->user_validity();
|
||||
} else if (auto text_area_element = as_if<Web::HTML::HTMLTextAreaElement>(element)) {
|
||||
user_validity = text_area_element->user_validity();
|
||||
}
|
||||
if (!user_validity)
|
||||
return false;
|
||||
|
||||
// are candidates for constraint validation but do not satisfy their constraints.
|
||||
auto& form_associated_element = as<Web::HTML::FormAssociatedElement>(element);
|
||||
if (form_associated_element.is_candidate_for_constraint_validation() && !form_associated_element.satisfies_its_constraints())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -923,7 +1052,7 @@ static bool fast_matches_simple_selector(CSS::Selector::SimpleSelector const& si
|
|||
case CSS::Selector::SimpleSelector::Type::Universal:
|
||||
return matches_namespace(simple_selector.qualified_name(), element, context.style_sheet_for_rule);
|
||||
case CSS::Selector::SimpleSelector::Type::TagName:
|
||||
// https://html.spec.whatwg.org/#case-sensitivity-of-selectors
|
||||
// https://html.spec.whatwg.org/multipage/semantics-other.html#case-sensitivity-of-selectors
|
||||
// When comparing a CSS element type selector to the names of HTML elements in HTML documents, the CSS element type selector must first be converted to ASCII lowercase. The
|
||||
// same selector when compared to other elements must be compared according to its original case. In both cases, to match the values must be identical to each other (and therefore
|
||||
// the comparison is case sensitive).
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue