• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011, 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 #include "core/css/CSSCalculationValue.h"
33 
34 #include "core/css/CSSPrimitiveValueMappings.h"
35 #include "core/css/resolver/StyleResolver.h"
36 #include "wtf/MathExtras.h"
37 #include "wtf/OwnPtr.h"
38 #include "wtf/text/StringBuilder.h"
39 
40 static const int maxExpressionDepth = 100;
41 
42 enum ParseState {
43     OK,
44     TooDeep,
45     NoMoreTokens
46 };
47 
48 namespace WebCore {
49 
unitCategory(CSSPrimitiveValue::UnitTypes type)50 static CalculationCategory unitCategory(CSSPrimitiveValue::UnitTypes type)
51 {
52     switch (type) {
53     case CSSPrimitiveValue::CSS_NUMBER:
54     case CSSPrimitiveValue::CSS_PARSER_INTEGER:
55         return CalcNumber;
56     case CSSPrimitiveValue::CSS_PERCENTAGE:
57         return CalcPercent;
58     case CSSPrimitiveValue::CSS_EMS:
59     case CSSPrimitiveValue::CSS_EXS:
60     case CSSPrimitiveValue::CSS_PX:
61     case CSSPrimitiveValue::CSS_CM:
62     case CSSPrimitiveValue::CSS_MM:
63     case CSSPrimitiveValue::CSS_IN:
64     case CSSPrimitiveValue::CSS_PT:
65     case CSSPrimitiveValue::CSS_PC:
66     case CSSPrimitiveValue::CSS_REMS:
67     case CSSPrimitiveValue::CSS_CHS:
68         return CalcLength;
69     case CSSPrimitiveValue::CSS_VARIABLE_NAME:
70         return CalcVariable;
71     default:
72         return CalcOther;
73     }
74 }
75 
hasDoubleValue(CSSPrimitiveValue::UnitTypes type)76 static bool hasDoubleValue(CSSPrimitiveValue::UnitTypes type)
77 {
78     switch (type) {
79     case CSSPrimitiveValue::CSS_NUMBER:
80     case CSSPrimitiveValue::CSS_PARSER_INTEGER:
81     case CSSPrimitiveValue::CSS_PERCENTAGE:
82     case CSSPrimitiveValue::CSS_EMS:
83     case CSSPrimitiveValue::CSS_EXS:
84     case CSSPrimitiveValue::CSS_CHS:
85     case CSSPrimitiveValue::CSS_REMS:
86     case CSSPrimitiveValue::CSS_PX:
87     case CSSPrimitiveValue::CSS_CM:
88     case CSSPrimitiveValue::CSS_MM:
89     case CSSPrimitiveValue::CSS_IN:
90     case CSSPrimitiveValue::CSS_PT:
91     case CSSPrimitiveValue::CSS_PC:
92     case CSSPrimitiveValue::CSS_DEG:
93     case CSSPrimitiveValue::CSS_RAD:
94     case CSSPrimitiveValue::CSS_GRAD:
95     case CSSPrimitiveValue::CSS_MS:
96     case CSSPrimitiveValue::CSS_S:
97     case CSSPrimitiveValue::CSS_HZ:
98     case CSSPrimitiveValue::CSS_KHZ:
99     case CSSPrimitiveValue::CSS_DIMENSION:
100     case CSSPrimitiveValue::CSS_VW:
101     case CSSPrimitiveValue::CSS_VH:
102     case CSSPrimitiveValue::CSS_VMIN:
103     case CSSPrimitiveValue::CSS_VMAX:
104     case CSSPrimitiveValue::CSS_DPPX:
105     case CSSPrimitiveValue::CSS_DPI:
106     case CSSPrimitiveValue::CSS_DPCM:
107     case CSSPrimitiveValue::CSS_FR:
108         return true;
109     case CSSPrimitiveValue::CSS_UNKNOWN:
110     case CSSPrimitiveValue::CSS_STRING:
111     case CSSPrimitiveValue::CSS_URI:
112     case CSSPrimitiveValue::CSS_IDENT:
113     case CSSPrimitiveValue::CSS_ATTR:
114     case CSSPrimitiveValue::CSS_COUNTER:
115     case CSSPrimitiveValue::CSS_RECT:
116     case CSSPrimitiveValue::CSS_RGBCOLOR:
117     case CSSPrimitiveValue::CSS_PAIR:
118     case CSSPrimitiveValue::CSS_UNICODE_RANGE:
119     case CSSPrimitiveValue::CSS_PARSER_OPERATOR:
120     case CSSPrimitiveValue::CSS_PARSER_HEXCOLOR:
121     case CSSPrimitiveValue::CSS_PARSER_IDENTIFIER:
122     case CSSPrimitiveValue::CSS_TURN:
123     case CSSPrimitiveValue::CSS_COUNTER_NAME:
124     case CSSPrimitiveValue::CSS_SHAPE:
125     case CSSPrimitiveValue::CSS_QUAD:
126     case CSSPrimitiveValue::CSS_CALC:
127     case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER:
128     case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH:
129     case CSSPrimitiveValue::CSS_VARIABLE_NAME:
130     case CSSPrimitiveValue::CSS_PROPERTY_ID:
131     case CSSPrimitiveValue::CSS_VALUE_ID:
132         return false;
133     };
134     ASSERT_NOT_REACHED();
135     return false;
136 }
137 
buildCSSText(const String & expression)138 static String buildCSSText(const String& expression)
139 {
140     StringBuilder result;
141     result.append("calc");
142     bool expressionHasSingleTerm = expression[0] != '(';
143     if (expressionHasSingleTerm)
144         result.append('(');
145     result.append(expression);
146     if (expressionHasSingleTerm)
147         result.append(')');
148     return result.toString();
149 }
150 
customCSSText() const151 String CSSCalcValue::customCSSText() const
152 {
153     return buildCSSText(m_expression->customCSSText());
154 }
155 
equals(const CSSCalcValue & other) const156 bool CSSCalcValue::equals(const CSSCalcValue& other) const
157 {
158     return compareCSSValuePtr(m_expression, other.m_expression);
159 }
160 
customSerializeResolvingVariables(const HashMap<AtomicString,String> & variables) const161 String CSSCalcValue::customSerializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
162 {
163     return buildCSSText(m_expression->serializeResolvingVariables(variables));
164 }
165 
hasVariableReference() const166 bool CSSCalcValue::hasVariableReference() const
167 {
168     return m_expression->hasVariableReference();
169 }
170 
clampToPermittedRange(double value) const171 double CSSCalcValue::clampToPermittedRange(double value) const
172 {
173     return m_nonNegative && value < 0 ? 0 : value;
174 }
175 
doubleValue() const176 double CSSCalcValue::doubleValue() const
177 {
178     return clampToPermittedRange(m_expression->doubleValue());
179 }
180 
computeLengthPx(const CSSToLengthConversionData & conversionData) const181 double CSSCalcValue::computeLengthPx(const CSSToLengthConversionData& conversionData) const
182 {
183     return clampToPermittedRange(m_expression->computeLengthPx(conversionData));
184 }
185 
~CSSCalcExpressionNode()186 CSSCalcExpressionNode::~CSSCalcExpressionNode()
187 {
188 }
189 
190 class CSSCalcPrimitiveValue : public CSSCalcExpressionNode {
191     WTF_MAKE_FAST_ALLOCATED;
192 public:
193 
create(PassRefPtr<CSSPrimitiveValue> value,bool isInteger)194     static PassRefPtr<CSSCalcPrimitiveValue> create(PassRefPtr<CSSPrimitiveValue> value, bool isInteger)
195     {
196         return adoptRef(new CSSCalcPrimitiveValue(value, isInteger));
197     }
198 
create(double value,CSSPrimitiveValue::UnitTypes type,bool isInteger)199     static PassRefPtr<CSSCalcPrimitiveValue> create(double value, CSSPrimitiveValue::UnitTypes type, bool isInteger)
200     {
201         if (std::isnan(value) || std::isinf(value))
202             return 0;
203         return adoptRef(new CSSCalcPrimitiveValue(CSSPrimitiveValue::create(value, type).get(), isInteger));
204     }
205 
isZero() const206     virtual bool isZero() const
207     {
208         return !m_value->getDoubleValue();
209     }
210 
customCSSText() const211     virtual String customCSSText() const
212     {
213         return m_value->cssText();
214     }
215 
serializeResolvingVariables(const HashMap<AtomicString,String> & variables) const216     virtual String serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
217     {
218         return m_value->customSerializeResolvingVariables(variables);
219     }
220 
hasVariableReference() const221     virtual bool hasVariableReference() const
222     {
223         return m_value->isVariableName();
224     }
225 
toCalcValue(const CSSToLengthConversionData & conversionData) const226     virtual PassOwnPtr<CalcExpressionNode> toCalcValue(const CSSToLengthConversionData& conversionData) const
227     {
228         switch (m_category) {
229         case CalcNumber:
230             return adoptPtr(new CalcExpressionNumber(m_value->getFloatValue()));
231         case CalcLength:
232             return adoptPtr(new CalcExpressionLength(Length(m_value->computeLength<float>(conversionData), WebCore::Fixed)));
233         case CalcPercent:
234         case CalcPercentLength: {
235             CSSPrimitiveValue* primitiveValue = m_value.get();
236             return adoptPtr(new CalcExpressionLength(primitiveValue
237                 ? primitiveValue->convertToLength<FixedConversion | PercentConversion>(conversionData)
238                 : Length(Undefined)));
239         }
240         // Only types that could be part of a Length expression can be converted
241         // to a CalcExpressionNode. CalcPercentNumber makes no sense as a Length.
242         case CalcPercentNumber:
243         case CalcVariable:
244         case CalcOther:
245             ASSERT_NOT_REACHED();
246         }
247         return nullptr;
248     }
249 
doubleValue() const250     virtual double doubleValue() const
251     {
252         if (hasDoubleValue(primitiveType()))
253             return m_value->getDoubleValue();
254         ASSERT_NOT_REACHED();
255         return 0;
256     }
257 
computeLengthPx(const CSSToLengthConversionData & conversionData) const258     virtual double computeLengthPx(const CSSToLengthConversionData& conversionData) const
259     {
260         switch (m_category) {
261         case CalcLength:
262             return m_value->computeLength<double>(conversionData);
263         case CalcPercent:
264         case CalcNumber:
265             return m_value->getDoubleValue();
266         case CalcPercentLength:
267         case CalcPercentNumber:
268         case CalcVariable:
269         case CalcOther:
270             ASSERT_NOT_REACHED();
271             break;
272         }
273         ASSERT_NOT_REACHED();
274         return 0;
275     }
276 
equals(const CSSCalcExpressionNode & other) const277     virtual bool equals(const CSSCalcExpressionNode& other) const
278     {
279         if (type() != other.type())
280             return false;
281 
282         return compareCSSValuePtr(m_value, static_cast<const CSSCalcPrimitiveValue&>(other).m_value);
283     }
284 
type() const285     virtual Type type() const { return CssCalcPrimitiveValue; }
primitiveType() const286     virtual CSSPrimitiveValue::UnitTypes primitiveType() const
287     {
288         return CSSPrimitiveValue::UnitTypes(m_value->primitiveType());
289     }
290 
291 private:
CSSCalcPrimitiveValue(PassRefPtr<CSSPrimitiveValue> value,bool isInteger)292     explicit CSSCalcPrimitiveValue(PassRefPtr<CSSPrimitiveValue> value, bool isInteger)
293         : CSSCalcExpressionNode(unitCategory((CSSPrimitiveValue::UnitTypes)value->primitiveType()), isInteger)
294         , m_value(value)
295     {
296     }
297 
298     RefPtr<CSSPrimitiveValue> m_value;
299 };
300 
301 static const CalculationCategory addSubtractResult[CalcOther][CalcOther] = {
302 //                        CalcNumber         CalcLength         CalcPercent        CalcPercentNumber  CalcPercentLength
303 /* CalcNumber */        { CalcNumber,        CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
304 /* CalcLength */        { CalcOther,         CalcLength,        CalcPercentLength, CalcOther,         CalcPercentLength },
305 /* CalcPercent */       { CalcPercentNumber, CalcPercentLength, CalcPercent,       CalcPercentNumber, CalcPercentLength },
306 /* CalcPercentNumber */ { CalcPercentNumber, CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
307 /* CalcPercentLength */ { CalcOther,         CalcPercentLength, CalcPercentLength, CalcOther,         CalcPercentLength },
308 };
309 
determineCategory(const CSSCalcExpressionNode & leftSide,const CSSCalcExpressionNode & rightSide,CalcOperator op)310 static CalculationCategory determineCategory(const CSSCalcExpressionNode& leftSide, const CSSCalcExpressionNode& rightSide, CalcOperator op)
311 {
312     CalculationCategory leftCategory = leftSide.category();
313     CalculationCategory rightCategory = rightSide.category();
314 
315     if (leftCategory == CalcOther || rightCategory == CalcOther)
316         return CalcOther;
317 
318     if (leftCategory == CalcVariable || rightCategory == CalcVariable)
319         return CalcVariable;
320 
321     switch (op) {
322     case CalcAdd:
323     case CalcSubtract:
324         return addSubtractResult[leftCategory][rightCategory];
325     case CalcMultiply:
326         if (leftCategory != CalcNumber && rightCategory != CalcNumber)
327             return CalcOther;
328         return leftCategory == CalcNumber ? rightCategory : leftCategory;
329     case CalcDivide:
330         if (rightCategory != CalcNumber || rightSide.isZero())
331             return CalcOther;
332         return leftCategory;
333     }
334 
335     ASSERT_NOT_REACHED();
336     return CalcOther;
337 }
338 
isIntegerResult(const CSSCalcExpressionNode * leftSide,const CSSCalcExpressionNode * rightSide,CalcOperator op)339 static bool isIntegerResult(const CSSCalcExpressionNode* leftSide, const CSSCalcExpressionNode* rightSide, CalcOperator op)
340 {
341     // Not testing for actual integer values.
342     // Performs W3C spec's type checking for calc integers.
343     // http://www.w3.org/TR/css3-values/#calc-type-checking
344     return op != CalcDivide && leftSide->isInteger() && rightSide->isInteger();
345 }
346 
347 class CSSCalcBinaryOperation : public CSSCalcExpressionNode {
348 
349 public:
create(PassRefPtr<CSSCalcExpressionNode> leftSide,PassRefPtr<CSSCalcExpressionNode> rightSide,CalcOperator op)350     static PassRefPtr<CSSCalcExpressionNode> create(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
351     {
352         ASSERT(leftSide->category() != CalcOther && rightSide->category() != CalcOther);
353 
354         CalculationCategory newCategory = determineCategory(*leftSide, *rightSide, op);
355         if (newCategory == CalcOther)
356             return 0;
357 
358         return adoptRef(new CSSCalcBinaryOperation(leftSide, rightSide, op, newCategory));
359     }
360 
createSimplified(PassRefPtr<CSSCalcExpressionNode> leftSide,PassRefPtr<CSSCalcExpressionNode> rightSide,CalcOperator op)361     static PassRefPtr<CSSCalcExpressionNode> createSimplified(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
362     {
363         CalculationCategory leftCategory = leftSide->category();
364         CalculationCategory rightCategory = rightSide->category();
365         ASSERT(leftCategory != CalcOther && rightCategory != CalcOther);
366 
367         bool isInteger = isIntegerResult(leftSide.get(), rightSide.get(), op);
368 
369         // Simplify numbers.
370         if (leftCategory == CalcNumber && rightCategory == CalcNumber) {
371             CSSPrimitiveValue::UnitTypes evaluationType = isInteger ? CSSPrimitiveValue::CSS_PARSER_INTEGER : CSSPrimitiveValue::CSS_NUMBER;
372             return CSSCalcPrimitiveValue::create(evaluateOperator(leftSide->doubleValue(), rightSide->doubleValue(), op), 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::UnitTypes leftType = leftSide->primitiveType();
379                 if (hasDoubleValue(leftType)) {
380                     CSSPrimitiveValue::UnitTypes rightType = rightSide->primitiveType();
381                     if (leftType == rightType)
382                         return CSSCalcPrimitiveValue::create(evaluateOperator(leftSide->doubleValue(), rightSide->doubleValue(), op), leftType, isInteger);
383                     CSSPrimitiveValue::UnitCategory leftUnitCategory = CSSPrimitiveValue::unitCategory(leftType);
384                     if (leftUnitCategory != CSSPrimitiveValue::UOther && leftUnitCategory == CSSPrimitiveValue::unitCategory(rightType)) {
385                         CSSPrimitiveValue::UnitTypes 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(leftValue, rightValue, op), 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.get(), rightSide.get());
398             if (!numberSide)
399                 return create(leftSide, rightSide, op);
400             if (numberSide == leftSide && op == CalcDivide)
401                 return 0;
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 0;
407             if (op == CalcDivide && !number)
408                 return 0;
409 
410             CSSPrimitiveValue::UnitTypes otherType = otherSide->primitiveType();
411             if (hasDoubleValue(otherType))
412                 return CSSCalcPrimitiveValue::create(evaluateOperator(otherSide->doubleValue(), number, op), otherType, isInteger);
413         }
414 
415         return create(leftSide, rightSide, op);
416     }
417 
isZero() const418     virtual bool isZero() const
419     {
420         return !doubleValue();
421     }
422 
toCalcValue(const CSSToLengthConversionData & conversionData) const423     virtual PassOwnPtr<CalcExpressionNode> toCalcValue(const CSSToLengthConversionData& conversionData) const
424     {
425         OwnPtr<CalcExpressionNode> left(m_leftSide->toCalcValue(conversionData));
426         if (!left)
427             return nullptr;
428         OwnPtr<CalcExpressionNode> right(m_rightSide->toCalcValue(conversionData));
429         if (!right)
430             return nullptr;
431         return adoptPtr(new CalcExpressionBinaryOperation(left.release(), right.release(), m_operator));
432     }
433 
doubleValue() const434     virtual double doubleValue() const
435     {
436         return evaluate(m_leftSide->doubleValue(), m_rightSide->doubleValue());
437     }
438 
computeLengthPx(const CSSToLengthConversionData & conversionData) const439     virtual double computeLengthPx(const CSSToLengthConversionData& conversionData) const
440     {
441         const double leftValue = m_leftSide->computeLengthPx(conversionData);
442         const double rightValue = m_rightSide->computeLengthPx(conversionData);
443         return evaluate(leftValue, rightValue);
444     }
445 
buildCSSText(const String & leftExpression,const String & rightExpression,CalcOperator op)446     static String buildCSSText(const String& leftExpression, const String& rightExpression, CalcOperator op)
447     {
448         StringBuilder result;
449         result.append('(');
450         result.append(leftExpression);
451         result.append(' ');
452         result.append(static_cast<char>(op));
453         result.append(' ');
454         result.append(rightExpression);
455         result.append(')');
456 
457         return result.toString();
458     }
459 
customCSSText() const460     virtual String customCSSText() const
461     {
462         return buildCSSText(m_leftSide->customCSSText(), m_rightSide->customCSSText(), m_operator);
463     }
464 
serializeResolvingVariables(const HashMap<AtomicString,String> & variables) const465     virtual String serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
466     {
467         return buildCSSText(m_leftSide->serializeResolvingVariables(variables), m_rightSide->serializeResolvingVariables(variables), m_operator);
468     }
469 
hasVariableReference() const470     virtual bool hasVariableReference() const
471     {
472         return m_leftSide->hasVariableReference() || m_rightSide->hasVariableReference();
473     }
474 
equals(const CSSCalcExpressionNode & exp) const475     virtual bool equals(const CSSCalcExpressionNode& exp) const
476     {
477         if (type() != exp.type())
478             return false;
479 
480         const CSSCalcBinaryOperation& other = static_cast<const CSSCalcBinaryOperation&>(exp);
481         return compareCSSValuePtr(m_leftSide, other.m_leftSide)
482             && compareCSSValuePtr(m_rightSide, other.m_rightSide)
483             && m_operator == other.m_operator;
484     }
485 
type() const486     virtual Type type() const { return CssCalcBinaryOperation; }
487 
primitiveType() const488     virtual CSSPrimitiveValue::UnitTypes primitiveType() const
489     {
490         switch (m_category) {
491         case CalcNumber:
492             ASSERT(m_leftSide->category() == CalcNumber && m_rightSide->category() == CalcNumber);
493             if (m_isInteger)
494                 return CSSPrimitiveValue::CSS_PARSER_INTEGER;
495             return CSSPrimitiveValue::CSS_NUMBER;
496         case CalcLength:
497         case CalcPercent: {
498             if (m_leftSide->category() == CalcNumber)
499                 return m_rightSide->primitiveType();
500             if (m_rightSide->category() == CalcNumber)
501                 return m_leftSide->primitiveType();
502             CSSPrimitiveValue::UnitTypes leftType = m_leftSide->primitiveType();
503             if (leftType == m_rightSide->primitiveType())
504                 return leftType;
505             return CSSPrimitiveValue::CSS_UNKNOWN;
506         }
507         case CalcVariable:
508             return CSSPrimitiveValue::CSS_VARIABLE_NAME;
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 
519 private:
CSSCalcBinaryOperation(PassRefPtr<CSSCalcExpressionNode> leftSide,PassRefPtr<CSSCalcExpressionNode> rightSide,CalcOperator op,CalculationCategory category)520     CSSCalcBinaryOperation(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op, CalculationCategory category)
521         : CSSCalcExpressionNode(category, isIntegerResult(leftSide.get(), rightSide.get(), op))
522         , m_leftSide(leftSide)
523         , m_rightSide(rightSide)
524         , m_operator(op)
525     {
526     }
527 
getNumberSide(CSSCalcExpressionNode * leftSide,CSSCalcExpressionNode * rightSide)528     static CSSCalcExpressionNode* getNumberSide(CSSCalcExpressionNode* leftSide, CSSCalcExpressionNode* rightSide)
529     {
530         if (leftSide->category() == CalcNumber)
531             return leftSide;
532         if (rightSide->category() == CalcNumber)
533             return rightSide;
534         return 0;
535     }
536 
evaluate(double leftSide,double rightSide) const537     double evaluate(double leftSide, double rightSide) const
538     {
539         return evaluateOperator(leftSide, rightSide, m_operator);
540     }
541 
evaluateOperator(double leftValue,double rightValue,CalcOperator op)542     static double evaluateOperator(double leftValue, double rightValue, CalcOperator op)
543     {
544         switch (op) {
545         case CalcAdd:
546             return leftValue + rightValue;
547         case CalcSubtract:
548             return leftValue - rightValue;
549         case CalcMultiply:
550             return leftValue * rightValue;
551         case CalcDivide:
552             if (rightValue)
553                 return leftValue / rightValue;
554             return std::numeric_limits<double>::quiet_NaN();
555         }
556         return 0;
557     }
558 
559     const RefPtr<CSSCalcExpressionNode> m_leftSide;
560     const RefPtr<CSSCalcExpressionNode> m_rightSide;
561     const CalcOperator m_operator;
562 };
563 
checkDepthAndIndex(int * depth,unsigned index,CSSParserValueList * tokens)564 static ParseState checkDepthAndIndex(int* depth, unsigned index, CSSParserValueList* tokens)
565 {
566     (*depth)++;
567     if (*depth > maxExpressionDepth)
568         return TooDeep;
569     if (index >= tokens->size())
570         return NoMoreTokens;
571     return OK;
572 }
573 
574 class CSSCalcExpressionNodeParser {
575 public:
parseCalc(CSSParserValueList * tokens)576     PassRefPtr<CSSCalcExpressionNode> parseCalc(CSSParserValueList* tokens)
577     {
578         unsigned index = 0;
579         Value result;
580         bool ok = parseValueExpression(tokens, 0, &index, &result);
581         ASSERT_WITH_SECURITY_IMPLICATION(index <= tokens->size());
582         if (!ok || index != tokens->size())
583             return 0;
584         return result.value;
585     }
586 
587 private:
588     struct Value {
589         RefPtr<CSSCalcExpressionNode> value;
590     };
591 
operatorValue(CSSParserValueList * tokens,unsigned index)592     char operatorValue(CSSParserValueList* tokens, unsigned index)
593     {
594         if (index >= tokens->size())
595             return 0;
596         CSSParserValue* value = tokens->valueAt(index);
597         if (value->unit != CSSParserValue::Operator)
598             return 0;
599 
600         return value->iValue;
601     }
602 
parseValue(CSSParserValueList * tokens,unsigned * index,Value * result)603     bool parseValue(CSSParserValueList* tokens, unsigned* index, Value* result)
604     {
605         CSSParserValue* parserValue = tokens->valueAt(*index);
606         if (parserValue->unit == CSSParserValue::Operator || parserValue->unit == CSSParserValue::Function)
607             return false;
608 
609         RefPtr<CSSValue> value = parserValue->createCSSValue();
610         if (!value || !value->isPrimitiveValue())
611             return false;
612 
613         result->value = CSSCalcPrimitiveValue::create(toCSSPrimitiveValue(value.get()), parserValue->isInt);
614 
615         ++*index;
616         return true;
617     }
618 
parseValueTerm(CSSParserValueList * tokens,int depth,unsigned * index,Value * result)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 
parseValueMultiplicativeExpression(CSSParserValueList * tokens,int depth,unsigned * index,Value * result)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(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
657             if (!result->value)
658                 return false;
659         }
660 
661         ASSERT_WITH_SECURITY_IMPLICATION(*index <= tokens->size());
662         return true;
663     }
664 
parseAdditiveValueExpression(CSSParserValueList * tokens,int depth,unsigned * index,Value * result)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(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
684             if (!result->value)
685                 return false;
686         }
687 
688         ASSERT_WITH_SECURITY_IMPLICATION(*index <= tokens->size());
689         return true;
690     }
691 
parseValueExpression(CSSParserValueList * tokens,int depth,unsigned * index,Value * result)692     bool parseValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
693     {
694         return parseAdditiveValueExpression(tokens, depth, index, result);
695     }
696 };
697 
createExpressionNode(PassRefPtr<CSSPrimitiveValue> value,bool isInteger)698 PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(PassRefPtr<CSSPrimitiveValue> value, bool isInteger)
699 {
700     return CSSCalcPrimitiveValue::create(value, isInteger);
701 }
702 
createExpressionNode(PassRefPtr<CSSCalcExpressionNode> leftSide,PassRefPtr<CSSCalcExpressionNode> rightSide,CalcOperator op)703 PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
704 {
705     return CSSCalcBinaryOperation::create(leftSide, rightSide, op);
706 }
707 
createExpressionNode(const CalcExpressionNode * node,float zoom)708 PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(const CalcExpressionNode* node, float zoom)
709 {
710     switch (node->type()) {
711     case CalcExpressionNodeNumber: {
712         float value = toCalcExpressionNumber(node)->value();
713         return createExpressionNode(CSSPrimitiveValue::create(value, CSSPrimitiveValue::CSS_NUMBER), value == trunc(value));
714     }
715     case CalcExpressionNodeLength:
716         return createExpressionNode(toCalcExpressionLength(node)->length(), zoom);
717     case CalcExpressionNodeBinaryOperation: {
718         const CalcExpressionBinaryOperation* binaryNode = toCalcExpressionBinaryOperation(node);
719         return createExpressionNode(createExpressionNode(binaryNode->leftSide(), zoom), createExpressionNode(binaryNode->rightSide(), zoom), binaryNode->getOperator());
720     }
721     case CalcExpressionNodeBlendLength: {
722         // FIXME(crbug.com/269320): Create a CSSCalcExpressionNode equivalent of CalcExpressionBlendLength.
723         const CalcExpressionBlendLength* blendNode = toCalcExpressionBlendLength(node);
724         const double progress = blendNode->progress();
725         const bool isInteger = !progress || (progress == 1);
726         return createExpressionNode(
727             createExpressionNode(
728                 createExpressionNode(blendNode->from(), zoom),
729                 createExpressionNode(CSSPrimitiveValue::create(1 - progress, CSSPrimitiveValue::CSS_NUMBER), isInteger),
730                 CalcMultiply),
731             createExpressionNode(
732                 createExpressionNode(blendNode->to(), zoom),
733                 createExpressionNode(CSSPrimitiveValue::create(progress, CSSPrimitiveValue::CSS_NUMBER), isInteger),
734                 CalcMultiply),
735             CalcAdd);
736     }
737     case CalcExpressionNodeUndefined:
738         ASSERT_NOT_REACHED();
739         return 0;
740     }
741     ASSERT_NOT_REACHED();
742     return 0;
743 }
744 
createExpressionNode(const Length & length,float zoom)745 PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(const Length& length, float zoom)
746 {
747     switch (length.type()) {
748     case Percent:
749     case ViewportPercentageWidth:
750     case ViewportPercentageHeight:
751     case ViewportPercentageMin:
752     case ViewportPercentageMax:
753     case Fixed:
754         return createExpressionNode(CSSPrimitiveValue::create(length, zoom), length.value() == trunc(length.value()));
755     case Calculated:
756         return createExpressionNode(length.calculationValue()->expression(), zoom);
757     case Auto:
758     case Intrinsic:
759     case MinIntrinsic:
760     case MinContent:
761     case MaxContent:
762     case FillAvailable:
763     case FitContent:
764     case ExtendToZoom:
765     case Undefined:
766         ASSERT_NOT_REACHED();
767         return 0;
768     }
769     ASSERT_NOT_REACHED();
770     return 0;
771 }
772 
create(CSSParserString name,CSSParserValueList * parserValueList,ValueRange range)773 PassRefPtr<CSSCalcValue> CSSCalcValue::create(CSSParserString name, CSSParserValueList* parserValueList, ValueRange range)
774 {
775     CSSCalcExpressionNodeParser parser;
776     RefPtr<CSSCalcExpressionNode> expression;
777 
778     if (equalIgnoringCase(name, "calc(") || equalIgnoringCase(name, "-webkit-calc("))
779         expression = parser.parseCalc(parserValueList);
780     // FIXME calc (http://webkit.org/b/16662) Add parsing for min and max here
781 
782     return expression ? adoptRef(new CSSCalcValue(expression, range)) : 0;
783 }
784 
create(PassRefPtr<CSSCalcExpressionNode> expression,ValueRange range)785 PassRefPtr<CSSCalcValue> CSSCalcValue::create(PassRefPtr<CSSCalcExpressionNode> expression, ValueRange range)
786 {
787     return adoptRef(new CSSCalcValue(expression, range));
788 }
789 
790 } // namespace WebCore
791