Bug 777674 - setCurrentNode for inDeepTreeWalker. r=bz
authorGabor Krizsanits <gkrizsanits@mozilla.com>
Wed, 24 Sep 2014 07:20:43 +0200
changeset 230204 f33605277b510e922b55275e076f596fd4f3594b
parent 230203 156b285c2025cd516c056865fa3e9fd56b27434e
child 230205 a6fe5d357027f31c0630326494b64bd228c9e555
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs777674
milestone35.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 777674 - setCurrentNode for inDeepTreeWalker. r=bz
layout/inspector/inDeepTreeWalker.cpp
layout/inspector/inDeepTreeWalker.h
layout/inspector/inIDeepTreeWalker.idl
layout/inspector/tests/file_bug522601.html
layout/inspector/tests/mochitest.ini
layout/inspector/tests/test_bug522601.xhtml
--- a/layout/inspector/inDeepTreeWalker.cpp
+++ b/layout/inspector/inDeepTreeWalker.cpp
@@ -21,25 +21,26 @@
 /*****************************************************************************
  * This implementation does not currently operaate according to the W3C spec.
  * In particular it does NOT handle DOM mutations during the walk.  It also
  * ignores whatToShow and the filter.
  *****************************************************************************/
 
 ////////////////////////////////////////////////////
 
-inDeepTreeWalker::inDeepTreeWalker() 
+inDeepTreeWalker::inDeepTreeWalker()
   : mShowAnonymousContent(false),
     mShowSubDocuments(false),
+    mShowDocumentsAsNodes(false),
     mWhatToShow(nsIDOMNodeFilter::SHOW_ALL)
 {
 }
 
-inDeepTreeWalker::~inDeepTreeWalker() 
-{ 
+inDeepTreeWalker::~inDeepTreeWalker()
+{
 }
 
 NS_IMPL_ISUPPORTS(inDeepTreeWalker,
                   inIDeepTreeWalker)
 
 ////////////////////////////////////////////////////
 // inIDeepTreeWalker
 
@@ -67,35 +68,52 @@ inDeepTreeWalker::GetShowSubDocuments(bo
 NS_IMETHODIMP
 inDeepTreeWalker::SetShowSubDocuments(bool aShowSubDocuments)
 {
   mShowSubDocuments = aShowSubDocuments;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+inDeepTreeWalker::GetShowDocumentsAsNodes(bool *aShowDocumentsAsNodes)
+{
+  *aShowDocumentsAsNodes = mShowDocumentsAsNodes;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::SetShowDocumentsAsNodes(bool aShowDocumentsAsNodes)
+{
+  mShowDocumentsAsNodes = aShowDocumentsAsNodes;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 inDeepTreeWalker::Init(nsIDOMNode* aRoot, uint32_t aWhatToShow)
 {
+  if (!aRoot) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
   mRoot = aRoot;
+  mCurrentNode = aRoot;
   mWhatToShow = aWhatToShow;
-  
-  PushNode(aRoot);
 
-  return NS_OK;
+  mDOMUtils = do_GetService("@mozilla.org/inspector/dom-utils;1");
+  return mDOMUtils ? NS_OK : NS_ERROR_UNEXPECTED;
 }
 
 ////////////////////////////////////////////////////
 // nsIDOMTreeWalker
 
 NS_IMETHODIMP
 inDeepTreeWalker::GetRoot(nsIDOMNode** aRoot)
 {
   *aRoot = mRoot;
   NS_IF_ADDREF(*aRoot);
-  
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 inDeepTreeWalker::GetWhatToShow(uint32_t* aWhatToShow)
 {
   *aWhatToShow = mWhatToShow;
   return NS_OK;
@@ -107,156 +125,255 @@ inDeepTreeWalker::GetFilter(nsIDOMNodeFi
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 inDeepTreeWalker::GetCurrentNode(nsIDOMNode** aCurrentNode)
 {
   *aCurrentNode = mCurrentNode;
   NS_IF_ADDREF(*aCurrentNode);
-  
   return NS_OK;
 }
 
+already_AddRefed<nsIDOMNode>
+inDeepTreeWalker::GetParent()
+{
+  if (mCurrentNode == mRoot) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIDOMNode> parent;
+  MOZ_ASSERT(mDOMUtils, "mDOMUtils should have been initiated already in Init");
+  mDOMUtils->GetParentForNode(mCurrentNode, mShowAnonymousContent,
+                              getter_AddRefs(parent));
+
+  uint16_t nodeType = 0;
+  if (parent) {
+    parent->GetNodeType(&nodeType);
+  }
+  // For compatibility reasons by default we skip the document nodes
+  // from the walk.
+  if (!mShowDocumentsAsNodes &&
+      nodeType == nsIDOMNode::DOCUMENT_NODE &&
+      parent != mRoot) {
+    mDOMUtils->GetParentForNode(parent, mShowAnonymousContent,
+                                getter_AddRefs(parent));
+  }
+
+  return parent.forget();
+}
+
+static already_AddRefed<nsINodeList>
+GetChildren(nsIDOMNode* aParent,
+            bool aShowAnonymousContent,
+            bool aShowSubDocuments)
+{
+  MOZ_ASSERT(aParent);
+
+  nsCOMPtr<nsINodeList> ret;
+  if (aShowSubDocuments) {
+    nsCOMPtr<nsIDOMDocument> domdoc = inLayoutUtils::GetSubDocumentFor(aParent);
+    if (domdoc) {
+      aParent = domdoc;
+    }
+  }
+
+  nsCOMPtr<nsIContent> parentAsContent = do_QueryInterface(aParent);
+  if (parentAsContent && aShowAnonymousContent) {
+      ret = parentAsContent->GetChildren(nsIContent::eAllChildren);
+  } else {
+    // If it's not a content, then it's a document (or an attribute but we can ignore that
+    // case here). If aShowAnonymousContent is false we also want to fall back to ChildNodes
+    // so we can skip any native anon content that GetChildren would return.
+    nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParent);
+    MOZ_ASSERT(parentNode);
+    ret = parentNode->ChildNodes();
+  }
+  return ret.forget();
+}
+
 NS_IMETHODIMP
 inDeepTreeWalker::SetCurrentNode(nsIDOMNode* aCurrentNode)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+  // mCurrentNode can only be null if init either failed, or has not been
+  // called yet.
+  if (!mCurrentNode || !aCurrentNode) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // If Document nodes are skipped by the walk, we should not allow
+  // one to set one as the current node either.
+  uint16_t nodeType = 0;
+  aCurrentNode->GetNodeType(&nodeType);
+  if (!mShowDocumentsAsNodes && nodeType == nsIDOMNode::DOCUMENT_NODE) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return SetCurrentNode(aCurrentNode, nullptr);
+}
+
+
+nsresult
+inDeepTreeWalker::SetCurrentNode(nsIDOMNode* aCurrentNode,
+                                 nsINodeList* aSiblings)
+{
+  MOZ_ASSERT(aCurrentNode);
+
+  // We want to store the original state so in case of error
+  // we can restore that.
+  nsCOMPtr<nsINodeList> tmpSiblings = mSiblings;
+  nsCOMPtr<nsIDOMNode> tmpCurrent = mCurrentNode;
+  mSiblings = aSiblings;
+  mCurrentNode = aCurrentNode;
+
+  // If siblings were not passed in as argument we have to
+  // get them from the parent node of aCurrentNode.
+  // Note: in the mShowDoucmentsAsNodes case when a sub document
+  // is set as the current, we don't want to get the children
+  // from the iframe accidentally here, so let's just skip this
+  // part for document nodes, they should never have siblings.
+  uint16_t nodeType = 0;
+  aCurrentNode->GetNodeType(&nodeType);
+  if (!mSiblings && nodeType != nsIDOMNode::DOCUMENT_NODE) {
+    nsCOMPtr<nsIDOMNode> parent = GetParent();
+    if (parent) {
+      mSiblings = GetChildren(parent,
+                              mShowAnonymousContent,
+                              mShowSubDocuments);
+    }
+  }
+
+  if (mSiblings && mSiblings->Length()) {
+    // We cached all the siblings (if there are any) of the current node, but we
+    // still have to set the index too, to be able to iterate over them.
+    nsCOMPtr<nsIContent> currentAsContent = do_QueryInterface(mCurrentNode);
+    MOZ_ASSERT(currentAsContent);
+    int32_t index = mSiblings->IndexOf(currentAsContent);
+    if (index < 0) {
+      // If someone tries to set current node to some value that is not reachable
+      // otherwise, let's throw. (For example mShowAnonymousContent is false and some
+      // XBL anon content was passed in)
+
+      // Restore state first.
+      mCurrentNode = tmpCurrent;
+      mSiblings = tmpSiblings;
+      return NS_ERROR_INVALID_ARG;
+    }
+    mCurrentIndex = index;
+  } else {
+    mCurrentIndex = -1;
+  }
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 inDeepTreeWalker::ParentNode(nsIDOMNode** _retval)
 {
   *_retval = nullptr;
-  if (!mCurrentNode) return NS_OK;
+  if (!mCurrentNode || mCurrentNode == mRoot) {
+    return NS_OK;
+  }
 
-  if (mStack.Length() == 1) {
-    // No parent
+  nsCOMPtr<nsIDOMNode> parent = GetParent();
+
+  if (!parent) {
     return NS_OK;
   }
 
-  // Pop off the current node, and push the new one
-  mStack.RemoveElementAt(mStack.Length()-1);
-  DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1);
-  mCurrentNode = top.node;
-  top.lastIndex = 0;
-  NS_ADDREF(*_retval = mCurrentNode);
+  nsresult rv = SetCurrentNode(parent);
+  NS_ENSURE_SUCCESS(rv,rv);
+
+  parent.forget(_retval);
+  return NS_OK;
+}
+
+// FirstChild and LastChild are very similar methods, this is the generic
+// version for internal use. With aReverse = true it returns the LastChild.
+nsresult
+inDeepTreeWalker::EdgeChild(nsIDOMNode** _retval, bool aFront)
+{
+  if (!mCurrentNode) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *_retval = nullptr;
+
+  nsCOMPtr<nsIDOMNode> echild;
+  if (mShowSubDocuments && mShowDocumentsAsNodes) {
+    // GetChildren below, will skip the document node from
+    // the walk. But if mShowDocumentsAsNodes is set to true
+    // we want to include the (sub)document itself too.
+    echild = inLayoutUtils::GetSubDocumentFor(mCurrentNode);
+  }
+
+  nsCOMPtr<nsINodeList> children;
+  if (!echild) {
+    children = GetChildren(mCurrentNode,
+                           mShowAnonymousContent,
+                           mShowSubDocuments);
+    if (children && children->Length() > 0) {
+      nsINode* childNode = children->Item(aFront ? 0 : children->Length() - 1);
+      echild = childNode ? childNode->AsDOMNode() : nullptr;
+    }
+  }
+
+  if (echild) {
+    nsresult rv = SetCurrentNode(echild, children);
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ADDREF(*_retval = mCurrentNode);
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
-inDeepTreeWalker::FirstChild(nsIDOMNode **_retval)
+inDeepTreeWalker::FirstChild(nsIDOMNode** _retval)
 {
-  *_retval = nullptr;
-  if (!mCurrentNode) {
-    return NS_OK;
-  }
-
-  DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1);
-  nsCOMPtr<nsIDOMNode> kid;
-  top.kids->Item(0, getter_AddRefs(kid));
-  if (!kid) {
-    return NS_OK;
-  }
-  top.lastIndex = 1;
-  PushNode(kid);
-  kid.forget(_retval);
-  return NS_OK;
+  return EdgeChild(_retval, /* aFront = */ true);
 }
 
 NS_IMETHODIMP
 inDeepTreeWalker::LastChild(nsIDOMNode **_retval)
 {
-  *_retval = nullptr;
-  if (!mCurrentNode) {
-    return NS_OK;
-  }
-
-  DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1);
-  nsCOMPtr<nsIDOMNode> kid;
-  uint32_t length;
-  top.kids->GetLength(&length);
-  top.kids->Item(length - 1, getter_AddRefs(kid));
-  if (!kid) {
-    return NS_OK;
-  }
-  top.lastIndex = length;
-  PushNode(kid);
-  kid.forget(_retval);
-  return NS_OK;
+  return EdgeChild(_retval, /* aFront = */ false);
 }
 
 NS_IMETHODIMP
 inDeepTreeWalker::PreviousSibling(nsIDOMNode **_retval)
 {
   *_retval = nullptr;
-  if (!mCurrentNode) {
-    return NS_OK;
-  }
-
-  NS_ASSERTION(mStack.Length() > 0, "Should have things in mStack");
-
-  if (mStack.Length() == 1) {
-    // No previous sibling
+  if (!mCurrentNode || !mSiblings || mCurrentIndex < 1) {
     return NS_OK;
   }
 
-  DeepTreeStackItem& parent = mStack.ElementAt(mStack.Length()-2);
-  nsCOMPtr<nsIDOMNode> previousSibling;
-  parent.kids->Item(parent.lastIndex-2, getter_AddRefs(previousSibling));
-  if (!previousSibling) {
-    return NS_OK;
-  }
-
-  // Our mStack's topmost element is our current node. Since we're trying to
-  // change that to the previous sibling, pop off the current node, and push
-  // the new one.
-  mStack.RemoveElementAt(mStack.Length() - 1);
-  parent.lastIndex--;
-  PushNode(previousSibling);
-  previousSibling.forget(_retval);
+  nsIContent* prev = mSiblings->Item(--mCurrentIndex);
+  mCurrentNode = prev->AsDOMNode();
+  NS_ADDREF(*_retval = mCurrentNode);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 inDeepTreeWalker::NextSibling(nsIDOMNode **_retval)
 {
   *_retval = nullptr;
-  if (!mCurrentNode) {
-    return NS_OK;
-  }
-
-  NS_ASSERTION(mStack.Length() > 0, "Should have things in mStack");
-
-  if (mStack.Length() == 1) {
-    // No next sibling
+  if (!mCurrentNode || !mSiblings ||
+      mCurrentIndex + 1 >= (int32_t) mSiblings->Length()) {
     return NS_OK;
   }
 
-  DeepTreeStackItem& parent = mStack.ElementAt(mStack.Length()-2);
-  nsCOMPtr<nsIDOMNode> nextSibling;
-  parent.kids->Item(parent.lastIndex, getter_AddRefs(nextSibling));
-  if (!nextSibling) {
-    return NS_OK;
-  }
-
-  // Our mStack's topmost element is our current node. Since we're trying to
-  // change that to the next sibling, pop off the current node, and push
-  // the new one.
-  mStack.RemoveElementAt(mStack.Length() - 1);
-  parent.lastIndex++;
-  PushNode(nextSibling);
-  nextSibling.forget(_retval);
+  nsIContent* next = mSiblings->Item(++mCurrentIndex);
+  mCurrentNode = next->AsDOMNode();
+  NS_ADDREF(*_retval = mCurrentNode);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 inDeepTreeWalker::PreviousNode(nsIDOMNode **_retval)
 {
-  if (!mCurrentNode || mStack.Length() == 1) {
+  if (!mCurrentNode || mCurrentNode == mRoot) {
     // Nowhere to go from here
     *_retval = nullptr;
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMNode> node;
   PreviousSibling(getter_AddRefs(node));
 
@@ -275,16 +392,20 @@ inDeepTreeWalker::PreviousNode(nsIDOMNod
 
   NS_ADDREF(*_retval = mCurrentNode);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 inDeepTreeWalker::NextNode(nsIDOMNode **_retval)
 {
+  if (!mCurrentNode) {
+    return NS_OK;
+  }
+
   // First try our kids
   FirstChild(_retval);
 
   if (*_retval) {
     return NS_OK;
   }
 
   // Now keep trying next siblings up the parent chain, but if we
@@ -314,92 +435,8 @@ inDeepTreeWalker::NextNode(nsIDOMNode **
       return NS_OK;
     }
     ++lastChildCallsToMake;
   }
 
   NS_NOTREACHED("how did we get here?");
   return NS_OK;
 }
-
-void
-inDeepTreeWalker::PushNode(nsIDOMNode* aNode)
-{
-  mCurrentNode = aNode;
-  if (!aNode) return;
-
-  DeepTreeStackItem item;
-  item.node = aNode;
-
-  nsCOMPtr<nsIDOMNodeList> kids;
-  if (mShowSubDocuments) {
-    nsCOMPtr<nsIDOMDocument> domdoc = inLayoutUtils::GetSubDocumentFor(aNode);
-    if (domdoc) {
-      domdoc->GetChildNodes(getter_AddRefs(kids));
-    }
-  }
-  
-  if (!kids) {
-    nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
-    if (content && mShowAnonymousContent) {
-      kids = content->GetChildren(nsIContent::eAllChildren);
-    }
-  }
-  if (!kids) {
-    aNode->GetChildNodes(getter_AddRefs(kids));
-  }
-  
-  item.kids = kids;
-  item.lastIndex = 0;
-  mStack.AppendElement(item);
-}
-
-/*
-// This NextNode implementation does not require the use of stacks, 
-// as does the one above. However, it does not handle anonymous 
-// content and sub-documents.
-NS_IMETHODIMP
-inDeepTreeWalker::NextNode(nsIDOMNode **_retval)
-{
-  if (!mCurrentNode) return NS_OK;
-  
-  // walk down the tree first
-  nsCOMPtr<nsIDOMNode> next;
-  mCurrentNode->GetFirstChild(getter_AddRefs(next));
-  if (!next) {
-    mCurrentNode->GetNextSibling(getter_AddRefs(next));
-    if (!next) { 
-      // we've hit the end, so walk back up the tree until another
-      // downward opening is found, or the top of the tree
-      nsCOMPtr<nsIDOMNode> subject = mCurrentNode;
-      nsCOMPtr<nsIDOMNode> parent;
-      while (1) {
-        subject->GetParentNode(getter_AddRefs(parent));
-        if (!parent) // hit the top of the tree
-          break;
-        parent->GetNextSibling(getter_AddRefs(subject));
-        if (subject) { // found a downward opening
-          next = subject;
-          break;
-        } else // walk up another level
-          subject = parent;
-      } 
-    }
-  }
-  
-  mCurrentNode = next;
-  
-  *_retval = next;
-  NS_IF_ADDREF(*_retval);
-  
-  return NS_OK;
-}
-
-
-char* getURL(nsIDOMDocument* aDoc)
-{
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
-  nsIURI *uri = doc->GetDocumentURI();
-  char* s;
-  uri->GetSpec(&s);
-  return s;
-}
-*/
--- a/layout/inspector/inDeepTreeWalker.h
+++ b/layout/inspector/inDeepTreeWalker.h
@@ -6,50 +6,60 @@
 #define __inDeepTreeWalker_h___
 
 #include "inIDeepTreeWalker.h"
 
 #include "nsCOMPtr.h"
 #include "nsIDOMNode.h"
 #include "nsTArray.h"
 
+class nsINodeList;
 class inIDOMUtils;
 
-////////////////////////////////////////////////////
-
-struct DeepTreeStackItem
-{
-  nsCOMPtr<nsIDOMNode> node;
-  nsCOMPtr<nsIDOMNodeList> kids;
-  uint32_t lastIndex; // Index one bigger than the index of whatever
-                      // kid we're currently at in |kids|.
-};
-
-////////////////////////////////////////////////////
-
 class inDeepTreeWalker : public inIDeepTreeWalker
 {
 public:
-	NS_DECL_ISUPPORTS
-	NS_DECL_INIDEEPTREEWALKER
+  NS_DECL_ISUPPORTS
+  NS_DECL_INIDEEPTREEWALKER
 
   inDeepTreeWalker();
 
+  nsresult SetCurrentNode(nsIDOMNode* aCurrentNode,
+                          nsINodeList* aSiblings);
 protected:
   virtual ~inDeepTreeWalker();
 
-  void PushNode(nsIDOMNode* aNode);
+  already_AddRefed<nsIDOMNode> GetParent();
+  nsresult EdgeChild(nsIDOMNode** _retval, bool aReverse);
 
   bool mShowAnonymousContent;
   bool mShowSubDocuments;
+  bool mShowDocumentsAsNodes;
+
+  // The root node. previousNode and parentNode will return
+  // null from here.
   nsCOMPtr<nsIDOMNode> mRoot;
   nsCOMPtr<nsIDOMNode> mCurrentNode;
+  nsCOMPtr<inIDOMUtils> mDOMUtils;
+
+  // We cache the siblings of mCurrentNode as a list of nodes.
+  // Notes: normally siblings are all the children of the parent
+  // of mCurrentNode (that are interesting for use for the walk)
+  // and mCurrentIndex is the index of mCurrentNode in that list
+  // But if mCurrentNode is a (sub) document then instead of
+  // storing a list that has only one element (the document)
+  // and setting mCurrentIndex to null, we set mSibilings to null.
+  // The reason for this is purely technical, since nsINodeList is
+  // nsIContent based hence we cannot use it to store a document node.
+  nsCOMPtr<nsINodeList> mSiblings;
+
+  // Index of mCurrentNode in the mSiblings list.
+  int32_t mCurrentIndex;
+
+  // Currently unused. Should be a filter for nodes.
   uint32_t mWhatToShow;
-  
-  nsAutoTArray<DeepTreeStackItem, 8> mStack;
-  nsCOMPtr<inIDOMUtils> mDOMUtils;
 };
 
 // {BFCB82C2-5611-4318-90D6-BAF4A7864252}
 #define IN_DEEPTREEWALKER_CID \
 { 0xbfcb82c2, 0x5611, 0x4318, { 0x90, 0xd6, 0xba, 0xf4, 0xa7, 0x86, 0x42, 0x52 } }
 
 #endif // __inDeepTreeWalker_h___
--- a/layout/inspector/inIDeepTreeWalker.idl
+++ b/layout/inspector/inIDeepTreeWalker.idl
@@ -2,30 +2,44 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMNode;
 interface nsIDOMNodeFilter;
 
-[scriptable, uuid(5ba1ec15-e18f-46df-9558-c618429f3db3)]
+// Note: the iterator does not handle DOM mutations gracefully. So if
+// the underlying DOM we are iterating over is changed, the behavior
+// of the walker is undefined. (With the current implementation we
+// cache the siblings of the current node and this list is not updated
+// when a mutation occurs).
+
+[scriptable, uuid(6657e8eb-b646-48e7-993e-cfa6e96415b4)]
 interface inIDeepTreeWalker : nsISupports
 {
   attribute boolean showAnonymousContent;
   attribute boolean showSubDocuments;
-  
+
+  // By default the walker skips document nodes from the iteration,
+  // by setting this flag to true this behavior can be altered.
+  attribute boolean showDocumentsAsNodes;
+
   void init(in nsIDOMNode aRoot, in unsigned long aWhatToShow);
 
   // Methods and attributes from nsIDOMTreeWalker, which is not scriptable.
+  // Note: normally parentNode cannot go further up on the tree once it reached
+  // the root, but setting currentNode does not have this limitation. If currentNode
+  // is set to a node that does not have the root as its ancestor the walk can be
+  // continued from there, and once we reach a node that is 'under' the root, the
+  // limitation for the parentNode will work again.
   readonly attribute nsIDOMNode       root;
   readonly attribute unsigned long    whatToShow;
   readonly attribute nsIDOMNodeFilter filter;
            attribute nsIDOMNode       currentNode;
-                                        // raises(DOMException) on setting
 
   nsIDOMNode         parentNode();
   nsIDOMNode         firstChild();
   nsIDOMNode         lastChild();
   nsIDOMNode         previousSibling();
   nsIDOMNode         nextSibling();
   nsIDOMNode         previousNode();
   nsIDOMNode         nextNode();
new file mode 100644
--- /dev/null
+++ b/layout/inspector/tests/file_bug522601.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+                      "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=522601
+-->
+<head>
+  <title>Test for Bug 522601</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<div id="some content">
+  <h1>Blah</h1>
+</div>
+</body>
+</html>
--- a/layout/inspector/tests/mochitest.ini
+++ b/layout/inspector/tests/mochitest.ini
@@ -1,10 +1,12 @@
 [DEFAULT]
-support-files = bug856317.css
+support-files = 
+  bug856317.css
+  file_bug522601.html
 
 [test_bug462787.html]
 [test_bug462789.html]
 [test_bug522601.xhtml]
 [test_bug536379.html]
 [test_bug536379-2.html]
 [test_bug557726.html]
 [test_bug609549.xhtml]
--- a/layout/inspector/tests/test_bug522601.xhtml
+++ b/layout/inspector/tests/test_bug522601.xhtml
@@ -17,32 +17,81 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=522601">Mozilla Bug 522601</a>
 <p id="display" style="-moz-binding: url(#testBinding)">
   <span id="s">This is some text</span>
   More text
   <b id="b">Even more <i id="i1">Italic</i>text<i id="i2">And more italic</i></b></p>
 <div id="content" style="display: none">
   
 </div>
+<div id="subdoc">
+  <iframe id="frame1" src="file_bug522601.html">frame text</iframe>
+</div>
 <pre id="test">
 <script type="application/javascript">
 <![CDATA[
 
 /** Test for Bug 522601 **/
 SimpleTest.waitForExplicitFinish();
 
 function testFunc(walker, func, expectedNode, str) {
   var oldCurrent = SpecialPowers.unwrap(walker.currentNode);
   var newNode = SpecialPowers.unwrap(walker[func]());
   is(newNode, expectedNode, "Unexpected node after " + str);
   is(SpecialPowers.unwrap(walker.currentNode), newNode ? newNode : oldCurrent,
      "Unexpected current node after " + str);
 }
 
 addLoadEvent(function() {
+ var walkerSubDocument =
+    SpecialPowers.Cc["@mozilla.org/inspector/deep-tree-walker;1"]
+              .createInstance(SpecialPowers.Ci.inIDeepTreeWalker);
+  walkerSubDocument.showAnonymousContent = false;
+  walkerSubDocument.showSubDocuments = true;
+  walkerSubDocument.init($("frame1"), NodeFilter.SHOW_ALL);
+
+  is(SpecialPowers.unwrap(walkerSubDocument.currentNode), $("frame1"), "Unexpected sub-doc root");
+  testFunc(walkerSubDocument, "firstChild", $("frame1").contentDocument.doctype,
+           "step to sub documents doctype");
+  testFunc(walkerSubDocument, "nextSibling", $("frame1").contentDocument.documentElement,
+           "step to sub documents documentElement");
+
+  walkerSubDocument =
+    SpecialPowers.Cc["@mozilla.org/inspector/deep-tree-walker;1"]
+              .createInstance(SpecialPowers.Ci.inIDeepTreeWalker);
+  walkerSubDocument.showAnonymousContent = false;
+  walkerSubDocument.showSubDocuments = true;
+  walkerSubDocument.showDocumentsAsNodes = true;
+  walkerSubDocument.init($("frame1"), NodeFilter.SHOW_ALL);
+
+  is(SpecialPowers.unwrap(walkerSubDocument.currentNode), $("frame1"), "Unexpected sub-doc root");
+  testFunc(walkerSubDocument, "firstChild", $("frame1").contentDocument,
+           "step to sub document");
+  testFunc(walkerSubDocument, "firstChild", $("frame1").contentDocument.doctype,
+           "step to sub documents doctype");
+  testFunc(walkerSubDocument, "nextSibling", $("frame1").contentDocument.documentElement,
+           "step to sub documents documentElement");
+
+  walkerSubDocument.currentNode = $("frame1").contentDocument;
+  is(SpecialPowers.unwrap(walkerSubDocument.currentNode), $("frame1").contentDocument,
+     "setting currentNode to sub document");
+  testFunc(walkerSubDocument, "nextSibling", null,
+           "nextSibling for sub document is null");
+
+  var walkerFrameChild =
+    SpecialPowers.Cc["@mozilla.org/inspector/deep-tree-walker;1"]
+              .createInstance(SpecialPowers.Ci.inIDeepTreeWalker);
+  walkerFrameChild.showAnonymousContent = false;
+  walkerFrameChild.showSubDocuments = false;
+  walkerFrameChild.init($("frame1"), NodeFilter.SHOW_ALL);
+
+  is(SpecialPowers.unwrap(walkerFrameChild.currentNode), $("frame1"), "Unexpected sub-doc root");
+  testFunc(walkerFrameChild, "firstChild", $("frame1").firstChild,
+           "step to frames child");
+
   var walkerNonAnon =
     SpecialPowers.Cc["@mozilla.org/inspector/deep-tree-walker;1"]
               .createInstance(SpecialPowers.Ci.inIDeepTreeWalker);
   walkerNonAnon.init($("display"), NodeFilter.SHOW_ALL);
   walkerNonAnon.showAnonymousContent = false;
 
   is(SpecialPowers.unwrap(walkerNonAnon.currentNode), $("display"), "Unexpected non-anon root");
   testFunc(walkerNonAnon, "nextNode", $("s").previousSibling,
@@ -101,18 +150,34 @@ addLoadEvent(function() {
   testFunc(walkerNonAnon, "previousNode", $("s"), "step back to span");
   testFunc(walkerNonAnon, "previousNode", $("s").previousSibling,
            "step back to some text");
   testFunc(walkerNonAnon, "previousNode", $("display"),
            "step back to root");
   testFunc(walkerNonAnon, "previousNode", null,
            "step back past root");
 
+  walkerNonAnon.currentNode =  $("s");
+  is(SpecialPowers.unwrap(walkerNonAnon.currentNode), SpecialPowers.unwrap($("s")), 
+     "Setting currentNode to span");
+
   var anonDiv = SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes($("display")))[0];
 
+  try {
+    walkerNonAnon.currentNode = anonDiv;
+    ok(false, "Setting current node to a node that is otherwise unreachable," +
+              " with the current visibility settings should throw");
+  } catch(e) {
+    ok(e.toString().indexOf("NS_ERROR_ILLEGAL_VALUE") > -1, "Setting current node to an anon node should throw" +
+       " NS_ERROR_ILLEGAL_VALUE if showAnonymousContent is set to false");
+    is(SpecialPowers.unwrap(walkerNonAnon.currentNode), SpecialPowers.unwrap($("s")), 
+       "An unsuccessfull set currentNode should leave behind the old state");
+    testFunc(walkerNonAnon, "nextSibling", $("s").nextSibling, "nextSibling after set currentNode");
+  }
+
   var walkerAnon =
     SpecialPowers.Cc["@mozilla.org/inspector/deep-tree-walker;1"]
               .createInstance(SpecialPowers.Ci.inIDeepTreeWalker);
   walkerAnon.showAnonymousContent = true;
   walkerAnon.init($("display"), NodeFilter.SHOW_ALL);
 
   is(SpecialPowers.unwrap(walkerAnon.currentNode), $("display"), "Unexpected anon root");
   testFunc(walkerAnon, "nextNode", anonDiv,
@@ -163,17 +228,17 @@ addLoadEvent(function() {
   testFunc(walkerAnon, "previousNode", $("b"), "step back to bold (anon)");
   testFunc(walkerAnon, "previousNode", $("s").nextSibling, "step back to more text (anon)");
   testFunc(walkerAnon, "previousNode", $("s").previousSibling,
            "step back to some text (anon)");
   testFunc(walkerAnon, "previousNode", anonDiv,
            "step back to anonymous div");
   testFunc(walkerAnon, "previousNode", $("display"), "step back to root (anon)");
   testFunc(walkerAnon, "previousNode", null, "step back past root (anon)");
-  
+
   SimpleTest.finish();
 });
 
 ]]>
 </script>
 </pre>
 </body>
 </html>