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     }
    265     return String();
    266 }
    267 
    268 String CSSMutableStyleDeclaration::borderSpacingValue(const int properties[2]) const
    269 {
    270     RefPtr<CSSValue> horizontalValue = getPropertyCSSValue(properties[0]);
    271     RefPtr<CSSValue> verticalValue = getPropertyCSSValue(properties[1]);
    272 
    273     if (!horizontalValue)
    274         return String();
    275     ASSERT(verticalValue); // By <http://www.w3.org/TR/CSS21/tables.html#separated-borders>.
    276 
    277     String horizontalValueCSSText = horizontalValue->cssText();
    278     String verticalValueCSSText = verticalValue->cssText();
    279     if (horizontalValueCSSText == verticalValueCSSText)
    280         return horizontalValueCSSText;
    281     return makeString(horizontalValueCSSText, ' ', verticalValueCSSText);
    282 }
    283 
    284 String CSSMutableStyleDeclaration::get4Values(const int* properties) const
    285 {
    286     // Assume the properties are in the usual order top, right, bottom, left.
    287     RefPtr<CSSValue> topValue = getPropertyCSSValue(properties[0]);
    288     RefPtr<CSSValue> rightValue = getPropertyCSSValue(properties[1]);
    289     RefPtr<CSSValue> bottomValue = getPropertyCSSValue(properties[2]);
    290     RefPtr<CSSValue> leftValue = getPropertyCSSValue(properties[3]);
    291 
    292     // All 4 properties must be specified.
    293     if (!topValue || !rightValue || !bottomValue || !leftValue)
    294         return String();
    295 
    296     bool showLeft = rightValue->cssText() != leftValue->cssText();
    297     bool showBottom = (topValue->cssText() != bottomValue->cssText()) || showLeft;
    298     bool showRight = (topValue->cssText() != rightValue->cssText()) || showBottom;
    299 
    300     String res = topValue->cssText();
    301     if (showRight)
    302         res += " " + rightValue->cssText();
    303     if (showBottom)
    304         res += " " + bottomValue->cssText();
    305     if (showLeft)
    306         res += " " + leftValue->cssText();
    307 
    308     return res;
    309 }
    310 
    311 String CSSMutableStyleDeclaration::getLayeredShorthandValue(const int* properties, size_t size) const
    312 {
    313     String res;
    314 
    315     // Begin by collecting the properties into an array.
    316     Vector< RefPtr<CSSValue> > values(size);
    317     size_t numLayers = 0;
    318 
    319     for (size_t i = 0; i < size; ++i) {
    320         values[i] = getPropertyCSSValue(properties[i]);
    321         if (values[i]) {
    322             if (values[i]->isValueList()) {
    323                 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
    324                 numLayers = max(valueList->length(), numLayers);
    325             } else
    326                 numLayers = max<size_t>(1U, numLayers);
    327         }
    328     }
    329 
    330     // Now stitch the properties together.  Implicit initial values are flagged as such and
    331     // can safely be omitted.
    332     for (size_t i = 0; i < numLayers; i++) {
    333         String layerRes;
    334         bool useRepeatXShorthand = false;
    335         bool useRepeatYShorthand = false;
    336         bool useSingleWordShorthand = false;
    337         for (size_t j = 0; j < size; j++) {
    338             RefPtr<CSSValue> value;
    339             if (values[j]) {
    340                 if (values[j]->isValueList())
    341                     value = static_cast<CSSValueList*>(values[j].get())->item(i);
    342                 else {
    343                     value = values[j];
    344 
    345                     // Color only belongs in the last layer.
    346                     if (properties[j] == CSSPropertyBackgroundColor) {
    347                         if (i != numLayers - 1)
    348                             value = 0;
    349                     } else if (i != 0) // Other singletons only belong in the first layer.
    350                         value = 0;
    351                 }
    352             }
    353 
    354             // We need to report background-repeat as it was written in the CSS. If the property is implicit,
    355             // then it was written with only one value. Here we figure out which value that was so we can
    356             // report back correctly.
    357             if (properties[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(properties[j])) {
    358 
    359                 // BUG 49055: make sure the value was not reset in the layer check just above.
    360                 if (j < size - 1 && properties[j + 1] == CSSPropertyBackgroundRepeatY && value) {
    361                     RefPtr<CSSValue> yValue;
    362                     RefPtr<CSSValue> nextValue = values[j + 1];
    363                     if (nextValue->isValueList())
    364                         yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
    365                     else
    366                         yValue = nextValue;
    367 
    368                     int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
    369                     int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
    370                     if (xId != yId) {
    371                         if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
    372                             useRepeatXShorthand = true;
    373                             ++j;
    374                         } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
    375                             useRepeatYShorthand = true;
    376                             continue;
    377                         }
    378                     } else {
    379                         useSingleWordShorthand = true;
    380                         ++j;
    381                     }
    382                 }
    383             }
    384 
    385             if (value && !value->isImplicitInitialValue()) {
    386                 if (!layerRes.isNull())
    387                     layerRes += " ";
    388                 if (useRepeatXShorthand) {
    389                     useRepeatXShorthand = false;
    390                     layerRes += getValueName(CSSValueRepeatX);
    391                 } else if (useRepeatYShorthand) {
    392                     useRepeatYShorthand = false;
    393                     layerRes += getValueName(CSSValueRepeatY);
    394                 } else if (useSingleWordShorthand) {
    395                     useSingleWordShorthand = false;
    396                     layerRes += value->cssText();
    397                 } else
    398                     layerRes += value->cssText();
    399             }
    400         }
    401 
    402         if (!layerRes.isNull()) {
    403             if (!res.isNull())
    404                 res += ", ";
    405             res += layerRes;
    406         }
    407     }
    408 
    409     return res;
    410 }
    411 
    412 String CSSMutableStyleDeclaration::getShorthandValue(const int* properties, size_t size) const
    413 {
    414     String res;
    415     for (size_t i = 0; i < size; ++i) {
    416         if (!isPropertyImplicit(properties[i])) {
    417             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
    418             // FIXME: provide default value if !value
    419             if (value) {
    420                 if (!res.isNull())
    421                     res += " ";
    422                 res += value->cssText();
    423             }
    424         }
    425     }
    426     return res;
    427 }
    428 
    429 // only returns a non-null value if all properties have the same, non-null value
    430 String CSSMutableStyleDeclaration::getCommonValue(const int* properties, size_t size) const
    431 {
    432     String res;
    433     for (size_t i = 0; i < size; ++i) {
    434         RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
    435         if (!value)
    436             return String();
    437         String text = value->cssText();
    438         if (text.isNull())
    439             return String();
    440         if (res.isNull())
    441             res = text;
    442         else if (res != text)
    443             return String();
    444     }
    445     return res;
    446 }
    447 
    448 PassRefPtr<CSSValue> CSSMutableStyleDeclaration::getPropertyCSSValue(int propertyID) const
    449 {
    450     const CSSProperty* property = findPropertyWithId(propertyID);
    451     return property ? property->value() : 0;
    452 }
    453 
    454 bool CSSMutableStyleDeclaration::removeShorthandProperty(int propertyID, bool notifyChanged)
    455 {
    456     CSSPropertyLonghand longhand = longhandForProperty(propertyID);
    457     if (longhand.length()) {
    458         removePropertiesInSet(longhand.properties(), longhand.length(), notifyChanged);
    459         return true;
    460     }
    461     return false;
    462 }
    463 
    464 String CSSMutableStyleDeclaration::removeProperty(int propertyID, bool notifyChanged, bool returnText)
    465 {
    466     ASSERT(!m_iteratorCount);
    467 
    468     if (removeShorthandProperty(propertyID, notifyChanged)) {
    469         // FIXME: Return an equivalent shorthand when possible.
    470         return String();
    471     }
    472 
    473     CSSProperty* foundProperty = findPropertyWithId(propertyID);
    474     if (!foundProperty)
    475         return String();
    476 
    477     String value = returnText ? foundProperty->value()->cssText() : String();
    478 
    479     // A more efficient removal strategy would involve marking entries as empty
    480     // and sweeping them when the vector grows too big.
    481     m_properties.remove(foundProperty - m_properties.data());
    482 
    483     if (notifyChanged)
    484         setNeedsStyleRecalc();
    485 
    486     return value;
    487 }
    488 
    489 bool CSSMutableStyleDeclaration::isInlineStyleDeclaration()
    490 {
    491     // FIXME: Ideally, this should be factored better and there
    492     // should be a subclass of CSSMutableStyleDeclaration just
    493     // for inline style declarations that handles this
    494     return m_node && m_node->isStyledElement() && static_cast<StyledElement*>(m_node)->inlineStyleDecl() == this;
    495 }
    496 
    497 void CSSMutableStyleDeclaration::setNeedsStyleRecalc()
    498 {
    499     if (m_node) {
    500         if (isInlineStyleDeclaration()) {
    501             m_node->setNeedsStyleRecalc(InlineStyleChange);
    502             static_cast<StyledElement*>(m_node)->invalidateStyleAttribute();
    503             if (m_node->document())
    504                 InspectorInstrumentation::didInvalidateStyleAttr(m_node->document(), m_node);
    505         } else
    506             m_node->setNeedsStyleRecalc(FullStyleChange);
    507         return;
    508     }
    509 
    510     StyleBase* root = this;
    511     while (StyleBase* parent = root->parent())
    512         root = parent;
    513     if (root->isCSSStyleSheet()) {
    514         if (Document* document = static_cast<CSSStyleSheet*>(root)->document())
    515             document->styleSelectorChanged(DeferRecalcStyle);
    516     }
    517 }
    518 
    519 bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const
    520 {
    521     const CSSProperty* property = findPropertyWithId(propertyID);
    522     return property ? property->isImportant() : false;
    523 }
    524 
    525 int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const
    526 {
    527     const CSSProperty* property = findPropertyWithId(propertyID);
    528     return property ? property->shorthandID() : 0;
    529 }
    530 
    531 bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const
    532 {
    533     const CSSProperty* property = findPropertyWithId(propertyID);
    534     return property ? property->isImplicit() : false;
    535 }
    536 
    537 void CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, ExceptionCode& ec)
    538 {
    539     ec = 0;
    540     setProperty(propertyID, value, important, true);
    541 }
    542 
    543 String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec)
    544 {
    545     ec = 0;
    546     return removeProperty(propertyID, true, true);
    547 }
    548 
    549 bool CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, bool notifyChanged)
    550 {
    551     ASSERT(!m_iteratorCount);
    552 
    553     // Setting the value to an empty string just removes the property in both IE and Gecko.
    554     // Setting it to null seems to produce less consistent results, but we treat it just the same.
    555     if (value.isEmpty()) {
    556         removeProperty(propertyID, notifyChanged, false);
    557         return true;
    558     }
    559 
    560     // When replacing an existing property value, this moves the property to the end of the list.
    561     // Firefox preserves the position, and MSIE moves the property to the beginning.
    562     bool success = CSSParser::parseValue(this, propertyID, value, important, useStrictParsing());
    563     if (!success) {
    564         // CSS DOM requires raising SYNTAX_ERR here, but this is too dangerous for compatibility,
    565         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
    566     } else if (notifyChanged)
    567         setNeedsStyleRecalc();
    568 
    569     return success;
    570 }
    571 
    572 void CSSMutableStyleDeclaration::setPropertyInternal(const CSSProperty& property, CSSProperty* slot)
    573 {
    574     ASSERT(!m_iteratorCount);
    575 
    576     if (!removeShorthandProperty(property.id(), false)) {
    577         CSSProperty* toReplace = slot ? slot : findPropertyWithId(property.id());
    578         if (toReplace) {
    579             *toReplace = property;
    580             return;
    581         }
    582     }
    583     m_properties.append(property);
    584 }
    585 
    586 bool CSSMutableStyleDeclaration::setProperty(int propertyID, int value, bool important, bool notifyChanged)
    587 {
    588     CSSProperty property(propertyID, CSSPrimitiveValue::createIdentifier(value), important);
    589     setPropertyInternal(property);
    590     if (notifyChanged)
    591         setNeedsStyleRecalc();
    592     return true;
    593 }
    594 
    595 bool CSSMutableStyleDeclaration::setProperty(int propertyID, double value, CSSPrimitiveValue::UnitTypes unit, bool important, bool notifyChanged)
    596 {
    597     CSSProperty property(propertyID, CSSPrimitiveValue::create(value, unit), important);
    598     setPropertyInternal(property);
    599     if (notifyChanged)
    600         setNeedsStyleRecalc();
    601     return true;
    602 }
    603 
    604 void CSSMutableStyleDeclaration::setStringProperty(int propertyId, const String &value, CSSPrimitiveValue::UnitTypes type, bool important)
    605 {
    606     ASSERT(!m_iteratorCount);
    607 
    608     setPropertyInternal(CSSProperty(propertyId, CSSPrimitiveValue::create(value, type), important));
    609     setNeedsStyleRecalc();
    610 }
    611 
    612 void CSSMutableStyleDeclaration::setImageProperty(int propertyId, const String& url, bool important)
    613 {
    614     ASSERT(!m_iteratorCount);
    615 
    616     setPropertyInternal(CSSProperty(propertyId, CSSImageValue::create(url), important));
    617     setNeedsStyleRecalc();
    618 }
    619 
    620 void CSSMutableStyleDeclaration::parseDeclaration(const String& styleDeclaration)
    621 {
    622     ASSERT(!m_iteratorCount);
    623 
    624     m_properties.clear();
    625     CSSParser parser(useStrictParsing());
    626     parser.parseDeclaration(this, styleDeclaration);
    627     setNeedsStyleRecalc();
    628 }
    629 
    630 void CSSMutableStyleDeclaration::addParsedProperties(const CSSProperty* const* properties, int numProperties)
    631 {
    632     ASSERT(!m_iteratorCount);
    633 
    634     m_properties.reserveCapacity(numProperties);
    635 
    636     for (int i = 0; i < numProperties; ++i) {
    637         // Only add properties that have no !important counterpart present
    638         if (!getPropertyPriority(properties[i]->id()) || properties[i]->isImportant()) {
    639             removeProperty(properties[i]->id(), false);
    640             ASSERT(properties[i]);
    641             m_properties.append(*properties[i]);
    642         }
    643     }
    644     // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
    645     // a notifyChanged argument to this function to follow the model of other functions in this class.
    646 }
    647 
    648 void CSSMutableStyleDeclaration::addParsedProperty(const CSSProperty& property)
    649 {
    650     ASSERT(!m_iteratorCount);
    651 
    652     setPropertyInternal(property);
    653 }
    654 
    655 void CSSMutableStyleDeclaration::setLengthProperty(int propertyId, const String& value, bool important, bool /*multiLength*/)
    656 {
    657     ASSERT(!m_iteratorCount);
    658 
    659     bool parseMode = useStrictParsing();
    660     setStrictParsing(false);
    661     setProperty(propertyId, value, important);
    662     setStrictParsing(parseMode);
    663 }
    664 
    665 unsigned CSSMutableStyleDeclaration::virtualLength() const
    666 {
    667     return length();
    668 }
    669 
    670 String CSSMutableStyleDeclaration::item(unsigned i) const
    671 {
    672     if (i >= m_properties.size())
    673        return "";
    674     return getPropertyName(static_cast<CSSPropertyID>(m_properties[i].id()));
    675 }
    676 
    677 String CSSMutableStyleDeclaration::cssText() const
    678 {
    679     String result = "";
    680 
    681     const CSSProperty* positionXProp = 0;
    682     const CSSProperty* positionYProp = 0;
    683     const CSSProperty* repeatXProp = 0;
    684     const CSSProperty* repeatYProp = 0;
    685 
    686     unsigned size = m_properties.size();
    687     for (unsigned n = 0; n < size; ++n) {
    688         const CSSProperty& prop = m_properties[n];
    689         if (prop.id() == CSSPropertyBackgroundPositionX)
    690             positionXProp = &prop;
    691         else if (prop.id() == CSSPropertyBackgroundPositionY)
    692             positionYProp = &prop;
    693         else if (prop.id() == CSSPropertyBackgroundRepeatX)
    694             repeatXProp = &prop;
    695         else if (prop.id() == CSSPropertyBackgroundRepeatY)
    696             repeatYProp = &prop;
    697         else
    698             result += prop.cssText();
    699     }
    700 
    701     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
    702     // It is required because background-position-x/y are non-standard properties and WebKit generated output
    703     // would not work in Firefox (<rdar://problem/5143183>)
    704     // It would be a better solution if background-position was CSS_PAIR.
    705     if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) {
    706         String positionValue;
    707         const int properties[2] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
    708         if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList())
    709             positionValue = getLayeredShorthandValue(properties);
    710         else
    711             positionValue = positionXProp->value()->cssText() + " " + positionYProp->value()->cssText();
    712         result += "background-position: " + positionValue + (positionXProp->isImportant() ? " !important" : "") + "; ";
    713     } else {
    714         if (positionXProp)
    715             result += positionXProp->cssText();
    716         if (positionYProp)
    717             result += positionYProp->cssText();
    718     }
    719 
    720     // FIXME: We need to do the same for background-repeat.
    721     if (repeatXProp && repeatYProp && repeatXProp->isImportant() == repeatYProp->isImportant()) {
    722         String repeatValue;
    723         const int repeatProperties[2] = { CSSPropertyBackgroundRepeatX, CSSPropertyBackgroundRepeatY };
    724         if (repeatXProp->value()->isValueList() || repeatYProp->value()->isValueList())
    725             repeatValue = getLayeredShorthandValue(repeatProperties);
    726         else
    727             repeatValue = repeatXProp->value()->cssText() + " " + repeatYProp->value()->cssText();
    728         result += "background-repeat: " + repeatValue + (repeatXProp->isImportant() ? " !important" : "") + "; ";
    729     } else {
    730         if (repeatXProp)
    731             result += repeatXProp->cssText();
    732         if (repeatYProp)
    733             result += repeatYProp->cssText();
    734     }
    735 
    736     return result;
    737 }
    738 
    739 void CSSMutableStyleDeclaration::setCssText(const String& text, ExceptionCode& ec)
    740 {
    741     ASSERT(!m_iteratorCount);
    742 
    743     ec = 0;
    744     m_properties.clear();
    745     CSSParser parser(useStrictParsing());
    746     parser.parseDeclaration(this, text);
    747     // FIXME: Detect syntax errors and set ec.
    748     setNeedsStyleRecalc();
    749 }
    750 
    751 void CSSMutableStyleDeclaration::merge(const CSSMutableStyleDeclaration* other, bool argOverridesOnConflict)
    752 {
    753     ASSERT(!m_iteratorCount);
    754 
    755     unsigned size = other->m_properties.size();
    756     for (unsigned n = 0; n < size; ++n) {
    757         const CSSProperty& toMerge = other->m_properties[n];
    758         CSSProperty* old = findPropertyWithId(toMerge.id());
    759         if (old) {
    760             if (!argOverridesOnConflict && old->value())
    761                 continue;
    762             setPropertyInternal(toMerge, old);
    763         } else
    764             m_properties.append(toMerge);
    765     }
    766     // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
    767     // a notifyChanged argument to this function to follow the model of other functions in this class.
    768 }
    769 
    770 void CSSMutableStyleDeclaration::addSubresourceStyleURLs(ListHashSet<KURL>& urls)
    771 {
    772     CSSStyleSheet* sheet = static_cast<CSSStyleSheet*>(stylesheet());
    773     size_t size = m_properties.size();
    774     for (size_t i = 0; i < size; ++i)
    775         m_properties[i].value()->addSubresourceStyleURLs(urls, sheet);
    776 }
    777 
    778 // This is the list of properties we want to copy in the copyBlockProperties() function.
    779 // It is the list of CSS properties that apply specially to block-level elements.
    780 static const int blockProperties[] = {
    781     CSSPropertyOrphans,
    782     CSSPropertyOverflow, // This can be also be applied to replaced elements
    783     CSSPropertyWebkitColumnCount,
    784     CSSPropertyWebkitColumnGap,
    785     CSSPropertyWebkitColumnRuleColor,
    786     CSSPropertyWebkitColumnRuleStyle,
    787     CSSPropertyWebkitColumnRuleWidth,
    788     CSSPropertyWebkitColumnBreakBefore,
    789     CSSPropertyWebkitColumnBreakAfter,
    790     CSSPropertyWebkitColumnBreakInside,
    791     CSSPropertyWebkitColumnWidth,
    792     CSSPropertyPageBreakAfter,
    793     CSSPropertyPageBreakBefore,
    794     CSSPropertyPageBreakInside,
    795     CSSPropertyTextAlign,
    796     CSSPropertyTextIndent,
    797     CSSPropertyWidows
    798 };
    799 
    800 const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
    801 
    802 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copyBlockProperties() const
    803 {
    804     return copyPropertiesInSet(blockProperties, numBlockProperties);
    805 }
    806 
    807 void CSSMutableStyleDeclaration::removeBlockProperties()
    808 {
    809     removePropertiesInSet(blockProperties, numBlockProperties);
    810 }
    811 
    812 void CSSMutableStyleDeclaration::removePropertiesInSet(const int* set, unsigned length, bool notifyChanged)
    813 {
    814     ASSERT(!m_iteratorCount);
    815 
    816     if (m_properties.isEmpty())
    817         return;
    818 
    819     // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
    820     HashSet<int> toRemove;
    821     for (unsigned i = 0; i < length; ++i)
    822         toRemove.add(set[i]);
    823 
    824     Vector<CSSProperty, 4> newProperties;
    825     newProperties.reserveInitialCapacity(m_properties.size());
    826 
    827     unsigned size = m_properties.size();
    828     for (unsigned n = 0; n < size; ++n) {
    829         const CSSProperty& property = m_properties[n];
    830         // Not quite sure if the isImportant test is needed but it matches the existing behavior.
    831         if (!property.isImportant()) {
    832             if (toRemove.contains(property.id()))
    833                 continue;
    834         }
    835         newProperties.append(property);
    836     }
    837 
    838     bool changed = newProperties.size() != m_properties.size();
    839     m_properties = newProperties;
    840 
    841     if (changed && notifyChanged)
    842         setNeedsStyleRecalc();
    843 }
    844 
    845 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable()
    846 {
    847     return this;
    848 }
    849 
    850 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const
    851 {
    852     return adoptRef(new CSSMutableStyleDeclaration(0, m_properties));
    853 }
    854 
    855 const CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID) const
    856 {
    857     for (int n = m_properties.size() - 1 ; n >= 0; --n) {
    858         if (propertyID == m_properties[n].m_id)
    859             return &m_properties[n];
    860     }
    861     return 0;
    862 }
    863 
    864 CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID)
    865 {
    866     for (int n = m_properties.size() - 1 ; n >= 0; --n) {
    867         if (propertyID == m_properties[n].m_id)
    868             return &m_properties[n];
    869     }
    870     return 0;
    871 }
    872 
    873 } // namespace WebCore
    874