Browse Source

LibWeb: Parse and plumb background-position-x/y

This parses the new background-position-x/y longhands and properly
hooks up them up. This requires converting PositionStyleValue to
just contain two EdgeStyleValues so that it can be easily expanded
into the longhands.
MacDue 2 years ago
parent
commit
bed55ac669

+ 94 - 30
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -41,6 +41,7 @@
 #include <LibWeb/CSS/StyleValues/ColorStyleValue.h>
 #include <LibWeb/CSS/StyleValues/ConicGradientStyleValue.h>
 #include <LibWeb/CSS/StyleValues/ContentStyleValue.h>
+#include <LibWeb/CSS/StyleValues/EdgeStyleValue.h>
 #include <LibWeb/CSS/StyleValues/FilterValueListStyleValue.h>
 #include <LibWeb/CSS/StyleValues/FlexFlowStyleValue.h>
 #include <LibWeb/CSS/StyleValues/FlexStyleValue.h>
@@ -4270,6 +4271,33 @@ RefPtr<StyleValue> Parser::parse_background_value(Vector<ComponentValue> const&
         background_clip.release_nonnull());
 }
 
+static Optional<PositionEdge> identifier_to_edge(ValueID identifier)
+{
+    switch (identifier) {
+    case ValueID::Top:
+        return PositionEdge::Top;
+    case ValueID::Bottom:
+        return PositionEdge::Bottom;
+    case ValueID::Left:
+        return PositionEdge::Left;
+    case ValueID::Right:
+        return PositionEdge::Right;
+    default:
+        return {};
+    }
+};
+
+static Optional<LengthPercentage> style_value_to_length_percentage(auto value)
+{
+    if (value->is_percentage())
+        return LengthPercentage { value->as_percentage().percentage() };
+    if (value->has_length())
+        return LengthPercentage { value->to_length() };
+    if (value->is_calculated())
+        return LengthPercentage { value->as_calculated() };
+    return {};
+};
+
 RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<ComponentValue>& tokens)
 {
     // NOTE: This *looks* like it parses a <position>, but it doesn't. From the spec:
@@ -4281,20 +4309,6 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
 
     auto transaction = tokens.begin_transaction();
 
-    auto to_edge = [](ValueID identifier) -> Optional<PositionEdge> {
-        switch (identifier) {
-        case ValueID::Top:
-            return PositionEdge::Top;
-        case ValueID::Bottom:
-            return PositionEdge::Bottom;
-        case ValueID::Left:
-            return PositionEdge::Left;
-        case ValueID::Right:
-            return PositionEdge::Right;
-        default:
-            return {};
-        }
-    };
     auto is_horizontal = [](ValueID identifier) -> bool {
         switch (identifier) {
         case ValueID::Left:
@@ -4328,16 +4342,6 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
     auto const center_offset = Percentage { 50 };
     auto const zero_offset = Length::make_px(0);
 
-    auto value_to_length_percentage = [&](auto value) -> Optional<LengthPercentage> {
-        if (value->is_percentage())
-            return LengthPercentage { value->as_percentage().percentage() };
-        if (value->has_length())
-            return LengthPercentage { value->to_length() };
-        if (value->is_calculated())
-            return LengthPercentage { value->as_calculated() };
-        return {};
-    };
-
     while (tokens.has_next_token()) {
         // Check if we're done
         auto seen_items = (horizontal.has_value() ? 1 : 0) + (vertical.has_value() ? 1 : 0) + (found_center ? 1 : 0);
@@ -4351,7 +4355,7 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
         tokens.next_token();
         auto value = maybe_value.release_nonnull();
 
-        auto offset = value_to_length_percentage(value);
+        auto offset = style_value_to_length_percentage(value);
         if (offset.has_value()) {
             if (!horizontal.has_value()) {
                 horizontal = EdgeOffset { PositionEdge::Left, *offset, false, true };
@@ -4369,7 +4373,7 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
                 auto maybe_value = parse_css_value(token);
                 if (!maybe_value)
                     return zero_offset;
-                auto offset = value_to_length_percentage(maybe_value.release_nonnull());
+                auto offset = style_value_to_length_percentage(maybe_value.release_nonnull());
                 if (offset.has_value()) {
                     offset_provided = true;
                     tokens.next_token();
@@ -4384,11 +4388,11 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
             if (is_horizontal(identifier)) {
                 bool offset_provided = false;
                 auto offset = try_parse_offset(offset_provided);
-                horizontal = EdgeOffset { *to_edge(identifier), offset, true, offset_provided };
+                horizontal = EdgeOffset { *identifier_to_edge(identifier), offset, true, offset_provided };
             } else if (is_vertical(identifier)) {
                 bool offset_provided = false;
                 auto offset = try_parse_offset(offset_provided);
-                vertical = EdgeOffset { *to_edge(identifier), offset, true, offset_provided };
+                vertical = EdgeOffset { *identifier_to_edge(identifier), offset, true, offset_provided };
             } else if (identifier == ValueID::Center) {
                 found_center = true;
             } else {
@@ -4436,8 +4440,63 @@ RefPtr<StyleValue> Parser::parse_single_background_position_value(TokenStream<Co
 
     transaction.commit();
     return PositionStyleValue::create(
-        horizontal->edge, horizontal->offset,
-        vertical->edge, vertical->offset);
+        EdgeStyleValue::create(horizontal->edge, horizontal->offset),
+        EdgeStyleValue::create(vertical->edge, vertical->offset));
+}
+
+RefPtr<StyleValue> Parser::parse_single_background_position_x_or_y_value(TokenStream<ComponentValue>& tokens, PropertyID property)
+{
+    PositionEdge relative_edge {};
+    if (property == PropertyID::BackgroundPositionX) {
+        // [ center | [ [ left | right | x-start | x-end ]? <length-percentage>? ]! ]#
+        relative_edge = PositionEdge::Left;
+    } else if (property == PropertyID::BackgroundPositionY) {
+        // [ center | [ [ top | bottom | y-start | y-end ]? <length-percentage>? ]! ]#
+        relative_edge = PositionEdge::Top;
+    } else {
+        VERIFY_NOT_REACHED();
+    }
+
+    auto transaction = tokens.begin_transaction();
+    if (!tokens.has_next_token())
+        return {};
+
+    auto parse_value = [&](auto& token) -> RefPtr<StyleValue> {
+        auto maybe_value = parse_css_value(token);
+        if (!maybe_value || !property_accepts_value(property, *maybe_value))
+            return {};
+        return maybe_value.release_nonnull();
+    };
+
+    auto value = parse_value(tokens.next_token());
+    if (value->has_identifier()) {
+        auto identifier = value->to_identifier();
+        if (identifier == ValueID::Center) {
+            transaction.commit();
+            return EdgeStyleValue::create(relative_edge, Percentage { 50 });
+        }
+        if (auto edge = identifier_to_edge(identifier); edge.has_value()) {
+            relative_edge = *edge;
+        } else {
+            return {};
+        }
+        if (tokens.has_next_token()) {
+            value = parse_value(tokens.peek_token());
+            if (!value) {
+                transaction.commit();
+                return EdgeStyleValue::create(relative_edge, Length::make_px(0));
+            }
+            tokens.next_token();
+        }
+    }
+
+    auto offset = style_value_to_length_percentage(value);
+    if (offset.has_value()) {
+        transaction.commit();
+        return EdgeStyleValue::create(relative_edge, *offset);
+    }
+
+    return {};
 }
 
 RefPtr<StyleValue> Parser::parse_single_background_repeat_value(TokenStream<ComponentValue>& tokens)
@@ -6531,6 +6590,11 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue>> Parser::parse_css_value(Property
         if (auto parsed_value = parse_comma_separated_value_list(component_values, [this](auto& tokens) { return parse_single_background_position_value(tokens); }))
             return parsed_value.release_nonnull();
         return ParseError::SyntaxError;
+    case PropertyID::BackgroundPositionX:
+    case PropertyID::BackgroundPositionY:
+        if (auto parsed_value = parse_comma_separated_value_list(component_values, [this, property_id](auto& tokens) { return parse_single_background_position_x_or_y_value(tokens, property_id); }))
+            return parsed_value.release_nonnull();
+        return ParseError::SyntaxError;
     case PropertyID::BackgroundRepeat:
         if (auto parsed_value = parse_comma_separated_value_list(component_values, [this](auto& tokens) { return parse_single_background_repeat_value(tokens); }))
             return parsed_value.release_nonnull();

+ 1 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.h

@@ -294,6 +294,7 @@ private:
     RefPtr<StyleValue> parse_filter_value_list_value(Vector<ComponentValue> const&);
     RefPtr<StyleValue> parse_background_value(Vector<ComponentValue> const&);
     RefPtr<StyleValue> parse_single_background_position_value(TokenStream<ComponentValue>&);
+    RefPtr<StyleValue> parse_single_background_position_x_or_y_value(TokenStream<ComponentValue>&, PropertyID);
     RefPtr<StyleValue> parse_single_background_repeat_value(TokenStream<ComponentValue>&);
     RefPtr<StyleValue> parse_single_background_size_value(TokenStream<ComponentValue>&);
     RefPtr<StyleValue> parse_border_value(Vector<ComponentValue> const&);

+ 2 - 1
Userland/Libraries/LibWeb/CSS/ResolvedCSSStyleDeclaration.cpp

@@ -19,6 +19,7 @@
 #include <LibWeb/CSS/StyleValues/BorderStyleValue.h>
 #include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
 #include <LibWeb/CSS/StyleValues/ColorStyleValue.h>
+#include <LibWeb/CSS/StyleValues/EdgeStyleValue.h>
 #include <LibWeb/CSS/StyleValues/GridAreaShorthandStyleValue.h>
 #include <LibWeb/CSS/StyleValues/GridTrackPlacementShorthandStyleValue.h>
 #include <LibWeb/CSS/StyleValues/GridTrackPlacementStyleValue.h>
@@ -195,7 +196,7 @@ RefPtr<StyleValue const> ResolvedCSSStyleDeclaration::style_value_for_property(L
         return BackgroundStyleValue::create(
             value_or_default(maybe_background_color, InitialStyleValue::the()),
             value_or_default(maybe_background_image, IdentifierStyleValue::create(CSS::ValueID::None)),
-            value_or_default(maybe_background_position, PositionStyleValue::create(PositionEdge::Left, Length::make_px(0), PositionEdge::Top, Length::make_px(0))),
+            value_or_default(maybe_background_position, PositionStyleValue::create(EdgeStyleValue::create(PositionEdge::Left, Length::make_px(0)), EdgeStyleValue::create(PositionEdge::Top, Length::make_px(0)))),
             value_or_default(maybe_background_size, IdentifierStyleValue::create(CSS::ValueID::Auto)),
             value_or_default(maybe_background_repeat, BackgroundRepeatStyleValue::create(CSS::Repeat::Repeat, CSS::Repeat::Repeat)),
             value_or_default(maybe_background_attachment, IdentifierStyleValue::create(CSS::ValueID::Scroll)),

+ 14 - 0
Userland/Libraries/LibWeb/CSS/StyleComputer.cpp

@@ -43,6 +43,7 @@
 #include <LibWeb/CSS/StyleValues/NumericStyleValue.h>
 #include <LibWeb/CSS/StyleValues/OverflowStyleValue.h>
 #include <LibWeb/CSS/StyleValues/PercentageStyleValue.h>
+#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
 #include <LibWeb/CSS/StyleValues/StyleValueList.h>
 #include <LibWeb/CSS/StyleValues/TextDecorationStyleValue.h>
 #include <LibWeb/CSS/StyleValues/UnresolvedStyleValue.h>
@@ -454,6 +455,19 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
         return;
     }
 
+    if (property_id == CSS::PropertyID::BackgroundPosition) {
+        if (value.is_position()) {
+            auto const& position = value.as_position();
+            style.set_property(CSS::PropertyID::BackgroundPositionX, position.edge_x());
+            style.set_property(CSS::PropertyID::BackgroundPositionY, position.edge_y());
+            return;
+        }
+
+        style.set_property(CSS::PropertyID::BackgroundPositionX, value);
+        style.set_property(CSS::PropertyID::BackgroundPositionY, value);
+        return;
+    }
+
     if (property_id == CSS::PropertyID::Margin) {
         if (value.is_value_list()) {
             auto const& values_list = value.as_value_list();

+ 1 - 15
Userland/Libraries/LibWeb/CSS/StyleValues/PositionStyleValue.cpp

@@ -13,21 +13,7 @@ namespace Web::CSS {
 
 ErrorOr<String> PositionStyleValue::to_string() const
 {
-    auto to_string = [](PositionEdge edge) {
-        switch (edge) {
-        case PositionEdge::Left:
-            return "left";
-        case PositionEdge::Right:
-            return "right";
-        case PositionEdge::Top:
-            return "top";
-        case PositionEdge::Bottom:
-            return "bottom";
-        }
-        VERIFY_NOT_REACHED();
-    };
-
-    return String::formatted("{} {} {} {}", to_string(m_properties.edge_x), TRY(m_properties.offset_x.to_string()), to_string(m_properties.edge_y), TRY(m_properties.offset_y.to_string()));
+    return String::formatted("{} {}", TRY(m_properties.edge_x->to_string()), TRY(m_properties.edge_y->to_string()));
 }
 
 }

+ 8 - 12
Userland/Libraries/LibWeb/CSS/StyleValues/PositionStyleValue.h

@@ -17,33 +17,29 @@ namespace Web::CSS {
 
 class PositionStyleValue final : public StyleValueWithDefaultOperators<PositionStyleValue> {
 public:
-    static ValueComparingNonnullRefPtr<PositionStyleValue> create(PositionEdge edge_x, LengthPercentage const& offset_x, PositionEdge edge_y, LengthPercentage const& offset_y)
+    static ValueComparingNonnullRefPtr<PositionStyleValue> create(ValueComparingNonnullRefPtr<StyleValue> egde_x, ValueComparingNonnullRefPtr<StyleValue> edge_y)
     {
-        return adopt_ref(*new PositionStyleValue(edge_x, offset_x, edge_y, offset_y));
+        return adopt_ref(*new PositionStyleValue(move(egde_x), move(edge_y)));
     }
     virtual ~PositionStyleValue() override = default;
 
-    PositionEdge edge_x() const { return m_properties.edge_x; }
-    LengthPercentage const& offset_x() const { return m_properties.offset_x; }
-    PositionEdge edge_y() const { return m_properties.edge_y; }
-    LengthPercentage const& offset_y() const { return m_properties.offset_y; }
+    ValueComparingNonnullRefPtr<StyleValue> edge_x() const { return m_properties.edge_x; }
+    ValueComparingNonnullRefPtr<StyleValue> edge_y() const { return m_properties.edge_y; }
 
     virtual ErrorOr<String> to_string() const override;
 
     bool properties_equal(PositionStyleValue const& other) const { return m_properties == other.m_properties; }
 
 private:
-    PositionStyleValue(PositionEdge edge_x, LengthPercentage const& offset_x, PositionEdge edge_y, LengthPercentage const& offset_y)
+    PositionStyleValue(ValueComparingNonnullRefPtr<StyleValue> edge_x, ValueComparingNonnullRefPtr<StyleValue> edge_y)
         : StyleValueWithDefaultOperators(Type::Position)
-        , m_properties { .edge_x = edge_x, .offset_x = offset_x, .edge_y = edge_y, .offset_y = offset_y }
+        , m_properties { .edge_x = edge_x, .edge_y = edge_y }
     {
     }
 
     struct Properties {
-        PositionEdge edge_x;
-        LengthPercentage offset_x;
-        PositionEdge edge_y;
-        LengthPercentage offset_y;
+        ValueComparingNonnullRefPtr<StyleValue> edge_x;
+        ValueComparingNonnullRefPtr<StyleValue> edge_y;
         bool operator==(Properties const&) const = default;
     } m_properties;
 };

+ 15 - 9
Userland/Libraries/LibWeb/Layout/Node.cpp

@@ -9,7 +9,7 @@
 #include <LibWeb/CSS/StyleValues/BackgroundRepeatStyleValue.h>
 #include <LibWeb/CSS/StyleValues/BackgroundSizeStyleValue.h>
 #include <LibWeb/CSS/StyleValues/BorderRadiusStyleValue.h>
-#include <LibWeb/CSS/StyleValues/PositionStyleValue.h>
+#include <LibWeb/CSS/StyleValues/EdgeStyleValue.h>
 #include <LibWeb/CSS/StyleValues/StyleValueList.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/Dump.h>
@@ -293,7 +293,8 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
         auto clips = computed_style.property(CSS::PropertyID::BackgroundClip);
         auto images = computed_style.property(CSS::PropertyID::BackgroundImage);
         auto origins = computed_style.property(CSS::PropertyID::BackgroundOrigin);
-        auto positions = computed_style.property(CSS::PropertyID::BackgroundPosition);
+        auto x_positions = computed_style.property(CSS::PropertyID::BackgroundPositionX);
+        auto y_positions = computed_style.property(CSS::PropertyID::BackgroundPositionY);
         auto repeats = computed_style.property(CSS::PropertyID::BackgroundRepeat);
         auto sizes = computed_style.property(CSS::PropertyID::BackgroundSize);
 
@@ -315,7 +316,8 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
         layer_count = max(layer_count, count_layers(clips));
         layer_count = max(layer_count, count_layers(images));
         layer_count = max(layer_count, count_layers(origins));
-        layer_count = max(layer_count, count_layers(positions));
+        layer_count = max(layer_count, count_layers(x_positions));
+        layer_count = max(layer_count, count_layers(y_positions));
         layer_count = max(layer_count, count_layers(repeats));
         layer_count = max(layer_count, count_layers(sizes));
 
@@ -369,14 +371,18 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& computed_style)
                 layer.clip = as_box(clip_value->to_identifier());
             }
 
-            if (auto position_value = value_for_layer(positions, layer_index); position_value && position_value->is_position()) {
-                auto& position = position_value->as_position();
-                layer.position_edge_x = position.edge_x();
-                layer.position_edge_y = position.edge_y();
-                layer.position_offset_x = position.offset_x();
-                layer.position_offset_y = position.offset_y();
+            if (auto position_value = value_for_layer(x_positions, layer_index); position_value && position_value->is_edge()) {
+                auto& position = position_value->as_edge();
+                layer.position_edge_x = position.edge();
+                layer.position_offset_x = position.offset();
             }
 
+            if (auto position_value = value_for_layer(y_positions, layer_index); position_value && position_value->is_edge()) {
+                auto& position = position_value->as_edge();
+                layer.position_edge_y = position.edge();
+                layer.position_offset_y = position.offset();
+            };
+
             if (auto size_value = value_for_layer(sizes, layer_index); size_value) {
                 if (size_value->is_background_size()) {
                     auto& size = size_value->as_background_size();