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