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