From 631a988a5736ba3d490a6cf03c3dc2490818e370 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Wed, 16 Aug 2023 12:45:23 +0100 Subject: [PATCH] LibWeb: Allow any valid `` in CSS gradients We now keep the color value as a StyleValue up until we go to paint the gradient, which makes `currentColor` work, along with any other color values that can't be immediately converted into a `Gfx::Color` while parsing. --- .../Ref/css-gradient-currentcolor-ref.html | 10 ++++++++++ Tests/LibWeb/Ref/css-gradient-currentcolor.html | 11 +++++++++++ Tests/LibWeb/Ref/manifest.json | 3 ++- Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp | 14 +++++++------- .../CSS/StyleValues/AbstractImageStyleValue.h | 6 +++--- .../CSS/StyleValues/ConicGradientStyleValue.cpp | 3 ++- .../CSS/StyleValues/ConicGradientStyleValue.h | 2 +- .../StyleValues/LinearGradientStyleValue.cpp | 2 +- .../CSS/StyleValues/LinearGradientStyleValue.h | 2 +- .../StyleValues/RadialGradientStyleValue.cpp | 3 ++- .../CSS/StyleValues/RadialGradientStyleValue.h | 2 +- .../LibWeb/Painting/GradientPainting.cpp | 17 +++++++++-------- .../LibWeb/Painting/GradientPainting.h | 6 +++--- 13 files changed, 53 insertions(+), 28 deletions(-) create mode 100644 Tests/LibWeb/Ref/css-gradient-currentcolor-ref.html create mode 100644 Tests/LibWeb/Ref/css-gradient-currentcolor.html diff --git a/Tests/LibWeb/Ref/css-gradient-currentcolor-ref.html b/Tests/LibWeb/Ref/css-gradient-currentcolor-ref.html new file mode 100644 index 0000000000..1f0ebff8b2 --- /dev/null +++ b/Tests/LibWeb/Ref/css-gradient-currentcolor-ref.html @@ -0,0 +1,10 @@ + + +
+ diff --git a/Tests/LibWeb/Ref/css-gradient-currentcolor.html b/Tests/LibWeb/Ref/css-gradient-currentcolor.html new file mode 100644 index 0000000000..e1be1ba6ff --- /dev/null +++ b/Tests/LibWeb/Ref/css-gradient-currentcolor.html @@ -0,0 +1,11 @@ + + +
+ diff --git a/Tests/LibWeb/Ref/manifest.json b/Tests/LibWeb/Ref/manifest.json index 9a65ccf16c..e47c0298fd 100644 --- a/Tests/LibWeb/Ref/manifest.json +++ b/Tests/LibWeb/Ref/manifest.json @@ -1,5 +1,6 @@ { "square-flex.html": "square-ref.html", "separate-borders-inline-table.html": "separate-borders-ref.html", - "opacity-stacking.html": "opacity-stacking-ref.html" + "opacity-stacking.html": "opacity-stacking-ref.html", + "css-gradient-currentcolor.html": "css-gradient-currentcolor-ref.html" } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 4be5ec262f..f4b7460784 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -2419,7 +2419,7 @@ static Optional> parse_color_stop_list(auto& tokens, auto is_po return ElementType::Garbage; auto const& token = tokens.next_token(); - Gfx::Color color; + RefPtr color; Optional position; Optional second_position; auto dimension = parse_dimension(token); @@ -2434,15 +2434,15 @@ static Optional> parse_color_stop_list(auto& tokens, auto is_po } // auto maybe_color = parse_color(tokens.next_token()); - if (!maybe_color.has_value()) + if (maybe_color.is_error()) return ElementType::Garbage; - color = *maybe_color; + color = maybe_color.release_value(); } else { // [ ?] auto maybe_color = parse_color(token); - if (!maybe_color.has_value()) + if (maybe_color.is_error()) return ElementType::Garbage; - color = *maybe_color; + color = maybe_color.release_value(); tokens.skip_whitespace(); // Allow up to [ ] (double-position color stops) // Note: Double-position color stops only appear to be valid in this order. @@ -2503,7 +2503,7 @@ Optional> Parser::parse_linear_color_stop_lis tokens, [](Dimension& dimension) { return dimension.is_length_percentage(); }, [](Dimension& dimension) { return dimension.length_percentage(); }, - [&](auto& token) { return parse_color(token); }, + [&](auto& token) { return parse_color_value(token); }, [&](auto& token) { return parse_dimension(token); }); } @@ -2515,7 +2515,7 @@ Optional> Parser::parse_angular_color_stop_l tokens, [](Dimension& dimension) { return dimension.is_angle_percentage(); }, [](Dimension& dimension) { return dimension.angle_percentage(); }, - [&](auto& token) { return parse_color(token); }, + [&](auto& token) { return parse_color_value(token); }, [&](auto& token) { return parse_dimension(token); }); } diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h index 692b822571..79777124cd 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h @@ -24,7 +24,7 @@ public: virtual Optional natural_height() const { return {}; } virtual void load_any_resources(DOM::Document&) {}; - virtual void resolve_for_size(Layout::Node const&, CSSPixelSize) const {}; + virtual void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const {}; virtual bool is_paintable() const = 0; virtual void paint(PaintContext& context, DevicePixelRect const& dest_rect, ImageRendering) const = 0; @@ -47,7 +47,7 @@ struct ColorStopListElement { Optional transition_hint; struct ColorStop { - Color color; + RefPtr color; Optional position; Optional second_position = {}; inline bool operator==(ColorStop const&) const = default; @@ -69,7 +69,7 @@ static ErrorOr serialize_color_stop_list(StringBuilder& builder, auto cons if (element.transition_hint.has_value()) TRY(builder.try_appendff("{}, "sv, TRY(element.transition_hint->value.to_string()))); - TRY(serialize_a_srgb_value(builder, element.color_stop.color)); + TRY(builder.try_append(TRY(element.color_stop.color->to_string()))); for (auto position : Array { &element.color_stop.position, &element.color_stop.second_position }) { if (position->has_value()) TRY(builder.try_appendff(" {}"sv, TRY((*position)->to_string()))); diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp index 8b2d61be9c..a685a90f15 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp @@ -8,6 +8,7 @@ */ #include "ConicGradientStyleValue.h" +#include namespace Web::CSS { @@ -34,7 +35,7 @@ ErrorOr ConicGradientStyleValue::to_string() const return builder.to_string(); } -void ConicGradientStyleValue::resolve_for_size(Layout::Node const& node, CSSPixelSize size) const +void ConicGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const& node, CSSPixelSize size) const { if (!m_resolved.has_value()) m_resolved = ResolvedData { Painting::resolve_conic_gradient_data(node, *this), {} }; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h index 7b40ffcae1..5603d0e517 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h @@ -39,7 +39,7 @@ public: bool is_paintable() const override { return true; } - void resolve_for_size(Layout::Node const&, CSSPixelSize) const override; + void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const override; virtual ~ConicGradientStyleValue() override = default; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp index 8119d6fb84..0a1b244988 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp @@ -102,7 +102,7 @@ float LinearGradientStyleValue::angle_degrees(CSSPixelSize gradient_size) const }); } -void LinearGradientStyleValue::resolve_for_size(Layout::Node const& node, CSSPixelSize size) const +void LinearGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const& node, CSSPixelSize size) const { if (m_resolved.has_value() && m_resolved->size == size) return; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h index 4e1f2de70c..4c5e08a565 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h @@ -57,7 +57,7 @@ public: float angle_degrees(CSSPixelSize gradient_size) const; - void resolve_for_size(Layout::Node const&, CSSPixelSize) const override; + void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const override; bool is_paintable() const override { return true; } void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const override; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp index f35d230677..ecae87b16e 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp @@ -8,6 +8,7 @@ */ #include "RadialGradientStyleValue.h" +#include namespace Web::CSS { @@ -184,7 +185,7 @@ Gfx::FloatSize RadialGradientStyleValue::resolve_size(Layout::Node const& node, return resolved_size; } -void RadialGradientStyleValue::resolve_for_size(Layout::Node const& node, CSSPixelSize paint_size) const +void RadialGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const& node, CSSPixelSize paint_size) const { CSSPixelRect gradient_box { { 0, 0 }, paint_size }; auto center = m_properties.position.resolved(node, gradient_box).to_type(); diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h index 96aec112a1..7b1a0d1a54 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h @@ -63,7 +63,7 @@ public: bool is_paintable() const override { return true; } - void resolve_for_size(Layout::Node const&, CSSPixelSize) const override; + void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const override; Gfx::FloatSize resolve_size(Layout::Node const&, Gfx::FloatPoint, Gfx::FloatRect const&) const; diff --git a/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp b/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp index e9d639141c..b5fce68190 100644 --- a/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp +++ b/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp @@ -10,11 +10,12 @@ #include #include #include +#include #include namespace Web::Painting { -static ColorStopData resolve_color_stop_positions(auto const& color_stop_list, auto resolve_position_to_float, bool repeating) +static ColorStopData resolve_color_stop_positions(Layout::NodeWithStyleAndBoxModelMetrics const& node, auto const& color_stop_list, auto resolve_position_to_float, bool repeating) { VERIFY(color_stop_list.size() >= 2); ColorStopList resolved_color_stops; @@ -29,7 +30,7 @@ static ColorStopData resolve_color_stop_positions(auto const& color_stop_list, a resolved_color_stops.ensure_capacity(expanded_size); for (auto& stop : color_stop_list) { - auto resolved_stop = Gfx::ColorStop { .color = stop.color_stop.color }; + auto resolved_stop = Gfx::ColorStop { .color = stop.color_stop.color->to_color(node) }; for (int i = 0; i < color_stop_length(stop); i++) resolved_color_stops.append(resolved_stop); } @@ -109,13 +110,13 @@ static ColorStopData resolve_color_stop_positions(auto const& color_stop_list, a return { resolved_color_stops, repeat_length }; } -LinearGradientData resolve_linear_gradient_data(Layout::Node const& node, CSSPixelSize gradient_size, CSS::LinearGradientStyleValue const& linear_gradient) +LinearGradientData resolve_linear_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const& node, CSSPixelSize gradient_size, CSS::LinearGradientStyleValue const& linear_gradient) { auto gradient_angle = linear_gradient.angle_degrees(gradient_size); auto gradient_length_px = Gfx::calculate_gradient_length(gradient_size.to_type(), gradient_angle); auto resolved_color_stops = resolve_color_stop_positions( - linear_gradient.color_stop_list(), [&](auto const& length_percentage) { + node, linear_gradient.color_stop_list(), [&](auto const& length_percentage) { return length_percentage.to_px(node, gradient_length_px).to_float() / static_cast(gradient_length_px); }, linear_gradient.is_repeating()); @@ -123,22 +124,22 @@ LinearGradientData resolve_linear_gradient_data(Layout::Node const& node, CSSPix return { gradient_angle, resolved_color_stops }; } -ConicGradientData resolve_conic_gradient_data(Layout::Node const& node, CSS::ConicGradientStyleValue const& conic_gradient) +ConicGradientData resolve_conic_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const& node, CSS::ConicGradientStyleValue const& conic_gradient) { CSS::Angle one_turn(360.0f, CSS::Angle::Type::Deg); auto resolved_color_stops = resolve_color_stop_positions( - conic_gradient.color_stop_list(), [&](auto const& angle_percentage) { + node, conic_gradient.color_stop_list(), [&](auto const& angle_percentage) { return angle_percentage.resolved(node, one_turn).to_degrees() / one_turn.to_degrees(); }, conic_gradient.is_repeating()); return { conic_gradient.angle_degrees(), resolved_color_stops }; } -RadialGradientData resolve_radial_gradient_data(Layout::Node const& node, CSSPixelSize gradient_size, CSS::RadialGradientStyleValue const& radial_gradient) +RadialGradientData resolve_radial_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const& node, CSSPixelSize gradient_size, CSS::RadialGradientStyleValue const& radial_gradient) { // Start center, goes right to ending point, where the gradient line intersects the ending shape auto resolved_color_stops = resolve_color_stop_positions( - radial_gradient.color_stop_list(), [&](auto const& length_percentage) { + node, radial_gradient.color_stop_list(), [&](auto const& length_percentage) { return (length_percentage.to_px(node, gradient_size.width()) / gradient_size.width()).to_float(); }, radial_gradient.is_repeating()); diff --git a/Userland/Libraries/LibWeb/Painting/GradientPainting.h b/Userland/Libraries/LibWeb/Painting/GradientPainting.h index 5154da16d5..3ca5ea4c19 100644 --- a/Userland/Libraries/LibWeb/Painting/GradientPainting.h +++ b/Userland/Libraries/LibWeb/Painting/GradientPainting.h @@ -36,9 +36,9 @@ struct RadialGradientData { ColorStopData color_stops; }; -LinearGradientData resolve_linear_gradient_data(Layout::Node const&, CSSPixelSize, CSS::LinearGradientStyleValue const&); -ConicGradientData resolve_conic_gradient_data(Layout::Node const&, CSS::ConicGradientStyleValue const&); -RadialGradientData resolve_radial_gradient_data(Layout::Node const&, CSSPixelSize, CSS::RadialGradientStyleValue const&); +LinearGradientData resolve_linear_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize, CSS::LinearGradientStyleValue const&); +ConicGradientData resolve_conic_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSS::ConicGradientStyleValue const&); +RadialGradientData resolve_radial_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize, CSS::RadialGradientStyleValue const&); void paint_linear_gradient(PaintContext&, DevicePixelRect const&, LinearGradientData const&); void paint_conic_gradient(PaintContext&, DevicePixelRect const&, ConicGradientData const&, DevicePixelPoint position);