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