2011-06-24 Abhishek Arya <inferno@chromium.org>
[WebKit-https.git] / Source / WebCore / css / CSSPrimitiveValue.cpp
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008 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 "CSSPrimitiveValue.h"
23
24 #include "CSSHelper.h"
25 #include "CSSParser.h"
26 #include "CSSPropertyNames.h"
27 #include "CSSStyleSheet.h"
28 #include "CSSValueKeywords.h"
29 #include "Color.h"
30 #include "Counter.h"
31 #include "ExceptionCode.h"
32 #include "Node.h"
33 #include "Pair.h"
34 #include "RGBColor.h"
35 #include "Rect.h"
36 #include "RenderStyle.h"
37 #include <wtf/ASCIICType.h>
38 #include <wtf/DecimalNumber.h>
39 #include <wtf/StdLibExtras.h>
40 #include <wtf/text/StringBuffer.h>
41
42 #if ENABLE(DASHBOARD_SUPPORT)
43 #include "DashboardRegion.h"
44 #endif
45
46 using namespace WTF;
47
48 namespace WebCore {
49
50 static CSSPrimitiveValue::UnitCategory unitCategory(CSSPrimitiveValue::UnitTypes type)
51 {
52     // Here we violate the spec (http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue) and allow conversions
53     // between CSS_PX and relative lengths (see cssPixelsPerInch comment in CSSHelper.h for the topic treatment).
54     switch (type) {
55     case CSSPrimitiveValue::CSS_NUMBER:
56         return CSSPrimitiveValue::UNumber;
57     case CSSPrimitiveValue::CSS_PERCENTAGE:
58         return CSSPrimitiveValue::UPercent;
59     case CSSPrimitiveValue::CSS_PX:
60     case CSSPrimitiveValue::CSS_CM:
61     case CSSPrimitiveValue::CSS_MM:
62     case CSSPrimitiveValue::CSS_IN:
63     case CSSPrimitiveValue::CSS_PT:
64     case CSSPrimitiveValue::CSS_PC:
65         return CSSPrimitiveValue::ULength;
66     case CSSPrimitiveValue::CSS_MS:
67     case CSSPrimitiveValue::CSS_S:
68         return CSSPrimitiveValue::UTime;
69     case CSSPrimitiveValue::CSS_DEG:
70     case CSSPrimitiveValue::CSS_RAD:
71     case CSSPrimitiveValue::CSS_GRAD:
72     case CSSPrimitiveValue::CSS_TURN:
73         return CSSPrimitiveValue::UAngle;
74     case CSSPrimitiveValue::CSS_HZ:
75     case CSSPrimitiveValue::CSS_KHZ:
76         return CSSPrimitiveValue::UFrequency;
77     default:
78         return CSSPrimitiveValue::UOther;
79     }
80 }
81
82 typedef HashMap<const CSSPrimitiveValue*, String> CSSTextCache;
83 static CSSTextCache& cssTextCache()
84 {
85     DEFINE_STATIC_LOCAL(CSSTextCache, cache, ());
86     return cache;
87 }
88
89 static const AtomicString& valueOrPropertyName(int valueOrPropertyID)
90 {
91     ASSERT_ARG(valueOrPropertyID, valueOrPropertyID >= 0);
92     ASSERT_ARG(valueOrPropertyID, valueOrPropertyID < numCSSValueKeywords || (valueOrPropertyID >= firstCSSProperty && valueOrPropertyID < firstCSSProperty + numCSSProperties));
93
94     if (valueOrPropertyID < 0)
95         return nullAtom;
96
97     if (valueOrPropertyID < numCSSValueKeywords) {
98         static AtomicString* cssValueKeywordStrings[numCSSValueKeywords];
99         if (!cssValueKeywordStrings[valueOrPropertyID])
100             cssValueKeywordStrings[valueOrPropertyID] = new AtomicString(getValueName(valueOrPropertyID));
101         return *cssValueKeywordStrings[valueOrPropertyID];
102     }
103
104     if (valueOrPropertyID >= firstCSSProperty && valueOrPropertyID < firstCSSProperty + numCSSProperties) {
105         static AtomicString* cssPropertyStrings[numCSSProperties];
106         int propertyIndex = valueOrPropertyID - firstCSSProperty;
107         if (!cssPropertyStrings[propertyIndex])
108             cssPropertyStrings[propertyIndex] = new AtomicString(getPropertyName(static_cast<CSSPropertyID>(valueOrPropertyID)));
109         return *cssPropertyStrings[propertyIndex];
110     }
111
112     return nullAtom;
113 }
114
115 CSSPrimitiveValue::CSSPrimitiveValue()
116     : m_type(0)
117     , m_hasCachedCSSText(false)
118 {
119 }
120
121 CSSPrimitiveValue::CSSPrimitiveValue(int ident)
122     : m_type(CSS_IDENT)
123     , m_hasCachedCSSText(false)
124 {
125     m_value.ident = ident;
126 }
127
128 CSSPrimitiveValue::CSSPrimitiveValue(double num, UnitTypes type)
129     : m_type(type)
130     , m_hasCachedCSSText(false)
131 {
132     ASSERT(isfinite(num));
133     m_value.num = num;
134 }
135
136 CSSPrimitiveValue::CSSPrimitiveValue(const String& str, UnitTypes type)
137     : m_type(type)
138     , m_hasCachedCSSText(false)
139 {
140     if ((m_value.string = str.impl()))
141         m_value.string->ref();
142 }
143
144 CSSPrimitiveValue::CSSPrimitiveValue(RGBA32 color)
145     : m_type(CSS_RGBCOLOR)
146     , m_hasCachedCSSText(false)
147 {
148     m_value.rgbcolor = color;
149 }
150
151 CSSPrimitiveValue::CSSPrimitiveValue(const Length& length)
152     : m_hasCachedCSSText(false)
153 {
154     switch (length.type()) {
155         case Auto:
156             m_type = CSS_IDENT;
157             m_value.ident = CSSValueAuto;
158             break;
159         case WebCore::Fixed:
160             m_type = CSS_PX;
161             ASSERT(isfinite(length.value()));
162             m_value.num = length.value();
163             break;
164         case Intrinsic:
165             m_type = CSS_IDENT;
166             m_value.ident = CSSValueIntrinsic;
167             break;
168         case MinIntrinsic:
169             m_type = CSS_IDENT;
170             m_value.ident = CSSValueMinIntrinsic;
171             break;
172         case Percent:
173             m_type = CSS_PERCENTAGE;
174             ASSERT(isfinite(length.percent()));
175             m_value.num = length.percent();
176             break;
177         case Relative:
178             ASSERT_NOT_REACHED();
179             break;
180     }
181 }
182
183 void CSSPrimitiveValue::init(PassRefPtr<Counter> c)
184 {
185     m_type = CSS_COUNTER;
186     m_hasCachedCSSText = false;
187     m_value.counter = c.releaseRef();
188 }
189
190 void CSSPrimitiveValue::init(PassRefPtr<Rect> r)
191 {
192     m_type = CSS_RECT;
193     m_hasCachedCSSText = false;
194     m_value.rect = r.releaseRef();
195 }
196
197 #if ENABLE(DASHBOARD_SUPPORT)
198 void CSSPrimitiveValue::init(PassRefPtr<DashboardRegion> r)
199 {
200     m_type = CSS_DASHBOARD_REGION;
201     m_hasCachedCSSText = false;
202     m_value.region = r.releaseRef();
203 }
204 #endif
205
206 void CSSPrimitiveValue::init(PassRefPtr<Pair> p)
207 {
208     m_type = CSS_PAIR;
209     m_hasCachedCSSText = false;
210     m_value.pair = p.releaseRef();
211 }
212
213 CSSPrimitiveValue::~CSSPrimitiveValue()
214 {
215     cleanup();
216 }
217
218 void CSSPrimitiveValue::cleanup()
219 {
220     switch (m_type) {
221         case CSS_STRING:
222         case CSS_URI:
223         case CSS_ATTR:
224         case CSS_PARSER_HEXCOLOR:
225             if (m_value.string)
226                 m_value.string->deref();
227             break;
228         case CSS_COUNTER:
229             m_value.counter->deref();
230             break;
231         case CSS_RECT:
232             m_value.rect->deref();
233             break;
234         case CSS_PAIR:
235             m_value.pair->deref();
236             break;
237 #if ENABLE(DASHBOARD_SUPPORT)
238         case CSS_DASHBOARD_REGION:
239             if (m_value.region)
240                 m_value.region->deref();
241             break;
242 #endif
243         default:
244             break;
245     }
246
247     m_type = 0;
248     if (m_hasCachedCSSText) {
249         cssTextCache().remove(this);
250         m_hasCachedCSSText = false;
251     }
252 }
253
254 template<> int CSSPrimitiveValue::computeLength(RenderStyle* style, RenderStyle* rootStyle, double multiplier, bool computingFontSize)
255 {
256     return roundForImpreciseConversion<int, INT_MAX, INT_MIN>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize));
257 }
258
259 // Lengths expect an int that is only 28-bits, so we have to check for a
260 // different overflow.
261 int CSSPrimitiveValue::computeLengthIntForLength(RenderStyle* style, RenderStyle* rootStyle, double multiplier, bool computingFontSize)
262 {
263     return roundForImpreciseConversion<int, intMaxForLength, intMinForLength>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize));
264 }
265
266 template<> Length CSSPrimitiveValue::computeLength(RenderStyle* style, RenderStyle* rootStyle, double multiplier, bool computingFontSize)
267 {
268     return Length(computeLengthIntForLength(style, rootStyle, multiplier, computingFontSize), Fixed);
269 }
270
271 template<> short CSSPrimitiveValue::computeLength(RenderStyle* style, RenderStyle* rootStyle, double multiplier, bool computingFontSize)
272 {
273     return roundForImpreciseConversion<short, SHRT_MAX, SHRT_MIN>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize));
274 }
275
276 template<> float CSSPrimitiveValue::computeLength(RenderStyle* style, RenderStyle* rootStyle, double multiplier, bool computingFontSize)
277 {
278     return static_cast<float>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize));
279 }
280
281 template<> double CSSPrimitiveValue::computeLength(RenderStyle* style, RenderStyle* rootStyle, double multiplier, bool computingFontSize)
282 {
283     return computeLengthDouble(style, rootStyle, multiplier, computingFontSize);
284 }
285
286 double CSSPrimitiveValue::computeLengthDouble(RenderStyle* style, RenderStyle* rootStyle, double multiplier, bool computingFontSize)
287 {
288     unsigned short type = primitiveType();
289
290     // We do not apply the zoom factor when we are computing the value of the font-size property.  The zooming
291     // for font sizes is much more complicated, since we have to worry about enforcing the minimum font size preference
292     // as well as enforcing the implicit "smart minimum."  In addition the CSS property text-size-adjust is used to
293     // prevent text from zooming at all.  Therefore we will not apply the zoom here if we are computing font-size.
294     bool applyZoomMultiplier = !computingFontSize;
295
296     double factor = 1.0;
297     switch (type) {
298         case CSS_EMS:
299             applyZoomMultiplier = false;
300             factor = computingFontSize ? style->fontDescription().specifiedSize() : style->fontDescription().computedSize();
301             break;
302         case CSS_EXS:
303             // FIXME: We have a bug right now where the zoom will be applied twice to EX units.
304             // We really need to compute EX using fontMetrics for the original specifiedSize and not use
305             // our actual constructed rendering font.
306             applyZoomMultiplier = false;
307             factor = style->fontMetrics().xHeight();
308             break;
309         case CSS_REMS:
310             applyZoomMultiplier = false;
311             factor = computingFontSize ? rootStyle->fontDescription().specifiedSize() : rootStyle->fontDescription().computedSize();
312             break;
313         case CSS_PX:
314             break;
315         case CSS_CM:
316             factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
317             break;
318         case CSS_MM:
319             factor = cssPixelsPerInch / 25.4;
320             break;
321         case CSS_IN:
322             factor = cssPixelsPerInch;
323             break;
324         case CSS_PT:
325             factor = cssPixelsPerInch / 72.0;
326             break;
327         case CSS_PC:
328             // 1 pc == 12 pt
329             factor = cssPixelsPerInch * 12.0 / 72.0;
330             break;
331         default:
332             return -1.0;
333     }
334
335     double result = getDoubleValue() * factor;
336     if (!applyZoomMultiplier || multiplier == 1.0)
337         return result;
338      
339     // Any original result that was >= 1 should not be allowed to fall below 1.  This keeps border lines from
340     // vanishing.
341     double zoomedResult = result * multiplier;
342     if (result >= 1.0)
343         zoomedResult = max(1.0, zoomedResult);
344     return zoomedResult;
345 }
346
347 void CSSPrimitiveValue::setFloatValue(unsigned short, double, ExceptionCode& ec)
348 {
349     // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects. 
350     // No other engine supports mutating style through this API. Computed style is always read-only anyway.
351     // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation.
352     ec = NO_MODIFICATION_ALLOWED_ERR;
353 }
354
355 static double conversionToCanonicalUnitsScaleFactor(unsigned short unitType)
356 {
357     double factor = 1.0;
358     // FIXME: the switch can be replaced by an array of scale factors.
359     switch (unitType) {
360         // These are "canonical" units in their respective categories.
361         case CSSPrimitiveValue::CSS_PX:
362         case CSSPrimitiveValue::CSS_DEG:
363         case CSSPrimitiveValue::CSS_MS:
364         case CSSPrimitiveValue::CSS_HZ:
365             break;
366         case CSSPrimitiveValue::CSS_CM:
367             factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
368             break;
369         case CSSPrimitiveValue::CSS_MM:
370             factor = cssPixelsPerInch / 25.4;
371             break;
372         case CSSPrimitiveValue::CSS_IN:
373             factor = cssPixelsPerInch;
374             break;
375         case CSSPrimitiveValue::CSS_PT:
376             factor = cssPixelsPerInch / 72.0;
377             break;
378         case CSSPrimitiveValue::CSS_PC:
379             factor = cssPixelsPerInch * 12.0 / 72.0; // 1 pc == 12 pt
380             break;
381         case CSSPrimitiveValue::CSS_RAD:
382             factor = 180 / piDouble;
383             break;
384         case CSSPrimitiveValue::CSS_GRAD:
385             factor = 0.9;
386             break;
387         case CSSPrimitiveValue::CSS_TURN:
388             factor = 360;
389             break;
390         case CSSPrimitiveValue::CSS_S:
391         case CSSPrimitiveValue::CSS_KHZ:
392             factor = 1000;
393             break;
394         default:
395             break;
396     }
397
398     return factor;
399 }
400
401 double CSSPrimitiveValue::getDoubleValue(unsigned short unitType, ExceptionCode& ec) const
402 {
403     double result = 0;
404     bool success = getDoubleValueInternal(static_cast<UnitTypes>(unitType), &result);
405     if (!success) {
406         ec = INVALID_ACCESS_ERR;
407         return 0.0;
408     }
409
410     ec = 0;
411     return result;
412 }
413
414 double CSSPrimitiveValue::getDoubleValue(unsigned short unitType) const
415 {
416     double result = 0;
417     getDoubleValueInternal(static_cast<UnitTypes>(unitType), &result);
418     return result;
419 }
420
421 CSSPrimitiveValue::UnitTypes CSSPrimitiveValue::canonicalUnitTypeForCategory(UnitCategory category)
422 {
423     // The canonical unit type is chosen according to the way CSSParser::validUnit() chooses the default unit
424     // in each category (based on unitflags).
425     switch (category) {
426     case UNumber:
427         return CSS_NUMBER;
428     case ULength:
429         return CSS_PX;
430     case UPercent:
431         return CSS_UNKNOWN; // Cannot convert between numbers and percent.
432     case UTime:
433         return CSS_MS;
434     case UAngle:
435         return CSS_DEG;
436     case UFrequency:
437         return CSS_HZ;
438     default:
439         return CSS_UNKNOWN;
440     }
441 }
442
443 bool CSSPrimitiveValue::getDoubleValueInternal(UnitTypes requestedUnitType, double* result) const
444 {
445     if (m_type < CSS_NUMBER || (m_type > CSS_DIMENSION && m_type < CSS_TURN) || requestedUnitType < CSS_NUMBER || (requestedUnitType > CSS_DIMENSION && requestedUnitType < CSS_TURN))
446         return false;
447     if (requestedUnitType == m_type || requestedUnitType == CSS_DIMENSION) {
448         *result = m_value.num;
449         return true;
450     }
451
452     UnitTypes sourceUnitType = static_cast<UnitTypes>(m_type);
453     UnitCategory sourceCategory = unitCategory(sourceUnitType);
454     ASSERT(sourceCategory != UOther);
455
456     UnitTypes targetUnitType = requestedUnitType;
457     UnitCategory targetCategory = unitCategory(targetUnitType);
458     ASSERT(targetCategory != UOther);
459
460     // Cannot convert between unrelated unit categories if one of them is not UNumber.
461     if (sourceCategory != targetCategory && sourceCategory != UNumber && targetCategory != UNumber)
462         return false;
463
464     if (targetCategory == UNumber) {
465         // We interpret conversion to CSS_NUMBER as conversion to a canonical unit in this value's category.
466         targetUnitType = canonicalUnitTypeForCategory(sourceCategory);
467         if (targetUnitType == CSS_UNKNOWN)
468             return false;
469     }
470
471     if (sourceUnitType == CSS_NUMBER) {
472         // We interpret conversion from CSS_NUMBER in the same way as CSSParser::validUnit() while using non-strict mode.
473         sourceUnitType = canonicalUnitTypeForCategory(targetCategory);
474         if (sourceUnitType == CSS_UNKNOWN)
475             return false;
476     }
477
478     double convertedValue = m_value.num;
479
480     // First convert the value from m_type to canonical type.
481     double factor = conversionToCanonicalUnitsScaleFactor(sourceUnitType);
482     convertedValue *= factor;
483
484     // Now convert from canonical type to the target unitType.
485     factor = conversionToCanonicalUnitsScaleFactor(targetUnitType);
486     convertedValue /= factor;
487
488     *result = convertedValue;
489     return true;
490 }
491
492 void CSSPrimitiveValue::setStringValue(unsigned short, const String&, ExceptionCode& ec)
493 {
494     // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects. 
495     // No other engine supports mutating style through this API. Computed style is always read-only anyway.
496     // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation.
497     ec = NO_MODIFICATION_ALLOWED_ERR;
498 }
499
500 String CSSPrimitiveValue::getStringValue(ExceptionCode& ec) const
501 {
502     ec = 0;
503     switch (m_type) {
504         case CSS_STRING:
505         case CSS_ATTR:
506         case CSS_URI:
507             return m_value.string;
508         case CSS_IDENT:
509             return valueOrPropertyName(m_value.ident);
510         default:
511             ec = INVALID_ACCESS_ERR;
512             break;
513     }
514
515     return String();
516 }
517
518 String CSSPrimitiveValue::getStringValue() const
519 {
520     switch (m_type) {
521         case CSS_STRING:
522         case CSS_ATTR:
523         case CSS_URI:
524              return m_value.string;
525         case CSS_IDENT:
526             return valueOrPropertyName(m_value.ident);
527         default:
528             break;
529     }
530
531     return String();
532 }
533
534 Counter* CSSPrimitiveValue::getCounterValue(ExceptionCode& ec) const
535 {
536     ec = 0;
537     if (m_type != CSS_COUNTER) {
538         ec = INVALID_ACCESS_ERR;
539         return 0;
540     }
541
542     return m_value.counter;
543 }
544
545 Rect* CSSPrimitiveValue::getRectValue(ExceptionCode& ec) const
546 {
547     ec = 0;
548     if (m_type != CSS_RECT) {
549         ec = INVALID_ACCESS_ERR;
550         return 0;
551     }
552
553     return m_value.rect;
554 }
555
556 PassRefPtr<RGBColor> CSSPrimitiveValue::getRGBColorValue(ExceptionCode& ec) const
557 {
558     ec = 0;
559     if (m_type != CSS_RGBCOLOR) {
560         ec = INVALID_ACCESS_ERR;
561         return 0;
562     }
563
564     // FIMXE: This should not return a new object for each invocation.
565     return RGBColor::create(m_value.rgbcolor);
566 }
567
568 Pair* CSSPrimitiveValue::getPairValue(ExceptionCode& ec) const
569 {
570     ec = 0;
571     if (m_type != CSS_PAIR) {
572         ec = INVALID_ACCESS_ERR;
573         return 0;
574     }
575
576     return m_value.pair;
577 }
578
579 unsigned short CSSPrimitiveValue::cssValueType() const
580 {
581     return CSS_PRIMITIVE_VALUE;
582 }
583
584 bool CSSPrimitiveValue::parseString(const String& /*string*/, bool /*strict*/)
585 {
586     // FIXME
587     return false;
588 }
589
590 int CSSPrimitiveValue::getIdent() const
591 {
592     if (m_type != CSS_IDENT)
593         return 0;
594     return m_value.ident;
595 }
596
597 static String formatNumber(double number)
598 {
599     DecimalNumber decimal(number);
600     
601     StringBuffer buffer(decimal.bufferLengthForStringDecimal());
602     unsigned length = decimal.toStringDecimal(buffer.characters(), buffer.length());
603     ASSERT_UNUSED(length, length == buffer.length());
604
605     return String::adopt(buffer);
606 }
607
608 String CSSPrimitiveValue::cssText() const
609 {
610     // FIXME: return the original value instead of a generated one (e.g. color
611     // name if it was specified) - check what spec says about this
612
613     if (m_hasCachedCSSText) {
614         ASSERT(cssTextCache().contains(this));
615         return cssTextCache().get(this);
616     }
617
618     String text;
619     switch (m_type) {
620         case CSS_UNKNOWN:
621             // FIXME
622             break;
623         case CSS_NUMBER:
624         case CSS_PARSER_INTEGER:
625             text = formatNumber(m_value.num);
626             break;
627         case CSS_PERCENTAGE:
628             text = formatNumber(m_value.num) + "%";
629             break;
630         case CSS_EMS:
631             text = formatNumber(m_value.num) + "em";
632             break;
633         case CSS_EXS:
634             text = formatNumber(m_value.num) + "ex";
635             break;
636         case CSS_REMS:
637             text = formatNumber(m_value.num) + "rem";
638             break;
639         case CSS_PX:
640             text = formatNumber(m_value.num) + "px";
641             break;
642         case CSS_CM:
643             text = formatNumber(m_value.num) + "cm";
644             break;
645         case CSS_MM:
646             text = formatNumber(m_value.num) + "mm";
647             break;
648         case CSS_IN:
649             text = formatNumber(m_value.num) + "in";
650             break;
651         case CSS_PT:
652             text = formatNumber(m_value.num) + "pt";
653             break;
654         case CSS_PC:
655             text = formatNumber(m_value.num) + "pc";
656             break;
657         case CSS_DEG:
658             text = formatNumber(m_value.num) + "deg";
659             break;
660         case CSS_RAD:
661             text = formatNumber(m_value.num) + "rad";
662             break;
663         case CSS_GRAD:
664             text = formatNumber(m_value.num) + "grad";
665             break;
666         case CSS_MS:
667             text = formatNumber(m_value.num) + "ms";
668             break;
669         case CSS_S:
670             text = formatNumber(m_value.num) + "s";
671             break;
672         case CSS_HZ:
673             text = formatNumber(m_value.num) + "hz";
674             break;
675         case CSS_KHZ:
676             text = formatNumber(m_value.num) + "khz";
677             break;
678         case CSS_TURN:
679             text = formatNumber(m_value.num) + "turn";
680             break;
681         case CSS_DIMENSION:
682             // FIXME
683             break;
684         case CSS_STRING:
685             text = quoteCSSStringIfNeeded(m_value.string);
686             break;
687         case CSS_URI:
688             text = "url(" + quoteCSSURLIfNeeded(m_value.string) + ")";
689             break;
690         case CSS_IDENT:
691             text = valueOrPropertyName(m_value.ident);
692             break;
693         case CSS_ATTR: {
694             DEFINE_STATIC_LOCAL(const String, attrParen, ("attr("));
695
696             Vector<UChar> result;
697             result.reserveInitialCapacity(6 + m_value.string->length());
698
699             append(result, attrParen);
700             append(result, m_value.string);
701             result.uncheckedAppend(')');
702
703             text = String::adopt(result);
704             break;
705         }
706         case CSS_COUNTER_NAME:
707             text = "counter(";
708             text += m_value.string;
709             text += ")";
710             break;
711         case CSS_COUNTER:
712             text = "counter(";
713             text += String::number(m_value.num);
714             text += ")";
715             // FIXME: Add list-style and separator
716             break;
717         case CSS_RECT: {
718             DEFINE_STATIC_LOCAL(const String, rectParen, ("rect("));
719
720             Rect* rectVal = getRectValue();
721             Vector<UChar> result;
722             result.reserveInitialCapacity(32);
723             append(result, rectParen);
724
725             append(result, rectVal->top()->cssText());
726             result.append(' ');
727
728             append(result, rectVal->right()->cssText());
729             result.append(' ');
730
731             append(result, rectVal->bottom()->cssText());
732             result.append(' ');
733
734             append(result, rectVal->left()->cssText());
735             result.append(')');
736
737             text = String::adopt(result);
738             break;
739         }
740         case CSS_RGBCOLOR:
741         case CSS_PARSER_HEXCOLOR: {
742             DEFINE_STATIC_LOCAL(const String, commaSpace, (", "));
743             DEFINE_STATIC_LOCAL(const String, rgbParen, ("rgb("));
744             DEFINE_STATIC_LOCAL(const String, rgbaParen, ("rgba("));
745
746             RGBA32 rgbColor = m_value.rgbcolor;
747             if (m_type == CSS_PARSER_HEXCOLOR)
748                 Color::parseHexColor(m_value.string, rgbColor);
749             Color color(rgbColor);
750
751             Vector<UChar> result;
752             result.reserveInitialCapacity(32);
753             if (color.hasAlpha())
754                 append(result, rgbaParen);
755             else
756                 append(result, rgbParen);
757
758             appendNumber(result, static_cast<unsigned char>(color.red()));
759             append(result, commaSpace);
760
761             appendNumber(result, static_cast<unsigned char>(color.green()));
762             append(result, commaSpace);
763
764             appendNumber(result, static_cast<unsigned char>(color.blue()));
765             if (color.hasAlpha()) {
766                 append(result, commaSpace);
767                 append(result, String::number(color.alpha() / 256.0f));
768             }
769
770             result.append(')');
771             text = String::adopt(result);
772             break;
773         }
774         case CSS_PAIR:
775             text = m_value.pair->first()->cssText();
776             text += " ";
777             text += m_value.pair->second()->cssText();
778             break;
779 #if ENABLE(DASHBOARD_SUPPORT)
780         case CSS_DASHBOARD_REGION:
781             for (DashboardRegion* region = getDashboardRegionValue(); region; region = region->m_next.get()) {
782                 if (!text.isEmpty())
783                     text.append(' ');
784                 text += "dashboard-region(";
785                 text += region->m_label;
786                 if (region->m_isCircle)
787                     text += " circle";
788                 else if (region->m_isRectangle)
789                     text += " rectangle";
790                 else
791                     break;
792                 if (region->top()->m_type == CSS_IDENT && region->top()->getIdent() == CSSValueInvalid) {
793                     ASSERT(region->right()->m_type == CSS_IDENT);
794                     ASSERT(region->bottom()->m_type == CSS_IDENT);
795                     ASSERT(region->left()->m_type == CSS_IDENT);
796                     ASSERT(region->right()->getIdent() == CSSValueInvalid);
797                     ASSERT(region->bottom()->getIdent() == CSSValueInvalid);
798                     ASSERT(region->left()->getIdent() == CSSValueInvalid);
799                 } else {
800                     text.append(' ');
801                     text += region->top()->cssText() + " ";
802                     text += region->right()->cssText() + " ";
803                     text += region->bottom()->cssText() + " ";
804                     text += region->left()->cssText();
805                 }
806                 text += ")";
807             }
808             break;
809 #endif
810         case CSS_PARSER_OPERATOR: {
811             char c = static_cast<char>(m_value.ident);
812             text = String(&c, 1U);
813             break;
814         }
815         case CSS_PARSER_IDENTIFIER:
816             text = quoteCSSStringIfNeeded(m_value.string);
817             break;
818     }
819
820     ASSERT(!cssTextCache().contains(this));
821     cssTextCache().set(this, text);
822     m_hasCachedCSSText = true;
823     return text;
824 }
825
826 void CSSPrimitiveValue::addSubresourceStyleURLs(ListHashSet<KURL>& urls, const CSSStyleSheet* styleSheet)
827 {
828     if (m_type == CSS_URI)
829         addSubresourceURL(urls, styleSheet->completeURL(m_value.string));
830 }
831
832 } // namespace WebCore