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