Browse Source

LibWeb: Parse the `content` property

For now, we only understand `none`, `normal`, `<image>` and `<string>`.
The various other functions and identifiers can be added later.

We can *almost* use a StyleValueList for this, except it's divided into
two parts - the content, and the optional "alt text". So, I've added a
new StyleValue for it.
Sam Atkins 3 years ago
parent
commit
adaab23149

+ 64 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -3280,6 +3280,66 @@ RefPtr<StyleValue> Parser::parse_single_box_shadow_value(TokenStream<StyleCompon
     return BoxShadowStyleValue::create(color.release_value(), offset_x.release_value(), offset_y.release_value(), blur_radius.release_value(), spread_distance.release_value(), placement.release_value());
 }
 
+RefPtr<StyleValue> Parser::parse_content_value(Vector<StyleComponentValueRule> const& component_values)
+{
+    // FIXME: `content` accepts several kinds of function() type, which we don't handle in property_accepts_value() yet.
+
+    auto is_single_value_identifier = [](ValueID identifier) -> bool {
+        switch (identifier) {
+        case ValueID::None:
+        case ValueID::Normal:
+            return true;
+        default:
+            return false;
+        }
+    };
+
+    if (component_values.size() == 1) {
+        if (auto identifier = parse_identifier_value(component_values.first())) {
+            if (is_single_value_identifier(identifier->to_identifier()))
+                return identifier;
+        }
+    }
+
+    NonnullRefPtrVector<StyleValue> content_values;
+    NonnullRefPtrVector<StyleValue> alt_text_values;
+    bool in_alt_text = false;
+
+    for (auto const& value : component_values) {
+        if (value.is(Token::Type::Delim) && value.token().delim() == "/"sv) {
+            if (in_alt_text || content_values.is_empty())
+                return {};
+            in_alt_text = true;
+            continue;
+        }
+        auto style_value = parse_css_value(value);
+        if (style_value && property_accepts_value(PropertyID::Content, *style_value)) {
+            if (is_single_value_identifier(style_value->to_identifier()))
+                return {};
+
+            if (in_alt_text) {
+                alt_text_values.append(style_value.release_nonnull());
+            } else {
+                content_values.append(style_value.release_nonnull());
+            }
+            continue;
+        }
+
+        return {};
+    }
+
+    if (content_values.is_empty())
+        return {};
+    if (in_alt_text && alt_text_values.is_empty())
+        return {};
+
+    RefPtr<StyleValueList> alt_text;
+    if (!alt_text_values.is_empty())
+        alt_text = StyleValueList::create(move(alt_text_values), StyleValueList::Separator::Space);
+
+    return ContentStyleValue::create(StyleValueList::create(move(content_values), StyleValueList::Separator::Space), move(alt_text));
+}
+
 RefPtr<StyleValue> Parser::parse_flex_value(Vector<StyleComponentValueRule> const& component_values)
 {
     if (component_values.size() == 1) {
@@ -3864,6 +3924,10 @@ Result<NonnullRefPtr<StyleValue>, Parser::ParsingResult> Parser::parse_css_value
         if (auto parsed_value = parse_box_shadow_value(component_values))
             return parsed_value.release_nonnull();
         return ParsingResult::SyntaxError;
+    case PropertyID::Content:
+        if (auto parsed_value = parse_content_value(component_values))
+            return parsed_value.release_nonnull();
+        return ParsingResult::SyntaxError;
     case PropertyID::Flex:
         if (auto parsed_value = parse_flex_value(component_values))
             return parsed_value.release_nonnull();

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

@@ -274,6 +274,7 @@ private:
     RefPtr<StyleValue> parse_border_radius_shorthand_value(Vector<StyleComponentValueRule> const&);
     RefPtr<StyleValue> parse_box_shadow_value(Vector<StyleComponentValueRule> const&);
     RefPtr<StyleValue> parse_single_box_shadow_value(TokenStream<StyleComponentValueRule>&);
+    RefPtr<StyleValue> parse_content_value(Vector<StyleComponentValueRule> const&);
     RefPtr<StyleValue> parse_flex_value(Vector<StyleComponentValueRule> const&);
     RefPtr<StyleValue> parse_flex_flow_value(Vector<StyleComponentValueRule> const&);
     RefPtr<StyleValue> parse_font_value(Vector<StyleComponentValueRule> const&);

+ 12 - 0
Userland/Libraries/LibWeb/CSS/Properties.json

@@ -510,6 +510,18 @@
       "hashless-hex-color"
     ]
   },
+  "content": {
+    "inherited": false,
+    "initial": "normal",
+    "__comment": "FIXME: This accepts a whole lot of other types and identifiers!",
+    "valid-types": [
+      "string"
+    ],
+    "valid-identifiers": [
+      "normal",
+      "none"
+    ]
+  },
   "cursor": {
     "inherited": true,
     "initial": "auto",

+ 13 - 0
Userland/Libraries/LibWeb/CSS/StyleValue.cpp

@@ -81,6 +81,12 @@ ColorStyleValue const& StyleValue::as_color() const
     return static_cast<ColorStyleValue const&>(*this);
 }
 
+ContentStyleValue const& StyleValue::as_content() const
+{
+    VERIFY(is_content());
+    return static_cast<ContentStyleValue const&>(*this);
+}
+
 FlexStyleValue const& StyleValue::as_flex() const
 {
     VERIFY(is_flex());
@@ -1015,6 +1021,13 @@ String CombinedBorderRadiusStyleValue::to_string() const
     return String::formatted("{} {} {} {} / {} {} {} {}", m_top_left->horizontal_radius().to_string(), m_top_right->horizontal_radius().to_string(), m_bottom_right->horizontal_radius().to_string(), m_bottom_left->horizontal_radius().to_string(), m_top_left->vertical_radius().to_string(), m_top_right->vertical_radius().to_string(), m_bottom_right->vertical_radius().to_string(), m_bottom_left->vertical_radius().to_string());
 }
 
+String ContentStyleValue::to_string() const
+{
+    if (has_alt_text())
+        return String::formatted("{} / {}", m_content->to_string(), m_alt_text->to_string());
+    return m_content->to_string();
+}
+
 String FlexStyleValue::to_string() const
 {
     return String::formatted("{} {} {}", m_grow->to_string(), m_shrink->to_string(), m_basis->to_string());

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

@@ -297,6 +297,7 @@ public:
         Calculated,
         Color,
         CombinedBorderRadius,
+        Content,
         Flex,
         FlexFlow,
         Font,
@@ -333,6 +334,7 @@ public:
     bool is_box_shadow() const { return type() == Type::BoxShadow; }
     bool is_calculated() const { return type() == Type::Calculated; }
     bool is_color() const { return type() == Type::Color; }
+    bool is_content() const { return type() == Type::Content; }
     bool is_flex() const { return type() == Type::Flex; }
     bool is_flex_flow() const { return type() == Type::FlexFlow; }
     bool is_font() const { return type() == Type::Font; }
@@ -367,6 +369,7 @@ public:
     BoxShadowStyleValue const& as_box_shadow() const;
     CalculatedStyleValue const& as_calculated() const;
     ColorStyleValue const& as_color() const;
+    ContentStyleValue const& as_content() const;
     FlexFlowStyleValue const& as_flex_flow() const;
     FlexStyleValue const& as_flex() const;
     FontStyleValue const& as_font() const;
@@ -399,6 +402,7 @@ public:
     BoxShadowStyleValue& as_box_shadow() { return const_cast<BoxShadowStyleValue&>(const_cast<StyleValue const&>(*this).as_box_shadow()); }
     CalculatedStyleValue& as_calculated() { return const_cast<CalculatedStyleValue&>(const_cast<StyleValue const&>(*this).as_calculated()); }
     ColorStyleValue& as_color() { return const_cast<ColorStyleValue&>(const_cast<StyleValue const&>(*this).as_color()); }
+    ContentStyleValue& as_content() { return const_cast<ContentStyleValue&>(const_cast<StyleValue const&>(*this).as_content()); }
     FlexFlowStyleValue& as_flex_flow() { return const_cast<FlexFlowStyleValue&>(const_cast<StyleValue const&>(*this).as_flex_flow()); }
     FlexStyleValue& as_flex() { return const_cast<FlexStyleValue&>(const_cast<StyleValue const&>(*this).as_flex()); }
     FontStyleValue& as_font() { return const_cast<FontStyleValue&>(const_cast<StyleValue const&>(*this).as_font()); }
@@ -969,6 +973,31 @@ private:
     NonnullRefPtr<BorderRadiusStyleValue> m_bottom_left;
 };
 
+class ContentStyleValue final : public StyleValue {
+public:
+    static NonnullRefPtr<ContentStyleValue> create(NonnullRefPtr<StyleValueList> content, RefPtr<StyleValueList> alt_text)
+    {
+        return adopt_ref(*new ContentStyleValue(move(content), move(alt_text)));
+    }
+
+    StyleValueList const& content() const { return *m_content; }
+    bool has_alt_text() const { return !m_alt_text.is_null(); }
+    StyleValueList const* alt_text() const { return m_alt_text; }
+
+    virtual String to_string() const override;
+
+private:
+    ContentStyleValue(NonnullRefPtr<StyleValueList> content, RefPtr<StyleValueList> alt_text)
+        : StyleValue(Type::Content)
+        , m_content(move(content))
+        , m_alt_text(move(alt_text))
+    {
+    }
+
+    NonnullRefPtr<StyleValueList> m_content;
+    RefPtr<StyleValueList> m_alt_text;
+};
+
 class FlexStyleValue final : public StyleValue {
 public:
     static NonnullRefPtr<FlexStyleValue> create(

+ 1 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -30,6 +30,7 @@ class BorderStyleValue;
 class BoxShadowStyleValue;
 class CalculatedStyleValue;
 class ColorStyleValue;
+class ContentStyleValue;
 class CSSImportRule;
 class CSSMediaRule;
 class CSSRule;