From 28e8bb9b1d3dfd0cbdf88c46924094cfc55b7e0c Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 8 Jul 2021 21:53:22 +0100 Subject: [PATCH] LibWeb: Convert style declarations and at rules into CSSRules --- .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 127 ++++++++++++++---- Userland/Libraries/LibWeb/CSS/Parser/Parser.h | 24 ++-- 2 files changed, 118 insertions(+), 33 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 88c11f91aa..6db27f766a 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -158,12 +158,14 @@ NonnullRefPtr Parser::parse_as_stylesheet(TokenStream& tokens) NonnullRefPtrVector rules; for (auto& raw_rule : parser_rules) { - auto rule = convert_rule(raw_rule); + auto rule = convert_to_rule(raw_rule); if (rule) rules.append(*rule); } - return CSSStyleSheet::create(rules); + auto stylesheet = CSSStyleSheet::create(rules); + dump_sheet(stylesheet); + return stylesheet; } Vector Parser::parse_a_selector() @@ -574,10 +576,11 @@ NonnullRefPtr Parser::consume_an_at_rule() template NonnullRefPtr Parser::consume_an_at_rule(TokenStream& tokens) { - auto initial = tokens.next_token(); + auto name_ident = tokens.next_token(); + VERIFY(name_ident.is(Token::Type::Ident)); NonnullRefPtr rule = create(StyleRule::Type::At); - rule->m_name = initial.m_value.to_string(); + rule->m_name = ((Token)name_ident).ident(); for (;;) { auto token = tokens.next_token(); @@ -636,9 +639,10 @@ RefPtr Parser::consume_a_qualified_rule(TokenStream& tokens) return rule; } -StyleComponentValueRule Parser::consume_a_component_value() +template<> +StyleComponentValueRule Parser::consume_a_component_value(TokenStream& tokens) { - return consume_a_component_value(m_token_stream); + return tokens.next_token(); } template @@ -655,6 +659,11 @@ StyleComponentValueRule Parser::consume_a_component_value(TokenStream& tokens return StyleComponentValueRule(token); } +StyleComponentValueRule Parser::consume_a_component_value() +{ + return consume_a_component_value(m_token_stream); +} + NonnullRefPtr Parser::consume_a_simple_block() { return consume_a_simple_block(m_token_stream); @@ -740,7 +749,6 @@ Optional Parser::consume_a_declaration(TokenStream& tok tokens.skip_whitespace(); auto colon = tokens.next_token(); - if (!colon.is(Token::Type::Colon)) { log_parse_error(); return {}; @@ -755,18 +763,20 @@ Optional Parser::consume_a_declaration(TokenStream& tok declaration.m_values.append(consume_a_component_value(tokens)); } - auto second_last = declaration.m_values.at(declaration.m_values.size() - 2); - auto last = declaration.m_values.at(declaration.m_values.size() - 1); + 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); - 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; + 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; - 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; + 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; + } } } } @@ -810,7 +820,7 @@ Vector Parser::consume_a_list_of_declarations(TokenStream temp; - temp.append(StyleComponentValueRule(token)); + temp.append(token); for (;;) { auto peek = tokens.peek_token(); @@ -825,13 +835,14 @@ Vector Parser::consume_a_list_of_declarations(TokenStream Parser::parse_as_rule(TokenStream& tokens) if (token.is(Token::Type::EndOfFile)) { return {}; } else if (token.is(Token::Type::AtKeyword)) { - auto at_rule = consume_an_at_rule(tokens); - rule = convert_rule(at_rule); + auto at_rule = consume_an_at_rule(); + rule = convert_to_rule(at_rule); } else { auto qualified_rule = consume_a_qualified_rule(tokens); if (!qualified_rule) return {}; - rule = convert_rule(*qualified_rule); + rule = convert_to_rule(*qualified_rule); } tokens.skip_whitespace(); @@ -887,7 +898,7 @@ NonnullRefPtrVector Parser::parse_as_list_of_rules(TokenStream& toke NonnullRefPtrVector rules; for (auto& rule : parsed_rules) { - auto converted_rule = convert_rule(rule); + auto converted_rule = convert_to_rule(rule); if (converted_rule) rules.append(*converted_rule); } @@ -1007,9 +1018,77 @@ Vector> Parser::parse_as_comma_separated_list_of return lists; } -RefPtr Parser::convert_rule(NonnullRefPtr) +RefPtr Parser::convert_to_rule(NonnullRefPtr rule) { + dbgln("Converting a rule: {}", rule->to_string()); + + if (rule->m_type == StyleRule::Type::At) { + dbgln("... It's an at rule"); + } else { + dbgln("... It's a style rule"); + + auto prelude_stream = TokenStream(rule->m_prelude); + Vector selectors = parse_a_selector(prelude_stream); + auto declaration = convert_to_declaration(*rule->m_block); + if (declaration && !selectors.is_empty()) + return CSSStyleRule::create(move(selectors), move(*declaration)); + } + + dbgln("... discarding because it's invalid or unsupported."); return {}; } +RefPtr Parser::convert_to_declaration(NonnullRefPtr block) +{ + if (!block->is_curly()) + return {}; + + Vector properties; + HashMap custom_properties; + + auto stream = TokenStream(block->m_values); + auto declarations_and_at_rules = consume_a_list_of_declarations(stream); + + for (auto& declaration_or_at_rule : declarations_and_at_rules) { + if (declaration_or_at_rule.is_at_rule()) { + dbgln("CSS::Parser::convert_to_declaration(): Skipping @ rule."); + continue; + } + + auto& declaration = declaration_or_at_rule.m_declaration; + + auto& property_name = declaration.m_name; + auto property_id = property_id_from_string(property_name); + if (property_id == CSS::PropertyID::Invalid && property_name.starts_with("--")) + property_id = CSS::PropertyID::Custom; + + if (property_id == CSS::PropertyID::Invalid && !property_name.starts_with("-")) { + dbgln("CSS::Parser::convert_to_declaration(): Unrecognized property '{}'", property_name); + continue; + } + + auto value_token_stream = TokenStream(declaration.m_values); + auto value = parse_css_value(property_id, value_token_stream); + if (!value) { + dbgln("CSS::Parser::convert_to_declaration(): Property '{}' has no value.", property_name); + continue; + } + + if (property_id == CSS::PropertyID::Custom) { + custom_properties.set(property_name, CSS::StyleProperty { property_id, value.release_nonnull(), declaration.m_name, declaration.m_important }); + } else { + properties.append(CSS::StyleProperty { property_id, value.release_nonnull(), {}, declaration.m_important }); + } + } + + return CSSStyleDeclaration::create(move(properties), move(custom_properties)); +} + +template +RefPtr Parser::parse_css_value(PropertyID, TokenStream&) +{ + // FIXME: This is mostly copied from the old, deprecated parser. It may or may not be to spec. + + return {}; +} } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index 09f694e513..4727f2cea4 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -24,6 +24,8 @@ class CSSStyleSheet; class CSSRule; class CSSStyleRule; struct StyleProperty; +class StyleValue; +enum class PropertyID; class ParsingContext { public: @@ -120,6 +122,9 @@ public: template Vector parse_a_relative_selector(TokenStream&); + template + RefPtr parse_css_value(PropertyID, TokenStream&); + // FIXME: https://drafts.csswg.org/css-backgrounds-3/ static Optional as_valid_background_repeat(String input) { return input; } static Optional as_valid_background_attachment(String input) { return input; } @@ -147,23 +152,24 @@ private: template [[nodiscard]] Vector consume_a_list_of_declarations(TokenStream&); - Optional consume_a_declaration(); + [[nodiscard]] Optional consume_a_declaration(); template - Optional consume_a_declaration(TokenStream&); + [[nodiscard]] Optional consume_a_declaration(TokenStream&); - StyleComponentValueRule consume_a_component_value(); + [[nodiscard]] StyleComponentValueRule consume_a_component_value(); template - StyleComponentValueRule consume_a_component_value(TokenStream&); + [[nodiscard]] StyleComponentValueRule consume_a_component_value(TokenStream&); - NonnullRefPtr consume_a_simple_block(); + [[nodiscard]] NonnullRefPtr consume_a_simple_block(); template - NonnullRefPtr consume_a_simple_block(TokenStream&); + [[nodiscard]] NonnullRefPtr consume_a_simple_block(TokenStream&); - NonnullRefPtr consume_a_function(); + [[nodiscard]] NonnullRefPtr consume_a_function(); template - NonnullRefPtr consume_a_function(TokenStream&); + [[nodiscard]] NonnullRefPtr consume_a_function(TokenStream&); - RefPtr convert_rule(NonnullRefPtr); + [[nodiscard]] RefPtr convert_to_rule(NonnullRefPtr); + [[nodiscard]] RefPtr convert_to_declaration(NonnullRefPtr); ParsingContext m_context;