1/* 2 * (C) 1999-2003 Lars Knoll (knoll@kde.org) 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2012 Apple Inc. All rights reserved. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 */ 20 21#include "config.h" 22#include "core/css/CSSPrimitiveValue.h" 23 24#include "CSSPropertyNames.h" 25#include "CSSValueKeywords.h" 26#include "bindings/v8/ExceptionState.h" 27#include "core/css/CSSBasicShapes.h" 28#include "core/css/CSSCalculationValue.h" 29#include "core/css/CSSHelper.h" 30#include "core/css/CSSParser.h" 31#include "core/css/Counter.h" 32#include "core/css/Pair.h" 33#include "core/css/RGBColor.h" 34#include "core/css/Rect.h" 35#include "core/css/StyleSheetContents.h" 36#include "core/dom/ExceptionCode.h" 37#include "core/dom/Node.h" 38#include "core/platform/LayoutUnit.h" 39#include "core/platform/graphics/Color.h" 40#include "core/rendering/style/RenderStyle.h" 41#include "wtf/DecimalNumber.h" 42#include "wtf/StdLibExtras.h" 43#include "wtf/text/StringBuffer.h" 44#include "wtf/text/StringBuilder.h" 45 46using namespace WTF; 47 48namespace WebCore { 49 50// Max/min values for CSS, needs to slightly smaller/larger than the true max/min values to allow for rounding without overflowing. 51// Subtract two (rather than one) to allow for values to be converted to float and back without exceeding the LayoutUnit::max. 52const int maxValueForCssLength = INT_MAX / kFixedPointDenominator - 2; 53const int minValueForCssLength = INT_MIN / kFixedPointDenominator + 2; 54 55static inline bool isValidCSSUnitTypeForDoubleConversion(CSSPrimitiveValue::UnitTypes unitType) 56{ 57 switch (unitType) { 58 case CSSPrimitiveValue::CSS_CALC: 59 case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER: 60 case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH: 61 case CSSPrimitiveValue::CSS_CM: 62 case CSSPrimitiveValue::CSS_DEG: 63 case CSSPrimitiveValue::CSS_DIMENSION: 64 case CSSPrimitiveValue::CSS_DPPX: 65 case CSSPrimitiveValue::CSS_DPI: 66 case CSSPrimitiveValue::CSS_DPCM: 67 case CSSPrimitiveValue::CSS_EMS: 68 case CSSPrimitiveValue::CSS_EXS: 69 case CSSPrimitiveValue::CSS_GRAD: 70 case CSSPrimitiveValue::CSS_HZ: 71 case CSSPrimitiveValue::CSS_IN: 72 case CSSPrimitiveValue::CSS_KHZ: 73 case CSSPrimitiveValue::CSS_MM: 74 case CSSPrimitiveValue::CSS_MS: 75 case CSSPrimitiveValue::CSS_NUMBER: 76 case CSSPrimitiveValue::CSS_PERCENTAGE: 77 case CSSPrimitiveValue::CSS_PC: 78 case CSSPrimitiveValue::CSS_PT: 79 case CSSPrimitiveValue::CSS_PX: 80 case CSSPrimitiveValue::CSS_RAD: 81 case CSSPrimitiveValue::CSS_REMS: 82 case CSSPrimitiveValue::CSS_CHS: 83 case CSSPrimitiveValue::CSS_S: 84 case CSSPrimitiveValue::CSS_TURN: 85 case CSSPrimitiveValue::CSS_VW: 86 case CSSPrimitiveValue::CSS_VH: 87 case CSSPrimitiveValue::CSS_VMIN: 88 case CSSPrimitiveValue::CSS_VMAX: 89 case CSSPrimitiveValue::CSS_FR: 90 return true; 91 case CSSPrimitiveValue::CSS_ATTR: 92 case CSSPrimitiveValue::CSS_COUNTER: 93 case CSSPrimitiveValue::CSS_COUNTER_NAME: 94 case CSSPrimitiveValue::CSS_IDENT: 95 case CSSPrimitiveValue::CSS_PROPERTY_ID: 96 case CSSPrimitiveValue::CSS_VALUE_ID: 97 case CSSPrimitiveValue::CSS_PAIR: 98 case CSSPrimitiveValue::CSS_PARSER_HEXCOLOR: 99 case CSSPrimitiveValue::CSS_PARSER_IDENTIFIER: 100 case CSSPrimitiveValue::CSS_PARSER_INTEGER: 101 case CSSPrimitiveValue::CSS_PARSER_OPERATOR: 102 case CSSPrimitiveValue::CSS_RECT: 103 case CSSPrimitiveValue::CSS_QUAD: 104 case CSSPrimitiveValue::CSS_RGBCOLOR: 105 case CSSPrimitiveValue::CSS_SHAPE: 106 case CSSPrimitiveValue::CSS_STRING: 107 case CSSPrimitiveValue::CSS_UNICODE_RANGE: 108 case CSSPrimitiveValue::CSS_UNKNOWN: 109 case CSSPrimitiveValue::CSS_URI: 110 case CSSPrimitiveValue::CSS_VARIABLE_NAME: 111 return false; 112 } 113 114 ASSERT_NOT_REACHED(); 115 return false; 116} 117 118CSSPrimitiveValue::UnitCategory CSSPrimitiveValue::unitCategory(CSSPrimitiveValue::UnitTypes type) 119{ 120 // Here we violate the spec (http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue) and allow conversions 121 // between CSS_PX and relative lengths (see cssPixelsPerInch comment in core/css/CSSHelper.h for the topic treatment). 122 switch (type) { 123 case CSS_NUMBER: 124 return CSSPrimitiveValue::UNumber; 125 case CSS_PERCENTAGE: 126 return CSSPrimitiveValue::UPercent; 127 case CSS_PX: 128 case CSS_CM: 129 case CSS_MM: 130 case CSS_IN: 131 case CSS_PT: 132 case CSS_PC: 133 return CSSPrimitiveValue::ULength; 134 case CSS_MS: 135 case CSS_S: 136 return CSSPrimitiveValue::UTime; 137 case CSS_DEG: 138 case CSS_RAD: 139 case CSS_GRAD: 140 case CSS_TURN: 141 return CSSPrimitiveValue::UAngle; 142 case CSS_HZ: 143 case CSS_KHZ: 144 return CSSPrimitiveValue::UFrequency; 145 case CSS_VW: 146 case CSS_VH: 147 case CSS_VMIN: 148 case CSS_VMAX: 149 return CSSPrimitiveValue::UViewportPercentageLength; 150 case CSS_DPPX: 151 case CSS_DPI: 152 case CSS_DPCM: 153 return CSSPrimitiveValue::UResolution; 154 default: 155 return CSSPrimitiveValue::UOther; 156 } 157} 158 159bool CSSPrimitiveValue::colorIsDerivedFromElement() const 160{ 161 int valueID = getValueID(); 162 switch (valueID) { 163 case CSSValueWebkitText: 164 case CSSValueWebkitLink: 165 case CSSValueWebkitActivelink: 166 case CSSValueCurrentcolor: 167 return true; 168 default: 169 return false; 170 } 171} 172 173typedef HashMap<const CSSPrimitiveValue*, String> CSSTextCache; 174static CSSTextCache& cssTextCache() 175{ 176 DEFINE_STATIC_LOCAL(CSSTextCache, cache, ()); 177 return cache; 178} 179 180unsigned short CSSPrimitiveValue::primitiveType() const 181{ 182 if (m_primitiveUnitType == CSS_PROPERTY_ID || m_primitiveUnitType == CSS_VALUE_ID) 183 return CSS_IDENT; 184 185 if (m_primitiveUnitType != CSS_CALC) 186 return m_primitiveUnitType; 187 188 switch (m_value.calc->category()) { 189 case CalcNumber: 190 return CSS_NUMBER; 191 case CalcPercent: 192 return CSS_PERCENTAGE; 193 case CalcLength: 194 return CSS_PX; 195 case CalcPercentNumber: 196 return CSS_CALC_PERCENTAGE_WITH_NUMBER; 197 case CalcPercentLength: 198 return CSS_CALC_PERCENTAGE_WITH_LENGTH; 199 case CalcVariable: 200 return CSS_UNKNOWN; // The type of a calculation containing a variable cannot be known until the value of the variable is determined. 201 case CalcOther: 202 return CSS_UNKNOWN; 203 } 204 return CSS_UNKNOWN; 205} 206 207static const AtomicString& propertyName(CSSPropertyID propertyID) 208{ 209 ASSERT_ARG(propertyID, propertyID >= 0); 210 ASSERT_ARG(propertyID, (propertyID >= firstCSSProperty && propertyID < firstCSSProperty + numCSSProperties)); 211 212 if (propertyID < 0) 213 return nullAtom; 214 215 return getPropertyNameAtomicString(propertyID); 216} 217 218static const AtomicString& valueName(CSSValueID valueID) 219{ 220 ASSERT_ARG(valueID, valueID >= 0); 221 ASSERT_ARG(valueID, valueID < numCSSValueKeywords); 222 223 if (valueID < 0) 224 return nullAtom; 225 226 static AtomicString* keywordStrings = new AtomicString[numCSSValueKeywords]; // Leaked intentionally. 227 AtomicString& keywordString = keywordStrings[valueID]; 228 if (keywordString.isNull()) 229 keywordString = getValueName(valueID); 230 return keywordString; 231} 232 233CSSPrimitiveValue::CSSPrimitiveValue(CSSValueID valueID) 234 : CSSValue(PrimitiveClass) 235{ 236 m_primitiveUnitType = CSS_VALUE_ID; 237 m_value.valueID = valueID; 238} 239 240CSSPrimitiveValue::CSSPrimitiveValue(CSSPropertyID propertyID) 241 : CSSValue(PrimitiveClass) 242{ 243 m_primitiveUnitType = CSS_PROPERTY_ID; 244 m_value.propertyID = propertyID; 245} 246 247CSSPrimitiveValue::CSSPrimitiveValue(int parserOperator) 248 : CSSValue(PrimitiveClass) 249{ 250 m_primitiveUnitType = CSS_PARSER_OPERATOR; 251 m_value.parserOperator = parserOperator; 252} 253 254CSSPrimitiveValue::CSSPrimitiveValue(double num, UnitTypes type) 255 : CSSValue(PrimitiveClass) 256{ 257 m_primitiveUnitType = type; 258 ASSERT(std::isfinite(num)); 259 m_value.num = num; 260} 261 262CSSPrimitiveValue::CSSPrimitiveValue(const String& str, UnitTypes type) 263 : CSSValue(PrimitiveClass) 264{ 265 m_primitiveUnitType = type; 266 if ((m_value.string = str.impl())) 267 m_value.string->ref(); 268} 269 270 271CSSPrimitiveValue::CSSPrimitiveValue(RGBA32 color) 272 : CSSValue(PrimitiveClass) 273{ 274 m_primitiveUnitType = CSS_RGBCOLOR; 275 m_value.rgbcolor = color; 276} 277 278CSSPrimitiveValue::CSSPrimitiveValue(const Length& length) 279 : CSSValue(PrimitiveClass) 280{ 281 switch (length.type()) { 282 case Auto: 283 m_primitiveUnitType = CSS_VALUE_ID; 284 m_value.valueID = CSSValueAuto; 285 break; 286 case WebCore::Fixed: 287 m_primitiveUnitType = CSS_PX; 288 m_value.num = length.value(); 289 break; 290 case Intrinsic: 291 m_primitiveUnitType = CSS_VALUE_ID; 292 m_value.valueID = CSSValueIntrinsic; 293 break; 294 case MinIntrinsic: 295 m_primitiveUnitType = CSS_VALUE_ID; 296 m_value.valueID = CSSValueMinIntrinsic; 297 break; 298 case MinContent: 299 m_primitiveUnitType = CSS_VALUE_ID; 300 m_value.valueID = CSSValueMinContent; 301 break; 302 case MaxContent: 303 m_primitiveUnitType = CSS_VALUE_ID; 304 m_value.valueID = CSSValueMaxContent; 305 break; 306 case FillAvailable: 307 m_primitiveUnitType = CSS_VALUE_ID; 308 m_value.valueID = CSSValueWebkitFillAvailable; 309 break; 310 case FitContent: 311 m_primitiveUnitType = CSS_VALUE_ID; 312 m_value.valueID = CSSValueWebkitFitContent; 313 break; 314 case ExtendToZoom: 315 m_primitiveUnitType = CSS_VALUE_ID; 316 m_value.valueID = CSSValueInternalExtendToZoom; 317 break; 318 case Percent: 319 m_primitiveUnitType = CSS_PERCENTAGE; 320 ASSERT(std::isfinite(length.percent())); 321 m_value.num = length.percent(); 322 break; 323 case ViewportPercentageWidth: 324 m_primitiveUnitType = CSS_VW; 325 m_value.num = length.viewportPercentageLength(); 326 break; 327 case ViewportPercentageHeight: 328 m_primitiveUnitType = CSS_VH; 329 m_value.num = length.viewportPercentageLength(); 330 break; 331 case ViewportPercentageMin: 332 m_primitiveUnitType = CSS_VMIN; 333 m_value.num = length.viewportPercentageLength(); 334 break; 335 case ViewportPercentageMax: 336 m_primitiveUnitType = CSS_VMAX; 337 m_value.num = length.viewportPercentageLength(); 338 break; 339 case Calculated: 340 case Relative: 341 case Undefined: 342 ASSERT_NOT_REACHED(); 343 break; 344 } 345} 346 347void CSSPrimitiveValue::init(PassRefPtr<Counter> c) 348{ 349 m_primitiveUnitType = CSS_COUNTER; 350 m_hasCachedCSSText = false; 351 m_value.counter = c.leakRef(); 352} 353 354void CSSPrimitiveValue::init(PassRefPtr<Rect> r) 355{ 356 m_primitiveUnitType = CSS_RECT; 357 m_hasCachedCSSText = false; 358 m_value.rect = r.leakRef(); 359} 360 361void CSSPrimitiveValue::init(PassRefPtr<Quad> quad) 362{ 363 m_primitiveUnitType = CSS_QUAD; 364 m_hasCachedCSSText = false; 365 m_value.quad = quad.leakRef(); 366} 367 368void CSSPrimitiveValue::init(PassRefPtr<Pair> p) 369{ 370 m_primitiveUnitType = CSS_PAIR; 371 m_hasCachedCSSText = false; 372 m_value.pair = p.leakRef(); 373} 374 375void CSSPrimitiveValue::init(PassRefPtr<CSSCalcValue> c) 376{ 377 m_primitiveUnitType = CSS_CALC; 378 m_hasCachedCSSText = false; 379 m_value.calc = c.leakRef(); 380} 381 382void CSSPrimitiveValue::init(PassRefPtr<CSSBasicShape> shape) 383{ 384 m_primitiveUnitType = CSS_SHAPE; 385 m_hasCachedCSSText = false; 386 m_value.shape = shape.leakRef(); 387} 388 389CSSPrimitiveValue::~CSSPrimitiveValue() 390{ 391 cleanup(); 392} 393 394void CSSPrimitiveValue::cleanup() 395{ 396 switch (static_cast<UnitTypes>(m_primitiveUnitType)) { 397 case CSS_STRING: 398 case CSS_URI: 399 case CSS_ATTR: 400 case CSS_COUNTER_NAME: 401 case CSS_VARIABLE_NAME: 402 case CSS_PARSER_HEXCOLOR: 403 if (m_value.string) 404 m_value.string->deref(); 405 break; 406 case CSS_COUNTER: 407 m_value.counter->deref(); 408 break; 409 case CSS_RECT: 410 m_value.rect->deref(); 411 break; 412 case CSS_QUAD: 413 m_value.quad->deref(); 414 break; 415 case CSS_PAIR: 416 m_value.pair->deref(); 417 break; 418 case CSS_CALC: 419 m_value.calc->deref(); 420 break; 421 case CSS_CALC_PERCENTAGE_WITH_NUMBER: 422 case CSS_CALC_PERCENTAGE_WITH_LENGTH: 423 ASSERT_NOT_REACHED(); 424 break; 425 case CSS_SHAPE: 426 m_value.shape->deref(); 427 break; 428 case CSS_NUMBER: 429 case CSS_PARSER_INTEGER: 430 case CSS_PERCENTAGE: 431 case CSS_EMS: 432 case CSS_EXS: 433 case CSS_REMS: 434 case CSS_CHS: 435 case CSS_PX: 436 case CSS_CM: 437 case CSS_MM: 438 case CSS_IN: 439 case CSS_PT: 440 case CSS_PC: 441 case CSS_DEG: 442 case CSS_RAD: 443 case CSS_GRAD: 444 case CSS_MS: 445 case CSS_S: 446 case CSS_HZ: 447 case CSS_KHZ: 448 case CSS_TURN: 449 case CSS_VW: 450 case CSS_VH: 451 case CSS_VMIN: 452 case CSS_VMAX: 453 case CSS_DPPX: 454 case CSS_DPI: 455 case CSS_DPCM: 456 case CSS_FR: 457 case CSS_IDENT: 458 case CSS_RGBCOLOR: 459 case CSS_DIMENSION: 460 case CSS_UNKNOWN: 461 case CSS_UNICODE_RANGE: 462 case CSS_PARSER_OPERATOR: 463 case CSS_PARSER_IDENTIFIER: 464 case CSS_PROPERTY_ID: 465 case CSS_VALUE_ID: 466 break; 467 } 468 m_primitiveUnitType = 0; 469 if (m_hasCachedCSSText) { 470 cssTextCache().remove(this); 471 m_hasCachedCSSText = false; 472 } 473} 474 475double CSSPrimitiveValue::computeDegrees() 476{ 477 switch (m_primitiveUnitType) { 478 case CSS_DEG: 479 return getDoubleValue(); 480 case CSS_RAD: 481 return rad2deg(getDoubleValue()); 482 case CSS_GRAD: 483 return grad2deg(getDoubleValue()); 484 case CSS_TURN: 485 return turn2deg(getDoubleValue()); 486 default: 487 ASSERT_NOT_REACHED(); 488 return 0; 489 } 490} 491 492template<> int CSSPrimitiveValue::computeLength(const RenderStyle* style, const RenderStyle* rootStyle, float multiplier, bool computingFontSize) 493{ 494 return roundForImpreciseConversion<int>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize)); 495} 496 497template<> unsigned CSSPrimitiveValue::computeLength(const RenderStyle* style, const RenderStyle* rootStyle, float multiplier, bool computingFontSize) 498{ 499 return roundForImpreciseConversion<unsigned>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize)); 500} 501 502template<> Length CSSPrimitiveValue::computeLength(const RenderStyle* style, const RenderStyle* rootStyle, float multiplier, bool computingFontSize) 503{ 504 return Length(clampTo<float>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize), minValueForCssLength, maxValueForCssLength), Fixed); 505} 506 507template<> short CSSPrimitiveValue::computeLength(const RenderStyle* style, const RenderStyle* rootStyle, float multiplier, bool computingFontSize) 508{ 509 return roundForImpreciseConversion<short>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize)); 510} 511 512template<> unsigned short CSSPrimitiveValue::computeLength(const RenderStyle* style, const RenderStyle* rootStyle, float multiplier, bool computingFontSize) 513{ 514 return roundForImpreciseConversion<unsigned short>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize)); 515} 516 517template<> float CSSPrimitiveValue::computeLength(const RenderStyle* style, const RenderStyle* rootStyle, float multiplier, bool computingFontSize) 518{ 519 return static_cast<float>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize)); 520} 521 522template<> double CSSPrimitiveValue::computeLength(const RenderStyle* style, const RenderStyle* rootStyle, float multiplier, bool computingFontSize) 523{ 524 return computeLengthDouble(style, rootStyle, multiplier, computingFontSize); 525} 526 527double CSSPrimitiveValue::computeLengthDouble(const RenderStyle* style, const RenderStyle* rootStyle, float multiplier, bool computingFontSize) 528{ 529 if (m_primitiveUnitType == CSS_CALC) 530 // The multiplier and factor is applied to each value in the calc expression individually 531 return m_value.calc->computeLengthPx(style, rootStyle, multiplier, computingFontSize); 532 533 double factor; 534 535 switch (primitiveType()) { 536 case CSS_EMS: 537 factor = computingFontSize ? style->fontDescription().specifiedSize() : style->fontDescription().computedSize(); 538 break; 539 case CSS_EXS: 540 // FIXME: We have a bug right now where the zoom will be applied twice to EX units. 541 // We really need to compute EX using fontMetrics for the original specifiedSize and not use 542 // our actual constructed rendering font. 543 if (style->fontMetrics().hasXHeight()) 544 factor = style->fontMetrics().xHeight(); 545 else 546 factor = (computingFontSize ? style->fontDescription().specifiedSize() : style->fontDescription().computedSize()) / 2.0; 547 break; 548 case CSS_REMS: 549 if (rootStyle) 550 factor = computingFontSize ? rootStyle->fontDescription().specifiedSize() : rootStyle->fontDescription().computedSize(); 551 else 552 factor = 1.0; 553 break; 554 case CSS_CHS: 555 factor = style->fontMetrics().zeroWidth(); 556 break; 557 case CSS_PX: 558 factor = 1.0; 559 break; 560 case CSS_CM: 561 factor = cssPixelsPerInch / 2.54; // (2.54 cm/in) 562 break; 563 case CSS_MM: 564 factor = cssPixelsPerInch / 25.4; 565 break; 566 case CSS_IN: 567 factor = cssPixelsPerInch; 568 break; 569 case CSS_PT: 570 factor = cssPixelsPerInch / 72.0; 571 break; 572 case CSS_PC: 573 // 1 pc == 12 pt 574 factor = cssPixelsPerInch * 12.0 / 72.0; 575 break; 576 case CSS_CALC_PERCENTAGE_WITH_LENGTH: 577 case CSS_CALC_PERCENTAGE_WITH_NUMBER: 578 ASSERT_NOT_REACHED(); 579 return -1.0; 580 default: 581 ASSERT_NOT_REACHED(); 582 return -1.0; 583 } 584 585 // We do not apply the zoom factor when we are computing the value of the font-size property. The zooming 586 // for font sizes is much more complicated, since we have to worry about enforcing the minimum font size preference 587 // as well as enforcing the implicit "smart minimum." 588 double result = getDoubleValue() * factor; 589 if (computingFontSize || isFontRelativeLength()) 590 return result; 591 592 return result * multiplier; 593} 594 595void CSSPrimitiveValue::setFloatValue(unsigned short, double, ExceptionState& es) 596{ 597 // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects. 598 // No other engine supports mutating style through this API. Computed style is always read-only anyway. 599 // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation. 600 es.throwDOMException(NoModificationAllowedError); 601} 602 603double CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor(unsigned short unitType) 604{ 605 double factor = 1.0; 606 // FIXME: the switch can be replaced by an array of scale factors. 607 switch (unitType) { 608 // These are "canonical" units in their respective categories. 609 case CSS_PX: 610 case CSS_DEG: 611 case CSS_MS: 612 case CSS_HZ: 613 break; 614 case CSS_CM: 615 factor = cssPixelsPerInch / 2.54; // (2.54 cm/in) 616 break; 617 case CSS_DPCM: 618 factor = 2.54 / cssPixelsPerInch; // (2.54 cm/in) 619 break; 620 case CSS_MM: 621 factor = cssPixelsPerInch / 25.4; 622 break; 623 case CSS_IN: 624 factor = cssPixelsPerInch; 625 break; 626 case CSS_DPI: 627 factor = 1 / cssPixelsPerInch; 628 break; 629 case CSS_PT: 630 factor = cssPixelsPerInch / 72.0; 631 break; 632 case CSS_PC: 633 factor = cssPixelsPerInch * 12.0 / 72.0; // 1 pc == 12 pt 634 break; 635 case CSS_RAD: 636 factor = 180 / piDouble; 637 break; 638 case CSS_GRAD: 639 factor = 0.9; 640 break; 641 case CSS_TURN: 642 factor = 360; 643 break; 644 case CSS_S: 645 case CSS_KHZ: 646 factor = 1000; 647 break; 648 default: 649 break; 650 } 651 652 return factor; 653} 654 655double CSSPrimitiveValue::getDoubleValue(unsigned short unitType, ExceptionState& es) const 656{ 657 double result = 0; 658 bool success = getDoubleValueInternal(static_cast<UnitTypes>(unitType), &result); 659 if (!success) { 660 es.throwDOMException(InvalidAccessError); 661 return 0.0; 662 } 663 664 return result; 665} 666 667double CSSPrimitiveValue::getDoubleValue(unsigned short unitType) const 668{ 669 double result = 0; 670 getDoubleValueInternal(static_cast<UnitTypes>(unitType), &result); 671 return result; 672} 673 674double CSSPrimitiveValue::getDoubleValue() const 675{ 676 return m_primitiveUnitType != CSS_CALC ? m_value.num : m_value.calc->doubleValue(); 677} 678 679CSSPrimitiveValue::UnitTypes CSSPrimitiveValue::canonicalUnitTypeForCategory(UnitCategory category) 680{ 681 // The canonical unit type is chosen according to the way CSSParser::validUnit() chooses the default unit 682 // in each category (based on unitflags). 683 switch (category) { 684 case UNumber: 685 return CSS_NUMBER; 686 case ULength: 687 return CSS_PX; 688 case UPercent: 689 return CSS_UNKNOWN; // Cannot convert between numbers and percent. 690 case UTime: 691 return CSS_MS; 692 case UAngle: 693 return CSS_DEG; 694 case UFrequency: 695 return CSS_HZ; 696 case UViewportPercentageLength: 697 return CSS_UNKNOWN; // Cannot convert between numbers and relative lengths. 698 case UResolution: 699 return CSS_DPPX; 700 default: 701 return CSS_UNKNOWN; 702 } 703} 704 705bool CSSPrimitiveValue::getDoubleValueInternal(UnitTypes requestedUnitType, double* result) const 706{ 707 if (!isValidCSSUnitTypeForDoubleConversion(static_cast<UnitTypes>(m_primitiveUnitType)) || !isValidCSSUnitTypeForDoubleConversion(requestedUnitType)) 708 return false; 709 710 UnitTypes sourceUnitType = static_cast<UnitTypes>(primitiveType()); 711 if (requestedUnitType == sourceUnitType || requestedUnitType == CSS_DIMENSION) { 712 *result = getDoubleValue(); 713 return true; 714 } 715 716 UnitCategory sourceCategory = unitCategory(sourceUnitType); 717 ASSERT(sourceCategory != UOther); 718 719 UnitTypes targetUnitType = requestedUnitType; 720 UnitCategory targetCategory = unitCategory(targetUnitType); 721 ASSERT(targetCategory != UOther); 722 723 // Cannot convert between unrelated unit categories if one of them is not UNumber. 724 if (sourceCategory != targetCategory && sourceCategory != UNumber && targetCategory != UNumber) 725 return false; 726 727 if (targetCategory == UNumber) { 728 // We interpret conversion to CSS_NUMBER as conversion to a canonical unit in this value's category. 729 targetUnitType = canonicalUnitTypeForCategory(sourceCategory); 730 if (targetUnitType == CSS_UNKNOWN) 731 return false; 732 } 733 734 if (sourceUnitType == CSS_NUMBER) { 735 // We interpret conversion from CSS_NUMBER in the same way as CSSParser::validUnit() while using non-strict mode. 736 sourceUnitType = canonicalUnitTypeForCategory(targetCategory); 737 if (sourceUnitType == CSS_UNKNOWN) 738 return false; 739 } 740 741 double convertedValue = getDoubleValue(); 742 743 // First convert the value from m_primitiveUnitType to canonical type. 744 double factor = conversionToCanonicalUnitsScaleFactor(sourceUnitType); 745 convertedValue *= factor; 746 747 // Now convert from canonical type to the target unitType. 748 factor = conversionToCanonicalUnitsScaleFactor(targetUnitType); 749 convertedValue /= factor; 750 751 *result = convertedValue; 752 return true; 753} 754 755void CSSPrimitiveValue::setStringValue(unsigned short, const String&, ExceptionState& es) 756{ 757 // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects. 758 // No other engine supports mutating style through this API. Computed style is always read-only anyway. 759 // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation. 760 es.throwDOMException(NoModificationAllowedError); 761} 762 763String CSSPrimitiveValue::getStringValue(ExceptionState& es) const 764{ 765 switch (m_primitiveUnitType) { 766 case CSS_STRING: 767 case CSS_ATTR: 768 case CSS_URI: 769 case CSS_VARIABLE_NAME: 770 return m_value.string; 771 case CSS_VALUE_ID: 772 return valueName(m_value.valueID); 773 case CSS_PROPERTY_ID: 774 return propertyName(m_value.propertyID); 775 default: 776 es.throwDOMException(InvalidAccessError); 777 break; 778 } 779 780 return String(); 781} 782 783String CSSPrimitiveValue::getStringValue() const 784{ 785 switch (m_primitiveUnitType) { 786 case CSS_STRING: 787 case CSS_ATTR: 788 case CSS_URI: 789 case CSS_VARIABLE_NAME: 790 return m_value.string; 791 case CSS_VALUE_ID: 792 return valueName(m_value.valueID); 793 case CSS_PROPERTY_ID: 794 return propertyName(m_value.propertyID); 795 default: 796 break; 797 } 798 799 return String(); 800} 801 802Counter* CSSPrimitiveValue::getCounterValue(ExceptionState& es) const 803{ 804 if (m_primitiveUnitType != CSS_COUNTER) { 805 es.throwDOMException(InvalidAccessError); 806 return 0; 807 } 808 809 return m_value.counter; 810} 811 812Rect* CSSPrimitiveValue::getRectValue(ExceptionState& es) const 813{ 814 if (m_primitiveUnitType != CSS_RECT) { 815 es.throwDOMException(InvalidAccessError); 816 return 0; 817 } 818 819 return m_value.rect; 820} 821 822Quad* CSSPrimitiveValue::getQuadValue(ExceptionState& es) const 823{ 824 if (m_primitiveUnitType != CSS_QUAD) { 825 es.throwDOMException(InvalidAccessError); 826 return 0; 827 } 828 829 return m_value.quad; 830} 831 832PassRefPtr<RGBColor> CSSPrimitiveValue::getRGBColorValue(ExceptionState& es) const 833{ 834 if (m_primitiveUnitType != CSS_RGBCOLOR) { 835 es.throwDOMException(InvalidAccessError); 836 return 0; 837 } 838 839 // FIMXE: This should not return a new object for each invocation. 840 return RGBColor::create(m_value.rgbcolor); 841} 842 843Pair* CSSPrimitiveValue::getPairValue(ExceptionState& es) const 844{ 845 if (m_primitiveUnitType != CSS_PAIR) { 846 es.throwDOMException(InvalidAccessError); 847 return 0; 848 } 849 850 return m_value.pair; 851} 852 853static String formatNumber(double number, const char* suffix, unsigned suffixLength) 854{ 855 DecimalNumber decimal(number); 856 857 StringBuffer<LChar> buffer(decimal.bufferLengthForStringDecimal() + suffixLength); 858 unsigned length = decimal.toStringDecimal(buffer.characters(), buffer.length()); 859 ASSERT(length + suffixLength == buffer.length()); 860 861 for (unsigned i = 0; i < suffixLength; ++i) 862 buffer[length + i] = static_cast<LChar>(suffix[i]); 863 864 return String::adopt(buffer); 865} 866 867template <unsigned characterCount> 868ALWAYS_INLINE static String formatNumber(double number, const char (&characters)[characterCount]) 869{ 870 return formatNumber(number, characters, characterCount - 1); 871} 872 873String CSSPrimitiveValue::customCssText(CssTextFormattingFlags formattingFlag) const 874{ 875 // FIXME: return the original value instead of a generated one (e.g. color 876 // name if it was specified) - check what spec says about this 877 878 if (m_hasCachedCSSText) { 879 ASSERT(cssTextCache().contains(this)); 880 return cssTextCache().get(this); 881 } 882 883 String text; 884 switch (m_primitiveUnitType) { 885 case CSS_UNKNOWN: 886 // FIXME 887 break; 888 case CSS_NUMBER: 889 case CSS_PARSER_INTEGER: 890 text = formatNumber(m_value.num, ""); 891 break; 892 case CSS_PERCENTAGE: 893 text = formatNumber(m_value.num, "%"); 894 break; 895 case CSS_EMS: 896 text = formatNumber(m_value.num, "em"); 897 break; 898 case CSS_EXS: 899 text = formatNumber(m_value.num, "ex"); 900 break; 901 case CSS_REMS: 902 text = formatNumber(m_value.num, "rem"); 903 break; 904 case CSS_CHS: 905 text = formatNumber(m_value.num, "ch"); 906 break; 907 case CSS_PX: 908 text = formatNumber(m_value.num, "px"); 909 break; 910 case CSS_CM: 911 text = formatNumber(m_value.num, "cm"); 912 break; 913 case CSS_DPPX: 914 text = formatNumber(m_value.num, "dppx"); 915 break; 916 case CSS_DPI: 917 text = formatNumber(m_value.num, "dpi"); 918 break; 919 case CSS_DPCM: 920 text = formatNumber(m_value.num, "dpcm"); 921 break; 922 case CSS_MM: 923 text = formatNumber(m_value.num, "mm"); 924 break; 925 case CSS_IN: 926 text = formatNumber(m_value.num, "in"); 927 break; 928 case CSS_PT: 929 text = formatNumber(m_value.num, "pt"); 930 break; 931 case CSS_PC: 932 text = formatNumber(m_value.num, "pc"); 933 break; 934 case CSS_DEG: 935 text = formatNumber(m_value.num, "deg"); 936 break; 937 case CSS_RAD: 938 text = formatNumber(m_value.num, "rad"); 939 break; 940 case CSS_GRAD: 941 text = formatNumber(m_value.num, "grad"); 942 break; 943 case CSS_MS: 944 text = formatNumber(m_value.num, "ms"); 945 break; 946 case CSS_S: 947 text = formatNumber(m_value.num, "s"); 948 break; 949 case CSS_HZ: 950 text = formatNumber(m_value.num, "hz"); 951 break; 952 case CSS_KHZ: 953 text = formatNumber(m_value.num, "khz"); 954 break; 955 case CSS_TURN: 956 text = formatNumber(m_value.num, "turn"); 957 break; 958 case CSS_DIMENSION: 959 text = m_value.string; 960 break; 961 case CSS_STRING: 962 text = formattingFlag == AlwaysQuoteCSSString ? quoteCSSString(m_value.string) : quoteCSSStringIfNeeded(m_value.string); 963 break; 964 case CSS_URI: 965 text = "url(" + quoteCSSURLIfNeeded(m_value.string) + ")"; 966 break; 967 case CSS_VALUE_ID: 968 text = valueName(m_value.valueID); 969 break; 970 case CSS_PROPERTY_ID: 971 text = propertyName(m_value.propertyID); 972 break; 973 case CSS_ATTR: { 974 StringBuilder result; 975 result.reserveCapacity(6 + m_value.string->length()); 976 result.appendLiteral("attr("); 977 result.append(m_value.string); 978 result.append(')'); 979 980 text = result.toString(); 981 break; 982 } 983 case CSS_COUNTER_NAME: 984 text = "counter(" + String(m_value.string) + ')'; 985 break; 986 case CSS_COUNTER: { 987 StringBuilder result; 988 String separator = m_value.counter->separator(); 989 if (separator.isEmpty()) 990 result.appendLiteral("counter("); 991 else 992 result.appendLiteral("counters("); 993 994 result.append(m_value.counter->identifier()); 995 if (!separator.isEmpty()) { 996 result.appendLiteral(", "); 997 result.append(quoteCSSStringIfNeeded(separator)); 998 } 999 String listStyle = m_value.counter->listStyle(); 1000 if (!listStyle.isEmpty()) { 1001 result.appendLiteral(", "); 1002 result.append(listStyle); 1003 } 1004 result.append(')'); 1005 1006 text = result.toString(); 1007 break; 1008 } 1009 case CSS_RECT: 1010 text = getRectValue()->cssText(); 1011 break; 1012 case CSS_QUAD: 1013 text = getQuadValue()->cssText(); 1014 break; 1015 case CSS_RGBCOLOR: 1016 case CSS_PARSER_HEXCOLOR: { 1017 RGBA32 rgbColor = m_value.rgbcolor; 1018 if (m_primitiveUnitType == CSS_PARSER_HEXCOLOR) 1019 Color::parseHexColor(m_value.string, rgbColor); 1020 Color color(rgbColor); 1021 1022 StringBuilder result; 1023 result.reserveCapacity(32); 1024 bool colorHasAlpha = color.hasAlpha(); 1025 if (colorHasAlpha) 1026 result.append("rgba(", 5); 1027 else 1028 result.append("rgb(", 4); 1029 1030 result.appendNumber(static_cast<unsigned char>(color.red())); 1031 result.append(", ", 2); 1032 1033 result.appendNumber(static_cast<unsigned char>(color.green())); 1034 result.append(", ", 2); 1035 1036 result.appendNumber(static_cast<unsigned char>(color.blue())); 1037 if (colorHasAlpha) { 1038 result.append(", ", 2); 1039 1040 NumberToStringBuffer buffer; 1041 const char* alphaString = numberToFixedPrecisionString(color.alpha() / 255.0f, 6, buffer, true); 1042 result.append(alphaString, strlen(alphaString)); 1043 } 1044 1045 result.append(')'); 1046 text = result.toString(); 1047 break; 1048 } 1049 case CSS_PAIR: 1050 text = getPairValue()->cssText(); 1051 break; 1052 case CSS_PARSER_OPERATOR: { 1053 char c = static_cast<char>(m_value.parserOperator); 1054 text = String(&c, 1U); 1055 break; 1056 } 1057 case CSS_PARSER_IDENTIFIER: 1058 text = quoteCSSStringIfNeeded(m_value.string); 1059 break; 1060 case CSS_CALC: 1061 text = m_value.calc->cssText(); 1062 break; 1063 case CSS_SHAPE: 1064 text = m_value.shape->cssText(); 1065 break; 1066 case CSS_VW: 1067 text = formatNumber(m_value.num, "vw"); 1068 break; 1069 case CSS_VH: 1070 text = formatNumber(m_value.num, "vh"); 1071 break; 1072 case CSS_VMIN: 1073 text = formatNumber(m_value.num, "vmin"); 1074 break; 1075 case CSS_VMAX: 1076 text = formatNumber(m_value.num, "vmax"); 1077 break; 1078 case CSS_VARIABLE_NAME: 1079 text = "var(" + String(m_value.string) + ")"; 1080 break; 1081 } 1082 1083 ASSERT(!cssTextCache().contains(this)); 1084 cssTextCache().set(this, text); 1085 m_hasCachedCSSText = true; 1086 return text; 1087} 1088 1089String CSSPrimitiveValue::customSerializeResolvingVariables(const HashMap<AtomicString, String>& variables) const 1090{ 1091 if (isVariableName() && variables.contains(m_value.string)) 1092 return variables.get(m_value.string); 1093 if (CSSCalcValue* calcValue = cssCalcValue()) 1094 return calcValue->customSerializeResolvingVariables(variables); 1095 if (Pair* pairValue = getPairValue()) 1096 return pairValue->serializeResolvingVariables(variables); 1097 if (Rect* rectVal = getRectValue()) 1098 return rectVal->serializeResolvingVariables(variables); 1099 if (Quad* quadVal = getQuadValue()) 1100 return quadVal->serializeResolvingVariables(variables); 1101 if (CSSBasicShape* shapeValue = getShapeValue()) 1102 return shapeValue->serializeResolvingVariables(variables); 1103 return customCssText(); 1104} 1105 1106bool CSSPrimitiveValue::hasVariableReference() const 1107{ 1108 if (CSSCalcValue* calcValue = cssCalcValue()) 1109 return calcValue->hasVariableReference(); 1110 if (Pair* pairValue = getPairValue()) 1111 return pairValue->hasVariableReference(); 1112 if (Quad* quadValue = getQuadValue()) 1113 return quadValue->hasVariableReference(); 1114 if (Rect* rectValue = getRectValue()) 1115 return rectValue->hasVariableReference(); 1116 if (CSSBasicShape* shapeValue = getShapeValue()) 1117 return shapeValue->hasVariableReference(); 1118 return isVariableName(); 1119} 1120 1121void CSSPrimitiveValue::addSubresourceStyleURLs(ListHashSet<KURL>& urls, const StyleSheetContents* styleSheet) const 1122{ 1123 if (m_primitiveUnitType == CSS_URI) 1124 addSubresourceURL(urls, styleSheet->completeURL(m_value.string)); 1125} 1126 1127Length CSSPrimitiveValue::viewportPercentageLength() 1128{ 1129 ASSERT(isViewportPercentageLength()); 1130 Length viewportLength; 1131 switch (m_primitiveUnitType) { 1132 case CSS_VW: 1133 viewportLength = Length(getDoubleValue(), ViewportPercentageWidth); 1134 break; 1135 case CSS_VH: 1136 viewportLength = Length(getDoubleValue(), ViewportPercentageHeight); 1137 break; 1138 case CSS_VMIN: 1139 viewportLength = Length(getDoubleValue(), ViewportPercentageMin); 1140 break; 1141 case CSS_VMAX: 1142 viewportLength = Length(getDoubleValue(), ViewportPercentageMax); 1143 break; 1144 default: 1145 break; 1146 } 1147 return viewportLength; 1148} 1149 1150PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::cloneForCSSOM() const 1151{ 1152 RefPtr<CSSPrimitiveValue> result; 1153 1154 switch (m_primitiveUnitType) { 1155 case CSS_STRING: 1156 case CSS_URI: 1157 case CSS_ATTR: 1158 case CSS_COUNTER_NAME: 1159 result = CSSPrimitiveValue::create(m_value.string, static_cast<UnitTypes>(m_primitiveUnitType)); 1160 break; 1161 case CSS_COUNTER: 1162 result = CSSPrimitiveValue::create(m_value.counter->cloneForCSSOM()); 1163 break; 1164 case CSS_RECT: 1165 result = CSSPrimitiveValue::create(m_value.rect->cloneForCSSOM()); 1166 break; 1167 case CSS_QUAD: 1168 result = CSSPrimitiveValue::create(m_value.quad->cloneForCSSOM()); 1169 break; 1170 case CSS_PAIR: 1171 // Pair is not exposed to the CSSOM, no need for a deep clone. 1172 result = CSSPrimitiveValue::create(m_value.pair); 1173 break; 1174 case CSS_CALC: 1175 // CSSCalcValue is not exposed to the CSSOM, no need for a deep clone. 1176 result = CSSPrimitiveValue::create(m_value.calc); 1177 break; 1178 case CSS_SHAPE: 1179 // CSSShapeValue is not exposed to the CSSOM, no need for a deep clone. 1180 result = CSSPrimitiveValue::create(m_value.shape); 1181 break; 1182 case CSS_NUMBER: 1183 case CSS_PARSER_INTEGER: 1184 case CSS_PERCENTAGE: 1185 case CSS_EMS: 1186 case CSS_EXS: 1187 case CSS_REMS: 1188 case CSS_CHS: 1189 case CSS_PX: 1190 case CSS_CM: 1191 case CSS_MM: 1192 case CSS_IN: 1193 case CSS_PT: 1194 case CSS_PC: 1195 case CSS_DEG: 1196 case CSS_RAD: 1197 case CSS_GRAD: 1198 case CSS_MS: 1199 case CSS_S: 1200 case CSS_HZ: 1201 case CSS_KHZ: 1202 case CSS_TURN: 1203 case CSS_VW: 1204 case CSS_VH: 1205 case CSS_VMIN: 1206 case CSS_VMAX: 1207 case CSS_DPPX: 1208 case CSS_DPI: 1209 case CSS_DPCM: 1210 result = CSSPrimitiveValue::create(m_value.num, static_cast<UnitTypes>(m_primitiveUnitType)); 1211 break; 1212 case CSS_PROPERTY_ID: 1213 result = CSSPrimitiveValue::createIdentifier(m_value.propertyID); 1214 break; 1215 case CSS_VALUE_ID: 1216 result = CSSPrimitiveValue::createIdentifier(m_value.valueID); 1217 break; 1218 case CSS_RGBCOLOR: 1219 result = CSSPrimitiveValue::createColor(m_value.rgbcolor); 1220 break; 1221 case CSS_DIMENSION: 1222 case CSS_UNKNOWN: 1223 case CSS_PARSER_OPERATOR: 1224 case CSS_PARSER_IDENTIFIER: 1225 case CSS_PARSER_HEXCOLOR: 1226 ASSERT_NOT_REACHED(); 1227 break; 1228 } 1229 if (result) 1230 result->setCSSOMSafe(); 1231 1232 return result; 1233} 1234 1235bool CSSPrimitiveValue::equals(const CSSPrimitiveValue& other) const 1236{ 1237 if (m_primitiveUnitType != other.m_primitiveUnitType) 1238 return false; 1239 1240 switch (m_primitiveUnitType) { 1241 case CSS_UNKNOWN: 1242 return false; 1243 case CSS_NUMBER: 1244 case CSS_PARSER_INTEGER: 1245 case CSS_PERCENTAGE: 1246 case CSS_EMS: 1247 case CSS_EXS: 1248 case CSS_REMS: 1249 case CSS_PX: 1250 case CSS_CM: 1251 case CSS_DPPX: 1252 case CSS_DPI: 1253 case CSS_DPCM: 1254 case CSS_MM: 1255 case CSS_IN: 1256 case CSS_PT: 1257 case CSS_PC: 1258 case CSS_DEG: 1259 case CSS_RAD: 1260 case CSS_GRAD: 1261 case CSS_MS: 1262 case CSS_S: 1263 case CSS_HZ: 1264 case CSS_KHZ: 1265 case CSS_TURN: 1266 case CSS_VW: 1267 case CSS_VH: 1268 case CSS_VMIN: 1269 case CSS_VMAX: 1270 case CSS_DIMENSION: 1271 return m_value.num == other.m_value.num; 1272 case CSS_PROPERTY_ID: 1273 return propertyName(m_value.propertyID) == propertyName(other.m_value.propertyID); 1274 case CSS_VALUE_ID: 1275 return valueName(m_value.valueID) == valueName(other.m_value.valueID); 1276 case CSS_STRING: 1277 case CSS_URI: 1278 case CSS_ATTR: 1279 case CSS_COUNTER_NAME: 1280 case CSS_PARSER_IDENTIFIER: 1281 case CSS_PARSER_HEXCOLOR: 1282 case CSS_VARIABLE_NAME: 1283 return equal(m_value.string, other.m_value.string); 1284 case CSS_COUNTER: 1285 return m_value.counter && other.m_value.counter && m_value.counter->equals(*other.m_value.counter); 1286 case CSS_RECT: 1287 return m_value.rect && other.m_value.rect && m_value.rect->equals(*other.m_value.rect); 1288 case CSS_QUAD: 1289 return m_value.quad && other.m_value.quad && m_value.quad->equals(*other.m_value.quad); 1290 case CSS_RGBCOLOR: 1291 return m_value.rgbcolor == other.m_value.rgbcolor; 1292 case CSS_PAIR: 1293 return m_value.pair && other.m_value.pair && m_value.pair->equals(*other.m_value.pair); 1294 case CSS_PARSER_OPERATOR: 1295 return m_value.parserOperator == other.m_value.parserOperator; 1296 case CSS_CALC: 1297 return m_value.calc && other.m_value.calc && m_value.calc->equals(*other.m_value.calc); 1298 case CSS_SHAPE: 1299 return m_value.shape && other.m_value.shape && m_value.shape->equals(*other.m_value.shape); 1300 } 1301 return false; 1302} 1303 1304} // namespace WebCore 1305