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