[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / WebCore / css / CSSCalculationValue.cpp
old mode 100755 (executable)
new mode 100644 (file)
index 972d62b..a42f564
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2011, 2012 Google Inc. All rights reserved.
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
 #include "config.h"
 #include "CSSCalculationValue.h"
 
-#include "CSSStyleSelector.h"
-#include "CSSValueList.h"
-#include "Length.h"
-
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
+#include "CSSParser.h"
+#include "CSSParserTokenRange.h"
+#include "CSSPrimitiveValueMappings.h"
+#include "StyleResolver.h"
+#include <wtf/MathExtras.h>
 #include <wtf/text/StringBuilder.h>
 
 static const int maxExpressionDepth = 100;
@@ -49,14 +49,14 @@ enum ParseState {
 
 namespace WebCore {
 
-static CalculationCategory unitCategory(CSSPrimitiveValue::UnitTypes type)
+static RefPtr<CSSCalcExpressionNode> createCSS(const CalcExpressionNode&, const RenderStyle&);
+static RefPtr<CSSCalcExpressionNode> createCSS(const Length&, const RenderStyle&);
+
+static CalculationCategory unitCategory(CSSPrimitiveValue::UnitType type)
 {
     switch (type) {
     case CSSPrimitiveValue::CSS_NUMBER:
-    case CSSPrimitiveValue::CSS_PARSER_INTEGER:
-        return CalcNumber;
-    case CSSPrimitiveValue::CSS_PERCENTAGE:
-        return CalcPercent;
+        return CalculationCategory::Number;
     case CSSPrimitiveValue::CSS_EMS:
     case CSSPrimitiveValue::CSS_EXS:
     case CSSPrimitiveValue::CSS_PX:
@@ -66,296 +66,928 @@ static CalculationCategory unitCategory(CSSPrimitiveValue::UnitTypes type)
     case CSSPrimitiveValue::CSS_PT:
     case CSSPrimitiveValue::CSS_PC:
     case CSSPrimitiveValue::CSS_REMS:
-        return CalcLength;
+    case CSSPrimitiveValue::CSS_CHS:
+    case CSSPrimitiveValue::CSS_VW:
+    case CSSPrimitiveValue::CSS_VH:
+    case CSSPrimitiveValue::CSS_VMIN:
+    case CSSPrimitiveValue::CSS_VMAX:
+        return CalculationCategory::Length;
+    case CSSPrimitiveValue::CSS_PERCENTAGE:
+        return CalculationCategory::Percent;
+    case CSSPrimitiveValue::CSS_DEG:
+    case CSSPrimitiveValue::CSS_RAD:
+    case CSSPrimitiveValue::CSS_GRAD:
+    case CSSPrimitiveValue::CSS_TURN:
+        return CalculationCategory::Angle;
+    case CSSPrimitiveValue::CSS_MS:
+    case CSSPrimitiveValue::CSS_S:
+        return CalculationCategory::Time;
+    case CSSPrimitiveValue::CSS_HZ:
+    case CSSPrimitiveValue::CSS_KHZ:
+        return CalculationCategory::Frequency;
     default:
-        return CalcOther;
+        return CalculationCategory::Other;
     }
 }
-    
-String CSSCalcValue::customCssText() const
+
+static bool hasDoubleValue(CSSPrimitiveValue::UnitType type)
 {
-    return "";
+    switch (type) {
+    case CSSPrimitiveValue::CSS_FR:
+    case CSSPrimitiveValue::CSS_NUMBER:
+    case CSSPrimitiveValue::CSS_PERCENTAGE:
+    case CSSPrimitiveValue::CSS_EMS:
+    case CSSPrimitiveValue::CSS_EXS:
+    case CSSPrimitiveValue::CSS_CHS:
+    case CSSPrimitiveValue::CSS_REMS:
+    case CSSPrimitiveValue::CSS_PX:
+    case CSSPrimitiveValue::CSS_CM:
+    case CSSPrimitiveValue::CSS_MM:
+    case CSSPrimitiveValue::CSS_IN:
+    case CSSPrimitiveValue::CSS_PT:
+    case CSSPrimitiveValue::CSS_PC:
+    case CSSPrimitiveValue::CSS_DEG:
+    case CSSPrimitiveValue::CSS_RAD:
+    case CSSPrimitiveValue::CSS_GRAD:
+    case CSSPrimitiveValue::CSS_TURN:
+    case CSSPrimitiveValue::CSS_MS:
+    case CSSPrimitiveValue::CSS_S:
+    case CSSPrimitiveValue::CSS_HZ:
+    case CSSPrimitiveValue::CSS_KHZ:
+    case CSSPrimitiveValue::CSS_DIMENSION:
+    case CSSPrimitiveValue::CSS_VW:
+    case CSSPrimitiveValue::CSS_VH:
+    case CSSPrimitiveValue::CSS_VMIN:
+    case CSSPrimitiveValue::CSS_VMAX:
+    case CSSPrimitiveValue::CSS_DPPX:
+    case CSSPrimitiveValue::CSS_DPI:
+    case CSSPrimitiveValue::CSS_DPCM:
+        return true;
+    case CSSPrimitiveValue::CSS_UNKNOWN:
+    case CSSPrimitiveValue::CSS_STRING:
+    case CSSPrimitiveValue::CSS_FONT_FAMILY:
+    case CSSPrimitiveValue::CSS_URI:
+    case CSSPrimitiveValue::CSS_IDENT:
+    case CSSPrimitiveValue::CSS_ATTR:
+    case CSSPrimitiveValue::CSS_COUNTER:
+    case CSSPrimitiveValue::CSS_RECT:
+    case CSSPrimitiveValue::CSS_RGBCOLOR:
+    case CSSPrimitiveValue::CSS_PAIR:
+    case CSSPrimitiveValue::CSS_UNICODE_RANGE:
+    case CSSPrimitiveValue::CSS_COUNTER_NAME:
+    case CSSPrimitiveValue::CSS_SHAPE:
+    case CSSPrimitiveValue::CSS_QUAD:
+    case CSSPrimitiveValue::CSS_QUIRKY_EMS:
+    case CSSPrimitiveValue::CSS_CALC:
+    case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER:
+    case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH:
+    case CSSPrimitiveValue::CSS_PROPERTY_ID:
+    case CSSPrimitiveValue::CSS_VALUE_ID:
+        return false;
+    };
+    ASSERT_NOT_REACHED();
+    return false;
 }
 
-double CSSCalcValue::doubleValue() const 
-{ 
-    return m_expression->doubleValue();
+static String buildCssText(const String& expression)
+{
+    StringBuilder result;
+    result.appendLiteral("calc");
+    bool expressionHasSingleTerm = expression[0] != '(';
+    if (expressionHasSingleTerm)
+        result.append('(');
+    result.append(expression);
+    if (expressionHasSingleTerm)
+        result.append(')');
+    return result.toString();
 }
-    
-CSSCalcExpressionNode::~CSSCalcExpressionNode() 
+
+String CSSCalcValue::customCSSText() const
 {
+    return buildCssText(m_expression->customCSSText());
 }
-    
-class CSSCalcPrimitiveValue : public CSSCalcExpressionNode {
+
+bool CSSCalcValue::equals(const CSSCalcValue& other) const
+{
+    return compareCSSValue(m_expression, other.m_expression);
+}
+
+inline double CSSCalcValue::clampToPermittedRange(double value) const
+{
+    return m_shouldClampToNonNegative && value < 0 ? 0 : value;
+}
+
+double CSSCalcValue::doubleValue() const
+{
+    return clampToPermittedRange(m_expression->doubleValue());
+}
+
+double CSSCalcValue::computeLengthPx(const CSSToLengthConversionData& conversionData) const
+{
+    return clampToPermittedRange(m_expression->computeLengthPx(conversionData));
+}
+
+class CSSCalcPrimitiveValue final : public CSSCalcExpressionNode {
+    WTF_MAKE_FAST_ALLOCATED;
 public:
+    static Ref<CSSCalcPrimitiveValue> create(Ref<CSSPrimitiveValue>&& value, bool isInteger)
+    {
+        return adoptRef(*new CSSCalcPrimitiveValue(WTFMove(value), isInteger));
+    }
 
-    static PassRefPtr<CSSCalcPrimitiveValue> create(CSSPrimitiveValue* value, bool isInteger)
+    static RefPtr<CSSCalcPrimitiveValue> create(double value, CSSPrimitiveValue::UnitType type, bool isInteger)
     {
-        return adoptRef(new CSSCalcPrimitiveValue(value, isInteger));
+        if (!std::isfinite(value))
+            return nullptr;
+        return adoptRef(new CSSCalcPrimitiveValue(CSSPrimitiveValue::create(value, type), isInteger));
     }
-    
-    virtual String cssText() const
+
+private:
+    bool isZero() const final
+    {
+        return !m_value->doubleValue();
+    }
+
+    String customCSSText() const final
     {
         return m_value->cssText();
     }
 
-    virtual double doubleValue() const
+    std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData& conversionData) const final
     {
-        switch (m_category) {
-        case CalcNumber:
-        case CalcPercent:                
-            return m_value->getDoubleValue();
-        case CalcLength:
-        case CalcPercentLength:
-        case CalcPercentNumber:
-        case CalcOther:
+        switch (category()) {
+        case CalculationCategory::Number:
+            return makeUnique<CalcExpressionNumber>(m_value->floatValue());
+        case CalculationCategory::Length:
+            return makeUnique<CalcExpressionLength>(Length(m_value->computeLength<float>(conversionData), WebCore::Fixed));
+        case CalculationCategory::Percent:
+        case CalculationCategory::PercentLength: {
+            return makeUnique<CalcExpressionLength>(m_value->convertToLength<FixedFloatConversion | PercentConversion>(conversionData));
+        }
+        // Only types that could be part of a Length expression can be converted
+        // to a CalcExpressionNode. CalculationCategory::PercentNumber makes no sense as a Length.
+        case CalculationCategory::PercentNumber:
+        case CalculationCategory::Angle:
+        case CalculationCategory::Time:
+        case CalculationCategory::Frequency:
+        case CalculationCategory::Other:
+            ASSERT_NOT_REACHED();
+        }
+        ASSERT_NOT_REACHED();
+        return nullptr;
+    }
+
+    double doubleValue() const final
+    {
+        if (hasDoubleValue(primitiveType()))
+            return m_value->doubleValue();
+        ASSERT_NOT_REACHED();
+        return 0;
+    }
+
+    double computeLengthPx(const CSSToLengthConversionData& conversionData) const final
+    {
+        switch (category()) {
+        case CalculationCategory::Length:
+            return m_value->computeLength<double>(conversionData);
+        case CalculationCategory::Percent:
+        case CalculationCategory::Number:
+            return m_value->doubleValue();
+        case CalculationCategory::PercentLength:
+        case CalculationCategory::PercentNumber:
+        case CalculationCategory::Angle:
+        case CalculationCategory::Time:
+        case CalculationCategory::Frequency:
+        case CalculationCategory::Other:
             ASSERT_NOT_REACHED();
             break;
         }
+        ASSERT_NOT_REACHED();
         return 0;
-    }    
+    }
+
+    void collectDirectComputationalDependencies(HashSet<CSSPropertyID>& values) const final
+    {
+        m_value->collectDirectComputationalDependencies(values);
+    }
+
+    void collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>& values) const final
+    {
+        m_value->collectDirectRootComputationalDependencies(values);
+    }
+
+    bool equals(const CSSCalcExpressionNode& other) const final
+    {
+        if (type() != other.type())
+            return false;
+
+        return compareCSSValue(m_value, static_cast<const CSSCalcPrimitiveValue&>(other).m_value);
+    }
+
+    Type type() const final { return CssCalcPrimitiveValue; }
+    CSSPrimitiveValue::UnitType primitiveType() const final
+    {
+        return CSSPrimitiveValue::UnitType(m_value->primitiveType());
+    }
+
 private:
-    explicit CSSCalcPrimitiveValue(CSSPrimitiveValue* value, bool isInteger)
-        : CSSCalcExpressionNode(unitCategory((CSSPrimitiveValue::UnitTypes)value->primitiveType()), isInteger)
-        , m_value(value)                 
+    explicit CSSCalcPrimitiveValue(Ref<CSSPrimitiveValue>&& value, bool isInteger)
+        : CSSCalcExpressionNode(unitCategory((CSSPrimitiveValue::UnitType)value->primitiveType()), isInteger)
+        , m_value(WTFMove(value))
     {
     }
 
-    RefPtr<CSSPrimitiveValue> m_value;
+    Ref<CSSPrimitiveValue> m_value;
 };
 
-static const CalculationCategory addSubtractResult[CalcOther][CalcOther] = {
-    { CalcNumber,        CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
-    { CalcOther,         CalcLength,        CalcPercentLength, CalcOther,         CalcPercentLength },
-    { CalcPercentNumber, CalcPercentLength, CalcPercent,       CalcPercentNumber, CalcPercentLength },
-    { CalcPercentNumber, CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
-    { CalcOther,         CalcPercentLength, CalcPercentLength, CalcOther,         CalcPercentLength },
-};    
-    
-class CSSCalcBinaryOperation : public CSSCalcExpressionNode {
+static const CalculationCategory addSubtractResult[static_cast<unsigned>(CalculationCategory::Angle)][static_cast<unsigned>(CalculationCategory::Angle)] = {
+//    CalculationCategory::Number         CalculationCategory::Length         CalculationCategory::Percent        CalculationCategory::PercentNumber  CalculationCategory::PercentLength
+    { CalculationCategory::Number,        CalculationCategory::Other,         CalculationCategory::PercentNumber, CalculationCategory::PercentNumber, CalculationCategory::Other }, //         CalculationCategory::Number
+    { CalculationCategory::Other,         CalculationCategory::Length,        CalculationCategory::PercentLength, CalculationCategory::Other,         CalculationCategory::PercentLength }, // CalculationCategory::Length
+    { CalculationCategory::PercentNumber, CalculationCategory::PercentLength, CalculationCategory::Percent,       CalculationCategory::PercentNumber, CalculationCategory::PercentLength }, // CalculationCategory::Percent
+    { CalculationCategory::PercentNumber, CalculationCategory::Other,         CalculationCategory::PercentNumber, CalculationCategory::PercentNumber, CalculationCategory::Other }, //         CalculationCategory::PercentNumber
+    { CalculationCategory::Other,         CalculationCategory::PercentLength, CalculationCategory::PercentLength, CalculationCategory::Other,         CalculationCategory::PercentLength }, // CalculationCategory::PercentLength
+};
+
+static CalculationCategory determineCategory(const CSSCalcExpressionNode& leftSide, const CSSCalcExpressionNode& rightSide, CalcOperator op)
+{
+    CalculationCategory leftCategory = leftSide.category();
+    CalculationCategory rightCategory = rightSide.category();
+    ASSERT(leftCategory < CalculationCategory::Other);
+    ASSERT(rightCategory < CalculationCategory::Other);
+
+    switch (op) {
+    case CalcOperator::Add:
+    case CalcOperator::Subtract:
+        if (leftCategory < CalculationCategory::Angle && rightCategory < CalculationCategory::Angle)
+            return addSubtractResult[static_cast<unsigned>(leftCategory)][static_cast<unsigned>(rightCategory)];
+        if (leftCategory == rightCategory)
+            return leftCategory;
+        return CalculationCategory::Other;
+    case CalcOperator::Multiply:
+        if (leftCategory != CalculationCategory::Number && rightCategory != CalculationCategory::Number)
+            return CalculationCategory::Other;
+        return leftCategory == CalculationCategory::Number ? rightCategory : leftCategory;
+    case CalcOperator::Divide:
+        if (rightCategory != CalculationCategory::Number || rightSide.isZero())
+            return CalculationCategory::Other;
+        return leftCategory;
+    case CalcOperator::Min:
+    case CalcOperator::Max:
+        ASSERT_NOT_REACHED();
+        return CalculationCategory::Other;
+    }
+
+    ASSERT_NOT_REACHED();
+    return CalculationCategory::Other;
+}
+
+static CalculationCategory resolvedTypeForMinOrMax(CalculationCategory category, CalculationCategory destinationCategory)
+{
+    switch (category) {
+    case CalculationCategory::Number:
+    case CalculationCategory::Length:
+    case CalculationCategory::PercentNumber:
+    case CalculationCategory::PercentLength:
+    case CalculationCategory::Angle:
+    case CalculationCategory::Time:
+    case CalculationCategory::Frequency:
+    case CalculationCategory::Other:
+        return category;
+
+    case CalculationCategory::Percent:
+        if (destinationCategory == CalculationCategory::Length)
+            return CalculationCategory::PercentLength;
+        if (destinationCategory == CalculationCategory::Number)
+            return CalculationCategory::PercentNumber;
+        return category;
+    }
+
+    return CalculationCategory::Other;
+}
+
+static inline bool isIntegerResult(CalcOperator op, const CSSCalcExpressionNode& leftSide, const CSSCalcExpressionNode& rightSide)
+{
+    // Performs W3C spec's type checking for calc integers.
+    // http://www.w3.org/TR/css3-values/#calc-type-checking
+    return op != CalcOperator::Divide && leftSide.isInteger() && rightSide.isInteger();
+}
+
+static inline bool isIntegerResult(CalcOperator op, const Vector<Ref<CSSCalcExpressionNode>>& nodes)
+{
+    // Performs W3C spec's type checking for calc integers.
+    // http://www.w3.org/TR/css3-values/#calc-type-checking
+    if (op == CalcOperator::Divide)
+        return false;
+
+    for (auto& node : nodes) {
+        if (!node->isInteger())
+            return false;
+    }
+
+    return true;
+}
+
+static bool isSamePair(CalculationCategory a, CalculationCategory b, CalculationCategory x, CalculationCategory y)
+{
+    return (a == x && b == y) || (a == y && b == x);
+}
+
+class CSSCalcOperation final : public CSSCalcExpressionNode {
+    WTF_MAKE_FAST_ALLOCATED;
 public:
-    static PassRefPtr<CSSCalcBinaryOperation> create(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
+    static RefPtr<CSSCalcOperation> create(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
     {
-        CalculationCategory leftCategory = leftSide->category();
-        CalculationCategory rightCategory = rightSide->category();
-        CalculationCategory newCategory = CalcOther;
-        
-        ASSERT(leftCategory != CalcOther && rightCategory != CalcOther);
-        
+        if (!leftSide || !rightSide)
+            return nullptr;
+
+        ASSERT(leftSide->category() < CalculationCategory::Other);
+        ASSERT(rightSide->category() < CalculationCategory::Other);
+
+        auto newCategory = determineCategory(*leftSide, *rightSide, op);
+        if (newCategory == CalculationCategory::Other)
+            return nullptr;
+
+        return adoptRef(new CSSCalcOperation(newCategory, op, leftSide.releaseNonNull(), rightSide.releaseNonNull()));
+    }
+
+    static RefPtr<CSSCalcOperation> createMinOrMax(CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& values, CalculationCategory destinationCategory)
+    {
+        ASSERT(op == CalcOperator::Min || op == CalcOperator::Max);
+
+        Optional<CalculationCategory> category = WTF::nullopt;
+        for (auto& value : values) {
+            auto valueCategory = resolvedTypeForMinOrMax(value->category(), destinationCategory);
+
+            ASSERT(valueCategory < CalculationCategory::Other);
+            if (!category) {
+                if (valueCategory == CalculationCategory::Other)
+                    return nullptr;
+                category = valueCategory;
+            }
+
+            if (category != valueCategory) {
+                if (isSamePair(category.value(), valueCategory, CalculationCategory::Length, CalculationCategory::PercentLength)) {
+                    category = CalculationCategory::PercentLength;
+                    continue;
+                }
+                if (isSamePair(category.value(), valueCategory, CalculationCategory::Number, CalculationCategory::PercentNumber)) {
+                    category = CalculationCategory::PercentNumber;
+                    continue;
+                }
+                return nullptr;
+            }
+        }
+
+        return adoptRef(new CSSCalcOperation(category.value(), op, WTFMove(values)));
+    }
+
+    static RefPtr<CSSCalcExpressionNode> createSimplified(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
+    {
+        if (!leftSide || !rightSide)
+            return nullptr;
+
+        auto leftCategory = leftSide->category();
+        auto rightCategory = rightSide->category();
+        ASSERT(leftCategory < CalculationCategory::Other);
+        ASSERT(rightCategory < CalculationCategory::Other);
+
+        bool isInteger = isIntegerResult(op, *leftSide, *rightSide);
+
+        // Simplify numbers.
+        if (leftCategory == CalculationCategory::Number && rightCategory == CalculationCategory::Number) {
+            CSSPrimitiveValue::UnitType evaluationType = CSSPrimitiveValue::CSS_NUMBER;
+            return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftSide->doubleValue(), rightSide->doubleValue() }), evaluationType, isInteger);
+        }
+
+        // Simplify addition and subtraction between same types.
+        if (op == CalcOperator::Add || op == CalcOperator::Subtract) {
+            if (leftCategory == rightSide->category()) {
+                CSSPrimitiveValue::UnitType leftType = leftSide->primitiveType();
+                if (hasDoubleValue(leftType)) {
+                    CSSPrimitiveValue::UnitType rightType = rightSide->primitiveType();
+                    if (leftType == rightType)
+                        return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftSide->doubleValue(), rightSide->doubleValue() }), leftType, isInteger);
+                    CSSPrimitiveValue::UnitCategory leftUnitCategory = CSSPrimitiveValue::unitCategory(leftType);
+                    if (leftUnitCategory != CSSPrimitiveValue::UOther && leftUnitCategory == CSSPrimitiveValue::unitCategory(rightType)) {
+                        CSSPrimitiveValue::UnitType canonicalType = CSSPrimitiveValue::canonicalUnitTypeForCategory(leftUnitCategory);
+                        if (canonicalType != CSSPrimitiveValue::CSS_UNKNOWN) {
+                            double leftValue = leftSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(leftType);
+                            double rightValue = rightSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(rightType);
+                            return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftValue, rightValue }), canonicalType, isInteger);
+                        }
+                    }
+                }
+            }
+        } else {
+            // Simplify multiplying or dividing by a number for simplifiable types.
+            ASSERT(op == CalcOperator::Multiply || op == CalcOperator::Divide);
+            auto* numberSide = getNumberSide(*leftSide, *rightSide);
+            if (!numberSide)
+                return create(op, leftSide.releaseNonNull(), rightSide.releaseNonNull());
+            if (numberSide == leftSide && op == CalcOperator::Divide)
+                return nullptr;
+            auto& otherSide = leftSide == numberSide ? *rightSide : *leftSide;
+
+            double number = numberSide->doubleValue();
+            if (!std::isfinite(number))
+                return nullptr;
+            if (op == CalcOperator::Divide && !number)
+                return nullptr;
+
+            auto otherType = otherSide.primitiveType();
+            if (hasDoubleValue(otherType))
+                return CSSCalcPrimitiveValue::create(evaluateOperator(op, { otherSide.doubleValue(), number }), otherType, isInteger);
+        }
+
+        return create(op, leftSide.releaseNonNull(), rightSide.releaseNonNull());
+    }
+
+private:
+    bool isZero() const final
+    {
+        return !doubleValue();
+    }
+
+    std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData& conversionData) const final
+    {
+        Vector<std::unique_ptr<CalcExpressionNode>> nodes;
+        nodes.reserveInitialCapacity(m_children.size());
+
+        for (auto& child : m_children) {
+            auto node = child->createCalcExpression(conversionData);
+            if (!node)
+                return nullptr;
+            nodes.uncheckedAppend(WTFMove(node));
+        }
+        return makeUnique<CalcExpressionOperation>(WTFMove(nodes), m_operator);
+    }
+
+    double doubleValue() const final
+    {
+        Vector<double> doubleValues;
+        for (auto& child : m_children)
+            doubleValues.append(child->doubleValue());
+        return evaluate(doubleValues);
+    }
+
+    double computeLengthPx(const CSSToLengthConversionData& conversionData) const final
+    {
+        Vector<double> doubleValues;
+        for (auto& child : m_children)
+            doubleValues.append(child->computeLengthPx(conversionData));
+        return evaluate(doubleValues);
+    }
+
+    void collectDirectComputationalDependencies(HashSet<CSSPropertyID>& values) const final
+    {
+        for (auto& child : m_children)
+            child->collectDirectComputationalDependencies(values);
+    }
+
+    void collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>& values) const final
+    {
+        for (auto& child : m_children)
+            child->collectDirectRootComputationalDependencies(values);
+    }
+
+    static String buildCssText(Vector<String> childExpressions, CalcOperator op)
+    {
+        StringBuilder result;
+        result.append('(');
         switch (op) {
-        case CalcAdd:
-        case CalcSubtract:             
-            if (leftCategory == CalcOther || rightCategory == CalcOther)
-                return 0;
-            newCategory = addSubtractResult[leftCategory][rightCategory];
-            break;   
-                
-        case CalcMultiply:
-            if (leftCategory != CalcNumber && rightCategory != CalcNumber) 
-                return 0;
-            
-            newCategory = leftCategory == CalcNumber ? rightCategory : leftCategory;
-            break;
-                
-        case CalcDivide:
-        case CalcMod:
-            if (rightCategory != CalcNumber || rightSide->isZero())
-                return 0;
-            newCategory = leftCategory;
+        case CalcOperator::Add:
+        case CalcOperator::Subtract:
+        case CalcOperator::Multiply:
+        case CalcOperator::Divide:
+            ASSERT(childExpressions.size() == 2);
+            result.append(childExpressions[0]);
+            result.append(' ');
+            result.append(static_cast<char>(op));
+            result.append(' ');
+            result.append(childExpressions[1]);
             break;
+        case CalcOperator::Min:
+        case CalcOperator::Max:
+            ASSERT(!childExpressions.isEmpty());
+            const char* functionName = op == CalcOperator::Min ? "min(" : "max(";
+            result.append(functionName);
+            result.append(childExpressions[0]);
+            for (size_t i = 1; i < childExpressions.size(); ++i) {
+                result.append(',');
+                result.append(' ');
+                result.append(childExpressions[i]);
+            }
+            result.append(')');
         }
-        
-        if (newCategory == CalcOther)
-            return 0;
-            
-        return adoptRef(new CSSCalcBinaryOperation(leftSide, rightSide, op, newCategory));
+        result.append(')');
+
+        return result.toString();
     }
-    
-    virtual double doubleValue() const 
+
+    String customCSSText() const final
     {
-        return evaluate(m_leftSide->doubleValue(), m_rightSide->doubleValue());
+        Vector<String> cssTexts;
+        for (auto& child : m_children)
+            cssTexts.append(child->customCSSText());
+        return buildCssText(cssTexts, m_operator);
     }
 
-private:
-    CSSCalcBinaryOperation(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op, CalculationCategory category)
-        : CSSCalcExpressionNode(category, leftSide->isInteger() && rightSide->isInteger())
-        , m_leftSide(leftSide)
-        , m_rightSide(rightSide)
+    bool equals(const CSSCalcExpressionNode& exp) const final
+    {
+        if (type() != exp.type())
+            return false;
+
+        const CSSCalcOperation& other = static_cast<const CSSCalcOperation&>(exp);
+
+        if (m_children.size() != other.m_children.size() || m_operator != other.m_operator)
+            return false;
+
+        for (size_t i = 0; i < m_children.size(); ++i) {
+            if (!compareCSSValue(m_children[i], other.m_children[i]))
+                return false;
+        }
+        return true;
+    }
+
+    Type type() const final { return CssCalcOperation; }
+
+    CSSPrimitiveValue::UnitType primitiveType() const final
+    {
+        switch (category()) {
+        case CalculationCategory::Number:
+#if !ASSERT_DISABLED
+            for (auto& child : m_children)
+                ASSERT(child->category() == CalculationCategory::Number);
+#endif
+            return CSSPrimitiveValue::CSS_NUMBER;
+        case CalculationCategory::Length:
+        case CalculationCategory::Percent: {
+            if (m_children.isEmpty())
+                return CSSPrimitiveValue::CSS_UNKNOWN;
+            if (m_children.size() == 2) {
+                if (m_children[0]->category() == CalculationCategory::Number)
+                    return m_children[1]->primitiveType();
+                if (m_children[1]->category() == CalculationCategory::Number)
+                    return m_children[0]->primitiveType();
+            }
+            CSSPrimitiveValue::UnitType firstType = m_children[0]->primitiveType();
+            for (auto& child : m_children) {
+                if (firstType != child->primitiveType())
+                    return CSSPrimitiveValue::CSS_UNKNOWN;
+            }
+            return firstType;
+        }
+        case CalculationCategory::Angle:
+            return CSSPrimitiveValue::CSS_DEG;
+        case CalculationCategory::Time:
+            return CSSPrimitiveValue::CSS_MS;
+        case CalculationCategory::Frequency:
+            return CSSPrimitiveValue::CSS_HZ;
+        case CalculationCategory::PercentLength:
+        case CalculationCategory::PercentNumber:
+        case CalculationCategory::Other:
+            return CSSPrimitiveValue::CSS_UNKNOWN;
+        }
+        ASSERT_NOT_REACHED();
+        return CSSPrimitiveValue::CSS_UNKNOWN;
+    }
+
+    CSSCalcOperation(CalculationCategory category, CalcOperator op, Ref<CSSCalcExpressionNode>&& leftSide, Ref<CSSCalcExpressionNode>&& rightSide)
+        : CSSCalcExpressionNode(category, isIntegerResult(op, leftSide.get(), rightSide.get()))
         , m_operator(op)
     {
+        m_children.reserveInitialCapacity(2);
+        m_children.uncheckedAppend(WTFMove(leftSide));
+        m_children.uncheckedAppend(WTFMove(rightSide));
     }
-    
-    double evaluate(double leftValue, double rightValue) const
-    {
-        switch (m_operator) {
-        case CalcAdd:
-            return leftValue + rightValue;
-        case CalcSubtract:
-            return leftValue - rightValue;
-        case CalcMultiply:
-            return leftValue * rightValue;
-        case CalcDivide:
-            if (rightValue)
-                return leftValue / rightValue;
-            return std::numeric_limits<double>::quiet_NaN();
-        case CalcMod:
-            // FIXME calc() : mod has been removed from the spec, need to remove
-            // this enum value
-            return 0;
+
+    CSSCalcOperation(CalculationCategory category, CalcOperator op, Vector<Ref<CSSCalcExpressionNode>>&& children)
+        : CSSCalcExpressionNode(category, isIntegerResult(op, children))
+        , m_operator(op)
+        , m_children(WTFMove(children))
+    {
+    }
+
+    static CSSCalcExpressionNode* getNumberSide(CSSCalcExpressionNode& leftSide, CSSCalcExpressionNode& rightSide)
+    {
+        if (leftSide.category() == CalculationCategory::Number)
+            return &leftSide;
+        if (rightSide.category() == CalculationCategory::Number)
+            return &rightSide;
+        return nullptr;
+    }
+
+    double evaluate(const Vector<double>& children) const
+    {
+        return evaluateOperator(m_operator, children);
+    }
+
+    static double evaluateOperator(CalcOperator op, const Vector<double>& children)
+    {
+        switch (op) {
+        case CalcOperator::Add:
+            ASSERT(children.size() == 2);
+            return children[0] + children[1];
+        case CalcOperator::Subtract:
+            ASSERT(children.size() == 2);
+            return children[0] - children[1];
+        case CalcOperator::Multiply:
+            ASSERT(children.size() == 2);
+            return children[0] * children[1];
+        case CalcOperator::Divide:
+            ASSERT(children.size() == 1 || children.size() == 2);
+            if (children.size() == 1)
+                return std::numeric_limits<double>::quiet_NaN();
+            return children[0] / children[1];
+        case CalcOperator::Min: {
+            if (children.isEmpty())
+                return std::numeric_limits<double>::quiet_NaN();
+            double minimum = children[0];
+            for (auto child : children)
+                minimum = std::min(minimum, child);
+            return minimum;
+        }
+        case CalcOperator::Max: {
+            if (children.isEmpty())
+                return std::numeric_limits<double>::quiet_NaN();
+            double maximum = children[0];
+            for (auto child : children)
+                maximum = std::max(maximum, child);
+            return maximum;
         }
+        }
+        ASSERT_NOT_REACHED();
         return 0;
     }
-    
-    const RefPtr<CSSCalcExpressionNode> m_leftSide;
-    const RefPtr<CSSCalcExpressionNode> m_rightSide;
+
     const CalcOperator m_operator;
+    Vector<Ref<CSSCalcExpressionNode>> m_children;
 };
 
-static ParseState checkDepthAndIndex(int* depth, unsigned index, CSSParserValueList* tokens)
+static ParseState checkDepthAndIndex(int* depth, CSSParserTokenRange tokens)
 {
     (*depth)++;
+    if (tokens.atEnd())
+        return NoMoreTokens;
     if (*depth > maxExpressionDepth)
         return TooDeep;
-    if (index >= tokens->size())
-        return NoMoreTokens;
     return OK;
 }
 
 class CSSCalcExpressionNodeParser {
 public:
-    PassRefPtr<CSSCalcExpressionNode> parseCalc(CSSParserValueList* tokens)
+    explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory)
+        : m_destinationCategory(destinationCategory)
+    { }
+
+    RefPtr<CSSCalcExpressionNode> parseCalc(CSSParserTokenRange tokens, CSSValueID function)
     {
-        unsigned index = 0;
         Value result;
-        bool ok = parseValueExpression(tokens, 0, &index, &result);
-        ASSERT(index <= tokens->size());
-        if (!ok || index != tokens->size())
-            return 0;
+        tokens.consumeWhitespace();
+        bool ok = false;
+        if (function == CSSValueCalc || function == CSSValueWebkitCalc)
+            ok = parseValueExpression(tokens, 0, &result);
+        else if (function == CSSValueMin || function == CSSValueMax)
+            ok = parseMinMaxExpression(tokens, function, 0, &result);
+        if (!ok || !tokens.atEnd())
+            return nullptr;
         return result.value;
     }
-
+    
 private:
     struct Value {
         RefPtr<CSSCalcExpressionNode> value;
     };
-
-    char operatorValue(CSSParserValueList* tokens, unsigned index)
+    
+    char operatorValue(const CSSParserToken& token)
     {
-        if (index >= tokens->size())
-            return 0;
-        CSSParserValue* value = tokens->valueAt(index);
-        if (value->unit != CSSParserValue::Operator)
-            return 0;
-
-        return value->iValue;
+        if (token.type() == DelimiterToken)
+            return token.delimiter();
+        return 0;
     }
-
-    bool parseValue(CSSParserValueList* tokens, unsigned* index, Value* result)
+    
+    bool parseValue(CSSParserTokenRange& tokens, Value* result)
     {
-        CSSParserValue* parserValue = tokens->valueAt(*index);
-        if (parserValue->unit == CSSParserValue::Operator || parserValue->unit == CSSParserValue::Function)
+        CSSParserToken token = tokens.consumeIncludingWhitespace();
+        if (!(token.type() == NumberToken || token.type() == PercentageToken || token.type() == DimensionToken))
             return false;
-
-        RefPtr<CSSValue> value = parserValue->createCSSValue();
-        if (!value || !value->isPrimitiveValue())
+        
+        CSSPrimitiveValue::UnitType type = token.unitType();
+        if (unitCategory(type) == CalculationCategory::Other)
             return false;
-
-        CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get());
-        result->value = CSSCalcPrimitiveValue::create(primitiveValue, parserValue->isInt);
-
-        ++*index;
+        
+        bool isInteger = token.numericValueType() == IntegerValueType || (token.numericValueType() == NumberValueType && token.numericValue() == trunc(token.numericValue()));
+        result->value = CSSCalcPrimitiveValue::create(CSSPrimitiveValue::create(token.numericValue(), type), isInteger);
+        
         return true;
     }
+    
+    bool parseValueTerm(CSSParserTokenRange& tokens, int depth, Value* result)
+    {
+        if (checkDepthAndIndex(&depth, tokens) != OK)
+            return false;
 
-    bool parseValueTerm(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
+        auto functionId = tokens.peek().functionId();
+        
+        if (tokens.peek().type() == LeftParenthesisToken || functionId == CSSValueCalc) {
+            CSSParserTokenRange innerRange = tokens.consumeBlock();
+            tokens.consumeWhitespace();
+            innerRange.consumeWhitespace();
+            return parseValueExpression(innerRange, depth, result);
+        }
+
+        if (functionId == CSSValueMax || functionId == CSSValueMin) {
+            CSSParserTokenRange innerRange = tokens.consumeBlock();
+            tokens.consumeWhitespace();
+            innerRange.consumeWhitespace();
+            return parseMinMaxExpression(innerRange, functionId, depth, result);
+        }
+        
+        return parseValue(tokens, result);
+    }
+    
+    bool parseValueMultiplicativeExpression(CSSParserTokenRange& tokens, int depth, Value* result)
     {
-        if (checkDepthAndIndex(&depth, *index, tokens) != OK)
-            return false;    
+        if (checkDepthAndIndex(&depth, tokens) != OK)
+            return false;
         
-        if (operatorValue(tokens, *index) == '(') {
-            unsigned currentIndex = *index + 1;
-            if (!parseValueExpression(tokens, depth, &currentIndex, result))
+        if (!parseValueTerm(tokens, depth, result))
+            return false;
+        
+        while (!tokens.atEnd()) {
+            char operatorCharacter = operatorValue(tokens.peek());
+            if (operatorCharacter != static_cast<char>(CalcOperator::Multiply) && operatorCharacter != static_cast<char>(CalcOperator::Divide))
+                break;
+            tokens.consumeIncludingWhitespace();
+            
+            Value rhs;
+            if (!parseValueTerm(tokens, depth, &rhs))
                 return false;
+            
+            result->value = CSSCalcOperation::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result->value), WTFMove(rhs.value));
 
-            if (operatorValue(tokens, currentIndex) != ')')
+            if (!result->value)
                 return false;
-            *index = currentIndex + 1;
-            return true;
         }
-
-        return parseValue(tokens, index, result);
+        
+        return true;
     }
-
-    bool parseValueMultiplicativeExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
+    
+    bool parseAdditiveValueExpression(CSSParserTokenRange& tokens, int depth, Value* result)
     {
-        if (checkDepthAndIndex(&depth, *index, tokens) != OK)
-            return false;    
-
-        if (!parseValueTerm(tokens, depth, index, result))
+        if (checkDepthAndIndex(&depth, tokens) != OK)
             return false;
-
-        while (*index < tokens->size() - 1) {
-            char operatorCharacter = operatorValue(tokens, *index);
-            if (operatorCharacter != CalcMultiply && operatorCharacter != CalcDivide && operatorCharacter != CalcMod)
+        
+        if (!parseValueMultiplicativeExpression(tokens, depth, result))
+            return false;
+        
+        while (!tokens.atEnd()) {
+            char operatorCharacter = operatorValue(tokens.peek());
+            if (operatorCharacter != static_cast<char>(CalcOperator::Add) && operatorCharacter != static_cast<char>(CalcOperator::Subtract))
                 break;
-            ++*index;
-
+            if ((&tokens.peek() - 1)->type() != WhitespaceToken)
+                return false; // calc(1px+ 2px) is invalid
+            tokens.consume();
+            if (tokens.peek().type() != WhitespaceToken)
+                return false; // calc(1px +2px) is invalid
+            tokens.consumeIncludingWhitespace();
+            
             Value rhs;
-            if (!parseValueTerm(tokens, depth, index, &rhs))
+            if (!parseValueMultiplicativeExpression(tokens, depth, &rhs))
                 return false;
-
-            result->value = CSSCalcBinaryOperation::create(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
+            
+            result->value = CSSCalcOperation::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result->value), WTFMove(rhs.value));
             if (!result->value)
                 return false;
         }
-
-        ASSERT(*index <= tokens->size());
+        
         return true;
     }
 
-    bool parseAdditiveValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
+    bool parseMinMaxExpression(CSSParserTokenRange& tokens, CSSValueID minMaxFunction, int depth, Value* result)
     {
-        if (checkDepthAndIndex(&depth, *index, tokens) != OK)
-            return false;    
+        if (checkDepthAndIndex(&depth, tokens) != OK)
+            return false;
 
-        if (!parseValueMultiplicativeExpression(tokens, depth, index, result))
+        CalcOperator op = (minMaxFunction == CSSValueMin) ? CalcOperator::Min : CalcOperator::Max;
+
+        Value value;
+        if (!parseValueExpression(tokens, depth, &value))
             return false;
 
-        while (*index < tokens->size() - 1) {
-            char operatorCharacter = operatorValue(tokens, *index);
-            if (operatorCharacter != CalcAdd && operatorCharacter != CalcSubtract)
-                break;
-            ++*index;
+        Vector<Ref<CSSCalcExpressionNode>> nodes;
+        nodes.append(value.value.releaseNonNull());
 
-            Value rhs;
-            if (!parseValueMultiplicativeExpression(tokens, depth, index, &rhs))
+        while (!tokens.atEnd()) {
+            tokens.consumeWhitespace();
+            if (tokens.consume().type() != CommaToken)
                 return false;
+            tokens.consumeWhitespace();
 
-            result->value = CSSCalcBinaryOperation::create(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
-            if (!result->value)
+            if (!parseValueExpression(tokens, depth, &value))
                 return false;
+
+            nodes.append(value.value.releaseNonNull());
         }
 
-        ASSERT(*index <= tokens->size());
-        return true;
+        result->value = CSSCalcOperation::createMinOrMax(op, WTFMove(nodes), m_destinationCategory);
+        return result->value;
     }
 
-    bool parseValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
+    bool parseValueExpression(CSSParserTokenRange& tokens, int depth, Value* result)
     {
-        return parseAdditiveValueExpression(tokens, depth, index, result);
+        return parseAdditiveValueExpression(tokens, depth, result);
     }
+
+    CalculationCategory m_destinationCategory;
 };
 
-PassRefPtr<CSSCalcValue> CSSCalcValue::create(CSSParserString name, CSSParserValueList* parserValueList)
-{    
-    CSSCalcExpressionNodeParser parser;    
-    RefPtr<CSSCalcExpressionNode> expression;
-    
-    if (equalIgnoringCase(name, "-webkit-calc("))
-        expression = parser.parseCalc(parserValueList);    
-    // FIXME calc (http://webkit.org/b/16662) Add parsing for min and max here
+static inline RefPtr<CSSCalcOperation> createBlendHalf(const Length& length, const RenderStyle& style, float progress)
+{
+    return CSSCalcOperation::create(CalcOperator::Multiply, createCSS(length, style),
+        CSSCalcPrimitiveValue::create(CSSPrimitiveValue::create(progress, CSSPrimitiveValue::CSS_NUMBER), !progress || progress == 1));
+}
+
+static RefPtr<CSSCalcExpressionNode> createCSS(const CalcExpressionNode& node, const RenderStyle& style)
+{
+    switch (node.type()) {
+    case CalcExpressionNodeType::Number: {
+        float value = toCalcExpressionNumber(node).value();
+        return CSSCalcPrimitiveValue::create(CSSPrimitiveValue::create(value, CSSPrimitiveValue::CSS_NUMBER), value == std::trunc(value));
+    }
+    case CalcExpressionNodeType::Length:
+        return createCSS(toCalcExpressionLength(node).length(), style);
+    case CalcExpressionNodeType::Operation: {
+        auto& operationNode = toCalcExpressionOperation(node);
+        auto& operationChildren = operationNode.children();
+        CalcOperator op = operationNode.getOperator();
+        if (op == CalcOperator::Min || op == CalcOperator::Max) {
+            Vector<Ref<CSSCalcExpressionNode>> values;
+            values.reserveInitialCapacity(operationChildren.size());
+            for (auto& child : operationChildren) {
+                auto cssNode = createCSS(*child, style);
+                if (!cssNode)
+                    return nullptr;
+                values.uncheckedAppend(*cssNode);
+            }
+            return CSSCalcOperation::createMinOrMax(operationNode.getOperator(), WTFMove(values), CalculationCategory::Other);
+        }
+
+        if (operationChildren.size() == 2)
+            return CSSCalcOperation::create(operationNode.getOperator(), createCSS(*operationChildren[0], style), createCSS(*operationChildren[1], style));
+
+        return nullptr;
+    }
+    case CalcExpressionNodeType::BlendLength: {
+        // FIXME: (http://webkit.org/b/122036) Create a CSSCalcExpressionNode equivalent of CalcExpressionBlendLength.
+        auto& blend = toCalcExpressionBlendLength(node);
+        float progress = blend.progress();
+        return CSSCalcOperation::create(CalcOperator::Add, createBlendHalf(blend.from(), style, 1 - progress), createBlendHalf(blend.to(), style, progress));
+    }
+    case CalcExpressionNodeType::Undefined:
+        ASSERT_NOT_REACHED();
+    }
+    return nullptr;
+}
 
-    return expression ? adoptRef(new CSSCalcValue(expression)) : 0;
+static RefPtr<CSSCalcExpressionNode> createCSS(const Length& length, const RenderStyle& style)
+{
+    switch (length.type()) {
+    case Percent:
+    case Fixed:
+        return CSSCalcPrimitiveValue::create(CSSPrimitiveValue::create(length, style), length.value() == trunc(length.value()));
+    case Calculated:
+        return createCSS(length.calculationValue().expression(), style);
+    case Auto:
+    case Intrinsic:
+    case MinIntrinsic:
+    case MinContent:
+    case MaxContent:
+    case FillAvailable:
+    case FitContent:
+    case Relative:
+    case Undefined:
+        ASSERT_NOT_REACHED();
+    }
+    return nullptr;
+}
+
+RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range)
+{
+    CSSCalcExpressionNodeParser parser(destinationCategory);
+    auto expression = parser.parseCalc(tokens, function);
+    if (!expression)
+        return nullptr;
+    return adoptRef(new CSSCalcValue(expression.releaseNonNull(), range != ValueRangeAll));
+}
+    
+RefPtr<CSSCalcValue> CSSCalcValue::create(const CalculationValue& value, const RenderStyle& style)
+{
+    auto expression = createCSS(value.expression(), style);
+    if (!expression)
+        return nullptr;
+    return adoptRef(new CSSCalcValue(expression.releaseNonNull(), value.shouldClampToNonNegative()));
 }
 
 } // namespace WebCore