CSS calc parsing stage
[WebKit-https.git] / Source / WebCore / css / CSSCalculationValue.cpp
1 /*
2  * Copyright (C) 2011 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 "CSSStyleSelector.h"
35 #include "CSSValueList.h"
36 #include "Length.h"
37
38 #include <wtf/OwnPtr.h>
39 #include <wtf/PassOwnPtr.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 CalculationCategory unitCategory(CSSPrimitiveValue::UnitTypes type)
53 {
54     switch (type) {
55     case CSSPrimitiveValue::CSS_NUMBER:
56     case CSSPrimitiveValue::CSS_PARSER_INTEGER:
57         return CalcNumber;
58     case CSSPrimitiveValue::CSS_PERCENTAGE:
59         return CalcPercent;
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         return CalcLength;
70     default:
71         return CalcOther;
72     }
73 }
74     
75 String CSSCalcValue::customCssText() const
76 {
77     return "";
78 }
79     
80 CSSCalcExpressionNode::~CSSCalcExpressionNode() 
81 {
82 }
83     
84 class CSSCalcPrimitiveValue : public CSSCalcExpressionNode {
85 public:
86
87     static PassRefPtr<CSSCalcPrimitiveValue> create(CSSPrimitiveValue* value, bool isInteger)
88     {
89         return adoptRef(new CSSCalcPrimitiveValue(value, isInteger));
90     }
91     
92     virtual String cssText() const
93     {
94         return m_value->cssText();
95     }
96
97     
98 private:
99     explicit CSSCalcPrimitiveValue(CSSPrimitiveValue* value, bool isInteger)
100         : CSSCalcExpressionNode(unitCategory((CSSPrimitiveValue::UnitTypes)value->primitiveType()), isInteger)
101         , m_value(value)                 
102     {
103     }
104
105     RefPtr<CSSPrimitiveValue> m_value;
106 };
107
108 static const CalculationCategory addSubtractResult[CalcOther][CalcOther] = {
109     { CalcNumber,        CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
110     { CalcOther,         CalcLength,        CalcPercentLength, CalcOther,         CalcPercentLength },
111     { CalcPercentNumber, CalcPercentLength, CalcPercent,       CalcPercentNumber, CalcPercentLength },
112     { CalcPercentNumber, CalcOther,         CalcPercentNumber, CalcPercentNumber, CalcOther },
113     { CalcOther,         CalcPercentLength, CalcPercentLength, CalcOther,         CalcPercentLength },
114 };    
115     
116 class CSSCalcBinaryOperation : public CSSCalcExpressionNode {
117 public:
118     static PassRefPtr<CSSCalcBinaryOperation> create(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op)
119     {
120         CalculationCategory leftCategory = leftSide->category();
121         CalculationCategory rightCategory = rightSide->category();
122         CalculationCategory newCategory = CalcOther;
123         
124         ASSERT(leftCategory != CalcOther && rightCategory != CalcOther);
125         
126         switch (op) {
127         case CalcAdd:
128         case CalcSubtract:             
129             if (leftCategory == CalcOther || rightCategory == CalcOther)
130                 return 0;
131             newCategory = addSubtractResult[leftCategory][rightCategory];
132             break;   
133                 
134         case CalcMultiply:
135             if (leftCategory != CalcNumber && rightCategory != CalcNumber) 
136                 return 0;
137             
138             newCategory = leftCategory == CalcNumber ? rightCategory : leftCategory;
139             break;
140                 
141         case CalcDivide:
142         case CalcMod:
143             if (rightCategory != CalcNumber || rightSide->isZero())
144                 return 0;
145             newCategory = leftCategory;
146             break;
147         }
148         
149         if (newCategory == CalcOther)
150             return 0;
151             
152         return adoptRef(new CSSCalcBinaryOperation(leftSide, rightSide, op, newCategory));
153     }
154     
155 private:
156     CSSCalcBinaryOperation(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op, CalculationCategory category)
157         : CSSCalcExpressionNode(category, leftSide->isInteger() && rightSide->isInteger())
158         , m_leftSide(leftSide)
159         , m_rightSide(rightSide)
160         , m_operator(op)
161     {
162     }
163     
164     const RefPtr<CSSCalcExpressionNode> m_leftSide;
165     const RefPtr<CSSCalcExpressionNode> m_rightSide;
166     const CalcOperator m_operator;
167 };
168
169 static ParseState checkDepthAndIndex(int* depth, unsigned index, CSSParserValueList* tokens)
170 {
171     (*depth)++;
172     if (*depth > maxExpressionDepth)
173         return TooDeep;
174     if (index >= tokens->size())
175         return NoMoreTokens;
176     return OK;
177 }
178
179 class CSSCalcExpressionNodeParser {
180 public:
181     PassRefPtr<CSSCalcExpressionNode> parseCalc(CSSParserValueList* tokens)
182     {
183         unsigned index = 0;
184         Value result;
185         bool ok = parseValueExpression(tokens, 0, &index, &result);
186         ASSERT(index <= tokens->size());
187         if (!ok || index != tokens->size())
188             return 0;
189         return result.value;
190     }
191
192 private:
193     struct Value {
194         RefPtr<CSSCalcExpressionNode> value;
195     };
196
197     char operatorValue(CSSParserValueList* tokens, unsigned index)
198     {
199         if (index >= tokens->size())
200             return 0;
201         CSSParserValue* value = tokens->valueAt(index);
202         if (value->unit != CSSParserValue::Operator)
203             return 0;
204
205         return value->iValue;
206     }
207
208     bool parseValue(CSSParserValueList* tokens, unsigned* index, Value* result)
209     {
210         CSSParserValue* parserValue = tokens->valueAt(*index);
211         if (parserValue->unit == CSSParserValue::Operator || parserValue->unit == CSSParserValue::Function)
212             return false;
213
214         RefPtr<CSSValue> value = parserValue->createCSSValue();
215         if (!value || !value->isPrimitiveValue())
216             return false;
217
218         CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get());
219         result->value = CSSCalcPrimitiveValue::create(primitiveValue, parserValue->isInt);
220
221         ++*index;
222         return true;
223     }
224
225     bool parseValueTerm(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
226     {
227         if (checkDepthAndIndex(&depth, *index, tokens) != OK)
228             return false;    
229         
230         if (operatorValue(tokens, *index) == '(') {
231             unsigned currentIndex = *index + 1;
232             if (!parseValueExpression(tokens, depth, &currentIndex, result))
233                 return false;
234
235             if (operatorValue(tokens, currentIndex) != ')')
236                 return false;
237             *index = currentIndex + 1;
238             return true;
239         }
240
241         return parseValue(tokens, index, result);
242     }
243
244     bool parseValueMultiplicativeExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
245     {
246         if (checkDepthAndIndex(&depth, *index, tokens) != OK)
247             return false;    
248
249         if (!parseValueTerm(tokens, depth, index, result))
250             return false;
251
252         while (*index < tokens->size() - 1) {
253             char operatorCharacter = operatorValue(tokens, *index);
254             if (operatorCharacter != CalcMultiply && operatorCharacter != CalcDivide && operatorCharacter != CalcMod)
255                 break;
256             ++*index;
257
258             Value rhs;
259             if (!parseValueTerm(tokens, depth, index, &rhs))
260                 return false;
261
262             result->value = CSSCalcBinaryOperation::create(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
263             if (!result->value)
264                 return false;
265         }
266
267         ASSERT(*index <= tokens->size());
268         return true;
269     }
270
271     bool parseAdditiveValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
272     {
273         if (checkDepthAndIndex(&depth, *index, tokens) != OK)
274             return false;    
275
276         if (!parseValueMultiplicativeExpression(tokens, depth, index, result))
277             return false;
278
279         while (*index < tokens->size() - 1) {
280             char operatorCharacter = operatorValue(tokens, *index);
281             if (operatorCharacter != CalcAdd && operatorCharacter != CalcSubtract)
282                 break;
283             ++*index;
284
285             Value rhs;
286             if (!parseValueMultiplicativeExpression(tokens, depth, index, &rhs))
287                 return false;
288
289             result->value = CSSCalcBinaryOperation::create(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter));
290             if (!result->value)
291                 return false;
292         }
293
294         ASSERT(*index <= tokens->size());
295         return true;
296     }
297
298     bool parseValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result)
299     {
300         return parseAdditiveValueExpression(tokens, depth, index, result);
301     }
302 };
303
304 PassRefPtr<CSSCalcValue> CSSCalcValue::create(CSSParserString name, CSSParserValueList* parserValueList)
305 {    
306     CSSCalcExpressionNodeParser parser;    
307     RefPtr<CSSCalcExpressionNode> expression;
308     
309     if (equalIgnoringCase(name, "-webkit-calc("))
310         expression = parser.parseCalc(parserValueList);    
311     // FIXME calc (http://webkit.org/b/16662) Add parsing for min and max here
312
313     return expression ? adoptRef(new CSSCalcValue(expression)) : 0;
314 }
315
316 } // namespace WebCore