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