2021-03-22 17:41:47 +01:00
/*
2021-07-08 13:44:01 +01:00
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
2021-04-28 22:46:44 +02:00
* Copyright ( c ) 2020 - 2021 , the SerenityOS developers .
2021-06-29 17:03:25 +01:00
* Copyright ( c ) 2021 , Sam Atkins < atkinssj @ gmail . com >
2021-07-28 16:30:59 +01:00
* Copyright ( c ) 2021 , Tobias Christiansen < tobi @ tobyase . de >
2021-03-22 17:41:47 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2021-03-22 17:41:47 +01:00
*/
2021-07-24 21:22:44 +01:00
# include <AK/CharacterTypes.h>
2021-07-23 13:06:31 +01:00
# include <AK/Debug.h>
2021-07-01 16:49:33 +01:00
# include <AK/NonnullRefPtrVector.h>
2021-04-24 20:20:14 -07:00
# include <AK/SourceLocation.h>
2021-07-09 16:34:29 +01:00
# include <LibWeb/CSS/CSSStyleDeclaration.h>
# include <LibWeb/CSS/CSSStyleRule.h>
# include <LibWeb/CSS/CSSStyleSheet.h>
2021-03-22 17:41:47 +01:00
# include <LibWeb/CSS/Parser/DeclarationOrAtRule.h>
# include <LibWeb/CSS/Parser/Parser.h>
# include <LibWeb/CSS/Parser/StyleBlockRule.h>
# include <LibWeb/CSS/Parser/StyleComponentValueRule.h>
# include <LibWeb/CSS/Parser/StyleFunctionRule.h>
2021-07-03 13:36:33 +01:00
# include <LibWeb/CSS/Parser/StyleRule.h>
2021-03-22 17:41:47 +01:00
# include <LibWeb/CSS/Selector.h>
2021-07-02 20:25:13 +01:00
# include <LibWeb/DOM/Document.h>
2021-04-29 21:16:28 +02:00
# include <LibWeb/Dump.h>
2021-03-22 17:41:47 +01:00
2021-04-24 20:20:14 -07:00
static void log_parse_error ( const SourceLocation & location = SourceLocation : : current ( ) )
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parse error (CSS) {} " , location ) ;
2021-04-24 20:20:14 -07:00
}
2021-03-22 17:41:47 +01:00
namespace Web : : CSS {
2021-07-02 20:25:13 +01:00
ParsingContext : : ParsingContext ( )
{
}
2021-07-22 13:00:29 +01:00
ParsingContext : : ParsingContext ( DOM : : Document & document )
2021-07-02 20:25:13 +01:00
: m_document ( & document )
{
}
2021-07-22 13:00:29 +01:00
ParsingContext : : ParsingContext ( DOM : : ParentNode & parent_node )
2021-07-02 20:25:13 +01:00
: m_document ( & parent_node . document ( ) )
{
}
bool ParsingContext : : in_quirks_mode ( ) const
{
return m_document ? m_document - > in_quirks_mode ( ) : false ;
}
URL ParsingContext : : complete_url ( String const & addr ) const
{
return m_document ? m_document - > url ( ) . complete_url ( addr ) : URL : : create_with_url_or_path ( addr ) ;
}
2021-07-03 14:00:41 +01:00
template < typename T >
TokenStream < T > : : TokenStream ( Vector < T > const & tokens )
: m_tokens ( tokens )
, m_eof ( make_eof ( ) )
2021-03-22 17:41:47 +01:00
{
}
2021-07-03 14:00:41 +01:00
template < typename T >
TokenStream < T > : : ~ TokenStream ( )
2021-03-22 17:41:47 +01:00
{
}
2021-07-03 14:00:41 +01:00
template < typename T >
bool TokenStream < T > : : has_next_token ( )
2021-03-22 17:41:47 +01:00
{
2021-07-03 14:00:41 +01:00
return ( size_t ) ( m_iterator_offset + 1 ) < m_tokens . size ( ) ;
}
2021-03-22 17:41:47 +01:00
2021-07-03 14:00:41 +01:00
template < typename T >
2021-07-28 16:27:45 +01:00
T const & TokenStream < T > : : peek_token ( int offset )
2021-07-03 14:00:41 +01:00
{
if ( ! has_next_token ( ) )
return m_eof ;
2021-03-22 17:41:47 +01:00
2021-07-28 16:27:45 +01:00
return m_tokens . at ( m_iterator_offset + offset + 1 ) ;
2021-03-22 17:41:47 +01:00
}
2021-07-03 14:00:41 +01:00
template < typename T >
T const & TokenStream < T > : : next_token ( )
2021-03-22 17:41:47 +01:00
{
2021-07-03 14:00:41 +01:00
if ( ! has_next_token ( ) )
return m_eof ;
2021-03-22 17:41:47 +01:00
2021-07-03 14:00:41 +01:00
+ + m_iterator_offset ;
2021-03-22 17:41:47 +01:00
2021-07-03 14:00:41 +01:00
return m_tokens . at ( m_iterator_offset ) ;
2021-03-22 17:41:47 +01:00
}
2021-07-03 14:00:41 +01:00
template < typename T >
T const & TokenStream < T > : : current_token ( )
2021-03-22 17:41:47 +01:00
{
2021-07-03 14:00:41 +01:00
if ( ( size_t ) m_iterator_offset > = m_tokens . size ( ) )
return m_eof ;
2021-03-22 17:41:47 +01:00
return m_tokens . at ( m_iterator_offset ) ;
}
2021-07-03 14:00:41 +01:00
template < typename T >
void TokenStream < T > : : reconsume_current_input_token ( )
{
VERIFY ( m_iterator_offset > = 0 ) ;
- - m_iterator_offset ;
}
template < typename T >
void TokenStream < T > : : skip_whitespace ( )
{
while ( peek_token ( ) . is ( Token : : Type : : Whitespace ) )
next_token ( ) ;
}
template < >
Token TokenStream < Token > : : make_eof ( )
{
return Tokenizer : : create_eof_token ( ) ;
}
template < >
StyleComponentValueRule TokenStream < StyleComponentValueRule > : : make_eof ( )
{
return StyleComponentValueRule ( Tokenizer : : create_eof_token ( ) ) ;
}
template < typename T >
void TokenStream < T > : : dump_all_tokens ( )
{
dbgln ( " Dumping all tokens: " ) ;
2021-07-22 17:51:07 +01:00
for ( size_t i = 0 ; i < m_tokens . size ( ) ; + + i ) {
auto & token = m_tokens [ i ] ;
if ( ( i - 1 ) = = ( size_t ) m_iterator_offset )
dbgln ( " -> {} " , token . to_debug_string ( ) ) ;
else
dbgln ( " {} " , token . to_debug_string ( ) ) ;
}
2021-07-03 14:00:41 +01:00
}
Parser : : Parser ( ParsingContext const & context , StringView const & input , String const & encoding )
: m_context ( context )
, m_tokenizer ( input , encoding )
, m_tokens ( m_tokenizer . parse ( ) )
, m_token_stream ( TokenStream ( m_tokens ) )
{
}
Parser : : ~ Parser ( )
{
}
2021-07-09 16:34:29 +01:00
NonnullRefPtr < CSSStyleSheet > Parser : : parse_as_stylesheet ( )
2021-03-22 17:41:47 +01:00
{
2021-07-03 15:40:06 +01:00
return parse_as_stylesheet ( m_token_stream ) ;
}
template < typename T >
NonnullRefPtr < CSSStyleSheet > Parser : : parse_as_stylesheet ( TokenStream < T > & tokens )
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_as_stylesheet " ) ;
2021-07-09 17:07:24 +01:00
2021-07-03 15:40:06 +01:00
auto parser_rules = consume_a_list_of_rules ( tokens , true ) ;
2021-07-09 16:34:29 +01:00
NonnullRefPtrVector < CSSRule > rules ;
2021-03-22 17:41:47 +01:00
2021-07-03 12:34:25 +01:00
for ( auto & raw_rule : parser_rules ) {
2021-07-08 21:53:22 +01:00
auto rule = convert_to_rule ( raw_rule ) ;
2021-07-03 12:34:25 +01:00
if ( rule )
rules . append ( * rule ) ;
}
2021-03-22 17:41:47 +01:00
2021-07-08 21:53:22 +01:00
auto stylesheet = CSSStyleSheet : : create ( rules ) ;
2021-07-30 15:19:17 +01:00
if constexpr ( CSS_PARSER_DEBUG ) {
dump_sheet ( stylesheet ) ;
}
2021-07-08 21:53:22 +01:00
return stylesheet ;
2021-07-03 12:34:25 +01:00
}
2021-03-22 17:41:47 +01:00
2021-07-23 16:13:07 +01:00
Optional < SelectorList > Parser : : parse_a_selector ( )
2021-07-03 12:34:25 +01:00
{
2021-07-03 15:40:06 +01:00
return parse_a_selector ( m_token_stream ) ;
2021-07-03 12:34:25 +01:00
}
2021-07-03 15:40:06 +01:00
template < typename T >
2021-07-23 16:13:07 +01:00
Optional < SelectorList > Parser : : parse_a_selector ( TokenStream < T > & tokens )
2021-07-03 12:34:25 +01:00
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_a_selector " ) ;
2021-07-09 17:07:24 +01:00
2021-07-23 16:13:07 +01:00
auto selector_list = parse_a_selector_list ( tokens ) ;
if ( selector_list . has_value ( ) )
return selector_list ;
return { } ;
}
Optional < SelectorList > Parser : : parse_a_relative_selector ( )
{
return parse_a_relative_selector ( m_token_stream ) ;
}
template < typename T >
Optional < SelectorList > Parser : : parse_a_relative_selector ( TokenStream < T > & tokens )
{
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_a_relative_selector " ) ;
auto selector_list = parse_a_relative_selector_list ( tokens ) ;
if ( selector_list . has_value ( ) )
return selector_list ;
return { } ;
}
template < typename T >
Optional < SelectorList > Parser : : parse_a_selector_list ( TokenStream < T > & tokens )
{
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_a_selector_list " ) ;
2021-07-03 15:40:06 +01:00
auto comma_separated_lists = parse_as_comma_separated_list_of_component_values ( tokens ) ;
2021-07-03 12:34:25 +01:00
2021-07-23 16:13:07 +01:00
NonnullRefPtrVector < Selector > selectors ;
2021-07-03 15:40:06 +01:00
for ( auto & selector_parts : comma_separated_lists ) {
auto stream = TokenStream ( selector_parts ) ;
2021-07-23 16:13:07 +01:00
auto selector = parse_complex_selector ( stream , false ) ;
2021-07-12 17:30:40 +01:00
if ( selector )
selectors . append ( selector . release_nonnull ( ) ) ;
2021-07-23 16:13:07 +01:00
else
return { } ;
2021-03-22 17:41:47 +01:00
}
2021-07-23 16:13:07 +01:00
if ( selectors . is_empty ( ) )
return { } ;
2021-03-22 17:41:47 +01:00
2021-07-23 16:13:07 +01:00
return selectors ;
2021-07-03 15:40:06 +01:00
}
template < typename T >
2021-07-23 16:13:07 +01:00
Optional < SelectorList > Parser : : parse_a_relative_selector_list ( TokenStream < T > & tokens )
2021-07-03 15:40:06 +01:00
{
2021-07-23 16:13:07 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_a_relative_selector_list " ) ;
2021-07-09 17:07:24 +01:00
2021-07-03 15:40:06 +01:00
auto comma_separated_lists = parse_as_comma_separated_list_of_component_values ( tokens ) ;
2021-07-03 12:34:25 +01:00
2021-07-12 17:30:40 +01:00
NonnullRefPtrVector < Selector > selectors ;
2021-07-03 12:34:25 +01:00
for ( auto & selector_parts : comma_separated_lists ) {
2021-07-03 15:40:06 +01:00
auto stream = TokenStream ( selector_parts ) ;
2021-07-23 16:13:07 +01:00
auto selector = parse_complex_selector ( stream , true ) ;
2021-07-12 17:30:40 +01:00
if ( selector )
selectors . append ( selector . release_nonnull ( ) ) ;
2021-07-23 16:13:07 +01:00
else
return { } ;
2021-07-03 12:34:25 +01:00
}
2021-07-23 16:13:07 +01:00
if ( selectors . is_empty ( ) )
return { } ;
2021-07-03 12:34:25 +01:00
return selectors ;
}
2021-07-23 16:13:07 +01:00
RefPtr < Selector > Parser : : parse_complex_selector ( TokenStream < StyleComponentValueRule > & tokens , bool allow_starting_combinator )
2021-07-03 12:34:25 +01:00
{
2021-07-23 16:13:07 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_complex_selector " ) ;
2021-07-03 12:34:25 +01:00
2021-07-23 16:13:07 +01:00
Vector < Selector : : CompoundSelector > compound_selectors ;
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
auto first_selector = parse_compound_selector ( tokens ) ;
if ( first_selector . is_error ( ) )
return { } ;
if ( ! allow_starting_combinator ) {
if ( first_selector . value ( ) . combinator ! = Selector : : Combinator : : Descendant )
return { } ;
first_selector . value ( ) . combinator = Selector : : Combinator : : None ;
}
compound_selectors . append ( first_selector . value ( ) ) ;
2021-07-07 17:47:17 +01:00
2021-07-23 16:13:07 +01:00
while ( tokens . has_next_token ( ) ) {
auto compound_selector = parse_compound_selector ( tokens ) ;
if ( compound_selector . is_error ( ) ) {
if ( compound_selector . error ( ) = = SelectorParsingResult : : Done )
break ;
else
return { } ;
2021-07-07 17:47:17 +01:00
}
2021-07-23 16:13:07 +01:00
compound_selectors . append ( compound_selector . value ( ) ) ;
}
2021-07-07 17:47:17 +01:00
2021-07-23 16:13:07 +01:00
if ( compound_selectors . is_empty ( ) )
return { } ;
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
return Selector : : create ( move ( compound_selectors ) ) ;
}
2021-06-30 16:27:37 +01:00
2021-07-23 16:13:07 +01:00
Result < Selector : : CompoundSelector , Parser : : SelectorParsingResult > Parser : : parse_compound_selector ( TokenStream < StyleComponentValueRule > & tokens )
{
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_compound_selector " ) ;
2021-06-30 16:27:37 +01:00
2021-07-23 16:13:07 +01:00
tokens . skip_whitespace ( ) ;
2021-06-30 16:27:37 +01:00
2021-07-23 16:13:07 +01:00
auto combinator = parse_selector_combinator ( tokens ) . value_or ( Selector : : Combinator : : Descendant ) ;
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
tokens . skip_whitespace ( ) ;
2021-06-30 16:27:37 +01:00
2021-07-23 16:13:07 +01:00
Vector < Selector : : SimpleSelector > simple_selectors ;
2021-07-01 14:54:27 +01:00
2021-07-23 16:13:07 +01:00
while ( tokens . has_next_token ( ) ) {
auto component = parse_simple_selector ( tokens ) ;
if ( component . is_error ( ) ) {
if ( component . error ( ) = = SelectorParsingResult : : Done )
break ;
else
return component . error ( ) ;
}
2021-07-03 15:40:06 +01:00
2021-07-23 16:13:07 +01:00
simple_selectors . append ( component . value ( ) ) ;
}
if ( simple_selectors . is_empty ( ) )
return SelectorParsingResult : : Done ;
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
return Selector : : CompoundSelector { combinator , move ( simple_selectors ) } ;
}
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
Optional < Selector : : Combinator > Parser : : parse_selector_combinator ( TokenStream < StyleComponentValueRule > & tokens )
{
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_selector_combinator " ) ;
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
auto & current_value = tokens . next_token ( ) ;
if ( current_value . is ( Token : : Type : : Delim ) ) {
auto delim = current_value . token ( ) . delim ( ) ;
if ( delim = = " > " sv ) {
return Selector : : Combinator : : ImmediateChild ;
} else if ( delim = = " + " sv ) {
return Selector : : Combinator : : NextSibling ;
} else if ( delim = = " ~ " sv ) {
return Selector : : Combinator : : SubsequentSibling ;
} else if ( delim = = " | " sv ) {
auto & next = tokens . peek_token ( ) ;
if ( next . is ( Token : : Type : : EndOfFile ) )
2021-07-01 14:54:27 +01:00
return { } ;
2021-07-23 16:13:07 +01:00
if ( next . is ( Token : : Type : : Delim ) & & next . token ( ) . delim ( ) = = " | " sv ) {
tokens . next_token ( ) ;
return Selector : : Combinator : : Column ;
2021-07-01 14:54:27 +01:00
}
2021-07-23 16:13:07 +01:00
}
}
2021-07-01 14:54:27 +01:00
2021-07-23 16:13:07 +01:00
tokens . reconsume_current_input_token ( ) ;
return { } ;
}
2021-07-03 15:40:06 +01:00
2021-07-23 16:13:07 +01:00
Result < Selector : : SimpleSelector , Parser : : SelectorParsingResult > Parser : : parse_simple_selector ( TokenStream < StyleComponentValueRule > & tokens )
{
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_simple_selector " ) ;
2021-07-01 14:54:27 +01:00
2021-07-23 16:13:07 +01:00
auto peek_token_ends_selector = [ & ] ( ) - > bool {
auto & value = tokens . peek_token ( ) ;
return ( value . is ( Token : : Type : : EndOfFile ) | | value . is ( Token : : Type : : Whitespace ) | | value . is ( Token : : Type : : Comma ) ) ;
} ;
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
if ( peek_token_ends_selector ( ) )
return SelectorParsingResult : : Done ;
2021-07-03 15:40:06 +01:00
2021-07-23 16:13:07 +01:00
auto & first_value = tokens . next_token ( ) ;
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
if ( first_value . is ( Token : : Type : : Delim ) & & first_value . token ( ) . delim ( ) = = " * " sv ) {
return Selector : : SimpleSelector {
. type = Selector : : SimpleSelector : : Type : : Universal
} ;
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
} else if ( first_value . is ( Token : : Type : : Hash ) ) {
if ( first_value . token ( ) . hash_type ( ) ! = Token : : HashType : : Id ) {
dbgln_if ( CSS_PARSER_DEBUG , " Selector contains hash token that is not an id: {} " , first_value . to_debug_string ( ) ) ;
return SelectorParsingResult : : SyntaxError ;
}
return Selector : : SimpleSelector {
. type = Selector : : SimpleSelector : : Type : : Id ,
. value = first_value . token ( ) . hash_value ( )
} ;
} else if ( first_value . is ( Token : : Type : : Delim ) & & first_value . token ( ) . delim ( ) = = " . " sv ) {
if ( peek_token_ends_selector ( ) )
return SelectorParsingResult : : SyntaxError ;
auto & class_name_value = tokens . next_token ( ) ;
if ( ! class_name_value . is ( Token : : Type : : Ident ) ) {
dbgln_if ( CSS_PARSER_DEBUG , " Expected an ident after '.', got: {} " , class_name_value . to_debug_string ( ) ) ;
return SelectorParsingResult : : SyntaxError ;
}
return Selector : : SimpleSelector {
. type = Selector : : SimpleSelector : : Type : : Class ,
. value = class_name_value . token ( ) . ident ( )
} ;
} else if ( first_value . is ( Token : : Type : : Ident ) ) {
return Selector : : SimpleSelector {
. type = Selector : : SimpleSelector : : Type : : TagName ,
. value = first_value . token ( ) . ident ( )
} ;
} else if ( first_value . is_block ( ) & & first_value . block ( ) . is_square ( ) ) {
auto & attribute_parts = first_value . block ( ) . values ( ) ;
if ( attribute_parts . is_empty ( ) ) {
dbgln_if ( CSS_PARSER_DEBUG , " CSS attribute selector is empty! " ) ;
return SelectorParsingResult : : SyntaxError ;
}
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
// FIXME: Handle namespace prefix for attribute name.
auto & attribute_part = attribute_parts . first ( ) ;
if ( ! attribute_part . is ( Token : : Type : : Ident ) ) {
dbgln_if ( CSS_PARSER_DEBUG , " Expected ident for attribute name, got: '{}' " , attribute_part . to_debug_string ( ) ) ;
return SelectorParsingResult : : SyntaxError ;
}
Selector : : SimpleSelector simple_selector {
. type = Selector : : SimpleSelector : : Type : : Attribute ,
. attribute = {
. match_type = Selector : : SimpleSelector : : Attribute : : MatchType : : HasAttribute ,
2021-07-28 12:34:05 +01:00
// FIXME: Case-sensitivity is defined by the document language.
// HTML is insensitive with attribute names, and our code generally assumes
// they are converted to lowercase, so we do that here too. If we want to be
// correct with XML later, we'll need to keep the original case and then do
// a case-insensitive compare later.
. name = attribute_part . token ( ) . ident ( ) . to_lowercase_string ( ) ,
2021-04-29 21:16:28 +02:00
}
2021-07-23 16:13:07 +01:00
} ;
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
if ( attribute_parts . size ( ) = = 1 )
return simple_selector ;
2021-07-12 16:34:18 +01:00
2021-07-23 16:13:07 +01:00
size_t attribute_index = 1 ;
auto & delim_part = attribute_parts . at ( attribute_index ) ;
if ( ! delim_part . is ( Token : : Type : : Delim ) ) {
dbgln_if ( CSS_PARSER_DEBUG , " Expected a delim for attribute comparison, got: '{}' " , delim_part . to_debug_string ( ) ) ;
return SelectorParsingResult : : SyntaxError ;
}
2021-07-12 16:34:18 +01:00
2021-07-23 16:13:07 +01:00
if ( delim_part . token ( ) . delim ( ) = = " = " sv ) {
simple_selector . attribute . match_type = Selector : : SimpleSelector : : Attribute : : MatchType : : ExactValueMatch ;
attribute_index + + ;
} else {
attribute_index + + ;
if ( attribute_index > = attribute_parts . size ( ) ) {
dbgln_if ( CSS_PARSER_DEBUG , " Attribute selector ended part way through a match type. " ) ;
return SelectorParsingResult : : SyntaxError ;
2021-07-12 16:34:18 +01:00
}
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
auto & delim_second_part = attribute_parts . at ( attribute_index ) ;
if ( ! ( delim_second_part . is ( Token : : Type : : Delim ) & & delim_second_part . token ( ) . delim ( ) = = " = " ) ) {
dbgln_if ( CSS_PARSER_DEBUG , " Expected a double delim for attribute comparison, got: '{}{}' " , delim_part . to_debug_string ( ) , delim_second_part . to_debug_string ( ) ) ;
return SelectorParsingResult : : SyntaxError ;
}
2021-06-30 16:27:37 +01:00
2021-07-23 16:13:07 +01:00
if ( delim_part . token ( ) . delim ( ) = = " ~ " sv ) {
simple_selector . attribute . match_type = Selector : : SimpleSelector : : Attribute : : MatchType : : ContainsWord ;
attribute_index + + ;
} else if ( delim_part . token ( ) . delim ( ) = = " * " sv ) {
simple_selector . attribute . match_type = Selector : : SimpleSelector : : Attribute : : MatchType : : ContainsString ;
attribute_index + + ;
} else if ( delim_part . token ( ) . delim ( ) = = " | " sv ) {
simple_selector . attribute . match_type = Selector : : SimpleSelector : : Attribute : : MatchType : : StartsWithSegment ;
attribute_index + + ;
} else if ( delim_part . token ( ) . delim ( ) = = " ^ " sv ) {
simple_selector . attribute . match_type = Selector : : SimpleSelector : : Attribute : : MatchType : : StartsWithString ;
attribute_index + + ;
} else if ( delim_part . token ( ) . delim ( ) = = " $ " sv ) {
simple_selector . attribute . match_type = Selector : : SimpleSelector : : Attribute : : MatchType : : EndsWithString ;
attribute_index + + ;
2021-04-29 21:16:28 +02:00
}
}
2021-07-23 16:13:07 +01:00
if ( attribute_index > = attribute_parts . size ( ) ) {
dbgln_if ( CSS_PARSER_DEBUG , " Attribute selector ended without a value to match. " ) ;
return SelectorParsingResult : : SyntaxError ;
}
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
auto & value_part = attribute_parts . at ( attribute_index ) ;
if ( ! value_part . is ( Token : : Type : : Ident ) & & ! value_part . is ( Token : : Type : : String ) ) {
dbgln_if ( CSS_PARSER_DEBUG , " Expected a string or ident for the value to match attribute against, got: '{}' " , value_part . to_debug_string ( ) ) ;
return SelectorParsingResult : : SyntaxError ;
}
simple_selector . attribute . value = value_part . token ( ) . is ( Token : : Type : : Ident ) ? value_part . token ( ) . ident ( ) : value_part . token ( ) . string ( ) ;
2021-07-07 17:47:17 +01:00
2021-07-23 16:13:07 +01:00
// FIXME: Handle case-sensitivity suffixes. https://www.w3.org/TR/selectors-4/#attribute-case
return simple_selector ;
2021-07-03 15:40:06 +01:00
2021-07-23 16:13:07 +01:00
} else if ( first_value . is ( Token : : Type : : Colon ) ) {
if ( peek_token_ends_selector ( ) )
return SelectorParsingResult : : SyntaxError ;
2021-07-02 13:01:38 +01:00
2021-07-23 16:13:07 +01:00
bool is_pseudo = false ;
if ( tokens . peek_token ( ) . is ( Token : : Type : : Colon ) ) {
is_pseudo = true ;
tokens . next_token ( ) ;
if ( peek_token_ends_selector ( ) )
return SelectorParsingResult : : SyntaxError ;
2021-04-29 21:16:28 +02:00
}
2021-07-23 16:13:07 +01:00
if ( is_pseudo ) {
Selector : : SimpleSelector simple_selector {
. type = Selector : : SimpleSelector : : Type : : PseudoElement
} ;
2021-07-07 17:47:17 +01:00
2021-07-23 16:13:07 +01:00
auto & 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 ( ) ) ;
return SelectorParsingResult : : SyntaxError ;
}
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
auto pseudo_name = name_token . token ( ) . ident ( ) ;
2021-07-07 17:47:17 +01:00
2021-07-23 16:13:07 +01:00
if ( pseudo_name . equals_ignoring_case ( " after " ) ) {
simple_selector . pseudo_element = Selector : : SimpleSelector : : PseudoElement : : After ;
} else if ( pseudo_name . equals_ignoring_case ( " before " ) ) {
simple_selector . pseudo_element = Selector : : SimpleSelector : : PseudoElement : : Before ;
} else if ( pseudo_name . equals_ignoring_case ( " first-letter " ) ) {
simple_selector . pseudo_element = Selector : : SimpleSelector : : PseudoElement : : FirstLetter ;
} else if ( pseudo_name . equals_ignoring_case ( " first-line " ) ) {
simple_selector . pseudo_element = Selector : : SimpleSelector : : PseudoElement : : FirstLine ;
} else {
dbgln_if ( CSS_PARSER_DEBUG , " Unrecognized pseudo-element: '{}' " , pseudo_name ) ;
return SelectorParsingResult : : SyntaxError ;
}
2021-06-30 16:27:37 +01:00
2021-07-23 16:13:07 +01:00
return simple_selector ;
2021-04-29 21:16:28 +02:00
}
2021-07-23 16:13:07 +01:00
if ( peek_token_ends_selector ( ) )
return SelectorParsingResult : : SyntaxError ;
auto & pseudo_class_token = tokens . next_token ( ) ;
Selector : : SimpleSelector simple_selector {
. type = Selector : : SimpleSelector : : Type : : PseudoClass
} ;
if ( pseudo_class_token . is ( Token : : Type : : Ident ) ) {
auto pseudo_name = pseudo_class_token . token ( ) . ident ( ) ;
if ( pseudo_name . equals_ignoring_case ( " active " ) ) {
simple_selector . pseudo_class . type = Selector : : SimpleSelector : : PseudoClass : : Type : : Active ;
} else if ( pseudo_name . equals_ignoring_case ( " checked " ) ) {
simple_selector . pseudo_class . type = Selector : : SimpleSelector : : PseudoClass : : Type : : Checked ;
} else if ( pseudo_name . equals_ignoring_case ( " disabled " ) ) {
simple_selector . pseudo_class . type = Selector : : SimpleSelector : : PseudoClass : : Type : : Disabled ;
} else if ( pseudo_name . equals_ignoring_case ( " empty " ) ) {
simple_selector . pseudo_class . type = Selector : : SimpleSelector : : PseudoClass : : Type : : Empty ;
} else if ( pseudo_name . equals_ignoring_case ( " enabled " ) ) {
simple_selector . pseudo_class . type = Selector : : SimpleSelector : : PseudoClass : : Type : : Enabled ;
} else if ( pseudo_name . equals_ignoring_case ( " first-child " ) ) {
simple_selector . pseudo_class . type = 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 ;
} else if ( pseudo_name . equals_ignoring_case ( " focus " ) ) {
simple_selector . pseudo_class . type = Selector : : SimpleSelector : : PseudoClass : : Type : : Focus ;
} else if ( pseudo_name . equals_ignoring_case ( " hover " ) ) {
simple_selector . pseudo_class . type = Selector : : SimpleSelector : : PseudoClass : : Type : : Hover ;
} else if ( pseudo_name . equals_ignoring_case ( " last-child " ) ) {
simple_selector . pseudo_class . type = 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 ;
} else if ( pseudo_name . equals_ignoring_case ( " link " ) ) {
simple_selector . pseudo_class . type = Selector : : SimpleSelector : : PseudoClass : : Type : : Link ;
} else if ( pseudo_name . equals_ignoring_case ( " only-child " ) ) {
simple_selector . pseudo_class . type = Selector : : SimpleSelector : : PseudoClass : : Type : : OnlyChild ;
} else if ( pseudo_name . equals_ignoring_case ( " root " ) ) {
simple_selector . pseudo_class . type = 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 . pseudo_element = Selector : : SimpleSelector : : PseudoElement : : After ;
} else if ( pseudo_name . equals_ignoring_case ( " before " ) ) {
// See :after
simple_selector . type = Selector : : SimpleSelector : : Type : : PseudoElement ;
simple_selector . pseudo_element = Selector : : SimpleSelector : : PseudoElement : : Before ;
} else if ( pseudo_name . equals_ignoring_case ( " first-letter " ) ) {
// See :after
simple_selector . type = Selector : : SimpleSelector : : Type : : PseudoElement ;
simple_selector . pseudo_element = Selector : : SimpleSelector : : PseudoElement : : FirstLetter ;
} else if ( pseudo_name . equals_ignoring_case ( " first-line " ) ) {
// See :after
simple_selector . type = Selector : : SimpleSelector : : Type : : PseudoElement ;
simple_selector . pseudo_element = Selector : : SimpleSelector : : PseudoElement : : FirstLine ;
} else {
dbgln_if ( CSS_PARSER_DEBUG , " Unknown pseudo class: '{}' " , pseudo_name ) ;
return SelectorParsingResult : : SyntaxError ;
}
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
return simple_selector ;
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
} else if ( pseudo_class_token . is_function ( ) ) {
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
auto & 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 ( function_token_stream ) ;
if ( ! not_selector . has_value ( ) ) {
dbgln_if ( CSS_PARSER_DEBUG , " Invalid selector in :not() clause " ) ;
return SelectorParsingResult : : SyntaxError ;
}
simple_selector . pseudo_class . not_selector = not_selector . value ( ) ;
} else if ( pseudo_function . name ( ) . equals_ignoring_case ( " nth-child " ) ) {
simple_selector . pseudo_class . type = Selector : : SimpleSelector : : PseudoClass : : Type : : NthChild ;
auto function_values = TokenStream < StyleComponentValueRule > ( pseudo_function . values ( ) ) ;
auto nth_child_pattern = parse_a_n_plus_b_pattern ( function_values ) ;
if ( nth_child_pattern . has_value ( ) ) {
simple_selector . pseudo_class . nth_child_pattern = nth_child_pattern . value ( ) ;
} else {
dbgln_if ( CSS_PARSER_DEBUG , " !!! Invalid nth-child format " ) ;
return SelectorParsingResult : : SyntaxError ;
}
} else if ( pseudo_function . name ( ) . equals_ignoring_case ( " nth-last-child " ) ) {
simple_selector . pseudo_class . type = Selector : : SimpleSelector : : PseudoClass : : Type : : NthLastChild ;
auto function_values = TokenStream < StyleComponentValueRule > ( pseudo_function . values ( ) ) ;
auto nth_child_pattern = parse_a_n_plus_b_pattern ( function_values ) ;
if ( nth_child_pattern . has_value ( ) ) {
simple_selector . pseudo_class . nth_child_pattern = nth_child_pattern . value ( ) ;
} else {
dbgln_if ( CSS_PARSER_DEBUG , " !!! Invalid nth-child format " ) ;
return SelectorParsingResult : : SyntaxError ;
}
} else {
dbgln_if ( CSS_PARSER_DEBUG , " Unknown pseudo class: '{}'() " , pseudo_function . name ( ) ) ;
return SelectorParsingResult : : SyntaxError ;
}
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
return simple_selector ;
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
} else {
dbgln_if ( CSS_PARSER_DEBUG , " Unexpected Block in pseudo-class name, expected a function or identifier. '{}' " , pseudo_class_token . to_debug_string ( ) ) ;
return SelectorParsingResult : : SyntaxError ;
}
}
2021-04-29 21:16:28 +02:00
2021-07-23 16:13:07 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " !!! Invalid simple selector! " ) ;
return SelectorParsingResult : : SyntaxError ;
2021-03-22 17:41:47 +01:00
}
2021-07-03 13:36:33 +01:00
NonnullRefPtrVector < StyleRule > Parser : : consume_a_list_of_rules ( bool top_level )
2021-07-03 15:40:06 +01:00
{
return consume_a_list_of_rules ( m_token_stream , top_level ) ;
}
template < typename T >
NonnullRefPtrVector < StyleRule > Parser : : consume_a_list_of_rules ( TokenStream < T > & tokens , bool top_level )
2021-03-22 17:41:47 +01:00
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::consume_a_list_of_rules " ) ;
2021-07-09 17:07:24 +01:00
2021-07-03 13:36:33 +01:00
NonnullRefPtrVector < StyleRule > rules ;
2021-03-22 17:41:47 +01:00
for ( ; ; ) {
2021-07-22 21:47:35 +01:00
auto & token = tokens . next_token ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : Whitespace ) ) {
2021-03-22 17:41:47 +01:00
continue ;
}
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : EndOfFile ) ) {
2021-03-22 17:41:47 +01:00
break ;
}
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : CDO ) | | token . is ( Token : : Type : : CDC ) ) {
2021-03-22 17:41:47 +01:00
if ( top_level ) {
continue ;
}
2021-07-03 15:40:06 +01:00
tokens . reconsume_current_input_token ( ) ;
auto maybe_qualified = consume_a_qualified_rule ( tokens ) ;
2021-07-01 16:49:33 +01:00
if ( maybe_qualified ) {
rules . append ( maybe_qualified . release_nonnull ( ) ) ;
2021-03-22 17:41:47 +01:00
}
continue ;
}
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : AtKeyword ) ) {
2021-07-03 15:40:06 +01:00
tokens . reconsume_current_input_token ( ) ;
rules . append ( consume_an_at_rule ( tokens ) ) ;
2021-03-22 17:41:47 +01:00
continue ;
}
2021-07-03 15:40:06 +01:00
tokens . reconsume_current_input_token ( ) ;
auto maybe_qualified = consume_a_qualified_rule ( tokens ) ;
2021-07-01 16:49:33 +01:00
if ( maybe_qualified ) {
rules . append ( maybe_qualified . release_nonnull ( ) ) ;
2021-03-22 17:41:47 +01:00
}
}
return rules ;
}
2021-07-03 13:36:33 +01:00
NonnullRefPtr < StyleRule > Parser : : consume_an_at_rule ( )
2021-03-22 17:41:47 +01:00
{
2021-07-03 15:40:06 +01:00
return consume_an_at_rule ( m_token_stream ) ;
}
template < typename T >
NonnullRefPtr < StyleRule > Parser : : consume_an_at_rule ( TokenStream < T > & tokens )
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::consume_an_at_rule " ) ;
2021-07-09 17:07:24 +01:00
2021-07-22 21:47:35 +01:00
auto & name_ident = tokens . next_token ( ) ;
2021-07-22 17:51:07 +01:00
VERIFY ( name_ident . is ( Token : : Type : : AtKeyword ) ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 13:36:33 +01:00
NonnullRefPtr < StyleRule > rule = create < StyleRule > ( StyleRule : : Type : : At ) ;
2021-07-22 17:51:07 +01:00
rule - > m_name = ( ( Token ) name_ident ) . at_keyword ( ) ;
2021-03-22 17:41:47 +01:00
for ( ; ; ) {
2021-07-22 21:47:35 +01:00
auto & token = tokens . next_token ( ) ;
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : Semicolon ) ) {
2021-03-22 17:41:47 +01:00
return rule ;
}
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : EndOfFile ) ) {
2021-04-24 20:20:14 -07:00
log_parse_error ( ) ;
2021-03-22 17:41:47 +01:00
return rule ;
}
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : OpenCurly ) ) {
2021-07-03 15:40:06 +01:00
rule - > m_block = consume_a_simple_block ( tokens ) ;
2021-03-22 17:41:47 +01:00
return rule ;
}
// how is "simple block with an associated token of <{-token>" a valid token?
2021-07-03 15:40:06 +01:00
tokens . reconsume_current_input_token ( ) ;
auto value = consume_a_component_value ( tokens ) ;
2021-07-03 13:36:33 +01:00
rule - > m_prelude . append ( value ) ;
2021-03-22 17:41:47 +01:00
}
}
2021-07-03 13:36:33 +01:00
RefPtr < StyleRule > Parser : : consume_a_qualified_rule ( )
2021-07-03 15:40:06 +01:00
{
return consume_a_qualified_rule ( m_token_stream ) ;
}
template < typename T >
RefPtr < StyleRule > Parser : : consume_a_qualified_rule ( TokenStream < T > & tokens )
2021-03-22 17:41:47 +01:00
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::consume_a_qualified_rule " ) ;
2021-07-09 17:07:24 +01:00
2021-07-03 13:36:33 +01:00
NonnullRefPtr < StyleRule > rule = create < StyleRule > ( StyleRule : : Type : : Qualified ) ;
2021-03-22 17:41:47 +01:00
for ( ; ; ) {
2021-07-22 21:47:35 +01:00
auto & token = tokens . next_token ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : EndOfFile ) ) {
2021-04-24 20:20:14 -07:00
log_parse_error ( ) ;
2021-03-22 17:41:47 +01:00
return { } ;
}
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : OpenCurly ) ) {
2021-07-03 15:40:06 +01:00
rule - > m_block = consume_a_simple_block ( tokens ) ;
2021-03-22 17:41:47 +01:00
return rule ;
}
// how is "simple block with an associated token of <{-token>" a valid token?
2021-07-03 15:40:06 +01:00
tokens . reconsume_current_input_token ( ) ;
auto value = consume_a_component_value ( tokens ) ;
2021-07-01 16:49:33 +01:00
rule - > m_prelude . append ( value ) ;
2021-03-22 17:41:47 +01:00
}
return rule ;
}
2021-07-08 21:53:22 +01:00
template < >
StyleComponentValueRule Parser : : consume_a_component_value ( TokenStream < StyleComponentValueRule > & tokens )
2021-03-22 17:41:47 +01:00
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::consume_a_component_value - shortcut: '{}' " , tokens . peek_token ( ) . to_debug_string ( ) ) ;
2021-07-09 17:07:24 +01:00
2021-07-08 21:53:22 +01:00
return tokens . next_token ( ) ;
2021-07-03 15:40:06 +01:00
}
template < typename T >
StyleComponentValueRule Parser : : consume_a_component_value ( TokenStream < T > & tokens )
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::consume_a_component_value " ) ;
2021-07-09 17:07:24 +01:00
2021-07-22 21:47:35 +01:00
auto & token = tokens . next_token ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : OpenCurly ) | | token . is ( Token : : Type : : OpenSquare ) | | token . is ( Token : : Type : : OpenParen ) )
2021-07-03 15:40:06 +01:00
return StyleComponentValueRule ( consume_a_simple_block ( tokens ) ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : Function ) )
2021-07-03 15:40:06 +01:00
return StyleComponentValueRule ( consume_a_function ( tokens ) ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 14:54:19 +01:00
return StyleComponentValueRule ( token ) ;
2021-03-22 17:41:47 +01:00
}
2021-07-08 21:53:22 +01:00
StyleComponentValueRule Parser : : consume_a_component_value ( )
{
return consume_a_component_value ( m_token_stream ) ;
}
2021-07-01 14:13:48 +01:00
NonnullRefPtr < StyleBlockRule > Parser : : consume_a_simple_block ( )
2021-03-22 17:41:47 +01:00
{
2021-07-03 15:40:06 +01:00
return consume_a_simple_block ( m_token_stream ) ;
}
template < typename T >
NonnullRefPtr < StyleBlockRule > Parser : : consume_a_simple_block ( TokenStream < T > & tokens )
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::consume_a_simple_block " ) ;
2021-07-09 17:07:24 +01:00
2021-07-03 15:40:06 +01:00
auto ending_token = ( ( Token ) tokens . current_token ( ) ) . mirror_variant ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-01 14:13:48 +01:00
NonnullRefPtr < StyleBlockRule > block = create < StyleBlockRule > ( ) ;
2021-07-03 15:40:06 +01:00
block - > m_token = tokens . current_token ( ) ;
2021-03-22 17:41:47 +01:00
for ( ; ; ) {
2021-07-22 21:47:35 +01:00
auto & token = tokens . next_token ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 15:06:53 +01:00
if ( token . is ( ending_token ) ) {
2021-03-22 17:41:47 +01:00
return block ;
}
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : EndOfFile ) ) {
2021-04-24 20:20:14 -07:00
log_parse_error ( ) ;
2021-03-22 17:41:47 +01:00
return block ;
}
2021-07-03 15:40:06 +01:00
tokens . reconsume_current_input_token ( ) ;
auto value = consume_a_component_value ( tokens ) ;
2021-07-01 14:54:27 +01:00
block - > m_values . append ( value ) ;
2021-03-22 17:41:47 +01:00
}
}
2021-07-01 14:13:48 +01:00
NonnullRefPtr < StyleFunctionRule > Parser : : consume_a_function ( )
2021-03-22 17:41:47 +01:00
{
2021-07-03 15:40:06 +01:00
return consume_a_function ( m_token_stream ) ;
}
template < typename T >
NonnullRefPtr < StyleFunctionRule > Parser : : consume_a_function ( TokenStream < T > & tokens )
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::consume_a_function " ) ;
2021-07-09 17:07:24 +01:00
2021-07-03 15:40:06 +01:00
auto name_ident = tokens . current_token ( ) ;
VERIFY ( name_ident . is ( Token : : Type : : Function ) ) ;
NonnullRefPtr < StyleFunctionRule > function = create < StyleFunctionRule > ( ( ( Token ) name_ident ) . m_value . to_string ( ) ) ;
2021-03-22 17:41:47 +01:00
for ( ; ; ) {
2021-07-22 21:47:35 +01:00
auto & token = tokens . next_token ( ) ;
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : CloseParen ) ) {
2021-03-22 17:41:47 +01:00
return function ;
}
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : EndOfFile ) ) {
2021-04-24 20:20:14 -07:00
log_parse_error ( ) ;
2021-03-22 17:41:47 +01:00
return function ;
}
2021-07-03 15:40:06 +01:00
tokens . reconsume_current_input_token ( ) ;
auto value = consume_a_component_value ( tokens ) ;
2021-07-08 12:33:49 +01:00
function - > m_values . append ( value ) ;
2021-03-22 17:41:47 +01:00
}
return function ;
}
2021-07-03 15:40:06 +01:00
Optional < StyleDeclarationRule > Parser : : consume_a_declaration ( )
2021-03-22 17:41:47 +01:00
{
2021-07-03 15:40:06 +01:00
return consume_a_declaration ( m_token_stream ) ;
2021-03-22 17:41:47 +01:00
}
2021-07-03 15:40:06 +01:00
template < typename T >
Optional < StyleDeclarationRule > Parser : : consume_a_declaration ( TokenStream < T > & tokens )
2021-03-22 17:41:47 +01:00
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::consume_a_declaration " ) ;
2021-07-09 17:07:24 +01:00
2021-07-22 21:47:35 +01:00
auto & token = tokens . next_token ( ) ;
2021-03-22 17:41:47 +01:00
StyleDeclarationRule declaration ;
2021-07-03 15:40:06 +01:00
VERIFY ( token . is ( Token : : Type : : Ident ) ) ;
declaration . m_name = ( ( Token ) token ) . ident ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 15:40:06 +01:00
tokens . skip_whitespace ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-22 21:47:35 +01:00
auto & maybe_colon = tokens . next_token ( ) ;
if ( ! maybe_colon . is ( Token : : Type : : Colon ) ) {
2021-04-24 20:20:14 -07:00
log_parse_error ( ) ;
2021-03-22 17:41:47 +01:00
return { } ;
}
2021-07-03 15:40:06 +01:00
tokens . skip_whitespace ( ) ;
2021-03-22 17:41:47 +01:00
for ( ; ; ) {
2021-07-03 15:40:06 +01:00
if ( tokens . peek_token ( ) . is ( Token : : Type : : EndOfFile ) ) {
2021-03-22 17:41:47 +01:00
break ;
}
2021-07-03 15:40:06 +01:00
declaration . m_values . append ( consume_a_component_value ( tokens ) ) ;
2021-03-22 17:41:47 +01:00
}
2021-07-08 21:53:22 +01:00
if ( declaration . m_values . size ( ) > = 2 ) {
auto second_last = declaration . m_values . at ( declaration . m_values . size ( ) - 2 ) ;
auto last = declaration . m_values . at ( declaration . m_values . size ( ) - 1 ) ;
2021-03-22 17:41:47 +01:00
2021-07-08 21:53:22 +01:00
if ( second_last . m_type = = StyleComponentValueRule : : ComponentType : : Token & & last . m_type = = StyleComponentValueRule : : ComponentType : : Token ) {
auto last_token = last . m_token ;
auto second_last_token = second_last . m_token ;
2021-03-22 17:41:47 +01:00
2021-07-08 21:53:22 +01:00
if ( second_last_token . is ( Token : : Type : : Delim ) & & second_last_token . m_value . to_string ( ) . equals_ignoring_case ( " ! " ) ) {
if ( last_token . is ( Token : : Type : : Ident ) & & last_token . m_value . to_string ( ) . equals_ignoring_case ( " important " ) ) {
declaration . m_values . remove ( declaration . m_values . size ( ) - 2 ) ;
declaration . m_values . remove ( declaration . m_values . size ( ) - 1 ) ;
declaration . m_important = true ;
}
2021-03-22 17:41:47 +01:00
}
}
}
2021-07-07 20:47:09 +01:00
while ( ! declaration . m_values . is_empty ( ) ) {
auto maybe_whitespace = declaration . m_values . last ( ) ;
2021-07-03 15:06:53 +01:00
if ( ! ( maybe_whitespace . is ( Token : : Type : : Whitespace ) ) ) {
2021-03-22 17:41:47 +01:00
break ;
}
2021-07-07 20:47:09 +01:00
declaration . m_values . take_last ( ) ;
2021-03-22 17:41:47 +01:00
}
return declaration ;
}
Vector < DeclarationOrAtRule > Parser : : consume_a_list_of_declarations ( )
2021-07-03 15:40:06 +01:00
{
return consume_a_list_of_declarations ( m_token_stream ) ;
}
template < typename T >
Vector < DeclarationOrAtRule > Parser : : consume_a_list_of_declarations ( TokenStream < T > & tokens )
2021-03-22 17:41:47 +01:00
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::consume_a_list_of_declarations " ) ;
2021-07-09 17:07:24 +01:00
2021-03-22 17:41:47 +01:00
Vector < DeclarationOrAtRule > list ;
for ( ; ; ) {
2021-07-22 21:47:35 +01:00
auto & token = tokens . next_token ( ) ;
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : Whitespace ) | | token . is ( Token : : Type : : Semicolon ) ) {
2021-03-22 17:41:47 +01:00
continue ;
}
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : EndOfFile ) ) {
2021-03-22 17:41:47 +01:00
return list ;
}
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : AtKeyword ) ) {
2021-07-03 15:40:06 +01:00
tokens . reconsume_current_input_token ( ) ;
list . append ( DeclarationOrAtRule ( consume_an_at_rule ( tokens ) ) ) ;
2021-03-22 17:41:47 +01:00
continue ;
}
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : Ident ) ) {
2021-03-22 17:41:47 +01:00
Vector < StyleComponentValueRule > temp ;
2021-07-08 21:53:22 +01:00
temp . append ( token ) ;
2021-03-22 17:41:47 +01:00
for ( ; ; ) {
2021-07-22 21:47:35 +01:00
auto & peek = tokens . peek_token ( ) ;
2021-07-03 15:06:53 +01:00
if ( peek . is ( Token : : Type : : Semicolon ) | | peek . is ( Token : : Type : : EndOfFile ) ) {
2021-03-22 17:41:47 +01:00
break ;
}
2021-07-03 15:40:06 +01:00
temp . append ( consume_a_component_value ( tokens ) ) ;
2021-03-22 17:41:47 +01:00
}
2021-07-03 15:40:06 +01:00
auto token_stream = TokenStream ( temp ) ;
auto maybe_declaration = consume_a_declaration ( token_stream ) ;
2021-03-22 17:41:47 +01:00
if ( maybe_declaration . has_value ( ) ) {
list . append ( DeclarationOrAtRule ( maybe_declaration . value ( ) ) ) ;
}
2021-07-08 21:53:22 +01:00
continue ;
2021-03-22 17:41:47 +01:00
}
2021-04-24 20:20:14 -07:00
log_parse_error ( ) ;
2021-07-03 15:40:06 +01:00
tokens . reconsume_current_input_token ( ) ;
2021-07-22 21:47:35 +01:00
for ( ; ; ) {
auto & peek = tokens . peek_token ( ) ;
if ( peek . is ( Token : : Type : : Semicolon ) | | peek . is ( Token : : Type : : EndOfFile ) )
break ;
2021-07-09 20:08:09 +01:00
dbgln ( " Discarding token: '{}' " , peek . to_debug_string ( ) ) ;
2021-07-08 21:53:22 +01:00
( void ) consume_a_component_value ( tokens ) ;
2021-03-22 17:41:47 +01:00
}
}
return list ;
}
2021-07-09 16:34:29 +01:00
RefPtr < CSSRule > Parser : : parse_as_rule ( )
2021-07-03 15:40:06 +01:00
{
return parse_as_rule ( m_token_stream ) ;
}
template < typename T >
RefPtr < CSSRule > Parser : : parse_as_rule ( TokenStream < T > & tokens )
2021-03-22 17:41:47 +01:00
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_as_rule " ) ;
2021-07-09 17:07:24 +01:00
2021-07-09 16:34:29 +01:00
RefPtr < CSSRule > rule ;
2021-03-22 17:41:47 +01:00
2021-07-03 15:40:06 +01:00
tokens . skip_whitespace ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-22 21:47:35 +01:00
auto & token = tokens . peek_token ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : EndOfFile ) ) {
2021-03-22 17:41:47 +01:00
return { } ;
2021-07-03 15:06:53 +01:00
} else if ( token . is ( Token : : Type : : AtKeyword ) ) {
2021-07-08 21:53:22 +01:00
auto at_rule = consume_an_at_rule ( ) ;
rule = convert_to_rule ( at_rule ) ;
2021-03-22 17:41:47 +01:00
} else {
2021-07-03 15:40:06 +01:00
auto qualified_rule = consume_a_qualified_rule ( tokens ) ;
2021-07-09 16:34:29 +01:00
if ( ! qualified_rule )
return { } ;
2021-07-08 21:53:22 +01:00
rule = convert_to_rule ( * qualified_rule ) ;
2021-03-22 17:41:47 +01:00
}
2021-07-03 15:40:06 +01:00
tokens . skip_whitespace ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-22 21:47:35 +01:00
auto & maybe_eof = tokens . peek_token ( ) ;
2021-07-03 15:06:53 +01:00
if ( maybe_eof . is ( Token : : Type : : EndOfFile ) ) {
2021-03-22 17:41:47 +01:00
return rule ;
}
return { } ;
}
2021-07-09 16:34:29 +01:00
NonnullRefPtrVector < CSSRule > Parser : : parse_as_list_of_rules ( )
2021-03-22 17:41:47 +01:00
{
2021-07-03 15:40:06 +01:00
return parse_as_list_of_rules ( m_token_stream ) ;
}
template < typename T >
NonnullRefPtrVector < CSSRule > Parser : : parse_as_list_of_rules ( TokenStream < T > & tokens )
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_as_list_of_rules " ) ;
2021-07-09 17:07:24 +01:00
2021-07-03 15:40:06 +01:00
auto parsed_rules = consume_a_list_of_rules ( tokens , false ) ;
2021-07-09 16:34:29 +01:00
NonnullRefPtrVector < CSSRule > rules ;
for ( auto & rule : parsed_rules ) {
2021-07-08 21:53:22 +01:00
auto converted_rule = convert_to_rule ( rule ) ;
2021-07-09 16:34:29 +01:00
if ( converted_rule )
rules . append ( * converted_rule ) ;
}
return rules ;
2021-03-22 17:41:47 +01:00
}
2021-07-09 16:34:29 +01:00
Optional < StyleProperty > Parser : : parse_as_declaration ( )
2021-03-22 17:41:47 +01:00
{
2021-07-03 15:40:06 +01:00
return parse_as_declaration ( m_token_stream ) ;
}
2021-03-22 17:41:47 +01:00
2021-07-03 15:40:06 +01:00
template < typename T >
Optional < StyleProperty > Parser : : parse_as_declaration ( TokenStream < T > & tokens )
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_as_declaration " ) ;
2021-07-09 17:07:24 +01:00
2021-07-03 15:40:06 +01:00
tokens . skip_whitespace ( ) ;
2021-07-22 21:47:35 +01:00
auto & token = tokens . peek_token ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 15:06:53 +01:00
if ( ! token . is ( Token : : Type : : Ident ) ) {
2021-03-22 17:41:47 +01:00
return { } ;
}
2021-07-03 15:40:06 +01:00
auto declaration = consume_a_declaration ( tokens ) ;
2021-07-06 16:32:18 +01:00
if ( declaration . has_value ( ) )
return convert_to_style_property ( declaration . value ( ) ) ;
2021-07-03 15:40:06 +01:00
2021-07-09 16:34:29 +01:00
return { } ;
2021-03-22 17:41:47 +01:00
}
2021-07-03 15:40:06 +01:00
RefPtr < CSSStyleDeclaration > Parser : : parse_as_list_of_declarations ( )
2021-03-22 17:41:47 +01:00
{
2021-07-03 15:40:06 +01:00
return parse_as_list_of_declarations ( m_token_stream ) ;
}
2021-07-09 16:34:29 +01:00
2021-07-03 15:40:06 +01:00
template < typename T >
2021-07-06 16:32:18 +01:00
RefPtr < CSSStyleDeclaration > Parser : : parse_as_list_of_declarations ( TokenStream < T > & tokens )
2021-07-03 15:40:06 +01:00
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_as_list_of_declarations " ) ;
2021-07-09 17:07:24 +01:00
2021-07-06 16:32:18 +01:00
auto declarations_and_at_rules = consume_a_list_of_declarations ( tokens ) ;
Vector < StyleProperty > properties ;
HashMap < String , StyleProperty > custom_properties ;
for ( auto & declaration_or_at_rule : declarations_and_at_rules ) {
if ( declaration_or_at_rule . is_at_rule ( ) ) {
dbgln ( " Parser::parse_as_list_of_declarations(): At-rule is not allowed here! " ) ;
continue ;
}
auto & declaration = declaration_or_at_rule . m_declaration ;
auto maybe_property = convert_to_style_property ( declaration ) ;
if ( maybe_property . has_value ( ) ) {
auto property = maybe_property . value ( ) ;
if ( property . property_id = = PropertyID : : Custom ) {
custom_properties . set ( property . custom_name , property ) ;
} else {
properties . append ( property ) ;
}
}
}
return CSSStyleDeclaration : : create ( move ( properties ) , move ( custom_properties ) ) ;
2021-03-22 17:41:47 +01:00
}
Optional < StyleComponentValueRule > Parser : : parse_as_component_value ( )
{
2021-07-03 15:40:06 +01:00
return parse_as_component_value ( m_token_stream ) ;
}
2021-03-22 17:41:47 +01:00
2021-07-03 15:40:06 +01:00
template < typename T >
Optional < StyleComponentValueRule > Parser : : parse_as_component_value ( TokenStream < T > & tokens )
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_as_component_value " ) ;
2021-07-09 17:07:24 +01:00
2021-07-03 15:40:06 +01:00
tokens . skip_whitespace ( ) ;
2021-07-22 21:47:35 +01:00
auto & token = tokens . peek_token ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 15:06:53 +01:00
if ( token . is ( Token : : Type : : EndOfFile ) ) {
2021-03-22 17:41:47 +01:00
return { } ;
}
2021-07-03 15:40:06 +01:00
auto value = consume_a_component_value ( tokens ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 15:40:06 +01:00
tokens . skip_whitespace ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-22 21:47:35 +01:00
auto & maybe_eof = tokens . peek_token ( ) ;
2021-07-03 15:06:53 +01:00
if ( maybe_eof . is ( Token : : Type : : EndOfFile ) ) {
2021-03-22 17:41:47 +01:00
return value ;
}
return { } ;
}
2021-07-03 15:40:06 +01:00
2021-03-22 17:41:47 +01:00
Vector < StyleComponentValueRule > Parser : : parse_as_list_of_component_values ( )
2021-07-03 15:40:06 +01:00
{
return parse_as_list_of_component_values ( m_token_stream ) ;
}
template < typename T >
Vector < StyleComponentValueRule > Parser : : parse_as_list_of_component_values ( TokenStream < T > & tokens )
2021-03-22 17:41:47 +01:00
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_as_list_of_component_values " ) ;
2021-07-09 17:07:24 +01:00
2021-03-22 17:41:47 +01:00
Vector < StyleComponentValueRule > rules ;
for ( ; ; ) {
2021-07-03 15:40:06 +01:00
if ( tokens . peek_token ( ) . is ( Token : : Type : : EndOfFile ) ) {
2021-03-22 17:41:47 +01:00
break ;
}
2021-07-03 15:40:06 +01:00
rules . append ( consume_a_component_value ( tokens ) ) ;
2021-03-22 17:41:47 +01:00
}
return rules ;
}
2021-07-02 19:52:07 +01:00
Vector < Vector < StyleComponentValueRule > > Parser : : parse_as_comma_separated_list_of_component_values ( )
2021-07-03 15:40:06 +01:00
{
return parse_as_comma_separated_list_of_component_values ( m_token_stream ) ;
}
template < typename T >
Vector < Vector < StyleComponentValueRule > > Parser : : parse_as_comma_separated_list_of_component_values ( TokenStream < T > & tokens )
2021-03-22 17:41:47 +01:00
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_as_comma_separated_list_of_component_values " ) ;
2021-07-09 17:07:24 +01:00
2021-07-02 19:52:07 +01:00
Vector < Vector < StyleComponentValueRule > > lists ;
lists . append ( { } ) ;
2021-03-22 17:41:47 +01:00
for ( ; ; ) {
2021-07-22 21:47:35 +01:00
auto & next = tokens . next_token ( ) ;
2021-03-22 17:41:47 +01:00
2021-07-03 15:06:53 +01:00
if ( next . is ( Token : : Type : : Comma ) ) {
2021-07-02 19:52:07 +01:00
lists . append ( { } ) ;
2021-03-22 17:41:47 +01:00
continue ;
2021-07-03 15:06:53 +01:00
} else if ( next . is ( Token : : Type : : EndOfFile ) ) {
2021-03-22 17:41:47 +01:00
break ;
2021-07-02 19:52:07 +01:00
}
2021-07-03 15:40:06 +01:00
tokens . reconsume_current_input_token ( ) ;
auto component_value = consume_a_component_value ( tokens ) ;
2021-07-02 19:52:07 +01:00
lists . last ( ) . append ( component_value ) ;
2021-03-22 17:41:47 +01:00
}
2021-07-02 19:52:07 +01:00
return lists ;
2021-03-22 17:41:47 +01:00
}
2021-07-09 16:34:29 +01:00
2021-07-22 17:51:07 +01:00
Optional < URL > Parser : : parse_url_function ( ParsingContext const & context , StyleComponentValueRule const & component_value )
{
// FIXME: Handle list of media queries. https://www.w3.org/TR/css-cascade-3/#conditional-import
2021-07-28 17:02:37 +01:00
// FIXME: Handle data: urls (RFC2397)
2021-07-22 17:51:07 +01:00
2021-07-28 17:02:37 +01:00
auto is_data_url = [ ] ( StringView & url_string ) - > bool {
return url_string . starts_with ( " data: " , CaseSensitivity : : CaseInsensitive ) ;
} ;
if ( component_value . is ( Token : : Type : : Url ) ) {
auto url_string = component_value . token ( ) . url ( ) ;
if ( is_data_url ( url_string ) )
return { } ;
return context . complete_url ( url_string ) ;
}
2021-07-22 17:51:07 +01:00
if ( component_value . is_function ( ) & & component_value . function ( ) . name ( ) . equals_ignoring_case ( " url " ) ) {
auto & function_values = component_value . function ( ) . values ( ) ;
// FIXME: Handle url-modifiers. https://www.w3.org/TR/css-values-4/#url-modifiers
for ( size_t i = 0 ; i < function_values . size ( ) ; + + i ) {
auto & value = function_values [ i ] ;
if ( value . is ( Token : : Type : : Whitespace ) )
continue ;
if ( value . is ( Token : : Type : : String ) ) {
2021-07-28 17:02:37 +01:00
auto url_string = value . token ( ) . string ( ) ;
if ( is_data_url ( url_string ) )
return { } ;
return context . complete_url ( url_string ) ;
2021-07-22 17:51:07 +01:00
}
2021-07-28 17:02:37 +01:00
break ;
2021-07-22 17:51:07 +01:00
}
}
return { } ;
}
2021-07-08 21:53:22 +01:00
RefPtr < CSSRule > Parser : : convert_to_rule ( NonnullRefPtr < StyleRule > rule )
2021-07-09 16:34:29 +01:00
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::convert_to_rule " ) ;
2021-07-09 17:07:24 +01:00
2021-07-08 21:53:22 +01:00
if ( rule - > m_type = = StyleRule : : Type : : At ) {
2021-07-06 16:05:12 +01:00
if ( rule - > m_name . equals_ignoring_case ( " import " sv ) & & ! rule - > prelude ( ) . is_empty ( ) ) {
2021-07-22 17:51:07 +01:00
Optional < URL > url ;
for ( auto & token : rule - > prelude ( ) ) {
if ( token . is ( Token : : Type : : Whitespace ) )
continue ;
if ( token . is ( Token : : Type : : String ) ) {
url = m_context . complete_url ( token . token ( ) . string ( ) ) ;
} else {
url = parse_url_function ( m_context , token ) ;
2021-07-08 12:33:49 +01:00
}
2021-07-08 21:53:22 +01:00
2021-07-22 17:51:07 +01:00
// FIXME: Handle list of media queries. https://www.w3.org/TR/css-cascade-3/#conditional-import
if ( url . has_value ( ) )
break ;
}
2021-07-06 16:05:12 +01:00
if ( url . has_value ( ) )
2021-07-22 17:51:07 +01:00
return CSSImportRule : : create ( url . value ( ) ) ;
else
dbgln ( " Unable to parse url from @import rule " ) ;
2021-07-06 16:05:12 +01:00
} else {
dbgln ( " Unrecognized CSS at-rule: {} " , rule - > m_name ) ;
}
// FIXME: More at rules!
} else {
2021-07-08 21:53:22 +01:00
auto prelude_stream = TokenStream ( rule - > m_prelude ) ;
2021-07-12 17:30:40 +01:00
auto selectors = parse_a_selector ( prelude_stream ) ;
2021-07-23 16:13:07 +01:00
if ( ! selectors . has_value ( ) | | selectors . value ( ) . is_empty ( ) ) {
dbgln ( " CSSParser: style rule selectors invalid; discarding. " ) ;
prelude_stream . dump_all_tokens ( ) ;
return { } ;
}
2021-07-08 21:53:22 +01:00
auto declaration = convert_to_declaration ( * rule - > m_block ) ;
2021-07-23 16:13:07 +01:00
if ( ! declaration ) {
dbgln ( " CSSParser: style rule declaration invalid; discarding. " ) ;
return { } ;
}
return CSSStyleRule : : create ( move ( selectors . value ( ) ) , move ( * declaration ) ) ;
2021-07-08 21:53:22 +01:00
}
2021-07-09 16:34:29 +01:00
return { } ;
}
2021-07-08 21:53:22 +01:00
RefPtr < CSSStyleDeclaration > Parser : : convert_to_declaration ( NonnullRefPtr < StyleBlockRule > block )
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::convert_to_declaration " ) ;
2021-07-09 17:07:24 +01:00
2021-07-08 21:53:22 +01:00
if ( ! block - > is_curly ( ) )
return { } ;
auto stream = TokenStream ( block - > m_values ) ;
2021-07-06 16:32:18 +01:00
return parse_as_list_of_declarations ( stream ) ;
}
2021-07-08 21:53:22 +01:00
2021-07-06 16:32:18 +01:00
Optional < StyleProperty > Parser : : convert_to_style_property ( StyleDeclarationRule & declaration )
{
2021-07-23 13:06:31 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::convert_to_style_property " ) ;
2021-07-09 17:07:24 +01:00
2021-07-06 16:32:18 +01:00
auto & property_name = declaration . m_name ;
auto property_id = property_id_from_string ( property_name ) ;
if ( property_id = = PropertyID : : Invalid & & property_name . starts_with ( " -- " ) )
property_id = PropertyID : : Custom ;
2021-07-08 21:53:22 +01:00
2021-07-06 16:32:18 +01:00
if ( property_id = = PropertyID : : Invalid & & ! property_name . starts_with ( " - " ) ) {
dbgln ( " Parser::convert_to_style_property(): Unrecognized property '{}' " , property_name ) ;
return { } ;
}
2021-07-08 21:53:22 +01:00
2021-07-06 16:32:18 +01:00
auto value_token_stream = TokenStream ( declaration . m_values ) ;
auto value = parse_css_value ( property_id , value_token_stream ) ;
if ( ! value ) {
dbgln ( " Parser::convert_to_style_property(): Property '{}' has no value. " , property_name ) ;
return { } ;
2021-07-08 21:53:22 +01:00
}
2021-07-06 16:32:18 +01:00
if ( property_id = = PropertyID : : Custom ) {
return StyleProperty { property_id , value . release_nonnull ( ) , declaration . m_name , declaration . m_important } ;
} else {
return StyleProperty { property_id , value . release_nonnull ( ) , { } , declaration . m_important } ;
}
2021-07-08 21:53:22 +01:00
}
2021-07-09 21:04:34 +01:00
Optional < float > Parser : : try_parse_float ( StringView string )
2021-07-08 21:53:22 +01:00
{
2021-07-09 21:04:34 +01:00
// FIXME: This is copied from DeprecatedCSSParser, so may not be to spec.
const char * str = string . characters_without_null_termination ( ) ;
size_t len = string . length ( ) ;
size_t weight = 1 ;
int exp_val = 0 ;
float value = 0.0f ;
float fraction = 0.0f ;
bool has_sign = false ;
bool is_negative = false ;
bool is_fractional = false ;
bool is_scientific = false ;
if ( str [ 0 ] = = ' - ' ) {
is_negative = true ;
has_sign = true ;
}
if ( str [ 0 ] = = ' + ' ) {
has_sign = true ;
}
for ( size_t i = has_sign ; i < len ; i + + ) {
// Looks like we're about to start working on the fractional part
if ( str [ i ] = = ' . ' ) {
is_fractional = true ;
continue ;
}
if ( str [ i ] = = ' e ' | | str [ i ] = = ' E ' ) {
if ( str [ i + 1 ] = = ' - ' | | str [ i + 1 ] = = ' + ' )
exp_val = atoi ( str + i + 2 ) ;
else
exp_val = atoi ( str + i + 1 ) ;
is_scientific = true ;
continue ;
}
if ( str [ i ] < ' 0 ' | | str [ i ] > ' 9 ' | | exp_val ! = 0 ) {
return { } ;
continue ;
}
if ( is_fractional ) {
fraction * = 10 ;
fraction + = str [ i ] - ' 0 ' ;
weight * = 10 ;
} else {
value = value * 10 ;
value + = str [ i ] - ' 0 ' ;
}
}
fraction / = weight ;
value + = fraction ;
if ( is_scientific ) {
bool divide = exp_val < 0 ;
if ( divide )
exp_val * = - 1 ;
for ( int i = 0 ; i < exp_val ; i + + ) {
if ( divide )
value / = 10 ;
else
value * = 10 ;
}
}
return is_negative ? - value : value ;
}
2021-07-28 16:29:11 +01:00
RefPtr < StyleValue > Parser : : parse_builtin_or_dynamic_value ( ParsingContext const & context , StyleComponentValueRule const & component_value )
2021-07-09 21:04:34 +01:00
{
2021-07-18 17:12:23 +01:00
if ( component_value . is ( Token : : Type : : Ident ) ) {
auto ident = component_value . token ( ) . ident ( ) ;
if ( ident . equals_ignoring_case ( " inherit " ) )
return InheritStyleValue : : create ( ) ;
if ( ident . equals_ignoring_case ( " initial " ) )
return InitialStyleValue : : create ( ) ;
if ( ident . equals_ignoring_case ( " auto " ) )
return LengthStyleValue : : create ( Length : : make_auto ( ) ) ;
// FIXME: Implement `unset` keyword
}
2021-07-09 21:04:34 +01:00
2021-07-28 16:30:59 +01:00
if ( component_value . is_function ( ) ) {
auto & function = component_value . function ( ) ;
if ( function . name ( ) . equals_ignoring_case ( " calc " ) ) {
auto calc_expression = parse_calc_expression ( context , function . values ( ) ) ;
// FIXME: Either produce a string value of calc() here, or do so in CalculatedStyleValue::to_string().
if ( calc_expression )
return CalculatedStyleValue : : create ( " (FIXME:calc to string) " , calc_expression . release_nonnull ( ) ) ;
} else if ( function . name ( ) . equals_ignoring_case ( " var " ) ) {
// FIXME: Handle fallback value as second parameter
// https://www.w3.org/TR/css-variables-1/#using-variables
if ( ! component_value . function ( ) . values ( ) . is_empty ( ) ) {
auto & property_name_token = component_value . function ( ) . values ( ) . first ( ) ;
if ( property_name_token . is ( Token : : Type : : Ident ) )
return CustomStyleValue : : create ( property_name_token . token ( ) . ident ( ) ) ;
else
dbgln ( " First argument to var() function was not an ident: '{}' " , property_name_token . to_debug_string ( ) ) ;
}
2021-07-18 17:12:23 +01:00
}
}
2021-07-09 21:04:34 +01:00
2021-07-18 17:12:23 +01:00
return { } ;
}
2021-07-28 14:10:03 +01:00
Optional < Length > Parser : : parse_length ( ParsingContext const & context , StyleComponentValueRule const & component_value )
2021-07-18 17:12:23 +01:00
{
2021-07-28 14:10:03 +01:00
Length : : Type type = Length : : Type : : Undefined ;
Optional < float > numeric_value ;
2021-07-09 21:04:34 +01:00
2021-07-28 14:10:03 +01:00
if ( component_value . is ( Token : : Type : : Dimension ) ) {
auto length_string = component_value . token ( ) . m_value . string_view ( ) ;
auto unit_string = component_value . token ( ) . m_unit . string_view ( ) ;
if ( unit_string . equals_ignoring_case ( " % " ) ) {
2021-07-18 17:12:23 +01:00
type = Length : : Type : : Percentage ;
2021-07-28 14:10:03 +01:00
} else if ( unit_string . equals_ignoring_case ( " px " ) ) {
type = Length : : Type : : Px ;
} else if ( unit_string . equals_ignoring_case ( " pt " ) ) {
type = Length : : Type : : Pt ;
} else if ( unit_string . equals_ignoring_case ( " pc " ) ) {
type = Length : : Type : : Pc ;
} else if ( unit_string . equals_ignoring_case ( " mm " ) ) {
type = Length : : Type : : Mm ;
} else if ( unit_string . equals_ignoring_case ( " rem " ) ) {
type = Length : : Type : : Rem ;
} else if ( unit_string . equals_ignoring_case ( " em " ) ) {
type = Length : : Type : : Em ;
} else if ( unit_string . equals_ignoring_case ( " ex " ) ) {
type = Length : : Type : : Ex ;
} else if ( unit_string . equals_ignoring_case ( " vw " ) ) {
type = Length : : Type : : Vw ;
} else if ( unit_string . equals_ignoring_case ( " vh " ) ) {
type = Length : : Type : : Vh ;
} else if ( unit_string . equals_ignoring_case ( " vmax " ) ) {
type = Length : : Type : : Vmax ;
} else if ( unit_string . equals_ignoring_case ( " vmin " ) ) {
type = Length : : Type : : Vmin ;
} else if ( unit_string . equals_ignoring_case ( " cm " ) ) {
type = Length : : Type : : Cm ;
} else if ( unit_string . equals_ignoring_case ( " in " ) ) {
type = Length : : Type : : In ;
} else if ( unit_string . equals_ignoring_case ( " Q " ) ) {
type = Length : : Type : : Q ;
} else if ( context . in_quirks_mode ( ) ) {
type = Length : : Type : : Px ;
}
numeric_value = try_parse_float ( length_string ) ;
} else if ( component_value . is ( Token : : Type : : Number ) ) {
auto value_string = component_value . token ( ) . m_value . string_view ( ) ;
if ( value_string = = " 0 " ) {
type = Length : : Type : : Px ;
numeric_value = 0 ;
} else if ( context . in_quirks_mode ( ) ) {
type = Length : : Type : : Px ;
2021-07-18 17:12:23 +01:00
numeric_value = try_parse_float ( value_string ) ;
2021-07-09 21:04:34 +01:00
}
2021-07-28 14:10:03 +01:00
} else if ( component_value . is ( Token : : Type : : Percentage ) ) {
type = Length : : Type : : Percentage ;
auto value_string = component_value . token ( ) . m_value . string_view ( ) ;
numeric_value = try_parse_float ( value_string ) ;
}
2021-07-09 21:04:34 +01:00
2021-07-28 14:10:03 +01:00
if ( ! numeric_value . has_value ( ) )
return { } ;
2021-07-09 21:04:34 +01:00
2021-07-28 14:10:03 +01:00
return Length ( numeric_value . value ( ) , type ) ;
}
2021-07-09 21:04:34 +01:00
2021-07-28 14:10:03 +01:00
RefPtr < StyleValue > Parser : : parse_length_value ( ParsingContext const & context , StyleComponentValueRule const & component_value )
{
2021-07-18 17:12:23 +01:00
if ( component_value . is ( Token : : Type : : Dimension ) | | component_value . is ( Token : : Type : : Number ) | | component_value . is ( Token : : Type : : Percentage ) ) {
2021-07-28 14:10:03 +01:00
auto length = parse_length ( context , component_value ) ;
2021-07-09 21:04:34 +01:00
if ( length . has_value ( ) )
return LengthStyleValue : : create ( length . value ( ) ) ;
}
2021-07-18 17:12:23 +01:00
return { } ;
}
2021-07-09 21:04:34 +01:00
2021-07-18 17:12:23 +01:00
RefPtr < StyleValue > Parser : : parse_numeric_value ( ParsingContext const & , StyleComponentValueRule const & component_value )
{
if ( component_value . is ( Token : : Type : : Number ) ) {
auto number = component_value . token ( ) ;
if ( number . m_number_type = = Token : : NumberType : : Integer ) {
2021-07-24 21:22:44 +01:00
return NumericStyleValue : : create ( number . to_integer ( ) ) ;
2021-07-18 17:12:23 +01:00
} else {
auto float_value = try_parse_float ( number . m_value . string_view ( ) ) ;
if ( float_value . has_value ( ) )
return NumericStyleValue : : create ( float_value . value ( ) ) ;
2021-07-09 21:04:34 +01:00
}
}
2021-07-18 17:12:23 +01:00
return { } ;
}
RefPtr < StyleValue > Parser : : parse_identifier_value ( ParsingContext const & , StyleComponentValueRule const & component_value )
{
2021-07-14 16:20:06 +01:00
if ( component_value . is ( Token : : Type : : Ident ) ) {
auto value_id = value_id_from_string ( component_value . token ( ) . ident ( ) ) ;
2021-07-09 21:04:34 +01:00
if ( value_id ! = ValueID : : Invalid )
return IdentifierStyleValue : : create ( value_id ) ;
}
2021-07-18 17:12:23 +01:00
return { } ;
}
2021-07-28 14:10:03 +01:00
Optional < Color > Parser : : parse_color ( ParsingContext const & , StyleComponentValueRule const & component_value )
2021-07-18 17:12:23 +01:00
{
2021-07-18 21:20:40 +01:00
// https://www.w3.org/TR/css-color-3/
2021-07-28 14:10:03 +01:00
if ( component_value . is ( Token : : Type : : Ident ) ) {
auto ident = component_value . token ( ) . ident ( ) ;
if ( ident . equals_ignoring_case ( " transparent " ) )
return Color : : from_rgba ( 0x00000000 ) ;
auto color = Color : : from_string ( ident . to_string ( ) . to_lowercase ( ) ) ;
if ( color . has_value ( ) )
return color ;
} else if ( component_value . is ( Token : : Type : : Hash ) ) {
// FIXME: Read it directly
auto color = Color : : from_string ( String : : formatted ( " #{} " , component_value . token ( ) . m_value . to_string ( ) . to_lowercase ( ) ) ) ;
if ( color . has_value ( ) )
return color ;
} else if ( component_value . is_function ( ) ) {
auto & function = component_value . function ( ) ;
auto & values = function . values ( ) ;
Vector < Token > params ;
for ( size_t i = 0 ; i < values . size ( ) ; + + i ) {
auto & value = values . at ( i ) ;
if ( value . is ( Token : : Type : : Whitespace ) )
continue ;
if ( value . is ( Token : : Type : : Percentage ) | | value . is ( Token : : Type : : Number ) ) {
params . append ( value . token ( ) ) ;
// Eat following comma and whitespace
while ( ( i + 1 ) < values . size ( ) ) {
auto & next = values . at ( i + 1 ) ;
if ( next . is ( Token : : Type : : Whitespace ) )
i + + ;
else if ( next . is ( Token : : Type : : Comma ) )
break ;
2021-07-18 21:20:40 +01:00
2021-07-28 14:10:03 +01:00
return { } ;
2021-07-18 21:20:40 +01:00
}
}
2021-07-28 14:10:03 +01:00
}
2021-07-18 21:20:40 +01:00
2021-07-28 14:10:03 +01:00
if ( function . name ( ) . equals_ignoring_case ( " rgb " ) ) {
if ( params . size ( ) ! = 3 )
return { } ;
2021-07-18 21:20:40 +01:00
2021-07-28 14:10:03 +01:00
auto r_val = params [ 0 ] ;
auto g_val = params [ 1 ] ;
auto b_val = params [ 2 ] ;
if ( r_val . is ( Token : : NumberType : : Integer )
& & g_val . is ( Token : : NumberType : : Integer )
& & b_val . is ( Token : : NumberType : : Integer ) ) {
auto maybe_r = r_val . m_value . string_view ( ) . to_uint < u8 > ( ) ;
auto maybe_g = g_val . m_value . string_view ( ) . to_uint < u8 > ( ) ;
auto maybe_b = b_val . m_value . string_view ( ) . to_uint < u8 > ( ) ;
if ( maybe_r . has_value ( ) & & maybe_g . has_value ( ) & & maybe_b . has_value ( ) )
return Color ( maybe_r . value ( ) , maybe_g . value ( ) , maybe_b . value ( ) ) ;
} else if ( r_val . is ( Token : : Type : : Percentage )
& & g_val . is ( Token : : Type : : Percentage )
& & b_val . is ( Token : : Type : : Percentage ) ) {
auto maybe_r = try_parse_float ( r_val . m_value . string_view ( ) ) ;
auto maybe_g = try_parse_float ( g_val . m_value . string_view ( ) ) ;
auto maybe_b = try_parse_float ( b_val . m_value . string_view ( ) ) ;
if ( maybe_r . has_value ( ) & & maybe_g . has_value ( ) & & maybe_b . has_value ( ) ) {
u8 r = clamp ( lroundf ( maybe_r . value ( ) * 2.55f ) , 0 , 255 ) ;
u8 g = clamp ( lroundf ( maybe_g . value ( ) * 2.55f ) , 0 , 255 ) ;
u8 b = clamp ( lroundf ( maybe_b . value ( ) * 2.55f ) , 0 , 255 ) ;
return Color ( r , g , b ) ;
2021-07-18 21:20:40 +01:00
}
2021-07-28 14:10:03 +01:00
}
} else if ( function . name ( ) . equals_ignoring_case ( " rgba " ) ) {
if ( params . size ( ) ! = 4 )
return { } ;
2021-07-18 21:20:40 +01:00
2021-07-28 14:10:03 +01:00
auto r_val = params [ 0 ] ;
auto g_val = params [ 1 ] ;
auto b_val = params [ 2 ] ;
auto a_val = params [ 3 ] ;
if ( r_val . is ( Token : : NumberType : : Integer )
& & g_val . is ( Token : : NumberType : : Integer )
& & b_val . is ( Token : : NumberType : : Integer )
& & a_val . is ( Token : : Type : : Number ) ) {
auto maybe_r = r_val . m_value . string_view ( ) . to_uint < u8 > ( ) ;
auto maybe_g = g_val . m_value . string_view ( ) . to_uint < u8 > ( ) ;
auto maybe_b = b_val . m_value . string_view ( ) . to_uint < u8 > ( ) ;
auto maybe_a = try_parse_float ( a_val . m_value . string_view ( ) ) ;
if ( maybe_r . has_value ( ) & & maybe_g . has_value ( ) & & maybe_b . has_value ( ) & & maybe_a . has_value ( ) ) {
u8 a = clamp ( lroundf ( maybe_a . value ( ) * 255.0f ) , 0 , 255 ) ;
return Color ( maybe_r . value ( ) , maybe_g . value ( ) , maybe_b . value ( ) , a ) ;
2021-07-18 21:20:40 +01:00
}
2021-07-28 14:10:03 +01:00
} else if ( r_val . is ( Token : : Type : : Percentage )
& & g_val . is ( Token : : Type : : Percentage )
& & b_val . is ( Token : : Type : : Percentage )
& & a_val . is ( Token : : Type : : Number ) ) {
auto maybe_r = try_parse_float ( r_val . m_value . string_view ( ) ) ;
auto maybe_g = try_parse_float ( g_val . m_value . string_view ( ) ) ;
auto maybe_b = try_parse_float ( b_val . m_value . string_view ( ) ) ;
auto maybe_a = try_parse_float ( a_val . m_value . string_view ( ) ) ;
if ( maybe_r . has_value ( ) & & maybe_g . has_value ( ) & & maybe_b . has_value ( ) & & maybe_a . has_value ( ) ) {
u8 r = clamp ( lroundf ( maybe_r . value ( ) * 2.55f ) , 0 , 255 ) ;
u8 g = clamp ( lroundf ( maybe_g . value ( ) * 2.55f ) , 0 , 255 ) ;
u8 b = clamp ( lroundf ( maybe_b . value ( ) * 2.55f ) , 0 , 255 ) ;
u8 a = clamp ( lroundf ( maybe_a . value ( ) * 255.0f ) , 0 , 255 ) ;
return Color ( r , g , b , a ) ;
2021-07-18 21:20:40 +01:00
}
2021-07-28 14:10:03 +01:00
}
} else if ( function . name ( ) . equals_ignoring_case ( " hsl " ) ) {
if ( params . size ( ) ! = 3 )
return { } ;
2021-07-18 21:20:40 +01:00
2021-07-28 14:10:03 +01:00
auto h_val = params [ 0 ] ;
auto s_val = params [ 1 ] ;
auto l_val = params [ 2 ] ;
if ( h_val . is ( Token : : Type : : Number )
& & s_val . is ( Token : : Type : : Percentage )
& & l_val . is ( Token : : Type : : Percentage ) ) {
auto maybe_h = try_parse_float ( h_val . m_value . string_view ( ) ) ;
auto maybe_s = try_parse_float ( s_val . m_value . string_view ( ) ) ;
auto maybe_l = try_parse_float ( l_val . m_value . string_view ( ) ) ;
if ( maybe_h . has_value ( ) & & maybe_s . has_value ( ) & & maybe_l . has_value ( ) ) {
float h = maybe_h . value ( ) ;
float s = maybe_s . value ( ) / 100.0f ;
float l = maybe_l . value ( ) / 100.0f ;
return Color : : from_hsl ( h , s , l ) ;
2021-07-18 21:20:40 +01:00
}
}
2021-07-28 14:10:03 +01:00
} else if ( function . name ( ) . equals_ignoring_case ( " hsla " ) ) {
if ( params . size ( ) ! = 4 )
return { } ;
2021-07-09 21:04:34 +01:00
2021-07-28 14:10:03 +01:00
auto h_val = params [ 0 ] ;
auto s_val = params [ 1 ] ;
auto l_val = params [ 2 ] ;
auto a_val = params [ 3 ] ;
if ( h_val . is ( Token : : Type : : Number )
& & s_val . is ( Token : : Type : : Percentage )
& & l_val . is ( Token : : Type : : Percentage )
& & a_val . is ( Token : : Type : : Number ) ) {
auto maybe_h = try_parse_float ( h_val . m_value . string_view ( ) ) ;
auto maybe_s = try_parse_float ( s_val . m_value . string_view ( ) ) ;
auto maybe_l = try_parse_float ( l_val . m_value . string_view ( ) ) ;
auto maybe_a = try_parse_float ( a_val . m_value . string_view ( ) ) ;
if ( maybe_h . has_value ( ) & & maybe_s . has_value ( ) & & maybe_l . has_value ( ) & & maybe_a . has_value ( ) ) {
float h = maybe_h . value ( ) ;
float s = maybe_s . value ( ) / 100.0f ;
float l = maybe_l . value ( ) / 100.0f ;
float a = maybe_a . value ( ) ;
return Color : : from_hsla ( h , s , l , a ) ;
}
}
}
2021-07-09 21:04:34 +01:00
return { } ;
2021-07-28 14:10:03 +01:00
}
2021-07-09 21:04:34 +01:00
2021-07-28 14:10:03 +01:00
return { } ;
}
RefPtr < StyleValue > Parser : : parse_color_value ( ParsingContext const & context , StyleComponentValueRule const & component_value )
{
auto color = parse_color ( context , component_value ) ;
2021-07-09 21:04:34 +01:00
if ( color . has_value ( ) )
return ColorStyleValue : : create ( color . value ( ) ) ;
2021-07-18 17:12:23 +01:00
return { } ;
}
RefPtr < StyleValue > Parser : : parse_string_value ( ParsingContext const & , StyleComponentValueRule const & component_value )
{
2021-07-14 16:20:06 +01:00
if ( component_value . is ( Token : : Type : : String ) )
return StringStyleValue : : create ( component_value . token ( ) . string ( ) ) ;
2021-07-08 21:53:22 +01:00
return { } ;
}
2021-07-08 13:44:01 +01:00
2021-07-22 13:00:29 +01:00
RefPtr < StyleValue > Parser : : parse_image_value ( ParsingContext const & context , StyleComponentValueRule const & component_value )
{
2021-07-22 17:51:07 +01:00
auto url = parse_url_function ( context , component_value ) ;
if ( url . has_value ( ) )
return ImageStyleValue : : create ( url . value ( ) , * context . document ( ) ) ;
2021-07-22 13:00:29 +01:00
// FIXME: Handle gradients.
return { } ;
}
2021-07-28 14:12:28 +01:00
RefPtr < StyleValue > Parser : : parse_box_shadow_value ( ParsingContext const & context , Vector < StyleComponentValueRule > const & component_values )
{
// FIXME: Also support inset, spread-radius and multiple comma-seperated box-shadows
Length offset_x { } ;
Length offset_y { } ;
Length blur_radius { } ;
Color color { } ;
if ( component_values . size ( ) < 3 | | component_values . size ( ) > 4 )
return nullptr ;
auto maybe_x = parse_length ( context , component_values [ 0 ] ) ;
if ( ! maybe_x . has_value ( ) )
return nullptr ;
offset_x = maybe_x . value ( ) ;
auto maybe_y = parse_length ( context , component_values [ 1 ] ) ;
if ( ! maybe_y . has_value ( ) )
return nullptr ;
offset_y = maybe_y . value ( ) ;
if ( component_values . size ( ) = = 3 ) {
auto parsed_color = parse_color ( context , component_values [ 2 ] ) ;
if ( ! parsed_color . has_value ( ) )
return nullptr ;
color = parsed_color . value ( ) ;
} else if ( component_values . size ( ) = = 4 ) {
auto maybe_blur_radius = parse_length ( context , component_values [ 2 ] ) ;
if ( ! maybe_blur_radius . has_value ( ) )
return nullptr ;
blur_radius = maybe_blur_radius . value ( ) ;
auto parsed_color = parse_color ( context , component_values [ 3 ] ) ;
if ( ! parsed_color . has_value ( ) )
return nullptr ;
color = parsed_color . value ( ) ;
}
return BoxShadowStyleValue : : create ( offset_x , offset_y , blur_radius , color ) ;
}
2021-07-14 16:20:06 +01:00
RefPtr < StyleValue > Parser : : parse_css_value ( PropertyID property_id , TokenStream < StyleComponentValueRule > & tokens )
{
Vector < StyleComponentValueRule > component_values ;
while ( tokens . has_next_token ( ) ) {
auto & token = tokens . next_token ( ) ;
if ( token . is ( Token : : Type : : Semicolon ) ) {
tokens . reconsume_current_input_token ( ) ;
break ;
}
if ( token . is ( Token : : Type : : Whitespace ) )
continue ;
component_values . append ( token ) ;
}
if ( component_values . is_empty ( ) )
return { } ;
2021-07-28 14:12:28 +01:00
// Special-case property handling
if ( property_id = = PropertyID : : BoxShadow ) {
if ( auto parsed_box_shadow = parse_box_shadow_value ( m_context , component_values ) )
return parsed_box_shadow ;
}
2021-07-14 16:20:06 +01:00
if ( component_values . size ( ) = = 1 )
2021-07-18 17:12:23 +01:00
return parse_css_value ( m_context , property_id , component_values . first ( ) ) ;
2021-07-14 16:20:06 +01:00
return ValueListStyleValue : : create ( move ( component_values ) ) ;
}
2021-07-18 17:12:23 +01:00
RefPtr < StyleValue > Parser : : parse_css_value ( ParsingContext const & context , PropertyID property_id , StyleComponentValueRule const & component_value )
{
// FIXME: Figure out if we still need takes_integer_value, and if so, move this information
// into Properties.json.
auto takes_integer_value = [ ] ( PropertyID property_id ) - > bool {
return property_id = = PropertyID : : ZIndex
| | property_id = = PropertyID : : FontWeight
| | property_id = = PropertyID : : Custom ;
} ;
if ( takes_integer_value ( property_id ) & & component_value . is ( Token : : Type : : Number ) ) {
auto number = component_value . token ( ) ;
if ( number . m_number_type = = Token : : NumberType : : Integer ) {
2021-07-24 21:22:44 +01:00
return LengthStyleValue : : create ( Length : : make_px ( number . to_integer ( ) ) ) ;
2021-07-18 17:12:23 +01:00
}
}
2021-07-28 16:29:11 +01:00
if ( auto builtin_or_dynamic = parse_builtin_or_dynamic_value ( context , component_value ) )
return builtin_or_dynamic ;
2021-07-18 17:12:23 +01:00
if ( auto length = parse_length_value ( context , component_value ) )
return length ;
if ( auto numeric = parse_numeric_value ( context , component_value ) )
return numeric ;
if ( auto identifier = parse_identifier_value ( context , component_value ) )
return identifier ;
if ( auto color = parse_color_value ( context , component_value ) )
return color ;
if ( auto string = parse_string_value ( context , component_value ) )
return string ;
2021-07-22 13:00:29 +01:00
if ( auto image = parse_image_value ( context , component_value ) )
return image ;
2021-07-18 17:12:23 +01:00
return { } ;
}
2021-07-24 21:22:44 +01:00
Optional < Selector : : SimpleSelector : : ANPlusBPattern > Parser : : parse_a_n_plus_b_pattern ( TokenStream < StyleComponentValueRule > & values )
2021-07-08 13:44:01 +01:00
{
2021-07-24 21:22:44 +01:00
dbgln_if ( CSS_PARSER_DEBUG , " Parser::parse_a_n_plus_b_pattern " ) ;
2021-07-09 17:07:24 +01:00
2021-07-24 21:22:44 +01:00
int a = 0 ;
int b = 0 ;
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
auto syntax_error = [ & ] ( ) - > Optional < Selector : : SimpleSelector : : ANPlusBPattern > {
if constexpr ( CSS_PARSER_DEBUG ) {
dbgln ( " Invalid An+B value: " ) ;
values . dump_all_tokens ( ) ;
}
return { } ;
} ;
auto make_return_value = [ & ] ( ) - > Optional < Selector : : SimpleSelector : : ANPlusBPattern > {
// When we think we are done, but there are more non-whitespace tokens, then it's a parse error.
values . skip_whitespace ( ) ;
if ( values . has_next_token ( ) ) {
if constexpr ( CSS_PARSER_DEBUG ) {
dbgln ( " Extra tokens at end of An+B value: " ) ;
values . dump_all_tokens ( ) ;
}
return syntax_error ( ) ;
} else {
return Selector : : SimpleSelector : : ANPlusBPattern { a , b } ;
}
} ;
auto is_n = [ ] ( StyleComponentValueRule const & value ) - > bool {
return value . is ( Token : : Type : : Ident ) & & value . token ( ) . ident ( ) . equals_ignoring_case ( " n " sv ) ;
} ;
auto is_ndash = [ ] ( StyleComponentValueRule const & value ) - > bool {
return value . is ( Token : : Type : : Ident ) & & value . token ( ) . ident ( ) . equals_ignoring_case ( " n- " sv ) ;
} ;
auto is_dashn = [ ] ( StyleComponentValueRule const & value ) - > bool {
return value . is ( Token : : Type : : Ident ) & & value . token ( ) . ident ( ) . equals_ignoring_case ( " -n " sv ) ;
} ;
auto is_dashndash = [ ] ( StyleComponentValueRule const & value ) - > bool {
return value . is ( Token : : Type : : Ident ) & & value . token ( ) . ident ( ) . equals_ignoring_case ( " -n- " sv ) ;
} ;
auto is_delim = [ ] ( StyleComponentValueRule const & value , StringView const & delim ) - > bool {
return value . is ( Token : : Type : : Delim ) & & value . token ( ) . delim ( ) . equals_ignoring_case ( delim ) ;
} ;
auto is_n_dimension = [ ] ( StyleComponentValueRule const & value ) - > bool {
if ( ! value . is ( Token : : Type : : Dimension ) )
return false ;
if ( value . token ( ) . number_type ( ) ! = Token : : NumberType : : Integer )
return false ;
if ( ! value . token ( ) . dimension_unit ( ) . equals_ignoring_case ( " n " sv ) )
return false ;
return true ;
} ;
auto is_ndash_dimension = [ ] ( StyleComponentValueRule const & value ) - > bool {
if ( ! value . is ( Token : : Type : : Dimension ) )
return false ;
if ( value . token ( ) . number_type ( ) ! = Token : : NumberType : : Integer )
return false ;
if ( ! value . token ( ) . dimension_unit ( ) . equals_ignoring_case ( " n- " sv ) )
return false ;
return true ;
} ;
auto is_ndashdigit_dimension = [ ] ( StyleComponentValueRule const & value ) - > bool {
if ( ! value . is ( Token : : Type : : Dimension ) )
return false ;
if ( value . token ( ) . number_type ( ) ! = Token : : NumberType : : Integer )
return false ;
auto dimension_unit = value . token ( ) . dimension_unit ( ) ;
if ( ! dimension_unit . starts_with ( " n- " sv , CaseSensitivity : : CaseInsensitive ) )
return false ;
for ( size_t i = 2 ; i < dimension_unit . length ( ) ; + + i ) {
if ( ! is_ascii_digit ( dimension_unit [ i ] ) )
return false ;
}
return true ;
} ;
auto is_ndashdigit_ident = [ ] ( StyleComponentValueRule const & value ) - > bool {
if ( ! value . is ( Token : : Type : : Ident ) )
return false ;
auto ident = value . token ( ) . ident ( ) ;
if ( ! ident . starts_with ( " n- " sv , CaseSensitivity : : CaseInsensitive ) )
return false ;
for ( size_t i = 2 ; i < ident . length ( ) ; + + i ) {
if ( ! is_ascii_digit ( ident [ i ] ) )
return false ;
}
return true ;
} ;
auto is_dashndashdigit_ident = [ ] ( StyleComponentValueRule const & value ) - > bool {
if ( ! value . is ( Token : : Type : : Ident ) )
return false ;
auto ident = value . token ( ) . ident ( ) ;
if ( ! ident . starts_with ( " -n- " sv , CaseSensitivity : : CaseInsensitive ) )
return false ;
for ( size_t i = 3 ; i < ident . length ( ) ; + + i ) {
if ( ! is_ascii_digit ( ident [ i ] ) )
return false ;
}
return true ;
} ;
auto is_integer = [ ] ( StyleComponentValueRule const & value ) - > bool {
return value . is ( Token : : Type : : Number ) & & value . token ( ) . is ( Token : : NumberType : : Integer ) ;
} ;
auto is_signed_integer = [ is_integer ] ( StyleComponentValueRule const & value ) - > bool {
return is_integer ( value ) & & value . token ( ) . is_integer_value_signed ( ) ;
} ;
auto is_signless_integer = [ is_integer ] ( StyleComponentValueRule const & value ) - > bool {
return is_integer ( value ) & & ! value . token ( ) . is_integer_value_signed ( ) ;
} ;
// https://www.w3.org/TR/css-syntax-3/#the-anb-type
// Unfortunately these can't be in the same order as in the spec.
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
values . skip_whitespace ( ) ;
auto & first_value = values . next_token ( ) ;
// odd | even
if ( first_value . is ( Token : : Type : : Ident ) ) {
auto ident = first_value . token ( ) . ident ( ) ;
if ( ident . equals_ignoring_case ( " odd " ) ) {
a = 2 ;
b = 1 ;
return make_return_value ( ) ;
2021-07-08 13:44:01 +01:00
} else if ( ident . equals_ignoring_case ( " even " ) ) {
2021-07-24 21:22:44 +01:00
a = 2 ;
return make_return_value ( ) ;
2021-07-08 13:44:01 +01:00
}
}
2021-07-24 21:22:44 +01:00
// <integer>
if ( is_integer ( first_value ) ) {
b = first_value . token ( ) . to_integer ( ) ;
return make_return_value ( ) ;
}
// <n-dimension>
// <n-dimension> <signed-integer>
// <n-dimension> ['+' | '-'] <signless-integer>
if ( is_n_dimension ( first_value ) ) {
a = first_value . token ( ) . dimension_value_int ( ) ;
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
values . skip_whitespace ( ) ;
auto & second_value = values . next_token ( ) ;
if ( second_value . is ( Token : : Type : : EndOfFile ) ) {
// <n-dimension>
return make_return_value ( ) ;
} else if ( is_signed_integer ( second_value ) ) {
// <n-dimension> <signed-integer>
b = second_value . token ( ) . to_integer ( ) ;
return make_return_value ( ) ;
}
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
values . skip_whitespace ( ) ;
auto & third_value = values . next_token ( ) ;
if ( ( is_delim ( second_value , " + " sv ) | | is_delim ( second_value , " - " sv ) ) & & is_signless_integer ( third_value ) ) {
// <n-dimension> ['+' | '-'] <signless-integer>
b = third_value . token ( ) . to_integer ( ) * ( is_delim ( second_value , " + " sv ) ? 1 : - 1 ) ;
return make_return_value ( ) ;
}
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
return syntax_error ( ) ;
}
// <ndash-dimension> <signless-integer>
if ( is_ndash_dimension ( first_value ) ) {
values . skip_whitespace ( ) ;
auto & second_value = values . next_token ( ) ;
if ( is_signless_integer ( second_value ) ) {
a = first_value . token ( ) . dimension_value_int ( ) ;
b = - second_value . token ( ) . to_integer ( ) ;
return make_return_value ( ) ;
}
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
return syntax_error ( ) ;
2021-07-08 13:44:01 +01:00
}
2021-07-24 21:22:44 +01:00
// <ndashdigit-dimension>
if ( is_ndashdigit_dimension ( first_value ) ) {
auto & dimension = first_value . token ( ) ;
a = dimension . dimension_value_int ( ) ;
auto maybe_b = dimension . dimension_unit ( ) . substring_view ( 1 ) . to_int ( ) ;
if ( maybe_b . has_value ( ) ) {
b = maybe_b . value ( ) ;
return make_return_value ( ) ;
}
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
return syntax_error ( ) ;
}
// <dashndashdigit-ident>
if ( is_dashndashdigit_ident ( first_value ) ) {
a = - 1 ;
auto maybe_b = first_value . token ( ) . ident ( ) . substring_view ( 2 ) . to_int ( ) ;
if ( maybe_b . has_value ( ) ) {
b = maybe_b . value ( ) ;
return make_return_value ( ) ;
}
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
return syntax_error ( ) ;
}
// -n
// -n <signed-integer>
// -n ['+' | '-'] <signless-integer>
if ( is_dashn ( first_value ) ) {
a = - 1 ;
2021-07-08 13:44:01 +01:00
values . skip_whitespace ( ) ;
2021-07-24 21:22:44 +01:00
auto & second_value = values . next_token ( ) ;
if ( second_value . is ( Token : : Type : : EndOfFile ) ) {
// -n
return make_return_value ( ) ;
} else if ( is_signed_integer ( second_value ) ) {
// -n <signed-integer>
b = second_value . token ( ) . to_integer ( ) ;
return make_return_value ( ) ;
}
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
values . skip_whitespace ( ) ;
auto & third_value = values . next_token ( ) ;
if ( ( is_delim ( second_value , " + " sv ) | | is_delim ( second_value , " - " sv ) ) & & is_signless_integer ( third_value ) ) {
// -n ['+' | '-'] <signless-integer>
b = third_value . token ( ) . to_integer ( ) * ( is_delim ( second_value , " + " sv ) ? 1 : - 1 ) ;
return make_return_value ( ) ;
}
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
return syntax_error ( ) ;
}
// -n- <signless-integer>
if ( is_dashndash ( first_value ) ) {
values . skip_whitespace ( ) ;
auto & second_value = values . next_token ( ) ;
if ( is_signless_integer ( second_value ) ) {
a = - 1 ;
b = - second_value . token ( ) . to_integer ( ) ;
return make_return_value ( ) ;
}
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
return syntax_error ( ) ;
}
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
// All that's left now are these:
// '+'?†n
// '+'?†n <signed-integer>
// '+'?†n ['+' | '-'] <signless-integer>
// '+'?†n- <signless-integer>
// '+'?†<ndashdigit-ident>
// In all of these cases, the + is optional, and has no effect.
// So, we just skip the +, and carry on.
if ( ! is_delim ( first_value , " + " sv ) ) {
values . reconsume_current_input_token ( ) ;
// We do *not* skip whitespace here.
}
auto & first_after_plus = values . next_token ( ) ;
// '+'?†n
// '+'?†n <signed-integer>
// '+'?†n ['+' | '-'] <signless-integer>
if ( is_n ( first_after_plus ) ) {
a = 1 ;
values . skip_whitespace ( ) ;
auto & second_value = values . next_token ( ) ;
if ( second_value . is ( Token : : Type : : EndOfFile ) ) {
// '+'?†n
return make_return_value ( ) ;
} else if ( is_signed_integer ( second_value ) ) {
// '+'?†n <signed-integer>
b = second_value . token ( ) . to_integer ( ) ;
return make_return_value ( ) ;
2021-07-08 13:44:01 +01:00
}
2021-07-24 21:22:44 +01:00
values . skip_whitespace ( ) ;
auto & third_value = values . next_token ( ) ;
if ( ( is_delim ( second_value , " + " sv ) | | is_delim ( second_value , " - " sv ) ) & & is_signless_integer ( third_value ) ) {
// '+'?†n ['+' | '-'] <signless-integer>
b = third_value . token ( ) . to_integer ( ) * ( is_delim ( second_value , " + " sv ) ? 1 : - 1 ) ;
return make_return_value ( ) ;
}
return syntax_error ( ) ;
2021-07-08 13:44:01 +01:00
}
2021-07-24 21:22:44 +01:00
// '+'?†n- <signless-integer>
if ( is_ndash ( first_after_plus ) ) {
values . skip_whitespace ( ) ;
auto & second_value = values . next_token ( ) ;
if ( is_signless_integer ( second_value ) ) {
a = 1 ;
b = - second_value . token ( ) . to_integer ( ) ;
return make_return_value ( ) ;
}
return syntax_error ( ) ;
}
// '+'?†<ndashdigit-ident>
if ( is_ndashdigit_ident ( first_after_plus ) ) {
a = 1 ;
auto maybe_b = first_after_plus . token ( ) . ident ( ) . substring_view ( 1 ) . to_int ( ) ;
if ( maybe_b . has_value ( ) ) {
b = maybe_b . value ( ) ;
return make_return_value ( ) ;
}
return syntax_error ( ) ;
}
2021-07-08 13:44:01 +01:00
2021-07-24 21:22:44 +01:00
return syntax_error ( ) ;
2021-07-08 13:44:01 +01:00
}
2021-07-28 16:30:59 +01:00
OwnPtr < CalculatedStyleValue : : CalcSum > Parser : : parse_calc_expression ( ParsingContext const & context , Vector < StyleComponentValueRule > const & values )
{
auto tokens = TokenStream ( values ) ;
return parse_calc_sum ( context , tokens ) ;
}
Optional < CalculatedStyleValue : : CalcValue > Parser : : parse_calc_value ( ParsingContext const & context , TokenStream < StyleComponentValueRule > & tokens )
{
auto current_token = tokens . next_token ( ) ;
if ( current_token . is_block ( ) & & current_token . block ( ) . is_paren ( ) ) {
auto block_values = TokenStream ( current_token . block ( ) . values ( ) ) ;
auto parsed_calc_sum = parse_calc_sum ( context , block_values ) ;
if ( ! parsed_calc_sum )
return { } ;
return ( CalculatedStyleValue : : CalcValue ) { parsed_calc_sum . release_nonnull ( ) } ;
}
if ( current_token . is ( Token : : Type : : Number ) ) {
auto try_the_number = try_parse_float ( current_token . token ( ) . number_string_value ( ) ) ;
if ( try_the_number . has_value ( ) )
return ( CalculatedStyleValue : : CalcValue ) { try_the_number . value ( ) } ;
return { } ;
}
if ( current_token . is ( Token : : Type : : Dimension ) | | current_token . is ( Token : : Type : : Percentage ) ) {
auto maybe_length = parse_length ( context , current_token ) ;
if ( maybe_length . has_value ( ) & & ! maybe_length . value ( ) . is_undefined ( ) )
return ( CalculatedStyleValue : : CalcValue ) { maybe_length . value ( ) } ;
return { } ;
}
return { } ;
}
OwnPtr < CalculatedStyleValue : : CalcProductPartWithOperator > Parser : : parse_calc_product_part_with_operator ( ParsingContext const & context , TokenStream < StyleComponentValueRule > & tokens )
{
auto product_with_operator = make < CalculatedStyleValue : : CalcProductPartWithOperator > ( ) ;
tokens . skip_whitespace ( ) ;
auto & op_token = tokens . peek_token ( ) ;
if ( ! op_token . is ( Token : : Type : : Delim ) )
return nullptr ;
auto op = op_token . token ( ) . delim ( ) ;
if ( op = = " * " sv ) {
tokens . next_token ( ) ;
tokens . skip_whitespace ( ) ;
product_with_operator - > op = CalculatedStyleValue : : CalcProductPartWithOperator : : Multiply ;
auto parsed_calc_value = parse_calc_value ( context , tokens ) ;
if ( ! parsed_calc_value . has_value ( ) )
return nullptr ;
product_with_operator - > value = { parsed_calc_value . release_value ( ) } ;
} else if ( op = = " / " sv ) {
tokens . next_token ( ) ;
tokens . skip_whitespace ( ) ;
product_with_operator - > op = CalculatedStyleValue : : CalcProductPartWithOperator : : Divide ;
auto parsed_calc_number_value = parse_calc_number_value ( context , tokens ) ;
if ( ! parsed_calc_number_value . has_value ( ) )
return nullptr ;
product_with_operator - > value = { parsed_calc_number_value . release_value ( ) } ;
} else {
return nullptr ;
}
return product_with_operator ;
}
OwnPtr < CalculatedStyleValue : : CalcNumberProductPartWithOperator > Parser : : parse_calc_number_product_part_with_operator ( ParsingContext const & context , TokenStream < StyleComponentValueRule > & tokens )
{
auto number_product_with_operator = make < CalculatedStyleValue : : CalcNumberProductPartWithOperator > ( ) ;
tokens . skip_whitespace ( ) ;
auto & op_token = tokens . peek_token ( ) ;
if ( ! op_token . is ( Token : : Type : : Delim ) )
return nullptr ;
auto op = op_token . token ( ) . delim ( ) ;
if ( op = = " * " sv ) {
tokens . next_token ( ) ;
tokens . skip_whitespace ( ) ;
number_product_with_operator - > op = CalculatedStyleValue : : CalcNumberProductPartWithOperator : : Multiply ;
} else if ( op = = " / " sv ) {
tokens . next_token ( ) ;
tokens . skip_whitespace ( ) ;
number_product_with_operator - > op = CalculatedStyleValue : : CalcNumberProductPartWithOperator : : Divide ;
} else {
return nullptr ;
}
auto parsed_calc_value = parse_calc_number_value ( context , tokens ) ;
if ( ! parsed_calc_value . has_value ( ) )
return nullptr ;
number_product_with_operator - > value = parsed_calc_value . release_value ( ) ;
return number_product_with_operator ;
}
OwnPtr < CalculatedStyleValue : : CalcNumberProduct > Parser : : parse_calc_number_product ( ParsingContext const & context , TokenStream < StyleComponentValueRule > & tokens )
{
auto calc_number_product = make < CalculatedStyleValue : : CalcNumberProduct > ( ) ;
auto first_calc_number_value_or_error = parse_calc_number_value ( context , tokens ) ;
if ( ! first_calc_number_value_or_error . has_value ( ) )
return nullptr ;
calc_number_product - > first_calc_number_value = first_calc_number_value_or_error . release_value ( ) ;
while ( tokens . has_next_token ( ) ) {
auto number_product_with_operator = parse_calc_number_product_part_with_operator ( context , tokens ) ;
if ( ! number_product_with_operator )
break ;
calc_number_product - > zero_or_more_additional_calc_number_values . append ( number_product_with_operator . release_nonnull ( ) ) ;
}
return calc_number_product ;
}
OwnPtr < CalculatedStyleValue : : CalcNumberSumPartWithOperator > Parser : : parse_calc_number_sum_part_with_operator ( ParsingContext const & context , TokenStream < StyleComponentValueRule > & tokens )
{
if ( ! ( tokens . peek_token ( ) . is ( Token : : Type : : Delim )
& & tokens . peek_token ( ) . token ( ) . delim ( ) . is_one_of ( " + " sv , " - " sv )
& & tokens . peek_token ( 1 ) . is ( Token : : Type : : Whitespace ) ) )
return nullptr ;
auto & token = tokens . next_token ( ) ;
tokens . skip_whitespace ( ) ;
CalculatedStyleValue : : CalcNumberSumPartWithOperator : : Operation op ;
auto delim = token . token ( ) . delim ( ) ;
if ( delim = = " + " sv )
op = CalculatedStyleValue : : CalcNumberSumPartWithOperator : : Operation : : Add ;
else if ( delim = = " - " sv )
op = CalculatedStyleValue : : CalcNumberSumPartWithOperator : : Operation : : Subtract ;
else
return nullptr ;
auto calc_number_product = parse_calc_number_product ( context , tokens ) ;
if ( ! calc_number_product )
return nullptr ;
return make < CalculatedStyleValue : : CalcNumberSumPartWithOperator > ( op , calc_number_product . release_nonnull ( ) ) ;
}
OwnPtr < CalculatedStyleValue : : CalcNumberSum > Parser : : parse_calc_number_sum ( ParsingContext const & context , TokenStream < StyleComponentValueRule > & tokens )
{
auto first_calc_number_product_or_error = parse_calc_number_product ( context , tokens ) ;
if ( ! first_calc_number_product_or_error )
return nullptr ;
NonnullOwnPtrVector < CalculatedStyleValue : : CalcNumberSumPartWithOperator > additional { } ;
while ( tokens . has_next_token ( ) ) {
auto calc_sum_part = parse_calc_number_sum_part_with_operator ( context , tokens ) ;
if ( ! calc_sum_part )
return nullptr ;
additional . append ( calc_sum_part . release_nonnull ( ) ) ;
}
tokens . skip_whitespace ( ) ;
auto calc_number_sum = make < CalculatedStyleValue : : CalcNumberSum > ( first_calc_number_product_or_error . release_nonnull ( ) , move ( additional ) ) ;
return calc_number_sum ;
}
Optional < CalculatedStyleValue : : CalcNumberValue > Parser : : parse_calc_number_value ( ParsingContext const & context , TokenStream < StyleComponentValueRule > & tokens )
{
auto & first = tokens . peek_token ( ) ;
if ( first . is_block ( ) & & first . block ( ) . is_paren ( ) ) {
tokens . next_token ( ) ;
auto block_values = TokenStream ( first . block ( ) . values ( ) ) ;
auto calc_number_sum = parse_calc_number_sum ( context , block_values ) ;
if ( calc_number_sum )
return { calc_number_sum . release_nonnull ( ) } ;
}
if ( ! first . is ( Token : : Type : : Number ) )
return { } ;
tokens . next_token ( ) ;
auto try_the_number = try_parse_float ( first . token ( ) . number_string_value ( ) ) ;
if ( ! try_the_number . has_value ( ) )
return { } ;
return try_the_number . value ( ) ;
}
OwnPtr < CalculatedStyleValue : : CalcProduct > Parser : : parse_calc_product ( ParsingContext const & context , TokenStream < StyleComponentValueRule > & tokens )
{
auto calc_product = make < CalculatedStyleValue : : CalcProduct > ( ) ;
auto first_calc_value_or_error = parse_calc_value ( context , tokens ) ;
if ( ! first_calc_value_or_error . has_value ( ) )
return nullptr ;
calc_product - > first_calc_value = first_calc_value_or_error . release_value ( ) ;
while ( tokens . has_next_token ( ) ) {
auto product_with_operator = parse_calc_product_part_with_operator ( context , tokens ) ;
if ( ! product_with_operator )
break ;
calc_product - > zero_or_more_additional_calc_values . append ( product_with_operator . release_nonnull ( ) ) ;
}
return calc_product ;
}
OwnPtr < CalculatedStyleValue : : CalcSumPartWithOperator > Parser : : parse_calc_sum_part_with_operator ( ParsingContext const & context , TokenStream < StyleComponentValueRule > & tokens )
{
// The following has to have the shape of <Whitespace><+ or -><Whitespace>
// But the first whitespace gets eaten in parse_calc_product_part_with_operator().
if ( ! ( tokens . peek_token ( ) . is ( Token : : Type : : Delim )
& & tokens . peek_token ( ) . token ( ) . delim ( ) . is_one_of ( " + " sv , " - " sv )
& & tokens . peek_token ( 1 ) . is ( Token : : Type : : Whitespace ) ) )
return nullptr ;
auto & token = tokens . next_token ( ) ;
tokens . skip_whitespace ( ) ;
CalculatedStyleValue : : CalcSumPartWithOperator : : Operation op ;
auto delim = token . token ( ) . delim ( ) ;
if ( delim = = " + " sv )
op = CalculatedStyleValue : : CalcSumPartWithOperator : : Operation : : Add ;
else if ( delim = = " - " sv )
op = CalculatedStyleValue : : CalcSumPartWithOperator : : Operation : : Subtract ;
else
return nullptr ;
auto calc_product = parse_calc_product ( context , tokens ) ;
if ( ! calc_product )
return nullptr ;
return make < CalculatedStyleValue : : CalcSumPartWithOperator > ( op , calc_product . release_nonnull ( ) ) ;
} ;
OwnPtr < CalculatedStyleValue : : CalcSum > Parser : : parse_calc_sum ( ParsingContext const & context , TokenStream < StyleComponentValueRule > & tokens )
{
auto parsed_calc_product = parse_calc_product ( context , tokens ) ;
if ( ! parsed_calc_product )
return nullptr ;
NonnullOwnPtrVector < CalculatedStyleValue : : CalcSumPartWithOperator > additional { } ;
while ( tokens . has_next_token ( ) ) {
auto calc_sum_part = parse_calc_sum_part_with_operator ( context , tokens ) ;
if ( ! calc_sum_part )
return nullptr ;
additional . append ( calc_sum_part . release_nonnull ( ) ) ;
}
tokens . skip_whitespace ( ) ;
return make < CalculatedStyleValue : : CalcSum > ( parsed_calc_product . release_nonnull ( ) , move ( additional ) ) ;
}
2021-03-22 17:41:47 +01:00
}