2011-04-06 Ryosuke Niwa <rniwa@webkit.org>
[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     if (isTabSpanTextNode(node))
304         node = tabSpanNode(node)->parentNode();
305     else if (isTabSpanNode(node))
306         node = node->parentNode();
307
308     RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = computedStyle(node);
309     m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copy() : editingStyleFromComputedStyle(computedStyleAtPosition);
310
311     if (node && node->computedStyle()) {
312         RenderStyle* renderStyle = node->computedStyle();
313         removeTextFillAndStrokeColorsIfNeeded(renderStyle);
314         replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get());
315     }
316
317     m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize();
318     extractFontSizeDelta();
319 }
320
321 void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle)
322 {
323     // If a node's text fill color is invalid, then its children use 
324     // their font-color as their text fill color (they don't
325     // inherit it).  Likewise for stroke color.
326     ExceptionCode ec = 0;
327     if (!renderStyle->textFillColor().isValid())
328         m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor, ec);
329     if (!renderStyle->textStrokeColor().isValid())
330         m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor, ec);
331     ASSERT(!ec);
332 }
333
334 void EditingStyle::setProperty(int propertyID, const String& value, bool important)
335 {
336     if (!m_mutableStyle)
337         m_mutableStyle = CSSMutableStyleDeclaration::create();
338
339     ExceptionCode ec;
340     m_mutableStyle->setProperty(propertyID, value, important, ec);
341 }
342
343 void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle)
344 {
345     ASSERT(renderStyle);
346     if (renderStyle->fontDescription().keywordSize())
347         m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText());
348 }
349
350 void EditingStyle::extractFontSizeDelta()
351 {
352     if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) {
353         // Explicit font size overrides any delta.
354         m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
355         return;
356     }
357
358     // Get the adjustment amount out of the style.
359     RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
360     if (!value || value->cssValueType() != CSSValue::CSS_PRIMITIVE_VALUE)
361         return;
362
363     CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get());
364
365     // Only PX handled now. If we handle more types in the future, perhaps
366     // a switch statement here would be more appropriate.
367     if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_PX)
368         return;
369
370     m_fontSizeDelta = primitiveValue->getFloatValue();
371     m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
372 }
373
374 bool EditingStyle::isEmpty() const
375 {
376     return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta;
377 }
378
379 bool EditingStyle::textDirection(WritingDirection& writingDirection) const
380 {
381     if (!m_mutableStyle)
382         return false;
383
384     RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
385     if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
386         return false;
387
388     int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
389     if (unicodeBidiValue == CSSValueEmbed) {
390         RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
391         if (!direction || !direction->isPrimitiveValue())
392             return false;
393
394         writingDirection = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
395
396         return true;
397     }
398
399     if (unicodeBidiValue == CSSValueNormal) {
400         writingDirection = NaturalWritingDirection;
401         return true;
402     }
403
404     return false;
405 }
406
407 void EditingStyle::setStyle(PassRefPtr<CSSMutableStyleDeclaration> style)
408 {
409     m_mutableStyle = style;
410     // FIXME: We should be able to figure out whether or not font is fixed width for mutable style.
411     // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here.
412     m_shouldUseFixedDefaultFontSize = false;
413     extractFontSizeDelta();
414 }
415
416 void EditingStyle::overrideWithStyle(const CSSMutableStyleDeclaration* style)
417 {
418     if (!style || !style->length())
419         return;
420     if (!m_mutableStyle)
421         m_mutableStyle = CSSMutableStyleDeclaration::create();
422     m_mutableStyle->merge(style);
423     extractFontSizeDelta();
424 }
425
426 void EditingStyle::clear()
427 {
428     m_mutableStyle.clear();
429     m_shouldUseFixedDefaultFontSize = false;
430     m_fontSizeDelta = NoFontDelta;
431 }
432
433 PassRefPtr<EditingStyle> EditingStyle::copy() const
434 {
435     RefPtr<EditingStyle> copy = EditingStyle::create();
436     if (m_mutableStyle)
437         copy->m_mutableStyle = m_mutableStyle->copy();
438     copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize;
439     copy->m_fontSizeDelta = m_fontSizeDelta;
440     return copy;
441 }
442
443 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties()
444 {
445     RefPtr<EditingStyle> blockProperties = EditingStyle::create();
446     if (!m_mutableStyle)
447         return blockProperties;
448
449     blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties();
450     m_mutableStyle->removeBlockProperties();
451
452     return blockProperties;
453 }
454
455 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection()
456 {
457     RefPtr<EditingStyle> textDirection = EditingStyle::create();
458     textDirection->m_mutableStyle = CSSMutableStyleDeclaration::create();
459     textDirection->m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed, m_mutableStyle->getPropertyPriority(CSSPropertyUnicodeBidi));
460     textDirection->m_mutableStyle->setProperty(CSSPropertyDirection, m_mutableStyle->getPropertyValue(CSSPropertyDirection),
461         m_mutableStyle->getPropertyPriority(CSSPropertyDirection));
462
463     m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi);
464     m_mutableStyle->removeProperty(CSSPropertyDirection);
465
466     return textDirection;
467 }
468
469 void EditingStyle::removeBlockProperties()
470 {
471     if (!m_mutableStyle)
472         return;
473
474     m_mutableStyle->removeBlockProperties();
475 }
476
477 void EditingStyle::removeStyleAddedByNode(Node* node)
478 {
479     if (!node || !node->parentNode())
480         return;
481     RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
482     RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
483     parentStyle->diff(nodeStyle.get());
484     nodeStyle->diff(m_mutableStyle.get());
485 }
486
487 void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node)
488 {
489     if (!node || !node->parentNode() || !m_mutableStyle)
490         return;
491     RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
492     RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
493     parentStyle->diff(nodeStyle.get());
494
495     CSSMutableStyleDeclaration::const_iterator end = nodeStyle->end();
496     for (CSSMutableStyleDeclaration::const_iterator it = nodeStyle->begin(); it != end; ++it)
497         m_mutableStyle->removeProperty(it->id());
498 }
499
500 void EditingStyle::removeNonEditingProperties()
501 {
502     if (m_mutableStyle)
503         m_mutableStyle = copyEditingProperties(m_mutableStyle.get());
504 }
505
506 void EditingStyle::collapseTextDecorationProperties()
507 {
508     if (!m_mutableStyle)
509         return;
510
511     RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
512     if (!textDecorationsInEffect)
513         return;
514
515     m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->getPropertyPriority(CSSPropertyTextDecoration));
516     m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
517 }
518
519 // CSS properties that create a visual difference only when applied to text.
520 static const int textOnlyProperties[] = {
521     CSSPropertyTextDecoration,
522     CSSPropertyWebkitTextDecorationsInEffect,
523     CSSPropertyFontStyle,
524     CSSPropertyFontWeight,
525     CSSPropertyColor,
526 };
527
528 TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
529 {
530     RefPtr<CSSMutableStyleDeclaration> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare);
531
532     if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
533         difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
534
535     if (!difference->length())
536         return TrueTriState;
537     if (difference->length() == m_mutableStyle->length())
538         return FalseTriState;
539
540     return MixedTriState;
541 }
542
543 bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
544 {
545     ASSERT(element);
546     ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
547
548     CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl();
549     if (!m_mutableStyle || !inlineStyle)
550         return false;
551
552     if (!conflictingProperties) {
553         CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
554         for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
555             CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id());
556
557             // We don't override whitespace property of a tab span because that would collapse the tab into a space.
558             if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element))
559                 continue;
560
561             if (inlineStyle->getPropertyCSSValue(propertyID))
562                 return true;
563         }
564
565         return false;
566     }
567
568     CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
569     for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
570         CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id());
571         if ((propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element)) || !inlineStyle->getPropertyCSSValue(propertyID))
572             continue;
573
574         if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
575             if (extractedStyle)
576                 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
577             conflictingProperties->append(CSSPropertyDirection);
578         }
579
580         conflictingProperties->append(propertyID);
581         if (extractedStyle)
582             extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
583     }
584
585     return !conflictingProperties->isEmpty();
586 }
587
588 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
589 {
590     if (!m_mutableStyle)
591         return false;
592
593     static const HTMLElementEquivalent* HTMLEquivalents[] = {
594         HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag).leakPtr(),
595         HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag).leakPtr(),
596         HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag).leakPtr(),
597         HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag).leakPtr(),
598         HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag).leakPtr(),
599         HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag).leakPtr(),
600
601         HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag).leakPtr(),
602         HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag).leakPtr(),
603         HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag).leakPtr(),
604     };
605
606     for (size_t i = 0; i < WTF_ARRAY_LENGTH(HTMLEquivalents); ++i) {
607         const HTMLElementEquivalent* equivalent = HTMLEquivalents[i];
608         if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get())
609             && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) {
610             if (extractedStyle)
611                 equivalent->addToStyle(element, extractedStyle);
612             return true;
613         }
614     }
615     return false;
616 }
617
618 static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents()
619 {
620     DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ());
621
622     if (!HTMLAttributeEquivalents.size()) {
623         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
624         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
625         HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create());
626
627         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr));
628         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
629     }
630
631     return HTMLAttributeEquivalents;
632 }
633
634 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
635 {
636     ASSERT(element);
637     if (!m_mutableStyle)
638         return false;
639
640     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
641     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
642         if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get())
643             && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get()))
644             return true;
645     }
646
647     return false;
648 }
649
650 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
651     EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
652 {
653     ASSERT(element);
654     // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
655     ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection);
656     if (!m_mutableStyle)
657         return false;
658
659     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
660     bool removed = false;
661     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
662         const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
663
664         // unicode-bidi and direction are pushed down separately so don't push down with other styles.
665         if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr)
666             continue;
667
668         if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
669             || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
670             continue;
671
672         if (extractedStyle)
673             equivalent->addToStyle(element, extractedStyle);
674         conflictingAttributes.append(equivalent->attributeName());
675         removed = true;
676     }
677
678     return removed;
679 }
680
681 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
682 {
683     return !m_mutableStyle || !getPropertiesNotIn(m_mutableStyle.get(), computedStyle(node).get())->length();
684 }
685
686 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection)
687 {
688     if (!m_mutableStyle)
689         return;
690
691     // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
692     // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
693     // which one of editingStyleAtPosition or computedStyle is called.
694     RefPtr<EditingStyle> style = EditingStyle::create(position);
695
696     RefPtr<CSSValue> unicodeBidi;
697     RefPtr<CSSValue> direction;
698     if (shouldPreserveWritingDirection == PreserveWritingDirection) {
699         unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
700         direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
701     }
702
703     style->m_mutableStyle->diff(m_mutableStyle.get());
704
705     // if alpha value is zero, we don't add the background color.
706     RefPtr<CSSValue> backgroundColor = m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
707     if (backgroundColor && backgroundColor->isPrimitiveValue()
708         && !alphaChannel(static_cast<CSSPrimitiveValue*>(backgroundColor.get())->getRGBA32Value())) {
709         ExceptionCode ec;
710         m_mutableStyle->removeProperty(CSSPropertyBackgroundColor, ec);
711     }
712
713     if (unicodeBidi && unicodeBidi->isPrimitiveValue()) {
714         m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent());
715         if (direction && direction->isPrimitiveValue())
716             m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
717     }
718 }
719
720 void EditingStyle::mergeTypingStyle(Document* document)
721 {
722     ASSERT(document);
723
724     RefPtr<EditingStyle> typingStyle = document->frame()->selection()->typingStyle();
725     if (!typingStyle || typingStyle == this)
726         return;
727
728     mergeStyle(typingStyle->style());
729 }
730
731 void EditingStyle::mergeInlineStyleOfElement(StyledElement* element)
732 {
733     ASSERT(element);
734     mergeStyle(element->inlineStyleDecl());
735 }
736
737 void EditingStyle::mergeStyle(CSSMutableStyleDeclaration* style)
738 {
739     if (!style)
740         return;
741
742     if (!m_mutableStyle) {
743         m_mutableStyle = style->copy();
744         return;
745     }
746
747     CSSMutableStyleDeclaration::const_iterator end = style->end();
748     for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
749         RefPtr<CSSValue> value;
750         if ((it->id() == CSSPropertyTextDecoration || it->id() == CSSPropertyWebkitTextDecorationsInEffect) && it->value()->isValueList()) {
751             value = m_mutableStyle->getPropertyCSSValue(it->id());
752             if (value && !value->isValueList())
753                 value = 0;
754         }
755
756         if (!value) {
757             ExceptionCode ec;
758             m_mutableStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec);
759             continue;
760         }
761
762         CSSValueList* newTextDecorations = static_cast<CSSValueList*>(it->value());
763         CSSValueList* textDecorations = static_cast<CSSValueList*>(value.get());
764
765         DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
766         DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
767
768         if (newTextDecorations->hasValue(underline.get()) && !textDecorations->hasValue(underline.get()))
769             textDecorations->append(underline.get());
770
771         if (newTextDecorations->hasValue(lineThrough.get()) && !textDecorations->hasValue(lineThrough.get()))
772             textDecorations->append(lineThrough.get());
773     }
774 }
775
776 }