LayoutTests:
authorjusting <justing@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 14 Nov 2006 02:37:58 +0000 (02:37 +0000)
committerjusting <justing@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 14 Nov 2006 02:37:58 +0000 (02:37 +0000)
        Reviewed by harrison

        * editing/selection/drag-select-1-expected.checksum: Added.
        * editing/selection/drag-select-1-expected.png: Added.
        * editing/selection/drag-select-1-expected.txt: Added.
        * editing/selection/drag-select-1.html: Added.

WebCore:

        Reviewed by harrison

        <rdar://problem/4828264>
        In Mail, a crash occurs at WebCore::Selection::toRange() when selecting this web content (http://www.cnet.com/)

        The start of the selection is in an editable area, and the end is in an
        input field inside that editable area.  The code that should pull the end
        of such a selection outside the input field didn't escape shadow nodes,
        it would leave a dangling end, causing the crash in toRange.

        * editing/Selection.cpp:
        (WebCore::Selection::adjustForEditableContent):  Added an ASSERT and a fixup
        to prevent crashes like this in future Release builds.
        * editing/htmlediting.cpp:
        (WebCore::firstEditablePositionAfterPositionInRoot): Let this function
        escape shadow nodes.  We might eventually push this code down into
        next/previous{VisuallyDistinct}Canditate.
        (WebCore::lastEditablePositionBeforePositionInRoot): Ditto.

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

LayoutTests/ChangeLog
LayoutTests/editing/selection/drag-select-1-expected.checksum [new file with mode: 0644]
LayoutTests/editing/selection/drag-select-1-expected.png [new file with mode: 0644]
LayoutTests/editing/selection/drag-select-1-expected.txt [new file with mode: 0644]
LayoutTests/editing/selection/drag-select-1.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/editing/Selection.cpp
WebCore/editing/htmlediting.cpp

index 7a1ce5caec28cd3fae4e7e8c07fa1ddd8f94f78f..d4b1c846d5f15900d19879de65f8ef2260a5298a 100644 (file)
@@ -1,3 +1,12 @@
+2006-11-13  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by harrison
+
+        * editing/selection/drag-select-1-expected.checksum: Added.
+        * editing/selection/drag-select-1-expected.png: Added.
+        * editing/selection/drag-select-1-expected.txt: Added.
+        * editing/selection/drag-select-1.html: Added.
+
 2006-11-13  Justin Garcia  <justin.garcia@apple.com>
 
         Reviewed by Oliver
diff --git a/LayoutTests/editing/selection/drag-select-1-expected.checksum b/LayoutTests/editing/selection/drag-select-1-expected.checksum
new file mode 100644 (file)
index 0000000..c39953b
--- /dev/null
@@ -0,0 +1 @@
+d9146fffe379c31c1ca656dfc926009a
\ No newline at end of file
diff --git a/LayoutTests/editing/selection/drag-select-1-expected.png b/LayoutTests/editing/selection/drag-select-1-expected.png
new file mode 100644 (file)
index 0000000..3ee0bf3
Binary files /dev/null and b/LayoutTests/editing/selection/drag-select-1-expected.png differ
diff --git a/LayoutTests/editing/selection/drag-select-1-expected.txt b/LayoutTests/editing/selection/drag-select-1-expected.txt
new file mode 100644 (file)
index 0000000..27f169a
--- /dev/null
@@ -0,0 +1,18 @@
+ALERT: 0.1724137931034483
+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 784x576
+      RenderBlock {DIV} at (0,0) size 784x23
+        RenderInline {SPAN} at (0,0) size 64x18
+          RenderText {#text} at (0,2) size 64x18
+            text run at (0,2) width 64: "Start here."
+        RenderText {#text} at (64,2) size 4x18
+          text run at (64,2) width 4: " "
+        RenderTextField {INPUT} at (70,2) size 148x19 [bgcolor=#FFFFFF] [border: (2px inset #000000)]
+      RenderBlock {UL} at (0,39) size 784x0
+layer at (81,13) size 142x13
+  RenderBlock {DIV} at (3,3) size 142x13
+selection start: position 5 of child 0 {#text} of child 0 {SPAN} of child 0 {DIV} of child 0 {BODY} of child 0 {HTML} of document
+selection end:   position 1 of child 1 {#text} of child 0 {DIV} of child 0 {BODY} of child 0 {HTML} of document
diff --git a/LayoutTests/editing/selection/drag-select-1.html b/LayoutTests/editing/selection/drag-select-1.html
new file mode 100644 (file)
index 0000000..d9181a3
--- /dev/null
@@ -0,0 +1,47 @@
+<p>This tests for a drag select crasher.</p>
+<div id="div" contenteditable="true"><span id="start">Start here.</span> <input id="end" type="text"></div>
+<ul id="console"></ul>
+
+<script>
+function log(str) {
+    var li = document.createElement("li");
+    li.appendChild(document.createTextNode(str));
+    var console = document.getElementById("console");
+    console.appendChild(li);
+}
+function shouldBe(expected, actual) {
+    if (expected != actual)
+        log("Failure. Expected: " + expected + ", Actual: " + actual);
+    else
+        log("Passed");
+}
+alert( 5 / 29);
+if (window.layoutTestController) {
+    
+    var div = document.getElementById("div");
+    var sel = window.getSelection();
+    sel.setPosition(div, 0);
+    
+    var start = document.getElementById("start");
+    var startx = start.offsetParent.offsetLeft + start.offsetLeft + start.offsetWidth / 2;
+    var starty = start.offsetParent.offsetTop + start.offsetTop + start.offsetHeight / 2;
+    eventSender.mouseMoveTo(startx, starty);
+    eventSender.mouseDown();
+
+    var end = document.getElementById("end");
+    endx = end.offsetParent.offsetLeft + end.offsetLeft + end.offsetWidth / 2;
+    endy = end.offsetParent.offsetTop + end.offsetTop + end.offsetHeight / 2;
+
+    var steps = 20;
+    for (var i = 1; i <= steps; i++) {
+        eventSender.mouseMoveTo(startx + Math.abs(startx - endx) * (i / steps), starty + Math.abs(starty - endy) * (i / steps));
+    }
+
+    eventSender.mouseMoveTo(endx, endy);
+    eventSender.mouseUp();
+
+    
+} else {
+    log("This uses the eventSender to perform a drag select.  To run it manually, mouse down over 'start here' and drag to the left, over the input field.");
+}
+</script>
index 5cbc1bd579e0478deebdde0d89bff85b73d2f92b..e6a9c2ad8f8fe8664eed414d6969918acf6381b8 100644 (file)
@@ -1,3 +1,24 @@
+2006-11-13  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by harrison
+        
+        <rdar://problem/4828264>
+        In Mail, a crash occurs at WebCore::Selection::toRange() when selecting this web content (http://www.cnet.com/)
+        
+        The start of the selection is in an editable area, and the end is in an 
+        input field inside that editable area.  The code that should pull the end
+        of such a selection outside the input field didn't escape shadow nodes,
+        it would leave a dangling end, causing the crash in toRange.
+        
+        * editing/Selection.cpp:
+        (WebCore::Selection::adjustForEditableContent):  Added an ASSERT and a fixup
+        to prevent crashes like this in future Release builds.
+        * editing/htmlediting.cpp:
+        (WebCore::firstEditablePositionAfterPositionInRoot): Let this function 
+        escape shadow nodes.  We might eventually push this code down into 
+        next/previous{VisuallyDistinct}Canditate.
+        (WebCore::lastEditablePositionBeforePositionInRoot): Ditto.
+
 2006-11-13  Justin Garcia  <justin.garcia@apple.com>
 
         Reviewed by darin
index ffe73795ee1a8552beceba74a76df2d9076c6261..c15ab5738d65cb5fb9d7c3ac6cc2483f31616f8d 100644 (file)
@@ -356,6 +356,10 @@ void Selection::adjustForEditableContent()
         if (startRoot != baseRoot) {
             VisiblePosition first = firstEditablePositionAfterPositionInRoot(m_start, baseRoot);
             m_start = first.deepEquivalent();
+            if (m_start.isNull()) {
+                ASSERT_NOT_REACHED();
+                m_start = m_end;
+            }
         }
         // If the end is outside the base's editable root, cap it at the end of that root.
         // If the end is in non-editable content that is inside the base's root, put it
@@ -363,6 +367,10 @@ void Selection::adjustForEditableContent()
         if (endRoot != baseRoot) {
             VisiblePosition last = lastEditablePositionBeforePositionInRoot(m_end, baseRoot);
             m_end = last.deepEquivalent();
+            if (m_end.isNull()) {
+                ASSERT_NOT_REACHED();
+                m_end = m_start;
+            }
         }
     // The selection is based in non-editable content.
     } else {
index 0644caacc65733a010fba0110b612f9acc13ddc4..b107eb63d5c145dd42ec62cd3d0849f5892600fd 100644 (file)
@@ -247,10 +247,18 @@ VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& positio
 {
     if (comparePositions(position, Position(highestRoot, 0)) == -1)
         return VisiblePosition(Position(highestRoot, 0));
-
+    
     Position p = nextVisuallyDistinctCandidate(position);
-    while (p.isNotNull() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot))
+    Node* root = editableRootForPosition(position);
+    if (p.isNull() && root && root->isShadowNode())
+        p = Position(root->shadowParentNode(), maxDeepOffset(root->shadowParentNode()));
+    while (p.isNotNull() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot)) {
         p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p);
+        
+        Node* root = editableRootForPosition(position);
+        if (p.isNull() && root && root->isShadowNode())
+            p = Position(root->shadowParentNode(), maxDeepOffset(root->shadowParentNode()));
+    }
 
     return VisiblePosition(p);
 }
@@ -261,8 +269,16 @@ VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& positio
         return VisiblePosition(Position(highestRoot, maxDeepOffset(highestRoot)));
     
     Position p = previousVisuallyDistinctCandidate(position);
-    while (p.isNotNull() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot))
+    Node* root = editableRootForPosition(position);
+    if (p.isNull() && root && root->isShadowNode())
+        p = Position(root->shadowParentNode(), 0);
+    while (p.isNotNull() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot)) {
         p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p);
+        
+        Node* root = editableRootForPosition(position);
+        if (p.isNull() && root && root->isShadowNode())
+            p = Position(root->shadowParentNode(), 0);
+    }
 
     return VisiblePosition(p);
 }