1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 08:58:11 +00:00

LibWeb: Parse radial-gradient()s

This commit is contained in:
MacDue 2022-11-11 19:12:00 +01:00 committed by Sam Atkins
parent 040dac558e
commit 22a7611e1c
2 changed files with 151 additions and 3 deletions

View file

@ -2579,8 +2579,8 @@ RefPtr<StyleValue> 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<StyleValue> Parser::parse_conic_gradient_function(ComponentValue const& c
return ConicGradientStyleValue::create(from_angle, at_position, move(*color_stops), repeating_gradient);
}
RefPtr<StyleValue> 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 = [&]<typename... T>(auto value, T&... transactions) {
(transactions.commit(), ...);
return value;
};
// radial-gradient( [ <ending-shape> || <size> ]? [ at <position> ]? , <color-stop-list> )
Size size = Extent::FarthestCorner;
EndingShape ending_shape = EndingShape::Circle;
PositionValue at_position = PositionValue::center();
auto parse_ending_shape = [&]() -> Optional<EndingShape> {
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<Extent> {
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<Size> {
// <size> =
// <extent-keyword> |
// <length [0,∞]> |
// <length-percentage [0,∞]>{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 {};
};
{
// [ <ending-shape> || <size> ]?
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<EllipseSize>())
return {};
if (ending_shape == EndingShape::Ellipse && size.has<CircleSize>())
return {};
} else {
ending_shape = size.has<CircleSize>() ? 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 {};
// <color-stop-list>
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<PositionValue> Parser::parse_position(TokenStream<ComponentValue>& tokens, PositionValue initial_value)
{
auto transaction = tokens.begin_transaction();
@ -3825,11 +3970,13 @@ RefPtr<StyleValue> 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<typename ParseFunction>