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