Bug 1316302 part.2 WillDeleteSelection() should retry to handle it when selection is collapsed and JoinBlocks() doesn't handle nor cancel the action r?smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 15 Nov 2016 18:40:32 +0900
changeset 439598 71091e4fc79d1659f5dcdff6a761c2580a7842a7
parent 439597 d201b4136a52f28cfc3f37fcbbb545c6a6fab99c
child 439599 8af44a431ad4d0942fcb552a4db36ddbe8ca39f0
push id36057
push usermasayuki@d-toybox.com
push dateWed, 16 Nov 2016 11:17:12 +0000
reviewerssmaug
bugs1316302
milestone53.0a1
Bug 1316302 part.2 WillDeleteSelection() should retry to handle it when selection is collapsed and JoinBlocks() doesn't handle nor cancel the action r?smaug When selection is collapsed and JoinBlocks() doesn't handle nor cancel the action, WillDeleteSelection() should move selection to the start/end of leftmost/rightmost editable leaf node and retry to handle the action again. For avoiding infinite loop, it checks if selected node is changed actually before calling itself again. MozReview-Commit-ID: GtEC4dim3r9
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/tests/mochitest.ini
editor/libeditor/tests/test_bug1316302.html
editor/libeditor/tests/test_bug414526.html
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -2170,22 +2170,34 @@ HTMLEditRules::WillDeleteSelection(Selec
         NS_ENSURE_STATE(mHTMLEditor);
         AutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
                                   address_of(selPointNode), &selPointOffset);
         NS_ENSURE_STATE(leftNode && leftNode->IsContent() &&
                         rightNode && rightNode->IsContent());
         bool handled = false, canceled = false;
         rv = TryToJoinBlocks(*leftNode->AsContent(), *rightNode->AsContent(),
                              &canceled, &handled);
-        // TODO: If it does nothing and previous or next node is a text node,
-        //       we should modify it.
-        *aHandled = true;
+        *aHandled |= handled;
         *aCancel |= canceled;
         NS_ENSURE_SUCCESS(rv, rv);
       }
+
+      // If TryToJoinBlocks() didn't handle it actually and it's not canceled,
+      // user may want to modify the start leaf node or the last leaf node
+      // of the block.
+      if (!*aHandled && !*aCancel && leafNode != startNode) {
+        int32_t offset =
+          aAction == nsIEditor::ePrevious ?
+            static_cast<int32_t>(leafNode->Length()) : 0;
+        aSelection->Collapse(leafNode, offset);
+        return WillDeleteSelection(aSelection, aAction, aStripWrappers,
+                                   aCancel, aHandled);
+      }
+
+      // Otherwise, we must have deleted the selection as user expected.
       aSelection->Collapse(selPointNode, selPointOffset);
       return NS_OK;
     }
 
     if (wsType == WSType::thisBlock) {
       // At edge of our block.  Look beside it and see if we can join to an
       // adjacent block
 
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -208,16 +208,17 @@ skip-if = toolkit == 'android'
 [test_bug1250010.html]
 [test_bug1257363.html]
 [test_bug1248185.html]
 [test_bug1258085.html]
 [test_bug1268736.html]
 [test_bug1310912.html]
 skip-if = toolkit == 'android' # bug 1315898
 [test_bug1315065.html]
+[test_bug1316302.html]
 
 [test_CF_HTML_clipboard.html]
 subsuite = clipboard
 [test_composition_event_created_in_chrome.html]
 [test_contenteditable_focus.html]
 [test_dom_input_event_on_htmleditor.html]
 skip-if = toolkit == 'android' # bug 1054087
 [test_dom_input_event_on_texteditor.html]
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1316302.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1316302
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1316302</title>
+  <script type="application/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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1316302">Mozilla Bug 1316302</a>
+<div contenteditable>
+<blockquote><p>abc</p></blockquote>
+</div>
+<script type="application/javascript">
+/** Test for Bug 1316302 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(()=>{
+  var editor = document.getElementsByTagName("div")[0];
+  var blockquote = document.getElementsByTagName("blockquote")[0];
+  var selection = window.getSelection();
+
+  editor.focus();
+
+  // Try to remove the last character from the end of the <blockquote>
+  selection.collapse(blockquote, blockquote.childNodes.length);
+  var range = selection.getRangeAt(0);
+  ok(range.collapsed, "range should be collapsed at the end of <blockquote>");
+  is(range.startContainer, blockquote, "range should be collapsed in the <blockquote>");
+  is(range.startOffset, blockquote.childNodes.length, "range should be collapsed at the end");
+  synthesizeKey("KEY_Backspace", { code: "Backspace" });
+  is(blockquote.innerHTML, "<p>ab</p>", "Pressing Backspace key at the end of <blockquote> should remove the last character in the <p>");
+
+  // Try to remove the first character from the start of the <blockquote>
+  selection.collapse(blockquote, 0);
+  range = selection.getRangeAt(0);
+  ok(range.collapsed, "range should be collapsed at the start of <blockquote>");
+  is(range.startContainer, blockquote, "range should be collapsed in the <blockquote>");
+  is(range.startOffset, 0, "range should be collapsed at the start");
+  synthesizeKey("KEY_Delete", { code: "Delete" });
+  is(blockquote.innerHTML, "<p>b</p>", "Pressing Delete key at the start of <blockquote> should remove the first character in the <p>");
+
+  SimpleTest.finish();
+});
+</script>
+</body>
+</html>
--- a/editor/libeditor/tests/test_bug414526.html
+++ b/editor/libeditor/tests/test_bug414526.html
@@ -145,30 +145,22 @@ function runTests()
   editor3.focus();
   moveCaretToStartOf(editor3);
   synthesizeKey("VK_DELETE", { });
   is(container.innerHTML, kTestCase1_editor3_deleteAtStart,
      "Pressing delete key at start of editor3 changes adjacent elements"
      + " and/or does not remove the first character.");
   reset();
 
-  // Backspace doesn't work here yet.
   editor3.focus();
   moveCaretToEndOf(editor3);
   synthesizeKey("VK_BACK_SPACE", { });
-  todo_is(container.innerHTML, kTestCase1_editor3_backspaceAtEnd,
-          "Pressing backspace key at end of editor3 changes adjacent elements"
-          + " and/or does not remove the last character.");
-  reset();
-  //  We can still check that adjacent elements are not affected.
-  editor3.focus();
-  moveCaretToEndOf(editor3);
-  synthesizeKey("VK_BACK_SPACE", { });
-  is(container.innerHTML, kTestCase1,
-     "Pressing backspace key at end of editor3 changes the content");
+  is(container.innerHTML, kTestCase1_editor3_backspaceAtEnd,
+     "Pressing backspace key at end of editor3 changes adjacent elements"
+     + " and/or does not remove the last character.");
   reset();
 
   /* TestCase #2:
    * two adjacent editable <span> in a table cell.
    */
   const kTestCase2 = "<table><tbody><tr><td><span id=\"editor1\" contenteditable=\"true\">test</span>" +
     "<span id=\"editor2\" contenteditable=\"true\">test</span></td></tr></tbody></table>";