diff options
author | Sam Atkins <atkinssj@serenityos.org> | 2022-03-21 16:21:01 +0000 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-03-22 15:47:36 +0100 |
commit | 75ec960495637d7f866e58be4208509729369431 (patch) | |
tree | 6c0cdf03ed6ef5b9d19924dd82b7248d2e57fe80 | |
parent | c0db19f63c97ff4343df70fc8751892874a4d71a (diff) | |
download | serenity-75ec960495637d7f866e58be4208509729369431.zip |
LibWeb: Initialize PseudoClass/PseudoElement selectors in one go
There was no real benefit to creating the SimpleSelector early and then
modifying it, and doing so made this code harder to follow than it
needs to be.
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp | 191 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/Selector.h | 4 |
2 files changed, 102 insertions, 93 deletions
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 34a54e5af2..880ee41a22 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -420,10 +420,6 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_pseudo_sim } if (is_pseudo) { - Selector::SimpleSelector simple_selector { - .type = Selector::SimpleSelector::Type::PseudoElement - }; - auto const& name_token = tokens.next_token(); if (!name_token.is(Token::Type::Ident)) { dbgln_if(CSS_PARSER_DEBUG, "Expected an ident for pseudo-element, got: '{}'", name_token.to_debug_string()); @@ -439,158 +435,171 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_pseudo_sim dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-element: '::{}'", pseudo_name); return ParsingResult::SyntaxError; } - simple_selector.value = pseudo_element.value(); - return simple_selector; + return Selector::SimpleSelector { + .type = Selector::SimpleSelector::Type::PseudoElement, + .value = pseudo_element.value() + }; } if (peek_token_ends_selector()) return ParsingResult::SyntaxError; auto const& pseudo_class_token = tokens.next_token(); - Selector::SimpleSelector simple_selector { - .type = Selector::SimpleSelector::Type::PseudoClass, - .value = Selector::SimpleSelector::PseudoClass {} - }; if (pseudo_class_token.is(Token::Type::Ident)) { auto pseudo_name = pseudo_class_token.token().ident(); if (has_ignored_vendor_prefix(pseudo_name)) return ParsingResult::IncludesIgnoredVendorPrefix; + auto make_pseudo_class_selector = [](auto pseudo_class) { + return Selector::SimpleSelector { + .type = Selector::SimpleSelector::Type::PseudoClass, + .value = Selector::SimpleSelector::PseudoClass { + .type = pseudo_class } + }; + }; + if (pseudo_name.equals_ignoring_case("active")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::Active; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Active); } else if (pseudo_name.equals_ignoring_case("checked")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::Checked; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Checked); } else if (pseudo_name.equals_ignoring_case("disabled")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::Disabled; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Disabled); } else if (pseudo_name.equals_ignoring_case("empty")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::Empty; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Empty); } else if (pseudo_name.equals_ignoring_case("enabled")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::Enabled; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Enabled); } else if (pseudo_name.equals_ignoring_case("first-child")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::FirstChild; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::FirstChild); } else if (pseudo_name.equals_ignoring_case("first-of-type")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::FirstOfType; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::FirstOfType); } else if (pseudo_name.equals_ignoring_case("focus")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::Focus; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Focus); } else if (pseudo_name.equals_ignoring_case("focus-within")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::FocusWithin; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::FocusWithin); } else if (pseudo_name.equals_ignoring_case("hover")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::Hover; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Hover); } else if (pseudo_name.equals_ignoring_case("last-child")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::LastChild; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::LastChild); } else if (pseudo_name.equals_ignoring_case("last-of-type")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::LastOfType; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::LastOfType); } else if (pseudo_name.equals_ignoring_case("link")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::Link; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Link); } else if (pseudo_name.equals_ignoring_case("only-child")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::OnlyChild; + return make_pseudo_class_selector(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; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::OnlyOfType); } else if (pseudo_name.equals_ignoring_case("root")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::Root; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Root); } else if (pseudo_name.equals_ignoring_case("visited")) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::Visited; - - } else if (pseudo_name.equals_ignoring_case("after")) { - // Single-colon syntax allowed for compatibility. https://www.w3.org/TR/selectors/#pseudo-element-syntax - simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; - simple_selector.value = Selector::PseudoElement::After; - } else if (pseudo_name.equals_ignoring_case("before")) { - // See :after - simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; - simple_selector.value = Selector::PseudoElement::Before; - } else if (pseudo_name.equals_ignoring_case("first-letter")) { - // See :after - simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; - simple_selector.value = Selector::PseudoElement::FirstLetter; - } else if (pseudo_name.equals_ignoring_case("first-line")) { - // See :after - simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; - simple_selector.value = Selector::PseudoElement::FirstLine; - } else { - dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class: ':{}'", pseudo_name); - return ParsingResult::SyntaxError; + return make_pseudo_class_selector(Selector::SimpleSelector::PseudoClass::Type::Visited); + } + + // Single-colon syntax allowed for ::after, ::before, ::first-letter and ::first-line for compatibility. + // https://www.w3.org/TR/selectors/#pseudo-element-syntax + if (auto pseudo_element = pseudo_element_from_string(pseudo_name); pseudo_element.has_value()) { + switch (pseudo_element.value()) { + case Selector::PseudoElement::After: + case Selector::PseudoElement::Before: + case Selector::PseudoElement::FirstLetter: + case Selector::PseudoElement::FirstLine: + return Selector::SimpleSelector { + .type = Selector::SimpleSelector::Type::PseudoElement, + .value = pseudo_element.value() + }; + default: + break; + } } - return simple_selector; + dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class: ':{}'", pseudo_name); + return ParsingResult::SyntaxError; } + if (pseudo_class_token.is_function()) { - auto parse_nth_child_pattern = [this](Selector::SimpleSelector& simple_selector, StyleFunctionRule const& pseudo_function, bool allow_of = false) -> bool { - auto function_values = TokenStream<StyleComponentValueRule>(pseudo_function.values()); - auto nth_child_pattern = parse_a_n_plus_b_pattern(function_values, allow_of ? AllowTrailingTokens::Yes : AllowTrailingTokens::No); - if (nth_child_pattern.has_value()) { - simple_selector.pseudo_class().nth_child_pattern = nth_child_pattern.value(); - } else { - dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid format for {}", pseudo_function.name()); - return false; + auto parse_nth_child_selector = [this](auto pseudo_class, Vector<StyleComponentValueRule> const& function_values, bool allow_of = false) -> Result<Selector::SimpleSelector, Parser::ParsingResult> { + auto tokens = TokenStream<StyleComponentValueRule>(function_values); + auto nth_child_pattern = parse_a_n_plus_b_pattern(tokens, allow_of ? AllowTrailingTokens::Yes : AllowTrailingTokens::No); + if (!nth_child_pattern.has_value()) { + dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid An+B format for {}", pseudo_class_name(pseudo_class)); + return ParsingResult::SyntaxError; } - if (!allow_of) - return true; - function_values.skip_whitespace(); - if (!function_values.has_next_token()) - return true; + tokens.skip_whitespace(); + if (!allow_of || !tokens.has_next_token()) { + return Selector::SimpleSelector { + .type = Selector::SimpleSelector::Type::PseudoClass, + .value = Selector::SimpleSelector::PseudoClass { + .type = pseudo_class, + .nth_child_pattern = nth_child_pattern.release_value() } + }; + } // Parse the `of <selector-list>` syntax - auto const& maybe_of = function_values.next_token(); + auto const& maybe_of = tokens.next_token(); if (!(maybe_of.is(Token::Type::Ident) && maybe_of.token().ident().equals_ignoring_case("of"sv))) - return false; + return ParsingResult::SyntaxError; - function_values.skip_whitespace(); - auto selector_list = parse_a_selector_list(function_values, SelectorType::Standalone); + tokens.skip_whitespace(); + auto selector_list = parse_a_selector_list(tokens, SelectorType::Standalone); if (selector_list.is_error()) - return false; + return ParsingResult::SyntaxError; - function_values.skip_whitespace(); - if (function_values.has_next_token()) - return false; + tokens.skip_whitespace(); + if (tokens.has_next_token()) + return ParsingResult::SyntaxError; - simple_selector.pseudo_class().argument_selector_list = selector_list.value(); - return true; + return Selector::SimpleSelector { + .type = Selector::SimpleSelector::Type::PseudoClass, + .value = Selector::SimpleSelector::PseudoClass { + .type = pseudo_class, + .nth_child_pattern = nth_child_pattern.release_value(), + .argument_selector_list = selector_list.release_value() } + }; }; auto const& pseudo_function = pseudo_class_token.function(); if (pseudo_function.name().equals_ignoring_case("not")) { - 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, SelectorType::Standalone); if (not_selector.is_error()) { dbgln_if(CSS_PARSER_DEBUG, "Invalid selector in :not() clause"); return ParsingResult::SyntaxError; } - simple_selector.pseudo_class().argument_selector_list = not_selector.release_value(); + + return Selector::SimpleSelector { + .type = Selector::SimpleSelector::Type::PseudoClass, + .value = Selector::SimpleSelector::PseudoClass { + .type = Selector::SimpleSelector::PseudoClass::Type::Not, + .argument_selector_list = not_selector.release_value() } + }; } else if (pseudo_function.name().equals_ignoring_case("lang"sv)) { - simple_selector.pseudo_class().type = Selector::SimpleSelector::PseudoClass::Type::Lang; if (pseudo_function.values().is_empty()) { dbgln_if(CSS_PARSER_DEBUG, "Empty :lang() selector"); return ParsingResult::SyntaxError; } // FIXME: Support multiple, comma-separated, language ranges. - simple_selector.pseudo_class().languages.append(pseudo_function.values().first().token().to_string()); + Vector<FlyString> languages; + languages.append(pseudo_function.values().first().token().to_string()); + return Selector::SimpleSelector { + .type = Selector::SimpleSelector::Type::PseudoClass, + .value = Selector::SimpleSelector::PseudoClass { + .type = Selector::SimpleSelector::PseudoClass::Type::Lang, + .languages = move(languages) } + }; } 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, true)) - return ParsingResult::SyntaxError; + return parse_nth_child_selector(Selector::SimpleSelector::PseudoClass::Type::NthChild, pseudo_function.values(), true); } 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, true)) - return ParsingResult::SyntaxError; + return parse_nth_child_selector(Selector::SimpleSelector::PseudoClass::Type::NthLastChild, pseudo_function.values(), true); } 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; + return parse_nth_child_selector(Selector::SimpleSelector::PseudoClass::Type::NthOfType, pseudo_function.values(), false); } 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; - } else { - dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class function: ':{}'()", pseudo_function.name()); - return ParsingResult::SyntaxError; + return parse_nth_child_selector(Selector::SimpleSelector::PseudoClass::Type::NthLastOfType, pseudo_function.values(), false); } - return simple_selector; + dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class function: ':{}'()", pseudo_function.name()); + return ParsingResult::SyntaxError; } dbgln_if(CSS_PARSER_DEBUG, "Unexpected Block in pseudo-class name, expected a function or identifier. '{}'", pseudo_class_token.to_debug_string()); return ParsingResult::SyntaxError; diff --git a/Userland/Libraries/LibWeb/CSS/Selector.h b/Userland/Libraries/LibWeb/CSS/Selector.h index bf63a82a25..6893bf083a 100644 --- a/Userland/Libraries/LibWeb/CSS/Selector.h +++ b/Userland/Libraries/LibWeb/CSS/Selector.h @@ -85,12 +85,12 @@ public: // FIXME: We don't need this field on every single SimpleSelector, but it's also annoying to malloc it somewhere. // Only used when "pseudo_class" is "NthChild" or "NthLastChild". - ANPlusBPattern nth_child_pattern; + ANPlusBPattern nth_child_pattern {}; SelectorList argument_selector_list {}; // Used for :lang(en-gb,dk) - Vector<FlyString> languages; + Vector<FlyString> languages {}; }; struct Attribute { |