Bug 1310961 - Stop using nsIDialogParamBlock in deletecert.(js|xul). r?mgoodwin draft
authorCykesiopka <cykesiopka.bmo@gmail.com>
Wed, 19 Oct 2016 22:47:29 +0800
changeset 427008 ce209c455998739412ec91ddede2977122eef2b7
parent 426997 d56e9b123ed63993592c477540b64ec24a18002b
child 534353 803dc8b4fe2f7b093da16e3b671320953f313839
push id32893
push usercykesiopka.bmo@gmail.com
push dateWed, 19 Oct 2016 14:48:20 +0000
reviewersmgoodwin
bugs1310961
milestone52.0a1
Bug 1310961 - Stop using nsIDialogParamBlock in deletecert.(js|xul). r?mgoodwin An nsIDialogParamBlock is unnecessary for how deletecert.(js|xul) is currently used. Moreover, nsIDialogParamBlock is arguably a poor API, so moving away from it is also advantageous. In addition, this patch also fixes this bug: 1. Select a cert to delete in one of the cert manager tabs. 2. Press the delete button to launch the confirmation dialog, but don't accept or cancel. 3. Switch to another tab in the cert manager. 4. Press the accept button in the confirmation dialog. ER: Cert selected in the original tab is deleted. AR: Cert at the same index of the new tab is deleted, even though it was never selected. MozReview-Commit-ID: 3N8klOhrVzi
security/manager/pki/resources/content/certManager.js
security/manager/pki/resources/content/deletecert.js
security/manager/pki/resources/content/deletecert.xul
security/manager/ssl/tests/mochitest/browser/browser_deleteCert_ui.js
--- a/security/manager/pki/resources/content/certManager.js
+++ b/security/manager/pki/resources/content/certManager.js
@@ -8,18 +8,16 @@ const { classes: Cc, interfaces: Ci, uti
 
 const nsIFilePicker = Components.interfaces.nsIFilePicker;
 const nsFilePicker = "@mozilla.org/filepicker;1";
 const nsIX509CertDB = Components.interfaces.nsIX509CertDB;
 const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
 const nsIX509Cert = Components.interfaces.nsIX509Cert;
 const nsICertTree = Components.interfaces.nsICertTree;
 const nsCertTree = "@mozilla.org/security/nsCertTree;1";
-const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
-const nsDialogParamBlock = "@mozilla.org/embedcomp/dialogparam;1";
 
 const gCertFileTypes = "*.p7b; *.crt; *.cert; *.cer; *.pem; *.der";
 
 var { NetUtil } = Components.utils.import("resource://gre/modules/NetUtil.jsm", {});
 var { Services } = Components.utils.import("resource://gre/modules/Services.jsm", {});
 
 var key;
 
@@ -27,20 +25,40 @@ var key;
  * List of certs currently selected in the active tab.
  * @type nsIX509Cert[]
  */
 var selected_certs = [];
 var selected_tree_items = [];
 var selected_index = [];
 var certdb;
 
+/**
+ * Cert tree for the "Authorities" tab.
+ * @type nsICertTree
+ */
 var caTreeView;
+/**
+ * Cert tree for the "Servers" tab.
+ * @type nsICertTree
+ */
 var serverTreeView;
+/**
+ * Cert tree for the "People" tab.
+ * @type nsICertTree
+ */
 var emailTreeView;
+/**
+ * Cert tree for the "Your Certificates" tab.
+ * @type nsICertTree
+ */
 var userTreeView;
+/**
+ * Cert tree for the "Other" tab.
+ * @type nsICertTree
+ */
 var orphanTreeView;
 
 var smartCardObserver = {
   observe: function() {
     onSmartCardChange();
   }
 };
 
@@ -397,77 +415,58 @@ function exportCerts()
 {
   getSelectedCerts();
 
   for (let cert of selected_certs) {
     exportToFile(window, cert);
   }
 }
 
-function deleteCerts()
-{
+/**
+ * Deletes the selected certs in the active tab.
+ */
+function deleteCerts() {
   getSelectedTreeItems();
-  var numcerts = selected_tree_items.length;
+  let numcerts = selected_tree_items.length;
   if (numcerts == 0) {
     return;
   }
 
-  var params = Components.classes[nsDialogParamBlock].createInstance(nsIDialogParamBlock);
-
-  var selTab = document.getElementById('certMgrTabbox').selectedItem;
-  var selTabID = selTab.getAttribute('id');
+  const treeViewMap = {
+    "mine_tab": userTreeView,
+    "websites_tab": serverTreeView,
+    "ca_tab": caTreeView,
+    "others_tab": emailTreeView,
+    "orphan_tab": orphanTreeView,
+  };
+  let selTab = document.getElementById("certMgrTabbox").selectedItem;
+  let selTabID = selTab.getAttribute("id");
 
-  switch (selTabID) {
-    case "mine_tab":
-    case "websites_tab":
-    case "ca_tab":
-    case "others_tab":
-    case "orphan_tab":
-      params.SetString(0, selTabID);
-      break;
-    default:
-      return;
+  if (!(selTabID in treeViewMap)) {
+    return;
   }
 
-  let array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
-  for (let treeItem of selected_tree_items) {
-    array.appendElement(treeItem, false);
-  }
-  params.objects = array;
-
-  window.openDialog('chrome://pippki/content/deletecert.xul', "",
-                    'chrome,centerscreen,modal', params);
-
-  if (params.GetInt(1) == 1) {
-    // user closed dialog with OK
-    var treeView = null;
-    var loadParam = null;
+  let retVals = {
+    deleteConfirmed: false,
+  };
+  window.openDialog("chrome://pippki/content/deletecert.xul", "",
+                    "chrome,centerscreen,modal", selTabID, selected_tree_items,
+                    retVals);
 
-    selTab = document.getElementById('certMgrTabbox').selectedItem;
-    selTabID = selTab.getAttribute('id');
-    if (selTabID == 'mine_tab') {
-      treeView = userTreeView;
-    } else if (selTabID == "others_tab") {
-      treeView = emailTreeView;
-    } else if (selTabID == "websites_tab") {
-      treeView = serverTreeView;
-    } else if (selTabID == "ca_tab") {
-      treeView = caTreeView;
-    } else if (selTabID == "orphan_tab") {
-      treeView = orphanTreeView;
-    }
+  if (retVals.deleteConfirmed) {
+    let treeView = treeViewMap[selTabID];
 
     for (let t = numcerts - 1; t >= 0; t--) {
       treeView.deleteEntryObject(selected_index[t]);
     }
 
     selected_tree_items = [];
     selected_index = [];
     treeView.selection.clearSelection();
-    if (selTabID == 'mine_tab') {
+    if (selTabID == "mine_tab") {
       enableBackupAllButton();
     }
   }
 }
 
 function viewCerts()
 {
   getSelectedCerts();
--- a/security/manager/pki/resources/content/deletecert.js
+++ b/security/manager/pki/resources/content/deletecert.js
@@ -1,21 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* import-globals-from pippki.js */
 "use strict";
 
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+/**
+ * @file Implements the functionality of deletecert.xul: a dialog that allows a
+ *       user to confirm whether to delete certain certificates.
+ * @argument {String} window.arguments[0]
+ *           One of the tab IDs listed in certManager.xul.
+ * @argument {nsICertTreeItem[]} window.arguments[1]
+ *           An array of cert tree items representing the certs to delete.
+ * @argument {DeleteCertReturnValues} window.arguments[2]
+ *           Object holding the return values of calling the dialog.
+ */
 
 /**
- * Param block to get passed in args and to set return values to.
- * @type nsIDialogParamBlock
+ * @typedef DeleteCertReturnValues
+ * @type Object
+ * @property {Boolean} deleteConfirmed
+ *           Set to true if the user confirmed deletion of the given certs,
+ *           false otherwise.
  */
-var gParams;
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 /**
  * Returns the most appropriate string to represent the given nsICertTreeItem.
  * @param {nsICertTreeItem} certTreeItem
  *        The item to represent.
  * @returns {String}
  *          A representative string.
  */
@@ -36,21 +49,21 @@ function certTreeItemToString(certTreeIt
       return attribute;
     }
   }
 
   let bundle = document.getElementById("pippki_bundle");
   return bundle.getFormattedString("certWithSerial", [cert.serialNumber]);
 }
 
-function setWindowName()
-{
-  gParams = window.arguments[0].QueryInterface(Ci.nsIDialogParamBlock);
-
-  let typeFlag = gParams.GetString(0);
+/**
+ * onload() handler.
+ */
+function onLoad() {
+  let typeFlag = window.arguments[0];
   let bundle = document.getElementById("pippki_bundle");
   let title;
   let confirm;
   let impact;
 
   switch (typeFlag) {
     case "mine_tab":
       title = bundle.getString("deleteUserCertTitle");
@@ -81,31 +94,41 @@ function setWindowName()
       return;
   }
 
   document.title = title;
 
   setText("confirm", confirm);
 
   let box = document.getElementById("certlist");
-  for (let x = 0; x < gParams.objects.length; x++) {
+  let certTreeItems = window.arguments[1];
+  for (let certTreeItem of certTreeItems) {
     let listItem = document.createElement("richlistitem");
     let label = document.createElement("label");
-    let certTreeItem = gParams.objects.queryElementAt(x, Ci.nsICertTreeItem);
     label.setAttribute("value", certTreeItemToString(certTreeItem));
     listItem.appendChild(label);
     box.appendChild(listItem);
   }
 
   setText("impact", impact);
 }
 
-function doOK()
-{
-  gParams.SetInt(1, 1); // means OK
+/**
+ * ondialogaccept() handler.
+ *
+ * @returns {Boolean} true to make the dialog close, false otherwise.
+ */
+function onDialogAccept() {
+  let retVals = window.arguments[2];
+  retVals.deleteConfirmed = true;
   return true;
 }
 
-function doCancel()
-{
-  gParams.SetInt(1, 0); // means CANCEL
+/**
+ * ondialogcancel() handler.
+ *
+ * @returns {Boolean} true to make the dialog close, false otherwise.
+ */
+function onDialogCancel() {
+  let retVals = window.arguments[2];
+  retVals.deleteConfirmed = false;
   return true;
 }
--- a/security/manager/pki/resources/content/deletecert.xul
+++ b/security/manager/pki/resources/content/deletecert.xul
@@ -2,23 +2,23 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 
 <!DOCTYPE dialog SYSTEM "chrome://pippki/locale/certManager.dtd">
 
-<dialog id="deleteCertificate" 
+<dialog id="deleteCertificate"
   title="&certmgr.deletecert.title;"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-  onload="setWindowName();"
+  onload="onLoad();"
   buttons="accept,cancel"
-  ondialogaccept="return doOK();"
-  ondialogcancel="return doCancel();">
+  ondialogaccept="return onDialogAccept();"
+  ondialogcancel="return onDialogCancel();">
 
   <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
 
   <script type="application/javascript" src="chrome://pippki/content/deletecert.js"/>
   <script type="application/javascript" src="pippki.js" />
 
   <description id="confirm" style="width: 400px;"/>
   <richlistbox id="certlist" class="box-padded" flex="1"
--- a/security/manager/ssl/tests/mochitest/browser/browser_deleteCert_ui.js
+++ b/security/manager/ssl/tests/mochitest/browser/browser_deleteCert_ui.js
@@ -6,19 +6,19 @@
 // Tests various aspects of the cert delete confirmation dialog.
 // Among other things, tests that for each type of cert that can be deleted:
 // 1. The various lines of explanation text are correctly set.
 // 2. The implementation correctly falls back through multiple cert attributes
 //    to determine what to display to represent a cert.
 
 /**
  * An array of tree items corresponding to TEST_CASES.
- * @type nsIMutableArray<nsICertTreeItem>
+ * @type nsICertTreeItem[]
  */
-var gCertArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+var gCertArray = [];
 
 const FAKE_HOST_PORT = "Fake host and port";
 
 /**
  * @typedef {TestCase}
  * @type Object
  * @property {String} certFilename
  *           Filename of the cert, or null if we don't want to import a cert for
@@ -51,30 +51,28 @@ const TEST_CASES = [
  * Opens the cert delete confirmation dialog.
  *
  * @param {String} tabID
  *        The ID of the cert category tab the certs to delete belong to.
  * @returns {Promise}
  *          A promise that resolves when the dialog has finished loading, with
  *          an array consisting of:
  *            1. The window of the opened dialog.
- *            2. The nsIDialogParamBlock passed to the dialog.
+ *            2. The return value object passed to the dialog.
  */
 function openDeleteCertConfirmDialog(tabID) {
-  let params = Cc["@mozilla.org/embedcomp/dialogparam;1"]
-                 .createInstance(Ci.nsIDialogParamBlock);
-  params.SetString(0, tabID);
-  params.objects = gCertArray;
-
+  let retVals = {
+    deleteConfirmed: false,
+  };
   let win = window.openDialog("chrome://pippki/content/deletecert.xul", "", "",
-                              params);
+                              tabID, gCertArray, retVals);
   return new Promise((resolve, reject) => {
     win.addEventListener("load", function onLoad() {
       win.removeEventListener("load", onLoad);
-      resolve([win, params]);
+      resolve([win, retVals]);
     });
   });
 }
 
 add_task(function* setup() {
   for (let testCase of TEST_CASES) {
     let cert = null;
     if (testCase.certFilename) {
@@ -86,34 +84,34 @@ add_task(function* setup() {
       QueryInterface(iid) {
         if (iid.equals(Ci.nsICertTreeItem)) {
           return this;
         }
 
         throw new Error(Cr.NS_ERROR_NO_INTERFACE);
       }
     };
-    gCertArray.appendElement(certTreeItem, false);
+    gCertArray.push(certTreeItem);
   }
 });
 
 /**
  * Test helper for the below test cases.
  *
  * @param {String} tabID
  *        ID of the cert category tab the certs to delete belong to.
  * @param {String} expectedTitle
  *        Title the dialog is expected to have.
  * @param {String} expectedConfirmMsg
  *        Confirmation message the dialog is expected to show.
  * @param {String} expectedImpact
  *        Impact the dialog is expected to show.
  */
 function* testHelper(tabID, expectedTitle, expectedConfirmMsg, expectedImpact) {
-  let [win, params] = yield openDeleteCertConfirmDialog(tabID);
+  let [win, retVals] = yield openDeleteCertConfirmDialog(tabID);
   let certList = win.document.getElementById("certlist");
 
   Assert.equal(win.document.title, expectedTitle,
                `Actual and expected titles should match for ${tabID}`);
   Assert.equal(win.document.getElementById("confirm").textContent,
                expectedConfirmMsg,
                `Actual and expected confirm message should match for ${tabID}`);
   Assert.equal(win.document.getElementById("impact").textContent,
@@ -191,27 +189,27 @@ add_task(function* testDeleteOtherCerts(
     "Are you sure you want to delete these certificates?";
   const expectedImpact = "";
   yield* testHelper("orphan_tab", expectedTitle, expectedConfirmMsg,
                     expectedImpact);
 });
 
 // Test that the right values are returned when the dialog is accepted.
 add_task(function* testAcceptDialogReturnValues() {
-  let [win, params] = yield openDeleteCertConfirmDialog("ca_tab" /*arbitrary*/);
+  let [win, retVals] = yield openDeleteCertConfirmDialog("ca_tab" /*arbitrary*/);
   info("Accepting dialog");
   win.document.getElementById("deleteCertificate").acceptDialog();
   yield BrowserTestUtils.windowClosed(win);
 
-  Assert.equal(params.GetInt(1), 1,
-               "1 should be returned to signal user accepted");
+  Assert.ok(retVals.deleteConfirmed,
+            "Return value should signal user accepted");
 });
 
 // Test that the right values are returned when the dialog is canceled.
 add_task(function* testCancelDialogReturnValues() {
-  let [win, params] = yield openDeleteCertConfirmDialog("ca_tab" /*arbitrary*/);
+  let [win, retVals] = yield openDeleteCertConfirmDialog("ca_tab" /*arbitrary*/);
   info("Canceling dialog");
   win.document.getElementById("deleteCertificate").cancelDialog();
   yield BrowserTestUtils.windowClosed(win);
 
-  Assert.equal(params.GetInt(1), 0,
-               "0 should be returned to signal user canceled");
+  Assert.ok(!retVals.deleteConfirmed,
+            "Return value should signal user did not accept");
 });