403cc187df0dc5ebd88caa3bee902747cb054105
[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 "CSSPropertyNames.h"
28 #include "CSSStyleSheet.h"
29 #include "CSSValueList.h"
30 #include "Document.h"
31 #include "ExceptionCode.h"
32 #include "StyledElement.h"
33
34 using namespace std;
35
36 namespace WebCore {
37
38 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration()
39     : m_node(0)
40 {
41 }
42
43 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent)
44     : CSSStyleDeclaration(parent)
45     , m_node(0)
46 {
47 }
48
49 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const DeprecatedValueList<CSSProperty>& values)
50     : CSSStyleDeclaration(parent)
51     , m_values(values)
52     , m_node(0)
53 {
54     // FIXME: This allows duplicate properties.
55 }
56
57 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const CSSProperty* const * properties, int numProperties)
58     : CSSStyleDeclaration(parent)
59     , m_node(0)
60 {
61     for (int i = 0; i < numProperties; ++i) {
62         ASSERT(properties[i]);
63         m_values.append(*properties[i]);
64     }
65     // FIXME: This allows duplicate properties.
66 }
67
68 CSSMutableStyleDeclaration& CSSMutableStyleDeclaration::operator=(const CSSMutableStyleDeclaration& other)
69 {
70     // don't attach it to the same node, just leave the current m_node value
71     m_values = other.m_values;
72     return *this;
73 }
74
75 String CSSMutableStyleDeclaration::getPropertyValue(int propertyID) const
76 {
77     RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
78     if (value)
79         return value->cssText();
80
81     // Shorthand and 4-values properties
82     switch (propertyID) {
83         case CSSPropertyBackgroundPosition: {
84             // FIXME: Is this correct? The code in cssparser.cpp is confusing
85             const int properties[2] = { CSSPropertyBackgroundPositionX,
86                                         CSSPropertyBackgroundPositionY };
87             return getLayeredShorthandValue(properties, 2);
88         }
89         case CSSPropertyBackground: {
90             const int properties[6] = { CSSPropertyBackgroundImage, CSSPropertyBackgroundRepeat,
91                                         CSSPropertyBackgroundAttachment, CSSPropertyBackgroundPositionX,
92                                         CSSPropertyBackgroundPositionY, CSSPropertyBackgroundColor };
93             return getLayeredShorthandValue(properties, 6);
94         }
95         case CSSPropertyBorder: {
96             const int properties[3] = { CSSPropertyBorderWidth, CSSPropertyBorderStyle,
97                                         CSSPropertyBorderColor };
98             return getShorthandValue(properties, 3);
99         }
100         case CSSPropertyBorderTop: {
101             const int properties[3] = { CSSPropertyBorderTopWidth, CSSPropertyBorderTopStyle,
102                                         CSSPropertyBorderTopColor};
103             return getShorthandValue(properties, 3);
104         }
105         case CSSPropertyBorderRight: {
106             const int properties[3] = { CSSPropertyBorderRightWidth, CSSPropertyBorderRightStyle,
107                                         CSSPropertyBorderRightColor};
108             return getShorthandValue(properties, 3);
109         }
110         case CSSPropertyBorderBottom: {
111             const int properties[3] = { CSSPropertyBorderBottomWidth, CSSPropertyBorderBottomStyle,
112                                         CSSPropertyBorderBottomColor};
113             return getShorthandValue(properties, 3);
114         }
115         case CSSPropertyBorderLeft: {
116             const int properties[3] = { CSSPropertyBorderLeftWidth, CSSPropertyBorderLeftStyle,
117                                         CSSPropertyBorderLeftColor};
118             return getShorthandValue(properties, 3);
119         }
120         case CSSPropertyOutline: {
121             const int properties[3] = { CSSPropertyOutlineWidth, CSSPropertyOutlineStyle,
122                                         CSSPropertyOutlineColor };
123             return getShorthandValue(properties, 3);
124         }
125         case CSSPropertyBorderColor: {
126             const int properties[4] = { CSSPropertyBorderTopColor, CSSPropertyBorderRightColor,
127                                         CSSPropertyBorderBottomColor, CSSPropertyBorderLeftColor };
128             return get4Values(properties);
129         }
130         case CSSPropertyBorderWidth: {
131             const int properties[4] = { CSSPropertyBorderTopWidth, CSSPropertyBorderRightWidth,
132                                         CSSPropertyBorderBottomWidth, CSSPropertyBorderLeftWidth };
133             return get4Values(properties);
134         }
135         case CSSPropertyBorderStyle: {
136             const int properties[4] = { CSSPropertyBorderTopStyle, CSSPropertyBorderRightStyle,
137                                         CSSPropertyBorderBottomStyle, CSSPropertyBorderLeftStyle };
138             return get4Values(properties);
139         }
140         case CSSPropertyMargin: {
141             const int properties[4] = { CSSPropertyMarginTop, CSSPropertyMarginRight,
142                                         CSSPropertyMarginBottom, CSSPropertyMarginLeft };
143             return get4Values(properties);
144         }
145         case CSSPropertyPadding: {
146             const int properties[4] = { CSSPropertyPaddingTop, CSSPropertyPaddingRight,
147                                         CSSPropertyPaddingBottom, CSSPropertyPaddingLeft };
148             return get4Values(properties);
149         }
150         case CSSPropertyListStyle: {
151             const int properties[3] = { CSSPropertyListStyleType, CSSPropertyListStylePosition,
152                                         CSSPropertyListStyleImage };
153             return getShorthandValue(properties, 3);
154         }
155     }
156     return String();
157 }
158
159 String CSSMutableStyleDeclaration::get4Values( const int* properties ) const
160 {
161     String res;
162     for (int i = 0; i < 4; ++i) {
163         if (!isPropertyImplicit(properties[i])) {
164             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
165
166             // apparently all 4 properties must be specified.
167             if (!value)
168                 return String();
169
170             if (!res.isNull())
171                 res += " ";
172             res += value->cssText();
173         }
174     }
175     return res;
176 }
177
178 String CSSMutableStyleDeclaration::getLayeredShorthandValue(const int* properties, unsigned number) const
179 {
180     String res;
181     unsigned i;
182     unsigned j;
183
184     // Begin by collecting the properties into an array.
185     Vector< RefPtr<CSSValue> > values(number);
186     unsigned numLayers = 0;
187     
188     for (i = 0; i < number; ++i) {
189         values[i] = getPropertyCSSValue(properties[i]);
190         if (values[i]) {
191             if (values[i]->isValueList()) {
192                 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
193                 numLayers = max(valueList->length(), numLayers);
194             } else
195                 numLayers = max(1U, numLayers);
196         }
197     }
198     
199     // Now stitch the properties together.  Implicit initial values are flagged as such and
200     // can safely be omitted.
201     for (i = 0; i < numLayers; i++) {
202         String layerRes;
203         for (j = 0; j < number; j++) {
204             RefPtr<CSSValue> value;
205             if (values[j]) {
206                 if (values[j]->isValueList())
207                     value = static_cast<CSSValueList*>(values[j].get())->item(i);
208                 else {
209                     value = values[j];
210                     
211                     // Color only belongs in the last layer.
212                     if (properties[j] == CSSPropertyBackgroundColor) {
213                         if (i != numLayers - 1)
214                             value = 0;
215                     } else if (i != 0) // Other singletons only belong in the first layer.
216                         value = 0;
217                 }
218             }
219             
220             if (value && !value->isImplicitInitialValue()) {
221                 if (!layerRes.isNull())
222                     layerRes += " ";
223                 layerRes += value->cssText();
224             }
225         }
226         
227         if (!layerRes.isNull()) {
228             if (!res.isNull())
229                 res += ", ";
230             res += layerRes;
231         }
232     }
233
234     return res;
235 }
236
237 String CSSMutableStyleDeclaration::getShorthandValue(const int* properties, int number) const
238 {
239     String res;
240     for (int i = 0; i < number; ++i) {
241         if (!isPropertyImplicit(properties[i])) {
242             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
243             // FIXME: provide default value if !value
244             if (value) {
245                 if (!res.isNull())
246                     res += " ";
247                 res += value->cssText();
248             }
249         }
250     }
251     return res;
252 }
253
254 PassRefPtr<CSSValue> CSSMutableStyleDeclaration::getPropertyCSSValue(int propertyID) const
255 {
256     DeprecatedValueListConstIterator<CSSProperty> end;
257     for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.fromLast(); it != end; --it) {
258         if (propertyID == (*it).m_id)
259             return (*it).value();
260     }
261     return 0;
262 }
263
264 struct PropertyLonghand {
265     PropertyLonghand()
266         : m_properties(0)
267         , m_length(0)
268     {
269     }
270
271     PropertyLonghand(const int* firstProperty, unsigned numProperties)
272         : m_properties(firstProperty)
273         , m_length(numProperties)
274     {
275     }
276
277     const int* properties() const { return m_properties; }
278     unsigned length() const { return m_length; }
279
280 private:
281     const int* m_properties;
282     unsigned m_length;
283 };
284
285 static void initShorthandMap(HashMap<int, PropertyLonghand>& shorthandMap)
286 {
287     #define SET_SHORTHAND_MAP_ENTRY(map, propID, array) \
288         map.set(propID, PropertyLonghand(array, sizeof(array) / sizeof(array[0])))
289
290     // FIXME: The 'font' property has "shorthand nature" but is not parsed as a shorthand.
291
292     // Do not change the order of the following four shorthands, and keep them together.
293     static const int borderProperties[4][3] = {
294         { CSSPropertyBorderTopColor, CSSPropertyBorderTopStyle, CSSPropertyBorderTopWidth },
295         { CSSPropertyBorderRightColor, CSSPropertyBorderRightStyle, CSSPropertyBorderRightWidth },
296         { CSSPropertyBorderBottomColor, CSSPropertyBorderBottomStyle, CSSPropertyBorderBottomWidth },
297         { CSSPropertyBorderLeftColor, CSSPropertyBorderLeftStyle, CSSPropertyBorderLeftWidth }
298     };
299     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyBorderTop, borderProperties[0]);
300     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyBorderRight, borderProperties[1]);
301     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyBorderBottom, borderProperties[2]);
302     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyBorderLeft, borderProperties[3]);
303
304     shorthandMap.set(CSSPropertyBorder, PropertyLonghand(borderProperties[0], sizeof(borderProperties) / sizeof(borderProperties[0][0])));
305
306     static const int borderColorProperties[] = {
307         CSSPropertyBorderTopColor,
308         CSSPropertyBorderRightColor,
309         CSSPropertyBorderBottomColor,
310         CSSPropertyBorderLeftColor
311     };
312     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyBorderColor, borderColorProperties);
313
314     static const int borderStyleProperties[] = {
315         CSSPropertyBorderTopStyle,
316         CSSPropertyBorderRightStyle,
317         CSSPropertyBorderBottomStyle,
318         CSSPropertyBorderLeftStyle
319     };
320     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyBorderStyle, borderStyleProperties);
321
322     static const int borderWidthProperties[] = {
323         CSSPropertyBorderTopWidth,
324         CSSPropertyBorderRightWidth,
325         CSSPropertyBorderBottomWidth,
326         CSSPropertyBorderLeftWidth
327     };
328     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyBorderWidth, borderWidthProperties);
329
330     static const int backgroundPositionProperties[] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
331     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyBackgroundPosition, backgroundPositionProperties);
332
333     static const int borderSpacingProperties[] = { CSSPropertyWebkitBorderHorizontalSpacing, CSSPropertyWebkitBorderVerticalSpacing };
334     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyBorderSpacing, borderSpacingProperties);
335
336     static const int listStyleProperties[] = {
337         CSSPropertyListStyleImage,
338         CSSPropertyListStylePosition,
339         CSSPropertyListStyleType
340     };
341     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyListStyle, listStyleProperties);
342
343     static const int marginProperties[] = {
344         CSSPropertyMarginTop,
345         CSSPropertyMarginRight,
346         CSSPropertyMarginBottom,
347         CSSPropertyMarginLeft
348     };
349     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyMargin, marginProperties);
350
351     static const int marginCollapseProperties[] = { CSSPropertyWebkitMarginTopCollapse, CSSPropertyWebkitMarginBottomCollapse };
352     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyWebkitMarginCollapse, marginCollapseProperties);
353
354     static const int marqueeProperties[] = {
355         CSSPropertyWebkitMarqueeDirection,
356         CSSPropertyWebkitMarqueeIncrement,
357         CSSPropertyWebkitMarqueeRepetition,
358         CSSPropertyWebkitMarqueeStyle,
359         CSSPropertyWebkitMarqueeSpeed
360     };
361     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyWebkitMarquee, marqueeProperties);
362
363     static const int outlineProperties[] = {
364         CSSPropertyOutlineColor,
365         CSSPropertyOutlineOffset,
366         CSSPropertyOutlineStyle,
367         CSSPropertyOutlineWidth
368     };
369     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyOutline, outlineProperties);
370
371     static const int paddingProperties[] = {
372         CSSPropertyPaddingTop,
373         CSSPropertyPaddingRight,
374         CSSPropertyPaddingBottom,
375         CSSPropertyPaddingLeft
376     };
377     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyPadding, paddingProperties);
378
379     static const int textStrokeProperties[] = { CSSPropertyWebkitTextStrokeColor, CSSPropertyWebkitTextStrokeWidth };
380     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyWebkitTextStroke, textStrokeProperties);
381
382     static const int backgroundProperties[] = {
383         CSSPropertyBackgroundAttachment,
384         CSSPropertyBackgroundColor,
385         CSSPropertyBackgroundImage,
386         CSSPropertyBackgroundPositionX,
387         CSSPropertyBackgroundPositionY,
388         CSSPropertyBackgroundRepeat,
389         CSSPropertyWebkitBackgroundSize
390     };
391     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyBackground, backgroundProperties);
392
393     static const int columnsProperties[] = { CSSPropertyWebkitColumnWidth, CSSPropertyWebkitColumnCount };
394     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyWebkitColumns, columnsProperties);
395
396     static const int columnRuleProperties[] = {
397         CSSPropertyWebkitColumnRuleColor,
398         CSSPropertyWebkitColumnRuleStyle,
399         CSSPropertyWebkitColumnRuleWidth
400     };
401     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyWebkitColumnRule, columnRuleProperties);
402
403     static const int overflowProperties[] = { CSSPropertyOverflowX, CSSPropertyOverflowY };
404     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyOverflow, overflowProperties);
405
406     static const int borderRadiusProperties[] = {
407         CSSPropertyWebkitBorderTopRightRadius,
408         CSSPropertyWebkitBorderTopLeftRadius,
409         CSSPropertyWebkitBorderBottomLeftRadius,
410         CSSPropertyWebkitBorderBottomRightRadius
411     };
412     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSSPropertyWebkitBorderRadius, borderRadiusProperties);
413
414     #undef SET_SHORTHAND_MAP_ENTRY
415 }
416
417 String CSSMutableStyleDeclaration::removeProperty(int propertyID, bool notifyChanged, bool returnText, ExceptionCode& ec)
418 {
419     ec = 0;
420
421     static HashMap<int, PropertyLonghand> shorthandMap;
422     if (shorthandMap.isEmpty())
423         initShorthandMap(shorthandMap);
424
425     PropertyLonghand longhand = shorthandMap.get(propertyID);
426     if (longhand.length()) {
427         removePropertiesInSet(longhand.properties(), longhand.length(), notifyChanged);
428         // FIXME: Return an equivalent shorthand when possible.
429         return String();
430     }
431
432     String value;
433
434     DeprecatedValueListIterator<CSSProperty> end;
435     for (DeprecatedValueListIterator<CSSProperty> it = m_values.fromLast(); it != end; --it) {
436         if (propertyID == (*it).m_id) {
437             if (returnText)
438                 value = (*it).value()->cssText();
439             m_values.remove(it);
440             if (notifyChanged)
441                 setChanged();
442             break;
443         }
444     }
445
446     return value;
447 }
448
449 void CSSMutableStyleDeclaration::setChanged()
450 {
451     if (m_node) {
452         // FIXME: Ideally, this should be factored better and there
453         // should be a subclass of CSSMutableStyleDeclaration just
454         // for inline style declarations that handles this
455         bool isInlineStyleDeclaration = m_node->isStyledElement() && this == static_cast<StyledElement*>(m_node)->inlineStyleDecl();
456         if (isInlineStyleDeclaration) {
457             m_node->setChanged(InlineStyleChange);
458             static_cast<StyledElement*>(m_node)->invalidateStyleAttribute();
459         } else
460             m_node->setChanged(FullStyleChange);
461         return;
462     }
463
464     // FIXME: quick&dirty hack for KDE 3.0... make this MUCH better! (Dirk)
465     StyleBase* root = this;
466     while (StyleBase* parent = root->parent())
467         root = parent;
468     if (root->isCSSStyleSheet())
469         static_cast<CSSStyleSheet*>(root)->doc()->updateStyleSelector();
470 }
471
472 bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const
473 {
474     DeprecatedValueListConstIterator<CSSProperty> end;
475     for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it) {
476         if (propertyID == (*it).id())
477             return (*it).isImportant();
478     }
479     return false;
480 }
481
482 int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const
483 {
484     DeprecatedValueListConstIterator<CSSProperty> end;
485     for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it) {
486         if (propertyID == (*it).id())
487             return (*it).shorthandID();
488     }
489     return false;
490 }
491
492 bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const
493 {
494     DeprecatedValueListConstIterator<CSSProperty> end;
495     for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it) {
496         if (propertyID == (*it).id())
497             return (*it).isImplicit();
498     }
499     return false;
500 }
501
502 void CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, ExceptionCode& ec)
503 {
504     setProperty(propertyID, value, important, true, ec);
505 }
506
507 String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec)
508 {
509     return removeProperty(propertyID, true, true, ec);
510 }
511
512 bool CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, bool notifyChanged, ExceptionCode& ec)
513 {
514     ec = 0;
515
516     // Setting the value to an empty string just removes the property in both IE and Gecko.
517     // Setting it to null seems to produce less consistent results, but we treat it just the same.
518     if (value.isEmpty()) {
519         removeProperty(propertyID, notifyChanged, false, ec);
520         return ec == 0;
521     }
522
523     // When replacing an existing property value, this moves the property to the end of the list.
524     // Firefox preserves the position, and MSIE moves the property to the beginning.
525     CSSParser parser(useStrictParsing());
526     bool success = parser.parseValue(this, propertyID, value, important);
527     if (!success) {
528         // CSS DOM requires raising SYNTAX_ERR here, but this is too dangerous for compatibility,
529         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
530     } else if (notifyChanged)
531         setChanged();
532     ASSERT(!ec);
533     return success;
534 }
535
536 bool CSSMutableStyleDeclaration::setProperty(int propertyID, int value, bool important, bool notifyChanged)
537 {
538     removeProperty(propertyID);
539     m_values.append(CSSProperty(propertyID, new CSSPrimitiveValue(value), important));
540     if (notifyChanged)
541         setChanged();
542     return true;
543 }
544
545 void CSSMutableStyleDeclaration::setStringProperty(int propertyId, const String &value, CSSPrimitiveValue::UnitTypes type, bool important)
546 {
547     removeProperty(propertyId);
548     m_values.append(CSSProperty(propertyId, new CSSPrimitiveValue(value, type), important));
549     setChanged();
550 }
551
552 void CSSMutableStyleDeclaration::setImageProperty(int propertyId, const String& url, bool important)
553 {
554     removeProperty(propertyId);
555     m_values.append(CSSProperty(propertyId, new CSSImageValue(url, this), important));
556     setChanged();
557 }
558
559 void CSSMutableStyleDeclaration::parseDeclaration(const String& styleDeclaration)
560 {
561     m_values.clear();
562     CSSParser parser(useStrictParsing());
563     parser.parseDeclaration(this, styleDeclaration);
564     setChanged();
565 }
566
567 void CSSMutableStyleDeclaration::addParsedProperties(const CSSProperty * const * properties, int numProperties)
568 {
569     for (int i = 0; i < numProperties; ++i) {
570         // Only add properties that have no !important counterpart present
571         if (!getPropertyPriority(properties[i]->id()) || properties[i]->isImportant()) {
572             removeProperty(properties[i]->id(), false);
573             ASSERT(properties[i]);
574             m_values.append(*properties[i]);
575         }
576     }
577     // FIXME: This probably should have a call to setChanged() if something changed. We may also wish to add
578     // a notifyChanged argument to this function to follow the model of other functions in this class.
579 }
580
581 void CSSMutableStyleDeclaration::setLengthProperty(int propertyId, const String& value, bool important, bool /*multiLength*/)
582 {
583     bool parseMode = useStrictParsing();
584     setStrictParsing(false);
585     setProperty(propertyId, value, important);
586     setStrictParsing(parseMode);
587 }
588
589 unsigned CSSMutableStyleDeclaration::length() const
590 {
591     return m_values.count();
592 }
593
594 String CSSMutableStyleDeclaration::item(unsigned i) const
595 {
596     if (i >= m_values.count())
597        return String();
598     return getPropertyName(static_cast<CSSPropertyID>(m_values[i].id()));
599 }
600
601 String CSSMutableStyleDeclaration::cssText() const
602 {
603     String result = "";
604     
605     const CSSProperty* positionXProp = 0;
606     const CSSProperty* positionYProp = 0;
607
608     DeprecatedValueListConstIterator<CSSProperty> end;
609     for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it) {
610         const CSSProperty& prop = *it;
611         if (prop.id() == CSSPropertyBackgroundPositionX)
612             positionXProp = &prop;
613         else if (prop.id() == CSSPropertyBackgroundPositionY)
614             positionYProp = &prop;
615         else
616             result += prop.cssText();
617     }
618     
619     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
620     // It is required because background-position-x/y are non-standard properties and WebKit generated output 
621     // would not work in Firefox (<rdar://problem/5143183>)
622     // It would be a better solution if background-position was CSS_PAIR.
623     if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) {
624         String positionValue;
625         const int properties[2] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
626         if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList()) 
627             positionValue = getLayeredShorthandValue(properties, 2);
628         else
629             positionValue = positionXProp->value()->cssText() + " " + positionYProp->value()->cssText();
630         result += "background-position: " + positionValue + (positionXProp->isImportant() ? " !important" : "") + "; ";
631     } else {
632         if (positionXProp) 
633             result += positionXProp->cssText();
634         if (positionYProp)
635             result += positionYProp->cssText();
636     }
637
638     return result;
639 }
640
641 void CSSMutableStyleDeclaration::setCssText(const String& text, ExceptionCode& ec)
642 {
643     ec = 0;
644     m_values.clear();
645     CSSParser parser(useStrictParsing());
646     parser.parseDeclaration(this, text);
647     // FIXME: Detect syntax errors and set ec.
648     setChanged();
649 }
650
651 void CSSMutableStyleDeclaration::merge(CSSMutableStyleDeclaration* other, bool argOverridesOnConflict)
652 {
653     DeprecatedValueListConstIterator<CSSProperty> end;
654     for (DeprecatedValueListConstIterator<CSSProperty> it = other->valuesIterator(); it != end; ++it) {
655         const CSSProperty& property = *it;
656         RefPtr<CSSValue> value = getPropertyCSSValue(property.id());
657         if (value) {
658             if (!argOverridesOnConflict)
659                 continue;
660             removeProperty(property.id());
661         }
662         m_values.append(property);
663     }
664     // FIXME: This probably should have a call to setChanged() if something changed. We may also wish to add
665     // a notifyChanged argument to this function to follow the model of other functions in this class.
666 }
667
668 // This is the list of properties we want to copy in the copyBlockProperties() function.
669 // It is the list of CSS properties that apply specially to block-level elements.
670 static const int blockProperties[] = {
671     CSSPropertyOrphans,
672     CSSPropertyOverflow, // This can be also be applied to replaced elements
673     CSSPropertyWebkitColumnCount,
674     CSSPropertyWebkitColumnGap,
675     CSSPropertyWebkitColumnRuleColor,
676     CSSPropertyWebkitColumnRuleStyle,
677     CSSPropertyWebkitColumnRuleWidth,
678     CSSPropertyWebkitColumnBreakBefore,
679     CSSPropertyWebkitColumnBreakAfter,
680     CSSPropertyWebkitColumnBreakInside,
681     CSSPropertyWebkitColumnWidth,
682     CSSPropertyPageBreakAfter,
683     CSSPropertyPageBreakBefore,
684     CSSPropertyPageBreakInside,
685     CSSPropertyTextAlign,
686     CSSPropertyTextIndent,
687     CSSPropertyWidows
688 };
689
690 const unsigned numBlockProperties = sizeof(blockProperties) / sizeof(blockProperties[0]);
691
692 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copyBlockProperties() const
693 {
694     return copyPropertiesInSet(blockProperties, numBlockProperties);
695 }
696
697 void CSSMutableStyleDeclaration::removeBlockProperties()
698 {
699     removePropertiesInSet(blockProperties, numBlockProperties);
700 }
701
702 void CSSMutableStyleDeclaration::removePropertiesInSet(const int* set, unsigned length, bool notifyChanged)
703 {
704     bool changed = false;
705     for (unsigned i = 0; i < length; i++) {
706         RefPtr<CSSValue> value = getPropertyCSSValue(set[i]);
707         if (value) {
708             m_values.remove(CSSProperty(set[i], value.release(), false));
709             changed = true;
710         }
711     }
712     if (changed && notifyChanged)
713         setChanged();
714 }
715
716 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable()
717 {
718     return this;
719 }
720
721 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const
722 {
723     return new CSSMutableStyleDeclaration(0, m_values);
724 }
725
726 } // namespace WebCore