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