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