Bug 1220237 - Remove uses of nsIEnumerator from PSM. r=keeler
authorCykesiopka <cykesiopka.bmo@gmail.com>
Wed, 24 Feb 2016 17:42:45 -0800
changeset 321878 63fdedf32876a6c2776e72d8d882c8afd7475c5f
parent 321877 406ee0a64991a8a9bc6b8ef9e1a27a16e80b9b15
child 321879 87a62c4c347446c5bf6bcdd1fa4808df3175463b
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1220237
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1220237 - Remove uses of nsIEnumerator from PSM. r=keeler MozReview-Commit-ID: 3FhBCqnJz4n
security/manager/pki/resources/content/device_manager.js
security/manager/pki/resources/content/password.js
security/manager/ssl/nsIPK11TokenDB.idl
security/manager/ssl/nsIPKCS11Module.idl
security/manager/ssl/nsIPKCS11ModuleDB.idl
security/manager/ssl/nsPK11TokenDB.cpp
security/manager/ssl/nsPKCS11Slot.cpp
security/manager/ssl/nsPKCS11Slot.h
security/manager/ssl/tests/unit/test_pkcs11_insert_remove.js
security/manager/ssl/tests/unit/test_pkcs11_list.js
security/manager/ssl/tests/unit/xpcshell-smartcards.ini
--- a/security/manager/pki/resources/content/device_manager.js
+++ b/security/manager/pki/resources/content/device_manager.js
@@ -66,56 +66,30 @@ function doConfirm(msg)
 {
   let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
     getService(Components.interfaces.nsIPromptService);
   return prompts.confirm(window, null, msg);
 }
 
 function RefreshDeviceList()
 {
-  var modules = secmoddb.listModules();
-  var done = false;
+  let modules = secmoddb.listModules();
+  while (modules.hasMoreElements()) {
+    let module = modules.getNext().QueryInterface(nsIPKCS11Module);
+    let slotnames = [];
+    let slots = module.listSlots();
+    while (slots.hasMoreElements()) {
+      let slot = slots.getNext().QueryInterface(nsIPKCS11Slot);
+      // Token names are preferred because NSS prefers lookup by token name.
+      slotnames.push(slot.tokenName ? slot.tokenName : slot.name);
+    }
+    AddModule(module.name, slotnames);
+  }
 
-  try {
-    modules.isDone();
-  } catch (e) { done = true; }
-  while (!done) {
-    var module = modules.currentItem().QueryInterface(nsIPKCS11Module);
-    if (module) {
-      var slotnames = [];
-      var slots = module.listSlots();
-      var slots_done = false;
-      try {
-        slots.isDone();
-      } catch (e) { slots_done = true; }
-      while (!slots_done) {
-        var slot = null;
-        try {
-          slot = slots.currentItem().QueryInterface(nsIPKCS11Slot);
-        } catch (e) { slot = null; }
-        // in the ongoing discussion of whether slot names or token names
-        // are to be shown, I've gone with token names because NSS will
-        // prefer lookup by token name.  However, the token may not be
-        // present, so maybe slot names should be listed, while token names
-        // are "remembered" for lookup?
-        if (slot != null) {
-          slotnames[slotnames.length] = slot.tokenName ? slot.tokenName
-                                                       : slot.name;
-        }
-        try {
-          slots.next();
-        } catch (e) { slots_done = true; }
-      }
-      AddModule(module.name, slotnames);
-    }
-    try {
-      modules.next();
-    } catch (e) { done = true; }
-  }
-  /* Set the text on the fips button */
+  // Set the text on the FIPS button.
   SetFIPSButton();
 }
 
 function SetFIPSButton()
 {
   var fipsButton = document.getElementById("fipsbutton");
   var label;
   if (secmoddb.isFIPSEnabled) {
--- a/security/manager/pki/resources/content/password.js
+++ b/security/manager/pki/resources/content/password.js
@@ -34,38 +34,34 @@ function onLoad()
       // as window name must be a subset of ascii, and the code was
       // previously trying to assign unicode to the window's name.
       // I checked all the places where we get a password prompt and
       // all of them pass an argument as part of this patch.
       tokenName = "";
   }
 
   if (tokenName == "") {
-     var sectokdb = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB);
-     var tokenList = sectokdb.listTokens();
-     var enumElement;
-     let i = 0;
-     var menu = document.getElementById("tokenMenu");
-     try {
-        for (; !tokenList.isDone(); tokenList.next()) {
-           enumElement = tokenList.currentItem();
-           var token = enumElement.QueryInterface(nsIPK11Token);
-           if(token.needsLogin() || !(token.needsUserInit)) {
-              var menuItemNode = document.createElement("menuitem");
-              menuItemNode.setAttribute("value", token.tokenName);
-              menuItemNode.setAttribute("label", token.tokenName);
-              menu.firstChild.appendChild(menuItemNode);
-              if (i == 0) {
-                 menu.selectedItem = menuItemNode;
-                 tokenName = token.tokenName;
-              }
-              i++;
-           }
+    let tokenDB = Components.classes[nsPK11TokenDB].getService(nsIPK11TokenDB);
+    let tokenList = tokenDB.listTokens();
+    let i = 0;
+    let menu = document.getElementById("tokenMenu");
+    while (tokenList.hasMoreElements()) {
+      let token = tokenList.getNext().QueryInterface(nsIPK11Token);
+      if (token.needsLogin() || !(token.needsUserInit)) {
+        let menuItemNode = document.createElement("menuitem");
+        menuItemNode.setAttribute("value", token.tokenName);
+        menuItemNode.setAttribute("label", token.tokenName);
+        menu.firstChild.appendChild(menuItemNode);
+        if (i == 0) {
+          menu.selectedItem = menuItemNode;
+          tokenName = token.tokenName;
         }
-     } catch (exception) {}
+        i++;
+      }
+    }
   } else {
     var sel = document.getElementById("tokenMenu");
     sel.setAttribute("hidden", "true");
     var tag = document.getElementById("tokenName");
     tag.setAttribute("value", tokenName);
   }
 
   process();
--- a/security/manager/ssl/nsIPK11TokenDB.idl
+++ b/security/manager/ssl/nsIPK11TokenDB.idl
@@ -2,17 +2,17 @@
  *
  * 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/. */
 
 #include "nsISupports.idl"
 
 interface nsIPK11Token;
-interface nsIEnumerator;
+interface nsISimpleEnumerator;
 
 /**
  * The PK11 Token Database provides access to the PK11 modules
  * that are installed, and the tokens that are available.
  * Interfaces: nsIPK11TokenDB
  * Threading: ??
  */
 %{C++
@@ -33,12 +33,10 @@ interface nsIPK11TokenDB : nsISupports
   /*
    * Find a token by name
    */
   nsIPK11Token findTokenByName(in wstring tokenName);
 
   /*
    * List all tokens
    */
-  nsIEnumerator listTokens();
-
+  nsISimpleEnumerator listTokens();
 };
-
--- a/security/manager/ssl/nsIPKCS11Module.idl
+++ b/security/manager/ssl/nsIPKCS11Module.idl
@@ -2,23 +2,20 @@
  *
  * 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/. */
 
 #include "nsISupports.idl"
 
 interface nsIPKCS11Slot;
-interface nsIEnumerator;
+interface nsISimpleEnumerator;
 
 [scriptable, uuid(8a44bdf9-d1a5-4734-bd5a-34ed7fe564c2)]
 interface nsIPKCS11Module : nsISupports
 {
-
   readonly attribute wstring name;
   readonly attribute wstring libName;
 
   nsIPKCS11Slot findSlotByName(in wstring name);
 
-  nsIEnumerator listSlots();
-
+  nsISimpleEnumerator listSlots();
 };
-
--- a/security/manager/ssl/nsIPKCS11ModuleDB.idl
+++ b/security/manager/ssl/nsIPKCS11ModuleDB.idl
@@ -3,34 +3,33 @@
  * 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/. */
 
 #include "nsISupports.idl"
 
 interface nsIPKCS11Module;
 interface nsIPKCS11Slot;
-interface nsIEnumerator;
+interface nsISimpleEnumerator;
 
 %{C++
 #define NS_PKCS11MODULEDB_CONTRACTID "@mozilla.org/security/pkcs11moduledb;1"
 %}
 
 [scriptable, uuid(ff9fbcd7-9517-4334-b97a-ceed78909974)]
 interface nsIPKCS11ModuleDB : nsISupports
 {
   nsIPKCS11Module getInternal();
 
   nsIPKCS11Module getInternalFIPS();
 
   nsIPKCS11Module findModuleByName(in wstring name);
 
   nsIPKCS11Slot findSlotByName(in wstring name);
 
-  nsIEnumerator listModules();
+  nsISimpleEnumerator listModules();
 
   readonly attribute boolean canToggleFIPS;
 
   void toggleFIPSMode();
 
   readonly attribute boolean isFIPSEnabled;
 };
-
--- a/security/manager/ssl/nsPK11TokenDB.cpp
+++ b/security/manager/ssl/nsPK11TokenDB.cpp
@@ -1,23 +1,23 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * 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/. */
+#include "nsPK11TokenDB.h"
+
+#include "nsIMutableArray.h"
 #include "nsISupports.h"
-#include "nsISupportsArray.h"
-#include "nsIPK11TokenDB.h"
+#include "nsNSSComponent.h"
+#include "nsReadableUtils.h"
+#include "nsServiceManagerUtils.h"
 #include "prerror.h"
+#include "ScopedNSSTypes.h"
 #include "secerr.h"
-#include "nsReadableUtils.h"
-#include "nsNSSComponent.h"
-#include "nsServiceManagerUtils.h"
-
-#include "nsPK11TokenDB.h"
 
 extern PRLogModuleInfo* gPIPNSSLog;
 
 NS_IMPL_ISUPPORTS(nsPK11Token, nsIPK11Token)
 
 nsPK11Token::nsPK11Token(PK11SlotInfo *slot)
 {
   nsNSSShutDownPreventionLock locker;
@@ -442,42 +442,36 @@ FindTokenByName(const char16_t* tokenNam
   token = new nsPK11Token(slot);
   token.forget(_retval);
 
 done:
   if (slot) PK11_FreeSlot(slot);
   return rv;
 }
 
-NS_IMETHODIMP nsPK11TokenDB::ListTokens(nsIEnumerator* *_retval)
+NS_IMETHODIMP
+nsPK11TokenDB::ListTokens(nsISimpleEnumerator** _retval)
 {
   nsNSSShutDownPreventionLock locker;
-  nsCOMPtr<nsISupportsArray> array;
-  PK11SlotList *list = 0;
-  PK11SlotListElement *le;
+  nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (!array) {
+    return NS_ERROR_FAILURE;
+  }
 
   *_retval = nullptr;
-  nsresult rv = NS_NewISupportsArray(getter_AddRefs(array));
-  if (NS_FAILED(rv)) { goto done; }
+
+  UniquePK11SlotList list(
+    PK11_GetAllTokens(CKM_INVALID_MECHANISM, false, false, 0));
+  if (!list) {
+    return NS_ERROR_FAILURE;
+  }
 
-  /* List all tokens, creating PK11Token objects and putting them
-   * into the array.
-   */
-  list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, false, false, 0);
-  if (!list) { rv = NS_ERROR_FAILURE; goto done; }
-
-  for (le = PK11_GetFirstSafe(list); le; le = PK11_GetNextSafe(list, le, false)) {
+  for (PK11SlotListElement* le = PK11_GetFirstSafe(list.get()); le;
+       le = PK11_GetNextSafe(list.get(), le, false)) {
     nsCOMPtr<nsIPK11Token> token = new nsPK11Token(le->slot);
-    rv = array->AppendElement(token);
+    nsresult rv = array->AppendElement(token, false);
     if (NS_FAILED(rv)) {
-      PK11_FreeSlotListElement(list, le);
-      rv = NS_ERROR_OUT_OF_MEMORY;
-      goto done;
+      return rv;
     }
   }
 
-  rv = array->Enumerate(_retval);
-
-done:
-  if (list) PK11_FreeSlotList(list);
-  return rv;
+  return array->Enumerate(_retval);
 }
-
--- a/security/manager/ssl/nsPKCS11Slot.cpp
+++ b/security/manager/ssl/nsPKCS11Slot.cpp
@@ -2,17 +2,17 @@
  * 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/. */
 
 #include "nsPKCS11Slot.h"
 
 #include "mozilla/Logging.h"
 #include "mozilla/Telemetry.h"
 #include "nsCOMPtr.h"
-#include "nsISupportsArray.h"
+#include "nsIMutableArray.h"
 #include "nsPK11TokenDB.h"
 #include "secmod.h"
 
 using mozilla::LogLevel;
 
 extern PRLogModuleInfo* gPIPNSSLog;
 
 NS_IMPL_ISUPPORTS(nsPKCS11Slot, nsIPKCS11Slot)
@@ -313,44 +313,45 @@ nsPKCS11Module::FindSlotByName(const cha
   } 
   free(asciiname);
   nsCOMPtr<nsIPKCS11Slot> slot = new nsPKCS11Slot(slotinfo);
   PK11_FreeSlot(slotinfo);
   slot.forget(_retval);
   return NS_OK;
 }
 
-NS_IMETHODIMP 
-nsPKCS11Module::ListSlots(nsIEnumerator **_retval)
+NS_IMETHODIMP
+nsPKCS11Module::ListSlots(nsISimpleEnumerator** _retval)
 {
   nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown())
+  if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
+  }
 
-  nsresult rv = NS_OK;
-  int i;
-  /* get isupports array */
-  nsCOMPtr<nsISupportsArray> array;
-  rv = NS_NewISupportsArray(getter_AddRefs(array));
-  if (NS_FAILED(rv)) return rv;
+  nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (!array) {
+    return NS_ERROR_FAILURE;
+  }
+
   /* applications which allow new slot creation (which Firefox now does
    * since it uses the WaitForSlotEvent call) need to hold the
    * ModuleList Read lock to prevent the slot array from changing out
    * from under it. */
-  SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
-  SECMOD_GetReadLock(lock);
-  for (i=0; i<mModule->slotCount; i++) {
+  AutoSECMODListReadLock lock;
+  for (int i = 0; i < mModule->slotCount; i++) {
     if (mModule->slots[i]) {
       nsCOMPtr<nsIPKCS11Slot> slot = new nsPKCS11Slot(mModule->slots[i]);
-      array->AppendElement(slot);
+      nsresult rv = array->AppendElement(slot, false);
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
     }
   }
-  SECMOD_ReleaseReadLock(lock);
-  rv = array->Enumerate(_retval);
-  return rv;
+
+  return array->Enumerate(_retval);
 }
 
 NS_IMPL_ISUPPORTS(nsPKCS11ModuleDB, nsIPKCS11ModuleDB, nsICryptoFIPSInfo)
 
 nsPKCS11ModuleDB::nsPKCS11ModuleDB()
 {
 }
 
@@ -412,45 +413,47 @@ nsPKCS11ModuleDB::FindSlotByName(const c
   if (!slotinfo)
     return NS_ERROR_FAILURE;
   nsCOMPtr<nsIPKCS11Slot> slot = new nsPKCS11Slot(slotinfo);
   PK11_FreeSlot(slotinfo);
   slot.forget(_retval);
   return NS_OK;
 }
 
-NS_IMETHODIMP 
-nsPKCS11ModuleDB::ListModules(nsIEnumerator **_retval)
+NS_IMETHODIMP
+nsPKCS11ModuleDB::ListModules(nsISimpleEnumerator** _retval)
 {
   nsNSSShutDownPreventionLock locker;
-  nsresult rv = NS_OK;
-  /* get isupports array */
-  nsCOMPtr<nsISupportsArray> array;
-  rv = NS_NewISupportsArray(getter_AddRefs(array));
-  if (NS_FAILED(rv)) return rv;
-  /* get the default list of modules */
-  SECMODModuleList *list = SECMOD_GetDefaultModuleList();
+  nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (!array) {
+    return NS_ERROR_FAILURE;
+  }
+
   /* lock down the list for reading */
-  SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
-  SECMOD_GetReadLock(lock);
-  while (list) {
+  AutoSECMODListReadLock lock;
+  for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
+       list = list->next) {
     nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(list->module);
-    array->AppendElement(module);
-    list = list->next;
+    nsresult rv = array->AppendElement(module, false);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
   }
+
   /* Get the modules in the database that didn't load */
-  list = SECMOD_GetDeadModuleList();
-  while (list) {
+  for (SECMODModuleList* list = SECMOD_GetDeadModuleList(); list;
+       list = list->next) {
     nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(list->module);
-    array->AppendElement(module);
-    list = list->next;
+    nsresult rv = array->AppendElement(module, false);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
   }
-  SECMOD_ReleaseReadLock(lock);
-  rv = array->Enumerate(_retval);
-  return rv;
+
+  return array->Enumerate(_retval);
 }
 
 NS_IMETHODIMP nsPKCS11ModuleDB::GetCanToggleFIPS(bool *aCanToggleFIPS)
 {
   nsNSSShutDownPreventionLock locker;
   *aCanToggleFIPS = SECMOD_CanDeleteInternalModule();
   return NS_OK;
 }
--- a/security/manager/ssl/nsPKCS11Slot.h
+++ b/security/manager/ssl/nsPKCS11Slot.h
@@ -1,25 +1,25 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * 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/. */
 
-#ifndef __NS_PKCS11SLOT_H__
-#define __NS_PKCS11SLOT_H__
+#ifndef nsPKCS11Slot_h
+#define nsPKCS11Slot_h
 
-#include "nsISupports.h"
-#include "nsIPKCS11Slot.h"
+#include "nsICryptoFIPSInfo.h"
 #include "nsIPKCS11Module.h"
 #include "nsIPKCS11ModuleDB.h"
-#include "nsICryptoFIPSInfo.h"
+#include "nsIPKCS11Slot.h"
+#include "nsISupports.h"
+#include "nsNSSShutDown.h"
 #include "nsString.h"
 #include "pk11func.h"
-#include "nsNSSShutDown.h"
 
 class nsPKCS11Slot : public nsIPKCS11Slot,
                      public nsNSSShutDownObject
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPKCS11SLOT
 
@@ -72,9 +72,28 @@ protected:
   virtual ~nsPKCS11ModuleDB();
   /* additional members */
 };
 
 #define NS_PKCS11MODULEDB_CID \
 { 0xff9fbcd7, 0x9517, 0x4334, \
   { 0xb9, 0x7a, 0xce, 0xed, 0x78, 0x90, 0x99, 0x74 }}
 
-#endif
+class MOZ_RAII AutoSECMODListReadLock final
+{
+public:
+  AutoSECMODListReadLock()
+    : mLock(SECMOD_GetDefaultModuleListLock())
+  {
+    MOZ_ASSERT(mLock, "Should have the default SECMOD lock");
+    SECMOD_GetReadLock(mLock);
+  }
+
+  ~AutoSECMODListReadLock()
+  {
+    SECMOD_ReleaseReadLock(mLock);
+  }
+
+private:
+  SECMODListLock* mLock;
+};
+
+#endif // nsPKCS11Slot_h
--- a/security/manager/ssl/tests/unit/test_pkcs11_insert_remove.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_insert_remove.js
@@ -11,26 +11,48 @@
 // has been succssfully observed, and then it unloads the test module.
 
 // Ensure that the appropriate initialization has happened.
 do_get_profile();
 Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
 
 const gExpectedTokenLabel = "Test PKCS11 TokeƱ Label";
 
+const gTokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
+                   .getService(Ci.nsIPK11TokenDB);
+
 function SmartcardObserver(type) {
   this.type = type;
   do_test_pending();
 }
 
 SmartcardObserver.prototype = {
   observe: function(subject, topic, data) {
     equal(topic, this.type, "Observed and expected types should match");
     equal(gExpectedTokenLabel, data,
           "Expected and observed token labels should match");
+
+    // Test that the token list contains the test token only when the test
+    // module is loaded.
+    // Note: This test is located here out of convenience. In particular,
+    //       observing the "smartcard-insert" event is the only time where it
+    //       is reasonably certain for the test token to be present (see the top
+    //       level comment for this file for why).
+    let tokenList = gTokenDB.listTokens();
+    let testTokenLabelFound = false;
+    while (tokenList.hasMoreElements()) {
+      let token = tokenList.getNext().QueryInterface(Ci.nsIPK11Token);
+      if (token.tokenLabel == gExpectedTokenLabel) {
+        testTokenLabelFound = true;
+        break;
+      }
+    }
+    equal(testTokenLabelFound, this.type == "smartcard-insert",
+          "Should find test token only when the test module is loaded");
+
     Services.obs.removeObserver(this, this.type);
     do_test_finished();
   }
 };
 
 function run_test() {
   Services.obs.addObserver(new SmartcardObserver("smartcard-insert"),
                            "smartcard-insert", false);
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pkcs11_list.js
@@ -0,0 +1,94 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests the methods for listing PKCS #11 modules and slots via loading and
+// unloading a test PKCS #11 module.
+
+// Note: Tests for listing PKCS #11 tokens are located in
+//       test_pkcs11_insert_remove.js out of convenience.
+
+// Ensure that the appropriate initialization has happened.
+do_get_profile();
+Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+
+const gModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"]
+                    .getService(Ci.nsIPKCS11ModuleDB);
+
+function checkTestModuleNotPresent() {
+  let modules = gModuleDB.listModules();
+  ok(modules.hasMoreElements(),
+     "One or more modules should be present with test module not present");
+  while (modules.hasMoreElements()) {
+    let module = modules.getNext().QueryInterface(Ci.nsIPKCS11Module);
+    notEqual(module.name, "PKCS11 Test Module",
+             "Non-test module name shouldn't equal 'PKCS11 Test Module'");
+  }
+}
+
+/**
+ * Checks that the test module exists in the module list.
+ *
+ * @returns {nsIPKCS11Module}
+ *          The test module.
+ */
+function checkTestModuleExists() {
+  let modules = gModuleDB.listModules();
+  ok(modules.hasMoreElements(),
+     "One or more modules should be present with test module present");
+  let testModule = null;
+  while (modules.hasMoreElements()) {
+    let module = modules.getNext().QueryInterface(Ci.nsIPKCS11Module);
+    if (module.name == "PKCS11 Test Module") {
+      testModule = module;
+      break;
+    }
+  }
+  notEqual(testModule, null, "Test module should have been found");
+
+  return testModule;
+}
+
+function run_test() {
+  let libraryName = ctypes.libraryName("pkcs11testmodule");
+  let libraryFile = Services.dirsvc.get("CurWorkD", Ci.nsILocalFile);
+  libraryFile.append("pkcs11testmodule");
+  libraryFile.append(libraryName);
+  ok(libraryFile.exists(), "The pkcs11testmodule file should exist");
+
+  // Check that if we have never added the test module, that we don't find it
+  // in the module list.
+  checkTestModuleNotPresent();
+
+  // Check that adding the test module makes it appear in the module list.
+  let pkcs11 = Cc["@mozilla.org/security/pkcs11;1"].getService(Ci.nsIPKCS11);
+  do_register_cleanup(() => {
+    try {
+      pkcs11.deleteModule("PKCS11 Test Module");
+    } catch (e) {
+      // deleteModule() throws if the module we tell it to delete is missing,
+      // or if some other thing went wrong. Since we're just cleaning up,
+      // there's nothing to do even if the call fails. In addition, we delete
+      // the test module during a normal run of this test file, so we need to
+      // catch the exception that is raised to not have the test fail.
+    }
+  });
+  pkcs11.addModule("PKCS11 Test Module", libraryFile.path, 0, 0);
+  let testModule = checkTestModuleExists();
+
+  // Check that listing the slots for the test module works.
+  let slots = testModule.listSlots();
+  let testModuleSlotCount = 0;
+  while (slots.hasMoreElements()) {
+    let slot = slots.getNext().QueryInterface(Ci.nsIPKCS11Slot);
+    equal(slot.name, "Test PKCS11 Slot",
+          "Test module slot should have correct name");
+    testModuleSlotCount++;
+  }
+  equal(testModuleSlotCount, 1, "Test module should only have one slot");
+
+  // Check that deleting the test module makes it disappear from the module list.
+  pkcs11.deleteModule("PKCS11 Test Module");
+  checkTestModuleNotPresent();
+}
--- a/security/manager/ssl/tests/unit/xpcshell-smartcards.ini
+++ b/security/manager/ssl/tests/unit/xpcshell-smartcards.ini
@@ -3,14 +3,17 @@ head = head_psm.js
 tail =
 tags = psm
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 support-files =
 
 [test_pkcs11_insert_remove.js]
 # Bug 1049969: this test doesn't work on windows
 skip-if = os == "win"
+[test_pkcs11_list.js]
+# Bug 1049969: this test doesn't work on windows
+skip-if = os == "win"
 [test_pkcs11_no_events_after_removal.js]
 # Bug 1049969: this test doesn't work on windows
 skip-if = os == "win"
 [test_pkcs11_safe_mode.js]
 # Bug 1049969: this test doesn't work on windows
 skip-if = os == "win"