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