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