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