Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
6 #include "inDOMView.h"
7 #include "inIDOMUtils.h"
9 #include "inLayoutUtils.h"
11 #include "nsString.h"
12 #include "nsReadableUtils.h"
13 #include "nsIDOMNode.h"
14 #include "nsIDOMNodeFilter.h"
15 #include "nsIDOMNodeList.h"
16 #include "nsIDOMCharacterData.h"
17 #include "nsIDOMAttr.h"
18 #include "nsIDOMMozNamedAttrMap.h"
19 #include "nsIDOMMutationEvent.h"
20 #include "nsBindingManager.h"
21 #include "nsNameSpaceManager.h"
22 #include "nsIDocument.h"
23 #include "nsIServiceManager.h"
24 #include "nsITreeColumns.h"
25 #include "nsITreeBoxObject.h"
26 #include "mozilla/dom/Element.h"
27 #include "mozilla/Services.h"
29 #ifdef ACCESSIBILITY
30 #include "nsIAccessible.h"
31 #include "nsIAccessibilityService.h"
32 #endif
34 using namespace mozilla;
36 ////////////////////////////////////////////////////////////////////////
37 // inDOMViewNode
39 class inDOMViewNode
40 {
41 public:
42 inDOMViewNode() {}
43 inDOMViewNode(nsIDOMNode* aNode);
44 ~inDOMViewNode();
46 nsCOMPtr<nsIDOMNode> node;
48 inDOMViewNode* parent;
49 inDOMViewNode* next;
50 inDOMViewNode* previous;
52 int32_t level;
53 bool isOpen;
54 bool isContainer;
55 bool hasAnonymous;
56 bool hasSubDocument;
57 };
59 inDOMViewNode::inDOMViewNode(nsIDOMNode* aNode) :
60 node(aNode),
61 parent(nullptr),
62 next(nullptr),
63 previous(nullptr),
64 level(0),
65 isOpen(false),
66 isContainer(false),
67 hasAnonymous(false),
68 hasSubDocument(false)
69 {
71 }
73 inDOMViewNode::~inDOMViewNode()
74 {
75 }
77 ////////////////////////////////////////////////////////////////////////
79 inDOMView::inDOMView() :
80 mShowAnonymous(false),
81 mShowSubDocuments(false),
82 mShowWhitespaceNodes(true),
83 mShowAccessibleNodes(false),
84 mWhatToShow(nsIDOMNodeFilter::SHOW_ALL)
85 {
86 }
88 inDOMView::~inDOMView()
89 {
90 SetRootNode(nullptr);
91 }
94 ////////////////////////////////////////////////////////////////////////
95 // nsISupports
97 NS_IMPL_ISUPPORTS(inDOMView,
98 inIDOMView,
99 nsITreeView,
100 nsIMutationObserver)
102 ////////////////////////////////////////////////////////////////////////
103 // inIDOMView
105 NS_IMETHODIMP
106 inDOMView::GetRootNode(nsIDOMNode** aNode)
107 {
108 *aNode = mRootNode;
109 NS_IF_ADDREF(*aNode);
110 return NS_OK;
111 }
113 NS_IMETHODIMP
114 inDOMView::SetRootNode(nsIDOMNode* aNode)
115 {
116 if (mTree)
117 mTree->BeginUpdateBatch();
119 if (mRootDocument) {
120 // remove previous document observer
121 nsCOMPtr<nsINode> doc(do_QueryInterface(mRootDocument));
122 if (doc)
123 doc->RemoveMutationObserver(this);
124 }
126 RemoveAllNodes();
128 mRootNode = aNode;
130 if (aNode) {
131 // If we are able to show element nodes, then start with the root node
132 // as the first node in the buffer
133 if (mWhatToShow & nsIDOMNodeFilter::SHOW_ELEMENT) {
134 // allocate new node array
135 AppendNode(CreateNode(aNode, nullptr));
136 } else {
137 // place only the children of the root node in the buffer
138 ExpandNode(-1);
139 }
141 // store an owning reference to document so that it isn't
142 // destroyed before we are
143 mRootDocument = do_QueryInterface(aNode);
144 if (!mRootDocument) {
145 aNode->GetOwnerDocument(getter_AddRefs(mRootDocument));
146 }
148 // add document observer
149 nsCOMPtr<nsINode> doc(do_QueryInterface(mRootDocument));
150 if (doc)
151 doc->AddMutationObserver(this);
152 } else {
153 mRootDocument = nullptr;
154 }
156 if (mTree)
157 mTree->EndUpdateBatch();
159 return NS_OK;
160 }
162 NS_IMETHODIMP
163 inDOMView::GetNodeFromRowIndex(int32_t rowIndex, nsIDOMNode **_retval)
164 {
165 inDOMViewNode* viewNode = nullptr;
166 RowToNode(rowIndex, &viewNode);
167 if (!viewNode) return NS_ERROR_FAILURE;
168 *_retval = viewNode->node;
169 NS_IF_ADDREF(*_retval);
171 return NS_OK;
172 }
174 NS_IMETHODIMP
175 inDOMView::GetRowIndexFromNode(nsIDOMNode *node, int32_t *_retval)
176 {
177 NodeToRow(node, _retval);
178 return NS_OK;
179 }
182 NS_IMETHODIMP
183 inDOMView::GetShowAnonymousContent(bool *aShowAnonymousContent)
184 {
185 *aShowAnonymousContent = mShowAnonymous;
186 return NS_OK;
187 }
189 NS_IMETHODIMP
190 inDOMView::SetShowAnonymousContent(bool aShowAnonymousContent)
191 {
192 mShowAnonymous = aShowAnonymousContent;
193 return NS_OK;
194 }
196 NS_IMETHODIMP
197 inDOMView::GetShowSubDocuments(bool *aShowSubDocuments)
198 {
199 *aShowSubDocuments = mShowSubDocuments;
200 return NS_OK;
201 }
203 NS_IMETHODIMP
204 inDOMView::SetShowSubDocuments(bool aShowSubDocuments)
205 {
206 mShowSubDocuments = aShowSubDocuments;
207 return NS_OK;
208 }
210 NS_IMETHODIMP
211 inDOMView::GetShowWhitespaceNodes(bool *aShowWhitespaceNodes)
212 {
213 *aShowWhitespaceNodes = mShowWhitespaceNodes;
214 return NS_OK;
215 }
217 NS_IMETHODIMP
218 inDOMView::SetShowWhitespaceNodes(bool aShowWhitespaceNodes)
219 {
220 mShowWhitespaceNodes = aShowWhitespaceNodes;
221 return NS_OK;
222 }
224 NS_IMETHODIMP
225 inDOMView::GetShowAccessibleNodes(bool *aShowAccessibleNodes)
226 {
227 *aShowAccessibleNodes = mShowAccessibleNodes;
228 return NS_OK;
229 }
231 NS_IMETHODIMP
232 inDOMView::SetShowAccessibleNodes(bool aShowAccessibleNodes)
233 {
234 mShowAccessibleNodes = aShowAccessibleNodes;
235 return NS_OK;
236 }
238 NS_IMETHODIMP
239 inDOMView::GetWhatToShow(uint32_t *aWhatToShow)
240 {
241 *aWhatToShow = mWhatToShow;
242 return NS_OK;
243 }
245 NS_IMETHODIMP
246 inDOMView::SetWhatToShow(uint32_t aWhatToShow)
247 {
248 mWhatToShow = aWhatToShow;
249 return NS_OK;
250 }
252 NS_IMETHODIMP
253 inDOMView::Rebuild()
254 {
255 nsCOMPtr<nsIDOMNode> root;
256 GetRootNode(getter_AddRefs(root));
257 SetRootNode(root);
258 return NS_OK;
259 }
261 ////////////////////////////////////////////////////////////////////////
262 // nsITreeView
264 NS_IMETHODIMP
265 inDOMView::GetRowCount(int32_t *aRowCount)
266 {
267 *aRowCount = GetRowCount();
268 return NS_OK;
269 }
271 NS_IMETHODIMP
272 inDOMView::GetRowProperties(int32_t index, nsAString& aProps)
273 {
274 return NS_OK;
275 }
277 NS_IMETHODIMP
278 inDOMView::GetCellProperties(int32_t row, nsITreeColumn* col,
279 nsAString& aProps)
280 {
281 inDOMViewNode* node = nullptr;
282 RowToNode(row, &node);
283 if (!node) return NS_ERROR_FAILURE;
285 nsCOMPtr<nsIContent> content = do_QueryInterface(node->node);
286 if (content && content->IsInAnonymousSubtree()) {
287 aProps.AppendLiteral("anonymous ");
288 }
290 uint16_t nodeType;
291 node->node->GetNodeType(&nodeType);
292 switch (nodeType) {
293 case nsIDOMNode::ELEMENT_NODE:
294 aProps.AppendLiteral("ELEMENT_NODE");
295 break;
296 case nsIDOMNode::ATTRIBUTE_NODE:
297 aProps.AppendLiteral("ATTRIBUTE_NODE");
298 break;
299 case nsIDOMNode::TEXT_NODE:
300 aProps.AppendLiteral("TEXT_NODE");
301 break;
302 case nsIDOMNode::CDATA_SECTION_NODE:
303 aProps.AppendLiteral("CDATA_SECTION_NODE");
304 break;
305 case nsIDOMNode::ENTITY_REFERENCE_NODE:
306 aProps.AppendLiteral("ENTITY_REFERENCE_NODE");
307 break;
308 case nsIDOMNode::ENTITY_NODE:
309 aProps.AppendLiteral("ENTITY_NODE");
310 break;
311 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
312 aProps.AppendLiteral("PROCESSING_INSTRUCTION_NODE");
313 break;
314 case nsIDOMNode::COMMENT_NODE:
315 aProps.AppendLiteral("COMMENT_NODE");
316 break;
317 case nsIDOMNode::DOCUMENT_NODE:
318 aProps.AppendLiteral("DOCUMENT_NODE");
319 break;
320 case nsIDOMNode::DOCUMENT_TYPE_NODE:
321 aProps.AppendLiteral("DOCUMENT_TYPE_NODE");
322 break;
323 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
324 aProps.AppendLiteral("DOCUMENT_FRAGMENT_NODE");
325 break;
326 case nsIDOMNode::NOTATION_NODE:
327 aProps.AppendLiteral("NOTATION_NODE");
328 break;
329 }
331 #ifdef ACCESSIBILITY
332 if (mShowAccessibleNodes) {
333 nsCOMPtr<nsIAccessibilityService> accService(
334 do_GetService("@mozilla.org/accessibilityService;1"));
335 NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
337 nsCOMPtr<nsIAccessible> accessible;
338 nsresult rv =
339 accService->GetAccessibleFor(node->node, getter_AddRefs(accessible));
340 if (NS_SUCCEEDED(rv) && accessible)
341 aProps.AppendLiteral(" ACCESSIBLE_NODE");
342 }
343 #endif
345 return NS_OK;
346 }
348 NS_IMETHODIMP
349 inDOMView::GetColumnProperties(nsITreeColumn* col, nsAString& aProps)
350 {
351 return NS_OK;
352 }
354 NS_IMETHODIMP
355 inDOMView::GetImageSrc(int32_t row, nsITreeColumn* col, nsAString& _retval)
356 {
357 return NS_OK;
358 }
360 NS_IMETHODIMP
361 inDOMView::GetProgressMode(int32_t row, nsITreeColumn* col, int32_t* _retval)
362 {
363 return NS_OK;
364 }
366 NS_IMETHODIMP
367 inDOMView::GetCellValue(int32_t row, nsITreeColumn* col, nsAString& _retval)
368 {
369 return NS_OK;
370 }
372 NS_IMETHODIMP
373 inDOMView::GetCellText(int32_t row, nsITreeColumn* col, nsAString& _retval)
374 {
375 inDOMViewNode* node = nullptr;
376 RowToNode(row, &node);
377 if (!node) return NS_ERROR_FAILURE;
379 nsIDOMNode* domNode = node->node;
381 nsAutoString colID;
382 col->GetId(colID);
383 if (colID.EqualsLiteral("colNodeName"))
384 domNode->GetNodeName(_retval);
385 else if (colID.EqualsLiteral("colLocalName"))
386 domNode->GetLocalName(_retval);
387 else if (colID.EqualsLiteral("colPrefix"))
388 domNode->GetPrefix(_retval);
389 else if (colID.EqualsLiteral("colNamespaceURI"))
390 domNode->GetNamespaceURI(_retval);
391 else if (colID.EqualsLiteral("colNodeType")) {
392 uint16_t nodeType;
393 domNode->GetNodeType(&nodeType);
394 nsAutoString temp;
395 temp.AppendInt(int32_t(nodeType));
396 _retval = temp;
397 } else if (colID.EqualsLiteral("colNodeValue"))
398 domNode->GetNodeValue(_retval);
399 else {
400 if (StringBeginsWith(colID, NS_LITERAL_STRING("col@"))) {
401 nsCOMPtr<nsIDOMElement> el = do_QueryInterface(node->node);
402 if (el) {
403 nsAutoString attr;
404 colID.Right(attr, colID.Length()-4); // have to use this because Substring is crashing on me!
405 el->GetAttribute(attr, _retval);
406 }
407 }
408 }
410 return NS_OK;
411 }
413 NS_IMETHODIMP
414 inDOMView::IsContainer(int32_t index, bool *_retval)
415 {
416 inDOMViewNode* node = nullptr;
417 RowToNode(index, &node);
418 if (!node) return NS_ERROR_FAILURE;
420 *_retval = node->isContainer;
421 return NS_OK;
422 }
424 NS_IMETHODIMP
425 inDOMView::IsContainerOpen(int32_t index, bool *_retval)
426 {
427 inDOMViewNode* node = nullptr;
428 RowToNode(index, &node);
429 if (!node) return NS_ERROR_FAILURE;
431 *_retval = node->isOpen;
432 return NS_OK;
433 }
435 NS_IMETHODIMP
436 inDOMView::IsContainerEmpty(int32_t index, bool *_retval)
437 {
438 inDOMViewNode* node = nullptr;
439 RowToNode(index, &node);
440 if (!node) return NS_ERROR_FAILURE;
442 *_retval = node->isContainer ? false : true;
443 return NS_OK;
444 }
446 NS_IMETHODIMP
447 inDOMView::GetLevel(int32_t index, int32_t *_retval)
448 {
449 inDOMViewNode* node = nullptr;
450 RowToNode(index, &node);
451 if (!node) return NS_ERROR_FAILURE;
453 *_retval = node->level;
454 return NS_OK;
455 }
457 NS_IMETHODIMP
458 inDOMView::GetParentIndex(int32_t rowIndex, int32_t *_retval)
459 {
460 inDOMViewNode* node = nullptr;
461 RowToNode(rowIndex, &node);
462 if (!node) return NS_ERROR_FAILURE;
464 // GetParentIndex returns -1 if there is no parent
465 *_retval = -1;
467 inDOMViewNode* checkNode = nullptr;
468 int32_t i = rowIndex - 1;
469 do {
470 nsresult rv = RowToNode(i, &checkNode);
471 if (NS_FAILED(rv)) {
472 // No parent. Just break out.
473 break;
474 }
476 if (checkNode == node->parent) {
477 *_retval = i;
478 return NS_OK;
479 }
480 --i;
481 } while (checkNode);
483 return NS_OK;
484 }
486 NS_IMETHODIMP
487 inDOMView::HasNextSibling(int32_t rowIndex, int32_t afterIndex, bool *_retval)
488 {
489 inDOMViewNode* node = nullptr;
490 RowToNode(rowIndex, &node);
491 if (!node) return NS_ERROR_FAILURE;
493 *_retval = node->next != nullptr;
495 return NS_OK;
496 }
498 NS_IMETHODIMP
499 inDOMView::ToggleOpenState(int32_t index)
500 {
501 inDOMViewNode* node = nullptr;
502 RowToNode(index, &node);
503 if (!node) return NS_ERROR_FAILURE;
505 int32_t oldCount = GetRowCount();
506 if (node->isOpen)
507 CollapseNode(index);
508 else
509 ExpandNode(index);
511 // Update the twisty.
512 mTree->InvalidateRow(index);
514 mTree->RowCountChanged(index+1, GetRowCount() - oldCount);
516 return NS_OK;
517 }
519 NS_IMETHODIMP
520 inDOMView::SetTree(nsITreeBoxObject *tree)
521 {
522 mTree = tree;
523 return NS_OK;
524 }
526 NS_IMETHODIMP
527 inDOMView::GetSelection(nsITreeSelection * *aSelection)
528 {
529 *aSelection = mSelection;
530 NS_IF_ADDREF(*aSelection);
531 return NS_OK;
532 }
534 NS_IMETHODIMP inDOMView::SetSelection(nsITreeSelection * aSelection)
535 {
536 mSelection = aSelection;
537 return NS_OK;
538 }
540 NS_IMETHODIMP
541 inDOMView::SelectionChanged()
542 {
543 return NS_OK;
544 }
546 NS_IMETHODIMP
547 inDOMView::SetCellValue(int32_t row, nsITreeColumn* col, const nsAString& value)
548 {
549 return NS_OK;
550 }
552 NS_IMETHODIMP
553 inDOMView::SetCellText(int32_t row, nsITreeColumn* col, const nsAString& value)
554 {
555 return NS_OK;
556 }
558 NS_IMETHODIMP
559 inDOMView::CycleHeader(nsITreeColumn* col)
560 {
561 return NS_OK;
562 }
564 NS_IMETHODIMP
565 inDOMView::CycleCell(int32_t row, nsITreeColumn* col)
566 {
567 return NS_OK;
568 }
570 NS_IMETHODIMP
571 inDOMView::IsEditable(int32_t row, nsITreeColumn* col, bool *_retval)
572 {
573 return NS_OK;
574 }
577 NS_IMETHODIMP
578 inDOMView::IsSelectable(int32_t row, nsITreeColumn* col, bool *_retval)
579 {
580 return NS_OK;
581 }
583 NS_IMETHODIMP
584 inDOMView::IsSeparator(int32_t index, bool *_retval)
585 {
586 return NS_OK;
587 }
589 NS_IMETHODIMP
590 inDOMView::IsSorted(bool *_retval)
591 {
592 return NS_OK;
593 }
595 NS_IMETHODIMP
596 inDOMView::CanDrop(int32_t index, int32_t orientation,
597 nsIDOMDataTransfer* aDataTransfer, bool *_retval)
598 {
599 *_retval = false;
600 return NS_OK;
601 }
603 NS_IMETHODIMP
604 inDOMView::Drop(int32_t row, int32_t orientation, nsIDOMDataTransfer* aDataTransfer)
605 {
606 return NS_OK;
607 }
609 NS_IMETHODIMP
610 inDOMView::PerformAction(const char16_t *action)
611 {
612 return NS_OK;
613 }
615 NS_IMETHODIMP
616 inDOMView::PerformActionOnRow(const char16_t *action, int32_t row)
617 {
618 return NS_OK;
619 }
621 NS_IMETHODIMP
622 inDOMView::PerformActionOnCell(const char16_t* action, int32_t row, nsITreeColumn* col)
623 {
624 return NS_OK;
625 }
627 ///////////////////////////////////////////////////////////////////////
628 // nsIMutationObserver
630 void
631 inDOMView::NodeWillBeDestroyed(const nsINode* aNode)
632 {
633 NS_NOTREACHED("Document destroyed while we're holding a strong ref to it");
634 }
636 void
637 inDOMView::AttributeChanged(nsIDocument* aDocument, dom::Element* aElement,
638 int32_t aNameSpaceID, nsIAtom* aAttribute,
639 int32_t aModType)
640 {
641 if (!mTree) {
642 return;
643 }
645 if (!(mWhatToShow & nsIDOMNodeFilter::SHOW_ATTRIBUTE)) {
646 return;
647 }
649 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
651 // get the dom attribute node, if there is any
652 nsCOMPtr<nsIDOMElement> el(do_QueryInterface(aElement));
653 nsCOMPtr<nsIDOMAttr> domAttr;
654 nsDependentAtomString attrStr(aAttribute);
655 if (aNameSpaceID) {
656 nsNameSpaceManager* nsm = nsNameSpaceManager::GetInstance();
657 if (!nsm) {
658 // we can't find out which attribute we want :(
659 return;
660 }
661 nsString attrNS;
662 nsresult rv = nsm->GetNameSpaceURI(aNameSpaceID, attrNS);
663 if (NS_FAILED(rv)) {
664 return;
665 }
666 (void)el->GetAttributeNodeNS(attrNS, attrStr, getter_AddRefs(domAttr));
667 } else {
668 (void)el->GetAttributeNode(attrStr, getter_AddRefs(domAttr));
669 }
671 if (aModType == nsIDOMMutationEvent::MODIFICATION) {
672 // No fancy stuff here, just invalidate the changed row
673 if (!domAttr) {
674 return;
675 }
676 int32_t row = 0;
677 NodeToRow(domAttr, &row);
678 mTree->InvalidateRange(row, row);
679 } else if (aModType == nsIDOMMutationEvent::ADDITION) {
680 if (!domAttr) {
681 return;
682 }
683 // get the number of attributes on this content node
684 nsCOMPtr<nsIDOMMozNamedAttrMap> attrs;
685 el->GetAttributes(getter_AddRefs(attrs));
686 uint32_t attrCount;
687 attrs->GetLength(&attrCount);
689 inDOMViewNode* contentNode = nullptr;
690 int32_t contentRow;
691 int32_t attrRow;
692 if (mRootNode == el &&
693 !(mWhatToShow & nsIDOMNodeFilter::SHOW_ELEMENT)) {
694 // if this view has a root node but is not displaying it,
695 // it is ok to act as if the changed attribute is on the root.
696 attrRow = attrCount - 1;
697 } else {
698 if (NS_FAILED(NodeToRow(el, &contentRow))) {
699 return;
700 }
701 RowToNode(contentRow, &contentNode);
702 if (!contentNode->isOpen) {
703 return;
704 }
705 attrRow = contentRow + attrCount;
706 }
708 inDOMViewNode* newNode = CreateNode(domAttr, contentNode);
709 inDOMViewNode* insertNode = nullptr;
710 RowToNode(attrRow, &insertNode);
711 if (insertNode) {
712 if (contentNode &&
713 insertNode->level <= contentNode->level) {
714 RowToNode(attrRow-1, &insertNode);
715 InsertLinkAfter(newNode, insertNode);
716 } else
717 InsertLinkBefore(newNode, insertNode);
718 }
719 InsertNode(newNode, attrRow);
720 mTree->RowCountChanged(attrRow, 1);
721 } else if (aModType == nsIDOMMutationEvent::REMOVAL) {
722 // At this point, the attribute is already gone from the DOM, but is still represented
723 // in our mRows array. Search through the content node's children for the corresponding
724 // node and remove it.
726 // get the row of the content node
727 inDOMViewNode* contentNode = nullptr;
728 int32_t contentRow;
729 int32_t baseLevel;
730 if (NS_SUCCEEDED(NodeToRow(el, &contentRow))) {
731 RowToNode(contentRow, &contentNode);
732 baseLevel = contentNode->level;
733 } else {
734 if (mRootNode == el) {
735 contentRow = -1;
736 baseLevel = -1;
737 } else
738 return;
739 }
741 // search for the attribute node that was removed
742 inDOMViewNode* checkNode = nullptr;
743 int32_t row = 0;
744 for (row = contentRow+1; row < GetRowCount(); ++row) {
745 checkNode = GetNodeAt(row);
746 if (checkNode->level == baseLevel+1) {
747 domAttr = do_QueryInterface(checkNode->node);
748 if (domAttr) {
749 nsAutoString attrName;
750 domAttr->GetNodeName(attrName);
751 if (attrName.Equals(attrStr)) {
752 // we have found the row for the attribute that was removed
753 RemoveLink(checkNode);
754 RemoveNode(row);
755 mTree->RowCountChanged(row, -1);
756 break;
757 }
758 }
759 }
760 if (checkNode->level <= baseLevel)
761 break;
762 }
764 }
765 }
767 void
768 inDOMView::ContentAppended(nsIDocument *aDocument,
769 nsIContent* aContainer,
770 nsIContent* aFirstNewContent,
771 int32_t /* unused */)
772 {
773 if (!mTree) {
774 return;
775 }
777 for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
778 // Our ContentInserted impl doesn't use the index
779 ContentInserted(aDocument, aContainer, cur, 0);
780 }
781 }
783 void
784 inDOMView::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
785 nsIContent* aChild, int32_t /* unused */)
786 {
787 if (!mTree)
788 return;
790 nsresult rv;
791 nsCOMPtr<nsIDOMNode> childDOMNode(do_QueryInterface(aChild));
792 nsCOMPtr<nsIDOMNode> parent;
793 if (!mDOMUtils) {
794 mDOMUtils = services::GetInDOMUtils();
795 if (!mDOMUtils) {
796 return;
797 }
798 }
799 mDOMUtils->GetParentForNode(childDOMNode, mShowAnonymous,
800 getter_AddRefs(parent));
802 // find the inDOMViewNode for the parent of the inserted content
803 int32_t parentRow = 0;
804 if (NS_FAILED(rv = NodeToRow(parent, &parentRow)))
805 return;
806 inDOMViewNode* parentNode = nullptr;
807 if (NS_FAILED(rv = RowToNode(parentRow, &parentNode)))
808 return;
810 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
812 if (!parentNode->isOpen) {
813 // Parent is not open, so don't bother creating tree rows for the
814 // kids. But do indicate that it's now a container, if needed.
815 if (!parentNode->isContainer) {
816 parentNode->isContainer = true;
817 mTree->InvalidateRow(parentRow);
818 }
819 return;
820 }
822 // get the previous sibling of the inserted content
823 nsCOMPtr<nsIDOMNode> previous;
824 GetRealPreviousSibling(childDOMNode, parent, getter_AddRefs(previous));
825 inDOMViewNode* previousNode = nullptr;
827 int32_t row = 0;
828 if (previous) {
829 // find the inDOMViewNode for the previous sibling of the inserted content
830 int32_t previousRow = 0;
831 if (NS_FAILED(rv = NodeToRow(previous, &previousRow)))
832 return;
833 if (NS_FAILED(rv = RowToNode(previousRow, &previousNode)))
834 return;
836 // get the last descendant of the previous row, which is the row
837 // after which to insert this new row
838 GetLastDescendantOf(previousNode, previousRow, &row);
839 ++row;
840 } else {
841 // there is no previous sibling, so the new row will be inserted after the parent
842 row = parentRow+1;
843 }
845 inDOMViewNode* newNode = CreateNode(childDOMNode, parentNode);
847 if (previous) {
848 InsertLinkAfter(newNode, previousNode);
849 } else {
850 int32_t firstChildRow;
851 if (NS_SUCCEEDED(GetFirstDescendantOf(parentNode, parentRow, &firstChildRow))) {
852 inDOMViewNode* firstChild;
853 RowToNode(firstChildRow, &firstChild);
854 InsertLinkBefore(newNode, firstChild);
855 }
856 }
858 // insert new node
859 InsertNode(newNode, row);
861 mTree->RowCountChanged(row, 1);
862 }
864 void
865 inDOMView::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
866 nsIContent* aChild, int32_t aIndexInContainer,
867 nsIContent* aPreviousSibling)
868 {
869 if (!mTree)
870 return;
872 nsresult rv;
874 // find the inDOMViewNode for the old child
875 nsCOMPtr<nsIDOMNode> oldDOMNode(do_QueryInterface(aChild));
876 int32_t row = 0;
877 if (NS_FAILED(rv = NodeToRow(oldDOMNode, &row)))
878 return;
879 inDOMViewNode* oldNode;
880 if (NS_FAILED(rv = RowToNode(row, &oldNode)))
881 return;
883 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
885 // The parent may no longer be a container. Note that we don't want
886 // to access oldNode after calling RemoveNode, so do this now.
887 inDOMViewNode* parentNode = oldNode->parent;
888 bool isOnlyChild = oldNode->previous == nullptr && oldNode->next == nullptr;
890 // Keep track of how many rows we are removing. It's at least one,
891 // but if we're open it's more.
892 int32_t oldCount = GetRowCount();
894 if (oldNode->isOpen)
895 CollapseNode(row);
897 RemoveLink(oldNode);
898 RemoveNode(row);
900 if (isOnlyChild) {
901 // Fix up the parent
902 parentNode->isContainer = false;
903 parentNode->isOpen = false;
904 mTree->InvalidateRow(NodeToRow(parentNode));
905 }
907 mTree->RowCountChanged(row, GetRowCount() - oldCount);
908 }
910 ///////////////////////////////////////////////////////////////////////
911 // inDOMView
913 //////// NODE MANAGEMENT
915 inDOMViewNode*
916 inDOMView::GetNodeAt(int32_t aRow)
917 {
918 return mNodes.ElementAt(aRow);
919 }
921 int32_t
922 inDOMView::GetRowCount()
923 {
924 return mNodes.Length();
925 }
927 int32_t
928 inDOMView::NodeToRow(inDOMViewNode* aNode)
929 {
930 return mNodes.IndexOf(aNode);
931 }
933 inDOMViewNode*
934 inDOMView::CreateNode(nsIDOMNode* aNode, inDOMViewNode* aParent)
935 {
936 inDOMViewNode* viewNode = new inDOMViewNode(aNode);
937 viewNode->level = aParent ? aParent->level+1 : 0;
938 viewNode->parent = aParent;
940 nsCOMArray<nsIDOMNode> grandKids;
941 GetChildNodesFor(aNode, grandKids);
942 viewNode->isContainer = (grandKids.Count() > 0);
943 return viewNode;
944 }
946 bool
947 inDOMView::RowOutOfBounds(int32_t aRow, int32_t aCount)
948 {
949 return aRow < 0 || aRow >= GetRowCount() || aCount+aRow > GetRowCount();
950 }
952 void
953 inDOMView::AppendNode(inDOMViewNode* aNode)
954 {
955 mNodes.AppendElement(aNode);
956 }
958 void
959 inDOMView::InsertNode(inDOMViewNode* aNode, int32_t aRow)
960 {
961 if (RowOutOfBounds(aRow, 1))
962 AppendNode(aNode);
963 else
964 mNodes.InsertElementAt(aRow, aNode);
965 }
967 void
968 inDOMView::RemoveNode(int32_t aRow)
969 {
970 if (RowOutOfBounds(aRow, 1))
971 return;
973 delete GetNodeAt(aRow);
974 mNodes.RemoveElementAt(aRow);
975 }
977 void
978 inDOMView::ReplaceNode(inDOMViewNode* aNode, int32_t aRow)
979 {
980 if (RowOutOfBounds(aRow, 1))
981 return;
983 delete GetNodeAt(aRow);
984 mNodes.ElementAt(aRow) = aNode;
985 }
987 void
988 inDOMView::InsertNodes(nsTArray<inDOMViewNode*>& aNodes, int32_t aRow)
989 {
990 if (aRow < 0 || aRow > GetRowCount())
991 return;
993 mNodes.InsertElementsAt(aRow, aNodes);
994 }
996 void
997 inDOMView::RemoveNodes(int32_t aRow, int32_t aCount)
998 {
999 if (aRow < 0)
1000 return;
1002 int32_t rowCount = GetRowCount();
1003 for (int32_t i = aRow; i < aRow+aCount && i < rowCount; ++i) {
1004 delete GetNodeAt(i);
1005 }
1007 mNodes.RemoveElementsAt(aRow, aCount);
1008 }
1010 void
1011 inDOMView::RemoveAllNodes()
1012 {
1013 int32_t rowCount = GetRowCount();
1014 for (int32_t i = 0; i < rowCount; ++i) {
1015 delete GetNodeAt(i);
1016 }
1018 mNodes.Clear();
1019 }
1021 void
1022 inDOMView::ExpandNode(int32_t aRow)
1023 {
1024 inDOMViewNode* node = nullptr;
1025 RowToNode(aRow, &node);
1027 nsCOMArray<nsIDOMNode> kids;
1028 GetChildNodesFor(node ? node->node : mRootNode,
1029 kids);
1030 int32_t kidCount = kids.Count();
1032 nsTArray<inDOMViewNode*> list(kidCount);
1034 inDOMViewNode* newNode = nullptr;
1035 inDOMViewNode* prevNode = nullptr;
1037 for (int32_t i = 0; i < kidCount; ++i) {
1038 newNode = CreateNode(kids[i], node);
1039 list.AppendElement(newNode);
1041 if (prevNode)
1042 prevNode->next = newNode;
1043 newNode->previous = prevNode;
1044 prevNode = newNode;
1045 }
1047 InsertNodes(list, aRow+1);
1049 if (node)
1050 node->isOpen = true;
1051 }
1053 void
1054 inDOMView::CollapseNode(int32_t aRow)
1055 {
1056 inDOMViewNode* node = nullptr;
1057 nsresult rv = RowToNode(aRow, &node);
1058 if (NS_FAILED(rv)) {
1059 return;
1060 }
1062 int32_t row = 0;
1063 GetLastDescendantOf(node, aRow, &row);
1065 RemoveNodes(aRow+1, row-aRow);
1067 node->isOpen = false;
1068 }
1070 //////// NODE AND ROW CONVERSION
1072 nsresult
1073 inDOMView::RowToNode(int32_t aRow, inDOMViewNode** aNode)
1074 {
1075 if (aRow < 0 || aRow >= GetRowCount())
1076 return NS_ERROR_FAILURE;
1078 *aNode = GetNodeAt(aRow);
1079 return NS_OK;
1080 }
1082 nsresult
1083 inDOMView::NodeToRow(nsIDOMNode* aNode, int32_t* aRow)
1084 {
1085 int32_t rowCount = GetRowCount();
1086 for (int32_t i = 0; i < rowCount; ++i) {
1087 if (GetNodeAt(i)->node == aNode) {
1088 *aRow = i;
1089 return NS_OK;
1090 }
1091 }
1093 *aRow = -1;
1094 return NS_ERROR_FAILURE;
1095 }
1097 //////// NODE HIERARCHY MUTATION
1099 void
1100 inDOMView::InsertLinkAfter(inDOMViewNode* aNode, inDOMViewNode* aInsertAfter)
1101 {
1102 if (aInsertAfter->next)
1103 aInsertAfter->next->previous = aNode;
1104 aNode->next = aInsertAfter->next;
1105 aInsertAfter->next = aNode;
1106 aNode->previous = aInsertAfter;
1107 }
1109 void
1110 inDOMView::InsertLinkBefore(inDOMViewNode* aNode, inDOMViewNode* aInsertBefore)
1111 {
1112 if (aInsertBefore->previous)
1113 aInsertBefore->previous->next = aNode;
1114 aNode->previous = aInsertBefore->previous;
1115 aInsertBefore->previous = aNode;
1116 aNode->next = aInsertBefore;
1117 }
1119 void
1120 inDOMView::RemoveLink(inDOMViewNode* aNode)
1121 {
1122 if (aNode->previous)
1123 aNode->previous->next = aNode->next;
1124 if (aNode->next)
1125 aNode->next->previous = aNode->previous;
1126 }
1128 void
1129 inDOMView::ReplaceLink(inDOMViewNode* aNewNode, inDOMViewNode* aOldNode)
1130 {
1131 if (aOldNode->previous)
1132 aOldNode->previous->next = aNewNode;
1133 if (aOldNode->next)
1134 aOldNode->next->previous = aNewNode;
1135 aNewNode->next = aOldNode->next;
1136 aNewNode->previous = aOldNode->previous;
1137 }
1139 //////// NODE HIERARCHY UTILITIES
1141 nsresult
1142 inDOMView::GetFirstDescendantOf(inDOMViewNode* aNode, int32_t aRow, int32_t* aResult)
1143 {
1144 // get the first node that is a descendant of the previous sibling
1145 int32_t row = 0;
1146 inDOMViewNode* node;
1147 for (row = aRow+1; row < GetRowCount(); ++row) {
1148 node = GetNodeAt(row);
1149 if (node->parent == aNode) {
1150 *aResult = row;
1151 return NS_OK;
1152 }
1153 if (node->level <= aNode->level)
1154 break;
1155 }
1156 return NS_ERROR_FAILURE;
1157 }
1159 nsresult
1160 inDOMView::GetLastDescendantOf(inDOMViewNode* aNode, int32_t aRow, int32_t* aResult)
1161 {
1162 // get the last node that is a descendant of the previous sibling
1163 int32_t row = 0;
1164 for (row = aRow+1; row < GetRowCount(); ++row) {
1165 if (GetNodeAt(row)->level <= aNode->level)
1166 break;
1167 }
1168 *aResult = row-1;
1169 return NS_OK;
1170 }
1172 //////// DOM UTILITIES
1174 nsresult
1175 inDOMView::GetChildNodesFor(nsIDOMNode* aNode, nsCOMArray<nsIDOMNode>& aResult)
1176 {
1177 NS_ENSURE_ARG(aNode);
1178 // attribute nodes
1179 if (mWhatToShow & nsIDOMNodeFilter::SHOW_ATTRIBUTE) {
1180 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
1181 if (element) {
1182 nsCOMPtr<nsIDOMMozNamedAttrMap> attrs;
1183 element->GetAttributes(getter_AddRefs(attrs));
1184 if (attrs) {
1185 AppendAttrsToArray(attrs, aResult);
1186 }
1187 }
1188 }
1190 if (mWhatToShow & nsIDOMNodeFilter::SHOW_ELEMENT) {
1191 nsCOMPtr<nsIDOMNodeList> kids;
1192 if (!mDOMUtils) {
1193 mDOMUtils = services::GetInDOMUtils();
1194 if (!mDOMUtils) {
1195 return NS_ERROR_FAILURE;
1196 }
1197 }
1199 mDOMUtils->GetChildrenForNode(aNode, mShowAnonymous,
1200 getter_AddRefs(kids));
1202 if (kids) {
1203 AppendKidsToArray(kids, aResult);
1204 }
1205 }
1207 if (mShowSubDocuments) {
1208 nsCOMPtr<nsIDOMNode> domdoc =
1209 do_QueryInterface(inLayoutUtils::GetSubDocumentFor(aNode));
1210 if (domdoc) {
1211 aResult.AppendObject(domdoc);
1212 }
1213 }
1215 return NS_OK;
1216 }
1218 nsresult
1219 inDOMView::GetRealPreviousSibling(nsIDOMNode* aNode, nsIDOMNode* aRealParent, nsIDOMNode** aSibling)
1220 {
1221 // XXXjrh: This won't work for some cases during some situations where XBL insertion points
1222 // are involved. Fix me!
1223 aNode->GetPreviousSibling(aSibling);
1224 return NS_OK;
1225 }
1227 nsresult
1228 inDOMView::AppendKidsToArray(nsIDOMNodeList* aKids,
1229 nsCOMArray<nsIDOMNode>& aArray)
1230 {
1231 uint32_t l = 0;
1232 aKids->GetLength(&l);
1233 nsCOMPtr<nsIDOMNode> kid;
1234 uint16_t nodeType = 0;
1236 // Try and get DOM Utils in case we don't have one yet.
1237 if (!mShowWhitespaceNodes && !mDOMUtils) {
1238 mDOMUtils = services::GetInDOMUtils();
1239 }
1241 for (uint32_t i = 0; i < l; ++i) {
1242 aKids->Item(i, getter_AddRefs(kid));
1243 kid->GetNodeType(&nodeType);
1245 NS_ASSERTION(nodeType && nodeType <= nsIDOMNode::NOTATION_NODE,
1246 "Unknown node type. "
1247 "Were new types added to the spec?");
1248 // As of DOM Level 2 Core and Traversal, each NodeFilter constant
1249 // is defined as the lower nth bit in the NodeFilter bitmask,
1250 // where n is the numeric constant of the nodeType it represents.
1251 // If this invariant ever changes, we will need to update the
1252 // following line.
1253 uint32_t filterForNodeType = 1 << (nodeType - 1);
1255 if (mWhatToShow & filterForNodeType) {
1256 if ((nodeType == nsIDOMNode::TEXT_NODE ||
1257 nodeType == nsIDOMNode::COMMENT_NODE) &&
1258 !mShowWhitespaceNodes && mDOMUtils) {
1259 nsCOMPtr<nsIDOMCharacterData> data = do_QueryInterface(kid);
1260 NS_ASSERTION(data, "Does not implement nsIDOMCharacterData!");
1261 bool ignore;
1262 mDOMUtils->IsIgnorableWhitespace(data, &ignore);
1263 if (ignore) {
1264 continue;
1265 }
1266 }
1268 aArray.AppendObject(kid);
1269 }
1270 }
1272 return NS_OK;
1273 }
1275 nsresult
1276 inDOMView::AppendAttrsToArray(nsIDOMMozNamedAttrMap* aAttributes,
1277 nsCOMArray<nsIDOMNode>& aArray)
1278 {
1279 uint32_t l = 0;
1280 aAttributes->GetLength(&l);
1281 nsCOMPtr<nsIDOMAttr> attribute;
1282 for (uint32_t i = 0; i < l; ++i) {
1283 aAttributes->Item(i, getter_AddRefs(attribute));
1284 aArray.AppendObject(attribute);
1285 }
1286 return NS_OK;
1287 }