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