initial import
[vuplus_webkit] / Source / WebCore / editing / EditingStyle.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc.
3  * Copyright (C) 2010 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 "CSSMutableStyleDeclaration.h"
33 #include "CSSParser.h"
34 #include "CSSStyleRule.h"
35 #include "CSSStyleSelector.h"
36 #include "CSSValueKeywords.h"
37 #include "CSSValueList.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 "Position.h"
45 #include "QualifiedName.h"
46 #include "RenderStyle.h"
47 #include "StyledElement.h"
48 #include "htmlediting.h"
49 #include <wtf/HashSet.h>
50
51 namespace WebCore {
52
53 // Editing style properties must be preserved during editing operation.
54 // e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph.
55 static const int editingInheritableProperties[] = {
56     // CSS inheritable properties
57     CSSPropertyColor,
58     CSSPropertyFontFamily,
59     CSSPropertyFontSize,
60     CSSPropertyFontStyle,
61     CSSPropertyFontVariant,
62     CSSPropertyFontWeight,
63     CSSPropertyLetterSpacing,
64     CSSPropertyLineHeight,
65     CSSPropertyOrphans,
66     CSSPropertyTextAlign,
67     CSSPropertyTextIndent,
68     CSSPropertyTextTransform,
69     CSSPropertyWhiteSpace,
70     CSSPropertyWidows,
71     CSSPropertyWordSpacing,
72     CSSPropertyWebkitTextDecorationsInEffect,
73     CSSPropertyWebkitTextFillColor,
74     CSSPropertyWebkitTextSizeAdjust,
75     CSSPropertyWebkitTextStrokeColor,
76     CSSPropertyWebkitTextStrokeWidth,
77 };
78 size_t numEditingInheritableProperties = WTF_ARRAY_LENGTH(editingInheritableProperties);
79
80 static PassRefPtr<CSSMutableStyleDeclaration> copyEditingProperties(CSSStyleDeclaration* style)
81 {
82     return style->copyPropertiesInSet(editingInheritableProperties, numEditingInheritableProperties);
83 }
84
85 static PassRefPtr<CSSMutableStyleDeclaration> editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style)
86 {
87     if (!style)
88         return CSSMutableStyleDeclaration::create();
89     return copyEditingProperties(style.get());
90 }
91
92 static RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle);
93
94 class HTMLElementEquivalent {
95 public:
96     static PassOwnPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, int primitiveValue, const QualifiedName& tagName)
97     {
98         return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName));
99     }
100
101     virtual ~HTMLElementEquivalent() { }
102     virtual bool matches(const Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); }
103     virtual bool hasAttribute() const { return false; }
104     virtual bool propertyExistsInStyle(CSSStyleDeclaration* style) const { return style->getPropertyCSSValue(m_propertyID); }
105     virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
106     virtual void addToStyle(Element*, EditingStyle*) const;
107
108 protected:
109     HTMLElementEquivalent(CSSPropertyID);
110     HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName);
111     HTMLElementEquivalent(CSSPropertyID, int primitiveValue, const QualifiedName& tagName);
112     const int m_propertyID;
113     const RefPtr<CSSPrimitiveValue> m_primitiveValue;
114     const QualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global.
115 };
116
117 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id)
118     : m_propertyID(id)
119     , m_tagName(0)
120 {
121 }
122
123 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const QualifiedName& tagName)
124     : m_propertyID(id)
125     , m_tagName(&tagName)
126 {
127 }
128
129 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, int primitiveValue, const QualifiedName& tagName)
130     : m_propertyID(id)
131     , m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue))
132     , m_tagName(&tagName)
133 {
134     ASSERT(primitiveValue != CSSValueInvalid);
135 }
136
137 bool HTMLElementEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
138 {
139     RefPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID);
140     return matches(element) && value && value->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == m_primitiveValue->getIdent();
141 }
142
143 void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const
144 {
145     style->setProperty(m_propertyID, m_primitiveValue->cssText());
146 }
147
148 class HTMLTextDecorationEquivalent : public HTMLElementEquivalent {
149 public:
150     static PassOwnPtr<HTMLElementEquivalent> create(int primitiveValue, const QualifiedName& tagName)
151     {
152         return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName));
153     }
154     virtual bool propertyExistsInStyle(CSSStyleDeclaration*) const;
155     virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
156
157 private:
158     HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName);
159 };
160
161 HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName)
162     : HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName)
163     // m_propertyID is used in HTMLElementEquivalent::addToStyle
164 {
165 }
166
167 bool HTMLTextDecorationEquivalent::propertyExistsInStyle(CSSStyleDeclaration* style) const
168 {
169     return style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect) || style->getPropertyCSSValue(CSSPropertyTextDecoration);
170 }
171
172 bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
173 {
174     RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
175     if (!styleValue)
176         styleValue = style->getPropertyCSSValue(CSSPropertyTextDecoration);
177     return matches(element) && styleValue && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(m_primitiveValue.get());
178 }
179
180 class HTMLAttributeEquivalent : public HTMLElementEquivalent {
181 public:
182     static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& tagName, const QualifiedName& attrName)
183     {
184         return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName));
185     }
186     static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName)
187     {
188         return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName));
189     }
190
191     bool matches(const Element* elem) const { return HTMLElementEquivalent::matches(elem) && elem->hasAttribute(m_attrName); }
192     virtual bool hasAttribute() const { return true; }
193     virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
194     virtual void addToStyle(Element*, EditingStyle*) const;
195     virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
196     inline const QualifiedName& attributeName() const { return m_attrName; }
197
198 protected:
199     HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName);
200     HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName);
201     const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global.
202 };
203
204 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& tagName, const QualifiedName& attrName)
205     : HTMLElementEquivalent(id, tagName)
206     , m_attrName(attrName)
207 {
208 }
209
210 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName)
211     : HTMLElementEquivalent(id)
212     , m_attrName(attrName)
213 {
214 }
215
216 bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
217 {
218     RefPtr<CSSValue> value = attributeValueAsCSSValue(element);
219     RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID);
220     
221     // FIXME: This is very inefficient way of comparing values
222     // but we can't string compare attribute value and CSS property value.
223     return value && styleValue && value->cssText() == styleValue->cssText();
224 }
225
226 void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const
227 {
228     if (RefPtr<CSSValue> value = attributeValueAsCSSValue(element))
229         style->setProperty(m_propertyID, value->cssText());
230 }
231
232 PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const
233 {
234     ASSERT(element);
235     if (!element->hasAttribute(m_attrName))
236         return 0;
237     
238     RefPtr<CSSMutableStyleDeclaration> dummyStyle;
239     dummyStyle = CSSMutableStyleDeclaration::create();
240     dummyStyle->setProperty(m_propertyID, element->getAttribute(m_attrName));
241     return dummyStyle->getPropertyCSSValue(m_propertyID);
242 }
243
244 class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent {
245 public:
246     static PassOwnPtr<HTMLFontSizeEquivalent> create()
247     {
248         return adoptPtr(new HTMLFontSizeEquivalent());
249     }
250     virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
251
252 private:
253     HTMLFontSizeEquivalent();
254 };
255
256 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
257     : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr)
258 {
259 }
260
261 PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const
262 {
263     ASSERT(element);
264     if (!element->hasAttribute(m_attrName))
265         return 0;
266     int size;
267     if (!HTMLFontElement::cssValueFromFontSizeNumber(element->getAttribute(m_attrName), size))
268         return 0;
269     return CSSPrimitiveValue::createIdentifier(size);
270 }
271
272 float EditingStyle::NoFontDelta = 0.0f;
273
274 EditingStyle::EditingStyle()
275     : m_shouldUseFixedDefaultFontSize(false)
276     , m_fontSizeDelta(NoFontDelta)
277 {
278 }
279
280 EditingStyle::EditingStyle(Node* node, PropertiesToInclude propertiesToInclude)
281     : m_shouldUseFixedDefaultFontSize(false)
282     , m_fontSizeDelta(NoFontDelta)
283 {
284     init(node, propertiesToInclude);
285 }
286
287 EditingStyle::EditingStyle(const Position& position, PropertiesToInclude propertiesToInclude)
288     : m_shouldUseFixedDefaultFontSize(false)
289     , m_fontSizeDelta(NoFontDelta)
290 {
291     init(position.deprecatedNode(), propertiesToInclude);
292 }
293
294 EditingStyle::EditingStyle(const CSSStyleDeclaration* style)
295     : m_mutableStyle(style ? style->copy() : 0)
296     , m_shouldUseFixedDefaultFontSize(false)
297     , m_fontSizeDelta(NoFontDelta)
298 {
299     extractFontSizeDelta();
300 }
301
302 EditingStyle::EditingStyle(int propertyID, const String& value)
303     : m_mutableStyle(0)
304     , m_shouldUseFixedDefaultFontSize(false)
305     , m_fontSizeDelta(NoFontDelta)
306 {
307     setProperty(propertyID, value);
308 }
309
310 EditingStyle::~EditingStyle()
311 {
312 }
313
314 static RGBA32 cssValueToRGBA(CSSValue* colorValue)
315 {
316     if (!colorValue || !colorValue->isPrimitiveValue())
317         return Color::transparent;
318     
319     CSSPrimitiveValue* primitiveColor = static_cast<CSSPrimitiveValue*>(colorValue);
320     if (primitiveColor->primitiveType() == CSSPrimitiveValue::CSS_RGBCOLOR)
321         return primitiveColor->getRGBA32Value();
322     
323     RGBA32 rgba = 0;
324     CSSParser::parseColor(rgba, colorValue->cssText());
325     return rgba;
326 }
327
328 static inline RGBA32 getRGBAFontColor(CSSStyleDeclaration* style)
329 {
330     return cssValueToRGBA(style->getPropertyCSSValue(CSSPropertyColor).get());
331 }
332
333 static inline RGBA32 rgbaBackgroundColorInEffect(Node* node)
334 {
335     return cssValueToRGBA(backgroundColorInEffect(node).get());
336 }
337
338 void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude)
339 {
340     if (isTabSpanTextNode(node))
341         node = tabSpanNode(node)->parentNode();
342     else if (isTabSpanNode(node))
343         node = node->parentNode();
344
345     RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = computedStyle(node);
346     m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copy() : editingStyleFromComputedStyle(computedStyleAtPosition);
347
348     if (propertiesToInclude == EditingInheritablePropertiesAndBackgroundColorInEffect) {
349         if (RefPtr<CSSValue> value = backgroundColorInEffect(node))
350             m_mutableStyle->setProperty(CSSPropertyBackgroundColor, value->cssText());
351     }
352
353     if (node && node->computedStyle()) {
354         RenderStyle* renderStyle = node->computedStyle();
355         removeTextFillAndStrokeColorsIfNeeded(renderStyle);
356         replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get());
357     }
358
359     m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize();
360     extractFontSizeDelta();
361 }
362
363 void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle)
364 {
365     // If a node's text fill color is invalid, then its children use 
366     // their font-color as their text fill color (they don't
367     // inherit it).  Likewise for stroke color.
368     ExceptionCode ec = 0;
369     if (!renderStyle->textFillColor().isValid())
370         m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor, ec);
371     if (!renderStyle->textStrokeColor().isValid())
372         m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor, ec);
373     ASSERT(!ec);
374 }
375
376 void EditingStyle::setProperty(int propertyID, const String& value, bool important)
377 {
378     if (!m_mutableStyle)
379         m_mutableStyle = CSSMutableStyleDeclaration::create();
380
381     ExceptionCode ec;
382     m_mutableStyle->setProperty(propertyID, value, important, ec);
383 }
384
385 void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle)
386 {
387     ASSERT(renderStyle);
388     if (renderStyle->fontDescription().keywordSize())
389         m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText());
390 }
391
392 void EditingStyle::extractFontSizeDelta()
393 {
394     if (!m_mutableStyle)
395         return;
396
397     if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) {
398         // Explicit font size overrides any delta.
399         m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
400         return;
401     }
402
403     // Get the adjustment amount out of the style.
404     RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
405     if (!value || value->cssValueType() != CSSValue::CSS_PRIMITIVE_VALUE)
406         return;
407
408     CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get());
409
410     // Only PX handled now. If we handle more types in the future, perhaps
411     // a switch statement here would be more appropriate.
412     if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_PX)
413         return;
414
415     m_fontSizeDelta = primitiveValue->getFloatValue();
416     m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
417 }
418
419 bool EditingStyle::isEmpty() const
420 {
421     return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta;
422 }
423
424 bool EditingStyle::textDirection(WritingDirection& writingDirection) const
425 {
426     if (!m_mutableStyle)
427         return false;
428
429     RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
430     if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
431         return false;
432
433     int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
434     if (unicodeBidiValue == CSSValueEmbed) {
435         RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
436         if (!direction || !direction->isPrimitiveValue())
437             return false;
438
439         writingDirection = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
440
441         return true;
442     }
443
444     if (unicodeBidiValue == CSSValueNormal) {
445         writingDirection = NaturalWritingDirection;
446         return true;
447     }
448
449     return false;
450 }
451
452 void EditingStyle::setStyle(PassRefPtr<CSSMutableStyleDeclaration> style)
453 {
454     m_mutableStyle = style;
455     // FIXME: We should be able to figure out whether or not font is fixed width for mutable style.
456     // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here.
457     m_shouldUseFixedDefaultFontSize = false;
458     extractFontSizeDelta();
459 }
460
461 void EditingStyle::overrideWithStyle(const CSSMutableStyleDeclaration* style)
462 {
463     if (!style || !style->length())
464         return;
465     if (!m_mutableStyle)
466         m_mutableStyle = CSSMutableStyleDeclaration::create();
467     m_mutableStyle->merge(style);
468     extractFontSizeDelta();
469 }
470
471 void EditingStyle::clear()
472 {
473     m_mutableStyle.clear();
474     m_shouldUseFixedDefaultFontSize = false;
475     m_fontSizeDelta = NoFontDelta;
476 }
477
478 PassRefPtr<EditingStyle> EditingStyle::copy() const
479 {
480     RefPtr<EditingStyle> copy = EditingStyle::create();
481     if (m_mutableStyle)
482         copy->m_mutableStyle = m_mutableStyle->copy();
483     copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize;
484     copy->m_fontSizeDelta = m_fontSizeDelta;
485     return copy;
486 }
487
488 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties()
489 {
490     RefPtr<EditingStyle> blockProperties = EditingStyle::create();
491     if (!m_mutableStyle)
492         return blockProperties;
493
494     blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties();
495     m_mutableStyle->removeBlockProperties();
496
497     return blockProperties;
498 }
499
500 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection()
501 {
502     RefPtr<EditingStyle> textDirection = EditingStyle::create();
503     textDirection->m_mutableStyle = CSSMutableStyleDeclaration::create();
504     textDirection->m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed, m_mutableStyle->getPropertyPriority(CSSPropertyUnicodeBidi));
505     textDirection->m_mutableStyle->setProperty(CSSPropertyDirection, m_mutableStyle->getPropertyValue(CSSPropertyDirection),
506         m_mutableStyle->getPropertyPriority(CSSPropertyDirection));
507
508     m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi);
509     m_mutableStyle->removeProperty(CSSPropertyDirection);
510
511     return textDirection;
512 }
513
514 void EditingStyle::removeBlockProperties()
515 {
516     if (!m_mutableStyle)
517         return;
518
519     m_mutableStyle->removeBlockProperties();
520 }
521
522 void EditingStyle::removeStyleAddedByNode(Node* node)
523 {
524     if (!node || !node->parentNode())
525         return;
526     RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
527     RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
528     parentStyle->diff(nodeStyle.get());
529     nodeStyle->diff(m_mutableStyle.get());
530 }
531
532 void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node)
533 {
534     if (!node || !node->parentNode() || !m_mutableStyle)
535         return;
536     RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
537     RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
538     parentStyle->diff(nodeStyle.get());
539
540     CSSMutableStyleDeclaration::const_iterator end = nodeStyle->end();
541     for (CSSMutableStyleDeclaration::const_iterator it = nodeStyle->begin(); it != end; ++it)
542         m_mutableStyle->removeProperty(it->id());
543 }
544
545 void EditingStyle::removeNonEditingProperties()
546 {
547     if (m_mutableStyle)
548         m_mutableStyle = copyEditingProperties(m_mutableStyle.get());
549 }
550
551 void EditingStyle::collapseTextDecorationProperties()
552 {
553     if (!m_mutableStyle)
554         return;
555
556     RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
557     if (!textDecorationsInEffect)
558         return;
559
560     if (textDecorationsInEffect->isValueList())
561         m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->getPropertyPriority(CSSPropertyTextDecoration));
562     else
563         m_mutableStyle->removeProperty(CSSPropertyTextDecoration);
564     m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
565 }
566
567 // CSS properties that create a visual difference only when applied to text.
568 static const int textOnlyProperties[] = {
569     CSSPropertyTextDecoration,
570     CSSPropertyWebkitTextDecorationsInEffect,
571     CSSPropertyFontStyle,
572     CSSPropertyFontWeight,
573     CSSPropertyColor,
574 };
575
576 TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
577 {
578     // FIXME: take care of background-color in effect
579     RefPtr<CSSMutableStyleDeclaration> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare);
580
581     if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
582         difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
583
584     if (!difference->length())
585         return TrueTriState;
586     if (difference->length() == m_mutableStyle->length())
587         return FalseTriState;
588
589     return MixedTriState;
590 }
591
592 bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
593 {
594     ASSERT(element);
595     ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
596
597     CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl();
598     if (!m_mutableStyle || !inlineStyle)
599         return false;
600
601     CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
602     for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
603         CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id());
604
605         // We don't override whitespace property of a tab span because that would collapse the tab into a space.
606         if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element))
607             continue;
608
609         if (propertyID == CSSPropertyWebkitTextDecorationsInEffect && inlineStyle->getPropertyCSSValue(CSSPropertyTextDecoration)) {
610             if (!conflictingProperties)
611                 return true;
612             conflictingProperties->append(CSSPropertyTextDecoration);
613             if (extractedStyle)
614                 extractedStyle->setProperty(CSSPropertyTextDecoration, inlineStyle->getPropertyValue(CSSPropertyTextDecoration), inlineStyle->getPropertyPriority(CSSPropertyTextDecoration));
615             continue;
616         }
617
618         if (!inlineStyle->getPropertyCSSValue(propertyID))
619             continue;
620
621         if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
622             if (!conflictingProperties)
623                 return true;
624             conflictingProperties->append(CSSPropertyDirection);
625             if (extractedStyle)
626                 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
627         }
628
629         if (!conflictingProperties)
630             return true;
631
632         conflictingProperties->append(propertyID);
633
634         if (extractedStyle)
635             extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
636     }
637
638     return conflictingProperties && !conflictingProperties->isEmpty();
639 }
640
641 static const Vector<OwnPtr<HTMLElementEquivalent> >& htmlElementEquivalents()
642 {
643     DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLElementEquivalent> >, HTMLElementEquivalents, ());
644
645     if (!HTMLElementEquivalents.size()) {
646         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag));
647         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag));
648         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag));
649         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag));
650         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag));
651         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag));
652
653         HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag));
654         HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag));
655         HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag));
656     }
657
658     return HTMLElementEquivalents;
659 }
660
661
662 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
663 {
664     if (!m_mutableStyle)
665         return false;
666
667     const Vector<OwnPtr<HTMLElementEquivalent> >& HTMLElementEquivalents = htmlElementEquivalents();
668     for (size_t i = 0; i < HTMLElementEquivalents.size(); ++i) {
669         const HTMLElementEquivalent* equivalent = HTMLElementEquivalents[i].get();
670         if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get())
671             && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) {
672             if (extractedStyle)
673                 equivalent->addToStyle(element, extractedStyle);
674             return true;
675         }
676     }
677     return false;
678 }
679
680 static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents()
681 {
682     DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ());
683
684     if (!HTMLAttributeEquivalents.size()) {
685         // elementIsStyledSpanOrHTMLEquivalent depends on the fact each HTMLAttriuteEquivalent matches exactly one attribute
686         // of exactly one element except dirAttr.
687         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
688         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
689         HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create());
690
691         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr));
692         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
693     }
694
695     return HTMLAttributeEquivalents;
696 }
697
698 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
699 {
700     ASSERT(element);
701     if (!m_mutableStyle)
702         return false;
703
704     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
705     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
706         if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get())
707             && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get()))
708             return true;
709     }
710
711     return false;
712 }
713
714 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
715     EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
716 {
717     ASSERT(element);
718     // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
719     ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection);
720     if (!m_mutableStyle)
721         return false;
722
723     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
724     bool removed = false;
725     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
726         const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
727
728         // unicode-bidi and direction are pushed down separately so don't push down with other styles.
729         if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr)
730             continue;
731
732         if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
733             || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
734             continue;
735
736         if (extractedStyle)
737             equivalent->addToStyle(element, extractedStyle);
738         conflictingAttributes.append(equivalent->attributeName());
739         removed = true;
740     }
741
742     return removed;
743 }
744
745 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
746 {
747     return !m_mutableStyle || !getPropertiesNotIn(m_mutableStyle.get(), computedStyle(node).get())->length();
748 }
749
750 bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement* element)
751 {
752     bool elementIsSpanOrElementEquivalent = false;
753     if (element->hasTagName(HTMLNames::spanTag))
754         elementIsSpanOrElementEquivalent = true;
755     else {
756         const Vector<OwnPtr<HTMLElementEquivalent> >& HTMLElementEquivalents = htmlElementEquivalents();
757         size_t i;
758         for (i = 0; i < HTMLElementEquivalents.size(); ++i) {
759             if (HTMLElementEquivalents[i]->matches(element)) {
760                 elementIsSpanOrElementEquivalent = true;
761                 break;
762             }
763         }
764     }
765
766     const NamedNodeMap* attributeMap = element->attributeMap();
767     if (!attributeMap || attributeMap->isEmpty())
768         return elementIsSpanOrElementEquivalent; // span, b, etc... without any attributes
769
770     unsigned matchedAttributes = 0;
771     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
772     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
773         if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->attributeName() != HTMLNames::dirAttr)
774             matchedAttributes++;
775     }
776
777     if (!elementIsSpanOrElementEquivalent && !matchedAttributes)
778         return false; // element is not a span, a html element equivalent, or font element.
779     
780     if (element->getAttribute(HTMLNames::classAttr) == AppleStyleSpanClass)
781         matchedAttributes++;
782
783     if (element->hasAttribute(HTMLNames::styleAttr)) {
784         if (CSSMutableStyleDeclaration* style = element->inlineStyleDecl()) {
785             CSSMutableStyleDeclaration::const_iterator end = style->end();
786             for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
787                 bool matched = false;
788                 for (size_t i = 0; i < numEditingInheritableProperties; ++i) {
789                     if (editingInheritableProperties[i] == it->id()) {
790                         matched = true;
791                         break;
792                     }
793                 }
794                 if (!matched && it->id() != CSSPropertyBackgroundColor)
795                     return false;
796             }
797         }
798         matchedAttributes++;
799     }
800
801     // font with color attribute, span with style attribute, etc...
802     ASSERT(matchedAttributes <= attributeMap->length());
803     return matchedAttributes >= attributeMap->length();
804 }
805
806 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection)
807 {
808     if (!m_mutableStyle)
809         return;
810
811     // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
812     // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
813     // which one of editingStyleAtPosition or computedStyle is called.
814     RefPtr<EditingStyle> style = EditingStyle::create(position, EditingInheritablePropertiesAndBackgroundColorInEffect);
815
816     RefPtr<CSSValue> unicodeBidi;
817     RefPtr<CSSValue> direction;
818     if (shouldPreserveWritingDirection == PreserveWritingDirection) {
819         unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
820         direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
821     }
822
823     style->m_mutableStyle->diff(m_mutableStyle.get());
824     if (getRGBAFontColor(m_mutableStyle.get()) == getRGBAFontColor(style->m_mutableStyle.get()))
825         m_mutableStyle->removeProperty(CSSPropertyColor);
826
827     if (hasTransparentBackgroundColor(m_mutableStyle.get())
828         || cssValueToRGBA(m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor).get()) == rgbaBackgroundColorInEffect(position.containerNode()))
829         m_mutableStyle->removeProperty(CSSPropertyBackgroundColor);
830
831     if (unicodeBidi && unicodeBidi->isPrimitiveValue()) {
832         m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent());
833         if (direction && direction->isPrimitiveValue())
834             m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
835     }
836 }
837
838 void EditingStyle::mergeTypingStyle(Document* document)
839 {
840     ASSERT(document);
841
842     RefPtr<EditingStyle> typingStyle = document->frame()->selection()->typingStyle();
843     if (!typingStyle || typingStyle == this)
844         return;
845
846     mergeStyle(typingStyle->style());
847 }
848
849 void EditingStyle::mergeInlineStyleOfElement(StyledElement* element)
850 {
851     ASSERT(element);
852     mergeStyle(element->inlineStyleDecl());
853 }
854
855 void EditingStyle::mergeStyle(CSSMutableStyleDeclaration* style)
856 {
857     if (!style)
858         return;
859
860     if (!m_mutableStyle) {
861         m_mutableStyle = style->copy();
862         return;
863     }
864
865     CSSMutableStyleDeclaration::const_iterator end = style->end();
866     for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
867         RefPtr<CSSValue> value;
868         if ((it->id() == CSSPropertyTextDecoration || it->id() == CSSPropertyWebkitTextDecorationsInEffect) && it->value()->isValueList()) {
869             value = m_mutableStyle->getPropertyCSSValue(it->id());
870             if (value && !value->isValueList())
871                 value = 0;
872         }
873
874         if (!value) {
875             ExceptionCode ec;
876             m_mutableStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec);
877             continue;
878         }
879
880         CSSValueList* newTextDecorations = static_cast<CSSValueList*>(it->value());
881         CSSValueList* textDecorations = static_cast<CSSValueList*>(value.get());
882
883         DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
884         DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
885
886         if (newTextDecorations->hasValue(underline.get()) && !textDecorations->hasValue(underline.get()))
887             textDecorations->append(underline.get());
888
889         if (newTextDecorations->hasValue(lineThrough.get()) && !textDecorations->hasValue(lineThrough.get()))
890             textDecorations->append(lineThrough.get());
891     }
892 }
893
894 static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesForElement(Element* element, unsigned rulesToInclude)
895 {
896     RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create();
897     RefPtr<CSSRuleList> matchedRules = element->document()->styleSelector()->styleRulesForElement(element, rulesToInclude);
898     if (matchedRules) {
899         for (unsigned i = 0; i < matchedRules->length(); i++) {
900             if (matchedRules->item(i)->type() == CSSRule::STYLE_RULE) {
901                 RefPtr<CSSMutableStyleDeclaration> s = static_cast<CSSStyleRule*>(matchedRules->item(i))->style();
902                 style->merge(s.get(), true);
903             }
904         }
905     }
906     
907     return style.release();
908 }
909
910 void EditingStyle::mergeStyleFromRules(StyledElement* element)
911 {
912     RefPtr<CSSMutableStyleDeclaration> styleFromMatchedRules = styleFromMatchedRulesForElement(element,
913         CSSStyleSelector::AuthorCSSRules | CSSStyleSelector::CrossOriginCSSRules);
914     // Styles from the inline style declaration, held in the variable "style", take precedence 
915     // over those from matched rules.
916     if (m_mutableStyle)
917         styleFromMatchedRules->merge(m_mutableStyle.get());
918
919     clear();
920     m_mutableStyle = styleFromMatchedRules;
921 }
922
923 void EditingStyle::mergeStyleFromRulesForSerialization(StyledElement* element)
924 {
925     mergeStyleFromRules(element);
926
927     // The property value, if it's a percentage, may not reflect the actual computed value.  
928     // For example: style="height: 1%; overflow: visible;" in quirksmode
929     // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem
930     RefPtr<CSSComputedStyleDeclaration> computedStyleForElement = computedStyle(element);
931     RefPtr<CSSMutableStyleDeclaration> fromComputedStyle = CSSMutableStyleDeclaration::create();
932     {
933         CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
934         for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
935             const CSSProperty& property = *it;
936             CSSValue* value = property.value();
937             if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE)
938                 if (static_cast<CSSPrimitiveValue*>(value)->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
939                     if (RefPtr<CSSValue> computedPropertyValue = computedStyleForElement->getPropertyCSSValue(property.id()))
940                         fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue));
941         }
942     }
943     m_mutableStyle->merge(fromComputedStyle.get());
944 }
945
946 void EditingStyle::removeStyleFromRulesAndContext(StyledElement* element, Node* context)
947 {
948     ASSERT(element);
949     if (!m_mutableStyle)
950         return;
951
952     // 1. Remove style from matched rules because style remain without repeating it in inline style declaration
953     RefPtr<CSSMutableStyleDeclaration> styleFromMatchedRules = styleFromMatchedRulesForElement(element, CSSStyleSelector::AllButEmptyCSSRules);
954     if (styleFromMatchedRules && styleFromMatchedRules->length())
955         m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), styleFromMatchedRules.get());
956
957     // 2. Remove style present in context and not overriden by matched rules.
958     RefPtr<EditingStyle> computedStyle = EditingStyle::create(context, EditingInheritablePropertiesAndBackgroundColorInEffect);
959     if (computedStyle->m_mutableStyle) {
960         computedStyle->removePropertiesInElementDefaultStyle(element);
961         m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), computedStyle->m_mutableStyle.get());
962     }
963
964     // 3. If this element is a span and has display: inline or float: none, remove them unless they are overriden by rules.
965     // These rules are added by serialization code to wrap text nodes.
966     if (isStyleSpanOrSpanWithOnlyStyleAttribute(element)) {
967         if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyDisplay) && getIdentifierValue(m_mutableStyle.get(), CSSPropertyDisplay) == CSSValueInline)
968             m_mutableStyle->removeProperty(CSSPropertyDisplay);
969         if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyFloat) && getIdentifierValue(m_mutableStyle.get(), CSSPropertyFloat) == CSSValueNone)
970             m_mutableStyle->removeProperty(CSSPropertyFloat);
971     }
972 }
973
974 void EditingStyle::removePropertiesInElementDefaultStyle(Element* element)
975 {
976     if (!m_mutableStyle || !m_mutableStyle->length())
977         return;
978
979     RefPtr<CSSMutableStyleDeclaration> defaultStyle = styleFromMatchedRulesForElement(element, CSSStyleSelector::UAAndUserCSSRules);
980
981     Vector<int> propertiesToRemove(defaultStyle->length());
982     size_t i = 0;
983     CSSMutableStyleDeclaration::const_iterator end = defaultStyle->end();
984     for (CSSMutableStyleDeclaration::const_iterator it = defaultStyle->begin(); it != end; ++it, ++i)
985         propertiesToRemove[i] = it->id();
986
987     m_mutableStyle->removePropertiesInSet(propertiesToRemove.data(), propertiesToRemove.size());
988 }
989
990 void EditingStyle::forceInline()
991 {
992     if (!m_mutableStyle)
993         m_mutableStyle = CSSMutableStyleDeclaration::create();
994     const bool propertyIsImportant = true;
995     m_mutableStyle->setProperty(CSSPropertyDisplay, CSSValueInline, propertyIsImportant);
996 }
997     
998
999 static void reconcileTextDecorationProperties(CSSMutableStyleDeclaration* style)
1000 {    
1001     RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1002     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
1003     // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense.
1004     ASSERT(!textDecorationsInEffect || !textDecoration);
1005     if (textDecorationsInEffect) {
1006         style->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText());
1007         style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
1008         textDecoration = textDecorationsInEffect;
1009     }
1010
1011     // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none".
1012     if (textDecoration && !textDecoration->isValueList())
1013         style->removeProperty(CSSPropertyTextDecoration);
1014 }
1015
1016 StyleChange::StyleChange(EditingStyle* style, const Position& position)
1017     : m_applyBold(false)
1018     , m_applyItalic(false)
1019     , m_applyUnderline(false)
1020     , m_applyLineThrough(false)
1021     , m_applySubscript(false)
1022     , m_applySuperscript(false)
1023 {
1024     Document* document = position.anchorNode() ? position.anchorNode()->document() : 0;
1025     if (!style || !style->style() || !document || !document->frame())
1026         return;
1027
1028     RefPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle();
1029     // FIXME: take care of background-color in effect
1030     RefPtr<CSSMutableStyleDeclaration> mutableStyle = getPropertiesNotIn(style->style(), computedStyle.get());
1031
1032     reconcileTextDecorationProperties(mutableStyle.get());
1033     if (!document->frame()->editor()->shouldStyleWithCSS())
1034         extractTextStyles(document, mutableStyle.get(), computedStyle->useFixedFontDefaultSize());
1035
1036     // Changing the whitespace style in a tab span would collapse the tab into a space.
1037     if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode())))
1038         mutableStyle->removeProperty(CSSPropertyWhiteSpace);
1039
1040     // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle.
1041     // FIXME: Shouldn't this be done in getPropertiesNotIn?
1042     if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection))
1043         mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection));
1044
1045     // Save the result for later
1046     m_cssStyle = mutableStyle->cssText().stripWhiteSpace();
1047 }
1048
1049 static void setTextDecorationProperty(CSSMutableStyleDeclaration* style, const CSSValueList* newTextDecoration, int propertyID)
1050 {
1051     if (newTextDecoration->length())
1052         style->setProperty(propertyID, newTextDecoration->cssText(), style->getPropertyPriority(propertyID));
1053     else {
1054         // text-decoration: none is redundant since it does not remove any text decorations.
1055         ASSERT(!style->getPropertyPriority(propertyID));
1056         style->removeProperty(propertyID);
1057     }
1058 }
1059
1060 void StyleChange::extractTextStyles(Document* document, CSSMutableStyleDeclaration* style, bool shouldUseFixedFontDefaultSize)
1061 {
1062     ASSERT(style);
1063
1064     if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) {
1065         style->removeProperty(CSSPropertyFontWeight);
1066         m_applyBold = true;
1067     }
1068
1069     int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle);
1070     if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) {
1071         style->removeProperty(CSSPropertyFontStyle);
1072         m_applyItalic = true;
1073     }
1074
1075     // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
1076     // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
1077     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
1078     if (textDecoration && textDecoration->isValueList()) {
1079         DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
1080         DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
1081
1082         RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
1083         if (newTextDecoration->removeAll(underline.get()))
1084             m_applyUnderline = true;
1085         if (newTextDecoration->removeAll(lineThrough.get()))
1086             m_applyLineThrough = true;
1087
1088         // If trimTextDecorations, delete underline and line-through
1089         setTextDecorationProperty(style, newTextDecoration.get(), CSSPropertyTextDecoration);
1090     }
1091
1092     int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign);
1093     switch (verticalAlign) {
1094     case CSSValueSub:
1095         style->removeProperty(CSSPropertyVerticalAlign);
1096         m_applySubscript = true;
1097         break;
1098     case CSSValueSuper:
1099         style->removeProperty(CSSPropertyVerticalAlign);
1100         m_applySuperscript = true;
1101         break;
1102     }
1103
1104     if (style->getPropertyCSSValue(CSSPropertyColor)) {
1105         m_applyFontColor = Color(getRGBAFontColor(style)).serialized();
1106         style->removeProperty(CSSPropertyColor);
1107     }
1108
1109     m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily);
1110     style->removeProperty(CSSPropertyFontFamily);
1111
1112     if (RefPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) {
1113         if (!fontSize->isPrimitiveValue())
1114             style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
1115         else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, static_cast<CSSPrimitiveValue*>(fontSize.get()),
1116                 shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) {
1117             m_applyFontSize = String::number(legacyFontSize);
1118             style->removeProperty(CSSPropertyFontSize);
1119         }
1120     }
1121 }
1122
1123 static void diffTextDecorations(CSSMutableStyleDeclaration* style, int propertID, CSSValue* refTextDecoration)
1124 {
1125     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID);
1126     if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList())
1127         return;
1128
1129     RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
1130     CSSValueList* valuesInRefTextDecoration = static_cast<CSSValueList*>(refTextDecoration);
1131
1132     for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++)
1133         newTextDecoration->removeAll(valuesInRefTextDecoration->item(i));
1134
1135     setTextDecorationProperty(style, newTextDecoration.get(), propertID);
1136 }
1137
1138 static bool fontWeightIsBold(CSSStyleDeclaration* style)
1139 {
1140     ASSERT(style);
1141     RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight);
1142
1143     if (!fontWeight)
1144         return false;
1145     if (!fontWeight->isPrimitiveValue())
1146         return false;
1147
1148     // Because b tag can only bold text, there are only two states in plain html: bold and not bold.
1149     // Collapse all other values to either one of these two states for editing purposes.
1150     switch (static_cast<CSSPrimitiveValue*>(fontWeight.get())->getIdent()) {
1151         case CSSValue100:
1152         case CSSValue200:
1153         case CSSValue300:
1154         case CSSValue400:
1155         case CSSValue500:
1156         case CSSValueNormal:
1157             return false;
1158         case CSSValueBold:
1159         case CSSValue600:
1160         case CSSValue700:
1161         case CSSValue800:
1162         case CSSValue900:
1163             return true;
1164     }
1165
1166     ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
1167     return false; // Make compiler happy
1168 }
1169
1170 static int getTextAlignment(CSSStyleDeclaration* style)
1171 {
1172     int textAlign = getIdentifierValue(style, CSSPropertyTextAlign);
1173     switch (textAlign) {
1174     case CSSValueCenter:
1175     case CSSValueWebkitCenter:
1176         return CSSValueCenter;
1177     case CSSValueJustify:
1178         return CSSValueJustify;
1179     case CSSValueLeft:
1180     case CSSValueWebkitLeft:
1181         return CSSValueLeft;
1182     case CSSValueRight:
1183     case CSSValueWebkitRight:
1184         return CSSValueRight;
1185     }
1186     return CSSValueInvalid;
1187 }
1188
1189 RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle)
1190 {
1191     ASSERT(styleWithRedundantProperties);
1192     ASSERT(baseStyle);
1193     RefPtr<CSSMutableStyleDeclaration> result = styleWithRedundantProperties->copy();
1194
1195     baseStyle->diff(result.get());
1196
1197     RefPtr<CSSValue> baseTextDecorationsInEffect = baseStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1198     diffTextDecorations(result.get(), CSSPropertyTextDecoration, baseTextDecorationsInEffect.get());
1199     diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
1200
1201     if (baseStyle->getPropertyCSSValue(CSSPropertyFontSize) && fontWeightIsBold(result.get()) == fontWeightIsBold(baseStyle))
1202         result->removeProperty(CSSPropertyFontWeight);
1203
1204     if (baseStyle->getPropertyCSSValue(CSSPropertyColor) && getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle))
1205         result->removeProperty(CSSPropertyColor);
1206
1207     if (baseStyle->getPropertyCSSValue(CSSPropertyTextAlign) && getTextAlignment(result.get()) == getTextAlignment(baseStyle))
1208         result->removeProperty(CSSPropertyTextAlign);
1209
1210     return result;
1211 }
1212
1213 int getIdentifierValue(CSSStyleDeclaration* style, int propertyID)
1214 {
1215     if (!style)
1216         return 0;
1217
1218     RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
1219     if (!value || !value->isPrimitiveValue())
1220         return 0;
1221
1222     return static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
1223 }
1224
1225 static bool isCSSValueLength(CSSPrimitiveValue* value)
1226 {
1227     return value->primitiveType() >= CSSPrimitiveValue::CSS_PX && value->primitiveType() <= CSSPrimitiveValue::CSS_PC;
1228 }
1229
1230 int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode mode)
1231 {
1232     if (isCSSValueLength(value)) {
1233         int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX);
1234         int legacyFontSize = CSSStyleSelector::legacyFontSize(document, pixelFontSize, shouldUseFixedFontDefaultSize);
1235         // Use legacy font size only if pixel value matches exactly to that of legacy font size.
1236         int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall;
1237         if (mode == AlwaysUseLegacyFontSize || CSSStyleSelector::fontSizeForKeyword(document, cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize) == pixelFontSize)
1238             return legacyFontSize;
1239
1240         return 0;
1241     }
1242
1243     if (CSSValueXSmall <= value->getIdent() && value->getIdent() <= CSSValueWebkitXxxLarge)
1244         return value->getIdent() - CSSValueXSmall + 1;
1245
1246     return 0;
1247 }
1248
1249 bool hasTransparentBackgroundColor(CSSStyleDeclaration* style)
1250 {
1251     RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(CSSPropertyBackgroundColor);
1252     if (!cssValue)
1253         return true;
1254     
1255     if (!cssValue->isPrimitiveValue())
1256         return false;
1257     CSSPrimitiveValue* value = static_cast<CSSPrimitiveValue*>(cssValue.get());
1258     
1259     if (value->primitiveType() == CSSPrimitiveValue::CSS_RGBCOLOR)
1260         return !alphaChannel(value->getRGBA32Value());
1261     
1262     return value->getIdent() == CSSValueTransparent;
1263 }
1264
1265 PassRefPtr<CSSValue> backgroundColorInEffect(Node* node)
1266 {
1267     for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
1268         RefPtr<CSSComputedStyleDeclaration> ancestorStyle = computedStyle(ancestor);
1269         if (!hasTransparentBackgroundColor(ancestorStyle.get()))
1270             return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
1271     }
1272     return 0;
1273 }
1274
1275 }