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