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,nsTreeColumn *,nsAString &)171 nsNSSASN1Tree::GetCellProperties(int32_t, nsTreeColumn*, nsAString&) {
172   return NS_OK;
173 }
174 
175 NS_IMETHODIMP
GetColumnProperties(nsTreeColumn *,nsAString &)176 nsNSSASN1Tree::GetColumnProperties(nsTreeColumn*, 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,nsTreeColumn *,nsAString &)229 nsNSSASN1Tree::GetImageSrc(int32_t, nsTreeColumn*, nsAString&) { return NS_OK; }
230 
231 NS_IMETHODIMP
GetCellValue(int32_t,nsTreeColumn *,nsAString &)232 nsNSSASN1Tree::GetCellValue(int32_t, nsTreeColumn*, nsAString&) {
233   return NS_OK;
234 }
235 
236 NS_IMETHODIMP
GetCellText(int32_t row,nsTreeColumn *,nsAString & _retval)237 nsNSSASN1Tree::GetCellText(int32_t row, nsTreeColumn*, nsAString& _retval) {
238   NS_ENSURE_ARG_MIN(row, 0);
239 
240   _retval.Truncate();
241 
242   myNode* n = FindNodeFromIndex(row);
243   if (!n) return NS_ERROR_FAILURE;
244 
245   // There's only one column for ASN1 dump.
246   return n->obj->GetDisplayName(_retval);
247 }
248 
249 NS_IMETHODIMP
GetDisplayData(uint32_t index,nsAString & _retval)250 nsNSSASN1Tree::GetDisplayData(uint32_t index, nsAString& _retval) {
251   myNode* n = FindNodeFromIndex(index);
252   if (!n) return NS_ERROR_FAILURE;
253 
254   return n->obj->GetDisplayValue(_retval);
255 }
256 
257 NS_IMETHODIMP
SetTree(mozilla::dom::XULTreeElement * tree)258 nsNSSASN1Tree::SetTree(mozilla::dom::XULTreeElement* tree) {
259   // Note: |tree| is allowed to be null.
260   mTree = tree;
261   return NS_OK;
262 }
263 
264 NS_IMETHODIMP
ToggleOpenState(int32_t index)265 nsNSSASN1Tree::ToggleOpenState(int32_t index) {
266   NS_ENSURE_ARG_MIN(index, 0);
267 
268   myNode* n = FindNodeFromIndex(index);
269   if (!n) return NS_ERROR_FAILURE;
270 
271   if (!n->seq) return NS_ERROR_FAILURE;
272 
273   bool IsExpanded;
274   n->seq->GetIsExpanded(&IsExpanded);
275   int32_t rowCountChange;
276   if (IsExpanded) {
277     rowCountChange = -CountVisibleNodes(n->child);
278     n->seq->SetIsExpanded(false);
279   } else {
280     n->seq->SetIsExpanded(true);
281     rowCountChange = CountVisibleNodes(n->child);
282   }
283   if (mTree) mTree->RowCountChanged(index, rowCountChange);
284   return NS_OK;
285 }
286 
287 NS_IMETHODIMP
CycleHeader(nsTreeColumn *)288 nsNSSASN1Tree::CycleHeader(nsTreeColumn*) { return NS_OK; }
289 
290 NS_IMETHODIMP
SelectionChangedXPCOM()291 nsNSSASN1Tree::SelectionChangedXPCOM() { return NS_ERROR_NOT_IMPLEMENTED; }
292 
293 NS_IMETHODIMP
CycleCell(int32_t,nsTreeColumn *)294 nsNSSASN1Tree::CycleCell(int32_t, nsTreeColumn*) { return NS_OK; }
295 
296 NS_IMETHODIMP
IsEditable(int32_t,nsTreeColumn *,bool * _retval)297 nsNSSASN1Tree::IsEditable(int32_t, nsTreeColumn*, bool* _retval) {
298   NS_ENSURE_ARG_POINTER(_retval);
299   *_retval = false;
300   return NS_OK;
301 }
302 
303 NS_IMETHODIMP
SetCellValue(int32_t,nsTreeColumn *,const nsAString &)304 nsNSSASN1Tree::SetCellValue(int32_t, nsTreeColumn*, const nsAString&) {
305   return NS_OK;
306 }
307 
308 NS_IMETHODIMP
SetCellText(int32_t,nsTreeColumn *,const nsAString &)309 nsNSSASN1Tree::SetCellText(int32_t, nsTreeColumn*, const nsAString&) {
310   return NS_OK;
311 }
312 
313 NS_IMETHODIMP
CanDrop(int32_t,int32_t,mozilla::dom::DataTransfer *,bool * _retval)314 nsNSSASN1Tree::CanDrop(int32_t, int32_t, mozilla::dom::DataTransfer*,
315                        bool* _retval) {
316   NS_ENSURE_ARG_POINTER(_retval);
317   *_retval = false;
318   return NS_OK;
319 }
320 
321 NS_IMETHODIMP
Drop(int32_t,int32_t,mozilla::dom::DataTransfer *)322 nsNSSASN1Tree::Drop(int32_t, int32_t, mozilla::dom::DataTransfer*) {
323   return NS_OK;
324 }
325 
326 NS_IMETHODIMP
IsSorted(bool * _retval)327 nsNSSASN1Tree::IsSorted(bool* _retval) {
328   NS_ENSURE_ARG_POINTER(_retval);
329   *_retval = false;
330   return NS_OK;
331 }
332 
333 NS_IMETHODIMP
GetParentIndex(int32_t rowIndex,int32_t * _retval)334 nsNSSASN1Tree::GetParentIndex(int32_t rowIndex, int32_t* _retval) {
335   NS_ENSURE_ARG_MIN(rowIndex, 0);
336   NS_ENSURE_ARG_POINTER(_retval);
337 
338   int32_t parentIndex = -1;
339 
340   myNode* n = FindNodeFromIndex(rowIndex, &parentIndex);
341   if (!n) return NS_ERROR_FAILURE;
342 
343   *_retval = parentIndex;
344   return NS_OK;
345 }
346 
347 NS_IMETHODIMP
HasNextSibling(int32_t rowIndex,int32_t afterIndex,bool * _retval)348 nsNSSASN1Tree::HasNextSibling(int32_t rowIndex, int32_t afterIndex,
349                               bool* _retval) {
350   NS_ENSURE_ARG_MIN(rowIndex, 0);
351   NS_ENSURE_ARG_MIN(afterIndex, 0);
352   NS_ENSURE_ARG_POINTER(_retval);
353 
354   myNode* n = FindNodeFromIndex(rowIndex);
355   if (!n) return NS_ERROR_FAILURE;
356 
357   if (!n->next) {
358     *_retval = false;
359   } else {
360     int32_t nTotalSize = CountVisibleNodes(n);
361     int32_t nLastChildPos = rowIndex + nTotalSize - 1;
362     int32_t nextSiblingPos = nLastChildPos + 1;
363     *_retval = (nextSiblingPos > afterIndex);
364   }
365 
366   return NS_OK;
367 }
368 
CountVisibleNodes(myNode * n)369 int32_t nsNSSASN1Tree::CountVisibleNodes(myNode* n) {
370   if (!n) return 0;
371 
372   myNode* walk = n;
373   int32_t count = 0;
374   while (walk) {
375     ++count;
376 
377     if (walk->seq) {
378       bool IsExpanded;
379       walk->seq->GetIsExpanded(&IsExpanded);
380       if (IsExpanded) {
381         count += CountVisibleNodes(walk->child);
382       }
383     }
384 
385     walk = walk->next;
386   }
387 
388   return count;
389 }
390 
391 // Entry point for find
FindNodeFromIndex(int32_t wantedIndex,int32_t * optionalOutParentIndex,int32_t * optionalOutLevel)392 nsNSSASN1Tree::myNode* nsNSSASN1Tree::FindNodeFromIndex(
393     int32_t wantedIndex, int32_t* optionalOutParentIndex,
394     int32_t* optionalOutLevel) {
395   MOZ_ASSERT(wantedIndex >= 0);
396   if (wantedIndex < 0) {
397     return nullptr;
398   }
399 
400   if (0 == wantedIndex) {
401     if (optionalOutLevel) {
402       *optionalOutLevel = 0;
403     }
404     if (optionalOutParentIndex) {
405       *optionalOutParentIndex = -1;
406     }
407     return mTopNode;
408   }
409 
410   int32_t index = 0;
411   int32_t level = 0;
412   return FindNodeFromIndex(mTopNode, wantedIndex, index, level,
413                            optionalOutParentIndex, optionalOutLevel);
414 }
415 
416 // Internal recursive helper function
FindNodeFromIndex(myNode * n,int32_t wantedIndex,int32_t & indexCounter,int32_t & levelCounter,int32_t * optionalOutParentIndex,int32_t * optionalOutLevel)417 nsNSSASN1Tree::myNode* nsNSSASN1Tree::FindNodeFromIndex(
418     myNode* n, int32_t wantedIndex, int32_t& indexCounter,
419     int32_t& levelCounter, int32_t* optionalOutParentIndex,
420     int32_t* optionalOutLevel) {
421   MOZ_ASSERT(wantedIndex >= 0);
422   MOZ_ASSERT(indexCounter >= 0);
423   MOZ_ASSERT(levelCounter >= 0);
424   if (!n || wantedIndex < 0 || indexCounter < 0 || levelCounter < 0) {
425     return nullptr;
426   }
427 
428   myNode* walk = n;
429   int32_t parentIndex = indexCounter - 1;
430 
431   while (walk) {
432     if (indexCounter == wantedIndex) {
433       if (optionalOutLevel) {
434         *optionalOutLevel = levelCounter;
435       }
436       if (optionalOutParentIndex) {
437         *optionalOutParentIndex = parentIndex;
438       }
439       return walk;
440     }
441 
442     if (walk->seq) {
443       bool IsExpanded;
444       walk->seq->GetIsExpanded(&IsExpanded);
445       if (IsExpanded) {
446         ++indexCounter;  // set to walk->child
447 
448         ++levelCounter;
449         myNode* found = FindNodeFromIndex(
450             walk->child, wantedIndex, indexCounter, levelCounter,
451             optionalOutParentIndex, optionalOutLevel);
452         --levelCounter;
453 
454         if (found) return found;
455       }
456     }
457 
458     walk = walk->next;
459     if (walk) {
460       ++indexCounter;
461     }
462   }
463 
464   return nullptr;
465 }
466