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