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 "CSSParser.h"
28#include "CSSValueKeywords.h"
29#include "CSSValueList.h"
30#include "CSSValuePool.h"
31#include "Document.h"
32#include "PropertySetCSSStyleDeclaration.h"
33#include "StylePropertyShorthand.h"
34#include "StyleSheetContents.h"
35#include <bitset>
36#include <wtf/text/StringBuilder.h>
37
38#ifndef NDEBUG
39#include <stdio.h>
40#include <wtf/ASCIICType.h>
41#include <wtf/text/CString.h>
42#endif
43
44namespace WebCore {
45
46static size_t sizeForImmutableStylePropertiesWithPropertyCount(unsigned count)
47{
48    return sizeof(ImmutableStyleProperties) - sizeof(void*) + sizeof(CSSValue*) * count + sizeof(StylePropertyMetadata) * count;
49}
50
51static bool isInitialOrInherit(const String& value)
52{
53    return value.length() == 7 && (value == "initial" || value == "inherit");
54}
55
56PassRef<ImmutableStyleProperties> ImmutableStyleProperties::create(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode)
57{
58    void* slot = WTF::fastMalloc(sizeForImmutableStylePropertiesWithPropertyCount(count));
59    return adoptRef(*new (NotNull, slot) ImmutableStyleProperties(properties, count, cssParserMode));
60}
61
62PassRef<ImmutableStyleProperties> StyleProperties::immutableCopyIfNeeded() const
63{
64    if (!isMutable())
65        return static_cast<ImmutableStyleProperties&>(const_cast<StyleProperties&>(*this));
66    const MutableStyleProperties& mutableThis = static_cast<const MutableStyleProperties&>(*this);
67    return ImmutableStyleProperties::create(mutableThis.m_propertyVector.data(), mutableThis.m_propertyVector.size(), cssParserMode());
68}
69
70MutableStyleProperties::MutableStyleProperties(CSSParserMode cssParserMode)
71    : StyleProperties(cssParserMode)
72{
73}
74
75MutableStyleProperties::MutableStyleProperties(const CSSProperty* properties, unsigned length)
76    : StyleProperties(CSSStrictMode)
77{
78    m_propertyVector.reserveInitialCapacity(length);
79    for (unsigned i = 0; i < length; ++i)
80        m_propertyVector.uncheckedAppend(properties[i]);
81}
82
83MutableStyleProperties::~MutableStyleProperties()
84{
85}
86
87ImmutableStyleProperties::ImmutableStyleProperties(const CSSProperty* properties, unsigned length, CSSParserMode cssParserMode)
88    : StyleProperties(cssParserMode, length)
89{
90    StylePropertyMetadata* metadataArray = const_cast<StylePropertyMetadata*>(this->metadataArray());
91    CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray());
92    for (unsigned i = 0; i < length; ++i) {
93        metadataArray[i] = properties[i].metadata();
94        valueArray[i] = properties[i].value();
95        valueArray[i]->ref();
96    }
97}
98
99ImmutableStyleProperties::~ImmutableStyleProperties()
100{
101    CSSValue** valueArray = const_cast<CSSValue**>(this->valueArray());
102    for (unsigned i = 0; i < m_arraySize; ++i)
103        valueArray[i]->deref();
104}
105
106MutableStyleProperties::MutableStyleProperties(const StyleProperties& other)
107    : StyleProperties(other.cssParserMode())
108{
109    if (other.isMutable())
110        m_propertyVector = static_cast<const MutableStyleProperties&>(other).m_propertyVector;
111    else {
112        m_propertyVector.reserveInitialCapacity(other.propertyCount());
113        for (unsigned i = 0; i < other.propertyCount(); ++i)
114            m_propertyVector.uncheckedAppend(other.propertyAt(i).toCSSProperty());
115    }
116}
117
118String StyleProperties::getPropertyValue(CSSPropertyID propertyID) const
119{
120    RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
121    if (value)
122        return value->cssText();
123
124    // Shorthand and 4-values properties
125    switch (propertyID) {
126    case CSSPropertyBorderSpacing:
127        return borderSpacingValue(borderSpacingShorthand());
128    case CSSPropertyBackgroundPosition:
129        return getLayeredShorthandValue(backgroundPositionShorthand());
130    case CSSPropertyBackgroundRepeat:
131        return getLayeredShorthandValue(backgroundRepeatShorthand());
132    case CSSPropertyBackground:
133        return getLayeredShorthandValue(backgroundShorthand());
134    case CSSPropertyBorder:
135        return borderPropertyValue(OmitUncommonValues);
136    case CSSPropertyBorderTop:
137        return getShorthandValue(borderTopShorthand());
138    case CSSPropertyBorderRight:
139        return getShorthandValue(borderRightShorthand());
140    case CSSPropertyBorderBottom:
141        return getShorthandValue(borderBottomShorthand());
142    case CSSPropertyBorderLeft:
143        return getShorthandValue(borderLeftShorthand());
144    case CSSPropertyOutline:
145        return getShorthandValue(outlineShorthand());
146    case CSSPropertyBorderColor:
147        return get4Values(borderColorShorthand());
148    case CSSPropertyBorderWidth:
149        return get4Values(borderWidthShorthand());
150    case CSSPropertyBorderStyle:
151        return get4Values(borderStyleShorthand());
152    case CSSPropertyWebkitColumnRule:
153        return getShorthandValue(webkitColumnRuleShorthand());
154    case CSSPropertyWebkitColumns:
155        return getShorthandValue(webkitColumnsShorthand());
156    case CSSPropertyWebkitFlex:
157        return getShorthandValue(webkitFlexShorthand());
158    case CSSPropertyWebkitFlexFlow:
159        return getShorthandValue(webkitFlexFlowShorthand());
160#if ENABLE(CSS_GRID_LAYOUT)
161    case CSSPropertyWebkitGridArea:
162        return getShorthandValue(webkitGridAreaShorthand());
163    case CSSPropertyWebkitGridTemplate:
164        return getShorthandValue(webkitGridTemplateShorthand());
165    case CSSPropertyWebkitGrid:
166        return getShorthandValue(webkitGridShorthand());
167    case CSSPropertyWebkitGridColumn:
168        return getShorthandValue(webkitGridColumnShorthand());
169    case CSSPropertyWebkitGridRow:
170        return getShorthandValue(webkitGridRowShorthand());
171#endif
172    case CSSPropertyFont:
173        return fontValue();
174    case CSSPropertyMargin:
175        return get4Values(marginShorthand());
176    case CSSPropertyWebkitMarginCollapse:
177        return getShorthandValue(webkitMarginCollapseShorthand());
178    case CSSPropertyOverflow:
179        return getCommonValue(overflowShorthand());
180    case CSSPropertyPadding:
181        return get4Values(paddingShorthand());
182    case CSSPropertyTransition:
183        return getLayeredShorthandValue(transitionShorthand());
184    case CSSPropertyListStyle:
185        return getShorthandValue(listStyleShorthand());
186    case CSSPropertyWebkitMarquee:
187        return getShorthandValue(webkitMarqueeShorthand());
188    case CSSPropertyWebkitMaskPosition:
189        return getLayeredShorthandValue(webkitMaskPositionShorthand());
190    case CSSPropertyWebkitMaskRepeat:
191        return getLayeredShorthandValue(webkitMaskRepeatShorthand());
192    case CSSPropertyWebkitMask:
193        return getLayeredShorthandValue(webkitMaskShorthand());
194    case CSSPropertyWebkitTextEmphasis:
195        return getShorthandValue(webkitTextEmphasisShorthand());
196    case CSSPropertyWebkitTextStroke:
197        return getShorthandValue(webkitTextStrokeShorthand());
198    case CSSPropertyWebkitTransformOrigin:
199        return getShorthandValue(webkitTransformOriginShorthand());
200    case CSSPropertyWebkitTransition:
201        return getLayeredShorthandValue(webkitTransitionShorthand());
202    case CSSPropertyWebkitAnimation:
203        return getLayeredShorthandValue(webkitAnimationShorthand());
204    case CSSPropertyMarker: {
205        RefPtr<CSSValue> value = getPropertyCSSValue(CSSPropertyMarkerStart);
206        if (value)
207            return value->cssText();
208        return String();
209    }
210    case CSSPropertyBorderRadius:
211        return get4Values(borderRadiusShorthand());
212    default:
213        return String();
214    }
215}
216
217String StyleProperties::borderSpacingValue(const StylePropertyShorthand& shorthand) const
218{
219    RefPtr<CSSValue> horizontalValue = getPropertyCSSValue(shorthand.properties()[0]);
220    RefPtr<CSSValue> verticalValue = getPropertyCSSValue(shorthand.properties()[1]);
221
222    // While standard border-spacing property does not allow specifying border-spacing-vertical without
223    // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
224    // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal.
225    if (!horizontalValue || !verticalValue)
226        return String();
227
228    String horizontalValueCSSText = horizontalValue->cssText();
229    String verticalValueCSSText = verticalValue->cssText();
230    if (horizontalValueCSSText == verticalValueCSSText)
231        return horizontalValueCSSText;
232    return horizontalValueCSSText + ' ' + verticalValueCSSText;
233}
234
235void StyleProperties::appendFontLonghandValueIfExplicit(CSSPropertyID propertyID, StringBuilder& result, String& commonValue) const
236{
237    int foundPropertyIndex = findPropertyIndex(propertyID);
238    if (foundPropertyIndex == -1)
239        return; // All longhands must have at least implicit values if "font" is specified.
240
241    if (propertyAt(foundPropertyIndex).isImplicit()) {
242        commonValue = String();
243        return;
244    }
245
246    char prefix = '\0';
247    switch (propertyID) {
248    case CSSPropertyFontStyle:
249        break; // No prefix.
250    case CSSPropertyFontFamily:
251    case CSSPropertyFontVariant:
252    case CSSPropertyFontWeight:
253        prefix = ' ';
254        break;
255    case CSSPropertyLineHeight:
256        prefix = '/';
257        break;
258    default:
259        ASSERT_NOT_REACHED();
260    }
261
262    if (prefix && !result.isEmpty())
263        result.append(prefix);
264    String value = propertyAt(foundPropertyIndex).value()->cssText();
265    result.append(value);
266    if (!commonValue.isNull() && commonValue != value)
267        commonValue = String();
268}
269
270String StyleProperties::fontValue() const
271{
272    int fontSizePropertyIndex = findPropertyIndex(CSSPropertyFontSize);
273    int fontFamilyPropertyIndex = findPropertyIndex(CSSPropertyFontFamily);
274    if (fontSizePropertyIndex == -1 || fontFamilyPropertyIndex == -1)
275        return emptyString();
276
277    PropertyReference fontSizeProperty = propertyAt(fontSizePropertyIndex);
278    PropertyReference fontFamilyProperty = propertyAt(fontFamilyPropertyIndex);
279    if (fontSizeProperty.isImplicit() || fontFamilyProperty.isImplicit())
280        return emptyString();
281
282    String commonValue = fontSizeProperty.value()->cssText();
283    StringBuilder result;
284    appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result, commonValue);
285    appendFontLonghandValueIfExplicit(CSSPropertyFontVariant, result, commonValue);
286    appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result, commonValue);
287    if (!result.isEmpty())
288        result.append(' ');
289    result.append(fontSizeProperty.value()->cssText());
290    appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result, commonValue);
291    if (!result.isEmpty())
292        result.append(' ');
293    result.append(fontFamilyProperty.value()->cssText());
294    if (isInitialOrInherit(commonValue))
295        return commonValue;
296    return result.toString();
297}
298
299String StyleProperties::get4Values(const StylePropertyShorthand& shorthand) const
300{
301    // Assume the properties are in the usual order top, right, bottom, left.
302    int topValueIndex = findPropertyIndex(shorthand.properties()[0]);
303    int rightValueIndex = findPropertyIndex(shorthand.properties()[1]);
304    int bottomValueIndex = findPropertyIndex(shorthand.properties()[2]);
305    int leftValueIndex = findPropertyIndex(shorthand.properties()[3]);
306
307    if (topValueIndex == -1 || rightValueIndex == -1 || bottomValueIndex == -1 || leftValueIndex == -1)
308        return String();
309
310    PropertyReference top = propertyAt(topValueIndex);
311    PropertyReference right = propertyAt(rightValueIndex);
312    PropertyReference bottom = propertyAt(bottomValueIndex);
313    PropertyReference left = propertyAt(leftValueIndex);
314
315    // All 4 properties must be specified.
316    if (!top.value() || !right.value() || !bottom.value() || !left.value())
317        return String();
318
319    if (top.isInherited() && right.isInherited() && bottom.isInherited() && left.isInherited())
320        return getValueName(CSSValueInherit);
321
322    if (top.value()->isInitialValue() || right.value()->isInitialValue() || bottom.value()->isInitialValue() || left.value()->isInitialValue()) {
323        if (top.value()->isInitialValue() && right.value()->isInitialValue() && bottom.value()->isInitialValue() && left.value()->isInitialValue() && !top.isImplicit()) {
324            // All components are "initial" and "top" is not implicit.
325            return getValueName(CSSValueInitial);
326        }
327        return String();
328    }
329    if (top.isImportant() != right.isImportant() || right.isImportant() != bottom.isImportant() || bottom.isImportant() != left.isImportant())
330        return String();
331
332    bool showLeft = !right.value()->equals(*left.value());
333    bool showBottom = !top.value()->equals(*bottom.value()) || showLeft;
334    bool showRight = !top.value()->equals(*right.value()) || showBottom;
335
336    StringBuilder result;
337    result.append(top.value()->cssText());
338    if (showRight) {
339        result.append(' ');
340        result.append(right.value()->cssText());
341    }
342    if (showBottom) {
343        result.append(' ');
344        result.append(bottom.value()->cssText());
345    }
346    if (showLeft) {
347        result.append(' ');
348        result.append(left.value()->cssText());
349    }
350    return result.toString();
351}
352
353String StyleProperties::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const
354{
355    StringBuilder result;
356
357    const unsigned size = shorthand.length();
358    // Begin by collecting the properties into an array.
359    Vector< RefPtr<CSSValue>> values(size);
360    size_t numLayers = 0;
361
362    for (unsigned i = 0; i < size; ++i) {
363        values[i] = getPropertyCSSValue(shorthand.properties()[i]);
364        if (values[i]) {
365            if (values[i]->isBaseValueList())
366                numLayers = std::max(toCSSValueList(values[i].get())->length(), numLayers);
367            else
368                numLayers = std::max<size_t>(1U, numLayers);
369        }
370    }
371
372    String commonValue;
373    bool commonValueInitialized = false;
374
375    // Now stitch the properties together. Implicit initial values are flagged as such and
376    // can safely be omitted.
377    for (size_t i = 0; i < numLayers; i++) {
378        StringBuilder layerResult;
379        bool useRepeatXShorthand = false;
380        bool useRepeatYShorthand = false;
381        bool useSingleWordShorthand = false;
382        bool foundPositionYCSSProperty = false;
383        for (unsigned j = 0; j < size; j++) {
384            RefPtr<CSSValue> value;
385            if (values[j]) {
386                if (values[j]->isBaseValueList())
387                    value = toCSSValueList(values[j].get())->item(i);
388                else {
389                    value = values[j];
390
391                    // Color only belongs in the last layer.
392                    if (shorthand.properties()[j] == CSSPropertyBackgroundColor) {
393                        if (i != numLayers - 1)
394                            value = 0;
395                    } else if (i) // Other singletons only belong in the first layer.
396                        value = 0;
397                }
398            }
399
400            // We need to report background-repeat as it was written in the CSS. If the property is implicit,
401            // then it was written with only one value. Here we figure out which value that was so we can
402            // report back correctly.
403            if ((shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(shorthand.properties()[j]))
404                || (shorthand.properties()[j] == CSSPropertyWebkitMaskRepeatX && isPropertyImplicit(shorthand.properties()[j]))) {
405
406                // BUG 49055: make sure the value was not reset in the layer check just above.
407                if ((j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value)
408                    || (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyWebkitMaskRepeatY && value)) {
409                    RefPtr<CSSValue> yValue;
410                    RefPtr<CSSValue> nextValue = values[j + 1];
411                    if (nextValue->isValueList())
412                        yValue = toCSSValueList(nextValue.get())->itemWithoutBoundsCheck(i);
413                    else
414                        yValue = nextValue;
415
416                    if (!value->isPrimitiveValue() || !yValue->isPrimitiveValue())
417                        continue;
418
419                    CSSValueID xId = toCSSPrimitiveValue(value.get())->getValueID();
420                    CSSValueID yId = toCSSPrimitiveValue(yValue.get())->getValueID();
421                    if (xId != yId) {
422                        if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
423                            useRepeatXShorthand = true;
424                            ++j;
425                        } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
426                            useRepeatYShorthand = true;
427                            continue;
428                        }
429                    } else {
430                        useSingleWordShorthand = true;
431                        ++j;
432                    }
433                }
434            }
435
436            String valueText;
437            if (value && !value->isImplicitInitialValue()) {
438                if (!layerResult.isEmpty())
439                    layerResult.append(' ');
440                if (foundPositionYCSSProperty
441                    && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
442                    layerResult.appendLiteral("/ ");
443                if (!foundPositionYCSSProperty
444                    && (shorthand.properties()[j] == CSSPropertyBackgroundSize || shorthand.properties()[j] == CSSPropertyWebkitMaskSize))
445                    continue;
446
447                if (useRepeatXShorthand) {
448                    useRepeatXShorthand = false;
449                    layerResult.append(getValueName(CSSValueRepeatX));
450                } else if (useRepeatYShorthand) {
451                    useRepeatYShorthand = false;
452                    layerResult.append(getValueName(CSSValueRepeatY));
453                } else {
454                    if (useSingleWordShorthand)
455                        useSingleWordShorthand = false;
456                    valueText = value->cssText();
457                    layerResult.append(valueText);
458                }
459
460                if (shorthand.properties()[j] == CSSPropertyBackgroundPositionY
461                    || shorthand.properties()[j] == CSSPropertyWebkitMaskPositionY) {
462                    foundPositionYCSSProperty = true;
463
464                    // background-position is a special case: if only the first offset is specified,
465                    // the second one defaults to "center", not the same value.
466                    if (commonValueInitialized && commonValue != "initial" && commonValue != "inherit")
467                        commonValue = String();
468                }
469            }
470
471            if (!commonValueInitialized) {
472                commonValue = valueText;
473                commonValueInitialized = true;
474            } else if (!commonValue.isNull() && commonValue != valueText)
475                commonValue = String();
476        }
477
478        if (!layerResult.isEmpty()) {
479            if (!result.isEmpty())
480                result.appendLiteral(", ");
481            result.append(layerResult);
482        }
483    }
484
485    if (isInitialOrInherit(commonValue))
486        return commonValue;
487
488    if (result.isEmpty())
489        return String();
490    return result.toString();
491}
492
493String StyleProperties::getShorthandValue(const StylePropertyShorthand& shorthand) const
494{
495    String commonValue;
496    StringBuilder result;
497    for (unsigned i = 0; i < shorthand.length(); ++i) {
498        if (!isPropertyImplicit(shorthand.properties()[i])) {
499            RefPtr<CSSValue> value = getPropertyCSSValue(shorthand.properties()[i]);
500            if (!value)
501                return String();
502            String valueText = value->cssText();
503            if (!i)
504                commonValue = valueText;
505            else if (!commonValue.isNull() && commonValue != valueText)
506                commonValue = String();
507            if (value->isInitialValue())
508                continue;
509            if (!result.isEmpty())
510                result.append(' ');
511            result.append(valueText);
512        } else
513            commonValue = String();
514    }
515    if (isInitialOrInherit(commonValue))
516        return commonValue;
517    if (result.isEmpty())
518        return String();
519    return result.toString();
520}
521
522// only returns a non-null value if all properties have the same, non-null value
523String StyleProperties::getCommonValue(const StylePropertyShorthand& shorthand) const
524{
525    String res;
526    bool lastPropertyWasImportant = false;
527    for (unsigned i = 0; i < shorthand.length(); ++i) {
528        RefPtr<CSSValue> value = getPropertyCSSValue(shorthand.properties()[i]);
529        // FIXME: CSSInitialValue::cssText should generate the right value.
530        if (!value)
531            return String();
532        String text = value->cssText();
533        if (text.isNull())
534            return String();
535        if (res.isNull())
536            res = text;
537        else if (res != text)
538            return String();
539
540        bool currentPropertyIsImportant = propertyIsImportant(shorthand.properties()[i]);
541        if (i && lastPropertyWasImportant != currentPropertyIsImportant)
542            return String();
543        lastPropertyWasImportant = currentPropertyIsImportant;
544    }
545    return res;
546}
547
548String StyleProperties::borderPropertyValue(CommonValueMode valueMode) const
549{
550    const StylePropertyShorthand properties[3] = { borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand() };
551    String commonValue;
552    StringBuilder result;
553    for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
554        String value = getCommonValue(properties[i]);
555        if (value.isNull()) {
556            if (valueMode == ReturnNullOnUncommonValues)
557                return String();
558            ASSERT(valueMode == OmitUncommonValues);
559            continue;
560        }
561        if (!i)
562            commonValue = value;
563        else if (!commonValue.isNull() && commonValue != value)
564            commonValue = String();
565        if (value == "initial")
566            continue;
567        if (!result.isEmpty())
568            result.append(' ');
569        result.append(value);
570    }
571    if (isInitialOrInherit(commonValue))
572        return commonValue;
573    return result.isEmpty() ? String() : result.toString();
574}
575
576PassRefPtr<CSSValue> StyleProperties::getPropertyCSSValue(CSSPropertyID propertyID) const
577{
578    int foundPropertyIndex = findPropertyIndex(propertyID);
579    if (foundPropertyIndex == -1)
580        return 0;
581    return propertyAt(foundPropertyIndex).value();
582}
583
584bool MutableStyleProperties::removeShorthandProperty(CSSPropertyID propertyID)
585{
586    StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
587    if (!shorthand.length())
588        return false;
589
590    bool ret = removePropertiesInSet(shorthand.properties(), shorthand.length());
591
592    CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(propertyID);
593    if (prefixingVariant == propertyID)
594        return ret;
595
596    StylePropertyShorthand shorthandPrefixingVariant = shorthandForProperty(prefixingVariant);
597    return removePropertiesInSet(shorthandPrefixingVariant.properties(), shorthandPrefixingVariant.length());
598}
599
600bool MutableStyleProperties::removeProperty(CSSPropertyID propertyID, String* returnText)
601{
602    if (removeShorthandProperty(propertyID)) {
603        // FIXME: Return an equivalent shorthand when possible.
604        if (returnText)
605            *returnText = "";
606        return true;
607    }
608
609    int foundPropertyIndex = findPropertyIndex(propertyID);
610    if (foundPropertyIndex == -1) {
611        if (returnText)
612            *returnText = "";
613        return false;
614    }
615
616    if (returnText)
617        *returnText = propertyAt(foundPropertyIndex).value()->cssText();
618
619    // A more efficient removal strategy would involve marking entries as empty
620    // and sweeping them when the vector grows too big.
621    m_propertyVector.remove(foundPropertyIndex);
622
623    removePrefixedOrUnprefixedProperty(propertyID);
624
625    return true;
626}
627
628void MutableStyleProperties::removePrefixedOrUnprefixedProperty(CSSPropertyID propertyID)
629{
630    int foundPropertyIndex = findPropertyIndex(prefixingVariantForPropertyId(propertyID));
631    if (foundPropertyIndex == -1)
632        return;
633    m_propertyVector.remove(foundPropertyIndex);
634}
635
636bool StyleProperties::propertyIsImportant(CSSPropertyID propertyID) const
637{
638    int foundPropertyIndex = findPropertyIndex(propertyID);
639    if (foundPropertyIndex != -1)
640        return propertyAt(foundPropertyIndex).isImportant();
641
642    StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
643    if (!shorthand.length())
644        return false;
645
646    for (unsigned i = 0; i < shorthand.length(); ++i) {
647        if (!propertyIsImportant(shorthand.properties()[i]))
648            return false;
649    }
650    return true;
651}
652
653String StyleProperties::getPropertyShorthand(CSSPropertyID propertyID) const
654{
655    int foundPropertyIndex = findPropertyIndex(propertyID);
656    if (foundPropertyIndex == -1)
657        return String();
658    return getPropertyNameString(propertyAt(foundPropertyIndex).shorthandID());
659}
660
661bool StyleProperties::isPropertyImplicit(CSSPropertyID propertyID) const
662{
663    int foundPropertyIndex = findPropertyIndex(propertyID);
664    if (foundPropertyIndex == -1)
665        return false;
666    return propertyAt(foundPropertyIndex).isImplicit();
667}
668
669bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String& value, bool important, StyleSheetContents* contextStyleSheet)
670{
671    // Setting the value to an empty string just removes the property in both IE and Gecko.
672    // Setting it to null seems to produce less consistent results, but we treat it just the same.
673    if (value.isEmpty())
674        return removeProperty(propertyID);
675
676    // When replacing an existing property value, this moves the property to the end of the list.
677    // Firefox preserves the position, and MSIE moves the property to the beginning.
678    return CSSParser::parseValue(this, propertyID, value, important, cssParserMode(), contextStyleSheet);
679}
680
681void MutableStyleProperties::setProperty(CSSPropertyID propertyID, PassRefPtr<CSSValue> prpValue, bool important)
682{
683    StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
684    if (!shorthand.length()) {
685        setProperty(CSSProperty(propertyID, prpValue, important));
686        return;
687    }
688
689    removePropertiesInSet(shorthand.properties(), shorthand.length());
690
691    RefPtr<CSSValue> value = prpValue;
692    for (unsigned i = 0; i < shorthand.length(); ++i)
693        m_propertyVector.append(CSSProperty(shorthand.properties()[i], value, important));
694}
695
696void MutableStyleProperties::setProperty(const CSSProperty& property, CSSProperty* slot)
697{
698    if (!removeShorthandProperty(property.id())) {
699        CSSProperty* toReplace = slot ? slot : findCSSPropertyWithID(property.id());
700        if (toReplace) {
701            *toReplace = property;
702            setPrefixingVariantProperty(property);
703            return;
704        }
705    }
706    appendPrefixingVariantProperty(property);
707}
708
709static unsigned getIndexInShorthandVectorForPrefixingVariant(const CSSProperty& property, CSSPropertyID prefixingVariant)
710{
711    if (!property.isSetFromShorthand())
712        return 0;
713
714    CSSPropertyID prefixedShorthand = prefixingVariantForPropertyId(property.shorthandID());
715    return indexOfShorthandForLonghand(prefixedShorthand, matchingShorthandsForLonghand(prefixingVariant));
716}
717
718void MutableStyleProperties::appendPrefixingVariantProperty(const CSSProperty& property)
719{
720    m_propertyVector.append(property);
721    CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(property.id());
722    if (prefixingVariant == property.id())
723        return;
724
725    m_propertyVector.append(CSSProperty(prefixingVariant, property.value(), property.isImportant(), property.isSetFromShorthand(), getIndexInShorthandVectorForPrefixingVariant(property, prefixingVariant), property.metadata().m_implicit));
726}
727
728void MutableStyleProperties::setPrefixingVariantProperty(const CSSProperty& property)
729{
730    CSSPropertyID prefixingVariant = prefixingVariantForPropertyId(property.id());
731    CSSProperty* toReplace = findCSSPropertyWithID(prefixingVariant);
732    if (toReplace && prefixingVariant != property.id())
733        *toReplace = CSSProperty(prefixingVariant, property.value(), property.isImportant(), property.isSetFromShorthand(), getIndexInShorthandVectorForPrefixingVariant(property, prefixingVariant), property.metadata().m_implicit);
734}
735
736bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSValueID identifier, bool important)
737{
738    setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
739    return true;
740}
741
742bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, CSSPropertyID identifier, bool important)
743{
744    setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
745    return true;
746}
747
748void MutableStyleProperties::parseDeclaration(const String& styleDeclaration, StyleSheetContents* contextStyleSheet)
749{
750    m_propertyVector.clear();
751
752    CSSParserContext context(cssParserMode());
753    if (contextStyleSheet) {
754        context = contextStyleSheet->parserContext();
755        context.mode = cssParserMode();
756    }
757    CSSParser parser(context);
758    parser.parseDeclaration(this, styleDeclaration, 0, contextStyleSheet);
759}
760
761void MutableStyleProperties::addParsedProperties(const Vector<CSSProperty>& properties)
762{
763    m_propertyVector.reserveCapacity(m_propertyVector.size() + properties.size());
764    for (unsigned i = 0; i < properties.size(); ++i)
765        addParsedProperty(properties[i]);
766}
767
768void MutableStyleProperties::addParsedProperty(const CSSProperty& property)
769{
770    // Only add properties that have no !important counterpart present
771    if (!propertyIsImportant(property.id()) || property.isImportant())
772        setProperty(property);
773}
774
775String StyleProperties::asText() const
776{
777    StringBuilder result;
778
779    int positionXPropertyIndex = -1;
780    int positionYPropertyIndex = -1;
781    int repeatXPropertyIndex = -1;
782    int repeatYPropertyIndex = -1;
783
784    std::bitset<numCSSProperties> shorthandPropertyUsed;
785    std::bitset<numCSSProperties> shorthandPropertyAppeared;
786
787    unsigned size = propertyCount();
788    unsigned numDecls = 0;
789    for (unsigned n = 0; n < size; ++n) {
790        PropertyReference property = propertyAt(n);
791        CSSPropertyID propertyID = property.id();
792        CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
793        CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
794        String value;
795
796        switch (propertyID) {
797        case CSSPropertyBackgroundPositionX:
798            positionXPropertyIndex = n;
799            continue;
800        case CSSPropertyBackgroundPositionY:
801            positionYPropertyIndex = n;
802            continue;
803        case CSSPropertyBackgroundRepeatX:
804            repeatXPropertyIndex = n;
805            continue;
806        case CSSPropertyBackgroundRepeatY:
807            repeatYPropertyIndex = n;
808            continue;
809        case CSSPropertyBorderTopWidth:
810        case CSSPropertyBorderRightWidth:
811        case CSSPropertyBorderBottomWidth:
812        case CSSPropertyBorderLeftWidth:
813            if (!borderFallbackShorthandProperty)
814                borderFallbackShorthandProperty = CSSPropertyBorderWidth;
815            FALLTHROUGH;
816        case CSSPropertyBorderTopStyle:
817        case CSSPropertyBorderRightStyle:
818        case CSSPropertyBorderBottomStyle:
819        case CSSPropertyBorderLeftStyle:
820            if (!borderFallbackShorthandProperty)
821                borderFallbackShorthandProperty = CSSPropertyBorderStyle;
822            FALLTHROUGH;
823        case CSSPropertyBorderTopColor:
824        case CSSPropertyBorderRightColor:
825        case CSSPropertyBorderBottomColor:
826        case CSSPropertyBorderLeftColor:
827            if (!borderFallbackShorthandProperty)
828                borderFallbackShorthandProperty = CSSPropertyBorderColor;
829
830            // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
831            ASSERT(CSSPropertyBorder - firstCSSProperty < shorthandPropertyAppeared.size());
832            if (!shorthandPropertyAppeared[CSSPropertyBorder - firstCSSProperty]) {
833                value = borderPropertyValue(ReturnNullOnUncommonValues);
834                if (value.isNull())
835                    shorthandPropertyAppeared.set(CSSPropertyBorder - firstCSSProperty);
836                else
837                    shorthandPropertyID = CSSPropertyBorder;
838            } else if (shorthandPropertyUsed[CSSPropertyBorder - firstCSSProperty])
839                shorthandPropertyID = CSSPropertyBorder;
840            if (!shorthandPropertyID)
841                shorthandPropertyID = borderFallbackShorthandProperty;
842            break;
843        case CSSPropertyWebkitBorderHorizontalSpacing:
844        case CSSPropertyWebkitBorderVerticalSpacing:
845            shorthandPropertyID = CSSPropertyBorderSpacing;
846            break;
847        case CSSPropertyFontFamily:
848        case CSSPropertyLineHeight:
849        case CSSPropertyFontSize:
850        case CSSPropertyFontStyle:
851        case CSSPropertyFontVariant:
852        case CSSPropertyFontWeight:
853            // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
854            break;
855        case CSSPropertyListStyleType:
856        case CSSPropertyListStylePosition:
857        case CSSPropertyListStyleImage:
858            shorthandPropertyID = CSSPropertyListStyle;
859            break;
860        case CSSPropertyMarginTop:
861        case CSSPropertyMarginRight:
862        case CSSPropertyMarginBottom:
863        case CSSPropertyMarginLeft:
864            shorthandPropertyID = CSSPropertyMargin;
865            break;
866        case CSSPropertyOutlineWidth:
867        case CSSPropertyOutlineStyle:
868        case CSSPropertyOutlineColor:
869            shorthandPropertyID = CSSPropertyOutline;
870            break;
871        case CSSPropertyOverflowX:
872        case CSSPropertyOverflowY:
873            shorthandPropertyID = CSSPropertyOverflow;
874            break;
875        case CSSPropertyPaddingTop:
876        case CSSPropertyPaddingRight:
877        case CSSPropertyPaddingBottom:
878        case CSSPropertyPaddingLeft:
879            shorthandPropertyID = CSSPropertyPadding;
880            break;
881        case CSSPropertyTransitionProperty:
882        case CSSPropertyTransitionDuration:
883        case CSSPropertyTransitionTimingFunction:
884        case CSSPropertyTransitionDelay:
885            shorthandPropertyID = CSSPropertyTransition;
886            break;
887        case CSSPropertyWebkitAnimationName:
888        case CSSPropertyWebkitAnimationDuration:
889        case CSSPropertyWebkitAnimationTimingFunction:
890        case CSSPropertyWebkitAnimationDelay:
891        case CSSPropertyWebkitAnimationIterationCount:
892        case CSSPropertyWebkitAnimationDirection:
893        case CSSPropertyWebkitAnimationFillMode:
894            shorthandPropertyID = CSSPropertyWebkitAnimation;
895            break;
896        case CSSPropertyWebkitFlexDirection:
897        case CSSPropertyWebkitFlexWrap:
898            shorthandPropertyID = CSSPropertyWebkitFlexFlow;
899            break;
900        case CSSPropertyWebkitFlexBasis:
901        case CSSPropertyWebkitFlexGrow:
902        case CSSPropertyWebkitFlexShrink:
903            shorthandPropertyID = CSSPropertyWebkitFlex;
904            break;
905        case CSSPropertyWebkitMaskPositionX:
906        case CSSPropertyWebkitMaskPositionY:
907        case CSSPropertyWebkitMaskRepeatX:
908        case CSSPropertyWebkitMaskRepeatY:
909        case CSSPropertyWebkitMaskImage:
910        case CSSPropertyWebkitMaskRepeat:
911        case CSSPropertyWebkitMaskPosition:
912        case CSSPropertyWebkitMaskClip:
913        case CSSPropertyWebkitMaskOrigin:
914            shorthandPropertyID = CSSPropertyWebkitMask;
915            break;
916        case CSSPropertyWebkitTransformOriginX:
917        case CSSPropertyWebkitTransformOriginY:
918        case CSSPropertyWebkitTransformOriginZ:
919            shorthandPropertyID = CSSPropertyWebkitTransformOrigin;
920            break;
921        case CSSPropertyWebkitTransitionProperty:
922        case CSSPropertyWebkitTransitionDuration:
923        case CSSPropertyWebkitTransitionTimingFunction:
924        case CSSPropertyWebkitTransitionDelay:
925            shorthandPropertyID = CSSPropertyWebkitTransition;
926            break;
927        default:
928            break;
929        }
930
931        unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
932        if (shorthandPropertyID) {
933            ASSERT(shortPropertyIndex < shorthandPropertyUsed.size());
934            if (shorthandPropertyUsed[shortPropertyIndex])
935                continue;
936            if (!shorthandPropertyAppeared[shortPropertyIndex] && value.isNull())
937                value = getPropertyValue(shorthandPropertyID);
938            shorthandPropertyAppeared.set(shortPropertyIndex);
939        }
940
941        if (!value.isNull()) {
942            propertyID = shorthandPropertyID;
943            shorthandPropertyUsed.set(shortPropertyIndex);
944        } else
945            value = property.value()->cssText();
946
947        if (value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
948            continue;
949
950        if (numDecls++)
951            result.append(' ');
952        result.append(getPropertyName(propertyID));
953        result.appendLiteral(": ");
954        result.append(value);
955        if (property.isImportant())
956            result.appendLiteral(" !important");
957        result.append(';');
958    }
959
960    // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
961    // It is required because background-position-x/y are non-standard properties and WebKit generated output
962    // would not work in Firefox (<rdar://problem/5143183>)
963    // It would be a better solution if background-position was CSS_PAIR.
964    if (positionXPropertyIndex != -1 && positionYPropertyIndex != -1 && propertyAt(positionXPropertyIndex).isImportant() == propertyAt(positionYPropertyIndex).isImportant()) {
965        PropertyReference positionXProperty = propertyAt(positionXPropertyIndex);
966        PropertyReference positionYProperty = propertyAt(positionYPropertyIndex);
967
968        if (numDecls++)
969            result.append(' ');
970        result.appendLiteral("background-position: ");
971        if (positionXProperty.value()->isValueList() || positionYProperty.value()->isValueList())
972            result.append(getLayeredShorthandValue(backgroundPositionShorthand()));
973        else {
974            result.append(positionXProperty.value()->cssText());
975            result.append(' ');
976            result.append(positionYProperty.value()->cssText());
977        }
978        if (positionXProperty.isImportant())
979            result.appendLiteral(" !important");
980        result.append(';');
981    } else {
982        if (positionXPropertyIndex != -1) {
983            if (numDecls++)
984                result.append(' ');
985            result.append(propertyAt(positionXPropertyIndex).cssText());
986        }
987        if (positionYPropertyIndex != -1) {
988            if (numDecls++)
989                result.append(' ');
990            result.append(propertyAt(positionYPropertyIndex).cssText());
991        }
992    }
993
994    // FIXME: We need to do the same for background-repeat.
995    if (repeatXPropertyIndex != -1 && repeatYPropertyIndex != -1 && propertyAt(repeatXPropertyIndex).isImportant() == propertyAt(repeatYPropertyIndex).isImportant()) {
996        PropertyReference repeatXProperty = propertyAt(repeatXPropertyIndex);
997        PropertyReference repeatYProperty = propertyAt(repeatYPropertyIndex);
998
999        if (numDecls++)
1000            result.append(' ');
1001        result.appendLiteral("background-repeat: ");
1002        if (repeatXProperty.value()->isValueList() || repeatYProperty.value()->isValueList())
1003            result.append(getLayeredShorthandValue(backgroundRepeatShorthand()));
1004        else {
1005            result.append(repeatXProperty.value()->cssText());
1006            result.append(' ');
1007            result.append(repeatYProperty.value()->cssText());
1008        }
1009        if (repeatXProperty.isImportant())
1010            result.appendLiteral(" !important");
1011        result.append(';');
1012    } else {
1013        if (repeatXPropertyIndex != -1) {
1014            if (numDecls++)
1015                result.append(' ');
1016            result.append(propertyAt(repeatXPropertyIndex).cssText());
1017        }
1018        if (repeatYPropertyIndex != -1) {
1019            if (numDecls++)
1020                result.append(' ');
1021            result.append(propertyAt(repeatYPropertyIndex).cssText());
1022        }
1023    }
1024
1025    ASSERT(!numDecls ^ !result.isEmpty());
1026    return result.toString();
1027}
1028
1029bool StyleProperties::hasCSSOMWrapper() const
1030{
1031    return m_isMutable && static_cast<const MutableStyleProperties*>(this)->m_cssomWrapper;
1032}
1033
1034void MutableStyleProperties::mergeAndOverrideOnConflict(const StyleProperties& other)
1035{
1036    unsigned size = other.propertyCount();
1037    for (unsigned i = 0; i < size; ++i)
1038        addParsedProperty(other.propertyAt(i).toCSSProperty());
1039}
1040
1041void StyleProperties::addSubresourceStyleURLs(ListHashSet<URL>& urls, StyleSheetContents* contextStyleSheet) const
1042{
1043    unsigned size = propertyCount();
1044    for (unsigned i = 0; i < size; ++i)
1045        propertyAt(i).value()->addSubresourceStyleURLs(urls, contextStyleSheet);
1046}
1047
1048bool StyleProperties::hasFailedOrCanceledSubresources() const
1049{
1050    unsigned size = propertyCount();
1051    for (unsigned i = 0; i < size; ++i) {
1052        if (propertyAt(i).value()->hasFailedOrCanceledSubresources())
1053            return true;
1054    }
1055    return false;
1056}
1057
1058// This is the list of properties we want to copy in the copyBlockProperties() function.
1059// It is the list of CSS properties that apply specially to block-level elements.
1060static const CSSPropertyID blockProperties[] = {
1061    CSSPropertyOrphans,
1062    CSSPropertyOverflow, // This can be also be applied to replaced elements
1063    CSSPropertyWebkitAspectRatio,
1064    CSSPropertyWebkitColumnCount,
1065    CSSPropertyWebkitColumnGap,
1066    CSSPropertyWebkitColumnRuleColor,
1067    CSSPropertyWebkitColumnRuleStyle,
1068    CSSPropertyWebkitColumnRuleWidth,
1069    CSSPropertyWebkitColumnBreakBefore,
1070    CSSPropertyWebkitColumnBreakAfter,
1071    CSSPropertyWebkitColumnBreakInside,
1072    CSSPropertyWebkitColumnWidth,
1073    CSSPropertyPageBreakAfter,
1074    CSSPropertyPageBreakBefore,
1075    CSSPropertyPageBreakInside,
1076#if ENABLE(CSS_REGIONS)
1077    CSSPropertyWebkitRegionBreakAfter,
1078    CSSPropertyWebkitRegionBreakBefore,
1079    CSSPropertyWebkitRegionBreakInside,
1080#endif
1081    CSSPropertyTextAlign,
1082#if ENABLE(CSS3_TEXT)
1083    CSSPropertyWebkitTextAlignLast,
1084    CSSPropertyWebkitTextJustify,
1085#endif // CSS3_TEXT
1086    CSSPropertyTextIndent,
1087    CSSPropertyWidows
1088};
1089
1090void MutableStyleProperties::clear()
1091{
1092    m_propertyVector.clear();
1093}
1094
1095const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
1096
1097PassRef<MutableStyleProperties> StyleProperties::copyBlockProperties() const
1098{
1099    return copyPropertiesInSet(blockProperties, numBlockProperties);
1100}
1101
1102void MutableStyleProperties::removeBlockProperties()
1103{
1104    removePropertiesInSet(blockProperties, numBlockProperties);
1105}
1106
1107bool MutableStyleProperties::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
1108{
1109    if (m_propertyVector.isEmpty())
1110        return false;
1111
1112    // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
1113    HashSet<CSSPropertyID> toRemove;
1114    for (unsigned i = 0; i < length; ++i)
1115        toRemove.add(set[i]);
1116
1117    Vector<CSSProperty> newProperties;
1118    newProperties.reserveInitialCapacity(m_propertyVector.size());
1119
1120    unsigned size = m_propertyVector.size();
1121    for (unsigned n = 0; n < size; ++n) {
1122        const CSSProperty& property = m_propertyVector.at(n);
1123        // Not quite sure if the isImportant test is needed but it matches the existing behavior.
1124        if (!property.isImportant()) {
1125            if (toRemove.contains(property.id()))
1126                continue;
1127        }
1128        newProperties.append(property);
1129    }
1130
1131    bool changed = newProperties.size() != m_propertyVector.size();
1132    m_propertyVector = newProperties;
1133    return changed;
1134}
1135
1136int ImmutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const
1137{
1138    // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1139    // the compiler converting it to an int multiple times in the loop.
1140    uint16_t id = static_cast<uint16_t>(propertyID);
1141    for (int n = m_arraySize - 1 ; n >= 0; --n) {
1142        if (metadataArray()[n].m_propertyID == id)
1143            return n;
1144    }
1145
1146    return -1;
1147}
1148
1149int MutableStyleProperties::findPropertyIndex(CSSPropertyID propertyID) const
1150{
1151    // Convert here propertyID into an uint16_t to compare it with the metadata's m_propertyID to avoid
1152    // the compiler converting it to an int multiple times in the loop.
1153    uint16_t id = static_cast<uint16_t>(propertyID);
1154    for (int n = m_propertyVector.size() - 1 ; n >= 0; --n) {
1155        if (m_propertyVector.at(n).metadata().m_propertyID == id)
1156            return n;
1157    }
1158
1159    return -1;
1160}
1161
1162CSSProperty* MutableStyleProperties::findCSSPropertyWithID(CSSPropertyID propertyID)
1163{
1164    int foundPropertyIndex = findPropertyIndex(propertyID);
1165    if (foundPropertyIndex == -1)
1166        return 0;
1167    return &m_propertyVector.at(foundPropertyIndex);
1168}
1169
1170bool StyleProperties::propertyMatches(CSSPropertyID propertyID, const CSSValue* propertyValue) const
1171{
1172    int foundPropertyIndex = findPropertyIndex(propertyID);
1173    if (foundPropertyIndex == -1)
1174        return false;
1175    return propertyAt(foundPropertyIndex).value()->equals(*propertyValue);
1176}
1177
1178PassRef<MutableStyleProperties> StyleProperties::mutableCopy() const
1179{
1180    return adoptRef(*new MutableStyleProperties(*this));
1181}
1182
1183PassRef<MutableStyleProperties> StyleProperties::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const
1184{
1185    Vector<CSSProperty, 256> list;
1186    list.reserveInitialCapacity(length);
1187    for (unsigned i = 0; i < length; ++i) {
1188        RefPtr<CSSValue> value = getPropertyCSSValue(set[i]);
1189        if (value)
1190            list.append(CSSProperty(set[i], value.release(), false));
1191    }
1192    return MutableStyleProperties::create(list.data(), list.size());
1193}
1194
1195PropertySetCSSStyleDeclaration* MutableStyleProperties::cssStyleDeclaration()
1196{
1197    return m_cssomWrapper.get();
1198}
1199
1200CSSStyleDeclaration* MutableStyleProperties::ensureCSSStyleDeclaration()
1201{
1202    if (m_cssomWrapper) {
1203        ASSERT(!static_cast<CSSStyleDeclaration*>(m_cssomWrapper.get())->parentRule());
1204        ASSERT(!m_cssomWrapper->parentElement());
1205        return m_cssomWrapper.get();
1206    }
1207    m_cssomWrapper = std::make_unique<PropertySetCSSStyleDeclaration>(this);
1208    return m_cssomWrapper.get();
1209}
1210
1211CSSStyleDeclaration* MutableStyleProperties::ensureInlineCSSStyleDeclaration(StyledElement* parentElement)
1212{
1213    if (m_cssomWrapper) {
1214        ASSERT(m_cssomWrapper->parentElement() == parentElement);
1215        return m_cssomWrapper.get();
1216    }
1217    m_cssomWrapper = std::make_unique<InlineCSSStyleDeclaration>(this, parentElement);
1218    return m_cssomWrapper.get();
1219}
1220
1221unsigned StyleProperties::averageSizeInBytes()
1222{
1223    // Please update this if the storage scheme changes so that this longer reflects the actual size.
1224    return sizeForImmutableStylePropertiesWithPropertyCount(4);
1225}
1226
1227// See the function above if you need to update this.
1228struct SameSizeAsStyleProperties : public RefCounted<SameSizeAsStyleProperties> {
1229    unsigned bitfield;
1230};
1231COMPILE_ASSERT(sizeof(StyleProperties) == sizeof(SameSizeAsStyleProperties), style_property_set_should_stay_small);
1232
1233#ifndef NDEBUG
1234void StyleProperties::showStyle()
1235{
1236    fprintf(stderr, "%s\n", asText().ascii().data());
1237}
1238#endif
1239
1240PassRef<MutableStyleProperties> MutableStyleProperties::create(CSSParserMode cssParserMode)
1241{
1242    return adoptRef(*new MutableStyleProperties(cssParserMode));
1243}
1244
1245PassRef<MutableStyleProperties> MutableStyleProperties::create(const CSSProperty* properties, unsigned count)
1246{
1247    return adoptRef(*new MutableStyleProperties(properties, count));
1248}
1249
1250String StyleProperties::PropertyReference::cssName() const
1251{
1252    return getPropertyNameString(id());
1253}
1254
1255String StyleProperties::PropertyReference::cssText() const
1256{
1257    StringBuilder result;
1258    result.append(cssName());
1259    result.appendLiteral(": ");
1260    result.append(propertyValue()->cssText());
1261    if (isImportant())
1262        result.appendLiteral(" !important");
1263    result.append(';');
1264    return result.toString();
1265}
1266
1267
1268} // namespace WebCore
1269