1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 #include "nsASN1Tree.h"
5 
6 #include "mozilla/Assertions.h"
7 #include "nsArrayUtils.h"
8 #include "nsDebug.h"
9 #include "nsIMutableArray.h"
10 #include "nsString.h"
11 
NS_IMPL_ISUPPORTS(nsNSSASN1Tree,nsIASN1Tree,nsITreeView)12 NS_IMPL_ISUPPORTS(nsNSSASN1Tree, nsIASN1Tree, nsITreeView)
13 
14 nsNSSASN1Tree::nsNSSASN1Tree()
15   : mTopNode(nullptr)
16 {
17 }
18 
~nsNSSASN1Tree()19 nsNSSASN1Tree::~nsNSSASN1Tree()
20 {
21   ClearNodes();
22 }
23 
24 void
ClearNodesRecursively(myNode * n)25 nsNSSASN1Tree::ClearNodesRecursively(myNode* n)
26 {
27   // Note: |n| is allowed to be null.
28 
29   myNode *walk = n;
30   while (walk) {
31     myNode *kill = walk;
32 
33     if (walk->child) {
34       ClearNodesRecursively(walk->child);
35     }
36 
37     walk = walk->next;
38     delete kill;
39   }
40 }
41 
42 void
ClearNodes()43 nsNSSASN1Tree::ClearNodes()
44 {
45   ClearNodesRecursively(mTopNode);
46   mTopNode = nullptr;
47 }
48 
49 void
InitChildsRecursively(myNode * n)50 nsNSSASN1Tree::InitChildsRecursively(myNode* n)
51 {
52   MOZ_ASSERT(n);
53   if (!n) {
54     return;
55   }
56 
57   if (!n->obj)
58     return;
59 
60   n->seq = do_QueryInterface(n->obj);
61   if (!n->seq)
62     return;
63 
64   // If the object is a sequence, there might still be a reason
65   // why it should not be displayed as a container.
66   // If we decide that it has all the properties to justify
67   // displaying as a container, we will create a new child chain.
68   // If we decide, it does not make sense to display as a container,
69   // we forget that it is a sequence by erasing n->seq.
70   // That way, n->seq and n->child will be either both set or both null.
71 
72   bool isContainer;
73   n->seq->GetIsValidContainer(&isContainer);
74   if (!isContainer) {
75     n->seq = nullptr;
76     return;
77   }
78 
79   nsCOMPtr<nsIMutableArray> asn1Objects;
80   n->seq->GetASN1Objects(getter_AddRefs(asn1Objects));
81   uint32_t numObjects;
82   asn1Objects->GetLength(&numObjects);
83   if (!numObjects) {
84     n->seq = nullptr;
85     return;
86   }
87 
88   myNode *walk = nullptr;
89   myNode *prev = nullptr;
90   for (uint32_t i = 0; i < numObjects; i++) {
91     if (0 == i) {
92       n->child = walk = new myNode;
93     }
94     else {
95       walk = new myNode;
96     }
97 
98     walk->parent = n;
99     if (prev) {
100       prev->next = walk;
101     }
102 
103     walk->obj = do_QueryElementAt(asn1Objects, i);
104 
105     InitChildsRecursively(walk);
106 
107     prev = walk;
108   }
109 }
110 
111 void
InitNodes()112 nsNSSASN1Tree::InitNodes()
113 {
114   ClearNodes();
115 
116   mTopNode = new myNode;
117   mTopNode->obj = mASN1Object;
118 
119   InitChildsRecursively(mTopNode);
120 }
121 
122 NS_IMETHODIMP
LoadASN1Structure(nsIASN1Object * asn1Object)123 nsNSSASN1Tree::LoadASN1Structure(nsIASN1Object* asn1Object)
124 {
125   // Note: |asn1Object| is allowed to be null.
126 
127   // The tree won't automatically re-draw if the contents
128   // have been changed.  So I do a quick test here to let
129   // me know if I should forced the tree to redraw itself
130   // by calling RowCountChanged on it.
131   //
132   bool redraw = (mASN1Object && mTree);
133   int32_t rowsToDelete = 0;
134 
135   if (redraw) {
136     // This is the number of rows we will be deleting after
137     // the contents have changed.
138     rowsToDelete = 0-CountVisibleNodes(mTopNode);
139   }
140 
141   mASN1Object = asn1Object;
142   InitNodes();
143 
144   if (redraw) {
145     // The number of rows in the new content.
146     int32_t newRows = CountVisibleNodes(mTopNode);
147     mTree->BeginUpdateBatch();
148     // Erase all of the old rows.
149     mTree->RowCountChanged(0, rowsToDelete);
150     // Replace them with the new contents
151     mTree->RowCountChanged(0, newRows);
152     mTree->EndUpdateBatch();
153   }
154 
155   return NS_OK;
156 }
157 
158 NS_IMETHODIMP
GetRowCount(int32_t * aRowCount)159 nsNSSASN1Tree::GetRowCount(int32_t* aRowCount)
160 {
161   NS_ENSURE_ARG_POINTER(aRowCount);
162 
163   if (mASN1Object) {
164     *aRowCount = CountVisibleNodes(mTopNode);
165   } else {
166     *aRowCount = 0;
167   }
168   return NS_OK;
169 }
170 
171 NS_IMETHODIMP
GetSelection(nsITreeSelection ** aSelection)172 nsNSSASN1Tree::GetSelection(nsITreeSelection** aSelection)
173 {
174   NS_ENSURE_ARG_POINTER(aSelection);
175   *aSelection = mSelection;
176   NS_IF_ADDREF(*aSelection);
177   return NS_OK;
178 }
179 
180 NS_IMETHODIMP
SetSelection(nsITreeSelection * aSelection)181 nsNSSASN1Tree::SetSelection(nsITreeSelection* aSelection)
182 {
183   // Note: |aSelection| is allowed to be null.
184   mSelection = aSelection;
185   return NS_OK;
186 }
187 
188 NS_IMETHODIMP
GetRowProperties(int32_t,nsAString &)189 nsNSSASN1Tree::GetRowProperties(int32_t, nsAString&)
190 {
191   return NS_OK;
192 }
193 
194 NS_IMETHODIMP
GetCellProperties(int32_t,nsITreeColumn *,nsAString &)195 nsNSSASN1Tree::GetCellProperties(int32_t, nsITreeColumn*, nsAString&)
196 {
197   return NS_OK;
198 }
199 
200 NS_IMETHODIMP
GetColumnProperties(nsITreeColumn *,nsAString &)201 nsNSSASN1Tree::GetColumnProperties(nsITreeColumn*, nsAString&)
202 {
203   return NS_OK;
204 }
205 
206 NS_IMETHODIMP
IsContainer(int32_t index,bool * _retval)207 nsNSSASN1Tree::IsContainer(int32_t index, bool* _retval)
208 {
209   NS_ENSURE_ARG_MIN(index, 0);
210   NS_ENSURE_ARG_POINTER(_retval);
211 
212   myNode *n = FindNodeFromIndex(index);
213   if (!n)
214     return NS_ERROR_FAILURE;
215 
216   *_retval = (n->seq != nullptr);
217   return NS_OK;
218 }
219 
220 NS_IMETHODIMP
IsContainerOpen(int32_t index,bool * _retval)221 nsNSSASN1Tree::IsContainerOpen(int32_t index, bool* _retval)
222 {
223   NS_ENSURE_ARG_MIN(index, 0);
224   NS_ENSURE_ARG_POINTER(_retval);
225 
226   myNode *n = FindNodeFromIndex(index);
227   if (!n || !n->seq)
228     return NS_ERROR_FAILURE;
229 
230   return n->seq->GetIsExpanded(_retval);
231 }
232 
233 NS_IMETHODIMP
IsContainerEmpty(int32_t,bool * _retval)234 nsNSSASN1Tree::IsContainerEmpty(int32_t, bool* _retval)
235 {
236   NS_ENSURE_ARG_POINTER(_retval);
237   *_retval = false;
238   return NS_OK;
239 }
240 
241 NS_IMETHODIMP
IsSeparator(int32_t,bool * _retval)242 nsNSSASN1Tree::IsSeparator(int32_t, bool* _retval)
243 {
244   NS_ENSURE_ARG_POINTER(_retval);
245   *_retval = false;
246   return NS_OK;
247 }
248 
249 NS_IMETHODIMP
GetLevel(int32_t index,int32_t * _retval)250 nsNSSASN1Tree::GetLevel(int32_t index, int32_t* _retval)
251 {
252   NS_ENSURE_ARG_MIN(index, 0);
253   NS_ENSURE_ARG_POINTER(_retval);
254 
255   int32_t nodeLevel;
256   myNode* n = FindNodeFromIndex(index, nullptr, &nodeLevel);
257   if (!n)
258     return NS_ERROR_FAILURE;
259 
260   *_retval = nodeLevel;
261   return NS_OK;
262 }
263 
264 NS_IMETHODIMP
GetImageSrc(int32_t,nsITreeColumn *,nsAString &)265 nsNSSASN1Tree::GetImageSrc(int32_t, nsITreeColumn*, nsAString&)
266 {
267   return NS_OK;
268 }
269 
270 NS_IMETHODIMP
GetProgressMode(int32_t,nsITreeColumn *,int32_t *)271 nsNSSASN1Tree::GetProgressMode(int32_t, nsITreeColumn*, int32_t*)
272 {
273   return NS_OK;
274 }
275 
276 NS_IMETHODIMP
GetCellValue(int32_t,nsITreeColumn *,nsAString &)277 nsNSSASN1Tree::GetCellValue(int32_t, nsITreeColumn*, nsAString&)
278 {
279   return NS_OK;
280 }
281 
282 NS_IMETHODIMP
GetCellText(int32_t row,nsITreeColumn *,nsAString & _retval)283 nsNSSASN1Tree::GetCellText(int32_t row, nsITreeColumn*, nsAString& _retval)
284 {
285   NS_ENSURE_ARG_MIN(row, 0);
286 
287   _retval.Truncate();
288 
289   myNode* n = FindNodeFromIndex(row);
290   if (!n)
291     return NS_ERROR_FAILURE;
292 
293   // There's only one column for ASN1 dump.
294   return n->obj->GetDisplayName(_retval);
295 }
296 
297 NS_IMETHODIMP
GetDisplayData(uint32_t index,nsAString & _retval)298 nsNSSASN1Tree::GetDisplayData(uint32_t index, nsAString& _retval)
299 {
300   myNode *n = FindNodeFromIndex(index);
301   if (!n)
302     return NS_ERROR_FAILURE;
303 
304   return n->obj->GetDisplayValue(_retval);
305 }
306 
307 NS_IMETHODIMP
SetTree(nsITreeBoxObject * tree)308 nsNSSASN1Tree::SetTree(nsITreeBoxObject* tree)
309 {
310   // Note: |tree| is allowed to be null.
311   mTree = tree;
312   return NS_OK;
313 }
314 
315 NS_IMETHODIMP
ToggleOpenState(int32_t index)316 nsNSSASN1Tree::ToggleOpenState(int32_t index)
317 {
318   NS_ENSURE_ARG_MIN(index, 0);
319 
320   myNode *n = FindNodeFromIndex(index);
321   if (!n)
322     return NS_ERROR_FAILURE;
323 
324   if (!n->seq)
325     return NS_ERROR_FAILURE;
326 
327   bool IsExpanded;
328   n->seq->GetIsExpanded(&IsExpanded);
329   int32_t rowCountChange;
330   if (IsExpanded) {
331     rowCountChange = -CountVisibleNodes(n->child);
332     n->seq->SetIsExpanded(false);
333   } else {
334     n->seq->SetIsExpanded(true);
335     rowCountChange = CountVisibleNodes(n->child);
336   }
337   if (mTree)
338     mTree->RowCountChanged(index, rowCountChange);
339   return NS_OK;
340 }
341 
342 NS_IMETHODIMP
CycleHeader(nsITreeColumn *)343 nsNSSASN1Tree::CycleHeader(nsITreeColumn*)
344 {
345   return NS_OK;
346 }
347 
348 NS_IMETHODIMP
SelectionChanged()349 nsNSSASN1Tree::SelectionChanged()
350 {
351   return NS_ERROR_NOT_IMPLEMENTED;
352 }
353 
354 NS_IMETHODIMP
CycleCell(int32_t,nsITreeColumn *)355 nsNSSASN1Tree::CycleCell(int32_t, nsITreeColumn*)
356 {
357   return NS_OK;
358 }
359 
360 NS_IMETHODIMP
IsEditable(int32_t,nsITreeColumn *,bool * _retval)361 nsNSSASN1Tree::IsEditable(int32_t, nsITreeColumn*, bool* _retval)
362 {
363   NS_ENSURE_ARG_POINTER(_retval);
364   *_retval = false;
365   return NS_OK;
366 }
367 
368 NS_IMETHODIMP
IsSelectable(int32_t,nsITreeColumn *,bool * _retval)369 nsNSSASN1Tree::IsSelectable(int32_t, nsITreeColumn*, bool* _retval)
370 {
371   NS_ENSURE_ARG_POINTER(_retval);
372   *_retval = false;
373   return NS_OK;
374 }
375 
376 NS_IMETHODIMP
SetCellValue(int32_t,nsITreeColumn *,const nsAString &)377 nsNSSASN1Tree::SetCellValue(int32_t, nsITreeColumn*, const nsAString&)
378 {
379   return NS_OK;
380 }
381 
382 NS_IMETHODIMP
SetCellText(int32_t,nsITreeColumn *,const nsAString &)383 nsNSSASN1Tree::SetCellText(int32_t, nsITreeColumn*, const nsAString&)
384 {
385   return NS_OK;
386 }
387 
388 NS_IMETHODIMP
PerformAction(const char16_t *)389 nsNSSASN1Tree::PerformAction(const char16_t*)
390 {
391   return NS_OK;
392 }
393 
394 NS_IMETHODIMP
PerformActionOnRow(const char16_t *,int32_t)395 nsNSSASN1Tree::PerformActionOnRow(const char16_t*, int32_t)
396 {
397   return NS_OK;
398 }
399 
400 NS_IMETHODIMP
PerformActionOnCell(const char16_t *,int32_t,nsITreeColumn *)401 nsNSSASN1Tree::PerformActionOnCell(const char16_t*, int32_t, nsITreeColumn*)
402 {
403   return NS_OK;
404 }
405 
406 NS_IMETHODIMP
CanDrop(int32_t,int32_t,nsIDOMDataTransfer *,bool * _retval)407 nsNSSASN1Tree::CanDrop(int32_t, int32_t, nsIDOMDataTransfer*, bool* _retval)
408 {
409   NS_ENSURE_ARG_POINTER(_retval);
410   *_retval = false;
411   return NS_OK;
412 }
413 
414 NS_IMETHODIMP
Drop(int32_t,int32_t,nsIDOMDataTransfer *)415 nsNSSASN1Tree::Drop(int32_t, int32_t, nsIDOMDataTransfer*)
416 {
417   return NS_OK;
418 }
419 
420 NS_IMETHODIMP
IsSorted(bool * _retval)421 nsNSSASN1Tree::IsSorted(bool* _retval)
422 {
423   NS_ENSURE_ARG_POINTER(_retval);
424   *_retval = false;
425   return NS_OK;
426 }
427 
428 NS_IMETHODIMP
GetParentIndex(int32_t rowIndex,int32_t * _retval)429 nsNSSASN1Tree::GetParentIndex(int32_t rowIndex, int32_t* _retval)
430 {
431   NS_ENSURE_ARG_MIN(rowIndex, 0);
432   NS_ENSURE_ARG_POINTER(_retval);
433 
434   int32_t parentIndex = -1;
435 
436   myNode *n = FindNodeFromIndex(rowIndex, &parentIndex);
437   if (!n)
438     return NS_ERROR_FAILURE;
439 
440   *_retval = parentIndex;
441   return NS_OK;
442 }
443 
444 NS_IMETHODIMP
HasNextSibling(int32_t rowIndex,int32_t afterIndex,bool * _retval)445 nsNSSASN1Tree::HasNextSibling(int32_t rowIndex, int32_t afterIndex,
446                               bool* _retval)
447 {
448   NS_ENSURE_ARG_MIN(rowIndex, 0);
449   NS_ENSURE_ARG_MIN(afterIndex, 0);
450   NS_ENSURE_ARG_POINTER(_retval);
451 
452   myNode *n = FindNodeFromIndex(rowIndex);
453   if (!n)
454     return NS_ERROR_FAILURE;
455 
456   if (!n->next) {
457     *_retval = false;
458   }
459   else {
460     int32_t nTotalSize = CountVisibleNodes(n);
461     int32_t nLastChildPos = rowIndex + nTotalSize -1;
462     int32_t nextSiblingPos = nLastChildPos +1;
463     *_retval = (nextSiblingPos > afterIndex);
464   }
465 
466   return NS_OK;
467 }
468 
469 int32_t
CountVisibleNodes(myNode * n)470 nsNSSASN1Tree::CountVisibleNodes(myNode* n)
471 {
472   if (!n)
473     return 0;
474 
475   myNode *walk = n;
476   int32_t count = 0;
477   while (walk) {
478     ++count;
479 
480     if (walk->seq) {
481       bool IsExpanded;
482       walk->seq->GetIsExpanded(&IsExpanded);
483       if (IsExpanded) {
484         count += CountVisibleNodes(walk->child);
485       }
486     }
487 
488     walk = walk->next;
489   }
490 
491   return count;
492 }
493 
494 // Entry point for find
495 nsNSSASN1Tree::myNode*
FindNodeFromIndex(int32_t wantedIndex,int32_t * optionalOutParentIndex,int32_t * optionalOutLevel)496 nsNSSASN1Tree::FindNodeFromIndex(int32_t wantedIndex,
497                                  int32_t* optionalOutParentIndex,
498                                  int32_t* optionalOutLevel)
499 {
500   MOZ_ASSERT(wantedIndex >= 0);
501   if (wantedIndex < 0) {
502     return nullptr;
503   }
504 
505   if (0 == wantedIndex) {
506     if (optionalOutLevel) {
507       *optionalOutLevel = 0;
508     }
509     if (optionalOutParentIndex) {
510       *optionalOutParentIndex = -1;
511     }
512     return mTopNode;
513   }
514 
515   int32_t index = 0;
516   int32_t level = 0;
517   return FindNodeFromIndex(mTopNode, wantedIndex, index, level,
518                            optionalOutParentIndex, optionalOutLevel);
519 }
520 
521 // Internal recursive helper function
522 nsNSSASN1Tree::myNode*
FindNodeFromIndex(myNode * n,int32_t wantedIndex,int32_t & indexCounter,int32_t & levelCounter,int32_t * optionalOutParentIndex,int32_t * optionalOutLevel)523 nsNSSASN1Tree::FindNodeFromIndex(myNode* n, int32_t wantedIndex,
524                                  int32_t& indexCounter, int32_t& levelCounter,
525                                  int32_t* optionalOutParentIndex,
526                                  int32_t* optionalOutLevel)
527 {
528   MOZ_ASSERT(wantedIndex >= 0);
529   MOZ_ASSERT(indexCounter >= 0);
530   MOZ_ASSERT(levelCounter >= 0);
531   if (!n || wantedIndex < 0 || indexCounter < 0 || levelCounter < 0) {
532     return nullptr;
533   }
534 
535   myNode *walk = n;
536   int32_t parentIndex = indexCounter - 1;
537 
538   while (walk) {
539     if (indexCounter == wantedIndex) {
540       if (optionalOutLevel) {
541         *optionalOutLevel = levelCounter;
542       }
543       if (optionalOutParentIndex) {
544         *optionalOutParentIndex = parentIndex;
545       }
546       return walk;
547     }
548 
549     if (walk->seq) {
550       bool IsExpanded;
551       walk->seq->GetIsExpanded(&IsExpanded);
552       if (IsExpanded) {
553         ++indexCounter; // set to walk->child
554 
555         ++levelCounter;
556         myNode* found = FindNodeFromIndex(walk->child, wantedIndex, indexCounter,
557                                           levelCounter, optionalOutParentIndex,
558                                           optionalOutLevel);
559         --levelCounter;
560 
561         if (found)
562           return found;
563       }
564     }
565 
566     walk = walk->next;
567     if (walk) {
568       ++indexCounter;
569     }
570   }
571 
572   return nullptr;
573 }
574