Mail hangs when removing multiple rows from large table.
[WebKit-https.git] / Source / WebCore / editing / DeleteSelectionCommand.cpp
index 8242c26..0917b8b 100644 (file)
@@ -30,6 +30,7 @@
 #include "DocumentMarkerController.h"
 #include "Editor.h"
 #include "EditorClient.h"
+#include "ElementIterator.h"
 #include "Frame.h"
 #include "HTMLBRElement.h"
 #include "HTMLLinkElement.h"
@@ -344,6 +345,37 @@ static Position firstEditablePositionInNode(Node* node)
     return next ? firstPositionInOrBeforeNode(next) : Position();
 }
 
+void DeleteSelectionCommand::insertBlockPlaceholderForTableCellIfNeeded(Element& element)
+{
+    // Make sure empty cell has some height.
+    auto* renderer = element.renderer();
+    if (!is<RenderTableCell>(renderer))
+        return;
+    if (downcast<RenderTableCell>(*renderer).contentHeight() > 0)
+        return;
+    insertBlockPlaceholder(firstEditablePositionInNode(&element));
+}
+    
+void DeleteSelectionCommand::removeNodeUpdatingStates(Node& node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
+{
+    if (&node == m_startBlock && !isEndOfBlock(VisiblePosition(firstPositionInNode(m_startBlock.get())).previous()))
+        m_needPlaceholder = true;
+    else if (&node == m_endBlock && !isStartOfBlock(VisiblePosition(lastPositionInNode(m_startBlock.get())).next()))
+        m_needPlaceholder = true;
+    
+    // FIXME: Update the endpoints of the range being deleted.
+    updatePositionForNodeRemoval(m_endingPosition, node);
+    updatePositionForNodeRemoval(m_leadingWhitespace, node);
+    updatePositionForNodeRemoval(m_trailingWhitespace, node);
+    
+    CompositeEditCommand::removeNode(&node, shouldAssumeContentIsAlwaysEditable);
+}
+    
+static inline bool shouldRemoveContentOnly(const Node& node)
+{
+    return isTableStructureNode(&node) || node.isRootEditableElement();
+}
+
 void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
 {
     if (!node)
@@ -371,38 +403,34 @@ void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node, ShouldAssumeConte
         }
     }
     
-    if (isTableStructureNode(node.get()) || node->isRootEditableElement()) {
+    if (shouldRemoveContentOnly(*node)) {
         // Do not remove an element of table structure; remove its contents.
         // Likewise for the root editable element.
-        Node* child = node->firstChild();
+        auto* child = NodeTraversal::next(*node, node.get());
         while (child) {
-            Node* remove = child;
-            child = child->nextSibling();
-            removeNode(remove, shouldAssumeContentIsAlwaysEditable);
+            if (shouldRemoveContentOnly(*child)) {
+                child = NodeTraversal::next(*child, node.get());
+                continue;
+            }
+            auto* remove = child;
+            child = NodeTraversal::nextSkippingChildren(*child, node.get());
+            removeNodeUpdatingStates(*remove, shouldAssumeContentIsAlwaysEditable);
         }
         
-        // Make sure empty cell has some height, if a placeholder can be inserted.
+        ASSERT(is<Element>(*node));
+        auto& element = downcast<Element>(*node);
         document().updateLayoutIgnorePendingStylesheets();
-        RenderObject* renderer = node->renderer();
-        if (is<RenderTableCell>(renderer) && downcast<RenderTableCell>(*renderer).contentHeight() <= 0) {
-            Position firstEditablePosition = firstEditablePositionInNode(node.get());
-            if (firstEditablePosition.isNotNull())
-                insertBlockPlaceholder(firstEditablePosition);
+        // Check if we need to insert a placeholder for descendant table cells.
+        auto* descendant = ElementTraversal::next(element, &element);
+        while (descendant) {
+            auto* placeholderCandidate = descendant;
+            descendant = ElementTraversal::next(*descendant, &element);
+            insertBlockPlaceholderForTableCellIfNeeded(*placeholderCandidate);
         }
+        insertBlockPlaceholderForTableCellIfNeeded(element);
         return;
     }
-    
-    if (node == m_startBlock && !isEndOfBlock(VisiblePosition(firstPositionInNode(m_startBlock.get())).previous()))
-        m_needPlaceholder = true;
-    else if (node == m_endBlock && !isStartOfBlock(VisiblePosition(lastPositionInNode(m_startBlock.get())).next()))
-        m_needPlaceholder = true;
-    
-    // FIXME: Update the endpoints of the range being deleted.
-    updatePositionForNodeRemoval(m_endingPosition, *node);
-    updatePositionForNodeRemoval(m_leadingWhitespace, *node);
-    updatePositionForNodeRemoval(m_trailingWhitespace, *node);
-    
-    CompositeEditCommand::removeNode(node, shouldAssumeContentIsAlwaysEditable);
+    removeNodeUpdatingStates(*node, shouldAssumeContentIsAlwaysEditable);
 }
 
 static void updatePositionForTextRemoval(Node* node, int offset, int count, Position& position)