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