From 4fe287e28994417e7b23c1016a27afd71abc2dfc Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 20 Oct 2009 13:27:46 -0400 Subject: [PATCH] Bug 522601. Make inDeepTreeWalker closer to spec behavior and implement various unimplemented methods. r=sicking,sdwilsh --- layout/inspector/src/inDeepTreeWalker.cpp | 199 ++++++++++++++++---- layout/inspector/src/inDeepTreeWalker.h | 3 +- layout/inspector/tests/Makefile.in | 1 + layout/inspector/tests/test_bug522601.xhtml | 181 ++++++++++++++++++ 4 files changed, 343 insertions(+), 41 deletions(-) create mode 100644 layout/inspector/tests/test_bug522601.xhtml diff --git a/layout/inspector/src/inDeepTreeWalker.cpp b/layout/inspector/src/inDeepTreeWalker.cpp index 5fb1d6cc674..b2cbef46e7b 100644 --- a/layout/inspector/src/inDeepTreeWalker.cpp +++ b/layout/inspector/src/inDeepTreeWalker.cpp @@ -1,3 +1,5 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=79: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -47,8 +49,8 @@ /***************************************************************************** * This implementation does not currently operaate according to the W3C spec. - * So far, only parentNode() and nextNode() are implemented, and are not - * interoperable. + * In particular it does NOT handle DOM mutations during the walk. It also + * ignores whatToShow and the filter. *****************************************************************************/ //////////////////////////////////////////////////// @@ -162,78 +164,195 @@ inDeepTreeWalker::ParentNode(nsIDOMNode** _retval) *_retval = nsnull; if (!mCurrentNode) return NS_OK; - if (!mDOMUtils) { - mDOMUtils = do_GetService("@mozilla.org/inspector/dom-utils;1"); - if (!mDOMUtils) { - return NS_ERROR_OUT_OF_MEMORY; - } + if (mStack.Length() == 1) { + // No parent + return NS_OK; } - nsresult rv = mDOMUtils->GetParentForNode(mCurrentNode, mShowAnonymousContent, - _retval); - mCurrentNode = *_retval; - return rv; + // 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); + return NS_OK; } NS_IMETHODIMP inDeepTreeWalker::FirstChild(nsIDOMNode **_retval) { - return NS_ERROR_NOT_IMPLEMENTED; + *_retval = nsnull; + if (!mCurrentNode) { + return NS_OK; + } + + DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1); + nsCOMPtr kid; + top.kids->Item(0, getter_AddRefs(kid)); + if (!kid) { + return NS_OK; + } + top.lastIndex = 1; + PushNode(kid); + kid.forget(_retval); + return NS_OK; } NS_IMETHODIMP inDeepTreeWalker::LastChild(nsIDOMNode **_retval) { - return NS_ERROR_NOT_IMPLEMENTED; + *_retval = nsnull; + if (!mCurrentNode) { + return NS_OK; + } + + DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1); + nsCOMPtr kid; + PRUint32 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; } NS_IMETHODIMP inDeepTreeWalker::PreviousSibling(nsIDOMNode **_retval) { - return NS_ERROR_NOT_IMPLEMENTED; + *_retval = nsnull; + if (!mCurrentNode) { + return NS_OK; + } + + NS_ASSERTION(mStack.Length() > 0, "Should have things in mStack"); + + if (mStack.Length() == 1) { + // No previous sibling + return NS_OK; + } + + DeepTreeStackItem& parent = mStack.ElementAt(mStack.Length()-2); + nsCOMPtr 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); + return NS_OK; } NS_IMETHODIMP inDeepTreeWalker::NextSibling(nsIDOMNode **_retval) { - return NS_ERROR_NOT_IMPLEMENTED; + *_retval = nsnull; + if (!mCurrentNode) { + return NS_OK; + } + + NS_ASSERTION(mStack.Length() > 0, "Should have things in mStack"); + + if (mStack.Length() == 1) { + // No next sibling + return NS_OK; + } + + DeepTreeStackItem& parent = mStack.ElementAt(mStack.Length()-2); + nsCOMPtr 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); + return NS_OK; } NS_IMETHODIMP inDeepTreeWalker::PreviousNode(nsIDOMNode **_retval) { - return NS_ERROR_NOT_IMPLEMENTED; + if (!mCurrentNode || mStack.Length() == 1) { + // Nowhere to go from here + *_retval = nsnull; + return NS_OK; + } + + nsCOMPtr node; + PreviousSibling(getter_AddRefs(node)); + + if (!node) { + return ParentNode(_retval); + } + + // Now we're positioned at our previous sibling. But since the DOM tree + // traversal is depth-first, the previous node is its most deeply nested last + // child. Just loop until LastChild() returns null; since the LastChild() + // call that returns null won't affect our position, we will then be + // positioned at the correct node. + while (node) { + LastChild(getter_AddRefs(node)); + } + + NS_ADDREF(*_retval = mCurrentNode); + return NS_OK; } NS_IMETHODIMP inDeepTreeWalker::NextNode(nsIDOMNode **_retval) { - if (!mCurrentNode) return NS_OK; - - nsCOMPtr next; - - while (1) { - DeepTreeStackItem& top = mStack.ElementAt(mStack.Length()-1); - nsCOMPtr kids = top.kids; - PRUint32 childCount; - kids->GetLength(&childCount); + // First try our kids + FirstChild(_retval); - if (top.lastIndex == childCount) { - mStack.RemoveElementAt(mStack.Length()-1); - if (mStack.Length() == 0) { - mCurrentNode = nsnull; - break; - } - } else { - kids->Item(top.lastIndex++, getter_AddRefs(next)); - PushNode(next); - break; + if (*_retval) { + return NS_OK; + } + + // Now keep trying next siblings up the parent chain, but if we + // discover there's nothing else restore our state. +#ifdef DEBUG + nsIDOMNode* origCurrentNode = mCurrentNode; +#endif + PRUint32 lastChildCallsToMake = 0; + while (1) { + NextSibling(_retval); + + if (*_retval) { + return NS_OK; } - } - - *_retval = next; - NS_IF_ADDREF(*_retval); - + + nsCOMPtr parent; + ParentNode(getter_AddRefs(parent)); + if (!parent) { + // Nowhere else to go; we're done. Restore our state. + while (lastChildCallsToMake--) { + nsCOMPtr dummy; + LastChild(getter_AddRefs(dummy)); + } + NS_ASSERTION(mCurrentNode == origCurrentNode, + "Didn't go back to the right node?"); + *_retval = nsnull; + return NS_OK; + } + ++lastChildCallsToMake; + } + + NS_NOTREACHED("how did we get here?"); return NS_OK; } diff --git a/layout/inspector/src/inDeepTreeWalker.h b/layout/inspector/src/inDeepTreeWalker.h index 2763bbf29f7..8952240bd2d 100644 --- a/layout/inspector/src/inDeepTreeWalker.h +++ b/layout/inspector/src/inDeepTreeWalker.h @@ -52,7 +52,8 @@ struct DeepTreeStackItem { nsCOMPtr node; nsCOMPtr kids; - PRUint32 lastIndex; + PRUint32 lastIndex; // Index one bigger than the index of whatever + // kid we're currently at in |kids|. }; //////////////////////////////////////////////////// diff --git a/layout/inspector/tests/Makefile.in b/layout/inspector/tests/Makefile.in index 32030b313c8..4ca7b798662 100644 --- a/layout/inspector/tests/Makefile.in +++ b/layout/inspector/tests/Makefile.in @@ -47,6 +47,7 @@ include $(topsrcdir)/config/rules.mk _TEST_FILES =\ test_bug462787.html \ test_bug462789.html \ + test_bug522601.xhtml \ $(NULL) libs:: $(_TEST_FILES) diff --git a/layout/inspector/tests/test_bug522601.xhtml b/layout/inspector/tests/test_bug522601.xhtml new file mode 100644 index 00000000000..13e6464244f --- /dev/null +++ b/layout/inspector/tests/test_bug522601.xhtml @@ -0,0 +1,181 @@ + + + + Test for Bug 522601 + + + + + + +
+
+
+ + +Mozilla Bug 522601 +

+ This is some text + More text + Even more ItalictextAnd more italic

+ +
+
+
+ +