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