Home | History | Annotate | Download | only in editing
      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