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