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