diff --git a/Libraries/LibWeb/CSS/Parser/Helpers.cpp b/Libraries/LibWeb/CSS/Parser/Helpers.cpp index a407a36e9f3..92292570e0b 100644 --- a/Libraries/LibWeb/CSS/Parser/Helpers.cpp +++ b/Libraries/LibWeb/CSS/Parser/Helpers.cpp @@ -106,6 +106,11 @@ Optional parse_selector_for_nested_style_rule(CSS::Parser::Pa return adapt_nested_relative_selector_list(*maybe_selectors); } +Optional parse_page_selector_list(CSS::Parser::ParsingParams const& params, StringView selector_text) +{ + return CSS::Parser::Parser::create(params, selector_text).parse_as_page_selector_list(); +} + Optional parse_pseudo_element_selector(CSS::Parser::ParsingParams const& context, StringView selector_text) { return CSS::Parser::Parser::create(context, selector_text).parse_as_pseudo_element_selector(); diff --git a/Libraries/LibWeb/CSS/Parser/Parser.h b/Libraries/LibWeb/CSS/Parser/Parser.h index 5d715531e36..bf378191146 100644 --- a/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Libraries/LibWeb/CSS/Parser/Parser.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -117,6 +118,8 @@ public: Optional parse_as_pseudo_element_selector(); + Optional parse_as_page_selector_list(); + Vector> parse_as_media_query_list(); RefPtr parse_as_media_query(); @@ -188,6 +191,9 @@ private: template ParseErrorOr parse_a_selector_list(TokenStream&, SelectorType, SelectorParsingMode = SelectorParsingMode::Standard); + template + ParseErrorOr parse_a_page_selector_list(TokenStream&); + template Vector> parse_a_media_query_list(TokenStream&); template @@ -523,6 +529,7 @@ RefPtr parse_css_value(CSS::Parser::ParsingParams cons RefPtr parse_css_descriptor(CSS::Parser::ParsingParams const&, CSS::AtRuleID, CSS::DescriptorID, StringView); Optional parse_selector(CSS::Parser::ParsingParams const&, StringView); Optional parse_selector_for_nested_style_rule(CSS::Parser::ParsingParams const&, StringView); +Optional parse_page_selector_list(CSS::Parser::ParsingParams const&, StringView); Optional parse_pseudo_element_selector(CSS::Parser::ParsingParams const&, StringView); CSS::CSSRule* parse_css_rule(CSS::Parser::ParsingParams const&, StringView); RefPtr parse_media_query(CSS::Parser::ParsingParams const&, StringView); diff --git a/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp b/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp index 491dd480634..ebeb794079d 100644 --- a/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/RuleParsing.cpp @@ -639,74 +639,13 @@ GC::Ptr Parser::convert_to_font_face_rule(AtRule const& rule) return CSSFontFaceRule::create(realm(), CSSFontFaceDescriptors::create(realm(), descriptors.release_descriptors())); } -static Optional parse_page_selector_list(Vector const& component_values) -{ - // https://drafts.csswg.org/css-page-3/#syntax-page-selector - // = # - // = [ ? * ]! - // = : [ left | right | first | blank ] - - PageSelectorList selector_list; - - TokenStream tokens { component_values }; - tokens.discard_whitespace(); - - while (tokens.has_next_token()) { - // First optional ident - Optional maybe_ident; - if (tokens.next_token().is(Token::Type::Ident)) - maybe_ident = tokens.consume_a_token().token().ident(); - - // Then an optional series of pseudo-classes - Vector pseudo_classes; - while (tokens.next_token().is(Token::Type::Colon)) { - tokens.discard_a_token(); // : - if (!tokens.next_token().is(Token::Type::Ident)) { - dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: pseudo-class is not an ident: `{}`", tokens.next_token().to_debug_string()); - return {}; - } - auto pseudo_class_name = tokens.consume_a_token().token().ident(); - if (auto pseudo_class = page_pseudo_class_from_string(pseudo_class_name); pseudo_class.has_value()) { - pseudo_classes.append(*pseudo_class); - } else { - dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: unrecognized pseudo-class `:{}`", pseudo_class_name); - return {}; - } - } - - if (!maybe_ident.has_value() && pseudo_classes.is_empty()) { - // Nothing parsed - dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: is empty"); - return {}; - } - - selector_list.empend(move(maybe_ident), move(pseudo_classes)); - - tokens.discard_whitespace(); - - if (tokens.next_token().is(Token::Type::Comma)) { - tokens.discard_a_token(); // , - tokens.discard_whitespace(); - if (!tokens.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: trailing comma"); - return {}; - } - - } else if (tokens.has_next_token()) { - dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: trailing token `{}`", tokens.next_token().to_debug_string()); - return {}; - } - } - - return selector_list; -} - GC::Ptr Parser::convert_to_page_rule(AtRule const& page_rule) { // https://drafts.csswg.org/css-page-3/#syntax-page-selector // @page = @page ? { } - auto page_selectors = parse_page_selector_list(page_rule.prelude); - if (!page_selectors.has_value()) + TokenStream tokens { page_rule.prelude }; + auto page_selectors = parse_a_page_selector_list(tokens); + if (page_selectors.is_error()) return nullptr; GC::RootVector> child_rules { realm().heap() }; diff --git a/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp b/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp index d067116ae1e..1456734b13d 100644 --- a/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/SelectorParsing.cpp @@ -1147,4 +1147,76 @@ Optional Parser::parse_a_n_plus_b_patt return syntax_error(); } +Optional Parser::parse_as_page_selector_list() +{ + auto selector_list = parse_a_page_selector_list(m_token_stream); + if (!selector_list.is_error()) + return selector_list.release_value(); + return {}; +} + +template +Parser::ParseErrorOr Parser::parse_a_page_selector_list(TokenStream& tokens) +{ + // https://drafts.csswg.org/css-page-3/#syntax-page-selector + // = # + // = [ ? * ]! + // = : [ left | right | first | blank ] + + PageSelectorList selector_list; + + tokens.discard_whitespace(); + + while (tokens.has_next_token()) { + // First optional ident + Optional maybe_ident; + if (tokens.next_token().is(Token::Type::Ident)) + maybe_ident = static_cast(tokens.consume_a_token()).ident(); + + // Then an optional series of pseudo-classes + Vector pseudo_classes; + while (tokens.next_token().is(Token::Type::Colon)) { + tokens.discard_a_token(); // : + if (!tokens.next_token().is(Token::Type::Ident)) { + dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: pseudo-class is not an ident: `{}`", tokens.next_token().to_debug_string()); + return ParseError::SyntaxError; + } + auto pseudo_class_name = static_cast(tokens.consume_a_token()).ident(); + if (auto pseudo_class = page_pseudo_class_from_string(pseudo_class_name); pseudo_class.has_value()) { + pseudo_classes.append(*pseudo_class); + } else { + dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: unrecognized pseudo-class `:{}`", pseudo_class_name); + return ParseError::SyntaxError; + } + } + + if (!maybe_ident.has_value() && pseudo_classes.is_empty()) { + // Nothing parsed + dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: is empty"); + return ParseError::SyntaxError; + } + + selector_list.empend(move(maybe_ident), move(pseudo_classes)); + + tokens.discard_whitespace(); + + if (tokens.next_token().is(Token::Type::Comma)) { + tokens.discard_a_token(); // , + tokens.discard_whitespace(); + if (!tokens.has_next_token()) { + dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: trailing comma"); + return ParseError::SyntaxError; + } + + } else if (tokens.has_next_token()) { + dbgln_if(CSS_PARSER_DEBUG, "Invalid @page selector: trailing token `{}`", tokens.next_token().to_debug_string()); + return ParseError::SyntaxError; + } + } + + return selector_list; +} +template Parser::ParseErrorOr Parser::parse_a_page_selector_list(TokenStream&); +template Parser::ParseErrorOr Parser::parse_a_page_selector_list(TokenStream&); + }