dom/xslt/xpath/txMozillaXPathTreeWalker.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/xslt/xpath/txMozillaXPathTreeWalker.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,781 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "txXPathTreeWalker.h"
    1.10 +#include "nsIAtom.h"
    1.11 +#include "nsIAttribute.h"
    1.12 +#include "nsIDOMAttr.h"
    1.13 +#include "nsIDOMDocument.h"
    1.14 +#include "nsIDOMNode.h"
    1.15 +#include "nsIDOMElement.h"
    1.16 +#include "nsIDOMProcessingInstruction.h"
    1.17 +#include "nsINodeInfo.h"
    1.18 +#include "nsPrintfCString.h"
    1.19 +#include "nsReadableUtils.h"
    1.20 +#include "nsString.h"
    1.21 +#include "nsTextFragment.h"
    1.22 +#include "txXMLUtils.h"
    1.23 +#include "txLog.h"
    1.24 +#include "nsUnicharUtils.h"
    1.25 +#include "nsAttrName.h"
    1.26 +#include "nsTArray.h"
    1.27 +#include "mozilla/dom/Attr.h"
    1.28 +#include "mozilla/dom/Element.h"
    1.29 +#include <stdint.h>
    1.30 +#include <algorithm>
    1.31 +
    1.32 +using mozilla::dom::Attr;
    1.33 +
    1.34 +const uint32_t kUnknownIndex = uint32_t(-1);
    1.35 +
    1.36 +txXPathTreeWalker::txXPathTreeWalker(const txXPathTreeWalker& aOther)
    1.37 +    : mPosition(aOther.mPosition),
    1.38 +      mCurrentIndex(aOther.mCurrentIndex)
    1.39 +{
    1.40 +}
    1.41 +
    1.42 +txXPathTreeWalker::txXPathTreeWalker(const txXPathNode& aNode)
    1.43 +    : mPosition(aNode),
    1.44 +      mCurrentIndex(kUnknownIndex)
    1.45 +{
    1.46 +}
    1.47 +
    1.48 +void
    1.49 +txXPathTreeWalker::moveToRoot()
    1.50 +{
    1.51 +    if (mPosition.isDocument()) {
    1.52 +        return;
    1.53 +    }
    1.54 +
    1.55 +    nsIDocument* root = mPosition.mNode->GetCurrentDoc();
    1.56 +    if (root) {
    1.57 +        mPosition.mIndex = txXPathNode::eDocument;
    1.58 +        mPosition.mNode = root;
    1.59 +    }
    1.60 +    else {
    1.61 +        nsINode *rootNode = mPosition.Root();
    1.62 +
    1.63 +        NS_ASSERTION(rootNode->IsNodeOfType(nsINode::eCONTENT),
    1.64 +                     "root of subtree wasn't an nsIContent");
    1.65 +
    1.66 +        mPosition.mIndex = txXPathNode::eContent;
    1.67 +        mPosition.mNode = rootNode;
    1.68 +    }
    1.69 +
    1.70 +    mCurrentIndex = kUnknownIndex;
    1.71 +    mDescendants.Clear();
    1.72 +}
    1.73 +
    1.74 +bool
    1.75 +txXPathTreeWalker::moveToElementById(const nsAString& aID)
    1.76 +{
    1.77 +    if (aID.IsEmpty()) {
    1.78 +        return false;
    1.79 +    }
    1.80 +
    1.81 +    nsIDocument* doc = mPosition.mNode->GetCurrentDoc();
    1.82 +
    1.83 +    nsCOMPtr<nsIContent> content;
    1.84 +    if (doc) {
    1.85 +        content = doc->GetElementById(aID);
    1.86 +    }
    1.87 +    else {
    1.88 +        // We're in a disconnected subtree, search only that subtree.
    1.89 +        nsINode *rootNode = mPosition.Root();
    1.90 +
    1.91 +        NS_ASSERTION(rootNode->IsNodeOfType(nsINode::eCONTENT),
    1.92 +                     "root of subtree wasn't an nsIContent");
    1.93 +
    1.94 +        content = nsContentUtils::MatchElementId(
    1.95 +            static_cast<nsIContent*>(rootNode), aID);
    1.96 +    }
    1.97 +
    1.98 +    if (!content) {
    1.99 +        return false;
   1.100 +    }
   1.101 +
   1.102 +    mPosition.mIndex = txXPathNode::eContent;
   1.103 +    mPosition.mNode = content;
   1.104 +    mCurrentIndex = kUnknownIndex;
   1.105 +    mDescendants.Clear();
   1.106 +
   1.107 +    return true;
   1.108 +}
   1.109 +
   1.110 +bool
   1.111 +txXPathTreeWalker::moveToFirstAttribute()
   1.112 +{
   1.113 +    if (!mPosition.isContent()) {
   1.114 +        return false;
   1.115 +    }
   1.116 +
   1.117 +    return moveToValidAttribute(0);
   1.118 +}
   1.119 +
   1.120 +bool
   1.121 +txXPathTreeWalker::moveToNextAttribute()
   1.122 +{
   1.123 +    // XXX an assertion should be enough here with the current code
   1.124 +    if (!mPosition.isAttribute()) {
   1.125 +        return false;
   1.126 +    }
   1.127 +
   1.128 +    return moveToValidAttribute(mPosition.mIndex + 1);
   1.129 +}
   1.130 +
   1.131 +bool
   1.132 +txXPathTreeWalker::moveToValidAttribute(uint32_t aStartIndex)
   1.133 +{
   1.134 +    NS_ASSERTION(!mPosition.isDocument(), "documents doesn't have attrs");
   1.135 +
   1.136 +    uint32_t total = mPosition.Content()->GetAttrCount();
   1.137 +    if (aStartIndex >= total) {
   1.138 +        return false;
   1.139 +    }
   1.140 +
   1.141 +    uint32_t index;
   1.142 +    for (index = aStartIndex; index < total; ++index) {
   1.143 +        const nsAttrName* name = mPosition.Content()->GetAttrNameAt(index);
   1.144 +
   1.145 +        // We need to ignore XMLNS attributes.
   1.146 +        if (name->NamespaceID() != kNameSpaceID_XMLNS) {
   1.147 +            mPosition.mIndex = index;
   1.148 +
   1.149 +            return true;
   1.150 +        }
   1.151 +    }
   1.152 +    return false;
   1.153 +}
   1.154 +
   1.155 +bool
   1.156 +txXPathTreeWalker::moveToNamedAttribute(nsIAtom* aLocalName, int32_t aNSID)
   1.157 +{
   1.158 +    if (!mPosition.isContent()) {
   1.159 +        return false;
   1.160 +    }
   1.161 +
   1.162 +    const nsAttrName* name;
   1.163 +    uint32_t i;
   1.164 +    for (i = 0; (name = mPosition.Content()->GetAttrNameAt(i)); ++i) {
   1.165 +        if (name->Equals(aLocalName, aNSID)) {
   1.166 +            mPosition.mIndex = i;
   1.167 +
   1.168 +            return true;
   1.169 +        }
   1.170 +    }
   1.171 +    return false;
   1.172 +}
   1.173 +
   1.174 +bool
   1.175 +txXPathTreeWalker::moveToFirstChild()
   1.176 +{
   1.177 +    if (mPosition.isAttribute()) {
   1.178 +        return false;
   1.179 +    }
   1.180 +
   1.181 +    NS_ASSERTION(!mPosition.isDocument() ||
   1.182 +                 (mCurrentIndex == kUnknownIndex && mDescendants.IsEmpty()),
   1.183 +                 "we shouldn't have any position info at the document");
   1.184 +    NS_ASSERTION(mCurrentIndex != kUnknownIndex || mDescendants.IsEmpty(),
   1.185 +                 "Index should be known if parents index are");
   1.186 +
   1.187 +    nsIContent* child = mPosition.mNode->GetFirstChild();
   1.188 +    if (!child) {
   1.189 +        return false;
   1.190 +    }
   1.191 +    mPosition.mIndex = txXPathNode::eContent;
   1.192 +    mPosition.mNode = child;
   1.193 +
   1.194 +    if (mCurrentIndex != kUnknownIndex &&
   1.195 +        !mDescendants.AppendValue(mCurrentIndex)) {
   1.196 +        mDescendants.Clear();
   1.197 +    }
   1.198 +    mCurrentIndex = 0;
   1.199 +
   1.200 +    return true;
   1.201 +}
   1.202 +
   1.203 +bool
   1.204 +txXPathTreeWalker::moveToLastChild()
   1.205 +{
   1.206 +    if (mPosition.isAttribute()) {
   1.207 +        return false;
   1.208 +    }
   1.209 +
   1.210 +    NS_ASSERTION(!mPosition.isDocument() ||
   1.211 +                 (mCurrentIndex == kUnknownIndex && mDescendants.IsEmpty()),
   1.212 +                 "we shouldn't have any position info at the document");
   1.213 +    NS_ASSERTION(mCurrentIndex != kUnknownIndex || mDescendants.IsEmpty(),
   1.214 +                 "Index should be known if parents index are");
   1.215 +
   1.216 +    uint32_t total = mPosition.mNode->GetChildCount();
   1.217 +    if (!total) {
   1.218 +        return false;
   1.219 +    }
   1.220 +    mPosition.mNode = mPosition.mNode->GetLastChild();
   1.221 +
   1.222 +    if (mCurrentIndex != kUnknownIndex &&
   1.223 +        !mDescendants.AppendValue(mCurrentIndex)) {
   1.224 +        mDescendants.Clear();
   1.225 +    }
   1.226 +    mCurrentIndex = total - 1;
   1.227 +
   1.228 +    return true;
   1.229 +}
   1.230 +
   1.231 +bool
   1.232 +txXPathTreeWalker::moveToNextSibling()
   1.233 +{
   1.234 +    if (!mPosition.isContent()) {
   1.235 +        return false;
   1.236 +    }
   1.237 +
   1.238 +    return moveToSibling(1);
   1.239 +}
   1.240 +
   1.241 +bool
   1.242 +txXPathTreeWalker::moveToPreviousSibling()
   1.243 +{
   1.244 +    if (!mPosition.isContent()) {
   1.245 +        return false;
   1.246 +    }
   1.247 +
   1.248 +    return moveToSibling(-1);
   1.249 +}
   1.250 +
   1.251 +bool
   1.252 +txXPathTreeWalker::moveToParent()
   1.253 +{
   1.254 +    if (mPosition.isDocument()) {
   1.255 +        return false;
   1.256 +    }
   1.257 +
   1.258 +    if (mPosition.isAttribute()) {
   1.259 +        mPosition.mIndex = txXPathNode::eContent;
   1.260 +
   1.261 +        return true;
   1.262 +    }
   1.263 +
   1.264 +    nsINode* parent = mPosition.mNode->GetParentNode();
   1.265 +    if (!parent) {
   1.266 +        return false;
   1.267 +    }
   1.268 +
   1.269 +    uint32_t count = mDescendants.Length();
   1.270 +    if (count) {
   1.271 +        mCurrentIndex = mDescendants.ValueAt(--count);
   1.272 +        mDescendants.RemoveValueAt(count);
   1.273 +    }
   1.274 +    else {
   1.275 +        mCurrentIndex = kUnknownIndex;
   1.276 +    }
   1.277 +
   1.278 +    mPosition.mIndex = mPosition.mNode->GetParent() ?
   1.279 +      txXPathNode::eContent : txXPathNode::eDocument;
   1.280 +    mPosition.mNode = parent;
   1.281 +
   1.282 +    return true;
   1.283 +}
   1.284 +
   1.285 +bool
   1.286 +txXPathTreeWalker::moveToSibling(int32_t aDir)
   1.287 +{
   1.288 +    NS_ASSERTION(mPosition.isContent(),
   1.289 +                 "moveToSibling should only be called for content");
   1.290 +
   1.291 +    nsINode* parent = mPosition.mNode->GetParentNode();
   1.292 +    if (!parent) {
   1.293 +        return false;
   1.294 +    }
   1.295 +    if (mCurrentIndex == kUnknownIndex) {
   1.296 +        mCurrentIndex = parent->IndexOf(mPosition.mNode);
   1.297 +    }
   1.298 +
   1.299 +    // if mCurrentIndex is 0 we rely on GetChildAt returning null for an
   1.300 +    // index of uint32_t(-1).
   1.301 +    uint32_t newIndex = mCurrentIndex + aDir;
   1.302 +    nsIContent* newChild = parent->GetChildAt(newIndex);
   1.303 +    if (!newChild) {
   1.304 +        return false;
   1.305 +    }
   1.306 +
   1.307 +    mPosition.mNode = newChild;
   1.308 +    mCurrentIndex = newIndex;
   1.309 +
   1.310 +    return true;
   1.311 +}
   1.312 +
   1.313 +txXPathNode::txXPathNode(const txXPathNode& aNode)
   1.314 +  : mNode(aNode.mNode),
   1.315 +    mRefCountRoot(aNode.mRefCountRoot),
   1.316 +    mIndex(aNode.mIndex)
   1.317 +{
   1.318 +    MOZ_COUNT_CTOR(txXPathNode);
   1.319 +    if (mRefCountRoot) {
   1.320 +        NS_ADDREF(Root());
   1.321 +    }
   1.322 +}
   1.323 +
   1.324 +txXPathNode::~txXPathNode()
   1.325 +{
   1.326 +    MOZ_COUNT_DTOR(txXPathNode);
   1.327 +    if (mRefCountRoot) {
   1.328 +        nsINode *root = Root();
   1.329 +        NS_RELEASE(root);
   1.330 +    }
   1.331 +}
   1.332 +
   1.333 +/* static */
   1.334 +bool
   1.335 +txXPathNodeUtils::getAttr(const txXPathNode& aNode, nsIAtom* aLocalName,
   1.336 +                          int32_t aNSID, nsAString& aValue)
   1.337 +{
   1.338 +    if (aNode.isDocument() || aNode.isAttribute()) {
   1.339 +        return false;
   1.340 +    }
   1.341 +
   1.342 +    return aNode.Content()->GetAttr(aNSID, aLocalName, aValue);
   1.343 +}
   1.344 +
   1.345 +/* static */
   1.346 +already_AddRefed<nsIAtom>
   1.347 +txXPathNodeUtils::getLocalName(const txXPathNode& aNode)
   1.348 +{
   1.349 +    if (aNode.isDocument()) {
   1.350 +        return nullptr;
   1.351 +    }
   1.352 +
   1.353 +    if (aNode.isContent()) {
   1.354 +        if (aNode.mNode->IsElement()) {
   1.355 +            nsCOMPtr<nsIAtom> localName = aNode.Content()->Tag();
   1.356 +            return localName.forget();
   1.357 +        }
   1.358 +
   1.359 +        if (aNode.mNode->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
   1.360 +            nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mNode);
   1.361 +            nsAutoString target;
   1.362 +            node->GetNodeName(target);
   1.363 +
   1.364 +            return NS_NewAtom(target);
   1.365 +        }
   1.366 +
   1.367 +        return nullptr;
   1.368 +    }
   1.369 +
   1.370 +    nsCOMPtr<nsIAtom> localName = aNode.Content()->
   1.371 +        GetAttrNameAt(aNode.mIndex)->LocalName();
   1.372 +
   1.373 +    return localName.forget();
   1.374 +}
   1.375 +
   1.376 +nsIAtom*
   1.377 +txXPathNodeUtils::getPrefix(const txXPathNode& aNode)
   1.378 +{
   1.379 +    if (aNode.isDocument()) {
   1.380 +        return nullptr;
   1.381 +    }
   1.382 +
   1.383 +    if (aNode.isContent()) {
   1.384 +        // All other nsIContent node types but elements have a null prefix
   1.385 +        // which is what we want here.
   1.386 +        return aNode.Content()->NodeInfo()->GetPrefixAtom();
   1.387 +    }
   1.388 +
   1.389 +    return aNode.Content()->GetAttrNameAt(aNode.mIndex)->GetPrefix();
   1.390 +}
   1.391 +
   1.392 +/* static */
   1.393 +void
   1.394 +txXPathNodeUtils::getLocalName(const txXPathNode& aNode, nsAString& aLocalName)
   1.395 +{
   1.396 +    if (aNode.isDocument()) {
   1.397 +        aLocalName.Truncate();
   1.398 +
   1.399 +        return;
   1.400 +    }
   1.401 +
   1.402 +    if (aNode.isContent()) {
   1.403 +        if (aNode.mNode->IsElement()) {
   1.404 +            nsINodeInfo* nodeInfo = aNode.Content()->NodeInfo();
   1.405 +            nodeInfo->GetName(aLocalName);
   1.406 +            return;
   1.407 +        }
   1.408 +
   1.409 +        if (aNode.mNode->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
   1.410 +            // PIs don't have a nodeinfo but do have a name
   1.411 +            nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mNode);
   1.412 +            node->GetNodeName(aLocalName);
   1.413 +
   1.414 +            return;
   1.415 +        }
   1.416 +
   1.417 +        aLocalName.Truncate();
   1.418 +
   1.419 +        return;
   1.420 +    }
   1.421 +
   1.422 +    aNode.Content()->GetAttrNameAt(aNode.mIndex)->LocalName()->
   1.423 +      ToString(aLocalName);
   1.424 +
   1.425 +    // Check for html
   1.426 +    if (aNode.Content()->NodeInfo()->NamespaceEquals(kNameSpaceID_None) &&
   1.427 +        aNode.Content()->IsHTML()) {
   1.428 +        nsContentUtils::ASCIIToUpper(aLocalName);
   1.429 +    }
   1.430 +}
   1.431 +
   1.432 +/* static */
   1.433 +void
   1.434 +txXPathNodeUtils::getNodeName(const txXPathNode& aNode, nsAString& aName)
   1.435 +{
   1.436 +    if (aNode.isDocument()) {
   1.437 +        aName.Truncate();
   1.438 +
   1.439 +        return;
   1.440 +    }
   1.441 +
   1.442 +    if (aNode.isContent()) {
   1.443 +        // Elements and PIs have a name
   1.444 +        if (aNode.mNode->IsElement() ||
   1.445 +            aNode.mNode->NodeType() ==
   1.446 +            nsIDOMNode::PROCESSING_INSTRUCTION_NODE) {
   1.447 +            aName = aNode.Content()->NodeName();
   1.448 +            return;
   1.449 +        }
   1.450 +
   1.451 +        aName.Truncate();
   1.452 +
   1.453 +        return;
   1.454 +    }
   1.455 +
   1.456 +    aNode.Content()->GetAttrNameAt(aNode.mIndex)->GetQualifiedName(aName);
   1.457 +}
   1.458 +
   1.459 +/* static */
   1.460 +int32_t
   1.461 +txXPathNodeUtils::getNamespaceID(const txXPathNode& aNode)
   1.462 +{
   1.463 +    if (aNode.isDocument()) {
   1.464 +        return kNameSpaceID_None;
   1.465 +    }
   1.466 +
   1.467 +    if (aNode.isContent()) {
   1.468 +        return aNode.Content()->GetNameSpaceID();
   1.469 +    }
   1.470 +
   1.471 +    return aNode.Content()->GetAttrNameAt(aNode.mIndex)->NamespaceID();
   1.472 +}
   1.473 +
   1.474 +/* static */
   1.475 +void
   1.476 +txXPathNodeUtils::getNamespaceURI(const txXPathNode& aNode, nsAString& aURI)
   1.477 +{
   1.478 +    nsContentUtils::NameSpaceManager()->GetNameSpaceURI(getNamespaceID(aNode), aURI);
   1.479 +}
   1.480 +
   1.481 +/* static */
   1.482 +uint16_t
   1.483 +txXPathNodeUtils::getNodeType(const txXPathNode& aNode)
   1.484 +{
   1.485 +    if (aNode.isDocument()) {
   1.486 +        return txXPathNodeType::DOCUMENT_NODE;
   1.487 +    }
   1.488 +
   1.489 +    if (aNode.isContent()) {
   1.490 +        return aNode.mNode->NodeType();
   1.491 +    }
   1.492 +
   1.493 +    return txXPathNodeType::ATTRIBUTE_NODE;
   1.494 +}
   1.495 +
   1.496 +/* static */
   1.497 +void
   1.498 +txXPathNodeUtils::appendNodeValue(const txXPathNode& aNode, nsAString& aResult)
   1.499 +{
   1.500 +    if (aNode.isAttribute()) {
   1.501 +        const nsAttrName* name = aNode.Content()->GetAttrNameAt(aNode.mIndex);
   1.502 +
   1.503 +        if (aResult.IsEmpty()) {
   1.504 +            aNode.Content()->GetAttr(name->NamespaceID(), name->LocalName(),
   1.505 +                                     aResult);
   1.506 +        }
   1.507 +        else {
   1.508 +            nsAutoString result;
   1.509 +            aNode.Content()->GetAttr(name->NamespaceID(), name->LocalName(),
   1.510 +                                     result);
   1.511 +            aResult.Append(result);
   1.512 +        }
   1.513 +
   1.514 +        return;
   1.515 +    }
   1.516 +
   1.517 +    if (aNode.isDocument() ||
   1.518 +        aNode.mNode->IsElement() ||
   1.519 +        aNode.mNode->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT)) {
   1.520 +        nsContentUtils::AppendNodeTextContent(aNode.mNode, true, aResult,
   1.521 +                                              mozilla::fallible_t());
   1.522 +
   1.523 +        return;
   1.524 +    }
   1.525 +
   1.526 +    aNode.Content()->AppendTextTo(aResult);
   1.527 +}
   1.528 +
   1.529 +/* static */
   1.530 +bool
   1.531 +txXPathNodeUtils::isWhitespace(const txXPathNode& aNode)
   1.532 +{
   1.533 +    NS_ASSERTION(aNode.isContent() && isText(aNode), "Wrong type!");
   1.534 +
   1.535 +    return aNode.Content()->TextIsOnlyWhitespace();
   1.536 +}
   1.537 +
   1.538 +/* static */
   1.539 +txXPathNode*
   1.540 +txXPathNodeUtils::getOwnerDocument(const txXPathNode& aNode)
   1.541 +{
   1.542 +    return new txXPathNode(aNode.mNode->OwnerDoc());
   1.543 +}
   1.544 +
   1.545 +const char gPrintfFmt[] = "id0x%p";
   1.546 +const char gPrintfFmtAttr[] = "id0x%p-%010i";
   1.547 +
   1.548 +/* static */
   1.549 +nsresult
   1.550 +txXPathNodeUtils::getXSLTId(const txXPathNode& aNode,
   1.551 +                            const txXPathNode& aBase,
   1.552 +                            nsAString& aResult)
   1.553 +{
   1.554 +    uintptr_t nodeid = ((uintptr_t)aNode.mNode) - ((uintptr_t)aBase.mNode);
   1.555 +    if (!aNode.isAttribute()) {
   1.556 +        CopyASCIItoUTF16(nsPrintfCString(gPrintfFmt, nodeid),
   1.557 +                         aResult);
   1.558 +    }
   1.559 +    else {
   1.560 +        CopyASCIItoUTF16(nsPrintfCString(gPrintfFmtAttr,
   1.561 +                                         nodeid, aNode.mIndex), aResult);
   1.562 +    }
   1.563 +
   1.564 +    return NS_OK;
   1.565 +}
   1.566 +
   1.567 +/* static */
   1.568 +void
   1.569 +txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsAString& aURI)
   1.570 +{
   1.571 +    aNode.mNode->GetBaseURI(aURI);
   1.572 +}
   1.573 +
   1.574 +/* static */
   1.575 +int
   1.576 +txXPathNodeUtils::comparePosition(const txXPathNode& aNode,
   1.577 +                                  const txXPathNode& aOtherNode)
   1.578 +{
   1.579 +    // First check for equal nodes or attribute-nodes on the same element.
   1.580 +    if (aNode.mNode == aOtherNode.mNode) {
   1.581 +        if (aNode.mIndex == aOtherNode.mIndex) {
   1.582 +            return 0;
   1.583 +        }
   1.584 +
   1.585 +        NS_ASSERTION(!aNode.isDocument() && !aOtherNode.isDocument(),
   1.586 +                     "documents should always have a set index");
   1.587 +
   1.588 +        if (aNode.isContent() || (!aOtherNode.isContent() &&
   1.589 +                                  aNode.mIndex < aOtherNode.mIndex)) {
   1.590 +            return -1;
   1.591 +        }
   1.592 +
   1.593 +        return 1;
   1.594 +    }
   1.595 +
   1.596 +    // Get document for both nodes.
   1.597 +    nsIDocument* document = aNode.mNode->GetCurrentDoc();
   1.598 +    nsIDocument* otherDocument = aOtherNode.mNode->GetCurrentDoc();
   1.599 +
   1.600 +    // If the nodes have different current documents, compare the document
   1.601 +    // pointers.
   1.602 +    if (document != otherDocument) {
   1.603 +        return document < otherDocument ? -1 : 1;
   1.604 +    }
   1.605 +
   1.606 +    // Now either both nodes are in orphan trees, or they are both in the
   1.607 +    // same tree.
   1.608 +
   1.609 +    // Get parents up the tree.
   1.610 +    nsAutoTArray<nsINode*, 8> parents, otherParents;
   1.611 +    nsINode* node = aNode.mNode;
   1.612 +    nsINode* otherNode = aOtherNode.mNode;
   1.613 +    nsINode* parent, *otherParent;
   1.614 +    while (node && otherNode) {
   1.615 +        parent = node->GetParentNode();
   1.616 +        otherParent = otherNode->GetParentNode();
   1.617 +
   1.618 +        // Hopefully this is a common case.
   1.619 +        if (parent == otherParent) {
   1.620 +            if (!parent) {
   1.621 +                // Both node and otherNode are root nodes in respective orphan
   1.622 +                // tree.
   1.623 +                return node < otherNode ? -1 : 1;
   1.624 +            }
   1.625 +
   1.626 +            return parent->IndexOf(node) < parent->IndexOf(otherNode) ?
   1.627 +                   -1 : 1;
   1.628 +        }
   1.629 +
   1.630 +        parents.AppendElement(node);
   1.631 +        otherParents.AppendElement(otherNode);
   1.632 +        node = parent;
   1.633 +        otherNode = otherParent;
   1.634 +    }
   1.635 +
   1.636 +    while (node) {
   1.637 +        parents.AppendElement(node);
   1.638 +        node = node->GetParentNode();
   1.639 +    }
   1.640 +    while (otherNode) {
   1.641 +        otherParents.AppendElement(otherNode);
   1.642 +        otherNode = otherNode->GetParentNode();
   1.643 +    }
   1.644 +
   1.645 +    // Walk back down along the parent-chains until we find where they split.
   1.646 +    int32_t total = parents.Length() - 1;
   1.647 +    int32_t otherTotal = otherParents.Length() - 1;
   1.648 +    NS_ASSERTION(total != otherTotal, "Can't have same number of parents");
   1.649 +
   1.650 +    int32_t lastIndex = std::min(total, otherTotal);
   1.651 +    int32_t i;
   1.652 +    parent = nullptr;
   1.653 +    for (i = 0; i <= lastIndex; ++i) {
   1.654 +        node = parents.ElementAt(total - i);
   1.655 +        otherNode = otherParents.ElementAt(otherTotal - i);
   1.656 +        if (node != otherNode) {
   1.657 +            if (!parent) {
   1.658 +                // The two nodes are in different orphan subtrees.
   1.659 +                NS_ASSERTION(i == 0, "this shouldn't happen");
   1.660 +                return node < otherNode ? -1 : 1;
   1.661 +            }
   1.662 +
   1.663 +            int32_t index = parent->IndexOf(node);
   1.664 +            int32_t otherIndex = parent->IndexOf(otherNode);
   1.665 +            NS_ASSERTION(index != otherIndex && index >= 0 && otherIndex >= 0,
   1.666 +                         "invalid index in compareTreePosition");
   1.667 +
   1.668 +            return index < otherIndex ? -1 : 1;
   1.669 +        }
   1.670 +
   1.671 +        parent = node;
   1.672 +    }
   1.673 +
   1.674 +    // One node is a descendant of the other. The one with the shortest
   1.675 +    // parent-chain is first in the document.
   1.676 +    return total < otherTotal ? -1 : 1;
   1.677 +}
   1.678 +
   1.679 +/* static */
   1.680 +txXPathNode*
   1.681 +txXPathNativeNode::createXPathNode(nsIContent* aContent, bool aKeepRootAlive)
   1.682 +{
   1.683 +    nsINode* root = aKeepRootAlive ? txXPathNode::RootOf(aContent) : nullptr;
   1.684 +
   1.685 +    return new txXPathNode(aContent, txXPathNode::eContent, root);
   1.686 +}
   1.687 +
   1.688 +/* static */
   1.689 +txXPathNode*
   1.690 +txXPathNativeNode::createXPathNode(nsIDOMNode* aNode, bool aKeepRootAlive)
   1.691 +{
   1.692 +    uint16_t nodeType;
   1.693 +    aNode->GetNodeType(&nodeType);
   1.694 +
   1.695 +    if (nodeType == nsIDOMNode::ATTRIBUTE_NODE) {
   1.696 +        nsCOMPtr<nsIAttribute> attr = do_QueryInterface(aNode);
   1.697 +        NS_ASSERTION(attr, "doesn't implement nsIAttribute");
   1.698 +
   1.699 +        nsINodeInfo *nodeInfo = attr->NodeInfo();
   1.700 +        mozilla::dom::Element* parent =
   1.701 +          static_cast<Attr*>(attr.get())->GetElement();
   1.702 +        if (!parent) {
   1.703 +            return nullptr;
   1.704 +        }
   1.705 +
   1.706 +        nsINode* root = aKeepRootAlive ? txXPathNode::RootOf(parent) : nullptr;
   1.707 +
   1.708 +        uint32_t i, total = parent->GetAttrCount();
   1.709 +        for (i = 0; i < total; ++i) {
   1.710 +            const nsAttrName* name = parent->GetAttrNameAt(i);
   1.711 +            if (nodeInfo->Equals(name->LocalName(), name->NamespaceID())) {
   1.712 +                return new txXPathNode(parent, i, root);
   1.713 +            }
   1.714 +        }
   1.715 +
   1.716 +        NS_ERROR("Couldn't find the attribute in its parent!");
   1.717 +
   1.718 +        return nullptr;
   1.719 +    }
   1.720 +
   1.721 +    nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   1.722 +    uint32_t index;
   1.723 +    nsINode* root = aKeepRootAlive ? node.get() : nullptr;
   1.724 +
   1.725 +    if (nodeType == nsIDOMNode::DOCUMENT_NODE) {
   1.726 +        index = txXPathNode::eDocument;
   1.727 +    }
   1.728 +    else {
   1.729 +        index = txXPathNode::eContent;
   1.730 +        if (root) {
   1.731 +            root = txXPathNode::RootOf(root);
   1.732 +        }
   1.733 +    }
   1.734 +
   1.735 +    return new txXPathNode(node, index, root);
   1.736 +}
   1.737 +
   1.738 +/* static */
   1.739 +txXPathNode*
   1.740 +txXPathNativeNode::createXPathNode(nsIDOMDocument* aDocument)
   1.741 +{
   1.742 +    nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument);
   1.743 +    return new txXPathNode(document);
   1.744 +}
   1.745 +
   1.746 +/* static */
   1.747 +nsresult
   1.748 +txXPathNativeNode::getNode(const txXPathNode& aNode, nsIDOMNode** aResult)
   1.749 +{
   1.750 +    if (!aNode.isAttribute()) {
   1.751 +        return CallQueryInterface(aNode.mNode, aResult);
   1.752 +    }
   1.753 +
   1.754 +    const nsAttrName* name = aNode.Content()->GetAttrNameAt(aNode.mIndex);
   1.755 +
   1.756 +    nsAutoString namespaceURI;
   1.757 +    nsContentUtils::NameSpaceManager()->GetNameSpaceURI(name->NamespaceID(), namespaceURI);
   1.758 +
   1.759 +    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode.mNode);
   1.760 +    nsCOMPtr<nsIDOMAttr> attr;
   1.761 +    element->GetAttributeNodeNS(namespaceURI,
   1.762 +                                nsDependentAtomString(name->LocalName()),
   1.763 +                                getter_AddRefs(attr));
   1.764 +
   1.765 +    return CallQueryInterface(attr, aResult);
   1.766 +}
   1.767 +
   1.768 +/* static */
   1.769 +nsIContent*
   1.770 +txXPathNativeNode::getContent(const txXPathNode& aNode)
   1.771 +{
   1.772 +    NS_ASSERTION(aNode.isContent(),
   1.773 +                 "Only call getContent on nsIContent wrappers!");
   1.774 +    return aNode.Content();
   1.775 +}
   1.776 +
   1.777 +/* static */
   1.778 +nsIDocument*
   1.779 +txXPathNativeNode::getDocument(const txXPathNode& aNode)
   1.780 +{
   1.781 +    NS_ASSERTION(aNode.isDocument(),
   1.782 +                 "Only call getDocument on nsIDocument wrappers!");
   1.783 +    return aNode.Document();
   1.784 +}

mercurial