Reviewed by Harrison
authorkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Oct 2004 20:07:47 +0000 (20:07 +0000)
committerkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 28 Oct 2004 20:07:47 +0000 (20:07 +0000)
        Reorganization of delete command functionality so that doApply is not
        several hundred lines long. This is not a squeaky-clean cleanup, but
        it is a step in the right direction. No functionality changes.

        * khtml/editing/htmlediting.cpp:
        (khtml::DeleteSelectionCommand::DeleteSelectionCommand):
        (khtml::DeleteSelectionCommand::initializePositionData): New helper.
        (khtml::DeleteSelectionCommand::saveTypingStyleState): Ditto.
        (khtml::DeleteSelectionCommand::performDelete): Ditto.
        (khtml::DeleteSelectionCommand::fixupWhitespace): Ditto.
        (khtml::DeleteSelectionCommand::moveNodesAfterNode): Ditto.
        (khtml::DeleteSelectionCommand::calculateEndingPosition): Ditto.
        (khtml::DeleteSelectionCommand::calculateTypingStyleAfterDelete): Ditto.
        (khtml::DeleteSelectionCommand::clearTransientState): Ditto.
        (khtml::DeleteSelectionCommand::doApply): Factor out code into new helpers.
        * khtml/editing/htmlediting.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@7909 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog-2005-08-23
WebCore/khtml/editing/htmlediting.cpp
WebCore/khtml/editing/htmlediting.h

index c0c3d03..6f867b8 100644 (file)
@@ -1,5 +1,26 @@
 2004-10-28  Ken Kocienda  <kocienda@apple.com>
 
+        Reviewed by Harrison
+
+        Reorganization of delete command functionality so that doApply is not
+        several hundred lines long. This is not a squeaky-clean cleanup, but
+        it is a step in the right direction. No functionality changes.
+
+        * khtml/editing/htmlediting.cpp:
+        (khtml::DeleteSelectionCommand::DeleteSelectionCommand):
+        (khtml::DeleteSelectionCommand::initializePositionData): New helper.
+        (khtml::DeleteSelectionCommand::saveTypingStyleState): Ditto.
+        (khtml::DeleteSelectionCommand::performDelete): Ditto.
+        (khtml::DeleteSelectionCommand::fixupWhitespace): Ditto.
+        (khtml::DeleteSelectionCommand::moveNodesAfterNode): Ditto.
+        (khtml::DeleteSelectionCommand::calculateEndingPosition): Ditto.
+        (khtml::DeleteSelectionCommand::calculateTypingStyleAfterDelete): Ditto.
+        (khtml::DeleteSelectionCommand::clearTransientState): Ditto.
+        (khtml::DeleteSelectionCommand::doApply): Factor out code into new helpers.
+        * khtml/editing/htmlediting.h:
+
+2004-10-28  Ken Kocienda  <kocienda@apple.com>
+
         Reviewed by me
 
         * khtml/editing/htmlediting.cpp:
index 1fb0952..1a674c2 100644 (file)
@@ -1244,68 +1244,50 @@ Position ApplyStyleCommand::positionInsertionPoint(Position pos)
 // DeleteSelectionCommand
 
 DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, bool smartDelete, bool mergeBlocksAfterDelete)
-    : CompositeEditCommand(document), m_hasSelectionToDelete(false), m_smartDelete(smartDelete), m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
+    : CompositeEditCommand(document), 
+      m_hasSelectionToDelete(false), 
+      m_smartDelete(smartDelete), 
+      m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
 {
 }
 
 DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete)
-    : CompositeEditCommand(document), m_selectionToDelete(selection), m_hasSelectionToDelete(true), m_smartDelete(smartDelete), m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
+    : CompositeEditCommand(document), 
+      m_hasSelectionToDelete(true), 
+      m_smartDelete(smartDelete), 
+      m_mergeBlocksAfterDelete(mergeBlocksAfterDelete),
+      m_selectionToDelete(selection)
 {
 }
 
-// This function moves nodes in the block containing startNode to dstBlock, starting
-// from startNode and proceeding to the end of the block. Nodes in the block containing
-// startNode that appear in document order before startNode are not moved.
-// This function is an important helper for deleting selections that cross block
-// boundaries.
-void DeleteSelectionCommand::moveNodesAfterNode(NodeImpl *startNode, NodeImpl *dstNode)
+void DeleteSelectionCommand::initializePositionData()
 {
-    if (!startNode || !dstNode)
-        return;
-
-    NodeImpl *startBlock = startNode->enclosingBlockFlowElement();
-    if (isTableStructureNode(startBlock))
-        // Do not move content between parts of a table
-        return;
-
-    // Now that we are about to add content, check to see if a placeholder element
-    // can be removed.
-    removeBlockPlaceholderIfNeeded(startBlock);
+    Position start = startPositionForDelete();
+    Position end = endPositionForDelete();
 
-    NodeImpl *node = startNode == startBlock ? startBlock->firstChild() : startNode;
+    m_upstreamStart = Position(start.upstream(StayInBlock));
+    m_downstreamStart = Position(start.downstream(StayInBlock));
+    m_upstreamEnd = Position(end.upstream(StayInBlock));
+    m_downstreamEnd = Position(end.downstream(StayInBlock));
 
-    // Do the move.
-    NodeImpl *refNode = dstNode;
-    while (node && node->isAncestor(startBlock)) {
-        NodeImpl *moveNode = node;
-        node = node->nextSibling();
-        removeNode(moveNode);
-        insertNodeAfter(moveNode, refNode);
-        refNode = moveNode;
-    }
+    m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition();
+    m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition();
+    m_trailingWhitespaceValid = true;
+    
+    debugPosition("m_upstreamStart      ", m_upstreamStart);
+    debugPosition("m_downstreamStart    ", m_downstreamStart);
+    debugPosition("m_upstreamEnd        ", m_upstreamEnd);
+    debugPosition("m_downstreamEnd      ", m_downstreamEnd);
+    debugPosition("m_leadingWhitespace  ", m_leadingWhitespace);
+    debugPosition("m_trailingWhitespace ", m_trailingWhitespace);
+    
+    m_startBlock = m_downstreamStart.node()->enclosingBlockFlowElement();
+    m_startBlock->ref();
+    m_endBlock = m_upstreamEnd.node()->enclosingBlockFlowElement();
+    m_endBlock->ref();
 
-    // If the startBlock no longer has any kids, we may need to deal with adding a BR
-    // to make the layout come out right. Consider this document:
-    //
-    // One
-    // <div>Two</div>
-    // Three
-    // 
-    // Placing the insertion before before the 'T' of 'Two' and hitting delete will
-    // move the contents of the div to the block containing 'One' and delete the div.
-    // This will have the side effect of moving 'Three' on to the same line as 'One'
-    // and 'Two'. This is undesirable. We fix this up by adding a BR before the 'Three'.
-    // This may not be ideal, but it is better than nothing.
-    document()->updateLayout();
-    if (startBlock->renderer() && startBlock->renderer()->height() == 0) {
-        removeNode(startBlock);
-        if (refNode->renderer() && refNode->renderer()->inlineBox() && refNode->renderer()->inlineBox()->nextOnLineExists()) {
-            int exceptionCode = 0;
-            ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
-            ASSERT(exceptionCode == 0);
-            insertNodeAfter(breakNode, refNode);
-        }
-    }
+    m_startNode = m_upstreamStart.node();
+    m_startNode->ref();
 }
 
 Position DeleteSelectionCommand::startPositionForDelete() const
@@ -1336,87 +1318,51 @@ Position DeleteSelectionCommand::endPositionForDelete() const
     return pos;
 }
 
-void DeleteSelectionCommand::doApply()
+void DeleteSelectionCommand::saveTypingStyleState()
 {
-    // If selection has not been set to a custom selection when the command was created,
-    // use the current ending selection.
-    if (!m_hasSelectionToDelete)
-        m_selectionToDelete = endingSelection();
-        
-    if (!m_selectionToDelete.isRange())
-        return;
-
-    Position start = startPositionForDelete();
-    Position end = endPositionForDelete();
-    Position upstreamStart(start.upstream(StayInBlock));
-    Position downstreamStart(start.downstream(StayInBlock));
-    Position upstreamEnd(end.upstream(StayInBlock));
-    Position downstreamEnd(end.downstream(StayInBlock));
-    Position endingPosition;
-
-    // Save away whitespace situation before doing any deletions
-    Position leading = upstreamStart.leadingWhitespacePosition();
-    Position trailing = downstreamEnd.trailingWhitespacePosition();
-    bool trailingValid = true;
-
-    // Delete any text that may hinder our ability to fixup whitespace after the detele
-    deleteInsignificantTextDownstream(trailing);    
-    
-    debugPosition("upstreamStart    ", upstreamStart);
-    debugPosition("downstreamStart  ", downstreamStart);
-    debugPosition("upstreamEnd      ", upstreamEnd);
-    debugPosition("downstreamEnd    ", downstreamEnd);
-    debugPosition("leading          ", leading);
-    debugPosition("trailing         ", trailing);
-    
-    NodeImpl *startBlock = downstreamStart.node()->enclosingBlockFlowElement();
-    NodeImpl *endBlock = upstreamEnd.node()->enclosingBlockFlowElement();
-    if (!startBlock || !endBlock)
-        // Can't figure out what blocks we're in. This can happen if
-        // the document structure is not what we are expecting, like if
-        // the document has no body element, or if the editable block
-        // has been changed to display: inline. Some day it might
-        // be nice to be able to deal with this, but for now, bail.
-        return;
-
     // Figure out the typing style in effect before the delete is done.
     // FIXME: Improve typing style.
     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
-    CSSComputedStyleDeclarationImpl *computedStyle = downstreamStart.computedStyle();
+    CSSComputedStyleDeclarationImpl *computedStyle = m_downstreamStart.computedStyle();
     computedStyle->ref();
-    CSSStyleDeclarationImpl *style = computedStyle->copyInheritableProperties();
-    style->ref();
+    m_typingStyle = computedStyle->copyInheritableProperties();
+    m_typingStyle->ref();
     computedStyle->deref();
+}
 
-    NodeImpl *startNode = upstreamStart.node();
-    int startOffset = upstreamStart.offset();
-    if (startOffset >= startNode->caretMaxOffset()) {
-        if (startNode->isTextNode()) {
+void DeleteSelectionCommand::performDelete()
+{
+    int startOffset = m_upstreamStart.offset();
+    if (startOffset >= m_startNode->caretMaxOffset()) {
+        if (m_startNode->isTextNode()) {
             // Delete any insignificant text from this node.
-            TextImpl *text = static_cast<TextImpl *>(startNode);
-            if (text->length() > (unsigned)startNode->caretMaxOffset())
-                deleteText(text, startNode->caretMaxOffset(), text->length() - startNode->caretMaxOffset());
+            TextImpl *text = static_cast<TextImpl *>(m_startNode);
+            if (text->length() > (unsigned)m_startNode->caretMaxOffset())
+                deleteText(text, m_startNode->caretMaxOffset(), text->length() - m_startNode->caretMaxOffset());
         }
-        startNode = startNode->traverseNextNode();
+        // shift the start node to the next
+        NodeImpl *old = m_startNode;
+        m_startNode = old->traverseNextNode();
+        old->deref();
         startOffset = 0;
     }
 
-    if (startNode == downstreamEnd.node()) {
+    if (m_startNode == m_downstreamEnd.node()) {
         // handle delete in one node
-        if (!startNode->renderer() || 
-            (startOffset <= startNode->caretMinOffset() && downstreamEnd.offset() >= startNode->caretMaxOffset())) {
+        if (!m_startNode->renderer() || 
+            (startOffset <= m_startNode->caretMinOffset() && m_downstreamEnd.offset() >= m_startNode->caretMaxOffset())) {
             // just delete
-            removeFullySelectedNode(startNode);
+            removeFullySelectedNode(m_startNode);
         }
-        else if (downstreamEnd.offset() - startOffset > 0) {
+        else if (m_downstreamEnd.offset() - startOffset > 0) {
             // in a text node that needs to be trimmed
-            TextImpl *text = static_cast<TextImpl *>(startNode);
-            deleteText(text, startOffset, downstreamEnd.offset() - startOffset);
-            trailingValid = false;
+            TextImpl *text = static_cast<TextImpl *>(m_startNode);
+            deleteText(text, startOffset, m_downstreamEnd.offset() - startOffset);
+            m_trailingWhitespaceValid = false;
         }
     }
     else {
-        NodeImpl *node = startNode;
+        NodeImpl *node = m_startNode;
         
         if (startOffset > 0) {
             // in a text node that needs to be trimmed
@@ -1426,8 +1372,8 @@ void DeleteSelectionCommand::doApply()
         }
         
         // handle deleting all nodes that are completely selected
-        while (node && node != downstreamEnd.node()) {
-            if (!downstreamEnd.node()->isAncestor(node)) {
+        while (node && node != m_downstreamEnd.node()) {
+            if (!m_downstreamEnd.node()->isAncestor(node)) {
                 NodeImpl *nextNode = node->traverseNextSibling();
                 removeFullySelectedNode(node);
                 node = nextNode;
@@ -1436,7 +1382,7 @@ void DeleteSelectionCommand::doApply()
                 NodeImpl *n = node->lastChild();
                 while (n && n->lastChild())
                     n = n->lastChild();
-                if (n == downstreamEnd.node() && downstreamEnd.offset() >= downstreamEnd.node()->caretMaxOffset()) {
+                if (n == m_downstreamEnd.node() && m_downstreamEnd.offset() >= m_downstreamEnd.node()->caretMaxOffset()) {
                     NodeImpl *nextNode = node->traverseNextSibling();
                     removeFullySelectedNode(node);
                     node = nextNode;
@@ -1447,89 +1393,138 @@ void DeleteSelectionCommand::doApply()
             }
         }
 
-        if (downstreamEnd.node() != startNode && downstreamEnd.node()->inDocument() && downstreamEnd.offset() >= downstreamEnd.node()->caretMinOffset()) {
-            if (downstreamEnd.offset() >= downstreamEnd.node()->caretMaxOffset()) {
+        if (m_downstreamEnd.node() != m_startNode && m_downstreamEnd.node()->inDocument() && m_downstreamEnd.offset() >= m_downstreamEnd.node()->caretMinOffset()) {
+            if (m_downstreamEnd.offset() >= m_downstreamEnd.node()->caretMaxOffset()) {
                 // need to delete whole node
                 // we can get here if this is the last node in the block
-                removeFullySelectedNode(downstreamEnd.node());
-                trailingValid = false;
+                removeFullySelectedNode(m_downstreamEnd.node());
+                m_trailingWhitespaceValid = false;
             }
             else {
                 // in a text node that needs to be trimmed
-                TextImpl *text = static_cast<TextImpl *>(downstreamEnd.node());
-                if (downstreamEnd.offset() > 0) {
-                    deleteText(text, 0, downstreamEnd.offset());
-                    trailingValid = false;
+                TextImpl *text = static_cast<TextImpl *>(m_downstreamEnd.node());
+                if (m_downstreamEnd.offset() > 0) {
+                    deleteText(text, 0, m_downstreamEnd.offset());
+                    m_trailingWhitespaceValid = false;
                 }
             }
-            if (!downstreamEnd.node()->inDocument() && downstreamEnd.node()->inDocument())
-                endingPosition = Position(downstreamEnd.node(), 0);
+            if (!m_downstreamEnd.node()->inDocument() && m_downstreamEnd.node()->inDocument())
+                m_endingPosition = Position(m_downstreamEnd.node(), 0);
         }
     }
-    
-    // Do block merge if start and end of selection are in different blocks.
-    if (m_mergeBlocksAfterDelete && endBlock != startBlock && downstreamEnd.node()->inDocument()) {
-        LOG(Editing,  "merging content from end block");
-        moveNodesAfterNode(downstreamEnd.node(), upstreamStart.node());
-    }
-      
-    // Figure out where the end position should be
-    if (endingPosition.isNotNull())
-        goto FixupWhitespace;
-
-    endingPosition = upstreamStart;
-    if (endingPosition.node()->inDocument())
-        goto FixupWhitespace;
-    
-    endingPosition = downstreamEnd;
-    if (endingPosition.node()->inDocument())
-        goto FixupWhitespace;
-
-    endingPosition = Position(startBlock, 0);
-    if (endingPosition.node()->inDocument())
-        goto FixupWhitespace;
-
-    endingPosition = Position(endBlock, 0);
-    if (endingPosition.node()->inDocument())
-        goto FixupWhitespace;
-
-    endingPosition = Position(document()->documentElement(), 0);
-
-    // Perform whitespace fixup
-    FixupWhitespace:
+}
 
+void DeleteSelectionCommand::fixupWhitespace()
+{
     document()->updateLayout();
-    if (leading.isNotNull() && (trailing.isNotNull() || !leading.isRenderedCharacter())) {
+    if (m_leadingWhitespace.isNotNull() && (m_trailingWhitespace.isNotNull() || !m_leadingWhitespace.isRenderedCharacter())) {
         LOG(Editing, "replace leading");
-        TextImpl *textNode = static_cast<TextImpl *>(leading.node());
-        replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
+        TextImpl *textNode = static_cast<TextImpl *>(m_leadingWhitespace.node());
+        replaceText(textNode, m_leadingWhitespace.offset(), 1, nonBreakingSpaceString());
     }
-    else if (trailing.isNotNull()) {
-        if (trailingValid) {
-            if (!trailing.isRenderedCharacter()) {
+    else if (m_trailingWhitespace.isNotNull()) {
+        if (m_trailingWhitespaceValid) {
+            if (!m_trailingWhitespace.isRenderedCharacter()) {
                 LOG(Editing, "replace trailing [valid]");
-                TextImpl *textNode = static_cast<TextImpl *>(trailing.node());
-                replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
+                TextImpl *textNode = static_cast<TextImpl *>(m_trailingWhitespace.node());
+                replaceText(textNode, m_trailingWhitespace.offset(), 1, nonBreakingSpaceString());
             }
         }
         else {
-            Position pos = endingPosition.downstream(StayInBlock);
+            Position pos = m_endingPosition.downstream(StayInBlock);
             pos = Position(pos.node(), pos.offset() - 1);
             if (isWS(pos) && !pos.isRenderedCharacter()) {
                 LOG(Editing, "replace trailing [invalid]");
                 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
                 replaceText(textNode, pos.offset(), 1, nonBreakingSpaceString());
-                endingPosition = pos;
+                // need to adjust ending position since the trailing position is not valid.
+                m_endingPosition = pos;
             }
         }
     }
+}
 
-    debugPosition("endingPosition   ", endingPosition);
+// This function moves nodes in the block containing startNode to dstBlock, starting
+// from startNode and proceeding to the end of the block. Nodes in the block containing
+// startNode that appear in document order before startNode are not moved.
+// This function is an important helper for deleting selections that cross block
+// boundaries.
+void DeleteSelectionCommand::moveNodesAfterNode(NodeImpl *startNode, NodeImpl *dstNode)
+{
+    if (!startNode || !dstNode)
+        return;
 
-    // If the delete emptied a block, add in a placeholder so the block does not
-    // seem to disappear.
-    insertBlockPlaceholderIfNeeded(endingPosition.node());
+    NodeImpl *startBlock = startNode->enclosingBlockFlowElement();
+    if (isTableStructureNode(startBlock))
+        // Do not move content between parts of a table
+        return;
+
+    // Now that we are about to add content, check to see if a placeholder element
+    // can be removed.
+    removeBlockPlaceholderIfNeeded(startBlock);
+
+    NodeImpl *node = startNode == startBlock ? startBlock->firstChild() : startNode;
 
+    // Do the move.
+    NodeImpl *refNode = dstNode;
+    while (node && node->isAncestor(startBlock)) {
+        NodeImpl *moveNode = node;
+        node = node->nextSibling();
+        removeNode(moveNode);
+        insertNodeAfter(moveNode, refNode);
+        refNode = moveNode;
+    }
+
+    // If the startBlock no longer has any kids, we may need to deal with adding a BR
+    // to make the layout come out right. Consider this document:
+    //
+    // One
+    // <div>Two</div>
+    // Three
+    // 
+    // Placing the insertion before before the 'T' of 'Two' and hitting delete will
+    // move the contents of the div to the block containing 'One' and delete the div.
+    // This will have the side effect of moving 'Three' on to the same line as 'One'
+    // and 'Two'. This is undesirable. We fix this up by adding a BR before the 'Three'.
+    // This may not be ideal, but it is better than nothing.
+    document()->updateLayout();
+    if (startBlock->renderer() && startBlock->renderer()->height() == 0) {
+        removeNode(startBlock);
+        if (refNode->renderer() && refNode->renderer()->inlineBox() && refNode->renderer()->inlineBox()->nextOnLineExists()) {
+            int exceptionCode = 0;
+            ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
+            ASSERT(exceptionCode == 0);
+            insertNodeAfter(breakNode, refNode);
+        }
+    }
+}
+
+void DeleteSelectionCommand::calculateEndingPosition()
+{
+    if (m_endingPosition.isNotNull() && m_endingPosition.node()->inDocument())
+        return;
+
+    m_endingPosition = m_upstreamStart;
+    if (m_endingPosition.node()->inDocument())
+        return;
+    
+    m_endingPosition = m_downstreamEnd;
+    if (m_endingPosition.node()->inDocument())
+        return;
+
+    m_endingPosition = Position(m_startBlock, 0);
+    if (m_endingPosition.node()->inDocument())
+        return;
+
+    m_endingPosition = Position(m_endBlock, 0);
+    if (m_endingPosition.node()->inDocument())
+        return;
+
+    m_endingPosition = Position(document()->documentElement(), 0);
+}
+
+void DeleteSelectionCommand::calculateTypingStyleAfterDelete()
+{
     // Compute the difference between the style before the delete and the style now
     // after the delete has been done. Set this style on the part, so other editing
     // commands being composed with this one will work, and also cache it on the command,
@@ -1537,21 +1532,93 @@ void DeleteSelectionCommand::doApply()
     // has completed.
     // FIXME: Improve typing style.
     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
-    if (startNode == endingPosition.node())
+    if (m_startNode == m_endingPosition.node())
         document()->part()->setTypingStyle(0);
     else {
-        CSSComputedStyleDeclarationImpl endingStyle(endingPosition.node());
-        endingStyle.diff(style);
-        if (!style->length()) {
-            style->deref();
-            style = 0;
+        CSSComputedStyleDeclarationImpl endingStyle(m_endingPosition.node());
+        endingStyle.diff(m_typingStyle);
+        if (!m_typingStyle->length()) {
+            m_typingStyle->deref();
+            m_typingStyle = 0;
         }
-        document()->part()->setTypingStyle(style);
-        setTypingStyle(style);
+        document()->part()->setTypingStyle(m_typingStyle);
+        setTypingStyle(m_typingStyle);
+    }
+}
+
+void DeleteSelectionCommand::clearTransientState()
+{
+    m_selectionToDelete.clear();
+    m_upstreamStart.clear();
+    m_downstreamStart.clear();
+    m_upstreamEnd.clear();
+    m_downstreamEnd.clear();
+    m_endingPosition.clear();
+    m_leadingWhitespace.clear();
+    m_trailingWhitespace.clear();
+    
+    if (m_startBlock) {
+        m_startBlock->deref();
+        m_startBlock = 0;
+    }
+    if (m_endBlock) {
+        m_endBlock->deref();
+        m_endBlock = 0;
+    }
+    if (m_typingStyle) {
+        m_typingStyle->deref();
+        m_endBlock = 0;
+    }
+    if (m_startNode) {
+        m_startNode->deref();
+        m_startNode = 0;
+    }
+}
+
+void DeleteSelectionCommand::doApply()
+{
+    // If selection has not been set to a custom selection when the command was created,
+    // use the current ending selection.
+    if (!m_hasSelectionToDelete)
+        m_selectionToDelete = endingSelection();
+        
+    if (!m_selectionToDelete.isRange())
+        return;
+
+    initializePositionData();
+
+    if (!m_startBlock || !m_endBlock) {
+        // Can't figure out what blocks we're in. This can happen if
+        // the document structure is not what we are expecting, like if
+        // the document has no body element, or if the editable block
+        // has been changed to display: inline. Some day it might
+        // be nice to be able to deal with this, but for now, bail.
+        clearTransientState();
+        return;
+    }
+
+    // Delete any text that may hinder our ability to fixup whitespace after the detele
+    deleteInsignificantTextDownstream(m_trailingWhitespace);    
+
+    saveTypingStyleState();
+    performDelete();
+    
+    // Do block merge if start and end of selection are in different blocks.
+    if (m_mergeBlocksAfterDelete && m_endBlock != m_startBlock && m_downstreamEnd.node()->inDocument()) {
+        LOG(Editing,  "merging content from end block");
+        moveNodesAfterNode(m_downstreamEnd.node(), m_upstreamStart.node());
     }
-    if (style)
-        style->deref();
-    setEndingSelection(endingPosition);
+    
+    calculateEndingPosition();
+    fixupWhitespace();
+
+    // If the delete emptied a block, add in a placeholder so the block does not
+    // seem to disappear.
+    insertBlockPlaceholderIfNeeded(m_endingPosition.node());
+    calculateTypingStyleAfterDelete();
+    setEndingSelection(m_endingPosition);
+    debugPosition("endingPosition   ", m_endingPosition);
+    clearTransientState();
 }
 
 bool DeleteSelectionCommand::preservesTypingStyle() const
index 1a2d8ba..895f570 100644 (file)
@@ -275,14 +275,35 @@ public:
 private:
     virtual bool preservesTypingStyle() const;
 
-    void moveNodesAfterNode(DOM::NodeImpl *startNode, DOM::NodeImpl *dstNode);
+    void initializePositionData();
     DOM::Position startPositionForDelete() const;
     DOM::Position endPositionForDelete() const;
+    void saveTypingStyleState();
+    void performDelete();
+    void fixupWhitespace();
+    void moveNodesAfterNode(DOM::NodeImpl *startNode, DOM::NodeImpl *dstNode);
+    void calculateEndingPosition();
+    void calculateTypingStyleAfterDelete();
+    void clearTransientState();
 
-    khtml::Selection m_selectionToDelete;
     bool m_hasSelectionToDelete;
     bool m_smartDelete;
     bool m_mergeBlocksAfterDelete;
+    bool m_trailingWhitespaceValid;
+
+    // This data is transient and should be cleared at the end of the doApply function.
+    khtml::Selection m_selectionToDelete;
+    DOM::Position m_upstreamStart;
+    DOM::Position m_downstreamStart;
+    DOM::Position m_upstreamEnd;
+    DOM::Position m_downstreamEnd;
+    DOM::Position m_endingPosition;
+    DOM::Position m_leadingWhitespace;
+    DOM::Position m_trailingWhitespace;
+    DOM::NodeImpl *m_startBlock;
+    DOM::NodeImpl *m_endBlock;
+    DOM::NodeImpl *m_startNode;
+    DOM::CSSStyleDeclarationImpl *m_typingStyle;
 };
 
 //------------------------------------------------------------------------------------------