33ab0bb5141da8ea7ee3ad1d1fef89b76ace7180
[WebKit-https.git] / WebCore / css / CSSMutableStyleDeclaration.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
5  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "config.h"
24 #include "CSSMutableStyleDeclaration.h"
25
26 #include "CSSImageValue.h"
27 #include "CSSParser.h"
28 #include "CSSProperty.h"
29 #include "CSSPropertyNames.h"
30 #include "CSSStyleSheet.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 {
43 }
44
45 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent)
46     : CSSStyleDeclaration(parent)
47     , m_node(0)
48 {
49 }
50
51 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const DeprecatedValueList<CSSProperty>& values)
52     : CSSStyleDeclaration(parent)
53     , m_values(values)
54     , m_node(0)
55 {
56     // FIXME: This allows duplicate properties.
57 }
58
59 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const CSSProperty* const * properties, int numProperties)
60     : CSSStyleDeclaration(parent)
61     , m_node(0)
62 {
63     for (int i = 0; i < numProperties; ++i)
64         m_values.append(*properties[i]);
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 CSS_PROP_BACKGROUND_POSITION: {
84             // FIXME: Is this correct? The code in cssparser.cpp is confusing
85             const int properties[2] = { CSS_PROP_BACKGROUND_POSITION_X,
86                                         CSS_PROP_BACKGROUND_POSITION_Y };
87             return getLayeredShorthandValue(properties, 2);
88         }
89         case CSS_PROP_BACKGROUND: {
90             const int properties[6] = { CSS_PROP_BACKGROUND_IMAGE, CSS_PROP_BACKGROUND_REPEAT,
91                                         CSS_PROP_BACKGROUND_ATTACHMENT, CSS_PROP_BACKGROUND_POSITION_X,
92                                         CSS_PROP_BACKGROUND_POSITION_Y, CSS_PROP_BACKGROUND_COLOR };
93             return getLayeredShorthandValue(properties, 6);
94         }
95         case CSS_PROP_BORDER: {
96             const int properties[3] = { CSS_PROP_BORDER_WIDTH, CSS_PROP_BORDER_STYLE,
97                                         CSS_PROP_BORDER_COLOR };
98             return getShorthandValue(properties, 3);
99         }
100         case CSS_PROP_BORDER_TOP: {
101             const int properties[3] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_TOP_STYLE,
102                                         CSS_PROP_BORDER_TOP_COLOR};
103             return getShorthandValue(properties, 3);
104         }
105         case CSS_PROP_BORDER_RIGHT: {
106             const int properties[3] = { CSS_PROP_BORDER_RIGHT_WIDTH, CSS_PROP_BORDER_RIGHT_STYLE,
107                                         CSS_PROP_BORDER_RIGHT_COLOR};
108             return getShorthandValue(properties, 3);
109         }
110         case CSS_PROP_BORDER_BOTTOM: {
111             const int properties[3] = { CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_BOTTOM_STYLE,
112                                         CSS_PROP_BORDER_BOTTOM_COLOR};
113             return getShorthandValue(properties, 3);
114         }
115         case CSS_PROP_BORDER_LEFT: {
116             const int properties[3] = { CSS_PROP_BORDER_LEFT_WIDTH, CSS_PROP_BORDER_LEFT_STYLE,
117                                         CSS_PROP_BORDER_LEFT_COLOR};
118             return getShorthandValue(properties, 3);
119         }
120         case CSS_PROP_OUTLINE: {
121             const int properties[3] = { CSS_PROP_OUTLINE_WIDTH, CSS_PROP_OUTLINE_STYLE,
122                                         CSS_PROP_OUTLINE_COLOR };
123             return getShorthandValue(properties, 3);
124         }
125         case CSS_PROP_BORDER_COLOR: {
126             const int properties[4] = { CSS_PROP_BORDER_TOP_COLOR, CSS_PROP_BORDER_RIGHT_COLOR,
127                                         CSS_PROP_BORDER_BOTTOM_COLOR, CSS_PROP_BORDER_LEFT_COLOR };
128             return get4Values(properties);
129         }
130         case CSS_PROP_BORDER_WIDTH: {
131             const int properties[4] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_RIGHT_WIDTH,
132                                         CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_LEFT_WIDTH };
133             return get4Values(properties);
134         }
135         case CSS_PROP_BORDER_STYLE: {
136             const int properties[4] = { CSS_PROP_BORDER_TOP_STYLE, CSS_PROP_BORDER_RIGHT_STYLE,
137                                         CSS_PROP_BORDER_BOTTOM_STYLE, CSS_PROP_BORDER_LEFT_STYLE };
138             return get4Values(properties);
139         }
140         case CSS_PROP_MARGIN: {
141             const int properties[4] = { CSS_PROP_MARGIN_TOP, CSS_PROP_MARGIN_RIGHT,
142                                         CSS_PROP_MARGIN_BOTTOM, CSS_PROP_MARGIN_LEFT };
143             return get4Values(properties);
144         }
145         case CSS_PROP_PADDING: {
146             const int properties[4] = { CSS_PROP_PADDING_TOP, CSS_PROP_PADDING_RIGHT,
147                                         CSS_PROP_PADDING_BOTTOM, CSS_PROP_PADDING_LEFT };
148             return get4Values(properties);
149         }
150         case CSS_PROP_LIST_STYLE: {
151             const int properties[3] = { CSS_PROP_LIST_STYLE_TYPE, CSS_PROP_LIST_STYLE_POSITION,
152                                         CSS_PROP_LIST_STYLE_IMAGE };
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] == CSS_PROP_BACKGROUND_COLOR) {
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         { CSS_PROP_BORDER_TOP_COLOR, CSS_PROP_BORDER_TOP_STYLE, CSS_PROP_BORDER_TOP_WIDTH },
295         { CSS_PROP_BORDER_RIGHT_COLOR, CSS_PROP_BORDER_RIGHT_STYLE, CSS_PROP_BORDER_RIGHT_WIDTH },
296         { CSS_PROP_BORDER_BOTTOM_COLOR, CSS_PROP_BORDER_BOTTOM_STYLE, CSS_PROP_BORDER_BOTTOM_WIDTH },
297         { CSS_PROP_BORDER_LEFT_COLOR, CSS_PROP_BORDER_LEFT_STYLE, CSS_PROP_BORDER_LEFT_WIDTH }
298     };
299     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_TOP, borderProperties[0]);
300     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_RIGHT, borderProperties[1]);
301     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_BOTTOM, borderProperties[2]);
302     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_LEFT, borderProperties[3]);
303
304     shorthandMap.set(CSS_PROP_BORDER, PropertyLonghand(borderProperties[0], sizeof(borderProperties) / sizeof(borderProperties[0][0])));
305
306     static const int borderColorProperties[] = {
307         CSS_PROP_BORDER_TOP_COLOR,
308         CSS_PROP_BORDER_RIGHT_COLOR,
309         CSS_PROP_BORDER_BOTTOM_COLOR,
310         CSS_PROP_BORDER_LEFT_COLOR
311     };
312     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_COLOR, borderColorProperties);
313
314     static const int borderStyleProperties[] = {
315         CSS_PROP_BORDER_TOP_STYLE,
316         CSS_PROP_BORDER_RIGHT_STYLE,
317         CSS_PROP_BORDER_BOTTOM_STYLE,
318         CSS_PROP_BORDER_LEFT_STYLE
319     };
320     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_STYLE, borderStyleProperties);
321
322     static const int borderWidthProperties[] = {
323         CSS_PROP_BORDER_TOP_WIDTH,
324         CSS_PROP_BORDER_RIGHT_WIDTH,
325         CSS_PROP_BORDER_BOTTOM_WIDTH,
326         CSS_PROP_BORDER_LEFT_WIDTH
327     };
328     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_WIDTH, borderWidthProperties);
329
330     static const int backgroundPositionProperties[] = { CSS_PROP_BACKGROUND_POSITION_X, CSS_PROP_BACKGROUND_POSITION_Y };
331     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BACKGROUND_POSITION, backgroundPositionProperties);
332
333     static const int borderSpacingProperties[] = { CSS_PROP__WEBKIT_BORDER_HORIZONTAL_SPACING, CSS_PROP__WEBKIT_BORDER_VERTICAL_SPACING };
334     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BORDER_SPACING, borderSpacingProperties);
335
336     static const int listStyleProperties[] = {
337         CSS_PROP_LIST_STYLE_IMAGE,
338         CSS_PROP_LIST_STYLE_POSITION,
339         CSS_PROP_LIST_STYLE_TYPE
340     };
341     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_LIST_STYLE, listStyleProperties);
342
343     static const int marginProperties[] = {
344         CSS_PROP_MARGIN_TOP,
345         CSS_PROP_MARGIN_RIGHT,
346         CSS_PROP_MARGIN_BOTTOM,
347         CSS_PROP_MARGIN_LEFT
348     };
349     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_MARGIN, marginProperties);
350
351     static const int marginCollapseProperties[] = { CSS_PROP__WEBKIT_MARGIN_TOP_COLLAPSE, CSS_PROP__WEBKIT_MARGIN_BOTTOM_COLLAPSE };
352     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__WEBKIT_MARGIN_COLLAPSE, marginCollapseProperties);
353
354     static const int marqueeProperties[] = {
355         CSS_PROP__WEBKIT_MARQUEE_DIRECTION,
356         CSS_PROP__WEBKIT_MARQUEE_INCREMENT,
357         CSS_PROP__WEBKIT_MARQUEE_REPETITION,
358         CSS_PROP__WEBKIT_MARQUEE_STYLE,
359         CSS_PROP__WEBKIT_MARQUEE_SPEED
360     };
361     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__WEBKIT_MARQUEE, marqueeProperties);
362
363     static const int outlineProperties[] = {
364         CSS_PROP_OUTLINE_COLOR,
365         CSS_PROP_OUTLINE_OFFSET,
366         CSS_PROP_OUTLINE_STYLE,
367         CSS_PROP_OUTLINE_WIDTH
368     };
369     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_OUTLINE, outlineProperties);
370
371     static const int paddingProperties[] = {
372         CSS_PROP_PADDING_TOP,
373         CSS_PROP_PADDING_RIGHT,
374         CSS_PROP_PADDING_BOTTOM,
375         CSS_PROP_PADDING_LEFT
376     };
377     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_PADDING, paddingProperties);
378
379     static const int textStrokeProperties[] = { CSS_PROP__WEBKIT_TEXT_STROKE_COLOR, CSS_PROP__WEBKIT_TEXT_STROKE_WIDTH };
380     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__WEBKIT_TEXT_STROKE, textStrokeProperties);
381
382     static const int backgroundProperties[] = {
383         CSS_PROP_BACKGROUND_ATTACHMENT,
384         CSS_PROP_BACKGROUND_COLOR,
385         CSS_PROP_BACKGROUND_IMAGE,
386         CSS_PROP_BACKGROUND_POSITION_X,
387         CSS_PROP_BACKGROUND_POSITION_Y,
388         CSS_PROP_BACKGROUND_REPEAT,
389         CSS_PROP__WEBKIT_BACKGROUND_SIZE
390     };
391     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_BACKGROUND, backgroundProperties);
392
393     static const int columnsProperties[] = { CSS_PROP__WEBKIT_COLUMN_WIDTH, CSS_PROP__WEBKIT_COLUMN_COUNT };
394     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__WEBKIT_COLUMNS, columnsProperties);
395
396     static const int columnRuleProperties[] = {
397         CSS_PROP__WEBKIT_COLUMN_RULE_COLOR,
398         CSS_PROP__WEBKIT_COLUMN_RULE_STYLE,
399         CSS_PROP__WEBKIT_COLUMN_RULE_WIDTH
400     };
401     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__WEBKIT_COLUMN_RULE, columnRuleProperties);
402
403     static const int overflowProperties[] = { CSS_PROP_OVERFLOW_X, CSS_PROP_OVERFLOW_Y };
404     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP_OVERFLOW, overflowProperties);
405
406     static const int borderRadiusProperties[] = {
407         CSS_PROP__WEBKIT_BORDER_TOP_RIGHT_RADIUS,
408         CSS_PROP__WEBKIT_BORDER_TOP_LEFT_RADIUS,
409         CSS_PROP__WEBKIT_BORDER_BOTTOM_LEFT_RADIUS,
410         CSS_PROP__WEBKIT_BORDER_BOTTOM_RIGHT_RADIUS
411     };
412     SET_SHORTHAND_MAP_ENTRY(shorthandMap, CSS_PROP__WEBKIT_BORDER_RADIUS, borderRadiusProperties);
413
414     #undef SET_SHORTHAND_MAP_ENTRY
415 }
416
417 String CSSMutableStyleDeclaration::removeProperty(int propertyID, bool notifyChanged, 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             value = (*it).value()->cssText();
438             m_values.remove(it);
439             if (notifyChanged)
440                 setChanged();
441             break;
442         }
443     }
444
445     return value;
446 }
447
448 void CSSMutableStyleDeclaration::clear()
449 {
450     m_values.clear();
451     setChanged();
452 }
453
454 void CSSMutableStyleDeclaration::setChanged(StyleChangeType changeType)
455 {
456     if (m_node) {
457         m_node->setChanged(changeType);
458         // FIXME: Ideally, this should be factored better and there
459         // should be a subclass of CSSMutableStyleDeclaration just
460         // for inline style declarations that handles this
461         if (m_node->isStyledElement() && this == static_cast<StyledElement*>(m_node)->inlineStyleDecl())
462             static_cast<StyledElement*>(m_node)->invalidateStyleAttribute();
463         return;
464     }
465
466     // FIXME: quick&dirty hack for KDE 3.0... make this MUCH better! (Dirk)
467     StyleBase* root = this;
468     while (StyleBase* parent = root->parent())
469         root = parent;
470     if (root->isCSSStyleSheet())
471         static_cast<CSSStyleSheet*>(root)->doc()->updateStyleSelector();
472 }
473
474 bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const
475 {
476     DeprecatedValueListConstIterator<CSSProperty> end;
477     for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it) {
478         if (propertyID == (*it).id())
479             return (*it).isImportant();
480     }
481     return false;
482 }
483
484 int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const
485 {
486     DeprecatedValueListConstIterator<CSSProperty> end;
487     for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it) {
488         if (propertyID == (*it).id())
489             return (*it).shorthandID();
490     }
491     return false;
492 }
493
494 bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const
495 {
496     DeprecatedValueListConstIterator<CSSProperty> end;
497     for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it) {
498         if (propertyID == (*it).id())
499             return (*it).isImplicit();
500     }
501     return false;
502 }
503
504 void CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, ExceptionCode& ec)
505 {
506     setProperty(propertyID, value, important, true, ec);
507 }
508
509 String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec)
510 {
511     return removeProperty(propertyID, true, ec);
512 }
513
514 bool CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, bool notifyChanged, ExceptionCode& ec)
515 {
516     ec = 0;
517
518     // Setting the value to an empty string just removes the property in both IE and Gecko.
519     // Setting it to null seems to produce less consistent results, but we treat it just the same.
520     if (value.isEmpty()) {
521         removeProperty(propertyID, notifyChanged, ec);
522         return ec == 0;
523     }
524
525     // When replacing an existing property value, this moves the property to the end of the list.
526     // Firefox preserves the position, and MSIE moves the property to the beginning.
527     CSSParser parser(useStrictParsing());
528     bool success = parser.parseValue(this, propertyID, value, important);
529     if (!success) {
530         // CSS DOM requires raising SYNTAX_ERR here, but this is too dangerous for compatibility,
531         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
532     } else if (notifyChanged)
533         setChanged(InlineStyleChange);
534     ASSERT(!ec);
535     return success;
536 }
537
538 bool CSSMutableStyleDeclaration::setProperty(int propertyID, int value, bool important, bool notifyChanged)
539 {
540     removeProperty(propertyID);
541     m_values.append(CSSProperty(propertyID, new CSSPrimitiveValue(value), important));
542     if (notifyChanged)
543         setChanged();
544     return true;
545 }
546
547 void CSSMutableStyleDeclaration::setStringProperty(int propertyId, const String &value, CSSPrimitiveValue::UnitTypes type, bool important)
548 {
549     removeProperty(propertyId);
550     m_values.append(CSSProperty(propertyId, new CSSPrimitiveValue(value, type), important));
551     setChanged();
552 }
553
554 void CSSMutableStyleDeclaration::setImageProperty(int propertyId, const String& url, bool important)
555 {
556     removeProperty(propertyId);
557     m_values.append(CSSProperty(propertyId, new CSSImageValue(url, this), important));
558     setChanged();
559 }
560
561 void CSSMutableStyleDeclaration::parseDeclaration(const String& styleDeclaration)
562 {
563     m_values.clear();
564     CSSParser parser(useStrictParsing());
565     parser.parseDeclaration(this, styleDeclaration);
566     setChanged();
567 }
568
569 void CSSMutableStyleDeclaration::addParsedProperties(const CSSProperty * const * properties, int numProperties)
570 {
571     for (int i = 0; i < numProperties; ++i) {
572         // Only add properties that have no !important counterpart present
573         if (!getPropertyPriority(properties[i]->id()) || properties[i]->isImportant()) {
574             removeProperty(properties[i]->id(), false);
575             m_values.append(*properties[i]);
576         }
577     }
578     // FIXME: This probably should have a call to setChanged() if something changed. We may also wish to add
579     // a notifyChanged argument to this function to follow the model of other functions in this class.
580 }
581
582 void CSSMutableStyleDeclaration::setLengthProperty(int propertyId, const String& value, bool important, bool /*multiLength*/)
583 {
584     bool parseMode = useStrictParsing();
585     setStrictParsing(false);
586     setProperty(propertyId, value, important);
587     setStrictParsing(parseMode);
588 }
589
590 unsigned CSSMutableStyleDeclaration::length() const
591 {
592     return m_values.count();
593 }
594
595 String CSSMutableStyleDeclaration::item(unsigned i) const
596 {
597     if (i >= m_values.count())
598        return String();
599     return getPropertyName(static_cast<CSSPropertyID>(m_values[i].id()));
600 }
601
602 String CSSMutableStyleDeclaration::cssText() const
603 {
604     String result = "";
605
606     DeprecatedValueListConstIterator<CSSProperty> end;
607     for (DeprecatedValueListConstIterator<CSSProperty> it = m_values.begin(); it != end; ++it)
608         result += (*it).cssText();
609
610     return result;
611 }
612
613 void CSSMutableStyleDeclaration::setCssText(const String& text, ExceptionCode& ec)
614 {
615     ec = 0;
616     m_values.clear();
617     CSSParser parser(useStrictParsing());
618     parser.parseDeclaration(this, text);
619     // FIXME: Detect syntax errors and set ec.
620     setChanged();
621 }
622
623 void CSSMutableStyleDeclaration::merge(CSSMutableStyleDeclaration* other, bool argOverridesOnConflict)
624 {
625     DeprecatedValueListConstIterator<CSSProperty> end;
626     for (DeprecatedValueListConstIterator<CSSProperty> it = other->valuesIterator(); it != end; ++it) {
627         const CSSProperty& property = *it;
628         RefPtr<CSSValue> value = getPropertyCSSValue(property.id());
629         if (value) {
630             if (!argOverridesOnConflict)
631                 continue;
632             removeProperty(property.id());
633         }
634         m_values.append(property);
635     }
636     // FIXME: This probably should have a call to setChanged() if something changed. We may also wish to add
637     // a notifyChanged argument to this function to follow the model of other functions in this class.
638 }
639
640 // This is the list of properties we want to copy in the copyBlockProperties() function.
641 // It is the list of CSS properties that apply specially to block-level elements.
642 static const int blockProperties[] = {
643     CSS_PROP_ORPHANS,
644     CSS_PROP_OVERFLOW, // This can be also be applied to replaced elements
645     CSS_PROP__WEBKIT_COLUMN_COUNT,
646     CSS_PROP__WEBKIT_COLUMN_GAP,
647     CSS_PROP__WEBKIT_COLUMN_RULE_COLOR,
648     CSS_PROP__WEBKIT_COLUMN_RULE_STYLE,
649     CSS_PROP__WEBKIT_COLUMN_RULE_WIDTH,
650     CSS_PROP__WEBKIT_COLUMN_BREAK_BEFORE,
651     CSS_PROP__WEBKIT_COLUMN_BREAK_AFTER,
652     CSS_PROP__WEBKIT_COLUMN_BREAK_INSIDE,
653     CSS_PROP__WEBKIT_COLUMN_WIDTH,
654     CSS_PROP_PAGE_BREAK_AFTER,
655     CSS_PROP_PAGE_BREAK_BEFORE,
656     CSS_PROP_PAGE_BREAK_INSIDE,
657     CSS_PROP_TEXT_ALIGN,
658     CSS_PROP_TEXT_INDENT,
659     CSS_PROP_WIDOWS
660 };
661
662 const unsigned numBlockProperties = sizeof(blockProperties) / sizeof(blockProperties[0]);
663
664 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copyBlockProperties() const
665 {
666     return copyPropertiesInSet(blockProperties, numBlockProperties);
667 }
668
669 void CSSMutableStyleDeclaration::removeBlockProperties()
670 {
671     removePropertiesInSet(blockProperties, numBlockProperties);
672 }
673
674 void CSSMutableStyleDeclaration::removePropertiesInSet(const int* set, unsigned length, bool notifyChanged)
675 {
676     bool changed = false;
677     for (unsigned i = 0; i < length; i++) {
678         RefPtr<CSSValue> value = getPropertyCSSValue(set[i]);
679         if (value) {
680             m_values.remove(CSSProperty(set[i], value.release(), false));
681             changed = true;
682         }
683     }
684     if (changed && notifyChanged)
685         setChanged();
686 }
687
688 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable()
689 {
690     return this;
691 }
692
693 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const
694 {
695     return new CSSMutableStyleDeclaration(0, m_values);
696 }
697
698 } // namespace WebCore