From c148ed50bb61df0d5109a91fb6a9de03631d3e88 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 17 Mar 2022 15:28:42 +0000 Subject: LibWeb: Implement the :is() selector This lets us finally get rid of a FIXME in the default style sheet. :^) --- Userland/Libraries/LibWeb/CSS/Default.css | 11 ++--------- Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp | 19 +++++++++++++------ Userland/Libraries/LibWeb/CSS/Selector.cpp | 7 +++++-- Userland/Libraries/LibWeb/CSS/Selector.h | 5 ++++- Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp | 8 +++++++- Userland/Libraries/LibWeb/Dump.cpp | 8 ++++++-- 6 files changed, 37 insertions(+), 21 deletions(-) (limited to 'Userland') diff --git a/Userland/Libraries/LibWeb/CSS/Default.css b/Userland/Libraries/LibWeb/CSS/Default.css index fbdfff0386..f66dc9f372 100644 --- a/Userland/Libraries/LibWeb/CSS/Default.css +++ b/Userland/Libraries/LibWeb/CSS/Default.css @@ -213,18 +213,11 @@ ol { list-style-type: decimal; } -/* FIXME: Implement these using :is() :^) */ -/* :is(ul, ol) ul */ -ul ul, -ol ul { +:is(ul, ol) ul { list-style-type: circle; } -/* :is(ul, ol) :is(ul, ol) ul */ -ul ul ul, -ol ul ul, -ul ol ul, -ol ol ul { +:is(ul, ol) :is(ul, ol) ul { list-style-type: square; } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 411dcb7e1e..134228c16b 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -584,7 +584,14 @@ Result Parser::parse_simple_sel }; auto& pseudo_function = pseudo_class_token.function(); - if (pseudo_function.name().equals_ignoring_case("not")) { + if (pseudo_function.name().equals_ignoring_case("is"sv)) { + simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Is; + auto function_token_stream = TokenStream(pseudo_function.values()); + auto is_selector = parse_a_selector_list(function_token_stream, SelectorParsingMode::Forgiving); + // NOTE: Because it's forgiving, even complete garbage will parse OK as an empty selector-list. + VERIFY(!is_selector.is_error()); + simple_selector.pseudo_class.argument_selector_list = is_selector.release_value(); + } else if (pseudo_function.name().equals_ignoring_case("not"sv)) { simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Not; auto function_token_stream = TokenStream(pseudo_function.values()); auto not_selector = parse_a_selector_list(function_token_stream); @@ -592,20 +599,20 @@ Result Parser::parse_simple_sel dbgln_if(CSS_PARSER_DEBUG, "Invalid selector in :not() clause"); return ParsingResult::SyntaxError; } - simple_selector.pseudo_class.not_selector = not_selector.release_value(); - } else if (pseudo_function.name().equals_ignoring_case("nth-child")) { + simple_selector.pseudo_class.argument_selector_list = not_selector.release_value(); + } else if (pseudo_function.name().equals_ignoring_case("nth-child"sv)) { simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthChild; if (!parse_nth_child_pattern(simple_selector, pseudo_function)) return ParsingResult::SyntaxError; - } else if (pseudo_function.name().equals_ignoring_case("nth-last-child")) { + } else if (pseudo_function.name().equals_ignoring_case("nth-last-child"sv)) { simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastChild; if (!parse_nth_child_pattern(simple_selector, pseudo_function)) return ParsingResult::SyntaxError; - } else if (pseudo_function.name().equals_ignoring_case("nth-of-type")) { + } else if (pseudo_function.name().equals_ignoring_case("nth-of-type"sv)) { simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthOfType; if (!parse_nth_child_pattern(simple_selector, pseudo_function)) return ParsingResult::SyntaxError; - } else if (pseudo_function.name().equals_ignoring_case("nth-last-of-type")) { + } else if (pseudo_function.name().equals_ignoring_case("nth-last-of-type"sv)) { simple_selector.pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastOfType; if (!parse_nth_child_pattern(simple_selector, pseudo_function)) return ParsingResult::SyntaxError; diff --git a/Userland/Libraries/LibWeb/CSS/Selector.cpp b/Userland/Libraries/LibWeb/CSS/Selector.cpp index c2e7a8ca2e..85dc1a3b07 100644 --- a/Userland/Libraries/LibWeb/CSS/Selector.cpp +++ b/Userland/Libraries/LibWeb/CSS/Selector.cpp @@ -158,6 +158,7 @@ String Selector::SimpleSelector::serialize() const case Selector::SimpleSelector::PseudoClass::Type::NthChild: case Selector::SimpleSelector::PseudoClass::Type::NthLastChild: case Selector::SimpleSelector::PseudoClass::Type::Not: + case Selector::SimpleSelector::PseudoClass::Type::Is: // Otherwise, append ":" (U+003A), followed by the name of the pseudo-class, followed by "(" (U+0028), // followed by the value of the pseudo-class argument(s) determined as per below, followed by ")" (U+0029), to s. s.append(':'); @@ -167,9 +168,11 @@ String Selector::SimpleSelector::serialize() const || pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::NthLastChild) { // The result of serializing the value using the rules to serialize an value. s.append(pseudo_class.nth_child_pattern.serialize()); - } else if (pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Not) { + } else if (pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Not + || pseudo_class.type == Selector::SimpleSelector::PseudoClass::Type::Is) { // The result of serializing the value using the rules for serializing a group of selectors. - s.append(serialize_a_group_of_selectors(pseudo_class.not_selector)); + // NOTE: `:is()` isn't in the spec for this yet, but it should be! + s.append(serialize_a_group_of_selectors(pseudo_class.argument_selector_list)); } s.append(')'); break; diff --git a/Userland/Libraries/LibWeb/CSS/Selector.h b/Userland/Libraries/LibWeb/CSS/Selector.h index cbc8a874f0..57a2ee4827 100644 --- a/Userland/Libraries/LibWeb/CSS/Selector.h +++ b/Userland/Libraries/LibWeb/CSS/Selector.h @@ -75,6 +75,7 @@ public: Disabled, Enabled, Checked, + Is, Not, Active, }; @@ -84,7 +85,7 @@ public: // Only used when "pseudo_class" is "NthChild" or "NthLastChild". ANPlusBPattern nth_child_pattern; - SelectorList not_selector {}; + SelectorList argument_selector_list {}; }; PseudoClass pseudo_class {}; PseudoElement pseudo_element { PseudoElement::None }; @@ -211,6 +212,8 @@ constexpr StringView pseudo_class_name(Selector::SimpleSelector::PseudoClass::Ty return "nth-child"sv; case Selector::SimpleSelector::PseudoClass::Type::NthLastChild: return "nth-last-child"sv; + case Selector::SimpleSelector::PseudoClass::Type::Is: + return "is"sv; case Selector::SimpleSelector::PseudoClass::Type::Not: return "not"sv; case Selector::SimpleSelector::PseudoClass::Type::None: diff --git a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp index ad031dd614..c841aa4f6a 100644 --- a/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp +++ b/Userland/Libraries/LibWeb/CSS/SelectorEngine.cpp @@ -154,8 +154,14 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla return true; case CSS::Selector::SimpleSelector::PseudoClass::Type::Checked: return matches_checked_pseudo_class(element); + case CSS::Selector::SimpleSelector::PseudoClass::Type::Is: + for (auto& selector : pseudo_class.argument_selector_list) { + if (matches(selector, element)) + return true; + } + return false; case CSS::Selector::SimpleSelector::PseudoClass::Type::Not: - for (auto& selector : pseudo_class.not_selector) { + for (auto& selector : pseudo_class.argument_selector_list) { if (matches(selector, element)) return false; } diff --git a/Userland/Libraries/LibWeb/Dump.cpp b/Userland/Libraries/LibWeb/Dump.cpp index b7c2c61ab8..62d68c231a 100644 --- a/Userland/Libraries/LibWeb/Dump.cpp +++ b/Userland/Libraries/LibWeb/Dump.cpp @@ -430,12 +430,16 @@ void dump_selector(StringBuilder& builder, CSS::Selector const& selector) case CSS::Selector::SimpleSelector::PseudoClass::Type::Not: pseudo_class_description = "Not"; break; + case CSS::Selector::SimpleSelector::PseudoClass::Type::Is: + pseudo_class_description = "Is"; + break; } builder.appendff(" pseudo_class={}", pseudo_class_description); - if (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Not) { + if (pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Not + || pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::Is) { builder.append("(["); - for (auto& selector : pseudo_class.not_selector) + for (auto& selector : pseudo_class.argument_selector_list) dump_selector(builder, selector); builder.append("])"); } else if ((pseudo_class.type == CSS::Selector::SimpleSelector::PseudoClass::Type::NthChild) -- cgit v1.2.3