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