getPropertyValue for -webkit-column-rule returns null, should compute the shorthand...
[WebKit-https.git] / Source / WebCore / css / StylePropertySet.cpp
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 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 "StylePropertySet.h"
24
25 #include "CSSParser.h"
26 #include "CSSValueKeywords.h"
27 #include "CSSValueList.h"
28 #include "CSSValuePool.h"
29 #include "Document.h"
30 #include "PropertySetCSSStyleDeclaration.h"
31 #include "StylePropertyShorthand.h"
32 #include "StyleSheetContents.h"
33 #include <wtf/BitArray.h>
34 #include <wtf/MemoryInstrumentationVector.h>
35 #include <wtf/text/StringBuilder.h>
36
37 #if ENABLE(CSS_VARIABLES)
38 #include "CSSVariableValue.h"
39 #endif
40
41 #ifndef NDEBUG
42 #include <stdio.h>
43 #include <wtf/ASCIICType.h>
44 #include <wtf/text/CString.h>
45 #endif
46
47 using namespace std;
48
49 namespace WebCore {
50
51 typedef HashMap<const StylePropertySet*, OwnPtr<PropertySetCSSStyleDeclaration> > PropertySetCSSOMWrapperMap;
52 static PropertySetCSSOMWrapperMap& propertySetCSSOMWrapperMap()
53 {
54     DEFINE_STATIC_LOCAL(PropertySetCSSOMWrapperMap, propertySetCSSOMWrapperMapInstance, ());
55     return propertySetCSSOMWrapperMapInstance;
56 }
57
58 static size_t sizeForImmutableStylePropertySetWithPropertyCount(unsigned count)
59 {
60     return sizeof(ImmutableStylePropertySet) - sizeof(void*) + sizeof(CSSValue*) * count + sizeof(StylePropertyMetadata) * count;
61 }
62
63 static bool isInitialOrInherit(const String& value)
64 {
65     DEFINE_STATIC_LOCAL(String, initial, ("initial"));
66     DEFINE_STATIC_LOCAL(String, inherit, ("inherit"));
67     return value.length() == 7 && (value == initial || value == inherit);
68 }
69
70 PassRefPtr<StylePropertySet> StylePropertySet::createImmutable(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode)
71 {
72     void* slot = WTF::fastMalloc(sizeForImmutableStylePropertySetWithPropertyCount(count));
73     return adoptRef(new (slot) ImmutableStylePropertySet(properties, count, cssParserMode));
74 }
75
76 PassRefPtr<StylePropertySet> StylePropertySet::immutableCopyIfNeeded() const
77 {
78     if (!isMutable())
79         return const_cast<StylePropertySet*>(this);
80     return createImmutable(mutablePropertyVector().data(), mutablePropertyVector().size(), cssParserMode());
81 }
82
83 MutableStylePropertySet::MutableStylePropertySet(const CSSProperty* properties, unsigned length)
84     : StylePropertySet(CSSStrictMode)
85 {
86     m_propertyVector.reserveInitialCapacity(length);
87     for (unsigned i = 0; i < length; ++i)
88         m_propertyVector.uncheckedAppend(properties[i]);
89 }
90
91 ImmutableStylePropertySet::ImmutableStylePropertySet(const CSSProperty* properties, unsigned length, CSSParserMode cssParserMode)
92     : StylePropertySet(cssParserMode, length)
93 {
94     StylePropertyMetadata* metadataArray = const_cast<StylePropertyMetadata*>(immutableMetadataArray());
95     CSSValue** valueArray = const_cast<CSSValue**>(immutableValueArray());
96     for (unsigned i = 0; i < length; ++i) {
97         metadataArray[i] = properties[i].metadata();
98         valueArray[i] = properties[i].value();
99         valueArray[i]->ref();
100     }
101 }
102
103 ImmutableStylePropertySet::~ImmutableStylePropertySet()
104 {
105     CSSValue** valueArray = const_cast<CSSValue**>(immutableValueArray());
106     for (unsigned i = 0; i < m_arraySize; ++i)
107         valueArray[i]->deref();
108 }
109
110 MutableStylePropertySet::MutableStylePropertySet(const StylePropertySet& other)
111     : StylePropertySet(other.cssParserMode())
112 {
113     if (other.isMutable())
114         m_propertyVector = static_cast<const MutableStylePropertySet&>(other).mutablePropertyVector();
115     else {
116         m_propertyVector.reserveInitialCapacity(other.propertyCount());
117         for (unsigned i = 0; i < other.propertyCount(); ++i)
118             m_propertyVector.uncheckedAppend(other.propertyAt(i).toCSSProperty());
119     }
120 }
121
122 StylePropertySet::~StylePropertySet()
123 {
124     ASSERT(!m_ownsCSSOMWrapper || propertySetCSSOMWrapperMap().contains(this));
125     if (m_ownsCSSOMWrapper)
126         propertySetCSSOMWrapperMap().remove(this);
127 }
128
129 String StylePropertySet::getPropertyValue(CSSPropertyID propertyID) const
130 {
131     RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
132     if (value)
133         return value->cssText();
134
135     // Shorthand and 4-values properties
136     switch (propertyID) {
137     case CSSPropertyBorderSpacing:
138         return borderSpacingValue(borderSpacingShorthand());
139     case CSSPropertyBackgroundPosition:
140         return getLayeredShorthandValue(backgroundPositionShorthand());
141     case CSSPropertyBackgroundRepeat:
142         return getLayeredShorthandValue(backgroundRepeatShorthand());
143     case CSSPropertyBackground:
144         return getLayeredShorthandValue(backgroundShorthand());
145     case CSSPropertyBorder:
146         return borderPropertyValue(OmitUncommonValues);
147     case CSSPropertyBorderTop:
148         return getShorthandValue(borderTopShorthand());
149     case CSSPropertyBorderRight:
150         return getShorthandValue(borderRightShorthand());
151     case CSSPropertyBorderBottom:
152         return getShorthandValue(borderBottomShorthand());
153     case CSSPropertyBorderLeft:
154         return getShorthandValue(borderLeftShorthand());
155     case CSSPropertyOutline:
156         return getShorthandValue(outlineShorthand());
157     case CSSPropertyBorderColor:
158         return get4Values(borderColorShorthand());
159     case CSSPropertyBorderWidth:
160         return get4Values(borderWidthShorthand());
161     case CSSPropertyBorderStyle:
162         return get4Values(borderStyleShorthand());
163     case CSSPropertyWebkitColumnRule:
164         return getShorthandValue(webkitColumnRuleShorthand());
165     case CSSPropertyWebkitFlex:
166         return getShorthandValue(webkitFlexShorthand());
167     case CSSPropertyWebkitFlexFlow:
168         return getShorthandValue(webkitFlexFlowShorthand());
169     case CSSPropertyFont:
170         return fontValue();
171     case CSSPropertyMargin:
172         return get4Values(marginShorthand());
173     case CSSPropertyWebkitMarginCollapse:
174         return getShorthandValue(webkitMarginCollapseShorthand());
175     case CSSPropertyOverflow:
176         return getCommonValue(overflowShorthand());
177     case CSSPropertyPadding:
178         return get4Values(paddingShorthand());
179     case CSSPropertyListStyle:
180         return getShorthandValue(listStyleShorthand());
181     case CSSPropertyWebkitMarquee:
182         return getShorthandValue(webkitMarqueeShorthand());
183     case CSSPropertyWebkitMaskPosition:
184         return getLayeredShorthandValue(webkitMaskPositionShorthand());
185     case CSSPropertyWebkitMaskRepeat:
186         return getLayeredShorthandValue(webkitMaskRepeatShorthand());
187     case CSSPropertyWebkitMask:
188         return getLayeredShorthandValue(webkitMaskShorthand());
189     case CSSPropertyWebkitTextEmphasis:
190         return getShorthandValue(webkitTextEmphasisShorthand());
191     case CSSPropertyWebkitTransformOrigin:
192         return getShorthandValue(webkitTransformOriginShorthand());
193     case CSSPropertyWebkitTransition:
194         return getLayeredShorthandValue(webkitTransitionShorthand());
195     case CSSPropertyWebkitAnimation:
196         return getLayeredShorthandValue(webkitAnimationShorthand());
197 #if ENABLE(CSS_EXCLUSIONS)
198     case CSSPropertyWebkitWrap:
199         return getShorthandValue(webkitWrapShorthand());
200 #endif
201 #if ENABLE(SVG)
202     case CSSPropertyMarker: {
203         RefPtr<CSSValue> value = getPropertyCSSValue(CSSPropertyMarkerStart);
204         if (value)
205             return value->cssText();
206         return String();
207     }
208 #endif
209     case CSSPropertyBorderRadius:
210         return get4Values(borderRadiusShorthand());
211     default:
212           return String();
213     }
214 }
215
216 String StylePropertySet::borderSpacingValue(const StylePropertyShorthand& shorthand) const
217 {
218     RefPtr<CSSValue> horizontalValue = getPropertyCSSValue(shorthand.properties()[0]);
219     RefPtr<CSSValue> verticalValue = getPropertyCSSValue(shorthand.properties()[1]);
220
221     // While standard border-spacing property does not allow specifying border-spacing-vertical without
222     // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
223     // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal.
224     if (!horizontalValue || !verticalValue)
225         return String();
226
227     String horizontalValueCSSText = horizontalValue->cssText();
228     String verticalValueCSSText = verticalValue->cssText();
229     if (horizontalValueCSSText == verticalValueCSSText)
230         return horizontalValueCSSText;
231     return horizontalValueCSSText + ' ' + verticalValueCSSText;
232 }
233
234 void StylePropertySet::appendFontLonghandValueIfExplicit(CSSPropertyID propertyID, StringBuilder& result, String& commonValue) const
235 {
236     int foundPropertyIndex = findPropertyIndex(propertyID);
237     if (foundPropertyIndex == -1)
238         return; // All longhands must have at least implicit values if "font" is specified.
239
240     if (propertyAt(foundPropertyIndex).isImplicit()) {
241         commonValue = String();
242         return;
243     }
244
245     char prefix = '\0';
246     switch (propertyID) {
247     case CSSPropertyFontStyle:
248         break; // No prefix.
249     case CSSPropertyFontFamily:
250     case CSSPropertyFontVariant:
251     case CSSPropertyFontWeight:
252         prefix = ' ';
253         break;
254     case CSSPropertyLineHeight:
255         prefix = '/';
256         break;
257     default:
258         ASSERT_NOT_REACHED();
259     }
260
261     if (prefix && !result.isEmpty())
262         result.append(prefix);
263     String value = propertyAt(foundPropertyIndex).value()->cssText();
264     result.append(value);
265     if (!commonValue.isNull() && commonValue != value)
266         commonValue = String();
267 }
268
269 String StylePropertySet::fontValue() const
270 {
271     int fontSizePropertyIndex = findPropertyIndex(CSSPropertyFontSize);
272     int fontFamilyPropertyIndex = findPropertyIndex(CSSPropertyFontFamily);
273     if (fontSizePropertyIndex == -1 || fontFamilyPropertyIndex == -1)
274         return emptyString();
275
276     PropertyReference fontSizeProperty = propertyAt(fontSizePropertyIndex);
277     PropertyReference fontFamilyProperty = propertyAt(fontFamilyPropertyIndex);
278     if (fontSizeProperty.isImplicit() || fontFamilyProperty.isImplicit())
279         return emptyString();
280
281     String commonValue = fontSizeProperty.value()->cssText();
282     StringBuilder result;
283     appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result, commonValue);
284     appendFontLonghandValueIfExplicit(CSSPropertyFontVariant, result, commonValue);
285     appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result, commonValue);
286     if (!result.isEmpty())
287         result.append(' ');
288     result.append(fontSizeProperty.value()->cssText());
289     appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result, commonValue);
290     if (!result.isEmpty())
291         result.append(' ');
292     result.append(fontFamilyProperty.value()->cssText());
293     if (isInitialOrInherit(commonValue))
294         return commonValue;
295     return result.toString();
296 }
297
298 String StylePropertySet::get4Values(const StylePropertyShorthand& shorthand) const
299 {
300     // Assume the properties are in the usual order top, right, bottom, left.
301     int topValueIndex = findPropertyIndex(shorthand.properties()[0]);
302     int rightValueIndex = findPropertyIndex(shorthand.properties()[1]);
303     int bottomValueIndex = findPropertyIndex(shorthand.properties()[2]);
304     int leftValueIndex = findPropertyIndex(shorthand.properties()[3]);
305
306     if (topValueIndex == -1 || rightValueIndex == -1 || bottomValueIndex == -1 || leftValueIndex == -1)
307         return String();
308
309     PropertyReference top = propertyAt(topValueIndex);
310     PropertyReference right = propertyAt(rightValueIndex);
311     PropertyReference bottom = propertyAt(bottomValueIndex);
312     PropertyReference left = propertyAt(leftValueIndex);
313
314     // All 4 properties must be specified.
315     if (!top.value() || !right.value() || !bottom.value() || !left.value())
316         return String();
317
318     if (top.isInherited() && right.isInherited() && bottom.isInherited() && left.isInherited())
319         return getValueName(CSSValueInherit);
320
321     if (top.value()->isInitialValue() || right.value()->isInitialValue() || bottom.value()->isInitialValue() || left.value()->isInitialValue()) {
322         if (top.value()->isInitialValue() && right.value()->isInitialValue() && bottom.value()->isInitialValue() && left.value()->isInitialValue() && !top.isImplicit()) {
323             // All components are "initial" and "top" is not implicit.
324             return getValueName(CSSValueInitial);
325         }
326         return String();
327     }
328     if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant())
329         return String();
330
331     bool showLeft = !right.value()->equals(*left.value());
332     bool showBottom = !top.value()->equals(*bottom.value()) || showLeft;
333     bool showRight = !top.value()->equals(*right.value()) || showBottom;
334
335     StringBuilder result;
336     result.append(top.value()->cssText());
337     if (showRight) {
338         result.append(' ');
339         result.append(right.value()->cssText());
340     }
341     if (showBottom) {
342         result.append(' ');
343         result.append(bottom.value()->cssText());
344     }
345     if (showLeft) {
346         result.append(' ');
347         result.append(left.value()->cssText());
348     }
349     return result.toString();
350 }
351
352 String StylePropertySet::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const
353 {
354     StringBuilder result;
355
356     const unsigned size = shorthand.length();
357     // Begin by collecting the properties into an array.
358     Vector< RefPtr<CSSValue> > values(size);
359     size_t numLayers = 0;
360
361     for (unsigned i = 0; i < size; ++i) {
362         values[i] = getPropertyCSSValue(shorthand.properties()[i]);
363         if (values[i]) {
364             if (values[i]->isBaseValueList()) {
365                 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
366                 numLayers = max(valueList->length(), numLayers);
367             } else
368                 numLayers = max<size_t>(1U, numLayers);
369         }
370     }
371
372     String commonValue;
373     bool commonValueInitialized = false;
374
375     // Now stitch the properties together.  Implicit initial values are flagged as such and
376     // can safely be omitted.
377     for (size_t i = 0; i < numLayers; i++) {
378         StringBuilder layerResult;
379         bool useRepeatXShorthand = false;
380         bool useRepeatYShorthand = false;
381         bool useSingleWordShorthand = false;
382         bool foundPositionYCSSProperty = false;
383         for (unsigned j = 0; j < size; j++) {
384             RefPtr<CSSValue> value;
385             if (values[j]) {
386                 if (values[j]->isBaseValueList())
387                     value = static_cast<CSSValueList*>(values[j].get())->item(i);
388                 else {
389                     value = values[j];
390
391                     // Color only belongs in the last layer.
392                     if (shorthand.properties()[j] == CSSPropertyBackgroundColor) {
393                         if (i != numLayers - 1)
394                             value = 0;
395                     } else if (i != 0) // Other singletons only belong in the first layer.
396                         value = 0;
397                 }
398             }
399
400             // We need to report background-repeat as it was written in the CSS. If the property is implicit,
401             // then it was written with only one value. Here we figure out which value that was so we can
402             // report back correctly.
403             if ((shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(shorthand.properties()[j]))
404                 || (shorthand.properties()[j] == CSSPropertyWebkitMaskRepeatX && isPropertyImplicit(shorthand.properties()[j]))) {
405
406                 // BUG 49055: make sure the value was not reset in the layer check just above.
407                 if ((j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value)
408                     || (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyWebkitMaskRepeatY && value)) {
409                     RefPtr<CSSValue> yValue;
410                     RefPtr<CSSValue> nextValue = values[j + 1];
411                     if (nextValue->isValueList())
412                         yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
413                     else
414                         yValue = nextValue;
415
416                     int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
417                     int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
418                     if (xId != yId) {
419                         if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
420                             useRepeatXShorthand = true;
421                             ++j;
422                         } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
423                             useRepeatYShorthand = true;
424                             continue;
425                         }
426                     } else {
427                         useSingleWordShorthand = true;
428                         ++j;
429                     }
430                 }
431             }
432
433             String valueText;
434             if (value && !value->isImplicitInitialValue()) {
435                 if (!layerResult.isEmpty())
436                     layerResult.append(' ');
437                 if (foundPositionYCSSProperty
438                     && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
439                     layerResult.appendLiteral("/ ");
440                 if (!foundPositionYCSSProperty
441                     && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
442                     continue;
443
444                 if (useRepeatXShorthand) {
445                     useRepeatXShorthand = false;
446                     layerResult.append(getValueName(CSSValueRepeatX));
447                 } else if (useRepeatYShorthand) {
448                     useRepeatYShorthand = false;
449                     layerResult.append(getValueName(CSSValueRepeatY));
450                 } else {
451                     if (useSingleWordShorthand)
452                         useSingleWordShorthand = false;
453                     valueText = value->cssText();
454                     layerResult.append(valueText);
455                 }
456
457                 if (shorthand.properties()[j] == CSSPropertyBackgroundPositionY
458                     || shorthand.properties()[j] == CSSPropertyWebkitMaskPositionY) {
459                     foundPositionYCSSProperty = true;
460
461                     // background-position is a special case: if only the first offset is specified,
462                     // the second one defaults to "center", not the same value.
463                     if (commonValueInitialized && commonValue != "initial" && commonValue != "inherit")
464                         commonValue = String();
465                 }
466             }
467
468             if (!commonValueInitialized) {
469                 commonValue = valueText;
470                 commonValueInitialized = true;
471             } else if (!commonValue.isNull() && commonValue != valueText)
472                 commonValue = String();
473         }
474
475         if (!layerResult.isEmpty()) {
476             if (!result.isEmpty())
477                 result.appendLiteral(", ");
478             result.append(layerResult);
479         }
480     }
481
482     if (isInitialOrInherit(commonValue))
483         return commonValue;
484
485     if (result.isEmpty())
486         return String();
487     return result.toString();
488 }
489
490 String StylePropertySet::getShorthandValue(const StylePropertyShorthand& shorthand) const
491 {
492     String commonValue;
493     StringBuilder result;
494     for (unsigned i = 0; i < shorthand.length(); ++i) {
495         if (!isPropertyImplicit(shorthand.properties()[i])) {
496             RefPtr<CSSValue> value = getPropertyCSSValue(shorthand.properties()[i]);
497             if (!value)
498                 return String();
499             String valueText = value->cssText();
500             if (!i)
501                 commonValue = valueText;
502             else if (!commonValue.isNull() && commonValue != valueText)
503                 commonValue = String();
504             if (value->isInitialValue())
505                 continue;
506             if (!result.isEmpty())
507                 result.append(' ');
508             result.append(valueText);
509         } else
510             commonValue = String();
511     }
512     if (isInitialOrInherit(commonValue))
513         return commonValue;
514     if (result.isEmpty())
515         return String();
516     return result.toString();
517 }
518
519 // only returns a non-null value if all properties have the same, non-null value
520 String StylePropertySet::getCommonValue(const StylePropertyShorthand& shorthand) const
521 {
522     String res;
523     bool lastPropertyWasImportant = false;
524     for (unsigned i = 0; i < shorthand.length(); ++i) {
525         RefPtr<CSSValue> value = getPropertyCSSValue(shorthand.properties()[i]);
526         // FIXME: CSSInitialValue::cssText should generate the right value.
527         if (!value)
528             return String();
529         String text = value->cssText();
530         if (text.isNull())
531             return String();
532         if (res.isNull())
533             res = text;
534         else if (res != text)
535             return String();
536
537         bool currentPropertyIsImportant = propertyIsImportant(shorthand.properties()[i]);
538         if (i && lastPropertyWasImportant != currentPropertyIsImportant)
539             return String();
540         lastPropertyWasImportant = currentPropertyIsImportant;
541     }
542     return res;
543 }
544
545 String StylePropertySet::borderPropertyValue(CommonValueMode valueMode) const
546 {
547     const StylePropertyShorthand properties[3] = { borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand() };
548     String commonValue;
549     StringBuilder result;
550     for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
551         String value = getCommonValue(properties[i]);
552         if (value.isNull()) {
553             if (valueMode == ReturnNullOnUncommonValues)
554                 return String();
555             ASSERT(valueMode == OmitUncommonValues);
556             continue;
557         }
558         if (!i)
559             commonValue = value;
560         else if (!commonValue.isNull() && commonValue != value)
561             commonValue = String();
562         if (value == "initial")
563             continue;
564         if (!result.isEmpty())
565             result.append(' ');
566         result.append(value);
567     }
568     if (isInitialOrInherit(commonValue))
569         return commonValue;
570     return result.isEmpty() ? String() : result.toString();
571 }
572
573 PassRefPtr<CSSValue> StylePropertySet::getPropertyCSSValue(CSSPropertyID propertyID) const
574 {
575     int foundPropertyIndex = findPropertyIndex(propertyID);
576     if (foundPropertyIndex == -1)
577         return 0;
578     return propertyAt(foundPropertyIndex).value();
579 }
580
581 bool StylePropertySet::removeShorthandProperty(CSSPropertyID propertyID)
582 {
583     ASSERT(isMutable());
584     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
585     if (!shorthand.length())
586         return false;
587     return removePropertiesInSet(shorthand.properties(), shorthand.length());
588 }
589
590 bool StylePropertySet::removeProperty(CSSPropertyID propertyID, String* returnText)
591 {
592     ASSERT(isMutable());
593     if (removeShorthandProperty(propertyID)) {
594         // FIXME: Return an equivalent shorthand when possible.
595         if (returnText)
596             *returnText = "";
597         return true;
598     }
599
600     int foundPropertyIndex = findPropertyIndex(propertyID);
601     if (foundPropertyIndex == -1) {
602         if (returnText)
603             *returnText = "";
604         return false;
605     }
606
607     if (returnText)
608         *returnText = propertyAt(foundPropertyIndex).value()->cssText();
609
610     // A more efficient removal strategy would involve marking entries as empty
611     // and sweeping them when the vector grows too big.
612     mutablePropertyVector().remove(foundPropertyIndex);
613     
614     return true;
615 }
616
617 bool StylePropertySet::propertyIsImportant(CSSPropertyID propertyID) const
618 {
619     int foundPropertyIndex = findPropertyIndex(propertyID);
620     if (foundPropertyIndex != -1)
621         return propertyAt(foundPropertyIndex).isImportant();
622
623     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
624     if (!shorthand.length())
625         return false;
626
627     for (unsigned i = 0; i < shorthand.length(); ++i) {
628         if (!propertyIsImportant(shorthand.properties()[i]))
629             return false;
630     }
631     return true;
632 }
633
634 CSSPropertyID StylePropertySet::getPropertyShorthand(CSSPropertyID propertyID) const
635 {
636     int foundPropertyIndex = findPropertyIndex(propertyID);
637     if (foundPropertyIndex == -1)
638         return CSSPropertyInvalid;
639     return propertyAt(foundPropertyIndex).shorthandID();
640 }
641
642 bool StylePropertySet::isPropertyImplicit(CSSPropertyID propertyID) const
643 {
644     int foundPropertyIndex = findPropertyIndex(propertyID);
645     if (foundPropertyIndex == -1)
646         return false;
647     return propertyAt(foundPropertyIndex).isImplicit();
648 }
649
650 bool StylePropertySet::setProperty(CSSPropertyID propertyID, const String& value, bool important, StyleSheetContents* contextStyleSheet)
651 {
652     ASSERT(isMutable());
653     // Setting the value to an empty string just removes the property in both IE and Gecko.
654     // Setting it to null seems to produce less consistent results, but we treat it just the same.
655     if (value.isEmpty()) {
656         removeProperty(propertyID);
657         return true;
658     }
659
660     // When replacing an existing property value, this moves the property to the end of the list.
661     // Firefox preserves the position, and MSIE moves the property to the beginning.
662     return CSSParser::parseValue(this, propertyID, value, important, cssParserMode(), contextStyleSheet);
663 }
664
665 void StylePropertySet::setProperty(CSSPropertyID propertyID, PassRefPtr<CSSValue> prpValue, bool important)
666 {
667     ASSERT(isMutable());
668     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
669     if (!shorthand.length()) {
670         setProperty(CSSProperty(propertyID, prpValue, important));
671         return;
672     }
673
674     removePropertiesInSet(shorthand.properties(), shorthand.length());
675
676     RefPtr<CSSValue> value = prpValue;
677     for (unsigned i = 0; i < shorthand.length(); ++i)
678         mutablePropertyVector().append(CSSProperty(shorthand.properties()[i], value, important));
679 }
680
681 void StylePropertySet::setProperty(const CSSProperty& property, CSSProperty* slot)
682 {
683     ASSERT(isMutable());
684     if (!removeShorthandProperty(property.id())) {
685         CSSProperty* toReplace = slot ? slot : findMutableCSSPropertyWithID(property.id());
686         if (toReplace) {
687             *toReplace = property;
688             return;
689         }
690     }
691     mutablePropertyVector().append(property);
692 }
693
694 bool StylePropertySet::setProperty(CSSPropertyID propertyID, int identifier, bool important)
695 {
696     ASSERT(isMutable());
697     setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
698     return true;
699 }
700
701 void StylePropertySet::parseDeclaration(const String& styleDeclaration, StyleSheetContents* contextStyleSheet)
702 {
703     ASSERT(isMutable());
704
705     mutablePropertyVector().clear();
706
707     CSSParserContext context(cssParserMode());
708     if (contextStyleSheet) {
709         context = contextStyleSheet->parserContext();
710         context.mode = cssParserMode();
711     }
712     CSSParser parser(context);
713     parser.parseDeclaration(this, styleDeclaration, 0, contextStyleSheet);
714 }
715
716 void StylePropertySet::addParsedProperties(const Vector<CSSProperty>& properties)
717 {
718     ASSERT(isMutable());
719     mutablePropertyVector().reserveCapacity(mutablePropertyVector().size() + properties.size());
720     for (unsigned i = 0; i < properties.size(); ++i)
721         addParsedProperty(properties[i]);
722 }
723
724 void StylePropertySet::addParsedProperty(const CSSProperty& property)
725 {
726     ASSERT(isMutable());
727     // Only add properties that have no !important counterpart present
728     if (!propertyIsImportant(property.id()) || property.isImportant())
729         setProperty(property);
730 }
731
732 String StylePropertySet::asText() const
733 {
734     StringBuilder result;
735
736     int positionXPropertyIndex = -1;
737     int positionYPropertyIndex = -1;
738     int repeatXPropertyIndex = -1;
739     int repeatYPropertyIndex = -1;
740
741     BitArray<numCSSProperties> shorthandPropertyUsed;
742     BitArray<numCSSProperties> shorthandPropertyAppeared;
743
744     unsigned size = propertyCount();
745     unsigned numDecls = 0;
746     for (unsigned n = 0; n < size; ++n) {
747         PropertyReference property = propertyAt(n);
748         CSSPropertyID propertyID = property.id();
749         CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
750         CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
751         String value;
752
753         switch (propertyID) {
754 #if ENABLE(CSS_VARIABLES)
755         case CSSPropertyVariable:
756             if (numDecls++)
757                 result.append(' ');
758             result.append(property.cssText());
759             continue;
760 #endif
761         case CSSPropertyBackgroundPositionX:
762             positionXPropertyIndex = n;
763             continue;
764         case CSSPropertyBackgroundPositionY:
765             positionYPropertyIndex = n;
766             continue;
767         case CSSPropertyBackgroundRepeatX:
768             repeatXPropertyIndex = n;
769             continue;
770         case CSSPropertyBackgroundRepeatY:
771             repeatYPropertyIndex = n;
772             continue;
773         case CSSPropertyBorderTopWidth:
774         case CSSPropertyBorderRightWidth:
775         case CSSPropertyBorderBottomWidth:
776         case CSSPropertyBorderLeftWidth:
777             if (!borderFallbackShorthandProperty)
778                 borderFallbackShorthandProperty = CSSPropertyBorderWidth;
779         case CSSPropertyBorderTopStyle:
780         case CSSPropertyBorderRightStyle:
781         case CSSPropertyBorderBottomStyle:
782         case CSSPropertyBorderLeftStyle:
783             if (!borderFallbackShorthandProperty)
784                 borderFallbackShorthandProperty = CSSPropertyBorderStyle;
785         case CSSPropertyBorderTopColor:
786         case CSSPropertyBorderRightColor:
787         case CSSPropertyBorderBottomColor:
788         case CSSPropertyBorderLeftColor:
789             if (!borderFallbackShorthandProperty)
790                 borderFallbackShorthandProperty = CSSPropertyBorderColor;
791
792             // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
793             if (!shorthandPropertyAppeared.get(CSSPropertyBorder - firstCSSProperty)) {
794                 value = borderPropertyValue(ReturnNullOnUncommonValues);
795                 if (value.isNull())
796                     shorthandPropertyAppeared.set(CSSPropertyBorder - firstCSSProperty);
797                 else
798                     shorthandPropertyID = CSSPropertyBorder;
799             } else if (shorthandPropertyUsed.get(CSSPropertyBorder - firstCSSProperty))
800                 shorthandPropertyID = CSSPropertyBorder;
801             if (!shorthandPropertyID)
802                 shorthandPropertyID = borderFallbackShorthandProperty;
803             break;
804         case CSSPropertyWebkitBorderHorizontalSpacing:
805         case CSSPropertyWebkitBorderVerticalSpacing:
806             shorthandPropertyID = CSSPropertyBorderSpacing;
807             break;
808         case CSSPropertyFontFamily:
809         case CSSPropertyLineHeight:
810         case CSSPropertyFontSize:
811         case CSSPropertyFontStyle:
812         case CSSPropertyFontVariant:
813         case CSSPropertyFontWeight:
814             // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
815             break;
816         case CSSPropertyListStyleType:
817         case CSSPropertyListStylePosition:
818         case CSSPropertyListStyleImage:
819             shorthandPropertyID = CSSPropertyListStyle;
820             break;
821         case CSSPropertyMarginTop:
822         case CSSPropertyMarginRight:
823         case CSSPropertyMarginBottom:
824         case CSSPropertyMarginLeft:
825             shorthandPropertyID = CSSPropertyMargin;
826             break;
827         case CSSPropertyOutlineWidth:
828         case CSSPropertyOutlineStyle:
829         case CSSPropertyOutlineColor:
830             shorthandPropertyID = CSSPropertyOutline;
831             break;
832         case CSSPropertyOverflowX:
833         case CSSPropertyOverflowY:
834             shorthandPropertyID = CSSPropertyOverflow;
835             break;
836         case CSSPropertyPaddingTop:
837         case CSSPropertyPaddingRight:
838         case CSSPropertyPaddingBottom:
839         case CSSPropertyPaddingLeft:
840             shorthandPropertyID = CSSPropertyPadding;
841             break;
842         case CSSPropertyWebkitAnimationName:
843         case CSSPropertyWebkitAnimationDuration:
844         case CSSPropertyWebkitAnimationTimingFunction:
845         case CSSPropertyWebkitAnimationDelay:
846         case CSSPropertyWebkitAnimationIterationCount:
847         case CSSPropertyWebkitAnimationDirection:
848         case CSSPropertyWebkitAnimationFillMode:
849             shorthandPropertyID = CSSPropertyWebkitAnimation;
850             break;
851         case CSSPropertyWebkitFlexDirection:
852         case CSSPropertyWebkitFlexWrap:
853             shorthandPropertyID = CSSPropertyWebkitFlexFlow;
854             break;
855         case CSSPropertyWebkitFlexBasis:
856         case CSSPropertyWebkitFlexGrow:
857         case CSSPropertyWebkitFlexShrink:
858             shorthandPropertyID = CSSPropertyWebkitFlex;
859             break;
860         case CSSPropertyWebkitMaskPositionX:
861         case CSSPropertyWebkitMaskPositionY:
862         case CSSPropertyWebkitMaskRepeatX:
863         case CSSPropertyWebkitMaskRepeatY:
864         case CSSPropertyWebkitMaskImage:
865         case CSSPropertyWebkitMaskRepeat:
866         case CSSPropertyWebkitMaskPosition:
867         case CSSPropertyWebkitMaskClip:
868         case CSSPropertyWebkitMaskOrigin:
869             shorthandPropertyID = CSSPropertyWebkitMask;
870             break;
871         case CSSPropertyWebkitTransformOriginX:
872         case CSSPropertyWebkitTransformOriginY:
873         case CSSPropertyWebkitTransformOriginZ:
874             shorthandPropertyID = CSSPropertyWebkitTransformOrigin;
875             break;
876         case CSSPropertyWebkitTransitionProperty:
877         case CSSPropertyWebkitTransitionDuration:
878         case CSSPropertyWebkitTransitionTimingFunction:
879         case CSSPropertyWebkitTransitionDelay:
880             shorthandPropertyID = CSSPropertyWebkitTransition;
881             break;
882 #if ENABLE(CSS_EXCLUSIONS)
883         case CSSPropertyWebkitWrapFlow:
884         case CSSPropertyWebkitShapeMargin:
885         case CSSPropertyWebkitShapePadding:
886             shorthandPropertyID = CSSPropertyWebkitWrap;
887             break;
888 #endif
889         default:
890             break;
891         }
892
893         unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
894         if (shorthandPropertyID) {
895             if (shorthandPropertyUsed.get(shortPropertyIndex))
896                 continue;
897             if (!shorthandPropertyAppeared.get(shortPropertyIndex) && value.isNull())
898                 value = getPropertyValue(shorthandPropertyID);
899             shorthandPropertyAppeared.set(shortPropertyIndex);
900         }
901
902         if (!value.isNull()) {
903             propertyID = shorthandPropertyID;
904             shorthandPropertyUsed.set(shortPropertyIndex);
905         } else
906             value = property.value()->cssText();
907
908         if (value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
909             continue;
910
911         if (numDecls++)
912             result.append(' ');
913         result.append(getPropertyName(propertyID));
914         result.appendLiteral(": ");
915         result.append(value);
916         if (property.isImportant())
917             result.appendLiteral(" !important");
918         result.append(';');
919     }
920
921     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
922     // It is required because background-position-x/y are non-standard properties and WebKit generated output
923     // would not work in Firefox (<rdar://problem/5143183>)
924     // It would be a better solution if background-position was CSS_PAIR.
925     if (positionXPropertyIndex != -1 && positionYPropertyIndex != -1 && propertyAt(positionXPropertyIndex).isImportant() == propertyAt(positionYPropertyIndex).isImportant()) {
926         PropertyReference positionXProperty = propertyAt(positionXPropertyIndex);
927         PropertyReference positionYProperty = propertyAt(positionYPropertyIndex);
928
929         if (numDecls++)
930             result.append(' ');
931         result.appendLiteral("background-position: ");
932         if (positionXProperty.value()->isValueList() || positionYProperty.value()->isValueList())
933             result.append(getLayeredShorthandValue(backgroundPositionShorthand()));
934         else {
935             result.append(positionXProperty.value()->cssText());
936             result.append(' ');
937             result.append(positionYProperty.value()->cssText());
938         }
939         if (positionXProperty.isImportant())
940             result.appendLiteral(" !important");
941         result.append(';');
942     } else {
943         if (positionXPropertyIndex != -1) {
944             if (numDecls++)
945                 result.append(' ');
946             result.append(propertyAt(positionXPropertyIndex).cssText());
947         }
948         if (positionYPropertyIndex != -1) {
949             if (numDecls++)
950                 result.append(' ');
951             result.append(propertyAt(positionYPropertyIndex).cssText());
952         }
953     }
954
955     // FIXME: We need to do the same for background-repeat.
956     if (repeatXPropertyIndex != -1 && repeatYPropertyIndex != -1 && propertyAt(repeatXPropertyIndex).isImportant() == propertyAt(repeatYPropertyIndex).isImportant()) {
957         PropertyReference repeatXProperty = propertyAt(repeatXPropertyIndex);
958         PropertyReference repeatYProperty = propertyAt(repeatYPropertyIndex);
959
960         if (numDecls++)
961             result.append(' ');
962         result.appendLiteral("background-repeat: ");
963         if (repeatXProperty.value()->isValueList() || repeatYProperty.value()->isValueList())
964             result.append(getLayeredShorthandValue(backgroundRepeatShorthand()));
965         else {
966             result.append(repeatXProperty.value()->cssText());
967             result.append(' ');
968             result.append(repeatYProperty.value()->cssText());
969         }
970         if (repeatXProperty.isImportant())
971             result.appendLiteral(" !important");
972         result.append(';');
973     } else {
974         if (repeatXPropertyIndex != -1) {
975             if (numDecls++)
976                 result.append(' ');
977             result.append(propertyAt(repeatXPropertyIndex).cssText());
978         }
979         if (repeatYPropertyIndex != -1) {
980             if (numDecls++)
981                 result.append(' ');
982             result.append(propertyAt(repeatYPropertyIndex).cssText());
983         }
984     }
985
986     ASSERT(!numDecls ^ !result.isEmpty());
987     return result.toString();
988 }
989
990 void StylePropertySet::mergeAndOverrideOnConflict(const StylePropertySet* other)
991 {
992     ASSERT(isMutable());
993     unsigned size = other->propertyCount();
994     for (unsigned n = 0; n < size; ++n) {
995         PropertyReference toMerge = other->propertyAt(n);
996         CSSProperty* old = findMutableCSSPropertyWithID(toMerge.id());
997         if (old)
998             setProperty(toMerge.toCSSProperty(), old);
999         else
1000             mutablePropertyVector().append(toMerge.toCSSProperty());
1001     }
1002 }
1003
1004 void StylePropertySet::addSubresourceStyleURLs(ListHashSet<KURL>& urls, StyleSheetContents* contextStyleSheet) const
1005 {
1006     unsigned size = propertyCount();
1007     for (unsigned i = 0; i < size; ++i)
1008         propertyAt(i).value()->addSubresourceStyleURLs(urls, contextStyleSheet);
1009 }
1010
1011 bool StylePropertySet::hasFailedOrCanceledSubresources() const
1012 {
1013     unsigned size = propertyCount();
1014     for (unsigned i = 0; i < size; ++i) {
1015         if (propertyAt(i).value()->hasFailedOrCanceledSubresources())
1016             return true;
1017     }
1018     return false;
1019 }
1020
1021 // This is the list of properties we want to copy in the copyBlockProperties() function.
1022 // It is the list of CSS properties that apply specially to block-level elements.
1023 static const CSSPropertyID blockProperties[] = {
1024     CSSPropertyOrphans,
1025     CSSPropertyOverflow, // This can be also be applied to replaced elements
1026     CSSPropertyWebkitAspectRatio,
1027     CSSPropertyWebkitColumnCount,
1028     CSSPropertyWebkitColumnGap,
1029     CSSPropertyWebkitColumnRuleColor,
1030     CSSPropertyWebkitColumnRuleStyle,
1031     CSSPropertyWebkitColumnRuleWidth,
1032     CSSPropertyWebkitColumnBreakBefore,
1033     CSSPropertyWebkitColumnBreakAfter,
1034     CSSPropertyWebkitColumnBreakInside,
1035     CSSPropertyWebkitColumnWidth,
1036     CSSPropertyPageBreakAfter,
1037     CSSPropertyPageBreakBefore,
1038     CSSPropertyPageBreakInside,
1039 #if ENABLE(CSS_REGIONS)
1040     CSSPropertyWebkitRegionBreakAfter,
1041     CSSPropertyWebkitRegionBreakBefore,
1042     CSSPropertyWebkitRegionBreakInside,
1043 #endif
1044     CSSPropertyTextAlign,
1045 #if ENABLE(CSS3_TEXT)
1046     CSSPropertyWebkitTextAlignLast,
1047 #endif // CSS3_TEXT
1048     CSSPropertyTextIndent,
1049     CSSPropertyWidows
1050 };
1051
1052 void StylePropertySet::clear()
1053 {
1054     ASSERT(isMutable());
1055     mutablePropertyVector().clear();
1056 }
1057
1058 const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
1059
1060 PassRefPtr<StylePropertySet> StylePropertySet::copyBlockProperties() const
1061 {
1062     return copyPropertiesInSet(blockProperties, numBlockProperties);
1063 }
1064
1065 void StylePropertySet::removeBlockProperties()
1066 {
1067     removePropertiesInSet(blockProperties, numBlockProperties);
1068 }
1069
1070 bool StylePropertySet::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
1071 {
1072     ASSERT(isMutable());
1073     if (mutablePropertyVector().isEmpty())
1074         return false;
1075
1076     // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
1077     HashSet<CSSPropertyID> toRemove;
1078     for (unsigned i = 0; i < length; ++i)
1079         toRemove.add(set[i]);
1080
1081     Vector<CSSProperty> newProperties;
1082     newProperties.reserveInitialCapacity(mutablePropertyVector().size());
1083
1084     unsigned size = mutablePropertyVector().size();
1085     for (unsigned n = 0; n < size; ++n) {
1086         const CSSProperty& property = mutablePropertyVector().at(n);
1087         // Not quite sure if the isImportant test is needed but it matches the existing behavior.
1088         if (!property.isImportant()) {
1089             if (toRemove.contains(property.id()))
1090                 continue;
1091         }
1092         newProperties.append(property);
1093     }
1094
1095     bool changed = newProperties.size() != mutablePropertyVector().size();
1096     mutablePropertyVector() = newProperties;
1097     return changed;
1098 }
1099
1100 int StylePropertySet::findPropertyIndex(CSSPropertyID propertyID) const
1101 {
1102     for (int n = propertyCount() - 1 ; n >= 0; --n) {
1103         if (propertyID == propertyAt(n).id())
1104             return n;
1105     }
1106     return -1;
1107 }
1108
1109 CSSProperty* StylePropertySet::findMutableCSSPropertyWithID(CSSPropertyID propertyID)
1110 {
1111     ASSERT(isMutable());
1112     int foundPropertyIndex = findPropertyIndex(propertyID);
1113     if (foundPropertyIndex == -1)
1114         return 0;
1115     return &mutablePropertyVector().at(foundPropertyIndex);
1116 }
1117     
1118 bool StylePropertySet::propertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const
1119 {
1120     int foundPropertyIndex = findPropertyIndex(propertyID);
1121     if (foundPropertyIndex == -1)
1122         return false;
1123     return propertyAt(foundPropertyIndex).value()->equals(*propertyValue);
1124 }
1125     
1126 void StylePropertySet::removeEquivalentProperties(const StylePropertySet* style)
1127 {
1128     ASSERT(isMutable());
1129     Vector<CSSPropertyID> propertiesToRemove;
1130     unsigned size = mutablePropertyVector().size();
1131     for (unsigned i = 0; i < size; ++i) {
1132         PropertyReference property = propertyAt(i);
1133         if (style->propertyMatches(property.id(), property.value()))
1134             propertiesToRemove.append(property.id());
1135     }    
1136     // FIXME: This should use mass removal.
1137     for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
1138         removeProperty(propertiesToRemove[i]);
1139 }
1140
1141 void StylePropertySet::removeEquivalentProperties(const CSSStyleDeclaration* style)
1142 {
1143     ASSERT(isMutable());
1144     Vector<CSSPropertyID> propertiesToRemove;
1145     unsigned size = mutablePropertyVector().size();
1146     for (unsigned i = 0; i < size; ++i) {
1147         PropertyReference property = propertyAt(i);
1148         if (style->cssPropertyMatches(property.id(), property.value()))
1149             propertiesToRemove.append(property.id());
1150     }    
1151     // FIXME: This should use mass removal.
1152     for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
1153         removeProperty(propertiesToRemove[i]);
1154 }
1155
1156 PassRefPtr<StylePropertySet> StylePropertySet::copy() const
1157 {
1158     return adoptRef(new MutableStylePropertySet(*this));
1159 }
1160
1161 PassRefPtr<StylePropertySet> StylePropertySet::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const
1162 {
1163     Vector<CSSProperty, 256> list;
1164     list.reserveInitialCapacity(length);
1165     for (unsigned i = 0; i < length; ++i) {
1166         RefPtr<CSSValue> value = getPropertyCSSValue(set[i]);
1167         if (value)
1168             list.append(CSSProperty(set[i], value.release(), false));
1169     }
1170     return StylePropertySet::create(list.data(), list.size());
1171 }
1172
1173 PropertySetCSSStyleDeclaration* StylePropertySet::cssStyleDeclaration()
1174 {
1175     if (!m_ownsCSSOMWrapper)
1176         return 0;
1177     ASSERT(isMutable());
1178     return propertySetCSSOMWrapperMap().get(this);
1179 }
1180
1181 CSSStyleDeclaration* StylePropertySet::ensureCSSStyleDeclaration()
1182 {
1183     ASSERT(isMutable());
1184
1185     if (m_ownsCSSOMWrapper) {
1186         ASSERT(!static_cast<CSSStyleDeclaration*>(propertySetCSSOMWrapperMap().get(this))->parentRule());
1187         ASSERT(!propertySetCSSOMWrapperMap().get(this)->parentElement());
1188         return propertySetCSSOMWrapperMap().get(this);
1189     }
1190     m_ownsCSSOMWrapper = true;
1191     PropertySetCSSStyleDeclaration* cssomWrapper = new PropertySetCSSStyleDeclaration(const_cast<StylePropertySet*>(this));
1192     propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
1193     return cssomWrapper;
1194 }
1195
1196 CSSStyleDeclaration* StylePropertySet::ensureInlineCSSStyleDeclaration(const StyledElement* parentElement)
1197 {
1198     ASSERT(isMutable());
1199
1200     if (m_ownsCSSOMWrapper) {
1201         ASSERT(propertySetCSSOMWrapperMap().get(this)->parentElement() == parentElement);
1202         return propertySetCSSOMWrapperMap().get(this);
1203     }
1204     m_ownsCSSOMWrapper = true;
1205     PropertySetCSSStyleDeclaration* cssomWrapper = new InlineCSSStyleDeclaration(const_cast<StylePropertySet*>(this), const_cast<StyledElement*>(parentElement));
1206     propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
1207     return cssomWrapper;
1208 }
1209
1210 unsigned StylePropertySet::averageSizeInBytes()
1211 {
1212     // Please update this if the storage scheme changes so that this longer reflects the actual size.
1213     return sizeForImmutableStylePropertySetWithPropertyCount(4);
1214 }
1215
1216 void StylePropertySet::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
1217 {
1218     size_t actualSize = m_isMutable ? sizeof(StylePropertySet) : sizeForImmutableStylePropertySetWithPropertyCount(m_arraySize);
1219     MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::CSS, actualSize);
1220     if (m_isMutable)
1221         info.addMember(mutablePropertyVector(), "mutablePropertyVector()");
1222     else {
1223         for (unsigned i = 0; i < propertyCount(); ++i)
1224             info.addMember(propertyAt(i).value(), "value");
1225     }
1226 }
1227
1228 // See the function above if you need to update this.
1229 struct SameSizeAsStylePropertySet : public RefCounted<SameSizeAsStylePropertySet> {
1230     unsigned bitfield;
1231 };
1232 COMPILE_ASSERT(sizeof(StylePropertySet) == sizeof(SameSizeAsStylePropertySet), style_property_set_should_stay_small);
1233
1234 #ifndef NDEBUG
1235 void StylePropertySet::showStyle()
1236 {
1237     fprintf(stderr, "%s\n", asText().ascii().data());
1238 }
1239 #endif
1240
1241 PassRefPtr<StylePropertySet> StylePropertySet::create(CSSParserMode cssParserMode)
1242 {
1243     return adoptRef(new MutableStylePropertySet(cssParserMode));
1244 }
1245
1246 PassRefPtr<StylePropertySet> StylePropertySet::create(const CSSProperty* properties, unsigned count)
1247 {
1248     return adoptRef(new MutableStylePropertySet(properties, count));
1249 }
1250
1251 String StylePropertySet::PropertyReference::cssName() const
1252 {
1253 #if ENABLE(CSS_VARIABLES)
1254     if (id() == CSSPropertyVariable) {
1255         ASSERT(propertyValue()->isVariableValue());
1256         if (!propertyValue()->isVariableValue())
1257             return emptyString(); // Should not happen, but if it does, avoid a bad cast.
1258         return "-webkit-var-" + static_cast<const CSSVariableValue*>(propertyValue())->name();
1259     }
1260 #endif
1261     return getPropertyNameString(id());
1262 }
1263
1264 String StylePropertySet::PropertyReference::cssText() const
1265 {
1266     StringBuilder result;
1267     result.append(cssName());
1268     result.appendLiteral(": ");
1269     result.append(propertyValue()->cssText());
1270     if (isImportant())
1271         result.appendLiteral(" !important");
1272     result.append(';');
1273     return result.toString();
1274 }
1275
1276
1277 } // namespace WebCore