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