RetroZilla/extensions/transformiix/source/xpath/txMozillaXPathTreeWalker.cpp

978 lines
27 KiB
C++
Raw Normal View History

2015-10-21 05:03:22 +02:00
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is TransforMiiX XSLT processor code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2003
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Peter Van der Beken <peterv@propagandism.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "txXPathTreeWalker.h"
#include "nsIAtom.h"
#include "nsIAttribute.h"
#include "nsIDOM3Node.h"
#include "nsIDOMAttr.h"
#include "nsIDOMDocument.h"
#include "nsIDOMNode.h"
#include "nsIDOMElement.h"
#include "nsIDOMProcessingInstruction.h"
#include "nsINodeInfo.h"
#include "nsITextContent.h"
#include "nsPrintfCString.h"
#include "nsReadableUtils.h"
#include "nsString.h"
#include "nsTextFragment.h"
#include "XMLUtils.h"
#include "TxLog.h"
#include "nsUnicharUtils.h"
const PRUint32 kUnknownIndex = PRUint32(-1);
txXPathTreeWalker::txXPathTreeWalker(const txXPathTreeWalker& aOther)
: mPosition(aOther.mPosition),
mCurrentIndex(aOther.mCurrentIndex)
{
}
txXPathTreeWalker::txXPathTreeWalker(const txXPathNode& aNode)
: mPosition(aNode),
mCurrentIndex(kUnknownIndex)
{
}
txXPathTreeWalker::~txXPathTreeWalker()
{
}
PRBool
txXPathTreeWalker::moveToElementById(const nsAString& aID)
{
nsCOMPtr<nsIDOMDocument> document;
if (mPosition.isDocument()) {
document = do_QueryInterface(mPosition.mDocument);
}
else {
document = do_QueryInterface(mPosition.mContent->GetDocument());
}
if (!document) {
return PR_FALSE;
}
nsCOMPtr<nsIDOMElement> element;
document->GetElementById(aID, getter_AddRefs(element));
if (!element) {
return PR_FALSE;
}
nsCOMPtr<nsIContent> content = do_QueryInterface(element);
NS_ENSURE_TRUE(content, PR_FALSE);
mPosition.mIndex = txXPathNode::eContent;
mPosition.mContent = content;
mCurrentIndex = kUnknownIndex;
mDescendants.Clear();
return PR_TRUE;
}
PRBool
txXPathTreeWalker::moveToFirstAttribute()
{
if (!mPosition.isContent()) {
return PR_FALSE;
}
return moveToValidAttribute(0);
}
PRBool
txXPathTreeWalker::moveToNextAttribute()
{
// XXX an assertion should be enough here with the current code
if (!mPosition.isAttribute()) {
return PR_FALSE;
}
return moveToValidAttribute(mPosition.mIndex + 1);
}
PRBool
txXPathTreeWalker::moveToValidAttribute(PRUint32 aStartIndex)
{
NS_ASSERTION(!mPosition.isDocument(), "documents doesn't have attrs");
PRUint32 total = mPosition.mContent->GetAttrCount();
if (aStartIndex >= total) {
return PR_FALSE;
}
PRInt32 namespaceID;
nsCOMPtr<nsIAtom> name, prefix;
PRUint32 index;
for (index = aStartIndex; index < total; ++index) {
mPosition.mContent->GetAttrNameAt(index, &namespaceID,
getter_AddRefs(name),
getter_AddRefs(prefix));
// We need to ignore XMLNS attributes.
if (namespaceID != kNameSpaceID_XMLNS) {
mPosition.mIndex = index;
return PR_TRUE;
}
}
return PR_FALSE;
}
PRBool
txXPathTreeWalker::moveToFirstChild()
{
if (mPosition.isAttribute()) {
return PR_FALSE;
}
if (mPosition.isDocument()) {
nsIContent* child = mPosition.mDocument->GetChildAt(0);
if (!child) {
return PR_FALSE;
}
mPosition.mIndex = txXPathNode::eContent;
mPosition.mContent = child;
NS_ASSERTION(mCurrentIndex == kUnknownIndex && !mDescendants.Count(),
"we shouldn't have any position info at the document");
mCurrentIndex = 0;
return PR_TRUE;
}
nsIContent* child = mPosition.mContent->GetChildAt(0);
if (!child) {
return PR_FALSE;
}
mPosition.mContent = child;
NS_ASSERTION(mCurrentIndex != kUnknownIndex || !mDescendants.Count(),
"Index should be known if parents index are");
if (mCurrentIndex != kUnknownIndex &&
!mDescendants.AppendValue(mCurrentIndex)) {
mDescendants.Clear();
}
mCurrentIndex = 0;
return PR_TRUE;
}
PRBool
txXPathTreeWalker::moveToLastChild()
{
if (mPosition.isAttribute()) {
return PR_FALSE;
}
if (mPosition.isDocument()) {
PRUint32 total = mPosition.mDocument->GetChildCount();
if (!total) {
return PR_FALSE;
}
mPosition.mIndex = txXPathNode::eContent;
mPosition.mContent = mPosition.mDocument->GetChildAt(total - 1);
NS_ASSERTION(mCurrentIndex == kUnknownIndex && !mDescendants.Count(),
"we shouldn't have any position info at the document");
mCurrentIndex = total - 1;
return PR_TRUE;
}
PRUint32 total = mPosition.mContent->GetChildCount();
if (!total) {
return PR_FALSE;
}
mPosition.mContent = mPosition.mContent->GetChildAt(total - 1);
NS_ASSERTION(mCurrentIndex != kUnknownIndex || !mDescendants.Count(),
"Index should be known if parents index are");
if (mCurrentIndex != kUnknownIndex &&
!mDescendants.AppendValue(mCurrentIndex)) {
mDescendants.Clear();
}
mCurrentIndex = total - 1;
return PR_TRUE;
}
PRBool
txXPathTreeWalker::moveToNextSibling()
{
if (!mPosition.isContent()) {
return PR_FALSE;
}
return moveToSibling(1);
}
PRBool
txXPathTreeWalker::moveToPreviousSibling()
{
if (!mPosition.isContent()) {
return PR_FALSE;
}
return moveToSibling(-1);
}
PRBool
txXPathTreeWalker::moveToParent()
{
if (mPosition.isDocument()) {
return PR_FALSE;
}
if (mPosition.isAttribute()) {
mPosition.mIndex = txXPathNode::eContent;
return PR_TRUE;
}
nsIContent *parent = mPosition.mContent->GetParent();
if (parent) {
mPosition.mContent = parent;
PRInt32 count = mDescendants.Count();
if (count) {
mCurrentIndex = mDescendants.ValueAt(--count);
mDescendants.RemoveValueAt(count);
}
else {
mCurrentIndex = kUnknownIndex;
}
return PR_TRUE;
}
nsIDocument* document = mPosition.mContent->GetDocument();
if (!document) {
return PR_FALSE;
}
mPosition.mIndex = txXPathNode::eDocument;
mPosition.mDocument = document;
return PR_TRUE;
}
PRBool
txXPathTreeWalker::moveToSibling(PRInt32 aDir)
{
NS_ASSERTION(mPosition.isContent(),
"moveToSibling should only be called for content");
nsIDocument* document;
nsIContent* parent = mPosition.mContent->GetParent();
if (!parent) {
document = mPosition.mContent->GetDocument();
if (!document) {
return PR_FALSE;
}
}
if (mCurrentIndex == kUnknownIndex) {
mCurrentIndex = parent ? parent->IndexOf(mPosition.mContent)
: document->IndexOf(mPosition.mContent);
}
// if mCurrentIndex is 0 we rely on GetChildAt returning null for an
// index of PRUint32(-1).
PRUint32 newIndex = mCurrentIndex + aDir;
nsIContent* newChild = parent ? parent->GetChildAt(newIndex) :
document->GetChildAt(newIndex);
if (!newChild) {
return PR_FALSE;
}
mPosition.mContent = newChild;
mCurrentIndex = newIndex;
return PR_TRUE;
}
txXPathNode::txXPathNode(const txXPathNode& aNode) : mIndex(aNode.mIndex)
{
// Hopefully it's ok to access mContent through mDocument.
mDocument = aNode.mDocument;
}
PRBool
txXPathNode::operator==(const txXPathNode& aNode) const
{
if (mIndex != aNode.mIndex) {
return PR_FALSE;
}
// Hopefully it's ok to access mContent through mDocument.
return (mDocument == aNode.mDocument);
}
/* static */
PRBool
txXPathNodeUtils::getAttr(const txXPathNode& aNode, nsIAtom* aLocalName,
PRInt32 aNSID, nsAString& aValue)
{
if (aNode.isDocument() || aNode.isAttribute()) {
return PR_FALSE;
}
nsresult rv = aNode.mContent->GetAttr(aNSID, aLocalName, aValue);
return NS_SUCCEEDED(rv) && rv != NS_CONTENT_ATTR_NOT_THERE;
}
/* static */
already_AddRefed<nsIAtom>
txXPathNodeUtils::getLocalName(const txXPathNode& aNode)
{
if (aNode.isDocument()) {
return nsnull;
}
if (aNode.isContent()) {
if (aNode.mContent->IsContentOfType(nsIContent::eELEMENT)) {
nsIAtom* localName = aNode.mContent->Tag();
NS_ADDREF(localName);
return localName;
}
if (aNode.mContent->IsContentOfType(nsIContent::ePROCESSING_INSTRUCTION)) {
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mContent);
nsAutoString target;
node->GetNodeName(target);
return NS_NewAtom(target);
}
return nsnull;
}
nsIAtom* localName;
nsCOMPtr<nsIAtom> prefix;
PRInt32 namespaceID;
aNode.mContent->GetAttrNameAt(aNode.mIndex, &namespaceID, &localName,
getter_AddRefs(prefix));
return localName;
}
/* static */
void
txXPathNodeUtils::getLocalName(const txXPathNode& aNode, nsAString& aLocalName)
{
if (aNode.isDocument()) {
aLocalName.Truncate();
return;
}
if (aNode.isContent()) {
nsINodeInfo* nodeInfo = aNode.mContent->GetNodeInfo();
if (nodeInfo) {
nodeInfo->GetLocalName(aLocalName);
// Check for html
if (nodeInfo->NamespaceEquals(kNameSpaceID_None) &&
aNode.mContent->IsContentOfType(nsIContent::eHTML)) {
ToUpperCase(aLocalName);
}
return;
}
if (aNode.mContent->IsContentOfType(nsIContent::ePROCESSING_INSTRUCTION)) {
// PIs don't have a nodeinfo but do have a name
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mContent);
node->GetNodeName(aLocalName);
return;
}
aLocalName.Truncate();
return;
}
nsCOMPtr<nsIAtom> prefix, localName;
PRInt32 namespaceID;
aNode.mContent->GetAttrNameAt(aNode.mIndex, &namespaceID,
getter_AddRefs(localName),
getter_AddRefs(prefix));
localName->ToString(aLocalName);
// Check for html
if (aNode.mContent->GetNodeInfo()->NamespaceEquals(kNameSpaceID_None) &&
aNode.mContent->IsContentOfType(nsIContent::eHTML)) {
ToUpperCase(aLocalName);
}
}
/* static */
void
txXPathNodeUtils::getNodeName(const txXPathNode& aNode, nsAString& aName)
{
if (aNode.isDocument()) {
aName.Truncate();
return;
}
if (aNode.isContent()) {
nsINodeInfo* nodeInfo = aNode.mContent->GetNodeInfo();
if (nodeInfo) {
nodeInfo->GetQualifiedName(aName);
// Check for html
if (nodeInfo->NamespaceEquals(kNameSpaceID_None) &&
aNode.mContent->IsContentOfType(nsIContent::eHTML)) {
ToUpperCase(aName);
}
return;
}
if (aNode.mContent->IsContentOfType(nsIContent::ePROCESSING_INSTRUCTION)) {
// PIs don't have a nodeinfo but do have a name
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mContent);
node->GetNodeName(aName);
return;
}
aName.Truncate();
return;
}
nsCOMPtr<nsIAtom> name, prefix;
PRInt32 namespaceID;
aNode.mContent->GetAttrNameAt(aNode.mIndex, &namespaceID,
getter_AddRefs(name),
getter_AddRefs(prefix));
if (prefix) {
prefix->ToString(aName);
aName.Append(PRUnichar(':'));
}
else {
aName.Truncate();
}
const char* attrName;
name->GetUTF8String(&attrName);
AppendUTF8toUTF16(attrName, aName);
// Check for html
if (aNode.mContent->GetNodeInfo()->NamespaceEquals(kNameSpaceID_None) &&
aNode.mContent->IsContentOfType(nsIContent::eHTML)) {
ToUpperCase(aName);
}
}
/* static */
PRInt32
txXPathNodeUtils::getNamespaceID(const txXPathNode& aNode)
{
if (aNode.isDocument()) {
return kNameSpaceID_None;
}
if (aNode.isContent()) {
nsINodeInfo* ni = aNode.mContent->GetNodeInfo();
return ni ? ni->NamespaceID() : kNameSpaceID_None;
}
nsCOMPtr<nsIAtom> name, prefix;
PRInt32 namespaceID;
aNode.mContent->GetAttrNameAt(aNode.mIndex, &namespaceID,
getter_AddRefs(name),
getter_AddRefs(prefix));
return namespaceID;
}
/* static */
void
txXPathNodeUtils::getNamespaceURI(const txXPathNode& aNode, nsAString& aURI)
{
gTxNameSpaceManager->GetNameSpaceURI(getNamespaceID(aNode), aURI);
}
/* static */
PRUint16
txXPathNodeUtils::getNodeType(const txXPathNode& aNode)
{
if (aNode.isDocument()) {
return txXPathNodeType::DOCUMENT_NODE;
}
if (aNode.isContent()) {
PRUint16 type;
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mContent);
node->GetNodeType(&type);
return type;
}
return txXPathNodeType::ATTRIBUTE_NODE;
}
static void appendTextContent(nsIContent* aElement, nsAString& aResult)
{
nsIContent* content = aElement->GetChildAt(0);
PRUint32 i = 0;
while (content) {
if (content->IsContentOfType(nsIContent::eELEMENT)) {
appendTextContent(content, aResult);
}
else if (content->IsContentOfType(nsIContent::eTEXT)) {
nsCOMPtr<nsITextContent> textContent = do_QueryInterface(content);
textContent->AppendTextTo(aResult);
}
content = aElement->GetChildAt(++i);
}
}
/* static */
void
txXPathNodeUtils::appendNodeValue(const txXPathNode& aNode, nsAString& aResult)
{
if (aNode.isAttribute()) {
nsCOMPtr<nsIAtom> name, prefix;
PRInt32 namespaceID;
aNode.mContent->GetAttrNameAt(aNode.mIndex, &namespaceID,
getter_AddRefs(name),
getter_AddRefs(prefix));
nsAutoString result;
aNode.mContent->GetAttr(namespaceID, name, result);
aResult.Append(result);
return;
}
if (aNode.isDocument()) {
nsIContent* content = aNode.mDocument->GetRootContent();
if (content) {
appendTextContent(content, aResult);
}
return;
}
if (aNode.mContent->IsContentOfType(nsIContent::eELEMENT)) {
appendTextContent(aNode.mContent, aResult);
return;
}
if (aNode.mContent->IsContentOfType(nsIContent::ePROCESSING_INSTRUCTION)) {
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mContent);
nsAutoString result;
node->GetNodeValue(result);
aResult.Append(result);
return;
}
nsCOMPtr<nsITextContent> textContent = do_QueryInterface(aNode.mContent);
if (!textContent) {
NS_ERROR("Unexpected nodetype.");
return;
}
textContent->AppendTextTo(aResult);
}
/* static */
PRBool
txXPathNodeUtils::isWhitespace(const txXPathNode& aNode)
{
NS_ASSERTION(aNode.isContent(), "Wrong type!");
nsCOMPtr<nsITextContent> textCont = do_QueryInterface(aNode.mContent);
if (!textCont) {
return PR_TRUE;
}
return textCont->IsOnlyWhitespace();
}
/* static */
txXPathNode*
txXPathNodeUtils::getDocument(const txXPathNode& aNode)
{
if (aNode.isDocument()) {
return new txXPathNode(aNode);
}
nsIDocument* document = aNode.mContent->GetDocument();
return document ? new txXPathNode(document) : nsnull;
}
/* static */
txXPathNode*
txXPathNodeUtils::getOwnerDocument(const txXPathNode& aNode)
{
if (aNode.isDocument()) {
return new txXPathNode(aNode);
}
nsIDocument* document = aNode.mContent->GetDocument();
if (!document) {
nsINodeInfo* ni = aNode.mContent->GetNodeInfo();
if (ni) {
document = ni->GetDocument();
}
}
return document ? new txXPathNode(document) : nsnull;
}
#ifndef HAVE_64BIT_OS
#define kFmtSize 13
#define kFmtSizeAttr 24
const char gPrintfFmt[] = "id0x%08p";
const char gPrintfFmtAttr[] = "id0x%08p-%010i";
#else
#define kFmtSize 21
#define kFmtSizeAttr 32
const char gPrintfFmt[] = "id0x%016p";
const char gPrintfFmtAttr[] = "id0x%016p-%010i";
#endif
/* static */
nsresult
txXPathNodeUtils::getXSLTId(const txXPathNode& aNode,
nsAString& aResult)
{
if (aNode.isDocument()) {
CopyASCIItoUTF16(nsPrintfCString(kFmtSize, gPrintfFmt,
aNode.mDocument), aResult);
return NS_OK;
}
if (aNode.isContent()) {
CopyASCIItoUTF16(nsPrintfCString(kFmtSize, gPrintfFmt, aNode.mContent),
aResult);
return NS_OK;
}
CopyASCIItoUTF16(nsPrintfCString(kFmtSizeAttr, gPrintfFmtAttr, aNode.mContent,
aNode.mIndex), aResult);
return NS_OK;
}
/* static */
void
txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsAString& aURI)
{
nsCOMPtr<nsIDOM3Node> node;
if (aNode.isDocument()) {
node = do_QueryInterface(aNode.mDocument);
}
else {
node = do_QueryInterface(aNode.mContent);
}
if (node) {
node->GetBaseURI(aURI);
}
else {
aURI.Truncate();
}
}
/* static */
PRIntn
txXPathNodeUtils::comparePosition(const txXPathNode& aNode,
const txXPathNode& aOtherNode)
{
// First check for equal nodes or attribute-nodes on the same element.
if (aNode.mContent == aOtherNode.mContent) {
if (aNode.mIndex == aOtherNode.mIndex) {
return 0;
}
if (aNode.isContent() || (!aOtherNode.isContent() &&
aNode.mIndex < aOtherNode.mIndex)) {
return -1;
}
return 1;
}
// Get document for both nodes.
nsIDocument* document = aNode.isDocument() ?
aNode.mDocument :
aNode.mContent->GetDocument();
nsIDocument* otherDocument = aOtherNode.isDocument() ?
aOtherNode.mDocument :
aOtherNode.mContent->GetDocument();
// If the nodes have different ownerdocuments, compare the document
// pointers.
if (document && otherDocument && document != otherDocument) {
return document > otherDocument ? 1 : -1;
}
// Every node comes after its ownerdocument.
if (aNode.isDocument()) {
return -1;
}
if (aOtherNode.isDocument()) {
return 1;
}
// Get parents up the tree.
nsAutoVoidArray parents, otherParents;
nsIContent* content = aNode.mContent;
nsIContent* otherContent = aOtherNode.mContent;
nsIContent* parent, *otherParent;
PRInt32 index, otherIndex;
while (content && otherContent) {
parent = content->GetParent();
otherParent = otherContent->GetParent();
// Hopefully this is a common case.
if (parent == otherParent) {
if (parent) {
index = (PRUint32)parent->IndexOf(content);
otherIndex = (PRUint32)parent->IndexOf(otherContent);
}
else {
if (!document) {
if (!otherDocument) {
// Neither aNode nor aOtherNode are not in a document,
// compare their top ancestors.
return content > otherContent ? 1 : -1;
}
// aNode is not in the tree, compare its top ancestor with
// aOtherNode's document.
return (void*)content > (void*)otherDocument ? 1 : -1;
}
else if (!otherDocument) {
// aOtherNode is not in a document, compare its top
// ancestor with aNode's document.
return (void*)document > (void*)otherContent ? 1 : -1;
}
// Both nodes are in the same document.
index = (PRUint32)document->IndexOf(content);
otherIndex = (PRUint32)document->IndexOf(otherContent);
}
return index < otherIndex ? -1 : 1;
}
parents.AppendElement(content);
otherParents.AppendElement(otherContent);
content = parent;
otherContent = otherParent;
}
while (content) {
parents.AppendElement(content);
content = content->GetParent();
}
while (otherContent) {
otherParents.AppendElement(otherContent);
otherContent = otherContent->GetParent();
}
if (!document) {
if (!otherDocument) {
// Neither aNode nor aOtherNode are not in a document, compare
// their top ancestors.
return parents.ElementAt(parents.Count() - 1) >
otherParents.ElementAt(otherParents.Count() - 1) ? 1 : -1;
}
// aNode is not in the tree, compare its top ancestor with aOtherNode's
// document.
return parents.ElementAt(parents.Count() - 1) > otherDocument ? 1 : -1;
}
else if (!otherDocument) {
// aOtherNode is not in a document, compare its top
// ancestor with aNode's document.
return document >
otherParents.ElementAt(otherParents.Count() - 1) ? 1 : -1;
}
// Walk back down along the parent-chains until we find where they split.
PRInt32 total = parents.Count() - 1;
PRInt32 otherTotal = otherParents.Count() - 1;
NS_ASSERTION(total != otherTotal, "Can't have same number of parents");
PRInt32 lastIndex = PR_MIN(total, otherTotal);
PRInt32 i;
parent = nsnull;
for (i = 0; i <= lastIndex; ++i) {
content = NS_STATIC_CAST(nsIContent*, parents.ElementAt(total - i));
otherContent = NS_STATIC_CAST(nsIContent*,
otherParents.ElementAt(otherTotal - i));
if (content != otherContent) {
if (parent) {
index = (PRUint32)parent->IndexOf(content);
otherIndex = (PRUint32)parent->IndexOf(otherContent);
}
else {
index = (PRUint32)document->IndexOf(content);
otherIndex = (PRUint32)document->IndexOf(otherContent);
}
NS_ASSERTION(index != otherIndex && index >= 0 && otherIndex >= 0,
"invalid index in compareTreePosition");
return index < otherIndex ? -1 : 1;
}
parent = content;
}
// One node is a descendant of the other. The one with the shortest
// parent-chain is first in the document.
return total < otherTotal ? -1 : 1;
}
/* static */
txXPathNode*
txXPathNativeNode::createXPathNode(nsIDOMNode* aNode)
{
PRUint16 nodeType;
aNode->GetNodeType(&nodeType);
if (nodeType == nsIDOMNode::ATTRIBUTE_NODE) {
nsCOMPtr<nsIAttribute> attr = do_QueryInterface(aNode);
NS_ASSERTION(attr, "doesn't implement nsIAttribute");
nsINodeInfo *nodeInfo = attr->NodeInfo();
nsIContent *parent = attr->GetContent();
if (!parent) {
return nsnull;
}
nsCOMPtr<nsIAtom> attName, attPrefix;
PRInt32 attNS;
PRUint32 i, total = parent->GetAttrCount();
for (i = 0; i < total; ++i) {
parent->GetAttrNameAt(i, &attNS, getter_AddRefs(attName),
getter_AddRefs(attPrefix));
if (nodeInfo->Equals(attName, attNS)) {
return new txXPathNode(parent, i);
}
}
NS_ERROR("Couldn't find the attribute in its parent!");
return nsnull;
}
if (nodeType == nsIDOMNode::DOCUMENT_NODE) {
nsCOMPtr<nsIDocument> document = do_QueryInterface(aNode);
return new txXPathNode(document);
}
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
return new txXPathNode(content);
}
/* static */
txXPathNode*
txXPathNativeNode::createXPathNode(nsIDOMDocument* aDocument)
{
nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument);
return new txXPathNode(document);
}
/* static */
nsresult
txXPathNativeNode::getNode(const txXPathNode& aNode, nsIDOMNode** aResult)
{
if (aNode.isDocument()) {
return CallQueryInterface(aNode.mDocument, aResult);
}
if (aNode.isContent()) {
return CallQueryInterface(aNode.mContent, aResult);
}
PRInt32 namespaceID;
nsCOMPtr<nsIAtom> name, prefix;
aNode.mContent->GetAttrNameAt(aNode.mIndex, &namespaceID,
getter_AddRefs(name),
getter_AddRefs(prefix));
nsAutoString namespaceURI, localname;
gTxNameSpaceManager->GetNameSpaceURI(namespaceID, namespaceURI);
name->ToString(localname);
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode.mContent);
nsCOMPtr<nsIDOMAttr> attr;
element->GetAttributeNodeNS(namespaceURI, localname,
getter_AddRefs(attr));
return CallQueryInterface(attr, aResult);
}
/* static */
nsIContent*
txXPathNativeNode::getContent(const txXPathNode& aNode)
{
NS_ASSERTION(aNode.isContent(),
"Only call getContent on nsIContent wrappers!");
return aNode.mContent;
}
/* static */
nsIDocument*
txXPathNativeNode::getDocument(const txXPathNode& aNode)
{
NS_ASSERTION(aNode.isDocument(),
"Only call getDocument on nsIDocument wrappers!");
return aNode.mDocument;
}