diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 6e544371fd..b0640d6e3f 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -2579,8 +2579,8 @@ RefPtr Parser::parse_conic_gradient_function(ComponentValue const& c bool got_color_interpolation_method = false; bool got_at_position = false; while (token.is(Token::Type::Ident)) { - auto token_string = token.token().ident(); auto consume_identifier = [&](auto identifier) { + auto token_string = token.token().ident(); if (token_string.equals_ignoring_case(identifier)) { (void)tokens.next_token(); tokens.skip_whitespace(); @@ -2644,6 +2644,151 @@ RefPtr Parser::parse_conic_gradient_function(ComponentValue const& c return ConicGradientStyleValue::create(from_angle, at_position, move(*color_stops), repeating_gradient); } +RefPtr Parser::parse_radial_gradient_function(ComponentValue const& component_value) +{ + using EndingShape = RadialGradientStyleValue::EndingShape; + using Extent = RadialGradientStyleValue::Extent; + using CircleSize = RadialGradientStyleValue::CircleSize; + using EllipseSize = RadialGradientStyleValue::EllipseSize; + using Size = RadialGradientStyleValue::Size; + + if (!component_value.is_function()) + return {}; + + auto function_name = component_value.function().name(); + + if (!function_name.equals_ignoring_case("radial-gradient"sv)) + return {}; + + TokenStream tokens { component_value.function().values() }; + tokens.skip_whitespace(); + if (!tokens.has_next_token()) + return {}; + + bool expect_comma = false; + + auto commit_value = [&](auto value, T&... transactions) { + (transactions.commit(), ...); + return value; + }; + + // radial-gradient( [ || ]? [ at ]? , ) + + Size size = Extent::FarthestCorner; + EndingShape ending_shape = EndingShape::Circle; + PositionValue at_position = PositionValue::center(); + + auto parse_ending_shape = [&]() -> Optional { + auto transaction = tokens.begin_transaction(); + tokens.skip_whitespace(); + auto& token = tokens.next_token(); + if (!token.is(Token::Type::Ident)) + return {}; + auto ident = token.token().ident(); + if (ident.equals_ignoring_case("circle"sv)) + return commit_value(EndingShape::Circle, transaction); + if (ident.equals_ignoring_case("ellipse"sv)) + return commit_value(EndingShape::Ellipse, transaction); + return {}; + }; + + auto parse_extent_keyword = [](StringView keyword) -> Optional { + if (keyword.equals_ignoring_case("closest-corner"sv)) + return Extent::ClosestCorner; + if (keyword.equals_ignoring_case("closest-side"sv)) + return Extent::ClosestSide; + if (keyword.equals_ignoring_case("farthest-corner"sv)) + return Extent::FarthestCorner; + if (keyword.equals_ignoring_case("farthest-side"sv)) + return Extent::FarthestSide; + return {}; + }; + + auto parse_size = [&]() -> Optional { + // = + // | + // | + // {2} + auto transaction_size = tokens.begin_transaction(); + tokens.skip_whitespace(); + if (!tokens.has_next_token()) + return {}; + auto& token = tokens.next_token(); + if (token.is(Token::Type::Ident)) { + auto extent = parse_extent_keyword(token.token().ident()); + if (!extent.has_value()) + return {}; + return commit_value(*extent, transaction_size); + } + auto first_dimension = parse_dimension(token); + if (!first_dimension.has_value()) + return {}; + if (!first_dimension->is_length_percentage()) + return {}; + auto transaction_second_dimension = tokens.begin_transaction(); + tokens.skip_whitespace(); + if (tokens.has_next_token()) { + auto& second_token = tokens.next_token(); + auto second_dimension = parse_dimension(second_token); + if (second_dimension.has_value() && second_dimension->is_length_percentage()) + return commit_value(EllipseSize { first_dimension->length_percentage(), second_dimension->length_percentage() }, + transaction_size, transaction_second_dimension); + } + if (first_dimension->is_length()) + return commit_value(CircleSize { first_dimension->length() }, transaction_size); + return {}; + }; + + { + // [ || ]? + auto maybe_ending_shape = parse_ending_shape(); + auto maybe_size = parse_size(); + if (!maybe_ending_shape.has_value() && maybe_size.has_value()) + maybe_ending_shape = parse_ending_shape(); + if (maybe_size.has_value()) { + size = *maybe_size; + expect_comma = true; + } + if (maybe_ending_shape.has_value()) { + expect_comma = true; + ending_shape = *maybe_ending_shape; + if (ending_shape == EndingShape::Circle && size.has()) + return {}; + if (ending_shape == EndingShape::Ellipse && size.has()) + return {}; + } else { + ending_shape = size.has() ? EndingShape::Circle : EndingShape::Ellipse; + } + } + + tokens.skip_whitespace(); + if (!tokens.has_next_token()) + return {}; + + auto& token = tokens.peek_token(); + if (token.is(Token::Type::Ident) && token.token().ident().equals_ignoring_case("at"sv)) { + (void)tokens.next_token(); + auto position = parse_position(tokens); + if (!position.has_value()) + return {}; + at_position = *position; + expect_comma = true; + } + + tokens.skip_whitespace(); + if (!tokens.has_next_token()) + return {}; + if (expect_comma && !tokens.next_token().is(Token::Type::Comma)) + return {}; + + // + auto color_stops = parse_linear_color_stop_list(tokens); + if (!color_stops.has_value()) + return {}; + + return RadialGradientStyleValue::create(ending_shape, size, at_position, move(*color_stops)); +} + Optional Parser::parse_position(TokenStream& tokens, PositionValue initial_value) { auto transaction = tokens.begin_transaction(); @@ -3825,11 +3970,13 @@ RefPtr Parser::parse_image_value(ComponentValue const& component_val auto url = parse_url_function(component_value, AllowedDataUrlType::Image); if (url.has_value()) return ImageStyleValue::create(url.value()); - // FIXME: Implement other kinds of gradient auto linear_gradient = parse_linear_gradient_function(component_value); if (linear_gradient) return linear_gradient; - return parse_conic_gradient_function(component_value); + auto conic_gradient = parse_conic_gradient_function(component_value); + if (conic_gradient) + return conic_gradient; + return parse_radial_gradient_function(component_value); } template diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index ea31c72909..dd05253af3 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -269,6 +269,7 @@ private: RefPtr parse_linear_gradient_function(ComponentValue const&); RefPtr parse_conic_gradient_function(ComponentValue const&); + RefPtr parse_radial_gradient_function(ComponentValue const&); ParseErrorOr> parse_css_value(PropertyID, TokenStream&); RefPtr parse_css_value(ComponentValue const&);