mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 17:37:37 +00:00
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.
This commit is contained in:
parent
ec3d8a7a18
commit
3a1f8d714a
4 changed files with 30 additions and 22 deletions
|
@ -2509,15 +2509,16 @@ RefPtr<StyleValue> Parser::parse_linear_gradient_function(ComponentValue const&
|
||||||
auto& token = tokens.next_token();
|
auto& token = tokens.next_token();
|
||||||
|
|
||||||
Gfx::Color color;
|
Gfx::Color color;
|
||||||
Optional<LengthPercentage> length;
|
Optional<LengthPercentage> position;
|
||||||
|
Optional<LengthPercentage> second_position;
|
||||||
auto dimension = parse_dimension(token);
|
auto dimension = parse_dimension(token);
|
||||||
if (dimension.has_value() && dimension->is_length_percentage()) {
|
if (dimension.has_value() && dimension->is_length_percentage()) {
|
||||||
// [<length-percentage> <color>] or [<length-percentage>]
|
// [<length-percentage> <color>] or [<length-percentage>]
|
||||||
length = dimension->length_percentage();
|
position = dimension->length_percentage();
|
||||||
tokens.skip_whitespace();
|
tokens.skip_whitespace();
|
||||||
// <length-percentage>
|
// <length-percentage>
|
||||||
if (!tokens.has_next_token() || tokens.peek_token().is(Token::Type::Comma)) {
|
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;
|
return ElementType::ColorHint;
|
||||||
}
|
}
|
||||||
// <length-percentage> <color>
|
// <length-percentage> <color>
|
||||||
|
@ -2532,16 +2533,21 @@ RefPtr<StyleValue> Parser::parse_linear_gradient_function(ComponentValue const&
|
||||||
return ElementType::Garbage;
|
return ElementType::Garbage;
|
||||||
color = *maybe_color;
|
color = *maybe_color;
|
||||||
tokens.skip_whitespace();
|
tokens.skip_whitespace();
|
||||||
if (tokens.has_next_token() && !tokens.peek_token().is(Token::Type::Comma)) {
|
// Allow up to [<color> <length-percentage> <length-percentage>] (double-position color stops)
|
||||||
auto token = tokens.next_token();
|
// Note: Double-position color stops only appear to be valid in this order.
|
||||||
auto dimension = parse_dimension(token);
|
for (auto stop_position : Array { &position, &second_position }) {
|
||||||
if (!dimension.has_value() || !dimension->is_length_percentage())
|
if (tokens.has_next_token() && !tokens.peek_token().is(Token::Type::Comma)) {
|
||||||
return ElementType::Garbage;
|
auto token = tokens.next_token();
|
||||||
length = dimension->length_percentage();
|
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;
|
return ElementType::ColorStop;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1512,8 +1512,9 @@ String LinearGradientStyleValue::to_string() const
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize_a_srgb_value(builder, element.color_stop.color);
|
serialize_a_srgb_value(builder, element.color_stop.color);
|
||||||
if (element.color_stop.length.has_value()) {
|
for (auto position : Array { &element.color_stop.position, &element.color_stop.second_position }) {
|
||||||
builder.appendff(" {}"sv, element.color_stop.length->to_string());
|
if (position->has_value())
|
||||||
|
builder.appendff(" {}"sv, (*position)->to_string());
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
@ -1537,7 +1538,7 @@ static bool operator==(GradientColorHint a, GradientColorHint b)
|
||||||
|
|
||||||
static bool operator==(GradientColorStop a, GradientColorStop 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)
|
static bool operator==(ColorStopListElement a, ColorStopListElement b)
|
||||||
|
|
|
@ -74,7 +74,8 @@ enum class SideOrCorner {
|
||||||
|
|
||||||
struct GradientColorStop {
|
struct GradientColorStop {
|
||||||
Color color;
|
Color color;
|
||||||
Optional<LengthPercentage> length;
|
Optional<LengthPercentage> position;
|
||||||
|
Optional<LengthPercentage> second_position = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GradientColorHint {
|
struct GradientColorHint {
|
||||||
|
|
|
@ -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%.
|
// 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;
|
auto& first_stop = color_stop_list.first().color_stop;
|
||||||
resolved_color_stops.first().position = first_stop.length.has_value()
|
resolved_color_stops.first().position = first_stop.position.has_value()
|
||||||
? first_stop.length->resolved(node, gradient_length).to_px(node)
|
? first_stop.position->resolved(node, gradient_length).to_px(node)
|
||||||
: 0;
|
: 0;
|
||||||
// If the last color stop does not have a position, set its position to 100%
|
// If the last color stop does not have a position, set its position to 100%
|
||||||
auto& last_stop = color_stop_list.last().color_stop;
|
auto& last_stop = color_stop_list.last().color_stop;
|
||||||
resolved_color_stops.last().position = last_stop.length.has_value()
|
resolved_color_stops.last().position = last_stop.position.has_value()
|
||||||
? last_stop.length->resolved(node, gradient_length).to_px(node)
|
? last_stop.position->resolved(node, gradient_length).to_px(node)
|
||||||
: gradient_length_px;
|
: gradient_length_px;
|
||||||
|
|
||||||
// 2. If a color stop or transition hint has a position that is less than the
|
// 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;
|
resolved_color_stops[i].transition_hint = value;
|
||||||
max_previous_color_stop_or_hint = value;
|
max_previous_color_stop_or_hint = value;
|
||||||
}
|
}
|
||||||
if (stop.color_stop.length.has_value()) {
|
if (stop.color_stop.position.has_value()) {
|
||||||
float value = stop.color_stop.length->resolved(node, gradient_length).to_px(node);
|
float value = stop.color_stop.position->resolved(node, gradient_length).to_px(node);
|
||||||
value = max(value, max_previous_color_stop_or_hint);
|
value = max(value, max_previous_color_stop_or_hint);
|
||||||
resolved_color_stops[i].position = value;
|
resolved_color_stops[i].position = value;
|
||||||
max_previous_color_stop_or_hint = 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;
|
size_t i = 1;
|
||||||
auto find_run_end = [&] {
|
auto find_run_end = [&] {
|
||||||
auto color_stop_has_position = [](auto& color_stop) {
|
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])) {
|
while (i < color_stop_list.size() - 1 && !color_stop_has_position(color_stop_list[i])) {
|
||||||
i++;
|
i++;
|
||||||
|
@ -95,7 +95,7 @@ LinearGradientData resolve_linear_gradient_data(Layout::Node const& node, Gfx::F
|
||||||
};
|
};
|
||||||
while (i < color_stop_list.size() - 1) {
|
while (i < color_stop_list.size() - 1) {
|
||||||
auto& stop = color_stop_list[i];
|
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 run_start = i - 1;
|
||||||
auto start_position = resolved_color_stops[i++].transition_hint.value_or(resolved_color_stops[run_start].position);
|
auto start_position = resolved_color_stops[i++].transition_hint.value_or(resolved_color_stops[run_start].position);
|
||||||
auto run_end = find_run_end();
|
auto run_end = find_run_end();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue