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