[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / WebCore / css / CSSCalculationValue.cpp
index 5d563b3..a42f564 100644 (file)
@@ -32,6 +32,8 @@
 #include "config.h"
 #include "CSSCalculationValue.h"
 
+#include "CSSParser.h"
+#include "CSSParserTokenRange.h"
 #include "CSSPrimitiveValueMappings.h"
 #include "StyleResolver.h"
 #include <wtf/MathExtras.h>
@@ -50,12 +52,11 @@ namespace WebCore {
 static RefPtr<CSSCalcExpressionNode> createCSS(const CalcExpressionNode&, const RenderStyle&);
 static RefPtr<CSSCalcExpressionNode> createCSS(const Length&, const RenderStyle&);
 
-static CalculationCategory unitCategory(CSSPrimitiveValue::UnitTypes type)
+static CalculationCategory unitCategory(CSSPrimitiveValue::UnitType type)
 {
     switch (type) {
     case CSSPrimitiveValue::CSS_NUMBER:
-    case CSSPrimitiveValue::CSS_PARSER_INTEGER:
-        return CalcNumber;
+        return CalculationCategory::Number;
     case CSSPrimitiveValue::CSS_EMS:
     case CSSPrimitiveValue::CSS_EXS:
     case CSSPrimitiveValue::CSS_PX:
@@ -70,31 +71,30 @@ static CalculationCategory unitCategory(CSSPrimitiveValue::UnitTypes type)
     case CSSPrimitiveValue::CSS_VH:
     case CSSPrimitiveValue::CSS_VMIN:
     case CSSPrimitiveValue::CSS_VMAX:
-        return CalcLength;
+        return CalculationCategory::Length;
     case CSSPrimitiveValue::CSS_PERCENTAGE:
-        return CalcPercent;
+        return CalculationCategory::Percent;
     case CSSPrimitiveValue::CSS_DEG:
     case CSSPrimitiveValue::CSS_RAD:
     case CSSPrimitiveValue::CSS_GRAD:
     case CSSPrimitiveValue::CSS_TURN:
-        return CalcAngle;
+        return CalculationCategory::Angle;
     case CSSPrimitiveValue::CSS_MS:
     case CSSPrimitiveValue::CSS_S:
-        return CalcTime;
+        return CalculationCategory::Time;
     case CSSPrimitiveValue::CSS_HZ:
     case CSSPrimitiveValue::CSS_KHZ:
-        return CalcFrequency;
+        return CalculationCategory::Frequency;
     default:
-        return CalcOther;
+        return CalculationCategory::Other;
     }
 }
 
-static bool hasDoubleValue(CSSPrimitiveValue::UnitTypes type)
+static bool hasDoubleValue(CSSPrimitiveValue::UnitType type)
 {
     switch (type) {
     case CSSPrimitiveValue::CSS_FR:
     case CSSPrimitiveValue::CSS_NUMBER:
-    case CSSPrimitiveValue::CSS_PARSER_INTEGER:
     case CSSPrimitiveValue::CSS_PERCENTAGE:
     case CSSPrimitiveValue::CSS_EMS:
     case CSSPrimitiveValue::CSS_EXS:
@@ -134,24 +134,15 @@ static bool hasDoubleValue(CSSPrimitiveValue::UnitTypes type)
     case CSSPrimitiveValue::CSS_RGBCOLOR:
     case CSSPrimitiveValue::CSS_PAIR:
     case CSSPrimitiveValue::CSS_UNICODE_RANGE:
-    case CSSPrimitiveValue::CSS_PARSER_OPERATOR:
-    case CSSPrimitiveValue::CSS_PARSER_HEXCOLOR:
-    case CSSPrimitiveValue::CSS_PARSER_IDENTIFIER:
-    case CSSPrimitiveValue::CSS_PARSER_WHITESPACE:
     case CSSPrimitiveValue::CSS_COUNTER_NAME:
     case CSSPrimitiveValue::CSS_SHAPE:
     case CSSPrimitiveValue::CSS_QUAD:
-#if ENABLE(CSS_SCROLL_SNAP)
-    case CSSPrimitiveValue::CSS_LENGTH_REPEAT:
-#endif
+    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:
-#if ENABLE(DASHBOARD_SUPPORT)
-    case CSSPrimitiveValue::CSS_DASHBOARD_REGION:
-#endif
         return false;
     };
     ASSERT_NOT_REACHED();
@@ -199,77 +190,75 @@ double CSSCalcValue::computeLengthPx(const CSSToLengthConversionData& conversion
 class CSSCalcPrimitiveValue final : public CSSCalcExpressionNode {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    static Ref<CSSCalcPrimitiveValue> create(PassRefPtr<CSSPrimitiveValue> value, bool isInteger)
+    static Ref<CSSCalcPrimitiveValue> create(Ref<CSSPrimitiveValue>&& value, bool isInteger)
     {
-        return adoptRef(*new CSSCalcPrimitiveValue(value, isInteger));
+        return adoptRef(*new CSSCalcPrimitiveValue(WTFMove(value), isInteger));
     }
 
-    static RefPtr<CSSCalcPrimitiveValue> create(double value, CSSPrimitiveValue::UnitTypes type, bool isInteger)
+    static RefPtr<CSSCalcPrimitiveValue> create(double value, CSSPrimitiveValue::UnitType type, bool isInteger)
     {
-        if (std::isnan(value) || std::isinf(value))
+        if (!std::isfinite(value))
             return nullptr;
         return adoptRef(new CSSCalcPrimitiveValue(CSSPrimitiveValue::create(value, type), isInteger));
     }
 
 private:
-    virtual bool isZero() const override
+    bool isZero() const final
     {
-        return !m_value->getDoubleValue();
+        return !m_value->doubleValue();
     }
 
-    virtual String customCSSText() const override
+    String customCSSText() const final
     {
         return m_value->cssText();
     }
 
-    virtual std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData& conversionData) const override
+    std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData& conversionData) const final
     {
         switch (category()) {
-        case CalcNumber:
-            return std::make_unique<CalcExpressionNumber>(m_value->getFloatValue());
-        case CalcLength:
-            return std::make_unique<CalcExpressionLength>(Length(m_value->computeLength<float>(conversionData), WebCore::Fixed));
-        case CalcPercent:
-        case CalcPercentLength: {
-            CSSPrimitiveValue* primitiveValue = m_value.get();
-            return std::make_unique<CalcExpressionLength>(primitiveValue
-                ? primitiveValue->convertToLength<FixedFloatConversion | PercentConversion>(conversionData) : Length(Undefined));
+        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. CalcPercentNumber makes no sense as a Length.
-        case CalcPercentNumber:
-        case CalcAngle:
-        case CalcTime:
-        case CalcFrequency:
-        case CalcOther:
+        // 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;
     }
 
-    virtual double doubleValue() const override
+    double doubleValue() const final
     {
         if (hasDoubleValue(primitiveType()))
-            return m_value->getDoubleValue();
+            return m_value->doubleValue();
         ASSERT_NOT_REACHED();
         return 0;
     }
 
-    virtual double computeLengthPx(const CSSToLengthConversionData& conversionData) const override
+    double computeLengthPx(const CSSToLengthConversionData& conversionData) const final
     {
         switch (category()) {
-        case CalcLength:
+        case CalculationCategory::Length:
             return m_value->computeLength<double>(conversionData);
-        case CalcPercent:
-        case CalcNumber:
-            return m_value->getDoubleValue();
-        case CalcPercentLength:
-        case CalcPercentNumber:
-        case CalcAngle:
-        case CalcTime:
-        case CalcFrequency:
-        case CalcOther:
+        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;
         }
@@ -277,454 +266,688 @@ private:
         return 0;
     }
 
-    virtual bool equals(const CSSCalcExpressionNode& other) const override
+    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 compareCSSValuePtr(m_value, static_cast<const CSSCalcPrimitiveValue&>(other).m_value);
+        return compareCSSValue(m_value, static_cast<const CSSCalcPrimitiveValue&>(other).m_value);
     }
 
-    virtual Type type() const override { return CssCalcPrimitiveValue; }
-    virtual CSSPrimitiveValue::UnitTypes primitiveType() const override
+    Type type() const final { return CssCalcPrimitiveValue; }
+    CSSPrimitiveValue::UnitType primitiveType() const final
     {
-        return CSSPrimitiveValue::UnitTypes(m_value->primitiveType());
+        return CSSPrimitiveValue::UnitType(m_value->primitiveType());
     }
 
 private:
-    explicit CSSCalcPrimitiveValue(PassRefPtr<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[CalcAngle][CalcAngle] = {
-//    CalcNumber         CalcLength         CalcPercent        CalcPercentNumber  CalcPercentLength
-    { CalcNumber,        CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther }, //         CalcNumber
-    { CalcOther,         CalcLength,        CalcPercentLength, CalcOther,         CalcPercentLength }, // CalcLength
-    { CalcPercentNumber, CalcPercentLength, CalcPercent,       CalcPercentNumber, CalcPercentLength }, // CalcPercent
-    { CalcPercentNumber, CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther }, //         CalcPercentNumber
-    { CalcOther,         CalcPercentLength, CalcPercentLength, CalcOther,         CalcPercentLength }, // CalcPercentLength
+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 < CalcOther);
-    ASSERT(rightCategory < CalcOther);
+    ASSERT(leftCategory < CalculationCategory::Other);
+    ASSERT(rightCategory < CalculationCategory::Other);
 
     switch (op) {
-    case CalcAdd:
-    case CalcSubtract:
-        if (leftCategory < CalcAngle && rightCategory < CalcAngle)
-            return addSubtractResult[leftCategory][rightCategory];
+    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 CalcOther;
-    case CalcMultiply:
-        if (leftCategory != CalcNumber && rightCategory != CalcNumber)
-            return CalcOther;
-        return leftCategory == CalcNumber ? rightCategory : leftCategory;
-    case CalcDivide:
-        if (rightCategory != CalcNumber || rightSide.isZero())
-            return CalcOther;
+        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 CalcOther;
+    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 != CalcDivide && leftSide.isInteger() && rightSide.isInteger();
+    return op != CalcOperator::Divide && leftSide.isInteger() && rightSide.isInteger();
 }
 
-class CSSCalcBinaryOperation final : public CSSCalcExpressionNode {
+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 RefPtr<CSSCalcBinaryOperation> create(CalcOperator op, PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide)
+    static RefPtr<CSSCalcOperation> create(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
     {
-        ASSERT(leftSide->category() < CalcOther);
-        ASSERT(rightSide->category() < CalcOther);
+        if (!leftSide || !rightSide)
+            return nullptr;
 
-        CalculationCategory newCategory = determineCategory(*leftSide, *rightSide, op);
+        ASSERT(leftSide->category() < CalculationCategory::Other);
+        ASSERT(rightSide->category() < CalculationCategory::Other);
 
-        if (newCategory == CalcOther)
+        auto newCategory = determineCategory(*leftSide, *rightSide, op);
+        if (newCategory == CalculationCategory::Other)
             return nullptr;
 
-        return adoptRef(new CSSCalcBinaryOperation(newCategory, op, leftSide, rightSide));
+        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, PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide)
+    static RefPtr<CSSCalcExpressionNode> createSimplified(CalcOperator op, RefPtr<CSSCalcExpressionNode>&& leftSide, RefPtr<CSSCalcExpressionNode>&& rightSide)
     {
-        CalculationCategory leftCategory = leftSide->category();
-        CalculationCategory rightCategory = rightSide->category();
-        ASSERT(leftCategory < CalcOther);
-        ASSERT(rightCategory < CalcOther);
+        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 == CalcNumber && rightCategory == CalcNumber) {
-            CSSPrimitiveValue::UnitTypes evaluationType = isInteger ? CSSPrimitiveValue::CSS_PARSER_INTEGER : CSSPrimitiveValue::CSS_NUMBER;
-            return CSSCalcPrimitiveValue::create(evaluateOperator(op, leftSide->doubleValue(), rightSide->doubleValue()), evaluationType, isInteger);
+        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 == CalcAdd || op == CalcSubtract) {
+        if (op == CalcOperator::Add || op == CalcOperator::Subtract) {
             if (leftCategory == rightSide->category()) {
-                CSSPrimitiveValue::UnitTypes leftType = leftSide->primitiveType();
+                CSSPrimitiveValue::UnitType leftType = leftSide->primitiveType();
                 if (hasDoubleValue(leftType)) {
-                    CSSPrimitiveValue::UnitTypes rightType = rightSide->primitiveType();
+                    CSSPrimitiveValue::UnitType rightType = rightSide->primitiveType();
                     if (leftType == rightType)
-                        return CSSCalcPrimitiveValue::create(evaluateOperator(op, leftSide->doubleValue(), rightSide->doubleValue()), leftType, isInteger);
+                        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::UnitTypes canonicalType = CSSPrimitiveValue::canonicalUnitTypeForCategory(leftUnitCategory);
+                        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);
+                            return CSSCalcPrimitiveValue::create(evaluateOperator(op, { leftValue, rightValue }), canonicalType, isInteger);
                         }
                     }
                 }
             }
         } else {
             // Simplify multiplying or dividing by a number for simplifiable types.
-            ASSERT(op == CalcMultiply || op == CalcDivide);
-            CSSCalcExpressionNode* numberSide = getNumberSide(*leftSide, *rightSide);
+            ASSERT(op == CalcOperator::Multiply || op == CalcOperator::Divide);
+            auto* numberSide = getNumberSide(*leftSide, *rightSide);
             if (!numberSide)
-                return create(op, leftSide, rightSide);
-            if (numberSide == leftSide && op == CalcDivide)
+                return create(op, leftSide.releaseNonNull(), rightSide.releaseNonNull());
+            if (numberSide == leftSide && op == CalcOperator::Divide)
                 return nullptr;
-            CSSCalcExpressionNode* otherSide = leftSide == numberSide ? rightSide.get() : leftSide.get();
+            auto& otherSide = leftSide == numberSide ? *rightSide : *leftSide;
 
             double number = numberSide->doubleValue();
-            if (std::isnan(number) || std::isinf(number))
+            if (!std::isfinite(number))
                 return nullptr;
-            if (op == CalcDivide && !number)
+            if (op == CalcOperator::Divide && !number)
                 return nullptr;
 
-            CSSPrimitiveValue::UnitTypes otherType = otherSide->primitiveType();
+            auto otherType = otherSide.primitiveType();
             if (hasDoubleValue(otherType))
-                return CSSCalcPrimitiveValue::create(evaluateOperator(op, otherSide->doubleValue(), number), otherType, isInteger);
+                return CSSCalcPrimitiveValue::create(evaluateOperator(op, { otherSide.doubleValue(), number }), otherType, isInteger);
         }
 
-        return create(op, leftSide, rightSide);
+        return create(op, leftSide.releaseNonNull(), rightSide.releaseNonNull());
     }
 
 private:
-    virtual bool isZero() const override
+    bool isZero() const final
     {
         return !doubleValue();
     }
 
-    virtual std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData& conversionData) const override
+    std::unique_ptr<CalcExpressionNode> createCalcExpression(const CSSToLengthConversionData& conversionData) const final
     {
-        std::unique_ptr<CalcExpressionNode> left(m_leftSide->createCalcExpression(conversionData));
-        if (!left)
-            return nullptr;
-        std::unique_ptr<CalcExpressionNode> right(m_rightSide->createCalcExpression(conversionData));
-        if (!right)
-            return nullptr;
-        return std::make_unique<CalcExpressionBinaryOperation>(WTFMove(left), WTFMove(right), m_operator);
+        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);
     }
 
-    virtual double doubleValue() const override
+    double doubleValue() const final
     {
-        return evaluate(m_leftSide->doubleValue(), m_rightSide->doubleValue());
+        Vector<double> doubleValues;
+        for (auto& child : m_children)
+            doubleValues.append(child->doubleValue());
+        return evaluate(doubleValues);
     }
 
-    virtual double computeLengthPx(const CSSToLengthConversionData& conversionData) const override
+    double computeLengthPx(const CSSToLengthConversionData& conversionData) const final
     {
-        const double leftValue = m_leftSide->computeLengthPx(conversionData);
-        const double rightValue = m_rightSide->computeLengthPx(conversionData);
-        return evaluate(leftValue, rightValue);
+        Vector<double> doubleValues;
+        for (auto& child : m_children)
+            doubleValues.append(child->computeLengthPx(conversionData));
+        return evaluate(doubleValues);
     }
 
-    static String buildCssText(const String& leftExpression, const String& rightExpression, CalcOperator op)
+    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('(');
-        result.append(leftExpression);
-        result.append(' ');
-        result.append(static_cast<char>(op));
-        result.append(' ');
-        result.append(rightExpression);
+        switch (op) {
+        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(')');
+        }
         result.append(')');
 
         return result.toString();
     }
 
-    virtual String customCSSText() const override
+    String customCSSText() const final
     {
-        return buildCssText(m_leftSide->customCSSText(), m_rightSide->customCSSText(), m_operator);
+        Vector<String> cssTexts;
+        for (auto& child : m_children)
+            cssTexts.append(child->customCSSText());
+        return buildCssText(cssTexts, m_operator);
     }
 
-    virtual bool equals(const CSSCalcExpressionNode& exp) const override
+    bool equals(const CSSCalcExpressionNode& exp) const final
     {
         if (type() != exp.type())
             return false;
 
-        const CSSCalcBinaryOperation& other = static_cast<const CSSCalcBinaryOperation&>(exp);
-        return compareCSSValuePtr(m_leftSide, other.m_leftSide)
-            && compareCSSValuePtr(m_rightSide, other.m_rightSide)
-            && m_operator == other.m_operator;
+        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;
     }
 
-    virtual Type type() const override { return CssCalcBinaryOperation; }
+    Type type() const final { return CssCalcOperation; }
 
-    virtual CSSPrimitiveValue::UnitTypes primitiveType() const override
+    CSSPrimitiveValue::UnitType primitiveType() const final
     {
         switch (category()) {
-        case CalcNumber:
-            ASSERT(m_leftSide->category() == CalcNumber && m_rightSide->category() == CalcNumber);
-            if (isInteger())
-                return CSSPrimitiveValue::CSS_PARSER_INTEGER;
+        case CalculationCategory::Number:
+#if !ASSERT_DISABLED
+            for (auto& child : m_children)
+                ASSERT(child->category() == CalculationCategory::Number);
+#endif
             return CSSPrimitiveValue::CSS_NUMBER;
-        case CalcLength:
-        case CalcPercent: {
-            if (m_leftSide->category() == CalcNumber)
-                return m_rightSide->primitiveType();
-            if (m_rightSide->category() == CalcNumber)
-                return m_leftSide->primitiveType();
-            CSSPrimitiveValue::UnitTypes leftType = m_leftSide->primitiveType();
-            if (leftType == m_rightSide->primitiveType())
-                return leftType;
-            return CSSPrimitiveValue::CSS_UNKNOWN;
+        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 CalcAngle:
+        case CalculationCategory::Angle:
             return CSSPrimitiveValue::CSS_DEG;
-        case CalcTime:
+        case CalculationCategory::Time:
             return CSSPrimitiveValue::CSS_MS;
-        case CalcFrequency:
+        case CalculationCategory::Frequency:
             return CSSPrimitiveValue::CSS_HZ;
-        case CalcPercentLength:
-        case CalcPercentNumber:
-        case CalcOther:
+        case CalculationCategory::PercentLength:
+        case CalculationCategory::PercentNumber:
+        case CalculationCategory::Other:
             return CSSPrimitiveValue::CSS_UNKNOWN;
         }
         ASSERT_NOT_REACHED();
         return CSSPrimitiveValue::CSS_UNKNOWN;
     }
 
-    CSSCalcBinaryOperation(CalculationCategory category, CalcOperator op, PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide)
-        : CSSCalcExpressionNode(category, isIntegerResult(op, *leftSide, *rightSide))
-        , m_leftSide(leftSide)
-        , m_rightSide(rightSide)
+    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));
+    }
+
+    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() == CalcNumber)
+        if (leftSide.category() == CalculationCategory::Number)
             return &leftSide;
-        if (rightSide.category() == CalcNumber)
+        if (rightSide.category() == CalculationCategory::Number)
             return &rightSide;
         return nullptr;
     }
 
-    double evaluate(double leftSide, double rightSide) const
+    double evaluate(const Vector<double>& children) const
     {
-        return evaluateOperator(m_operator, leftSide, rightSide);
+        return evaluateOperator(m_operator, children);
     }
 
-    static double evaluateOperator(CalcOperator op, double leftValue, double rightValue)
+    static double evaluateOperator(CalcOperator op, const Vector<double>& children)
     {
         switch (op) {
-        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 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:
-    RefPtr<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_WITH_SECURITY_IMPLICATION(index <= tokens->size());
-        if (!ok || index != tokens->size())
+        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 (!is<CSSPrimitiveValue>(value.get()))
+        
+        CSSPrimitiveValue::UnitType type = token.unitType();
+        if (unitCategory(type) == CalculationCategory::Other)
             return false;
-
-        CSSPrimitiveValue& primitiveValue = downcast<CSSPrimitiveValue>(*value);
-        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(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
+    
+    bool parseValueTerm(CSSParserTokenRange& tokens, int depth, Value* result)
     {
-        if (checkDepthAndIndex(&depth, *index, tokens) != OK)
+        if (checkDepthAndIndex(&depth, tokens) != OK)
             return false;
 
-        if (operatorValue(tokens, *index) == '(') {
-            unsigned currentIndex = *index + 1;
-            if (!parseValueExpression(tokens, depth, &currentIndex, 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, tokens) != OK)
+            return false;
+        
+        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)
+        if (checkDepthAndIndex(&depth, tokens) != OK)
             return false;
-
-        if (!parseValueTerm(tokens, depth, index, result))
+        
+        if (!parseValueMultiplicativeExpression(tokens, depth, result))
             return false;
-
-        while (*index < tokens->size() - 1) {
-            char operatorCharacter = operatorValue(tokens, *index);
-            if (operatorCharacter != CalcMultiply && operatorCharacter != CalcDivide)
+        
+        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::createSimplified(static_cast<CalcOperator>(operatorCharacter), result->value, rhs.value);
+            
+            result->value = CSSCalcOperation::createSimplified(static_cast<CalcOperator>(operatorCharacter), WTFMove(result->value), WTFMove(rhs.value));
             if (!result->value)
                 return false;
         }
-
-        ASSERT_WITH_SECURITY_IMPLICATION(*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)
+        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::createSimplified(static_cast<CalcOperator>(operatorCharacter), result->value, rhs.value);
-            if (!result->value)
+            if (!parseValueExpression(tokens, depth, &value))
                 return false;
+
+            nodes.append(value.value.releaseNonNull());
         }
 
-        ASSERT_WITH_SECURITY_IMPLICATION(*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;
 };
 
-static inline RefPtr<CSSCalcBinaryOperation> createBlendHalf(const Length& length, const RenderStyle& style, float progress)
+static inline RefPtr<CSSCalcOperation> createBlendHalf(const Length& length, const RenderStyle& style, float progress)
 {
-    return CSSCalcBinaryOperation::create(CalcMultiply, createCSS(length, style),
+    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 CalcExpressionNodeNumber: {
+    case CalcExpressionNodeType::Number: {
         float value = toCalcExpressionNumber(node).value();
-        return CSSCalcPrimitiveValue::create(CSSPrimitiveValue::create(value, CSSPrimitiveValue::CSS_NUMBER), value == truncf(value));
+        return CSSCalcPrimitiveValue::create(CSSPrimitiveValue::create(value, CSSPrimitiveValue::CSS_NUMBER), value == std::trunc(value));
     }
-    case CalcExpressionNodeLength:
+    case CalcExpressionNodeType::Length:
         return createCSS(toCalcExpressionLength(node).length(), style);
-    case CalcExpressionNodeBinaryOperation: {
-        auto& binaryNode = toCalcExpressionBinaryOperation(node);
-        return CSSCalcBinaryOperation::create(binaryNode.getOperator(), createCSS(binaryNode.leftSide(), style), createCSS(binaryNode.rightSide(), 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 CalcExpressionNodeBlendLength: {
+    case CalcExpressionNodeType::BlendLength: {
         // FIXME: (http://webkit.org/b/122036) Create a CSSCalcExpressionNode equivalent of CalcExpressionBlendLength.
         auto& blend = toCalcExpressionBlendLength(node);
         float progress = blend.progress();
-        return CSSCalcBinaryOperation::create(CalcAdd, createBlendHalf(blend.from(), style, 1 - progress), createBlendHalf(blend.to(), style, progress));
+        return CSSCalcOperation::create(CalcOperator::Add, createBlendHalf(blend.from(), style, 1 - progress), createBlendHalf(blend.to(), style, progress));
     }
-    case CalcExpressionNodeUndefined:
+    case CalcExpressionNodeType::Undefined:
         ASSERT_NOT_REACHED();
-        return nullptr;
     }
-    ASSERT_NOT_REACHED();
     return nullptr;
 }
 
@@ -746,26 +969,22 @@ static RefPtr<CSSCalcExpressionNode> createCSS(const Length& length, const Rende
     case Relative:
     case Undefined:
         ASSERT_NOT_REACHED();
-        return nullptr;
     }
-    ASSERT_NOT_REACHED();
     return nullptr;
 }
 
-RefPtr<CSSCalcValue> CSSCalcValue::create(CSSParserString name, CSSParserValueList& parserValueList, CalculationPermittedValueRange range)
+RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range)
 {
-    CSSCalcExpressionNodeParser parser;
-    RefPtr<CSSCalcExpressionNode> expression;
-
-    if (equalLettersIgnoringASCIICase(name, "calc(") || equalLettersIgnoringASCIICase(name, "-webkit-calc("))
-        expression = parser.parseCalc(&parserValueList);
-
-    return expression ? adoptRef(new CSSCalcValue(expression.releaseNonNull(), range != CalculationRangeAll)) : nullptr;
+    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)
 {
-    RefPtr<CSSCalcExpressionNode> expression = createCSS(value.expression(), style);
+    auto expression = createCSS(value.expression(), style);
     if (!expression)
         return nullptr;
     return adoptRef(new CSSCalcValue(expression.releaseNonNull(), value.shouldClampToNonNegative()));