dom/xslt/xpath/txMozillaXPathTreeWalker.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "txXPathTreeWalker.h"
michael@0 7 #include "nsIAtom.h"
michael@0 8 #include "nsIAttribute.h"
michael@0 9 #include "nsIDOMAttr.h"
michael@0 10 #include "nsIDOMDocument.h"
michael@0 11 #include "nsIDOMNode.h"
michael@0 12 #include "nsIDOMElement.h"
michael@0 13 #include "nsIDOMProcessingInstruction.h"
michael@0 14 #include "nsINodeInfo.h"
michael@0 15 #include "nsPrintfCString.h"
michael@0 16 #include "nsReadableUtils.h"
michael@0 17 #include "nsString.h"
michael@0 18 #include "nsTextFragment.h"
michael@0 19 #include "txXMLUtils.h"
michael@0 20 #include "txLog.h"
michael@0 21 #include "nsUnicharUtils.h"
michael@0 22 #include "nsAttrName.h"
michael@0 23 #include "nsTArray.h"
michael@0 24 #include "mozilla/dom/Attr.h"
michael@0 25 #include "mozilla/dom/Element.h"
michael@0 26 #include <stdint.h>
michael@0 27 #include <algorithm>
michael@0 28
michael@0 29 using mozilla::dom::Attr;
michael@0 30
michael@0 31 const uint32_t kUnknownIndex = uint32_t(-1);
michael@0 32
michael@0 33 txXPathTreeWalker::txXPathTreeWalker(const txXPathTreeWalker& aOther)
michael@0 34 : mPosition(aOther.mPosition),
michael@0 35 mCurrentIndex(aOther.mCurrentIndex)
michael@0 36 {
michael@0 37 }
michael@0 38
michael@0 39 txXPathTreeWalker::txXPathTreeWalker(const txXPathNode& aNode)
michael@0 40 : mPosition(aNode),
michael@0 41 mCurrentIndex(kUnknownIndex)
michael@0 42 {
michael@0 43 }
michael@0 44
michael@0 45 void
michael@0 46 txXPathTreeWalker::moveToRoot()
michael@0 47 {
michael@0 48 if (mPosition.isDocument()) {
michael@0 49 return;
michael@0 50 }
michael@0 51
michael@0 52 nsIDocument* root = mPosition.mNode->GetCurrentDoc();
michael@0 53 if (root) {
michael@0 54 mPosition.mIndex = txXPathNode::eDocument;
michael@0 55 mPosition.mNode = root;
michael@0 56 }
michael@0 57 else {
michael@0 58 nsINode *rootNode = mPosition.Root();
michael@0 59
michael@0 60 NS_ASSERTION(rootNode->IsNodeOfType(nsINode::eCONTENT),
michael@0 61 "root of subtree wasn't an nsIContent");
michael@0 62
michael@0 63 mPosition.mIndex = txXPathNode::eContent;
michael@0 64 mPosition.mNode = rootNode;
michael@0 65 }
michael@0 66
michael@0 67 mCurrentIndex = kUnknownIndex;
michael@0 68 mDescendants.Clear();
michael@0 69 }
michael@0 70
michael@0 71 bool
michael@0 72 txXPathTreeWalker::moveToElementById(const nsAString& aID)
michael@0 73 {
michael@0 74 if (aID.IsEmpty()) {
michael@0 75 return false;
michael@0 76 }
michael@0 77
michael@0 78 nsIDocument* doc = mPosition.mNode->GetCurrentDoc();
michael@0 79
michael@0 80 nsCOMPtr<nsIContent> content;
michael@0 81 if (doc) {
michael@0 82 content = doc->GetElementById(aID);
michael@0 83 }
michael@0 84 else {
michael@0 85 // We're in a disconnected subtree, search only that subtree.
michael@0 86 nsINode *rootNode = mPosition.Root();
michael@0 87
michael@0 88 NS_ASSERTION(rootNode->IsNodeOfType(nsINode::eCONTENT),
michael@0 89 "root of subtree wasn't an nsIContent");
michael@0 90
michael@0 91 content = nsContentUtils::MatchElementId(
michael@0 92 static_cast<nsIContent*>(rootNode), aID);
michael@0 93 }
michael@0 94
michael@0 95 if (!content) {
michael@0 96 return false;
michael@0 97 }
michael@0 98
michael@0 99 mPosition.mIndex = txXPathNode::eContent;
michael@0 100 mPosition.mNode = content;
michael@0 101 mCurrentIndex = kUnknownIndex;
michael@0 102 mDescendants.Clear();
michael@0 103
michael@0 104 return true;
michael@0 105 }
michael@0 106
michael@0 107 bool
michael@0 108 txXPathTreeWalker::moveToFirstAttribute()
michael@0 109 {
michael@0 110 if (!mPosition.isContent()) {
michael@0 111 return false;
michael@0 112 }
michael@0 113
michael@0 114 return moveToValidAttribute(0);
michael@0 115 }
michael@0 116
michael@0 117 bool
michael@0 118 txXPathTreeWalker::moveToNextAttribute()
michael@0 119 {
michael@0 120 // XXX an assertion should be enough here with the current code
michael@0 121 if (!mPosition.isAttribute()) {
michael@0 122 return false;
michael@0 123 }
michael@0 124
michael@0 125 return moveToValidAttribute(mPosition.mIndex + 1);
michael@0 126 }
michael@0 127
michael@0 128 bool
michael@0 129 txXPathTreeWalker::moveToValidAttribute(uint32_t aStartIndex)
michael@0 130 {
michael@0 131 NS_ASSERTION(!mPosition.isDocument(), "documents doesn't have attrs");
michael@0 132
michael@0 133 uint32_t total = mPosition.Content()->GetAttrCount();
michael@0 134 if (aStartIndex >= total) {
michael@0 135 return false;
michael@0 136 }
michael@0 137
michael@0 138 uint32_t index;
michael@0 139 for (index = aStartIndex; index < total; ++index) {
michael@0 140 const nsAttrName* name = mPosition.Content()->GetAttrNameAt(index);
michael@0 141
michael@0 142 // We need to ignore XMLNS attributes.
michael@0 143 if (name->NamespaceID() != kNameSpaceID_XMLNS) {
michael@0 144 mPosition.mIndex = index;
michael@0 145
michael@0 146 return true;
michael@0 147 }
michael@0 148 }
michael@0 149 return false;
michael@0 150 }
michael@0 151
michael@0 152 bool
michael@0 153 txXPathTreeWalker::moveToNamedAttribute(nsIAtom* aLocalName, int32_t aNSID)
michael@0 154 {
michael@0 155 if (!mPosition.isContent()) {
michael@0 156 return false;
michael@0 157 }
michael@0 158
michael@0 159 const nsAttrName* name;
michael@0 160 uint32_t i;
michael@0 161 for (i = 0; (name = mPosition.Content()->GetAttrNameAt(i)); ++i) {
michael@0 162 if (name->Equals(aLocalName, aNSID)) {
michael@0 163 mPosition.mIndex = i;
michael@0 164
michael@0 165 return true;
michael@0 166 }
michael@0 167 }
michael@0 168 return false;
michael@0 169 }
michael@0 170
michael@0 171 bool
michael@0 172 txXPathTreeWalker::moveToFirstChild()
michael@0 173 {
michael@0 174 if (mPosition.isAttribute()) {
michael@0 175 return false;
michael@0 176 }
michael@0 177
michael@0 178 NS_ASSERTION(!mPosition.isDocument() ||
michael@0 179 (mCurrentIndex == kUnknownIndex && mDescendants.IsEmpty()),
michael@0 180 "we shouldn't have any position info at the document");
michael@0 181 NS_ASSERTION(mCurrentIndex != kUnknownIndex || mDescendants.IsEmpty(),
michael@0 182 "Index should be known if parents index are");
michael@0 183
michael@0 184 nsIContent* child = mPosition.mNode->GetFirstChild();
michael@0 185 if (!child) {
michael@0 186 return false;
michael@0 187 }
michael@0 188 mPosition.mIndex = txXPathNode::eContent;
michael@0 189 mPosition.mNode = child;
michael@0 190
michael@0 191 if (mCurrentIndex != kUnknownIndex &&
michael@0 192 !mDescendants.AppendValue(mCurrentIndex)) {
michael@0 193 mDescendants.Clear();
michael@0 194 }
michael@0 195 mCurrentIndex = 0;
michael@0 196
michael@0 197 return true;
michael@0 198 }
michael@0 199
michael@0 200 bool
michael@0 201 txXPathTreeWalker::moveToLastChild()
michael@0 202 {
michael@0 203 if (mPosition.isAttribute()) {
michael@0 204 return false;
michael@0 205 }
michael@0 206
michael@0 207 NS_ASSERTION(!mPosition.isDocument() ||
michael@0 208 (mCurrentIndex == kUnknownIndex && mDescendants.IsEmpty()),
michael@0 209 "we shouldn't have any position info at the document");
michael@0 210 NS_ASSERTION(mCurrentIndex != kUnknownIndex || mDescendants.IsEmpty(),
michael@0 211 "Index should be known if parents index are");
michael@0 212
michael@0 213 uint32_t total = mPosition.mNode->GetChildCount();
michael@0 214 if (!total) {
michael@0 215 return false;
michael@0 216 }
michael@0 217 mPosition.mNode = mPosition.mNode->GetLastChild();
michael@0 218
michael@0 219 if (mCurrentIndex != kUnknownIndex &&
michael@0 220 !mDescendants.AppendValue(mCurrentIndex)) {
michael@0 221 mDescendants.Clear();
michael@0 222 }
michael@0 223 mCurrentIndex = total - 1;
michael@0 224
michael@0 225 return true;
michael@0 226 }
michael@0 227
michael@0 228 bool
michael@0 229 txXPathTreeWalker::moveToNextSibling()
michael@0 230 {
michael@0 231 if (!mPosition.isContent()) {
michael@0 232 return false;
michael@0 233 }
michael@0 234
michael@0 235 return moveToSibling(1);
michael@0 236 }
michael@0 237
michael@0 238 bool
michael@0 239 txXPathTreeWalker::moveToPreviousSibling()
michael@0 240 {
michael@0 241 if (!mPosition.isContent()) {
michael@0 242 return false;
michael@0 243 }
michael@0 244
michael@0 245 return moveToSibling(-1);
michael@0 246 }
michael@0 247
michael@0 248 bool
michael@0 249 txXPathTreeWalker::moveToParent()
michael@0 250 {
michael@0 251 if (mPosition.isDocument()) {
michael@0 252 return false;
michael@0 253 }
michael@0 254
michael@0 255 if (mPosition.isAttribute()) {
michael@0 256 mPosition.mIndex = txXPathNode::eContent;
michael@0 257
michael@0 258 return true;
michael@0 259 }
michael@0 260
michael@0 261 nsINode* parent = mPosition.mNode->GetParentNode();
michael@0 262 if (!parent) {
michael@0 263 return false;
michael@0 264 }
michael@0 265
michael@0 266 uint32_t count = mDescendants.Length();
michael@0 267 if (count) {
michael@0 268 mCurrentIndex = mDescendants.ValueAt(--count);
michael@0 269 mDescendants.RemoveValueAt(count);
michael@0 270 }
michael@0 271 else {
michael@0 272 mCurrentIndex = kUnknownIndex;
michael@0 273 }
michael@0 274
michael@0 275 mPosition.mIndex = mPosition.mNode->GetParent() ?
michael@0 276 txXPathNode::eContent : txXPathNode::eDocument;
michael@0 277 mPosition.mNode = parent;
michael@0 278
michael@0 279 return true;
michael@0 280 }
michael@0 281
michael@0 282 bool
michael@0 283 txXPathTreeWalker::moveToSibling(int32_t aDir)
michael@0 284 {
michael@0 285 NS_ASSERTION(mPosition.isContent(),
michael@0 286 "moveToSibling should only be called for content");
michael@0 287
michael@0 288 nsINode* parent = mPosition.mNode->GetParentNode();
michael@0 289 if (!parent) {
michael@0 290 return false;
michael@0 291 }
michael@0 292 if (mCurrentIndex == kUnknownIndex) {
michael@0 293 mCurrentIndex = parent->IndexOf(mPosition.mNode);
michael@0 294 }
michael@0 295
michael@0 296 // if mCurrentIndex is 0 we rely on GetChildAt returning null for an
michael@0 297 // index of uint32_t(-1).
michael@0 298 uint32_t newIndex = mCurrentIndex + aDir;
michael@0 299 nsIContent* newChild = parent->GetChildAt(newIndex);
michael@0 300 if (!newChild) {
michael@0 301 return false;
michael@0 302 }
michael@0 303
michael@0 304 mPosition.mNode = newChild;
michael@0 305 mCurrentIndex = newIndex;
michael@0 306
michael@0 307 return true;
michael@0 308 }
michael@0 309
michael@0 310 txXPathNode::txXPathNode(const txXPathNode& aNode)
michael@0 311 : mNode(aNode.mNode),
michael@0 312 mRefCountRoot(aNode.mRefCountRoot),
michael@0 313 mIndex(aNode.mIndex)
michael@0 314 {
michael@0 315 MOZ_COUNT_CTOR(txXPathNode);
michael@0 316 if (mRefCountRoot) {
michael@0 317 NS_ADDREF(Root());
michael@0 318 }
michael@0 319 }
michael@0 320
michael@0 321 txXPathNode::~txXPathNode()
michael@0 322 {
michael@0 323 MOZ_COUNT_DTOR(txXPathNode);
michael@0 324 if (mRefCountRoot) {
michael@0 325 nsINode *root = Root();
michael@0 326 NS_RELEASE(root);
michael@0 327 }
michael@0 328 }
michael@0 329
michael@0 330 /* static */
michael@0 331 bool
michael@0 332 txXPathNodeUtils::getAttr(const txXPathNode& aNode, nsIAtom* aLocalName,
michael@0 333 int32_t aNSID, nsAString& aValue)
michael@0 334 {
michael@0 335 if (aNode.isDocument() || aNode.isAttribute()) {
michael@0 336 return false;
michael@0 337 }
michael@0 338
michael@0 339 return aNode.Content()->GetAttr(aNSID, aLocalName, aValue);
michael@0 340 }
michael@0 341
michael@0 342 /* static */
michael@0 343 already_AddRefed<nsIAtom>
michael@0 344 txXPathNodeUtils::getLocalName(const txXPathNode& aNode)
michael@0 345 {
michael@0 346 if (aNode.isDocument()) {
michael@0 347 return nullptr;
michael@0 348 }
michael@0 349
michael@0 350 if (aNode.isContent()) {
michael@0 351 if (aNode.mNode->IsElement()) {
michael@0 352 nsCOMPtr<nsIAtom> localName = aNode.Content()->Tag();
michael@0 353 return localName.forget();
michael@0 354 }
michael@0 355
michael@0 356 if (aNode.mNode->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
michael@0 357 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mNode);
michael@0 358 nsAutoString target;
michael@0 359 node->GetNodeName(target);
michael@0 360
michael@0 361 return NS_NewAtom(target);
michael@0 362 }
michael@0 363
michael@0 364 return nullptr;
michael@0 365 }
michael@0 366
michael@0 367 nsCOMPtr<nsIAtom> localName = aNode.Content()->
michael@0 368 GetAttrNameAt(aNode.mIndex)->LocalName();
michael@0 369
michael@0 370 return localName.forget();
michael@0 371 }
michael@0 372
michael@0 373 nsIAtom*
michael@0 374 txXPathNodeUtils::getPrefix(const txXPathNode& aNode)
michael@0 375 {
michael@0 376 if (aNode.isDocument()) {
michael@0 377 return nullptr;
michael@0 378 }
michael@0 379
michael@0 380 if (aNode.isContent()) {
michael@0 381 // All other nsIContent node types but elements have a null prefix
michael@0 382 // which is what we want here.
michael@0 383 return aNode.Content()->NodeInfo()->GetPrefixAtom();
michael@0 384 }
michael@0 385
michael@0 386 return aNode.Content()->GetAttrNameAt(aNode.mIndex)->GetPrefix();
michael@0 387 }
michael@0 388
michael@0 389 /* static */
michael@0 390 void
michael@0 391 txXPathNodeUtils::getLocalName(const txXPathNode& aNode, nsAString& aLocalName)
michael@0 392 {
michael@0 393 if (aNode.isDocument()) {
michael@0 394 aLocalName.Truncate();
michael@0 395
michael@0 396 return;
michael@0 397 }
michael@0 398
michael@0 399 if (aNode.isContent()) {
michael@0 400 if (aNode.mNode->IsElement()) {
michael@0 401 nsINodeInfo* nodeInfo = aNode.Content()->NodeInfo();
michael@0 402 nodeInfo->GetName(aLocalName);
michael@0 403 return;
michael@0 404 }
michael@0 405
michael@0 406 if (aNode.mNode->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
michael@0 407 // PIs don't have a nodeinfo but do have a name
michael@0 408 nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aNode.mNode);
michael@0 409 node->GetNodeName(aLocalName);
michael@0 410
michael@0 411 return;
michael@0 412 }
michael@0 413
michael@0 414 aLocalName.Truncate();
michael@0 415
michael@0 416 return;
michael@0 417 }
michael@0 418
michael@0 419 aNode.Content()->GetAttrNameAt(aNode.mIndex)->LocalName()->
michael@0 420 ToString(aLocalName);
michael@0 421
michael@0 422 // Check for html
michael@0 423 if (aNode.Content()->NodeInfo()->NamespaceEquals(kNameSpaceID_None) &&
michael@0 424 aNode.Content()->IsHTML()) {
michael@0 425 nsContentUtils::ASCIIToUpper(aLocalName);
michael@0 426 }
michael@0 427 }
michael@0 428
michael@0 429 /* static */
michael@0 430 void
michael@0 431 txXPathNodeUtils::getNodeName(const txXPathNode& aNode, nsAString& aName)
michael@0 432 {
michael@0 433 if (aNode.isDocument()) {
michael@0 434 aName.Truncate();
michael@0 435
michael@0 436 return;
michael@0 437 }
michael@0 438
michael@0 439 if (aNode.isContent()) {
michael@0 440 // Elements and PIs have a name
michael@0 441 if (aNode.mNode->IsElement() ||
michael@0 442 aNode.mNode->NodeType() ==
michael@0 443 nsIDOMNode::PROCESSING_INSTRUCTION_NODE) {
michael@0 444 aName = aNode.Content()->NodeName();
michael@0 445 return;
michael@0 446 }
michael@0 447
michael@0 448 aName.Truncate();
michael@0 449
michael@0 450 return;
michael@0 451 }
michael@0 452
michael@0 453 aNode.Content()->GetAttrNameAt(aNode.mIndex)->GetQualifiedName(aName);
michael@0 454 }
michael@0 455
michael@0 456 /* static */
michael@0 457 int32_t
michael@0 458 txXPathNodeUtils::getNamespaceID(const txXPathNode& aNode)
michael@0 459 {
michael@0 460 if (aNode.isDocument()) {
michael@0 461 return kNameSpaceID_None;
michael@0 462 }
michael@0 463
michael@0 464 if (aNode.isContent()) {
michael@0 465 return aNode.Content()->GetNameSpaceID();
michael@0 466 }
michael@0 467
michael@0 468 return aNode.Content()->GetAttrNameAt(aNode.mIndex)->NamespaceID();
michael@0 469 }
michael@0 470
michael@0 471 /* static */
michael@0 472 void
michael@0 473 txXPathNodeUtils::getNamespaceURI(const txXPathNode& aNode, nsAString& aURI)
michael@0 474 {
michael@0 475 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(getNamespaceID(aNode), aURI);
michael@0 476 }
michael@0 477
michael@0 478 /* static */
michael@0 479 uint16_t
michael@0 480 txXPathNodeUtils::getNodeType(const txXPathNode& aNode)
michael@0 481 {
michael@0 482 if (aNode.isDocument()) {
michael@0 483 return txXPathNodeType::DOCUMENT_NODE;
michael@0 484 }
michael@0 485
michael@0 486 if (aNode.isContent()) {
michael@0 487 return aNode.mNode->NodeType();
michael@0 488 }
michael@0 489
michael@0 490 return txXPathNodeType::ATTRIBUTE_NODE;
michael@0 491 }
michael@0 492
michael@0 493 /* static */
michael@0 494 void
michael@0 495 txXPathNodeUtils::appendNodeValue(const txXPathNode& aNode, nsAString& aResult)
michael@0 496 {
michael@0 497 if (aNode.isAttribute()) {
michael@0 498 const nsAttrName* name = aNode.Content()->GetAttrNameAt(aNode.mIndex);
michael@0 499
michael@0 500 if (aResult.IsEmpty()) {
michael@0 501 aNode.Content()->GetAttr(name->NamespaceID(), name->LocalName(),
michael@0 502 aResult);
michael@0 503 }
michael@0 504 else {
michael@0 505 nsAutoString result;
michael@0 506 aNode.Content()->GetAttr(name->NamespaceID(), name->LocalName(),
michael@0 507 result);
michael@0 508 aResult.Append(result);
michael@0 509 }
michael@0 510
michael@0 511 return;
michael@0 512 }
michael@0 513
michael@0 514 if (aNode.isDocument() ||
michael@0 515 aNode.mNode->IsElement() ||
michael@0 516 aNode.mNode->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT)) {
michael@0 517 nsContentUtils::AppendNodeTextContent(aNode.mNode, true, aResult,
michael@0 518 mozilla::fallible_t());
michael@0 519
michael@0 520 return;
michael@0 521 }
michael@0 522
michael@0 523 aNode.Content()->AppendTextTo(aResult);
michael@0 524 }
michael@0 525
michael@0 526 /* static */
michael@0 527 bool
michael@0 528 txXPathNodeUtils::isWhitespace(const txXPathNode& aNode)
michael@0 529 {
michael@0 530 NS_ASSERTION(aNode.isContent() && isText(aNode), "Wrong type!");
michael@0 531
michael@0 532 return aNode.Content()->TextIsOnlyWhitespace();
michael@0 533 }
michael@0 534
michael@0 535 /* static */
michael@0 536 txXPathNode*
michael@0 537 txXPathNodeUtils::getOwnerDocument(const txXPathNode& aNode)
michael@0 538 {
michael@0 539 return new txXPathNode(aNode.mNode->OwnerDoc());
michael@0 540 }
michael@0 541
michael@0 542 const char gPrintfFmt[] = "id0x%p";
michael@0 543 const char gPrintfFmtAttr[] = "id0x%p-%010i";
michael@0 544
michael@0 545 /* static */
michael@0 546 nsresult
michael@0 547 txXPathNodeUtils::getXSLTId(const txXPathNode& aNode,
michael@0 548 const txXPathNode& aBase,
michael@0 549 nsAString& aResult)
michael@0 550 {
michael@0 551 uintptr_t nodeid = ((uintptr_t)aNode.mNode) - ((uintptr_t)aBase.mNode);
michael@0 552 if (!aNode.isAttribute()) {
michael@0 553 CopyASCIItoUTF16(nsPrintfCString(gPrintfFmt, nodeid),
michael@0 554 aResult);
michael@0 555 }
michael@0 556 else {
michael@0 557 CopyASCIItoUTF16(nsPrintfCString(gPrintfFmtAttr,
michael@0 558 nodeid, aNode.mIndex), aResult);
michael@0 559 }
michael@0 560
michael@0 561 return NS_OK;
michael@0 562 }
michael@0 563
michael@0 564 /* static */
michael@0 565 void
michael@0 566 txXPathNodeUtils::getBaseURI(const txXPathNode& aNode, nsAString& aURI)
michael@0 567 {
michael@0 568 aNode.mNode->GetBaseURI(aURI);
michael@0 569 }
michael@0 570
michael@0 571 /* static */
michael@0 572 int
michael@0 573 txXPathNodeUtils::comparePosition(const txXPathNode& aNode,
michael@0 574 const txXPathNode& aOtherNode)
michael@0 575 {
michael@0 576 // First check for equal nodes or attribute-nodes on the same element.
michael@0 577 if (aNode.mNode == aOtherNode.mNode) {
michael@0 578 if (aNode.mIndex == aOtherNode.mIndex) {
michael@0 579 return 0;
michael@0 580 }
michael@0 581
michael@0 582 NS_ASSERTION(!aNode.isDocument() && !aOtherNode.isDocument(),
michael@0 583 "documents should always have a set index");
michael@0 584
michael@0 585 if (aNode.isContent() || (!aOtherNode.isContent() &&
michael@0 586 aNode.mIndex < aOtherNode.mIndex)) {
michael@0 587 return -1;
michael@0 588 }
michael@0 589
michael@0 590 return 1;
michael@0 591 }
michael@0 592
michael@0 593 // Get document for both nodes.
michael@0 594 nsIDocument* document = aNode.mNode->GetCurrentDoc();
michael@0 595 nsIDocument* otherDocument = aOtherNode.mNode->GetCurrentDoc();
michael@0 596
michael@0 597 // If the nodes have different current documents, compare the document
michael@0 598 // pointers.
michael@0 599 if (document != otherDocument) {
michael@0 600 return document < otherDocument ? -1 : 1;
michael@0 601 }
michael@0 602
michael@0 603 // Now either both nodes are in orphan trees, or they are both in the
michael@0 604 // same tree.
michael@0 605
michael@0 606 // Get parents up the tree.
michael@0 607 nsAutoTArray<nsINode*, 8> parents, otherParents;
michael@0 608 nsINode* node = aNode.mNode;
michael@0 609 nsINode* otherNode = aOtherNode.mNode;
michael@0 610 nsINode* parent, *otherParent;
michael@0 611 while (node && otherNode) {
michael@0 612 parent = node->GetParentNode();
michael@0 613 otherParent = otherNode->GetParentNode();
michael@0 614
michael@0 615 // Hopefully this is a common case.
michael@0 616 if (parent == otherParent) {
michael@0 617 if (!parent) {
michael@0 618 // Both node and otherNode are root nodes in respective orphan
michael@0 619 // tree.
michael@0 620 return node < otherNode ? -1 : 1;
michael@0 621 }
michael@0 622
michael@0 623 return parent->IndexOf(node) < parent->IndexOf(otherNode) ?
michael@0 624 -1 : 1;
michael@0 625 }
michael@0 626
michael@0 627 parents.AppendElement(node);
michael@0 628 otherParents.AppendElement(otherNode);
michael@0 629 node = parent;
michael@0 630 otherNode = otherParent;
michael@0 631 }
michael@0 632
michael@0 633 while (node) {
michael@0 634 parents.AppendElement(node);
michael@0 635 node = node->GetParentNode();
michael@0 636 }
michael@0 637 while (otherNode) {
michael@0 638 otherParents.AppendElement(otherNode);
michael@0 639 otherNode = otherNode->GetParentNode();
michael@0 640 }
michael@0 641
michael@0 642 // Walk back down along the parent-chains until we find where they split.
michael@0 643 int32_t total = parents.Length() - 1;
michael@0 644 int32_t otherTotal = otherParents.Length() - 1;
michael@0 645 NS_ASSERTION(total != otherTotal, "Can't have same number of parents");
michael@0 646
michael@0 647 int32_t lastIndex = std::min(total, otherTotal);
michael@0 648 int32_t i;
michael@0 649 parent = nullptr;
michael@0 650 for (i = 0; i <= lastIndex; ++i) {
michael@0 651 node = parents.ElementAt(total - i);
michael@0 652 otherNode = otherParents.ElementAt(otherTotal - i);
michael@0 653 if (node != otherNode) {
michael@0 654 if (!parent) {
michael@0 655 // The two nodes are in different orphan subtrees.
michael@0 656 NS_ASSERTION(i == 0, "this shouldn't happen");
michael@0 657 return node < otherNode ? -1 : 1;
michael@0 658 }
michael@0 659
michael@0 660 int32_t index = parent->IndexOf(node);
michael@0 661 int32_t otherIndex = parent->IndexOf(otherNode);
michael@0 662 NS_ASSERTION(index != otherIndex && index >= 0 && otherIndex >= 0,
michael@0 663 "invalid index in compareTreePosition");
michael@0 664
michael@0 665 return index < otherIndex ? -1 : 1;
michael@0 666 }
michael@0 667
michael@0 668 parent = node;
michael@0 669 }
michael@0 670
michael@0 671 // One node is a descendant of the other. The one with the shortest
michael@0 672 // parent-chain is first in the document.
michael@0 673 return total < otherTotal ? -1 : 1;
michael@0 674 }
michael@0 675
michael@0 676 /* static */
michael@0 677 txXPathNode*
michael@0 678 txXPathNativeNode::createXPathNode(nsIContent* aContent, bool aKeepRootAlive)
michael@0 679 {
michael@0 680 nsINode* root = aKeepRootAlive ? txXPathNode::RootOf(aContent) : nullptr;
michael@0 681
michael@0 682 return new txXPathNode(aContent, txXPathNode::eContent, root);
michael@0 683 }
michael@0 684
michael@0 685 /* static */
michael@0 686 txXPathNode*
michael@0 687 txXPathNativeNode::createXPathNode(nsIDOMNode* aNode, bool aKeepRootAlive)
michael@0 688 {
michael@0 689 uint16_t nodeType;
michael@0 690 aNode->GetNodeType(&nodeType);
michael@0 691
michael@0 692 if (nodeType == nsIDOMNode::ATTRIBUTE_NODE) {
michael@0 693 nsCOMPtr<nsIAttribute> attr = do_QueryInterface(aNode);
michael@0 694 NS_ASSERTION(attr, "doesn't implement nsIAttribute");
michael@0 695
michael@0 696 nsINodeInfo *nodeInfo = attr->NodeInfo();
michael@0 697 mozilla::dom::Element* parent =
michael@0 698 static_cast<Attr*>(attr.get())->GetElement();
michael@0 699 if (!parent) {
michael@0 700 return nullptr;
michael@0 701 }
michael@0 702
michael@0 703 nsINode* root = aKeepRootAlive ? txXPathNode::RootOf(parent) : nullptr;
michael@0 704
michael@0 705 uint32_t i, total = parent->GetAttrCount();
michael@0 706 for (i = 0; i < total; ++i) {
michael@0 707 const nsAttrName* name = parent->GetAttrNameAt(i);
michael@0 708 if (nodeInfo->Equals(name->LocalName(), name->NamespaceID())) {
michael@0 709 return new txXPathNode(parent, i, root);
michael@0 710 }
michael@0 711 }
michael@0 712
michael@0 713 NS_ERROR("Couldn't find the attribute in its parent!");
michael@0 714
michael@0 715 return nullptr;
michael@0 716 }
michael@0 717
michael@0 718 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
michael@0 719 uint32_t index;
michael@0 720 nsINode* root = aKeepRootAlive ? node.get() : nullptr;
michael@0 721
michael@0 722 if (nodeType == nsIDOMNode::DOCUMENT_NODE) {
michael@0 723 index = txXPathNode::eDocument;
michael@0 724 }
michael@0 725 else {
michael@0 726 index = txXPathNode::eContent;
michael@0 727 if (root) {
michael@0 728 root = txXPathNode::RootOf(root);
michael@0 729 }
michael@0 730 }
michael@0 731
michael@0 732 return new txXPathNode(node, index, root);
michael@0 733 }
michael@0 734
michael@0 735 /* static */
michael@0 736 txXPathNode*
michael@0 737 txXPathNativeNode::createXPathNode(nsIDOMDocument* aDocument)
michael@0 738 {
michael@0 739 nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument);
michael@0 740 return new txXPathNode(document);
michael@0 741 }
michael@0 742
michael@0 743 /* static */
michael@0 744 nsresult
michael@0 745 txXPathNativeNode::getNode(const txXPathNode& aNode, nsIDOMNode** aResult)
michael@0 746 {
michael@0 747 if (!aNode.isAttribute()) {
michael@0 748 return CallQueryInterface(aNode.mNode, aResult);
michael@0 749 }
michael@0 750
michael@0 751 const nsAttrName* name = aNode.Content()->GetAttrNameAt(aNode.mIndex);
michael@0 752
michael@0 753 nsAutoString namespaceURI;
michael@0 754 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(name->NamespaceID(), namespaceURI);
michael@0 755
michael@0 756 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode.mNode);
michael@0 757 nsCOMPtr<nsIDOMAttr> attr;
michael@0 758 element->GetAttributeNodeNS(namespaceURI,
michael@0 759 nsDependentAtomString(name->LocalName()),
michael@0 760 getter_AddRefs(attr));
michael@0 761
michael@0 762 return CallQueryInterface(attr, aResult);
michael@0 763 }
michael@0 764
michael@0 765 /* static */
michael@0 766 nsIContent*
michael@0 767 txXPathNativeNode::getContent(const txXPathNode& aNode)
michael@0 768 {
michael@0 769 NS_ASSERTION(aNode.isContent(),
michael@0 770 "Only call getContent on nsIContent wrappers!");
michael@0 771 return aNode.Content();
michael@0 772 }
michael@0 773
michael@0 774 /* static */
michael@0 775 nsIDocument*
michael@0 776 txXPathNativeNode::getDocument(const txXPathNode& aNode)
michael@0 777 {
michael@0 778 NS_ASSERTION(aNode.isDocument(),
michael@0 779 "Only call getDocument on nsIDocument wrappers!");
michael@0 780 return aNode.Document();
michael@0 781 }

mercurial