From 110eeb85914a533c46e812cd7210e43ee597eab4 Mon Sep 17 00:00:00 2001 From: FalseHonesty Date: Wed, 31 May 2023 16:07:06 -0400 Subject: [PATCH] LibWeb: Support calc(...) in box-shadow's values of type Length The CSS box-shadow property takes 2-4 properties that are ``s, those being: - offset-x - offset-y - blur-radius - spread-radius Previously these were resolved directly to concrete Lengths at parse time, but now they will be parsed as LengthStyleValues and/or CalculatedStyleValues and be stored that way until styles are later resolved. --- .../box-shadow-resolves-length-functions.txt | 1 + .../box-shadow-resolves-length-functions.html | 13 ++++ .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 59 ++++++++++++------- .../CSS/ResolvedCSSStyleDeclaration.cpp | 8 ++- .../Libraries/LibWeb/CSS/StyleProperties.cpp | 57 ++++++++++++++---- .../Libraries/LibWeb/CSS/StyleProperties.h | 6 +- .../CSS/StyleValues/ShadowStyleValue.cpp | 10 ++-- .../LibWeb/CSS/StyleValues/ShadowStyleValue.h | 43 ++++++++++---- Userland/Libraries/LibWeb/Layout/Node.cpp | 4 +- 9 files changed, 143 insertions(+), 58 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/css/box-shadow-resolves-length-functions.txt create mode 100644 Tests/LibWeb/Text/input/css/box-shadow-resolves-length-functions.html diff --git a/Tests/LibWeb/Text/expected/css/box-shadow-resolves-length-functions.txt b/Tests/LibWeb/Text/expected/css/box-shadow-resolves-length-functions.txt new file mode 100644 index 00000000000000..284f5c23fb30eb --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/box-shadow-resolves-length-functions.txt @@ -0,0 +1 @@ +0 calc(5px - 10px) 0 calc(2px + 3px) => #000000ff 0px calc(5px + (0 - 10px)) 0px calc(2px + 3px) diff --git a/Tests/LibWeb/Text/input/css/box-shadow-resolves-length-functions.html b/Tests/LibWeb/Text/input/css/box-shadow-resolves-length-functions.html new file mode 100644 index 00000000000000..81bc32cdc1d2b5 --- /dev/null +++ b/Tests/LibWeb/Text/input/css/box-shadow-resolves-length-functions.html @@ -0,0 +1,13 @@ + + diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index bf230b31c91510..7fd96d046bf9ca 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -5849,12 +5849,27 @@ ErrorOr> Parser::parse_single_shadow_value(TokenStream color; - Optional offset_x; - Optional offset_y; - Optional blur_radius; - Optional spread_distance; + RefPtr offset_x; + RefPtr offset_y; + RefPtr blur_radius; + RefPtr spread_distance; Optional placement; + auto possibly_dynamic_length = [&](ComponentValue const& token) -> ErrorOr> { + if (auto maybe_dynamic_value = TRY(parse_dynamic_value(token))) { + if (!maybe_dynamic_value->is_calculated()) + return nullptr; + auto const& calculated_value = maybe_dynamic_value->as_calculated(); + if (!calculated_value.resolves_to_length()) + return nullptr; + return calculated_value; + } + auto maybe_length = parse_length(token); + if (!maybe_length.has_value()) + return nullptr; + return LengthStyleValue::create(maybe_length.release_value()); + }; + while (tokens.has_next_token()) { auto const& token = tokens.peek_token(); @@ -5866,38 +5881,38 @@ ErrorOr> Parser::parse_single_shadow_value(TokenStream> Parser::parse_single_shadow_value(TokenStream> Parser::parse_content_value(Vector const& component_values) diff --git a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp index 828082197e3e35..e43da914b9a87d 100644 --- a/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp +++ b/Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp @@ -523,8 +523,12 @@ ErrorOr> ResolvedCSSStyleDeclaration::style_value_for_p if (box_shadow_layers.is_empty()) return nullptr; - auto make_box_shadow_style_value = [](ShadowData const& data) { - return ShadowStyleValue::create(data.color, data.offset_x, data.offset_y, data.blur_radius, data.spread_distance, data.placement); + auto make_box_shadow_style_value = [](ShadowData const& data) -> ErrorOr> { + auto offset_x = TRY(LengthStyleValue::create(data.offset_x)); + auto offset_y = TRY(LengthStyleValue::create(data.offset_y)); + auto blur_radius = TRY(LengthStyleValue::create(data.blur_radius)); + auto spread_distance = TRY(LengthStyleValue::create(data.spread_distance)); + return ShadowStyleValue::create(data.color, offset_x, offset_y, blur_radius, spread_distance, data.placement); }; if (box_shadow_layers.size() == 1) diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index e8110f531ca72c..1ac07b7c947b69 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -751,41 +751,74 @@ Optional StyleProperties::overflow(CSS::PropertyID property_id) c return value_id_to_overflow(value->to_identifier()); } -Vector StyleProperties::shadow(PropertyID property_id) const +Vector StyleProperties::shadow(PropertyID property_id, Layout::Node const& layout_node) const { auto value = property(property_id); - auto make_shadow_data = [](ShadowStyleValue const& value) { - return ShadowData { value.color(), value.offset_x(), value.offset_y(), value.blur_radius(), value.spread_distance(), value.placement() }; + auto resolve_to_length = [&layout_node](NonnullRefPtr const& value) -> Optional { + if (value->is_length()) + return value->as_length().length(); + if (value->is_calculated()) + return value->as_calculated().resolve_length(layout_node); + return {}; + }; + + auto make_shadow_data = [resolve_to_length](ShadowStyleValue const& value) -> Optional { + auto maybe_offset_x = resolve_to_length(value.offset_x()); + if (!maybe_offset_x.has_value()) + return {}; + auto maybe_offset_y = resolve_to_length(value.offset_y()); + if (!maybe_offset_y.has_value()) + return {}; + auto maybe_blur_radius = resolve_to_length(value.blur_radius()); + if (!maybe_blur_radius.has_value()) + return {}; + auto maybe_spread_distance = resolve_to_length(value.spread_distance()); + if (!maybe_spread_distance.has_value()) + return {}; + return ShadowData { + value.color(), + maybe_offset_x.release_value(), + maybe_offset_y.release_value(), + maybe_blur_radius.release_value(), + maybe_spread_distance.release_value(), + value.placement() + }; }; if (value->is_value_list()) { - auto& value_list = value->as_value_list(); + auto const& value_list = value->as_value_list(); Vector shadow_data; shadow_data.ensure_capacity(value_list.size()); - for (auto const& layer_value : value_list.values()) - shadow_data.append(make_shadow_data(layer_value->as_shadow())); + for (auto const& layer_value : value_list.values()) { + auto maybe_shadow_data = make_shadow_data(layer_value->as_shadow()); + if (!maybe_shadow_data.has_value()) + return {}; + shadow_data.append(maybe_shadow_data.release_value()); + } return shadow_data; } if (value->is_shadow()) { - auto& box = value->as_shadow(); - return { make_shadow_data(box) }; + auto maybe_shadow_data = make_shadow_data(value->as_shadow()); + if (!maybe_shadow_data.has_value()) + return {}; + return { maybe_shadow_data.release_value() }; } return {}; } -Vector StyleProperties::box_shadow() const +Vector StyleProperties::box_shadow(Layout::Node const& layout_node) const { - return shadow(PropertyID::BoxShadow); + return shadow(PropertyID::BoxShadow, layout_node); } -Vector StyleProperties::text_shadow() const +Vector StyleProperties::text_shadow(Layout::Node const& layout_node) const { - return shadow(PropertyID::TextShadow); + return shadow(PropertyID::TextShadow, layout_node); } Optional StyleProperties::box_sizing() const diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h index 675325cf457029..56758da90e985c 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h @@ -64,7 +64,7 @@ class StyleProperties : public RefCounted { Vector text_decoration_line() const; Optional text_decoration_style() const; Optional text_transform() const; - Vector text_shadow() const; + Vector text_shadow(Layout::Node const&) const; Optional list_style_type() const; Optional list_style_position() const; Optional flex_direction() const; @@ -85,7 +85,7 @@ class StyleProperties : public RefCounted { Optional justify_content() const; Optional overflow_x() const; Optional overflow_y() const; - Vector box_shadow() const; + Vector box_shadow(Layout::Node const&) const; Optional box_sizing() const; Optional pointer_events() const; Variant vertical_align() const; @@ -141,7 +141,7 @@ class StyleProperties : public RefCounted { }; Array, to_underlying(CSS::last_property_id) + 1> m_property_values; Optional overflow(CSS::PropertyID) const; - Vector shadow(CSS::PropertyID) const; + Vector shadow(CSS::PropertyID, Layout::Node const&) const; mutable RefPtr m_font; }; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ShadowStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/ShadowStyleValue.cpp index a94669dd46e540..d6516f6a31db50 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ShadowStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ShadowStyleValue.cpp @@ -14,7 +14,7 @@ namespace Web::CSS { ErrorOr ShadowStyleValue::to_string() const { StringBuilder builder; - TRY(builder.try_appendff("{} {} {} {} {}", m_properties.color.to_deprecated_string(), TRY(m_properties.offset_x.to_string()), TRY(m_properties.offset_y.to_string()), TRY(m_properties.blur_radius.to_string()), TRY(m_properties.spread_distance.to_string()))); + TRY(builder.try_appendff("{} {} {} {} {}", m_properties.color.to_deprecated_string(), TRY(m_properties.offset_x->to_string()), TRY(m_properties.offset_y->to_string()), TRY(m_properties.blur_radius->to_string()), TRY(m_properties.spread_distance->to_string()))); if (m_properties.placement == ShadowPlacement::Inner) TRY(builder.try_append(" inset"sv)); return builder.to_string(); @@ -22,10 +22,10 @@ ErrorOr ShadowStyleValue::to_string() const ErrorOr> ShadowStyleValue::absolutized(CSSPixelRect const& viewport_rect, Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const { - auto absolutized_offset_x = m_properties.offset_x.absolutized(viewport_rect, font_metrics, root_font_metrics); - auto absolutized_offset_y = m_properties.offset_y.absolutized(viewport_rect, font_metrics, root_font_metrics); - auto absolutized_blur_radius = m_properties.blur_radius.absolutized(viewport_rect, font_metrics, root_font_metrics); - auto absolutized_spread_distance = m_properties.spread_distance.absolutized(viewport_rect, font_metrics, root_font_metrics); + auto absolutized_offset_x = TRY(m_properties.offset_x->absolutized(viewport_rect, font_metrics, root_font_metrics)); + auto absolutized_offset_y = TRY(m_properties.offset_y->absolutized(viewport_rect, font_metrics, root_font_metrics)); + auto absolutized_blur_radius = TRY(m_properties.blur_radius->absolutized(viewport_rect, font_metrics, root_font_metrics)); + auto absolutized_spread_distance = TRY(m_properties.spread_distance->absolutized(viewport_rect, font_metrics, root_font_metrics)); return ShadowStyleValue::create(m_properties.color, absolutized_offset_x, absolutized_offset_y, absolutized_blur_radius, absolutized_spread_distance, m_properties.placement); } diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ShadowStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/ShadowStyleValue.h index 4932e6951b00e7..1c90ba5b984b4a 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ShadowStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ShadowStyleValue.h @@ -22,17 +22,23 @@ enum class ShadowPlacement { class ShadowStyleValue final : public StyleValueWithDefaultOperators { public: - static ErrorOr> create(Color color, Length const& offset_x, Length const& offset_y, Length const& blur_radius, Length const& spread_distance, ShadowPlacement placement) + static ErrorOr> create( + Color color, + ValueComparingNonnullRefPtr offset_x, + ValueComparingNonnullRefPtr offset_y, + ValueComparingNonnullRefPtr blur_radius, + ValueComparingNonnullRefPtr spread_distance, + ShadowPlacement placement) { - return adopt_nonnull_ref_or_enomem(new (nothrow) ShadowStyleValue(color, offset_x, offset_y, blur_radius, spread_distance, placement)); + return adopt_nonnull_ref_or_enomem(new (nothrow) ShadowStyleValue(color, move(offset_x), move(offset_y), move(blur_radius), move(spread_distance), placement)); } virtual ~ShadowStyleValue() override = default; Color color() const { return m_properties.color; } - Length const& offset_x() const { return m_properties.offset_x; } - Length const& offset_y() const { return m_properties.offset_y; } - Length const& blur_radius() const { return m_properties.blur_radius; } - Length const& spread_distance() const { return m_properties.spread_distance; } + ValueComparingNonnullRefPtr const& offset_x() const { return m_properties.offset_x; } + ValueComparingNonnullRefPtr const& offset_y() const { return m_properties.offset_y; } + ValueComparingNonnullRefPtr const& blur_radius() const { return m_properties.blur_radius; } + ValueComparingNonnullRefPtr const& spread_distance() const { return m_properties.spread_distance; } ShadowPlacement placement() const { return m_properties.placement; } virtual ErrorOr to_string() const override; @@ -40,9 +46,22 @@ class ShadowStyleValue final : public StyleValueWithDefaultOperators offset_x, + ValueComparingNonnullRefPtr offset_y, + ValueComparingNonnullRefPtr blur_radius, + ValueComparingNonnullRefPtr spread_distance, + ShadowPlacement placement) : StyleValueWithDefaultOperators(Type::Shadow) - , m_properties { .color = color, .offset_x = offset_x, .offset_y = offset_y, .blur_radius = blur_radius, .spread_distance = spread_distance, .placement = placement } + , m_properties { + .color = color, + .offset_x = move(offset_x), + .offset_y = move(offset_y), + .blur_radius = move(blur_radius), + .spread_distance = move(spread_distance), + .placement = placement + } { } @@ -50,10 +69,10 @@ class ShadowStyleValue final : public StyleValueWithDefaultOperators offset_x; + ValueComparingNonnullRefPtr offset_y; + ValueComparingNonnullRefPtr blur_radius; + ValueComparingNonnullRefPtr spread_distance; ShadowPlacement placement; bool operator==(Properties const&) const = default; } m_properties; diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index cca409a8932c9b..65e5d684e2173d 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -594,7 +594,7 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style) if (auto maybe_text_decoration_thickness = computed_style.length_percentage(CSS::PropertyID::TextDecorationThickness); maybe_text_decoration_thickness.has_value()) computed_values.set_text_decoration_thickness(maybe_text_decoration_thickness.release_value()); - computed_values.set_text_shadow(computed_style.text_shadow()); + computed_values.set_text_shadow(computed_style.text_shadow(*this)); computed_values.set_z_index(computed_style.z_index()); computed_values.set_opacity(computed_style.opacity()); @@ -616,7 +616,7 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style) computed_values.set_margin(computed_style.length_box(CSS::PropertyID::MarginLeft, CSS::PropertyID::MarginTop, CSS::PropertyID::MarginRight, CSS::PropertyID::MarginBottom, CSS::Length::make_px(0))); computed_values.set_padding(computed_style.length_box(CSS::PropertyID::PaddingLeft, CSS::PropertyID::PaddingTop, CSS::PropertyID::PaddingRight, CSS::PropertyID::PaddingBottom, CSS::Length::make_px(0))); - computed_values.set_box_shadow(computed_style.box_shadow()); + computed_values.set_box_shadow(computed_style.box_shadow(*this)); computed_values.set_transformations(computed_style.transformations()); computed_values.set_transform_origin(computed_style.transform_origin());