e78a75d7b4f792bde695e6c7145df42b617f527f
[WebKit-https.git] / Source / WebCore / editing / CompositeEditCommand.cpp
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008 Apple 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 "DeleteFromTextNodeCommand.h"
32 #include "DeleteSelectionCommand.h"
33 #include "Document.h"
34 #include "DocumentFragment.h"
35 #include "DocumentMarkerController.h"
36 #include "Editor.h"
37 #include "EditorInsertAction.h"
38 #include "ExceptionCodePlaceholder.h"
39 #include "Frame.h"
40 #include "HTMLElement.h"
41 #include "HTMLNames.h"
42 #include "InlineTextBox.h"
43 #include "InsertIntoTextNodeCommand.h"
44 #include "InsertLineBreakCommand.h"
45 #include "InsertNodeBeforeCommand.h"
46 #include "InsertParagraphSeparatorCommand.h"
47 #include "InsertTextCommand.h"
48 #include "MergeIdenticalElementsCommand.h"
49 #include "NodeTraversal.h"
50 #include "Range.h"
51 #include "RemoveCSSPropertyCommand.h"
52 #include "RemoveNodeCommand.h"
53 #include "RemoveNodePreservingChildrenCommand.h"
54 #include "ReplaceNodeWithSpanCommand.h"
55 #include "ReplaceSelectionCommand.h"
56 #include "RenderBlock.h"
57 #include "RenderText.h"
58 #include "ScopedEventQueue.h"
59 #include "SetNodeAttributeCommand.h"
60 #include "SplitElementCommand.h"
61 #include "SplitTextNodeCommand.h"
62 #include "SplitTextNodeContainingElementCommand.h"
63 #include "Text.h"
64 #include "TextIterator.h"
65 #include "VisibleUnits.h"
66 #include "WrapContentsInDummySpanCommand.h"
67 #include "htmlediting.h"
68 #include "markup.h"
69 #include <wtf/unicode/CharacterNames.h>
70
71 #if ENABLE(DELETION_UI)
72 #include "DeleteButtonController.h"
73 #endif
74
75 using namespace std;
76
77 namespace WebCore {
78
79 using namespace HTMLNames;
80
81 PassRefPtr<EditCommandComposition> EditCommandComposition::create(Document* document,
82     const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction editAction)
83 {
84     return adoptRef(new EditCommandComposition(document, startingSelection, endingSelection, editAction));
85 }
86
87 EditCommandComposition::EditCommandComposition(Document* document, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction editAction)
88     : m_document(document)
89     , m_startingSelection(startingSelection)
90     , m_endingSelection(endingSelection)
91     , m_startingRootEditableElement(startingSelection.rootEditableElement())
92     , m_endingRootEditableElement(endingSelection.rootEditableElement())
93     , m_editAction(editAction)
94 {
95 }
96
97 void EditCommandComposition::unapply()
98 {
99     ASSERT(m_document);
100     RefPtr<Frame> frame = m_document->frame();
101     ASSERT(frame);
102
103     // Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
104     // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
105     // if one is necessary (like for the creation of VisiblePositions).
106     m_document->updateLayoutIgnorePendingStylesheets();
107
108     {
109 #if ENABLE(DELETION_UI)
110         DeleteButtonControllerDisableScope deleteButtonControllerDisableScope(frame.get());
111 #endif
112
113         size_t size = m_commands.size();
114         for (size_t i = size; i; --i)
115             m_commands[i - 1]->doUnapply();
116     }
117
118     frame->editor()->unappliedEditing(this);
119 }
120
121 void EditCommandComposition::reapply()
122 {
123     ASSERT(m_document);
124     RefPtr<Frame> frame = m_document->frame();
125     ASSERT(frame);
126
127     // Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
128     // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
129     // if one is necessary (like for the creation of VisiblePositions).
130     m_document->updateLayoutIgnorePendingStylesheets();
131
132     {
133 #if ENABLE(DELETION_UI)
134         DeleteButtonControllerDisableScope deleteButtonControllerDisableScope(frame.get());
135 #endif
136         size_t size = m_commands.size();
137         for (size_t i = 0; i != size; ++i)
138             m_commands[i]->doReapply();
139     }
140     
141     frame->editor()->reappliedEditing(this);
142 }
143
144 void EditCommandComposition::append(SimpleEditCommand* command)
145 {
146     m_commands.append(command);
147 }
148
149 void EditCommandComposition::setStartingSelection(const VisibleSelection& selection)
150 {
151     m_startingSelection = selection;
152     m_startingRootEditableElement = selection.rootEditableElement();
153 }
154
155 void EditCommandComposition::setEndingSelection(const VisibleSelection& selection)
156 {
157     m_endingSelection = selection;
158     m_endingRootEditableElement = selection.rootEditableElement();
159 }
160
161 #ifndef NDEBUG
162 void EditCommandComposition::getNodesInCommand(HashSet<Node*>& nodes)
163 {
164     size_t size = m_commands.size();
165     for (size_t i = 0; i < size; ++i)
166         m_commands[i]->getNodesInCommand(nodes);
167 }
168 #endif
169
170 void applyCommand(PassRefPtr<CompositeEditCommand> command)
171 {
172     command->apply();
173 }
174
175 CompositeEditCommand::CompositeEditCommand(Document *document)
176     : EditCommand(document)
177 {
178 }
179
180 CompositeEditCommand::~CompositeEditCommand()
181 {
182     ASSERT(isTopLevelCommand() || !m_composition);
183 }
184
185 void CompositeEditCommand::apply()
186 {
187     if (!endingSelection().isContentRichlyEditable()) {
188         switch (editingAction()) {
189         case EditActionTyping:
190         case EditActionPaste:
191         case EditActionDrag:
192         case EditActionSetWritingDirection:
193         case EditActionCut:
194         case EditActionUnspecified:
195             break;
196         default:
197             ASSERT_NOT_REACHED();
198             return;
199         }
200     }
201     ensureComposition();
202
203     // Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
204     // Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
205     // if one is necessary (like for the creation of VisiblePositions).
206     ASSERT(document());
207     document()->updateLayoutIgnorePendingStylesheets();
208
209     Frame* frame = document()->frame();
210     ASSERT(frame);
211     {
212         EventQueueScope scope;
213 #if ENABLE(DELETION_UI)
214         DeleteButtonControllerDisableScope deleteButtonControllerDisableScope(frame);
215 #endif
216         doApply();
217     }
218
219     // Only need to call appliedEditing for top-level commands,
220     // and TypingCommands do it on their own (see TypingCommand::typingAddedToOpenCommand).
221     if (!callsAppliedEditingInDoApply())
222         frame->editor()->appliedEditing(this);
223     setShouldRetainAutocorrectionIndicator(false);
224 }
225
226 EditCommandComposition* CompositeEditCommand::ensureComposition()
227 {
228     CompositeEditCommand* command = this;
229     while (command && command->parent())
230         command = command->parent();
231     if (!command->m_composition)
232         command->m_composition = EditCommandComposition::create(document(), startingSelection(), endingSelection(), editingAction());
233     return command->m_composition.get();
234 }
235
236 bool CompositeEditCommand::isCreateLinkCommand() const
237 {
238     return false;
239 }
240
241 bool CompositeEditCommand::preservesTypingStyle() const
242 {
243     return false;
244 }
245
246 bool CompositeEditCommand::isTypingCommand() const
247 {
248     return false;
249 }
250
251 bool CompositeEditCommand::callsAppliedEditingInDoApply() const
252 {
253     return false;
254 }
255
256 bool CompositeEditCommand::shouldRetainAutocorrectionIndicator() const
257 {
258     return false;
259 }
260
261 void CompositeEditCommand::setShouldRetainAutocorrectionIndicator(bool)
262 {
263 }
264
265 //
266 // sugary-sweet convenience functions to help create and apply edit commands in composite commands
267 //
268 void CompositeEditCommand::applyCommandToComposite(PassRefPtr<EditCommand> prpCommand)
269 {
270     RefPtr<EditCommand> command = prpCommand;
271     command->setParent(this);
272     command->doApply();
273     if (command->isSimpleEditCommand()) {
274         command->setParent(0);
275         ensureComposition()->append(toSimpleEditCommand(command.get()));
276     }
277     m_commands.append(command.release());
278 }
279
280 void CompositeEditCommand::applyCommandToComposite(PassRefPtr<CompositeEditCommand> command, const VisibleSelection& selection)
281 {
282     command->setParent(this);
283     if (selection != command->endingSelection()) {
284         command->setStartingSelection(selection);
285         command->setEndingSelection(selection);
286     }
287     command->doApply();
288     m_commands.append(command);
289 }
290
291 void CompositeEditCommand::applyStyle(const EditingStyle* style, EditAction editingAction)
292 {
293     applyCommandToComposite(ApplyStyleCommand::create(document(), style, editingAction));
294 }
295
296 void CompositeEditCommand::applyStyle(const EditingStyle* style, const Position& start, const Position& end, EditAction editingAction)
297 {
298     applyCommandToComposite(ApplyStyleCommand::create(document(), style, start, end, editingAction));
299 }
300
301 void CompositeEditCommand::applyStyledElement(PassRefPtr<Element> element)
302 {
303     applyCommandToComposite(ApplyStyleCommand::create(element, false));
304 }
305
306 void CompositeEditCommand::removeStyledElement(PassRefPtr<Element> element)
307 {
308     applyCommandToComposite(ApplyStyleCommand::create(element, true));
309 }
310
311 void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea)
312 {
313     applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea));
314 }
315
316 void CompositeEditCommand::insertLineBreak()
317 {
318     applyCommandToComposite(InsertLineBreakCommand::create(document()));
319 }
320
321 bool CompositeEditCommand::isRemovableBlock(const Node* node)
322 {
323     if (!node->hasTagName(divTag))
324         return false;
325
326     Node* parentNode = node->parentNode();
327     if (parentNode && parentNode->firstChild() != parentNode->lastChild())
328         return false;
329
330     if (!toElement(node)->hasAttributes())
331         return true;
332
333     return false;
334 }
335
336 void CompositeEditCommand::insertNodeBefore(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
337 {
338     ASSERT(!refChild->hasTagName(bodyTag));
339     applyCommandToComposite(InsertNodeBeforeCommand::create(insertChild, refChild, shouldAssumeContentIsAlwaysEditable));
340 }
341
342 void CompositeEditCommand::insertNodeAfter(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild)
343 {
344     ASSERT(insertChild);
345     ASSERT(refChild);
346     ASSERT(!refChild->hasTagName(bodyTag));
347     ContainerNode* parent = refChild->parentNode();
348     ASSERT(parent);
349     ASSERT(!parent->isShadowRoot());
350     if (parent->lastChild() == refChild)
351         appendNode(insertChild, parent);
352     else {
353         ASSERT(refChild->nextSibling());
354         insertNodeBefore(insertChild, refChild->nextSibling());
355     }
356 }
357
358 void CompositeEditCommand::insertNodeAt(PassRefPtr<Node> insertChild, const Position& editingPosition)
359 {
360     ASSERT(isEditablePosition(editingPosition));
361     // For editing positions like [table, 0], insert before the table,
362     // likewise for replaced elements, brs, etc.
363     Position p = editingPosition.parentAnchoredEquivalent();
364     Node* refChild = p.deprecatedNode();
365     int offset = p.deprecatedEditingOffset();
366     
367     if (canHaveChildrenForEditing(refChild)) {
368         Node* child = refChild->firstChild();
369         for (int i = 0; child && i < offset; i++)
370             child = child->nextSibling();
371         if (child)
372             insertNodeBefore(insertChild, child);
373         else
374             appendNode(insertChild, toContainerNode(refChild));
375     } else if (caretMinOffset(refChild) >= offset)
376         insertNodeBefore(insertChild, refChild);
377     else if (refChild->isTextNode() && caretMaxOffset(refChild) > offset) {
378         splitTextNode(toText(refChild), offset);
379
380         // Mutation events (bug 22634) from the text node insertion may have removed the refChild
381         if (!refChild->inDocument())
382             return;
383         insertNodeBefore(insertChild, refChild);
384     } else
385         insertNodeAfter(insertChild, refChild);
386 }
387
388 void CompositeEditCommand::appendNode(PassRefPtr<Node> node, PassRefPtr<ContainerNode> parent)
389 {
390     ASSERT(canHaveChildrenForEditing(parent.get()));
391     applyCommandToComposite(AppendNodeCommand::create(parent, node));
392 }
393
394 void CompositeEditCommand::removeChildrenInRange(PassRefPtr<Node> node, unsigned from, unsigned to)
395 {
396     Vector<RefPtr<Node> > children;
397     Node* child = node->childNode(from);
398     for (unsigned i = from; child && i < to; i++, child = child->nextSibling())
399         children.append(child);
400
401     size_t size = children.size();
402     for (size_t i = 0; i < size; ++i)
403         removeNode(children[i].release());
404 }
405
406 void CompositeEditCommand::removeNode(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
407 {
408     if (!node || !node->nonShadowBoundaryParentNode())
409         return;
410     applyCommandToComposite(RemoveNodeCommand::create(node, shouldAssumeContentIsAlwaysEditable));
411 }
412
413 void CompositeEditCommand::removeNodePreservingChildren(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
414 {
415     applyCommandToComposite(RemoveNodePreservingChildrenCommand::create(node, shouldAssumeContentIsAlwaysEditable));
416 }
417
418 void CompositeEditCommand::removeNodeAndPruneAncestors(PassRefPtr<Node> node)
419 {
420     RefPtr<ContainerNode> parent = node->parentNode();
421     removeNode(node);
422     prune(parent.release());
423 }
424
425 void CompositeEditCommand::moveRemainingSiblingsToNewParent(Node* node, Node* pastLastNodeToMove, PassRefPtr<Element> prpNewParent)
426 {
427     NodeVector nodesToRemove;
428     RefPtr<Element> newParent = prpNewParent;
429
430     for (; node && node != pastLastNodeToMove; node = node->nextSibling())
431         nodesToRemove.append(node);
432
433     for (unsigned i = 0; i < nodesToRemove.size(); i++) {
434         removeNode(nodesToRemove[i]);
435         appendNode(nodesToRemove[i], newParent);
436     }
437 }
438
439 void CompositeEditCommand::updatePositionForNodeRemovalPreservingChildren(Position& position, Node* node)
440 {
441     int offset = (position.anchorType() == Position::PositionIsOffsetInAnchor) ? position.offsetInContainerNode() : 0;
442     updatePositionForNodeRemoval(position, node);
443     if (offset)
444         position.moveToOffset(offset);    
445 }
446
447 HTMLElement* CompositeEditCommand::replaceElementWithSpanPreservingChildrenAndAttributes(PassRefPtr<HTMLElement> node)
448 {
449     // It would also be possible to implement all of ReplaceNodeWithSpanCommand
450     // as a series of existing smaller edit commands.  Someone who wanted to
451     // reduce the number of edit commands could do so here.
452     RefPtr<ReplaceNodeWithSpanCommand> command = ReplaceNodeWithSpanCommand::create(node);
453     applyCommandToComposite(command);
454     // Returning a raw pointer here is OK because the command is retained by
455     // applyCommandToComposite (thus retaining the span), and the span is also
456     // in the DOM tree, and thus alive whie it has a parent.
457     ASSERT(command->spanElement()->inDocument());
458     return command->spanElement();
459 }
460
461 void CompositeEditCommand::prune(PassRefPtr<Node> node)
462 {
463     if (RefPtr<Node> highestNodeToRemove = highestNodeToRemoveInPruning(node.get()))
464         removeNode(highestNodeToRemove.release());
465 }
466
467 void CompositeEditCommand::splitTextNode(PassRefPtr<Text> node, unsigned offset)
468 {
469     applyCommandToComposite(SplitTextNodeCommand::create(node, offset));
470 }
471
472 void CompositeEditCommand::splitElement(PassRefPtr<Element> element, PassRefPtr<Node> atChild)
473 {
474     applyCommandToComposite(SplitElementCommand::create(element, atChild));
475 }
476
477 void CompositeEditCommand::mergeIdenticalElements(PassRefPtr<Element> prpFirst, PassRefPtr<Element> prpSecond)
478 {
479     RefPtr<Element> first = prpFirst;
480     RefPtr<Element> second = prpSecond;
481     ASSERT(!first->isDescendantOf(second.get()) && second != first);
482     if (first->nextSibling() != second) {
483         removeNode(second);
484         insertNodeAfter(second, first);
485     }
486     applyCommandToComposite(MergeIdenticalElementsCommand::create(first, second));
487 }
488
489 void CompositeEditCommand::wrapContentsInDummySpan(PassRefPtr<Element> element)
490 {
491     applyCommandToComposite(WrapContentsInDummySpanCommand::create(element));
492 }
493
494 void CompositeEditCommand::splitTextNodeContainingElement(PassRefPtr<Text> text, unsigned offset)
495 {
496     applyCommandToComposite(SplitTextNodeContainingElementCommand::create(text, offset));
497 }
498
499 void CompositeEditCommand::insertTextIntoNode(PassRefPtr<Text> node, unsigned offset, const String& text)
500 {
501     if (!text.isEmpty())
502         applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, text));
503 }
504
505 void CompositeEditCommand::deleteTextFromNode(PassRefPtr<Text> node, unsigned offset, unsigned count)
506 {
507     applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count));
508 }
509
510 void CompositeEditCommand::replaceTextInNode(PassRefPtr<Text> prpNode, unsigned offset, unsigned count, const String& replacementText)
511 {
512     RefPtr<Text> node(prpNode);
513     applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count));
514     if (!replacementText.isEmpty())
515         applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, replacementText));
516 }
517
518 Position CompositeEditCommand::replaceSelectedTextInNode(const String& text)
519 {
520     Position start = endingSelection().start();
521     Position end = endingSelection().end();
522     if (start.containerNode() != end.containerNode() || !start.containerNode()->isTextNode() || isTabSpanTextNode(start.containerNode()))
523         return Position();
524
525     RefPtr<Text> textNode = start.containerText();
526     replaceTextInNode(textNode, start.offsetInContainerNode(), end.offsetInContainerNode() - start.offsetInContainerNode(), text);
527
528     return Position(textNode.release(), start.offsetInContainerNode() + text.length());
529 }
530
531 static void copyMarkers(const Vector<DocumentMarker*>& markerPointers, Vector<DocumentMarker>& markers)
532 {
533     size_t arraySize = markerPointers.size();
534     markers.reserveCapacity(arraySize);
535     for (size_t i = 0; i < arraySize; ++i)
536         markers.append(*markerPointers[i]);
537 }
538
539 void CompositeEditCommand::replaceTextInNodePreservingMarkers(PassRefPtr<Text> prpNode, unsigned offset, unsigned count, const String& replacementText)
540 {
541     RefPtr<Text> node(prpNode);
542     DocumentMarkerController* markerController = document()->markers();
543     Vector<DocumentMarker> markers;
544     copyMarkers(markerController->markersInRange(Range::create(document(), node, offset, node, offset + count).get(), DocumentMarker::AllMarkers()), markers);
545     replaceTextInNode(node, offset, count, replacementText);
546     RefPtr<Range> newRange = Range::create(document(), node, offset, node, offset + replacementText.length());
547     for (size_t i = 0; i < markers.size(); ++i)
548         markerController->addMarker(newRange.get(), markers[i].type(), markers[i].description());
549 }
550
551 Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos)
552 {
553     if (!isTabSpanTextNode(pos.anchorNode()))
554         return pos;
555
556     switch (pos.anchorType()) {
557     case Position::PositionIsBeforeChildren:
558     case Position::PositionIsAfterChildren:
559         ASSERT_NOT_REACHED();
560         return pos;
561     case Position::PositionIsOffsetInAnchor:
562         break;
563     case Position::PositionIsBeforeAnchor:
564         return positionInParentBeforeNode(pos.anchorNode());
565     case Position::PositionIsAfterAnchor:
566         return positionInParentAfterNode(pos.anchorNode());
567     }
568
569     Node* tabSpan = tabSpanNode(pos.containerNode());
570
571     if (pos.offsetInContainerNode() <= caretMinOffset(pos.containerNode()))
572         return positionInParentBeforeNode(tabSpan);
573
574     if (pos.offsetInContainerNode() >= caretMaxOffset(pos.containerNode()))
575         return positionInParentAfterNode(tabSpan);
576
577     splitTextNodeContainingElement(toText(pos.containerNode()), pos.offsetInContainerNode());
578     return positionInParentBeforeNode(tabSpan);
579 }
580
581 void CompositeEditCommand::insertNodeAtTabSpanPosition(PassRefPtr<Node> node, const Position& pos)
582 {
583     // insert node before, after, or at split of tab span
584     insertNodeAt(node, positionOutsideTabSpan(pos));
585 }
586
587 void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
588 {
589     if (endingSelection().isRange())
590         applyCommandToComposite(DeleteSelectionCommand::create(document(), smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup));
591 }
592
593 void CompositeEditCommand::deleteSelection(const VisibleSelection &selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
594 {
595     if (selection.isRange())
596         applyCommandToComposite(DeleteSelectionCommand::create(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup));
597 }
598
599 void CompositeEditCommand::removeCSSProperty(PassRefPtr<StyledElement> element, CSSPropertyID property)
600 {
601     applyCommandToComposite(RemoveCSSPropertyCommand::create(document(), element, property));
602 }
603
604 void CompositeEditCommand::removeNodeAttribute(PassRefPtr<Element> element, const QualifiedName& attribute)
605 {
606     setNodeAttribute(element, attribute, AtomicString());
607 }
608
609 void CompositeEditCommand::setNodeAttribute(PassRefPtr<Element> element, const QualifiedName& attribute, const AtomicString& value)
610 {
611     applyCommandToComposite(SetNodeAttributeCommand::create(element, attribute, value));
612 }
613
614 static inline bool containsOnlyWhitespace(const String& text)
615 {
616     for (unsigned i = 0; i < text.length(); ++i) {
617         if (!isWhitespace(text.characters()[i]))
618             return false;
619     }
620     
621     return true;
622 }
623
624 bool CompositeEditCommand::shouldRebalanceLeadingWhitespaceFor(const String& text) const
625 {
626     return containsOnlyWhitespace(text);
627 }
628
629 bool CompositeEditCommand::canRebalance(const Position& position) const
630 {
631     Node* node = position.containerNode();
632     if (position.anchorType() != Position::PositionIsOffsetInAnchor || !node || !node->isTextNode())
633         return false;
634
635     Text* textNode = toText(node);
636     if (textNode->length() == 0)
637         return false;
638
639     RenderObject* renderer = textNode->renderer();
640     if (renderer && !renderer->style()->collapseWhiteSpace())
641         return false;
642
643     return true;
644 }
645
646 // FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, cousins, etc).
647 void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position)
648 {
649     Node* node = position.containerNode();
650     if (!canRebalance(position))
651         return;
652
653     // If the rebalance is for the single offset, and neither text[offset] nor text[offset - 1] are some form of whitespace, do nothing.
654     int offset = position.deprecatedEditingOffset();
655     String text = toText(node)->data();
656     if (!isWhitespace(text[offset])) {
657         offset--;
658         if (offset < 0 || !isWhitespace(text[offset]))
659             return;
660     }
661
662     rebalanceWhitespaceOnTextSubstring(toText(node), position.offsetInContainerNode(), position.offsetInContainerNode());
663 }
664
665 void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(PassRefPtr<Text> prpTextNode, int startOffset, int endOffset)
666 {
667     RefPtr<Text> textNode = prpTextNode;
668
669     String text = textNode->data();
670     ASSERT(!text.isEmpty());
671
672     // Set upstream and downstream to define the extent of the whitespace surrounding text[offset].
673     int upstream = startOffset;
674     while (upstream > 0 && isWhitespace(text[upstream - 1]))
675         upstream--;
676     
677     int downstream = endOffset;
678     while ((unsigned)downstream < text.length() && isWhitespace(text[downstream]))
679         downstream++;
680     
681     int length = downstream - upstream;
682     if (!length)
683         return;
684
685     VisiblePosition visibleUpstreamPos(Position(textNode, upstream));
686     VisiblePosition visibleDownstreamPos(Position(textNode, downstream));
687     
688     String string = text.substring(upstream, length);
689     String rebalancedString = stringWithRebalancedWhitespace(string,
690     // FIXME: Because of the problem mentioned at the top of this function, we must also use nbsps at the start/end of the string because
691     // this function doesn't get all surrounding whitespace, just the whitespace in the current text node.
692                                                              isStartOfParagraph(visibleUpstreamPos) || upstream == 0, 
693                                                              isEndOfParagraph(visibleDownstreamPos) || (unsigned)downstream == text.length());
694     
695     if (string != rebalancedString)
696         replaceTextInNodePreservingMarkers(textNode.release(), upstream, length, rebalancedString);
697 }
698
699 void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& position)
700 {
701     Node* node = position.deprecatedNode();
702     if (!node || !node->isTextNode())
703         return;
704     Text* textNode = toText(node);    
705     
706     if (textNode->length() == 0)
707         return;
708     RenderObject* renderer = textNode->renderer();
709     if (renderer && !renderer->style()->collapseWhiteSpace())
710         return;
711
712     // Delete collapsed whitespace so that inserting nbsps doesn't uncollapse it.
713     Position upstreamPos = position.upstream();
714     deleteInsignificantText(position.upstream(), position.downstream());
715     position = upstreamPos.downstream();
716
717     VisiblePosition visiblePos(position);
718     VisiblePosition previousVisiblePos(visiblePos.previous());
719     Position previous(previousVisiblePos.deepEquivalent());
720     
721     if (isCollapsibleWhitespace(previousVisiblePos.characterAfter()) && previous.deprecatedNode()->isTextNode() && !previous.deprecatedNode()->hasTagName(brTag))
722         replaceTextInNodePreservingMarkers(toText(previous.deprecatedNode()), previous.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
723     if (isCollapsibleWhitespace(visiblePos.characterAfter()) && position.deprecatedNode()->isTextNode() && !position.deprecatedNode()->hasTagName(brTag))
724         replaceTextInNodePreservingMarkers(toText(position.deprecatedNode()), position.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
725 }
726
727 void CompositeEditCommand::rebalanceWhitespace()
728 {
729     VisibleSelection selection = endingSelection();
730     if (selection.isNone())
731         return;
732         
733     rebalanceWhitespaceAt(selection.start());
734     if (selection.isRange())
735         rebalanceWhitespaceAt(selection.end());
736 }
737
738 void CompositeEditCommand::deleteInsignificantText(PassRefPtr<Text> textNode, unsigned start, unsigned end)
739 {
740     if (!textNode || start >= end)
741         return;
742
743     document()->updateLayout();
744
745     RenderText* textRenderer = toRenderText(textNode->renderer());
746     if (!textRenderer)
747         return;
748
749     Vector<InlineTextBox*> sortedTextBoxes;
750     size_t sortedTextBoxesPosition = 0;
751    
752     for (InlineTextBox* textBox = textRenderer->firstTextBox(); textBox; textBox = textBox->nextTextBox())
753         sortedTextBoxes.append(textBox);
754     
755     // If there is mixed directionality text, the boxes can be out of order,
756     // (like Arabic with embedded LTR), so sort them first. 
757     if (textRenderer->containsReversedText())    
758         std::sort(sortedTextBoxes.begin(), sortedTextBoxes.end(), InlineTextBox::compareByStart);
759     InlineTextBox* box = sortedTextBoxes.isEmpty() ? 0 : sortedTextBoxes[sortedTextBoxesPosition];
760
761     if (!box) {
762         // whole text node is empty
763         removeNode(textNode);
764         return;    
765     }
766     
767     unsigned length = textNode->length();
768     if (start >= length || end > length)
769         return;
770
771     unsigned removed = 0;
772     InlineTextBox* prevBox = 0;
773     String str;
774
775     // This loop structure works to process all gaps preceding a box,
776     // and also will look at the gap after the last box.
777     while (prevBox || box) {
778         unsigned gapStart = prevBox ? prevBox->start() + prevBox->len() : 0;
779         if (end < gapStart)
780             // No more chance for any intersections
781             break;
782
783         unsigned gapEnd = box ? box->start() : length;
784         bool indicesIntersect = start <= gapEnd && end >= gapStart;
785         int gapLen = gapEnd - gapStart;
786         if (indicesIntersect && gapLen > 0) {
787             gapStart = max(gapStart, start);
788             gapEnd = min(gapEnd, end);
789             if (str.isNull())
790                 str = textNode->data().substring(start, end - start);
791             // remove text in the gap
792             str.remove(gapStart - start - removed, gapLen);
793             removed += gapLen;
794         }
795         
796         prevBox = box;
797         if (box) {
798             if (++sortedTextBoxesPosition < sortedTextBoxes.size())
799                 box = sortedTextBoxes[sortedTextBoxesPosition];
800             else
801                 box = 0;
802         }
803     }
804
805     if (!str.isNull()) {
806         // Replace the text between start and end with our pruned version.
807         if (!str.isEmpty())
808             replaceTextInNode(textNode, start, end - start, str);
809         else {
810             // Assert that we are not going to delete all of the text in the node.
811             // If we were, that should have been done above with the call to 
812             // removeNode and return.
813             ASSERT(start > 0 || end - start < textNode->length());
814             deleteTextFromNode(textNode, start, end - start);
815         }
816     }
817 }
818
819 void CompositeEditCommand::deleteInsignificantText(const Position& start, const Position& end)
820 {
821     if (start.isNull() || end.isNull())
822         return;
823
824     if (comparePositions(start, end) >= 0)
825         return;
826
827     Vector<RefPtr<Text> > nodes;
828     for (Node* node = start.deprecatedNode(); node; node = NodeTraversal::next(node)) {
829         if (node->isTextNode())
830             nodes.append(toText(node));
831         if (node == end.deprecatedNode())
832             break;
833     }
834
835     for (size_t i = 0; i < nodes.size(); ++i) {
836         Text* textNode = nodes[i].get();
837         int startOffset = textNode == start.deprecatedNode() ? start.deprecatedEditingOffset() : 0;
838         int endOffset = textNode == end.deprecatedNode() ? end.deprecatedEditingOffset() : static_cast<int>(textNode->length());
839         deleteInsignificantText(textNode, startOffset, endOffset);
840     }
841 }
842
843 void CompositeEditCommand::deleteInsignificantTextDownstream(const Position& pos)
844 {
845     Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivalent().downstream();
846     deleteInsignificantText(pos, end);
847 }
848
849 PassRefPtr<Node> CompositeEditCommand::appendBlockPlaceholder(PassRefPtr<Element> container)
850 {
851     if (!container)
852         return 0;
853
854     document()->updateLayoutIgnorePendingStylesheets();
855     
856     // Should assert isBlockFlow || isInlineFlow when deletion improves. See 4244964.
857     ASSERT(container->renderer());
858
859     RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
860     appendNode(placeholder, container);
861     return placeholder.release();
862 }
863
864 PassRefPtr<Node> CompositeEditCommand::insertBlockPlaceholder(const Position& pos)
865 {
866     if (pos.isNull())
867         return 0;
868
869     // Should assert isBlockFlow || isInlineFlow when deletion improves.  See 4244964.
870     ASSERT(pos.deprecatedNode()->renderer());
871
872     RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
873     insertNodeAt(placeholder, pos);
874     return placeholder.release();
875 }
876
877 PassRefPtr<Node> CompositeEditCommand::addBlockPlaceholderIfNeeded(Element* container)
878 {
879     if (!container)
880         return 0;
881
882     document()->updateLayoutIgnorePendingStylesheets();
883
884     RenderObject* renderer = container->renderer();
885     if (!renderer || !renderer->isBlockFlow())
886         return 0;
887     
888     // append the placeholder to make sure it follows
889     // any unrendered blocks
890     RenderBlock* block = toRenderBlock(renderer);
891     if (block->height() == 0 || (block->isListItem() && block->isEmpty()))
892         return appendBlockPlaceholder(container);
893
894     return 0;
895 }
896
897 // Assumes that the position is at a placeholder and does the removal without much checking.
898 void CompositeEditCommand::removePlaceholderAt(const Position& p)
899 {
900     ASSERT(lineBreakExistsAtPosition(p));
901     
902     // We are certain that the position is at a line break, but it may be a br or a preserved newline.
903     if (p.anchorNode()->hasTagName(brTag)) {
904         removeNode(p.anchorNode());
905         return;
906     }
907     
908     deleteTextFromNode(toText(p.anchorNode()), p.offsetInContainerNode(), 1);
909 }
910
911 PassRefPtr<Node> CompositeEditCommand::insertNewDefaultParagraphElementAt(const Position& position)
912 {
913     RefPtr<Element> paragraphElement = createDefaultParagraphElement(document());
914     paragraphElement->appendChild(createBreakElement(document()), IGNORE_EXCEPTION);
915     insertNodeAt(paragraphElement, position);
916     return paragraphElement.release();
917 }
918
919 // If the paragraph is not entirely within it's own block, create one and move the paragraph into 
920 // it, and return that block.  Otherwise return 0.
921 PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position& pos)
922 {
923     if (pos.isNull())
924         return 0;
925     
926     document()->updateLayoutIgnorePendingStylesheets();
927     
928     // It's strange that this function is responsible for verifying that pos has not been invalidated
929     // by an earlier call to this function.  The caller, applyBlockStyle, should do this.
930     VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
931     VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
932     VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePos);
933     VisiblePosition next = visibleParagraphEnd.next();
934     VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd;
935     
936     Position upstreamStart = visibleParagraphStart.deepEquivalent().upstream();
937     Position upstreamEnd = visibleEnd.deepEquivalent().upstream();
938
939     // If there are no VisiblePositions in the same block as pos then 
940     // upstreamStart will be outside the paragraph
941     if (comparePositions(pos, upstreamStart) < 0)
942         return 0;
943
944     // Perform some checks to see if we need to perform work in this function.
945     if (isBlock(upstreamStart.deprecatedNode())) {
946         // If the block is the root editable element, always move content to a new block,
947         // since it is illegal to modify attributes on the root editable element for editing.
948         if (upstreamStart.deprecatedNode() == editableRootForPosition(upstreamStart)) {
949             // If the block is the root editable element and it contains no visible content, create a new
950             // block but don't try and move content into it, since there's nothing for moveParagraphs to move.
951             if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(upstreamStart.deprecatedNode()->renderer()))
952                 return insertNewDefaultParagraphElementAt(upstreamStart);
953         } else if (isBlock(upstreamEnd.deprecatedNode())) {
954             if (!upstreamEnd.deprecatedNode()->isDescendantOf(upstreamStart.deprecatedNode())) {
955                 // If the paragraph end is a descendant of paragraph start, then we need to run
956                 // the rest of this function. If not, we can bail here.
957                 return 0;
958             }
959         } else if (enclosingBlock(upstreamEnd.deprecatedNode()) != upstreamStart.deprecatedNode()) {
960             // The visibleEnd.  It must be an ancestor of the paragraph start.
961             // We can bail as we have a full block to work with.
962             ASSERT(upstreamStart.deprecatedNode()->isDescendantOf(enclosingBlock(upstreamEnd.deprecatedNode())));
963             return 0;
964         } else if (isEndOfEditableOrNonEditableContent(visibleEnd)) {
965             // At the end of the editable region. We can bail here as well.
966             return 0;
967         }
968     }
969
970     RefPtr<Node> newBlock = insertNewDefaultParagraphElementAt(upstreamStart);
971
972     bool endWasBr = visibleParagraphEnd.deepEquivalent().deprecatedNode()->hasTagName(brTag);
973
974     moveParagraphs(visibleParagraphStart, visibleParagraphEnd, VisiblePosition(firstPositionInNode(newBlock.get())));
975
976     if (newBlock->lastChild() && newBlock->lastChild()->hasTagName(brTag) && !endWasBr)
977         removeNode(newBlock->lastChild());
978
979     return newBlock.release();
980 }
981
982 void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode)
983 {
984     if (!anchorNode)
985         return;
986     
987     ASSERT(anchorNode->isLink());
988     
989     setEndingSelection(VisibleSelection::selectionFromContentsOfNode(anchorNode));
990     applyStyledElement(toElement(anchorNode));
991     // Clones of anchorNode have been pushed down, now remove it.
992     if (anchorNode->inDocument())
993         removeNodePreservingChildren(anchorNode);
994 }
995
996 // Clone the paragraph between start and end under blockElement,
997 // preserving the hierarchy up to outerNode. 
998
999 void CompositeEditCommand::cloneParagraphUnderNewElement(Position& start, Position& end, Node* passedOuterNode, Element* blockElement)
1000 {
1001     // First we clone the outerNode
1002     RefPtr<Node> lastNode;
1003     RefPtr<Node> outerNode = passedOuterNode;
1004
1005     if (outerNode->isRootEditableElement()) {
1006         lastNode = blockElement;
1007     } else {
1008         lastNode = outerNode->cloneNode(isTableElement(outerNode.get()));
1009         appendNode(lastNode, blockElement);
1010     }
1011
1012     if (start.deprecatedNode() != outerNode && lastNode->isElementNode()) {
1013         Vector<RefPtr<Node> > ancestors;
1014         
1015         // Insert each node from innerNode to outerNode (excluded) in a list.
1016         for (Node* n = start.deprecatedNode(); n && n != outerNode; n = n->parentNode())
1017             ancestors.append(n);
1018
1019         // Clone every node between start.deprecatedNode() and outerBlock.
1020
1021         for (size_t i = ancestors.size(); i != 0; --i) {
1022             Node* item = ancestors[i - 1].get();
1023             RefPtr<Node> child = item->cloneNode(isTableElement(item));
1024             appendNode(child, static_cast<Element *>(lastNode.get()));
1025             lastNode = child.release();
1026         }
1027     }
1028
1029     // Handle the case of paragraphs with more than one node,
1030     // cloning all the siblings until end.deprecatedNode() is reached.
1031     
1032     if (start.deprecatedNode() != end.deprecatedNode() && !start.deprecatedNode()->isDescendantOf(end.deprecatedNode())) {
1033         // If end is not a descendant of outerNode we need to
1034         // find the first common ancestor to increase the scope
1035         // of our nextSibling traversal.
1036         while (!end.deprecatedNode()->isDescendantOf(outerNode.get())) {
1037             outerNode = outerNode->parentNode();
1038         }
1039
1040         Node* startNode = start.deprecatedNode();
1041         for (Node* node = NodeTraversal::nextSkippingChildren(startNode, outerNode.get()); node; node = NodeTraversal::nextSkippingChildren(node, outerNode.get())) {
1042             // Move lastNode up in the tree as much as node was moved up in the
1043             // tree by NodeTraversal::nextSkippingChildren, so that the relative depth between
1044             // node and the original start node is maintained in the clone.
1045             while (startNode->parentNode() != node->parentNode()) {
1046                 startNode = startNode->parentNode();
1047                 lastNode = lastNode->parentNode();
1048             }
1049
1050             RefPtr<Node> clonedNode = node->cloneNode(true);
1051             insertNodeAfter(clonedNode, lastNode);
1052             lastNode = clonedNode.release();
1053             if (node == end.deprecatedNode() || end.deprecatedNode()->isDescendantOf(node))
1054                 break;
1055         }
1056     }
1057 }
1058
1059     
1060 // There are bugs in deletion when it removes a fully selected table/list.
1061 // It expands and removes the entire table/list, but will let content
1062 // before and after the table/list collapse onto one line.   
1063 // Deleting a paragraph will leave a placeholder. Remove it (and prune
1064 // empty or unrendered parents).
1065
1066 void CompositeEditCommand::cleanupAfterDeletion(VisiblePosition destination)
1067 {
1068     VisiblePosition caretAfterDelete = endingSelection().visibleStart();
1069     if (caretAfterDelete != destination && isStartOfParagraph(caretAfterDelete) && isEndOfParagraph(caretAfterDelete)) {
1070         // Note: We want the rightmost candidate.
1071         Position position = caretAfterDelete.deepEquivalent().downstream();
1072         Node* node = position.deprecatedNode();
1073         // Normally deletion will leave a br as a placeholder.
1074         if (node->hasTagName(brTag))
1075             removeNodeAndPruneAncestors(node);
1076         // If the selection to move was empty and in an empty block that 
1077         // doesn't require a placeholder to prop itself open (like a bordered
1078         // div or an li), remove it during the move (the list removal code
1079         // expects this behavior).
1080         else if (isBlock(node)) {
1081             // If caret position after deletion and destination position coincides,
1082             // node should not be removed.
1083             if (!position.rendersInDifferentPosition(destination.deepEquivalent())) {
1084                 prune(node);
1085                 return;
1086             }
1087             removeNodeAndPruneAncestors(node);
1088         }
1089         else if (lineBreakExistsAtPosition(position)) {
1090             // There is a preserved '\n' at caretAfterDelete.
1091             // We can safely assume this is a text node.
1092             Text* textNode = toText(node);
1093             if (textNode->length() == 1)
1094                 removeNodeAndPruneAncestors(node);
1095             else
1096                 deleteTextFromNode(textNode, position.deprecatedEditingOffset(), 1);
1097         }
1098     }
1099 }
1100     
1101 // This is a version of moveParagraph that preserves style by keeping the original markup
1102 // It is currently used only by IndentOutdentCommand but it is meant to be used in the
1103 // future by several other commands such as InsertList and the align commands.
1104 // The blockElement parameter is the element to move the paragraph to,
1105 // outerNode is the top element of the paragraph hierarchy. 
1106
1107 void CompositeEditCommand::moveParagraphWithClones(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, Element* blockElement, Node* outerNode)
1108 {
1109     ASSERT(outerNode);
1110     ASSERT(blockElement);
1111
1112     VisiblePosition beforeParagraph = startOfParagraphToMove.previous();
1113     VisiblePosition afterParagraph(endOfParagraphToMove.next());
1114     
1115     // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
1116     // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.
1117     Position start = startOfParagraphToMove.deepEquivalent().downstream();
1118     Position end = endOfParagraphToMove.deepEquivalent().upstream();
1119
1120     cloneParagraphUnderNewElement(start, end, outerNode, blockElement);
1121       
1122     setEndingSelection(VisibleSelection(start, end, DOWNSTREAM));
1123     deleteSelection(false, false, false, false);
1124     
1125     // There are bugs in deletion when it removes a fully selected table/list.
1126     // It expands and removes the entire table/list, but will let content
1127     // before and after the table/list collapse onto one line.
1128        
1129     cleanupAfterDeletion();
1130     
1131     // Add a br if pruning an empty block level element caused a collapse.  For example:
1132     // foo^
1133     // <div>bar</div>
1134     // baz
1135     // Imagine moving 'bar' to ^.  'bar' will be deleted and its div pruned.  That would
1136     // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br.
1137     // Must recononicalize these two VisiblePositions after the pruning above.
1138     beforeParagraph = VisiblePosition(beforeParagraph.deepEquivalent());
1139     afterParagraph = VisiblePosition(afterParagraph.deepEquivalent());
1140
1141     if (beforeParagraph.isNotNull() && !isTableElement(beforeParagraph.deepEquivalent().deprecatedNode())
1142         && ((!isEndOfParagraph(beforeParagraph) && !isStartOfParagraph(beforeParagraph)) || beforeParagraph == afterParagraph)) {
1143         // FIXME: Trim text between beforeParagraph and afterParagraph if they aren't equal.
1144         insertNodeAt(createBreakElement(document()), beforeParagraph.deepEquivalent());
1145     }
1146 }
1147     
1148     
1149 // This moves a paragraph preserving its style.
1150 void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
1151 {
1152     ASSERT(isStartOfParagraph(startOfParagraphToMove));
1153     ASSERT(isEndOfParagraph(endOfParagraphToMove));
1154     moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection, preserveStyle);
1155 }
1156
1157 void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
1158 {
1159     if (startOfParagraphToMove == destination)
1160         return;
1161     
1162     int startIndex = -1;
1163     int endIndex = -1;
1164     int destinationIndex = -1;
1165     bool originalIsDirectional = endingSelection().isDirectional();
1166     if (preserveSelection && !endingSelection().isNone()) {
1167         VisiblePosition visibleStart = endingSelection().visibleStart();
1168         VisiblePosition visibleEnd = endingSelection().visibleEnd();
1169         
1170         bool startAfterParagraph = comparePositions(visibleStart, endOfParagraphToMove) > 0;
1171         bool endBeforeParagraph = comparePositions(visibleEnd, startOfParagraphToMove) < 0;
1172         
1173         if (!startAfterParagraph && !endBeforeParagraph) {
1174             bool startInParagraph = comparePositions(visibleStart, startOfParagraphToMove) >= 0;
1175             bool endInParagraph = comparePositions(visibleEnd, endOfParagraphToMove) <= 0;
1176             
1177             startIndex = 0;
1178             if (startInParagraph) {
1179                 RefPtr<Range> startRange = Range::create(document(), startOfParagraphToMove.deepEquivalent().parentAnchoredEquivalent(), visibleStart.deepEquivalent().parentAnchoredEquivalent());
1180                 startIndex = TextIterator::rangeLength(startRange.get(), true);
1181             }
1182
1183             endIndex = 0;
1184             if (endInParagraph) {
1185                 RefPtr<Range> endRange = Range::create(document(), startOfParagraphToMove.deepEquivalent().parentAnchoredEquivalent(), visibleEnd.deepEquivalent().parentAnchoredEquivalent());
1186                 endIndex = TextIterator::rangeLength(endRange.get(), true);
1187             }
1188         }
1189     }
1190     
1191     VisiblePosition beforeParagraph = startOfParagraphToMove.previous(CannotCrossEditingBoundary);
1192     VisiblePosition afterParagraph(endOfParagraphToMove.next(CannotCrossEditingBoundary));
1193
1194     // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
1195     // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.
1196     Position start = startOfParagraphToMove.deepEquivalent().downstream();
1197     Position end = endOfParagraphToMove.deepEquivalent().upstream();
1198      
1199     // start and end can't be used directly to create a Range; they are "editing positions"
1200     Position startRangeCompliant = start.parentAnchoredEquivalent();
1201     Position endRangeCompliant = end.parentAnchoredEquivalent();
1202     RefPtr<Range> range = Range::create(document(), startRangeCompliant.deprecatedNode(), startRangeCompliant.deprecatedEditingOffset(), endRangeCompliant.deprecatedNode(), endRangeCompliant.deprecatedEditingOffset());
1203
1204     // FIXME: This is an inefficient way to preserve style on nodes in the paragraph to move. It
1205     // shouldn't matter though, since moved paragraphs will usually be quite small.
1206     RefPtr<DocumentFragment> fragment;
1207     // This used to use a ternary for initialization, but that confused some versions of GCC, see bug 37912
1208     if (startOfParagraphToMove != endOfParagraphToMove)
1209         fragment = createFragmentFromMarkup(document(), createMarkup(range.get(), 0, DoNotAnnotateForInterchange, true), "");
1210
1211     // A non-empty paragraph's style is moved when we copy and move it.  We don't move 
1212     // anything if we're given an empty paragraph, but an empty paragraph can have style
1213     // too, <div><b><br></b></div> for example.  Save it so that we can preserve it later.
1214     RefPtr<EditingStyle> styleInEmptyParagraph;
1215     if (startOfParagraphToMove == endOfParagraphToMove && preserveStyle) {
1216         styleInEmptyParagraph = EditingStyle::create(startOfParagraphToMove.deepEquivalent());
1217         styleInEmptyParagraph->mergeTypingStyle(document());
1218         // The moved paragraph should assume the block style of the destination.
1219         styleInEmptyParagraph->removeBlockProperties();
1220     }
1221     
1222     // FIXME (5098931): We should add a new insert action "WebViewInsertActionMoved" and call shouldInsertFragment here.
1223     
1224     setEndingSelection(VisibleSelection(start, end, DOWNSTREAM));
1225     document()->frame()->editor()->clearMisspellingsAndBadGrammar(endingSelection());
1226     deleteSelection(false, false, false, false);
1227
1228     ASSERT(destination.deepEquivalent().anchorNode()->inDocument());
1229     cleanupAfterDeletion(destination);
1230     ASSERT(destination.deepEquivalent().anchorNode()->inDocument());
1231
1232     // Add a br if pruning an empty block level element caused a collapse. For example:
1233     // foo^
1234     // <div>bar</div>
1235     // baz
1236     // Imagine moving 'bar' to ^. 'bar' will be deleted and its div pruned. That would
1237     // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br.
1238     // Must recononicalize these two VisiblePositions after the pruning above.
1239     beforeParagraph = VisiblePosition(beforeParagraph.deepEquivalent());
1240     afterParagraph = VisiblePosition(afterParagraph.deepEquivalent());
1241     if (beforeParagraph.isNotNull() && (!isEndOfParagraph(beforeParagraph) || beforeParagraph == afterParagraph)) {
1242         // FIXME: Trim text between beforeParagraph and afterParagraph if they aren't equal.
1243         insertNodeAt(createBreakElement(document()), beforeParagraph.deepEquivalent());
1244         // Need an updateLayout here in case inserting the br has split a text node.
1245         document()->updateLayoutIgnorePendingStylesheets();
1246     }
1247
1248     RefPtr<Range> startToDestinationRange(Range::create(document(), firstPositionInNode(document()->documentElement()), destination.deepEquivalent().parentAnchoredEquivalent()));
1249     destinationIndex = TextIterator::rangeLength(startToDestinationRange.get(), true);
1250
1251     setEndingSelection(VisibleSelection(destination, originalIsDirectional));
1252     ASSERT(endingSelection().isCaretOrRange());
1253     ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::MovingParagraph;
1254     if (!preserveStyle)
1255         options |= ReplaceSelectionCommand::MatchStyle;
1256     applyCommandToComposite(ReplaceSelectionCommand::create(document(), fragment, options));
1257
1258     document()->frame()->editor()->markMisspellingsAndBadGrammar(endingSelection());
1259
1260     // If the selection is in an empty paragraph, restore styles from the old empty paragraph to the new empty paragraph.
1261     bool selectionIsEmptyParagraph = endingSelection().isCaret() && isStartOfParagraph(endingSelection().visibleStart()) && isEndOfParagraph(endingSelection().visibleStart());
1262     if (styleInEmptyParagraph && selectionIsEmptyParagraph)
1263         applyStyle(styleInEmptyParagraph.get());
1264
1265     if (preserveSelection && startIndex != -1) {
1266         // Fragment creation (using createMarkup) incorrectly uses regular
1267         // spaces instead of nbsps for some spaces that were rendered (11475), which
1268         // causes spaces to be collapsed during the move operation.  This results
1269         // in a call to rangeFromLocationAndLength with a location past the end
1270         // of the document (which will return null).
1271         RefPtr<Range> start = TextIterator::rangeFromLocationAndLength(document()->documentElement(), destinationIndex + startIndex, 0, true);
1272         RefPtr<Range> end = TextIterator::rangeFromLocationAndLength(document()->documentElement(), destinationIndex + endIndex, 0, true);
1273         if (start && end)
1274             setEndingSelection(VisibleSelection(start->startPosition(), end->startPosition(), DOWNSTREAM, originalIsDirectional));
1275     }
1276 }
1277
1278 // FIXME: Send an appropriate shouldDeleteRange call.
1279 bool CompositeEditCommand::breakOutOfEmptyListItem()
1280 {
1281     RefPtr<Node> emptyListItem = enclosingEmptyListItem(endingSelection().visibleStart());
1282     if (!emptyListItem)
1283         return false;
1284
1285     RefPtr<EditingStyle> style = EditingStyle::create(endingSelection().start());
1286     style->mergeTypingStyle(document());
1287
1288     RefPtr<ContainerNode> listNode = emptyListItem->parentNode();
1289     // FIXME: Can't we do something better when the immediate parent wasn't a list node?
1290     if (!listNode
1291         || (!listNode->hasTagName(ulTag) && !listNode->hasTagName(olTag))
1292         || !listNode->rendererIsEditable()
1293         || listNode == emptyListItem->rootEditableElement())
1294         return false;
1295
1296     RefPtr<Element> newBlock = 0;
1297     if (ContainerNode* blockEnclosingList = listNode->parentNode()) {
1298         if (blockEnclosingList->hasTagName(liTag)) { // listNode is inside another list item
1299             if (visiblePositionAfterNode(blockEnclosingList) == visiblePositionAfterNode(listNode.get())) {
1300                 // If listNode appears at the end of the outer list item, then move listNode outside of this list item
1301                 // e.g. <ul><li>hello <ul><li><br></li></ul> </li></ul> should become <ul><li>hello</li> <ul><li><br></li></ul> </ul> after this section
1302                 // If listNode does NOT appear at the end, then we should consider it as a regular paragraph.
1303                 // e.g. <ul><li> <ul><li><br></li></ul> hello</li></ul> should become <ul><li> <div><br></div> hello</li></ul> at the end
1304                 splitElement(toElement(blockEnclosingList), listNode);
1305                 removeNodePreservingChildren(listNode->parentNode());
1306                 newBlock = createListItemElement(document());
1307             }
1308             // If listNode does NOT appear at the end of the outer list item, then behave as if in a regular paragraph.
1309         } else if (blockEnclosingList->hasTagName(olTag) || blockEnclosingList->hasTagName(ulTag))
1310             newBlock = createListItemElement(document());
1311     }
1312     if (!newBlock)
1313         newBlock = createDefaultParagraphElement(document());
1314
1315     RefPtr<Node> previousListNode = emptyListItem->isElementNode() ? toElement(emptyListItem.get())->previousElementSibling(): emptyListItem->previousSibling();
1316     RefPtr<Node> nextListNode = emptyListItem->isElementNode() ? toElement(emptyListItem.get())->nextElementSibling(): emptyListItem->nextSibling();
1317     if (isListItem(nextListNode.get()) || isListElement(nextListNode.get())) {
1318         // If emptyListItem follows another list item or nested list, split the list node.
1319         if (isListItem(previousListNode.get()) || isListElement(previousListNode.get()))
1320             splitElement(toElement(listNode.get()), emptyListItem);
1321
1322         // If emptyListItem is followed by other list item or nested list, then insert newBlock before the list node.
1323         // Because we have splitted the element, emptyListItem is the first element in the list node.
1324         // i.e. insert newBlock before ul or ol whose first element is emptyListItem
1325         insertNodeBefore(newBlock, listNode);
1326         removeNode(emptyListItem);
1327     } else {
1328         // When emptyListItem does not follow any list item or nested list, insert newBlock after the enclosing list node.
1329         // Remove the enclosing node if emptyListItem is the only child; otherwise just remove emptyListItem.
1330         insertNodeAfter(newBlock, listNode);
1331         removeNode(isListItem(previousListNode.get()) || isListElement(previousListNode.get()) ? emptyListItem.get() : listNode.get());
1332     }
1333
1334     appendBlockPlaceholder(newBlock);
1335     setEndingSelection(VisibleSelection(firstPositionInNode(newBlock.get()), DOWNSTREAM, endingSelection().isDirectional()));
1336
1337     style->prepareToApplyAt(endingSelection().start());
1338     if (!style->isEmpty())
1339         applyStyle(style.get());
1340
1341     return true;
1342 }
1343
1344 // If the caret is in an empty quoted paragraph, and either there is nothing before that
1345 // paragraph, or what is before is unquoted, and the user presses delete, unquote that paragraph.
1346 bool CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph()
1347 {
1348     if (!endingSelection().isCaret())
1349         return false;
1350         
1351     VisiblePosition caret(endingSelection().visibleStart());
1352     Node* highestBlockquote = highestEnclosingNodeOfType(caret.deepEquivalent(), &isMailBlockquote);
1353     if (!highestBlockquote)
1354         return false;
1355         
1356     if (!isStartOfParagraph(caret) || !isEndOfParagraph(caret))
1357         return false;
1358     
1359     VisiblePosition previous(caret.previous(CannotCrossEditingBoundary));
1360     // Only move forward if there's nothing before the caret, or if there's unquoted content before it.
1361     if (enclosingNodeOfType(previous.deepEquivalent(), &isMailBlockquote))
1362         return false;
1363     
1364     RefPtr<Node> br = createBreakElement(document());
1365     // We want to replace this quoted paragraph with an unquoted one, so insert a br
1366     // to hold the caret before the highest blockquote.
1367     insertNodeBefore(br, highestBlockquote);
1368     VisiblePosition atBR(positionBeforeNode(br.get()));
1369     // If the br we inserted collapsed, for example foo<br><blockquote>...</blockquote>, insert
1370     // a second one.
1371     if (!isStartOfParagraph(atBR))
1372         insertNodeBefore(createBreakElement(document()), br);
1373     setEndingSelection(VisibleSelection(atBR, endingSelection().isDirectional()));
1374     
1375     // If this is an empty paragraph there must be a line break here.
1376     if (!lineBreakExistsAtVisiblePosition(caret))
1377         return false;
1378
1379     Position caretPos(caret.deepEquivalent().downstream());
1380     // A line break is either a br or a preserved newline.
1381     ASSERT(caretPos.deprecatedNode()->hasTagName(brTag) || (caretPos.deprecatedNode()->isTextNode() && caretPos.deprecatedNode()->renderer()->style()->preserveNewline()));
1382     
1383     if (caretPos.deprecatedNode()->hasTagName(brTag))
1384         removeNodeAndPruneAncestors(caretPos.deprecatedNode());
1385     else if (caretPos.deprecatedNode()->isTextNode()) {
1386         ASSERT(caretPos.deprecatedEditingOffset() == 0);
1387         Text* textNode = toText(caretPos.deprecatedNode());
1388         ContainerNode* parentNode = textNode->parentNode();
1389         // The preserved newline must be the first thing in the node, since otherwise the previous
1390         // paragraph would be quoted, and we verified that it wasn't above.
1391         deleteTextFromNode(textNode, 0, 1);
1392         prune(parentNode);
1393     }
1394
1395     return true;
1396 }
1397
1398 // Operations use this function to avoid inserting content into an anchor when at the start or the end of
1399 // that anchor, as in NSTextView.
1400 // FIXME: This is only an approximation of NSTextViews insertion behavior, which varies depending on how
1401 // the caret was made. 
1402 Position CompositeEditCommand::positionAvoidingSpecialElementBoundary(const Position& original)
1403 {
1404     if (original.isNull())
1405         return original;
1406         
1407     VisiblePosition visiblePos(original);
1408     Node* enclosingAnchor = enclosingAnchorElement(original);
1409     Position result = original;
1410
1411     if (!enclosingAnchor)
1412         return result;
1413
1414     // Don't avoid block level anchors, because that would insert content into the wrong paragraph.
1415     if (enclosingAnchor && !isBlock(enclosingAnchor)) {
1416         VisiblePosition firstInAnchor(firstPositionInNode(enclosingAnchor));
1417         VisiblePosition lastInAnchor(lastPositionInNode(enclosingAnchor));
1418         // If visually just after the anchor, insert *inside* the anchor unless it's the last
1419         // VisiblePosition in the document, to match NSTextView.
1420         if (visiblePos == lastInAnchor) {
1421             // Make sure anchors are pushed down before avoiding them so that we don't
1422             // also avoid structural elements like lists and blocks (5142012).
1423             if (original.deprecatedNode() != enclosingAnchor && original.deprecatedNode()->parentNode() != enclosingAnchor) {
1424                 pushAnchorElementDown(enclosingAnchor);
1425                 enclosingAnchor = enclosingAnchorElement(original);
1426                 if (!enclosingAnchor)
1427                     return original;
1428             }
1429             // Don't insert outside an anchor if doing so would skip over a line break.  It would
1430             // probably be safe to move the line break so that we could still avoid the anchor here.
1431             Position downstream(visiblePos.deepEquivalent().downstream());
1432             if (lineBreakExistsAtVisiblePosition(visiblePos) && downstream.deprecatedNode()->isDescendantOf(enclosingAnchor))
1433                 return original;
1434             
1435             result = positionInParentAfterNode(enclosingAnchor);
1436         }
1437         // If visually just before an anchor, insert *outside* the anchor unless it's the first
1438         // VisiblePosition in a paragraph, to match NSTextView.
1439         if (visiblePos == firstInAnchor) {
1440             // Make sure anchors are pushed down before avoiding them so that we don't
1441             // also avoid structural elements like lists and blocks (5142012).
1442             if (original.deprecatedNode() != enclosingAnchor && original.deprecatedNode()->parentNode() != enclosingAnchor) {
1443                 pushAnchorElementDown(enclosingAnchor);
1444                 enclosingAnchor = enclosingAnchorElement(original);
1445             }
1446             if (!enclosingAnchor)
1447                 return original;
1448
1449             result = positionInParentBeforeNode(enclosingAnchor);
1450         }
1451     }
1452         
1453     if (result.isNull() || !editableRootForPosition(result))
1454         result = original;
1455     
1456     return result;
1457 }
1458
1459 // Splits the tree parent by parent until we reach the specified ancestor. We use VisiblePositions
1460 // to determine if the split is necessary. Returns the last split node.
1461 PassRefPtr<Node> CompositeEditCommand::splitTreeToNode(Node* start, Node* end, bool shouldSplitAncestor)
1462 {
1463     ASSERT(start);
1464     ASSERT(end);
1465     ASSERT(start != end);
1466
1467     RefPtr<Node> node;
1468     if (shouldSplitAncestor && end->parentNode())
1469         end = end->parentNode();
1470
1471     RefPtr<Node> endNode = end;
1472     for (node = start; node && node->parentNode() != endNode; node = node->parentNode()) {
1473         if (!node->parentNode()->isElementNode())
1474             break;
1475         // Do not split a node when doing so introduces an empty node.
1476         VisiblePosition positionInParent = firstPositionInNode(node->parentNode());
1477         VisiblePosition positionInNode = firstPositionInOrBeforeNode(node.get());
1478         if (positionInParent != positionInNode)
1479             splitElement(toElement(node->parentNode()), node);
1480     }
1481
1482     return node.release();
1483 }
1484
1485 PassRefPtr<Element> createBlockPlaceholderElement(Document* document)
1486 {
1487     RefPtr<Element> breakNode = document->createElement(brTag, false);
1488     return breakNode.release();
1489 }
1490
1491 } // namespace WebCore