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