From 0e15561df08a5fe0d080cbd3a2dcb1b0c750a24b Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 3 Aug 2021 15:56:51 +0100 Subject: LibWeb: Implement and use ListStyleStyleValue Yes, the name is silly, but it's a StyleValue for list-style, so... yeah. :^) Since `list-style-type` and `list-style-image` can both have `none` as a value, and can appear in any order, we have to handle it separately, and then assign either or both of those to `none` depending on how many `none`s there are, and whether those sub-properties already have values. Added some extra test cases to lists.html to cover list-style-image and list-style-position parts of the list-style shorthand, and the `none` values. --- Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp | 114 ++++++++++++++++++++++++ Userland/Libraries/LibWeb/CSS/Parser/Parser.h | 1 + Userland/Libraries/LibWeb/CSS/StyleResolver.cpp | 106 ++-------------------- Userland/Libraries/LibWeb/CSS/StyleValue.h | 39 ++++++++ 4 files changed, 162 insertions(+), 98 deletions(-) (limited to 'Userland/Libraries/LibWeb/CSS') diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index b222f02453..d933b8bcc5 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -2063,6 +2063,116 @@ RefPtr Parser::parse_font_value(ParsingContext const& context, Vecto return FontStyleValue::create(font_style.release_nonnull(), font_weight.release_nonnull(), font_size.release_nonnull(), line_height.release_nonnull(), move(font_families)); } +RefPtr Parser::parse_list_style_value(ParsingContext const& context, Vector const& component_values) +{ + auto is_list_style_image = [](StyleValue const& value) -> bool { + if (value.is_image()) + return true; + if (value.is_identifier() && value.to_identifier() == ValueID::None) + return true; + + return false; + }; + + auto is_list_style_position = [](StyleValue const& value) -> bool { + switch (value.to_identifier()) { + case ValueID::Inside: + case ValueID::Outside: + return true; + default: + return false; + } + }; + + auto is_list_style_type = [](StyleValue const& value) -> bool { + // FIXME: Handle strings and symbols("...") syntax + switch (value.to_identifier()) { + case CSS::ValueID::None: + case CSS::ValueID::Disc: + case CSS::ValueID::Circle: + case CSS::ValueID::Square: + case CSS::ValueID::Decimal: + case CSS::ValueID::DecimalLeadingZero: + case CSS::ValueID::LowerAlpha: + case CSS::ValueID::LowerLatin: + case CSS::ValueID::UpperAlpha: + case CSS::ValueID::UpperLatin: + case CSS::ValueID::UpperRoman: + case CSS::ValueID::LowerRoman: + return true; + default: + return false; + } + }; + + if (component_values.size() > 3) + return nullptr; + + RefPtr list_position; + RefPtr list_image; + RefPtr list_type; + int found_nones = 0; + + for (auto& part : component_values) { + auto value = parse_css_value(context, PropertyID::ListStyle, part); + if (!value) + return nullptr; + + if (value->to_identifier() == ValueID::None) { + found_nones++; + continue; + } + + if (is_list_style_position(*value)) { + if (list_position) + return nullptr; + list_position = value.release_nonnull(); + continue; + } + if (is_list_style_image(*value)) { + if (list_image) + return nullptr; + list_image = value.release_nonnull(); + continue; + } + if (is_list_style_type(*value)) { + if (list_type) + return nullptr; + list_type = value.release_nonnull(); + continue; + } + } + + if (found_nones > 2) + return nullptr; + + if (found_nones == 2) { + if (list_image || list_type) + return nullptr; + auto none = IdentifierStyleValue::create(ValueID::None); + list_image = none; + list_type = none; + + } else if (found_nones == 1) { + if (list_image && list_type) + return nullptr; + auto none = IdentifierStyleValue::create(ValueID::None); + if (!list_image) + list_image = none; + if (!list_type) + list_type = none; + } + + if (!list_position) + list_position = IdentifierStyleValue::create(ValueID::Outside); + if (!list_image) + list_image = IdentifierStyleValue::create(ValueID::None); + if (!list_type) + list_type = IdentifierStyleValue::create(ValueID::Disc); + + return ListStyleStyleValue::create(list_position.release_nonnull(), list_image.release_nonnull(), list_type.release_nonnull()); +} + RefPtr Parser::parse_as_css_value(PropertyID property_id) { auto component_values = parse_as_list_of_component_values(); @@ -2105,6 +2215,10 @@ RefPtr Parser::parse_css_value(PropertyID property_id, TokenStream parse_background_value(ParsingContext const&, Vector const&); static RefPtr parse_box_shadow_value(ParsingContext const&, Vector const&); static RefPtr parse_font_value(ParsingContext const&, Vector const&); + static RefPtr parse_list_style_value(ParsingContext const&, Vector const&); // calc() parsing, according to https://www.w3.org/TR/css-values-3/#calc-syntax static OwnPtr parse_calc_sum(ParsingContext const&, TokenStream&); diff --git a/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp b/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp index 89ae825ddb..0e759ade69 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp @@ -336,56 +336,6 @@ static inline bool is_line_width(StyleValue const& value) } } -static inline bool is_list_style_image(StyleValue const& value) -{ - if (value.is_builtin_or_dynamic()) - return true; - if (value.is_image()) - return true; - if (value.is_identifier() && value.to_identifier() == ValueID::None) - return true; - - return false; -} - -static inline bool is_list_style_position(StyleValue const& value) -{ - if (value.is_builtin_or_dynamic()) - return true; - - switch (value.to_identifier()) { - case ValueID::Inside: - case ValueID::Outside: - return true; - default: - return false; - } -} - -static inline bool is_list_style_type(StyleValue const& value) -{ - if (value.is_builtin_or_dynamic()) - return true; - // FIXME: Handle strings and symbols("...") syntax - switch (value.to_identifier()) { - case CSS::ValueID::None: - case CSS::ValueID::Disc: - case CSS::ValueID::Circle: - case CSS::ValueID::Square: - case CSS::ValueID::Decimal: - case CSS::ValueID::DecimalLeadingZero: - case CSS::ValueID::LowerAlpha: - case CSS::ValueID::LowerLatin: - case CSS::ValueID::UpperAlpha: - case CSS::ValueID::UpperLatin: - case CSS::ValueID::UpperRoman: - case CSS::ValueID::LowerRoman: - return true; - default: - return true; - } -} - static inline bool is_text_decoration_line(StyleValue const& value) { if (value.is_builtin_or_dynamic()) @@ -868,59 +818,19 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope } if (property_id == CSS::PropertyID::ListStyle) { - if (value.is_component_value_list()) { - auto parts = static_cast(value).values(); - - if (!parts.is_empty() && parts.size() <= 3) { - RefPtr position_value; - RefPtr image_value; - RefPtr type_value; - - // FIXME: `none` is ambiguous as it is a valid value for ListStyleImage and ListStyleType, - // so requires special handling. https://www.w3.org/TR/css-lists-3/#propdef-list-style - - for (auto& part : parts) { - auto value = Parser::parse_css_value(context, property_id, part); - if (!value) - return; - - if (is_list_style_position(*value)) { - if (position_value) - return; - position_value = move(value); - continue; - } - if (is_list_style_image(*value)) { - if (image_value) - return; - image_value = move(value); - continue; - } - if (is_list_style_type(*value)) { - if (type_value) - return; - type_value = move(value); - continue; - } - } - - if (position_value) - style.set_property(CSS::PropertyID::ListStylePosition, *position_value); - if (image_value) - style.set_property(CSS::PropertyID::ListStyleImage, *image_value); - if (type_value) - style.set_property(CSS::PropertyID::ListStyleType, *type_value); - } + if (value.is_list_style()) { + auto& list_style = static_cast(value); + style.set_property(CSS::PropertyID::ListStylePosition, list_style.position()); + style.set_property(CSS::PropertyID::ListStyleImage, list_style.image()); + style.set_property(CSS::PropertyID::ListStyleType, list_style.style_type()); return; } - - if (is_list_style_position(value)) + if (value.is_builtin()) { style.set_property(CSS::PropertyID::ListStylePosition, value); - else if (is_list_style_image(value)) style.set_property(CSS::PropertyID::ListStyleImage, value); - else if (is_list_style_type(value)) style.set_property(CSS::PropertyID::ListStyleType, value); - + return; + } return; } diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index fa0d859b86..4a19bf8a6b 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -232,6 +232,7 @@ public: Background, BoxShadow, Font, + ListStyle, }; Type type() const { return m_type; } @@ -251,6 +252,7 @@ public: bool is_background() const { return type() == Type::Background; } bool is_box_shadow() const { return type() == Type::BoxShadow; } bool is_font() const { return type() == Type::Font; } + bool is_list_style() const { return type() == Type::ListStyle; } bool is_builtin() const { return is_inherit() || is_initial(); } @@ -717,6 +719,43 @@ private: // FIXME: Implement font-stretch and font-variant. }; +class ListStyleStyleValue final : public StyleValue { +public: + static NonnullRefPtr create( + NonnullRefPtr position, + NonnullRefPtr image, + NonnullRefPtr style_type) + { + return adopt_ref(*new ListStyleStyleValue(position, image, style_type)); + } + virtual ~ListStyleStyleValue() override { } + + NonnullRefPtr position() const { return m_position; } + NonnullRefPtr image() const { return m_image; } + NonnullRefPtr style_type() const { return m_style_type; } + + virtual String to_string() const override + { + return String::formatted("ListStyle position: {}, image: {}, style_type: {}", m_position->to_string(), m_image->to_string(), m_style_type->to_string()); + } + +private: + ListStyleStyleValue( + NonnullRefPtr position, + NonnullRefPtr image, + NonnullRefPtr style_type) + : StyleValue(Type::ListStyle) + , m_position(position) + , m_image(image) + , m_style_type(style_type) + { + } + + NonnullRefPtr m_position; + NonnullRefPtr m_image; + NonnullRefPtr m_style_type; +}; + class StyleValueList final : public StyleValue { public: static NonnullRefPtr create(NonnullRefPtrVector&& values) { return adopt_ref(*new StyleValueList(move(values))); } -- cgit v1.2.3