Skip to content

Commit 9a1352f

Browse files
AtkinsSJgmta
authored andcommitted
LibWeb/CSS: Support calculated angles in conic-gradient()
1 parent e72080b commit 9a1352f

File tree

6 files changed

+155
-41
lines changed

6 files changed

+155
-41
lines changed

Libraries/LibWeb/CSS/Parser/GradientParsing.cpp

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* Copyright (c) 2018-2022, Andreas Kling <andreas@ladybird.org>
33
* Copyright (c) 2020-2021, the SerenityOS developers.
4-
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
4+
* Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
55
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
66
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
77
*
@@ -10,6 +10,7 @@
1010

1111
#include <AK/NonnullRawPtr.h>
1212
#include <LibWeb/CSS/Parser/Parser.h>
13+
#include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
1314
#include <LibWeb/CSS/StyleValues/ConicGradientStyleValue.h>
1415
#include <LibWeb/CSS/StyleValues/LinearGradientStyleValue.h>
1516
#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
@@ -408,15 +409,12 @@ RefPtr<ConicGradientStyleValue const> Parser::parse_conic_gradient_function(Toke
408409
if (!tokens.has_next_token())
409410
return nullptr;
410411

411-
auto from_angle = Angle::make_degrees(0);
412+
RefPtr<StyleValue const> from_angle;
412413
RefPtr<PositionStyleValue const> at_position;
413414
Optional<InterpolationMethod> maybe_interpolation_method;
414415

415416
// conic-gradient( [ [ [ from [ <angle> | <zero> ] ]? [ at <position> ]? ] || <color-interpolation-method> ]? , <angular-color-stop-list> )
416417
NonnullRawPtr<ComponentValue const> token = tokens.next_token();
417-
bool got_from_angle = false;
418-
bool got_color_interpolation_method = false;
419-
bool got_at_position = false;
420418
while (token->is(Token::Type::Ident)) {
421419
auto consume_identifier = [&](auto identifier) {
422420
auto token_string = token->token().ident();
@@ -430,42 +428,28 @@ RefPtr<ConicGradientStyleValue const> Parser::parse_conic_gradient_function(Toke
430428

431429
if (consume_identifier("from"sv)) {
432430
// from [ <angle> | <zero> ]
433-
if (got_from_angle || got_at_position)
431+
if (from_angle || at_position)
434432
return nullptr;
435-
if (!tokens.has_next_token())
436-
return nullptr;
437-
438-
auto const& angle_token = tokens.consume_a_token();
439-
if (angle_token.is(Token::Type::Dimension)) {
440-
auto angle = angle_token.token().dimension_value();
441-
auto angle_unit = angle_token.token().dimension_unit();
442-
auto angle_type = string_to_angle_unit(angle_unit);
443-
if (!angle_type.has_value())
444-
return nullptr;
445-
446-
from_angle = Angle(angle, *angle_type);
447-
got_from_angle = true;
448-
} else if (angle_token.is(Token::Type::Number) && angle_token.token().number().value() == 0) {
449-
from_angle = Angle::make_degrees(0);
450-
got_from_angle = true;
433+
if (auto maybe_angle = parse_angle_value(tokens)) {
434+
from_angle = maybe_angle.release_nonnull();
435+
} else if (auto peek_token = tokens.next_token(); peek_token.is(Token::Type::Number) && peek_token.token().number().value() == 0) {
436+
tokens.discard_a_token(); // 0
437+
from_angle = AngleStyleValue::create(Angle::make_degrees(0));
451438
} else {
452439
return nullptr;
453440
}
454441
} else if (consume_identifier("at"sv)) {
455442
// at <position>
456-
if (got_at_position)
443+
if (at_position)
457444
return nullptr;
458445
auto position = parse_position_value(tokens);
459446
if (!position)
460447
return nullptr;
461-
at_position = position;
462-
got_at_position = true;
448+
at_position = move(position);
463449
} else if (token->token().ident().equals_ignoring_ascii_case("in"sv)) {
464450
// <color-interpolation-method>
465-
if (got_color_interpolation_method)
451+
if (maybe_interpolation_method.has_value())
466452
return nullptr;
467-
got_color_interpolation_method = true;
468-
469453
maybe_interpolation_method = parse_interpolation_method(tokens);
470454
if (!maybe_interpolation_method.has_value())
471455
return nullptr;
@@ -481,7 +465,7 @@ RefPtr<ConicGradientStyleValue const> Parser::parse_conic_gradient_function(Toke
481465
tokens.discard_whitespace();
482466
if (!tokens.has_next_token())
483467
return nullptr;
484-
if ((got_from_angle || got_at_position || got_color_interpolation_method) && !tokens.consume_a_token().is(Token::Type::Comma))
468+
if ((from_angle || at_position || maybe_interpolation_method.has_value()) && !tokens.consume_a_token().is(Token::Type::Comma))
485469
return nullptr;
486470

487471
auto color_stops = parse_angular_color_stop_list(tokens);
@@ -492,7 +476,7 @@ RefPtr<ConicGradientStyleValue const> Parser::parse_conic_gradient_function(Toke
492476
at_position = PositionStyleValue::create_center();
493477

494478
transaction.commit();
495-
return ConicGradientStyleValue::create(from_angle, at_position.release_nonnull(), move(*color_stops), repeating_gradient, maybe_interpolation_method);
479+
return ConicGradientStyleValue::create(move(from_angle), at_position.release_nonnull(), move(*color_stops), repeating_gradient, maybe_interpolation_method);
496480
}
497481

498482
RefPtr<RadialGradientStyleValue const> Parser::parse_radial_gradient_function(TokenStream<ComponentValue>& outer_tokens)

Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
#include "ConicGradientStyleValue.h"
11+
#include <LibWeb/CSS/StyleValues/AngleStyleValue.h>
1112
#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
1213
#include <LibWeb/Layout/Node.h>
1314
#include <LibWeb/Painting/DisplayListRecorder.h>
@@ -20,12 +21,12 @@ String ConicGradientStyleValue::to_string(SerializationMode mode) const
2021
if (is_repeating())
2122
builder.append("repeating-"sv);
2223
builder.append("conic-gradient("sv);
23-
bool has_from_angle = m_properties.from_angle.to_degrees() != 0;
24+
bool has_from_angle = m_properties.from_angle;
2425
bool has_at_position = !m_properties.position->is_center();
2526
bool has_color_space = m_properties.interpolation_method.has_value() && m_properties.interpolation_method.value().color_space != InterpolationMethod::default_color_space(m_properties.color_syntax);
2627

2728
if (has_from_angle)
28-
builder.appendff("from {}", m_properties.from_angle.to_string());
29+
builder.appendff("from {}", m_properties.from_angle->to_string(mode));
2930
if (has_at_position) {
3031
if (has_from_angle)
3132
builder.append(' ');
@@ -72,9 +73,18 @@ bool ConicGradientStyleValue::equals(StyleValue const& other) const
7273
return m_properties == other_gradient.m_properties;
7374
}
7475

75-
float ConicGradientStyleValue::angle_degrees() const
76+
float ConicGradientStyleValue::angle_degrees(CalculationResolutionContext const& context) const
7677
{
77-
return m_properties.from_angle.to_degrees();
78+
if (!m_properties.from_angle)
79+
return 0;
80+
if (m_properties.from_angle->is_angle())
81+
return m_properties.from_angle->as_angle().angle().to_degrees();
82+
if (m_properties.from_angle->is_calculated()) {
83+
if (auto maybe_angle = m_properties.from_angle->as_calculated().resolve_angle(context); maybe_angle.has_value())
84+
return maybe_angle->to_degrees();
85+
return 0;
86+
}
87+
VERIFY_NOT_REACHED();
7888
}
7989

8090
}

Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,19 @@
1010
#pragma once
1111

1212
#include <LibWeb/CSS/Angle.h>
13+
#include <LibWeb/CSS/CalculatedOr.h>
1314
#include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
1415
#include <LibWeb/Painting/GradientPainting.h>
1516

1617
namespace Web::CSS {
1718

1819
class ConicGradientStyleValue final : public AbstractImageStyleValue {
1920
public:
20-
static ValueComparingNonnullRefPtr<ConicGradientStyleValue const> create(Angle from_angle, ValueComparingNonnullRefPtr<PositionStyleValue const> position, Vector<AngularColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method)
21+
static ValueComparingNonnullRefPtr<ConicGradientStyleValue const> create(ValueComparingRefPtr<StyleValue const> from_angle, ValueComparingNonnullRefPtr<PositionStyleValue const> position, Vector<AngularColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method)
2122
{
2223
VERIFY(!color_stop_list.is_empty());
2324
bool any_non_legacy = color_stop_list.find_first_index_if([](auto const& stop) { return !stop.color_stop.color->is_keyword() && stop.color_stop.color->as_color().color_syntax() == ColorSyntax::Modern; }).has_value();
24-
return adopt_ref(*new (nothrow) ConicGradientStyleValue(from_angle, move(position), move(color_stop_list), repeating, interpolation_method, any_non_legacy ? ColorSyntax::Modern : ColorSyntax::Legacy));
25+
return adopt_ref(*new (nothrow) ConicGradientStyleValue(move(from_angle), move(position), move(color_stop_list), repeating, move(interpolation_method), any_non_legacy ? ColorSyntax::Modern : ColorSyntax::Legacy));
2526
}
2627

2728
virtual String to_string(SerializationMode) const override;
@@ -43,7 +44,7 @@ class ConicGradientStyleValue final : public AbstractImageStyleValue {
4344
return InterpolationMethod { .color_space = InterpolationMethod::default_color_space(m_properties.color_syntax) };
4445
}
4546

46-
float angle_degrees() const;
47+
float angle_degrees(CalculationResolutionContext const&) const;
4748

4849
bool is_paintable() const override { return true; }
4950

@@ -54,14 +55,14 @@ class ConicGradientStyleValue final : public AbstractImageStyleValue {
5455
bool is_repeating() const { return m_properties.repeating == GradientRepeating::Yes; }
5556

5657
private:
57-
ConicGradientStyleValue(Angle from_angle, ValueComparingNonnullRefPtr<PositionStyleValue const> position, Vector<AngularColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method, ColorSyntax color_syntax)
58+
ConicGradientStyleValue(ValueComparingRefPtr<StyleValue const> from_angle, ValueComparingNonnullRefPtr<PositionStyleValue const> position, Vector<AngularColorStopListElement> color_stop_list, GradientRepeating repeating, Optional<InterpolationMethod> interpolation_method, ColorSyntax color_syntax)
5859
: AbstractImageStyleValue(Type::ConicGradient)
59-
, m_properties { .from_angle = from_angle, .position = move(position), .color_stop_list = move(color_stop_list), .repeating = repeating, .interpolation_method = interpolation_method, .color_syntax = color_syntax }
60+
, m_properties { .from_angle = move(from_angle), .position = move(position), .color_stop_list = move(color_stop_list), .repeating = repeating, .interpolation_method = interpolation_method, .color_syntax = color_syntax }
6061
{
6162
}
6263

6364
struct Properties {
64-
Angle from_angle;
65+
ValueComparingRefPtr<StyleValue const> from_angle;
6566
ValueComparingNonnullRefPtr<PositionStyleValue const> position;
6667
Vector<AngularColorStopListElement> color_stop_list;
6768
GradientRepeating repeating;

Libraries/LibWeb/Painting/GradientPainting.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <AK/Math.h>
88
#include <LibGfx/Gradients.h>
9+
#include <LibWeb/CSS/CalculationResolutionContext.h>
910
#include <LibWeb/CSS/StyleValues/ConicGradientStyleValue.h>
1011
#include <LibWeb/CSS/StyleValues/LinearGradientStyleValue.h>
1112
#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
@@ -132,7 +133,10 @@ ConicGradientData resolve_conic_gradient_data(Layout::NodeWithStyle const& node,
132133
return angle_percentage.resolved(node, one_turn).to_degrees() / one_turn.to_degrees();
133134
},
134135
conic_gradient.is_repeating());
135-
return { conic_gradient.angle_degrees(), resolved_color_stops, conic_gradient.interpolation_method() };
136+
CSS::CalculationResolutionContext context {
137+
.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(node),
138+
};
139+
return { conic_gradient.angle_degrees(context), resolved_color_stops, conic_gradient.interpolation_method() };
136140
}
137141

138142
RadialGradientData resolve_radial_gradient_data(Layout::NodeWithStyle const& node, CSSPixelSize gradient_size, CSS::RadialGradientStyleValue const& radial_gradient)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<!DOCTYPE html>
2+
<meta charset="UTF-8">
3+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
4+
<title>Tests the maximum value of color stops in conic-gradient().</title>
5+
<style>
6+
body {
7+
background-color: lightblue;
8+
}
9+
10+
.test {
11+
display: flex;
12+
flex-wrap: wrap;
13+
}
14+
15+
li {
16+
width: 100px;
17+
height: 100px;
18+
margin-right: 30px;
19+
margin-bottom: 30px;
20+
outline: 1px solid black;
21+
font-size: 14px;
22+
text-align: center;
23+
background: lime;
24+
}
25+
</style>
26+
<p>Should be lime in the background of all boxes.</p>
27+
<ol class="test">
28+
<li>0 999999999%</li>
29+
<li>0 calc(Infinity * 0%)</li>
30+
<li>0 calc(Infinity * 1%)</li>
31+
<li>calc(Infinity * 0%) 100%</li>
32+
<li>calc(Infinity * 1%) 100%</li>
33+
<li>calc(Infinity * -1%) 100%</li>
34+
<li>0% calc(Infinity * 1%)</li>
35+
<li>from calc(Infinity * 1deg)</li>
36+
<li>from calc(Infinity * 0deg)</li>
37+
</ol>
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<!DOCTYPE html>
2+
<meta charset="utf-8">
3+
<link rel="author" title="CGQAQ" href="mailto:m.jason.liu@gmail.com">
4+
<link rel="author" title="一丝" href="mailto:yiorsi@gmail.com">
5+
<link rel="help" href="https://www.w3.org/TR/css-color-4/#interpolation">
6+
<link rel="match" href="../../../../../expected/wpt-import/css/css-images/gradient/conic-gradient-001-ref.html">
7+
<meta name="fuzzy" content="maxDifference=0-1;totalPixels=0-40000">
8+
<title>Tests the maximum value of color stops in conic-gradient().</title>
9+
<style>
10+
body {
11+
background-color: lightblue;
12+
}
13+
14+
.test {
15+
display: flex;
16+
flex-wrap: wrap;
17+
}
18+
19+
li {
20+
width: 100px;
21+
height: 100px;
22+
margin-right: 30px;
23+
margin-bottom: 30px;
24+
outline: 1px solid black;
25+
font-size: 14px;
26+
text-align: center;
27+
background: red;
28+
}
29+
30+
li:nth-child(1) {
31+
background: conic-gradient(lime 0 999999999%);
32+
}
33+
34+
li:nth-child(2) {
35+
background: conic-gradient(in hsl, lime 0 calc(Infinity * 0%));
36+
}
37+
38+
li:nth-child(3) {
39+
background: conic-gradient(in lch, lime 0 calc(Infinity * 1%));
40+
}
41+
42+
li:nth-child(4) {
43+
background: conic-gradient(in oklab, lime calc(Infinity * 0%) 100%);
44+
}
45+
46+
li:nth-child(5) {
47+
background: conic-gradient(in srgb, lime calc(Infinity * 1%) 100%);
48+
}
49+
50+
li:nth-child(6) {
51+
background: conic-gradient(in srgb, lime calc(Infinity * -1%) 100%);
52+
}
53+
54+
li:nth-child(7) {
55+
background: conic-gradient(in srgb, lime 0 calc(Infinity * 1%));
56+
}
57+
58+
li:nth-child(8) {
59+
background: conic-gradient(from calc(Infinity * 1deg), lime 0 100%);
60+
}
61+
62+
li:nth-child(9) {
63+
background: conic-gradient(from calc(Infinity * 0deg), lime 0 100%);
64+
}
65+
</style>
66+
67+
<p>Should be lime in the background of all boxes.</p>
68+
<ol class="test">
69+
<li>0 999999999%</li>
70+
<li>0 calc(Infinity * 0%)</li>
71+
<li>0 calc(Infinity * 1%)</li>
72+
<li>calc(Infinity * 0%) 100%</li>
73+
<li>calc(Infinity * 1%) 100%</li>
74+
<li>calc(Infinity * -1%) 100%</li>
75+
<li>0% calc(Infinity * 1%)</li>
76+
<li>from calc(Infinity * 1deg)</li>
77+
<li>from calc(Infinity * 0deg)</li>
78+
</ol>

0 commit comments

Comments
 (0)