Home | History | Annotate | Download | only in css
      1 /*
      2  * (C) 1999-2003 Lars Knoll (knoll (at) kde.org)
      3  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  */
     20 
     21 #include "config.h"
     22 #include "CSSMutableStyleDeclaration.h"
     23 
     24 #include "CSSImageValue.h"
     25 #include "CSSParser.h"
     26 #include "CSSPropertyLonghand.h"
     27 #include "CSSPropertyNames.h"
     28 #include "CSSRule.h"
     29 #include "CSSStyleSheet.h"
     30 #include "CSSValueKeywords.h"
     31 #include "CSSValueList.h"
     32 #include "Document.h"
     33 #include "ExceptionCode.h"
     34 #include "StyledElement.h"
     35 
     36 using namespace std;
     37 
     38 namespace WebCore {
     39 
     40 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration()
     41     : m_node(0)
     42     , m_variableDependentValueCount(0)
     43     , m_strictParsing(false)
     44 #ifndef NDEBUG
     45     , m_iteratorCount(0)
     46 #endif
     47 {
     48 }
     49 
     50 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent)
     51     : CSSStyleDeclaration(parent)
     52     , m_node(0)
     53     , m_variableDependentValueCount(0)
     54     , m_strictParsing(!parent || parent->useStrictParsing())
     55 #ifndef NDEBUG
     56     , m_iteratorCount(0)
     57 #endif
     58 {
     59 }
     60 
     61 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const Vector<CSSProperty>& properties, unsigned variableDependentValueCount)
     62     : CSSStyleDeclaration(parent)
     63     , m_properties(properties)
     64     , m_node(0)
     65     , m_variableDependentValueCount(variableDependentValueCount)
     66     , m_strictParsing(!parent || parent->useStrictParsing())
     67 #ifndef NDEBUG
     68     , m_iteratorCount(0)
     69 #endif
     70 {
     71     m_properties.shrinkToFit();
     72     // FIXME: This allows duplicate properties.
     73 }
     74 
     75 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const CSSProperty* const * properties, int numProperties)
     76     : CSSStyleDeclaration(parent)
     77     , m_node(0)
     78     , m_variableDependentValueCount(0)
     79     , m_strictParsing(!parent || parent->useStrictParsing())
     80 #ifndef NDEBUG
     81     , m_iteratorCount(0)
     82 #endif
     83 {
     84     m_properties.reserveInitialCapacity(numProperties);
     85     for (int i = 0; i < numProperties; ++i) {
     86         ASSERT(properties[i]);
     87         m_properties.append(*properties[i]);
     88         if (properties[i]->value()->isVariableDependentValue())
     89             m_variableDependentValueCount++;
     90     }
     91     // FIXME: This allows duplicate properties.
     92 }
     93 
     94 CSSMutableStyleDeclaration& CSSMutableStyleDeclaration::operator=(const CSSMutableStyleDeclaration& other)
     95 {
     96     ASSERT(!m_iteratorCount);
     97     // don't attach it to the same node, just leave the current m_node value
     98     m_properties = other.m_properties;
     99     m_strictParsing = other.m_strictParsing;
    100     return *this;
    101 }
    102 
    103 String CSSMutableStyleDeclaration::getPropertyValue(int propertyID) const
    104 {
    105     RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
    106     if (value)
    107         return value->cssText();
    108 
    109     // Shorthand and 4-values properties
    110     switch (propertyID) {
    111         case CSSPropertyBackgroundPosition: {
    112             // FIXME: Is this correct? The code in cssparser.cpp is confusing
    113             const int properties[2] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
    114             return getLayeredShorthandValue(properties, 2);
    115         }
    116         case CSSPropertyBackgroundRepeat: {
    117             const int properties[2] = { CSSPropertyBackgroundRepeatX, CSSPropertyBackgroundRepeatY };
    118             return getLayeredShorthandValue(properties, 2);
    119         }
    120         case CSSPropertyBackground: {
    121             const int properties[9] = { CSSPropertyBackgroundColor,
    122                                         CSSPropertyBackgroundImage,
    123                                         CSSPropertyBackgroundRepeatX,
    124                                         CSSPropertyBackgroundRepeatY,
    125                                         CSSPropertyBackgroundAttachment,
    126                                         CSSPropertyBackgroundPositionX,
    127                                         CSSPropertyBackgroundPositionY,
    128                                         CSSPropertyBackgroundClip,
    129                                         CSSPropertyBackgroundOrigin };
    130             return getLayeredShorthandValue(properties, 9);
    131         }
    132         case CSSPropertyBorder: {
    133             const int properties[3][4] = {{ CSSPropertyBorderTopWidth,
    134                                             CSSPropertyBorderRightWidth,
    135                                             CSSPropertyBorderBottomWidth,
    136                                             CSSPropertyBorderLeftWidth },
    137                                           { CSSPropertyBorderTopStyle,
    138                                             CSSPropertyBorderRightStyle,
    139                                             CSSPropertyBorderBottomStyle,
    140                                             CSSPropertyBorderLeftStyle },
    141                                           { CSSPropertyBorderTopColor,
    142                                             CSSPropertyBorderRightColor,
    143                                             CSSPropertyBorderBottomColor,
    144                                             CSSPropertyBorderLeftColor }};
    145             String res;
    146             const int nrprops = sizeof(properties) / sizeof(properties[0]);
    147             for (int i = 0; i < nrprops; ++i) {
    148                 String value = getCommonValue(properties[i], 4);
    149                 if (!value.isNull()) {
    150                     if (!res.isNull())
    151                         res += " ";
    152                     res += value;
    153                 }
    154             }
    155             return res;
    156         }
    157         case CSSPropertyBorderTop: {
    158             const int properties[3] = { CSSPropertyBorderTopWidth, CSSPropertyBorderTopStyle,
    159                                         CSSPropertyBorderTopColor};
    160             return getShorthandValue(properties, 3);
    161         }
    162         case CSSPropertyBorderRight: {
    163             const int properties[3] = { CSSPropertyBorderRightWidth, CSSPropertyBorderRightStyle,
    164                                         CSSPropertyBorderRightColor};
    165             return getShorthandValue(properties, 3);
    166         }
    167         case CSSPropertyBorderBottom: {
    168             const int properties[3] = { CSSPropertyBorderBottomWidth, CSSPropertyBorderBottomStyle,
    169                                         CSSPropertyBorderBottomColor};
    170             return getShorthandValue(properties, 3);
    171         }
    172         case CSSPropertyBorderLeft: {
    173             const int properties[3] = { CSSPropertyBorderLeftWidth, CSSPropertyBorderLeftStyle,
    174                                         CSSPropertyBorderLeftColor};
    175             return getShorthandValue(properties, 3);
    176         }
    177         case CSSPropertyOutline: {
    178             const int properties[3] = { CSSPropertyOutlineWidth, CSSPropertyOutlineStyle,
    179                                         CSSPropertyOutlineColor };
    180             return getShorthandValue(properties, 3);
    181         }
    182         case CSSPropertyBorderColor: {
    183             const int properties[4] = { CSSPropertyBorderTopColor, CSSPropertyBorderRightColor,
    184                                         CSSPropertyBorderBottomColor, CSSPropertyBorderLeftColor };
    185             return get4Values(properties);
    186         }
    187         case CSSPropertyBorderWidth: {
    188             const int properties[4] = { CSSPropertyBorderTopWidth, CSSPropertyBorderRightWidth,
    189                                         CSSPropertyBorderBottomWidth, CSSPropertyBorderLeftWidth };
    190             return get4Values(properties);
    191         }
    192         case CSSPropertyBorderStyle: {
    193             const int properties[4] = { CSSPropertyBorderTopStyle, CSSPropertyBorderRightStyle,
    194                                         CSSPropertyBorderBottomStyle, CSSPropertyBorderLeftStyle };
    195             return get4Values(properties);
    196         }
    197         case CSSPropertyMargin: {
    198             const int properties[4] = { CSSPropertyMarginTop, CSSPropertyMarginRight,
    199                                         CSSPropertyMarginBottom, CSSPropertyMarginLeft };
    200             return get4Values(properties);
    201         }
    202         case CSSPropertyOverflow: {
    203             const int properties[2] = { CSSPropertyOverflowX, CSSPropertyOverflowY };
    204             return getCommonValue(properties, 2);
    205         }
    206         case CSSPropertyPadding: {
    207             const int properties[4] = { CSSPropertyPaddingTop, CSSPropertyPaddingRight,
    208                                         CSSPropertyPaddingBottom, CSSPropertyPaddingLeft };
    209             return get4Values(properties);
    210         }
    211         case CSSPropertyListStyle: {
    212             const int properties[3] = { CSSPropertyListStyleType, CSSPropertyListStylePosition,
    213                                         CSSPropertyListStyleImage };
    214             return getShorthandValue(properties, 3);
    215         }
    216         case CSSPropertyWebkitMaskPosition: {
    217             // FIXME: Is this correct? The code in cssparser.cpp is confusing
    218             const int properties[2] = { CSSPropertyWebkitMaskPositionX, CSSPropertyWebkitMaskPositionY };
    219             return getLayeredShorthandValue(properties, 2);
    220         }
    221         case CSSPropertyWebkitMaskRepeat: {
    222             const int properties[2] = { CSSPropertyWebkitMaskRepeatX, CSSPropertyWebkitMaskRepeatY };
    223             return getLayeredShorthandValue(properties, 2);
    224         }
    225         case CSSPropertyWebkitMask: {
    226             const int properties[] = { CSSPropertyWebkitMaskImage, CSSPropertyWebkitMaskRepeat,
    227                                        CSSPropertyWebkitMaskAttachment, CSSPropertyWebkitMaskPosition, CSSPropertyWebkitMaskClip,
    228                                        CSSPropertyWebkitMaskOrigin };
    229             return getLayeredShorthandValue(properties, 6);
    230         }
    231         case CSSPropertyWebkitTransformOrigin: {
    232             const int properties[3] = { CSSPropertyWebkitTransformOriginX,
    233                                         CSSPropertyWebkitTransformOriginY,
    234                                         CSSPropertyWebkitTransformOriginZ };
    235             return getShorthandValue(properties, 3);
    236         }
    237         case CSSPropertyWebkitTransition: {
    238             const int properties[4] = { CSSPropertyWebkitTransitionProperty, CSSPropertyWebkitTransitionDuration,
    239                                         CSSPropertyWebkitTransitionTimingFunction, CSSPropertyWebkitTransitionDelay };
    240             return getLayeredShorthandValue(properties, 4);
    241         }
    242         case CSSPropertyWebkitAnimation: {
    243             const int properties[6] = { CSSPropertyWebkitAnimationName, CSSPropertyWebkitAnimationDuration,
    244                                         CSSPropertyWebkitAnimationTimingFunction, CSSPropertyWebkitAnimationDelay,
    245                                         CSSPropertyWebkitAnimationIterationCount, CSSPropertyWebkitAnimationDirection };
    246             return getLayeredShorthandValue(properties, 6);
    247         }
    248 #if ENABLE(SVG)
    249         case CSSPropertyMarker: {
    250             RefPtr<CSSValue> value = getPropertyCSSValue(CSSPropertyMarkerStart);
    251             if (value)
    252                 return value->cssText();
    253         }
    254 #endif
    255     }
    256     return String();
    257 }
    258 
    259 String CSSMutableStyleDeclaration::get4Values(const int* properties) const
    260 {
    261     // Assume the properties are in the usual order top, right, bottom, left.
    262     RefPtr<CSSValue> topValue = getPropertyCSSValue(properties[0]);
    263     RefPtr<CSSValue> rightValue = getPropertyCSSValue(properties[1]);
    264     RefPtr<CSSValue> bottomValue = getPropertyCSSValue(properties[2]);
    265     RefPtr<CSSValue> leftValue = getPropertyCSSValue(properties[3]);
    266 
    267     // All 4 properties must be specified.
    268     if (!topValue || !rightValue || !bottomValue || !leftValue)
    269         return String();
    270 
    271     bool showLeft = rightValue->cssText() != leftValue->cssText();
    272     bool showBottom = (topValue->cssText() != bottomValue->cssText()) || showLeft;
    273     bool showRight = (topValue->cssText() != rightValue->cssText()) || showBottom;
    274 
    275     String res = topValue->cssText();
    276     if (showRight)
    277         res += " " + rightValue->cssText();
    278     if (showBottom)
    279         res += " " + bottomValue->cssText();
    280     if (showLeft)
    281         res += " " + leftValue->cssText();
    282 
    283     return res;
    284 }
    285 
    286 String CSSMutableStyleDeclaration::getLayeredShorthandValue(const int* properties, unsigned number) const
    287 {
    288     String res;
    289 
    290     // Begin by collecting the properties into an array.
    291     Vector< RefPtr<CSSValue> > values(number);
    292     size_t numLayers = 0;
    293 
    294     for (size_t i = 0; i < number; ++i) {
    295         values[i] = getPropertyCSSValue(properties[i]);
    296         if (values[i]) {
    297             if (values[i]->isValueList()) {
    298                 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
    299                 numLayers = max(valueList->length(), numLayers);
    300             } else
    301                 numLayers = max<size_t>(1U, numLayers);
    302         }
    303     }
    304 
    305     // Now stitch the properties together.  Implicit initial values are flagged as such and
    306     // can safely be omitted.
    307     for (size_t i = 0; i < numLayers; i++) {
    308         String layerRes;
    309         bool useRepeatXShorthand = false;
    310         bool useRepeatYShorthand = false;
    311         bool useSingleWordShorthand = false;
    312         for (size_t j = 0; j < number; j++) {
    313             RefPtr<CSSValue> value;
    314             if (values[j]) {
    315                 if (values[j]->isValueList())
    316                     value = static_cast<CSSValueList*>(values[j].get())->item(i);
    317                 else {
    318                     value = values[j];
    319 
    320                     // Color only belongs in the last layer.
    321                     if (properties[j] == CSSPropertyBackgroundColor) {
    322                         if (i != numLayers - 1)
    323                             value = 0;
    324                     } else if (i != 0) // Other singletons only belong in the first layer.
    325                         value = 0;
    326                 }
    327             }
    328 
    329             // We need to report background-repeat as it was written in the CSS. If the property is implicit,
    330             // then it was written with only one value. Here we figure out which value that was so we can
    331             // report back correctly.
    332             if (properties[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(properties[j])) {
    333                 if (j < number - 1 && properties[j + 1] == CSSPropertyBackgroundRepeatY) {
    334                     RefPtr<CSSValue> yValue;
    335                     RefPtr<CSSValue> nextValue = values[j + 1];
    336                     if (nextValue->isValueList())
    337                         yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
    338                     else
    339                         yValue = nextValue;
    340 
    341                     int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
    342                     int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
    343                     if (xId != yId) {
    344                         if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
    345                             useRepeatXShorthand = true;
    346                             ++j;
    347                         } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
    348                             useRepeatYShorthand = true;
    349                             continue;
    350                         }
    351                     } else {
    352                         useSingleWordShorthand = true;
    353                         ++j;
    354                     }
    355                 }
    356             }
    357 
    358             if (value && !value->isImplicitInitialValue()) {
    359                 if (!layerRes.isNull())
    360                     layerRes += " ";
    361                 if (useRepeatXShorthand) {
    362                     useRepeatXShorthand = false;
    363                     layerRes += getValueName(CSSValueRepeatX);
    364                 } else if (useRepeatYShorthand) {
    365                     useRepeatYShorthand = false;
    366                     layerRes += getValueName(CSSValueRepeatY);
    367                 } else if (useSingleWordShorthand) {
    368                     useSingleWordShorthand = false;
    369                     layerRes += value->cssText();
    370                 } else
    371                     layerRes += value->cssText();
    372             }
    373         }
    374 
    375         if (!layerRes.isNull()) {
    376             if (!res.isNull())
    377                 res += ", ";
    378             res += layerRes;
    379         }
    380     }
    381 
    382     return res;
    383 }
    384 
    385 String CSSMutableStyleDeclaration::getShorthandValue(const int* properties, int number) const
    386 {
    387     String res;
    388     for (int i = 0; i < number; ++i) {
    389         if (!isPropertyImplicit(properties[i])) {
    390             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
    391             // FIXME: provide default value if !value
    392             if (value) {
    393                 if (!res.isNull())
    394                     res += " ";
    395                 res += value->cssText();
    396             }
    397         }
    398     }
    399     return res;
    400 }
    401 
    402 // only returns a non-null value if all properties have the same, non-null value
    403 String CSSMutableStyleDeclaration::getCommonValue(const int* properties, int number) const
    404 {
    405     String res;
    406     for (int i = 0; i < number; ++i) {
    407         if (!isPropertyImplicit(properties[i])) {
    408             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
    409             if (!value)
    410                 return String();
    411             String text = value->cssText();
    412             if (text.isNull())
    413                 return String();
    414             if (res.isNull())
    415                 res = text;
    416             else if (res != text)
    417                 return String();
    418         }
    419     }
    420     return res;
    421 }
    422 
    423 PassRefPtr<CSSValue> CSSMutableStyleDeclaration::getPropertyCSSValue(int propertyID) const
    424 {
    425     const CSSProperty* property = findPropertyWithId(propertyID);
    426     return property ? property->value() : 0;
    427 }
    428 
    429 bool CSSMutableStyleDeclaration::removeShorthandProperty(int propertyID, bool notifyChanged)
    430 {
    431     CSSPropertyLonghand longhand = longhandForProperty(propertyID);
    432     if (longhand.length()) {
    433         removePropertiesInSet(longhand.properties(), longhand.length(), notifyChanged);
    434         return true;
    435     }
    436     return false;
    437 }
    438 
    439 String CSSMutableStyleDeclaration::removeProperty(int propertyID, bool notifyChanged, bool returnText)
    440 {
    441     ASSERT(!m_iteratorCount);
    442 
    443     if (removeShorthandProperty(propertyID, notifyChanged)) {
    444         // FIXME: Return an equivalent shorthand when possible.
    445         return String();
    446     }
    447 
    448     CSSProperty* foundProperty = findPropertyWithId(propertyID);
    449     if (!foundProperty)
    450         return String();
    451 
    452     String value = returnText ? foundProperty->value()->cssText() : String();
    453 
    454     if (foundProperty->value()->isVariableDependentValue())
    455         m_variableDependentValueCount--;
    456 
    457     // A more efficient removal strategy would involve marking entries as empty
    458     // and sweeping them when the vector grows too big.
    459     m_properties.remove(foundProperty - m_properties.data());
    460 
    461     if (notifyChanged)
    462         setNeedsStyleRecalc();
    463 
    464     return value;
    465 }
    466 
    467 void CSSMutableStyleDeclaration::setNeedsStyleRecalc()
    468 {
    469     if (m_node) {
    470         // FIXME: Ideally, this should be factored better and there
    471         // should be a subclass of CSSMutableStyleDeclaration just
    472         // for inline style declarations that handles this
    473         bool isInlineStyleDeclaration = m_node->isStyledElement() && this == static_cast<StyledElement*>(m_node)->inlineStyleDecl();
    474         if (isInlineStyleDeclaration) {
    475             m_node->setNeedsStyleRecalc(InlineStyleChange);
    476             static_cast<StyledElement*>(m_node)->invalidateStyleAttribute();
    477         } else
    478             m_node->setNeedsStyleRecalc(FullStyleChange);
    479         return;
    480     }
    481 
    482     // FIXME: quick&dirty hack for KDE 3.0... make this MUCH better! (Dirk)
    483     StyleBase* root = this;
    484     while (StyleBase* parent = root->parent())
    485         root = parent;
    486     if (root->isCSSStyleSheet())
    487         static_cast<CSSStyleSheet*>(root)->doc()->updateStyleSelector();
    488 }
    489 
    490 bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const
    491 {
    492     const CSSProperty* property = findPropertyWithId(propertyID);
    493     return property ? property->isImportant() : false;
    494 }
    495 
    496 int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const
    497 {
    498     const CSSProperty* property = findPropertyWithId(propertyID);
    499     return property ? property->shorthandID() : 0;
    500 }
    501 
    502 bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const
    503 {
    504     const CSSProperty* property = findPropertyWithId(propertyID);
    505     return property ? property->isImplicit() : false;
    506 }
    507 
    508 void CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, ExceptionCode& ec)
    509 {
    510     ec = 0;
    511     setProperty(propertyID, value, important, true);
    512 }
    513 
    514 String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec)
    515 {
    516     ec = 0;
    517     return removeProperty(propertyID, true, true);
    518 }
    519 
    520 bool CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, bool notifyChanged)
    521 {
    522     ASSERT(!m_iteratorCount);
    523 
    524     // Setting the value to an empty string just removes the property in both IE and Gecko.
    525     // Setting it to null seems to produce less consistent results, but we treat it just the same.
    526     if (value.isEmpty()) {
    527         removeProperty(propertyID, notifyChanged, false);
    528         return true;
    529     }
    530 
    531     // When replacing an existing property value, this moves the property to the end of the list.
    532     // Firefox preserves the position, and MSIE moves the property to the beginning.
    533     CSSParser parser(useStrictParsing());
    534     bool success = parser.parseValue(this, propertyID, value, important);
    535     if (!success) {
    536         // CSS DOM requires raising SYNTAX_ERR here, but this is too dangerous for compatibility,
    537         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
    538     } else if (notifyChanged)
    539         setNeedsStyleRecalc();
    540 
    541     return success;
    542 }
    543 
    544 void CSSMutableStyleDeclaration::setPropertyInternal(const CSSProperty& property, CSSProperty* slot)
    545 {
    546     ASSERT(!m_iteratorCount);
    547 
    548     if (!removeShorthandProperty(property.id(), false)) {
    549         CSSProperty* toReplace = slot ? slot : findPropertyWithId(property.id());
    550         if (toReplace) {
    551             *toReplace = property;
    552             return;
    553         }
    554     }
    555     m_properties.append(property);
    556 }
    557 
    558 bool CSSMutableStyleDeclaration::setProperty(int propertyID, int value, bool important, bool notifyChanged)
    559 {
    560     CSSProperty property(propertyID, CSSPrimitiveValue::createIdentifier(value), important);
    561     setPropertyInternal(property);
    562     if (notifyChanged)
    563         setNeedsStyleRecalc();
    564     return true;
    565 }
    566 
    567 void CSSMutableStyleDeclaration::setStringProperty(int propertyId, const String &value, CSSPrimitiveValue::UnitTypes type, bool important)
    568 {
    569     ASSERT(!m_iteratorCount);
    570 
    571     setPropertyInternal(CSSProperty(propertyId, CSSPrimitiveValue::create(value, type), important));
    572     setNeedsStyleRecalc();
    573 }
    574 
    575 void CSSMutableStyleDeclaration::setImageProperty(int propertyId, const String& url, bool important)
    576 {
    577     ASSERT(!m_iteratorCount);
    578 
    579     setPropertyInternal(CSSProperty(propertyId, CSSImageValue::create(url), important));
    580     setNeedsStyleRecalc();
    581 }
    582 
    583 void CSSMutableStyleDeclaration::parseDeclaration(const String& styleDeclaration)
    584 {
    585     ASSERT(!m_iteratorCount);
    586 
    587     m_properties.clear();
    588     CSSParser parser(useStrictParsing());
    589     parser.parseDeclaration(this, styleDeclaration);
    590     setNeedsStyleRecalc();
    591 }
    592 
    593 void CSSMutableStyleDeclaration::addParsedProperties(const CSSProperty* const* properties, int numProperties)
    594 {
    595     ASSERT(!m_iteratorCount);
    596 
    597     m_properties.reserveCapacity(numProperties);
    598 
    599     for (int i = 0; i < numProperties; ++i) {
    600         // Only add properties that have no !important counterpart present
    601         if (!getPropertyPriority(properties[i]->id()) || properties[i]->isImportant()) {
    602             removeProperty(properties[i]->id(), false);
    603             ASSERT(properties[i]);
    604             m_properties.append(*properties[i]);
    605             if (properties[i]->value()->isVariableDependentValue())
    606                 m_variableDependentValueCount++;
    607         }
    608     }
    609     // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
    610     // a notifyChanged argument to this function to follow the model of other functions in this class.
    611 }
    612 
    613 void CSSMutableStyleDeclaration::addParsedProperty(const CSSProperty& property)
    614 {
    615     ASSERT(!m_iteratorCount);
    616 
    617     setPropertyInternal(property);
    618 }
    619 
    620 void CSSMutableStyleDeclaration::setLengthProperty(int propertyId, const String& value, bool important, bool /*multiLength*/)
    621 {
    622     ASSERT(!m_iteratorCount);
    623 
    624     bool parseMode = useStrictParsing();
    625     setStrictParsing(false);
    626     setProperty(propertyId, value, important);
    627     setStrictParsing(parseMode);
    628 }
    629 
    630 unsigned CSSMutableStyleDeclaration::length() const
    631 {
    632     return m_properties.size();
    633 }
    634 
    635 String CSSMutableStyleDeclaration::item(unsigned i) const
    636 {
    637     if (i >= m_properties.size())
    638        return "";
    639     return getPropertyName(static_cast<CSSPropertyID>(m_properties[i].id()));
    640 }
    641 
    642 String CSSMutableStyleDeclaration::cssText() const
    643 {
    644     String result = "";
    645 
    646     const CSSProperty* positionXProp = 0;
    647     const CSSProperty* positionYProp = 0;
    648     const CSSProperty* repeatXProp = 0;
    649     const CSSProperty* repeatYProp = 0;
    650 
    651     unsigned size = m_properties.size();
    652     for (unsigned n = 0; n < size; ++n) {
    653         const CSSProperty& prop = m_properties[n];
    654         if (prop.id() == CSSPropertyBackgroundPositionX)
    655             positionXProp = &prop;
    656         else if (prop.id() == CSSPropertyBackgroundPositionY)
    657             positionYProp = &prop;
    658         else if (prop.id() == CSSPropertyBackgroundRepeatX)
    659             repeatXProp = &prop;
    660         else if (prop.id() == CSSPropertyBackgroundRepeatY)
    661             repeatYProp = &prop;
    662         else
    663             result += prop.cssText();
    664     }
    665 
    666     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
    667     // It is required because background-position-x/y are non-standard properties and WebKit generated output
    668     // would not work in Firefox (<rdar://problem/5143183>)
    669     // It would be a better solution if background-position was CSS_PAIR.
    670     if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) {
    671         String positionValue;
    672         const int properties[2] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
    673         if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList())
    674             positionValue = getLayeredShorthandValue(properties, 2);
    675         else
    676             positionValue = positionXProp->value()->cssText() + " " + positionYProp->value()->cssText();
    677         result += "background-position: " + positionValue + (positionXProp->isImportant() ? " !important" : "") + "; ";
    678     } else {
    679         if (positionXProp)
    680             result += positionXProp->cssText();
    681         if (positionYProp)
    682             result += positionYProp->cssText();
    683     }
    684 
    685     // FIXME: We need to do the same for background-repeat.
    686     if (repeatXProp && repeatYProp && repeatXProp->isImportant() == repeatYProp->isImportant()) {
    687         String repeatValue;
    688         const int repeatProperties[2] = { CSSPropertyBackgroundRepeatX, CSSPropertyBackgroundRepeatY };
    689         if (repeatXProp->value()->isValueList() || repeatYProp->value()->isValueList())
    690             repeatValue = getLayeredShorthandValue(repeatProperties, 2);
    691         else
    692             repeatValue = repeatXProp->value()->cssText() + " " + repeatYProp->value()->cssText();
    693         result += "background-repeat: " + repeatValue + (repeatXProp->isImportant() ? " !important" : "") + "; ";
    694     } else {
    695         if (repeatXProp)
    696             result += repeatXProp->cssText();
    697         if (repeatYProp)
    698             result += repeatYProp->cssText();
    699     }
    700 
    701     return result;
    702 }
    703 
    704 void CSSMutableStyleDeclaration::setCssText(const String& text, ExceptionCode& ec)
    705 {
    706     ASSERT(!m_iteratorCount);
    707 
    708     ec = 0;
    709     m_properties.clear();
    710     CSSParser parser(useStrictParsing());
    711     parser.parseDeclaration(this, text);
    712     // FIXME: Detect syntax errors and set ec.
    713     setNeedsStyleRecalc();
    714 }
    715 
    716 void CSSMutableStyleDeclaration::merge(CSSMutableStyleDeclaration* other, bool argOverridesOnConflict)
    717 {
    718     ASSERT(!m_iteratorCount);
    719 
    720     unsigned size = other->m_properties.size();
    721     for (unsigned n = 0; n < size; ++n) {
    722         CSSProperty& toMerge = other->m_properties[n];
    723         CSSProperty* old = findPropertyWithId(toMerge.id());
    724         if (old) {
    725             if (!argOverridesOnConflict && old->value())
    726                 continue;
    727             setPropertyInternal(toMerge, old);
    728         } else
    729             m_properties.append(toMerge);
    730     }
    731     // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
    732     // a notifyChanged argument to this function to follow the model of other functions in this class.
    733 }
    734 
    735 void CSSMutableStyleDeclaration::addSubresourceStyleURLs(ListHashSet<KURL>& urls)
    736 {
    737     CSSStyleSheet* sheet = static_cast<CSSStyleSheet*>(stylesheet());
    738     size_t size = m_properties.size();
    739     for (size_t i = 0; i < size; ++i)
    740         m_properties[i].value()->addSubresourceStyleURLs(urls, sheet);
    741 }
    742 
    743 // This is the list of properties we want to copy in the copyBlockProperties() function.
    744 // It is the list of CSS properties that apply specially to block-level elements.
    745 static const int blockProperties[] = {
    746     CSSPropertyOrphans,
    747     CSSPropertyOverflow, // This can be also be applied to replaced elements
    748     CSSPropertyWebkitColumnCount,
    749     CSSPropertyWebkitColumnGap,
    750     CSSPropertyWebkitColumnRuleColor,
    751     CSSPropertyWebkitColumnRuleStyle,
    752     CSSPropertyWebkitColumnRuleWidth,
    753     CSSPropertyWebkitColumnBreakBefore,
    754     CSSPropertyWebkitColumnBreakAfter,
    755     CSSPropertyWebkitColumnBreakInside,
    756     CSSPropertyWebkitColumnWidth,
    757     CSSPropertyPageBreakAfter,
    758     CSSPropertyPageBreakBefore,
    759     CSSPropertyPageBreakInside,
    760     CSSPropertyTextAlign,
    761     CSSPropertyTextIndent,
    762     CSSPropertyWidows
    763 };
    764 
    765 const unsigned numBlockProperties = sizeof(blockProperties) / sizeof(blockProperties[0]);
    766 
    767 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copyBlockProperties() const
    768 {
    769     return copyPropertiesInSet(blockProperties, numBlockProperties);
    770 }
    771 
    772 void CSSMutableStyleDeclaration::removeBlockProperties()
    773 {
    774     removePropertiesInSet(blockProperties, numBlockProperties);
    775 }
    776 
    777 void CSSMutableStyleDeclaration::removePropertiesInSet(const int* set, unsigned length, bool notifyChanged)
    778 {
    779     ASSERT(!m_iteratorCount);
    780 
    781     if (m_properties.isEmpty())
    782         return;
    783 
    784     // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
    785     HashSet<int> toRemove;
    786     for (unsigned i = 0; i < length; ++i)
    787         toRemove.add(set[i]);
    788 
    789     Vector<CSSProperty, 4> newProperties;
    790     newProperties.reserveInitialCapacity(m_properties.size());
    791 
    792     unsigned size = m_properties.size();
    793     for (unsigned n = 0; n < size; ++n) {
    794         const CSSProperty& property = m_properties[n];
    795         // Not quite sure if the isImportant test is needed but it matches the existing behavior.
    796         if (!property.isImportant()) {
    797             if (toRemove.contains(property.id()))
    798                 continue;
    799         }
    800         newProperties.append(property);
    801     }
    802 
    803     bool changed = newProperties.size() != m_properties.size();
    804     m_properties = newProperties;
    805 
    806     if (changed && notifyChanged)
    807         setNeedsStyleRecalc();
    808 }
    809 
    810 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable()
    811 {
    812     return this;
    813 }
    814 
    815 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const
    816 {
    817     return adoptRef(new CSSMutableStyleDeclaration(0, m_properties, m_variableDependentValueCount));
    818 }
    819 
    820 const CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID) const
    821 {
    822     for (int n = m_properties.size() - 1 ; n >= 0; --n) {
    823         if (propertyID == m_properties[n].m_id)
    824             return &m_properties[n];
    825     }
    826     return 0;
    827 }
    828 
    829 CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID)
    830 {
    831     for (int n = m_properties.size() - 1 ; n >= 0; --n) {
    832         if (propertyID == m_properties[n].m_id)
    833             return &m_properties[n];
    834     }
    835     return 0;
    836 }
    837 
    838 } // namespace WebCore
    839