Home | History | Annotate | Download | only in editing
      1 /*
      2  * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "DeleteSelectionCommand.h"
     28 
     29 #include "CSSMutableStyleDeclaration.h"
     30 #include "Document.h"
     31 #include "DocumentFragment.h"
     32 #include "Editor.h"
     33 #include "EditorClient.h"
     34 #include "Element.h"
     35 #include "Frame.h"
     36 #include "Logging.h"
     37 #include "CSSComputedStyleDeclaration.h"
     38 #include "htmlediting.h"
     39 #include "HTMLInputElement.h"
     40 #include "HTMLNames.h"
     41 #include "markup.h"
     42 #include "RenderTableCell.h"
     43 #include "ReplaceSelectionCommand.h"
     44 #include "Text.h"
     45 #include "TextIterator.h"
     46 #include "visible_units.h"
     47 #include "ApplyStyleCommand.h"
     48 
     49 namespace WebCore {
     50 
     51 using namespace HTMLNames;
     52 
     53 static bool isTableRow(const Node* node)
     54 {
     55     return node && node->hasTagName(trTag);
     56 }
     57 
     58 static bool isTableCellEmpty(Node* cell)
     59 {
     60     ASSERT(isTableCell(cell));
     61     return VisiblePosition(firstDeepEditingPositionForNode(cell)) == VisiblePosition(lastDeepEditingPositionForNode(cell));
     62 }
     63 
     64 static bool isTableRowEmpty(Node* row)
     65 {
     66     if (!isTableRow(row))
     67         return false;
     68 
     69     for (Node* child = row->firstChild(); child; child = child->nextSibling())
     70         if (isTableCell(child) && !isTableCellEmpty(child))
     71             return false;
     72 
     73     return true;
     74 }
     75 
     76 DeleteSelectionCommand::DeleteSelectionCommand(Document *document, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
     77     : CompositeEditCommand(document),
     78       m_hasSelectionToDelete(false),
     79       m_smartDelete(smartDelete),
     80       m_mergeBlocksAfterDelete(mergeBlocksAfterDelete),
     81       m_replace(replace),
     82       m_expandForSpecialElements(expandForSpecialElements),
     83       m_pruneStartBlockIfNecessary(false),
     84       m_startsAtEmptyLine(false),
     85       m_startBlock(0),
     86       m_endBlock(0),
     87       m_typingStyle(0),
     88       m_deleteIntoBlockquoteStyle(0)
     89 {
     90 }
     91 
     92 DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
     93     : CompositeEditCommand(selection.start().node()->document()),
     94       m_hasSelectionToDelete(true),
     95       m_smartDelete(smartDelete),
     96       m_mergeBlocksAfterDelete(mergeBlocksAfterDelete),
     97       m_replace(replace),
     98       m_expandForSpecialElements(expandForSpecialElements),
     99       m_pruneStartBlockIfNecessary(false),
    100       m_startsAtEmptyLine(false),
    101       m_selectionToDelete(selection),
    102       m_startBlock(0),
    103       m_endBlock(0),
    104       m_typingStyle(0),
    105       m_deleteIntoBlockquoteStyle(0)
    106 {
    107 }
    108 
    109 void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end)
    110 {
    111     Node* startSpecialContainer = 0;
    112     Node* endSpecialContainer = 0;
    113 
    114     start = m_selectionToDelete.start();
    115     end = m_selectionToDelete.end();
    116 
    117     // For HRs, we'll get a position at (HR,1) when hitting delete from the beginning of the previous line, or (HR,0) when forward deleting,
    118     // but in these cases, we want to delete it, so manually expand the selection
    119     if (start.node()->hasTagName(hrTag))
    120         start = Position(start.node(), 0);
    121     else if (end.node()->hasTagName(hrTag))
    122         end = Position(end.node(), 1);
    123 
    124     // FIXME: This is only used so that moveParagraphs can avoid the bugs in special element expansion.
    125     if (!m_expandForSpecialElements)
    126         return;
    127 
    128     while (1) {
    129         startSpecialContainer = 0;
    130         endSpecialContainer = 0;
    131 
    132         Position s = positionBeforeContainingSpecialElement(start, &startSpecialContainer);
    133         Position e = positionAfterContainingSpecialElement(end, &endSpecialContainer);
    134 
    135         if (!startSpecialContainer && !endSpecialContainer)
    136             break;
    137 
    138         if (VisiblePosition(start) != m_selectionToDelete.visibleStart() || VisiblePosition(end) != m_selectionToDelete.visibleEnd())
    139             break;
    140 
    141         // If we're going to expand to include the startSpecialContainer, it must be fully selected.
    142         if (startSpecialContainer && !endSpecialContainer && comparePositions(positionInParentAfterNode(startSpecialContainer), end) > -1)
    143             break;
    144 
    145         // If we're going to expand to include the endSpecialContainer, it must be fully selected.
    146         if (endSpecialContainer && !startSpecialContainer && comparePositions(start, positionInParentBeforeNode(endSpecialContainer)) > -1)
    147             break;
    148 
    149         if (startSpecialContainer && startSpecialContainer->isDescendantOf(endSpecialContainer))
    150             // Don't adjust the end yet, it is the end of a special element that contains the start
    151             // special element (which may or may not be fully selected).
    152             start = s;
    153         else if (endSpecialContainer && endSpecialContainer->isDescendantOf(startSpecialContainer))
    154             // Don't adjust the start yet, it is the start of a special element that contains the end
    155             // special element (which may or may not be fully selected).
    156             end = e;
    157         else {
    158             start = s;
    159             end = e;
    160         }
    161     }
    162 }
    163 
    164 void DeleteSelectionCommand::initializePositionData()
    165 {
    166     Position start, end;
    167     initializeStartEnd(start, end);
    168 
    169     m_upstreamStart = start.upstream();
    170     m_downstreamStart = start.downstream();
    171     m_upstreamEnd = end.upstream();
    172     m_downstreamEnd = end.downstream();
    173 
    174     m_startRoot = editableRootForPosition(start);
    175     m_endRoot = editableRootForPosition(end);
    176 
    177     m_startTableRow = enclosingNodeOfType(start, &isTableRow);
    178     m_endTableRow = enclosingNodeOfType(end, &isTableRow);
    179 
    180     // Don't move content out of a table cell.
    181     // If the cell is non-editable, enclosingNodeOfType won't return it by default, so
    182     // tell that function that we don't care if it returns non-editable nodes.
    183     Node* startCell = enclosingNodeOfType(m_upstreamStart, &isTableCell, false);
    184     Node* endCell = enclosingNodeOfType(m_downstreamEnd, &isTableCell, false);
    185     // FIXME: This isn't right.  A borderless table with two rows and a single column would appear as two paragraphs.
    186     if (endCell && endCell != startCell)
    187         m_mergeBlocksAfterDelete = false;
    188 
    189     // Usually the start and the end of the selection to delete are pulled together as a result of the deletion.
    190     // Sometimes they aren't (like when no merge is requested), so we must choose one position to hold the caret
    191     // and receive the placeholder after deletion.
    192     VisiblePosition visibleEnd(m_downstreamEnd);
    193     if (m_mergeBlocksAfterDelete && !isEndOfParagraph(visibleEnd))
    194         m_endingPosition = m_downstreamEnd;
    195     else
    196         m_endingPosition = m_downstreamStart;
    197 
    198     // We don't want to merge into a block if it will mean changing the quote level of content after deleting
    199     // selections that contain a whole number paragraphs plus a line break, since it is unclear to most users
    200     // that such a selection actually ends at the start of the next paragraph. This matches TextEdit behavior
    201     // for indented paragraphs.
    202     // Only apply this rule if the endingSelection is a range selection.  If it is a caret, then other operations have created
    203     // the selection we're deleting (like the process of creating a selection to delete during a backspace), and the user isn't in the situation described above.
    204     if (numEnclosingMailBlockquotes(start) != numEnclosingMailBlockquotes(end)
    205             && isStartOfParagraph(visibleEnd) && isStartOfParagraph(VisiblePosition(start))
    206             && endingSelection().isRange()) {
    207         m_mergeBlocksAfterDelete = false;
    208         m_pruneStartBlockIfNecessary = true;
    209     }
    210 
    211     // Handle leading and trailing whitespace, as well as smart delete adjustments to the selection
    212     m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.affinity());
    213     m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY);
    214 
    215     if (m_smartDelete) {
    216 
    217         // skip smart delete if the selection to delete already starts or ends with whitespace
    218         Position pos = VisiblePosition(m_upstreamStart, m_selectionToDelete.affinity()).deepEquivalent();
    219         bool skipSmartDelete = pos.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull();
    220         if (!skipSmartDelete)
    221             skipSmartDelete = m_downstreamEnd.leadingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull();
    222 
    223         // extend selection upstream if there is whitespace there
    224         bool hasLeadingWhitespaceBeforeAdjustment = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.affinity(), true).isNotNull();
    225         if (!skipSmartDelete && hasLeadingWhitespaceBeforeAdjustment) {
    226             VisiblePosition visiblePos = VisiblePosition(m_upstreamStart, VP_DEFAULT_AFFINITY).previous();
    227             pos = visiblePos.deepEquivalent();
    228             // Expand out one character upstream for smart delete and recalculate
    229             // positions based on this change.
    230             m_upstreamStart = pos.upstream();
    231             m_downstreamStart = pos.downstream();
    232             m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition(visiblePos.affinity());
    233         }
    234 
    235         // trailing whitespace is only considered for smart delete if there is no leading
    236         // whitespace, as in the case where you double-click the first word of a paragraph.
    237         if (!skipSmartDelete && !hasLeadingWhitespaceBeforeAdjustment && m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull()) {
    238             // Expand out one character downstream for smart delete and recalculate
    239             // positions based on this change.
    240             pos = VisiblePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY).next().deepEquivalent();
    241             m_upstreamEnd = pos.upstream();
    242             m_downstreamEnd = pos.downstream();
    243             m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY);
    244         }
    245     }
    246 
    247     // We must pass the positions through rangeCompliantEquivalent, since some editing positions
    248     // that appear inside their nodes aren't really inside them.  [hr, 0] is one example.
    249     // FIXME: rangeComplaintEquivalent should eventually be moved into enclosing element getters
    250     // like the one below, since editing functions should obviously accept editing positions.
    251     // FIXME: Passing false to enclosingNodeOfType tells it that it's OK to return a non-editable
    252     // node.  This was done to match existing behavior, but it seems wrong.
    253     m_startBlock = enclosingNodeOfType(rangeCompliantEquivalent(m_downstreamStart), &isBlock, false);
    254     m_endBlock = enclosingNodeOfType(rangeCompliantEquivalent(m_upstreamEnd), &isBlock, false);
    255 }
    256 
    257 static void removeEnclosingAnchorStyle(CSSMutableStyleDeclaration* style, const Position& position)
    258 {
    259     Node* enclosingAnchor = enclosingAnchorElement(position);
    260     if (!enclosingAnchor || !enclosingAnchor->parentNode())
    261         return;
    262 
    263     removeStylesAddedByNode(style, enclosingAnchor);
    264 }
    265 
    266 void DeleteSelectionCommand::saveTypingStyleState()
    267 {
    268     // A common case is deleting characters that are all from the same text node. In
    269     // that case, the style at the start of the selection before deletion will be the
    270     // same as the style at the start of the selection after deletion (since those
    271     // two positions will be identical). Therefore there is no need to save the
    272     // typing style at the start of the selection, nor is there a reason to
    273     // compute the style at the start of the selection after deletion (see the
    274     // early return in calculateTypingStyleAfterDelete).
    275     if (m_upstreamStart.node() == m_downstreamEnd.node() && m_upstreamStart.node()->isTextNode())
    276         return;
    277 
    278     // Figure out the typing style in effect before the delete is done.
    279     m_typingStyle = editingStyleAtPosition(positionBeforeTabSpan(m_selectionToDelete.start()));
    280 
    281     removeEnclosingAnchorStyle(m_typingStyle.get(), m_selectionToDelete.start());
    282 
    283     // If we're deleting into a Mail blockquote, save the style at end() instead of start()
    284     // We'll use this later in computeTypingStyleAfterDelete if we end up outside of a Mail blockquote
    285     if (nearestMailBlockquote(m_selectionToDelete.start().node()))
    286         m_deleteIntoBlockquoteStyle = editingStyleAtPosition(m_selectionToDelete.end());
    287     else
    288         m_deleteIntoBlockquoteStyle = 0;
    289 }
    290 
    291 bool DeleteSelectionCommand::handleSpecialCaseBRDelete()
    292 {
    293     // Check for special-case where the selection contains only a BR on a line by itself after another BR.
    294     bool upstreamStartIsBR = m_upstreamStart.node()->hasTagName(brTag);
    295     bool downstreamStartIsBR = m_downstreamStart.node()->hasTagName(brTag);
    296     bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && m_downstreamStart.node() == m_upstreamEnd.node();
    297     if (isBROnLineByItself) {
    298         removeNode(m_downstreamStart.node());
    299         return true;
    300     }
    301 
    302     // Not a special-case delete per se, but we can detect that the merging of content between blocks
    303     // should not be done.
    304     if (upstreamStartIsBR && downstreamStartIsBR) {
    305         m_startsAtEmptyLine = true;
    306         m_endingPosition = m_downstreamEnd;
    307     }
    308 
    309     return false;
    310 }
    311 
    312 static void updatePositionForNodeRemoval(Node* node, Position& position)
    313 {
    314     if (position.isNull())
    315         return;
    316     if (node->parent() == position.node() && node->nodeIndex() < (unsigned)position.deprecatedEditingOffset())
    317         position = Position(position.node(), position.deprecatedEditingOffset() - 1);
    318     if (position.node() == node || position.node()->isDescendantOf(node))
    319         position = positionInParentBeforeNode(node);
    320 }
    321 
    322 void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node)
    323 {
    324     if (!node)
    325         return;
    326 
    327     if (m_startRoot != m_endRoot && !(node->isDescendantOf(m_startRoot.get()) && node->isDescendantOf(m_endRoot.get()))) {
    328         // If a node is not in both the start and end editable roots, remove it only if its inside an editable region.
    329         if (!node->parentNode()->isContentEditable()) {
    330             // Don't remove non-editable atomic nodes.
    331             if (!node->firstChild())
    332                 return;
    333             // Search this non-editable region for editable regions to empty.
    334             RefPtr<Node> child = node->firstChild();
    335             while (child) {
    336                 RefPtr<Node> nextChild = child->nextSibling();
    337                 removeNode(child.get());
    338                 // Bail if nextChild is no longer node's child.
    339                 if (nextChild && nextChild->parentNode() != node)
    340                     return;
    341                 child = nextChild;
    342             }
    343 
    344             // Don't remove editable regions that are inside non-editable ones, just clear them.
    345             return;
    346         }
    347     }
    348 
    349     if (isTableStructureNode(node.get()) || node == node->rootEditableElement()) {
    350         // Do not remove an element of table structure; remove its contents.
    351         // Likewise for the root editable element.
    352         Node* child = node->firstChild();
    353         while (child) {
    354             Node* remove = child;
    355             child = child->nextSibling();
    356             removeNode(remove);
    357         }
    358 
    359         // make sure empty cell has some height
    360         updateLayout();
    361         RenderObject *r = node->renderer();
    362         if (r && r->isTableCell() && toRenderTableCell(r)->contentHeight() <= 0)
    363             insertBlockPlaceholder(Position(node, 0));
    364         return;
    365     }
    366 
    367     if (node == m_startBlock && !isEndOfBlock(VisiblePosition(firstDeepEditingPositionForNode(m_startBlock.get())).previous()))
    368         m_needPlaceholder = true;
    369     else if (node == m_endBlock && !isStartOfBlock(VisiblePosition(lastDeepEditingPositionForNode(m_startBlock.get())).next()))
    370         m_needPlaceholder = true;
    371 
    372     // FIXME: Update the endpoints of the range being deleted.
    373     updatePositionForNodeRemoval(node.get(), m_endingPosition);
    374     updatePositionForNodeRemoval(node.get(), m_leadingWhitespace);
    375     updatePositionForNodeRemoval(node.get(), m_trailingWhitespace);
    376 
    377     CompositeEditCommand::removeNode(node);
    378 }
    379 
    380 static void updatePositionForTextRemoval(Node* node, int offset, int count, Position& position)
    381 {
    382     if (position.node() == node) {
    383         if (position.deprecatedEditingOffset() > offset + count)
    384             position = Position(position.node(), position.deprecatedEditingOffset() - count);
    385         else if (position.deprecatedEditingOffset() > offset)
    386             position = Position(position.node(), offset);
    387     }
    388 }
    389 
    390 void DeleteSelectionCommand::deleteTextFromNode(PassRefPtr<Text> node, unsigned offset, unsigned count)
    391 {
    392     // FIXME: Update the endpoints of the range being deleted.
    393     updatePositionForTextRemoval(node.get(), offset, count, m_endingPosition);
    394     updatePositionForTextRemoval(node.get(), offset, count, m_leadingWhitespace);
    395     updatePositionForTextRemoval(node.get(), offset, count, m_trailingWhitespace);
    396     updatePositionForTextRemoval(node.get(), offset, count, m_downstreamEnd);
    397 
    398     CompositeEditCommand::deleteTextFromNode(node, offset, count);
    399 }
    400 
    401 void DeleteSelectionCommand::handleGeneralDelete()
    402 {
    403     int startOffset = m_upstreamStart.deprecatedEditingOffset();
    404     Node* startNode = m_upstreamStart.node();
    405 
    406     // Never remove the start block unless it's a table, in which case we won't merge content in.
    407     if (startNode == m_startBlock && startOffset == 0 && canHaveChildrenForEditing(startNode) && !startNode->hasTagName(tableTag)) {
    408         startOffset = 0;
    409         startNode = startNode->traverseNextNode();
    410     }
    411 
    412     if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) {
    413         Text *text = static_cast<Text *>(startNode);
    414         if (text->length() > (unsigned)caretMaxOffset(startNode))
    415             deleteTextFromNode(text, caretMaxOffset(startNode), text->length() - caretMaxOffset(startNode));
    416     }
    417 
    418     if (startOffset >= lastOffsetForEditing(startNode)) {
    419         startNode = startNode->traverseNextSibling();
    420         startOffset = 0;
    421     }
    422 
    423     // Done adjusting the start.  See if we're all done.
    424     if (!startNode)
    425         return;
    426 
    427     if (startNode == m_downstreamEnd.node()) {
    428         // The selection to delete is all in one node.
    429         if (!startNode->renderer() || (startOffset == 0 && m_downstreamEnd.atLastEditingPositionForNode())) {
    430             // just delete
    431             removeNode(startNode);
    432         } else if (m_downstreamEnd.deprecatedEditingOffset() - startOffset > 0) {
    433             if (startNode->isTextNode()) {
    434                 // in a text node that needs to be trimmed
    435                 Text* text = static_cast<Text*>(startNode);
    436                 deleteTextFromNode(text, startOffset, m_downstreamEnd.deprecatedEditingOffset() - startOffset);
    437             } else {
    438                 removeChildrenInRange(startNode, startOffset, m_downstreamEnd.deprecatedEditingOffset());
    439                 m_endingPosition = m_upstreamStart;
    440             }
    441         }
    442     }
    443     else {
    444         bool startNodeWasDescendantOfEndNode = m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node());
    445         // The selection to delete spans more than one node.
    446         RefPtr<Node> node(startNode);
    447 
    448         if (startOffset > 0) {
    449             if (startNode->isTextNode()) {
    450                 // in a text node that needs to be trimmed
    451                 Text *text = static_cast<Text *>(node.get());
    452                 deleteTextFromNode(text, startOffset, text->length() - startOffset);
    453                 node = node->traverseNextNode();
    454             } else {
    455                 node = startNode->childNode(startOffset);
    456             }
    457         }
    458 
    459         // handle deleting all nodes that are completely selected
    460         while (node && node != m_downstreamEnd.node()) {
    461             if (comparePositions(Position(node.get(), 0), m_downstreamEnd) >= 0) {
    462                 // traverseNextSibling just blew past the end position, so stop deleting
    463                 node = 0;
    464             } else if (!m_downstreamEnd.node()->isDescendantOf(node.get())) {
    465                 RefPtr<Node> nextNode = node->traverseNextSibling();
    466                 // if we just removed a node from the end container, update end position so the
    467                 // check above will work
    468                 if (node->parentNode() == m_downstreamEnd.node()) {
    469                     ASSERT(node->nodeIndex() < (unsigned)m_downstreamEnd.deprecatedEditingOffset());
    470                     m_downstreamEnd = Position(m_downstreamEnd.node(), m_downstreamEnd.deprecatedEditingOffset() - 1);
    471                 }
    472                 removeNode(node.get());
    473                 node = nextNode.get();
    474             } else {
    475                 Node* n = node->lastDescendant();
    476                 if (m_downstreamEnd.node() == n && m_downstreamEnd.deprecatedEditingOffset() >= caretMaxOffset(n)) {
    477                     removeNode(node.get());
    478                     node = 0;
    479                 } else
    480                     node = node->traverseNextNode();
    481             }
    482         }
    483 
    484         if (m_downstreamEnd.node() != startNode && !m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node()) && m_downstreamEnd.node()->inDocument() && m_downstreamEnd.deprecatedEditingOffset() >= caretMinOffset(m_downstreamEnd.node())) {
    485             if (m_downstreamEnd.atLastEditingPositionForNode() && !canHaveChildrenForEditing(m_downstreamEnd.node())) {
    486                 // The node itself is fully selected, not just its contents.  Delete it.
    487                 removeNode(m_downstreamEnd.node());
    488             } else {
    489                 if (m_downstreamEnd.node()->isTextNode()) {
    490                     // in a text node that needs to be trimmed
    491                     Text *text = static_cast<Text *>(m_downstreamEnd.node());
    492                     if (m_downstreamEnd.deprecatedEditingOffset() > 0) {
    493                         deleteTextFromNode(text, 0, m_downstreamEnd.deprecatedEditingOffset());
    494                     }
    495                 // Remove children of m_downstreamEnd.node() that come after m_upstreamStart.
    496                 // Don't try to remove children if m_upstreamStart was inside m_downstreamEnd.node()
    497                 // and m_upstreamStart has been removed from the document, because then we don't
    498                 // know how many children to remove.
    499                 // FIXME: Make m_upstreamStart a position we update as we remove content, then we can
    500                 // always know which children to remove.
    501                 } else if (!(startNodeWasDescendantOfEndNode && !m_upstreamStart.node()->inDocument())) {
    502                     int offset = 0;
    503                     if (m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node())) {
    504                         Node *n = m_upstreamStart.node();
    505                         while (n && n->parentNode() != m_downstreamEnd.node())
    506                             n = n->parentNode();
    507                         if (n)
    508                             offset = n->nodeIndex() + 1;
    509                     }
    510                     removeChildrenInRange(m_downstreamEnd.node(), offset, m_downstreamEnd.deprecatedEditingOffset());
    511                     m_downstreamEnd = Position(m_downstreamEnd.node(), offset);
    512                 }
    513             }
    514         }
    515     }
    516 }
    517 
    518 void DeleteSelectionCommand::fixupWhitespace()
    519 {
    520     updateLayout();
    521     // FIXME: isRenderedCharacter should be removed, and we should use VisiblePosition::characterAfter and VisiblePosition::characterBefore
    522     if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter() && m_leadingWhitespace.node()->isTextNode()) {
    523         Text* textNode = static_cast<Text*>(m_leadingWhitespace.node());
    524         ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace());
    525         replaceTextInNode(textNode, m_leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
    526     }
    527     if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter() && m_trailingWhitespace.node()->isTextNode()) {
    528         Text* textNode = static_cast<Text*>(m_trailingWhitespace.node());
    529         ASSERT(!textNode->renderer() ||textNode->renderer()->style()->collapseWhiteSpace());
    530         replaceTextInNode(textNode, m_trailingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
    531     }
    532 }
    533 
    534 // If a selection starts in one block and ends in another, we have to merge to bring content before the
    535 // start together with content after the end.
    536 void DeleteSelectionCommand::mergeParagraphs()
    537 {
    538     if (!m_mergeBlocksAfterDelete) {
    539         if (m_pruneStartBlockIfNecessary) {
    540             // We aren't going to merge into the start block, so remove it if it's empty.
    541             prune(m_startBlock);
    542             // Removing the start block during a deletion is usually an indication that we need
    543             // a placeholder, but not in this case.
    544             m_needPlaceholder = false;
    545         }
    546         return;
    547     }
    548 
    549     // It shouldn't have been asked to both try and merge content into the start block and prune it.
    550     ASSERT(!m_pruneStartBlockIfNecessary);
    551 
    552     // FIXME: Deletion should adjust selection endpoints as it removes nodes so that we never get into this state (4099839).
    553     if (!m_downstreamEnd.node()->inDocument() || !m_upstreamStart.node()->inDocument())
    554          return;
    555 
    556     // FIXME: The deletion algorithm shouldn't let this happen.
    557     if (comparePositions(m_upstreamStart, m_downstreamEnd) > 0)
    558         return;
    559 
    560     // There's nothing to merge.
    561     if (m_upstreamStart == m_downstreamEnd)
    562         return;
    563 
    564     VisiblePosition startOfParagraphToMove(m_downstreamEnd);
    565     VisiblePosition mergeDestination(m_upstreamStart);
    566 
    567     // m_downstreamEnd's block has been emptied out by deletion.  There is no content inside of it to
    568     // move, so just remove it.
    569     Element* endBlock = static_cast<Element*>(enclosingBlock(m_downstreamEnd.node()));
    570     if (!startOfParagraphToMove.deepEquivalent().node() || !endBlock->contains(startOfParagraphToMove.deepEquivalent().node())) {
    571         removeNode(enclosingBlock(m_downstreamEnd.node()));
    572         return;
    573     }
    574 
    575     // We need to merge into m_upstreamStart's block, but it's been emptied out and collapsed by deletion.
    576     if (!mergeDestination.deepEquivalent().node() || !mergeDestination.deepEquivalent().node()->isDescendantOf(m_upstreamStart.node()->enclosingBlockFlowElement()) || m_startsAtEmptyLine) {
    577         insertNodeAt(createBreakElement(document()).get(), m_upstreamStart);
    578         mergeDestination = VisiblePosition(m_upstreamStart);
    579     }
    580 
    581     if (mergeDestination == startOfParagraphToMove)
    582         return;
    583 
    584     VisiblePosition endOfParagraphToMove = endOfParagraph(startOfParagraphToMove);
    585 
    586     if (mergeDestination == endOfParagraphToMove)
    587         return;
    588 
    589     // The rule for merging into an empty block is: only do so if its farther to the right.
    590     // FIXME: Consider RTL.
    591     if (!m_startsAtEmptyLine && isStartOfParagraph(mergeDestination) && startOfParagraphToMove.absoluteCaretBounds().x() > mergeDestination.absoluteCaretBounds().x()) {
    592         if (mergeDestination.deepEquivalent().downstream().node()->hasTagName(brTag)) {
    593             removeNodeAndPruneAncestors(mergeDestination.deepEquivalent().downstream().node());
    594             m_endingPosition = startOfParagraphToMove.deepEquivalent();
    595             return;
    596         }
    597     }
    598 
    599     // Block images, tables and horizontal rules cannot be made inline with content at mergeDestination.  If there is
    600     // any (!isStartOfParagraph(mergeDestination)), don't merge, just move the caret to just before the selection we deleted.
    601     // See https://bugs.webkit.org/show_bug.cgi?id=25439
    602     if (isRenderedAsNonInlineTableImageOrHR(startOfParagraphToMove.deepEquivalent().node()) && !isStartOfParagraph(mergeDestination)) {
    603         m_endingPosition = m_upstreamStart;
    604         return;
    605     }
    606 
    607     RefPtr<Range> range = Range::create(document(), rangeCompliantEquivalent(startOfParagraphToMove.deepEquivalent()), rangeCompliantEquivalent(endOfParagraphToMove.deepEquivalent()));
    608     RefPtr<Range> rangeToBeReplaced = Range::create(document(), rangeCompliantEquivalent(mergeDestination.deepEquivalent()), rangeCompliantEquivalent(mergeDestination.deepEquivalent()));
    609     if (!document()->frame()->editor()->client()->shouldMoveRangeAfterDelete(range.get(), rangeToBeReplaced.get()))
    610         return;
    611 
    612     // moveParagraphs will insert placeholders if it removes blocks that would require their use, don't let block
    613     // removals that it does cause the insertion of *another* placeholder.
    614     bool needPlaceholder = m_needPlaceholder;
    615     moveParagraph(startOfParagraphToMove, endOfParagraphToMove, mergeDestination);
    616     m_needPlaceholder = needPlaceholder;
    617     // The endingPosition was likely clobbered by the move, so recompute it (moveParagraph selects the moved paragraph).
    618     m_endingPosition = endingSelection().start();
    619 }
    620 
    621 void DeleteSelectionCommand::removePreviouslySelectedEmptyTableRows()
    622 {
    623     if (m_endTableRow && m_endTableRow->inDocument() && m_endTableRow != m_startTableRow) {
    624         Node* row = m_endTableRow->previousSibling();
    625         while (row && row != m_startTableRow) {
    626             RefPtr<Node> previousRow = row->previousSibling();
    627             if (isTableRowEmpty(row))
    628                 // Use a raw removeNode, instead of DeleteSelectionCommand's, because
    629                 // that won't remove rows, it only empties them in preparation for this function.
    630                 CompositeEditCommand::removeNode(row);
    631             row = previousRow.get();
    632         }
    633     }
    634 
    635     // Remove empty rows after the start row.
    636     if (m_startTableRow && m_startTableRow->inDocument() && m_startTableRow != m_endTableRow) {
    637         Node* row = m_startTableRow->nextSibling();
    638         while (row && row != m_endTableRow) {
    639             RefPtr<Node> nextRow = row->nextSibling();
    640             if (isTableRowEmpty(row))
    641                 CompositeEditCommand::removeNode(row);
    642             row = nextRow.get();
    643         }
    644     }
    645 
    646     if (m_endTableRow && m_endTableRow->inDocument() && m_endTableRow != m_startTableRow)
    647         if (isTableRowEmpty(m_endTableRow.get())) {
    648             // Don't remove m_endTableRow if it's where we're putting the ending selection.
    649             if (!m_endingPosition.node()->isDescendantOf(m_endTableRow.get())) {
    650                 // FIXME: We probably shouldn't remove m_endTableRow unless it's fully selected, even if it is empty.
    651                 // We'll need to start adjusting the selection endpoints during deletion to know whether or not m_endTableRow
    652                 // was fully selected here.
    653                 CompositeEditCommand::removeNode(m_endTableRow.get());
    654             }
    655         }
    656 }
    657 
    658 void DeleteSelectionCommand::calculateTypingStyleAfterDelete()
    659 {
    660     if (!m_typingStyle)
    661         return;
    662 
    663     // Compute the difference between the style before the delete and the style now
    664     // after the delete has been done. Set this style on the frame, so other editing
    665     // commands being composed with this one will work, and also cache it on the command,
    666     // so the Frame::appliedEditing can set it after the whole composite command
    667     // has completed.
    668 
    669     // If we deleted into a blockquote, but are now no longer in a blockquote, use the alternate typing style
    670     if (m_deleteIntoBlockquoteStyle && !nearestMailBlockquote(m_endingPosition.node()))
    671         m_typingStyle = m_deleteIntoBlockquoteStyle;
    672     m_deleteIntoBlockquoteStyle = 0;
    673 
    674     prepareEditingStyleToApplyAt(m_typingStyle.get(), m_endingPosition);
    675     if (!m_typingStyle->length())
    676         m_typingStyle = 0;
    677     VisiblePosition visibleEnd(m_endingPosition);
    678     if (m_typingStyle &&
    679         isStartOfParagraph(visibleEnd) &&
    680         isEndOfParagraph(visibleEnd) &&
    681         lineBreakExistsAtVisiblePosition(visibleEnd)) {
    682         // Apply style to the placeholder that is now holding open the empty paragraph.
    683         // This makes sure that the paragraph has the right height, and that the paragraph
    684         // takes on the right style and retains it even if you move the selection away and
    685         // then move it back (which will clear typing style).
    686 
    687         setEndingSelection(visibleEnd);
    688         applyStyle(m_typingStyle.get(), EditActionUnspecified);
    689         // applyStyle can destroy the placeholder that was at m_endingPosition if it needs to
    690         // move it, but it will set an endingSelection() at [movedPlaceholder, 0] if it does so.
    691         m_endingPosition = endingSelection().start();
    692         m_typingStyle = 0;
    693     }
    694     // This is where we've deleted all traces of a style but not a whole paragraph (that's handled above).
    695     // In this case if we start typing, the new characters should have the same style as the just deleted ones,
    696     // but, if we change the selection, come back and start typing that style should be lost.  Also see
    697     // preserveTypingStyle() below.
    698     document()->frame()->setTypingStyle(m_typingStyle.get());
    699 }
    700 
    701 void DeleteSelectionCommand::clearTransientState()
    702 {
    703     m_selectionToDelete = VisibleSelection();
    704     m_upstreamStart.clear();
    705     m_downstreamStart.clear();
    706     m_upstreamEnd.clear();
    707     m_downstreamEnd.clear();
    708     m_endingPosition.clear();
    709     m_leadingWhitespace.clear();
    710     m_trailingWhitespace.clear();
    711 }
    712 
    713 void DeleteSelectionCommand::doApply()
    714 {
    715     // If selection has not been set to a custom selection when the command was created,
    716     // use the current ending selection.
    717     if (!m_hasSelectionToDelete)
    718         m_selectionToDelete = endingSelection();
    719 
    720     if (!m_selectionToDelete.isRange())
    721         return;
    722 
    723     // If the deletion is occurring in a text field, and we're not deleting to replace the selection, then let the frame call across the bridge to notify the form delegate.
    724     if (!m_replace) {
    725         Node* startNode = m_selectionToDelete.start().node();
    726         Node* ancestorNode = startNode ? startNode->shadowAncestorNode() : 0;
    727         if (ancestorNode && ancestorNode->hasTagName(inputTag)
    728                 && static_cast<HTMLInputElement*>(ancestorNode)->isTextField()
    729                 && ancestorNode->focused())
    730             document()->frame()->textWillBeDeletedInTextField(static_cast<Element*>(ancestorNode));
    731     }
    732 
    733     // save this to later make the selection with
    734     EAffinity affinity = m_selectionToDelete.affinity();
    735 
    736     Position downstreamEnd = m_selectionToDelete.end().downstream();
    737     m_needPlaceholder = isStartOfParagraph(m_selectionToDelete.visibleStart()) &&
    738                         isEndOfParagraph(m_selectionToDelete.visibleEnd()) &&
    739                         !lineBreakExistsAtVisiblePosition(m_selectionToDelete.visibleEnd());
    740     if (m_needPlaceholder) {
    741         // Don't need a placeholder when deleting a selection that starts just before a table
    742         // and ends inside it (we do need placeholders to hold open empty cells, but that's
    743         // handled elsewhere).
    744         if (Node* table = isLastPositionBeforeTable(m_selectionToDelete.visibleStart()))
    745             if (m_selectionToDelete.end().node()->isDescendantOf(table))
    746                 m_needPlaceholder = false;
    747     }
    748 
    749 
    750     // set up our state
    751     initializePositionData();
    752 
    753     // Delete any text that may hinder our ability to fixup whitespace after the delete
    754     deleteInsignificantTextDownstream(m_trailingWhitespace);
    755 
    756     saveTypingStyleState();
    757 
    758     // deleting just a BR is handled specially, at least because we do not
    759     // want to replace it with a placeholder BR!
    760     if (handleSpecialCaseBRDelete()) {
    761         calculateTypingStyleAfterDelete();
    762         setEndingSelection(VisibleSelection(m_endingPosition, affinity));
    763         clearTransientState();
    764         rebalanceWhitespace();
    765         return;
    766     }
    767 
    768     handleGeneralDelete();
    769 
    770     fixupWhitespace();
    771 
    772     mergeParagraphs();
    773 
    774     removePreviouslySelectedEmptyTableRows();
    775 
    776     RefPtr<Node> placeholder = m_needPlaceholder ? createBreakElement(document()).get() : 0;
    777 
    778     if (placeholder)
    779         insertNodeAt(placeholder.get(), m_endingPosition);
    780 
    781     rebalanceWhitespaceAt(m_endingPosition);
    782 
    783     calculateTypingStyleAfterDelete();
    784 
    785     setEndingSelection(VisibleSelection(m_endingPosition, affinity));
    786     clearTransientState();
    787 }
    788 
    789 EditAction DeleteSelectionCommand::editingAction() const
    790 {
    791     // Note that DeleteSelectionCommand is also used when the user presses the Delete key,
    792     // but in that case there's a TypingCommand that supplies the editingAction(), so
    793     // the Undo menu correctly shows "Undo Typing"
    794     return EditActionCut;
    795 }
    796 
    797 // Normally deletion doesn't preserve the typing style that was present before it.  For example,
    798 // type a character, Bold, then delete the character and start typing.  The Bold typing style shouldn't
    799 // stick around.  Deletion should preserve a typing style that *it* sets, however.
    800 bool DeleteSelectionCommand::preservesTypingStyle() const
    801 {
    802     return m_typingStyle;
    803 }
    804 
    805 } // namespace WebCore
    806