Bug 462188. Make nsHTMLEditRules::WillDeleteSelection extend the selection before checking whether there's anything to delete. r+sr=peterv.
authorTheppitak Karoonboonyanan <thep@linux.thai.net>
Tue, 27 Jan 2009 13:50:32 +0100
changeset 24233 11e029835944
parent 24232 4a34b7ff26e2
child 24234 ebde0d5039ee
push id4993
push userpvanderbeken@mozilla.com
push date2009-01-27 12:50 +0000
treeherdermozilla-central@11e029835944 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs462188
milestone1.9.2a1pre
Bug 462188. Make nsHTMLEditRules::WillDeleteSelection extend the selection before checking whether there's anything to delete. r+sr=peterv.
editor/libeditor/html/nsHTMLEditRules.cpp
layout/generic/test/Makefile.in
layout/generic/test/test_backspace_delete.html
layout/generic/test/test_backspace_delete.xul
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -1943,20 +1943,34 @@ nsHTMLEditRules::WillDeleteSelection(nsI
     if (NS_FAILED(res)) return res;
     if (*aHandled) return NS_OK;
         
     // Test for distance between caret and text that will be deleted
     res = CheckBidiLevelForDeletion(aSelection, startNode, startOffset, aAction, aCancel);
     if (NS_FAILED(res)) return res;
     if (*aCancel) return NS_OK;
 
+    res = mHTMLEditor->ExtendSelectionForDelete(aSelection, &aAction);
+    NS_ENSURE_SUCCESS(res, res);
+
     // We should delete nothing.
     if (aAction == nsIEditor::eNone)
       return NS_OK;
 
+    // ExtendSelectionForDelete() may have changed the selection, update it
+    res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(startNode), &startOffset);
+    if (NS_FAILED(res)) return res;
+    if (!startNode) return NS_ERROR_FAILURE;
+    
+    res = aSelection->GetIsCollapsed(&bCollapsed);
+    if (NS_FAILED(res)) return res;
+  }
+
+  if (bCollapsed)
+  {
     // what's in the direction we are deleting?
     nsWSRunObject wsObj(mHTMLEditor, startNode, startOffset);
     nsCOMPtr<nsIDOMNode> visNode;
     PRInt32 visOffset;
     PRInt16 wsType;
 
     // find next visible node
     if (aAction == nsIEditor::eNext)
@@ -1991,35 +2005,34 @@ nsHTMLEditRules::WillDeleteSelection(nsI
       if (aAction == nsIEditor::ePrevious) 
       { 
         if (so == 0) return NS_ERROR_UNEXPECTED;
         so--; 
         eo--; 
       }
       else
       {
-        res = mHTMLEditor->ExtendSelectionForDelete(aSelection, &aAction);
-        NS_ENSURE_SUCCESS(res, res);
-
         nsCOMPtr<nsIDOMRange> range;
         res = aSelection->GetRangeAt(0, getter_AddRefs(range));
         NS_ENSURE_SUCCESS(res, res);
 
 #ifdef DEBUG
         nsIDOMNode *container;
 
         res = range->GetStartContainer(&container);
         NS_ENSURE_SUCCESS(res, res);
         NS_ASSERTION(container == visNode, "selection start not in visNode");
 
         res = range->GetEndContainer(&container);
         NS_ENSURE_SUCCESS(res, res);
         NS_ASSERTION(container == visNode, "selection end not in visNode");
 #endif
 
+        res = range->GetStartOffset(&so);
+        NS_ENSURE_SUCCESS(res, res);
         res = range->GetEndOffset(&eo);
         NS_ENSURE_SUCCESS(res, res);
       }
       res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor, address_of(visNode), &so, address_of(visNode), &eo);
       if (NS_FAILED(res)) return res;
       nsCOMPtr<nsIDOMCharacterData> nodeAsText(do_QueryInterface(visNode));
       res = mHTMLEditor->DeleteText(nodeAsText, PR_MIN(so, eo), PR_ABS(eo - so));
       *aHandled = PR_TRUE;
--- a/layout/generic/test/Makefile.in
+++ b/layout/generic/test/Makefile.in
@@ -63,13 +63,13 @@ include $(topsrcdir)/config/rules.mk
 		test_bug416168.html \
 		test_bug421436.html \
 		test_bug448860.html \
 		test_bug460532.html \
 		test_bug468167.html \
 		test_bug469613.xul \
 		test_character_movement.html \
 		test_word_movement.html \
-		test_backspace_delete.html \
+		test_backspace_delete.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
deleted file mode 100644
--- a/layout/generic/test/test_backspace_delete.html
+++ /dev/null
@@ -1,179 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test BackSpace/Delete Keys</title>
-  <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: block">
-<div contentEditable id="editor"></div>
-</div>
-<p id="catch">Catch-all
-<pre id="test">
-<script class="testbody" type="text/javascript;version=1.7">
-
-/** Test for Bug 157546, 417745 **/
-
-SimpleTest.waitForExplicitFinish();
-
-// This seems to be necessary because the selection is not set up properly otherwise
-setTimeout(test, 0);
-
-var eatSpace;
-
-function getPrefs() {
-  const prefSvcContractID = "@mozilla.org/preferences-service;1";
-  const prefSvcIID = Components.interfaces.nsIPrefService;
-  return Components.classes[prefSvcContractID].getService(prefSvcIID)
-                                               .getBranch("layout.word_select.");
-}
-
-function setEatSpace(newValue) {
-  getPrefs().setBoolPref("eat_space_to_next_word", newValue);
-  eatSpace = newValue;
-}
-
-function restoreEatSpace() {
-  try {
-    getPrefs().clearUserPref("eat_space_to_next_word");
-  } catch(ex) {}
-}
-
-function test() {
-  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-
-  var wordSelModifiers =
-      (navigator.platform.indexOf("Mac") >= 0) ?
-        {shiftKey:true, altKey:true} : {shiftKey:true, ctrlKey:true};
-  var sel = window.getSelection();
-  var editor = document.getElementById("editor");
-
-  function testRight(node, offset) {
-    synthesizeKey("VK_RIGHT", {});
-    is(sel.anchorNode, node, "Right movement broken in \"" + editor.innerHTML + "\"");
-    is(sel.anchorOffset, offset, "Right movement broken in \"" + editor.innerHTML + "\"");
-  }
-
-  function selErrString(dir) {
-    return dir + " selection broken with eatSpace=" + eatSpace + " in \"" + editor.innerHTML + "\"";
-  }
-
-  function testWordSelRight(startNode, startOffset, endNode, endOffset) {
-    synthesizeKey("VK_RIGHT", wordSelModifiers);
-    var selRange = sel.getRangeAt(0);
-    is(selRange.startContainer, startNode, selErrString("Word right"));
-    is(selRange.startOffset, startOffset, selErrString("Word right"));
-    is(selRange.endContainer, endNode, selErrString("Word right"));
-    is(selRange.endOffset, endOffset, selErrString("Word right"));
-  }
-
-  function testDelete(node, offset, text) {
-    synthesizeKey("VK_DELETE", {});
-    is(sel.anchorNode, node, "Delete broken in \"" + editor.innerHTML + "\"");
-    is(sel.anchorOffset, offset, "Delete broken in \"" + editor.innerHTML + "\"");
-    is(editor.textContent, text, "Delete broken in \"" + editor.innerHTML + "\"");
-  }
-
-  function testBackspace(node, offset, text) {
-    synthesizeKey("VK_BACK_SPACE", {});
-    is(sel.anchorNode, node, "Backspace broken in \"" + editor.innerHTML + "\"");
-    is(sel.anchorOffset, offset, "Backspace broken in \"" + editor.innerHTML + "\"");
-    is(editor.textContent, text, "Backspace broken in \"" + editor.innerHTML + "\"");
-  }
-
-  // Test cell-wise deletion of Delete
-  editor.innerHTML = "สวัสดีพ่อแม่พี่น้อง";
-  sel.collapse(editor.firstChild, 0);
-  testRight(editor.firstChild, 1);
-  testDelete(editor.firstChild, 1, "สสดีพ่อแม่พี่น้อง");
-  testRight(editor.firstChild, 2);
-  testDelete(editor.firstChild, 2, "สสพ่อแม่พี่น้อง");
-  testRight(editor.firstChild, 4);
-  testDelete(editor.firstChild, 4, "สสพ่แม่พี่น้อง");
-  testRight(editor.firstChild, 5);
-  testDelete(editor.firstChild, 5, "สสพ่แพี่น้อง");
-  testRight(editor.firstChild, 8);
-  testDelete(editor.firstChild, 8, "สสพ่แพี่อง");
-  testRight(editor.firstChild, 9);
-  testDelete(editor.firstChild, 9, "สสพ่แพี่อ");
-
-  // Test character-wise deletion of Backspace
-  editor.innerHTML = "สวัสดีพ่อแม่พี่น้อง";
-  sel.collapse(editor.firstChild, 0);
-  testRight(editor.firstChild, 1);
-  testBackspace(editor.firstChild, 0, "วัสดีพ่อแม่พี่น้อง");
-  testRight(editor.firstChild, 2);
-  testBackspace(editor.firstChild, 1, "วสดีพ่อแม่พี่น้อง");
-  testRight(editor.firstChild, 2);
-  testBackspace(editor.firstChild, 1, "วดีพ่อแม่พี่น้อง");
-  testRight(editor.firstChild, 3);
-  testBackspace(editor.firstChild, 2, "วดพ่อแม่พี่น้อง");
-  testRight(editor.firstChild, 4);
-  testBackspace(editor.firstChild, 3, "วดพอแม่พี่น้อง");
-  testRight(editor.firstChild, 4);
-  testBackspace(editor.firstChild, 3, "วดพแม่พี่น้อง");
-  testRight(editor.firstChild, 4);
-  testBackspace(editor.firstChild, 3, "วดพม่พี่น้อง");
-  testRight(editor.firstChild, 5);
-  testBackspace(editor.firstChild, 4, "วดพมพี่น้อง");
-  testRight(editor.firstChild, 7);
-  testBackspace(editor.firstChild, 6, "วดพมพีน้อง");
-  testRight(editor.firstChild, 8);
-  testBackspace(editor.firstChild, 7, "วดพมพีนอง");
-  testRight(editor.firstChild, 8);
-  testBackspace(editor.firstChild, 7, "วดพมพีนง");
-  testRight(editor.firstChild, 8);
-  testBackspace(editor.firstChild, 7, "วดพมพีน");
-
-  // Tests for Bug 417745
-
-  setEatSpace(true);
-
-  editor.innerHTML = "Quick yellow fox";
-  sel.collapse(editor.firstChild, 0);
-  testWordSelRight(editor.firstChild, 0, editor.firstChild, 6);
-  testDelete(editor.firstChild, 0, "yellow fox");
-  testWordSelRight(editor.firstChild, 0, editor.firstChild, 7);
-  testDelete(editor.firstChild, 0, "fox");
-
-  setEatSpace(false);
-
-  editor.innerHTML = "Quick yellow fox";
-  sel.collapse(editor.firstChild, 0);
-  testWordSelRight(editor.firstChild, 0, editor.firstChild, 5);
-  // editor converts the leading space to an &nbsp;, otherwise it
-  // wouldn't show up which would confuse users
-  testDelete(editor.firstChild, 0, "\u00A0yellow fox");
-  testWordSelRight(editor.firstChild, 0, editor.firstChild, 7);
-  testDelete(editor.firstChild, 0, "\u00A0fox");
-  testWordSelRight(editor.firstChild, 0, editor.firstChild, 4);
-  testDelete(editor, 0, "");
-
-  restoreEatSpace();
-
-  // Tests for Bug 419217
-
-  editor.innerHTML = "foo<div>bar</div>";
-  sel.collapse(editor.firstChild, 3);
-  testDelete(editor.firstChild, 3, "foobar");
-
-  // Tests for Bug 419406
-
-  editor.innerHTML = "helloשלום";
-  sel.collapse(editor.firstChild, 4);
-  testRight(editor.firstChild, 5);
-  testDelete(editor.firstChild, 5, "helloשלום");
-
-  SimpleTest.finish();
-}
-
-
-</script>
-</pre>
-</body>
-</html>
new file mode 100644
--- /dev/null
+++ b/layout/generic/test/test_backspace_delete.xul
@@ -0,0 +1,223 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        xmlns:html="http://www.w3.org/1999/xhtml"
+        title="Test BackSpace/Delete Keys">
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+function execTests() {
+  var e = document.getElementById("edit");
+  var doc = e.contentDocument;
+  var win = e.contentWindow;
+  var root = doc.documentElement;
+  var editor = doc.body;
+  var sel = win.getSelection();
+  win.focus();
+
+  function setupTest(html, firstChildOffsetForCaret) {
+    // Work around bug 474255 --- we need to have nonempty content before we turn on
+    // editing, or the tests below break because the editor doesn't notice when we
+    // insert non-empty content using innerHTML.
+    doc.designMode = 'off';
+    editor.innerHTML = html;
+    doc.designMode = 'on';
+    sel.collapse(editor.firstChild, firstChildOffsetForCaret);
+  }
+
+  netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+
+  var eatSpace;
+
+  function getPrefs() {
+    const prefSvcContractID = "@mozilla.org/preferences-service;1";
+    const prefSvcIID = Components.interfaces.nsIPrefService;
+    return Components.classes[prefSvcContractID].getService(prefSvcIID)
+                                                 .getBranch("layout.word_select.");
+  }
+
+  function setEatSpace(newValue) {
+    getPrefs().setBoolPref("eat_space_to_next_word", newValue);
+    eatSpace = newValue;
+  }
+
+  function restoreEatSpace() {
+    try {
+      getPrefs().clearUserPref("eat_space_to_next_word");
+    } catch(ex) {}
+  }
+
+  function doCommand(cmd) {
+    var controller = document.commandDispatcher.getControllerForCommand(cmd);
+    if (controller) {
+      controller.doCommand(cmd);
+    }
+  }
+
+  function testRight(node, offset) {
+    doCommand("cmd_charNext");
+    var msg = "Right movement broken in \"" + editor.innerHTML + "\", offset " + offset;
+    is(sel.anchorNode, node, msg);
+    is(sel.anchorOffset, offset, msg);
+  }
+
+  function selErrString(dir) {
+    return dir + " selection broken with eatSpace=" + eatSpace + " in \"" + editor.innerHTML + "\"";
+  }
+
+  function testWordSelRight(startNode, startOffset, endNode, endOffset) {
+    doCommand("cmd_selectWordNext");
+    var selRange = sel.getRangeAt(0);
+    is(selRange.startContainer, startNode, selErrString("Word right"));
+    is(selRange.startOffset, startOffset, selErrString("Word right"));
+    is(selRange.endContainer, endNode, selErrString("Word right"));
+    is(selRange.endOffset, endOffset, selErrString("Word right"));
+  }
+
+  function testDelete(node, offset, text) {
+    doCommand("cmd_deleteCharForward");
+    var msg = "Delete broken in \"" + editor.innerHTML + "\", offset " + offset;
+    is(sel.anchorNode, node, msg);
+    is(sel.anchorOffset, offset, msg);
+    is(editor.textContent, text, msg);
+  }
+
+  function testBackspace(node, offset, text) {
+    doCommand("cmd_deleteCharBackward");
+    var msg = "Backspace broken in \"" + editor.innerHTML + "\", offset " + offset;
+    is(sel.anchorNode, node, msg);
+    is(sel.anchorOffset, offset, msg);
+    is(editor.textContent, text, msg);
+  }
+
+  function testDeletePrevWord(node, offset, text) {
+    doCommand("cmd_deleteWordBackward");
+    var msg = "Delete previous word broken in \"" + editor.innerHTML + "\", offset " + offset;
+    is(sel.anchorNode, node, msg);
+    is(sel.anchorOffset, offset, msg);
+    is(editor.textContent, text, msg);
+  }
+
+  function testDeleteNextWord(node, offset, text) {
+    doCommand("cmd_deleteWordForward");
+    var msg = "Delete next word broken in \"" + editor.innerHTML + "\", offset " + offset;
+    is(sel.anchorNode, node, msg);
+    is(sel.anchorOffset, offset, msg);
+    todo_is(editor.textContent, text, msg);
+  }
+
+  // Test cell-wise deletion of Delete
+  setupTest("สวัสดีพ่อแม่พี่น้อง", 0);
+  testRight(editor.firstChild, 1);
+  testDelete(editor.firstChild, 1, "สสดีพ่อแม่พี่น้อง");
+  testRight(editor.firstChild, 2);
+  testDelete(editor.firstChild, 2, "สสพ่อแม่พี่น้อง");
+  testRight(editor.firstChild, 4);
+  testDelete(editor.firstChild, 4, "สสพ่แม่พี่น้อง");
+  testRight(editor.firstChild, 5);
+  testDelete(editor.firstChild, 5, "สสพ่แพี่น้อง");
+  testRight(editor.firstChild, 8);
+  testDelete(editor.firstChild, 8, "สสพ่แพี่อง");
+  testRight(editor.firstChild, 9);
+  testDelete(editor.firstChild, 9, "สสพ่แพี่อ");
+
+  // Test character-wise deletion of Backspace
+  setupTest("สวัสดีพ่อแม่พี่น้อง", 0);
+  testRight(editor.firstChild, 1);
+  testBackspace(editor.firstChild, 0, "วัสดีพ่อแม่พี่น้อง");
+  testRight(editor.firstChild, 2);
+  testBackspace(editor.firstChild, 1, "วสดีพ่อแม่พี่น้อง");
+  testRight(editor.firstChild, 2);
+  testBackspace(editor.firstChild, 1, "วดีพ่อแม่พี่น้อง");
+  testRight(editor.firstChild, 3);
+  testBackspace(editor.firstChild, 2, "วดพ่อแม่พี่น้อง");
+  testRight(editor.firstChild, 4);
+  testBackspace(editor.firstChild, 3, "วดพอแม่พี่น้อง");
+  testRight(editor.firstChild, 4);
+  testBackspace(editor.firstChild, 3, "วดพแม่พี่น้อง");
+  testRight(editor.firstChild, 4);
+  testBackspace(editor.firstChild, 3, "วดพม่พี่น้อง");
+  testRight(editor.firstChild, 5);
+  testBackspace(editor.firstChild, 4, "วดพมพี่น้อง");
+  testRight(editor.firstChild, 7);
+  testBackspace(editor.firstChild, 6, "วดพมพีน้อง");
+  testRight(editor.firstChild, 8);
+  testBackspace(editor.firstChild, 7, "วดพมพีนอง");
+  testRight(editor.firstChild, 8);
+  testBackspace(editor.firstChild, 7, "วดพมพีนง");
+  testRight(editor.firstChild, 8);
+  testBackspace(editor.firstChild, 7, "วดพมพีน");
+
+  // Tests for Bug 417745
+
+  setEatSpace(true);
+
+  setupTest("Quick yellow fox", 0);
+  testWordSelRight(editor.firstChild, 0, editor.firstChild, 6);
+  testDelete(editor.firstChild, 0, "yellow fox");
+  testWordSelRight(editor.firstChild, 0, editor.firstChild, 7);
+  testDelete(editor.firstChild, 0, "fox");
+
+  setEatSpace(false);
+
+  setupTest("Quick yellow fox", 0);
+  testWordSelRight(editor.firstChild, 0, editor.firstChild, 5);
+  // editor converts the leading space to an &nbsp;, otherwise it
+  // wouldn't show up which would confuse users
+  testDelete(editor.firstChild, 0, "\u00A0yellow fox");
+  testWordSelRight(editor.firstChild, 0, editor.firstChild, 7);
+  testDelete(editor.firstChild, 0, "\u00A0fox");
+  testWordSelRight(editor.firstChild, 0, editor.firstChild, 4);
+  testDelete(editor, 0, "");
+
+  restoreEatSpace();
+
+  // Tests for Bug 419217
+
+  setupTest("foo<div>bar</div>", 3);
+  testDelete(editor.firstChild, 3, "foobar");
+
+  // Tests for Bug 419406
+  var s = "helloשלום";
+  setupTest(s, 4);
+  testRight(editor.firstChild, 5);
+  testDelete(editor.firstChild, 5, "helloשלום");
+
+  // Tests for Bug 462188
+  setupTest("You should not see this text.", 29);
+  testDeletePrevWord(editor.firstChild, 24, "You should not see this ");
+  testDeletePrevWord(editor.firstChild, 19, "You should not see ");
+  testDeletePrevWord(editor.firstChild, 15, "You should not ");
+  testDeletePrevWord(editor.firstChild, 11, "You should ");
+  testDeletePrevWord(editor.firstChild,  4, "You ");
+  testDeletePrevWord(editor,  0, "");
+
+  setupTest("You should not see this text.", 0);
+  testDeleteNextWord(editor.firstChild, 0, "\u00A0should not see this text.");
+  testDeleteNextWord(editor.firstChild, 0, "\u00A0not see this text.");
+  testDeleteNextWord(editor.firstChild, 0, "\u00A0see this text.");
+  testDeleteNextWord(editor.firstChild, 0, "\u00A0this text.");
+  testDeleteNextWord(editor.firstChild, 0, "\u00A0text.");
+  // testDeleteNextWord(editor, 0, "");
+
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(execTests);
+]]>
+</script>
+
+<body  id="html_body" xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=462188">Mozilla Bug 462188</a>
+<p id="display"></p>
+
+<pre id="test">
+</pre>
+<iframe id="edit" width="200" height="100" src="about:blank"/>
+</body>
+</window>