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, ¤tIndex, 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