diff --git a/Libraries/LibWeb/CSS/PseudoClasses.json b/Libraries/LibWeb/CSS/PseudoClasses.json index ba69497114c..4c3f43e42d8 100644 --- a/Libraries/LibWeb/CSS/PseudoClasses.json +++ b/Libraries/LibWeb/CSS/PseudoClasses.json @@ -164,6 +164,9 @@ "target": { "argument": "" }, + "unchecked": { + "argument": "" + }, "user-invalid": { "argument": "" }, diff --git a/Libraries/LibWeb/CSS/Selector.cpp b/Libraries/LibWeb/CSS/Selector.cpp index b358025e373..94b2f36d216 100644 --- a/Libraries/LibWeb/CSS/Selector.cpp +++ b/Libraries/LibWeb/CSS/Selector.cpp @@ -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, diff --git a/Libraries/LibWeb/CSS/SelectorEngine.cpp b/Libraries/LibWeb/CSS/SelectorEngine.cpp index 3a00569e022..3af523f1fac 100644 --- a/Libraries/LibWeb/CSS/SelectorEngine.cpp +++ b/Libraries/LibWeb/CSS/SelectorEngine.cpp @@ -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: diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index 6bcb9869597..32ad7bfe526 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -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(*this)) { - auto const& input_element = static_cast(*this); - switch (input_element.type_state()) { + if (auto* input_element = as_if(*this)) { + switch (input_element->type_state()) { case HTML::HTMLInputElement::TypeAttributeState::Checkbox: case HTML::HTMLInputElement::TypeAttributeState::RadioButton: return static_cast(*this).checked(); @@ -1437,8 +1436,27 @@ bool Element::matches_checked_pseudo_class() const } // - option elements whose selectedness is true - if (is(*this)) { - return static_cast(*this).selected(); + if (auto* option_element = as_if(*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(*this)) { + switch (input_element->type_state()) { + case HTML::HTMLInputElement::TypeAttributeState::Checkbox: + case HTML::HTMLInputElement::TypeAttributeState::RadioButton: + return !static_cast(*this).checked(); + default: + return false; + } + } + + if (auto* option_element = as_if(*this)) { + return !option_element->selected(); } return false; } diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h index e7315e481f6..4cda69ed442 100644 --- a/Libraries/LibWeb/DOM/Element.h +++ b/Libraries/LibWeb/DOM/Element.h @@ -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; diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 5ea57f4a2de..0d71f593353 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -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())