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