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