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