diff options
Diffstat (limited to 'Source/WebCore/css/CSSPrimitiveValue.cpp')
-rw-r--r-- | Source/WebCore/css/CSSPrimitiveValue.cpp | 998 |
1 files changed, 998 insertions, 0 deletions
diff --git a/Source/WebCore/css/CSSPrimitiveValue.cpp b/Source/WebCore/css/CSSPrimitiveValue.cpp new file mode 100644 index 0000000..ce1b87b --- /dev/null +++ b/Source/WebCore/css/CSSPrimitiveValue.cpp @@ -0,0 +1,998 @@ +/* + * (C) 1999-2003 Lars Knoll (knoll@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "CSSPrimitiveValue.h" + +#include "CSSHelper.h" +#include "CSSParser.h" +#include "CSSPropertyNames.h" +#include "CSSStyleSheet.h" +#include "CSSValueKeywords.h" +#include "Color.h" +#include "Counter.h" +#include "ExceptionCode.h" +#include "Node.h" +#include "Pair.h" +#include "RGBColor.h" +#include "Rect.h" +#include "RenderStyle.h" +#include <wtf/ASCIICType.h> +#include <wtf/DecimalNumber.h> +#include <wtf/MathExtras.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringBuffer.h> + +#if ENABLE(DASHBOARD_SUPPORT) +#include "DashboardRegion.h" +#endif + +using namespace WTF; + +namespace WebCore { + +static CSSPrimitiveValue::UnitCategory unitCategory(CSSPrimitiveValue::UnitTypes type) +{ + // Here we violate the spec (http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSPrimitiveValue) and allow conversions + // between CSS_PX and relative lengths (see cssPixelsPerInch comment in CSSHelper.h for the topic treatment). + switch (type) { + case CSSPrimitiveValue::CSS_NUMBER: + return CSSPrimitiveValue::UNumber; + case CSSPrimitiveValue::CSS_PERCENTAGE: + return CSSPrimitiveValue::UPercent; + case CSSPrimitiveValue::CSS_PX: + case CSSPrimitiveValue::CSS_CM: + case CSSPrimitiveValue::CSS_MM: + case CSSPrimitiveValue::CSS_IN: + case CSSPrimitiveValue::CSS_PT: + case CSSPrimitiveValue::CSS_PC: + return CSSPrimitiveValue::ULength; + case CSSPrimitiveValue::CSS_MS: + case CSSPrimitiveValue::CSS_S: + return CSSPrimitiveValue::UTime; + case CSSPrimitiveValue::CSS_DEG: + case CSSPrimitiveValue::CSS_RAD: + case CSSPrimitiveValue::CSS_GRAD: + case CSSPrimitiveValue::CSS_TURN: + return CSSPrimitiveValue::UAngle; + case CSSPrimitiveValue::CSS_HZ: + case CSSPrimitiveValue::CSS_KHZ: + return CSSPrimitiveValue::UFrequency; + default: + return CSSPrimitiveValue::UOther; + } +} + +typedef HashMap<const CSSPrimitiveValue*, String> CSSTextCache; +static CSSTextCache& cssTextCache() +{ + DEFINE_STATIC_LOCAL(CSSTextCache, cache, ()); + return cache; +} + +// A more stylish solution than sharing would be to turn CSSPrimitiveValue (or CSSValues in general) into non-virtual, +// non-refcounted simple type with value semantics. In practice these sharing tricks get similar memory benefits +// with less need for refactoring. + +inline PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::createUncachedIdentifier(int identifier) +{ + return adoptRef(new CSSPrimitiveValue(identifier)); +} + +inline PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::createUncachedColor(unsigned rgbValue) +{ + return adoptRef(new CSSPrimitiveValue(rgbValue)); +} + +inline PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::createUncached(double value, UnitTypes type) +{ + return adoptRef(new CSSPrimitiveValue(value, type)); +} + +PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::createIdentifier(int ident) +{ + static RefPtr<CSSPrimitiveValue>* identValueCache = new RefPtr<CSSPrimitiveValue>[numCSSValueKeywords]; + if (ident >= 0 && ident < numCSSValueKeywords) { + RefPtr<CSSPrimitiveValue> primitiveValue = identValueCache[ident]; + if (!primitiveValue) { + primitiveValue = createUncachedIdentifier(ident); + identValueCache[ident] = primitiveValue; + } + return primitiveValue.release(); + } + return createUncachedIdentifier(ident); +} + +PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::createColor(unsigned rgbValue) +{ + typedef HashMap<unsigned, RefPtr<CSSPrimitiveValue> > ColorValueCache; + static ColorValueCache* colorValueCache = new ColorValueCache; + // These are the empty and deleted values of the hash table. + if (rgbValue == Color::transparent) { + static CSSPrimitiveValue* colorTransparent = createUncachedColor(Color::transparent).releaseRef(); + return colorTransparent; + } + if (rgbValue == Color::white) { + static CSSPrimitiveValue* colorWhite = createUncachedColor(Color::white).releaseRef(); + return colorWhite; + } + RefPtr<CSSPrimitiveValue> primitiveValue = colorValueCache->get(rgbValue); + if (primitiveValue) + return primitiveValue.release(); + primitiveValue = createUncachedColor(rgbValue); + // Just wipe out the cache and start rebuilding when it gets too big. + const int maxColorCacheSize = 512; + if (colorValueCache->size() >= maxColorCacheSize) + colorValueCache->clear(); + colorValueCache->add(rgbValue, primitiveValue); + + return primitiveValue.release(); +} + +PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::create(double value, UnitTypes type) +{ + // Small integers are very common. Try to share them. + const int cachedIntegerCount = 128; + // Other common primitive types have UnitTypes smaller than this. + const int maxCachedUnitType = CSS_PX; + typedef RefPtr<CSSPrimitiveValue>(* IntegerValueCache)[maxCachedUnitType + 1]; + static IntegerValueCache integerValueCache = new RefPtr<CSSPrimitiveValue>[cachedIntegerCount][maxCachedUnitType + 1]; + if (type <= maxCachedUnitType && value >= 0 && value < cachedIntegerCount) { + int intValue = static_cast<int>(value); + if (value == intValue) { + RefPtr<CSSPrimitiveValue> primitiveValue = integerValueCache[intValue][type]; + if (!primitiveValue) { + primitiveValue = createUncached(value, type); + integerValueCache[intValue][type] = primitiveValue; + } + return primitiveValue.release(); + } + } + + return createUncached(value, type); +} + +PassRefPtr<CSSPrimitiveValue> CSSPrimitiveValue::create(const String& value, UnitTypes type) +{ + return adoptRef(new CSSPrimitiveValue(value, type)); +} + +static const AtomicString& valueOrPropertyName(int valueOrPropertyID) +{ + ASSERT_ARG(valueOrPropertyID, valueOrPropertyID >= 0); + ASSERT_ARG(valueOrPropertyID, valueOrPropertyID < numCSSValueKeywords || (valueOrPropertyID >= firstCSSProperty && valueOrPropertyID < firstCSSProperty + numCSSProperties)); + + if (valueOrPropertyID < 0) + return nullAtom; + + if (valueOrPropertyID < numCSSValueKeywords) { + static AtomicString* cssValueKeywordStrings[numCSSValueKeywords]; + if (!cssValueKeywordStrings[valueOrPropertyID]) + cssValueKeywordStrings[valueOrPropertyID] = new AtomicString(getValueName(valueOrPropertyID)); + return *cssValueKeywordStrings[valueOrPropertyID]; + } + + if (valueOrPropertyID >= firstCSSProperty && valueOrPropertyID < firstCSSProperty + numCSSProperties) { + static AtomicString* cssPropertyStrings[numCSSProperties]; + int propertyIndex = valueOrPropertyID - firstCSSProperty; + if (!cssPropertyStrings[propertyIndex]) + cssPropertyStrings[propertyIndex] = new AtomicString(getPropertyName(static_cast<CSSPropertyID>(valueOrPropertyID))); + return *cssPropertyStrings[propertyIndex]; + } + + return nullAtom; +} + +CSSPrimitiveValue::CSSPrimitiveValue() + : m_type(0) + , m_hasCachedCSSText(false) +{ +} + +CSSPrimitiveValue::CSSPrimitiveValue(int ident) + : m_type(CSS_IDENT) + , m_hasCachedCSSText(false) +{ + m_value.ident = ident; +} + +CSSPrimitiveValue::CSSPrimitiveValue(double num, UnitTypes type) + : m_type(type) + , m_hasCachedCSSText(false) +{ + m_value.num = num; +} + +CSSPrimitiveValue::CSSPrimitiveValue(const String& str, UnitTypes type) + : m_type(type) + , m_hasCachedCSSText(false) +{ + if ((m_value.string = str.impl())) + m_value.string->ref(); +} + +CSSPrimitiveValue::CSSPrimitiveValue(RGBA32 color) + : m_type(CSS_RGBCOLOR) + , m_hasCachedCSSText(false) +{ + m_value.rgbcolor = color; +} + +CSSPrimitiveValue::CSSPrimitiveValue(const Length& length) + : m_hasCachedCSSText(false) +{ + switch (length.type()) { + case Auto: + m_type = CSS_IDENT; + m_value.ident = CSSValueAuto; + break; + case WebCore::Fixed: + m_type = CSS_PX; + m_value.num = length.value(); + break; + case Intrinsic: + m_type = CSS_IDENT; + m_value.ident = CSSValueIntrinsic; + break; + case MinIntrinsic: + m_type = CSS_IDENT; + m_value.ident = CSSValueMinIntrinsic; + break; + case Percent: + m_type = CSS_PERCENTAGE; + m_value.num = length.percent(); + break; + case Relative: + case Static: + ASSERT_NOT_REACHED(); + break; + } +} + +void CSSPrimitiveValue::init(PassRefPtr<Counter> c) +{ + m_type = CSS_COUNTER; + m_hasCachedCSSText = false; + m_value.counter = c.releaseRef(); +} + +void CSSPrimitiveValue::init(PassRefPtr<Rect> r) +{ + m_type = CSS_RECT; + m_hasCachedCSSText = false; + m_value.rect = r.releaseRef(); +} + +#if ENABLE(DASHBOARD_SUPPORT) +void CSSPrimitiveValue::init(PassRefPtr<DashboardRegion> r) +{ + m_type = CSS_DASHBOARD_REGION; + m_hasCachedCSSText = false; + m_value.region = r.releaseRef(); +} +#endif + +void CSSPrimitiveValue::init(PassRefPtr<Pair> p) +{ + m_type = CSS_PAIR; + m_hasCachedCSSText = false; + m_value.pair = p.releaseRef(); +} + +CSSPrimitiveValue::~CSSPrimitiveValue() +{ + cleanup(); +} + +void CSSPrimitiveValue::cleanup() +{ + switch (m_type) { + case CSS_STRING: + case CSS_URI: + case CSS_ATTR: + case CSS_PARSER_HEXCOLOR: + if (m_value.string) + m_value.string->deref(); + break; + case CSS_COUNTER: + m_value.counter->deref(); + break; + case CSS_RECT: + m_value.rect->deref(); + break; + case CSS_PAIR: + m_value.pair->deref(); + break; +#if ENABLE(DASHBOARD_SUPPORT) + case CSS_DASHBOARD_REGION: + if (m_value.region) + m_value.region->deref(); + break; +#endif + default: + break; + } + + m_type = 0; + if (m_hasCachedCSSText) { + cssTextCache().remove(this); + m_hasCachedCSSText = false; + } +} + +int CSSPrimitiveValue::computeLengthInt(RenderStyle* style, RenderStyle* rootStyle) +{ + return roundForImpreciseConversion<int, INT_MAX, INT_MIN>(computeLengthDouble(style, rootStyle)); +} + +int CSSPrimitiveValue::computeLengthInt(RenderStyle* style, RenderStyle* rootStyle, double multiplier) +{ + return roundForImpreciseConversion<int, INT_MAX, INT_MIN>(computeLengthDouble(style, rootStyle, multiplier)); +} + +// Lengths expect an int that is only 28-bits, so we have to check for a +// different overflow. +int CSSPrimitiveValue::computeLengthIntForLength(RenderStyle* style, RenderStyle* rootStyle) +{ + return roundForImpreciseConversion<int, intMaxForLength, intMinForLength>(computeLengthDouble(style, rootStyle)); +} + +int CSSPrimitiveValue::computeLengthIntForLength(RenderStyle* style, RenderStyle* rootStyle, double multiplier) +{ + return roundForImpreciseConversion<int, intMaxForLength, intMinForLength>(computeLengthDouble(style, rootStyle, multiplier)); +} + +short CSSPrimitiveValue::computeLengthShort(RenderStyle* style, RenderStyle* rootStyle) +{ + return roundForImpreciseConversion<short, SHRT_MAX, SHRT_MIN>(computeLengthDouble(style, rootStyle)); +} + +short CSSPrimitiveValue::computeLengthShort(RenderStyle* style, RenderStyle* rootStyle, double multiplier) +{ + return roundForImpreciseConversion<short, SHRT_MAX, SHRT_MIN>(computeLengthDouble(style, rootStyle, multiplier)); +} + +float CSSPrimitiveValue::computeLengthFloat(RenderStyle* style, RenderStyle* rootStyle, bool computingFontSize) +{ + return static_cast<float>(computeLengthDouble(style, rootStyle, 1.0, computingFontSize)); +} + +float CSSPrimitiveValue::computeLengthFloat(RenderStyle* style, RenderStyle* rootStyle, double multiplier, bool computingFontSize) +{ + return static_cast<float>(computeLengthDouble(style, rootStyle, multiplier, computingFontSize)); +} + +double CSSPrimitiveValue::computeLengthDouble(RenderStyle* style, RenderStyle* rootStyle, double multiplier, bool computingFontSize) +{ + unsigned short type = primitiveType(); + + // We do not apply the zoom factor when we are computing the value of the font-size property. The zooming + // for font sizes is much more complicated, since we have to worry about enforcing the minimum font size preference + // as well as enforcing the implicit "smart minimum." In addition the CSS property text-size-adjust is used to + // prevent text from zooming at all. Therefore we will not apply the zoom here if we are computing font-size. + bool applyZoomMultiplier = !computingFontSize; + + double factor = 1.0; + switch (type) { + case CSS_EMS: + applyZoomMultiplier = false; + factor = computingFontSize ? style->fontDescription().specifiedSize() : style->fontDescription().computedSize(); + break; + case CSS_EXS: + // FIXME: We have a bug right now where the zoom will be applied twice to EX units. + // We really need to compute EX using fontMetrics for the original specifiedSize and not use + // our actual constructed rendering font. + applyZoomMultiplier = false; + factor = style->font().xHeight(); + break; + case CSS_REMS: + applyZoomMultiplier = false; + factor = computingFontSize ? rootStyle->fontDescription().specifiedSize() : rootStyle->fontDescription().computedSize(); + break; + case CSS_PX: + break; + case CSS_CM: + factor = cssPixelsPerInch / 2.54; // (2.54 cm/in) + break; + case CSS_MM: + factor = cssPixelsPerInch / 25.4; + break; + case CSS_IN: + factor = cssPixelsPerInch; + break; + case CSS_PT: + factor = cssPixelsPerInch / 72.0; + break; + case CSS_PC: + // 1 pc == 12 pt + factor = cssPixelsPerInch * 12.0 / 72.0; + break; + default: + return -1.0; + } + + double result = getDoubleValue() * factor; + if (!applyZoomMultiplier || multiplier == 1.0) + return result; + + // Any original result that was >= 1 should not be allowed to fall below 1. This keeps border lines from + // vanishing. + double zoomedResult = result * multiplier; + if (result >= 1.0) + zoomedResult = max(1.0, zoomedResult); + return zoomedResult; +} + +void CSSPrimitiveValue::setFloatValue(unsigned short, double, ExceptionCode& ec) +{ + // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects. + // No other engine supports mutating style through this API. Computed style is always read-only anyway. + // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation. + ec = NO_MODIFICATION_ALLOWED_ERR; +} + +static double conversionToCanonicalUnitsScaleFactor(unsigned short unitType) +{ + double factor = 1.0; + // FIXME: the switch can be replaced by an array of scale factors. + switch (unitType) { + // These are "canonical" units in their respective categories. + case CSSPrimitiveValue::CSS_PX: + case CSSPrimitiveValue::CSS_DEG: + case CSSPrimitiveValue::CSS_MS: + case CSSPrimitiveValue::CSS_HZ: + break; + case CSSPrimitiveValue::CSS_CM: + factor = cssPixelsPerInch / 2.54; // (2.54 cm/in) + break; + case CSSPrimitiveValue::CSS_MM: + factor = cssPixelsPerInch / 25.4; + break; + case CSSPrimitiveValue::CSS_IN: + factor = cssPixelsPerInch; + break; + case CSSPrimitiveValue::CSS_PT: + factor = cssPixelsPerInch / 72.0; + break; + case CSSPrimitiveValue::CSS_PC: + factor = cssPixelsPerInch * 12.0 / 72.0; // 1 pc == 12 pt + break; + case CSSPrimitiveValue::CSS_RAD: + factor = 180 / piDouble; + break; + case CSSPrimitiveValue::CSS_GRAD: + factor = 0.9; + break; + case CSSPrimitiveValue::CSS_TURN: + factor = 360; + break; + case CSSPrimitiveValue::CSS_S: + case CSSPrimitiveValue::CSS_KHZ: + factor = 1000; + break; + default: + break; + } + + return factor; +} + +double CSSPrimitiveValue::getDoubleValue(unsigned short unitType, ExceptionCode& ec) const +{ + double result = 0; + bool success = getDoubleValueInternal(static_cast<UnitTypes>(unitType), &result); + if (!success) { + ec = INVALID_ACCESS_ERR; + return 0.0; + } + + ec = 0; + return result; +} + +double CSSPrimitiveValue::getDoubleValue(unsigned short unitType) const +{ + double result = 0; + getDoubleValueInternal(static_cast<UnitTypes>(unitType), &result); + return result; +} + +CSSPrimitiveValue::UnitTypes CSSPrimitiveValue::canonicalUnitTypeForCategory(UnitCategory category) +{ + // The canonical unit type is chosen according to the way CSSParser::validUnit() chooses the default unit + // in each category (based on unitflags). + switch (category) { + case UNumber: + return CSS_NUMBER; + case ULength: + return CSS_PX; + case UPercent: + return CSS_UNKNOWN; // Cannot convert between numbers and percent. + case UTime: + return CSS_MS; + case UAngle: + return CSS_DEG; + case UFrequency: + return CSS_HZ; + default: + return CSS_UNKNOWN; + } +} + +bool CSSPrimitiveValue::getDoubleValueInternal(UnitTypes requestedUnitType, double* result) const +{ + if (m_type < CSS_NUMBER || (m_type > CSS_DIMENSION && m_type < CSS_TURN) || requestedUnitType < CSS_NUMBER || (requestedUnitType > CSS_DIMENSION && requestedUnitType < CSS_TURN)) + return false; + if (requestedUnitType == m_type || requestedUnitType == CSS_DIMENSION) { + *result = m_value.num; + return true; + } + + UnitTypes sourceUnitType = static_cast<UnitTypes>(m_type); + UnitCategory sourceCategory = unitCategory(sourceUnitType); + ASSERT(sourceCategory != UOther); + + UnitTypes targetUnitType = requestedUnitType; + UnitCategory targetCategory = unitCategory(targetUnitType); + ASSERT(targetCategory != UOther); + + // Cannot convert between unrelated unit categories if one of them is not UNumber. + if (sourceCategory != targetCategory && sourceCategory != UNumber && targetCategory != UNumber) + return false; + + if (targetCategory == UNumber) { + // We interpret conversion to CSS_NUMBER as conversion to a canonical unit in this value's category. + targetUnitType = canonicalUnitTypeForCategory(sourceCategory); + if (targetUnitType == CSS_UNKNOWN) + return false; + } + + if (sourceUnitType == CSS_NUMBER) { + // We interpret conversion from CSS_NUMBER in the same way as CSSParser::validUnit() while using non-strict mode. + sourceUnitType = canonicalUnitTypeForCategory(targetCategory); + if (sourceUnitType == CSS_UNKNOWN) + return false; + } + + double convertedValue = m_value.num; + + // First convert the value from m_type to canonical type. + double factor = conversionToCanonicalUnitsScaleFactor(sourceUnitType); + convertedValue *= factor; + + // Now convert from canonical type to the target unitType. + factor = conversionToCanonicalUnitsScaleFactor(targetUnitType); + convertedValue /= factor; + + *result = convertedValue; + return true; +} + +void CSSPrimitiveValue::setStringValue(unsigned short, const String&, ExceptionCode& ec) +{ + // Keeping values immutable makes optimizations easier and allows sharing of the primitive value objects. + // No other engine supports mutating style through this API. Computed style is always read-only anyway. + // Supporting setter would require making primitive value copy-on-write and taking care of style invalidation. + ec = NO_MODIFICATION_ALLOWED_ERR; +} + +String CSSPrimitiveValue::getStringValue(ExceptionCode& ec) const +{ + ec = 0; + switch (m_type) { + case CSS_STRING: + case CSS_ATTR: + case CSS_URI: + return m_value.string; + case CSS_IDENT: + return valueOrPropertyName(m_value.ident); + default: + ec = INVALID_ACCESS_ERR; + break; + } + + return String(); +} + +String CSSPrimitiveValue::getStringValue() const +{ + switch (m_type) { + case CSS_STRING: + case CSS_ATTR: + case CSS_URI: + return m_value.string; + case CSS_IDENT: + return valueOrPropertyName(m_value.ident); + default: + break; + } + + return String(); +} + +Counter* CSSPrimitiveValue::getCounterValue(ExceptionCode& ec) const +{ + ec = 0; + if (m_type != CSS_COUNTER) { + ec = INVALID_ACCESS_ERR; + return 0; + } + + return m_value.counter; +} + +Rect* CSSPrimitiveValue::getRectValue(ExceptionCode& ec) const +{ + ec = 0; + if (m_type != CSS_RECT) { + ec = INVALID_ACCESS_ERR; + return 0; + } + + return m_value.rect; +} + +PassRefPtr<RGBColor> CSSPrimitiveValue::getRGBColorValue(ExceptionCode& ec) const +{ + ec = 0; + if (m_type != CSS_RGBCOLOR) { + ec = INVALID_ACCESS_ERR; + return 0; + } + + // FIMXE: This should not return a new object for each invocation. + return RGBColor::create(m_value.rgbcolor); +} + +Pair* CSSPrimitiveValue::getPairValue(ExceptionCode& ec) const +{ + ec = 0; + if (m_type != CSS_PAIR) { + ec = INVALID_ACCESS_ERR; + return 0; + } + + return m_value.pair; +} + +unsigned short CSSPrimitiveValue::cssValueType() const +{ + return CSS_PRIMITIVE_VALUE; +} + +bool CSSPrimitiveValue::parseString(const String& /*string*/, bool /*strict*/) +{ + // FIXME + return false; +} + +int CSSPrimitiveValue::getIdent() const +{ + if (m_type != CSS_IDENT) + return 0; + return m_value.ident; +} + +static String formatNumber(double number) +{ + DecimalNumber decimal(number); + + StringBuffer buffer(decimal.bufferLengthForStringDecimal()); + unsigned length = decimal.toStringDecimal(buffer.characters(), buffer.length()); + ASSERT_UNUSED(length, length == buffer.length()); + + return String::adopt(buffer); +} + +String CSSPrimitiveValue::cssText() const +{ + // FIXME: return the original value instead of a generated one (e.g. color + // name if it was specified) - check what spec says about this + + if (m_hasCachedCSSText) { + ASSERT(cssTextCache().contains(this)); + return cssTextCache().get(this); + } + + String text; + switch (m_type) { + case CSS_UNKNOWN: + // FIXME + break; + case CSS_NUMBER: + case CSS_PARSER_INTEGER: + text = formatNumber(m_value.num); + break; + case CSS_PERCENTAGE: + text = formatNumber(m_value.num) + "%"; + break; + case CSS_EMS: + text = formatNumber(m_value.num) + "em"; + break; + case CSS_EXS: + text = formatNumber(m_value.num) + "ex"; + break; + case CSS_REMS: + text = formatNumber(m_value.num) + "rem"; + break; + case CSS_PX: + text = formatNumber(m_value.num) + "px"; + break; + case CSS_CM: + text = formatNumber(m_value.num) + "cm"; + break; + case CSS_MM: + text = formatNumber(m_value.num) + "mm"; + break; + case CSS_IN: + text = formatNumber(m_value.num) + "in"; + break; + case CSS_PT: + text = formatNumber(m_value.num) + "pt"; + break; + case CSS_PC: + text = formatNumber(m_value.num) + "pc"; + break; + case CSS_DEG: + text = formatNumber(m_value.num) + "deg"; + break; + case CSS_RAD: + text = formatNumber(m_value.num) + "rad"; + break; + case CSS_GRAD: + text = formatNumber(m_value.num) + "grad"; + break; + case CSS_MS: + text = formatNumber(m_value.num) + "ms"; + break; + case CSS_S: + text = formatNumber(m_value.num) + "s"; + break; + case CSS_HZ: + text = formatNumber(m_value.num) + "hz"; + break; + case CSS_KHZ: + text = formatNumber(m_value.num) + "khz"; + break; + case CSS_TURN: + text = formatNumber(m_value.num) + "turn"; + break; + case CSS_DIMENSION: + // FIXME + break; + case CSS_STRING: + text = quoteCSSStringIfNeeded(m_value.string); + break; + case CSS_URI: + text = "url(" + quoteCSSURLIfNeeded(m_value.string) + ")"; + break; + case CSS_IDENT: + text = valueOrPropertyName(m_value.ident); + break; + case CSS_ATTR: { + DEFINE_STATIC_LOCAL(const String, attrParen, ("attr(")); + + Vector<UChar> result; + result.reserveInitialCapacity(6 + m_value.string->length()); + + append(result, attrParen); + append(result, m_value.string); + result.uncheckedAppend(')'); + + text = String::adopt(result); + break; + } + case CSS_COUNTER: + text = "counter("; + text += String::number(m_value.num); + text += ")"; + // FIXME: Add list-style and separator + break; + case CSS_RECT: { + DEFINE_STATIC_LOCAL(const String, rectParen, ("rect(")); + + Rect* rectVal = getRectValue(); + Vector<UChar> result; + result.reserveInitialCapacity(32); + append(result, rectParen); + + append(result, rectVal->top()->cssText()); + result.append(' '); + + append(result, rectVal->right()->cssText()); + result.append(' '); + + append(result, rectVal->bottom()->cssText()); + result.append(' '); + + append(result, rectVal->left()->cssText()); + result.append(')'); + + text = String::adopt(result); + break; + } + case CSS_RGBCOLOR: + case CSS_PARSER_HEXCOLOR: { + DEFINE_STATIC_LOCAL(const String, commaSpace, (", ")); + DEFINE_STATIC_LOCAL(const String, rgbParen, ("rgb(")); + DEFINE_STATIC_LOCAL(const String, rgbaParen, ("rgba(")); + + RGBA32 rgbColor = m_value.rgbcolor; + if (m_type == CSS_PARSER_HEXCOLOR) + Color::parseHexColor(m_value.string, rgbColor); + Color color(rgbColor); + + Vector<UChar> result; + result.reserveInitialCapacity(32); + if (color.hasAlpha()) + append(result, rgbaParen); + else + append(result, rgbParen); + + appendNumber(result, static_cast<unsigned char>(color.red())); + append(result, commaSpace); + + appendNumber(result, static_cast<unsigned char>(color.green())); + append(result, commaSpace); + + appendNumber(result, static_cast<unsigned char>(color.blue())); + if (color.hasAlpha()) { + append(result, commaSpace); + append(result, String::number(color.alpha() / 256.0f)); + } + + result.append(')'); + text = String::adopt(result); + break; + } + case CSS_PAIR: + text = m_value.pair->first()->cssText(); + text += " "; + text += m_value.pair->second()->cssText(); + break; +#if ENABLE(DASHBOARD_SUPPORT) + case CSS_DASHBOARD_REGION: + for (DashboardRegion* region = getDashboardRegionValue(); region; region = region->m_next.get()) { + if (!text.isEmpty()) + text.append(' '); + text += "dashboard-region("; + text += region->m_label; + if (region->m_isCircle) + text += " circle"; + else if (region->m_isRectangle) + text += " rectangle"; + else + break; + if (region->top()->m_type == CSS_IDENT && region->top()->getIdent() == CSSValueInvalid) { + ASSERT(region->right()->m_type == CSS_IDENT); + ASSERT(region->bottom()->m_type == CSS_IDENT); + ASSERT(region->left()->m_type == CSS_IDENT); + ASSERT(region->right()->getIdent() == CSSValueInvalid); + ASSERT(region->bottom()->getIdent() == CSSValueInvalid); + ASSERT(region->left()->getIdent() == CSSValueInvalid); + } else { + text.append(' '); + text += region->top()->cssText() + " "; + text += region->right()->cssText() + " "; + text += region->bottom()->cssText() + " "; + text += region->left()->cssText(); + } + text += ")"; + } + break; +#endif + case CSS_PARSER_OPERATOR: { + char c = static_cast<char>(m_value.ident); + text = String(&c, 1U); + break; + } + case CSS_PARSER_IDENTIFIER: + text = quoteCSSStringIfNeeded(m_value.string); + break; + } + + ASSERT(!cssTextCache().contains(this)); + cssTextCache().set(this, text); + m_hasCachedCSSText = true; + return text; +} + +CSSParserValue CSSPrimitiveValue::parserValue() const +{ + // We only have to handle a subset of types. + CSSParserValue value; + value.id = 0; + value.isInt = false; + value.unit = CSSPrimitiveValue::CSS_IDENT; + switch (m_type) { + case CSS_NUMBER: + case CSS_PERCENTAGE: + case CSS_EMS: + case CSS_EXS: + case CSS_REMS: + case CSS_PX: + case CSS_CM: + case CSS_MM: + case CSS_IN: + case CSS_PT: + case CSS_PC: + case CSS_DEG: + case CSS_RAD: + case CSS_GRAD: + case CSS_MS: + case CSS_S: + case CSS_HZ: + case CSS_KHZ: + case CSS_DIMENSION: + case CSS_TURN: + value.fValue = m_value.num; + value.unit = m_type; + break; + case CSS_STRING: + case CSS_URI: + case CSS_PARSER_HEXCOLOR: + value.string.characters = const_cast<UChar*>(m_value.string->characters()); + value.string.length = m_value.string->length(); + value.unit = m_type; + break; + case CSS_IDENT: { + value.id = m_value.ident; + const AtomicString& name = valueOrPropertyName(m_value.ident); + value.string.characters = const_cast<UChar*>(name.characters()); + value.string.length = name.length(); + break; + } + case CSS_PARSER_OPERATOR: + value.iValue = m_value.ident; + value.unit = CSSParserValue::Operator; + break; + case CSS_PARSER_INTEGER: + value.fValue = m_value.num; + value.unit = CSSPrimitiveValue::CSS_NUMBER; + value.isInt = true; + break; + case CSS_PARSER_IDENTIFIER: + value.string.characters = const_cast<UChar*>(m_value.string->characters()); + value.string.length = m_value.string->length(); + value.unit = CSSPrimitiveValue::CSS_IDENT; + break; + case CSS_UNKNOWN: + case CSS_ATTR: + case CSS_COUNTER: + case CSS_RECT: + case CSS_RGBCOLOR: + case CSS_PAIR: +#if ENABLE(DASHBOARD_SUPPORT) + case CSS_DASHBOARD_REGION: +#endif + ASSERT_NOT_REACHED(); + break; + } + + return value; +} + +void CSSPrimitiveValue::addSubresourceStyleURLs(ListHashSet<KURL>& urls, const CSSStyleSheet* styleSheet) +{ + if (m_type == CSS_URI) + addSubresourceURL(urls, styleSheet->completeURL(m_value.string)); +} + +} // namespace WebCore |