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