1 /* 2 * (C) 1999-2003 Lars Knoll (knoll (at) 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 43 CSSMutableStyleDeclaration::CSSMutableStyleDeclaration() 44 : m_node(0) 45 , m_strictParsing(false) 46 #ifndef NDEBUG 47 , m_iteratorCount(0) 48 #endif 49 { 50 } 51 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 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 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 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 108 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 282 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 298 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 325 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 426 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 444 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 462 PassRefPtr<CSSValue> CSSMutableStyleDeclaration::getPropertyCSSValue(int propertyID) const 463 { 464 const CSSProperty* property = findPropertyWithId(propertyID); 465 return property ? property->value() : 0; 466 } 467 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 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 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 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 533 bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const 534 { 535 const CSSProperty* property = findPropertyWithId(propertyID); 536 return property ? property->isImportant() : false; 537 } 538 539 int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const 540 { 541 const CSSProperty* property = findPropertyWithId(propertyID); 542 return property ? property->shorthandID() : 0; 543 } 544 545 bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const 546 { 547 const CSSProperty* property = findPropertyWithId(propertyID); 548 return property ? property->isImplicit() : false; 549 } 550 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 557 String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec) 558 { 559 ec = 0; 560 return removeProperty(propertyID, true, true); 561 } 562 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 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 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 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 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 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 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 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 662 void CSSMutableStyleDeclaration::addParsedProperty(const CSSProperty& property) 663 { 664 ASSERT(!m_iteratorCount); 665 666 setPropertyInternal(property); 667 } 668 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 679 unsigned CSSMutableStyleDeclaration::virtualLength() const 680 { 681 return length(); 682 } 683 684 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 691 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 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 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 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 816 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copyBlockProperties() const 817 { 818 return copyPropertiesInSet(blockProperties, numBlockProperties); 819 } 820 821 void CSSMutableStyleDeclaration::removeBlockProperties() 822 { 823 removePropertiesInSet(blockProperties, numBlockProperties); 824 } 825 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 859 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable() 860 { 861 return this; 862 } 863 864 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const 865 { 866 return adoptRef(new CSSMutableStyleDeclaration(0, m_properties)); 867 } 868 869 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 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