Bug 1213589 part.8 When there are no nodes causing text, ContentEventHandler should set start of the editor root to start of the range r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 02 Dec 2015 13:20:01 +0900
changeset 275087 5b3f4ce479a5092965a56802193a7343d7008e63
parent 275086 be5d2f1ae122666135bbb4550bdc53f50643040d
child 275088 4434a5a96375d3ffe6e5cc05cab67b675b3772c1
push id68758
push usermasayuki@d-toybox.com
push dateWed, 02 Dec 2015 04:20:12 +0000
treeherdermozilla-inbound@4434a5a96375 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1213589
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1213589 part.8 When there are no nodes causing text, ContentEventHandler should set start of the editor root to start of the range r=smaug
dom/events/ContentEventHandler.cpp
widget/tests/window_composition_text_querycontent.xul
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -53,17 +53,20 @@ using namespace widget;
 //        parent.
 //   1.3. Case: <element/>[
 //        When after an empty element node is start of a range, start node is
 //        parent of the element and start offset is the element's index in the
 //        parent + 1.
 //   1.4. Case: <element>[
 //        When start of a non-empty element is start of a range, start node is
 //        the element and start offset is 0.
-//   1.5. Case: [</root>
+//   1.5. Case: <root>[
+//        When start of a range is 0 and there are no nodes causing text,
+//        start node is the root node and start offset is 0.
+//   1.6. Case: [</root>
 //        When start of a range is out of bounds, start node is the root node
 //        and start offset is number of the children.
 // 2. End of range:
 //   2.1. Cases: ]textNode or text]Node or textNode]
 //        When a text node is end of a range, end node is the text node and
 //        end offset is any number between 0 and the length of the text.
 //   2.2. Case: ]<element>
 //        When before an element node (meaning before the open tag of the
@@ -996,22 +999,41 @@ ContentEventHandler::SetRangeFromFlatTex
       }
       return NS_OK;
     }
 
     offset += textLength;
   }
 
   if (!startSet) {
-    // Rule #1.5: [</root>
     MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT));
-    rv = aRange->SetStart(mRootContent,
-                          static_cast<int32_t>(mRootContent->GetChildCount()));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+    if (!offset) {
+      // Rule #1.5: <root>[</root>
+      // When there are no nodes causing text, the start of the DOM range
+      // should be start of the root node since clicking on such editor (e.g.,
+      // <div contenteditable><span></span></div>) sets caret to the start of
+      // the editor (i.e., before <span> in the example).
+      rv = aRange->SetStart(mRootContent, 0);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      if (!aLength) {
+        rv = aRange->SetEnd(mRootContent, 0);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+        return NS_OK;
+      }
+    } else {
+      // Rule #1.5: [</root>
+      rv = aRange->SetStart(mRootContent,
+                     static_cast<int32_t>(mRootContent->GetChildCount()));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
     if (aNewOffset) {
       *aNewOffset = offset;
     }
   }
   // Rule #2.5: ]</root>
   rv = aRange->SetEnd(mRootContent,
                       static_cast<int32_t>(mRootContent->GetChildCount()));
--- a/widget/tests/window_composition_text_querycontent.xul
+++ b/widget/tests/window_composition_text_querycontent.xul
@@ -3112,32 +3112,32 @@ function runSetSelectionEventTest()
   checkSelection(0, "", "runSetSelectionEventTest #10 (0, 1), \"" + contenteditable.innerHTML + "\"");
 
   // #11
   contenteditable.innerHTML = "<span></span><i><u></u></i>";
 
   synthesizeSelectionSet(0, 0);
   is(selection.anchorNode, contenteditable,
      "runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
-  todo_is(selection.anchorOffset, 0,
+  is(selection.anchorOffset, 0,
      "runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
   is(selection.focusNode, contenteditable,
      "runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
-  todo_is(selection.focusOffset, 0,
+  is(selection.focusOffset, 0,
      "runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\": selection focus offset should be 0");
   checkSelection(0, "", "runSetSelectionEventTest #11 (0, 0), \"" + contenteditable.innerHTML + "\"");
 
   synthesizeSelectionSet(0, 1);
   is(selection.anchorNode, contenteditable,
      "runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\": selection anchor node should be the root node");
-  todo_is(selection.anchorOffset, 0,
+  is(selection.anchorOffset, 0,
      "runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\": selection anchor offset should be 0");
   is(selection.focusNode, contenteditable,
      "runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\": selection focus node should be the root node");
-  todo_is(selection.focusOffset, contenteditable.childNodes.length,
+  is(selection.focusOffset, contenteditable.childNodes.length,
      "runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\": selection focus offset should be the count of the root's children");
   checkSelection(0, "", "runSetSelectionEventTest #11 (0, 1), \"" + contenteditable.innerHTML + "\"");
 
   // #12
   contenteditable.innerHTML = "<span>abc</span><i><u></u></i>";
 
   synthesizeSelectionSet(0, 0);
   is(selection.anchorNode, contenteditable.firstChild.firstChild,