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 = ∝
691 else if (prop.id() == CSSPropertyBackgroundPositionY)
692 positionYProp = ∝
693 else if (prop.id() == CSSPropertyBackgroundRepeatX)
694 repeatXProp = ∝
695 else if (prop.id() == CSSPropertyBackgroundRepeatY)
696 repeatYProp = ∝
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