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