forked from mirrors/gecko-dev
Depends on D200551 Differential Revision: https://phabricator.services.mozilla.com/D200552
312 lines
8.3 KiB
JavaScript
312 lines
8.3 KiB
JavaScript
// 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.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" : "", // 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);
|
|
});
|