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