summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMacDue <macdue@dueutil.tech>2022-08-22 21:37:53 +0100
committerAndreas Kling <kling@serenityos.org>2022-08-23 01:02:49 +0200
commit3a1f8d714a0afa03eadcbc6bb6cd35941383f1e2 (patch)
treed28ab472280e3e21956bb7d6e1c01a19f8c6d868
parentec3d8a7a1837cb6b8667d7e03514821cc74ed27e (diff)
downloadserenity-3a1f8d714a0afa03eadcbc6bb6cd35941383f1e2.zip
LibWeb: Parse double-position `linear-gradient()` color stops
The only accepted syntax for these seems to be <color> <length percentage> <length percentage>, no other order. But that's just gathered from looking at other browsers as though these are supported by all major browsers, they don't appear in the W3C spec.
-rw-r--r--Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp26
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleValue.cpp7
-rw-r--r--Userland/Libraries/LibWeb/CSS/StyleValue.h3
-rw-r--r--Userland/Libraries/LibWeb/Painting/GradientPainting.cpp16
4 files changed, 30 insertions, 22 deletions
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
index 9bd1354946..da1ec16fc0 100644
--- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
+++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp
@@ -2509,15 +2509,16 @@ RefPtr<StyleValue> Parser::parse_linear_gradient_function(ComponentValue const&
auto& token = tokens.next_token();
Gfx::Color color;
- Optional<LengthPercentage> length;
+ Optional<LengthPercentage> position;
+ Optional<LengthPercentage> second_position;
auto dimension = parse_dimension(token);
if (dimension.has_value() && dimension->is_length_percentage()) {
// [<length-percentage> <color>] or [<length-percentage>]
- length = dimension->length_percentage();
+ position = dimension->length_percentage();
tokens.skip_whitespace();
// <length-percentage>
if (!tokens.has_next_token() || tokens.peek_token().is(Token::Type::Comma)) {
- element.transition_hint = GradientColorHint { *length };
+ element.transition_hint = GradientColorHint { *position };
return ElementType::ColorHint;
}
// <length-percentage> <color>
@@ -2532,16 +2533,21 @@ RefPtr<StyleValue> Parser::parse_linear_gradient_function(ComponentValue const&
return ElementType::Garbage;
color = *maybe_color;
tokens.skip_whitespace();
- if (tokens.has_next_token() && !tokens.peek_token().is(Token::Type::Comma)) {
- auto token = tokens.next_token();
- auto dimension = parse_dimension(token);
- if (!dimension.has_value() || !dimension->is_length_percentage())
- return ElementType::Garbage;
- length = dimension->length_percentage();
+ // Allow up to [<color> <length-percentage> <length-percentage>] (double-position color stops)
+ // Note: Double-position color stops only appear to be valid in this order.
+ for (auto stop_position : Array { &position, &second_position }) {
+ if (tokens.has_next_token() && !tokens.peek_token().is(Token::Type::Comma)) {
+ auto token = tokens.next_token();
+ auto dimension = parse_dimension(token);
+ if (!dimension.has_value() || !dimension->is_length_percentage())
+ return ElementType::Garbage;
+ *stop_position = dimension->length_percentage();
+ tokens.skip_whitespace();
+ }
}
}
- element.color_stop = GradientColorStop { color, length };
+ element.color_stop = GradientColorStop { color, position, second_position };
return ElementType::ColorStop;
};
diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp
index daa59d3920..4e8f892c45 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp
+++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp
@@ -1512,8 +1512,9 @@ String LinearGradientStyleValue::to_string() const
}
serialize_a_srgb_value(builder, element.color_stop.color);
- if (element.color_stop.length.has_value()) {
- builder.appendff(" {}"sv, element.color_stop.length->to_string());
+ for (auto position : Array { &element.color_stop.position, &element.color_stop.second_position }) {
+ if (position->has_value())
+ builder.appendff(" {}"sv, (*position)->to_string());
}
first = false;
}
@@ -1537,7 +1538,7 @@ static bool operator==(GradientColorHint a, GradientColorHint b)
static bool operator==(GradientColorStop a, GradientColorStop b)
{
- return a.color == b.color && a.length == b.length;
+ return a.color == b.color && a.position == b.position && a.second_position == b.second_position;
}
static bool operator==(ColorStopListElement a, ColorStopListElement b)
diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h
index 89ed09e583..38a0e0f3b6 100644
--- a/Userland/Libraries/LibWeb/CSS/StyleValue.h
+++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h
@@ -74,7 +74,8 @@ enum class SideOrCorner {
struct GradientColorStop {
Color color;
- Optional<LengthPercentage> length;
+ Optional<LengthPercentage> position;
+ Optional<LengthPercentage> second_position = {};
};
struct GradientColorHint {
diff --git a/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp b/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp
index 3d1fcdc3fa..7cdb49bd72 100644
--- a/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp
+++ b/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp
@@ -49,13 +49,13 @@ LinearGradientData resolve_linear_gradient_data(Layout::Node const& node, Gfx::F
// 1. If the first color stop does not have a position, set its position to 0%.
auto& first_stop = color_stop_list.first().color_stop;
- resolved_color_stops.first().position = first_stop.length.has_value()
- ? first_stop.length->resolved(node, gradient_length).to_px(node)
+ resolved_color_stops.first().position = first_stop.position.has_value()
+ ? first_stop.position->resolved(node, gradient_length).to_px(node)
: 0;
// If the last color stop does not have a position, set its position to 100%
auto& last_stop = color_stop_list.last().color_stop;
- resolved_color_stops.last().position = last_stop.length.has_value()
- ? last_stop.length->resolved(node, gradient_length).to_px(node)
+ resolved_color_stops.last().position = last_stop.position.has_value()
+ ? last_stop.position->resolved(node, gradient_length).to_px(node)
: gradient_length_px;
// 2. If a color stop or transition hint has a position that is less than the
@@ -71,8 +71,8 @@ LinearGradientData resolve_linear_gradient_data(Layout::Node const& node, Gfx::F
resolved_color_stops[i].transition_hint = value;
max_previous_color_stop_or_hint = value;
}
- if (stop.color_stop.length.has_value()) {
- float value = stop.color_stop.length->resolved(node, gradient_length).to_px(node);
+ if (stop.color_stop.position.has_value()) {
+ float value = stop.color_stop.position->resolved(node, gradient_length).to_px(node);
value = max(value, max_previous_color_stop_or_hint);
resolved_color_stops[i].position = value;
max_previous_color_stop_or_hint = value;
@@ -86,7 +86,7 @@ LinearGradientData resolve_linear_gradient_data(Layout::Node const& node, Gfx::F
size_t i = 1;
auto find_run_end = [&] {
auto color_stop_has_position = [](auto& color_stop) {
- return color_stop.transition_hint.has_value() || color_stop.color_stop.length.has_value();
+ return color_stop.transition_hint.has_value() || color_stop.color_stop.position.has_value();
};
while (i < color_stop_list.size() - 1 && !color_stop_has_position(color_stop_list[i])) {
i++;
@@ -95,7 +95,7 @@ LinearGradientData resolve_linear_gradient_data(Layout::Node const& node, Gfx::F
};
while (i < color_stop_list.size() - 1) {
auto& stop = color_stop_list[i];
- if (!stop.color_stop.length.has_value()) {
+ if (!stop.color_stop.position.has_value()) {
auto run_start = i - 1;
auto start_position = resolved_color_stops[i++].transition_hint.value_or(resolved_color_stops[run_start].position);
auto run_end = find_run_end();