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