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