Reviewed by Adele.
[WebKit-https.git] / WebCore / editing / CompositeEditCommand.cpp
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, 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 "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 "Element.h"
36 #include "HTMLNames.h"
37 #include "InlineTextBox.h"
38 #include "InsertIntoTextNodeCommand.h"
39 #include "InsertNodeBeforeCommand.h"
40 #include "InsertParagraphSeparatorCommand.h"
41 #include "InsertTextCommand.h"
42 #include "JoinTextNodesCommand.h"
43 #include "markup.h"
44 #include "MergeIdenticalElementsCommand.h"
45 #include "Range.h"
46 #include "RebalanceWhitespaceCommand.h"
47 #include "RemoveCSSPropertyCommand.h"
48 #include "RemoveNodeAttributeCommand.h"
49 #include "RemoveNodeCommand.h"
50 #include "RemoveNodePreservingChildrenCommand.h"
51 #include "ReplaceSelectionCommand.h"
52 #include "SetNodeAttributeCommand.h"
53 #include "SplitElementCommand.h"
54 #include "SplitTextNodeCommand.h"
55 #include "SplitTextNodeContainingElementCommand.h"
56 #include "TextIterator.h"
57 #include "WrapContentsInDummySpanCommand.h"
58 #include "htmlediting.h"
59 #include "visible_units.h"
60
61 using namespace std;
62
63 namespace WebCore {
64
65 using namespace HTMLNames;
66
67 static const String &blockPlaceholderClassString();
68
69 CompositeEditCommand::CompositeEditCommand(Document *document) 
70     : EditCommand(document)
71 {
72 }
73
74 void CompositeEditCommand::doUnapply()
75 {
76     if (m_cmds.count() == 0)
77         return;
78     
79     DeprecatedValueList<EditCommandPtr>::ConstIterator end;
80     for (DeprecatedValueList<EditCommandPtr>::ConstIterator it = m_cmds.fromLast(); it != end; --it)
81         (*it)->unapply();
82
83     setState(NotApplied);
84 }
85
86 void CompositeEditCommand::doReapply()
87 {
88     if (m_cmds.count() == 0)
89         return;
90
91     for (DeprecatedValueList<EditCommandPtr>::ConstIterator it = m_cmds.begin(); it != m_cmds.end(); ++it)
92         (*it)->reapply();
93
94     setState(Applied);
95 }
96
97 //
98 // sugary-sweet convenience functions to help create and apply edit commands in composite commands
99 //
100 void CompositeEditCommand::applyCommandToComposite(EditCommandPtr &cmd)
101 {
102     cmd.setStartingSelection(endingSelection());
103     cmd.setEndingSelection(endingSelection());
104     cmd.setParent(this);
105     cmd.apply();
106     m_cmds.append(cmd);
107 }
108
109 void CompositeEditCommand::applyStyle(CSSStyleDeclaration *style, EditAction editingAction)
110 {
111     EditCommandPtr cmd(new ApplyStyleCommand(document(), style, editingAction));
112     applyCommandToComposite(cmd);
113 }
114
115 void CompositeEditCommand::applyStyle(CSSStyleDeclaration *style, Position start, Position end, EditAction editingAction)
116 {
117     EditCommandPtr cmd(new ApplyStyleCommand(document(), style, start, end, editingAction));
118     applyCommandToComposite(cmd);
119 }
120
121 void CompositeEditCommand::applyStyledElement(Element* element)
122 {
123     EditCommandPtr cmd(new ApplyStyleCommand(document(), element, false));
124     applyCommandToComposite(cmd);
125 }
126
127 void CompositeEditCommand::removeStyledElement(Element* element)
128 {
129     EditCommandPtr cmd(new ApplyStyleCommand(document(), element, true));
130     applyCommandToComposite(cmd);
131 }
132
133 void CompositeEditCommand::insertParagraphSeparator()
134 {
135     EditCommandPtr cmd(new InsertParagraphSeparatorCommand(document()));
136     applyCommandToComposite(cmd);
137 }
138
139 void CompositeEditCommand::insertNodeBefore(Node *insertChild, Node *refChild)
140 {
141     ASSERT(!refChild->hasTagName(bodyTag));
142     EditCommandPtr cmd(new InsertNodeBeforeCommand(document(), insertChild, refChild));
143     applyCommandToComposite(cmd);
144 }
145
146 void CompositeEditCommand::insertNodeAfter(Node *insertChild, Node *refChild)
147 {
148     ASSERT(!refChild->hasTagName(bodyTag));
149     if (refChild->parentNode()->lastChild() == refChild) {
150         appendNode(insertChild, refChild->parentNode());
151     }
152     else {
153         ASSERT(refChild->nextSibling());
154         insertNodeBefore(insertChild, refChild->nextSibling());
155     }
156 }
157
158 void CompositeEditCommand::insertNodeAt(Node *insertChild, Node *refChild, int offset)
159 {
160     if (canHaveChildrenForEditing(refChild)) {
161         Node *child = refChild->firstChild();
162         for (int i = 0; child && i < offset; i++)
163             child = child->nextSibling();
164         if (child)
165             insertNodeBefore(insertChild, child);
166         else
167             appendNode(insertChild, refChild);
168     }
169     else if (refChild->caretMinOffset() >= offset) {
170         insertNodeBefore(insertChild, refChild);
171     } 
172     else if (refChild->isTextNode() && refChild->caretMaxOffset() > offset) {
173         splitTextNode(static_cast<Text *>(refChild), offset);
174         insertNodeBefore(insertChild, refChild);
175     } 
176     else {
177         insertNodeAfter(insertChild, refChild);
178     }
179 }
180
181 void CompositeEditCommand::appendNode(Node *appendChild, Node *parent)
182 {
183     ASSERT(canHaveChildrenForEditing(parent));
184     EditCommandPtr cmd(new AppendNodeCommand(document(), appendChild, parent));
185     applyCommandToComposite(cmd);
186 }
187
188 void CompositeEditCommand::removeChildrenInRange(Node *node, int from, int to)
189 {
190     Node *nodeToRemove = node->childNode(from);
191     for (int i = from; i < to; i++) {
192         ASSERT(nodeToRemove);
193         Node *next = nodeToRemove->nextSibling();
194         removeNode(nodeToRemove);
195         nodeToRemove = next;
196     }
197 }
198
199 void CompositeEditCommand::removeNode(Node *removeChild)
200 {
201     EditCommandPtr cmd(new RemoveNodeCommand(document(), removeChild));
202     applyCommandToComposite(cmd);
203 }
204
205 void CompositeEditCommand::removeNodePreservingChildren(Node *removeChild)
206 {
207     EditCommandPtr cmd(new RemoveNodePreservingChildrenCommand(document(), removeChild));
208     applyCommandToComposite(cmd);
209 }
210
211 void CompositeEditCommand::removeNodeAndPruneAncestors(Node* node)
212 {
213     RefPtr<Node> parent = node->parentNode();
214     removeNode(node);
215     prune(parent);
216 }
217
218 bool hasARenderedDescendant(Node* node)
219 {
220     Node* n = node->firstChild();
221     while (n) {
222         if (n->renderer())
223             return true;
224         n = n->traverseNextNode(node);
225     }
226     return false;
227 }
228
229 void CompositeEditCommand::prune(PassRefPtr<Node> node)
230 {
231     while (node) {
232         // If you change this rule you may have to add an updateLayout() here.
233         RenderObject* renderer = node->renderer();
234         if (renderer && (!renderer->canHaveChildren() || hasARenderedDescendant(node.get()) || node->rootEditableElement() == node))
235             return;
236             
237         RefPtr<Node> next = node->parentNode();
238         removeNode(node.get());
239         node = next;
240     }
241 }
242
243 void CompositeEditCommand::splitTextNode(Text *text, int offset)
244 {
245     EditCommandPtr cmd(new SplitTextNodeCommand(document(), text, offset));
246     applyCommandToComposite(cmd);
247 }
248
249 void CompositeEditCommand::splitElement(Element *element, Node *atChild)
250 {
251     EditCommandPtr cmd(new SplitElementCommand(document(), element, atChild));
252     applyCommandToComposite(cmd);
253 }
254
255 void CompositeEditCommand::mergeIdenticalElements(WebCore::Element *first, WebCore::Element *second)
256 {
257     ASSERT(!first->isAncestor(second) && second != first);
258     if (first->nextSibling() != second) {
259         removeNode(second);
260         insertNodeAfter(second, first);
261     }
262     EditCommandPtr cmd(new MergeIdenticalElementsCommand(document(), first, second));
263     applyCommandToComposite(cmd);
264 }
265
266 void CompositeEditCommand::wrapContentsInDummySpan(WebCore::Element *element)
267 {
268     EditCommandPtr cmd(new WrapContentsInDummySpanCommand(document(), element));
269     applyCommandToComposite(cmd);
270 }
271
272 void CompositeEditCommand::splitTextNodeContainingElement(WebCore::Text *text, int offset)
273 {
274     EditCommandPtr cmd(new SplitTextNodeContainingElementCommand(document(), text, offset));
275     applyCommandToComposite(cmd);
276 }
277
278 void CompositeEditCommand::joinTextNodes(Text *text1, Text *text2)
279 {
280     EditCommandPtr cmd(new JoinTextNodesCommand(document(), text1, text2));
281     applyCommandToComposite(cmd);
282 }
283
284 void CompositeEditCommand::inputText(const String &text, bool selectInsertedText)
285 {
286     InsertTextCommand *impl = new InsertTextCommand(document());
287     EditCommandPtr cmd(impl);
288     applyCommandToComposite(cmd);
289     impl->input(text, selectInsertedText);
290 }
291
292 void CompositeEditCommand::insertTextIntoNode(Text *node, int offset, const String &text)
293 {
294     EditCommandPtr cmd(new InsertIntoTextNodeCommand(document(), node, offset, text));
295     applyCommandToComposite(cmd);
296 }
297
298 void CompositeEditCommand::deleteTextFromNode(Text *node, int offset, int count)
299 {
300     EditCommandPtr cmd(new DeleteFromTextNodeCommand(document(), node, offset, count));
301     applyCommandToComposite(cmd);
302 }
303
304 void CompositeEditCommand::replaceTextInNode(Text *node, int offset, int count, const String &replacementText)
305 {
306     EditCommandPtr deleteCommand(new DeleteFromTextNodeCommand(document(), node, offset, count));
307     applyCommandToComposite(deleteCommand);
308     EditCommandPtr insertCommand(new InsertIntoTextNodeCommand(document(), node, offset, replacementText));
309     applyCommandToComposite(insertCommand);
310 }
311
312 Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos)
313 {
314     if (!isTabSpanTextNode(pos.node()))
315         return pos;
316     
317     Node *tabSpan = tabSpanNode(pos.node());
318     
319     if (pos.offset() <= pos.node()->caretMinOffset())
320         return positionBeforeNode(tabSpan);
321         
322     if (pos.offset() >= pos.node()->caretMaxOffset())
323         return positionAfterNode(tabSpan);
324
325     splitTextNodeContainingElement(static_cast<Text *>(pos.node()), pos.offset());
326     return positionBeforeNode(tabSpan);
327 }
328
329 void CompositeEditCommand::insertNodeAtTabSpanPosition(Node *node, const Position& pos)
330 {
331     // insert node before, after, or at split of tab span
332     Position insertPos = positionOutsideTabSpan(pos);
333     insertNodeAt(node, insertPos.node(), insertPos.offset());
334 }
335
336 void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete)
337 {
338     if (endingSelection().isRange()) {
339         EditCommandPtr cmd(new DeleteSelectionCommand(document(), smartDelete, mergeBlocksAfterDelete));
340         applyCommandToComposite(cmd);
341     }
342 }
343
344 void CompositeEditCommand::deleteSelection(const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete)
345 {
346     if (selection.isRange()) {
347         EditCommandPtr cmd(new DeleteSelectionCommand(document(), selection, smartDelete, mergeBlocksAfterDelete));
348         applyCommandToComposite(cmd);
349     }
350 }
351
352 void CompositeEditCommand::removeCSSProperty(CSSStyleDeclaration *decl, int property)
353 {
354     EditCommandPtr cmd(new RemoveCSSPropertyCommand(document(), decl, property));
355     applyCommandToComposite(cmd);
356 }
357
358 void CompositeEditCommand::removeNodeAttribute(Element *element, const QualifiedName& attribute)
359 {
360     String value = element->getAttribute(attribute);
361     if (value.isEmpty())
362         return;
363     EditCommandPtr cmd(new RemoveNodeAttributeCommand(document(), element, attribute));
364     applyCommandToComposite(cmd);
365 }
366
367 void CompositeEditCommand::setNodeAttribute(Element *element, const QualifiedName& attribute, const String &value)
368 {
369     EditCommandPtr cmd(new SetNodeAttributeCommand(document(), element, attribute, value));
370     applyCommandToComposite(cmd);
371 }
372
373 void CompositeEditCommand::rebalanceWhitespaceAt(const Position &position)
374 {
375     EditCommandPtr cmd(new RebalanceWhitespaceCommand(document(), position));
376     applyCommandToComposite(cmd);    
377 }
378
379 void CompositeEditCommand::rebalanceWhitespace()
380 {
381     Selection selection = endingSelection();
382     if (selection.isCaretOrRange()) {
383         EditCommandPtr startCmd(new RebalanceWhitespaceCommand(document(), endingSelection().start()));
384         applyCommandToComposite(startCmd);
385         if (selection.isRange()) {
386             EditCommandPtr endCmd(new RebalanceWhitespaceCommand(document(), endingSelection().end()));
387             applyCommandToComposite(endCmd);
388         }
389     }
390 }
391
392 void CompositeEditCommand::deleteInsignificantText(Text *textNode, int start, int end)
393 {
394     if (!textNode || !textNode->renderer() || start >= end)
395         return;
396
397     RenderText *textRenderer = static_cast<RenderText *>(textNode->renderer());
398     InlineTextBox *box = textRenderer->firstTextBox();
399     if (!box) {
400         // whole text node is empty
401         removeNode(textNode);
402         return;    
403     }
404     
405     int length = textNode->length();
406     if (start >= length || end > length)
407         return;
408
409     int removed = 0;
410     InlineTextBox *prevBox = 0;
411     RefPtr<StringImpl> str;
412
413     // This loop structure works to process all gaps preceding a box,
414     // and also will look at the gap after the last box.
415     while (prevBox || box) {
416         int gapStart = prevBox ? prevBox->m_start + prevBox->m_len : 0;
417         if (end < gapStart)
418             // No more chance for any intersections
419             break;
420
421         int gapEnd = box ? box->m_start : length;
422         bool indicesIntersect = start <= gapEnd && end >= gapStart;
423         int gapLen = gapEnd - gapStart;
424         if (indicesIntersect && gapLen > 0) {
425             gapStart = max(gapStart, start);
426             gapEnd = min(gapEnd, end);
427             if (!str)
428                 str = textNode->string()->substring(start, end - start);
429             // remove text in the gap
430             str->remove(gapStart - start - removed, gapLen);
431             removed += gapLen;
432         }
433         
434         prevBox = box;
435         if (box)
436             box = box->nextTextBox();
437     }
438
439     if (str) {
440         // Replace the text between start and end with our pruned version.
441         if (str->length() > 0) {
442             replaceTextInNode(textNode, start, end - start, str.get());
443         } else {
444             // Assert that we are not going to delete all of the text in the node.
445             // If we were, that should have been done above with the call to 
446             // removeNode and return.
447             ASSERT(start > 0 || (unsigned)end - start < textNode->length());
448             deleteTextFromNode(textNode, start, end - start);
449         }
450     }
451 }
452
453 void CompositeEditCommand::deleteInsignificantText(const Position &start, const Position &end)
454 {
455     if (start.isNull() || end.isNull())
456         return;
457
458     if (Range::compareBoundaryPoints(start, end) >= 0)
459         return;
460
461     Node *node = start.node();
462     while (node) {
463         Node *next = node->traverseNextNode();
464     
465         if (node->isTextNode()) {
466             Text *textNode = static_cast<Text *>(node);
467             bool isStartNode = node == start.node();
468             bool isEndNode = node == end.node();
469             int startOffset = isStartNode ? start.offset() : 0;
470             int endOffset = isEndNode ? end.offset() : textNode->length();
471             deleteInsignificantText(textNode, startOffset, endOffset);
472         }
473             
474         if (node == end.node())
475             break;
476         node = next;
477     }
478 }
479
480 void CompositeEditCommand::deleteInsignificantTextDownstream(const WebCore::Position &pos)
481 {
482     Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivalent().downstream();
483     deleteInsignificantText(pos, end);
484 }
485
486 Node *CompositeEditCommand::appendBlockPlaceholder(Node *node)
487 {
488     if (!node)
489         return NULL;
490     
491     // Should assert isBlockFlow || isInlineFlow when deletion improves.  See 4244964.
492     ASSERT(node->renderer());
493
494     RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
495     appendNode(placeholder.get(), node);
496     return placeholder.get();
497 }
498
499 Node *CompositeEditCommand::insertBlockPlaceholder(const Position &pos)
500 {
501     if (pos.isNull())
502         return NULL;
503
504     // Should assert isBlockFlow || isInlineFlow when deletion improves.  See 4244964.
505     ASSERT(pos.node()->renderer());
506
507     RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
508     insertNodeAt(placeholder.get(), pos.node(), pos.offset());
509     return placeholder.get();
510 }
511
512 Node *CompositeEditCommand::addBlockPlaceholderIfNeeded(Node *node)
513 {
514     if (!node)
515         return false;
516
517     updateLayout();
518
519     RenderObject *renderer = node->renderer();
520     if (!renderer || !renderer->isBlockFlow())
521         return false;
522     
523     // append the placeholder to make sure it follows
524     // any unrendered blocks
525     if (renderer->height() == 0 || (renderer->isListItem() && renderer->isEmpty()))
526         return appendBlockPlaceholder(node);
527
528     return NULL;
529 }
530
531 void CompositeEditCommand::removeBlockPlaceholder(const VisiblePosition& visiblePosition)
532 {
533     Position p = visiblePosition.deepEquivalent().downstream();
534     if (p.node()->hasTagName(brTag) && p.offset() == 0 && isEndOfBlock(visiblePosition) && isStartOfBlock(visiblePosition))
535         removeNode(p.node());
536         
537     return;
538 }
539
540 void CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position &pos)
541 {
542     if (pos.isNull())
543         return;
544     
545     updateLayout();
546     
547     VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
548     VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
549     VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePos);
550     VisiblePosition next = visibleParagraphEnd.next();
551     VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd;
552     
553     Position paragraphStart = visibleParagraphStart.deepEquivalent().upstream();
554     Position end = visibleEnd.deepEquivalent().upstream();
555
556     // Perform some sanity checks. If there are no VisiblePositions in
557     // the same block as pos then paragraphStart will be outside the paragraph
558     if (Range::compareBoundaryPoints(pos, paragraphStart) < 0)
559         return;
560
561     // Perform some checks to see if we need to perform work in this function.
562     if (paragraphStart.node()->isBlockFlow()) {
563         if (end.node()->isBlockFlow()) {
564             if (!end.node()->isAncestor(paragraphStart.node())) {
565                 // If the paragraph end is a descendant of paragraph start, then we need to run
566                 // the rest of this function. If not, we can bail here.
567                 return;
568             }
569         }
570         else if (end.node()->enclosingBlockFlowElement() != paragraphStart.node()) {
571             // The paragraph end is in another block that is an ancestor of the paragraph start.
572             // We can bail as we have a full block to work with.
573             ASSERT(paragraphStart.node()->isAncestor(end.node()->enclosingBlockFlowElement()));
574             return;
575         }
576         else if (isEndOfDocument(visibleEnd)) {
577             // At the end of the document. We can bail here as well.
578             return;
579         }
580     }
581
582     RefPtr<Node> newBlock = createDefaultParagraphElement(document());
583
584     Node *moveNode = paragraphStart.node();
585     if (paragraphStart.offset() >= paragraphStart.node()->caretMaxOffset())
586         moveNode = moveNode->traverseNextNode();
587     Node *endNode = end.node();
588     
589     insertNodeAt(newBlock.get(), paragraphStart.node(), paragraphStart.offset());
590
591     while (moveNode && !moveNode->isBlockFlow()) {
592         Node *next = moveNode->traverseNextSibling();
593         removeNode(moveNode);
594         appendNode(moveNode, newBlock.get());
595         if (moveNode == endNode)
596             break;
597         moveNode = next;
598     }
599 }
600
601 Node* enclosingAnchorElement(Node* node)
602 {
603     while (node && !(node->isElementNode() && node->isLink()))
604         node = node->parentNode();
605     
606     return node;
607 }
608
609 void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode)
610 {
611     if (!anchorNode)
612         return;
613     
614     ASSERT(anchorNode->isLink());
615     
616     setEndingSelection(Selection::selectionFromContentsOfNode(anchorNode));
617     applyStyledElement(static_cast<Element*>(anchorNode));
618 }
619
620 // We must push partially selected anchors down before creating or removing
621 // links from a selection to create fully selected chunks that can be removed.
622 // ApplyStyleCommand doesn't do this for us because styles can be nested.
623 // Anchors cannot be nested.
624 void CompositeEditCommand::pushPartiallySelectedAnchorElementsDown()
625 {
626     Selection originalSelection = endingSelection();
627     VisiblePosition visibleStart(originalSelection.start());
628     VisiblePosition visibleEnd(originalSelection.end());
629     
630     Node* startAnchor = enclosingAnchorElement(originalSelection.start().node());
631     VisiblePosition startOfStartAnchor(Position(startAnchor, 0));
632     if (startAnchor && startOfStartAnchor != visibleStart)
633         pushAnchorElementDown(startAnchor);
634
635     Node* endAnchor = enclosingAnchorElement(originalSelection.end().node());
636     VisiblePosition endOfEndAnchor(Position(endAnchor, 0));
637     if (endAnchor && endOfEndAnchor != visibleEnd)
638         pushAnchorElementDown(endAnchor);
639
640     ASSERT(originalSelection.start().node()->inDocument() && originalSelection.end().node()->inDocument());
641     setEndingSelection(originalSelection);
642 }
643
644 // This moves a paragraph preserving its style.
645 void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
646 {
647     ASSERT(isStartOfParagraph(startOfParagraphToMove));
648     ASSERT(isEndOfParagraph(endOfParagraphToMove));
649     moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection, preserveStyle);
650 }
651
652 void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
653 {
654     if (startOfParagraphToMove == destination)
655         return;
656     
657     int startIndex = -1;
658     int endIndex = -1;
659     int destinationIndex = -1;
660     if (preserveSelection && !endingSelection().isNone()) {
661         VisiblePosition visibleStart = endingSelection().visibleStart();
662         VisiblePosition visibleEnd = endingSelection().visibleEnd();
663         
664         bool startAfterParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) > 0;
665         bool endBeforeParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) < 0;
666         
667         if (!startAfterParagraph && !endBeforeParagraph) {
668             bool startInParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) >= 0;
669             bool endInParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) <= 0;
670             
671             startIndex = 0;
672             if (startInParagraph) {
673                 RefPtr<Range> startRange = new Range(document(), startOfParagraphToMove.deepEquivalent(), visibleStart.deepEquivalent());
674                 startIndex = TextIterator::rangeLength(startRange.get());
675             }
676
677             endIndex = 0;
678             if (endInParagraph) {
679                 RefPtr<Range> endRange = new Range(document(), startOfParagraphToMove.deepEquivalent(), visibleEnd.deepEquivalent());
680                 endIndex = TextIterator::rangeLength(endRange.get());
681             }
682         }
683     }
684     
685     VisiblePosition beforeParagraph = startOfParagraphToMove.previous();
686
687     // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
688     // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.    
689     Position start = startOfParagraphToMove.deepEquivalent().downstream();
690     Position end = endOfParagraphToMove.deepEquivalent().upstream();
691     RefPtr<Range> range = new Range(document(), start.node(), start.offset(), end.node(), end.offset());
692
693     // FIXME: This is an inefficient way to preserve style on nodes in the paragraph to move.  It 
694     // shouldn't matter though, since moved paragraphs will usually be quite small.
695     RefPtr<DocumentFragment> fragment = startOfParagraphToMove != endOfParagraphToMove ? createFragmentFromMarkup(document(), range->toHTML(), "") : 0;
696     
697     setEndingSelection(Selection(start, end, DOWNSTREAM));
698     deleteSelection(false, false);
699
700     ASSERT(destination.deepEquivalent().node()->inDocument());
701     
702     // Deleting a paragraph leaves a placeholder (it always does when a whole paragraph is deleted).
703     // We remove it and prune its parents since we want to remove all traces of the paragraph we're moving.
704     Node* placeholder = endingSelection().end().downstream().node();
705     ASSERT(placeholder->hasTagName(brTag));
706     // There are bugs in deletion when it removes a fully selected table/list.  It expands and removes the entire table/list, but will let content
707     // before and after the table/list collapse onto one line.
708     if (placeholder->hasTagName(brTag) && isStartOfParagraph(endingSelection().visibleStart()) && isEndOfParagraph(endingSelection().visibleStart()))
709         removeNodeAndPruneAncestors(placeholder);
710
711     // Add a br if pruning an empty block level element caused a collapse.  For example:
712     // foo^
713     // <div>bar</div>
714     // baz
715     // Imagine moving 'bar' to ^.  'bar' will be deleted and its div pruned.  That would
716     // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br.
717     if (beforeParagraph.isNotNull() && !isEndOfParagraph(beforeParagraph))
718         insertNodeAt(createBreakElement(document()).get(), beforeParagraph.deepEquivalent().node(), beforeParagraph.deepEquivalent().offset());
719         
720     RefPtr<Range> startToDestinationRange(new Range(document(), Position(document(), 0), destination.deepEquivalent()));
721     destinationIndex = TextIterator::rangeLength(startToDestinationRange.get());
722     
723     setEndingSelection(destination);
724     EditCommandPtr cmd(new ReplaceSelectionCommand(document(), fragment.get(), true, false, !preserveStyle, true));
725     applyCommandToComposite(cmd);
726     
727     if (preserveSelection && startIndex != -1) {
728         setEndingSelection(Selection(TextIterator::rangeFromLocationAndLength(document(), destinationIndex + startIndex, 0)->startPosition(), 
729                                      TextIterator::rangeFromLocationAndLength(document(), destinationIndex + endIndex, 0)->startPosition(), 
730                                      DOWNSTREAM));
731     }
732 }
733
734 PassRefPtr<Element> createBlockPlaceholderElement(Document* document)
735 {
736     ExceptionCode ec = 0;
737     RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "br", ec);
738     ASSERT(ec == 0);
739     breakNode->setAttribute(classAttr, blockPlaceholderClassString());
740     return breakNode.release();
741 }
742
743 static const String &blockPlaceholderClassString()
744 {
745     static String blockPlaceholderClassString = "webkit-block-placeholder";
746     return blockPlaceholderClassString;
747 }
748
749 } // namespace WebCore