OSDN Git Service

Merge WebKit at r78450: Initial merge by git.
[android-x86/external-webkit.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 // A more stylish solution than sharing would be to turn CSSPrimitiveValue (or CSSValues in general) into non-virtual,
91 // non-refcounted simple type with value semantics. In practice these sharing tricks get similar memory benefits
92 // with less need for refactoring.
93
94 inline PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::createUncachedIdentifier(int identifier)
95 {
96     return adoptRef(new CSSPrimitiveValue(identifier));
97 }
98
99 inline PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::createUncachedColor(unsigned rgbValue)
100 {
101     return adoptRef(new CSSPrimitiveValue(rgbValue));
102 }
103
104 inline PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::createUncached(double value, UnitTypes type)
105 {
106     return adoptRef(new CSSPrimitiveValue(value, type));
107 }
108
109 PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::createIdentifier(int ident)
110 {
111     static RefPtr<CSSPrimitiveValue>* identValueCache = new RefPtr<CSSPrimitiveValue>[numCSSValueKeywords];
112     if (ident >= 0 && ident < numCSSValueKeywords) {
113         RefPtr<CSSPrimitiveValue> primitiveValue = identValueCache[ident];
114         if (!primitiveValue) {
115             primitiveValue = createUncachedIdentifier(ident);
116             identValueCache[ident] = primitiveValue;
117         }
118         return primitiveValue.release();
119     } 
120     return createUncachedIdentifier(ident);
121 }
122
123 PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::createColor(unsigned rgbValue)
124 {
125     typedef HashMap<unsigned, RefPtr<CSSPrimitiveValue> > ColorValueCache;
126     static ColorValueCache* colorValueCache = new ColorValueCache;
127     // These are the empty and deleted values of the hash table.
128     if (rgbValue == Color::transparent) {
129         static CSSPrimitiveValue* colorTransparent = createUncachedColor(Color::transparent).releaseRef();
130 #if CPU(ARM) && OS(LINUX)
131          // A workaround for gcc bug on ARM.
132         if (!colorTransparent)
133             return 0;
134 #endif
135         return colorTransparent;
136     }
137     if (rgbValue == Color::white) {
138         static CSSPrimitiveValue* colorWhite = createUncachedColor(Color::white).releaseRef();
139 #if CPU(ARM) && OS(LINUX)
140         // A workaround for gcc bug on ARM.
141         if (!colorWhite)
142             return 0;
143 #endif
144         return colorWhite;
145     }
146     RefPtr<CSSPrimitiveValue> primitiveValue = colorValueCache->get(rgbValue);
147     if (primitiveValue)
148         return primitiveValue.release();
149     primitiveValue = createUncachedColor(rgbValue);
150     // Just wipe out the cache and start rebuilding when it gets too big.
151     const int maxColorCacheSize = 512;
152     if (colorValueCache->size() >= maxColorCacheSize)
153         colorValueCache->clear();
154     colorValueCache->add(rgbValue, primitiveValue);
155     
156     return primitiveValue.release();
157 }
158
159 PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::create(double value, UnitTypes type)
160 {
161     // Small integers are very common. Try to share them.
162     const int cachedIntegerCount = 128;
163     // Other common primitive types have UnitTypes smaller than this.
164     const int maxCachedUnitType = CSS_PX;
165     typedef RefPtr<CSSPrimitiveValue>(* IntegerValueCache)[maxCachedUnitType + 1];
166     static IntegerValueCache integerValueCache = new RefPtr<CSSPrimitiveValue>[cachedIntegerCount][maxCachedUnitType + 1];
167     if (type <= maxCachedUnitType && value >= 0 && value < cachedIntegerCount) {
168         int intValue = static_cast<int>(value);
169         if (value == intValue) {
170             RefPtr<CSSPrimitiveValue> primitiveValue = integerValueCache[intValue][type];
171             if (!primitiveValue) {
172                 primitiveValue = createUncached(value, type);
173                 integerValueCache[intValue][type] = primitiveValue;
174             }
175             return primitiveValue.release();
176         }
177     }
178
179     return createUncached(value, type);
180 }
181
182 PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::create(const String& value, UnitTypes type)
183 {
184     return adoptRef(new CSSPrimitiveValue(value, type));
185 }
186
187 static const AtomicString& valueOrPropertyName(int valueOrPropertyID)
188 {
189     ASSERT_ARG(valueOrPropertyID, valueOrPropertyID >= 0);
190     ASSERT_ARG(valueOrPropertyID, valueOrPropertyID < numCSSValueKeywords || (valueOrPropertyID >= firstCSSProperty && valueOrPropertyID < firstCSSProperty + numCSSProperties));
191
192     if (valueOrPropertyID < 0)
193         return nullAtom;
194
195     if (valueOrPropertyID < numCSSValueKeywords) {
196         static AtomicString* cssValueKeywordStrings[numCSSValueKeywords];
197         if (!cssValueKeywordStrings[valueOrPropertyID])
198             cssValueKeywordStrings[valueOrPropertyID] = new AtomicString(getValueName(valueOrPropertyID));
199         return *cssValueKeywordStrings[valueOrPropertyID];
200     }
201
202     if (valueOrPropertyID >= firstCSSProperty && valueOrPropertyID < firstCSSProperty + numCSSProperties) {
203         static AtomicString* cssPropertyStrings[numCSSProperties];
204         int propertyIndex = valueOrPropertyID - firstCSSProperty;
205         if (!cssPropertyStrings[propertyIndex])
206             cssPropertyStrings[propertyIndex] = new AtomicString(getPropertyName(static_cast<CSSPropertyID>(valueOrPropertyID)));
207         return *cssPropertyStrings[propertyIndex];
208     }
209
210     return nullAtom;
211 }
212
213 CSSPrimitiveValue::CSSPrimitiveValue()
214     : m_type(0)
215     , m_hasCachedCSSText(false)
216 {
217 }
218
219 CSSPrimitiveValue::CSSPrimitiveValue(int ident)
220     : m_type(CSS_IDENT)
221     , m_hasCachedCSSText(false)
222 {
223     m_value.ident = ident;
224 }
225
226 CSSPrimitiveValue::CSSPrimitiveValue(double num, UnitTypes type)
227     : m_type(type)
228     , m_hasCachedCSSText(false)
229 {
230     m_value.num = num;
231 }
232
233 CSSPrimitiveValue::CSSPrimitiveValue(const String& str, UnitTypes type)
234     : m_type(type)
235     , m_hasCachedCSSText(false)
236 {
237     if ((m_value.string = str.impl()))
238         m_value.string->ref();
239 }
240
241 CSSPrimitiveValue::CSSPrimitiveValue(RGBA32 color)
242     : m_type(CSS_RGBCOLOR)
243     , m_hasCachedCSSText(false)
244 {
245     m_value.rgbcolor = color;
246 }
247
248 CSSPrimitiveValue::CSSPrimitiveValue(const Length& length)
249     : m_hasCachedCSSText(false)
250 {
251     switch (length.type()) {
252         case Auto:
253             m_type = CSS_IDENT;
254             m_value.ident = CSSValueAuto;
255             break;
256         case WebCore::Fixed:
257             m_type = CSS_PX;
258             m_value.num = length.value();
259             break;
260         case Intrinsic:
261             m_type = CSS_IDENT;
262             m_value.ident = CSSValueIntrinsic;
263             break;
264         case MinIntrinsic:
265             m_type = CSS_IDENT;
266             m_value.ident = CSSValueMinIntrinsic;
267             break;
268         case Percent:
269             m_type = CSS_PERCENTAGE;
270             m_value.num = length.percent();
271             break;
272         case Relative:
273         case Static:
274             ASSERT_NOT_REACHED();
275             break;
276     }
277 }
278
279 void CSSPrimitiveValue::init(PassRefPtr<Counter> c)
280 {
281     m_type = CSS_COUNTER;
282     m_hasCachedCSSText = false;
283     m_value.counter = c.releaseRef();
284 }
285
286 void CSSPrimitiveValue::init(PassRefPtr<Rect> r)
287 {
288     m_type = CSS_RECT;
289     m_hasCachedCSSText = false;
290     m_value.rect = r.releaseRef();
291 }
292
293 #if ENABLE(DASHBOARD_SUPPORT)
294 void CSSPrimitiveValue::init(PassRefPtr<DashboardRegion> r)
295 {
296     m_type = CSS_DASHBOARD_REGION;
297     m_hasCachedCSSText = false;
298     m_value.region = r.releaseRef();
299 }
300 #endif
301
302 void CSSPrimitiveValue::init(PassRefPtr<Pair> p)
303 {
304     m_type = CSS_PAIR;
305     m_hasCachedCSSText = false;
306     m_value.pair = p.releaseRef();
307 }
308
309 CSSPrimitiveValue::~CSSPrimitiveValue()
310 {
311     cleanup();
312 }
313
314 void CSSPrimitiveValue::cleanup()
315 {
316     switch (m_type) {
317         case CSS_STRING:
318         case CSS_URI:
319         case CSS_ATTR:
320         case CSS_PARSER_HEXCOLOR:
321             if (m_value.string)
322                 m_value.string->deref();
323             break;
324         case CSS_COUNTER:
325             m_value.counter->deref();
326             break;
327         case CSS_RECT:
328             m_value.rect->deref();
329             break;
330         case CSS_PAIR:
331             m_value.pair->deref();
332             break;
333 #if ENABLE(DASHBOARD_SUPPORT)
334         case CSS_DASHBOARD_REGION:
335             if (m_value.region)
336                 m_value.region->deref();
337             break;
338 #endif
339         default:
340             break;
341     }
342
343     m_type = 0;
344     if (m_hasCachedCSSText) {
345         cssTextCache().remove(this);
346         m_hasCachedCSSText = false;
347     }
348 }
349
350 int CSSPrimitiveValue::computeLengthInt(RenderStyle* style, RenderStyle* rootStyle)
351 {
352     return roundForImpreciseConversion<int, INT_MAX, INT_MIN>(computeLengthDouble(style, rootStyle));
353 }
354
355 int CSSPrimitiveValue::computeLengthInt(RenderStyle* style, RenderStyle* rootStyle, double multiplier)
356 {
357     return roundForImpreciseConversion<int, INT_MAX, INT_MIN>(computeLengthDouble(style, rootStyle, multiplier));
358 }
359
360 // Lengths expect an int that is only 28-bits, so we have to check for a
361 // different overflow.
362 int CSSPrimitiveValue::computeLengthIntForLength(RenderStyle* style, RenderStyle* rootStyle)
363 {
364     return roundForImpreciseConversion<int, intMaxForLength, intMinForLength>(computeLengthDouble(style, rootStyle));
365 }
366
367 int CSSPrimitiveValue::computeLengthIntForLength(RenderStyle* style, RenderStyle* rootStyle, double multiplier)
368 {
369     return roundForImpreciseConversion<int, intMaxForLength, intMinForLength>(computeLengthDouble(style, rootStyle, multiplier));
370 }
371
372 short CSSPrimitiveValue::computeLengthShort(RenderStyle* style, RenderStyle* rootStyle)
373 {
374     return roundForImpreciseConversion<short, SHRT_MAX, SHRT_MIN>(computeLengthDouble(style, rootStyle));
375 }
376
377 short CSSPrimitiveValue::computeLengthShort(RenderStyle* style, RenderStyle* rootStyle, double multiplier)
378 {
379     return roundForImpreciseConversion<short, SHRT_MAX, SHRT_MIN>(computeLengthDouble(style, rootStyle, multiplier));
380 }
381
382 float CSSPrimitiveValue::computeLengthFloat(RenderStyle* style, RenderStyle* rootStyle, bool computingFontSize)
383 {
384     return static_cast<float>(computeLengthDouble(style, rootStyle, 1.0, computingFontSize));
385 }
386
387 float CSSPrimitiveValue::computeLengthFloat(RenderStyle* style, RenderStyle* rootStyle, double multiplier, bool computingFontSize)
388 {
389     return static_cast<float>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize));
390 }
391
392 double CSSPrimitiveValue::computeLengthDouble(RenderStyle* style, RenderStyle* rootStyle, double multiplier, bool computingFontSize)
393 {
394     unsigned short type = primitiveType();
395
396     // We do not apply the zoom factor when we are computing the value of the font-size property.  The zooming
397     // for font sizes is much more complicated, since we have to worry about enforcing the minimum font size preference
398     // as well as enforcing the implicit "smart minimum."  In addition the CSS property text-size-adjust is used to
399     // prevent text from zooming at all.  Therefore we will not apply the zoom here if we are computing font-size.
400     bool applyZoomMultiplier = !computingFontSize;
401
402     double factor = 1.0;
403     switch (type) {
404         case CSS_EMS:
405             applyZoomMultiplier = false;
406             factor = computingFontSize ? style->fontDescription().specifiedSize() : style->fontDescription().computedSize();
407             break;
408         case CSS_EXS:
409             // FIXME: We have a bug right now where the zoom will be applied twice to EX units.
410             // We really need to compute EX using fontMetrics for the original specifiedSize and not use
411             // our actual constructed rendering font.
412             applyZoomMultiplier = false;
413             factor = style->fontMetrics().xHeight();
414             break;
415         case CSS_REMS:
416             applyZoomMultiplier = false;
417             factor = computingFontSize ? rootStyle->fontDescription().specifiedSize() : rootStyle->fontDescription().computedSize();
418             break;
419         case CSS_PX:
420             break;
421         case CSS_CM:
422             factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
423             break;
424         case CSS_MM:
425             factor = cssPixelsPerInch / 25.4;
426             break;
427         case CSS_IN:
428             factor = cssPixelsPerInch;
429             break;
430         case CSS_PT:
431             factor = cssPixelsPerInch / 72.0;
432             break;
433         case CSS_PC:
434             // 1 pc == 12 pt
435             factor = cssPixelsPerInch * 12.0 / 72.0;
436             break;
437         default:
438             return -1.0;
439     }
440
441     double result = getDoubleValue() * factor;
442     if (!applyZoomMultiplier || multiplier == 1.0)
443         return result;
444      
445     // Any original result that was >= 1 should not be allowed to fall below 1.  This keeps border lines from
446     // vanishing.
447     double zoomedResult = result * multiplier;
448     if (result >= 1.0)
449         zoomedResult = max(1.0, zoomedResult);
450     return zoomedResult;
451 }
452
453 void CSSPrimitiveValue::setFloatValue(unsigned short, double, ExceptionCode& ec)
454 {
455     // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects. 
456     // No other engine supports mutating style through this API. Computed style is always read-only anyway.
457     // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation.
458     ec = NO_MODIFICATION_ALLOWED_ERR;
459 }
460
461 static double conversionToCanonicalUnitsScaleFactor(unsigned short unitType)
462 {
463     double factor = 1.0;
464     // FIXME: the switch can be replaced by an array of scale factors.
465     switch (unitType) {
466         // These are "canonical" units in their respective categories.
467         case CSSPrimitiveValue::CSS_PX:
468         case CSSPrimitiveValue::CSS_DEG:
469         case CSSPrimitiveValue::CSS_MS:
470         case CSSPrimitiveValue::CSS_HZ:
471             break;
472         case CSSPrimitiveValue::CSS_CM:
473             factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
474             break;
475         case CSSPrimitiveValue::CSS_MM:
476             factor = cssPixelsPerInch / 25.4;
477             break;
478         case CSSPrimitiveValue::CSS_IN:
479             factor = cssPixelsPerInch;
480             break;
481         case CSSPrimitiveValue::CSS_PT:
482             factor = cssPixelsPerInch / 72.0;
483             break;
484         case CSSPrimitiveValue::CSS_PC:
485             factor = cssPixelsPerInch * 12.0 / 72.0; // 1 pc == 12 pt
486             break;
487         case CSSPrimitiveValue::CSS_RAD:
488             factor = 180 / piDouble;
489             break;
490         case CSSPrimitiveValue::CSS_GRAD:
491             factor = 0.9;
492             break;
493         case CSSPrimitiveValue::CSS_TURN:
494             factor = 360;
495             break;
496         case CSSPrimitiveValue::CSS_S:
497         case CSSPrimitiveValue::CSS_KHZ:
498             factor = 1000;
499             break;
500         default:
501             break;
502     }
503
504     return factor;
505 }
506
507 double CSSPrimitiveValue::getDoubleValue(unsigned short unitType, ExceptionCode& ec) const
508 {
509     double result = 0;
510     bool success = getDoubleValueInternal(static_cast<UnitTypes>(unitType), &result);
511     if (!success) {
512         ec = INVALID_ACCESS_ERR;
513         return 0.0;
514     }
515
516     ec = 0;
517     return result;
518 }
519
520 double CSSPrimitiveValue::getDoubleValue(unsigned short unitType) const
521 {
522     double result = 0;
523     getDoubleValueInternal(static_cast<UnitTypes>(unitType), &result);
524     return result;
525 }
526
527 CSSPrimitiveValue::UnitTypes CSSPrimitiveValue::canonicalUnitTypeForCategory(UnitCategory category)
528 {
529     // The canonical unit type is chosen according to the way CSSParser::validUnit() chooses the default unit
530     // in each category (based on unitflags).
531     switch (category) {
532     case UNumber:
533         return CSS_NUMBER;
534     case ULength:
535         return CSS_PX;
536     case UPercent:
537         return CSS_UNKNOWN; // Cannot convert between numbers and percent.
538     case UTime:
539         return CSS_MS;
540     case UAngle:
541         return CSS_DEG;
542     case UFrequency:
543         return CSS_HZ;
544     default:
545         return CSS_UNKNOWN;
546     }
547 }
548
549 bool CSSPrimitiveValue::getDoubleValueInternal(UnitTypes requestedUnitType, double* result) const
550 {
551     if (m_type < CSS_NUMBER || (m_type > CSS_DIMENSION && m_type < CSS_TURN) || requestedUnitType < CSS_NUMBER || (requestedUnitType > CSS_DIMENSION && requestedUnitType < CSS_TURN))
552         return false;
553     if (requestedUnitType == m_type || requestedUnitType == CSS_DIMENSION) {
554         *result = m_value.num;
555         return true;
556     }
557
558     UnitTypes sourceUnitType = static_cast<UnitTypes>(m_type);
559     UnitCategory sourceCategory = unitCategory(sourceUnitType);
560     ASSERT(sourceCategory != UOther);
561
562     UnitTypes targetUnitType = requestedUnitType;
563     UnitCategory targetCategory = unitCategory(targetUnitType);
564     ASSERT(targetCategory != UOther);
565
566     // Cannot convert between unrelated unit categories if one of them is not UNumber.
567     if (sourceCategory != targetCategory && sourceCategory != UNumber && targetCategory != UNumber)
568         return false;
569
570     if (targetCategory == UNumber) {
571         // We interpret conversion to CSS_NUMBER as conversion to a canonical unit in this value's category.
572         targetUnitType = canonicalUnitTypeForCategory(sourceCategory);
573         if (targetUnitType == CSS_UNKNOWN)
574             return false;
575     }
576
577     if (sourceUnitType == CSS_NUMBER) {
578         // We interpret conversion from CSS_NUMBER in the same way as CSSParser::validUnit() while using non-strict mode.
579         sourceUnitType = canonicalUnitTypeForCategory(targetCategory);
580         if (sourceUnitType == CSS_UNKNOWN)
581             return false;
582     }
583
584     double convertedValue = m_value.num;
585
586     // First convert the value from m_type to canonical type.
587     double factor = conversionToCanonicalUnitsScaleFactor(sourceUnitType);
588     convertedValue *= factor;
589
590     // Now convert from canonical type to the target unitType.
591     factor = conversionToCanonicalUnitsScaleFactor(targetUnitType);
592     convertedValue /= factor;
593
594     *result = convertedValue;
595     return true;
596 }
597
598 void CSSPrimitiveValue::setStringValue(unsigned short, const String&, ExceptionCode& ec)
599 {
600     // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects. 
601     // No other engine supports mutating style through this API. Computed style is always read-only anyway.
602     // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation.
603     ec = NO_MODIFICATION_ALLOWED_ERR;
604 }
605
606 String CSSPrimitiveValue::getStringValue(ExceptionCode& ec) const
607 {
608     ec = 0;
609     switch (m_type) {
610         case CSS_STRING:
611         case CSS_ATTR:
612         case CSS_URI:
613             return m_value.string;
614         case CSS_IDENT:
615             return valueOrPropertyName(m_value.ident);
616         default:
617             ec = INVALID_ACCESS_ERR;
618             break;
619     }
620
621     return String();
622 }
623
624 String CSSPrimitiveValue::getStringValue() const
625 {
626     switch (m_type) {
627         case CSS_STRING:
628         case CSS_ATTR:
629         case CSS_URI:
630              return m_value.string;
631         case CSS_IDENT:
632             return valueOrPropertyName(m_value.ident);
633         default:
634             break;
635     }
636
637     return String();
638 }
639
640 Counter* CSSPrimitiveValue::getCounterValue(ExceptionCode& ec) const
641 {
642     ec = 0;
643     if (m_type != CSS_COUNTER) {
644         ec = INVALID_ACCESS_ERR;
645         return 0;
646     }
647
648     return m_value.counter;
649 }
650
651 Rect* CSSPrimitiveValue::getRectValue(ExceptionCode& ec) const
652 {
653     ec = 0;
654     if (m_type != CSS_RECT) {
655         ec = INVALID_ACCESS_ERR;
656         return 0;
657     }
658
659     return m_value.rect;
660 }
661
662 PassRefPtr<RGBColor> CSSPrimitiveValue::getRGBColorValue(ExceptionCode& ec) const
663 {
664     ec = 0;
665     if (m_type != CSS_RGBCOLOR) {
666         ec = INVALID_ACCESS_ERR;
667         return 0;
668     }
669
670     // FIMXE: This should not return a new object for each invocation.
671     return RGBColor::create(m_value.rgbcolor);
672 }
673
674 Pair* CSSPrimitiveValue::getPairValue(ExceptionCode& ec) const
675 {
676     ec = 0;
677     if (m_type != CSS_PAIR) {
678         ec = INVALID_ACCESS_ERR;
679         return 0;
680     }
681
682     return m_value.pair;
683 }
684
685 unsigned short CSSPrimitiveValue::cssValueType() const
686 {
687     return CSS_PRIMITIVE_VALUE;
688 }
689
690 bool CSSPrimitiveValue::parseString(const String& /*string*/, bool /*strict*/)
691 {
692     // FIXME
693     return false;
694 }
695
696 int CSSPrimitiveValue::getIdent() const
697 {
698     if (m_type != CSS_IDENT)
699         return 0;
700     return m_value.ident;
701 }
702
703 static String formatNumber(double number)
704 {
705     DecimalNumber decimal(number);
706     
707     StringBuffer buffer(decimal.bufferLengthForStringDecimal());
708     unsigned length = decimal.toStringDecimal(buffer.characters(), buffer.length());
709     ASSERT_UNUSED(length, length == buffer.length());
710
711     return String::adopt(buffer);
712 }
713
714 String CSSPrimitiveValue::cssText() const
715 {
716     // FIXME: return the original value instead of a generated one (e.g. color
717     // name if it was specified) - check what spec says about this
718
719     if (m_hasCachedCSSText) {
720         ASSERT(cssTextCache().contains(this));
721         return cssTextCache().get(this);
722     }
723
724     String text;
725     switch (m_type) {
726         case CSS_UNKNOWN:
727             // FIXME
728             break;
729         case CSS_NUMBER:
730         case CSS_PARSER_INTEGER:
731             text = formatNumber(m_value.num);
732             break;
733         case CSS_PERCENTAGE:
734             text = formatNumber(m_value.num) + "%";
735             break;
736         case CSS_EMS:
737             text = formatNumber(m_value.num) + "em";
738             break;
739         case CSS_EXS:
740             text = formatNumber(m_value.num) + "ex";
741             break;
742         case CSS_REMS:
743             text = formatNumber(m_value.num) + "rem";
744             break;
745         case CSS_PX:
746             text = formatNumber(m_value.num) + "px";
747             break;
748         case CSS_CM:
749             text = formatNumber(m_value.num) + "cm";
750             break;
751         case CSS_MM:
752             text = formatNumber(m_value.num) + "mm";
753             break;
754         case CSS_IN:
755             text = formatNumber(m_value.num) + "in";
756             break;
757         case CSS_PT:
758             text = formatNumber(m_value.num) + "pt";
759             break;
760         case CSS_PC:
761             text = formatNumber(m_value.num) + "pc";
762             break;
763         case CSS_DEG:
764             text = formatNumber(m_value.num) + "deg";
765             break;
766         case CSS_RAD:
767             text = formatNumber(m_value.num) + "rad";
768             break;
769         case CSS_GRAD:
770             text = formatNumber(m_value.num) + "grad";
771             break;
772         case CSS_MS:
773             text = formatNumber(m_value.num) + "ms";
774             break;
775         case CSS_S:
776             text = formatNumber(m_value.num) + "s";
777             break;
778         case CSS_HZ:
779             text = formatNumber(m_value.num) + "hz";
780             break;
781         case CSS_KHZ:
782             text = formatNumber(m_value.num) + "khz";
783             break;
784         case CSS_TURN:
785             text = formatNumber(m_value.num) + "turn";
786             break;
787         case CSS_DIMENSION:
788             // FIXME
789             break;
790         case CSS_STRING:
791             text = quoteCSSStringIfNeeded(m_value.string);
792             break;
793         case CSS_URI:
794             text = "url(" + quoteCSSURLIfNeeded(m_value.string) + ")";
795             break;
796         case CSS_IDENT:
797             text = valueOrPropertyName(m_value.ident);
798             break;
799         case CSS_ATTR: {
800             DEFINE_STATIC_LOCAL(const String, attrParen, ("attr("));
801
802             Vector<UChar> result;
803             result.reserveInitialCapacity(6 + m_value.string->length());
804
805             append(result, attrParen);
806             append(result, m_value.string);
807             result.uncheckedAppend(')');
808
809             text = String::adopt(result);
810             break;
811         }
812         case CSS_COUNTER:
813             text = "counter(";
814             text += String::number(m_value.num);
815             text += ")";
816             // FIXME: Add list-style and separator
817             break;
818         case CSS_RECT: {
819             DEFINE_STATIC_LOCAL(const String, rectParen, ("rect("));
820
821             Rect* rectVal = getRectValue();
822             Vector<UChar> result;
823             result.reserveInitialCapacity(32);
824             append(result, rectParen);
825
826             append(result, rectVal->top()->cssText());
827             result.append(' ');
828
829             append(result, rectVal->right()->cssText());
830             result.append(' ');
831
832             append(result, rectVal->bottom()->cssText());
833             result.append(' ');
834
835             append(result, rectVal->left()->cssText());
836             result.append(')');
837
838             text = String::adopt(result);
839             break;
840         }
841         case CSS_RGBCOLOR:
842         case CSS_PARSER_HEXCOLOR: {
843             DEFINE_STATIC_LOCAL(const String, commaSpace, (", "));
844             DEFINE_STATIC_LOCAL(const String, rgbParen, ("rgb("));
845             DEFINE_STATIC_LOCAL(const String, rgbaParen, ("rgba("));
846
847             RGBA32 rgbColor = m_value.rgbcolor;
848             if (m_type == CSS_PARSER_HEXCOLOR)
849                 Color::parseHexColor(m_value.string, rgbColor);
850             Color color(rgbColor);
851
852             Vector<UChar> result;
853             result.reserveInitialCapacity(32);
854             if (color.hasAlpha())
855                 append(result, rgbaParen);
856             else
857                 append(result, rgbParen);
858
859             appendNumber(result, static_cast<unsigned char>(color.red()));
860             append(result, commaSpace);
861
862             appendNumber(result, static_cast<unsigned char>(color.green()));
863             append(result, commaSpace);
864
865             appendNumber(result, static_cast<unsigned char>(color.blue()));
866             if (color.hasAlpha()) {
867                 append(result, commaSpace);
868                 append(result, String::number(color.alpha() / 256.0f));
869             }
870
871             result.append(')');
872             text = String::adopt(result);
873             break;
874         }
875         case CSS_PAIR:
876             text = m_value.pair->first()->cssText();
877             text += " ";
878             text += m_value.pair->second()->cssText();
879             break;
880 #if ENABLE(DASHBOARD_SUPPORT)
881         case CSS_DASHBOARD_REGION:
882             for (DashboardRegion* region = getDashboardRegionValue(); region; region = region->m_next.get()) {
883                 if (!text.isEmpty())
884                     text.append(' ');
885                 text += "dashboard-region(";
886                 text += region->m_label;
887                 if (region->m_isCircle)
888                     text += " circle";
889                 else if (region->m_isRectangle)
890                     text += " rectangle";
891                 else
892                     break;
893                 if (region->top()->m_type == CSS_IDENT && region->top()->getIdent() == CSSValueInvalid) {
894                     ASSERT(region->right()->m_type == CSS_IDENT);
895                     ASSERT(region->bottom()->m_type == CSS_IDENT);
896                     ASSERT(region->left()->m_type == CSS_IDENT);
897                     ASSERT(region->right()->getIdent() == CSSValueInvalid);
898                     ASSERT(region->bottom()->getIdent() == CSSValueInvalid);
899                     ASSERT(region->left()->getIdent() == CSSValueInvalid);
900                 } else {
901                     text.append(' ');
902                     text += region->top()->cssText() + " ";
903                     text += region->right()->cssText() + " ";
904                     text += region->bottom()->cssText() + " ";
905                     text += region->left()->cssText();
906                 }
907                 text += ")";
908             }
909             break;
910 #endif
911         case CSS_PARSER_OPERATOR: {
912             char c = static_cast<char>(m_value.ident);
913             text = String(&c, 1U);
914             break;
915         }
916         case CSS_PARSER_IDENTIFIER:
917             text = quoteCSSStringIfNeeded(m_value.string);
918             break;
919     }
920
921     ASSERT(!cssTextCache().contains(this));
922     cssTextCache().set(this, text);
923     m_hasCachedCSSText = true;
924     return text;
925 }
926
927 void CSSPrimitiveValue::addSubresourceStyleURLs(ListHashSet<KURL>& urls, const CSSStyleSheet* styleSheet)
928 {
929     if (m_type == CSS_URI)
930         addSubresourceURL(urls, styleSheet->completeURL(m_value.string));
931 }
932
933 } // namespace WebCore