Przeglądaj źródła

LibWeb: Implement and use BorderStyleValue

Sam Atkins 4 lat temu
rodzic
commit
cb3e097663

+ 87 - 1
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -1848,6 +1848,84 @@ RefPtr<StyleValue> Parser::parse_background_value(ParsingContext const& context,
     return BackgroundStyleValue::create(background_color.release_nonnull(), background_image.release_nonnull(), repeat_x.release_nonnull(), repeat_y.release_nonnull());
 }
 
+RefPtr<StyleValue> Parser::parse_border_value(ParsingContext const& context, PropertyID property_id, Vector<StyleComponentValueRule> const& component_values)
+{
+    auto is_line_style = [](StyleValue const& value) -> bool {
+        switch (value.to_identifier()) {
+        case ValueID::Dotted:
+        case ValueID::Dashed:
+        case ValueID::Solid:
+        case ValueID::Double:
+        case ValueID::Groove:
+        case ValueID::Ridge:
+        case ValueID::None:
+        case ValueID::Hidden:
+        case ValueID::Inset:
+        case ValueID::Outset:
+            return true;
+        default:
+            return false;
+        }
+    };
+
+    auto is_line_width = [](StyleValue const& value) -> bool {
+        if (value.is_length())
+            return true;
+
+        // FIXME: Implement thin/medium/thick
+        switch (value.to_identifier()) {
+        case ValueID::None:
+            return true;
+        default:
+            return false;
+        }
+    };
+
+    if (component_values.size() > 3)
+        return nullptr;
+
+    RefPtr<StyleValue> border_width;
+    RefPtr<StyleValue> border_color;
+    RefPtr<StyleValue> border_style;
+
+    for (auto& part : component_values) {
+        auto value = parse_css_value(context, property_id, part);
+        if (!value)
+            return nullptr;
+
+        if (is_line_width(*value)) {
+            if (border_width)
+                return nullptr;
+            border_width = value.release_nonnull();
+            continue;
+        }
+        if (value->is_color()) {
+            if (border_color)
+                return nullptr;
+            border_color = value.release_nonnull();
+            continue;
+        }
+        if (is_line_style(*value)) {
+            if (border_style)
+                return nullptr;
+            border_style = value.release_nonnull();
+            continue;
+        }
+
+        return nullptr;
+    }
+
+    if (!border_width)
+        border_width = IdentifierStyleValue::create(ValueID::Medium);
+    if (!border_style)
+        border_style = IdentifierStyleValue::create(ValueID::None);
+    // FIXME: Default should be `currentcolor` special value. https://www.w3.org/TR/css-color-4/#currentcolor-color
+    if (!border_color)
+        border_color = ColorStyleValue::create(Gfx::Color::Black);
+
+    return BorderStyleValue::create(border_width.release_nonnull(), border_style.release_nonnull(), border_color.release_nonnull());
+}
+
 RefPtr<StyleValue> Parser::parse_box_shadow_value(ParsingContext const& context, Vector<StyleComponentValueRule> const& component_values)
 {
     // FIXME: Also support inset, spread-radius and multiple comma-seperated box-shadows
@@ -2013,7 +2091,7 @@ RefPtr<StyleValue> Parser::parse_flex_flow_value(ParsingContext const& context,
     RefPtr<StyleValue> flex_wrap;
 
     for (auto& part : component_values) {
-        auto value = Parser::parse_css_value(context, PropertyID::FlexFlow, part);
+        auto value = parse_css_value(context, PropertyID::FlexFlow, part);
         if (!value)
             return nullptr;
         if (is_flex_direction(*value)) {
@@ -2430,6 +2508,14 @@ RefPtr<StyleValue> Parser::parse_css_value(PropertyID property_id, TokenStream<S
         if (auto parsed_value = parse_background_value(m_context, component_values))
             return parsed_value;
         break;
+    case PropertyID::Border:
+    case PropertyID::BorderBottom:
+    case PropertyID::BorderLeft:
+    case PropertyID::BorderRight:
+    case PropertyID::BorderTop:
+        if (auto parsed_value = parse_border_value(m_context, property_id, component_values))
+            return parsed_value;
+        break;
     case PropertyID::BoxShadow:
         if (auto parsed_box_shadow = parse_box_shadow_value(m_context, component_values))
             return parsed_box_shadow;

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

@@ -176,6 +176,7 @@ private:
     static RefPtr<StyleValue> parse_string_value(ParsingContext const&, StyleComponentValueRule const&);
     static RefPtr<StyleValue> parse_image_value(ParsingContext const&, StyleComponentValueRule const&);
     static RefPtr<StyleValue> parse_background_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
+    static RefPtr<StyleValue> parse_border_value(ParsingContext const&, PropertyID, Vector<StyleComponentValueRule> const&);
     static RefPtr<StyleValue> parse_box_shadow_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
     static RefPtr<StyleValue> parse_flex_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
     static RefPtr<StyleValue> parse_flex_flow_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);

+ 21 - 168
Userland/Libraries/LibWeb/CSS/StyleResolver.cpp

@@ -134,28 +134,6 @@ bool StyleResolver::is_inherited_property(CSS::PropertyID property_id)
     return inherited_properties.contains(property_id);
 }
 
-static Vector<String> split_on_whitespace(StringView const& string)
-{
-    if (string.is_empty())
-        return {};
-
-    Vector<String> v;
-    size_t substart = 0;
-    for (size_t i = 0; i < string.length(); ++i) {
-        char ch = string.characters_without_null_termination()[i];
-        if (isspace(ch)) {
-            size_t sublen = i - substart;
-            if (sublen != 0)
-                v.append(string.substring_view(substart, sublen));
-            substart = i + 1;
-        }
-    }
-    size_t taillen = string.length() - substart;
-    if (taillen != 0)
-        v.append(string.substring_view(substart, taillen));
-    return v;
-}
-
 enum class Edge {
     Top,
     Right,
@@ -169,56 +147,6 @@ static bool contains(Edge a, Edge b)
     return a == b || b == Edge::All;
 }
 
-static inline void set_property_border_width(StyleProperties& style, StyleValue const& value, Edge edge)
-{
-    VERIFY(value.is_length());
-    if (contains(Edge::Top, edge))
-        style.set_property(CSS::PropertyID::BorderTopWidth, value);
-    if (contains(Edge::Right, edge))
-        style.set_property(CSS::PropertyID::BorderRightWidth, value);
-    if (contains(Edge::Bottom, edge))
-        style.set_property(CSS::PropertyID::BorderBottomWidth, value);
-    if (contains(Edge::Left, edge))
-        style.set_property(CSS::PropertyID::BorderLeftWidth, value);
-}
-
-static inline void set_property_border_color(StyleProperties& style, StyleValue const& value, Edge edge)
-{
-    VERIFY(value.is_color());
-    if (contains(Edge::Top, edge))
-        style.set_property(CSS::PropertyID::BorderTopColor, value);
-    if (contains(Edge::Right, edge))
-        style.set_property(CSS::PropertyID::BorderRightColor, value);
-    if (contains(Edge::Bottom, edge))
-        style.set_property(CSS::PropertyID::BorderBottomColor, value);
-    if (contains(Edge::Left, edge))
-        style.set_property(CSS::PropertyID::BorderLeftColor, value);
-}
-
-static inline void set_property_border_style(StyleProperties& style, StyleValue const& value, Edge edge)
-{
-    VERIFY(value.type() == CSS::StyleValue::Type::Identifier);
-    if (contains(Edge::Top, edge))
-        style.set_property(CSS::PropertyID::BorderTopStyle, value);
-    if (contains(Edge::Right, edge))
-        style.set_property(CSS::PropertyID::BorderRightStyle, value);
-    if (contains(Edge::Bottom, edge))
-        style.set_property(CSS::PropertyID::BorderBottomStyle, value);
-    if (contains(Edge::Left, edge))
-        style.set_property(CSS::PropertyID::BorderLeftStyle, value);
-}
-
-static inline bool is_color(StyleValue const& value)
-{
-    if (value.is_builtin_or_dynamic())
-        return true;
-
-    if (value.is_color())
-        return true;
-
-    return false;
-}
-
 static inline bool is_font_family(StyleValue const& value)
 {
     if (value.is_builtin_or_dynamic())
@@ -241,45 +169,6 @@ static inline bool is_font_family(StyleValue const& value)
     }
 }
 
-static inline bool is_line_style(StyleValue const& value)
-{
-    if (value.is_builtin_or_dynamic())
-        return true;
-
-    switch (value.to_identifier()) {
-    case ValueID::Dotted:
-    case ValueID::Dashed:
-    case ValueID::Solid:
-    case ValueID::Double:
-    case ValueID::Groove:
-    case ValueID::Ridge:
-    case ValueID::None:
-    case ValueID::Hidden:
-    case ValueID::Inset:
-    case ValueID::Outset:
-        return true;
-    default:
-        return false;
-    }
-}
-
-static inline bool is_line_width(StyleValue const& value)
-{
-    if (value.is_builtin_or_dynamic())
-        return true;
-
-    if (value.is_length())
-        return true;
-
-    // FIXME: Implement thin/medium/thick
-    switch (value.to_identifier()) {
-    case ValueID::None:
-        return true;
-    default:
-        return false;
-    }
-}
-
 static void set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, StyleValue const& value, DOM::Document& document, bool is_internally_generated_pseudo_property = false)
 {
     CSS::ParsingContext context(document);
@@ -341,6 +230,7 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
         set_property_expanding_shorthands(style, CSS::PropertyID::BorderRight, value, document);
         set_property_expanding_shorthands(style, CSS::PropertyID::BorderBottom, value, document);
         set_property_expanding_shorthands(style, CSS::PropertyID::BorderLeft, value, document);
+        // FIXME: Also reset border-image, in line with the spec: https://www.w3.org/TR/css-backgrounds-3/#border-shorthands
         return;
     }
 
@@ -422,65 +312,28 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
             break;
         }
 
-        auto parts = split_on_whitespace(value.to_string());
-        if (value.is_length()) {
-            set_property_border_width(style, value, edge);
-            return;
-        }
-        if (value.is_color()) {
-            set_property_border_color(style, value, edge);
-            return;
-        }
-
-        if (value.is_component_value_list()) {
-            auto& parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
-
-            if (parts.size() == 1) {
-                auto value = Parser::parse_css_value(context, property_id, parts[0]);
-                if (value && is_line_style(*value)) {
-                    set_property_border_style(style, value.release_nonnull(), edge);
-                    set_property_border_color(style, ColorStyleValue::create(Gfx::Color::Black), edge);
-                    set_property_border_width(style, LengthStyleValue::create(Length(3, Length::Type::Px)), edge);
-                    return;
-                }
+        if (value.is_border()) {
+            auto& border = static_cast<BorderStyleValue const&>(value);
+            if (contains(Edge::Top, edge)) {
+                style.set_property(PropertyID::BorderTopWidth, border.border_width());
+                style.set_property(PropertyID::BorderTopStyle, border.border_style());
+                style.set_property(PropertyID::BorderTopColor, border.border_color());
             }
-
-            RefPtr<StyleValue> line_width_value;
-            RefPtr<StyleValue> color_value;
-            RefPtr<StyleValue> line_style_value;
-
-            for (auto& part : parts) {
-                auto value = Parser::parse_css_value(context, property_id, part);
-                if (!value)
-                    return;
-
-                if (is_line_width(*value)) {
-                    if (line_width_value)
-                        return;
-                    line_width_value = move(value);
-                    continue;
-                }
-                if (is_color(*value)) {
-                    if (color_value)
-                        return;
-                    color_value = move(value);
-                    continue;
-                }
-                if (is_line_style(*value)) {
-                    if (line_style_value)
-                        return;
-                    line_style_value = move(value);
-                    continue;
-                }
+            if (contains(Edge::Right, edge)) {
+                style.set_property(PropertyID::BorderRightWidth, border.border_width());
+                style.set_property(PropertyID::BorderRightStyle, border.border_style());
+                style.set_property(PropertyID::BorderRightColor, border.border_color());
+            }
+            if (contains(Edge::Bottom, edge)) {
+                style.set_property(PropertyID::BorderBottomWidth, border.border_width());
+                style.set_property(PropertyID::BorderBottomStyle, border.border_style());
+                style.set_property(PropertyID::BorderBottomColor, border.border_color());
+            }
+            if (contains(Edge::Left, edge)) {
+                style.set_property(PropertyID::BorderLeftWidth, border.border_width());
+                style.set_property(PropertyID::BorderLeftStyle, border.border_style());
+                style.set_property(PropertyID::BorderLeftColor, border.border_color());
             }
-
-            if (line_width_value)
-                set_property_border_width(style, line_width_value.release_nonnull(), edge);
-            if (color_value)
-                set_property_border_color(style, color_value.release_nonnull(), edge);
-            if (line_style_value)
-                set_property_border_style(style, line_style_value.release_nonnull(), edge);
-
             return;
         }
         return;

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

@@ -230,6 +230,7 @@ public:
         ComponentValueList,
         Calculated,
         Background,
+        Border,
         BoxShadow,
         Flex,
         FlexFlow,
@@ -253,6 +254,7 @@ public:
     bool is_component_value_list() const { return type() == Type::ComponentValueList; }
     bool is_calculated() const { return type() == Type::Calculated; }
     bool is_background() const { return type() == Type::Background; }
+    bool is_border() const { return type() == Type::Border; }
     bool is_box_shadow() const { return type() == Type::BoxShadow; }
     bool is_flex() const { return type() == Type::Flex; }
     bool is_flex_flow() const { return type() == Type::FlexFlow; }
@@ -681,6 +683,43 @@ private:
     // FIXME: background-origin
 };
 
+class BorderStyleValue final : public StyleValue {
+public:
+    static NonnullRefPtr<BorderStyleValue> create(
+        NonnullRefPtr<StyleValue> border_width,
+        NonnullRefPtr<StyleValue> border_style,
+        NonnullRefPtr<StyleValue> border_color)
+    {
+        return adopt_ref(*new BorderStyleValue(border_width, border_style, border_color));
+    }
+    virtual ~BorderStyleValue() override { }
+
+    NonnullRefPtr<StyleValue> border_width() const { return m_border_width; }
+    NonnullRefPtr<StyleValue> border_style() const { return m_border_style; }
+    NonnullRefPtr<StyleValue> border_color() const { return m_border_color; }
+
+    virtual String to_string() const override
+    {
+        return String::formatted("Border border_width: {}, border_style: {}, border_color: {}", m_border_width->to_string(), m_border_style->to_string(), m_border_color->to_string());
+    }
+
+private:
+    BorderStyleValue(
+        NonnullRefPtr<StyleValue> border_width,
+        NonnullRefPtr<StyleValue> border_style,
+        NonnullRefPtr<StyleValue> border_color)
+        : StyleValue(Type::Border)
+        , m_border_width(border_width)
+        , m_border_style(border_style)
+        , m_border_color(border_color)
+    {
+    }
+
+    NonnullRefPtr<StyleValue> m_border_width;
+    NonnullRefPtr<StyleValue> m_border_style;
+    NonnullRefPtr<StyleValue> m_border_color;
+};
+
 class FlexStyleValue final : public StyleValue {
 public:
     static NonnullRefPtr<FlexStyleValue> create(