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