• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 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 "CSSMutableStyleDeclaration.h"
24 
25 #include "CSSImageValue.h"
26 #include "CSSParser.h"
27 #include "CSSPropertyLonghand.h"
28 #include "CSSPropertyNames.h"
29 #include "CSSRule.h"
30 #include "CSSStyleSheet.h"
31 #include "CSSValueKeywords.h"
32 #include "CSSValueList.h"
33 #include "Document.h"
34 #include "ExceptionCode.h"
35 #include "InspectorInstrumentation.h"
36 #include "StyledElement.h"
37 #include <wtf/text/StringConcatenate.h>
38 
39 using namespace std;
40 
41 namespace WebCore {
42 
CSSMutableStyleDeclaration()43 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration()
44     : m_node(0)
45     , m_strictParsing(false)
46 #ifndef NDEBUG
47     , m_iteratorCount(0)
48 #endif
49 {
50 }
51 
CSSMutableStyleDeclaration(CSSRule * parent)52 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent)
53     : CSSStyleDeclaration(parent)
54     , m_node(0)
55     , m_strictParsing(!parent || parent->useStrictParsing())
56 #ifndef NDEBUG
57     , m_iteratorCount(0)
58 #endif
59 {
60 }
61 
CSSMutableStyleDeclaration(CSSRule * parent,const Vector<CSSProperty> & properties)62 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration(CSSRule* parent, const Vector<CSSProperty>& properties)
63     : CSSStyleDeclaration(parent)
64     , m_properties(properties)
65     , m_node(0)
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_strictParsing(!parent || parent->useStrictParsing())
79 #ifndef NDEBUG
80     , m_iteratorCount(0)
81 #endif
82 {
83     m_properties.reserveInitialCapacity(numProperties);
84     HashMap<int, bool> candidates;
85     for (int i = 0; i < numProperties; ++i) {
86         const CSSProperty *property = properties[i];
87         ASSERT(property);
88         bool important = property->isImportant();
89         if (candidates.contains(property->id())) {
90             if (!important && candidates.get(property->id()))
91                 continue;
92             removeProperty(property->id(), false);
93         }
94         m_properties.append(*property);
95         candidates.set(property->id(), important);
96     }
97 }
98 
operator =(const CSSMutableStyleDeclaration & other)99 CSSMutableStyleDeclaration& CSSMutableStyleDeclaration::operator=(const CSSMutableStyleDeclaration& other)
100 {
101     ASSERT(!m_iteratorCount);
102     // don't attach it to the same node, just leave the current m_node value
103     m_properties = other.m_properties;
104     m_strictParsing = other.m_strictParsing;
105     return *this;
106 }
107 
getPropertyValue(int propertyID) const108 String CSSMutableStyleDeclaration::getPropertyValue(int propertyID) const
109 {
110     RefPtr<CSSValue> value = getPropertyCSSValue(propertyID);
111     if (value)
112         return value->cssText();
113 
114     // Shorthand and 4-values properties
115     switch (propertyID) {
116         case CSSPropertyBorderSpacing: {
117             const int properties[2] = { CSSPropertyWebkitBorderHorizontalSpacing, CSSPropertyWebkitBorderVerticalSpacing };
118             return borderSpacingValue(properties);
119         }
120         case CSSPropertyBackgroundPosition: {
121             // FIXME: Is this correct? The code in cssparser.cpp is confusing
122             const int properties[2] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
123             return getLayeredShorthandValue(properties);
124         }
125         case CSSPropertyBackgroundRepeat: {
126             const int properties[2] = { CSSPropertyBackgroundRepeatX, CSSPropertyBackgroundRepeatY };
127             return getLayeredShorthandValue(properties);
128         }
129         case CSSPropertyBackground: {
130             const int properties[9] = { CSSPropertyBackgroundColor,
131                                         CSSPropertyBackgroundImage,
132                                         CSSPropertyBackgroundRepeatX,
133                                         CSSPropertyBackgroundRepeatY,
134                                         CSSPropertyBackgroundAttachment,
135                                         CSSPropertyBackgroundPositionX,
136                                         CSSPropertyBackgroundPositionY,
137                                         CSSPropertyBackgroundClip,
138                                         CSSPropertyBackgroundOrigin };
139             return getLayeredShorthandValue(properties);
140         }
141         case CSSPropertyBorder: {
142             const int properties[3][4] = {{ CSSPropertyBorderTopWidth,
143                                             CSSPropertyBorderRightWidth,
144                                             CSSPropertyBorderBottomWidth,
145                                             CSSPropertyBorderLeftWidth },
146                                           { CSSPropertyBorderTopStyle,
147                                             CSSPropertyBorderRightStyle,
148                                             CSSPropertyBorderBottomStyle,
149                                             CSSPropertyBorderLeftStyle },
150                                           { CSSPropertyBorderTopColor,
151                                             CSSPropertyBorderRightColor,
152                                             CSSPropertyBorderBottomColor,
153                                             CSSPropertyBorderLeftColor }};
154             String res;
155             for (size_t i = 0; i < WTF_ARRAY_LENGTH(properties); ++i) {
156                 String value = getCommonValue(properties[i]);
157                 if (!value.isNull()) {
158                     if (!res.isNull())
159                         res += " ";
160                     res += value;
161                 }
162             }
163             return res;
164         }
165         case CSSPropertyBorderTop: {
166             const int properties[3] = { CSSPropertyBorderTopWidth, CSSPropertyBorderTopStyle,
167                                         CSSPropertyBorderTopColor};
168             return getShorthandValue(properties);
169         }
170         case CSSPropertyBorderRight: {
171             const int properties[3] = { CSSPropertyBorderRightWidth, CSSPropertyBorderRightStyle,
172                                         CSSPropertyBorderRightColor};
173             return getShorthandValue(properties);
174         }
175         case CSSPropertyBorderBottom: {
176             const int properties[3] = { CSSPropertyBorderBottomWidth, CSSPropertyBorderBottomStyle,
177                                         CSSPropertyBorderBottomColor};
178             return getShorthandValue(properties);
179         }
180         case CSSPropertyBorderLeft: {
181             const int properties[3] = { CSSPropertyBorderLeftWidth, CSSPropertyBorderLeftStyle,
182                                         CSSPropertyBorderLeftColor};
183             return getShorthandValue(properties);
184         }
185         case CSSPropertyOutline: {
186             const int properties[3] = { CSSPropertyOutlineWidth, CSSPropertyOutlineStyle,
187                                         CSSPropertyOutlineColor };
188             return getShorthandValue(properties);
189         }
190         case CSSPropertyBorderColor: {
191             const int properties[4] = { CSSPropertyBorderTopColor, CSSPropertyBorderRightColor,
192                                         CSSPropertyBorderBottomColor, CSSPropertyBorderLeftColor };
193             return get4Values(properties);
194         }
195         case CSSPropertyBorderWidth: {
196             const int properties[4] = { CSSPropertyBorderTopWidth, CSSPropertyBorderRightWidth,
197                                         CSSPropertyBorderBottomWidth, CSSPropertyBorderLeftWidth };
198             return get4Values(properties);
199         }
200         case CSSPropertyBorderStyle: {
201             const int properties[4] = { CSSPropertyBorderTopStyle, CSSPropertyBorderRightStyle,
202                                         CSSPropertyBorderBottomStyle, CSSPropertyBorderLeftStyle };
203             return get4Values(properties);
204         }
205         case CSSPropertyMargin: {
206             const int properties[4] = { CSSPropertyMarginTop, CSSPropertyMarginRight,
207                                         CSSPropertyMarginBottom, CSSPropertyMarginLeft };
208             return get4Values(properties);
209         }
210         case CSSPropertyOverflow: {
211             const int properties[2] = { CSSPropertyOverflowX, CSSPropertyOverflowY };
212             return getCommonValue(properties);
213         }
214         case CSSPropertyPadding: {
215             const int properties[4] = { CSSPropertyPaddingTop, CSSPropertyPaddingRight,
216                                         CSSPropertyPaddingBottom, CSSPropertyPaddingLeft };
217             return get4Values(properties);
218         }
219         case CSSPropertyListStyle: {
220             const int properties[3] = { CSSPropertyListStyleType, CSSPropertyListStylePosition,
221                                         CSSPropertyListStyleImage };
222             return getShorthandValue(properties);
223         }
224         case CSSPropertyWebkitMaskPosition: {
225             // FIXME: Is this correct? The code in cssparser.cpp is confusing
226             const int properties[2] = { CSSPropertyWebkitMaskPositionX, CSSPropertyWebkitMaskPositionY };
227             return getLayeredShorthandValue(properties);
228         }
229         case CSSPropertyWebkitMaskRepeat: {
230             const int properties[2] = { CSSPropertyWebkitMaskRepeatX, CSSPropertyWebkitMaskRepeatY };
231             return getLayeredShorthandValue(properties);
232         }
233         case CSSPropertyWebkitMask: {
234             const int properties[] = { CSSPropertyWebkitMaskImage, CSSPropertyWebkitMaskRepeat,
235                                        CSSPropertyWebkitMaskAttachment, CSSPropertyWebkitMaskPosition, CSSPropertyWebkitMaskClip,
236                                        CSSPropertyWebkitMaskOrigin };
237             return getLayeredShorthandValue(properties);
238         }
239         case CSSPropertyWebkitTransformOrigin: {
240             const int properties[3] = { CSSPropertyWebkitTransformOriginX,
241                                         CSSPropertyWebkitTransformOriginY,
242                                         CSSPropertyWebkitTransformOriginZ };
243             return getShorthandValue(properties);
244         }
245         case CSSPropertyWebkitTransition: {
246             const int properties[4] = { CSSPropertyWebkitTransitionProperty, CSSPropertyWebkitTransitionDuration,
247                                         CSSPropertyWebkitTransitionTimingFunction, CSSPropertyWebkitTransitionDelay };
248             return getLayeredShorthandValue(properties);
249         }
250         case CSSPropertyWebkitAnimation: {
251             const int properties[7] = { CSSPropertyWebkitAnimationName, CSSPropertyWebkitAnimationDuration,
252                                         CSSPropertyWebkitAnimationTimingFunction, CSSPropertyWebkitAnimationDelay,
253                                         CSSPropertyWebkitAnimationIterationCount, CSSPropertyWebkitAnimationDirection,
254                                         CSSPropertyWebkitAnimationFillMode };
255             return getLayeredShorthandValue(properties);
256         }
257 #if ENABLE(SVG)
258         case CSSPropertyMarker: {
259             RefPtr<CSSValue> value = getPropertyCSSValue(CSSPropertyMarkerStart);
260             if (value)
261                 return value->cssText();
262         }
263 #endif
264     }
265     return String();
266 }
267 
borderSpacingValue(const int properties[2]) const268 String CSSMutableStyleDeclaration::borderSpacingValue(const int properties[2]) const
269 {
270     RefPtr<CSSValue> horizontalValue = getPropertyCSSValue(properties[0]);
271     RefPtr<CSSValue> verticalValue = getPropertyCSSValue(properties[1]);
272 
273     if (!horizontalValue)
274         return String();
275     ASSERT(verticalValue); // By <http://www.w3.org/TR/CSS21/tables.html#separated-borders>.
276 
277     String horizontalValueCSSText = horizontalValue->cssText();
278     String verticalValueCSSText = verticalValue->cssText();
279     if (horizontalValueCSSText == verticalValueCSSText)
280         return horizontalValueCSSText;
281     return makeString(horizontalValueCSSText, ' ', verticalValueCSSText);
282 }
283 
get4Values(const int * properties) const284 String CSSMutableStyleDeclaration::get4Values(const int* properties) const
285 {
286     // Assume the properties are in the usual order top, right, bottom, left.
287     RefPtr<CSSValue> topValue = getPropertyCSSValue(properties[0]);
288     RefPtr<CSSValue> rightValue = getPropertyCSSValue(properties[1]);
289     RefPtr<CSSValue> bottomValue = getPropertyCSSValue(properties[2]);
290     RefPtr<CSSValue> leftValue = getPropertyCSSValue(properties[3]);
291 
292     // All 4 properties must be specified.
293     if (!topValue || !rightValue || !bottomValue || !leftValue)
294         return String();
295 
296     bool showLeft = rightValue->cssText() != leftValue->cssText();
297     bool showBottom = (topValue->cssText() != bottomValue->cssText()) || showLeft;
298     bool showRight = (topValue->cssText() != rightValue->cssText()) || showBottom;
299 
300     String res = topValue->cssText();
301     if (showRight)
302         res += " " + rightValue->cssText();
303     if (showBottom)
304         res += " " + bottomValue->cssText();
305     if (showLeft)
306         res += " " + leftValue->cssText();
307 
308     return res;
309 }
310 
getLayeredShorthandValue(const int * properties,size_t size) const311 String CSSMutableStyleDeclaration::getLayeredShorthandValue(const int* properties, size_t size) const
312 {
313     String res;
314 
315     // Begin by collecting the properties into an array.
316     Vector< RefPtr<CSSValue> > values(size);
317     size_t numLayers = 0;
318 
319     for (size_t i = 0; i < size; ++i) {
320         values[i] = getPropertyCSSValue(properties[i]);
321         if (values[i]) {
322             if (values[i]->isValueList()) {
323                 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
324                 numLayers = max(valueList->length(), numLayers);
325             } else
326                 numLayers = max<size_t>(1U, numLayers);
327         }
328     }
329 
330     // Now stitch the properties together.  Implicit initial values are flagged as such and
331     // can safely be omitted.
332     for (size_t i = 0; i < numLayers; i++) {
333         String layerRes;
334         bool useRepeatXShorthand = false;
335         bool useRepeatYShorthand = false;
336         bool useSingleWordShorthand = false;
337         for (size_t j = 0; j < size; j++) {
338             RefPtr<CSSValue> value;
339             if (values[j]) {
340                 if (values[j]->isValueList())
341                     value = static_cast<CSSValueList*>(values[j].get())->item(i);
342                 else {
343                     value = values[j];
344 
345                     // Color only belongs in the last layer.
346                     if (properties[j] == CSSPropertyBackgroundColor) {
347                         if (i != numLayers - 1)
348                             value = 0;
349                     } else if (i != 0) // Other singletons only belong in the first layer.
350                         value = 0;
351                 }
352             }
353 
354             // We need to report background-repeat as it was written in the CSS. If the property is implicit,
355             // then it was written with only one value. Here we figure out which value that was so we can
356             // report back correctly.
357             if (properties[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(properties[j])) {
358 
359                 // BUG 49055: make sure the value was not reset in the layer check just above.
360                 if (j < size - 1 && properties[j + 1] == CSSPropertyBackgroundRepeatY && value) {
361                     RefPtr<CSSValue> yValue;
362                     RefPtr<CSSValue> nextValue = values[j + 1];
363                     if (nextValue->isValueList())
364                         yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
365                     else
366                         yValue = nextValue;
367 
368                     int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
369                     int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
370                     if (xId != yId) {
371                         if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
372                             useRepeatXShorthand = true;
373                             ++j;
374                         } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
375                             useRepeatYShorthand = true;
376                             continue;
377                         }
378                     } else {
379                         useSingleWordShorthand = true;
380                         ++j;
381                     }
382                 }
383             }
384 
385             if (value && !value->isImplicitInitialValue()) {
386                 if (!layerRes.isNull())
387                     layerRes += " ";
388                 if (useRepeatXShorthand) {
389                     useRepeatXShorthand = false;
390                     layerRes += getValueName(CSSValueRepeatX);
391                 } else if (useRepeatYShorthand) {
392                     useRepeatYShorthand = false;
393                     layerRes += getValueName(CSSValueRepeatY);
394                 } else if (useSingleWordShorthand) {
395                     useSingleWordShorthand = false;
396                     layerRes += value->cssText();
397                 } else
398                     layerRes += value->cssText();
399             }
400         }
401 
402         if (!layerRes.isNull()) {
403             if (!res.isNull())
404                 res += ", ";
405             res += layerRes;
406         }
407     }
408 
409     return res;
410 }
411 
getShorthandValue(const int * properties,size_t size) const412 String CSSMutableStyleDeclaration::getShorthandValue(const int* properties, size_t size) const
413 {
414     String res;
415     for (size_t i = 0; i < size; ++i) {
416         if (!isPropertyImplicit(properties[i])) {
417             RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
418             // FIXME: provide default value if !value
419             if (value) {
420                 if (!res.isNull())
421                     res += " ";
422                 res += value->cssText();
423             }
424         }
425     }
426     return res;
427 }
428 
429 // only returns a non-null value if all properties have the same, non-null value
getCommonValue(const int * properties,size_t size) const430 String CSSMutableStyleDeclaration::getCommonValue(const int* properties, size_t size) const
431 {
432     String res;
433     for (size_t i = 0; i < size; ++i) {
434         RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
435         if (!value)
436             return String();
437         String text = value->cssText();
438         if (text.isNull())
439             return String();
440         if (res.isNull())
441             res = text;
442         else if (res != text)
443             return String();
444     }
445     return res;
446 }
447 
getPropertyCSSValue(int propertyID) const448 PassRefPtr<CSSValue> CSSMutableStyleDeclaration::getPropertyCSSValue(int propertyID) const
449 {
450     const CSSProperty* property = findPropertyWithId(propertyID);
451     return property ? property->value() : 0;
452 }
453 
removeShorthandProperty(int propertyID,bool notifyChanged)454 bool CSSMutableStyleDeclaration::removeShorthandProperty(int propertyID, bool notifyChanged)
455 {
456     CSSPropertyLonghand longhand = longhandForProperty(propertyID);
457     if (longhand.length()) {
458         removePropertiesInSet(longhand.properties(), longhand.length(), notifyChanged);
459         return true;
460     }
461     return false;
462 }
463 
removeProperty(int propertyID,bool notifyChanged,bool returnText)464 String CSSMutableStyleDeclaration::removeProperty(int propertyID, bool notifyChanged, bool returnText)
465 {
466     ASSERT(!m_iteratorCount);
467 
468     if (removeShorthandProperty(propertyID, notifyChanged)) {
469         // FIXME: Return an equivalent shorthand when possible.
470         return String();
471     }
472 
473     CSSProperty* foundProperty = findPropertyWithId(propertyID);
474     if (!foundProperty)
475         return String();
476 
477     String value = returnText ? foundProperty->value()->cssText() : String();
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     if (notifyChanged)
484         setNeedsStyleRecalc();
485 
486     return value;
487 }
488 
isInlineStyleDeclaration()489 bool CSSMutableStyleDeclaration::isInlineStyleDeclaration()
490 {
491     // FIXME: Ideally, this should be factored better and there
492     // should be a subclass of CSSMutableStyleDeclaration just
493     // for inline style declarations that handles this
494     return m_node && m_node->isStyledElement() && static_cast<StyledElement*>(m_node)->inlineStyleDecl() == this;
495 }
496 
setNeedsStyleRecalc()497 void CSSMutableStyleDeclaration::setNeedsStyleRecalc()
498 {
499     if (m_node) {
500         if (isInlineStyleDeclaration()) {
501             m_node->setNeedsStyleRecalc(InlineStyleChange);
502             static_cast<StyledElement*>(m_node)->invalidateStyleAttribute();
503             if (m_node->document())
504                 InspectorInstrumentation::didInvalidateStyleAttr(m_node->document(), m_node);
505         } else
506             m_node->setNeedsStyleRecalc(FullStyleChange);
507         return;
508     }
509 
510     StyleBase* root = this;
511     while (StyleBase* parent = root->parent())
512         root = parent;
513     if (root->isCSSStyleSheet()) {
514         if (Document* document = static_cast<CSSStyleSheet*>(root)->document())
515             document->styleSelectorChanged(DeferRecalcStyle);
516     }
517 }
518 
getPropertyPriority(int propertyID) const519 bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const
520 {
521     const CSSProperty* property = findPropertyWithId(propertyID);
522     return property ? property->isImportant() : false;
523 }
524 
getPropertyShorthand(int propertyID) const525 int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const
526 {
527     const CSSProperty* property = findPropertyWithId(propertyID);
528     return property ? property->shorthandID() : 0;
529 }
530 
isPropertyImplicit(int propertyID) const531 bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const
532 {
533     const CSSProperty* property = findPropertyWithId(propertyID);
534     return property ? property->isImplicit() : false;
535 }
536 
setProperty(int propertyID,const String & value,bool important,ExceptionCode & ec)537 void CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, ExceptionCode& ec)
538 {
539     ec = 0;
540     setProperty(propertyID, value, important, true);
541 }
542 
removeProperty(int propertyID,ExceptionCode & ec)543 String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec)
544 {
545     ec = 0;
546     return removeProperty(propertyID, true, true);
547 }
548 
setProperty(int propertyID,const String & value,bool important,bool notifyChanged)549 bool CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, bool notifyChanged)
550 {
551     ASSERT(!m_iteratorCount);
552 
553     // Setting the value to an empty string just removes the property in both IE and Gecko.
554     // Setting it to null seems to produce less consistent results, but we treat it just the same.
555     if (value.isEmpty()) {
556         removeProperty(propertyID, notifyChanged, false);
557         return true;
558     }
559 
560     // When replacing an existing property value, this moves the property to the end of the list.
561     // Firefox preserves the position, and MSIE moves the property to the beginning.
562     bool success = CSSParser::parseValue(this, propertyID, value, important, useStrictParsing());
563     if (!success) {
564         // CSS DOM requires raising SYNTAX_ERR here, but this is too dangerous for compatibility,
565         // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
566     } else if (notifyChanged)
567         setNeedsStyleRecalc();
568 
569     return success;
570 }
571 
setPropertyInternal(const CSSProperty & property,CSSProperty * slot)572 void CSSMutableStyleDeclaration::setPropertyInternal(const CSSProperty& property, CSSProperty* slot)
573 {
574     ASSERT(!m_iteratorCount);
575 
576     if (!removeShorthandProperty(property.id(), false)) {
577         CSSProperty* toReplace = slot ? slot : findPropertyWithId(property.id());
578         if (toReplace) {
579             *toReplace = property;
580             return;
581         }
582     }
583     m_properties.append(property);
584 }
585 
setProperty(int propertyID,int value,bool important,bool notifyChanged)586 bool CSSMutableStyleDeclaration::setProperty(int propertyID, int value, bool important, bool notifyChanged)
587 {
588     CSSProperty property(propertyID, CSSPrimitiveValue::createIdentifier(value), important);
589     setPropertyInternal(property);
590     if (notifyChanged)
591         setNeedsStyleRecalc();
592     return true;
593 }
594 
setProperty(int propertyID,double value,CSSPrimitiveValue::UnitTypes unit,bool important,bool notifyChanged)595 bool CSSMutableStyleDeclaration::setProperty(int propertyID, double value, CSSPrimitiveValue::UnitTypes unit, bool important, bool notifyChanged)
596 {
597     CSSProperty property(propertyID, CSSPrimitiveValue::create(value, unit), important);
598     setPropertyInternal(property);
599     if (notifyChanged)
600         setNeedsStyleRecalc();
601     return true;
602 }
603 
setStringProperty(int propertyId,const String & value,CSSPrimitiveValue::UnitTypes type,bool important)604 void CSSMutableStyleDeclaration::setStringProperty(int propertyId, const String &value, CSSPrimitiveValue::UnitTypes type, bool important)
605 {
606     ASSERT(!m_iteratorCount);
607 
608     setPropertyInternal(CSSProperty(propertyId, CSSPrimitiveValue::create(value, type), important));
609     setNeedsStyleRecalc();
610 }
611 
setImageProperty(int propertyId,const String & url,bool important)612 void CSSMutableStyleDeclaration::setImageProperty(int propertyId, const String& url, bool important)
613 {
614     ASSERT(!m_iteratorCount);
615 
616     setPropertyInternal(CSSProperty(propertyId, CSSImageValue::create(url), important));
617     setNeedsStyleRecalc();
618 }
619 
parseDeclaration(const String & styleDeclaration)620 void CSSMutableStyleDeclaration::parseDeclaration(const String& styleDeclaration)
621 {
622     ASSERT(!m_iteratorCount);
623 
624     m_properties.clear();
625     CSSParser parser(useStrictParsing());
626     parser.parseDeclaration(this, styleDeclaration);
627     setNeedsStyleRecalc();
628 }
629 
addParsedProperties(const CSSProperty * const * properties,int numProperties)630 void CSSMutableStyleDeclaration::addParsedProperties(const CSSProperty* const* properties, int numProperties)
631 {
632     ASSERT(!m_iteratorCount);
633 
634     m_properties.reserveCapacity(numProperties);
635 
636     for (int i = 0; i < numProperties; ++i) {
637         // Only add properties that have no !important counterpart present
638         if (!getPropertyPriority(properties[i]->id()) || properties[i]->isImportant()) {
639             removeProperty(properties[i]->id(), false);
640             ASSERT(properties[i]);
641             m_properties.append(*properties[i]);
642         }
643     }
644     // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
645     // a notifyChanged argument to this function to follow the model of other functions in this class.
646 }
647 
addParsedProperty(const CSSProperty & property)648 void CSSMutableStyleDeclaration::addParsedProperty(const CSSProperty& property)
649 {
650     ASSERT(!m_iteratorCount);
651 
652     setPropertyInternal(property);
653 }
654 
setLengthProperty(int propertyId,const String & value,bool important,bool)655 void CSSMutableStyleDeclaration::setLengthProperty(int propertyId, const String& value, bool important, bool /*multiLength*/)
656 {
657     ASSERT(!m_iteratorCount);
658 
659     bool parseMode = useStrictParsing();
660     setStrictParsing(false);
661     setProperty(propertyId, value, important);
662     setStrictParsing(parseMode);
663 }
664 
virtualLength() const665 unsigned CSSMutableStyleDeclaration::virtualLength() const
666 {
667     return length();
668 }
669 
item(unsigned i) const670 String CSSMutableStyleDeclaration::item(unsigned i) const
671 {
672     if (i >= m_properties.size())
673        return "";
674     return getPropertyName(static_cast<CSSPropertyID>(m_properties[i].id()));
675 }
676 
cssText() const677 String CSSMutableStyleDeclaration::cssText() const
678 {
679     String result = "";
680 
681     const CSSProperty* positionXProp = 0;
682     const CSSProperty* positionYProp = 0;
683     const CSSProperty* repeatXProp = 0;
684     const CSSProperty* repeatYProp = 0;
685 
686     unsigned size = m_properties.size();
687     for (unsigned n = 0; n < size; ++n) {
688         const CSSProperty& prop = m_properties[n];
689         if (prop.id() == CSSPropertyBackgroundPositionX)
690             positionXProp = &prop;
691         else if (prop.id() == CSSPropertyBackgroundPositionY)
692             positionYProp = &prop;
693         else if (prop.id() == CSSPropertyBackgroundRepeatX)
694             repeatXProp = &prop;
695         else if (prop.id() == CSSPropertyBackgroundRepeatY)
696             repeatYProp = &prop;
697         else
698             result += prop.cssText();
699     }
700 
701     // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
702     // It is required because background-position-x/y are non-standard properties and WebKit generated output
703     // would not work in Firefox (<rdar://problem/5143183>)
704     // It would be a better solution if background-position was CSS_PAIR.
705     if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) {
706         String positionValue;
707         const int properties[2] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
708         if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList())
709             positionValue = getLayeredShorthandValue(properties);
710         else
711             positionValue = positionXProp->value()->cssText() + " " + positionYProp->value()->cssText();
712         result += "background-position: " + positionValue + (positionXProp->isImportant() ? " !important" : "") + "; ";
713     } else {
714         if (positionXProp)
715             result += positionXProp->cssText();
716         if (positionYProp)
717             result += positionYProp->cssText();
718     }
719 
720     // FIXME: We need to do the same for background-repeat.
721     if (repeatXProp && repeatYProp && repeatXProp->isImportant() == repeatYProp->isImportant()) {
722         String repeatValue;
723         const int repeatProperties[2] = { CSSPropertyBackgroundRepeatX, CSSPropertyBackgroundRepeatY };
724         if (repeatXProp->value()->isValueList() || repeatYProp->value()->isValueList())
725             repeatValue = getLayeredShorthandValue(repeatProperties);
726         else
727             repeatValue = repeatXProp->value()->cssText() + " " + repeatYProp->value()->cssText();
728         result += "background-repeat: " + repeatValue + (repeatXProp->isImportant() ? " !important" : "") + "; ";
729     } else {
730         if (repeatXProp)
731             result += repeatXProp->cssText();
732         if (repeatYProp)
733             result += repeatYProp->cssText();
734     }
735 
736     return result;
737 }
738 
setCssText(const String & text,ExceptionCode & ec)739 void CSSMutableStyleDeclaration::setCssText(const String& text, ExceptionCode& ec)
740 {
741     ASSERT(!m_iteratorCount);
742 
743     ec = 0;
744     m_properties.clear();
745     CSSParser parser(useStrictParsing());
746     parser.parseDeclaration(this, text);
747     // FIXME: Detect syntax errors and set ec.
748     setNeedsStyleRecalc();
749 }
750 
merge(const CSSMutableStyleDeclaration * other,bool argOverridesOnConflict)751 void CSSMutableStyleDeclaration::merge(const CSSMutableStyleDeclaration* other, bool argOverridesOnConflict)
752 {
753     ASSERT(!m_iteratorCount);
754 
755     unsigned size = other->m_properties.size();
756     for (unsigned n = 0; n < size; ++n) {
757         const CSSProperty& toMerge = other->m_properties[n];
758         CSSProperty* old = findPropertyWithId(toMerge.id());
759         if (old) {
760             if (!argOverridesOnConflict && old->value())
761                 continue;
762             setPropertyInternal(toMerge, old);
763         } else
764             m_properties.append(toMerge);
765     }
766     // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
767     // a notifyChanged argument to this function to follow the model of other functions in this class.
768 }
769 
addSubresourceStyleURLs(ListHashSet<KURL> & urls)770 void CSSMutableStyleDeclaration::addSubresourceStyleURLs(ListHashSet<KURL>& urls)
771 {
772     CSSStyleSheet* sheet = static_cast<CSSStyleSheet*>(stylesheet());
773     size_t size = m_properties.size();
774     for (size_t i = 0; i < size; ++i)
775         m_properties[i].value()->addSubresourceStyleURLs(urls, sheet);
776 }
777 
778 // This is the list of properties we want to copy in the copyBlockProperties() function.
779 // It is the list of CSS properties that apply specially to block-level elements.
780 static const int blockProperties[] = {
781     CSSPropertyOrphans,
782     CSSPropertyOverflow, // This can be also be applied to replaced elements
783     CSSPropertyWebkitColumnCount,
784     CSSPropertyWebkitColumnGap,
785     CSSPropertyWebkitColumnRuleColor,
786     CSSPropertyWebkitColumnRuleStyle,
787     CSSPropertyWebkitColumnRuleWidth,
788     CSSPropertyWebkitColumnBreakBefore,
789     CSSPropertyWebkitColumnBreakAfter,
790     CSSPropertyWebkitColumnBreakInside,
791     CSSPropertyWebkitColumnWidth,
792     CSSPropertyPageBreakAfter,
793     CSSPropertyPageBreakBefore,
794     CSSPropertyPageBreakInside,
795     CSSPropertyTextAlign,
796     CSSPropertyTextIndent,
797     CSSPropertyWidows
798 };
799 
800 const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
801 
copyBlockProperties() const802 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copyBlockProperties() const
803 {
804     return copyPropertiesInSet(blockProperties, numBlockProperties);
805 }
806 
removeBlockProperties()807 void CSSMutableStyleDeclaration::removeBlockProperties()
808 {
809     removePropertiesInSet(blockProperties, numBlockProperties);
810 }
811 
removePropertiesInSet(const int * set,unsigned length,bool notifyChanged)812 void CSSMutableStyleDeclaration::removePropertiesInSet(const int* set, unsigned length, bool notifyChanged)
813 {
814     ASSERT(!m_iteratorCount);
815 
816     if (m_properties.isEmpty())
817         return;
818 
819     // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
820     HashSet<int> toRemove;
821     for (unsigned i = 0; i < length; ++i)
822         toRemove.add(set[i]);
823 
824     Vector<CSSProperty, 4> newProperties;
825     newProperties.reserveInitialCapacity(m_properties.size());
826 
827     unsigned size = m_properties.size();
828     for (unsigned n = 0; n < size; ++n) {
829         const CSSProperty& property = m_properties[n];
830         // Not quite sure if the isImportant test is needed but it matches the existing behavior.
831         if (!property.isImportant()) {
832             if (toRemove.contains(property.id()))
833                 continue;
834         }
835         newProperties.append(property);
836     }
837 
838     bool changed = newProperties.size() != m_properties.size();
839     m_properties = newProperties;
840 
841     if (changed && notifyChanged)
842         setNeedsStyleRecalc();
843 }
844 
makeMutable()845 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable()
846 {
847     return this;
848 }
849 
copy() const850 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const
851 {
852     return adoptRef(new CSSMutableStyleDeclaration(0, m_properties));
853 }
854 
findPropertyWithId(int propertyID) const855 const CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID) const
856 {
857     for (int n = m_properties.size() - 1 ; n >= 0; --n) {
858         if (propertyID == m_properties[n].m_id)
859             return &m_properties[n];
860     }
861     return 0;
862 }
863 
findPropertyWithId(int propertyID)864 CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID)
865 {
866     for (int n = m_properties.size() - 1 ; n >= 0; --n) {
867         if (propertyID == m_properties[n].m_id)
868             return &m_properties[n];
869     }
870     return 0;
871 }
872 
873 } // namespace WebCore
874