• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include "config.h"
22 #include "CSSMutableStyleDeclaration.h"
23 
24 #include "CSSImageValue.h"
25 #include "CSSParser.h"
26 #include "CSSProperty.h"
27 #include "CSSPropertyLonghand.h"
28 #include "CSSPropertyNames.h"
29 #include "CSSRule.h"
30 #include "CSSStyleSheet.h"
31 #include "CSSValueList.h"
32 #include "Document.h"
33 #include "ExceptionCode.h"
34 #include "StyledElement.h"
35 
36 using namespace std;
37 
38 namespace WebCore {
39 
CSSMutableStyleDeclaration()40 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration()
41     : m_node(0)
42     , m_variableDependentValueCount(0)
43     , m_strictParsing(false)
44 #ifndef NDEBUG
45     , m_iteratorCount(0)
46 #endif
47 {
48 }
49 
CSSMutableStyleDeclaration(CSSRule * parent)50 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent)
51     : CSSStyleDeclaration(parent)
52     , m_node(0)
53     , m_variableDependentValueCount(0)
54     , m_strictParsing(!parent || parent->useStrictParsing())
55 #ifndef NDEBUG
56     , m_iteratorCount(0)
57 #endif
58 {
59 }
60 
CSSMutableStyleDeclaration(CSSRule * parent,const Vector<CSSProperty> & properties,unsigned variableDependentValueCount)61 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const Vector<CSSProperty>& properties, unsigned variableDependentValueCount)
62     : CSSStyleDeclaration(parent)
63     , m_properties(properties)
64     , m_node(0)
65     , m_variableDependentValueCount(variableDependentValueCount)
66     , m_strictParsing(!parent || parent->useStrictParsing())
67 #ifndef NDEBUG
68     , m_iteratorCount(0)
69 #endif
70 {
71     m_properties.shrinkToFit();
72     // FIXME: This allows duplicate properties.
73 }
74 
CSSMutableStyleDeclaration(CSSRule * parent,const CSSProperty * const * properties,int numProperties)75 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const CSSProperty* const * properties, int numProperties)
76     : CSSStyleDeclaration(parent)
77     , m_node(0)
78     , m_variableDependentValueCount(0)
79     , m_strictParsing(!parent || parent->useStrictParsing())
80 #ifndef NDEBUG
81     , m_iteratorCount(0)
82 #endif
83 {
84     m_properties.reserveInitialCapacity(numProperties);
85     for (int i = 0; i < numProperties; ++i) {
86         ASSERT(properties[i]);
87         m_properties.append(*properties[i]);
88         if (properties[i]->value()->isVariableDependentValue())
89             m_variableDependentValueCount++;
90     }
91     // FIXME: This allows duplicate properties.
92 }
93 
operator =(const CSSMutableStyleDeclaration & other)94 CSSMutableStyleDeclaration& CSSMutableStyleDeclaration::operator=(const CSSMutableStyleDeclaration& other)
95 {
96     ASSERT(!m_iteratorCount);
97     // don't attach it to the same node, just leave the current m_node value
98     m_properties = other.m_properties;
99     m_strictParsing = other.m_strictParsing;
100     return *this;
101 }
102 
getPropertyValue(int propertyID) const103 String CSSMutableStyleDeclaration::getPropertyValue(int propertyID) const
104 {
105     RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
106     if (value)
107         return value->cssText();
108 
109     // Shorthand and 4-values properties
110     switch (propertyID) {
111         case CSSPropertyBackgroundPosition: {
112             // FIXME: Is this correct? The code in cssparser.cpp is confusing
113             const int properties[2] = { CSSPropertyBackgroundPositionX,
114                                         CSSPropertyBackgroundPositionY };
115             return getLayeredShorthandValue(properties, 2);
116         }
117         case CSSPropertyBackground: {
118             const int properties[7] = { CSSPropertyBackgroundImage, CSSPropertyBackgroundRepeat,
119                                         CSSPropertyBackgroundAttachment, CSSPropertyBackgroundPosition, CSSPropertyBackgroundClip,
120                                         CSSPropertyBackgroundOrigin, CSSPropertyBackgroundColor };
121             return getLayeredShorthandValue(properties, 7);
122         }
123         case CSSPropertyBorder: {
124             const int properties[3][4] = {{ CSSPropertyBorderTopWidth,
125                                             CSSPropertyBorderRightWidth,
126                                             CSSPropertyBorderBottomWidth,
127                                             CSSPropertyBorderLeftWidth },
128                                           { CSSPropertyBorderTopStyle,
129                                             CSSPropertyBorderRightStyle,
130                                             CSSPropertyBorderBottomStyle,
131                                             CSSPropertyBorderLeftStyle },
132                                           { CSSPropertyBorderTopColor,
133                                             CSSPropertyBorderRightColor,
134                                             CSSPropertyBorderBottomColor,
135                                             CSSPropertyBorderLeftColor }};
136             String res;
137             const int nrprops = sizeof(properties) / sizeof(properties[0]);
138             for (int i = 0; i < nrprops; ++i) {
139                 String value = getCommonValue(properties[i], 4);
140                 if (!value.isNull()) {
141                     if (!res.isNull())
142                         res += " ";
143                     res += value;
144                 }
145             }
146             return res;
147         }
148         case CSSPropertyBorderTop: {
149             const int properties[3] = { CSSPropertyBorderTopWidth, CSSPropertyBorderTopStyle,
150                                         CSSPropertyBorderTopColor};
151             return getShorthandValue(properties, 3);
152         }
153         case CSSPropertyBorderRight: {
154             const int properties[3] = { CSSPropertyBorderRightWidth, CSSPropertyBorderRightStyle,
155                                         CSSPropertyBorderRightColor};
156             return getShorthandValue(properties, 3);
157         }
158         case CSSPropertyBorderBottom: {
159             const int properties[3] = { CSSPropertyBorderBottomWidth, CSSPropertyBorderBottomStyle,
160                                         CSSPropertyBorderBottomColor};
161             return getShorthandValue(properties, 3);
162         }
163         case CSSPropertyBorderLeft: {
164             const int properties[3] = { CSSPropertyBorderLeftWidth, CSSPropertyBorderLeftStyle,
165                                         CSSPropertyBorderLeftColor};
166             return getShorthandValue(properties, 3);
167         }
168         case CSSPropertyOutline: {
169             const int properties[3] = { CSSPropertyOutlineWidth, CSSPropertyOutlineStyle,
170                                         CSSPropertyOutlineColor };
171             return getShorthandValue(properties, 3);
172         }
173         case CSSPropertyBorderColor: {
174             const int properties[4] = { CSSPropertyBorderTopColor, CSSPropertyBorderRightColor,
175                                         CSSPropertyBorderBottomColor, CSSPropertyBorderLeftColor };
176             return get4Values(properties);
177         }
178         case CSSPropertyBorderWidth: {
179             const int properties[4] = { CSSPropertyBorderTopWidth, CSSPropertyBorderRightWidth,
180                                         CSSPropertyBorderBottomWidth, CSSPropertyBorderLeftWidth };
181             return get4Values(properties);
182         }
183         case CSSPropertyBorderStyle: {
184             const int properties[4] = { CSSPropertyBorderTopStyle, CSSPropertyBorderRightStyle,
185                                         CSSPropertyBorderBottomStyle, CSSPropertyBorderLeftStyle };
186             return get4Values(properties);
187         }
188         case CSSPropertyMargin: {
189             const int properties[4] = { CSSPropertyMarginTop, CSSPropertyMarginRight,
190                                         CSSPropertyMarginBottom, CSSPropertyMarginLeft };
191             return get4Values(properties);
192         }
193         case CSSPropertyOverflow: {
194             const int properties[2] = { CSSPropertyOverflowX, CSSPropertyOverflowY };
195             return getCommonValue(properties, 2);
196         }
197         case CSSPropertyPadding: {
198             const int properties[4] = { CSSPropertyPaddingTop, CSSPropertyPaddingRight,
199                                         CSSPropertyPaddingBottom, CSSPropertyPaddingLeft };
200             return get4Values(properties);
201         }
202         case CSSPropertyListStyle: {
203             const int properties[3] = { CSSPropertyListStyleType, CSSPropertyListStylePosition,
204                                         CSSPropertyListStyleImage };
205             return getShorthandValue(properties, 3);
206         }
207         case CSSPropertyWebkitMaskPosition: {
208             // FIXME: Is this correct? The code in cssparser.cpp is confusing
209             const int properties[2] = { CSSPropertyWebkitMaskPositionX,
210                                         CSSPropertyWebkitMaskPositionY };
211             return getLayeredShorthandValue(properties, 2);
212         }
213         case CSSPropertyWebkitMask: {
214             const int properties[] = { CSSPropertyWebkitMaskImage, CSSPropertyWebkitMaskRepeat,
215                                        CSSPropertyWebkitMaskAttachment, CSSPropertyWebkitMaskPosition, CSSPropertyWebkitMaskClip,
216                                        CSSPropertyWebkitMaskOrigin };
217             return getLayeredShorthandValue(properties, 6);
218         }
219         case CSSPropertyWebkitTransformOrigin: {
220             const int properties[3] = { CSSPropertyWebkitTransformOriginX,
221                                         CSSPropertyWebkitTransformOriginY,
222                                         CSSPropertyWebkitTransformOriginZ };
223             return getShorthandValue(properties, 3);
224         }
225         case CSSPropertyWebkitTransition: {
226             const int properties[4] = { CSSPropertyWebkitTransitionProperty, CSSPropertyWebkitTransitionDuration,
227                                         CSSPropertyWebkitTransitionTimingFunction, CSSPropertyWebkitTransitionDelay };
228             return getLayeredShorthandValue(properties, 4);
229         }
230         case CSSPropertyWebkitAnimation: {
231             const int properties[6] = { CSSPropertyWebkitAnimationName, CSSPropertyWebkitAnimationDuration,
232                                         CSSPropertyWebkitAnimationTimingFunction, CSSPropertyWebkitAnimationDelay,
233                                         CSSPropertyWebkitAnimationIterationCount, CSSPropertyWebkitAnimationDirection };
234             return getLayeredShorthandValue(properties, 6);
235         }
236 #if ENABLE(SVG)
237         case CSSPropertyMarker: {
238             RefPtr<CSSValue> value = getPropertyCSSValue(CSSPropertyMarkerStart);
239             if (value)
240                 return value->cssText();
241         }
242 #endif
243     }
244     return String();
245 }
246 
get4Values(const int * properties) const247 String CSSMutableStyleDeclaration::get4Values(const int* properties) const
248 {
249     String res;
250     for (int i = 0; i < 4; ++i) {
251         if (!isPropertyImplicit(properties[i])) {
252             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
253 
254             // apparently all 4 properties must be specified.
255             if (!value)
256                 return String();
257 
258             if (!res.isNull())
259                 res += " ";
260             res += value->cssText();
261         }
262     }
263     return res;
264 }
265 
getLayeredShorthandValue(const int * properties,unsigned number) const266 String CSSMutableStyleDeclaration::getLayeredShorthandValue(const int* properties, unsigned number) const
267 {
268     String res;
269 
270     // Begin by collecting the properties into an array.
271     Vector< RefPtr<CSSValue> > values(number);
272     size_t numLayers = 0;
273 
274     for (size_t i = 0; i < number; ++i) {
275         values[i] = getPropertyCSSValue(properties[i]);
276         if (values[i]) {
277             if (values[i]->isValueList()) {
278                 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
279                 numLayers = max(valueList->length(), numLayers);
280             } else
281                 numLayers = max<size_t>(1U, numLayers);
282         }
283     }
284 
285     // Now stitch the properties together.  Implicit initial values are flagged as such and
286     // can safely be omitted.
287     for (size_t i = 0; i < numLayers; i++) {
288         String layerRes;
289         for (size_t j = 0; j < number; j++) {
290             RefPtr<CSSValue> value;
291             if (values[j]) {
292                 if (values[j]->isValueList())
293                     value = static_cast<CSSValueList*>(values[j].get())->itemWithoutBoundsCheck(i);
294                 else {
295                     value = values[j];
296 
297                     // Color only belongs in the last layer.
298                     if (properties[j] == CSSPropertyBackgroundColor) {
299                         if (i != numLayers - 1)
300                             value = 0;
301                     } else if (i != 0) // Other singletons only belong in the first layer.
302                         value = 0;
303                 }
304             }
305 
306             if (value && !value->isImplicitInitialValue()) {
307                 if (!layerRes.isNull())
308                     layerRes += " ";
309                 layerRes += value->cssText();
310             }
311         }
312 
313         if (!layerRes.isNull()) {
314             if (!res.isNull())
315                 res += ", ";
316             res += layerRes;
317         }
318     }
319 
320     return res;
321 }
322 
getShorthandValue(const int * properties,int number) const323 String CSSMutableStyleDeclaration::getShorthandValue(const int* properties, int number) const
324 {
325     String res;
326     for (int i = 0; i < number; ++i) {
327         if (!isPropertyImplicit(properties[i])) {
328             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
329             // FIXME: provide default value if !value
330             if (value) {
331                 if (!res.isNull())
332                     res += " ";
333                 res += value->cssText();
334             }
335         }
336     }
337     return res;
338 }
339 
340 // only returns a non-null value if all properties have the same, non-null value
getCommonValue(const int * properties,int number) const341 String CSSMutableStyleDeclaration::getCommonValue(const int* properties, int number) const
342 {
343     String res;
344     for (int i = 0; i < number; ++i) {
345         if (!isPropertyImplicit(properties[i])) {
346             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
347             if (!value)
348                 return String();
349             String text = value->cssText();
350             if (text.isNull())
351                 return String();
352             if (res.isNull())
353                 res = text;
354             else if (res != text)
355                 return String();
356         }
357     }
358     return res;
359 }
360 
getPropertyCSSValue(int propertyID) const361 PassRefPtr<CSSValue> CSSMutableStyleDeclaration::getPropertyCSSValue(int propertyID) const
362 {
363     const CSSProperty* property = findPropertyWithId(propertyID);
364     return property ? property->value() : 0;
365 }
366 
removeShorthandProperty(int propertyID,bool notifyChanged)367 bool CSSMutableStyleDeclaration::removeShorthandProperty(int propertyID, bool notifyChanged)
368 {
369     CSSPropertyLonghand longhand = longhandForProperty(propertyID);
370     if (longhand.length()) {
371         removePropertiesInSet(longhand.properties(), longhand.length(), notifyChanged);
372         return true;
373     }
374     return false;
375 }
376 
removeProperty(int propertyID,bool notifyChanged,bool returnText)377 String CSSMutableStyleDeclaration::removeProperty(int propertyID, bool notifyChanged, bool returnText)
378 {
379     ASSERT(!m_iteratorCount);
380 
381     if (removeShorthandProperty(propertyID, notifyChanged)) {
382         // FIXME: Return an equivalent shorthand when possible.
383         return String();
384     }
385 
386     CSSProperty* foundProperty = findPropertyWithId(propertyID);
387     if (!foundProperty)
388         return String();
389 
390     String value = returnText ? foundProperty->value()->cssText() : String();
391 
392     if (foundProperty->value()->isVariableDependentValue())
393         m_variableDependentValueCount--;
394 
395     // A more efficient removal strategy would involve marking entries as empty
396     // and sweeping them when the vector grows too big.
397     m_properties.remove(foundProperty - m_properties.data());
398 
399     if (notifyChanged)
400         setNeedsStyleRecalc();
401 
402     return value;
403 }
404 
setNeedsStyleRecalc()405 void CSSMutableStyleDeclaration::setNeedsStyleRecalc()
406 {
407     if (m_node) {
408         // FIXME: Ideally, this should be factored better and there
409         // should be a subclass of CSSMutableStyleDeclaration just
410         // for inline style declarations that handles this
411         bool isInlineStyleDeclaration = m_node->isStyledElement() && this == static_cast<StyledElement*>(m_node)->inlineStyleDecl();
412         if (isInlineStyleDeclaration) {
413             m_node->setNeedsStyleRecalc(InlineStyleChange);
414             static_cast<StyledElement*>(m_node)->invalidateStyleAttribute();
415         } else
416             m_node->setNeedsStyleRecalc(FullStyleChange);
417         return;
418     }
419 
420     // FIXME: quick&dirty hack for KDE 3.0... make this MUCH better! (Dirk)
421     StyleBase* root = this;
422     while (StyleBase* parent = root->parent())
423         root = parent;
424     if (root->isCSSStyleSheet())
425         static_cast<CSSStyleSheet*>(root)->doc()->updateStyleSelector();
426 }
427 
getPropertyPriority(int propertyID) const428 bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const
429 {
430     const CSSProperty* property = findPropertyWithId(propertyID);
431     return property ? property->isImportant() : false;
432 }
433 
getPropertyShorthand(int propertyID) const434 int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const
435 {
436     const CSSProperty* property = findPropertyWithId(propertyID);
437     return property ? property->shorthandID() : 0;
438 }
439 
isPropertyImplicit(int propertyID) const440 bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const
441 {
442     const CSSProperty* property = findPropertyWithId(propertyID);
443     return property ? property->isImplicit() : false;
444 }
445 
setProperty(int propertyID,const String & value,bool important,ExceptionCode & ec)446 void CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, ExceptionCode& ec)
447 {
448     ec = 0;
449     setProperty(propertyID, value, important, true);
450 }
451 
removeProperty(int propertyID,ExceptionCode & ec)452 String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec)
453 {
454     ec = 0;
455     return removeProperty(propertyID, true, true);
456 }
457 
setProperty(int propertyID,const String & value,bool important,bool notifyChanged)458 bool CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, bool notifyChanged)
459 {
460     ASSERT(!m_iteratorCount);
461 
462     // Setting the value to an empty string just removes the property in both IE and Gecko.
463     // Setting it to null seems to produce less consistent results, but we treat it just the same.
464     if (value.isEmpty()) {
465         removeProperty(propertyID, notifyChanged, false);
466         return true;
467     }
468 
469     // When replacing an existing property value, this moves the property to the end of the list.
470     // Firefox preserves the position, and MSIE moves the property to the beginning.
471     CSSParser parser(useStrictParsing());
472     bool success = parser.parseValue(this, propertyID, value, important);
473     if (!success) {
474         // CSS DOM requires raising SYNTAX_ERR here, but this is too dangerous for compatibility,
475         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
476     } else if (notifyChanged)
477         setNeedsStyleRecalc();
478 
479     return success;
480 }
481 
setPropertyInternal(const CSSProperty & property,CSSProperty * slot)482 void CSSMutableStyleDeclaration::setPropertyInternal(const CSSProperty& property, CSSProperty* slot)
483 {
484     ASSERT(!m_iteratorCount);
485 
486     if (!removeShorthandProperty(property.id(), false)) {
487         CSSProperty* toReplace = slot ? slot : findPropertyWithId(property.id());
488         if (toReplace) {
489             *toReplace = property;
490             return;
491         }
492     }
493     m_properties.append(property);
494 }
495 
setProperty(int propertyID,int value,bool important,bool notifyChanged)496 bool CSSMutableStyleDeclaration::setProperty(int propertyID, int value, bool important, bool notifyChanged)
497 {
498     CSSProperty property(propertyID, CSSPrimitiveValue::createIdentifier(value), important);
499     setPropertyInternal(property);
500     if (notifyChanged)
501         setNeedsStyleRecalc();
502     return true;
503 }
504 
setStringProperty(int propertyId,const String & value,CSSPrimitiveValue::UnitTypes type,bool important)505 void CSSMutableStyleDeclaration::setStringProperty(int propertyId, const String &value, CSSPrimitiveValue::UnitTypes type, bool important)
506 {
507     ASSERT(!m_iteratorCount);
508 
509     setPropertyInternal(CSSProperty(propertyId, CSSPrimitiveValue::create(value, type), important));
510     setNeedsStyleRecalc();
511 }
512 
setImageProperty(int propertyId,const String & url,bool important)513 void CSSMutableStyleDeclaration::setImageProperty(int propertyId, const String& url, bool important)
514 {
515     ASSERT(!m_iteratorCount);
516 
517     setPropertyInternal(CSSProperty(propertyId, CSSImageValue::create(url), important));
518     setNeedsStyleRecalc();
519 }
520 
parseDeclaration(const String & styleDeclaration)521 void CSSMutableStyleDeclaration::parseDeclaration(const String& styleDeclaration)
522 {
523     ASSERT(!m_iteratorCount);
524 
525     m_properties.clear();
526     CSSParser parser(useStrictParsing());
527     parser.parseDeclaration(this, styleDeclaration);
528     setNeedsStyleRecalc();
529 }
530 
addParsedProperties(const CSSProperty * const * properties,int numProperties)531 void CSSMutableStyleDeclaration::addParsedProperties(const CSSProperty* const* properties, int numProperties)
532 {
533     ASSERT(!m_iteratorCount);
534 
535     m_properties.reserveCapacity(numProperties);
536 
537     for (int i = 0; i < numProperties; ++i) {
538         // Only add properties that have no !important counterpart present
539         if (!getPropertyPriority(properties[i]->id()) || properties[i]->isImportant()) {
540             removeProperty(properties[i]->id(), false);
541             ASSERT(properties[i]);
542             m_properties.append(*properties[i]);
543             if (properties[i]->value()->isVariableDependentValue())
544                 m_variableDependentValueCount++;
545         }
546     }
547     // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
548     // a notifyChanged argument to this function to follow the model of other functions in this class.
549 }
550 
addParsedProperty(const CSSProperty & property)551 void CSSMutableStyleDeclaration::addParsedProperty(const CSSProperty& property)
552 {
553     ASSERT(!m_iteratorCount);
554 
555     setPropertyInternal(property);
556 }
557 
setLengthProperty(int propertyId,const String & value,bool important,bool)558 void CSSMutableStyleDeclaration::setLengthProperty(int propertyId, const String& value, bool important, bool /*multiLength*/)
559 {
560     ASSERT(!m_iteratorCount);
561 
562     bool parseMode = useStrictParsing();
563     setStrictParsing(false);
564     setProperty(propertyId, value, important);
565     setStrictParsing(parseMode);
566 }
567 
length() const568 unsigned CSSMutableStyleDeclaration::length() const
569 {
570     return m_properties.size();
571 }
572 
item(unsigned i) const573 String CSSMutableStyleDeclaration::item(unsigned i) const
574 {
575     if (i >= m_properties.size())
576        return String();
577     return getPropertyName(static_cast<CSSPropertyID>(m_properties[i].id()));
578 }
579 
cssText() const580 String CSSMutableStyleDeclaration::cssText() const
581 {
582     String result = "";
583 
584     const CSSProperty* positionXProp = 0;
585     const CSSProperty* positionYProp = 0;
586 
587     unsigned size = m_properties.size();
588     for (unsigned n = 0; n < size; ++n) {
589         const CSSProperty& prop = m_properties[n];
590         if (prop.id() == CSSPropertyBackgroundPositionX)
591             positionXProp = &prop;
592         else if (prop.id() == CSSPropertyBackgroundPositionY)
593             positionYProp = &prop;
594         else
595             result += prop.cssText();
596     }
597 
598     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
599     // It is required because background-position-x/y are non-standard properties and WebKit generated output
600     // would not work in Firefox (<rdar://problem/5143183>)
601     // It would be a better solution if background-position was CSS_PAIR.
602     if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) {
603         String positionValue;
604         const int properties[2] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
605         if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList())
606             positionValue = getLayeredShorthandValue(properties, 2);
607         else
608             positionValue = positionXProp->value()->cssText() + " " + positionYProp->value()->cssText();
609         result += "background-position: " + positionValue + (positionXProp->isImportant() ? " !important" : "") + "; ";
610     } else {
611         if (positionXProp)
612             result += positionXProp->cssText();
613         if (positionYProp)
614             result += positionYProp->cssText();
615     }
616 
617     return result;
618 }
619 
setCssText(const String & text,ExceptionCode & ec)620 void CSSMutableStyleDeclaration::setCssText(const String& text, ExceptionCode& ec)
621 {
622     ASSERT(!m_iteratorCount);
623 
624     ec = 0;
625     m_properties.clear();
626     CSSParser parser(useStrictParsing());
627     parser.parseDeclaration(this, text);
628     // FIXME: Detect syntax errors and set ec.
629     setNeedsStyleRecalc();
630 }
631 
merge(CSSMutableStyleDeclaration * other,bool argOverridesOnConflict)632 void CSSMutableStyleDeclaration::merge(CSSMutableStyleDeclaration* other, bool argOverridesOnConflict)
633 {
634     ASSERT(!m_iteratorCount);
635 
636     unsigned size = other->m_properties.size();
637     for (unsigned n = 0; n < size; ++n) {
638         CSSProperty& toMerge = other->m_properties[n];
639         CSSProperty* old = findPropertyWithId(toMerge.id());
640         if (old) {
641             if (!argOverridesOnConflict && old->value())
642                 continue;
643             setPropertyInternal(toMerge, old);
644         } else
645             m_properties.append(toMerge);
646     }
647     // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
648     // a notifyChanged argument to this function to follow the model of other functions in this class.
649 }
650 
addSubresourceStyleURLs(ListHashSet<KURL> & urls)651 void CSSMutableStyleDeclaration::addSubresourceStyleURLs(ListHashSet<KURL>& urls)
652 {
653     CSSStyleSheet* sheet = static_cast<CSSStyleSheet*>(stylesheet());
654     size_t size = m_properties.size();
655     for (size_t i = 0; i < size; ++i)
656         m_properties[i].value()->addSubresourceStyleURLs(urls, sheet);
657 }
658 
659 // This is the list of properties we want to copy in the copyBlockProperties() function.
660 // It is the list of CSS properties that apply specially to block-level elements.
661 static const int blockProperties[] = {
662     CSSPropertyOrphans,
663     CSSPropertyOverflow, // This can be also be applied to replaced elements
664     CSSPropertyWebkitColumnCount,
665     CSSPropertyWebkitColumnGap,
666     CSSPropertyWebkitColumnRuleColor,
667     CSSPropertyWebkitColumnRuleStyle,
668     CSSPropertyWebkitColumnRuleWidth,
669     CSSPropertyWebkitColumnBreakBefore,
670     CSSPropertyWebkitColumnBreakAfter,
671     CSSPropertyWebkitColumnBreakInside,
672     CSSPropertyWebkitColumnWidth,
673     CSSPropertyPageBreakAfter,
674     CSSPropertyPageBreakBefore,
675     CSSPropertyPageBreakInside,
676     CSSPropertyTextAlign,
677     CSSPropertyTextIndent,
678     CSSPropertyWidows
679 };
680 
681 const unsigned numBlockProperties = sizeof(blockProperties) / sizeof(blockProperties[0]);
682 
copyBlockProperties() const683 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copyBlockProperties() const
684 {
685     return copyPropertiesInSet(blockProperties, numBlockProperties);
686 }
687 
removeBlockProperties()688 void CSSMutableStyleDeclaration::removeBlockProperties()
689 {
690     removePropertiesInSet(blockProperties, numBlockProperties);
691 }
692 
removePropertiesInSet(const int * set,unsigned length,bool notifyChanged)693 void CSSMutableStyleDeclaration::removePropertiesInSet(const int* set, unsigned length, bool notifyChanged)
694 {
695     ASSERT(!m_iteratorCount);
696 
697     if (m_properties.isEmpty())
698         return;
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 
722     if (changed && notifyChanged)
723         setNeedsStyleRecalc();
724 }
725 
makeMutable()726 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable()
727 {
728     return this;
729 }
730 
copy() const731 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const
732 {
733     return adoptRef(new CSSMutableStyleDeclaration(0, m_properties, m_variableDependentValueCount));
734 }
735 
findPropertyWithId(int propertyID) const736 const CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID) const
737 {
738     for (int n = m_properties.size() - 1 ; n >= 0; --n) {
739         if (propertyID == m_properties[n].m_id)
740             return &m_properties[n];
741     }
742     return 0;
743 }
744 
findPropertyWithId(int propertyID)745 CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID)
746 {
747     for (int n = m_properties.size() - 1 ; n >= 0; --n) {
748         if (propertyID == m_properties[n].m_id)
749             return &m_properties[n];
750     }
751     return 0;
752 }
753 
754 } // namespace WebCore
755