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 } 265 return String(); 266 } 267 268 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 284 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 311 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 412 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 430 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 448 PassRefPtr<CSSValue> CSSMutableStyleDeclaration::getPropertyCSSValue(int propertyID) const 449 { 450 const CSSProperty* property = findPropertyWithId(propertyID); 451 return property ? property->value() : 0; 452 } 453 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 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 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 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 519 bool CSSMutableStyleDeclaration::getPropertyPriority(int propertyID) const 520 { 521 const CSSProperty* property = findPropertyWithId(propertyID); 522 return property ? property->isImportant() : false; 523 } 524 525 int CSSMutableStyleDeclaration::getPropertyShorthand(int propertyID) const 526 { 527 const CSSProperty* property = findPropertyWithId(propertyID); 528 return property ? property->shorthandID() : 0; 529 } 530 531 bool CSSMutableStyleDeclaration::isPropertyImplicit(int propertyID) const 532 { 533 const CSSProperty* property = findPropertyWithId(propertyID); 534 return property ? property->isImplicit() : false; 535 } 536 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 543 String CSSMutableStyleDeclaration::removeProperty(int propertyID, ExceptionCode& ec) 544 { 545 ec = 0; 546 return removeProperty(propertyID, true, true); 547 } 548 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 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 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 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 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 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 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 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 648 void CSSMutableStyleDeclaration::addParsedProperty(const CSSProperty& property) 649 { 650 ASSERT(!m_iteratorCount); 651 652 setPropertyInternal(property); 653 } 654 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 665 unsigned CSSMutableStyleDeclaration::virtualLength() const 666 { 667 return length(); 668 } 669 670 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 677 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 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 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 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 802 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copyBlockProperties() const 803 { 804 return copyPropertiesInSet(blockProperties, numBlockProperties); 805 } 806 807 void CSSMutableStyleDeclaration::removeBlockProperties() 808 { 809 removePropertiesInSet(blockProperties, numBlockProperties); 810 } 811 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 845 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::makeMutable() 846 { 847 return this; 848 } 849 850 PassRefPtr<CSSMutableStyleDeclaration> CSSMutableStyleDeclaration::copy() const 851 { 852 return adoptRef(new CSSMutableStyleDeclaration(0, m_properties)); 853 } 854 855 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 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