michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=79: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "inDeepTreeWalker.h" michael@0: #include "inLayoutUtils.h" michael@0: michael@0: #include "nsString.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMNodeFilter.h" michael@0: #include "nsIDOMNodeList.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "inIDOMUtils.h" michael@0: #include "nsIContent.h" michael@0: #include "nsContentList.h" michael@0: #include "ChildIterator.h" michael@0: #include "mozilla/dom/Element.h" michael@0: michael@0: /***************************************************************************** michael@0: * This implementation does not currently operaate according to the W3C spec. michael@0: * In particular it does NOT handle DOM mutations during the walk. It also michael@0: * ignores whatToShow and the filter. michael@0: *****************************************************************************/ michael@0: michael@0: //////////////////////////////////////////////////// michael@0: michael@0: inDeepTreeWalker::inDeepTreeWalker() michael@0: : mShowAnonymousContent(false), michael@0: mShowSubDocuments(false), michael@0: mWhatToShow(nsIDOMNodeFilter::SHOW_ALL) michael@0: { michael@0: } michael@0: michael@0: inDeepTreeWalker::~inDeepTreeWalker() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(inDeepTreeWalker, michael@0: inIDeepTreeWalker, michael@0: nsIDOMTreeWalker) michael@0: michael@0: //////////////////////////////////////////////////// michael@0: // inIDeepTreeWalker michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::GetShowAnonymousContent(bool *aShowAnonymousContent) michael@0: { michael@0: *aShowAnonymousContent = mShowAnonymousContent; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::SetShowAnonymousContent(bool aShowAnonymousContent) michael@0: { michael@0: mShowAnonymousContent = aShowAnonymousContent; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::GetShowSubDocuments(bool *aShowSubDocuments) michael@0: { michael@0: *aShowSubDocuments = mShowSubDocuments; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::SetShowSubDocuments(bool aShowSubDocuments) michael@0: { michael@0: mShowSubDocuments = aShowSubDocuments; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::Init(nsIDOMNode* aRoot, uint32_t aWhatToShow) michael@0: { michael@0: mRoot = aRoot; michael@0: mWhatToShow = aWhatToShow; michael@0: michael@0: PushNode(aRoot); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////// michael@0: // nsIDOMTreeWalker michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::GetRoot(nsIDOMNode** aRoot) michael@0: { michael@0: *aRoot = mRoot; michael@0: NS_IF_ADDREF(*aRoot); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::GetWhatToShow(uint32_t* aWhatToShow) michael@0: { michael@0: *aWhatToShow = mWhatToShow; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::GetFilter(nsIDOMNodeFilter** aFilter) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::GetCurrentNode(nsIDOMNode** aCurrentNode) michael@0: { michael@0: *aCurrentNode = mCurrentNode; michael@0: NS_IF_ADDREF(*aCurrentNode); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::SetCurrentNode(nsIDOMNode* aCurrentNode) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::ParentNode(nsIDOMNode** _retval) michael@0: { michael@0: *_retval = nullptr; michael@0: if (!mCurrentNode) return NS_OK; michael@0: michael@0: if (mStack.Length() == 1) { michael@0: // No parent michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Pop off the current node, and push the new one michael@0: mStack.RemoveElementAt(mStack.Length()-1); michael@0: DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1); michael@0: mCurrentNode = top.node; michael@0: top.lastIndex = 0; michael@0: NS_ADDREF(*_retval = mCurrentNode); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::FirstChild(nsIDOMNode **_retval) michael@0: { michael@0: *_retval = nullptr; michael@0: if (!mCurrentNode) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1); michael@0: nsCOMPtr kid; michael@0: top.kids->Item(0, getter_AddRefs(kid)); michael@0: if (!kid) { michael@0: return NS_OK; michael@0: } michael@0: top.lastIndex = 1; michael@0: PushNode(kid); michael@0: kid.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::LastChild(nsIDOMNode **_retval) michael@0: { michael@0: *_retval = nullptr; michael@0: if (!mCurrentNode) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: DeepTreeStackItem& top = mStack.ElementAt(mStack.Length() - 1); michael@0: nsCOMPtr kid; michael@0: uint32_t length; michael@0: top.kids->GetLength(&length); michael@0: top.kids->Item(length - 1, getter_AddRefs(kid)); michael@0: if (!kid) { michael@0: return NS_OK; michael@0: } michael@0: top.lastIndex = length; michael@0: PushNode(kid); michael@0: kid.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::PreviousSibling(nsIDOMNode **_retval) michael@0: { michael@0: *_retval = nullptr; michael@0: if (!mCurrentNode) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ASSERTION(mStack.Length() > 0, "Should have things in mStack"); michael@0: michael@0: if (mStack.Length() == 1) { michael@0: // No previous sibling michael@0: return NS_OK; michael@0: } michael@0: michael@0: DeepTreeStackItem& parent = mStack.ElementAt(mStack.Length()-2); michael@0: nsCOMPtr previousSibling; michael@0: parent.kids->Item(parent.lastIndex-2, getter_AddRefs(previousSibling)); michael@0: if (!previousSibling) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Our mStack's topmost element is our current node. Since we're trying to michael@0: // change that to the previous sibling, pop off the current node, and push michael@0: // the new one. michael@0: mStack.RemoveElementAt(mStack.Length() - 1); michael@0: parent.lastIndex--; michael@0: PushNode(previousSibling); michael@0: previousSibling.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::NextSibling(nsIDOMNode **_retval) michael@0: { michael@0: *_retval = nullptr; michael@0: if (!mCurrentNode) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ASSERTION(mStack.Length() > 0, "Should have things in mStack"); michael@0: michael@0: if (mStack.Length() == 1) { michael@0: // No next sibling michael@0: return NS_OK; michael@0: } michael@0: michael@0: DeepTreeStackItem& parent = mStack.ElementAt(mStack.Length()-2); michael@0: nsCOMPtr nextSibling; michael@0: parent.kids->Item(parent.lastIndex, getter_AddRefs(nextSibling)); michael@0: if (!nextSibling) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Our mStack's topmost element is our current node. Since we're trying to michael@0: // change that to the next sibling, pop off the current node, and push michael@0: // the new one. michael@0: mStack.RemoveElementAt(mStack.Length() - 1); michael@0: parent.lastIndex++; michael@0: PushNode(nextSibling); michael@0: nextSibling.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::PreviousNode(nsIDOMNode **_retval) michael@0: { michael@0: if (!mCurrentNode || mStack.Length() == 1) { michael@0: // Nowhere to go from here michael@0: *_retval = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr node; michael@0: PreviousSibling(getter_AddRefs(node)); michael@0: michael@0: if (!node) { michael@0: return ParentNode(_retval); michael@0: } michael@0: michael@0: // Now we're positioned at our previous sibling. But since the DOM tree michael@0: // traversal is depth-first, the previous node is its most deeply nested last michael@0: // child. Just loop until LastChild() returns null; since the LastChild() michael@0: // call that returns null won't affect our position, we will then be michael@0: // positioned at the correct node. michael@0: while (node) { michael@0: LastChild(getter_AddRefs(node)); michael@0: } michael@0: michael@0: NS_ADDREF(*_retval = mCurrentNode); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::NextNode(nsIDOMNode **_retval) michael@0: { michael@0: // First try our kids michael@0: FirstChild(_retval); michael@0: michael@0: if (*_retval) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Now keep trying next siblings up the parent chain, but if we michael@0: // discover there's nothing else restore our state. michael@0: #ifdef DEBUG michael@0: nsIDOMNode* origCurrentNode = mCurrentNode; michael@0: #endif michael@0: uint32_t lastChildCallsToMake = 0; michael@0: while (1) { michael@0: NextSibling(_retval); michael@0: michael@0: if (*_retval) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr parent; michael@0: ParentNode(getter_AddRefs(parent)); michael@0: if (!parent) { michael@0: // Nowhere else to go; we're done. Restore our state. michael@0: while (lastChildCallsToMake--) { michael@0: nsCOMPtr dummy; michael@0: LastChild(getter_AddRefs(dummy)); michael@0: } michael@0: NS_ASSERTION(mCurrentNode == origCurrentNode, michael@0: "Didn't go back to the right node?"); michael@0: *_retval = nullptr; michael@0: return NS_OK; michael@0: } michael@0: ++lastChildCallsToMake; michael@0: } michael@0: michael@0: NS_NOTREACHED("how did we get here?"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: inDeepTreeWalker::PushNode(nsIDOMNode* aNode) michael@0: { michael@0: mCurrentNode = aNode; michael@0: if (!aNode) return; michael@0: michael@0: DeepTreeStackItem item; michael@0: item.node = aNode; michael@0: michael@0: nsCOMPtr kids; michael@0: if (mShowSubDocuments) { michael@0: nsCOMPtr domdoc = inLayoutUtils::GetSubDocumentFor(aNode); michael@0: if (domdoc) { michael@0: domdoc->GetChildNodes(getter_AddRefs(kids)); michael@0: } michael@0: } michael@0: michael@0: if (!kids) { michael@0: nsCOMPtr content = do_QueryInterface(aNode); michael@0: if (content && mShowAnonymousContent) { michael@0: kids = content->GetChildren(nsIContent::eAllChildren); michael@0: } michael@0: } michael@0: if (!kids) { michael@0: aNode->GetChildNodes(getter_AddRefs(kids)); michael@0: } michael@0: michael@0: item.kids = kids; michael@0: item.lastIndex = 0; michael@0: mStack.AppendElement(item); michael@0: } michael@0: michael@0: /* michael@0: // This NextNode implementation does not require the use of stacks, michael@0: // as does the one above. However, it does not handle anonymous michael@0: // content and sub-documents. michael@0: NS_IMETHODIMP michael@0: inDeepTreeWalker::NextNode(nsIDOMNode **_retval) michael@0: { michael@0: if (!mCurrentNode) return NS_OK; michael@0: michael@0: // walk down the tree first michael@0: nsCOMPtr next; michael@0: mCurrentNode->GetFirstChild(getter_AddRefs(next)); michael@0: if (!next) { michael@0: mCurrentNode->GetNextSibling(getter_AddRefs(next)); michael@0: if (!next) { michael@0: // we've hit the end, so walk back up the tree until another michael@0: // downward opening is found, or the top of the tree michael@0: nsCOMPtr subject = mCurrentNode; michael@0: nsCOMPtr parent; michael@0: while (1) { michael@0: subject->GetParentNode(getter_AddRefs(parent)); michael@0: if (!parent) // hit the top of the tree michael@0: break; michael@0: parent->GetNextSibling(getter_AddRefs(subject)); michael@0: if (subject) { // found a downward opening michael@0: next = subject; michael@0: break; michael@0: } else // walk up another level michael@0: subject = parent; michael@0: } michael@0: } michael@0: } michael@0: michael@0: mCurrentNode = next; michael@0: michael@0: *_retval = next; michael@0: NS_IF_ADDREF(*_retval); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: char* getURL(nsIDOMDocument* aDoc) michael@0: { michael@0: nsCOMPtr doc = do_QueryInterface(aDoc); michael@0: nsIURI *uri = doc->GetDocumentURI(); michael@0: char* s; michael@0: uri->GetSpec(&s); michael@0: return s; michael@0: } michael@0: */