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