2dd2f5d83074131355c13b6ceb691085dd8b07c7
[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     String res;
263     for (int i = 0; i < 4; ++i) {
264         if (!isPropertyImplicit(properties[i])) {
265             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
266
267             // apparently all 4 properties must be specified.
268             if (!value)
269                 return String();
270
271             if (!res.isNull())
272                 res += " ";
273             res += value->cssText();
274         }
275     }
276     return res;
277 }
278
279 String CSSMutableStyleDeclaration::getLayeredShorthandValue(const int* properties, unsigned number) const
280 {
281     String res;
282
283     // Begin by collecting the properties into an array.
284     Vector< RefPtr<CSSValue> > values(number);
285     size_t numLayers = 0;
286     
287     for (size_t i = 0; i < number; ++i) {
288         values[i] = getPropertyCSSValue(properties[i]);
289         if (values[i]) {
290             if (values[i]->isValueList()) {
291                 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
292                 numLayers = max(valueList->length(), numLayers);
293             } else
294                 numLayers = max<size_t>(1U, numLayers);
295         }
296     }
297     
298     // Now stitch the properties together.  Implicit initial values are flagged as such and
299     // can safely be omitted.
300     for (size_t i = 0; i < numLayers; i++) {
301         String layerRes;
302         bool useRepeatXShorthand = false;
303         bool useRepeatYShorthand = false;
304         bool useSingleWordShorthand = false;
305         for (size_t j = 0; j < number; j++) {
306             RefPtr<CSSValue> value;
307             if (values[j]) {
308                 if (values[j]->isValueList())
309                     value = static_cast<CSSValueList*>(values[j].get())->itemWithoutBoundsCheck(i);
310                 else {
311                     value = values[j];
312                     
313                     // Color only belongs in the last layer.
314                     if (properties[j] == CSSPropertyBackgroundColor) {
315                         if (i != numLayers - 1)
316                             value = 0;
317                     } else if (i != 0) // Other singletons only belong in the first layer.
318                         value = 0;
319                 }
320             }
321
322             // We need to report background-repeat as it was written in the CSS. If the property is implicit,
323             // then it was written with only one value. Here we figure out which value that was so we can
324             // report back correctly. 
325             if (properties[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(properties[j])) {
326                 if (j < number - 1 && properties[j + 1] == CSSPropertyBackgroundRepeatY) {
327                     RefPtr<CSSValue> yValue;
328                     RefPtr<CSSValue> nextValue = values[j + 1];
329                     if (nextValue->isValueList())
330                         yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
331                     else
332                         yValue = nextValue;
333                         
334                     int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
335                     int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
336                     if (xId != yId) {
337                         if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
338                             useRepeatXShorthand = true;
339                             ++j;
340                         } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
341                             useRepeatYShorthand = true;
342                             continue;
343                         }
344                     } else {
345                         useSingleWordShorthand = true;
346                         ++j;
347                     }
348                 }
349             }
350             
351             if (value && !value->isImplicitInitialValue()) {
352                 if (!layerRes.isNull())
353                     layerRes += " ";
354                 if (useRepeatXShorthand) {
355                     useRepeatXShorthand = false;
356                     layerRes += getValueName(CSSValueRepeatX);
357                 } else if (useRepeatYShorthand) {
358                     useRepeatYShorthand = false;
359                     layerRes += getValueName(CSSValueRepeatY);
360                 } else if (useSingleWordShorthand) {
361                     useSingleWordShorthand = false;
362                     layerRes += value->cssText();
363                 } else
364                     layerRes += value->cssText();
365             }
366         }
367         
368         if (!layerRes.isNull()) {
369             if (!res.isNull())
370                 res += ", ";
371             res += layerRes;
372         }
373     }
374
375     return res;
376 }
377
378 String CSSMutableStyleDeclaration::getShorthandValue(const int* properties, int number) const
379 {
380     String res;
381     for (int i = 0; i < number; ++i) {
382         if (!isPropertyImplicit(properties[i])) {
383             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
384             // FIXME: provide default value if !value
385             if (value) {
386                 if (!res.isNull())
387                     res += " ";
388                 res += value->cssText();
389             }
390         }
391     }
392     return res;
393 }
394
395 // only returns a non-null value if all properties have the same, non-null value
396 String CSSMutableStyleDeclaration::getCommonValue(const int* properties, int number) const
397 {
398     String res;
399     for (int i = 0; i < number; ++i) {
400         if (!isPropertyImplicit(properties[i])) {
401             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
402             if (!value)
403                 return String();
404             String text = value->cssText();
405             if (text.isNull())
406                 return String();
407             if (res.isNull())
408                 res = text;
409             else if (res != text)
410                 return String();
411         }
412     }
413     return res;
414 }
415
416 PassRefPtr<CSSValue> CSSMutableStyleDeclaration::getPropertyCSSValue(int propertyID) const
417 {
418     const CSSProperty* property = findPropertyWithId(propertyID);
419     return property ? property->value() : 0;
420 }
421
422 bool CSSMutableStyleDeclaration::removeShorthandProperty(int propertyID, bool notifyChanged) 
423 {
424     CSSPropertyLonghand longhand = longhandForProperty(propertyID);
425     if (longhand.length()) {
426         removePropertiesInSet(longhand.properties(), longhand.length(), notifyChanged);
427         return true;
428     }
429     return false;
430 }
431
432 String CSSMutableStyleDeclaration::removeProperty(int propertyID, bool notifyChanged, bool returnText)
433 {
434     ASSERT(!m_iteratorCount);
435
436     if (removeShorthandProperty(propertyID, notifyChanged)) {
437         // FIXME: Return an equivalent shorthand when possible.
438         return String();
439     }
440    
441     CSSProperty* foundProperty = findPropertyWithId(propertyID);
442     if (!foundProperty)
443         return String();
444     
445     String value = returnText ? foundProperty->value()->cssText() : String();
446
447     if (foundProperty->value()->isVariableDependentValue())
448         m_variableDependentValueCount--;
449
450     // A more efficient removal strategy would involve marking entries as empty
451     // and sweeping them when the vector grows too big.
452     m_properties.remove(foundProperty - m_properties.data());
453
454     if (notifyChanged)
455         setNeedsStyleRecalc();
456
457     return value;
458 }
459
460 void CSSMutableStyleDeclaration::setNeedsStyleRecalc()
461 {
462     if (m_node) {
463         // FIXME: Ideally, this should be factored better and there
464         // should be a subclass of CSSMutableStyleDeclaration just
465         // for inline style declarations that handles this
466         bool isInlineStyleDeclaration = m_node->isStyledElement() && this == static_cast<StyledElement*>(m_node)->inlineStyleDecl();
467         if (isInlineStyleDeclaration) {
468             m_node->setNeedsStyleRecalc(InlineStyleChange);
469             static_cast<StyledElement*>(m_node)->invalidateStyleAttribute();
470         } else
471             m_node->setNeedsStyleRecalc(FullStyleChange);
472         return;
473     }
474
475     // FIXME: quick&dirty hack for KDE 3.0... make this MUCH better! (Dirk)
476     StyleBase* root = this;
477     while (StyleBase* parent = root->parent())
478         root = parent;
479     if (root->isCSSStyleSheet())
480         static_cast<CSSStyleSheet*>(root)->doc()->updateStyleSelector();
481 }
482
483 bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const
484 {
485     const CSSProperty* property = findPropertyWithId(propertyID);
486     return property ? property->isImportant() : false;
487 }
488
489 int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const
490 {
491     const CSSProperty* property = findPropertyWithId(propertyID);
492     return property ? property->shorthandID() : 0;
493 }
494
495 bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const
496 {
497     const CSSProperty* property = findPropertyWithId(propertyID);
498     return property ? property->isImplicit() : false;
499 }
500
501 void CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, ExceptionCode& ec)
502 {
503     ec = 0;
504     setProperty(propertyID, value, important, true);
505 }
506
507 String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec)
508 {
509     ec = 0;
510     return removeProperty(propertyID, true, true);
511 }
512
513 bool CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, bool notifyChanged)
514 {
515     ASSERT(!m_iteratorCount);
516
517     // Setting the value to an empty string just removes the property in both IE and Gecko.
518     // Setting it to null seems to produce less consistent results, but we treat it just the same.
519     if (value.isEmpty()) {
520         removeProperty(propertyID, notifyChanged, false);
521         return true;
522     }
523
524     // When replacing an existing property value, this moves the property to the end of the list.
525     // Firefox preserves the position, and MSIE moves the property to the beginning.
526     CSSParser parser(useStrictParsing());
527     bool success = parser.parseValue(this, propertyID, value, important);
528     if (!success) {
529         // CSS DOM requires raising SYNTAX_ERR here, but this is too dangerous for compatibility,
530         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
531     } else if (notifyChanged)
532         setNeedsStyleRecalc();
533
534     return success;
535 }
536     
537 void CSSMutableStyleDeclaration::setPropertyInternal(const CSSProperty& property, CSSProperty* slot)
538 {
539     ASSERT(!m_iteratorCount);
540
541     if (!removeShorthandProperty(property.id(), false)) {
542         CSSProperty* toReplace = slot ? slot : findPropertyWithId(property.id());
543         if (toReplace) {
544             *toReplace = property;
545             return;
546         }
547     }
548     m_properties.append(property);
549 }
550
551 bool CSSMutableStyleDeclaration::setProperty(int propertyID, int value, bool important, bool notifyChanged)
552 {
553     CSSProperty property(propertyID, CSSPrimitiveValue::createIdentifier(value), important);
554     setPropertyInternal(property);
555     if (notifyChanged)
556         setNeedsStyleRecalc();
557     return true;
558 }
559
560 void CSSMutableStyleDeclaration::setStringProperty(int propertyId, const String &value, CSSPrimitiveValue::UnitTypes type, bool important)
561 {
562     ASSERT(!m_iteratorCount);
563
564     setPropertyInternal(CSSProperty(propertyId, CSSPrimitiveValue::create(value, type), important));
565     setNeedsStyleRecalc();
566 }
567
568 void CSSMutableStyleDeclaration::setImageProperty(int propertyId, const String& url, bool important)
569 {
570     ASSERT(!m_iteratorCount);
571
572     setPropertyInternal(CSSProperty(propertyId, CSSImageValue::create(url), important));
573     setNeedsStyleRecalc();
574 }
575
576 void CSSMutableStyleDeclaration::parseDeclaration(const String& styleDeclaration)
577 {
578     ASSERT(!m_iteratorCount);
579
580     m_properties.clear();
581     CSSParser parser(useStrictParsing());
582     parser.parseDeclaration(this, styleDeclaration);
583     setNeedsStyleRecalc();
584 }
585
586 void CSSMutableStyleDeclaration::addParsedProperties(const CSSProperty* const* properties, int numProperties)
587 {
588     ASSERT(!m_iteratorCount);
589     
590     m_properties.reserveCapacity(numProperties);
591     
592     for (int i = 0; i < numProperties; ++i) {
593         // Only add properties that have no !important counterpart present
594         if (!getPropertyPriority(properties[i]->id()) || properties[i]->isImportant()) {
595             removeProperty(properties[i]->id(), false);
596             ASSERT(properties[i]);
597             m_properties.append(*properties[i]);
598             if (properties[i]->value()->isVariableDependentValue())
599                 m_variableDependentValueCount++;
600         }
601     }
602     // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
603     // a notifyChanged argument to this function to follow the model of other functions in this class.
604 }
605
606 void CSSMutableStyleDeclaration::addParsedProperty(const CSSProperty& property)
607 {
608     ASSERT(!m_iteratorCount);
609
610     setPropertyInternal(property);
611 }
612
613 void CSSMutableStyleDeclaration::setLengthProperty(int propertyId, const String& value, bool important, bool /*multiLength*/)
614 {
615     ASSERT(!m_iteratorCount);
616
617     bool parseMode = useStrictParsing();
618     setStrictParsing(false);
619     setProperty(propertyId, value, important);
620     setStrictParsing(parseMode);
621 }
622
623 unsigned CSSMutableStyleDeclaration::length() const
624 {
625     return m_properties.size();
626 }
627
628 String CSSMutableStyleDeclaration::item(unsigned i) const
629 {
630     if (i >= m_properties.size())
631        return String();
632     return getPropertyName(static_cast<CSSPropertyID>(m_properties[i].id()));
633 }
634
635 String CSSMutableStyleDeclaration::cssText() const
636 {
637     String result = "";
638     
639     const CSSProperty* positionXProp = 0;
640     const CSSProperty* positionYProp = 0;
641     const CSSProperty* repeatXProp = 0;
642     const CSSProperty* repeatYProp = 0;
643     
644     unsigned size = m_properties.size();
645     for (unsigned n = 0; n < size; ++n) {
646         const CSSProperty& prop = m_properties[n];
647         if (prop.id() == CSSPropertyBackgroundPositionX)
648             positionXProp = &prop;
649         else if (prop.id() == CSSPropertyBackgroundPositionY)
650             positionYProp = &prop;
651         else if (prop.id() == CSSPropertyBackgroundRepeatX)
652             repeatXProp = &prop;
653         else if (prop.id() == CSSPropertyBackgroundRepeatY)
654             repeatYProp = &prop;
655         else
656             result += prop.cssText();
657     }
658     
659     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
660     // It is required because background-position-x/y are non-standard properties and WebKit generated output 
661     // would not work in Firefox (<rdar://problem/5143183>)
662     // It would be a better solution if background-position was CSS_PAIR.
663     if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) {
664         String positionValue;
665         const int properties[2] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
666         if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList()) 
667             positionValue = getLayeredShorthandValue(properties, 2);
668         else
669             positionValue = positionXProp->value()->cssText() + " " + positionYProp->value()->cssText();
670         result += "background-position: " + positionValue + (positionXProp->isImportant() ? " !important" : "") + "; ";
671     } else {
672         if (positionXProp) 
673             result += positionXProp->cssText();
674         if (positionYProp)
675             result += positionYProp->cssText();
676     }
677
678     // FIXME: We need to do the same for background-repeat.
679     if (repeatXProp && repeatYProp && repeatXProp->isImportant() == repeatYProp->isImportant()) {
680         String repeatValue;
681         const int repeatProperties[2] = { CSSPropertyBackgroundRepeatX, CSSPropertyBackgroundRepeatY };
682         if (repeatXProp->value()->isValueList() || repeatYProp->value()->isValueList()) 
683             repeatValue = getLayeredShorthandValue(repeatProperties, 2);
684         else
685             repeatValue = repeatXProp->value()->cssText() + " " + repeatYProp->value()->cssText();
686         result += "background-repeat: " + repeatValue + (repeatXProp->isImportant() ? " !important" : "") + "; ";
687     } else {
688         if (repeatXProp) 
689             result += repeatXProp->cssText();
690         if (repeatYProp)
691             result += repeatYProp->cssText();
692     }
693
694     return result;
695 }
696
697 void CSSMutableStyleDeclaration::setCssText(const String& text, ExceptionCode& ec)
698 {
699     ASSERT(!m_iteratorCount);
700
701     ec = 0;
702     m_properties.clear();
703     CSSParser parser(useStrictParsing());
704     parser.parseDeclaration(this, text);
705     // FIXME: Detect syntax errors and set ec.
706     setNeedsStyleRecalc();
707 }
708
709 void CSSMutableStyleDeclaration::merge(CSSMutableStyleDeclaration* other, bool argOverridesOnConflict)
710 {
711     ASSERT(!m_iteratorCount);
712
713     unsigned size = other->m_properties.size();
714     for (unsigned n = 0; n < size; ++n) {
715         CSSProperty& toMerge = other->m_properties[n];
716         CSSProperty* old = findPropertyWithId(toMerge.id());
717         if (old) {
718             if (!argOverridesOnConflict && old->value())
719                 continue;
720             setPropertyInternal(toMerge, old);
721         } else
722             m_properties.append(toMerge);
723     }
724     // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
725     // a notifyChanged argument to this function to follow the model of other functions in this class.
726 }
727
728 void CSSMutableStyleDeclaration::addSubresourceStyleURLs(ListHashSet<KURL>& urls)
729 {
730     CSSStyleSheet* sheet = static_cast<CSSStyleSheet*>(stylesheet());
731     size_t size = m_properties.size();
732     for (size_t i = 0; i < size; ++i)
733         m_properties[i].value()->addSubresourceStyleURLs(urls, sheet);
734 }
735
736 // This is the list of properties we want to copy in the copyBlockProperties() function.
737 // It is the list of CSS properties that apply specially to block-level elements.
738 static const int blockProperties[] = {
739     CSSPropertyOrphans,
740     CSSPropertyOverflow, // This can be also be applied to replaced elements
741     CSSPropertyWebkitColumnCount,
742     CSSPropertyWebkitColumnGap,
743     CSSPropertyWebkitColumnRuleColor,
744     CSSPropertyWebkitColumnRuleStyle,
745     CSSPropertyWebkitColumnRuleWidth,
746     CSSPropertyWebkitColumnBreakBefore,
747     CSSPropertyWebkitColumnBreakAfter,
748     CSSPropertyWebkitColumnBreakInside,
749     CSSPropertyWebkitColumnWidth,
750     CSSPropertyPageBreakAfter,
751     CSSPropertyPageBreakBefore,
752     CSSPropertyPageBreakInside,
753     CSSPropertyTextAlign,
754     CSSPropertyTextIndent,
755     CSSPropertyWidows
756 };
757
758 const unsigned numBlockProperties = sizeof(blockProperties) / sizeof(blockProperties[0]);
759
760 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copyBlockProperties() const
761 {
762     return copyPropertiesInSet(blockProperties, numBlockProperties);
763 }
764
765 void CSSMutableStyleDeclaration::removeBlockProperties()
766 {
767     removePropertiesInSet(blockProperties, numBlockProperties);
768 }
769
770 void CSSMutableStyleDeclaration::removePropertiesInSet(const int* set, unsigned length, bool notifyChanged)
771 {
772     ASSERT(!m_iteratorCount);
773     
774     if (m_properties.isEmpty())
775         return;
776     
777     // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
778     HashSet<int> toRemove;
779     for (unsigned i = 0; i < length; ++i)
780         toRemove.add(set[i]);
781     
782     Vector<CSSProperty, 4> newProperties;
783     newProperties.reserveInitialCapacity(m_properties.size());
784     
785     unsigned size = m_properties.size();
786     for (unsigned n = 0; n < size; ++n) {
787         const CSSProperty& property = m_properties[n];
788         // Not quite sure if the isImportant test is needed but it matches the existing behavior.
789         if (!property.isImportant()) {
790             if (toRemove.contains(property.id()))
791                 continue;
792         }
793         newProperties.append(property);
794     }
795
796     bool changed = newProperties.size() != m_properties.size();
797     m_properties = newProperties;
798     
799     if (changed && notifyChanged)
800         setNeedsStyleRecalc();
801 }
802
803 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable()
804 {
805     return this;
806 }
807
808 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const
809 {
810     return adoptRef(new CSSMutableStyleDeclaration(0, m_properties, m_variableDependentValueCount));
811 }
812
813 const CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID) const
814 {    
815     for (int n = m_properties.size() - 1 ; n >= 0; --n) {
816         if (propertyID == m_properties[n].m_id)
817             return &m_properties[n];
818     }
819     return 0;
820 }
821
822 CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID)
823 {
824     for (int n = m_properties.size() - 1 ; n >= 0; --n) {
825         if (propertyID == m_properties[n].m_id)
826             return &m_properties[n];
827     }
828     return 0;
829 }
830
831 } // namespace WebCore