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