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