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 #ifdef ANDROID_CSS_RING
265 case CSSPropertyWebkitRing: {
266 const int properties[9] = { CSSPropertyWebkitRingFillColor,
267 CSSPropertyWebkitRingInnerWidth,
268 CSSPropertyWebkitRingOuterWidth,
269 CSSPropertyWebkitRingOutset,
270 CSSPropertyWebkitRingPressedInnerColor,
271 CSSPropertyWebkitRingPressedOuterColor,
272 CSSPropertyWebkitRingRadius,
273 CSSPropertyWebkitRingSelectedInnerColor,
274 CSSPropertyWebkitRingSelectedOuterColor };
275 return getLayeredShorthandValue(properties, 9);
276 }
277 #endif
278 }
279 return String();
280 }
281
borderSpacingValue(const int properties[2]) const282 String CSSMutableStyleDeclaration::borderSpacingValue(const int properties[2]) const
283 {
284 RefPtr<CSSValue> horizontalValue = getPropertyCSSValue(properties[0]);
285 RefPtr<CSSValue> verticalValue = getPropertyCSSValue(properties[1]);
286
287 if (!horizontalValue)
288 return String();
289 ASSERT(verticalValue); // By <http://www.w3.org/TR/CSS21/tables.html#separated-borders>.
290
291 String horizontalValueCSSText = horizontalValue->cssText();
292 String verticalValueCSSText = verticalValue->cssText();
293 if (horizontalValueCSSText == verticalValueCSSText)
294 return horizontalValueCSSText;
295 return makeString(horizontalValueCSSText, ' ', verticalValueCSSText);
296 }
297
get4Values(const int * properties) const298 String CSSMutableStyleDeclaration::get4Values(const int* properties) const
299 {
300 // Assume the properties are in the usual order top, right, bottom, left.
301 RefPtr<CSSValue> topValue = getPropertyCSSValue(properties[0]);
302 RefPtr<CSSValue> rightValue = getPropertyCSSValue(properties[1]);
303 RefPtr<CSSValue> bottomValue = getPropertyCSSValue(properties[2]);
304 RefPtr<CSSValue> leftValue = getPropertyCSSValue(properties[3]);
305
306 // All 4 properties must be specified.
307 if (!topValue || !rightValue || !bottomValue || !leftValue)
308 return String();
309
310 bool showLeft = rightValue->cssText() != leftValue->cssText();
311 bool showBottom = (topValue->cssText() != bottomValue->cssText()) || showLeft;
312 bool showRight = (topValue->cssText() != rightValue->cssText()) || showBottom;
313
314 String res = topValue->cssText();
315 if (showRight)
316 res += " " + rightValue->cssText();
317 if (showBottom)
318 res += " " + bottomValue->cssText();
319 if (showLeft)
320 res += " " + leftValue->cssText();
321
322 return res;
323 }
324
getLayeredShorthandValue(const int * properties,size_t size) const325 String CSSMutableStyleDeclaration::getLayeredShorthandValue(const int* properties, size_t size) const
326 {
327 String res;
328
329 // Begin by collecting the properties into an array.
330 Vector< RefPtr<CSSValue> > values(size);
331 size_t numLayers = 0;
332
333 for (size_t i = 0; i < size; ++i) {
334 values[i] = getPropertyCSSValue(properties[i]);
335 if (values[i]) {
336 if (values[i]->isValueList()) {
337 CSSValueList* valueList = static_cast<CSSValueList*>(values[i].get());
338 numLayers = max(valueList->length(), numLayers);
339 } else
340 numLayers = max<size_t>(1U, numLayers);
341 }
342 }
343
344 // Now stitch the properties together. Implicit initial values are flagged as such and
345 // can safely be omitted.
346 for (size_t i = 0; i < numLayers; i++) {
347 String layerRes;
348 bool useRepeatXShorthand = false;
349 bool useRepeatYShorthand = false;
350 bool useSingleWordShorthand = false;
351 for (size_t j = 0; j < size; j++) {
352 RefPtr<CSSValue> value;
353 if (values[j]) {
354 if (values[j]->isValueList())
355 value = static_cast<CSSValueList*>(values[j].get())->item(i);
356 else {
357 value = values[j];
358
359 // Color only belongs in the last layer.
360 if (properties[j] == CSSPropertyBackgroundColor) {
361 if (i != numLayers - 1)
362 value = 0;
363 } else if (i != 0) // Other singletons only belong in the first layer.
364 value = 0;
365 }
366 }
367
368 // We need to report background-repeat as it was written in the CSS. If the property is implicit,
369 // then it was written with only one value. Here we figure out which value that was so we can
370 // report back correctly.
371 if (properties[j] == CSSPropertyBackgroundRepeatX && isPropertyImplicit(properties[j])) {
372
373 // BUG 49055: make sure the value was not reset in the layer check just above.
374 if (j < size - 1 && properties[j + 1] == CSSPropertyBackgroundRepeatY && value) {
375 RefPtr<CSSValue> yValue;
376 RefPtr<CSSValue> nextValue = values[j + 1];
377 if (nextValue->isValueList())
378 yValue = static_cast<CSSValueList*>(nextValue.get())->itemWithoutBoundsCheck(i);
379 else
380 yValue = nextValue;
381
382 int xId = static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
383 int yId = static_cast<CSSPrimitiveValue*>(yValue.get())->getIdent();
384 if (xId != yId) {
385 if (xId == CSSValueRepeat && yId == CSSValueNoRepeat) {
386 useRepeatXShorthand = true;
387 ++j;
388 } else if (xId == CSSValueNoRepeat && yId == CSSValueRepeat) {
389 useRepeatYShorthand = true;
390 continue;
391 }
392 } else {
393 useSingleWordShorthand = true;
394 ++j;
395 }
396 }
397 }
398
399 if (value && !value->isImplicitInitialValue()) {
400 if (!layerRes.isNull())
401 layerRes += " ";
402 if (useRepeatXShorthand) {
403 useRepeatXShorthand = false;
404 layerRes += getValueName(CSSValueRepeatX);
405 } else if (useRepeatYShorthand) {
406 useRepeatYShorthand = false;
407 layerRes += getValueName(CSSValueRepeatY);
408 } else if (useSingleWordShorthand) {
409 useSingleWordShorthand = false;
410 layerRes += value->cssText();
411 } else
412 layerRes += value->cssText();
413 }
414 }
415
416 if (!layerRes.isNull()) {
417 if (!res.isNull())
418 res += ", ";
419 res += layerRes;
420 }
421 }
422
423 return res;
424 }
425
getShorthandValue(const int * properties,size_t size) const426 String CSSMutableStyleDeclaration::getShorthandValue(const int* properties, size_t size) const
427 {
428 String res;
429 for (size_t i = 0; i < size; ++i) {
430 if (!isPropertyImplicit(properties[i])) {
431 RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
432 // FIXME: provide default value if !value
433 if (value) {
434 if (!res.isNull())
435 res += " ";
436 res += value->cssText();
437 }
438 }
439 }
440 return res;
441 }
442
443 // only returns a non-null value if all properties have the same, non-null value
getCommonValue(const int * properties,size_t size) const444 String CSSMutableStyleDeclaration::getCommonValue(const int* properties, size_t size) const
445 {
446 String res;
447 for (size_t i = 0; i < size; ++i) {
448 RefPtr<CSSValue> value = getPropertyCSSValue(properties[i]);
449 if (!value)
450 return String();
451 String text = value->cssText();
452 if (text.isNull())
453 return String();
454 if (res.isNull())
455 res = text;
456 else if (res != text)
457 return String();
458 }
459 return res;
460 }
461
getPropertyCSSValue(int propertyID) const462 PassRefPtr<CSSValue> CSSMutableStyleDeclaration::getPropertyCSSValue(int propertyID) const
463 {
464 const CSSProperty* property = findPropertyWithId(propertyID);
465 return property ? property->value() : 0;
466 }
467
removeShorthandProperty(int propertyID,bool notifyChanged)468 bool CSSMutableStyleDeclaration::removeShorthandProperty(int propertyID, bool notifyChanged)
469 {
470 CSSPropertyLonghand longhand = longhandForProperty(propertyID);
471 if (longhand.length()) {
472 removePropertiesInSet(longhand.properties(), longhand.length(), notifyChanged);
473 return true;
474 }
475 return false;
476 }
477
removeProperty(int propertyID,bool notifyChanged,bool returnText)478 String CSSMutableStyleDeclaration::removeProperty(int propertyID, bool notifyChanged, bool returnText)
479 {
480 ASSERT(!m_iteratorCount);
481
482 if (removeShorthandProperty(propertyID, notifyChanged)) {
483 // FIXME: Return an equivalent shorthand when possible.
484 return String();
485 }
486
487 CSSProperty* foundProperty = findPropertyWithId(propertyID);
488 if (!foundProperty)
489 return String();
490
491 String value = returnText ? foundProperty->value()->cssText() : String();
492
493 // A more efficient removal strategy would involve marking entries as empty
494 // and sweeping them when the vector grows too big.
495 m_properties.remove(foundProperty - m_properties.data());
496
497 if (notifyChanged)
498 setNeedsStyleRecalc();
499
500 return value;
501 }
502
isInlineStyleDeclaration()503 bool CSSMutableStyleDeclaration::isInlineStyleDeclaration()
504 {
505 // FIXME: Ideally, this should be factored better and there
506 // should be a subclass of CSSMutableStyleDeclaration just
507 // for inline style declarations that handles this
508 return m_node && m_node->isStyledElement() && static_cast<StyledElement*>(m_node)->inlineStyleDecl() == this;
509 }
510
setNeedsStyleRecalc()511 void CSSMutableStyleDeclaration::setNeedsStyleRecalc()
512 {
513 if (m_node) {
514 if (isInlineStyleDeclaration()) {
515 m_node->setNeedsStyleRecalc(InlineStyleChange);
516 static_cast<StyledElement*>(m_node)->invalidateStyleAttribute();
517 if (m_node->document())
518 InspectorInstrumentation::didInvalidateStyleAttr(m_node->document(), m_node);
519 } else
520 m_node->setNeedsStyleRecalc(FullStyleChange);
521 return;
522 }
523
524 StyleBase* root = this;
525 while (StyleBase* parent = root->parent())
526 root = parent;
527 if (root->isCSSStyleSheet()) {
528 if (Document* document = static_cast<CSSStyleSheet*>(root)->document())
529 document->styleSelectorChanged(DeferRecalcStyle);
530 }
531 }
532
getPropertyPriority(int propertyID) const533 bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const
534 {
535 const CSSProperty* property = findPropertyWithId(propertyID);
536 return property ? property->isImportant() : false;
537 }
538
getPropertyShorthand(int propertyID) const539 int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const
540 {
541 const CSSProperty* property = findPropertyWithId(propertyID);
542 return property ? property->shorthandID() : 0;
543 }
544
isPropertyImplicit(int propertyID) const545 bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const
546 {
547 const CSSProperty* property = findPropertyWithId(propertyID);
548 return property ? property->isImplicit() : false;
549 }
550
setProperty(int propertyID,const String & value,bool important,ExceptionCode & ec)551 void CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, ExceptionCode& ec)
552 {
553 ec = 0;
554 setProperty(propertyID, value, important, true);
555 }
556
removeProperty(int propertyID,ExceptionCode & ec)557 String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec)
558 {
559 ec = 0;
560 return removeProperty(propertyID, true, true);
561 }
562
setProperty(int propertyID,const String & value,bool important,bool notifyChanged)563 bool CSSMutableStyleDeclaration::setProperty(int propertyID, const String& value, bool important, bool notifyChanged)
564 {
565 ASSERT(!m_iteratorCount);
566
567 // Setting the value to an empty string just removes the property in both IE and Gecko.
568 // Setting it to null seems to produce less consistent results, but we treat it just the same.
569 if (value.isEmpty()) {
570 removeProperty(propertyID, notifyChanged, false);
571 return true;
572 }
573
574 // When replacing an existing property value, this moves the property to the end of the list.
575 // Firefox preserves the position, and MSIE moves the property to the beginning.
576 bool success = CSSParser::parseValue(this, propertyID, value, important, useStrictParsing());
577 if (!success) {
578 // CSS DOM requires raising SYNTAX_ERR here, but this is too dangerous for compatibility,
579 // see <http://bugs.webkit.org/show_bug.cgi?id=7296>.
580 } else if (notifyChanged)
581 setNeedsStyleRecalc();
582
583 return success;
584 }
585
setPropertyInternal(const CSSProperty & property,CSSProperty * slot)586 void CSSMutableStyleDeclaration::setPropertyInternal(const CSSProperty& property, CSSProperty* slot)
587 {
588 ASSERT(!m_iteratorCount);
589
590 if (!removeShorthandProperty(property.id(), false)) {
591 CSSProperty* toReplace = slot ? slot : findPropertyWithId(property.id());
592 if (toReplace) {
593 *toReplace = property;
594 return;
595 }
596 }
597 m_properties.append(property);
598 }
599
setProperty(int propertyID,int value,bool important,bool notifyChanged)600 bool CSSMutableStyleDeclaration::setProperty(int propertyID, int value, bool important, bool notifyChanged)
601 {
602 CSSProperty property(propertyID, CSSPrimitiveValue::createIdentifier(value), important);
603 setPropertyInternal(property);
604 if (notifyChanged)
605 setNeedsStyleRecalc();
606 return true;
607 }
608
setProperty(int propertyID,double value,CSSPrimitiveValue::UnitTypes unit,bool important,bool notifyChanged)609 bool CSSMutableStyleDeclaration::setProperty(int propertyID, double value, CSSPrimitiveValue::UnitTypes unit, bool important, bool notifyChanged)
610 {
611 CSSProperty property(propertyID, CSSPrimitiveValue::create(value, unit), important);
612 setPropertyInternal(property);
613 if (notifyChanged)
614 setNeedsStyleRecalc();
615 return true;
616 }
617
setStringProperty(int propertyId,const String & value,CSSPrimitiveValue::UnitTypes type,bool important)618 void CSSMutableStyleDeclaration::setStringProperty(int propertyId, const String &value, CSSPrimitiveValue::UnitTypes type, bool important)
619 {
620 ASSERT(!m_iteratorCount);
621
622 setPropertyInternal(CSSProperty(propertyId, CSSPrimitiveValue::create(value, type), important));
623 setNeedsStyleRecalc();
624 }
625
setImageProperty(int propertyId,const String & url,bool important)626 void CSSMutableStyleDeclaration::setImageProperty(int propertyId, const String& url, bool important)
627 {
628 ASSERT(!m_iteratorCount);
629
630 setPropertyInternal(CSSProperty(propertyId, CSSImageValue::create(url), important));
631 setNeedsStyleRecalc();
632 }
633
parseDeclaration(const String & styleDeclaration)634 void CSSMutableStyleDeclaration::parseDeclaration(const String& styleDeclaration)
635 {
636 ASSERT(!m_iteratorCount);
637
638 m_properties.clear();
639 CSSParser parser(useStrictParsing());
640 parser.parseDeclaration(this, styleDeclaration);
641 setNeedsStyleRecalc();
642 }
643
addParsedProperties(const CSSProperty * const * properties,int numProperties)644 void CSSMutableStyleDeclaration::addParsedProperties(const CSSProperty* const* properties, int numProperties)
645 {
646 ASSERT(!m_iteratorCount);
647
648 m_properties.reserveCapacity(numProperties);
649
650 for (int i = 0; i < numProperties; ++i) {
651 // Only add properties that have no !important counterpart present
652 if (!getPropertyPriority(properties[i]->id()) || properties[i]->isImportant()) {
653 removeProperty(properties[i]->id(), false);
654 ASSERT(properties[i]);
655 m_properties.append(*properties[i]);
656 }
657 }
658 // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
659 // a notifyChanged argument to this function to follow the model of other functions in this class.
660 }
661
addParsedProperty(const CSSProperty & property)662 void CSSMutableStyleDeclaration::addParsedProperty(const CSSProperty& property)
663 {
664 ASSERT(!m_iteratorCount);
665
666 setPropertyInternal(property);
667 }
668
setLengthProperty(int propertyId,const String & value,bool important,bool)669 void CSSMutableStyleDeclaration::setLengthProperty(int propertyId, const String& value, bool important, bool /*multiLength*/)
670 {
671 ASSERT(!m_iteratorCount);
672
673 bool parseMode = useStrictParsing();
674 setStrictParsing(false);
675 setProperty(propertyId, value, important);
676 setStrictParsing(parseMode);
677 }
678
virtualLength() const679 unsigned CSSMutableStyleDeclaration::virtualLength() const
680 {
681 return length();
682 }
683
item(unsigned i) const684 String CSSMutableStyleDeclaration::item(unsigned i) const
685 {
686 if (i >= m_properties.size())
687 return "";
688 return getPropertyName(static_cast<CSSPropertyID>(m_properties[i].id()));
689 }
690
cssText() const691 String CSSMutableStyleDeclaration::cssText() const
692 {
693 String result = "";
694
695 const CSSProperty* positionXProp = 0;
696 const CSSProperty* positionYProp = 0;
697 const CSSProperty* repeatXProp = 0;
698 const CSSProperty* repeatYProp = 0;
699
700 unsigned size = m_properties.size();
701 for (unsigned n = 0; n < size; ++n) {
702 const CSSProperty& prop = m_properties[n];
703 if (prop.id() == CSSPropertyBackgroundPositionX)
704 positionXProp = ∝
705 else if (prop.id() == CSSPropertyBackgroundPositionY)
706 positionYProp = ∝
707 else if (prop.id() == CSSPropertyBackgroundRepeatX)
708 repeatXProp = ∝
709 else if (prop.id() == CSSPropertyBackgroundRepeatY)
710 repeatYProp = ∝
711 else
712 result += prop.cssText();
713 }
714
715 // FIXME: This is a not-so-nice way to turn x/y positions into single background-position in output.
716 // It is required because background-position-x/y are non-standard properties and WebKit generated output
717 // would not work in Firefox (<rdar://problem/5143183>)
718 // It would be a better solution if background-position was CSS_PAIR.
719 if (positionXProp && positionYProp && positionXProp->isImportant() == positionYProp->isImportant()) {
720 String positionValue;
721 const int properties[2] = { CSSPropertyBackgroundPositionX, CSSPropertyBackgroundPositionY };
722 if (positionXProp->value()->isValueList() || positionYProp->value()->isValueList())
723 positionValue = getLayeredShorthandValue(properties);
724 else
725 positionValue = positionXProp->value()->cssText() + " " + positionYProp->value()->cssText();
726 result += "background-position: " + positionValue + (positionXProp->isImportant() ? " !important" : "") + "; ";
727 } else {
728 if (positionXProp)
729 result += positionXProp->cssText();
730 if (positionYProp)
731 result += positionYProp->cssText();
732 }
733
734 // FIXME: We need to do the same for background-repeat.
735 if (repeatXProp && repeatYProp && repeatXProp->isImportant() == repeatYProp->isImportant()) {
736 String repeatValue;
737 const int repeatProperties[2] = { CSSPropertyBackgroundRepeatX, CSSPropertyBackgroundRepeatY };
738 if (repeatXProp->value()->isValueList() || repeatYProp->value()->isValueList())
739 repeatValue = getLayeredShorthandValue(repeatProperties);
740 else
741 repeatValue = repeatXProp->value()->cssText() + " " + repeatYProp->value()->cssText();
742 result += "background-repeat: " + repeatValue + (repeatXProp->isImportant() ? " !important" : "") + "; ";
743 } else {
744 if (repeatXProp)
745 result += repeatXProp->cssText();
746 if (repeatYProp)
747 result += repeatYProp->cssText();
748 }
749
750 return result;
751 }
752
setCssText(const String & text,ExceptionCode & ec)753 void CSSMutableStyleDeclaration::setCssText(const String& text, ExceptionCode& ec)
754 {
755 ASSERT(!m_iteratorCount);
756
757 ec = 0;
758 m_properties.clear();
759 CSSParser parser(useStrictParsing());
760 parser.parseDeclaration(this, text);
761 // FIXME: Detect syntax errors and set ec.
762 setNeedsStyleRecalc();
763 }
764
merge(const CSSMutableStyleDeclaration * other,bool argOverridesOnConflict)765 void CSSMutableStyleDeclaration::merge(const CSSMutableStyleDeclaration* other, bool argOverridesOnConflict)
766 {
767 ASSERT(!m_iteratorCount);
768
769 unsigned size = other->m_properties.size();
770 for (unsigned n = 0; n < size; ++n) {
771 const CSSProperty& toMerge = other->m_properties[n];
772 CSSProperty* old = findPropertyWithId(toMerge.id());
773 if (old) {
774 if (!argOverridesOnConflict && old->value())
775 continue;
776 setPropertyInternal(toMerge, old);
777 } else
778 m_properties.append(toMerge);
779 }
780 // FIXME: This probably should have a call to setNeedsStyleRecalc() if something changed. We may also wish to add
781 // a notifyChanged argument to this function to follow the model of other functions in this class.
782 }
783
addSubresourceStyleURLs(ListHashSet<KURL> & urls)784 void CSSMutableStyleDeclaration::addSubresourceStyleURLs(ListHashSet<KURL>& urls)
785 {
786 CSSStyleSheet* sheet = static_cast<CSSStyleSheet*>(stylesheet());
787 size_t size = m_properties.size();
788 for (size_t i = 0; i < size; ++i)
789 m_properties[i].value()->addSubresourceStyleURLs(urls, sheet);
790 }
791
792 // This is the list of properties we want to copy in the copyBlockProperties() function.
793 // It is the list of CSS properties that apply specially to block-level elements.
794 static const int blockProperties[] = {
795 CSSPropertyOrphans,
796 CSSPropertyOverflow, // This can be also be applied to replaced elements
797 CSSPropertyWebkitColumnCount,
798 CSSPropertyWebkitColumnGap,
799 CSSPropertyWebkitColumnRuleColor,
800 CSSPropertyWebkitColumnRuleStyle,
801 CSSPropertyWebkitColumnRuleWidth,
802 CSSPropertyWebkitColumnBreakBefore,
803 CSSPropertyWebkitColumnBreakAfter,
804 CSSPropertyWebkitColumnBreakInside,
805 CSSPropertyWebkitColumnWidth,
806 CSSPropertyPageBreakAfter,
807 CSSPropertyPageBreakBefore,
808 CSSPropertyPageBreakInside,
809 CSSPropertyTextAlign,
810 CSSPropertyTextIndent,
811 CSSPropertyWidows
812 };
813
814 const unsigned numBlockProperties = WTF_ARRAY_LENGTH(blockProperties);
815
copyBlockProperties() const816 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copyBlockProperties() const
817 {
818 return copyPropertiesInSet(blockProperties, numBlockProperties);
819 }
820
removeBlockProperties()821 void CSSMutableStyleDeclaration::removeBlockProperties()
822 {
823 removePropertiesInSet(blockProperties, numBlockProperties);
824 }
825
removePropertiesInSet(const int * set,unsigned length,bool notifyChanged)826 void CSSMutableStyleDeclaration::removePropertiesInSet(const int* set, unsigned length, bool notifyChanged)
827 {
828 ASSERT(!m_iteratorCount);
829
830 if (m_properties.isEmpty())
831 return;
832
833 // FIXME: This is always used with static sets and in that case constructing the hash repeatedly is pretty pointless.
834 HashSet<int> toRemove;
835 for (unsigned i = 0; i < length; ++i)
836 toRemove.add(set[i]);
837
838 Vector<CSSProperty, 4> newProperties;
839 newProperties.reserveInitialCapacity(m_properties.size());
840
841 unsigned size = m_properties.size();
842 for (unsigned n = 0; n < size; ++n) {
843 const CSSProperty& property = m_properties[n];
844 // Not quite sure if the isImportant test is needed but it matches the existing behavior.
845 if (!property.isImportant()) {
846 if (toRemove.contains(property.id()))
847 continue;
848 }
849 newProperties.append(property);
850 }
851
852 bool changed = newProperties.size() != m_properties.size();
853 m_properties = newProperties;
854
855 if (changed && notifyChanged)
856 setNeedsStyleRecalc();
857 }
858
makeMutable()859 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable()
860 {
861 return this;
862 }
863
copy() const864 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const
865 {
866 return adoptRef(new CSSMutableStyleDeclaration(0, m_properties));
867 }
868
findPropertyWithId(int propertyID) const869 const CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID) const
870 {
871 for (int n = m_properties.size() - 1 ; n >= 0; --n) {
872 if (propertyID == m_properties[n].m_id)
873 return &m_properties[n];
874 }
875 return 0;
876 }
877
findPropertyWithId(int propertyID)878 CSSProperty* CSSMutableStyleDeclaration::findPropertyWithId(int propertyID)
879 {
880 for (int n = m_properties.size() - 1 ; n >= 0; --n) {
881 if (propertyID == m_properties[n].m_id)
882 return &m_properties[n];
883 }
884 return 0;
885 }
886
887 } // namespace WebCore
888