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