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             if (rootStyle)
    320                 factor = computingFontSize ? rootStyle->fontDescription().specifiedSize() : rootStyle->fontDescription().computedSize();
    321             break;
    322         case CSS_PX:
    323             break;
    324         case CSS_CM:
    325             factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
    326             break;
    327         case CSS_MM:
    328             factor = cssPixelsPerInch / 25.4;
    329             break;
    330         case CSS_IN:
    331             factor = cssPixelsPerInch;
    332             break;
    333         case CSS_PT:
    334             factor = cssPixelsPerInch / 72.0;
    335             break;
    336         case CSS_PC:
    337             // 1 pc == 12 pt
    338             factor = cssPixelsPerInch * 12.0 / 72.0;
    339             break;
    340         default:
    341             return -1.0;
    342     }
    343 
    344     double result = getDoubleValue() * factor;
    345     if (!applyZoomMultiplier || multiplier == 1.0)
    346         return result;
    347 
    348     // Any original result that was >= 1 should not be allowed to fall below 1.  This keeps border lines from
    349     // vanishing.
    350     double zoomedResult = result * multiplier;
    351     if (result >= 1.0)
    352         zoomedResult = max(1.0, zoomedResult);
    353     return zoomedResult;
    354 }
    355 
    356 void CSSPrimitiveValue::setFloatValue(unsigned short, double, ExceptionCode& ec)
    357 {
    358     // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects.
    359     // No other engine supports mutating style through this API. Computed style is always read-only anyway.
    360     // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation.
    361     ec = NO_MODIFICATION_ALLOWED_ERR;
    362 }
    363 
    364 static double conversionToCanonicalUnitsScaleFactor(unsigned short unitType)
    365 {
    366     double factor = 1.0;
    367     // FIXME: the switch can be replaced by an array of scale factors.
    368     switch (unitType) {
    369         // These are "canonical" units in their respective categories.
    370         case CSSPrimitiveValue::CSS_PX:
    371         case CSSPrimitiveValue::CSS_DEG:
    372         case CSSPrimitiveValue::CSS_MS:
    373         case CSSPrimitiveValue::CSS_HZ:
    374             break;
    375         case CSSPrimitiveValue::CSS_CM:
    376             factor = cssPixelsPerInch / 2.54; // (2.54 cm/in)
    377             break;
    378         case CSSPrimitiveValue::CSS_MM:
    379             factor = cssPixelsPerInch / 25.4;
    380             break;
    381         case CSSPrimitiveValue::CSS_IN:
    382             factor = cssPixelsPerInch;
    383             break;
    384         case CSSPrimitiveValue::CSS_PT:
    385             factor = cssPixelsPerInch / 72.0;
    386             break;
    387         case CSSPrimitiveValue::CSS_PC:
    388             factor = cssPixelsPerInch * 12.0 / 72.0; // 1 pc == 12 pt
    389             break;
    390         case CSSPrimitiveValue::CSS_RAD:
    391             factor = 180 / piDouble;
    392             break;
    393         case CSSPrimitiveValue::CSS_GRAD:
    394             factor = 0.9;
    395             break;
    396         case CSSPrimitiveValue::CSS_TURN:
    397             factor = 360;
    398             break;
    399         case CSSPrimitiveValue::CSS_S:
    400         case CSSPrimitiveValue::CSS_KHZ:
    401             factor = 1000;
    402             break;
    403         default:
    404             break;
    405     }
    406 
    407     return factor;
    408 }
    409 
    410 double CSSPrimitiveValue::getDoubleValue(unsigned short unitType, ExceptionCode& ec) const
    411 {
    412     double result = 0;
    413     bool success = getDoubleValueInternal(static_cast<UnitTypes>(unitType), &result);
    414     if (!success) {
    415         ec = INVALID_ACCESS_ERR;
    416         return 0.0;
    417     }
    418 
    419     ec = 0;
    420     return result;
    421 }
    422 
    423 double CSSPrimitiveValue::getDoubleValue(unsigned short unitType) const
    424 {
    425     double result = 0;
    426     getDoubleValueInternal(static_cast<UnitTypes>(unitType), &result);
    427     return result;
    428 }
    429 
    430 CSSPrimitiveValue::UnitTypes CSSPrimitiveValue::canonicalUnitTypeForCategory(UnitCategory category)
    431 {
    432     // The canonical unit type is chosen according to the way CSSParser::validUnit() chooses the default unit
    433     // in each category (based on unitflags).
    434     switch (category) {
    435     case UNumber:
    436         return CSS_NUMBER;
    437     case ULength:
    438         return CSS_PX;
    439     case UPercent:
    440         return CSS_UNKNOWN; // Cannot convert between numbers and percent.
    441     case UTime:
    442         return CSS_MS;
    443     case UAngle:
    444         return CSS_DEG;
    445     case UFrequency:
    446         return CSS_HZ;
    447     default:
    448         return CSS_UNKNOWN;
    449     }
    450 }
    451 
    452 bool CSSPrimitiveValue::getDoubleValueInternal(UnitTypes requestedUnitType, double* result) const
    453 {
    454     if (m_type < CSS_NUMBER || (m_type > CSS_DIMENSION && m_type < CSS_TURN) || requestedUnitType < CSS_NUMBER || (requestedUnitType > CSS_DIMENSION && requestedUnitType < CSS_TURN))
    455         return false;
    456     if (requestedUnitType == m_type || requestedUnitType == CSS_DIMENSION) {
    457         *result = m_value.num;
    458         return true;
    459     }
    460 
    461     UnitTypes sourceUnitType = static_cast<UnitTypes>(m_type);
    462     UnitCategory sourceCategory = unitCategory(sourceUnitType);
    463     ASSERT(sourceCategory != UOther);
    464 
    465     UnitTypes targetUnitType = requestedUnitType;
    466     UnitCategory targetCategory = unitCategory(targetUnitType);
    467     ASSERT(targetCategory != UOther);
    468 
    469     // Cannot convert between unrelated unit categories if one of them is not UNumber.
    470     if (sourceCategory != targetCategory && sourceCategory != UNumber && targetCategory != UNumber)
    471         return false;
    472 
    473     if (targetCategory == UNumber) {
    474         // We interpret conversion to CSS_NUMBER as conversion to a canonical unit in this value's category.
    475         targetUnitType = canonicalUnitTypeForCategory(sourceCategory);
    476         if (targetUnitType == CSS_UNKNOWN)
    477             return false;
    478     }
    479 
    480     if (sourceUnitType == CSS_NUMBER) {
    481         // We interpret conversion from CSS_NUMBER in the same way as CSSParser::validUnit() while using non-strict mode.
    482         sourceUnitType = canonicalUnitTypeForCategory(targetCategory);
    483         if (sourceUnitType == CSS_UNKNOWN)
    484             return false;
    485     }
    486 
    487     double convertedValue = m_value.num;
    488 
    489     // First convert the value from m_type to canonical type.
    490     double factor = conversionToCanonicalUnitsScaleFactor(sourceUnitType);
    491     convertedValue *= factor;
    492 
    493     // Now convert from canonical type to the target unitType.
    494     factor = conversionToCanonicalUnitsScaleFactor(targetUnitType);
    495     convertedValue /= factor;
    496 
    497     *result = convertedValue;
    498     return true;
    499 }
    500 
    501 void CSSPrimitiveValue::setStringValue(unsigned short, const String&, ExceptionCode& ec)
    502 {
    503     // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects.
    504     // No other engine supports mutating style through this API. Computed style is always read-only anyway.
    505     // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation.
    506     ec = NO_MODIFICATION_ALLOWED_ERR;
    507 }
    508 
    509 String CSSPrimitiveValue::getStringValue(ExceptionCode& ec) const
    510 {
    511     ec = 0;
    512     switch (m_type) {
    513         case CSS_STRING:
    514         case CSS_ATTR:
    515         case CSS_URI:
    516             return m_value.string;
    517         case CSS_IDENT:
    518             return valueOrPropertyName(m_value.ident);
    519         default:
    520             ec = INVALID_ACCESS_ERR;
    521             break;
    522     }
    523 
    524     return String();
    525 }
    526 
    527 String CSSPrimitiveValue::getStringValue() const
    528 {
    529     switch (m_type) {
    530         case CSS_STRING:
    531         case CSS_ATTR:
    532         case CSS_URI:
    533              return m_value.string;
    534         case CSS_IDENT:
    535             return valueOrPropertyName(m_value.ident);
    536         default:
    537             break;
    538     }
    539 
    540     return String();
    541 }
    542 
    543 Counter* CSSPrimitiveValue::getCounterValue(ExceptionCode& ec) const
    544 {
    545     ec = 0;
    546     if (m_type != CSS_COUNTER) {
    547         ec = INVALID_ACCESS_ERR;
    548         return 0;
    549     }
    550 
    551     return m_value.counter;
    552 }
    553 
    554 Rect* CSSPrimitiveValue::getRectValue(ExceptionCode& ec) const
    555 {
    556     ec = 0;
    557     if (m_type != CSS_RECT) {
    558         ec = INVALID_ACCESS_ERR;
    559         return 0;
    560     }
    561 
    562     return m_value.rect;
    563 }
    564 
    565 PassRefPtr<RGBColor> CSSPrimitiveValue::getRGBColorValue(ExceptionCode& ec) const
    566 {
    567     ec = 0;
    568     if (m_type != CSS_RGBCOLOR) {
    569         ec = INVALID_ACCESS_ERR;
    570         return 0;
    571     }
    572 
    573     // FIMXE: This should not return a new object for each invocation.
    574     return RGBColor::create(m_value.rgbcolor);
    575 }
    576 
    577 Pair* CSSPrimitiveValue::getPairValue(ExceptionCode& ec) const
    578 {
    579     ec = 0;
    580     if (m_type != CSS_PAIR) {
    581         ec = INVALID_ACCESS_ERR;
    582         return 0;
    583     }
    584 
    585     return m_value.pair;
    586 }
    587 
    588 unsigned short CSSPrimitiveValue::cssValueType() const
    589 {
    590     return CSS_PRIMITIVE_VALUE;
    591 }
    592 
    593 bool CSSPrimitiveValue::parseString(const String& /*string*/, bool /*strict*/)
    594 {
    595     // FIXME
    596     return false;
    597 }
    598 
    599 int CSSPrimitiveValue::getIdent() const
    600 {
    601     if (m_type != CSS_IDENT)
    602         return 0;
    603     return m_value.ident;
    604 }
    605 
    606 static String formatNumber(double number)
    607 {
    608     DecimalNumber decimal(number);
    609 
    610     StringBuffer buffer(decimal.bufferLengthForStringDecimal());
    611     unsigned length = decimal.toStringDecimal(buffer.characters(), buffer.length());
    612     ASSERT_UNUSED(length, length == buffer.length());
    613 
    614     return String::adopt(buffer);
    615 }
    616 
    617 String CSSPrimitiveValue::cssText() const
    618 {
    619     // FIXME: return the original value instead of a generated one (e.g. color
    620     // name if it was specified) - check what spec says about this
    621 
    622     if (m_hasCachedCSSText) {
    623         ASSERT(cssTextCache().contains(this));
    624         return cssTextCache().get(this);
    625     }
    626 
    627     String text;
    628     switch (m_type) {
    629         case CSS_UNKNOWN:
    630             // FIXME
    631             break;
    632         case CSS_NUMBER:
    633         case CSS_PARSER_INTEGER:
    634             text = formatNumber(m_value.num);
    635             break;
    636         case CSS_PERCENTAGE:
    637             text = formatNumber(m_value.num) + "%";
    638             break;
    639         case CSS_EMS:
    640             text = formatNumber(m_value.num) + "em";
    641             break;
    642         case CSS_EXS:
    643             text = formatNumber(m_value.num) + "ex";
    644             break;
    645         case CSS_REMS:
    646             text = formatNumber(m_value.num) + "rem";
    647             break;
    648         case CSS_PX:
    649             text = formatNumber(m_value.num) + "px";
    650             break;
    651         case CSS_CM:
    652             text = formatNumber(m_value.num) + "cm";
    653             break;
    654         case CSS_MM:
    655             text = formatNumber(m_value.num) + "mm";
    656             break;
    657         case CSS_IN:
    658             text = formatNumber(m_value.num) + "in";
    659             break;
    660         case CSS_PT:
    661             text = formatNumber(m_value.num) + "pt";
    662             break;
    663         case CSS_PC:
    664             text = formatNumber(m_value.num) + "pc";
    665             break;
    666         case CSS_DEG:
    667             text = formatNumber(m_value.num) + "deg";
    668             break;
    669         case CSS_RAD:
    670             text = formatNumber(m_value.num) + "rad";
    671             break;
    672         case CSS_GRAD:
    673             text = formatNumber(m_value.num) + "grad";
    674             break;
    675         case CSS_MS:
    676             text = formatNumber(m_value.num) + "ms";
    677             break;
    678         case CSS_S:
    679             text = formatNumber(m_value.num) + "s";
    680             break;
    681         case CSS_HZ:
    682             text = formatNumber(m_value.num) + "hz";
    683             break;
    684         case CSS_KHZ:
    685             text = formatNumber(m_value.num) + "khz";
    686             break;
    687         case CSS_TURN:
    688             text = formatNumber(m_value.num) + "turn";
    689             break;
    690         case CSS_DIMENSION:
    691             // FIXME
    692             break;
    693         case CSS_STRING:
    694             text = quoteCSSStringIfNeeded(m_value.string);
    695             break;
    696         case CSS_URI:
    697             text = "url(" + quoteCSSURLIfNeeded(m_value.string) + ")";
    698             break;
    699         case CSS_IDENT:
    700             text = valueOrPropertyName(m_value.ident);
    701             break;
    702         case CSS_ATTR: {
    703             DEFINE_STATIC_LOCAL(const String, attrParen, ("attr("));
    704 
    705             Vector<UChar> result;
    706             result.reserveInitialCapacity(6 + m_value.string->length());
    707 
    708             append(result, attrParen);
    709             append(result, m_value.string);
    710             result.uncheckedAppend(')');
    711 
    712             text = String::adopt(result);
    713             break;
    714         }
    715         case CSS_COUNTER_NAME:
    716             text = "counter(";
    717             text += m_value.string;
    718             text += ")";
    719             break;
    720         case CSS_COUNTER:
    721             text = "counter(";
    722             text += String::number(m_value.num);
    723             text += ")";
    724             // FIXME: Add list-style and separator
    725             break;
    726         case CSS_RECT: {
    727             DEFINE_STATIC_LOCAL(const String, rectParen, ("rect("));
    728 
    729             Rect* rectVal = getRectValue();
    730             Vector<UChar> result;
    731             result.reserveInitialCapacity(32);
    732             append(result, rectParen);
    733 
    734             append(result, rectVal->top()->cssText());
    735             result.append(' ');
    736 
    737             append(result, rectVal->right()->cssText());
    738             result.append(' ');
    739 
    740             append(result, rectVal->bottom()->cssText());
    741             result.append(' ');
    742 
    743             append(result, rectVal->left()->cssText());
    744             result.append(')');
    745 
    746             text = String::adopt(result);
    747             break;
    748         }
    749         case CSS_RGBCOLOR:
    750         case CSS_PARSER_HEXCOLOR: {
    751             DEFINE_STATIC_LOCAL(const String, commaSpace, (", "));
    752             DEFINE_STATIC_LOCAL(const String, rgbParen, ("rgb("));
    753             DEFINE_STATIC_LOCAL(const String, rgbaParen, ("rgba("));
    754 
    755             RGBA32 rgbColor = m_value.rgbcolor;
    756             if (m_type == CSS_PARSER_HEXCOLOR)
    757                 Color::parseHexColor(m_value.string, rgbColor);
    758             Color color(rgbColor);
    759 
    760             Vector<UChar> result;
    761             result.reserveInitialCapacity(32);
    762             if (color.hasAlpha())
    763                 append(result, rgbaParen);
    764             else
    765                 append(result, rgbParen);
    766 
    767             appendNumber(result, static_cast<unsigned char>(color.red()));
    768             append(result, commaSpace);
    769 
    770             appendNumber(result, static_cast<unsigned char>(color.green()));
    771             append(result, commaSpace);
    772 
    773             appendNumber(result, static_cast<unsigned char>(color.blue()));
    774             if (color.hasAlpha()) {
    775                 append(result, commaSpace);
    776                 append(result, String::number(color.alpha() / 256.0f));
    777             }
    778 
    779             result.append(')');
    780             text = String::adopt(result);
    781             break;
    782         }
    783         case CSS_PAIR:
    784             text = m_value.pair->first()->cssText();
    785             text += " ";
    786             text += m_value.pair->second()->cssText();
    787             break;
    788 #if ENABLE(DASHBOARD_SUPPORT)
    789         case CSS_DASHBOARD_REGION:
    790             for (DashboardRegion* region = getDashboardRegionValue(); region; region = region->m_next.get()) {
    791                 if (!text.isEmpty())
    792                     text.append(' ');
    793                 text += "dashboard-region(";
    794                 text += region->m_label;
    795                 if (region->m_isCircle)
    796                     text += " circle";
    797                 else if (region->m_isRectangle)
    798                     text += " rectangle";
    799                 else
    800                     break;
    801                 if (region->top()->m_type == CSS_IDENT && region->top()->getIdent() == CSSValueInvalid) {
    802                     ASSERT(region->right()->m_type == CSS_IDENT);
    803                     ASSERT(region->bottom()->m_type == CSS_IDENT);
    804                     ASSERT(region->left()->m_type == CSS_IDENT);
    805                     ASSERT(region->right()->getIdent() == CSSValueInvalid);
    806                     ASSERT(region->bottom()->getIdent() == CSSValueInvalid);
    807                     ASSERT(region->left()->getIdent() == CSSValueInvalid);
    808                 } else {
    809                     text.append(' ');
    810                     text += region->top()->cssText() + " ";
    811                     text += region->right()->cssText() + " ";
    812                     text += region->bottom()->cssText() + " ";
    813                     text += region->left()->cssText();
    814                 }
    815                 text += ")";
    816             }
    817             break;
    818 #endif
    819         case CSS_PARSER_OPERATOR: {
    820             char c = static_cast<char>(m_value.ident);
    821             text = String(&c, 1U);
    822             break;
    823         }
    824         case CSS_PARSER_IDENTIFIER:
    825             text = quoteCSSStringIfNeeded(m_value.string);
    826             break;
    827     }
    828 
    829     ASSERT(!cssTextCache().contains(this));
    830     cssTextCache().set(this, text);
    831     m_hasCachedCSSText = true;
    832     return text;
    833 }
    834 
    835 void CSSPrimitiveValue::addSubresourceStyleURLs(ListHashSet<KURL>& urls, const CSSStyleSheet* styleSheet)
    836 {
    837     if (m_type == CSS_URI)
    838         addSubresourceURL(urls, styleSheet->completeURL(m_value.string));
    839 }
    840 
    841 } // namespace WebCore
    842