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