79ab04337cae66f1580b76417883ccc643ccf411
[WebKit-https.git] / Source / WebCore / css / StylePropertySet.cpp
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 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 "CSSParser.h"
26 #include "CSSValueKeywords.h"
27 #include "CSSValueList.h"
28 #include "CSSValuePool.h"
29 #include "Document.h"
30 #include "PropertySetCSSStyleDeclaration.h"
31 #include "StylePropertyShorthand.h"
32 #include "StyleSheetContents.h"
33 #include <wtf/BitArray.h>
34 #include <wtf/text/StringBuilder.h>
35
36 #ifndef NDEBUG
37 #include <stdio.h>
38 #include <wtf/ASCIICType.h>
39 #include <wtf/text/CString.h>
40 #endif
41
42 using namespace std;
43
44 namespace WebCore {
45
46 typedef HashMap<const StylePropertySet*, OwnPtr<PropertySetCSSStyleDeclaration> > PropertySetCSSOMWrapperMap;
47 static PropertySetCSSOMWrapperMap& propertySetCSSOMWrapperMap()
48 {
49     DEFINE_STATIC_LOCAL(PropertySetCSSOMWrapperMap, propertySetCSSOMWrapperMapInstance, ());
50     return propertySetCSSOMWrapperMapInstance;
51 }
52
53 PassRefPtr<StylePropertySet> StylePropertySet::createImmutable(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode)
54 {
55     void* slot = WTF::fastMalloc(sizeof(StylePropertySet) - sizeof(void*) + sizeof(CSSProperty) * count);
56     return adoptRef(new (slot) StylePropertySet(properties, count, cssParserMode, /* makeMutable */ false));
57 }
58
59 StylePropertySet::StylePropertySet(CSSParserMode cssParserMode)
60     : m_cssParserMode(cssParserMode)
61     , m_ownsCSSOMWrapper(false)
62     , m_isMutable(true)
63     , m_arraySize(0)
64     , m_mutablePropertyVector(new Vector<CSSProperty>)
65 {
66 }
67
68 StylePropertySet::StylePropertySet(const CSSProperty* properties, unsigned count, CSSParserMode cssParserMode, bool makeMutable)
69     : m_cssParserMode(cssParserMode)
70     , m_ownsCSSOMWrapper(false)
71     , m_isMutable(makeMutable)
72 {
73     if (makeMutable) {
74         m_mutablePropertyVector = new Vector<CSSProperty>;
75         m_mutablePropertyVector->reserveInitialCapacity(count);
76         for (unsigned i = 0; i < count; ++i)
77             m_mutablePropertyVector->uncheckedAppend(properties[i]);
78     } else {
79         m_arraySize = count;
80         for (unsigned i = 0; i < m_arraySize; ++i)
81             new (&array()[i]) CSSProperty(properties[i]);
82     }
83 }
84
85 StylePropertySet::StylePropertySet(const StylePropertySet& o)
86     : RefCounted<StylePropertySet>()
87     , m_cssParserMode(o.m_cssParserMode)
88     , m_ownsCSSOMWrapper(false)
89     , m_isMutable(true)
90     , m_arraySize(0)
91     , m_mutablePropertyVector(new Vector<CSSProperty>)
92 {
93     copyPropertiesFrom(o);
94 }
95
96 StylePropertySet::~StylePropertySet()
97 {
98     ASSERT(!m_ownsCSSOMWrapper || propertySetCSSOMWrapperMap().contains(this));
99     if (m_ownsCSSOMWrapper)
100         propertySetCSSOMWrapperMap().remove(this);
101     if (isMutable())
102         delete m_mutablePropertyVector;
103     else {
104         for (unsigned i = 0; i < m_arraySize; ++i)
105             array()[i].~CSSProperty();
106     }
107 }
108
109 void StylePropertySet::setCSSParserMode(CSSParserMode cssParserMode)
110 {
111     ASSERT(isMutable());
112     m_cssParserMode = cssParserMode;
113 }
114
115 void StylePropertySet::copyPropertiesFrom(const StylePropertySet& other)
116 {
117     ASSERT(isMutable());
118
119     if (other.isMutable()) {
120         *m_mutablePropertyVector = *other.m_mutablePropertyVector;
121         return;
122     }
123
124     ASSERT(m_mutablePropertyVector->isEmpty());
125     m_mutablePropertyVector->reserveInitialCapacity(other.m_arraySize);
126     for (unsigned i = 0; i < other.m_arraySize; ++i)
127         m_mutablePropertyVector->uncheckedAppend(other.array()[i]);
128 }
129
130 String 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 #if ENABLE(CSS3_FLEXBOX)
165     case CSSPropertyWebkitFlex:
166         return getShorthandValue(webkitFlexShorthand());
167     case CSSPropertyWebkitFlexFlow:
168         return getShorthandValue(webkitFlexFlowShorthand());
169 #endif
170     case CSSPropertyFont:
171         return fontValue();
172     case CSSPropertyMargin:
173         return get4Values(marginShorthand());
174     case CSSPropertyOverflow:
175         return getCommonValue(overflowShorthand());
176     case CSSPropertyPadding:
177         return get4Values(paddingShorthand());
178     case CSSPropertyListStyle:
179         return getShorthandValue(listStyleShorthand());
180     case CSSPropertyWebkitMaskPosition:
181         return getLayeredShorthandValue(webkitMaskPositionShorthand());
182     case CSSPropertyWebkitMaskRepeat:
183         return getLayeredShorthandValue(webkitMaskRepeatShorthand());
184     case CSSPropertyWebkitMask:
185         return getLayeredShorthandValue(webkitMaskShorthand());
186     case CSSPropertyWebkitTransformOrigin:
187         return getShorthandValue(webkitTransformOriginShorthand());
188     case CSSPropertyWebkitTransition:
189         return getLayeredShorthandValue(webkitTransitionShorthand());
190     case CSSPropertyWebkitAnimation:
191         return getLayeredShorthandValue(webkitAnimationShorthand());
192 #if ENABLE(CSS_EXCLUSIONS)
193     case CSSPropertyWebkitWrap:
194         return getShorthandValue(webkitWrapShorthand());
195 #endif
196 #if ENABLE(SVG)
197     case CSSPropertyMarker: {
198         RefPtr<CSSValue> value = getPropertyCSSValue(CSSPropertyMarkerStart);
199         if (value)
200             return value->cssText();
201         return String();
202     }
203 #endif
204     case CSSPropertyBorderRadius:
205         return get4Values(borderRadiusShorthand());
206     default:
207           return String();
208     }
209 }
210
211 String StylePropertySet::borderSpacingValue(const StylePropertyShorthand& shorthand) const
212 {
213     RefPtr<CSSValue> horizontalValue = getPropertyCSSValue(shorthand.properties()[0]);
214     RefPtr<CSSValue> verticalValue = getPropertyCSSValue(shorthand.properties()[1]);
215
216     // While standard border-spacing property does not allow specifying border-spacing-vertical without
217     // specifying border-spacing-horizontal <http://www.w3.org/TR/CSS21/tables.html#separated-borders>,
218     // -webkit-border-spacing-vertical can be set without -webkit-border-spacing-horizontal.
219     if (!horizontalValue || !verticalValue)
220         return String();
221
222     String horizontalValueCSSText = horizontalValue->cssText();
223     String verticalValueCSSText = verticalValue->cssText();
224     if (horizontalValueCSSText == verticalValueCSSText)
225         return horizontalValueCSSText;
226     return horizontalValueCSSText + ' ' + verticalValueCSSText;
227 }
228
229 bool StylePropertySet::appendFontLonghandValueIfExplicit(CSSPropertyID propertyId, StringBuilder& result) const
230 {
231     const CSSProperty* property = findPropertyWithId(propertyId);
232     if (!property)
233         return false; // All longhands must have at least implicit values if "font" is specified.
234     if (property->isImplicit())
235         return true;
236
237     char prefix = '\0';
238     switch (propertyId) {
239     case CSSPropertyFontStyle:
240         break; // No prefix.
241     case CSSPropertyFontFamily:
242     case CSSPropertyFontVariant:
243     case CSSPropertyFontWeight:
244         prefix = ' ';
245         break;
246     case CSSPropertyLineHeight:
247         prefix = '/';
248         break;
249     default:
250         ASSERT_NOT_REACHED();
251     }
252
253     if (prefix && !result.isEmpty())
254         result.append(prefix);
255     result.append(property->value()->cssText());
256
257     return true;
258 }
259
260 String StylePropertySet::fontValue() const
261 {
262     const CSSProperty* fontSizeProperty = findPropertyWithId(CSSPropertyFontSize);
263     if (!fontSizeProperty || fontSizeProperty->isImplicit())
264         return emptyString();
265
266     StringBuilder result;
267     bool success = true;
268     success &= appendFontLonghandValueIfExplicit(CSSPropertyFontStyle, result);
269     success &= appendFontLonghandValueIfExplicit(CSSPropertyFontVariant, result);
270     success &= appendFontLonghandValueIfExplicit(CSSPropertyFontWeight, result);
271     if (!result.isEmpty())
272         result.append(' ');
273     result.append(fontSizeProperty->value()->cssText());
274     success &= appendFontLonghandValueIfExplicit(CSSPropertyLineHeight, result);
275     success &= appendFontLonghandValueIfExplicit(CSSPropertyFontFamily, result);
276     if (!success) {
277         // An invalid "font" value has been built (should never happen, as at least implicit values
278         // for mandatory longhands are always found in the style), report empty value instead.
279         ASSERT_NOT_REACHED();
280         return emptyString();
281     }
282     return result.toString();
283 }
284
285 String StylePropertySet::get4Values(const StylePropertyShorthand& shorthand) const
286 {
287     // Assume the properties are in the usual order top, right, bottom, left.
288     const CSSProperty* top = findPropertyWithId(shorthand.properties()[0]);
289     const CSSProperty* right = findPropertyWithId(shorthand.properties()[1]);
290     const CSSProperty* bottom = findPropertyWithId(shorthand.properties()[2]);
291     const CSSProperty* left = findPropertyWithId(shorthand.properties()[3]);
292
293     // All 4 properties must be specified.
294     if (!top || !top->value() || !right || !right->value() || !bottom || !bottom->value() || !left || !left->value())
295         return String();
296     if (top->value()->isInitialValue() || right->value()->isInitialValue() || bottom->value()->isInitialValue() || left->value()->isInitialValue())
297         return String();
298     if (top->isImportant() != right->isImportant() || right->isImportant() != bottom->isImportant() || bottom->isImportant() != left->isImportant())
299         return String();
300
301     bool showLeft = right->value()->cssText() != left->value()->cssText();
302     bool showBottom = (top->value()->cssText() != bottom->value()->cssText()) || showLeft;
303     bool showRight = (top->value()->cssText() != right->value()->cssText()) || showBottom;
304
305     String res = top->value()->cssText();
306     if (showRight)
307         res += " " + right->value()->cssText();
308     if (showBottom)
309         res += " " + bottom->value()->cssText();
310     if (showLeft)
311         res += " " + left->value()->cssText();
312
313     return res;
314 }
315
316 String StylePropertySet::getLayeredShorthandValue(const StylePropertyShorthand& shorthand) const
317 {
318     String res;
319
320     const unsigned size = shorthand.length();
321     // Begin by collecting the properties into an array.
322     Vector< RefPtr<CSSValue> > values(size);
323     size_t numLayers = 0;
324
325     for (unsigned i = 0; i < size; ++i) {
326         values[i] = getPropertyCSSValue(shorthand.properties()[i]);
327         if (values[i]) {
328             if (values[i]->isValueList()) {
329                 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
330                 numLayers = max(valueList->length(), numLayers);
331             } else
332                 numLayers = max<size_t>(1U, numLayers);
333         }
334     }
335
336     // Now stitch the properties together.  Implicit initial values are flagged as such and
337     // can safely be omitted.
338     for (size_t i = 0; i < numLayers; i++) {
339         String layerRes;
340         bool useRepeatXShorthand = false;
341         bool useRepeatYShorthand = false;
342         bool useSingleWordShorthand = false;
343         bool foundBackgroundPositionYCSSProperty = false;
344         for (unsigned j = 0; j < size; j++) {
345             RefPtr<CSSValue> value;
346             if (values[j]) {
347                 if (values[j]->isValueList())
348                     value = static_cast<CSSValueList*>(values[j].get())->item(i);
349                 else {
350                     value = values[j];
351
352                     // Color only belongs in the last layer.
353                     if (shorthand.properties()[j] == CSSPropertyBackgroundColor) {
354                         if (i != numLayers - 1)
355                             value = 0;
356                     } else if (i != 0) // Other singletons only belong in the first layer.
357                         value = 0;
358                 }
359             }
360
361             // We need to report background-repeat as it was written in the CSS. If the property is implicit,
362             // then it was written with only one value. Here we figure out which value that was so we can
363             // report back correctly.
364             if (shorthand.properties()[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(shorthand.properties()[j])) {
365
366                 // BUG 49055: make sure the value was not reset in the layer check just above.
367                 if (j < size - 1 && shorthand.properties()[j + 1] == CSSPropertyBackgroundRepeatY && value) {
368                     RefPtr<CSSValue> yValue;
369                     RefPtr<CSSValue> nextValue = values[j + 1];
370                     if (nextValue->isValueList())
371                         yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
372                     else
373                         yValue = nextValue;
374
375                     int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
376                     int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
377                     if (xId != yId) {
378                         if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
379                             useRepeatXShorthand = true;
380                             ++j;
381                         } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
382                             useRepeatYShorthand = true;
383                             continue;
384                         }
385                     } else {
386                         useSingleWordShorthand = true;
387                         ++j;
388                     }
389                 }
390             }
391
392             if (value && !value->isImplicitInitialValue()) {
393                 if (!layerRes.isNull())
394                     layerRes += " ";
395                 if (foundBackgroundPositionYCSSProperty && shorthand.properties()[j] == CSSPropertyBackgroundSize) 
396                     layerRes += "/ ";
397                 if (!foundBackgroundPositionYCSSProperty && shorthand.properties()[j] == CSSPropertyBackgroundSize) 
398                     continue;
399
400                 if (useRepeatXShorthand) {
401                     useRepeatXShorthand = false;
402                     layerRes += getValueName(CSSValueRepeatX);
403                 } else if (useRepeatYShorthand) {
404                     useRepeatYShorthand = false;
405                     layerRes += getValueName(CSSValueRepeatY);
406                 } else if (useSingleWordShorthand) {
407                     useSingleWordShorthand = false;
408                     layerRes += value->cssText();
409                 } else
410                     layerRes += value->cssText();
411
412                 if (shorthand.properties()[j] == CSSPropertyBackgroundPositionY)
413                     foundBackgroundPositionYCSSProperty = true;
414             }
415         }
416
417         if (!layerRes.isNull()) {
418             if (!res.isNull())
419                 res += ", ";
420             res += layerRes;
421         }
422     }
423     return res;
424 }
425
426 String StylePropertySet::getShorthandValue(const StylePropertyShorthand& shorthand) const
427 {
428     String res;
429     for (unsigned i = 0; i < shorthand.length(); ++i) {
430         if (!isPropertyImplicit(shorthand.properties()[i])) {
431             RefPtr<CSSValue> value = getPropertyCSSValue(shorthand.properties()[i]);
432             if (!value)
433                 return String();
434             if (value->isInitialValue())
435                 continue;
436             if (!res.isNull())
437                 res += " ";
438             res += value->cssText();
439         }
440     }
441     return res;
442 }
443
444 // only returns a non-null value if all properties have the same, non-null value
445 String StylePropertySet::getCommonValue(const StylePropertyShorthand& shorthand) const
446 {
447     String res;
448     bool lastPropertyWasImportant = false;
449     for (unsigned i = 0; i < shorthand.length(); ++i) {
450         RefPtr<CSSValue> value = getPropertyCSSValue(shorthand.properties()[i]);
451         // FIXME: CSSInitialValue::cssText should generate the right value.
452         if (!value)
453             return String();
454         String text = value->cssText();
455         if (text.isNull())
456             return String();
457         if (res.isNull())
458             res = text;
459         else if (res != text)
460             return String();
461
462         bool currentPropertyIsImportant = propertyIsImportant(shorthand.properties()[i]);
463         if (i && lastPropertyWasImportant != currentPropertyIsImportant)
464             return String();
465         lastPropertyWasImportant = currentPropertyIsImportant;
466     }
467     return res;
468 }
469
470 String StylePropertySet::borderPropertyValue(CommonValueMode valueMode) const
471 {
472     const StylePropertyShorthand properties[3] = { borderWidthShorthand(), borderStyleShorthand(), borderColorShorthand() };
473     StringBuilder result;
474     for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
475         String value = getCommonValue(properties[i]);
476         if (value.isNull()) {
477             if (valueMode == ReturnNullOnUncommonValues)
478                 return String();
479             ASSERT(valueMode == OmitUncommonValues);
480             continue;
481         }
482         if (value == "initial")
483             continue;
484         if (!result.isEmpty())
485             result.append(' ');
486         result.append(value);
487     }
488     return result.isEmpty() ? String() : result.toString();
489 }
490
491 PassRefPtr<CSSValue> StylePropertySet::getPropertyCSSValue(CSSPropertyID propertyID) const
492 {
493     const CSSProperty* property = findPropertyWithId(propertyID);
494     return property ? property->value() : 0;
495 }
496
497 bool StylePropertySet::removeShorthandProperty(CSSPropertyID propertyID)
498 {
499     ASSERT(isMutable());
500     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
501     if (!shorthand.length())
502         return false;
503     return removePropertiesInSet(shorthand.properties(), shorthand.length());
504 }
505
506 bool StylePropertySet::removeProperty(CSSPropertyID propertyID, String* returnText)
507 {
508     ASSERT(isMutable());
509     if (removeShorthandProperty(propertyID)) {
510         // FIXME: Return an equivalent shorthand when possible.
511         if (returnText)
512             *returnText = "";
513         return true;
514     }
515
516     CSSProperty* foundProperty = findPropertyWithId(propertyID);
517     if (!foundProperty) {
518         if (returnText)
519             *returnText = "";
520         return false;
521     }
522
523     if (returnText)
524         *returnText = foundProperty->value()->cssText();
525
526     // A more efficient removal strategy would involve marking entries as empty
527     // and sweeping them when the vector grows too big.
528     m_mutablePropertyVector->remove(foundProperty - m_mutablePropertyVector->data());
529     
530     return true;
531 }
532
533 bool StylePropertySet::propertyIsImportant(CSSPropertyID propertyID) const
534 {
535     const CSSProperty* property = findPropertyWithId(propertyID);
536     if (property)
537         return property->isImportant();
538
539     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
540     if (!shorthand.length())
541         return false;
542
543     for (unsigned i = 0; i < shorthand.length(); ++i) {
544         if (!propertyIsImportant(shorthand.properties()[i]))
545             return false;
546     }
547     return true;
548 }
549
550 CSSPropertyID StylePropertySet::getPropertyShorthand(CSSPropertyID propertyID) const
551 {
552     const CSSProperty* property = findPropertyWithId(propertyID);
553     return property ? property->shorthandID() : CSSPropertyInvalid;
554 }
555
556 bool StylePropertySet::isPropertyImplicit(CSSPropertyID propertyID) const
557 {
558     const CSSProperty* property = findPropertyWithId(propertyID);
559     return property ? property->isImplicit() : false;
560 }
561
562 bool StylePropertySet::setProperty(CSSPropertyID propertyID, const String& value, bool important, StyleSheetContents* contextStyleSheet)
563 {
564     ASSERT(isMutable());
565     // Setting the value to an empty string just removes the property in both IE and Gecko.
566     // Setting it to null seems to produce less consistent results, but we treat it just the same.
567     if (value.isEmpty()) {
568         removeProperty(propertyID);
569         return true;
570     }
571
572     // When replacing an existing property value, this moves the property to the end of the list.
573     // Firefox preserves the position, and MSIE moves the property to the beginning.
574     return CSSParser::parseValue(this, propertyID, value, important, cssParserMode(), contextStyleSheet);
575 }
576
577 void StylePropertySet::setProperty(CSSPropertyID propertyID, PassRefPtr<CSSValue> prpValue, bool important)
578 {
579     ASSERT(isMutable());
580     StylePropertyShorthand shorthand = shorthandForProperty(propertyID);
581     if (!shorthand.length()) {
582         setProperty(CSSProperty(propertyID, prpValue, important));
583         return;
584     }
585
586     removePropertiesInSet(shorthand.properties(), shorthand.length());
587
588     RefPtr<CSSValue> value = prpValue;
589     for (unsigned i = 0; i < shorthand.length(); ++i)
590         append(CSSProperty(shorthand.properties()[i], value, important));
591 }
592
593 void StylePropertySet::setProperty(const CSSProperty& property, CSSProperty* slot)
594 {
595     ASSERT(isMutable());
596     if (!removeShorthandProperty(property.id())) {
597         CSSProperty* toReplace = slot ? slot : findPropertyWithId(property.id());
598         if (toReplace) {
599             *toReplace = property;
600             return;
601         }
602     }
603     append(property);
604 }
605
606 bool StylePropertySet::setProperty(CSSPropertyID propertyID, int identifier, bool important)
607 {
608     ASSERT(isMutable());
609     setProperty(CSSProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important));
610     return true;
611 }
612
613 void StylePropertySet::parseDeclaration(const String& styleDeclaration, StyleSheetContents* contextStyleSheet)
614 {
615     ASSERT(isMutable());
616
617     m_mutablePropertyVector->clear();
618
619     CSSParserContext context(cssParserMode());
620     if (contextStyleSheet) {
621         context = contextStyleSheet->parserContext();
622         context.mode = cssParserMode();
623     }
624     CSSParser parser(context);
625     parser.parseDeclaration(this, styleDeclaration, 0, contextStyleSheet);
626 }
627
628 void StylePropertySet::addParsedProperties(const Vector<CSSProperty>& properties)
629 {
630     ASSERT(isMutable());
631     m_mutablePropertyVector->reserveCapacity(m_mutablePropertyVector->size() + properties.size());
632     for (unsigned i = 0; i < properties.size(); ++i)
633         addParsedProperty(properties[i]);
634 }
635
636 void StylePropertySet::addParsedProperty(const CSSProperty& property)
637 {
638     ASSERT(isMutable());
639     // Only add properties that have no !important counterpart present
640     if (!propertyIsImportant(property.id()) || property.isImportant())
641         setProperty(property);
642 }
643
644 String StylePropertySet::asText() const
645 {
646     StringBuilder result;
647
648     const CSSProperty* positionXProp = 0;
649     const CSSProperty* positionYProp = 0;
650     const CSSProperty* repeatXProp = 0;
651     const CSSProperty* repeatYProp = 0;
652
653     BitArray<numCSSProperties> shorthandPropertyUsed;
654     BitArray<numCSSProperties> shorthandPropertyAppeared;
655
656     unsigned size = propertyCount();
657     for (unsigned n = 0; n < size; ++n) {
658         const CSSProperty& prop = propertyAt(n);
659         CSSPropertyID propertyID = prop.id();
660         CSSPropertyID shorthandPropertyID = CSSPropertyInvalid;
661         CSSPropertyID borderFallbackShorthandProperty = CSSPropertyInvalid;
662         String value;
663
664         switch (propertyID) {
665 #if ENABLE(CSS_VARIABLES)
666         case CSSPropertyVariable:
667             result.append(prop.cssText());
668             continue;
669 #endif
670         case CSSPropertyBackgroundPositionX:
671             positionXProp = &prop;
672             continue;
673         case CSSPropertyBackgroundPositionY:
674             positionYProp = &prop;
675             continue;
676         case CSSPropertyBackgroundRepeatX:
677             repeatXProp = &prop;
678             continue;
679         case CSSPropertyBackgroundRepeatY:
680             repeatYProp = &prop;
681             continue;
682         case CSSPropertyBorderTopWidth:
683         case CSSPropertyBorderRightWidth:
684         case CSSPropertyBorderBottomWidth:
685         case CSSPropertyBorderLeftWidth:
686             if (!borderFallbackShorthandProperty)
687                 borderFallbackShorthandProperty = CSSPropertyBorderWidth;
688         case CSSPropertyBorderTopStyle:
689         case CSSPropertyBorderRightStyle:
690         case CSSPropertyBorderBottomStyle:
691         case CSSPropertyBorderLeftStyle:
692             if (!borderFallbackShorthandProperty)
693                 borderFallbackShorthandProperty = CSSPropertyBorderStyle;
694         case CSSPropertyBorderTopColor:
695         case CSSPropertyBorderRightColor:
696         case CSSPropertyBorderBottomColor:
697         case CSSPropertyBorderLeftColor:
698             if (!borderFallbackShorthandProperty)
699                 borderFallbackShorthandProperty = CSSPropertyBorderColor;
700
701             // FIXME: Deal with cases where only some of border-(top|right|bottom|left) are specified.
702             if (!shorthandPropertyAppeared.get(CSSPropertyBorder - firstCSSProperty)) {
703                 value = borderPropertyValue(ReturnNullOnUncommonValues);
704                 if (value.isNull())
705                     shorthandPropertyAppeared.set(CSSPropertyBorder - firstCSSProperty);
706                 else
707                     shorthandPropertyID = CSSPropertyBorder;
708             } else if (shorthandPropertyUsed.get(CSSPropertyBorder - firstCSSProperty))
709                 shorthandPropertyID = CSSPropertyBorder;
710             if (!shorthandPropertyID)
711                 shorthandPropertyID = borderFallbackShorthandProperty;
712             break;
713         case CSSPropertyWebkitBorderHorizontalSpacing:
714         case CSSPropertyWebkitBorderVerticalSpacing:
715             shorthandPropertyID = CSSPropertyBorderSpacing;
716             break;
717         case CSSPropertyFontFamily:
718         case CSSPropertyLineHeight:
719         case CSSPropertyFontSize:
720         case CSSPropertyFontStyle:
721         case CSSPropertyFontVariant:
722         case CSSPropertyFontWeight:
723             // Don't use CSSPropertyFont because old UAs can't recognize them but are important for editing.
724             break;
725         case CSSPropertyListStyleType:
726         case CSSPropertyListStylePosition:
727         case CSSPropertyListStyleImage:
728             shorthandPropertyID = CSSPropertyListStyle;
729             break;
730         case CSSPropertyMarginTop:
731         case CSSPropertyMarginRight:
732         case CSSPropertyMarginBottom:
733         case CSSPropertyMarginLeft:
734             shorthandPropertyID = CSSPropertyMargin;
735             break;
736         case CSSPropertyOutlineWidth:
737         case CSSPropertyOutlineStyle:
738         case CSSPropertyOutlineColor:
739             shorthandPropertyID = CSSPropertyOutline;
740             break;
741         case CSSPropertyOverflowX:
742         case CSSPropertyOverflowY:
743             shorthandPropertyID = CSSPropertyOverflow;
744             break;
745         case CSSPropertyPaddingTop:
746         case CSSPropertyPaddingRight:
747         case CSSPropertyPaddingBottom:
748         case CSSPropertyPaddingLeft:
749             shorthandPropertyID = CSSPropertyPadding;
750             break;
751         case CSSPropertyWebkitAnimationName:
752         case CSSPropertyWebkitAnimationDuration:
753         case CSSPropertyWebkitAnimationTimingFunction:
754         case CSSPropertyWebkitAnimationDelay:
755         case CSSPropertyWebkitAnimationIterationCount:
756         case CSSPropertyWebkitAnimationDirection:
757         case CSSPropertyWebkitAnimationFillMode:
758             shorthandPropertyID = CSSPropertyWebkitAnimation;
759             break;
760 #if ENABLE(CSS3_FLEXBOX)
761         case CSSPropertyWebkitFlexDirection:
762         case CSSPropertyWebkitFlexWrap:
763             shorthandPropertyID = CSSPropertyWebkitFlexFlow;
764             break;
765         case CSSPropertyWebkitFlexBasis:
766         case CSSPropertyWebkitFlexGrow:
767         case CSSPropertyWebkitFlexShrink:
768             shorthandPropertyID = CSSPropertyWebkitFlex;
769             break;
770 #endif
771         case CSSPropertyWebkitMaskPositionX:
772         case CSSPropertyWebkitMaskPositionY:
773         case CSSPropertyWebkitMaskRepeatX:
774         case CSSPropertyWebkitMaskRepeatY:
775         case CSSPropertyWebkitMaskImage:
776         case CSSPropertyWebkitMaskRepeat:
777         case CSSPropertyWebkitMaskAttachment:
778         case CSSPropertyWebkitMaskPosition:
779         case CSSPropertyWebkitMaskClip:
780         case CSSPropertyWebkitMaskOrigin:
781             shorthandPropertyID = CSSPropertyWebkitMask;
782             break;
783         case CSSPropertyWebkitTransformOriginX:
784         case CSSPropertyWebkitTransformOriginY:
785         case CSSPropertyWebkitTransformOriginZ:
786             shorthandPropertyID = CSSPropertyWebkitTransformOrigin;
787             break;
788         case CSSPropertyWebkitTransitionProperty:
789         case CSSPropertyWebkitTransitionDuration:
790         case CSSPropertyWebkitTransitionTimingFunction:
791         case CSSPropertyWebkitTransitionDelay:
792             shorthandPropertyID = CSSPropertyWebkitTransition;
793             break;
794 #if ENABLE(CSS_EXCLUSIONS)
795         case CSSPropertyWebkitWrapFlow:
796         case CSSPropertyWebkitWrapMargin:
797         case CSSPropertyWebkitWrapPadding:
798             shorthandPropertyID = CSSPropertyWebkitWrap;
799             break;
800 #endif
801         default:
802             break;
803         }
804
805         unsigned shortPropertyIndex = shorthandPropertyID - firstCSSProperty;
806         if (shorthandPropertyID) {
807             if (shorthandPropertyUsed.get(shortPropertyIndex))
808                 continue;
809             if (!shorthandPropertyAppeared.get(shortPropertyIndex) && value.isNull())
810                 value = getPropertyValue(shorthandPropertyID);
811             shorthandPropertyAppeared.set(shortPropertyIndex);
812         }
813
814         if (!value.isNull()) {
815             propertyID = shorthandPropertyID;
816             shorthandPropertyUsed.set(shortPropertyIndex);
817         } else
818             value = prop.value()->cssText();
819
820         if (value == "initial" && !CSSProperty::isInheritedProperty(propertyID))
821             continue;
822
823         result.append(getPropertyName(propertyID));
824         result.append(": ");
825         result.append(value);
826         result.append(prop.isImportant() ? " !important" : "");
827         result.append("; ");
828     }
829
830     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
831     // It is required because background-position-x/y are non-standard properties and WebKit generated output
832     // would not work in Firefox (<rdar://problem/5143183>)
833     // It would be a better solution if background-position was CSS_PAIR.
834     if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) {
835         result.append("background-position: ");
836         if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList())
837             result.append(getLayeredShorthandValue(backgroundPositionShorthand()));
838         else {
839             result.append(positionXProp->value()->cssText());
840             result.append(" ");
841             result.append(positionYProp->value()->cssText());
842         }
843         if (positionXProp->isImportant())
844             result.append(" !important");
845         result.append("; ");
846     } else {
847         if (positionXProp)
848             result.append(positionXProp->cssText());
849         if (positionYProp)
850             result.append(positionYProp->cssText());
851     }
852
853     // FIXME: We need to do the same for background-repeat.
854     if (repeatXProp && repeatYProp && repeatXProp->isImportant() == repeatYProp->isImportant()) {
855         result.append("background-repeat: ");
856         if (repeatXProp->value()->isValueList() || repeatYProp->value()->isValueList())
857             result.append(getLayeredShorthandValue(backgroundRepeatShorthand()));
858         else {
859             result.append(repeatXProp->value()->cssText());
860             result.append(" ");
861             result.append(repeatYProp->value()->cssText());
862         }
863         if (repeatXProp->isImportant())
864             result.append(" !important");
865         result.append("; ");
866     } else {
867         if (repeatXProp)
868             result.append(repeatXProp->cssText());
869         if (repeatYProp)
870             result.append(repeatYProp->cssText());
871     }
872
873     return result.toString();
874 }
875
876 void StylePropertySet::mergeAndOverrideOnConflict(const StylePropertySet* other)
877 {
878     ASSERT(isMutable());
879     unsigned size = other->propertyCount();
880     for (unsigned n = 0; n < size; ++n) {
881         const CSSProperty& toMerge = other->propertyAt(n);
882         CSSProperty* old = findPropertyWithId(toMerge.id());
883         if (old)
884             setProperty(toMerge, old);
885         else
886             append(toMerge);
887     }
888 }
889
890 void StylePropertySet::addSubresourceStyleURLs(ListHashSet<KURL>& urls, StyleSheetContents* contextStyleSheet) const
891 {
892     unsigned size = propertyCount();
893     for (unsigned i = 0; i < size; ++i)
894         propertyAt(i).value()->addSubresourceStyleURLs(urls, contextStyleSheet);
895 }
896
897 // This is the list of properties we want to copy in the copyBlockProperties() function.
898 // It is the list of CSS properties that apply specially to block-level elements.
899 static const CSSPropertyID blockProperties[] = {
900     CSSPropertyOrphans,
901     CSSPropertyOverflow, // This can be also be applied to replaced elements
902     CSSPropertyWebkitAspectRatio,
903     CSSPropertyWebkitColumnCount,
904     CSSPropertyWebkitColumnGap,
905     CSSPropertyWebkitColumnRuleColor,
906     CSSPropertyWebkitColumnRuleStyle,
907     CSSPropertyWebkitColumnRuleWidth,
908     CSSPropertyWebkitColumnBreakBefore,
909     CSSPropertyWebkitColumnBreakAfter,
910     CSSPropertyWebkitColumnBreakInside,
911     CSSPropertyWebkitColumnWidth,
912     CSSPropertyPageBreakAfter,
913     CSSPropertyPageBreakBefore,
914     CSSPropertyPageBreakInside,
915 #if ENABLE(CSS_REGIONS)
916     CSSPropertyWebkitRegionBreakAfter,
917     CSSPropertyWebkitRegionBreakBefore,
918     CSSPropertyWebkitRegionBreakInside,
919 #endif
920     CSSPropertyTextAlign,
921     CSSPropertyTextIndent,
922     CSSPropertyWidows
923 };
924
925 const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
926
927 PassRefPtr<StylePropertySet> StylePropertySet::copyBlockProperties() const
928 {
929     return copyPropertiesInSet(blockProperties, numBlockProperties);
930 }
931
932 void StylePropertySet::removeBlockProperties()
933 {
934     removePropertiesInSet(blockProperties, numBlockProperties);
935 }
936
937 bool StylePropertySet::removePropertiesInSet(const CSSPropertyID* set, unsigned length)
938 {
939     ASSERT(isMutable());
940     if (m_mutablePropertyVector->isEmpty())
941         return false;
942
943     // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
944     HashSet<CSSPropertyID> toRemove;
945     for (unsigned i = 0; i < length; ++i)
946         toRemove.add(set[i]);
947
948     Vector<CSSProperty> newProperties;
949     newProperties.reserveInitialCapacity(m_mutablePropertyVector->size());
950
951     unsigned size = m_mutablePropertyVector->size();
952     for (unsigned n = 0; n < size; ++n) {
953         const CSSProperty& property = m_mutablePropertyVector->at(n);
954         // Not quite sure if the isImportant test is needed but it matches the existing behavior.
955         if (!property.isImportant()) {
956             if (toRemove.contains(property.id()))
957                 continue;
958         }
959         newProperties.append(property);
960     }
961
962     bool changed = newProperties.size() != m_mutablePropertyVector->size();
963     *m_mutablePropertyVector = newProperties;
964     return changed;
965 }
966
967 const CSSProperty* StylePropertySet::findPropertyWithId(CSSPropertyID propertyID) const
968 {
969     for (int n = propertyCount() - 1 ; n >= 0; --n) {
970         if (propertyID == propertyAt(n).id())
971             return &propertyAt(n);
972     }
973     return 0;
974 }
975
976 CSSProperty* StylePropertySet::findPropertyWithId(CSSPropertyID propertyID)
977 {
978     ASSERT(isMutable());
979     for (int n = propertyCount() - 1 ; n >= 0; --n) {
980         if (propertyID == propertyAt(n).id())
981             return &propertyAt(n);
982     }
983     return 0;
984 }
985     
986 bool StylePropertySet::propertyMatches(const CSSProperty* property) const
987 {
988     RefPtr<CSSValue> value = getPropertyCSSValue(property->id());
989     return value && value->cssText() == property->value()->cssText();
990 }
991     
992 void StylePropertySet::removeEquivalentProperties(const StylePropertySet* style)
993 {
994     ASSERT(isMutable());
995     Vector<CSSPropertyID> propertiesToRemove;
996     unsigned size = m_mutablePropertyVector->size();
997     for (unsigned i = 0; i < size; ++i) {
998         const CSSProperty& property = m_mutablePropertyVector->at(i);
999         if (style->propertyMatches(&property))
1000             propertiesToRemove.append(property.id());
1001     }    
1002     // FIXME: This should use mass removal.
1003     for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
1004         removeProperty(propertiesToRemove[i]);
1005 }
1006
1007 void StylePropertySet::removeEquivalentProperties(const CSSStyleDeclaration* style)
1008 {
1009     ASSERT(isMutable());
1010     Vector<CSSPropertyID> propertiesToRemove;
1011     unsigned size = m_mutablePropertyVector->size();
1012     for (unsigned i = 0; i < size; ++i) {
1013         const CSSProperty& property = m_mutablePropertyVector->at(i);
1014         if (style->cssPropertyMatches(&property))
1015             propertiesToRemove.append(property.id());
1016     }    
1017     // FIXME: This should use mass removal.
1018     for (unsigned i = 0; i < propertiesToRemove.size(); ++i)
1019         removeProperty(propertiesToRemove[i]);
1020 }
1021
1022 PassRefPtr<StylePropertySet> StylePropertySet::copy() const
1023 {
1024     return adoptRef(new StylePropertySet(*this));
1025 }
1026
1027 PassRefPtr<StylePropertySet> StylePropertySet::copyPropertiesInSet(const CSSPropertyID* set, unsigned length) const
1028 {
1029     Vector<CSSProperty, 256> list;
1030     list.reserveInitialCapacity(length);
1031     for (unsigned i = 0; i < length; ++i) {
1032         RefPtr<CSSValue> value = getPropertyCSSValue(set[i]);
1033         if (value)
1034             list.append(CSSProperty(set[i], value.release(), false));
1035     }
1036     return StylePropertySet::create(list.data(), list.size());
1037 }
1038
1039 CSSStyleDeclaration* StylePropertySet::ensureCSSStyleDeclaration() const
1040 {
1041     if (m_ownsCSSOMWrapper) {
1042         ASSERT(!static_cast<CSSStyleDeclaration*>(propertySetCSSOMWrapperMap().get(this))->parentRule());
1043         ASSERT(!propertySetCSSOMWrapperMap().get(this)->parentElement());
1044         return propertySetCSSOMWrapperMap().get(this);
1045     }
1046     m_ownsCSSOMWrapper = true;
1047     PropertySetCSSStyleDeclaration* cssomWrapper = new PropertySetCSSStyleDeclaration(const_cast<StylePropertySet*>(this));
1048     propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
1049     return cssomWrapper;
1050 }
1051
1052 CSSStyleDeclaration* StylePropertySet::ensureInlineCSSStyleDeclaration(const StyledElement* parentElement) const
1053 {
1054     if (m_ownsCSSOMWrapper) {
1055         ASSERT(propertySetCSSOMWrapperMap().get(this)->parentElement() == parentElement);
1056         return propertySetCSSOMWrapperMap().get(this);
1057     }
1058     m_ownsCSSOMWrapper = true;
1059     PropertySetCSSStyleDeclaration* cssomWrapper = new InlineCSSStyleDeclaration(const_cast<StylePropertySet*>(this), const_cast<StyledElement*>(parentElement));
1060     propertySetCSSOMWrapperMap().add(this, adoptPtr(cssomWrapper));
1061     return cssomWrapper;
1062 }
1063
1064 void StylePropertySet::clearParentElement(StyledElement* element)
1065 {
1066     if (!m_ownsCSSOMWrapper)
1067         return;
1068     ASSERT_UNUSED(element, propertySetCSSOMWrapperMap().get(this)->parentElement() == element);
1069     propertySetCSSOMWrapperMap().get(this)->clearParentElement();
1070 }
1071
1072 unsigned StylePropertySet::averageSizeInBytes()
1073 {
1074     // Please update this if the storage scheme changes so that this longer reflects the actual size.
1075     return sizeof(StylePropertySet) + sizeof(CSSProperty) * 2;
1076 }
1077
1078 void StylePropertySet::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
1079 {
1080     MemoryClassInfo<StylePropertySet> info(memoryObjectInfo, this, MemoryInstrumentation::CSS);
1081     if (m_isMutable)
1082         info.addVectorPtr(m_mutablePropertyVector);
1083     else
1084         info.addRawBuffer(m_properties, m_arraySize * sizeof(CSSProperty));
1085 }
1086
1087 // See the function above if you need to update this.
1088 struct SameSizeAsStylePropertySet : public RefCounted<SameSizeAsStylePropertySet> {
1089     unsigned bitfield;
1090     void* properties;
1091 };
1092 COMPILE_ASSERT(sizeof(StylePropertySet) == sizeof(SameSizeAsStylePropertySet), style_property_set_should_stay_small);
1093
1094 #ifndef NDEBUG
1095 void StylePropertySet::showStyle()
1096 {
1097     fprintf(stderr, "%s\n", asText().ascii().data());
1098 }
1099 #endif
1100
1101 inline void StylePropertySet::append(const CSSProperty& property)
1102 {
1103     ASSERT(isMutable());
1104     m_mutablePropertyVector->append(property);
1105 }
1106
1107 } // namespace WebCore