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
40static const int maxExpressionDepth = 100;
41
42enum ParseState {
43    OK,
44    TooDeep,
45    NoMoreTokens
46};
47
48namespace WebCore {
49
50static CalculationCategory unitCategory(CSSPrimitiveValue::UnitTypes 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        return CalcLength;
69    case CSSPrimitiveValue::CSS_VARIABLE_NAME:
70        return CalcVariable;
71    default:
72        return CalcOther;
73    }
74}
75
76static bool hasDoubleValue(CSSPrimitiveValue::UnitTypes type)
77{
78    switch (type) {
79    case CSSPrimitiveValue::CSS_NUMBER:
80    case CSSPrimitiveValue::CSS_PARSER_INTEGER:
81    case CSSPrimitiveValue::CSS_PERCENTAGE:
82    case CSSPrimitiveValue::CSS_EMS:
83    case CSSPrimitiveValue::CSS_EXS:
84    case CSSPrimitiveValue::CSS_CHS:
85    case CSSPrimitiveValue::CSS_REMS:
86    case CSSPrimitiveValue::CSS_PX:
87    case CSSPrimitiveValue::CSS_CM:
88    case CSSPrimitiveValue::CSS_MM:
89    case CSSPrimitiveValue::CSS_IN:
90    case CSSPrimitiveValue::CSS_PT:
91    case CSSPrimitiveValue::CSS_PC:
92    case CSSPrimitiveValue::CSS_DEG:
93    case CSSPrimitiveValue::CSS_RAD:
94    case CSSPrimitiveValue::CSS_GRAD:
95    case CSSPrimitiveValue::CSS_MS:
96    case CSSPrimitiveValue::CSS_S:
97    case CSSPrimitiveValue::CSS_HZ:
98    case CSSPrimitiveValue::CSS_KHZ:
99    case CSSPrimitiveValue::CSS_DIMENSION:
100    case CSSPrimitiveValue::CSS_VW:
101    case CSSPrimitiveValue::CSS_VH:
102    case CSSPrimitiveValue::CSS_VMIN:
103    case CSSPrimitiveValue::CSS_VMAX:
104    case CSSPrimitiveValue::CSS_DPPX:
105    case CSSPrimitiveValue::CSS_DPI:
106    case CSSPrimitiveValue::CSS_DPCM:
107    case CSSPrimitiveValue::CSS_FR:
108        return true;
109    case CSSPrimitiveValue::CSS_UNKNOWN:
110    case CSSPrimitiveValue::CSS_STRING:
111    case CSSPrimitiveValue::CSS_URI:
112    case CSSPrimitiveValue::CSS_IDENT:
113    case CSSPrimitiveValue::CSS_ATTR:
114    case CSSPrimitiveValue::CSS_COUNTER:
115    case CSSPrimitiveValue::CSS_RECT:
116    case CSSPrimitiveValue::CSS_RGBCOLOR:
117    case CSSPrimitiveValue::CSS_PAIR:
118    case CSSPrimitiveValue::CSS_UNICODE_RANGE:
119    case CSSPrimitiveValue::CSS_PARSER_OPERATOR:
120    case CSSPrimitiveValue::CSS_PARSER_HEXCOLOR:
121    case CSSPrimitiveValue::CSS_PARSER_IDENTIFIER:
122    case CSSPrimitiveValue::CSS_TURN:
123    case CSSPrimitiveValue::CSS_COUNTER_NAME:
124    case CSSPrimitiveValue::CSS_SHAPE:
125    case CSSPrimitiveValue::CSS_QUAD:
126    case CSSPrimitiveValue::CSS_CALC:
127    case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER:
128    case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH:
129    case CSSPrimitiveValue::CSS_VARIABLE_NAME:
130    case CSSPrimitiveValue::CSS_PROPERTY_ID:
131    case CSSPrimitiveValue::CSS_VALUE_ID:
132        return false;
133    };
134    ASSERT_NOT_REACHED();
135    return false;
136}
137
138static String buildCSSText(const String& expression)
139{
140    StringBuilder result;
141    result.append("calc");
142    bool expressionHasSingleTerm = expression[0] != '(';
143    if (expressionHasSingleTerm)
144        result.append('(');
145    result.append(expression);
146    if (expressionHasSingleTerm)
147        result.append(')');
148    return result.toString();
149}
150
151String CSSCalcValue::customCSSText() const
152{
153    return buildCSSText(m_expression->customCSSText());
154}
155
156bool CSSCalcValue::equals(const CSSCalcValue& other) const
157{
158    return compareCSSValuePtr(m_expression, other.m_expression);
159}
160
161String CSSCalcValue::customSerializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
162{
163    return buildCSSText(m_expression->serializeResolvingVariables(variables));
164}
165
166bool CSSCalcValue::hasVariableReference() const
167{
168    return m_expression->hasVariableReference();
169}
170
171double CSSCalcValue::clampToPermittedRange(double value) const
172{
173    return m_nonNegative && value < 0 ? 0 : value;
174}
175
176double CSSCalcValue::doubleValue() const
177{
178    return clampToPermittedRange(m_expression->doubleValue());
179}
180
181double CSSCalcValue::computeLengthPx(const CSSToLengthConversionData& conversionData) const
182{
183    return clampToPermittedRange(m_expression->computeLengthPx(conversionData));
184}
185
186CSSCalcExpressionNode::~CSSCalcExpressionNode()
187{
188}
189
190class CSSCalcPrimitiveValue : public CSSCalcExpressionNode {
191    WTF_MAKE_FAST_ALLOCATED;
192public:
193
194    static PassRefPtr<CSSCalcPrimitiveValue> create(PassRefPtr<CSSPrimitiveValue> value, bool isInteger)
195    {
196        return adoptRef(new CSSCalcPrimitiveValue(value, isInteger));
197    }
198
199    static PassRefPtr<CSSCalcPrimitiveValue> create(double value, CSSPrimitiveValue::UnitTypes type, bool isInteger)
200    {
201        if (std::isnan(value) || std::isinf(value))
202            return 0;
203        return adoptRef(new CSSCalcPrimitiveValue(CSSPrimitiveValue::create(value, type).get(), isInteger));
204    }
205
206    virtual bool isZero() const
207    {
208        return !m_value->getDoubleValue();
209    }
210
211    virtual String customCSSText() const
212    {
213        return m_value->cssText();
214    }
215
216    virtual String serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
217    {
218        return m_value->customSerializeResolvingVariables(variables);
219    }
220
221    virtual bool hasVariableReference() const
222    {
223        return m_value->isVariableName();
224    }
225
226    virtual PassOwnPtr<CalcExpressionNode> toCalcValue(const CSSToLengthConversionData& conversionData) const
227    {
228        switch (m_category) {
229        case CalcNumber:
230            return adoptPtr(new CalcExpressionNumber(m_value->getFloatValue()));
231        case CalcLength:
232            return adoptPtr(new CalcExpressionLength(Length(m_value->computeLength<float>(conversionData), WebCore::Fixed)));
233        case CalcPercent:
234        case CalcPercentLength: {
235            CSSPrimitiveValue* primitiveValue = m_value.get();
236            return adoptPtr(new CalcExpressionLength(primitiveValue
237                ? primitiveValue->convertToLength<FixedConversion | PercentConversion>(conversionData)
238                : Length(Undefined)));
239        }
240        // Only types that could be part of a Length expression can be converted
241        // to a CalcExpressionNode. CalcPercentNumber makes no sense as a Length.
242        case CalcPercentNumber:
243        case CalcVariable:
244        case CalcOther:
245            ASSERT_NOT_REACHED();
246        }
247        return nullptr;
248    }
249
250    virtual double doubleValue() const
251    {
252        if (hasDoubleValue(primitiveType()))
253            return m_value->getDoubleValue();
254        ASSERT_NOT_REACHED();
255        return 0;
256    }
257
258    virtual double computeLengthPx(const CSSToLengthConversionData& conversionData) const
259    {
260        switch (m_category) {
261        case CalcLength:
262            return m_value->computeLength<double>(conversionData);
263        case CalcPercent:
264        case CalcNumber:
265            return m_value->getDoubleValue();
266        case CalcPercentLength:
267        case CalcPercentNumber:
268        case CalcVariable:
269        case CalcOther:
270            ASSERT_NOT_REACHED();
271            break;
272        }
273        ASSERT_NOT_REACHED();
274        return 0;
275    }
276
277    virtual bool equals(const CSSCalcExpressionNode& other) const
278    {
279        if (type() != other.type())
280            return false;
281
282        return compareCSSValuePtr(m_value, static_cast<const CSSCalcPrimitiveValue&>(other).m_value);
283    }
284
285    virtual Type type() const { return CssCalcPrimitiveValue; }
286    virtual CSSPrimitiveValue::UnitTypes primitiveType() const
287    {
288        return CSSPrimitiveValue::UnitTypes(m_value->primitiveType());
289    }
290
291private:
292    explicit CSSCalcPrimitiveValue(PassRefPtr<CSSPrimitiveValue> value, bool isInteger)
293        : CSSCalcExpressionNode(unitCategory((CSSPrimitiveValue::UnitTypes)value->primitiveType()), isInteger)
294        , m_value(value)
295    {
296    }
297
298    RefPtr<CSSPrimitiveValue> m_value;
299};
300
301static const CalculationCategory addSubtractResult[CalcOther][CalcOther] = {
302//                        CalcNumber         CalcLength         CalcPercent        CalcPercentNumber  CalcPercentLength
303/* CalcNumber */        { CalcNumber,        CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
304/* CalcLength */        { CalcOther,         CalcLength,        CalcPercentLength, CalcOther,         CalcPercentLength },
305/* CalcPercent */       { CalcPercentNumber, CalcPercentLength, CalcPercent,       CalcPercentNumber, CalcPercentLength },
306/* CalcPercentNumber */ { CalcPercentNumber, CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
307/* CalcPercentLength */ { CalcOther,         CalcPercentLength, CalcPercentLength, CalcOther,         CalcPercentLength },
308};
309
310static CalculationCategory determineCategory(const CSSCalcExpressionNode& leftSide, const CSSCalcExpressionNode& rightSide, CalcOperator op)
311{
312    CalculationCategory leftCategory = leftSide.category();
313    CalculationCategory rightCategory = rightSide.category();
314
315    if (leftCategory == CalcOther || rightCategory == CalcOther)
316        return CalcOther;
317
318    if (leftCategory == CalcVariable || rightCategory == CalcVariable)
319        return CalcVariable;
320
321    switch (op) {
322    case CalcAdd:
323    case CalcSubtract:
324        return addSubtractResult[leftCategory][rightCategory];
325    case CalcMultiply:
326        if (leftCategory != CalcNumber && rightCategory != CalcNumber)
327            return CalcOther;
328        return leftCategory == CalcNumber ? rightCategory : leftCategory;
329    case CalcDivide:
330        if (rightCategory != CalcNumber || rightSide.isZero())
331            return CalcOther;
332        return leftCategory;
333    }
334
335    ASSERT_NOT_REACHED();
336    return CalcOther;
337}
338
339static bool isIntegerResult(const CSSCalcExpressionNode* leftSide, const CSSCalcExpressionNode* rightSide, CalcOperator op)
340{
341    // Not testing for actual integer values.
342    // Performs W3C spec's type checking for calc integers.
343    // http://www.w3.org/TR/css3-values/#calc-type-checking
344    return op != CalcDivide && leftSide->isInteger() && rightSide->isInteger();
345}
346
347class CSSCalcBinaryOperation : public CSSCalcExpressionNode {
348
349public:
350    static PassRefPtr<CSSCalcExpressionNode> create(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
351    {
352        ASSERT(leftSide->category() != CalcOther && rightSide->category() != CalcOther);
353
354        CalculationCategory newCategory = determineCategory(*leftSide, *rightSide, op);
355        if (newCategory == CalcOther)
356            return 0;
357
358        return adoptRef(new CSSCalcBinaryOperation(leftSide, rightSide, op, newCategory));
359    }
360
361    static PassRefPtr<CSSCalcExpressionNode> createSimplified(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
362    {
363        CalculationCategory leftCategory = leftSide->category();
364        CalculationCategory rightCategory = rightSide->category();
365        ASSERT(leftCategory != CalcOther && rightCategory != CalcOther);
366
367        bool isInteger = isIntegerResult(leftSide.get(), rightSide.get(), op);
368
369        // Simplify numbers.
370        if (leftCategory == CalcNumber && rightCategory == CalcNumber) {
371            CSSPrimitiveValue::UnitTypes evaluationType = isInteger ? CSSPrimitiveValue::CSS_PARSER_INTEGER : CSSPrimitiveValue::CSS_NUMBER;
372            return CSSCalcPrimitiveValue::create(evaluateOperator(leftSide->doubleValue(), rightSide->doubleValue(), op), evaluationType, isInteger);
373        }
374
375        // Simplify addition and subtraction between same types.
376        if (op == CalcAdd || op == CalcSubtract) {
377            if (leftCategory == rightSide->category()) {
378                CSSPrimitiveValue::UnitTypes leftType = leftSide->primitiveType();
379                if (hasDoubleValue(leftType)) {
380                    CSSPrimitiveValue::UnitTypes rightType = rightSide->primitiveType();
381                    if (leftType == rightType)
382                        return CSSCalcPrimitiveValue::create(evaluateOperator(leftSide->doubleValue(), rightSide->doubleValue(), op), leftType, isInteger);
383                    CSSPrimitiveValue::UnitCategory leftUnitCategory = CSSPrimitiveValue::unitCategory(leftType);
384                    if (leftUnitCategory != CSSPrimitiveValue::UOther && leftUnitCategory == CSSPrimitiveValue::unitCategory(rightType)) {
385                        CSSPrimitiveValue::UnitTypes canonicalType = CSSPrimitiveValue::canonicalUnitTypeForCategory(leftUnitCategory);
386                        if (canonicalType != CSSPrimitiveValue::CSS_UNKNOWN) {
387                            double leftValue = leftSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(leftType);
388                            double rightValue = rightSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(rightType);
389                            return CSSCalcPrimitiveValue::create(evaluateOperator(leftValue, rightValue, op), canonicalType, isInteger);
390                        }
391                    }
392                }
393            }
394        } else {
395            // Simplify multiplying or dividing by a number for simplifiable types.
396            ASSERT(op == CalcMultiply || op == CalcDivide);
397            CSSCalcExpressionNode* numberSide = getNumberSide(leftSide.get(), rightSide.get());
398            if (!numberSide)
399                return create(leftSide, rightSide, op);
400            if (numberSide == leftSide && op == CalcDivide)
401                return 0;
402            CSSCalcExpressionNode* otherSide = leftSide == numberSide ? rightSide.get() : leftSide.get();
403
404            double number = numberSide->doubleValue();
405            if (std::isnan(number) || std::isinf(number))
406                return 0;
407            if (op == CalcDivide && !number)
408                return 0;
409
410            CSSPrimitiveValue::UnitTypes otherType = otherSide->primitiveType();
411            if (hasDoubleValue(otherType))
412                return CSSCalcPrimitiveValue::create(evaluateOperator(otherSide->doubleValue(), number, op), otherType, isInteger);
413        }
414
415        return create(leftSide, rightSide, op);
416    }
417
418    virtual bool isZero() const
419    {
420        return !doubleValue();
421    }
422
423    virtual PassOwnPtr<CalcExpressionNode> toCalcValue(const CSSToLengthConversionData& conversionData) const
424    {
425        OwnPtr<CalcExpressionNode> left(m_leftSide->toCalcValue(conversionData));
426        if (!left)
427            return nullptr;
428        OwnPtr<CalcExpressionNode> right(m_rightSide->toCalcValue(conversionData));
429        if (!right)
430            return nullptr;
431        return adoptPtr(new CalcExpressionBinaryOperation(left.release(), right.release(), m_operator));
432    }
433
434    virtual double doubleValue() const
435    {
436        return evaluate(m_leftSide->doubleValue(), m_rightSide->doubleValue());
437    }
438
439    virtual double computeLengthPx(const CSSToLengthConversionData& conversionData) const
440    {
441        const double leftValue = m_leftSide->computeLengthPx(conversionData);
442        const double rightValue = m_rightSide->computeLengthPx(conversionData);
443        return evaluate(leftValue, rightValue);
444    }
445
446    static String buildCSSText(const String& leftExpression, const String& rightExpression, CalcOperator op)
447    {
448        StringBuilder result;
449        result.append('(');
450        result.append(leftExpression);
451        result.append(' ');
452        result.append(static_cast<char>(op));
453        result.append(' ');
454        result.append(rightExpression);
455        result.append(')');
456
457        return result.toString();
458    }
459
460    virtual String customCSSText() const
461    {
462        return buildCSSText(m_leftSide->customCSSText(), m_rightSide->customCSSText(), m_operator);
463    }
464
465    virtual String serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
466    {
467        return buildCSSText(m_leftSide->serializeResolvingVariables(variables), m_rightSide->serializeResolvingVariables(variables), m_operator);
468    }
469
470    virtual bool hasVariableReference() const
471    {
472        return m_leftSide->hasVariableReference() || m_rightSide->hasVariableReference();
473    }
474
475    virtual bool equals(const CSSCalcExpressionNode& exp) const
476    {
477        if (type() != exp.type())
478            return false;
479
480        const CSSCalcBinaryOperation& other = static_cast<const CSSCalcBinaryOperation&>(exp);
481        return compareCSSValuePtr(m_leftSide, other.m_leftSide)
482            && compareCSSValuePtr(m_rightSide, other.m_rightSide)
483            && m_operator == other.m_operator;
484    }
485
486    virtual Type type() const { return CssCalcBinaryOperation; }
487
488    virtual CSSPrimitiveValue::UnitTypes primitiveType() const
489    {
490        switch (m_category) {
491        case CalcNumber:
492            ASSERT(m_leftSide->category() == CalcNumber && m_rightSide->category() == CalcNumber);
493            if (m_isInteger)
494                return CSSPrimitiveValue::CSS_PARSER_INTEGER;
495            return CSSPrimitiveValue::CSS_NUMBER;
496        case CalcLength:
497        case CalcPercent: {
498            if (m_leftSide->category() == CalcNumber)
499                return m_rightSide->primitiveType();
500            if (m_rightSide->category() == CalcNumber)
501                return m_leftSide->primitiveType();
502            CSSPrimitiveValue::UnitTypes leftType = m_leftSide->primitiveType();
503            if (leftType == m_rightSide->primitiveType())
504                return leftType;
505            return CSSPrimitiveValue::CSS_UNKNOWN;
506        }
507        case CalcVariable:
508            return CSSPrimitiveValue::CSS_VARIABLE_NAME;
509        case CalcPercentLength:
510        case CalcPercentNumber:
511        case CalcOther:
512            return CSSPrimitiveValue::CSS_UNKNOWN;
513        }
514        ASSERT_NOT_REACHED();
515        return CSSPrimitiveValue::CSS_UNKNOWN;
516    }
517
518
519private:
520    CSSCalcBinaryOperation(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op, CalculationCategory category)
521        : CSSCalcExpressionNode(category, isIntegerResult(leftSide.get(), rightSide.get(), op))
522        , m_leftSide(leftSide)
523        , m_rightSide(rightSide)
524        , m_operator(op)
525    {
526    }
527
528    static CSSCalcExpressionNode* getNumberSide(CSSCalcExpressionNode* leftSide, CSSCalcExpressionNode* rightSide)
529    {
530        if (leftSide->category() == CalcNumber)
531            return leftSide;
532        if (rightSide->category() == CalcNumber)
533            return rightSide;
534        return 0;
535    }
536
537    double evaluate(double leftSide, double rightSide) const
538    {
539        return evaluateOperator(leftSide, rightSide, m_operator);
540    }
541
542    static double evaluateOperator(double leftValue, double rightValue, CalcOperator op)
543    {
544        switch (op) {
545        case CalcAdd:
546            return leftValue + rightValue;
547        case CalcSubtract:
548            return leftValue - rightValue;
549        case CalcMultiply:
550            return leftValue * rightValue;
551        case CalcDivide:
552            if (rightValue)
553                return leftValue / rightValue;
554            return std::numeric_limits<double>::quiet_NaN();
555        }
556        return 0;
557    }
558
559    const RefPtr<CSSCalcExpressionNode> m_leftSide;
560    const RefPtr<CSSCalcExpressionNode> m_rightSide;
561    const CalcOperator m_operator;
562};
563
564static ParseState checkDepthAndIndex(int* depth, unsigned index, CSSParserValueList* tokens)
565{
566    (*depth)++;
567    if (*depth > maxExpressionDepth)
568        return TooDeep;
569    if (index >= tokens->size())
570        return NoMoreTokens;
571    return OK;
572}
573
574class CSSCalcExpressionNodeParser {
575public:
576    PassRefPtr<CSSCalcExpressionNode> parseCalc(CSSParserValueList* tokens)
577    {
578        unsigned index = 0;
579        Value result;
580        bool ok = parseValueExpression(tokens, 0, &index, &result);
581        ASSERT_WITH_SECURITY_IMPLICATION(index <= tokens->size());
582        if (!ok || index != tokens->size())
583            return 0;
584        return result.value;
585    }
586
587private:
588    struct Value {
589        RefPtr<CSSCalcExpressionNode> value;
590    };
591
592    char operatorValue(CSSParserValueList* tokens, unsigned index)
593    {
594        if (index >= tokens->size())
595            return 0;
596        CSSParserValue* value = tokens->valueAt(index);
597        if (value->unit != CSSParserValue::Operator)
598            return 0;
599
600        return value->iValue;
601    }
602
603    bool parseValue(CSSParserValueList* tokens, unsigned* index, Value* result)
604    {
605        CSSParserValue* parserValue = tokens->valueAt(*index);
606        if (parserValue->unit == CSSParserValue::Operator || parserValue->unit == CSSParserValue::Function)
607            return false;
608
609        RefPtr<CSSValue> value = parserValue->createCSSValue();
610        if (!value || !value->isPrimitiveValue())
611            return false;
612
613        result->value = CSSCalcPrimitiveValue::create(toCSSPrimitiveValue(value.get()), parserValue->isInt);
614
615        ++*index;
616        return true;
617    }
618
619    bool parseValueTerm(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
620    {
621        if (checkDepthAndIndex(&depth, *index, tokens) != OK)
622            return false;
623
624        if (operatorValue(tokens, *index) == '(') {
625            unsigned currentIndex = *index + 1;
626            if (!parseValueExpression(tokens, depth, &currentIndex, result))
627                return false;
628
629            if (operatorValue(tokens, currentIndex) != ')')
630                return false;
631            *index = currentIndex + 1;
632            return true;
633        }
634
635        return parseValue(tokens, index, result);
636    }
637
638    bool parseValueMultiplicativeExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
639    {
640        if (checkDepthAndIndex(&depth, *index, tokens) != OK)
641            return false;
642
643        if (!parseValueTerm(tokens, depth, index, result))
644            return false;
645
646        while (*index < tokens->size() - 1) {
647            char operatorCharacter = operatorValue(tokens, *index);
648            if (operatorCharacter != CalcMultiply && operatorCharacter != CalcDivide)
649                break;
650            ++*index;
651
652            Value rhs;
653            if (!parseValueTerm(tokens, depth, index, &rhs))
654                return false;
655
656            result->value = CSSCalcBinaryOperation::createSimplified(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
657            if (!result->value)
658                return false;
659        }
660
661        ASSERT_WITH_SECURITY_IMPLICATION(*index <= tokens->size());
662        return true;
663    }
664
665    bool parseAdditiveValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
666    {
667        if (checkDepthAndIndex(&depth, *index, tokens) != OK)
668            return false;
669
670        if (!parseValueMultiplicativeExpression(tokens, depth, index, result))
671            return false;
672
673        while (*index < tokens->size() - 1) {
674            char operatorCharacter = operatorValue(tokens, *index);
675            if (operatorCharacter != CalcAdd && operatorCharacter != CalcSubtract)
676                break;
677            ++*index;
678
679            Value rhs;
680            if (!parseValueMultiplicativeExpression(tokens, depth, index, &rhs))
681                return false;
682
683            result->value = CSSCalcBinaryOperation::createSimplified(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
684            if (!result->value)
685                return false;
686        }
687
688        ASSERT_WITH_SECURITY_IMPLICATION(*index <= tokens->size());
689        return true;
690    }
691
692    bool parseValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
693    {
694        return parseAdditiveValueExpression(tokens, depth, index, result);
695    }
696};
697
698PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(PassRefPtr<CSSPrimitiveValue> value, bool isInteger)
699{
700    return CSSCalcPrimitiveValue::create(value, isInteger);
701}
702
703PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
704{
705    return CSSCalcBinaryOperation::create(leftSide, rightSide, op);
706}
707
708PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(const CalcExpressionNode* node, float zoom)
709{
710    switch (node->type()) {
711    case CalcExpressionNodeNumber: {
712        float value = toCalcExpressionNumber(node)->value();
713        return createExpressionNode(CSSPrimitiveValue::create(value, CSSPrimitiveValue::CSS_NUMBER), value == trunc(value));
714    }
715    case CalcExpressionNodeLength:
716        return createExpressionNode(toCalcExpressionLength(node)->length(), zoom);
717    case CalcExpressionNodeBinaryOperation: {
718        const CalcExpressionBinaryOperation* binaryNode = toCalcExpressionBinaryOperation(node);
719        return createExpressionNode(createExpressionNode(binaryNode->leftSide(), zoom), createExpressionNode(binaryNode->rightSide(), zoom), binaryNode->getOperator());
720    }
721    case CalcExpressionNodeBlendLength: {
722        // FIXME(crbug.com/269320): Create a CSSCalcExpressionNode equivalent of CalcExpressionBlendLength.
723        const CalcExpressionBlendLength* blendNode = toCalcExpressionBlendLength(node);
724        const double progress = blendNode->progress();
725        const bool isInteger = !progress || (progress == 1);
726        return createExpressionNode(
727            createExpressionNode(
728                createExpressionNode(blendNode->from(), zoom),
729                createExpressionNode(CSSPrimitiveValue::create(1 - progress, CSSPrimitiveValue::CSS_NUMBER), isInteger),
730                CalcMultiply),
731            createExpressionNode(
732                createExpressionNode(blendNode->to(), zoom),
733                createExpressionNode(CSSPrimitiveValue::create(progress, CSSPrimitiveValue::CSS_NUMBER), isInteger),
734                CalcMultiply),
735            CalcAdd);
736    }
737    case CalcExpressionNodeUndefined:
738        ASSERT_NOT_REACHED();
739        return 0;
740    }
741    ASSERT_NOT_REACHED();
742    return 0;
743}
744
745PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(const Length& length, float zoom)
746{
747    switch (length.type()) {
748    case Percent:
749    case ViewportPercentageWidth:
750    case ViewportPercentageHeight:
751    case ViewportPercentageMin:
752    case ViewportPercentageMax:
753    case Fixed:
754        return createExpressionNode(CSSPrimitiveValue::create(length, zoom), length.value() == trunc(length.value()));
755    case Calculated:
756        return createExpressionNode(length.calculationValue()->expression(), zoom);
757    case Auto:
758    case Intrinsic:
759    case MinIntrinsic:
760    case MinContent:
761    case MaxContent:
762    case FillAvailable:
763    case FitContent:
764    case ExtendToZoom:
765    case Undefined:
766        ASSERT_NOT_REACHED();
767        return 0;
768    }
769    ASSERT_NOT_REACHED();
770    return 0;
771}
772
773PassRefPtr<CSSCalcValue> CSSCalcValue::create(CSSParserString name, CSSParserValueList* parserValueList, ValueRange range)
774{
775    CSSCalcExpressionNodeParser parser;
776    RefPtr<CSSCalcExpressionNode> expression;
777
778    if (equalIgnoringCase(name, "calc(") || equalIgnoringCase(name, "-webkit-calc("))
779        expression = parser.parseCalc(parserValueList);
780    // FIXME calc (http://webkit.org/b/16662) Add parsing for min and max here
781
782    return expression ? adoptRef(new CSSCalcValue(expression, range)) : 0;
783}
784
785PassRefPtr<CSSCalcValue> CSSCalcValue::create(PassRefPtr<CSSCalcExpressionNode> expression, ValueRange range)
786{
787    return adoptRef(new CSSCalcValue(expression, range));
788}
789
790} // namespace WebCore
791