Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

// Any copyright is dedicated to the Public Domain.
"use strict";
// Tests the dialog used for loading PKCS #11 modules.
const { MockRegistrar } = ChromeUtils.importESModule(
);
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() {
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.browsingContext);
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" : null,
`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);
});