~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/editing/EditingStyle.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

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