зеркало из https://github.com/mozilla/gecko-dev.git
407 строки
9.4 KiB
C++
407 строки
9.4 KiB
C++
|
/* -*- 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_ISUPPORTS2(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<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;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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<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);
|
||
|
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<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);
|
||
|
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<nsIDOMNode> 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<nsIDOMNode> parent;
|
||
|
ParentNode(getter_AddRefs(parent));
|
||
|
if (!parent) {
|
||
|
// Nowhere else to go; we're done. Restore our state.
|
||
|
while (lastChildCallsToMake--) {
|
||
|
nsCOMPtr<nsIDOMNode> 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<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;
|
||
|
}
|
||
|
*/
|