/** * This file is part of the DOM implementation for KDE. * * (C) 1999-2003 Lars Knoll (knoll@kde.org) * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #include "CSSPrimitiveValue.h" #include "Color.h" #include "Counter.h" #include "CSSValueKeywords.h" #include "DashboardRegion.h" #include "ExceptionCode.h" #include "Pair.h" #include "RenderStyle.h" namespace WebCore { // Quotes the string if it needs quoting. // We use single quotes for now beause markup.cpp uses double quotes. static String quoteStringIfNeeded(const String &string) { // For now, just do this for strings that start with "#" to fix Korean font names that start with "#". // Post-Tiger, we should isLegalIdentifier instead after working out all the ancillary issues. if (string[0] != '#') return string; // FIXME: Also need to transform control characters into \ sequences. String s = string; s.replace('\\', "\\\\"); s.replace('\'', "\\'"); return "'" + s + "'"; } CSSPrimitiveValue::CSSPrimitiveValue() { m_type = 0; } CSSPrimitiveValue::CSSPrimitiveValue(int ident) { m_value.ident = ident; m_type = CSS_IDENT; } CSSPrimitiveValue::CSSPrimitiveValue(double num, UnitTypes type) { m_value.num = num; m_type = type; } CSSPrimitiveValue::CSSPrimitiveValue(const String& str, UnitTypes type) { if ((m_value.string = str.impl())) m_value.string->ref(); m_type = type; } CSSPrimitiveValue::CSSPrimitiveValue(PassRefPtr c) { m_value.counter = c.release(); m_type = CSS_COUNTER; } CSSPrimitiveValue::CSSPrimitiveValue(PassRefPtr r) { m_value.rect = r.release(); m_type = CSS_RECT; } #if __APPLE__ CSSPrimitiveValue::CSSPrimitiveValue(PassRefPtr r) { m_value.region = r.release(); m_type = CSS_DASHBOARD_REGION; } #endif CSSPrimitiveValue::CSSPrimitiveValue(RGBA32 color) { m_value.rgbcolor = color; m_type = CSS_RGBCOLOR; } CSSPrimitiveValue::CSSPrimitiveValue(PassRefPtr p) { m_value.pair = p.release(); m_type = CSS_PAIR; } CSSPrimitiveValue::~CSSPrimitiveValue() { cleanup(); } void CSSPrimitiveValue::cleanup() { switch(m_type) { case CSS_STRING: case CSS_URI: case CSS_ATTR: 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 __APPLE__ case CSS_DASHBOARD_REGION: if (m_value.region) m_value.region->deref(); break; #endif default: break; } m_type = 0; } int CSSPrimitiveValue::computeLengthInt(RenderStyle *style) { double result = computeLengthFloat(style); // This conversion is imprecise, often resulting in values of, e.g., 44.99998. We // need to go ahead and round if we're really close to the next integer value. result += result < 0 ? -0.01 : +0.01; if (result > INT_MAX || result < INT_MIN) return 0; return (int)result; } int CSSPrimitiveValue::computeLengthInt(RenderStyle *style, double multiplier) { double result = multiplier * computeLengthFloat(style); // This conversion is imprecise, often resulting in values of, e.g., 44.99998. We // need to go ahead and round if we're really close to the next integer value. result += result < 0 ? -0.01 : +0.01; if (result > INT_MAX || result < INT_MIN) return 0; return (int)result; } const int intMaxForLength = 0x7ffffff; // max value for a 28-bit int const int intMinForLength = (-0x7ffffff-1); // min value for a 28-bit int // Lengths expect an int that is only 28-bits, so we have to check for a different overflow. int CSSPrimitiveValue::computeLengthIntForLength(RenderStyle *style) { double result = computeLengthFloat(style); // This conversion is imprecise, often resulting in values of, e.g., 44.99998. We // need to go ahead and round if we're really close to the next integer value. result += result < 0 ? -0.01 : +0.01; if (result > intMaxForLength || result < intMinForLength) return 0; return (int)result; } // Lengths expect an int that is only 28-bits, so we have to check for a different overflow. int CSSPrimitiveValue::computeLengthIntForLength(RenderStyle *style, double multiplier) { double result = multiplier * computeLengthFloat(style); // This conversion is imprecise, often resulting in values of, e.g., 44.99998. We // need to go ahead and round if we're really close to the next integer value. result += result < 0 ? -0.01 : +0.01; if (result > intMaxForLength || result < intMinForLength) return 0; return (int)result; } short CSSPrimitiveValue::computeLengthShort(RenderStyle *style) { double result = computeLengthFloat(style); // This conversion is imprecise, often resulting in values of, e.g., 44.99998. We // need to go ahead and round if we're really close to the next integer value. result += result < 0 ? -0.01 : +0.01; if (result > SHRT_MAX || result < SHRT_MIN) return 0; return (short)result; } short CSSPrimitiveValue::computeLengthShort(RenderStyle *style, double multiplier) { double result = multiplier * computeLengthFloat(style); // This conversion is imprecise, often resulting in values of, e.g., 44.99998. We // need to go ahead and round if we're really close to the next integer value. result += result < 0 ? -0.01 : +0.01; if (result > SHRT_MAX || result < SHRT_MIN) return 0; return (short)result; } double CSSPrimitiveValue::computeLengthFloat(RenderStyle *style, bool applyZoomFactor) { unsigned short type = primitiveType(); // We always assume 96 CSS pixels in a CSS inch. This is the cold hard truth of the Web. // At high DPI, we may scale a CSS pixel, but the ratio of the CSS pixel to the so-called // "absolute" CSS length units like inch and pt is always fixed and never changes. double cssPixelsPerInch = 96.; double factor = 1.; switch(type) { case CSS_EMS: factor = applyZoomFactor ? style->fontDescription().computedSize() : style->fontDescription().specifiedSize(); break; case CSS_EXS: { // FIXME: We have a bug right now where the zoom will be applied multiple times to EX units. // We really need to compute EX using fontMetrics for the original specifiedSize and not use // our actual constructed rendering font. factor = style->font().xHeight(); 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.; break; case CSS_PC: // 1 pc == 12 pt factor = cssPixelsPerInch*12./72.; break; default: return -1; } return getFloatValue() * factor; } void CSSPrimitiveValue::setFloatValue( unsigned short unitType, double floatValue, ExceptionCode& ec) { ec = 0; // ### check if property supports this type if (m_type > CSS_DIMENSION) { ec = SYNTAX_ERR; return; } cleanup(); //if(m_type > CSS_DIMENSION) throw DOMException(INVALID_ACCESS_ERR); m_value.num = floatValue; m_type = unitType; } double scaleFactorForConversion(unsigned short unitType) { double cssPixelsPerInch = 96.0; double factor = 1.0; switch(unitType) { case CSSPrimitiveValue::CSS_PX: 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; default: break; } return factor; } double CSSPrimitiveValue::getFloatValue(unsigned short unitType) { if (unitType == m_type || unitType < CSS_PX || unitType > CSS_PC) return m_value.num; double convertedValue = m_value.num; // First convert the value from m_type into CSSPixels double factor = scaleFactorForConversion(m_type); convertedValue *= factor; // Now convert from CSSPixels to the specified unitType factor = scaleFactorForConversion(unitType); convertedValue /= factor; return convertedValue; } void CSSPrimitiveValue::setStringValue( unsigned short stringType, const String &stringValue, ExceptionCode& ec) { ec = 0; //if(m_type < CSS_STRING) throw DOMException(INVALID_ACCESS_ERR); //if(m_type > CSS_ATTR) throw DOMException(INVALID_ACCESS_ERR); if (m_type < CSS_STRING || m_type > CSS_ATTR) { ec = SYNTAX_ERR; return; } cleanup(); if (stringType != CSS_IDENT) { m_value.string = stringValue.impl(); m_value.string->ref(); m_type = stringType; } // ### parse ident } String CSSPrimitiveValue::getStringValue() const { switch (m_type) { case CSS_STRING: case CSS_ATTR: case CSS_URI: return m_value.string; case CSS_IDENT: return getValueName(m_value.ident); default: // FIXME: The CSS 2.1 spec says you should throw an exception here. break; } return String(); } unsigned short CSSPrimitiveValue::cssValueType() const { return CSS_PRIMITIVE_VALUE; } bool CSSPrimitiveValue::parseString( const String &/*string*/, bool ) { // ### return false; } int CSSPrimitiveValue::getIdent() { if(m_type != CSS_IDENT) return 0; return m_value.ident; } String CSSPrimitiveValue::cssText() const { // ### return the original value instead of a generated one (e.g. color // name if it was specified) - check what spec says about this String text; switch ( m_type ) { case CSS_UNKNOWN: // ### break; case CSS_NUMBER: text = String::number(m_value.num); break; case CSS_PERCENTAGE: text = String::number(m_value.num) + "%"; break; case CSS_EMS: text = String::number(m_value.num) + "em"; break; case CSS_EXS: text = String::number(m_value.num) + "ex"; break; case CSS_PX: text = String::number(m_value.num) + "px"; break; case CSS_CM: text = String::number(m_value.num) + "cm"; break; case CSS_MM: text = String::number(m_value.num) + "mm"; break; case CSS_IN: text = String::number(m_value.num) + "in"; break; case CSS_PT: text = String::number(m_value.num) + "pt"; break; case CSS_PC: text = String::number(m_value.num) + "pc"; break; case CSS_DEG: text = String::number(m_value.num) + "deg"; break; case CSS_RAD: text = String::number(m_value.num) + "rad"; break; case CSS_GRAD: text = String::number(m_value.num) + "grad"; break; case CSS_MS: text = String::number(m_value.num) + "ms"; break; case CSS_S: text = String::number(m_value.num) + "s"; break; case CSS_HZ: text = String::number(m_value.num) + "hz"; break; case CSS_KHZ: text = String::number(m_value.num) + "khz"; break; case CSS_DIMENSION: // ### break; case CSS_STRING: text = quoteStringIfNeeded(m_value.string); break; case CSS_URI: text = "url(" + String(m_value.string) + ")"; break; case CSS_IDENT: text = getValueName(m_value.ident); break; case CSS_ATTR: // ### break; case CSS_COUNTER: // ### break; case CSS_RECT: { RectImpl* rectVal = getRectValue(); text = "rect("; text += rectVal->top()->cssText() + " "; text += rectVal->right()->cssText() + " "; text += rectVal->bottom()->cssText() + " "; text += rectVal->left()->cssText() + ")"; break; } case CSS_RGBCOLOR: { Color color(m_value.rgbcolor); if (color.alpha() < 0xFF) text = "rgba("; else text = "rgb("; text += String::number(color.red()) + ", "; text += String::number(color.green()) + ", "; text += String::number(color.blue()); if (color.alpha() < 0xFF) text += ", " + String::number((float)color.alpha() / 0xFF); text += ")"; break; } case CSS_PAIR: text = m_value.pair->first()->cssText(); text += " "; text += m_value.pair->second()->cssText(); break; #if __APPLE__ case CSS_DASHBOARD_REGION: for (DashboardRegion* region = getDashboardRegionValue(); region; region = region->m_next.get()) { text = "dashboard-region("; text += region->m_label; if (region->m_isCircle) text += " circle "; else if (region->m_isRectangle) text += " rectangle "; else break; text += region->top()->cssText() + " "; text += region->right()->cssText() + " "; text += region->bottom()->cssText() + " "; text += region->left()->cssText(); text += ")"; } break; #endif } return text; } }