dd45dc2054c78a65a49bc507b8c38e64532790f9
[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     RefPtr<CSSCustomPropertyValue> customValue = CSSCustomPropertyValue::create(propertyName, value);
741     addParsedProperty(CSSProperty(CSSPropertyCustom, customValue, important));
742     
743     return true;
744 }
745
746 void MutableStyleProperties::setProperty(CSSPropertyID propertyID, PassRefPtr<CSSValue> prpValue, bool important)
747 {
748     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
749     if (!shorthand.length()) {
750         setProperty(CSSProperty(propertyID, prpValue, important));
751         return;
752     }
753
754     removePropertiesInSet(shorthand.properties(), shorthand.length());
755
756     RefPtr<CSSValue> value = prpValue;
757     for (unsigned i = 0; i < shorthand.length(); ++i)
758         m_propertyVector.append(CSSProperty(shorthand.properties()[i], value, important));
759 }
760
761 bool MutableStyleProperties::setProperty(const CSSProperty& property, CSSProperty* slot)
762 {
763     if (!removeShorthandProperty(property.id())) {
764         CSSProperty* toReplace = slot;
765         if (!slot) {
766             if (property.id() == CSSPropertyCustom) {
767                 if (property.value())
768                     toReplace = findCustomCSSPropertyWithName(downcast<CSSCustomPropertyValue>(*property.value()).name());
769             } else
770                 toReplace = findCSSPropertyWithID(property.id());
771         }
772         
773         if (toReplace) {
774             if (*toReplace == property)
775                 return false;
776
777             *toReplace = property;
778             setPrefixingVariantProperty(property);
779             return true;
780         }
781     }
782
783     return appendPrefixingVariantProperty(property);
784 }
785
786 static unsigned getIndexInShorthandVectorForPrefixingVariant(const CSSProperty& property, CSSPropertyID prefixingVariant)
787 {
788     if (!property.isSetFromShorthand())
789         return 0;
790
791     CSSPropertyID prefixedShorthand = prefixingVariantForPropertyId(property.shorthandID());
792     return indexOfShorthandForLonghand(prefixedShorthand, matchingShorthandsForLonghand(prefixingVariant));
793 }
794
795 bool MutableStyleProperties::appendPrefixingVariantProperty(const CSSProperty& property)
796 {
797     m_propertyVector.append(property);
798     CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(property.id());
799     if (prefixingVariant == property.id())
800         return true;
801
802     m_propertyVector.append(CSSProperty(prefixingVariant, property.value(), property.isImportant(), property.isSetFromShorthand(), getIndexInShorthandVectorForPrefixingVariant(property, prefixingVariant), property.metadata().m_implicit));
803     return true;
804 }
805
806 void MutableStyleProperties::setPrefixingVariantProperty(const CSSProperty& property)
807 {
808     CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(property.id());
809     CSSProperty* toReplace = findCSSPropertyWithID(prefixingVariant);
810     if (toReplace && prefixingVariant != property.id())
811         *toReplace = CSSProperty(prefixingVariant, property.value(), property.isImportant(), property.isSetFromShorthand(), getIndexInShorthandVectorForPrefixingVariant(property, prefixingVariant), property.metadata().m_implicit);
812 }
813
814 bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
815 {
816     return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important));
817 }
818
819 bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
820 {
821     return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important));
822 }
823
824 void MutableStyleProperties::parseDeclaration(const String& styleDeclaration, StyleSheetContents* contextStyleSheet)
825 {
826     m_propertyVector.clear();
827
828     CSSParserContext context(cssParserMode());
829     if (contextStyleSheet) {
830         context = contextStyleSheet->parserContext();
831         context.mode = cssParserMode();
832     }
833     CSSParser parser(context);
834     parser.parseDeclaration(this, styleDeclaration, 0, contextStyleSheet);
835 }
836
837 bool MutableStyleProperties::addParsedProperties(const CSSParser::ParsedPropertyVector& properties)
838 {
839     bool anyChanged = false;
840     m_propertyVector.reserveCapacity(m_propertyVector.size() + properties.size());
841     for (const auto& property : properties) {
842         if (addParsedProperty(property))
843             anyChanged = true;
844     }
845
846     return anyChanged;
847 }
848
849 bool MutableStyleProperties::addParsedProperty(const CSSProperty& property)
850 {
851     if (property.id() == CSSPropertyCustom) {
852         if ((property.value() && !customPropertyIsImportant(downcast<CSSCustomPropertyValue>(*property.value()).name())) || property.isImportant())
853             return setProperty(property);
854         return false;
855     }
856     
857     // Only add properties that have no !important counterpart present
858     if (!propertyIsImportant(property.id()) || property.isImportant())
859         return setProperty(property);
860     
861     return false;
862 }
863
864 String StyleProperties::asText() const
865 {
866     StringBuilder result;
867
868     int positionXPropertyIndex = -1;
869     int positionYPropertyIndex = -1;
870     int repeatXPropertyIndex = -1;
871     int repeatYPropertyIndex = -1;
872
873     std::bitset<numCSSProperties> shorthandPropertyUsed;
874     std::bitset<numCSSProperties> shorthandPropertyAppeared;
875
876     unsigned size = propertyCount();
877     unsigned numDecls = 0;
878     for (unsigned n = 0; n < size; ++n) {
879         PropertyReference property = propertyAt(n);
880         CSSPropertyID propertyID = property.id();
881         CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
882         CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
883         String value;
884
885         switch (propertyID) {
886         case CSSPropertyAnimationName:
887         case CSSPropertyAnimationDuration:
888         case CSSPropertyAnimationTimingFunction:
889         case CSSPropertyAnimationDelay:
890         case CSSPropertyAnimationIterationCount:
891         case CSSPropertyAnimationDirection:
892         case CSSPropertyAnimationFillMode:
893             shorthandPropertyID = CSSPropertyAnimation;
894             break;
895         case CSSPropertyBackgroundPositionX:
896             positionXPropertyIndex = n;
897             continue;
898         case CSSPropertyBackgroundPositionY:
899             positionYPropertyIndex = n;
900             continue;
901         case CSSPropertyBackgroundRepeatX:
902             repeatXPropertyIndex = n;
903             continue;
904         case CSSPropertyBackgroundRepeatY:
905             repeatYPropertyIndex = n;
906             continue;
907         case CSSPropertyBorderTopWidth:
908         case CSSPropertyBorderRightWidth:
909         case CSSPropertyBorderBottomWidth:
910         case CSSPropertyBorderLeftWidth:
911             if (!borderFallbackShorthandProperty)
912                 borderFallbackShorthandProperty = CSSPropertyBorderWidth;
913             FALLTHROUGH;
914         case CSSPropertyBorderTopStyle:
915         case CSSPropertyBorderRightStyle:
916         case CSSPropertyBorderBottomStyle:
917         case CSSPropertyBorderLeftStyle:
918             if (!borderFallbackShorthandProperty)
919                 borderFallbackShorthandProperty = CSSPropertyBorderStyle;
920             FALLTHROUGH;
921         case CSSPropertyBorderTopColor:
922         case CSSPropertyBorderRightColor:
923         case CSSPropertyBorderBottomColor:
924         case CSSPropertyBorderLeftColor:
925             if (!borderFallbackShorthandProperty)
926                 borderFallbackShorthandProperty = CSSPropertyBorderColor;
927
928             // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
929             ASSERT(CSSPropertyBorder - firstCSSProperty < shorthandPropertyAppeared.size());
930             if (!shorthandPropertyAppeared[CSSPropertyBorder - firstCSSProperty]) {
931                 value = borderPropertyValue(ReturnNullOnUncommonValues);
932                 if (value.isNull())
933                     shorthandPropertyAppeared.set(CSSPropertyBorder - firstCSSProperty);
934                 else
935                     shorthandPropertyID = CSSPropertyBorder;
936             } else if (shorthandPropertyUsed[CSSPropertyBorder - firstCSSProperty])
937                 shorthandPropertyID = CSSPropertyBorder;
938             if (!shorthandPropertyID)
939                 shorthandPropertyID = borderFallbackShorthandProperty;
940             break;
941         case CSSPropertyWebkitBorderHorizontalSpacing:
942         case CSSPropertyWebkitBorderVerticalSpacing:
943             shorthandPropertyID = CSSPropertyBorderSpacing;
944             break;
945         case CSSPropertyFontFamily:
946         case CSSPropertyLineHeight:
947         case CSSPropertyFontSize:
948         case CSSPropertyFontStyle:
949         case CSSPropertyFontVariant:
950         case CSSPropertyFontWeight:
951             // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
952             break;
953         case CSSPropertyListStyleType:
954         case CSSPropertyListStylePosition:
955         case CSSPropertyListStyleImage:
956             shorthandPropertyID = CSSPropertyListStyle;
957             break;
958         case CSSPropertyMarginTop:
959         case CSSPropertyMarginRight:
960         case CSSPropertyMarginBottom:
961         case CSSPropertyMarginLeft:
962             shorthandPropertyID = CSSPropertyMargin;
963             break;
964         case CSSPropertyOutlineWidth:
965         case CSSPropertyOutlineStyle:
966         case CSSPropertyOutlineColor:
967             shorthandPropertyID = CSSPropertyOutline;
968             break;
969         case CSSPropertyOverflowX:
970         case CSSPropertyOverflowY:
971             shorthandPropertyID = CSSPropertyOverflow;
972             break;
973         case CSSPropertyPaddingTop:
974         case CSSPropertyPaddingRight:
975         case CSSPropertyPaddingBottom:
976         case CSSPropertyPaddingLeft:
977             shorthandPropertyID = CSSPropertyPadding;
978             break;
979         case CSSPropertyTransitionProperty:
980         case CSSPropertyTransitionDuration:
981         case CSSPropertyTransitionTimingFunction:
982         case CSSPropertyTransitionDelay:
983             shorthandPropertyID = CSSPropertyTransition;
984             break;
985         case CSSPropertyWebkitAnimationName:
986         case CSSPropertyWebkitAnimationDuration:
987         case CSSPropertyWebkitAnimationTimingFunction:
988         case CSSPropertyWebkitAnimationDelay:
989         case CSSPropertyWebkitAnimationIterationCount:
990         case CSSPropertyWebkitAnimationDirection:
991         case CSSPropertyWebkitAnimationFillMode:
992             shorthandPropertyID = CSSPropertyWebkitAnimation;
993             break;
994         case CSSPropertyFlexDirection:
995         case CSSPropertyFlexWrap:
996             shorthandPropertyID = CSSPropertyFlexFlow;
997             break;
998         case CSSPropertyFlexBasis:
999         case CSSPropertyFlexGrow:
1000         case CSSPropertyFlexShrink:
1001             shorthandPropertyID = CSSPropertyFlex;
1002             break;
1003         case CSSPropertyWebkitMaskPositionX:
1004         case CSSPropertyWebkitMaskPositionY:
1005         case CSSPropertyWebkitMaskRepeatX:
1006         case CSSPropertyWebkitMaskRepeatY:
1007         case CSSPropertyWebkitMaskImage:
1008         case CSSPropertyWebkitMaskRepeat:
1009         case CSSPropertyWebkitMaskPosition:
1010         case CSSPropertyWebkitMaskClip:
1011         case CSSPropertyWebkitMaskOrigin:
1012             shorthandPropertyID = CSSPropertyWebkitMask;
1013             break;
1014         case CSSPropertyPerspectiveOriginX:
1015         case CSSPropertyPerspectiveOriginY:
1016             shorthandPropertyID = CSSPropertyPerspectiveOrigin;
1017             break;
1018         case CSSPropertyTransformOriginX:
1019         case CSSPropertyTransformOriginY:
1020         case CSSPropertyTransformOriginZ:
1021             shorthandPropertyID = CSSPropertyTransformOrigin;
1022             break;
1023         case CSSPropertyWebkitTransitionProperty:
1024         case CSSPropertyWebkitTransitionDuration:
1025         case CSSPropertyWebkitTransitionTimingFunction:
1026         case CSSPropertyWebkitTransitionDelay:
1027             shorthandPropertyID = CSSPropertyWebkitTransition;
1028             break;
1029         default:
1030             break;
1031         }
1032
1033         unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
1034         if (shorthandPropertyID) {
1035             ASSERT(shortPropertyIndex < shorthandPropertyUsed.size());
1036             if (shorthandPropertyUsed[shortPropertyIndex])
1037                 continue;
1038             if (!shorthandPropertyAppeared[shortPropertyIndex] && value.isNull())
1039                 value = getPropertyValue(shorthandPropertyID);
1040             shorthandPropertyAppeared.set(shortPropertyIndex);
1041         }
1042
1043         if (!value.isNull()) {
1044             propertyID = shorthandPropertyID;
1045             shorthandPropertyUsed.set(shortPropertyIndex);
1046         } else
1047             value = property.value()->cssText();
1048
1049         if (propertyID != CSSPropertyCustom && value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
1050             continue;
1051
1052         if (numDecls++)
1053             result.append(' ');
1054
1055         if (propertyID == CSSPropertyCustom)
1056             result.append(downcast<CSSCustomPropertyValue>(*property.value()).name());
1057         else
1058             result.append(getPropertyName(propertyID));
1059
1060         result.appendLiteral(": ");
1061         result.append(value);
1062         if (property.isImportant())
1063             result.appendLiteral(" !important");
1064         result.append(';');
1065     }
1066
1067     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
1068     // It is required because background-position-x/y are non-standard properties and WebKit generated output
1069     // would not work in Firefox (<rdar://problem/5143183>)
1070     // It would be a better solution if background-position was CSS_PAIR.
1071     if (positionXPropertyIndex != -1 && positionYPropertyIndex != -1 && propertyAt(positionXPropertyIndex).isImportant() == propertyAt(positionYPropertyIndex).isImportant()) {
1072         PropertyReference positionXProperty = propertyAt(positionXPropertyIndex);
1073         PropertyReference positionYProperty = propertyAt(positionYPropertyIndex);
1074
1075         if (numDecls++)
1076             result.append(' ');
1077         result.appendLiteral("background-position: ");
1078         if (positionXProperty.value()->isValueList() || positionYProperty.value()->isValueList())
1079             result.append(getLayeredShorthandValue(backgroundPositionShorthand()));
1080         else {
1081             result.append(positionXProperty.value()->cssText());
1082             result.append(' ');
1083             result.append(positionYProperty.value()->cssText());
1084         }
1085         if (positionXProperty.isImportant())
1086             result.appendLiteral(" !important");
1087         result.append(';');
1088     } else {
1089         if (positionXPropertyIndex != -1) {
1090             if (numDecls++)
1091                 result.append(' ');
1092             result.append(propertyAt(positionXPropertyIndex).cssText());
1093         }
1094         if (positionYPropertyIndex != -1) {
1095             if (numDecls++)
1096                 result.append(' ');
1097             result.append(propertyAt(positionYPropertyIndex).cssText());
1098         }
1099     }
1100
1101     // FIXME: We need to do the same for background-repeat.
1102     if (repeatXPropertyIndex != -1 && repeatYPropertyIndex != -1 && propertyAt(repeatXPropertyIndex).isImportant() == propertyAt(repeatYPropertyIndex).isImportant()) {
1103         PropertyReference repeatXProperty = propertyAt(repeatXPropertyIndex);
1104         PropertyReference repeatYProperty = propertyAt(repeatYPropertyIndex);
1105
1106         if (numDecls++)
1107             result.append(' ');
1108         result.appendLiteral("background-repeat: ");
1109         if (repeatXProperty.value()->isValueList() || repeatYProperty.value()->isValueList())
1110             result.append(getLayeredShorthandValue(backgroundRepeatShorthand()));
1111         else {
1112             result.append(repeatXProperty.value()->cssText());
1113             result.append(' ');
1114             result.append(repeatYProperty.value()->cssText());
1115         }
1116         if (repeatXProperty.isImportant())
1117             result.appendLiteral(" !important");
1118         result.append(';');
1119     } else {
1120         if (repeatXPropertyIndex != -1) {
1121             if (numDecls++)
1122                 result.append(' ');
1123             result.append(propertyAt(repeatXPropertyIndex).cssText());
1124         }
1125         if (repeatYPropertyIndex != -1) {
1126             if (numDecls++)
1127                 result.append(' ');
1128             result.append(propertyAt(repeatYPropertyIndex).cssText());
1129         }
1130     }
1131
1132     ASSERT(!numDecls ^ !result.isEmpty());
1133     return result.toString();
1134 }
1135
1136 bool StyleProperties::hasCSSOMWrapper() const
1137 {
1138     return is<MutableStyleProperties>(*this) && downcast<MutableStyleProperties>(*this).m_cssomWrapper;
1139 }
1140
1141 void MutableStyleProperties::mergeAndOverrideOnConflict(const StyleProperties& other)
1142 {
1143     unsigned size = other.propertyCount();
1144     for (unsigned i = 0; i < size; ++i)
1145         addParsedProperty(other.propertyAt(i).toCSSProperty());
1146 }
1147
1148 void StyleProperties::addSubresourceStyleURLs(ListHashSet<URL>& urls, StyleSheetContents* contextStyleSheet) const
1149 {
1150     unsigned size = propertyCount();
1151     for (unsigned i = 0; i < size; ++i)
1152         propertyAt(i).value()->addSubresourceStyleURLs(urls, contextStyleSheet);
1153 }
1154
1155 bool StyleProperties::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
1156 {
1157     unsigned size = propertyCount();
1158     for (unsigned i = 0; i < size; ++i) {
1159         if (propertyAt(i).value()->traverseSubresources(handler))
1160             return true;
1161     }
1162     return false;
1163 }
1164
1165 // This is the list of properties we want to copy in the copyBlockProperties() function.
1166 // It is the list of CSS properties that apply specially to block-level elements.
1167 static const CSSPropertyID blockProperties[] = {
1168     CSSPropertyOrphans,
1169     CSSPropertyOverflow, // This can be also be applied to replaced elements
1170     CSSPropertyWebkitAspectRatio,
1171     CSSPropertyColumnCount,
1172     CSSPropertyColumnGap,
1173     CSSPropertyColumnRuleColor,
1174     CSSPropertyColumnRuleStyle,
1175     CSSPropertyColumnRuleWidth,
1176     CSSPropertyWebkitColumnBreakBefore,
1177     CSSPropertyWebkitColumnBreakAfter,
1178     CSSPropertyWebkitColumnBreakInside,
1179     CSSPropertyColumnWidth,
1180     CSSPropertyPageBreakAfter,
1181     CSSPropertyPageBreakBefore,
1182     CSSPropertyPageBreakInside,
1183 #if ENABLE(CSS_REGIONS)
1184     CSSPropertyWebkitRegionBreakAfter,
1185     CSSPropertyWebkitRegionBreakBefore,
1186     CSSPropertyWebkitRegionBreakInside,
1187 #endif
1188     CSSPropertyTextAlign,
1189 #if ENABLE(CSS3_TEXT)
1190     CSSPropertyWebkitTextAlignLast,
1191     CSSPropertyWebkitTextJustify,
1192 #endif // CSS3_TEXT
1193     CSSPropertyTextIndent,
1194     CSSPropertyWidows
1195 };
1196
1197 void MutableStyleProperties::clear()
1198 {
1199     m_propertyVector.clear();
1200 }
1201
1202 const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
1203
1204 Ref<MutableStyleProperties> StyleProperties::copyBlockProperties() const
1205 {
1206     return copyPropertiesInSet(blockProperties, numBlockProperties);
1207 }
1208
1209 void MutableStyleProperties::removeBlockProperties()
1210 {
1211     removePropertiesInSet(blockProperties, numBlockProperties);
1212 }
1213
1214 bool MutableStyleProperties::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
1215 {
1216     if (m_propertyVector.isEmpty())
1217         return false;
1218
1219     // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
1220     HashSet<CSSPropertyID> toRemove;
1221     for (unsigned i = 0; i < length; ++i)
1222         toRemove.add(set[i]);
1223
1224     return m_propertyVector.removeAllMatching([&toRemove] (const CSSProperty& property) {
1225         // Not quite sure if the isImportant test is needed but it matches the existing behavior.
1226         return !property.isImportant() && toRemove.contains(property.id());
1227     }) > 0;
1228 }
1229
1230 int ImmutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const
1231 {
1232     // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1233     // the compiler converting it to an int multiple times in the loop.
1234     uint16_t id = static_cast<uint16_t>(propertyID);
1235     for (int n = m_arraySize - 1 ; n >= 0; --n) {
1236         if (metadataArray()[n].m_propertyID == id)
1237             return n;
1238     }
1239
1240     return -1;
1241 }
1242
1243 int MutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const
1244 {
1245     // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1246     // the compiler converting it to an int multiple times in the loop.
1247     uint16_t id = static_cast<uint16_t>(propertyID);
1248     for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) {
1249         if (m_propertyVector.at(n).metadata().m_propertyID == id)
1250             return n;
1251     }
1252
1253     return -1;
1254 }
1255
1256 int ImmutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const
1257 {
1258     // Convert the propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1259     // the compiler converting it to an int multiple times in the loop.
1260     for (int n = m_arraySize - 1 ; n >= 0; --n) {
1261         if (metadataArray()[n].m_propertyID == CSSPropertyCustom) {
1262             // We found a custom property. See if the name matches.
1263             if (!valueArray()[n])
1264                 continue;
1265             if (downcast<CSSCustomPropertyValue>(*valueArray()[n]).name() == propertyName)
1266                 return n;
1267         }
1268     }
1269
1270     return -1;
1271 }
1272
1273 int MutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const
1274 {
1275     // Convert the propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1276     // the compiler converting it to an int multiple times in the loop.
1277     for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) {
1278         if (m_propertyVector.at(n).metadata().m_propertyID == CSSPropertyCustom) {
1279             // We found a custom property. See if the name matches.
1280             if (!m_propertyVector.at(n).value())
1281                 continue;
1282             if (downcast<CSSCustomPropertyValue>(*m_propertyVector.at(n).value()).name() == propertyName)
1283                 return n;
1284         }
1285     }
1286
1287     return -1;
1288 }
1289
1290 CSSProperty* MutableStyleProperties::findCSSPropertyWithID(CSSPropertyID propertyID)
1291 {
1292     int foundPropertyIndex = findPropertyIndex(propertyID);
1293     if (foundPropertyIndex == -1)
1294         return 0;
1295     return &m_propertyVector.at(foundPropertyIndex);
1296 }
1297
1298 CSSProperty* MutableStyleProperties::findCustomCSSPropertyWithName(const String& propertyName)
1299 {
1300     int foundPropertyIndex = findCustomPropertyIndex(propertyName);
1301     if (foundPropertyIndex == -1)
1302         return 0;
1303     return &m_propertyVector.at(foundPropertyIndex);
1304 }
1305
1306 bool StyleProperties::propertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const
1307 {
1308     int foundPropertyIndex = findPropertyIndex(propertyID);
1309     if (foundPropertyIndex == -1)
1310         return false;
1311     return propertyAt(foundPropertyIndex).value()->equals(*propertyValue);
1312 }
1313
1314 Ref<MutableStyleProperties> StyleProperties::mutableCopy() const
1315 {
1316     return adoptRef(*new MutableStyleProperties(*this));
1317 }
1318
1319 Ref<MutableStyleProperties> StyleProperties::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const
1320 {
1321     Vector<CSSProperty, 256> list;
1322     list.reserveInitialCapacity(length);
1323     for (unsigned i = 0; i < length; ++i) {
1324         RefPtr<CSSValue> value = getPropertyCSSValue(set[i]);
1325         if (value)
1326             list.append(CSSProperty(set[i], value.release(), false));
1327     }
1328     return MutableStyleProperties::create(list.data(), list.size());
1329 }
1330
1331 PropertySetCSSStyleDeclaration* MutableStyleProperties::cssStyleDeclaration()
1332 {
1333     return m_cssomWrapper.get();
1334 }
1335
1336 CSSStyleDeclaration* MutableStyleProperties::ensureCSSStyleDeclaration()
1337 {
1338     if (m_cssomWrapper) {
1339         ASSERT(!static_cast<CSSStyleDeclaration*>(m_cssomWrapper.get())->parentRule());
1340         ASSERT(!m_cssomWrapper->parentElement());
1341         return m_cssomWrapper.get();
1342     }
1343     m_cssomWrapper = std::make_unique<PropertySetCSSStyleDeclaration>(this);
1344     return m_cssomWrapper.get();
1345 }
1346
1347 CSSStyleDeclaration* MutableStyleProperties::ensureInlineCSSStyleDeclaration(StyledElement* parentElement)
1348 {
1349     if (m_cssomWrapper) {
1350         ASSERT(m_cssomWrapper->parentElement() == parentElement);
1351         return m_cssomWrapper.get();
1352     }
1353     m_cssomWrapper = std::make_unique<InlineCSSStyleDeclaration>(this, parentElement);
1354     return m_cssomWrapper.get();
1355 }
1356
1357 unsigned StyleProperties::averageSizeInBytes()
1358 {
1359     // Please update this if the storage scheme changes so that this longer reflects the actual size.
1360     return sizeForImmutableStylePropertiesWithPropertyCount(4);
1361 }
1362
1363 // See the function above if you need to update this.
1364 struct SameSizeAsStyleProperties : public RefCounted<SameSizeAsStyleProperties> {
1365     unsigned bitfield;
1366 };
1367 COMPILE_ASSERT(sizeof(StyleProperties) == sizeof(SameSizeAsStyleProperties), style_property_set_should_stay_small);
1368
1369 #ifndef NDEBUG
1370 void StyleProperties::showStyle()
1371 {
1372     fprintf(stderr, "%s\n", asText().ascii().data());
1373 }
1374 #endif
1375
1376 Ref<MutableStyleProperties> MutableStyleProperties::create(CSSParserMode cssParserMode)
1377 {
1378     return adoptRef(*new MutableStyleProperties(cssParserMode));
1379 }
1380
1381 Ref<MutableStyleProperties> MutableStyleProperties::create(const CSSProperty* properties, unsigned count)
1382 {
1383     return adoptRef(*new MutableStyleProperties(properties, count));
1384 }
1385
1386 String StyleProperties::PropertyReference::cssName() const
1387 {
1388     if (id() == CSSPropertyCustom)
1389         return downcast<CSSCustomPropertyValue>(*value()).name();
1390     return getPropertyNameString(id());
1391 }
1392
1393 String StyleProperties::PropertyReference::cssText() const
1394 {
1395     StringBuilder result;
1396     result.append(cssName());
1397     result.appendLiteral(": ");
1398     result.append(m_value->cssText());
1399     if (isImportant())
1400         result.appendLiteral(" !important");
1401     result.append(';');
1402     return result.toString();
1403 }
1404
1405
1406 } // namespace WebCore