Bug 1415800 - part 1: Redesign EditorBase::GetPriorNode() with EditorRawDOMPoint r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 09 Nov 2017 17:08:10 +0900
changeset 696005 52e7826c7efec6b61c9ff8264f47942253546983
parent 696004 a1285db2a916449ded6c517af63d7cda6bdde71f
child 696006 1d08e03f00a0a765b589a60e44a32c1d035cec8d
push id88610
push usermasayuki@d-toybox.com
push dateFri, 10 Nov 2017 04:02:33 +0000
reviewersm_kato
bugs1415800
milestone58.0a1
Bug 1415800 - part 1: Redesign EditorBase::GetPriorNode() with EditorRawDOMPoint r?m_kato An overload of EditorBase::GetPriorNode() takes a set of container, child node and offset of the child in the container. Replacing it with EditorRawDOMPoint makes the caller simpler. Additionally, it has two bool arguments, one is for searching if editable node, the other is for searching if in same block. So, they can be hidden with some human readable inline methods. Finally, "Prior" isn't a term of DOM. So, let's use "Previous" instead. MozReview-Commit-ID: A9uKzHaikY9
editor/libeditor/EditorBase.cpp
editor/libeditor/EditorBase.h
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/HTMLEditor.cpp
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -3280,48 +3280,70 @@ EditorBase::GetLengthOfDOMNode(nsIDOMNod
   aCount = 0;
   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
   aCount = node->Length();
   return NS_OK;
 }
 
 nsIContent*
-EditorBase::GetPriorNode(nsINode* aParentNode,
-                         int32_t aOffset,
-                         nsINode* aChildAtOffset,
-                         bool aEditableNode,
-                         bool aNoBlockCrossing)
-{
-  MOZ_ASSERT(aParentNode);
+EditorBase::GetPreviousNodeInternal(nsINode& aNode,
+                                    bool aFindEditableNode,
+                                    bool aNoBlockCrossing)
+{
+  if (!IsDescendantOfEditorRoot(&aNode)) {
+    return nullptr;
+  }
+  return FindNode(&aNode, false, aFindEditableNode, aNoBlockCrossing);
+}
+
+nsIContent*
+EditorBase::GetPreviousNodeInternal(const EditorRawDOMPoint& aPoint,
+                                    bool aFindEditableNode,
+                                    bool aNoBlockCrossing)
+{
+  MOZ_ASSERT(aPoint.IsSetAndValid());
+  NS_WARNING_ASSERTION(!aPoint.Container()->IsNodeOfType(nsINode::eDATA_NODE) ||
+                       aPoint.Container()->IsNodeOfType(nsINode::eTEXT),
+    "GetPreviousNodeInternal() doesn't assume that the start point is a "
+    "data node except text node");
 
   // If we are at the beginning of the node, or it is a text node, then just
   // look before it.
-  if (!aOffset || aParentNode->NodeType() == nsIDOMNode::TEXT_NODE) {
-    if (aNoBlockCrossing && IsBlockNode(aParentNode)) {
+  if (aPoint.IsStartOfContainer() ||
+      aPoint.Container()->IsNodeOfType(nsINode::eTEXT)) {
+    if (aNoBlockCrossing && IsBlockNode(aPoint.Container())) {
       // If we aren't allowed to cross blocks, don't look before this block.
       return nullptr;
     }
-    return GetPriorNode(aParentNode, aEditableNode, aNoBlockCrossing);
+    return GetPreviousNodeInternal(*aPoint.Container(),
+                                   aFindEditableNode, aNoBlockCrossing);
   }
 
   // else look before the child at 'aOffset'
-  if (aChildAtOffset) {
-    return GetPriorNode(aChildAtOffset, aEditableNode, aNoBlockCrossing);
+  if (aPoint.GetChildAtOffset()) {
+    return GetPreviousNodeInternal(*aPoint.GetChildAtOffset(),
+                                   aFindEditableNode, aNoBlockCrossing);
   }
 
   // unless there isn't one, in which case we are at the end of the node
   // and want the deep-right child.
-  nsIContent* resultNode = GetRightmostChild(aParentNode, aNoBlockCrossing);
-  if (!resultNode || !aEditableNode || IsEditable(resultNode)) {
-    return resultNode;
+  nsIContent* rightMostNode =
+    GetRightmostChild(aPoint.Container(), aNoBlockCrossing);
+  if (!rightMostNode) {
+    return nullptr;
+  }
+
+  if (!aFindEditableNode || IsEditable(rightMostNode)) {
+    return rightMostNode;
   }
 
   // restart the search from the non-editable node we just found
-  return GetPriorNode(resultNode, aEditableNode, aNoBlockCrossing);
+  return GetPreviousNodeInternal(*rightMostNode,
+                                 aFindEditableNode, aNoBlockCrossing);
 }
 
 nsIContent*
 EditorBase::GetNextNode(nsINode* aParentNode,
                         int32_t aOffset,
                         nsINode* aChildAtOffset,
                         bool aEditableNode,
                         bool aNoBlockCrossing)
@@ -3367,30 +3389,16 @@ EditorBase::GetNextNode(nsINode* aParent
     // don't cross out of parent block
     return nullptr;
   }
 
   return GetNextNode(aParentNode, aEditableNode, aNoBlockCrossing);
 }
 
 nsIContent*
-EditorBase::GetPriorNode(nsINode* aCurrentNode,
-                         bool aEditableNode,
-                         bool aNoBlockCrossing /* = false */)
-{
-  MOZ_ASSERT(aCurrentNode);
-
-  if (!IsDescendantOfEditorRoot(aCurrentNode)) {
-    return nullptr;
-  }
-
-  return FindNode(aCurrentNode, false, aEditableNode, aNoBlockCrossing);
-}
-
-nsIContent*
 EditorBase::FindNextLeafNode(nsINode* aCurrentNode,
                              bool aGoForward,
                              bool bNoBlockCrossing)
 {
   // called only by GetPriorNode so we don't need to check params.
   NS_PRECONDITION(IsDescendantOfEditorRoot(aCurrentNode) &&
                   !IsEditorRoot(aCurrentNode),
                   "Bogus arguments");
@@ -4594,17 +4602,17 @@ EditorBase::CreateTxnForDeleteRange(nsRa
   // XXX: if isFirst && isLast, then we'll need to delete the node
   //      as well as the 1 child
 
   // build a transaction for deleting the appropriate data
   // XXX: this has to come from rule section
   if (aAction == ePrevious && isFirst) {
     // we're backspacing from the beginning of the node.  Delete the first
     // thing to our left
-    nsCOMPtr<nsIContent> priorNode = GetPriorNode(node, true);
+    nsCOMPtr<nsIContent> priorNode = GetPreviousEditableNode(*node);
     if (NS_WARN_IF(!priorNode)) {
       return nullptr;
     }
 
     // there is a priorNode, so delete its last child (if chardata, delete the
     // last char). if it has no children, delete it
     if (priorNode->IsNodeOfType(nsINode::eDATA_NODE)) {
       RefPtr<nsGenericDOMDataNode> priorNodeAsCharData =
@@ -4688,27 +4696,28 @@ EditorBase::CreateTxnForDeleteRange(nsRa
     node.forget(aRemovingNode);
     return deleteTextTransaction.forget();
   }
 
   // we're either deleting a node or chardata, need to dig into the next/prev
   // node to find out
   nsCOMPtr<nsINode> selectedNode;
   if (aAction == ePrevious) {
-    selectedNode = GetPriorNode(node, offset, child, true);
+    selectedNode =
+      GetPreviousEditableNode(EditorRawDOMPoint(node, child, offset));
   } else if (aAction == eNext) {
     selectedNode = GetNextNode(node, offset, child, true);
   }
 
   while (selectedNode &&
          selectedNode->IsNodeOfType(nsINode::eDATA_NODE) &&
          !selectedNode->Length()) {
     // Can't delete an empty chardata node (bug 762183)
     if (aAction == ePrevious) {
-      selectedNode = GetPriorNode(selectedNode, true);
+      selectedNode = GetPreviousEditableNode(*selectedNode);
     } else if (aAction == eNext) {
       selectedNode = GetNextNode(selectedNode, true);
     }
   }
 
   if (NS_WARN_IF(!selectedNode)) {
     return nullptr;
   }
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -602,22 +602,44 @@ protected:
    * that the editor's sync/async settings for reflowing, painting, and
    * scrolling match.
    */
   nsresult ScrollSelectionIntoView(bool aScrollToAnchor);
 
   virtual bool IsBlockNode(nsINode* aNode);
 
   /**
-   * Helper for GetPriorNode() and GetNextNode().
+   * Helper for GetPreviousNodeInternal() and GetNextNode().
    */
   nsIContent* FindNextLeafNode(nsINode* aCurrentNode,
                                bool aGoForward,
                                bool bNoBlockCrossing);
 
+  /**
+   * Get the node immediately previous node of aNode.
+   * @param atNode               The node from which we start the search.
+   * @param aFindEditableNode    If true, only return an editable node.
+   * @param aNoBlockCrossing     If true, don't move across "block" nodes,
+   *                             whatever that means.
+   * @return                     The node that occurs before aNode in
+   *                             the tree, skipping non-editable nodes if
+   *                             aFindEditableNode is true.  If there is no
+   *                             previous node, returns nullptr.
+   */
+  nsIContent* GetPreviousNodeInternal(nsINode& aNode,
+                                      bool aFindEditableNode,
+                                      bool aNoBlockCrossing);
+
+  /**
+   * And another version that takes a point in DOM tree rather than a node.
+   */
+  nsIContent* GetPreviousNodeInternal(const EditorRawDOMPoint& aPoint,
+                                      bool aFindEditableNode,
+                                      bool aNoBlockCrossing);
+
   virtual nsresult InstallEventListeners();
   virtual void CreateEventListeners();
   virtual void RemoveEventListeners();
 
   /**
    * Return true if spellchecking should be enabled for this editor.
    */
   bool GetDesiredSpellCheckState();
@@ -732,38 +754,51 @@ public:
    * @param  aNode is the node to get the length of.
    *         If aNode is text, returns number of characters.
    *         If not, returns number of children nodes.
    * @param  aCount [OUT] the result of the above calculation.
    */
   static nsresult GetLengthOfDOMNode(nsIDOMNode *aNode, uint32_t &aCount);
 
   /**
-   * Get the node immediately prior to aCurrentNode.
-   * @param aCurrentNode   the node from which we start the search
-   * @param aEditableNode  if true, only return an editable node
-   * @param aResultNode    [OUT] the node that occurs before aCurrentNode in
-   *                             the tree, skipping non-editable nodes if
-   *                             aEditableNode is true.  If there is no prior
-   *                             node, aResultNode will be nullptr.
-   * @param bNoBlockCrossing If true, don't move across "block" nodes,
-   *                         whatever that means.
+   * Get the previous node.
    */
-  nsIContent* GetPriorNode(nsINode* aCurrentNode, bool aEditableNode,
-                           bool aNoBlockCrossing = false);
-
-  /**
-   * And another version that takes a {parent,offset} pair rather than a node.
-   */
-  nsIContent* GetPriorNode(nsINode* aParentNode,
-                           int32_t aOffset,
-                           nsINode* aChildAtOffset,
-                           bool aEditableNode,
-                           bool aNoBlockCrossing = false);
-
+  nsIContent* GetPreviousNode(const EditorRawDOMPoint& aPoint)
+  {
+    return GetPreviousNodeInternal(aPoint, false, false);
+  }
+  nsIContent* GetPreviousEditableNode(const EditorRawDOMPoint& aPoint)
+  {
+    return GetPreviousNodeInternal(aPoint, true, false);
+  }
+  nsIContent* GetPreviousNodeInBlock(const EditorRawDOMPoint& aPoint)
+  {
+    return GetPreviousNodeInternal(aPoint, false, true);
+  }
+  nsIContent* GetPreviousEditableNodeInBlock(
+                const EditorRawDOMPoint& aPoint)
+  {
+    return GetPreviousNodeInternal(aPoint, true, true);
+  }
+  nsIContent* GetPreviousNode(nsINode& aNode)
+  {
+    return GetPreviousNodeInternal(aNode, false, false);
+  }
+  nsIContent* GetPreviousEditableNode(nsINode& aNode)
+  {
+    return GetPreviousNodeInternal(aNode, true, false);
+  }
+  nsIContent* GetPreviousNodeInBlock(nsINode& aNode)
+  {
+    return GetPreviousNodeInternal(aNode, false, true);
+  }
+  nsIContent* GetPreviousEditableNodeInBlock(nsINode& aNode)
+  {
+    return GetPreviousNodeInternal(aNode, true, true);
+  }
 
   /**
    * Get the node immediately after to aCurrentNode.
    * @param aCurrentNode   the node from which we start the search
    * @param aEditableNode  if true, only return an editable node
    * @param aResultNode    [OUT] the node that occurs after aCurrentNode in the
    *                             tree, skipping non-editable nodes if
    *                             aEditableNode is true.  If there is no prior
@@ -778,17 +813,17 @@ public:
    */
   nsIContent* GetNextNode(nsINode* aParentNode,
                           int32_t aOffset,
                           nsINode* aChildAtOffset,
                           bool aEditableNode,
                           bool aNoBlockCrossing = false);
 
   /**
-   * Helper for GetNextNode() and GetPriorNode().
+   * Helper for GetNextNode() and GetPreviousNodeInternal().
    */
   nsIContent* FindNode(nsINode* aCurrentNode,
                        bool aGoForward,
                        bool aEditableNode,
                        bool bNoBlockCrossing);
   /**
    * Get the rightmost child of aCurrentNode;
    * return nullptr if aCurrentNode has no children.
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -5267,21 +5267,19 @@ HTMLEditRules::CheckForEmptyBlock(nsINod
           }
           nsresult rv = aSelection->Collapse(afterEmptyBlock);
           NS_ENSURE_SUCCESS(rv, rv);
         }
       } else if (aAction == nsIEditor::ePrevious ||
                  aAction == nsIEditor::ePreviousWord ||
                  aAction == nsIEditor::eToBeginningOfLine) {
         // Move to the end of the previous node
-        int32_t offset = blockParent->IndexOf(emptyBlock);
-        nsCOMPtr<nsIContent> priorNode = htmlEditor->GetPriorNode(blockParent,
-                                                                  offset,
-                                                                  emptyBlock,
-                                                                  true);
+        EditorRawDOMPoint atEmptyBlock(emptyBlock);
+        nsCOMPtr<nsIContent> priorNode =
+          htmlEditor->GetPreviousEditableNode(atEmptyBlock);
         if (priorNode) {
           EditorDOMPoint pt = GetGoodSelPointForNode(*priorNode, aAction);
           nsresult rv = aSelection->Collapse(pt.AsRaw());
           NS_ENSURE_SUCCESS(rv, rv);
         } else {
           EditorRawDOMPoint afterEmptyBlock(emptyBlock);
           if (NS_WARN_IF(!afterEmptyBlock.AdvanceOffset())) {
             return NS_ERROR_FAILURE;
@@ -7934,16 +7932,18 @@ HTMLEditRules::AdjustSelection(Selection
           // the user will see no new line for the break.  Also, things
           // like table cells won't grow in height.
           nsCOMPtr<nsIDOMNode> brNode;
           rv = CreateMozBR(GetAsDOMNode(selNode), selOffset,
                            getter_AddRefs(brNode));
           NS_ENSURE_SUCCESS(rv, rv);
           nsCOMPtr<nsIDOMNode> brParent =
             EditorBase::GetNodeLocation(brNode, &selOffset);
+          nsCOMPtr<nsIContent> br = do_QueryInterface(brNode);
+          child = br;
           // selection stays *before* moz-br, sticking to it
           aSelection->SetInterlinePosition(true);
           rv = aSelection->Collapse(brParent, selOffset);
           NS_ENSURE_SUCCESS(rv, rv);
         } else {
           NS_ENSURE_STATE(mHTMLEditor);
           nsCOMPtr<nsIContent> nextNode =
             mHTMLEditor->GetNextHTMLNode(nearNode, true);
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3876,17 +3876,18 @@ HTMLEditor::GetPriorHTMLNode(nsINode* aN
                              bool aNoBlockCrossing)
 {
   MOZ_ASSERT(aNode);
 
   if (!GetActiveEditingHost()) {
     return nullptr;
   }
 
-  return GetPriorNode(aNode, true, aNoBlockCrossing);
+  return aNoBlockCrossing ? GetPreviousEditableNodeInBlock(*aNode) :
+                            GetPreviousEditableNode(*aNode);
 }
 
 /**
  * GetPriorHTMLNode() is same as above but takes {parent,offset} instead of
  * node.
  */
 nsIContent*
 HTMLEditor::GetPriorHTMLNode(nsINode* aParent,
@@ -3895,17 +3896,22 @@ HTMLEditor::GetPriorHTMLNode(nsINode* aP
                              bool aNoBlockCrossing)
 {
   MOZ_ASSERT(aParent);
 
   if (!GetActiveEditingHost()) {
     return nullptr;
   }
 
-  return GetPriorNode(aParent, aOffset, aChildAtOffset, true, aNoBlockCrossing);
+  EditorRawDOMPoint point(aParent,
+                          aChildAtOffset && aChildAtOffset->IsContent() ?
+                            aChildAtOffset->AsContent() : nullptr,
+                          aOffset);
+  return aNoBlockCrossing ? GetPreviousEditableNodeInBlock(point) :
+                            GetPreviousEditableNode(point);
 }
 
 /**
  * GetNextHTMLNode() returns the next editable leaf node, if there is
  * one within the <body>.
  */
 nsIContent*
 HTMLEditor::GetNextHTMLNode(nsINode* aNode,