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     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     if (visiblePosition.isNull())
541         return;
542         
543     Position p = visiblePosition.deepEquivalent().downstream();
544     if (p.node()->hasTagName(brTag) && p.offset() == 0 && isEndOfBlock(visiblePosition) && isStartOfBlock(visiblePosition))
545         removeNode(p.node());
546         
547     return;
548 }
549
550 void CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position &pos)
551 {
552     if (pos.isNull())
553         return;
554     
555     updateLayout();
556     
557     VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
558     VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
559     VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePos);
560     VisiblePosition next = visibleParagraphEnd.next();
561     VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd;
562     
563     Position paragraphStart = visibleParagraphStart.deepEquivalent().upstream();
564     Position end = visibleEnd.deepEquivalent().upstream();
565
566     // If there are no VisiblePositions in the same block as pos then 
567     // paragraphStart will be outside the paragraph
568     if (Range::compareBoundaryPoints(pos, paragraphStart) < 0)
569         return;
570
571     // Perform some checks to see if we need to perform work in this function.
572     if (isBlock(paragraphStart.node())) {
573         if (isBlock(end.node())) {
574             if (!end.node()->isAncestor(paragraphStart.node())) {
575                 // If the paragraph end is a descendant of paragraph start, then we need to run
576                 // the rest of this function. If not, we can bail here.
577                 return;
578             }
579         }
580         else if (enclosingBlock(end.node()) != paragraphStart.node()) {
581             // The visibleEnd.  It must be an ancestor of the paragraph start.
582             // We can bail as we have a full block to work with.
583             ASSERT(paragraphStart.node()->isAncestor(enclosingBlock(end.node())));
584             return;
585         }
586         else if (isEndOfDocument(visibleEnd)) {
587             // At the end of the document. We can bail here as well.
588             return;
589         }
590     }
591
592     RefPtr<Node> newBlock = createDefaultParagraphElement(document());
593
594     Node *moveNode = paragraphStart.node();
595     if (paragraphStart.offset() >= paragraphStart.node()->caretMaxOffset())
596         moveNode = moveNode->traverseNextNode();
597     Node *endNode = end.node();
598     
599     if (!isAtomicNode(paragraphStart.node()))
600         insertNodeAt(newBlock.get(), paragraphStart.node(), paragraphStart.offset());
601     else {
602         ASSERT(paragraphStart.offset() <= 1);
603         ASSERT(paragraphStart.node()->parentNode());
604         insertNodeAt(newBlock.get(), paragraphStart.node()->parentNode(), paragraphStart.node()->nodeIndex() + paragraphStart.offset());
605     }
606
607     while (moveNode && !isBlock(moveNode)) {
608         Node *next = moveNode->traverseNextSibling();
609         removeNode(moveNode);
610         appendNode(moveNode, newBlock.get());
611         if (moveNode == endNode)
612             break;
613         moveNode = next;
614     }
615 }
616
617 Node* enclosingAnchorElement(Node* node)
618 {
619     while (node && !(node->isElementNode() && node->isLink()))
620         node = node->parentNode();
621     
622     return node;
623 }
624
625 void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode)
626 {
627     if (!anchorNode)
628         return;
629     
630     ASSERT(anchorNode->isLink());
631     
632     setEndingSelection(Selection::selectionFromContentsOfNode(anchorNode));
633     applyStyledElement(static_cast<Element*>(anchorNode));
634 }
635
636 // We must push partially selected anchors down before creating or removing
637 // links from a selection to create fully selected chunks that can be removed.
638 // ApplyStyleCommand doesn't do this for us because styles can be nested.
639 // Anchors cannot be nested.
640 void CompositeEditCommand::pushPartiallySelectedAnchorElementsDown()
641 {
642     Selection originalSelection = endingSelection();
643     VisiblePosition visibleStart(originalSelection.start());
644     VisiblePosition visibleEnd(originalSelection.end());
645     
646     Node* startAnchor = enclosingAnchorElement(originalSelection.start().node());
647     VisiblePosition startOfStartAnchor(Position(startAnchor, 0));
648     if (startAnchor && startOfStartAnchor != visibleStart)
649         pushAnchorElementDown(startAnchor);
650
651     Node* endAnchor = enclosingAnchorElement(originalSelection.end().node());
652     VisiblePosition endOfEndAnchor(Position(endAnchor, 0));
653     if (endAnchor && endOfEndAnchor != visibleEnd)
654         pushAnchorElementDown(endAnchor);
655
656     ASSERT(originalSelection.start().node()->inDocument() && originalSelection.end().node()->inDocument());
657     setEndingSelection(originalSelection);
658 }
659
660 // This moves a paragraph preserving its style.
661 void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
662 {
663     ASSERT(isStartOfParagraph(startOfParagraphToMove));
664     ASSERT(isEndOfParagraph(endOfParagraphToMove));
665     moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection, preserveStyle);
666 }
667
668 void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
669 {
670     if (startOfParagraphToMove == destination)
671         return;
672     
673     int startIndex = -1;
674     int endIndex = -1;
675     int destinationIndex = -1;
676     if (preserveSelection && !endingSelection().isNone()) {
677         VisiblePosition visibleStart = endingSelection().visibleStart();
678         VisiblePosition visibleEnd = endingSelection().visibleEnd();
679         
680         bool startAfterParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) > 0;
681         bool endBeforeParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) < 0;
682         
683         if (!startAfterParagraph && !endBeforeParagraph) {
684             bool startInParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) >= 0;
685             bool endInParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) <= 0;
686             
687             startIndex = 0;
688             if (startInParagraph) {
689                 RefPtr<Range> startRange = new Range(document(), startOfParagraphToMove.deepEquivalent(), visibleStart.deepEquivalent());
690                 startIndex = TextIterator::rangeLength(startRange.get());
691             }
692
693             endIndex = 0;
694             if (endInParagraph) {
695                 RefPtr<Range> endRange = new Range(document(), startOfParagraphToMove.deepEquivalent(), visibleEnd.deepEquivalent());
696                 endIndex = TextIterator::rangeLength(endRange.get());
697             }
698         }
699     }
700     
701     VisiblePosition beforeParagraph = startOfParagraphToMove.previous();
702
703     // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
704     // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.    
705     Position start = startOfParagraphToMove.deepEquivalent().downstream();
706     Position end = endOfParagraphToMove.deepEquivalent().upstream();
707     RefPtr<Range> range = new Range(document(), start.node(), start.offset(), end.node(), end.offset());
708
709     // FIXME: This is an inefficient way to preserve style on nodes in the paragraph to move.  It 
710     // shouldn't matter though, since moved paragraphs will usually be quite small.
711     RefPtr<DocumentFragment> fragment = startOfParagraphToMove != endOfParagraphToMove ? createFragmentFromMarkup(document(), range->toHTML(), "") : 0;
712     
713     setEndingSelection(Selection(start, end, DOWNSTREAM));
714     deleteSelection(false, false);
715
716     ASSERT(destination.deepEquivalent().node()->inDocument());
717     
718     // Deleting a paragraph leaves a placeholder (it always does when a whole paragraph is deleted).
719     // We remove it and prune its parents since we want to remove all traces of the paragraph we're moving.
720     Node* placeholder = endingSelection().end().downstream().node();
721     ASSERT(placeholder->hasTagName(brTag));
722     // 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
723     // before and after the table/list collapse onto one line.
724     if (placeholder->hasTagName(brTag) && isStartOfParagraph(endingSelection().visibleStart()) && isEndOfParagraph(endingSelection().visibleStart()))
725         removeNodeAndPruneAncestors(placeholder);
726
727     // Add a br if pruning an empty block level element caused a collapse.  For example:
728     // foo^
729     // <div>bar</div>
730     // baz
731     // Imagine moving 'bar' to ^.  'bar' will be deleted and its div pruned.  That would
732     // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br.
733     if (beforeParagraph.isNotNull() && !isEndOfParagraph(beforeParagraph))
734         insertNodeAt(createBreakElement(document()).get(), beforeParagraph.deepEquivalent().node(), beforeParagraph.deepEquivalent().offset());
735         
736     RefPtr<Range> startToDestinationRange(new Range(document(), Position(document(), 0), destination.deepEquivalent()));
737     destinationIndex = TextIterator::rangeLength(startToDestinationRange.get());
738     
739     setEndingSelection(destination);
740     EditCommandPtr cmd(new ReplaceSelectionCommand(document(), fragment.get(), true, false, !preserveStyle, true));
741     applyCommandToComposite(cmd);
742     
743     if (preserveSelection && startIndex != -1) {
744         setEndingSelection(Selection(TextIterator::rangeFromLocationAndLength(document(), destinationIndex + startIndex, 0)->startPosition(), 
745                                      TextIterator::rangeFromLocationAndLength(document(), destinationIndex + endIndex, 0)->startPosition(), 
746                                      DOWNSTREAM));
747     }
748 }
749
750 // FIXME: Send an appropriate shouldDeleteRange call.
751 bool CompositeEditCommand::breakOutOfEmptyListItem()
752 {
753     Node* emptyListItem = enclosingEmptyListItem(endingSelection().visibleStart());
754     if (!emptyListItem)
755         return false;
756         
757     RefPtr<CSSMutableStyleDeclaration> style = styleAtPosition(endingSelection().start());
758
759     Node *listNode = emptyListItem->parentNode();
760     RefPtr<Node> newBlock = isListElement(listNode->parentNode()) ? createListItemElement(document()) : createDefaultParagraphElement(document());
761     
762     if (emptyListItem->renderer()->nextSibling()) {
763         if (emptyListItem->renderer()->previousSibling())
764             splitElement(static_cast<Element *>(listNode), emptyListItem);
765         insertNodeBefore(newBlock.get(), listNode);
766         removeNode(emptyListItem);
767     } else {
768         insertNodeAfter(newBlock.get(), listNode);
769         removeNode(emptyListItem->renderer()->previousSibling() ? emptyListItem : listNode);
770     }
771     
772     appendBlockPlaceholder(newBlock.get());
773     setEndingSelection(Position(newBlock.get(), 0), DOWNSTREAM);
774     
775     CSSComputedStyleDeclaration endingStyle(endingSelection().start().node());
776     endingStyle.diff(style.get());
777     if (style->length() > 0)
778         applyStyle(style.get());
779     
780     return true;
781 }
782
783 PassRefPtr<Element> createBlockPlaceholderElement(Document* document)
784 {
785     ExceptionCode ec = 0;
786     RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "br", ec);
787     ASSERT(ec == 0);
788     breakNode->setAttribute(classAttr, blockPlaceholderClassString());
789     return breakNode.release();
790 }
791
792 static const String &blockPlaceholderClassString()
793 {
794     static String blockPlaceholderClassString = "webkit-block-placeholder";
795     return blockPlaceholderClassString;
796 }
797
798 } // namespace WebCore