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