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