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