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