dom/xslt/xpath/txMozillaXPathTreeWalker.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:c10f7e0498e5
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 }

mercurial