Reviewed by Darin
authorkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 13 Sep 2004 18:06:38 +0000 (18:06 +0000)
committerkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 13 Sep 2004 18:06:38 +0000 (18:06 +0000)
        Various editing improvements, many focused on the improvements made possible by the
        new CaretPosition class.

        Includes fixes for these bugs:

        <rdar://problem/3748164> REGRESSION (Mail): Arrow navigation in typical mail message can result in stuck caret
        <rdar://problem/3782062> REGRESSION (Mail): option-delete can delete almost all of a message when it has trouble finding a word
        <rdar://problem/3790456> triple click does not select entire paragraph (folklore.org)

        * WebCore.pbproj/project.pbxproj: Added CaretPosition class files.
        * khtml/dom/dom2_range.h:
        (DOM::offsetInCharacters): Moved this helper here from khtml_text_operations.cpp. This
        function helps to determine how to interpret the offsets used in DOM Ranges.
        * khtml/editing/htmlediting_impl.cpp:
        (khtml::InputNewlineCommandImpl::doApply): Use CaretPosition class to make "end-of-block" determination.
        Also, fix a caret placement glitch in "case 1" in the code: Place the caret in the node after the inserted
        BR. This makes it show up in the right place.
        * khtml/khtml_part.cpp:
        (KHTMLPart::handleMousePressEventDoubleClick): Don't limit double-click and triple-click handling only to text nodes.
        (KHTMLPart::handleMousePressEventTripleClick): Ditto.
        (KHTMLPart::selectAll): Use CaretPosition class to implement improved selectAll.
        * khtml/misc/khtml_text_operations.cpp:
        (khtml::SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator): Make a fix to the initial state setting of
        m_handledChildren. This is true if the offset into the end node is 0, meaning that we do not want to descend
        into its children at all.
        * khtml/xml/dom_caretposition.cpp: Added.
        * khtml/xml/dom_caretposition.h: Added.
        * khtml/xml/dom_nodeimpl.cpp:
        (NodeImpl::traverseNextNode): Fix bugs with the stayWithin implementation. We could miss nodes we want to test
        due to an erroneous check of stayWithin when no such check is needed.
        (NodeImpl::traverseNextSibling): Ditto.
        (NodeImpl::traversePreviousNodePostOrder): Ditto.
        * khtml/xml/dom_position.cpp:
        (DOM::Position::Position): Remove spurious semi-colon.
        (DOM::Position::upstream): Move incoming Position to its equivalentDeepPosition. This is part of the transition
        that will make this code work better with increased use of CaretPosition.
        (DOM::Position::downstream): Ditto.
        (DOM::Position::atStartOfContainingEditableBlock): Removed. Dead code; was not being called.
        (DOM::Position::atStartOfRootEditableElement): Removed. Dead code; was not being called.
        (DOM::Position::isLastRenderedPositionInEditableBlock): Removed. Replaced with calls to CaretPosition class.
        (DOM::Position::inLastEditableInRootEditableElement): Removed. Dead code; was not being called.
        (DOM::Position::inFirstEditableInRootEditableElement): Removed. Was only being called by other code that has been removed.
        * khtml/xml/dom_position.h:
        * khtml/xml/dom_selection.cpp:
        (DOM::Selection::modifyExtendingRightForward): Moved implementation of CHARACTER case to use CaretPosition class instead
        of Position class helpers.
        (DOM::Selection::modifyMovingRightForward): Ditto.
        (DOM::Selection::modifyExtendingLeftBackward): Ditto.
        (DOM::Selection::modifyMovingLeftBackward): Ditto.
        (DOM::Selection::validate): Made simplifications in code that used to call a concoction of Position class helpers to
        do the right thing. Now calls CaretPosition equivalents.

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

18 files changed:
WebCore/ChangeLog-2005-08-23
WebCore/ForwardingHeaders/xml/dom_caretposition.h [new file with mode: 0644]
WebCore/WebCore.pbproj/project.pbxproj
WebCore/khtml/dom/dom2_range.h
WebCore/khtml/editing/SelectionController.cpp
WebCore/khtml/editing/htmlediting_impl.cpp
WebCore/khtml/editing/selection.cpp
WebCore/khtml/editing/visible_position.cpp [new file with mode: 0644]
WebCore/khtml/editing/visible_position.h [new file with mode: 0644]
WebCore/khtml/editing/visible_text.cpp
WebCore/khtml/khtml_part.cpp
WebCore/khtml/misc/khtml_text_operations.cpp
WebCore/khtml/xml/dom_caretposition.cpp [new file with mode: 0644]
WebCore/khtml/xml/dom_caretposition.h [new file with mode: 0644]
WebCore/khtml/xml/dom_nodeimpl.cpp
WebCore/khtml/xml/dom_position.cpp
WebCore/khtml/xml/dom_position.h
WebCore/khtml/xml/dom_selection.cpp

index 80c731a2e9de797a38d592686b3b56e4a1596051..1ad9fb20ad7d7fa6f67be4410e779945b1b47070 100644 (file)
@@ -1,3 +1,59 @@
+2004-09-13  Ken Kocienda  <kocienda@apple.com>
+
+        Reviewed by Darin
+
+        Various editing improvements, many focused on the improvements made possible by the
+        new CaretPosition class.
+        
+        Includes fixes for these bugs:
+        
+        <rdar://problem/3748164> REGRESSION (Mail): Arrow navigation in typical mail message can result in stuck caret
+        <rdar://problem/3782062> REGRESSION (Mail): option-delete can delete almost all of a message when it has trouble finding a word
+        <rdar://problem/3790456> triple click does not select entire paragraph (folklore.org)
+
+        * WebCore.pbproj/project.pbxproj: Added CaretPosition class files.
+        * khtml/dom/dom2_range.h:
+        (DOM::offsetInCharacters): Moved this helper here from khtml_text_operations.cpp. This
+        function helps to determine how to interpret the offsets used in DOM Ranges.
+        * khtml/editing/htmlediting_impl.cpp:
+        (khtml::InputNewlineCommandImpl::doApply): Use CaretPosition class to make "end-of-block" determination.
+        Also, fix a caret placement glitch in "case 1" in the code: Place the caret in the node after the inserted
+        BR. This makes it show up in the right place.
+        * khtml/khtml_part.cpp:
+        (KHTMLPart::handleMousePressEventDoubleClick): Don't limit double-click and triple-click handling only to text nodes.
+        (KHTMLPart::handleMousePressEventTripleClick): Ditto.
+        (KHTMLPart::selectAll): Use CaretPosition class to implement improved selectAll.
+        * khtml/misc/khtml_text_operations.cpp:
+        (khtml::SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator): Make a fix to the initial state setting of
+        m_handledChildren. This is true if the offset into the end node is 0, meaning that we do not want to descend
+        into its children at all.
+        * khtml/xml/dom_caretposition.cpp: Added.
+        * khtml/xml/dom_caretposition.h: Added.
+        * khtml/xml/dom_nodeimpl.cpp:
+        (NodeImpl::traverseNextNode): Fix bugs with the stayWithin implementation. We could miss nodes we want to test
+        due to an erroneous check of stayWithin when no such check is needed.
+        (NodeImpl::traverseNextSibling): Ditto.
+        (NodeImpl::traversePreviousNodePostOrder): Ditto.
+        * khtml/xml/dom_position.cpp:
+        (DOM::Position::Position): Remove spurious semi-colon.
+        (DOM::Position::upstream): Move incoming Position to its equivalentDeepPosition. This is part of the transition
+        that will make this code work better with increased use of CaretPosition.
+        (DOM::Position::downstream): Ditto.
+        (DOM::Position::atStartOfContainingEditableBlock): Removed. Dead code; was not being called.
+        (DOM::Position::atStartOfRootEditableElement): Removed. Dead code; was not being called.
+        (DOM::Position::isLastRenderedPositionInEditableBlock): Removed. Replaced with calls to CaretPosition class.
+        (DOM::Position::inLastEditableInRootEditableElement): Removed. Dead code; was not being called.
+        (DOM::Position::inFirstEditableInRootEditableElement): Removed. Was only being called by other code that has been removed.
+        * khtml/xml/dom_position.h:
+        * khtml/xml/dom_selection.cpp:
+        (DOM::Selection::modifyExtendingRightForward): Moved implementation of CHARACTER case to use CaretPosition class instead
+        of Position class helpers.
+        (DOM::Selection::modifyMovingRightForward): Ditto.
+        (DOM::Selection::modifyExtendingLeftBackward): Ditto.
+        (DOM::Selection::modifyMovingLeftBackward): Ditto.
+        (DOM::Selection::validate): Made simplifications in code that used to call a concoction of Position class helpers to
+        do the right thing. Now calls CaretPosition equivalents.
+
 2004-09-13  Ken Kocienda  <kocienda@apple.com>
 
         Reviewed by me
diff --git a/WebCore/ForwardingHeaders/xml/dom_caretposition.h b/WebCore/ForwardingHeaders/xml/dom_caretposition.h
new file mode 100644 (file)
index 0000000..7726ea2
--- /dev/null
@@ -0,0 +1 @@
+#include <dom_caretposition.h>
index cd1c813349be8649b18578d7469a38577c82a29f..5ed98af8862865cb2076e68ff64f4b3ce69f49d9 100644 (file)
                                BC06F25006D18A7E004A6FA3,
                                93ABCE6006E1A42E0085925B,
                                93B641F406E28C5C0055F610,
+                               BEF3442706F1123C000A65D3,
                        );
                        isa = PBXHeadersBuildPhase;
                        runOnlyForDeploymentPostprocessing = 0;
                                BC06F24F06D18A7E004A6FA3,
                                93ABCE5F06E1A42E0085925B,
                                93B641F306E28C5C0055F610,
+                               BEF3442606F1123C000A65D3,
                        );
                        isa = PBXSourcesBuildPhase;
                        runOnlyForDeploymentPostprocessing = 0;
                        refType = 4;
                        sourceTree = "<group>";
                };
+               BEF3442406F1123C000A65D3 = {
+                       fileEncoding = 30;
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.cpp.cpp;
+                       path = dom_caretposition.cpp;
+                       refType = 4;
+                       sourceTree = "<group>";
+               };
+               BEF3442506F1123C000A65D3 = {
+                       fileEncoding = 30;
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.c.h;
+                       path = dom_caretposition.h;
+                       refType = 4;
+                       sourceTree = "<group>";
+               };
+               BEF3442606F1123C000A65D3 = {
+                       fileRef = BEF3442406F1123C000A65D3;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               BEF3442706F1123C000A65D3 = {
+                       fileRef = BEF3442506F1123C000A65D3;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
                BEF7EEA005FF8F0D009717EE = {
                        fileEncoding = 30;
                        isa = PBXFileReference;
                                BC3B364505C9D5E200E42902,
                                BC3B364605C9D5E200E42902,
                                BC3B364705C9D5E200E42902,
+                               BEF3442406F1123C000A65D3,
+                               BEF3442506F1123C000A65D3,
                                F523D2F402DE4476018635CA,
                                F523D2F502DE4476018635CA,
                                F523D2F702DE4476018635CA,
index 49c74fe36377561f5b72e145a0d805182d2707a3..435d6519cabf07d0dae895e52fe76dd2883595c2 100644 (file)
@@ -477,6 +477,34 @@ private:
 bool operator==(const Range &, const Range &);
 inline bool operator!=(const Range &a, const Range &b) { return !(a == b); }
 
+// Used determine how to interpret the offsets used in DOM Ranges.
+inline bool offsetInCharacters(unsigned short type)
+{
+    switch (type) {
+        case Node::ATTRIBUTE_NODE:
+        case Node::DOCUMENT_FRAGMENT_NODE:
+        case Node::DOCUMENT_NODE:
+        case Node::ELEMENT_NODE:
+        case Node::ENTITY_REFERENCE_NODE:
+            return false;
+
+        case Node::CDATA_SECTION_NODE:
+        case Node::COMMENT_NODE:
+        case Node::PROCESSING_INSTRUCTION_NODE:
+        case Node::TEXT_NODE:
+            return true;
+
+        case Node::DOCUMENT_TYPE_NODE:
+        case Node::ENTITY_NODE:
+        case Node::NOTATION_NODE:
+            assert(false); // should never be reached
+            return false;
+    }
+
+    assert(false); // should never be reached
+    return false;
+}
+
 } // namespace
 
 #endif
index 2552884316cce8585a4c5e24a7529ad94b1d1cb8..52b1d42a3c90c7cc2c5a06fe4e48abca3d132aa2 100644 (file)
   
 #include "dom_selection.h"
 
+#include "dom_caretposition.h"
+#include "dom_docimpl.h"
+#include "dom_elementimpl.h"
+#include "dom_node.h"
+#include "dom_nodeimpl.h"
+#include "dom_positioniterator.h"
+#include "dom_string.h"
+#include "dom_textimpl.h"
+#include "dom2_range.h"
 #include "htmltags.h"
 #include "khtml_part.h"
 #include "khtmlview.h"
 #include "qevent.h"
 #include "qpainter.h"
 #include "qrect.h"
-#include "dom/dom2_range.h"
-#include "dom/dom_node.h"
-#include "dom/dom_string.h"
-#include "rendering/render_object.h"
-#include "rendering/render_style.h"
-#include "rendering/render_text.h"
-#include "xml/dom_docimpl.h"
-#include "xml/dom_positioniterator.h"
-#include "xml/dom_elementimpl.h"
-#include "xml/dom_nodeimpl.h"
-#include "xml/dom_textimpl.h"
+#include "render_object.h"
+#include "render_style.h"
+#include "render_text.h"
 
 #if APPLE_CHANGES
 #include "KWQAssertions.h"
@@ -204,7 +205,7 @@ Position Selection::modifyExtendingRightForward(ETextGranularity granularity)
             pos = pos.nextCharacterPosition();
             break;
         case WORD:
-            pos = pos.nextWordPosition();
+            pos = CaretPosition(pos).next().deepEquivalent();
             break;
         case PARAGRAPH:
             // "Next paragraph" not implemented yet. Fall through to LINE.
@@ -235,7 +236,7 @@ Position Selection::modifyMovingRightForward(ETextGranularity granularity)
             if (state() == RANGE) 
                 pos = end();
             else
-                pos = extent().nextCharacterPosition();
+                pos = CaretPosition(extent()).next().deepEquivalent();
             break;
         case WORD:
             pos = extent().nextWordPosition();
@@ -269,7 +270,7 @@ Position Selection::modifyExtendingLeftBackward(ETextGranularity granularity)
     Position pos = extent();
     switch (granularity) {
         case CHARACTER:
-            pos = pos.previousCharacterPosition();
+            pos = CaretPosition(pos).previous().deepEquivalent();
             break;
         case WORD:
             pos = pos.previousWordPosition();
@@ -301,7 +302,7 @@ Position Selection::modifyMovingLeftBackward(ETextGranularity granularity)
             if (state() == RANGE) 
                 pos = start();
             else
-                pos = extent().previousCharacterPosition();
+                pos = CaretPosition(extent()).previous().deepEquivalent();
             break;
         case WORD:
             pos = extent().previousWordPosition();
@@ -698,26 +699,26 @@ void Selection::validate(ETextGranularity granularity)
         }
         case PARAGRAPH:
             if (m_baseIsStart) {
-                assignStart(base().startParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-                assignEnd(extent().endParagraphBoundary(IncludeLineBreak).equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+                assignStart(CaretPosition(base().startParagraphBoundary()).deepEquivalent());
+                assignEnd(CaretPosition(extent().endParagraphBoundary(IncludeLineBreak)).deepEquivalent());
             } else {
-                assignStart(extent().startParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-                assignEnd(base().endParagraphBoundary(IncludeLineBreak).equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+                assignStart(CaretPosition(extent().startParagraphBoundary()).deepEquivalent());
+                assignEnd(CaretPosition(base().endParagraphBoundary(IncludeLineBreak)).deepEquivalent());
             }
             break;
         case DOCUMENT: {
             NodeImpl *de = start().node()->getDocument()->documentElement();
-            assignStart(Position(de, 0).equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-            assignEnd(Position(de, de->childNodeCount()).equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+            assignStart(CaretPosition(de, 0).deepEquivalent());
+            assignEnd(CaretPosition(de, de->childNodeCount()).deepEquivalent());
             break;
         }
         case PARAGRAPH_BOUNDARY:
             if (m_baseIsStart) {
-                assignStart(base().startParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-                assignEnd(extent().endParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+                assignStart(CaretPosition(base().startParagraphBoundary()).deepEquivalent());
+                assignEnd(CaretPosition(extent().endParagraphBoundary(IncludeLineBreak)).deepEquivalent());
             } else {
-                assignStart(extent().startParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-                assignEnd(base().endParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+                assignStart(CaretPosition(extent().startParagraphBoundary()).deepEquivalent());
+                assignEnd(CaretPosition(base().endParagraphBoundary(IncludeLineBreak)).deepEquivalent());
             }
             break;
     }
index 14687f47c2ddd6f1444e7a86605797f6fbda8b52..2c414ce4c322fb97c8a0b7df0babbd144b04d63e 100644 (file)
@@ -40,6 +40,7 @@
 #include "rendering/render_object.h"
 #include "rendering/render_style.h"
 #include "rendering/render_text.h"
+#include "xml/dom_caretposition.h"
 #include "xml/dom_docimpl.h"
 #include "xml/dom_elementimpl.h"
 #include "xml/dom_positioniterator.h"
@@ -56,6 +57,7 @@
 #endif
 
 using DOM::AttrImpl;
+using DOM::CaretPosition;
 using DOM::CSSComputedStyleDeclarationImpl;
 using DOM::CSSPrimitiveValue;
 using DOM::CSSPrimitiveValueImpl;
@@ -1381,7 +1383,7 @@ void InputNewlineCommandImpl::doApply()
     Position pos(selection.start().upstream(StayInBlock));
     bool atStart = pos.offset() <= pos.node()->caretMinOffset();
     bool atEnd = pos.offset() >= pos.node()->caretMaxOffset();
-    bool atEndOfBlock = pos.isLastRenderedPositionInEditableBlock();
+    bool atEndOfBlock = CaretPosition(pos).isLastInBlock();
     
     if (atEndOfBlock) {
         LOG(Editing, "input newline case 1");
@@ -1393,7 +1395,7 @@ void InputNewlineCommandImpl::doApply()
         bool hasTrailingBR = next && next->id() == ID_BR;
         insertNodeAfterPosition(nodeToInsert, pos);
         if (hasTrailingBR) {
-            setEndingSelection(Position(nodeToInsert, 0));
+            setEndingSelection(Position(next, 0));
         }
         else {
             // Insert an "extra" BR at the end of the block. 
index 2552884316cce8585a4c5e24a7529ad94b1d1cb8..52b1d42a3c90c7cc2c5a06fe4e48abca3d132aa2 100644 (file)
   
 #include "dom_selection.h"
 
+#include "dom_caretposition.h"
+#include "dom_docimpl.h"
+#include "dom_elementimpl.h"
+#include "dom_node.h"
+#include "dom_nodeimpl.h"
+#include "dom_positioniterator.h"
+#include "dom_string.h"
+#include "dom_textimpl.h"
+#include "dom2_range.h"
 #include "htmltags.h"
 #include "khtml_part.h"
 #include "khtmlview.h"
 #include "qevent.h"
 #include "qpainter.h"
 #include "qrect.h"
-#include "dom/dom2_range.h"
-#include "dom/dom_node.h"
-#include "dom/dom_string.h"
-#include "rendering/render_object.h"
-#include "rendering/render_style.h"
-#include "rendering/render_text.h"
-#include "xml/dom_docimpl.h"
-#include "xml/dom_positioniterator.h"
-#include "xml/dom_elementimpl.h"
-#include "xml/dom_nodeimpl.h"
-#include "xml/dom_textimpl.h"
+#include "render_object.h"
+#include "render_style.h"
+#include "render_text.h"
 
 #if APPLE_CHANGES
 #include "KWQAssertions.h"
@@ -204,7 +205,7 @@ Position Selection::modifyExtendingRightForward(ETextGranularity granularity)
             pos = pos.nextCharacterPosition();
             break;
         case WORD:
-            pos = pos.nextWordPosition();
+            pos = CaretPosition(pos).next().deepEquivalent();
             break;
         case PARAGRAPH:
             // "Next paragraph" not implemented yet. Fall through to LINE.
@@ -235,7 +236,7 @@ Position Selection::modifyMovingRightForward(ETextGranularity granularity)
             if (state() == RANGE) 
                 pos = end();
             else
-                pos = extent().nextCharacterPosition();
+                pos = CaretPosition(extent()).next().deepEquivalent();
             break;
         case WORD:
             pos = extent().nextWordPosition();
@@ -269,7 +270,7 @@ Position Selection::modifyExtendingLeftBackward(ETextGranularity granularity)
     Position pos = extent();
     switch (granularity) {
         case CHARACTER:
-            pos = pos.previousCharacterPosition();
+            pos = CaretPosition(pos).previous().deepEquivalent();
             break;
         case WORD:
             pos = pos.previousWordPosition();
@@ -301,7 +302,7 @@ Position Selection::modifyMovingLeftBackward(ETextGranularity granularity)
             if (state() == RANGE) 
                 pos = start();
             else
-                pos = extent().previousCharacterPosition();
+                pos = CaretPosition(extent()).previous().deepEquivalent();
             break;
         case WORD:
             pos = extent().previousWordPosition();
@@ -698,26 +699,26 @@ void Selection::validate(ETextGranularity granularity)
         }
         case PARAGRAPH:
             if (m_baseIsStart) {
-                assignStart(base().startParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-                assignEnd(extent().endParagraphBoundary(IncludeLineBreak).equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+                assignStart(CaretPosition(base().startParagraphBoundary()).deepEquivalent());
+                assignEnd(CaretPosition(extent().endParagraphBoundary(IncludeLineBreak)).deepEquivalent());
             } else {
-                assignStart(extent().startParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-                assignEnd(base().endParagraphBoundary(IncludeLineBreak).equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+                assignStart(CaretPosition(extent().startParagraphBoundary()).deepEquivalent());
+                assignEnd(CaretPosition(base().endParagraphBoundary(IncludeLineBreak)).deepEquivalent());
             }
             break;
         case DOCUMENT: {
             NodeImpl *de = start().node()->getDocument()->documentElement();
-            assignStart(Position(de, 0).equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-            assignEnd(Position(de, de->childNodeCount()).equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+            assignStart(CaretPosition(de, 0).deepEquivalent());
+            assignEnd(CaretPosition(de, de->childNodeCount()).deepEquivalent());
             break;
         }
         case PARAGRAPH_BOUNDARY:
             if (m_baseIsStart) {
-                assignStart(base().startParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-                assignEnd(extent().endParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+                assignStart(CaretPosition(base().startParagraphBoundary()).deepEquivalent());
+                assignEnd(CaretPosition(extent().endParagraphBoundary(IncludeLineBreak)).deepEquivalent());
             } else {
-                assignStart(extent().startParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-                assignEnd(base().endParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+                assignStart(CaretPosition(extent().startParagraphBoundary()).deepEquivalent());
+                assignEnd(CaretPosition(base().endParagraphBoundary(IncludeLineBreak)).deepEquivalent());
             }
             break;
     }
diff --git a/WebCore/khtml/editing/visible_position.cpp b/WebCore/khtml/editing/visible_position.cpp
new file mode 100644 (file)
index 0000000..6a82f79
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "dom_caretposition.h"
+
+#include "dom2_range.h"
+#include "dom_elementimpl.h"
+#include "dom_nodeimpl.h"
+#include "dom_string.h"
+#include "htmltags.h"
+#include "render_object.h"
+#include "render_text.h"
+
+#if APPLE_CHANGES
+#include "KWQAssertions.h"
+#include "KWQLogging.h"
+#else
+#define ASSERT(assertion) assert(assertion)
+#define LOG(channel, formatAndArgs...) ((void)0)
+#endif
+
+using khtml::InlineTextBox;
+using khtml::RenderObject;
+using khtml::RenderText;
+using khtml::VISIBLE;
+
+namespace DOM {
+
+CaretPosition::CaretPosition(NodeImpl *node, long offset)
+    : m_node(0)
+{
+    init(Position(node, offset));
+}
+
+CaretPosition::CaretPosition(const Position &pos)
+    : m_node(0)
+{
+    init(pos);
+}
+
+CaretPosition::CaretPosition(const CaretPosition &pos)
+    : m_node(0)
+{
+    setPosition(pos);
+}
+
+CaretPosition::~CaretPosition()
+{
+    if (m_node)
+        m_node->deref();
+}
+
+void CaretPosition::init(const Position &pos)
+{
+    Position result;
+    Position deepPos = deepEquivalent(pos);
+    
+    if (isCandidate(deepPos)) {
+        result = deepPos;
+        Position previous = previousCaretPosition(deepPos);
+        if (previous.notEmpty()) {
+            Position next = nextCaretPosition(previous);
+            if (next.notEmpty())
+                result = next;
+        }
+    }
+    else {
+        Position next = nextCaretPosition(deepPos);
+        if (next.notEmpty()) {
+            result = next;
+        }
+        else {
+            Position previous = previousCaretPosition(deepPos);
+            if (previous.notEmpty())
+                result = previous;
+        }
+    }
+    
+    setPosition(result);
+}
+
+CaretPosition &CaretPosition::operator=(const CaretPosition &pos)
+{
+    setPosition(pos);
+    return *this;
+}
+
+bool CaretPosition::isLastInBlock() const
+{
+    if (isEmpty())
+        return false;
+        
+    CaretPosition n = next();
+    return n.isEmpty() || (n.deepEquivalent().node()->enclosingBlockFlowElement() != node()->enclosingBlockFlowElement());
+}
+
+CaretPosition CaretPosition::next() const
+{
+    CaretPosition result;
+    result.setPosition(nextCaretPosition(*this));
+    return result;
+}
+
+CaretPosition CaretPosition::previous() const
+{
+    CaretPosition result;
+    result.setPosition(previousCaretPosition(*this));
+    return result;
+}
+
+Position CaretPosition::previousCaretPosition(const Position &pos)
+{
+    if (pos.isEmpty() || atStart(pos))
+        return Position();
+
+    Position test = deepEquivalent(pos);
+    bool acceptAnyCaretPosition = !isCandidate(test) || pos.isFirstRenderedPositionOnLine();
+
+    Position current = test;
+    while (!atStart(current)) {
+        current = previousPosition(current);
+        if (isCandidate(current) && (acceptAnyCaretPosition || current.rendersInDifferentPosition(test))) {
+            return current;
+        }
+    }
+    
+    return Position();
+}
+
+Position CaretPosition::nextCaretPosition(const Position &pos)
+{
+    if (pos.isEmpty() || atEnd(pos))
+        return Position();
+
+    Position test = deepEquivalent(pos);
+    bool acceptAnyCaretPosition = !isCandidate(test) || pos.isLastRenderedPositionOnLine();
+
+    Position current = test;
+    while (!atEnd(current)) {
+        current = nextPosition(current);
+        if (isCandidate(current) && (acceptAnyCaretPosition || current.rendersInDifferentPosition(test))) {
+            return current;
+        }
+    }
+    
+    return Position();
+}
+
+Position CaretPosition::previousPosition(const Position &pos)
+{
+    if (pos.isEmpty())
+        return pos;
+    
+    Position result;
+
+    if (pos.offset() <= 0) {
+        NodeImpl *prevNode = pos.node()->previousLeafNode();
+        if (prevNode)
+            result = Position(prevNode, prevNode->maxOffset());
+    }
+    else {
+        result = Position(pos.node(), pos.offset() - 1);
+    }
+    
+    return result;
+}
+
+Position CaretPosition::nextPosition(const Position &pos)
+{
+    if (pos.isEmpty())
+        return pos;
+    
+    Position result;
+
+    if (pos.offset() >= pos.node()->maxOffset()) {
+        NodeImpl *nextNode = pos.node()->nextLeafNode();
+        if (nextNode)
+            result = Position(nextNode, 0);
+    }
+    else {
+        result = Position(pos.node(), pos.offset() + 1);
+    }
+    
+    return result;
+}
+
+bool CaretPosition::atStart(const Position &pos)
+{
+    if (pos.isEmpty())
+        return true;
+
+    return pos.offset() <= 0 && pos.node()->previousLeafNode() == 0;
+}
+
+bool CaretPosition::atEnd(const Position &pos)
+{
+    if (pos.isEmpty())
+        return true;
+
+    return pos.offset() >= pos.node()->maxOffset() && pos.node()->nextLeafNode() == 0;
+}
+
+bool CaretPosition::isCandidate(const Position &pos)
+{
+    if (pos.isEmpty())
+        return false;
+        
+    RenderObject *renderer = pos.node()->renderer();
+    if (!renderer)
+        return false;
+    
+    if (renderer->style()->visibility() != VISIBLE)
+        return false;
+
+    if (renderer->isBR() && static_cast<RenderText *>(renderer)->firstTextBox()) {
+        return pos.offset() == 0;
+    }
+    else if (renderer->isText()) {
+        RenderText *textRenderer = static_cast<RenderText *>(renderer);
+        for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+            if (pos.offset() >= box->m_start && pos.offset() <= box->m_start + box->m_len) {
+                return true;
+            }
+            else if (pos.offset() < box->m_start) {
+                // The offset we're looking for is before this node
+                // this means the offset must be in content that is
+                // not rendered. Return false.
+                return false;
+            }
+        }
+    }
+    else if (renderer->isReplaced() || (pos.node()->isBlockFlow() && !pos.node()->firstChild() && pos.offset() == 0)) {
+        // return true for replaced elements, for inline flows if they have a line box
+        // and for blocks if they are empty
+        return true;
+    }
+    
+    return false;
+}
+
+Position CaretPosition::deepEquivalent(const Position &pos)
+{
+    if (pos.isEmpty())
+        return pos;
+    
+    if (isAtomicNode(pos.node())) {
+        // This is part of the strategy to wean the code off Positions with BRs and replaced elements
+        // as the nodes and offsets > 0.
+        if (pos.offset() > 0 && (pos.node()->id() == ID_BR || pos.node()->renderer() && pos.node()->renderer()->isReplaced())) {
+            NodeImpl *next = pos.node()->traverseNextNode();
+            if (next && pos.node()->enclosingBlockFlowElement() == next->enclosingBlockFlowElement())
+                return deepEquivalent(Position(next, 0));
+        }
+        return pos;
+    }
+
+    NodeImpl *child = 0;
+    Position result = pos;
+    if (pos.offset() >= (long)pos.node()->childNodeCount()) {
+        child = pos.node()->lastChild();
+        result = Position(child, maxOffset(child));
+        ASSERT(child);
+        while (!isAtomicNode(child) && result.node()->hasChildNodes()) {
+            child = result.node()->lastChild();
+            ASSERT(child);
+            result = Position(child, maxOffset(child));
+        }
+    }
+    else {
+        child = pos.node()->childNode(pos.offset());
+        ASSERT(child);
+        result = Position(child, 0);
+        while (!isAtomicNode(child) && result.node()->hasChildNodes()) {
+            child = result.node()->firstChild();
+            ASSERT(child);
+            result = Position(child, 0);
+        }
+    }
+    return result;
+}
+
+Position CaretPosition::rangeCompliantEquivalent(const Position &pos)
+{
+    if (pos.isEmpty())
+        return Position();
+    
+    if (isAtomicNode(pos.node()))
+        return Position(pos.node()->parentNode(), pos.offset() > 0 ? pos.node()->nodeIndex() + 1 : pos.node()->nodeIndex());
+    else
+        return Position(pos.node(), kMin(pos.offset(), maxOffset(pos.node())));
+}
+
+long CaretPosition::maxOffset(const NodeImpl *node)
+{
+    return offsetInCharacters(node->nodeType()) ? (long)static_cast<const CharacterDataImpl *>(node)->length() : (long)node->childNodeCount();
+}
+
+bool CaretPosition::isAtomicNode(const NodeImpl *node)
+{
+    return node && (!node->hasChildNodes() || (node->id() == ID_OBJECT && node->renderer() && node->renderer()->isReplaced()));
+}
+
+// CaretPosition objects are immutable.
+// This helper is only used during construction and assignment.
+void CaretPosition::setPosition(const Position &pos)
+{
+    if (m_node)
+        m_node->deref();
+    m_node = pos.node();
+    if (m_node)
+        m_node->ref();
+    
+    m_offset = pos.offset();
+}
+
+void CaretPosition::debugPosition(const char *msg) const
+{
+    if (isEmpty())
+        fprintf(stderr, "Position [%s]: empty\n", msg);
+    else
+        fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, getTagName(node()->id()).string().latin1(), node(), offset());
+}
+
+#ifndef NDEBUG
+#define FormatBufferSize 1024
+void CaretPosition::formatForDebugger(char *buffer, unsigned length) const
+{
+    DOMString result;
+    DOMString s;
+    
+    if (isEmpty()) {
+        result = "<empty>";
+    }
+    else {
+        char s[FormatBufferSize];
+        result += "offset ";
+        result += QString::number(m_offset);
+        result += " of ";
+        m_node->formatForDebugger(s, FormatBufferSize);
+        result += s;
+    }
+          
+    strncpy(buffer, result.string().latin1(), length - 1);
+}
+#undef FormatBufferSize
+#endif
+
+}  // namespace DOM
diff --git a/WebCore/khtml/editing/visible_position.h b/WebCore/khtml/editing/visible_position.h
new file mode 100644 (file)
index 0000000..8ed2d95
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef __dom_caretposition_h__
+#define __dom_caretposition_h__
+
+#include "dom_position.h"
+
+namespace DOM {
+
+class NodeImpl;
+class Position;
+
+class CaretPosition
+{
+public:
+    CaretPosition() : m_node(0), m_offset(0) {}
+    CaretPosition(NodeImpl *, long offset);
+    explicit CaretPosition(const Position &);
+    CaretPosition(const CaretPosition &);
+    ~CaretPosition();
+
+    CaretPosition &operator=(const CaretPosition &o);
+
+    bool isLastInBlock() const;
+
+    CaretPosition next() const;
+    CaretPosition previous() const;
+
+    bool isEmpty() const { return m_node == 0; }
+    bool notEmpty() const { return m_node != 0; }
+
+    Position position() const { return rangeCompliantEquivalent(*this); }
+    Position deepEquivalent() const { return Position(node(), offset()); }
+
+    friend inline bool operator==(const CaretPosition &a, const CaretPosition &b);
+    friend inline bool operator!=(const CaretPosition &a, const CaretPosition &b);
+
+    void debugPosition(const char *msg="") const;
+
+#ifndef NDEBUG
+    void formatForDebugger(char *buffer, unsigned length) const;
+#endif
+    
+private:
+    operator Position() const { return Position(node(), offset()); }
+
+    void init(const Position &);
+    void setPosition(const Position &);
+
+    NodeImpl *node() const { return m_node; }
+    long offset() const { return m_offset; }
+
+    static Position deepEquivalent(const Position &);
+    static Position rangeCompliantEquivalent(const Position &);
+
+    static long maxOffset(const NodeImpl *);
+    static bool isAtomicNode(const NodeImpl *);
+    
+    static Position previousCaretPosition(const Position &);
+    static Position nextCaretPosition(const Position &);
+
+    static Position previousPosition(const Position &);
+    static Position nextPosition(const Position &);
+
+    static bool atStart(const Position &);
+    static bool atEnd(const Position &);
+
+    static bool isCandidate(const Position &);
+    
+    NodeImpl *m_node;
+    long m_offset;
+};
+
+inline bool operator==(const CaretPosition &a, const CaretPosition &b)
+{
+    return a.node() == b.node() && a.offset() == b.offset();
+}
+
+inline bool operator!=(const CaretPosition &a, const CaretPosition &b)
+{
+    return !(a == b);
+}
+
+} // namespace DOM
+
+#endif // __dom_caretposition_h__
\ No newline at end of file
index 260b3ca54bc4b53fde9153a08868353a1a979c67..973d0602404e3f8db7d2cf7ddab7c4bd782732d6 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "khtml_text_operations.h"
 
+#include <dom/dom2_range.h>
 #include <misc/htmltags.h>
 #include <rendering/render_text.h>
 #include <xml/dom_nodeimpl.h>
@@ -34,6 +35,7 @@
 using DOM::DOMString;
 using DOM::Node;
 using DOM::NodeImpl;
+using DOM::offsetInCharacters;
 using DOM::Range;
 
 namespace khtml {
@@ -72,33 +74,6 @@ TextIterator::TextIterator() : m_positionNode(0)
 {
 }
 
-inline bool offsetInCharacters(unsigned short type)
-{
-    switch (type) {
-        case Node::ATTRIBUTE_NODE:
-        case Node::DOCUMENT_FRAGMENT_NODE:
-        case Node::DOCUMENT_NODE:
-        case Node::ELEMENT_NODE:
-        case Node::ENTITY_REFERENCE_NODE:
-            return false;
-
-        case Node::CDATA_SECTION_NODE:
-        case Node::COMMENT_NODE:
-        case Node::PROCESSING_INSTRUCTION_NODE:
-        case Node::TEXT_NODE:
-            return true;
-
-        case Node::DOCUMENT_TYPE_NODE:
-        case Node::ENTITY_NODE:
-        case Node::NOTATION_NODE:
-            assert(false); // should never be reached
-            return false;
-    }
-
-    assert(false); // should never be reached
-    return false;
-}
-
 TextIterator::TextIterator(const Range &r)
 {
     if (r.isNull()) {
@@ -483,7 +458,7 @@ SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range &r)
     m_node = endNode;
     m_offset = endOffset;
     m_handledNode = false;
-    m_handledChildren = false;
+    m_handledChildren = endOffset == 0;
 
     m_startNode = startNode;
     m_startOffset = startOffset;
index 39d0646d78ed821470fd172d6241ae368eef6f6f..47226447ffd7ede27fac7275c38ee074521b0003 100644 (file)
@@ -48,6 +48,7 @@
 #include "rendering/render_frames.h"
 #include "misc/htmlhashes.h"
 #include "misc/loader.h"
+#include "xml/dom_caretposition.h"
 #include "xml/dom_selection.h"
 #include "xml/dom2_eventsimpl.h"
 #include "xml/xml_tokenizer.h"
@@ -4409,7 +4410,7 @@ void KHTMLPart::handleMousePressEventDoubleClick(khtml::MousePressEvent *event)
     if (mouse->button() == LeftButton && !innerNode.isNull() && innerNode.handle()->renderer() &&
         innerNode.handle()->renderer()->shouldSelect()) {
         Position pos(innerNode.handle()->positionForCoordinates(event->x(), event->y()));
-        if (pos.node() && (pos.node()->nodeType() == Node::TEXT_NODE || pos.node()->nodeType() == Node::CDATA_SECTION_NODE)) {
+        if (pos.notEmpty()) {
             selection.moveTo(pos);
             selection.expandUsingGranularity(Selection::WORD);
         }
@@ -4434,7 +4435,7 @@ void KHTMLPart::handleMousePressEventTripleClick(khtml::MousePressEvent *event)
     if (mouse->button() == LeftButton && !innerNode.isNull() && innerNode.handle()->renderer() &&
         innerNode.handle()->renderer()->shouldSelect()) {
         Position pos(innerNode.handle()->positionForCoordinates(event->x(), event->y()));
-        if (pos.node() && (pos.node()->nodeType() == Node::TEXT_NODE || pos.node()->nodeType() == Node::CDATA_SECTION_NODE)) {
+        if (pos.notEmpty()) {
             selection.moveTo(pos);
             selection.expandUsingGranularity(Selection::PARAGRAPH);
         }
@@ -4925,8 +4926,9 @@ void KHTMLPart::selectAll()
 {
     if (!d->m_doc)
         return;
-    Selection selection(Position(d->m_doc->documentElement(), 0));
-    selection.validate(Selection::DOCUMENT);
+    CaretPosition start(d->m_doc->documentElement(), 0);
+    CaretPosition end(d->m_doc->documentElement(), d->m_doc->documentElement()->childNodeCount());
+    Selection selection(start.deepEquivalent(), end.deepEquivalent());
     setSelection(selection);
 }
 
index 260b3ca54bc4b53fde9153a08868353a1a979c67..973d0602404e3f8db7d2cf7ddab7c4bd782732d6 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "khtml_text_operations.h"
 
+#include <dom/dom2_range.h>
 #include <misc/htmltags.h>
 #include <rendering/render_text.h>
 #include <xml/dom_nodeimpl.h>
@@ -34,6 +35,7 @@
 using DOM::DOMString;
 using DOM::Node;
 using DOM::NodeImpl;
+using DOM::offsetInCharacters;
 using DOM::Range;
 
 namespace khtml {
@@ -72,33 +74,6 @@ TextIterator::TextIterator() : m_positionNode(0)
 {
 }
 
-inline bool offsetInCharacters(unsigned short type)
-{
-    switch (type) {
-        case Node::ATTRIBUTE_NODE:
-        case Node::DOCUMENT_FRAGMENT_NODE:
-        case Node::DOCUMENT_NODE:
-        case Node::ELEMENT_NODE:
-        case Node::ENTITY_REFERENCE_NODE:
-            return false;
-
-        case Node::CDATA_SECTION_NODE:
-        case Node::COMMENT_NODE:
-        case Node::PROCESSING_INSTRUCTION_NODE:
-        case Node::TEXT_NODE:
-            return true;
-
-        case Node::DOCUMENT_TYPE_NODE:
-        case Node::ENTITY_NODE:
-        case Node::NOTATION_NODE:
-            assert(false); // should never be reached
-            return false;
-    }
-
-    assert(false); // should never be reached
-    return false;
-}
-
 TextIterator::TextIterator(const Range &r)
 {
     if (r.isNull()) {
@@ -483,7 +458,7 @@ SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range &r)
     m_node = endNode;
     m_offset = endOffset;
     m_handledNode = false;
-    m_handledChildren = false;
+    m_handledChildren = endOffset == 0;
 
     m_startNode = startNode;
     m_startOffset = startOffset;
diff --git a/WebCore/khtml/xml/dom_caretposition.cpp b/WebCore/khtml/xml/dom_caretposition.cpp
new file mode 100644 (file)
index 0000000..6a82f79
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "dom_caretposition.h"
+
+#include "dom2_range.h"
+#include "dom_elementimpl.h"
+#include "dom_nodeimpl.h"
+#include "dom_string.h"
+#include "htmltags.h"
+#include "render_object.h"
+#include "render_text.h"
+
+#if APPLE_CHANGES
+#include "KWQAssertions.h"
+#include "KWQLogging.h"
+#else
+#define ASSERT(assertion) assert(assertion)
+#define LOG(channel, formatAndArgs...) ((void)0)
+#endif
+
+using khtml::InlineTextBox;
+using khtml::RenderObject;
+using khtml::RenderText;
+using khtml::VISIBLE;
+
+namespace DOM {
+
+CaretPosition::CaretPosition(NodeImpl *node, long offset)
+    : m_node(0)
+{
+    init(Position(node, offset));
+}
+
+CaretPosition::CaretPosition(const Position &pos)
+    : m_node(0)
+{
+    init(pos);
+}
+
+CaretPosition::CaretPosition(const CaretPosition &pos)
+    : m_node(0)
+{
+    setPosition(pos);
+}
+
+CaretPosition::~CaretPosition()
+{
+    if (m_node)
+        m_node->deref();
+}
+
+void CaretPosition::init(const Position &pos)
+{
+    Position result;
+    Position deepPos = deepEquivalent(pos);
+    
+    if (isCandidate(deepPos)) {
+        result = deepPos;
+        Position previous = previousCaretPosition(deepPos);
+        if (previous.notEmpty()) {
+            Position next = nextCaretPosition(previous);
+            if (next.notEmpty())
+                result = next;
+        }
+    }
+    else {
+        Position next = nextCaretPosition(deepPos);
+        if (next.notEmpty()) {
+            result = next;
+        }
+        else {
+            Position previous = previousCaretPosition(deepPos);
+            if (previous.notEmpty())
+                result = previous;
+        }
+    }
+    
+    setPosition(result);
+}
+
+CaretPosition &CaretPosition::operator=(const CaretPosition &pos)
+{
+    setPosition(pos);
+    return *this;
+}
+
+bool CaretPosition::isLastInBlock() const
+{
+    if (isEmpty())
+        return false;
+        
+    CaretPosition n = next();
+    return n.isEmpty() || (n.deepEquivalent().node()->enclosingBlockFlowElement() != node()->enclosingBlockFlowElement());
+}
+
+CaretPosition CaretPosition::next() const
+{
+    CaretPosition result;
+    result.setPosition(nextCaretPosition(*this));
+    return result;
+}
+
+CaretPosition CaretPosition::previous() const
+{
+    CaretPosition result;
+    result.setPosition(previousCaretPosition(*this));
+    return result;
+}
+
+Position CaretPosition::previousCaretPosition(const Position &pos)
+{
+    if (pos.isEmpty() || atStart(pos))
+        return Position();
+
+    Position test = deepEquivalent(pos);
+    bool acceptAnyCaretPosition = !isCandidate(test) || pos.isFirstRenderedPositionOnLine();
+
+    Position current = test;
+    while (!atStart(current)) {
+        current = previousPosition(current);
+        if (isCandidate(current) && (acceptAnyCaretPosition || current.rendersInDifferentPosition(test))) {
+            return current;
+        }
+    }
+    
+    return Position();
+}
+
+Position CaretPosition::nextCaretPosition(const Position &pos)
+{
+    if (pos.isEmpty() || atEnd(pos))
+        return Position();
+
+    Position test = deepEquivalent(pos);
+    bool acceptAnyCaretPosition = !isCandidate(test) || pos.isLastRenderedPositionOnLine();
+
+    Position current = test;
+    while (!atEnd(current)) {
+        current = nextPosition(current);
+        if (isCandidate(current) && (acceptAnyCaretPosition || current.rendersInDifferentPosition(test))) {
+            return current;
+        }
+    }
+    
+    return Position();
+}
+
+Position CaretPosition::previousPosition(const Position &pos)
+{
+    if (pos.isEmpty())
+        return pos;
+    
+    Position result;
+
+    if (pos.offset() <= 0) {
+        NodeImpl *prevNode = pos.node()->previousLeafNode();
+        if (prevNode)
+            result = Position(prevNode, prevNode->maxOffset());
+    }
+    else {
+        result = Position(pos.node(), pos.offset() - 1);
+    }
+    
+    return result;
+}
+
+Position CaretPosition::nextPosition(const Position &pos)
+{
+    if (pos.isEmpty())
+        return pos;
+    
+    Position result;
+
+    if (pos.offset() >= pos.node()->maxOffset()) {
+        NodeImpl *nextNode = pos.node()->nextLeafNode();
+        if (nextNode)
+            result = Position(nextNode, 0);
+    }
+    else {
+        result = Position(pos.node(), pos.offset() + 1);
+    }
+    
+    return result;
+}
+
+bool CaretPosition::atStart(const Position &pos)
+{
+    if (pos.isEmpty())
+        return true;
+
+    return pos.offset() <= 0 && pos.node()->previousLeafNode() == 0;
+}
+
+bool CaretPosition::atEnd(const Position &pos)
+{
+    if (pos.isEmpty())
+        return true;
+
+    return pos.offset() >= pos.node()->maxOffset() && pos.node()->nextLeafNode() == 0;
+}
+
+bool CaretPosition::isCandidate(const Position &pos)
+{
+    if (pos.isEmpty())
+        return false;
+        
+    RenderObject *renderer = pos.node()->renderer();
+    if (!renderer)
+        return false;
+    
+    if (renderer->style()->visibility() != VISIBLE)
+        return false;
+
+    if (renderer->isBR() && static_cast<RenderText *>(renderer)->firstTextBox()) {
+        return pos.offset() == 0;
+    }
+    else if (renderer->isText()) {
+        RenderText *textRenderer = static_cast<RenderText *>(renderer);
+        for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+            if (pos.offset() >= box->m_start && pos.offset() <= box->m_start + box->m_len) {
+                return true;
+            }
+            else if (pos.offset() < box->m_start) {
+                // The offset we're looking for is before this node
+                // this means the offset must be in content that is
+                // not rendered. Return false.
+                return false;
+            }
+        }
+    }
+    else if (renderer->isReplaced() || (pos.node()->isBlockFlow() && !pos.node()->firstChild() && pos.offset() == 0)) {
+        // return true for replaced elements, for inline flows if they have a line box
+        // and for blocks if they are empty
+        return true;
+    }
+    
+    return false;
+}
+
+Position CaretPosition::deepEquivalent(const Position &pos)
+{
+    if (pos.isEmpty())
+        return pos;
+    
+    if (isAtomicNode(pos.node())) {
+        // This is part of the strategy to wean the code off Positions with BRs and replaced elements
+        // as the nodes and offsets > 0.
+        if (pos.offset() > 0 && (pos.node()->id() == ID_BR || pos.node()->renderer() && pos.node()->renderer()->isReplaced())) {
+            NodeImpl *next = pos.node()->traverseNextNode();
+            if (next && pos.node()->enclosingBlockFlowElement() == next->enclosingBlockFlowElement())
+                return deepEquivalent(Position(next, 0));
+        }
+        return pos;
+    }
+
+    NodeImpl *child = 0;
+    Position result = pos;
+    if (pos.offset() >= (long)pos.node()->childNodeCount()) {
+        child = pos.node()->lastChild();
+        result = Position(child, maxOffset(child));
+        ASSERT(child);
+        while (!isAtomicNode(child) && result.node()->hasChildNodes()) {
+            child = result.node()->lastChild();
+            ASSERT(child);
+            result = Position(child, maxOffset(child));
+        }
+    }
+    else {
+        child = pos.node()->childNode(pos.offset());
+        ASSERT(child);
+        result = Position(child, 0);
+        while (!isAtomicNode(child) && result.node()->hasChildNodes()) {
+            child = result.node()->firstChild();
+            ASSERT(child);
+            result = Position(child, 0);
+        }
+    }
+    return result;
+}
+
+Position CaretPosition::rangeCompliantEquivalent(const Position &pos)
+{
+    if (pos.isEmpty())
+        return Position();
+    
+    if (isAtomicNode(pos.node()))
+        return Position(pos.node()->parentNode(), pos.offset() > 0 ? pos.node()->nodeIndex() + 1 : pos.node()->nodeIndex());
+    else
+        return Position(pos.node(), kMin(pos.offset(), maxOffset(pos.node())));
+}
+
+long CaretPosition::maxOffset(const NodeImpl *node)
+{
+    return offsetInCharacters(node->nodeType()) ? (long)static_cast<const CharacterDataImpl *>(node)->length() : (long)node->childNodeCount();
+}
+
+bool CaretPosition::isAtomicNode(const NodeImpl *node)
+{
+    return node && (!node->hasChildNodes() || (node->id() == ID_OBJECT && node->renderer() && node->renderer()->isReplaced()));
+}
+
+// CaretPosition objects are immutable.
+// This helper is only used during construction and assignment.
+void CaretPosition::setPosition(const Position &pos)
+{
+    if (m_node)
+        m_node->deref();
+    m_node = pos.node();
+    if (m_node)
+        m_node->ref();
+    
+    m_offset = pos.offset();
+}
+
+void CaretPosition::debugPosition(const char *msg) const
+{
+    if (isEmpty())
+        fprintf(stderr, "Position [%s]: empty\n", msg);
+    else
+        fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, getTagName(node()->id()).string().latin1(), node(), offset());
+}
+
+#ifndef NDEBUG
+#define FormatBufferSize 1024
+void CaretPosition::formatForDebugger(char *buffer, unsigned length) const
+{
+    DOMString result;
+    DOMString s;
+    
+    if (isEmpty()) {
+        result = "<empty>";
+    }
+    else {
+        char s[FormatBufferSize];
+        result += "offset ";
+        result += QString::number(m_offset);
+        result += " of ";
+        m_node->formatForDebugger(s, FormatBufferSize);
+        result += s;
+    }
+          
+    strncpy(buffer, result.string().latin1(), length - 1);
+}
+#undef FormatBufferSize
+#endif
+
+}  // namespace DOM
diff --git a/WebCore/khtml/xml/dom_caretposition.h b/WebCore/khtml/xml/dom_caretposition.h
new file mode 100644 (file)
index 0000000..8ed2d95
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef __dom_caretposition_h__
+#define __dom_caretposition_h__
+
+#include "dom_position.h"
+
+namespace DOM {
+
+class NodeImpl;
+class Position;
+
+class CaretPosition
+{
+public:
+    CaretPosition() : m_node(0), m_offset(0) {}
+    CaretPosition(NodeImpl *, long offset);
+    explicit CaretPosition(const Position &);
+    CaretPosition(const CaretPosition &);
+    ~CaretPosition();
+
+    CaretPosition &operator=(const CaretPosition &o);
+
+    bool isLastInBlock() const;
+
+    CaretPosition next() const;
+    CaretPosition previous() const;
+
+    bool isEmpty() const { return m_node == 0; }
+    bool notEmpty() const { return m_node != 0; }
+
+    Position position() const { return rangeCompliantEquivalent(*this); }
+    Position deepEquivalent() const { return Position(node(), offset()); }
+
+    friend inline bool operator==(const CaretPosition &a, const CaretPosition &b);
+    friend inline bool operator!=(const CaretPosition &a, const CaretPosition &b);
+
+    void debugPosition(const char *msg="") const;
+
+#ifndef NDEBUG
+    void formatForDebugger(char *buffer, unsigned length) const;
+#endif
+    
+private:
+    operator Position() const { return Position(node(), offset()); }
+
+    void init(const Position &);
+    void setPosition(const Position &);
+
+    NodeImpl *node() const { return m_node; }
+    long offset() const { return m_offset; }
+
+    static Position deepEquivalent(const Position &);
+    static Position rangeCompliantEquivalent(const Position &);
+
+    static long maxOffset(const NodeImpl *);
+    static bool isAtomicNode(const NodeImpl *);
+    
+    static Position previousCaretPosition(const Position &);
+    static Position nextCaretPosition(const Position &);
+
+    static Position previousPosition(const Position &);
+    static Position nextPosition(const Position &);
+
+    static bool atStart(const Position &);
+    static bool atEnd(const Position &);
+
+    static bool isCandidate(const Position &);
+    
+    NodeImpl *m_node;
+    long m_offset;
+};
+
+inline bool operator==(const CaretPosition &a, const CaretPosition &b)
+{
+    return a.node() == b.node() && a.offset() == b.offset();
+}
+
+inline bool operator!=(const CaretPosition &a, const CaretPosition &b)
+{
+    return !(a == b);
+}
+
+} // namespace DOM
+
+#endif // __dom_caretposition_h__
\ No newline at end of file
index fac1b41c9a17eaa4489335bc2e058f82603854ad..90c8a934c8a761ac23ae405d1d5b7905b8e2ca48 100644 (file)
@@ -900,7 +900,7 @@ NodeImpl *NodeImpl::traverseNextNode(NodeImpl *stayWithin) const
     const NodeImpl *n = this;
     while (n && !n->nextSibling() && (!stayWithin || n->parentNode() != stayWithin))
         n = n->parentNode();
-    if (n && (!stayWithin || n->parentNode() != stayWithin))
+    if (n)
         return n->nextSibling();
     return 0;
 }
@@ -912,7 +912,7 @@ NodeImpl *NodeImpl::traverseNextSibling(NodeImpl *stayWithin) const
     const NodeImpl *n = this;
     while (n && !n->nextSibling() && (!stayWithin || n->parentNode() != stayWithin))
         n = n->parentNode();
-    if (n && (!stayWithin || n->parentNode() != stayWithin))
+    if (n)
         return n->nextSibling();
     return 0;
 }
@@ -942,7 +942,7 @@ NodeImpl *NodeImpl::traversePreviousNodePostOrder(NodeImpl *stayWithin) const
     const NodeImpl *n = this;
     while (n && !n->previousSibling() && (!stayWithin || n->parentNode() != stayWithin))
         n = n->parentNode();
-    if (n && (!stayWithin || n->parentNode() != stayWithin))
+    if (n)
         return n->previousSibling();
     return 0;
 }
index b65662268d4b7a7a862445899e6e24386f967a14..39577709eb8fc0477962264fcf6416cc03e7d469 100644 (file)
@@ -111,7 +111,7 @@ Position::Position(NodeImpl *node, long offset)
     if (node) {
         node->ref();
     }
-};
+}
 
 Position::Position(const Position &o)
     : m_node(o.m_node), m_offset(o.m_offset) 
@@ -716,7 +716,7 @@ Position Position::upstream(EStayInBlock stayInBlock) const
 
     NodeImpl *block = node()->isBlockFlow() ? node() : node()->enclosingBlockFlowElement();
     
-    PositionIterator it(*this);            
+    PositionIterator it(equivalentDeepPosition());            
     for (; !it.atStart(); it.previous()) {
         if (stayInBlock) {
             NodeImpl *currentBlock = it.current().node()->isBlockFlow() ? it.current().node() : it.current().node()->enclosingBlockFlowElement();
@@ -768,7 +768,7 @@ Position Position::downstream(EStayInBlock stayInBlock) const
 
     NodeImpl *block = node()->isBlockFlow() ? node() : node()->enclosingBlockFlowElement();
     
-    PositionIterator it(*this);            
+    PositionIterator it(equivalentDeepPosition());            
     for (; !it.atEnd(); it.next()) {   
         if (stayInBlock) {
             NodeImpl *currentBlock = it.current().node()->isBlockFlow() ? it.current().node() : it.current().node()->enclosingBlockFlowElement();
@@ -923,16 +923,6 @@ Position Position::closestRenderedPosition(EAffinity affinity) const
     return Position();
 }
 
-bool Position::atStartOfContainingEditableBlock() const
-{
-    return renderedOffset() == 0 && inFirstEditableInContainingEditableBlock();
-}
-
-bool Position::atStartOfRootEditableElement() const
-{
-    return renderedOffset() == 0 && inFirstEditableInRootEditableElement();
-}
-
 bool Position::inRenderedContent() const
 {
     if (isEmpty())
@@ -1168,36 +1158,6 @@ bool Position::isLastRenderedPositionOnLine() const
     return true;
 }
 
-bool Position::isLastRenderedPositionInEditableBlock() const
-{
-    if (isEmpty())
-        return false;
-
-    RenderObject *renderer = node()->renderer();
-    if (!renderer)
-        return false;
-
-    if (renderer->style()->visibility() != VISIBLE)
-        return false;
-
-    if (renderedOffset() != (long)node()->caretMaxRenderedOffset())
-        return false;
-
-    PositionIterator it(*this);
-    while (!it.atEnd()) {
-        it.next();
-        if (!it.current().node()->inSameContainingBlockFlowElement(node()))
-            return true;
-        RenderObject *currentRenderer = it.current().node()->renderer();
-        if (!currentRenderer || currentRenderer->firstChild())
-            // we want a leaf for this check
-            continue;
-        if (it.current().inRenderedContent())
-            return false;
-    }
-    return true;
-}
-
 bool Position::inFirstEditableInRootEditableElement() const
 {
     if (isEmpty() || !inRenderedContent())
@@ -1217,69 +1177,6 @@ bool Position::inFirstEditableInRootEditableElement() const
     return true;
 }
 
-bool Position::inLastEditableInRootEditableElement() const
-{
-    if (isEmpty() || !inRenderedContent())
-        return false;
-
-    PositionIterator it(*this);
-    while (!it.atEnd()) {
-        it.next();
-        RenderObject *currentRenderer = it.current().node()->renderer();
-        if (!currentRenderer || currentRenderer->firstChild())
-            // we want a leaf for this check
-            continue;
-        if (it.current().inRenderedContent())
-            return false;
-    }
-
-    return true;
-}
-
-bool Position::inFirstEditableInContainingEditableBlock() const
-{
-    if (isEmpty() || !inRenderedContent())
-        return false;
-    
-    NodeImpl *block = node()->enclosingBlockFlowElement();
-
-    PositionIterator it(*this);
-    while (!it.atStart()) {
-        it.previous();
-        RenderObject *currentRenderer = it.current().node()->renderer();
-        if (!currentRenderer || currentRenderer->firstChild())
-            // we want a leaf for this check
-            continue;
-        if (!it.current().inRenderedContent())
-            continue;
-        return block != it.current().node()->enclosingBlockFlowElement();
-    }
-
-    return true;
-}
-
-bool Position::inLastEditableInContainingEditableBlock() const
-{
-    if (isEmpty() || !inRenderedContent())
-        return false;
-    
-    NodeImpl *block = node()->enclosingBlockFlowElement();
-
-    PositionIterator it(*this);
-    while (!it.atEnd()) {
-        it.next();
-        RenderObject *currentRenderer = it.current().node()->renderer();
-        if (!currentRenderer || currentRenderer->firstChild())
-            // we want a leaf for this check
-            continue;
-        if (!it.current().inRenderedContent())
-            continue;
-        return block != it.current().node()->enclosingBlockFlowElement();
-    }
-
-    return true;
-}
-
 static inline bool isWS(const QChar &c)
 {
     const char nonBreakingSpace = 0xA0;
index ca8a5987864a5c4d3ddac1c06ab2f2b203337509..578062f7897f468d508db0791354825396e1a0fc 100644 (file)
@@ -103,19 +103,13 @@ public:
     Position equivalentShallowPosition() const;
     Position equivalentDeepPosition() const;
     Position closestRenderedPosition(EAffinity) const;
-    bool atStartOfContainingEditableBlock() const;
-    bool atStartOfRootEditableElement() const;
     bool inRenderedContent() const;
     bool inRenderedText() const;
     bool isRenderedCharacter() const;
     bool rendersInDifferentPosition(const Position &pos) const;
     bool isFirstRenderedPositionOnLine() const;
     bool isLastRenderedPositionOnLine() const;
-    bool isLastRenderedPositionInEditableBlock() const;
     bool inFirstEditableInRootEditableElement() const;
-    bool inLastEditableInRootEditableElement() const;
-    bool inFirstEditableInContainingEditableBlock() const;
-    bool inLastEditableInContainingEditableBlock() const;
     
     Position &operator=(const Position &o);
     
index 2552884316cce8585a4c5e24a7529ad94b1d1cb8..52b1d42a3c90c7cc2c5a06fe4e48abca3d132aa2 100644 (file)
   
 #include "dom_selection.h"
 
+#include "dom_caretposition.h"
+#include "dom_docimpl.h"
+#include "dom_elementimpl.h"
+#include "dom_node.h"
+#include "dom_nodeimpl.h"
+#include "dom_positioniterator.h"
+#include "dom_string.h"
+#include "dom_textimpl.h"
+#include "dom2_range.h"
 #include "htmltags.h"
 #include "khtml_part.h"
 #include "khtmlview.h"
 #include "qevent.h"
 #include "qpainter.h"
 #include "qrect.h"
-#include "dom/dom2_range.h"
-#include "dom/dom_node.h"
-#include "dom/dom_string.h"
-#include "rendering/render_object.h"
-#include "rendering/render_style.h"
-#include "rendering/render_text.h"
-#include "xml/dom_docimpl.h"
-#include "xml/dom_positioniterator.h"
-#include "xml/dom_elementimpl.h"
-#include "xml/dom_nodeimpl.h"
-#include "xml/dom_textimpl.h"
+#include "render_object.h"
+#include "render_style.h"
+#include "render_text.h"
 
 #if APPLE_CHANGES
 #include "KWQAssertions.h"
@@ -204,7 +205,7 @@ Position Selection::modifyExtendingRightForward(ETextGranularity granularity)
             pos = pos.nextCharacterPosition();
             break;
         case WORD:
-            pos = pos.nextWordPosition();
+            pos = CaretPosition(pos).next().deepEquivalent();
             break;
         case PARAGRAPH:
             // "Next paragraph" not implemented yet. Fall through to LINE.
@@ -235,7 +236,7 @@ Position Selection::modifyMovingRightForward(ETextGranularity granularity)
             if (state() == RANGE) 
                 pos = end();
             else
-                pos = extent().nextCharacterPosition();
+                pos = CaretPosition(extent()).next().deepEquivalent();
             break;
         case WORD:
             pos = extent().nextWordPosition();
@@ -269,7 +270,7 @@ Position Selection::modifyExtendingLeftBackward(ETextGranularity granularity)
     Position pos = extent();
     switch (granularity) {
         case CHARACTER:
-            pos = pos.previousCharacterPosition();
+            pos = CaretPosition(pos).previous().deepEquivalent();
             break;
         case WORD:
             pos = pos.previousWordPosition();
@@ -301,7 +302,7 @@ Position Selection::modifyMovingLeftBackward(ETextGranularity granularity)
             if (state() == RANGE) 
                 pos = start();
             else
-                pos = extent().previousCharacterPosition();
+                pos = CaretPosition(extent()).previous().deepEquivalent();
             break;
         case WORD:
             pos = extent().previousWordPosition();
@@ -698,26 +699,26 @@ void Selection::validate(ETextGranularity granularity)
         }
         case PARAGRAPH:
             if (m_baseIsStart) {
-                assignStart(base().startParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-                assignEnd(extent().endParagraphBoundary(IncludeLineBreak).equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+                assignStart(CaretPosition(base().startParagraphBoundary()).deepEquivalent());
+                assignEnd(CaretPosition(extent().endParagraphBoundary(IncludeLineBreak)).deepEquivalent());
             } else {
-                assignStart(extent().startParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-                assignEnd(base().endParagraphBoundary(IncludeLineBreak).equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+                assignStart(CaretPosition(extent().startParagraphBoundary()).deepEquivalent());
+                assignEnd(CaretPosition(base().endParagraphBoundary(IncludeLineBreak)).deepEquivalent());
             }
             break;
         case DOCUMENT: {
             NodeImpl *de = start().node()->getDocument()->documentElement();
-            assignStart(Position(de, 0).equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-            assignEnd(Position(de, de->childNodeCount()).equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+            assignStart(CaretPosition(de, 0).deepEquivalent());
+            assignEnd(CaretPosition(de, de->childNodeCount()).deepEquivalent());
             break;
         }
         case PARAGRAPH_BOUNDARY:
             if (m_baseIsStart) {
-                assignStart(base().startParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-                assignEnd(extent().endParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+                assignStart(CaretPosition(base().startParagraphBoundary()).deepEquivalent());
+                assignEnd(CaretPosition(extent().endParagraphBoundary(IncludeLineBreak)).deepEquivalent());
             } else {
-                assignStart(extent().startParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(DOWNSTREAM));
-                assignEnd(base().endParagraphBoundary().equivalentDeepPosition().closestRenderedPosition(UPSTREAM));
+                assignStart(CaretPosition(extent().startParagraphBoundary()).deepEquivalent());
+                assignEnd(CaretPosition(base().endParagraphBoundary(IncludeLineBreak)).deepEquivalent());
             }
             break;
     }