From 2644d2c22113c801dc2df85b189c4159da51f5e9 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Wed, 4 Aug 2021 17:48:08 +0100 Subject: [PATCH] LibWeb: Implement and use FlexStyleValue This is not just moving the code from StyleResolver to Parser. The logic has changed to allow for the `flex-basis` to come before or after the `flex-grow/shrink` values, as well as handle the special one-value cases. Also added test cases to flex.html to check the parsing. It does parse correctly, but elements with `flex-basis: auto` do not calculate their width correctly. --- Base/res/html/misc/flex.html | 62 ++++++++++++ .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 96 +++++++++++++++++++ Userland/Libraries/LibWeb/CSS/Parser/Parser.h | 1 + .../Libraries/LibWeb/CSS/StyleResolver.cpp | 87 ++--------------- Userland/Libraries/LibWeb/CSS/StyleValue.h | 39 ++++++++ 5 files changed, 208 insertions(+), 77 deletions(-) diff --git a/Base/res/html/misc/flex.html b/Base/res/html/misc/flex.html index 33b380f43f..a487eb1e16 100644 --- a/Base/res/html/misc/flex.html +++ b/Base/res/html/misc/flex.html @@ -154,6 +154,68 @@
2 I shrink
3 I don't
+ +

Flex shorthand

+

flex: initial;

+
+
1
+
2
+
3
+
+

flex: auto;

+
+
1
+
2
+
3
+
+

flex: none;

+
+
1
+
2
+
3
+
+

flex: 1/2/3;

+
+
1
+
2
+
3
+
+

flex: 0 0 0;

+
+
1
+
2
+
3
+
+

flex: 1 2 0;

+
+
1
+
2
+
3
+
+

flex: 0 1 2; (Invalid)

+
+
1
+
2
+
3
+
+

flex: 4/1/0 0 50px;

+
+
1
+
2
+
3
+
+

flex: 80% 0 4/1/0;

+
+
1
+
2
+
3
+
+

flex: auto 0 4/1/0;

+
+
1
+
2
+
3
+
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 06a3135178..3e7c092c09 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -1889,6 +1889,98 @@ RefPtr Parser::parse_box_shadow_value(ParsingContext const& context, return BoxShadowStyleValue::create(offset_x, offset_y, blur_radius, color); } +RefPtr Parser::parse_flex_value(ParsingContext const& context, Vector const& component_values) +{ + auto is_flex_grow_or_shrink = [](StyleValue const& value) -> bool { + if (value.is_numeric()) + return true; + return false; + }; + + auto is_flex_basis = [](StyleValue const& value) -> bool { + if (value.is_length()) + return true; + switch (value.to_identifier()) { + case ValueID::Auto: + case ValueID::Content: + return true; + default: + return false; + } + }; + + if (component_values.size() == 1) { + auto value = parse_css_value(context, PropertyID::Flex, component_values[0]); + if (!value) + return nullptr; + + switch (value->to_identifier()) { + case ValueID::Auto: { + auto one = NumericStyleValue::create(1); + return FlexStyleValue::create(one, one, IdentifierStyleValue::create(ValueID::Auto)); + } + case ValueID::None: { + auto zero = NumericStyleValue::create(0); + return FlexStyleValue::create(zero, zero, IdentifierStyleValue::create(ValueID::Auto)); + } + default: + break; + } + } + + RefPtr flex_grow; + RefPtr flex_shrink; + RefPtr flex_basis; + + for (size_t i = 0; i < component_values.size(); ++i) { + auto value = parse_css_value(context, PropertyID::Flex, component_values[i]); + if (!value) + return nullptr; + + // Zero is a valid value for basis, but only if grow and shrink are already specified. + if (value->is_numeric() && static_cast(*value).value() == 0) { + if (flex_grow && flex_shrink && !flex_basis) { + flex_basis = LengthStyleValue::create(Length(0, Length::Type::Px)); + continue; + } + } + + if (is_flex_grow_or_shrink(*value)) { + if (flex_grow) + return nullptr; + flex_grow = value.release_nonnull(); + + // Flex-shrink may optionally follow directly after. + if (i + 1 < component_values.size()) { + auto second_value = parse_css_value(context, PropertyID::Flex, component_values[i + 1]); + if (second_value && is_flex_grow_or_shrink(*second_value)) { + flex_shrink = second_value.release_nonnull(); + i++; + } + } + continue; + } + + if (is_flex_basis(*value)) { + if (flex_basis) + return nullptr; + flex_basis = value.release_nonnull(); + continue; + } + + return nullptr; + } + + if (!flex_grow) + flex_grow = NumericStyleValue::create(0); + if (!flex_shrink) + flex_shrink = NumericStyleValue::create(1); + if (!flex_basis) + flex_basis = IdentifierStyleValue::create(ValueID::Auto); + + return FlexStyleValue::create(flex_grow.release_nonnull(), flex_shrink.release_nonnull(), flex_basis.release_nonnull()); +} + RefPtr Parser::parse_font_value(ParsingContext const& context, Vector const& component_values) { auto is_font_size = [](StyleValue const& value) -> bool { @@ -2285,6 +2377,10 @@ RefPtr Parser::parse_css_value(PropertyID property_id, TokenStream parse_image_value(ParsingContext const&, StyleComponentValueRule const&); static RefPtr parse_background_value(ParsingContext const&, Vector const&); static RefPtr parse_box_shadow_value(ParsingContext const&, Vector const&); + static RefPtr parse_flex_value(ParsingContext const&, Vector const&); static RefPtr parse_font_value(ParsingContext const&, Vector const&); static RefPtr parse_list_style_value(ParsingContext const&, Vector const&); static RefPtr parse_text_decoration_value(ParsingContext const&, Vector const&); diff --git a/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp b/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp index 387b0308e9..32720bd4a9 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleResolver.cpp @@ -250,31 +250,6 @@ static inline bool is_flex_wrap(StyleValue const& value) } } -static inline bool is_flex_grow_or_shrink(StyleValue const& value) -{ - if (value.is_builtin_or_dynamic()) - return true; - - if (value.is_numeric()) - return true; - - return false; -} - -static inline bool is_flex_basis(StyleValue const& value) -{ - if (value.is_builtin_or_dynamic()) - return true; - - if (value.is_length()) - return true; - - if (value.is_identifier() && value.to_identifier() == ValueID::Content) - return true; - - return false; -} - static inline bool is_font_family(StyleValue const& value) { if (value.is_builtin_or_dynamic()) @@ -801,61 +776,19 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope } if (property_id == CSS::PropertyID::Flex) { - if (value.is_length() || (value.is_identifier() && value.to_identifier() == CSS::ValueID::Content)) { + if (value.is_flex()) { + auto& flex = static_cast(value); + style.set_property(CSS::PropertyID::FlexGrow, flex.grow()); + style.set_property(CSS::PropertyID::FlexShrink, flex.shrink()); + style.set_property(CSS::PropertyID::FlexBasis, flex.basis()); + return; + } + if (value.is_builtin()) { + style.set_property(CSS::PropertyID::FlexGrow, value); + style.set_property(CSS::PropertyID::FlexShrink, value); style.set_property(CSS::PropertyID::FlexBasis, value); return; } - - if (value.is_component_value_list()) { - auto parts = static_cast(value).values(); - if (parts.size() == 1) { - auto value = Parser::parse_css_value(context, property_id, parts[0]); - if (!value) - return; - if (is_flex_basis(*value)) { - style.set_property(CSS::PropertyID::FlexBasis, *value); - } else if (is_flex_grow_or_shrink(*value)) { - style.set_property(CSS::PropertyID::FlexGrow, *value); - } - return; - } - - if (parts.size() == 2) { - auto flex_grow = Parser::parse_css_value(context, property_id, parts[0]); - auto second_value = Parser::parse_css_value(context, property_id, parts[1]); - if (!flex_grow || !second_value) - return; - - style.set_property(CSS::PropertyID::FlexGrow, *flex_grow); - - if (is_flex_basis(*second_value)) { - style.set_property(CSS::PropertyID::FlexBasis, *second_value); - } else if (is_flex_grow_or_shrink(*second_value)) { - style.set_property(CSS::PropertyID::FlexShrink, *second_value); - } - return; - } - - if (parts.size() == 3) { - auto flex_grow = Parser::parse_css_value(context, property_id, parts[0]); - auto flex_shrink = Parser::parse_css_value(context, property_id, parts[1]); - auto flex_basis = Parser::parse_css_value(context, property_id, parts[2]); - if (!flex_grow || !flex_shrink || !flex_basis) - return; - - style.set_property(CSS::PropertyID::FlexGrow, *flex_grow); - style.set_property(CSS::PropertyID::FlexShrink, *flex_shrink); - - if (is_flex_basis(*flex_basis)) - style.set_property(CSS::PropertyID::FlexBasis, *flex_basis); - - return; - } - - return; - } - - dbgln("Unsure what to do with CSS flex value '{}'", value.to_string()); return; } diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index bcbde3f97f..1699a36d58 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -231,6 +231,7 @@ public: Calculated, Background, BoxShadow, + Flex, Font, ListStyle, TextDecoration, @@ -252,6 +253,7 @@ public: bool is_calculated() const { return type() == Type::Calculated; } bool is_background() const { return type() == Type::Background; } bool is_box_shadow() const { return type() == Type::BoxShadow; } + bool is_flex() const { return type() == Type::Flex; } bool is_font() const { return type() == Type::Font; } bool is_list_style() const { return type() == Type::ListStyle; } bool is_text_decoration() const { return type() == Type::TextDecoration; } @@ -677,6 +679,43 @@ private: // FIXME: background-origin }; +class FlexStyleValue final : public StyleValue { +public: + static NonnullRefPtr create( + NonnullRefPtr grow, + NonnullRefPtr shrink, + NonnullRefPtr basis) + { + return adopt_ref(*new FlexStyleValue(grow, shrink, basis)); + } + virtual ~FlexStyleValue() override { } + + NonnullRefPtr grow() const { return m_grow; } + NonnullRefPtr shrink() const { return m_shrink; } + NonnullRefPtr basis() const { return m_basis; } + + virtual String to_string() const override + { + return String::formatted("Flex grow: {}, shrink: {}, basis: {}", m_grow->to_string(), m_shrink->to_string(), m_basis->to_string()); + } + +private: + FlexStyleValue( + NonnullRefPtr grow, + NonnullRefPtr shrink, + NonnullRefPtr basis) + : StyleValue(Type::Flex) + , m_grow(grow) + , m_shrink(shrink) + , m_basis(basis) + { + } + + NonnullRefPtr m_grow; + NonnullRefPtr m_shrink; + NonnullRefPtr m_basis; +}; + class FontStyleValue final : public StyleValue { public: static NonnullRefPtr create(NonnullRefPtr font_style, NonnullRefPtr font_weight, NonnullRefPtr font_size, NonnullRefPtr line_height, NonnullRefPtrVector&& font_families) { return adopt_ref(*new FontStyleValue(font_style, font_weight, font_size, line_height, move(font_families))); }