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