46509a0934be51a8b6d69947f3d57610fc72bed3
[WebKit-https.git] / Source / WebCore / css / CSSMutableStyleDeclaration.cpp
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
4  * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23 #include "CSSMutableStyleDeclaration.h"
24
25 #include "CSSImageValue.h"
26 #include "CSSInlineStyleDeclaration.h"
27 #include "CSSParser.h"
28 #include "CSSPropertyLonghand.h"
29 #include "CSSPropertyNames.h"
30 #include "CSSRule.h"
31 #include "CSSStyleSheet.h"
32 #include "CSSValueKeywords.h"
33 #include "CSSValueList.h"
34 #include "Document.h"
35 #include "ExceptionCode.h"
36 #include "HTMLNames.h"
37 #include "InspectorInstrumentation.h"
38 #include "MutationRecord.h"
39 #include "StyledElement.h"
40 #include "WebKitMutationObserver.h"
41 #include <wtf/text/StringBuilder.h>
42 #include <wtf/text/WTFString.h>
43
44 using namespace std;
45
46 namespace WebCore {
47
48 #if ENABLE(MUTATION_OBSERVERS)
49 namespace {
50
51 class StyleAttributeMutationScope {
52     WTF_MAKE_NONCOPYABLE(StyleAttributeMutationScope);
53 public:
54     StyleAttributeMutationScope(CSSMutableStyleDeclaration* decl)
55     {
56         ++s_scopeCount;
57
58         if (s_scopeCount != 1) {
59             ASSERT(s_currentDecl == decl);
60             return;
61         }
62
63         ASSERT(!s_currentDecl);
64         s_currentDecl = decl;
65
66         if (!s_currentDecl->isInlineStyleDeclaration())
67             return;
68
69         CSSInlineStyleDeclaration* inlineDecl = toCSSInlineStyleDeclaration(s_currentDecl);
70         s_mutationRecipients = MutationObserverInterestGroup::createForAttributesMutation(inlineDecl->element(), HTMLNames::styleAttr);
71         if (s_mutationRecipients->isEmpty()) {
72             s_mutationRecipients.clear();
73             return;
74         }
75
76         AtomicString oldValue = s_mutationRecipients->isOldValueRequested() ? inlineDecl->element()->getAttribute(HTMLNames::styleAttr) : nullAtom;
77         s_mutation = MutationRecord::createAttributes(inlineDecl->element(), HTMLNames::styleAttr, oldValue);
78     }
79
80     ~StyleAttributeMutationScope()
81     {
82         --s_scopeCount;
83         if (!s_scopeCount)
84             s_currentDecl = 0;
85     }
86
87     void enqueueMutationRecord()
88     {
89         if (!s_mutation)
90             return;
91         s_mutationRecipients->enqueueMutationRecord(s_mutation);
92         s_mutation.clear();
93         s_mutationRecipients.clear();
94     }
95
96 private:
97     static unsigned s_scopeCount;
98     static OwnPtr<MutationObserverInterestGroup> s_mutationRecipients;
99     static RefPtr<MutationRecord> s_mutation;
100     static CSSMutableStyleDeclaration* s_currentDecl;
101 };
102
103 unsigned StyleAttributeMutationScope::s_scopeCount = 0;
104 OwnPtr<MutationObserverInterestGroup> StyleAttributeMutationScope::s_mutationRecipients;
105 RefPtr<MutationRecord> StyleAttributeMutationScope::s_mutation;
106 CSSMutableStyleDeclaration* StyleAttributeMutationScope::s_currentDecl = 0;
107
108 } // namespace
109 #endif // ENABLE(MUTATION_OBSERVERS)
110
111 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration()
112     : CSSStyleDeclaration(0)
113 {
114     // This constructor is used for various inline style declarations, so disable strict parsing.
115     m_strictParsing = false;
116 }
117
118 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent)
119     : CSSStyleDeclaration(parent)
120 {
121 }
122
123 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const Vector<CSSProperty>& properties)
124     : CSSStyleDeclaration(parent)
125     , m_properties(properties)
126 {
127     m_properties.shrinkToFit();
128     // FIXME: This allows duplicate properties.
129 }
130
131 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const CSSProperty* const * properties, int numProperties)
132     : CSSStyleDeclaration(parent)
133 {
134     m_properties.reserveInitialCapacity(numProperties);
135     HashMap<int, bool> candidates;
136     for (int i = 0; i < numProperties; ++i) {
137         const CSSProperty *property = properties[i];
138         ASSERT(property);
139         bool important = property->isImportant();
140         if (candidates.contains(property->id())) {
141             if (!important && candidates.get(property->id()))
142                 continue;
143             removeProperty(property->id(), false, false);
144         }
145         m_properties.append(*property);
146         candidates.set(property->id(), important);
147     }
148 }
149
150 CSSMutableStyleDeclaration::~CSSMutableStyleDeclaration()
151 {
152 }
153
154 void CSSMutableStyleDeclaration::copyPropertiesFrom(const CSSMutableStyleDeclaration& other)
155 {
156     ASSERT(!m_iteratorCount);
157     m_properties = other.m_properties;
158 }
159
160 String CSSMutableStyleDeclaration::getPropertyValue(int propertyID) const
161 {
162     RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
163     if (value)
164         return value->cssText();
165
166     // Shorthand and 4-values properties
167     switch (propertyID) {
168         case CSSPropertyBorderSpacing: {
169             const int properties[2] = { CSSPropertyWebkitBorderHorizontalSpacing, CSSPropertyWebkitBorderVerticalSpacing };
170             return borderSpacingValue(properties);
171         }
172         case CSSPropertyBackgroundPosition: {
173             // FIXME: Is this correct? The code in cssparser.cpp is confusing
174             const int properties[2] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
175             return getLayeredShorthandValue(properties);
176         }
177         case CSSPropertyBackgroundRepeat: {
178             const int properties[2] = { CSSPropertyBackgroundRepeatX, CSSPropertyBackgroundRepeatY };
179             return getLayeredShorthandValue(properties);
180         }
181         case CSSPropertyBackground: {
182             const int properties[9] = { CSSPropertyBackgroundColor,
183                                         CSSPropertyBackgroundImage,
184                                         CSSPropertyBackgroundRepeatX,
185                                         CSSPropertyBackgroundRepeatY,
186                                         CSSPropertyBackgroundAttachment,
187                                         CSSPropertyBackgroundPositionX,
188                                         CSSPropertyBackgroundPositionY,
189                                         CSSPropertyBackgroundClip,
190                                         CSSPropertyBackgroundOrigin };
191             return getLayeredShorthandValue(properties);
192         }
193         case CSSPropertyBorder: {
194             const int properties[3][4] = {{ CSSPropertyBorderTopWidth,
195                                             CSSPropertyBorderRightWidth,
196                                             CSSPropertyBorderBottomWidth,
197                                             CSSPropertyBorderLeftWidth },
198                                           { CSSPropertyBorderTopStyle,
199                                             CSSPropertyBorderRightStyle,
200                                             CSSPropertyBorderBottomStyle,
201                                             CSSPropertyBorderLeftStyle },
202                                           { CSSPropertyBorderTopColor,
203                                             CSSPropertyBorderRightColor,
204                                             CSSPropertyBorderBottomColor,
205                                             CSSPropertyBorderLeftColor }};
206             String res;
207             for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
208                 String value = getCommonValue(properties[i]);
209                 if (!value.isNull()) {
210                     if (!res.isNull())
211                         res += " ";
212                     res += value;
213                 }
214             }
215             return res;
216         }
217         case CSSPropertyBorderTop: {
218             const int properties[3] = { CSSPropertyBorderTopWidth, CSSPropertyBorderTopStyle,
219                                         CSSPropertyBorderTopColor};
220             return getShorthandValue(properties);
221         }
222         case CSSPropertyBorderRight: {
223             const int properties[3] = { CSSPropertyBorderRightWidth, CSSPropertyBorderRightStyle,
224                                         CSSPropertyBorderRightColor};
225             return getShorthandValue(properties);
226         }
227         case CSSPropertyBorderBottom: {
228             const int properties[3] = { CSSPropertyBorderBottomWidth, CSSPropertyBorderBottomStyle,
229                                         CSSPropertyBorderBottomColor};
230             return getShorthandValue(properties);
231         }
232         case CSSPropertyBorderLeft: {
233             const int properties[3] = { CSSPropertyBorderLeftWidth, CSSPropertyBorderLeftStyle,
234                                         CSSPropertyBorderLeftColor};
235             return getShorthandValue(properties);
236         }
237         case CSSPropertyOutline: {
238             const int properties[3] = { CSSPropertyOutlineWidth, CSSPropertyOutlineStyle,
239                                         CSSPropertyOutlineColor };
240             return getShorthandValue(properties);
241         }
242         case CSSPropertyBorderColor: {
243             const int properties[4] = { CSSPropertyBorderTopColor, CSSPropertyBorderRightColor,
244                                         CSSPropertyBorderBottomColor, CSSPropertyBorderLeftColor };
245             return get4Values(properties);
246         }
247         case CSSPropertyBorderWidth: {
248             const int properties[4] = { CSSPropertyBorderTopWidth, CSSPropertyBorderRightWidth,
249                                         CSSPropertyBorderBottomWidth, CSSPropertyBorderLeftWidth };
250             return get4Values(properties);
251         }
252         case CSSPropertyBorderStyle: {
253             const int properties[4] = { CSSPropertyBorderTopStyle, CSSPropertyBorderRightStyle,
254                                         CSSPropertyBorderBottomStyle, CSSPropertyBorderLeftStyle };
255             return get4Values(properties);
256         }
257         case CSSPropertyFont:
258             return fontValue();
259         case CSSPropertyMargin: {
260             const int properties[4] = { CSSPropertyMarginTop, CSSPropertyMarginRight,
261                                         CSSPropertyMarginBottom, CSSPropertyMarginLeft };
262             return get4Values(properties);
263         }
264         case CSSPropertyOverflow: {
265             const int properties[2] = { CSSPropertyOverflowX, CSSPropertyOverflowY };
266             return getCommonValue(properties);
267         }
268         case CSSPropertyPadding: {
269             const int properties[4] = { CSSPropertyPaddingTop, CSSPropertyPaddingRight,
270                                         CSSPropertyPaddingBottom, CSSPropertyPaddingLeft };
271             return get4Values(properties);
272         }
273         case CSSPropertyListStyle: {
274             const int properties[3] = { CSSPropertyListStyleType, CSSPropertyListStylePosition,
275                                         CSSPropertyListStyleImage };
276             return getShorthandValue(properties);
277         }
278         case CSSPropertyWebkitMaskPosition: {
279             // FIXME: Is this correct? The code in cssparser.cpp is confusing
280             const int properties[2] = { CSSPropertyWebkitMaskPositionX, CSSPropertyWebkitMaskPositionY };
281             return getLayeredShorthandValue(properties);
282         }
283         case CSSPropertyWebkitMaskRepeat: {
284             const int properties[2] = { CSSPropertyWebkitMaskRepeatX, CSSPropertyWebkitMaskRepeatY };
285             return getLayeredShorthandValue(properties);
286         }
287         case CSSPropertyWebkitMask: {
288             const int properties[] = { CSSPropertyWebkitMaskImage, CSSPropertyWebkitMaskRepeat,
289                                        CSSPropertyWebkitMaskAttachment, CSSPropertyWebkitMaskPosition, CSSPropertyWebkitMaskClip,
290                                        CSSPropertyWebkitMaskOrigin };
291             return getLayeredShorthandValue(properties);
292         }
293         case CSSPropertyWebkitTransformOrigin: {
294             const int properties[3] = { CSSPropertyWebkitTransformOriginX,
295                                         CSSPropertyWebkitTransformOriginY,
296                                         CSSPropertyWebkitTransformOriginZ };
297             return getShorthandValue(properties);
298         }
299         case CSSPropertyWebkitTransition: {
300             const int properties[4] = { CSSPropertyWebkitTransitionProperty, CSSPropertyWebkitTransitionDuration,
301                                         CSSPropertyWebkitTransitionTimingFunction, CSSPropertyWebkitTransitionDelay };
302             return getLayeredShorthandValue(properties);
303         }
304         case CSSPropertyWebkitAnimation: {
305             const int properties[7] = { CSSPropertyWebkitAnimationName, CSSPropertyWebkitAnimationDuration,
306                                         CSSPropertyWebkitAnimationTimingFunction, CSSPropertyWebkitAnimationDelay,
307                                         CSSPropertyWebkitAnimationIterationCount, CSSPropertyWebkitAnimationDirection,
308                                         CSSPropertyWebkitAnimationFillMode };
309             return getLayeredShorthandValue(properties);
310         }
311         case CSSPropertyWebkitWrap: {
312             const int properties[3] = { CSSPropertyWebkitWrapFlow, CSSPropertyWebkitWrapMargin,
313                 CSSPropertyWebkitWrapPadding };
314             return getShorthandValue(properties);
315         }
316 #if ENABLE(SVG)
317         case CSSPropertyMarker: {
318             RefPtr<CSSValue> value = getPropertyCSSValue(CSSPropertyMarkerStart);
319             if (value)
320                 return value->cssText();
321         }
322 #endif
323     }
324     return String();
325 }
326
327 String CSSMutableStyleDeclaration::borderSpacingValue(const int properties[2]) const
328 {
329     RefPtr<CSSValue> horizontalValue = getPropertyCSSValue(properties[0]);
330     RefPtr<CSSValue> verticalValue = getPropertyCSSValue(properties[1]);
331
332     if (!horizontalValue)
333         return String();
334     ASSERT(verticalValue); // By <http://www.w3.org/TR/CSS21/tables.html#separated-borders>.
335
336     String horizontalValueCSSText = horizontalValue->cssText();
337     String verticalValueCSSText = verticalValue->cssText();
338     if (horizontalValueCSSText == verticalValueCSSText)
339         return horizontalValueCSSText;
340     return horizontalValueCSSText + ' ' + verticalValueCSSText;
341 }
342
343 bool CSSMutableStyleDeclaration::appendFontLonghandValueIfExplicit(int propertyId, StringBuilder& result) const
344 {
345     const CSSProperty* property = findPropertyWithId(propertyId);
346     if (!property)
347         return false; // All longhands must have at least implicit values if "font" is specified.
348     if (property->isImplicit())
349         return true;
350
351     char prefix = '\0';
352     switch (propertyId) {
353     case CSSPropertyFontStyle:
354         break; // No prefix.
355     case CSSPropertyFontFamily:
356     case CSSPropertyFontVariant:
357     case CSSPropertyFontWeight:
358         prefix = ' ';
359         break;
360     case CSSPropertyLineHeight:
361         prefix = '/';
362         break;
363     default:
364         ASSERT_NOT_REACHED();
365     }
366
367     if (prefix && !result.isEmpty())
368         result.append(prefix);
369     result.append(property->value()->cssText());
370
371     return true;
372 }
373
374 String CSSMutableStyleDeclaration::fontValue() const
375 {
376     const CSSProperty* fontSizeProperty = findPropertyWithId(CSSPropertyFontSize);
377     if (!fontSizeProperty || fontSizeProperty->isImplicit())
378         return emptyString();
379
380     StringBuilder result;
381     bool success = true;
382     success &= appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result);
383     success &= appendFontLonghandValueIfExplicit(CSSPropertyFontVariant, result);
384     success &= appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result);
385     if (!result.isEmpty())
386         result.append(' ');
387     result.append(fontSizeProperty->value()->cssText());
388     success &= appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result);
389     success &= appendFontLonghandValueIfExplicit(CSSPropertyFontFamily, result);
390     if (!success) {
391         // An invalid "font" value has been built (should never happen, as at least implicit values
392         // for mandatory longhands are always found in the style), report empty value instead.
393         ASSERT_NOT_REACHED();
394         return emptyString();
395     }
396     return result.toString();
397 }
398
399 String CSSMutableStyleDeclaration::get4Values(const int* properties) const
400 {
401     // Assume the properties are in the usual order top, right, bottom, left.
402     RefPtr<CSSValue> topValue = getPropertyCSSValue(properties[0]);
403     RefPtr<CSSValue> rightValue = getPropertyCSSValue(properties[1]);
404     RefPtr<CSSValue> bottomValue = getPropertyCSSValue(properties[2]);
405     RefPtr<CSSValue> leftValue = getPropertyCSSValue(properties[3]);
406
407     // All 4 properties must be specified.
408     if (!topValue || !rightValue || !bottomValue || !leftValue)
409         return String();
410
411     bool showLeft = rightValue->cssText() != leftValue->cssText();
412     bool showBottom = (topValue->cssText() != bottomValue->cssText()) || showLeft;
413     bool showRight = (topValue->cssText() != rightValue->cssText()) || showBottom;
414
415     String res = topValue->cssText();
416     if (showRight)
417         res += " " + rightValue->cssText();
418     if (showBottom)
419         res += " " + bottomValue->cssText();
420     if (showLeft)
421         res += " " + leftValue->cssText();
422
423     return res;
424 }
425
426 String CSSMutableStyleDeclaration::getLayeredShorthandValue(const int* properties, size_t size) const
427 {
428     String res;
429
430     // Begin by collecting the properties into an array.
431     Vector< RefPtr<CSSValue> > values(size);
432     size_t numLayers = 0;
433
434     for (size_t i = 0; i < size; ++i) {
435         values[i] = getPropertyCSSValue(properties[i]);
436         if (values[i]) {
437             if (values[i]->isValueList()) {
438                 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
439                 numLayers = max(valueList->length(), numLayers);
440             } else
441                 numLayers = max<size_t>(1U, numLayers);
442         }
443     }
444
445     // Now stitch the properties together.  Implicit initial values are flagged as such and
446     // can safely be omitted.
447     for (size_t i = 0; i < numLayers; i++) {
448         String layerRes;
449         bool useRepeatXShorthand = false;
450         bool useRepeatYShorthand = false;
451         bool useSingleWordShorthand = false;
452         for (size_t j = 0; j < size; j++) {
453             RefPtr<CSSValue> value;
454             if (values[j]) {
455                 if (values[j]->isValueList())
456                     value = static_cast<CSSValueList*>(values[j].get())->item(i);
457                 else {
458                     value = values[j];
459
460                     // Color only belongs in the last layer.
461                     if (properties[j] == CSSPropertyBackgroundColor) {
462                         if (i != numLayers - 1)
463                             value = 0;
464                     } else if (i != 0) // Other singletons only belong in the first layer.
465                         value = 0;
466                 }
467             }
468
469             // We need to report background-repeat as it was written in the CSS. If the property is implicit,
470             // then it was written with only one value. Here we figure out which value that was so we can
471             // report back correctly.
472             if (properties[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(properties[j])) {
473
474                 // BUG 49055: make sure the value was not reset in the layer check just above.
475                 if (j < size - 1 && properties[j + 1] == CSSPropertyBackgroundRepeatY && value) {
476                     RefPtr<CSSValue> yValue;
477                     RefPtr<CSSValue> nextValue = values[j + 1];
478                     if (nextValue->isValueList())
479                         yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
480                     else
481                         yValue = nextValue;
482
483                     int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
484                     int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
485                     if (xId != yId) {
486                         if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
487                             useRepeatXShorthand = true;
488                             ++j;
489                         } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
490                             useRepeatYShorthand = true;
491                             continue;
492                         }
493                     } else {
494                         useSingleWordShorthand = true;
495                         ++j;
496                     }
497                 }
498             }
499
500             if (value && !value->isImplicitInitialValue()) {
501                 if (!layerRes.isNull())
502                     layerRes += " ";
503                 if (useRepeatXShorthand) {
504                     useRepeatXShorthand = false;
505                     layerRes += getValueName(CSSValueRepeatX);
506                 } else if (useRepeatYShorthand) {
507                     useRepeatYShorthand = false;
508                     layerRes += getValueName(CSSValueRepeatY);
509                 } else if (useSingleWordShorthand) {
510                     useSingleWordShorthand = false;
511                     layerRes += value->cssText();
512                 } else
513                     layerRes += value->cssText();
514             }
515         }
516
517         if (!layerRes.isNull()) {
518             if (!res.isNull())
519                 res += ", ";
520             res += layerRes;
521         }
522     }
523
524     return res;
525 }
526
527 String CSSMutableStyleDeclaration::getShorthandValue(const int* properties, size_t size) const
528 {
529     String res;
530     for (size_t i = 0; i < size; ++i) {
531         if (!isPropertyImplicit(properties[i])) {
532             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
533             // FIXME: provide default value if !value
534             if (value) {
535                 if (!res.isNull())
536                     res += " ";
537                 res += value->cssText();
538             }
539         }
540     }
541     return res;
542 }
543
544 // only returns a non-null value if all properties have the same, non-null value
545 String CSSMutableStyleDeclaration::getCommonValue(const int* properties, size_t size) const
546 {
547     String res;
548     for (size_t i = 0; i < size; ++i) {
549         RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
550         if (!value)
551             return String();
552         String text = value->cssText();
553         if (text.isNull())
554             return String();
555         if (res.isNull())
556             res = text;
557         else if (res != text)
558             return String();
559     }
560     return res;
561 }
562
563 PassRefPtr<CSSValue> CSSMutableStyleDeclaration::getPropertyCSSValue(int propertyID) const
564 {
565     const CSSProperty* property = findPropertyWithId(propertyID);
566     return property ? property->value() : 0;
567 }
568
569 bool CSSMutableStyleDeclaration::removeShorthandProperty(int propertyID, bool notifyChanged)
570 {
571     CSSPropertyLonghand longhand = longhandForProperty(propertyID);
572     if (longhand.length()) {
573         removePropertiesInSet(longhand.properties(), longhand.length(), notifyChanged);
574         return true;
575     }
576     return false;
577 }
578
579 String CSSMutableStyleDeclaration::removeProperty(int propertyID, bool notifyChanged, bool returnText)
580 {
581     ASSERT(!m_iteratorCount);
582
583 #if ENABLE(MUTATION_OBSERVERS)
584     StyleAttributeMutationScope mutationScope(this);
585 #endif
586
587     if (removeShorthandProperty(propertyID, notifyChanged)) {
588         // FIXME: Return an equivalent shorthand when possible.
589         return String();
590     }
591
592     CSSProperty* foundProperty = findPropertyWithId(propertyID);
593     if (!foundProperty)
594         return String();
595
596     String value = returnText ? foundProperty->value()->cssText() : String();
597
598     // A more efficient removal strategy would involve marking entries as empty
599     // and sweeping them when the vector grows too big.
600     m_properties.remove(foundProperty - m_properties.data());
601
602 #if ENABLE(MUTATION_OBSERVERS)
603     mutationScope.enqueueMutationRecord();
604 #endif
605
606     if (notifyChanged)
607         setNeedsStyleRecalc();
608
609     return value;
610 }
611
612 void CSSMutableStyleDeclaration::setNeedsStyleRecalc()
613 {
614     if (isElementStyleDeclaration() && static_cast<CSSElementStyleDeclaration*>(this)->element()) {
615         StyledElement* element = static_cast<CSSElementStyleDeclaration*>(this)->element();
616         if (!isInlineStyleDeclaration())
617             element->setNeedsStyleRecalc(FullStyleChange);
618         else {
619             element->setNeedsStyleRecalc(InlineStyleChange);
620             element->invalidateStyleAttribute();
621             if (Document* document = element->document())
622                 InspectorInstrumentation::didInvalidateStyleAttr(document, element);
623         }
624         return;
625     }
626
627     if (CSSStyleSheet* styleSheet = parentStyleSheet()) {
628         if (Document* document = styleSheet->findDocument())
629             document->styleSelectorChanged(DeferRecalcStyle);
630     }
631 }
632
633 bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const
634 {
635     const CSSProperty* property = findPropertyWithId(propertyID);
636     return property ? property->isImportant() : false;
637 }
638
639 int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const
640 {
641     const CSSProperty* property = findPropertyWithId(propertyID);
642     return property ? property->shorthandID() : 0;
643 }
644
645 bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const
646 {
647     const CSSProperty* property = findPropertyWithId(propertyID);
648     return property ? property->isImplicit() : false;
649 }
650
651 void CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, ExceptionCode& ec)
652 {
653     ec = 0;
654     setProperty(propertyID, value, important, true);
655 }
656
657 String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec)
658 {
659     ec = 0;
660     return removeProperty(propertyID, true, true);
661 }
662
663 bool CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, bool notifyChanged)
664 {
665     ASSERT(!m_iteratorCount);
666
667 #if ENABLE(MUTATION_OBSERVERS)
668     StyleAttributeMutationScope mutationScope(this);
669 #endif
670
671     // Setting the value to an empty string just removes the property in both IE and Gecko.
672     // Setting it to null seems to produce less consistent results, but we treat it just the same.
673     if (value.isEmpty()) {
674         removeProperty(propertyID, notifyChanged, false);
675         return true;
676     }
677
678     // When replacing an existing property value, this moves the property to the end of the list.
679     // Firefox preserves the position, and MSIE moves the property to the beginning.
680     bool success = CSSParser::parseValue(this, propertyID, value, important, useStrictParsing());
681     if (!success) {
682         // CSS DOM requires raising SYNTAX_ERR here, but this is too dangerous for compatibility,
683         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
684         return false;
685     }
686
687 #if ENABLE(MUTATION_OBSERVERS)
688     mutationScope.enqueueMutationRecord();
689 #endif
690
691     if (notifyChanged)
692         setNeedsStyleRecalc();
693
694     return true;
695 }
696
697 void CSSMutableStyleDeclaration::setPropertyInternal(const CSSProperty& property, CSSProperty* slot)
698 {
699     ASSERT(!m_iteratorCount);
700
701 #if ENABLE(MUTATION_OBSERVERS)
702     StyleAttributeMutationScope mutationScope(this);
703 #endif
704
705     if (!removeShorthandProperty(property.id(), false)) {
706         CSSProperty* toReplace = slot ? slot : findPropertyWithId(property.id());
707         if (toReplace) {
708             *toReplace = property;
709             return;
710         }
711     }
712     m_properties.append(property);
713
714 #if ENABLE(MUTATION_OBSERVERS)
715     mutationScope.enqueueMutationRecord();
716 #endif
717 }
718
719 bool CSSMutableStyleDeclaration::setProperty(int propertyID, int value, bool important, bool notifyChanged)
720 {
721     CSSProperty property(propertyID, CSSPrimitiveValue::createIdentifier(value), important);
722     setPropertyInternal(property);
723     if (notifyChanged)
724         setNeedsStyleRecalc();
725     return true;
726 }
727
728 bool CSSMutableStyleDeclaration::setProperty(int propertyID, double value, CSSPrimitiveValue::UnitTypes unit, bool important, bool notifyChanged)
729 {
730     CSSProperty property(propertyID, CSSPrimitiveValue::create(value, unit), important);
731     setPropertyInternal(property);
732     if (notifyChanged)
733         setNeedsStyleRecalc();
734     return true;
735 }
736
737 void CSSMutableStyleDeclaration::setStringProperty(int propertyId, const String &value, CSSPrimitiveValue::UnitTypes type, bool important)
738 {
739     ASSERT(!m_iteratorCount);
740
741     setPropertyInternal(CSSProperty(propertyId, CSSPrimitiveValue::create(value, type), important));
742     setNeedsStyleRecalc();
743 }
744
745 void CSSMutableStyleDeclaration::setImageProperty(int propertyId, const String& url, bool important)
746 {
747     ASSERT(!m_iteratorCount);
748
749     setPropertyInternal(CSSProperty(propertyId, CSSImageValue::create(url), important));
750     setNeedsStyleRecalc();
751 }
752
753 void CSSMutableStyleDeclaration::parseDeclaration(const String& styleDeclaration)
754 {
755     ASSERT(!m_iteratorCount);
756
757 #if ENABLE(MUTATION_OBSERVERS)
758     StyleAttributeMutationScope mutationScope(this);
759 #endif
760
761     m_properties.clear();
762     CSSParser parser(useStrictParsing());
763     parser.parseDeclaration(this, styleDeclaration);
764
765 #if ENABLE(MUTATION_OBSERVERS)
766     mutationScope.enqueueMutationRecord();
767 #endif
768
769     setNeedsStyleRecalc();
770 }
771
772 void CSSMutableStyleDeclaration::addParsedProperties(const CSSProperty* const* properties, int numProperties)
773 {
774     ASSERT(!m_iteratorCount);
775
776 #if ENABLE(MUTATION_OBSERVERS)
777     StyleAttributeMutationScope mutationScope(this);
778 #endif
779
780     m_properties.reserveCapacity(numProperties);
781     for (int i = 0; i < numProperties; ++i)
782         addParsedProperty(*properties[i]);
783
784 #if ENABLE(MUTATION_OBSERVERS)
785     mutationScope.enqueueMutationRecord();
786 #endif
787
788     // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
789     // a notifyChanged argument to this function to follow the model of other functions in this class.
790 }
791
792 void CSSMutableStyleDeclaration::addParsedProperty(const CSSProperty& property)
793 {
794     ASSERT(!m_iteratorCount);
795
796 #if ENABLE(MUTATION_OBSERVERS)
797     StyleAttributeMutationScope mutationScope(this);
798 #endif
799
800     // Only add properties that have no !important counterpart present
801     if (!getPropertyPriority(property.id()) || property.isImportant()) {
802         removeProperty(property.id(), false, false);
803         m_properties.append(property);
804     }
805
806 #if ENABLE(MUTATION_OBSERVERS)
807     mutationScope.enqueueMutationRecord();
808 #endif
809 }
810
811 void CSSMutableStyleDeclaration::setLengthProperty(int propertyId, const String& value, bool important, bool /*multiLength*/)
812 {
813     ASSERT(!m_iteratorCount);
814
815     bool parseMode = useStrictParsing();
816     setStrictParsing(false);
817     setProperty(propertyId, value, important);
818     setStrictParsing(parseMode);
819 }
820
821 unsigned CSSMutableStyleDeclaration::virtualLength() const
822 {
823     return length();
824 }
825
826 String CSSMutableStyleDeclaration::item(unsigned i) const
827 {
828     if (i >= m_properties.size())
829        return "";
830     return getPropertyName(static_cast<CSSPropertyID>(m_properties[i].id()));
831 }
832
833 String CSSMutableStyleDeclaration::cssText() const
834 {
835     String result = "";
836
837     const CSSProperty* positionXProp = 0;
838     const CSSProperty* positionYProp = 0;
839     const CSSProperty* repeatXProp = 0;
840     const CSSProperty* repeatYProp = 0;
841
842     unsigned size = m_properties.size();
843     for (unsigned n = 0; n < size; ++n) {
844         const CSSProperty& prop = m_properties[n];
845         if (prop.id() == CSSPropertyBackgroundPositionX)
846             positionXProp = &prop;
847         else if (prop.id() == CSSPropertyBackgroundPositionY)
848             positionYProp = &prop;
849         else if (prop.id() == CSSPropertyBackgroundRepeatX)
850             repeatXProp = &prop;
851         else if (prop.id() == CSSPropertyBackgroundRepeatY)
852             repeatYProp = &prop;
853         else
854             result += prop.cssText();
855     }
856
857     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
858     // It is required because background-position-x/y are non-standard properties and WebKit generated output
859     // would not work in Firefox (<rdar://problem/5143183>)
860     // It would be a better solution if background-position was CSS_PAIR.
861     if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) {
862         String positionValue;
863         const int properties[2] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
864         if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList())
865             positionValue = getLayeredShorthandValue(properties);
866         else
867             positionValue = positionXProp->value()->cssText() + " " + positionYProp->value()->cssText();
868         result += "background-position: " + positionValue + (positionXProp->isImportant() ? " !important" : "") + "; ";
869     } else {
870         if (positionXProp)
871             result += positionXProp->cssText();
872         if (positionYProp)
873             result += positionYProp->cssText();
874     }
875
876     // FIXME: We need to do the same for background-repeat.
877     if (repeatXProp && repeatYProp && repeatXProp->isImportant() == repeatYProp->isImportant()) {
878         String repeatValue;
879         const int repeatProperties[2] = { CSSPropertyBackgroundRepeatX, CSSPropertyBackgroundRepeatY };
880         if (repeatXProp->value()->isValueList() || repeatYProp->value()->isValueList())
881             repeatValue = getLayeredShorthandValue(repeatProperties);
882         else
883             repeatValue = repeatXProp->value()->cssText() + " " + repeatYProp->value()->cssText();
884         result += "background-repeat: " + repeatValue + (repeatXProp->isImportant() ? " !important" : "") + "; ";
885     } else {
886         if (repeatXProp)
887             result += repeatXProp->cssText();
888         if (repeatYProp)
889             result += repeatYProp->cssText();
890     }
891
892     return result;
893 }
894
895 void CSSMutableStyleDeclaration::setCssText(const String& text, ExceptionCode& ec)
896 {
897     ec = 0;
898     // FIXME: Detect syntax errors and set ec.
899     parseDeclaration(text);
900 }
901
902 void CSSMutableStyleDeclaration::merge(const CSSMutableStyleDeclaration* other, bool argOverridesOnConflict)
903 {
904     ASSERT(!m_iteratorCount);
905
906 #if ENABLE(MUTATION_OBSERVERS)
907     StyleAttributeMutationScope mutationScope(this);
908 #endif
909
910     unsigned size = other->m_properties.size();
911     for (unsigned n = 0; n < size; ++n) {
912         const CSSProperty& toMerge = other->m_properties[n];
913         CSSProperty* old = findPropertyWithId(toMerge.id());
914         if (old) {
915             if (!argOverridesOnConflict && old->value())
916                 continue;
917             setPropertyInternal(toMerge, old);
918         } else
919             m_properties.append(toMerge);
920     }
921
922 #if ENABLE(MUTATION_OBSERVERS)
923     mutationScope.enqueueMutationRecord();
924 #endif
925
926     // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
927     // a notifyChanged argument to this function to follow the model of other functions in this class.
928 }
929
930 void CSSMutableStyleDeclaration::addSubresourceStyleURLs(ListHashSet<KURL>& urls)
931 {
932     CSSStyleSheet* sheet = parentStyleSheet();
933     size_t size = m_properties.size();
934     for (size_t i = 0; i < size; ++i)
935         m_properties[i].value()->addSubresourceStyleURLs(urls, sheet);
936 }
937
938 // This is the list of properties we want to copy in the copyBlockProperties() function.
939 // It is the list of CSS properties that apply specially to block-level elements.
940 static const int blockProperties[] = {
941     CSSPropertyOrphans,
942     CSSPropertyOverflow, // This can be also be applied to replaced elements
943     CSSPropertyWebkitAspectRatio,
944     CSSPropertyWebkitColumnCount,
945     CSSPropertyWebkitColumnGap,
946     CSSPropertyWebkitColumnRuleColor,
947     CSSPropertyWebkitColumnRuleStyle,
948     CSSPropertyWebkitColumnRuleWidth,
949     CSSPropertyWebkitColumnBreakBefore,
950     CSSPropertyWebkitColumnBreakAfter,
951     CSSPropertyWebkitColumnBreakInside,
952     CSSPropertyWebkitColumnWidth,
953     CSSPropertyPageBreakAfter,
954     CSSPropertyPageBreakBefore,
955     CSSPropertyPageBreakInside,
956     CSSPropertyWebkitRegionBreakAfter,
957     CSSPropertyWebkitRegionBreakBefore,
958     CSSPropertyWebkitRegionBreakInside,
959     CSSPropertyTextAlign,
960     CSSPropertyTextIndent,
961     CSSPropertyWidows
962 };
963
964 const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
965
966 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copyBlockProperties() const
967 {
968     return copyPropertiesInSet(blockProperties, numBlockProperties);
969 }
970
971 void CSSMutableStyleDeclaration::removeBlockProperties()
972 {
973     removePropertiesInSet(blockProperties, numBlockProperties);
974 }
975
976 void CSSMutableStyleDeclaration::removePropertiesInSet(const int* set, unsigned length, bool notifyChanged)
977 {
978     ASSERT(!m_iteratorCount);
979
980     if (m_properties.isEmpty())
981         return;
982
983 #if ENABLE(MUTATION_OBSERVERS)
984     StyleAttributeMutationScope mutationScope(this);
985 #endif
986
987     // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
988     HashSet<int> toRemove;
989     for (unsigned i = 0; i < length; ++i)
990         toRemove.add(set[i]);
991
992     Vector<CSSProperty, 4> newProperties;
993     newProperties.reserveInitialCapacity(m_properties.size());
994
995     unsigned size = m_properties.size();
996     for (unsigned n = 0; n < size; ++n) {
997         const CSSProperty& property = m_properties[n];
998         // Not quite sure if the isImportant test is needed but it matches the existing behavior.
999         if (!property.isImportant()) {
1000             if (toRemove.contains(property.id()))
1001                 continue;
1002         }
1003         newProperties.append(property);
1004     }
1005
1006     bool changed = newProperties.size() != m_properties.size();
1007     m_properties = newProperties;
1008
1009     if (!changed || !notifyChanged)
1010         return;
1011
1012 #if ENABLE(MUTATION_OBSERVERS)
1013     mutationScope.enqueueMutationRecord();
1014 #endif
1015
1016     setNeedsStyleRecalc();
1017 }
1018
1019 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable()
1020 {
1021     return this;
1022 }
1023
1024 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const
1025 {
1026     return adoptRef(new CSSMutableStyleDeclaration(0, m_properties));
1027 }
1028
1029 const CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID) const
1030 {
1031     for (int n = m_properties.size() - 1 ; n >= 0; --n) {
1032         if (propertyID == m_properties[n].m_id)
1033             return &m_properties[n];
1034     }
1035     return 0;
1036 }
1037
1038 CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID)
1039 {
1040     for (int n = m_properties.size() - 1 ; n >= 0; --n) {
1041         if (propertyID == m_properties[n].m_id)
1042             return &m_properties[n];
1043     }
1044     return 0;
1045 }
1046
1047 CSSStyleSheet* CSSElementStyleDeclaration::styleSheet() const
1048 {
1049     if (m_element && m_element->document())
1050         return m_element->document()->elementSheet();
1051     return 0;
1052 }
1053
1054 } // namespace WebCore