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