diff options
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.js | 312 |
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); +}); |