浏览代码

LibWeb: Add the parsing to the calc() handling

This patch adds the parsing of previously tokenized calc() expressions
into the CSS-Parser. The tokens are processed into a complete
CalculatedStyleValue.
Tobias Christiansen 4 年之前
父节点
当前提交
328afa32c6
共有 1 个文件被更改,包括 281 次插入20 次删除
  1. 281 20
      Userland/Libraries/LibWeb/CSS/Parser/DeprecatedCSSParser.cpp

+ 281 - 20
Userland/Libraries/LibWeb/CSS/Parser/DeprecatedCSSParser.cpp

@@ -7,6 +7,7 @@
 
 #include <AK/GenericLexer.h>
 #include <AK/HashMap.h>
+#include <AK/NonnullOwnPtr.h>
 #include <AK/SourceLocation.h>
 #include <LibWeb/CSS/CSSImportRule.h>
 #include <LibWeb/CSS/CSSRule.h>
@@ -280,26 +281,38 @@ static StringView isolate_calc_expression(const StringView& value)
     return value.substring_view(5, substring_length);
 }
 
-static Optional<NonnullOwnPtr<CSS::CalculatedStyleValue::CalcSum>> parse_calc_expression(const StringView& expression_string)
+struct CalcToken {
+    enum class Type {
+        Undefined,
+        Number,
+        Unit,
+        Whitespace,
+        Plus,
+        Minus,
+        Asterisk,
+        Slash,
+        OpenBracket,
+        CloseBracket,
+    } type { Type::Undefined };
+    String value {};
+};
+
+static void eat_white_space(Vector<CalcToken>&);
+static Optional<CSS::CalculatedStyleValue::CalcValue> parse_calc_value(Vector<CalcToken>&);
+static OwnPtr<CSS::CalculatedStyleValue::CalcProductPartWithOperator> parse_calc_product_part_with_operator(Vector<CalcToken>&);
+static Optional<CSS::CalculatedStyleValue::CalcNumberValue> parse_calc_number_value(Vector<CalcToken>&);
+static OwnPtr<CSS::CalculatedStyleValue::CalcProduct> parse_calc_product(Vector<CalcToken>&);
+static OwnPtr<CSS::CalculatedStyleValue::CalcSumPartWithOperator> parse_calc_sum_part_with_operator(Vector<CalcToken>&);
+static OwnPtr<CSS::CalculatedStyleValue::CalcSum> parse_calc_sum(Vector<CalcToken>&);
+static OwnPtr<CSS::CalculatedStyleValue::CalcNumberSum> parse_calc_number_sum(Vector<CalcToken>& tokens);
+static OwnPtr<CSS::CalculatedStyleValue::CalcNumberProductPartWithOperator> parse_calc_number_product_part_with_operator(Vector<CalcToken>& tokens);
+static OwnPtr<CSS::CalculatedStyleValue::CalcNumberProduct> parse_calc_number_product(Vector<CalcToken>& tokens);
+static OwnPtr<CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator> parse_calc_number_sum_part_with_operator(Vector<CalcToken>& tokens);
+
+static OwnPtr<CSS::CalculatedStyleValue::CalcSum> parse_calc_expression(const StringView& expression_string)
 {
     // First, tokenize
 
-    struct CalcToken {
-        enum class Type {
-            Undefined,
-            Number,
-            Unit,
-            Whitespace,
-            Plus,
-            Minus,
-            Asterisk,
-            Slash,
-            OpenBracket,
-            CloseBracket,
-        } type { Type::Undefined };
-        String value {};
-    };
-
     Vector<CalcToken> tokens;
 
     GenericLexer lexer(expression_string);
@@ -367,7 +380,255 @@ static Optional<NonnullOwnPtr<CSS::CalculatedStyleValue::CalcSum>> parse_calc_ex
         VERIFY_NOT_REACHED();
     }
 
-    return {};
+    // Then, parse
+
+    return parse_calc_sum(tokens);
+}
+
+static void eat_white_space(Vector<CalcToken>& tokens)
+{
+    while (tokens.size() > 0 && tokens.first().type == CalcToken::Type::Whitespace)
+        tokens.take_first();
+}
+
+static Optional<CSS::CalculatedStyleValue::CalcValue> parse_calc_value(Vector<CalcToken>& tokens)
+{
+    auto current_token = tokens.take_first();
+
+    if (current_token.type == CalcToken::Type::OpenBracket) {
+        auto parsed_calc_sum = parse_calc_sum(tokens);
+        if (!parsed_calc_sum)
+            return {};
+        return (CSS::CalculatedStyleValue::CalcValue) { parsed_calc_sum.release_nonnull() };
+    }
+
+    if (current_token.type != CalcToken::Type::Number)
+        return {};
+
+    auto try_the_number = try_parse_float(current_token.value);
+    if (!try_the_number.has_value())
+        return {};
+
+    float the_number = try_the_number.value();
+
+    if (tokens.first().type != CalcToken::Type::Unit)
+        return (CSS::CalculatedStyleValue::CalcValue) { the_number };
+
+    auto type = length_type_from_unit(tokens.take_first().value);
+
+    if (type == CSS::Length::Type::Undefined)
+        return {};
+
+    return (CSS::CalculatedStyleValue::CalcValue) { CSS::Length(the_number, type) };
+}
+
+static OwnPtr<CSS::CalculatedStyleValue::CalcProductPartWithOperator> parse_calc_product_part_with_operator(Vector<CalcToken>& tokens)
+{
+    auto product_with_operator = make<CSS::CalculatedStyleValue::CalcProductPartWithOperator>();
+
+    eat_white_space(tokens);
+
+    auto op = tokens.first();
+    if (op.type == CalcToken::Type::Asterisk) {
+        tokens.take_first();
+        eat_white_space(tokens);
+        product_with_operator->op = CSS::CalculatedStyleValue::CalcProductPartWithOperator::Multiply;
+        auto parsed_calc_value = parse_calc_value(tokens);
+        if (!parsed_calc_value.has_value())
+            return nullptr;
+        product_with_operator->value = { parsed_calc_value.release_value() };
+
+    } else if (op.type == CalcToken::Type::Slash) {
+        tokens.take_first();
+        eat_white_space(tokens);
+        product_with_operator->op = CSS::CalculatedStyleValue::CalcProductPartWithOperator::Divide;
+        auto parsed_calc_number_value = parse_calc_number_value(tokens);
+        if (!parsed_calc_number_value.has_value())
+            return nullptr;
+        product_with_operator->value = { parsed_calc_number_value.release_value() };
+    } else {
+        return nullptr;
+    }
+
+    return product_with_operator;
+}
+
+static OwnPtr<CSS::CalculatedStyleValue::CalcNumberProductPartWithOperator> parse_calc_number_product_part_with_operator(Vector<CalcToken>& tokens)
+{
+    auto number_product_with_operator = make<CSS::CalculatedStyleValue::CalcNumberProductPartWithOperator>();
+
+    eat_white_space(tokens);
+
+    auto op = tokens.first();
+    if (op.type == CalcToken::Type::Asterisk) {
+        tokens.take_first();
+        eat_white_space(tokens);
+        number_product_with_operator->op = CSS::CalculatedStyleValue::CalcNumberProductPartWithOperator::Multiply;
+    } else if (op.type == CalcToken::Type::Slash) {
+        tokens.take_first();
+        eat_white_space(tokens);
+        number_product_with_operator->op = CSS::CalculatedStyleValue::CalcNumberProductPartWithOperator::Divide;
+    } else {
+        return nullptr;
+    }
+    auto parsed_calc_value = parse_calc_number_value(tokens);
+    if (!parsed_calc_value.has_value())
+        return nullptr;
+    number_product_with_operator->value = parsed_calc_value.release_value();
+
+    return number_product_with_operator;
+}
+
+static OwnPtr<CSS::CalculatedStyleValue::CalcNumberProduct> parse_calc_number_product(Vector<CalcToken>& tokens)
+{
+    auto calc_number_product = make<CSS::CalculatedStyleValue::CalcNumberProduct>();
+
+    auto first_calc_number_value_or_error = parse_calc_number_value(tokens);
+    if (!first_calc_number_value_or_error.has_value())
+        return nullptr;
+    calc_number_product->first_calc_number_value = first_calc_number_value_or_error.release_value();
+
+    while (tokens.size() > 0) {
+        auto number_product_with_operator = parse_calc_number_product_part_with_operator(tokens);
+        if (!number_product_with_operator)
+            break;
+        calc_number_product->zero_or_more_additional_calc_number_values.append(number_product_with_operator.release_nonnull());
+    }
+
+    return calc_number_product;
+}
+
+static OwnPtr<CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator> parse_calc_number_sum_part_with_operator(Vector<CalcToken>& tokens)
+{
+    if (tokens.size() < 3)
+        return nullptr;
+    if (!((tokens[0].type == CalcToken::Type::Plus
+              || tokens[0].type == CalcToken::Type::Minus)
+            && tokens[1].type == CalcToken::Type::Whitespace))
+        return nullptr;
+
+    auto op_token = tokens.take_first().type;
+    tokens.take_first(); // Whitespace;
+
+    CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator::Operation op;
+    if (op_token == CalcToken::Type::Plus)
+        op = CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator::Operation::Add;
+    else if (op_token == CalcToken::Type::Minus)
+        op = CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator::Operation::Subtract;
+    else
+        return nullptr;
+
+    auto calc_number_product = parse_calc_number_product(tokens);
+    if (!calc_number_product)
+        return nullptr;
+    return make<CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator>(op, calc_number_product.release_nonnull());
+}
+
+static OwnPtr<CSS::CalculatedStyleValue::CalcNumberSum> parse_calc_number_sum(Vector<CalcToken>& tokens)
+{
+    if (tokens.take_first().type != CalcToken::Type::OpenBracket)
+        return nullptr;
+
+    auto first_calc_number_product_or_error = parse_calc_number_product(tokens);
+    if (!first_calc_number_product_or_error)
+        return nullptr;
+
+    NonnullOwnPtrVector<CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator> additional {};
+    while (tokens.size() > 0 && tokens.first().type != CalcToken::Type::CloseBracket) {
+        auto calc_sum_part = parse_calc_number_sum_part_with_operator(tokens);
+        if (!calc_sum_part)
+            return nullptr;
+        additional.append(calc_sum_part.release_nonnull());
+    }
+
+    eat_white_space(tokens);
+
+    auto calc_number_sum = make<CSS::CalculatedStyleValue::CalcNumberSum>(first_calc_number_product_or_error.release_nonnull(), move(additional));
+    return calc_number_sum;
+}
+
+static Optional<CSS::CalculatedStyleValue::CalcNumberValue> parse_calc_number_value(Vector<CalcToken>& tokens)
+{
+    if (tokens.first().type == CalcToken::Type::OpenBracket) {
+        auto calc_number_sum = parse_calc_number_sum(tokens);
+        if (calc_number_sum)
+            return { calc_number_sum.release_nonnull() };
+    }
+
+    if (tokens.first().type != CalcToken::Type::Number)
+        return {};
+
+    auto the_number_string = tokens.take_first().value;
+    auto try_the_number = try_parse_float(the_number_string);
+    if (!try_the_number.has_value())
+        return {};
+    return try_the_number.value();
+}
+
+static OwnPtr<CSS::CalculatedStyleValue::CalcProduct> parse_calc_product(Vector<CalcToken>& tokens)
+{
+    auto calc_product = make<CSS::CalculatedStyleValue::CalcProduct>();
+
+    auto first_calc_value_or_error = parse_calc_value(tokens);
+    if (!first_calc_value_or_error.has_value())
+        return nullptr;
+    calc_product->first_calc_value = first_calc_value_or_error.release_value();
+
+    while (tokens.size() > 0) {
+        auto product_with_operator = parse_calc_product_part_with_operator(tokens);
+        if (!product_with_operator)
+            break;
+        calc_product->zero_or_more_additional_calc_values.append(product_with_operator.release_nonnull());
+    }
+
+    return calc_product;
+}
+
+static OwnPtr<CSS::CalculatedStyleValue::CalcSumPartWithOperator> parse_calc_sum_part_with_operator(Vector<CalcToken>& tokens)
+{
+    // The following has to have the shape of <Whitespace><+ or -><Whitespace>
+    // But the first whitespace gets eaten in parse_calc_product_part_with_operator().
+    if (tokens.size() < 3)
+        return {};
+    if (!((tokens[0].type == CalcToken::Type::Plus
+              || tokens[0].type == CalcToken::Type::Minus)
+            && tokens[1].type == CalcToken::Type::Whitespace))
+        return nullptr;
+
+    auto op_token = tokens.take_first().type;
+    tokens.take_first(); // Whitespace;
+
+    CSS::CalculatedStyleValue::CalcSumPartWithOperator::Operation op;
+    if (op_token == CalcToken::Type::Plus)
+        op = CSS::CalculatedStyleValue::CalcSumPartWithOperator::Operation::Add;
+    else if (op_token == CalcToken::Type::Minus)
+        op = CSS::CalculatedStyleValue::CalcSumPartWithOperator::Operation::Subtract;
+    else
+        return nullptr;
+
+    auto calc_product = parse_calc_product(tokens);
+    if (!calc_product)
+        return nullptr;
+    return make<CSS::CalculatedStyleValue::CalcSumPartWithOperator>(op, calc_product.release_nonnull());
+};
+
+static OwnPtr<CSS::CalculatedStyleValue::CalcSum> parse_calc_sum(Vector<CalcToken>& tokens)
+{
+    auto parsed_calc_product = parse_calc_product(tokens);
+    if (!parsed_calc_product)
+        return nullptr;
+
+    NonnullOwnPtrVector<CSS::CalculatedStyleValue::CalcSumPartWithOperator> additional {};
+    while (tokens.size() > 0 && tokens.first().type != CalcToken::Type::CloseBracket) {
+        auto calc_sum_part = parse_calc_sum_part_with_operator(tokens);
+        if (!calc_sum_part)
+            return nullptr;
+        additional.append(calc_sum_part.release_nonnull());
+    }
+
+    eat_white_space(tokens);
+
+    return make<CSS::CalculatedStyleValue::CalcSum>(parsed_calc_product.release_nonnull(), move(additional));
 }
 
 RefPtr<CSS::StyleValue> parse_css_value(const CSS::DeprecatedParsingContext& context, const StringView& string, CSS::PropertyID property_id)
@@ -401,8 +662,8 @@ RefPtr<CSS::StyleValue> parse_css_value(const CSS::DeprecatedParsingContext& con
     if (string.starts_with("calc(")) {
         auto calc_expression_string = isolate_calc_expression(string);
         auto calc_expression = parse_calc_expression(calc_expression_string);
-        if (calc_expression.has_value())
-            return CSS::CalculatedStyleValue::create(calc_expression_string, calc_expression.release_value());
+        if (calc_expression)
+            return CSS::CalculatedStyleValue::create(calc_expression_string, calc_expression.release_nonnull());
     }
 
     auto value_id = CSS::value_id_from_string(string);