diff -r 000000000000 -r 6474c204b198 layout/inspector/inDeepTreeWalker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layout/inspector/inDeepTreeWalker.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,406 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=79: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 "inDeepTreeWalker.h" +#include "inLayoutUtils.h" + +#include "nsString.h" +#include "nsIDOMDocument.h" +#include "nsIDOMNodeFilter.h" +#include "nsIDOMNodeList.h" +#include "nsServiceManagerUtils.h" +#include "inIDOMUtils.h" +#include "nsIContent.h" +#include "nsContentList.h" +#include "ChildIterator.h" +#include "mozilla/dom/Element.h" + +/***************************************************************************** + * 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() + : mShowAnonymousContent(false), + mShowSubDocuments(false), + mWhatToShow(nsIDOMNodeFilter::SHOW_ALL) +{ +} + +inDeepTreeWalker::~inDeepTreeWalker() +{ +} + +NS_IMPL_ISUPPORTS(inDeepTreeWalker, + inIDeepTreeWalker, + nsIDOMTreeWalker) + +//////////////////////////////////////////////////// +// inIDeepTreeWalker + +NS_IMETHODIMP +inDeepTreeWalker::GetShowAnonymousContent(bool *aShowAnonymousContent) +{ + *aShowAnonymousContent = mShowAnonymousContent; + return NS_OK; +} + +NS_IMETHODIMP +inDeepTreeWalker::SetShowAnonymousContent(bool aShowAnonymousContent) +{ + mShowAnonymousContent = aShowAnonymousContent; + return NS_OK; +} + +NS_IMETHODIMP +inDeepTreeWalker::GetShowSubDocuments(bool *aShowSubDocuments) +{ + *aShowSubDocuments = mShowSubDocuments; + return NS_OK; +} + +NS_IMETHODIMP +inDeepTreeWalker::SetShowSubDocuments(bool aShowSubDocuments) +{ + mShowSubDocuments = aShowSubDocuments; + return NS_OK; +} + +NS_IMETHODIMP +inDeepTreeWalker::Init(nsIDOMNode* aRoot, uint32_t aWhatToShow) +{ + mRoot = aRoot; + mWhatToShow = aWhatToShow; + + PushNode(aRoot); + + return NS_OK; +} + +//////////////////////////////////////////////////// +// 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; +} + +NS_IMETHODIMP +inDeepTreeWalker::GetFilter(nsIDOMNodeFilter** aFilter) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +inDeepTreeWalker::GetCurrentNode(nsIDOMNode** aCurrentNode) +{ + *aCurrentNode = mCurrentNode; + NS_IF_ADDREF(*aCurrentNode); + + return NS_OK; +} + +NS_IMETHODIMP +inDeepTreeWalker::SetCurrentNode(nsIDOMNode* aCurrentNode) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +inDeepTreeWalker::ParentNode(nsIDOMNode** _retval) +{ + *_retval = nullptr; + if (!mCurrentNode) return NS_OK; + + if (mStack.Length() == 1) { + // No 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); + return NS_OK; +} + +NS_IMETHODIMP +inDeepTreeWalker::FirstChild(nsIDOMNode **_retval) +{ + *_retval = nullptr; + 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) +{ + *_retval = nullptr; + if (!mCurrentNode) { + return NS_OK; + } + + DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1); + nsCOMPtr 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; +} + +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 + 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) +{ + *_retval = nullptr; + 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) +{ + if (!mCurrentNode || mStack.Length() == 1) { + // Nowhere to go from here + *_retval = nullptr; + 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) +{ + // First try our kids + FirstChild(_retval); + + 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 + uint32_t lastChildCallsToMake = 0; + while (1) { + NextSibling(_retval); + + if (*_retval) { + return NS_OK; + } + + 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 = nullptr; + 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 kids; + if (mShowSubDocuments) { + nsCOMPtr domdoc = inLayoutUtils::GetSubDocumentFor(aNode); + if (domdoc) { + domdoc->GetChildNodes(getter_AddRefs(kids)); + } + } + + if (!kids) { + nsCOMPtr 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 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 subject = mCurrentNode; + nsCOMPtr 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 doc = do_QueryInterface(aDoc); + nsIURI *uri = doc->GetDocumentURI(); + char* s; + uri->GetSpec(&s); + return s; +} +*/