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