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 // Removes '\n's and brs that will collapse when content is inserted just before them.
488 // FIXME: We shouldn't really have to remove placeholders, but removing them is a workaround for 9661.
489 void CompositeEditCommand::removePlaceholderAt(const VisiblePosition& visiblePosition)
490 {
491     if (visiblePosition.isNull())
492         return;
493         
494     Position p = visiblePosition.deepEquivalent().downstream();
495     // If a br or '\n' is at the end of a block and not at the start of a paragraph,
496     // then it is superfluous, so adding content before a br or '\n' that is at
497     // the start of a paragraph will render it superfluous.
498     // FIXME: This doesn't remove placeholders at the end of anonymous blocks.
499     if (isEndOfBlock(visiblePosition) && isStartOfParagraph(visiblePosition)) {
500         if (p.node()->hasTagName(brTag) && p.offset() == 0)
501             removeNode(p.node());
502         else if (p.node()->renderer()->style()->preserveNewline() && visiblePosition.characterAfter() == '\n')
503             deleteTextFromNode(static_cast<Text*>(p.node()), p.offset(), 1);
504     }
505 }
506
507 void CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position& pos)
508 {
509     if (pos.isNull())
510         return;
511     
512     updateLayout();
513     
514     VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
515     VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
516     VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePos);
517     VisiblePosition next = visibleParagraphEnd.next();
518     VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd;
519     
520     Position paragraphStart = visibleParagraphStart.deepEquivalent().upstream();
521     Position end = visibleEnd.deepEquivalent().upstream();
522
523     // If there are no VisiblePositions in the same block as pos then 
524     // paragraphStart will be outside the paragraph
525     if (Range::compareBoundaryPoints(pos, paragraphStart) < 0)
526         return;
527
528     // Perform some checks to see if we need to perform work in this function.
529     if (isBlock(paragraphStart.node())) {
530         if (isBlock(end.node())) {
531             if (!end.node()->isDescendantOf(paragraphStart.node())) {
532                 // If the paragraph end is a descendant of paragraph start, then we need to run
533                 // the rest of this function. If not, we can bail here.
534                 return;
535             }
536         }
537         else if (enclosingBlock(end.node()) != paragraphStart.node()) {
538             // The visibleEnd.  It must be an ancestor of the paragraph start.
539             // We can bail as we have a full block to work with.
540             ASSERT(paragraphStart.node()->isDescendantOf(enclosingBlock(end.node())));
541             return;
542         }
543         else if (isEndOfDocument(visibleEnd)) {
544             // At the end of the document. We can bail here as well.
545             return;
546         }
547     }
548
549     RefPtr<Node> newBlock = createDefaultParagraphElement(document());
550
551     Node* moveNode = paragraphStart.node();
552     if (paragraphStart.offset() >= paragraphStart.node()->caretMaxOffset())
553         moveNode = moveNode->traverseNextNode();
554     Node* endNode = end.node();
555     
556     if (!isAtomicNode(paragraphStart.node()))
557         insertNodeAt(newBlock.get(), paragraphStart.node(), paragraphStart.offset());
558     else {
559         ASSERT(paragraphStart.offset() <= 1);
560         ASSERT(paragraphStart.node()->parentNode());
561         insertNodeAt(newBlock.get(), paragraphStart.node()->parentNode(), paragraphStart.node()->nodeIndex() + paragraphStart.offset());
562     }
563
564     while (moveNode && !isBlock(moveNode)) {
565         Node* next = moveNode->traverseNextSibling();
566         removeNode(moveNode);
567         appendNode(moveNode, newBlock.get());
568         if (moveNode == endNode)
569             break;
570         moveNode = next;
571     }
572 }
573
574 Node* enclosingAnchorElement(Node* node)
575 {
576     while (node && !(node->isElementNode() && node->isLink()))
577         node = node->parentNode();
578     return node;
579 }
580
581 void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode)
582 {
583     if (!anchorNode)
584         return;
585     
586     ASSERT(anchorNode->isLink());
587     
588     setEndingSelection(Selection::selectionFromContentsOfNode(anchorNode));
589     applyStyledElement(static_cast<Element*>(anchorNode));
590 }
591
592 // We must push partially selected anchors down before creating or removing
593 // links from a selection to create fully selected chunks that can be removed.
594 // ApplyStyleCommand doesn't do this for us because styles can be nested.
595 // Anchors cannot be nested.
596 void CompositeEditCommand::pushPartiallySelectedAnchorElementsDown()
597 {
598     Selection originalSelection = endingSelection();
599     VisiblePosition visibleStart(originalSelection.start());
600     VisiblePosition visibleEnd(originalSelection.end());
601     
602     Node* startAnchor = enclosingAnchorElement(originalSelection.start().node());
603     VisiblePosition startOfStartAnchor(Position(startAnchor, 0));
604     if (startAnchor && startOfStartAnchor != visibleStart)
605         pushAnchorElementDown(startAnchor);
606
607     Node* endAnchor = enclosingAnchorElement(originalSelection.end().node());
608     VisiblePosition endOfEndAnchor(Position(endAnchor, 0));
609     if (endAnchor && endOfEndAnchor != visibleEnd)
610         pushAnchorElementDown(endAnchor);
611
612     ASSERT(originalSelection.start().node()->inDocument() && originalSelection.end().node()->inDocument());
613     setEndingSelection(originalSelection);
614 }
615
616 // This moves a paragraph preserving its style.
617 void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
618 {
619     ASSERT(isStartOfParagraph(startOfParagraphToMove));
620     ASSERT(isEndOfParagraph(endOfParagraphToMove));
621     moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection, preserveStyle);
622 }
623
624 void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
625 {
626     if (startOfParagraphToMove == destination)
627         return;
628     
629     int startIndex = -1;
630     int endIndex = -1;
631     int destinationIndex = -1;
632     if (preserveSelection && !endingSelection().isNone()) {
633         VisiblePosition visibleStart = endingSelection().visibleStart();
634         VisiblePosition visibleEnd = endingSelection().visibleEnd();
635         
636         bool startAfterParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) > 0;
637         bool endBeforeParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) < 0;
638         
639         if (!startAfterParagraph && !endBeforeParagraph) {
640             bool startInParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) >= 0;
641             bool endInParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) <= 0;
642             
643             startIndex = 0;
644             if (startInParagraph) {
645                 RefPtr<Range> startRange = new Range(document(), startOfParagraphToMove.deepEquivalent(), visibleStart.deepEquivalent());
646                 startIndex = TextIterator::rangeLength(startRange.get());
647             }
648
649             endIndex = 0;
650             if (endInParagraph) {
651                 RefPtr<Range> endRange = new Range(document(), startOfParagraphToMove.deepEquivalent(), visibleEnd.deepEquivalent());
652                 endIndex = TextIterator::rangeLength(endRange.get());
653             }
654         }
655     }
656     
657     VisiblePosition beforeParagraph = startOfParagraphToMove.previous();
658
659     // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
660     // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.    
661     Position start = startOfParagraphToMove.deepEquivalent().downstream();
662     Position end = endOfParagraphToMove.deepEquivalent().upstream();
663     
664     // start and end can't be used directly to create a Range; they are "editing positions"
665     Position startRangeCompliant = rangeCompliantEquivalent(start);
666     Position endRangeCompliant = rangeCompliantEquivalent(end);
667     RefPtr<Range> range = new Range(document(), startRangeCompliant.node(), startRangeCompliant.offset(), endRangeCompliant.node(), endRangeCompliant.offset());
668
669     // FIXME: This is an inefficient way to preserve style on nodes in the paragraph to move.  It 
670     // shouldn't matter though, since moved paragraphs will usually be quite small.
671     RefPtr<DocumentFragment> fragment = startOfParagraphToMove != endOfParagraphToMove ? createFragmentFromMarkup(document(), range->toHTML(), "") : 0;
672     
673     setEndingSelection(Selection(start, end, DOWNSTREAM));
674     deleteSelection(false, false);
675
676     ASSERT(destination.deepEquivalent().node()->inDocument());
677     
678     // There are bugs in deletion when it removes a fully selected table/list.  
679     // It expands and removes the entire table/list, but will let content
680     // before and after the table/list collapse onto one line.
681     
682     // Deleting a paragraph will leave a placeholder.  Remove it (and prune
683     // empty or unrendered parents).
684     VisiblePosition caretAfterDelete = endingSelection().visibleStart();
685     if (isStartOfParagraph(caretAfterDelete) && isEndOfParagraph(caretAfterDelete)) {
686         // Note: We want the rightmost candidate.
687         Position position = caretAfterDelete.deepEquivalent().downstream();
688         Node* node = position.node();
689         // Normally deletion will leave a br as a placeholder.
690         if (node->hasTagName(brTag))
691             removeNodeAndPruneAncestors(node);
692         // If the selection to move was empty and in an empty block that 
693         // doesn't require a placeholder to prop itself open (like a bordered 
694         // div or an li), remove it during the move (the list removal code 
695         // expects this behavior).
696         else if (isBlock(node))
697             removeNodeAndPruneAncestors(node);
698         else if (node->renderer() && node->renderer()->style()->preserveNewline() && caretAfterDelete.characterAfter() == '\n')
699             deleteTextFromNode(static_cast<Text*>(node), position.offset(), 1);
700     }
701
702     // Add a br if pruning an empty block level element caused a collapse.  For example:
703     // foo^
704     // <div>bar</div>
705     // baz
706     // Imagine moving 'bar' to ^.  'bar' will be deleted and its div pruned.  That would
707     // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br.
708     if (beforeParagraph.isNotNull() && !isEndOfParagraph(beforeParagraph))
709         insertNodeAt(createBreakElement(document()).get(), beforeParagraph.deepEquivalent().node(), beforeParagraph.deepEquivalent().offset());
710         
711     RefPtr<Range> startToDestinationRange(new Range(document(), Position(document(), 0), rangeCompliantEquivalent(destination.deepEquivalent())));
712     destinationIndex = TextIterator::rangeLength(startToDestinationRange.get());
713     
714     setEndingSelection(destination);
715     applyCommandToComposite(new ReplaceSelectionCommand(document(), fragment.get(), true, false, !preserveStyle, false));
716     
717     if (preserveSelection && startIndex != -1) {
718         setEndingSelection(Selection(TextIterator::rangeFromLocationAndLength(document()->documentElement(), destinationIndex + startIndex, 0)->startPosition(), 
719                                      TextIterator::rangeFromLocationAndLength(document()->documentElement(), destinationIndex + endIndex, 0)->startPosition(), 
720                                      DOWNSTREAM));
721     }
722 }
723
724 // FIXME: Send an appropriate shouldDeleteRange call.
725 bool CompositeEditCommand::breakOutOfEmptyListItem()
726 {
727     Node* emptyListItem = enclosingEmptyListItem(endingSelection().visibleStart());
728     if (!emptyListItem)
729         return false;
730         
731     RefPtr<CSSMutableStyleDeclaration> style = styleAtPosition(endingSelection().start());
732
733     Node* listNode = emptyListItem->parentNode();
734     RefPtr<Node> newBlock = isListElement(listNode->parentNode()) ? createListItemElement(document()) : createDefaultParagraphElement(document());
735     
736     if (emptyListItem->renderer()->nextSibling()) {
737         if (emptyListItem->renderer()->previousSibling())
738             splitElement(static_cast<Element*>(listNode), emptyListItem);
739         insertNodeBefore(newBlock.get(), listNode);
740         removeNode(emptyListItem);
741     } else {
742         insertNodeAfter(newBlock.get(), listNode);
743         removeNode(emptyListItem->renderer()->previousSibling() ? emptyListItem : listNode);
744     }
745     
746     appendBlockPlaceholder(newBlock.get());
747     setEndingSelection(Selection(Position(newBlock.get(), 0), DOWNSTREAM));
748     
749     CSSComputedStyleDeclaration endingStyle(endingSelection().start().node());
750     endingStyle.diff(style.get());
751     if (style->length() > 0)
752         applyStyle(style.get());
753     
754     return true;
755 }
756
757 PassRefPtr<Element> createBlockPlaceholderElement(Document* document)
758 {
759     ExceptionCode ec = 0;
760     RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "br", ec);
761     ASSERT(ec == 0);
762     static String classString = "webkit-block-placeholder";
763     breakNode->setAttribute(classAttr, classString);
764     return breakNode.release();
765 }
766
767 } // namespace WebCore