1/*
2 * Copyright (C) 2005, 2006, 2007, 2008 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 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 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 "CompositeEditCommand.h"
28
29#include "AppendNodeCommand.h"
30#include "ApplyStyleCommand.h"
31#include "DeleteFromTextNodeCommand.h"
32#include "DeleteSelectionCommand.h"
33#include "Document.h"
34#include "DocumentFragment.h"
35#include "DocumentMarkerController.h"
36#include "Editor.h"
37#include "EditorInsertAction.h"
38#include "ElementTraversal.h"
39#include "ExceptionCodePlaceholder.h"
40#include "Frame.h"
41#include "HTMLElement.h"
42#include "HTMLNames.h"
43#include "InlineTextBox.h"
44#include "InsertIntoTextNodeCommand.h"
45#include "InsertLineBreakCommand.h"
46#include "InsertNodeBeforeCommand.h"
47#include "InsertParagraphSeparatorCommand.h"
48#include "InsertTextCommand.h"
49#include "MergeIdenticalElementsCommand.h"
50#include "NodeTraversal.h"
51#include "Range.h"
52#include "RemoveCSSPropertyCommand.h"
53#include "RemoveNodeCommand.h"
54#include "RemoveNodePreservingChildrenCommand.h"
55#include "ReplaceNodeWithSpanCommand.h"
56#include "ReplaceSelectionCommand.h"
57#include "RenderBlock.h"
58#include "RenderText.h"
59#include "ScopedEventQueue.h"
60#include "SetNodeAttributeCommand.h"
61#include "SplitElementCommand.h"
62#include "SplitTextNodeCommand.h"
63#include "SplitTextNodeContainingElementCommand.h"
64#include "Text.h"
65#include "TextIterator.h"
66#include "VisibleUnits.h"
67#include "WrapContentsInDummySpanCommand.h"
68#include "htmlediting.h"
69#include "markup.h"
70
71#if ENABLE(DELETION_UI)
72#include "DeleteButtonController.h"
73#endif
74
75#if PLATFORM(IOS)
76#include "BreakBlockquoteCommand.h"
77#endif
78
79namespace WebCore {
80
81using namespace HTMLNames;
82
83PassRefPtr<EditCommandComposition> EditCommandComposition::create(Document& document,
84    const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction editAction)
85{
86    return adoptRef(new EditCommandComposition(document, startingSelection, endingSelection, editAction));
87}
88
89EditCommandComposition::EditCommandComposition(Document& document, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction editAction)
90    : m_document(&document)
91    , m_startingSelection(startingSelection)
92    , m_endingSelection(endingSelection)
93    , m_startingRootEditableElement(startingSelection.rootEditableElement())
94    , m_endingRootEditableElement(endingSelection.rootEditableElement())
95    , m_editAction(editAction)
96{
97}
98
99void EditCommandComposition::unapply()
100{
101    ASSERT(m_document);
102    RefPtr<Frame> frame = m_document->frame();
103    ASSERT(frame);
104
105    // Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
106    // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
107    // if one is necessary (like for the creation of VisiblePositions).
108    m_document->updateLayoutIgnorePendingStylesheets();
109#if PLATFORM(IOS)
110    // FIXME: Where should iPhone code deal with the composition?
111    // Since editing commands don't save/restore the composition, undoing without fixing
112    // up the composition will leave a stale, invalid composition, as in <rdar://problem/6831637>.
113    // Desktop handles this in -[WebHTMLView _updateSelectionForInputManager], but the phone
114    // goes another route.
115    frame->editor().cancelComposition();
116#endif
117
118    {
119#if ENABLE(DELETION_UI)
120        DeleteButtonControllerDisableScope deleteButtonControllerDisableScope(frame.get());
121#endif
122
123        size_t size = m_commands.size();
124        for (size_t i = size; i; --i)
125            m_commands[i - 1]->doUnapply();
126    }
127
128    frame->editor().unappliedEditing(this);
129}
130
131void EditCommandComposition::reapply()
132{
133    ASSERT(m_document);
134    RefPtr<Frame> frame = m_document->frame();
135    ASSERT(frame);
136
137    // Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
138    // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
139    // if one is necessary (like for the creation of VisiblePositions).
140    m_document->updateLayoutIgnorePendingStylesheets();
141
142    {
143#if ENABLE(DELETION_UI)
144        DeleteButtonControllerDisableScope deleteButtonControllerDisableScope(frame.get());
145#endif
146        size_t size = m_commands.size();
147        for (size_t i = 0; i != size; ++i)
148            m_commands[i]->doReapply();
149    }
150
151    frame->editor().reappliedEditing(this);
152}
153
154void EditCommandComposition::append(SimpleEditCommand* command)
155{
156    m_commands.append(command);
157}
158
159void EditCommandComposition::setStartingSelection(const VisibleSelection& selection)
160{
161    m_startingSelection = selection;
162    m_startingRootEditableElement = selection.rootEditableElement();
163}
164
165void EditCommandComposition::setEndingSelection(const VisibleSelection& selection)
166{
167    m_endingSelection = selection;
168    m_endingRootEditableElement = selection.rootEditableElement();
169}
170
171#ifndef NDEBUG
172void EditCommandComposition::getNodesInCommand(HashSet<Node*>& nodes)
173{
174    size_t size = m_commands.size();
175    for (size_t i = 0; i < size; ++i)
176        m_commands[i]->getNodesInCommand(nodes);
177}
178#endif
179
180void applyCommand(PassRefPtr<CompositeEditCommand> command)
181{
182    command->apply();
183}
184
185CompositeEditCommand::CompositeEditCommand(Document& document)
186    : EditCommand(document)
187{
188}
189
190CompositeEditCommand::~CompositeEditCommand()
191{
192    ASSERT(isTopLevelCommand() || !m_composition);
193}
194
195void CompositeEditCommand::apply()
196{
197    if (!endingSelection().isContentRichlyEditable()) {
198        switch (editingAction()) {
199        case EditActionTyping:
200        case EditActionPaste:
201        case EditActionDrag:
202        case EditActionSetWritingDirection:
203        case EditActionCut:
204        case EditActionUnspecified:
205#if PLATFORM(IOS)
206        case EditActionDelete:
207        case EditActionDictation:
208#endif
209            break;
210        default:
211            ASSERT_NOT_REACHED();
212            return;
213        }
214    }
215    ensureComposition();
216
217    // Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
218    // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
219    // if one is necessary (like for the creation of VisiblePositions).
220    document().updateLayoutIgnorePendingStylesheets();
221
222    {
223        EventQueueScope eventQueueScope;
224#if ENABLE(DELETION_UI)
225        DeleteButtonControllerDisableScope deleteButtonControllerDisableScope(&frame());
226#endif
227        doApply();
228    }
229
230    // Only need to call appliedEditing for top-level commands,
231    // and TypingCommands do it on their own (see TypingCommand::typingAddedToOpenCommand).
232    if (!isTypingCommand())
233        frame().editor().appliedEditing(this);
234    setShouldRetainAutocorrectionIndicator(false);
235}
236
237EditCommandComposition* CompositeEditCommand::ensureComposition()
238{
239    CompositeEditCommand* command = this;
240    while (command && command->parent())
241        command = command->parent();
242    if (!command->m_composition)
243        command->m_composition = EditCommandComposition::create(document(), startingSelection(), endingSelection(), editingAction());
244    return command->m_composition.get();
245}
246
247bool CompositeEditCommand::isCreateLinkCommand() const
248{
249    return false;
250}
251
252bool CompositeEditCommand::preservesTypingStyle() const
253{
254    return false;
255}
256
257bool CompositeEditCommand::isTypingCommand() const
258{
259    return false;
260}
261
262bool CompositeEditCommand::shouldRetainAutocorrectionIndicator() const
263{
264    return false;
265}
266
267void CompositeEditCommand::setShouldRetainAutocorrectionIndicator(bool)
268{
269}
270
271//
272// sugary-sweet convenience functions to help create and apply edit commands in composite commands
273//
274void CompositeEditCommand::applyCommandToComposite(PassRefPtr<EditCommand> prpCommand)
275{
276    RefPtr<EditCommand> command = prpCommand;
277    command->setParent(this);
278    command->doApply();
279    if (command->isSimpleEditCommand()) {
280        command->setParent(0);
281        ensureComposition()->append(toSimpleEditCommand(command.get()));
282    }
283    m_commands.append(command.release());
284}
285
286void CompositeEditCommand::applyCommandToComposite(PassRefPtr<CompositeEditCommand> command, const VisibleSelection& selection)
287{
288    command->setParent(this);
289    if (selection != command->endingSelection()) {
290        command->setStartingSelection(selection);
291        command->setEndingSelection(selection);
292    }
293    command->doApply();
294    m_commands.append(command);
295}
296
297void CompositeEditCommand::applyStyle(const EditingStyle* style, EditAction editingAction)
298{
299    applyCommandToComposite(ApplyStyleCommand::create(document(), style, editingAction));
300}
301
302void CompositeEditCommand::applyStyle(const EditingStyle* style, const Position& start, const Position& end, EditAction editingAction)
303{
304    applyCommandToComposite(ApplyStyleCommand::create(document(), style, start, end, editingAction));
305}
306
307void CompositeEditCommand::applyStyledElement(PassRefPtr<Element> element)
308{
309    applyCommandToComposite(ApplyStyleCommand::create(element, false));
310}
311
312void CompositeEditCommand::removeStyledElement(PassRefPtr<Element> element)
313{
314    applyCommandToComposite(ApplyStyleCommand::create(element, true));
315}
316
317void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea)
318{
319    applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea));
320}
321
322void CompositeEditCommand::insertLineBreak()
323{
324    applyCommandToComposite(InsertLineBreakCommand::create(document()));
325}
326
327bool CompositeEditCommand::isRemovableBlock(const Node* node)
328{
329    if (!node->hasTagName(divTag))
330        return false;
331
332    Node* parentNode = node->parentNode();
333    if (parentNode && parentNode->firstChild() != parentNode->lastChild())
334        return false;
335
336    if (!toElement(node)->hasAttributes())
337        return true;
338
339    return false;
340}
341
342void CompositeEditCommand::insertNodeBefore(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
343{
344    ASSERT(!refChild->hasTagName(bodyTag));
345    applyCommandToComposite(InsertNodeBeforeCommand::create(insertChild, refChild, shouldAssumeContentIsAlwaysEditable));
346}
347
348void CompositeEditCommand::insertNodeAfter(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild)
349{
350    ASSERT(insertChild);
351    ASSERT(refChild);
352    ASSERT(!refChild->hasTagName(bodyTag));
353    ContainerNode* parent = refChild->parentNode();
354    ASSERT(parent);
355    ASSERT(!parent->isShadowRoot());
356    if (parent->lastChild() == refChild)
357        appendNode(insertChild, parent);
358    else {
359        ASSERT(refChild->nextSibling());
360        insertNodeBefore(insertChild, refChild->nextSibling());
361    }
362}
363
364void CompositeEditCommand::insertNodeAt(PassRefPtr<Node> insertChild, const Position& editingPosition)
365{
366    ASSERT(isEditablePosition(editingPosition));
367    // For editing positions like [table, 0], insert before the table,
368    // likewise for replaced elements, brs, etc.
369    Position p = editingPosition.parentAnchoredEquivalent();
370    Node* refChild = p.deprecatedNode();
371    int offset = p.deprecatedEditingOffset();
372
373    if (canHaveChildrenForEditing(refChild)) {
374        Node* child = refChild->firstChild();
375        for (int i = 0; child && i < offset; i++)
376            child = child->nextSibling();
377        if (child)
378            insertNodeBefore(insertChild, child);
379        else
380            appendNode(insertChild, toContainerNode(refChild));
381    } else if (caretMinOffset(refChild) >= offset)
382        insertNodeBefore(insertChild, refChild);
383    else if (refChild->isTextNode() && caretMaxOffset(refChild) > offset) {
384        splitTextNode(toText(refChild), offset);
385
386        // Mutation events (bug 22634) from the text node insertion may have removed the refChild
387        if (!refChild->inDocument())
388            return;
389        insertNodeBefore(insertChild, refChild);
390    } else
391        insertNodeAfter(insertChild, refChild);
392}
393
394void CompositeEditCommand::appendNode(PassRefPtr<Node> node, PassRefPtr<ContainerNode> parent)
395{
396    ASSERT(canHaveChildrenForEditing(parent.get()));
397    applyCommandToComposite(AppendNodeCommand::create(parent, node));
398}
399
400void CompositeEditCommand::removeChildrenInRange(PassRefPtr<Node> node, unsigned from, unsigned to)
401{
402    Vector<RefPtr<Node>> children;
403    Node* child = node->childNode(from);
404    for (unsigned i = from; child && i < to; i++, child = child->nextSibling())
405        children.append(child);
406
407    size_t size = children.size();
408    for (size_t i = 0; i < size; ++i)
409        removeNode(children[i].release());
410}
411
412void CompositeEditCommand::removeNode(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
413{
414    if (!node || !node->nonShadowBoundaryParentNode())
415        return;
416    applyCommandToComposite(RemoveNodeCommand::create(node, shouldAssumeContentIsAlwaysEditable));
417}
418
419void CompositeEditCommand::removeNodePreservingChildren(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
420{
421    applyCommandToComposite(RemoveNodePreservingChildrenCommand::create(node, shouldAssumeContentIsAlwaysEditable));
422}
423
424void CompositeEditCommand::removeNodeAndPruneAncestors(PassRefPtr<Node> node)
425{
426    RefPtr<ContainerNode> parent = node->parentNode();
427    removeNode(node);
428    prune(parent.release());
429}
430
431void CompositeEditCommand::moveRemainingSiblingsToNewParent(Node* node, Node* pastLastNodeToMove, PassRefPtr<Element> prpNewParent)
432{
433    NodeVector nodesToRemove;
434    RefPtr<Element> newParent = prpNewParent;
435
436    for (; node && node != pastLastNodeToMove; node = node->nextSibling())
437        nodesToRemove.append(*node);
438
439    for (unsigned i = 0; i < nodesToRemove.size(); i++) {
440        removeNode(&nodesToRemove[i].get());
441        appendNode(&nodesToRemove[i].get(), newParent);
442    }
443}
444
445void CompositeEditCommand::updatePositionForNodeRemovalPreservingChildren(Position& position, Node* node)
446{
447    int offset = (position.anchorType() == Position::PositionIsOffsetInAnchor) ? position.offsetInContainerNode() : 0;
448    updatePositionForNodeRemoval(position, node);
449    if (offset)
450        position.moveToOffset(offset);
451}
452
453HTMLElement* CompositeEditCommand::replaceElementWithSpanPreservingChildrenAndAttributes(PassRefPtr<HTMLElement> node)
454{
455    // It would also be possible to implement all of ReplaceNodeWithSpanCommand
456    // as a series of existing smaller edit commands.  Someone who wanted to
457    // reduce the number of edit commands could do so here.
458    RefPtr<ReplaceNodeWithSpanCommand> command = ReplaceNodeWithSpanCommand::create(node);
459    applyCommandToComposite(command);
460    // Returning a raw pointer here is OK because the command is retained by
461    // applyCommandToComposite (thus retaining the span), and the span is also
462    // in the DOM tree, and thus alive whie it has a parent.
463    ASSERT(command->spanElement()->inDocument());
464    return command->spanElement();
465}
466
467void CompositeEditCommand::prune(PassRefPtr<Node> node)
468{
469    if (RefPtr<Node> highestNodeToRemove = highestNodeToRemoveInPruning(node.get()))
470        removeNode(highestNodeToRemove.release());
471}
472
473void CompositeEditCommand::splitTextNode(PassRefPtr<Text> node, unsigned offset)
474{
475    applyCommandToComposite(SplitTextNodeCommand::create(node, offset));
476}
477
478void CompositeEditCommand::splitElement(PassRefPtr<Element> element, PassRefPtr<Node> atChild)
479{
480    applyCommandToComposite(SplitElementCommand::create(element, atChild));
481}
482
483void CompositeEditCommand::mergeIdenticalElements(PassRefPtr<Element> prpFirst, PassRefPtr<Element> prpSecond)
484{
485    RefPtr<Element> first = prpFirst;
486    RefPtr<Element> second = prpSecond;
487    ASSERT(!first->isDescendantOf(second.get()) && second != first);
488    if (first->nextSibling() != second) {
489        removeNode(second);
490        insertNodeAfter(second, first);
491    }
492    applyCommandToComposite(MergeIdenticalElementsCommand::create(first, second));
493}
494
495void CompositeEditCommand::wrapContentsInDummySpan(PassRefPtr<Element> element)
496{
497    applyCommandToComposite(WrapContentsInDummySpanCommand::create(element));
498}
499
500void CompositeEditCommand::splitTextNodeContainingElement(PassRefPtr<Text> text, unsigned offset)
501{
502    applyCommandToComposite(SplitTextNodeContainingElementCommand::create(text, offset));
503}
504
505#if PLATFORM(IOS)
506void CompositeEditCommand::inputText(const String& text, bool selectInsertedText)
507{
508    unsigned offset = 0;
509    unsigned length = text.length();
510
511    RefPtr<ContainerNode> scope;
512    unsigned startIndex = indexForVisiblePosition(endingSelection().visibleStart(), scope);
513
514    size_t newline;
515    do {
516        newline = text.find('\n', offset);
517        if (newline != offset) {
518            int substringLength = newline == notFound ? length - offset : newline - offset;
519            RefPtr<InsertTextCommand> command = InsertTextCommand::create(document(), text.substring(offset, substringLength), false);
520            applyCommandToComposite(command);
521        }
522        if (newline != notFound) {
523            VisiblePosition caret(endingSelection().visibleStart());
524            if (enclosingNodeOfType(caret.deepEquivalent(), &isMailBlockquote)) {
525                // FIXME: Breaking a blockquote when the caret is just after a space will collapse the
526                // space. Modify startIndex or length to compensate for this so that the ending selection
527                // will be positioned correctly.
528                // <rdar://problem/9914462> breaking a Mail blockquote just after a space collapses the space
529                if (caret.previous().characterAfter() == ' ') {
530                    if (!offset && !startIndex)
531                        startIndex--;
532                    else if (!length)
533                        length--;
534                }
535                applyCommandToComposite(BreakBlockquoteCommand::create(document()));
536            } else
537                insertLineBreak();
538        }
539
540        offset = newline + 1;
541    } while (newline != notFound && offset != length);
542
543    if (selectInsertedText)
544        setEndingSelection(VisibleSelection(visiblePositionForIndex(startIndex, scope.get()), visiblePositionForIndex(startIndex + length, scope.get())));
545}
546#endif
547
548void CompositeEditCommand::insertTextIntoNode(PassRefPtr<Text> node, unsigned offset, const String& text)
549{
550    if (!text.isEmpty())
551        applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, text));
552}
553
554void CompositeEditCommand::deleteTextFromNode(PassRefPtr<Text> node, unsigned offset, unsigned count)
555{
556    applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count));
557}
558
559void CompositeEditCommand::replaceTextInNode(PassRefPtr<Text> prpNode, unsigned offset, unsigned count, const String& replacementText)
560{
561    RefPtr<Text> node(prpNode);
562    applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count));
563    if (!replacementText.isEmpty())
564        applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, replacementText));
565}
566
567Position CompositeEditCommand::replaceSelectedTextInNode(const String& text)
568{
569    Position start = endingSelection().start();
570    Position end = endingSelection().end();
571    if (start.containerNode() != end.containerNode() || !start.containerNode()->isTextNode() || isTabSpanTextNode(start.containerNode()))
572        return Position();
573
574    RefPtr<Text> textNode = start.containerText();
575    replaceTextInNode(textNode, start.offsetInContainerNode(), end.offsetInContainerNode() - start.offsetInContainerNode(), text);
576
577    return Position(textNode.release(), start.offsetInContainerNode() + text.length());
578}
579
580static void copyMarkers(const Vector<DocumentMarker*>& markerPointers, Vector<DocumentMarker>& markers)
581{
582    size_t arraySize = markerPointers.size();
583    markers.reserveCapacity(arraySize);
584    for (size_t i = 0; i < arraySize; ++i)
585        markers.append(*markerPointers[i]);
586}
587
588void CompositeEditCommand::replaceTextInNodePreservingMarkers(PassRefPtr<Text> prpNode, unsigned offset, unsigned count, const String& replacementText)
589{
590    RefPtr<Text> node(prpNode);
591    DocumentMarkerController& markerController = document().markers();
592    Vector<DocumentMarker> markers;
593    copyMarkers(markerController.markersInRange(Range::create(document(), node, offset, node, offset + count).get(), DocumentMarker::AllMarkers()), markers);
594    replaceTextInNode(node, offset, count, replacementText);
595    RefPtr<Range> newRange = Range::create(document(), node, offset, node, offset + replacementText.length());
596    for (size_t i = 0; i < markers.size(); ++i)
597#if PLATFORM(IOS)
598        markerController.addMarker(newRange.get(), markers[i].type(), markers[i].description(), markers[i].alternatives(), markers[i].metadata());
599#else
600        markerController.addMarker(newRange.get(), markers[i].type(), markers[i].description());
601#endif // PLATFORM(IOS)
602}
603
604Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos)
605{
606    if (!isTabSpanTextNode(pos.anchorNode()))
607        return pos;
608
609    switch (pos.anchorType()) {
610    case Position::PositionIsBeforeChildren:
611    case Position::PositionIsAfterChildren:
612        ASSERT_NOT_REACHED();
613        return pos;
614    case Position::PositionIsOffsetInAnchor:
615        break;
616    case Position::PositionIsBeforeAnchor:
617        return positionInParentBeforeNode(pos.anchorNode());
618    case Position::PositionIsAfterAnchor:
619        return positionInParentAfterNode(pos.anchorNode());
620    }
621
622    Node* tabSpan = tabSpanNode(pos.containerNode());
623
624    if (pos.offsetInContainerNode() <= caretMinOffset(pos.containerNode()))
625        return positionInParentBeforeNode(tabSpan);
626
627    if (pos.offsetInContainerNode() >= caretMaxOffset(pos.containerNode()))
628        return positionInParentAfterNode(tabSpan);
629
630    splitTextNodeContainingElement(toText(pos.containerNode()), pos.offsetInContainerNode());
631    return positionInParentBeforeNode(tabSpan);
632}
633
634void CompositeEditCommand::insertNodeAtTabSpanPosition(PassRefPtr<Node> node, const Position& pos)
635{
636    // insert node before, after, or at split of tab span
637    insertNodeAt(node, positionOutsideTabSpan(pos));
638}
639
640void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
641{
642    if (endingSelection().isRange())
643        applyCommandToComposite(DeleteSelectionCommand::create(document(), smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup));
644}
645
646void CompositeEditCommand::deleteSelection(const VisibleSelection &selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
647{
648    if (selection.isRange())
649        applyCommandToComposite(DeleteSelectionCommand::create(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup));
650}
651
652void CompositeEditCommand::removeCSSProperty(PassRefPtr<StyledElement> element, CSSPropertyID property)
653{
654    applyCommandToComposite(RemoveCSSPropertyCommand::create(document(), element, property));
655}
656
657void CompositeEditCommand::removeNodeAttribute(PassRefPtr<Element> element, const QualifiedName& attribute)
658{
659    setNodeAttribute(element, attribute, AtomicString());
660}
661
662void CompositeEditCommand::setNodeAttribute(PassRefPtr<Element> element, const QualifiedName& attribute, const AtomicString& value)
663{
664    applyCommandToComposite(SetNodeAttributeCommand::create(element, attribute, value));
665}
666
667static inline bool containsOnlyWhitespace(const String& text)
668{
669    for (unsigned i = 0; i < text.length(); ++i) {
670        if (!isWhitespace(text[i]))
671            return false;
672    }
673
674    return true;
675}
676
677bool CompositeEditCommand::shouldRebalanceLeadingWhitespaceFor(const String& text) const
678{
679    return containsOnlyWhitespace(text);
680}
681
682bool CompositeEditCommand::canRebalance(const Position& position) const
683{
684    Node* node = position.containerNode();
685    if (position.anchorType() != Position::PositionIsOffsetInAnchor || !node || !node->isTextNode())
686        return false;
687
688    Text* textNode = toText(node);
689    if (textNode->length() == 0)
690        return false;
691
692    node->document().updateStyleIfNeeded();
693
694    RenderObject* renderer = textNode->renderer();
695    if (renderer && !renderer->style().collapseWhiteSpace())
696        return false;
697
698    return true;
699}
700
701// FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, cousins, etc).
702void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position)
703{
704    Node* node = position.containerNode();
705    if (!canRebalance(position))
706        return;
707
708    // If the rebalance is for the single offset, and neither text[offset] nor text[offset - 1] are some form of whitespace, do nothing.
709    int offset = position.deprecatedEditingOffset();
710    String text = toText(node)->data();
711    if (!isWhitespace(text[offset])) {
712        offset--;
713        if (offset < 0 || !isWhitespace(text[offset]))
714            return;
715    }
716
717    rebalanceWhitespaceOnTextSubstring(toText(node), position.offsetInContainerNode(), position.offsetInContainerNode());
718}
719
720void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(PassRefPtr<Text> prpTextNode, int startOffset, int endOffset)
721{
722    RefPtr<Text> textNode = prpTextNode;
723
724    String text = textNode->data();
725    ASSERT(!text.isEmpty());
726
727    // Set upstream and downstream to define the extent of the whitespace surrounding text[offset].
728    int upstream = startOffset;
729    while (upstream > 0 && isWhitespace(text[upstream - 1]))
730        upstream--;
731
732    int downstream = endOffset;
733    while ((unsigned)downstream < text.length() && isWhitespace(text[downstream]))
734        downstream++;
735
736    int length = downstream - upstream;
737    if (!length)
738        return;
739
740    VisiblePosition visibleUpstreamPos(Position(textNode, upstream));
741    VisiblePosition visibleDownstreamPos(Position(textNode, downstream));
742
743    String string = text.substring(upstream, length);
744    String rebalancedString = stringWithRebalancedWhitespace(string,
745    // FIXME: Because of the problem mentioned at the top of this function, we must also use nbsps at the start/end of the string because
746    // this function doesn't get all surrounding whitespace, just the whitespace in the current text node.
747                                                             isStartOfParagraph(visibleUpstreamPos) || upstream == 0,
748                                                             isEndOfParagraph(visibleDownstreamPos) || (unsigned)downstream == text.length());
749
750    if (string != rebalancedString)
751        replaceTextInNodePreservingMarkers(textNode.release(), upstream, length, rebalancedString);
752}
753
754void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& position)
755{
756    Node* node = position.deprecatedNode();
757    if (!node || !node->isTextNode())
758        return;
759    Text* textNode = toText(node);
760
761    if (textNode->length() == 0)
762        return;
763    RenderObject* renderer = textNode->renderer();
764    if (renderer && !renderer->style().collapseWhiteSpace())
765        return;
766
767    // Delete collapsed whitespace so that inserting nbsps doesn't uncollapse it.
768    Position upstreamPos = position.upstream();
769    deleteInsignificantText(position.upstream(), position.downstream());
770    position = upstreamPos.downstream();
771
772    VisiblePosition visiblePos(position);
773    VisiblePosition previousVisiblePos(visiblePos.previous());
774    Position previous(previousVisiblePos.deepEquivalent());
775
776    if (deprecatedIsCollapsibleWhitespace(previousVisiblePos.characterAfter()) && previous.deprecatedNode()->isTextNode() && !previous.deprecatedNode()->hasTagName(brTag))
777        replaceTextInNodePreservingMarkers(toText(previous.deprecatedNode()), previous.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
778    if (deprecatedIsCollapsibleWhitespace(visiblePos.characterAfter()) && position.deprecatedNode()->isTextNode() && !position.deprecatedNode()->hasTagName(brTag))
779        replaceTextInNodePreservingMarkers(toText(position.deprecatedNode()), position.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
780}
781
782void CompositeEditCommand::rebalanceWhitespace()
783{
784    VisibleSelection selection = endingSelection();
785    if (selection.isNone())
786        return;
787
788    rebalanceWhitespaceAt(selection.start());
789    if (selection.isRange())
790        rebalanceWhitespaceAt(selection.end());
791}
792
793void CompositeEditCommand::deleteInsignificantText(PassRefPtr<Text> textNode, unsigned start, unsigned end)
794{
795    if (!textNode || start >= end)
796        return;
797
798    document().updateLayout();
799
800    RenderText* textRenderer = textNode->renderer();
801    if (!textRenderer)
802        return;
803
804    Vector<InlineTextBox*> sortedTextBoxes;
805    size_t sortedTextBoxesPosition = 0;
806
807    for (InlineTextBox* textBox = textRenderer->firstTextBox(); textBox; textBox = textBox->nextTextBox())
808        sortedTextBoxes.append(textBox);
809
810    // If there is mixed directionality text, the boxes can be out of order,
811    // (like Arabic with embedded LTR), so sort them first.
812    if (textRenderer->containsReversedText())
813        std::sort(sortedTextBoxes.begin(), sortedTextBoxes.end(), InlineTextBox::compareByStart);
814    InlineTextBox* box = sortedTextBoxes.isEmpty() ? 0 : sortedTextBoxes[sortedTextBoxesPosition];
815
816    if (!box) {
817        // whole text node is empty
818        removeNode(textNode);
819        return;
820    }
821
822    unsigned length = textNode->length();
823    if (start >= length || end > length)
824        return;
825
826    unsigned removed = 0;
827    InlineTextBox* prevBox = 0;
828    String str;
829
830    // This loop structure works to process all gaps preceding a box,
831    // and also will look at the gap after the last box.
832    while (prevBox || box) {
833        unsigned gapStart = prevBox ? prevBox->start() + prevBox->len() : 0;
834        if (end < gapStart)
835            // No more chance for any intersections
836            break;
837
838        unsigned gapEnd = box ? box->start() : length;
839        bool indicesIntersect = start <= gapEnd && end >= gapStart;
840        int gapLen = gapEnd - gapStart;
841        if (indicesIntersect && gapLen > 0) {
842            gapStart = std::max(gapStart, start);
843            gapEnd = std::min(gapEnd, end);
844            if (str.isNull())
845                str = textNode->data().substring(start, end - start);
846            // remove text in the gap
847            str.remove(gapStart - start - removed, gapLen);
848            removed += gapLen;
849        }
850
851        prevBox = box;
852        if (box) {
853            if (++sortedTextBoxesPosition < sortedTextBoxes.size())
854                box = sortedTextBoxes[sortedTextBoxesPosition];
855            else
856                box = 0;
857        }
858    }
859
860    if (!str.isNull()) {
861        // Replace the text between start and end with our pruned version.
862        if (!str.isEmpty())
863            replaceTextInNode(textNode, start, end - start, str);
864        else {
865            // Assert that we are not going to delete all of the text in the node.
866            // If we were, that should have been done above with the call to
867            // removeNode and return.
868            ASSERT(start > 0 || end - start < textNode->length());
869            deleteTextFromNode(textNode, start, end - start);
870        }
871    }
872}
873
874void CompositeEditCommand::deleteInsignificantText(const Position& start, const Position& end)
875{
876    if (start.isNull() || end.isNull())
877        return;
878
879    if (comparePositions(start, end) >= 0)
880        return;
881
882    Vector<RefPtr<Text>> nodes;
883    for (Node* node = start.deprecatedNode(); node; node = NodeTraversal::next(node)) {
884        if (node->isTextNode())
885            nodes.append(toText(node));
886        if (node == end.deprecatedNode())
887            break;
888    }
889
890    for (size_t i = 0; i < nodes.size(); ++i) {
891        Text* textNode = nodes[i].get();
892        int startOffset = textNode == start.deprecatedNode() ? start.deprecatedEditingOffset() : 0;
893        int endOffset = textNode == end.deprecatedNode() ? end.deprecatedEditingOffset() : static_cast<int>(textNode->length());
894        deleteInsignificantText(textNode, startOffset, endOffset);
895    }
896}
897
898void CompositeEditCommand::deleteInsignificantTextDownstream(const Position& pos)
899{
900    Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivalent().downstream();
901    deleteInsignificantText(pos, end);
902}
903
904PassRefPtr<Node> CompositeEditCommand::appendBlockPlaceholder(PassRefPtr<Element> container)
905{
906    if (!container)
907        return 0;
908
909    document().updateLayoutIgnorePendingStylesheets();
910
911    // Should assert isBlockFlow || isInlineFlow when deletion improves. See 4244964.
912    ASSERT(container->renderer());
913
914    RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
915    appendNode(placeholder, container);
916    return placeholder.release();
917}
918
919PassRefPtr<Node> CompositeEditCommand::insertBlockPlaceholder(const Position& pos)
920{
921    if (pos.isNull())
922        return 0;
923
924    // Should assert isBlockFlow || isInlineFlow when deletion improves.  See 4244964.
925    ASSERT(pos.deprecatedNode()->renderer());
926
927    RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
928    insertNodeAt(placeholder, pos);
929    return placeholder.release();
930}
931
932PassRefPtr<Node> CompositeEditCommand::addBlockPlaceholderIfNeeded(Element* container)
933{
934    if (!container)
935        return 0;
936
937    document().updateLayoutIgnorePendingStylesheets();
938
939    RenderObject* renderer = container->renderer();
940    if (!renderer || !renderer->isRenderBlockFlow())
941        return 0;
942
943    // append the placeholder to make sure it follows
944    // any unrendered blocks
945    RenderBlock* block = toRenderBlock(renderer);
946    if (block->height() == 0 || (block->isListItem() && block->isEmpty()))
947        return appendBlockPlaceholder(container);
948
949    return 0;
950}
951
952// Assumes that the position is at a placeholder and does the removal without much checking.
953void CompositeEditCommand::removePlaceholderAt(const Position& p)
954{
955    ASSERT(lineBreakExistsAtPosition(p));
956
957    // We are certain that the position is at a line break, but it may be a br or a preserved newline.
958    if (p.anchorNode()->hasTagName(brTag)) {
959        removeNode(p.anchorNode());
960        return;
961    }
962
963    deleteTextFromNode(toText(p.anchorNode()), p.offsetInContainerNode(), 1);
964}
965
966PassRefPtr<Node> CompositeEditCommand::insertNewDefaultParagraphElementAt(const Position& position)
967{
968    RefPtr<Element> paragraphElement = createDefaultParagraphElement(document());
969    paragraphElement->appendChild(createBreakElement(document()), IGNORE_EXCEPTION);
970    insertNodeAt(paragraphElement, position);
971    return paragraphElement.release();
972}
973
974// If the paragraph is not entirely within it's own block, create one and move the paragraph into
975// it, and return that block.  Otherwise return 0.
976PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position& pos)
977{
978    if (pos.isNull())
979        return 0;
980
981    document().updateLayoutIgnorePendingStylesheets();
982
983    // It's strange that this function is responsible for verifying that pos has not been invalidated
984    // by an earlier call to this function.  The caller, applyBlockStyle, should do this.
985    VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
986    VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
987    VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePos);
988    VisiblePosition next = visibleParagraphEnd.next();
989    VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd;
990
991    Position upstreamStart = visibleParagraphStart.deepEquivalent().upstream();
992    Position upstreamEnd = visibleEnd.deepEquivalent().upstream();
993
994    // If there are no VisiblePositions in the same block as pos then
995    // upstreamStart will be outside the paragraph
996    if (comparePositions(pos, upstreamStart) < 0)
997        return 0;
998
999    // Perform some checks to see if we need to perform work in this function.
1000    if (isBlock(upstreamStart.deprecatedNode())) {
1001        // If the block is the root editable element, always move content to a new block,
1002        // since it is illegal to modify attributes on the root editable element for editing.
1003        if (upstreamStart.deprecatedNode() == editableRootForPosition(upstreamStart)) {
1004            // If the block is the root editable element and it contains no visible content, create a new
1005            // block but don't try and move content into it, since there's nothing for moveParagraphs to move.
1006            if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(toRenderElement(*upstreamStart.deprecatedNode()->renderer())))
1007                return insertNewDefaultParagraphElementAt(upstreamStart);
1008        } else if (isBlock(upstreamEnd.deprecatedNode())) {
1009            if (!upstreamEnd.deprecatedNode()->isDescendantOf(upstreamStart.deprecatedNode())) {
1010                // If the paragraph end is a descendant of paragraph start, then we need to run
1011                // the rest of this function. If not, we can bail here.
1012                return 0;
1013            }
1014        } else if (enclosingBlock(upstreamEnd.deprecatedNode()) != upstreamStart.deprecatedNode()) {
1015            // The visibleEnd.  It must be an ancestor of the paragraph start.
1016            // We can bail as we have a full block to work with.
1017            ASSERT(upstreamStart.deprecatedNode()->isDescendantOf(enclosingBlock(upstreamEnd.deprecatedNode())));
1018            return 0;
1019        } else if (isEndOfEditableOrNonEditableContent(visibleEnd)) {
1020            // At the end of the editable region. We can bail here as well.
1021            return 0;
1022        }
1023    }
1024
1025    RefPtr<Node> newBlock = insertNewDefaultParagraphElementAt(upstreamStart);
1026
1027    bool endWasBr = visibleParagraphEnd.deepEquivalent().deprecatedNode()->hasTagName(brTag);
1028
1029    moveParagraphs(visibleParagraphStart, visibleParagraphEnd, VisiblePosition(firstPositionInNode(newBlock.get())));
1030
1031    if (newBlock->lastChild() && newBlock->lastChild()->hasTagName(brTag) && !endWasBr)
1032        removeNode(newBlock->lastChild());
1033
1034    return newBlock.release();
1035}
1036
1037void CompositeEditCommand::pushAnchorElementDown(Element& anchorElement)
1038{
1039    ASSERT(anchorElement.isLink());
1040
1041    setEndingSelection(VisibleSelection::selectionFromContentsOfNode(&anchorElement));
1042    applyStyledElement(&anchorElement);
1043    // Clones of anchorElement have been pushed down, now remove it.
1044    if (anchorElement.inDocument())
1045        removeNodePreservingChildren(&anchorElement);
1046}
1047
1048// Clone the paragraph between start and end under blockElement,
1049// preserving the hierarchy up to outerNode.
1050
1051void CompositeEditCommand::cloneParagraphUnderNewElement(const Position& start, const Position& end, Node* passedOuterNode, Element* blockElement)
1052{
1053    ASSERT(comparePositions(start, end) <= 0);
1054
1055    // First we clone the outerNode
1056    RefPtr<Node> lastNode;
1057    RefPtr<Node> outerNode = passedOuterNode;
1058
1059    if (outerNode->isRootEditableElement()) {
1060        lastNode = blockElement;
1061    } else {
1062        lastNode = outerNode->cloneNode(isRenderedTable(outerNode.get()));
1063        appendNode(lastNode, blockElement);
1064    }
1065
1066    if (start.deprecatedNode() != outerNode && lastNode->isElementNode() && start.anchorNode()->isDescendantOf(outerNode.get())) {
1067        Vector<RefPtr<Node>> ancestors;
1068
1069        // Insert each node from innerNode to outerNode (excluded) in a list.
1070        for (Node* n = start.deprecatedNode(); n && n != outerNode; n = n->parentNode())
1071            ancestors.append(n);
1072
1073        // Clone every node between start.deprecatedNode() and outerBlock.
1074
1075        for (size_t i = ancestors.size(); i != 0; --i) {
1076            Node* item = ancestors[i - 1].get();
1077            RefPtr<Node> child = item->cloneNode(isRenderedTable(item));
1078            appendNode(child, toElement(lastNode.get()));
1079            lastNode = child.release();
1080        }
1081    }
1082
1083    // Handle the case of paragraphs with more than one node,
1084    // cloning all the siblings until end.deprecatedNode() is reached.
1085
1086    if (start.deprecatedNode() != end.deprecatedNode() && !start.deprecatedNode()->isDescendantOf(end.deprecatedNode())) {
1087        // If end is not a descendant of outerNode we need to
1088        // find the first common ancestor to increase the scope
1089        // of our nextSibling traversal.
1090        while (!end.deprecatedNode()->isDescendantOf(outerNode.get())) {
1091            outerNode = outerNode->parentNode();
1092        }
1093
1094        RefPtr<Node> startNode = start.deprecatedNode();
1095        for (RefPtr<Node> node = NodeTraversal::nextSkippingChildren(startNode.get(), outerNode.get()); node; node = NodeTraversal::nextSkippingChildren(node.get(), outerNode.get())) {
1096            // Move lastNode up in the tree as much as node was moved up in the
1097            // tree by NodeTraversal::nextSkippingChildren, so that the relative depth between
1098            // node and the original start node is maintained in the clone.
1099            while (startNode->parentNode() != node->parentNode()) {
1100                startNode = startNode->parentNode();
1101                lastNode = lastNode->parentNode();
1102            }
1103
1104            RefPtr<Node> clonedNode = node->cloneNode(true);
1105            insertNodeAfter(clonedNode, lastNode);
1106            lastNode = clonedNode.release();
1107            if (node == end.deprecatedNode() || end.deprecatedNode()->isDescendantOf(node.get()))
1108                break;
1109        }
1110    }
1111}
1112
1113
1114// There are bugs in deletion when it removes a fully selected table/list.
1115// It expands and removes the entire table/list, but will let content
1116// before and after the table/list collapse onto one line.
1117// Deleting a paragraph will leave a placeholder. Remove it (and prune
1118// empty or unrendered parents).
1119
1120void CompositeEditCommand::cleanupAfterDeletion(VisiblePosition destination)
1121{
1122    VisiblePosition caretAfterDelete = endingSelection().visibleStart();
1123    if (caretAfterDelete != destination && isStartOfParagraph(caretAfterDelete) && isEndOfParagraph(caretAfterDelete)) {
1124        // Note: We want the rightmost candidate.
1125        Position position = caretAfterDelete.deepEquivalent().downstream();
1126        Node* node = position.deprecatedNode();
1127        // Normally deletion will leave a br as a placeholder.
1128        if (node->hasTagName(brTag))
1129            removeNodeAndPruneAncestors(node);
1130        // If the selection to move was empty and in an empty block that
1131        // doesn't require a placeholder to prop itself open (like a bordered
1132        // div or an li), remove it during the move (the list removal code
1133        // expects this behavior).
1134        else if (isBlock(node)) {
1135            // If caret position after deletion and destination position coincides,
1136            // node should not be removed.
1137            if (!position.rendersInDifferentPosition(destination.deepEquivalent())) {
1138                prune(node);
1139                return;
1140            }
1141            removeNodeAndPruneAncestors(node);
1142        }
1143        else if (lineBreakExistsAtPosition(position)) {
1144            // There is a preserved '\n' at caretAfterDelete.
1145            // We can safely assume this is a text node.
1146            Text* textNode = toText(node);
1147            if (textNode->length() == 1)
1148                removeNodeAndPruneAncestors(node);
1149            else
1150                deleteTextFromNode(textNode, position.deprecatedEditingOffset(), 1);
1151        }
1152    }
1153}
1154
1155// This is a version of moveParagraph that preserves style by keeping the original markup
1156// It is currently used only by IndentOutdentCommand but it is meant to be used in the
1157// future by several other commands such as InsertList and the align commands.
1158// The blockElement parameter is the element to move the paragraph to,
1159// outerNode is the top element of the paragraph hierarchy.
1160
1161void CompositeEditCommand::moveParagraphWithClones(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, Element* blockElement, Node* outerNode)
1162{
1163    ASSERT(outerNode);
1164    ASSERT(blockElement);
1165
1166    VisiblePosition beforeParagraph = startOfParagraphToMove.previous();
1167    VisiblePosition afterParagraph(endOfParagraphToMove.next());
1168
1169    // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
1170    // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.
1171    Position start = startOfParagraphToMove.deepEquivalent().downstream();
1172    Position end = startOfParagraphToMove == endOfParagraphToMove ? start : endOfParagraphToMove.deepEquivalent().upstream();
1173
1174    cloneParagraphUnderNewElement(start, end, outerNode, blockElement);
1175
1176    setEndingSelection(VisibleSelection(start, end, DOWNSTREAM));
1177    deleteSelection(false, false, false, false);
1178
1179    // There are bugs in deletion when it removes a fully selected table/list.
1180    // It expands and removes the entire table/list, but will let content
1181    // before and after the table/list collapse onto one line.
1182
1183    cleanupAfterDeletion();
1184
1185    // Add a br if pruning an empty block level element caused a collapse.  For example:
1186    // foo^
1187    // <div>bar</div>
1188    // baz
1189    // Imagine moving 'bar' to ^.  'bar' will be deleted and its div pruned.  That would
1190    // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br.
1191    // Must recononicalize these two VisiblePositions after the pruning above.
1192    beforeParagraph = VisiblePosition(beforeParagraph.deepEquivalent());
1193    afterParagraph = VisiblePosition(afterParagraph.deepEquivalent());
1194
1195    if (beforeParagraph.isNotNull() && !isRenderedTable(beforeParagraph.deepEquivalent().deprecatedNode())
1196        && ((!isEndOfParagraph(beforeParagraph) && !isStartOfParagraph(beforeParagraph)) || beforeParagraph == afterParagraph)) {
1197        // FIXME: Trim text between beforeParagraph and afterParagraph if they aren't equal.
1198        insertNodeAt(createBreakElement(document()), beforeParagraph.deepEquivalent());
1199    }
1200}
1201
1202
1203// This moves a paragraph preserving its style.
1204void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
1205{
1206    ASSERT(isStartOfParagraph(startOfParagraphToMove));
1207    ASSERT(isEndOfParagraph(endOfParagraphToMove));
1208    moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection, preserveStyle);
1209}
1210
1211void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
1212{
1213    if (startOfParagraphToMove == destination)
1214        return;
1215
1216    int startIndex = -1;
1217    int endIndex = -1;
1218    int destinationIndex = -1;
1219    bool originalIsDirectional = endingSelection().isDirectional();
1220    if (preserveSelection && !endingSelection().isNone()) {
1221        VisiblePosition visibleStart = endingSelection().visibleStart();
1222        VisiblePosition visibleEnd = endingSelection().visibleEnd();
1223
1224        bool startAfterParagraph = comparePositions(visibleStart, endOfParagraphToMove) > 0;
1225        bool endBeforeParagraph = comparePositions(visibleEnd, startOfParagraphToMove) < 0;
1226
1227        if (!startAfterParagraph && !endBeforeParagraph) {
1228            bool startInParagraph = comparePositions(visibleStart, startOfParagraphToMove) >= 0;
1229            bool endInParagraph = comparePositions(visibleEnd, endOfParagraphToMove) <= 0;
1230
1231            startIndex = 0;
1232            if (startInParagraph) {
1233                RefPtr<Range> startRange = Range::create(document(), startOfParagraphToMove.deepEquivalent().parentAnchoredEquivalent(), visibleStart.deepEquivalent().parentAnchoredEquivalent());
1234                startIndex = TextIterator::rangeLength(startRange.get(), true);
1235            }
1236
1237            endIndex = 0;
1238            if (endInParagraph) {
1239                RefPtr<Range> endRange = Range::create(document(), startOfParagraphToMove.deepEquivalent().parentAnchoredEquivalent(), visibleEnd.deepEquivalent().parentAnchoredEquivalent());
1240                endIndex = TextIterator::rangeLength(endRange.get(), true);
1241            }
1242        }
1243    }
1244
1245    VisiblePosition beforeParagraph = startOfParagraphToMove.previous(CannotCrossEditingBoundary);
1246    VisiblePosition afterParagraph(endOfParagraphToMove.next(CannotCrossEditingBoundary));
1247
1248    // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
1249    // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.
1250    Position start = startOfParagraphToMove.deepEquivalent().downstream();
1251    Position end = endOfParagraphToMove.deepEquivalent().upstream();
1252
1253    // start and end can't be used directly to create a Range; they are "editing positions"
1254    Position startRangeCompliant = start.parentAnchoredEquivalent();
1255    Position endRangeCompliant = end.parentAnchoredEquivalent();
1256    RefPtr<Range> range = Range::create(document(), startRangeCompliant.deprecatedNode(), startRangeCompliant.deprecatedEditingOffset(), endRangeCompliant.deprecatedNode(), endRangeCompliant.deprecatedEditingOffset());
1257
1258    // FIXME: This is an inefficient way to preserve style on nodes in the paragraph to move. It
1259    // shouldn't matter though, since moved paragraphs will usually be quite small.
1260    RefPtr<DocumentFragment> fragment;
1261    // This used to use a ternary for initialization, but that confused some versions of GCC, see bug 37912
1262    if (startOfParagraphToMove != endOfParagraphToMove)
1263        fragment = createFragmentFromMarkup(document(), createMarkup(*range, 0, DoNotAnnotateForInterchange, true), "");
1264
1265    // A non-empty paragraph's style is moved when we copy and move it.  We don't move
1266    // anything if we're given an empty paragraph, but an empty paragraph can have style
1267    // too, <div><b><br></b></div> for example.  Save it so that we can preserve it later.
1268    RefPtr<EditingStyle> styleInEmptyParagraph;
1269#if !PLATFORM(IOS)
1270    if (startOfParagraphToMove == endOfParagraphToMove && preserveStyle) {
1271#else
1272    if (startOfParagraphToMove == endOfParagraphToMove && preserveStyle && isRichlyEditablePosition(destination.deepEquivalent())) {
1273#endif
1274        styleInEmptyParagraph = EditingStyle::create(startOfParagraphToMove.deepEquivalent());
1275        styleInEmptyParagraph->mergeTypingStyle(document());
1276        // The moved paragraph should assume the block style of the destination.
1277        styleInEmptyParagraph->removeBlockProperties();
1278    }
1279
1280    // FIXME (5098931): We should add a new insert action "WebViewInsertActionMoved" and call shouldInsertFragment here.
1281
1282    setEndingSelection(VisibleSelection(start, end, DOWNSTREAM));
1283    frame().editor().clearMisspellingsAndBadGrammar(endingSelection());
1284    deleteSelection(false, false, false, false);
1285
1286    ASSERT(destination.deepEquivalent().anchorNode()->inDocument());
1287    cleanupAfterDeletion(destination);
1288    ASSERT(destination.deepEquivalent().anchorNode()->inDocument());
1289
1290    // Add a br if pruning an empty block level element caused a collapse. For example:
1291    // foo^
1292    // <div>bar</div>
1293    // baz
1294    // Imagine moving 'bar' to ^. 'bar' will be deleted and its div pruned. That would
1295    // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br.
1296    // Must recononicalize these two VisiblePositions after the pruning above.
1297    beforeParagraph = VisiblePosition(beforeParagraph.deepEquivalent());
1298    afterParagraph = VisiblePosition(afterParagraph.deepEquivalent());
1299    if (beforeParagraph.isNotNull() && (!isEndOfParagraph(beforeParagraph) || beforeParagraph == afterParagraph)) {
1300        // FIXME: Trim text between beforeParagraph and afterParagraph if they aren't equal.
1301        insertNodeAt(createBreakElement(document()), beforeParagraph.deepEquivalent());
1302        // Need an updateLayout here in case inserting the br has split a text node.
1303        document().updateLayoutIgnorePendingStylesheets();
1304    }
1305
1306    RefPtr<Range> startToDestinationRange(Range::create(document(), firstPositionInNode(document().documentElement()), destination.deepEquivalent().parentAnchoredEquivalent()));
1307    destinationIndex = TextIterator::rangeLength(startToDestinationRange.get(), true);
1308
1309    setEndingSelection(VisibleSelection(destination, originalIsDirectional));
1310    ASSERT(endingSelection().isCaretOrRange());
1311    ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::MovingParagraph;
1312    if (!preserveStyle)
1313        options |= ReplaceSelectionCommand::MatchStyle;
1314    applyCommandToComposite(ReplaceSelectionCommand::create(document(), fragment, options));
1315
1316    frame().editor().markMisspellingsAndBadGrammar(endingSelection());
1317
1318    // If the selection is in an empty paragraph, restore styles from the old empty paragraph to the new empty paragraph.
1319    bool selectionIsEmptyParagraph = endingSelection().isCaret() && isStartOfParagraph(endingSelection().visibleStart()) && isEndOfParagraph(endingSelection().visibleStart());
1320    if (styleInEmptyParagraph && selectionIsEmptyParagraph)
1321        applyStyle(styleInEmptyParagraph.get());
1322
1323    if (preserveSelection && startIndex != -1) {
1324        // Fragment creation (using createMarkup) incorrectly uses regular
1325        // spaces instead of nbsps for some spaces that were rendered (11475), which
1326        // causes spaces to be collapsed during the move operation.  This results
1327        // in a call to rangeFromLocationAndLength with a location past the end
1328        // of the document (which will return null).
1329        RefPtr<Range> start = TextIterator::rangeFromLocationAndLength(document().documentElement(), destinationIndex + startIndex, 0, true);
1330        RefPtr<Range> end = TextIterator::rangeFromLocationAndLength(document().documentElement(), destinationIndex + endIndex, 0, true);
1331        if (start && end)
1332            setEndingSelection(VisibleSelection(start->startPosition(), end->startPosition(), DOWNSTREAM, originalIsDirectional));
1333    }
1334}
1335
1336// FIXME: Send an appropriate shouldDeleteRange call.
1337bool CompositeEditCommand::breakOutOfEmptyListItem()
1338{
1339    RefPtr<Node> emptyListItem = enclosingEmptyListItem(endingSelection().visibleStart());
1340    if (!emptyListItem)
1341        return false;
1342
1343    RefPtr<EditingStyle> style = EditingStyle::create(endingSelection().start());
1344    style->mergeTypingStyle(document());
1345
1346    RefPtr<ContainerNode> listNode = emptyListItem->parentNode();
1347    // FIXME: Can't we do something better when the immediate parent wasn't a list node?
1348    if (!listNode
1349        || (!listNode->hasTagName(ulTag) && !listNode->hasTagName(olTag))
1350        || !listNode->hasEditableStyle()
1351        || listNode == emptyListItem->rootEditableElement())
1352        return false;
1353
1354    RefPtr<Element> newBlock = 0;
1355    if (ContainerNode* blockEnclosingList = listNode->parentNode()) {
1356        if (blockEnclosingList->hasTagName(liTag)) { // listNode is inside another list item
1357            if (visiblePositionAfterNode(blockEnclosingList) == visiblePositionAfterNode(listNode.get())) {
1358                // If listNode appears at the end of the outer list item, then move listNode outside of this list item
1359                // e.g. <ul><li>hello <ul><li><br></li></ul> </li></ul> should become <ul><li>hello</li> <ul><li><br></li></ul> </ul> after this section
1360                // If listNode does NOT appear at the end, then we should consider it as a regular paragraph.
1361                // e.g. <ul><li> <ul><li><br></li></ul> hello</li></ul> should become <ul><li> <div><br></div> hello</li></ul> at the end
1362                splitElement(toElement(blockEnclosingList), listNode);
1363                removeNodePreservingChildren(listNode->parentNode());
1364                newBlock = createListItemElement(document());
1365            }
1366            // If listNode does NOT appear at the end of the outer list item, then behave as if in a regular paragraph.
1367        } else if (blockEnclosingList->hasTagName(olTag) || blockEnclosingList->hasTagName(ulTag))
1368            newBlock = createListItemElement(document());
1369    }
1370    if (!newBlock)
1371        newBlock = createDefaultParagraphElement(document());
1372
1373    RefPtr<Node> previousListNode = emptyListItem->isElementNode() ? ElementTraversal::previousSibling(emptyListItem.get()): emptyListItem->previousSibling();
1374    RefPtr<Node> nextListNode = emptyListItem->isElementNode() ? ElementTraversal::nextSibling(emptyListItem.get()): emptyListItem->nextSibling();
1375    if (isListItem(nextListNode.get()) || isListElement(nextListNode.get())) {
1376        // If emptyListItem follows another list item or nested list, split the list node.
1377        if (isListItem(previousListNode.get()) || isListElement(previousListNode.get()))
1378            splitElement(toElement(listNode.get()), emptyListItem);
1379
1380        // If emptyListItem is followed by other list item or nested list, then insert newBlock before the list node.
1381        // Because we have splitted the element, emptyListItem is the first element in the list node.
1382        // i.e. insert newBlock before ul or ol whose first element is emptyListItem
1383        insertNodeBefore(newBlock, listNode);
1384        removeNode(emptyListItem);
1385    } else {
1386        // When emptyListItem does not follow any list item or nested list, insert newBlock after the enclosing list node.
1387        // Remove the enclosing node if emptyListItem is the only child; otherwise just remove emptyListItem.
1388        insertNodeAfter(newBlock, listNode);
1389        removeNode(isListItem(previousListNode.get()) || isListElement(previousListNode.get()) ? emptyListItem.get() : listNode.get());
1390    }
1391
1392    appendBlockPlaceholder(newBlock);
1393    setEndingSelection(VisibleSelection(firstPositionInNode(newBlock.get()), DOWNSTREAM, endingSelection().isDirectional()));
1394
1395    style->prepareToApplyAt(endingSelection().start());
1396    if (!style->isEmpty())
1397        applyStyle(style.get());
1398
1399    return true;
1400}
1401
1402// If the caret is in an empty quoted paragraph, and either there is nothing before that
1403// paragraph, or what is before is unquoted, and the user presses delete, unquote that paragraph.
1404bool CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph()
1405{
1406    if (!endingSelection().isCaret())
1407        return false;
1408
1409    VisiblePosition caret(endingSelection().visibleStart());
1410    Node* highestBlockquote = highestEnclosingNodeOfType(caret.deepEquivalent(), &isMailBlockquote);
1411    if (!highestBlockquote)
1412        return false;
1413
1414    if (!isStartOfParagraph(caret) || !isEndOfParagraph(caret))
1415        return false;
1416
1417    VisiblePosition previous(caret.previous(CannotCrossEditingBoundary));
1418    // Only move forward if there's nothing before the caret, or if there's unquoted content before it.
1419    if (enclosingNodeOfType(previous.deepEquivalent(), &isMailBlockquote))
1420        return false;
1421
1422    RefPtr<Node> br = createBreakElement(document());
1423    // We want to replace this quoted paragraph with an unquoted one, so insert a br
1424    // to hold the caret before the highest blockquote.
1425    insertNodeBefore(br, highestBlockquote);
1426    VisiblePosition atBR(positionBeforeNode(br.get()));
1427    // If the br we inserted collapsed, for example foo<br><blockquote>...</blockquote>, insert
1428    // a second one.
1429    if (!isStartOfParagraph(atBR))
1430        insertNodeBefore(createBreakElement(document()), br);
1431    setEndingSelection(VisibleSelection(atBR, endingSelection().isDirectional()));
1432
1433    // If this is an empty paragraph there must be a line break here.
1434    if (!lineBreakExistsAtVisiblePosition(caret))
1435        return false;
1436
1437    Position caretPos(caret.deepEquivalent().downstream());
1438    // A line break is either a br or a preserved newline.
1439    ASSERT(caretPos.deprecatedNode()->hasTagName(brTag) || (caretPos.deprecatedNode()->isTextNode() && caretPos.deprecatedNode()->renderer()->style().preserveNewline()));
1440
1441    if (caretPos.deprecatedNode()->hasTagName(brTag))
1442        removeNodeAndPruneAncestors(caretPos.deprecatedNode());
1443    else if (caretPos.deprecatedNode()->isTextNode()) {
1444        ASSERT(caretPos.deprecatedEditingOffset() == 0);
1445        Text* textNode = toText(caretPos.deprecatedNode());
1446        ContainerNode* parentNode = textNode->parentNode();
1447        // The preserved newline must be the first thing in the node, since otherwise the previous
1448        // paragraph would be quoted, and we verified that it wasn't above.
1449        deleteTextFromNode(textNode, 0, 1);
1450        prune(parentNode);
1451    }
1452
1453    return true;
1454}
1455
1456// Operations use this function to avoid inserting content into an anchor when at the start or the end of
1457// that anchor, as in NSTextView.
1458// FIXME: This is only an approximation of NSTextViews insertion behavior, which varies depending on how
1459// the caret was made.
1460Position CompositeEditCommand::positionAvoidingSpecialElementBoundary(const Position& original)
1461{
1462    if (original.isNull())
1463        return original;
1464
1465    VisiblePosition visiblePos(original);
1466    Element* enclosingAnchor = enclosingAnchorElement(original);
1467    Position result = original;
1468
1469    if (!enclosingAnchor)
1470        return result;
1471
1472    // Don't avoid block level anchors, because that would insert content into the wrong paragraph.
1473    if (enclosingAnchor && !isBlock(enclosingAnchor)) {
1474        VisiblePosition firstInAnchor(firstPositionInNode(enclosingAnchor));
1475        VisiblePosition lastInAnchor(lastPositionInNode(enclosingAnchor));
1476        // If visually just after the anchor, insert *inside* the anchor unless it's the last
1477        // VisiblePosition in the document, to match NSTextView.
1478        if (visiblePos == lastInAnchor) {
1479            // Make sure anchors are pushed down before avoiding them so that we don't
1480            // also avoid structural elements like lists and blocks (5142012).
1481            if (original.deprecatedNode() != enclosingAnchor && original.deprecatedNode()->parentNode() != enclosingAnchor) {
1482                pushAnchorElementDown(*enclosingAnchor);
1483                enclosingAnchor = enclosingAnchorElement(original);
1484                if (!enclosingAnchor)
1485                    return original;
1486            }
1487            // Don't insert outside an anchor if doing so would skip over a line break.  It would
1488            // probably be safe to move the line break so that we could still avoid the anchor here.
1489            Position downstream(visiblePos.deepEquivalent().downstream());
1490            if (lineBreakExistsAtVisiblePosition(visiblePos) && downstream.deprecatedNode()->isDescendantOf(enclosingAnchor))
1491                return original;
1492
1493            result = positionInParentAfterNode(enclosingAnchor);
1494        }
1495        // If visually just before an anchor, insert *outside* the anchor unless it's the first
1496        // VisiblePosition in a paragraph, to match NSTextView.
1497        if (visiblePos == firstInAnchor) {
1498            // Make sure anchors are pushed down before avoiding them so that we don't
1499            // also avoid structural elements like lists and blocks (5142012).
1500            if (original.deprecatedNode() != enclosingAnchor && original.deprecatedNode()->parentNode() != enclosingAnchor) {
1501                pushAnchorElementDown(*enclosingAnchor);
1502                enclosingAnchor = enclosingAnchorElement(original);
1503            }
1504            if (!enclosingAnchor)
1505                return original;
1506
1507            result = positionInParentBeforeNode(enclosingAnchor);
1508        }
1509    }
1510
1511    if (result.isNull() || !editableRootForPosition(result))
1512        result = original;
1513
1514    return result;
1515}
1516
1517// Splits the tree parent by parent until we reach the specified ancestor. We use VisiblePositions
1518// to determine if the split is necessary. Returns the last split node.
1519PassRefPtr<Node> CompositeEditCommand::splitTreeToNode(Node* start, Node* end, bool shouldSplitAncestor)
1520{
1521    ASSERT(start);
1522    ASSERT(end);
1523    ASSERT(start != end);
1524
1525    RefPtr<Node> node;
1526    if (shouldSplitAncestor && end->parentNode())
1527        end = end->parentNode();
1528
1529    RefPtr<Node> endNode = end;
1530    for (node = start; node && node->parentNode() != endNode; node = node->parentNode()) {
1531        if (!node->parentNode()->isElementNode())
1532            break;
1533        // Do not split a node when doing so introduces an empty node.
1534        VisiblePosition positionInParent = firstPositionInNode(node->parentNode());
1535        VisiblePosition positionInNode = firstPositionInOrBeforeNode(node.get());
1536        if (positionInParent != positionInNode)
1537            splitElement(toElement(node->parentNode()), node);
1538    }
1539
1540    return node.release();
1541}
1542
1543PassRefPtr<Element> createBlockPlaceholderElement(Document& document)
1544{
1545    return document.createElement(brTag, false);
1546}
1547
1548} // namespace WebCore
1549