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