1/*
2 * Copyright (C) 2005, 2006, 2008, 2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ApplyStyleCommand.h"
28
29#include "CSSComputedStyleDeclaration.h"
30#include "CSSMutableStyleDeclaration.h"
31#include "CSSParser.h"
32#include "CSSProperty.h"
33#include "CSSPropertyNames.h"
34#include "CSSValueKeywords.h"
35#include "Document.h"
36#include "Editor.h"
37#include "Frame.h"
38#include "HTMLElement.h"
39#include "HTMLInterchange.h"
40#include "HTMLNames.h"
41#include "NodeList.h"
42#include "Range.h"
43#include "RenderObject.h"
44#include "Text.h"
45#include "TextIterator.h"
46#include "htmlediting.h"
47#include "visible_units.h"
48#include <wtf/StdLibExtras.h>
49
50namespace WebCore {
51
52using namespace HTMLNames;
53
54class StyleChange {
55public:
56    explicit StyleChange(CSSStyleDeclaration*, const Position&);
57
58    String cssStyle() const { return m_cssStyle; }
59    bool applyBold() const { return m_applyBold; }
60    bool applyItalic() const { return m_applyItalic; }
61    bool applyUnderline() const { return m_applyUnderline; }
62    bool applyLineThrough() const { return m_applyLineThrough; }
63    bool applySubscript() const { return m_applySubscript; }
64    bool applySuperscript() const { return m_applySuperscript; }
65    bool applyFontColor() const { return m_applyFontColor.length() > 0; }
66    bool applyFontFace() const { return m_applyFontFace.length() > 0; }
67    bool applyFontSize() const { return m_applyFontSize.length() > 0; }
68
69    String fontColor() { return m_applyFontColor; }
70    String fontFace() { return m_applyFontFace; }
71    String fontSize() { return m_applyFontSize; }
72
73private:
74    void init(PassRefPtr<CSSStyleDeclaration>, const Position&);
75    void reconcileTextDecorationProperties(CSSMutableStyleDeclaration*);
76    void extractTextStyles(CSSMutableStyleDeclaration*);
77
78    String m_cssStyle;
79    bool m_applyBold;
80    bool m_applyItalic;
81    bool m_applyUnderline;
82    bool m_applyLineThrough;
83    bool m_applySubscript;
84    bool m_applySuperscript;
85    String m_applyFontColor;
86    String m_applyFontFace;
87    String m_applyFontSize;
88};
89
90
91StyleChange::StyleChange(CSSStyleDeclaration* style, const Position& position)
92    : m_applyBold(false)
93    , m_applyItalic(false)
94    , m_applyUnderline(false)
95    , m_applyLineThrough(false)
96    , m_applySubscript(false)
97    , m_applySuperscript(false)
98{
99    init(style, position);
100}
101
102void StyleChange::init(PassRefPtr<CSSStyleDeclaration> style, const Position& position)
103{
104    Document* document = position.node() ? position.node()->document() : 0;
105    if (!document || !document->frame())
106        return;
107
108    RefPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle();
109    RefPtr<CSSMutableStyleDeclaration> mutableStyle = getPropertiesNotInComputedStyle(style.get(), computedStyle.get());
110
111    reconcileTextDecorationProperties(mutableStyle.get());
112    if (!document->frame()->editor()->shouldStyleWithCSS())
113        extractTextStyles(mutableStyle.get());
114
115    // Changing the whitespace style in a tab span would collapse the tab into a space.
116    if (isTabSpanTextNode(position.node()) || isTabSpanNode((position.node())))
117        mutableStyle->removeProperty(CSSPropertyWhiteSpace);
118
119    // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle.
120    // FIXME: Shouldn't this be done in getPropertiesNotInComputedStyle?
121    if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->getPropertyCSSValue(CSSPropertyDirection))
122        mutableStyle->setProperty(CSSPropertyDirection, style->getPropertyValue(CSSPropertyDirection));
123
124    // Save the result for later
125    m_cssStyle = mutableStyle->cssText().stripWhiteSpace();
126}
127
128void StyleChange::reconcileTextDecorationProperties(CSSMutableStyleDeclaration* style)
129{
130    RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
131    RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
132    // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense.
133    ASSERT(!textDecorationsInEffect || !textDecoration);
134    if (textDecorationsInEffect) {
135        style->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText());
136        style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
137        textDecoration = textDecorationsInEffect;
138    }
139
140    // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none".
141    if (textDecoration && !textDecoration->isValueList())
142        style->removeProperty(CSSPropertyTextDecoration);
143}
144
145static int getIdentifierValue(CSSMutableStyleDeclaration* style, int propertyID)
146{
147    if (!style)
148        return 0;
149
150    RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
151    if (!value || !value->isPrimitiveValue())
152        return 0;
153
154    return static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
155}
156
157static void setTextDecorationProperty(CSSMutableStyleDeclaration* style, const CSSValueList* newTextDecoration, int propertyID)
158{
159    if (newTextDecoration->length())
160        style->setProperty(propertyID, newTextDecoration->cssText(), style->getPropertyPriority(propertyID));
161    else {
162        // text-decoration: none is redundant since it does not remove any text decorations.
163        ASSERT(!style->getPropertyPriority(propertyID));
164        style->removeProperty(propertyID);
165    }
166}
167
168void StyleChange::extractTextStyles(CSSMutableStyleDeclaration* style)
169{
170    ASSERT(style);
171
172    if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) {
173        style->removeProperty(CSSPropertyFontWeight);
174        m_applyBold = true;
175    }
176
177    int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle);
178    if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) {
179        style->removeProperty(CSSPropertyFontStyle);
180        m_applyItalic = true;
181    }
182
183    // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
184    // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
185    if (RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration)) {
186        ASSERT(textDecoration->isValueList());
187        DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
188        DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
189
190        RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
191        if (newTextDecoration->removeAll(underline.get()))
192            m_applyUnderline = true;
193        if (newTextDecoration->removeAll(lineThrough.get()))
194            m_applyLineThrough = true;
195
196        // If trimTextDecorations, delete underline and line-through
197        setTextDecorationProperty(style, newTextDecoration.get(), CSSPropertyTextDecoration);
198    }
199
200    int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign);
201    switch (verticalAlign) {
202    case CSSValueSub:
203        style->removeProperty(CSSPropertyVerticalAlign);
204        m_applySubscript = true;
205        break;
206    case CSSValueSuper:
207        style->removeProperty(CSSPropertyVerticalAlign);
208        m_applySuperscript = true;
209        break;
210    }
211
212    if (RefPtr<CSSValue> colorValue = style->getPropertyCSSValue(CSSPropertyColor)) {
213        ASSERT(colorValue->isPrimitiveValue());
214        CSSPrimitiveValue* primitiveColor = static_cast<CSSPrimitiveValue*>(colorValue.get());
215        RGBA32 rgba;
216        if (primitiveColor->primitiveType() != CSSPrimitiveValue::CSS_RGBCOLOR) {
217            CSSParser::parseColor(rgba, colorValue->cssText());
218            // Need to take care of named color such as green and black
219            // This code should be removed after https://bugs.webkit.org/show_bug.cgi?id=28282 is fixed.
220        } else
221            rgba = primitiveColor->getRGBA32Value();
222        m_applyFontColor = Color(rgba).name();
223        style->removeProperty(CSSPropertyColor);
224    }
225
226    m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily);
227    style->removeProperty(CSSPropertyFontFamily);
228
229    if (RefPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) {
230        if (!fontSize->isPrimitiveValue())
231            style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
232        else {
233            CSSPrimitiveValue* value = static_cast<CSSPrimitiveValue*>(fontSize.get());
234
235            // Only accept absolute scale
236            if (value->primitiveType() >= CSSPrimitiveValue::CSS_PX && value->primitiveType() <= CSSPrimitiveValue::CSS_PC) {
237                float number = value->getFloatValue(CSSPrimitiveValue::CSS_PX);
238                if (number <= 9)
239                    m_applyFontSize = "1";
240                else if (number <= 10)
241                    m_applyFontSize = "2";
242                else if (number <= 13)
243                    m_applyFontSize = "3";
244                else if (number <= 16)
245                    m_applyFontSize = "4";
246                else if (number <= 18)
247                    m_applyFontSize = "5";
248                else if (number <= 24)
249                    m_applyFontSize = "6";
250                else
251                    m_applyFontSize = "7";
252            }
253            // Huge quirk in Microsoft Entourage is that they understand CSS font-size, but also write
254            // out legacy 1-7 values in font tags (I guess for mailers that are not CSS-savvy at all,
255            // like Eudora). Yes, they write out *both*. We need to write out both as well.
256        }
257    }
258}
259
260static String& styleSpanClassString()
261{
262    DEFINE_STATIC_LOCAL(String, styleSpanClassString, ((AppleStyleSpanClass)));
263    return styleSpanClassString;
264}
265
266bool isStyleSpan(const Node *node)
267{
268    if (!node || !node->isHTMLElement())
269        return false;
270
271    const HTMLElement* elem = static_cast<const HTMLElement*>(node);
272    return elem->hasLocalName(spanAttr) && elem->getAttribute(classAttr) == styleSpanClassString();
273}
274
275static bool isUnstyledStyleSpan(const Node* node)
276{
277    if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
278        return false;
279
280    const HTMLElement* elem = static_cast<const HTMLElement*>(node);
281    CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl();
282    return (!inlineStyleDecl || inlineStyleDecl->isEmpty()) && elem->getAttribute(classAttr) == styleSpanClassString();
283}
284
285static bool isSpanWithoutAttributesOrUnstyleStyleSpan(const Node* node)
286{
287    if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
288        return false;
289
290    const HTMLElement* elem = static_cast<const HTMLElement*>(node);
291    NamedNodeMap* attributes = elem->attributes(true); // readonly
292    if (attributes->isEmpty())
293        return true;
294
295    return isUnstyledStyleSpan(node);
296}
297
298static bool isEmptyFontTag(const Node *node)
299{
300    if (!node || !node->hasTagName(fontTag))
301        return false;
302
303    const Element *elem = static_cast<const Element *>(node);
304    NamedNodeMap *map = elem->attributes(true); // true for read-only
305    if (!map)
306        return true;
307    return map->isEmpty() || (map->length() == 1 && elem->getAttribute(classAttr) == styleSpanClassString());
308}
309
310static PassRefPtr<Element> createFontElement(Document* document)
311{
312    RefPtr<Element> fontNode = createHTMLElement(document, fontTag);
313    fontNode->setAttribute(classAttr, styleSpanClassString());
314    return fontNode.release();
315}
316
317PassRefPtr<HTMLElement> createStyleSpanElement(Document* document)
318{
319    RefPtr<HTMLElement> styleElement = createHTMLElement(document, spanTag);
320    styleElement->setAttribute(classAttr, styleSpanClassString());
321    return styleElement.release();
322}
323
324static void diffTextDecorations(CSSMutableStyleDeclaration* style, int propertID, CSSValue* refTextDecoration)
325{
326    RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID);
327    if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList())
328        return;
329
330    RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
331    CSSValueList* valuesInRefTextDecoration = static_cast<CSSValueList*>(refTextDecoration);
332
333    for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++)
334        newTextDecoration->removeAll(valuesInRefTextDecoration->item(i));
335
336    setTextDecorationProperty(style, newTextDecoration.get(), propertID);
337}
338
339static bool fontWeightIsBold(CSSStyleDeclaration* style)
340{
341    ASSERT(style);
342    RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight);
343
344    if (!fontWeight)
345        return false;
346    if (!fontWeight->isPrimitiveValue())
347        return false;
348
349    // Because b tag can only bold text, there are only two states in plain html: bold and not bold.
350    // Collapse all other values to either one of these two states for editing purposes.
351    switch (static_cast<CSSPrimitiveValue*>(fontWeight.get())->getIdent()) {
352        case CSSValue100:
353        case CSSValue200:
354        case CSSValue300:
355        case CSSValue400:
356        case CSSValue500:
357        case CSSValueNormal:
358            return false;
359        case CSSValueBold:
360        case CSSValue600:
361        case CSSValue700:
362        case CSSValue800:
363        case CSSValue900:
364            return true;
365    }
366
367    ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
368    return false; // Make compiler happy
369}
370
371RefPtr<CSSMutableStyleDeclaration> getPropertiesNotInComputedStyle(CSSStyleDeclaration* style, CSSComputedStyleDeclaration* computedStyle)
372{
373    ASSERT(style);
374    ASSERT(computedStyle);
375    RefPtr<CSSMutableStyleDeclaration> result = style->copy();
376    computedStyle->diff(result.get());
377
378    RefPtr<CSSValue> computedTextDecorationsInEffect = computedStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
379    diffTextDecorations(result.get(), CSSPropertyTextDecoration, computedTextDecorationsInEffect.get());
380    diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, computedTextDecorationsInEffect.get());
381
382    if (fontWeightIsBold(result.get()) == fontWeightIsBold(computedStyle))
383        result->removeProperty(CSSPropertyFontWeight);
384
385    return result;
386}
387
388// Editing style properties must be preserved during editing operation.
389// e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph.
390// FIXME: The current editingStyleProperties contains all inheritableProperties but we may not need to preserve all inheritable properties
391static const int editingStyleProperties[] = {
392    // CSS inheritable properties
393    CSSPropertyBorderCollapse,
394    CSSPropertyColor,
395    CSSPropertyFontFamily,
396    CSSPropertyFontSize,
397    CSSPropertyFontStyle,
398    CSSPropertyFontVariant,
399    CSSPropertyFontWeight,
400    CSSPropertyLetterSpacing,
401    CSSPropertyLineHeight,
402    CSSPropertyOrphans,
403    CSSPropertyTextAlign,
404    CSSPropertyTextIndent,
405    CSSPropertyTextTransform,
406    CSSPropertyWhiteSpace,
407    CSSPropertyWidows,
408    CSSPropertyWordSpacing,
409    CSSPropertyWebkitBorderHorizontalSpacing,
410    CSSPropertyWebkitBorderVerticalSpacing,
411    CSSPropertyWebkitTextDecorationsInEffect,
412    CSSPropertyWebkitTextFillColor,
413    CSSPropertyWebkitTextSizeAdjust,
414    CSSPropertyWebkitTextStrokeColor,
415    CSSPropertyWebkitTextStrokeWidth,
416};
417size_t numEditingStyleProperties = sizeof(editingStyleProperties)/sizeof(editingStyleProperties[0]);
418
419PassRefPtr<CSSMutableStyleDeclaration> editingStyleAtPosition(Position pos, ShouldIncludeTypingStyle shouldIncludeTypingStyle)
420{
421    RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = pos.computedStyle();
422    RefPtr<CSSMutableStyleDeclaration> style;
423    if (!computedStyleAtPosition)
424        style = CSSMutableStyleDeclaration::create();
425    else
426        style = computedStyleAtPosition->copyPropertiesInSet(editingStyleProperties, numEditingStyleProperties);
427
428    if (style && pos.node() && pos.node()->computedStyle()) {
429        RenderStyle* renderStyle = pos.node()->computedStyle();
430        // If a node's text fill color is invalid, then its children use
431        // their font-color as their text fill color (they don't
432        // inherit it).  Likewise for stroke color.
433        ExceptionCode ec = 0;
434        if (!renderStyle->textFillColor().isValid())
435            style->removeProperty(CSSPropertyWebkitTextFillColor, ec);
436        if (!renderStyle->textStrokeColor().isValid())
437            style->removeProperty(CSSPropertyWebkitTextStrokeColor, ec);
438        ASSERT(ec == 0);
439        if (renderStyle->fontDescription().keywordSize())
440            style->setProperty(CSSPropertyFontSize, computedStyleAtPosition->getFontSizeCSSValuePreferringKeyword()->cssText());
441    }
442
443    if (shouldIncludeTypingStyle == IncludeTypingStyle) {
444        CSSMutableStyleDeclaration* typingStyle = pos.node()->document()->frame()->typingStyle();
445        if (typingStyle)
446            style->merge(typingStyle);
447    }
448
449    return style.release();
450}
451
452void prepareEditingStyleToApplyAt(CSSMutableStyleDeclaration* editingStyle, Position pos)
453{
454    // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
455    // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
456    // which one of editingStyleAtPosition or computedStyle is called.
457    RefPtr<CSSMutableStyleDeclaration> style = editingStyleAtPosition(pos);
458    style->diff(editingStyle);
459
460    // if alpha value is zero, we don't add the background color.
461    RefPtr<CSSValue> backgroundColor = editingStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
462    if (backgroundColor && backgroundColor->isPrimitiveValue()) {
463        CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(backgroundColor.get());
464        Color color = Color(primitiveValue->getRGBA32Value());
465        ExceptionCode ec;
466        if (color.alpha() == 0)
467            editingStyle->removeProperty(CSSPropertyBackgroundColor, ec);
468    }
469}
470
471void removeStylesAddedByNode(CSSMutableStyleDeclaration* editingStyle, Node* node)
472{
473    ASSERT(node);
474    ASSERT(node->parentNode());
475    RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleAtPosition(Position(node->parentNode(), 0));
476    RefPtr<CSSMutableStyleDeclaration> style = editingStyleAtPosition(Position(node, 0));
477    parentStyle->diff(style.get());
478    style->diff(editingStyle);
479}
480
481ApplyStyleCommand::ApplyStyleCommand(Document* document, CSSStyleDeclaration* style, EditAction editingAction, EPropertyLevel propertyLevel)
482    : CompositeEditCommand(document)
483    , m_style(style->makeMutable())
484    , m_editingAction(editingAction)
485    , m_propertyLevel(propertyLevel)
486    , m_start(endingSelection().start().downstream())
487    , m_end(endingSelection().end().upstream())
488    , m_useEndingSelection(true)
489    , m_styledInlineElement(0)
490    , m_removeOnly(false)
491{
492}
493
494ApplyStyleCommand::ApplyStyleCommand(Document* document, CSSStyleDeclaration* style, const Position& start, const Position& end, EditAction editingAction, EPropertyLevel propertyLevel)
495    : CompositeEditCommand(document)
496    , m_style(style->makeMutable())
497    , m_editingAction(editingAction)
498    , m_propertyLevel(propertyLevel)
499    , m_start(start)
500    , m_end(end)
501    , m_useEndingSelection(false)
502    , m_styledInlineElement(0)
503    , m_removeOnly(false)
504{
505}
506
507ApplyStyleCommand::ApplyStyleCommand(PassRefPtr<Element> element, bool removeOnly, EditAction editingAction)
508    : CompositeEditCommand(element->document())
509    , m_style(CSSMutableStyleDeclaration::create())
510    , m_editingAction(editingAction)
511    , m_propertyLevel(PropertyDefault)
512    , m_start(endingSelection().start().downstream())
513    , m_end(endingSelection().end().upstream())
514    , m_useEndingSelection(true)
515    , m_styledInlineElement(element)
516    , m_removeOnly(removeOnly)
517{
518}
519
520void ApplyStyleCommand::updateStartEnd(const Position& newStart, const Position& newEnd)
521{
522    ASSERT(comparePositions(newEnd, newStart) >= 0);
523
524    if (!m_useEndingSelection && (newStart != m_start || newEnd != m_end))
525        m_useEndingSelection = true;
526
527    setEndingSelection(VisibleSelection(newStart, newEnd, VP_DEFAULT_AFFINITY));
528    m_start = newStart;
529    m_end = newEnd;
530}
531
532Position ApplyStyleCommand::startPosition()
533{
534    if (m_useEndingSelection)
535        return endingSelection().start();
536
537    return m_start;
538}
539
540Position ApplyStyleCommand::endPosition()
541{
542    if (m_useEndingSelection)
543        return endingSelection().end();
544
545    return m_end;
546}
547
548void ApplyStyleCommand::doApply()
549{
550    switch (m_propertyLevel) {
551        case PropertyDefault: {
552            // apply the block-centric properties of the style
553            RefPtr<CSSMutableStyleDeclaration> blockStyle = m_style->copyBlockProperties();
554            if (blockStyle->length())
555                applyBlockStyle(blockStyle.get());
556            // apply any remaining styles to the inline elements
557            // NOTE: hopefully, this string comparison is the same as checking for a non-null diff
558            if (blockStyle->length() < m_style->length() || m_styledInlineElement) {
559                RefPtr<CSSMutableStyleDeclaration> inlineStyle = m_style->copy();
560                applyRelativeFontStyleChange(inlineStyle.get());
561                blockStyle->diff(inlineStyle.get());
562                applyInlineStyle(inlineStyle.get());
563            }
564            break;
565        }
566        case ForceBlockProperties:
567            // Force all properties to be applied as block styles.
568            applyBlockStyle(m_style.get());
569            break;
570    }
571}
572
573EditAction ApplyStyleCommand::editingAction() const
574{
575    return m_editingAction;
576}
577
578void ApplyStyleCommand::applyBlockStyle(CSSMutableStyleDeclaration *style)
579{
580    // update document layout once before removing styles
581    // so that we avoid the expense of updating before each and every call
582    // to check a computed style
583    updateLayout();
584
585    // get positions we want to use for applying style
586    Position start = startPosition();
587    Position end = endPosition();
588    if (comparePositions(end, start) < 0) {
589        Position swap = start;
590        start = end;
591        end = swap;
592    }
593
594    VisiblePosition visibleStart(start);
595    VisiblePosition visibleEnd(end);
596    // Save and restore the selection endpoints using their indices in the document, since
597    // addBlockStyleIfNeeded may moveParagraphs, which can remove these endpoints.
598    // Calculate start and end indices from the start of the tree that they're in.
599    Node* scope = highestAncestor(visibleStart.deepEquivalent().node());
600    Position rangeStart(scope, 0);
601    RefPtr<Range> startRange = Range::create(document(), rangeStart, rangeCompliantEquivalent(visibleStart.deepEquivalent()));
602    RefPtr<Range> endRange = Range::create(document(), rangeStart, rangeCompliantEquivalent(visibleEnd.deepEquivalent()));
603    int startIndex = TextIterator::rangeLength(startRange.get(), true);
604    int endIndex = TextIterator::rangeLength(endRange.get(), true);
605
606    VisiblePosition paragraphStart(startOfParagraph(visibleStart));
607    VisiblePosition nextParagraphStart(endOfParagraph(paragraphStart).next());
608    VisiblePosition beyondEnd(endOfParagraph(visibleEnd).next());
609    while (paragraphStart.isNotNull() && paragraphStart != beyondEnd) {
610        StyleChange styleChange(style, paragraphStart.deepEquivalent());
611        if (styleChange.cssStyle().length() || m_removeOnly) {
612            RefPtr<Node> block = enclosingBlock(paragraphStart.deepEquivalent().node());
613            RefPtr<Node> newBlock = moveParagraphContentsToNewBlockIfNecessary(paragraphStart.deepEquivalent());
614            if (newBlock)
615                block = newBlock;
616            ASSERT(block->isHTMLElement());
617            if (block->isHTMLElement()) {
618                removeCSSStyle(style, static_cast<HTMLElement*>(block.get()));
619                if (!m_removeOnly)
620                    addBlockStyle(styleChange, static_cast<HTMLElement*>(block.get()));
621            }
622        }
623        paragraphStart = nextParagraphStart;
624        nextParagraphStart = endOfParagraph(paragraphStart).next();
625    }
626
627    startRange = TextIterator::rangeFromLocationAndLength(static_cast<Element*>(scope), startIndex, 0, true);
628    endRange = TextIterator::rangeFromLocationAndLength(static_cast<Element*>(scope), endIndex, 0, true);
629    if (startRange && endRange)
630        updateStartEnd(startRange->startPosition(), endRange->startPosition());
631}
632
633#define NoFontDelta (0.0f)
634#define MinimumFontSize (0.1f)
635
636void ApplyStyleCommand::applyRelativeFontStyleChange(CSSMutableStyleDeclaration *style)
637{
638    RefPtr<CSSValue> value = style->getPropertyCSSValue(CSSPropertyFontSize);
639    if (value) {
640        // Explicit font size overrides any delta.
641        style->removeProperty(CSSPropertyWebkitFontSizeDelta);
642        return;
643    }
644
645    // Get the adjustment amount out of the style.
646    value = style->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
647    if (!value)
648        return;
649    float adjustment = NoFontDelta;
650    if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
651        CSSPrimitiveValue *primitiveValue = static_cast<CSSPrimitiveValue *>(value.get());
652        if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_PX) {
653            // Only PX handled now. If we handle more types in the future, perhaps
654            // a switch statement here would be more appropriate.
655            adjustment = primitiveValue->getFloatValue();
656        }
657    }
658    style->removeProperty(CSSPropertyWebkitFontSizeDelta);
659    if (adjustment == NoFontDelta)
660        return;
661
662    Position start = startPosition();
663    Position end = endPosition();
664    if (comparePositions(end, start) < 0) {
665        Position swap = start;
666        start = end;
667        end = swap;
668    }
669
670    // Join up any adjacent text nodes.
671    if (start.node()->isTextNode()) {
672        joinChildTextNodes(start.node()->parentNode(), start, end);
673        start = startPosition();
674        end = endPosition();
675    }
676    if (end.node()->isTextNode() && start.node()->parentNode() != end.node()->parentNode()) {
677        joinChildTextNodes(end.node()->parentNode(), start, end);
678        start = startPosition();
679        end = endPosition();
680    }
681
682    // Split the start text nodes if needed to apply style.
683    bool splitStart = splitTextAtStartIfNeeded(start, end);
684    if (splitStart) {
685        start = startPosition();
686        end = endPosition();
687    }
688    bool splitEnd = splitTextAtEndIfNeeded(start, end);
689    if (splitEnd) {
690        start = startPosition();
691        end = endPosition();
692    }
693
694    // Calculate loop end point.
695    // If the end node is before the start node (can only happen if the end node is
696    // an ancestor of the start node), we gather nodes up to the next sibling of the end node
697    Node *beyondEnd;
698    if (start.node()->isDescendantOf(end.node()))
699        beyondEnd = end.node()->traverseNextSibling();
700    else
701        beyondEnd = end.node()->traverseNextNode();
702
703    start = start.upstream(); // Move upstream to ensure we do not add redundant spans.
704    Node *startNode = start.node();
705    if (startNode->isTextNode() && start.deprecatedEditingOffset() >= caretMaxOffset(startNode)) // Move out of text node if range does not include its characters.
706        startNode = startNode->traverseNextNode();
707
708    // Store away font size before making any changes to the document.
709    // This ensures that changes to one node won't effect another.
710    HashMap<Node*, float> startingFontSizes;
711    for (Node *node = startNode; node != beyondEnd; node = node->traverseNextNode())
712        startingFontSizes.set(node, computedFontSize(node));
713
714    // These spans were added by us. If empty after font size changes, they can be removed.
715    Vector<RefPtr<HTMLElement> > unstyledSpans;
716
717    Node* lastStyledNode = 0;
718    for (Node* node = startNode; node != beyondEnd; node = node->traverseNextNode()) {
719        RefPtr<HTMLElement> element;
720        if (node->isHTMLElement()) {
721            // Only work on fully selected nodes.
722            if (!nodeFullySelected(node, start, end))
723                continue;
724            element = static_cast<HTMLElement*>(node);
725        } else if (node->isTextNode() && node->renderer() && node->parentNode() != lastStyledNode) {
726            // Last styled node was not parent node of this text node, but we wish to style this
727            // text node. To make this possible, add a style span to surround this text node.
728            RefPtr<HTMLElement> span = createStyleSpanElement(document());
729            surroundNodeRangeWithElement(node, node, span.get());
730            element = span.release();
731        }  else {
732            // Only handle HTML elements and text nodes.
733            continue;
734        }
735        lastStyledNode = node;
736
737        CSSMutableStyleDeclaration* inlineStyleDecl = element->getInlineStyleDecl();
738        float currentFontSize = computedFontSize(node);
739        float desiredFontSize = max(MinimumFontSize, startingFontSizes.get(node) + adjustment);
740        RefPtr<CSSValue> value = inlineStyleDecl->getPropertyCSSValue(CSSPropertyFontSize);
741        if (value) {
742            inlineStyleDecl->removeProperty(CSSPropertyFontSize, true);
743            currentFontSize = computedFontSize(node);
744        }
745        if (currentFontSize != desiredFontSize) {
746            inlineStyleDecl->setProperty(CSSPropertyFontSize, String::number(desiredFontSize) + "px", false, false);
747            setNodeAttribute(element.get(), styleAttr, inlineStyleDecl->cssText());
748        }
749        if (inlineStyleDecl->isEmpty()) {
750            removeNodeAttribute(element.get(), styleAttr);
751            // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan?  Need a test.
752            if (isUnstyledStyleSpan(element.get()))
753                unstyledSpans.append(element.release());
754        }
755    }
756
757    size_t size = unstyledSpans.size();
758    for (size_t i = 0; i < size; ++i)
759        removeNodePreservingChildren(unstyledSpans[i].get());
760}
761
762#undef NoFontDelta
763#undef MinimumFontSize
764
765static Node* dummySpanAncestorForNode(const Node* node)
766{
767    while (node && !isStyleSpan(node))
768        node = node->parent();
769
770    return node ? node->parent() : 0;
771}
772
773void ApplyStyleCommand::cleanupUnstyledAppleStyleSpans(Node* dummySpanAncestor)
774{
775    if (!dummySpanAncestor)
776        return;
777
778    // Dummy spans are created when text node is split, so that style information
779    // can be propagated, which can result in more splitting. If a dummy span gets
780    // cloned/split, the new node is always a sibling of it. Therefore, we scan
781    // all the children of the dummy's parent
782    Node* next;
783    for (Node* node = dummySpanAncestor->firstChild(); node; node = next) {
784        next = node->nextSibling();
785        if (isUnstyledStyleSpan(node))
786            removeNodePreservingChildren(node);
787        node = next;
788    }
789}
790
791HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool before, RefPtr<CSSPrimitiveValue> allowedDirection)
792{
793    // We are allowed to leave the highest ancestor with unicode-bidi unsplit if it is unicode-bidi: embed and direction: allowedDirection.
794    // In that case, we return the unsplit ancestor. Otherwise, we return 0.
795    Node* block = enclosingBlock(node);
796    if (!block)
797        return 0;
798
799    Node* highestAncestorWithUnicodeBidi = 0;
800    Node* nextHighestAncestorWithUnicodeBidi = 0;
801    RefPtr<CSSPrimitiveValue> highestAncestorUnicodeBidi;
802    for (Node* n = node->parent(); n != block; n = n->parent()) {
803        RefPtr<CSSValue> unicodeBidi = computedStyle(n)->getPropertyCSSValue(CSSPropertyUnicodeBidi);
804        if (unicodeBidi) {
805            ASSERT(unicodeBidi->isPrimitiveValue());
806            if (static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent() != CSSValueNormal) {
807                highestAncestorUnicodeBidi = static_cast<CSSPrimitiveValue*>(unicodeBidi.get());
808                nextHighestAncestorWithUnicodeBidi = highestAncestorWithUnicodeBidi;
809                highestAncestorWithUnicodeBidi = n;
810            }
811        }
812    }
813
814    if (!highestAncestorWithUnicodeBidi)
815        return 0;
816
817    HTMLElement* unsplitAncestor = 0;
818
819    if (allowedDirection && highestAncestorUnicodeBidi->getIdent() != CSSValueBidiOverride) {
820        RefPtr<CSSValue> highestAncestorDirection = computedStyle(highestAncestorWithUnicodeBidi)->getPropertyCSSValue(CSSPropertyDirection);
821        ASSERT(highestAncestorDirection->isPrimitiveValue());
822        if (static_cast<CSSPrimitiveValue*>(highestAncestorDirection.get())->getIdent() == allowedDirection->getIdent() && highestAncestorWithUnicodeBidi->isHTMLElement()) {
823            if (!nextHighestAncestorWithUnicodeBidi)
824                return static_cast<HTMLElement*>(highestAncestorWithUnicodeBidi);
825
826            unsplitAncestor = static_cast<HTMLElement*>(highestAncestorWithUnicodeBidi);
827            highestAncestorWithUnicodeBidi = nextHighestAncestorWithUnicodeBidi;
828        }
829    }
830
831    // Split every ancestor through highest ancestor with embedding.
832    Node* n = node;
833    while (true) {
834        Element* parent = static_cast<Element*>(n->parent());
835        if (before ? n->previousSibling() : n->nextSibling())
836            splitElement(parent, before ? n : n->nextSibling());
837        if (parent == highestAncestorWithUnicodeBidi)
838            break;
839        n = n->parent();
840    }
841    return unsplitAncestor;
842}
843
844void ApplyStyleCommand::removeEmbeddingUpToEnclosingBlock(Node* node, Node* unsplitAncestor)
845{
846    Node* block = enclosingBlock(node);
847    if (!block)
848        return;
849
850    Node* n = node->parent();
851    while (n != block && n != unsplitAncestor) {
852        Node* parent = n->parent();
853        if (!n->isStyledElement()) {
854            n = parent;
855            continue;
856        }
857
858        StyledElement* element = static_cast<StyledElement*>(n);
859        RefPtr<CSSValue> unicodeBidi = computedStyle(element)->getPropertyCSSValue(CSSPropertyUnicodeBidi);
860        if (unicodeBidi) {
861            ASSERT(unicodeBidi->isPrimitiveValue());
862            if (static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent() != CSSValueNormal) {
863                // FIXME: This code should really consider the mapped attribute 'dir', the inline style declaration,
864                // and all matching style rules in order to determine how to best set the unicode-bidi property to 'normal'.
865                // For now, it assumes that if the 'dir' attribute is present, then removing it will suffice, and
866                // otherwise it sets the property in the inline style declaration.
867                if (element->hasAttribute(dirAttr)) {
868                    // FIXME: If this is a BDO element, we should probably just remove it if it has no
869                    // other attributes, like we (should) do with B and I elements.
870                    removeNodeAttribute(element, dirAttr);
871                } else {
872                    RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy();
873                    inlineStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal);
874                    inlineStyle->removeProperty(CSSPropertyDirection);
875                    setNodeAttribute(element, styleAttr, inlineStyle->cssText());
876                    // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan?  Need a test.
877                    if (isUnstyledStyleSpan(element))
878                        removeNodePreservingChildren(element);
879                }
880            }
881        }
882        n = parent;
883    }
884}
885
886void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclaration *style)
887{
888    Node* startDummySpanAncestor = 0;
889    Node* endDummySpanAncestor = 0;
890
891    // update document layout once before removing styles
892    // so that we avoid the expense of updating before each and every call
893    // to check a computed style
894    updateLayout();
895
896    // adjust to the positions we want to use for applying style
897    Position start = startPosition();
898    Position end = endPosition();
899    if (comparePositions(end, start) < 0) {
900        Position swap = start;
901        start = end;
902        end = swap;
903    }
904
905    // split the start node and containing element if the selection starts inside of it
906    bool splitStart = splitTextElementAtStartIfNeeded(start, end);
907    if (splitStart) {
908        start = startPosition();
909        end = endPosition();
910        startDummySpanAncestor = dummySpanAncestorForNode(start.node());
911    }
912
913    // split the end node and containing element if the selection ends inside of it
914    bool splitEnd = splitTextElementAtEndIfNeeded(start, end);
915    if (splitEnd) {
916        start = startPosition();
917        end = endPosition();
918        endDummySpanAncestor = dummySpanAncestorForNode(end.node());
919    }
920
921    RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
922    RefPtr<CSSValue> direction;
923    HTMLElement* startUnsplitAncestor = 0;
924    HTMLElement* endUnsplitAncestor = 0;
925    if (unicodeBidi) {
926        RefPtr<CSSPrimitiveValue> allowedDirection;
927        ASSERT(unicodeBidi->isPrimitiveValue());
928        if (static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent() == CSSValueEmbed) {
929            // Leave alone an ancestor that provides the desired single level embedding, if there is one.
930            direction = style->getPropertyCSSValue(CSSPropertyDirection);
931            ASSERT(direction->isPrimitiveValue());
932            allowedDirection = static_cast<CSSPrimitiveValue*>(direction.get());
933        }
934        startUnsplitAncestor = splitAncestorsWithUnicodeBidi(start.node(), true, allowedDirection);
935        endUnsplitAncestor = splitAncestorsWithUnicodeBidi(end.node(), false, allowedDirection);
936        removeEmbeddingUpToEnclosingBlock(start.node(), startUnsplitAncestor);
937        removeEmbeddingUpToEnclosingBlock(end.node(), endUnsplitAncestor);
938    }
939
940    // Remove style from the selection.
941    // Use the upstream position of the start for removing style.
942    // This will ensure we remove all traces of the relevant styles from the selection
943    // and prevent us from adding redundant ones, as described in:
944    // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags
945    Position removeStart = start.upstream();
946    Position embeddingRemoveStart = removeStart;
947    Position embeddingRemoveEnd = end;
948    if (unicodeBidi) {
949        // Avoid removing the dir attribute and the unicode-bidi and direction properties from the unsplit ancestors.
950        if (startUnsplitAncestor && nodeFullySelected(startUnsplitAncestor, removeStart, end))
951            embeddingRemoveStart = positionInParentAfterNode(startUnsplitAncestor);
952        if (endUnsplitAncestor && nodeFullySelected(endUnsplitAncestor, removeStart, end))
953            embeddingRemoveEnd = positionInParentBeforeNode(endUnsplitAncestor).downstream();
954    }
955
956    if (embeddingRemoveStart != removeStart || embeddingRemoveEnd != end) {
957        RefPtr<CSSMutableStyleDeclaration> embeddingStyle = CSSMutableStyleDeclaration::create();
958        embeddingStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed);
959        embeddingStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
960        if (comparePositions(embeddingRemoveStart, embeddingRemoveEnd) <= 0)
961            removeInlineStyle(embeddingStyle, embeddingRemoveStart, embeddingRemoveEnd);
962
963        RefPtr<CSSMutableStyleDeclaration> styleWithoutEmbedding = style->copy();
964        styleWithoutEmbedding->removeProperty(CSSPropertyUnicodeBidi);
965        styleWithoutEmbedding->removeProperty(CSSPropertyDirection);
966        removeInlineStyle(styleWithoutEmbedding, removeStart, end);
967    } else
968        removeInlineStyle(style, removeStart, end);
969
970    start = startPosition();
971    end = endPosition();
972
973    if (splitStart) {
974        bool mergedStart = mergeStartWithPreviousIfIdentical(start, end);
975        if (mergedStart) {
976            start = startPosition();
977            end = endPosition();
978        }
979    }
980
981    if (splitEnd) {
982        mergeEndWithNextIfIdentical(start, end);
983        start = startPosition();
984        end = endPosition();
985    }
986
987    // update document layout once before running the rest of the function
988    // so that we avoid the expense of updating before each and every call
989    // to check a computed style
990    updateLayout();
991
992    Position embeddingApplyStart = start;
993    Position embeddingApplyEnd = end;
994    if (unicodeBidi) {
995        // Avoid applying the unicode-bidi and direction properties beneath ancestors that already have them.
996        Node* startEnclosingBlock = enclosingBlock(start.node());
997        for (Node* n = start.node(); n != startEnclosingBlock; n = n->parent()) {
998            if (n->isHTMLElement()) {
999                RefPtr<CSSValue> ancestorUnicodeBidi = computedStyle(n)->getPropertyCSSValue(CSSPropertyUnicodeBidi);
1000                if (ancestorUnicodeBidi) {
1001                    ASSERT(ancestorUnicodeBidi->isPrimitiveValue());
1002                    if (static_cast<CSSPrimitiveValue*>(ancestorUnicodeBidi.get())->getIdent() == CSSValueEmbed) {
1003                        embeddingApplyStart = positionInParentAfterNode(n);
1004                        break;
1005                    }
1006                }
1007            }
1008        }
1009
1010        Node* endEnclosingBlock = enclosingBlock(end.node());
1011        for (Node* n = end.node(); n != endEnclosingBlock; n = n->parent()) {
1012            if (n->isHTMLElement()) {
1013                RefPtr<CSSValue> ancestorUnicodeBidi = computedStyle(n)->getPropertyCSSValue(CSSPropertyUnicodeBidi);
1014                if (ancestorUnicodeBidi) {
1015                    ASSERT(ancestorUnicodeBidi->isPrimitiveValue());
1016                    if (static_cast<CSSPrimitiveValue*>(ancestorUnicodeBidi.get())->getIdent() == CSSValueEmbed) {
1017                        embeddingApplyEnd = positionInParentBeforeNode(n);
1018                        break;
1019                    }
1020                }
1021            }
1022        }
1023    }
1024
1025    if (embeddingApplyStart != start || embeddingApplyEnd != end) {
1026        if (embeddingApplyStart.isNotNull() && embeddingApplyEnd.isNotNull()) {
1027            RefPtr<CSSMutableStyleDeclaration> embeddingStyle = CSSMutableStyleDeclaration::create();
1028            embeddingStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed);
1029            embeddingStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
1030            applyInlineStyleToRange(embeddingStyle.get(), embeddingApplyStart, embeddingApplyEnd);
1031        }
1032
1033        RefPtr<CSSMutableStyleDeclaration> styleWithoutEmbedding = style->copy();
1034        styleWithoutEmbedding->removeProperty(CSSPropertyUnicodeBidi);
1035        styleWithoutEmbedding->removeProperty(CSSPropertyDirection);
1036        applyInlineStyleToRange(styleWithoutEmbedding.get(), start, end);
1037    } else
1038        applyInlineStyleToRange(style, start, end);
1039
1040    // Remove dummy style spans created by splitting text elements.
1041    cleanupUnstyledAppleStyleSpans(startDummySpanAncestor);
1042    if (endDummySpanAncestor != startDummySpanAncestor)
1043        cleanupUnstyledAppleStyleSpans(endDummySpanAncestor);
1044}
1045
1046void ApplyStyleCommand::applyInlineStyleToRange(CSSMutableStyleDeclaration* style, const Position& start, const Position& rangeEnd)
1047{
1048    Node* node = start.node();
1049    Position end = rangeEnd;
1050
1051    bool rangeIsEmpty = false;
1052
1053    if (start.deprecatedEditingOffset() >= caretMaxOffset(start.node())) {
1054        node = node->traverseNextNode();
1055        Position newStart = Position(node, 0);
1056        if (!node || comparePositions(end, newStart) < 0)
1057            rangeIsEmpty = true;
1058    }
1059
1060    if (!rangeIsEmpty) {
1061        // pastEndNode is the node after the last fully selected node.
1062        Node* pastEndNode = end.node();
1063        if (end.deprecatedEditingOffset() >= caretMaxOffset(end.node()))
1064            pastEndNode = end.node()->traverseNextSibling();
1065        // FIXME: Callers should perform this operation on a Range that includes the br
1066        // if they want style applied to the empty line.
1067        if (start == end && start.node()->hasTagName(brTag))
1068            pastEndNode = start.node()->traverseNextNode();
1069        // Add the style to selected inline runs.
1070        for (Node* next; node && node != pastEndNode; node = next) {
1071
1072            next = node->traverseNextNode();
1073
1074            if (!node->renderer() || !node->isContentEditable())
1075                continue;
1076
1077            if (!node->isContentRichlyEditable() && node->isHTMLElement()) {
1078                // This is a plaintext-only region. Only proceed if it's fully selected.
1079                // pastEndNode is the node after the last fully selected node, so if it's inside node then
1080                // node isn't fully selected.
1081                if (pastEndNode && pastEndNode->isDescendantOf(node))
1082                    break;
1083                // Add to this element's inline style and skip over its contents.
1084                HTMLElement* element = static_cast<HTMLElement*>(node);
1085                RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy();
1086                inlineStyle->merge(style);
1087                setNodeAttribute(element, styleAttr, inlineStyle->cssText());
1088                next = node->traverseNextSibling();
1089                continue;
1090            }
1091
1092            if (isBlock(node))
1093                continue;
1094
1095            if (node->childNodeCount()) {
1096                if (editingIgnoresContent(node)) {
1097                    next = node->traverseNextSibling();
1098                    continue;
1099                }
1100                continue;
1101            }
1102
1103            Node* runStart = node;
1104            // Find the end of the run.
1105            Node* sibling = node->nextSibling();
1106            while (sibling && sibling != pastEndNode && (!sibling->isElementNode() || sibling->hasTagName(brTag)) && !isBlock(sibling)) {
1107                node = sibling;
1108                sibling = node->nextSibling();
1109            }
1110            // Recompute next, since node has changed.
1111            next = node->traverseNextNode();
1112            // Apply the style to the run.
1113            addInlineStyleIfNeeded(style, runStart, node);
1114        }
1115    }
1116}
1117
1118bool ApplyStyleCommand::shouldRemoveTextDecorationTag(CSSStyleDeclaration* styleToApply, int textDecorationAddedByTag) const
1119{
1120    // Honor text-decorations-in-effect
1121    RefPtr<CSSValue> textDecorationsToApply = styleToApply->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1122    if (!textDecorationsToApply || !textDecorationsToApply->isValueList())
1123        textDecorationsToApply = styleToApply->getPropertyCSSValue(CSSPropertyTextDecoration);
1124
1125    // When there is no text decorations to apply, remove any one of u, s, & strike
1126    if (!textDecorationsToApply || !textDecorationsToApply->isValueList())
1127        return true;
1128
1129    // Remove node if it implicitly adds style not present in styleToApply
1130    CSSValueList* valueList = static_cast<CSSValueList*>(textDecorationsToApply.get());
1131    RefPtr<CSSPrimitiveValue> value = CSSPrimitiveValue::createIdentifier(textDecorationAddedByTag);
1132    return !valueList->hasValue(value.get());
1133}
1134
1135// This function maps from styling tags to CSS styles.  Used for knowing which
1136// styling tags should be removed when toggling styles.
1137bool ApplyStyleCommand::implicitlyStyledElementShouldBeRemovedWhenApplyingStyle(HTMLElement* elem, CSSMutableStyleDeclaration* style)
1138{
1139    CSSMutableStyleDeclaration::const_iterator end = style->end();
1140    for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
1141        const CSSProperty& property = *it;
1142        // FIXME: This should probably be re-written to lookup the tagname in a
1143        // hash and match against an expected property/value pair.
1144        switch (property.id()) {
1145        case CSSPropertyFontWeight:
1146            // IE inserts "strong" tags for execCommand("bold"), so we remove them, even though they're not strictly presentational
1147            if (elem->hasLocalName(bTag) || elem->hasLocalName(strongTag))
1148                return !equalIgnoringCase(property.value()->cssText(), "bold") || !elem->hasChildNodes();
1149            break;
1150        case CSSPropertyVerticalAlign:
1151            if (elem->hasLocalName(subTag))
1152                return !equalIgnoringCase(property.value()->cssText(), "sub") || !elem->hasChildNodes();
1153            if (elem->hasLocalName(supTag))
1154                return !equalIgnoringCase(property.value()->cssText(), "sup") || !elem->hasChildNodes();
1155            break;
1156        case CSSPropertyFontStyle:
1157            // IE inserts "em" tags for execCommand("italic"), so we remove them, even though they're not strictly presentational
1158            if (elem->hasLocalName(iTag) || elem->hasLocalName(emTag))
1159                return !equalIgnoringCase(property.value()->cssText(), "italic") || !elem->hasChildNodes();
1160            break;
1161        case CSSPropertyTextDecoration:
1162        case CSSPropertyWebkitTextDecorationsInEffect:
1163                if (elem->hasLocalName(uTag))
1164                    return shouldRemoveTextDecorationTag(style, CSSValueUnderline) || !elem->hasChildNodes();
1165                else if (elem->hasLocalName(sTag) || elem->hasTagName(strikeTag))
1166                    return shouldRemoveTextDecorationTag(style,CSSValueLineThrough) || !elem->hasChildNodes();
1167        }
1168    }
1169    return false;
1170}
1171
1172void ApplyStyleCommand::replaceWithSpanOrRemoveIfWithoutAttributes(HTMLElement*& elem)
1173{
1174    bool removeNode = false;
1175
1176    // Similar to isSpanWithoutAttributesOrUnstyleStyleSpan, but does not look for Apple-style-span.
1177    NamedNodeMap* attributes = elem->attributes(true); // readonly
1178    if (!attributes || attributes->isEmpty())
1179        removeNode = true;
1180    else if (attributes->length() == 1 && elem->hasAttribute(styleAttr)) {
1181        // Remove the element even if it has just style='' (this might be redundantly checked later too)
1182        CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl();
1183        if (!inlineStyleDecl || inlineStyleDecl->isEmpty())
1184            removeNode = true;
1185    }
1186
1187    if (removeNode)
1188        removeNodePreservingChildren(elem);
1189    else {
1190        HTMLElement* newSpanElement = replaceNodeWithSpanPreservingChildrenAndAttributes(elem);
1191        ASSERT(newSpanElement && newSpanElement->inDocument());
1192        elem = newSpanElement;
1193    }
1194}
1195
1196void ApplyStyleCommand::removeHTMLFontStyle(CSSMutableStyleDeclaration *style, HTMLElement *elem)
1197{
1198    ASSERT(style);
1199    ASSERT(elem);
1200
1201    if (!elem->hasLocalName(fontTag))
1202        return;
1203
1204    CSSMutableStyleDeclaration::const_iterator end = style->end();
1205    for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
1206        switch ((*it).id()) {
1207            case CSSPropertyColor:
1208                removeNodeAttribute(elem, colorAttr);
1209                break;
1210            case CSSPropertyFontFamily:
1211                removeNodeAttribute(elem, faceAttr);
1212                break;
1213            case CSSPropertyFontSize:
1214                removeNodeAttribute(elem, sizeAttr);
1215                break;
1216        }
1217    }
1218
1219    if (isEmptyFontTag(elem))
1220        removeNodePreservingChildren(elem);
1221}
1222
1223void ApplyStyleCommand::removeHTMLBidiEmbeddingStyle(CSSMutableStyleDeclaration *style, HTMLElement *elem)
1224{
1225    ASSERT(style);
1226    ASSERT(elem);
1227
1228    if (!elem->hasAttribute(dirAttr))
1229        return;
1230
1231    if (!style->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->getPropertyCSSValue(CSSPropertyDirection))
1232        return;
1233
1234    removeNodeAttribute(elem, dirAttr);
1235
1236    // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan?  Need a test.
1237    if (isUnstyledStyleSpan(elem))
1238        removeNodePreservingChildren(elem);
1239}
1240
1241void ApplyStyleCommand::removeCSSStyle(CSSMutableStyleDeclaration* style, HTMLElement* elem)
1242{
1243    ASSERT(style);
1244    ASSERT(elem);
1245
1246    CSSMutableStyleDeclaration* decl = elem->inlineStyleDecl();
1247    if (!decl)
1248        return;
1249
1250    CSSMutableStyleDeclaration::const_iterator end = style->end();
1251    for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
1252        CSSPropertyID propertyID = static_cast<CSSPropertyID>((*it).id());
1253        RefPtr<CSSValue> value = decl->getPropertyCSSValue(propertyID);
1254        if (value && (propertyID != CSSPropertyWhiteSpace || !isTabSpanNode(elem))) {
1255            removeCSSProperty(decl, propertyID);
1256            if (propertyID == CSSPropertyUnicodeBidi && !decl->getPropertyValue(CSSPropertyDirection).isEmpty())
1257                removeCSSProperty(decl, CSSPropertyDirection);
1258        }
1259    }
1260
1261    // No need to serialize <foo style=""> if we just removed the last css property
1262    if (decl->isEmpty())
1263        removeNodeAttribute(elem, styleAttr);
1264
1265    if (isSpanWithoutAttributesOrUnstyleStyleSpan(elem))
1266        removeNodePreservingChildren(elem);
1267}
1268
1269static bool hasTextDecorationProperty(Node *node)
1270{
1271    if (!node->isElementNode())
1272        return false;
1273
1274    RefPtr<CSSValue> value = computedStyle(node)->getPropertyCSSValue(CSSPropertyTextDecoration, DoNotUpdateLayout);
1275    return value && !equalIgnoringCase(value->cssText(), "none");
1276}
1277
1278static Node* highestAncestorWithTextDecoration(Node *node)
1279{
1280    ASSERT(node);
1281    Node* result = 0;
1282    Node* unsplittableElement = unsplittableElementForPosition(Position(node, 0));
1283
1284    for (Node *n = node; n; n = n->parentNode()) {
1285        if (hasTextDecorationProperty(n))
1286            result = n;
1287        // Should stop at the editable root (cannot cross editing boundary) and
1288        // also stop at the unsplittable element to be consistent with other UAs
1289        if (n == unsplittableElement)
1290            break;
1291    }
1292
1293    return result;
1294}
1295
1296PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractTextDecorationStyle(Node* node)
1297{
1298    ASSERT(node);
1299    ASSERT(node->isElementNode());
1300
1301    // non-html elements not handled yet
1302    if (!node->isHTMLElement())
1303        return 0;
1304
1305    HTMLElement *element = static_cast<HTMLElement *>(node);
1306    RefPtr<CSSMutableStyleDeclaration> style = element->inlineStyleDecl();
1307    if (!style)
1308        return 0;
1309
1310    int properties[1] = { CSSPropertyTextDecoration };
1311    RefPtr<CSSMutableStyleDeclaration> textDecorationStyle = style->copyPropertiesInSet(properties, 1);
1312
1313    RefPtr<CSSValue> property = style->getPropertyCSSValue(CSSPropertyTextDecoration);
1314    if (property && !equalIgnoringCase(property->cssText(), "none"))
1315        removeCSSProperty(style.get(), CSSPropertyTextDecoration);
1316
1317    return textDecorationStyle.release();
1318}
1319
1320PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractAndNegateTextDecorationStyle(Node* node)
1321{
1322    ASSERT(node);
1323    ASSERT(node->isElementNode());
1324
1325    // non-html elements not handled yet
1326    if (!node->isHTMLElement())
1327        return 0;
1328
1329    RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node);
1330    ASSERT(nodeStyle);
1331
1332    int properties[1] = { CSSPropertyTextDecoration };
1333    RefPtr<CSSMutableStyleDeclaration> textDecorationStyle = nodeStyle->copyPropertiesInSet(properties, 1);
1334
1335    RefPtr<CSSValue> property = nodeStyle->getPropertyCSSValue(CSSPropertyTextDecoration);
1336    if (property && !equalIgnoringCase(property->cssText(), "none")) {
1337        RefPtr<CSSMutableStyleDeclaration> newStyle = textDecorationStyle->copy();
1338        newStyle->setProperty(CSSPropertyTextDecoration, "none");
1339        applyTextDecorationStyle(node, newStyle.get());
1340    }
1341
1342    return textDecorationStyle.release();
1343}
1344
1345void ApplyStyleCommand::applyTextDecorationStyle(Node *node, CSSMutableStyleDeclaration *style)
1346{
1347    ASSERT(node);
1348
1349    if (!style || style->cssText().isEmpty())
1350        return;
1351
1352    StyleChange styleChange(style, Position(node, 0));
1353    if (styleChange.cssStyle().length()) {
1354        if (node->isTextNode()) {
1355            RefPtr<HTMLElement> styleSpan = createStyleSpanElement(document());
1356            surroundNodeRangeWithElement(node, node, styleSpan.get());
1357            node = styleSpan.get();
1358        }
1359
1360        if (!node->isElementNode())
1361            return;
1362
1363        HTMLElement *element = static_cast<HTMLElement *>(node);
1364        String cssText = styleChange.cssStyle();
1365        CSSMutableStyleDeclaration *decl = element->inlineStyleDecl();
1366        if (decl)
1367            cssText += decl->cssText();
1368        setNodeAttribute(element, styleAttr, cssText);
1369    }
1370
1371    if (styleChange.applyUnderline())
1372        surroundNodeRangeWithElement(node, node, createHTMLElement(document(), uTag));
1373
1374    if (styleChange.applyLineThrough())
1375        surroundNodeRangeWithElement(node, node, createHTMLElement(document(), sTag));
1376}
1377
1378void ApplyStyleCommand::pushDownTextDecorationStyleAroundNode(Node* targetNode, bool forceNegate)
1379{
1380    ASSERT(targetNode);
1381    Node* highestAncestor = highestAncestorWithTextDecoration(targetNode);
1382    if (!highestAncestor)
1383        return;
1384
1385    // The outer loop is traversing the tree vertically from highestAncestor to targetNode
1386    Node* current = highestAncestor;
1387    while (current != targetNode) {
1388        ASSERT(current);
1389        ASSERT(current->contains(targetNode));
1390        RefPtr<CSSMutableStyleDeclaration> decoration = forceNegate ? extractAndNegateTextDecorationStyle(current) : extractTextDecorationStyle(current);
1391
1392        // The inner loop will go through children on each level
1393        Node* child = current->firstChild();
1394        while (child) {
1395            Node* nextChild = child->nextSibling();
1396
1397            // Apply text decoration to all nodes containing targetNode and their siblings but NOT to targetNode
1398            if (child != targetNode)
1399                applyTextDecorationStyle(child, decoration.get());
1400
1401            // We found the next node for the outer loop (contains targetNode)
1402            // When reached targetNode, stop the outer loop upon the completion of the current inner loop
1403            if (child == targetNode || child->contains(targetNode))
1404                current = child;
1405
1406            child = nextChild;
1407        }
1408    }
1409}
1410
1411void ApplyStyleCommand::pushDownTextDecorationStyleAtBoundaries(const Position &start, const Position &end)
1412{
1413    // We need to work in two passes. First we push down any inline
1414    // styles that set text decoration. Then we look for any remaining
1415    // styles (caused by stylesheets) and explicitly negate text
1416    // decoration while pushing down.
1417
1418    pushDownTextDecorationStyleAroundNode(start.node(), false);
1419    updateLayout();
1420    pushDownTextDecorationStyleAroundNode(start.node(), true);
1421
1422    pushDownTextDecorationStyleAroundNode(end.node(), false);
1423    updateLayout();
1424    pushDownTextDecorationStyleAroundNode(end.node(), true);
1425}
1426
1427// FIXME: Why does this exist?  Callers should either use lastOffsetForEditing or lastOffsetInNode
1428static int maxRangeOffset(Node *n)
1429{
1430    if (n->offsetInCharacters())
1431        return n->maxCharacterOffset();
1432
1433    if (n->isElementNode())
1434        return n->childNodeCount();
1435
1436    return 1;
1437}
1438
1439void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration> style, const Position &start, const Position &end)
1440{
1441    ASSERT(start.isNotNull());
1442    ASSERT(end.isNotNull());
1443    ASSERT(start.node()->inDocument());
1444    ASSERT(end.node()->inDocument());
1445    ASSERT(comparePositions(start, end) <= 0);
1446
1447    RefPtr<CSSValue> textDecorationSpecialProperty = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1448
1449    if (textDecorationSpecialProperty) {
1450        pushDownTextDecorationStyleAtBoundaries(start.downstream(), end.upstream());
1451        style = style->copy();
1452        style->setProperty(CSSPropertyTextDecoration, textDecorationSpecialProperty->cssText(), style->getPropertyPriority(CSSPropertyWebkitTextDecorationsInEffect));
1453    }
1454
1455    // The s and e variables store the positions used to set the ending selection after style removal
1456    // takes place. This will help callers to recognize when either the start node or the end node
1457    // are removed from the document during the work of this function.
1458    Position s = start;
1459    Position e = end;
1460
1461    Node* node = start.node();
1462    while (node) {
1463        Node* next = node->traverseNextNode();
1464        if (node->isHTMLElement() && nodeFullySelected(node, start, end)) {
1465            HTMLElement* elem = static_cast<HTMLElement*>(node);
1466            Node* prev = elem->traversePreviousNodePostOrder();
1467            Node* next = elem->traverseNextNode();
1468            if (m_styledInlineElement && elem->hasTagName(m_styledInlineElement->tagQName()))
1469                removeNodePreservingChildren(elem);
1470
1471            if (implicitlyStyledElementShouldBeRemovedWhenApplyingStyle(elem, style.get()))
1472                replaceWithSpanOrRemoveIfWithoutAttributes(elem);
1473
1474            // If the node was converted to a span, the span may still contain relevant
1475            // styles which must be removed (e.g. <b style='font-weight: bold'>)
1476            if (elem->inDocument()) {
1477                removeHTMLFontStyle(style.get(), elem);
1478                removeHTMLBidiEmbeddingStyle(style.get(), elem);
1479                removeCSSStyle(style.get(), elem);
1480            }
1481            if (!elem->inDocument()) {
1482                if (s.node() == elem) {
1483                    // Since elem must have been fully selected, and it is at the start
1484                    // of the selection, it is clear we can set the new s offset to 0.
1485                    ASSERT(s.deprecatedEditingOffset() <= caretMinOffset(s.node()));
1486                    s = Position(next, 0);
1487                }
1488                if (e.node() == elem) {
1489                    // Since elem must have been fully selected, and it is at the end
1490                    // of the selection, it is clear we can set the new e offset to
1491                    // the max range offset of prev.
1492                    ASSERT(e.deprecatedEditingOffset() >= maxRangeOffset(e.node()));
1493                    e = Position(prev, maxRangeOffset(prev));
1494                }
1495            }
1496        }
1497        if (node == end.node())
1498            break;
1499        node = next;
1500    }
1501
1502    ASSERT(s.node()->inDocument());
1503    ASSERT(e.node()->inDocument());
1504    updateStartEnd(s, e);
1505}
1506
1507bool ApplyStyleCommand::nodeFullySelected(Node *node, const Position &start, const Position &end) const
1508{
1509    ASSERT(node);
1510    ASSERT(node->isElementNode());
1511
1512    Position pos = Position(node, node->childNodeCount()).upstream();
1513    return comparePositions(Position(node, 0), start) >= 0 && comparePositions(pos, end) <= 0;
1514}
1515
1516bool ApplyStyleCommand::nodeFullyUnselected(Node *node, const Position &start, const Position &end) const
1517{
1518    ASSERT(node);
1519    ASSERT(node->isElementNode());
1520
1521    Position pos = Position(node, node->childNodeCount()).upstream();
1522    bool isFullyBeforeStart = comparePositions(pos, start) < 0;
1523    bool isFullyAfterEnd = comparePositions(Position(node, 0), end) > 0;
1524
1525    return isFullyBeforeStart || isFullyAfterEnd;
1526}
1527
1528
1529bool ApplyStyleCommand::splitTextAtStartIfNeeded(const Position &start, const Position &end)
1530{
1531    if (start.node()->isTextNode() && start.deprecatedEditingOffset() > caretMinOffset(start.node()) && start.deprecatedEditingOffset() < caretMaxOffset(start.node())) {
1532        int endOffsetAdjustment = start.node() == end.node() ? start.deprecatedEditingOffset() : 0;
1533        Text *text = static_cast<Text *>(start.node());
1534        splitTextNode(text, start.deprecatedEditingOffset());
1535        updateStartEnd(Position(start.node(), 0), Position(end.node(), end.deprecatedEditingOffset() - endOffsetAdjustment));
1536        return true;
1537    }
1538    return false;
1539}
1540
1541bool ApplyStyleCommand::splitTextAtEndIfNeeded(const Position &start, const Position &end)
1542{
1543    if (end.node()->isTextNode() && end.deprecatedEditingOffset() > caretMinOffset(end.node()) && end.deprecatedEditingOffset() < caretMaxOffset(end.node())) {
1544        Text *text = static_cast<Text *>(end.node());
1545        splitTextNode(text, end.deprecatedEditingOffset());
1546
1547        Node *prevNode = text->previousSibling();
1548        ASSERT(prevNode);
1549        Node *startNode = start.node() == end.node() ? prevNode : start.node();
1550        ASSERT(startNode);
1551        updateStartEnd(Position(startNode, start.deprecatedEditingOffset()), Position(prevNode, caretMaxOffset(prevNode)));
1552        return true;
1553    }
1554    return false;
1555}
1556
1557bool ApplyStyleCommand::splitTextElementAtStartIfNeeded(const Position &start, const Position &end)
1558{
1559    if (start.node()->isTextNode() && start.deprecatedEditingOffset() > caretMinOffset(start.node()) && start.deprecatedEditingOffset() < caretMaxOffset(start.node())) {
1560        int endOffsetAdjustment = start.node() == end.node() ? start.deprecatedEditingOffset() : 0;
1561        Text *text = static_cast<Text *>(start.node());
1562        splitTextNodeContainingElement(text, start.deprecatedEditingOffset());
1563
1564        updateStartEnd(Position(start.node()->parentNode(), start.node()->nodeIndex()), Position(end.node(), end.deprecatedEditingOffset() - endOffsetAdjustment));
1565        return true;
1566    }
1567    return false;
1568}
1569
1570bool ApplyStyleCommand::splitTextElementAtEndIfNeeded(const Position &start, const Position &end)
1571{
1572    if (end.node()->isTextNode() && end.deprecatedEditingOffset() > caretMinOffset(end.node()) && end.deprecatedEditingOffset() < caretMaxOffset(end.node())) {
1573        Text *text = static_cast<Text *>(end.node());
1574        splitTextNodeContainingElement(text, end.deprecatedEditingOffset());
1575
1576        Node *prevNode = text->parent()->previousSibling()->lastChild();
1577        ASSERT(prevNode);
1578        Node *startNode = start.node() == end.node() ? prevNode : start.node();
1579        ASSERT(startNode);
1580        updateStartEnd(Position(startNode, start.deprecatedEditingOffset()), Position(prevNode->parent(), prevNode->nodeIndex() + 1));
1581        return true;
1582    }
1583    return false;
1584}
1585
1586static bool areIdenticalElements(Node *first, Node *second)
1587{
1588    // check that tag name and all attribute names and values are identical
1589
1590    if (!first->isElementNode())
1591        return false;
1592
1593    if (!second->isElementNode())
1594        return false;
1595
1596    Element *firstElement = static_cast<Element *>(first);
1597    Element *secondElement = static_cast<Element *>(second);
1598
1599    if (!firstElement->tagQName().matches(secondElement->tagQName()))
1600        return false;
1601
1602    NamedNodeMap *firstMap = firstElement->attributes();
1603    NamedNodeMap *secondMap = secondElement->attributes();
1604
1605    unsigned firstLength = firstMap->length();
1606
1607    if (firstLength != secondMap->length())
1608        return false;
1609
1610    for (unsigned i = 0; i < firstLength; i++) {
1611        Attribute *attribute = firstMap->attributeItem(i);
1612        Attribute *secondAttribute = secondMap->getAttributeItem(attribute->name());
1613
1614        if (!secondAttribute || attribute->value() != secondAttribute->value())
1615            return false;
1616    }
1617
1618    return true;
1619}
1620
1621bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position &start, const Position &end)
1622{
1623    Node *startNode = start.node();
1624    int startOffset = start.deprecatedEditingOffset();
1625
1626    if (isAtomicNode(start.node())) {
1627        if (start.deprecatedEditingOffset() != 0)
1628            return false;
1629
1630        // note: prior siblings could be unrendered elements. it's silly to miss the
1631        // merge opportunity just for that.
1632        if (start.node()->previousSibling())
1633            return false;
1634
1635        startNode = start.node()->parent();
1636        startOffset = 0;
1637    }
1638
1639    if (!startNode->isElementNode())
1640        return false;
1641
1642    if (startOffset != 0)
1643        return false;
1644
1645    Node *previousSibling = startNode->previousSibling();
1646
1647    if (previousSibling && areIdenticalElements(startNode, previousSibling)) {
1648        Element *previousElement = static_cast<Element *>(previousSibling);
1649        Element *element = static_cast<Element *>(startNode);
1650        Node *startChild = element->firstChild();
1651        ASSERT(startChild);
1652        mergeIdenticalElements(previousElement, element);
1653
1654        int startOffsetAdjustment = startChild->nodeIndex();
1655        int endOffsetAdjustment = startNode == end.node() ? startOffsetAdjustment : 0;
1656        updateStartEnd(Position(startNode, startOffsetAdjustment), Position(end.node(), end.deprecatedEditingOffset() + endOffsetAdjustment));
1657        return true;
1658    }
1659
1660    return false;
1661}
1662
1663bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position &start, const Position &end)
1664{
1665    Node *endNode = end.node();
1666    int endOffset = end.deprecatedEditingOffset();
1667
1668    if (isAtomicNode(endNode)) {
1669        if (endOffset < caretMaxOffset(endNode))
1670            return false;
1671
1672        unsigned parentLastOffset = end.node()->parent()->childNodes()->length() - 1;
1673        if (end.node()->nextSibling())
1674            return false;
1675
1676        endNode = end.node()->parent();
1677        endOffset = parentLastOffset;
1678    }
1679
1680    if (!endNode->isElementNode() || endNode->hasTagName(brTag))
1681        return false;
1682
1683    Node *nextSibling = endNode->nextSibling();
1684
1685    if (nextSibling && areIdenticalElements(endNode, nextSibling)) {
1686        Element *nextElement = static_cast<Element *>(nextSibling);
1687        Element *element = static_cast<Element *>(endNode);
1688        Node *nextChild = nextElement->firstChild();
1689
1690        mergeIdenticalElements(element, nextElement);
1691
1692        Node *startNode = start.node() == endNode ? nextElement : start.node();
1693        ASSERT(startNode);
1694
1695        int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childNodes()->length();
1696        updateStartEnd(Position(startNode, start.deprecatedEditingOffset()), Position(nextElement, endOffset));
1697        return true;
1698    }
1699
1700    return false;
1701}
1702
1703void ApplyStyleCommand::surroundNodeRangeWithElement(Node* startNode, Node* endNode, PassRefPtr<Element> elementToInsert)
1704{
1705    ASSERT(startNode);
1706    ASSERT(endNode);
1707    ASSERT(elementToInsert);
1708    RefPtr<Element> element = elementToInsert;
1709
1710    insertNodeBefore(element, startNode);
1711
1712    Node* node = startNode;
1713    while (1) {
1714        Node* next = node->traverseNextNode();
1715        if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
1716            removeNode(node);
1717            appendNode(node, element);
1718        }
1719        if (node == endNode)
1720            break;
1721        node = next;
1722    }
1723
1724    Node* nextSibling = element->nextSibling();
1725    Node* previousSibling = element->previousSibling();
1726    if (nextSibling && nextSibling->isElementNode() && nextSibling->isContentEditable()
1727        && areIdenticalElements(element.get(), static_cast<Element*>(nextSibling)))
1728        mergeIdenticalElements(element, static_cast<Element*>(nextSibling));
1729
1730    if (previousSibling && previousSibling->isElementNode() && previousSibling->isContentEditable()) {
1731        Node* mergedElement = previousSibling->nextSibling();
1732        if (mergedElement->isElementNode() && mergedElement->isContentEditable()
1733            && areIdenticalElements(static_cast<Element*>(previousSibling), static_cast<Element*>(mergedElement)))
1734            mergeIdenticalElements(static_cast<Element*>(previousSibling), static_cast<Element*>(mergedElement));
1735    }
1736
1737    // FIXME: We should probably call updateStartEnd if the start or end was in the node
1738    // range so that the endingSelection() is canonicalized.  See the comments at the end of
1739    // VisibleSelection::validate().
1740}
1741
1742void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, HTMLElement* block)
1743{
1744    // Do not check for legacy styles here. Those styles, like <B> and <I>, only apply for
1745    // inline content.
1746    if (!block)
1747        return;
1748
1749    String cssText = styleChange.cssStyle();
1750    CSSMutableStyleDeclaration* decl = block->inlineStyleDecl();
1751    if (decl)
1752        cssText += decl->cssText();
1753    setNodeAttribute(block, styleAttr, cssText);
1754}
1755
1756static bool fontColorChangesComputedStyle(RenderStyle* computedStyle, StyleChange styleChange)
1757{
1758    if (styleChange.applyFontColor()) {
1759        if (Color(styleChange.fontColor()) != computedStyle->color())
1760            return true;
1761    }
1762    return false;
1763}
1764
1765static bool fontSizeChangesComputedStyle(RenderStyle* computedStyle, StyleChange styleChange)
1766{
1767    if (styleChange.applyFontSize()) {
1768        if (styleChange.fontSize().toInt() != computedStyle->fontSize())
1769            return true;
1770    }
1771    return false;
1772}
1773
1774static bool fontFaceChangesComputedStyle(RenderStyle* computedStyle, StyleChange styleChange)
1775{
1776    if (styleChange.applyFontFace()) {
1777        if (computedStyle->fontDescription().family().family().string() != styleChange.fontFace())
1778            return true;
1779    }
1780    return false;
1781}
1782
1783void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style, Node *startNode, Node *endNode)
1784{
1785    if (m_removeOnly)
1786        return;
1787
1788    StyleChange styleChange(style, Position(startNode, 0));
1789
1790    //
1791    // Font tags need to go outside of CSS so that CSS font sizes override leagcy font sizes.
1792    //
1793    if (styleChange.applyFontColor() || styleChange.applyFontFace() || styleChange.applyFontSize()) {
1794        RefPtr<Element> fontElement = createFontElement(document());
1795        RenderStyle* computedStyle = startNode->computedStyle();
1796
1797        // We only want to insert a font element if it will end up changing the style of the
1798        // text somehow. Otherwise it will be a garbage node that will create problems for us
1799        // most notably when we apply a blockquote style for a message reply.
1800        if (fontColorChangesComputedStyle(computedStyle, styleChange)
1801                || fontFaceChangesComputedStyle(computedStyle, styleChange)
1802                || fontSizeChangesComputedStyle(computedStyle, styleChange)) {
1803            if (styleChange.applyFontColor())
1804                fontElement->setAttribute(colorAttr, styleChange.fontColor());
1805            if (styleChange.applyFontFace())
1806                fontElement->setAttribute(faceAttr, styleChange.fontFace());
1807            if (styleChange.applyFontSize())
1808                fontElement->setAttribute(sizeAttr, styleChange.fontSize());
1809            surroundNodeRangeWithElement(startNode, endNode, fontElement.get());
1810        }
1811    }
1812
1813    if (styleChange.cssStyle().length()) {
1814        RefPtr<Element> styleElement = createStyleSpanElement(document());
1815        styleElement->setAttribute(styleAttr, styleChange.cssStyle());
1816        surroundNodeRangeWithElement(startNode, endNode, styleElement.release());
1817    }
1818
1819    if (styleChange.applyBold())
1820        surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), bTag));
1821
1822    if (styleChange.applyItalic())
1823        surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), iTag));
1824
1825    if (styleChange.applyUnderline())
1826        surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), uTag));
1827
1828    if (styleChange.applyLineThrough())
1829        surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), sTag));
1830
1831    if (styleChange.applySubscript())
1832        surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), subTag));
1833    else if (styleChange.applySuperscript())
1834        surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), supTag));
1835
1836    if (m_styledInlineElement)
1837        surroundNodeRangeWithElement(startNode, endNode, m_styledInlineElement->cloneElementWithoutChildren());
1838}
1839
1840float ApplyStyleCommand::computedFontSize(const Node *node)
1841{
1842    if (!node)
1843        return 0;
1844
1845    Position pos(const_cast<Node *>(node), 0);
1846    RefPtr<CSSComputedStyleDeclaration> computedStyle = pos.computedStyle();
1847    if (!computedStyle)
1848        return 0;
1849
1850    RefPtr<CSSPrimitiveValue> value = static_pointer_cast<CSSPrimitiveValue>(computedStyle->getPropertyCSSValue(CSSPropertyFontSize));
1851    if (!value)
1852        return 0;
1853
1854    return value->getFloatValue(CSSPrimitiveValue::CSS_PX);
1855}
1856
1857void ApplyStyleCommand::joinChildTextNodes(Node *node, const Position &start, const Position &end)
1858{
1859    if (!node)
1860        return;
1861
1862    Position newStart = start;
1863    Position newEnd = end;
1864
1865    Node *child = node->firstChild();
1866    while (child) {
1867        Node *next = child->nextSibling();
1868        if (child->isTextNode() && next && next->isTextNode()) {
1869            Text *childText = static_cast<Text *>(child);
1870            Text *nextText = static_cast<Text *>(next);
1871            if (next == start.node())
1872                newStart = Position(childText, childText->length() + start.deprecatedEditingOffset());
1873            if (next == end.node())
1874                newEnd = Position(childText, childText->length() + end.deprecatedEditingOffset());
1875            String textToMove = nextText->data();
1876            insertTextIntoNode(childText, childText->length(), textToMove);
1877            removeNode(next);
1878            // don't move child node pointer. it may want to merge with more text nodes.
1879        }
1880        else {
1881            child = child->nextSibling();
1882        }
1883    }
1884
1885    updateStartEnd(newStart, newEnd);
1886}
1887
1888}
1889