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