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