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