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