String CSSPrimitiveValue::customCSSText() const
{
    if (m_hasCachedCSSText) {
        ASSERT(cssTextCache().contains(this));
        return cssTextCache().get(this);
    }

    String text;
    switch (type()) {
    case UnitType::Unknown:
        // FIXME
        break;
    case UnitType::Integer:
        text = String::format("%d", getIntValue());
        break;
    case UnitType::Number:
    case UnitType::Percentage:
    case UnitType::Ems:
    case UnitType::Exs:
    case UnitType::Rems:
    case UnitType::Chs:
    case UnitType::Pixels:
    case UnitType::Centimeters:
    case UnitType::DotsPerPixel:
    case UnitType::DotsPerInch:
    case UnitType::DotsPerCentimeter:
    case UnitType::Millimeters:
    case UnitType::Inches:
    case UnitType::Points:
    case UnitType::Picas:
    case UnitType::Degrees:
    case UnitType::Radians:
    case UnitType::Gradians:
    case UnitType::Milliseconds:
    case UnitType::Seconds:
    case UnitType::Hertz:
    case UnitType::Kilohertz:
    case UnitType::Turns:
    case UnitType::Fraction:
    case UnitType::ViewportWidth:
    case UnitType::ViewportHeight:
    case UnitType::ViewportMin:
    case UnitType::ViewportMax:
        text = formatNumber(m_value.num, unitTypeToString(type()));
        break;
    case UnitType::CustomIdentifier:
        text = quoteCSSStringIfNeeded(m_value.string);
        break;
    case UnitType::String: {
        text = serializeString(m_value.string);
        break;
    }
    case UnitType::URI:
        text = "url(" + quoteCSSURLIfNeeded(m_value.string) + ")";
        break;
    case UnitType::ValueID:
        text = valueName(m_value.valueID);
        break;
    case UnitType::PropertyID:
        text = propertyName(m_value.propertyID);
        break;
    case UnitType::Attribute: {
        StringBuilder result;
        result.reserveCapacity(6 + m_value.string->length());
        result.appendLiteral("attr(");
        result.append(m_value.string);
        result.append(')');

        text = result.toString();
        break;
    }
    case UnitType::Counter: {
        StringBuilder result;
        String separator = m_value.counter->separator();
        if (separator.isEmpty())
            result.appendLiteral("counter(");
        else
            result.appendLiteral("counters(");

        result.append(m_value.counter->identifier());
        if (!separator.isEmpty()) {
            result.appendLiteral(", ");
            result.append(serializeString(separator));
        }
        String listStyle = m_value.counter->listStyle();
        bool isDefaultListStyle = m_value.counter->listStyleIdent() == CSSValueDecimal;
        if (!listStyle.isEmpty() && !isDefaultListStyle) {
            result.appendLiteral(", ");
            result.append(listStyle);
        }
        result.append(')');

        text = result.toString();
        break;
    }
    case UnitType::Rect:
        text = getRectValue()->cssText();
        break;
    case UnitType::Quad:
        text = getQuadValue()->cssText();
        break;
    case UnitType::RGBColor: {
        text = Color(m_value.rgbcolor).serializedAsCSSComponentValue();
        break;
    }
    case UnitType::Pair:
        text = getPairValue()->cssText();
        break;
    case UnitType::Calc:
        text = m_value.calc->customCSSText();
        break;
    case UnitType::Shape:
        text = m_value.shape->cssText();
        break;
    case UnitType::CalcPercentageWithNumber:
    case UnitType::CalcPercentageWithLength:
    case UnitType::QuirkyEms:
        ASSERT_NOT_REACHED();
        break;
    }

    ASSERT(!cssTextCache().contains(this));
    cssTextCache().set(this, text);
    m_hasCachedCSSText = true;
    return text;
}
String CSSPrimitiveValue::customCSSText() const
{
    if (m_hasCachedCSSText) {
        ASSERT(cssTextCache().contains(this));
        return cssTextCache().get(this);
    }

    String text;
    switch (type()) {
    case UnitType::Unknown:
        // FIXME
        break;
    case UnitType::Integer:
        text = String::format("%d", getIntValue());
        break;
    case UnitType::Number:
    case UnitType::Percentage:
    case UnitType::Ems:
    case UnitType::QuirkyEms:
    case UnitType::Exs:
    case UnitType::Rems:
    case UnitType::Chs:
    case UnitType::Pixels:
    case UnitType::Centimeters:
    case UnitType::DotsPerPixel:
    case UnitType::DotsPerInch:
    case UnitType::DotsPerCentimeter:
    case UnitType::Millimeters:
    case UnitType::Inches:
    case UnitType::Points:
    case UnitType::Picas:
    case UnitType::Degrees:
    case UnitType::Radians:
    case UnitType::Gradians:
    case UnitType::Milliseconds:
    case UnitType::Seconds:
    case UnitType::Hertz:
    case UnitType::Kilohertz:
    case UnitType::Turns:
    case UnitType::Fraction:
    case UnitType::ViewportWidth:
    case UnitType::ViewportHeight:
    case UnitType::ViewportMin:
    case UnitType::ViewportMax:
        text = formatNumber(m_value.num, unitTypeToString(type()));
        break;
    case UnitType::CustomIdentifier:
        text = quoteCSSStringIfNeeded(m_value.string);
        break;
    case UnitType::String: {
        text = serializeString(m_value.string);
        break;
    }
    case UnitType::URI:
        text = serializeURI(m_value.string);
        break;
    case UnitType::ValueID:
        text = valueName(m_value.valueID);
        break;
    case UnitType::PropertyID:
        text = propertyName(m_value.propertyID);
        break;
    case UnitType::RGBColor: {
        text = Color(m_value.rgbcolor).serializedAsCSSComponentValue();
        break;
    }
    case UnitType::Calc:
        text = m_value.calc->customCSSText();
        break;
    case UnitType::CalcPercentageWithNumber:
    case UnitType::CalcPercentageWithLength:
        ASSERT_NOT_REACHED();
        break;
    }

    ASSERT(!cssTextCache().contains(this));
    cssTextCache().set(this, text);
    m_hasCachedCSSText = true;
    return text;
}