فهرست منبع

LibWeb/CSS: Add quick-and-dirty parsing for pseudo-element selectors

As noted, this is hacky because the parser wasn't written to allow
parsing an individual component of a selector. (Fox example, the
convenient-sounding `parse_pseudo_simple_selector()` assumes the first
colon has already been consumed...) So until that changes, this parses
the input as an entire selector-list, and then throws it away if it's
not a single pseudo-element selector.

It's only temporary though, I promise. 😅
Sam Atkins 11 ماه پیش
والد
کامیت
dae9c9be40

+ 6 - 1
Userland/Libraries/LibWeb/CSS/Parser/Helpers.cpp

@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
  * Copyright (c) 2020-2023, the SerenityOS developers.
- * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
+ * Copyright (c) 2021-2024, Sam Atkins <atkinssj@serenityos.org>
  * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  * Copyright (c) 2022, MacDue <macdue@dueutil.tech>
  *
@@ -49,6 +49,11 @@ Optional<CSS::SelectorList> parse_selector(CSS::Parser::ParsingContext const& co
     return CSS::Parser::Parser::create(context, selector_text).parse_as_selector();
 }
 
+Optional<CSS::Selector::PseudoElement> parse_pseudo_element_selector(CSS::Parser::ParsingContext const& context, StringView selector_text)
+{
+    return CSS::Parser::Parser::create(context, selector_text).parse_as_pseudo_element_selector();
+}
+
 RefPtr<CSS::MediaQuery> parse_media_query(CSS::Parser::ParsingContext const& context, StringView string)
 {
     return CSS::Parser::Parser::create(context, string).parse_as_media_query();

+ 3 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.h

@@ -60,6 +60,8 @@ public:
     Optional<SelectorList> parse_as_selector(SelectorParsingMode = SelectorParsingMode::Standard);
     Optional<SelectorList> parse_as_relative_selector(SelectorParsingMode = SelectorParsingMode::Standard);
 
+    Optional<Selector::PseudoElement> parse_as_pseudo_element_selector();
+
     Vector<NonnullRefPtr<MediaQuery>> parse_as_media_query_list();
     RefPtr<MediaQuery> parse_as_media_query();
 
@@ -412,6 +414,7 @@ CSS::CSSStyleSheet* parse_css_stylesheet(CSS::Parser::ParsingContext const&, Str
 CSS::ElementInlineCSSStyleDeclaration* parse_css_style_attribute(CSS::Parser::ParsingContext const&, StringView, DOM::Element&);
 RefPtr<CSS::StyleValue> parse_css_value(CSS::Parser::ParsingContext const&, StringView, CSS::PropertyID property_id = CSS::PropertyID::Invalid);
 Optional<CSS::SelectorList> parse_selector(CSS::Parser::ParsingContext const&, StringView);
+Optional<CSS::Selector::PseudoElement> parse_pseudo_element_selector(CSS::Parser::ParsingContext const&, StringView);
 CSS::CSSRule* parse_css_rule(CSS::Parser::ParsingContext const&, StringView);
 RefPtr<CSS::MediaQuery> parse_media_query(CSS::Parser::ParsingContext const&, StringView);
 Vector<NonnullRefPtr<CSS::MediaQuery>> parse_media_query_list(CSS::Parser::ParsingContext const&, StringView);

+ 28 - 0
Userland/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp

@@ -32,6 +32,34 @@ Optional<SelectorList> Parser::parse_as_relative_selector(SelectorParsingMode pa
     return {};
 }
 
+Optional<Selector::PseudoElement> Parser::parse_as_pseudo_element_selector()
+{
+    // FIXME: This is quite janky. Selector parsing is not at all designed to allow parsing just a single part of a selector.
+    //        So, this code parses a whole selector, then rejects it if it's not a single pseudo-element simple selector.
+    //        Come back and fix this, future Sam!
+    auto maybe_selector_list = parse_a_selector_list(m_token_stream, SelectorType::Standalone, SelectorParsingMode::Standard);
+    if (maybe_selector_list.is_error())
+        return {};
+    auto& selector_list = maybe_selector_list.value();
+
+    if (selector_list.size() != 1)
+        return {};
+    auto& selector = selector_list.first();
+
+    if (selector->compound_selectors().size() != 1)
+        return {};
+    auto& first_compound_selector = selector->compound_selectors().first();
+
+    if (first_compound_selector.simple_selectors.size() != 1)
+        return {};
+    auto& simple_selector = first_compound_selector.simple_selectors.first();
+
+    if (simple_selector.type != Selector::SimpleSelector::Type::PseudoElement)
+        return {};
+
+    return simple_selector.pseudo_element();
+}
+
 template<typename T>
 Parser::ParseErrorOr<SelectorList> Parser::parse_a_selector_list(TokenStream<T>& tokens, SelectorType mode, SelectorParsingMode parsing_mode)
 {