a357cdf7a7ccef01d67c4989ca64540194b5cab3
[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     Node* textNode = position.node();
378     if (!textNode || !textNode->isTextNode())
379         return;
380     if (static_cast<Text*>(textNode)->length() == 0)
381         return;
382     RenderObject* renderer = textNode->renderer();
383     if (renderer && !renderer->style()->collapseWhiteSpace())
384         return;
385     EditCommandPtr cmd(new RebalanceWhitespaceCommand(document(), position));
386     applyCommandToComposite(cmd);    
387 }
388
389 void CompositeEditCommand::rebalanceWhitespace()
390 {
391     Selection selection = endingSelection();
392     if (selection.isCaretOrRange()) {
393         rebalanceWhitespaceAt(endingSelection().start());
394         if (selection.isRange())
395             rebalanceWhitespaceAt(endingSelection().end());
396     }
397 }
398
399 void CompositeEditCommand::deleteInsignificantText(Text *textNode, int start, int end)
400 {
401     if (!textNode || !textNode->renderer() || start >= end)
402         return;
403
404     RenderText *textRenderer = static_cast<RenderText *>(textNode->renderer());
405     InlineTextBox *box = textRenderer->firstTextBox();
406     if (!box) {
407         // whole text node is empty
408         removeNode(textNode);
409         return;    
410     }
411     
412     int length = textNode->length();
413     if (start >= length || end > length)
414         return;
415
416     int removed = 0;
417     InlineTextBox *prevBox = 0;
418     RefPtr<StringImpl> str;
419
420     // This loop structure works to process all gaps preceding a box,
421     // and also will look at the gap after the last box.
422     while (prevBox || box) {
423         int gapStart = prevBox ? prevBox->m_start + prevBox->m_len : 0;
424         if (end < gapStart)
425             // No more chance for any intersections
426             break;
427
428         int gapEnd = box ? box->m_start : length;
429         bool indicesIntersect = start <= gapEnd && end >= gapStart;
430         int gapLen = gapEnd - gapStart;
431         if (indicesIntersect && gapLen > 0) {
432             gapStart = max(gapStart, start);
433             gapEnd = min(gapEnd, end);
434             if (!str)
435                 str = textNode->string()->substring(start, end - start);
436             // remove text in the gap
437             str->remove(gapStart - start - removed, gapLen);
438             removed += gapLen;
439         }
440         
441         prevBox = box;
442         if (box)
443             box = box->nextTextBox();
444     }
445
446     if (str) {
447         // Replace the text between start and end with our pruned version.
448         if (str->length() > 0) {
449             replaceTextInNode(textNode, start, end - start, str.get());
450         } else {
451             // Assert that we are not going to delete all of the text in the node.
452             // If we were, that should have been done above with the call to 
453             // removeNode and return.
454             ASSERT(start > 0 || (unsigned)end - start < textNode->length());
455             deleteTextFromNode(textNode, start, end - start);
456         }
457     }
458 }
459
460 void CompositeEditCommand::deleteInsignificantText(const Position &start, const Position &end)
461 {
462     if (start.isNull() || end.isNull())
463         return;
464
465     if (Range::compareBoundaryPoints(start, end) >= 0)
466         return;
467
468     Node *node = start.node();
469     while (node) {
470         Node *next = node->traverseNextNode();
471     
472         if (node->isTextNode()) {
473             Text *textNode = static_cast<Text *>(node);
474             bool isStartNode = node == start.node();
475             bool isEndNode = node == end.node();
476             int startOffset = isStartNode ? start.offset() : 0;
477             int endOffset = isEndNode ? end.offset() : textNode->length();
478             deleteInsignificantText(textNode, startOffset, endOffset);
479         }
480             
481         if (node == end.node())
482             break;
483         node = next;
484     }
485 }
486
487 void CompositeEditCommand::deleteInsignificantTextDownstream(const Position &pos)
488 {
489     Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivalent().downstream();
490     deleteInsignificantText(pos, end);
491 }
492
493 Node *CompositeEditCommand::appendBlockPlaceholder(Node *node)
494 {
495     if (!node)
496         return NULL;
497     
498     // Should assert isBlockFlow || isInlineFlow when deletion improves.  See 4244964.
499     ASSERT(node->renderer());
500
501     RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
502     appendNode(placeholder.get(), node);
503     return placeholder.get();
504 }
505
506 Node *CompositeEditCommand::insertBlockPlaceholder(const Position &pos)
507 {
508     if (pos.isNull())
509         return NULL;
510
511     // Should assert isBlockFlow || isInlineFlow when deletion improves.  See 4244964.
512     ASSERT(pos.node()->renderer());
513
514     RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
515     insertNodeAt(placeholder.get(), pos.node(), pos.offset());
516     return placeholder.get();
517 }
518
519 Node *CompositeEditCommand::addBlockPlaceholderIfNeeded(Node *node)
520 {
521     if (!node)
522         return false;
523
524     updateLayout();
525
526     RenderObject *renderer = node->renderer();
527     if (!renderer || !renderer->isBlockFlow())
528         return false;
529     
530     // append the placeholder to make sure it follows
531     // any unrendered blocks
532     if (renderer->height() == 0 || (renderer->isListItem() && renderer->isEmpty()))
533         return appendBlockPlaceholder(node);
534
535     return NULL;
536 }
537
538 void CompositeEditCommand::removeBlockPlaceholder(const VisiblePosition& visiblePosition)
539 {
540     Position p = visiblePosition.deepEquivalent().downstream();
541     if (p.node()->hasTagName(brTag) && p.offset() == 0 && isEndOfBlock(visiblePosition) && isStartOfBlock(visiblePosition))
542         removeNode(p.node());
543         
544     return;
545 }
546
547 void CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position &pos)
548 {
549     if (pos.isNull())
550         return;
551     
552     updateLayout();
553     
554     VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
555     VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
556     VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePos);
557     VisiblePosition next = visibleParagraphEnd.next();
558     VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd;
559     
560     Position paragraphStart = visibleParagraphStart.deepEquivalent().upstream();
561     Position end = visibleEnd.deepEquivalent().upstream();
562
563     // If there are no VisiblePositions in the same block as pos then 
564     // paragraphStart will be outside the paragraph
565     if (Range::compareBoundaryPoints(pos, paragraphStart) < 0)
566         return;
567
568     // Perform some checks to see if we need to perform work in this function.
569     if (isBlock(paragraphStart.node())) {
570         if (isBlock(end.node())) {
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 (enclosingBlock(end.node()) != paragraphStart.node()) {
578             // The visibleEnd.  It must be an ancestor of the paragraph start.
579             // We can bail as we have a full block to work with.
580             ASSERT(paragraphStart.node()->isAncestor(enclosingBlock(end.node())));
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     if (!isAtomicNode(paragraphStart.node()))
597         insertNodeAt(newBlock.get(), paragraphStart.node(), paragraphStart.offset());
598     else {
599         ASSERT(paragraphStart.offset() <= 1);
600         ASSERT(paragraphStart.node()->parentNode());
601         insertNodeAt(newBlock.get(), paragraphStart.node()->parentNode(), paragraphStart.node()->nodeIndex() + paragraphStart.offset());
602     }
603
604     while (moveNode && !isBlock(moveNode)) {
605         Node *next = moveNode->traverseNextSibling();
606         removeNode(moveNode);
607         appendNode(moveNode, newBlock.get());
608         if (moveNode == endNode)
609             break;
610         moveNode = next;
611     }
612 }
613
614 Node* enclosingAnchorElement(Node* node)
615 {
616     while (node && !(node->isElementNode() && node->isLink()))
617         node = node->parentNode();
618     
619     return node;
620 }
621
622 void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode)
623 {
624     if (!anchorNode)
625         return;
626     
627     ASSERT(anchorNode->isLink());
628     
629     setEndingSelection(Selection::selectionFromContentsOfNode(anchorNode));
630     applyStyledElement(static_cast<Element*>(anchorNode));
631 }
632
633 // We must push partially selected anchors down before creating or removing
634 // links from a selection to create fully selected chunks that can be removed.
635 // ApplyStyleCommand doesn't do this for us because styles can be nested.
636 // Anchors cannot be nested.
637 void CompositeEditCommand::pushPartiallySelectedAnchorElementsDown()
638 {
639     Selection originalSelection = endingSelection();
640     VisiblePosition visibleStart(originalSelection.start());
641     VisiblePosition visibleEnd(originalSelection.end());
642     
643     Node* startAnchor = enclosingAnchorElement(originalSelection.start().node());
644     VisiblePosition startOfStartAnchor(Position(startAnchor, 0));
645     if (startAnchor && startOfStartAnchor != visibleStart)
646         pushAnchorElementDown(startAnchor);
647
648     Node* endAnchor = enclosingAnchorElement(originalSelection.end().node());
649     VisiblePosition endOfEndAnchor(Position(endAnchor, 0));
650     if (endAnchor && endOfEndAnchor != visibleEnd)
651         pushAnchorElementDown(endAnchor);
652
653     ASSERT(originalSelection.start().node()->inDocument() && originalSelection.end().node()->inDocument());
654     setEndingSelection(originalSelection);
655 }
656
657 // This moves a paragraph preserving its style.
658 void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
659 {
660     ASSERT(isStartOfParagraph(startOfParagraphToMove));
661     ASSERT(isEndOfParagraph(endOfParagraphToMove));
662     moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection, preserveStyle);
663 }
664
665 void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
666 {
667     if (startOfParagraphToMove == destination)
668         return;
669     
670     int startIndex = -1;
671     int endIndex = -1;
672     int destinationIndex = -1;
673     if (preserveSelection && !endingSelection().isNone()) {
674         VisiblePosition visibleStart = endingSelection().visibleStart();
675         VisiblePosition visibleEnd = endingSelection().visibleEnd();
676         
677         bool startAfterParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) > 0;
678         bool endBeforeParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) < 0;
679         
680         if (!startAfterParagraph && !endBeforeParagraph) {
681             bool startInParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) >= 0;
682             bool endInParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) <= 0;
683             
684             startIndex = 0;
685             if (startInParagraph) {
686                 RefPtr<Range> startRange = new Range(document(), startOfParagraphToMove.deepEquivalent(), visibleStart.deepEquivalent());
687                 startIndex = TextIterator::rangeLength(startRange.get());
688             }
689
690             endIndex = 0;
691             if (endInParagraph) {
692                 RefPtr<Range> endRange = new Range(document(), startOfParagraphToMove.deepEquivalent(), visibleEnd.deepEquivalent());
693                 endIndex = TextIterator::rangeLength(endRange.get());
694             }
695         }
696     }
697     
698     VisiblePosition beforeParagraph = startOfParagraphToMove.previous();
699
700     // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
701     // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.    
702     Position start = startOfParagraphToMove.deepEquivalent().downstream();
703     Position end = endOfParagraphToMove.deepEquivalent().upstream();
704     RefPtr<Range> range = new Range(document(), start.node(), start.offset(), end.node(), end.offset());
705
706     // FIXME: This is an inefficient way to preserve style on nodes in the paragraph to move.  It 
707     // shouldn't matter though, since moved paragraphs will usually be quite small.
708     RefPtr<DocumentFragment> fragment = startOfParagraphToMove != endOfParagraphToMove ? createFragmentFromMarkup(document(), range->toHTML(), "") : 0;
709     
710     setEndingSelection(Selection(start, end, DOWNSTREAM));
711     deleteSelection(false, false);
712
713     ASSERT(destination.deepEquivalent().node()->inDocument());
714     
715     // Deleting a paragraph leaves a placeholder (it always does when a whole paragraph is deleted).
716     // We remove it and prune its parents since we want to remove all traces of the paragraph we're moving.
717     Node* placeholder = endingSelection().end().downstream().node();
718     ASSERT(placeholder->hasTagName(brTag));
719     // 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
720     // before and after the table/list collapse onto one line.
721     if (placeholder->hasTagName(brTag) && isStartOfParagraph(endingSelection().visibleStart()) && isEndOfParagraph(endingSelection().visibleStart()))
722         removeNodeAndPruneAncestors(placeholder);
723
724     // Add a br if pruning an empty block level element caused a collapse.  For example:
725     // foo^
726     // <div>bar</div>
727     // baz
728     // Imagine moving 'bar' to ^.  'bar' will be deleted and its div pruned.  That would
729     // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br.
730     if (beforeParagraph.isNotNull() && !isEndOfParagraph(beforeParagraph))
731         insertNodeAt(createBreakElement(document()).get(), beforeParagraph.deepEquivalent().node(), beforeParagraph.deepEquivalent().offset());
732         
733     RefPtr<Range> startToDestinationRange(new Range(document(), Position(document(), 0), destination.deepEquivalent()));
734     destinationIndex = TextIterator::rangeLength(startToDestinationRange.get());
735     
736     setEndingSelection(destination);
737     EditCommandPtr cmd(new ReplaceSelectionCommand(document(), fragment.get(), true, false, !preserveStyle, true));
738     applyCommandToComposite(cmd);
739     
740     if (preserveSelection && startIndex != -1) {
741         setEndingSelection(Selection(TextIterator::rangeFromLocationAndLength(document(), destinationIndex + startIndex, 0)->startPosition(), 
742                                      TextIterator::rangeFromLocationAndLength(document(), destinationIndex + endIndex, 0)->startPosition(), 
743                                      DOWNSTREAM));
744     }
745 }
746
747 // FIXME: Send an appropriate shouldDeleteRange call.
748 bool CompositeEditCommand::breakOutOfEmptyListItem()
749 {
750     Node* emptyListItem = enclosingEmptyListItem(endingSelection().visibleStart());
751     if (!emptyListItem)
752         return false;
753         
754     RefPtr<CSSMutableStyleDeclaration> style = styleAtPosition(endingSelection().start());
755
756     Node *listNode = emptyListItem->parentNode();
757     RefPtr<Node> newBlock = isListElement(listNode->parentNode()) ? createListItemElement(document()) : createDefaultParagraphElement(document());
758     
759     if (emptyListItem->renderer()->nextSibling()) {
760         if (emptyListItem->renderer()->previousSibling())
761             splitElement(static_cast<Element *>(listNode), emptyListItem);
762         insertNodeBefore(newBlock.get(), listNode);
763         removeNode(emptyListItem);
764     } else {
765         insertNodeAfter(newBlock.get(), listNode);
766         removeNode(emptyListItem->renderer()->previousSibling() ? emptyListItem : listNode);
767     }
768     
769     appendBlockPlaceholder(newBlock.get());
770     setEndingSelection(Position(newBlock.get(), 0), DOWNSTREAM);
771     
772     CSSComputedStyleDeclaration endingStyle(endingSelection().start().node());
773     endingStyle.diff(style.get());
774     if (style->length() > 0)
775         applyStyle(style.get());
776     
777     return true;
778 }
779
780 PassRefPtr<Element> createBlockPlaceholderElement(Document* document)
781 {
782     ExceptionCode ec = 0;
783     RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "br", ec);
784     ASSERT(ec == 0);
785     breakNode->setAttribute(classAttr, blockPlaceholderClassString());
786     return breakNode.release();
787 }
788
789 static const String &blockPlaceholderClassString()
790 {
791     static String blockPlaceholderClassString = "webkit-block-placeholder";
792     return blockPlaceholderClassString;
793 }
794
795 } // namespace WebCore