diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 69b216a74a..a55787c6d0 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -3280,6 +3280,66 @@ RefPtr Parser::parse_single_box_shadow_value(TokenStream Parser::parse_content_value(Vector 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 content_values; + NonnullRefPtrVector 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 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 Parser::parse_flex_value(Vector const& component_values) { if (component_values.size() == 1) { @@ -3864,6 +3924,10 @@ Result, 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(); diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index febe81110b..a1ae0c5871 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -274,6 +274,7 @@ private: RefPtr parse_border_radius_shorthand_value(Vector const&); RefPtr parse_box_shadow_value(Vector const&); RefPtr parse_single_box_shadow_value(TokenStream&); + RefPtr parse_content_value(Vector const&); RefPtr parse_flex_value(Vector const&); RefPtr parse_flex_flow_value(Vector const&); RefPtr parse_font_value(Vector const&); diff --git a/Userland/Libraries/LibWeb/CSS/Properties.json b/Userland/Libraries/LibWeb/CSS/Properties.json index 76b2980fee..4d72137c9c 100644 --- a/Userland/Libraries/LibWeb/CSS/Properties.json +++ b/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", diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp index 61574be54c..0aab0d5ab4 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -81,6 +81,12 @@ ColorStyleValue const& StyleValue::as_color() const return static_cast(*this); } +ContentStyleValue const& StyleValue::as_content() const +{ + VERIFY(is_content()); + return static_cast(*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()); diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index b98cf3ff19..ad09542f20 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/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(const_cast(*this).as_box_shadow()); } CalculatedStyleValue& as_calculated() { return const_cast(const_cast(*this).as_calculated()); } ColorStyleValue& as_color() { return const_cast(const_cast(*this).as_color()); } + ContentStyleValue& as_content() { return const_cast(const_cast(*this).as_content()); } FlexFlowStyleValue& as_flex_flow() { return const_cast(const_cast(*this).as_flex_flow()); } FlexStyleValue& as_flex() { return const_cast(const_cast(*this).as_flex()); } FontStyleValue& as_font() { return const_cast(const_cast(*this).as_font()); } @@ -969,6 +973,31 @@ private: NonnullRefPtr m_bottom_left; }; +class ContentStyleValue final : public StyleValue { +public: + static NonnullRefPtr create(NonnullRefPtr content, RefPtr 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 content, RefPtr alt_text) + : StyleValue(Type::Content) + , m_content(move(content)) + , m_alt_text(move(alt_text)) + { + } + + NonnullRefPtr m_content; + RefPtr m_alt_text; +}; + class FlexStyleValue final : public StyleValue { public: static NonnullRefPtr create( diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index fbdadad579..e26f5f06da 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/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;