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