5c65c85eaf33834551a4766517407a4692a6d5e7
[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->traverseNextNode(node);
221     while (n) {
222         if (n->renderer())
223             return true;
224         n = n->traverseNextNode(node);
225     }
226     
227     return false;
228 }
229
230 void CompositeEditCommand::prune(PassRefPtr<Node> node)
231 {
232     while (node) {
233         // If you change this rule you may have to add an updateLayout() here.
234         RenderObject* renderer = node->renderer();
235         if (renderer && (!renderer->canHaveChildren() || hasARenderedDescendant(node.get()) || node->rootEditableElement() == node))
236             return;
237             
238         RefPtr<Node> next = node->parentNode();
239         
240         if (renderer) {
241             RenderObject* p = renderer->parent();
242             while (p && !p->element())
243                 p = p->parent();
244             ASSERT(p);
245             if (!p)
246                 return;
247             next = p->element();        
248         }
249
250         removeNode(node.get());
251         node = next;
252     }
253 }
254
255 void CompositeEditCommand::splitTextNode(Text *text, int offset)
256 {
257     EditCommandPtr cmd(new SplitTextNodeCommand(document(), text, offset));
258     applyCommandToComposite(cmd);
259 }
260
261 void CompositeEditCommand::splitElement(Element *element, Node *atChild)
262 {
263     EditCommandPtr cmd(new SplitElementCommand(document(), element, atChild));
264     applyCommandToComposite(cmd);
265 }
266
267 void CompositeEditCommand::mergeIdenticalElements(WebCore::Element *first, WebCore::Element *second)
268 {
269     ASSERT(!first->isAncestor(second) && second != first);
270     if (first->nextSibling() != second) {
271         removeNode(second);
272         insertNodeAfter(second, first);
273     }
274     EditCommandPtr cmd(new MergeIdenticalElementsCommand(document(), first, second));
275     applyCommandToComposite(cmd);
276 }
277
278 void CompositeEditCommand::wrapContentsInDummySpan(WebCore::Element *element)
279 {
280     EditCommandPtr cmd(new WrapContentsInDummySpanCommand(document(), element));
281     applyCommandToComposite(cmd);
282 }
283
284 void CompositeEditCommand::splitTextNodeContainingElement(WebCore::Text *text, int offset)
285 {
286     EditCommandPtr cmd(new SplitTextNodeContainingElementCommand(document(), text, offset));
287     applyCommandToComposite(cmd);
288 }
289
290 void CompositeEditCommand::joinTextNodes(Text *text1, Text *text2)
291 {
292     EditCommandPtr cmd(new JoinTextNodesCommand(document(), text1, text2));
293     applyCommandToComposite(cmd);
294 }
295
296 void CompositeEditCommand::inputText(const String &text, bool selectInsertedText)
297 {
298     InsertTextCommand *impl = new InsertTextCommand(document());
299     EditCommandPtr cmd(impl);
300     applyCommandToComposite(cmd);
301     impl->input(text, selectInsertedText);
302 }
303
304 void CompositeEditCommand::insertTextIntoNode(Text *node, int offset, const String &text)
305 {
306     EditCommandPtr cmd(new InsertIntoTextNodeCommand(document(), node, offset, text));
307     applyCommandToComposite(cmd);
308 }
309
310 void CompositeEditCommand::deleteTextFromNode(Text *node, int offset, int count)
311 {
312     EditCommandPtr cmd(new DeleteFromTextNodeCommand(document(), node, offset, count));
313     applyCommandToComposite(cmd);
314 }
315
316 void CompositeEditCommand::replaceTextInNode(Text *node, int offset, int count, const String &replacementText)
317 {
318     EditCommandPtr deleteCommand(new DeleteFromTextNodeCommand(document(), node, offset, count));
319     applyCommandToComposite(deleteCommand);
320     EditCommandPtr insertCommand(new InsertIntoTextNodeCommand(document(), node, offset, replacementText));
321     applyCommandToComposite(insertCommand);
322 }
323
324 Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos)
325 {
326     if (!isTabSpanTextNode(pos.node()))
327         return pos;
328     
329     Node *tabSpan = tabSpanNode(pos.node());
330     
331     if (pos.offset() <= pos.node()->caretMinOffset())
332         return positionBeforeNode(tabSpan);
333         
334     if (pos.offset() >= pos.node()->caretMaxOffset())
335         return positionAfterNode(tabSpan);
336
337     splitTextNodeContainingElement(static_cast<Text *>(pos.node()), pos.offset());
338     return positionBeforeNode(tabSpan);
339 }
340
341 void CompositeEditCommand::insertNodeAtTabSpanPosition(Node *node, const Position& pos)
342 {
343     // insert node before, after, or at split of tab span
344     Position insertPos = positionOutsideTabSpan(pos);
345     insertNodeAt(node, insertPos.node(), insertPos.offset());
346 }
347
348 void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete)
349 {
350     if (endingSelection().isRange()) {
351         EditCommandPtr cmd(new DeleteSelectionCommand(document(), smartDelete, mergeBlocksAfterDelete));
352         applyCommandToComposite(cmd);
353     }
354 }
355
356 void CompositeEditCommand::deleteSelection(const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete)
357 {
358     if (selection.isRange()) {
359         EditCommandPtr cmd(new DeleteSelectionCommand(document(), selection, smartDelete, mergeBlocksAfterDelete));
360         applyCommandToComposite(cmd);
361     }
362 }
363
364 void CompositeEditCommand::removeCSSProperty(CSSStyleDeclaration *decl, int property)
365 {
366     EditCommandPtr cmd(new RemoveCSSPropertyCommand(document(), decl, property));
367     applyCommandToComposite(cmd);
368 }
369
370 void CompositeEditCommand::removeNodeAttribute(Element *element, const QualifiedName& attribute)
371 {
372     String value = element->getAttribute(attribute);
373     if (value.isEmpty())
374         return;
375     EditCommandPtr cmd(new RemoveNodeAttributeCommand(document(), element, attribute));
376     applyCommandToComposite(cmd);
377 }
378
379 void CompositeEditCommand::setNodeAttribute(Element *element, const QualifiedName& attribute, const String &value)
380 {
381     EditCommandPtr cmd(new SetNodeAttributeCommand(document(), element, attribute, value));
382     applyCommandToComposite(cmd);
383 }
384
385 void CompositeEditCommand::rebalanceWhitespaceAt(const Position &position)
386 {
387     EditCommandPtr cmd(new RebalanceWhitespaceCommand(document(), position));
388     applyCommandToComposite(cmd);    
389 }
390
391 void CompositeEditCommand::rebalanceWhitespace()
392 {
393     Selection selection = endingSelection();
394     if (selection.isCaretOrRange()) {
395         EditCommandPtr startCmd(new RebalanceWhitespaceCommand(document(), endingSelection().start()));
396         applyCommandToComposite(startCmd);
397         if (selection.isRange()) {
398             EditCommandPtr endCmd(new RebalanceWhitespaceCommand(document(), endingSelection().end()));
399             applyCommandToComposite(endCmd);
400         }
401     }
402 }
403
404 void CompositeEditCommand::deleteInsignificantText(Text *textNode, int start, int end)
405 {
406     if (!textNode || !textNode->renderer() || start >= end)
407         return;
408
409     RenderText *textRenderer = static_cast<RenderText *>(textNode->renderer());
410     InlineTextBox *box = textRenderer->firstTextBox();
411     if (!box) {
412         // whole text node is empty
413         removeNode(textNode);
414         return;    
415     }
416     
417     int length = textNode->length();
418     if (start >= length || end > length)
419         return;
420
421     int removed = 0;
422     InlineTextBox *prevBox = 0;
423     RefPtr<StringImpl> str;
424
425     // This loop structure works to process all gaps preceding a box,
426     // and also will look at the gap after the last box.
427     while (prevBox || box) {
428         int gapStart = prevBox ? prevBox->m_start + prevBox->m_len : 0;
429         if (end < gapStart)
430             // No more chance for any intersections
431             break;
432
433         int gapEnd = box ? box->m_start : length;
434         bool indicesIntersect = start <= gapEnd && end >= gapStart;
435         int gapLen = gapEnd - gapStart;
436         if (indicesIntersect && gapLen > 0) {
437             gapStart = max(gapStart, start);
438             gapEnd = min(gapEnd, end);
439             if (!str)
440                 str = textNode->string()->substring(start, end - start);
441             // remove text in the gap
442             str->remove(gapStart - start - removed, gapLen);
443             removed += gapLen;
444         }
445         
446         prevBox = box;
447         if (box)
448             box = box->nextTextBox();
449     }
450
451     if (str) {
452         // Replace the text between start and end with our pruned version.
453         if (str->length() > 0) {
454             replaceTextInNode(textNode, start, end - start, str.get());
455         } else {
456             // Assert that we are not going to delete all of the text in the node.
457             // If we were, that should have been done above with the call to 
458             // removeNode and return.
459             ASSERT(start > 0 || (unsigned)end - start < textNode->length());
460             deleteTextFromNode(textNode, start, end - start);
461         }
462     }
463 }
464
465 void CompositeEditCommand::deleteInsignificantText(const Position &start, const Position &end)
466 {
467     if (start.isNull() || end.isNull())
468         return;
469
470     if (Range::compareBoundaryPoints(start, end) >= 0)
471         return;
472
473     Node *node = start.node();
474     while (node) {
475         Node *next = node->traverseNextNode();
476     
477         if (node->isTextNode()) {
478             Text *textNode = static_cast<Text *>(node);
479             bool isStartNode = node == start.node();
480             bool isEndNode = node == end.node();
481             int startOffset = isStartNode ? start.offset() : 0;
482             int endOffset = isEndNode ? end.offset() : textNode->length();
483             deleteInsignificantText(textNode, startOffset, endOffset);
484         }
485             
486         if (node == end.node())
487             break;
488         node = next;
489     }
490 }
491
492 void CompositeEditCommand::deleteInsignificantTextDownstream(const WebCore::Position &pos)
493 {
494     Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivalent().downstream();
495     deleteInsignificantText(pos, end);
496 }
497
498 Node *CompositeEditCommand::appendBlockPlaceholder(Node *node)
499 {
500     if (!node)
501         return NULL;
502     
503     // Should assert isBlockFlow || isInlineFlow when deletion improves.  See 4244964.
504     ASSERT(node->renderer());
505
506     RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
507     appendNode(placeholder.get(), node);
508     return placeholder.get();
509 }
510
511 Node *CompositeEditCommand::insertBlockPlaceholder(const Position &pos)
512 {
513     if (pos.isNull())
514         return NULL;
515
516     // Should assert isBlockFlow || isInlineFlow when deletion improves.  See 4244964.
517     ASSERT(pos.node()->renderer());
518
519     RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
520     insertNodeAt(placeholder.get(), pos.node(), pos.offset());
521     return placeholder.get();
522 }
523
524 Node *CompositeEditCommand::addBlockPlaceholderIfNeeded(Node *node)
525 {
526     if (!node)
527         return false;
528
529     updateLayout();
530
531     RenderObject *renderer = node->renderer();
532     if (!renderer || !renderer->isBlockFlow())
533         return false;
534     
535     // append the placeholder to make sure it follows
536     // any unrendered blocks
537     if (renderer->height() == 0 || (renderer->isListItem() && renderer->isEmpty()))
538         return appendBlockPlaceholder(node);
539
540     return NULL;
541 }
542
543 void CompositeEditCommand::removeBlockPlaceholder(const VisiblePosition& visiblePosition)
544 {
545     Position p = visiblePosition.deepEquivalent().downstream();
546     if (p.node()->hasTagName(brTag) && p.offset() == 0 && isEndOfBlock(visiblePosition) && isStartOfBlock(visiblePosition))
547         removeNode(p.node());
548         
549     return;
550 }
551
552 void CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position &pos)
553 {
554     if (pos.isNull())
555         return;
556     
557     updateLayout();
558     
559     VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
560     VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
561     VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePos);
562     VisiblePosition next = visibleParagraphEnd.next();
563     VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd;
564     
565     Position paragraphStart = visibleParagraphStart.deepEquivalent().upstream();
566     Position end = visibleEnd.deepEquivalent().upstream();
567     
568     // Perform some checks to see if we need to perform work in this function.
569     if (paragraphStart.node()->isBlockFlow()) {
570         if (end.node()->isBlockFlow()) {
571             if (!end.node()->isAncestor(paragraphStart.node())) {
572                 // If the paragraph end is a descendant of paragraph start, then we need to run
573                 // the rest of this function. If not, we can bail here.
574                 return;
575             }
576         }
577         else if (end.node()->enclosingBlockFlowElement() != paragraphStart.node()) {
578             // The paragraph end is in another block that is an ancestor of the paragraph start.
579             // We can bail as we have a full block to work with.
580             ASSERT(paragraphStart.node()->isAncestor(end.node()->enclosingBlockFlowElement()));
581             return;
582         }
583         else if (isEndOfDocument(visibleEnd)) {
584             // At the end of the document. We can bail here as well.
585             return;
586         }
587     }
588
589     RefPtr<Node> newBlock = createDefaultParagraphElement(document());
590
591     Node *moveNode = paragraphStart.node();
592     if (paragraphStart.offset() >= paragraphStart.node()->caretMaxOffset())
593         moveNode = moveNode->traverseNextNode();
594     Node *endNode = end.node();
595     
596     insertNodeAt(newBlock.get(), paragraphStart.node(), paragraphStart.offset());
597
598     while (moveNode && !moveNode->isBlockFlow()) {
599         Node *next = moveNode->traverseNextSibling();
600         removeNode(moveNode);
601         appendNode(moveNode, newBlock.get());
602         if (moveNode == endNode)
603             break;
604         moveNode = next;
605     }
606 }
607
608 Node* enclosingAnchorElement(Node* node)
609 {
610     while (node && !(node->isElementNode() && node->isLink()))
611         node = node->parentNode();
612     
613     return node;
614 }
615
616 void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode)
617 {
618     if (!anchorNode)
619         return;
620     
621     ASSERT(anchorNode->isLink());
622     
623     setEndingSelection(Selection::selectionFromContentsOfNode(anchorNode));
624     applyStyledElement(static_cast<Element*>(anchorNode));
625 }
626
627 // We must push partially selected anchors down before creating or removing
628 // links from a selection to create fully selected chunks that can be removed.
629 // ApplyStyleCommand doesn't do this for us because styles can be nested.
630 // Anchors cannot be nested.
631 void CompositeEditCommand::pushPartiallySelectedAnchorElementsDown()
632 {
633     Selection originalSelection = endingSelection();
634     VisiblePosition visibleStart(originalSelection.start());
635     VisiblePosition visibleEnd(originalSelection.end());
636     
637     Node* startAnchor = enclosingAnchorElement(originalSelection.start().node());
638     VisiblePosition startOfStartAnchor(Position(startAnchor, 0));
639     if (startAnchor && startOfStartAnchor != visibleStart)
640         pushAnchorElementDown(startAnchor);
641
642     Node* endAnchor = enclosingAnchorElement(originalSelection.end().node());
643     VisiblePosition endOfEndAnchor(Position(endAnchor, 0));
644     if (endAnchor && endOfEndAnchor != visibleEnd)
645         pushAnchorElementDown(endAnchor);
646
647     ASSERT(originalSelection.start().node()->inDocument() && originalSelection.end().node()->inDocument());
648     setEndingSelection(originalSelection);
649 }
650
651 // This moves a paragraph preserving its style.
652 void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
653 {
654     ASSERT(isStartOfParagraph(startOfParagraphToMove));
655     ASSERT(isEndOfParagraph(endOfParagraphToMove));
656     moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection, preserveStyle);
657 }
658
659 void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
660 {
661     if (startOfParagraphToMove == destination)
662         return;
663     
664     int startIndex = -1;
665     int endIndex = -1;
666     int destinationIndex = -1;
667     if (preserveSelection && !endingSelection().isNone()) {
668         VisiblePosition visibleStart = endingSelection().visibleStart();
669         VisiblePosition visibleEnd = endingSelection().visibleEnd();
670         
671         bool startAfterParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) > 0;
672         bool endBeforeParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) < 0;
673         
674         if (!startAfterParagraph && !endBeforeParagraph) {
675             bool startInParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) >= 0;
676             bool endInParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) <= 0;
677             
678             startIndex = startInParagraph ? TextIterator::rangeLength(new Range(document(), startOfParagraphToMove.deepEquivalent(), visibleStart.deepEquivalent())) : 0;
679             endIndex = endInParagraph ? TextIterator::rangeLength(new Range(document(), startOfParagraphToMove.deepEquivalent(), visibleEnd.deepEquivalent())) : 0;
680         }
681     }
682     
683     VisiblePosition beforeParagraph = startOfParagraphToMove.previous();
684
685     // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
686     // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.    
687     Position start = startOfParagraphToMove.deepEquivalent().downstream();
688     Position end = endOfParagraphToMove.deepEquivalent().upstream();
689     RefPtr<Range> range = new Range(document(), start.node(), start.offset(), end.node(), end.offset());
690
691     // FIXME: This is an inefficient way to preserve style on nodes in the paragraph to move.  It 
692     // shouldn't matter though, since moved paragraphs will usually be quite small.
693     RefPtr<DocumentFragment> fragment = startOfParagraphToMove != endOfParagraphToMove ? createFragmentFromMarkup(document(), range->toHTML(), "") : 0;
694     
695     setEndingSelection(Selection(start, end, DOWNSTREAM));
696     deleteSelection(false, false);
697     
698     ASSERT(destination.deepEquivalent().node()->inDocument());
699     
700     // Deleting a paragraph leaves a placeholder (it always does when a whole paragraph is deleted).
701     // We remove it and prune its parents since we want to remove all traces of the paragraph we're moving.
702     Node* placeholder = endingSelection().end().node();
703     if (placeholder->hasTagName(brTag))
704         removeNodeAndPruneAncestors(placeholder);
705     // FIXME: Deletion has bugs and it doesn't always add a placeholder.  If it fails, still do pruning.
706     else
707         prune(placeholder);
708
709     // Add a br if pruning an empty block level element caused a collapse.  For example:
710     // foo^
711     // <div>bar</div>
712     // baz
713     // Imagine moving 'bar' to ^.  'bar' will be deleted and its div pruned.  That would
714     // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br.
715     if (beforeParagraph.isNotNull() && !isEndOfParagraph(beforeParagraph))
716         insertNodeAt(createBreakElement(document()).get(), beforeParagraph.deepEquivalent().node(), beforeParagraph.deepEquivalent().offset());
717         
718     destinationIndex = TextIterator::rangeLength(new Range(document(), Position(document(), 0), destination.deepEquivalent()));
719     
720     setEndingSelection(destination);
721     EditCommandPtr cmd(new ReplaceSelectionCommand(document(), fragment.get(), true, false, !preserveStyle, true));
722     applyCommandToComposite(cmd);
723     
724     if (preserveSelection && startIndex != -1) {
725         setEndingSelection(Selection(TextIterator::rangeFromLocationAndLength(document(), destinationIndex + startIndex, 0)->startPosition(), 
726                                      TextIterator::rangeFromLocationAndLength(document(), destinationIndex + endIndex, 0)->startPosition(), 
727                                      DOWNSTREAM));
728     }
729 }
730
731 PassRefPtr<Element> createBlockPlaceholderElement(Document* document)
732 {
733     ExceptionCode ec = 0;
734     RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "br", ec);
735     ASSERT(ec == 0);
736     breakNode->setAttribute(classAttr, blockPlaceholderClassString());
737     return breakNode.release();
738 }
739
740 static const String &blockPlaceholderClassString()
741 {
742     static String blockPlaceholderClassString = "webkit-block-placeholder";
743     return blockPlaceholderClassString;
744 }
745
746 } // namespace WebCore