1 /* 2 * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc. 3 * Copyright (C) 2010, 2011 Google Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include "config.h" 28 #include "core/editing/EditingStyle.h" 29 30 #include "CSSValueKeywords.h" 31 #include "HTMLNames.h" 32 #include "bindings/v8/ExceptionStatePlaceholder.h" 33 #include "core/css/CSSComputedStyleDeclaration.h" 34 #include "core/css/CSSParser.h" 35 #include "core/css/CSSRuleList.h" 36 #include "core/css/CSSStyleRule.h" 37 #include "core/css/CSSValueList.h" 38 #include "core/css/FontSize.h" 39 #include "core/css/StylePropertySet.h" 40 #include "core/css/StyleRule.h" 41 #include "core/css/resolver/StyleResolver.h" 42 #include "core/dom/Element.h" 43 #include "core/dom/Node.h" 44 #include "core/dom/NodeTraversal.h" 45 #include "core/dom/Position.h" 46 #include "core/dom/QualifiedName.h" 47 #include "core/editing/ApplyStyleCommand.h" 48 #include "core/editing/Editor.h" 49 #include "core/editing/FrameSelection.h" 50 #include "core/editing/HTMLInterchange.h" 51 #include "core/editing/htmlediting.h" 52 #include "core/html/HTMLFontElement.h" 53 #include "core/page/Frame.h" 54 #include "core/page/RuntimeCSSEnabled.h" 55 #include "core/rendering/style/RenderStyle.h" 56 57 namespace WebCore { 58 59 // Editing style properties must be preserved during editing operation. 60 // e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph. 61 // NOTE: Use either allEditingProperties() or inheritableEditingProperties() to 62 // respect runtime enabling of properties. 63 static const CSSPropertyID staticEditingProperties[] = { 64 CSSPropertyBackgroundColor, 65 CSSPropertyColor, 66 CSSPropertyFontFamily, 67 CSSPropertyFontSize, 68 CSSPropertyFontStyle, 69 CSSPropertyFontVariant, 70 CSSPropertyFontWeight, 71 CSSPropertyLetterSpacing, 72 CSSPropertyLineHeight, 73 CSSPropertyOrphans, 74 CSSPropertyTextAlign, 75 CSSPropertyTextDecoration, 76 CSSPropertyTextIndent, 77 CSSPropertyTextTransform, 78 CSSPropertyWhiteSpace, 79 CSSPropertyWidows, 80 CSSPropertyWordSpacing, 81 CSSPropertyWebkitTextDecorationsInEffect, 82 CSSPropertyWebkitTextFillColor, 83 CSSPropertyWebkitTextStrokeColor, 84 CSSPropertyWebkitTextStrokeWidth, 85 }; 86 87 enum EditingPropertiesType { OnlyInheritableEditingProperties, AllEditingProperties }; 88 89 static const Vector<CSSPropertyID>& allEditingProperties() 90 { 91 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ()); 92 if (properties.isEmpty()) 93 RuntimeCSSEnabled::filterEnabledCSSPropertiesIntoVector(staticEditingProperties, WTF_ARRAY_LENGTH(staticEditingProperties), properties); 94 return properties; 95 } 96 97 static const Vector<CSSPropertyID>& inheritableEditingProperties() 98 { 99 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ()); 100 if (properties.isEmpty()) { 101 RuntimeCSSEnabled::filterEnabledCSSPropertiesIntoVector(staticEditingProperties, WTF_ARRAY_LENGTH(staticEditingProperties), properties); 102 for (size_t index = 0; index < properties.size();) { 103 if (!CSSProperty::isInheritedProperty(properties[index])) { 104 properties.remove(index); 105 continue; 106 } 107 ++index; 108 } 109 } 110 return properties; 111 } 112 113 template <class StyleDeclarationType> 114 static PassRefPtr<MutableStylePropertySet> copyEditingProperties(StyleDeclarationType* style, EditingPropertiesType type = OnlyInheritableEditingProperties) 115 { 116 if (type == AllEditingProperties) 117 return style->copyPropertiesInSet(allEditingProperties()); 118 return style->copyPropertiesInSet(inheritableEditingProperties()); 119 } 120 121 static inline bool isEditingProperty(int id) 122 { 123 return allEditingProperties().contains(static_cast<CSSPropertyID>(id)); 124 } 125 126 static PassRefPtr<MutableStylePropertySet> editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style, EditingPropertiesType type = OnlyInheritableEditingProperties) 127 { 128 if (!style) 129 return MutableStylePropertySet::create(); 130 return copyEditingProperties(style.get(), type); 131 } 132 133 static PassRefPtr<MutableStylePropertySet> getPropertiesNotIn(StylePropertySet* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle); 134 enum LegacyFontSizeMode { AlwaysUseLegacyFontSize, UseLegacyFontSizeOnlyIfPixelValuesMatch }; 135 static int legacyFontSizeFromCSSValue(Document*, CSSPrimitiveValue*, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode); 136 static bool isTransparentColorValue(CSSValue*); 137 static bool hasTransparentBackgroundColor(CSSStyleDeclaration*); 138 static bool hasTransparentBackgroundColor(StylePropertySet*); 139 static PassRefPtr<CSSValue> backgroundColorInEffect(Node*); 140 141 class HTMLElementEquivalent { 142 WTF_MAKE_FAST_ALLOCATED; 143 public: 144 static PassOwnPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, CSSValueID primitiveValue, const QualifiedName& tagName) 145 { 146 return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName)); 147 } 148 149 virtual ~HTMLElementEquivalent() { } 150 virtual bool matches(const Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); } 151 virtual bool hasAttribute() const { return false; } 152 virtual bool propertyExistsInStyle(const StylePropertySet* style) const { return style->getPropertyCSSValue(m_propertyID); } 153 virtual bool valueIsPresentInStyle(Element*, StylePropertySet*) const; 154 virtual void addToStyle(Element*, EditingStyle*) const; 155 156 protected: 157 HTMLElementEquivalent(CSSPropertyID); 158 HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName); 159 HTMLElementEquivalent(CSSPropertyID, CSSValueID primitiveValue, const QualifiedName& tagName); 160 const CSSPropertyID m_propertyID; 161 const RefPtr<CSSPrimitiveValue> m_primitiveValue; 162 const QualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global. 163 }; 164 165 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id) 166 : m_propertyID(id) 167 , m_tagName(0) 168 { 169 } 170 171 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const QualifiedName& tagName) 172 : m_propertyID(id) 173 , m_tagName(&tagName) 174 { 175 } 176 177 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, CSSValueID primitiveValue, const QualifiedName& tagName) 178 : m_propertyID(id) 179 , m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue)) 180 , m_tagName(&tagName) 181 { 182 ASSERT(primitiveValue != CSSValueInvalid); 183 } 184 185 bool HTMLElementEquivalent::valueIsPresentInStyle(Element* element, StylePropertySet* style) const 186 { 187 RefPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID); 188 return matches(element) && value && value->isPrimitiveValue() && toCSSPrimitiveValue(value.get())->getValueID() == m_primitiveValue->getValueID(); 189 } 190 191 void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const 192 { 193 style->setProperty(m_propertyID, m_primitiveValue->cssText()); 194 } 195 196 class HTMLTextDecorationEquivalent : public HTMLElementEquivalent { 197 public: 198 static PassOwnPtr<HTMLElementEquivalent> create(CSSValueID primitiveValue, const QualifiedName& tagName) 199 { 200 return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName)); 201 } 202 virtual bool propertyExistsInStyle(const StylePropertySet*) const; 203 virtual bool valueIsPresentInStyle(Element*, StylePropertySet*) const; 204 205 private: 206 HTMLTextDecorationEquivalent(CSSValueID primitiveValue, const QualifiedName& tagName); 207 }; 208 209 HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(CSSValueID primitiveValue, const QualifiedName& tagName) 210 : HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName) 211 // m_propertyID is used in HTMLElementEquivalent::addToStyle 212 { 213 } 214 215 bool HTMLTextDecorationEquivalent::propertyExistsInStyle(const StylePropertySet* style) const 216 { 217 return style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect) || style->getPropertyCSSValue(CSSPropertyTextDecoration); 218 } 219 220 bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(Element* element, StylePropertySet* style) const 221 { 222 RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); 223 if (!styleValue) 224 styleValue = style->getPropertyCSSValue(CSSPropertyTextDecoration); 225 return matches(element) && styleValue && styleValue->isValueList() && toCSSValueList(styleValue.get())->hasValue(m_primitiveValue.get()); 226 } 227 228 class HTMLAttributeEquivalent : public HTMLElementEquivalent { 229 public: 230 static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& tagName, const QualifiedName& attrName) 231 { 232 return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName)); 233 } 234 static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName) 235 { 236 return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName)); 237 } 238 239 bool matches(const Element* elem) const { return HTMLElementEquivalent::matches(elem) && elem->hasAttribute(m_attrName); } 240 virtual bool hasAttribute() const { return true; } 241 virtual bool valueIsPresentInStyle(Element*, StylePropertySet*) const; 242 virtual void addToStyle(Element*, EditingStyle*) const; 243 virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const; 244 inline const QualifiedName& attributeName() const { return m_attrName; } 245 246 protected: 247 HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName); 248 HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName); 249 const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global. 250 }; 251 252 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& tagName, const QualifiedName& attrName) 253 : HTMLElementEquivalent(id, tagName) 254 , m_attrName(attrName) 255 { 256 } 257 258 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName) 259 : HTMLElementEquivalent(id) 260 , m_attrName(attrName) 261 { 262 } 263 264 bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element* element, StylePropertySet* style) const 265 { 266 RefPtr<CSSValue> value = attributeValueAsCSSValue(element); 267 RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID); 268 269 return compareCSSValuePtr(value, styleValue); 270 } 271 272 void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const 273 { 274 if (RefPtr<CSSValue> value = attributeValueAsCSSValue(element)) 275 style->setProperty(m_propertyID, value->cssText()); 276 } 277 278 PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const 279 { 280 ASSERT(element); 281 if (!element->hasAttribute(m_attrName)) 282 return 0; 283 284 RefPtr<MutableStylePropertySet> dummyStyle; 285 dummyStyle = MutableStylePropertySet::create(); 286 dummyStyle->setProperty(m_propertyID, element->getAttribute(m_attrName)); 287 return dummyStyle->getPropertyCSSValue(m_propertyID); 288 } 289 290 class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent { 291 public: 292 static PassOwnPtr<HTMLFontSizeEquivalent> create() 293 { 294 return adoptPtr(new HTMLFontSizeEquivalent()); 295 } 296 virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const; 297 298 private: 299 HTMLFontSizeEquivalent(); 300 }; 301 302 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent() 303 : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr) 304 { 305 } 306 307 PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const 308 { 309 ASSERT(element); 310 if (!element->hasAttribute(m_attrName)) 311 return 0; 312 CSSValueID size; 313 if (!HTMLFontElement::cssValueFromFontSizeNumber(element->getAttribute(m_attrName), size)) 314 return 0; 315 return CSSPrimitiveValue::createIdentifier(size); 316 } 317 318 float EditingStyle::NoFontDelta = 0.0f; 319 320 EditingStyle::EditingStyle() 321 : m_shouldUseFixedDefaultFontSize(false) 322 , m_fontSizeDelta(NoFontDelta) 323 { 324 } 325 326 EditingStyle::EditingStyle(Node* node, PropertiesToInclude propertiesToInclude) 327 : m_shouldUseFixedDefaultFontSize(false) 328 , m_fontSizeDelta(NoFontDelta) 329 { 330 init(node, propertiesToInclude); 331 } 332 333 EditingStyle::EditingStyle(const Position& position, PropertiesToInclude propertiesToInclude) 334 : m_shouldUseFixedDefaultFontSize(false) 335 , m_fontSizeDelta(NoFontDelta) 336 { 337 init(position.deprecatedNode(), propertiesToInclude); 338 } 339 340 EditingStyle::EditingStyle(const StylePropertySet* style) 341 : m_mutableStyle(style ? style->mutableCopy() : 0) 342 , m_shouldUseFixedDefaultFontSize(false) 343 , m_fontSizeDelta(NoFontDelta) 344 { 345 extractFontSizeDelta(); 346 } 347 348 EditingStyle::EditingStyle(const CSSStyleDeclaration* style) 349 : m_mutableStyle(style ? style->copyProperties() : 0) 350 , m_shouldUseFixedDefaultFontSize(false) 351 , m_fontSizeDelta(NoFontDelta) 352 { 353 extractFontSizeDelta(); 354 } 355 356 EditingStyle::EditingStyle(CSSPropertyID propertyID, const String& value) 357 : m_mutableStyle(0) 358 , m_shouldUseFixedDefaultFontSize(false) 359 , m_fontSizeDelta(NoFontDelta) 360 { 361 setProperty(propertyID, value); 362 } 363 364 EditingStyle::~EditingStyle() 365 { 366 } 367 368 static RGBA32 cssValueToRGBA(CSSValue* colorValue) 369 { 370 if (!colorValue || !colorValue->isPrimitiveValue()) 371 return Color::transparent; 372 373 CSSPrimitiveValue* primitiveColor = toCSSPrimitiveValue(colorValue); 374 if (primitiveColor->isRGBColor()) 375 return primitiveColor->getRGBA32Value(); 376 377 RGBA32 rgba = 0; 378 CSSParser::parseColor(rgba, colorValue->cssText()); 379 return rgba; 380 } 381 382 static inline RGBA32 getRGBAFontColor(CSSStyleDeclaration* style) 383 { 384 return cssValueToRGBA(style->getPropertyCSSValueInternal(CSSPropertyColor).get()); 385 } 386 387 static inline RGBA32 getRGBAFontColor(StylePropertySet* style) 388 { 389 return cssValueToRGBA(style->getPropertyCSSValue(CSSPropertyColor).get()); 390 } 391 392 static inline RGBA32 getRGBABackgroundColor(CSSStyleDeclaration* style) 393 { 394 return cssValueToRGBA(style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor).get()); 395 } 396 397 static inline RGBA32 getRGBABackgroundColor(StylePropertySet* style) 398 { 399 return cssValueToRGBA(style->getPropertyCSSValue(CSSPropertyBackgroundColor).get()); 400 } 401 402 static inline RGBA32 rgbaBackgroundColorInEffect(Node* node) 403 { 404 return cssValueToRGBA(backgroundColorInEffect(node).get()); 405 } 406 407 static int textAlignResolvingStartAndEnd(int textAlign, int direction) 408 { 409 switch (textAlign) { 410 case CSSValueCenter: 411 case CSSValueWebkitCenter: 412 return CSSValueCenter; 413 case CSSValueJustify: 414 return CSSValueJustify; 415 case CSSValueLeft: 416 case CSSValueWebkitLeft: 417 return CSSValueLeft; 418 case CSSValueRight: 419 case CSSValueWebkitRight: 420 return CSSValueRight; 421 case CSSValueStart: 422 return direction != CSSValueRtl ? CSSValueLeft : CSSValueRight; 423 case CSSValueEnd: 424 return direction == CSSValueRtl ? CSSValueRight : CSSValueLeft; 425 } 426 return CSSValueInvalid; 427 } 428 429 template<typename T> 430 static int textAlignResolvingStartAndEnd(T* style) 431 { 432 return textAlignResolvingStartAndEnd(getIdentifierValue(style, CSSPropertyTextAlign), getIdentifierValue(style, CSSPropertyDirection)); 433 } 434 435 void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude) 436 { 437 if (isTabSpanTextNode(node)) 438 node = tabSpanNode(node)->parentNode(); 439 else if (isTabSpanNode(node)) 440 node = node->parentNode(); 441 442 RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = CSSComputedStyleDeclaration::create(node); 443 m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copyProperties() : editingStyleFromComputedStyle(computedStyleAtPosition); 444 445 if (propertiesToInclude == EditingPropertiesInEffect) { 446 if (RefPtr<CSSValue> value = backgroundColorInEffect(node)) 447 m_mutableStyle->setProperty(CSSPropertyBackgroundColor, value->cssText()); 448 if (RefPtr<CSSValue> value = computedStyleAtPosition->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect)) 449 m_mutableStyle->setProperty(CSSPropertyTextDecoration, value->cssText()); 450 } 451 452 if (node && node->computedStyle()) { 453 RenderStyle* renderStyle = node->computedStyle(); 454 removeTextFillAndStrokeColorsIfNeeded(renderStyle); 455 replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get()); 456 } 457 458 m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize(); 459 extractFontSizeDelta(); 460 } 461 462 void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle) 463 { 464 // If a node's text fill color is invalid, then its children use 465 // their font-color as their text fill color (they don't 466 // inherit it). Likewise for stroke color. 467 if (!renderStyle->textFillColor().isValid()) 468 m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor); 469 if (!renderStyle->textStrokeColor().isValid()) 470 m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor); 471 } 472 473 void EditingStyle::setProperty(CSSPropertyID propertyID, const String& value, bool important) 474 { 475 if (!m_mutableStyle) 476 m_mutableStyle = MutableStylePropertySet::create(); 477 478 m_mutableStyle->setProperty(propertyID, value, important); 479 } 480 481 void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle) 482 { 483 ASSERT(renderStyle); 484 if (renderStyle->fontDescription().keywordSize()) 485 m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText()); 486 } 487 488 void EditingStyle::extractFontSizeDelta() 489 { 490 if (!m_mutableStyle) 491 return; 492 493 if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) { 494 // Explicit font size overrides any delta. 495 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta); 496 return; 497 } 498 499 // Get the adjustment amount out of the style. 500 RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta); 501 if (!value || !value->isPrimitiveValue()) 502 return; 503 504 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value.get()); 505 506 // Only PX handled now. If we handle more types in the future, perhaps 507 // a switch statement here would be more appropriate. 508 if (!primitiveValue->isPx()) 509 return; 510 511 m_fontSizeDelta = primitiveValue->getFloatValue(); 512 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta); 513 } 514 515 bool EditingStyle::isEmpty() const 516 { 517 return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta; 518 } 519 520 bool EditingStyle::textDirection(WritingDirection& writingDirection) const 521 { 522 if (!m_mutableStyle) 523 return false; 524 525 RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); 526 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue()) 527 return false; 528 529 CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID(); 530 if (unicodeBidiValue == CSSValueEmbed) { 531 RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection); 532 if (!direction || !direction->isPrimitiveValue()) 533 return false; 534 535 writingDirection = toCSSPrimitiveValue(direction.get())->getValueID() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection; 536 537 return true; 538 } 539 540 if (unicodeBidiValue == CSSValueNormal) { 541 writingDirection = NaturalWritingDirection; 542 return true; 543 } 544 545 return false; 546 } 547 548 void EditingStyle::setStyle(PassRefPtr<MutableStylePropertySet> style) 549 { 550 m_mutableStyle = style; 551 // FIXME: We should be able to figure out whether or not font is fixed width for mutable style. 552 // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here. 553 m_shouldUseFixedDefaultFontSize = false; 554 extractFontSizeDelta(); 555 } 556 557 void EditingStyle::overrideWithStyle(const StylePropertySet* style) 558 { 559 if (!style || style->isEmpty()) 560 return; 561 if (!m_mutableStyle) 562 m_mutableStyle = MutableStylePropertySet::create(); 563 m_mutableStyle->mergeAndOverrideOnConflict(style); 564 extractFontSizeDelta(); 565 } 566 567 void EditingStyle::clear() 568 { 569 m_mutableStyle.clear(); 570 m_shouldUseFixedDefaultFontSize = false; 571 m_fontSizeDelta = NoFontDelta; 572 } 573 574 PassRefPtr<EditingStyle> EditingStyle::copy() const 575 { 576 RefPtr<EditingStyle> copy = EditingStyle::create(); 577 if (m_mutableStyle) 578 copy->m_mutableStyle = m_mutableStyle->mutableCopy(); 579 copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize; 580 copy->m_fontSizeDelta = m_fontSizeDelta; 581 return copy; 582 } 583 584 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties() 585 { 586 RefPtr<EditingStyle> blockProperties = EditingStyle::create(); 587 if (!m_mutableStyle) 588 return blockProperties; 589 590 blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties(); 591 m_mutableStyle->removeBlockProperties(); 592 593 return blockProperties; 594 } 595 596 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection() 597 { 598 RefPtr<EditingStyle> textDirection = EditingStyle::create(); 599 textDirection->m_mutableStyle = MutableStylePropertySet::create(); 600 textDirection->m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed, m_mutableStyle->propertyIsImportant(CSSPropertyUnicodeBidi)); 601 textDirection->m_mutableStyle->setProperty(CSSPropertyDirection, m_mutableStyle->getPropertyValue(CSSPropertyDirection), 602 m_mutableStyle->propertyIsImportant(CSSPropertyDirection)); 603 604 m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi); 605 m_mutableStyle->removeProperty(CSSPropertyDirection); 606 607 return textDirection; 608 } 609 610 void EditingStyle::removeBlockProperties() 611 { 612 if (!m_mutableStyle) 613 return; 614 615 m_mutableStyle->removeBlockProperties(); 616 } 617 618 void EditingStyle::removeStyleAddedByNode(Node* node) 619 { 620 if (!node || !node->parentNode()) 621 return; 622 RefPtr<MutableStylePropertySet> parentStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(node->parentNode()), AllEditingProperties); 623 RefPtr<MutableStylePropertySet> nodeStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(node), AllEditingProperties); 624 nodeStyle->removeEquivalentProperties(parentStyle->ensureCSSStyleDeclaration()); 625 m_mutableStyle->removeEquivalentProperties(nodeStyle->ensureCSSStyleDeclaration()); 626 } 627 628 void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node) 629 { 630 if (!node || !node->parentNode() || !m_mutableStyle) 631 return; 632 633 RefPtr<MutableStylePropertySet> parentStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(node->parentNode()), AllEditingProperties); 634 RefPtr<MutableStylePropertySet> nodeStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(node), AllEditingProperties); 635 nodeStyle->removeEquivalentProperties(parentStyle->ensureCSSStyleDeclaration()); 636 637 unsigned propertyCount = nodeStyle->propertyCount(); 638 for (unsigned i = 0; i < propertyCount; ++i) 639 m_mutableStyle->removeProperty(nodeStyle->propertyAt(i).id()); 640 } 641 642 void EditingStyle::collapseTextDecorationProperties() 643 { 644 if (!m_mutableStyle) 645 return; 646 647 RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); 648 if (!textDecorationsInEffect) 649 return; 650 651 if (textDecorationsInEffect->isValueList()) 652 m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->propertyIsImportant(CSSPropertyTextDecoration)); 653 else 654 m_mutableStyle->removeProperty(CSSPropertyTextDecoration); 655 m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect); 656 } 657 658 // CSS properties that create a visual difference only when applied to text. 659 static const CSSPropertyID textOnlyProperties[] = { 660 CSSPropertyTextDecoration, 661 CSSPropertyWebkitTextDecorationsInEffect, 662 CSSPropertyFontStyle, 663 CSSPropertyFontWeight, 664 CSSPropertyColor, 665 }; 666 667 TriState EditingStyle::triStateOfStyle(EditingStyle* style) const 668 { 669 if (!style || !style->m_mutableStyle) 670 return FalseTriState; 671 return triStateOfStyle(style->m_mutableStyle->ensureCSSStyleDeclaration(), DoNotIgnoreTextOnlyProperties); 672 } 673 674 TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const 675 { 676 RefPtr<MutableStylePropertySet> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare); 677 678 if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties) 679 difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties)); 680 681 if (difference->isEmpty()) 682 return TrueTriState; 683 if (difference->propertyCount() == m_mutableStyle->propertyCount()) 684 return FalseTriState; 685 686 return MixedTriState; 687 } 688 689 TriState EditingStyle::triStateOfStyle(const VisibleSelection& selection) const 690 { 691 if (!selection.isCaretOrRange()) 692 return FalseTriState; 693 694 if (selection.isCaret()) 695 return triStateOfStyle(EditingStyle::styleAtSelectionStart(selection).get()); 696 697 TriState state = FalseTriState; 698 bool nodeIsStart = true; 699 for (Node* node = selection.start().deprecatedNode(); node; node = NodeTraversal::next(node)) { 700 if (node->renderer() && node->rendererIsEditable()) { 701 RefPtr<CSSComputedStyleDeclaration> nodeStyle = CSSComputedStyleDeclaration::create(node); 702 if (nodeStyle) { 703 TriState nodeState = triStateOfStyle(nodeStyle.get(), node->isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties : EditingStyle::IgnoreTextOnlyProperties); 704 if (nodeIsStart) { 705 state = nodeState; 706 nodeIsStart = false; 707 } else if (state != nodeState && node->isTextNode()) { 708 state = MixedTriState; 709 break; 710 } 711 } 712 } 713 if (node == selection.end().deprecatedNode()) 714 break; 715 } 716 717 return state; 718 } 719 720 bool EditingStyle::conflictsWithInlineStyleOfElement(Element* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const 721 { 722 ASSERT(element); 723 ASSERT(!conflictingProperties || conflictingProperties->isEmpty()); 724 725 const StylePropertySet* inlineStyle = element->inlineStyle(); 726 if (!m_mutableStyle || !inlineStyle) 727 return false; 728 729 unsigned propertyCount = m_mutableStyle->propertyCount(); 730 for (unsigned i = 0; i < propertyCount; ++i) { 731 CSSPropertyID propertyID = m_mutableStyle->propertyAt(i).id(); 732 733 // We don't override whitespace property of a tab span because that would collapse the tab into a space. 734 if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element)) 735 continue; 736 737 if (propertyID == CSSPropertyWebkitTextDecorationsInEffect && inlineStyle->getPropertyCSSValue(CSSPropertyTextDecoration)) { 738 if (!conflictingProperties) 739 return true; 740 conflictingProperties->append(CSSPropertyTextDecoration); 741 if (extractedStyle) 742 extractedStyle->setProperty(CSSPropertyTextDecoration, inlineStyle->getPropertyValue(CSSPropertyTextDecoration), inlineStyle->propertyIsImportant(CSSPropertyTextDecoration)); 743 continue; 744 } 745 746 if (!inlineStyle->getPropertyCSSValue(propertyID)) 747 continue; 748 749 if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) { 750 if (!conflictingProperties) 751 return true; 752 conflictingProperties->append(CSSPropertyDirection); 753 if (extractedStyle) 754 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID)); 755 } 756 757 if (!conflictingProperties) 758 return true; 759 760 conflictingProperties->append(propertyID); 761 762 if (extractedStyle) 763 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID)); 764 } 765 766 return conflictingProperties && !conflictingProperties->isEmpty(); 767 } 768 769 static const Vector<OwnPtr<HTMLElementEquivalent> >& htmlElementEquivalents() 770 { 771 DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLElementEquivalent> >, HTMLElementEquivalents, ()); 772 773 if (!HTMLElementEquivalents.size()) { 774 HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag)); 775 HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag)); 776 HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag)); 777 HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag)); 778 HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag)); 779 HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag)); 780 781 HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag)); 782 HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag)); 783 HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag)); 784 } 785 786 return HTMLElementEquivalents; 787 } 788 789 790 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const 791 { 792 if (!m_mutableStyle) 793 return false; 794 795 const Vector<OwnPtr<HTMLElementEquivalent> >& HTMLElementEquivalents = htmlElementEquivalents(); 796 for (size_t i = 0; i < HTMLElementEquivalents.size(); ++i) { 797 const HTMLElementEquivalent* equivalent = HTMLElementEquivalents[i].get(); 798 if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get()) 799 && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) { 800 if (extractedStyle) 801 equivalent->addToStyle(element, extractedStyle); 802 return true; 803 } 804 } 805 return false; 806 } 807 808 static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents() 809 { 810 DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ()); 811 812 if (!HTMLAttributeEquivalents.size()) { 813 // elementIsStyledSpanOrHTMLEquivalent depends on the fact each HTMLAttriuteEquivalent matches exactly one attribute 814 // of exactly one element except dirAttr. 815 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr)); 816 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr)); 817 HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create()); 818 819 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr)); 820 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr)); 821 } 822 823 return HTMLAttributeEquivalents; 824 } 825 826 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const 827 { 828 ASSERT(element); 829 if (!m_mutableStyle) 830 return false; 831 832 const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents(); 833 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) { 834 if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get()) 835 && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get())) 836 return true; 837 } 838 839 return false; 840 } 841 842 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection, 843 EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const 844 { 845 ASSERT(element); 846 // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties 847 ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection); 848 if (!m_mutableStyle) 849 return false; 850 851 const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents(); 852 bool removed = false; 853 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) { 854 const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get(); 855 856 // unicode-bidi and direction are pushed down separately so don't push down with other styles. 857 if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr) 858 continue; 859 860 if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get()) 861 || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) 862 continue; 863 864 if (extractedStyle) 865 equivalent->addToStyle(element, extractedStyle); 866 conflictingAttributes.append(equivalent->attributeName()); 867 removed = true; 868 } 869 870 return removed; 871 } 872 873 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const 874 { 875 return !m_mutableStyle || getPropertiesNotIn(m_mutableStyle.get(), CSSComputedStyleDeclaration::create(node).get())->isEmpty(); 876 } 877 878 bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement* element) 879 { 880 bool elementIsSpanOrElementEquivalent = false; 881 if (element->hasTagName(HTMLNames::spanTag)) 882 elementIsSpanOrElementEquivalent = true; 883 else { 884 const Vector<OwnPtr<HTMLElementEquivalent> >& HTMLElementEquivalents = htmlElementEquivalents(); 885 size_t i; 886 for (i = 0; i < HTMLElementEquivalents.size(); ++i) { 887 if (HTMLElementEquivalents[i]->matches(element)) { 888 elementIsSpanOrElementEquivalent = true; 889 break; 890 } 891 } 892 } 893 894 if (!element->hasAttributes()) 895 return elementIsSpanOrElementEquivalent; // span, b, etc... without any attributes 896 897 unsigned matchedAttributes = 0; 898 const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents(); 899 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) { 900 if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->attributeName() != HTMLNames::dirAttr) 901 matchedAttributes++; 902 } 903 904 if (!elementIsSpanOrElementEquivalent && !matchedAttributes) 905 return false; // element is not a span, a html element equivalent, or font element. 906 907 if (element->getAttribute(HTMLNames::classAttr) == AppleStyleSpanClass) 908 matchedAttributes++; 909 910 if (element->hasAttribute(HTMLNames::styleAttr)) { 911 if (const StylePropertySet* style = element->inlineStyle()) { 912 unsigned propertyCount = style->propertyCount(); 913 for (unsigned i = 0; i < propertyCount; ++i) { 914 if (!isEditingProperty(style->propertyAt(i).id())) 915 return false; 916 } 917 } 918 matchedAttributes++; 919 } 920 921 // font with color attribute, span with style attribute, etc... 922 ASSERT(matchedAttributes <= element->attributeCount()); 923 return matchedAttributes >= element->attributeCount(); 924 } 925 926 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection) 927 { 928 if (!m_mutableStyle) 929 return; 930 931 // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style. 932 // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate 933 // which one of editingStyleAtPosition or computedStyle is called. 934 RefPtr<EditingStyle> editingStyleAtPosition = EditingStyle::create(position, EditingPropertiesInEffect); 935 StylePropertySet* styleAtPosition = editingStyleAtPosition->m_mutableStyle.get(); 936 937 RefPtr<CSSValue> unicodeBidi; 938 RefPtr<CSSValue> direction; 939 if (shouldPreserveWritingDirection == PreserveWritingDirection) { 940 unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); 941 direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection); 942 } 943 944 m_mutableStyle->removeEquivalentProperties(styleAtPosition); 945 946 if (textAlignResolvingStartAndEnd(m_mutableStyle.get()) == textAlignResolvingStartAndEnd(styleAtPosition)) 947 m_mutableStyle->removeProperty(CSSPropertyTextAlign); 948 949 if (getRGBAFontColor(m_mutableStyle.get()) == getRGBAFontColor(styleAtPosition)) 950 m_mutableStyle->removeProperty(CSSPropertyColor); 951 952 if (hasTransparentBackgroundColor(m_mutableStyle.get()) 953 || cssValueToRGBA(m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor).get()) == rgbaBackgroundColorInEffect(position.containerNode())) 954 m_mutableStyle->removeProperty(CSSPropertyBackgroundColor); 955 956 if (unicodeBidi && unicodeBidi->isPrimitiveValue()) { 957 m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, toCSSPrimitiveValue(unicodeBidi.get())->getValueID()); 958 if (direction && direction->isPrimitiveValue()) 959 m_mutableStyle->setProperty(CSSPropertyDirection, toCSSPrimitiveValue(direction.get())->getValueID()); 960 } 961 } 962 963 void EditingStyle::mergeTypingStyle(Document* document) 964 { 965 ASSERT(document); 966 967 RefPtr<EditingStyle> typingStyle = document->frame()->selection()->typingStyle(); 968 if (!typingStyle || typingStyle == this) 969 return; 970 971 mergeStyle(typingStyle->style(), OverrideValues); 972 } 973 974 void EditingStyle::mergeInlineStyleOfElement(Element* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude) 975 { 976 ASSERT(element); 977 if (!element->inlineStyle()) 978 return; 979 980 switch (propertiesToInclude) { 981 case AllProperties: 982 mergeStyle(element->inlineStyle(), mode); 983 return; 984 case OnlyEditingInheritableProperties: 985 mergeStyle(copyEditingProperties(element->inlineStyle(), OnlyInheritableEditingProperties).get(), mode); 986 return; 987 case EditingPropertiesInEffect: 988 mergeStyle(copyEditingProperties(element->inlineStyle(), AllEditingProperties).get(), mode); 989 return; 990 } 991 } 992 993 static inline bool elementMatchesAndPropertyIsNotInInlineStyleDecl(const HTMLElementEquivalent* equivalent, const Element* element, 994 EditingStyle::CSSPropertyOverrideMode mode, StylePropertySet* style) 995 { 996 return equivalent->matches(element) && (!element->inlineStyle() || !equivalent->propertyExistsInStyle(element->inlineStyle())) 997 && (mode == EditingStyle::OverrideValues || !equivalent->propertyExistsInStyle(style)); 998 } 999 1000 static PassRefPtr<MutableStylePropertySet> extractEditingProperties(const StylePropertySet* style, EditingStyle::PropertiesToInclude propertiesToInclude) 1001 { 1002 if (!style) 1003 return 0; 1004 1005 switch (propertiesToInclude) { 1006 case EditingStyle::AllProperties: 1007 case EditingStyle::EditingPropertiesInEffect: 1008 return copyEditingProperties(style, AllEditingProperties); 1009 case EditingStyle::OnlyEditingInheritableProperties: 1010 return copyEditingProperties(style, OnlyInheritableEditingProperties); 1011 } 1012 1013 ASSERT_NOT_REACHED(); 1014 return 0; 1015 } 1016 1017 void EditingStyle::mergeInlineAndImplicitStyleOfElement(Element* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude) 1018 { 1019 RefPtr<EditingStyle> styleFromRules = EditingStyle::create(); 1020 styleFromRules->mergeStyleFromRulesForSerialization(element); 1021 styleFromRules->m_mutableStyle = extractEditingProperties(styleFromRules->m_mutableStyle.get(), propertiesToInclude); 1022 mergeStyle(styleFromRules->m_mutableStyle.get(), mode); 1023 1024 mergeInlineStyleOfElement(element, mode, propertiesToInclude); 1025 1026 const Vector<OwnPtr<HTMLElementEquivalent> >& elementEquivalents = htmlElementEquivalents(); 1027 for (size_t i = 0; i < elementEquivalents.size(); ++i) { 1028 if (elementMatchesAndPropertyIsNotInInlineStyleDecl(elementEquivalents[i].get(), element, mode, m_mutableStyle.get())) 1029 elementEquivalents[i]->addToStyle(element, this); 1030 } 1031 1032 const Vector<OwnPtr<HTMLAttributeEquivalent> >& attributeEquivalents = htmlAttributeEquivalents(); 1033 for (size_t i = 0; i < attributeEquivalents.size(); ++i) { 1034 if (attributeEquivalents[i]->attributeName() == HTMLNames::dirAttr) 1035 continue; // We don't want to include directionality 1036 if (elementMatchesAndPropertyIsNotInInlineStyleDecl(attributeEquivalents[i].get(), element, mode, m_mutableStyle.get())) 1037 attributeEquivalents[i]->addToStyle(element, this); 1038 } 1039 } 1040 1041 PassRefPtr<EditingStyle> EditingStyle::wrappingStyleForSerialization(Node* context, bool shouldAnnotate) 1042 { 1043 RefPtr<EditingStyle> wrappingStyle; 1044 if (shouldAnnotate) { 1045 wrappingStyle = EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect); 1046 1047 // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote, 1048 // to help us differentiate those styles from ones that the user has applied. 1049 // This helps us get the color of content pasted into blockquotes right. 1050 wrappingStyle->removeStyleAddedByNode(enclosingNodeOfType(firstPositionInOrBeforeNode(context), isMailBlockquote, CanCrossEditingBoundary)); 1051 1052 // Call collapseTextDecorationProperties first or otherwise it'll copy the value over from in-effect to text-decorations. 1053 wrappingStyle->collapseTextDecorationProperties(); 1054 1055 return wrappingStyle.release(); 1056 } 1057 1058 wrappingStyle = EditingStyle::create(); 1059 1060 // When not annotating for interchange, we only preserve inline style declarations. 1061 for (Node* node = context; node && !node->isDocumentNode(); node = node->parentNode()) { 1062 if (node->isStyledElement() && !isMailBlockquote(node)) { 1063 wrappingStyle->mergeInlineAndImplicitStyleOfElement(toElement(node), EditingStyle::DoNotOverrideValues, 1064 EditingStyle::EditingPropertiesInEffect); 1065 } 1066 } 1067 1068 return wrappingStyle.release(); 1069 } 1070 1071 1072 static void mergeTextDecorationValues(CSSValueList* mergedValue, const CSSValueList* valueToMerge) 1073 { 1074 DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline))); 1075 DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough))); 1076 1077 if (valueToMerge->hasValue(underline.get()) && !mergedValue->hasValue(underline.get())) 1078 mergedValue->append(underline.get()); 1079 1080 if (valueToMerge->hasValue(lineThrough.get()) && !mergedValue->hasValue(lineThrough.get())) 1081 mergedValue->append(lineThrough.get()); 1082 } 1083 1084 void EditingStyle::mergeStyle(const StylePropertySet* style, CSSPropertyOverrideMode mode) 1085 { 1086 if (!style) 1087 return; 1088 1089 if (!m_mutableStyle) { 1090 m_mutableStyle = style->mutableCopy(); 1091 return; 1092 } 1093 1094 unsigned propertyCount = style->propertyCount(); 1095 for (unsigned i = 0; i < propertyCount; ++i) { 1096 StylePropertySet::PropertyReference property = style->propertyAt(i); 1097 RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(property.id()); 1098 1099 // text decorations never override values 1100 if ((property.id() == CSSPropertyTextDecoration || property.id() == CSSPropertyWebkitTextDecorationsInEffect) && property.value()->isValueList() && value) { 1101 if (value->isValueList()) { 1102 mergeTextDecorationValues(toCSSValueList(value.get()), toCSSValueList(property.value())); 1103 continue; 1104 } 1105 value = 0; // text-decoration: none is equivalent to not having the property 1106 } 1107 1108 if (mode == OverrideValues || (mode == DoNotOverrideValues && !value)) 1109 m_mutableStyle->setProperty(property.id(), property.value()->cssText(), property.isImportant()); 1110 } 1111 } 1112 1113 static PassRefPtr<MutableStylePropertySet> styleFromMatchedRulesForElement(Element* element, unsigned rulesToInclude) 1114 { 1115 RefPtr<MutableStylePropertySet> style = MutableStylePropertySet::create(); 1116 RefPtr<CSSRuleList> matchedRules = element->document()->styleResolver()->styleRulesForElement(element, rulesToInclude); 1117 if (matchedRules) { 1118 for (unsigned i = 0; i < matchedRules->length(); i++) { 1119 if (matchedRules->item(i)->type() == CSSRule::STYLE_RULE) 1120 style->mergeAndOverrideOnConflict(static_cast<CSSStyleRule*>(matchedRules->item(i))->styleRule()->properties()); 1121 } 1122 } 1123 1124 return style.release(); 1125 } 1126 1127 void EditingStyle::mergeStyleFromRules(Element* element) 1128 { 1129 RefPtr<MutableStylePropertySet> styleFromMatchedRules = styleFromMatchedRulesForElement(element, 1130 StyleResolver::AuthorCSSRules | StyleResolver::CrossOriginCSSRules); 1131 // Styles from the inline style declaration, held in the variable "style", take precedence 1132 // over those from matched rules. 1133 if (m_mutableStyle) 1134 styleFromMatchedRules->mergeAndOverrideOnConflict(m_mutableStyle.get()); 1135 1136 clear(); 1137 m_mutableStyle = styleFromMatchedRules; 1138 } 1139 1140 void EditingStyle::mergeStyleFromRulesForSerialization(Element* element) 1141 { 1142 mergeStyleFromRules(element); 1143 1144 // The property value, if it's a percentage, may not reflect the actual computed value. 1145 // For example: style="height: 1%; overflow: visible;" in quirksmode 1146 // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem 1147 RefPtr<CSSComputedStyleDeclaration> computedStyleForElement = CSSComputedStyleDeclaration::create(element); 1148 RefPtr<MutableStylePropertySet> fromComputedStyle = MutableStylePropertySet::create(); 1149 { 1150 unsigned propertyCount = m_mutableStyle->propertyCount(); 1151 for (unsigned i = 0; i < propertyCount; ++i) { 1152 StylePropertySet::PropertyReference property = m_mutableStyle->propertyAt(i); 1153 CSSValue* value = property.value(); 1154 if (!value->isPrimitiveValue()) 1155 continue; 1156 if (toCSSPrimitiveValue(value)->isPercentage()) { 1157 if (RefPtr<CSSValue> computedPropertyValue = computedStyleForElement->getPropertyCSSValue(property.id())) 1158 fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue)); 1159 } 1160 } 1161 } 1162 m_mutableStyle->mergeAndOverrideOnConflict(fromComputedStyle.get()); 1163 } 1164 1165 static void removePropertiesInStyle(MutableStylePropertySet* styleToRemovePropertiesFrom, StylePropertySet* style) 1166 { 1167 unsigned propertyCount = style->propertyCount(); 1168 Vector<CSSPropertyID> propertiesToRemove(propertyCount); 1169 for (unsigned i = 0; i < propertyCount; ++i) 1170 propertiesToRemove[i] = style->propertyAt(i).id(); 1171 1172 styleToRemovePropertiesFrom->removePropertiesInSet(propertiesToRemove.data(), propertiesToRemove.size()); 1173 } 1174 1175 void EditingStyle::removeStyleFromRulesAndContext(Element* element, Node* context) 1176 { 1177 ASSERT(element); 1178 if (!m_mutableStyle) 1179 return; 1180 1181 // 1. Remove style from matched rules because style remain without repeating it in inline style declaration 1182 RefPtr<MutableStylePropertySet> styleFromMatchedRules = styleFromMatchedRulesForElement(element, StyleResolver::AllButEmptyCSSRules); 1183 if (styleFromMatchedRules && !styleFromMatchedRules->isEmpty()) 1184 m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), styleFromMatchedRules->ensureCSSStyleDeclaration()); 1185 1186 // 2. Remove style present in context and not overriden by matched rules. 1187 RefPtr<EditingStyle> computedStyle = EditingStyle::create(context, EditingPropertiesInEffect); 1188 if (computedStyle->m_mutableStyle) { 1189 if (!computedStyle->m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor)) 1190 computedStyle->m_mutableStyle->setProperty(CSSPropertyBackgroundColor, CSSValueTransparent); 1191 1192 removePropertiesInStyle(computedStyle->m_mutableStyle.get(), styleFromMatchedRules.get()); 1193 m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), computedStyle->m_mutableStyle->ensureCSSStyleDeclaration()); 1194 } 1195 1196 // 3. If this element is a span and has display: inline or float: none, remove them unless they are overriden by rules. 1197 // These rules are added by serialization code to wrap text nodes. 1198 if (isStyleSpanOrSpanWithOnlyStyleAttribute(element)) { 1199 if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyDisplay) && getIdentifierValue(m_mutableStyle.get(), CSSPropertyDisplay) == CSSValueInline) 1200 m_mutableStyle->removeProperty(CSSPropertyDisplay); 1201 if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyFloat) && getIdentifierValue(m_mutableStyle.get(), CSSPropertyFloat) == CSSValueNone) 1202 m_mutableStyle->removeProperty(CSSPropertyFloat); 1203 } 1204 } 1205 1206 void EditingStyle::removePropertiesInElementDefaultStyle(Element* element) 1207 { 1208 if (!m_mutableStyle || m_mutableStyle->isEmpty()) 1209 return; 1210 1211 RefPtr<StylePropertySet> defaultStyle = styleFromMatchedRulesForElement(element, StyleResolver::UAAndUserCSSRules); 1212 1213 removePropertiesInStyle(m_mutableStyle.get(), defaultStyle.get()); 1214 } 1215 1216 void EditingStyle::forceInline() 1217 { 1218 if (!m_mutableStyle) 1219 m_mutableStyle = MutableStylePropertySet::create(); 1220 const bool propertyIsImportant = true; 1221 m_mutableStyle->setProperty(CSSPropertyDisplay, CSSValueInline, propertyIsImportant); 1222 } 1223 1224 int EditingStyle::legacyFontSize(Document* document) const 1225 { 1226 RefPtr<CSSValue> cssValue = m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize); 1227 if (!cssValue || !cssValue->isPrimitiveValue()) 1228 return 0; 1229 return legacyFontSizeFromCSSValue(document, toCSSPrimitiveValue(cssValue.get()), 1230 m_shouldUseFixedDefaultFontSize, AlwaysUseLegacyFontSize); 1231 } 1232 1233 PassRefPtr<EditingStyle> EditingStyle::styleAtSelectionStart(const VisibleSelection& selection, bool shouldUseBackgroundColorInEffect) 1234 { 1235 if (selection.isNone()) 1236 return 0; 1237 1238 Position position = adjustedSelectionStartForStyleComputation(selection); 1239 1240 // If the pos is at the end of a text node, then this node is not fully selected. 1241 // Move it to the next deep equivalent position to avoid removing the style from this node. 1242 // e.g. if pos was at Position("hello", 5) in <b>hello<div>world</div></b>, we want Position("world", 0) instead. 1243 // We only do this for range because caret at Position("hello", 5) in <b>hello</b>world should give you font-weight: bold. 1244 Node* positionNode = position.containerNode(); 1245 if (selection.isRange() && positionNode && positionNode->isTextNode() && position.computeOffsetInContainerNode() == positionNode->maxCharacterOffset()) 1246 position = nextVisuallyDistinctCandidate(position); 1247 1248 Element* element = position.element(); 1249 if (!element) 1250 return 0; 1251 1252 RefPtr<EditingStyle> style = EditingStyle::create(element, EditingStyle::AllProperties); 1253 style->mergeTypingStyle(element->document()); 1254 1255 // If background color is transparent, traverse parent nodes until we hit a different value or document root 1256 // Also, if the selection is a range, ignore the background color at the start of selection, 1257 // and find the background color of the common ancestor. 1258 if (shouldUseBackgroundColorInEffect && (selection.isRange() || hasTransparentBackgroundColor(style->m_mutableStyle.get()))) { 1259 RefPtr<Range> range(selection.toNormalizedRange()); 1260 if (PassRefPtr<CSSValue> value = backgroundColorInEffect(range->commonAncestorContainer(IGNORE_EXCEPTION))) 1261 style->setProperty(CSSPropertyBackgroundColor, value->cssText()); 1262 } 1263 1264 return style; 1265 } 1266 1267 WritingDirection EditingStyle::textDirectionForSelection(const VisibleSelection& selection, EditingStyle* typingStyle, bool& hasNestedOrMultipleEmbeddings) 1268 { 1269 hasNestedOrMultipleEmbeddings = true; 1270 1271 if (selection.isNone()) 1272 return NaturalWritingDirection; 1273 1274 Position position = selection.start().downstream(); 1275 1276 Node* node = position.deprecatedNode(); 1277 if (!node) 1278 return NaturalWritingDirection; 1279 1280 Position end; 1281 if (selection.isRange()) { 1282 end = selection.end().upstream(); 1283 1284 Node* pastLast = Range::create(end.document(), position.parentAnchoredEquivalent(), end.parentAnchoredEquivalent())->pastLastNode(); 1285 for (Node* n = node; n && n != pastLast; n = NodeTraversal::next(n)) { 1286 if (!n->isStyledElement()) 1287 continue; 1288 1289 RefPtr<CSSComputedStyleDeclaration> style = CSSComputedStyleDeclaration::create(n); 1290 RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi); 1291 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue()) 1292 continue; 1293 1294 CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID(); 1295 if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride) 1296 return NaturalWritingDirection; 1297 } 1298 } 1299 1300 if (selection.isCaret()) { 1301 WritingDirection direction; 1302 if (typingStyle && typingStyle->textDirection(direction)) { 1303 hasNestedOrMultipleEmbeddings = false; 1304 return direction; 1305 } 1306 node = selection.visibleStart().deepEquivalent().deprecatedNode(); 1307 } 1308 1309 // The selection is either a caret with no typing attributes or a range in which no embedding is added, so just use the start position 1310 // to decide. 1311 Node* block = enclosingBlock(node); 1312 WritingDirection foundDirection = NaturalWritingDirection; 1313 1314 for (; node != block; node = node->parentNode()) { 1315 if (!node->isStyledElement()) 1316 continue; 1317 1318 RefPtr<CSSComputedStyleDeclaration> style = CSSComputedStyleDeclaration::create(node); 1319 RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi); 1320 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue()) 1321 continue; 1322 1323 CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID(); 1324 if (unicodeBidiValue == CSSValueNormal) 1325 continue; 1326 1327 if (unicodeBidiValue == CSSValueBidiOverride) 1328 return NaturalWritingDirection; 1329 1330 ASSERT(unicodeBidiValue == CSSValueEmbed); 1331 RefPtr<CSSValue> direction = style->getPropertyCSSValue(CSSPropertyDirection); 1332 if (!direction || !direction->isPrimitiveValue()) 1333 continue; 1334 1335 int directionValue = toCSSPrimitiveValue(direction.get())->getValueID(); 1336 if (directionValue != CSSValueLtr && directionValue != CSSValueRtl) 1337 continue; 1338 1339 if (foundDirection != NaturalWritingDirection) 1340 return NaturalWritingDirection; 1341 1342 // In the range case, make sure that the embedding element persists until the end of the range. 1343 if (selection.isRange() && !end.deprecatedNode()->isDescendantOf(node)) 1344 return NaturalWritingDirection; 1345 1346 foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection; 1347 } 1348 hasNestedOrMultipleEmbeddings = false; 1349 return foundDirection; 1350 } 1351 1352 static void reconcileTextDecorationProperties(MutableStylePropertySet* style) 1353 { 1354 RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); 1355 RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration); 1356 // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense. 1357 ASSERT(!textDecorationsInEffect || !textDecoration); 1358 if (textDecorationsInEffect) { 1359 style->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText()); 1360 style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect); 1361 textDecoration = textDecorationsInEffect; 1362 } 1363 1364 // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none". 1365 if (textDecoration && !textDecoration->isValueList()) 1366 style->removeProperty(CSSPropertyTextDecoration); 1367 } 1368 1369 StyleChange::StyleChange(EditingStyle* style, const Position& position) 1370 : m_applyBold(false) 1371 , m_applyItalic(false) 1372 , m_applyUnderline(false) 1373 , m_applyLineThrough(false) 1374 , m_applySubscript(false) 1375 , m_applySuperscript(false) 1376 { 1377 Document* document = position.anchorNode() ? position.anchorNode()->document() : 0; 1378 if (!style || !style->style() || !document || !document->frame()) 1379 return; 1380 1381 RefPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle(); 1382 // FIXME: take care of background-color in effect 1383 RefPtr<MutableStylePropertySet> mutableStyle = getPropertiesNotIn(style->style(), computedStyle.get()); 1384 1385 reconcileTextDecorationProperties(mutableStyle.get()); 1386 if (!document->frame()->editor()->shouldStyleWithCSS()) 1387 extractTextStyles(document, mutableStyle.get(), computedStyle->useFixedFontDefaultSize()); 1388 1389 // Changing the whitespace style in a tab span would collapse the tab into a space. 1390 if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode()))) 1391 mutableStyle->removeProperty(CSSPropertyWhiteSpace); 1392 1393 // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle. 1394 // FIXME: Shouldn't this be done in getPropertiesNotIn? 1395 if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection)) 1396 mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection)); 1397 1398 // Save the result for later 1399 m_cssStyle = mutableStyle->asText().stripWhiteSpace(); 1400 } 1401 1402 static void setTextDecorationProperty(MutableStylePropertySet* style, const CSSValueList* newTextDecoration, CSSPropertyID propertyID) 1403 { 1404 if (newTextDecoration->length()) 1405 style->setProperty(propertyID, newTextDecoration->cssText(), style->propertyIsImportant(propertyID)); 1406 else { 1407 // text-decoration: none is redundant since it does not remove any text decorations. 1408 ASSERT(!style->propertyIsImportant(propertyID)); 1409 style->removeProperty(propertyID); 1410 } 1411 } 1412 1413 void StyleChange::extractTextStyles(Document* document, MutableStylePropertySet* style, bool shouldUseFixedFontDefaultSize) 1414 { 1415 ASSERT(style); 1416 1417 if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) { 1418 style->removeProperty(CSSPropertyFontWeight); 1419 m_applyBold = true; 1420 } 1421 1422 int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle); 1423 if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) { 1424 style->removeProperty(CSSPropertyFontStyle); 1425 m_applyItalic = true; 1426 } 1427 1428 // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect 1429 // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList. 1430 RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration); 1431 if (textDecoration && textDecoration->isValueList()) { 1432 DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline))); 1433 DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough))); 1434 1435 RefPtr<CSSValueList> newTextDecoration = toCSSValueList(textDecoration.get())->copy(); 1436 if (newTextDecoration->removeAll(underline.get())) 1437 m_applyUnderline = true; 1438 if (newTextDecoration->removeAll(lineThrough.get())) 1439 m_applyLineThrough = true; 1440 1441 // If trimTextDecorations, delete underline and line-through 1442 setTextDecorationProperty(style, newTextDecoration.get(), CSSPropertyTextDecoration); 1443 } 1444 1445 int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign); 1446 switch (verticalAlign) { 1447 case CSSValueSub: 1448 style->removeProperty(CSSPropertyVerticalAlign); 1449 m_applySubscript = true; 1450 break; 1451 case CSSValueSuper: 1452 style->removeProperty(CSSPropertyVerticalAlign); 1453 m_applySuperscript = true; 1454 break; 1455 } 1456 1457 if (style->getPropertyCSSValue(CSSPropertyColor)) { 1458 m_applyFontColor = Color(getRGBAFontColor(style)).serialized(); 1459 style->removeProperty(CSSPropertyColor); 1460 } 1461 1462 m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily); 1463 // Remove single quotes for Outlook 2007 compatibility. See https://bugs.webkit.org/show_bug.cgi?id=79448 1464 m_applyFontFace.replaceWithLiteral('\'', ""); 1465 style->removeProperty(CSSPropertyFontFamily); 1466 1467 if (RefPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) { 1468 if (!fontSize->isPrimitiveValue()) 1469 style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size. 1470 else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, toCSSPrimitiveValue(fontSize.get()), 1471 shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) { 1472 m_applyFontSize = String::number(legacyFontSize); 1473 style->removeProperty(CSSPropertyFontSize); 1474 } 1475 } 1476 } 1477 1478 static void diffTextDecorations(MutableStylePropertySet* style, CSSPropertyID propertID, CSSValue* refTextDecoration) 1479 { 1480 RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID); 1481 if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList()) 1482 return; 1483 1484 RefPtr<CSSValueList> newTextDecoration = toCSSValueList(textDecoration.get())->copy(); 1485 CSSValueList* valuesInRefTextDecoration = toCSSValueList(refTextDecoration); 1486 1487 for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++) 1488 newTextDecoration->removeAll(valuesInRefTextDecoration->item(i)); 1489 1490 setTextDecorationProperty(style, newTextDecoration.get(), propertID); 1491 } 1492 1493 static bool fontWeightIsBold(CSSValue* fontWeight) 1494 { 1495 if (!fontWeight) 1496 return false; 1497 if (!fontWeight->isPrimitiveValue()) 1498 return false; 1499 1500 // Because b tag can only bold text, there are only two states in plain html: bold and not bold. 1501 // Collapse all other values to either one of these two states for editing purposes. 1502 switch (toCSSPrimitiveValue(fontWeight)->getValueID()) { 1503 case CSSValue100: 1504 case CSSValue200: 1505 case CSSValue300: 1506 case CSSValue400: 1507 case CSSValue500: 1508 case CSSValueNormal: 1509 return false; 1510 case CSSValueBold: 1511 case CSSValue600: 1512 case CSSValue700: 1513 case CSSValue800: 1514 case CSSValue900: 1515 return true; 1516 default: 1517 break; 1518 } 1519 1520 ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter 1521 return false; 1522 } 1523 1524 static bool fontWeightIsBold(CSSStyleDeclaration* style) 1525 { 1526 ASSERT(style); 1527 RefPtr<CSSValue> fontWeight = style->getPropertyCSSValueInternal(CSSPropertyFontWeight); 1528 return fontWeightIsBold(fontWeight.get()); 1529 } 1530 1531 static bool fontWeightIsBold(StylePropertySet* style) 1532 { 1533 ASSERT(style); 1534 RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight); 1535 return fontWeightIsBold(fontWeight.get()); 1536 } 1537 1538 PassRefPtr<MutableStylePropertySet> getPropertiesNotIn(StylePropertySet* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle) 1539 { 1540 ASSERT(styleWithRedundantProperties); 1541 ASSERT(baseStyle); 1542 RefPtr<MutableStylePropertySet> result = styleWithRedundantProperties->mutableCopy(); 1543 1544 result->removeEquivalentProperties(baseStyle); 1545 1546 RefPtr<CSSValue> baseTextDecorationsInEffect = baseStyle->getPropertyCSSValueInternal(CSSPropertyWebkitTextDecorationsInEffect); 1547 diffTextDecorations(result.get(), CSSPropertyTextDecoration, baseTextDecorationsInEffect.get()); 1548 diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get()); 1549 1550 if (baseStyle->getPropertyCSSValueInternal(CSSPropertyFontWeight) && fontWeightIsBold(result.get()) == fontWeightIsBold(baseStyle)) 1551 result->removeProperty(CSSPropertyFontWeight); 1552 1553 if (baseStyle->getPropertyCSSValueInternal(CSSPropertyColor) && getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle)) 1554 result->removeProperty(CSSPropertyColor); 1555 1556 if (baseStyle->getPropertyCSSValueInternal(CSSPropertyTextAlign) 1557 && textAlignResolvingStartAndEnd(result.get()) == textAlignResolvingStartAndEnd(baseStyle)) 1558 result->removeProperty(CSSPropertyTextAlign); 1559 1560 if (baseStyle->getPropertyCSSValueInternal(CSSPropertyBackgroundColor) && getRGBABackgroundColor(result.get()) == getRGBABackgroundColor(baseStyle)) 1561 result->removeProperty(CSSPropertyBackgroundColor); 1562 1563 return result.release(); 1564 } 1565 1566 CSSValueID getIdentifierValue(StylePropertySet* style, CSSPropertyID propertyID) 1567 { 1568 if (!style) 1569 return CSSValueInvalid; 1570 RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID); 1571 if (!value || !value->isPrimitiveValue()) 1572 return CSSValueInvalid; 1573 return toCSSPrimitiveValue(value.get())->getValueID(); 1574 } 1575 1576 CSSValueID getIdentifierValue(CSSStyleDeclaration* style, CSSPropertyID propertyID) 1577 { 1578 if (!style) 1579 return CSSValueInvalid; 1580 RefPtr<CSSValue> value = style->getPropertyCSSValueInternal(propertyID); 1581 if (!value || !value->isPrimitiveValue()) 1582 return CSSValueInvalid; 1583 return toCSSPrimitiveValue(value.get())->getValueID(); 1584 } 1585 1586 static bool isCSSValueLength(CSSPrimitiveValue* value) 1587 { 1588 return value->isFontIndependentLength(); 1589 } 1590 1591 int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode mode) 1592 { 1593 if (isCSSValueLength(value)) { 1594 int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX); 1595 int legacyFontSize = FontSize::legacyFontSize(document, pixelFontSize, shouldUseFixedFontDefaultSize); 1596 // Use legacy font size only if pixel value matches exactly to that of legacy font size. 1597 int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall; 1598 if (mode == AlwaysUseLegacyFontSize || FontSize::fontSizeForKeyword(document, cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize) == pixelFontSize) 1599 return legacyFontSize; 1600 1601 return 0; 1602 } 1603 1604 if (CSSValueXSmall <= value->getValueID() && value->getValueID() <= CSSValueWebkitXxxLarge) 1605 return value->getValueID() - CSSValueXSmall + 1; 1606 1607 return 0; 1608 } 1609 1610 bool isTransparentColorValue(CSSValue* cssValue) 1611 { 1612 if (!cssValue) 1613 return true; 1614 if (!cssValue->isPrimitiveValue()) 1615 return false; 1616 CSSPrimitiveValue* value = toCSSPrimitiveValue(cssValue); 1617 if (value->isRGBColor()) 1618 return !alphaChannel(value->getRGBA32Value()); 1619 return value->getValueID() == CSSValueTransparent; 1620 } 1621 1622 bool hasTransparentBackgroundColor(CSSStyleDeclaration* style) 1623 { 1624 RefPtr<CSSValue> cssValue = style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor); 1625 return isTransparentColorValue(cssValue.get()); 1626 } 1627 1628 bool hasTransparentBackgroundColor(StylePropertySet* style) 1629 { 1630 RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(CSSPropertyBackgroundColor); 1631 return isTransparentColorValue(cssValue.get()); 1632 } 1633 1634 PassRefPtr<CSSValue> backgroundColorInEffect(Node* node) 1635 { 1636 for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) { 1637 RefPtr<CSSComputedStyleDeclaration> ancestorStyle = CSSComputedStyleDeclaration::create(ancestor); 1638 if (!hasTransparentBackgroundColor(ancestorStyle.get())) 1639 return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor); 1640 } 1641 return 0; 1642 } 1643 1644 } 1645