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