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());