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