WebCore/css/CSSMutableStyleDeclaration.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/css/CSSMutableStyleDeclaration.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,845 @@
+/*
+ * (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 "CSSPropertyLonghand.h"
+#include "CSSPropertyNames.h"
+#include "CSSRule.h"
+#include "CSSStyleSheet.h"
+#include "CSSValueKeywords.h"
+#include "CSSValueList.h"
+#include "Document.h"
+#include "ExceptionCode.h"
+#include "StyledElement.h"
+
+using namespace std;
+
+namespace WebCore {
+
+CSSMutableStyleDeclaration::CSSMutableStyleDeclaration()
+    : m_node(0)
+    , m_variableDependentValueCount(0)
+    , m_strictParsing(false)
+#ifndef NDEBUG
+    , m_iteratorCount(0)
+#endif
+{
+}
+
+CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent)
+    : CSSStyleDeclaration(parent)
+    , m_node(0)
+    , m_variableDependentValueCount(0)
+    , m_strictParsing(!parent || parent->useStrictParsing())
+#ifndef NDEBUG
+    , m_iteratorCount(0)
+#endif
+{
+}
+
+CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const Vector<CSSProperty>& properties, unsigned variableDependentValueCount)
+    : CSSStyleDeclaration(parent)
+    , m_properties(properties)
+    , m_node(0)
+    , m_variableDependentValueCount(variableDependentValueCount)
+    , m_strictParsing(!parent || parent->useStrictParsing())
+#ifndef NDEBUG
+    , m_iteratorCount(0)
+#endif
+{
+    m_properties.shrinkToFit();
+    // FIXME: This allows duplicate properties.
+}
+
+CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const CSSProperty* const * properties, int numProperties)
+    : CSSStyleDeclaration(parent)
+    , m_node(0)
+    , m_variableDependentValueCount(0)
+    , m_strictParsing(!parent || parent->useStrictParsing())
+#ifndef NDEBUG
+    , m_iteratorCount(0)
+#endif
+{
+    m_properties.reserveInitialCapacity(numProperties);
+    HashSet<int> candidates;
+    for (int i = 0; i < numProperties; ++i) {
+        const CSSProperty *property = properties[i];
+        ASSERT(property);
+        if (property->value()->isVariableDependentValue())
+            m_variableDependentValueCount++;
+        else if (candidates.contains(property->id()))
+            removeProperty(properties[i]->id(), false);
+        m_properties.append(*property);
+        if (!getPropertyPriority(property->id()) && !property->isImportant())
+            candidates.add(property->id());
+    }
+}
+
+CSSMutableStyleDeclaration& CSSMutableStyleDeclaration::operator=(const CSSMutableStyleDeclaration& other)
+{
+    ASSERT(!m_iteratorCount);
+    // don't attach it to the same node, just leave the current m_node value
+    m_properties = other.m_properties;
+    m_strictParsing = other.m_strictParsing;
+    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 CSSPropertyBackgroundPosition: {
+            // FIXME: Is this correct? The code in cssparser.cpp is confusing
+            const int properties[2] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
+            return getLayeredShorthandValue(properties, 2);
+        }
+        case CSSPropertyBackgroundRepeat: {
+            const int properties[2] = { CSSPropertyBackgroundRepeatX, CSSPropertyBackgroundRepeatY };
+            return getLayeredShorthandValue(properties, 2);
+        }
+        case CSSPropertyBackground: {
+            const int properties[9] = { CSSPropertyBackgroundColor,
+                                        CSSPropertyBackgroundImage, 
+                                        CSSPropertyBackgroundRepeatX, 
+                                        CSSPropertyBackgroundRepeatY, 
+                                        CSSPropertyBackgroundAttachment, 
+                                        CSSPropertyBackgroundPositionX, 
+                                        CSSPropertyBackgroundPositionY, 
+                                        CSSPropertyBackgroundClip,
+                                        CSSPropertyBackgroundOrigin }; 
+            return getLayeredShorthandValue(properties, 9);
+        }
+        case CSSPropertyBorder: {
+            const int properties[3][4] = {{ CSSPropertyBorderTopWidth,
+                                            CSSPropertyBorderRightWidth,
+                                            CSSPropertyBorderBottomWidth,
+                                            CSSPropertyBorderLeftWidth },
+                                          { CSSPropertyBorderTopStyle,
+                                            CSSPropertyBorderRightStyle,
+                                            CSSPropertyBorderBottomStyle,
+                                            CSSPropertyBorderLeftStyle },
+                                          { CSSPropertyBorderTopColor,
+                                            CSSPropertyBorderRightColor,
+                                            CSSPropertyBorderBottomColor,
+                                            CSSPropertyBorderLeftColor }};
+            String res;
+            const int nrprops = sizeof(properties) / sizeof(properties[0]);
+            for (int i = 0; i < nrprops; ++i) {
+                String value = getCommonValue(properties[i], 4);
+                if (!value.isNull()) {
+                    if (!res.isNull())
+                        res += " ";
+                    res += value;
+                }
+            }
+            return res;
+        }
+        case CSSPropertyBorderTop: {
+            const int properties[3] = { CSSPropertyBorderTopWidth, CSSPropertyBorderTopStyle,
+                                        CSSPropertyBorderTopColor};
+            return getShorthandValue(properties, 3);
+        }
+        case CSSPropertyBorderRight: {
+            const int properties[3] = { CSSPropertyBorderRightWidth, CSSPropertyBorderRightStyle,
+                                        CSSPropertyBorderRightColor};
+            return getShorthandValue(properties, 3);
+        }
+        case CSSPropertyBorderBottom: {
+            const int properties[3] = { CSSPropertyBorderBottomWidth, CSSPropertyBorderBottomStyle,
+                                        CSSPropertyBorderBottomColor};
+            return getShorthandValue(properties, 3);
+        }
+        case CSSPropertyBorderLeft: {
+            const int properties[3] = { CSSPropertyBorderLeftWidth, CSSPropertyBorderLeftStyle,
+                                        CSSPropertyBorderLeftColor};
+            return getShorthandValue(properties, 3);
+        }
+        case CSSPropertyOutline: {
+            const int properties[3] = { CSSPropertyOutlineWidth, CSSPropertyOutlineStyle,
+                                        CSSPropertyOutlineColor };
+            return getShorthandValue(properties, 3);
+        }
+        case CSSPropertyBorderColor: {
+            const int properties[4] = { CSSPropertyBorderTopColor, CSSPropertyBorderRightColor,
+                                        CSSPropertyBorderBottomColor, CSSPropertyBorderLeftColor };
+            return get4Values(properties);
+        }
+        case CSSPropertyBorderWidth: {
+            const int properties[4] = { CSSPropertyBorderTopWidth, CSSPropertyBorderRightWidth,
+                                        CSSPropertyBorderBottomWidth, CSSPropertyBorderLeftWidth };
+            return get4Values(properties);
+        }
+        case CSSPropertyBorderStyle: {
+            const int properties[4] = { CSSPropertyBorderTopStyle, CSSPropertyBorderRightStyle,
+                                        CSSPropertyBorderBottomStyle, CSSPropertyBorderLeftStyle };
+            return get4Values(properties);
+        }
+        case CSSPropertyMargin: {
+            const int properties[4] = { CSSPropertyMarginTop, CSSPropertyMarginRight,
+                                        CSSPropertyMarginBottom, CSSPropertyMarginLeft };
+            return get4Values(properties);
+        }
+        case CSSPropertyOverflow: {
+            const int properties[2] = { CSSPropertyOverflowX, CSSPropertyOverflowY };
+            return getCommonValue(properties, 2);
+        }
+        case CSSPropertyPadding: {
+            const int properties[4] = { CSSPropertyPaddingTop, CSSPropertyPaddingRight,
+                                        CSSPropertyPaddingBottom, CSSPropertyPaddingLeft };
+            return get4Values(properties);
+        }
+        case CSSPropertyListStyle: {
+            const int properties[3] = { CSSPropertyListStyleType, CSSPropertyListStylePosition,
+                                        CSSPropertyListStyleImage };
+            return getShorthandValue(properties, 3);
+        }
+        case CSSPropertyWebkitMaskPosition: {
+            // FIXME: Is this correct? The code in cssparser.cpp is confusing
+            const int properties[2] = { CSSPropertyWebkitMaskPositionX, CSSPropertyWebkitMaskPositionY };
+            return getLayeredShorthandValue(properties, 2);
+        }
+        case CSSPropertyWebkitMaskRepeat: {
+            const int properties[2] = { CSSPropertyWebkitMaskRepeatX, CSSPropertyWebkitMaskRepeatY };
+            return getLayeredShorthandValue(properties, 2);
+        }
+        case CSSPropertyWebkitMask: {
+            const int properties[] = { CSSPropertyWebkitMaskImage, CSSPropertyWebkitMaskRepeat, 
+                                       CSSPropertyWebkitMaskAttachment, CSSPropertyWebkitMaskPosition, CSSPropertyWebkitMaskClip,
+                                       CSSPropertyWebkitMaskOrigin };
+            return getLayeredShorthandValue(properties, 6);
+        }
+        case CSSPropertyWebkitTransformOrigin: {
+            const int properties[3] = { CSSPropertyWebkitTransformOriginX,
+                                        CSSPropertyWebkitTransformOriginY,
+                                        CSSPropertyWebkitTransformOriginZ };
+            return getShorthandValue(properties, 3);
+        }
+        case CSSPropertyWebkitTransition: {
+            const int properties[4] = { CSSPropertyWebkitTransitionProperty, CSSPropertyWebkitTransitionDuration,
+                                        CSSPropertyWebkitTransitionTimingFunction, CSSPropertyWebkitTransitionDelay };
+            return getLayeredShorthandValue(properties, 4);
+        }
+        case CSSPropertyWebkitAnimation: {
+            const int properties[7] = { CSSPropertyWebkitAnimationName, CSSPropertyWebkitAnimationDuration,
+                                        CSSPropertyWebkitAnimationTimingFunction, CSSPropertyWebkitAnimationDelay,
+                                        CSSPropertyWebkitAnimationIterationCount, CSSPropertyWebkitAnimationDirection,
+                                        CSSPropertyWebkitAnimationFillMode };
+            return getLayeredShorthandValue(properties, 7);
+        }
+#if ENABLE(SVG)
+        case CSSPropertyMarker: {
+            RefPtr<CSSValue> value = getPropertyCSSValue(CSSPropertyMarkerStart);
+            if (value)
+                return value->cssText();
+        }
+#endif
+    }
+    return String();
+}
+
+String CSSMutableStyleDeclaration::get4Values(const int* properties) const
+{
+    // Assume the properties are in the usual order top, right, bottom, left.
+    RefPtr<CSSValue> topValue = getPropertyCSSValue(properties[0]);
+    RefPtr<CSSValue> rightValue = getPropertyCSSValue(properties[1]);
+    RefPtr<CSSValue> bottomValue = getPropertyCSSValue(properties[2]);
+    RefPtr<CSSValue> leftValue = getPropertyCSSValue(properties[3]);
+
+    // All 4 properties must be specified.
+    if (!topValue || !rightValue || !bottomValue || !leftValue)
+        return String();
+
+    bool showLeft = rightValue->cssText() != leftValue->cssText();
+    bool showBottom = (topValue->cssText() != bottomValue->cssText()) || showLeft;
+    bool showRight = (topValue->cssText() != rightValue->cssText()) || showBottom;
+
+    String res = topValue->cssText();
+    if (showRight)
+        res += " " + rightValue->cssText();
+    if (showBottom)
+        res += " " + bottomValue->cssText();
+    if (showLeft)
+        res += " " + leftValue->cssText();
+
+    return res;
+}
+
+String CSSMutableStyleDeclaration::getLayeredShorthandValue(const int* properties, unsigned number) const
+{
+    String res;
+
+    // Begin by collecting the properties into an array.
+    Vector< RefPtr<CSSValue> > values(number);
+    size_t numLayers = 0;
+    
+    for (size_t 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<size_t>(1U, numLayers);
+        }
+    }
+    
+    // Now stitch the properties together.  Implicit initial values are flagged as such and
+    // can safely be omitted.
+    for (size_t i = 0; i < numLayers; i++) {
+        String layerRes;
+        bool useRepeatXShorthand = false;
+        bool useRepeatYShorthand = false;
+        bool useSingleWordShorthand = false;
+        for (size_t 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] == CSSPropertyBackgroundColor) {
+                        if (i != numLayers - 1)
+                            value = 0;
+                    } else if (i != 0) // Other singletons only belong in the first layer.
+                        value = 0;
+                }
+            }
+
+            // We need to report background-repeat as it was written in the CSS. If the property is implicit,
+            // then it was written with only one value. Here we figure out which value that was so we can
+            // report back correctly. 
+            if (properties[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(properties[j])) {
+                if (j < number - 1 && properties[j + 1] == CSSPropertyBackgroundRepeatY) {
+                    RefPtr<CSSValue> yValue;
+                    RefPtr<CSSValue> nextValue = values[j + 1];
+                    if (nextValue->isValueList())
+                        yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
+                    else
+                        yValue = nextValue;
+                        
+                    int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
+                    int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
+                    if (xId != yId) {
+                        if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
+                            useRepeatXShorthand = true;
+                            ++j;
+                        } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
+                            useRepeatYShorthand = true;
+                            continue;
+                        }
+                    } else {
+                        useSingleWordShorthand = true;
+                        ++j;
+                    }
+                }
+            }
+            
+            if (value && !value->isImplicitInitialValue()) {
+                if (!layerRes.isNull())
+                    layerRes += " ";
+                if (useRepeatXShorthand) {
+                    useRepeatXShorthand = false;
+                    layerRes += getValueName(CSSValueRepeatX);
+                } else if (useRepeatYShorthand) {
+                    useRepeatYShorthand = false;
+                    layerRes += getValueName(CSSValueRepeatY);
+                } else if (useSingleWordShorthand) {
+                    useSingleWordShorthand = false;
+                    layerRes += value->cssText();
+                } else
+                    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;
+}
+
+// only returns a non-null value if all properties have the same, non-null value
+String CSSMutableStyleDeclaration::getCommonValue(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)
+                return String();
+            String text = value->cssText();
+            if (text.isNull())
+                return String();
+            if (res.isNull())
+                res = text;
+            else if (res != text)
+                return String();
+        }
+    }
+    return res;
+}
+
+PassRefPtr<CSSValue> CSSMutableStyleDeclaration::getPropertyCSSValue(int propertyID) const
+{
+    const CSSProperty* property = findPropertyWithId(propertyID);
+    return property ? property->value() : 0;
+}
+
+bool CSSMutableStyleDeclaration::removeShorthandProperty(int propertyID, bool notifyChanged) 
+{
+    CSSPropertyLonghand longhand = longhandForProperty(propertyID);
+    if (longhand.length()) {
+        removePropertiesInSet(longhand.properties(), longhand.length(), notifyChanged);
+        return true;
+    }
+    return false;
+}
+
+String CSSMutableStyleDeclaration::removeProperty(int propertyID, bool notifyChanged, bool returnText)
+{
+    ASSERT(!m_iteratorCount);
+
+    if (removeShorthandProperty(propertyID, notifyChanged)) {
+        // FIXME: Return an equivalent shorthand when possible.
+        return String();
+    }
+   
+    CSSProperty* foundProperty = findPropertyWithId(propertyID);
+    if (!foundProperty)
+        return String();
+    
+    String value = returnText ? foundProperty->value()->cssText() : String();
+
+    if (foundProperty->value()->isVariableDependentValue())
+        m_variableDependentValueCount--;
+
+    // A more efficient removal strategy would involve marking entries as empty
+    // and sweeping them when the vector grows too big.
+    m_properties.remove(foundProperty - m_properties.data());
+
+    if (notifyChanged)
+        setNeedsStyleRecalc();
+
+    return value;
+}
+
+void CSSMutableStyleDeclaration::setNeedsStyleRecalc()
+{
+    if (m_node) {
+        // FIXME: Ideally, this should be factored better and there
+        // should be a subclass of CSSMutableStyleDeclaration just
+        // for inline style declarations that handles this
+        bool isInlineStyleDeclaration = m_node->isStyledElement() && this == static_cast<StyledElement*>(m_node)->inlineStyleDecl();
+        if (isInlineStyleDeclaration) {
+            m_node->setNeedsStyleRecalc(InlineStyleChange);
+            static_cast<StyledElement*>(m_node)->invalidateStyleAttribute();
+        } else
+            m_node->setNeedsStyleRecalc(FullStyleChange);
+        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())
+        if (Document* doc = static_cast<CSSStyleSheet*>(root)->doc())
+            doc->updateStyleSelector();
+}
+
+bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const
+{
+    const CSSProperty* property = findPropertyWithId(propertyID);
+    return property ? property->isImportant() : false;
+}
+
+int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const
+{
+    const CSSProperty* property = findPropertyWithId(propertyID);
+    return property ? property->shorthandID() : 0;
+}
+
+bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const
+{
+    const CSSProperty* property = findPropertyWithId(propertyID);
+    return property ? property->isImplicit() : false;
+}
+
+void CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, ExceptionCode& ec)
+{
+    ec = 0;
+    setProperty(propertyID, value, important, true);
+}
+
+String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec)
+{
+    ec = 0;
+    return removeProperty(propertyID, true, true);
+}
+
+bool CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, bool notifyChanged)
+{
+    ASSERT(!m_iteratorCount);
+
+    // 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);
+        return true;
+    }
+
+    // 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)
+        setNeedsStyleRecalc();
+
+    return success;
+}
+    
+void CSSMutableStyleDeclaration::setPropertyInternal(const CSSProperty& property, CSSProperty* slot)
+{
+    ASSERT(!m_iteratorCount);
+
+    if (!removeShorthandProperty(property.id(), false)) {
+        CSSProperty* toReplace = slot ? slot : findPropertyWithId(property.id());
+        if (toReplace) {
+            *toReplace = property;
+            return;
+        }
+    }
+    m_properties.append(property);
+}
+
+bool CSSMutableStyleDeclaration::setProperty(int propertyID, int value, bool important, bool notifyChanged)
+{
+    CSSProperty property(propertyID, CSSPrimitiveValue::createIdentifier(value), important);
+    setPropertyInternal(property);
+    if (notifyChanged)
+        setNeedsStyleRecalc();
+    return true;
+}
+
+void CSSMutableStyleDeclaration::setStringProperty(int propertyId, const String &value, CSSPrimitiveValue::UnitTypes type, bool important)
+{
+    ASSERT(!m_iteratorCount);
+
+    setPropertyInternal(CSSProperty(propertyId, CSSPrimitiveValue::create(value, type), important));
+    setNeedsStyleRecalc();
+}
+
+void CSSMutableStyleDeclaration::setImageProperty(int propertyId, const String& url, bool important)
+{
+    ASSERT(!m_iteratorCount);
+
+    setPropertyInternal(CSSProperty(propertyId, CSSImageValue::create(url), important));
+    setNeedsStyleRecalc();
+}
+
+void CSSMutableStyleDeclaration::parseDeclaration(const String& styleDeclaration)
+{
+    ASSERT(!m_iteratorCount);
+
+    m_properties.clear();
+    CSSParser parser(useStrictParsing());
+    parser.parseDeclaration(this, styleDeclaration);
+    setNeedsStyleRecalc();
+}
+
+void CSSMutableStyleDeclaration::addParsedProperties(const CSSProperty* const* properties, int numProperties)
+{
+    ASSERT(!m_iteratorCount);
+    
+    m_properties.reserveCapacity(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_properties.append(*properties[i]);
+            if (properties[i]->value()->isVariableDependentValue())
+                m_variableDependentValueCount++;
+        }
+    }
+    // FIXME: This probably should have a call to setNeedsStyleRecalc() 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::addParsedProperty(const CSSProperty& property)
+{
+    ASSERT(!m_iteratorCount);
+
+    setPropertyInternal(property);
+}
+
+void CSSMutableStyleDeclaration::setLengthProperty(int propertyId, const String& value, bool important, bool /*multiLength*/)
+{
+    ASSERT(!m_iteratorCount);
+
+    bool parseMode = useStrictParsing();
+    setStrictParsing(false);
+    setProperty(propertyId, value, important);
+    setStrictParsing(parseMode);
+}
+
+unsigned CSSMutableStyleDeclaration::virtualLength() const
+{
+    return length();
+}
+
+String CSSMutableStyleDeclaration::item(unsigned i) const
+{
+    if (i >= m_properties.size())
+       return "";
+    return getPropertyName(static_cast<CSSPropertyID>(m_properties[i].id()));
+}
+
+String CSSMutableStyleDeclaration::cssText() const
+{
+    String result = "";
+    
+    const CSSProperty* positionXProp = 0;
+    const CSSProperty* positionYProp = 0;
+    const CSSProperty* repeatXProp = 0;
+    const CSSProperty* repeatYProp = 0;
+    
+    unsigned size = m_properties.size();
+    for (unsigned n = 0; n < size; ++n) {
+        const CSSProperty& prop = m_properties[n];
+        if (prop.id() == CSSPropertyBackgroundPositionX)
+            positionXProp = &prop;
+        else if (prop.id() == CSSPropertyBackgroundPositionY)
+            positionYProp = &prop;
+        else if (prop.id() == CSSPropertyBackgroundRepeatX)
+            repeatXProp = &prop;
+        else if (prop.id() == CSSPropertyBackgroundRepeatY)
+            repeatYProp = &prop;
+        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] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
+        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();
+    }
+
+    // FIXME: We need to do the same for background-repeat.
+    if (repeatXProp && repeatYProp && repeatXProp->isImportant() == repeatYProp->isImportant()) {
+        String repeatValue;
+        const int repeatProperties[2] = { CSSPropertyBackgroundRepeatX, CSSPropertyBackgroundRepeatY };
+        if (repeatXProp->value()->isValueList() || repeatYProp->value()->isValueList()) 
+            repeatValue = getLayeredShorthandValue(repeatProperties, 2);
+        else
+            repeatValue = repeatXProp->value()->cssText() + " " + repeatYProp->value()->cssText();
+        result += "background-repeat: " + repeatValue + (repeatXProp->isImportant() ? " !important" : "") + "; ";
+    } else {
+        if (repeatXProp) 
+            result += repeatXProp->cssText();
+        if (repeatYProp)
+            result += repeatYProp->cssText();
+    }
+
+    return result;
+}
+
+void CSSMutableStyleDeclaration::setCssText(const String& text, ExceptionCode& ec)
+{
+    ASSERT(!m_iteratorCount);
+
+    ec = 0;
+    m_properties.clear();
+    CSSParser parser(useStrictParsing());
+    parser.parseDeclaration(this, text);
+    // FIXME: Detect syntax errors and set ec.
+    setNeedsStyleRecalc();
+}
+
+void CSSMutableStyleDeclaration::merge(CSSMutableStyleDeclaration* other, bool argOverridesOnConflict)
+{
+    ASSERT(!m_iteratorCount);
+
+    unsigned size = other->m_properties.size();
+    for (unsigned n = 0; n < size; ++n) {
+        CSSProperty& toMerge = other->m_properties[n];
+        CSSProperty* old = findPropertyWithId(toMerge.id());
+        if (old) {
+            if (!argOverridesOnConflict && old->value())
+                continue;
+            setPropertyInternal(toMerge, old);
+        } else
+            m_properties.append(toMerge);
+    }
+    // FIXME: This probably should have a call to setNeedsStyleRecalc() 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::addSubresourceStyleURLs(ListHashSet<KURL>& urls)
+{
+    CSSStyleSheet* sheet = static_cast<CSSStyleSheet*>(stylesheet());
+    size_t size = m_properties.size();
+    for (size_t i = 0; i < size; ++i)
+        m_properties[i].value()->addSubresourceStyleURLs(urls, sheet);
+}
+
+// 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[] = {
+    CSSPropertyOrphans,
+    CSSPropertyOverflow, // This can be also be applied to replaced elements
+    CSSPropertyWebkitColumnCount,
+    CSSPropertyWebkitColumnGap,
+    CSSPropertyWebkitColumnRuleColor,
+    CSSPropertyWebkitColumnRuleStyle,
+    CSSPropertyWebkitColumnRuleWidth,
+    CSSPropertyWebkitColumnBreakBefore,
+    CSSPropertyWebkitColumnBreakAfter,
+    CSSPropertyWebkitColumnBreakInside,
+    CSSPropertyWebkitColumnWidth,
+    CSSPropertyPageBreakAfter,
+    CSSPropertyPageBreakBefore,
+    CSSPropertyPageBreakInside,
+    CSSPropertyTextAlign,
+    CSSPropertyTextIndent,
+    CSSPropertyWidows
+};
+
+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)
+{
+    ASSERT(!m_iteratorCount);
+    
+    if (m_properties.isEmpty())
+        return;
+    
+    // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
+    HashSet<int> toRemove;
+    for (unsigned i = 0; i < length; ++i)
+        toRemove.add(set[i]);
+    
+    Vector<CSSProperty, 4> newProperties;
+    newProperties.reserveInitialCapacity(m_properties.size());
+    
+    unsigned size = m_properties.size();
+    for (unsigned n = 0; n < size; ++n) {
+        const CSSProperty& property = m_properties[n];
+        // Not quite sure if the isImportant test is needed but it matches the existing behavior.
+        if (!property.isImportant()) {
+            if (toRemove.contains(property.id()))
+                continue;
+        }
+        newProperties.append(property);
+    }
+
+    bool changed = newProperties.size() != m_properties.size();
+    m_properties = newProperties;
+    
+    if (changed && notifyChanged)
+        setNeedsStyleRecalc();
+}
+
+PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable()
+{
+    return this;
+}
+
+PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const
+{
+    return adoptRef(new CSSMutableStyleDeclaration(0, m_properties, m_variableDependentValueCount));
+}
+
+const CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID) const
+{    
+    for (int n = m_properties.size() - 1 ; n >= 0; --n) {
+        if (propertyID == m_properties[n].m_id)
+            return &m_properties[n];
+    }
+    return 0;
+}
+
+CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID)
+{
+    for (int n = m_properties.size() - 1 ; n >= 0; --n) {
+        if (propertyID == m_properties[n].m_id)
+            return &m_properties[n];
+    }
+    return 0;
+}
+
+} // namespace WebCore