Implement the updated port/area-based Scroll Snap Module Level 1 Spec
[WebKit-https.git] / Source / WebCore / css / StyleProperties.cpp
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved.
4  * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
5  * Copyright (C) 2013 Intel Corporation. All rights reserved.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #include "config.h"
24 #include "StyleProperties.h"
25
26 #include "CSSComputedStyleDeclaration.h"
27 #include "CSSCustomPropertyValue.h"
28 #include "CSSDeferredParser.h"
29 #include "CSSParser.h"
30 #include "CSSPendingSubstitutionValue.h"
31 #include "CSSValueKeywords.h"
32 #include "CSSValueList.h"
33 #include "CSSValuePool.h"
34 #include "Document.h"
35 #include "PropertySetCSSStyleDeclaration.h"
36 #include "StylePropertyShorthand.h"
37 #include "StylePropertyShorthandFunctions.h"
38 #include "StyleSheetContents.h"
39 #include <bitset>
40 #include <wtf/text/StringBuilder.h>
41
42 #ifndef NDEBUG
43 #include <stdio.h>
44 #include <wtf/ASCIICType.h>
45 #include <wtf/text/CString.h>
46 #endif
47
48 namespace WebCore {
49
50 static size_t sizeForImmutableStylePropertiesWithPropertyCount(unsigned count)
51 {
52     return sizeof(ImmutableStyleProperties) - sizeof(void*) + sizeof(CSSValue*) * count + sizeof(StylePropertyMetadata) * count;
53 }
54
55 static bool isInitialOrInherit(const String& value)
56 {
57     return value.length() == 7 && (value == "initial" || value == "inherit");
58 }
59
60 Ref<ImmutableStyleProperties> ImmutableStyleProperties::create(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode)
61 {
62     void* slot = WTF::fastMalloc(sizeForImmutableStylePropertiesWithPropertyCount(count));
63     return adoptRef(*new (NotNull, slot) ImmutableStyleProperties(properties, count, cssParserMode));
64 }
65
66 Ref<ImmutableStyleProperties> StyleProperties::immutableCopyIfNeeded() const
67 {
68     if (is<ImmutableStyleProperties>(*this))
69         return downcast<ImmutableStyleProperties>(const_cast<StyleProperties&>(*this));
70     const MutableStyleProperties& mutableThis = downcast<MutableStyleProperties>(*this);
71     return ImmutableStyleProperties::create(mutableThis.m_propertyVector.data(), mutableThis.m_propertyVector.size(), cssParserMode());
72 }
73
74 MutableStyleProperties::MutableStyleProperties(CSSParserMode cssParserMode)
75     : StyleProperties(cssParserMode, MutablePropertiesType)
76 {
77 }
78
79 MutableStyleProperties::MutableStyleProperties(const CSSProperty* properties, unsigned length)
80     : StyleProperties(HTMLStandardMode, MutablePropertiesType)
81 {
82     m_propertyVector.reserveInitialCapacity(length);
83     for (unsigned i = 0; i < length; ++i)
84         m_propertyVector.uncheckedAppend(properties[i]);
85 }
86
87 MutableStyleProperties::~MutableStyleProperties()
88 {
89 }
90
91 ImmutableStyleProperties::ImmutableStyleProperties(const CSSProperty* properties, unsigned length, CSSParserMode cssParserMode)
92     : StyleProperties(cssParserMode, length)
93 {
94     StylePropertyMetadata* metadataArray = const_cast<StylePropertyMetadata*>(this->metadataArray());
95     CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray());
96     for (unsigned i = 0; i < length; ++i) {
97         metadataArray[i] = properties[i].metadata();
98         valueArray[i] = properties[i].value();
99         valueArray[i]->ref();
100     }
101 }
102
103 ImmutableStyleProperties::~ImmutableStyleProperties()
104 {
105     CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray());
106     for (unsigned i = 0; i < m_arraySize; ++i)
107         valueArray[i]->deref();
108 }
109
110 MutableStyleProperties::MutableStyleProperties(const StyleProperties& other)
111     : StyleProperties(other.cssParserMode(), MutablePropertiesType)
112 {
113     ASSERT(other.type() != DeferredPropertiesType);
114     if (is<MutableStyleProperties>(other))
115         m_propertyVector = downcast<MutableStyleProperties>(other).m_propertyVector;
116     else {
117         const auto& immutableOther = downcast<ImmutableStyleProperties>(other);
118         unsigned propertyCount = immutableOther.propertyCount();
119         m_propertyVector.reserveInitialCapacity(propertyCount);
120         for (unsigned i = 0; i < propertyCount; ++i)
121             m_propertyVector.uncheckedAppend(immutableOther.propertyAt(i).toCSSProperty());
122     }
123 }
124
125 String StyleProperties::getPropertyValue(CSSPropertyID propertyID) const
126 {
127     RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
128     if (value)
129         return value->cssText();
130
131     const StylePropertyShorthand& shorthand = shorthandForProperty(propertyID);
132     if (shorthand.length()) {
133         RefPtr<CSSValue> value = getPropertyCSSValueInternal(shorthand.properties()[0]);
134         if (!value)
135             return String();
136         if (value->isPendingSubstitutionValue())
137             return downcast<CSSPendingSubstitutionValue>(*value).shorthandValue()->cssText();
138     }
139
140     // Shorthand and 4-values properties
141     switch (propertyID) {
142     case CSSPropertyAll:
143         return getCommonValue(allShorthand());
144     case CSSPropertyAnimation:
145         return getLayeredShorthandValue(animationShorthand());
146     case CSSPropertyBorderSpacing:
147         return borderSpacingValue(borderSpacingShorthand());
148     case CSSPropertyBackgroundPosition:
149         return getLayeredShorthandValue(backgroundPositionShorthand());
150     case CSSPropertyBackgroundRepeat:
151         return getLayeredShorthandValue(backgroundRepeatShorthand());
152     case CSSPropertyBackground:
153         return getLayeredShorthandValue(backgroundShorthand());
154     case CSSPropertyBorder:
155         return borderPropertyValue(OmitUncommonValues);
156     case CSSPropertyBorderTop:
157         return getShorthandValue(borderTopShorthand());
158     case CSSPropertyBorderRight:
159         return getShorthandValue(borderRightShorthand());
160     case CSSPropertyBorderBottom:
161         return getShorthandValue(borderBottomShorthand());
162     case CSSPropertyBorderLeft:
163         return getShorthandValue(borderLeftShorthand());
164     case CSSPropertyOutline:
165         return getShorthandValue(outlineShorthand());
166     case CSSPropertyBorderColor:
167         return get4Values(borderColorShorthand());
168     case CSSPropertyBorderWidth:
169         return get4Values(borderWidthShorthand());
170     case CSSPropertyBorderStyle:
171         return get4Values(borderStyleShorthand());
172     case CSSPropertyColumnRule:
173         return getShorthandValue(columnRuleShorthand());
174     case CSSPropertyColumns:
175         return getShorthandValue(columnsShorthand());
176     case CSSPropertyFlex:
177         return getShorthandValue(flexShorthand());
178     case CSSPropertyFlexFlow:
179         return getShorthandValue(flexFlowShorthand());
180 #if ENABLE(CSS_GRID_LAYOUT)
181     case CSSPropertyGridArea:
182         return getShorthandValue(gridAreaShorthand());
183     case CSSPropertyGridTemplate:
184         return getShorthandValue(gridTemplateShorthand());
185     case CSSPropertyGrid:
186         return getShorthandValue(gridShorthand());
187     case CSSPropertyGridColumn:
188         return getShorthandValue(gridColumnShorthand());
189     case CSSPropertyGridRow:
190         return getShorthandValue(gridRowShorthand());
191 #endif
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         prefix = ' ';
286         break;
287     case CSSPropertyLineHeight:
288         prefix = '/';
289         break;
290     default:
291         ASSERT_NOT_REACHED();
292     }
293
294     if (prefix && !result.isEmpty())
295         result.append(prefix);
296     String value = propertyAt(foundPropertyIndex).value()->cssText();
297     result.append(value);
298     if (!commonValue.isNull() && commonValue != value)
299         commonValue = String();
300 }
301
302 String StyleProperties::fontValue() const
303 {
304     int fontSizePropertyIndex = findPropertyIndex(CSSPropertyFontSize);
305     int fontFamilyPropertyIndex = findPropertyIndex(CSSPropertyFontFamily);
306     if (fontSizePropertyIndex == -1 || fontFamilyPropertyIndex == -1)
307         return emptyString();
308
309     PropertyReference fontSizeProperty = propertyAt(fontSizePropertyIndex);
310     PropertyReference fontFamilyProperty = propertyAt(fontFamilyPropertyIndex);
311     if (fontSizeProperty.isImplicit() || fontFamilyProperty.isImplicit())
312         return emptyString();
313
314     String commonValue = fontSizeProperty.value()->cssText();
315     StringBuilder result;
316     appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result, commonValue);
317     appendFontLonghandValueIfExplicit(CSSPropertyFontVariantCaps, result, commonValue);
318     appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, 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::borderPropertyValue(CommonValueMode valueMode) const
587 {
588     const StylePropertyShorthand properties[3] = { borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand() };
589     String commonValue;
590     StringBuilder result;
591     for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
592         String value = getCommonValue(properties[i]);
593         if (value.isNull()) {
594             if (valueMode == ReturnNullOnUncommonValues)
595                 return String();
596             ASSERT(valueMode == OmitUncommonValues);
597             continue;
598         }
599         if (!i)
600             commonValue = value;
601         else if (!commonValue.isNull() && commonValue != value)
602             commonValue = String();
603         if (value == "initial")
604             continue;
605         if (!result.isEmpty())
606             result.append(' ');
607         result.append(value);
608     }
609     if (isInitialOrInherit(commonValue))
610         return commonValue;
611     return result.isEmpty() ? String() : result.toString();
612 }
613
614 RefPtr<CSSValue> StyleProperties::getPropertyCSSValue(CSSPropertyID propertyID) const
615 {
616     return getPropertyCSSValueInternal(propertyID);
617 }
618
619 RefPtr<CSSValue> StyleProperties::getPropertyCSSValueInternal(CSSPropertyID propertyID) const
620 {
621     int foundPropertyIndex = findPropertyIndex(propertyID);
622     if (foundPropertyIndex == -1)
623         return nullptr;
624     return propertyAt(foundPropertyIndex).value();
625 }
626
627 RefPtr<CSSValue> StyleProperties::getCustomPropertyCSSValue(const String& propertyName) const
628 {
629     int foundPropertyIndex = findCustomPropertyIndex(propertyName);
630     if (foundPropertyIndex == -1)
631         return nullptr;
632     return propertyAt(foundPropertyIndex).value();
633 }
634
635 bool MutableStyleProperties::removeShorthandProperty(CSSPropertyID propertyID)
636 {
637     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
638     if (!shorthand.length())
639         return false;
640
641     return removePropertiesInSet(shorthand.properties(), shorthand.length());
642 }
643
644 bool MutableStyleProperties::removeProperty(CSSPropertyID propertyID, String* returnText)
645 {
646     if (removeShorthandProperty(propertyID)) {
647         // FIXME: Return an equivalent shorthand when possible.
648         if (returnText)
649             *returnText = emptyString();
650         return true;
651     }
652
653     int foundPropertyIndex = findPropertyIndex(propertyID);
654     if (foundPropertyIndex == -1) {
655         if (returnText)
656             *returnText = emptyString();
657         return false;
658     }
659
660     if (returnText)
661         *returnText = propertyAt(foundPropertyIndex).value()->cssText();
662
663     // A more efficient removal strategy would involve marking entries as empty
664     // and sweeping them when the vector grows too big.
665     m_propertyVector.remove(foundPropertyIndex);
666
667     return true;
668 }
669
670 bool MutableStyleProperties::removeCustomProperty(const String& propertyName, String* returnText)
671 {
672     int foundPropertyIndex = findCustomPropertyIndex(propertyName);
673     if (foundPropertyIndex == -1) {
674         if (returnText)
675             *returnText = emptyString();
676         return false;
677     }
678
679     if (returnText)
680         *returnText = propertyAt(foundPropertyIndex).value()->cssText();
681
682     // A more efficient removal strategy would involve marking entries as empty
683     // and sweeping them when the vector grows too big.
684     m_propertyVector.remove(foundPropertyIndex);
685
686     return true;
687 }
688
689 bool StyleProperties::propertyIsImportant(CSSPropertyID propertyID) const
690 {
691     int foundPropertyIndex = findPropertyIndex(propertyID);
692     if (foundPropertyIndex != -1)
693         return propertyAt(foundPropertyIndex).isImportant();
694
695     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
696     if (!shorthand.length())
697         return false;
698
699     for (unsigned i = 0; i < shorthand.length(); ++i) {
700         if (!propertyIsImportant(shorthand.properties()[i]))
701             return false;
702     }
703     return true;
704 }
705
706 bool StyleProperties::customPropertyIsImportant(const String& propertyName) const
707 {
708     int foundPropertyIndex = findCustomPropertyIndex(propertyName);
709     if (foundPropertyIndex != -1)
710         return propertyAt(foundPropertyIndex).isImportant();
711     return false;
712 }
713
714 String StyleProperties::getPropertyShorthand(CSSPropertyID propertyID) const
715 {
716     int foundPropertyIndex = findPropertyIndex(propertyID);
717     if (foundPropertyIndex == -1)
718         return String();
719     return getPropertyNameString(propertyAt(foundPropertyIndex).shorthandID());
720 }
721
722 bool StyleProperties::isPropertyImplicit(CSSPropertyID propertyID) const
723 {
724     int foundPropertyIndex = findPropertyIndex(propertyID);
725     if (foundPropertyIndex == -1)
726         return false;
727     return propertyAt(foundPropertyIndex).isImplicit();
728 }
729
730 bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String& value, bool important, CSSParserContext parserContext)
731 {
732     // Setting the value to an empty string just removes the property in both IE and Gecko.
733     // Setting it to null seems to produce less consistent results, but we treat it just the same.
734     if (value.isEmpty())
735         return removeProperty(propertyID);
736
737     parserContext.mode = cssParserMode();
738
739     // When replacing an existing property value, this moves the property to the end of the list.
740     // Firefox preserves the position, and MSIE moves the property to the beginning.
741     return CSSParser::parseValue(*this, propertyID, value, important, parserContext) == CSSParser::ParseResult::Changed;
742 }
743
744 bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String& value, bool important)
745 {
746     CSSParserContext parserContext(cssParserMode());
747     return setProperty(propertyID, value, important, parserContext);
748 }
749
750 bool MutableStyleProperties::setCustomProperty(const String& propertyName, const String& value, bool important, CSSParserContext parserContext)
751 {
752     // Setting the value to an empty string just removes the property in both IE and Gecko.
753     // Setting it to null seems to produce less consistent results, but we treat it just the same.
754     if (value.isEmpty())
755         return removeCustomProperty(propertyName);
756
757     parserContext.mode = cssParserMode();
758     // When replacing an existing property value, this moves the property to the end of the list.
759     // Firefox preserves the position, and MSIE moves the property to the beginning.
760     return CSSParser::parseCustomPropertyValue(*this, propertyName, value, important, parserContext) == CSSParser::ParseResult::Changed;
761 }
762
763 void MutableStyleProperties::setProperty(CSSPropertyID propertyID, RefPtr<CSSValue>&& value, bool important)
764 {
765     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
766     if (!shorthand.length()) {
767         setProperty(CSSProperty(propertyID, WTFMove(value), important));
768         return;
769     }
770
771     removePropertiesInSet(shorthand.properties(), shorthand.length());
772
773     for (unsigned i = 0; i < shorthand.length(); ++i)
774         m_propertyVector.append(CSSProperty(shorthand.properties()[i], value.copyRef(), important));
775 }
776
777 bool MutableStyleProperties::setProperty(const CSSProperty& property, CSSProperty* slot)
778 {
779     if (!removeShorthandProperty(property.id())) {
780         CSSProperty* toReplace = slot;
781         if (!slot) {
782             if (property.id() == CSSPropertyCustom) {
783                 if (property.value())
784                     toReplace = findCustomCSSPropertyWithName(downcast<CSSCustomPropertyValue>(*property.value()).name());
785             } else
786                 toReplace = findCSSPropertyWithID(property.id());
787         }
788         
789         if (toReplace) {
790             if (*toReplace == property)
791                 return false;
792
793             *toReplace = property;
794             return true;
795         }
796     }
797
798     m_propertyVector.append(property);
799     return true;
800 }
801
802 bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
803 {
804     return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important));
805 }
806
807 bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
808 {
809     return setProperty(CSSProperty(propertyID, CSSValuePool::singleton().createIdentifierValue(identifier), important));
810 }
811
812 bool MutableStyleProperties::parseDeclaration(const String& styleDeclaration, CSSParserContext context)
813 {
814     auto oldProperties = WTFMove(m_propertyVector);
815     m_propertyVector.clear();
816
817     context.mode = cssParserMode();
818
819     CSSParser parser(context);
820     parser.parseDeclaration(*this, styleDeclaration);
821
822     // We could do better. Just changing property order does not require style invalidation.
823     return oldProperties != m_propertyVector;
824 }
825
826 bool MutableStyleProperties::addParsedProperties(const ParsedPropertyVector& properties)
827 {
828     bool anyChanged = false;
829     m_propertyVector.reserveCapacity(m_propertyVector.size() + properties.size());
830     for (const auto& property : properties) {
831         if (addParsedProperty(property))
832             anyChanged = true;
833     }
834
835     return anyChanged;
836 }
837
838 bool MutableStyleProperties::addParsedProperty(const CSSProperty& property)
839 {
840     if (property.id() == CSSPropertyCustom) {
841         if ((property.value() && !customPropertyIsImportant(downcast<CSSCustomPropertyValue>(*property.value()).name())) || property.isImportant())
842             return setProperty(property);
843         return false;
844     }
845     return setProperty(property);
846 }
847
848 String StyleProperties::asText() const
849 {
850     StringBuilder result;
851
852     int positionXPropertyIndex = -1;
853     int positionYPropertyIndex = -1;
854     int repeatXPropertyIndex = -1;
855     int repeatYPropertyIndex = -1;
856
857     std::bitset<numCSSProperties> shorthandPropertyUsed;
858     std::bitset<numCSSProperties> shorthandPropertyAppeared;
859
860     unsigned size = propertyCount();
861     unsigned numDecls = 0;
862     for (unsigned n = 0; n < size; ++n) {
863         PropertyReference property = propertyAt(n);
864         CSSPropertyID propertyID = property.id();
865         CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
866         CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
867         String value;
868         
869         if (property.value() && property.value()->isPendingSubstitutionValue()) {
870             auto& substitutionValue = downcast<CSSPendingSubstitutionValue>(*property.value());
871             shorthandPropertyID = substitutionValue.shorthandPropertyId();
872             value = substitutionValue.shorthandValue()->cssText();
873         } else {
874             switch (propertyID) {
875             case CSSPropertyAnimationName:
876             case CSSPropertyAnimationDuration:
877             case CSSPropertyAnimationTimingFunction:
878             case CSSPropertyAnimationDelay:
879             case CSSPropertyAnimationIterationCount:
880             case CSSPropertyAnimationDirection:
881             case CSSPropertyAnimationFillMode:
882             case CSSPropertyAnimationPlayState:
883                 shorthandPropertyID = CSSPropertyAnimation;
884                 break;
885             case CSSPropertyBackgroundPositionX:
886                 positionXPropertyIndex = n;
887                 continue;
888             case CSSPropertyBackgroundPositionY:
889                 positionYPropertyIndex = n;
890                 continue;
891             case CSSPropertyBackgroundRepeatX:
892                 repeatXPropertyIndex = n;
893                 continue;
894             case CSSPropertyBackgroundRepeatY:
895                 repeatYPropertyIndex = n;
896                 continue;
897             case CSSPropertyBorderTopWidth:
898             case CSSPropertyBorderRightWidth:
899             case CSSPropertyBorderBottomWidth:
900             case CSSPropertyBorderLeftWidth:
901                 if (!borderFallbackShorthandProperty)
902                     borderFallbackShorthandProperty = CSSPropertyBorderWidth;
903                 FALLTHROUGH;
904             case CSSPropertyBorderTopStyle:
905             case CSSPropertyBorderRightStyle:
906             case CSSPropertyBorderBottomStyle:
907             case CSSPropertyBorderLeftStyle:
908                 if (!borderFallbackShorthandProperty)
909                     borderFallbackShorthandProperty = CSSPropertyBorderStyle;
910                 FALLTHROUGH;
911             case CSSPropertyBorderTopColor:
912             case CSSPropertyBorderRightColor:
913             case CSSPropertyBorderBottomColor:
914             case CSSPropertyBorderLeftColor:
915                 if (!borderFallbackShorthandProperty)
916                     borderFallbackShorthandProperty = CSSPropertyBorderColor;
917
918                 // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
919                 ASSERT(CSSPropertyBorder - firstCSSProperty < shorthandPropertyAppeared.size());
920                 if (!shorthandPropertyAppeared[CSSPropertyBorder - firstCSSProperty]) {
921                     value = borderPropertyValue(ReturnNullOnUncommonValues);
922                     if (value.isNull())
923                         shorthandPropertyAppeared.set(CSSPropertyBorder - firstCSSProperty);
924                     else
925                         shorthandPropertyID = CSSPropertyBorder;
926                 } else if (shorthandPropertyUsed[CSSPropertyBorder - firstCSSProperty])
927                     shorthandPropertyID = CSSPropertyBorder;
928                 if (!shorthandPropertyID)
929                     shorthandPropertyID = borderFallbackShorthandProperty;
930                 break;
931             case CSSPropertyWebkitBorderHorizontalSpacing:
932             case CSSPropertyWebkitBorderVerticalSpacing:
933                 shorthandPropertyID = CSSPropertyBorderSpacing;
934                 break;
935             case CSSPropertyFontFamily:
936             case CSSPropertyLineHeight:
937             case CSSPropertyFontSize:
938             case CSSPropertyFontStyle:
939             case CSSPropertyFontVariantCaps:
940             case CSSPropertyFontWeight:
941                 // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
942                 break;
943             case CSSPropertyListStyleType:
944             case CSSPropertyListStylePosition:
945             case CSSPropertyListStyleImage:
946                 shorthandPropertyID = CSSPropertyListStyle;
947                 break;
948             case CSSPropertyMarginTop:
949             case CSSPropertyMarginRight:
950             case CSSPropertyMarginBottom:
951             case CSSPropertyMarginLeft:
952                 shorthandPropertyID = CSSPropertyMargin;
953                 break;
954             case CSSPropertyOutlineWidth:
955             case CSSPropertyOutlineStyle:
956             case CSSPropertyOutlineColor:
957                 shorthandPropertyID = CSSPropertyOutline;
958                 break;
959             case CSSPropertyOverflowX:
960             case CSSPropertyOverflowY:
961                 shorthandPropertyID = CSSPropertyOverflow;
962                 break;
963             case CSSPropertyPaddingTop:
964             case CSSPropertyPaddingRight:
965             case CSSPropertyPaddingBottom:
966             case CSSPropertyPaddingLeft:
967                 shorthandPropertyID = CSSPropertyPadding;
968                 break;
969 #if ENABLE(CSS_SCROLL_SNAP)
970             case CSSPropertyScrollPaddingTop:
971             case CSSPropertyScrollPaddingRight:
972             case CSSPropertyScrollPaddingBottom:
973             case CSSPropertyScrollPaddingLeft:
974                 shorthandPropertyID = CSSPropertyScrollPadding;
975                 break;
976             case CSSPropertyScrollSnapMarginTop:
977             case CSSPropertyScrollSnapMarginRight:
978             case CSSPropertyScrollSnapMarginBottom:
979             case CSSPropertyScrollSnapMarginLeft:
980                 shorthandPropertyID = CSSPropertyScrollSnapMargin;
981                 break;
982 #endif
983             case CSSPropertyTransitionProperty:
984             case CSSPropertyTransitionDuration:
985             case CSSPropertyTransitionTimingFunction:
986             case CSSPropertyTransitionDelay:
987                 shorthandPropertyID = CSSPropertyTransition;
988                 break;
989             case CSSPropertyFlexDirection:
990             case CSSPropertyFlexWrap:
991                 shorthandPropertyID = CSSPropertyFlexFlow;
992                 break;
993             case CSSPropertyFlexBasis:
994             case CSSPropertyFlexGrow:
995             case CSSPropertyFlexShrink:
996                 shorthandPropertyID = CSSPropertyFlex;
997                 break;
998             case CSSPropertyWebkitMaskPositionX:
999             case CSSPropertyWebkitMaskPositionY:
1000             case CSSPropertyWebkitMaskRepeatX:
1001             case CSSPropertyWebkitMaskRepeatY:
1002             case CSSPropertyWebkitMaskImage:
1003             case CSSPropertyWebkitMaskRepeat:
1004             case CSSPropertyWebkitMaskPosition:
1005             case CSSPropertyWebkitMaskClip:
1006             case CSSPropertyWebkitMaskOrigin:
1007                 shorthandPropertyID = CSSPropertyWebkitMask;
1008                 break;
1009             case CSSPropertyPerspectiveOriginX:
1010             case CSSPropertyPerspectiveOriginY:
1011                 shorthandPropertyID = CSSPropertyPerspectiveOrigin;
1012                 break;
1013             case CSSPropertyTransformOriginX:
1014             case CSSPropertyTransformOriginY:
1015             case CSSPropertyTransformOriginZ:
1016                 shorthandPropertyID = CSSPropertyTransformOrigin;
1017                 break;
1018             default:
1019                 break;
1020             }
1021         }
1022
1023         unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
1024         if (shorthandPropertyID) {
1025             ASSERT(shortPropertyIndex < shorthandPropertyUsed.size());
1026             if (shorthandPropertyUsed[shortPropertyIndex])
1027                 continue;
1028             if (!shorthandPropertyAppeared[shortPropertyIndex] && value.isNull())
1029                 value = getPropertyValue(shorthandPropertyID);
1030             shorthandPropertyAppeared.set(shortPropertyIndex);
1031         }
1032
1033         if (!value.isNull()) {
1034             propertyID = shorthandPropertyID;
1035             shorthandPropertyUsed.set(shortPropertyIndex);
1036         } else
1037             value = property.value()->cssText();
1038
1039         if (propertyID != CSSPropertyCustom && value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
1040             continue;
1041
1042         if (numDecls++)
1043             result.append(' ');
1044
1045         if (propertyID == CSSPropertyCustom)
1046             result.append(downcast<CSSCustomPropertyValue>(*property.value()).name());
1047         else
1048             result.append(getPropertyName(propertyID));
1049
1050         result.appendLiteral(": ");
1051         result.append(value);
1052         if (property.isImportant())
1053             result.appendLiteral(" !important");
1054         result.append(';');
1055     }
1056
1057     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
1058     // It is required because background-position-x/y are non-standard properties and WebKit generated output
1059     // would not work in Firefox (<rdar://problem/5143183>)
1060     // It would be a better solution if background-position was CSS_PAIR.
1061     if (positionXPropertyIndex != -1 && positionYPropertyIndex != -1 && propertyAt(positionXPropertyIndex).isImportant() == propertyAt(positionYPropertyIndex).isImportant()) {
1062         PropertyReference positionXProperty = propertyAt(positionXPropertyIndex);
1063         PropertyReference positionYProperty = propertyAt(positionYPropertyIndex);
1064
1065         if (numDecls++)
1066             result.append(' ');
1067         result.appendLiteral("background-position: ");
1068         if (positionXProperty.value()->isValueList() || positionYProperty.value()->isValueList())
1069             result.append(getLayeredShorthandValue(backgroundPositionShorthand()));
1070         else {
1071             result.append(positionXProperty.value()->cssText());
1072             result.append(' ');
1073             result.append(positionYProperty.value()->cssText());
1074         }
1075         if (positionXProperty.isImportant())
1076             result.appendLiteral(" !important");
1077         result.append(';');
1078     } else {
1079         if (positionXPropertyIndex != -1) {
1080             if (numDecls++)
1081                 result.append(' ');
1082             result.append(propertyAt(positionXPropertyIndex).cssText());
1083         }
1084         if (positionYPropertyIndex != -1) {
1085             if (numDecls++)
1086                 result.append(' ');
1087             result.append(propertyAt(positionYPropertyIndex).cssText());
1088         }
1089     }
1090
1091     // FIXME: We need to do the same for background-repeat.
1092     if (repeatXPropertyIndex != -1 && repeatYPropertyIndex != -1 && propertyAt(repeatXPropertyIndex).isImportant() == propertyAt(repeatYPropertyIndex).isImportant()) {
1093         PropertyReference repeatXProperty = propertyAt(repeatXPropertyIndex);
1094         PropertyReference repeatYProperty = propertyAt(repeatYPropertyIndex);
1095
1096         if (numDecls++)
1097             result.append(' ');
1098         result.appendLiteral("background-repeat: ");
1099         if (repeatXProperty.value()->isValueList() || repeatYProperty.value()->isValueList())
1100             result.append(getLayeredShorthandValue(backgroundRepeatShorthand()));
1101         else {
1102             result.append(repeatXProperty.value()->cssText());
1103             result.append(' ');
1104             result.append(repeatYProperty.value()->cssText());
1105         }
1106         if (repeatXProperty.isImportant())
1107             result.appendLiteral(" !important");
1108         result.append(';');
1109     } else {
1110         if (repeatXPropertyIndex != -1) {
1111             if (numDecls++)
1112                 result.append(' ');
1113             result.append(propertyAt(repeatXPropertyIndex).cssText());
1114         }
1115         if (repeatYPropertyIndex != -1) {
1116             if (numDecls++)
1117                 result.append(' ');
1118             result.append(propertyAt(repeatYPropertyIndex).cssText());
1119         }
1120     }
1121
1122     ASSERT(!numDecls ^ !result.isEmpty());
1123     return result.toString();
1124 }
1125
1126 bool StyleProperties::hasCSSOMWrapper() const
1127 {
1128     return is<MutableStyleProperties>(*this) && downcast<MutableStyleProperties>(*this).m_cssomWrapper;
1129 }
1130
1131 void MutableStyleProperties::mergeAndOverrideOnConflict(const StyleProperties& other)
1132 {
1133     unsigned size = other.propertyCount();
1134     for (unsigned i = 0; i < size; ++i)
1135         addParsedProperty(other.propertyAt(i).toCSSProperty());
1136 }
1137
1138 bool StyleProperties::traverseSubresources(const std::function<bool (const CachedResource&)>& handler) const
1139 {
1140     unsigned size = propertyCount();
1141     for (unsigned i = 0; i < size; ++i) {
1142         if (propertyAt(i).value()->traverseSubresources(handler))
1143             return true;
1144     }
1145     return false;
1146 }
1147
1148 // This is the list of properties we want to copy in the copyBlockProperties() function.
1149 // It is the list of CSS properties that apply specially to block-level elements.
1150 static const CSSPropertyID blockProperties[] = {
1151     CSSPropertyOrphans,
1152     CSSPropertyOverflow, // This can be also be applied to replaced elements
1153     CSSPropertyWebkitAspectRatio,
1154     CSSPropertyColumnCount,
1155     CSSPropertyColumnGap,
1156     CSSPropertyColumnRuleColor,
1157     CSSPropertyColumnRuleStyle,
1158     CSSPropertyColumnRuleWidth,
1159     CSSPropertyWebkitColumnBreakBefore,
1160     CSSPropertyWebkitColumnBreakAfter,
1161     CSSPropertyWebkitColumnBreakInside,
1162     CSSPropertyColumnWidth,
1163     CSSPropertyPageBreakAfter,
1164     CSSPropertyPageBreakBefore,
1165     CSSPropertyPageBreakInside,
1166 #if ENABLE(CSS_REGIONS)
1167     CSSPropertyWebkitRegionBreakAfter,
1168     CSSPropertyWebkitRegionBreakBefore,
1169     CSSPropertyWebkitRegionBreakInside,
1170 #endif
1171     CSSPropertyTextAlign,
1172 #if ENABLE(CSS3_TEXT)
1173     CSSPropertyWebkitTextAlignLast,
1174     CSSPropertyWebkitTextJustify,
1175 #endif // CSS3_TEXT
1176     CSSPropertyTextIndent,
1177     CSSPropertyWidows
1178 };
1179
1180 void MutableStyleProperties::clear()
1181 {
1182     m_propertyVector.clear();
1183 }
1184
1185 const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
1186
1187 Ref<MutableStyleProperties> StyleProperties::copyBlockProperties() const
1188 {
1189     return copyPropertiesInSet(blockProperties, numBlockProperties);
1190 }
1191
1192 void MutableStyleProperties::removeBlockProperties()
1193 {
1194     removePropertiesInSet(blockProperties, numBlockProperties);
1195 }
1196
1197 bool MutableStyleProperties::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
1198 {
1199     if (m_propertyVector.isEmpty())
1200         return false;
1201
1202     // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
1203     HashSet<CSSPropertyID> toRemove;
1204     for (unsigned i = 0; i < length; ++i)
1205         toRemove.add(set[i]);
1206
1207     return m_propertyVector.removeAllMatching([&toRemove] (const CSSProperty& property) {
1208         // Not quite sure if the isImportant test is needed but it matches the existing behavior.
1209         return !property.isImportant() && toRemove.contains(property.id());
1210     }) > 0;
1211 }
1212
1213 int ImmutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const
1214 {
1215     // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1216     // the compiler converting it to an int multiple times in the loop.
1217     uint16_t id = static_cast<uint16_t>(propertyID);
1218     for (int n = m_arraySize - 1 ; n >= 0; --n) {
1219         if (metadataArray()[n].m_propertyID == id)
1220             return n;
1221     }
1222
1223     return -1;
1224 }
1225
1226 int MutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const
1227 {
1228     // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1229     // the compiler converting it to an int multiple times in the loop.
1230     uint16_t id = static_cast<uint16_t>(propertyID);
1231     for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) {
1232         if (m_propertyVector.at(n).metadata().m_propertyID == id)
1233             return n;
1234     }
1235
1236     return -1;
1237 }
1238
1239 int ImmutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const
1240 {
1241     // Convert the propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1242     // the compiler converting it to an int multiple times in the loop.
1243     for (int n = m_arraySize - 1 ; n >= 0; --n) {
1244         if (metadataArray()[n].m_propertyID == CSSPropertyCustom) {
1245             // We found a custom property. See if the name matches.
1246             if (!valueArray()[n])
1247                 continue;
1248             if (downcast<CSSCustomPropertyValue>(*valueArray()[n]).name() == propertyName)
1249                 return n;
1250         }
1251     }
1252
1253     return -1;
1254 }
1255
1256 int MutableStyleProperties::findCustomPropertyIndex(const String& propertyName) const
1257 {
1258     // Convert the propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1259     // the compiler converting it to an int multiple times in the loop.
1260     for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) {
1261         if (m_propertyVector.at(n).metadata().m_propertyID == CSSPropertyCustom) {
1262             // We found a custom property. See if the name matches.
1263             if (!m_propertyVector.at(n).value())
1264                 continue;
1265             if (downcast<CSSCustomPropertyValue>(*m_propertyVector.at(n).value()).name() == propertyName)
1266                 return n;
1267         }
1268     }
1269
1270     return -1;
1271 }
1272
1273 CSSProperty* MutableStyleProperties::findCSSPropertyWithID(CSSPropertyID propertyID)
1274 {
1275     int foundPropertyIndex = findPropertyIndex(propertyID);
1276     if (foundPropertyIndex == -1)
1277         return 0;
1278     return &m_propertyVector.at(foundPropertyIndex);
1279 }
1280
1281 CSSProperty* MutableStyleProperties::findCustomCSSPropertyWithName(const String& propertyName)
1282 {
1283     int foundPropertyIndex = findCustomPropertyIndex(propertyName);
1284     if (foundPropertyIndex == -1)
1285         return 0;
1286     return &m_propertyVector.at(foundPropertyIndex);
1287 }
1288
1289 bool StyleProperties::propertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const
1290 {
1291     int foundPropertyIndex = findPropertyIndex(propertyID);
1292     if (foundPropertyIndex == -1)
1293         return false;
1294     return propertyAt(foundPropertyIndex).value()->equals(*propertyValue);
1295 }
1296
1297 Ref<MutableStyleProperties> StyleProperties::mutableCopy() const
1298 {
1299     return adoptRef(*new MutableStyleProperties(*this));
1300 }
1301
1302 Ref<MutableStyleProperties> StyleProperties::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const
1303 {
1304     Vector<CSSProperty, 256> list;
1305     list.reserveInitialCapacity(length);
1306     for (unsigned i = 0; i < length; ++i) {
1307         if (auto value = getPropertyCSSValueInternal(set[i]))
1308             list.uncheckedAppend(CSSProperty(set[i], WTFMove(value), false));
1309     }
1310     return MutableStyleProperties::create(list.data(), list.size());
1311 }
1312
1313 PropertySetCSSStyleDeclaration* MutableStyleProperties::cssStyleDeclaration()
1314 {
1315     return m_cssomWrapper.get();
1316 }
1317
1318 CSSStyleDeclaration* MutableStyleProperties::ensureCSSStyleDeclaration()
1319 {
1320     if (m_cssomWrapper) {
1321         ASSERT(!static_cast<CSSStyleDeclaration*>(m_cssomWrapper.get())->parentRule());
1322         ASSERT(!m_cssomWrapper->parentElement());
1323         return m_cssomWrapper.get();
1324     }
1325     m_cssomWrapper = std::make_unique<PropertySetCSSStyleDeclaration>(this);
1326     return m_cssomWrapper.get();
1327 }
1328
1329 CSSStyleDeclaration* MutableStyleProperties::ensureInlineCSSStyleDeclaration(StyledElement* parentElement)
1330 {
1331     if (m_cssomWrapper) {
1332         ASSERT(m_cssomWrapper->parentElement() == parentElement);
1333         return m_cssomWrapper.get();
1334     }
1335     m_cssomWrapper = std::make_unique<InlineCSSStyleDeclaration>(this, parentElement);
1336     return m_cssomWrapper.get();
1337 }
1338
1339 unsigned StyleProperties::averageSizeInBytes()
1340 {
1341     // Please update this if the storage scheme changes so that this longer reflects the actual size.
1342     return sizeForImmutableStylePropertiesWithPropertyCount(4);
1343 }
1344
1345 // See the function above if you need to update this.
1346 struct SameSizeAsStyleProperties : public RefCounted<SameSizeAsStyleProperties> {
1347     unsigned bitfield;
1348 };
1349 COMPILE_ASSERT(sizeof(StyleProperties) == sizeof(SameSizeAsStyleProperties), style_property_set_should_stay_small);
1350
1351 #ifndef NDEBUG
1352 void StyleProperties::showStyle()
1353 {
1354     fprintf(stderr, "%s\n", asText().ascii().data());
1355 }
1356 #endif
1357
1358 Ref<MutableStyleProperties> MutableStyleProperties::create(CSSParserMode cssParserMode)
1359 {
1360     return adoptRef(*new MutableStyleProperties(cssParserMode));
1361 }
1362
1363 Ref<MutableStyleProperties> MutableStyleProperties::create(const CSSProperty* properties, unsigned count)
1364 {
1365     return adoptRef(*new MutableStyleProperties(properties, count));
1366 }
1367
1368 String StyleProperties::PropertyReference::cssName() const
1369 {
1370     if (id() == CSSPropertyCustom)
1371         return downcast<CSSCustomPropertyValue>(*value()).name();
1372     return getPropertyNameString(id());
1373 }
1374
1375 String StyleProperties::PropertyReference::cssText() const
1376 {
1377     StringBuilder result;
1378     result.append(cssName());
1379     result.appendLiteral(": ");
1380     result.append(m_value->cssText());
1381     if (isImportant())
1382         result.appendLiteral(" !important");
1383     result.append(';');
1384     return result.toString();
1385 }
1386     
1387 Ref<DeferredStyleProperties> DeferredStyleProperties::create(const CSSParserTokenRange& tokenRange, CSSDeferredParser& parser)
1388 {
1389     return adoptRef(*new DeferredStyleProperties(tokenRange, parser));
1390 }
1391
1392 DeferredStyleProperties::DeferredStyleProperties(const CSSParserTokenRange& range, CSSDeferredParser& parser)
1393     : StylePropertiesBase(parser.mode(), DeferredPropertiesType)
1394     , m_parser(parser)
1395 {
1396     size_t length = range.end() - range.begin();
1397     m_tokens.reserveCapacity(length);
1398     m_tokens.append(range.begin(), length);
1399 }
1400     
1401 DeferredStyleProperties::~DeferredStyleProperties()
1402 {
1403 }
1404
1405 Ref<ImmutableStyleProperties> DeferredStyleProperties::parseDeferredProperties()
1406 {
1407     return m_parser->parseDeclaration(m_tokens);
1408 }
1409
1410 } // namespace WebCore