Home | History | Annotate | Download | only in css
      1 /*
      2  * Copyright (C) 2011, 2012 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "core/css/CSSCalculationValue.h"
     33 
     34 #include "core/css/CSSPrimitiveValueMappings.h"
     35 #include "core/css/resolver/StyleResolver.h"
     36 #include "wtf/MathExtras.h"
     37 #include "wtf/OwnPtr.h"
     38 #include "wtf/text/StringBuilder.h"
     39 
     40 static const int maxExpressionDepth = 100;
     41 
     42 enum ParseState {
     43     OK,
     44     TooDeep,
     45     NoMoreTokens
     46 };
     47 
     48 namespace WebCore {
     49 
     50 static CalculationCategory unitCategory(CSSPrimitiveValue::UnitType type)
     51 {
     52     switch (type) {
     53     case CSSPrimitiveValue::CSS_NUMBER:
     54     case CSSPrimitiveValue::CSS_PARSER_INTEGER:
     55         return CalcNumber;
     56     case CSSPrimitiveValue::CSS_PERCENTAGE:
     57         return CalcPercent;
     58     case CSSPrimitiveValue::CSS_EMS:
     59     case CSSPrimitiveValue::CSS_EXS:
     60     case CSSPrimitiveValue::CSS_PX:
     61     case CSSPrimitiveValue::CSS_CM:
     62     case CSSPrimitiveValue::CSS_MM:
     63     case CSSPrimitiveValue::CSS_IN:
     64     case CSSPrimitiveValue::CSS_PT:
     65     case CSSPrimitiveValue::CSS_PC:
     66     case CSSPrimitiveValue::CSS_REMS:
     67     case CSSPrimitiveValue::CSS_CHS:
     68     case CSSPrimitiveValue::CSS_VW:
     69     case CSSPrimitiveValue::CSS_VH:
     70     case CSSPrimitiveValue::CSS_VMIN:
     71     case CSSPrimitiveValue::CSS_VMAX:
     72         return CalcLength;
     73     // FIXME: Support angle, time and frequency units.
     74     // http://www.w3.org/TR/css3-values/#calc-notation
     75     default:
     76         return CalcOther;
     77     }
     78 }
     79 
     80 static bool hasDoubleValue(CSSPrimitiveValue::UnitType type)
     81 {
     82     switch (type) {
     83     case CSSPrimitiveValue::CSS_NUMBER:
     84     case CSSPrimitiveValue::CSS_PARSER_INTEGER:
     85     case CSSPrimitiveValue::CSS_PERCENTAGE:
     86     case CSSPrimitiveValue::CSS_EMS:
     87     case CSSPrimitiveValue::CSS_EXS:
     88     case CSSPrimitiveValue::CSS_CHS:
     89     case CSSPrimitiveValue::CSS_REMS:
     90     case CSSPrimitiveValue::CSS_PX:
     91     case CSSPrimitiveValue::CSS_CM:
     92     case CSSPrimitiveValue::CSS_MM:
     93     case CSSPrimitiveValue::CSS_IN:
     94     case CSSPrimitiveValue::CSS_PT:
     95     case CSSPrimitiveValue::CSS_PC:
     96     case CSSPrimitiveValue::CSS_DEG:
     97     case CSSPrimitiveValue::CSS_RAD:
     98     case CSSPrimitiveValue::CSS_GRAD:
     99     case CSSPrimitiveValue::CSS_MS:
    100     case CSSPrimitiveValue::CSS_S:
    101     case CSSPrimitiveValue::CSS_HZ:
    102     case CSSPrimitiveValue::CSS_KHZ:
    103     case CSSPrimitiveValue::CSS_DIMENSION:
    104     case CSSPrimitiveValue::CSS_VW:
    105     case CSSPrimitiveValue::CSS_VH:
    106     case CSSPrimitiveValue::CSS_VMIN:
    107     case CSSPrimitiveValue::CSS_VMAX:
    108     case CSSPrimitiveValue::CSS_DPPX:
    109     case CSSPrimitiveValue::CSS_DPI:
    110     case CSSPrimitiveValue::CSS_DPCM:
    111     case CSSPrimitiveValue::CSS_FR:
    112         return true;
    113     case CSSPrimitiveValue::CSS_UNKNOWN:
    114     case CSSPrimitiveValue::CSS_STRING:
    115     case CSSPrimitiveValue::CSS_URI:
    116     case CSSPrimitiveValue::CSS_IDENT:
    117     case CSSPrimitiveValue::CSS_ATTR:
    118     case CSSPrimitiveValue::CSS_COUNTER:
    119     case CSSPrimitiveValue::CSS_RECT:
    120     case CSSPrimitiveValue::CSS_RGBCOLOR:
    121     case CSSPrimitiveValue::CSS_PAIR:
    122     case CSSPrimitiveValue::CSS_UNICODE_RANGE:
    123     case CSSPrimitiveValue::CSS_PARSER_OPERATOR:
    124     case CSSPrimitiveValue::CSS_PARSER_HEXCOLOR:
    125     case CSSPrimitiveValue::CSS_PARSER_IDENTIFIER:
    126     case CSSPrimitiveValue::CSS_TURN:
    127     case CSSPrimitiveValue::CSS_COUNTER_NAME:
    128     case CSSPrimitiveValue::CSS_SHAPE:
    129     case CSSPrimitiveValue::CSS_QUAD:
    130     case CSSPrimitiveValue::CSS_CALC:
    131     case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER:
    132     case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH:
    133     case CSSPrimitiveValue::CSS_PROPERTY_ID:
    134     case CSSPrimitiveValue::CSS_VALUE_ID:
    135         return false;
    136     };
    137     ASSERT_NOT_REACHED();
    138     return false;
    139 }
    140 
    141 static String buildCSSText(const String& expression)
    142 {
    143     StringBuilder result;
    144     result.append("calc");
    145     bool expressionHasSingleTerm = expression[0] != '(';
    146     if (expressionHasSingleTerm)
    147         result.append('(');
    148     result.append(expression);
    149     if (expressionHasSingleTerm)
    150         result.append(')');
    151     return result.toString();
    152 }
    153 
    154 String CSSCalcValue::customCSSText() const
    155 {
    156     return buildCSSText(m_expression->customCSSText());
    157 }
    158 
    159 bool CSSCalcValue::equals(const CSSCalcValue& other) const
    160 {
    161     return compareCSSValuePtr(m_expression, other.m_expression);
    162 }
    163 
    164 double CSSCalcValue::clampToPermittedRange(double value) const
    165 {
    166     return m_nonNegative && value < 0 ? 0 : value;
    167 }
    168 
    169 double CSSCalcValue::doubleValue() const
    170 {
    171     return clampToPermittedRange(m_expression->doubleValue());
    172 }
    173 
    174 double CSSCalcValue::computeLengthPx(const CSSToLengthConversionData& conversionData) const
    175 {
    176     return clampToPermittedRange(m_expression->computeLengthPx(conversionData));
    177 }
    178 
    179 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CSSCalcExpressionNode)
    180 
    181 class CSSCalcPrimitiveValue FINAL : public CSSCalcExpressionNode {
    182     WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED;
    183 public:
    184 
    185     static PassRefPtrWillBeRawPtr<CSSCalcPrimitiveValue> create(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> value, bool isInteger)
    186     {
    187         return adoptRefWillBeNoop(new CSSCalcPrimitiveValue(value, isInteger));
    188     }
    189 
    190     static PassRefPtrWillBeRawPtr<CSSCalcPrimitiveValue> create(double value, CSSPrimitiveValue::UnitType type, bool isInteger)
    191     {
    192         if (std::isnan(value) || std::isinf(value))
    193             return nullptr;
    194         return adoptRefWillBeNoop(new CSSCalcPrimitiveValue(CSSPrimitiveValue::create(value, type).get(), isInteger));
    195     }
    196 
    197     virtual bool isZero() const OVERRIDE
    198     {
    199         return !m_value->getDoubleValue();
    200     }
    201 
    202     virtual String customCSSText() const OVERRIDE
    203     {
    204         return m_value->cssText();
    205     }
    206 
    207     virtual void accumulatePixelsAndPercent(const CSSToLengthConversionData& conversionData, PixelsAndPercent& value, float multiplier) const OVERRIDE
    208     {
    209         switch (m_category) {
    210         case CalcLength:
    211             value.pixels += m_value->computeLength<float>(conversionData) * multiplier;
    212             break;
    213         case CalcPercent:
    214             ASSERT(m_value->isPercentage());
    215             value.percent += m_value->getDoubleValue() * multiplier;
    216             break;
    217         default:
    218             ASSERT_NOT_REACHED();
    219         }
    220     }
    221 
    222     virtual double doubleValue() const OVERRIDE
    223     {
    224         if (hasDoubleValue(primitiveType()))
    225             return m_value->getDoubleValue();
    226         ASSERT_NOT_REACHED();
    227         return 0;
    228     }
    229 
    230     virtual double computeLengthPx(const CSSToLengthConversionData& conversionData) const OVERRIDE
    231     {
    232         switch (m_category) {
    233         case CalcLength:
    234             return m_value->computeLength<double>(conversionData);
    235         case CalcPercent:
    236         case CalcNumber:
    237             return m_value->getDoubleValue();
    238         case CalcPercentLength:
    239         case CalcPercentNumber:
    240         case CalcOther:
    241             ASSERT_NOT_REACHED();
    242             break;
    243         }
    244         ASSERT_NOT_REACHED();
    245         return 0;
    246     }
    247 
    248     virtual void accumulateLengthArray(CSSLengthArray& lengthArray, double multiplier) const
    249     {
    250         ASSERT(category() != CalcNumber);
    251         m_value->accumulateLengthArray(lengthArray, multiplier);
    252     }
    253 
    254     virtual bool equals(const CSSCalcExpressionNode& other) const OVERRIDE
    255     {
    256         if (type() != other.type())
    257             return false;
    258 
    259         return compareCSSValuePtr(m_value, static_cast<const CSSCalcPrimitiveValue&>(other).m_value);
    260     }
    261 
    262     virtual Type type() const OVERRIDE { return CssCalcPrimitiveValue; }
    263     virtual CSSPrimitiveValue::UnitType primitiveType() const OVERRIDE
    264     {
    265         return m_value->primitiveType();
    266     }
    267 
    268 
    269     virtual void trace(Visitor* visitor)
    270     {
    271         visitor->trace(m_value);
    272         CSSCalcExpressionNode::trace(visitor);
    273     }
    274 
    275 private:
    276     CSSCalcPrimitiveValue(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> value, bool isInteger)
    277         : CSSCalcExpressionNode(unitCategory(value->primitiveType()), isInteger)
    278         , m_value(value)
    279     {
    280     }
    281 
    282     RefPtrWillBeMember<CSSPrimitiveValue> m_value;
    283 };
    284 
    285 static const CalculationCategory addSubtractResult[CalcOther][CalcOther] = {
    286 //                        CalcNumber         CalcLength         CalcPercent        CalcPercentNumber  CalcPercentLength
    287 /* CalcNumber */        { CalcNumber,        CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
    288 /* CalcLength */        { CalcOther,         CalcLength,        CalcPercentLength, CalcOther,         CalcPercentLength },
    289 /* CalcPercent */       { CalcPercentNumber, CalcPercentLength, CalcPercent,       CalcPercentNumber, CalcPercentLength },
    290 /* CalcPercentNumber */ { CalcPercentNumber, CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
    291 /* CalcPercentLength */ { CalcOther,         CalcPercentLength, CalcPercentLength, CalcOther,         CalcPercentLength },
    292 };
    293 
    294 static CalculationCategory determineCategory(const CSSCalcExpressionNode& leftSide, const CSSCalcExpressionNode& rightSide, CalcOperator op)
    295 {
    296     CalculationCategory leftCategory = leftSide.category();
    297     CalculationCategory rightCategory = rightSide.category();
    298 
    299     if (leftCategory == CalcOther || rightCategory == CalcOther)
    300         return CalcOther;
    301 
    302     switch (op) {
    303     case CalcAdd:
    304     case CalcSubtract:
    305         return addSubtractResult[leftCategory][rightCategory];
    306     case CalcMultiply:
    307         if (leftCategory != CalcNumber && rightCategory != CalcNumber)
    308             return CalcOther;
    309         return leftCategory == CalcNumber ? rightCategory : leftCategory;
    310     case CalcDivide:
    311         if (rightCategory != CalcNumber || rightSide.isZero())
    312             return CalcOther;
    313         return leftCategory;
    314     }
    315 
    316     ASSERT_NOT_REACHED();
    317     return CalcOther;
    318 }
    319 
    320 static bool isIntegerResult(const CSSCalcExpressionNode* leftSide, const CSSCalcExpressionNode* rightSide, CalcOperator op)
    321 {
    322     // Not testing for actual integer values.
    323     // Performs W3C spec's type checking for calc integers.
    324     // http://www.w3.org/TR/css3-values/#calc-type-checking
    325     return op != CalcDivide && leftSide->isInteger() && rightSide->isInteger();
    326 }
    327 
    328 class CSSCalcBinaryOperation FINAL : public CSSCalcExpressionNode {
    329 public:
    330     static PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> create(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> leftSide, PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
    331     {
    332         ASSERT(leftSide->category() != CalcOther && rightSide->category() != CalcOther);
    333 
    334         CalculationCategory newCategory = determineCategory(*leftSide, *rightSide, op);
    335         if (newCategory == CalcOther)
    336             return nullptr;
    337 
    338         return adoptRefWillBeNoop(new CSSCalcBinaryOperation(leftSide, rightSide, op, newCategory));
    339     }
    340 
    341     static PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> createSimplified(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> leftSide, PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
    342     {
    343         CalculationCategory leftCategory = leftSide->category();
    344         CalculationCategory rightCategory = rightSide->category();
    345         ASSERT(leftCategory != CalcOther && rightCategory != CalcOther);
    346 
    347         bool isInteger = isIntegerResult(leftSide.get(), rightSide.get(), op);
    348 
    349         // Simplify numbers.
    350         if (leftCategory == CalcNumber && rightCategory == CalcNumber) {
    351             CSSPrimitiveValue::UnitType evaluationType = isInteger ? CSSPrimitiveValue::CSS_PARSER_INTEGER : CSSPrimitiveValue::CSS_NUMBER;
    352             return CSSCalcPrimitiveValue::create(evaluateOperator(leftSide->doubleValue(), rightSide->doubleValue(), op), evaluationType, isInteger);
    353         }
    354 
    355         // Simplify addition and subtraction between same types.
    356         if (op == CalcAdd || op == CalcSubtract) {
    357             if (leftCategory == rightSide->category()) {
    358                 CSSPrimitiveValue::UnitType leftType = leftSide->primitiveType();
    359                 if (hasDoubleValue(leftType)) {
    360                     CSSPrimitiveValue::UnitType rightType = rightSide->primitiveType();
    361                     if (leftType == rightType)
    362                         return CSSCalcPrimitiveValue::create(evaluateOperator(leftSide->doubleValue(), rightSide->doubleValue(), op), leftType, isInteger);
    363                     CSSPrimitiveValue::UnitCategory leftUnitCategory = CSSPrimitiveValue::unitCategory(leftType);
    364                     if (leftUnitCategory != CSSPrimitiveValue::UOther && leftUnitCategory == CSSPrimitiveValue::unitCategory(rightType)) {
    365                         CSSPrimitiveValue::UnitType canonicalType = CSSPrimitiveValue::canonicalUnitTypeForCategory(leftUnitCategory);
    366                         if (canonicalType != CSSPrimitiveValue::CSS_UNKNOWN) {
    367                             double leftValue = leftSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(leftType);
    368                             double rightValue = rightSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(rightType);
    369                             return CSSCalcPrimitiveValue::create(evaluateOperator(leftValue, rightValue, op), canonicalType, isInteger);
    370                         }
    371                     }
    372                 }
    373             }
    374         } else {
    375             // Simplify multiplying or dividing by a number for simplifiable types.
    376             ASSERT(op == CalcMultiply || op == CalcDivide);
    377             CSSCalcExpressionNode* numberSide = getNumberSide(leftSide.get(), rightSide.get());
    378             if (!numberSide)
    379                 return create(leftSide, rightSide, op);
    380             if (numberSide == leftSide && op == CalcDivide)
    381                 return nullptr;
    382             CSSCalcExpressionNode* otherSide = leftSide == numberSide ? rightSide.get() : leftSide.get();
    383 
    384             double number = numberSide->doubleValue();
    385             if (std::isnan(number) || std::isinf(number))
    386                 return nullptr;
    387             if (op == CalcDivide && !number)
    388                 return nullptr;
    389 
    390             CSSPrimitiveValue::UnitType otherType = otherSide->primitiveType();
    391             if (hasDoubleValue(otherType))
    392                 return CSSCalcPrimitiveValue::create(evaluateOperator(otherSide->doubleValue(), number, op), otherType, isInteger);
    393         }
    394 
    395         return create(leftSide, rightSide, op);
    396     }
    397 
    398     virtual bool isZero() const OVERRIDE
    399     {
    400         return !doubleValue();
    401     }
    402 
    403     virtual void accumulatePixelsAndPercent(const CSSToLengthConversionData& conversionData, PixelsAndPercent& value, float multiplier) const OVERRIDE
    404     {
    405         switch (m_operator) {
    406         case CalcAdd:
    407             m_leftSide->accumulatePixelsAndPercent(conversionData, value, multiplier);
    408             m_rightSide->accumulatePixelsAndPercent(conversionData, value, multiplier);
    409             break;
    410         case CalcSubtract:
    411             m_leftSide->accumulatePixelsAndPercent(conversionData, value, multiplier);
    412             m_rightSide->accumulatePixelsAndPercent(conversionData, value, -multiplier);
    413             break;
    414         case CalcMultiply:
    415             ASSERT((m_leftSide->category() == CalcNumber) != (m_rightSide->category() == CalcNumber));
    416             if (m_leftSide->category() == CalcNumber)
    417                 m_rightSide->accumulatePixelsAndPercent(conversionData, value, multiplier * m_leftSide->doubleValue());
    418             else
    419                 m_leftSide->accumulatePixelsAndPercent(conversionData, value, multiplier * m_rightSide->doubleValue());
    420             break;
    421         case CalcDivide:
    422             ASSERT(m_rightSide->category() == CalcNumber);
    423             m_leftSide->accumulatePixelsAndPercent(conversionData, value, multiplier / m_rightSide->doubleValue());
    424             break;
    425         default:
    426             ASSERT_NOT_REACHED();
    427         }
    428     }
    429 
    430     virtual double doubleValue() const OVERRIDE
    431     {
    432         return evaluate(m_leftSide->doubleValue(), m_rightSide->doubleValue());
    433     }
    434 
    435     virtual double computeLengthPx(const CSSToLengthConversionData& conversionData) const OVERRIDE
    436     {
    437         const double leftValue = m_leftSide->computeLengthPx(conversionData);
    438         const double rightValue = m_rightSide->computeLengthPx(conversionData);
    439         return evaluate(leftValue, rightValue);
    440     }
    441 
    442     virtual void accumulateLengthArray(CSSLengthArray& lengthArray, double multiplier) const
    443     {
    444         switch (m_operator) {
    445         case CalcAdd:
    446             m_leftSide->accumulateLengthArray(lengthArray, multiplier);
    447             m_rightSide->accumulateLengthArray(lengthArray, multiplier);
    448             break;
    449         case CalcSubtract:
    450             m_leftSide->accumulateLengthArray(lengthArray, multiplier);
    451             m_rightSide->accumulateLengthArray(lengthArray, -multiplier);
    452             break;
    453         case CalcMultiply:
    454             ASSERT((m_leftSide->category() == CalcNumber) != (m_rightSide->category() == CalcNumber));
    455             if (m_leftSide->category() == CalcNumber)
    456                 m_rightSide->accumulateLengthArray(lengthArray, multiplier * m_leftSide->doubleValue());
    457             else
    458                 m_leftSide->accumulateLengthArray(lengthArray, multiplier * m_rightSide->doubleValue());
    459             break;
    460         case CalcDivide:
    461             ASSERT(m_rightSide->category() == CalcNumber);
    462             m_leftSide->accumulateLengthArray(lengthArray, multiplier / m_rightSide->doubleValue());
    463             break;
    464         default:
    465             ASSERT_NOT_REACHED();
    466         }
    467     }
    468 
    469     static String buildCSSText(const String& leftExpression, const String& rightExpression, CalcOperator op)
    470     {
    471         StringBuilder result;
    472         result.append('(');
    473         result.append(leftExpression);
    474         result.append(' ');
    475         result.append(static_cast<char>(op));
    476         result.append(' ');
    477         result.append(rightExpression);
    478         result.append(')');
    479 
    480         return result.toString();
    481     }
    482 
    483     virtual String customCSSText() const OVERRIDE
    484     {
    485         return buildCSSText(m_leftSide->customCSSText(), m_rightSide->customCSSText(), m_operator);
    486     }
    487 
    488     virtual bool equals(const CSSCalcExpressionNode& exp) const OVERRIDE
    489     {
    490         if (type() != exp.type())
    491             return false;
    492 
    493         const CSSCalcBinaryOperation& other = static_cast<const CSSCalcBinaryOperation&>(exp);
    494         return compareCSSValuePtr(m_leftSide, other.m_leftSide)
    495             && compareCSSValuePtr(m_rightSide, other.m_rightSide)
    496             && m_operator == other.m_operator;
    497     }
    498 
    499     virtual Type type() const OVERRIDE { return CssCalcBinaryOperation; }
    500 
    501     virtual CSSPrimitiveValue::UnitType primitiveType() const OVERRIDE
    502     {
    503         switch (m_category) {
    504         case CalcNumber:
    505             ASSERT(m_leftSide->category() == CalcNumber && m_rightSide->category() == CalcNumber);
    506             if (m_isInteger)
    507                 return CSSPrimitiveValue::CSS_PARSER_INTEGER;
    508             return CSSPrimitiveValue::CSS_NUMBER;
    509         case CalcLength:
    510         case CalcPercent: {
    511             if (m_leftSide->category() == CalcNumber)
    512                 return m_rightSide->primitiveType();
    513             if (m_rightSide->category() == CalcNumber)
    514                 return m_leftSide->primitiveType();
    515             CSSPrimitiveValue::UnitType leftType = m_leftSide->primitiveType();
    516             if (leftType == m_rightSide->primitiveType())
    517                 return leftType;
    518             return CSSPrimitiveValue::CSS_UNKNOWN;
    519         }
    520         case CalcPercentLength:
    521         case CalcPercentNumber:
    522         case CalcOther:
    523             return CSSPrimitiveValue::CSS_UNKNOWN;
    524         }
    525         ASSERT_NOT_REACHED();
    526         return CSSPrimitiveValue::CSS_UNKNOWN;
    527     }
    528 
    529     virtual void trace(Visitor* visitor)
    530     {
    531         visitor->trace(m_leftSide);
    532         visitor->trace(m_rightSide);
    533         CSSCalcExpressionNode::trace(visitor);
    534     }
    535 
    536 private:
    537     CSSCalcBinaryOperation(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> leftSide, PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> rightSide, CalcOperator op, CalculationCategory category)
    538         : CSSCalcExpressionNode(category, isIntegerResult(leftSide.get(), rightSide.get(), op))
    539         , m_leftSide(leftSide)
    540         , m_rightSide(rightSide)
    541         , m_operator(op)
    542     {
    543     }
    544 
    545     static CSSCalcExpressionNode* getNumberSide(CSSCalcExpressionNode* leftSide, CSSCalcExpressionNode* rightSide)
    546     {
    547         if (leftSide->category() == CalcNumber)
    548             return leftSide;
    549         if (rightSide->category() == CalcNumber)
    550             return rightSide;
    551         return 0;
    552     }
    553 
    554     double evaluate(double leftSide, double rightSide) const
    555     {
    556         return evaluateOperator(leftSide, rightSide, m_operator);
    557     }
    558 
    559     static double evaluateOperator(double leftValue, double rightValue, CalcOperator op)
    560     {
    561         switch (op) {
    562         case CalcAdd:
    563             return leftValue + rightValue;
    564         case CalcSubtract:
    565             return leftValue - rightValue;
    566         case CalcMultiply:
    567             return leftValue * rightValue;
    568         case CalcDivide:
    569             if (rightValue)
    570                 return leftValue / rightValue;
    571             return std::numeric_limits<double>::quiet_NaN();
    572         }
    573         return 0;
    574     }
    575 
    576     const RefPtrWillBeMember<CSSCalcExpressionNode> m_leftSide;
    577     const RefPtrWillBeMember<CSSCalcExpressionNode> m_rightSide;
    578     const CalcOperator m_operator;
    579 };
    580 
    581 static ParseState checkDepthAndIndex(int* depth, unsigned index, CSSParserValueList* tokens)
    582 {
    583     (*depth)++;
    584     if (*depth > maxExpressionDepth)
    585         return TooDeep;
    586     if (index >= tokens->size())
    587         return NoMoreTokens;
    588     return OK;
    589 }
    590 
    591 class CSSCalcExpressionNodeParser {
    592     STACK_ALLOCATED();
    593 public:
    594     PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> parseCalc(CSSParserValueList* tokens)
    595     {
    596         unsigned index = 0;
    597         Value result;
    598         bool ok = parseValueExpression(tokens, 0, &index, &result);
    599         ASSERT_WITH_SECURITY_IMPLICATION(index <= tokens->size());
    600         if (!ok || index != tokens->size())
    601             return nullptr;
    602         return result.value;
    603     }
    604 
    605 private:
    606     struct Value {
    607         STACK_ALLOCATED();
    608     public:
    609         RefPtrWillBeMember<CSSCalcExpressionNode> value;
    610     };
    611 
    612     char operatorValue(CSSParserValueList* tokens, unsigned index)
    613     {
    614         if (index >= tokens->size())
    615             return 0;
    616         CSSParserValue* value = tokens->valueAt(index);
    617         if (value->unit != CSSParserValue::Operator)
    618             return 0;
    619 
    620         return value->iValue;
    621     }
    622 
    623     bool parseValue(CSSParserValueList* tokens, unsigned* index, Value* result)
    624     {
    625         CSSParserValue* parserValue = tokens->valueAt(*index);
    626         if (parserValue->unit == CSSParserValue::Operator)
    627             return false;
    628 
    629         RefPtrWillBeRawPtr<CSSValue> value = parserValue->createCSSValue();
    630         if (!value || !value->isPrimitiveValue())
    631             return false;
    632 
    633         CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value.get());
    634         if (unitCategory(primitiveValue->primitiveType()) == CalcOther)
    635             return false;
    636 
    637         result->value = CSSCalcPrimitiveValue::create(primitiveValue, parserValue->isInt);
    638 
    639         ++*index;
    640         return true;
    641     }
    642 
    643     bool parseValueTerm(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
    644     {
    645         if (checkDepthAndIndex(&depth, *index, tokens) != OK)
    646             return false;
    647 
    648         if (operatorValue(tokens, *index) == '(') {
    649             unsigned currentIndex = *index + 1;
    650             if (!parseValueExpression(tokens, depth, &currentIndex, result))
    651                 return false;
    652 
    653             if (operatorValue(tokens, currentIndex) != ')')
    654                 return false;
    655             *index = currentIndex + 1;
    656             return true;
    657         }
    658 
    659         return parseValue(tokens, index, result);
    660     }
    661 
    662     bool parseValueMultiplicativeExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
    663     {
    664         if (checkDepthAndIndex(&depth, *index, tokens) != OK)
    665             return false;
    666 
    667         if (!parseValueTerm(tokens, depth, index, result))
    668             return false;
    669 
    670         while (*index < tokens->size() - 1) {
    671             char operatorCharacter = operatorValue(tokens, *index);
    672             if (operatorCharacter != CalcMultiply && operatorCharacter != CalcDivide)
    673                 break;
    674             ++*index;
    675 
    676             Value rhs;
    677             if (!parseValueTerm(tokens, depth, index, &rhs))
    678                 return false;
    679 
    680             result->value = CSSCalcBinaryOperation::createSimplified(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
    681             if (!result->value)
    682                 return false;
    683         }
    684 
    685         ASSERT_WITH_SECURITY_IMPLICATION(*index <= tokens->size());
    686         return true;
    687     }
    688 
    689     bool parseAdditiveValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
    690     {
    691         if (checkDepthAndIndex(&depth, *index, tokens) != OK)
    692             return false;
    693 
    694         if (!parseValueMultiplicativeExpression(tokens, depth, index, result))
    695             return false;
    696 
    697         while (*index < tokens->size() - 1) {
    698             char operatorCharacter = operatorValue(tokens, *index);
    699             if (operatorCharacter != CalcAdd && operatorCharacter != CalcSubtract)
    700                 break;
    701             ++*index;
    702 
    703             Value rhs;
    704             if (!parseValueMultiplicativeExpression(tokens, depth, index, &rhs))
    705                 return false;
    706 
    707             result->value = CSSCalcBinaryOperation::createSimplified(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
    708             if (!result->value)
    709                 return false;
    710         }
    711 
    712         ASSERT_WITH_SECURITY_IMPLICATION(*index <= tokens->size());
    713         return true;
    714     }
    715 
    716     bool parseValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
    717     {
    718         return parseAdditiveValueExpression(tokens, depth, index, result);
    719     }
    720 };
    721 
    722 PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> value, bool isInteger)
    723 {
    724     return CSSCalcPrimitiveValue::create(value, isInteger);
    725 }
    726 
    727 PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> leftSide, PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
    728 {
    729     return CSSCalcBinaryOperation::create(leftSide, rightSide, op);
    730 }
    731 
    732 PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(double pixels, double percent)
    733 {
    734     return createExpressionNode(
    735         createExpressionNode(CSSPrimitiveValue::create(pixels, CSSPrimitiveValue::CSS_PX), pixels == trunc(pixels)),
    736         createExpressionNode(CSSPrimitiveValue::create(percent, CSSPrimitiveValue::CSS_PERCENTAGE), percent == trunc(percent)),
    737         CalcAdd);
    738 }
    739 
    740 PassRefPtrWillBeRawPtr<CSSCalcValue> CSSCalcValue::create(CSSParserString name, CSSParserValueList* parserValueList, ValueRange range)
    741 {
    742     CSSCalcExpressionNodeParser parser;
    743     RefPtrWillBeRawPtr<CSSCalcExpressionNode> expression = nullptr;
    744 
    745     if (equalIgnoringCase(name, "calc(") || equalIgnoringCase(name, "-webkit-calc("))
    746         expression = parser.parseCalc(parserValueList);
    747     // FIXME calc (http://webkit.org/b/16662) Add parsing for min and max here
    748 
    749     return expression ? adoptRefWillBeNoop(new CSSCalcValue(expression, range)) : nullptr;
    750 }
    751 
    752 PassRefPtrWillBeRawPtr<CSSCalcValue> CSSCalcValue::create(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> expression, ValueRange range)
    753 {
    754     return adoptRefWillBeNoop(new CSSCalcValue(expression, range));
    755 }
    756 
    757 void CSSCalcValue::traceAfterDispatch(Visitor* visitor)
    758 {
    759     visitor->trace(m_expression);
    760     CSSValue::traceAfterDispatch(visitor);
    761 }
    762 
    763 } // namespace WebCore
    764