| 
									
										
										
										
											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-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-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> | 
					
						
							|  |  |  | T const& TokenStream<T>::peek_token() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!has_next_token()) | 
					
						
							|  |  |  |         return m_eof; | 
					
						
							| 
									
										
										
										
											2021-03-22 17:41:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 14:00:41 +01:00
										 |  |  |     return m_tokens.at(m_iterator_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); | 
					
						
							|  |  |  |     dump_sheet(stylesheet); | 
					
						
							|  |  |  |     return stylesheet; | 
					
						
							| 
									
										
										
										
											2021-07-03 12:34:25 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-03-22 17:41:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 17:30:40 +01:00
										 |  |  | NonnullRefPtrVector<Selector> 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-12 17:30:40 +01:00
										 |  |  | NonnullRefPtrVector<Selector> 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-03 15:40:06 +01:00
										 |  |  |     auto comma_separated_lists = parse_as_comma_separated_list_of_component_values(tokens); | 
					
						
							| 
									
										
										
										
											2021-07-12 17:30:40 +01:00
										 |  |  |     NonnullRefPtrVector<Selector> selectors; | 
					
						
							| 
									
										
										
										
											2021-07-03 12:34:25 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |     for (auto& selector_parts : comma_separated_lists) { | 
					
						
							|  |  |  |         auto stream = TokenStream(selector_parts); | 
					
						
							|  |  |  |         auto selector = parse_single_selector(stream); | 
					
						
							| 
									
										
										
										
											2021-07-12 17:30:40 +01:00
										 |  |  |         if (selector) | 
					
						
							|  |  |  |             selectors.append(selector.release_nonnull()); | 
					
						
							| 
									
										
										
										
											2021-03-22 17:41:47 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 12:34:25 +01:00
										 |  |  |     return selectors; | 
					
						
							| 
									
										
										
										
											2021-03-22 17:41:47 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 17:30:40 +01:00
										 |  |  | NonnullRefPtrVector<Selector> Parser::parse_a_relative_selector() | 
					
						
							| 
									
										
										
										
											2021-03-22 17:41:47 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |     return parse_a_relative_selector(m_token_stream); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template<typename T> | 
					
						
							| 
									
										
										
										
											2021-07-12 17:30:40 +01:00
										 |  |  | NonnullRefPtrVector<Selector> Parser::parse_a_relative_selector(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_a_relative_selector"); | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							|  |  |  |         auto selector = parse_single_selector(stream, true); | 
					
						
							| 
									
										
										
										
											2021-07-12 17:30:40 +01:00
										 |  |  |         if (selector) | 
					
						
							|  |  |  |             selectors.append(selector.release_nonnull()); | 
					
						
							| 
									
										
										
										
											2021-07-03 12:34:25 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return selectors; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  | template<typename T> | 
					
						
							| 
									
										
										
										
											2021-07-12 17:30:40 +01:00
										 |  |  | RefPtr<Selector> Parser::parse_single_selector(TokenStream<T>& tokens, bool is_relative) | 
					
						
							| 
									
										
										
										
											2021-07-03 12:34:25 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-07-23 13:06:31 +01:00
										 |  |  |     dbgln_if(CSS_PARSER_DEBUG, "Parser::parse_single_selector"); | 
					
						
							| 
									
										
										
										
											2021-07-09 17:07:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 12:34:25 +01:00
										 |  |  |     // FIXME: Bring this all in line with the spec. https://www.w3.org/TR/selectors-4/
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-23 15:24:33 +01:00
										 |  |  |     Vector<Selector::CompoundSelector> selectors; | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-07 17:47:17 +01:00
										 |  |  |     auto check_for_eof_or_whitespace = [&](T& current_value) -> bool { | 
					
						
							|  |  |  |         if (current_value.is(Token::Type::EndOfFile)) | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (current_value.is(Token::Type::Whitespace)) { | 
					
						
							|  |  |  |             tokens.reconsume_current_input_token(); | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-09 21:02:18 +01:00
										 |  |  |     auto parse_simple_selector = [&]() -> Optional<Selector::SimpleSelector> { | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |         auto current_value = tokens.next_token(); | 
					
						
							| 
									
										
										
										
											2021-07-07 17:47:17 +01:00
										 |  |  |         if (check_for_eof_or_whitespace(current_value)) | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |             return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |         Selector::SimpleSelector simple_selector; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |         // FIXME: Handle namespace prefixes.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |         if (current_value.is(Token::Type::Delim) && ((Token)current_value).delim() == "*") { | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |             simple_selector.type = Selector::SimpleSelector::Type::Universal; | 
					
						
							|  |  |  |         } else if (current_value.is(Token::Type::Hash)) { | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |             if (((Token)current_value).m_hash_type != Token::HashType::Id) { | 
					
						
							| 
									
										
										
										
											2021-07-07 21:29:24 +01:00
										 |  |  |                 dbgln("Selector contains hash token that is not an id: {}", current_value.to_debug_string()); | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 return {}; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |             simple_selector.type = Selector::SimpleSelector::Type::Id; | 
					
						
							|  |  |  |             simple_selector.value = ((Token)current_value).m_value.to_string(); | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |         } else if (current_value.is(Token::Type::Delim) && ((Token)current_value).delim() == ".") { | 
					
						
							|  |  |  |             current_value = tokens.next_token(); | 
					
						
							| 
									
										
										
										
											2021-07-07 17:47:17 +01:00
										 |  |  |             if (check_for_eof_or_whitespace(current_value)) | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-09 20:54:06 +01:00
										 |  |  |             if (!current_value.is(Token::Type::Ident)) { | 
					
						
							| 
									
										
										
										
											2021-07-07 21:29:24 +01:00
										 |  |  |                 dbgln("Expected an ident after '.', got: {}", current_value.to_debug_string()); | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 return {}; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |             simple_selector.type = Selector::SimpleSelector::Type::Class; | 
					
						
							|  |  |  |             simple_selector.value = current_value.token().ident().to_lowercase_string(); | 
					
						
							| 
									
										
										
										
											2021-07-07 20:47:09 +01:00
										 |  |  |         } else if (current_value.is(Token::Type::Ident)) { | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |             simple_selector.type = Selector::SimpleSelector::Type::TagName; | 
					
						
							|  |  |  |             simple_selector.value = current_value.token().ident().to_lowercase_string(); | 
					
						
							|  |  |  |         } else if (current_value.is_block() && current_value.block().is_square()) { | 
					
						
							|  |  |  |             simple_selector.type = Selector::SimpleSelector::Type::Attribute; | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |             auto& attribute = simple_selector.attribute; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |             Vector<StyleComponentValueRule> const& attribute_parts = current_value.block().values(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |             if (attribute_parts.is_empty()) { | 
					
						
							|  |  |  |                 dbgln("CSS attribute selector is empty!"); | 
					
						
							|  |  |  |                 return {}; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |             // FIXME: Handle namespace prefix for attribute name.
 | 
					
						
							|  |  |  |             auto& attribute_part = attribute_parts.first(); | 
					
						
							| 
									
										
										
										
											2021-07-09 20:54:06 +01:00
										 |  |  |             if (!attribute_part.is(Token::Type::Ident)) { | 
					
						
							| 
									
										
										
										
											2021-07-07 21:29:24 +01:00
										 |  |  |                 dbgln("Expected ident for attribute name, got: '{}'", attribute_part.to_debug_string()); | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |                 return {}; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |             attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::HasAttribute; | 
					
						
							|  |  |  |             attribute.name = attribute_part.token().ident(); | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-07 20:47:09 +01:00
										 |  |  |             if (attribute_parts.size() == 1) | 
					
						
							|  |  |  |                 return simple_selector; | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-07 20:47:09 +01:00
										 |  |  |             size_t attribute_index = 1; | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |             auto& delim_part = attribute_parts.at(attribute_index); | 
					
						
							| 
									
										
										
										
											2021-07-09 20:54:06 +01:00
										 |  |  |             if (!delim_part.is(Token::Type::Delim)) { | 
					
						
							| 
									
										
										
										
											2021-07-07 21:29:24 +01:00
										 |  |  |                 dbgln("Expected a delim for attribute comparison, got: '{}'", delim_part.to_debug_string()); | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |                 return {}; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (delim_part.token().delim() == "=") { | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |                 attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch; | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |                 attribute_index++; | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |             } else { | 
					
						
							|  |  |  |                 attribute_index++; | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |                 if (attribute_index >= attribute_parts.size()) { | 
					
						
							|  |  |  |                     dbgln("Attribute selector ended part way through a match type."); | 
					
						
							|  |  |  |                     return {}; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |                 auto& delim_second_part = attribute_parts.at(attribute_index); | 
					
						
							| 
									
										
										
										
											2021-07-07 20:47:09 +01:00
										 |  |  |                 if (!(delim_second_part.is(Token::Type::Delim) && delim_second_part.token().delim() == "=")) { | 
					
						
							| 
									
										
										
										
											2021-07-07 21:29:24 +01:00
										 |  |  |                     dbgln("Expected a double delim for attribute comparison, got: '{}{}'", delim_part.to_debug_string(), delim_second_part.to_debug_string()); | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |                     return {}; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (delim_part.token().delim() == "~") { | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |                     attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::ContainsWord; | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |                     attribute_index++; | 
					
						
							| 
									
										
										
										
											2021-07-01 15:35:24 +01:00
										 |  |  |                 } else if (delim_part.token().delim() == "*") { | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |                     attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::ContainsString; | 
					
						
							| 
									
										
										
										
											2021-07-01 15:35:24 +01:00
										 |  |  |                     attribute_index++; | 
					
						
							|  |  |  |                 } else if (delim_part.token().delim() == "|") { | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |                     attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::StartsWithSegment; | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |                     attribute_index++; | 
					
						
							| 
									
										
										
										
											2021-07-01 15:35:24 +01:00
										 |  |  |                 } else if (delim_part.token().delim() == "^") { | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |                     attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::StartsWithString; | 
					
						
							| 
									
										
										
										
											2021-07-01 15:35:24 +01:00
										 |  |  |                     attribute_index++; | 
					
						
							|  |  |  |                 } else if (delim_part.token().delim() == "$") { | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |                     attribute.match_type = Selector::SimpleSelector::Attribute::MatchType::EndsWithString; | 
					
						
							| 
									
										
										
										
											2021-07-01 15:35:24 +01:00
										 |  |  |                     attribute_index++; | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |             if (attribute_index >= attribute_parts.size()) { | 
					
						
							|  |  |  |                 dbgln("Attribute selector ended without a value to match."); | 
					
						
							|  |  |  |                 return {}; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |             auto& value_part = attribute_parts.at(attribute_index); | 
					
						
							| 
									
										
										
										
											2021-07-09 20:54:06 +01:00
										 |  |  |             if (!value_part.is(Token::Type::Ident) && !value_part.is(Token::Type::String)) { | 
					
						
							| 
									
										
										
										
											2021-07-07 21:29:24 +01:00
										 |  |  |                 dbgln("Expected a string or ident for the value to match attribute against, got: '{}'", value_part.to_debug_string()); | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |                 return {}; | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-07-12 14:58:03 +01:00
										 |  |  |             attribute.value = value_part.token().is(Token::Type::Ident) ? value_part.token().ident() : value_part.token().string(); | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-01 14:54:27 +01:00
										 |  |  |             // FIXME: Handle case-sensitivity suffixes. https://www.w3.org/TR/selectors-4/#attribute-case
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |         } else if (current_value.is(Token::Type::Colon)) { | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |             bool is_pseudo = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |             current_value = tokens.next_token(); | 
					
						
							| 
									
										
										
										
											2021-07-07 17:47:17 +01:00
										 |  |  |             if (check_for_eof_or_whitespace(current_value)) | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |                 return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-09 20:54:06 +01:00
										 |  |  |             if (current_value.is(Token::Type::Colon)) { | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |                 is_pseudo = true; | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |                 current_value = tokens.next_token(); | 
					
						
							| 
									
										
										
										
											2021-07-07 17:47:17 +01:00
										 |  |  |                 if (check_for_eof_or_whitespace(current_value)) | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                     return {}; | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:34:18 +01:00
										 |  |  |             if (is_pseudo) { | 
					
						
							|  |  |  |                 auto pseudo_name = ((Token)current_value).ident(); | 
					
						
							|  |  |  |                 simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (pseudo_name.equals_ignoring_case("before")) { | 
					
						
							|  |  |  |                     simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::Before; | 
					
						
							|  |  |  |                 } else if (pseudo_name.equals_ignoring_case("after")) { | 
					
						
							|  |  |  |                     simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::After; | 
					
						
							|  |  |  |                 } else if (pseudo_name.equals_ignoring_case("first-line")) { | 
					
						
							|  |  |  |                     simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::FirstLine; | 
					
						
							|  |  |  |                 } else if (pseudo_name.equals_ignoring_case("first-letter")) { | 
					
						
							|  |  |  |                     simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::FirstLetter; | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     return {}; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return simple_selector; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |             auto& pseudo_class = simple_selector.pseudo_class; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |             current_value = tokens.next_token(); | 
					
						
							| 
									
										
										
										
											2021-07-07 17:47:17 +01:00
										 |  |  |             if (check_for_eof_or_whitespace(current_value)) | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                 return {}; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |             simple_selector.type = Selector::SimpleSelector::Type::PseudoClass; | 
					
						
							| 
									
										
										
										
											2021-07-09 20:54:06 +01:00
										 |  |  |             if (current_value.is(Token::Type::Ident)) { | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |                 auto pseudo_name = ((Token)current_value).ident(); | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 if (pseudo_name.equals_ignoring_case("link")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Link; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("visited")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Visited; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("active")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Active; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("hover")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Hover; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("focus")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Focus; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("first-child")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::FirstChild; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("last-child")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::LastChild; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("only-child")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::OnlyChild; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("empty")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Empty; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("root")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Root; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("first-of-type")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::FirstOfType; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("last-of-type")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::LastOfType; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("before")) { | 
					
						
							| 
									
										
										
										
											2021-07-09 21:02:18 +01:00
										 |  |  |                     simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::Before; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("after")) { | 
					
						
							| 
									
										
										
										
											2021-07-09 21:02:18 +01:00
										 |  |  |                     simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::After; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("disabled")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Disabled; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("enabled")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Enabled; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("checked")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Checked; | 
					
						
							| 
									
										
										
										
											2021-07-12 16:34:18 +01:00
										 |  |  |                 } else if (pseudo_name.equals_ignoring_case("before")) { | 
					
						
							|  |  |  |                     // 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::Before; | 
					
						
							|  |  |  |                 } else if (pseudo_name.equals_ignoring_case("after")) { | 
					
						
							|  |  |  |                     // See :before
 | 
					
						
							|  |  |  |                     simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; | 
					
						
							|  |  |  |                     simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::After; | 
					
						
							|  |  |  |                 } else if (pseudo_name.equals_ignoring_case("first-line")) { | 
					
						
							|  |  |  |                     // See :before
 | 
					
						
							|  |  |  |                     simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; | 
					
						
							|  |  |  |                     simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::FirstLine; | 
					
						
							|  |  |  |                 } else if (pseudo_name.equals_ignoring_case("first-letter")) { | 
					
						
							|  |  |  |                     // See :before
 | 
					
						
							|  |  |  |                     simple_selector.type = Selector::SimpleSelector::Type::PseudoElement; | 
					
						
							|  |  |  |                     simple_selector.pseudo_element = Selector::SimpleSelector::PseudoElement::FirstLetter; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else { | 
					
						
							|  |  |  |                     dbgln("Unknown pseudo class: '{}'", pseudo_name); | 
					
						
							|  |  |  |                     return simple_selector; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-07-03 15:06:53 +01:00
										 |  |  |             } else if (current_value.is(Token::Type::Function)) { | 
					
						
							| 
									
										
										
										
											2021-07-01 14:13:48 +01:00
										 |  |  |                 auto& pseudo_function = current_value.function(); | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 if (pseudo_function.name().equals_ignoring_case("nth-child")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthChild; | 
					
						
							| 
									
										
										
										
											2021-07-08 13:44:01 +01:00
										 |  |  |                     auto function_values = TokenStream<StyleComponentValueRule>(pseudo_function.values()); | 
					
						
							|  |  |  |                     auto nth_child_pattern = parse_nth_child_pattern(function_values); | 
					
						
							|  |  |  |                     if (nth_child_pattern.has_value()) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                         pseudo_class.nth_child_pattern = nth_child_pattern.value(); | 
					
						
							| 
									
										
										
										
											2021-07-08 13:44:01 +01:00
										 |  |  |                     } else { | 
					
						
							|  |  |  |                         dbgln("Invalid nth-child format"); | 
					
						
							|  |  |  |                         return {}; | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_function.name().equals_ignoring_case("nth-last-child")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::NthLastChild; | 
					
						
							| 
									
										
										
										
											2021-07-08 13:44:01 +01:00
										 |  |  |                     auto function_values = TokenStream<StyleComponentValueRule>(pseudo_function.values()); | 
					
						
							|  |  |  |                     auto nth_child_pattern = parse_nth_child_pattern(function_values); | 
					
						
							|  |  |  |                     if (nth_child_pattern.has_value()) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                         pseudo_class.nth_child_pattern = nth_child_pattern.value(); | 
					
						
							| 
									
										
										
										
											2021-07-08 13:44:01 +01:00
										 |  |  |                     } else { | 
					
						
							|  |  |  |                         dbgln("Invalid nth-child format"); | 
					
						
							|  |  |  |                         return {}; | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else if (pseudo_function.name().equals_ignoring_case("not")) { | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |                     pseudo_class.type = Selector::SimpleSelector::PseudoClass::Type::Not; | 
					
						
							| 
									
										
										
										
											2021-07-12 17:58:47 +01:00
										 |  |  |                     auto function_token_stream = TokenStream(pseudo_function.values()); | 
					
						
							|  |  |  |                     pseudo_class.not_selector = parse_a_selector(function_token_stream); | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } else { | 
					
						
							|  |  |  |                     dbgln("Unknown pseudo class: '{}'()", pseudo_function.name()); | 
					
						
							| 
									
										
										
										
											2021-07-08 13:44:01 +01:00
										 |  |  |                     return {}; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2021-07-07 21:29:24 +01:00
										 |  |  |                 dbgln("Unexpected Block in pseudo-class name, expected a function or identifier. '{}'", current_value.to_debug_string()); | 
					
						
							|  |  |  |                 return {}; | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-07-12 16:18:00 +01:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             dbgln("Invalid simple selector!"); | 
					
						
							|  |  |  |             return {}; | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return simple_selector; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-23 15:24:33 +01:00
										 |  |  |     auto parse_complex_selector = [&]() -> Optional<Selector::CompoundSelector> { | 
					
						
							|  |  |  |         auto combinator = Selector::Combinator::Descendant; | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-07 17:47:17 +01:00
										 |  |  |         tokens.skip_whitespace(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-22 21:47:35 +01:00
										 |  |  |         auto& current_value = tokens.peek_token(); | 
					
						
							| 
									
										
										
										
											2021-07-09 20:54:06 +01:00
										 |  |  |         if (current_value.is(Token::Type::Delim)) { | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |             auto delim = ((Token)current_value).delim(); | 
					
						
							| 
									
										
										
										
											2021-07-02 13:01:38 +01:00
										 |  |  |             if (delim == ">") { | 
					
						
							| 
									
										
										
										
											2021-07-23 15:24:33 +01:00
										 |  |  |                 combinator = Selector::Combinator::ImmediateChild; | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |                 tokens.next_token(); | 
					
						
							| 
									
										
										
										
											2021-07-02 13:01:38 +01:00
										 |  |  |             } else if (delim == "+") { | 
					
						
							| 
									
										
										
										
											2021-07-23 15:24:33 +01:00
										 |  |  |                 combinator = Selector::Combinator::NextSibling; | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |                 tokens.next_token(); | 
					
						
							| 
									
										
										
										
											2021-07-02 13:01:38 +01:00
										 |  |  |             } else if (delim == "~") { | 
					
						
							| 
									
										
										
										
											2021-07-23 15:24:33 +01:00
										 |  |  |                 combinator = Selector::Combinator::SubsequentSibling; | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |                 tokens.next_token(); | 
					
						
							| 
									
										
										
										
											2021-07-02 13:01:38 +01:00
										 |  |  |             } else if (delim == "|") { | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |                 tokens.next_token(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-22 21:47:35 +01:00
										 |  |  |                 auto& next = tokens.peek_token(); | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |                 if (next.is(Token::Type::EndOfFile)) | 
					
						
							| 
									
										
										
										
											2021-07-02 13:01:38 +01:00
										 |  |  |                     return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-09 20:54:06 +01:00
										 |  |  |                 if (next.is(Token::Type::Delim) && next.token().delim() == "|") { | 
					
						
							| 
									
										
										
										
											2021-07-23 15:24:33 +01:00
										 |  |  |                     combinator = Selector::Combinator::Column; | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |                     tokens.next_token(); | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-07 17:47:17 +01:00
										 |  |  |         tokens.skip_whitespace(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-09 21:02:18 +01:00
										 |  |  |         Vector<Selector::SimpleSelector> simple_selectors; | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for (;;) { | 
					
						
							| 
									
										
										
										
											2021-07-22 21:47:35 +01:00
										 |  |  |             auto& current_value = tokens.peek_token(); | 
					
						
							| 
									
										
										
										
											2021-07-07 17:47:17 +01:00
										 |  |  |             if (current_value.is(Token::Type::EndOfFile) || current_value.is(Token::Type::Whitespace)) | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |             auto component = parse_simple_selector(); | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |             if (!component.has_value()) | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |             simple_selectors.append(component.value()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (simple_selectors.is_empty()) | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-23 15:24:33 +01:00
										 |  |  |         return Selector::CompoundSelector { combinator, move(simple_selectors) }; | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (;;) { | 
					
						
							| 
									
										
										
										
											2021-07-22 21:47:35 +01:00
										 |  |  |         auto& current_value = tokens.peek_token(); | 
					
						
							| 
									
										
										
										
											2021-07-03 15:40:06 +01:00
										 |  |  |         if (current_value.is(Token::Type::EndOfFile)) | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |             break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-07 17:47:17 +01:00
										 |  |  |         auto complex = parse_complex_selector(); | 
					
						
							|  |  |  |         if (complex.has_value()) | 
					
						
							|  |  |  |             selectors.append(complex.value()); | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 16:27:37 +01:00
										 |  |  |     if (selectors.is_empty()) | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  |         return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-03 12:34:25 +01:00
										 |  |  |     if (!is_relative) | 
					
						
							| 
									
										
										
										
											2021-07-23 15:24:33 +01:00
										 |  |  |         selectors.first().combinator = Selector::Combinator::None; | 
					
						
							| 
									
										
										
										
											2021-04-29 21:16:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 17:30:40 +01:00
										 |  |  |     return Selector::create(move(selectors)); | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (component_value.is(Token::Type::Url)) | 
					
						
							|  |  |  |         return context.complete_url(component_value.token().url()); | 
					
						
							|  |  |  |     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)) { | 
					
						
							|  |  |  |                 // FIXME: RFC2397
 | 
					
						
							|  |  |  |                 if (value.token().string().starts_with("data:")) | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 return context.complete_url(value.token().string()); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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-08 21:53:22 +01:00
										 |  |  |         auto declaration = convert_to_declaration(*rule->m_block); | 
					
						
							|  |  |  |         if (declaration && !selectors.is_empty()) | 
					
						
							|  |  |  |             return CSSStyleRule::create(move(selectors), move(*declaration)); | 
					
						
							| 
									
										
										
										
											2021-07-06 16:05:12 +01:00
										 |  |  |         else | 
					
						
							|  |  |  |             dbgln("Discarding invalid/unsupported style rule: '{}'", rule->to_string()); | 
					
						
							| 
									
										
										
										
											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-18 17:12:23 +01:00
										 |  |  | RefPtr<StyleValue> Parser::parse_keyword_or_custom_value(ParsingContext const&, 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-18 17:12:23 +01:00
										 |  |  |     if (component_value.is_function() && component_value.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-09 21:04:34 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-18 17:12:23 +01:00
										 |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RefPtr<StyleValue> Parser::parse_length_value(ParsingContext const& context, StyleComponentValueRule const& component_value) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-07-09 21:04:34 +01:00
										 |  |  |     auto parse_length = [&]() -> Optional<Length> { | 
					
						
							|  |  |  |         Length::Type type = Length::Type::Undefined; | 
					
						
							|  |  |  |         Optional<float> numeric_value; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 16:20:06 +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(); | 
					
						
							| 
									
										
										
										
											2021-07-09 21:04:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (unit_string.equals_ignoring_case("%")) { | 
					
						
							|  |  |  |                 type = Length::Type::Percentage; | 
					
						
							|  |  |  |             } 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; | 
					
						
							| 
									
										
										
										
											2021-07-18 17:12:23 +01:00
										 |  |  |             } else if (context.in_quirks_mode()) { | 
					
						
							| 
									
										
										
										
											2021-07-09 21:04:34 +01:00
										 |  |  |                 type = Length::Type::Px; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             numeric_value = try_parse_float(length_string); | 
					
						
							| 
									
										
										
										
											2021-07-14 16:20:06 +01:00
										 |  |  |         } else if (component_value.is(Token::Type::Number)) { | 
					
						
							|  |  |  |             auto value_string = component_value.token().m_value.string_view(); | 
					
						
							| 
									
										
										
										
											2021-07-09 21:04:34 +01:00
										 |  |  |             if (value_string == "0") { | 
					
						
							|  |  |  |                 type = Length::Type::Px; | 
					
						
							|  |  |  |                 numeric_value = 0; | 
					
						
							| 
									
										
										
										
											2021-07-18 17:12:23 +01:00
										 |  |  |             } else if (context.in_quirks_mode()) { | 
					
						
							| 
									
										
										
										
											2021-07-09 21:04:34 +01:00
										 |  |  |                 type = Length::Type::Px; | 
					
						
							|  |  |  |                 numeric_value = try_parse_float(value_string); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-07-18 17:12:23 +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
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!numeric_value.has_value()) | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return Length(numeric_value.value(), type); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-09 21:04:34 +01:00
										 |  |  |         auto length = parse_length(); | 
					
						
							|  |  |  |         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-23 13:51:46 +01:00
										 |  |  |             return NumericStyleValue::create(number.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 {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RefPtr<StyleValue> Parser::parse_color_value(ParsingContext const&, StyleComponentValueRule const& component_value) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-07-18 21:20:40 +01:00
										 |  |  |     // https://www.w3.org/TR/css-color-3/
 | 
					
						
							| 
									
										
										
										
											2021-07-09 21:04:34 +01:00
										 |  |  |     auto parse_css_color = [&]() -> Optional<Color> { | 
					
						
							| 
									
										
										
										
											2021-07-18 21:20:40 +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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         return {}; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (function.name().equals_ignoring_case("rgb")) { | 
					
						
							|  |  |  |                 if (params.size() != 3) | 
					
						
							|  |  |  |                     return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 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); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } else if (function.name().equals_ignoring_case("rgba")) { | 
					
						
							|  |  |  |                 if (params.size() != 4) | 
					
						
							|  |  |  |                     return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 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); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 } 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); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } else if (function.name().equals_ignoring_case("hsl")) { | 
					
						
							|  |  |  |                 if (params.size() != 3) | 
					
						
							|  |  |  |                     return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 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); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } else if (function.name().equals_ignoring_case("hsla")) { | 
					
						
							|  |  |  |                 if (params.size() != 4) | 
					
						
							|  |  |  |                     return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 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); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return {}; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-07-09 21:04:34 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto color = parse_css_color(); | 
					
						
							|  |  |  |     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-14 16:20:06 +01:00
										 |  |  | RefPtr<StyleValue> Parser::parse_css_value(PropertyID property_id, TokenStream<StyleComponentValueRule>& tokens) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-07-23 13:06:31 +01:00
										 |  |  |     dbgln_if(CSS_PARSER_DEBUG, "Parser::parse_css_value"); | 
					
						
							| 
									
										
										
										
											2021-07-14 16:20:06 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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 {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-07-23 13:06:31 +01:00
										 |  |  |     dbgln_if(CSS_PARSER_DEBUG, "Parser::parse_css_value '{}'", component_value.to_debug_string()); | 
					
						
							| 
									
										
										
										
											2021-07-18 17:12:23 +01:00
										 |  |  |     // 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) { | 
					
						
							|  |  |  |             return LengthStyleValue::create(Length::make_px(number.integer())); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (auto keyword_or_custom = parse_keyword_or_custom_value(context, component_value)) | 
					
						
							|  |  |  |         return keyword_or_custom; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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-08 13:44:01 +01:00
										 |  |  | Optional<Selector::SimpleSelector::NthChildPattern> Parser::parse_nth_child_pattern(TokenStream<StyleComponentValueRule>& values) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-07-23 13:06:31 +01:00
										 |  |  |     dbgln_if(CSS_PARSER_DEBUG, "Parser::parse_nth_child_pattern"); | 
					
						
							| 
									
										
										
										
											2021-07-09 17:07:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-08 13:44:01 +01:00
										 |  |  |     Selector::SimpleSelector::NthChildPattern pattern; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto current_value = values.next_token(); | 
					
						
							|  |  |  |     if (current_value.is(Token::Type::Ident)) { | 
					
						
							|  |  |  |         auto ident = current_value.token().ident(); | 
					
						
							|  |  |  |         if (ident.equals_ignoring_case("odd")) { | 
					
						
							|  |  |  |             pattern.step_size = 2; | 
					
						
							|  |  |  |             pattern.offset = 1; | 
					
						
							|  |  |  |             return pattern; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } else if (ident.equals_ignoring_case("even")) { | 
					
						
							|  |  |  |             pattern.step_size = 2; | 
					
						
							|  |  |  |             return pattern; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Try to match any of following patterns:
 | 
					
						
							|  |  |  |     // 1. An+B
 | 
					
						
							|  |  |  |     // 2. An
 | 
					
						
							|  |  |  |     // 3. B
 | 
					
						
							|  |  |  |     // ...where "A" is "step_size", "B" is "offset" and rest are literals.
 | 
					
						
							|  |  |  |     // "A" can be omitted, in that case "A" = 1.
 | 
					
						
							|  |  |  |     // "A" may have "+" or "-" sign, "B" always must be predated by sign for pattern (1).
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto is_n = [](StyleComponentValueRule value) -> bool { | 
					
						
							|  |  |  |         return value.is(Token::Type::Ident) && value.token().ident().equals_ignoring_case("n"); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto is_delim = [](StyleComponentValueRule value, StringView delim) -> bool { | 
					
						
							|  |  |  |         return value.is(Token::Type::Delim) && value.token().delim().equals_ignoring_case(delim); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int step_size_or_offset = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // "When a=1, or a=-1, the 1 may be omitted from the rule."
 | 
					
						
							|  |  |  |     if (is_n(current_value)) { | 
					
						
							|  |  |  |         step_size_or_offset = +1; | 
					
						
							|  |  |  |     } else if (is_delim(current_value, "+"sv) && is_n(values.peek_token())) { | 
					
						
							|  |  |  |         step_size_or_offset = +1; | 
					
						
							|  |  |  |         values.next_token(); | 
					
						
							|  |  |  |     } else if (is_delim(current_value, "-"sv) && is_n(values.peek_token())) { | 
					
						
							|  |  |  |         step_size_or_offset = -1; | 
					
						
							|  |  |  |         values.next_token(); | 
					
						
							|  |  |  |     } else if (current_value.is(Token::Type::Number)) { | 
					
						
							|  |  |  |         step_size_or_offset = current_value.token().integer(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         values.reconsume_current_input_token(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     current_value = values.next_token(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (is_n(current_value)) { | 
					
						
							|  |  |  |         values.skip_whitespace(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         auto next_value = values.peek_token(); | 
					
						
							|  |  |  |         if (is_delim(next_value, "+") || is_delim(next_value, "-")) { | 
					
						
							|  |  |  |             const auto sign = is_delim(next_value, "+") ? 1 : -1; | 
					
						
							|  |  |  |             values.next_token(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             values.skip_whitespace(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // "An+B" pattern
 | 
					
						
							|  |  |  |             auto number = values.next_token(); | 
					
						
							|  |  |  |             if (!number.is(Token::Type::Number)) | 
					
						
							|  |  |  |                 return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             pattern.step_size = step_size_or_offset; | 
					
						
							|  |  |  |             pattern.offset = sign * number.token().integer(); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             // "An" pattern
 | 
					
						
							|  |  |  |             pattern.step_size = step_size_or_offset; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         // "B" pattern
 | 
					
						
							|  |  |  |         pattern.offset = step_size_or_offset; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (values.has_next_token()) | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return pattern; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-03-22 17:41:47 +01:00
										 |  |  | } |