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 "CSSStyleSelector.h"
35#include "CSSValueKeywords.h"
36#include "CSSValueList.h"
37#include "Document.h"
38#include "EditingStyle.h"
39#include "Editor.h"
40#include "Frame.h"
41#include "HTMLFontElement.h"
42#include "HTMLInterchange.h"
43#include "HTMLNames.h"
44#include "NodeList.h"
45#include "Range.h"
46#include "RenderObject.h"
47#include "Text.h"
48#include "TextIterator.h"
49#include "htmlediting.h"
50#include "visible_units.h"
51#include <wtf/StdLibExtras.h>
52
53namespace WebCore {
54
55using namespace HTMLNames;
56
57static String& styleSpanClassString()
58{
59    DEFINE_STATIC_LOCAL(String, styleSpanClassString, ((AppleStyleSpanClass)));
60    return styleSpanClassString;
61}
62
63bool isStyleSpan(const Node *node)
64{
65    if (!node || !node->isHTMLElement())
66        return false;
67
68    const HTMLElement* elem = static_cast<const HTMLElement*>(node);
69    return elem->hasLocalName(spanAttr) && elem->getAttribute(classAttr) == styleSpanClassString();
70}
71
72static bool isUnstyledStyleSpan(const Node* node)
73{
74    if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
75        return false;
76
77    const HTMLElement* elem = static_cast<const HTMLElement*>(node);
78    CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl();
79    return (!inlineStyleDecl || inlineStyleDecl->isEmpty()) && elem->getAttribute(classAttr) == styleSpanClassString();
80}
81
82static bool isSpanWithoutAttributesOrUnstyleStyleSpan(const Node* node)
83{
84    if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
85        return false;
86
87    const HTMLElement* elem = static_cast<const HTMLElement*>(node);
88    NamedNodeMap* attributes = elem->attributes(true); // readonly
89    if (attributes->isEmpty())
90        return true;
91
92    return isUnstyledStyleSpan(node);
93}
94
95static bool isEmptyFontTag(const Node *node)
96{
97    if (!node || !node->hasTagName(fontTag))
98        return false;
99
100    const Element *elem = static_cast<const Element *>(node);
101    NamedNodeMap *map = elem->attributes(true); // true for read-only
102    if (!map)
103        return true;
104    return map->isEmpty() || (map->length() == 1 && elem->getAttribute(classAttr) == styleSpanClassString());
105}
106
107static PassRefPtr<Element> createFontElement(Document* document)
108{
109    RefPtr<Element> fontNode = createHTMLElement(document, fontTag);
110    fontNode->setAttribute(classAttr, styleSpanClassString());
111    return fontNode.release();
112}
113
114PassRefPtr<HTMLElement> createStyleSpanElement(Document* document)
115{
116    RefPtr<HTMLElement> styleElement = createHTMLElement(document, spanTag);
117    styleElement->setAttribute(classAttr, styleSpanClassString());
118    return styleElement.release();
119}
120
121ApplyStyleCommand::ApplyStyleCommand(Document* document, const EditingStyle* style, EditAction editingAction, EPropertyLevel propertyLevel)
122    : CompositeEditCommand(document)
123    , m_style(style->copy())
124    , m_editingAction(editingAction)
125    , m_propertyLevel(propertyLevel)
126    , m_start(endingSelection().start().downstream())
127    , m_end(endingSelection().end().upstream())
128    , m_useEndingSelection(true)
129    , m_styledInlineElement(0)
130    , m_removeOnly(false)
131    , m_isInlineElementToRemoveFunction(0)
132{
133}
134
135ApplyStyleCommand::ApplyStyleCommand(Document* document, const EditingStyle* style, const Position& start, const Position& end, EditAction editingAction, EPropertyLevel propertyLevel)
136    : CompositeEditCommand(document)
137    , m_style(style->copy())
138    , m_editingAction(editingAction)
139    , m_propertyLevel(propertyLevel)
140    , m_start(start)
141    , m_end(end)
142    , m_useEndingSelection(false)
143    , m_styledInlineElement(0)
144    , m_removeOnly(false)
145    , m_isInlineElementToRemoveFunction(0)
146{
147}
148
149ApplyStyleCommand::ApplyStyleCommand(PassRefPtr<Element> element, bool removeOnly, EditAction editingAction)
150    : CompositeEditCommand(element->document())
151    , m_style(EditingStyle::create())
152    , m_editingAction(editingAction)
153    , m_propertyLevel(PropertyDefault)
154    , m_start(endingSelection().start().downstream())
155    , m_end(endingSelection().end().upstream())
156    , m_useEndingSelection(true)
157    , m_styledInlineElement(element)
158    , m_removeOnly(removeOnly)
159    , m_isInlineElementToRemoveFunction(0)
160{
161}
162
163ApplyStyleCommand::ApplyStyleCommand(Document* document, const EditingStyle* style, IsInlineElementToRemoveFunction isInlineElementToRemoveFunction, EditAction editingAction)
164    : CompositeEditCommand(document)
165    , m_style(style->copy())
166    , m_editingAction(editingAction)
167    , m_propertyLevel(PropertyDefault)
168    , m_start(endingSelection().start().downstream())
169    , m_end(endingSelection().end().upstream())
170    , m_useEndingSelection(true)
171    , m_styledInlineElement(0)
172    , m_removeOnly(true)
173    , m_isInlineElementToRemoveFunction(isInlineElementToRemoveFunction)
174{
175}
176
177void ApplyStyleCommand::updateStartEnd(const Position& newStart, const Position& newEnd)
178{
179    ASSERT(comparePositions(newEnd, newStart) >= 0);
180
181    if (!m_useEndingSelection && (newStart != m_start || newEnd != m_end))
182        m_useEndingSelection = true;
183
184    setEndingSelection(VisibleSelection(newStart, newEnd, VP_DEFAULT_AFFINITY));
185    m_start = newStart;
186    m_end = newEnd;
187}
188
189Position ApplyStyleCommand::startPosition()
190{
191    if (m_useEndingSelection)
192        return endingSelection().start();
193
194    return m_start;
195}
196
197Position ApplyStyleCommand::endPosition()
198{
199    if (m_useEndingSelection)
200        return endingSelection().end();
201
202    return m_end;
203}
204
205void ApplyStyleCommand::doApply()
206{
207    switch (m_propertyLevel) {
208    case PropertyDefault: {
209        // Apply the block-centric properties of the style.
210        RefPtr<EditingStyle> blockStyle = m_style->extractAndRemoveBlockProperties();
211        if (!blockStyle->isEmpty())
212            applyBlockStyle(blockStyle.get());
213        // Apply any remaining styles to the inline elements.
214        if (!m_style->isEmpty() || m_styledInlineElement || m_isInlineElementToRemoveFunction) {
215            applyRelativeFontStyleChange(m_style.get());
216            applyInlineStyle(m_style.get());
217        }
218        break;
219    }
220    case ForceBlockProperties:
221        // Force all properties to be applied as block styles.
222        applyBlockStyle(m_style.get());
223        break;
224    }
225}
226
227EditAction ApplyStyleCommand::editingAction() const
228{
229    return m_editingAction;
230}
231
232void ApplyStyleCommand::applyBlockStyle(EditingStyle *style)
233{
234    // update document layout once before removing styles
235    // so that we avoid the expense of updating before each and every call
236    // to check a computed style
237    updateLayout();
238
239    // get positions we want to use for applying style
240    Position start = startPosition();
241    Position end = endPosition();
242    if (comparePositions(end, start) < 0) {
243        Position swap = start;
244        start = end;
245        end = swap;
246    }
247
248    VisiblePosition visibleStart(start);
249    VisiblePosition visibleEnd(end);
250
251    if (visibleStart.isNull() || visibleStart.isOrphan() || visibleEnd.isNull() || visibleEnd.isOrphan())
252        return;
253
254    // Save and restore the selection endpoints using their indices in the document, since
255    // addBlockStyleIfNeeded may moveParagraphs, which can remove these endpoints.
256    // Calculate start and end indices from the start of the tree that they're in.
257    Node* scope = highestAncestor(visibleStart.deepEquivalent().deprecatedNode());
258    RefPtr<Range> startRange = Range::create(document(), firstPositionInNode(scope), visibleStart.deepEquivalent().parentAnchoredEquivalent());
259    RefPtr<Range> endRange = Range::create(document(), firstPositionInNode(scope), visibleEnd.deepEquivalent().parentAnchoredEquivalent());
260    int startIndex = TextIterator::rangeLength(startRange.get(), true);
261    int endIndex = TextIterator::rangeLength(endRange.get(), true);
262
263    VisiblePosition paragraphStart(startOfParagraph(visibleStart));
264    VisiblePosition nextParagraphStart(endOfParagraph(paragraphStart).next());
265    VisiblePosition beyondEnd(endOfParagraph(visibleEnd).next());
266    while (paragraphStart.isNotNull() && paragraphStart != beyondEnd) {
267        StyleChange styleChange(style, paragraphStart.deepEquivalent());
268        if (styleChange.cssStyle().length() || m_removeOnly) {
269            RefPtr<Node> block = enclosingBlock(paragraphStart.deepEquivalent().deprecatedNode());
270            if (!m_removeOnly) {
271                RefPtr<Node> newBlock = moveParagraphContentsToNewBlockIfNecessary(paragraphStart.deepEquivalent());
272                if (newBlock)
273                    block = newBlock;
274            }
275            ASSERT(block->isHTMLElement());
276            if (block->isHTMLElement()) {
277                removeCSSStyle(style, toHTMLElement(block.get()));
278                if (!m_removeOnly)
279                    addBlockStyle(styleChange, toHTMLElement(block.get()));
280            }
281
282            if (nextParagraphStart.isOrphan())
283                nextParagraphStart = endOfParagraph(paragraphStart).next();
284        }
285
286        paragraphStart = nextParagraphStart;
287        nextParagraphStart = endOfParagraph(paragraphStart).next();
288    }
289
290    startRange = TextIterator::rangeFromLocationAndLength(static_cast<Element*>(scope), startIndex, 0, true);
291    endRange = TextIterator::rangeFromLocationAndLength(static_cast<Element*>(scope), endIndex, 0, true);
292    if (startRange && endRange)
293        updateStartEnd(startRange->startPosition(), endRange->startPosition());
294}
295
296void ApplyStyleCommand::applyRelativeFontStyleChange(EditingStyle* style)
297{
298    static const float MinimumFontSize = 0.1f;
299
300    if (!style || !style->hasFontSizeDelta())
301        return;
302
303    Position start = startPosition();
304    Position end = endPosition();
305    if (comparePositions(end, start) < 0) {
306        Position swap = start;
307        start = end;
308        end = swap;
309    }
310
311    // Join up any adjacent text nodes.
312    if (start.deprecatedNode()->isTextNode()) {
313        joinChildTextNodes(start.deprecatedNode()->parentNode(), start, end);
314        start = startPosition();
315        end = endPosition();
316    }
317    if (end.deprecatedNode()->isTextNode() && start.deprecatedNode()->parentNode() != end.deprecatedNode()->parentNode()) {
318        joinChildTextNodes(end.deprecatedNode()->parentNode(), start, end);
319        start = startPosition();
320        end = endPosition();
321    }
322
323    // Split the start text nodes if needed to apply style.
324    if (isValidCaretPositionInTextNode(start)) {
325        splitTextAtStart(start, end);
326        start = startPosition();
327        end = endPosition();
328    }
329
330    if (isValidCaretPositionInTextNode(end)) {
331        splitTextAtEnd(start, end);
332        start = startPosition();
333        end = endPosition();
334    }
335
336    // Calculate loop end point.
337    // If the end node is before the start node (can only happen if the end node is
338    // an ancestor of the start node), we gather nodes up to the next sibling of the end node
339    Node *beyondEnd;
340    if (start.deprecatedNode()->isDescendantOf(end.deprecatedNode()))
341        beyondEnd = end.deprecatedNode()->traverseNextSibling();
342    else
343        beyondEnd = end.deprecatedNode()->traverseNextNode();
344
345    start = start.upstream(); // Move upstream to ensure we do not add redundant spans.
346    Node* startNode = start.deprecatedNode();
347    if (startNode->isTextNode() && start.deprecatedEditingOffset() >= caretMaxOffset(startNode)) // Move out of text node if range does not include its characters.
348        startNode = startNode->traverseNextNode();
349
350    // Store away font size before making any changes to the document.
351    // This ensures that changes to one node won't effect another.
352    HashMap<Node*, float> startingFontSizes;
353    for (Node *node = startNode; node != beyondEnd; node = node->traverseNextNode())
354        startingFontSizes.set(node, computedFontSize(node));
355
356    // These spans were added by us. If empty after font size changes, they can be removed.
357    Vector<RefPtr<HTMLElement> > unstyledSpans;
358
359    Node* lastStyledNode = 0;
360    for (Node* node = startNode; node != beyondEnd; node = node->traverseNextNode()) {
361        RefPtr<HTMLElement> element;
362        if (node->isHTMLElement()) {
363            // Only work on fully selected nodes.
364            if (!nodeFullySelected(node, start, end))
365                continue;
366            element = toHTMLElement(node);
367        } else if (node->isTextNode() && node->renderer() && node->parentNode() != lastStyledNode) {
368            // Last styled node was not parent node of this text node, but we wish to style this
369            // text node. To make this possible, add a style span to surround this text node.
370            RefPtr<HTMLElement> span = createStyleSpanElement(document());
371            surroundNodeRangeWithElement(node, node, span.get());
372            element = span.release();
373        }  else {
374            // Only handle HTML elements and text nodes.
375            continue;
376        }
377        lastStyledNode = node;
378
379        CSSMutableStyleDeclaration* inlineStyleDecl = element->getInlineStyleDecl();
380        float currentFontSize = computedFontSize(node);
381        float desiredFontSize = max(MinimumFontSize, startingFontSizes.get(node) + style->fontSizeDelta());
382        RefPtr<CSSValue> value = inlineStyleDecl->getPropertyCSSValue(CSSPropertyFontSize);
383        if (value) {
384            inlineStyleDecl->removeProperty(CSSPropertyFontSize, true);
385            currentFontSize = computedFontSize(node);
386        }
387        if (currentFontSize != desiredFontSize) {
388            inlineStyleDecl->setProperty(CSSPropertyFontSize, String::number(desiredFontSize) + "px", false, false);
389            setNodeAttribute(element.get(), styleAttr, inlineStyleDecl->cssText());
390        }
391        if (inlineStyleDecl->isEmpty()) {
392            removeNodeAttribute(element.get(), styleAttr);
393            // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan?  Need a test.
394            if (isUnstyledStyleSpan(element.get()))
395                unstyledSpans.append(element.release());
396        }
397    }
398
399    size_t size = unstyledSpans.size();
400    for (size_t i = 0; i < size; ++i)
401        removeNodePreservingChildren(unstyledSpans[i].get());
402}
403
404static Node* dummySpanAncestorForNode(const Node* node)
405{
406    while (node && !isStyleSpan(node))
407        node = node->parentNode();
408
409    return node ? node->parentNode() : 0;
410}
411
412void ApplyStyleCommand::cleanupUnstyledAppleStyleSpans(Node* dummySpanAncestor)
413{
414    if (!dummySpanAncestor)
415        return;
416
417    // Dummy spans are created when text node is split, so that style information
418    // can be propagated, which can result in more splitting. If a dummy span gets
419    // cloned/split, the new node is always a sibling of it. Therefore, we scan
420    // all the children of the dummy's parent
421    Node* next;
422    for (Node* node = dummySpanAncestor->firstChild(); node; node = next) {
423        next = node->nextSibling();
424        if (isUnstyledStyleSpan(node))
425            removeNodePreservingChildren(node);
426        node = next;
427    }
428}
429
430HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool before, WritingDirection allowedDirection)
431{
432    // We are allowed to leave the highest ancestor with unicode-bidi unsplit if it is unicode-bidi: embed and direction: allowedDirection.
433    // In that case, we return the unsplit ancestor. Otherwise, we return 0.
434    Node* block = enclosingBlock(node);
435    if (!block)
436        return 0;
437
438    Node* highestAncestorWithUnicodeBidi = 0;
439    Node* nextHighestAncestorWithUnicodeBidi = 0;
440    int highestAncestorUnicodeBidi = 0;
441    for (Node* n = node->parentNode(); n != block; n = n->parentNode()) {
442        int unicodeBidi = getIdentifierValue(computedStyle(n).get(), CSSPropertyUnicodeBidi);
443        if (unicodeBidi && unicodeBidi != CSSValueNormal) {
444            highestAncestorUnicodeBidi = unicodeBidi;
445            nextHighestAncestorWithUnicodeBidi = highestAncestorWithUnicodeBidi;
446            highestAncestorWithUnicodeBidi = n;
447        }
448    }
449
450    if (!highestAncestorWithUnicodeBidi)
451        return 0;
452
453    HTMLElement* unsplitAncestor = 0;
454
455    WritingDirection highestAncestorDirection;
456    if (allowedDirection != NaturalWritingDirection
457        && highestAncestorUnicodeBidi != CSSValueBidiOverride
458        && highestAncestorWithUnicodeBidi->isHTMLElement()
459        && EditingStyle::create(highestAncestorWithUnicodeBidi, EditingStyle::AllProperties)->textDirection(highestAncestorDirection)
460        && highestAncestorDirection == allowedDirection) {
461        if (!nextHighestAncestorWithUnicodeBidi)
462            return toHTMLElement(highestAncestorWithUnicodeBidi);
463
464        unsplitAncestor = toHTMLElement(highestAncestorWithUnicodeBidi);
465        highestAncestorWithUnicodeBidi = nextHighestAncestorWithUnicodeBidi;
466    }
467
468    // Split every ancestor through highest ancestor with embedding.
469    Node* n = node;
470    while (true) {
471        Element* parent = static_cast<Element*>(n->parentNode());
472        if (before ? n->previousSibling() : n->nextSibling())
473            splitElement(parent, before ? n : n->nextSibling());
474        if (parent == highestAncestorWithUnicodeBidi)
475            break;
476        n = n->parentNode();
477    }
478    return unsplitAncestor;
479}
480
481void ApplyStyleCommand::removeEmbeddingUpToEnclosingBlock(Node* node, Node* unsplitAncestor)
482{
483    Node* block = enclosingBlock(node);
484    if (!block)
485        return;
486
487    Node* parent = 0;
488    for (Node* n = node->parentNode(); n != block && n != unsplitAncestor; n = parent) {
489        parent = n->parentNode();
490        if (!n->isStyledElement())
491            continue;
492
493        StyledElement* element = static_cast<StyledElement*>(n);
494        int unicodeBidi = getIdentifierValue(computedStyle(element).get(), CSSPropertyUnicodeBidi);
495        if (!unicodeBidi || unicodeBidi == CSSValueNormal)
496            continue;
497
498        // FIXME: This code should really consider the mapped attribute 'dir', the inline style declaration,
499        // and all matching style rules in order to determine how to best set the unicode-bidi property to 'normal'.
500        // For now, it assumes that if the 'dir' attribute is present, then removing it will suffice, and
501        // otherwise it sets the property in the inline style declaration.
502        if (element->hasAttribute(dirAttr)) {
503            // FIXME: If this is a BDO element, we should probably just remove it if it has no
504            // other attributes, like we (should) do with B and I elements.
505            removeNodeAttribute(element, dirAttr);
506        } else {
507            RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy();
508            inlineStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal);
509            inlineStyle->removeProperty(CSSPropertyDirection);
510            setNodeAttribute(element, styleAttr, inlineStyle->cssText());
511            // FIXME: should this be isSpanWithoutAttributesOrUnstyleStyleSpan?  Need a test.
512            if (isUnstyledStyleSpan(element))
513                removeNodePreservingChildren(element);
514        }
515    }
516}
517
518static Node* highestEmbeddingAncestor(Node* startNode, Node* enclosingNode)
519{
520    for (Node* n = startNode; n && n != enclosingNode; n = n->parentNode()) {
521        if (n->isHTMLElement() && getIdentifierValue(computedStyle(n).get(), CSSPropertyUnicodeBidi) == CSSValueEmbed)
522            return n;
523    }
524
525    return 0;
526}
527
528void ApplyStyleCommand::applyInlineStyle(EditingStyle* style)
529{
530    Node* startDummySpanAncestor = 0;
531    Node* endDummySpanAncestor = 0;
532
533    style->collapseTextDecorationProperties();
534
535    // update document layout once before removing styles
536    // so that we avoid the expense of updating before each and every call
537    // to check a computed style
538    updateLayout();
539
540    // adjust to the positions we want to use for applying style
541    Position start = startPosition();
542    Position end = endPosition();
543    if (comparePositions(end, start) < 0) {
544        Position swap = start;
545        start = end;
546        end = swap;
547    }
548
549    // split the start node and containing element if the selection starts inside of it
550    bool splitStart = isValidCaretPositionInTextNode(start);
551    if (splitStart) {
552        if (shouldSplitTextElement(start.deprecatedNode()->parentElement(), style))
553            splitTextElementAtStart(start, end);
554        else
555            splitTextAtStart(start, end);
556        start = startPosition();
557        end = endPosition();
558        startDummySpanAncestor = dummySpanAncestorForNode(start.deprecatedNode());
559    }
560
561    // split the end node and containing element if the selection ends inside of it
562    bool splitEnd = isValidCaretPositionInTextNode(end);
563    if (splitEnd) {
564        if (shouldSplitTextElement(end.deprecatedNode()->parentElement(), style))
565            splitTextElementAtEnd(start, end);
566        else
567            splitTextAtEnd(start, end);
568        start = startPosition();
569        end = endPosition();
570        endDummySpanAncestor = dummySpanAncestorForNode(end.deprecatedNode());
571    }
572
573    // Remove style from the selection.
574    // Use the upstream position of the start for removing style.
575    // This will ensure we remove all traces of the relevant styles from the selection
576    // and prevent us from adding redundant ones, as described in:
577    // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags
578    Position removeStart = start.upstream();
579    WritingDirection textDirection = NaturalWritingDirection;
580    bool hasTextDirection = style->textDirection(textDirection);
581    RefPtr<EditingStyle> styleWithoutEmbedding;
582    RefPtr<EditingStyle> embeddingStyle;
583    if (hasTextDirection) {
584        // Leave alone an ancestor that provides the desired single level embedding, if there is one.
585        HTMLElement* startUnsplitAncestor = splitAncestorsWithUnicodeBidi(start.deprecatedNode(), true, textDirection);
586        HTMLElement* endUnsplitAncestor = splitAncestorsWithUnicodeBidi(end.deprecatedNode(), false, textDirection);
587        removeEmbeddingUpToEnclosingBlock(start.deprecatedNode(), startUnsplitAncestor);
588        removeEmbeddingUpToEnclosingBlock(end.deprecatedNode(), endUnsplitAncestor);
589
590        // Avoid removing the dir attribute and the unicode-bidi and direction properties from the unsplit ancestors.
591        Position embeddingRemoveStart = removeStart;
592        if (startUnsplitAncestor && nodeFullySelected(startUnsplitAncestor, removeStart, end))
593            embeddingRemoveStart = positionInParentAfterNode(startUnsplitAncestor);
594
595        Position embeddingRemoveEnd = end;
596        if (endUnsplitAncestor && nodeFullySelected(endUnsplitAncestor, removeStart, end))
597            embeddingRemoveEnd = positionInParentBeforeNode(endUnsplitAncestor).downstream();
598
599        if (embeddingRemoveEnd != removeStart || embeddingRemoveEnd != end) {
600            styleWithoutEmbedding = style->copy();
601            embeddingStyle = styleWithoutEmbedding->extractAndRemoveTextDirection();
602
603            if (comparePositions(embeddingRemoveStart, embeddingRemoveEnd) <= 0)
604                removeInlineStyle(embeddingStyle.get(), embeddingRemoveStart, embeddingRemoveEnd);
605        }
606    }
607
608    removeInlineStyle(styleWithoutEmbedding ? styleWithoutEmbedding.get() : style, removeStart, end);
609    start = startPosition();
610    end = endPosition();
611    if (start.isNull() || start.isOrphan() || end.isNull() || end.isOrphan())
612        return;
613
614    if (splitStart && mergeStartWithPreviousIfIdentical(start, end)) {
615        start = startPosition();
616        end = endPosition();
617    }
618
619    if (splitEnd) {
620        mergeEndWithNextIfIdentical(start, end);
621        start = startPosition();
622        end = endPosition();
623    }
624
625    // update document layout once before running the rest of the function
626    // so that we avoid the expense of updating before each and every call
627    // to check a computed style
628    updateLayout();
629
630    RefPtr<EditingStyle> styleToApply = style;
631    if (hasTextDirection) {
632        // Avoid applying the unicode-bidi and direction properties beneath ancestors that already have them.
633        Node* embeddingStartNode = highestEmbeddingAncestor(start.deprecatedNode(), enclosingBlock(start.deprecatedNode()));
634        Node* embeddingEndNode = highestEmbeddingAncestor(end.deprecatedNode(), enclosingBlock(end.deprecatedNode()));
635
636        if (embeddingStartNode || embeddingEndNode) {
637            Position embeddingApplyStart = embeddingStartNode ? positionInParentAfterNode(embeddingStartNode) : start;
638            Position embeddingApplyEnd = embeddingEndNode ? positionInParentBeforeNode(embeddingEndNode) : end;
639            ASSERT(embeddingApplyStart.isNotNull() && embeddingApplyEnd.isNotNull());
640
641            if (!embeddingStyle) {
642                styleWithoutEmbedding = style->copy();
643                embeddingStyle = styleWithoutEmbedding->extractAndRemoveTextDirection();
644            }
645            fixRangeAndApplyInlineStyle(embeddingStyle.get(), embeddingApplyStart, embeddingApplyEnd);
646
647            styleToApply = styleWithoutEmbedding;
648        }
649    }
650
651    fixRangeAndApplyInlineStyle(styleToApply.get(), start, end);
652
653    // Remove dummy style spans created by splitting text elements.
654    cleanupUnstyledAppleStyleSpans(startDummySpanAncestor);
655    if (endDummySpanAncestor != startDummySpanAncestor)
656        cleanupUnstyledAppleStyleSpans(endDummySpanAncestor);
657}
658
659void ApplyStyleCommand::fixRangeAndApplyInlineStyle(EditingStyle* style, const Position& start, const Position& end)
660{
661    Node* startNode = start.deprecatedNode();
662
663    if (start.deprecatedEditingOffset() >= caretMaxOffset(start.deprecatedNode())) {
664        startNode = startNode->traverseNextNode();
665        if (!startNode || comparePositions(end, firstPositionInOrBeforeNode(startNode)) < 0)
666            return;
667    }
668
669    Node* pastEndNode = end.deprecatedNode();
670    if (end.deprecatedEditingOffset() >= caretMaxOffset(end.deprecatedNode()))
671        pastEndNode = end.deprecatedNode()->traverseNextSibling();
672
673    // FIXME: Callers should perform this operation on a Range that includes the br
674    // if they want style applied to the empty line.
675    if (start == end && start.deprecatedNode()->hasTagName(brTag))
676        pastEndNode = start.deprecatedNode()->traverseNextNode();
677
678    // Start from the highest fully selected ancestor so that we can modify the fully selected node.
679    // e.g. When applying font-size: large on <font color="blue">hello</font>, we need to include the font element in our run
680    // to generate <font color="blue" size="4">hello</font> instead of <font color="blue"><font size="4">hello</font></font>
681    RefPtr<Range> range = Range::create(startNode->document(), start, end);
682    Element* editableRoot = startNode->rootEditableElement();
683    if (startNode != editableRoot) {
684        while (editableRoot && startNode->parentNode() != editableRoot && isNodeVisiblyContainedWithin(startNode->parentNode(), range.get()))
685            startNode = startNode->parentNode();
686    }
687
688    applyInlineStyleToNodeRange(style, startNode, pastEndNode);
689}
690
691static bool containsNonEditableRegion(Node* node)
692{
693    if (!node->rendererIsEditable())
694        return true;
695
696    Node* sibling = node->traverseNextSibling();
697    for (Node* descendent = node->firstChild(); descendent && descendent != sibling; descendent = descendent->traverseNextNode()) {
698        if (!descendent->rendererIsEditable())
699            return true;
700    }
701
702    return false;
703}
704
705void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, Node* node, Node* pastEndNode)
706{
707    if (m_removeOnly)
708        return;
709
710    for (RefPtr<Node> next; node && node != pastEndNode; node = next.get()) {
711        next = node->traverseNextNode();
712
713        if (!node->renderer() || !node->rendererIsEditable())
714            continue;
715
716        if (!node->rendererIsRichlyEditable() && node->isHTMLElement()) {
717            // This is a plaintext-only region. Only proceed if it's fully selected.
718            // pastEndNode is the node after the last fully selected node, so if it's inside node then
719            // node isn't fully selected.
720            if (pastEndNode && pastEndNode->isDescendantOf(node))
721                break;
722            // Add to this element's inline style and skip over its contents.
723            HTMLElement* element = toHTMLElement(node);
724            RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy();
725            inlineStyle->merge(style->style());
726            setNodeAttribute(element, styleAttr, inlineStyle->cssText());
727            next = node->traverseNextSibling();
728            continue;
729        }
730
731        if (isBlock(node))
732            continue;
733
734        if (node->childNodeCount()) {
735            if (node->contains(pastEndNode) || containsNonEditableRegion(node) || !node->parentNode()->rendererIsEditable())
736                continue;
737            if (editingIgnoresContent(node)) {
738                next = node->traverseNextSibling();
739                continue;
740            }
741        }
742
743        RefPtr<Node> runStart = node;
744        RefPtr<Node> runEnd = node;
745        Node* sibling = node->nextSibling();
746        while (sibling && sibling != pastEndNode && !sibling->contains(pastEndNode)
747               && (!isBlock(sibling) || sibling->hasTagName(brTag))
748               && !containsNonEditableRegion(sibling)) {
749            runEnd = sibling;
750            sibling = runEnd->nextSibling();
751        }
752        next = runEnd->traverseNextSibling();
753
754        if (!removeStyleFromRunBeforeApplyingStyle(style, runStart, runEnd))
755            continue;
756        addInlineStyleIfNeeded(style, runStart.get(), runEnd.get(), AddStyledElement);
757    }
758}
759
760bool ApplyStyleCommand::isStyledInlineElementToRemove(Element* element) const
761{
762    return (m_styledInlineElement && element->hasTagName(m_styledInlineElement->tagQName()))
763        || (m_isInlineElementToRemoveFunction && m_isInlineElementToRemoveFunction(element));
764}
765
766bool ApplyStyleCommand::removeStyleFromRunBeforeApplyingStyle(EditingStyle* style, RefPtr<Node>& runStart, RefPtr<Node>& runEnd)
767{
768    ASSERT(runStart && runEnd && runStart->parentNode() == runEnd->parentNode());
769    RefPtr<Node> pastEndNode = runEnd->traverseNextSibling();
770    bool needToApplyStyle = false;
771    for (Node* node = runStart.get(); node && node != pastEndNode.get(); node = node->traverseNextNode()) {
772        if (node->childNodeCount())
773            continue;
774        // We don't consider m_isInlineElementToRemoveFunction here because we never apply style when m_isInlineElementToRemoveFunction is specified
775        if (!style->styleIsPresentInComputedStyleOfNode(node)
776            || (m_styledInlineElement && !enclosingNodeWithTag(positionBeforeNode(node), m_styledInlineElement->tagQName()))) {
777            needToApplyStyle = true;
778            break;
779        }
780    }
781    if (!needToApplyStyle)
782        return false;
783
784    RefPtr<Node> next = runStart;
785    for (RefPtr<Node> node = next; node && node->inDocument() && node != pastEndNode; node = next) {
786        next = node->traverseNextNode();
787        if (!node->isHTMLElement())
788            continue;
789
790        RefPtr<Node> previousSibling = node->previousSibling();
791        RefPtr<Node> nextSibling = node->nextSibling();
792        RefPtr<ContainerNode> parent = node->parentNode();
793        removeInlineStyleFromElement(style, toHTMLElement(node.get()), RemoveAlways);
794        if (!node->inDocument()) {
795            // FIXME: We might need to update the start and the end of current selection here but need a test.
796            if (runStart == node)
797                runStart = previousSibling ? previousSibling->nextSibling() : parent->firstChild();
798            if (runEnd == node)
799                runEnd = nextSibling ? nextSibling->previousSibling() : parent->lastChild();
800        }
801    }
802
803    return true;
804}
805
806bool ApplyStyleCommand::removeInlineStyleFromElement(EditingStyle* style, PassRefPtr<HTMLElement> element, InlineStyleRemovalMode mode, EditingStyle* extractedStyle)
807{
808    ASSERT(element);
809
810    if (!element->parentNode() || !element->parentNode()->rendererIsEditable())
811        return false;
812
813    if (isStyledInlineElementToRemove(element.get())) {
814        if (mode == RemoveNone)
815            return true;
816        ASSERT(extractedStyle);
817        extractedStyle->mergeInlineStyleOfElement(element.get());
818        removeNodePreservingChildren(element);
819        return true;
820    }
821
822    bool removed = false;
823    if (removeImplicitlyStyledElement(style, element.get(), mode, extractedStyle))
824        removed = true;
825
826    if (!element->inDocument())
827        return removed;
828
829    // If the node was converted to a span, the span may still contain relevant
830    // styles which must be removed (e.g. <b style='font-weight: bold'>)
831    if (removeCSSStyle(style, element.get(), mode, extractedStyle))
832        removed = true;
833
834    return removed;
835}
836
837void ApplyStyleCommand::replaceWithSpanOrRemoveIfWithoutAttributes(HTMLElement*& elem)
838{
839    bool removeNode = false;
840
841    // Similar to isSpanWithoutAttributesOrUnstyleStyleSpan, but does not look for Apple-style-span.
842    NamedNodeMap* attributes = elem->attributes(true); // readonly
843    if (!attributes || attributes->isEmpty())
844        removeNode = true;
845    else if (attributes->length() == 1 && elem->hasAttribute(styleAttr)) {
846        // Remove the element even if it has just style='' (this might be redundantly checked later too)
847        CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl();
848        if (!inlineStyleDecl || inlineStyleDecl->isEmpty())
849            removeNode = true;
850    }
851
852    if (removeNode)
853        removeNodePreservingChildren(elem);
854    else {
855        HTMLElement* newSpanElement = replaceElementWithSpanPreservingChildrenAndAttributes(elem);
856        ASSERT(newSpanElement && newSpanElement->inDocument());
857        elem = newSpanElement;
858    }
859}
860
861bool ApplyStyleCommand::removeImplicitlyStyledElement(EditingStyle* style, HTMLElement* element, InlineStyleRemovalMode mode, EditingStyle* extractedStyle)
862{
863    ASSERT(style);
864    if (mode == RemoveNone) {
865        ASSERT(!extractedStyle);
866        return style->conflictsWithImplicitStyleOfElement(element) || style->conflictsWithImplicitStyleOfAttributes(element);
867    }
868
869    ASSERT(mode == RemoveIfNeeded || mode == RemoveAlways);
870    if (style->conflictsWithImplicitStyleOfElement(element, extractedStyle, mode == RemoveAlways ? EditingStyle::ExtractMatchingStyle : EditingStyle::DoNotExtractMatchingStyle)) {
871        replaceWithSpanOrRemoveIfWithoutAttributes(element);
872        return true;
873    }
874
875    // unicode-bidi and direction are pushed down separately so don't push down with other styles
876    Vector<QualifiedName> attributes;
877    if (!style->extractConflictingImplicitStyleOfAttributes(element, extractedStyle ? EditingStyle::PreserveWritingDirection : EditingStyle::DoNotPreserveWritingDirection,
878        extractedStyle, attributes, mode == RemoveAlways ? EditingStyle::ExtractMatchingStyle : EditingStyle::DoNotExtractMatchingStyle))
879        return false;
880
881    for (size_t i = 0; i < attributes.size(); i++)
882        removeNodeAttribute(element, attributes[i]);
883
884    if (isEmptyFontTag(element) || isSpanWithoutAttributesOrUnstyleStyleSpan(element))
885        removeNodePreservingChildren(element);
886
887    return true;
888}
889
890bool ApplyStyleCommand::removeCSSStyle(EditingStyle* style, HTMLElement* element, InlineStyleRemovalMode mode, EditingStyle* extractedStyle)
891{
892    ASSERT(style);
893    ASSERT(element);
894
895    if (mode == RemoveNone)
896        return style->conflictsWithInlineStyleOfElement(element);
897
898    Vector<CSSPropertyID> properties;
899    if (!style->conflictsWithInlineStyleOfElement(element, extractedStyle, properties))
900        return false;
901
902    CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl();
903    ASSERT(inlineStyle);
904    // FIXME: We should use a mass-removal function here but we don't have an undoable one yet.
905    for (size_t i = 0; i < properties.size(); i++)
906        removeCSSProperty(element, properties[i]);
907
908    // No need to serialize <foo style=""> if we just removed the last css property
909    if (inlineStyle->isEmpty())
910        removeNodeAttribute(element, styleAttr);
911
912    if (isSpanWithoutAttributesOrUnstyleStyleSpan(element))
913        removeNodePreservingChildren(element);
914
915    return true;
916}
917
918HTMLElement* ApplyStyleCommand::highestAncestorWithConflictingInlineStyle(EditingStyle* style, Node* node)
919{
920    if (!node)
921        return 0;
922
923    HTMLElement* result = 0;
924    Node* unsplittableElement = unsplittableElementForPosition(firstPositionInOrBeforeNode(node));
925
926    for (Node *n = node; n; n = n->parentNode()) {
927        if (n->isHTMLElement() && shouldRemoveInlineStyleFromElement(style, toHTMLElement(n)))
928            result = toHTMLElement(n);
929        // Should stop at the editable root (cannot cross editing boundary) and
930        // also stop at the unsplittable element to be consistent with other UAs
931        if (n == unsplittableElement)
932            break;
933    }
934
935    return result;
936}
937
938void ApplyStyleCommand::applyInlineStyleToPushDown(Node* node, EditingStyle* style)
939{
940    ASSERT(node);
941
942    if (!style || style->isEmpty() || !node->renderer())
943        return;
944
945    RefPtr<EditingStyle> newInlineStyle = style;
946    if (node->isHTMLElement() && static_cast<HTMLElement*>(node)->inlineStyleDecl()) {
947        newInlineStyle = style->copy();
948        newInlineStyle->mergeInlineStyleOfElement(static_cast<HTMLElement*>(node));
949    }
950
951    // Since addInlineStyleIfNeeded can't add styles to block-flow render objects, add style attribute instead.
952    // FIXME: applyInlineStyleToRange should be used here instead.
953    if ((node->renderer()->isBlockFlow() || node->childNodeCount()) && node->isHTMLElement()) {
954        setNodeAttribute(toHTMLElement(node), styleAttr, newInlineStyle->style()->cssText());
955        return;
956    }
957
958    if (node->renderer()->isText() && static_cast<RenderText*>(node->renderer())->isAllCollapsibleWhitespace())
959        return;
960
961    // We can't wrap node with the styled element here because new styled element will never be removed if we did.
962    // If we modified the child pointer in pushDownInlineStyleAroundNode to point to new style element
963    // then we fall into an infinite loop where we keep removing and adding styled element wrapping node.
964    addInlineStyleIfNeeded(newInlineStyle.get(), node, node, DoNotAddStyledElement);
965}
966
967void ApplyStyleCommand::pushDownInlineStyleAroundNode(EditingStyle* style, Node* targetNode)
968{
969    HTMLElement* highestAncestor = highestAncestorWithConflictingInlineStyle(style, targetNode);
970    if (!highestAncestor)
971        return;
972
973    // The outer loop is traversing the tree vertically from highestAncestor to targetNode
974    Node* current = highestAncestor;
975    // Along the way, styled elements that contain targetNode are removed and accumulated into elementsToPushDown.
976    // Each child of the removed element, exclusing ancestors of targetNode, is then wrapped by clones of elements in elementsToPushDown.
977    Vector<RefPtr<Element> > elementsToPushDown;
978    while (current != targetNode) {
979        ASSERT(current);
980        ASSERT(current->contains(targetNode));
981        Node* child = current->firstChild();
982        Node* lastChild = current->lastChild();
983        RefPtr<StyledElement> styledElement;
984        if (current->isStyledElement() && isStyledInlineElementToRemove(static_cast<Element*>(current))) {
985            styledElement = static_cast<StyledElement*>(current);
986            elementsToPushDown.append(styledElement);
987        }
988
989        RefPtr<EditingStyle> styleToPushDown = EditingStyle::create();
990        if (current->isHTMLElement())
991            removeInlineStyleFromElement(style, toHTMLElement(current), RemoveIfNeeded, styleToPushDown.get());
992
993        // The inner loop will go through children on each level
994        // FIXME: we should aggregate inline child elements together so that we don't wrap each child separately.
995        while (child) {
996            Node* nextChild = child->nextSibling();
997
998            if (!child->contains(targetNode) && elementsToPushDown.size()) {
999                for (size_t i = 0; i < elementsToPushDown.size(); i++) {
1000                    RefPtr<Element> wrapper = elementsToPushDown[i]->cloneElementWithoutChildren();
1001                    ExceptionCode ec = 0;
1002                    wrapper->removeAttribute(styleAttr, ec);
1003                    ASSERT(!ec);
1004                    surroundNodeRangeWithElement(child, child, wrapper);
1005                }
1006            }
1007
1008            // Apply text decoration to all nodes containing targetNode and their siblings but NOT to targetNode
1009            // But if we've removed styledElement then go ahead and always apply the style.
1010            if (child != targetNode || styledElement)
1011                applyInlineStyleToPushDown(child, styleToPushDown.get());
1012
1013            // We found the next node for the outer loop (contains targetNode)
1014            // When reached targetNode, stop the outer loop upon the completion of the current inner loop
1015            if (child == targetNode || child->contains(targetNode))
1016                current = child;
1017
1018            if (child == lastChild || child->contains(lastChild))
1019                break;
1020            child = nextChild;
1021        }
1022    }
1023}
1024
1025void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position &start, const Position &end)
1026{
1027    ASSERT(start.isNotNull());
1028    ASSERT(end.isNotNull());
1029    ASSERT(start.anchorNode()->inDocument());
1030    ASSERT(end.anchorNode()->inDocument());
1031    ASSERT(comparePositions(start, end) <= 0);
1032
1033    Position pushDownStart = start.downstream();
1034    // If the pushDownStart is at the end of a text node, then this node is not fully selected.
1035    // Move it to the next deep quivalent position to avoid removing the style from this node.
1036    // e.g. if pushDownStart was at Position("hello", 5) in <b>hello<div>world</div></b>, we want Position("world", 0) instead.
1037    Node* pushDownStartContainer = pushDownStart.containerNode();
1038    if (pushDownStartContainer && pushDownStartContainer->isTextNode()
1039        && pushDownStart.computeOffsetInContainerNode() == pushDownStartContainer->maxCharacterOffset())
1040        pushDownStart = nextVisuallyDistinctCandidate(pushDownStart);
1041    Position pushDownEnd = end.upstream();
1042
1043    pushDownInlineStyleAroundNode(style, pushDownStart.deprecatedNode());
1044    pushDownInlineStyleAroundNode(style, pushDownEnd.deprecatedNode());
1045
1046    // The s and e variables store the positions used to set the ending selection after style removal
1047    // takes place. This will help callers to recognize when either the start node or the end node
1048    // are removed from the document during the work of this function.
1049    // If pushDownInlineStyleAroundNode has pruned start.deprecatedNode() or end.deprecatedNode(),
1050    // use pushDownStart or pushDownEnd instead, which pushDownInlineStyleAroundNode won't prune.
1051    Position s = start.isNull() || start.isOrphan() ? pushDownStart : start;
1052    Position e = end.isNull() || end.isOrphan() ? pushDownEnd : end;
1053
1054    Node* node = start.deprecatedNode();
1055    while (node) {
1056        RefPtr<Node> next = node->traverseNextNode();
1057        if (node->isHTMLElement() && nodeFullySelected(node, start, end)) {
1058            RefPtr<HTMLElement> elem = toHTMLElement(node);
1059            RefPtr<Node> prev = elem->traversePreviousNodePostOrder();
1060            RefPtr<Node> next = elem->traverseNextNode();
1061            RefPtr<EditingStyle> styleToPushDown;
1062            PassRefPtr<Node> childNode = 0;
1063            if (isStyledInlineElementToRemove(elem.get())) {
1064                styleToPushDown = EditingStyle::create();
1065                childNode = elem->firstChild();
1066            }
1067
1068            removeInlineStyleFromElement(style, elem.get(), RemoveIfNeeded, styleToPushDown.get());
1069            if (!elem->inDocument()) {
1070                if (s.deprecatedNode() == elem) {
1071                    // Since elem must have been fully selected, and it is at the start
1072                    // of the selection, it is clear we can set the new s offset to 0.
1073                    ASSERT(s.anchorType() == Position::PositionIsBeforeAnchor || s.offsetInContainerNode() <= 0);
1074                    s = firstPositionInOrBeforeNode(next.get());
1075                }
1076                if (e.deprecatedNode() == elem) {
1077                    // Since elem must have been fully selected, and it is at the end
1078                    // of the selection, it is clear we can set the new e offset to
1079                    // the max range offset of prev.
1080                    ASSERT(s.anchorType() == Position::PositionIsAfterAnchor
1081                           || s.offsetInContainerNode() >= lastOffsetInNode(s.containerNode()));
1082                    e = lastPositionInOrAfterNode(prev.get());
1083                }
1084            }
1085
1086            if (styleToPushDown) {
1087                for (; childNode; childNode = childNode->nextSibling())
1088                    applyInlineStyleToPushDown(childNode.get(), styleToPushDown.get());
1089            }
1090        }
1091        if (node == end.deprecatedNode())
1092            break;
1093        node = next.get();
1094    }
1095
1096    updateStartEnd(s, e);
1097}
1098
1099bool ApplyStyleCommand::nodeFullySelected(Node *node, const Position &start, const Position &end) const
1100{
1101    ASSERT(node);
1102    ASSERT(node->isElementNode());
1103
1104    return comparePositions(firstPositionInOrBeforeNode(node), start) >= 0
1105        && comparePositions(lastPositionInOrAfterNode(node).upstream(), end) <= 0;
1106}
1107
1108bool ApplyStyleCommand::nodeFullyUnselected(Node *node, const Position &start, const Position &end) const
1109{
1110    ASSERT(node);
1111    ASSERT(node->isElementNode());
1112
1113    bool isFullyBeforeStart = comparePositions(lastPositionInOrAfterNode(node).upstream(), start) < 0;
1114    bool isFullyAfterEnd = comparePositions(firstPositionInOrBeforeNode(node), end) > 0;
1115
1116    return isFullyBeforeStart || isFullyAfterEnd;
1117}
1118
1119void ApplyStyleCommand::splitTextAtStart(const Position& start, const Position& end)
1120{
1121    ASSERT(start.anchorType() == Position::PositionIsOffsetInAnchor);
1122
1123    Position newEnd;
1124    if (end.anchorType() == Position::PositionIsOffsetInAnchor && start.containerNode() == end.containerNode())
1125        newEnd = Position(end.containerNode(), end.offsetInContainerNode() - start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor);
1126    else
1127        newEnd = end;
1128
1129    Text* text = static_cast<Text*>(start.deprecatedNode());
1130    splitTextNode(text, start.offsetInContainerNode());
1131    updateStartEnd(firstPositionInNode(start.deprecatedNode()), newEnd);
1132}
1133
1134void ApplyStyleCommand::splitTextAtEnd(const Position& start, const Position& end)
1135{
1136    ASSERT(end.anchorType() == Position::PositionIsOffsetInAnchor);
1137
1138    bool shouldUpdateStart = start.anchorType() == Position::PositionIsOffsetInAnchor && start.containerNode() == end.containerNode();
1139    Text* text = static_cast<Text *>(end.deprecatedNode());
1140    splitTextNode(text, end.offsetInContainerNode());
1141
1142    Node* prevNode = text->previousSibling();
1143    ASSERT(prevNode);
1144    Position newStart = shouldUpdateStart ? Position(prevNode, start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor) : start;
1145    updateStartEnd(newStart, lastPositionInNode(prevNode));
1146}
1147
1148void ApplyStyleCommand::splitTextElementAtStart(const Position& start, const Position& end)
1149{
1150    ASSERT(start.anchorType() == Position::PositionIsOffsetInAnchor);
1151
1152    Position newEnd;
1153    if (end.anchorType() == Position::PositionIsOffsetInAnchor && start.containerNode() == end.containerNode())
1154        newEnd = Position(end.containerNode(), end.offsetInContainerNode() - start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor);
1155    else
1156        newEnd = end;
1157
1158    Text* text = static_cast<Text*>(start.deprecatedNode());
1159    splitTextNodeContainingElement(text, start.deprecatedEditingOffset());
1160    updateStartEnd(Position(start.deprecatedNode()->parentNode(), start.deprecatedNode()->nodeIndex(), Position::PositionIsOffsetInAnchor), newEnd);
1161}
1162
1163void ApplyStyleCommand::splitTextElementAtEnd(const Position& start, const Position& end)
1164{
1165    ASSERT(end.anchorType() == Position::PositionIsOffsetInAnchor);
1166
1167    bool shouldUpdateStart = start.anchorType() == Position::PositionIsOffsetInAnchor && start.containerNode() == end.containerNode();
1168    Text* text = static_cast<Text*>(end.deprecatedNode());
1169    splitTextNodeContainingElement(text, end.deprecatedEditingOffset());
1170
1171    Node* prevNode = text->parentNode()->previousSibling()->lastChild();
1172    ASSERT(prevNode);
1173    Position newStart = shouldUpdateStart ? Position(prevNode, start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor) : start;
1174    updateStartEnd(newStart, Position(prevNode->parentNode(), prevNode->nodeIndex() + 1, Position::PositionIsOffsetInAnchor));
1175}
1176
1177bool ApplyStyleCommand::shouldSplitTextElement(Element* element, EditingStyle* style)
1178{
1179    if (!element || !element->isHTMLElement())
1180        return false;
1181
1182    return shouldRemoveInlineStyleFromElement(style, toHTMLElement(element));
1183}
1184
1185bool ApplyStyleCommand::isValidCaretPositionInTextNode(const Position& position)
1186{
1187    Node* node = position.containerNode();
1188    if (position.anchorType() != Position::PositionIsOffsetInAnchor || !node->isTextNode())
1189        return false;
1190    int offsetInText = position.offsetInContainerNode();
1191    return offsetInText > caretMinOffset(node) && offsetInText < caretMaxOffset(node);
1192}
1193
1194static bool areIdenticalElements(Node *first, Node *second)
1195{
1196    // check that tag name and all attribute names and values are identical
1197
1198    if (!first->isElementNode())
1199        return false;
1200
1201    if (!second->isElementNode())
1202        return false;
1203
1204    Element *firstElement = static_cast<Element *>(first);
1205    Element *secondElement = static_cast<Element *>(second);
1206
1207    if (!firstElement->tagQName().matches(secondElement->tagQName()))
1208        return false;
1209
1210    NamedNodeMap *firstMap = firstElement->attributes();
1211    NamedNodeMap *secondMap = secondElement->attributes();
1212
1213    unsigned firstLength = firstMap->length();
1214
1215    if (firstLength != secondMap->length())
1216        return false;
1217
1218    for (unsigned i = 0; i < firstLength; i++) {
1219        Attribute *attribute = firstMap->attributeItem(i);
1220        Attribute *secondAttribute = secondMap->getAttributeItem(attribute->name());
1221
1222        if (!secondAttribute || attribute->value() != secondAttribute->value())
1223            return false;
1224    }
1225
1226    return true;
1227}
1228
1229bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position& start, const Position& end)
1230{
1231    Node* startNode = start.containerNode();
1232    int startOffset = start.computeOffsetInContainerNode();
1233    if (startOffset)
1234        return false;
1235
1236    if (isAtomicNode(startNode)) {
1237        // note: prior siblings could be unrendered elements. it's silly to miss the
1238        // merge opportunity just for that.
1239        if (startNode->previousSibling())
1240            return false;
1241
1242        startNode = startNode->parentNode();
1243        startOffset = 0;
1244    }
1245
1246    if (!startNode->isElementNode())
1247        return false;
1248
1249    Node* previousSibling = startNode->previousSibling();
1250
1251    if (previousSibling && areIdenticalElements(startNode, previousSibling)) {
1252        Element* previousElement = static_cast<Element*>(previousSibling);
1253        Element* element = static_cast<Element*>(startNode);
1254        Node* startChild = element->firstChild();
1255        ASSERT(startChild);
1256        mergeIdenticalElements(previousElement, element);
1257
1258        int startOffsetAdjustment = startChild->nodeIndex();
1259        int endOffsetAdjustment = startNode == end.deprecatedNode() ? startOffsetAdjustment : 0;
1260        updateStartEnd(Position(startNode, startOffsetAdjustment, Position::PositionIsOffsetInAnchor),
1261                       Position(end.deprecatedNode(), end.deprecatedEditingOffset() + endOffsetAdjustment, Position::PositionIsOffsetInAnchor));
1262        return true;
1263    }
1264
1265    return false;
1266}
1267
1268bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position& start, const Position& end)
1269{
1270    Node* endNode = end.containerNode();
1271    int endOffset = end.computeOffsetInContainerNode();
1272
1273    if (isAtomicNode(endNode)) {
1274        if (endOffset < lastOffsetInNode(endNode))
1275            return false;
1276
1277        unsigned parentLastOffset = end.deprecatedNode()->parentNode()->childNodes()->length() - 1;
1278        if (end.deprecatedNode()->nextSibling())
1279            return false;
1280
1281        endNode = end.deprecatedNode()->parentNode();
1282        endOffset = parentLastOffset;
1283    }
1284
1285    if (!endNode->isElementNode() || endNode->hasTagName(brTag))
1286        return false;
1287
1288    Node* nextSibling = endNode->nextSibling();
1289    if (nextSibling && areIdenticalElements(endNode, nextSibling)) {
1290        Element* nextElement = static_cast<Element *>(nextSibling);
1291        Element* element = static_cast<Element *>(endNode);
1292        Node* nextChild = nextElement->firstChild();
1293
1294        mergeIdenticalElements(element, nextElement);
1295
1296        bool shouldUpdateStart = start.containerNode() == endNode;
1297        int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childNodes()->length();
1298        updateStartEnd(shouldUpdateStart ? Position(nextElement, start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor) : start,
1299                       Position(nextElement, endOffset, Position::PositionIsOffsetInAnchor));
1300        return true;
1301    }
1302
1303    return false;
1304}
1305
1306void ApplyStyleCommand::surroundNodeRangeWithElement(PassRefPtr<Node> passedStartNode, PassRefPtr<Node> endNode, PassRefPtr<Element> elementToInsert)
1307{
1308    ASSERT(passedStartNode);
1309    ASSERT(endNode);
1310    ASSERT(elementToInsert);
1311    RefPtr<Node> startNode = passedStartNode;
1312    RefPtr<Element> element = elementToInsert;
1313
1314    insertNodeBefore(element, startNode);
1315
1316    RefPtr<Node> node = startNode;
1317    while (node) {
1318        RefPtr<Node> next = node->nextSibling();
1319        removeNode(node);
1320        appendNode(node, element);
1321        if (node == endNode)
1322            break;
1323        node = next;
1324    }
1325
1326    RefPtr<Node> nextSibling = element->nextSibling();
1327    RefPtr<Node> previousSibling = element->previousSibling();
1328    if (nextSibling && nextSibling->isElementNode() && nextSibling->rendererIsEditable()
1329        && areIdenticalElements(element.get(), static_cast<Element*>(nextSibling.get())))
1330        mergeIdenticalElements(element.get(), static_cast<Element*>(nextSibling.get()));
1331
1332    if (previousSibling && previousSibling->isElementNode() && previousSibling->rendererIsEditable()) {
1333        Node* mergedElement = previousSibling->nextSibling();
1334        if (mergedElement->isElementNode() && mergedElement->rendererIsEditable()
1335            && areIdenticalElements(static_cast<Element*>(previousSibling.get()), static_cast<Element*>(mergedElement)))
1336            mergeIdenticalElements(static_cast<Element*>(previousSibling.get()), static_cast<Element*>(mergedElement));
1337    }
1338
1339    // FIXME: We should probably call updateStartEnd if the start or end was in the node
1340    // range so that the endingSelection() is canonicalized.  See the comments at the end of
1341    // VisibleSelection::validate().
1342}
1343
1344void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, HTMLElement* block)
1345{
1346    // Do not check for legacy styles here. Those styles, like <B> and <I>, only apply for
1347    // inline content.
1348    if (!block)
1349        return;
1350
1351    String cssText = styleChange.cssStyle();
1352    CSSMutableStyleDeclaration* decl = block->inlineStyleDecl();
1353    if (decl)
1354        cssText += decl->cssText();
1355    setNodeAttribute(block, styleAttr, cssText);
1356}
1357
1358void ApplyStyleCommand::addInlineStyleIfNeeded(EditingStyle* style, PassRefPtr<Node> passedStart, PassRefPtr<Node> passedEnd, EAddStyledElement addStyledElement)
1359{
1360    if (!passedStart || !passedEnd || !passedStart->inDocument() || !passedEnd->inDocument())
1361        return;
1362    RefPtr<Node> startNode = passedStart;
1363    RefPtr<Node> endNode = passedEnd;
1364
1365    // It's okay to obtain the style at the startNode because we've removed all relevant styles from the current run.
1366    RefPtr<HTMLElement> dummyElement;
1367    Position positionForStyleComparison;
1368    if (!startNode->isElementNode()) {
1369        dummyElement = createStyleSpanElement(document());
1370        insertNodeAt(dummyElement, positionBeforeNode(startNode.get()));
1371        positionForStyleComparison = positionBeforeNode(dummyElement.get());
1372    } else
1373        positionForStyleComparison = firstPositionInOrBeforeNode(startNode.get());
1374
1375    StyleChange styleChange(style, positionForStyleComparison);
1376
1377    if (dummyElement)
1378        removeNode(dummyElement);
1379
1380    // Find appropriate font and span elements top-down.
1381    HTMLElement* fontContainer = 0;
1382    HTMLElement* styleContainer = 0;
1383    for (Node* container = startNode.get(); container && startNode == endNode; container = container->firstChild()) {
1384        if (container->isHTMLElement() && container->hasTagName(fontTag))
1385            fontContainer = toHTMLElement(container);
1386        bool styleContainerIsNotSpan = !styleContainer || !styleContainer->hasTagName(spanTag);
1387        if (container->isHTMLElement() && (container->hasTagName(spanTag) || (styleContainerIsNotSpan && container->childNodeCount())))
1388            styleContainer = toHTMLElement(container);
1389        if (!container->firstChild())
1390            break;
1391        startNode = container->firstChild();
1392        endNode = container->lastChild();
1393    }
1394
1395    // Font tags need to go outside of CSS so that CSS font sizes override leagcy font sizes.
1396    if (styleChange.applyFontColor() || styleChange.applyFontFace() || styleChange.applyFontSize()) {
1397        if (fontContainer) {
1398            if (styleChange.applyFontColor())
1399                setNodeAttribute(fontContainer, colorAttr, styleChange.fontColor());
1400            if (styleChange.applyFontFace())
1401                setNodeAttribute(fontContainer, faceAttr, styleChange.fontFace());
1402            if (styleChange.applyFontSize())
1403                setNodeAttribute(fontContainer, sizeAttr, styleChange.fontSize());
1404        } else {
1405            RefPtr<Element> fontElement = createFontElement(document());
1406            if (styleChange.applyFontColor())
1407                fontElement->setAttribute(colorAttr, styleChange.fontColor());
1408            if (styleChange.applyFontFace())
1409                fontElement->setAttribute(faceAttr, styleChange.fontFace());
1410            if (styleChange.applyFontSize())
1411                fontElement->setAttribute(sizeAttr, styleChange.fontSize());
1412            surroundNodeRangeWithElement(startNode, endNode, fontElement.get());
1413        }
1414    }
1415
1416    if (styleChange.cssStyle().length()) {
1417        if (styleContainer) {
1418            CSSMutableStyleDeclaration* existingStyle = toHTMLElement(styleContainer)->inlineStyleDecl();
1419            if (existingStyle)
1420                setNodeAttribute(styleContainer, styleAttr, existingStyle->cssText() + styleChange.cssStyle());
1421            else
1422                setNodeAttribute(styleContainer, styleAttr, styleChange.cssStyle());
1423        } else {
1424            RefPtr<Element> styleElement = createStyleSpanElement(document());
1425            styleElement->setAttribute(styleAttr, styleChange.cssStyle());
1426            surroundNodeRangeWithElement(startNode, endNode, styleElement.release());
1427        }
1428    }
1429
1430    if (styleChange.applyBold())
1431        surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), bTag));
1432
1433    if (styleChange.applyItalic())
1434        surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), iTag));
1435
1436    if (styleChange.applyUnderline())
1437        surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), uTag));
1438
1439    if (styleChange.applyLineThrough())
1440        surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), strikeTag));
1441
1442    if (styleChange.applySubscript())
1443        surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), subTag));
1444    else if (styleChange.applySuperscript())
1445        surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), supTag));
1446
1447    if (m_styledInlineElement && addStyledElement == AddStyledElement)
1448        surroundNodeRangeWithElement(startNode, endNode, m_styledInlineElement->cloneElementWithoutChildren());
1449}
1450
1451float ApplyStyleCommand::computedFontSize(Node* node)
1452{
1453    if (!node)
1454        return 0;
1455
1456    RefPtr<CSSComputedStyleDeclaration> style = computedStyle(node);
1457    if (!style)
1458        return 0;
1459
1460    RefPtr<CSSPrimitiveValue> value = static_pointer_cast<CSSPrimitiveValue>(style->getPropertyCSSValue(CSSPropertyFontSize));
1461    if (!value)
1462        return 0;
1463
1464    return value->getFloatValue(CSSPrimitiveValue::CSS_PX);
1465}
1466
1467void ApplyStyleCommand::joinChildTextNodes(Node* node, const Position& start, const Position& end)
1468{
1469    if (!node)
1470        return;
1471
1472    Position newStart = start;
1473    Position newEnd = end;
1474
1475    Node* child = node->firstChild();
1476    while (child) {
1477        Node* next = child->nextSibling();
1478        if (child->isTextNode() && next && next->isTextNode()) {
1479            Text* childText = static_cast<Text *>(child);
1480            Text* nextText = static_cast<Text *>(next);
1481            if (start.anchorType() == Position::PositionIsOffsetInAnchor && next == start.containerNode())
1482                newStart = Position(childText, childText->length() + start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor);
1483            if (end.anchorType() == Position::PositionIsOffsetInAnchor && next == end.containerNode())
1484                newEnd = Position(childText, childText->length() + end.offsetInContainerNode(), Position::PositionIsOffsetInAnchor);
1485            String textToMove = nextText->data();
1486            insertTextIntoNode(childText, childText->length(), textToMove);
1487            removeNode(next);
1488            // don't move child node pointer. it may want to merge with more text nodes.
1489        }
1490        else {
1491            child = child->nextSibling();
1492        }
1493    }
1494
1495    updateStartEnd(newStart, newEnd);
1496}
1497
1498}
1499