dfb78f5c1578e24b67717701842b01381848660b
[WebKit-https.git] / 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     m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->getPropertyPriority(CSSPropertyTextDecoration));
561     m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
562 }
563
564 // CSS properties that create a visual difference only when applied to text.
565 static const int textOnlyProperties[] = {
566     CSSPropertyTextDecoration,
567     CSSPropertyWebkitTextDecorationsInEffect,
568     CSSPropertyFontStyle,
569     CSSPropertyFontWeight,
570     CSSPropertyColor,
571 };
572
573 TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
574 {
575     // FIXME: take care of background-color in effect
576     RefPtr<CSSMutableStyleDeclaration> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare);
577
578     if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
579         difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
580
581     if (!difference->length())
582         return TrueTriState;
583     if (difference->length() == m_mutableStyle->length())
584         return FalseTriState;
585
586     return MixedTriState;
587 }
588
589 bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
590 {
591     ASSERT(element);
592     ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
593
594     CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl();
595     if (!m_mutableStyle || !inlineStyle)
596         return false;
597
598     CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
599     for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
600         CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id());
601
602         // We don't override whitespace property of a tab span because that would collapse the tab into a space.
603         if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element))
604             continue;
605
606         if (propertyID == CSSPropertyWebkitTextDecorationsInEffect && inlineStyle->getPropertyCSSValue(CSSPropertyTextDecoration)) {
607             if (!conflictingProperties)
608                 return true;
609             conflictingProperties->append(CSSPropertyTextDecoration);
610             if (extractedStyle)
611                 extractedStyle->setProperty(CSSPropertyTextDecoration, inlineStyle->getPropertyValue(CSSPropertyTextDecoration), inlineStyle->getPropertyPriority(CSSPropertyTextDecoration));
612             continue;
613         }
614
615         if (!inlineStyle->getPropertyCSSValue(propertyID))
616             continue;
617
618         if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
619             if (!conflictingProperties)
620                 return true;
621             conflictingProperties->append(CSSPropertyDirection);
622             if (extractedStyle)
623                 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
624         }
625
626         if (!conflictingProperties)
627             return true;
628
629         conflictingProperties->append(propertyID);
630
631         if (extractedStyle)
632             extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
633     }
634
635     return conflictingProperties && !conflictingProperties->isEmpty();
636 }
637
638 static const Vector<OwnPtr<HTMLElementEquivalent> >& htmlElementEquivalents()
639 {
640     DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLElementEquivalent> >, HTMLElementEquivalents, ());
641
642     if (!HTMLElementEquivalents.size()) {
643         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag));
644         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag));
645         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag));
646         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag));
647         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag));
648         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag));
649
650         HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag));
651         HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag));
652         HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag));
653     }
654
655     return HTMLElementEquivalents;
656 }
657
658
659 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
660 {
661     if (!m_mutableStyle)
662         return false;
663
664     const Vector<OwnPtr<HTMLElementEquivalent> >& HTMLElementEquivalents = htmlElementEquivalents();
665     for (size_t i = 0; i < HTMLElementEquivalents.size(); ++i) {
666         const HTMLElementEquivalent* equivalent = HTMLElementEquivalents[i].get();
667         if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get())
668             && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) {
669             if (extractedStyle)
670                 equivalent->addToStyle(element, extractedStyle);
671             return true;
672         }
673     }
674     return false;
675 }
676
677 static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents()
678 {
679     DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ());
680
681     if (!HTMLAttributeEquivalents.size()) {
682         // elementIsStyledSpanOrHTMLEquivalent depends on the fact each HTMLAttriuteEquivalent matches exactly one attribute
683         // of exactly one element except dirAttr.
684         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
685         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
686         HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create());
687
688         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr));
689         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
690     }
691
692     return HTMLAttributeEquivalents;
693 }
694
695 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
696 {
697     ASSERT(element);
698     if (!m_mutableStyle)
699         return false;
700
701     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
702     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
703         if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get())
704             && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get()))
705             return true;
706     }
707
708     return false;
709 }
710
711 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
712     EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
713 {
714     ASSERT(element);
715     // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
716     ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection);
717     if (!m_mutableStyle)
718         return false;
719
720     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
721     bool removed = false;
722     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
723         const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
724
725         // unicode-bidi and direction are pushed down separately so don't push down with other styles.
726         if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr)
727             continue;
728
729         if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
730             || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
731             continue;
732
733         if (extractedStyle)
734             equivalent->addToStyle(element, extractedStyle);
735         conflictingAttributes.append(equivalent->attributeName());
736         removed = true;
737     }
738
739     return removed;
740 }
741
742 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
743 {
744     return !m_mutableStyle || !getPropertiesNotIn(m_mutableStyle.get(), computedStyle(node).get())->length();
745 }
746
747 bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement* element)
748 {
749     bool elementIsSpanOrElementEquivalent = false;
750     if (element->hasTagName(HTMLNames::spanTag))
751         elementIsSpanOrElementEquivalent = true;
752     else {
753         const Vector<OwnPtr<HTMLElementEquivalent> >& HTMLElementEquivalents = htmlElementEquivalents();
754         size_t i;
755         for (i = 0; i < HTMLElementEquivalents.size(); ++i) {
756             if (HTMLElementEquivalents[i]->matches(element)) {
757                 elementIsSpanOrElementEquivalent = true;
758                 break;
759             }
760         }
761     }
762
763     const NamedNodeMap* attributeMap = element->attributeMap();
764     if (!attributeMap || attributeMap->isEmpty())
765         return elementIsSpanOrElementEquivalent; // span, b, etc... without any attributes
766
767     unsigned matchedAttributes = 0;
768     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
769     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
770         if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->attributeName() != HTMLNames::dirAttr)
771             matchedAttributes++;
772     }
773
774     if (!elementIsSpanOrElementEquivalent && !matchedAttributes)
775         return false; // element is not a span, a html element equivalent, or font element.
776     
777     if (element->getAttribute(HTMLNames::classAttr) == AppleStyleSpanClass)
778         matchedAttributes++;
779
780     if (element->hasAttribute(HTMLNames::styleAttr)) {
781         if (CSSMutableStyleDeclaration* style = element->inlineStyleDecl()) {
782             CSSMutableStyleDeclaration::const_iterator end = style->end();
783             for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
784                 bool matched = false;
785                 for (size_t i = 0; i < numEditingInheritableProperties; ++i) {
786                     if (editingInheritableProperties[i] == it->id()) {
787                         matched = true;
788                         break;
789                     }
790                 }
791                 if (!matched && it->id() != CSSPropertyBackgroundColor)
792                     return false;
793             }
794         }
795         matchedAttributes++;
796     }
797
798     // font with color attribute, span with style attribute, etc...
799     ASSERT(matchedAttributes <= attributeMap->length());
800     return matchedAttributes >= attributeMap->length();
801 }
802
803 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection)
804 {
805     if (!m_mutableStyle)
806         return;
807
808     // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
809     // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
810     // which one of editingStyleAtPosition or computedStyle is called.
811     RefPtr<EditingStyle> style = EditingStyle::create(position, EditingInheritablePropertiesAndBackgroundColorInEffect);
812
813     RefPtr<CSSValue> unicodeBidi;
814     RefPtr<CSSValue> direction;
815     if (shouldPreserveWritingDirection == PreserveWritingDirection) {
816         unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
817         direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
818     }
819
820     style->m_mutableStyle->diff(m_mutableStyle.get());
821     if (getRGBAFontColor(m_mutableStyle.get()) == getRGBAFontColor(style->m_mutableStyle.get()))
822         m_mutableStyle->removeProperty(CSSPropertyColor);
823
824     if (hasTransparentBackgroundColor(m_mutableStyle.get())
825         || cssValueToRGBA(m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor).get()) == rgbaBackgroundColorInEffect(position.containerNode()))
826         m_mutableStyle->removeProperty(CSSPropertyBackgroundColor);
827
828     if (unicodeBidi && unicodeBidi->isPrimitiveValue()) {
829         m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent());
830         if (direction && direction->isPrimitiveValue())
831             m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
832     }
833 }
834
835 void EditingStyle::mergeTypingStyle(Document* document)
836 {
837     ASSERT(document);
838
839     RefPtr<EditingStyle> typingStyle = document->frame()->selection()->typingStyle();
840     if (!typingStyle || typingStyle == this)
841         return;
842
843     mergeStyle(typingStyle->style());
844 }
845
846 void EditingStyle::mergeInlineStyleOfElement(StyledElement* element)
847 {
848     ASSERT(element);
849     mergeStyle(element->inlineStyleDecl());
850 }
851
852 void EditingStyle::mergeStyle(CSSMutableStyleDeclaration* style)
853 {
854     if (!style)
855         return;
856
857     if (!m_mutableStyle) {
858         m_mutableStyle = style->copy();
859         return;
860     }
861
862     CSSMutableStyleDeclaration::const_iterator end = style->end();
863     for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
864         RefPtr<CSSValue> value;
865         if ((it->id() == CSSPropertyTextDecoration || it->id() == CSSPropertyWebkitTextDecorationsInEffect) && it->value()->isValueList()) {
866             value = m_mutableStyle->getPropertyCSSValue(it->id());
867             if (value && !value->isValueList())
868                 value = 0;
869         }
870
871         if (!value) {
872             ExceptionCode ec;
873             m_mutableStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec);
874             continue;
875         }
876
877         CSSValueList* newTextDecorations = static_cast<CSSValueList*>(it->value());
878         CSSValueList* textDecorations = static_cast<CSSValueList*>(value.get());
879
880         DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
881         DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
882
883         if (newTextDecorations->hasValue(underline.get()) && !textDecorations->hasValue(underline.get()))
884             textDecorations->append(underline.get());
885
886         if (newTextDecorations->hasValue(lineThrough.get()) && !textDecorations->hasValue(lineThrough.get()))
887             textDecorations->append(lineThrough.get());
888     }
889 }
890
891 static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesForElement(Element* element, unsigned rulesToInclude)
892 {
893     RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create();
894     RefPtr<CSSRuleList> matchedRules = element->document()->styleSelector()->styleRulesForElement(element, rulesToInclude);
895     if (matchedRules) {
896         for (unsigned i = 0; i < matchedRules->length(); i++) {
897             if (matchedRules->item(i)->type() == CSSRule::STYLE_RULE) {
898                 RefPtr<CSSMutableStyleDeclaration> s = static_cast<CSSStyleRule*>(matchedRules->item(i))->style();
899                 style->merge(s.get(), true);
900             }
901         }
902     }
903     
904     return style.release();
905 }
906
907 void EditingStyle::mergeStyleFromRules(StyledElement* element)
908 {
909     RefPtr<CSSMutableStyleDeclaration> styleFromMatchedRules = styleFromMatchedRulesForElement(element,
910         CSSStyleSelector::AuthorCSSRules | CSSStyleSelector::CrossOriginCSSRules);
911     // Styles from the inline style declaration, held in the variable "style", take precedence 
912     // over those from matched rules.
913     if (m_mutableStyle)
914         styleFromMatchedRules->merge(m_mutableStyle.get());
915
916     clear();
917     m_mutableStyle = styleFromMatchedRules;
918 }
919
920 void EditingStyle::mergeStyleFromRulesForSerialization(StyledElement* element)
921 {
922     mergeStyleFromRules(element);
923
924     // The property value, if it's a percentage, may not reflect the actual computed value.  
925     // For example: style="height: 1%; overflow: visible;" in quirksmode
926     // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem
927     RefPtr<CSSComputedStyleDeclaration> computedStyleForElement = computedStyle(element);
928     RefPtr<CSSMutableStyleDeclaration> fromComputedStyle = CSSMutableStyleDeclaration::create();
929     {
930         CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
931         for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
932             const CSSProperty& property = *it;
933             CSSValue* value = property.value();
934             if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE)
935                 if (static_cast<CSSPrimitiveValue*>(value)->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
936                     if (RefPtr<CSSValue> computedPropertyValue = computedStyleForElement->getPropertyCSSValue(property.id()))
937                         fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue));
938         }
939     }
940     m_mutableStyle->merge(fromComputedStyle.get());
941 }
942
943 void EditingStyle::removeStyleFromRulesAndContext(StyledElement* element, Node* context)
944 {
945     ASSERT(element);
946     if (!m_mutableStyle)
947         return;
948
949     // 1. Remove style from matched rules because style remain without repeating it in inline style declaration
950     RefPtr<CSSMutableStyleDeclaration> styleFromMatchedRules = styleFromMatchedRulesForElement(element, CSSStyleSelector::AllButEmptyCSSRules);
951     if (styleFromMatchedRules && styleFromMatchedRules->length())
952         m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), styleFromMatchedRules.get());
953
954     // 2. Remove style present in context and not overriden by matched rules.
955     RefPtr<EditingStyle> computedStyle = EditingStyle::create(context, EditingInheritablePropertiesAndBackgroundColorInEffect);
956     if (computedStyle->m_mutableStyle) {
957         computedStyle->removePropertiesInElementDefaultStyle(element);
958         m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), computedStyle->m_mutableStyle.get());
959     }
960 }
961
962 void EditingStyle::removePropertiesInElementDefaultStyle(StyledElement* element)
963 {
964     if (!m_mutableStyle || !m_mutableStyle->length())
965         return;
966
967     RefPtr<CSSMutableStyleDeclaration> defaultStyle = styleFromMatchedRulesForElement(element, CSSStyleSelector::UAAndUserCSSRules);
968
969     Vector<int> propertiesToRemove(defaultStyle->length());
970     size_t i = 0;
971     CSSMutableStyleDeclaration::const_iterator end = defaultStyle->end();
972     for (CSSMutableStyleDeclaration::const_iterator it = defaultStyle->begin(); it != end; ++it, ++i)
973         propertiesToRemove[i] = it->id();
974
975     m_mutableStyle->removePropertiesInSet(propertiesToRemove.data(), propertiesToRemove.size());
976 }
977     
978
979 static void reconcileTextDecorationProperties(CSSMutableStyleDeclaration* style)
980 {    
981     RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
982     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
983     // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense.
984     ASSERT(!textDecorationsInEffect || !textDecoration);
985     if (textDecorationsInEffect) {
986         style->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText());
987         style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
988         textDecoration = textDecorationsInEffect;
989     }
990
991     // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none".
992     if (textDecoration && !textDecoration->isValueList())
993         style->removeProperty(CSSPropertyTextDecoration);
994 }
995
996 StyleChange::StyleChange(EditingStyle* style, const Position& position)
997     : m_applyBold(false)
998     , m_applyItalic(false)
999     , m_applyUnderline(false)
1000     , m_applyLineThrough(false)
1001     , m_applySubscript(false)
1002     , m_applySuperscript(false)
1003 {
1004     Document* document = position.anchorNode() ? position.anchorNode()->document() : 0;
1005     if (!style || !style->style() || !document || !document->frame())
1006         return;
1007
1008     RefPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle();
1009     // FIXME: take care of background-color in effect
1010     RefPtr<CSSMutableStyleDeclaration> mutableStyle = getPropertiesNotIn(style->style(), computedStyle.get());
1011
1012     reconcileTextDecorationProperties(mutableStyle.get());
1013     if (!document->frame()->editor()->shouldStyleWithCSS())
1014         extractTextStyles(document, mutableStyle.get(), computedStyle->useFixedFontDefaultSize());
1015
1016     // Changing the whitespace style in a tab span would collapse the tab into a space.
1017     if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode())))
1018         mutableStyle->removeProperty(CSSPropertyWhiteSpace);
1019
1020     // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle.
1021     // FIXME: Shouldn't this be done in getPropertiesNotIn?
1022     if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection))
1023         mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection));
1024
1025     // Save the result for later
1026     m_cssStyle = mutableStyle->cssText().stripWhiteSpace();
1027 }
1028
1029 static void setTextDecorationProperty(CSSMutableStyleDeclaration* style, const CSSValueList* newTextDecoration, int propertyID)
1030 {
1031     if (newTextDecoration->length())
1032         style->setProperty(propertyID, newTextDecoration->cssText(), style->getPropertyPriority(propertyID));
1033     else {
1034         // text-decoration: none is redundant since it does not remove any text decorations.
1035         ASSERT(!style->getPropertyPriority(propertyID));
1036         style->removeProperty(propertyID);
1037     }
1038 }
1039
1040 void StyleChange::extractTextStyles(Document* document, CSSMutableStyleDeclaration* style, bool shouldUseFixedFontDefaultSize)
1041 {
1042     ASSERT(style);
1043
1044     if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) {
1045         style->removeProperty(CSSPropertyFontWeight);
1046         m_applyBold = true;
1047     }
1048
1049     int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle);
1050     if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) {
1051         style->removeProperty(CSSPropertyFontStyle);
1052         m_applyItalic = true;
1053     }
1054
1055     // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
1056     // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
1057     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
1058     if (textDecoration && textDecoration->isValueList()) {
1059         DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
1060         DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
1061
1062         RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
1063         if (newTextDecoration->removeAll(underline.get()))
1064             m_applyUnderline = true;
1065         if (newTextDecoration->removeAll(lineThrough.get()))
1066             m_applyLineThrough = true;
1067
1068         // If trimTextDecorations, delete underline and line-through
1069         setTextDecorationProperty(style, newTextDecoration.get(), CSSPropertyTextDecoration);
1070     }
1071
1072     int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign);
1073     switch (verticalAlign) {
1074     case CSSValueSub:
1075         style->removeProperty(CSSPropertyVerticalAlign);
1076         m_applySubscript = true;
1077         break;
1078     case CSSValueSuper:
1079         style->removeProperty(CSSPropertyVerticalAlign);
1080         m_applySuperscript = true;
1081         break;
1082     }
1083
1084     if (style->getPropertyCSSValue(CSSPropertyColor)) {
1085         m_applyFontColor = Color(getRGBAFontColor(style)).serialized();
1086         style->removeProperty(CSSPropertyColor);
1087     }
1088
1089     m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily);
1090     style->removeProperty(CSSPropertyFontFamily);
1091
1092     if (RefPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) {
1093         if (!fontSize->isPrimitiveValue())
1094             style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
1095         else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, static_cast<CSSPrimitiveValue*>(fontSize.get()),
1096                 shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) {
1097             m_applyFontSize = String::number(legacyFontSize);
1098             style->removeProperty(CSSPropertyFontSize);
1099         }
1100     }
1101 }
1102
1103 static void diffTextDecorations(CSSMutableStyleDeclaration* style, int propertID, CSSValue* refTextDecoration)
1104 {
1105     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID);
1106     if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList())
1107         return;
1108
1109     RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
1110     CSSValueList* valuesInRefTextDecoration = static_cast<CSSValueList*>(refTextDecoration);
1111
1112     for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++)
1113         newTextDecoration->removeAll(valuesInRefTextDecoration->item(i));
1114
1115     setTextDecorationProperty(style, newTextDecoration.get(), propertID);
1116 }
1117
1118 static bool fontWeightIsBold(CSSStyleDeclaration* style)
1119 {
1120     ASSERT(style);
1121     RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight);
1122
1123     if (!fontWeight)
1124         return false;
1125     if (!fontWeight->isPrimitiveValue())
1126         return false;
1127
1128     // Because b tag can only bold text, there are only two states in plain html: bold and not bold.
1129     // Collapse all other values to either one of these two states for editing purposes.
1130     switch (static_cast<CSSPrimitiveValue*>(fontWeight.get())->getIdent()) {
1131         case CSSValue100:
1132         case CSSValue200:
1133         case CSSValue300:
1134         case CSSValue400:
1135         case CSSValue500:
1136         case CSSValueNormal:
1137             return false;
1138         case CSSValueBold:
1139         case CSSValue600:
1140         case CSSValue700:
1141         case CSSValue800:
1142         case CSSValue900:
1143             return true;
1144     }
1145
1146     ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
1147     return false; // Make compiler happy
1148 }
1149
1150 static int getTextAlignment(CSSStyleDeclaration* style)
1151 {
1152     int textAlign = getIdentifierValue(style, CSSPropertyTextAlign);
1153     switch (textAlign) {
1154     case CSSValueCenter:
1155     case CSSValueWebkitCenter:
1156         return CSSValueCenter;
1157     case CSSValueJustify:
1158         return CSSValueJustify;
1159     case CSSValueLeft:
1160     case CSSValueWebkitLeft:
1161         return CSSValueLeft;
1162     case CSSValueRight:
1163     case CSSValueWebkitRight:
1164         return CSSValueRight;
1165     }
1166     return CSSValueInvalid;
1167 }
1168
1169 RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle)
1170 {
1171     ASSERT(styleWithRedundantProperties);
1172     ASSERT(baseStyle);
1173     RefPtr<CSSMutableStyleDeclaration> result = styleWithRedundantProperties->copy();
1174
1175     baseStyle->diff(result.get());
1176
1177     RefPtr<CSSValue> baseTextDecorationsInEffect = baseStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1178     diffTextDecorations(result.get(), CSSPropertyTextDecoration, baseTextDecorationsInEffect.get());
1179     diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
1180
1181     if (fontWeightIsBold(result.get()) == fontWeightIsBold(baseStyle))
1182         result->removeProperty(CSSPropertyFontWeight);
1183
1184     if (getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle))
1185         result->removeProperty(CSSPropertyColor);
1186
1187     if (getTextAlignment(result.get()) == getTextAlignment(baseStyle))
1188         result->removeProperty(CSSPropertyTextAlign);
1189
1190     return result;
1191 }
1192
1193 int getIdentifierValue(CSSStyleDeclaration* style, int propertyID)
1194 {
1195     if (!style)
1196         return 0;
1197
1198     RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
1199     if (!value || !value->isPrimitiveValue())
1200         return 0;
1201
1202     return static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
1203 }
1204
1205 static bool isCSSValueLength(CSSPrimitiveValue* value)
1206 {
1207     return value->primitiveType() >= CSSPrimitiveValue::CSS_PX && value->primitiveType() <= CSSPrimitiveValue::CSS_PC;
1208 }
1209
1210 int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode mode)
1211 {
1212     if (isCSSValueLength(value)) {
1213         int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX);
1214         int legacyFontSize = CSSStyleSelector::legacyFontSize(document, pixelFontSize, shouldUseFixedFontDefaultSize);
1215         // Use legacy font size only if pixel value matches exactly to that of legacy font size.
1216         int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall;
1217         if (mode == AlwaysUseLegacyFontSize || CSSStyleSelector::fontSizeForKeyword(document, cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize) == pixelFontSize)
1218             return legacyFontSize;
1219
1220         return 0;
1221     }
1222
1223     if (CSSValueXSmall <= value->getIdent() && value->getIdent() <= CSSValueWebkitXxxLarge)
1224         return value->getIdent() - CSSValueXSmall + 1;
1225
1226     return 0;
1227 }
1228
1229 bool hasTransparentBackgroundColor(CSSStyleDeclaration* style)
1230 {
1231     RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(CSSPropertyBackgroundColor);
1232     if (!cssValue)
1233         return true;
1234     
1235     if (!cssValue->isPrimitiveValue())
1236         return false;
1237     CSSPrimitiveValue* value = static_cast<CSSPrimitiveValue*>(cssValue.get());
1238     
1239     if (value->primitiveType() == CSSPrimitiveValue::CSS_RGBCOLOR)
1240         return !alphaChannel(value->getRGBA32Value());
1241     
1242     return value->getIdent() == CSSValueTransparent;
1243 }
1244
1245 PassRefPtr<CSSValue> backgroundColorInEffect(Node* node)
1246 {
1247     for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
1248         RefPtr<CSSComputedStyleDeclaration> ancestorStyle = computedStyle(ancestor);
1249         if (!hasTransparentBackgroundColor(ancestorStyle.get()))
1250             return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
1251     }
1252     return 0;
1253 }
1254
1255 }