diff options
author | Sam Atkins <atkinssj@serenityos.org> | 2022-01-14 17:09:02 +0000 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-01-20 00:04:10 +0100 |
commit | ea0f6b42f002d55716108e38b660dc5d732fbec9 (patch) | |
tree | 4416f462a70e123afa1f347d4a835abf01b15797 | |
parent | 0bb5bda23eaa1b39e2d416e9f27bbf286279b603 (diff) | |
download | serenity-ea0f6b42f002d55716108e38b660dc5d732fbec9.zip |
LibWeb: Add PercentageStyleValue, and parse it
This is in a slightly weird state, where Percentages are sometimes
Lengths and sometimes not, which I will be cleaning up in subsequent
commits, in an attempt not to change all of LibWeb in one go. :^)
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp | 186 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/Parser/Parser.h | 26 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/StyleValue.cpp | 6 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/CSS/StyleValue.h | 35 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Forward.h | 1 |
5 files changed, 187 insertions, 67 deletions
diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index bff0e7ea12..813d17c314 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -2110,94 +2110,114 @@ RefPtr<StyleValue> Parser::parse_dynamic_value(StyleComponentValueRule const& co return {}; } -Optional<Length> Parser::parse_length(StyleComponentValueRule const& component_value) +Optional<Parser::Dimension> Parser::parse_dimension(StyleComponentValueRule const& component_value) { - Length::Type type = Length::Type::Undefined; - Optional<float> numeric_value; - if (component_value.is(Token::Type::Dimension)) { - numeric_value = component_value.token().dimension_value(); + float numeric_value = component_value.token().dimension_value(); auto unit_string = component_value.token().dimension_unit(); - - if (unit_string.equals_ignoring_case("px")) { - type = Length::Type::Px; - } else if (unit_string.equals_ignoring_case("pt")) { - type = Length::Type::Pt; - } else if (unit_string.equals_ignoring_case("pc")) { - type = Length::Type::Pc; - } else if (unit_string.equals_ignoring_case("mm")) { - type = Length::Type::Mm; - } else if (unit_string.equals_ignoring_case("rem")) { - type = Length::Type::Rem; - } else if (unit_string.equals_ignoring_case("em")) { - type = Length::Type::Em; - } else if (unit_string.equals_ignoring_case("ex")) { - type = Length::Type::Ex; - } else if (unit_string.equals_ignoring_case("ch")) { - type = Length::Type::Ch; - } else if (unit_string.equals_ignoring_case("vw")) { - type = Length::Type::Vw; - } else if (unit_string.equals_ignoring_case("vh")) { - type = Length::Type::Vh; - } else if (unit_string.equals_ignoring_case("vmax")) { - type = Length::Type::Vmax; - } else if (unit_string.equals_ignoring_case("vmin")) { - type = Length::Type::Vmin; - } else if (unit_string.equals_ignoring_case("cm")) { - type = Length::Type::Cm; - } else if (unit_string.equals_ignoring_case("in")) { - type = Length::Type::In; - } else if (unit_string.equals_ignoring_case("Q")) { - type = Length::Type::Q; - } else if (unit_string.equals_ignoring_case("%")) { + Optional<Length::Type> length_type = Length::Type::Undefined; + + if (unit_string.equals_ignoring_case("px"sv)) { + length_type = Length::Type::Px; + } else if (unit_string.equals_ignoring_case("pt"sv)) { + length_type = Length::Type::Pt; + } else if (unit_string.equals_ignoring_case("pc"sv)) { + length_type = Length::Type::Pc; + } else if (unit_string.equals_ignoring_case("mm"sv)) { + length_type = Length::Type::Mm; + } else if (unit_string.equals_ignoring_case("rem"sv)) { + length_type = Length::Type::Rem; + } else if (unit_string.equals_ignoring_case("em"sv)) { + length_type = Length::Type::Em; + } else if (unit_string.equals_ignoring_case("ex"sv)) { + length_type = Length::Type::Ex; + } else if (unit_string.equals_ignoring_case("ch"sv)) { + length_type = Length::Type::Ch; + } else if (unit_string.equals_ignoring_case("vw"sv)) { + length_type = Length::Type::Vw; + } else if (unit_string.equals_ignoring_case("vh"sv)) { + length_type = Length::Type::Vh; + } else if (unit_string.equals_ignoring_case("vmax"sv)) { + length_type = Length::Type::Vmax; + } else if (unit_string.equals_ignoring_case("vmin"sv)) { + length_type = Length::Type::Vmin; + } else if (unit_string.equals_ignoring_case("cm"sv)) { + length_type = Length::Type::Cm; + } else if (unit_string.equals_ignoring_case("in"sv)) { + length_type = Length::Type::In; + } else if (unit_string.equals_ignoring_case("Q"sv)) { + length_type = Length::Type::Q; + } else if (unit_string.equals_ignoring_case("%"sv)) { // A number followed by `%` must always result in a Percentage token. VERIFY_NOT_REACHED(); - } else { - return {}; } - } else if (component_value.is(Token::Type::Percentage)) { - type = Length::Type::Percentage; - numeric_value = component_value.token().percentage(); - } else if (component_value.is(Token::Type::Ident) && component_value.token().ident().equals_ignoring_case("auto")) { - return Length::make_auto(); - } else if (component_value.is(Token::Type::Number)) { - numeric_value = component_value.token().number_value(); - if (numeric_value == 0) { - type = Length::Type::Px; - } else if (m_context.in_quirks_mode() && property_has_quirk(m_context.current_property_id(), Quirk::UnitlessLength)) { + + if (length_type.has_value()) + return Length { numeric_value, length_type.value() }; + } + + if (component_value.is(Token::Type::Percentage)) + return Percentage { static_cast<float>(component_value.token().percentage()) }; + + if (component_value.is(Token::Type::Number)) { + float numeric_value = component_value.token().number_value(); + if (numeric_value == 0) + return Length::make_px(0); + if (m_context.in_quirks_mode() && property_has_quirk(m_context.current_property_id(), Quirk::UnitlessLength)) { // https://quirks.spec.whatwg.org/#quirky-length-value // FIXME: Disallow quirk when inside a CSS sub-expression (like `calc()`) // "The <quirky-length> value must not be supported in arguments to CSS expressions other than the rect() // expression, and must not be supported in the supports() static method of the CSS interface." - type = Length::Type::Px; + return Length::make_px(numeric_value); } } - if (!numeric_value.has_value()) + return {}; +} + +Optional<Length> Parser::parse_length(StyleComponentValueRule const& component_value) +{ + auto dimension = parse_dimension(component_value); + if (!dimension.has_value()) return {}; - return Length(numeric_value.value(), type); + if (dimension->is_length()) + return dimension->length(); + + // FIXME: This is a temporary hack until Length is split up fully. + if (dimension->is_percentage()) { + auto percentage = dimension->percentage(); + return Length { (float)percentage.value(), Length::Type::Percentage }; + } + + if (component_value.is(Token::Type::Ident) && component_value.token().ident().equals_ignoring_case("auto")) + return Length::make_auto(); + + return {}; } -RefPtr<StyleValue> Parser::parse_length_value(StyleComponentValueRule const& component_value) +RefPtr<StyleValue> Parser::parse_dimension_value(StyleComponentValueRule const& component_value) { // Numbers with no units can be lengths, in two situations: // 1) We're in quirks mode, and it's an integer. // 2) It's a 0. // We handle case 1 here. Case 2 is handled by NumericStyleValue pretending to be a LengthStyleValue if it is 0. - // FIXME: "auto" is also treated as a Length, and most of the time that is how it is used, but not always. - // Possibly it should always be an Identifier, and then quietly converted to a Length when needed, like 0 above. - // Right now, it instead is quietly converted to an Identifier when needed. - if (component_value.is(Token::Type::Dimension) || component_value.is(Token::Type::Percentage) - || (component_value.is(Token::Type::Ident) && component_value.token().ident().equals_ignoring_case("auto"sv)) - || (m_context.in_quirks_mode() && property_has_quirk(m_context.current_property_id(), Quirk::UnitlessLength) && component_value.is(Token::Type::Number) && component_value.token().number_value() != 0)) { - auto length = parse_length(component_value); - if (length.has_value()) - return LengthStyleValue::create(length.value()); - } + if (component_value.is(Token::Type::Number) && !(m_context.in_quirks_mode() && property_has_quirk(m_context.current_property_id(), Quirk::UnitlessLength))) + return {}; - return {}; + if (component_value.is(Token::Type::Ident) && component_value.token().ident().equals_ignoring_case("auto")) + return LengthStyleValue::create(Length::make_auto()); + + auto dimension = parse_dimension(component_value); + if (!dimension.has_value()) + return {}; + + if (dimension->is_length()) + return LengthStyleValue::create(dimension->length()); + if (dimension->is_percentage()) + return PercentageStyleValue::create(dimension->percentage()); + VERIFY_NOT_REACHED(); } RefPtr<StyleValue> Parser::parse_numeric_value(StyleComponentValueRule const& component_value) @@ -3793,8 +3813,8 @@ RefPtr<StyleValue> Parser::parse_css_value(StyleComponentValueRule const& compon if (auto color = parse_color_value(component_value)) return color; - if (auto length = parse_length_value(component_value)) - return length; + if (auto dimension = parse_dimension_value(component_value)) + return dimension; if (auto numeric = parse_numeric_value(component_value)) return numeric; @@ -4390,6 +4410,40 @@ RefPtr<StyleValue> Parser::parse_css_value(Badge<StyleComputer>, ParsingContext return result.release_value(); } +bool Parser::Dimension::is_length() const +{ + return m_value.has<Length>(); +} + +Length Parser::Dimension::length() const +{ + return m_value.get<Length>(); +} + +bool Parser::Dimension::is_percentage() const +{ + return m_value.has<Percentage>(); +} + +Percentage Parser::Dimension::percentage() const +{ + return m_value.get<Percentage>(); +} + +bool Parser::Dimension::is_length_percentage() const +{ + return is_length() || is_percentage(); +} + +LengthPercentage Parser::Dimension::length_percentage() const +{ + if (is_length()) + return length(); + if (is_percentage()) + return percentage(); + VERIFY_NOT_REACHED(); +} + } namespace Web { diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index 592d977776..38713894ed 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -176,6 +176,30 @@ private: [[nodiscard]] RefPtr<PropertyOwningCSSStyleDeclaration> convert_to_declaration(NonnullRefPtr<StyleBlockRule>); [[nodiscard]] Optional<StyleProperty> convert_to_style_property(StyleDeclarationRule const&); + class Dimension { + public: + Dimension(Length&& value) + : m_value(move(value)) + { + } + Dimension(Percentage&& value) + : m_value(move(value)) + { + } + + bool is_length() const; + Length length() const; + + bool is_percentage() const; + Percentage percentage() const; + + bool is_length_percentage() const; + LengthPercentage length_percentage() const; + + private: + Variant<Length, Percentage> m_value; + }; + Optional<Dimension> parse_dimension(StyleComponentValueRule const&); Optional<Color> parse_color(StyleComponentValueRule const&); Optional<Length> parse_length(StyleComponentValueRule const&); @@ -189,7 +213,7 @@ private: RefPtr<StyleValue> parse_css_value(StyleComponentValueRule const&); RefPtr<StyleValue> parse_builtin_value(StyleComponentValueRule const&); RefPtr<StyleValue> parse_dynamic_value(StyleComponentValueRule const&); - RefPtr<StyleValue> parse_length_value(StyleComponentValueRule const&); + RefPtr<StyleValue> parse_dimension_value(StyleComponentValueRule const&); RefPtr<StyleValue> parse_numeric_value(StyleComponentValueRule const&); RefPtr<StyleValue> parse_identifier_value(StyleComponentValueRule const&); RefPtr<StyleValue> parse_color_value(StyleComponentValueRule const&); diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp index bffce543c3..57fee4632d 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -139,6 +139,12 @@ OverflowStyleValue const& StyleValue::as_overflow() const return static_cast<OverflowStyleValue const&>(*this); } +PercentageStyleValue const& StyleValue::as_percentage() const +{ + VERIFY(is_percentage()); + return static_cast<PercentageStyleValue const&>(*this); +} + PositionStyleValue const& StyleValue::as_position() const { VERIFY(is_position()); diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index 0e9598aa88..0e195b84e3 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -25,6 +25,7 @@ #include <LibWeb/CSS/Display.h> #include <LibWeb/CSS/Length.h> #include <LibWeb/CSS/Parser/StyleComponentValueRule.h> +#include <LibWeb/CSS/Percentage.h> #include <LibWeb/CSS/PropertyID.h> #include <LibWeb/CSS/ValueID.h> #include <LibWeb/Forward.h> @@ -284,6 +285,7 @@ public: ListStyle, Numeric, Overflow, + Percentage, Position, String, TextDecoration, @@ -314,6 +316,7 @@ public: bool is_list_style() const { return type() == Type::ListStyle; } bool is_numeric() const { return type() == Type::Numeric; } bool is_overflow() const { return type() == Type::Overflow; } + bool is_percentage() const { return type() == Type::Percentage; } bool is_position() const { return type() == Type::Position; } bool is_string() const { return type() == Type::String; } bool is_text_decoration() const { return type() == Type::TextDecoration; } @@ -343,6 +346,7 @@ public: ListStyleStyleValue const& as_list_style() const; NumericStyleValue const& as_numeric() const; OverflowStyleValue const& as_overflow() const; + PercentageStyleValue const& as_percentage() const; PositionStyleValue const& as_position() const; StringStyleValue const& as_string() const; TextDecorationStyleValue const& as_text_decoration() const; @@ -370,6 +374,7 @@ public: ListStyleStyleValue& as_list_style() { return const_cast<ListStyleStyleValue&>(const_cast<StyleValue const&>(*this).as_list_style()); } NumericStyleValue& as_numeric() { return const_cast<NumericStyleValue&>(const_cast<StyleValue const&>(*this).as_numeric()); } OverflowStyleValue& as_overflow() { return const_cast<OverflowStyleValue&>(const_cast<StyleValue const&>(*this).as_overflow()); } + PercentageStyleValue& as_percentage() { return const_cast<PercentageStyleValue&>(const_cast<StyleValue const&>(*this).as_percentage()); } PositionStyleValue& as_position() { return const_cast<PositionStyleValue&>(const_cast<StyleValue const&>(*this).as_position()); } StringStyleValue& as_string() { return const_cast<StringStyleValue&>(const_cast<StyleValue const&>(*this).as_string()); } TextDecorationStyleValue& as_text_decoration() { return const_cast<TextDecorationStyleValue&>(const_cast<StyleValue const&>(*this).as_text_decoration()); } @@ -1173,6 +1178,36 @@ private: NonnullRefPtr<StyleValue> m_overflow_y; }; +class PercentageStyleValue final : public StyleValue { +public: + static NonnullRefPtr<PercentageStyleValue> create(Percentage&& percentage) + { + return adopt_ref(*new PercentageStyleValue(move(percentage))); + } + virtual ~PercentageStyleValue() override { } + + Percentage const& percentage() const { return m_percentage; } + Percentage& percentage() { return m_percentage; } + + // FIXME: This is a temporary hack until we fully separate Length and Percentage. + bool has_length() const override { return true; } + Length to_length() const override { return { m_percentage.value(), Length::Type::Percentage }; } + + virtual String to_string() const override + { + return m_percentage.to_string(); + } + +private: + PercentageStyleValue(Percentage&& percentage) + : StyleValue(Type::Percentage) + , m_percentage(percentage) + { + } + + Percentage m_percentage; +}; + class PositionStyleValue final : public StyleValue { public: static NonnullRefPtr<PositionStyleValue> create(PositionEdge edge_x, Length const& offset_x, PositionEdge edge_y, Length const& offset_y) diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 0454931ec4..48f72e22e6 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -55,6 +55,7 @@ class MediaQueryListEvent; class NumericStyleValue; class OverflowStyleValue; class Percentage; +class PercentageStyleValue; class PositionStyleValue; class PropertyOwningCSSStyleDeclaration; class Screen; |