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