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