blob: 2a1a0041bfc864e70f3d089c94185a246e2a492a [file] [log] [blame]
/**
* 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 "css_valueimpl.h"
#include "Cache.h"
#include "CachedImage.h"
#include "DocLoader.h"
#include "Document.h"
#include "ExceptionCode.h"
#include "HTMLElement.h"
#include "css_stylesheetimpl.h"
#include "cssparser.h"
#include "CSSPropertyNames.h"
#include "cssstyleselector.h"
#include "CSSValueKeywords.h"
#include "RegularExpression.h"
#if SVG_SUPPORT
#include "ksvgcssproperties.h"
#endif
// Not in any header, so just declare it here for now.
WebCore::String getPropertyName(unsigned short id);
namespace WebCore {
// Defined in CSSGrammar.y, but not in any header, so just declare it here for now.
int getPropertyID(const char* str, int len);
static int propertyID(const String& s)
{
char buffer[maxCSSPropertyNameLength];
unsigned len = s.length();
if (len > maxCSSPropertyNameLength)
return 0;
for (unsigned i = 0; i != len; ++i) {
UChar c = s[i];
if (c == 0 || c >= 0x7F)
return 0; // illegal character
buffer[i] = c;
}
int id = getPropertyID(buffer, len);
#if SVG_SUPPORT
if (id == 0)
id = SVG::getSVGCSSPropertyID(buffer, len);
#endif
return id;
}
// 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 + "'";
}
CSSStyleDeclaration::CSSStyleDeclaration(CSSRule *parent)
: StyleBase(parent)
{
}
bool CSSStyleDeclaration::isStyleDeclaration()
{
return true;
}
PassRefPtr<CSSValue> CSSStyleDeclaration::getPropertyCSSValue(const String& propertyName)
{
int propID = propertyID(propertyName);
if (!propID)
return 0;
return getPropertyCSSValue(propID);
}
String CSSStyleDeclaration::getPropertyValue(const String &propertyName)
{
int propID = propertyID(propertyName);
if (!propID)
return String();
return getPropertyValue(propID);
}
String CSSStyleDeclaration::getPropertyPriority(const String &propertyName)
{
int propID = propertyID(propertyName);
if (!propID)
return String();
return getPropertyPriority(propID) ? "important" : "";
}
String CSSStyleDeclaration::getPropertyShorthand(const String &propertyName)
{
int propID = propertyID(propertyName);
if (!propID)
return String();
int shorthandID = getPropertyShorthand(propID);
if (!shorthandID)
return String();
return getPropertyName(shorthandID);
}
bool CSSStyleDeclaration::isPropertyImplicit(const String &propertyName)
{
int propID = propertyID(propertyName);
if (!propID)
return false;
return isPropertyImplicit(propID);
}
void CSSStyleDeclaration::setProperty(const String& propertyName, const String& value, const String& priority, ExceptionCode& ec)
{
int propID = propertyID(propertyName);
if (!propID) // set exception?
return;
bool important = priority.find("important", 0, false) != -1;
setProperty(propID, value, important, ec);
}
String CSSStyleDeclaration::removeProperty(const String& propertyName, ExceptionCode& ec)
{
int propID = propertyID(propertyName);
if (!propID)
return String();
return removeProperty(propID, ec);
}
bool CSSStyleDeclaration::isPropertyName(const String &propertyName)
{
return propertyID(propertyName);
}
// --------------------------------------------------------------------------------------
CSSMutableStyleDeclaration::CSSMutableStyleDeclaration()
: m_node(0)
{
}
CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule *parent)
: CSSStyleDeclaration(parent), m_node(0)
{
}
CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule *parent, const DeprecatedValueList<CSSProperty> &values)
: CSSStyleDeclaration(parent), m_values(values), m_node(0)
{
// FIXME: This allows duplicate properties.
}
CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule *parent, const CSSProperty * const *properties, int numProperties)
: CSSStyleDeclaration(parent), m_node(0)
{
for (int i = 0; i < numProperties; ++i)
m_values.append(*properties[i]);
// FIXME: This allows duplicate properties.
}
CSSMutableStyleDeclaration& CSSMutableStyleDeclaration::operator=(const CSSMutableStyleDeclaration& o)
{
// don't attach it to the same node, just leave the current m_node value
m_values = o.m_values;
return *this;
}
String CSSMutableStyleDeclaration::getPropertyValue(int propertyID) const
{
RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
if (value)
return value->cssText();
// Shorthand and 4-values properties
switch ( propertyID ) {
case CSS_PROP_BACKGROUND_POSITION:
{
// ## Is this correct? The code in cssparser.cpp is confusing
const int properties[2] = { CSS_PROP_BACKGROUND_POSITION_X,
CSS_PROP_BACKGROUND_POSITION_Y };
return getShortHandValue( properties, 2 );
}
case CSS_PROP_BACKGROUND:
{
const int properties[5] = { CSS_PROP_BACKGROUND_IMAGE, CSS_PROP_BACKGROUND_REPEAT,
CSS_PROP_BACKGROUND_ATTACHMENT, CSS_PROP_BACKGROUND_POSITION,
CSS_PROP_BACKGROUND_COLOR };
return getShortHandValue( properties, 5 );
}
case CSS_PROP_BORDER:
{
const int properties[3] = { CSS_PROP_BORDER_WIDTH, CSS_PROP_BORDER_STYLE,
CSS_PROP_BORDER_COLOR };
return getShortHandValue( properties, 3 );
}
case CSS_PROP_BORDER_TOP:
{
const int properties[3] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_TOP_STYLE,
CSS_PROP_BORDER_TOP_COLOR};
return getShortHandValue( properties, 3 );
}
case CSS_PROP_BORDER_RIGHT:
{
const int properties[3] = { CSS_PROP_BORDER_RIGHT_WIDTH, CSS_PROP_BORDER_RIGHT_STYLE,
CSS_PROP_BORDER_RIGHT_COLOR};
return getShortHandValue( properties, 3 );
}
case CSS_PROP_BORDER_BOTTOM:
{
const int properties[3] = { CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_BOTTOM_STYLE,
CSS_PROP_BORDER_BOTTOM_COLOR};
return getShortHandValue( properties, 3 );
}
case CSS_PROP_BORDER_LEFT:
{
const int properties[3] = { CSS_PROP_BORDER_LEFT_WIDTH, CSS_PROP_BORDER_LEFT_STYLE,
CSS_PROP_BORDER_LEFT_COLOR};
return getShortHandValue( properties, 3 );
}
case CSS_PROP_OUTLINE:
{
const int properties[3] = { CSS_PROP_OUTLINE_WIDTH, CSS_PROP_OUTLINE_STYLE,
CSS_PROP_OUTLINE_COLOR };
return getShortHandValue( properties, 3 );
}
case CSS_PROP_BORDER_COLOR:
{
const int properties[4] = { CSS_PROP_BORDER_TOP_COLOR, CSS_PROP_BORDER_RIGHT_COLOR,
CSS_PROP_BORDER_BOTTOM_COLOR, CSS_PROP_BORDER_LEFT_COLOR };
return get4Values( properties );
}
case CSS_PROP_BORDER_WIDTH:
{
const int properties[4] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_RIGHT_WIDTH,
CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_LEFT_WIDTH };
return get4Values( properties );
}
case CSS_PROP_BORDER_STYLE:
{
const int properties[4] = { CSS_PROP_BORDER_TOP_STYLE, CSS_PROP_BORDER_RIGHT_STYLE,
CSS_PROP_BORDER_BOTTOM_STYLE, CSS_PROP_BORDER_LEFT_STYLE };
return get4Values( properties );
}
case CSS_PROP_MARGIN:
{
const int properties[4] = { CSS_PROP_MARGIN_TOP, CSS_PROP_MARGIN_RIGHT,
CSS_PROP_MARGIN_BOTTOM, CSS_PROP_MARGIN_LEFT };
return get4Values( properties );
}
case CSS_PROP_PADDING:
{
const int properties[4] = { CSS_PROP_PADDING_TOP, CSS_PROP_PADDING_RIGHT,
CSS_PROP_PADDING_BOTTOM, CSS_PROP_PADDING_LEFT };
return get4Values( properties );
}
case CSS_PROP_LIST_STYLE:
{
const int properties[3] = { CSS_PROP_LIST_STYLE_TYPE, CSS_PROP_LIST_STYLE_POSITION,
CSS_PROP_LIST_STYLE_IMAGE };
return getShortHandValue( properties, 3 );
}
}
return String();
}
String CSSMutableStyleDeclaration::get4Values( const int* properties ) const
{
String res;
for (int i = 0; i < 4; ++i) {
if (!isPropertyImplicit(properties[i])) {
RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
if (!value) // apparently all 4 properties must be specified.
return String();
if (!res.isNull())
res += " ";
res += value->cssText();
}
}
return res;
}
String CSSMutableStyleDeclaration::getShortHandValue( const int* properties, int number ) const
{
String res;
for (int i = 0; i < number; ++i) {
if (!isPropertyImplicit(properties[i])) {
RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
if (value) { // TODO provide default value if !value
if (!res.isNull())
res += " ";
res += value->cssText();
}
}
}
return res;
}
PassRefPtr<CSSValue> CSSMutableStyleDeclaration::getPropertyCSSValue(int propertyID) const
{
DeprecatedValueListConstIterator<CSSProperty> end;
for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.fromLast(); it != end; --it)
if (propertyID == (*it).m_id)
return (*it).value();
return 0;
}
String CSSMutableStyleDeclaration::removeProperty(int propertyID, bool notifyChanged, ExceptionCode& ec)
{
ec = 0;
String value;
DeprecatedValueListIterator<CSSProperty> end;
for (DeprecatedValueListIterator<CSSProperty> it = m_values.fromLast(); it != end; --it)
if (propertyID == (*it).m_id) {
value = (*it).value()->cssText();
m_values.remove(it);
if (notifyChanged)
setChanged();
break;
}
return value;
}
void CSSMutableStyleDeclaration::clear()
{
m_values.clear();
setChanged();
}
void CSSMutableStyleDeclaration::setChanged()
{
if (m_node) {
m_node->setChanged();
// FIXME: Ideally, this should be factored better and there
// should be a subclass of CSSMutableStyleDeclaration just
// for inline style declarations that handles this
if (m_node->isStyledElement() && this == static_cast<StyledElement *>(m_node)->inlineStyleDecl())
static_cast<StyledElement *>(m_node)->invalidateStyleAttribute();
return;
}
// ### quick&dirty hack for KDE 3.0... make this MUCH better! (Dirk)
StyleBase *root = this;
while (StyleBase *parent = root->parent())
root = parent;
if (root->isCSSStyleSheet())
static_cast<CSSStyleSheet*>(root)->doc()->updateStyleSelector();
}
bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const
{
DeprecatedValueListConstIterator<CSSProperty> end;
for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it)
if (propertyID == (*it).id())
return (*it).isImportant();
return false;
}
int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const
{
DeprecatedValueListConstIterator<CSSProperty> end;
for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it)
if (propertyID == (*it).id())
return (*it).shorthandID();
return false;
}
bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const
{
DeprecatedValueListConstIterator<CSSProperty> end;
for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it)
if (propertyID == (*it).id())
return (*it).isImplicit();
return false;
}
void CSSMutableStyleDeclaration::setProperty(int propertyID, const String &value, bool important, ExceptionCode& ec)
{
setProperty(propertyID, value, important, true, ec);
}
String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec)
{
return removeProperty(propertyID, true, ec);
}
bool CSSMutableStyleDeclaration::setProperty(int propertyID, const String &value, bool important, bool notifyChanged, ExceptionCode& ec)
{
ec = 0;
removeProperty(propertyID);
CSSParser parser(useStrictParsing());
bool success = parser.parseValue(this, propertyID, value, important);
if (!success) {
ec = SYNTAX_ERR;
} else if (notifyChanged)
setChanged();
return success;
}
bool CSSMutableStyleDeclaration::setProperty(int propertyID, int value, bool important, bool notifyChanged)
{
removeProperty(propertyID);
m_values.append(CSSProperty(propertyID, new CSSPrimitiveValue(value), important));
if (notifyChanged)
setChanged();
return true;
}
void CSSMutableStyleDeclaration::setStringProperty(int propertyId, const String &value, CSSPrimitiveValue::UnitTypes type, bool important)
{
removeProperty(propertyId);
m_values.append(CSSProperty(propertyId, new CSSPrimitiveValue(value, type), important));
setChanged();
}
void CSSMutableStyleDeclaration::setImageProperty(int propertyId, const String &URL, bool important)
{
removeProperty(propertyId);
m_values.append(CSSProperty(propertyId, new CSSImageValue(URL, this), important));
setChanged();
}
void CSSMutableStyleDeclaration::parseDeclaration(const String &styleDeclaration)
{
m_values.clear();
CSSParser parser(useStrictParsing());
parser.parseDeclaration(this, styleDeclaration);
setChanged();
}
void CSSMutableStyleDeclaration::addParsedProperties(const CSSProperty * const *properties, int numProperties)
{
for (int i = 0; i < numProperties; ++i) {
removeProperty(properties[i]->id(), false);
m_values.append(*properties[i]);
}
// FIXME: This probably should have a call to setChanged() if something changed. We may also wish to add
// a notifyChanged argument to this function to follow the model of other functions in this class.
}
void CSSMutableStyleDeclaration::setLengthProperty(int id, const String &value, bool important, bool /* multiLength*/)
{
bool parseMode = useStrictParsing();
setStrictParsing(false);
setProperty(id, value, important);
setStrictParsing(parseMode);
}
unsigned CSSMutableStyleDeclaration::length() const
{
return m_values.count();
}
String CSSMutableStyleDeclaration::item(unsigned i) const
{
if (i >= m_values.count())
return String();
return getPropertyName(m_values[i].id());
}
CSSRule *CSSStyleDeclaration::parentRule() const
{
return (parent() && parent()->isRule()) ? static_cast<CSSRule *>(parent()) : 0;
}
String CSSMutableStyleDeclaration::cssText() const
{
String result = "";
DeprecatedValueListConstIterator<CSSProperty> end;
for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it)
result += (*it).cssText();
return result;
}
void CSSMutableStyleDeclaration::setCssText(const String& text, ExceptionCode& ec)
{
ec = 0;
m_values.clear();
CSSParser parser(useStrictParsing());
parser.parseDeclaration(this, text);
// FIXME: Detect syntax errors and set ec.
setChanged();
}
void CSSMutableStyleDeclaration::merge(CSSMutableStyleDeclaration *other, bool argOverridesOnConflict)
{
DeprecatedValueListConstIterator<CSSProperty> end;
for (DeprecatedValueListConstIterator<CSSProperty> it = other->valuesIterator(); it != end; ++it) {
const CSSProperty &property = *it;
RefPtr<CSSValue> value = getPropertyCSSValue(property.id());
if (value) {
if (!argOverridesOnConflict)
continue;
removeProperty(property.id());
}
m_values.append(property);
}
// FIXME: This probably should have a call to setChanged() if something changed. We may also wish to add
// a notifyChanged argument to this function to follow the model of other functions in this class.
}
void CSSStyleDeclaration::diff(CSSMutableStyleDeclaration *style) const
{
if (!style)
return;
DeprecatedValueList<int> properties;
DeprecatedValueListConstIterator<CSSProperty> end;
for (DeprecatedValueListConstIterator<CSSProperty> it(style->valuesIterator()); it != end; ++it) {
const CSSProperty &property = *it;
RefPtr<CSSValue> value = getPropertyCSSValue(property.id());
if (value && (value->cssText() == property.value()->cssText()))
properties.append(property.id());
}
for (DeprecatedValueListIterator<int> it(properties.begin()); it != properties.end(); ++it)
style->removeProperty(*it);
}
// This is the list of properties we want to copy in the copyInheritableProperties() function.
// It is the intersection of the list of inherited CSS properties and the
// properties for which we have a computed implementation in this file.
const int inheritableProperties[] = {
CSS_PROP_BORDER_COLLAPSE,
CSS_PROP_BORDER_SPACING,
CSS_PROP_COLOR,
CSS_PROP_FONT_FAMILY,
CSS_PROP_FONT_SIZE,
CSS_PROP_FONT_STYLE,
CSS_PROP_FONT_VARIANT,
CSS_PROP_FONT_WEIGHT,
CSS_PROP_LETTER_SPACING,
CSS_PROP_LINE_HEIGHT,
CSS_PROP_TEXT_ALIGN,
CSS_PROP__WEBKIT_TEXT_DECORATIONS_IN_EFFECT,
CSS_PROP_TEXT_INDENT,
CSS_PROP__WEBKIT_TEXT_SIZE_ADJUST,
CSS_PROP_TEXT_TRANSFORM,
CSS_PROP_ORPHANS,
CSS_PROP_WHITE_SPACE,
CSS_PROP_WIDOWS,
CSS_PROP_WORD_SPACING,
};
const unsigned numInheritableProperties = sizeof(inheritableProperties) / sizeof(inheritableProperties[0]);
// This is the list of properties we want to copy in the copyBlockProperties() function.
// It is the list of CSS properties that apply specially to block-level elements.
static const int blockProperties[] = {
CSS_PROP_ORPHANS,
CSS_PROP_OVERFLOW, // This can be also be applied to replaced elements
CSS_PROP_PAGE_BREAK_AFTER,
CSS_PROP_PAGE_BREAK_BEFORE,
CSS_PROP_PAGE_BREAK_INSIDE,
CSS_PROP_TEXT_ALIGN,
CSS_PROP_TEXT_INDENT,
CSS_PROP_WIDOWS
};
const unsigned numBlockProperties = sizeof(blockProperties) / sizeof(blockProperties[0]);
PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copyBlockProperties() const
{
return copyPropertiesInSet(blockProperties, numBlockProperties);
}
void CSSMutableStyleDeclaration::removeBlockProperties()
{
removePropertiesInSet(blockProperties, numBlockProperties);
}
void CSSMutableStyleDeclaration::removeInheritableProperties()
{
removePropertiesInSet(inheritableProperties, numInheritableProperties);
}
PassRefPtr<CSSMutableStyleDeclaration> CSSStyleDeclaration::copyPropertiesInSet(const int *set, unsigned length) const
{
DeprecatedValueList<CSSProperty> list;
for (unsigned i = 0; i < length; i++) {
RefPtr<CSSValue> value = getPropertyCSSValue(set[i]);
if (value)
list.append(CSSProperty(set[i], value.release(), false));
}
return new CSSMutableStyleDeclaration(0, list);
}
void CSSMutableStyleDeclaration::removePropertiesInSet(const int *set, unsigned length)
{
bool changed = false;
for (unsigned i = 0; i < length; i++) {
RefPtr<CSSValue> value = getPropertyCSSValue(set[i]);
if (value) {
m_values.remove(CSSProperty(set[i], value.release(), false));
changed = true;
}
}
if (changed)
setChanged();
}
PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable()
{
return this;
}
PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const
{
return new CSSMutableStyleDeclaration(0, m_values);
}
// --------------------------------------------------------------------------------------
unsigned short CSSInheritedValue::cssValueType() const
{
return CSS_INHERIT;
}
String CSSInheritedValue::cssText() const
{
return "inherit";
}
unsigned short CSSInitialValue::cssValueType() const
{
return CSS_INITIAL;
}
String CSSInitialValue::cssText() const
{
return "initial";
}
// ----------------------------------------------------------------------------------------
CSSValueList::~CSSValueList()
{
for (CSSValue *val = m_values.first(); val; val = m_values.next())
val->deref();
}
unsigned short CSSValueList::cssValueType() const
{
return CSS_VALUE_LIST;
}
void CSSValueList::append(PassRefPtr<CSSValue> val)
{
m_values.append(val.release());
}
String CSSValueList::cssText() const
{
String result = "";
for (DeprecatedPtrListIterator<CSSValue> iterator(m_values); iterator.current(); ++iterator) {
if (!result.isEmpty())
result += ", ";
result += iterator.current()->cssText();
}
return result;
}
// -------------------------------------------------------------------------------------
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<Counter> c)
{
m_value.counter = c.release();
m_type = CSS_COUNTER;
}
CSSPrimitiveValue::CSSPrimitiveValue(PassRefPtr<RectImpl> r)
{
m_value.rect = r.release();
m_type = CSS_RECT;
}
#if __APPLE__
CSSPrimitiveValue::CSSPrimitiveValue(PassRefPtr<DashboardRegion> 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<Pair> 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;
cleanup();
// ### check if property supports this type
if (m_type > CSS_DIMENSION) {
ec = SYNTAX_ERR;
return;
}
//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;
cleanup();
//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;
}
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;
}
// -----------------------------------------------------------------
RectImpl::~RectImpl()
{
}
// -----------------------------------------------------------------
Pair::~Pair()
{
}
// -----------------------------------------------------------------
CSSImageValue::CSSImageValue(const String& url, StyleBase *style)
: CSSPrimitiveValue(url, CSS_URI), m_image(0), m_accessedImage(false)
{
}
CSSImageValue::CSSImageValue()
: CSSPrimitiveValue(CSS_VAL_NONE), m_image(0), m_accessedImage(true)
{
}
CSSImageValue::~CSSImageValue()
{
if (m_image)
m_image->deref(this);
}
CachedImage* CSSImageValue::image(DocLoader* loader)
{
if (!m_accessedImage) {
m_accessedImage = true;
if (loader)
m_image = loader->requestImage(getStringValue());
else
m_image = Cache::requestImage(0, getStringValue());
if (m_image)
m_image->ref(this);
}
return m_image;
}
// ------------------------------------------------------------------------
CSSBorderImageValue::CSSBorderImageValue(PassRefPtr<CSSImageValue> image,
PassRefPtr<RectImpl> imageRect, int horizontalRule, int verticalRule)
: m_image(image), m_imageSliceRect(imageRect)
, m_horizontalSizeRule(horizontalRule), m_verticalSizeRule(verticalRule)
{
}
String CSSBorderImageValue::cssText() const
{
// Image first.
String text(m_image->cssText());
text += " ";
// Now the rect, but it isn't really a rect, so we dump manually
text += m_imageSliceRect->top()->cssText();
text += " ";
text += m_imageSliceRect->right()->cssText();
text += " ";
text += m_imageSliceRect->bottom()->cssText();
text += " ";
text += m_imageSliceRect->left()->cssText();
// Now the keywords.
text += " ";
text += CSSPrimitiveValue(m_horizontalSizeRule).cssText();
text += " ";
text += CSSPrimitiveValue(m_verticalSizeRule).cssText();
return text;
}
// ------------------------------------------------------------------------
FontFamilyValue::FontFamilyValue(const DeprecatedString& string)
: CSSPrimitiveValue(String(), CSS_STRING)
{
static const RegularExpression parenReg(" \\(.*\\)$");
static const RegularExpression braceReg(" \\[.*\\]$");
parsedFontName = string;
// a language tag is often added in braces at the end. Remove it.
parsedFontName.replace(parenReg, "");
// remove [Xft] qualifiers
parsedFontName.replace(braceReg, "");
}
String FontFamilyValue::cssText() const
{
return quoteStringIfNeeded(parsedFontName);
}
String FontValue::cssText() const
{
// font variant weight size / line-height family
String result("");
if (style) {
result += style->cssText();
}
if (variant) {
if (!result.isEmpty())
result += " ";
result += variant->cssText();
}
if (weight) {
if (!result.isEmpty())
result += " ";
result += weight->cssText();
}
if (size) {
if (!result.isEmpty())
result += " ";
result += size->cssText();
}
if (lineHeight) {
if (!size)
result += " ";
result += "/";
result += lineHeight->cssText();
}
if (family) {
if (!result.isEmpty())
result += " ";
result += family->cssText();
}
return result;
}
// Used for text-shadow and box-shadow
ShadowValue::ShadowValue(PassRefPtr<CSSPrimitiveValue> _x,
PassRefPtr<CSSPrimitiveValue> _y,
PassRefPtr<CSSPrimitiveValue> _blur,
PassRefPtr<CSSPrimitiveValue> _color)
: x(_x), y(_y), blur(_blur), color(_color)
{
}
String ShadowValue::cssText() const
{
String text("");
if (color)
text += color->cssText();
if (x) {
if (!text.isEmpty())
text += " ";
text += x->cssText();
}
if (y) {
if (!text.isEmpty())
text += " ";
text += y->cssText();
}
if (blur) {
if (!text.isEmpty())
text += " ";
text += blur->cssText();
}
return text;
}
String CSSProperty::cssText() const
{
return getPropertyName(id()) + ": " + m_value->cssText()
+ (isImportant() ? " !important" : "") + "; ";
}
bool operator==(const CSSProperty &a, const CSSProperty &b)
{
return a.m_id == b.m_id && a.m_important == b.m_important && a.m_value == b.m_value;
}
}