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