From fe372cd0733c464920ccc0edce61933d81d54d30 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Mon, 21 Mar 2022 21:01:27 +0000 Subject: [PATCH] LibWeb: Use CSS::Number for Token numeric values --- .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 36 +++++++------- .../Libraries/LibWeb/CSS/Parser/Token.cpp | 20 ++++---- Userland/Libraries/LibWeb/CSS/Parser/Token.h | 47 +++++-------------- .../Libraries/LibWeb/CSS/Parser/Tokenizer.cpp | 26 +++++----- .../Libraries/LibWeb/CSS/Parser/Tokenizer.h | 9 +--- 5 files changed, 53 insertions(+), 85 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index 59e1e57bd7..455fae2d51 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -1233,14 +1233,14 @@ Optional Parser::parse_media_feature_value(MediaFeatureID med // Boolean ( in the spec: a 1 or 0) if (media_feature_accepts_type(media_feature, MediaFeatureValueType::Boolean)) { - if (first.is(Token::Type::Number) && first.token().number_type() == Token::NumberType::Integer + if (first.is(Token::Type::Number) && first.token().number().is_integer() && (first.token().number_value() == 0 || first.token().number_value() == 1)) return MediaFeatureValue(first.token().number_value()); } // Integer if (media_feature_accepts_type(media_feature, MediaFeatureValueType::Integer)) { - if (first.is(Token::Type::Number) && first.token().number_type() == Token::NumberType::Integer) + if (first.is(Token::Type::Number) && first.token().number().is_integer()) return MediaFeatureValue(first.token().number_value()); } @@ -2392,7 +2392,7 @@ RefPtr Parser::parse_numeric_value(StyleComponentValueRule const& co { if (component_value.is(Token::Type::Number)) { auto number = component_value.token(); - if (number.number_type() == Token::NumberType::Integer) { + if (number.number().is_integer()) { return NumericStyleValue::create_integer(number.to_integer()); } else { return NumericStyleValue::create_float(number.number_value()); @@ -2462,9 +2462,9 @@ Optional Parser::parse_color(StyleComponentValueRule const& component_val auto g_val = params[1]; auto b_val = params[2]; - if (r_val.is(Token::NumberType::Integer) - && g_val.is(Token::NumberType::Integer) - && b_val.is(Token::NumberType::Integer)) { + if (r_val.is(Token::Type::Number) && r_val.number().is_integer() + && g_val.is(Token::Type::Number) && g_val.number().is_integer() + && b_val.is(Token::Type::Number) && b_val.number().is_integer()) { auto r = r_val.to_integer(); auto g = g_val.to_integer(); @@ -2490,9 +2490,9 @@ Optional Parser::parse_color(StyleComponentValueRule const& component_val auto b_val = params[2]; auto a_val = params[3]; - if (r_val.is(Token::NumberType::Integer) - && g_val.is(Token::NumberType::Integer) - && b_val.is(Token::NumberType::Integer) + if (r_val.is(Token::Type::Number) && r_val.number().is_integer() + && g_val.is(Token::Type::Number) && g_val.number().is_integer() + && b_val.is(Token::Type::Number) && b_val.number().is_integer() && a_val.is(Token::Type::Number)) { auto r = r_val.to_integer(); @@ -2571,7 +2571,7 @@ Optional Parser::parse_color(StyleComponentValueRule const& component_val if (cv.is(Token::Type::Number) || cv.is(Token::Type::Dimension)) { // 1. If cv’s type flag is not "integer", return an error. // This means that values that happen to use scientific notation, e.g., 5e5e5e, will fail to parse. - if (cv.token().number_type() != Token::NumberType::Integer) + if (!cv.token().number().is_integer()) return {}; // 2. If cv’s value is less than zero, return an error. @@ -4372,7 +4372,7 @@ Optional Parser::parse_a_n_plus_b_patt auto is_n_dimension = [](StyleComponentValueRule const& value) -> bool { if (!value.is(Token::Type::Dimension)) return false; - if (value.token().number_type() != Token::NumberType::Integer) + if (!value.token().number().is_integer()) return false; if (!value.token().dimension_unit().equals_ignoring_case("n"sv)) return false; @@ -4381,7 +4381,7 @@ Optional Parser::parse_a_n_plus_b_patt auto is_ndash_dimension = [](StyleComponentValueRule const& value) -> bool { if (!value.is(Token::Type::Dimension)) return false; - if (value.token().number_type() != Token::NumberType::Integer) + if (!value.token().number().is_integer()) return false; if (!value.token().dimension_unit().equals_ignoring_case("n-"sv)) return false; @@ -4390,7 +4390,7 @@ Optional Parser::parse_a_n_plus_b_patt auto is_ndashdigit_dimension = [](StyleComponentValueRule const& value) -> bool { if (!value.is(Token::Type::Dimension)) return false; - if (value.token().number_type() != Token::NumberType::Integer) + if (!value.token().number().is_integer()) return false; auto dimension_unit = value.token().dimension_unit(); if (!dimension_unit.starts_with("n-"sv, CaseSensitivity::CaseInsensitive)) @@ -4426,13 +4426,13 @@ Optional Parser::parse_a_n_plus_b_patt return true; }; auto is_integer = [](StyleComponentValueRule const& value) -> bool { - return value.is(Token::Type::Number) && value.token().is(Token::NumberType::Integer); + return value.is(Token::Type::Number) && value.token().number().is_integer(); }; - auto is_signed_integer = [is_integer](StyleComponentValueRule const& value) -> bool { - return is_integer(value) && value.token().is_integer_value_signed(); + auto is_signed_integer = [](StyleComponentValueRule const& value) -> bool { + return value.is(Token::Type::Number) && value.token().number().is_integer_with_explicit_sign(); }; - auto is_signless_integer = [is_integer](StyleComponentValueRule const& value) -> bool { - return is_integer(value) && !value.token().is_integer_value_signed(); + auto is_signless_integer = [](StyleComponentValueRule const& value) -> bool { + return value.is(Token::Type::Number) && !value.token().number().is_integer_with_explicit_sign(); }; // https://www.w3.org/TR/css-syntax-3/#the-anb-type diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Token.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Token.cpp index 11e64c4599..0a1ee2f410 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Token.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Token.cpp @@ -43,11 +43,11 @@ String Token::to_string() const case Type::Delim: return m_value; case Type::Number: - return String::number(m_number_value); + return String::number(m_number_value.value()); case Type::Percentage: - return String::formatted("{}%", m_number_value); + return String::formatted("{}%", m_number_value.value()); case Type::Dimension: - return String::formatted("{}{}", m_number_value, m_unit); + return String::formatted("{}{}", m_number_value.value(), dimension_unit()); case Type::Whitespace: return " "; case Type::CDO: @@ -122,8 +122,8 @@ String Token::to_debug_string() const return builder.to_string(); case Type::Number: builder.append("Number: "); - builder.append(m_value); - builder.append(m_number_type == NumberType::Integer ? " (int)" : " (float)"); + builder.append(m_number_value.value()); + builder.append(m_number_value.is_integer() ? " (int)" : " (float)"); return builder.to_string(); case Type::Percentage: builder.append("Percentage: "); @@ -132,8 +132,8 @@ String Token::to_debug_string() const return builder.to_string(); case Type::Dimension: builder.append("Dimension: "); - builder.append(m_value); - builder.append(m_unit); + builder.append(dimension_value()); + builder.append(dimension_unit()); return builder.to_string(); case Type::Whitespace: builder.append("Whitespace"); @@ -193,7 +193,7 @@ String Token::to_debug_string() const if (m_type == Token::Type::Number) { builder.append("', number_type: '"); - if (m_number_type == Token::NumberType::Integer) { + if (m_number_value.is_integer()) { builder.append("Integer"); } else { builder.append("Number"); @@ -202,14 +202,14 @@ String Token::to_debug_string() const if (m_type == Token::Type::Dimension) { builder.append("', number_type: '"); - if (m_number_type == Token::NumberType::Integer) { + if (m_number_value.is_integer()) { builder.append("Integer"); } else { builder.append("Number"); } builder.append("', unit: '"); - builder.append(m_unit); + builder.append(dimension_unit()); } builder.append("' }"); diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Token.h b/Userland/Libraries/LibWeb/CSS/Parser/Token.h index dd9955960f..f09b74df5a 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Token.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Token.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2020-2021, the SerenityOS developers. - * Copyright (c) 2021, Sam Atkins + * Copyright (c) 2021-2022, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -10,7 +10,7 @@ #include #include #include -#include +#include namespace Web::CSS { @@ -52,11 +52,6 @@ public: Unrestricted, }; - enum class NumberType { - Integer, - Number, - }; - struct Position { size_t line { 0 }; size_t column { 0 }; @@ -112,23 +107,21 @@ public: return m_value.view(); } - bool is(NumberType number_type) const { return is(Token::Type::Number) && m_number_type == number_type; } - StringView number_string_value() const + Number const& number() const { - VERIFY(m_type == Type::Number); - return m_value.view(); + VERIFY(m_type == Type::Number || m_type == Type::Dimension || m_type == Type::Percentage); + return m_number_value; } float number_value() const { VERIFY(m_type == Type::Number); - return m_number_value; + return m_number_value.value(); } i64 to_integer() const { - VERIFY(m_type == Type::Number && m_number_type == NumberType::Integer); - return to_closest_integer(m_number_value); + VERIFY(m_type == Type::Number && m_number_value.is_integer()); + return m_number_value.integer_value(); } - bool is_integer_value_signed() const { return number_string_value().starts_with('-') || number_string_value().starts_with('+'); } StringView dimension_unit() const { @@ -138,20 +131,14 @@ public: float dimension_value() const { VERIFY(m_type == Type::Dimension); - return m_number_value; + return m_number_value.value(); } - i64 dimension_value_int() const { return to_closest_integer(dimension_value()); } + i64 dimension_value_int() const { return m_number_value.integer_value(); } float percentage() const { VERIFY(m_type == Type::Percentage); - return m_number_value; - } - - NumberType number_type() const - { - VERIFY((m_type == Type::Number) || (m_type == Type::Dimension) || (m_type == Type::Percentage)); - return m_number_type; + return m_number_value.value(); } Type mirror_variant() const; @@ -165,21 +152,11 @@ public: Position const& end_position() const { return m_end_position; } private: - static i64 to_closest_integer(float value) - { - // https://www.w3.org/TR/css-values-4/#numeric-types - // When a value cannot be explicitly supported due to range/precision limitations, it must be converted - // to the closest value supported by the implementation, but how the implementation defines "closest" - // is explicitly undefined as well. - return llroundf(value); - } - Type m_type { Type::Invalid }; FlyString m_value; FlyString m_unit; - float m_number_value { 0 }; - NumberType m_number_type { NumberType::Integer }; + Number m_number_value; HashType m_hash_type { HashType::Unrestricted }; Position m_start_position; diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Tokenizer.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Tokenizer.cpp index 83fba2fe81..033af6beb5 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Tokenizer.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Tokenizer.cpp @@ -465,7 +465,7 @@ Token Tokenizer::consume_an_ident_like_token() } // https://www.w3.org/TR/css-syntax-3/#consume-number -CSSNumber Tokenizer::consume_a_number() +Number Tokenizer::consume_a_number() { // This section describes how to consume a number from a stream of code points. // It returns a numeric value, and a type which is either "integer" or "number". @@ -478,12 +478,14 @@ CSSNumber Tokenizer::consume_a_number() // 1. Initially set type to "integer". Let repr be the empty string. StringBuilder repr; - Token::NumberType type = Token::NumberType::Integer; + Number::Type type = Number::Type::Integer; // 2. If the next input code point is U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-), // consume it and append it to repr. + bool has_explicit_sign = false; auto next_input = peek_code_point(); if (is_plus_sign(next_input) || is_hyphen_minus(next_input)) { + has_explicit_sign = true; repr.append_code_point(next_code_point()); } @@ -505,7 +507,7 @@ CSSNumber Tokenizer::consume_a_number() repr.append_code_point(next_code_point()); // 3. Set type to "number". - type = Token::NumberType::Number; + type = Number::Type::Number; // 4. While the next input code point is a digit, consume it and append it to repr. for (;;) { @@ -537,7 +539,7 @@ CSSNumber Tokenizer::consume_a_number() } // 3. Set type to "number". - type = Token::NumberType::Number; + type = Number::Type::Number; // 4. While the next input code point is a digit, consume it and append it to repr. for (;;) { @@ -553,7 +555,9 @@ CSSNumber Tokenizer::consume_a_number() auto value = convert_a_string_to_a_number(repr.string_view()); // 7. Return value and type. - return { repr.to_string(), value, type }; + if (type == Number::Type::Integer && has_explicit_sign) + return Number { Number::Type::IntegerWithExplicitSign, value }; + return Number { type, value }; } // https://www.w3.org/TR/css-syntax-3/#convert-string-to-number @@ -836,9 +840,7 @@ Token Tokenizer::consume_a_numeric_token() // 1. Create a with the same value and type flag as number, // and a unit set initially to the empty string. auto token = create_new_token(Token::Type::Dimension); - token.m_value = move(number.string); - token.m_number_type = number.type; - token.m_number_value = number.value; + token.m_number_value = number; // 2. Consume a name. Set the ’s unit to the returned value. auto unit = consume_a_name(); @@ -855,17 +857,13 @@ Token Tokenizer::consume_a_numeric_token() // Create a with the same value as number, and return it. auto token = create_new_token(Token::Type::Percentage); - token.m_value = move(number.string); - token.m_number_type = number.type; - token.m_number_value = number.value; + token.m_number_value = number; return token; } // Otherwise, create a with the same value and type flag as number, and return it. auto token = create_new_token(Token::Type::Number); - token.m_value = move(number.string); - token.m_number_type = number.type; - token.m_number_value = number.value; + token.m_number_value = number; return token; } diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Tokenizer.h b/Userland/Libraries/LibWeb/CSS/Parser/Tokenizer.h index 0a623b5eb5..6521e74eb2 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Tokenizer.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Tokenizer.h @@ -57,13 +57,6 @@ public: u32 third {}; }; -class CSSNumber { -public: - String string; - float value { 0 }; - Token::NumberType type {}; -}; - class Tokenizer { public: @@ -89,7 +82,7 @@ private: [[nodiscard]] Token consume_string_token(u32 ending_code_point); [[nodiscard]] Token consume_a_numeric_token(); [[nodiscard]] Token consume_an_ident_like_token(); - [[nodiscard]] CSSNumber consume_a_number(); + [[nodiscard]] Number consume_a_number(); [[nodiscard]] float convert_a_string_to_a_number(StringView); [[nodiscard]] String consume_a_name(); [[nodiscard]] u32 consume_escaped_code_point();