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