Jelajahi Sumber

LibWeb: Support two-value background-repeat

The background-repeat value may be specified as either one- or two-value
identifiers (to be interpreted as horizontal and vertical repeat). This
adds two pseudo-properties, background-repeat-x and background-repeat-y,
to handle this. One-value identifiers are mapped to two-value in
accordance with the spec.
Timothy Flynn 4 tahun lalu
induk
melakukan
5de0e0068c

+ 6 - 3
Userland/Libraries/LibWeb/CSS/ComputedValues.h

@@ -92,7 +92,8 @@ public:
 
     Color color() const { return m_inherited.color; }
     Color background_color() const { return m_noninherited.background_color; }
-    CSS::Repeat background_repeat() const { return m_noninherited.background_repeat; }
+    CSS::Repeat background_repeat_x() const { return m_noninherited.background_repeat_x; }
+    CSS::Repeat background_repeat_y() const { return m_noninherited.background_repeat_y; }
 
     CSS::ListStyleType list_style_type() const { return m_inherited.list_style_type; }
 
@@ -134,7 +135,8 @@ protected:
         BorderData border_right;
         BorderData border_bottom;
         Color background_color { InitialValues::background_color() };
-        CSS::Repeat background_repeat { InitialValues::background_repeat() };
+        CSS::Repeat background_repeat_x { InitialValues::background_repeat() };
+        CSS::Repeat background_repeat_y { InitialValues::background_repeat() };
         CSS::FlexDirection flex_direction { InitialValues::flex_direction() };
         CSS::Overflow overflow_x { InitialValues::overflow() };
         CSS::Overflow overflow_y { InitialValues::overflow() };
@@ -149,7 +151,8 @@ public:
     void set_color(const Color& color) { m_inherited.color = color; }
     void set_cursor(CSS::Cursor cursor) { m_inherited.cursor = cursor; }
     void set_background_color(const Color& color) { m_noninherited.background_color = color; }
-    void set_background_repeat(CSS::Repeat repeat) { m_noninherited.background_repeat = repeat; }
+    void set_background_repeat_x(CSS::Repeat repeat) { m_noninherited.background_repeat_x = repeat; }
+    void set_background_repeat_y(CSS::Repeat repeat) { m_noninherited.background_repeat_y = repeat; }
     void set_float(CSS::Float value) { m_noninherited.float_ = value; }
     void set_clear(CSS::Clear value) { m_noninherited.clear = value; }
     void set_z_index(Optional<int> value) { m_noninherited.z_index = value; }

+ 13 - 1
Userland/Libraries/LibWeb/CSS/Properties.json

@@ -18,8 +18,20 @@
     "initial": "0% 0%"
   },
   "background-repeat": {
+    "longhands": [
+      "background-repeat-x",
+      "background-repeat-y"
+    ]
+  },
+  "background-repeat-x": {
+    "inherited": false,
+    "initial": "repeat",
+    "pseudo": true
+  },
+  "background-repeat-y": {
     "inherited": false,
-    "initial": "repeat"
+    "initial": "repeat",
+    "pseudo": true
   },
   "border": {
     "longhands": [

+ 22 - 6
Userland/Libraries/LibWeb/CSS/StyleProperties.cpp

@@ -612,9 +612,29 @@ Optional<CSS::Overflow> StyleProperties::overflow(CSS::PropertyID property_id) c
     }
 }
 
-Optional<CSS::Repeat> StyleProperties::background_repeat() const
+Optional<CSS::Repeat> StyleProperties::background_repeat_x() const
 {
-    auto value = property(CSS::PropertyID::BackgroundRepeat);
+    auto value = property(CSS::PropertyID::BackgroundRepeatX);
+    if (!value.has_value())
+        return {};
+
+    switch (value.value()->to_identifier()) {
+    case CSS::ValueID::NoRepeat:
+        return CSS::Repeat::NoRepeat;
+    case CSS::ValueID::Repeat:
+        return CSS::Repeat::Repeat;
+    case CSS::ValueID::Round:
+        return CSS::Repeat::Round;
+    case CSS::ValueID::Space:
+        return CSS::Repeat::Space;
+    default:
+        return {};
+    }
+}
+
+Optional<CSS::Repeat> StyleProperties::background_repeat_y() const
+{
+    auto value = property(CSS::PropertyID::BackgroundRepeatY);
     if (!value.has_value())
         return {};
 
@@ -623,10 +643,6 @@ Optional<CSS::Repeat> StyleProperties::background_repeat() const
         return CSS::Repeat::NoRepeat;
     case CSS::ValueID::Repeat:
         return CSS::Repeat::Repeat;
-    case CSS::ValueID::RepeatX:
-        return CSS::Repeat::RepeatX;
-    case CSS::ValueID::RepeatY:
-        return CSS::Repeat::RepeatY;
     case CSS::ValueID::Round:
         return CSS::Repeat::Round;
     case CSS::ValueID::Space:

+ 2 - 1
Userland/Libraries/LibWeb/CSS/StyleProperties.h

@@ -73,7 +73,8 @@ public:
     Optional<CSS::FlexDirection> flex_direction() const;
     Optional<CSS::Overflow> overflow_x() const;
     Optional<CSS::Overflow> overflow_y() const;
-    Optional<CSS::Repeat> background_repeat() const;
+    Optional<CSS::Repeat> background_repeat_x() const;
+    Optional<CSS::Repeat> background_repeat_y() const;
 
     const Gfx::Font& font() const
     {

+ 66 - 5
Userland/Libraries/LibWeb/CSS/StyleResolver.cpp

@@ -229,6 +229,24 @@ static inline void set_property_border_style(StyleProperties& style, const Style
         style.set_property(CSS::PropertyID::BorderLeftStyle, value);
 }
 
+static inline bool is_background_repeat_property(const StyleValue& value)
+{
+    if (!value.is_identifier())
+        return false;
+
+    switch (value.to_identifier()) {
+    case CSS::ValueID::NoRepeat:
+    case CSS::ValueID::Repeat:
+    case CSS::ValueID::RepeatX:
+    case CSS::ValueID::RepeatY:
+    case CSS::ValueID::Round:
+    case CSS::ValueID::Space:
+        return true;
+    default:
+        return false;
+    }
+}
+
 static void set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, const StyleValue& value, DOM::Document& document, bool is_internally_generated_pseudo_property = false)
 {
     CSS::ParsingContext context(document);
@@ -433,12 +451,22 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
         if (values[0].is_color() && color_value_count == 1)
             style.set_property(CSS::PropertyID::BackgroundColor, values[0]);
 
-        for (auto& value : values) {
-            if (value.is_identifier())
-                set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeat, value, document);
+        for (auto it = values.begin(); it != values.end(); ++it) {
+            auto& value = *it;
+
+            if (is_background_repeat_property(value)) {
+                if ((it + 1 != values.end()) && is_background_repeat_property(*(it + 1))) {
+                    ++it;
+
+                    set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatX, value, document, true);
+                    set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatY, *it, document, true);
+                } else {
+                    set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeat, value, document);
+                }
+            }
+
             if (!value.is_string())
                 continue;
-            auto string = value.to_string();
             set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundImage, value, document);
         }
         return;
@@ -464,7 +492,40 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
     }
 
     if (property_id == CSS::PropertyID::BackgroundRepeat) {
-        style.set_property(CSS::PropertyID::BackgroundRepeat, value);
+        auto parts = split_on_whitespace(value.to_string());
+        NonnullRefPtrVector<StyleValue> values;
+        for (auto& part : parts) {
+            auto value = parse_css_value(context, part);
+            if (!value || !is_background_repeat_property(*value))
+                return;
+            values.append(value.release_nonnull());
+        }
+
+        if (values.size() == 1) {
+            auto value_id = values[0].to_identifier();
+            if (value_id == CSS::ValueID::RepeatX || value_id == CSS::ValueID::RepeatY) {
+                auto repeat_x = IdentifierStyleValue::create(value_id == CSS::ValueID::RepeatX ? CSS::ValueID::Repeat : CSS::ValueID::NoRepeat);
+                auto repeat_y = IdentifierStyleValue::create(value_id == CSS::ValueID::RepeatX ? CSS::ValueID::NoRepeat : CSS::ValueID::Repeat);
+                set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatX, repeat_x, document, true);
+                set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatY, repeat_y, document, true);
+            } else {
+                set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatX, values[0], document, true);
+                set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatY, values[0], document, true);
+            }
+        } else if (values.size() == 2) {
+            set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatX, values[0], document, true);
+            set_property_expanding_shorthands(style, CSS::PropertyID::BackgroundRepeatY, values[1], document, true);
+        }
+
+        return;
+    }
+
+    if (property_id == CSS::PropertyID::BackgroundRepeatX || property_id == CSS::PropertyID::BackgroundRepeatY) {
+        auto value_id = value.to_identifier();
+        if (value_id == CSS::ValueID::RepeatX || value_id == CSS::ValueID::RepeatY)
+            return;
+
+        style.set_property(property_id, value);
         return;
     }
 

+ 0 - 2
Userland/Libraries/LibWeb/CSS/StyleValue.h

@@ -192,8 +192,6 @@ enum class Overflow : u8 {
 enum class Repeat : u8 {
     NoRepeat,
     Repeat,
-    RepeatX,
-    RepeatY,
     Round,
     Space,
 };

+ 15 - 2
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -362,7 +362,7 @@ RefPtr<Gfx::Bitmap> Document::background_image() const
     return background_image->bitmap();
 }
 
-CSS::Repeat Document::background_repeat() const
+CSS::Repeat Document::background_repeat_x() const
 {
     auto* body_element = body();
     if (!body_element)
@@ -372,7 +372,20 @@ CSS::Repeat Document::background_repeat() const
     if (!body_layout_node)
         return CSS::Repeat::Repeat;
 
-    return body_layout_node->computed_values().background_repeat();
+    return body_layout_node->computed_values().background_repeat_x();
+}
+
+CSS::Repeat Document::background_repeat_y() const
+{
+    auto* body_element = body();
+    if (!body_element)
+        return CSS::Repeat::Repeat;
+
+    auto* body_layout_node = body_element->layout_node();
+    if (!body_layout_node)
+        return CSS::Repeat::Repeat;
+
+    return body_layout_node->computed_values().background_repeat_y();
 }
 
 URL Document::complete_url(const String& string) const

+ 2 - 1
Userland/Libraries/LibWeb/DOM/Document.h

@@ -129,7 +129,8 @@ public:
 
     Color background_color(const Gfx::Palette&) const;
     RefPtr<Gfx::Bitmap> background_image() const;
-    CSS::Repeat background_repeat() const;
+    CSS::Repeat background_repeat_x() const;
+    CSS::Repeat background_repeat_y() const;
 
     Color link_color() const;
     void set_link_color(Color);

+ 17 - 9
Userland/Libraries/LibWeb/Layout/Box.cpp

@@ -50,7 +50,7 @@ void Box::paint(PaintContext& context, PaintPhase phase)
         context.painter().fill_rect(background_rect, computed_values().background_color());
 
         if (background_image() && background_image()->bitmap()) {
-            paint_background_image(context, *background_image()->bitmap(), computed_values().background_repeat(), move(background_rect));
+            paint_background_image(context, *background_image()->bitmap(), computed_values().background_repeat_x(), computed_values().background_repeat_y(), move(background_rect));
         }
     }
 
@@ -87,22 +87,30 @@ void Box::paint(PaintContext& context, PaintPhase phase)
 void Box::paint_background_image(
     PaintContext& context,
     const Gfx::Bitmap& background_image,
-    CSS::Repeat background_repeat,
+    CSS::Repeat background_repeat_x,
+    CSS::Repeat background_repeat_y,
     Gfx::IntRect background_rect)
 {
-    switch (background_repeat) {
+    switch (background_repeat_x) {
+    case CSS::Repeat::Round:
+    case CSS::Repeat::Space:
+        // FIXME: Support 'round' and 'space'. Fall through to 'repeat' since that most closely resembles these.
     case CSS::Repeat::Repeat:
         // The background rect is already sized to align with 'repeat'.
         break;
-    case CSS::Repeat::RepeatX:
-        background_rect.set_height(background_image.height());
-        break;
-    case CSS::Repeat::RepeatY:
+    case CSS::Repeat::NoRepeat:
         background_rect.set_width(background_image.width());
         break;
+    }
+
+    switch (background_repeat_y) {
+    case CSS::Repeat::Round:
+    case CSS::Repeat::Space:
+        // FIXME: Support 'round' and 'space'. Fall through to 'repeat' since that most closely resembles these.
+    case CSS::Repeat::Repeat:
+        // The background rect is already sized to align with 'repeat'.
+        break;
     case CSS::Repeat::NoRepeat:
-    default: // FIXME: Support 'round' and 'square'
-        background_rect.set_width(background_image.width());
         background_rect.set_height(background_image.height());
         break;
     }

+ 1 - 1
Userland/Libraries/LibWeb/Layout/Box.h

@@ -153,7 +153,7 @@ protected:
 
     virtual void did_set_rect() { }
 
-    void paint_background_image(PaintContext&, const Gfx::Bitmap&, CSS::Repeat, Gfx::IntRect);
+    void paint_background_image(PaintContext&, const Gfx::Bitmap&, CSS::Repeat, CSS::Repeat, Gfx::IntRect);
 
     Vector<LineBox> m_line_boxes;
 

+ 1 - 1
Userland/Libraries/LibWeb/Layout/InitialContainingBlockBox.cpp

@@ -69,7 +69,7 @@ void InitialContainingBlockBox::paint_document_background(PaintContext& context)
 
     if (auto background_bitmap = document().background_image()) {
         Gfx::IntRect background_rect = { 0, 0, context.viewport_rect().x() + context.viewport_rect().width(), context.viewport_rect().y() + context.viewport_rect().height() };
-        paint_background_image(context, *background_bitmap, document().background_repeat(), move(background_rect));
+        paint_background_image(context, *background_bitmap, document().background_repeat_x(), document().background_repeat_y(), move(background_rect));
     }
 }
 

+ 7 - 3
Userland/Libraries/LibWeb/Layout/Node.cpp

@@ -234,9 +234,13 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& specified_style)
         m_background_image = static_ptr_cast<CSS::ImageStyleValue>(bgimage.value());
     }
 
-    auto background_repeat = specified_style.background_repeat();
-    if (background_repeat.has_value())
-        computed_values.set_background_repeat(background_repeat.value());
+    auto background_repeat_x = specified_style.background_repeat_x();
+    if (background_repeat_x.has_value())
+        computed_values.set_background_repeat_x(background_repeat_x.value());
+
+    auto background_repeat_y = specified_style.background_repeat_y();
+    if (background_repeat_y.has_value())
+        computed_values.set_background_repeat_y(background_repeat_y.value());
 
     computed_values.set_display(specified_style.display());