Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

<!doctype html>
<title>Test saveAs option</title>
<script src="head.js"></script>
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
<script type="text/javascript">
"use strict";
const {FileUtils} = ChromeUtils.importESModule(
const DOWNLOAD_FILENAME = "file_download.nonext.txt";
const DEFAULT_SUBDIR = "subdir";
// We need to be able to distinguish files downloaded by the file picker from
// files downloaded without it.
let pickerDir;
let pbPickerDir; // for incognito downloads
let defaultDir;
add_task(async function setup() {
// Reset DownloadLastDir preferences in case other tests set them.
// Set up temporary directories.
let downloadDir = FileUtils.getDir("TmpD", ["downloads"]);
pickerDir = downloadDir.clone();
pickerDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
info(`Using file picker download directory ${pickerDir.path}`);
pbPickerDir = downloadDir.clone();
pbPickerDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
info(`Using private browsing file picker download directory ${pbPickerDir.path}`);
defaultDir = downloadDir.clone();
defaultDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
info(`Using default download directory ${defaultDir.path}`);
let subDir = defaultDir.clone();
subDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
isnot(pickerDir.path, defaultDir.path,
"Should be able to distinguish between files saved with or without the file picker");
isnot(pickerDir.path, pbPickerDir.path,
"Should be able to distinguish between files saved in and out of private browsing mode");
await SpecialPowers.pushPrefEnv({"set": [
["", 2],
["", defaultDir.path],
SimpleTest.registerCleanupFunction(async () => {
await SpecialPowers.popPrefEnv();
defaultDir.remove(true); // This also removes DEFAULT_SUBDIR.
add_task(async function test_downloads_saveAs() {
const pickerFile = pickerDir.clone();
const pbPickerFile = pbPickerDir.clone();
const defaultFile = defaultDir.clone();
const {MockFilePicker} = SpecialPowers;
function mockFilePickerCallback(expectedStartingDir, pickedFile) {
return fp => {
// Assert that the downloads API correctly sets the starting directory.
ok(fp.displayDirectory.equals(expectedStartingDir), "Got the expected FilePicker displayDirectory");
// Assert that the downloads API configures both default properties.
is(fp.defaultString, DOWNLOAD_FILENAME, "Got the expected FilePicker defaultString");
is(fp.defaultExtension, "txt", "Got the expected FilePicker defaultExtension");
function background() {
const url = URL.createObjectURL(new Blob(["file content"]));
browser.test.onMessage.addListener(async (filename, saveAs, isPrivate) => {
try {
let options = {
incognito: isPrivate,
// Only define the saveAs option if the argument was actually set
if (saveAs !== undefined) {
options.saveAs = saveAs;
let id = await;
browser.downloads.onChanged.addListener(delta => {
if ( == id && delta.state.current === "complete") {
browser.test.sendMessage("done", {ok: true, id});
} catch ({message}) {
browser.test.sendMessage("done", {ok: false, message});
const manifest = {
incognitoOverride: "spanning",
manifest: {permissions: ["downloads"]},
const extension = ExtensionTestUtils.loadExtension(manifest);
await extension.startup();
await extension.awaitMessage("ready");
// options should have the following properties:
// saveAs (Boolean or undefined)
// isPrivate (Boolean)
// fileName (string)
// expectedStartingDir (nsIFile)
// destinationFile (nsIFile)
async function testExpectFilePicker(options) {
ok(!options.destinationFile.exists(), "the file should have been cleaned up properly previously");
MockFilePicker.showCallback = mockFilePickerCallback(
MockFilePicker.returnValue = MockFilePicker.returnOK;
extension.sendMessage(options.fileName, options.saveAs, options.isPrivate);
let result = await extension.awaitMessage("done");
ok(result.ok, ` works with saveAs=${options.saveAs}`);
ok(options.destinationFile.exists(), "the file exists.");
is(options.destinationFile.fileSize, 12, "downloaded file is the correct size");
// Test the user canceling the save dialog.
MockFilePicker.returnValue = MockFilePicker.returnCancel;
extension.sendMessage(options.fileName, options.saveAs, options.isPrivate);
result = await extension.awaitMessage("done");
ok(!result.ok, "download rejected if the user cancels the dialog");
is(result.message, "Download canceled by the user", "with the correct message");
ok(!options.destinationFile.exists(), "file was not downloaded");
async function testNoFilePicker(saveAs) {
ok(!defaultFile.exists(), "the file should have been cleaned up properly previously");
extension.sendMessage(DOWNLOAD_FILENAME, saveAs, false);
let result = await extension.awaitMessage("done");
ok(result.ok, ` works with saveAs=${saveAs}`);
ok(defaultFile.exists(), "the file exists.");
is(defaultFile.fileSize, 12, "downloaded file is the correct size");
info("Testing that saveAs=true uses the file picker as expected");
let expectedStartingDir = defaultDir;
let fpOptions = {
saveAs: true,
isPrivate: false,
expectedStartingDir: expectedStartingDir,
destinationFile: pickerFile,
await testExpectFilePicker(fpOptions);
info("Testing that saveas=true reuses last file picker directory");
fpOptions.expectedStartingDir = pickerDir;
await testExpectFilePicker(fpOptions);
info("Testing that saveAs=true in PB reuses last directory");
let nonPBStartingDir = fpOptions.expectedStartingDir;
fpOptions.isPrivate = true;
fpOptions.destinationFile = pbPickerFile;
await testExpectFilePicker(fpOptions);
info("Testing that saveAs=true in PB uses a separate last directory");
fpOptions.expectedStartingDir = pbPickerDir;
await testExpectFilePicker(fpOptions);
info("Testing that saveAs=true in Permanent PB mode ignores the incognito option");
await SpecialPowers.pushPrefEnv({
set: [["browser.privatebrowsing.autostart", true]],
fpOptions.isPrivate = false;
fpOptions.expectedStartingDir = pbPickerDir;
await testExpectFilePicker(fpOptions);
info("Testing that saveas=true reuses the non-PB last directory after private download");
await SpecialPowers.popPrefEnv();
fpOptions.isPrivate = false;
fpOptions.expectedStartingDir = nonPBStartingDir;
fpOptions.destinationFile = pickerFile;
await testExpectFilePicker(fpOptions);
info("Testing that saveAs=true does not reuse last directory when filename contains a path separator");
fpOptions.fileName = DEFAULT_SUBDIR + "/" + DOWNLOAD_FILENAME;
let destinationFile = defaultDir.clone();
fpOptions.expectedStartingDir = destinationFile.clone();
fpOptions.destinationFile = destinationFile;
await testExpectFilePicker(fpOptions);
info("Testing that saveAs=false does not use the file picker");
fpOptions.saveAs = false;
await testNoFilePicker(fpOptions.saveAs);
// When saveAs is not set, the behavior should be determined by the Firefox
// pref that normally determines whether the "Save As" prompt should be
// displayed.
info(`Testing that the file picker is used when saveAs is not specified ` +
`but ${PROMPTLESS_DOWNLOAD_PREF} is disabled`);
fpOptions.saveAs = undefined;
await SpecialPowers.pushPrefEnv({"set": [
await testExpectFilePicker(fpOptions);
info(`Testing that the file picker is NOT used when saveAs is not ` +
`specified but ${PROMPTLESS_DOWNLOAD_PREF} is enabled`);
await SpecialPowers.popPrefEnv();
await SpecialPowers.pushPrefEnv({"set": [
await testNoFilePicker(fpOptions.saveAs);
await extension.unload();