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