8a7120bdfbb5360c162e90eeb8479612049852f1
[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.getPropertyCSSValue(propertyID);
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 (!is<CSSPrimitiveValue>(value.get()))
146         return 0;
147     return downcast<CSSPrimitiveValue>(*value).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) && is<CSSPrimitiveValue>(value.get()) && downcast<CSSPrimitiveValue>(*value).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) && is<CSSValueList>(styleValue.get()) && downcast<CSSValueList>(*styleValue).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 (!is<CSSPrimitiveValue>(colorValue))
360         return Color::transparent;
361     
362     CSSPrimitiveValue& primitiveColor = downcast<CSSPrimitiveValue>(*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 (!is<CSSPrimitiveValue>(value.get()))
480         return;
481
482     CSSPrimitiveValue& primitiveValue = downcast<CSSPrimitiveValue>(*value);
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 (!is<CSSPrimitiveValue>(unicodeBidi.get()))
505         return false;
506
507     CSSValueID unicodeBidiValue = downcast<CSSPrimitiveValue>(*unicodeBidi).getValueID();
508     if (unicodeBidiValue == CSSValueEmbed) {
509         RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
510         if (!is<CSSPrimitiveValue>(direction.get()))
511             return false;
512
513         writingDirection = downcast<CSSPrimitiveValue>(*direction).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, DoNotIgnoreTextOnlyProperties);
646 }
647
648 template<typename T>
649 TriState EditingStyle::triStateOfStyle(T& styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
650 {
651     if (!m_mutableStyle)
652         return TrueTriState;
653
654     RefPtr<MutableStyleProperties> difference = getPropertiesNotIn(*m_mutableStyle, styleToCompare);
655
656     if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
657         difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
658
659     if (difference->isEmpty())
660         return TrueTriState;
661     if (difference->propertyCount() == m_mutableStyle->propertyCount())
662         return FalseTriState;
663
664     return MixedTriState;
665 }
666
667 TriState EditingStyle::triStateOfStyle(const VisibleSelection& selection) const
668 {
669     if (!selection.isCaretOrRange())
670         return FalseTriState;
671
672     if (selection.isCaret())
673         return triStateOfStyle(EditingStyle::styleAtSelectionStart(selection).get());
674
675     TriState state = FalseTriState;
676     bool nodeIsStart = true;
677     for (Node* node = selection.start().deprecatedNode(); node; node = NodeTraversal::next(*node)) {
678         if (node->renderer() && node->hasEditableStyle()) {
679             ComputedStyleExtractor computedStyle(node);
680             TriState nodeState = triStateOfStyle(computedStyle, node->isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties : EditingStyle::IgnoreTextOnlyProperties);
681             if (nodeIsStart) {
682                 state = nodeState;
683                 nodeIsStart = false;
684             } else if (state != nodeState && node->isTextNode()) {
685                 state = MixedTriState;
686                 break;
687             }
688         }
689
690         if (node == selection.end().deprecatedNode())
691             break;
692     }
693
694     return state;
695 }
696
697 bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
698 {
699     ASSERT(element);
700     ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
701
702     const StyleProperties* inlineStyle = element->inlineStyle();
703     if (!m_mutableStyle || !inlineStyle)
704         return false;
705
706     unsigned propertyCount = m_mutableStyle->propertyCount();
707     for (unsigned i = 0; i < propertyCount; ++i) {
708         CSSPropertyID propertyID = m_mutableStyle->propertyAt(i).id();
709
710         // We don't override whitespace property of a tab span because that would collapse the tab into a space.
711         if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element))
712             continue;
713
714         if (propertyID == CSSPropertyWebkitTextDecorationsInEffect && inlineStyle->getPropertyCSSValue(CSSPropertyTextDecoration)) {
715             if (!conflictingProperties)
716                 return true;
717             conflictingProperties->append(CSSPropertyTextDecoration);
718             if (extractedStyle)
719                 extractedStyle->setProperty(CSSPropertyTextDecoration, inlineStyle->getPropertyValue(CSSPropertyTextDecoration), inlineStyle->propertyIsImportant(CSSPropertyTextDecoration));
720             continue;
721         }
722
723         if (!inlineStyle->getPropertyCSSValue(propertyID))
724             continue;
725
726         if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
727             if (!conflictingProperties)
728                 return true;
729             conflictingProperties->append(CSSPropertyDirection);
730             if (extractedStyle)
731                 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID));
732         }
733
734         if (!conflictingProperties)
735             return true;
736
737         conflictingProperties->append(propertyID);
738
739         if (extractedStyle)
740             extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID));
741     }
742
743     return conflictingProperties && !conflictingProperties->isEmpty();
744 }
745
746 static const Vector<std::unique_ptr<HTMLElementEquivalent>>& htmlElementEquivalents()
747 {
748     static NeverDestroyed<Vector<std::unique_ptr<HTMLElementEquivalent>>> HTMLElementEquivalents;
749
750     if (!HTMLElementEquivalents.get().size()) {
751         HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag));
752         HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag));
753         HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag));
754         HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag));
755         HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag));
756         HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag));
757
758         HTMLElementEquivalents.get().append(std::make_unique<HTMLTextDecorationEquivalent>(CSSValueUnderline, HTMLNames::uTag));
759         HTMLElementEquivalents.get().append(std::make_unique<HTMLTextDecorationEquivalent>(CSSValueLineThrough, HTMLNames::sTag));
760         HTMLElementEquivalents.get().append(std::make_unique<HTMLTextDecorationEquivalent>(CSSValueLineThrough, HTMLNames::strikeTag));
761     }
762
763     return HTMLElementEquivalents;
764 }
765
766
767 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
768 {
769     if (!m_mutableStyle)
770         return false;
771
772     const Vector<std::unique_ptr<HTMLElementEquivalent>>& HTMLElementEquivalents = htmlElementEquivalents();
773     for (size_t i = 0; i < HTMLElementEquivalents.size(); ++i) {
774         const HTMLElementEquivalent* equivalent = HTMLElementEquivalents[i].get();
775         if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get())
776             && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) {
777             if (extractedStyle)
778                 equivalent->addToStyle(element, extractedStyle);
779             return true;
780         }
781     }
782     return false;
783 }
784
785 static const Vector<std::unique_ptr<HTMLAttributeEquivalent>>& htmlAttributeEquivalents()
786 {
787     static NeverDestroyed<Vector<std::unique_ptr<HTMLAttributeEquivalent>>> HTMLAttributeEquivalents;
788
789     if (!HTMLAttributeEquivalents.get().size()) {
790         // elementIsStyledSpanOrHTMLEquivalent depends on the fact each HTMLAttriuteEquivalent matches exactly one attribute
791         // of exactly one element except dirAttr.
792         HTMLAttributeEquivalents.get().append(std::make_unique<HTMLAttributeEquivalent>(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
793         HTMLAttributeEquivalents.get().append(std::make_unique<HTMLAttributeEquivalent>(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
794         HTMLAttributeEquivalents.get().append(std::make_unique<HTMLFontSizeEquivalent>());
795
796         HTMLAttributeEquivalents.get().append(std::make_unique<HTMLAttributeEquivalent>(CSSPropertyDirection, HTMLNames::dirAttr));
797         HTMLAttributeEquivalents.get().append(std::make_unique<HTMLAttributeEquivalent>(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
798     }
799
800     return HTMLAttributeEquivalents;
801 }
802
803 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
804 {
805     ASSERT(element);
806     if (!m_mutableStyle)
807         return false;
808
809     const Vector<std::unique_ptr<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = htmlAttributeEquivalents();
810     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
811         if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get())
812             && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get()))
813             return true;
814     }
815
816     return false;
817 }
818
819 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
820     EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
821 {
822     ASSERT(element);
823     // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
824     ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection);
825     if (!m_mutableStyle)
826         return false;
827
828     const Vector<std::unique_ptr<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = htmlAttributeEquivalents();
829     bool removed = false;
830     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
831         const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
832
833         // unicode-bidi and direction are pushed down separately so don't push down with other styles.
834         if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr)
835             continue;
836
837         if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
838             || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
839             continue;
840
841         if (extractedStyle)
842             equivalent->addToStyle(element, extractedStyle);
843         conflictingAttributes.append(equivalent->attributeName());
844         removed = true;
845     }
846
847     return removed;
848 }
849
850 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
851 {
852     if (!m_mutableStyle)
853         return true;
854     ComputedStyleExtractor computedStyle(node);
855     return getPropertiesNotIn(*m_mutableStyle, computedStyle)->isEmpty();
856 }
857
858 bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement* element)
859 {
860     bool elementIsSpanOrElementEquivalent = false;
861     if (element->hasTagName(HTMLNames::spanTag))
862         elementIsSpanOrElementEquivalent = true;
863     else {
864         const Vector<std::unique_ptr<HTMLElementEquivalent>>& HTMLElementEquivalents = htmlElementEquivalents();
865         size_t i;
866         for (i = 0; i < HTMLElementEquivalents.size(); ++i) {
867             if (HTMLElementEquivalents[i]->matches(element)) {
868                 elementIsSpanOrElementEquivalent = true;
869                 break;
870             }
871         }
872     }
873
874     if (!element->hasAttributes())
875         return elementIsSpanOrElementEquivalent; // span, b, etc... without any attributes
876
877     unsigned matchedAttributes = 0;
878     const Vector<std::unique_ptr<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = htmlAttributeEquivalents();
879     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
880         if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->attributeName() != HTMLNames::dirAttr)
881             matchedAttributes++;
882     }
883
884     if (!elementIsSpanOrElementEquivalent && !matchedAttributes)
885         return false; // element is not a span, a html element equivalent, or font element.
886     
887     if (element->getAttribute(HTMLNames::classAttr) == AppleStyleSpanClass)
888         matchedAttributes++;
889
890     if (element->hasAttribute(HTMLNames::styleAttr)) {
891         if (const StyleProperties* style = element->inlineStyle()) {
892             unsigned propertyCount = style->propertyCount();
893             for (unsigned i = 0; i < propertyCount; ++i) {
894                 if (!isEditingProperty(style->propertyAt(i).id()))
895                     return false;
896             }
897         }
898         matchedAttributes++;
899     }
900
901     // font with color attribute, span with style attribute, etc...
902     ASSERT(matchedAttributes <= element->attributeCount());
903     return matchedAttributes >= element->attributeCount();
904 }
905
906 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection)
907 {
908     if (!m_mutableStyle)
909         return;
910
911     // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
912     // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
913     // which one of editingStyleAtPosition or computedStyle is called.
914     RefPtr<EditingStyle> editingStyleAtPosition = EditingStyle::create(position, EditingPropertiesInEffect);
915     StyleProperties* styleAtPosition = editingStyleAtPosition->m_mutableStyle.get();
916
917     RefPtr<CSSValue> unicodeBidi;
918     RefPtr<CSSValue> direction;
919     if (shouldPreserveWritingDirection == PreserveWritingDirection) {
920         unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
921         direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
922     }
923
924     removeEquivalentProperties(*styleAtPosition);
925
926     if (textAlignResolvingStartAndEnd(*m_mutableStyle) == textAlignResolvingStartAndEnd(*styleAtPosition))
927         m_mutableStyle->removeProperty(CSSPropertyTextAlign);
928
929     if (textColorFromStyle(*m_mutableStyle) == textColorFromStyle(*styleAtPosition))
930         m_mutableStyle->removeProperty(CSSPropertyColor);
931
932     if (hasTransparentBackgroundColor(m_mutableStyle.get())
933         || cssValueToRGBA(m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor).get()) == rgbaBackgroundColorInEffect(position.containerNode()))
934         m_mutableStyle->removeProperty(CSSPropertyBackgroundColor);
935
936     if (is<CSSPrimitiveValue>(unicodeBidi.get())) {
937         m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSValueID>(downcast<CSSPrimitiveValue>(*unicodeBidi).getValueID()));
938         if (is<CSSPrimitiveValue>(direction.get()))
939             m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSValueID>(downcast<CSSPrimitiveValue>(*direction).getValueID()));
940     }
941 }
942
943 void EditingStyle::mergeTypingStyle(Document& document)
944 {
945     RefPtr<EditingStyle> typingStyle = document.frame()->selection().typingStyle();
946     if (!typingStyle || typingStyle == this)
947         return;
948
949     mergeStyle(typingStyle->style(), OverrideValues);
950 }
951
952 void EditingStyle::mergeInlineStyleOfElement(StyledElement* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude)
953 {
954     ASSERT(element);
955     if (!element->inlineStyle())
956         return;
957
958     switch (propertiesToInclude) {
959     case AllProperties:
960         mergeStyle(element->inlineStyle(), mode);
961         return;
962     case OnlyEditingInheritableProperties:
963         mergeStyle(copyEditingProperties(element->inlineStyle(), OnlyInheritableEditingProperties).get(), mode);
964         return;
965     case EditingPropertiesInEffect:
966         mergeStyle(copyEditingProperties(element->inlineStyle(), AllEditingProperties).get(), mode);
967         return;
968     }
969 }
970
971 static inline bool elementMatchesAndPropertyIsNotInInlineStyleDecl(const HTMLElementEquivalent* equivalent, const StyledElement* element,
972     EditingStyle::CSSPropertyOverrideMode mode, StyleProperties* style)
973 {
974     return equivalent->matches(element) && (!element->inlineStyle() || !equivalent->propertyExistsInStyle(element->inlineStyle()))
975         && (mode == EditingStyle::OverrideValues || !equivalent->propertyExistsInStyle(style));
976 }
977
978 static PassRefPtr<MutableStyleProperties> extractEditingProperties(const StyleProperties* style, EditingStyle::PropertiesToInclude propertiesToInclude)
979 {
980     if (!style)
981         return 0;
982
983     switch (propertiesToInclude) {
984     case EditingStyle::AllProperties:
985     case EditingStyle::EditingPropertiesInEffect:
986         return copyEditingProperties(style, AllEditingProperties);
987     case EditingStyle::OnlyEditingInheritableProperties:
988         return copyEditingProperties(style, OnlyInheritableEditingProperties);
989     }
990
991     ASSERT_NOT_REACHED();
992     return 0;
993 }
994
995 void EditingStyle::mergeInlineAndImplicitStyleOfElement(StyledElement* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude)
996 {
997     RefPtr<EditingStyle> styleFromRules = EditingStyle::create();
998     styleFromRules->mergeStyleFromRulesForSerialization(element);
999
1000     if (element->inlineStyle())
1001         styleFromRules->m_mutableStyle->mergeAndOverrideOnConflict(*element->inlineStyle());
1002
1003     styleFromRules->m_mutableStyle = extractEditingProperties(styleFromRules->m_mutableStyle.get(), propertiesToInclude);
1004     mergeStyle(styleFromRules->m_mutableStyle.get(), mode);
1005
1006     const Vector<std::unique_ptr<HTMLElementEquivalent>>& elementEquivalents = htmlElementEquivalents();
1007     for (size_t i = 0; i < elementEquivalents.size(); ++i) {
1008         if (elementMatchesAndPropertyIsNotInInlineStyleDecl(elementEquivalents[i].get(), element, mode, m_mutableStyle.get()))
1009             elementEquivalents[i]->addToStyle(element, this);
1010     }
1011
1012     const Vector<std::unique_ptr<HTMLAttributeEquivalent>>& attributeEquivalents = htmlAttributeEquivalents();
1013     for (size_t i = 0; i < attributeEquivalents.size(); ++i) {
1014         if (attributeEquivalents[i]->attributeName() == HTMLNames::dirAttr)
1015             continue; // We don't want to include directionality
1016         if (elementMatchesAndPropertyIsNotInInlineStyleDecl(attributeEquivalents[i].get(), element, mode, m_mutableStyle.get()))
1017             attributeEquivalents[i]->addToStyle(element, this);
1018     }
1019 }
1020
1021 PassRefPtr<EditingStyle> EditingStyle::wrappingStyleForSerialization(Node* context, bool shouldAnnotate)
1022 {
1023     RefPtr<EditingStyle> wrappingStyle;
1024     if (shouldAnnotate) {
1025         wrappingStyle = EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect);
1026
1027         // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote,
1028         // to help us differentiate those styles from ones that the user has applied.
1029         // This helps us get the color of content pasted into blockquotes right.
1030         wrappingStyle->removeStyleAddedByNode(enclosingNodeOfType(firstPositionInOrBeforeNode(context), isMailBlockquote, CanCrossEditingBoundary));
1031
1032         // Call collapseTextDecorationProperties first or otherwise it'll copy the value over from in-effect to text-decorations.
1033         wrappingStyle->collapseTextDecorationProperties();
1034         
1035         return wrappingStyle.release();
1036     }
1037
1038     wrappingStyle = EditingStyle::create();
1039
1040     // When not annotating for interchange, we only preserve inline style declarations.
1041     for (Node* node = context; node && !node->isDocumentNode(); node = node->parentNode()) {
1042         if (is<StyledElement>(*node) && !isMailBlockquote(node)) {
1043             wrappingStyle->mergeInlineAndImplicitStyleOfElement(downcast<StyledElement>(node), EditingStyle::DoNotOverrideValues,
1044                 EditingStyle::EditingPropertiesInEffect);
1045         }
1046     }
1047
1048     return wrappingStyle.release();
1049 }
1050
1051
1052 static void mergeTextDecorationValues(CSSValueList* mergedValue, const CSSValueList* valueToMerge)
1053 {
1054     RefPtr<CSSPrimitiveValue> underline = cssValuePool().createIdentifierValue(CSSValueUnderline);
1055     RefPtr<CSSPrimitiveValue> lineThrough = cssValuePool().createIdentifierValue(CSSValueLineThrough);
1056
1057     if (valueToMerge->hasValue(underline.get()) && !mergedValue->hasValue(underline.get()))
1058         mergedValue->append(underline.releaseNonNull());
1059
1060     if (valueToMerge->hasValue(lineThrough.get()) && !mergedValue->hasValue(lineThrough.get()))
1061         mergedValue->append(lineThrough.releaseNonNull());
1062 }
1063
1064 void EditingStyle::mergeStyle(const StyleProperties* style, CSSPropertyOverrideMode mode)
1065 {
1066     if (!style)
1067         return;
1068
1069     if (!m_mutableStyle) {
1070         m_mutableStyle = style->mutableCopy();
1071         return;
1072     }
1073
1074     unsigned propertyCount = style->propertyCount();
1075     for (unsigned i = 0; i < propertyCount; ++i) {
1076         StyleProperties::PropertyReference property = style->propertyAt(i);
1077         RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(property.id());
1078
1079         // text decorations never override values.
1080         if ((property.id() == CSSPropertyTextDecoration || property.id() == CSSPropertyWebkitTextDecorationsInEffect) && property.value()->isValueList() && value) {
1081             if (is<CSSValueList>(*value)) {
1082                 mergeTextDecorationValues(downcast<CSSValueList>(value.get()), downcast<CSSValueList>(property.value()));
1083                 continue;
1084             }
1085             value = nullptr; // text-decoration: none is equivalent to not having the property.
1086         }
1087
1088         if (mode == OverrideValues || (mode == DoNotOverrideValues && !value))
1089             m_mutableStyle->setProperty(property.id(), property.value()->cssText(), property.isImportant());
1090     }
1091
1092     int oldFontSizeDelta = m_fontSizeDelta;
1093     extractFontSizeDelta();
1094     m_fontSizeDelta += oldFontSizeDelta;
1095 }
1096
1097 static PassRefPtr<MutableStyleProperties> styleFromMatchedRulesForElement(Element* element, unsigned rulesToInclude)
1098 {
1099     RefPtr<MutableStyleProperties> style = MutableStyleProperties::create();
1100     auto matchedRules = element->document().ensureStyleResolver().styleRulesForElement(element, rulesToInclude);
1101     for (unsigned i = 0; i < matchedRules.size(); ++i) {
1102         if (matchedRules[i]->isStyleRule())
1103             style->mergeAndOverrideOnConflict(static_pointer_cast<StyleRule>(matchedRules[i])->properties());
1104     }
1105     
1106     return style.release();
1107 }
1108
1109 void EditingStyle::mergeStyleFromRules(StyledElement* element)
1110 {
1111     RefPtr<MutableStyleProperties> styleFromMatchedRules = styleFromMatchedRulesForElement(element,
1112         StyleResolver::AuthorCSSRules | StyleResolver::CrossOriginCSSRules);
1113     // Styles from the inline style declaration, held in the variable "style", take precedence 
1114     // over those from matched rules.
1115     if (m_mutableStyle)
1116         styleFromMatchedRules->mergeAndOverrideOnConflict(*m_mutableStyle);
1117
1118     clear();
1119     m_mutableStyle = styleFromMatchedRules;
1120 }
1121
1122 void EditingStyle::mergeStyleFromRulesForSerialization(StyledElement* element)
1123 {
1124     mergeStyleFromRules(element);
1125
1126     // The property value, if it's a percentage, may not reflect the actual computed value.  
1127     // For example: style="height: 1%; overflow: visible;" in quirksmode
1128     // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem
1129     RefPtr<MutableStyleProperties> fromComputedStyle = MutableStyleProperties::create();
1130     ComputedStyleExtractor computedStyle(element);
1131
1132     {
1133         unsigned propertyCount = m_mutableStyle->propertyCount();
1134         for (unsigned i = 0; i < propertyCount; ++i) {
1135             StyleProperties::PropertyReference property = m_mutableStyle->propertyAt(i);
1136             CSSValue* value = property.value();
1137             if (!is<CSSPrimitiveValue>(*value))
1138                 continue;
1139             if (downcast<CSSPrimitiveValue>(*value).isPercentage()) {
1140                 if (RefPtr<CSSValue> computedPropertyValue = computedStyle.propertyValue(property.id()))
1141                     fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue.release()));
1142             }
1143         }
1144     }
1145     m_mutableStyle->mergeAndOverrideOnConflict(*fromComputedStyle);
1146 }
1147
1148 static void removePropertiesInStyle(MutableStyleProperties* styleToRemovePropertiesFrom, MutableStyleProperties* style)
1149 {
1150     unsigned propertyCount = style->propertyCount();
1151     Vector<CSSPropertyID> propertiesToRemove(propertyCount);
1152     for (unsigned i = 0; i < propertyCount; ++i)
1153         propertiesToRemove[i] = style->propertyAt(i).id();
1154
1155     styleToRemovePropertiesFrom->removePropertiesInSet(propertiesToRemove.data(), propertiesToRemove.size());
1156 }
1157
1158 void EditingStyle::removeStyleFromRulesAndContext(StyledElement* element, Node* context)
1159 {
1160     ASSERT(element);
1161     if (!m_mutableStyle)
1162         return;
1163
1164     // 1. Remove style from matched rules because style remain without repeating it in inline style declaration
1165     RefPtr<MutableStyleProperties> styleFromMatchedRules = styleFromMatchedRulesForElement(element, StyleResolver::AllButEmptyCSSRules);
1166     if (styleFromMatchedRules && !styleFromMatchedRules->isEmpty())
1167         m_mutableStyle = getPropertiesNotIn(*m_mutableStyle, *styleFromMatchedRules);
1168
1169     // 2. Remove style present in context and not overriden by matched rules.
1170     RefPtr<EditingStyle> computedStyle = EditingStyle::create(context, EditingPropertiesInEffect);
1171     if (computedStyle->m_mutableStyle) {
1172         if (!computedStyle->m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor))
1173             computedStyle->m_mutableStyle->setProperty(CSSPropertyBackgroundColor, CSSValueTransparent);
1174
1175         removePropertiesInStyle(computedStyle->m_mutableStyle.get(), styleFromMatchedRules.get());
1176         m_mutableStyle = getPropertiesNotIn(*m_mutableStyle, *computedStyle->m_mutableStyle);
1177     }
1178
1179     // 3. If this element is a span and has display: inline or float: none, remove them unless they are overriden by rules.
1180     // These rules are added by serialization code to wrap text nodes.
1181     if (isStyleSpanOrSpanWithOnlyStyleAttribute(element)) {
1182         if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyDisplay) && identifierForStyleProperty(*m_mutableStyle, CSSPropertyDisplay) == CSSValueInline)
1183             m_mutableStyle->removeProperty(CSSPropertyDisplay);
1184         if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyFloat) && identifierForStyleProperty(*m_mutableStyle, CSSPropertyFloat) == CSSValueNone)
1185             m_mutableStyle->removeProperty(CSSPropertyFloat);
1186     }
1187 }
1188
1189 void EditingStyle::removePropertiesInElementDefaultStyle(Element* element)
1190 {
1191     if (!m_mutableStyle || m_mutableStyle->isEmpty())
1192         return;
1193
1194     RefPtr<MutableStyleProperties> defaultStyle = styleFromMatchedRulesForElement(element, StyleResolver::UAAndUserCSSRules);
1195
1196     removePropertiesInStyle(m_mutableStyle.get(), defaultStyle.get());
1197 }
1198
1199 template<typename T>
1200 void EditingStyle::removeEquivalentProperties(const T& style)
1201 {
1202     Vector<CSSPropertyID> propertiesToRemove;
1203     for (auto& property : m_mutableStyle->m_propertyVector) {
1204         if (style.propertyMatches(property.id(), property.value()))
1205             propertiesToRemove.append(property.id());
1206     }
1207     // FIXME: This should use mass removal.
1208     for (auto& property : propertiesToRemove)
1209         m_mutableStyle->removeProperty(property);
1210 }
1211
1212 void EditingStyle::forceInline()
1213 {
1214     if (!m_mutableStyle)
1215         m_mutableStyle = MutableStyleProperties::create();
1216     const bool propertyIsImportant = true;
1217     m_mutableStyle->setProperty(CSSPropertyDisplay, CSSValueInline, propertyIsImportant);
1218 }
1219
1220 bool EditingStyle::convertPositionStyle()
1221 {
1222     if (!m_mutableStyle)
1223         return false;
1224
1225     RefPtr<CSSPrimitiveValue> sticky = cssValuePool().createIdentifierValue(CSSValueWebkitSticky);
1226     if (m_mutableStyle->propertyMatches(CSSPropertyPosition, sticky.get())) {
1227         m_mutableStyle->setProperty(CSSPropertyPosition, cssValuePool().createIdentifierValue(CSSValueStatic), m_mutableStyle->propertyIsImportant(CSSPropertyPosition));
1228         return false;
1229     }
1230     RefPtr<CSSPrimitiveValue> fixed = cssValuePool().createIdentifierValue(CSSValueFixed);
1231     if (m_mutableStyle->propertyMatches(CSSPropertyPosition, fixed.get())) {
1232         m_mutableStyle->setProperty(CSSPropertyPosition, cssValuePool().createIdentifierValue(CSSValueAbsolute), m_mutableStyle->propertyIsImportant(CSSPropertyPosition));
1233         return true;
1234     }
1235     RefPtr<CSSPrimitiveValue> absolute = cssValuePool().createIdentifierValue(CSSValueAbsolute);
1236     if (m_mutableStyle->propertyMatches(CSSPropertyPosition, absolute.get()))
1237         return true;
1238     return false;
1239 }
1240
1241 bool EditingStyle::isFloating()
1242 {
1243     RefPtr<CSSValue> v = m_mutableStyle->getPropertyCSSValue(CSSPropertyFloat);
1244     RefPtr<CSSPrimitiveValue> noneValue = cssValuePool().createIdentifierValue(CSSValueNone);
1245     return v && !v->equals(*noneValue);
1246 }
1247
1248 int EditingStyle::legacyFontSize(Document* document) const
1249 {
1250     RefPtr<CSSValue> cssValue = m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize);
1251     if (!is<CSSPrimitiveValue>(cssValue.get()))
1252         return 0;
1253     return legacyFontSizeFromCSSValue(document, downcast<CSSPrimitiveValue>(cssValue.get()),
1254         m_shouldUseFixedDefaultFontSize, AlwaysUseLegacyFontSize);
1255 }
1256
1257 PassRefPtr<EditingStyle> EditingStyle::styleAtSelectionStart(const VisibleSelection& selection, bool shouldUseBackgroundColorInEffect)
1258 {
1259     if (selection.isNone())
1260         return 0;
1261
1262     Position position = adjustedSelectionStartForStyleComputation(selection);
1263
1264     // If the pos is at the end of a text node, then this node is not fully selected. 
1265     // Move it to the next deep equivalent position to avoid removing the style from this node. 
1266     // e.g. if pos was at Position("hello", 5) in <b>hello<div>world</div></b>, we want Position("world", 0) instead. 
1267     // We only do this for range because caret at Position("hello", 5) in <b>hello</b>world should give you font-weight: bold. 
1268     Node* positionNode = position.containerNode(); 
1269     if (selection.isRange() && positionNode && positionNode->isTextNode() && position.computeOffsetInContainerNode() == positionNode->maxCharacterOffset()) 
1270         position = nextVisuallyDistinctCandidate(position); 
1271
1272     Element* element = position.element();
1273     if (!element)
1274         return 0;
1275
1276     RefPtr<EditingStyle> style = EditingStyle::create(element, EditingStyle::AllProperties);
1277     style->mergeTypingStyle(element->document());
1278
1279     // If background color is transparent, traverse parent nodes until we hit a different value or document root
1280     // Also, if the selection is a range, ignore the background color at the start of selection,
1281     // and find the background color of the common ancestor.
1282     if (shouldUseBackgroundColorInEffect && (selection.isRange() || hasTransparentBackgroundColor(style->m_mutableStyle.get()))) {
1283         RefPtr<Range> range(selection.toNormalizedRange());
1284         if (PassRefPtr<CSSValue> value = backgroundColorInEffect(range->commonAncestorContainer(IGNORE_EXCEPTION)))
1285             style->setProperty(CSSPropertyBackgroundColor, value->cssText());
1286     }
1287
1288     return style;
1289 }
1290
1291 WritingDirection EditingStyle::textDirectionForSelection(const VisibleSelection& selection, EditingStyle* typingStyle, bool& hasNestedOrMultipleEmbeddings)
1292 {
1293     hasNestedOrMultipleEmbeddings = true;
1294
1295     if (selection.isNone())
1296         return NaturalWritingDirection;
1297
1298     Position position = selection.start().downstream();
1299
1300     Node* node = position.deprecatedNode();
1301     if (!node)
1302         return NaturalWritingDirection;
1303
1304     Position end;
1305     if (selection.isRange()) {
1306         end = selection.end().upstream();
1307
1308         Node* pastLast = Range::create(*end.document(), position.parentAnchoredEquivalent(), end.parentAnchoredEquivalent())->pastLastNode();
1309         for (Node* n = node; n && n != pastLast; n = NodeTraversal::next(*n)) {
1310             if (!n->isStyledElement())
1311                 continue;
1312
1313             RefPtr<CSSValue> unicodeBidi = ComputedStyleExtractor(n).propertyValue(CSSPropertyUnicodeBidi);
1314             if (!is<CSSPrimitiveValue>(unicodeBidi.get()))
1315                 continue;
1316
1317             CSSValueID unicodeBidiValue = downcast<CSSPrimitiveValue>(*unicodeBidi).getValueID();
1318             if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride)
1319                 return NaturalWritingDirection;
1320         }
1321     }
1322
1323     if (selection.isCaret()) {
1324         WritingDirection direction;
1325         if (typingStyle && typingStyle->textDirection(direction)) {
1326             hasNestedOrMultipleEmbeddings = false;
1327             return direction;
1328         }
1329         node = selection.visibleStart().deepEquivalent().deprecatedNode();
1330     }
1331
1332     // 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
1333     // to decide.
1334     Node* block = enclosingBlock(node);
1335     WritingDirection foundDirection = NaturalWritingDirection;
1336
1337     for (; node != block; node = node->parentNode()) {
1338         if (!node->isStyledElement())
1339             continue;
1340
1341         ComputedStyleExtractor computedStyle(node);
1342         RefPtr<CSSValue> unicodeBidi = computedStyle.propertyValue(CSSPropertyUnicodeBidi);
1343         if (!is<CSSPrimitiveValue>(unicodeBidi.get()))
1344             continue;
1345
1346         CSSValueID unicodeBidiValue = downcast<CSSPrimitiveValue>(*unicodeBidi).getValueID();
1347         if (unicodeBidiValue == CSSValueNormal)
1348             continue;
1349
1350         if (unicodeBidiValue == CSSValueBidiOverride)
1351             return NaturalWritingDirection;
1352
1353         ASSERT(unicodeBidiValue == CSSValueEmbed);
1354         RefPtr<CSSValue> direction = computedStyle.propertyValue(CSSPropertyDirection);
1355         if (!is<CSSPrimitiveValue>(direction.get()))
1356             continue;
1357
1358         CSSValueID directionValue = downcast<CSSPrimitiveValue>(*direction).getValueID();
1359         if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
1360             continue;
1361
1362         if (foundDirection != NaturalWritingDirection)
1363             return NaturalWritingDirection;
1364
1365         // In the range case, make sure that the embedding element persists until the end of the range.
1366         if (selection.isRange() && !end.deprecatedNode()->isDescendantOf(node))
1367             return NaturalWritingDirection;
1368         
1369         foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
1370     }
1371     hasNestedOrMultipleEmbeddings = false;
1372     return foundDirection;
1373 }
1374
1375 static void reconcileTextDecorationProperties(MutableStyleProperties* style)
1376 {    
1377     RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1378     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
1379     // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense.
1380     ASSERT(!textDecorationsInEffect || !textDecoration);
1381     if (textDecorationsInEffect) {
1382         style->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText());
1383         style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
1384         textDecoration = textDecorationsInEffect;
1385     }
1386
1387     // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none".
1388     if (textDecoration && !textDecoration->isValueList())
1389         style->removeProperty(CSSPropertyTextDecoration);
1390 }
1391
1392 StyleChange::StyleChange(EditingStyle* style, const Position& position)
1393     : m_applyBold(false)
1394     , m_applyItalic(false)
1395     , m_applyUnderline(false)
1396     , m_applyLineThrough(false)
1397     , m_applySubscript(false)
1398     , m_applySuperscript(false)
1399 {
1400     Document* document = position.anchorNode() ? &position.anchorNode()->document() : 0;
1401     if (!style || !style->style() || !document || !document->frame())
1402         return;
1403
1404     Node* node = position.containerNode();
1405     if (!node)
1406         return;
1407
1408     ComputedStyleExtractor computedStyle(node);
1409
1410     // FIXME: take care of background-color in effect
1411     RefPtr<MutableStyleProperties> mutableStyle = getPropertiesNotIn(*style->style(), computedStyle);
1412
1413     reconcileTextDecorationProperties(mutableStyle.get());
1414     if (!document->frame()->editor().shouldStyleWithCSS())
1415         extractTextStyles(document, *mutableStyle, computedStyle.useFixedFontDefaultSize());
1416
1417     // Changing the whitespace style in a tab span would collapse the tab into a space.
1418     if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode())))
1419         mutableStyle->removeProperty(CSSPropertyWhiteSpace);
1420
1421     // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle.
1422     // FIXME: Shouldn't this be done in getPropertiesNotIn?
1423     if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection))
1424         mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection));
1425
1426     // Save the result for later
1427     m_cssStyle = mutableStyle->asText().stripWhiteSpace();
1428 }
1429
1430 static void setTextDecorationProperty(MutableStyleProperties& style, const CSSValueList* newTextDecoration, CSSPropertyID propertyID)
1431 {
1432     if (newTextDecoration->length())
1433         style.setProperty(propertyID, newTextDecoration->cssText(), style.propertyIsImportant(propertyID));
1434     else {
1435         // text-decoration: none is redundant since it does not remove any text decorations.
1436         style.removeProperty(propertyID);
1437     }
1438 }
1439
1440 void StyleChange::extractTextStyles(Document* document, MutableStyleProperties& style, bool shouldUseFixedFontDefaultSize)
1441 {
1442     if (identifierForStyleProperty(style, CSSPropertyFontWeight) == CSSValueBold) {
1443         style.removeProperty(CSSPropertyFontWeight);
1444         m_applyBold = true;
1445     }
1446
1447     int fontStyle = identifierForStyleProperty(style, CSSPropertyFontStyle);
1448     if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) {
1449         style.removeProperty(CSSPropertyFontStyle);
1450         m_applyItalic = true;
1451     }
1452
1453     // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
1454     // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
1455     RefPtr<CSSValue> textDecoration = style.getPropertyCSSValue(CSSPropertyTextDecoration);
1456     if (is<CSSValueList>(textDecoration.get())) {
1457         RefPtr<CSSPrimitiveValue> underline = cssValuePool().createIdentifierValue(CSSValueUnderline);
1458         RefPtr<CSSPrimitiveValue> lineThrough = cssValuePool().createIdentifierValue(CSSValueLineThrough);
1459
1460         RefPtr<CSSValueList> newTextDecoration = downcast<CSSValueList>(*textDecoration).copy();
1461         if (newTextDecoration->removeAll(underline.get()))
1462             m_applyUnderline = true;
1463         if (newTextDecoration->removeAll(lineThrough.get()))
1464             m_applyLineThrough = true;
1465
1466         // If trimTextDecorations, delete underline and line-through
1467         setTextDecorationProperty(style, newTextDecoration.get(), CSSPropertyTextDecoration);
1468     }
1469
1470     int verticalAlign = identifierForStyleProperty(style, CSSPropertyVerticalAlign);
1471     switch (verticalAlign) {
1472     case CSSValueSub:
1473         style.removeProperty(CSSPropertyVerticalAlign);
1474         m_applySubscript = true;
1475         break;
1476     case CSSValueSuper:
1477         style.removeProperty(CSSPropertyVerticalAlign);
1478         m_applySuperscript = true;
1479         break;
1480     }
1481
1482     if (style.getPropertyCSSValue(CSSPropertyColor)) {
1483         m_applyFontColor = Color(textColorFromStyle(style)).serialized();
1484         style.removeProperty(CSSPropertyColor);
1485     }
1486
1487     m_applyFontFace = style.getPropertyValue(CSSPropertyFontFamily);
1488     // Remove single quotes for Outlook 2007 compatibility. See https://bugs.webkit.org/show_bug.cgi?id=79448
1489     m_applyFontFace.replaceWithLiteral('\'', "");
1490     style.removeProperty(CSSPropertyFontFamily);
1491
1492     if (RefPtr<CSSValue> fontSize = style.getPropertyCSSValue(CSSPropertyFontSize)) {
1493         if (!is<CSSPrimitiveValue>(*fontSize))
1494             style.removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
1495         else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, downcast<CSSPrimitiveValue>(fontSize.get()),
1496                 shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) {
1497             m_applyFontSize = String::number(legacyFontSize);
1498             style.removeProperty(CSSPropertyFontSize);
1499         }
1500     }
1501 }
1502
1503 static void diffTextDecorations(MutableStyleProperties& style, CSSPropertyID propertID, CSSValue* refTextDecoration)
1504 {
1505     RefPtr<CSSValue> textDecoration = style.getPropertyCSSValue(propertID);
1506     if (!is<CSSValueList>(textDecoration.get()) || !is<CSSValueList>(refTextDecoration))
1507         return;
1508
1509     RefPtr<CSSValueList> newTextDecoration = downcast<CSSValueList>(*textDecoration).copy();
1510     CSSValueList& valuesInRefTextDecoration = downcast<CSSValueList>(*refTextDecoration);
1511
1512     for (size_t i = 0; i < valuesInRefTextDecoration.length(); ++i)
1513         newTextDecoration->removeAll(valuesInRefTextDecoration.item(i));
1514
1515     setTextDecorationProperty(style, newTextDecoration.get(), propertID);
1516 }
1517
1518 static bool fontWeightIsBold(CSSValue& fontWeight)
1519 {
1520     if (!is<CSSPrimitiveValue>(fontWeight))
1521         return false;
1522
1523     // Because b tag can only bold text, there are only two states in plain html: bold and not bold.
1524     // Collapse all other values to either one of these two states for editing purposes.
1525     switch (downcast<CSSPrimitiveValue>(fontWeight).getValueID()) {
1526         case CSSValue100:
1527         case CSSValue200:
1528         case CSSValue300:
1529         case CSSValue400:
1530         case CSSValue500:
1531         case CSSValueNormal:
1532             return false;
1533         case CSSValueBold:
1534         case CSSValue600:
1535         case CSSValue700:
1536         case CSSValue800:
1537         case CSSValue900:
1538             return true;
1539         default:
1540             break;
1541     }
1542
1543     ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
1544     return false;
1545 }
1546
1547 template<typename T>
1548 static bool fontWeightIsBold(T& style)
1549 {
1550     RefPtr<CSSValue> fontWeight = extractPropertyValue(style, CSSPropertyFontWeight);
1551     return fontWeight && fontWeightIsBold(*fontWeight);
1552 }
1553
1554 template<typename T>
1555 static PassRefPtr<MutableStyleProperties> extractPropertiesNotIn(StyleProperties& styleWithRedundantProperties, T& baseStyle)
1556 {
1557     RefPtr<EditingStyle> result = EditingStyle::create(&styleWithRedundantProperties);
1558     result->removeEquivalentProperties(baseStyle);
1559     RefPtr<MutableStyleProperties> mutableStyle = result->style();
1560
1561     RefPtr<CSSValue> baseTextDecorationsInEffect = extractPropertyValue(baseStyle, CSSPropertyWebkitTextDecorationsInEffect);
1562     diffTextDecorations(*mutableStyle, CSSPropertyTextDecoration, baseTextDecorationsInEffect.get());
1563     diffTextDecorations(*mutableStyle, CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
1564
1565     if (extractPropertyValue(baseStyle, CSSPropertyFontWeight) && fontWeightIsBold(*mutableStyle) == fontWeightIsBold(baseStyle))
1566         mutableStyle->removeProperty(CSSPropertyFontWeight);
1567
1568     if (extractPropertyValue(baseStyle, CSSPropertyColor) && textColorFromStyle(*mutableStyle) == textColorFromStyle(baseStyle))
1569         mutableStyle->removeProperty(CSSPropertyColor);
1570
1571     if (extractPropertyValue(baseStyle, CSSPropertyTextAlign)
1572         && textAlignResolvingStartAndEnd(*mutableStyle) == textAlignResolvingStartAndEnd(baseStyle))
1573         mutableStyle->removeProperty(CSSPropertyTextAlign);
1574
1575     if (extractPropertyValue(baseStyle, CSSPropertyBackgroundColor) && backgroundColorFromStyle(*mutableStyle) == backgroundColorFromStyle(baseStyle))
1576         mutableStyle->removeProperty(CSSPropertyBackgroundColor);
1577
1578     return mutableStyle.release();
1579 }
1580
1581 template<typename T>
1582 PassRefPtr<MutableStyleProperties> getPropertiesNotIn(StyleProperties& styleWithRedundantProperties, T& baseStyle)
1583 {
1584     return extractPropertiesNotIn(styleWithRedundantProperties, baseStyle);
1585 }
1586
1587 static bool isCSSValueLength(CSSPrimitiveValue* value)
1588 {
1589     return value->isFontIndependentLength();
1590 }
1591
1592 int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode mode)
1593 {
1594     ASSERT(document); // FIXME: This method should take a Document&
1595
1596     if (isCSSValueLength(value)) {
1597         int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX);
1598         int legacyFontSize = Style::legacyFontSizeForPixelSize(pixelFontSize, shouldUseFixedFontDefaultSize, *document);
1599         // Use legacy font size only if pixel value matches exactly to that of legacy font size.
1600         int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall;
1601         if (mode == AlwaysUseLegacyFontSize || Style::fontSizeForKeyword(cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize, *document) == pixelFontSize)
1602             return legacyFontSize;
1603
1604         return 0;
1605     }
1606
1607     if (CSSValueXSmall <= value->getValueID() && value->getValueID() <= CSSValueWebkitXxxLarge)
1608         return value->getValueID() - CSSValueXSmall + 1;
1609
1610     return 0;
1611 }
1612
1613 bool isTransparentColorValue(CSSValue* cssValue)
1614 {
1615     if (!cssValue)
1616         return true;
1617     if (!is<CSSPrimitiveValue>(*cssValue))
1618         return false;
1619     CSSPrimitiveValue& value = downcast<CSSPrimitiveValue>(*cssValue);
1620     if (value.isRGBColor())
1621         return !alphaChannel(value.getRGBA32Value());
1622     return value.getValueID() == CSSValueTransparent;
1623 }
1624
1625 bool hasTransparentBackgroundColor(StyleProperties* style)
1626 {
1627     RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(CSSPropertyBackgroundColor);
1628     return isTransparentColorValue(cssValue.get());
1629 }
1630
1631 PassRefPtr<CSSValue> backgroundColorInEffect(Node* node)
1632 {
1633     for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
1634         if (RefPtr<CSSValue> value = ComputedStyleExtractor(ancestor).propertyValue(CSSPropertyBackgroundColor)) {
1635             if (!isTransparentColorValue(value.get()))
1636                 return value.release();
1637         }
1638     }
1639     return 0;
1640 }
1641
1642 }