mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 04:09:13 +00:00
LibWeb/CSS: Make @page
selector parsing accessible
Mostly minor changes - the code has moved, and has to support Token or ComponentValue TokenStreams, but otherwise it's the same.
This commit is contained in:
parent
107b47f884
commit
d852ae17e8
Notes:
github-actions[bot]
2025-05-16 15:45:11 +00:00
Author: https://github.com/AtkinsSJ
Commit: d852ae17e8
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4771
4 changed files with 87 additions and 64 deletions
|
@ -106,6 +106,11 @@ Optional<CSS::SelectorList> parse_selector_for_nested_style_rule(CSS::Parser::Pa
|
||||||
return adapt_nested_relative_selector_list(*maybe_selectors);
|
return adapt_nested_relative_selector_list(*maybe_selectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<CSS::PageSelectorList> 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<CSS::Selector::PseudoElementSelector> parse_pseudo_element_selector(CSS::Parser::ParsingParams const& context, StringView selector_text)
|
Optional<CSS::Selector::PseudoElementSelector> 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();
|
return CSS::Parser::Parser::create(context, selector_text).parse_as_pseudo_element_selector();
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <LibWeb/CSS/Descriptor.h>
|
#include <LibWeb/CSS/Descriptor.h>
|
||||||
#include <LibWeb/CSS/DescriptorID.h>
|
#include <LibWeb/CSS/DescriptorID.h>
|
||||||
#include <LibWeb/CSS/MediaQuery.h>
|
#include <LibWeb/CSS/MediaQuery.h>
|
||||||
|
#include <LibWeb/CSS/PageSelector.h>
|
||||||
#include <LibWeb/CSS/ParsedFontFace.h>
|
#include <LibWeb/CSS/ParsedFontFace.h>
|
||||||
#include <LibWeb/CSS/Parser/ComponentValue.h>
|
#include <LibWeb/CSS/Parser/ComponentValue.h>
|
||||||
#include <LibWeb/CSS/Parser/Dimension.h>
|
#include <LibWeb/CSS/Parser/Dimension.h>
|
||||||
|
@ -117,6 +118,8 @@ public:
|
||||||
|
|
||||||
Optional<Selector::PseudoElementSelector> parse_as_pseudo_element_selector();
|
Optional<Selector::PseudoElementSelector> parse_as_pseudo_element_selector();
|
||||||
|
|
||||||
|
Optional<PageSelectorList> parse_as_page_selector_list();
|
||||||
|
|
||||||
Vector<NonnullRefPtr<MediaQuery>> parse_as_media_query_list();
|
Vector<NonnullRefPtr<MediaQuery>> parse_as_media_query_list();
|
||||||
RefPtr<MediaQuery> parse_as_media_query();
|
RefPtr<MediaQuery> parse_as_media_query();
|
||||||
|
|
||||||
|
@ -188,6 +191,9 @@ private:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ParseErrorOr<SelectorList> parse_a_selector_list(TokenStream<T>&, SelectorType, SelectorParsingMode = SelectorParsingMode::Standard);
|
ParseErrorOr<SelectorList> parse_a_selector_list(TokenStream<T>&, SelectorType, SelectorParsingMode = SelectorParsingMode::Standard);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
ParseErrorOr<PageSelectorList> parse_a_page_selector_list(TokenStream<T>&);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Vector<NonnullRefPtr<MediaQuery>> parse_a_media_query_list(TokenStream<T>&);
|
Vector<NonnullRefPtr<MediaQuery>> parse_a_media_query_list(TokenStream<T>&);
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -523,6 +529,7 @@ RefPtr<CSS::CSSStyleValue const> parse_css_value(CSS::Parser::ParsingParams cons
|
||||||
RefPtr<CSS::CSSStyleValue const> parse_css_descriptor(CSS::Parser::ParsingParams const&, CSS::AtRuleID, CSS::DescriptorID, StringView);
|
RefPtr<CSS::CSSStyleValue const> parse_css_descriptor(CSS::Parser::ParsingParams const&, CSS::AtRuleID, CSS::DescriptorID, StringView);
|
||||||
Optional<CSS::SelectorList> parse_selector(CSS::Parser::ParsingParams const&, StringView);
|
Optional<CSS::SelectorList> parse_selector(CSS::Parser::ParsingParams const&, StringView);
|
||||||
Optional<CSS::SelectorList> parse_selector_for_nested_style_rule(CSS::Parser::ParsingParams const&, StringView);
|
Optional<CSS::SelectorList> parse_selector_for_nested_style_rule(CSS::Parser::ParsingParams const&, StringView);
|
||||||
|
Optional<CSS::PageSelectorList> parse_page_selector_list(CSS::Parser::ParsingParams const&, StringView);
|
||||||
Optional<CSS::Selector::PseudoElementSelector> parse_pseudo_element_selector(CSS::Parser::ParsingParams const&, StringView);
|
Optional<CSS::Selector::PseudoElementSelector> parse_pseudo_element_selector(CSS::Parser::ParsingParams const&, StringView);
|
||||||
CSS::CSSRule* parse_css_rule(CSS::Parser::ParsingParams const&, StringView);
|
CSS::CSSRule* parse_css_rule(CSS::Parser::ParsingParams const&, StringView);
|
||||||
RefPtr<CSS::MediaQuery> parse_media_query(CSS::Parser::ParsingParams const&, StringView);
|
RefPtr<CSS::MediaQuery> parse_media_query(CSS::Parser::ParsingParams const&, StringView);
|
||||||
|
|
|
@ -639,74 +639,13 @@ GC::Ptr<CSSFontFaceRule> Parser::convert_to_font_face_rule(AtRule const& rule)
|
||||||
return CSSFontFaceRule::create(realm(), CSSFontFaceDescriptors::create(realm(), descriptors.release_descriptors()));
|
return CSSFontFaceRule::create(realm(), CSSFontFaceDescriptors::create(realm(), descriptors.release_descriptors()));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Optional<PageSelectorList> parse_page_selector_list(Vector<ComponentValue> const& component_values)
|
|
||||||
{
|
|
||||||
// https://drafts.csswg.org/css-page-3/#syntax-page-selector
|
|
||||||
// <page-selector-list> = <page-selector>#
|
|
||||||
// <page-selector> = [ <ident-token>? <pseudo-page>* ]!
|
|
||||||
// <pseudo-page> = : [ left | right | first | blank ]
|
|
||||||
|
|
||||||
PageSelectorList selector_list;
|
|
||||||
|
|
||||||
TokenStream tokens { component_values };
|
|
||||||
tokens.discard_whitespace();
|
|
||||||
|
|
||||||
while (tokens.has_next_token()) {
|
|
||||||
// First optional ident
|
|
||||||
Optional<FlyString> 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<PagePseudoClass> 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<CSSPageRule> Parser::convert_to_page_rule(AtRule const& page_rule)
|
GC::Ptr<CSSPageRule> Parser::convert_to_page_rule(AtRule const& page_rule)
|
||||||
{
|
{
|
||||||
// https://drafts.csswg.org/css-page-3/#syntax-page-selector
|
// https://drafts.csswg.org/css-page-3/#syntax-page-selector
|
||||||
// @page = @page <page-selector-list>? { <declaration-rule-list> }
|
// @page = @page <page-selector-list>? { <declaration-rule-list> }
|
||||||
auto page_selectors = parse_page_selector_list(page_rule.prelude);
|
TokenStream tokens { page_rule.prelude };
|
||||||
if (!page_selectors.has_value())
|
auto page_selectors = parse_a_page_selector_list(tokens);
|
||||||
|
if (page_selectors.is_error())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
GC::RootVector<GC::Ref<CSSRule>> child_rules { realm().heap() };
|
GC::RootVector<GC::Ref<CSSRule>> child_rules { realm().heap() };
|
||||||
|
|
|
@ -1147,4 +1147,76 @@ Optional<Selector::SimpleSelector::ANPlusBPattern> Parser::parse_a_n_plus_b_patt
|
||||||
return syntax_error();
|
return syntax_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<PageSelectorList> 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<typename T>
|
||||||
|
Parser::ParseErrorOr<PageSelectorList> Parser::parse_a_page_selector_list(TokenStream<T>& tokens)
|
||||||
|
{
|
||||||
|
// https://drafts.csswg.org/css-page-3/#syntax-page-selector
|
||||||
|
// <page-selector-list> = <page-selector>#
|
||||||
|
// <page-selector> = [ <ident-token>? <pseudo-page>* ]!
|
||||||
|
// <pseudo-page> = : [ left | right | first | blank ]
|
||||||
|
|
||||||
|
PageSelectorList selector_list;
|
||||||
|
|
||||||
|
tokens.discard_whitespace();
|
||||||
|
|
||||||
|
while (tokens.has_next_token()) {
|
||||||
|
// First optional ident
|
||||||
|
Optional<FlyString> maybe_ident;
|
||||||
|
if (tokens.next_token().is(Token::Type::Ident))
|
||||||
|
maybe_ident = static_cast<Token>(tokens.consume_a_token()).ident();
|
||||||
|
|
||||||
|
// Then an optional series of pseudo-classes
|
||||||
|
Vector<PagePseudoClass> 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<Token>(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<PageSelectorList> Parser::parse_a_page_selector_list(TokenStream<ComponentValue>&);
|
||||||
|
template Parser::ParseErrorOr<PageSelectorList> Parser::parse_a_page_selector_list(TokenStream<Token>&);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue