Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
"use strict";
const { DownloadIntegration } = ChromeUtils.importESModule(
"resource://gre/modules/DownloadIntegration.sys.mjs"
);
const TEST_PATH = getRootDirectory(gTestPath).replace(
);
const { handleInternally, useHelperApp, useSystemDefault, saveToDisk } =
Ci.nsIHandlerInfo;
let MockFilePicker = SpecialPowers.MockFilePicker;
MockFilePicker.init(window.browsingContext);
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
["browser.download.always_ask_before_handling_new_types", false],
["browser.download.useDownloadDir", false],
],
});
registerCleanupFunction(async () => {
let hiddenPromise = BrowserTestUtils.waitForEvent(
DownloadsPanel.panel,
"popuphidden"
);
DownloadsPanel.hidePanel();
await hiddenPromise;
MockFilePicker.cleanup();
});
});
// This test ensures that a "Save as..." filepicker dialog is shown for a file
// if useDownloadDir ("Always ask where to save files") is set to false and
// the filetype is set to save to disk.
add_task(async function aDownloadSavedToDiskPromptsForFolder() {
let publicList = await Downloads.getList(Downloads.PUBLIC);
ensureMIMEState(
{ preferredAction: saveToDisk },
{ type: "text/plain", ext: "txt" }
);
registerCleanupFunction(async () => {
await publicList.removeFinished();
});
let filePickerShownPromise = new Promise(resolve => {
MockFilePicker.showCallback = function () {
setTimeout(resolve, 0);
return Ci.nsIFilePicker.returnCancel;
};
});
let loadingTab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
opening: TEST_PATH + "file_txt_attachment_test.txt",
waitForLoad: false,
waitForStateStop: true,
});
info("Waiting on filepicker.");
await filePickerShownPromise;
ok(true, "filepicker should have been shown");
BrowserTestUtils.removeTab(loadingTab);
});
// This test ensures that downloads configured to open internally create only
// one file destination when saved via the filepicker, and don't prompt.
add_task(async function testFilesHandledInternally() {
let dir = await setupFilePickerDirectory();
ensureMIMEState(
{ preferredAction: handleInternally },
{ type: "image/webp", ext: "webp" }
);
let filePickerShown = false;
MockFilePicker.showCallback = function () {
filePickerShown = true;
return Ci.nsIFilePicker.returnCancel;
};
let thirdTabPromise = BrowserTestUtils.waitForNewTab(
gBrowser,
url => {
info("Got load for " + url);
return url.endsWith("file_green.webp") && url.startsWith("file:");
},
true,
true
);
let loadingTab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
opening: TEST_PATH + "file_green.webp",
waitForLoad: false,
waitForStateStop: true,
});
let openedTab = await thirdTabPromise;
ok(!filePickerShown, "file picker should not have shown up.");
assertCorrectFile(dir, "file_green.webp");
// Cleanup
BrowserTestUtils.removeTab(loadingTab);
BrowserTestUtils.removeTab(openedTab);
});
// This test ensures that downloads configured to open with a system default
// app create only one file destination and don't open the filepicker.
add_task(async function testFilesHandledBySystemDefaultApp() {
let dir = await setupFilePickerDirectory();
ensureMIMEState({ preferredAction: useSystemDefault });
let filePickerShown = false;
MockFilePicker.showCallback = function () {
filePickerShown = true;
return Ci.nsIFilePicker.returnCancel;
};
let oldLaunchFile = DownloadIntegration.launchFile;
let launchFileCalled = new Promise(resolve => {
DownloadIntegration.launchFile = async (file, mimeInfo) => {
is(
useSystemDefault,
mimeInfo.preferredAction,
"The file should be launched with a system app handler."
);
resolve();
};
});
let loadingTab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
opening: TEST_PATH + "file_pdf_application_pdf.pdf",
waitForLoad: false,
waitForStateStop: true,
});
await launchFileCalled;
ok(!filePickerShown, "file picker should not have shown up.");
assertCorrectFile(dir, "file_pdf_application_pdf.pdf");
// Cleanup
BrowserTestUtils.removeTab(loadingTab);
DownloadIntegration.launchFile = oldLaunchFile;
});
// This test ensures that downloads configured to open with a helper app create
// only one file destination when saved via the filepicker.
add_task(async function testFilesHandledByHelperApp() {
let dir = await setupFilePickerDirectory();
// Create a custom helper app so we can check that a launcherPath is
// configured for the serialized download.
let appHandler = Cc[
"@mozilla.org/uriloader/local-handler-app;1"
].createInstance(Ci.nsILocalHandlerApp);
appHandler.name = "Dummy Test Handler";
appHandler.executable = Services.dirsvc.get("ProfD", Ci.nsIFile);
appHandler.executable.append("helper_handler_test.exe");
if (!appHandler.executable.exists()) {
appHandler.executable.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o777);
}
ensureMIMEState({
preferredAction: useHelperApp,
preferredHandlerApp: appHandler,
});
let filePickerShown = false;
MockFilePicker.showCallback = function () {
filePickerShown = true;
return Ci.nsIFilePicker.returnCancel;
};
let publicDownloads = await Downloads.getList(Downloads.PUBLIC);
let downloadFinishedPromise = new Promise(resolve => {
publicDownloads.addView({
onDownloadChanged(download) {
if (download.succeeded || download.error) {
ok(
download.launcherPath.includes("helper_handler_test.exe"),
"Launcher path is available."
);
resolve();
}
},
});
});
let oldLaunchFile = DownloadIntegration.launchFile;
let launchFileCalled = new Promise(resolve => {
DownloadIntegration.launchFile = async (file, mimeInfo) => {
is(
useHelperApp,
mimeInfo.preferredAction,
"The file should be launched with a helper app handler."
);
resolve();
};
});
let loadingTab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
opening: TEST_PATH + "file_pdf_application_pdf.pdf",
waitForLoad: false,
waitForStateStop: true,
});
await downloadFinishedPromise;
await launchFileCalled;
ok(!filePickerShown, "file picker should not have shown up.");
assertCorrectFile(dir, "file_pdf_application_pdf.pdf");
// Cleanup
BrowserTestUtils.removeTab(loadingTab);
DownloadIntegration.launchFile = oldLaunchFile;
});
async function setupFilePickerDirectory() {
let saveDir = createSaveDir();
Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, saveDir);
Services.prefs.setIntPref("browser.download.folderList", 2);
MockFilePicker.displayDirectory = saveDir;
MockFilePicker.returnValue = MockFilePicker.returnOK;
MockFilePicker.showCallback = function (fp) {
let file = saveDir.clone();
file.append(fp.defaultString);
MockFilePicker.setFiles([file]);
};
registerCleanupFunction(async () => {
Services.prefs.clearUserPref("browser.download.dir");
Services.prefs.clearUserPref("browser.download.folderList");
let publicList = await Downloads.getList(Downloads.PUBLIC);
let unfinishedDownloads = new Set(
(await publicList.getAll()).filter(dl => !dl.succeeded && !dl.error)
);
if (unfinishedDownloads.size) {
info(`Have ${unfinishedDownloads.size} unfinished downloads, waiting.`);
await new Promise(resolve => {
let view = {
onChanged(dl) {
if (unfinishedDownloads.has(dl) && (dl.succeeded || dl.error)) {
unfinishedDownloads.delete(dl);
info(`Removed another download.`);
if (!unfinishedDownloads.size) {
publicList.removeView(view);
resolve();
}
}
},
};
publicList.addView(view);
});
}
try {
await IOUtils.remove(saveDir.path, { recursive: true });
} catch (e) {
console.error(e);
}
});
return saveDir;
}
function assertCorrectFile(saveDir, filename) {
info("Make sure additional files haven't been created.");
let iter = saveDir.directoryEntries;
let file = iter.nextFile;
ok(file.path.includes(filename), "Download has correct filename");
ok(!iter.nextFile, "Only one file was created.");
}
function createSaveDir() {
info("Creating save directory.");
let time = new Date().getTime();
let saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
saveDir.append(time);
return saveDir;
}
function ensureMIMEState(
{ preferredAction, preferredHandlerApp = null },
{ type = "application/pdf", ext = "pdf" } = {}
) {
const mimeInfo = gMimeSvc.getFromTypeAndExtension(type, ext);
mimeInfo.preferredAction = preferredAction;
mimeInfo.preferredApplicationHandler = preferredHandlerApp;
mimeInfo.alwaysAskBeforeHandling = false;
gHandlerSvc.store(mimeInfo);
}