Bug 635618 part 5. Pass an optional argument to GetPriorNode and GetNextNode to limit the search. r=ehsan
authorBoris Zbarsky <bzbarsky@mit.edu>
Mon, 28 Nov 2011 07:54:32 -0500
changeset 81676 68ed3842525d9455ec5b9fee4f0ad58430ec2da6
parent 81675 4af87ec88918ba80d330ba1d5dbb86d3574b8e69
child 81677 21e68f31057698620d20c90627b29d2e040e6c3c
push idunknown
push userunknown
push dateunknown
reviewersehsan
bugs635618
milestone11.0a1
Bug 635618 part 5. Pass an optional argument to GetPriorNode and GetNextNode to limit the search. r=ehsan
editor/libeditor/base/nsEditor.cpp
editor/libeditor/base/nsEditor.h
editor/libeditor/html/nsHTMLEditor.cpp
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -3145,57 +3145,62 @@ nsEditor::GetLengthOfDOMNode(nsIDOMNode 
 }
 
 
 nsresult 
 nsEditor::GetPriorNode(nsIDOMNode  *aParentNode, 
                        PRInt32      aOffset, 
                        bool         aEditableNode, 
                        nsCOMPtr<nsIDOMNode> *aResultNode,
-                       bool         bNoBlockCrossing)
+                       bool         bNoBlockCrossing,
+                       nsIContent  *aActiveEditorRoot)
 {
   // just another version of GetPriorNode that takes a {parent, offset}
   // instead of a node
   if (!aParentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
   *aResultNode = nsnull;
   
   // if we are at beginning of node, or it is a textnode, then just look before it
   if (!aOffset || IsTextNode(aParentNode))
   {
     if (bNoBlockCrossing && IsBlockNode(aParentNode))
     {
       // if we aren't allowed to cross blocks, don't look before this block
       return NS_OK;
     }
-    return GetPriorNode(aParentNode, aEditableNode, aResultNode, bNoBlockCrossing);
+    return GetPriorNode(aParentNode, aEditableNode, aResultNode,
+                        bNoBlockCrossing, aActiveEditorRoot);
   }
 
   // else look before the child at 'aOffset'
   nsCOMPtr<nsIDOMNode> child = GetChildAt(aParentNode, aOffset);
   if (child)
-    return GetPriorNode(child, aEditableNode, aResultNode, bNoBlockCrossing);
+    return GetPriorNode(child, aEditableNode, aResultNode, bNoBlockCrossing,
+                        aActiveEditorRoot);
 
   // unless there isn't one, in which case we are at the end of the node
   // and want the deep-right child.
   *aResultNode = GetRightmostChild(aParentNode, bNoBlockCrossing);
   if (!*aResultNode || !aEditableNode || IsEditable(*aResultNode))
     return NS_OK;
 
   // restart the search from the non-editable node we just found
-  nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(*aResultNode);
-  return GetPriorNode(notEditableNode, aEditableNode, aResultNode, bNoBlockCrossing);
+  nsCOMPtr<nsIDOMNode> notEditableNode = *aResultNode;
+  return GetPriorNode(notEditableNode, aEditableNode, aResultNode,
+                      bNoBlockCrossing, aActiveEditorRoot);
 }
 
 
 nsresult 
 nsEditor::GetNextNode(nsIDOMNode   *aParentNode, 
-                       PRInt32      aOffset, 
-                       bool         aEditableNode, 
-                       nsCOMPtr<nsIDOMNode> *aResultNode,
-                       bool         bNoBlockCrossing)
+                      PRInt32      aOffset, 
+                      bool         aEditableNode, 
+                      nsCOMPtr<nsIDOMNode> *aResultNode,
+                      bool         bNoBlockCrossing,
+                      nsIContent  *aActiveEditorRoot)
 {
   // just another version of GetNextNode that takes a {parent, offset}
   // instead of a node
   if (!aParentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
   
   *aResultNode = nsnull;
 
   // if aParentNode is a text node, use it's location instead
@@ -3227,51 +3232,68 @@ nsEditor::GetNextNode(nsIDOMNode   *aPar
       return NS_OK;
     }
 
     if (!aEditableNode || IsEditable(*aResultNode))
       return NS_OK;
 
     // restart the search from the non-editable node we just found
     nsCOMPtr<nsIDOMNode> notEditableNode = do_QueryInterface(*aResultNode);
-    return GetNextNode(notEditableNode, aEditableNode, aResultNode, bNoBlockCrossing);
+    return GetNextNode(notEditableNode, aEditableNode, aResultNode,
+                       bNoBlockCrossing, aActiveEditorRoot);
   }
     
   // unless there isn't one, in which case we are at the end of the node
   // and want the next one.
   if (bNoBlockCrossing && IsBlockNode(aParentNode))
   {
     // don't cross out of parent block
     return NS_OK;
   }
-  return GetNextNode(aParentNode, aEditableNode, aResultNode, bNoBlockCrossing);
+  return GetNextNode(aParentNode, aEditableNode, aResultNode, bNoBlockCrossing,
+                     aActiveEditorRoot);
 }
 
 
 nsresult 
 nsEditor::GetPriorNode(nsIDOMNode  *aCurrentNode, 
                        bool         aEditableNode, 
                        nsCOMPtr<nsIDOMNode> *aResultNode,
-                       bool         bNoBlockCrossing)
+                       bool         bNoBlockCrossing,
+                       nsIContent  *aActiveEditorRoot)
 {
   if (!aCurrentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
 
   nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode);
+
+  if (!IsDescendantOfBody(currentNode) ||
+      (aActiveEditorRoot &&
+       !nsContentUtils::ContentIsDescendantOf(currentNode,
+                                              aActiveEditorRoot))) {
+    *aResultNode = nsnull;
+    return NS_OK;
+  }
+
   *aResultNode =
-    do_QueryInterface(FindNode(currentNode, false, aEditableNode, bNoBlockCrossing));
+    do_QueryInterface(FindNode(currentNode, false, aEditableNode,
+                               bNoBlockCrossing, aActiveEditorRoot));
   return NS_OK;
 }
 
 nsIContent*
 nsEditor::FindNextLeafNode(nsINode  *aCurrentNode, 
                            bool      aGoForward,
-                           bool      bNoBlockCrossing)
+                           bool      bNoBlockCrossing,
+                           nsIContent *aActiveEditorRoot)
 {
   // called only by GetPriorNode so we don't need to check params.
-  NS_PRECONDITION(IsDescendantOfBody(aCurrentNode) && !IsRootNode(aCurrentNode),
+  NS_PRECONDITION(IsDescendantOfBody(aCurrentNode) && !IsRootNode(aCurrentNode) &&
+                  (!aActiveEditorRoot ||
+                   nsContentUtils::ContentIsDescendantOf(aCurrentNode,
+                                                         aActiveEditorRoot)),
                   "Bogus arguments");
 
   nsINode* cur = aCurrentNode;
   for (;;) {
     // if aCurrentNode has a sibling in the right direction, return
     // that sibling's closest child (or itself if it has no children)
     nsIContent* sibling =
       aGoForward ? cur->GetNextSibling() : cur->GetPreviousSibling();
@@ -3295,68 +3317,82 @@ nsEditor::FindNextLeafNode(nsINode  *aCu
       return nsnull;
     }
 
     NS_ASSERTION(IsDescendantOfBody(parent),
                  "We started with a proper descendant of root, and should stop "
                  "if we ever hit the root, so we better have a descendant of "
                  "root now!");
     if (IsRootNode(parent) ||
-        (bNoBlockCrossing && IsBlockNode(parent))) {
+        (bNoBlockCrossing && IsBlockNode(parent)) ||
+        parent == aActiveEditorRoot) {
       return nsnull;
     }
 
     cur = parent;
   }
 
   NS_NOTREACHED("What part of for(;;) do you not understand?");
   return nsnull;
 }
 
 nsresult 
 nsEditor::GetNextNode(nsIDOMNode  *aCurrentNode, 
                       bool         aEditableNode, 
                       nsCOMPtr<nsIDOMNode> *aResultNode,
-                      bool         bNoBlockCrossing)
+                      bool         bNoBlockCrossing,
+                      nsIContent  *aActiveEditorRoot)
 {
   if (!aCurrentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
 
   nsCOMPtr<nsINode> currentNode = do_QueryInterface(aCurrentNode);
+  if (!IsDescendantOfBody(currentNode) ||
+      (aActiveEditorRoot &&
+       !nsContentUtils::ContentIsDescendantOf(currentNode,
+                                              aActiveEditorRoot))) {
+    *aResultNode = nsnull;
+    return NS_OK;
+  }
+
   *aResultNode =
-    do_QueryInterface(FindNode(currentNode, true, aEditableNode, bNoBlockCrossing));
+    do_QueryInterface(FindNode(currentNode, true, aEditableNode,
+                               bNoBlockCrossing, aActiveEditorRoot));
   return NS_OK;
 }
 
 nsIContent*
 nsEditor::FindNode(nsINode *aCurrentNode,
                    bool     aGoForward,
                    bool     aEditableNode,
-                   bool     bNoBlockCrossing)
-{
-  if (IsRootNode(aCurrentNode))
+                   bool     bNoBlockCrossing,
+                   nsIContent *aActiveEditorRoot)
+{
+  if (IsRootNode(aCurrentNode) || aCurrentNode == aActiveEditorRoot)
   {
     // Don't allow traversal above the root node! This helps
     // prevent us from accidentally editing browser content
     // when the editor is in a text widget.
 
     return nsnull;
   }
 
   nsIContent* candidate =
-    FindNextLeafNode(aCurrentNode, aGoForward, bNoBlockCrossing);
+    FindNextLeafNode(aCurrentNode, aGoForward, bNoBlockCrossing,
+                     aActiveEditorRoot);
   
   if (!candidate) {
     return nsnull;
   }
 
   if (!aEditableNode || IsEditable(candidate)) {
     return candidate;
   }
 
-  return FindNode(candidate, aGoForward, aEditableNode, bNoBlockCrossing);
+  return FindNode(candidate, aGoForward, aEditableNode, bNoBlockCrossing,
+                  aActiveEditorRoot);
 }
 
 already_AddRefed<nsIDOMNode>
 nsEditor::GetRightmostChild(nsIDOMNode *aCurrentNode, 
                             bool bNoBlockCrossing)
 {
   NS_ENSURE_TRUE(aCurrentNode, nsnull);
   nsCOMPtr<nsIDOMNode> resultNode, temp = aCurrentNode;
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -368,17 +368,18 @@ protected:
 
   // stub.  see comment in source.                     
   virtual bool IsBlockNode(nsIDOMNode *aNode);
   virtual bool IsBlockNode(nsINode *aNode);
   
   // helper for GetPriorNode and GetNextNode
   nsIContent* FindNextLeafNode(nsINode  *aCurrentNode,
                                bool      aGoForward,
-                               bool      bNoBlockCrossing);
+                               bool      bNoBlockCrossing,
+                               nsIContent *aActiveEditorRoot);
 
   // Get nsIWidget interface
   nsresult GetWidget(nsIWidget **aWidget);
 
 
   // install the event listeners for the editor 
   virtual nsresult InstallEventListeners();
 
@@ -469,53 +470,60 @@ public:
   static nsresult GetLengthOfDOMNode(nsIDOMNode *aNode, PRUint32 &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 nsnull.
+    * @param bNoBlockCrossing If true, don't move across "block" nodes, whatever that means.
+    * @param aActiveEditorRoot If non-null, only return descendants of aActiveEditorRoot.
     */
   nsresult GetPriorNode(nsIDOMNode  *aCurrentNode, 
                         bool         aEditableNode,
                         nsCOMPtr<nsIDOMNode> *aResultNode,
-                        bool         bNoBlockCrossing = false);
+                        bool         bNoBlockCrossing = false,
+                        nsIContent  *aActiveEditorRoot = nsnull);
 
   // and another version that takes a {parent,offset} pair rather than a node
   nsresult GetPriorNode(nsIDOMNode  *aParentNode, 
                         PRInt32      aOffset, 
                         bool         aEditableNode, 
                         nsCOMPtr<nsIDOMNode> *aResultNode,
-                        bool         bNoBlockCrossing = false);
+                        bool         bNoBlockCrossing = false,
+                        nsIContent  *aActiveEditorRoot = nsnull);
                        
   /** 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 node, aResultNode will be nsnull.
     */
   nsresult GetNextNode(nsIDOMNode  *aCurrentNode, 
                        bool         aEditableNode,
                        nsCOMPtr<nsIDOMNode> *aResultNode,
-                       bool         bNoBlockCrossing = false);
+                       bool         bNoBlockCrossing = false,
+                       nsIContent  *aActiveEditorRoot = nsnull);
 
   // and another version that takes a {parent,offset} pair rather than a node
   nsresult GetNextNode(nsIDOMNode  *aParentNode, 
                        PRInt32      aOffset, 
                        bool         aEditableNode, 
                        nsCOMPtr<nsIDOMNode> *aResultNode,
-                       bool         bNoBlockCrossing = false);
+                       bool         bNoBlockCrossing = false,
+                       nsIContent  *aActiveEditorRoot = nsnull);
 
   // Helper for GetNextNode and GetPriorNode
   nsIContent* FindNode(nsINode *aCurrentNode,
                        bool     aGoForward,
                        bool     aEditableNode,
-                       bool     bNoBlockCrossing);
+                       bool     bNoBlockCrossing,
+                       nsIContent *aActiveEditorRoot);
   /**
    * Get the rightmost child of aCurrentNode;
    * return nsnull if aCurrentNode has no children.
    */
   already_AddRefed<nsIDOMNode> GetRightmostChild(nsIDOMNode *aCurrentNode, 
                                                  bool        bNoBlockCrossing = false);
   nsIContent* GetRightmostChild(nsINode *aCurrentNode,
                                 bool     bNoBlockCrossing = false);
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -4701,41 +4701,51 @@ nsHTMLEditor::GetNextHTMLSibling(nsIDOMN
 ///////////////////////////////////////////////////////////////////////////
 // GetPriorHTMLNode: returns the previous editable leaf node, if there is
 //                   one within the <body>
 //
 nsresult
 nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode, bool bNoBlockCrossing)
 {
   NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER);
-  nsresult res = GetPriorNode(inNode, true, address_of(*outNode), bNoBlockCrossing);
+
+  nsIContent* activeEditingHost = GetActiveEditingHost();
+  if (!activeEditingHost) {
+    *outNode = nsnull;
+    return NS_OK;
+  }
+
+  nsresult res = GetPriorNode(inNode, true, address_of(*outNode), bNoBlockCrossing, activeEditingHost);
   NS_ENSURE_SUCCESS(res, res);
   
-  // if it's not in the body, then zero it out
-  if (*outNode && !IsNodeInActiveEditor(*outNode)) {
-    *outNode = nsnull;
-  }
+  NS_ASSERTION(!*outNode || IsNodeInActiveEditor(*outNode),
+               "GetPriorNode screwed up");
   return res;
 }
 
 
 ///////////////////////////////////////////////////////////////////////////
 // GetPriorHTMLNode: same as above but takes {parent,offset} instead of node
 //                       
 nsresult
 nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outNode, bool bNoBlockCrossing)
 {
   NS_ENSURE_TRUE(outNode, NS_ERROR_NULL_POINTER);
-  nsresult res = GetPriorNode(inParent, inOffset, true, address_of(*outNode), bNoBlockCrossing);
+
+  nsIContent* activeEditingHost = GetActiveEditingHost();
+  if (!activeEditingHost) {
+    *outNode = nsnull;
+    return NS_OK;
+  }
+
+  nsresult res = GetPriorNode(inParent, inOffset, true, address_of(*outNode), bNoBlockCrossing, activeEditingHost);
   NS_ENSURE_SUCCESS(res, res);
   
-  // if it's not in the body, then zero it out
-  if (*outNode && !IsNodeInActiveEditor(*outNode)) {
-    *outNode = nsnull;
-  }
+  NS_ASSERTION(!*outNode || IsNodeInActiveEditor(*outNode),
+               "GetPriorNode screwed up");
   return res;
 }
 
 
 ///////////////////////////////////////////////////////////////////////////
 // GetNextHTMLNode: returns the next editable leaf node, if there is
 //                   one within the <body>
 //