no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / security / manager / pki / resources / content / certManager.js
blob660240ff560d824b8c416d8fd74c621c2812b4c3
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 /* import-globals-from pippki.js */
5 "use strict";
7 const gCertFileTypes = "*.p7b; *.crt; *.cert; *.cer; *.pem; *.der";
9 var { NetUtil } = ChromeUtils.importESModule(
10   "resource://gre/modules/NetUtil.sys.mjs"
13 var key;
15 var certdialogs = Cc["@mozilla.org/nsCertificateDialogs;1"].getService(
16   Ci.nsICertificateDialogs
19 /**
20  * List of certs currently selected in the active tab.
21  *
22  * @type {nsIX509Cert[]}
23  */
24 var selected_certs = [];
25 var selected_tree_items = [];
26 var selected_index = [];
27 var certdb;
29 /**
30  * Cert tree for the "Authorities" tab.
31  *
32  * @type {nsICertTree}
33  */
34 var caTreeView;
35 /**
36  * Cert tree for the "Servers" tab.
37  *
38  * @type {nsICertTree}
39  */
40 var serverTreeView;
42 var overrideService;
44 function createRichlistItem(item) {
45   let innerHbox = document.createXULElement("hbox");
46   innerHbox.setAttribute("align", "center");
47   innerHbox.setAttribute("flex", "1");
49   let row = document.createXULElement("label");
50   row.setAttribute("flex", "1");
51   row.setAttribute("crop", "end");
52   row.setAttribute("style", "margin-inline-start: 15px;");
53   if ("raw" in item) {
54     row.setAttribute("value", item.raw);
55   } else {
56     document.l10n.setAttributes(row, item.l10nid);
57   }
58   row.setAttribute("ordinal", "1");
59   innerHbox.appendChild(row);
61   return innerHbox;
64 var serverRichList = {
65   richlist: undefined,
67   buildRichList() {
68     let overrides = overrideService.getOverrides().map(item => {
69       return {
70         hostPort: item.hostPort,
71         asciiHost: item.asciiHost,
72         port: item.port,
73         originAttributes: item.originAttributes,
74         fingerprint: item.fingerprint,
75       };
76     });
77     overrides.sort((a, b) => {
78       let criteria = ["hostPort", "fingerprint"];
79       for (let c of criteria) {
80         let res = a[c].localeCompare(b[c]);
81         if (res !== 0) {
82           return res;
83         }
84       }
85       return 0;
86     });
88     this.richlist.textContent = "";
89     this.richlist.clearSelection();
91     let frag = document.createDocumentFragment();
92     for (let override of overrides) {
93       let richlistitem = this._richBoxAddItem(override);
94       frag.appendChild(richlistitem);
95     }
96     this.richlist.appendChild(frag);
98     this._setButtonState();
99     this.richlist.addEventListener("select", () => this._setButtonState());
100   },
102   _richBoxAddItem(item) {
103     let richlistitem = document.createXULElement("richlistitem");
105     richlistitem.setAttribute("host", item.asciiHost);
106     richlistitem.setAttribute("port", item.port);
107     richlistitem.setAttribute("hostPort", item.hostPort);
108     richlistitem.setAttribute("fingerprint", item.fingerprint);
109     richlistitem.setAttribute(
110       "originAttributes",
111       JSON.stringify(item.originAttributes)
112     );
114     let hbox = document.createXULElement("hbox");
115     hbox.setAttribute("flex", "1");
116     hbox.setAttribute("equalsize", "always");
118     hbox.appendChild(createRichlistItem({ raw: item.hostPort }));
119     hbox.appendChild(createRichlistItem({ raw: item.fingerprint }));
121     richlistitem.appendChild(hbox);
123     return richlistitem;
124   },
126   deleteSelectedRichListItem() {
127     let selectedItem = this.richlist.selectedItem;
128     if (!selectedItem) {
129       return;
130     }
132     let retVals = {
133       deleteConfirmed: false,
134     };
135     window.browsingContext.topChromeWindow.openDialog(
136       "chrome://pippki/content/deletecert.xhtml",
137       "",
138       "chrome,centerscreen,modal",
139       "websites_tab",
140       [
141         {
142           hostPort: selectedItem.attributes.hostPort.value,
143         },
144       ],
145       retVals
146     );
148     if (retVals.deleteConfirmed) {
149       overrideService.clearValidityOverride(
150         selectedItem.attributes.host.value,
151         selectedItem.attributes.port.value,
152         JSON.parse(selectedItem.attributes.originAttributes.value)
153       );
154       this.buildRichList();
155     }
156   },
158   addException() {
159     let retval = {
160       exceptionAdded: false,
161     };
162     window.browsingContext.topChromeWindow.openDialog(
163       "chrome://pippki/content/exceptionDialog.xhtml",
164       "",
165       "chrome,centerscreen,modal",
166       retval
167     );
168     if (retval.exceptionAdded) {
169       this.buildRichList();
170     }
171   },
173   _setButtonState() {
174     let websiteDeleteButton = document.getElementById("websites_deleteButton");
175     websiteDeleteButton.disabled = this.richlist.selectedIndex < 0;
176   },
179  * Cert tree for the "People" tab.
181  * @type {nsICertTree}
182  */
183 var emailTreeView;
185  * Cert tree for the "Your Certificates" tab.
187  * @type {nsICertTree}
188  */
189 var userTreeView;
191 var clientAuthRememberService;
193 var rememberedDecisionsRichList = {
194   richlist: undefined,
196   buildRichList() {
197     let rememberedDecisions = clientAuthRememberService.getDecisions();
199     let oldItems = this.richlist.querySelectorAll("richlistitem");
200     for (let item of oldItems) {
201       item.remove();
202     }
204     let frag = document.createDocumentFragment();
205     for (let decision of rememberedDecisions) {
206       let richlistitem = this._richBoxAddItem(decision);
207       frag.appendChild(richlistitem);
208     }
209     this.richlist.appendChild(frag);
211     this.richlist.addEventListener("select", () => this.setButtonState());
212   },
214   _richBoxAddItem(item) {
215     let richlistitem = document.createXULElement("richlistitem");
217     richlistitem.setAttribute("entryKey", item.entryKey);
218     richlistitem.setAttribute("dbKey", item.dbKey);
220     let hbox = document.createXULElement("hbox");
221     hbox.setAttribute("flex", "1");
222     hbox.setAttribute("equalsize", "always");
224     hbox.appendChild(createRichlistItem({ raw: item.asciiHost }));
225     if (item.dbKey == "") {
226       hbox.appendChild(
227         createRichlistItem({ l10nid: "send-no-client-certificate" })
228       );
230       hbox.appendChild(createRichlistItem({ raw: "" }));
231     } else {
232       let tmpCert = certdb.findCertByDBKey(item.dbKey);
233       // The certificate corresponding to this item's dbKey may not be
234       // available (for example, if it was stored on a token that's been
235       // removed, or if it was deleted).
236       if (tmpCert) {
237         hbox.appendChild(createRichlistItem({ raw: tmpCert.commonName }));
238         hbox.appendChild(createRichlistItem({ raw: tmpCert.serialNumber }));
239       } else {
240         hbox.appendChild(
241           createRichlistItem({ l10nid: "certificate-not-available" })
242         );
243         hbox.appendChild(
244           createRichlistItem({ l10nid: "certificate-not-available" })
245         );
246       }
247     }
249     richlistitem.appendChild(hbox);
251     return richlistitem;
252   },
254   deleteSelectedRichListItem() {
255     let selectedItem = this.richlist.selectedItem;
256     let index = this.richlist.selectedIndex;
257     if (index < 0) {
258       return;
259     }
261     clientAuthRememberService.forgetRememberedDecision(
262       selectedItem.attributes.entryKey.value
263     );
265     this.buildRichList();
266     this.setButtonState();
267   },
269   viewSelectedRichListItem() {
270     let selectedItem = this.richlist.selectedItem;
271     let index = this.richlist.selectedIndex;
272     if (index < 0) {
273       return;
274     }
276     if (selectedItem.attributes.dbKey.value != "") {
277       let cert = certdb.findCertByDBKey(selectedItem.attributes.dbKey.value);
278       viewCertHelper(window, cert);
279     }
280   },
282   setButtonState() {
283     let rememberedDeleteButton = document.getElementById(
284       "remembered_deleteButton"
285     );
286     let rememberedViewButton = document.getElementById("remembered_viewButton");
288     rememberedDeleteButton.disabled = this.richlist.selectedIndex < 0;
289     rememberedViewButton.disabled =
290       this.richlist.selectedItem == null
291         ? true
292         : this.richlist.selectedItem.attributes.dbKey.value == "";
293   },
296 function LoadCerts() {
297   certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
298     Ci.nsIX509CertDB
299   );
300   var certcache = certdb.getCerts();
302   caTreeView = Cc["@mozilla.org/security/nsCertTree;1"].createInstance(
303     Ci.nsICertTree
304   );
305   caTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.CA_CERT);
306   document.getElementById("ca-tree").view = caTreeView;
308   emailTreeView = Cc["@mozilla.org/security/nsCertTree;1"].createInstance(
309     Ci.nsICertTree
310   );
311   emailTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.EMAIL_CERT);
312   document.getElementById("email-tree").view = emailTreeView;
314   userTreeView = Cc["@mozilla.org/security/nsCertTree;1"].createInstance(
315     Ci.nsICertTree
316   );
317   userTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.USER_CERT);
318   document.getElementById("user-tree").view = userTreeView;
320   clientAuthRememberService = Cc[
321     "@mozilla.org/security/clientAuthRememberService;1"
322   ].getService(Ci.nsIClientAuthRememberService);
324   overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(
325     Ci.nsICertOverrideService
326   );
328   rememberedDecisionsRichList.richlist =
329     document.getElementById("rememberedList");
330   serverRichList.richlist = document.getElementById("serverList");
332   rememberedDecisionsRichList.buildRichList();
333   serverRichList.buildRichList();
335   rememberedDecisionsRichList.setButtonState();
337   enableBackupAllButton();
340 function enableBackupAllButton() {
341   let backupAllButton = document.getElementById("mine_backupAllButton");
342   backupAllButton.disabled = userTreeView.rowCount < 1;
345 function getSelectedCerts() {
346   var ca_tab = document.getElementById("ca_tab");
347   var mine_tab = document.getElementById("mine_tab");
348   var others_tab = document.getElementById("others_tab");
349   var items = null;
350   if (ca_tab.selected) {
351     items = caTreeView.selection;
352   } else if (mine_tab.selected) {
353     items = userTreeView.selection;
354   } else if (others_tab.selected) {
355     items = emailTreeView.selection;
356   }
357   selected_certs = [];
358   var cert = null;
359   var nr = 0;
360   if (items != null) {
361     nr = items.getRangeCount();
362   }
363   if (nr > 0) {
364     for (let i = 0; i < nr; i++) {
365       var o1 = {};
366       var o2 = {};
367       items.getRangeAt(i, o1, o2);
368       var min = o1.value;
369       var max = o2.value;
370       for (let j = min; j <= max; j++) {
371         if (ca_tab.selected) {
372           cert = caTreeView.getCert(j);
373         } else if (mine_tab.selected) {
374           cert = userTreeView.getCert(j);
375         } else if (others_tab.selected) {
376           cert = emailTreeView.getCert(j);
377         }
378         if (cert) {
379           var sc = selected_certs.length;
380           selected_certs[sc] = cert;
381           selected_index[sc] = j;
382         }
383       }
384     }
385   }
388 function getSelectedTreeItems() {
389   var ca_tab = document.getElementById("ca_tab");
390   var mine_tab = document.getElementById("mine_tab");
391   var others_tab = document.getElementById("others_tab");
392   var items = null;
393   if (ca_tab.selected) {
394     items = caTreeView.selection;
395   } else if (mine_tab.selected) {
396     items = userTreeView.selection;
397   } else if (others_tab.selected) {
398     items = emailTreeView.selection;
399   }
400   selected_certs = [];
401   selected_tree_items = [];
402   selected_index = [];
403   var tree_item = null;
404   var nr = 0;
405   if (items != null) {
406     nr = items.getRangeCount();
407   }
408   if (nr > 0) {
409     for (let i = 0; i < nr; i++) {
410       var o1 = {};
411       var o2 = {};
412       items.getRangeAt(i, o1, o2);
413       var min = o1.value;
414       var max = o2.value;
415       for (let j = min; j <= max; j++) {
416         if (ca_tab.selected) {
417           tree_item = caTreeView.getTreeItem(j);
418         } else if (mine_tab.selected) {
419           tree_item = userTreeView.getTreeItem(j);
420         } else if (others_tab.selected) {
421           tree_item = emailTreeView.getTreeItem(j);
422         }
423         if (tree_item) {
424           var sc = selected_tree_items.length;
425           selected_tree_items[sc] = tree_item;
426           selected_index[sc] = j;
427         }
428       }
429     }
430   }
434  * Returns true if nothing in the given cert tree is selected or if the
435  * selection includes a container. Returns false otherwise.
437  * @param {nsICertTree} certTree
438  * @returns {boolean}
439  */
440 function nothingOrContainerSelected(certTree) {
441   var certTreeSelection = certTree.selection;
442   var numSelectionRanges = certTreeSelection.getRangeCount();
444   if (numSelectionRanges == 0) {
445     return true;
446   }
448   for (var i = 0; i < numSelectionRanges; i++) {
449     var o1 = {};
450     var o2 = {};
451     certTreeSelection.getRangeAt(i, o1, o2);
452     var minIndex = o1.value;
453     var maxIndex = o2.value;
454     for (var j = minIndex; j <= maxIndex; j++) {
455       if (certTree.isContainer(j)) {
456         return true;
457       }
458     }
459   }
461   return false;
464 async function promptError(aErrorCode) {
465   if (aErrorCode != Ci.nsIX509CertDB.Success) {
466     let msgName = "pkcs12-unknown-err";
467     switch (aErrorCode) {
468       case Ci.nsIX509CertDB.ERROR_PKCS12_NOSMARTCARD_EXPORT:
469         msgName = "pkcs12-info-no-smartcard-backup";
470         break;
471       case Ci.nsIX509CertDB.ERROR_PKCS12_RESTORE_FAILED:
472         msgName = "pkcs12-unknown-err-restore";
473         break;
474       case Ci.nsIX509CertDB.ERROR_PKCS12_BACKUP_FAILED:
475         msgName = "pkcs12-unknown-err-backup";
476         break;
477       case Ci.nsIX509CertDB.ERROR_PKCS12_CERT_COLLISION:
478       case Ci.nsIX509CertDB.ERROR_PKCS12_DUPLICATE_DATA:
479         msgName = "pkcs12-dup-data";
480         break;
481       case Ci.nsIX509CertDB.ERROR_BAD_PASSWORD:
482         msgName = "pk11-bad-password";
483         break;
484       case Ci.nsIX509CertDB.ERROR_DECODE_ERROR:
485         msgName = "pkcs12-decode-err";
486         break;
487       default:
488         break;
489     }
490     let [message] = await document.l10n.formatValues([{ id: msgName }]);
491     let prompter = Services.ww.getNewPrompter(window);
492     prompter.alert(null, message);
493   }
497  * Enables or disables buttons corresponding to a cert tree depending on what
498  * is selected in the cert tree.
500  * @param {nsICertTree} certTree
501  * @param {Array} idList A list of string identifiers for button elements to
502  *    enable or disable.
503  */
504 function enableButtonsForCertTree(certTree, idList) {
505   let disableButtons = nothingOrContainerSelected(certTree);
507   for (let id of idList) {
508     document.getElementById(id).setAttribute("disabled", disableButtons);
509   }
512 function ca_enableButtons() {
513   let idList = [
514     "ca_viewButton",
515     "ca_editButton",
516     "ca_exportButton",
517     "ca_deleteButton",
518   ];
519   enableButtonsForCertTree(caTreeView, idList);
522 function mine_enableButtons() {
523   let idList = ["mine_viewButton", "mine_backupButton", "mine_deleteButton"];
524   enableButtonsForCertTree(userTreeView, idList);
527 function email_enableButtons() {
528   let idList = ["email_viewButton", "email_exportButton", "email_deleteButton"];
529   enableButtonsForCertTree(emailTreeView, idList);
532 async function backupCerts() {
533   getSelectedCerts();
534   var numcerts = selected_certs.length;
535   if (numcerts == 0) {
536     return;
537   }
539   var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
540   let [backupFileDialog, filePkcs12Spec] = await document.l10n.formatValues([
541     { id: "choose-p12-backup-file-dialog" },
542     { id: "file-browse-pkcs12-spec" },
543   ]);
544   fp.init(window.browsingContext, backupFileDialog, Ci.nsIFilePicker.modeSave);
545   fp.appendFilter(filePkcs12Spec, "*.p12");
546   fp.appendFilters(Ci.nsIFilePicker.filterAll);
547   fp.defaultExtension = "p12";
548   fp.open(rv => {
549     if (
550       rv == Ci.nsIFilePicker.returnOK ||
551       rv == Ci.nsIFilePicker.returnReplace
552     ) {
553       let password = {};
554       if (certdialogs.setPKCS12FilePassword(window, password)) {
555         let errorCode = certdb.exportPKCS12File(
556           fp.file,
557           selected_certs,
558           password.value
559         );
560         promptError(errorCode);
561       }
562     }
563   });
566 function backupAllCerts() {
567   // Select all rows, then call doBackup()
568   userTreeView.selection.selectAll();
569   backupCerts();
572 function editCerts() {
573   getSelectedCerts();
575   for (let cert of selected_certs) {
576     window.browsingContext.topChromeWindow.openDialog(
577       "chrome://pippki/content/editcacert.xhtml",
578       "",
579       "chrome,centerscreen,modal",
580       cert
581     );
582   }
585 async function restoreCerts() {
586   var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
587   let [restoreFileDialog, filePkcs12Spec, fileCertSpec] =
588     await document.l10n.formatValues([
589       { id: "choose-p12-restore-file-dialog" },
590       { id: "file-browse-pkcs12-spec" },
591       { id: "file-browse-certificate-spec" },
592     ]);
593   fp.init(window.browsingContext, restoreFileDialog, Ci.nsIFilePicker.modeOpen);
594   fp.appendFilter(filePkcs12Spec, "*.p12; *.pfx");
595   fp.appendFilter(fileCertSpec, gCertFileTypes);
596   fp.appendFilters(Ci.nsIFilePicker.filterAll);
597   fp.open(rv => {
598     if (rv != Ci.nsIFilePicker.returnOK) {
599       return;
600     }
602     // If this is an X509 user certificate, import it as one.
604     var isX509FileType = false;
605     var fileTypesList = gCertFileTypes.slice(1).split("; *");
606     for (var type of fileTypesList) {
607       if (fp.file.path.endsWith(type)) {
608         isX509FileType = true;
609         break;
610       }
611     }
613     if (isX509FileType) {
614       let fstream = Cc[
615         "@mozilla.org/network/file-input-stream;1"
616       ].createInstance(Ci.nsIFileInputStream);
617       fstream.init(fp.file, -1, 0, 0);
618       let dataString = NetUtil.readInputStreamToString(
619         fstream,
620         fstream.available()
621       );
622       let dataArray = [];
623       for (let i = 0; i < dataString.length; i++) {
624         dataArray.push(dataString.charCodeAt(i));
625       }
626       fstream.close();
627       let prompter = Services.ww.getNewPrompter(window);
628       let interfaceRequestor = {
629         getInterface() {
630           return prompter;
631         },
632       };
633       certdb.importUserCertificate(
634         dataArray,
635         dataArray.length,
636         interfaceRequestor
637       );
638     } else {
639       // Otherwise, assume it's a PKCS12 file and import it that way.
640       let password = {};
641       let errorCode = Ci.nsIX509CertDB.ERROR_BAD_PASSWORD;
642       while (
643         errorCode == Ci.nsIX509CertDB.ERROR_BAD_PASSWORD &&
644         certdialogs.getPKCS12FilePassword(window, password)
645       ) {
646         errorCode = certdb.importPKCS12File(fp.file, password.value);
647         if (
648           errorCode == Ci.nsIX509CertDB.ERROR_BAD_PASSWORD &&
649           !password.value.length
650         ) {
651           // It didn't like empty string password, try no password.
652           errorCode = certdb.importPKCS12File(fp.file, null);
653         }
654         promptError(errorCode);
655       }
656     }
658     var certcache = certdb.getCerts();
659     userTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.USER_CERT);
660     userTreeView.selection.clearSelection();
661     caTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.CA_CERT);
662     caTreeView.selection.clearSelection();
663     enableBackupAllButton();
664   });
667 async function exportCerts() {
668   getSelectedCerts();
670   for (let cert of selected_certs) {
671     await exportToFile(window, cert);
672   }
676  * Deletes the selected certs in the active tab.
677  */
678 function deleteCerts() {
679   getSelectedTreeItems();
680   let numcerts = selected_tree_items.length;
681   if (numcerts == 0) {
682     return;
683   }
685   const treeViewMap = {
686     mine_tab: userTreeView,
687     ca_tab: caTreeView,
688     others_tab: emailTreeView,
689   };
690   let selTab = document.getElementById("certMgrTabbox").selectedItem;
691   let selTabID = selTab.getAttribute("id");
693   if (!(selTabID in treeViewMap)) {
694     return;
695   }
697   let retVals = {
698     deleteConfirmed: false,
699   };
700   window.browsingContext.topChromeWindow.openDialog(
701     "chrome://pippki/content/deletecert.xhtml",
702     "",
703     "chrome,centerscreen,modal",
704     selTabID,
705     selected_tree_items,
706     retVals
707   );
709   if (retVals.deleteConfirmed) {
710     let treeView = treeViewMap[selTabID];
712     for (let t = numcerts - 1; t >= 0; t--) {
713       treeView.deleteEntryObject(selected_index[t]);
714     }
716     selected_tree_items = [];
717     selected_index = [];
718     treeView.selection.clearSelection();
719     if (selTabID == "mine_tab") {
720       enableBackupAllButton();
721     }
722   }
725 function viewCerts() {
726   getSelectedCerts();
728   for (let cert of selected_certs) {
729     viewCertHelper(window, cert);
730   }
733 async function addCACerts() {
734   var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
735   let [importCa, fileCertSpec] = await document.l10n.formatValues([
736     { id: "import-ca-certs-prompt" },
737     { id: "file-browse-certificate-spec" },
738   ]);
739   fp.init(window.browsingContext, importCa, Ci.nsIFilePicker.modeOpen);
740   fp.appendFilter(fileCertSpec, gCertFileTypes);
741   fp.appendFilters(Ci.nsIFilePicker.filterAll);
742   fp.open(rv => {
743     if (rv == Ci.nsIFilePicker.returnOK) {
744       certdb.importCertsFromFile(fp.file, Ci.nsIX509Cert.CA_CERT);
745       let certcache = certdb.getCerts();
746       caTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.CA_CERT);
747       caTreeView.selection.clearSelection();
748     }
749   });
752 async function addEmailCert() {
753   var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
754   let [importEmail, fileCertSpec] = await document.l10n.formatValues([
755     { id: "import-email-cert-prompt" },
756     { id: "file-browse-certificate-spec" },
757   ]);
758   fp.init(window.browsingContext, importEmail, Ci.nsIFilePicker.modeOpen);
759   fp.appendFilter(fileCertSpec, gCertFileTypes);
760   fp.appendFilters(Ci.nsIFilePicker.filterAll);
761   fp.open(rv => {
762     if (rv == Ci.nsIFilePicker.returnOK) {
763       certdb.importCertsFromFile(fp.file, Ci.nsIX509Cert.EMAIL_CERT);
764       var certcache = certdb.getCerts();
765       emailTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.EMAIL_CERT);
766       emailTreeView.selection.clearSelection();
767       caTreeView.loadCertsFromCache(certcache, Ci.nsIX509Cert.CA_CERT);
768       caTreeView.selection.clearSelection();
769     }
770   });