Reviewed by Darin.
authormjs <mjs@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 19 Mar 2005 22:36:50 +0000 (22:36 +0000)
committermjs <mjs@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 19 Mar 2005 22:36:50 +0000 (22:36 +0000)
<rdar://problem/4053506> Pasting Tables and Cells in Mail does not allow editing before or after
<rdar://problem/4005954> REGRESSION (Mail): After copy/paste of content containing list element cannot go back to entering text at left side of page

        * khtml/editing/htmlediting.cpp:
        (khtml::maxDeepOffset):
        (khtml::CompositeEditCommand::removeFullySelectedNodePreservingPosition):
        (khtml::CompositeEditCommand::removeChildrenInRangePreservingPosition):
        (khtml::CompositeEditCommand::removeNodePreservingPosition):
        (khtml::CompositeEditCommand::insertBlockPlaceholder):
        (khtml::CompositeEditCommand::appendBlockPlaceholder):
        (khtml::CompositeEditCommand::forceBlockPlaceholder):
        (khtml::CompositeEditCommand::addBlockPlaceholderIfNeeded):
        (khtml::isSpecialElement):
        (khtml::isFirstVisiblePositionInSpecialElementInFragment):
        (khtml::positionBeforePossibleContainingSpecialElement):
        (khtml::positionAfterPossibleContainingSpecialElement):
        (khtml::ApplyStyleCommand::applyInlineStyle):
        (khtml::DeleteSelectionCommand::initializePositionData):
        (khtml::DeleteSelectionCommand::insertPlaceholderForAncestorBlockContent):
        (khtml::DeleteSelectionCommand::handleGeneralDelete):
        (khtml::DeleteSelectionCommand::calculateTypingStyleAfterDelete):
        (khtml::DeleteSelectionCommand::doApply):
        (khtml::InsertParagraphSeparatorCommand::doApply):
        (khtml::ReplacementFragment::ReplacementFragment):
        (khtml::ReplaceSelectionCommand::doApply):
        * khtml/editing/htmlediting.h:
        * khtml/editing/visible_position.cpp:
        (khtml::isRenderedBR):
        (khtml::VisiblePosition::initDownstream):
        (khtml::isLastVisiblePositionInBlock):
        * khtml/rendering/render_line.cpp:
        (khtml::RootInlineBox::closestLeafChildForXPos):
        * khtml/xml/dom_nodeimpl.cpp:
        (NodeImpl::isBlockFlowOrTable):
        (NodeImpl::isEditableBlock):
        (NodeImpl::enclosingBlockFlowOrTableElement):
        * khtml/xml/dom_nodeimpl.h:
        * khtml/xml/dom_position.cpp:
        (DOM::Position::upstream):
        (DOM::Position::downstream):
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-003-expected.txt:
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-004-expected.txt:
        * layout-tests/editing/deleting/delete-select-all-001-expected.txt:
        * layout-tests/editing/deleting/delete-select-all-003-expected.txt:
        * layout-tests/editing/inserting/insert-3786362-fix-expected.txt:

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

13 files changed:
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-003-expected.txt
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-004-expected.txt
LayoutTests/editing/deleting/delete-select-all-001-expected.txt
LayoutTests/editing/deleting/delete-select-all-003-expected.txt
LayoutTests/editing/inserting/insert-3786362-fix-expected.txt
WebCore/ChangeLog-2005-08-23
WebCore/khtml/editing/htmlediting.cpp
WebCore/khtml/editing/htmlediting.h
WebCore/khtml/editing/visible_position.cpp
WebCore/khtml/rendering/render_line.cpp
WebCore/khtml/xml/dom_nodeimpl.cpp
WebCore/khtml/xml/dom_nodeimpl.h
WebCore/khtml/xml/dom_position.cpp

index 302ff81b8c66cab518415be0e11d1c1345bc30fb..461d4f74778d1ea6d78b37b553f138ad2550280a 100644 (file)
@@ -19,15 +19,15 @@ layer at (0,0) size 800x600
           RenderText {TEXT} at (0,28) size 682x56
             text run at (0,28) width 682: "Should see the two lines in the red box. Each should contain \"A\" only."
             text run at (0,56) width 620: "Insertion point should be blinking at the start of the second line."
-      RenderBlock {DIV} at (0,264) size 784x60
-        RenderBlock {DIV} at (0,0) size 784x60 [border: (2px solid #FF0000)]
+      RenderBlock {DIV} at (0,264) size 784x50
+        RenderBlock {DIV} at (0,0) size 784x32 [border: (2px solid #FF0000)]
           RenderBlock {P} at (2,2) size 780x28
             RenderText {TEXT} at (0,0) size 17x28
               text run at (0,0) width 17: "A"
-          RenderBlock {P} at (2,30) size 780x28
-            RenderText {TEXT} at (0,0) size 17x28
-              text run at (0,0) width 17: "A"
+        RenderBlock (anonymous) at (0,32) size 784x18
+          RenderText {TEXT} at (0,0) size 12x18
+            text run at (0,0) width 12: "A"
 selection is CARET:
-start:      position 0 of child 1 {TEXT} of child 4 {P} of child 1 {DIV} of root {DIV}
-upstream:   position 0 of child 4 {P} of child 1 {DIV} of root {DIV}
-downstream: position 0 of child 1 {TEXT} of child 4 {P} of child 1 {DIV} of root {DIV}
+start:      position 0 of child 2 {TEXT} of root {DIV}
+upstream:   position 0 of child 2 {TEXT} of root {DIV}
+downstream: position 0 of child 2 {TEXT} of root {DIV}
index d9792a6958c0c89a05ba4c4f95a2fc574a4b879c..4c53c6e83e59c6fdc410be6ad51b70710459cf8d 100644 (file)
@@ -19,16 +19,16 @@ layer at (0,0) size 800x600
           RenderText {TEXT} at (0,28) size 682x56
             text run at (0,28) width 682: "Should see the two lines in the red box. Each should contain \"A\" only."
             text run at (0,56) width 620: "Insertion point should be blinking at the start of the second line."
-      RenderBlock {DIV} at (0,264) size 784x60
-        RenderBlock {DIV} at (0,0) size 784x60 [border: (2px solid #FF0000)]
+      RenderBlock {DIV} at (0,264) size 784x50
+        RenderBlock {DIV} at (0,0) size 784x32 [border: (2px solid #FF0000)]
           RenderBlock {P} at (2,2) size 780x28
             RenderText {TEXT} at (0,0) size 17x28
               text run at (0,0) width 17: "A"
           RenderBlock (anonymous) at (2,30) size 780x0
-          RenderBlock {P} at (2,30) size 780x28
-            RenderText {TEXT} at (0,0) size 17x28
-              text run at (0,0) width 17: "A"
+        RenderBlock (anonymous) at (0,32) size 784x18
+          RenderText {TEXT} at (0,0) size 12x18
+            text run at (0,0) width 12: "A"
 selection is CARET:
-start:      position 0 of child 1 {TEXT} of child 3 {P} of child 1 {DIV} of root {DIV}
-upstream:   position 0 of child 3 {P} of child 1 {DIV} of root {DIV}
-downstream: position 0 of child 1 {TEXT} of child 3 {P} of child 1 {DIV} of root {DIV}
+start:      position 0 of child 2 {TEXT} of root {DIV}
+upstream:   position 0 of child 2 {TEXT} of root {DIV}
+downstream: position 0 of child 2 {TEXT} of root {DIV}
index 664804cf5190ef1cc2f7c98cdb4a43fadec83cb0..094524ffa84c58c0761506c4fbb11e884441d255 100644 (file)
@@ -3,22 +3,8 @@ layer at (0,0) size 800x600
 layer at (0,0) size 800x600
   RenderBlock {HTML} at (0,0) size 800x600
     RenderBody {BODY} at (8,8) size 784x584
-      RenderBlock {DIV} at (0,0) size 784x50 [border: (2px solid #FF0000)]
-        RenderTable {TABLE} at (14,14) size 22x22 [border: (1px outset #808080)]
-          RenderTableSection {TBODY} at (1,1) size 0x20
-            RenderTableRow {TR} at (0,0) size 0x0
-              RenderTableCell {TD} at (2,2) size 4x4 [border: (1px inset #808080)] [r=0 c=0 rs=1 cs=1]
-              RenderTableCell {TD} at (8,2) size 4x4 [border: (1px inset #808080)] [r=0 c=1 rs=1 cs=1]
-              RenderTableCell {TD} at (14,2) size 4x4 [border: (1px inset #808080)] [r=0 c=2 rs=1 cs=1]
-            RenderTableRow {TR} at (0,0) size 0x0
-              RenderTableCell {TD} at (2,8) size 4x4 [border: (1px inset #808080)] [r=1 c=0 rs=1 cs=1]
-              RenderTableCell {TD} at (8,8) size 4x4 [border: (1px inset #808080)] [r=1 c=1 rs=1 cs=1]
-              RenderTableCell {TD} at (14,8) size 4x4 [border: (1px inset #808080)] [r=1 c=2 rs=1 cs=1]
-            RenderTableRow {TR} at (0,0) size 0x0
-              RenderTableCell {TD} at (2,14) size 4x4 [border: (1px inset #808080)] [r=2 c=0 rs=1 cs=1]
-              RenderTableCell {TD} at (8,14) size 4x4 [border: (1px inset #808080)] [r=2 c=1 rs=1 cs=1]
-              RenderTableCell {TD} at (14,14) size 4x4 [border: (1px inset #808080)] [r=2 c=2 rs=1 cs=1]
+      RenderBlock {DIV} at (0,0) size 784x28 [border: (2px solid #FF0000)]
 selection is CARET:
-start:      position 0 of child 1 {TD} of child 1 {TR} of child 1 {TBODY} of child 2 {TABLE} of root {DIV}
-upstream:   position 0 of child 1 {TD} of child 1 {TR} of child 1 {TBODY} of child 2 {TABLE} of root {DIV}
-downstream: position 1 of child 1 {TD} of child 1 {TR} of child 1 {TBODY} of child 2 {TABLE} of root {DIV}
+start:      position 0 of  of root {DIV}
+upstream:   position 0 of  of root {DIV}
+downstream: position 1 of child 2 {TEXT} of root {DIV}
index 52153da0599f95dedd2f42575d726008c662dfbd..094524ffa84c58c0761506c4fbb11e884441d255 100644 (file)
@@ -3,11 +3,8 @@ layer at (0,0) size 800x600
 layer at (0,0) size 800x600
   RenderBlock {HTML} at (0,0) size 800x600
     RenderBody {BODY} at (8,8) size 784x584
-      RenderBlock {DIV} at (0,0) size 784x104 [border: (2px solid #FF0000)]
-        RenderBlock {UL} at (14,38) size 756x28
-          RenderListItem {LI} at (40,0) size 716x28
-            RenderListMarker at (0,0) size 0x22
+      RenderBlock {DIV} at (0,0) size 784x28 [border: (2px solid #FF0000)]
 selection is CARET:
-start:      position 0 of child 1 {LI} of child 2 {UL} of root {DIV}
-upstream:   position 0 of child 1 {LI} of child 2 {UL} of root {DIV}
-downstream: position 1 of child 1 {LI} of child 2 {UL} of root {DIV}
+start:      position 0 of  of root {DIV}
+upstream:   position 0 of  of root {DIV}
+downstream: position 1 of child 2 {TEXT} of root {DIV}
index f05b7de272ff5506db484c3dac4af945be4dd9c3..c51c7be6c29ba7b21e929dbbc300692d4747b657 100644 (file)
@@ -17,6 +17,6 @@ layer at (0,0) size 800x600
         RenderBR {BR} at (2,30) size 0x28
         RenderBR {BR} at (2,58) size 0x28
 selection is CARET:
-start:      position 0 of child 4 {BR} of child 3 {DIV} of root {BODY}
-upstream:   position 1 of child 3 {BR} of child 3 {DIV} of root {BODY}
-downstream: position 0 of child 4 {BR} of child 3 {DIV} of root {BODY}
+start:      position 1 of child 4 {BR} of child 3 {DIV} of root {BODY}
+upstream:   position 1 of child 4 {BR} of child 3 {DIV} of root {BODY}
+downstream: position 1 of child 4 {BR} of child 3 {DIV} of root {BODY}
index 031e983fa5a9d5fe5ffea3c38978e18b53af2bfc..37dbaa7a8f6ba928be0742c491196a5d418191ba 100644 (file)
@@ -1,3 +1,54 @@
+2005-03-18  Maciej Stachowiak  <mjs@apple.com>
+
+        Reviewed by Darin.
+
+       <rdar://problem/4053506> Pasting Tables and Cells in Mail does not allow editing before or after
+       <rdar://problem/4005954> REGRESSION (Mail): After copy/paste of content containing list element cannot go back to entering text at left side of page
+
+       
+        * khtml/editing/htmlediting.cpp:
+        (khtml::maxDeepOffset):
+        (khtml::CompositeEditCommand::removeFullySelectedNodePreservingPosition):
+        (khtml::CompositeEditCommand::removeChildrenInRangePreservingPosition):
+        (khtml::CompositeEditCommand::removeNodePreservingPosition):
+        (khtml::CompositeEditCommand::insertBlockPlaceholder):
+        (khtml::CompositeEditCommand::appendBlockPlaceholder):
+        (khtml::CompositeEditCommand::forceBlockPlaceholder):
+        (khtml::CompositeEditCommand::addBlockPlaceholderIfNeeded):
+        (khtml::isSpecialElement):
+        (khtml::isFirstVisiblePositionInSpecialElementInFragment):
+        (khtml::positionBeforePossibleContainingSpecialElement):
+        (khtml::positionAfterPossibleContainingSpecialElement):
+        (khtml::ApplyStyleCommand::applyInlineStyle):
+        (khtml::DeleteSelectionCommand::initializePositionData):
+        (khtml::DeleteSelectionCommand::insertPlaceholderForAncestorBlockContent):
+        (khtml::DeleteSelectionCommand::handleGeneralDelete):
+        (khtml::DeleteSelectionCommand::calculateTypingStyleAfterDelete):
+        (khtml::DeleteSelectionCommand::doApply):
+        (khtml::InsertParagraphSeparatorCommand::doApply):
+        (khtml::ReplacementFragment::ReplacementFragment):
+        (khtml::ReplaceSelectionCommand::doApply):
+        * khtml/editing/htmlediting.h:
+        * khtml/editing/visible_position.cpp:
+        (khtml::isRenderedBR):
+        (khtml::VisiblePosition::initDownstream):
+        (khtml::isLastVisiblePositionInBlock):
+        * khtml/rendering/render_line.cpp:
+        (khtml::RootInlineBox::closestLeafChildForXPos):
+        * khtml/xml/dom_nodeimpl.cpp:
+        (NodeImpl::isBlockFlowOrTable):
+        (NodeImpl::isEditableBlock):
+        (NodeImpl::enclosingBlockFlowOrTableElement):
+        * khtml/xml/dom_nodeimpl.h:
+        * khtml/xml/dom_position.cpp:
+        (DOM::Position::upstream):
+        (DOM::Position::downstream):
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-003-expected.txt:
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-004-expected.txt:
+        * layout-tests/editing/deleting/delete-select-all-001-expected.txt:
+        * layout-tests/editing/deleting/delete-select-all-003-expected.txt:
+        * layout-tests/editing/inserting/insert-3786362-fix-expected.txt:
+
 2005-03-19  John Sullivan  <sullivan@apple.com>
 
         Reviewed by Darin.
index 3d3be2bfb2ec6ce08b2882fabf9e9515c9ee2532..c9ecb0c711c7041547d81d4191f9c74dc2a62aa1 100644 (file)
@@ -212,6 +212,17 @@ static int maxRangeOffset(NodeImpl *n)
     return 1;
 }
 
+static int maxDeepOffset(NodeImpl *n)
+{
+    if (n->isAtomicNode())
+        return n->caretMaxOffset();
+
+    if (n->isElementNode())
+        return n->childNodeCount();
+
+    return 1;
+}
+
 static void debugPosition(const char *prefix, const Position &pos)
 {
     if (!prefix)
@@ -832,23 +843,47 @@ void CompositeEditCommand::appendNode(NodeImpl *appendChild, NodeImpl *parent)
     applyCommandToComposite(cmd);
 }
 
-void CompositeEditCommand::removeFullySelectedNode(NodeImpl *node)
+void CompositeEditCommand::removeFullySelectedNodePreservingPosition(NodeImpl *node, Position &pos)
 {
-    if (isTableStructureNode(node)) {
+    if (isTableStructureNode(node) || node == node->rootEditableElement()) {
         // Do not remove an element of table structure; remove its contents.
+        // Likewise for the root editable element.
         NodeImpl *child = node->firstChild();
         while (child) {
             NodeImpl *remove = child;
             child = child->nextSibling();
-            removeFullySelectedNode(remove);
+            removeFullySelectedNodePreservingPosition(remove, pos);
         }
     }
     else {
-        EditCommandPtr cmd(new RemoveNodeCommand(document(), node));
-        applyCommandToComposite(cmd);
+        removeNodePreservingPosition(node, pos);
     }
 }
 
+void CompositeEditCommand::removeChildrenInRangePreservingPosition(NodeImpl *node, int from, int to, Position &pos)
+{
+    NodeImpl *nodeToRemove = node->childNode(from);
+    for (int i = from; i < to; i++) {
+        ASSERT(nodeToRemove);
+        NodeImpl *next = nodeToRemove->nextSibling();
+        removeNodePreservingPosition(nodeToRemove, pos);
+        nodeToRemove = next;
+    }
+}
+
+void CompositeEditCommand::removeNodePreservingPosition(NodeImpl *removeChild, Position &pos)
+{
+    if (removeChild == pos.node() || pos.node()->isAncestor(removeChild)) {
+        pos = Position(removeChild->parentNode(), removeChild->nodeIndex());
+    } else if (removeChild->parentNode() == pos.node() && removeChild->nodeIndex() < (unsigned)pos.offset()) {
+        pos = Position(pos.node(), pos.offset() - 1);
+    }
+
+    EditCommandPtr cmd(new RemoveNodeCommand(document(), removeChild));
+    applyCommandToComposite(cmd);
+}
+
+
 void CompositeEditCommand::removeNode(NodeImpl *removeChild)
 {
     EditCommandPtr cmd(new RemoveNodeCommand(document(), removeChild));
@@ -1073,27 +1108,31 @@ void CompositeEditCommand::deleteInsignificantTextDownstream(const DOM::Position
     deleteInsignificantText(pos, end);
 }
 
-void CompositeEditCommand::insertBlockPlaceholder(NodeImpl *node)
+NodeImpl *CompositeEditCommand::appendBlockPlaceholder(NodeImpl *node)
 {
     if (!node)
-        return;
+        return NULL;
 
     ASSERT(node->renderer() && node->renderer()->isBlockFlow());
 
-    insertNodeAt(createBlockPlaceholderElement(document()), node, 0);
+    NodeImpl *placeholder = createBlockPlaceholderElement(document());
+    appendNode(placeholder, node);
+    return placeholder;
 }
 
-void CompositeEditCommand::appendBlockPlaceholder(NodeImpl *node)
+NodeImpl *CompositeEditCommand::insertBlockPlaceholder(const Position &pos)
 {
-    if (!node)
-        return;
+    if (pos.isNull())
+        return NULL;
 
-    ASSERT(node->renderer() && node->renderer()->isBlockFlow());
+    ASSERT(pos.node()->renderer() && pos.node()->renderer()->isBlockFlow());
 
-    appendNode(createBlockPlaceholderElement(document()), node);
+    NodeImpl *placeholder = createBlockPlaceholderElement(document());
+    insertNodeAt(placeholder, pos.node(), pos.offset());
+    return placeholder;
 }
 
-bool CompositeEditCommand::addBlockPlaceholderIfNeeded(NodeImpl *node, bool forceInsertIfNonEmpty)
+NodeImpl *CompositeEditCommand::addBlockPlaceholderIfNeeded(NodeImpl *node)
 {
     if (!node)
         return false;
@@ -1107,18 +1146,10 @@ bool CompositeEditCommand::addBlockPlaceholderIfNeeded(NodeImpl *node, bool forc
     // append the placeholder to make sure it follows
     // any unrendered blocks
     if (renderer->height() == 0) {
-        appendBlockPlaceholder(node);
-        return true;
-    }
-
-    // when forcing a blank paragraph in a non-empty block, tho,
-    // we want to insert the placeholder at the front of that block
-    if (forceInsertIfNonEmpty) {
-        insertBlockPlaceholder(node);
-        return true;
+        return appendBlockPlaceholder(node);
     }
 
-    return false;
+    return NULL;
 }
 
 bool CompositeEditCommand::removeBlockPlaceholder(NodeImpl *node)
@@ -1242,6 +1273,37 @@ static bool isSpecialElement(NodeImpl *n)
     if (n->id() == ID_A && n->hasAnchor())
         return true;
 
+    if (n->id() == ID_UL || n->id() == ID_OL || n->id() == ID_DL)
+        return true;
+
+    RenderObject *renderer = n->renderer();
+
+    if (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE))
+        return true;
+
+    if (renderer && renderer->style()->isFloating())
+        return true;
+
+    if (renderer && renderer->style()->position() != STATIC)
+        return true;
+
+    return false;
+}
+
+// This version of the function is menat to be called on positons in a document fragment,
+// so it does not check for a root editable element, it is assumed these nodes will be put
+// somewhere editable in the future
+static bool isFirstVisiblePositionInSpecialElementInFragment(const Position& pos)
+{
+    VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
+
+    for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
+        if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
+            return false;
+        if (isSpecialElement(n))
+            return true;
+    }
+
     return false;
 }
 
@@ -1348,6 +1410,24 @@ static Position positionOutsideContainingSpecialElement(const Position &pos)
     return pos;
 }
 
+static Position positionBeforePossibleContainingSpecialElement(const Position &pos)
+{
+    if (isFirstVisiblePositionInSpecialElement(pos)) {
+        return positionBeforeContainingSpecialElement(pos);
+    } 
+
+    return pos;
+}
+
+static Position positionAfterPossibleContainingSpecialElement(const Position &pos)
+{
+    if (isLastVisiblePositionInSpecialElement(pos)) {
+        return positionAfterContainingSpecialElement(pos);
+    }
+
+    return pos;
+}
+
 //==========================================================================================
 // Concrete commands
 //------------------------------------------------------------------------------------------
@@ -1623,6 +1703,7 @@ void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclarationImpl *style)
     // adjust to the positions we want to use for applying style
     Position start(endingSelection().start().downstream(StayInBlock).equivalentRangeCompliantPosition());
     Position end(endingSelection().end().upstream(StayInBlock));
+
     if (RangeImpl::compareBoundaryPoints(end, start) < 0) {
         Position swap = start;
         start = end;
@@ -2519,12 +2600,14 @@ void DeleteSelectionCommand::initializePositionData()
     // Handle setting some basic positions
     //
     Position start = m_selectionToDelete.start();
+    start = positionOutsideContainingSpecialElement(start);
     Position end = m_selectionToDelete.end();
+    end = positionOutsideContainingSpecialElement(end);
 
-    m_upstreamStart = start.upstream(StayInBlock);
-    m_downstreamStart = start.downstream(StayInBlock);
-    m_upstreamEnd = end.upstream(StayInBlock);
-    m_downstreamEnd = end.downstream(StayInBlock);
+    m_upstreamStart = positionBeforePossibleContainingSpecialElement(start.upstream(StayInBlock));
+    m_downstreamStart = positionBeforePossibleContainingSpecialElement(start.downstream(StayInBlock));
+    m_upstreamEnd = positionAfterPossibleContainingSpecialElement(end.upstream(StayInBlock));
+    m_downstreamEnd = positionAfterPossibleContainingSpecialElement(end.downstream(StayInBlock));
 
     //
     // Handle leading and trailing whitespace, as well as smart delete adjustments to the selection
@@ -2613,7 +2696,9 @@ void DeleteSelectionCommand::insertPlaceholderForAncestorBlockContent()
     NodeImpl *upstreamBlock = m_upstreamStart.node()->enclosingBlockFlowElement();
     NodeImpl *beforeUpstreamBlock = m_upstreamStart.upstream().node()->enclosingBlockFlowElement();
     
-    if (upstreamBlock != beforeUpstreamBlock && beforeUpstreamBlock->isAncestor(upstreamBlock)) {
+    if (upstreamBlock != beforeUpstreamBlock && 
+        beforeUpstreamBlock->isAncestor(upstreamBlock) &&
+        upstreamBlock != m_upstreamStart.node()) {
         NodeImpl *downstreamBlock = m_downstreamEnd.node()->enclosingBlockFlowElement();
         NodeImpl *afterDownstreamBlock = m_downstreamEnd.downstream().node()->enclosingBlockFlowElement();
         
@@ -2706,10 +2791,13 @@ void DeleteSelectionCommand::handleGeneralDelete()
             startOffset = 0;
         }
     }
-    else if (startOffset >= m_startNode->caretMaxOffset()) {
+    else if (startOffset >= m_startNode->caretMaxOffset() &&
+             (m_startNode->isAtomicNode() || startOffset == 0)) {
         // Move the start node to the next node in the tree since the startOffset is equal to
         // or beyond the start node's caretMaxOffset This means there is nothing visible to delete. 
-        // However, before moving on, delete any insignificant text that may be present in a text node.
+        // But don't do this if the node is not atomic - we don't want to move into the first child.
+
+        // Also, before moving on, delete any insignificant text that may be present in a text node.
         if (m_startNode->isTextNode()) {
             // Delete any insignificant text from this node.
             TextImpl *text = static_cast<TextImpl *>(m_startNode);
@@ -2728,15 +2816,19 @@ void DeleteSelectionCommand::handleGeneralDelete()
     if (m_startNode == m_downstreamEnd.node()) {
         // The selection to delete is all in one node.
         if (!m_startNode->renderer() || 
-            (startOffset <= m_startNode->caretMinOffset() && m_downstreamEnd.offset() >= m_startNode->caretMaxOffset())) {
+            (startOffset == 0 && m_downstreamEnd.offset() >= maxDeepOffset(m_startNode))) {
             // just delete
-            removeFullySelectedNode(m_startNode);
-        }
-        else if (m_downstreamEnd.offset() - startOffset > 0) {
-            // in a text node that needs to be trimmed
-            TextImpl *text = static_cast<TextImpl *>(m_startNode);
-            deleteTextFromNode(text, startOffset, m_downstreamEnd.offset() - startOffset);
-            m_trailingWhitespaceValid = false;
+            removeFullySelectedNodePreservingPosition(m_startNode, m_upstreamStart);
+        } else if (m_downstreamEnd.offset() - startOffset > 0) {
+            if (m_startNode->isTextNode()) {
+                // in a text node that needs to be trimmed
+                TextImpl *text = static_cast<TextImpl *>(m_startNode);
+                deleteTextFromNode(text, startOffset, m_downstreamEnd.offset() - startOffset);
+                m_trailingWhitespaceValid = false;
+            } else {
+                removeChildrenInRangePreservingPosition(m_startNode, startOffset, m_downstreamEnd.offset(), m_upstreamStart);
+                m_endingPosition = m_upstreamStart;
+            }
         }
     }
     else {
@@ -2744,26 +2836,37 @@ void DeleteSelectionCommand::handleGeneralDelete()
         NodeImpl *node = m_startNode;
         
         if (startOffset > 0) {
-            // in a text node that needs to be trimmed
-            TextImpl *text = static_cast<TextImpl *>(node);
-            deleteTextFromNode(text, startOffset, text->length() - startOffset);
-            node = node->traverseNextNode();
+            if (m_startNode->isTextNode()) {
+                // in a text node that needs to be trimmed
+                TextImpl *text = static_cast<TextImpl *>(node);
+                deleteTextFromNode(text, startOffset, text->length() - startOffset);
+                node = node->traverseNextNode();
+            } else {
+                node = m_startNode->childNode(startOffset);
+            }
         }
         
         // handle deleting all nodes that are completely selected
         while (node && node != m_downstreamEnd.node()) {
-            if (!m_downstreamEnd.node()->isAncestor(node)) {
+            if (RangeImpl::compareBoundaryPoints(Position(node, 0), m_downstreamEnd) >= 0) {
+                // traverseNextSibling just blew past the end position, so stop deleting
+                node = 0;
+            } else if (!m_downstreamEnd.node()->isAncestor(node)) {
                 NodeImpl *nextNode = node->traverseNextSibling();
-                removeFullySelectedNode(node);
+                // if we just removed a node from the end container, update end position so the
+                // check above will work
+                if (node->parentNode() == m_downstreamEnd.node()) {
+                    ASSERT(node->nodeIndex() < (unsigned)m_downstreamEnd.offset());
+                    m_downstreamEnd = Position(m_downstreamEnd.node(), m_downstreamEnd.offset() - 1);
+                }
+                removeFullySelectedNodePreservingPosition(node, m_upstreamStart);
                 node = nextNode;
-            }
-            else {
+            } else {
                 NodeImpl *n = node->lastChild();
                 while (n && n->lastChild())
                     n = n->lastChild();
                 if (n == m_downstreamEnd.node() && m_downstreamEnd.offset() >= m_downstreamEnd.node()->caretMaxOffset()) {
-                    // remove an ancestor of m_downstreamEnd.node(), and thus m_downstreamEnd.node() itself
-                    removeFullySelectedNode(node);
+                    removeFullySelectedNodePreservingPosition(node, m_upstreamStart);
                     m_trailingWhitespaceValid = false;
                     node = 0;
                 } 
@@ -2774,19 +2877,29 @@ void DeleteSelectionCommand::handleGeneralDelete()
         }
 
         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()) {
+            if (m_downstreamEnd.offset() >= maxDeepOffset(m_downstreamEnd.node())) {
                 // need to delete whole node
                 // we can get here if this is the last node in the block
-                removeFullySelectedNode(m_downstreamEnd.node());
+                // remove an ancestor of m_downstreamEnd.node(), and thus m_downstreamEnd.node() itself
+                if (m_upstreamStart.node() == m_downstreamEnd.node() ||
+                    m_upstreamStart.node()->isAncestor(m_downstreamEnd.node())) {
+                    m_upstreamStart = Position(m_downstreamEnd.node()->parentNode(), m_downstreamEnd.node()->nodeIndex());
+                }
+                
+                removeFullySelectedNodePreservingPosition(m_downstreamEnd.node(), m_upstreamStart);
                 m_trailingWhitespaceValid = false;
-            }
-            else {
-                // in a text node that needs to be trimmed
-                TextImpl *text = static_cast<TextImpl *>(m_downstreamEnd.node());
-                if (m_downstreamEnd.offset() > 0) {
-                    deleteTextFromNode(text, 0, m_downstreamEnd.offset());
-                    m_downstreamEnd = Position(text, 0);
-                    m_trailingWhitespaceValid = false;
+            } else {
+                if (m_downstreamEnd.node()->isTextNode()) {
+                    // in a text node that needs to be trimmed
+                    TextImpl *text = static_cast<TextImpl *>(m_downstreamEnd.node());
+                    if (m_downstreamEnd.offset() > 0) {
+                        deleteTextFromNode(text, 0, m_downstreamEnd.offset());
+                        m_downstreamEnd = Position(text, 0);
+                        m_trailingWhitespaceValid = false;
+                    }
+                } else {
+                    removeChildrenInRangePreservingPosition(m_downstreamEnd.node(), 0, m_downstreamEnd.offset(), m_upstreamStart);
+                    m_downstreamEnd = Position(m_downstreamEnd.node(), 0);
                 }
             }
         }
@@ -2929,7 +3042,7 @@ void DeleteSelectionCommand::calculateEndingPosition()
     m_endingPosition = Position(document()->documentElement(), 0);
 }
 
-void DeleteSelectionCommand::calculateTypingStyleAfterDelete(bool insertedPlaceholder)
+void DeleteSelectionCommand::calculateTypingStyleAfterDelete(NodeImpl *insertedPlaceholder)
 {
     // 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
@@ -2950,9 +3063,16 @@ void DeleteSelectionCommand::calculateTypingStyleAfterDelete(bool insertedPlaceh
         // of the preceding line and retains it even if you click away, click back, and
         // then start typing. In this case, the typing style is applied right now, and
         // is not retained until the next typing action.
-        Position pastPlaceholder = endOfParagraph(VisiblePosition(m_endingPosition, m_selectionToDelete.endAffinity())).deepEquivalent();
+
+        // FIXME: is this even right? I don't think post-deletion typing style is supposed 
+        // to be saved across clicking away and clicking back, it certainly isn't in TextEdit
+
+        Position pastPlaceholder(insertedPlaceholder, 1);
+
         setEndingSelection(Selection(m_endingPosition, m_selectionToDelete.endAffinity(), pastPlaceholder, DOWNSTREAM));
+
         applyStyle(m_typingStyle, EditActionUnspecified);
+
         m_typingStyle->deref();
         m_typingStyle = 0;
     }
@@ -3045,8 +3165,10 @@ void DeleteSelectionCommand::doApply()
         forceBlankParagraph = false;
     }
     
-    bool addedBlockPlaceHolder = addBlockPlaceholderIfNeeded(m_endingPosition.node(), forceBlankParagraph);
-    calculateTypingStyleAfterDelete(addedBlockPlaceHolder);
+    NodeImpl *addedPlaceholder = forceBlankParagraph ? insertBlockPlaceholder(m_endingPosition) :
+        addBlockPlaceholderIfNeeded(m_endingPosition.node());
+
+    calculateTypingStyleAfterDelete(addedPlaceholder);
     debugPosition("endingPosition   ", m_endingPosition);
     setEndingSelection(Selection(m_endingPosition, affinity));
     clearTransientState();
@@ -3408,6 +3530,20 @@ void InsertParagraphSeparatorCommand::doApply()
         return;
     }
 
+    //---------------------------------------------------------------------
+    // Handle case when position is in the last visible position in its block. 
+    if (isLastInBlock) {
+        LOG(Editing, "insert paragraph separator: last in block case");
+        if (startBlockIsRoot)
+            appendNode(blockToInsert, startBlock);
+        else
+            insertNodeAfter(blockToInsert, startBlock);
+        appendBlockPlaceholder(blockToInsert);
+        setEndingSelection(Position(blockToInsert, 0), DOWNSTREAM);
+        applyStyleAfterInsertion();
+        return;
+    }
+
     //---------------------------------------------------------------------
     // Handle case when position is in the first visible position in its block.
     // and similar case where upstream position is in another block.
@@ -3416,7 +3552,17 @@ void InsertParagraphSeparatorCommand::doApply()
         LOG(Editing, "insert paragraph separator: first in block case");
         pos = pos.downstream(StayInBlock);
         pos = positionOutsideContainingSpecialElement(pos);
-        NodeImpl *refNode = isFirstInBlock && !startBlockIsRoot ? startBlock : pos.node();
+        Position refPos;
+        NodeImpl *refNode;
+        if (isFirstInBlock && !startBlockIsRoot) {
+            refNode = startBlock;
+        } else if (pos.node() == startBlock && startBlockIsRoot) {
+            ASSERT(startBlock->childNode(pos.offset())); // must be true or we'd be in the end of block case
+            refNode = startBlock->childNode(pos.offset());
+        } else {
+            refNode = pos.node();
+        }
+
         insertNodeBefore(blockToInsert, refNode);
         appendBlockPlaceholder(blockToInsert);
         setEndingSelection(Position(blockToInsert, 0), DOWNSTREAM);
@@ -3425,20 +3571,6 @@ void InsertParagraphSeparatorCommand::doApply()
         return;
     }
 
-    //---------------------------------------------------------------------
-    // Handle case when position is in the last visible position in its block. 
-    if (isLastInBlock) {
-        LOG(Editing, "insert paragraph separator: last in block case");
-        if (startBlockIsRoot)
-            appendNode(blockToInsert, startBlock);
-        else
-            insertNodeAfter(blockToInsert, startBlock);
-        appendBlockPlaceholder(blockToInsert);
-        setEndingSelection(Position(blockToInsert, 0), DOWNSTREAM);
-        applyStyleAfterInsertion();
-        return;
-    }
-
     //---------------------------------------------------------------------
     // Handle the (more complicated) general case,
 
@@ -4325,7 +4457,8 @@ ReplacementFragment::ReplacementFragment(DocumentImpl *document, DocumentFragmen
         removeNode(newlineAtEndNode);
     
     NodeImpl *holder = insertFragmentForTestRendering();
-    holder->ref();
+    if (holder)
+        holder->ref();
     if (!m_matchStyle) {
         computeStylesUsingTestRendering(holder);
     }
@@ -4668,7 +4801,8 @@ void ReplaceSelectionCommand::doApply()
     } else {
         // merge if current selection starts inside a paragraph, or there is only one block and no interchange newline to add
         mergeStart = !m_fragment.hasInterchangeNewlineAtStart() && 
-            (!isStartOfParagraph(visibleStart) || (!m_fragment.hasInterchangeNewlineAtEnd() && !m_fragment.hasMoreThanOneBlock()));
+            (!isStartOfParagraph(visibleStart) || (!m_fragment.hasInterchangeNewlineAtEnd() && !m_fragment.hasMoreThanOneBlock())) &&
+            !isLastVisiblePositionInSpecialElement(selection.start());
         
         // This is a workaround for this bug:
         // <rdar://problem/4013642> REGRESSION (Mail): Copied quoted word does not paste as a quote if pasted at the start of a line
@@ -4681,7 +4815,9 @@ void ReplaceSelectionCommand::doApply()
     // decide whether to later append nodes to the end
     NodeImpl *beyondEndNode = 0;
     if (!isEndOfParagraph(visibleEnd) && !m_fragment.hasInterchangeNewlineAtEnd()) {
-        beyondEndNode = selection.end().downstream(StayInBlock).node();
+        Position beyondEndPos = selection.end().downstream(StayInBlock);
+        if (!isFirstVisiblePositionInSpecialElement(beyondEndPos))
+            beyondEndNode = beyondEndPos.node();
     }
     bool moveNodesAfterEnd = beyondEndNode && (startBlock != endBlock || m_fragment.hasMoreThanOneBlock());
 
@@ -4751,6 +4887,7 @@ void ReplaceSelectionCommand::doApply()
     NodeImpl *linePlaceholder = findBlockPlaceholder(block);
     if (!linePlaceholder) {
         Position downstream = startPos.downstream(StayInBlock);
+        downstream = positionOutsideContainingSpecialElement(downstream);
         if (downstream.node()->id() == ID_BR && downstream.offset() == 0 && 
             m_fragment.hasInterchangeNewlineAtEnd() &&
             isFirstVisiblePositionOnLine(VisiblePosition(downstream, VP_DEFAULT_AFFINITY)))
@@ -4789,7 +4926,7 @@ void ReplaceSelectionCommand::doApply()
     Position insertionPos = startPos;
 
     // step 1: merge content into the start block, if that is needed
-    if (mergeStart) {
+    if (mergeStart && !isFirstVisiblePositionInSpecialElementInFragment(Position(m_fragment.mergeStartNode(), 0))) {
         NodeImpl *refNode = m_fragment.mergeStartNode();
         if (refNode) {
             NodeImpl *node = refNode ? refNode->nextSibling() : 0;
@@ -4923,7 +5060,7 @@ void ReplaceSelectionCommand::doApply()
             }
         }
 
-        if (moveNodesAfterEnd) {
+        if (moveNodesAfterEnd && !isLastVisiblePositionInSpecialElement(Position(m_lastNodeInserted, maxRangeOffset(m_lastNodeInserted)))) {
             document()->updateLayout();
             QValueList<NodeDesiredStyle> styles;
             QPtrList<NodeImpl> blocks;
index 31e441d3a1c17d498185d2bfc50472425f33b79b..bd943ece44baaac23c009f5a5d1b2d0641f20c89 100644 (file)
@@ -228,9 +228,11 @@ protected:
     void joinTextNodes(DOM::TextImpl *text1, DOM::TextImpl *text2);
     void rebalanceWhitespace();
     void removeCSSProperty(DOM::CSSStyleDeclarationImpl *, int property);
-    void removeFullySelectedNode(DOM::NodeImpl *);
+    void removeFullySelectedNodePreservingPosition(DOM::NodeImpl *node, DOM::Position &pos);
     void removeNodeAttribute(DOM::ElementImpl *, int attribute);
+    void removeChildrenInRangePreservingPosition(DOM::NodeImpl *node, int from, int to, DOM::Position &pos);
     void removeNode(DOM::NodeImpl *removeChild);
+    void removeNodePreservingPosition(DOM::NodeImpl *removeChild, DOM::Position &pos);
     void removeNodePreservingChildren(DOM::NodeImpl *node);
     void replaceTextInNode(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText);
     void setNodeAttribute(DOM::ElementImpl *, int attribute, const DOM::DOMString &);
@@ -244,9 +246,9 @@ protected:
     void deleteInsignificantText(const DOM::Position &start, const DOM::Position &end);
     void deleteInsignificantTextDownstream(const DOM::Position &);
 
-    void insertBlockPlaceholder(DOM::NodeImpl *);
-    void appendBlockPlaceholder(DOM::NodeImpl *);
-    bool addBlockPlaceholderIfNeeded(DOM::NodeImpl *, bool forceInsertIfNonEmpty=false);
+    DOM::NodeImpl *appendBlockPlaceholder(DOM::NodeImpl *);
+    DOM::NodeImpl *insertBlockPlaceholder(const DOM::Position &pos);
+    DOM::NodeImpl *addBlockPlaceholderIfNeeded(DOM::NodeImpl *);
     bool removeBlockPlaceholder(DOM::NodeImpl *);
     DOM::NodeImpl *findBlockPlaceholder(DOM::NodeImpl *);
 
@@ -378,7 +380,7 @@ private:
     void fixupWhitespace();
     void moveNodesAfterNode();
     void calculateEndingPosition();
-    void calculateTypingStyleAfterDelete(bool insertedPlaceholder);
+    void calculateTypingStyleAfterDelete(DOM::NodeImpl *insertedPlaceholder);
     void clearTransientState();
 
     void setStartNode(DOM::NodeImpl *);
index e49205b1e89bec096cd0e433bf15b4e854c68320..49e3559fbaa71e785b219cda099cbae74c73c763 100644 (file)
@@ -115,6 +115,24 @@ void VisiblePosition::initUpstream(const Position &pos)
     }
 }
 
+static bool isRenderedBR(NodeImpl *node)
+{
+    if (!node)
+        return false;
+
+    RenderObject *renderer = node->renderer();
+    if (!renderer)
+        return false;
+    
+    if (renderer->style()->visibility() != VISIBLE)
+        return false;
+
+    if (renderer->isBR())
+        return true;
+
+    return false;
+}
+
 void VisiblePosition::initDownstream(const Position &pos)
 {
     Position deepPos = deepEquivalent(pos);
@@ -127,11 +145,12 @@ void VisiblePosition::initDownstream(const Position &pos)
             if (next.isNotNull())
                 m_deepPosition = next;
         }
-    }
-    else {
+    } else {
         Position next = nextVisiblePosition(deepPos);
         if (next.isNotNull()) {
             m_deepPosition = next;
+        } else if (isRenderedBR(deepPos.node()) && deepPos.offset() == 1) {
+            m_deepPosition = deepPos;
         } else {
             Position previous = previousVisiblePosition(deepPos);
             if (previous.isNotNull())
@@ -617,8 +636,8 @@ bool isLastVisiblePositionInBlock(const VisiblePosition &pos)
         case NoBlockRelationship:
         case SameBlockRelationship:
         case AncestorBlockRelationship:
-        case OtherBlockRelationship:
             return false;
+        case OtherBlockRelationship:
         case PeerBlockRelationship:
         case DescendantBlockRelationship:
             return true;
index ed03c231660fa4abc9b4d54aecebce4319eee484..7340047e992f4d2c5639feca2d4c1c892ac6b271 100644 (file)
@@ -1305,9 +1305,8 @@ InlineBox* RootInlineBox::closestLeafChildForXPos(int _x, int _tx)
     for (InlineBox *leaf = firstLeaf; leaf && leaf != lastLeaf; leaf = leaf->nextLeafChild()) {
         if (!leaf->object()->isListMarker()) {
             int leafX = _tx + leaf->m_x;
-            assert(_x >= leafX);
             if (_x < leafX + leaf->m_width)
-                // The x coordinate is greater or equal to left edge of the box's start.
+                // The x coordinate is less than the right edge of the box.
                 // Return it.
                 return leaf;
         }
index 5c554e863bc3cad20e56a6f3f98a8b36c74ad899..53d25b505a9ada2bbebe08788cc7df31a68f1e8b 100644 (file)
@@ -1398,11 +1398,32 @@ bool NodeImpl::isBlockFlow() const
     return renderer() && renderer()->isBlockFlow();
 }
 
+bool NodeImpl::isBlockFlowOrTable() const
+{
+    return renderer() && (renderer()->isBlockFlow() || renderer()->isTable());
+}
+
 bool NodeImpl::isEditableBlock() const
 {
     return isContentEditable() && isBlockFlow();
 }
 
+ElementImpl *NodeImpl::enclosingBlockFlowOrTableElement() const
+{
+    NodeImpl *n = const_cast<NodeImpl *>(this);
+    if (isBlockFlowOrTable())
+        return static_cast<ElementImpl *>(n);
+
+    while (1) {
+        n = n->parentNode();
+        if (!n)
+            break;
+        if (n->isBlockFlowOrTable() || n->id() == ID_BODY)
+            return static_cast<ElementImpl *>(n);
+    }
+    return 0;
+}
+
 ElementImpl *NodeImpl::enclosingBlockFlowElement() const
 {
     NodeImpl *n = const_cast<NodeImpl *>(this);
index 522ab1fa3450d73eeddc99e0264b807131e98ed8..812427f19d8dc7c2485326b5d343679123893b26 100644 (file)
@@ -127,6 +127,7 @@ public:
     virtual bool isDocumentNode() const { return false; }
     virtual bool isXMLElementNode() const { return false; }
     bool isBlockFlow() const;
+    bool isBlockFlowOrTable() const;
     
     // Used by <form> elements to indicate a malformed state of some kind, typically
     // used to keep from applying the bottom margin of the form.
@@ -167,6 +168,7 @@ public:
 
     bool isEditableBlock() const;
     ElementImpl *enclosingBlockFlowElement() const;
+    ElementImpl *enclosingBlockFlowOrTableElement() const;
     ElementImpl *enclosingInlineElement() const;
     ElementImpl *rootEditableElement() const;
     
index a5a609519523bfcd89a4b722e1aa1cff00eac2b6..1a040fe492d6ccd1eb4ff8790f4841ba0f3b0063 100644 (file)
@@ -247,7 +247,7 @@ Position Position::upstream(EStayInBlock stayInBlock) const
     if (!startNode)
         return Position();
 
-    NodeImpl *block = startNode->enclosingBlockFlowElement();
+    NodeImpl *block = startNode->enclosingBlockFlowOrTableElement();
     Position lastVisible;
     
     PositionIterator it(start);
@@ -255,7 +255,7 @@ Position Position::upstream(EStayInBlock stayInBlock) const
         NodeImpl *currentNode = it.current().node();
 
         if (stayInBlock) {
-            NodeImpl *currentBlock = currentNode->enclosingBlockFlowElement();
+            NodeImpl *currentBlock = currentNode->enclosingBlockFlowOrTableElement();
             if (block != currentBlock)
                 return it.next();
         }
@@ -306,7 +306,7 @@ Position Position::downstream(EStayInBlock stayInBlock) const
     if (!startNode)
         return Position();
 
-    NodeImpl *block = startNode->enclosingBlockFlowElement();
+    NodeImpl *block = startNode->enclosingBlockFlowOrTableElement();
     Position lastVisible;
     
     PositionIterator it(start);            
@@ -314,7 +314,7 @@ Position Position::downstream(EStayInBlock stayInBlock) const
         NodeImpl *currentNode = it.current().node();
 
         if (stayInBlock) {
-            NodeImpl *currentBlock = currentNode->enclosingBlockFlowElement();
+            NodeImpl *currentBlock = currentNode->enclosingBlockFlowOrTableElement();
             if (block != currentBlock)
                 return it.previous();
         }
@@ -354,7 +354,7 @@ Position Position::downstream(EStayInBlock stayInBlock) const
         }
 
         if (renderer->isText() && static_cast<RenderText *>(renderer)->firstTextBox()) {
-            if (currentNode != node())
+            if (currentNode != start.node())
                 return Position(currentNode, renderer->caretMinOffset());
 
             if (it.current().offset() < 0)