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