Support the ch unit from css3-values
[WebKit-https.git] / Source / WebCore / css / CSSCalculationValue.cpp
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 "CSSCalculationValue.h"
33
34 #include "CSSValueList.h"
35 #include "Length.h"
36 #include "StyleResolver.h"
37 #include "WebCoreMemoryInstrumentation.h"
38
39 #include <wtf/OwnPtr.h>
40 #include <wtf/PassOwnPtr.h>
41 #include <wtf/text/StringBuilder.h>
42
43 static const int maxExpressionDepth = 100;
44
45 enum ParseState {
46     OK,
47     TooDeep,
48     NoMoreTokens
49 };
50
51 namespace WebCore {
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_PERCENTAGE:
60         return CalcPercent;
61     case CSSPrimitiveValue::CSS_EMS:
62     case CSSPrimitiveValue::CSS_EXS:
63     case CSSPrimitiveValue::CSS_PX:
64     case CSSPrimitiveValue::CSS_CM:
65     case CSSPrimitiveValue::CSS_MM:
66     case CSSPrimitiveValue::CSS_IN:
67     case CSSPrimitiveValue::CSS_PT:
68     case CSSPrimitiveValue::CSS_PC:
69     case CSSPrimitiveValue::CSS_REMS:
70     case CSSPrimitiveValue::CSS_CHS:
71         return CalcLength;
72 #if ENABLE(CSS_VARIABLES)
73     case CSSPrimitiveValue::CSS_VARIABLE_NAME:
74         return CalcVariable;
75 #endif
76     default:
77         return CalcOther;
78     }
79 }
80
81 static String buildCssText(const String& expression)
82 {
83     StringBuilder result;
84     result.append("calc");
85     bool expressionHasSingleTerm = expression[0] != '(';
86     if (expressionHasSingleTerm)
87         result.append('(');
88     result.append(expression);
89     if (expressionHasSingleTerm)
90         result.append(')');
91     return result.toString(); 
92 }
93
94 String CSSCalcValue::customCssText() const
95 {
96     return buildCssText(m_expression->customCssText());
97 }
98
99 bool CSSCalcValue::equals(const CSSCalcValue& other) const
100 {
101     return compareCSSValuePtr(m_expression, other.m_expression);
102 }
103
104 #if ENABLE(CSS_VARIABLES)
105 String CSSCalcValue::customSerializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
106 {
107     return buildCssText(m_expression->serializeResolvingVariables(variables));
108 }
109
110 bool CSSCalcValue::hasVariableReference() const
111 {
112     return m_expression->hasVariableReference();
113 }
114 #endif
115
116 void CSSCalcValue::reportDescendantMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
117 {
118     MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::CSS);
119 }
120     
121 double CSSCalcValue::clampToPermittedRange(double value) const
122 {
123     return m_nonNegative && value < 0 ? 0 : value;
124 }    
125     
126 double CSSCalcValue::doubleValue() const 
127
128     return clampToPermittedRange(m_expression->doubleValue());
129 }
130
131 double CSSCalcValue::computeLengthPx(RenderStyle* currentStyle, RenderStyle* rootStyle, double multiplier, bool computingFontSize) const
132 {
133     return clampToPermittedRange(m_expression->computeLengthPx(currentStyle, rootStyle, multiplier, computingFontSize));
134 }
135     
136 CSSCalcExpressionNode::~CSSCalcExpressionNode() 
137 {
138 }
139     
140 class CSSCalcPrimitiveValue : public CSSCalcExpressionNode {
141     WTF_MAKE_FAST_ALLOCATED;
142 public:
143
144     static PassRefPtr<CSSCalcPrimitiveValue> create(CSSPrimitiveValue* value, bool isInteger)
145     {
146         return adoptRef(new CSSCalcPrimitiveValue(value, isInteger));
147     }
148     
149     virtual bool isZero() const
150     {
151         return !m_value->getDoubleValue();
152     }
153
154     virtual String customCssText() const
155     {
156         return m_value->cssText();
157     }
158
159 #if ENABLE(CSS_VARIABLES)
160     virtual String serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
161     {
162         return m_value->customSerializeResolvingVariables(variables);
163     }
164     
165     virtual bool hasVariableReference() const
166     {
167         return m_value->isVariableName();
168     }
169 #endif
170
171     virtual PassOwnPtr<CalcExpressionNode> toCalcValue(RenderStyle* style, RenderStyle* rootStyle, double zoom) const
172     {
173         switch (m_category) {
174         case CalcNumber:
175             return adoptPtr(new CalcExpressionNumber(m_value->getFloatValue()));
176         case CalcLength:
177             return adoptPtr(new CalcExpressionNumber(m_value->computeLength<float>(style, rootStyle, zoom)));
178         case CalcPercent:
179         case CalcPercentLength:
180             return adoptPtr(new CalcExpressionLength(StyleResolver::convertToFloatLength(m_value.get(), style, rootStyle, zoom)));
181         // Only types that could be part of a Length expression can be converted
182         // to a CalcExpressionNode. CalcPercentNumber makes no sense as a Length.
183         case CalcPercentNumber:
184 #if ENABLE(CSS_VARIABLES)
185         case CalcVariable:
186 #endif
187         case CalcOther:
188             ASSERT_NOT_REACHED();
189         }
190         return nullptr;
191     }
192
193     virtual double doubleValue() const
194     {
195         switch (m_category) {
196         case CalcNumber:
197         case CalcPercent:                
198             return m_value->getDoubleValue();
199         case CalcLength:
200         case CalcPercentLength:
201         case CalcPercentNumber:
202 #if ENABLE(CSS_VARIABLES)
203         case CalcVariable:
204 #endif
205         case CalcOther:
206             ASSERT_NOT_REACHED();
207             break;
208         }
209         return 0;
210     }
211     
212     virtual double computeLengthPx(RenderStyle* currentStyle, RenderStyle* rootStyle, double multiplier, bool computingFontSize) const
213     {
214         switch (m_category) {
215         case CalcLength:
216             return m_value->computeLength<double>(currentStyle, rootStyle, multiplier, computingFontSize);
217         case CalcPercent:
218         case CalcNumber:
219             return m_value->getDoubleValue();
220         case CalcPercentLength:
221         case CalcPercentNumber:
222 #if ENABLE(CSS_VARIABLES)
223         case CalcVariable:
224 #endif
225         case CalcOther:
226             ASSERT_NOT_REACHED();
227             break;
228         }
229         return 0;        
230     }
231
232     virtual bool equals(const CSSCalcExpressionNode& other) const
233     {
234         if (type() != other.type())
235             return false;
236
237         return compareCSSValuePtr(m_value, static_cast<const CSSCalcPrimitiveValue&>(other).m_value);
238     }
239
240     virtual void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const OVERRIDE
241     {
242         MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::CSS);
243         info.addMember(m_value, "value");
244     }
245
246     virtual Type type() const { return CssCalcPrimitiveValue; }
247     
248 private:
249     explicit CSSCalcPrimitiveValue(CSSPrimitiveValue* value, bool isInteger)
250         : CSSCalcExpressionNode(unitCategory((CSSPrimitiveValue::UnitTypes)value->primitiveType()), isInteger)
251         , m_value(value)
252     {
253     }
254
255     RefPtr<CSSPrimitiveValue> m_value;
256 };
257
258 static const CalculationCategory addSubtractResult[CalcOther][CalcOther] = {
259     { CalcNumber,        CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
260     { CalcOther,         CalcLength,        CalcPercentLength, CalcOther,         CalcPercentLength },
261     { CalcPercentNumber, CalcPercentLength, CalcPercent,       CalcPercentNumber, CalcPercentLength },
262     { CalcPercentNumber, CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
263     { CalcOther,         CalcPercentLength, CalcPercentLength, CalcOther,         CalcPercentLength },
264 };    
265
266 static CalculationCategory determineCategory(const CSSCalcExpressionNode& leftSide, const CSSCalcExpressionNode& rightSide, CalcOperator op)
267 {
268     CalculationCategory leftCategory = leftSide.category();
269     CalculationCategory rightCategory = rightSide.category();
270
271     if (leftCategory == CalcOther || rightCategory == CalcOther)
272         return CalcOther;
273
274 #if ENABLE(CSS_VARIABLES)
275     if (leftCategory == CalcVariable || rightCategory == CalcVariable)
276         return CalcVariable;
277 #endif
278
279     switch (op) {
280     case CalcAdd:
281     case CalcSubtract:             
282         return addSubtractResult[leftCategory][rightCategory];
283     case CalcMultiply:
284         if (leftCategory != CalcNumber && rightCategory != CalcNumber) 
285             return CalcOther;
286         return leftCategory == CalcNumber ? rightCategory : leftCategory;
287     case CalcDivide:
288         if (rightCategory != CalcNumber || rightSide.isZero())
289             return CalcOther;
290         return leftCategory;
291     }
292     
293     ASSERT_NOT_REACHED();
294     return CalcOther;
295 }
296
297 class CSSCalcBinaryOperation : public CSSCalcExpressionNode {
298
299 public:
300     static PassRefPtr<CSSCalcBinaryOperation> create(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
301     {
302         ASSERT(leftSide->category() != CalcOther && rightSide->category() != CalcOther);
303         
304         CalculationCategory newCategory = determineCategory(*leftSide, *rightSide, op);
305
306         if (newCategory == CalcOther)
307             return 0;
308
309         return adoptRef(new CSSCalcBinaryOperation(leftSide, rightSide, op, newCategory));
310     }
311     
312     virtual bool isZero() const
313     {
314         return !doubleValue();
315     }
316
317     virtual PassOwnPtr<CalcExpressionNode> toCalcValue(RenderStyle* style, RenderStyle* rootStyle, double zoom) const
318     {
319         OwnPtr<CalcExpressionNode> left(m_leftSide->toCalcValue(style, rootStyle, zoom));
320         if (!left)
321             return nullptr;
322         OwnPtr<CalcExpressionNode> right(m_rightSide->toCalcValue(style, rootStyle, zoom));
323         if (!right)
324             return nullptr;
325         return adoptPtr(new CalcExpressionBinaryOperation(left.release(), right.release(), m_operator));
326     }
327
328     virtual double doubleValue() const 
329     {
330         return evaluate(m_leftSide->doubleValue(), m_rightSide->doubleValue());
331     }
332     
333     virtual double computeLengthPx(RenderStyle* currentStyle, RenderStyle* rootStyle, double multiplier, bool computingFontSize) const
334     {
335         const double leftValue = m_leftSide->computeLengthPx(currentStyle, rootStyle, multiplier, computingFontSize);
336         const double rightValue = m_rightSide->computeLengthPx(currentStyle, rootStyle, multiplier, computingFontSize);
337         return evaluate(leftValue, rightValue);
338     }
339
340     virtual void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const OVERRIDE
341     {
342         MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::CSS);
343         info.addMember(m_leftSide, "leftSide");
344         info.addMember(m_rightSide, "rightSide");
345     }
346
347     static String buildCssText(const String& leftExpression, const String& rightExpression, CalcOperator op)
348     {
349         StringBuilder result;
350         result.append('(');
351         result.append(leftExpression);
352         result.append(' ');
353         result.append(static_cast<char>(op));
354         result.append(' ');
355         result.append(rightExpression);
356         result.append(')');
357     
358         return result.toString();  
359     }
360
361     virtual String customCssText() const
362     {
363         return buildCssText(m_leftSide->customCssText(), m_rightSide->customCssText(), m_operator);
364     }
365
366 #if ENABLE(CSS_VARIABLES)
367     virtual String serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
368     {
369         return buildCssText(m_leftSide->serializeResolvingVariables(variables), m_rightSide->serializeResolvingVariables(variables), m_operator);
370     }
371
372     virtual bool hasVariableReference() const
373     {
374         return m_leftSide->hasVariableReference() || m_rightSide->hasVariableReference();
375     }
376 #endif
377
378     virtual bool equals(const CSSCalcExpressionNode& exp) const
379     {
380         if (type() != exp.type())
381             return false;
382
383         const CSSCalcBinaryOperation& other = static_cast<const CSSCalcBinaryOperation&>(exp);
384         return compareCSSValuePtr(m_leftSide, other.m_leftSide)
385             && compareCSSValuePtr(m_rightSide, other.m_rightSide)
386             && m_operator == other.m_operator;
387     }
388
389     virtual Type type() const { return CssCalcBinaryOperation; }
390
391 private:
392     CSSCalcBinaryOperation(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op, CalculationCategory category)
393         : CSSCalcExpressionNode(category, leftSide->isInteger() && rightSide->isInteger())
394         , m_leftSide(leftSide)
395         , m_rightSide(rightSide)
396         , m_operator(op)
397     {
398     }
399     
400     double evaluate(double leftValue, double rightValue) const
401     {
402         switch (m_operator) {
403         case CalcAdd:
404             return leftValue + rightValue;
405         case CalcSubtract:
406             return leftValue - rightValue;
407         case CalcMultiply:
408             return leftValue * rightValue;
409         case CalcDivide:
410             if (rightValue)
411                 return leftValue / rightValue;
412             return std::numeric_limits<double>::quiet_NaN();
413         }
414         return 0;
415     }
416     
417     const RefPtr<CSSCalcExpressionNode> m_leftSide;
418     const RefPtr<CSSCalcExpressionNode> m_rightSide;
419     const CalcOperator m_operator;
420 };
421
422 static ParseState checkDepthAndIndex(int* depth, unsigned index, CSSParserValueList* tokens)
423 {
424     (*depth)++;
425     if (*depth > maxExpressionDepth)
426         return TooDeep;
427     if (index >= tokens->size())
428         return NoMoreTokens;
429     return OK;
430 }
431
432 class CSSCalcExpressionNodeParser {
433 public:
434     PassRefPtr<CSSCalcExpressionNode> parseCalc(CSSParserValueList* tokens)
435     {
436         unsigned index = 0;
437         Value result;
438         bool ok = parseValueExpression(tokens, 0, &index, &result);
439         ASSERT_WITH_SECURITY_IMPLICATION(index <= tokens->size());
440         if (!ok || index != tokens->size())
441             return 0;
442         return result.value;
443     }
444
445 private:
446     struct Value {
447         RefPtr<CSSCalcExpressionNode> value;
448     };
449
450     char operatorValue(CSSParserValueList* tokens, unsigned index)
451     {
452         if (index >= tokens->size())
453             return 0;
454         CSSParserValue* value = tokens->valueAt(index);
455         if (value->unit != CSSParserValue::Operator)
456             return 0;
457
458         return value->iValue;
459     }
460
461     bool parseValue(CSSParserValueList* tokens, unsigned* index, Value* result)
462     {
463         CSSParserValue* parserValue = tokens->valueAt(*index);
464         if (parserValue->unit == CSSParserValue::Operator || parserValue->unit == CSSParserValue::Function)
465             return false;
466
467         RefPtr<CSSValue> value = parserValue->createCSSValue();
468         if (!value || !value->isPrimitiveValue())
469             return false;
470
471         CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get());
472         result->value = CSSCalcPrimitiveValue::create(primitiveValue, parserValue->isInt);
473
474         ++*index;
475         return true;
476     }
477
478     bool parseValueTerm(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
479     {
480         if (checkDepthAndIndex(&depth, *index, tokens) != OK)
481             return false;    
482         
483         if (operatorValue(tokens, *index) == '(') {
484             unsigned currentIndex = *index + 1;
485             if (!parseValueExpression(tokens, depth, &currentIndex, result))
486                 return false;
487
488             if (operatorValue(tokens, currentIndex) != ')')
489                 return false;
490             *index = currentIndex + 1;
491             return true;
492         }
493
494         return parseValue(tokens, index, result);
495     }
496
497     bool parseValueMultiplicativeExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
498     {
499         if (checkDepthAndIndex(&depth, *index, tokens) != OK)
500             return false;    
501
502         if (!parseValueTerm(tokens, depth, index, result))
503             return false;
504
505         while (*index < tokens->size() - 1) {
506             char operatorCharacter = operatorValue(tokens, *index);
507             if (operatorCharacter != CalcMultiply && operatorCharacter != CalcDivide)
508                 break;
509             ++*index;
510
511             Value rhs;
512             if (!parseValueTerm(tokens, depth, index, &rhs))
513                 return false;
514
515             result->value = CSSCalcBinaryOperation::create(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
516             if (!result->value)
517                 return false;
518         }
519
520         ASSERT_WITH_SECURITY_IMPLICATION(*index <= tokens->size());
521         return true;
522     }
523
524     bool parseAdditiveValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
525     {
526         if (checkDepthAndIndex(&depth, *index, tokens) != OK)
527             return false;    
528
529         if (!parseValueMultiplicativeExpression(tokens, depth, index, result))
530             return false;
531
532         while (*index < tokens->size() - 1) {
533             char operatorCharacter = operatorValue(tokens, *index);
534             if (operatorCharacter != CalcAdd && operatorCharacter != CalcSubtract)
535                 break;
536             ++*index;
537
538             Value rhs;
539             if (!parseValueMultiplicativeExpression(tokens, depth, index, &rhs))
540                 return false;
541
542             result->value = CSSCalcBinaryOperation::create(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
543             if (!result->value)
544                 return false;
545         }
546
547         ASSERT_WITH_SECURITY_IMPLICATION(*index <= tokens->size());
548         return true;
549     }
550
551     bool parseValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
552     {
553         return parseAdditiveValueExpression(tokens, depth, index, result);
554     }
555 };
556
557 PassRefPtr<CSSCalcValue> CSSCalcValue::create(CSSParserString name, CSSParserValueList* parserValueList, CalculationPermittedValueRange range)
558 {    
559     CSSCalcExpressionNodeParser parser;    
560     RefPtr<CSSCalcExpressionNode> expression;
561     
562     if (equalIgnoringCase(name, "calc(") || equalIgnoringCase(name, "-webkit-calc("))
563         expression = parser.parseCalc(parserValueList);    
564     // FIXME calc (http://webkit.org/b/16662) Add parsing for min and max here
565
566     return expression ? adoptRef(new CSSCalcValue(expression, range)) : 0;
567 }
568
569 } // namespace WebCore