1 // Any copyright is dedicated to the Public Domain.
2 // http://creativecommons.org/publicdomain/zero/1.0/
5 // Tests the dialog used for loading PKCS #11 modules.
7 const { MockRegistrar } = ChromeUtils.importESModule(
8 "resource://testing-common/MockRegistrar.sys.mjs"
11 const gMockPKCS11ModuleDB = {
12 addModuleCallCount: 0,
14 expectedModuleName: "",
15 throwOnAddModule: false,
17 addModule(moduleName, libraryFullPath, cryptoMechanismFlags, cipherFlags) {
18 this.addModuleCallCount++;
21 this.expectedModuleName,
22 "addModule: Name given should be what's in the name textbox"
27 "addModule: Path given should be what's in the path textbox"
32 "addModule: No crypto mechanism flags should be passed"
34 Assert.equal(cipherFlags, 0, "addModule: No cipher flags should be passed");
36 if (this.throwOnAddModule) {
37 throw new Error(`addModule: Throwing exception`);
42 Assert.ok(false, `deleteModule: should not be called`);
46 throw new Error("not expecting getInternal() to be called");
50 throw new Error("not expecting getInternalFIPS() to be called");
54 throw new Error("not expecting listModules() to be called");
58 throw new Error("not expecting get canToggleFIPS() to be called");
62 throw new Error("not expecting toggleFIPSMode() to be called");
66 throw new Error("not expecting get isFIPSEnabled() to be called");
69 QueryInterface: ChromeUtils.generateQI(["nsIPKCS11ModuleDB"]),
72 const gMockPromptService = {
77 alert(parent, dialogTitle, text) {
78 this.alertCallCount++;
82 "alert: Parent should be expected window"
84 Assert.equal(dialogTitle, null, "alert: Title should be null");
88 "alert: Actual and expected text should match"
92 QueryInterface: ChromeUtils.generateQI(["nsIPromptService"]),
95 var gMockPKCS11CID = MockRegistrar.register(
96 "@mozilla.org/security/pkcs11moduledb;1",
99 var gMockPromptServiceCID = MockRegistrar.register(
100 "@mozilla.org/prompter;1",
104 var gMockFilePicker = SpecialPowers.MockFilePicker;
105 gMockFilePicker.init(window.browsingContext);
107 var gTempFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
108 gTempFile.append("browser_loadPKCS11Module_ui-fakeModule");
110 registerCleanupFunction(() => {
111 gMockFilePicker.cleanup();
112 MockRegistrar.unregister(gMockPKCS11CID);
113 MockRegistrar.unregister(gMockPromptServiceCID);
116 function resetCallCounts() {
117 gMockPKCS11ModuleDB.addModuleCallCount = 0;
118 gMockPromptService.alertCallCount = 0;
122 * Opens the dialog shown to load a PKCS #11 module.
125 * A promise that resolves when the dialog has finished loading, with
126 * the window of the opened dialog.
128 function openLoadModuleDialog() {
129 let win = window.openDialog(
130 "chrome://pippki/content/load_device.xhtml",
134 return new Promise(resolve => {
135 win.addEventListener(
138 executeSoon(() => resolve(win));
146 * Presses the browse button and simulates interacting with the file picker that
147 * should be triggered.
149 * @param {window} win
151 * @param {boolean} cancel
152 * If true, the file picker is canceled. If false, gTempFile is chosen in
153 * the file picker and the file picker is accepted.
155 async function browseToTempFile(win, cancel) {
156 gMockFilePicker.showCallback = () => {
157 gMockFilePicker.setFiles([gTempFile]);
160 info("MockFilePicker returning cancel");
161 return Ci.nsIFilePicker.returnCancel;
164 info("MockFilePicker returning OK");
165 return Ci.nsIFilePicker.returnOK;
168 info("Pressing browse button");
169 win.document.getElementById("browse").doCommand();
170 await TestUtils.topicObserved("LoadPKCS11Module:FilePickHandled");
173 add_task(async function testBrowseButton() {
174 let win = await openLoadModuleDialog();
175 let pathBox = win.document.getElementById("device_path");
176 let originalPathBoxValue = "expected path if picker is canceled";
177 pathBox.value = originalPathBoxValue;
179 // Test what happens if the file picker is canceled.
180 await browseToTempFile(win, true);
183 originalPathBoxValue,
184 "Path shown should be unchanged due to canceled picker"
187 // Test what happens if the file picker is not canceled.
188 await browseToTempFile(win, false);
192 "Path shown should be same as the one chosen in the file picker"
195 await BrowserTestUtils.closeWindow(win);
198 function testAddModuleHelper(win, throwOnAddModule) {
200 gMockPKCS11ModuleDB.expectedLibPath = gTempFile.path;
201 gMockPKCS11ModuleDB.expectedModuleName = "test module";
202 gMockPKCS11ModuleDB.throwOnAddModule = throwOnAddModule;
204 win.document.getElementById("device_name").value =
205 gMockPKCS11ModuleDB.expectedModuleName;
206 win.document.getElementById("device_path").value =
207 gMockPKCS11ModuleDB.expectedLibPath;
209 info("Accepting dialog");
210 win.document.getElementById("loaddevice").acceptDialog();
213 add_task(async function testAddModuleSuccess() {
214 let win = await openLoadModuleDialog();
216 testAddModuleHelper(win, false);
217 await BrowserTestUtils.windowClosed(win);
220 gMockPKCS11ModuleDB.addModuleCallCount,
222 "addModule() should have been called once"
225 gMockPromptService.alertCallCount,
227 "alert() should never have been called"
231 add_task(async function testAddModuleFailure() {
232 let win = await openLoadModuleDialog();
233 gMockPromptService.expectedText = "Unable to add module";
234 gMockPromptService.expectedWindow = win;
236 // The exception we throw in addModule is first reported as an uncaught
237 // exception by XPConnect before an exception is propagated to the actual
239 expectUncaughtException(true);
241 testAddModuleHelper(win, true);
242 expectUncaughtException(false);
243 // If adding a module fails, the dialog will not close. As such, we have to
244 // close the window ourselves.
245 await BrowserTestUtils.closeWindow(win);
248 gMockPKCS11ModuleDB.addModuleCallCount,
250 "addModule() should have been called once"
253 gMockPromptService.alertCallCount,
255 "alert() should have been called once"
259 add_task(async function testCancel() {
260 let win = await openLoadModuleDialog();
263 info("Canceling dialog");
264 win.document.getElementById("loaddevice").cancelDialog();
267 gMockPKCS11ModuleDB.addModuleCallCount,
269 "addModule() should never have been called"
272 gMockPromptService.alertCallCount,
274 "alert() should never have been called"
277 await BrowserTestUtils.windowClosed(win);
280 async function testModuleNameHelper(moduleName, acceptButtonShouldBeDisabled) {
281 let win = await openLoadModuleDialog();
284 info(`Setting Module Name to '${moduleName}'`);
285 let moduleNameBox = win.document.getElementById("device_name");
286 moduleNameBox.value = moduleName;
287 // this makes this not a great test, but it's the easiest way to simulate this
288 moduleNameBox.onchange();
290 let dialogNode = win.document.querySelector("dialog");
292 dialogNode.getAttribute("buttondisabledaccept"),
293 acceptButtonShouldBeDisabled ? "true" : null,
294 `dialog accept button should ${
295 acceptButtonShouldBeDisabled ? "" : "not "
299 return BrowserTestUtils.closeWindow(win);
302 add_task(async function testEmptyModuleName() {
303 await testModuleNameHelper("", true);
306 add_task(async function testReservedModuleName() {
307 await testModuleNameHelper("Root Certs", true);
310 add_task(async function testAcceptableModuleName() {
311 await testModuleNameHelper("Some Module Name", false);