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