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