michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 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 "txXPathTreeWalker.h" michael@0: #include "nsIAtom.h" michael@0: #include "nsIAttribute.h" michael@0: #include "nsIDOMAttr.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMProcessingInstruction.h" michael@0: #include "nsINodeInfo.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsString.h" michael@0: #include "nsTextFragment.h" michael@0: #include "txXMLUtils.h" michael@0: #include "txLog.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsAttrName.h" michael@0: #include "nsTArray.h" michael@0: #include "mozilla/dom/Attr.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include michael@0: #include michael@0: michael@0: using mozilla::dom::Attr; michael@0: michael@0: const uint32_t kUnknownIndex = uint32_t(-1); michael@0: michael@0: txXPathTreeWalker::txXPathTreeWalker(const txXPathTreeWalker& aOther) michael@0: : mPosition(aOther.mPosition), michael@0: mCurrentIndex(aOther.mCurrentIndex) michael@0: { michael@0: } michael@0: michael@0: txXPathTreeWalker::txXPathTreeWalker(const txXPathNode& aNode) michael@0: : mPosition(aNode), michael@0: mCurrentIndex(kUnknownIndex) michael@0: { michael@0: } michael@0: michael@0: void michael@0: txXPathTreeWalker::moveToRoot() michael@0: { michael@0: if (mPosition.isDocument()) { michael@0: return; michael@0: } michael@0: michael@0: nsIDocument* root = mPosition.mNode->GetCurrentDoc(); michael@0: if (root) { michael@0: mPosition.mIndex = txXPathNode::eDocument; michael@0: mPosition.mNode = root; michael@0: } michael@0: else { michael@0: nsINode *rootNode = mPosition.Root(); michael@0: michael@0: NS_ASSERTION(rootNode->IsNodeOfType(nsINode::eCONTENT), michael@0: "root of subtree wasn't an nsIContent"); michael@0: michael@0: mPosition.mIndex = txXPathNode::eContent; michael@0: mPosition.mNode = rootNode; michael@0: } michael@0: michael@0: mCurrentIndex = kUnknownIndex; michael@0: mDescendants.Clear(); michael@0: } michael@0: michael@0: bool michael@0: txXPathTreeWalker::moveToElementById(const nsAString& aID) michael@0: { michael@0: if (aID.IsEmpty()) { michael@0: return false; michael@0: } michael@0: michael@0: nsIDocument* doc = mPosition.mNode->GetCurrentDoc(); michael@0: michael@0: nsCOMPtr content; michael@0: if (doc) { michael@0: content = doc->GetElementById(aID); michael@0: } michael@0: else { michael@0: // We're in a disconnected subtree, search only that subtree. michael@0: nsINode *rootNode = mPosition.Root(); michael@0: michael@0: NS_ASSERTION(rootNode->IsNodeOfType(nsINode::eCONTENT), michael@0: "root of subtree wasn't an nsIContent"); michael@0: michael@0: content = nsContentUtils::MatchElementId( michael@0: static_cast(rootNode), aID); michael@0: } michael@0: michael@0: if (!content) { michael@0: return false; michael@0: } michael@0: michael@0: mPosition.mIndex = txXPathNode::eContent; michael@0: mPosition.mNode = content; michael@0: mCurrentIndex = kUnknownIndex; michael@0: mDescendants.Clear(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: txXPathTreeWalker::moveToFirstAttribute() michael@0: { michael@0: if (!mPosition.isContent()) { michael@0: return false; michael@0: } michael@0: michael@0: return moveToValidAttribute(0); michael@0: } michael@0: michael@0: bool michael@0: txXPathTreeWalker::moveToNextAttribute() michael@0: { michael@0: // XXX an assertion should be enough here with the current code michael@0: if (!mPosition.isAttribute()) { michael@0: return false; michael@0: } michael@0: michael@0: return moveToValidAttribute(mPosition.mIndex + 1); michael@0: } michael@0: michael@0: bool michael@0: txXPathTreeWalker::moveToValidAttribute(uint32_t aStartIndex) michael@0: { michael@0: NS_ASSERTION(!mPosition.isDocument(), "documents doesn't have attrs"); michael@0: michael@0: uint32_t total = mPosition.Content()->GetAttrCount(); michael@0: if (aStartIndex >= total) { michael@0: return false; michael@0: } michael@0: michael@0: uint32_t index; michael@0: for (index = aStartIndex; index < total; ++index) { michael@0: const nsAttrName* name = mPosition.Content()->GetAttrNameAt(index); michael@0: michael@0: // We need to ignore XMLNS attributes. michael@0: if (name->NamespaceID() != kNameSpaceID_XMLNS) { michael@0: mPosition.mIndex = index; michael@0: michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: txXPathTreeWalker::moveToNamedAttribute(nsIAtom* aLocalName, int32_t aNSID) michael@0: { michael@0: if (!mPosition.isContent()) { michael@0: return false; michael@0: } michael@0: michael@0: const nsAttrName* name; michael@0: uint32_t i; michael@0: for (i = 0; (name = mPosition.Content()->GetAttrNameAt(i)); ++i) { michael@0: if (name->Equals(aLocalName, aNSID)) { michael@0: mPosition.mIndex = i; michael@0: michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: txXPathTreeWalker::moveToFirstChild() michael@0: { michael@0: if (mPosition.isAttribute()) { michael@0: return false; michael@0: } michael@0: michael@0: NS_ASSERTION(!mPosition.isDocument() || michael@0: (mCurrentIndex == kUnknownIndex && mDescendants.IsEmpty()), michael@0: "we shouldn't have any position info at the document"); michael@0: NS_ASSERTION(mCurrentIndex != kUnknownIndex || mDescendants.IsEmpty(), michael@0: "Index should be known if parents index are"); michael@0: michael@0: nsIContent* child = mPosition.mNode->GetFirstChild(); michael@0: if (!child) { michael@0: return false; michael@0: } michael@0: mPosition.mIndex = txXPathNode::eContent; michael@0: mPosition.mNode = child; michael@0: michael@0: if (mCurrentIndex != kUnknownIndex && michael@0: !mDescendants.AppendValue(mCurrentIndex)) { michael@0: mDescendants.Clear(); michael@0: } michael@0: mCurrentIndex = 0; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: txXPathTreeWalker::moveToLastChild() michael@0: { michael@0: if (mPosition.isAttribute()) { michael@0: return false; michael@0: } michael@0: michael@0: NS_ASSERTION(!mPosition.isDocument() || michael@0: (mCurrentIndex == kUnknownIndex && mDescendants.IsEmpty()), michael@0: "we shouldn't have any position info at the document"); michael@0: NS_ASSERTION(mCurrentIndex != kUnknownIndex || mDescendants.IsEmpty(), michael@0: "Index should be known if parents index are"); michael@0: michael@0: uint32_t total = mPosition.mNode->GetChildCount(); michael@0: if (!total) { michael@0: return false; michael@0: } michael@0: mPosition.mNode = mPosition.mNode->GetLastChild(); michael@0: michael@0: if (mCurrentIndex != kUnknownIndex && michael@0: !mDescendants.AppendValue(mCurrentIndex)) { michael@0: mDescendants.Clear(); michael@0: } michael@0: mCurrentIndex = total - 1; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: txXPathTreeWalker::moveToNextSibling() michael@0: { michael@0: if (!mPosition.isContent()) { michael@0: return false; michael@0: } michael@0: michael@0: return moveToSibling(1); michael@0: } michael@0: michael@0: bool michael@0: txXPathTreeWalker::moveToPreviousSibling() michael@0: { michael@0: if (!mPosition.isContent()) { michael@0: return false; michael@0: } michael@0: michael@0: return moveToSibling(-1); michael@0: } michael@0: michael@0: bool michael@0: txXPathTreeWalker::moveToParent() michael@0: { michael@0: if (mPosition.isDocument()) { michael@0: return false; michael@0: } michael@0: michael@0: if (mPosition.isAttribute()) { michael@0: mPosition.mIndex = txXPathNode::eContent; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: nsINode* parent = mPosition.mNode->GetParentNode(); michael@0: if (!parent) { michael@0: return false; michael@0: } michael@0: michael@0: uint32_t count = mDescendants.Length(); michael@0: if (count) { michael@0: mCurrentIndex = mDescendants.ValueAt(--count); michael@0: mDescendants.RemoveValueAt(count); michael@0: } michael@0: else { michael@0: mCurrentIndex = kUnknownIndex; michael@0: } michael@0: michael@0: mPosition.mIndex = mPosition.mNode->GetParent() ? michael@0: txXPathNode::eContent : txXPathNode::eDocument; michael@0: mPosition.mNode = parent; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: txXPathTreeWalker::moveToSibling(int32_t aDir) michael@0: { michael@0: NS_ASSERTION(mPosition.isContent(), michael@0: "moveToSibling should only be called for content"); michael@0: michael@0: nsINode* parent = mPosition.mNode->GetParentNode(); michael@0: if (!parent) { michael@0: return false; michael@0: } michael@0: if (mCurrentIndex == kUnknownIndex) { michael@0: mCurrentIndex = parent->IndexOf(mPosition.mNode); michael@0: } michael@0: michael@0: // if mCurrentIndex is 0 we rely on GetChildAt returning null for an michael@0: // index of uint32_t(-1). michael@0: uint32_t newIndex = mCurrentIndex + aDir; michael@0: nsIContent* newChild = parent->GetChildAt(newIndex); michael@0: if (!newChild) { michael@0: return false; michael@0: } michael@0: michael@0: mPosition.mNode = newChild; michael@0: mCurrentIndex = newIndex; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: txXPathNode::txXPathNode(const txXPathNode& aNode) michael@0: : mNode(aNode.mNode), michael@0: mRefCountRoot(aNode.mRefCountRoot), michael@0: mIndex(aNode.mIndex) michael@0: { michael@0: MOZ_COUNT_CTOR(txXPathNode); michael@0: if (mRefCountRoot) { michael@0: NS_ADDREF(Root()); michael@0: } michael@0: } michael@0: michael@0: txXPathNode::~txXPathNode() michael@0: { michael@0: MOZ_COUNT_DTOR(txXPathNode); michael@0: if (mRefCountRoot) { michael@0: nsINode *root = Root(); michael@0: NS_RELEASE(root); michael@0: } michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: txXPathNodeUtils::getAttr(const txXPathNode& aNode, nsIAtom* aLocalName, michael@0: int32_t aNSID, nsAString& aValue) michael@0: { michael@0: if (aNode.isDocument() || aNode.isAttribute()) { michael@0: return false; michael@0: } michael@0: michael@0: return aNode.Content()->GetAttr(aNSID, aLocalName, aValue); michael@0: } michael@0: michael@0: /* static */ michael@0: already_AddRefed michael@0: txXPathNodeUtils::getLocalName(const txXPathNode& aNode) michael@0: { michael@0: if (aNode.isDocument()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aNode.isContent()) { michael@0: if (aNode.mNode->IsElement()) { michael@0: nsCOMPtr localName = aNode.Content()->Tag(); michael@0: return localName.forget(); michael@0: } michael@0: michael@0: if (aNode.mNode->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) { michael@0: nsCOMPtr node = do_QueryInterface(aNode.mNode); michael@0: nsAutoString target; michael@0: node->GetNodeName(target); michael@0: michael@0: return NS_NewAtom(target); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr localName = aNode.Content()-> michael@0: GetAttrNameAt(aNode.mIndex)->LocalName(); michael@0: michael@0: return localName.forget(); michael@0: } michael@0: michael@0: nsIAtom* michael@0: txXPathNodeUtils::getPrefix(const txXPathNode& aNode) michael@0: { michael@0: if (aNode.isDocument()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aNode.isContent()) { michael@0: // All other nsIContent node types but elements have a null prefix michael@0: // which is what we want here. michael@0: return aNode.Content()->NodeInfo()->GetPrefixAtom(); michael@0: } michael@0: michael@0: return aNode.Content()->GetAttrNameAt(aNode.mIndex)->GetPrefix(); michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: txXPathNodeUtils::getLocalName(const txXPathNode& aNode, nsAString& aLocalName) michael@0: { michael@0: if (aNode.isDocument()) { michael@0: aLocalName.Truncate(); michael@0: michael@0: return; michael@0: } michael@0: michael@0: if (aNode.isContent()) { michael@0: if (aNode.mNode->IsElement()) { michael@0: nsINodeInfo* nodeInfo = aNode.Content()->NodeInfo(); michael@0: nodeInfo->GetName(aLocalName); michael@0: return; michael@0: } michael@0: michael@0: if (aNode.mNode->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) { michael@0: // PIs don't have a nodeinfo but do have a name michael@0: nsCOMPtr node = do_QueryInterface(aNode.mNode); michael@0: node->GetNodeName(aLocalName); michael@0: michael@0: return; michael@0: } michael@0: michael@0: aLocalName.Truncate(); michael@0: michael@0: return; michael@0: } michael@0: michael@0: aNode.Content()->GetAttrNameAt(aNode.mIndex)->LocalName()-> michael@0: ToString(aLocalName); michael@0: michael@0: // Check for html michael@0: if (aNode.Content()->NodeInfo()->NamespaceEquals(kNameSpaceID_None) && michael@0: aNode.Content()->IsHTML()) { michael@0: nsContentUtils::ASCIIToUpper(aLocalName); michael@0: } michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: txXPathNodeUtils::getNodeName(const txXPathNode& aNode, nsAString& aName) michael@0: { michael@0: if (aNode.isDocument()) { michael@0: aName.Truncate(); michael@0: michael@0: return; michael@0: } michael@0: michael@0: if (aNode.isContent()) { michael@0: // Elements and PIs have a name michael@0: if (aNode.mNode->IsElement() || michael@0: aNode.mNode->NodeType() == michael@0: nsIDOMNode::PROCESSING_INSTRUCTION_NODE) { michael@0: aName = aNode.Content()->NodeName(); michael@0: return; michael@0: } michael@0: michael@0: aName.Truncate(); michael@0: michael@0: return; michael@0: } michael@0: michael@0: aNode.Content()->GetAttrNameAt(aNode.mIndex)->GetQualifiedName(aName); michael@0: } michael@0: michael@0: /* static */ michael@0: int32_t michael@0: txXPathNodeUtils::getNamespaceID(const txXPathNode& aNode) michael@0: { michael@0: if (aNode.isDocument()) { michael@0: return kNameSpaceID_None; michael@0: } michael@0: michael@0: if (aNode.isContent()) { michael@0: return aNode.Content()->GetNameSpaceID(); michael@0: } michael@0: michael@0: return aNode.Content()->GetAttrNameAt(aNode.mIndex)->NamespaceID(); michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: txXPathNodeUtils::getNamespaceURI(const txXPathNode& aNode, nsAString& aURI) michael@0: { michael@0: nsContentUtils::NameSpaceManager()->GetNameSpaceURI(getNamespaceID(aNode), aURI); michael@0: } michael@0: michael@0: /* static */ michael@0: uint16_t michael@0: txXPathNodeUtils::getNodeType(const txXPathNode& aNode) michael@0: { michael@0: if (aNode.isDocument()) { michael@0: return txXPathNodeType::DOCUMENT_NODE; michael@0: } michael@0: michael@0: if (aNode.isContent()) { michael@0: return aNode.mNode->NodeType(); michael@0: } michael@0: michael@0: return txXPathNodeType::ATTRIBUTE_NODE; michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: txXPathNodeUtils::appendNodeValue(const txXPathNode& aNode, nsAString& aResult) michael@0: { michael@0: if (aNode.isAttribute()) { michael@0: const nsAttrName* name = aNode.Content()->GetAttrNameAt(aNode.mIndex); michael@0: michael@0: if (aResult.IsEmpty()) { michael@0: aNode.Content()->GetAttr(name->NamespaceID(), name->LocalName(), michael@0: aResult); michael@0: } michael@0: else { michael@0: nsAutoString result; michael@0: aNode.Content()->GetAttr(name->NamespaceID(), name->LocalName(), michael@0: result); michael@0: aResult.Append(result); michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: if (aNode.isDocument() || michael@0: aNode.mNode->IsElement() || michael@0: aNode.mNode->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT)) { michael@0: nsContentUtils::AppendNodeTextContent(aNode.mNode, true, aResult, michael@0: mozilla::fallible_t()); michael@0: michael@0: return; michael@0: } michael@0: michael@0: aNode.Content()->AppendTextTo(aResult); michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: txXPathNodeUtils::isWhitespace(const txXPathNode& aNode) michael@0: { michael@0: NS_ASSERTION(aNode.isContent() && isText(aNode), "Wrong type!"); michael@0: michael@0: return aNode.Content()->TextIsOnlyWhitespace(); michael@0: } michael@0: michael@0: /* static */ michael@0: txXPathNode* michael@0: txXPathNodeUtils::getOwnerDocument(const txXPathNode& aNode) michael@0: { michael@0: return new txXPathNode(aNode.mNode->OwnerDoc()); michael@0: } michael@0: michael@0: const char gPrintfFmt[] = "id0x%p"; michael@0: const char gPrintfFmtAttr[] = "id0x%p-%010i"; michael@0: michael@0: /* static */ michael@0: nsresult michael@0: txXPathNodeUtils::getXSLTId(const txXPathNode& aNode, michael@0: const txXPathNode& aBase, michael@0: nsAString& aResult) michael@0: { michael@0: uintptr_t nodeid = ((uintptr_t)aNode.mNode) - ((uintptr_t)aBase.mNode); michael@0: if (!aNode.isAttribute()) { michael@0: CopyASCIItoUTF16(nsPrintfCString(gPrintfFmt, nodeid), michael@0: aResult); michael@0: } michael@0: else { michael@0: CopyASCIItoUTF16(nsPrintfCString(gPrintfFmtAttr, michael@0: nodeid, aNode.mIndex), aResult); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsAString& aURI) michael@0: { michael@0: aNode.mNode->GetBaseURI(aURI); michael@0: } michael@0: michael@0: /* static */ michael@0: int michael@0: txXPathNodeUtils::comparePosition(const txXPathNode& aNode, michael@0: const txXPathNode& aOtherNode) michael@0: { michael@0: // First check for equal nodes or attribute-nodes on the same element. michael@0: if (aNode.mNode == aOtherNode.mNode) { michael@0: if (aNode.mIndex == aOtherNode.mIndex) { michael@0: return 0; michael@0: } michael@0: michael@0: NS_ASSERTION(!aNode.isDocument() && !aOtherNode.isDocument(), michael@0: "documents should always have a set index"); michael@0: michael@0: if (aNode.isContent() || (!aOtherNode.isContent() && michael@0: aNode.mIndex < aOtherNode.mIndex)) { michael@0: return -1; michael@0: } michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: // Get document for both nodes. michael@0: nsIDocument* document = aNode.mNode->GetCurrentDoc(); michael@0: nsIDocument* otherDocument = aOtherNode.mNode->GetCurrentDoc(); michael@0: michael@0: // If the nodes have different current documents, compare the document michael@0: // pointers. michael@0: if (document != otherDocument) { michael@0: return document < otherDocument ? -1 : 1; michael@0: } michael@0: michael@0: // Now either both nodes are in orphan trees, or they are both in the michael@0: // same tree. michael@0: michael@0: // Get parents up the tree. michael@0: nsAutoTArray parents, otherParents; michael@0: nsINode* node = aNode.mNode; michael@0: nsINode* otherNode = aOtherNode.mNode; michael@0: nsINode* parent, *otherParent; michael@0: while (node && otherNode) { michael@0: parent = node->GetParentNode(); michael@0: otherParent = otherNode->GetParentNode(); michael@0: michael@0: // Hopefully this is a common case. michael@0: if (parent == otherParent) { michael@0: if (!parent) { michael@0: // Both node and otherNode are root nodes in respective orphan michael@0: // tree. michael@0: return node < otherNode ? -1 : 1; michael@0: } michael@0: michael@0: return parent->IndexOf(node) < parent->IndexOf(otherNode) ? michael@0: -1 : 1; michael@0: } michael@0: michael@0: parents.AppendElement(node); michael@0: otherParents.AppendElement(otherNode); michael@0: node = parent; michael@0: otherNode = otherParent; michael@0: } michael@0: michael@0: while (node) { michael@0: parents.AppendElement(node); michael@0: node = node->GetParentNode(); michael@0: } michael@0: while (otherNode) { michael@0: otherParents.AppendElement(otherNode); michael@0: otherNode = otherNode->GetParentNode(); michael@0: } michael@0: michael@0: // Walk back down along the parent-chains until we find where they split. michael@0: int32_t total = parents.Length() - 1; michael@0: int32_t otherTotal = otherParents.Length() - 1; michael@0: NS_ASSERTION(total != otherTotal, "Can't have same number of parents"); michael@0: michael@0: int32_t lastIndex = std::min(total, otherTotal); michael@0: int32_t i; michael@0: parent = nullptr; michael@0: for (i = 0; i <= lastIndex; ++i) { michael@0: node = parents.ElementAt(total - i); michael@0: otherNode = otherParents.ElementAt(otherTotal - i); michael@0: if (node != otherNode) { michael@0: if (!parent) { michael@0: // The two nodes are in different orphan subtrees. michael@0: NS_ASSERTION(i == 0, "this shouldn't happen"); michael@0: return node < otherNode ? -1 : 1; michael@0: } michael@0: michael@0: int32_t index = parent->IndexOf(node); michael@0: int32_t otherIndex = parent->IndexOf(otherNode); michael@0: NS_ASSERTION(index != otherIndex && index >= 0 && otherIndex >= 0, michael@0: "invalid index in compareTreePosition"); michael@0: michael@0: return index < otherIndex ? -1 : 1; michael@0: } michael@0: michael@0: parent = node; michael@0: } michael@0: michael@0: // One node is a descendant of the other. The one with the shortest michael@0: // parent-chain is first in the document. michael@0: return total < otherTotal ? -1 : 1; michael@0: } michael@0: michael@0: /* static */ michael@0: txXPathNode* michael@0: txXPathNativeNode::createXPathNode(nsIContent* aContent, bool aKeepRootAlive) michael@0: { michael@0: nsINode* root = aKeepRootAlive ? txXPathNode::RootOf(aContent) : nullptr; michael@0: michael@0: return new txXPathNode(aContent, txXPathNode::eContent, root); michael@0: } michael@0: michael@0: /* static */ michael@0: txXPathNode* michael@0: txXPathNativeNode::createXPathNode(nsIDOMNode* aNode, bool aKeepRootAlive) michael@0: { michael@0: uint16_t nodeType; michael@0: aNode->GetNodeType(&nodeType); michael@0: michael@0: if (nodeType == nsIDOMNode::ATTRIBUTE_NODE) { michael@0: nsCOMPtr attr = do_QueryInterface(aNode); michael@0: NS_ASSERTION(attr, "doesn't implement nsIAttribute"); michael@0: michael@0: nsINodeInfo *nodeInfo = attr->NodeInfo(); michael@0: mozilla::dom::Element* parent = michael@0: static_cast(attr.get())->GetElement(); michael@0: if (!parent) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsINode* root = aKeepRootAlive ? txXPathNode::RootOf(parent) : nullptr; michael@0: michael@0: uint32_t i, total = parent->GetAttrCount(); michael@0: for (i = 0; i < total; ++i) { michael@0: const nsAttrName* name = parent->GetAttrNameAt(i); michael@0: if (nodeInfo->Equals(name->LocalName(), name->NamespaceID())) { michael@0: return new txXPathNode(parent, i, root); michael@0: } michael@0: } michael@0: michael@0: NS_ERROR("Couldn't find the attribute in its parent!"); michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: uint32_t index; michael@0: nsINode* root = aKeepRootAlive ? node.get() : nullptr; michael@0: michael@0: if (nodeType == nsIDOMNode::DOCUMENT_NODE) { michael@0: index = txXPathNode::eDocument; michael@0: } michael@0: else { michael@0: index = txXPathNode::eContent; michael@0: if (root) { michael@0: root = txXPathNode::RootOf(root); michael@0: } michael@0: } michael@0: michael@0: return new txXPathNode(node, index, root); michael@0: } michael@0: michael@0: /* static */ michael@0: txXPathNode* michael@0: txXPathNativeNode::createXPathNode(nsIDOMDocument* aDocument) michael@0: { michael@0: nsCOMPtr document = do_QueryInterface(aDocument); michael@0: return new txXPathNode(document); michael@0: } michael@0: michael@0: /* static */ michael@0: nsresult michael@0: txXPathNativeNode::getNode(const txXPathNode& aNode, nsIDOMNode** aResult) michael@0: { michael@0: if (!aNode.isAttribute()) { michael@0: return CallQueryInterface(aNode.mNode, aResult); michael@0: } michael@0: michael@0: const nsAttrName* name = aNode.Content()->GetAttrNameAt(aNode.mIndex); michael@0: michael@0: nsAutoString namespaceURI; michael@0: nsContentUtils::NameSpaceManager()->GetNameSpaceURI(name->NamespaceID(), namespaceURI); michael@0: michael@0: nsCOMPtr element = do_QueryInterface(aNode.mNode); michael@0: nsCOMPtr attr; michael@0: element->GetAttributeNodeNS(namespaceURI, michael@0: nsDependentAtomString(name->LocalName()), michael@0: getter_AddRefs(attr)); michael@0: michael@0: return CallQueryInterface(attr, aResult); michael@0: } michael@0: michael@0: /* static */ michael@0: nsIContent* michael@0: txXPathNativeNode::getContent(const txXPathNode& aNode) michael@0: { michael@0: NS_ASSERTION(aNode.isContent(), michael@0: "Only call getContent on nsIContent wrappers!"); michael@0: return aNode.Content(); michael@0: } michael@0: michael@0: /* static */ michael@0: nsIDocument* michael@0: txXPathNativeNode::getDocument(const txXPathNode& aNode) michael@0: { michael@0: NS_ASSERTION(aNode.isDocument(), michael@0: "Only call getDocument on nsIDocument wrappers!"); michael@0: return aNode.Document(); michael@0: }