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