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