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