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