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