Bug 1936575 - PART 2: Do not trigger default browser prompt until ToS is accepted...
[gecko.git] / security / manager / ssl / tests / mochitest / browser / browser_loadPKCS11Module_ui.js
blob8b65d1e411881cd64a5b86e9911ce3934093c684
1 // Any copyright is dedicated to the Public Domain.
2 // http://creativecommons.org/publicdomain/zero/1.0/
3 "use strict";
5 // Tests the dialog used for loading PKCS #11 modules.
7 const { MockRegistrar } = ChromeUtils.importESModule(
8   "resource://testing-common/MockRegistrar.sys.mjs"
9 );
11 const gMockPKCS11ModuleDB = {
12   addModuleCallCount: 0,
13   expectedLibPath: "",
14   expectedModuleName: "",
15   throwOnAddModule: false,
17   addModule(moduleName, libraryFullPath, cryptoMechanismFlags, cipherFlags) {
18     this.addModuleCallCount++;
19     Assert.equal(
20       moduleName,
21       this.expectedModuleName,
22       "addModule: Name given should be what's in the name textbox"
23     );
24     Assert.equal(
25       libraryFullPath,
26       this.expectedLibPath,
27       "addModule: Path given should be what's in the path textbox"
28     );
29     Assert.equal(
30       cryptoMechanismFlags,
31       0,
32       "addModule: No crypto mechanism flags should be passed"
33     );
34     Assert.equal(cipherFlags, 0, "addModule: No cipher flags should be passed");
36     if (this.throwOnAddModule) {
37       throw new Error(`addModule: Throwing exception`);
38     }
39   },
41   deleteModule() {
42     Assert.ok(false, `deleteModule: should not be called`);
43   },
45   getInternal() {
46     throw new Error("not expecting getInternal() to be called");
47   },
49   getInternalFIPS() {
50     throw new Error("not expecting getInternalFIPS() to be called");
51   },
53   listModules() {
54     throw new Error("not expecting listModules() to be called");
55   },
57   get canToggleFIPS() {
58     throw new Error("not expecting get canToggleFIPS() to be called");
59   },
61   toggleFIPSMode() {
62     throw new Error("not expecting toggleFIPSMode() to be called");
63   },
65   get isFIPSEnabled() {
66     throw new Error("not expecting get isFIPSEnabled() to be called");
67   },
69   QueryInterface: ChromeUtils.generateQI(["nsIPKCS11ModuleDB"]),
72 const gMockPromptService = {
73   alertCallCount: 0,
74   expectedText: "",
75   expectedWindow: null,
77   alert(parent, dialogTitle, text) {
78     this.alertCallCount++;
79     Assert.equal(
80       parent,
81       this.expectedWindow,
82       "alert: Parent should be expected window"
83     );
84     Assert.equal(dialogTitle, null, "alert: Title should be null");
85     Assert.equal(
86       text,
87       this.expectedText,
88       "alert: Actual and expected text should match"
89     );
90   },
92   QueryInterface: ChromeUtils.generateQI(["nsIPromptService"]),
95 var gMockPKCS11CID = MockRegistrar.register(
96   "@mozilla.org/security/pkcs11moduledb;1",
97   gMockPKCS11ModuleDB
99 var gMockPromptServiceCID = MockRegistrar.register(
100   "@mozilla.org/prompter;1",
101   gMockPromptService
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.
124  * @returns {Promise}
125  *          A promise that resolves when the dialog has finished loading, with
126  *          the window of the opened dialog.
127  */
128 function openLoadModuleDialog() {
129   let win = window.openDialog(
130     "chrome://pippki/content/load_device.xhtml",
131     "",
132     ""
133   );
134   return new Promise(resolve => {
135     win.addEventListener(
136       "load",
137       function () {
138         executeSoon(() => resolve(win));
139       },
140       { once: true }
141     );
142   });
146  * Presses the browse button and simulates interacting with the file picker that
147  * should be triggered.
149  * @param {window} win
150  *        The dialog window.
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.
154  */
155 async function browseToTempFile(win, cancel) {
156   gMockFilePicker.showCallback = () => {
157     gMockFilePicker.setFiles([gTempFile]);
159     if (cancel) {
160       info("MockFilePicker returning cancel");
161       return Ci.nsIFilePicker.returnCancel;
162     }
164     info("MockFilePicker returning OK");
165     return Ci.nsIFilePicker.returnOK;
166   };
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);
181   Assert.equal(
182     pathBox.value,
183     originalPathBoxValue,
184     "Path shown should be unchanged due to canceled picker"
185   );
187   // Test what happens if the file picker is not canceled.
188   await browseToTempFile(win, false);
189   Assert.equal(
190     pathBox.value,
191     gTempFile.path,
192     "Path shown should be same as the one chosen in the file picker"
193   );
195   await BrowserTestUtils.closeWindow(win);
198 function testAddModuleHelper(win, throwOnAddModule) {
199   resetCallCounts();
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);
219   Assert.equal(
220     gMockPKCS11ModuleDB.addModuleCallCount,
221     1,
222     "addModule() should have been called once"
223   );
224   Assert.equal(
225     gMockPromptService.alertCallCount,
226     0,
227     "alert() should never have been called"
228   );
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
238   // caller.
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);
247   Assert.equal(
248     gMockPKCS11ModuleDB.addModuleCallCount,
249     1,
250     "addModule() should have been called once"
251   );
252   Assert.equal(
253     gMockPromptService.alertCallCount,
254     1,
255     "alert() should have been called once"
256   );
259 add_task(async function testCancel() {
260   let win = await openLoadModuleDialog();
261   resetCallCounts();
263   info("Canceling dialog");
264   win.document.getElementById("loaddevice").cancelDialog();
266   Assert.equal(
267     gMockPKCS11ModuleDB.addModuleCallCount,
268     0,
269     "addModule() should never have been called"
270   );
271   Assert.equal(
272     gMockPromptService.alertCallCount,
273     0,
274     "alert() should never have been called"
275   );
277   await BrowserTestUtils.windowClosed(win);
280 async function testModuleNameHelper(moduleName, acceptButtonShouldBeDisabled) {
281   let win = await openLoadModuleDialog();
282   resetCallCounts();
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");
291   Assert.equal(
292     dialogNode.getAttribute("buttondisabledaccept"),
293     acceptButtonShouldBeDisabled ? "true" : null,
294     `dialog accept button should ${
295       acceptButtonShouldBeDisabled ? "" : "not "
296     }be disabled`
297   );
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);