summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorMacDue <macdue@dueutil.tech>2022-11-11 19:12:00 +0100
committerSam Atkins <atkinssj@gmail.com>2022-11-30 14:24:04 +0000
commit22a7611e1c09ef15aea008b047ff498bed89aaa7 (patch)
tree101ad27a76d5473803e4c113471502ec60971c92 /Userland
parent040dac558e0355bd7f79db5dc58e97bfaef096a6 (diff)
downloadserenity-22a7611e1c09ef15aea008b047ff498bed89aaa7.zip
LibWeb: Parse `radial-gradient()`s
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp153
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.h1
2 files changed, 151 insertions, 3 deletions
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<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>
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<StyleValue> parse_linear_gradient_function(ComponentValue const&);
RefPtr<StyleValue> parse_conic_gradient_function(ComponentValue const&);
+ RefPtr<StyleValue> parse_radial_gradient_function(ComponentValue const&);
ParseErrorOr<NonnullRefPtr<StyleValue>> parse_css_value(PropertyID, TokenStream<ComponentValue>&);
RefPtr<StyleValue> parse_css_value(ComponentValue const&);