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