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