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