79de5eb33bf8ee142bfad3958a9777d28b560c61
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / CSSCalculationValue.cpp
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