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 50 static CalculationCategory unitCategory(CSSPrimitiveValue::UnitType 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 case CSSPrimitiveValue::CSS_VW: 69 case CSSPrimitiveValue::CSS_VH: 70 case CSSPrimitiveValue::CSS_VMIN: 71 case CSSPrimitiveValue::CSS_VMAX: 72 return CalcLength; 73 // FIXME: Support angle, time and frequency units. 74 // http://www.w3.org/TR/css3-values/#calc-notation 75 default: 76 return CalcOther; 77 } 78 } 79 80 static bool hasDoubleValue(CSSPrimitiveValue::UnitType type) 81 { 82 switch (type) { 83 case CSSPrimitiveValue::CSS_NUMBER: 84 case CSSPrimitiveValue::CSS_PARSER_INTEGER: 85 case CSSPrimitiveValue::CSS_PERCENTAGE: 86 case CSSPrimitiveValue::CSS_EMS: 87 case CSSPrimitiveValue::CSS_EXS: 88 case CSSPrimitiveValue::CSS_CHS: 89 case CSSPrimitiveValue::CSS_REMS: 90 case CSSPrimitiveValue::CSS_PX: 91 case CSSPrimitiveValue::CSS_CM: 92 case CSSPrimitiveValue::CSS_MM: 93 case CSSPrimitiveValue::CSS_IN: 94 case CSSPrimitiveValue::CSS_PT: 95 case CSSPrimitiveValue::CSS_PC: 96 case CSSPrimitiveValue::CSS_DEG: 97 case CSSPrimitiveValue::CSS_RAD: 98 case CSSPrimitiveValue::CSS_GRAD: 99 case CSSPrimitiveValue::CSS_MS: 100 case CSSPrimitiveValue::CSS_S: 101 case CSSPrimitiveValue::CSS_HZ: 102 case CSSPrimitiveValue::CSS_KHZ: 103 case CSSPrimitiveValue::CSS_DIMENSION: 104 case CSSPrimitiveValue::CSS_VW: 105 case CSSPrimitiveValue::CSS_VH: 106 case CSSPrimitiveValue::CSS_VMIN: 107 case CSSPrimitiveValue::CSS_VMAX: 108 case CSSPrimitiveValue::CSS_DPPX: 109 case CSSPrimitiveValue::CSS_DPI: 110 case CSSPrimitiveValue::CSS_DPCM: 111 case CSSPrimitiveValue::CSS_FR: 112 return true; 113 case CSSPrimitiveValue::CSS_UNKNOWN: 114 case CSSPrimitiveValue::CSS_STRING: 115 case CSSPrimitiveValue::CSS_URI: 116 case CSSPrimitiveValue::CSS_IDENT: 117 case CSSPrimitiveValue::CSS_ATTR: 118 case CSSPrimitiveValue::CSS_COUNTER: 119 case CSSPrimitiveValue::CSS_RECT: 120 case CSSPrimitiveValue::CSS_RGBCOLOR: 121 case CSSPrimitiveValue::CSS_PAIR: 122 case CSSPrimitiveValue::CSS_UNICODE_RANGE: 123 case CSSPrimitiveValue::CSS_PARSER_OPERATOR: 124 case CSSPrimitiveValue::CSS_PARSER_HEXCOLOR: 125 case CSSPrimitiveValue::CSS_PARSER_IDENTIFIER: 126 case CSSPrimitiveValue::CSS_TURN: 127 case CSSPrimitiveValue::CSS_COUNTER_NAME: 128 case CSSPrimitiveValue::CSS_SHAPE: 129 case CSSPrimitiveValue::CSS_QUAD: 130 case CSSPrimitiveValue::CSS_CALC: 131 case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER: 132 case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH: 133 case CSSPrimitiveValue::CSS_PROPERTY_ID: 134 case CSSPrimitiveValue::CSS_VALUE_ID: 135 return false; 136 }; 137 ASSERT_NOT_REACHED(); 138 return false; 139 } 140 141 static String buildCSSText(const String& expression) 142 { 143 StringBuilder result; 144 result.append("calc"); 145 bool expressionHasSingleTerm = expression[0] != '('; 146 if (expressionHasSingleTerm) 147 result.append('('); 148 result.append(expression); 149 if (expressionHasSingleTerm) 150 result.append(')'); 151 return result.toString(); 152 } 153 154 String CSSCalcValue::customCSSText() const 155 { 156 return buildCSSText(m_expression->customCSSText()); 157 } 158 159 bool CSSCalcValue::equals(const CSSCalcValue& other) const 160 { 161 return compareCSSValuePtr(m_expression, other.m_expression); 162 } 163 164 double CSSCalcValue::clampToPermittedRange(double value) const 165 { 166 return m_nonNegative && value < 0 ? 0 : value; 167 } 168 169 double CSSCalcValue::doubleValue() const 170 { 171 return clampToPermittedRange(m_expression->doubleValue()); 172 } 173 174 double CSSCalcValue::computeLengthPx(const CSSToLengthConversionData& conversionData) const 175 { 176 return clampToPermittedRange(m_expression->computeLengthPx(conversionData)); 177 } 178 179 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(CSSCalcExpressionNode) 180 181 class CSSCalcPrimitiveValue FINAL : public CSSCalcExpressionNode { 182 WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED; 183 public: 184 185 static PassRefPtrWillBeRawPtr<CSSCalcPrimitiveValue> create(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> value, bool isInteger) 186 { 187 return adoptRefWillBeNoop(new CSSCalcPrimitiveValue(value, isInteger)); 188 } 189 190 static PassRefPtrWillBeRawPtr<CSSCalcPrimitiveValue> create(double value, CSSPrimitiveValue::UnitType type, bool isInteger) 191 { 192 if (std::isnan(value) || std::isinf(value)) 193 return nullptr; 194 return adoptRefWillBeNoop(new CSSCalcPrimitiveValue(CSSPrimitiveValue::create(value, type).get(), isInteger)); 195 } 196 197 virtual bool isZero() const OVERRIDE 198 { 199 return !m_value->getDoubleValue(); 200 } 201 202 virtual String customCSSText() const OVERRIDE 203 { 204 return m_value->cssText(); 205 } 206 207 virtual void accumulatePixelsAndPercent(const CSSToLengthConversionData& conversionData, PixelsAndPercent& value, float multiplier) const OVERRIDE 208 { 209 switch (m_category) { 210 case CalcLength: 211 value.pixels += m_value->computeLength<float>(conversionData) * multiplier; 212 break; 213 case CalcPercent: 214 ASSERT(m_value->isPercentage()); 215 value.percent += m_value->getDoubleValue() * multiplier; 216 break; 217 default: 218 ASSERT_NOT_REACHED(); 219 } 220 } 221 222 virtual double doubleValue() const OVERRIDE 223 { 224 if (hasDoubleValue(primitiveType())) 225 return m_value->getDoubleValue(); 226 ASSERT_NOT_REACHED(); 227 return 0; 228 } 229 230 virtual double computeLengthPx(const CSSToLengthConversionData& conversionData) const OVERRIDE 231 { 232 switch (m_category) { 233 case CalcLength: 234 return m_value->computeLength<double>(conversionData); 235 case CalcPercent: 236 case CalcNumber: 237 return m_value->getDoubleValue(); 238 case CalcPercentLength: 239 case CalcPercentNumber: 240 case CalcOther: 241 ASSERT_NOT_REACHED(); 242 break; 243 } 244 ASSERT_NOT_REACHED(); 245 return 0; 246 } 247 248 virtual void accumulateLengthArray(CSSLengthArray& lengthArray, double multiplier) const 249 { 250 ASSERT(category() != CalcNumber); 251 m_value->accumulateLengthArray(lengthArray, multiplier); 252 } 253 254 virtual bool equals(const CSSCalcExpressionNode& other) const OVERRIDE 255 { 256 if (type() != other.type()) 257 return false; 258 259 return compareCSSValuePtr(m_value, static_cast<const CSSCalcPrimitiveValue&>(other).m_value); 260 } 261 262 virtual Type type() const OVERRIDE { return CssCalcPrimitiveValue; } 263 virtual CSSPrimitiveValue::UnitType primitiveType() const OVERRIDE 264 { 265 return m_value->primitiveType(); 266 } 267 268 269 virtual void trace(Visitor* visitor) 270 { 271 visitor->trace(m_value); 272 CSSCalcExpressionNode::trace(visitor); 273 } 274 275 private: 276 CSSCalcPrimitiveValue(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> value, bool isInteger) 277 : CSSCalcExpressionNode(unitCategory(value->primitiveType()), isInteger) 278 , m_value(value) 279 { 280 } 281 282 RefPtrWillBeMember<CSSPrimitiveValue> m_value; 283 }; 284 285 static const CalculationCategory addSubtractResult[CalcOther][CalcOther] = { 286 // CalcNumber CalcLength CalcPercent CalcPercentNumber CalcPercentLength 287 /* CalcNumber */ { CalcNumber, CalcOther, CalcPercentNumber, CalcPercentNumber, CalcOther }, 288 /* CalcLength */ { CalcOther, CalcLength, CalcPercentLength, CalcOther, CalcPercentLength }, 289 /* CalcPercent */ { CalcPercentNumber, CalcPercentLength, CalcPercent, CalcPercentNumber, CalcPercentLength }, 290 /* CalcPercentNumber */ { CalcPercentNumber, CalcOther, CalcPercentNumber, CalcPercentNumber, CalcOther }, 291 /* CalcPercentLength */ { CalcOther, CalcPercentLength, CalcPercentLength, CalcOther, CalcPercentLength }, 292 }; 293 294 static CalculationCategory determineCategory(const CSSCalcExpressionNode& leftSide, const CSSCalcExpressionNode& rightSide, CalcOperator op) 295 { 296 CalculationCategory leftCategory = leftSide.category(); 297 CalculationCategory rightCategory = rightSide.category(); 298 299 if (leftCategory == CalcOther || rightCategory == CalcOther) 300 return CalcOther; 301 302 switch (op) { 303 case CalcAdd: 304 case CalcSubtract: 305 return addSubtractResult[leftCategory][rightCategory]; 306 case CalcMultiply: 307 if (leftCategory != CalcNumber && rightCategory != CalcNumber) 308 return CalcOther; 309 return leftCategory == CalcNumber ? rightCategory : leftCategory; 310 case CalcDivide: 311 if (rightCategory != CalcNumber || rightSide.isZero()) 312 return CalcOther; 313 return leftCategory; 314 } 315 316 ASSERT_NOT_REACHED(); 317 return CalcOther; 318 } 319 320 static bool isIntegerResult(const CSSCalcExpressionNode* leftSide, const CSSCalcExpressionNode* rightSide, CalcOperator op) 321 { 322 // Not testing for actual integer values. 323 // Performs W3C spec's type checking for calc integers. 324 // http://www.w3.org/TR/css3-values/#calc-type-checking 325 return op != CalcDivide && leftSide->isInteger() && rightSide->isInteger(); 326 } 327 328 class CSSCalcBinaryOperation FINAL : public CSSCalcExpressionNode { 329 public: 330 static PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> create(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> leftSide, PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> rightSide, CalcOperator op) 331 { 332 ASSERT(leftSide->category() != CalcOther && rightSide->category() != CalcOther); 333 334 CalculationCategory newCategory = determineCategory(*leftSide, *rightSide, op); 335 if (newCategory == CalcOther) 336 return nullptr; 337 338 return adoptRefWillBeNoop(new CSSCalcBinaryOperation(leftSide, rightSide, op, newCategory)); 339 } 340 341 static PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> createSimplified(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> leftSide, PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> rightSide, CalcOperator op) 342 { 343 CalculationCategory leftCategory = leftSide->category(); 344 CalculationCategory rightCategory = rightSide->category(); 345 ASSERT(leftCategory != CalcOther && rightCategory != CalcOther); 346 347 bool isInteger = isIntegerResult(leftSide.get(), rightSide.get(), op); 348 349 // Simplify numbers. 350 if (leftCategory == CalcNumber && rightCategory == CalcNumber) { 351 CSSPrimitiveValue::UnitType evaluationType = isInteger ? CSSPrimitiveValue::CSS_PARSER_INTEGER : CSSPrimitiveValue::CSS_NUMBER; 352 return CSSCalcPrimitiveValue::create(evaluateOperator(leftSide->doubleValue(), rightSide->doubleValue(), op), evaluationType, isInteger); 353 } 354 355 // Simplify addition and subtraction between same types. 356 if (op == CalcAdd || op == CalcSubtract) { 357 if (leftCategory == rightSide->category()) { 358 CSSPrimitiveValue::UnitType leftType = leftSide->primitiveType(); 359 if (hasDoubleValue(leftType)) { 360 CSSPrimitiveValue::UnitType rightType = rightSide->primitiveType(); 361 if (leftType == rightType) 362 return CSSCalcPrimitiveValue::create(evaluateOperator(leftSide->doubleValue(), rightSide->doubleValue(), op), leftType, isInteger); 363 CSSPrimitiveValue::UnitCategory leftUnitCategory = CSSPrimitiveValue::unitCategory(leftType); 364 if (leftUnitCategory != CSSPrimitiveValue::UOther && leftUnitCategory == CSSPrimitiveValue::unitCategory(rightType)) { 365 CSSPrimitiveValue::UnitType canonicalType = CSSPrimitiveValue::canonicalUnitTypeForCategory(leftUnitCategory); 366 if (canonicalType != CSSPrimitiveValue::CSS_UNKNOWN) { 367 double leftValue = leftSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(leftType); 368 double rightValue = rightSide->doubleValue() * CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(rightType); 369 return CSSCalcPrimitiveValue::create(evaluateOperator(leftValue, rightValue, op), canonicalType, isInteger); 370 } 371 } 372 } 373 } 374 } else { 375 // Simplify multiplying or dividing by a number for simplifiable types. 376 ASSERT(op == CalcMultiply || op == CalcDivide); 377 CSSCalcExpressionNode* numberSide = getNumberSide(leftSide.get(), rightSide.get()); 378 if (!numberSide) 379 return create(leftSide, rightSide, op); 380 if (numberSide == leftSide && op == CalcDivide) 381 return nullptr; 382 CSSCalcExpressionNode* otherSide = leftSide == numberSide ? rightSide.get() : leftSide.get(); 383 384 double number = numberSide->doubleValue(); 385 if (std::isnan(number) || std::isinf(number)) 386 return nullptr; 387 if (op == CalcDivide && !number) 388 return nullptr; 389 390 CSSPrimitiveValue::UnitType otherType = otherSide->primitiveType(); 391 if (hasDoubleValue(otherType)) 392 return CSSCalcPrimitiveValue::create(evaluateOperator(otherSide->doubleValue(), number, op), otherType, isInteger); 393 } 394 395 return create(leftSide, rightSide, op); 396 } 397 398 virtual bool isZero() const OVERRIDE 399 { 400 return !doubleValue(); 401 } 402 403 virtual void accumulatePixelsAndPercent(const CSSToLengthConversionData& conversionData, PixelsAndPercent& value, float multiplier) const OVERRIDE 404 { 405 switch (m_operator) { 406 case CalcAdd: 407 m_leftSide->accumulatePixelsAndPercent(conversionData, value, multiplier); 408 m_rightSide->accumulatePixelsAndPercent(conversionData, value, multiplier); 409 break; 410 case CalcSubtract: 411 m_leftSide->accumulatePixelsAndPercent(conversionData, value, multiplier); 412 m_rightSide->accumulatePixelsAndPercent(conversionData, value, -multiplier); 413 break; 414 case CalcMultiply: 415 ASSERT((m_leftSide->category() == CalcNumber) != (m_rightSide->category() == CalcNumber)); 416 if (m_leftSide->category() == CalcNumber) 417 m_rightSide->accumulatePixelsAndPercent(conversionData, value, multiplier * m_leftSide->doubleValue()); 418 else 419 m_leftSide->accumulatePixelsAndPercent(conversionData, value, multiplier * m_rightSide->doubleValue()); 420 break; 421 case CalcDivide: 422 ASSERT(m_rightSide->category() == CalcNumber); 423 m_leftSide->accumulatePixelsAndPercent(conversionData, value, multiplier / m_rightSide->doubleValue()); 424 break; 425 default: 426 ASSERT_NOT_REACHED(); 427 } 428 } 429 430 virtual double doubleValue() const OVERRIDE 431 { 432 return evaluate(m_leftSide->doubleValue(), m_rightSide->doubleValue()); 433 } 434 435 virtual double computeLengthPx(const CSSToLengthConversionData& conversionData) const OVERRIDE 436 { 437 const double leftValue = m_leftSide->computeLengthPx(conversionData); 438 const double rightValue = m_rightSide->computeLengthPx(conversionData); 439 return evaluate(leftValue, rightValue); 440 } 441 442 virtual void accumulateLengthArray(CSSLengthArray& lengthArray, double multiplier) const 443 { 444 switch (m_operator) { 445 case CalcAdd: 446 m_leftSide->accumulateLengthArray(lengthArray, multiplier); 447 m_rightSide->accumulateLengthArray(lengthArray, multiplier); 448 break; 449 case CalcSubtract: 450 m_leftSide->accumulateLengthArray(lengthArray, multiplier); 451 m_rightSide->accumulateLengthArray(lengthArray, -multiplier); 452 break; 453 case CalcMultiply: 454 ASSERT((m_leftSide->category() == CalcNumber) != (m_rightSide->category() == CalcNumber)); 455 if (m_leftSide->category() == CalcNumber) 456 m_rightSide->accumulateLengthArray(lengthArray, multiplier * m_leftSide->doubleValue()); 457 else 458 m_leftSide->accumulateLengthArray(lengthArray, multiplier * m_rightSide->doubleValue()); 459 break; 460 case CalcDivide: 461 ASSERT(m_rightSide->category() == CalcNumber); 462 m_leftSide->accumulateLengthArray(lengthArray, multiplier / m_rightSide->doubleValue()); 463 break; 464 default: 465 ASSERT_NOT_REACHED(); 466 } 467 } 468 469 static String buildCSSText(const String& leftExpression, const String& rightExpression, CalcOperator op) 470 { 471 StringBuilder result; 472 result.append('('); 473 result.append(leftExpression); 474 result.append(' '); 475 result.append(static_cast<char>(op)); 476 result.append(' '); 477 result.append(rightExpression); 478 result.append(')'); 479 480 return result.toString(); 481 } 482 483 virtual String customCSSText() const OVERRIDE 484 { 485 return buildCSSText(m_leftSide->customCSSText(), m_rightSide->customCSSText(), m_operator); 486 } 487 488 virtual bool equals(const CSSCalcExpressionNode& exp) const OVERRIDE 489 { 490 if (type() != exp.type()) 491 return false; 492 493 const CSSCalcBinaryOperation& other = static_cast<const CSSCalcBinaryOperation&>(exp); 494 return compareCSSValuePtr(m_leftSide, other.m_leftSide) 495 && compareCSSValuePtr(m_rightSide, other.m_rightSide) 496 && m_operator == other.m_operator; 497 } 498 499 virtual Type type() const OVERRIDE { return CssCalcBinaryOperation; } 500 501 virtual CSSPrimitiveValue::UnitType primitiveType() const OVERRIDE 502 { 503 switch (m_category) { 504 case CalcNumber: 505 ASSERT(m_leftSide->category() == CalcNumber && m_rightSide->category() == CalcNumber); 506 if (m_isInteger) 507 return CSSPrimitiveValue::CSS_PARSER_INTEGER; 508 return CSSPrimitiveValue::CSS_NUMBER; 509 case CalcLength: 510 case CalcPercent: { 511 if (m_leftSide->category() == CalcNumber) 512 return m_rightSide->primitiveType(); 513 if (m_rightSide->category() == CalcNumber) 514 return m_leftSide->primitiveType(); 515 CSSPrimitiveValue::UnitType leftType = m_leftSide->primitiveType(); 516 if (leftType == m_rightSide->primitiveType()) 517 return leftType; 518 return CSSPrimitiveValue::CSS_UNKNOWN; 519 } 520 case CalcPercentLength: 521 case CalcPercentNumber: 522 case CalcOther: 523 return CSSPrimitiveValue::CSS_UNKNOWN; 524 } 525 ASSERT_NOT_REACHED(); 526 return CSSPrimitiveValue::CSS_UNKNOWN; 527 } 528 529 virtual void trace(Visitor* visitor) 530 { 531 visitor->trace(m_leftSide); 532 visitor->trace(m_rightSide); 533 CSSCalcExpressionNode::trace(visitor); 534 } 535 536 private: 537 CSSCalcBinaryOperation(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> leftSide, PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> rightSide, CalcOperator op, CalculationCategory category) 538 : CSSCalcExpressionNode(category, isIntegerResult(leftSide.get(), rightSide.get(), op)) 539 , m_leftSide(leftSide) 540 , m_rightSide(rightSide) 541 , m_operator(op) 542 { 543 } 544 545 static CSSCalcExpressionNode* getNumberSide(CSSCalcExpressionNode* leftSide, CSSCalcExpressionNode* rightSide) 546 { 547 if (leftSide->category() == CalcNumber) 548 return leftSide; 549 if (rightSide->category() == CalcNumber) 550 return rightSide; 551 return 0; 552 } 553 554 double evaluate(double leftSide, double rightSide) const 555 { 556 return evaluateOperator(leftSide, rightSide, m_operator); 557 } 558 559 static double evaluateOperator(double leftValue, double rightValue, CalcOperator op) 560 { 561 switch (op) { 562 case CalcAdd: 563 return leftValue + rightValue; 564 case CalcSubtract: 565 return leftValue - rightValue; 566 case CalcMultiply: 567 return leftValue * rightValue; 568 case CalcDivide: 569 if (rightValue) 570 return leftValue / rightValue; 571 return std::numeric_limits<double>::quiet_NaN(); 572 } 573 return 0; 574 } 575 576 const RefPtrWillBeMember<CSSCalcExpressionNode> m_leftSide; 577 const RefPtrWillBeMember<CSSCalcExpressionNode> m_rightSide; 578 const CalcOperator m_operator; 579 }; 580 581 static ParseState checkDepthAndIndex(int* depth, unsigned index, CSSParserValueList* tokens) 582 { 583 (*depth)++; 584 if (*depth > maxExpressionDepth) 585 return TooDeep; 586 if (index >= tokens->size()) 587 return NoMoreTokens; 588 return OK; 589 } 590 591 class CSSCalcExpressionNodeParser { 592 STACK_ALLOCATED(); 593 public: 594 PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> parseCalc(CSSParserValueList* tokens) 595 { 596 unsigned index = 0; 597 Value result; 598 bool ok = parseValueExpression(tokens, 0, &index, &result); 599 ASSERT_WITH_SECURITY_IMPLICATION(index <= tokens->size()); 600 if (!ok || index != tokens->size()) 601 return nullptr; 602 return result.value; 603 } 604 605 private: 606 struct Value { 607 STACK_ALLOCATED(); 608 public: 609 RefPtrWillBeMember<CSSCalcExpressionNode> value; 610 }; 611 612 char operatorValue(CSSParserValueList* tokens, unsigned index) 613 { 614 if (index >= tokens->size()) 615 return 0; 616 CSSParserValue* value = tokens->valueAt(index); 617 if (value->unit != CSSParserValue::Operator) 618 return 0; 619 620 return value->iValue; 621 } 622 623 bool parseValue(CSSParserValueList* tokens, unsigned* index, Value* result) 624 { 625 CSSParserValue* parserValue = tokens->valueAt(*index); 626 if (parserValue->unit == CSSParserValue::Operator) 627 return false; 628 629 RefPtrWillBeRawPtr<CSSValue> value = parserValue->createCSSValue(); 630 if (!value || !value->isPrimitiveValue()) 631 return false; 632 633 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value.get()); 634 if (unitCategory(primitiveValue->primitiveType()) == CalcOther) 635 return false; 636 637 result->value = CSSCalcPrimitiveValue::create(primitiveValue, parserValue->isInt); 638 639 ++*index; 640 return true; 641 } 642 643 bool parseValueTerm(CSSParserValueList* tokens, int depth, unsigned* index, Value* result) 644 { 645 if (checkDepthAndIndex(&depth, *index, tokens) != OK) 646 return false; 647 648 if (operatorValue(tokens, *index) == '(') { 649 unsigned currentIndex = *index + 1; 650 if (!parseValueExpression(tokens, depth, ¤tIndex, result)) 651 return false; 652 653 if (operatorValue(tokens, currentIndex) != ')') 654 return false; 655 *index = currentIndex + 1; 656 return true; 657 } 658 659 return parseValue(tokens, index, result); 660 } 661 662 bool parseValueMultiplicativeExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result) 663 { 664 if (checkDepthAndIndex(&depth, *index, tokens) != OK) 665 return false; 666 667 if (!parseValueTerm(tokens, depth, index, result)) 668 return false; 669 670 while (*index < tokens->size() - 1) { 671 char operatorCharacter = operatorValue(tokens, *index); 672 if (operatorCharacter != CalcMultiply && operatorCharacter != CalcDivide) 673 break; 674 ++*index; 675 676 Value rhs; 677 if (!parseValueTerm(tokens, depth, index, &rhs)) 678 return false; 679 680 result->value = CSSCalcBinaryOperation::createSimplified(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter)); 681 if (!result->value) 682 return false; 683 } 684 685 ASSERT_WITH_SECURITY_IMPLICATION(*index <= tokens->size()); 686 return true; 687 } 688 689 bool parseAdditiveValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result) 690 { 691 if (checkDepthAndIndex(&depth, *index, tokens) != OK) 692 return false; 693 694 if (!parseValueMultiplicativeExpression(tokens, depth, index, result)) 695 return false; 696 697 while (*index < tokens->size() - 1) { 698 char operatorCharacter = operatorValue(tokens, *index); 699 if (operatorCharacter != CalcAdd && operatorCharacter != CalcSubtract) 700 break; 701 ++*index; 702 703 Value rhs; 704 if (!parseValueMultiplicativeExpression(tokens, depth, index, &rhs)) 705 return false; 706 707 result->value = CSSCalcBinaryOperation::createSimplified(result->value, rhs.value, static_cast<CalcOperator>(operatorCharacter)); 708 if (!result->value) 709 return false; 710 } 711 712 ASSERT_WITH_SECURITY_IMPLICATION(*index <= tokens->size()); 713 return true; 714 } 715 716 bool parseValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result) 717 { 718 return parseAdditiveValueExpression(tokens, depth, index, result); 719 } 720 }; 721 722 PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(PassRefPtrWillBeRawPtr<CSSPrimitiveValue> value, bool isInteger) 723 { 724 return CSSCalcPrimitiveValue::create(value, isInteger); 725 } 726 727 PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> leftSide, PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> rightSide, CalcOperator op) 728 { 729 return CSSCalcBinaryOperation::create(leftSide, rightSide, op); 730 } 731 732 PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(double pixels, double percent) 733 { 734 return createExpressionNode( 735 createExpressionNode(CSSPrimitiveValue::create(pixels, CSSPrimitiveValue::CSS_PX), pixels == trunc(pixels)), 736 createExpressionNode(CSSPrimitiveValue::create(percent, CSSPrimitiveValue::CSS_PERCENTAGE), percent == trunc(percent)), 737 CalcAdd); 738 } 739 740 PassRefPtrWillBeRawPtr<CSSCalcValue> CSSCalcValue::create(CSSParserString name, CSSParserValueList* parserValueList, ValueRange range) 741 { 742 CSSCalcExpressionNodeParser parser; 743 RefPtrWillBeRawPtr<CSSCalcExpressionNode> expression = nullptr; 744 745 if (equalIgnoringCase(name, "calc(") || equalIgnoringCase(name, "-webkit-calc(")) 746 expression = parser.parseCalc(parserValueList); 747 // FIXME calc (http://webkit.org/b/16662) Add parsing for min and max here 748 749 return expression ? adoptRefWillBeNoop(new CSSCalcValue(expression, range)) : nullptr; 750 } 751 752 PassRefPtrWillBeRawPtr<CSSCalcValue> CSSCalcValue::create(PassRefPtrWillBeRawPtr<CSSCalcExpressionNode> expression, ValueRange range) 753 { 754 return adoptRefWillBeNoop(new CSSCalcValue(expression, range)); 755 } 756 757 void CSSCalcValue::traceAfterDispatch(Visitor* visitor) 758 { 759 visitor->trace(m_expression); 760 CSSValue::traceAfterDispatch(visitor); 761 } 762 763 } // namespace WebCore 764