668c9431edd8fa9b986b5f71491869853eb9bcdd
[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 "CSSValueKeywords.h"
34 #include "CSSValueList.h"
35 #include "Frame.h"
36 #include "HTMLFontElement.h"
37 #include "HTMLNames.h"
38 #include "Node.h"
39 #include "Position.h"
40 #include "RenderStyle.h"
41 #include "SelectionController.h"
42 #include "StyledElement.h"
43 #include "htmlediting.h"
44
45 namespace WebCore {
46
47 // Editing style properties must be preserved during editing operation.
48 // e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph.
49 // FIXME: The current editingStyleProperties contains all inheritableProperties but we may not need to preserve all inheritable properties
50 static const int editingStyleProperties[] = {
51     // CSS inheritable properties
52     CSSPropertyBorderCollapse,
53     CSSPropertyColor,
54     CSSPropertyFontFamily,
55     CSSPropertyFontSize,
56     CSSPropertyFontStyle,
57     CSSPropertyFontVariant,
58     CSSPropertyFontWeight,
59     CSSPropertyLetterSpacing,
60     CSSPropertyLineHeight,
61     CSSPropertyOrphans,
62     CSSPropertyTextAlign,
63     CSSPropertyTextIndent,
64     CSSPropertyTextTransform,
65     CSSPropertyWhiteSpace,
66     CSSPropertyWidows,
67     CSSPropertyWordSpacing,
68     CSSPropertyWebkitBorderHorizontalSpacing,
69     CSSPropertyWebkitBorderVerticalSpacing,
70     CSSPropertyWebkitTextDecorationsInEffect,
71     CSSPropertyWebkitTextFillColor,
72     CSSPropertyWebkitTextSizeAdjust,
73     CSSPropertyWebkitTextStrokeColor,
74     CSSPropertyWebkitTextStrokeWidth,
75 };
76 size_t numEditingStyleProperties = WTF_ARRAY_LENGTH(editingStyleProperties);
77
78 static PassRefPtr<CSSMutableStyleDeclaration> copyEditingProperties(CSSStyleDeclaration* style)
79 {
80     return style->copyPropertiesInSet(editingStyleProperties, numEditingStyleProperties);
81 }
82
83 static PassRefPtr<CSSMutableStyleDeclaration> editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style)
84 {
85     if (!style)
86         return CSSMutableStyleDeclaration::create();
87     return copyEditingProperties(style.get());
88 }
89
90 class HTMLElementEquivalent {
91 public:
92     static PassOwnPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, int primitiveValue, const QualifiedName& tagName)
93     {
94         return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName));
95     }
96
97     virtual ~HTMLElementEquivalent() { }
98     virtual bool matches(Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); }
99     virtual bool hasAttribute() const { return false; }
100     bool propertyExistsInStyle(CSSStyleDeclaration* style) const { return style->getPropertyCSSValue(m_propertyID); }
101     virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
102     virtual void addToStyle(Element*, EditingStyle*) const;
103
104 protected:
105     HTMLElementEquivalent(CSSPropertyID);
106     HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName);
107     HTMLElementEquivalent(CSSPropertyID, int primitiveValue, const QualifiedName& tagName);
108     const int m_propertyID;
109     const RefPtr<CSSPrimitiveValue> m_primitiveValue;
110     const QualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global.
111 };
112
113 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id)
114     : m_propertyID(id)
115     , m_tagName(0)
116 {
117 }
118
119 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const QualifiedName& tagName)
120     : m_propertyID(id)
121     , m_tagName(&tagName)
122 {
123 }
124
125 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, int primitiveValue, const QualifiedName& tagName)
126     : m_propertyID(id)
127     , m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue))
128     , m_tagName(&tagName)
129 {
130     ASSERT(primitiveValue != CSSValueInvalid);
131 }
132
133 bool HTMLElementEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
134 {
135     RefPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID);
136     return matches(element) && value && value->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == m_primitiveValue->getIdent();
137 }
138
139 void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const
140 {
141     style->setProperty(m_propertyID, m_primitiveValue->cssText());
142 }
143
144 class HTMLTextDecorationEquivalent : public HTMLElementEquivalent {
145 public:
146     static PassOwnPtr<HTMLElementEquivalent> create(int primitiveValue, const QualifiedName& tagName)
147     {
148         return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName));
149     }
150     virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
151
152 private:
153     HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName);
154 };
155
156 HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName)
157     : HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName)
158 {
159 }
160
161 bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
162 {
163     RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID);
164     return matches(element) && styleValue && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(m_primitiveValue.get());
165 }
166
167 class HTMLAttributeEquivalent : public HTMLElementEquivalent {
168 public:
169     static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& tagName, const QualifiedName& attrName)
170     {
171         return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName));
172     }
173     static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName)
174     {
175         return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName));
176     }
177
178     bool matches(Element* elem) const { return HTMLElementEquivalent::matches(elem) && elem->hasAttribute(m_attrName); }
179     virtual bool hasAttribute() const { return true; }
180     virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
181     virtual void addToStyle(Element*, EditingStyle*) const;
182     virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
183     inline const QualifiedName& attributeName() const { return m_attrName; }
184
185 protected:
186     HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName);
187     HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName);
188     const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global.
189 };
190
191 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& tagName, const QualifiedName& attrName)
192     : HTMLElementEquivalent(id, tagName)
193     , m_attrName(attrName)
194 {
195 }
196
197 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName)
198     : HTMLElementEquivalent(id)
199     , m_attrName(attrName)
200 {
201 }
202
203 bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
204 {
205     RefPtr<CSSValue> value = attributeValueAsCSSValue(element);
206     RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID);
207     
208     // FIXME: This is very inefficient way of comparing values
209     // but we can't string compare attribute value and CSS property value.
210     return value && styleValue && value->cssText() == styleValue->cssText();
211 }
212
213 void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const
214 {
215     if (RefPtr<CSSValue> value = attributeValueAsCSSValue(element))
216         style->setProperty(m_propertyID, value->cssText());
217 }
218
219 PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const
220 {
221     ASSERT(element);
222     if (!element->hasAttribute(m_attrName))
223         return 0;
224     
225     RefPtr<CSSMutableStyleDeclaration> dummyStyle;
226     dummyStyle = CSSMutableStyleDeclaration::create();
227     dummyStyle->setProperty(m_propertyID, element->getAttribute(m_attrName));
228     return dummyStyle->getPropertyCSSValue(m_propertyID);
229 }
230
231 class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent {
232 public:
233     static PassOwnPtr<HTMLFontSizeEquivalent> create()
234     {
235         return adoptPtr(new HTMLFontSizeEquivalent());
236     }
237     virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
238
239 private:
240     HTMLFontSizeEquivalent();
241 };
242
243 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
244     : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr)
245 {
246 }
247
248 PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const
249 {
250     ASSERT(element);
251     if (!element->hasAttribute(m_attrName))
252         return 0;
253     int size;
254     if (!HTMLFontElement::cssValueFromFontSizeNumber(element->getAttribute(m_attrName), size))
255         return 0;
256     return CSSPrimitiveValue::createIdentifier(size);
257 }
258
259 float EditingStyle::NoFontDelta = 0.0f;
260
261 EditingStyle::EditingStyle()
262     : m_shouldUseFixedDefaultFontSize(false)
263     , m_fontSizeDelta(NoFontDelta)
264 {
265 }
266
267 EditingStyle::EditingStyle(Node* node, PropertiesToInclude propertiesToInclude)
268     : m_shouldUseFixedDefaultFontSize(false)
269     , m_fontSizeDelta(NoFontDelta)
270 {
271     init(node, propertiesToInclude);
272 }
273
274 EditingStyle::EditingStyle(const Position& position)
275     : m_shouldUseFixedDefaultFontSize(false)
276     , m_fontSizeDelta(NoFontDelta)
277 {
278     init(position.deprecatedNode(), OnlyInheritableProperties);
279 }
280
281 EditingStyle::EditingStyle(const CSSStyleDeclaration* style)
282     : m_mutableStyle(style->copy())
283     , m_shouldUseFixedDefaultFontSize(false)
284     , m_fontSizeDelta(NoFontDelta)
285 {
286     extractFontSizeDelta();
287 }
288
289 EditingStyle::EditingStyle(int propertyID, const String& value)
290     : m_mutableStyle(0)
291     , m_shouldUseFixedDefaultFontSize(false)
292     , m_fontSizeDelta(NoFontDelta)
293 {
294     setProperty(propertyID, value);
295 }
296
297 EditingStyle::~EditingStyle()
298 {
299 }
300
301 void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude)
302 {
303     RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = computedStyle(node);
304     m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copy() : editingStyleFromComputedStyle(computedStyleAtPosition);
305
306     if (node && node->computedStyle()) {
307         RenderStyle* renderStyle = node->computedStyle();
308         removeTextFillAndStrokeColorsIfNeeded(renderStyle);
309         replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get());
310     }
311
312     m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize();
313     extractFontSizeDelta();
314 }
315
316 void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle)
317 {
318     // If a node's text fill color is invalid, then its children use 
319     // their font-color as their text fill color (they don't
320     // inherit it).  Likewise for stroke color.
321     ExceptionCode ec = 0;
322     if (!renderStyle->textFillColor().isValid())
323         m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor, ec);
324     if (!renderStyle->textStrokeColor().isValid())
325         m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor, ec);
326     ASSERT(!ec);
327 }
328
329 void EditingStyle::setProperty(int propertyID, const String& value, bool important)
330 {
331     if (!m_mutableStyle)
332         m_mutableStyle = CSSMutableStyleDeclaration::create();
333
334     ExceptionCode ec;
335     m_mutableStyle->setProperty(propertyID, value, important, ec);
336 }
337
338 void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle)
339 {
340     ASSERT(renderStyle);
341     if (renderStyle->fontDescription().keywordSize())
342         m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText());
343 }
344
345 void EditingStyle::extractFontSizeDelta()
346 {
347     if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) {
348         // Explicit font size overrides any delta.
349         m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
350         return;
351     }
352
353     // Get the adjustment amount out of the style.
354     RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
355     if (!value || value->cssValueType() != CSSValue::CSS_PRIMITIVE_VALUE)
356         return;
357
358     CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get());
359
360     // Only PX handled now. If we handle more types in the future, perhaps
361     // a switch statement here would be more appropriate.
362     if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_PX)
363         return;
364
365     m_fontSizeDelta = primitiveValue->getFloatValue();
366     m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
367 }
368
369 bool EditingStyle::isEmpty() const
370 {
371     return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta;
372 }
373
374 bool EditingStyle::textDirection(WritingDirection& writingDirection) const
375 {
376     if (!m_mutableStyle)
377         return false;
378
379     RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
380     if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
381         return false;
382
383     int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
384     if (unicodeBidiValue == CSSValueEmbed) {
385         RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
386         if (!direction || !direction->isPrimitiveValue())
387             return false;
388
389         writingDirection = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
390
391         return true;
392     }
393
394     if (unicodeBidiValue == CSSValueNormal) {
395         writingDirection = NaturalWritingDirection;
396         return true;
397     }
398
399     return false;
400 }
401
402 void EditingStyle::setStyle(PassRefPtr<CSSMutableStyleDeclaration> style)
403 {
404     m_mutableStyle = style;
405     // FIXME: We should be able to figure out whether or not font is fixed width for mutable style.
406     // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here.
407     m_shouldUseFixedDefaultFontSize = false;
408     extractFontSizeDelta();
409 }
410
411 void EditingStyle::overrideWithStyle(const CSSMutableStyleDeclaration* style)
412 {
413     if (!style || !style->length())
414         return;
415     if (!m_mutableStyle)
416         m_mutableStyle = CSSMutableStyleDeclaration::create();
417     m_mutableStyle->merge(style);
418     extractFontSizeDelta();
419 }
420
421 void EditingStyle::clear()
422 {
423     m_mutableStyle.clear();
424     m_shouldUseFixedDefaultFontSize = false;
425     m_fontSizeDelta = NoFontDelta;
426 }
427
428 PassRefPtr<EditingStyle> EditingStyle::copy() const
429 {
430     RefPtr<EditingStyle> copy = EditingStyle::create();
431     if (m_mutableStyle)
432         copy->m_mutableStyle = m_mutableStyle->copy();
433     copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize;
434     copy->m_fontSizeDelta = m_fontSizeDelta;
435     return copy;
436 }
437
438 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties()
439 {
440     RefPtr<EditingStyle> blockProperties = EditingStyle::create();
441     if (!m_mutableStyle)
442         return blockProperties;
443
444     blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties();
445     m_mutableStyle->removeBlockProperties();
446
447     return blockProperties;
448 }
449
450 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection()
451 {
452     RefPtr<EditingStyle> textDirection = EditingStyle::create();
453     textDirection->m_mutableStyle = CSSMutableStyleDeclaration::create();
454     textDirection->m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed, m_mutableStyle->getPropertyPriority(CSSPropertyUnicodeBidi));
455     textDirection->m_mutableStyle->setProperty(CSSPropertyDirection, m_mutableStyle->getPropertyValue(CSSPropertyDirection),
456         m_mutableStyle->getPropertyPriority(CSSPropertyDirection));
457
458     m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi);
459     m_mutableStyle->removeProperty(CSSPropertyDirection);
460
461     return textDirection;
462 }
463
464 void EditingStyle::removeBlockProperties()
465 {
466     if (!m_mutableStyle)
467         return;
468
469     m_mutableStyle->removeBlockProperties();
470 }
471
472 void EditingStyle::removeStyleAddedByNode(Node* node)
473 {
474     if (!node || !node->parentNode())
475         return;
476     RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
477     RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
478     parentStyle->diff(nodeStyle.get());
479     nodeStyle->diff(m_mutableStyle.get());
480 }
481
482 void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node)
483 {
484     if (!node || !node->parentNode() || !m_mutableStyle)
485         return;
486     RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
487     RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
488     parentStyle->diff(nodeStyle.get());
489
490     CSSMutableStyleDeclaration::const_iterator end = nodeStyle->end();
491     for (CSSMutableStyleDeclaration::const_iterator it = nodeStyle->begin(); it != end; ++it)
492         m_mutableStyle->removeProperty(it->id());
493 }
494
495 void EditingStyle::removeNonEditingProperties()
496 {
497     if (m_mutableStyle)
498         m_mutableStyle = copyEditingProperties(m_mutableStyle.get());
499 }
500
501 void EditingStyle::collapseTextDecorationProperties()
502 {
503     if (!m_mutableStyle)
504         return;
505
506     RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
507     if (!textDecorationsInEffect)
508         return;
509
510     m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->getPropertyPriority(CSSPropertyTextDecoration));
511     m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
512 }
513
514 // CSS properties that create a visual difference only when applied to text.
515 static const int textOnlyProperties[] = {
516     CSSPropertyTextDecoration,
517     CSSPropertyWebkitTextDecorationsInEffect,
518     CSSPropertyFontStyle,
519     CSSPropertyFontWeight,
520     CSSPropertyColor,
521 };
522
523 TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
524 {
525     RefPtr<CSSMutableStyleDeclaration> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare);
526
527     if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
528         difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
529
530     if (!difference->length())
531         return TrueTriState;
532     if (difference->length() == m_mutableStyle->length())
533         return FalseTriState;
534
535     return MixedTriState;
536 }
537
538 bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
539 {
540     ASSERT(element);
541     ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
542
543     CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl();
544     if (!m_mutableStyle || !inlineStyle)
545         return false;
546
547     if (!conflictingProperties) {
548         CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
549         for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
550             CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id());
551
552             // We don't override whitespace property of a tab span because that would collapse the tab into a space.
553             if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element))
554                 continue;
555
556             if (inlineStyle->getPropertyCSSValue(propertyID))
557                 return true;
558         }
559
560         return false;
561     }
562
563     CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
564     for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
565         CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id());
566         if ((propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element)) || !inlineStyle->getPropertyCSSValue(propertyID))
567             continue;
568
569         if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
570             if (extractedStyle)
571                 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
572             conflictingProperties->append(CSSPropertyDirection);
573         }
574
575         conflictingProperties->append(propertyID);
576         if (extractedStyle)
577             extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
578     }
579
580     return !conflictingProperties->isEmpty();
581 }
582
583 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
584 {
585     if (!m_mutableStyle)
586         return false;
587
588     static const HTMLElementEquivalent* HTMLEquivalents[] = {
589         HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag).leakPtr(),
590         HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag).leakPtr(),
591         HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag).leakPtr(),
592         HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag).leakPtr(),
593         HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag).leakPtr(),
594         HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag).leakPtr(),
595
596         HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag).leakPtr(),
597         HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag).leakPtr(),
598         HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag).leakPtr(),
599     };
600
601     for (size_t i = 0; i < WTF_ARRAY_LENGTH(HTMLEquivalents); ++i) {
602         const HTMLElementEquivalent* equivalent = HTMLEquivalents[i];
603         if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get())
604             && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) {
605             if (extractedStyle)
606                 equivalent->addToStyle(element, extractedStyle);
607             return true;
608         }
609     }
610     return false;
611 }
612
613 static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents()
614 {
615     DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ());
616
617     if (!HTMLAttributeEquivalents.size()) {
618         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
619         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
620         HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create());
621
622         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr));
623         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
624     }
625
626     return HTMLAttributeEquivalents;
627 }
628
629 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
630 {
631     ASSERT(element);
632     if (!m_mutableStyle)
633         return false;
634
635     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
636     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
637         if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get())
638             && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get()))
639             return true;
640     }
641
642     return false;
643 }
644
645 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
646     EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
647 {
648     ASSERT(element);
649     // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
650     ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection);
651     if (!m_mutableStyle)
652         return false;
653
654     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
655     bool removed = false;
656     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
657         const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
658
659         // unicode-bidi and direction are pushed down separately so don't push down with other styles.
660         if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr)
661             continue;
662
663         if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
664             || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
665             continue;
666
667         if (extractedStyle)
668             equivalent->addToStyle(element, extractedStyle);
669         conflictingAttributes.append(equivalent->attributeName());
670         removed = true;
671     }
672
673     return removed;
674 }
675
676 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
677 {
678     return !m_mutableStyle || !getPropertiesNotIn(m_mutableStyle.get(), computedStyle(node).get())->length();
679 }
680
681 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection)
682 {
683     if (!m_mutableStyle)
684         return;
685
686     // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
687     // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
688     // which one of editingStyleAtPosition or computedStyle is called.
689     RefPtr<EditingStyle> style = EditingStyle::create(position);
690
691     RefPtr<CSSValue> unicodeBidi;
692     RefPtr<CSSValue> direction;
693     if (shouldPreserveWritingDirection == PreserveWritingDirection) {
694         unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
695         direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
696     }
697
698     style->m_mutableStyle->diff(m_mutableStyle.get());
699
700     // if alpha value is zero, we don't add the background color.
701     RefPtr<CSSValue> backgroundColor = m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
702     if (backgroundColor && backgroundColor->isPrimitiveValue()
703         && !alphaChannel(static_cast<CSSPrimitiveValue*>(backgroundColor.get())->getRGBA32Value())) {
704         ExceptionCode ec;
705         m_mutableStyle->removeProperty(CSSPropertyBackgroundColor, ec);
706     }
707
708     if (unicodeBidi && unicodeBidi->isPrimitiveValue()) {
709         m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent());
710         if (direction && direction->isPrimitiveValue())
711             m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
712     }
713 }
714
715 void EditingStyle::mergeTypingStyle(Document* document)
716 {
717     ASSERT(document);
718
719     RefPtr<EditingStyle> typingStyle = document->frame()->selection()->typingStyle();
720     if (!typingStyle || typingStyle == this)
721         return;
722
723     mergeStyle(typingStyle->style());
724 }
725
726 void EditingStyle::mergeInlineStyleOfElement(StyledElement* element)
727 {
728     ASSERT(element);
729     mergeStyle(element->inlineStyleDecl());
730 }
731
732 void EditingStyle::mergeStyle(CSSMutableStyleDeclaration* style)
733 {
734     if (!style)
735         return;
736
737     if (!m_mutableStyle) {
738         m_mutableStyle = style->copy();
739         return;
740     }
741
742     CSSMutableStyleDeclaration::const_iterator end = style->end();
743     for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
744         RefPtr<CSSValue> value;
745         if ((it->id() == CSSPropertyTextDecoration || it->id() == CSSPropertyWebkitTextDecorationsInEffect) && it->value()->isValueList()) {
746             value = m_mutableStyle->getPropertyCSSValue(it->id());
747             if (value && !value->isValueList())
748                 value = 0;
749         }
750
751         if (!value) {
752             ExceptionCode ec;
753             m_mutableStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec);
754             continue;
755         }
756
757         CSSValueList* newTextDecorations = static_cast<CSSValueList*>(it->value());
758         CSSValueList* textDecorations = static_cast<CSSValueList*>(value.get());
759
760         DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
761         DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
762
763         if (newTextDecorations->hasValue(underline.get()) && !textDecorations->hasValue(underline.get()))
764             textDecorations->append(underline.get());
765
766         if (newTextDecorations->hasValue(lineThrough.get()) && !textDecorations->hasValue(lineThrough.get()))
767             textDecorations->append(lineThrough.get());
768     }
769 }
770
771 }