1 /* 2 * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc. 3 * Copyright (C) 2010 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 "CSSMutableStyleDeclaration.h" 33 #include "CSSParser.h" 34 #include "CSSStyleSelector.h" 35 #include "CSSValueKeywords.h" 36 #include "CSSValueList.h" 37 #include "Frame.h" 38 #include "HTMLFontElement.h" 39 #include "HTMLNames.h" 40 #include "Node.h" 41 #include "Position.h" 42 #include "RenderStyle.h" 43 #include "SelectionController.h" 44 #include "StyledElement.h" 45 #include "htmlediting.h" 46 47 namespace WebCore { 48 49 // Editing style properties must be preserved during editing operation. 50 // e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph. 51 // FIXME: The current editingStyleProperties contains all inheritableProperties but we may not need to preserve all inheritable properties 52 static const int editingStyleProperties[] = { 53 // CSS inheritable properties 54 CSSPropertyBorderCollapse, 55 CSSPropertyColor, 56 CSSPropertyFontFamily, 57 CSSPropertyFontSize, 58 CSSPropertyFontStyle, 59 CSSPropertyFontVariant, 60 CSSPropertyFontWeight, 61 CSSPropertyLetterSpacing, 62 CSSPropertyLineHeight, 63 CSSPropertyOrphans, 64 CSSPropertyTextAlign, 65 CSSPropertyTextIndent, 66 CSSPropertyTextTransform, 67 CSSPropertyWhiteSpace, 68 CSSPropertyWidows, 69 CSSPropertyWordSpacing, 70 CSSPropertyWebkitBorderHorizontalSpacing, 71 CSSPropertyWebkitBorderVerticalSpacing, 72 CSSPropertyWebkitTextDecorationsInEffect, 73 CSSPropertyWebkitTextFillColor, 74 CSSPropertyWebkitTextSizeAdjust, 75 CSSPropertyWebkitTextStrokeColor, 76 CSSPropertyWebkitTextStrokeWidth, 77 }; 78 size_t numEditingStyleProperties = WTF_ARRAY_LENGTH(editingStyleProperties); 79 80 static PassRefPtr<CSSMutableStyleDeclaration> copyEditingProperties(CSSStyleDeclaration* style) 81 { 82 return style->copyPropertiesInSet(editingStyleProperties, numEditingStyleProperties); 83 } 84 85 static PassRefPtr<CSSMutableStyleDeclaration> editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style) 86 { 87 if (!style) 88 return CSSMutableStyleDeclaration::create(); 89 return copyEditingProperties(style.get()); 90 } 91 92 static RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle); 93 94 class HTMLElementEquivalent { 95 public: 96 static PassOwnPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, int primitiveValue, const QualifiedName& tagName) 97 { 98 return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName)); 99 } 100 101 virtual ~HTMLElementEquivalent() { } 102 virtual bool matches(Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); } 103 virtual bool hasAttribute() const { return false; } 104 bool propertyExistsInStyle(CSSStyleDeclaration* style) const { return style->getPropertyCSSValue(m_propertyID); } 105 virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const; 106 virtual void addToStyle(Element*, EditingStyle*) const; 107 108 protected: 109 HTMLElementEquivalent(CSSPropertyID); 110 HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName); 111 HTMLElementEquivalent(CSSPropertyID, int primitiveValue, const QualifiedName& tagName); 112 const int m_propertyID; 113 const RefPtr<CSSPrimitiveValue> m_primitiveValue; 114 const QualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global. 115 }; 116 117 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id) 118 : m_propertyID(id) 119 , m_tagName(0) 120 { 121 } 122 123 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const QualifiedName& tagName) 124 : m_propertyID(id) 125 , m_tagName(&tagName) 126 { 127 } 128 129 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, int primitiveValue, const QualifiedName& tagName) 130 : m_propertyID(id) 131 , m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue)) 132 , m_tagName(&tagName) 133 { 134 ASSERT(primitiveValue != CSSValueInvalid); 135 } 136 137 bool HTMLElementEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const 138 { 139 RefPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID); 140 return matches(element) && value && value->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == m_primitiveValue->getIdent(); 141 } 142 143 void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const 144 { 145 style->setProperty(m_propertyID, m_primitiveValue->cssText()); 146 } 147 148 class HTMLTextDecorationEquivalent : public HTMLElementEquivalent { 149 public: 150 static PassOwnPtr<HTMLElementEquivalent> create(int primitiveValue, const QualifiedName& tagName) 151 { 152 return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName)); 153 } 154 virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const; 155 156 private: 157 HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName); 158 }; 159 160 HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName) 161 : HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName) 162 { 163 } 164 165 bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const 166 { 167 RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID); 168 return matches(element) && styleValue && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(m_primitiveValue.get()); 169 } 170 171 class HTMLAttributeEquivalent : public HTMLElementEquivalent { 172 public: 173 static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& tagName, const QualifiedName& attrName) 174 { 175 return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName)); 176 } 177 static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName) 178 { 179 return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName)); 180 } 181 182 bool matches(Element* elem) const { return HTMLElementEquivalent::matches(elem) && elem->hasAttribute(m_attrName); } 183 virtual bool hasAttribute() const { return true; } 184 virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const; 185 virtual void addToStyle(Element*, EditingStyle*) const; 186 virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const; 187 inline const QualifiedName& attributeName() const { return m_attrName; } 188 189 protected: 190 HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName); 191 HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName); 192 const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global. 193 }; 194 195 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& tagName, const QualifiedName& attrName) 196 : HTMLElementEquivalent(id, tagName) 197 , m_attrName(attrName) 198 { 199 } 200 201 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName) 202 : HTMLElementEquivalent(id) 203 , m_attrName(attrName) 204 { 205 } 206 207 bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const 208 { 209 RefPtr<CSSValue> value = attributeValueAsCSSValue(element); 210 RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID); 211 212 // FIXME: This is very inefficient way of comparing values 213 // but we can't string compare attribute value and CSS property value. 214 return value && styleValue && value->cssText() == styleValue->cssText(); 215 } 216 217 void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const 218 { 219 if (RefPtr<CSSValue> value = attributeValueAsCSSValue(element)) 220 style->setProperty(m_propertyID, value->cssText()); 221 } 222 223 PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const 224 { 225 ASSERT(element); 226 if (!element->hasAttribute(m_attrName)) 227 return 0; 228 229 RefPtr<CSSMutableStyleDeclaration> dummyStyle; 230 dummyStyle = CSSMutableStyleDeclaration::create(); 231 dummyStyle->setProperty(m_propertyID, element->getAttribute(m_attrName)); 232 return dummyStyle->getPropertyCSSValue(m_propertyID); 233 } 234 235 class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent { 236 public: 237 static PassOwnPtr<HTMLFontSizeEquivalent> create() 238 { 239 return adoptPtr(new HTMLFontSizeEquivalent()); 240 } 241 virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const; 242 243 private: 244 HTMLFontSizeEquivalent(); 245 }; 246 247 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent() 248 : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr) 249 { 250 } 251 252 PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const 253 { 254 ASSERT(element); 255 if (!element->hasAttribute(m_attrName)) 256 return 0; 257 int size; 258 if (!HTMLFontElement::cssValueFromFontSizeNumber(element->getAttribute(m_attrName), size)) 259 return 0; 260 return CSSPrimitiveValue::createIdentifier(size); 261 } 262 263 float EditingStyle::NoFontDelta = 0.0f; 264 265 EditingStyle::EditingStyle() 266 : m_shouldUseFixedDefaultFontSize(false) 267 , m_fontSizeDelta(NoFontDelta) 268 { 269 } 270 271 EditingStyle::EditingStyle(Node* node, PropertiesToInclude propertiesToInclude) 272 : m_shouldUseFixedDefaultFontSize(false) 273 , m_fontSizeDelta(NoFontDelta) 274 { 275 init(node, propertiesToInclude); 276 } 277 278 EditingStyle::EditingStyle(const Position& position) 279 : m_shouldUseFixedDefaultFontSize(false) 280 , m_fontSizeDelta(NoFontDelta) 281 { 282 init(position.deprecatedNode(), OnlyInheritableProperties); 283 } 284 285 EditingStyle::EditingStyle(const CSSStyleDeclaration* style) 286 : m_mutableStyle(style->copy()) 287 , m_shouldUseFixedDefaultFontSize(false) 288 , m_fontSizeDelta(NoFontDelta) 289 { 290 extractFontSizeDelta(); 291 } 292 293 EditingStyle::EditingStyle(int propertyID, const String& value) 294 : m_mutableStyle(0) 295 , m_shouldUseFixedDefaultFontSize(false) 296 , m_fontSizeDelta(NoFontDelta) 297 { 298 setProperty(propertyID, value); 299 } 300 301 EditingStyle::~EditingStyle() 302 { 303 } 304 305 void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude) 306 { 307 if (isTabSpanTextNode(node)) 308 node = tabSpanNode(node)->parentNode(); 309 else if (isTabSpanNode(node)) 310 node = node->parentNode(); 311 312 RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = computedStyle(node); 313 m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copy() : editingStyleFromComputedStyle(computedStyleAtPosition); 314 315 if (node && node->computedStyle()) { 316 RenderStyle* renderStyle = node->computedStyle(); 317 removeTextFillAndStrokeColorsIfNeeded(renderStyle); 318 replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get()); 319 } 320 321 m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize(); 322 extractFontSizeDelta(); 323 } 324 325 void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle) 326 { 327 // If a node's text fill color is invalid, then its children use 328 // their font-color as their text fill color (they don't 329 // inherit it). Likewise for stroke color. 330 ExceptionCode ec = 0; 331 if (!renderStyle->textFillColor().isValid()) 332 m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor, ec); 333 if (!renderStyle->textStrokeColor().isValid()) 334 m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor, ec); 335 ASSERT(!ec); 336 } 337 338 void EditingStyle::setProperty(int propertyID, const String& value, bool important) 339 { 340 if (!m_mutableStyle) 341 m_mutableStyle = CSSMutableStyleDeclaration::create(); 342 343 ExceptionCode ec; 344 m_mutableStyle->setProperty(propertyID, value, important, ec); 345 } 346 347 void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle) 348 { 349 ASSERT(renderStyle); 350 if (renderStyle->fontDescription().keywordSize()) 351 m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText()); 352 } 353 354 void EditingStyle::extractFontSizeDelta() 355 { 356 if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) { 357 // Explicit font size overrides any delta. 358 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta); 359 return; 360 } 361 362 // Get the adjustment amount out of the style. 363 RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta); 364 if (!value || value->cssValueType() != CSSValue::CSS_PRIMITIVE_VALUE) 365 return; 366 367 CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get()); 368 369 // Only PX handled now. If we handle more types in the future, perhaps 370 // a switch statement here would be more appropriate. 371 if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_PX) 372 return; 373 374 m_fontSizeDelta = primitiveValue->getFloatValue(); 375 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta); 376 } 377 378 bool EditingStyle::isEmpty() const 379 { 380 return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta; 381 } 382 383 bool EditingStyle::textDirection(WritingDirection& writingDirection) const 384 { 385 if (!m_mutableStyle) 386 return false; 387 388 RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); 389 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue()) 390 return false; 391 392 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent(); 393 if (unicodeBidiValue == CSSValueEmbed) { 394 RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection); 395 if (!direction || !direction->isPrimitiveValue()) 396 return false; 397 398 writingDirection = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection; 399 400 return true; 401 } 402 403 if (unicodeBidiValue == CSSValueNormal) { 404 writingDirection = NaturalWritingDirection; 405 return true; 406 } 407 408 return false; 409 } 410 411 void EditingStyle::setStyle(PassRefPtr<CSSMutableStyleDeclaration> style) 412 { 413 m_mutableStyle = style; 414 // FIXME: We should be able to figure out whether or not font is fixed width for mutable style. 415 // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here. 416 m_shouldUseFixedDefaultFontSize = false; 417 extractFontSizeDelta(); 418 } 419 420 void EditingStyle::overrideWithStyle(const CSSMutableStyleDeclaration* style) 421 { 422 if (!style || !style->length()) 423 return; 424 if (!m_mutableStyle) 425 m_mutableStyle = CSSMutableStyleDeclaration::create(); 426 m_mutableStyle->merge(style); 427 extractFontSizeDelta(); 428 } 429 430 void EditingStyle::clear() 431 { 432 m_mutableStyle.clear(); 433 m_shouldUseFixedDefaultFontSize = false; 434 m_fontSizeDelta = NoFontDelta; 435 } 436 437 PassRefPtr<EditingStyle> EditingStyle::copy() const 438 { 439 RefPtr<EditingStyle> copy = EditingStyle::create(); 440 if (m_mutableStyle) 441 copy->m_mutableStyle = m_mutableStyle->copy(); 442 copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize; 443 copy->m_fontSizeDelta = m_fontSizeDelta; 444 return copy; 445 } 446 447 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties() 448 { 449 RefPtr<EditingStyle> blockProperties = EditingStyle::create(); 450 if (!m_mutableStyle) 451 return blockProperties; 452 453 blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties(); 454 m_mutableStyle->removeBlockProperties(); 455 456 return blockProperties; 457 } 458 459 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection() 460 { 461 RefPtr<EditingStyle> textDirection = EditingStyle::create(); 462 textDirection->m_mutableStyle = CSSMutableStyleDeclaration::create(); 463 textDirection->m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed, m_mutableStyle->getPropertyPriority(CSSPropertyUnicodeBidi)); 464 textDirection->m_mutableStyle->setProperty(CSSPropertyDirection, m_mutableStyle->getPropertyValue(CSSPropertyDirection), 465 m_mutableStyle->getPropertyPriority(CSSPropertyDirection)); 466 467 m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi); 468 m_mutableStyle->removeProperty(CSSPropertyDirection); 469 470 return textDirection; 471 } 472 473 void EditingStyle::removeBlockProperties() 474 { 475 if (!m_mutableStyle) 476 return; 477 478 m_mutableStyle->removeBlockProperties(); 479 } 480 481 void EditingStyle::removeStyleAddedByNode(Node* node) 482 { 483 if (!node || !node->parentNode()) 484 return; 485 RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode())); 486 RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node)); 487 parentStyle->diff(nodeStyle.get()); 488 nodeStyle->diff(m_mutableStyle.get()); 489 } 490 491 void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node) 492 { 493 if (!node || !node->parentNode() || !m_mutableStyle) 494 return; 495 RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode())); 496 RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node)); 497 parentStyle->diff(nodeStyle.get()); 498 499 CSSMutableStyleDeclaration::const_iterator end = nodeStyle->end(); 500 for (CSSMutableStyleDeclaration::const_iterator it = nodeStyle->begin(); it != end; ++it) 501 m_mutableStyle->removeProperty(it->id()); 502 } 503 504 void EditingStyle::removeNonEditingProperties() 505 { 506 if (m_mutableStyle) 507 m_mutableStyle = copyEditingProperties(m_mutableStyle.get()); 508 } 509 510 void EditingStyle::collapseTextDecorationProperties() 511 { 512 if (!m_mutableStyle) 513 return; 514 515 RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); 516 if (!textDecorationsInEffect) 517 return; 518 519 m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->getPropertyPriority(CSSPropertyTextDecoration)); 520 m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect); 521 } 522 523 // CSS properties that create a visual difference only when applied to text. 524 static const int textOnlyProperties[] = { 525 CSSPropertyTextDecoration, 526 CSSPropertyWebkitTextDecorationsInEffect, 527 CSSPropertyFontStyle, 528 CSSPropertyFontWeight, 529 CSSPropertyColor, 530 }; 531 532 TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const 533 { 534 RefPtr<CSSMutableStyleDeclaration> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare); 535 536 if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties) 537 difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties)); 538 539 if (!difference->length()) 540 return TrueTriState; 541 if (difference->length() == m_mutableStyle->length()) 542 return FalseTriState; 543 544 return MixedTriState; 545 } 546 547 bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const 548 { 549 ASSERT(element); 550 ASSERT(!conflictingProperties || conflictingProperties->isEmpty()); 551 552 CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl(); 553 if (!m_mutableStyle || !inlineStyle) 554 return false; 555 556 if (!conflictingProperties) { 557 CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end(); 558 for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) { 559 CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id()); 560 561 // We don't override whitespace property of a tab span because that would collapse the tab into a space. 562 if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element)) 563 continue; 564 565 if (inlineStyle->getPropertyCSSValue(propertyID)) 566 return true; 567 } 568 569 return false; 570 } 571 572 CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end(); 573 for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) { 574 CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id()); 575 if ((propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element)) || !inlineStyle->getPropertyCSSValue(propertyID)) 576 continue; 577 578 if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) { 579 if (extractedStyle) 580 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID)); 581 conflictingProperties->append(CSSPropertyDirection); 582 } 583 584 conflictingProperties->append(propertyID); 585 if (extractedStyle) 586 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID)); 587 } 588 589 return !conflictingProperties->isEmpty(); 590 } 591 592 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const 593 { 594 if (!m_mutableStyle) 595 return false; 596 597 static const HTMLElementEquivalent* HTMLEquivalents[] = { 598 HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag).leakPtr(), 599 HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag).leakPtr(), 600 HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag).leakPtr(), 601 HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag).leakPtr(), 602 HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag).leakPtr(), 603 HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag).leakPtr(), 604 605 HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag).leakPtr(), 606 HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag).leakPtr(), 607 HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag).leakPtr(), 608 }; 609 610 for (size_t i = 0; i < WTF_ARRAY_LENGTH(HTMLEquivalents); ++i) { 611 const HTMLElementEquivalent* equivalent = HTMLEquivalents[i]; 612 if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get()) 613 && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) { 614 if (extractedStyle) 615 equivalent->addToStyle(element, extractedStyle); 616 return true; 617 } 618 } 619 return false; 620 } 621 622 static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents() 623 { 624 DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ()); 625 626 if (!HTMLAttributeEquivalents.size()) { 627 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr)); 628 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr)); 629 HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create()); 630 631 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr)); 632 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr)); 633 } 634 635 return HTMLAttributeEquivalents; 636 } 637 638 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const 639 { 640 ASSERT(element); 641 if (!m_mutableStyle) 642 return false; 643 644 const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents(); 645 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) { 646 if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get()) 647 && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get())) 648 return true; 649 } 650 651 return false; 652 } 653 654 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection, 655 EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const 656 { 657 ASSERT(element); 658 // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties 659 ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection); 660 if (!m_mutableStyle) 661 return false; 662 663 const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents(); 664 bool removed = false; 665 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) { 666 const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get(); 667 668 // unicode-bidi and direction are pushed down separately so don't push down with other styles. 669 if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr) 670 continue; 671 672 if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get()) 673 || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) 674 continue; 675 676 if (extractedStyle) 677 equivalent->addToStyle(element, extractedStyle); 678 conflictingAttributes.append(equivalent->attributeName()); 679 removed = true; 680 } 681 682 return removed; 683 } 684 685 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const 686 { 687 return !m_mutableStyle || !getPropertiesNotIn(m_mutableStyle.get(), computedStyle(node).get())->length(); 688 } 689 690 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection) 691 { 692 if (!m_mutableStyle) 693 return; 694 695 // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style. 696 // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate 697 // which one of editingStyleAtPosition or computedStyle is called. 698 RefPtr<EditingStyle> style = EditingStyle::create(position); 699 700 RefPtr<CSSValue> unicodeBidi; 701 RefPtr<CSSValue> direction; 702 if (shouldPreserveWritingDirection == PreserveWritingDirection) { 703 unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); 704 direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection); 705 } 706 707 style->m_mutableStyle->diff(m_mutableStyle.get()); 708 709 // if alpha value is zero, we don't add the background color. 710 RefPtr<CSSValue> backgroundColor = m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor); 711 if (backgroundColor && backgroundColor->isPrimitiveValue() 712 && !alphaChannel(static_cast<CSSPrimitiveValue*>(backgroundColor.get())->getRGBA32Value())) { 713 ExceptionCode ec; 714 m_mutableStyle->removeProperty(CSSPropertyBackgroundColor, ec); 715 } 716 717 if (unicodeBidi && unicodeBidi->isPrimitiveValue()) { 718 m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent()); 719 if (direction && direction->isPrimitiveValue()) 720 m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent()); 721 } 722 } 723 724 void EditingStyle::mergeTypingStyle(Document* document) 725 { 726 ASSERT(document); 727 728 RefPtr<EditingStyle> typingStyle = document->frame()->selection()->typingStyle(); 729 if (!typingStyle || typingStyle == this) 730 return; 731 732 mergeStyle(typingStyle->style()); 733 } 734 735 void EditingStyle::mergeInlineStyleOfElement(StyledElement* element) 736 { 737 ASSERT(element); 738 mergeStyle(element->inlineStyleDecl()); 739 } 740 741 void EditingStyle::mergeStyle(CSSMutableStyleDeclaration* style) 742 { 743 if (!style) 744 return; 745 746 if (!m_mutableStyle) { 747 m_mutableStyle = style->copy(); 748 return; 749 } 750 751 CSSMutableStyleDeclaration::const_iterator end = style->end(); 752 for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) { 753 RefPtr<CSSValue> value; 754 if ((it->id() == CSSPropertyTextDecoration || it->id() == CSSPropertyWebkitTextDecorationsInEffect) && it->value()->isValueList()) { 755 value = m_mutableStyle->getPropertyCSSValue(it->id()); 756 if (value && !value->isValueList()) 757 value = 0; 758 } 759 760 if (!value) { 761 ExceptionCode ec; 762 m_mutableStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec); 763 continue; 764 } 765 766 CSSValueList* newTextDecorations = static_cast<CSSValueList*>(it->value()); 767 CSSValueList* textDecorations = static_cast<CSSValueList*>(value.get()); 768 769 DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline))); 770 DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough))); 771 772 if (newTextDecorations->hasValue(underline.get()) && !textDecorations->hasValue(underline.get())) 773 textDecorations->append(underline.get()); 774 775 if (newTextDecorations->hasValue(lineThrough.get()) && !textDecorations->hasValue(lineThrough.get())) 776 textDecorations->append(lineThrough.get()); 777 } 778 } 779 780 static void reconcileTextDecorationProperties(CSSMutableStyleDeclaration* style) 781 { 782 RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); 783 RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration); 784 // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense. 785 ASSERT(!textDecorationsInEffect || !textDecoration); 786 if (textDecorationsInEffect) { 787 style->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText()); 788 style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect); 789 textDecoration = textDecorationsInEffect; 790 } 791 792 // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none". 793 if (textDecoration && !textDecoration->isValueList()) 794 style->removeProperty(CSSPropertyTextDecoration); 795 } 796 797 StyleChange::StyleChange(EditingStyle* style, const Position& position) 798 : m_applyBold(false) 799 , m_applyItalic(false) 800 , m_applyUnderline(false) 801 , m_applyLineThrough(false) 802 , m_applySubscript(false) 803 , m_applySuperscript(false) 804 { 805 Document* document = position.anchorNode() ? position.anchorNode()->document() : 0; 806 if (!style || !style->style() || !document || !document->frame()) 807 return; 808 809 RefPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle(); 810 RefPtr<CSSMutableStyleDeclaration> mutableStyle = getPropertiesNotIn(style->style(), computedStyle.get()); 811 812 reconcileTextDecorationProperties(mutableStyle.get()); 813 if (!document->frame()->editor()->shouldStyleWithCSS()) 814 extractTextStyles(document, mutableStyle.get(), computedStyle->useFixedFontDefaultSize()); 815 816 // Changing the whitespace style in a tab span would collapse the tab into a space. 817 if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode()))) 818 mutableStyle->removeProperty(CSSPropertyWhiteSpace); 819 820 // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle. 821 // FIXME: Shouldn't this be done in getPropertiesNotIn? 822 if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection)) 823 mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection)); 824 825 // Save the result for later 826 m_cssStyle = mutableStyle->cssText().stripWhiteSpace(); 827 } 828 829 static void setTextDecorationProperty(CSSMutableStyleDeclaration* style, const CSSValueList* newTextDecoration, int propertyID) 830 { 831 if (newTextDecoration->length()) 832 style->setProperty(propertyID, newTextDecoration->cssText(), style->getPropertyPriority(propertyID)); 833 else { 834 // text-decoration: none is redundant since it does not remove any text decorations. 835 ASSERT(!style->getPropertyPriority(propertyID)); 836 style->removeProperty(propertyID); 837 } 838 } 839 840 static RGBA32 getRGBAFontColor(CSSStyleDeclaration* style) 841 { 842 RefPtr<CSSValue> colorValue = style->getPropertyCSSValue(CSSPropertyColor); 843 if (!colorValue || !colorValue->isPrimitiveValue()) 844 return Color::transparent; 845 846 CSSPrimitiveValue* primitiveColor = static_cast<CSSPrimitiveValue*>(colorValue.get()); 847 if (primitiveColor->primitiveType() == CSSPrimitiveValue::CSS_RGBCOLOR) 848 return primitiveColor->getRGBA32Value(); 849 850 // Need to take care of named color such as green and black 851 // This code should be removed after https://bugs.webkit.org/show_bug.cgi?id=28282 is fixed. 852 RGBA32 rgba = 0; 853 CSSParser::parseColor(rgba, colorValue->cssText()); 854 return rgba; 855 } 856 857 void StyleChange::extractTextStyles(Document* document, CSSMutableStyleDeclaration* style, bool shouldUseFixedFontDefaultSize) 858 { 859 ASSERT(style); 860 861 if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) { 862 style->removeProperty(CSSPropertyFontWeight); 863 m_applyBold = true; 864 } 865 866 int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle); 867 if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) { 868 style->removeProperty(CSSPropertyFontStyle); 869 m_applyItalic = true; 870 } 871 872 // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect 873 // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList. 874 RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration); 875 if (textDecoration && textDecoration->isValueList()) { 876 DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline))); 877 DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough))); 878 879 RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy(); 880 if (newTextDecoration->removeAll(underline.get())) 881 m_applyUnderline = true; 882 if (newTextDecoration->removeAll(lineThrough.get())) 883 m_applyLineThrough = true; 884 885 // If trimTextDecorations, delete underline and line-through 886 setTextDecorationProperty(style, newTextDecoration.get(), CSSPropertyTextDecoration); 887 } 888 889 int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign); 890 switch (verticalAlign) { 891 case CSSValueSub: 892 style->removeProperty(CSSPropertyVerticalAlign); 893 m_applySubscript = true; 894 break; 895 case CSSValueSuper: 896 style->removeProperty(CSSPropertyVerticalAlign); 897 m_applySuperscript = true; 898 break; 899 } 900 901 if (style->getPropertyCSSValue(CSSPropertyColor)) { 902 m_applyFontColor = Color(getRGBAFontColor(style)).serialized(); 903 style->removeProperty(CSSPropertyColor); 904 } 905 906 m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily); 907 style->removeProperty(CSSPropertyFontFamily); 908 909 if (RefPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) { 910 if (!fontSize->isPrimitiveValue()) 911 style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size. 912 else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, static_cast<CSSPrimitiveValue*>(fontSize.get()), 913 shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) { 914 m_applyFontSize = String::number(legacyFontSize); 915 style->removeProperty(CSSPropertyFontSize); 916 } 917 } 918 } 919 920 static void diffTextDecorations(CSSMutableStyleDeclaration* style, int propertID, CSSValue* refTextDecoration) 921 { 922 RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID); 923 if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList()) 924 return; 925 926 RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy(); 927 CSSValueList* valuesInRefTextDecoration = static_cast<CSSValueList*>(refTextDecoration); 928 929 for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++) 930 newTextDecoration->removeAll(valuesInRefTextDecoration->item(i)); 931 932 setTextDecorationProperty(style, newTextDecoration.get(), propertID); 933 } 934 935 static bool fontWeightIsBold(CSSStyleDeclaration* style) 936 { 937 ASSERT(style); 938 RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight); 939 940 if (!fontWeight) 941 return false; 942 if (!fontWeight->isPrimitiveValue()) 943 return false; 944 945 // Because b tag can only bold text, there are only two states in plain html: bold and not bold. 946 // Collapse all other values to either one of these two states for editing purposes. 947 switch (static_cast<CSSPrimitiveValue*>(fontWeight.get())->getIdent()) { 948 case CSSValue100: 949 case CSSValue200: 950 case CSSValue300: 951 case CSSValue400: 952 case CSSValue500: 953 case CSSValueNormal: 954 return false; 955 case CSSValueBold: 956 case CSSValue600: 957 case CSSValue700: 958 case CSSValue800: 959 case CSSValue900: 960 return true; 961 } 962 963 ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter 964 return false; // Make compiler happy 965 } 966 967 static int getTextAlignment(CSSStyleDeclaration* style) 968 { 969 int textAlign = getIdentifierValue(style, CSSPropertyTextAlign); 970 switch (textAlign) { 971 case CSSValueCenter: 972 case CSSValueWebkitCenter: 973 return CSSValueCenter; 974 case CSSValueJustify: 975 return CSSValueJustify; 976 case CSSValueLeft: 977 case CSSValueWebkitLeft: 978 return CSSValueLeft; 979 case CSSValueRight: 980 case CSSValueWebkitRight: 981 return CSSValueRight; 982 } 983 return CSSValueInvalid; 984 } 985 986 RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle) 987 { 988 ASSERT(styleWithRedundantProperties); 989 ASSERT(baseStyle); 990 RefPtr<CSSMutableStyleDeclaration> result = styleWithRedundantProperties->copy(); 991 baseStyle->diff(result.get()); 992 993 RefPtr<CSSValue> baseTextDecorationsInEffect = baseStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); 994 diffTextDecorations(result.get(), CSSPropertyTextDecoration, baseTextDecorationsInEffect.get()); 995 diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get()); 996 997 if (fontWeightIsBold(result.get()) == fontWeightIsBold(baseStyle)) 998 result->removeProperty(CSSPropertyFontWeight); 999 1000 if (getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle)) 1001 result->removeProperty(CSSPropertyColor); 1002 1003 if (getTextAlignment(result.get()) == getTextAlignment(baseStyle)) 1004 result->removeProperty(CSSPropertyTextAlign); 1005 1006 return result; 1007 } 1008 1009 1010 int getIdentifierValue(CSSStyleDeclaration* style, int propertyID) 1011 { 1012 if (!style) 1013 return 0; 1014 1015 RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID); 1016 if (!value || !value->isPrimitiveValue()) 1017 return 0; 1018 1019 return static_cast<CSSPrimitiveValue*>(value.get())->getIdent(); 1020 } 1021 1022 static bool isCSSValueLength(CSSPrimitiveValue* value) 1023 { 1024 return value->primitiveType() >= CSSPrimitiveValue::CSS_PX && value->primitiveType() <= CSSPrimitiveValue::CSS_PC; 1025 } 1026 1027 int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode mode) 1028 { 1029 if (isCSSValueLength(value)) { 1030 int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX); 1031 int legacyFontSize = CSSStyleSelector::legacyFontSize(document, pixelFontSize, shouldUseFixedFontDefaultSize); 1032 // Use legacy font size only if pixel value matches exactly to that of legacy font size. 1033 int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall; 1034 if (mode == AlwaysUseLegacyFontSize || CSSStyleSelector::fontSizeForKeyword(document, cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize) == pixelFontSize) 1035 return legacyFontSize; 1036 1037 return 0; 1038 } 1039 1040 if (CSSValueXSmall <= value->getIdent() && value->getIdent() <= CSSValueWebkitXxxLarge) 1041 return value->getIdent() - CSSValueXSmall + 1; 1042 1043 return 0; 1044 } 1045 1046 } 1047