2 * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2012 Apple Inc. All rights reserved.
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.
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.
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.
22 #include "CSSPrimitiveValue.h"
24 #include "CSSBasicShapes.h"
25 #include "CSSCalculationValue.h"
26 #include "CSSHelper.h"
27 #include "CSSParser.h"
28 #include "CSSPropertyNames.h"
29 #include "CSSValueKeywords.h"
30 #include "CalculationValue.h"
33 #include "ExceptionCode.h"
35 #include "LayoutUnit.h"
40 #include "RenderStyle.h"
41 #include "StyleSheetContents.h"
42 #include "WebCoreMemoryInstrumentation.h"
43 #include <wtf/ASCIICType.h>
44 #include <wtf/DecimalNumber.h>
45 #include <wtf/StdLibExtras.h>
46 #include <wtf/text/StringBuffer.h>
47 #include <wtf/text/StringBuilder.h>
49 #if ENABLE(DASHBOARD_SUPPORT)
50 #include "DashboardRegion.h"
57 // Max/min values for CSS, needs to slightly smaller/larger than the true max/min values to allow for rounding without overflowing.
58 // Subtract two (rather than one) to allow for values to be converted to float and back without exceeding the LayoutUnit::max.
59 const int maxValueForCssLength = INT_MAX / kFixedPointDenominator - 2;
60 const int minValueForCssLength = INT_MIN / kFixedPointDenominator + 2;
62 static inline bool isValidCSSUnitTypeForDoubleConversion(CSSPrimitiveValue::UnitTypes unitType)
65 case CSSPrimitiveValue::CSS_CALC:
66 case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER:
67 case CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH:
68 case CSSPrimitiveValue::CSS_CM:
69 case CSSPrimitiveValue::CSS_DEG:
70 case CSSPrimitiveValue::CSS_DIMENSION:
71 #if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
72 case CSSPrimitiveValue::CSS_DPPX:
73 case CSSPrimitiveValue::CSS_DPI:
74 case CSSPrimitiveValue::CSS_DPCM:
76 case CSSPrimitiveValue::CSS_EMS:
77 case CSSPrimitiveValue::CSS_EXS:
78 case CSSPrimitiveValue::CSS_GRAD:
79 case CSSPrimitiveValue::CSS_HZ:
80 case CSSPrimitiveValue::CSS_IN:
81 case CSSPrimitiveValue::CSS_KHZ:
82 case CSSPrimitiveValue::CSS_MM:
83 case CSSPrimitiveValue::CSS_MS:
84 case CSSPrimitiveValue::CSS_NUMBER:
85 case CSSPrimitiveValue::CSS_PERCENTAGE:
86 case CSSPrimitiveValue::CSS_PC:
87 case CSSPrimitiveValue::CSS_PT:
88 case CSSPrimitiveValue::CSS_PX:
89 case CSSPrimitiveValue::CSS_RAD:
90 case CSSPrimitiveValue::CSS_REMS:
91 case CSSPrimitiveValue::CSS_S:
92 case CSSPrimitiveValue::CSS_TURN:
93 case CSSPrimitiveValue::CSS_VW:
94 case CSSPrimitiveValue::CSS_VH:
95 case CSSPrimitiveValue::CSS_VMIN:
96 case CSSPrimitiveValue::CSS_VMAX:
98 case CSSPrimitiveValue::CSS_ATTR:
99 case CSSPrimitiveValue::CSS_COUNTER:
100 case CSSPrimitiveValue::CSS_COUNTER_NAME:
101 #if ENABLE(DASHBOARD_SUPPORT)
102 case CSSPrimitiveValue::CSS_DASHBOARD_REGION:
104 #if !ENABLE(CSS_IMAGE_RESOLUTION) && !ENABLE(RESOLUTION_MEDIA_QUERY)
105 case CSSPrimitiveValue::CSS_DPPX:
106 case CSSPrimitiveValue::CSS_DPI:
107 case CSSPrimitiveValue::CSS_DPCM:
109 case CSSPrimitiveValue::CSS_IDENT:
110 case CSSPrimitiveValue::CSS_PAIR:
111 case CSSPrimitiveValue::CSS_PARSER_HEXCOLOR:
112 case CSSPrimitiveValue::CSS_PARSER_IDENTIFIER:
113 case CSSPrimitiveValue::CSS_PARSER_INTEGER:
114 case CSSPrimitiveValue::CSS_PARSER_OPERATOR:
115 case CSSPrimitiveValue::CSS_RECT:
116 case CSSPrimitiveValue::CSS_QUAD:
117 case CSSPrimitiveValue::CSS_RGBCOLOR:
118 case CSSPrimitiveValue::CSS_SHAPE:
119 case CSSPrimitiveValue::CSS_STRING:
120 case CSSPrimitiveValue::CSS_UNICODE_RANGE:
121 case CSSPrimitiveValue::CSS_UNKNOWN:
122 case CSSPrimitiveValue::CSS_URI:
123 #if ENABLE(CSS_VARIABLES)
124 case CSSPrimitiveValue::CSS_VARIABLE_NAME:
129 ASSERT_NOT_REACHED();
133 static CSSPrimitiveValue::UnitCategory unitCategory(CSSPrimitiveValue::UnitTypes type)
135 // Here we violate the spec (http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue) and allow conversions
136 // between CSS_PX and relative lengths (see cssPixelsPerInch comment in CSSHelper.h for the topic treatment).
138 case CSSPrimitiveValue::CSS_NUMBER:
139 return CSSPrimitiveValue::UNumber;
140 case CSSPrimitiveValue::CSS_PERCENTAGE:
141 return CSSPrimitiveValue::UPercent;
142 case CSSPrimitiveValue::CSS_PX:
143 case CSSPrimitiveValue::CSS_CM:
144 case CSSPrimitiveValue::CSS_MM:
145 case CSSPrimitiveValue::CSS_IN:
146 case CSSPrimitiveValue::CSS_PT:
147 case CSSPrimitiveValue::CSS_PC:
148 return CSSPrimitiveValue::ULength;
149 case CSSPrimitiveValue::CSS_MS:
150 case CSSPrimitiveValue::CSS_S:
151 return CSSPrimitiveValue::UTime;
152 case CSSPrimitiveValue::CSS_DEG:
153 case CSSPrimitiveValue::CSS_RAD:
154 case CSSPrimitiveValue::CSS_GRAD:
155 case CSSPrimitiveValue::CSS_TURN:
156 return CSSPrimitiveValue::UAngle;
157 case CSSPrimitiveValue::CSS_HZ:
158 case CSSPrimitiveValue::CSS_KHZ:
159 return CSSPrimitiveValue::UFrequency;
160 case CSSPrimitiveValue::CSS_VW:
161 case CSSPrimitiveValue::CSS_VH:
162 case CSSPrimitiveValue::CSS_VMIN:
163 case CSSPrimitiveValue::CSS_VMAX:
164 return CSSPrimitiveValue::UViewportPercentageLength;
165 #if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
166 case CSSPrimitiveValue::CSS_DPPX:
167 case CSSPrimitiveValue::CSS_DPI:
168 case CSSPrimitiveValue::CSS_DPCM:
169 return CSSPrimitiveValue::UResolution;
172 return CSSPrimitiveValue::UOther;
176 typedef HashMap<const CSSPrimitiveValue*, String> CSSTextCache;
177 static CSSTextCache& cssTextCache()
179 DEFINE_STATIC_LOCAL(CSSTextCache, cache, ());
183 unsigned short CSSPrimitiveValue::primitiveType() const
185 if (m_primitiveUnitType != CSSPrimitiveValue::CSS_CALC)
186 return m_primitiveUnitType;
188 switch (m_value.calc->category()) {
190 return CSSPrimitiveValue::CSS_NUMBER;
192 return CSSPrimitiveValue::CSS_PERCENTAGE;
194 return CSSPrimitiveValue::CSS_PX;
195 case CalcPercentNumber:
196 return CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_NUMBER;
197 case CalcPercentLength:
198 return CSSPrimitiveValue::CSS_CALC_PERCENTAGE_WITH_LENGTH;
199 #if ENABLE(CSS_VARIABLES)
201 return CSSPrimitiveValue::CSS_UNKNOWN; // The type of a calculation containing a variable cannot be known until the value of the variable is determined.
204 return CSSPrimitiveValue::CSS_UNKNOWN;
206 return CSSPrimitiveValue::CSS_UNKNOWN;
209 static const AtomicString& valueOrPropertyName(int valueOrPropertyID)
211 ASSERT_ARG(valueOrPropertyID, valueOrPropertyID >= 0);
212 ASSERT_ARG(valueOrPropertyID, valueOrPropertyID < numCSSValueKeywords || (valueOrPropertyID >= firstCSSProperty && valueOrPropertyID < firstCSSProperty + numCSSProperties));
214 if (valueOrPropertyID < 0)
217 if (valueOrPropertyID < numCSSValueKeywords) {
218 static AtomicString* keywordStrings = new AtomicString[numCSSValueKeywords]; // Leaked intentionally.
219 AtomicString& keywordString = keywordStrings[valueOrPropertyID];
220 if (keywordString.isNull())
221 keywordString = getValueName(valueOrPropertyID);
222 return keywordString;
225 return getPropertyNameAtomicString(static_cast<CSSPropertyID>(valueOrPropertyID));
228 CSSPrimitiveValue::CSSPrimitiveValue(int ident)
229 : CSSValue(PrimitiveClass)
231 m_primitiveUnitType = CSS_IDENT;
232 m_value.ident = ident;
235 CSSPrimitiveValue::CSSPrimitiveValue(double num, UnitTypes type)
236 : CSSValue(PrimitiveClass)
238 m_primitiveUnitType = type;
239 ASSERT(isfinite(num));
243 CSSPrimitiveValue::CSSPrimitiveValue(const String& str, UnitTypes type)
244 : CSSValue(PrimitiveClass)
246 m_primitiveUnitType = type;
247 if ((m_value.string = str.impl()))
248 m_value.string->ref();
252 CSSPrimitiveValue::CSSPrimitiveValue(RGBA32 color)
253 : CSSValue(PrimitiveClass)
255 m_primitiveUnitType = CSS_RGBCOLOR;
256 m_value.rgbcolor = color;
259 CSSPrimitiveValue::CSSPrimitiveValue(const Length& length)
260 : CSSValue(PrimitiveClass)
262 switch (length.type()) {
264 m_primitiveUnitType = CSS_IDENT;
265 m_value.ident = CSSValueAuto;
268 m_primitiveUnitType = CSS_PX;
269 m_value.num = length.value();
272 m_primitiveUnitType = CSS_IDENT;
273 m_value.ident = CSSValueIntrinsic;
276 m_primitiveUnitType = CSS_IDENT;
277 m_value.ident = CSSValueMinIntrinsic;
280 m_primitiveUnitType = CSS_IDENT;
281 m_value.ident = CSSValueWebkitMinContent;
284 m_primitiveUnitType = CSS_IDENT;
285 m_value.ident = CSSValueWebkitMaxContent;
288 m_primitiveUnitType = CSS_IDENT;
289 m_value.ident = CSSValueWebkitFillAvailable;
292 m_primitiveUnitType = CSS_IDENT;
293 m_value.ident = CSSValueWebkitFitContent;
296 m_primitiveUnitType = CSS_PERCENTAGE;
297 ASSERT(isfinite(length.percent()));
298 m_value.num = length.percent();
300 case ViewportPercentageWidth:
301 m_primitiveUnitType = CSS_VW;
302 m_value.num = length.viewportPercentageLength();
304 case ViewportPercentageHeight:
305 m_primitiveUnitType = CSS_VH;
306 m_value.num = length.viewportPercentageLength();
308 case ViewportPercentageMin:
309 m_primitiveUnitType = CSS_VMIN;
310 m_value.num = length.viewportPercentageLength();
312 case ViewportPercentageMax:
313 m_primitiveUnitType = CSS_VMAX;
314 m_value.num = length.viewportPercentageLength();
319 ASSERT_NOT_REACHED();
324 void CSSPrimitiveValue::init(PassRefPtr<Counter> c)
326 m_primitiveUnitType = CSS_COUNTER;
327 m_hasCachedCSSText = false;
328 m_value.counter = c.leakRef();
331 void CSSPrimitiveValue::init(PassRefPtr<Rect> r)
333 m_primitiveUnitType = CSS_RECT;
334 m_hasCachedCSSText = false;
335 m_value.rect = r.leakRef();
338 void CSSPrimitiveValue::init(PassRefPtr<Quad> quad)
340 m_primitiveUnitType = CSS_QUAD;
341 m_hasCachedCSSText = false;
342 m_value.quad = quad.leakRef();
345 #if ENABLE(DASHBOARD_SUPPORT)
346 void CSSPrimitiveValue::init(PassRefPtr<DashboardRegion> r)
348 m_primitiveUnitType = CSS_DASHBOARD_REGION;
349 m_hasCachedCSSText = false;
350 m_value.region = r.leakRef();
354 void CSSPrimitiveValue::init(PassRefPtr<Pair> p)
356 m_primitiveUnitType = CSS_PAIR;
357 m_hasCachedCSSText = false;
358 m_value.pair = p.leakRef();
361 void CSSPrimitiveValue::init(PassRefPtr<CSSCalcValue> c)
363 m_primitiveUnitType = CSS_CALC;
364 m_hasCachedCSSText = false;
365 m_value.calc = c.leakRef();
368 void CSSPrimitiveValue::init(PassRefPtr<CSSBasicShape> shape)
370 m_primitiveUnitType = CSS_SHAPE;
371 m_hasCachedCSSText = false;
372 m_value.shape = shape.leakRef();
375 CSSPrimitiveValue::~CSSPrimitiveValue()
380 void CSSPrimitiveValue::cleanup()
382 switch (static_cast<UnitTypes>(m_primitiveUnitType)) {
386 case CSS_COUNTER_NAME:
387 #if ENABLE(CSS_VARIABLES)
388 case CSS_VARIABLE_NAME:
390 case CSS_PARSER_HEXCOLOR:
392 m_value.string->deref();
395 m_value.counter->deref();
398 m_value.rect->deref();
401 m_value.quad->deref();
404 m_value.pair->deref();
406 #if ENABLE(DASHBOARD_SUPPORT)
407 case CSS_DASHBOARD_REGION:
409 m_value.region->deref();
413 m_value.calc->deref();
415 case CSS_CALC_PERCENTAGE_WITH_NUMBER:
416 case CSS_CALC_PERCENTAGE_WITH_LENGTH:
417 ASSERT_NOT_REACHED();
420 m_value.shape->deref();
423 case CSS_PARSER_INTEGER:
453 case CSS_UNICODE_RANGE:
454 case CSS_PARSER_OPERATOR:
455 case CSS_PARSER_IDENTIFIER:
458 m_primitiveUnitType = 0;
459 if (m_hasCachedCSSText) {
460 cssTextCache().remove(this);
461 m_hasCachedCSSText = false;
465 double CSSPrimitiveValue::computeDegrees()
467 switch (m_primitiveUnitType) {
469 return getDoubleValue();
471 return rad2deg(getDoubleValue());
473 return grad2deg(getDoubleValue());
475 return turn2deg(getDoubleValue());
477 ASSERT_NOT_REACHED();
482 template<> int CSSPrimitiveValue::computeLength(RenderStyle* style, RenderStyle* rootStyle, float multiplier, bool computingFontSize)
484 return roundForImpreciseConversion<int>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize));
487 template<> unsigned CSSPrimitiveValue::computeLength(RenderStyle* style, RenderStyle* rootStyle, float multiplier, bool computingFontSize)
489 return roundForImpreciseConversion<unsigned>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize));
492 template<> Length CSSPrimitiveValue::computeLength(RenderStyle* style, RenderStyle* rootStyle, float multiplier, bool computingFontSize)
494 #if ENABLE(SUBPIXEL_LAYOUT)
495 return Length(clampTo<float>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize), minValueForCssLength, maxValueForCssLength), Fixed);
497 return Length(clampTo<float>(roundForImpreciseConversion<float>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize)), minValueForCssLength, maxValueForCssLength), Fixed);
501 template<> short CSSPrimitiveValue::computeLength(RenderStyle* style, RenderStyle* rootStyle, float multiplier, bool computingFontSize)
503 return roundForImpreciseConversion<short>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize));
506 template<> unsigned short CSSPrimitiveValue::computeLength(RenderStyle* style, RenderStyle* rootStyle, float multiplier, bool computingFontSize)
508 return roundForImpreciseConversion<unsigned short>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize));
511 template<> float CSSPrimitiveValue::computeLength(RenderStyle* style, RenderStyle* rootStyle, float multiplier, bool computingFontSize)
513 return static_cast<float>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize));
516 template<> double CSSPrimitiveValue::computeLength(RenderStyle* style, RenderStyle* rootStyle, float multiplier, bool computingFontSize)
518 return computeLengthDouble(style, rootStyle, multiplier, computingFontSize);
521 double CSSPrimitiveValue::computeLengthDouble(RenderStyle* style, RenderStyle* rootStyle, float multiplier, bool computingFontSize)
523 if (m_primitiveUnitType == CSS_CALC)
524 // The multiplier and factor is applied to each value in the calc expression individually
525 return m_value.calc->computeLengthPx(style, rootStyle, multiplier, computingFontSize);
529 switch (primitiveType()) {
531 factor = computingFontSize ? style->fontDescription().specifiedSize() : style->fontDescription().computedSize();
534 // FIXME: We have a bug right now where the zoom will be applied twice to EX units.
535 // We really need to compute EX using fontMetrics for the original specifiedSize and not use
536 // our actual constructed rendering font.
537 if (style->fontMetrics().hasXHeight())
538 factor = style->fontMetrics().xHeight();
540 factor = (computingFontSize ? style->fontDescription().specifiedSize() : style->fontDescription().computedSize()) / 2.0;
544 factor = computingFontSize ? rootStyle->fontDescription().specifiedSize() : rootStyle->fontDescription().computedSize();
552 factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
555 factor = cssPixelsPerInch / 25.4;
558 factor = cssPixelsPerInch;
561 factor = cssPixelsPerInch / 72.0;
565 factor = cssPixelsPerInch * 12.0 / 72.0;
567 case CSS_CALC_PERCENTAGE_WITH_LENGTH:
568 case CSS_CALC_PERCENTAGE_WITH_NUMBER:
569 ASSERT_NOT_REACHED();
572 ASSERT_NOT_REACHED();
576 // We do not apply the zoom factor when we are computing the value of the font-size property. The zooming
577 // for font sizes is much more complicated, since we have to worry about enforcing the minimum font size preference
578 // as well as enforcing the implicit "smart minimum." In addition the CSS property text-size-adjust is used to
579 // prevent text from zooming at all. Therefore we will not apply the zoom here if we are computing font-size.
580 double result = getDoubleValue() * factor;
581 if (computingFontSize || isFontRelativeLength())
584 return result * multiplier;
587 void CSSPrimitiveValue::setFloatValue(unsigned short, double, ExceptionCode& ec)
589 // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects.
590 // No other engine supports mutating style through this API. Computed style is always read-only anyway.
591 // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation.
592 ec = NO_MODIFICATION_ALLOWED_ERR;
595 static double conversionToCanonicalUnitsScaleFactor(unsigned short unitType)
598 // FIXME: the switch can be replaced by an array of scale factors.
600 // These are "canonical" units in their respective categories.
601 case CSSPrimitiveValue::CSS_PX:
602 case CSSPrimitiveValue::CSS_DEG:
603 case CSSPrimitiveValue::CSS_MS:
604 case CSSPrimitiveValue::CSS_HZ:
606 case CSSPrimitiveValue::CSS_CM:
607 factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
609 case CSSPrimitiveValue::CSS_DPCM:
610 factor = 2.54 / cssPixelsPerInch; // (2.54 cm/in)
612 case CSSPrimitiveValue::CSS_MM:
613 factor = cssPixelsPerInch / 25.4;
615 case CSSPrimitiveValue::CSS_IN:
616 factor = cssPixelsPerInch;
618 case CSSPrimitiveValue::CSS_DPI:
619 factor = 1 / cssPixelsPerInch;
621 case CSSPrimitiveValue::CSS_PT:
622 factor = cssPixelsPerInch / 72.0;
624 case CSSPrimitiveValue::CSS_PC:
625 factor = cssPixelsPerInch * 12.0 / 72.0; // 1 pc == 12 pt
627 case CSSPrimitiveValue::CSS_RAD:
628 factor = 180 / piDouble;
630 case CSSPrimitiveValue::CSS_GRAD:
633 case CSSPrimitiveValue::CSS_TURN:
636 case CSSPrimitiveValue::CSS_S:
637 case CSSPrimitiveValue::CSS_KHZ:
647 double CSSPrimitiveValue::getDoubleValue(unsigned short unitType, ExceptionCode& ec) const
650 bool success = getDoubleValueInternal(static_cast<UnitTypes>(unitType), &result);
652 ec = INVALID_ACCESS_ERR;
660 double CSSPrimitiveValue::getDoubleValue(unsigned short unitType) const
663 getDoubleValueInternal(static_cast<UnitTypes>(unitType), &result);
667 double CSSPrimitiveValue::getDoubleValue() const
669 return m_primitiveUnitType != CSS_CALC ? m_value.num : m_value.calc->doubleValue();
672 CSSPrimitiveValue::UnitTypes CSSPrimitiveValue::canonicalUnitTypeForCategory(UnitCategory category)
674 // The canonical unit type is chosen according to the way CSSParser::validUnit() chooses the default unit
675 // in each category (based on unitflags).
682 return CSS_UNKNOWN; // Cannot convert between numbers and percent.
689 case UViewportPercentageLength:
690 return CSS_UNKNOWN; // Cannot convert between numbers and relative lengths.
691 #if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
700 bool CSSPrimitiveValue::getDoubleValueInternal(UnitTypes requestedUnitType, double* result) const
702 if (!isValidCSSUnitTypeForDoubleConversion(static_cast<UnitTypes>(m_primitiveUnitType)) || !isValidCSSUnitTypeForDoubleConversion(requestedUnitType))
705 UnitTypes sourceUnitType = static_cast<UnitTypes>(primitiveType());
706 if (requestedUnitType == sourceUnitType || requestedUnitType == CSS_DIMENSION) {
707 *result = getDoubleValue();
711 UnitCategory sourceCategory = unitCategory(sourceUnitType);
712 ASSERT(sourceCategory != UOther);
714 UnitTypes targetUnitType = requestedUnitType;
715 UnitCategory targetCategory = unitCategory(targetUnitType);
716 ASSERT(targetCategory != UOther);
718 // Cannot convert between unrelated unit categories if one of them is not UNumber.
719 if (sourceCategory != targetCategory && sourceCategory != UNumber && targetCategory != UNumber)
722 if (targetCategory == UNumber) {
723 // We interpret conversion to CSS_NUMBER as conversion to a canonical unit in this value's category.
724 targetUnitType = canonicalUnitTypeForCategory(sourceCategory);
725 if (targetUnitType == CSS_UNKNOWN)
729 if (sourceUnitType == CSS_NUMBER) {
730 // We interpret conversion from CSS_NUMBER in the same way as CSSParser::validUnit() while using non-strict mode.
731 sourceUnitType = canonicalUnitTypeForCategory(targetCategory);
732 if (sourceUnitType == CSS_UNKNOWN)
736 double convertedValue = getDoubleValue();
738 // First convert the value from m_primitiveUnitType to canonical type.
739 double factor = conversionToCanonicalUnitsScaleFactor(sourceUnitType);
740 convertedValue *= factor;
742 // Now convert from canonical type to the target unitType.
743 factor = conversionToCanonicalUnitsScaleFactor(targetUnitType);
744 convertedValue /= factor;
746 *result = convertedValue;
750 void CSSPrimitiveValue::setStringValue(unsigned short, const String&, ExceptionCode& ec)
752 // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects.
753 // No other engine supports mutating style through this API. Computed style is always read-only anyway.
754 // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation.
755 ec = NO_MODIFICATION_ALLOWED_ERR;
758 String CSSPrimitiveValue::getStringValue(ExceptionCode& ec) const
761 switch (m_primitiveUnitType) {
765 #if ENABLE(CSS_VARIABLES)
766 case CSS_VARIABLE_NAME:
768 return m_value.string;
770 return valueOrPropertyName(m_value.ident);
772 ec = INVALID_ACCESS_ERR;
779 String CSSPrimitiveValue::getStringValue() const
781 switch (m_primitiveUnitType) {
785 #if ENABLE(CSS_VARIABLES)
786 case CSS_VARIABLE_NAME:
788 return m_value.string;
790 return valueOrPropertyName(m_value.ident);
798 Counter* CSSPrimitiveValue::getCounterValue(ExceptionCode& ec) const
801 if (m_primitiveUnitType != CSS_COUNTER) {
802 ec = INVALID_ACCESS_ERR;
806 return m_value.counter;
809 Rect* CSSPrimitiveValue::getRectValue(ExceptionCode& ec) const
812 if (m_primitiveUnitType != CSS_RECT) {
813 ec = INVALID_ACCESS_ERR;
820 Quad* CSSPrimitiveValue::getQuadValue(ExceptionCode& ec) const
823 if (m_primitiveUnitType != CSS_QUAD) {
824 ec = INVALID_ACCESS_ERR;
831 PassRefPtr<RGBColor> CSSPrimitiveValue::getRGBColorValue(ExceptionCode& ec) const
834 if (m_primitiveUnitType != CSS_RGBCOLOR) {
835 ec = INVALID_ACCESS_ERR;
839 // FIMXE: This should not return a new object for each invocation.
840 return RGBColor::create(m_value.rgbcolor);
843 Pair* CSSPrimitiveValue::getPairValue(ExceptionCode& ec) const
846 if (m_primitiveUnitType != CSS_PAIR) {
847 ec = INVALID_ACCESS_ERR;
854 static String formatNumber(double number, const char* suffix, unsigned suffixLength)
856 DecimalNumber decimal(number);
858 StringBuffer<LChar> buffer(decimal.bufferLengthForStringDecimal() + suffixLength);
859 unsigned length = decimal.toStringDecimal(buffer.characters(), buffer.length());
860 ASSERT(length + suffixLength == buffer.length());
862 for (unsigned i = 0; i < suffixLength; ++i)
863 buffer[length + i] = static_cast<LChar>(suffix[i]);
865 return String::adopt(buffer);
868 template <unsigned characterCount>
869 ALWAYS_INLINE static String formatNumber(double number, const char (&characters)[characterCount])
871 return formatNumber(number, characters, characterCount - 1);
874 String CSSPrimitiveValue::customCssText() const
876 // FIXME: return the original value instead of a generated one (e.g. color
877 // name if it was specified) - check what spec says about this
879 if (m_hasCachedCSSText) {
880 ASSERT(cssTextCache().contains(this));
881 return cssTextCache().get(this);
885 switch (m_primitiveUnitType) {
890 case CSS_PARSER_INTEGER:
891 text = formatNumber(m_value.num, "");
894 text = formatNumber(m_value.num, "%");
897 text = formatNumber(m_value.num, "em");
900 text = formatNumber(m_value.num, "ex");
903 text = formatNumber(m_value.num, "rem");
906 text = formatNumber(m_value.num, "px");
909 text = formatNumber(m_value.num, "cm");
911 #if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
913 text = formatNumber(m_value.num, "dppx");
916 text = formatNumber(m_value.num, "dpi");
919 text = formatNumber(m_value.num, "dpcm");
923 text = formatNumber(m_value.num, "mm");
926 text = formatNumber(m_value.num, "in");
929 text = formatNumber(m_value.num, "pt");
932 text = formatNumber(m_value.num, "pc");
935 text = formatNumber(m_value.num, "deg");
938 text = formatNumber(m_value.num, "rad");
941 text = formatNumber(m_value.num, "grad");
944 text = formatNumber(m_value.num, "ms");
947 text = formatNumber(m_value.num, "s");
950 text = formatNumber(m_value.num, "hz");
953 text = formatNumber(m_value.num, "khz");
956 text = formatNumber(m_value.num, "turn");
962 text = quoteCSSStringIfNeeded(m_value.string);
965 text = "url(" + quoteCSSURLIfNeeded(m_value.string) + ")";
968 text = valueOrPropertyName(m_value.ident);
971 StringBuilder result;
972 result.reserveCapacity(6 + m_value.string->length());
973 result.appendLiteral("attr(");
974 result.append(m_value.string);
977 text = result.toString();
980 case CSS_COUNTER_NAME:
981 text = "counter(" + String(m_value.string) + ')';
984 StringBuilder result;
985 String separator = m_value.counter->separator();
986 if (separator.isEmpty())
987 result.appendLiteral("counter(");
989 result.appendLiteral("counters(");
991 result.append(m_value.counter->identifier());
992 if (!separator.isEmpty()) {
993 result.appendLiteral(", ");
994 result.append(quoteCSSStringIfNeeded(separator));
996 String listStyle = m_value.counter->listStyle();
997 if (!listStyle.isEmpty()) {
998 result.appendLiteral(", ");
999 result.append(listStyle);
1003 text = result.toString();
1007 text = getRectValue()->cssText();
1010 text = getQuadValue()->cssText();
1013 case CSS_PARSER_HEXCOLOR: {
1014 RGBA32 rgbColor = m_value.rgbcolor;
1015 if (m_primitiveUnitType == CSS_PARSER_HEXCOLOR)
1016 Color::parseHexColor(m_value.string, rgbColor);
1017 Color color(rgbColor);
1019 Vector<LChar> result;
1020 result.reserveInitialCapacity(32);
1021 bool colorHasAlpha = color.hasAlpha();
1023 result.append("rgba(", 5);
1025 result.append("rgb(", 4);
1027 appendNumber(result, static_cast<unsigned char>(color.red()));
1028 result.append(", ", 2);
1030 appendNumber(result, static_cast<unsigned char>(color.green()));
1031 result.append(", ", 2);
1033 appendNumber(result, static_cast<unsigned char>(color.blue()));
1034 if (colorHasAlpha) {
1035 result.append(", ", 2);
1037 NumberToStringBuffer buffer;
1038 const char* alphaString = numberToFixedPrecisionString(color.alpha() / 255.0f, 6, buffer, true);
1039 result.append(alphaString, strlen(alphaString));
1043 text = String::adopt(result);
1047 text = getPairValue()->cssText();
1049 #if ENABLE(DASHBOARD_SUPPORT)
1050 case CSS_DASHBOARD_REGION: {
1051 StringBuilder result;
1052 for (DashboardRegion* region = getDashboardRegionValue(); region; region = region->m_next.get()) {
1053 if (!result.isEmpty())
1055 result.appendLiteral("dashboard-region(");
1056 result.append(region->m_label);
1057 if (region->m_isCircle)
1058 result.appendLiteral(" circle");
1059 else if (region->m_isRectangle)
1060 result.appendLiteral(" rectangle");
1063 if (region->top()->m_primitiveUnitType == CSS_IDENT && region->top()->getIdent() == CSSValueInvalid) {
1064 ASSERT(region->right()->m_primitiveUnitType == CSS_IDENT);
1065 ASSERT(region->bottom()->m_primitiveUnitType == CSS_IDENT);
1066 ASSERT(region->left()->m_primitiveUnitType == CSS_IDENT);
1067 ASSERT(region->right()->getIdent() == CSSValueInvalid);
1068 ASSERT(region->bottom()->getIdent() == CSSValueInvalid);
1069 ASSERT(region->left()->getIdent() == CSSValueInvalid);
1072 result.append(region->top()->cssText());
1074 result.append(region->right()->cssText());
1076 result.append(region->bottom()->cssText());
1078 result.append(region->left()->cssText());
1082 text = result.toString();
1086 case CSS_PARSER_OPERATOR: {
1087 char c = static_cast<char>(m_value.ident);
1088 text = String(&c, 1U);
1091 case CSS_PARSER_IDENTIFIER:
1092 text = quoteCSSStringIfNeeded(m_value.string);
1095 text = m_value.calc->cssText();
1098 text = m_value.shape->cssText();
1101 text = formatNumber(m_value.num, "vw");
1104 text = formatNumber(m_value.num, "vh");
1107 text = formatNumber(m_value.num, "vmin");
1110 text = formatNumber(m_value.num, "vmax");
1112 #if ENABLE(CSS_VARIABLES)
1113 case CSS_VARIABLE_NAME:
1114 text = "-webkit-var(" + String(m_value.string) + ")";
1119 ASSERT(!cssTextCache().contains(this));
1120 cssTextCache().set(this, text);
1121 m_hasCachedCSSText = true;
1125 #if ENABLE(CSS_VARIABLES)
1126 String CSSPrimitiveValue::customSerializeResolvingVariables(const HashMap<AtomicString, String>& variables) const
1128 if (isVariableName() && variables.contains(m_value.string))
1129 return variables.get(m_value.string);
1130 if (CSSCalcValue* calcValue = cssCalcValue())
1131 return calcValue->customSerializeResolvingVariables(variables);
1132 if (Pair* pairValue = getPairValue())
1133 return pairValue->serializeResolvingVariables(variables);
1134 if (Rect* rectVal = getRectValue())
1135 return rectVal->serializeResolvingVariables(variables);
1136 if (Quad* quadVal = getQuadValue())
1137 return quadVal->serializeResolvingVariables(variables);
1138 if (CSSBasicShape* shapeValue = getShapeValue())
1139 return shapeValue->serializeResolvingVariables(variables);
1140 return customCssText();
1143 bool CSSPrimitiveValue::hasVariableReference() const
1145 if (CSSCalcValue* calcValue = cssCalcValue())
1146 return calcValue->hasVariableReference();
1147 if (Pair* pairValue = getPairValue())
1148 return pairValue->hasVariableReference();
1149 if (Quad* quadValue = getQuadValue())
1150 return quadValue->hasVariableReference();
1151 if (Rect* rectValue = getRectValue())
1152 return rectValue->hasVariableReference();
1153 if (CSSBasicShape* shapeValue = getShapeValue())
1154 return shapeValue->hasVariableReference();
1155 return isVariableName();
1159 void CSSPrimitiveValue::addSubresourceStyleURLs(ListHashSet<KURL>& urls, const StyleSheetContents* styleSheet) const
1161 if (m_primitiveUnitType == CSS_URI)
1162 addSubresourceURL(urls, styleSheet->completeURL(m_value.string));
1165 Length CSSPrimitiveValue::viewportPercentageLength()
1167 ASSERT(isViewportPercentageLength());
1168 Length viewportLength;
1169 switch (m_primitiveUnitType) {
1171 viewportLength = Length(getDoubleValue(), ViewportPercentageWidth);
1174 viewportLength = Length(getDoubleValue(), ViewportPercentageHeight);
1177 viewportLength = Length(getDoubleValue(), ViewportPercentageMin);
1180 viewportLength = Length(getDoubleValue(), ViewportPercentageMax);
1185 return viewportLength;
1188 PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::cloneForCSSOM() const
1190 RefPtr<CSSPrimitiveValue> result;
1192 switch (m_primitiveUnitType) {
1196 case CSS_COUNTER_NAME:
1197 result = CSSPrimitiveValue::create(m_value.string, static_cast<UnitTypes>(m_primitiveUnitType));
1200 result = CSSPrimitiveValue::create(m_value.counter->cloneForCSSOM());
1203 result = CSSPrimitiveValue::create(m_value.rect->cloneForCSSOM());
1206 result = CSSPrimitiveValue::create(m_value.quad->cloneForCSSOM());
1209 // Pair is not exposed to the CSSOM, no need for a deep clone.
1210 result = CSSPrimitiveValue::create(m_value.pair);
1212 #if ENABLE(DASHBOARD_SUPPORT)
1213 case CSS_DASHBOARD_REGION:
1214 // DashboardRegion is not exposed to the CSSOM, no need for a deep clone.
1215 result = CSSPrimitiveValue::create(m_value.region);
1219 // CSSCalcValue is not exposed to the CSSOM, no need for a deep clone.
1220 result = CSSPrimitiveValue::create(m_value.calc);
1223 // CSSShapeValue is not exposed to the CSSOM, no need for a deep clone.
1224 result = CSSPrimitiveValue::create(m_value.shape);
1227 case CSS_PARSER_INTEGER:
1228 case CSS_PERCENTAGE:
1250 #if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
1255 result = CSSPrimitiveValue::create(m_value.num, static_cast<UnitTypes>(m_primitiveUnitType));
1258 result = CSSPrimitiveValue::createIdentifier(m_value.ident);
1261 result = CSSPrimitiveValue::createColor(m_value.rgbcolor);
1265 case CSS_PARSER_OPERATOR:
1266 case CSS_PARSER_IDENTIFIER:
1267 case CSS_PARSER_HEXCOLOR:
1268 ASSERT_NOT_REACHED();
1272 result->setCSSOMSafe();
1277 bool CSSPrimitiveValue::equals(const CSSPrimitiveValue& other) const
1279 if (m_primitiveUnitType != other.m_primitiveUnitType)
1282 switch (m_primitiveUnitType) {
1286 case CSS_PARSER_INTEGER:
1287 case CSS_PERCENTAGE:
1293 #if ENABLE(CSS_IMAGE_RESOLUTION) || ENABLE(RESOLUTION_MEDIA_QUERY)
1314 return m_value.num == other.m_value.num;
1316 return valueOrPropertyName(m_value.ident) == valueOrPropertyName(other.m_value.ident);
1320 case CSS_COUNTER_NAME:
1321 case CSS_PARSER_IDENTIFIER:
1322 case CSS_PARSER_HEXCOLOR:
1323 #if ENABLE(CSS_VARIABLES)
1324 case CSS_VARIABLE_NAME:
1326 return equal(m_value.string, other.m_value.string);
1328 return m_value.counter && other.m_value.counter && m_value.counter->equals(*other.m_value.counter);
1330 return m_value.rect && other.m_value.rect && m_value.rect->equals(*other.m_value.rect);
1332 return m_value.quad && other.m_value.quad && m_value.quad->equals(*other.m_value.quad);
1334 return m_value.rgbcolor == other.m_value.rgbcolor;
1336 return m_value.pair && other.m_value.pair && m_value.pair->equals(*other.m_value.pair);
1337 #if ENABLE(DASHBOARD_SUPPORT)
1338 case CSS_DASHBOARD_REGION: {
1339 DashboardRegion* region = getDashboardRegionValue();
1340 DashboardRegion* otherRegion = other.getDashboardRegionValue();
1341 return region ? otherRegion && region->equals(*otherRegion) : !otherRegion;
1344 case CSS_PARSER_OPERATOR:
1345 return m_value.ident == other.m_value.ident;
1347 return m_value.calc && other.m_value.calc && m_value.calc->equals(*other.m_value.calc);
1349 return m_value.shape && other.m_value.shape && m_value.shape->equals(*other.m_value.shape);
1354 void CSSPrimitiveValue::reportDescendantMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
1356 MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::CSS);
1357 switch (m_primitiveUnitType) {
1359 case CSS_COUNTER_NAME:
1360 case CSS_PARSER_IDENTIFIER:
1361 case CSS_PARSER_HEXCOLOR:
1364 #if ENABLE(CSS_VARIABLES)
1365 case CSS_VARIABLE_NAME:
1367 // FIXME: detect other cases when m_value is StringImpl*
1368 info.addMember(m_value.string, "value.string");
1371 info.addMember(m_value.counter, "value.counter");
1374 info.addMember(m_value.rect, "value.rect");
1377 info.addMember(m_value.quad, "value.quad");
1380 info.addMember(m_value.pair, "value.pair");
1382 #if ENABLE(DASHBOARD_SUPPORT)
1383 case CSS_DASHBOARD_REGION:
1384 info.addMember(m_value.region, "value.region");
1388 info.addMember(m_value.shape, "value.shape");
1391 info.addMember(m_value.calc, "value.calc");
1398 } // namespace WebCore