From b4dd4776443922f9bf9e3b743e807abe53c3ec94 Mon Sep 17 00:00:00 2001 From: Tom Date: Sun, 31 Jul 2022 18:46:35 +0200 Subject: [PATCH] LibWeb: Parse rect style value Add ability to parse a rect when it is used as the value of a style property. --- .../LibWeb/GenerateCSSPropertyID.cpp | 5 +++ .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 44 +++++++++++++++++++ Userland/Libraries/LibWeb/CSS/Parser/Parser.h | 1 + Userland/Libraries/LibWeb/CSS/Properties.json | 3 ++ Userland/Libraries/LibWeb/CSS/StyleValue.cpp | 29 ++++++++++++ Userland/Libraries/LibWeb/CSS/StyleValue.h | 34 ++++++++++++++ Userland/Libraries/LibWeb/Forward.h | 1 + 7 files changed, 117 insertions(+) diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPropertyID.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPropertyID.cpp index a80ea8d835..8ca71305e3 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPropertyID.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPropertyID.cpp @@ -501,6 +501,11 @@ bool property_accepts_value(PropertyID property_id, StyleValue& style_value) output_numeric_value_check(property_generator, "has_number"sv, "to_number()"sv, Array { "Integer"sv, "Number"sv }, min_value, max_value); } else if (type_name == "percentage") { output_numeric_value_check(property_generator, "is_percentage"sv, "as_percentage().percentage().value()"sv, Array { "Percentage"sv }, min_value, max_value); + } else if (type_name == "rect") { + property_generator.append(R"~~~( + if (style_value.has_rect()) + return true; +)~~~"); } else if (type_name == "resolution") { output_numeric_value_check(property_generator, "is_resolution"sv, "as_resolution().resolution().to_dots_per_pixel()"sv, Array {}, min_value, max_value); } else if (type_name == "string") { diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 8b676e5a1e..7389134c2d 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -3350,6 +3351,46 @@ Optional Parser::parse_rgb_or_hsl_color(StringView function_name, Vector< return {}; } +// https://www.w3.org/TR/CSS2/visufx.html#value-def-shape +RefPtr Parser::parse_rect_value(ComponentValue const& component_value) +{ + if (!component_value.is_function()) + return {}; + auto& function = component_value.function(); + if (!function.name().equals_ignoring_case("rect"sv)) + return {}; + + Vector params; + auto tokens = TokenStream { function.values() }; + + // In CSS 2.1, the only valid value is: rect(, , , ) where + // and specify offsets from the top border edge of the box, and , and + // specify offsets from the left border edge of the box. + for (size_t idx = 0; idx < 4; idx++) { + tokens.skip_whitespace(); + + // , , , and may either have a value or 'auto'. + // Negative lengths are permitted. + auto current_token = tokens.next_token().token(); + if (current_token.to_string() == "auto") { + params.append(Length::make_auto()); + } else { + auto maybe_length = parse_length(current_token); + if (!maybe_length.has_value()) + return {}; + params.append(maybe_length.value()); + } + tokens.skip_whitespace(); + + // Authors should separate offset values with commas. User agents must support separation + // with commas, but may also support separation without commas (but not a combination), + // because a previous revision of this specification was ambiguous in this respect. + if (tokens.peek_token().is(Token::Type::Comma)) + tokens.next_token(); + } + return RectStyleValue::create(EdgeRect { params[0], params[1], params[2], params[3] }); +} + Optional Parser::parse_color(ComponentValue const& component_value) { // https://www.w3.org/TR/css-color-4/ @@ -5416,6 +5457,9 @@ RefPtr Parser::parse_css_value(ComponentValue const& component_value if (auto image = parse_image_value(component_value)) return image; + if (auto rect = parse_rect_value(component_value)) + return rect; + return {}; } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index a59c17155a..48258aef9b 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -328,6 +328,7 @@ private: RefPtr parse_numeric_value(ComponentValue const&); RefPtr parse_identifier_value(ComponentValue const&); RefPtr parse_color_value(ComponentValue const&); + RefPtr parse_rect_value(ComponentValue const&); RefPtr parse_string_value(ComponentValue const&); RefPtr parse_image_value(ComponentValue const&); template diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json index 02678cf9fb..d3e18ba3c8 100644 --- a/Userland/Libraries/LibWeb/CSS/Properties.json +++ b/Userland/Libraries/LibWeb/CSS/Properties.json @@ -475,6 +475,9 @@ "valid-identifiers": [ "auto" ], + "valid-types": [ + "rect" + ], "quirks": [ "unitless-length" ] diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp index 234b947bc4..6f71ca239a 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -179,6 +179,12 @@ PositionStyleValue const& StyleValue::as_position() const return static_cast(*this); } +RectStyleValue const& StyleValue::as_rect() const +{ + VERIFY(is_rect()); + return static_cast(*this); +} + ResolutionStyleValue const& StyleValue::as_resolution() const { VERIFY(is_resolution()); @@ -1485,6 +1491,11 @@ static bool operator==(ColorStopListElement a, ColorStopListElement b) return a.transition_hint == b.transition_hint && a.color_stop == b.color_stop; } +static bool operator==(EdgeRect a, EdgeRect b) +{ + return a.top_edge == b.top_edge && a.right_edge == b.right_edge && a.bottom_edge == b.bottom_edge && a.left_edge == b.left_edge; +} + bool LinearGradientStyleValue::equals(StyleValue const& other_) const { if (type() != other_.type()) @@ -1650,6 +1661,19 @@ bool PositionStyleValue::equals(StyleValue const& other) const && m_offset_y == typed_other.m_offset_y; } +String RectStyleValue::to_string() const +{ + return String::formatted("top_edge: {}, right_edge: {}, bottom_edge: {}, left_edge: {}", m_rect.top_edge, m_rect.right_edge, m_rect.bottom_edge, m_rect.left_edge); +} + +bool RectStyleValue::equals(StyleValue const& other) const +{ + if (type() != other.type()) + return false; + auto const& typed_other = other.as_rect(); + return m_rect == typed_other.rect(); +} + bool ResolutionStyleValue::equals(StyleValue const& other) const { if (type() != other.type()) @@ -1810,6 +1834,11 @@ NonnullRefPtr ColorStyleValue::create(Color color) return adopt_ref(*new ColorStyleValue(color)); } +NonnullRefPtr RectStyleValue::create(EdgeRect rect) +{ + return adopt_ref(*new RectStyleValue(rect)); +} + NonnullRefPtr LengthStyleValue::create(Length const& length) { if (length.is_auto()) { diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index b28a579a1a..cea155528d 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -84,6 +84,13 @@ struct ColorStopListElement { GradientColorStop color_stop; }; +struct EdgeRect { + Length top_edge; + Length right_edge; + Length bottom_edge; + Length left_edge; +}; + // FIXME: Find a better place for this helper. inline Gfx::Painter::ScalingMode to_gfx_scaling_mode(CSS::ImageRendering css_value) { @@ -131,6 +138,7 @@ public: Overflow, Percentage, Position, + Rect, Resolution, Shadow, String, @@ -169,6 +177,7 @@ public: bool is_overflow() const { return type() == Type::Overflow; } bool is_percentage() const { return type() == Type::Percentage; } bool is_position() const { return type() == Type::Position; } + bool is_rect() const { return type() == Type::Rect; } bool is_resolution() const { return type() == Type::Resolution; } bool is_shadow() const { return type() == Type::Shadow; } bool is_string() const { return type() == Type::String; } @@ -206,6 +215,7 @@ public: OverflowStyleValue const& as_overflow() const; PercentageStyleValue const& as_percentage() const; PositionStyleValue const& as_position() const; + RectStyleValue const& as_rect() const; ResolutionStyleValue const& as_resolution() const; ShadowStyleValue const& as_shadow() const; StringStyleValue const& as_string() const; @@ -241,6 +251,7 @@ public: OverflowStyleValue& as_overflow() { return const_cast(const_cast(*this).as_overflow()); } PercentageStyleValue& as_percentage() { return const_cast(const_cast(*this).as_percentage()); } PositionStyleValue& as_position() { return const_cast(const_cast(*this).as_position()); } + RectStyleValue& as_rect() { return const_cast(const_cast(*this).as_rect()); } ResolutionStyleValue& as_resolution() { return const_cast(const_cast(*this).as_resolution()); } ShadowStyleValue& as_shadow() { return const_cast(const_cast(*this).as_shadow()); } StringStyleValue& as_string() { return const_cast(const_cast(*this).as_string()); } @@ -255,12 +266,14 @@ public: virtual bool has_color() const { return false; } virtual bool has_identifier() const { return false; } virtual bool has_length() const { return false; } + virtual bool has_rect() const { return false; } virtual bool has_number() const { return false; } virtual bool has_integer() const { return false; } virtual NonnullRefPtr absolutized(Gfx::IntRect const& viewport_rect, Gfx::FontPixelMetrics const& font_metrics, float font_size, float root_font_size) const; virtual Color to_color(Layout::NodeWithStyle const&) const { return {}; } + virtual EdgeRect to_rect() const { VERIFY_NOT_REACHED(); } virtual CSS::ValueID to_identifier() const { return ValueID::Invalid; } virtual Length to_length() const { VERIFY_NOT_REACHED(); } virtual float to_number() const { return 0; } @@ -1440,6 +1453,27 @@ private: NonnullRefPtrVector m_values; }; +class RectStyleValue : public StyleValue { +public: + static NonnullRefPtr create(EdgeRect rect); + virtual ~RectStyleValue() override = default; + + EdgeRect rect() const { return m_rect; } + virtual String to_string() const override; + virtual bool has_rect() const override { return true; } + virtual EdgeRect to_rect() const override { return m_rect; } + virtual bool equals(StyleValue const& other) const override; + +private: + explicit RectStyleValue(EdgeRect rect) + : StyleValue(Type::Rect) + , m_rect(rect) + { + } + + EdgeRect m_rect; +}; + } template<> diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 0db319a442..f92cc6e1c9 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -75,6 +75,7 @@ class Percentage; class PercentageStyleValue; class PositionStyleValue; class PropertyOwningCSSStyleDeclaration; +class RectStyleValue; class Resolution; class ResolutionStyleValue; class Screen;