Reviewed by John
authorkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 2 Sep 2004 17:06:05 +0000 (17:06 +0000)
committerkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 2 Sep 2004 17:06:05 +0000 (17:06 +0000)
        Fix for these bugs:

        <rdar://problem/3729219> REGRESSION (Mail): Caret placement and navigation not working correctly at yahoo.com
        <rdar://problem/3758606> REGRESSION (Mail): Deletions across DIV elements is broken
        <rdar://problem/3784810> REGRESSION (Mail): delete at beginning of line instead deletes to the end of the document (simple reduced test)

        I made some very big improvements to the code which handles deletions of selections
        which span more than one block.

        * khtml/editing/htmlediting_impl.cpp:
        (khtml::CompositeEditCommandImpl::deleteUnrenderedText): An new, overloaded version of this
        function which works on a NodeImpl. The code in this function fell out of some refactoring
        I did in the function with the same name which takes a Position as an argument.
        (khtml::DeleteSelectionCommandImpl::moveNodesToBlock): New function. This new logic provides
        the smarts to fix the bugs listed above and greatly improves the code to perform deleting
        selections which span more than one block.
        (khtml::DeleteSelectionCommandImpl::doApply): Several changes. Constrain downstream positions
        so that they stay in the current block. This was a mistake before I think. Add code to detect
        when the insertion point is a caret at the start of a block. This is a special-case of a
        cross-block selection. Change the end-of-deletion-fixup case to operate on the downstreamEnd
        position of the selection rather than the upstream. This is more logical. Refine the block
        merge logic to handle more cases, like dealing with whitespace.
        * khtml/editing/htmlediting_impl.h: Associated header file changes.
        * khtml/xml/dom_nodeimpl.cpp:
        (NodeImpl::rootEditableElement): Add code to clamp the return value to the body element.
        * khtml/xml/dom_selection.cpp:
        (DOM::Selection::validate): Make selection canonicalizations stay in the current block.
        * layout-tests/editing/deleting/delete-block-merge-contents-001-expected.txt:
        * layout-tests/editing/deleting/delete-block-merge-contents-001.html:
        * layout-tests/editing/deleting/delete-block-merge-contents-002-expected.txt: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-002.html: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-003-expected.txt: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-003.html: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-004-expected.txt: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-004.html: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-005-expected.txt: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-005.html: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-006-expected.txt: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-006.html: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-007-expected.txt: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-007.html: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-008-expected.txt: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-008.html: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-009-expected.txt: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-009.html: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-010-expected.txt: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-010.html: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-011-expected.txt: Added.
        * layout-tests/editing/deleting/delete-block-merge-contents-011.html: Added.
        * layout-tests/editing/inserting/insert-3654864-fix-expected.txt: Updated with new results.
        * layout-tests/editing/inserting/insert-3654864-fix.html: Updated test to deal better with
        improved select all behavior.
        * layout-tests/editing/selection/select-all-003-expected.txt: Updated with new results.

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

32 files changed:
LayoutTests/editing/deleting/delete-block-merge-contents-001-expected.txt
LayoutTests/editing/deleting/delete-block-merge-contents-001.html
LayoutTests/editing/deleting/delete-block-merge-contents-002-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-002.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-003-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-003.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-004-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-004.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-005-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-005.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-006-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-006.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-007-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-007.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-008-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-008.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-009-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-009.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-010-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-010.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-011-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-block-merge-contents-011.html [new file with mode: 0644]
LayoutTests/editing/inserting/insert-3654864-fix-expected.txt
LayoutTests/editing/inserting/insert-3654864-fix.html
LayoutTests/editing/selection/select-all-003-expected.txt
WebCore/ChangeLog-2005-08-23
WebCore/khtml/editing/SelectionController.cpp
WebCore/khtml/editing/htmlediting_impl.cpp
WebCore/khtml/editing/htmlediting_impl.h
WebCore/khtml/editing/selection.cpp
WebCore/khtml/xml/dom_nodeimpl.cpp
WebCore/khtml/xml/dom_selection.cpp

index c7f230c9ec4bca527a2020fa3b6e384849233696..de8b0e311fc89b19a40901cca74adb08940b4baa 100644 (file)
@@ -3,10 +3,14 @@ layer at (0,0) size 800x600
 layer at (0,0) size 800x600
   RenderBlock {HTML} at (0,0) size 800x600
     RenderBody {BODY} at (8,8) size 784x584
-      RenderBlock {DIV} at (0,0) size 784x84 [border: (2px solid #FF0000)]
-        RenderBlock (anonymous) at (14,14) size 756x28
-          RenderText {TEXT} at (0,0) size 42x28
-            text run at (0,0) width 42: "Test"
-        RenderBlock {DIV} at (14,42) size 756x28
-          RenderText {TEXT} at (0,0) size 42x28
-            text run at (0,0) width 42: "Test"
+      RenderBlock {DIV} at (0,0) size 784x56 [border: (2px solid #FF0000)]
+        RenderBlock {DIV} at (14,14) size 756x28
+          RenderBlock (anonymous) at (0,0) size 756x28
+            RenderText {TEXT} at (0,0) size 40x28
+              text run at (0,0) width 40: "One"
+            RenderText {TEXT} at (40,0) size 44x28
+              text run at (40,0) width 44: "Two"
+selection is CARET:
+start:      position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+upstream:   position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+downstream: position 0 of child 2 {TEXT} of child 2 {DIV} of root {DIV}
index 83d80cee4e2361ce8cd9c0ef71630eaad4356c46..c8e7da19b1bd2cb65afe8e4e7e0eeda68072d694 100644 (file)
@@ -13,8 +13,8 @@
 <script>
 
 function editingTest() {
-    for (i = 0; i < 5; i++) {
-        extendSelectionForwardByCharacterCommand();    
+    for (i = 0; i < 4; i++) {
+        moveSelectionForwardByCharacterCommand();    
     }
     deleteCommand();
 }
@@ -25,8 +25,10 @@ function editingTest() {
 </head> 
 <body>
 <div contenteditable id="root" class="editing">
-Test 
-<div>Test</div>
+<div id="test">
+One 
+<div>Two</div>
+</div>
 </div>
 
 <script>
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-002-expected.txt b/LayoutTests/editing/deleting/delete-block-merge-contents-002-expected.txt
new file mode 100644 (file)
index 0000000..83e5201
--- /dev/null
@@ -0,0 +1,18 @@
+layer at (0,0) size 800x600
+  RenderCanvas 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 784x84 [border: (2px solid #FF0000)]
+        RenderBlock {DIV} at (14,14) size 756x56
+          RenderText {TEXT} at (0,0) size 40x28
+            text run at (0,0) width 40: "One"
+          RenderText {TEXT} at (40,0) size 44x28
+            text run at (40,0) width 44: "Two"
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 57x28
+            text run at (0,28) width 57: "Three"
+selection is CARET:
+start:      position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+upstream:   position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+downstream: position 0 of child 2 {TEXT} of child 2 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-002.html b/LayoutTests/editing/deleting/delete-block-merge-contents-002.html
new file mode 100644 (file)
index 0000000..4ff7094
--- /dev/null
@@ -0,0 +1,40 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    padding: 12px; 
+    font-size: 24px; 
+}
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    for (i = 0; i < 4; i++) {
+        moveSelectionForwardByCharacterCommand();    
+    }
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+<div contenteditable id="root" class="editing">
+<div id="test">
+One 
+<div>Two</div>
+Three
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-003-expected.txt b/LayoutTests/editing/deleting/delete-block-merge-contents-003-expected.txt
new file mode 100644 (file)
index 0000000..83e5201
--- /dev/null
@@ -0,0 +1,18 @@
+layer at (0,0) size 800x600
+  RenderCanvas 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 784x84 [border: (2px solid #FF0000)]
+        RenderBlock {DIV} at (14,14) size 756x56
+          RenderText {TEXT} at (0,0) size 40x28
+            text run at (0,0) width 40: "One"
+          RenderText {TEXT} at (40,0) size 44x28
+            text run at (40,0) width 44: "Two"
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 57x28
+            text run at (0,28) width 57: "Three"
+selection is CARET:
+start:      position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+upstream:   position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+downstream: position 0 of child 2 {TEXT} of child 2 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-003.html b/LayoutTests/editing/deleting/delete-block-merge-contents-003.html
new file mode 100644 (file)
index 0000000..509061f
--- /dev/null
@@ -0,0 +1,40 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    padding: 12px; 
+    font-size: 24px; 
+}
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    for (i = 0; i < 4; i++) {
+        moveSelectionForwardByCharacterCommand();    
+    }
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+<div contenteditable id="root" class="editing">
+<div id="test">
+One 
+<div>Two<br></div>
+Three
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-004-expected.txt b/LayoutTests/editing/deleting/delete-block-merge-contents-004-expected.txt
new file mode 100644 (file)
index 0000000..d79725c
--- /dev/null
@@ -0,0 +1,19 @@
+layer at (0,0) size 800x600
+  RenderCanvas 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 784x84 [border: (2px solid #FF0000)]
+        RenderBlock {DIV} at (14,14) size 756x56
+          RenderText {TEXT} at (0,0) size 40x28
+            text run at (0,0) width 40: "One"
+          RenderText {TEXT} at (40,0) size 44x28
+            text run at (40,0) width 44: "Two"
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 57x28
+            text run at (0,28) width 57: "Three"
+selection is CARET:
+start:      position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+upstream:   position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+downstream: position 0 of child 2 {TEXT} of child 2 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-004.html b/LayoutTests/editing/deleting/delete-block-merge-contents-004.html
new file mode 100644 (file)
index 0000000..f78f64c
--- /dev/null
@@ -0,0 +1,41 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    padding: 12px; 
+    font-size: 24px; 
+}
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    for (i = 0; i < 4; i++) {
+        moveSelectionForwardByCharacterCommand();    
+    }
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+<div contenteditable id="root" class="editing">
+<div id="test">
+One 
+<div>Two<br>
+</div>
+Three
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-005-expected.txt b/LayoutTests/editing/deleting/delete-block-merge-contents-005-expected.txt
new file mode 100644 (file)
index 0000000..e36d231
--- /dev/null
@@ -0,0 +1,22 @@
+layer at (0,0) size 800x600
+  RenderCanvas 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 784x112 [border: (2px solid #FF0000)]
+        RenderBlock {DIV} at (14,14) size 756x84
+          RenderBlock (anonymous) at (0,0) size 756x28
+            RenderText {TEXT} at (0,0) size 40x28
+              text run at (0,0) width 40: "One"
+            RenderText {TEXT} at (40,0) size 44x28
+              text run at (40,0) width 44: "Two"
+          RenderBlock {DIV} at (0,28) size 756x28
+            RenderText {TEXT} at (0,0) size 57x28
+              text run at (0,0) width 57: "Three"
+          RenderBlock (anonymous) at (0,56) size 756x28
+            RenderText {TEXT} at (0,0) size 45x28
+              text run at (0,0) width 45: "Four"
+selection is CARET:
+start:      position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+upstream:   position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+downstream: position 0 of child 2 {TEXT} of child 2 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-005.html b/LayoutTests/editing/deleting/delete-block-merge-contents-005.html
new file mode 100644 (file)
index 0000000..31524e7
--- /dev/null
@@ -0,0 +1,40 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    padding: 12px; 
+    font-size: 24px; 
+}
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    for (i = 0; i < 4; i++) {
+        moveSelectionForwardByCharacterCommand();    
+    }
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+<div contenteditable id="root" class="editing">
+<div id="test">
+One 
+<div>Two<div>Three</div></div>
+Four
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-006-expected.txt b/LayoutTests/editing/deleting/delete-block-merge-contents-006-expected.txt
new file mode 100644 (file)
index 0000000..6535e78
--- /dev/null
@@ -0,0 +1,23 @@
+layer at (0,0) size 800x600
+  RenderCanvas 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 784x112 [border: (2px solid #FF0000)]
+        RenderBlock {DIV} at (14,14) size 756x84
+          RenderBlock (anonymous) at (0,0) size 756x28
+            RenderText {TEXT} at (0,0) size 40x28
+              text run at (0,0) width 40: "One"
+            RenderText {TEXT} at (40,0) size 44x28
+              text run at (40,0) width 44: "Two"
+          RenderBlock {DIV} at (0,28) size 756x28
+            RenderText {TEXT} at (0,0) size 57x28
+              text run at (0,0) width 57: "Three"
+            RenderBR {BR} at (0,0) size 0x0
+          RenderBlock (anonymous) at (0,56) size 756x28
+            RenderText {TEXT} at (0,0) size 45x28
+              text run at (0,0) width 45: "Four"
+selection is CARET:
+start:      position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+upstream:   position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+downstream: position 0 of child 2 {TEXT} of child 2 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-006.html b/LayoutTests/editing/deleting/delete-block-merge-contents-006.html
new file mode 100644 (file)
index 0000000..5d45aa9
--- /dev/null
@@ -0,0 +1,40 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    padding: 12px; 
+    font-size: 24px; 
+}
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    for (i = 0; i < 4; i++) {
+        moveSelectionForwardByCharacterCommand();    
+    }
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+<div contenteditable id="root" class="editing">
+<div id="test">
+One 
+<div>Two<div>Three<br></div></div>
+Four
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-007-expected.txt b/LayoutTests/editing/deleting/delete-block-merge-contents-007-expected.txt
new file mode 100644 (file)
index 0000000..fbe8b15
--- /dev/null
@@ -0,0 +1,24 @@
+layer at (0,0) size 800x600
+  RenderCanvas 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 784x112 [border: (2px solid #FF0000)]
+        RenderBlock {DIV} at (14,14) size 756x84
+          RenderBlock (anonymous) at (0,0) size 756x28
+            RenderText {TEXT} at (0,0) size 40x28
+              text run at (0,0) width 40: "One"
+            RenderText {TEXT} at (40,0) size 44x28
+              text run at (40,0) width 44: "Two"
+            RenderBR {BR} at (0,0) size 0x0
+          RenderBlock {DIV} at (0,28) size 756x28
+            RenderText {TEXT} at (0,0) size 57x28
+              text run at (0,0) width 57: "Three"
+            RenderBR {BR} at (0,0) size 0x0
+          RenderBlock (anonymous) at (0,56) size 756x28
+            RenderText {TEXT} at (0,0) size 45x28
+              text run at (0,0) width 45: "Four"
+selection is CARET:
+start:      position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+upstream:   position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+downstream: position 0 of child 2 {TEXT} of child 2 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-007.html b/LayoutTests/editing/deleting/delete-block-merge-contents-007.html
new file mode 100644 (file)
index 0000000..d475d1a
--- /dev/null
@@ -0,0 +1,40 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    padding: 12px; 
+    font-size: 24px; 
+}
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    for (i = 0; i < 4; i++) {
+        moveSelectionForwardByCharacterCommand();    
+    }
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+<div contenteditable id="root" class="editing">
+<div id="test">
+One 
+<div>Two<br><div>Three<br></div></div>
+Four
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-008-expected.txt b/LayoutTests/editing/deleting/delete-block-merge-contents-008-expected.txt
new file mode 100644 (file)
index 0000000..0152956
--- /dev/null
@@ -0,0 +1,23 @@
+layer at (0,0) size 800x600
+  RenderCanvas 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 784x112 [border: (2px solid #FF0000)]
+        RenderBlock {DIV} at (14,14) size 756x84
+          RenderBlock (anonymous) at (0,0) size 756x28
+            RenderText {TEXT} at (0,0) size 40x28
+              text run at (0,0) width 40: "One"
+            RenderText {TEXT} at (40,0) size 44x28
+              text run at (40,0) width 44: "Two"
+          RenderBlock {DIV} at (0,28) size 756x28
+            RenderText {TEXT} at (0,0) size 57x28
+              text run at (0,0) width 57: "Three"
+          RenderBlock {DIV} at (0,56) size 756x28
+            RenderBlock (anonymous) at (0,0) size 756x28
+              RenderText {TEXT} at (0,0) size 45x28
+                text run at (0,0) width 45: "Four"
+selection is CARET:
+start:      position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+upstream:   position 3 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+downstream: position 0 of child 2 {TEXT} of child 2 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-008.html b/LayoutTests/editing/deleting/delete-block-merge-contents-008.html
new file mode 100644 (file)
index 0000000..6d936f0
--- /dev/null
@@ -0,0 +1,38 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    padding: 12px; 
+    font-size: 24px; 
+}
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    for (i = 0; i < 4; i++) {
+        moveSelectionForwardByCharacterCommand();    
+    }
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+<div contenteditable id="root" class="editing">
+<div id="test">One<div> 
+<div>Two<div>Three</div></div>
+Four
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-009-expected.txt b/LayoutTests/editing/deleting/delete-block-merge-contents-009-expected.txt
new file mode 100644 (file)
index 0000000..e35dd95
--- /dev/null
@@ -0,0 +1,25 @@
+layer at (0,0) size 800x600
+  RenderCanvas 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 784x140 [border: (2px solid #FF0000)]
+        RenderBlock {DIV} at (14,14) size 756x112
+          RenderBlock (anonymous) at (0,0) size 756x28
+            RenderText {TEXT} at (0,0) size 40x28
+              text run at (0,0) width 40: "One"
+          RenderBlock {DIV} at (0,28) size 756x56
+            RenderText {TEXT} at (0,0) size 44x28
+              text run at (0,0) width 44: "Two"
+            RenderText {TEXT} at (44,0) size 57x28
+              text run at (44,0) width 57: "Three"
+            RenderBR {BR} at (0,0) size 0x0
+            RenderText {TEXT} at (0,28) size 45x28
+              text run at (0,28) width 45: "Four"
+          RenderBlock (anonymous) at (0,84) size 756x28
+            RenderText {TEXT} at (0,0) size 43x28
+              text run at (0,0) width 43: "Five"
+selection is CARET:
+start:      position 3 of child 1 {TEXT} of child 2 {DIV} of child 2 {DIV} of root {DIV}
+upstream:   position 3 of child 1 {TEXT} of child 2 {DIV} of child 2 {DIV} of root {DIV}
+downstream: position 0 of child 2 {TEXT} of child 2 {DIV} of child 2 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-009.html b/LayoutTests/editing/deleting/delete-block-merge-contents-009.html
new file mode 100644 (file)
index 0000000..ac4141c
--- /dev/null
@@ -0,0 +1,39 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    padding: 12px; 
+    font-size: 24px; 
+}
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    for (i = 0; i < 8; i++)
+        moveSelectionForwardByCharacterCommand();    
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+<div contenteditable id="root" class="editing">
+<div id="test">
+One 
+<div>Two<br><div>Three<br></div>Four</div>
+Five
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-010-expected.txt b/LayoutTests/editing/deleting/delete-block-merge-contents-010-expected.txt
new file mode 100644 (file)
index 0000000..bcf0770
--- /dev/null
@@ -0,0 +1,19 @@
+layer at (0,0) size 800x600
+  RenderCanvas 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 784x56 [border: (2px solid #FF0000)]
+        RenderBlock {DIV} at (14,14) size 756x28
+          RenderText {TEXT} at (0,0) size 40x28
+            text run at (0,0) width 40: "One"
+          RenderText {TEXT} at (40,0) size 44x28
+            text run at (40,0) width 44: "Two"
+          RenderText {TEXT} at (84,0) size 57x28
+            text run at (84,0) width 57: "Three"
+          RenderText {TEXT} at (141,0) size 45x28
+            text run at (141,0) width 45: "Four"
+selection is CARET:
+start:      position 5 of child 3 {TEXT} of child 2 {DIV} of root {DIV}
+upstream:   position 5 of child 3 {TEXT} of child 2 {DIV} of root {DIV}
+downstream: position 0 of child 4 {TEXT} of child 2 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-010.html b/LayoutTests/editing/deleting/delete-block-merge-contents-010.html
new file mode 100644 (file)
index 0000000..99122ff
--- /dev/null
@@ -0,0 +1,45 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    padding: 12px; 
+    font-size: 24px; 
+}
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    for (i = 0; i < 4; i++)
+        moveSelectionForwardByCharacterCommand();    
+    deleteCommand();
+    for (i = 0; i < 4; i++)
+        moveSelectionForwardByCharacterCommand();    
+    deleteCommand();
+    for (i = 0; i < 6; i++)
+        moveSelectionForwardByCharacterCommand();    
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+<div contenteditable id="root" class="editing">
+<div id="test">
+One 
+<div>Two<br><div>Three<br></div></div>
+Four
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-011-expected.txt b/LayoutTests/editing/deleting/delete-block-merge-contents-011-expected.txt
new file mode 100644 (file)
index 0000000..50659ad
--- /dev/null
@@ -0,0 +1,22 @@
+layer at (0,0) size 800x600
+  RenderCanvas 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 784x140 [border: (2px solid #FF0000)]
+        RenderBlock {DIV} at (14,14) size 756x112
+          RenderBlock (anonymous) at (0,0) size 756x28
+            RenderText {TEXT} at (0,0) size 40x28
+              text run at (0,0) width 40: "One"
+          RenderBlock {DIV} at (0,28) size 756x56
+            RenderText {TEXT} at (0,0) size 44x28
+              text run at (0,0) width 44: "Two"
+            RenderBR {BR} at (0,0) size 0x0
+            RenderBR {BR} at (0,28) size 0x28
+          RenderBlock (anonymous) at (0,84) size 756x28
+            RenderText {TEXT} at (0,0) size 57x28
+              text run at (0,0) width 57: "Three"
+selection is CARET:
+start:      position 0 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
+upstream:   position 0 of child 2 {DIV} of root {DIV}
+downstream: position 0 of child 1 {TEXT} of child 2 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-block-merge-contents-011.html b/LayoutTests/editing/deleting/delete-block-merge-contents-011.html
new file mode 100644 (file)
index 0000000..b69ca6f
--- /dev/null
@@ -0,0 +1,36 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    padding: 12px; 
+    font-size: 24px; 
+}
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+<div contenteditable id="root" class="editing">
+<div id="test">
+One 
+<div>Two<br><br></div>
+Three
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
index bae69b0656a09b0c539f2e1ef5ebd6483557333f..e9bfc510400e76b9d6f4a326fc540693dc0c3755 100644 (file)
@@ -2,13 +2,14 @@ layer at (0,0) size 800x600
   RenderCanvas 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 784x84 [border: (2px solid #FF0000)]
-        RenderBR {BR} at (14,14) size 0x28
-        RenderText {TEXT} at (14,42) size 36x28
-          text run at (14,42) width 36: "xxx"
-        RenderBR {BR} at (14,42) size 0x28
+    RenderBody {BODY} at (8,8) size 784x584 [border: (2px solid #FF0000)]
+      RenderBR {BR} at (14,14) size 0x28
+      RenderText {TEXT} at (14,42) size 36x28
+        text run at (14,42) width 36: "xxx"
+      RenderBR {BR} at (14,42) size 0x28
+      RenderText {TEXT} at (0,0) size 0x0
+      RenderText {TEXT} at (0,0) size 0x0
 selection is CARET:
-start:      position 3 of child 2 {TEXT} of root {DIV}
-upstream:   position 3 of child 2 {TEXT} of root {DIV}
-downstream: position 0 of child 3 {BR} of root {DIV}
+start:      position 3 of child 2 {TEXT} of root {BODY}
+upstream:   position 3 of child 2 {TEXT} of root {BODY}
+downstream: position 0 of child 3 {BR} of root {BODY}
index 9a44ea8ad445dae45bc1c5f96f8edf0c5cdf1fcd..6431ca49c17d103145fba6f67824f8fda24acd52 100644 (file)
@@ -24,10 +24,8 @@ function editingTest() {
 
 <title>Editing Test</title> 
 </head> 
-<body>
-<div contenteditable id="root" class="editing">
+<body contenteditable id="root" class="editing">
 <span id="test">Foo    <i>  bar</i></span>
-</div>
 
 <script>
 runEditingTest();
index 58ef861988ab387b9218ac012fd8fddc972469bd..f89c600af615d3bd2c54804cdffc9b1f19d8ec67 100644 (file)
@@ -30,6 +30,6 @@ selection is RANGE:
 start:      position 0 of child 1 {BR} of root {BODY}
 upstream:   position 0 of  of root {BODY}
 downstream: position 0 of child 1 {BR} of root {BODY}
-end:        position 1 of child 7 {BR} of root {BODY}
-upstream:   position 1 of child 7 {BR} of root {BODY}
-downstream: position 1 of child 8 {TEXT} of root {BODY}
+end:        position 0 of child 9 {DIV} of root {BODY}
+upstream:   position 0 of child 9 {DIV} of root {BODY}
+downstream: position 1 of child 9 {DIV} of root {BODY}
index 07352d5c6d5e585d84a3467bbf10cb619d934381..794b48fe001b0254ba7ababe82aaf73718c11c05 100644 (file)
@@ -1,3 +1,61 @@
+2004-09-02  Ken Kocienda  <kocienda@apple.com>
+
+        Reviewed by John
+
+        Fix for these bugs:
+        
+        <rdar://problem/3729219> REGRESSION (Mail): Caret placement and navigation not working correctly at yahoo.com
+        <rdar://problem/3758606> REGRESSION (Mail): Deletions across DIV elements is broken
+        <rdar://problem/3784810> REGRESSION (Mail): delete at beginning of line instead deletes to the end of the document (simple reduced test)
+
+        I made some very big improvements to the code which handles deletions of selections
+        which span more than one block.
+
+        * khtml/editing/htmlediting_impl.cpp:
+        (khtml::CompositeEditCommandImpl::deleteUnrenderedText): An new, overloaded version of this
+        function which works on a NodeImpl. The code in this function fell out of some refactoring
+        I did in the function with the same name which takes a Position as an argument.
+        (khtml::DeleteSelectionCommandImpl::moveNodesToBlock): New function. This new logic provides
+        the smarts to fix the bugs listed above and greatly improves the code to perform deleting 
+        selections which span more than one block.
+        (khtml::DeleteSelectionCommandImpl::doApply): Several changes. Constrain downstream positions
+        so that they stay in the current block. This was a mistake before I think. Add code to detect
+        when the insertion point is a caret at the start of a block. This is a special-case of a
+        cross-block selection. Change the end-of-deletion-fixup case to operate on the downstreamEnd
+        position of the selection rather than the upstream. This is more logical. Refine the block 
+        merge logic to handle more cases, like dealing with whitespace. 
+        * khtml/editing/htmlediting_impl.h: Associated header file changes.
+        * khtml/xml/dom_nodeimpl.cpp: 
+        (NodeImpl::rootEditableElement): Add code to clamp the return value to the body element.
+        * khtml/xml/dom_selection.cpp:
+        (DOM::Selection::validate): Make selection canonicalizations stay in the current block.
+        * layout-tests/editing/deleting/delete-block-merge-contents-001-expected.txt:
+        * layout-tests/editing/deleting/delete-block-merge-contents-001.html:
+        * layout-tests/editing/deleting/delete-block-merge-contents-002-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-002.html: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-003-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-003.html: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-004-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-004.html: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-005-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-005.html: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-006-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-006.html: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-007-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-007.html: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-008-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-008.html: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-009-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-009.html: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-010-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-010.html: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-011-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-block-merge-contents-011.html: Added.
+        * layout-tests/editing/inserting/insert-3654864-fix-expected.txt: Updated with new results.
+        * layout-tests/editing/inserting/insert-3654864-fix.html: Updated test to deal better with
+        improved select all behavior.
+        * layout-tests/editing/selection/select-all-003-expected.txt: Updated with new results.
+
 2004-09-01  Richard Williamson   <rjw@apple.com>
 
        Fixed panther build glitch.
index c2f144f7ff55408256c2e262175da30aaf4cf83b..75bba6361a22c8c9896a1c331bd26036489ed183 100644 (file)
@@ -719,7 +719,7 @@ void Selection::validate(ETextGranularity granularity)
     if (start().isEmpty() && end().isEmpty()) {
         m_state = NONE;
     }
-    else if (start() == end() || start().upstream() == end().upstream()) {
+    else if (start() == end() || start().upstream(StayInBlock) == end().upstream(StayInBlock)) {
         m_state = CARET;
     }
     else {
@@ -730,8 +730,8 @@ void Selection::validate(ETextGranularity granularity)
         // purposes of comparing selections). This is an ideal point of the code
         // to do this operation, since all selection changes that result in a RANGE 
         // come through here before anyone uses it.
-        assignStart(start().downstream());
-        assignEnd(end().upstream());
+        assignStart(start().downstream(StayInBlock));
+        assignEnd(end().upstream(StayInBlock));
     }
 
     m_needsCaretLayout = true;
index 2909bf213b67c560bb5be323be9657675e427dea..f18a51eb60061fff134598866d2c52f8b2575a81 100644 (file)
@@ -593,35 +593,40 @@ ElementImpl *CompositeEditCommandImpl::applyTypingStyle(NodeImpl *child) const
     return element;
 }
 
+void CompositeEditCommandImpl::deleteUnrenderedText(NodeImpl *node)
+{
+    if (!node)
+        return;
+
+    if (node->isTextNode()) {
+        if (!node->renderer() || !static_cast<RenderText *>(node->renderer())->firstTextBox())
+            removeNode(node);
+        else {
+            TextImpl *text = static_cast<TextImpl *>(node);
+            if (text->caretMinOffset() > 0)
+                deleteText(text, 0, text->caretMinOffset());
+            if ((int)text->length() > text->caretMaxOffset())
+                deleteText(text, text->caretMaxOffset(), text->length() - text->caretMaxOffset());
+        }
+    }
+}
+
 void CompositeEditCommandImpl::deleteUnrenderedText(const Position &pos)
 {
+    if (pos.isEmpty())
+        return;
+
     Position upstream = pos.upstream(StayInBlock);
     Position downstream = pos.downstream(StayInBlock);
     Position block = Position(pos.node()->enclosingBlockFlowElement(), 0);
     
     NodeImpl *node = upstream.node();
-    while (node != downstream.node()) {
+    while (node && node != downstream.node()) {
         NodeImpl *next = node->traverseNextNode();
-        if (node->isTextNode()) {
-            if (!node->renderer() || !static_cast<RenderText *>(node->renderer())->firstTextBox())
-                removeNode(node);
-            else {
-                TextImpl *text = static_cast<TextImpl *>(node);
-                if ((int)text->length() > text->caretMaxOffset())
-                    deleteText(text, text->caretMaxOffset(), text->length() - text->caretMaxOffset());
-            }
-        }
+        deleteUnrenderedText(node);
         node = next;
     }
-    if (downstream.node()->isTextNode()) {
-        if (!node->renderer() || !static_cast<RenderText *>(node->renderer())->firstTextBox())
-            removeNode(downstream.node());
-        else {
-            TextImpl *text = static_cast<TextImpl *>(downstream.node());
-            if (text->caretMinOffset() > 0)
-                deleteText(text, 0, text->caretMinOffset());
-        }
-    }
+    deleteUnrenderedText(downstream.node());
     
     if (pos.node()->inDocument())
         setEndingSelection(pos);
@@ -1042,6 +1047,64 @@ CSSStyleDeclarationImpl *DeleteSelectionCommandImpl::computeTypingStyle(const Po
     return result;
 }
 
+// This function moves nodes in the block containing startNode to dstBlock, starting
+// from startNode and proceeding to the end of the block. Nodes in the block containing
+// startNode that appear in document order before startNode are not moved.
+// This function is an important helper for deleting selections that cross block
+// boundaries.
+void DeleteSelectionCommandImpl::moveNodesToBlock(NodeImpl *startNode, NodeImpl *dstBlock)
+{
+    ASSERT(!dstBlock->isAncestor(startNode));
+
+    NodeImpl *startBlock = startNode->enclosingBlockFlowElement();
+    unsigned long index = dstBlock->childNodeCount();
+    
+    // Fix the right index to use for inserting content in dstBlock, based on the
+    // position of startNode relative to dstBlock.
+    if (startNode->isAncestor(dstBlock)) {
+        NodeImpl *n = startNode;
+        while (n->parentNode() != dstBlock)
+            n = n->parentNode();
+        index = n->nodeIndex();
+    }
+
+    // Do the move.
+    NodeImpl *node = startNode == startBlock ? startBlock->firstChild() : startNode;
+    while (node && (node == startBlock || node->isAncestor(startBlock))) {
+        NodeImpl *moveNode = node;
+        node = node->nextSibling();
+        removeNode(moveNode);
+        insertNodeAt(moveNode, dstBlock, index);
+        index++;
+    }
+
+    // If the startBlock no longer has any kids, we may need to deal with adding a BR
+    // to make the layout come out right. Consider this document:
+    //
+    // One
+    // <div>Two</div>
+    // Three
+    // 
+    // Placing the insertion before before the 'T' of 'Two' and hitting delete will
+    // move the contents of the div to the block containing 'One' and delete the div.
+    // This will have the side effect of moving 'Three' on to the same line as 'One'
+    // and 'Two'. This is undesirable. We fix this up by adding a BR before the 'Three'.
+    // This may not be ideal, but it is better than nothing.
+    if (!startBlock->firstChild()) {
+        removeNode(startBlock);
+        document()->updateLayout();
+        if (index != dstBlock->childNodeCount()) {
+            NodeImpl *node = dstBlock->childNode(index);
+            if (node->renderer() && node->renderer()->inlineBox() && node->renderer()->inlineBox()->prevOnLineExists()) {
+                int exceptionCode = 0;
+                ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
+                ASSERT(exceptionCode == 0);
+                insertNodeBefore(breakNode, node);
+            }
+        }
+    }
+}
+
 void DeleteSelectionCommandImpl::doApply()
 {
     // If selection has not been set to a custom selection when the command was created,
@@ -1053,9 +1116,9 @@ void DeleteSelectionCommandImpl::doApply()
         return;
 
     Position upstreamStart(m_selectionToDelete.start().upstream(StayInBlock));
-    Position downstreamStart(m_selectionToDelete.start().downstream());
+    Position downstreamStart(m_selectionToDelete.start().downstream(StayInBlock));
     Position upstreamEnd(m_selectionToDelete.end().upstream(StayInBlock));
-    Position downstreamEnd(m_selectionToDelete.end().downstream());
+    Position downstreamEnd(m_selectionToDelete.end().downstream(StayInBlock));
     Position endingPosition;
 
     // Save away whitespace situation before doing any deletions
@@ -1097,7 +1160,18 @@ void DeleteSelectionCommandImpl::doApply()
         startOffset = 0;
     }
 
-    if (startNode == downstreamEnd.node()) {
+    if (upstreamStart == upstreamEnd && downstreamStart == downstreamEnd && upstreamStart.node()->isBlockFlow()) {
+        // handle a delete when the selection is a caret placed at the start of a block.
+        ASSERT(startBlock == endBlock);
+        NodeImpl *block = upstreamStart.node()->traversePreviousNode()->enclosingBlockFlowElement();
+        while (block && !block->isContentEditable())
+            block = block->traversePreviousNode()->enclosingBlockFlowElement();
+        if (block && block->rootEditableElement() == upstreamStart.node()->rootEditableElement())
+            startBlock = block;
+        else
+            return; // nothing to do; at the start of a root editable block.
+    }
+    else if (startNode == downstreamEnd.node()) {
         // handle delete in one node
         if (!startNode->renderer() || 
             (startOffset <= startNode->caretMinOffset() && downstreamEnd.offset() >= startNode->caretMaxOffset())) {
@@ -1143,37 +1217,36 @@ void DeleteSelectionCommandImpl::doApply()
             }
         }
 
-        if (upstreamEnd.node() != startNode && upstreamEnd.node()->inDocument() && upstreamEnd.offset() >= upstreamEnd.node()->caretMinOffset()) {
-            if (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset()) {
+        if (downstreamEnd.node() != startNode && downstreamEnd.node()->inDocument() && downstreamEnd.offset() >= downstreamEnd.node()->caretMinOffset()) {
+            if (downstreamEnd.offset() >= downstreamEnd.node()->caretMaxOffset()) {
                 // need to delete whole node
                 // we can get here if this is the last node in the block
-                removeNode(upstreamEnd.node());
+                removeNode(downstreamEnd.node());
                 trailingValid = false;
             }
             else {
                 // in a text node that needs to be trimmed
-                TextImpl *text = static_cast<TextImpl *>(upstreamEnd.node());
-                if (upstreamEnd.offset() > 0) {
-                    deleteText(text, 0, upstreamEnd.offset());
+                TextImpl *text = static_cast<TextImpl *>(downstreamEnd.node());
+                if (downstreamEnd.offset() > 0) {
+                    deleteText(text, 0, downstreamEnd.offset());
                     trailingValid = false;
                 }
             }
-            if (!upstreamStart.node()->inDocument() && upstreamEnd.node()->inDocument())
-                endingPosition = Position(upstreamEnd.node(), 0);
+            if (!downstreamEnd.node()->inDocument() && downstreamEnd.node()->inDocument())
+                endingPosition = Position(downstreamEnd.node(), 0);
         }
     }
     
     // Do block merge if start and end of selection are in different blocks.
-    if (endBlock != startBlock && endBlock->inDocument()) {
+    if (endBlock != startBlock && downstreamEnd.node()->inDocument()) {
         LOG(Editing,  "merging content to start block");
-        NodeImpl *node = endBlock->firstChild();
-        while (node) {
-            NodeImpl *moveNode = node;
-            node = node->nextSibling();
-            removeNode(moveNode);
-            appendNode(moveNode, startBlock);
-        }
-        removeNode(endBlock);
+        // cross blocks and delete unrenderered text from the destination location
+        deleteUnrenderedText(upstreamStart.upstream());
+        // stay in this block and delete unrenderered text from the source location
+        deleteUnrenderedText(upstreamStart);
+        // move the downstream end position's node, and all other nodes in its
+        // block to the start block.
+        moveNodesToBlock(downstreamEnd.node(), startBlock);
     }
       
     // Figure out where the end position should be
index 3cd516fe9b179f3bf8dff0a99646b2d0285145b2..60cb2bb852d4fccee73f4eea65c15ca10567fde5 100644 (file)
@@ -167,6 +167,7 @@ protected:
     void splitTextNode(DOM::TextImpl *text, long offset);
 
     DOM::ElementImpl *applyTypingStyle(DOM::NodeImpl *) const;
+    void deleteUnrenderedText(DOM::NodeImpl *);
     void deleteUnrenderedText(const DOM::Position &pos);
 
     QValueList<EditCommand> m_cmds;
@@ -248,6 +249,7 @@ private:
     void deleteDownstreamWS(const DOM::Position &start);
     bool containsOnlyWhitespace(const DOM::Position &start, const DOM::Position &end);
     DOM::CSSStyleDeclarationImpl *computeTypingStyle(const DOM::Position &pos) const;
+    void moveNodesToBlock(DOM::NodeImpl *startNode, DOM::NodeImpl *dstBlock);
 
     DOM::Selection m_selectionToDelete;
     bool m_hasSelectionToDelete;
index c2f144f7ff55408256c2e262175da30aaf4cf83b..75bba6361a22c8c9896a1c331bd26036489ed183 100644 (file)
@@ -719,7 +719,7 @@ void Selection::validate(ETextGranularity granularity)
     if (start().isEmpty() && end().isEmpty()) {
         m_state = NONE;
     }
-    else if (start() == end() || start().upstream() == end().upstream()) {
+    else if (start() == end() || start().upstream(StayInBlock) == end().upstream(StayInBlock)) {
         m_state = CARET;
     }
     else {
@@ -730,8 +730,8 @@ void Selection::validate(ETextGranularity granularity)
         // purposes of comparing selections). This is an ideal point of the code
         // to do this operation, since all selection changes that result in a RANGE 
         // come through here before anyone uses it.
-        assignStart(start().downstream());
-        assignEnd(end().upstream());
+        assignStart(start().downstream(StayInBlock));
+        assignEnd(end().upstream(StayInBlock));
     }
 
     m_needsCaretLayout = true;
index 4d15094eed8a402d8cd2a0699a2b51d4d8d0828a..60426f19dd1dd87ebe350db1a447ec37b677e871 100644 (file)
@@ -1351,6 +1351,9 @@ ElementImpl *NodeImpl::rootEditableElement() const
         return 0;
 
     NodeImpl *n = const_cast<NodeImpl *>(this);
+    if (n->id() == ID_BODY)
+        return static_cast<ElementImpl *>(n);
+
     NodeImpl *result = n->isEditableBlock() ? n : 0;
     while (1) {
         n = n->parentNode();
index c2f144f7ff55408256c2e262175da30aaf4cf83b..75bba6361a22c8c9896a1c331bd26036489ed183 100644 (file)
@@ -719,7 +719,7 @@ void Selection::validate(ETextGranularity granularity)
     if (start().isEmpty() && end().isEmpty()) {
         m_state = NONE;
     }
-    else if (start() == end() || start().upstream() == end().upstream()) {
+    else if (start() == end() || start().upstream(StayInBlock) == end().upstream(StayInBlock)) {
         m_state = CARET;
     }
     else {
@@ -730,8 +730,8 @@ void Selection::validate(ETextGranularity granularity)
         // purposes of comparing selections). This is an ideal point of the code
         // to do this operation, since all selection changes that result in a RANGE 
         // come through here before anyone uses it.
-        assignStart(start().downstream());
-        assignEnd(end().upstream());
+        assignStart(start().downstream(StayInBlock));
+        assignEnd(end().upstream(StayInBlock));
     }
 
     m_needsCaretLayout = true;