From cb757fbca3c7546cbf4f462a74d551167c2e535a Mon Sep 17 00:00:00 2001 From: "liucougar@gmail.com" Date: Tue, 23 Mar 2010 09:23:23 +0100 Subject: [PATCH] Bug 507936 - Should join node when deleting at end of paragraph. r=peterv --- editor/libeditor/html/nsHTMLEditRules.cpp | 42 +++++++++++----- layout/generic/test/test_backspace_delete.xul | 49 ++++++++++++++++++- 2 files changed, 78 insertions(+), 13 deletions(-) diff --git a/editor/libeditor/html/nsHTMLEditRules.cpp b/editor/libeditor/html/nsHTMLEditRules.cpp index 2cc5b095dedb..3bae1318da19 100644 --- a/editor/libeditor/html/nsHTMLEditRules.cpp +++ b/editor/libeditor/html/nsHTMLEditRules.cpp @@ -1920,10 +1920,17 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection, nsresult res = NS_OK; PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask; - PRBool bCollapsed; + PRBool bCollapsed, join = PR_FALSE; res = aSelection->GetIsCollapsed(&bCollapsed); if (NS_FAILED(res)) return res; - + + // origCollapsed is used later to determine whether we should join + // blocks. We don't really care about bCollapsed because it will be + // modified by ExtendSelectionForDelete later. JoinBlocks should + // happen if the original selection is collapsed and the cursor is + // at the end of a block element, in which case ExtendSelectionForDelete + // would always make the selection not collapsed. + PRBool origCollapsed = bCollapsed; nsCOMPtr startNode, selNode; PRInt32 startOffset, selOffset; @@ -2467,6 +2474,8 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection, if (NS_FAILED(res)) return res; if (!enumerator) return NS_ERROR_UNEXPECTED; + join = PR_TRUE; + for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next()) { nsCOMPtr currentItem; @@ -2493,6 +2502,17 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection, nsIDOMNode* somenode = arrayOfNodes[0]; res = DeleteNonTableElements(somenode); arrayOfNodes.RemoveObjectAt(0); + // If something visible is deleted, no need to join. + // Visible means all nodes except non-visible textnodes and breaks. + if (join && origCollapsed) { + if (mHTMLEditor->IsTextNode(somenode)) { + mHTMLEditor->IsVisTextNode(somenode, &join, PR_TRUE); + } + else { + join = nsTextEditUtils::IsBreak(somenode) && + !mHTMLEditor->IsVisBreak(somenode); + } + } } } @@ -2526,14 +2546,6 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection, } } - PRBool join = leftBlockParent == rightBlockParent; - if (!join) { - nsCOMPtr parent1 = do_QueryInterface(leftParent); - nsCOMPtr parent2 = do_QueryInterface(rightParent); - PRUint16 pos = nsContentUtils::ComparePosition(parent1, parent2); - join = (pos & (nsIDOM3Node::DOCUMENT_POSITION_CONTAINS | - nsIDOM3Node::DOCUMENT_POSITION_CONTAINED_BY)) != 0; - } if (join) { res = JoinBlocks(address_of(leftParent), address_of(rightParent), aCancel); @@ -2542,7 +2554,15 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection, } } } - if (aAction == nsIEditor::eNext) + //If we're joining blocks: if deleting forward the selection should be + //collapsed to the end of the selection, if deleting backward the selection + //should be collapsed to the beginning of the selection. But if we're not + //joining then the selection should collapse to the beginning of the + //selection if we'redeleting forward, because the end of the selection will + //still be in the next block. And same thing for deleting backwards + //(selection should collapse to the end, because the beginning will still + //be in the first block). See Bug 507936 + if (join ? aAction == nsIEditor::eNext : aAction == nsIEditor::ePrevious) { res = aSelection->Collapse(endNode,endOffset); } diff --git a/layout/generic/test/test_backspace_delete.xul b/layout/generic/test/test_backspace_delete.xul index 7c689b7237ad..16a38641eae9 100644 --- a/layout/generic/test/test_backspace_delete.xul +++ b/layout/generic/test/test_backspace_delete.xul @@ -19,14 +19,18 @@ function execTests() { var sel = win.getSelection(); win.focus(); - function setupTest(html, firstChildOffsetForCaret) { + function setupTest(html, firstChildOffsetForCaret, node) { // 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); + var n = editor.firstChild; + if (node) { + n = node(); + } + sel.collapse(n, firstChildOffsetForCaret); } netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); @@ -211,6 +215,47 @@ function execTests() { setupTest("

Bug

\n

502259

", 1); testDelete(function(){return editor.firstChild.firstChild;}, 3, "

Bug502259

", true); + // Tests for Bug 507936 + var nodecallback = function(){return editor.firstChild.firstChild.lastChild.firstChild.lastChild;}; + setupTest("
  1. one
    1. two
\n

three

", 3, nodecallback); + testDelete(nodecallback, 0, "
  1. one
    1. twothree
", true); + + setupTest("
  1. one
    1. two
\n
\n

three

", 3, nodecallback); + testDelete(nodecallback, 3, + "
  1. one
    1. two

three

", true); + + // Tests for Bug 519751 + var nodecallback = function(){return editor.firstChild.lastChild;}; + setupTest("

one

  1. two
  2. three
", 3, nodecallback); + testDelete(nodecallback, 0, "

onetwo

  1. three
", true); + + nodecallback = function(){return editor.firstChild.childNodes[1].firstChild;}; + setupTest("
  1. one
  2. two
  1. three
  2. four
", 3, nodecallback); + testDelete(function(){return editor.firstChild.childNodes[2].firstChild;}, + 0, "
  1. one
  2. two
  3. three
  4. four
", true); + /*todo_is(false, true, 'The above testDelete should use the same nodecallback' + + 'as in the proceeding setupTest: the cursor should stay at the end of "two", while currently it is at the beginning of "three" after delete');*/ + + // More Tests for Bug 507936 + nodecallback = function(){return editor.firstChild.firstChild.firstChild;} + setupTest("
abcdef
bar
ghi
", 5, nodecallback); + sel.extend(editor.lastChild.lastChild.lastChild, 1); + testDelete(editor.lastChild.lastChild.lastChild, 5, "
abcdehi
", true); + + setupTest("
abcdef
ghi
", 5, nodecallback); + sel.extend(editor.lastChild.lastChild.lastChild, 1); + testDelete(editor.lastChild.lastChild.lastChild, 5, "
abcdehi
", true); + + nodecallback = function(){return editor.firstChild.firstChild;} + setupTest("
abcdef
bar
ghi
", 5, nodecallback); + sel.extend(editor.lastChild.lastChild.lastChild, 1); + expectednodecallback = function(){return editor.lastChild.lastChild;} + testDelete(expectednodecallback, 0, "
abcdehi
", true); + + setupTest("
abcdef
ghi
", 5, nodecallback); + sel.extend(editor.lastChild.lastChild.lastChild, 1); + testDelete(expectednodecallback, 0, "
abcdehi
", true); + SimpleTest.finish(); }