1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 17:17:44 +00:00

LibWeb: Support CSS :only-of-type selector

This matches any element that doesn't have a sibling with the same tag
name as itself.
This commit is contained in:
Andreas Kling 2022-02-17 22:43:22 +01:00
parent b9b24cb1c1
commit 7c33a084fb
5 changed files with 31 additions and 10 deletions

View file

@ -551,6 +551,8 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Link; simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Link;
} else if (pseudo_name.equals_ignoring_case("only-child")) { } else if (pseudo_name.equals_ignoring_case("only-child")) {
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::OnlyChild; simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::OnlyChild;
} else if (pseudo_name.equals_ignoring_case("only-of-type")) {
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::OnlyOfType;
} else if (pseudo_name.equals_ignoring_case("root")) { } else if (pseudo_name.equals_ignoring_case("root")) {
simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Root; simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Root;
} else if (pseudo_name.equals_ignoring_case("visited")) { } else if (pseudo_name.equals_ignoring_case("visited")) {

View file

@ -135,6 +135,7 @@ String Selector::SimpleSelector::serialize() const
case Selector::SimpleSelector::PseudoClass::Type::Root: case Selector::SimpleSelector::PseudoClass::Type::Root:
case Selector::SimpleSelector::PseudoClass::Type::FirstOfType: case Selector::SimpleSelector::PseudoClass::Type::FirstOfType:
case Selector::SimpleSelector::PseudoClass::Type::LastOfType: case Selector::SimpleSelector::PseudoClass::Type::LastOfType:
case Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
case Selector::SimpleSelector::PseudoClass::Type::Disabled: case Selector::SimpleSelector::PseudoClass::Type::Disabled:
case Selector::SimpleSelector::PseudoClass::Type::Enabled: case Selector::SimpleSelector::PseudoClass::Type::Enabled:
case Selector::SimpleSelector::PseudoClass::Type::Checked: case Selector::SimpleSelector::PseudoClass::Type::Checked:
@ -281,6 +282,8 @@ constexpr StringView pseudo_class_name(Selector::SimpleSelector::PseudoClass::Ty
return "first-of-type"sv; return "first-of-type"sv;
case Selector::SimpleSelector::PseudoClass::Type::LastOfType: case Selector::SimpleSelector::PseudoClass::Type::LastOfType:
return "last-of-type"sv; return "last-of-type"sv;
case Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
return "only-of-type"sv;
case Selector::SimpleSelector::PseudoClass::Type::Disabled: case Selector::SimpleSelector::PseudoClass::Type::Disabled:
return "disabled"sv; return "disabled"sv;
case Selector::SimpleSelector::PseudoClass::Type::Enabled: case Selector::SimpleSelector::PseudoClass::Type::Enabled:

View file

@ -57,6 +57,7 @@ public:
Root, Root,
FirstOfType, FirstOfType,
LastOfType, LastOfType,
OnlyOfType,
NthChild, NthChild,
NthLastChild, NthLastChild,
Disabled, Disabled,

View file

@ -53,6 +53,24 @@ static inline bool matches_attribute(CSS::Selector::SimpleSelector::Attribute co
return false; return false;
} }
static inline DOM::Element const* previous_sibling_with_same_tag_name(DOM::Element const& element)
{
for (auto const* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) {
if (sibling->tag_name() == element.tag_name())
return sibling;
}
return nullptr;
}
static inline DOM::Element const* next_sibling_with_same_tag_name(DOM::Element const& element)
{
for (auto const* sibling = element.next_element_sibling(); sibling; sibling = sibling->next_element_sibling()) {
if (sibling->tag_name() == element.tag_name())
return sibling;
}
return nullptr;
}
static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClass const& pseudo_class, DOM::Element const& element) static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClass const& pseudo_class, DOM::Element const& element)
{ {
switch (pseudo_class.type) { switch (pseudo_class.type) {
@ -80,17 +98,11 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla
case CSS::Selector::SimpleSelector::PseudoClass::Type::Root: case CSS::Selector::SimpleSelector::PseudoClass::Type::Root:
return is<HTML::HTMLHtmlElement>(element); return is<HTML::HTMLHtmlElement>(element);
case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstOfType: case CSS::Selector::SimpleSelector::PseudoClass::Type::FirstOfType:
for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) { return !previous_sibling_with_same_tag_name(element);
if (sibling->tag_name() == element.tag_name())
return false;
}
return true;
case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType: case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType:
for (auto* sibling = element.next_element_sibling(); sibling; sibling = sibling->next_element_sibling()) { return !next_sibling_with_same_tag_name(element);
if (sibling->tag_name() == element.tag_name()) case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
return false; return !previous_sibling_with_same_tag_name(element) && !next_sibling_with_same_tag_name(element);
}
return true;
case CSS::Selector::SimpleSelector::PseudoClass::Type::Disabled: case CSS::Selector::SimpleSelector::PseudoClass::Type::Disabled:
if (!element.tag_name().equals_ignoring_case(HTML::TagNames::input)) if (!element.tag_name().equals_ignoring_case(HTML::TagNames::input))
return false; return false;

View file

@ -382,6 +382,9 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector)
case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType: case CSS::Selector::SimpleSelector::PseudoClass::Type::LastOfType:
pseudo_class_description = "LastOfType"; pseudo_class_description = "LastOfType";
break; break;
case CSS::Selector::SimpleSelector::PseudoClass::Type::OnlyOfType:
pseudo_class_description = "OnlyOfType";
break;
case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild: case CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild:
pseudo_class_description = "NthChild"; pseudo_class_description = "NthChild";
break; break;