Bug 1423097 - part 1: Implement Selection::AnchorRef() and Selection::FocusRef() r?smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Tue, 05 Dec 2017 16:36:57 +0900
changeset 709489 259db8743b7cbe245c4c641fe0a1237cf0f50737
parent 709488 07bfcb60db5f66e97fb4c5f512da821c05a2f77e
child 709490 a6499c5f9b31b39e4690929bccb8c448d6821cbb
child 710242 f1a78f0266703368b6cc75416ef2ac4333e4927c
push id92666
push usermasayuki@d-toybox.com
push dateFri, 08 Dec 2017 05:43:15 +0000
reviewerssmaug
bugs1423097
milestone59.0a1
Bug 1423097 - part 1: Implement Selection::AnchorRef() and Selection::FocusRef() r?smaug Some methods of editor retrieves anchor and focus of selection. However, there are no methods which directly access RangeBoundary of anchor and focus. This patch adds it for making editor code simpler and avoiding unnecessary child offset computation. MozReview-Commit-ID: EvepQpFMi8S
dom/base/Selection.cpp
dom/base/Selection.h
editor/libeditor/EditorBase.cpp
editor/libeditor/HTMLEditor.cpp
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -811,42 +811,58 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Selection)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Selection)
 
+const RangeBoundary&
+Selection::AnchorRef()
+{
+  if (!mAnchorFocusRange) {
+    static RangeBoundary sEmpty;
+    return sEmpty;
+  }
+
+  if (GetDirection() == eDirNext) {
+    return mAnchorFocusRange->StartRef();
+  }
+
+  return mAnchorFocusRange->EndRef();
+}
+
+const RangeBoundary&
+Selection::FocusRef()
+{
+  if (!mAnchorFocusRange) {
+    static RangeBoundary sEmpty;
+    return sEmpty;
+  }
+
+  if (GetDirection() == eDirNext){
+    return mAnchorFocusRange->EndRef();
+  }
+
+  return mAnchorFocusRange->StartRef();
+}
 
 NS_IMETHODIMP
 Selection::GetAnchorNode(nsIDOMNode** aAnchorNode)
 {
   nsINode* anchorNode = GetAnchorNode();
   if (anchorNode) {
     return CallQueryInterface(anchorNode, aAnchorNode);
   }
 
   *aAnchorNode = nullptr;
   return NS_OK;
 }
 
-nsINode*
-Selection::GetAnchorNode()
-{
-  if (!mAnchorFocusRange)
-    return nullptr;
-
-  if (GetDirection() == eDirNext) {
-    return mAnchorFocusRange->GetStartContainer();
-  }
-
-  return mAnchorFocusRange->GetEndContainer();
-}
-
 NS_IMETHODIMP
 Selection::GetAnchorOffset(int32_t* aAnchorOffset)
 {
   *aAnchorOffset = static_cast<int32_t>(AnchorOffset());
   return NS_OK;
 }
 
 // note: this can return a nil focus node
@@ -857,29 +873,16 @@ Selection::GetFocusNode(nsIDOMNode** aFo
   if (focusNode) {
     return CallQueryInterface(focusNode, aFocusNode);
   }
 
   *aFocusNode = nullptr;
   return NS_OK;
 }
 
-nsINode*
-Selection::GetFocusNode()
-{
-  if (!mAnchorFocusRange)
-    return nullptr;
-
-  if (GetDirection() == eDirNext){
-    return mAnchorFocusRange->GetEndContainer();
-  }
-
-  return mAnchorFocusRange->GetStartContainer();
-}
-
 NS_IMETHODIMP
 Selection::GetFocusOffset(int32_t* aFocusOffset)
 {
   *aFocusOffset = static_cast<int32_t>(FocusOffset());
   return NS_OK;
 }
 
 void
@@ -891,70 +894,16 @@ Selection::SetAnchorFocusRange(int32_t i
   {
     mAnchorFocusRange = nullptr;
   }
   else{
     mAnchorFocusRange = mRanges[indx].mRange;
   }
 }
 
-uint32_t
-Selection::AnchorOffset()
-{
-  if (!mAnchorFocusRange)
-    return 0;
-
-  if (GetDirection() == eDirNext){
-    return mAnchorFocusRange->StartOffset();
-  }
-
-  return mAnchorFocusRange->EndOffset();
-}
-
-uint32_t
-Selection::FocusOffset()
-{
-  if (!mAnchorFocusRange)
-    return 0;
-
-  if (GetDirection() == eDirNext){
-    return mAnchorFocusRange->EndOffset();
-  }
-
-  return mAnchorFocusRange->StartOffset();
-}
-
-nsIContent*
-Selection::GetChildAtAnchorOffset()
-{
-  if (!mAnchorFocusRange) {
-    return nullptr;
-  }
-
-  if (GetDirection() == eDirNext) {
-    return mAnchorFocusRange->GetChildAtStartOffset();
-  }
-
-  return mAnchorFocusRange->GetChildAtEndOffset();
-}
-
-nsIContent*
-Selection::GetChildAtFocusOffset()
-{
-  if (!mAnchorFocusRange) {
-    return nullptr;
-  }
-
-  if (GetDirection() == eDirNext){
-    return mAnchorFocusRange->GetChildAtEndOffset();
-  }
-
-  return mAnchorFocusRange->GetChildAtStartOffset();
-}
-
 static nsresult
 CompareToRangeStart(nsINode* aCompareNode, int32_t aCompareOffset,
                     nsRange* aRange, int32_t* aCmp)
 {
   nsINode* start = aRange->GetStartContainer();
   NS_ENSURE_STATE(aCompareNode && start);
   // If the nodes that we're comparing are not in the same document,
   // assume that aCompareNode will fall at the end of the ranges.
--- a/dom/base/Selection.h
+++ b/dom/base/Selection.h
@@ -180,23 +180,50 @@ public:
                                     const nsPoint& aPoint,
                                     uint32_t aDelay);
 
   nsresult     StopAutoScrollTimer();
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL methods
-  nsINode*     GetAnchorNode();
-  uint32_t     AnchorOffset();
-  nsINode*     GetFocusNode();
-  uint32_t     FocusOffset();
+  nsINode* GetAnchorNode()
+  {
+    const RangeBoundary& anchor = AnchorRef();
+    return anchor.IsSet() ? anchor.Container() : nullptr;
+  }
+  uint32_t AnchorOffset()
+  {
+    const RangeBoundary& anchor = AnchorRef();
+    return anchor.IsSet() ? anchor.Offset() : 0;
+  }
+  nsINode* GetFocusNode()
+  {
+    const RangeBoundary& focus = FocusRef();
+    return focus.IsSet() ? focus.Container() : nullptr;
+  }
+  uint32_t FocusOffset()
+  {
+    const RangeBoundary& focus = FocusRef();
+    return focus.IsSet() ? focus.Offset() : 0;
+  }
 
-  nsIContent*  GetChildAtAnchorOffset();
-  nsIContent*  GetChildAtFocusOffset();
+  nsIContent* GetChildAtAnchorOffset()
+  {
+    const RangeBoundary& anchor = AnchorRef();
+    return anchor.IsSet() ? anchor.GetChildAtOffset() : nullptr;
+  }
+  nsIContent* GetChildAtFocusOffset()
+  {
+    const RangeBoundary& focus = FocusRef();
+    return focus.IsSet() ? focus.GetChildAtOffset() : nullptr;
+  }
+
+  const RangeBoundary& AnchorRef();
+  const RangeBoundary& FocusRef();
 
   /*
    * IsCollapsed -- is the whole selection just one point, or unset?
    */
   bool IsCollapsed() const
   {
     uint32_t cnt = mRanges.Length();
     if (cnt == 0) {
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -4478,23 +4478,19 @@ already_AddRefed<Element>
 EditorBase::DeleteSelectionAndCreateElement(nsAtom& aTag)
 {
   nsresult rv = DeleteSelectionAndPrepareToCreateNode();
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, nullptr);
 
-  EditorRawDOMPoint pointToInsert(selection->GetChildAtAnchorOffset());
+  EditorRawDOMPoint pointToInsert(selection->AnchorRef());
   if (!pointToInsert.IsSet()) {
-    // Perhaps, the anchor point is in a text node.
-    pointToInsert.Set(selection->GetAnchorNode(), selection->AnchorOffset());
-    if (NS_WARN_IF(!pointToInsert.IsSet())) {
-      return nullptr;
-    }
+    return nullptr;
   }
   RefPtr<Element> newElement = CreateNode(&aTag, pointToInsert);
 
   // We want the selection to be just after the new node
   DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
   NS_WARNING_ASSERTION(advanced,
                        "Failed to move offset next to the new element");
   ErrorResult error;
@@ -4543,66 +4539,59 @@ EditorBase::DeleteSelectionAndPrepareToC
 
     MOZ_ASSERT(selection->GetAnchorFocusRange() &&
                selection->GetAnchorFocusRange()->Collapsed(),
                "Selection not collapsed after delete");
   }
 
   // If the selection is a chardata node, split it if necessary and compute
   // where to put the new node
-  nsCOMPtr<nsINode> node = selection->GetAnchorNode();
-  MOZ_ASSERT(node, "Selection has no ranges in it");
-
-  if (!node || !node->IsNodeOfType(nsINode::eDATA_NODE)) {
+  EditorDOMPoint atAnchor(selection->AnchorRef());
+  if (NS_WARN_IF(!atAnchor.IsSet()) ||
+      !atAnchor.Container()->IsNodeOfType(nsINode::eDATA_NODE)) {
     return NS_OK;
   }
 
-  NS_ASSERTION(node->GetParentNode(),
-               "It's impossible to insert into chardata with no parent -- "
-               "fix the caller");
-  NS_ENSURE_STATE(node->GetParentNode());
-
-  // XXX We want Selection::AnchorRef()
-  uint32_t offset = selection->AnchorOffset();
-
-  if (!offset) {
-    EditorRawDOMPoint atNode(node);
-    if (NS_WARN_IF(!atNode.IsSetAndValid())) {
+  if (NS_WARN_IF(!atAnchor.Container()->GetParentNode())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (atAnchor.IsStartOfContainer()) {
+    EditorRawDOMPoint atAnchorContainer(atAnchor.Container());
+    if (NS_WARN_IF(!atAnchorContainer.IsSetAndValid())) {
       return NS_ERROR_FAILURE;
     }
     ErrorResult error;
-    selection->Collapse(atNode, error);
+    selection->Collapse(atAnchorContainer, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
     return NS_OK;
   }
 
-  if (offset == node->Length()) {
-    EditorRawDOMPoint afterNode(node);
-    if (NS_WARN_IF(!afterNode.AdvanceOffset())) {
+  if (atAnchor.IsEndOfContainer()) {
+    EditorRawDOMPoint afterAnchorContainer(atAnchor.Container());
+    if (NS_WARN_IF(!afterAnchorContainer.AdvanceOffset())) {
       return NS_ERROR_FAILURE;
     }
     ErrorResult error;
-    selection->Collapse(afterNode, error);
+    selection->Collapse(afterAnchorContainer, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.StealNSResult();
     }
     return NS_OK;
   }
 
-  EditorRawDOMPoint atStartOfRightNode(node, offset);
-  MOZ_ASSERT(atStartOfRightNode.IsSetAndValid());
   ErrorResult error;
-  nsCOMPtr<nsIContent> newLeftNode = SplitNode(atStartOfRightNode, error);
+  nsCOMPtr<nsIContent> newLeftNode = SplitNode(atAnchor.AsRaw(), error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
-  EditorRawDOMPoint atRightNode(atStartOfRightNode.Container());
+  EditorRawDOMPoint atRightNode(atAnchor.Container());
   if (NS_WARN_IF(!atRightNode.IsSet())) {
     return NS_ERROR_FAILURE;
   }
   MOZ_ASSERT(atRightNode.IsSetAndValid());
   selection->Collapse(atRightNode, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1470,22 +1470,17 @@ HTMLEditor::InsertElementAtSelection(nsI
       if (HTMLEditUtils::IsNamedAnchor(node)) {
         selection->CollapseToStart();
       } else {
         selection->CollapseToEnd();
       }
     }
 
     if (selection->GetAnchorNode()) {
-      EditorRawDOMPoint atAnchor;
-      if (selection->GetChildAtAnchorOffset()) {
-        atAnchor.Set(selection->GetChildAtAnchorOffset());
-      } else {
-        atAnchor.Set(selection->GetAnchorNode(), selection->AnchorOffset());
-      }
+      EditorRawDOMPoint atAnchor(selection->AnchorRef());
       // Adjust position based on the node we are going to insert.
       EditorRawDOMPoint pointToInsert =
         GetBetterInsertionPointFor(*element, atAnchor);
       if (NS_WARN_IF(!pointToInsert.IsSet())) {
         return NS_ERROR_FAILURE;
       }
 
       EditorDOMPoint insertedPoint =
@@ -2242,28 +2237,33 @@ HTMLEditor::GetElementOrParentByTagName(
                                         nsINode* aNode)
 {
   MOZ_ASSERT(!aTagName.IsEmpty());
 
   nsCOMPtr<nsINode> node = aNode;
   if (!node) {
     // If no node supplied, get it from anchor node of current selection
     RefPtr<Selection> selection = GetSelection();
-    NS_ENSURE_TRUE(selection, nullptr);
-
-    nsCOMPtr<nsINode> anchorNode = selection->GetAnchorNode();
-    NS_ENSURE_TRUE(anchorNode, nullptr);
+    if (NS_WARN_IF(!selection)) {
+      return nullptr;
+    }
+
+    const EditorDOMPoint atAnchor(selection->AnchorRef());
+    if (NS_WARN_IF(!atAnchor.IsSet())) {
+      return nullptr;
+    }
 
     // Try to get the actual selected node
-    if (anchorNode->HasChildNodes() && anchorNode->IsContent()) {
-      node = selection->GetChildAtAnchorOffset();
+    if (atAnchor.Container()->HasChildNodes() &&
+        atAnchor.Container()->IsContent()) {
+      node = atAnchor.GetChildAtOffset();
     }
     // Anchor node is probably a text node - just use that
     if (!node) {
-      node = anchorNode;
+      node = atAnchor.Container();
     }
   }
 
   nsCOMPtr<Element> current;
   if (node->IsElement()) {
     current = node->AsElement();
   } else if (node->GetParentElement()) {
     current = node->GetParentElement();
@@ -2390,57 +2390,54 @@ HTMLEditor::GetSelectedElement(const nsA
     }
   }
 
   if (!bNodeFound) {
     if (isLinkTag) {
       // Link tag is a special case - we return the anchor node
       //  found for any selection that is totally within a link,
       //  included a collapsed selection (just a caret in a link)
-      nsCOMPtr<nsINode> anchorNode = selection->GetAnchorNode();
-      nsIContent* anchorChild = selection->GetChildAtAnchorOffset();
-
-      nsCOMPtr<nsINode> focusNode = selection->GetFocusNode();
-      nsIContent* focusChild = selection->GetChildAtFocusOffset();
-
+      const RangeBoundary& anchor = selection->AnchorRef();
+      const RangeBoundary& focus = selection->FocusRef();
       // Link node must be the same for both ends of selection
-      if (anchorNode) {
+      if (anchor.IsSet()) {
         nsCOMPtr<nsIDOMElement> parentLinkOfAnchor;
         nsresult rv =
           GetElementOrParentByTagName(NS_LITERAL_STRING("href"),
-                                      GetAsDOMNode(anchorNode),
+                                      anchor.Container()->AsDOMNode(),
                                       getter_AddRefs(parentLinkOfAnchor));
         // XXX: ERROR_HANDLING  can parentLinkOfAnchor be null?
         if (NS_SUCCEEDED(rv) && parentLinkOfAnchor) {
           if (isCollapsed) {
             // We have just a caret in the link
             bNodeFound = true;
-          } else if (focusNode) {
+          } else if (focus.IsSet()) {
             // Link node must be the same for both ends of selection.
             nsCOMPtr<nsIDOMElement> parentLinkOfFocus;
             rv = GetElementOrParentByTagName(NS_LITERAL_STRING("href"),
-                                             GetAsDOMNode(focusNode),
+                                             focus.Container()->AsDOMNode(),
                                              getter_AddRefs(parentLinkOfFocus));
             if (NS_SUCCEEDED(rv) && parentLinkOfFocus == parentLinkOfAnchor) {
               bNodeFound = true;
             }
           }
 
           // We found a link node parent
           if (bNodeFound) {
             // GetElementOrParentByTagName addref'd this, so we don't need to do it here
             parentLinkOfAnchor.forget(aReturn);
             return NS_OK;
           }
-        } else if (anchorChild && focusChild) {
+        } else if (anchor.GetChildAtOffset() && focus.GetChildAtOffset()) {
           // Check if link node is the only thing selected
-          if (HTMLEditUtils::IsLink(anchorChild) &&
-              anchorNode == focusNode &&
-              focusChild == anchorChild->GetNextSibling()) {
-            selectedElement = do_QueryInterface(anchorChild);
+          if (HTMLEditUtils::IsLink(anchor.GetChildAtOffset()) &&
+              anchor.Container() == focus.Container() &&
+              focus.GetChildAtOffset() ==
+                anchor.GetChildAtOffset()->GetNextSibling()) {
+            selectedElement = do_QueryInterface(anchor.GetChildAtOffset());
             bNodeFound = true;
           }
         }
       }
     }
 
     if (!isCollapsed) {
       RefPtr<nsRange> currange = selection->GetRangeAt(0);