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