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}
<script>
function editingTest() {
- for (i = 0; i < 5; i++) {
- extendSelectionForwardByCharacterCommand();
+ for (i = 0; i < 4; i++) {
+ moveSelectionForwardByCharacterCommand();
}
deleteCommand();
}
</head>
<body>
<div contenteditable id="root" class="editing">
-Test
-<div>Test</div>
+<div id="test">
+One
+<div>Two</div>
+</div>
</div>
<script>
--- /dev/null
+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}
--- /dev/null
+<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>
--- /dev/null
+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}
--- /dev/null
+<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>
--- /dev/null
+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}
--- /dev/null
+<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>
--- /dev/null
+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}
--- /dev/null
+<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>
--- /dev/null
+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}
--- /dev/null
+<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>
--- /dev/null
+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}
--- /dev/null
+<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>
--- /dev/null
+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}
--- /dev/null
+<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>
--- /dev/null
+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}
--- /dev/null
+<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>
--- /dev/null
+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}
--- /dev/null
+<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>
--- /dev/null
+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}
--- /dev/null
+<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>
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}
<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();
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}
+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.
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 {
// 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;
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);
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,
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
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())) {
}
}
- 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
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;
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;
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 {
// 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;
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();
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 {
// 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;