summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js')
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js312
1 files changed, 312 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js b/security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js
new file mode 100644
index 0000000000..9e4e244123
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js
@@ -0,0 +1,312 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// Tests the dialog used for loading PKCS #11 modules.
+
+const { MockRegistrar } = ChromeUtils.importESModule(
+ "resource://testing-common/MockRegistrar.sys.mjs"
+);
+
+const gMockPKCS11ModuleDB = {
+ addModuleCallCount: 0,
+ expectedLibPath: "",
+ expectedModuleName: "",
+ throwOnAddModule: false,
+
+ addModule(moduleName, libraryFullPath, cryptoMechanismFlags, cipherFlags) {
+ this.addModuleCallCount++;
+ Assert.equal(
+ moduleName,
+ this.expectedModuleName,
+ "addModule: Name given should be what's in the name textbox"
+ );
+ Assert.equal(
+ libraryFullPath,
+ this.expectedLibPath,
+ "addModule: Path given should be what's in the path textbox"
+ );
+ Assert.equal(
+ cryptoMechanismFlags,
+ 0,
+ "addModule: No crypto mechanism flags should be passed"
+ );
+ Assert.equal(cipherFlags, 0, "addModule: No cipher flags should be passed");
+
+ if (this.throwOnAddModule) {
+ throw new Error(`addModule: Throwing exception`);
+ }
+ },
+
+ deleteModule(moduleName) {
+ Assert.ok(false, `deleteModule: should not be called`);
+ },
+
+ getInternal() {
+ throw new Error("not expecting getInternal() to be called");
+ },
+
+ getInternalFIPS() {
+ throw new Error("not expecting getInternalFIPS() to be called");
+ },
+
+ listModules() {
+ throw new Error("not expecting listModules() to be called");
+ },
+
+ get canToggleFIPS() {
+ throw new Error("not expecting get canToggleFIPS() to be called");
+ },
+
+ toggleFIPSMode() {
+ throw new Error("not expecting toggleFIPSMode() to be called");
+ },
+
+ get isFIPSEnabled() {
+ throw new Error("not expecting get isFIPSEnabled() to be called");
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIPKCS11ModuleDB"]),
+};
+
+const gMockPromptService = {
+ alertCallCount: 0,
+ expectedText: "",
+ expectedWindow: null,
+
+ alert(parent, dialogTitle, text) {
+ this.alertCallCount++;
+ Assert.equal(
+ parent,
+ this.expectedWindow,
+ "alert: Parent should be expected window"
+ );
+ Assert.equal(dialogTitle, null, "alert: Title should be null");
+ Assert.equal(
+ text,
+ this.expectedText,
+ "alert: Actual and expected text should match"
+ );
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIPromptService"]),
+};
+
+var gMockPKCS11CID = MockRegistrar.register(
+ "@mozilla.org/security/pkcs11moduledb;1",
+ gMockPKCS11ModuleDB
+);
+var gMockPromptServiceCID = MockRegistrar.register(
+ "@mozilla.org/prompter;1",
+ gMockPromptService
+);
+
+var gMockFilePicker = SpecialPowers.MockFilePicker;
+gMockFilePicker.init(window);
+
+var gTempFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
+gTempFile.append("browser_loadPKCS11Module_ui-fakeModule");
+
+registerCleanupFunction(() => {
+ gMockFilePicker.cleanup();
+ MockRegistrar.unregister(gMockPKCS11CID);
+ MockRegistrar.unregister(gMockPromptServiceCID);
+});
+
+function resetCallCounts() {
+ gMockPKCS11ModuleDB.addModuleCallCount = 0;
+ gMockPromptService.alertCallCount = 0;
+}
+
+/**
+ * Opens the dialog shown to load a PKCS #11 module.
+ *
+ * @returns {Promise}
+ * A promise that resolves when the dialog has finished loading, with
+ * the window of the opened dialog.
+ */
+function openLoadModuleDialog() {
+ let win = window.openDialog(
+ "chrome://pippki/content/load_device.xhtml",
+ "",
+ ""
+ );
+ return new Promise(resolve => {
+ win.addEventListener(
+ "load",
+ function () {
+ executeSoon(() => resolve(win));
+ },
+ { once: true }
+ );
+ });
+}
+
+/**
+ * Presses the browse button and simulates interacting with the file picker that
+ * should be triggered.
+ *
+ * @param {window} win
+ * The dialog window.
+ * @param {boolean} cancel
+ * If true, the file picker is canceled. If false, gTempFile is chosen in
+ * the file picker and the file picker is accepted.
+ */
+async function browseToTempFile(win, cancel) {
+ gMockFilePicker.showCallback = () => {
+ gMockFilePicker.setFiles([gTempFile]);
+
+ if (cancel) {
+ info("MockFilePicker returning cancel");
+ return Ci.nsIFilePicker.returnCancel;
+ }
+
+ info("MockFilePicker returning OK");
+ return Ci.nsIFilePicker.returnOK;
+ };
+
+ info("Pressing browse button");
+ win.document.getElementById("browse").doCommand();
+ await TestUtils.topicObserved("LoadPKCS11Module:FilePickHandled");
+}
+
+add_task(async function testBrowseButton() {
+ let win = await openLoadModuleDialog();
+ let pathBox = win.document.getElementById("device_path");
+ let originalPathBoxValue = "expected path if picker is canceled";
+ pathBox.value = originalPathBoxValue;
+
+ // Test what happens if the file picker is canceled.
+ await browseToTempFile(win, true);
+ Assert.equal(
+ pathBox.value,
+ originalPathBoxValue,
+ "Path shown should be unchanged due to canceled picker"
+ );
+
+ // Test what happens if the file picker is not canceled.
+ await browseToTempFile(win, false);
+ Assert.equal(
+ pathBox.value,
+ gTempFile.path,
+ "Path shown should be same as the one chosen in the file picker"
+ );
+
+ await BrowserTestUtils.closeWindow(win);
+});
+
+function testAddModuleHelper(win, throwOnAddModule) {
+ resetCallCounts();
+ gMockPKCS11ModuleDB.expectedLibPath = gTempFile.path;
+ gMockPKCS11ModuleDB.expectedModuleName = "test module";
+ gMockPKCS11ModuleDB.throwOnAddModule = throwOnAddModule;
+
+ win.document.getElementById("device_name").value =
+ gMockPKCS11ModuleDB.expectedModuleName;
+ win.document.getElementById("device_path").value =
+ gMockPKCS11ModuleDB.expectedLibPath;
+
+ info("Accepting dialog");
+ win.document.getElementById("loaddevice").acceptDialog();
+}
+
+add_task(async function testAddModuleSuccess() {
+ let win = await openLoadModuleDialog();
+
+ testAddModuleHelper(win, false);
+ await BrowserTestUtils.windowClosed(win);
+
+ Assert.equal(
+ gMockPKCS11ModuleDB.addModuleCallCount,
+ 1,
+ "addModule() should have been called once"
+ );
+ Assert.equal(
+ gMockPromptService.alertCallCount,
+ 0,
+ "alert() should never have been called"
+ );
+});
+
+add_task(async function testAddModuleFailure() {
+ let win = await openLoadModuleDialog();
+ gMockPromptService.expectedText = "Unable to add module";
+ gMockPromptService.expectedWindow = win;
+
+ // The exception we throw in addModule is first reported as an uncaught
+ // exception by XPConnect before an exception is propagated to the actual
+ // caller.
+ expectUncaughtException(true);
+
+ testAddModuleHelper(win, true);
+ expectUncaughtException(false);
+ // If adding a module fails, the dialog will not close. As such, we have to
+ // close the window ourselves.
+ await BrowserTestUtils.closeWindow(win);
+
+ Assert.equal(
+ gMockPKCS11ModuleDB.addModuleCallCount,
+ 1,
+ "addModule() should have been called once"
+ );
+ Assert.equal(
+ gMockPromptService.alertCallCount,
+ 1,
+ "alert() should have been called once"
+ );
+});
+
+add_task(async function testCancel() {
+ let win = await openLoadModuleDialog();
+ resetCallCounts();
+
+ info("Canceling dialog");
+ win.document.getElementById("loaddevice").cancelDialog();
+
+ Assert.equal(
+ gMockPKCS11ModuleDB.addModuleCallCount,
+ 0,
+ "addModule() should never have been called"
+ );
+ Assert.equal(
+ gMockPromptService.alertCallCount,
+ 0,
+ "alert() should never have been called"
+ );
+
+ await BrowserTestUtils.windowClosed(win);
+});
+
+async function testModuleNameHelper(moduleName, acceptButtonShouldBeDisabled) {
+ let win = await openLoadModuleDialog();
+ resetCallCounts();
+
+ info(`Setting Module Name to '${moduleName}'`);
+ let moduleNameBox = win.document.getElementById("device_name");
+ moduleNameBox.value = moduleName;
+ // this makes this not a great test, but it's the easiest way to simulate this
+ moduleNameBox.onchange();
+
+ let dialogNode = win.document.querySelector("dialog");
+ Assert.equal(
+ dialogNode.getAttribute("buttondisabledaccept"),
+ acceptButtonShouldBeDisabled ? "true" : "", // it's a string
+ `dialog accept button should ${
+ acceptButtonShouldBeDisabled ? "" : "not "
+ }be disabled`
+ );
+
+ return BrowserTestUtils.closeWindow(win);
+}
+
+add_task(async function testEmptyModuleName() {
+ await testModuleNameHelper("", true);
+});
+
+add_task(async function testReservedModuleName() {
+ await testModuleNameHelper("Root Certs", true);
+});
+
+add_task(async function testAcceptableModuleName() {
+ await testModuleNameHelper("Some Module Name", false);
+});