gecko/toolkit/components/jsdownloads/test/unit/common_test_Download.js

1722 lines
58 KiB
JavaScript

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This script is loaded by "test_DownloadCore.js" and "test_DownloadLegacy.js"
* with different values of the gUseLegacySaver variable, to apply tests to both
* the "copy" and "legacy" saver implementations.
*/
"use strict";
////////////////////////////////////////////////////////////////////////////////
//// Globals
/**
* Creates and starts a new download, using either DownloadCopySaver or
* DownloadLegacySaver based on the current test run.
*
* @return {Promise}
* @resolves The newly created Download object. The download may be in progress
* or already finished. The promiseDownloadStopped function can be
* used to wait for completion.
* @rejects JavaScript exception.
*/
function promiseStartDownload(aSourceUrl) {
if (gUseLegacySaver) {
return promiseStartLegacyDownload(aSourceUrl);
}
return promiseNewDownload(aSourceUrl).then(download => {
download.start();
return download;
});
}
/**
* Creates and starts a new download, configured to keep partial data, and
* returns only when the first part of "interruptible_resumable.txt" has been
* saved to disk. You must call "continueResponses" to allow the interruptible
* request to continue.
*
* This function uses either DownloadCopySaver or DownloadLegacySaver based on
* the current test run.
*
* @return {Promise}
* @resolves The newly created Download object, still in progress.
* @rejects JavaScript exception.
*/
function promiseStartDownload_tryToKeepPartialData() {
return Task.spawn(function () {
mustInterruptResponses();
// Start a new download and configure it to keep partially downloaded data.
let download;
if (!gUseLegacySaver) {
let targetFilePath = getTempFile(TEST_TARGET_FILE_NAME).path;
download = yield Downloads.createDownload({
source: httpUrl("interruptible_resumable.txt"),
target: { path: targetFilePath,
partFilePath: targetFilePath + ".part" },
});
download.tryToKeepPartialData = true;
download.start();
} else {
// Start a download using nsIExternalHelperAppService, that is configured
// to keep partially downloaded data by default.
download = yield promiseStartExternalHelperAppServiceDownload();
}
yield promiseDownloadMidway(download);
yield promisePartFileReady(download);
throw new Task.Result(download);
});
}
/**
* This function should be called after the progress notification for a download
* is received, and waits for the worker thread of BackgroundFileSaver to
* receive the data to be written to the ".part" file on disk.
*
* @return {Promise}
* @resolves When the ".part" file has been written to disk.
* @rejects JavaScript exception.
*/
function promisePartFileReady(aDownload) {
return Task.spawn(function () {
// We don't have control over the file output code in BackgroundFileSaver.
// After we receive the download progress notification, we may only check
// that the ".part" file has been created, while its size cannot be
// determined because the file is currently open.
try {
do {
yield promiseTimeout(50);
} while (!(yield OS.File.exists(aDownload.target.partFilePath)));
} catch (ex if ex instanceof OS.File.Error) {
// This indicates that the file has been created and cannot be accessed.
// The specific error might vary with the platform.
do_print("Expected exception while checking existence: " + ex.toString());
// Wait some more time to allow the write to complete.
yield promiseTimeout(100);
}
});
}
////////////////////////////////////////////////////////////////////////////////
//// Tests
/**
* Executes a download and checks its basic properties after construction.
* The download is started by constructing the simplest Download object with
* the "copy" saver, or using the legacy nsITransfer interface.
*/
add_task(function test_basic()
{
let targetFile = getTempFile(TEST_TARGET_FILE_NAME);
let download;
if (!gUseLegacySaver) {
// When testing DownloadCopySaver, we have control over the download, thus
// we can check its basic properties before it starts.
download = yield Downloads.createDownload({
source: { url: httpUrl("source.txt") },
target: { path: targetFile.path },
saver: { type: "copy" },
});
do_check_eq(download.source.url, httpUrl("source.txt"));
do_check_eq(download.target.path, targetFile.path);
yield download.start();
} else {
// When testing DownloadLegacySaver, the download is already started when it
// is created, thus we must check its basic properties while in progress.
download = yield promiseStartLegacyDownload(null,
{ targetFile: targetFile });
do_check_eq(download.source.url, httpUrl("source.txt"));
do_check_eq(download.target.path, targetFile.path);
yield promiseDownloadStopped(download);
}
// Check additional properties on the finished download.
do_check_true(download.source.referrer === null);
yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
});
/**
* Executes a download with the tryToKeepPartialData property set, and ensures
* that the file is saved correctly. When testing DownloadLegacySaver, the
* download is executed using the nsIExternalHelperAppService component.
*/
add_task(function test_basic_tryToKeepPartialData()
{
let download = yield promiseStartDownload_tryToKeepPartialData();
continueResponses();
yield promiseDownloadStopped(download);
// The target file should now have been created, and the ".part" file deleted.
yield promiseVerifyContents(download.target.path,
TEST_DATA_SHORT + TEST_DATA_SHORT);
do_check_false(yield OS.File.exists(download.target.partFilePath));
});
/**
* Checks the referrer for downloads.
*/
add_task(function test_referrer()
{
let sourcePath = "/test_referrer.txt";
let sourceUrl = httpUrl("test_referrer.txt");
let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path;
function cleanup() {
gHttpServer.registerPathHandler(sourcePath, null);
}
do_register_cleanup(cleanup);
gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) {
aResponse.setHeader("Content-Type", "text/plain", false);
do_check_true(aRequest.hasHeader("Referer"));
do_check_eq(aRequest.getHeader("Referer"), TEST_REFERRER_URL);
});
let download = yield Downloads.createDownload({
source: { url: sourceUrl, referrer: TEST_REFERRER_URL },
target: targetPath,
});
do_check_eq(download.source.referrer, TEST_REFERRER_URL);
yield download.start();
download = yield Downloads.createDownload({
source: { url: sourceUrl, referrer: TEST_REFERRER_URL,
isPrivate: true },
target: targetPath,
});
do_check_eq(download.source.referrer, TEST_REFERRER_URL);
yield download.start();
// Test the download still works for non-HTTP channel with referrer.
sourceUrl = "data:text/html,<html><body></body></html>";
download = yield Downloads.createDownload({
source: { url: sourceUrl, referrer: TEST_REFERRER_URL },
target: targetPath,
});
do_check_eq(download.source.referrer, TEST_REFERRER_URL);
yield download.start();
cleanup();
});
/**
* Checks initial and final state and progress for a successful download.
*/
add_task(function test_initial_final_state()
{
let download;
if (!gUseLegacySaver) {
// When testing DownloadCopySaver, we have control over the download, thus
// we can check its state before it starts.
download = yield promiseNewDownload();
do_check_true(download.stopped);
do_check_false(download.succeeded);
do_check_false(download.canceled);
do_check_true(download.error === null);
do_check_eq(download.progress, 0);
do_check_true(download.startTime === null);
yield download.start();
} else {
// When testing DownloadLegacySaver, the download is already started when it
// is created, thus we cannot check its initial state.
download = yield promiseStartLegacyDownload();
yield promiseDownloadStopped(download);
}
do_check_true(download.stopped);
do_check_true(download.succeeded);
do_check_false(download.canceled);
do_check_true(download.error === null);
do_check_eq(download.progress, 100);
do_check_true(isValidDate(download.startTime));
});
/**
* Checks the notification of the final download state.
*/
add_task(function test_final_state_notified()
{
mustInterruptResponses();
let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
let onchangeNotified = false;
let lastNotifiedStopped;
let lastNotifiedProgress;
download.onchange = function () {
onchangeNotified = true;
lastNotifiedStopped = download.stopped;
lastNotifiedProgress = download.progress;
};
// Allow the download to complete.
let promiseAttempt = download.start();
continueResponses();
yield promiseAttempt;
// The view should have been notified before the download completes.
do_check_true(onchangeNotified);
do_check_true(lastNotifiedStopped);
do_check_eq(lastNotifiedProgress, 100);
});
/**
* Checks intermediate progress for a successful download.
*/
add_task(function test_intermediate_progress()
{
mustInterruptResponses();
let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
yield promiseDownloadMidway(download);
do_check_true(download.hasProgress);
do_check_eq(download.currentBytes, TEST_DATA_SHORT.length);
do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2);
// Continue after the first chunk of data is fully received.
continueResponses();
yield promiseDownloadStopped(download);
do_check_true(download.stopped);
do_check_eq(download.progress, 100);
yield promiseVerifyContents(download.target.path,
TEST_DATA_SHORT + TEST_DATA_SHORT);
});
/**
* Downloads a file with a "Content-Length" of 0 and checks the progress.
*/
add_task(function test_empty_progress()
{
let download = yield promiseStartDownload(httpUrl("empty.txt"));
yield promiseDownloadStopped(download);
do_check_true(download.stopped);
do_check_true(download.hasProgress);
do_check_eq(download.progress, 100);
do_check_eq(download.currentBytes, 0);
do_check_eq(download.totalBytes, 0);
// We should have received the content type even for an empty file.
do_check_eq(download.contentType, "text/plain");
do_check_eq((yield OS.File.stat(download.target.path)).size, 0);
});
/**
* Downloads a file with a "Content-Length" of 0 with the tryToKeepPartialData
* property set, and ensures that the file is saved correctly.
*/
add_task(function test_empty_progress_tryToKeepPartialData()
{
// Start a new download and configure it to keep partially downloaded data.
let download;
if (!gUseLegacySaver) {
let targetFilePath = getTempFile(TEST_TARGET_FILE_NAME).path;
download = yield Downloads.createDownload({
source: httpUrl("empty.txt"),
target: { path: targetFilePath,
partFilePath: targetFilePath + ".part" },
});
download.tryToKeepPartialData = true;
download.start();
} else {
// Start a download using nsIExternalHelperAppService, that is configured
// to keep partially downloaded data by default.
download = yield promiseStartExternalHelperAppServiceDownload(
httpUrl("empty.txt"));
}
yield promiseDownloadStopped(download);
// The target file should now have been created, and the ".part" file deleted.
do_check_eq((yield OS.File.stat(download.target.path)).size, 0);
do_check_false(yield OS.File.exists(download.target.partFilePath));
});
/**
* Downloads an empty file with no "Content-Length" and checks the progress.
*/
add_task(function test_empty_noprogress()
{
let sourcePath = "/test_empty_noprogress.txt";
let sourceUrl = httpUrl("test_empty_noprogress.txt");
let deferRequestReceived = Promise.defer();
// Register an interruptible handler that notifies us when the request occurs.
function cleanup() {
gHttpServer.registerPathHandler(sourcePath, null);
}
do_register_cleanup(cleanup);
registerInterruptibleHandler(sourcePath,
function firstPart(aRequest, aResponse) {
aResponse.setHeader("Content-Type", "text/plain", false);
deferRequestReceived.resolve();
}, function secondPart(aRequest, aResponse) { });
// Start the download, without allowing the request to finish.
mustInterruptResponses();
let download;
if (!gUseLegacySaver) {
// When testing DownloadCopySaver, we have control over the download, thus
// we can hook its onchange callback that will be notified when the
// download starts.
download = yield promiseNewDownload(sourceUrl);
download.onchange = function () {
if (!download.stopped) {
do_check_false(download.hasProgress);
do_check_eq(download.currentBytes, 0);
do_check_eq(download.totalBytes, 0);
}
};
download.start();
} else {
// When testing DownloadLegacySaver, the download is already started when it
// is created, and it may have already made all needed property change
// notifications, thus there is no point in checking the onchange callback.
download = yield promiseStartLegacyDownload(sourceUrl);
}
// Wait for the request to be received by the HTTP server, but don't allow the
// request to finish yet. Before checking the download state, wait for the
// events to be processed by the client.
yield deferRequestReceived.promise;
yield promiseExecuteSoon();
// Check that this download has no progress report.
do_check_false(download.stopped);
do_check_false(download.hasProgress);
do_check_eq(download.currentBytes, 0);
do_check_eq(download.totalBytes, 0);
// Now allow the response to finish.
continueResponses();
yield promiseDownloadStopped(download);
// We should have received the content type even if no progress is reported.
do_check_eq(download.contentType, "text/plain");
// Verify the state of the completed download.
do_check_true(download.stopped);
do_check_false(download.hasProgress);
do_check_eq(download.progress, 100);
do_check_eq(download.currentBytes, 0);
do_check_eq(download.totalBytes, 0);
do_check_eq((yield OS.File.stat(download.target.path)).size, 0);
});
/**
* Calls the "start" method two times before the download is finished.
*/
add_task(function test_start_twice()
{
mustInterruptResponses();
let download;
if (!gUseLegacySaver) {
// When testing DownloadCopySaver, we have control over the download, thus
// we can start the download later during the test.
download = yield promiseNewDownload(httpUrl("interruptible.txt"));
} else {
// When testing DownloadLegacySaver, the download is already started when it
// is created. Effectively, we are starting the download three times.
download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt"));
}
// Call the start method two times.
let promiseAttempt1 = download.start();
let promiseAttempt2 = download.start();
// Allow the download to finish.
continueResponses();
// Both promises should now be resolved.
yield promiseAttempt1;
yield promiseAttempt2;
do_check_true(download.stopped);
do_check_true(download.succeeded);
do_check_false(download.canceled);
do_check_true(download.error === null);
yield promiseVerifyContents(download.target.path,
TEST_DATA_SHORT + TEST_DATA_SHORT);
});
/**
* Cancels a download and verifies that its state is reported correctly.
*/
add_task(function test_cancel_midway()
{
mustInterruptResponses();
// In this test case, we execute different checks that are only possible with
// DownloadCopySaver or DownloadLegacySaver respectively.
let download;
let options = {};
if (!gUseLegacySaver) {
download = yield promiseNewDownload(httpUrl("interruptible.txt"));
} else {
download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt"),
options);
}
// Cancel the download after receiving the first part of the response.
let deferCancel = Promise.defer();
let onchange = function () {
if (!download.stopped && !download.canceled && download.progress == 50) {
// Cancel the download immediately during the notification.
deferCancel.resolve(download.cancel());
// The state change happens immediately after calling "cancel", but
// temporary files or part files may still exist at this point.
do_check_true(download.canceled);
}
};
// Register for the notification, but also call the function directly in
// case the download already reached the expected progress. This may happen
// when using DownloadLegacySaver.
download.onchange = onchange;
onchange();
let promiseAttempt;
if (!gUseLegacySaver) {
promiseAttempt = download.start();
}
// Wait on the promise returned by the "cancel" method to ensure that the
// cancellation process finished and temporary files were removed.
yield deferCancel.promise;
if (gUseLegacySaver) {
// The nsIWebBrowserPersist instance should have been canceled now.
do_check_eq(options.outPersist.result, Cr.NS_ERROR_ABORT);
}
do_check_true(download.stopped);
do_check_true(download.canceled);
do_check_true(download.error === null);
do_check_false(yield OS.File.exists(download.target.path));
// Progress properties are not reset by canceling.
do_check_eq(download.progress, 50);
do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2);
do_check_eq(download.currentBytes, TEST_DATA_SHORT.length);
if (!gUseLegacySaver) {
// The promise returned by "start" should have been rejected meanwhile.
try {
yield promiseAttempt;
do_throw("The download should have been canceled.");
} catch (ex if ex instanceof Downloads.Error) {
do_check_false(ex.becauseSourceFailed);
do_check_false(ex.becauseTargetFailed);
}
}
});
/**
* Cancels a download right after starting it.
*/
add_task(function test_cancel_immediately()
{
mustInterruptResponses();
let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
let promiseAttempt = download.start();
do_check_false(download.stopped);
let promiseCancel = download.cancel();
do_check_true(download.canceled);
// At this point, we don't know whether the download has already stopped or
// is still waiting for cancellation. We can wait on the promise returned
// by the "start" method to know for sure.
try {
yield promiseAttempt;
do_throw("The download should have been canceled.");
} catch (ex if ex instanceof Downloads.Error) {
do_check_false(ex.becauseSourceFailed);
do_check_false(ex.becauseTargetFailed);
}
do_check_true(download.stopped);
do_check_true(download.canceled);
do_check_true(download.error === null);
do_check_false(yield OS.File.exists(download.target.path));
// Check that the promise returned by the "cancel" method has been resolved.
yield promiseCancel;
});
/**
* Cancels and restarts a download sequentially.
*/
add_task(function test_cancel_midway_restart()
{
mustInterruptResponses();
let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
// The first time, cancel the download midway.
yield promiseDownloadMidway(download);
yield download.cancel();
do_check_true(download.stopped);
// The second time, we'll provide the entire interruptible response.
continueResponses();
download.onchange = null;
let promiseAttempt = download.start();
// Download state should have already been reset.
do_check_false(download.stopped);
do_check_false(download.canceled);
do_check_true(download.error === null);
// For the following test, we rely on the network layer reporting its progress
// asynchronously. Otherwise, there is nothing stopping the restarted
// download from reaching the same progress as the first request already.
do_check_eq(download.progress, 0);
do_check_eq(download.totalBytes, 0);
do_check_eq(download.currentBytes, 0);
yield promiseAttempt;
do_check_true(download.stopped);
do_check_true(download.succeeded);
do_check_false(download.canceled);
do_check_true(download.error === null);
yield promiseVerifyContents(download.target.path,
TEST_DATA_SHORT + TEST_DATA_SHORT);
});
/**
* Cancels a download and restarts it from where it stopped.
*/
add_task(function test_cancel_midway_restart_tryToKeepPartialData()
{
let download = yield promiseStartDownload_tryToKeepPartialData();
yield download.cancel();
do_check_true(download.stopped);
do_check_true(download.hasPartialData);
// The target file should not exist, but we should have kept the partial data.
do_check_false(yield OS.File.exists(download.target.path));
yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT);
// Verify that the server sent the response from the start.
do_check_eq(gMostRecentFirstBytePos, 0);
// The second time, we'll request and obtain the second part of the response,
// but we still stop when half of the remaining progress is reached.
let deferMidway = Promise.defer();
download.onchange = function () {
if (!download.stopped && !download.canceled &&
download.currentBytes == Math.floor(TEST_DATA_SHORT.length * 3 / 2)) {
download.onchange = null;
deferMidway.resolve();
}
};
mustInterruptResponses();
let promiseAttempt = download.start();
// Continue when the number of bytes we received is correct, then check that
// progress is at about 75 percent. The exact figure may vary because of
// rounding issues, since the total number of bytes in the response might not
// be a multiple of four.
yield deferMidway.promise;
do_check_true(download.progress > 72 && download.progress < 78);
// Now we allow the download to finish.
continueResponses();
yield promiseAttempt;
// Check that the server now sent the second part only.
do_check_eq(gMostRecentFirstBytePos, TEST_DATA_SHORT.length);
// The target file should now have been created, and the ".part" file deleted.
yield promiseVerifyContents(download.target.path,
TEST_DATA_SHORT + TEST_DATA_SHORT);
do_check_false(yield OS.File.exists(download.target.partFilePath));
});
/**
* Cancels a download while keeping partially downloaded data, then removes the
* data and restarts the download from the beginning.
*/
add_task(function test_cancel_midway_restart_removePartialData()
{
let download = yield promiseStartDownload_tryToKeepPartialData();
yield download.cancel();
do_check_true(download.hasPartialData);
yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT);
yield download.removePartialData();
do_check_false(download.hasPartialData);
do_check_false(yield OS.File.exists(download.target.partFilePath));
// The second time, we'll request and obtain the entire response again.
continueResponses();
yield download.start();
// Verify that the server sent the response from the start.
do_check_eq(gMostRecentFirstBytePos, 0);
// The target file should now have been created, and the ".part" file deleted.
yield promiseVerifyContents(download.target.path,
TEST_DATA_SHORT + TEST_DATA_SHORT);
do_check_false(yield OS.File.exists(download.target.partFilePath));
});
/**
* Cancels a download while keeping partially downloaded data, then removes the
* data and restarts the download from the beginning without keeping the partial
* data anymore.
*/
add_task(function test_cancel_midway_restart_tryToKeepPartialData_false()
{
let download = yield promiseStartDownload_tryToKeepPartialData();
yield download.cancel();
download.tryToKeepPartialData = false;
// The above property change does not affect existing partial data.
do_check_true(download.hasPartialData);
yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT);
yield download.removePartialData();
do_check_false(yield OS.File.exists(download.target.partFilePath));
// Restart the download from the beginning.
mustInterruptResponses();
download.start();
yield promiseDownloadMidway(download);
yield promisePartFileReady(download);
// While the download is in progress, we should still have a ".part" file.
do_check_false(download.hasPartialData);
do_check_true(yield OS.File.exists(download.target.partFilePath));
yield download.cancel();
// The ".part" file should be deleted now that the download is canceled.
do_check_false(download.hasPartialData);
do_check_false(yield OS.File.exists(download.target.partFilePath));
// The third time, we'll request and obtain the entire response again.
continueResponses();
yield download.start();
// Verify that the server sent the response from the start.
do_check_eq(gMostRecentFirstBytePos, 0);
// The target file should now have been created, and the ".part" file deleted.
yield promiseVerifyContents(download.target.path,
TEST_DATA_SHORT + TEST_DATA_SHORT);
do_check_false(yield OS.File.exists(download.target.partFilePath));
});
/**
* Cancels a download right after starting it, then restarts it immediately.
*/
add_task(function test_cancel_immediately_restart_immediately()
{
mustInterruptResponses();
let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
let promiseAttempt = download.start();
do_check_false(download.stopped);
download.cancel();
do_check_true(download.canceled);
let promiseRestarted = download.start();
do_check_false(download.stopped);
do_check_false(download.canceled);
do_check_true(download.error === null);
// For the following test, we rely on the network layer reporting its progress
// asynchronously. Otherwise, there is nothing stopping the restarted
// download from reaching the same progress as the first request already.
do_check_eq(download.hasProgress, false);
do_check_eq(download.progress, 0);
do_check_eq(download.totalBytes, 0);
do_check_eq(download.currentBytes, 0);
// Ensure the next request is now allowed to complete, regardless of whether
// the canceled request was received by the server or not.
continueResponses();
try {
yield promiseAttempt;
do_throw("The download should have been canceled.");
} catch (ex if ex instanceof Downloads.Error) {
do_check_false(ex.becauseSourceFailed);
do_check_false(ex.becauseTargetFailed);
}
yield promiseRestarted;
do_check_true(download.stopped);
do_check_true(download.succeeded);
do_check_false(download.canceled);
do_check_true(download.error === null);
yield promiseVerifyContents(download.target.path,
TEST_DATA_SHORT + TEST_DATA_SHORT);
});
/**
* Cancels a download midway, then restarts it immediately.
*/
add_task(function test_cancel_midway_restart_immediately()
{
mustInterruptResponses();
let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
let promiseAttempt = download.start();
// The first time, cancel the download midway.
yield promiseDownloadMidway(download);
download.cancel();
do_check_true(download.canceled);
let promiseRestarted = download.start();
do_check_false(download.stopped);
do_check_false(download.canceled);
do_check_true(download.error === null);
// For the following test, we rely on the network layer reporting its progress
// asynchronously. Otherwise, there is nothing stopping the restarted
// download from reaching the same progress as the first request already.
do_check_eq(download.hasProgress, false);
do_check_eq(download.progress, 0);
do_check_eq(download.totalBytes, 0);
do_check_eq(download.currentBytes, 0);
// The second request is allowed to complete.
continueResponses();
try {
yield promiseAttempt;
do_throw("The download should have been canceled.");
} catch (ex if ex instanceof Downloads.Error) {
do_check_false(ex.becauseSourceFailed);
do_check_false(ex.becauseTargetFailed);
}
yield promiseRestarted;
do_check_true(download.stopped);
do_check_true(download.succeeded);
do_check_false(download.canceled);
do_check_true(download.error === null);
yield promiseVerifyContents(download.target.path,
TEST_DATA_SHORT + TEST_DATA_SHORT);
});
/**
* Calls the "cancel" method on a successful download.
*/
add_task(function test_cancel_successful()
{
let download = yield promiseStartDownload();
yield promiseDownloadStopped(download);
// The cancel method should succeed with no effect.
yield download.cancel();
do_check_true(download.stopped);
do_check_true(download.succeeded);
do_check_false(download.canceled);
do_check_true(download.error === null);
yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
});
/**
* Calls the "cancel" method two times in a row.
*/
add_task(function test_cancel_twice()
{
mustInterruptResponses();
let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
let promiseAttempt = download.start();
do_check_false(download.stopped);
let promiseCancel1 = download.cancel();
do_check_true(download.canceled);
let promiseCancel2 = download.cancel();
try {
yield promiseAttempt;
do_throw("The download should have been canceled.");
} catch (ex if ex instanceof Downloads.Error) {
do_check_false(ex.becauseSourceFailed);
do_check_false(ex.becauseTargetFailed);
}
// Both promises should now be resolved.
yield promiseCancel1;
yield promiseCancel2;
do_check_true(download.stopped);
do_check_false(download.succeeded);
do_check_true(download.canceled);
do_check_true(download.error === null);
do_check_false(yield OS.File.exists(download.target.path));
});
/**
* Checks that a download cannot be restarted after the "finalize" method.
*/
add_task(function test_finalize()
{
mustInterruptResponses();
let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
let promiseFinalized = download.finalize();
try {
yield download.start();
do_throw("It should not be possible to restart after finalization.");
} catch (ex) { }
yield promiseFinalized;
do_check_true(download.stopped);
do_check_false(download.succeeded);
do_check_true(download.canceled);
do_check_true(download.error === null);
do_check_false(yield OS.File.exists(download.target.path));
});
/**
* Checks that the "finalize" method can remove partially downloaded data.
*/
add_task(function test_finalize_tryToKeepPartialData()
{
// Check finalization without removing partial data.
let download = yield promiseStartDownload_tryToKeepPartialData();
yield download.finalize();
do_check_true(download.hasPartialData);
do_check_true(yield OS.File.exists(download.target.partFilePath));
// Clean up.
yield download.removePartialData();
// Check finalization while removing partial data.
download = yield promiseStartDownload_tryToKeepPartialData();
yield download.finalize(true);
do_check_false(download.hasPartialData);
do_check_false(yield OS.File.exists(download.target.partFilePath));
});
/**
* Checks that whenSucceeded returns a promise that is resolved after a restart.
*/
add_task(function test_whenSucceeded_after_restart()
{
mustInterruptResponses();
let promiseSucceeded;
let download;
if (!gUseLegacySaver) {
// When testing DownloadCopySaver, we have control over the download, thus
// we can verify getting a reference before the first download attempt.
download = yield promiseNewDownload(httpUrl("interruptible.txt"));
promiseSucceeded = download.whenSucceeded();
download.start();
} else {
// When testing DownloadLegacySaver, the download is already started when it
// is created, thus we cannot get the reference before the first attempt.
download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt"));
promiseSucceeded = download.whenSucceeded();
}
// Cancel the first download attempt.
yield download.cancel();
// The second request is allowed to complete.
continueResponses();
download.start();
// Wait for the download to finish by waiting on the whenSucceeded promise.
yield promiseSucceeded;
do_check_true(download.stopped);
do_check_true(download.succeeded);
do_check_false(download.canceled);
do_check_true(download.error === null);
yield promiseVerifyContents(download.target.path,
TEST_DATA_SHORT + TEST_DATA_SHORT);
});
/**
* Ensures download error details are reported on network failures.
*/
add_task(function test_error_source()
{
let serverSocket = startFakeServer();
try {
let sourceUrl = "http://localhost:" + serverSocket.port + "/source.txt";
let download;
try {
if (!gUseLegacySaver) {
// When testing DownloadCopySaver, we want to check that the promise
// returned by the "start" method is rejected.
download = yield promiseNewDownload(sourceUrl);
do_check_true(download.error === null);
yield download.start();
} else {
// When testing DownloadLegacySaver, we cannot be sure whether we are
// testing the promise returned by the "start" method or we are testing
// the "error" property checked by promiseDownloadStopped. This happens
// because we don't have control over when the download is started.
download = yield promiseStartLegacyDownload(sourceUrl);
yield promiseDownloadStopped(download);
}
do_throw("The download should have failed.");
} catch (ex if ex instanceof Downloads.Error && ex.becauseSourceFailed) {
// A specific error object is thrown when reading from the source fails.
}
// Check the properties now that the download stopped.
do_check_true(download.stopped);
do_check_false(download.canceled);
do_check_true(download.error !== null);
do_check_true(download.error.becauseSourceFailed);
do_check_false(download.error.becauseTargetFailed);
} finally {
serverSocket.close();
}
});
/**
* Ensures download error details are reported on local writing failures.
*/
add_task(function test_error_target()
{
// Create a file without write access permissions before downloading.
let targetFile = getTempFile(TEST_TARGET_FILE_NAME);
targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0);
try {
let download;
try {
if (!gUseLegacySaver) {
// When testing DownloadCopySaver, we want to check that the promise
// returned by the "start" method is rejected.
download = yield Downloads.createDownload({
source: httpUrl("source.txt"),
target: targetFile,
});
yield download.start();
} else {
// When testing DownloadLegacySaver, we cannot be sure whether we are
// testing the promise returned by the "start" method or we are testing
// the "error" property checked by promiseDownloadStopped. This happens
// because we don't have control over when the download is started.
download = yield promiseStartLegacyDownload(null,
{ targetFile: targetFile });
yield promiseDownloadStopped(download);
}
do_throw("The download should have failed.");
} catch (ex if ex instanceof Downloads.Error && ex.becauseTargetFailed) {
// A specific error object is thrown when writing to the target fails.
}
// Check the properties now that the download stopped.
do_check_true(download.stopped);
do_check_false(download.canceled);
do_check_true(download.error !== null);
do_check_true(download.error.becauseTargetFailed);
do_check_false(download.error.becauseSourceFailed);
} finally {
// Restore the default permissions to allow deleting the file on Windows.
if (targetFile.exists()) {
targetFile.permissions = FileUtils.PERMS_FILE;
targetFile.remove(false);
}
}
});
/**
* Restarts a failed download.
*/
add_task(function test_error_restart()
{
let download;
// Create a file without write access permissions before downloading.
let targetFile = getTempFile(TEST_TARGET_FILE_NAME);
targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0);
try {
// Use DownloadCopySaver or DownloadLegacySaver based on the test run,
// specifying the target file we created.
if (!gUseLegacySaver) {
download = yield Downloads.createDownload({
source: httpUrl("source.txt"),
target: targetFile,
});
download.start();
} else {
download = yield promiseStartLegacyDownload(null,
{ targetFile: targetFile });
}
yield promiseDownloadStopped(download);
do_throw("The download should have failed.");
} catch (ex if ex instanceof Downloads.Error && ex.becauseTargetFailed) {
// A specific error object is thrown when writing to the target fails.
} finally {
// Restore the default permissions to allow deleting the file on Windows.
if (targetFile.exists()) {
targetFile.permissions = FileUtils.PERMS_FILE;
// Also for Windows, rename the file before deleting. This makes the
// current file name available immediately for a new file, while deleting
// in place prevents creation of a file with the same name for some time.
targetFile.moveTo(null, targetFile.leafName + ".delete.tmp");
targetFile.remove(false);
}
}
// Restart the download and wait for completion.
yield download.start();
do_check_true(download.stopped);
do_check_true(download.succeeded);
do_check_false(download.canceled);
do_check_true(download.error === null);
do_check_eq(download.progress, 100);
yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
});
/**
* Executes download in both public and private modes.
*/
add_task(function test_public_and_private()
{
let sourcePath = "/test_public_and_private.txt";
let sourceUrl = httpUrl("test_public_and_private.txt");
let testCount = 0;
// Apply pref to allow all cookies.
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
function cleanup() {
Services.prefs.clearUserPref("network.cookie.cookieBehavior");
Services.cookies.removeAll();
gHttpServer.registerPathHandler(sourcePath, null);
}
do_register_cleanup(cleanup);
gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) {
aResponse.setHeader("Content-Type", "text/plain", false);
if (testCount == 0) {
// No cookies should exist for first public download.
do_check_false(aRequest.hasHeader("Cookie"));
aResponse.setHeader("Set-Cookie", "foobar=1", false);
testCount++;
} else if (testCount == 1) {
// The cookie should exists for second public download.
do_check_true(aRequest.hasHeader("Cookie"));
do_check_eq(aRequest.getHeader("Cookie"), "foobar=1");
testCount++;
} else if (testCount == 2) {
// No cookies should exist for first private download.
do_check_false(aRequest.hasHeader("Cookie"));
}
});
let targetFile = getTempFile(TEST_TARGET_FILE_NAME);
yield Downloads.fetch(sourceUrl, targetFile);
yield Downloads.fetch(sourceUrl, targetFile);
if (!gUseLegacySaver) {
let download = yield Downloads.createDownload({
source: { url: sourceUrl, isPrivate: true },
target: targetFile,
});
yield download.start();
} else {
let download = yield promiseStartLegacyDownload(sourceUrl,
{ isPrivate: true });
yield promiseDownloadStopped(download);
}
cleanup();
});
/**
* Checks the startTime gets updated even after a restart.
*/
add_task(function test_cancel_immediately_restart_and_check_startTime()
{
let download = yield promiseStartDownload();
let startTime = download.startTime;
do_check_true(isValidDate(download.startTime));
yield download.cancel();
do_check_eq(download.startTime.getTime(), startTime.getTime());
// Wait for a timeout.
yield promiseTimeout(10);
yield download.start();
do_check_true(download.startTime.getTime() > startTime.getTime());
});
/**
* Executes download with content-encoding.
*/
add_task(function test_with_content_encoding()
{
let sourcePath = "/test_with_content_encoding.txt";
let sourceUrl = httpUrl("test_with_content_encoding.txt");
function cleanup() {
gHttpServer.registerPathHandler(sourcePath, null);
}
do_register_cleanup(cleanup);
gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) {
aResponse.setHeader("Content-Type", "text/plain", false);
aResponse.setHeader("Content-Encoding", "gzip", false);
aResponse.setHeader("Content-Length",
"" + TEST_DATA_SHORT_GZIP_ENCODED.length, false);
let bos = new BinaryOutputStream(aResponse.bodyOutputStream);
bos.writeByteArray(TEST_DATA_SHORT_GZIP_ENCODED,
TEST_DATA_SHORT_GZIP_ENCODED.length);
});
let download = yield promiseStartDownload(sourceUrl);
yield promiseDownloadStopped(download);
do_check_eq(download.progress, 100);
do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length);
// Ensure the content matches the decoded test data.
yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
cleanup();
});
/**
* Checks that the file is not decoded if the extension matches the encoding.
*/
add_task(function test_with_content_encoding_ignore_extension()
{
let sourcePath = "/test_with_content_encoding_ignore_extension.gz";
let sourceUrl = httpUrl("test_with_content_encoding_ignore_extension.gz");
function cleanup() {
gHttpServer.registerPathHandler(sourcePath, null);
}
do_register_cleanup(cleanup);
gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) {
aResponse.setHeader("Content-Type", "text/plain", false);
aResponse.setHeader("Content-Encoding", "gzip", false);
aResponse.setHeader("Content-Length",
"" + TEST_DATA_SHORT_GZIP_ENCODED.length, false);
let bos = new BinaryOutputStream(aResponse.bodyOutputStream);
bos.writeByteArray(TEST_DATA_SHORT_GZIP_ENCODED,
TEST_DATA_SHORT_GZIP_ENCODED.length);
});
let download = yield promiseStartDownload(sourceUrl);
yield promiseDownloadStopped(download);
do_check_eq(download.progress, 100);
do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length);
// Ensure the content matches the encoded test data. We convert the data to a
// string before executing the content check.
yield promiseVerifyContents(download.target.path,
String.fromCharCode.apply(String, TEST_DATA_SHORT_GZIP_ENCODED));
cleanup();
});
/**
* Cancels and restarts a download sequentially with content-encoding.
*/
add_task(function test_cancel_midway_restart_with_content_encoding()
{
mustInterruptResponses();
let download = yield promiseStartDownload(httpUrl("interruptible_gzip.txt"));
// The first time, cancel the download midway.
let deferCancel = Promise.defer();
let onchange = function () {
if (!download.stopped && !download.canceled &&
download.currentBytes == TEST_DATA_SHORT_GZIP_ENCODED_FIRST.length) {
deferCancel.resolve(download.cancel());
}
};
// Register for the notification, but also call the function directly in
// case the download already reached the expected progress.
download.onchange = onchange;
onchange();
yield deferCancel.promise;
do_check_true(download.stopped);
// The second time, we'll provide the entire interruptible response.
continueResponses();
download.onchange = null;
yield download.start();
do_check_eq(download.progress, 100);
do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length);
yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
});
/**
* Download with parental controls enabled.
*/
add_task(function test_blocked_parental_controls()
{
function cleanup() {
DownloadIntegration.shouldBlockInTest = false;
}
do_register_cleanup(cleanup);
DownloadIntegration.shouldBlockInTest = true;
let download;
try {
if (!gUseLegacySaver) {
// When testing DownloadCopySaver, we want to check that the promise
// returned by the "start" method is rejected.
download = yield promiseNewDownload();
yield download.start();
} else {
// When testing DownloadLegacySaver, we cannot be sure whether we are
// testing the promise returned by the "start" method or we are testing
// the "error" property checked by promiseDownloadStopped. This happens
// because we don't have control over when the download is started.
download = yield promiseStartLegacyDownload();
yield promiseDownloadStopped(download);
}
do_throw("The download should have blocked.");
} catch (ex if ex instanceof Downloads.Error && ex.becauseBlocked) {
do_check_true(ex.becauseBlockedByParentalControls);
}
// Now that the download stopped, the target file should not exist.
do_check_false(yield OS.File.exists(download.target.path));
cleanup();
});
/**
* download.showContainingDirectory() action
*/
add_task(function test_showContainingDirectory() {
DownloadIntegration._deferTestShowDir = Promise.defer();
let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path;
let download = yield Downloads.createDownload({
source: { url: httpUrl("source.txt") },
target: ""
});
try {
yield download.showContainingDirectory();
do_throw("Should have failed because of an invalid path.");
} catch (ex if ex instanceof Components.Exception) {
// Invalid paths on Windows are reported with NS_ERROR_FAILURE,
// but with NS_ERROR_FILE_UNRECOGNIZED_PATH on Mac/Linux
let validResult = ex.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH ||
ex.result == Cr.NS_ERROR_FAILURE;
do_check_true(validResult);
}
download = yield Downloads.createDownload({
source: { url: httpUrl("source.txt") },
target: targetPath
});
DownloadIntegration._deferTestShowDir = Promise.defer();
download.showContainingDirectory();
let result = yield DownloadIntegration._deferTestShowDir.promise;
do_check_eq(result, "success");
});
/**
* download.launch() action
*/
add_task(function test_launch() {
let customLauncher = getTempFile("app-launcher");
// Test both with and without setting a custom application.
for (let launcherPath of [null, customLauncher.path]) {
let download;
if (!gUseLegacySaver) {
// When testing DownloadCopySaver, we have control over the download, thus
// we can test that file is not launched if download.succeeded is not set.
download = yield Downloads.createDownload({
source: httpUrl("source.txt"),
target: getTempFile(TEST_TARGET_FILE_NAME).path,
launcherPath: launcherPath,
});
try {
yield download.launch();
do_throw("Can't launch download file as it has not completed yet");
} catch (ex) {
do_check_eq(ex.message,
"launch can only be called if the download succeeded");
}
yield download.start();
} else {
// When testing DownloadLegacySaver, the download is already started when
// it is created, thus we don't test calling "launch" before starting.
download = yield promiseStartLegacyDownload(
httpUrl("source.txt"),
{ launcherPath: launcherPath });
yield promiseDownloadStopped(download);
}
do_check_false(download.launchWhenSucceeded);
DownloadIntegration._deferTestOpenFile = Promise.defer();
download.launch();
let result = yield DownloadIntegration._deferTestOpenFile.promise;
// Verify that the results match the test case.
if (!launcherPath) {
// This indicates that the default handler has been chosen.
do_check_true(result === null);
} else {
// Check the nsIMIMEInfo instance that would have been used for launching.
do_check_eq(result.preferredAction, Ci.nsIMIMEInfo.useHelperApp);
do_check_true(result.preferredApplicationHandler
.QueryInterface(Ci.nsILocalHandlerApp)
.executable.equals(customLauncher));
}
}
});
/**
* Test passing an invalid path as the launcherPath property.
*/
add_task(function test_launcherPath_invalid() {
let download = yield Downloads.createDownload({
source: { url: httpUrl("source.txt") },
target: { path: getTempFile(TEST_TARGET_FILE_NAME).path },
launcherPath: " "
});
DownloadIntegration._deferTestOpenFile = Promise.defer();
yield download.start();
try {
download.launch();
result = yield DownloadIntegration._deferTestOpenFile.promise;
do_throw("Can't launch file with invalid custom launcher")
} catch (ex if ex instanceof Components.Exception) {
// Invalid paths on Windows are reported with NS_ERROR_FAILURE,
// but with NS_ERROR_FILE_UNRECOGNIZED_PATH on Mac/Linux
let validResult = ex.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH ||
ex.result == Cr.NS_ERROR_FAILURE;
do_check_true(validResult);
}
});
/**
* Tests that download.launch() is automatically called after
* the download finishes if download.launchWhenSucceeded = true
*/
add_task(function test_launchWhenSucceeded() {
let customLauncher = getTempFile("app-launcher");
// Test both with and without setting a custom application.
for (let launcherPath of [null, customLauncher.path]) {
DownloadIntegration._deferTestOpenFile = Promise.defer();
if (!gUseLegacySaver) {
let download = yield Downloads.createDownload({
source: httpUrl("source.txt"),
target: getTempFile(TEST_TARGET_FILE_NAME).path,
launchWhenSucceeded: true,
launcherPath: launcherPath,
});
yield download.start();
} else {
let download = yield promiseStartLegacyDownload(
httpUrl("source.txt"),
{ launcherPath: launcherPath,
launchWhenSucceeded: true });
yield promiseDownloadStopped(download);
}
let result = yield DownloadIntegration._deferTestOpenFile.promise;
// Verify that the results match the test case.
if (!launcherPath) {
// This indicates that the default handler has been chosen.
do_check_true(result === null);
} else {
// Check the nsIMIMEInfo instance that would have been used for launching.
do_check_eq(result.preferredAction, Ci.nsIMIMEInfo.useHelperApp);
do_check_true(result.preferredApplicationHandler
.QueryInterface(Ci.nsILocalHandlerApp)
.executable.equals(customLauncher));
}
}
});
/**
* Tests that the proper content type is set for a normal download.
*/
add_task(function test_contentType() {
let download = yield promiseStartDownload(httpUrl("source.txt"));
yield promiseDownloadStopped(download);
do_check_eq("text/plain", download.contentType);
});
/**
* Tests that the serialization/deserialization of the startTime Date
* object works correctly.
*/
add_task(function test_toSerializable_startTime()
{
let download1 = yield promiseStartDownload(httpUrl("source.txt"));
yield promiseDownloadStopped(download1);
let serializable = download1.toSerializable();
let reserialized = JSON.parse(JSON.stringify(serializable));
let download2 = yield Downloads.createDownload(reserialized);
do_check_eq(download1.startTime.constructor.name, "Date");
do_check_eq(download2.startTime.constructor.name, "Date");
do_check_eq(download1.startTime.toJSON(), download2.startTime.toJSON());
});
/**
* This test will call the platform specific operations within
* DownloadPlatform::DownloadDone. While there is no test to verify the
* specific behaviours, this at least ensures that there is no error or crash.
*/
add_task(function test_platform_integration()
{
let downloadFiles = [];
function cleanup() {
for (let file of downloadFiles) {
file.remove(true);
}
}
do_register_cleanup(cleanup);
for (let isPrivate of [false, true]) {
DownloadIntegration.downloadDoneCalled = false;
// Some platform specific operations only operate on files outside the
// temporary directory or in the Downloads directory (such as setting
// the Windows searchable attribute, and the Mac Downloads icon bouncing),
// so use the system Downloads directory for the target file.
let targetFile = yield DownloadIntegration.getSystemDownloadsDirectory();
targetFile = targetFile.clone();
targetFile.append("test" + (Math.floor(Math.random() * 1000000)));
downloadFiles.push(targetFile);
let download;
if (gUseLegacySaver) {
download = yield promiseStartLegacyDownload(httpUrl("source.txt"),
{ targetFile: targetFile });
}
else {
download = yield Downloads.createDownload({
source: httpUrl("source.txt"),
target: targetFile,
});
download.start();
}
// Wait for the whenSucceeded promise to be resolved first.
// downloadDone should be called before the whenSucceeded promise is resolved.
yield download.whenSucceeded().then(function () {
do_check_true(DownloadIntegration.downloadDoneCalled);
});
// Then, wait for the promise returned by "start" to be resolved.
yield promiseDownloadStopped(download);
yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
}
});
/**
* Checks that downloads are added to browsing history when they start.
*/
add_task(function test_history()
{
mustInterruptResponses();
// We will wait for the visit to be notified during the download.
yield promiseClearHistory();
let promiseVisit = promiseWaitForVisit(httpUrl("interruptible.txt"));
// Start a download that is not allowed to finish yet.
let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
// The history notifications should be received before the download completes.
let [time, transitionType] = yield promiseVisit;
do_check_eq(time, download.startTime.getTime() * 1000);
do_check_eq(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD);
// Restart and complete the download after clearing history.
yield promiseClearHistory();
download.cancel();
continueResponses();
yield download.start();
// The restart should not have added a new history visit.
do_check_false(yield promiseIsURIVisited(httpUrl("interruptible.txt")));
});
/**
* Checks that downloads started by nsIHelperAppService are added to the
* browsing history when they start.
*/
add_task(function test_history_tryToKeepPartialData()
{
// We will wait for the visit to be notified during the download.
yield promiseClearHistory();
let promiseVisit =
promiseWaitForVisit(httpUrl("interruptible_resumable.txt"));
// Start a download that is not allowed to finish yet.
let beforeStartTimeMs = Date.now();
let download = yield promiseStartDownload_tryToKeepPartialData();
// The history notifications should be received before the download completes.
let [time, transitionType] = yield promiseVisit;
do_check_eq(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD);
// The time set by nsIHelperAppService may be different than the start time in
// the download object, thus we only check that it is a meaningful time. Note
// that we subtract one second from the earliest time to account for rounding.
do_check_true(time >= beforeStartTimeMs * 1000 - 1000000);
// Complete the download before finishing the test.
continueResponses();
yield promiseDownloadStopped(download);
});
/**
* Tests that the temp download files are removed on exit and exiting private
* mode after they have been launched.
*/
add_task(function test_launchWhenSucceeded_deleteTempFileOnExit() {
const kDeleteTempFileOnExit = "browser.helperApps.deleteTempFileOnExit";
let customLauncherPath = getTempFile("app-launcher").path;
let autoDeleteTargetPathOne = getTempFile(TEST_TARGET_FILE_NAME).path;
let autoDeleteTargetPathTwo = getTempFile(TEST_TARGET_FILE_NAME).path;
let noAutoDeleteTargetPath = getTempFile(TEST_TARGET_FILE_NAME).path;
let autoDeleteDownloadOne = yield Downloads.createDownload({
source: { url: httpUrl("source.txt"), isPrivate: true },
target: autoDeleteTargetPathOne,
launchWhenSucceeded: true,
launcherPath: customLauncherPath,
});
yield autoDeleteDownloadOne.start();
Services.prefs.setBoolPref(kDeleteTempFileOnExit, true);
let autoDeleteDownloadTwo = yield Downloads.createDownload({
source: httpUrl("source.txt"),
target: autoDeleteTargetPathTwo,
launchWhenSucceeded: true,
launcherPath: customLauncherPath,
});
yield autoDeleteDownloadTwo.start();
Services.prefs.setBoolPref(kDeleteTempFileOnExit, false);
let noAutoDeleteDownload = yield Downloads.createDownload({
source: httpUrl("source.txt"),
target: noAutoDeleteTargetPath,
launchWhenSucceeded: true,
launcherPath: customLauncherPath,
});
yield noAutoDeleteDownload.start();
Services.prefs.clearUserPref(kDeleteTempFileOnExit);
do_check_true(yield OS.File.exists(autoDeleteTargetPathOne));
do_check_true(yield OS.File.exists(autoDeleteTargetPathTwo));
do_check_true(yield OS.File.exists(noAutoDeleteTargetPath));
// Simulate leaving private browsing mode
Services.obs.notifyObservers(null, "last-pb-context-exited", null);
do_check_false(yield OS.File.exists(autoDeleteTargetPathOne));
// Simulate browser shutdown
let expire = Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
.getService(Ci.nsIObserver);
expire.observe(null, "profile-before-change", null);
do_check_false(yield OS.File.exists(autoDeleteTargetPathTwo));
do_check_true(yield OS.File.exists(noAutoDeleteTargetPath));
});