diff options
Diffstat (limited to 'WebCore/css/CSSMutableStyleDeclaration.cpp')
-rw-r--r-- | WebCore/css/CSSMutableStyleDeclaration.cpp | 729 |
1 files changed, 729 insertions, 0 deletions
diff --git a/WebCore/css/CSSMutableStyleDeclaration.cpp b/WebCore/css/CSSMutableStyleDeclaration.cpp new file mode 100644 index 0000000..bc354ff --- /dev/null +++ b/WebCore/css/CSSMutableStyleDeclaration.cpp @@ -0,0 +1,729 @@ +/* + * (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 "CSSMutableStyleDeclaration.h" + +#include "CSSImageValue.h" +#include "CSSParser.h" +#include "CSSProperty.h" +#include "CSSPropertyNames.h" +#include "CSSStyleSheet.h" +#include "CSSValueList.h" +#include "Document.h" +#include "ExceptionCode.h" +#include "StyledElement.h" + +using namespace std; + +namespace WebCore { + +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) { + ASSERT(properties[i]); + m_values.append(*properties[i]); + } + // FIXME: This allows duplicate properties. +} + +CSSMutableStyleDeclaration& CSSMutableStyleDeclaration::operator=(const CSSMutableStyleDeclaration& other) +{ + // don't attach it to the same node, just leave the current m_node value + m_values = other.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: { + // FIXME: 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 getLayeredShorthandValue(properties, 2); + } + case CSS_PROP_BACKGROUND: { + const int properties[6] = { CSS_PROP_BACKGROUND_IMAGE, CSS_PROP_BACKGROUND_REPEAT, + CSS_PROP_BACKGROUND_ATTACHMENT, CSS_PROP_BACKGROUND_POSITION_X, + CSS_PROP_BACKGROUND_POSITION_Y, CSS_PROP_BACKGROUND_COLOR }; + return getLayeredShorthandValue(properties, 6); + } + 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]); + + // apparently all 4 properties must be specified. + if (!value) + return String(); + + if (!res.isNull()) + res += " "; + res += value->cssText(); + } + } + return res; +} + +String CSSMutableStyleDeclaration::getLayeredShorthandValue(const int* properties, unsigned number) const +{ + String res; + unsigned i; + unsigned j; + + // Begin by collecting the properties into an array. + Vector< RefPtr<CSSValue> > values(number); + unsigned numLayers = 0; + + for (i = 0; i < number; ++i) { + values[i] = getPropertyCSSValue(properties[i]); + if (values[i]) { + if (values[i]->isValueList()) { + CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get()); + numLayers = max(valueList->length(), numLayers); + } else + numLayers = max(1U, numLayers); + } + } + + // Now stitch the properties together. Implicit initial values are flagged as such and + // can safely be omitted. + for (i = 0; i < numLayers; i++) { + String layerRes; + for (j = 0; j < number; j++) { + RefPtr<CSSValue> value; + if (values[j]) { + if (values[j]->isValueList()) + value = static_cast<CSSValueList*>(values[j].get())->item(i); + else { + value = values[j]; + + // Color only belongs in the last layer. + if (properties[j] == CSS_PROP_BACKGROUND_COLOR) { + if (i != numLayers - 1) + value = 0; + } else if (i != 0) // Other singletons only belong in the first layer. + value = 0; + } + } + + if (value && !value->isImplicitInitialValue()) { + if (!layerRes.isNull()) + layerRes += " "; + layerRes += value->cssText(); + } + } + + if (!layerRes.isNull()) { + if (!res.isNull()) + res += ", "; + res += layerRes; + } + } + + 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]); + // FIXME: provide default value if !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; +} + +struct PropertyLonghand { + PropertyLonghand() + : m_properties(0) + , m_length(0) + { + } + + PropertyLonghand(const int* firstProperty, unsigned numProperties) + : m_properties(firstProperty) + , m_length(numProperties) + { + } + + const int* properties() const { return m_properties; } + unsigned length() const { return m_length; } + +private: + const int* m_properties; + unsigned m_length; +}; + +static void initShorthandMap(HashMap<int, PropertyLonghand>& shorthandMap) +{ + #define SET_SHORTHAND_MAP_ENTRY(map, propID, array) \ + map.set(propID, PropertyLonghand(array, sizeof(array) / sizeof(array[0]))) + + // FIXME: The 'font' property has "shorthand nature" but is not parsed as a shorthand. + + // Do not change the order of the following four shorthands, and keep them together. + static const int borderProperties[4][3] = { + { CSS_PROP_BORDER_TOP_COLOR, CSS_PROP_BORDER_TOP_STYLE, CSS_PROP_BORDER_TOP_WIDTH }, + { CSS_PROP_BORDER_RIGHT_COLOR, CSS_PROP_BORDER_RIGHT_STYLE, CSS_PROP_BORDER_RIGHT_WIDTH }, + { CSS_PROP_BORDER_BOTTOM_COLOR, CSS_PROP_BORDER_BOTTOM_STYLE, CSS_PROP_BORDER_BOTTOM_WIDTH }, + { CSS_PROP_BORDER_LEFT_COLOR, CSS_PROP_BORDER_LEFT_STYLE, CSS_PROP_BORDER_LEFT_WIDTH } + }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_TOP, borderProperties[0]); + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_RIGHT, borderProperties[1]); + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_BOTTOM, borderProperties[2]); + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_LEFT, borderProperties[3]); + + shorthandMap.set(CSS_PROP_BORDER, PropertyLonghand(borderProperties[0], sizeof(borderProperties) / sizeof(borderProperties[0][0]))); + + static const int borderColorProperties[] = { + CSS_PROP_BORDER_TOP_COLOR, + CSS_PROP_BORDER_RIGHT_COLOR, + CSS_PROP_BORDER_BOTTOM_COLOR, + CSS_PROP_BORDER_LEFT_COLOR + }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_COLOR, borderColorProperties); + + static const int borderStyleProperties[] = { + CSS_PROP_BORDER_TOP_STYLE, + CSS_PROP_BORDER_RIGHT_STYLE, + CSS_PROP_BORDER_BOTTOM_STYLE, + CSS_PROP_BORDER_LEFT_STYLE + }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_STYLE, borderStyleProperties); + + static const int borderWidthProperties[] = { + CSS_PROP_BORDER_TOP_WIDTH, + CSS_PROP_BORDER_RIGHT_WIDTH, + CSS_PROP_BORDER_BOTTOM_WIDTH, + CSS_PROP_BORDER_LEFT_WIDTH + }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_WIDTH, borderWidthProperties); + + static const int backgroundPositionProperties[] = { CSS_PROP_BACKGROUND_POSITION_X, CSS_PROP_BACKGROUND_POSITION_Y }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BACKGROUND_POSITION, backgroundPositionProperties); + + static const int borderSpacingProperties[] = { CSS_PROP__WEBKIT_BORDER_HORIZONTAL_SPACING, CSS_PROP__WEBKIT_BORDER_VERTICAL_SPACING }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_SPACING, borderSpacingProperties); + + static const int listStyleProperties[] = { + CSS_PROP_LIST_STYLE_IMAGE, + CSS_PROP_LIST_STYLE_POSITION, + CSS_PROP_LIST_STYLE_TYPE + }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_LIST_STYLE, listStyleProperties); + + static const int marginProperties[] = { + CSS_PROP_MARGIN_TOP, + CSS_PROP_MARGIN_RIGHT, + CSS_PROP_MARGIN_BOTTOM, + CSS_PROP_MARGIN_LEFT + }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_MARGIN, marginProperties); + + static const int marginCollapseProperties[] = { CSS_PROP__WEBKIT_MARGIN_TOP_COLLAPSE, CSS_PROP__WEBKIT_MARGIN_BOTTOM_COLLAPSE }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__WEBKIT_MARGIN_COLLAPSE, marginCollapseProperties); + + static const int marqueeProperties[] = { + CSS_PROP__WEBKIT_MARQUEE_DIRECTION, + CSS_PROP__WEBKIT_MARQUEE_INCREMENT, + CSS_PROP__WEBKIT_MARQUEE_REPETITION, + CSS_PROP__WEBKIT_MARQUEE_STYLE, + CSS_PROP__WEBKIT_MARQUEE_SPEED + }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__WEBKIT_MARQUEE, marqueeProperties); + + static const int outlineProperties[] = { + CSS_PROP_OUTLINE_COLOR, + CSS_PROP_OUTLINE_OFFSET, + CSS_PROP_OUTLINE_STYLE, + CSS_PROP_OUTLINE_WIDTH + }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_OUTLINE, outlineProperties); + + static const int paddingProperties[] = { + CSS_PROP_PADDING_TOP, + CSS_PROP_PADDING_RIGHT, + CSS_PROP_PADDING_BOTTOM, + CSS_PROP_PADDING_LEFT + }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_PADDING, paddingProperties); + + static const int textStrokeProperties[] = { CSS_PROP__WEBKIT_TEXT_STROKE_COLOR, CSS_PROP__WEBKIT_TEXT_STROKE_WIDTH }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__WEBKIT_TEXT_STROKE, textStrokeProperties); + + static const int backgroundProperties[] = { + CSS_PROP_BACKGROUND_ATTACHMENT, + CSS_PROP_BACKGROUND_COLOR, + CSS_PROP_BACKGROUND_IMAGE, + CSS_PROP_BACKGROUND_POSITION_X, + CSS_PROP_BACKGROUND_POSITION_Y, + CSS_PROP_BACKGROUND_REPEAT, + CSS_PROP__WEBKIT_BACKGROUND_SIZE + }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BACKGROUND, backgroundProperties); + + static const int columnsProperties[] = { CSS_PROP__WEBKIT_COLUMN_WIDTH, CSS_PROP__WEBKIT_COLUMN_COUNT }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__WEBKIT_COLUMNS, columnsProperties); + + static const int columnRuleProperties[] = { + CSS_PROP__WEBKIT_COLUMN_RULE_COLOR, + CSS_PROP__WEBKIT_COLUMN_RULE_STYLE, + CSS_PROP__WEBKIT_COLUMN_RULE_WIDTH + }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__WEBKIT_COLUMN_RULE, columnRuleProperties); + + static const int overflowProperties[] = { CSS_PROP_OVERFLOW_X, CSS_PROP_OVERFLOW_Y }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_OVERFLOW, overflowProperties); + + static const int borderRadiusProperties[] = { + CSS_PROP__WEBKIT_BORDER_TOP_RIGHT_RADIUS, + CSS_PROP__WEBKIT_BORDER_TOP_LEFT_RADIUS, + CSS_PROP__WEBKIT_BORDER_BOTTOM_LEFT_RADIUS, + CSS_PROP__WEBKIT_BORDER_BOTTOM_RIGHT_RADIUS + }; + SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__WEBKIT_BORDER_RADIUS, borderRadiusProperties); + + #undef SET_SHORTHAND_MAP_ENTRY +} + +String CSSMutableStyleDeclaration::removeProperty(int propertyID, bool notifyChanged, bool returnText, ExceptionCode& ec) +{ + ec = 0; + + static HashMap<int, PropertyLonghand> shorthandMap; + if (shorthandMap.isEmpty()) + initShorthandMap(shorthandMap); + + PropertyLonghand longhand = shorthandMap.get(propertyID); + if (longhand.length()) { + removePropertiesInSet(longhand.properties(), longhand.length(), notifyChanged); + // FIXME: Return an equivalent shorthand when possible. + return String(); + } + + String value; + + DeprecatedValueListIterator<CSSProperty> end; + for (DeprecatedValueListIterator<CSSProperty> it = m_values.fromLast(); it != end; --it) { + if (propertyID == (*it).m_id) { + if (returnText) + value = (*it).value()->cssText(); + m_values.remove(it); + if (notifyChanged) + setChanged(); + break; + } + } + + return value; +} + +void CSSMutableStyleDeclaration::clear() +{ + m_values.clear(); + setChanged(); +} + +void CSSMutableStyleDeclaration::setChanged(StyleChangeType changeType) +{ + if (m_node) { + m_node->setChanged(changeType); + // 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; + } + + // FIXME: 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, true, ec); +} + +bool CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, bool notifyChanged, ExceptionCode& ec) +{ + ec = 0; + + // Setting the value to an empty string just removes the property in both IE and Gecko. + // Setting it to null seems to produce less consistent results, but we treat it just the same. + if (value.isEmpty()) { + removeProperty(propertyID, notifyChanged, false, ec); + return ec == 0; + } + + // When replacing an existing property value, this moves the property to the end of the list. + // Firefox preserves the position, and MSIE moves the property to the beginning. + CSSParser parser(useStrictParsing()); + bool success = parser.parseValue(this, propertyID, value, important); + if (!success) { + // CSS DOM requires raising SYNTAX_ERR here, but this is too dangerous for compatibility, + // see <http://bugs.webkit.org/show_bug.cgi?id=7296>. + } else if (notifyChanged) + setChanged(InlineStyleChange); + ASSERT(!ec); + 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) { + // Only add properties that have no !important counterpart present + if (!getPropertyPriority(properties[i]->id()) || properties[i]->isImportant()) { + removeProperty(properties[i]->id(), false); + ASSERT(properties[i]); + 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 propertyId, const String& value, bool important, bool /*multiLength*/) +{ + bool parseMode = useStrictParsing(); + setStrictParsing(false); + setProperty(propertyId, 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(static_cast<CSSPropertyID>(m_values[i].id())); +} + +String CSSMutableStyleDeclaration::cssText() const +{ + String result = ""; + + const CSSProperty* positionXProp = 0; + const CSSProperty* positionYProp = 0; + + DeprecatedValueListConstIterator<CSSProperty> end; + for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it) { + const CSSProperty& prop = *it; + if (prop.id() == CSS_PROP_BACKGROUND_POSITION_X) + positionXProp = ∝ + else if (prop.id() == CSS_PROP_BACKGROUND_POSITION_Y) + positionYProp = ∝ + else + result += prop.cssText(); + } + + // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output. + // It is required because background-position-x/y are non-standard properties and WebKit generated output + // would not work in Firefox (<rdar://problem/5143183>) + // It would be a better solution if background-position was CSS_PAIR. + if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) { + String positionValue; + const int properties[2] = { CSS_PROP_BACKGROUND_POSITION_X, CSS_PROP_BACKGROUND_POSITION_Y }; + if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList()) + positionValue = getLayeredShorthandValue(properties, 2); + else + positionValue = positionXProp->value()->cssText() + " " + positionYProp->value()->cssText(); + result += "background-position: " + positionValue + (positionXProp->isImportant() ? " !important" : "") + "; "; + } else { + if (positionXProp) + result += positionXProp->cssText(); + if (positionYProp) + result += positionYProp->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. +} + +// 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__WEBKIT_COLUMN_COUNT, + CSS_PROP__WEBKIT_COLUMN_GAP, + CSS_PROP__WEBKIT_COLUMN_RULE_COLOR, + CSS_PROP__WEBKIT_COLUMN_RULE_STYLE, + CSS_PROP__WEBKIT_COLUMN_RULE_WIDTH, + CSS_PROP__WEBKIT_COLUMN_BREAK_BEFORE, + CSS_PROP__WEBKIT_COLUMN_BREAK_AFTER, + CSS_PROP__WEBKIT_COLUMN_BREAK_INSIDE, + CSS_PROP__WEBKIT_COLUMN_WIDTH, + 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::removePropertiesInSet(const int* set, unsigned length, bool notifyChanged) +{ + 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 && notifyChanged) + setChanged(); +} + +PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable() +{ + return this; +} + +PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const +{ + return new CSSMutableStyleDeclaration(0, m_values); +} + +} // namespace WebCore |