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