Changeset 212151 in webkit


Ignore:
Timestamp:
Feb 10, 2017 2:36:12 PM (7 years ago)
Author:
Alan Bujtas
Message:

Mail hangs when removing multiple rows from large table.
https://bugs.webkit.org/show_bug.cgi?id=168103
<rdar://problem/30090186>

Reviewed by Ryosuke Niwa.

PerformanceTests:

  • DOM/large-table-edit.html: Added.

Source/WebCore:

DeleteSelectionCommand::removeNode doesn't actually destroy table structure items,
but instead it removes their content. In order to be able to continue editing the table after
the delete, we need to ensure that its cells' width and height are > 0. Currently we issue layout on
each table item recursively.
This patch delays the layout until after we've finished with the entire subtree delete (10x progression).

Performance test added.

  • editing/DeleteSelectionCommand.cpp:

(WebCore::DeleteSelectionCommand::insertBlockPlaceholderForTableCellIfNeeded):
(WebCore::DeleteSelectionCommand::removeNodeUpdatingStates):
(WebCore::shouldRemoveContentOnly):
(WebCore::DeleteSelectionCommand::removeNode):

  • editing/DeleteSelectionCommand.h:
Location:
trunk
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/PerformanceTests/ChangeLog

    r212129 r212151  
     12017-02-10  Zalan Bujtas  <zalan@apple.com>
     2
     3        Mail hangs when removing multiple rows from large table.
     4        https://bugs.webkit.org/show_bug.cgi?id=168103
     5        <rdar://problem/30090186>
     6
     7        Reviewed by Ryosuke Niwa.
     8
     9        * DOM/large-table-edit.html: Added.
     10
    1112017-02-05  Filip Pizlo  <fpizlo@apple.com>
    212
  • trunk/Source/WebCore/ChangeLog

    r212150 r212151  
     12017-02-10  Zalan Bujtas  <zalan@apple.com>
     2
     3        Mail hangs when removing multiple rows from large table.
     4        https://bugs.webkit.org/show_bug.cgi?id=168103
     5        <rdar://problem/30090186>
     6
     7        Reviewed by Ryosuke Niwa.
     8
     9        DeleteSelectionCommand::removeNode doesn't actually destroy table structure items,
     10        but instead it removes their content. In order to be able to continue editing the table after
     11        the delete, we need to ensure that its cells' width and height are > 0. Currently we issue layout on
     12        each table item recursively.
     13        This patch delays the layout until after we've finished with the entire subtree delete (10x progression).
     14
     15        Performance test added.
     16
     17        * editing/DeleteSelectionCommand.cpp:
     18        (WebCore::DeleteSelectionCommand::insertBlockPlaceholderForTableCellIfNeeded):
     19        (WebCore::DeleteSelectionCommand::removeNodeUpdatingStates):
     20        (WebCore::shouldRemoveContentOnly):
     21        (WebCore::DeleteSelectionCommand::removeNode):
     22        * editing/DeleteSelectionCommand.h:
     23
    1242017-02-10  Joseph Pecoraro  <pecoraro@apple.com>
    225
  • trunk/Source/WebCore/editing/DeleteSelectionCommand.cpp

    r211591 r212151  
    3131#include "Editor.h"
    3232#include "EditorClient.h"
     33#include "ElementIterator.h"
    3334#include "Frame.h"
    3435#include "HTMLBRElement.h"
     
    343344        next = NodeTraversal::next(*next, node);
    344345    return next ? firstPositionInOrBeforeNode(next) : Position();
     346}
     347
     348void DeleteSelectionCommand::insertBlockPlaceholderForTableCellIfNeeded(Element& element)
     349{
     350    // Make sure empty cell has some height.
     351    auto* renderer = element.renderer();
     352    if (!is<RenderTableCell>(renderer))
     353        return;
     354    if (downcast<RenderTableCell>(*renderer).contentHeight() > 0)
     355        return;
     356    insertBlockPlaceholder(firstEditablePositionInNode(&element));
     357}
     358   
     359void DeleteSelectionCommand::removeNodeUpdatingStates(Node& node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
     360{
     361    if (&node == m_startBlock && !isEndOfBlock(VisiblePosition(firstPositionInNode(m_startBlock.get())).previous()))
     362        m_needPlaceholder = true;
     363    else if (&node == m_endBlock && !isStartOfBlock(VisiblePosition(lastPositionInNode(m_startBlock.get())).next()))
     364        m_needPlaceholder = true;
     365   
     366    // FIXME: Update the endpoints of the range being deleted.
     367    updatePositionForNodeRemoval(m_endingPosition, node);
     368    updatePositionForNodeRemoval(m_leadingWhitespace, node);
     369    updatePositionForNodeRemoval(m_trailingWhitespace, node);
     370   
     371    CompositeEditCommand::removeNode(&node, shouldAssumeContentIsAlwaysEditable);
     372}
     373   
     374static inline bool shouldRemoveContentOnly(const Node& node)
     375{
     376    return isTableStructureNode(&node) || node.isRootEditableElement();
    345377}
    346378
     
    372404    }
    373405   
    374     if (isTableStructureNode(node.get()) || node->isRootEditableElement()) {
     406    if (shouldRemoveContentOnly(*node)) {
    375407        // Do not remove an element of table structure; remove its contents.
    376408        // Likewise for the root editable element.
    377         Node* child = node->firstChild();
     409        auto* child = NodeTraversal::next(*node, node.get());
    378410        while (child) {
    379             Node* remove = child;
    380             child = child->nextSibling();
    381             removeNode(remove, shouldAssumeContentIsAlwaysEditable);
    382         }
    383        
    384         // Make sure empty cell has some height, if a placeholder can be inserted.
     411            if (shouldRemoveContentOnly(*child)) {
     412                child = NodeTraversal::next(*child, node.get());
     413                continue;
     414            }
     415            auto* remove = child;
     416            child = NodeTraversal::nextSkippingChildren(*child, node.get());
     417            removeNodeUpdatingStates(*remove, shouldAssumeContentIsAlwaysEditable);
     418        }
     419       
     420        ASSERT(is<Element>(*node));
     421        auto& element = downcast<Element>(*node);
    385422        document().updateLayoutIgnorePendingStylesheets();
    386         RenderObject* renderer = node->renderer();
    387         if (is<RenderTableCell>(renderer) && downcast<RenderTableCell>(*renderer).contentHeight() <= 0) {
    388             Position firstEditablePosition = firstEditablePositionInNode(node.get());
    389             if (firstEditablePosition.isNotNull())
    390                 insertBlockPlaceholder(firstEditablePosition);
    391         }
    392         return;
    393     }
    394    
    395     if (node == m_startBlock && !isEndOfBlock(VisiblePosition(firstPositionInNode(m_startBlock.get())).previous()))
    396         m_needPlaceholder = true;
    397     else if (node == m_endBlock && !isStartOfBlock(VisiblePosition(lastPositionInNode(m_startBlock.get())).next()))
    398         m_needPlaceholder = true;
    399    
    400     // FIXME: Update the endpoints of the range being deleted.
    401     updatePositionForNodeRemoval(m_endingPosition, *node);
    402     updatePositionForNodeRemoval(m_leadingWhitespace, *node);
    403     updatePositionForNodeRemoval(m_trailingWhitespace, *node);
    404    
    405     CompositeEditCommand::removeNode(node, shouldAssumeContentIsAlwaysEditable);
     423        // Check if we need to insert a placeholder for descendant table cells.
     424        auto* descendant = ElementTraversal::next(element, &element);
     425        while (descendant) {
     426            auto* placeholderCandidate = descendant;
     427            descendant = ElementTraversal::next(*descendant, &element);
     428            insertBlockPlaceholderForTableCellIfNeeded(*placeholderCandidate);
     429        }
     430        insertBlockPlaceholderForTableCellIfNeeded(element);
     431        return;
     432    }
     433    removeNodeUpdatingStates(*node, shouldAssumeContentIsAlwaysEditable);
    406434}
    407435
  • trunk/Source/WebCore/editing/DeleteSelectionCommand.h

    r209871 r212151  
    7474    String originalStringForAutocorrectionAtBeginningOfSelection();
    7575
     76    void removeNodeUpdatingStates(Node&, ShouldAssumeContentIsAlwaysEditable);
     77    void insertBlockPlaceholderForTableCellIfNeeded(Element&);
     78
    7679    bool m_hasSelectionToDelete;
    7780    bool m_smartDelete;
Note: See TracChangeset for help on using the changeset viewer.