1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/jsdownloads/test/unit/common_test_Download.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1832 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* Any copyright is dedicated to the Public Domain. 1.7 + * http://creativecommons.org/publicdomain/zero/1.0/ */ 1.8 + 1.9 +/** 1.10 + * This script is loaded by "test_DownloadCore.js" and "test_DownloadLegacy.js" 1.11 + * with different values of the gUseLegacySaver variable, to apply tests to both 1.12 + * the "copy" and "legacy" saver implementations. 1.13 + */ 1.14 + 1.15 +"use strict"; 1.16 + 1.17 +//////////////////////////////////////////////////////////////////////////////// 1.18 +//// Globals 1.19 + 1.20 +/** 1.21 + * Creates and starts a new download, using either DownloadCopySaver or 1.22 + * DownloadLegacySaver based on the current test run. 1.23 + * 1.24 + * @return {Promise} 1.25 + * @resolves The newly created Download object. The download may be in progress 1.26 + * or already finished. The promiseDownloadStopped function can be 1.27 + * used to wait for completion. 1.28 + * @rejects JavaScript exception. 1.29 + */ 1.30 +function promiseStartDownload(aSourceUrl) { 1.31 + if (gUseLegacySaver) { 1.32 + return promiseStartLegacyDownload(aSourceUrl); 1.33 + } 1.34 + 1.35 + return promiseNewDownload(aSourceUrl).then(download => { 1.36 + download.start(); 1.37 + return download; 1.38 + }); 1.39 +} 1.40 + 1.41 +/** 1.42 + * Creates and starts a new download, configured to keep partial data, and 1.43 + * returns only when the first part of "interruptible_resumable.txt" has been 1.44 + * saved to disk. You must call "continueResponses" to allow the interruptible 1.45 + * request to continue. 1.46 + * 1.47 + * This function uses either DownloadCopySaver or DownloadLegacySaver based on 1.48 + * the current test run. 1.49 + * 1.50 + * @return {Promise} 1.51 + * @resolves The newly created Download object, still in progress. 1.52 + * @rejects JavaScript exception. 1.53 + */ 1.54 +function promiseStartDownload_tryToKeepPartialData() { 1.55 + return Task.spawn(function () { 1.56 + mustInterruptResponses(); 1.57 + 1.58 + // Start a new download and configure it to keep partially downloaded data. 1.59 + let download; 1.60 + if (!gUseLegacySaver) { 1.61 + let targetFilePath = getTempFile(TEST_TARGET_FILE_NAME).path; 1.62 + download = yield Downloads.createDownload({ 1.63 + source: httpUrl("interruptible_resumable.txt"), 1.64 + target: { path: targetFilePath, 1.65 + partFilePath: targetFilePath + ".part" }, 1.66 + }); 1.67 + download.tryToKeepPartialData = true; 1.68 + download.start(); 1.69 + } else { 1.70 + // Start a download using nsIExternalHelperAppService, that is configured 1.71 + // to keep partially downloaded data by default. 1.72 + download = yield promiseStartExternalHelperAppServiceDownload(); 1.73 + } 1.74 + 1.75 + yield promiseDownloadMidway(download); 1.76 + yield promisePartFileReady(download); 1.77 + 1.78 + throw new Task.Result(download); 1.79 + }); 1.80 +} 1.81 + 1.82 +/** 1.83 + * This function should be called after the progress notification for a download 1.84 + * is received, and waits for the worker thread of BackgroundFileSaver to 1.85 + * receive the data to be written to the ".part" file on disk. 1.86 + * 1.87 + * @return {Promise} 1.88 + * @resolves When the ".part" file has been written to disk. 1.89 + * @rejects JavaScript exception. 1.90 + */ 1.91 +function promisePartFileReady(aDownload) { 1.92 + return Task.spawn(function () { 1.93 + // We don't have control over the file output code in BackgroundFileSaver. 1.94 + // After we receive the download progress notification, we may only check 1.95 + // that the ".part" file has been created, while its size cannot be 1.96 + // determined because the file is currently open. 1.97 + try { 1.98 + do { 1.99 + yield promiseTimeout(50); 1.100 + } while (!(yield OS.File.exists(aDownload.target.partFilePath))); 1.101 + } catch (ex if ex instanceof OS.File.Error) { 1.102 + // This indicates that the file has been created and cannot be accessed. 1.103 + // The specific error might vary with the platform. 1.104 + do_print("Expected exception while checking existence: " + ex.toString()); 1.105 + // Wait some more time to allow the write to complete. 1.106 + yield promiseTimeout(100); 1.107 + } 1.108 + }); 1.109 +} 1.110 + 1.111 +//////////////////////////////////////////////////////////////////////////////// 1.112 +//// Tests 1.113 + 1.114 +/** 1.115 + * Executes a download and checks its basic properties after construction. 1.116 + * The download is started by constructing the simplest Download object with 1.117 + * the "copy" saver, or using the legacy nsITransfer interface. 1.118 + */ 1.119 +add_task(function test_basic() 1.120 +{ 1.121 + let targetFile = getTempFile(TEST_TARGET_FILE_NAME); 1.122 + 1.123 + let download; 1.124 + if (!gUseLegacySaver) { 1.125 + // When testing DownloadCopySaver, we have control over the download, thus 1.126 + // we can check its basic properties before it starts. 1.127 + download = yield Downloads.createDownload({ 1.128 + source: { url: httpUrl("source.txt") }, 1.129 + target: { path: targetFile.path }, 1.130 + saver: { type: "copy" }, 1.131 + }); 1.132 + 1.133 + do_check_eq(download.source.url, httpUrl("source.txt")); 1.134 + do_check_eq(download.target.path, targetFile.path); 1.135 + 1.136 + yield download.start(); 1.137 + } else { 1.138 + // When testing DownloadLegacySaver, the download is already started when it 1.139 + // is created, thus we must check its basic properties while in progress. 1.140 + download = yield promiseStartLegacyDownload(null, 1.141 + { targetFile: targetFile }); 1.142 + 1.143 + do_check_eq(download.source.url, httpUrl("source.txt")); 1.144 + do_check_eq(download.target.path, targetFile.path); 1.145 + 1.146 + yield promiseDownloadStopped(download); 1.147 + } 1.148 + 1.149 + // Check additional properties on the finished download. 1.150 + do_check_true(download.source.referrer === null); 1.151 + 1.152 + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); 1.153 +}); 1.154 + 1.155 +/** 1.156 + * Executes a download with the tryToKeepPartialData property set, and ensures 1.157 + * that the file is saved correctly. When testing DownloadLegacySaver, the 1.158 + * download is executed using the nsIExternalHelperAppService component. 1.159 + */ 1.160 +add_task(function test_basic_tryToKeepPartialData() 1.161 +{ 1.162 + let download = yield promiseStartDownload_tryToKeepPartialData(); 1.163 + continueResponses(); 1.164 + yield promiseDownloadStopped(download); 1.165 + 1.166 + // The target file should now have been created, and the ".part" file deleted. 1.167 + yield promiseVerifyContents(download.target.path, 1.168 + TEST_DATA_SHORT + TEST_DATA_SHORT); 1.169 + do_check_false(yield OS.File.exists(download.target.partFilePath)); 1.170 + do_check_eq(32, download.saver.getSha256Hash().length); 1.171 +}); 1.172 + 1.173 +/** 1.174 + * Checks the referrer for downloads. 1.175 + */ 1.176 +add_task(function test_referrer() 1.177 +{ 1.178 + let sourcePath = "/test_referrer.txt"; 1.179 + let sourceUrl = httpUrl("test_referrer.txt"); 1.180 + let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; 1.181 + 1.182 + function cleanup() { 1.183 + gHttpServer.registerPathHandler(sourcePath, null); 1.184 + } 1.185 + do_register_cleanup(cleanup); 1.186 + 1.187 + gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { 1.188 + aResponse.setHeader("Content-Type", "text/plain", false); 1.189 + 1.190 + do_check_true(aRequest.hasHeader("Referer")); 1.191 + do_check_eq(aRequest.getHeader("Referer"), TEST_REFERRER_URL); 1.192 + }); 1.193 + let download = yield Downloads.createDownload({ 1.194 + source: { url: sourceUrl, referrer: TEST_REFERRER_URL }, 1.195 + target: targetPath, 1.196 + }); 1.197 + do_check_eq(download.source.referrer, TEST_REFERRER_URL); 1.198 + yield download.start(); 1.199 + 1.200 + download = yield Downloads.createDownload({ 1.201 + source: { url: sourceUrl, referrer: TEST_REFERRER_URL, 1.202 + isPrivate: true }, 1.203 + target: targetPath, 1.204 + }); 1.205 + do_check_eq(download.source.referrer, TEST_REFERRER_URL); 1.206 + yield download.start(); 1.207 + 1.208 + // Test the download still works for non-HTTP channel with referrer. 1.209 + sourceUrl = "data:text/html,<html><body></body></html>"; 1.210 + download = yield Downloads.createDownload({ 1.211 + source: { url: sourceUrl, referrer: TEST_REFERRER_URL }, 1.212 + target: targetPath, 1.213 + }); 1.214 + do_check_eq(download.source.referrer, TEST_REFERRER_URL); 1.215 + yield download.start(); 1.216 + 1.217 + cleanup(); 1.218 +}); 1.219 + 1.220 +/** 1.221 + * Checks initial and final state and progress for a successful download. 1.222 + */ 1.223 +add_task(function test_initial_final_state() 1.224 +{ 1.225 + let download; 1.226 + if (!gUseLegacySaver) { 1.227 + // When testing DownloadCopySaver, we have control over the download, thus 1.228 + // we can check its state before it starts. 1.229 + download = yield promiseNewDownload(); 1.230 + 1.231 + do_check_true(download.stopped); 1.232 + do_check_false(download.succeeded); 1.233 + do_check_false(download.canceled); 1.234 + do_check_true(download.error === null); 1.235 + do_check_eq(download.progress, 0); 1.236 + do_check_true(download.startTime === null); 1.237 + 1.238 + yield download.start(); 1.239 + } else { 1.240 + // When testing DownloadLegacySaver, the download is already started when it 1.241 + // is created, thus we cannot check its initial state. 1.242 + download = yield promiseStartLegacyDownload(); 1.243 + yield promiseDownloadStopped(download); 1.244 + } 1.245 + 1.246 + do_check_true(download.stopped); 1.247 + do_check_true(download.succeeded); 1.248 + do_check_false(download.canceled); 1.249 + do_check_true(download.error === null); 1.250 + do_check_eq(download.progress, 100); 1.251 + do_check_true(isValidDate(download.startTime)); 1.252 +}); 1.253 + 1.254 +/** 1.255 + * Checks the notification of the final download state. 1.256 + */ 1.257 +add_task(function test_final_state_notified() 1.258 +{ 1.259 + mustInterruptResponses(); 1.260 + 1.261 + let download = yield promiseStartDownload(httpUrl("interruptible.txt")); 1.262 + 1.263 + let onchangeNotified = false; 1.264 + let lastNotifiedStopped; 1.265 + let lastNotifiedProgress; 1.266 + download.onchange = function () { 1.267 + onchangeNotified = true; 1.268 + lastNotifiedStopped = download.stopped; 1.269 + lastNotifiedProgress = download.progress; 1.270 + }; 1.271 + 1.272 + // Allow the download to complete. 1.273 + let promiseAttempt = download.start(); 1.274 + continueResponses(); 1.275 + yield promiseAttempt; 1.276 + 1.277 + // The view should have been notified before the download completes. 1.278 + do_check_true(onchangeNotified); 1.279 + do_check_true(lastNotifiedStopped); 1.280 + do_check_eq(lastNotifiedProgress, 100); 1.281 +}); 1.282 + 1.283 +/** 1.284 + * Checks intermediate progress for a successful download. 1.285 + */ 1.286 +add_task(function test_intermediate_progress() 1.287 +{ 1.288 + mustInterruptResponses(); 1.289 + 1.290 + let download = yield promiseStartDownload(httpUrl("interruptible.txt")); 1.291 + 1.292 + yield promiseDownloadMidway(download); 1.293 + 1.294 + do_check_true(download.hasProgress); 1.295 + do_check_eq(download.currentBytes, TEST_DATA_SHORT.length); 1.296 + do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2); 1.297 + 1.298 + // Continue after the first chunk of data is fully received. 1.299 + continueResponses(); 1.300 + yield promiseDownloadStopped(download); 1.301 + 1.302 + do_check_true(download.stopped); 1.303 + do_check_eq(download.progress, 100); 1.304 + 1.305 + yield promiseVerifyContents(download.target.path, 1.306 + TEST_DATA_SHORT + TEST_DATA_SHORT); 1.307 +}); 1.308 + 1.309 +/** 1.310 + * Downloads a file with a "Content-Length" of 0 and checks the progress. 1.311 + */ 1.312 +add_task(function test_empty_progress() 1.313 +{ 1.314 + let download = yield promiseStartDownload(httpUrl("empty.txt")); 1.315 + yield promiseDownloadStopped(download); 1.316 + 1.317 + do_check_true(download.stopped); 1.318 + do_check_true(download.hasProgress); 1.319 + do_check_eq(download.progress, 100); 1.320 + do_check_eq(download.currentBytes, 0); 1.321 + do_check_eq(download.totalBytes, 0); 1.322 + 1.323 + // We should have received the content type even for an empty file. 1.324 + do_check_eq(download.contentType, "text/plain"); 1.325 + 1.326 + do_check_eq((yield OS.File.stat(download.target.path)).size, 0); 1.327 +}); 1.328 + 1.329 +/** 1.330 + * Downloads a file with a "Content-Length" of 0 with the tryToKeepPartialData 1.331 + * property set, and ensures that the file is saved correctly. 1.332 + */ 1.333 +add_task(function test_empty_progress_tryToKeepPartialData() 1.334 +{ 1.335 + // Start a new download and configure it to keep partially downloaded data. 1.336 + let download; 1.337 + if (!gUseLegacySaver) { 1.338 + let targetFilePath = getTempFile(TEST_TARGET_FILE_NAME).path; 1.339 + download = yield Downloads.createDownload({ 1.340 + source: httpUrl("empty.txt"), 1.341 + target: { path: targetFilePath, 1.342 + partFilePath: targetFilePath + ".part" }, 1.343 + }); 1.344 + download.tryToKeepPartialData = true; 1.345 + download.start(); 1.346 + } else { 1.347 + // Start a download using nsIExternalHelperAppService, that is configured 1.348 + // to keep partially downloaded data by default. 1.349 + download = yield promiseStartExternalHelperAppServiceDownload( 1.350 + httpUrl("empty.txt")); 1.351 + } 1.352 + yield promiseDownloadStopped(download); 1.353 + 1.354 + // The target file should now have been created, and the ".part" file deleted. 1.355 + do_check_eq((yield OS.File.stat(download.target.path)).size, 0); 1.356 + do_check_false(yield OS.File.exists(download.target.partFilePath)); 1.357 + do_check_eq(32, download.saver.getSha256Hash().length); 1.358 +}); 1.359 + 1.360 +/** 1.361 + * Downloads an empty file with no "Content-Length" and checks the progress. 1.362 + */ 1.363 +add_task(function test_empty_noprogress() 1.364 +{ 1.365 + let sourcePath = "/test_empty_noprogress.txt"; 1.366 + let sourceUrl = httpUrl("test_empty_noprogress.txt"); 1.367 + let deferRequestReceived = Promise.defer(); 1.368 + 1.369 + // Register an interruptible handler that notifies us when the request occurs. 1.370 + function cleanup() { 1.371 + gHttpServer.registerPathHandler(sourcePath, null); 1.372 + } 1.373 + do_register_cleanup(cleanup); 1.374 + 1.375 + registerInterruptibleHandler(sourcePath, 1.376 + function firstPart(aRequest, aResponse) { 1.377 + aResponse.setHeader("Content-Type", "text/plain", false); 1.378 + deferRequestReceived.resolve(); 1.379 + }, function secondPart(aRequest, aResponse) { }); 1.380 + 1.381 + // Start the download, without allowing the request to finish. 1.382 + mustInterruptResponses(); 1.383 + let download; 1.384 + if (!gUseLegacySaver) { 1.385 + // When testing DownloadCopySaver, we have control over the download, thus 1.386 + // we can hook its onchange callback that will be notified when the 1.387 + // download starts. 1.388 + download = yield promiseNewDownload(sourceUrl); 1.389 + 1.390 + download.onchange = function () { 1.391 + if (!download.stopped) { 1.392 + do_check_false(download.hasProgress); 1.393 + do_check_eq(download.currentBytes, 0); 1.394 + do_check_eq(download.totalBytes, 0); 1.395 + } 1.396 + }; 1.397 + 1.398 + download.start(); 1.399 + } else { 1.400 + // When testing DownloadLegacySaver, the download is already started when it 1.401 + // is created, and it may have already made all needed property change 1.402 + // notifications, thus there is no point in checking the onchange callback. 1.403 + download = yield promiseStartLegacyDownload(sourceUrl); 1.404 + } 1.405 + 1.406 + // Wait for the request to be received by the HTTP server, but don't allow the 1.407 + // request to finish yet. Before checking the download state, wait for the 1.408 + // events to be processed by the client. 1.409 + yield deferRequestReceived.promise; 1.410 + yield promiseExecuteSoon(); 1.411 + 1.412 + // Check that this download has no progress report. 1.413 + do_check_false(download.stopped); 1.414 + do_check_false(download.hasProgress); 1.415 + do_check_eq(download.currentBytes, 0); 1.416 + do_check_eq(download.totalBytes, 0); 1.417 + 1.418 + // Now allow the response to finish. 1.419 + continueResponses(); 1.420 + yield promiseDownloadStopped(download); 1.421 + 1.422 + // We should have received the content type even if no progress is reported. 1.423 + do_check_eq(download.contentType, "text/plain"); 1.424 + 1.425 + // Verify the state of the completed download. 1.426 + do_check_true(download.stopped); 1.427 + do_check_false(download.hasProgress); 1.428 + do_check_eq(download.progress, 100); 1.429 + do_check_eq(download.currentBytes, 0); 1.430 + do_check_eq(download.totalBytes, 0); 1.431 + 1.432 + do_check_eq((yield OS.File.stat(download.target.path)).size, 0); 1.433 +}); 1.434 + 1.435 +/** 1.436 + * Calls the "start" method two times before the download is finished. 1.437 + */ 1.438 +add_task(function test_start_twice() 1.439 +{ 1.440 + mustInterruptResponses(); 1.441 + 1.442 + let download; 1.443 + if (!gUseLegacySaver) { 1.444 + // When testing DownloadCopySaver, we have control over the download, thus 1.445 + // we can start the download later during the test. 1.446 + download = yield promiseNewDownload(httpUrl("interruptible.txt")); 1.447 + } else { 1.448 + // When testing DownloadLegacySaver, the download is already started when it 1.449 + // is created. Effectively, we are starting the download three times. 1.450 + download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt")); 1.451 + } 1.452 + 1.453 + // Call the start method two times. 1.454 + let promiseAttempt1 = download.start(); 1.455 + let promiseAttempt2 = download.start(); 1.456 + 1.457 + // Allow the download to finish. 1.458 + continueResponses(); 1.459 + 1.460 + // Both promises should now be resolved. 1.461 + yield promiseAttempt1; 1.462 + yield promiseAttempt2; 1.463 + 1.464 + do_check_true(download.stopped); 1.465 + do_check_true(download.succeeded); 1.466 + do_check_false(download.canceled); 1.467 + do_check_true(download.error === null); 1.468 + 1.469 + yield promiseVerifyContents(download.target.path, 1.470 + TEST_DATA_SHORT + TEST_DATA_SHORT); 1.471 +}); 1.472 + 1.473 +/** 1.474 + * Cancels a download and verifies that its state is reported correctly. 1.475 + */ 1.476 +add_task(function test_cancel_midway() 1.477 +{ 1.478 + mustInterruptResponses(); 1.479 + 1.480 + // In this test case, we execute different checks that are only possible with 1.481 + // DownloadCopySaver or DownloadLegacySaver respectively. 1.482 + let download; 1.483 + let options = {}; 1.484 + if (!gUseLegacySaver) { 1.485 + download = yield promiseNewDownload(httpUrl("interruptible.txt")); 1.486 + } else { 1.487 + download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt"), 1.488 + options); 1.489 + } 1.490 + 1.491 + // Cancel the download after receiving the first part of the response. 1.492 + let deferCancel = Promise.defer(); 1.493 + let onchange = function () { 1.494 + if (!download.stopped && !download.canceled && download.progress == 50) { 1.495 + // Cancel the download immediately during the notification. 1.496 + deferCancel.resolve(download.cancel()); 1.497 + 1.498 + // The state change happens immediately after calling "cancel", but 1.499 + // temporary files or part files may still exist at this point. 1.500 + do_check_true(download.canceled); 1.501 + } 1.502 + }; 1.503 + 1.504 + // Register for the notification, but also call the function directly in 1.505 + // case the download already reached the expected progress. This may happen 1.506 + // when using DownloadLegacySaver. 1.507 + download.onchange = onchange; 1.508 + onchange(); 1.509 + 1.510 + let promiseAttempt; 1.511 + if (!gUseLegacySaver) { 1.512 + promiseAttempt = download.start(); 1.513 + } 1.514 + 1.515 + // Wait on the promise returned by the "cancel" method to ensure that the 1.516 + // cancellation process finished and temporary files were removed. 1.517 + yield deferCancel.promise; 1.518 + 1.519 + if (gUseLegacySaver) { 1.520 + // The nsIWebBrowserPersist instance should have been canceled now. 1.521 + do_check_eq(options.outPersist.result, Cr.NS_ERROR_ABORT); 1.522 + } 1.523 + 1.524 + do_check_true(download.stopped); 1.525 + do_check_true(download.canceled); 1.526 + do_check_true(download.error === null); 1.527 + 1.528 + do_check_false(yield OS.File.exists(download.target.path)); 1.529 + 1.530 + // Progress properties are not reset by canceling. 1.531 + do_check_eq(download.progress, 50); 1.532 + do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2); 1.533 + do_check_eq(download.currentBytes, TEST_DATA_SHORT.length); 1.534 + 1.535 + if (!gUseLegacySaver) { 1.536 + // The promise returned by "start" should have been rejected meanwhile. 1.537 + try { 1.538 + yield promiseAttempt; 1.539 + do_throw("The download should have been canceled."); 1.540 + } catch (ex if ex instanceof Downloads.Error) { 1.541 + do_check_false(ex.becauseSourceFailed); 1.542 + do_check_false(ex.becauseTargetFailed); 1.543 + } 1.544 + } 1.545 +}); 1.546 + 1.547 +/** 1.548 + * Cancels a download while keeping partially downloaded data, and verifies that 1.549 + * both the target file and the ".part" file are deleted. 1.550 + */ 1.551 +add_task(function test_cancel_midway_tryToKeepPartialData() 1.552 +{ 1.553 + let download = yield promiseStartDownload_tryToKeepPartialData(); 1.554 + 1.555 + do_check_true(yield OS.File.exists(download.target.path)); 1.556 + do_check_true(yield OS.File.exists(download.target.partFilePath)); 1.557 + 1.558 + yield download.cancel(); 1.559 + yield download.removePartialData(); 1.560 + 1.561 + do_check_true(download.stopped); 1.562 + do_check_true(download.canceled); 1.563 + do_check_true(download.error === null); 1.564 + 1.565 + do_check_false(yield OS.File.exists(download.target.path)); 1.566 + do_check_false(yield OS.File.exists(download.target.partFilePath)); 1.567 +}); 1.568 + 1.569 +/** 1.570 + * Cancels a download right after starting it. 1.571 + */ 1.572 +add_task(function test_cancel_immediately() 1.573 +{ 1.574 + mustInterruptResponses(); 1.575 + 1.576 + let download = yield promiseStartDownload(httpUrl("interruptible.txt")); 1.577 + 1.578 + let promiseAttempt = download.start(); 1.579 + do_check_false(download.stopped); 1.580 + 1.581 + let promiseCancel = download.cancel(); 1.582 + do_check_true(download.canceled); 1.583 + 1.584 + // At this point, we don't know whether the download has already stopped or 1.585 + // is still waiting for cancellation. We can wait on the promise returned 1.586 + // by the "start" method to know for sure. 1.587 + try { 1.588 + yield promiseAttempt; 1.589 + do_throw("The download should have been canceled."); 1.590 + } catch (ex if ex instanceof Downloads.Error) { 1.591 + do_check_false(ex.becauseSourceFailed); 1.592 + do_check_false(ex.becauseTargetFailed); 1.593 + } 1.594 + 1.595 + do_check_true(download.stopped); 1.596 + do_check_true(download.canceled); 1.597 + do_check_true(download.error === null); 1.598 + 1.599 + do_check_false(yield OS.File.exists(download.target.path)); 1.600 + 1.601 + // Check that the promise returned by the "cancel" method has been resolved. 1.602 + yield promiseCancel; 1.603 +}); 1.604 + 1.605 +/** 1.606 + * Cancels and restarts a download sequentially. 1.607 + */ 1.608 +add_task(function test_cancel_midway_restart() 1.609 +{ 1.610 + mustInterruptResponses(); 1.611 + 1.612 + let download = yield promiseStartDownload(httpUrl("interruptible.txt")); 1.613 + 1.614 + // The first time, cancel the download midway. 1.615 + yield promiseDownloadMidway(download); 1.616 + yield download.cancel(); 1.617 + 1.618 + do_check_true(download.stopped); 1.619 + 1.620 + // The second time, we'll provide the entire interruptible response. 1.621 + continueResponses(); 1.622 + download.onchange = null; 1.623 + let promiseAttempt = download.start(); 1.624 + 1.625 + // Download state should have already been reset. 1.626 + do_check_false(download.stopped); 1.627 + do_check_false(download.canceled); 1.628 + do_check_true(download.error === null); 1.629 + 1.630 + // For the following test, we rely on the network layer reporting its progress 1.631 + // asynchronously. Otherwise, there is nothing stopping the restarted 1.632 + // download from reaching the same progress as the first request already. 1.633 + do_check_eq(download.progress, 0); 1.634 + do_check_eq(download.totalBytes, 0); 1.635 + do_check_eq(download.currentBytes, 0); 1.636 + 1.637 + yield promiseAttempt; 1.638 + 1.639 + do_check_true(download.stopped); 1.640 + do_check_true(download.succeeded); 1.641 + do_check_false(download.canceled); 1.642 + do_check_true(download.error === null); 1.643 + 1.644 + yield promiseVerifyContents(download.target.path, 1.645 + TEST_DATA_SHORT + TEST_DATA_SHORT); 1.646 +}); 1.647 + 1.648 +/** 1.649 + * Cancels a download and restarts it from where it stopped. 1.650 + */ 1.651 +add_task(function test_cancel_midway_restart_tryToKeepPartialData() 1.652 +{ 1.653 + let download = yield promiseStartDownload_tryToKeepPartialData(); 1.654 + yield download.cancel(); 1.655 + 1.656 + do_check_true(download.stopped); 1.657 + do_check_true(download.hasPartialData); 1.658 + 1.659 + // The target file should not exist, but we should have kept the partial data. 1.660 + do_check_false(yield OS.File.exists(download.target.path)); 1.661 + yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT); 1.662 + 1.663 + // Verify that the server sent the response from the start. 1.664 + do_check_eq(gMostRecentFirstBytePos, 0); 1.665 + 1.666 + // The second time, we'll request and obtain the second part of the response, 1.667 + // but we still stop when half of the remaining progress is reached. 1.668 + let deferMidway = Promise.defer(); 1.669 + download.onchange = function () { 1.670 + if (!download.stopped && !download.canceled && 1.671 + download.currentBytes == Math.floor(TEST_DATA_SHORT.length * 3 / 2)) { 1.672 + download.onchange = null; 1.673 + deferMidway.resolve(); 1.674 + } 1.675 + }; 1.676 + 1.677 + mustInterruptResponses(); 1.678 + let promiseAttempt = download.start(); 1.679 + 1.680 + // Continue when the number of bytes we received is correct, then check that 1.681 + // progress is at about 75 percent. The exact figure may vary because of 1.682 + // rounding issues, since the total number of bytes in the response might not 1.683 + // be a multiple of four. 1.684 + yield deferMidway.promise; 1.685 + do_check_true(download.progress > 72 && download.progress < 78); 1.686 + 1.687 + // Now we allow the download to finish. 1.688 + continueResponses(); 1.689 + yield promiseAttempt; 1.690 + 1.691 + // Check that the server now sent the second part only. 1.692 + do_check_eq(gMostRecentFirstBytePos, TEST_DATA_SHORT.length); 1.693 + 1.694 + // The target file should now have been created, and the ".part" file deleted. 1.695 + yield promiseVerifyContents(download.target.path, 1.696 + TEST_DATA_SHORT + TEST_DATA_SHORT); 1.697 + do_check_false(yield OS.File.exists(download.target.partFilePath)); 1.698 +}); 1.699 + 1.700 +/** 1.701 + * Cancels a download while keeping partially downloaded data, then removes the 1.702 + * data and restarts the download from the beginning. 1.703 + */ 1.704 +add_task(function test_cancel_midway_restart_removePartialData() 1.705 +{ 1.706 + let download = yield promiseStartDownload_tryToKeepPartialData(); 1.707 + yield download.cancel(); 1.708 + 1.709 + do_check_true(download.hasPartialData); 1.710 + yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT); 1.711 + 1.712 + yield download.removePartialData(); 1.713 + 1.714 + do_check_false(download.hasPartialData); 1.715 + do_check_false(yield OS.File.exists(download.target.partFilePath)); 1.716 + 1.717 + // The second time, we'll request and obtain the entire response again. 1.718 + continueResponses(); 1.719 + yield download.start(); 1.720 + 1.721 + // Verify that the server sent the response from the start. 1.722 + do_check_eq(gMostRecentFirstBytePos, 0); 1.723 + 1.724 + // The target file should now have been created, and the ".part" file deleted. 1.725 + yield promiseVerifyContents(download.target.path, 1.726 + TEST_DATA_SHORT + TEST_DATA_SHORT); 1.727 + do_check_false(yield OS.File.exists(download.target.partFilePath)); 1.728 +}); 1.729 + 1.730 +/** 1.731 + * Cancels a download while keeping partially downloaded data, then removes the 1.732 + * data and restarts the download from the beginning without keeping the partial 1.733 + * data anymore. 1.734 + */ 1.735 +add_task(function test_cancel_midway_restart_tryToKeepPartialData_false() 1.736 +{ 1.737 + let download = yield promiseStartDownload_tryToKeepPartialData(); 1.738 + yield download.cancel(); 1.739 + 1.740 + download.tryToKeepPartialData = false; 1.741 + 1.742 + // The above property change does not affect existing partial data. 1.743 + do_check_true(download.hasPartialData); 1.744 + yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT); 1.745 + 1.746 + yield download.removePartialData(); 1.747 + do_check_false(yield OS.File.exists(download.target.partFilePath)); 1.748 + 1.749 + // Restart the download from the beginning. 1.750 + mustInterruptResponses(); 1.751 + download.start(); 1.752 + 1.753 + yield promiseDownloadMidway(download); 1.754 + yield promisePartFileReady(download); 1.755 + 1.756 + // While the download is in progress, we should still have a ".part" file. 1.757 + do_check_false(download.hasPartialData); 1.758 + do_check_true(yield OS.File.exists(download.target.partFilePath)); 1.759 + 1.760 + yield download.cancel(); 1.761 + 1.762 + // The ".part" file should be deleted now that the download is canceled. 1.763 + do_check_false(download.hasPartialData); 1.764 + do_check_false(yield OS.File.exists(download.target.partFilePath)); 1.765 + 1.766 + // The third time, we'll request and obtain the entire response again. 1.767 + continueResponses(); 1.768 + yield download.start(); 1.769 + 1.770 + // Verify that the server sent the response from the start. 1.771 + do_check_eq(gMostRecentFirstBytePos, 0); 1.772 + 1.773 + // The target file should now have been created, and the ".part" file deleted. 1.774 + yield promiseVerifyContents(download.target.path, 1.775 + TEST_DATA_SHORT + TEST_DATA_SHORT); 1.776 + do_check_false(yield OS.File.exists(download.target.partFilePath)); 1.777 +}); 1.778 + 1.779 +/** 1.780 + * Cancels a download right after starting it, then restarts it immediately. 1.781 + */ 1.782 +add_task(function test_cancel_immediately_restart_immediately() 1.783 +{ 1.784 + mustInterruptResponses(); 1.785 + 1.786 + let download = yield promiseStartDownload(httpUrl("interruptible.txt")); 1.787 + let promiseAttempt = download.start(); 1.788 + 1.789 + do_check_false(download.stopped); 1.790 + 1.791 + download.cancel(); 1.792 + do_check_true(download.canceled); 1.793 + 1.794 + let promiseRestarted = download.start(); 1.795 + do_check_false(download.stopped); 1.796 + do_check_false(download.canceled); 1.797 + do_check_true(download.error === null); 1.798 + 1.799 + // For the following test, we rely on the network layer reporting its progress 1.800 + // asynchronously. Otherwise, there is nothing stopping the restarted 1.801 + // download from reaching the same progress as the first request already. 1.802 + do_check_eq(download.hasProgress, false); 1.803 + do_check_eq(download.progress, 0); 1.804 + do_check_eq(download.totalBytes, 0); 1.805 + do_check_eq(download.currentBytes, 0); 1.806 + 1.807 + // Ensure the next request is now allowed to complete, regardless of whether 1.808 + // the canceled request was received by the server or not. 1.809 + continueResponses(); 1.810 + try { 1.811 + yield promiseAttempt; 1.812 + // If we get here, it means that the first attempt actually succeeded. In 1.813 + // fact, this could be a valid outcome, because the cancellation request may 1.814 + // not have been processed in time before the download finished. 1.815 + do_print("The download should have been canceled."); 1.816 + } catch (ex if ex instanceof Downloads.Error) { 1.817 + do_check_false(ex.becauseSourceFailed); 1.818 + do_check_false(ex.becauseTargetFailed); 1.819 + } 1.820 + 1.821 + yield promiseRestarted; 1.822 + 1.823 + do_check_true(download.stopped); 1.824 + do_check_true(download.succeeded); 1.825 + do_check_false(download.canceled); 1.826 + do_check_true(download.error === null); 1.827 + 1.828 + yield promiseVerifyContents(download.target.path, 1.829 + TEST_DATA_SHORT + TEST_DATA_SHORT); 1.830 +}); 1.831 + 1.832 +/** 1.833 + * Cancels a download midway, then restarts it immediately. 1.834 + */ 1.835 +add_task(function test_cancel_midway_restart_immediately() 1.836 +{ 1.837 + mustInterruptResponses(); 1.838 + 1.839 + let download = yield promiseStartDownload(httpUrl("interruptible.txt")); 1.840 + let promiseAttempt = download.start(); 1.841 + 1.842 + // The first time, cancel the download midway. 1.843 + yield promiseDownloadMidway(download); 1.844 + download.cancel(); 1.845 + do_check_true(download.canceled); 1.846 + 1.847 + let promiseRestarted = download.start(); 1.848 + do_check_false(download.stopped); 1.849 + do_check_false(download.canceled); 1.850 + do_check_true(download.error === null); 1.851 + 1.852 + // For the following test, we rely on the network layer reporting its progress 1.853 + // asynchronously. Otherwise, there is nothing stopping the restarted 1.854 + // download from reaching the same progress as the first request already. 1.855 + do_check_eq(download.hasProgress, false); 1.856 + do_check_eq(download.progress, 0); 1.857 + do_check_eq(download.totalBytes, 0); 1.858 + do_check_eq(download.currentBytes, 0); 1.859 + 1.860 + // The second request is allowed to complete. 1.861 + continueResponses(); 1.862 + try { 1.863 + yield promiseAttempt; 1.864 + do_throw("The download should have been canceled."); 1.865 + } catch (ex if ex instanceof Downloads.Error) { 1.866 + do_check_false(ex.becauseSourceFailed); 1.867 + do_check_false(ex.becauseTargetFailed); 1.868 + } 1.869 + 1.870 + yield promiseRestarted; 1.871 + 1.872 + do_check_true(download.stopped); 1.873 + do_check_true(download.succeeded); 1.874 + do_check_false(download.canceled); 1.875 + do_check_true(download.error === null); 1.876 + 1.877 + yield promiseVerifyContents(download.target.path, 1.878 + TEST_DATA_SHORT + TEST_DATA_SHORT); 1.879 +}); 1.880 + 1.881 +/** 1.882 + * Calls the "cancel" method on a successful download. 1.883 + */ 1.884 +add_task(function test_cancel_successful() 1.885 +{ 1.886 + let download = yield promiseStartDownload(); 1.887 + yield promiseDownloadStopped(download); 1.888 + 1.889 + // The cancel method should succeed with no effect. 1.890 + yield download.cancel(); 1.891 + 1.892 + do_check_true(download.stopped); 1.893 + do_check_true(download.succeeded); 1.894 + do_check_false(download.canceled); 1.895 + do_check_true(download.error === null); 1.896 + 1.897 + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); 1.898 +}); 1.899 + 1.900 +/** 1.901 + * Calls the "cancel" method two times in a row. 1.902 + */ 1.903 +add_task(function test_cancel_twice() 1.904 +{ 1.905 + mustInterruptResponses(); 1.906 + 1.907 + let download = yield promiseStartDownload(httpUrl("interruptible.txt")); 1.908 + 1.909 + let promiseAttempt = download.start(); 1.910 + do_check_false(download.stopped); 1.911 + 1.912 + let promiseCancel1 = download.cancel(); 1.913 + do_check_true(download.canceled); 1.914 + let promiseCancel2 = download.cancel(); 1.915 + 1.916 + try { 1.917 + yield promiseAttempt; 1.918 + do_throw("The download should have been canceled."); 1.919 + } catch (ex if ex instanceof Downloads.Error) { 1.920 + do_check_false(ex.becauseSourceFailed); 1.921 + do_check_false(ex.becauseTargetFailed); 1.922 + } 1.923 + 1.924 + // Both promises should now be resolved. 1.925 + yield promiseCancel1; 1.926 + yield promiseCancel2; 1.927 + 1.928 + do_check_true(download.stopped); 1.929 + do_check_false(download.succeeded); 1.930 + do_check_true(download.canceled); 1.931 + do_check_true(download.error === null); 1.932 + 1.933 + do_check_false(yield OS.File.exists(download.target.path)); 1.934 +}); 1.935 + 1.936 +/** 1.937 + * Checks that a download cannot be restarted after the "finalize" method. 1.938 + */ 1.939 +add_task(function test_finalize() 1.940 +{ 1.941 + mustInterruptResponses(); 1.942 + 1.943 + let download = yield promiseStartDownload(httpUrl("interruptible.txt")); 1.944 + 1.945 + let promiseFinalized = download.finalize(); 1.946 + 1.947 + try { 1.948 + yield download.start(); 1.949 + do_throw("It should not be possible to restart after finalization."); 1.950 + } catch (ex) { } 1.951 + 1.952 + yield promiseFinalized; 1.953 + 1.954 + do_check_true(download.stopped); 1.955 + do_check_false(download.succeeded); 1.956 + do_check_true(download.canceled); 1.957 + do_check_true(download.error === null); 1.958 + 1.959 + do_check_false(yield OS.File.exists(download.target.path)); 1.960 +}); 1.961 + 1.962 +/** 1.963 + * Checks that the "finalize" method can remove partially downloaded data. 1.964 + */ 1.965 +add_task(function test_finalize_tryToKeepPartialData() 1.966 +{ 1.967 + // Check finalization without removing partial data. 1.968 + let download = yield promiseStartDownload_tryToKeepPartialData(); 1.969 + yield download.finalize(); 1.970 + 1.971 + do_check_true(download.hasPartialData); 1.972 + do_check_true(yield OS.File.exists(download.target.partFilePath)); 1.973 + 1.974 + // Clean up. 1.975 + yield download.removePartialData(); 1.976 + 1.977 + // Check finalization while removing partial data. 1.978 + download = yield promiseStartDownload_tryToKeepPartialData(); 1.979 + yield download.finalize(true); 1.980 + 1.981 + do_check_false(download.hasPartialData); 1.982 + do_check_false(yield OS.File.exists(download.target.partFilePath)); 1.983 +}); 1.984 + 1.985 +/** 1.986 + * Checks that whenSucceeded returns a promise that is resolved after a restart. 1.987 + */ 1.988 +add_task(function test_whenSucceeded_after_restart() 1.989 +{ 1.990 + mustInterruptResponses(); 1.991 + 1.992 + let promiseSucceeded; 1.993 + 1.994 + let download; 1.995 + if (!gUseLegacySaver) { 1.996 + // When testing DownloadCopySaver, we have control over the download, thus 1.997 + // we can verify getting a reference before the first download attempt. 1.998 + download = yield promiseNewDownload(httpUrl("interruptible.txt")); 1.999 + promiseSucceeded = download.whenSucceeded(); 1.1000 + download.start(); 1.1001 + } else { 1.1002 + // When testing DownloadLegacySaver, the download is already started when it 1.1003 + // is created, thus we cannot get the reference before the first attempt. 1.1004 + download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt")); 1.1005 + promiseSucceeded = download.whenSucceeded(); 1.1006 + } 1.1007 + 1.1008 + // Cancel the first download attempt. 1.1009 + yield download.cancel(); 1.1010 + 1.1011 + // The second request is allowed to complete. 1.1012 + continueResponses(); 1.1013 + download.start(); 1.1014 + 1.1015 + // Wait for the download to finish by waiting on the whenSucceeded promise. 1.1016 + yield promiseSucceeded; 1.1017 + 1.1018 + do_check_true(download.stopped); 1.1019 + do_check_true(download.succeeded); 1.1020 + do_check_false(download.canceled); 1.1021 + do_check_true(download.error === null); 1.1022 + 1.1023 + yield promiseVerifyContents(download.target.path, 1.1024 + TEST_DATA_SHORT + TEST_DATA_SHORT); 1.1025 +}); 1.1026 + 1.1027 +/** 1.1028 + * Ensures download error details are reported on network failures. 1.1029 + */ 1.1030 +add_task(function test_error_source() 1.1031 +{ 1.1032 + let serverSocket = startFakeServer(); 1.1033 + try { 1.1034 + let sourceUrl = "http://localhost:" + serverSocket.port + "/source.txt"; 1.1035 + 1.1036 + let download; 1.1037 + try { 1.1038 + if (!gUseLegacySaver) { 1.1039 + // When testing DownloadCopySaver, we want to check that the promise 1.1040 + // returned by the "start" method is rejected. 1.1041 + download = yield promiseNewDownload(sourceUrl); 1.1042 + 1.1043 + do_check_true(download.error === null); 1.1044 + 1.1045 + yield download.start(); 1.1046 + } else { 1.1047 + // When testing DownloadLegacySaver, we cannot be sure whether we are 1.1048 + // testing the promise returned by the "start" method or we are testing 1.1049 + // the "error" property checked by promiseDownloadStopped. This happens 1.1050 + // because we don't have control over when the download is started. 1.1051 + download = yield promiseStartLegacyDownload(sourceUrl); 1.1052 + yield promiseDownloadStopped(download); 1.1053 + } 1.1054 + do_throw("The download should have failed."); 1.1055 + } catch (ex if ex instanceof Downloads.Error && ex.becauseSourceFailed) { 1.1056 + // A specific error object is thrown when reading from the source fails. 1.1057 + } 1.1058 + 1.1059 + // Check the properties now that the download stopped. 1.1060 + do_check_true(download.stopped); 1.1061 + do_check_false(download.canceled); 1.1062 + do_check_true(download.error !== null); 1.1063 + do_check_true(download.error.becauseSourceFailed); 1.1064 + do_check_false(download.error.becauseTargetFailed); 1.1065 + 1.1066 + do_check_false(yield OS.File.exists(download.target.path)); 1.1067 + } finally { 1.1068 + serverSocket.close(); 1.1069 + } 1.1070 +}); 1.1071 + 1.1072 +/** 1.1073 + * Ensures download error details are reported on local writing failures. 1.1074 + */ 1.1075 +add_task(function test_error_target() 1.1076 +{ 1.1077 + // Create a file without write access permissions before downloading. 1.1078 + let targetFile = getTempFile(TEST_TARGET_FILE_NAME); 1.1079 + targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0); 1.1080 + try { 1.1081 + let download; 1.1082 + try { 1.1083 + if (!gUseLegacySaver) { 1.1084 + // When testing DownloadCopySaver, we want to check that the promise 1.1085 + // returned by the "start" method is rejected. 1.1086 + download = yield Downloads.createDownload({ 1.1087 + source: httpUrl("source.txt"), 1.1088 + target: targetFile, 1.1089 + }); 1.1090 + yield download.start(); 1.1091 + } else { 1.1092 + // When testing DownloadLegacySaver, we cannot be sure whether we are 1.1093 + // testing the promise returned by the "start" method or we are testing 1.1094 + // the "error" property checked by promiseDownloadStopped. This happens 1.1095 + // because we don't have control over when the download is started. 1.1096 + download = yield promiseStartLegacyDownload(null, 1.1097 + { targetFile: targetFile }); 1.1098 + yield promiseDownloadStopped(download); 1.1099 + } 1.1100 + do_throw("The download should have failed."); 1.1101 + } catch (ex if ex instanceof Downloads.Error && ex.becauseTargetFailed) { 1.1102 + // A specific error object is thrown when writing to the target fails. 1.1103 + } 1.1104 + 1.1105 + // Check the properties now that the download stopped. 1.1106 + do_check_true(download.stopped); 1.1107 + do_check_false(download.canceled); 1.1108 + do_check_true(download.error !== null); 1.1109 + do_check_true(download.error.becauseTargetFailed); 1.1110 + do_check_false(download.error.becauseSourceFailed); 1.1111 + } finally { 1.1112 + // Restore the default permissions to allow deleting the file on Windows. 1.1113 + if (targetFile.exists()) { 1.1114 + targetFile.permissions = FileUtils.PERMS_FILE; 1.1115 + targetFile.remove(false); 1.1116 + } 1.1117 + } 1.1118 +}); 1.1119 + 1.1120 +/** 1.1121 + * Restarts a failed download. 1.1122 + */ 1.1123 +add_task(function test_error_restart() 1.1124 +{ 1.1125 + let download; 1.1126 + 1.1127 + // Create a file without write access permissions before downloading. 1.1128 + let targetFile = getTempFile(TEST_TARGET_FILE_NAME); 1.1129 + targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0); 1.1130 + try { 1.1131 + // Use DownloadCopySaver or DownloadLegacySaver based on the test run, 1.1132 + // specifying the target file we created. 1.1133 + if (!gUseLegacySaver) { 1.1134 + download = yield Downloads.createDownload({ 1.1135 + source: httpUrl("source.txt"), 1.1136 + target: targetFile, 1.1137 + }); 1.1138 + download.start(); 1.1139 + } else { 1.1140 + download = yield promiseStartLegacyDownload(null, 1.1141 + { targetFile: targetFile }); 1.1142 + } 1.1143 + yield promiseDownloadStopped(download); 1.1144 + do_throw("The download should have failed."); 1.1145 + } catch (ex if ex instanceof Downloads.Error && ex.becauseTargetFailed) { 1.1146 + // A specific error object is thrown when writing to the target fails. 1.1147 + } finally { 1.1148 + // Restore the default permissions to allow deleting the file on Windows. 1.1149 + if (targetFile.exists()) { 1.1150 + targetFile.permissions = FileUtils.PERMS_FILE; 1.1151 + 1.1152 + // Also for Windows, rename the file before deleting. This makes the 1.1153 + // current file name available immediately for a new file, while deleting 1.1154 + // in place prevents creation of a file with the same name for some time. 1.1155 + targetFile.moveTo(null, targetFile.leafName + ".delete.tmp"); 1.1156 + targetFile.remove(false); 1.1157 + } 1.1158 + } 1.1159 + 1.1160 + // Restart the download and wait for completion. 1.1161 + yield download.start(); 1.1162 + 1.1163 + do_check_true(download.stopped); 1.1164 + do_check_true(download.succeeded); 1.1165 + do_check_false(download.canceled); 1.1166 + do_check_true(download.error === null); 1.1167 + do_check_eq(download.progress, 100); 1.1168 + 1.1169 + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); 1.1170 +}); 1.1171 + 1.1172 +/** 1.1173 + * Executes download in both public and private modes. 1.1174 + */ 1.1175 +add_task(function test_public_and_private() 1.1176 +{ 1.1177 + let sourcePath = "/test_public_and_private.txt"; 1.1178 + let sourceUrl = httpUrl("test_public_and_private.txt"); 1.1179 + let testCount = 0; 1.1180 + 1.1181 + // Apply pref to allow all cookies. 1.1182 + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); 1.1183 + 1.1184 + function cleanup() { 1.1185 + Services.prefs.clearUserPref("network.cookie.cookieBehavior"); 1.1186 + Services.cookies.removeAll(); 1.1187 + gHttpServer.registerPathHandler(sourcePath, null); 1.1188 + } 1.1189 + do_register_cleanup(cleanup); 1.1190 + 1.1191 + gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { 1.1192 + aResponse.setHeader("Content-Type", "text/plain", false); 1.1193 + 1.1194 + if (testCount == 0) { 1.1195 + // No cookies should exist for first public download. 1.1196 + do_check_false(aRequest.hasHeader("Cookie")); 1.1197 + aResponse.setHeader("Set-Cookie", "foobar=1", false); 1.1198 + testCount++; 1.1199 + } else if (testCount == 1) { 1.1200 + // The cookie should exists for second public download. 1.1201 + do_check_true(aRequest.hasHeader("Cookie")); 1.1202 + do_check_eq(aRequest.getHeader("Cookie"), "foobar=1"); 1.1203 + testCount++; 1.1204 + } else if (testCount == 2) { 1.1205 + // No cookies should exist for first private download. 1.1206 + do_check_false(aRequest.hasHeader("Cookie")); 1.1207 + } 1.1208 + }); 1.1209 + 1.1210 + let targetFile = getTempFile(TEST_TARGET_FILE_NAME); 1.1211 + yield Downloads.fetch(sourceUrl, targetFile); 1.1212 + yield Downloads.fetch(sourceUrl, targetFile); 1.1213 + 1.1214 + if (!gUseLegacySaver) { 1.1215 + let download = yield Downloads.createDownload({ 1.1216 + source: { url: sourceUrl, isPrivate: true }, 1.1217 + target: targetFile, 1.1218 + }); 1.1219 + yield download.start(); 1.1220 + } else { 1.1221 + let download = yield promiseStartLegacyDownload(sourceUrl, 1.1222 + { isPrivate: true }); 1.1223 + yield promiseDownloadStopped(download); 1.1224 + } 1.1225 + 1.1226 + cleanup(); 1.1227 +}); 1.1228 + 1.1229 +/** 1.1230 + * Checks the startTime gets updated even after a restart. 1.1231 + */ 1.1232 +add_task(function test_cancel_immediately_restart_and_check_startTime() 1.1233 +{ 1.1234 + let download = yield promiseStartDownload(); 1.1235 + 1.1236 + let startTime = download.startTime; 1.1237 + do_check_true(isValidDate(download.startTime)); 1.1238 + 1.1239 + yield download.cancel(); 1.1240 + do_check_eq(download.startTime.getTime(), startTime.getTime()); 1.1241 + 1.1242 + // Wait for a timeout. 1.1243 + yield promiseTimeout(10); 1.1244 + 1.1245 + yield download.start(); 1.1246 + do_check_true(download.startTime.getTime() > startTime.getTime()); 1.1247 +}); 1.1248 + 1.1249 +/** 1.1250 + * Executes download with content-encoding. 1.1251 + */ 1.1252 +add_task(function test_with_content_encoding() 1.1253 +{ 1.1254 + let sourcePath = "/test_with_content_encoding.txt"; 1.1255 + let sourceUrl = httpUrl("test_with_content_encoding.txt"); 1.1256 + 1.1257 + function cleanup() { 1.1258 + gHttpServer.registerPathHandler(sourcePath, null); 1.1259 + } 1.1260 + do_register_cleanup(cleanup); 1.1261 + 1.1262 + gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { 1.1263 + aResponse.setHeader("Content-Type", "text/plain", false); 1.1264 + aResponse.setHeader("Content-Encoding", "gzip", false); 1.1265 + aResponse.setHeader("Content-Length", 1.1266 + "" + TEST_DATA_SHORT_GZIP_ENCODED.length, false); 1.1267 + 1.1268 + let bos = new BinaryOutputStream(aResponse.bodyOutputStream); 1.1269 + bos.writeByteArray(TEST_DATA_SHORT_GZIP_ENCODED, 1.1270 + TEST_DATA_SHORT_GZIP_ENCODED.length); 1.1271 + }); 1.1272 + 1.1273 + let download = yield promiseStartDownload(sourceUrl); 1.1274 + yield promiseDownloadStopped(download); 1.1275 + 1.1276 + do_check_eq(download.progress, 100); 1.1277 + do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length); 1.1278 + 1.1279 + // Ensure the content matches the decoded test data. 1.1280 + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); 1.1281 + 1.1282 + cleanup(); 1.1283 +}); 1.1284 + 1.1285 +/** 1.1286 + * Checks that the file is not decoded if the extension matches the encoding. 1.1287 + */ 1.1288 +add_task(function test_with_content_encoding_ignore_extension() 1.1289 +{ 1.1290 + let sourcePath = "/test_with_content_encoding_ignore_extension.gz"; 1.1291 + let sourceUrl = httpUrl("test_with_content_encoding_ignore_extension.gz"); 1.1292 + 1.1293 + function cleanup() { 1.1294 + gHttpServer.registerPathHandler(sourcePath, null); 1.1295 + } 1.1296 + do_register_cleanup(cleanup); 1.1297 + 1.1298 + gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { 1.1299 + aResponse.setHeader("Content-Type", "text/plain", false); 1.1300 + aResponse.setHeader("Content-Encoding", "gzip", false); 1.1301 + aResponse.setHeader("Content-Length", 1.1302 + "" + TEST_DATA_SHORT_GZIP_ENCODED.length, false); 1.1303 + 1.1304 + let bos = new BinaryOutputStream(aResponse.bodyOutputStream); 1.1305 + bos.writeByteArray(TEST_DATA_SHORT_GZIP_ENCODED, 1.1306 + TEST_DATA_SHORT_GZIP_ENCODED.length); 1.1307 + }); 1.1308 + 1.1309 + let download = yield promiseStartDownload(sourceUrl); 1.1310 + yield promiseDownloadStopped(download); 1.1311 + 1.1312 + do_check_eq(download.progress, 100); 1.1313 + do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length); 1.1314 + 1.1315 + // Ensure the content matches the encoded test data. We convert the data to a 1.1316 + // string before executing the content check. 1.1317 + yield promiseVerifyContents(download.target.path, 1.1318 + String.fromCharCode.apply(String, TEST_DATA_SHORT_GZIP_ENCODED)); 1.1319 + 1.1320 + cleanup(); 1.1321 +}); 1.1322 + 1.1323 +/** 1.1324 + * Cancels and restarts a download sequentially with content-encoding. 1.1325 + */ 1.1326 +add_task(function test_cancel_midway_restart_with_content_encoding() 1.1327 +{ 1.1328 + mustInterruptResponses(); 1.1329 + 1.1330 + let download = yield promiseStartDownload(httpUrl("interruptible_gzip.txt")); 1.1331 + 1.1332 + // The first time, cancel the download midway. 1.1333 + let deferCancel = Promise.defer(); 1.1334 + let onchange = function () { 1.1335 + if (!download.stopped && !download.canceled && 1.1336 + download.currentBytes == TEST_DATA_SHORT_GZIP_ENCODED_FIRST.length) { 1.1337 + deferCancel.resolve(download.cancel()); 1.1338 + } 1.1339 + }; 1.1340 + 1.1341 + // Register for the notification, but also call the function directly in 1.1342 + // case the download already reached the expected progress. 1.1343 + download.onchange = onchange; 1.1344 + onchange(); 1.1345 + 1.1346 + yield deferCancel.promise; 1.1347 + 1.1348 + do_check_true(download.stopped); 1.1349 + 1.1350 + // The second time, we'll provide the entire interruptible response. 1.1351 + continueResponses(); 1.1352 + download.onchange = null; 1.1353 + yield download.start(); 1.1354 + 1.1355 + do_check_eq(download.progress, 100); 1.1356 + do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length); 1.1357 + 1.1358 + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); 1.1359 +}); 1.1360 + 1.1361 +/** 1.1362 + * Download with parental controls enabled. 1.1363 + */ 1.1364 +add_task(function test_blocked_parental_controls() 1.1365 +{ 1.1366 + function cleanup() { 1.1367 + DownloadIntegration.shouldBlockInTest = false; 1.1368 + } 1.1369 + do_register_cleanup(cleanup); 1.1370 + DownloadIntegration.shouldBlockInTest = true; 1.1371 + 1.1372 + let download; 1.1373 + try { 1.1374 + if (!gUseLegacySaver) { 1.1375 + // When testing DownloadCopySaver, we want to check that the promise 1.1376 + // returned by the "start" method is rejected. 1.1377 + download = yield promiseNewDownload(); 1.1378 + yield download.start(); 1.1379 + } else { 1.1380 + // When testing DownloadLegacySaver, we cannot be sure whether we are 1.1381 + // testing the promise returned by the "start" method or we are testing 1.1382 + // the "error" property checked by promiseDownloadStopped. This happens 1.1383 + // because we don't have control over when the download is started. 1.1384 + download = yield promiseStartLegacyDownload(); 1.1385 + yield promiseDownloadStopped(download); 1.1386 + } 1.1387 + do_throw("The download should have blocked."); 1.1388 + } catch (ex if ex instanceof Downloads.Error && ex.becauseBlocked) { 1.1389 + do_check_true(ex.becauseBlockedByParentalControls); 1.1390 + do_check_true(download.error.becauseBlockedByParentalControls); 1.1391 + } 1.1392 + 1.1393 + // Now that the download stopped, the target file should not exist. 1.1394 + do_check_false(yield OS.File.exists(download.target.path)); 1.1395 + 1.1396 + cleanup(); 1.1397 +}); 1.1398 + 1.1399 +/** 1.1400 + * Test a download that will be blocked by Windows parental controls by 1.1401 + * resulting in an HTTP status code of 450. 1.1402 + */ 1.1403 +add_task(function test_blocked_parental_controls_httpstatus450() 1.1404 +{ 1.1405 + let download; 1.1406 + try { 1.1407 + if (!gUseLegacySaver) { 1.1408 + download = yield promiseNewDownload(httpUrl("parentalblocked.zip")); 1.1409 + yield download.start(); 1.1410 + } 1.1411 + else { 1.1412 + download = yield promiseStartLegacyDownload(httpUrl("parentalblocked.zip")); 1.1413 + yield promiseDownloadStopped(download); 1.1414 + } 1.1415 + do_throw("The download should have blocked."); 1.1416 + } catch (ex if ex instanceof Downloads.Error && ex.becauseBlocked) { 1.1417 + do_check_true(ex.becauseBlockedByParentalControls); 1.1418 + do_check_true(download.error.becauseBlockedByParentalControls); 1.1419 + do_check_true(download.stopped); 1.1420 + } 1.1421 + 1.1422 + do_check_false(yield OS.File.exists(download.target.path)); 1.1423 +}); 1.1424 + 1.1425 +/** 1.1426 + * Check that DownloadCopySaver can always retrieve the hash. 1.1427 + * DownloadLegacySaver can only retrieve the hash when 1.1428 + * nsIExternalHelperAppService is invoked. 1.1429 + */ 1.1430 +add_task(function test_getSha256Hash() 1.1431 +{ 1.1432 + if (!gUseLegacySaver) { 1.1433 + let download = yield promiseStartDownload(httpUrl("source.txt")); 1.1434 + yield promiseDownloadStopped(download); 1.1435 + do_check_true(download.stopped); 1.1436 + do_check_eq(32, download.saver.getSha256Hash().length); 1.1437 + } 1.1438 +}); 1.1439 + 1.1440 +/** 1.1441 + * Checks that application reputation blocks the download and the target file 1.1442 + * does not exist. 1.1443 + */ 1.1444 +add_task(function test_blocked_applicationReputation() 1.1445 +{ 1.1446 + function cleanup() { 1.1447 + DownloadIntegration.shouldBlockInTestForApplicationReputation = false; 1.1448 + } 1.1449 + do_register_cleanup(cleanup); 1.1450 + DownloadIntegration.shouldBlockInTestForApplicationReputation = true; 1.1451 + 1.1452 + let download; 1.1453 + try { 1.1454 + if (!gUseLegacySaver) { 1.1455 + // When testing DownloadCopySaver, we want to check that the promise 1.1456 + // returned by the "start" method is rejected. 1.1457 + download = yield promiseNewDownload(); 1.1458 + yield download.start(); 1.1459 + } else { 1.1460 + // When testing DownloadLegacySaver, we cannot be sure whether we are 1.1461 + // testing the promise returned by the "start" method or we are testing 1.1462 + // the "error" property checked by promiseDownloadStopped. This happens 1.1463 + // because we don't have control over when the download is started. 1.1464 + download = yield promiseStartLegacyDownload(); 1.1465 + yield promiseDownloadStopped(download); 1.1466 + } 1.1467 + do_throw("The download should have blocked."); 1.1468 + } catch (ex if ex instanceof Downloads.Error && ex.becauseBlocked) { 1.1469 + do_check_true(ex.becauseBlockedByReputationCheck); 1.1470 + do_check_true(download.error.becauseBlockedByReputationCheck); 1.1471 + } 1.1472 + 1.1473 + // Now that the download is blocked, the target file should not exist. 1.1474 + do_check_false(yield OS.File.exists(download.target.path)); 1.1475 + cleanup(); 1.1476 +}); 1.1477 + 1.1478 +/** 1.1479 + * download.showContainingDirectory() action 1.1480 + */ 1.1481 +add_task(function test_showContainingDirectory() { 1.1482 + DownloadIntegration._deferTestShowDir = Promise.defer(); 1.1483 + 1.1484 + let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; 1.1485 + 1.1486 + let download = yield Downloads.createDownload({ 1.1487 + source: { url: httpUrl("source.txt") }, 1.1488 + target: "" 1.1489 + }); 1.1490 + 1.1491 + try { 1.1492 + yield download.showContainingDirectory(); 1.1493 + do_throw("Should have failed because of an invalid path."); 1.1494 + } catch (ex if ex instanceof Components.Exception) { 1.1495 + // Invalid paths on Windows are reported with NS_ERROR_FAILURE, 1.1496 + // but with NS_ERROR_FILE_UNRECOGNIZED_PATH on Mac/Linux 1.1497 + let validResult = ex.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH || 1.1498 + ex.result == Cr.NS_ERROR_FAILURE; 1.1499 + do_check_true(validResult); 1.1500 + } 1.1501 + 1.1502 + download = yield Downloads.createDownload({ 1.1503 + source: { url: httpUrl("source.txt") }, 1.1504 + target: targetPath 1.1505 + }); 1.1506 + 1.1507 + 1.1508 + DownloadIntegration._deferTestShowDir = Promise.defer(); 1.1509 + download.showContainingDirectory(); 1.1510 + let result = yield DownloadIntegration._deferTestShowDir.promise; 1.1511 + do_check_eq(result, "success"); 1.1512 +}); 1.1513 + 1.1514 +/** 1.1515 + * download.launch() action 1.1516 + */ 1.1517 +add_task(function test_launch() { 1.1518 + let customLauncher = getTempFile("app-launcher"); 1.1519 + 1.1520 + // Test both with and without setting a custom application. 1.1521 + for (let launcherPath of [null, customLauncher.path]) { 1.1522 + let download; 1.1523 + if (!gUseLegacySaver) { 1.1524 + // When testing DownloadCopySaver, we have control over the download, thus 1.1525 + // we can test that file is not launched if download.succeeded is not set. 1.1526 + download = yield Downloads.createDownload({ 1.1527 + source: httpUrl("source.txt"), 1.1528 + target: getTempFile(TEST_TARGET_FILE_NAME).path, 1.1529 + launcherPath: launcherPath, 1.1530 + launchWhenSucceeded: true 1.1531 + }); 1.1532 + 1.1533 + try { 1.1534 + yield download.launch(); 1.1535 + do_throw("Can't launch download file as it has not completed yet"); 1.1536 + } catch (ex) { 1.1537 + do_check_eq(ex.message, 1.1538 + "launch can only be called if the download succeeded"); 1.1539 + } 1.1540 + 1.1541 + yield download.start(); 1.1542 + } else { 1.1543 + // When testing DownloadLegacySaver, the download is already started when 1.1544 + // it is created, thus we don't test calling "launch" before starting. 1.1545 + download = yield promiseStartLegacyDownload( 1.1546 + httpUrl("source.txt"), 1.1547 + { launcherPath: launcherPath, 1.1548 + launchWhenSucceeded: true }); 1.1549 + yield promiseDownloadStopped(download); 1.1550 + } 1.1551 + 1.1552 + do_check_true(download.launchWhenSucceeded); 1.1553 + 1.1554 + DownloadIntegration._deferTestOpenFile = Promise.defer(); 1.1555 + download.launch(); 1.1556 + let result = yield DownloadIntegration._deferTestOpenFile.promise; 1.1557 + 1.1558 + // Verify that the results match the test case. 1.1559 + if (!launcherPath) { 1.1560 + // This indicates that the default handler has been chosen. 1.1561 + do_check_true(result === null); 1.1562 + } else { 1.1563 + // Check the nsIMIMEInfo instance that would have been used for launching. 1.1564 + do_check_eq(result.preferredAction, Ci.nsIMIMEInfo.useHelperApp); 1.1565 + do_check_true(result.preferredApplicationHandler 1.1566 + .QueryInterface(Ci.nsILocalHandlerApp) 1.1567 + .executable.equals(customLauncher)); 1.1568 + } 1.1569 + } 1.1570 +}); 1.1571 + 1.1572 +/** 1.1573 + * Test passing an invalid path as the launcherPath property. 1.1574 + */ 1.1575 +add_task(function test_launcherPath_invalid() { 1.1576 + let download = yield Downloads.createDownload({ 1.1577 + source: { url: httpUrl("source.txt") }, 1.1578 + target: { path: getTempFile(TEST_TARGET_FILE_NAME).path }, 1.1579 + launcherPath: " " 1.1580 + }); 1.1581 + 1.1582 + DownloadIntegration._deferTestOpenFile = Promise.defer(); 1.1583 + yield download.start(); 1.1584 + try { 1.1585 + download.launch(); 1.1586 + result = yield DownloadIntegration._deferTestOpenFile.promise; 1.1587 + do_throw("Can't launch file with invalid custom launcher") 1.1588 + } catch (ex if ex instanceof Components.Exception) { 1.1589 + // Invalid paths on Windows are reported with NS_ERROR_FAILURE, 1.1590 + // but with NS_ERROR_FILE_UNRECOGNIZED_PATH on Mac/Linux 1.1591 + let validResult = ex.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH || 1.1592 + ex.result == Cr.NS_ERROR_FAILURE; 1.1593 + do_check_true(validResult); 1.1594 + } 1.1595 +}); 1.1596 + 1.1597 +/** 1.1598 + * Tests that download.launch() is automatically called after 1.1599 + * the download finishes if download.launchWhenSucceeded = true 1.1600 + */ 1.1601 +add_task(function test_launchWhenSucceeded() { 1.1602 + let customLauncher = getTempFile("app-launcher"); 1.1603 + 1.1604 + // Test both with and without setting a custom application. 1.1605 + for (let launcherPath of [null, customLauncher.path]) { 1.1606 + DownloadIntegration._deferTestOpenFile = Promise.defer(); 1.1607 + 1.1608 + if (!gUseLegacySaver) { 1.1609 + let download = yield Downloads.createDownload({ 1.1610 + source: httpUrl("source.txt"), 1.1611 + target: getTempFile(TEST_TARGET_FILE_NAME).path, 1.1612 + launchWhenSucceeded: true, 1.1613 + launcherPath: launcherPath, 1.1614 + }); 1.1615 + yield download.start(); 1.1616 + } else { 1.1617 + let download = yield promiseStartLegacyDownload( 1.1618 + httpUrl("source.txt"), 1.1619 + { launcherPath: launcherPath, 1.1620 + launchWhenSucceeded: true }); 1.1621 + yield promiseDownloadStopped(download); 1.1622 + } 1.1623 + 1.1624 + let result = yield DownloadIntegration._deferTestOpenFile.promise; 1.1625 + 1.1626 + // Verify that the results match the test case. 1.1627 + if (!launcherPath) { 1.1628 + // This indicates that the default handler has been chosen. 1.1629 + do_check_true(result === null); 1.1630 + } else { 1.1631 + // Check the nsIMIMEInfo instance that would have been used for launching. 1.1632 + do_check_eq(result.preferredAction, Ci.nsIMIMEInfo.useHelperApp); 1.1633 + do_check_true(result.preferredApplicationHandler 1.1634 + .QueryInterface(Ci.nsILocalHandlerApp) 1.1635 + .executable.equals(customLauncher)); 1.1636 + } 1.1637 + } 1.1638 +}); 1.1639 + 1.1640 +/** 1.1641 + * Tests that the proper content type is set for a normal download. 1.1642 + */ 1.1643 +add_task(function test_contentType() { 1.1644 + let download = yield promiseStartDownload(httpUrl("source.txt")); 1.1645 + yield promiseDownloadStopped(download); 1.1646 + 1.1647 + do_check_eq("text/plain", download.contentType); 1.1648 +}); 1.1649 + 1.1650 +/** 1.1651 + * Tests that the serialization/deserialization of the startTime Date 1.1652 + * object works correctly. 1.1653 + */ 1.1654 +add_task(function test_toSerializable_startTime() 1.1655 +{ 1.1656 + let download1 = yield promiseStartDownload(httpUrl("source.txt")); 1.1657 + yield promiseDownloadStopped(download1); 1.1658 + 1.1659 + let serializable = download1.toSerializable(); 1.1660 + let reserialized = JSON.parse(JSON.stringify(serializable)); 1.1661 + 1.1662 + let download2 = yield Downloads.createDownload(reserialized); 1.1663 + 1.1664 + do_check_eq(download1.startTime.constructor.name, "Date"); 1.1665 + do_check_eq(download2.startTime.constructor.name, "Date"); 1.1666 + do_check_eq(download1.startTime.toJSON(), download2.startTime.toJSON()); 1.1667 +}); 1.1668 + 1.1669 +/** 1.1670 + * This test will call the platform specific operations within 1.1671 + * DownloadPlatform::DownloadDone. While there is no test to verify the 1.1672 + * specific behaviours, this at least ensures that there is no error or crash. 1.1673 + */ 1.1674 +add_task(function test_platform_integration() 1.1675 +{ 1.1676 + let downloadFiles = []; 1.1677 + function cleanup() { 1.1678 + for (let file of downloadFiles) { 1.1679 + file.remove(true); 1.1680 + } 1.1681 + } 1.1682 + do_register_cleanup(cleanup); 1.1683 + 1.1684 + for (let isPrivate of [false, true]) { 1.1685 + DownloadIntegration.downloadDoneCalled = false; 1.1686 + 1.1687 + // Some platform specific operations only operate on files outside the 1.1688 + // temporary directory or in the Downloads directory (such as setting 1.1689 + // the Windows searchable attribute, and the Mac Downloads icon bouncing), 1.1690 + // so use the system Downloads directory for the target file. 1.1691 + let targetFilePath = yield DownloadIntegration.getSystemDownloadsDirectory(); 1.1692 + targetFilePath = OS.Path.join(targetFilePath, 1.1693 + "test" + (Math.floor(Math.random() * 1000000))); 1.1694 + let targetFile = new FileUtils.File(targetFilePath); 1.1695 + downloadFiles.push(targetFile); 1.1696 + 1.1697 + let download; 1.1698 + if (gUseLegacySaver) { 1.1699 + download = yield promiseStartLegacyDownload(httpUrl("source.txt"), 1.1700 + { targetFile: targetFile }); 1.1701 + } 1.1702 + else { 1.1703 + download = yield Downloads.createDownload({ 1.1704 + source: httpUrl("source.txt"), 1.1705 + target: targetFile, 1.1706 + }); 1.1707 + download.start(); 1.1708 + } 1.1709 + 1.1710 + // Wait for the whenSucceeded promise to be resolved first. 1.1711 + // downloadDone should be called before the whenSucceeded promise is resolved. 1.1712 + yield download.whenSucceeded().then(function () { 1.1713 + do_check_true(DownloadIntegration.downloadDoneCalled); 1.1714 + }); 1.1715 + 1.1716 + // Then, wait for the promise returned by "start" to be resolved. 1.1717 + yield promiseDownloadStopped(download); 1.1718 + 1.1719 + yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); 1.1720 + } 1.1721 +}); 1.1722 + 1.1723 +/** 1.1724 + * Checks that downloads are added to browsing history when they start. 1.1725 + */ 1.1726 +add_task(function test_history() 1.1727 +{ 1.1728 + mustInterruptResponses(); 1.1729 + 1.1730 + // We will wait for the visit to be notified during the download. 1.1731 + yield promiseClearHistory(); 1.1732 + let promiseVisit = promiseWaitForVisit(httpUrl("interruptible.txt")); 1.1733 + 1.1734 + // Start a download that is not allowed to finish yet. 1.1735 + let download = yield promiseStartDownload(httpUrl("interruptible.txt")); 1.1736 + 1.1737 + // The history notifications should be received before the download completes. 1.1738 + let [time, transitionType] = yield promiseVisit; 1.1739 + do_check_eq(time, download.startTime.getTime() * 1000); 1.1740 + do_check_eq(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD); 1.1741 + 1.1742 + // Restart and complete the download after clearing history. 1.1743 + yield promiseClearHistory(); 1.1744 + download.cancel(); 1.1745 + continueResponses(); 1.1746 + yield download.start(); 1.1747 + 1.1748 + // The restart should not have added a new history visit. 1.1749 + do_check_false(yield promiseIsURIVisited(httpUrl("interruptible.txt"))); 1.1750 +}); 1.1751 + 1.1752 +/** 1.1753 + * Checks that downloads started by nsIHelperAppService are added to the 1.1754 + * browsing history when they start. 1.1755 + */ 1.1756 +add_task(function test_history_tryToKeepPartialData() 1.1757 +{ 1.1758 + // We will wait for the visit to be notified during the download. 1.1759 + yield promiseClearHistory(); 1.1760 + let promiseVisit = 1.1761 + promiseWaitForVisit(httpUrl("interruptible_resumable.txt")); 1.1762 + 1.1763 + // Start a download that is not allowed to finish yet. 1.1764 + let beforeStartTimeMs = Date.now(); 1.1765 + let download = yield promiseStartDownload_tryToKeepPartialData(); 1.1766 + 1.1767 + // The history notifications should be received before the download completes. 1.1768 + let [time, transitionType] = yield promiseVisit; 1.1769 + do_check_eq(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD); 1.1770 + 1.1771 + // The time set by nsIHelperAppService may be different than the start time in 1.1772 + // the download object, thus we only check that it is a meaningful time. Note 1.1773 + // that we subtract one second from the earliest time to account for rounding. 1.1774 + do_check_true(time >= beforeStartTimeMs * 1000 - 1000000); 1.1775 + 1.1776 + // Complete the download before finishing the test. 1.1777 + continueResponses(); 1.1778 + yield promiseDownloadStopped(download); 1.1779 +}); 1.1780 + 1.1781 +/** 1.1782 + * Tests that the temp download files are removed on exit and exiting private 1.1783 + * mode after they have been launched. 1.1784 + */ 1.1785 +add_task(function test_launchWhenSucceeded_deleteTempFileOnExit() { 1.1786 + const kDeleteTempFileOnExit = "browser.helperApps.deleteTempFileOnExit"; 1.1787 + 1.1788 + let customLauncherPath = getTempFile("app-launcher").path; 1.1789 + let autoDeleteTargetPathOne = getTempFile(TEST_TARGET_FILE_NAME).path; 1.1790 + let autoDeleteTargetPathTwo = getTempFile(TEST_TARGET_FILE_NAME).path; 1.1791 + let noAutoDeleteTargetPath = getTempFile(TEST_TARGET_FILE_NAME).path; 1.1792 + 1.1793 + let autoDeleteDownloadOne = yield Downloads.createDownload({ 1.1794 + source: { url: httpUrl("source.txt"), isPrivate: true }, 1.1795 + target: autoDeleteTargetPathOne, 1.1796 + launchWhenSucceeded: true, 1.1797 + launcherPath: customLauncherPath, 1.1798 + }); 1.1799 + yield autoDeleteDownloadOne.start(); 1.1800 + 1.1801 + Services.prefs.setBoolPref(kDeleteTempFileOnExit, true); 1.1802 + let autoDeleteDownloadTwo = yield Downloads.createDownload({ 1.1803 + source: httpUrl("source.txt"), 1.1804 + target: autoDeleteTargetPathTwo, 1.1805 + launchWhenSucceeded: true, 1.1806 + launcherPath: customLauncherPath, 1.1807 + }); 1.1808 + yield autoDeleteDownloadTwo.start(); 1.1809 + 1.1810 + Services.prefs.setBoolPref(kDeleteTempFileOnExit, false); 1.1811 + let noAutoDeleteDownload = yield Downloads.createDownload({ 1.1812 + source: httpUrl("source.txt"), 1.1813 + target: noAutoDeleteTargetPath, 1.1814 + launchWhenSucceeded: true, 1.1815 + launcherPath: customLauncherPath, 1.1816 + }); 1.1817 + yield noAutoDeleteDownload.start(); 1.1818 + 1.1819 + Services.prefs.clearUserPref(kDeleteTempFileOnExit); 1.1820 + 1.1821 + do_check_true(yield OS.File.exists(autoDeleteTargetPathOne)); 1.1822 + do_check_true(yield OS.File.exists(autoDeleteTargetPathTwo)); 1.1823 + do_check_true(yield OS.File.exists(noAutoDeleteTargetPath)); 1.1824 + 1.1825 + // Simulate leaving private browsing mode 1.1826 + Services.obs.notifyObservers(null, "last-pb-context-exited", null); 1.1827 + do_check_false(yield OS.File.exists(autoDeleteTargetPathOne)); 1.1828 + 1.1829 + // Simulate browser shutdown 1.1830 + let expire = Cc["@mozilla.org/uriloader/external-helper-app-service;1"] 1.1831 + .getService(Ci.nsIObserver); 1.1832 + expire.observe(null, "profile-before-change", null); 1.1833 + do_check_false(yield OS.File.exists(autoDeleteTargetPathTwo)); 1.1834 + do_check_true(yield OS.File.exists(noAutoDeleteTargetPath)); 1.1835 +});