summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/css/CSSMutableStyleDeclaration.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/css/CSSMutableStyleDeclaration.cpp
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/css/CSSMutableStyleDeclaration.cpp')
-rw-r--r--Source/WebCore/css/CSSMutableStyleDeclaration.cpp847
1 files changed, 847 insertions, 0 deletions
diff --git a/Source/WebCore/css/CSSMutableStyleDeclaration.cpp b/Source/WebCore/css/CSSMutableStyleDeclaration.cpp
new file mode 100644
index 0000000..573f4c0
--- /dev/null
+++ b/Source/WebCore/css/CSSMutableStyleDeclaration.cpp
@@ -0,0 +1,847 @@
+/*
+ * (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 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_strictParsing(false)
+#ifndef NDEBUG
+ , m_iteratorCount(0)
+#endif
+{
+}
+
+CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent)
+ : CSSStyleDeclaration(parent)
+ , m_node(0)
+ , m_strictParsing(!parent || parent->useStrictParsing())
+#ifndef NDEBUG
+ , m_iteratorCount(0)
+#endif
+{
+}
+
+CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const Vector<CSSProperty>& properties)
+ : CSSStyleDeclaration(parent)
+ , m_properties(properties)
+ , m_node(0)
+ , 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_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 (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;
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++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
+#ifdef ANDROID_CSS_RING
+ case CSSPropertyWebkitRing: {
+ const int properties[9] = { CSSPropertyWebkitRingFillColor,
+ CSSPropertyWebkitRingInnerWidth,
+ CSSPropertyWebkitRingOuterWidth,
+ CSSPropertyWebkitRingOutset,
+ CSSPropertyWebkitRingPressedInnerColor,
+ CSSPropertyWebkitRingPressedOuterColor,
+ CSSPropertyWebkitRingRadius,
+ CSSPropertyWebkitRingSelectedInnerColor,
+ CSSPropertyWebkitRingSelectedOuterColor };
+ return getLayeredShorthandValue(properties, 9);
+ }
+#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])) {
+
+ // BUG 49055: make sure the value was not reset in the layer check just above.
+ if (j < number - 1 && properties[j + 1] == CSSPropertyBackgroundRepeatY && value) {
+ 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) {
+ 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();
+
+ // 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;
+ }
+
+ StyleBase* root = this;
+ while (StyleBase* parent = root->parent())
+ root = parent;
+ if (root->isCSSStyleSheet()) {
+ if (Document* document = static_cast<CSSStyleSheet*>(root)->document())
+ document->styleSelectorChanged(DeferRecalcStyle);
+ }
+}
+
+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]);
+ }
+ }
+ // 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(const CSSMutableStyleDeclaration* other, bool argOverridesOnConflict)
+{
+ ASSERT(!m_iteratorCount);
+
+ unsigned size = other->m_properties.size();
+ for (unsigned n = 0; n < size; ++n) {
+ const 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 = WTF_ARRAY_LENGTH(blockProperties);
+
+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));
+}
+
+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