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