From c8626f229429cbb98805e31079b2855681202f72 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Wed, 24 May 2023 16:09:38 +0100 Subject: [PATCH] LibWeb: Use new StyleValue parsing for font and font-family --- .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 165 +++++++++--------- Userland/Libraries/LibWeb/CSS/Parser/Parser.h | 2 +- 2 files changed, 88 insertions(+), 79 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index d941406e52..a7d43c1742 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -5529,64 +5529,71 @@ ErrorOr> Parser::parse_font_value(Vector cons // So, we have to handle that separately. int normal_count = 0; - for (size_t i = 0; i < component_values.size(); ++i) { - auto value = TRY(parse_css_value(component_values[i])); - if (!value) - return nullptr; + auto tokens = TokenStream { component_values }; + auto remaining_longhands = Vector { PropertyID::FontSize, PropertyID::FontStretch, PropertyID::FontStyle, PropertyID::FontVariant, PropertyID::FontWeight }; - if (value->to_identifier() == ValueID::Normal) { + while (tokens.has_next_token()) { + auto& peek_token = tokens.peek_token(); + if (peek_token.is(Token::Type::Ident) && value_id_from_string(peek_token.token().ident()) == ValueID::Normal) { normal_count++; + (void)tokens.next_token(); continue; } - // FIXME: Handle angle parameter to `oblique`: https://www.w3.org/TR/css-fonts-4/#font-style-prop - if (property_accepts_value(PropertyID::FontStyle, *value)) { - if (font_style) - return nullptr; - font_style = value.release_nonnull(); - continue; - } - if (property_accepts_value(PropertyID::FontWeight, *value)) { - if (font_weight) - return nullptr; - font_weight = value.release_nonnull(); - continue; - } - if (property_accepts_value(PropertyID::FontVariant, *value)) { - if (font_variant) - return nullptr; - font_variant = value.release_nonnull(); - continue; - } - if (property_accepts_value(PropertyID::FontSize, *value)) { - if (font_size) - return nullptr; + + auto property_and_value = TRY(parse_css_value_for_properties(remaining_longhands, tokens)); + if (!property_and_value.style_value) + return nullptr; + auto& value = property_and_value.style_value; + remove_property(remaining_longhands, property_and_value.property); + + switch (property_and_value.property) { + case PropertyID::FontSize: { + VERIFY(!font_size); font_size = value.release_nonnull(); // Consume `/ line-height` if present - if (i + 2 < component_values.size()) { - auto const& maybe_solidus = component_values[i + 1]; - if (maybe_solidus.is(Token::Type::Delim) && maybe_solidus.token().delim() == '/') { - auto maybe_line_height = TRY(parse_css_value(component_values[i + 2])); - if (!(maybe_line_height && property_accepts_value(PropertyID::LineHeight, *maybe_line_height))) - return nullptr; - line_height = maybe_line_height.release_nonnull(); - i += 2; - } + auto maybe_solidus = tokens.peek_token(); + if (maybe_solidus.is(Token::Type::Delim) && maybe_solidus.token().delim() == '/') { + (void)tokens.next_token(); + auto maybe_line_height = TRY(parse_css_value_for_property(PropertyID::LineHeight, tokens)); + if (!maybe_line_height) + return nullptr; + line_height = maybe_line_height.release_nonnull(); } // Consume font-families - auto maybe_font_families = TRY(parse_font_family_value(component_values, i + 1)); - if (!maybe_font_families) + auto maybe_font_families = TRY(parse_font_family_value(tokens)); + // font-family comes last, so we must not have any tokens left over. + if (!maybe_font_families || tokens.has_next_token()) return nullptr; font_families = maybe_font_families.release_nonnull(); - break; + continue; } - if (property_accepts_value(PropertyID::FontStretch, *value)) { - if (font_stretch) - return nullptr; + case PropertyID::FontStretch: { + VERIFY(!font_stretch); font_stretch = value.release_nonnull(); continue; } + case PropertyID::FontStyle: { + // FIXME: Handle angle parameter to `oblique`: https://www.w3.org/TR/css-fonts-4/#font-style-prop + VERIFY(!font_style); + font_style = value.release_nonnull(); + continue; + } + case PropertyID::FontVariant: { + VERIFY(!font_variant); + font_variant = value.release_nonnull(); + continue; + } + case PropertyID::FontWeight: { + VERIFY(!font_weight); + font_weight = value.release_nonnull(); + continue; + } + default: + VERIFY_NOT_REACHED(); + } + return nullptr; } @@ -5612,15 +5619,10 @@ ErrorOr> Parser::parse_font_value(Vector cons return FontStyleValue::create(font_stretch.release_nonnull(), font_style.release_nonnull(), font_weight.release_nonnull(), font_size.release_nonnull(), line_height.release_nonnull(), font_families.release_nonnull()); } -ErrorOr> Parser::parse_font_family_value(Vector const& component_values, size_t start_index) +ErrorOr> Parser::parse_font_family_value(TokenStream& tokens) { - auto is_comma_or_eof = [&](size_t i) -> bool { - if (i < component_values.size()) { - auto const& maybe_comma = component_values[i]; - if (!maybe_comma.is(Token::Type::Comma)) - return false; - } - return true; + auto next_is_comma_or_eof = [&]() -> bool { + return !tokens.has_next_token() || tokens.peek_token().is(Token::Type::Comma); }; // Note: Font-family names can either be a quoted string, or a keyword, or a series of custom-idents. @@ -5629,54 +5631,59 @@ ErrorOr> Parser::parse_font_family_value(Vector current_name_parts; - for (size_t i = start_index; i < component_values.size(); ++i) { - auto const& part = component_values[i]; + while (tokens.has_next_token()) { + auto const& peek = tokens.peek_token(); - if (part.is(Token::Type::String)) { + if (peek.is(Token::Type::String)) { // `font-family: my cool "font";` is invalid. if (!current_name_parts.is_empty()) return nullptr; - if (!is_comma_or_eof(i + 1)) + (void)tokens.next_token(); // String + if (!next_is_comma_or_eof()) return nullptr; - font_families.append(TRY(StringStyleValue::create(TRY(String::from_utf8(part.token().string()))))); - i++; + TRY(font_families.try_append(TRY(StringStyleValue::create(TRY(String::from_utf8(peek.token().string())))))); + (void)tokens.next_token(); // Comma continue; } - if (part.is(Token::Type::Ident)) { + + if (peek.is(Token::Type::Ident)) { // If this is a valid identifier, it's NOT a custom-ident and can't be part of a larger name. - auto maybe_ident = TRY(parse_css_value(part)); - if (maybe_ident) { - // CSS-wide keywords are not allowed - if (maybe_ident->is_builtin()) + + // CSS-wide keywords are not allowed + if (auto builtin = TRY(parse_builtin_value(peek))) + return nullptr; + + auto maybe_ident = value_id_from_string(peek.token().ident()); + // Can't have a generic-font-name as a token in an unquoted font name. + if (maybe_ident.has_value() && is_generic_font_family(maybe_ident.value())) { + if (!current_name_parts.is_empty()) return nullptr; - if (is_generic_font_family(maybe_ident->to_identifier())) { - // Can't have a generic-font-name as a token in an unquoted font name. - if (!current_name_parts.is_empty()) - return nullptr; - if (!is_comma_or_eof(i + 1)) - return nullptr; - font_families.append(maybe_ident.release_nonnull()); - i++; - continue; - } + (void)tokens.next_token(); // Ident + if (!next_is_comma_or_eof()) + return nullptr; + TRY(font_families.try_append(TRY(IdentifierStyleValue::create(maybe_ident.value())))); + (void)tokens.next_token(); // Comma + continue; } - current_name_parts.append(part.token().ident()); + TRY(current_name_parts.try_append(tokens.next_token().token().ident())); continue; } - if (part.is(Token::Type::Comma)) { + + if (peek.is(Token::Type::Comma)) { if (current_name_parts.is_empty()) return nullptr; - font_families.append(TRY(StringStyleValue::create(TRY(String::from_utf8(DeprecatedString::join(' ', current_name_parts)))))); + (void)tokens.next_token(); // Comma + TRY(font_families.try_append(TRY(StringStyleValue::create(TRY(String::join(' ', current_name_parts)))))); current_name_parts.clear(); // Can't have a trailing comma - if (i + 1 == component_values.size()) + if (!tokens.has_next_token()) return nullptr; continue; } } if (!current_name_parts.is_empty()) { - font_families.append(TRY(StringStyleValue::create(TRY(String::from_utf8(DeprecatedString::join(' ', current_name_parts)))))); + TRY(font_families.try_append(TRY(StringStyleValue::create(TRY(String::join(' ', current_name_parts)))))); current_name_parts.clear(); } @@ -7036,10 +7043,12 @@ Parser::ParseErrorOr> Parser::parse_css_value(Property if (auto parsed_value = FIXME_TRY(parse_font_value(component_values))) return parsed_value.release_nonnull(); return ParseError::SyntaxError; - case PropertyID::FontFamily: - if (auto parsed_value = FIXME_TRY(parse_font_family_value(component_values))) + case PropertyID::FontFamily: { + auto tokens_without_whitespace = TokenStream { component_values }; + if (auto parsed_value = FIXME_TRY(parse_font_family_value(tokens_without_whitespace))) return parsed_value.release_nonnull(); return ParseError::SyntaxError; + } case PropertyID::GridColumn: if (auto parsed_value = FIXME_TRY(parse_grid_track_placement_shorthand_value(component_values))) return parsed_value.release_nonnull(); diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index fbe7ffca22..b7c1ccaec1 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -315,7 +315,7 @@ private: ErrorOr> parse_flex_value(Vector const&); ErrorOr> parse_flex_flow_value(Vector const&); ErrorOr> parse_font_value(Vector const&); - ErrorOr> parse_font_family_value(Vector const&, size_t start_index = 0); + ErrorOr> parse_font_family_value(TokenStream&); ErrorOr> parse_list_style_value(Vector const&); ErrorOr> parse_overflow_value(Vector const&); enum class AllowInsetKeyword {