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