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