LayoutTests:
authorjusting <justing@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 8 Jul 2006 02:32:13 +0000 (02:32 +0000)
committerjusting <justing@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 8 Jul 2006 02:32:13 +0000 (02:32 +0000)
        Reviewed by levi

        * editing/selection/editable-non-editable-crash-expected.checksum: Added.
        * editing/selection/editable-non-editable-crash-expected.png: Added.
        * editing/selection/editable-non-editable-crash-expected.txt: Added.
        * editing/selection/editable-non-editable-crash.html: Added.
        * editing/selection/skip-non-editable-1-expected.txt:
        * editing/selection/skip-non-editable-1.html:

WebCore:

        Reviewed by levi

        Fix Mail ToDo crashers.

        * dom/Range.cpp:
        (WebCore::Range::compareBoundaryPoints): Added an ASSERT that both
        containers are non-null and an early return.
        * editing/Selection.cpp:
        (WebCore::Selection::validate): Fix a dangling start/end.
        (WebCore::Selection::adjustForEditableContent): Added an early return if
        m_start or m_end are null.
        (WebCore::Selection::isContentEditable): Use isRichlyEditablePosition.
        (WebCore::Selection::isContentRichlyEditable): Ditto.
        * editing/Selection.h:
        * editing/VisiblePosition.cpp:
        (WebCore::VisiblePosition::next): Use the new highestEditableRoot.
        (WebCore::VisiblePosition::previous): Ditto.
        * editing/htmlediting.cpp:
        (WebCore::highestEditableRoot): Takes in a position.
        (WebCore::isEditablePosition): Added.
        (WebCore::isRichlyEditablePosition): Added.
        (WebCore::rootEditableElement): Added.
        (WebCore::nextCandidate): Moved and split out from nextVisiblePosition.
        (WebCore::nextVisuallyDistinctCandidate): Ditto.
        (WebCore::previousCandidate): Moved and split out from previousVisiblePosition.
        (WebCore::previousVisuallyDistinctCandidate): Ditto.
        (WebCore::firstEditablePositionAfterPositionInRoot): Iterate over positions,
        using nextVisuallyDistinctCandidate, skipping atomic nodes that are non-editable.
        (WebCore::lastEditablePositionBeforePositionInRoot): Ditto.
        * editing/htmlediting.h:
        * editing/visible_units.cpp:
        (WebCore::startOfWord): Added a FIXME.

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

16 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/selection/editable-non-editable-crash-expected.checksum [new file with mode: 0644]
LayoutTests/editing/selection/editable-non-editable-crash-expected.png [new file with mode: 0644]
LayoutTests/editing/selection/editable-non-editable-crash-expected.txt [new file with mode: 0644]
LayoutTests/editing/selection/editable-non-editable-crash.html [new file with mode: 0644]
LayoutTests/editing/selection/skip-non-editable-1-expected.txt
LayoutTests/editing/selection/skip-non-editable-1.html
WebCore/ChangeLog
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/dom/Range.cpp
WebCore/editing/Selection.cpp
WebCore/editing/Selection.h
WebCore/editing/VisiblePosition.cpp
WebCore/editing/htmlediting.cpp
WebCore/editing/htmlediting.h
WebCore/editing/visible_units.cpp

index 70c86de96f672d057ed3a2683d8bc4cd8f9e3a13..3bdafcdd2f6fe17b37f485352802afbeb8d07d46 100644 (file)
@@ -1,3 +1,14 @@
+2006-07-07  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by levi
+
+        * editing/selection/editable-non-editable-crash-expected.checksum: Added.
+        * editing/selection/editable-non-editable-crash-expected.png: Added.
+        * editing/selection/editable-non-editable-crash-expected.txt: Added.
+        * editing/selection/editable-non-editable-crash.html: Added.
+        * editing/selection/skip-non-editable-1-expected.txt:
+        * editing/selection/skip-non-editable-1.html:
+
 2006-07-07  Levi Weintraub  <lweintraub@apple.com>
 
         Reviewed by NOBODY
diff --git a/LayoutTests/editing/selection/editable-non-editable-crash-expected.checksum b/LayoutTests/editing/selection/editable-non-editable-crash-expected.checksum
new file mode 100644 (file)
index 0000000..0e7a372
--- /dev/null
@@ -0,0 +1 @@
+ebd6d307301e73acdbf6f85451b4e575
\ No newline at end of file
diff --git a/LayoutTests/editing/selection/editable-non-editable-crash-expected.png b/LayoutTests/editing/selection/editable-non-editable-crash-expected.png
new file mode 100644 (file)
index 0000000..b2be6e0
Binary files /dev/null and b/LayoutTests/editing/selection/editable-non-editable-crash-expected.png differ
diff --git a/LayoutTests/editing/selection/editable-non-editable-crash-expected.txt b/LayoutTests/editing/selection/editable-non-editable-crash-expected.txt
new file mode 100644 (file)
index 0000000..3f508b8
--- /dev/null
@@ -0,0 +1,17 @@
+EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of DIV > BODY > HTML > #document to 1 of DIV > BODY > HTML > #document
+EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+layer at (0,0) size 800x600
+  RenderView 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 784x46
+        RenderTable {TABLE} at (0,0) size 784x46 [border: (1px solid #AAAAAA)]
+          RenderTableSection {TBODY} at (1,1) size 782x44
+            RenderTableRow {TR} at (0,2) size 782x40
+              RenderTableCell {TD} at (2,2) size 778x40 [border: (1px solid #AAAAAA)] [r=0 c=0 rs=1 cs=1]
+                RenderText {#text} at (2,2) size 773x36
+                  text run at (2,2) width 773: "This tests for a Mail crasher that happened when a selection was created with one endpoint in non-editable content and the "
+                  text run at (2,20) width 155: "other in editable content."
+caret: position 0 of child 0 {TABLE} of child 1 {DIV} of child 1 {BODY} of child 0 {HTML} of document
diff --git a/LayoutTests/editing/selection/editable-non-editable-crash.html b/LayoutTests/editing/selection/editable-non-editable-crash.html
new file mode 100644 (file)
index 0000000..6fcf1e5
--- /dev/null
@@ -0,0 +1,15 @@
+<head><style>
+table, td {
+    border: 1px solid #aaa;
+}
+</style></head>
+<body>
+<div contenteditable="true"><table style="border: 1px solid #aaa" id="base"><tr><td id="extent" contenteditable="false">This tests for a Mail crasher that happened when a selection was created with one endpoint in non-editable content and the other in editable content.</td></tr></table></div>
+
+<script>
+var s = window.getSelection();
+var b = document.getElementById("base");
+var e = document.getElementById("extent");
+s.setBaseAndExtent(b, 0, e, 0);
+</script>
+</body>
\ No newline at end of file
index 9087b1bc1ef9f5c70388c9c544760887bffad1f2..591c2133d31c4969fb9a7ac1aea893ee0469bd3e 100644 (file)
@@ -1,11 +1,13 @@
 EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of BODY > HTML > #document to 13 of BODY > HTML > #document
 EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: shouldEndEditingInDOMRange:range from 0 of BODY > HTML > #document to 13 of BODY > HTML > #document
 EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification
 EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of TD > TR > TBODY > TABLE > BODY > HTML > #document to 1 of TD > TR > TBODY > TABLE > BODY > HTML > #document
 EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: shouldEndEditingInDOMRange:range from 0 of TD > TR > TBODY > TABLE > BODY > HTML > #document to 1 of TD > TR > TBODY > TABLE > BODY > HTML > #document
 EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification
 EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of BODY > HTML > #document to 13 of BODY > HTML > #document
@@ -16,11 +18,13 @@ EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification
 EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of TD > TR > TBODY > TABLE > BODY > HTML > #document to 1 of TD > TR > TBODY > TABLE > BODY > HTML > #document
 EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: shouldEndEditingInDOMRange:range from 0 of TD > TR > TBODY > TABLE > BODY > HTML > #document to 1 of TD > TR > TBODY > TABLE > BODY > HTML > #document
 EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification
 EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of BODY > HTML > #document to 13 of BODY > HTML > #document
 EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: shouldEndEditingInDOMRange:range from 0 of BODY > HTML > #document to 13 of BODY > HTML > #document
 EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification
 EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of TD > TR > TBODY > TABLE > BODY > HTML > #document to 1 of TD > TR > TBODY > TABLE > BODY > HTML > #document
index dd0cb1cc8ebda310f9e260562307719bffeb3ff6..0f315cfa72aa16fe3e6bceeeeeb2bd67d0a963be 100644 (file)
@@ -47,15 +47,19 @@ var e3 = document.getElementById("e3");
 
 s.setPosition(e1.firstChild, e1.firstChild.length);
 s.modify("move", "forward", "character");
+s.modify("move", "forward", "character");
 assert(s.anchorNode == e2.firstChild && s.anchorOffset == 0);
 
+s.modify("move", "backward", "character");
 s.modify("move", "backward", "character");
 assert(s.anchorNode == e1.firstChild && s.anchorOffset == e1.firstChild.length);
     
 s.setPosition(e2.firstChild, e2.firstChild.length);
 s.modify("move", "forward", "character");
+s.modify("move", "forward", "character");
 assert(s.anchorNode == e3.firstChild && s.anchorOffset == 0);
 
+s.modify("move", "backward", "character");
 s.modify("move", "backward", "character");
 assert(s.anchorNode == e2.firstChild && s.anchorOffset == e2.firstChild.length)
 </script>
\ No newline at end of file
index 9cb935e1e3e238b6aff10d8a98fda7b9e2d754fc..e96c97ba567a3229c2db846186b6c703f0ff37c1 100644 (file)
@@ -1,3 +1,38 @@
+2006-07-07  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by levi
+        
+        Fix Mail ToDo crashers.
+
+        * dom/Range.cpp:
+        (WebCore::Range::compareBoundaryPoints): Added an ASSERT that both
+        containers are non-null and an early return.
+        * editing/Selection.cpp:
+        (WebCore::Selection::validate): Fix a dangling start/end.
+        (WebCore::Selection::adjustForEditableContent): Added an early return if
+        m_start or m_end are null.
+        (WebCore::Selection::isContentEditable): Use isRichlyEditablePosition.
+        (WebCore::Selection::isContentRichlyEditable): Ditto.
+        * editing/Selection.h:
+        * editing/VisiblePosition.cpp:
+        (WebCore::VisiblePosition::next): Use the new highestEditableRoot.
+        (WebCore::VisiblePosition::previous): Ditto.
+        * editing/htmlediting.cpp:
+        (WebCore::highestEditableRoot): Takes in a position.
+        (WebCore::isEditablePosition): Added.
+        (WebCore::isRichlyEditablePosition): Added.
+        (WebCore::rootEditableElement): Added.
+        (WebCore::nextCandidate): Moved and split out from nextVisiblePosition.
+        (WebCore::nextVisuallyDistinctCandidate): Ditto.
+        (WebCore::previousCandidate): Moved and split out from previousVisiblePosition.
+        (WebCore::previousVisuallyDistinctCandidate): Ditto.
+        (WebCore::firstEditablePositionAfterPositionInRoot): Iterate over positions,
+        using nextVisuallyDistinctCandidate, skipping atomic nodes that are non-editable.
+        (WebCore::lastEditablePositionBeforePositionInRoot): Ditto.
+        * editing/htmlediting.h:
+        * editing/visible_units.cpp:
+        (WebCore::startOfWord): Added a FIXME.
+
 2006-07-07  Levi Weintraub  <lweintraub@apple.com>
 
         Reviewed by justin
index a8951afe403567c0f2292723ae9bd40a9093dcf0..ec3794a10afc0783217fde2c617aac345a6d0b5d 100644 (file)
                FAE04190097596C9000540BE /* SVGImageLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE0418E097596C9000540BE /* SVGImageLoader.h */; };
 /* End PBXBuildFile section */
 
+/* Begin PBXBuildStyle section */
+               D041FC250A5E04A700841F7F /* Development */ = {
+                       isa = PBXBuildStyle;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = NO;
+                       };
+                       name = Development;
+               };
+               D041FC260A5E04A700841F7F /* Deployment */ = {
+                       isa = PBXBuildStyle;
+                       buildSettings = {
+                               COPY_PHASE_STRIP = YES;
+                       };
+                       name = Deployment;
+               };
+/* End PBXBuildStyle section */
+
 /* Begin PBXContainerItemProxy section */
                DD041FF009D9E3250010AF2A /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                0867D690FE84028FC02AAC07 /* Project object */ = {
                        isa = PBXProject;
                        buildConfigurationList = 149C284308902B11008A9EFC /* Build configuration list for PBXProject "WebCore" */;
+                       buildSettings = {
+                       };
+                       buildStyles = (
+                               D041FC250A5E04A700841F7F /* Development */,
+                               D041FC260A5E04A700841F7F /* Deployment */,
+                       );
                        hasScannedForEncodings = 1;
                        knownRegions = (
                                English,
index 3bd325ca1cc76d6ae84ea0e239c0b763a1d4c1e6..c21c41b6f053b523482ac84b1a514399bbad3c13 100644 (file)
@@ -388,6 +388,11 @@ short Range::compareBoundaryPoints(CompareHow how, const Range *sourceRange, Exc
 
 short Range::compareBoundaryPoints( Node *containerA, int offsetA, Node *containerB, int offsetB )
 {
+    ASSERT(containerA && containerB);
+    if (!containerA)
+        return -1;
+    if (!containerB)
+        return 1;
     // see DOM2 traversal & range section 2.5
 
     // case 1: both points have the same container
index 16f8ed8b33a450036d803b888fefbe5bfab09a69..d8e4934f79d0098a85506d63671966275eb2f282 100644 (file)
@@ -160,7 +160,7 @@ void Selection::validate()
     if (m_extent.isNotNull() && !baseAndExtentEqual)
         m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent();
 
-    // Make sure we do not have a dangling start or end
+    // Make sure we do not have a dangling base or extent.
     if (m_base.isNull() && m_extent.isNull())
         m_baseIsFirst = true;
     else if (m_base.isNull()) {
@@ -258,6 +258,12 @@ void Selection::validate()
             break;
     }
     
+    // Make sure we do not have a dangling start or end.
+    if (m_start.isNull())
+        m_start = m_end;
+    if (m_end.isNull())
+        m_end = m_start;
+    
     adjustForEditableContent();
 
     // adjust the state
@@ -290,7 +296,7 @@ void Selection::validate()
 
 void Selection::adjustForEditableContent()
 {
-    if (m_base.isNull())
+    if (m_base.isNull() || m_start.isNull() || m_end.isNull())
         return;
 
     Node *baseRoot = m_base.node()->rootEditableElement();
@@ -372,6 +378,21 @@ void Selection::adjustForEditableContent()
         m_extent = m_baseIsFirst ? m_end : m_start;
 }
 
+bool Selection::isContentEditable() const
+{
+    return isEditablePosition(start());
+}
+
+bool Selection::isContentRichlyEditable() const
+{
+    return isRichlyEditablePosition(start());
+}
+
+Element* Selection::rootEditableElement() const
+{
+    return editableRootForPosition(start());
+}
+
 void Selection::debugPosition() const
 {
     if (!m_start.node())
index 8a2b4129768ae1ca24c398e07d8667e242649e24..d7238ca7982819f316c9a613acec165eb0394139 100644 (file)
@@ -75,9 +75,9 @@ public:
 
     PassRefPtr<Range> toRange() const;
     
-    Element* rootEditableElement() const { return start().node() ? start().node()->rootEditableElement() : 0; }
-    bool isContentEditable() const { return start().node() ? start().node()->isContentEditable() : false; }
-    bool isContentRichlyEditable() const { return start().node() ? start().node()->isContentRichlyEditable() : false; }
+    Element* rootEditableElement() const;
+    bool isContentEditable() const;
+    bool isContentRichlyEditable() const;
 
     void debugPosition() const;
 
index f6d258d6dcedf3b032c406436301db5bf3c9eec9..b8c17685d41378a8cea8858a019feeb9aee4bd85 100644 (file)
@@ -67,12 +67,12 @@ VisiblePosition VisiblePosition::next(bool stayInEditableContent) const
     if (!stayInEditableContent || next.isNull())
         return next;
     
-    Node* highestRoot = highestEditableRoot(deepEquivalent().node());
+    Node* highestRoot = highestEditableRoot(deepEquivalent());
     
     if (!next.deepEquivalent().node()->isAncestor(highestRoot))
         return VisiblePosition();
 
-    if (highestEditableRoot(next.deepEquivalent().node()) == highestRoot)
+    if (highestEditableRoot(next.deepEquivalent()) == highestRoot)
         return next;
     
     return firstEditablePositionAfterPositionInRoot(next.deepEquivalent(), highestRoot);
@@ -103,12 +103,12 @@ VisiblePosition VisiblePosition::previous(bool stayInEditableContent) const
     if (!stayInEditableContent || prev.isNull())
         return prev;
     
-    Node* highestRoot = highestEditableRoot(deepEquivalent().node());
+    Node* highestRoot = highestEditableRoot(deepEquivalent());
     
     if (!prev.deepEquivalent().node()->isAncestor(highestRoot))
         return VisiblePosition();
         
-    if (highestEditableRoot(prev.deepEquivalent().node()) == highestRoot)
+    if (highestEditableRoot(prev.deepEquivalent()) == highestRoot)
         return prev;
 
     return lastEditablePositionBeforePositionInRoot(prev.deepEquivalent(), highestRoot);
index 51a52c91a1ba7cb91965c04e641f50505678614c..9d4daddf9eefd8b210feb97e82e617da16b5ad63 100644 (file)
@@ -120,12 +120,13 @@ int comparePositions(const Position& a, const Position& b)
     return Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB);
 }
 
-Node* highestEditableRoot(Node* node)
+Node* highestEditableRoot(const Position& position)
 {
+    Node* node = position.node();
     if (!node)
         return 0;
         
-    Node* highestRoot = node->rootEditableElement();
+    Node* highestRoot = editableRootForPosition(position);
     if (!highestRoot)
         return 0;
     
@@ -158,40 +159,110 @@ Node* lowestEditableAncestor(Node* node)
     return lowestRoot;
 }
 
+bool isEditablePosition(const Position& p)
+{
+    Node* node = p.node();
+    if (!node)
+        return false;
+        
+    if (node->renderer() && node->renderer()->isTable())
+        node = node->parentNode();
+    
+    return node->isContentEditable();
+}
+
+bool isRichlyEditablePosition(const Position& p)
+{
+    Node* node = p.node();
+    if (!node)
+        return false;
+        
+    if (node->renderer() && node->renderer()->isTable())
+        node = node->parentNode();
+    
+    return node->isContentRichlyEditable();
+}
+
+Element* editableRootForPosition(const Position& p)
+{
+    Node* node = p.node();
+    if (!node)
+        return 0;
+        
+    if (node->renderer() && node->renderer()->isTable())
+        node = node->parentNode();
+    
+    return node->rootEditableElement();
+}
+
+Position nextCandidate(const Position& position)
+{
+    Position p = position;
+    while (!p.atEnd()) {
+        p = p.next(UsingComposedCharacters);
+        if (p.inRenderedContent())
+            return p;
+    }
+    return Position();
+}
+
+Position nextVisuallyDistinctCandidate(const Position& position)
+{
+    Position p = position;
+    Position downstreamStart = p.downstream();
+    while (!p.atEnd()) {
+        p = p.next(UsingComposedCharacters);
+        if (p.inRenderedContent() && p.downstream() != downstreamStart)
+            return p;
+    }
+    return Position();
+}
+
+Position previousCandidate(const Position& position)
+{
+    Position p = position;
+    while (!p.atStart()) {
+        p = p.previous(UsingComposedCharacters);
+        if (p.inRenderedContent())
+            return p;
+    }
+    return Position();
+}
+
+Position previousVisuallyDistinctCandidate(const Position& position)
+{
+    Position p = position;
+    Position downstreamStart = p.downstream();
+    while (!p.atStart()) {
+        p = p.previous(UsingComposedCharacters);
+        if (p.inRenderedContent() && p.downstream() != downstreamStart)
+            return p;
+    }
+    return Position();
+}
+
 VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
 {
     if (comparePositions(position, Position(highestRoot, 0)) == -1)
         return VisiblePosition(Position(highestRoot, 0));
 
-    Node* node = position.node();
-    Node* child = node->childNode(position.offset());
-    node = child ? child : node->traverseNextSibling(highestRoot);
-
-    while (node && !node->isContentEditable())
-        node = node->traverseNextNode(highestRoot);
-    
-    if (!node)
-        return VisiblePosition();
+    Position p = nextVisuallyDistinctCandidate(position);
+    while (p.isNotNull() && !isEditablePosition(p) && p.node()->isAncestor(highestRoot))
+        p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p);
 
-    return VisiblePosition(Position(node, 0));
+    return VisiblePosition(p);
 }
 
 VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
 {
     if (comparePositions(position, Position(highestRoot, maxDeepOffset(highestRoot))) == 1)
         return VisiblePosition(Position(highestRoot, maxDeepOffset(highestRoot)));
-
-    Node* node = position.node();
-    Node* child = node->firstChild() && position.offset() > 1 ? node->childNode(position.offset() - 1) : 0;
-    node = child ? child : node->traversePreviousNode(highestRoot);
-
-    while (node && !node->isContentEditable())
-        node = node->traversePreviousNodePostOrder(highestRoot);
     
-    if (!node)
-        return VisiblePosition();
-        
-    return VisiblePosition(Position(node, maxDeepOffset(node)));
+    Position p = previousVisuallyDistinctCandidate(position);
+    while (p.isNotNull() && !isEditablePosition(p) && p.node()->isAncestor(highestRoot))
+        p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p);
+
+    return VisiblePosition(p);
 }
 
 // antidote for maxDeepOffset()
index e0da87f8cd2bd99d054d8fc7b82b0ba42261596a..5764c6d85d209aacaa2cf2359d855fd2cbd93474 100644 (file)
@@ -46,11 +46,18 @@ int maxDeepOffset(const Node*);
 bool isAtomicNode(const Node*);
 bool editingIgnoresContent(const Node*);
 bool canHaveChildrenForEditing(const Node*);
-Node* highestEditableRoot(Node*);
+Node* highestEditableRoot(const Position&);
 VisiblePosition firstEditablePositionAfterPositionInRoot(const Position&, Node*);
 VisiblePosition lastEditablePositionBeforePositionInRoot(const Position&, Node*);
 int comparePositions(const Position&, const Position&);
 Node* lowestEditableAncestor(Node*);
+Position nextCandidate(const Position&);
+Position nextVisuallyDistinctCandidate(const Position&);
+Position previousCandidate(const Position&);
+Position previousVisuallyDistinctCandidate(const Position&);
+bool isEditablePosition(const Position&);
+bool isRichlyEditablePosition(const Position&);
+Element* editableRootForPosition(const Position&);
 
 void rebalanceWhitespaceInTextNode(Node*, unsigned start, unsigned length);
 const String& nonBreakingSpaceString();
index 427d429545cfa3b94b9356467053d7702968714f..337e4555a53eeddb22ccf644fdedaff5b8afd914 100644 (file)
@@ -193,6 +193,8 @@ static unsigned startWordBoundary(const UChar* characters, unsigned length)
 
 VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side)
 {
+    // FIXME: This returns a null VP for c at the start of the document
+    // and side == LeftWordIfOnBoundary
     VisiblePosition p = c;
     if (side == RightWordIfOnBoundary) {
         // at paragraph end, the startofWord is the current position