LibWeb/CSS: Add :unchecked pseudo-class

This just got added to the Selectors spec:

b78c97c19d

It's thus missing from the HTML spec and WPT, but I figured it was
simple enough to add.
This commit is contained in:
Sam Atkins 2025-07-15 15:38:50 +01:00 committed by Jelle Raaijmakers
commit 632ce9523b
Notes: github-actions[bot] 2025-07-15 19:28:57 +00:00
6 changed files with 34 additions and 6 deletions

View file

@ -164,6 +164,9 @@
"target": {
"argument": ""
},
"unchecked": {
"argument": ""
},
"user-invalid": {
"argument": ""
},

View file

@ -62,6 +62,7 @@ static bool can_selector_use_fast_matches(Selector const& selector)
PseudoClass::OnlyChild,
PseudoClass::Root,
PseudoClass::State,
PseudoClass::Unchecked,
PseudoClass::Visited))
return false;
} else if (!first_is_one_of(simple_selector.type,

View file

@ -616,6 +616,8 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
return element.matches_enabled_pseudo_class();
case CSS::PseudoClass::Checked:
return element.matches_checked_pseudo_class();
case CSS::PseudoClass::Unchecked:
return element.matches_unchecked_pseudo_class();
case CSS::PseudoClass::Indeterminate:
return matches_indeterminate_pseudo_class(element);
case CSS::PseudoClass::HighValue:

View file

@ -1425,9 +1425,8 @@ bool Element::matches_checked_pseudo_class() const
// The :checked pseudo-class must match any element falling into one of the following categories:
// - input elements whose type attribute is in the Checkbox state and whose checkedness state is true
// - input elements whose type attribute is in the Radio Button state and whose checkedness state is true
if (is<HTML::HTMLInputElement>(*this)) {
auto const& input_element = static_cast<HTML::HTMLInputElement const&>(*this);
switch (input_element.type_state()) {
if (auto* input_element = as_if<HTML::HTMLInputElement>(*this)) {
switch (input_element->type_state()) {
case HTML::HTMLInputElement::TypeAttributeState::Checkbox:
case HTML::HTMLInputElement::TypeAttributeState::RadioButton:
return static_cast<HTML::HTMLInputElement const&>(*this).checked();
@ -1437,8 +1436,27 @@ bool Element::matches_checked_pseudo_class() const
}
// - option elements whose selectedness is true
if (is<HTML::HTMLOptionElement>(*this)) {
return static_cast<HTML::HTMLOptionElement const&>(*this).selected();
if (auto* option_element = as_if<HTML::HTMLOptionElement>(*this)) {
return option_element->selected();
}
return false;
}
bool Element::matches_unchecked_pseudo_class() const
{
// AD-HOC: There is no spec for this yet, so it's based on the spec for :checked, assuming that :unchecked applies to the same cases but with a `false` value.
if (auto* input_element = as_if<HTML::HTMLInputElement>(*this)) {
switch (input_element->type_state()) {
case HTML::HTMLInputElement::TypeAttributeState::Checkbox:
case HTML::HTMLInputElement::TypeAttributeState::RadioButton:
return !static_cast<HTML::HTMLInputElement const&>(*this).checked();
default:
return false;
}
}
if (auto* option_element = as_if<HTML::HTMLOptionElement>(*this)) {
return !option_element->selected();
}
return false;
}

View file

@ -434,6 +434,7 @@ public:
bool matches_enabled_pseudo_class() const;
bool matches_disabled_pseudo_class() const;
bool matches_checked_pseudo_class() const;
bool matches_unchecked_pseudo_class() const;
bool matches_placeholder_shown_pseudo_class() const;
bool matches_link_pseudo_class() const;
bool matches_local_link_pseudo_class() const;

View file

@ -167,7 +167,10 @@ void HTMLInputElement::set_checked(bool checked)
invalidate_style(
DOM::StyleInvalidationReason::HTMLInputElementSetChecked,
{ { .type = CSS::InvalidationSet::Property::Type::PseudoClass, .value = CSS::PseudoClass::Checked } },
{
{ .type = CSS::InvalidationSet::Property::Type::PseudoClass, .value = CSS::PseudoClass::Checked },
{ .type = CSS::InvalidationSet::Property::Type::PseudoClass, .value = CSS::PseudoClass::Unchecked },
},
{});
if (auto* paintable = this->paintable())