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