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 40static const int maxExpressionDepth = 100; 41 42enum ParseState { 43 OK, 44 TooDeep, 45 NoMoreTokens 46}; 47 48namespace WebCore { 49 50static 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 76static 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 138static 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 151String CSSCalcValue::customCSSText() const 152{ 153 return buildCSSText(m_expression->customCSSText()); 154} 155 156bool CSSCalcValue::equals(const CSSCalcValue& other) const 157{ 158 return compareCSSValuePtr(m_expression, other.m_expression); 159} 160 161String CSSCalcValue::customSerializeResolvingVariables(const HashMap<AtomicString, String>& variables) const 162{ 163 return buildCSSText(m_expression->serializeResolvingVariables(variables)); 164} 165 166bool CSSCalcValue::hasVariableReference() const 167{ 168 return m_expression->hasVariableReference(); 169} 170 171double CSSCalcValue::clampToPermittedRange(double value) const 172{ 173 return m_nonNegative && value < 0 ? 0 : value; 174} 175 176double CSSCalcValue::doubleValue() const 177{ 178 return clampToPermittedRange(m_expression->doubleValue()); 179} 180 181double CSSCalcValue::computeLengthPx(const CSSToLengthConversionData& conversionData) const 182{ 183 return clampToPermittedRange(m_expression->computeLengthPx(conversionData)); 184} 185 186CSSCalcExpressionNode::~CSSCalcExpressionNode() 187{ 188} 189 190class CSSCalcPrimitiveValue : public CSSCalcExpressionNode { 191 WTF_MAKE_FAST_ALLOCATED; 192public: 193 194 static PassRefPtr<CSSCalcPrimitiveValue> create(PassRefPtr<CSSPrimitiveValue> value, bool isInteger) 195 { 196 return adoptRef(new CSSCalcPrimitiveValue(value, isInteger)); 197 } 198 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 206 virtual bool isZero() const 207 { 208 return !m_value->getDoubleValue(); 209 } 210 211 virtual String customCSSText() const 212 { 213 return m_value->cssText(); 214 } 215 216 virtual String serializeResolvingVariables(const HashMap<AtomicString, String>& variables) const 217 { 218 return m_value->customSerializeResolvingVariables(variables); 219 } 220 221 virtual bool hasVariableReference() const 222 { 223 return m_value->isVariableName(); 224 } 225 226 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 250 virtual double doubleValue() const 251 { 252 if (hasDoubleValue(primitiveType())) 253 return m_value->getDoubleValue(); 254 ASSERT_NOT_REACHED(); 255 return 0; 256 } 257 258 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 277 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 285 virtual Type type() const { return CssCalcPrimitiveValue; } 286 virtual CSSPrimitiveValue::UnitTypes primitiveType() const 287 { 288 return CSSPrimitiveValue::UnitTypes(m_value->primitiveType()); 289 } 290 291private: 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 301static 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 310static 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 339static 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 347class CSSCalcBinaryOperation : public CSSCalcExpressionNode { 348 349public: 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 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 418 virtual bool isZero() const 419 { 420 return !doubleValue(); 421 } 422 423 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 434 virtual double doubleValue() const 435 { 436 return evaluate(m_leftSide->doubleValue(), m_rightSide->doubleValue()); 437 } 438 439 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 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 460 virtual String customCSSText() const 461 { 462 return buildCSSText(m_leftSide->customCSSText(), m_rightSide->customCSSText(), m_operator); 463 } 464 465 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 470 virtual bool hasVariableReference() const 471 { 472 return m_leftSide->hasVariableReference() || m_rightSide->hasVariableReference(); 473 } 474 475 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 486 virtual Type type() const { return CssCalcBinaryOperation; } 487 488 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 519private: 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 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 537 double evaluate(double leftSide, double rightSide) const 538 { 539 return evaluateOperator(leftSide, rightSide, m_operator); 540 } 541 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 564static 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 574class CSSCalcExpressionNodeParser { 575public: 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 587private: 588 struct Value { 589 RefPtr<CSSCalcExpressionNode> value; 590 }; 591 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 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 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 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 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 692 bool parseValueExpression(CSSParserValueList* tokens, int depth, unsigned* index, Value* result) 693 { 694 return parseAdditiveValueExpression(tokens, depth, index, result); 695 } 696}; 697 698PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(PassRefPtr<CSSPrimitiveValue> value, bool isInteger) 699{ 700 return CSSCalcPrimitiveValue::create(value, isInteger); 701} 702 703PassRefPtr<CSSCalcExpressionNode> CSSCalcValue::createExpressionNode(PassRefPtr<CSSCalcExpressionNode> leftSide, PassRefPtr<CSSCalcExpressionNode> rightSide, CalcOperator op) 704{ 705 return CSSCalcBinaryOperation::create(leftSide, rightSide, op); 706} 707 708PassRefPtr<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 745PassRefPtr<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 773PassRefPtr<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 785PassRefPtr<CSSCalcValue> CSSCalcValue::create(PassRefPtr<CSSCalcExpressionNode> expression, ValueRange range) 786{ 787 return adoptRef(new CSSCalcValue(expression, range)); 788} 789 790} // namespace WebCore 791