Reviewed by Chris
authorkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Feb 2005 22:18:43 +0000 (22:18 +0000)
committerkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Feb 2005 22:18:43 +0000 (22:18 +0000)
        Fix for this bug:

        <rdar://problem/3986155> Insertion point goes to beginning of doc after deleting

        * khtml/editing/htmlediting.cpp:
        (khtml::DeleteSelectionCommand::setStartNode): New convenience to handle reference counting when setting.
        (khtml::DeleteSelectionCommand::handleGeneralDelete): This contains the crux of the bug fix. Improve tests
        that detect when a selected node needs to be retained, rather than deleted, to preserve the intent of the user.
        This has the side effect of causing the insertion point placement code to succeed rather than fail. Before
        this fix, the failure of the insertion point placement code caused the insertion point to jump to the start
        of the document, which is the symptom that can be perceived by users when editing.
        * khtml/editing/htmlediting.h: Add setStartNode declaration.
        * khtml/editing/visible_units.cpp:
        (khtml::startOfBlock): This function had a stubbed-in non-tested implementation. Implement and
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-001-expected.txt: Added.
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-001.html: Added.
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-002-expected.txt: Added.
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-002.html: Added.
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-003-expected.txt: Added.
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-003.html: Added.
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-004-expected.txt: Added.
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-004.html: Added.
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-005-expected.txt: Added.
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-005.html: Added.
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-006-expected.txt: Added.
        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-006.html: Added.

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

18 files changed:
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-001-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-001.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-002-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-002.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-003-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-003.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-004-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-004.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-005-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-005.html [new file with mode: 0644]
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-006-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/delete-at-paragraph-boundaries-006.html [new file with mode: 0644]
WebCore/ChangeLog-2005-08-23
WebCore/khtml/editing/SelectionController.cpp
WebCore/khtml/editing/htmlediting.cpp
WebCore/khtml/editing/htmlediting.h
WebCore/khtml/editing/selection.cpp
WebCore/khtml/editing/visible_units.cpp

diff --git a/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-001-expected.txt b/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-001-expected.txt
new file mode 100644 (file)
index 0000000..ecc7fb9
--- /dev/null
@@ -0,0 +1,34 @@
+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 784x240 [border: (2px solid #0000FF)]
+        RenderBlock {DIV} at (14,14) size 756x84
+          RenderText {TEXT} at (0,0) size 67x28
+            text run at (0,0) width 67: "Tests: "
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 742x56
+            text run at (0,28) width 742: "Deleting when a selection starts in a blank line created by a block with a BR"
+            text run at (0,56) width 491: "placeholder in it and extends to the end of a block."
+        RenderBlock {DIV} at (14,114) size 756x112
+          RenderText {TEXT} at (0,0) size 189x28
+            text run at (0,0) width 189: "Expected Results: "
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 734x84
+            text run at (0,28) width 730: "Should see the three lines in the red box. First line should be \"A\". Next two"
+            text run at (0,56) width 734: "lines should be empty. Insertion point should be blinking on the second line"
+            text run at (0,84) width 198: "(the first blank one)."
+      RenderBlock {DIV} at (0,264) size 784x88
+        RenderBlock {DIV} at (0,0) size 784x88 [border: (2px solid #FF0000)]
+          RenderBlock {P} at (2,2) size 780x28
+            RenderText {TEXT} at (0,0) size 17x28
+              text run at (0,0) width 17: "A"
+          RenderBlock {P} at (2,30) size 780x28
+            RenderBR {BR} at (0,0) size 0x28
+          RenderBlock {P} at (2,58) size 780x28
+            RenderBR {BR} at (0,0) size 0x28
+selection is CARET:
+start:      position 0 of child 1 {BR} of child 4 {P} of child 1 {DIV} of root {DIV}
+upstream:   position 0 of child 4 {P} of child 1 {DIV} of root {DIV}
+downstream: position 0 of child 1 {BR} of child 4 {P} of child 1 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-001.html b/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-001.html
new file mode 100644 (file)
index 0000000..79a811c
--- /dev/null
@@ -0,0 +1,64 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    font-size: 24px; 
+}
+.explanation { 
+    border: 2px solid blue; 
+    padding: 12px; 
+    font-size: 24px; 
+    margin-bottom: 24px;
+}
+.scenario { margin-bottom: 16px;}
+.scenario:first-line { font-weight: bold; margin-bottom: 16px;}
+.expected-results:first-line { font-weight: bold }
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    moveSelectionForwardByLineCommand();
+    extendSelectionForwardByLineCommand();
+    extendSelectionForwardByCharacterCommand();
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+
+<div class="explanation">
+<div class="scenario">
+Tests: 
+<br>
+Deleting when a selection starts in a blank line created by a block with a BR placeholder in it and extends to the end of a block.
+</div>
+<div class="expected-results">
+Expected Results:
+<br>
+Should see the three lines in the red box. First line should be "A". Next two lines should be empty. Insertion point should
+be blinking on the second line (the first blank one).
+</div>
+</div>
+
+<div contenteditable id="root" style="word-wrap: break-word; -khtml-nbsp-mode: space; -khtml-line-break: after-white-space;">
+<div id="test" class="editing">
+<p style="margin-top: 0; margin-bottom: 0">A</p>
+<p style="margin-top: 0; margin-bottom: 0"><BR class="khtml-block-placeholder"></p>
+<p style="margin-top: 0; margin-bottom: 0">A</p>
+<p style="margin-top: 0; margin-bottom: 0"><BR class="khtml-block-placeholder"></p>
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-002-expected.txt b/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-002-expected.txt
new file mode 100644 (file)
index 0000000..c39a701
--- /dev/null
@@ -0,0 +1,35 @@
+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 784x240 [border: (2px solid #0000FF)]
+        RenderBlock {DIV} at (14,14) size 756x84
+          RenderText {TEXT} at (0,0) size 67x28
+            text run at (0,0) width 67: "Tests: "
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 741x56
+            text run at (0,28) width 741: "Deleting when a selection starts in a blank line created by a BR element and"
+            text run at (0,56) width 285: "extends to the end of a block."
+        RenderBlock {DIV} at (14,114) size 756x112
+          RenderText {TEXT} at (0,0) size 189x28
+            text run at (0,0) width 189: "Expected Results: "
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 734x84
+            text run at (0,28) width 730: "Should see the three lines in the red box. First line should be \"A\". Next two"
+            text run at (0,56) width 734: "lines should be empty. Insertion point should be blinking on the second line"
+            text run at (0,84) width 198: "(the first blank one)."
+      RenderBlock {DIV} at (0,264) size 784x88
+        RenderBlock {DIV} at (0,0) size 784x88 [border: (2px solid #FF0000)]
+          RenderBlock {P} at (2,2) size 780x28
+            RenderText {TEXT} at (0,0) size 17x28
+              text run at (0,0) width 17: "A"
+          RenderBlock {P} at (2,30) size 780x28
+            RenderBR {BR} at (0,0) size 0x28
+          RenderBlock (anonymous) at (2,58) size 780x0
+          RenderBlock {P} at (2,58) size 780x28
+            RenderBR {BR} at (0,0) size 0x28
+selection is CARET:
+start:      position 0 of child 1 {BR} of child 3 {P} of child 1 {DIV} of root {DIV}
+upstream:   position 0 of child 3 {P} of child 1 {DIV} of root {DIV}
+downstream: position 0 of child 1 {BR} of child 3 {P} of child 1 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-002.html b/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-002.html
new file mode 100644 (file)
index 0000000..ae0e277
--- /dev/null
@@ -0,0 +1,64 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    font-size: 24px; 
+}
+.explanation { 
+    border: 2px solid blue; 
+    padding: 12px; 
+    font-size: 24px; 
+    margin-bottom: 24px;
+}
+.scenario { margin-bottom: 16px;}
+.scenario:first-line { font-weight: bold; margin-bottom: 16px;}
+.expected-results:first-line { font-weight: bold }
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    moveSelectionForwardByLineCommand();
+    extendSelectionForwardByLineCommand();
+    extendSelectionForwardByCharacterCommand();
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+
+<div class="explanation">
+<div class="scenario">
+Tests: 
+<br>
+Deleting when a selection starts in a blank line created by a BR element and extends to the end of a block.
+</div>
+<div class="expected-results">
+Expected Results:
+<br>
+Should see the three lines in the red box. First line should be "A". Next two lines should be empty. Insertion point should
+be blinking on the second line (the first blank one).
+</div>
+</div>
+
+<div contenteditable id="root" style="word-wrap: break-word; -khtml-nbsp-mode: space; -khtml-line-break: after-white-space;">
+<div id="test" class="editing">
+<p style="margin-top: 0; margin-bottom: 0">A</p>
+<BR>
+<p style="margin-top: 0; margin-bottom: 0">A</p>
+<p style="margin-top: 0; margin-bottom: 0"><BR class="khtml-block-placeholder"></p>
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-003-expected.txt b/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-003-expected.txt
new file mode 100644 (file)
index 0000000..302ff81
--- /dev/null
@@ -0,0 +1,33 @@
+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 784x240 [border: (2px solid #0000FF)]
+        RenderBlock {DIV} at (14,14) size 756x112
+          RenderText {TEXT} at (0,0) size 67x28
+            text run at (0,0) width 67: "Tests: "
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 742x84
+            text run at (0,28) width 742: "Deleting when a selection starts in a blank line created by a block with a BR"
+            text run at (0,56) width 730: "placeholder in it and extends to a character that is not at the end of a block."
+            text run at (0,84) width 735: "This ensures that some of our \"special-case\" code does not run for this case."
+        RenderBlock {DIV} at (14,142) size 756x84
+          RenderText {TEXT} at (0,0) size 189x28
+            text run at (0,0) width 189: "Expected Results: "
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 682x56
+            text run at (0,28) width 682: "Should see the two lines in the red box. Each should contain \"A\" only."
+            text run at (0,56) width 620: "Insertion point should be blinking at the start of the second line."
+      RenderBlock {DIV} at (0,264) size 784x60
+        RenderBlock {DIV} at (0,0) size 784x60 [border: (2px solid #FF0000)]
+          RenderBlock {P} at (2,2) size 780x28
+            RenderText {TEXT} at (0,0) size 17x28
+              text run at (0,0) width 17: "A"
+          RenderBlock {P} at (2,30) size 780x28
+            RenderText {TEXT} at (0,0) size 17x28
+              text run at (0,0) width 17: "A"
+selection is CARET:
+start:      position 0 of child 1 {TEXT} of child 4 {P} of child 1 {DIV} of root {DIV}
+upstream:   position 0 of child 4 {P} of child 1 {DIV} of root {DIV}
+downstream: position 0 of child 1 {TEXT} of child 4 {P} of child 1 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-003.html b/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-003.html
new file mode 100644 (file)
index 0000000..7e99047
--- /dev/null
@@ -0,0 +1,64 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    font-size: 24px; 
+}
+.explanation { 
+    border: 2px solid blue; 
+    padding: 12px; 
+    font-size: 24px; 
+    margin-bottom: 24px;
+}
+.scenario { margin-bottom: 16px;}
+.scenario:first-line { font-weight: bold; margin-bottom: 16px;}
+.expected-results:first-line { font-weight: bold }
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    moveSelectionForwardByLineCommand();
+    extendSelectionForwardByLineCommand();
+    extendSelectionForwardByCharacterCommand();
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+
+<div class="explanation">
+<div class="scenario">
+Tests: 
+<br>
+Deleting when a selection starts in a blank line created by a block with a BR placeholder in it and extends to a character
+that is not at the end of a block. This ensures that some of our "special-case" code does not run for this case.
+</div>
+<div class="expected-results">
+Expected Results:
+<br>
+Should see the two lines in the red box. Each should contain "A" only. Insertion point should
+be blinking at the start of the second line.
+</div>
+</div>
+
+<div contenteditable id="root" style="word-wrap: break-word; -khtml-nbsp-mode: space; -khtml-line-break: after-white-space;">
+<div id="test" class="editing">
+<p style="margin-top: 0; margin-bottom: 0">A</p>
+<p style="margin-top: 0; margin-bottom: 0"><BR class="khtml-block-placeholder"></p>
+<p style="margin-top: 0; margin-bottom: 0">AA</p>
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-004-expected.txt b/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-004-expected.txt
new file mode 100644 (file)
index 0000000..d9792a6
--- /dev/null
@@ -0,0 +1,34 @@
+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 784x240 [border: (2px solid #0000FF)]
+        RenderBlock {DIV} at (14,14) size 756x112
+          RenderText {TEXT} at (0,0) size 67x28
+            text run at (0,0) width 67: "Tests: "
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 751x84
+            text run at (0,28) width 741: "Deleting when a selection starts in a blank line created by a BR element and"
+            text run at (0,56) width 751: "extends to a character that is not at the end of a block. This ensures that some"
+            text run at (0,84) width 508: "of our \"special-case\" code does not run for this case."
+        RenderBlock {DIV} at (14,142) size 756x84
+          RenderText {TEXT} at (0,0) size 189x28
+            text run at (0,0) width 189: "Expected Results: "
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 682x56
+            text run at (0,28) width 682: "Should see the two lines in the red box. Each should contain \"A\" only."
+            text run at (0,56) width 620: "Insertion point should be blinking at the start of the second line."
+      RenderBlock {DIV} at (0,264) size 784x60
+        RenderBlock {DIV} at (0,0) size 784x60 [border: (2px solid #FF0000)]
+          RenderBlock {P} at (2,2) size 780x28
+            RenderText {TEXT} at (0,0) size 17x28
+              text run at (0,0) width 17: "A"
+          RenderBlock (anonymous) at (2,30) size 780x0
+          RenderBlock {P} at (2,30) size 780x28
+            RenderText {TEXT} at (0,0) size 17x28
+              text run at (0,0) width 17: "A"
+selection is CARET:
+start:      position 0 of child 1 {TEXT} of child 3 {P} of child 1 {DIV} of root {DIV}
+upstream:   position 0 of child 3 {P} of child 1 {DIV} of root {DIV}
+downstream: position 0 of child 1 {TEXT} of child 3 {P} of child 1 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-004.html b/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-004.html
new file mode 100644 (file)
index 0000000..0e51373
--- /dev/null
@@ -0,0 +1,64 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    font-size: 24px; 
+}
+.explanation { 
+    border: 2px solid blue; 
+    padding: 12px; 
+    font-size: 24px; 
+    margin-bottom: 24px;
+}
+.scenario { margin-bottom: 16px;}
+.scenario:first-line { font-weight: bold; margin-bottom: 16px;}
+.expected-results:first-line { font-weight: bold }
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    moveSelectionForwardByLineCommand();
+    extendSelectionForwardByLineCommand();
+    extendSelectionForwardByCharacterCommand();
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+
+<div class="explanation">
+<div class="scenario">
+Tests: 
+<br>
+Deleting when a selection starts in a blank line created by a BR element and extends to a character
+that is not at the end of a block. This ensures that some of our "special-case" code does not run for this case.
+</div>
+<div class="expected-results">
+Expected Results:
+<br>
+Should see the two lines in the red box. Each should contain "A" only. Insertion point should
+be blinking at the start of the second line.
+</div>
+</div>
+
+<div contenteditable id="root" style="word-wrap: break-word; -khtml-nbsp-mode: space; -khtml-line-break: after-white-space;">
+<div id="test" class="editing">
+<p style="margin-top: 0; margin-bottom: 0">A</p>
+<BR>
+<p style="margin-top: 0; margin-bottom: 0">AA</p>
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-005-expected.txt b/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-005-expected.txt
new file mode 100644 (file)
index 0000000..65bc6df
--- /dev/null
@@ -0,0 +1,32 @@
+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 784x240 [border: (2px solid #0000FF)]
+        RenderBlock {DIV} at (14,14) size 756x84
+          RenderText {TEXT} at (0,0) size 67x28
+            text run at (0,0) width 67: "Tests: "
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 741x56
+            text run at (0,28) width 741: "Deleting when a selection starts in a blank line created by a BR element and"
+            text run at (0,56) width 346: "extends to the end of the document."
+        RenderBlock {DIV} at (14,114) size 756x112
+          RenderText {TEXT} at (0,0) size 189x28
+            text run at (0,0) width 189: "Expected Results: "
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 743x84
+            text run at (0,28) width 716: "Should see the two lines in the red box. First line should be \"A\". Next one"
+            text run at (0,56) width 743: "should be empty. Insertion point should be blinking at the start of the second"
+            text run at (0,84) width 43: "line."
+      RenderBlock {DIV} at (0,264) size 784x60
+        RenderBlock {DIV} at (0,0) size 784x60 [border: (2px solid #FF0000)]
+          RenderBlock {P} at (2,2) size 780x28
+            RenderText {TEXT} at (0,0) size 17x28
+              text run at (0,0) width 17: "A"
+          RenderBlock (anonymous) at (2,30) size 780x28
+            RenderBR {BR} at (0,0) size 0x28
+selection is CARET:
+start:      position 0 of child 3 {BR} of child 1 {DIV} of root {DIV}
+upstream:   position 0 of child 3 {BR} of child 1 {DIV} of root {DIV}
+downstream: position 0 of child 3 {BR} of child 1 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-005.html b/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-005.html
new file mode 100644 (file)
index 0000000..eb4c462
--- /dev/null
@@ -0,0 +1,64 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    font-size: 24px; 
+}
+.explanation { 
+    border: 2px solid blue; 
+    padding: 12px; 
+    font-size: 24px; 
+    margin-bottom: 24px;
+}
+.scenario { margin-bottom: 16px;}
+.scenario:first-line { font-weight: bold; margin-bottom: 16px;}
+.expected-results:first-line { font-weight: bold }
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    moveSelectionForwardByLineCommand();
+    extendSelectionForwardByLineCommand();
+    extendSelectionForwardByLineCommand();
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+
+<div class="explanation">
+<div class="scenario">
+Tests: 
+<br>
+Deleting when a selection starts in a blank line created by a BR element and extends to the end of the document. 
+</div>
+<div class="expected-results">
+Expected Results:
+<br>
+Should see the two lines in the red box. First line should be "A". Next one should be empty. Insertion point should
+be blinking at the start of the second line.
+</div>
+</div>
+
+<div contenteditable id="root" style="word-wrap: break-word; -khtml-nbsp-mode: space; -khtml-line-break: after-white-space;">
+<div id="test" class="editing">
+<p style="margin-top: 0; margin-bottom: 0">A</p>
+<BR>
+<p style="margin-top: 0; margin-bottom: 0">A</p>
+<BR>
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-006-expected.txt b/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-006-expected.txt
new file mode 100644 (file)
index 0000000..2c8e667
--- /dev/null
@@ -0,0 +1,32 @@
+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 784x240 [border: (2px solid #0000FF)]
+        RenderBlock {DIV} at (14,14) size 756x84
+          RenderText {TEXT} at (0,0) size 67x28
+            text run at (0,0) width 67: "Tests: "
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 742x56
+            text run at (0,28) width 742: "Deleting when a selection starts in a blank line created by a block with a BR"
+            text run at (0,56) width 552: "placeholder in it and extends to the end of the document."
+        RenderBlock {DIV} at (14,114) size 756x112
+          RenderText {TEXT} at (0,0) size 189x28
+            text run at (0,0) width 189: "Expected Results: "
+          RenderBR {BR} at (0,0) size 0x0
+          RenderText {TEXT} at (0,28) size 743x84
+            text run at (0,28) width 716: "Should see the two lines in the red box. First line should be \"A\". Next one"
+            text run at (0,56) width 743: "should be empty. Insertion point should be blinking at the start of the second"
+            text run at (0,84) width 43: "line."
+      RenderBlock {DIV} at (0,264) size 784x60
+        RenderBlock {DIV} at (0,0) size 784x60 [border: (2px solid #FF0000)]
+          RenderBlock {P} at (2,2) size 780x28
+            RenderText {TEXT} at (0,0) size 17x28
+              text run at (0,0) width 17: "A"
+          RenderBlock {P} at (2,30) size 780x28
+            RenderBR {BR} at (0,0) size 0x28
+selection is CARET:
+start:      position 0 of child 1 {BR} of child 4 {P} of child 1 {DIV} of root {DIV}
+upstream:   position 0 of child 4 {P} of child 1 {DIV} of root {DIV}
+downstream: position 0 of child 1 {BR} of child 4 {P} of child 1 {DIV} of root {DIV}
diff --git a/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-006.html b/LayoutTests/editing/deleting/delete-at-paragraph-boundaries-006.html
new file mode 100644 (file)
index 0000000..3355ee7
--- /dev/null
@@ -0,0 +1,64 @@
+<html> 
+<head>
+
+<style>
+.editing { 
+    border: 2px solid red; 
+    font-size: 24px; 
+}
+.explanation { 
+    border: 2px solid blue; 
+    padding: 12px; 
+    font-size: 24px; 
+    margin-bottom: 24px;
+}
+.scenario { margin-bottom: 16px;}
+.scenario:first-line { font-weight: bold; margin-bottom: 16px;}
+.expected-results:first-line { font-weight: bold }
+</style>
+<script src=../editing.js language="JavaScript" type="text/JavaScript" ></script>
+
+<script>
+
+function editingTest() {
+    moveSelectionForwardByLineCommand();
+    extendSelectionForwardByLineCommand();
+    extendSelectionForwardByLineCommand();
+    deleteCommand();
+}
+
+</script>
+
+<title>Editing Test</title> 
+</head> 
+<body>
+
+<div class="explanation">
+<div class="scenario">
+Tests: 
+<br>
+Deleting when a selection starts in a blank line created by a block with a BR placeholder in it and extends to the end of the document. 
+</div>
+<div class="expected-results">
+Expected Results:
+<br>
+Should see the two lines in the red box. First line should be "A". Next one should be empty. Insertion point should
+be blinking at the start of the second line.
+</div>
+</div>
+
+<div contenteditable id="root" style="word-wrap: break-word; -khtml-nbsp-mode: space; -khtml-line-break: after-white-space;">
+<div id="test" class="editing">
+<p style="margin-top: 0; margin-bottom: 0">A</p>
+<p style="margin-top: 0; margin-bottom: 0"><BR class="khtml-block-placeholder"></p>
+<p style="margin-top: 0; margin-bottom: 0">A</p>
+<p style="margin-top: 0; margin-bottom: 0"><BR class="khtml-block-placeholder"></p>
+</div>
+</div>
+
+<script>
+runEditingTest();
+</script>
+
+</body>
+</html>
index e7e571edb429fb85f1bc4aa0726573cc2f88d570..1657f026814a507982455954117323cea1d710a0 100644 (file)
@@ -1,3 +1,34 @@
+2005-02-03  Ken Kocienda  <kocienda@apple.com>
+
+        Reviewed by Chris
+
+        Fix for this bug:
+        
+        <rdar://problem/3986155> Insertion point goes to beginning of doc after deleting
+
+        * khtml/editing/htmlediting.cpp:
+        (khtml::DeleteSelectionCommand::setStartNode): New convenience to handle reference counting when setting.
+        (khtml::DeleteSelectionCommand::handleGeneralDelete): This contains the crux of the bug fix. Improve tests
+        that detect when a selected node needs to be retained, rather than deleted, to preserve the intent of the user.
+        This has the side effect of causing the insertion point placement code to succeed rather than fail. Before
+        this fix, the failure of the insertion point placement code caused the insertion point to jump to the start
+        of the document, which is the symptom that can be perceived by users when editing.
+        * khtml/editing/htmlediting.h: Add setStartNode declaration.
+        * khtml/editing/visible_units.cpp:
+        (khtml::startOfBlock): This function had a stubbed-in non-tested implementation. Implement and 
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-001-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-001.html: Added.
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-002-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-002.html: Added.
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-003-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-003.html: Added.
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-004-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-004.html: Added.
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-005-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-005.html: Added.
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-006-expected.txt: Added.
+        * layout-tests/editing/deleting/delete-at-paragraph-boundaries-006.html: Added.
+
 === Safari-183 ===
 
 2005-02-03  Richard Williamson   <rjw@apple.com>
index b0eeee4f6dec33a3737a3596a514f1100903ae82..26894c7789d7df0874a6403d41f41d6e459ae4a4 100644 (file)
@@ -871,11 +871,16 @@ void Selection::validate(ETextGranularity granularity)
                 // However, the end of the document is an exception and always selects the previous word even though it could be
                 // both the start of a line and after a hard line break.
                 VisiblePosition pos(m_base);
-                EWordSide side = LeftWordIfOnBoundary;
-                if ((isEndOfParagraph(pos) || isStartOfLine(pos, m_affinity)) && !isEndOfDocument(pos))
-                    side = RightWordIfOnBoundary;
-                m_start = startOfWord(pos, side).deepEquivalent();
-                m_end = endOfWord(pos, side).deepEquivalent();
+                if (isStartOfBlock(pos) && isEndOfBlock(pos)) {
+                    m_start = m_end = m_base;
+                }
+                else {
+                    EWordSide side = LeftWordIfOnBoundary;
+                    //if ((isEndOfParagraph(pos) || isStartOfLine(pos, m_affinity)) && !isEndOfDocument(pos))
+                    //    side = RightWordIfOnBoundary;
+                    m_start = startOfWord(pos, side).deepEquivalent();
+                    m_end = endOfWord(pos, side).deepEquivalent();
+                }
             } else if (m_baseIsStart) {
                 m_start = startOfWord(VisiblePosition(m_base)).deepEquivalent();
                 m_end = endOfWord(VisiblePosition(m_extent)).deepEquivalent();
index 8da943c35ef4a1b330f6febca558d6b268106987..bf85de750b32678b219533134eb078babf434740 100644 (file)
@@ -2338,42 +2338,55 @@ bool DeleteSelectionCommand::handleSpecialCaseBRDelete()
     return false;
 }
 
+void DeleteSelectionCommand::setStartNode(NodeImpl *node)
+{
+    NodeImpl *old = m_startNode;
+    m_startNode = node;
+    if (m_startNode)
+        m_startNode->ref();
+    if (old)
+        old->deref();
+}
+
 void DeleteSelectionCommand::handleGeneralDelete()
 {
     int startOffset = m_upstreamStart.offset();
-
-    if (startOffset == 0 && m_startNode->isBlockFlow() && m_startBlock != m_endBlock && !m_endBlock->isAncestor(m_startBlock)) {
-        // The block containing the start of the selection is completely selected. 
-        // See if it can be deleted in one step right here.
-        ASSERT(!m_downstreamEnd.node()->isAncestor(m_startNode));
-
-        // The next few lines help us deal with a bit of a quirk.
-        //     1. Open a new Blot or Mail document
-        //     2. hit Return ten times or so
-        //     3. Type a letter (do not hit Return after it)
-        //     4. Type shift-up-arrow to select the line containing the letter and the previous blank line
-        //     5. Hit Delete
-        // You expect the insertion point to wind up at the start of the line where your selection began.
-        // Because of the nature of HTML, the editing code needs to perform a special check to get
-        // this behavior. So:
-        // If the entire start block is selected, and
-        //     a) the selection does not extend to the end of the document, then delete the start block, otherwise
-        //     b) the selection extends to the end of the document, then do not delete the start block.
-        //
-        NodeImpl *old = m_startNode;
-        VisiblePosition visibleEnd = VisiblePosition(m_downstreamEnd);
-        if (isEndOfDocument(visibleEnd) && !isFirstVisiblePositionOnLine(visibleEnd)) {
-            m_startNode = m_startBlock->firstChild();
+    VisiblePosition visibleEnd = VisiblePosition(m_downstreamEnd);
+    bool endAtEndOfBlock = isEndOfBlock(visibleEnd);
+
+    // Handle some special cases where the selection begins and ends on specific visible units.
+    // Sometimes a node that is actually selected needs to be retained in order to maintain
+    // user expectations for the delete operation. Here is an example:
+    //     1. Open a new Blot or Mail document
+    //     2. hit Return ten times or so
+    //     3. Type a letter (do not hit Return after it)
+    //     4. Type shift-up-arrow to select the line containing the letter and the previous blank line
+    //     5. Hit Delete
+    // You expect the insertion point to wind up at the start of the line where your selection began.
+    // Because of the nature of HTML, the editing code needs to perform a special check to get
+    // this behavior. So:
+    // If the entire start block is selected, and the selection does not extend to the end of the 
+    // end of a block other than the block containing the selection start, then do not delete the 
+    // start block, otherwise delete the start block.
+    // A similar case is provided to cover selections starting in BR elements.
+    if (startOffset == 1 && m_startNode->id() == ID_BR) {
+        setStartNode(m_startNode->traverseNextNode());
+        startOffset = 0;
+    }
+    if (m_startBlock != m_endBlock && startOffset == 0 && m_startNode->id() == ID_BR && endAtEndOfBlock) {
+        // Don't delete the BR element
+        setStartNode(m_startNode->traverseNextNode());
+    }
+    else if (m_startBlock != m_endBlock && isStartOfBlock(VisiblePosition(m_upstreamStart))) {
+        if (!isStartOfBlock(visibleEnd) && endAtEndOfBlock) {
+            // Delete all the children of the block, but not the block itself.
+            setStartNode(m_startBlock->firstChild());
         }
         else {
-            // shift the start node to the start of the next block.
-            m_startNode = m_startBlock->traverseNextSibling();
+            // The whole block can be deleted.
+            setStartNode(m_startBlock->traverseNextSibling());
             removeFullySelectedNode(m_startBlock);
         }
-
-        if (m_startNode)
-            m_startNode->ref();
-        old->deref();
         startOffset = 0;
     }
     else if (startOffset >= m_startNode->caretMaxOffset()) {
@@ -2388,10 +2401,7 @@ void DeleteSelectionCommand::handleGeneralDelete()
         }
         
         // shift the start node to the next
-        NodeImpl *old = m_startNode;
-        m_startNode = old->traverseNextNode();
-        m_startNode->ref();
-        old->deref();
+        setStartNode(m_startNode->traverseNextNode());
         startOffset = 0;
     }
 
index e90a946c6fc35f392986d11cca14e964c5134662..72f49d1fe13403e52432dd9228bbb45f7e937f32 100644 (file)
@@ -357,6 +357,8 @@ private:
     void calculateTypingStyleAfterDelete(bool insertedPlaceholder);
     void clearTransientState();
 
+    void setStartNode(DOM::NodeImpl *);
+
     bool m_hasSelectionToDelete;
     bool m_smartDelete;
     bool m_mergeBlocksAfterDelete;
index b0eeee4f6dec33a3737a3596a514f1100903ae82..26894c7789d7df0874a6403d41f41d6e459ae4a4 100644 (file)
@@ -871,11 +871,16 @@ void Selection::validate(ETextGranularity granularity)
                 // However, the end of the document is an exception and always selects the previous word even though it could be
                 // both the start of a line and after a hard line break.
                 VisiblePosition pos(m_base);
-                EWordSide side = LeftWordIfOnBoundary;
-                if ((isEndOfParagraph(pos) || isStartOfLine(pos, m_affinity)) && !isEndOfDocument(pos))
-                    side = RightWordIfOnBoundary;
-                m_start = startOfWord(pos, side).deepEquivalent();
-                m_end = endOfWord(pos, side).deepEquivalent();
+                if (isStartOfBlock(pos) && isEndOfBlock(pos)) {
+                    m_start = m_end = m_base;
+                }
+                else {
+                    EWordSide side = LeftWordIfOnBoundary;
+                    //if ((isEndOfParagraph(pos) || isStartOfLine(pos, m_affinity)) && !isEndOfDocument(pos))
+                    //    side = RightWordIfOnBoundary;
+                    m_start = startOfWord(pos, side).deepEquivalent();
+                    m_end = endOfWord(pos, side).deepEquivalent();
+                }
             } else if (m_baseIsStart) {
                 m_start = startOfWord(VisiblePosition(m_base)).deepEquivalent();
                 m_end = endOfWord(VisiblePosition(m_extent)).deepEquivalent();
index 3b9976e81fe4e646cbb38036f124c2e60b8b1476..df97d6d9f8bd702f4db5bb39ac98880c6cf68b4a 100644 (file)
@@ -53,9 +53,16 @@ static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*sea
     NodeImpl *de = d->documentElement();
     if (!de)
         return VisiblePosition();
+    NodeImpl *boundary = n->enclosingBlockFlowElement();
+    if (!boundary)
+        return VisiblePosition();
+    bool isContentEditable = boundary->isContentEditable();
+    while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable()) {
+        boundary = boundary->parentNode();
+    }
 
     Range searchRange(d);
-    searchRange.setStartBefore(de);
+    searchRange.setStartBefore(boundary);
     Position end(pos.equivalentRangeCompliantPosition());
     searchRange.setEnd(end.node(), end.offset());
     SimplifiedBackwardsTextIterator it(searchRange);
@@ -122,11 +129,18 @@ static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchF
     NodeImpl *de = d->documentElement();
     if (!de)
         return VisiblePosition();
+    NodeImpl *boundary = n->enclosingBlockFlowElement();
+    if (!boundary)
+        return VisiblePosition();
+    bool isContentEditable = boundary->isContentEditable();
+    while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable()) {
+        boundary = boundary->parentNode();
+    }
 
     Range searchRange(d);
     Position start(pos.equivalentRangeCompliantPosition());
     searchRange.setStart(start.node(), start.offset());
-    searchRange.setEndAfter(de);
+    searchRange.setEndAfter(boundary);
     TextIterator it(searchRange, RUNFINDER);
     QString string;
     unsigned next = 0;
@@ -437,6 +451,8 @@ VisiblePosition endOfParagraph(const VisiblePosition &c, EIncludeLineBreak inclu
     long offset = p.offset();
 
     for (NodeImpl *n = startNode; n; n = n->traverseNextNode(stayInsideBlock)) {
+        if (n->isContentEditable() != startNode->isContentEditable())
+            break;
         RenderObject *r = n->renderer();
         if (!r)
             continue;
@@ -520,38 +536,13 @@ VisiblePosition nextParagraphPosition(const VisiblePosition &p, EAffinity affini
 
 // ---------
 
-// written, but not yet tested
 VisiblePosition startOfBlock(const VisiblePosition &c)
 {
     Position p = c.deepEquivalent();
     NodeImpl *startNode = p.node();
     if (!startNode)
         return VisiblePosition();
-
-    NodeImpl *startBlock = startNode->enclosingBlockFlowElement();
-
-    NodeImpl *node = startNode;
-    long offset = p.offset();
-
-    for (NodeImpl *n = startNode; n; n = n->traversePreviousNodePostOrder(startBlock)) {
-        RenderObject *r = n->renderer();
-        if (!r)
-            continue;
-        RenderStyle *style = r->style();
-        if (style->visibility() != VISIBLE)
-            continue;
-        if (r->isBlockFlow())
-            break;
-        if (r->isText()) {
-            node = n;
-            offset = 0;
-        } else if (r->isReplaced()) {
-            node = n;
-            offset = 0;
-        }
-    }
-
-    return VisiblePosition(node, offset);
+    return VisiblePosition(Position(startNode->enclosingBlockFlowElement(), 0));
 }
 
 // written, but not yet tested