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