Toggling underline or strike through affects each other
[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 EditingStyle& style) const { return style.m_mutableStyle && style.m_mutableStyle->getPropertyCSSValue(m_propertyID); }
166     virtual bool valueIsPresentInStyle(Element&, const EditingStyle&) 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, const EditingStyle& style) const
198 {
199     RefPtr<CSSValue> value = style.m_mutableStyle->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         : HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName)
212         , m_isUnderline(primitiveValue == CSSValueUnderline)
213     {
214     }
215
216     bool propertyExistsInStyle(const EditingStyle& style) const override
217     {
218         if (changeInStyle(style) != TextDecorationChange::None)
219             return true;
220
221         if (!style.m_mutableStyle)
222             return false;
223
224         auto& mutableStyle = *style.m_mutableStyle;
225         return mutableStyle.getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect)
226             || mutableStyle.getPropertyCSSValue(CSSPropertyTextDecoration);
227     }
228
229     bool valueIsPresentInStyle(Element& element, const EditingStyle& style) const override
230     {
231         if (!matches(element))
232             return false;
233         auto change = changeInStyle(style);
234         if (change != TextDecorationChange::None)
235             return change == TextDecorationChange::Add;
236         RefPtr<CSSValue> styleValue = style.m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
237         if (!styleValue)
238             styleValue = style.m_mutableStyle->getPropertyCSSValue(CSSPropertyTextDecoration);
239         return is<CSSValueList>(styleValue.get()) && downcast<CSSValueList>(*styleValue).hasValue(m_primitiveValue.get());
240     }
241
242 private:
243     TextDecorationChange changeInStyle(const EditingStyle& style) const
244     {
245         return m_isUnderline ? style.underlineChange() : style.strikeThroughChange();
246     }
247
248     bool m_isUnderline;
249 };
250
251 class HTMLAttributeEquivalent : public HTMLElementEquivalent {
252 public:
253     HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName);
254     HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName);
255
256     bool matches(const Element& element) const { return HTMLElementEquivalent::matches(element) && element.hasAttribute(m_attrName); }
257     virtual bool hasAttribute() const { return true; }
258     virtual bool valueIsPresentInStyle(Element&, const EditingStyle&) const;
259     virtual void addToStyle(Element*, EditingStyle*) const;
260     virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
261     inline const QualifiedName& attributeName() const { return m_attrName; }
262
263 protected:
264     const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global.
265 };
266
267 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& tagName, const QualifiedName& attrName)
268     : HTMLElementEquivalent(id, tagName)
269     , m_attrName(attrName)
270 {
271 }
272
273 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName)
274     : HTMLElementEquivalent(id)
275     , m_attrName(attrName)
276 {
277 }
278
279 bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element& element, const EditingStyle& style) const
280 {
281     RefPtr<CSSValue> value = attributeValueAsCSSValue(&element);
282     RefPtr<CSSValue> styleValue = style.m_mutableStyle->getPropertyCSSValue(m_propertyID);
283     
284     return compareCSSValuePtr(value, styleValue);
285 }
286
287 void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const
288 {
289     if (RefPtr<CSSValue> value = attributeValueAsCSSValue(element))
290         style->setProperty(m_propertyID, value->cssText());
291 }
292
293 PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const
294 {
295     ASSERT(element);
296     const AtomicString& value = element->getAttribute(m_attrName);
297     if (value.isNull())
298         return nullptr;
299     
300     RefPtr<MutableStyleProperties> dummyStyle;
301     dummyStyle = MutableStyleProperties::create();
302     dummyStyle->setProperty(m_propertyID, value);
303     return dummyStyle->getPropertyCSSValue(m_propertyID);
304 }
305
306 class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent {
307 public:
308     HTMLFontSizeEquivalent();
309
310     virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
311 };
312
313 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
314     : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr)
315 {
316 }
317
318 PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const
319 {
320     ASSERT(element);
321     const AtomicString& value = element->getAttribute(m_attrName);
322     if (value.isNull())
323         return nullptr;
324     CSSValueID size;
325     if (!HTMLFontElement::cssValueFromFontSizeNumber(value, size))
326         return nullptr;
327     return CSSPrimitiveValue::createIdentifier(size);
328 }
329
330 float EditingStyle::NoFontDelta = 0.0f;
331
332 EditingStyle::EditingStyle()
333     : m_shouldUseFixedDefaultFontSize(false)
334     , m_underlineChange(static_cast<unsigned>(TextDecorationChange::None))
335     , m_strikeThroughChange(static_cast<unsigned>(TextDecorationChange::None))
336 {
337 }
338
339 EditingStyle::EditingStyle(Node* node, PropertiesToInclude propertiesToInclude)
340     : EditingStyle()
341 {
342     init(node, propertiesToInclude);
343 }
344
345 EditingStyle::EditingStyle(const Position& position, PropertiesToInclude propertiesToInclude)
346     : EditingStyle()
347 {
348     init(position.deprecatedNode(), propertiesToInclude);
349 }
350
351 EditingStyle::EditingStyle(const StyleProperties* style)
352     : EditingStyle()
353 {
354     if (style)
355         m_mutableStyle = style->mutableCopy();
356     extractFontSizeDelta();
357 }
358
359 EditingStyle::EditingStyle(CSSPropertyID propertyID, const String& value)
360     : EditingStyle()
361 {
362     setProperty(propertyID, value);
363     extractFontSizeDelta();
364 }
365
366 EditingStyle::EditingStyle(CSSPropertyID propertyID, CSSValueID value)
367     : EditingStyle()
368 {
369     m_mutableStyle = MutableStyleProperties::create();
370     m_mutableStyle->setProperty(propertyID, value);
371     extractFontSizeDelta();
372 }
373
374 EditingStyle::~EditingStyle()
375 {
376 }
377
378 static RGBA32 cssValueToRGBA(CSSValue* colorValue)
379 {
380     if (!is<CSSPrimitiveValue>(colorValue))
381         return Color::transparent;
382     
383     CSSPrimitiveValue& primitiveColor = downcast<CSSPrimitiveValue>(*colorValue);
384     if (primitiveColor.isRGBColor())
385         return primitiveColor.getRGBA32Value();
386     
387     RGBA32 rgba = 0;
388     CSSParser::parseColor(rgba, colorValue->cssText());
389     return rgba;
390 }
391
392 template<typename T>
393 static inline RGBA32 textColorFromStyle(T& style)
394 {
395     return cssValueToRGBA(extractPropertyValue(style, CSSPropertyColor).get());
396 }
397
398 template<typename T>
399 static inline RGBA32 backgroundColorFromStyle(T& style)
400 {
401     return cssValueToRGBA(extractPropertyValue(style, CSSPropertyBackgroundColor).get());
402 }
403
404 static inline RGBA32 rgbaBackgroundColorInEffect(Node* node)
405 {
406     return cssValueToRGBA(backgroundColorInEffect(node).get());
407 }
408
409 static int textAlignResolvingStartAndEnd(int textAlign, int direction)
410 {
411     switch (textAlign) {
412     case CSSValueCenter:
413     case CSSValueWebkitCenter:
414         return CSSValueCenter;
415     case CSSValueJustify:
416         return CSSValueJustify;
417     case CSSValueLeft:
418     case CSSValueWebkitLeft:
419         return CSSValueLeft;
420     case CSSValueRight:
421     case CSSValueWebkitRight:
422         return CSSValueRight;
423     case CSSValueStart:
424         return direction != CSSValueRtl ? CSSValueLeft : CSSValueRight;
425     case CSSValueEnd:
426         return direction == CSSValueRtl ? CSSValueRight : CSSValueLeft;
427     }
428     return CSSValueInvalid;
429 }
430
431 template<typename T>
432 static int textAlignResolvingStartAndEnd(T& style)
433 {
434     return textAlignResolvingStartAndEnd(identifierForStyleProperty(style, CSSPropertyTextAlign), identifierForStyleProperty(style, CSSPropertyDirection));
435 }
436
437 void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude)
438 {
439     if (isTabSpanTextNode(node))
440         node = tabSpanNode(node)->parentNode();
441     else if (isTabSpanNode(node))
442         node = node->parentNode();
443
444     ComputedStyleExtractor computedStyleAtPosition(node);
445     // FIXME: It's strange to not set background-color and text-decoration when propertiesToInclude is EditingPropertiesInEffect.
446     // However editing/selection/contains-boundaries.html fails without this ternary.
447     m_mutableStyle = copyPropertiesFromComputedStyle(computedStyleAtPosition,
448         propertiesToInclude == EditingPropertiesInEffect ? OnlyEditingInheritableProperties : propertiesToInclude);
449
450     if (propertiesToInclude == EditingPropertiesInEffect) {
451         if (RefPtr<CSSValue> value = backgroundColorInEffect(node))
452             m_mutableStyle->setProperty(CSSPropertyBackgroundColor, value->cssText());
453         if (RefPtr<CSSValue> value = computedStyleAtPosition.propertyValue(CSSPropertyWebkitTextDecorationsInEffect))
454             m_mutableStyle->setProperty(CSSPropertyTextDecoration, value->cssText());
455     }
456
457     if (node && node->computedStyle()) {
458         RenderStyle* renderStyle = node->computedStyle();
459         removeTextFillAndStrokeColorsIfNeeded(renderStyle);
460         if (renderStyle->fontDescription().keywordSize())
461             m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyleAtPosition.getFontSizeCSSValuePreferringKeyword()->cssText());
462     }
463
464     m_shouldUseFixedDefaultFontSize = computedStyleAtPosition.useFixedFontDefaultSize();
465     extractFontSizeDelta();
466 }
467
468 void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle)
469 {
470     // If a node's text fill color is invalid, then its children use 
471     // their font-color as their text fill color (they don't
472     // inherit it).  Likewise for stroke color.
473     if (!renderStyle->textFillColor().isValid())
474         m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor);
475     if (!renderStyle->textStrokeColor().isValid())
476         m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor);
477 }
478
479 void EditingStyle::setProperty(CSSPropertyID propertyID, const String& value, bool important)
480 {
481     if (!m_mutableStyle)
482         m_mutableStyle = MutableStyleProperties::create();
483
484     m_mutableStyle->setProperty(propertyID, value, important);
485 }
486
487 void EditingStyle::extractFontSizeDelta()
488 {
489     if (!m_mutableStyle)
490         return;
491
492     if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) {
493         // Explicit font size overrides any delta.
494         m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
495         return;
496     }
497
498     // Get the adjustment amount out of the style.
499     RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
500     if (!is<CSSPrimitiveValue>(value.get()))
501         return;
502
503     CSSPrimitiveValue& primitiveValue = downcast<CSSPrimitiveValue>(*value);
504
505     // Only PX handled now. If we handle more types in the future, perhaps
506     // a switch statement here would be more appropriate.
507     if (!primitiveValue.isPx())
508         return;
509
510     m_fontSizeDelta = primitiveValue.getFloatValue();
511     m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
512 }
513
514 bool EditingStyle::isEmpty() const
515 {
516     return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta
517         && underlineChange() == TextDecorationChange::None && strikeThroughChange() == TextDecorationChange::None;
518 }
519
520 bool EditingStyle::textDirection(WritingDirection& writingDirection) const
521 {
522     if (!m_mutableStyle)
523         return false;
524
525     RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
526     if (!is<CSSPrimitiveValue>(unicodeBidi.get()))
527         return false;
528
529     CSSValueID unicodeBidiValue = downcast<CSSPrimitiveValue>(*unicodeBidi).getValueID();
530     if (unicodeBidiValue == CSSValueEmbed) {
531         RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
532         if (!is<CSSPrimitiveValue>(direction.get()))
533             return false;
534
535         writingDirection = downcast<CSSPrimitiveValue>(*direction).getValueID() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
536
537         return true;
538     }
539
540     if (unicodeBidiValue == CSSValueNormal) {
541         writingDirection = NaturalWritingDirection;
542         return true;
543     }
544
545     return false;
546 }
547
548 void EditingStyle::setStyle(PassRefPtr<MutableStyleProperties> style)
549 {
550     m_mutableStyle = style;
551     // FIXME: We should be able to figure out whether or not font is fixed width for mutable style.
552     // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here.
553     m_shouldUseFixedDefaultFontSize = false;
554     extractFontSizeDelta();
555 }
556
557 void EditingStyle::overrideWithStyle(const StyleProperties* style)
558 {
559     return mergeStyle(style, OverrideValues);
560 }
561
562 static void applyTextDecorationChangeToValueList(CSSValueList& valueList, TextDecorationChange change, Ref<CSSPrimitiveValue>&& value)
563 {
564     switch (change) {
565     case TextDecorationChange::None:
566         break;
567     case TextDecorationChange::Add:
568         valueList.append(WTF::move(value));
569         break;
570     case TextDecorationChange::Remove:
571         valueList.removeAll(&value.get());
572         break;
573     }
574 }
575
576 void EditingStyle::overrideTypingStyleAt(const EditingStyle& style, const Position& position)
577 {
578     mergeStyle(style.m_mutableStyle.get(), OverrideValues);
579
580     m_fontSizeDelta += style.m_fontSizeDelta;
581
582     prepareToApplyAt(position, EditingStyle::PreserveWritingDirection);
583
584     auto underlineChange = style.underlineChange();
585     auto strikeThroughChange = style.strikeThroughChange();
586     if (underlineChange == TextDecorationChange::None && strikeThroughChange == TextDecorationChange::None)
587         return;
588
589     if (!m_mutableStyle)
590         m_mutableStyle = MutableStyleProperties::create();
591
592     Ref<CSSPrimitiveValue> underline = cssValuePool().createIdentifierValue(CSSValueUnderline);
593     Ref<CSSPrimitiveValue> lineThrough = cssValuePool().createIdentifierValue(CSSValueLineThrough);
594     RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
595     RefPtr<CSSValueList> valueList;
596     if (value && value->isValueList()) {
597         valueList = downcast<CSSValueList>(*value).copy();
598         applyTextDecorationChangeToValueList(*valueList, underlineChange, WTF::move(underline));
599         applyTextDecorationChangeToValueList(*valueList, strikeThroughChange, WTF::move(lineThrough));
600     } else {
601         valueList = CSSValueList::createSpaceSeparated();
602         if (underlineChange == TextDecorationChange::Add)
603             valueList->append(WTF::move(underline));
604         if (strikeThroughChange == TextDecorationChange::Add)
605             valueList->append(WTF::move(lineThrough));
606     }
607     m_mutableStyle->setProperty(CSSPropertyWebkitTextDecorationsInEffect, valueList.get());
608 }
609
610 void EditingStyle::clear()
611 {
612     m_mutableStyle.clear();
613     m_shouldUseFixedDefaultFontSize = false;
614     m_fontSizeDelta = NoFontDelta;
615     setUnderlineChange(TextDecorationChange::None);
616     setStrikeThroughChange(TextDecorationChange::None);
617 }
618
619 PassRefPtr<EditingStyle> EditingStyle::copy() const
620 {
621     RefPtr<EditingStyle> copy = EditingStyle::create();
622     if (m_mutableStyle)
623         copy->m_mutableStyle = m_mutableStyle->mutableCopy();
624     copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize;
625     copy->m_underlineChange = m_underlineChange;
626     copy->m_strikeThroughChange = m_strikeThroughChange;
627     copy->m_fontSizeDelta = m_fontSizeDelta;
628     return copy;
629 }
630
631 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties()
632 {
633     RefPtr<EditingStyle> blockProperties = EditingStyle::create();
634     if (!m_mutableStyle)
635         return blockProperties;
636
637     blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties();
638     m_mutableStyle->removeBlockProperties();
639
640     return blockProperties;
641 }
642
643 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection()
644 {
645     RefPtr<EditingStyle> textDirection = EditingStyle::create();
646     textDirection->m_mutableStyle = MutableStyleProperties::create();
647     textDirection->m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed, m_mutableStyle->propertyIsImportant(CSSPropertyUnicodeBidi));
648     textDirection->m_mutableStyle->setProperty(CSSPropertyDirection, m_mutableStyle->getPropertyValue(CSSPropertyDirection),
649         m_mutableStyle->propertyIsImportant(CSSPropertyDirection));
650
651     m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi);
652     m_mutableStyle->removeProperty(CSSPropertyDirection);
653
654     return textDirection;
655 }
656
657 void EditingStyle::removeBlockProperties()
658 {
659     if (!m_mutableStyle)
660         return;
661
662     m_mutableStyle->removeBlockProperties();
663 }
664
665 void EditingStyle::removeStyleAddedByNode(Node* node)
666 {
667     if (!node || !node->parentNode())
668         return;
669     RefPtr<MutableStyleProperties> parentStyle = copyPropertiesFromComputedStyle(node->parentNode(), EditingPropertiesInEffect);
670     RefPtr<MutableStyleProperties> nodeStyle = copyPropertiesFromComputedStyle(node, EditingPropertiesInEffect);
671     removeEquivalentProperties(*parentStyle);
672     removeEquivalentProperties(*nodeStyle);
673 }
674
675 void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node)
676 {
677     if (!node || !node->parentNode() || !m_mutableStyle)
678         return;
679
680     RefPtr<MutableStyleProperties> parentStyle = copyPropertiesFromComputedStyle(node->parentNode(), EditingPropertiesInEffect);
681     RefPtr<EditingStyle> nodeStyle = EditingStyle::create(node, EditingPropertiesInEffect);
682     nodeStyle->removeEquivalentProperties(*parentStyle);
683
684     MutableStyleProperties* style = nodeStyle->style();
685     unsigned propertyCount = style->propertyCount();
686     for (unsigned i = 0; i < propertyCount; ++i)
687         m_mutableStyle->removeProperty(style->propertyAt(i).id());
688 }
689
690 void EditingStyle::collapseTextDecorationProperties()
691 {
692     if (!m_mutableStyle)
693         return;
694
695     RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
696     if (!textDecorationsInEffect)
697         return;
698
699     if (textDecorationsInEffect->isValueList())
700         m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->propertyIsImportant(CSSPropertyTextDecoration));
701     else
702         m_mutableStyle->removeProperty(CSSPropertyTextDecoration);
703     m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
704 }
705
706 // CSS properties that create a visual difference only when applied to text.
707 static const CSSPropertyID textOnlyProperties[] = {
708     CSSPropertyTextDecoration,
709     CSSPropertyWebkitTextDecorationsInEffect,
710     CSSPropertyFontStyle,
711     CSSPropertyFontWeight,
712     CSSPropertyColor,
713 };
714
715 TriState EditingStyle::triStateOfStyle(EditingStyle* style) const
716 {
717     if (!style || !style->m_mutableStyle)
718         return FalseTriState;
719     return triStateOfStyle(*style->m_mutableStyle, DoNotIgnoreTextOnlyProperties);
720 }
721
722 template<typename T>
723 TriState EditingStyle::triStateOfStyle(T& styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
724 {
725     if (!m_mutableStyle)
726         return TrueTriState;
727
728     RefPtr<MutableStyleProperties> difference = getPropertiesNotIn(*m_mutableStyle, styleToCompare);
729
730     if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
731         difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
732
733     if (difference->isEmpty())
734         return TrueTriState;
735     if (difference->propertyCount() == m_mutableStyle->propertyCount())
736         return FalseTriState;
737
738     return MixedTriState;
739 }
740
741 TriState EditingStyle::triStateOfStyle(const VisibleSelection& selection) const
742 {
743     if (!selection.isCaretOrRange())
744         return FalseTriState;
745
746     if (selection.isCaret())
747         return triStateOfStyle(EditingStyle::styleAtSelectionStart(selection).get());
748
749     TriState state = FalseTriState;
750     bool nodeIsStart = true;
751     for (Node* node = selection.start().deprecatedNode(); node; node = NodeTraversal::next(*node)) {
752         if (node->renderer() && node->hasEditableStyle()) {
753             ComputedStyleExtractor computedStyle(node);
754             TriState nodeState = triStateOfStyle(computedStyle, node->isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties : EditingStyle::IgnoreTextOnlyProperties);
755             if (nodeIsStart) {
756                 state = nodeState;
757                 nodeIsStart = false;
758             } else if (state != nodeState && node->isTextNode()) {
759                 state = MixedTriState;
760                 break;
761             }
762         }
763
764         if (node == selection.end().deprecatedNode())
765             break;
766     }
767
768     return state;
769 }
770
771 static RefPtr<CSSValueList> textDecorationValueList(const StyleProperties& properties)
772 {
773     RefPtr<CSSValue> value = properties.getPropertyCSSValue(CSSPropertyTextDecoration);
774     if (!is<CSSValueList>(value.get()))
775         return nullptr;
776     return downcast<CSSValueList>(value.get());
777 }
778
779 bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, RefPtr<MutableStyleProperties>* newInlineStylePtr, EditingStyle* extractedStyle) const
780 {
781     ASSERT(element);
782
783     const StyleProperties* inlineStyle = element->inlineStyle();
784     if (!inlineStyle)
785         return false;
786     bool conflicts = false;
787     RefPtr<MutableStyleProperties> newInlineStyle;
788     if (newInlineStylePtr) {
789         newInlineStyle = inlineStyle->mutableCopy();
790         *newInlineStylePtr = newInlineStyle;
791     }
792
793     bool shouldRemoveUnderline = underlineChange() == TextDecorationChange::Remove;
794     bool shouldRemoveStrikeThrough = strikeThroughChange() == TextDecorationChange::Remove;
795     if (shouldRemoveUnderline || shouldRemoveStrikeThrough) {
796         if (RefPtr<CSSValueList> valueList = textDecorationValueList(*inlineStyle)) {
797             RefPtr<CSSValueList> newValueList = valueList->copy();
798             RefPtr<CSSValueList> extractedValueList = CSSValueList::createSpaceSeparated();
799
800             Ref<CSSPrimitiveValue> underline = cssValuePool().createIdentifierValue(CSSValueUnderline);
801             if (shouldRemoveUnderline && valueList->hasValue(underline.ptr())) {
802                 if (!newInlineStyle)
803                     return true;
804                 newValueList->removeAll(underline.ptr());
805                 extractedValueList->append(WTF::move(underline));
806             }
807
808             Ref<CSSPrimitiveValue> lineThrough = cssValuePool().createIdentifierValue(CSSValueLineThrough);
809             if (shouldRemoveStrikeThrough && valueList->hasValue(lineThrough.ptr())) {
810                 if (!newInlineStyle)
811                     return true;
812                 newValueList->removeAll(lineThrough.ptr());
813                 extractedValueList->append(WTF::move(lineThrough));
814             }
815
816             if (extractedValueList->length()) {
817                 conflicts = true;
818                 if (newValueList->length())
819                     newInlineStyle->setProperty(CSSPropertyTextDecoration, newValueList);
820                 else
821                     newInlineStyle->removeProperty(CSSPropertyTextDecoration);
822
823                 if (extractedStyle) {
824                     bool isImportant = inlineStyle->propertyIsImportant(CSSPropertyTextDecoration);
825                     extractedStyle->setProperty(CSSPropertyTextDecoration, extractedValueList->cssText(), isImportant);
826                 }
827             }
828         }
829     }
830
831     unsigned propertyCount = m_mutableStyle ? m_mutableStyle->propertyCount() : 0;
832     for (unsigned i = 0; i < propertyCount; ++i) {
833         CSSPropertyID propertyID = m_mutableStyle->propertyAt(i).id();
834
835         // We don't override whitespace property of a tab span because that would collapse the tab into a space.
836         if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element))
837             continue;
838
839         if (propertyID == CSSPropertyWebkitTextDecorationsInEffect && inlineStyle->getPropertyCSSValue(CSSPropertyTextDecoration)) {
840             if (!newInlineStyle)
841                 return true;
842             conflicts = true;
843             newInlineStyle->removeProperty(CSSPropertyTextDecoration);
844             if (extractedStyle)
845                 extractedStyle->setProperty(CSSPropertyTextDecoration, inlineStyle->getPropertyValue(CSSPropertyTextDecoration), inlineStyle->propertyIsImportant(CSSPropertyTextDecoration));
846         }
847
848         if (!inlineStyle->getPropertyCSSValue(propertyID))
849             continue;
850
851         if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
852             if (!newInlineStyle)
853                 return true;
854             conflicts = true;
855             newInlineStyle->removeProperty(CSSPropertyDirection);
856             if (extractedStyle)
857                 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID));
858         }
859
860         if (!newInlineStyle)
861             return true;
862
863         conflicts = true;
864         newInlineStyle->removeProperty(propertyID);
865         if (extractedStyle)
866             extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID));
867     }
868
869     return conflicts;
870 }
871
872 static const Vector<std::unique_ptr<HTMLElementEquivalent>>& htmlElementEquivalents()
873 {
874     static NeverDestroyed<Vector<std::unique_ptr<HTMLElementEquivalent>>> HTMLElementEquivalents;
875
876     if (!HTMLElementEquivalents.get().size()) {
877         HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag));
878         HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag));
879         HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag));
880         HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag));
881         HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag));
882         HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag));
883
884         HTMLElementEquivalents.get().append(std::make_unique<HTMLTextDecorationEquivalent>(CSSValueUnderline, HTMLNames::uTag));
885         HTMLElementEquivalents.get().append(std::make_unique<HTMLTextDecorationEquivalent>(CSSValueLineThrough, HTMLNames::sTag));
886         HTMLElementEquivalents.get().append(std::make_unique<HTMLTextDecorationEquivalent>(CSSValueLineThrough, HTMLNames::strikeTag));
887     }
888
889     return HTMLElementEquivalents;
890 }
891
892
893 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
894 {
895     if (isEmpty())
896         return false;
897
898     const Vector<std::unique_ptr<HTMLElementEquivalent>>& HTMLElementEquivalents = htmlElementEquivalents();
899     for (auto& equivalent : HTMLElementEquivalents) {
900         if (equivalent->matches(*element) && equivalent->propertyExistsInStyle(*this)
901             && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(*element, *this))) {
902             if (extractedStyle)
903                 equivalent->addToStyle(element, extractedStyle);
904             return true;
905         }
906     }
907     return false;
908 }
909
910 static const Vector<std::unique_ptr<HTMLAttributeEquivalent>>& htmlAttributeEquivalents()
911 {
912     static NeverDestroyed<Vector<std::unique_ptr<HTMLAttributeEquivalent>>> HTMLAttributeEquivalents;
913
914     if (!HTMLAttributeEquivalents.get().size()) {
915         // elementIsStyledSpanOrHTMLEquivalent depends on the fact each HTMLAttriuteEquivalent matches exactly one attribute
916         // of exactly one element except dirAttr.
917         HTMLAttributeEquivalents.get().append(std::make_unique<HTMLAttributeEquivalent>(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
918         HTMLAttributeEquivalents.get().append(std::make_unique<HTMLAttributeEquivalent>(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
919         HTMLAttributeEquivalents.get().append(std::make_unique<HTMLFontSizeEquivalent>());
920
921         HTMLAttributeEquivalents.get().append(std::make_unique<HTMLAttributeEquivalent>(CSSPropertyDirection, HTMLNames::dirAttr));
922         HTMLAttributeEquivalents.get().append(std::make_unique<HTMLAttributeEquivalent>(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
923     }
924
925     return HTMLAttributeEquivalents;
926 }
927
928 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
929 {
930     ASSERT(element);
931     if (isEmpty())
932         return false;
933
934     const Vector<std::unique_ptr<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = htmlAttributeEquivalents();
935     for (auto& equivalent : HTMLAttributeEquivalents) {
936         if (equivalent->matches(*element) && equivalent->propertyExistsInStyle(*this) && !equivalent->valueIsPresentInStyle(*element, *this))
937             return true;
938     }
939
940     return false;
941 }
942
943 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
944     EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
945 {
946     ASSERT(element);
947     // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
948     ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection);
949     if (!m_mutableStyle)
950         return false;
951
952     const Vector<std::unique_ptr<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = htmlAttributeEquivalents();
953     bool removed = false;
954     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
955         const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
956
957         // unicode-bidi and direction are pushed down separately so don't push down with other styles.
958         if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr)
959             continue;
960
961         if (!equivalent->matches(*element) || !equivalent->propertyExistsInStyle(*this)
962             || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(*element, *this)))
963             continue;
964
965         if (extractedStyle)
966             equivalent->addToStyle(element, extractedStyle);
967         conflictingAttributes.append(equivalent->attributeName());
968         removed = true;
969     }
970
971     return removed;
972 }
973
974 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
975 {
976     if (isEmpty())
977         return true;
978     ComputedStyleExtractor computedStyle(node);
979
980     bool shouldAddUnderline = underlineChange() == TextDecorationChange::Add;
981     bool shouldAddLineThrough = strikeThroughChange() == TextDecorationChange::Add;
982     if (shouldAddUnderline || shouldAddLineThrough) {
983         bool hasUnderline = false;
984         bool hasLineThrough = false;
985         if (RefPtr<CSSValue> value = computedStyle.propertyValue(CSSPropertyTextDecoration)) {
986             if (value->isValueList()) {
987                 const CSSValueList& valueList = downcast<CSSValueList>(*value);
988                 hasUnderline = valueList.hasValue(&cssValuePool().createIdentifierValue(CSSValueUnderline).get());
989                 hasLineThrough = valueList.hasValue(&cssValuePool().createIdentifierValue(CSSValueLineThrough).get());
990             }
991         }
992         if ((shouldAddUnderline && !hasUnderline) || (shouldAddLineThrough && !hasLineThrough))
993             return false;
994     }
995
996     return !m_mutableStyle || getPropertiesNotIn(*m_mutableStyle, computedStyle)->isEmpty();
997 }
998
999 bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement* element)
1000 {
1001     bool elementIsSpanOrElementEquivalent = false;
1002     if (element->hasTagName(HTMLNames::spanTag))
1003         elementIsSpanOrElementEquivalent = true;
1004     else {
1005         const Vector<std::unique_ptr<HTMLElementEquivalent>>& HTMLElementEquivalents = htmlElementEquivalents();
1006         size_t i;
1007         for (i = 0; i < HTMLElementEquivalents.size(); ++i) {
1008             if (HTMLElementEquivalents[i]->matches(*element)) {
1009                 elementIsSpanOrElementEquivalent = true;
1010                 break;
1011             }
1012         }
1013     }
1014
1015     if (!element->hasAttributes())
1016         return elementIsSpanOrElementEquivalent; // span, b, etc... without any attributes
1017
1018     unsigned matchedAttributes = 0;
1019     const Vector<std::unique_ptr<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = htmlAttributeEquivalents();
1020     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
1021         if (HTMLAttributeEquivalents[i]->matches(*element) && HTMLAttributeEquivalents[i]->attributeName() != HTMLNames::dirAttr)
1022             matchedAttributes++;
1023     }
1024
1025     if (!elementIsSpanOrElementEquivalent && !matchedAttributes)
1026         return false; // element is not a span, a html element equivalent, or font element.
1027     
1028     if (element->getAttribute(HTMLNames::classAttr) == AppleStyleSpanClass)
1029         matchedAttributes++;
1030
1031     if (element->hasAttribute(HTMLNames::styleAttr)) {
1032         if (const StyleProperties* style = element->inlineStyle()) {
1033             unsigned propertyCount = style->propertyCount();
1034             for (unsigned i = 0; i < propertyCount; ++i) {
1035                 if (!isEditingProperty(style->propertyAt(i).id()))
1036                     return false;
1037             }
1038         }
1039         matchedAttributes++;
1040     }
1041
1042     // font with color attribute, span with style attribute, etc...
1043     ASSERT(matchedAttributes <= element->attributeCount());
1044     return matchedAttributes >= element->attributeCount();
1045 }
1046
1047 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection)
1048 {
1049     if (!m_mutableStyle)
1050         return;
1051
1052     // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
1053     // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
1054     // which one of editingStyleAtPosition or computedStyle is called.
1055     RefPtr<EditingStyle> editingStyleAtPosition = EditingStyle::create(position, EditingPropertiesInEffect);
1056     StyleProperties* styleAtPosition = editingStyleAtPosition->m_mutableStyle.get();
1057
1058     RefPtr<CSSValue> unicodeBidi;
1059     RefPtr<CSSValue> direction;
1060     if (shouldPreserveWritingDirection == PreserveWritingDirection) {
1061         unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
1062         direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
1063     }
1064
1065     removeEquivalentProperties(*styleAtPosition);
1066
1067     if (textAlignResolvingStartAndEnd(*m_mutableStyle) == textAlignResolvingStartAndEnd(*styleAtPosition))
1068         m_mutableStyle->removeProperty(CSSPropertyTextAlign);
1069
1070     if (textColorFromStyle(*m_mutableStyle) == textColorFromStyle(*styleAtPosition))
1071         m_mutableStyle->removeProperty(CSSPropertyColor);
1072
1073     if (hasTransparentBackgroundColor(m_mutableStyle.get())
1074         || cssValueToRGBA(m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor).get()) == rgbaBackgroundColorInEffect(position.containerNode()))
1075         m_mutableStyle->removeProperty(CSSPropertyBackgroundColor);
1076
1077     if (is<CSSPrimitiveValue>(unicodeBidi.get())) {
1078         m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSValueID>(downcast<CSSPrimitiveValue>(*unicodeBidi).getValueID()));
1079         if (is<CSSPrimitiveValue>(direction.get()))
1080             m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSValueID>(downcast<CSSPrimitiveValue>(*direction).getValueID()));
1081     }
1082 }
1083
1084 void EditingStyle::mergeTypingStyle(Document& document)
1085 {
1086     RefPtr<EditingStyle> typingStyle = document.frame()->selection().typingStyle();
1087     if (!typingStyle || typingStyle == this)
1088         return;
1089
1090     mergeStyle(typingStyle->style(), OverrideValues);
1091 }
1092
1093 void EditingStyle::mergeInlineStyleOfElement(StyledElement* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude)
1094 {
1095     ASSERT(element);
1096     if (!element->inlineStyle())
1097         return;
1098
1099     switch (propertiesToInclude) {
1100     case AllProperties:
1101         mergeStyle(element->inlineStyle(), mode);
1102         return;
1103     case OnlyEditingInheritableProperties:
1104         mergeStyle(copyEditingProperties(element->inlineStyle(), OnlyInheritableEditingProperties).get(), mode);
1105         return;
1106     case EditingPropertiesInEffect:
1107         mergeStyle(copyEditingProperties(element->inlineStyle(), AllEditingProperties).get(), mode);
1108         return;
1109     }
1110 }
1111
1112 static inline bool elementMatchesAndPropertyIsNotInInlineStyleDecl(const HTMLElementEquivalent* equivalent, const StyledElement* element,
1113     EditingStyle::CSSPropertyOverrideMode mode, EditingStyle& style)
1114 {
1115     if (!equivalent->matches(*element))
1116         return false;
1117     if (mode != EditingStyle::OverrideValues && equivalent->propertyExistsInStyle(style))
1118         return false;
1119
1120     return !element->inlineStyle() || !equivalent->propertyExistsInStyle(EditingStyle::create(element->inlineStyle()).get());
1121 }
1122
1123 static PassRefPtr<MutableStyleProperties> extractEditingProperties(const StyleProperties* style, EditingStyle::PropertiesToInclude propertiesToInclude)
1124 {
1125     if (!style)
1126         return 0;
1127
1128     switch (propertiesToInclude) {
1129     case EditingStyle::AllProperties:
1130     case EditingStyle::EditingPropertiesInEffect:
1131         return copyEditingProperties(style, AllEditingProperties);
1132     case EditingStyle::OnlyEditingInheritableProperties:
1133         return copyEditingProperties(style, OnlyInheritableEditingProperties);
1134     }
1135
1136     ASSERT_NOT_REACHED();
1137     return 0;
1138 }
1139
1140 void EditingStyle::mergeInlineAndImplicitStyleOfElement(StyledElement* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude)
1141 {
1142     RefPtr<EditingStyle> styleFromRules = EditingStyle::create();
1143     styleFromRules->mergeStyleFromRulesForSerialization(element);
1144
1145     if (element->inlineStyle())
1146         styleFromRules->m_mutableStyle->mergeAndOverrideOnConflict(*element->inlineStyle());
1147
1148     styleFromRules->m_mutableStyle = extractEditingProperties(styleFromRules->m_mutableStyle.get(), propertiesToInclude);
1149     mergeStyle(styleFromRules->m_mutableStyle.get(), mode);
1150
1151     const Vector<std::unique_ptr<HTMLElementEquivalent>>& elementEquivalents = htmlElementEquivalents();
1152     for (size_t i = 0; i < elementEquivalents.size(); ++i) {
1153         if (elementMatchesAndPropertyIsNotInInlineStyleDecl(elementEquivalents[i].get(), element, mode, *this))
1154             elementEquivalents[i]->addToStyle(element, this);
1155     }
1156
1157     const Vector<std::unique_ptr<HTMLAttributeEquivalent>>& attributeEquivalents = htmlAttributeEquivalents();
1158     for (size_t i = 0; i < attributeEquivalents.size(); ++i) {
1159         if (attributeEquivalents[i]->attributeName() == HTMLNames::dirAttr)
1160             continue; // We don't want to include directionality
1161         if (elementMatchesAndPropertyIsNotInInlineStyleDecl(attributeEquivalents[i].get(), element, mode, *this))
1162             attributeEquivalents[i]->addToStyle(element, this);
1163     }
1164 }
1165
1166 PassRefPtr<EditingStyle> EditingStyle::wrappingStyleForSerialization(Node* context, bool shouldAnnotate)
1167 {
1168     RefPtr<EditingStyle> wrappingStyle;
1169     if (shouldAnnotate) {
1170         wrappingStyle = EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect);
1171
1172         // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote,
1173         // to help us differentiate those styles from ones that the user has applied.
1174         // This helps us get the color of content pasted into blockquotes right.
1175         wrappingStyle->removeStyleAddedByNode(enclosingNodeOfType(firstPositionInOrBeforeNode(context), isMailBlockquote, CanCrossEditingBoundary));
1176
1177         // Call collapseTextDecorationProperties first or otherwise it'll copy the value over from in-effect to text-decorations.
1178         wrappingStyle->collapseTextDecorationProperties();
1179         
1180         return wrappingStyle.release();
1181     }
1182
1183     wrappingStyle = EditingStyle::create();
1184
1185     // When not annotating for interchange, we only preserve inline style declarations.
1186     for (Node* node = context; node && !node->isDocumentNode(); node = node->parentNode()) {
1187         if (is<StyledElement>(*node) && !isMailBlockquote(node)) {
1188             wrappingStyle->mergeInlineAndImplicitStyleOfElement(downcast<StyledElement>(node), EditingStyle::DoNotOverrideValues,
1189                 EditingStyle::EditingPropertiesInEffect);
1190         }
1191     }
1192
1193     return wrappingStyle.release();
1194 }
1195
1196
1197 static void mergeTextDecorationValues(CSSValueList& mergedValue, const CSSValueList& valueToMerge)
1198 {
1199     Ref<CSSPrimitiveValue> underline = cssValuePool().createIdentifierValue(CSSValueUnderline);
1200     Ref<CSSPrimitiveValue> lineThrough = cssValuePool().createIdentifierValue(CSSValueLineThrough);
1201
1202     if (valueToMerge.hasValue(underline.ptr()) && !mergedValue.hasValue(underline.ptr()))
1203         mergedValue.append(WTF::move(underline));
1204
1205     if (valueToMerge.hasValue(lineThrough.ptr()) && !mergedValue.hasValue(lineThrough.ptr()))
1206         mergedValue.append(WTF::move(lineThrough));
1207 }
1208
1209 void EditingStyle::mergeStyle(const StyleProperties* style, CSSPropertyOverrideMode mode)
1210 {
1211     if (!style)
1212         return;
1213
1214     if (!m_mutableStyle) {
1215         m_mutableStyle = style->mutableCopy();
1216         return;
1217     }
1218
1219     unsigned propertyCount = style->propertyCount();
1220     for (unsigned i = 0; i < propertyCount; ++i) {
1221         StyleProperties::PropertyReference property = style->propertyAt(i);
1222         RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(property.id());
1223
1224         // text decorations never override values.
1225         if ((property.id() == CSSPropertyTextDecoration || property.id() == CSSPropertyWebkitTextDecorationsInEffect)
1226             && is<CSSValueList>(*property.value()) && value) {
1227             if (is<CSSValueList>(*value)) {
1228                 RefPtr<CSSValueList> newValue = downcast<CSSValueList>(*value).copy();
1229                 mergeTextDecorationValues(*newValue, downcast<CSSValueList>(*property.value()));
1230                 m_mutableStyle->setProperty(property.id(), newValue.release(), property.isImportant());
1231                 continue;
1232             }
1233             value = nullptr; // text-decoration: none is equivalent to not having the property.
1234         }
1235
1236         if (mode == OverrideValues || (mode == DoNotOverrideValues && !value))
1237             m_mutableStyle->setProperty(property.id(), property.value(), property.isImportant());
1238     }
1239
1240     int oldFontSizeDelta = m_fontSizeDelta;
1241     extractFontSizeDelta();
1242     m_fontSizeDelta += oldFontSizeDelta;
1243 }
1244
1245 static PassRefPtr<MutableStyleProperties> styleFromMatchedRulesForElement(Element* element, unsigned rulesToInclude)
1246 {
1247     RefPtr<MutableStyleProperties> style = MutableStyleProperties::create();
1248     auto matchedRules = element->document().ensureStyleResolver().styleRulesForElement(element, rulesToInclude);
1249     for (unsigned i = 0; i < matchedRules.size(); ++i) {
1250         if (matchedRules[i]->isStyleRule())
1251             style->mergeAndOverrideOnConflict(static_pointer_cast<StyleRule>(matchedRules[i])->properties());
1252     }
1253     
1254     return style.release();
1255 }
1256
1257 void EditingStyle::mergeStyleFromRules(StyledElement* element)
1258 {
1259     RefPtr<MutableStyleProperties> styleFromMatchedRules = styleFromMatchedRulesForElement(element,
1260         StyleResolver::AuthorCSSRules | StyleResolver::CrossOriginCSSRules);
1261     // Styles from the inline style declaration, held in the variable "style", take precedence 
1262     // over those from matched rules.
1263     if (m_mutableStyle)
1264         styleFromMatchedRules->mergeAndOverrideOnConflict(*m_mutableStyle);
1265
1266     clear();
1267     m_mutableStyle = styleFromMatchedRules;
1268 }
1269
1270 void EditingStyle::mergeStyleFromRulesForSerialization(StyledElement* element)
1271 {
1272     mergeStyleFromRules(element);
1273
1274     // The property value, if it's a percentage, may not reflect the actual computed value.  
1275     // For example: style="height: 1%; overflow: visible;" in quirksmode
1276     // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem
1277     RefPtr<MutableStyleProperties> fromComputedStyle = MutableStyleProperties::create();
1278     ComputedStyleExtractor computedStyle(element);
1279
1280     {
1281         unsigned propertyCount = m_mutableStyle->propertyCount();
1282         for (unsigned i = 0; i < propertyCount; ++i) {
1283             StyleProperties::PropertyReference property = m_mutableStyle->propertyAt(i);
1284             CSSValue* value = property.value();
1285             if (!is<CSSPrimitiveValue>(*value))
1286                 continue;
1287             if (downcast<CSSPrimitiveValue>(*value).isPercentage()) {
1288                 if (RefPtr<CSSValue> computedPropertyValue = computedStyle.propertyValue(property.id()))
1289                     fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue.release()));
1290             }
1291         }
1292     }
1293     m_mutableStyle->mergeAndOverrideOnConflict(*fromComputedStyle);
1294 }
1295
1296 static void removePropertiesInStyle(MutableStyleProperties* styleToRemovePropertiesFrom, MutableStyleProperties* style)
1297 {
1298     unsigned propertyCount = style->propertyCount();
1299     Vector<CSSPropertyID> propertiesToRemove(propertyCount);
1300     for (unsigned i = 0; i < propertyCount; ++i)
1301         propertiesToRemove[i] = style->propertyAt(i).id();
1302
1303     styleToRemovePropertiesFrom->removePropertiesInSet(propertiesToRemove.data(), propertiesToRemove.size());
1304 }
1305
1306 void EditingStyle::removeStyleFromRulesAndContext(StyledElement* element, Node* context)
1307 {
1308     ASSERT(element);
1309     if (!m_mutableStyle)
1310         return;
1311
1312     // 1. Remove style from matched rules because style remain without repeating it in inline style declaration
1313     RefPtr<MutableStyleProperties> styleFromMatchedRules = styleFromMatchedRulesForElement(element, StyleResolver::AllButEmptyCSSRules);
1314     if (styleFromMatchedRules && !styleFromMatchedRules->isEmpty())
1315         m_mutableStyle = getPropertiesNotIn(*m_mutableStyle, *styleFromMatchedRules);
1316
1317     // 2. Remove style present in context and not overriden by matched rules.
1318     RefPtr<EditingStyle> computedStyle = EditingStyle::create(context, EditingPropertiesInEffect);
1319     if (computedStyle->m_mutableStyle) {
1320         if (!computedStyle->m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor))
1321             computedStyle->m_mutableStyle->setProperty(CSSPropertyBackgroundColor, CSSValueTransparent);
1322
1323         removePropertiesInStyle(computedStyle->m_mutableStyle.get(), styleFromMatchedRules.get());
1324         m_mutableStyle = getPropertiesNotIn(*m_mutableStyle, *computedStyle->m_mutableStyle);
1325     }
1326
1327     // 3. If this element is a span and has display: inline or float: none, remove them unless they are overriden by rules.
1328     // These rules are added by serialization code to wrap text nodes.
1329     if (isStyleSpanOrSpanWithOnlyStyleAttribute(element)) {
1330         if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyDisplay) && identifierForStyleProperty(*m_mutableStyle, CSSPropertyDisplay) == CSSValueInline)
1331             m_mutableStyle->removeProperty(CSSPropertyDisplay);
1332         if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyFloat) && identifierForStyleProperty(*m_mutableStyle, CSSPropertyFloat) == CSSValueNone)
1333             m_mutableStyle->removeProperty(CSSPropertyFloat);
1334     }
1335 }
1336
1337 void EditingStyle::removePropertiesInElementDefaultStyle(Element* element)
1338 {
1339     if (!m_mutableStyle || m_mutableStyle->isEmpty())
1340         return;
1341
1342     RefPtr<MutableStyleProperties> defaultStyle = styleFromMatchedRulesForElement(element, StyleResolver::UAAndUserCSSRules);
1343
1344     removePropertiesInStyle(m_mutableStyle.get(), defaultStyle.get());
1345 }
1346
1347 template<typename T>
1348 void EditingStyle::removeEquivalentProperties(const T& style)
1349 {
1350     Vector<CSSPropertyID> propertiesToRemove;
1351     for (auto& property : m_mutableStyle->m_propertyVector) {
1352         if (style.propertyMatches(property.id(), property.value()))
1353             propertiesToRemove.append(property.id());
1354     }
1355     // FIXME: This should use mass removal.
1356     for (auto& property : propertiesToRemove)
1357         m_mutableStyle->removeProperty(property);
1358 }
1359
1360 void EditingStyle::forceInline()
1361 {
1362     if (!m_mutableStyle)
1363         m_mutableStyle = MutableStyleProperties::create();
1364     const bool propertyIsImportant = true;
1365     m_mutableStyle->setProperty(CSSPropertyDisplay, CSSValueInline, propertyIsImportant);
1366 }
1367
1368 bool EditingStyle::convertPositionStyle()
1369 {
1370     if (!m_mutableStyle)
1371         return false;
1372
1373     RefPtr<CSSPrimitiveValue> sticky = cssValuePool().createIdentifierValue(CSSValueWebkitSticky);
1374     if (m_mutableStyle->propertyMatches(CSSPropertyPosition, sticky.get())) {
1375         m_mutableStyle->setProperty(CSSPropertyPosition, cssValuePool().createIdentifierValue(CSSValueStatic), m_mutableStyle->propertyIsImportant(CSSPropertyPosition));
1376         return false;
1377     }
1378     RefPtr<CSSPrimitiveValue> fixed = cssValuePool().createIdentifierValue(CSSValueFixed);
1379     if (m_mutableStyle->propertyMatches(CSSPropertyPosition, fixed.get())) {
1380         m_mutableStyle->setProperty(CSSPropertyPosition, cssValuePool().createIdentifierValue(CSSValueAbsolute), m_mutableStyle->propertyIsImportant(CSSPropertyPosition));
1381         return true;
1382     }
1383     RefPtr<CSSPrimitiveValue> absolute = cssValuePool().createIdentifierValue(CSSValueAbsolute);
1384     if (m_mutableStyle->propertyMatches(CSSPropertyPosition, absolute.get()))
1385         return true;
1386     return false;
1387 }
1388
1389 bool EditingStyle::isFloating()
1390 {
1391     RefPtr<CSSValue> v = m_mutableStyle->getPropertyCSSValue(CSSPropertyFloat);
1392     RefPtr<CSSPrimitiveValue> noneValue = cssValuePool().createIdentifierValue(CSSValueNone);
1393     return v && !v->equals(*noneValue);
1394 }
1395
1396 int EditingStyle::legacyFontSize(Document* document) const
1397 {
1398     RefPtr<CSSValue> cssValue = m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize);
1399     if (!is<CSSPrimitiveValue>(cssValue.get()))
1400         return 0;
1401     return legacyFontSizeFromCSSValue(document, downcast<CSSPrimitiveValue>(cssValue.get()),
1402         m_shouldUseFixedDefaultFontSize, AlwaysUseLegacyFontSize);
1403 }
1404
1405 PassRefPtr<EditingStyle> EditingStyle::styleAtSelectionStart(const VisibleSelection& selection, bool shouldUseBackgroundColorInEffect)
1406 {
1407     if (selection.isNone())
1408         return 0;
1409
1410     Position position = adjustedSelectionStartForStyleComputation(selection);
1411
1412     // If the pos is at the end of a text node, then this node is not fully selected. 
1413     // Move it to the next deep equivalent position to avoid removing the style from this node. 
1414     // e.g. if pos was at Position("hello", 5) in <b>hello<div>world</div></b>, we want Position("world", 0) instead. 
1415     // We only do this for range because caret at Position("hello", 5) in <b>hello</b>world should give you font-weight: bold. 
1416     Node* positionNode = position.containerNode(); 
1417     if (selection.isRange() && positionNode && positionNode->isTextNode() && position.computeOffsetInContainerNode() == positionNode->maxCharacterOffset()) 
1418         position = nextVisuallyDistinctCandidate(position); 
1419
1420     Element* element = position.element();
1421     if (!element)
1422         return 0;
1423
1424     RefPtr<EditingStyle> style = EditingStyle::create(element, EditingStyle::AllProperties);
1425     style->mergeTypingStyle(element->document());
1426
1427     // If background color is transparent, traverse parent nodes until we hit a different value or document root
1428     // Also, if the selection is a range, ignore the background color at the start of selection,
1429     // and find the background color of the common ancestor.
1430     if (shouldUseBackgroundColorInEffect && (selection.isRange() || hasTransparentBackgroundColor(style->m_mutableStyle.get()))) {
1431         RefPtr<Range> range(selection.toNormalizedRange());
1432         if (PassRefPtr<CSSValue> value = backgroundColorInEffect(range->commonAncestorContainer(IGNORE_EXCEPTION)))
1433             style->setProperty(CSSPropertyBackgroundColor, value->cssText());
1434     }
1435
1436     return style;
1437 }
1438
1439 WritingDirection EditingStyle::textDirectionForSelection(const VisibleSelection& selection, EditingStyle* typingStyle, bool& hasNestedOrMultipleEmbeddings)
1440 {
1441     hasNestedOrMultipleEmbeddings = true;
1442
1443     if (selection.isNone())
1444         return NaturalWritingDirection;
1445
1446     Position position = selection.start().downstream();
1447
1448     Node* node = position.deprecatedNode();
1449     if (!node)
1450         return NaturalWritingDirection;
1451
1452     Position end;
1453     if (selection.isRange()) {
1454         end = selection.end().upstream();
1455
1456         Node* pastLast = Range::create(*end.document(), position.parentAnchoredEquivalent(), end.parentAnchoredEquivalent())->pastLastNode();
1457         for (Node* n = node; n && n != pastLast; n = NodeTraversal::next(*n)) {
1458             if (!n->isStyledElement())
1459                 continue;
1460
1461             RefPtr<CSSValue> unicodeBidi = ComputedStyleExtractor(n).propertyValue(CSSPropertyUnicodeBidi);
1462             if (!is<CSSPrimitiveValue>(unicodeBidi.get()))
1463                 continue;
1464
1465             CSSValueID unicodeBidiValue = downcast<CSSPrimitiveValue>(*unicodeBidi).getValueID();
1466             if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride)
1467                 return NaturalWritingDirection;
1468         }
1469     }
1470
1471     if (selection.isCaret()) {
1472         WritingDirection direction;
1473         if (typingStyle && typingStyle->textDirection(direction)) {
1474             hasNestedOrMultipleEmbeddings = false;
1475             return direction;
1476         }
1477         node = selection.visibleStart().deepEquivalent().deprecatedNode();
1478     }
1479
1480     // 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
1481     // to decide.
1482     Node* block = enclosingBlock(node);
1483     WritingDirection foundDirection = NaturalWritingDirection;
1484
1485     for (; node != block; node = node->parentNode()) {
1486         if (!node->isStyledElement())
1487             continue;
1488
1489         ComputedStyleExtractor computedStyle(node);
1490         RefPtr<CSSValue> unicodeBidi = computedStyle.propertyValue(CSSPropertyUnicodeBidi);
1491         if (!is<CSSPrimitiveValue>(unicodeBidi.get()))
1492             continue;
1493
1494         CSSValueID unicodeBidiValue = downcast<CSSPrimitiveValue>(*unicodeBidi).getValueID();
1495         if (unicodeBidiValue == CSSValueNormal)
1496             continue;
1497
1498         if (unicodeBidiValue == CSSValueBidiOverride)
1499             return NaturalWritingDirection;
1500
1501         ASSERT(unicodeBidiValue == CSSValueEmbed);
1502         RefPtr<CSSValue> direction = computedStyle.propertyValue(CSSPropertyDirection);
1503         if (!is<CSSPrimitiveValue>(direction.get()))
1504             continue;
1505
1506         CSSValueID directionValue = downcast<CSSPrimitiveValue>(*direction).getValueID();
1507         if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
1508             continue;
1509
1510         if (foundDirection != NaturalWritingDirection)
1511             return NaturalWritingDirection;
1512
1513         // In the range case, make sure that the embedding element persists until the end of the range.
1514         if (selection.isRange() && !end.deprecatedNode()->isDescendantOf(node))
1515             return NaturalWritingDirection;
1516         
1517         foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
1518     }
1519     hasNestedOrMultipleEmbeddings = false;
1520     return foundDirection;
1521 }
1522
1523 static void reconcileTextDecorationProperties(MutableStyleProperties* style)
1524 {    
1525     RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1526     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
1527     // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense.
1528     ASSERT(!textDecorationsInEffect || !textDecoration);
1529     if (textDecorationsInEffect) {
1530         style->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText());
1531         style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
1532         textDecoration = textDecorationsInEffect;
1533     }
1534
1535     // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none".
1536     if (textDecoration && !textDecoration->isValueList())
1537         style->removeProperty(CSSPropertyTextDecoration);
1538 }
1539
1540 StyleChange::StyleChange(EditingStyle* style, const Position& position)
1541     : m_applyBold(false)
1542     , m_applyItalic(false)
1543     , m_applyUnderline(false)
1544     , m_applyLineThrough(false)
1545     , m_applySubscript(false)
1546     , m_applySuperscript(false)
1547 {
1548     Document* document = position.deprecatedNode() ? &position.deprecatedNode()->document() : 0;
1549     if (!style || style->isEmpty() || !document || !document->frame())
1550         return;
1551
1552     Node* node = position.containerNode();
1553     if (!node)
1554         return;
1555
1556     ComputedStyleExtractor computedStyle(node);
1557
1558     // FIXME: take care of background-color in effect
1559     RefPtr<MutableStyleProperties> mutableStyle = style->style() ?
1560         getPropertiesNotIn(*style->style(), computedStyle) : MutableStyleProperties::create();
1561
1562     reconcileTextDecorationProperties(mutableStyle.get());
1563     bool shouldStyleWithCSS = document->frame()->editor().shouldStyleWithCSS();
1564     if (!shouldStyleWithCSS)
1565         extractTextStyles(document, *mutableStyle, computedStyle.useFixedFontDefaultSize());
1566
1567     bool shouldAddUnderline = style->underlineChange() == TextDecorationChange::Add;
1568     bool shouldAddStrikeThrough = style->strikeThroughChange() == TextDecorationChange::Add;
1569     if (shouldAddUnderline || shouldAddStrikeThrough) {
1570         RefPtr<CSSValue> value = computedStyle.propertyValue(CSSPropertyWebkitTextDecorationsInEffect);
1571         if (!is<CSSValueList>(value.get()))
1572             value = computedStyle.propertyValue(CSSPropertyTextDecoration);
1573
1574         RefPtr<CSSValueList> valueList;
1575         if (is<CSSValueList>(value.get()))
1576             valueList = downcast<CSSValueList>(value.get());
1577
1578         Ref<CSSValue> underline = cssValuePool().createIdentifierValue(CSSValueUnderline);
1579         bool hasUnderline = valueList && valueList->hasValue(underline.ptr());
1580
1581         Ref<CSSValue> lineThrough = cssValuePool().createIdentifierValue(CSSValueLineThrough);
1582         bool hasLineThrough = valueList && valueList->hasValue(lineThrough.ptr());
1583
1584         if (shouldStyleWithCSS) {
1585             valueList = valueList ? valueList->copy() : CSSValueList::createSpaceSeparated();
1586             if (shouldAddUnderline && !hasUnderline)
1587                 valueList->append(WTF::move(underline));
1588             if (shouldAddStrikeThrough && !hasLineThrough)
1589                 valueList->append(WTF::move(lineThrough));
1590             mutableStyle->setProperty(CSSPropertyTextDecoration, valueList.get());
1591         } else {
1592             m_applyUnderline = shouldAddUnderline && !hasUnderline;
1593             m_applyLineThrough = shouldAddStrikeThrough && !hasLineThrough;
1594         }
1595     }
1596
1597     // Changing the whitespace style in a tab span would collapse the tab into a space.
1598     if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode())))
1599         mutableStyle->removeProperty(CSSPropertyWhiteSpace);
1600
1601     // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle.
1602     // FIXME: Shouldn't this be done in getPropertiesNotIn?
1603     if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection))
1604         mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection));
1605
1606     if (!mutableStyle->isEmpty())
1607         m_cssStyle = mutableStyle;
1608 }
1609
1610 bool StyleChange::operator==(const StyleChange& other)
1611 {
1612     if (m_applyBold != other.m_applyBold
1613         || m_applyItalic != other.m_applyItalic
1614         || m_applyUnderline != other.m_applyUnderline
1615         || m_applyLineThrough != other.m_applyLineThrough
1616         || m_applySubscript != other.m_applySubscript
1617         || m_applySuperscript != other.m_applySuperscript
1618         || m_applyFontColor != other.m_applyFontColor
1619         || m_applyFontFace != other.m_applyFontFace
1620         || m_applyFontSize != other.m_applyFontSize)
1621         return false;
1622
1623     return (!m_cssStyle && !other.m_cssStyle)
1624         || (m_cssStyle && other.m_cssStyle && m_cssStyle->asText() == other.m_cssStyle->asText());
1625 }
1626
1627 static void setTextDecorationProperty(MutableStyleProperties& style, const CSSValueList* newTextDecoration, CSSPropertyID propertyID)
1628 {
1629     if (newTextDecoration->length())
1630         style.setProperty(propertyID, newTextDecoration->cssText(), style.propertyIsImportant(propertyID));
1631     else {
1632         // text-decoration: none is redundant since it does not remove any text decorations.
1633         style.removeProperty(propertyID);
1634     }
1635 }
1636
1637 void StyleChange::extractTextStyles(Document* document, MutableStyleProperties& style, bool shouldUseFixedFontDefaultSize)
1638 {
1639     if (identifierForStyleProperty(style, CSSPropertyFontWeight) == CSSValueBold) {
1640         style.removeProperty(CSSPropertyFontWeight);
1641         m_applyBold = true;
1642     }
1643
1644     int fontStyle = identifierForStyleProperty(style, CSSPropertyFontStyle);
1645     if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) {
1646         style.removeProperty(CSSPropertyFontStyle);
1647         m_applyItalic = true;
1648     }
1649
1650     // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
1651     // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
1652     RefPtr<CSSValue> textDecoration = style.getPropertyCSSValue(CSSPropertyTextDecoration);
1653     if (is<CSSValueList>(textDecoration.get())) {
1654         RefPtr<CSSPrimitiveValue> underline = cssValuePool().createIdentifierValue(CSSValueUnderline);
1655         RefPtr<CSSPrimitiveValue> lineThrough = cssValuePool().createIdentifierValue(CSSValueLineThrough);
1656
1657         RefPtr<CSSValueList> newTextDecoration = downcast<CSSValueList>(*textDecoration).copy();
1658         if (newTextDecoration->removeAll(underline.get()))
1659             m_applyUnderline = true;
1660         if (newTextDecoration->removeAll(lineThrough.get()))
1661             m_applyLineThrough = true;
1662
1663         // If trimTextDecorations, delete underline and line-through
1664         setTextDecorationProperty(style, newTextDecoration.get(), CSSPropertyTextDecoration);
1665     }
1666
1667     int verticalAlign = identifierForStyleProperty(style, CSSPropertyVerticalAlign);
1668     switch (verticalAlign) {
1669     case CSSValueSub:
1670         style.removeProperty(CSSPropertyVerticalAlign);
1671         m_applySubscript = true;
1672         break;
1673     case CSSValueSuper:
1674         style.removeProperty(CSSPropertyVerticalAlign);
1675         m_applySuperscript = true;
1676         break;
1677     }
1678
1679     if (style.getPropertyCSSValue(CSSPropertyColor)) {
1680         m_applyFontColor = Color(textColorFromStyle(style)).serialized();
1681         style.removeProperty(CSSPropertyColor);
1682     }
1683
1684     m_applyFontFace = style.getPropertyValue(CSSPropertyFontFamily);
1685     // Remove single quotes for Outlook 2007 compatibility. See https://bugs.webkit.org/show_bug.cgi?id=79448
1686     m_applyFontFace.replaceWithLiteral('\'', "");
1687     style.removeProperty(CSSPropertyFontFamily);
1688
1689     if (RefPtr<CSSValue> fontSize = style.getPropertyCSSValue(CSSPropertyFontSize)) {
1690         if (!is<CSSPrimitiveValue>(*fontSize))
1691             style.removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
1692         else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, downcast<CSSPrimitiveValue>(fontSize.get()),
1693                 shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) {
1694             m_applyFontSize = String::number(legacyFontSize);
1695             style.removeProperty(CSSPropertyFontSize);
1696         }
1697     }
1698 }
1699
1700 static void diffTextDecorations(MutableStyleProperties& style, CSSPropertyID propertID, CSSValue* refTextDecoration)
1701 {
1702     RefPtr<CSSValue> textDecoration = style.getPropertyCSSValue(propertID);
1703     if (!is<CSSValueList>(textDecoration.get()) || !is<CSSValueList>(refTextDecoration))
1704         return;
1705
1706     RefPtr<CSSValueList> newTextDecoration = downcast<CSSValueList>(*textDecoration).copy();
1707     CSSValueList& valuesInRefTextDecoration = downcast<CSSValueList>(*refTextDecoration);
1708
1709     for (size_t i = 0; i < valuesInRefTextDecoration.length(); ++i)
1710         newTextDecoration->removeAll(valuesInRefTextDecoration.item(i));
1711
1712     setTextDecorationProperty(style, newTextDecoration.get(), propertID);
1713 }
1714
1715 static bool fontWeightIsBold(CSSValue& fontWeight)
1716 {
1717     if (!is<CSSPrimitiveValue>(fontWeight))
1718         return false;
1719
1720     // Because b tag can only bold text, there are only two states in plain html: bold and not bold.
1721     // Collapse all other values to either one of these two states for editing purposes.
1722     switch (downcast<CSSPrimitiveValue>(fontWeight).getValueID()) {
1723         case CSSValue100:
1724         case CSSValue200:
1725         case CSSValue300:
1726         case CSSValue400:
1727         case CSSValue500:
1728         case CSSValueNormal:
1729             return false;
1730         case CSSValueBold:
1731         case CSSValue600:
1732         case CSSValue700:
1733         case CSSValue800:
1734         case CSSValue900:
1735             return true;
1736         default:
1737             break;
1738     }
1739
1740     ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
1741     return false;
1742 }
1743
1744 template<typename T>
1745 static bool fontWeightIsBold(T& style)
1746 {
1747     RefPtr<CSSValue> fontWeight = extractPropertyValue(style, CSSPropertyFontWeight);
1748     return fontWeight && fontWeightIsBold(*fontWeight);
1749 }
1750
1751 template<typename T>
1752 static PassRefPtr<MutableStyleProperties> extractPropertiesNotIn(StyleProperties& styleWithRedundantProperties, T& baseStyle)
1753 {
1754     RefPtr<EditingStyle> result = EditingStyle::create(&styleWithRedundantProperties);
1755     result->removeEquivalentProperties(baseStyle);
1756     RefPtr<MutableStyleProperties> mutableStyle = result->style();
1757
1758     RefPtr<CSSValue> baseTextDecorationsInEffect = extractPropertyValue(baseStyle, CSSPropertyWebkitTextDecorationsInEffect);
1759     diffTextDecorations(*mutableStyle, CSSPropertyTextDecoration, baseTextDecorationsInEffect.get());
1760     diffTextDecorations(*mutableStyle, CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
1761
1762     if (extractPropertyValue(baseStyle, CSSPropertyFontWeight) && fontWeightIsBold(*mutableStyle) == fontWeightIsBold(baseStyle))
1763         mutableStyle->removeProperty(CSSPropertyFontWeight);
1764
1765     if (extractPropertyValue(baseStyle, CSSPropertyColor) && textColorFromStyle(*mutableStyle) == textColorFromStyle(baseStyle))
1766         mutableStyle->removeProperty(CSSPropertyColor);
1767
1768     if (extractPropertyValue(baseStyle, CSSPropertyTextAlign)
1769         && textAlignResolvingStartAndEnd(*mutableStyle) == textAlignResolvingStartAndEnd(baseStyle))
1770         mutableStyle->removeProperty(CSSPropertyTextAlign);
1771
1772     if (extractPropertyValue(baseStyle, CSSPropertyBackgroundColor) && backgroundColorFromStyle(*mutableStyle) == backgroundColorFromStyle(baseStyle))
1773         mutableStyle->removeProperty(CSSPropertyBackgroundColor);
1774
1775     return mutableStyle.release();
1776 }
1777
1778 template<typename T>
1779 PassRefPtr<MutableStyleProperties> getPropertiesNotIn(StyleProperties& styleWithRedundantProperties, T& baseStyle)
1780 {
1781     return extractPropertiesNotIn(styleWithRedundantProperties, baseStyle);
1782 }
1783
1784 static bool isCSSValueLength(CSSPrimitiveValue* value)
1785 {
1786     return value->isFontIndependentLength();
1787 }
1788
1789 int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode mode)
1790 {
1791     ASSERT(document); // FIXME: This method should take a Document&
1792
1793     if (isCSSValueLength(value)) {
1794         int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX);
1795         int legacyFontSize = Style::legacyFontSizeForPixelSize(pixelFontSize, shouldUseFixedFontDefaultSize, *document);
1796         // Use legacy font size only if pixel value matches exactly to that of legacy font size.
1797         int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall;
1798         if (mode == AlwaysUseLegacyFontSize || Style::fontSizeForKeyword(cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize, *document) == pixelFontSize)
1799             return legacyFontSize;
1800
1801         return 0;
1802     }
1803
1804     if (CSSValueXSmall <= value->getValueID() && value->getValueID() <= CSSValueWebkitXxxLarge)
1805         return value->getValueID() - CSSValueXSmall + 1;
1806
1807     return 0;
1808 }
1809
1810 bool isTransparentColorValue(CSSValue* cssValue)
1811 {
1812     if (!cssValue)
1813         return true;
1814     if (!is<CSSPrimitiveValue>(*cssValue))
1815         return false;
1816     CSSPrimitiveValue& value = downcast<CSSPrimitiveValue>(*cssValue);
1817     if (value.isRGBColor())
1818         return !alphaChannel(value.getRGBA32Value());
1819     return value.getValueID() == CSSValueTransparent;
1820 }
1821
1822 bool hasTransparentBackgroundColor(StyleProperties* style)
1823 {
1824     RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(CSSPropertyBackgroundColor);
1825     return isTransparentColorValue(cssValue.get());
1826 }
1827
1828 PassRefPtr<CSSValue> backgroundColorInEffect(Node* node)
1829 {
1830     for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
1831         if (RefPtr<CSSValue> value = ComputedStyleExtractor(ancestor).propertyValue(CSSPropertyBackgroundColor)) {
1832             if (!isTransparentColorValue(value.get()))
1833                 return value.release();
1834         }
1835     }
1836     return 0;
1837 }
1838
1839 }