|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* Any copyright is dedicated to the Public Domain. |
|
4 * http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
5 |
|
6 /** |
|
7 * This script is loaded by "test_DownloadCore.js" and "test_DownloadLegacy.js" |
|
8 * with different values of the gUseLegacySaver variable, to apply tests to both |
|
9 * the "copy" and "legacy" saver implementations. |
|
10 */ |
|
11 |
|
12 "use strict"; |
|
13 |
|
14 //////////////////////////////////////////////////////////////////////////////// |
|
15 //// Globals |
|
16 |
|
17 /** |
|
18 * Creates and starts a new download, using either DownloadCopySaver or |
|
19 * DownloadLegacySaver based on the current test run. |
|
20 * |
|
21 * @return {Promise} |
|
22 * @resolves The newly created Download object. The download may be in progress |
|
23 * or already finished. The promiseDownloadStopped function can be |
|
24 * used to wait for completion. |
|
25 * @rejects JavaScript exception. |
|
26 */ |
|
27 function promiseStartDownload(aSourceUrl) { |
|
28 if (gUseLegacySaver) { |
|
29 return promiseStartLegacyDownload(aSourceUrl); |
|
30 } |
|
31 |
|
32 return promiseNewDownload(aSourceUrl).then(download => { |
|
33 download.start(); |
|
34 return download; |
|
35 }); |
|
36 } |
|
37 |
|
38 /** |
|
39 * Creates and starts a new download, configured to keep partial data, and |
|
40 * returns only when the first part of "interruptible_resumable.txt" has been |
|
41 * saved to disk. You must call "continueResponses" to allow the interruptible |
|
42 * request to continue. |
|
43 * |
|
44 * This function uses either DownloadCopySaver or DownloadLegacySaver based on |
|
45 * the current test run. |
|
46 * |
|
47 * @return {Promise} |
|
48 * @resolves The newly created Download object, still in progress. |
|
49 * @rejects JavaScript exception. |
|
50 */ |
|
51 function promiseStartDownload_tryToKeepPartialData() { |
|
52 return Task.spawn(function () { |
|
53 mustInterruptResponses(); |
|
54 |
|
55 // Start a new download and configure it to keep partially downloaded data. |
|
56 let download; |
|
57 if (!gUseLegacySaver) { |
|
58 let targetFilePath = getTempFile(TEST_TARGET_FILE_NAME).path; |
|
59 download = yield Downloads.createDownload({ |
|
60 source: httpUrl("interruptible_resumable.txt"), |
|
61 target: { path: targetFilePath, |
|
62 partFilePath: targetFilePath + ".part" }, |
|
63 }); |
|
64 download.tryToKeepPartialData = true; |
|
65 download.start(); |
|
66 } else { |
|
67 // Start a download using nsIExternalHelperAppService, that is configured |
|
68 // to keep partially downloaded data by default. |
|
69 download = yield promiseStartExternalHelperAppServiceDownload(); |
|
70 } |
|
71 |
|
72 yield promiseDownloadMidway(download); |
|
73 yield promisePartFileReady(download); |
|
74 |
|
75 throw new Task.Result(download); |
|
76 }); |
|
77 } |
|
78 |
|
79 /** |
|
80 * This function should be called after the progress notification for a download |
|
81 * is received, and waits for the worker thread of BackgroundFileSaver to |
|
82 * receive the data to be written to the ".part" file on disk. |
|
83 * |
|
84 * @return {Promise} |
|
85 * @resolves When the ".part" file has been written to disk. |
|
86 * @rejects JavaScript exception. |
|
87 */ |
|
88 function promisePartFileReady(aDownload) { |
|
89 return Task.spawn(function () { |
|
90 // We don't have control over the file output code in BackgroundFileSaver. |
|
91 // After we receive the download progress notification, we may only check |
|
92 // that the ".part" file has been created, while its size cannot be |
|
93 // determined because the file is currently open. |
|
94 try { |
|
95 do { |
|
96 yield promiseTimeout(50); |
|
97 } while (!(yield OS.File.exists(aDownload.target.partFilePath))); |
|
98 } catch (ex if ex instanceof OS.File.Error) { |
|
99 // This indicates that the file has been created and cannot be accessed. |
|
100 // The specific error might vary with the platform. |
|
101 do_print("Expected exception while checking existence: " + ex.toString()); |
|
102 // Wait some more time to allow the write to complete. |
|
103 yield promiseTimeout(100); |
|
104 } |
|
105 }); |
|
106 } |
|
107 |
|
108 //////////////////////////////////////////////////////////////////////////////// |
|
109 //// Tests |
|
110 |
|
111 /** |
|
112 * Executes a download and checks its basic properties after construction. |
|
113 * The download is started by constructing the simplest Download object with |
|
114 * the "copy" saver, or using the legacy nsITransfer interface. |
|
115 */ |
|
116 add_task(function test_basic() |
|
117 { |
|
118 let targetFile = getTempFile(TEST_TARGET_FILE_NAME); |
|
119 |
|
120 let download; |
|
121 if (!gUseLegacySaver) { |
|
122 // When testing DownloadCopySaver, we have control over the download, thus |
|
123 // we can check its basic properties before it starts. |
|
124 download = yield Downloads.createDownload({ |
|
125 source: { url: httpUrl("source.txt") }, |
|
126 target: { path: targetFile.path }, |
|
127 saver: { type: "copy" }, |
|
128 }); |
|
129 |
|
130 do_check_eq(download.source.url, httpUrl("source.txt")); |
|
131 do_check_eq(download.target.path, targetFile.path); |
|
132 |
|
133 yield download.start(); |
|
134 } else { |
|
135 // When testing DownloadLegacySaver, the download is already started when it |
|
136 // is created, thus we must check its basic properties while in progress. |
|
137 download = yield promiseStartLegacyDownload(null, |
|
138 { targetFile: targetFile }); |
|
139 |
|
140 do_check_eq(download.source.url, httpUrl("source.txt")); |
|
141 do_check_eq(download.target.path, targetFile.path); |
|
142 |
|
143 yield promiseDownloadStopped(download); |
|
144 } |
|
145 |
|
146 // Check additional properties on the finished download. |
|
147 do_check_true(download.source.referrer === null); |
|
148 |
|
149 yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); |
|
150 }); |
|
151 |
|
152 /** |
|
153 * Executes a download with the tryToKeepPartialData property set, and ensures |
|
154 * that the file is saved correctly. When testing DownloadLegacySaver, the |
|
155 * download is executed using the nsIExternalHelperAppService component. |
|
156 */ |
|
157 add_task(function test_basic_tryToKeepPartialData() |
|
158 { |
|
159 let download = yield promiseStartDownload_tryToKeepPartialData(); |
|
160 continueResponses(); |
|
161 yield promiseDownloadStopped(download); |
|
162 |
|
163 // The target file should now have been created, and the ".part" file deleted. |
|
164 yield promiseVerifyContents(download.target.path, |
|
165 TEST_DATA_SHORT + TEST_DATA_SHORT); |
|
166 do_check_false(yield OS.File.exists(download.target.partFilePath)); |
|
167 do_check_eq(32, download.saver.getSha256Hash().length); |
|
168 }); |
|
169 |
|
170 /** |
|
171 * Checks the referrer for downloads. |
|
172 */ |
|
173 add_task(function test_referrer() |
|
174 { |
|
175 let sourcePath = "/test_referrer.txt"; |
|
176 let sourceUrl = httpUrl("test_referrer.txt"); |
|
177 let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; |
|
178 |
|
179 function cleanup() { |
|
180 gHttpServer.registerPathHandler(sourcePath, null); |
|
181 } |
|
182 do_register_cleanup(cleanup); |
|
183 |
|
184 gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { |
|
185 aResponse.setHeader("Content-Type", "text/plain", false); |
|
186 |
|
187 do_check_true(aRequest.hasHeader("Referer")); |
|
188 do_check_eq(aRequest.getHeader("Referer"), TEST_REFERRER_URL); |
|
189 }); |
|
190 let download = yield Downloads.createDownload({ |
|
191 source: { url: sourceUrl, referrer: TEST_REFERRER_URL }, |
|
192 target: targetPath, |
|
193 }); |
|
194 do_check_eq(download.source.referrer, TEST_REFERRER_URL); |
|
195 yield download.start(); |
|
196 |
|
197 download = yield Downloads.createDownload({ |
|
198 source: { url: sourceUrl, referrer: TEST_REFERRER_URL, |
|
199 isPrivate: true }, |
|
200 target: targetPath, |
|
201 }); |
|
202 do_check_eq(download.source.referrer, TEST_REFERRER_URL); |
|
203 yield download.start(); |
|
204 |
|
205 // Test the download still works for non-HTTP channel with referrer. |
|
206 sourceUrl = "data:text/html,<html><body></body></html>"; |
|
207 download = yield Downloads.createDownload({ |
|
208 source: { url: sourceUrl, referrer: TEST_REFERRER_URL }, |
|
209 target: targetPath, |
|
210 }); |
|
211 do_check_eq(download.source.referrer, TEST_REFERRER_URL); |
|
212 yield download.start(); |
|
213 |
|
214 cleanup(); |
|
215 }); |
|
216 |
|
217 /** |
|
218 * Checks initial and final state and progress for a successful download. |
|
219 */ |
|
220 add_task(function test_initial_final_state() |
|
221 { |
|
222 let download; |
|
223 if (!gUseLegacySaver) { |
|
224 // When testing DownloadCopySaver, we have control over the download, thus |
|
225 // we can check its state before it starts. |
|
226 download = yield promiseNewDownload(); |
|
227 |
|
228 do_check_true(download.stopped); |
|
229 do_check_false(download.succeeded); |
|
230 do_check_false(download.canceled); |
|
231 do_check_true(download.error === null); |
|
232 do_check_eq(download.progress, 0); |
|
233 do_check_true(download.startTime === null); |
|
234 |
|
235 yield download.start(); |
|
236 } else { |
|
237 // When testing DownloadLegacySaver, the download is already started when it |
|
238 // is created, thus we cannot check its initial state. |
|
239 download = yield promiseStartLegacyDownload(); |
|
240 yield promiseDownloadStopped(download); |
|
241 } |
|
242 |
|
243 do_check_true(download.stopped); |
|
244 do_check_true(download.succeeded); |
|
245 do_check_false(download.canceled); |
|
246 do_check_true(download.error === null); |
|
247 do_check_eq(download.progress, 100); |
|
248 do_check_true(isValidDate(download.startTime)); |
|
249 }); |
|
250 |
|
251 /** |
|
252 * Checks the notification of the final download state. |
|
253 */ |
|
254 add_task(function test_final_state_notified() |
|
255 { |
|
256 mustInterruptResponses(); |
|
257 |
|
258 let download = yield promiseStartDownload(httpUrl("interruptible.txt")); |
|
259 |
|
260 let onchangeNotified = false; |
|
261 let lastNotifiedStopped; |
|
262 let lastNotifiedProgress; |
|
263 download.onchange = function () { |
|
264 onchangeNotified = true; |
|
265 lastNotifiedStopped = download.stopped; |
|
266 lastNotifiedProgress = download.progress; |
|
267 }; |
|
268 |
|
269 // Allow the download to complete. |
|
270 let promiseAttempt = download.start(); |
|
271 continueResponses(); |
|
272 yield promiseAttempt; |
|
273 |
|
274 // The view should have been notified before the download completes. |
|
275 do_check_true(onchangeNotified); |
|
276 do_check_true(lastNotifiedStopped); |
|
277 do_check_eq(lastNotifiedProgress, 100); |
|
278 }); |
|
279 |
|
280 /** |
|
281 * Checks intermediate progress for a successful download. |
|
282 */ |
|
283 add_task(function test_intermediate_progress() |
|
284 { |
|
285 mustInterruptResponses(); |
|
286 |
|
287 let download = yield promiseStartDownload(httpUrl("interruptible.txt")); |
|
288 |
|
289 yield promiseDownloadMidway(download); |
|
290 |
|
291 do_check_true(download.hasProgress); |
|
292 do_check_eq(download.currentBytes, TEST_DATA_SHORT.length); |
|
293 do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2); |
|
294 |
|
295 // Continue after the first chunk of data is fully received. |
|
296 continueResponses(); |
|
297 yield promiseDownloadStopped(download); |
|
298 |
|
299 do_check_true(download.stopped); |
|
300 do_check_eq(download.progress, 100); |
|
301 |
|
302 yield promiseVerifyContents(download.target.path, |
|
303 TEST_DATA_SHORT + TEST_DATA_SHORT); |
|
304 }); |
|
305 |
|
306 /** |
|
307 * Downloads a file with a "Content-Length" of 0 and checks the progress. |
|
308 */ |
|
309 add_task(function test_empty_progress() |
|
310 { |
|
311 let download = yield promiseStartDownload(httpUrl("empty.txt")); |
|
312 yield promiseDownloadStopped(download); |
|
313 |
|
314 do_check_true(download.stopped); |
|
315 do_check_true(download.hasProgress); |
|
316 do_check_eq(download.progress, 100); |
|
317 do_check_eq(download.currentBytes, 0); |
|
318 do_check_eq(download.totalBytes, 0); |
|
319 |
|
320 // We should have received the content type even for an empty file. |
|
321 do_check_eq(download.contentType, "text/plain"); |
|
322 |
|
323 do_check_eq((yield OS.File.stat(download.target.path)).size, 0); |
|
324 }); |
|
325 |
|
326 /** |
|
327 * Downloads a file with a "Content-Length" of 0 with the tryToKeepPartialData |
|
328 * property set, and ensures that the file is saved correctly. |
|
329 */ |
|
330 add_task(function test_empty_progress_tryToKeepPartialData() |
|
331 { |
|
332 // Start a new download and configure it to keep partially downloaded data. |
|
333 let download; |
|
334 if (!gUseLegacySaver) { |
|
335 let targetFilePath = getTempFile(TEST_TARGET_FILE_NAME).path; |
|
336 download = yield Downloads.createDownload({ |
|
337 source: httpUrl("empty.txt"), |
|
338 target: { path: targetFilePath, |
|
339 partFilePath: targetFilePath + ".part" }, |
|
340 }); |
|
341 download.tryToKeepPartialData = true; |
|
342 download.start(); |
|
343 } else { |
|
344 // Start a download using nsIExternalHelperAppService, that is configured |
|
345 // to keep partially downloaded data by default. |
|
346 download = yield promiseStartExternalHelperAppServiceDownload( |
|
347 httpUrl("empty.txt")); |
|
348 } |
|
349 yield promiseDownloadStopped(download); |
|
350 |
|
351 // The target file should now have been created, and the ".part" file deleted. |
|
352 do_check_eq((yield OS.File.stat(download.target.path)).size, 0); |
|
353 do_check_false(yield OS.File.exists(download.target.partFilePath)); |
|
354 do_check_eq(32, download.saver.getSha256Hash().length); |
|
355 }); |
|
356 |
|
357 /** |
|
358 * Downloads an empty file with no "Content-Length" and checks the progress. |
|
359 */ |
|
360 add_task(function test_empty_noprogress() |
|
361 { |
|
362 let sourcePath = "/test_empty_noprogress.txt"; |
|
363 let sourceUrl = httpUrl("test_empty_noprogress.txt"); |
|
364 let deferRequestReceived = Promise.defer(); |
|
365 |
|
366 // Register an interruptible handler that notifies us when the request occurs. |
|
367 function cleanup() { |
|
368 gHttpServer.registerPathHandler(sourcePath, null); |
|
369 } |
|
370 do_register_cleanup(cleanup); |
|
371 |
|
372 registerInterruptibleHandler(sourcePath, |
|
373 function firstPart(aRequest, aResponse) { |
|
374 aResponse.setHeader("Content-Type", "text/plain", false); |
|
375 deferRequestReceived.resolve(); |
|
376 }, function secondPart(aRequest, aResponse) { }); |
|
377 |
|
378 // Start the download, without allowing the request to finish. |
|
379 mustInterruptResponses(); |
|
380 let download; |
|
381 if (!gUseLegacySaver) { |
|
382 // When testing DownloadCopySaver, we have control over the download, thus |
|
383 // we can hook its onchange callback that will be notified when the |
|
384 // download starts. |
|
385 download = yield promiseNewDownload(sourceUrl); |
|
386 |
|
387 download.onchange = function () { |
|
388 if (!download.stopped) { |
|
389 do_check_false(download.hasProgress); |
|
390 do_check_eq(download.currentBytes, 0); |
|
391 do_check_eq(download.totalBytes, 0); |
|
392 } |
|
393 }; |
|
394 |
|
395 download.start(); |
|
396 } else { |
|
397 // When testing DownloadLegacySaver, the download is already started when it |
|
398 // is created, and it may have already made all needed property change |
|
399 // notifications, thus there is no point in checking the onchange callback. |
|
400 download = yield promiseStartLegacyDownload(sourceUrl); |
|
401 } |
|
402 |
|
403 // Wait for the request to be received by the HTTP server, but don't allow the |
|
404 // request to finish yet. Before checking the download state, wait for the |
|
405 // events to be processed by the client. |
|
406 yield deferRequestReceived.promise; |
|
407 yield promiseExecuteSoon(); |
|
408 |
|
409 // Check that this download has no progress report. |
|
410 do_check_false(download.stopped); |
|
411 do_check_false(download.hasProgress); |
|
412 do_check_eq(download.currentBytes, 0); |
|
413 do_check_eq(download.totalBytes, 0); |
|
414 |
|
415 // Now allow the response to finish. |
|
416 continueResponses(); |
|
417 yield promiseDownloadStopped(download); |
|
418 |
|
419 // We should have received the content type even if no progress is reported. |
|
420 do_check_eq(download.contentType, "text/plain"); |
|
421 |
|
422 // Verify the state of the completed download. |
|
423 do_check_true(download.stopped); |
|
424 do_check_false(download.hasProgress); |
|
425 do_check_eq(download.progress, 100); |
|
426 do_check_eq(download.currentBytes, 0); |
|
427 do_check_eq(download.totalBytes, 0); |
|
428 |
|
429 do_check_eq((yield OS.File.stat(download.target.path)).size, 0); |
|
430 }); |
|
431 |
|
432 /** |
|
433 * Calls the "start" method two times before the download is finished. |
|
434 */ |
|
435 add_task(function test_start_twice() |
|
436 { |
|
437 mustInterruptResponses(); |
|
438 |
|
439 let download; |
|
440 if (!gUseLegacySaver) { |
|
441 // When testing DownloadCopySaver, we have control over the download, thus |
|
442 // we can start the download later during the test. |
|
443 download = yield promiseNewDownload(httpUrl("interruptible.txt")); |
|
444 } else { |
|
445 // When testing DownloadLegacySaver, the download is already started when it |
|
446 // is created. Effectively, we are starting the download three times. |
|
447 download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt")); |
|
448 } |
|
449 |
|
450 // Call the start method two times. |
|
451 let promiseAttempt1 = download.start(); |
|
452 let promiseAttempt2 = download.start(); |
|
453 |
|
454 // Allow the download to finish. |
|
455 continueResponses(); |
|
456 |
|
457 // Both promises should now be resolved. |
|
458 yield promiseAttempt1; |
|
459 yield promiseAttempt2; |
|
460 |
|
461 do_check_true(download.stopped); |
|
462 do_check_true(download.succeeded); |
|
463 do_check_false(download.canceled); |
|
464 do_check_true(download.error === null); |
|
465 |
|
466 yield promiseVerifyContents(download.target.path, |
|
467 TEST_DATA_SHORT + TEST_DATA_SHORT); |
|
468 }); |
|
469 |
|
470 /** |
|
471 * Cancels a download and verifies that its state is reported correctly. |
|
472 */ |
|
473 add_task(function test_cancel_midway() |
|
474 { |
|
475 mustInterruptResponses(); |
|
476 |
|
477 // In this test case, we execute different checks that are only possible with |
|
478 // DownloadCopySaver or DownloadLegacySaver respectively. |
|
479 let download; |
|
480 let options = {}; |
|
481 if (!gUseLegacySaver) { |
|
482 download = yield promiseNewDownload(httpUrl("interruptible.txt")); |
|
483 } else { |
|
484 download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt"), |
|
485 options); |
|
486 } |
|
487 |
|
488 // Cancel the download after receiving the first part of the response. |
|
489 let deferCancel = Promise.defer(); |
|
490 let onchange = function () { |
|
491 if (!download.stopped && !download.canceled && download.progress == 50) { |
|
492 // Cancel the download immediately during the notification. |
|
493 deferCancel.resolve(download.cancel()); |
|
494 |
|
495 // The state change happens immediately after calling "cancel", but |
|
496 // temporary files or part files may still exist at this point. |
|
497 do_check_true(download.canceled); |
|
498 } |
|
499 }; |
|
500 |
|
501 // Register for the notification, but also call the function directly in |
|
502 // case the download already reached the expected progress. This may happen |
|
503 // when using DownloadLegacySaver. |
|
504 download.onchange = onchange; |
|
505 onchange(); |
|
506 |
|
507 let promiseAttempt; |
|
508 if (!gUseLegacySaver) { |
|
509 promiseAttempt = download.start(); |
|
510 } |
|
511 |
|
512 // Wait on the promise returned by the "cancel" method to ensure that the |
|
513 // cancellation process finished and temporary files were removed. |
|
514 yield deferCancel.promise; |
|
515 |
|
516 if (gUseLegacySaver) { |
|
517 // The nsIWebBrowserPersist instance should have been canceled now. |
|
518 do_check_eq(options.outPersist.result, Cr.NS_ERROR_ABORT); |
|
519 } |
|
520 |
|
521 do_check_true(download.stopped); |
|
522 do_check_true(download.canceled); |
|
523 do_check_true(download.error === null); |
|
524 |
|
525 do_check_false(yield OS.File.exists(download.target.path)); |
|
526 |
|
527 // Progress properties are not reset by canceling. |
|
528 do_check_eq(download.progress, 50); |
|
529 do_check_eq(download.totalBytes, TEST_DATA_SHORT.length * 2); |
|
530 do_check_eq(download.currentBytes, TEST_DATA_SHORT.length); |
|
531 |
|
532 if (!gUseLegacySaver) { |
|
533 // The promise returned by "start" should have been rejected meanwhile. |
|
534 try { |
|
535 yield promiseAttempt; |
|
536 do_throw("The download should have been canceled."); |
|
537 } catch (ex if ex instanceof Downloads.Error) { |
|
538 do_check_false(ex.becauseSourceFailed); |
|
539 do_check_false(ex.becauseTargetFailed); |
|
540 } |
|
541 } |
|
542 }); |
|
543 |
|
544 /** |
|
545 * Cancels a download while keeping partially downloaded data, and verifies that |
|
546 * both the target file and the ".part" file are deleted. |
|
547 */ |
|
548 add_task(function test_cancel_midway_tryToKeepPartialData() |
|
549 { |
|
550 let download = yield promiseStartDownload_tryToKeepPartialData(); |
|
551 |
|
552 do_check_true(yield OS.File.exists(download.target.path)); |
|
553 do_check_true(yield OS.File.exists(download.target.partFilePath)); |
|
554 |
|
555 yield download.cancel(); |
|
556 yield download.removePartialData(); |
|
557 |
|
558 do_check_true(download.stopped); |
|
559 do_check_true(download.canceled); |
|
560 do_check_true(download.error === null); |
|
561 |
|
562 do_check_false(yield OS.File.exists(download.target.path)); |
|
563 do_check_false(yield OS.File.exists(download.target.partFilePath)); |
|
564 }); |
|
565 |
|
566 /** |
|
567 * Cancels a download right after starting it. |
|
568 */ |
|
569 add_task(function test_cancel_immediately() |
|
570 { |
|
571 mustInterruptResponses(); |
|
572 |
|
573 let download = yield promiseStartDownload(httpUrl("interruptible.txt")); |
|
574 |
|
575 let promiseAttempt = download.start(); |
|
576 do_check_false(download.stopped); |
|
577 |
|
578 let promiseCancel = download.cancel(); |
|
579 do_check_true(download.canceled); |
|
580 |
|
581 // At this point, we don't know whether the download has already stopped or |
|
582 // is still waiting for cancellation. We can wait on the promise returned |
|
583 // by the "start" method to know for sure. |
|
584 try { |
|
585 yield promiseAttempt; |
|
586 do_throw("The download should have been canceled."); |
|
587 } catch (ex if ex instanceof Downloads.Error) { |
|
588 do_check_false(ex.becauseSourceFailed); |
|
589 do_check_false(ex.becauseTargetFailed); |
|
590 } |
|
591 |
|
592 do_check_true(download.stopped); |
|
593 do_check_true(download.canceled); |
|
594 do_check_true(download.error === null); |
|
595 |
|
596 do_check_false(yield OS.File.exists(download.target.path)); |
|
597 |
|
598 // Check that the promise returned by the "cancel" method has been resolved. |
|
599 yield promiseCancel; |
|
600 }); |
|
601 |
|
602 /** |
|
603 * Cancels and restarts a download sequentially. |
|
604 */ |
|
605 add_task(function test_cancel_midway_restart() |
|
606 { |
|
607 mustInterruptResponses(); |
|
608 |
|
609 let download = yield promiseStartDownload(httpUrl("interruptible.txt")); |
|
610 |
|
611 // The first time, cancel the download midway. |
|
612 yield promiseDownloadMidway(download); |
|
613 yield download.cancel(); |
|
614 |
|
615 do_check_true(download.stopped); |
|
616 |
|
617 // The second time, we'll provide the entire interruptible response. |
|
618 continueResponses(); |
|
619 download.onchange = null; |
|
620 let promiseAttempt = download.start(); |
|
621 |
|
622 // Download state should have already been reset. |
|
623 do_check_false(download.stopped); |
|
624 do_check_false(download.canceled); |
|
625 do_check_true(download.error === null); |
|
626 |
|
627 // For the following test, we rely on the network layer reporting its progress |
|
628 // asynchronously. Otherwise, there is nothing stopping the restarted |
|
629 // download from reaching the same progress as the first request already. |
|
630 do_check_eq(download.progress, 0); |
|
631 do_check_eq(download.totalBytes, 0); |
|
632 do_check_eq(download.currentBytes, 0); |
|
633 |
|
634 yield promiseAttempt; |
|
635 |
|
636 do_check_true(download.stopped); |
|
637 do_check_true(download.succeeded); |
|
638 do_check_false(download.canceled); |
|
639 do_check_true(download.error === null); |
|
640 |
|
641 yield promiseVerifyContents(download.target.path, |
|
642 TEST_DATA_SHORT + TEST_DATA_SHORT); |
|
643 }); |
|
644 |
|
645 /** |
|
646 * Cancels a download and restarts it from where it stopped. |
|
647 */ |
|
648 add_task(function test_cancel_midway_restart_tryToKeepPartialData() |
|
649 { |
|
650 let download = yield promiseStartDownload_tryToKeepPartialData(); |
|
651 yield download.cancel(); |
|
652 |
|
653 do_check_true(download.stopped); |
|
654 do_check_true(download.hasPartialData); |
|
655 |
|
656 // The target file should not exist, but we should have kept the partial data. |
|
657 do_check_false(yield OS.File.exists(download.target.path)); |
|
658 yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT); |
|
659 |
|
660 // Verify that the server sent the response from the start. |
|
661 do_check_eq(gMostRecentFirstBytePos, 0); |
|
662 |
|
663 // The second time, we'll request and obtain the second part of the response, |
|
664 // but we still stop when half of the remaining progress is reached. |
|
665 let deferMidway = Promise.defer(); |
|
666 download.onchange = function () { |
|
667 if (!download.stopped && !download.canceled && |
|
668 download.currentBytes == Math.floor(TEST_DATA_SHORT.length * 3 / 2)) { |
|
669 download.onchange = null; |
|
670 deferMidway.resolve(); |
|
671 } |
|
672 }; |
|
673 |
|
674 mustInterruptResponses(); |
|
675 let promiseAttempt = download.start(); |
|
676 |
|
677 // Continue when the number of bytes we received is correct, then check that |
|
678 // progress is at about 75 percent. The exact figure may vary because of |
|
679 // rounding issues, since the total number of bytes in the response might not |
|
680 // be a multiple of four. |
|
681 yield deferMidway.promise; |
|
682 do_check_true(download.progress > 72 && download.progress < 78); |
|
683 |
|
684 // Now we allow the download to finish. |
|
685 continueResponses(); |
|
686 yield promiseAttempt; |
|
687 |
|
688 // Check that the server now sent the second part only. |
|
689 do_check_eq(gMostRecentFirstBytePos, TEST_DATA_SHORT.length); |
|
690 |
|
691 // The target file should now have been created, and the ".part" file deleted. |
|
692 yield promiseVerifyContents(download.target.path, |
|
693 TEST_DATA_SHORT + TEST_DATA_SHORT); |
|
694 do_check_false(yield OS.File.exists(download.target.partFilePath)); |
|
695 }); |
|
696 |
|
697 /** |
|
698 * Cancels a download while keeping partially downloaded data, then removes the |
|
699 * data and restarts the download from the beginning. |
|
700 */ |
|
701 add_task(function test_cancel_midway_restart_removePartialData() |
|
702 { |
|
703 let download = yield promiseStartDownload_tryToKeepPartialData(); |
|
704 yield download.cancel(); |
|
705 |
|
706 do_check_true(download.hasPartialData); |
|
707 yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT); |
|
708 |
|
709 yield download.removePartialData(); |
|
710 |
|
711 do_check_false(download.hasPartialData); |
|
712 do_check_false(yield OS.File.exists(download.target.partFilePath)); |
|
713 |
|
714 // The second time, we'll request and obtain the entire response again. |
|
715 continueResponses(); |
|
716 yield download.start(); |
|
717 |
|
718 // Verify that the server sent the response from the start. |
|
719 do_check_eq(gMostRecentFirstBytePos, 0); |
|
720 |
|
721 // The target file should now have been created, and the ".part" file deleted. |
|
722 yield promiseVerifyContents(download.target.path, |
|
723 TEST_DATA_SHORT + TEST_DATA_SHORT); |
|
724 do_check_false(yield OS.File.exists(download.target.partFilePath)); |
|
725 }); |
|
726 |
|
727 /** |
|
728 * Cancels a download while keeping partially downloaded data, then removes the |
|
729 * data and restarts the download from the beginning without keeping the partial |
|
730 * data anymore. |
|
731 */ |
|
732 add_task(function test_cancel_midway_restart_tryToKeepPartialData_false() |
|
733 { |
|
734 let download = yield promiseStartDownload_tryToKeepPartialData(); |
|
735 yield download.cancel(); |
|
736 |
|
737 download.tryToKeepPartialData = false; |
|
738 |
|
739 // The above property change does not affect existing partial data. |
|
740 do_check_true(download.hasPartialData); |
|
741 yield promiseVerifyContents(download.target.partFilePath, TEST_DATA_SHORT); |
|
742 |
|
743 yield download.removePartialData(); |
|
744 do_check_false(yield OS.File.exists(download.target.partFilePath)); |
|
745 |
|
746 // Restart the download from the beginning. |
|
747 mustInterruptResponses(); |
|
748 download.start(); |
|
749 |
|
750 yield promiseDownloadMidway(download); |
|
751 yield promisePartFileReady(download); |
|
752 |
|
753 // While the download is in progress, we should still have a ".part" file. |
|
754 do_check_false(download.hasPartialData); |
|
755 do_check_true(yield OS.File.exists(download.target.partFilePath)); |
|
756 |
|
757 yield download.cancel(); |
|
758 |
|
759 // The ".part" file should be deleted now that the download is canceled. |
|
760 do_check_false(download.hasPartialData); |
|
761 do_check_false(yield OS.File.exists(download.target.partFilePath)); |
|
762 |
|
763 // The third time, we'll request and obtain the entire response again. |
|
764 continueResponses(); |
|
765 yield download.start(); |
|
766 |
|
767 // Verify that the server sent the response from the start. |
|
768 do_check_eq(gMostRecentFirstBytePos, 0); |
|
769 |
|
770 // The target file should now have been created, and the ".part" file deleted. |
|
771 yield promiseVerifyContents(download.target.path, |
|
772 TEST_DATA_SHORT + TEST_DATA_SHORT); |
|
773 do_check_false(yield OS.File.exists(download.target.partFilePath)); |
|
774 }); |
|
775 |
|
776 /** |
|
777 * Cancels a download right after starting it, then restarts it immediately. |
|
778 */ |
|
779 add_task(function test_cancel_immediately_restart_immediately() |
|
780 { |
|
781 mustInterruptResponses(); |
|
782 |
|
783 let download = yield promiseStartDownload(httpUrl("interruptible.txt")); |
|
784 let promiseAttempt = download.start(); |
|
785 |
|
786 do_check_false(download.stopped); |
|
787 |
|
788 download.cancel(); |
|
789 do_check_true(download.canceled); |
|
790 |
|
791 let promiseRestarted = download.start(); |
|
792 do_check_false(download.stopped); |
|
793 do_check_false(download.canceled); |
|
794 do_check_true(download.error === null); |
|
795 |
|
796 // For the following test, we rely on the network layer reporting its progress |
|
797 // asynchronously. Otherwise, there is nothing stopping the restarted |
|
798 // download from reaching the same progress as the first request already. |
|
799 do_check_eq(download.hasProgress, false); |
|
800 do_check_eq(download.progress, 0); |
|
801 do_check_eq(download.totalBytes, 0); |
|
802 do_check_eq(download.currentBytes, 0); |
|
803 |
|
804 // Ensure the next request is now allowed to complete, regardless of whether |
|
805 // the canceled request was received by the server or not. |
|
806 continueResponses(); |
|
807 try { |
|
808 yield promiseAttempt; |
|
809 // If we get here, it means that the first attempt actually succeeded. In |
|
810 // fact, this could be a valid outcome, because the cancellation request may |
|
811 // not have been processed in time before the download finished. |
|
812 do_print("The download should have been canceled."); |
|
813 } catch (ex if ex instanceof Downloads.Error) { |
|
814 do_check_false(ex.becauseSourceFailed); |
|
815 do_check_false(ex.becauseTargetFailed); |
|
816 } |
|
817 |
|
818 yield promiseRestarted; |
|
819 |
|
820 do_check_true(download.stopped); |
|
821 do_check_true(download.succeeded); |
|
822 do_check_false(download.canceled); |
|
823 do_check_true(download.error === null); |
|
824 |
|
825 yield promiseVerifyContents(download.target.path, |
|
826 TEST_DATA_SHORT + TEST_DATA_SHORT); |
|
827 }); |
|
828 |
|
829 /** |
|
830 * Cancels a download midway, then restarts it immediately. |
|
831 */ |
|
832 add_task(function test_cancel_midway_restart_immediately() |
|
833 { |
|
834 mustInterruptResponses(); |
|
835 |
|
836 let download = yield promiseStartDownload(httpUrl("interruptible.txt")); |
|
837 let promiseAttempt = download.start(); |
|
838 |
|
839 // The first time, cancel the download midway. |
|
840 yield promiseDownloadMidway(download); |
|
841 download.cancel(); |
|
842 do_check_true(download.canceled); |
|
843 |
|
844 let promiseRestarted = download.start(); |
|
845 do_check_false(download.stopped); |
|
846 do_check_false(download.canceled); |
|
847 do_check_true(download.error === null); |
|
848 |
|
849 // For the following test, we rely on the network layer reporting its progress |
|
850 // asynchronously. Otherwise, there is nothing stopping the restarted |
|
851 // download from reaching the same progress as the first request already. |
|
852 do_check_eq(download.hasProgress, false); |
|
853 do_check_eq(download.progress, 0); |
|
854 do_check_eq(download.totalBytes, 0); |
|
855 do_check_eq(download.currentBytes, 0); |
|
856 |
|
857 // The second request is allowed to complete. |
|
858 continueResponses(); |
|
859 try { |
|
860 yield promiseAttempt; |
|
861 do_throw("The download should have been canceled."); |
|
862 } catch (ex if ex instanceof Downloads.Error) { |
|
863 do_check_false(ex.becauseSourceFailed); |
|
864 do_check_false(ex.becauseTargetFailed); |
|
865 } |
|
866 |
|
867 yield promiseRestarted; |
|
868 |
|
869 do_check_true(download.stopped); |
|
870 do_check_true(download.succeeded); |
|
871 do_check_false(download.canceled); |
|
872 do_check_true(download.error === null); |
|
873 |
|
874 yield promiseVerifyContents(download.target.path, |
|
875 TEST_DATA_SHORT + TEST_DATA_SHORT); |
|
876 }); |
|
877 |
|
878 /** |
|
879 * Calls the "cancel" method on a successful download. |
|
880 */ |
|
881 add_task(function test_cancel_successful() |
|
882 { |
|
883 let download = yield promiseStartDownload(); |
|
884 yield promiseDownloadStopped(download); |
|
885 |
|
886 // The cancel method should succeed with no effect. |
|
887 yield download.cancel(); |
|
888 |
|
889 do_check_true(download.stopped); |
|
890 do_check_true(download.succeeded); |
|
891 do_check_false(download.canceled); |
|
892 do_check_true(download.error === null); |
|
893 |
|
894 yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); |
|
895 }); |
|
896 |
|
897 /** |
|
898 * Calls the "cancel" method two times in a row. |
|
899 */ |
|
900 add_task(function test_cancel_twice() |
|
901 { |
|
902 mustInterruptResponses(); |
|
903 |
|
904 let download = yield promiseStartDownload(httpUrl("interruptible.txt")); |
|
905 |
|
906 let promiseAttempt = download.start(); |
|
907 do_check_false(download.stopped); |
|
908 |
|
909 let promiseCancel1 = download.cancel(); |
|
910 do_check_true(download.canceled); |
|
911 let promiseCancel2 = download.cancel(); |
|
912 |
|
913 try { |
|
914 yield promiseAttempt; |
|
915 do_throw("The download should have been canceled."); |
|
916 } catch (ex if ex instanceof Downloads.Error) { |
|
917 do_check_false(ex.becauseSourceFailed); |
|
918 do_check_false(ex.becauseTargetFailed); |
|
919 } |
|
920 |
|
921 // Both promises should now be resolved. |
|
922 yield promiseCancel1; |
|
923 yield promiseCancel2; |
|
924 |
|
925 do_check_true(download.stopped); |
|
926 do_check_false(download.succeeded); |
|
927 do_check_true(download.canceled); |
|
928 do_check_true(download.error === null); |
|
929 |
|
930 do_check_false(yield OS.File.exists(download.target.path)); |
|
931 }); |
|
932 |
|
933 /** |
|
934 * Checks that a download cannot be restarted after the "finalize" method. |
|
935 */ |
|
936 add_task(function test_finalize() |
|
937 { |
|
938 mustInterruptResponses(); |
|
939 |
|
940 let download = yield promiseStartDownload(httpUrl("interruptible.txt")); |
|
941 |
|
942 let promiseFinalized = download.finalize(); |
|
943 |
|
944 try { |
|
945 yield download.start(); |
|
946 do_throw("It should not be possible to restart after finalization."); |
|
947 } catch (ex) { } |
|
948 |
|
949 yield promiseFinalized; |
|
950 |
|
951 do_check_true(download.stopped); |
|
952 do_check_false(download.succeeded); |
|
953 do_check_true(download.canceled); |
|
954 do_check_true(download.error === null); |
|
955 |
|
956 do_check_false(yield OS.File.exists(download.target.path)); |
|
957 }); |
|
958 |
|
959 /** |
|
960 * Checks that the "finalize" method can remove partially downloaded data. |
|
961 */ |
|
962 add_task(function test_finalize_tryToKeepPartialData() |
|
963 { |
|
964 // Check finalization without removing partial data. |
|
965 let download = yield promiseStartDownload_tryToKeepPartialData(); |
|
966 yield download.finalize(); |
|
967 |
|
968 do_check_true(download.hasPartialData); |
|
969 do_check_true(yield OS.File.exists(download.target.partFilePath)); |
|
970 |
|
971 // Clean up. |
|
972 yield download.removePartialData(); |
|
973 |
|
974 // Check finalization while removing partial data. |
|
975 download = yield promiseStartDownload_tryToKeepPartialData(); |
|
976 yield download.finalize(true); |
|
977 |
|
978 do_check_false(download.hasPartialData); |
|
979 do_check_false(yield OS.File.exists(download.target.partFilePath)); |
|
980 }); |
|
981 |
|
982 /** |
|
983 * Checks that whenSucceeded returns a promise that is resolved after a restart. |
|
984 */ |
|
985 add_task(function test_whenSucceeded_after_restart() |
|
986 { |
|
987 mustInterruptResponses(); |
|
988 |
|
989 let promiseSucceeded; |
|
990 |
|
991 let download; |
|
992 if (!gUseLegacySaver) { |
|
993 // When testing DownloadCopySaver, we have control over the download, thus |
|
994 // we can verify getting a reference before the first download attempt. |
|
995 download = yield promiseNewDownload(httpUrl("interruptible.txt")); |
|
996 promiseSucceeded = download.whenSucceeded(); |
|
997 download.start(); |
|
998 } else { |
|
999 // When testing DownloadLegacySaver, the download is already started when it |
|
1000 // is created, thus we cannot get the reference before the first attempt. |
|
1001 download = yield promiseStartLegacyDownload(httpUrl("interruptible.txt")); |
|
1002 promiseSucceeded = download.whenSucceeded(); |
|
1003 } |
|
1004 |
|
1005 // Cancel the first download attempt. |
|
1006 yield download.cancel(); |
|
1007 |
|
1008 // The second request is allowed to complete. |
|
1009 continueResponses(); |
|
1010 download.start(); |
|
1011 |
|
1012 // Wait for the download to finish by waiting on the whenSucceeded promise. |
|
1013 yield promiseSucceeded; |
|
1014 |
|
1015 do_check_true(download.stopped); |
|
1016 do_check_true(download.succeeded); |
|
1017 do_check_false(download.canceled); |
|
1018 do_check_true(download.error === null); |
|
1019 |
|
1020 yield promiseVerifyContents(download.target.path, |
|
1021 TEST_DATA_SHORT + TEST_DATA_SHORT); |
|
1022 }); |
|
1023 |
|
1024 /** |
|
1025 * Ensures download error details are reported on network failures. |
|
1026 */ |
|
1027 add_task(function test_error_source() |
|
1028 { |
|
1029 let serverSocket = startFakeServer(); |
|
1030 try { |
|
1031 let sourceUrl = "http://localhost:" + serverSocket.port + "/source.txt"; |
|
1032 |
|
1033 let download; |
|
1034 try { |
|
1035 if (!gUseLegacySaver) { |
|
1036 // When testing DownloadCopySaver, we want to check that the promise |
|
1037 // returned by the "start" method is rejected. |
|
1038 download = yield promiseNewDownload(sourceUrl); |
|
1039 |
|
1040 do_check_true(download.error === null); |
|
1041 |
|
1042 yield download.start(); |
|
1043 } else { |
|
1044 // When testing DownloadLegacySaver, we cannot be sure whether we are |
|
1045 // testing the promise returned by the "start" method or we are testing |
|
1046 // the "error" property checked by promiseDownloadStopped. This happens |
|
1047 // because we don't have control over when the download is started. |
|
1048 download = yield promiseStartLegacyDownload(sourceUrl); |
|
1049 yield promiseDownloadStopped(download); |
|
1050 } |
|
1051 do_throw("The download should have failed."); |
|
1052 } catch (ex if ex instanceof Downloads.Error && ex.becauseSourceFailed) { |
|
1053 // A specific error object is thrown when reading from the source fails. |
|
1054 } |
|
1055 |
|
1056 // Check the properties now that the download stopped. |
|
1057 do_check_true(download.stopped); |
|
1058 do_check_false(download.canceled); |
|
1059 do_check_true(download.error !== null); |
|
1060 do_check_true(download.error.becauseSourceFailed); |
|
1061 do_check_false(download.error.becauseTargetFailed); |
|
1062 |
|
1063 do_check_false(yield OS.File.exists(download.target.path)); |
|
1064 } finally { |
|
1065 serverSocket.close(); |
|
1066 } |
|
1067 }); |
|
1068 |
|
1069 /** |
|
1070 * Ensures download error details are reported on local writing failures. |
|
1071 */ |
|
1072 add_task(function test_error_target() |
|
1073 { |
|
1074 // Create a file without write access permissions before downloading. |
|
1075 let targetFile = getTempFile(TEST_TARGET_FILE_NAME); |
|
1076 targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0); |
|
1077 try { |
|
1078 let download; |
|
1079 try { |
|
1080 if (!gUseLegacySaver) { |
|
1081 // When testing DownloadCopySaver, we want to check that the promise |
|
1082 // returned by the "start" method is rejected. |
|
1083 download = yield Downloads.createDownload({ |
|
1084 source: httpUrl("source.txt"), |
|
1085 target: targetFile, |
|
1086 }); |
|
1087 yield download.start(); |
|
1088 } else { |
|
1089 // When testing DownloadLegacySaver, we cannot be sure whether we are |
|
1090 // testing the promise returned by the "start" method or we are testing |
|
1091 // the "error" property checked by promiseDownloadStopped. This happens |
|
1092 // because we don't have control over when the download is started. |
|
1093 download = yield promiseStartLegacyDownload(null, |
|
1094 { targetFile: targetFile }); |
|
1095 yield promiseDownloadStopped(download); |
|
1096 } |
|
1097 do_throw("The download should have failed."); |
|
1098 } catch (ex if ex instanceof Downloads.Error && ex.becauseTargetFailed) { |
|
1099 // A specific error object is thrown when writing to the target fails. |
|
1100 } |
|
1101 |
|
1102 // Check the properties now that the download stopped. |
|
1103 do_check_true(download.stopped); |
|
1104 do_check_false(download.canceled); |
|
1105 do_check_true(download.error !== null); |
|
1106 do_check_true(download.error.becauseTargetFailed); |
|
1107 do_check_false(download.error.becauseSourceFailed); |
|
1108 } finally { |
|
1109 // Restore the default permissions to allow deleting the file on Windows. |
|
1110 if (targetFile.exists()) { |
|
1111 targetFile.permissions = FileUtils.PERMS_FILE; |
|
1112 targetFile.remove(false); |
|
1113 } |
|
1114 } |
|
1115 }); |
|
1116 |
|
1117 /** |
|
1118 * Restarts a failed download. |
|
1119 */ |
|
1120 add_task(function test_error_restart() |
|
1121 { |
|
1122 let download; |
|
1123 |
|
1124 // Create a file without write access permissions before downloading. |
|
1125 let targetFile = getTempFile(TEST_TARGET_FILE_NAME); |
|
1126 targetFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0); |
|
1127 try { |
|
1128 // Use DownloadCopySaver or DownloadLegacySaver based on the test run, |
|
1129 // specifying the target file we created. |
|
1130 if (!gUseLegacySaver) { |
|
1131 download = yield Downloads.createDownload({ |
|
1132 source: httpUrl("source.txt"), |
|
1133 target: targetFile, |
|
1134 }); |
|
1135 download.start(); |
|
1136 } else { |
|
1137 download = yield promiseStartLegacyDownload(null, |
|
1138 { targetFile: targetFile }); |
|
1139 } |
|
1140 yield promiseDownloadStopped(download); |
|
1141 do_throw("The download should have failed."); |
|
1142 } catch (ex if ex instanceof Downloads.Error && ex.becauseTargetFailed) { |
|
1143 // A specific error object is thrown when writing to the target fails. |
|
1144 } finally { |
|
1145 // Restore the default permissions to allow deleting the file on Windows. |
|
1146 if (targetFile.exists()) { |
|
1147 targetFile.permissions = FileUtils.PERMS_FILE; |
|
1148 |
|
1149 // Also for Windows, rename the file before deleting. This makes the |
|
1150 // current file name available immediately for a new file, while deleting |
|
1151 // in place prevents creation of a file with the same name for some time. |
|
1152 targetFile.moveTo(null, targetFile.leafName + ".delete.tmp"); |
|
1153 targetFile.remove(false); |
|
1154 } |
|
1155 } |
|
1156 |
|
1157 // Restart the download and wait for completion. |
|
1158 yield download.start(); |
|
1159 |
|
1160 do_check_true(download.stopped); |
|
1161 do_check_true(download.succeeded); |
|
1162 do_check_false(download.canceled); |
|
1163 do_check_true(download.error === null); |
|
1164 do_check_eq(download.progress, 100); |
|
1165 |
|
1166 yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); |
|
1167 }); |
|
1168 |
|
1169 /** |
|
1170 * Executes download in both public and private modes. |
|
1171 */ |
|
1172 add_task(function test_public_and_private() |
|
1173 { |
|
1174 let sourcePath = "/test_public_and_private.txt"; |
|
1175 let sourceUrl = httpUrl("test_public_and_private.txt"); |
|
1176 let testCount = 0; |
|
1177 |
|
1178 // Apply pref to allow all cookies. |
|
1179 Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); |
|
1180 |
|
1181 function cleanup() { |
|
1182 Services.prefs.clearUserPref("network.cookie.cookieBehavior"); |
|
1183 Services.cookies.removeAll(); |
|
1184 gHttpServer.registerPathHandler(sourcePath, null); |
|
1185 } |
|
1186 do_register_cleanup(cleanup); |
|
1187 |
|
1188 gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { |
|
1189 aResponse.setHeader("Content-Type", "text/plain", false); |
|
1190 |
|
1191 if (testCount == 0) { |
|
1192 // No cookies should exist for first public download. |
|
1193 do_check_false(aRequest.hasHeader("Cookie")); |
|
1194 aResponse.setHeader("Set-Cookie", "foobar=1", false); |
|
1195 testCount++; |
|
1196 } else if (testCount == 1) { |
|
1197 // The cookie should exists for second public download. |
|
1198 do_check_true(aRequest.hasHeader("Cookie")); |
|
1199 do_check_eq(aRequest.getHeader("Cookie"), "foobar=1"); |
|
1200 testCount++; |
|
1201 } else if (testCount == 2) { |
|
1202 // No cookies should exist for first private download. |
|
1203 do_check_false(aRequest.hasHeader("Cookie")); |
|
1204 } |
|
1205 }); |
|
1206 |
|
1207 let targetFile = getTempFile(TEST_TARGET_FILE_NAME); |
|
1208 yield Downloads.fetch(sourceUrl, targetFile); |
|
1209 yield Downloads.fetch(sourceUrl, targetFile); |
|
1210 |
|
1211 if (!gUseLegacySaver) { |
|
1212 let download = yield Downloads.createDownload({ |
|
1213 source: { url: sourceUrl, isPrivate: true }, |
|
1214 target: targetFile, |
|
1215 }); |
|
1216 yield download.start(); |
|
1217 } else { |
|
1218 let download = yield promiseStartLegacyDownload(sourceUrl, |
|
1219 { isPrivate: true }); |
|
1220 yield promiseDownloadStopped(download); |
|
1221 } |
|
1222 |
|
1223 cleanup(); |
|
1224 }); |
|
1225 |
|
1226 /** |
|
1227 * Checks the startTime gets updated even after a restart. |
|
1228 */ |
|
1229 add_task(function test_cancel_immediately_restart_and_check_startTime() |
|
1230 { |
|
1231 let download = yield promiseStartDownload(); |
|
1232 |
|
1233 let startTime = download.startTime; |
|
1234 do_check_true(isValidDate(download.startTime)); |
|
1235 |
|
1236 yield download.cancel(); |
|
1237 do_check_eq(download.startTime.getTime(), startTime.getTime()); |
|
1238 |
|
1239 // Wait for a timeout. |
|
1240 yield promiseTimeout(10); |
|
1241 |
|
1242 yield download.start(); |
|
1243 do_check_true(download.startTime.getTime() > startTime.getTime()); |
|
1244 }); |
|
1245 |
|
1246 /** |
|
1247 * Executes download with content-encoding. |
|
1248 */ |
|
1249 add_task(function test_with_content_encoding() |
|
1250 { |
|
1251 let sourcePath = "/test_with_content_encoding.txt"; |
|
1252 let sourceUrl = httpUrl("test_with_content_encoding.txt"); |
|
1253 |
|
1254 function cleanup() { |
|
1255 gHttpServer.registerPathHandler(sourcePath, null); |
|
1256 } |
|
1257 do_register_cleanup(cleanup); |
|
1258 |
|
1259 gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { |
|
1260 aResponse.setHeader("Content-Type", "text/plain", false); |
|
1261 aResponse.setHeader("Content-Encoding", "gzip", false); |
|
1262 aResponse.setHeader("Content-Length", |
|
1263 "" + TEST_DATA_SHORT_GZIP_ENCODED.length, false); |
|
1264 |
|
1265 let bos = new BinaryOutputStream(aResponse.bodyOutputStream); |
|
1266 bos.writeByteArray(TEST_DATA_SHORT_GZIP_ENCODED, |
|
1267 TEST_DATA_SHORT_GZIP_ENCODED.length); |
|
1268 }); |
|
1269 |
|
1270 let download = yield promiseStartDownload(sourceUrl); |
|
1271 yield promiseDownloadStopped(download); |
|
1272 |
|
1273 do_check_eq(download.progress, 100); |
|
1274 do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length); |
|
1275 |
|
1276 // Ensure the content matches the decoded test data. |
|
1277 yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); |
|
1278 |
|
1279 cleanup(); |
|
1280 }); |
|
1281 |
|
1282 /** |
|
1283 * Checks that the file is not decoded if the extension matches the encoding. |
|
1284 */ |
|
1285 add_task(function test_with_content_encoding_ignore_extension() |
|
1286 { |
|
1287 let sourcePath = "/test_with_content_encoding_ignore_extension.gz"; |
|
1288 let sourceUrl = httpUrl("test_with_content_encoding_ignore_extension.gz"); |
|
1289 |
|
1290 function cleanup() { |
|
1291 gHttpServer.registerPathHandler(sourcePath, null); |
|
1292 } |
|
1293 do_register_cleanup(cleanup); |
|
1294 |
|
1295 gHttpServer.registerPathHandler(sourcePath, function (aRequest, aResponse) { |
|
1296 aResponse.setHeader("Content-Type", "text/plain", false); |
|
1297 aResponse.setHeader("Content-Encoding", "gzip", false); |
|
1298 aResponse.setHeader("Content-Length", |
|
1299 "" + TEST_DATA_SHORT_GZIP_ENCODED.length, false); |
|
1300 |
|
1301 let bos = new BinaryOutputStream(aResponse.bodyOutputStream); |
|
1302 bos.writeByteArray(TEST_DATA_SHORT_GZIP_ENCODED, |
|
1303 TEST_DATA_SHORT_GZIP_ENCODED.length); |
|
1304 }); |
|
1305 |
|
1306 let download = yield promiseStartDownload(sourceUrl); |
|
1307 yield promiseDownloadStopped(download); |
|
1308 |
|
1309 do_check_eq(download.progress, 100); |
|
1310 do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length); |
|
1311 |
|
1312 // Ensure the content matches the encoded test data. We convert the data to a |
|
1313 // string before executing the content check. |
|
1314 yield promiseVerifyContents(download.target.path, |
|
1315 String.fromCharCode.apply(String, TEST_DATA_SHORT_GZIP_ENCODED)); |
|
1316 |
|
1317 cleanup(); |
|
1318 }); |
|
1319 |
|
1320 /** |
|
1321 * Cancels and restarts a download sequentially with content-encoding. |
|
1322 */ |
|
1323 add_task(function test_cancel_midway_restart_with_content_encoding() |
|
1324 { |
|
1325 mustInterruptResponses(); |
|
1326 |
|
1327 let download = yield promiseStartDownload(httpUrl("interruptible_gzip.txt")); |
|
1328 |
|
1329 // The first time, cancel the download midway. |
|
1330 let deferCancel = Promise.defer(); |
|
1331 let onchange = function () { |
|
1332 if (!download.stopped && !download.canceled && |
|
1333 download.currentBytes == TEST_DATA_SHORT_GZIP_ENCODED_FIRST.length) { |
|
1334 deferCancel.resolve(download.cancel()); |
|
1335 } |
|
1336 }; |
|
1337 |
|
1338 // Register for the notification, but also call the function directly in |
|
1339 // case the download already reached the expected progress. |
|
1340 download.onchange = onchange; |
|
1341 onchange(); |
|
1342 |
|
1343 yield deferCancel.promise; |
|
1344 |
|
1345 do_check_true(download.stopped); |
|
1346 |
|
1347 // The second time, we'll provide the entire interruptible response. |
|
1348 continueResponses(); |
|
1349 download.onchange = null; |
|
1350 yield download.start(); |
|
1351 |
|
1352 do_check_eq(download.progress, 100); |
|
1353 do_check_eq(download.totalBytes, TEST_DATA_SHORT_GZIP_ENCODED.length); |
|
1354 |
|
1355 yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); |
|
1356 }); |
|
1357 |
|
1358 /** |
|
1359 * Download with parental controls enabled. |
|
1360 */ |
|
1361 add_task(function test_blocked_parental_controls() |
|
1362 { |
|
1363 function cleanup() { |
|
1364 DownloadIntegration.shouldBlockInTest = false; |
|
1365 } |
|
1366 do_register_cleanup(cleanup); |
|
1367 DownloadIntegration.shouldBlockInTest = true; |
|
1368 |
|
1369 let download; |
|
1370 try { |
|
1371 if (!gUseLegacySaver) { |
|
1372 // When testing DownloadCopySaver, we want to check that the promise |
|
1373 // returned by the "start" method is rejected. |
|
1374 download = yield promiseNewDownload(); |
|
1375 yield download.start(); |
|
1376 } else { |
|
1377 // When testing DownloadLegacySaver, we cannot be sure whether we are |
|
1378 // testing the promise returned by the "start" method or we are testing |
|
1379 // the "error" property checked by promiseDownloadStopped. This happens |
|
1380 // because we don't have control over when the download is started. |
|
1381 download = yield promiseStartLegacyDownload(); |
|
1382 yield promiseDownloadStopped(download); |
|
1383 } |
|
1384 do_throw("The download should have blocked."); |
|
1385 } catch (ex if ex instanceof Downloads.Error && ex.becauseBlocked) { |
|
1386 do_check_true(ex.becauseBlockedByParentalControls); |
|
1387 do_check_true(download.error.becauseBlockedByParentalControls); |
|
1388 } |
|
1389 |
|
1390 // Now that the download stopped, the target file should not exist. |
|
1391 do_check_false(yield OS.File.exists(download.target.path)); |
|
1392 |
|
1393 cleanup(); |
|
1394 }); |
|
1395 |
|
1396 /** |
|
1397 * Test a download that will be blocked by Windows parental controls by |
|
1398 * resulting in an HTTP status code of 450. |
|
1399 */ |
|
1400 add_task(function test_blocked_parental_controls_httpstatus450() |
|
1401 { |
|
1402 let download; |
|
1403 try { |
|
1404 if (!gUseLegacySaver) { |
|
1405 download = yield promiseNewDownload(httpUrl("parentalblocked.zip")); |
|
1406 yield download.start(); |
|
1407 } |
|
1408 else { |
|
1409 download = yield promiseStartLegacyDownload(httpUrl("parentalblocked.zip")); |
|
1410 yield promiseDownloadStopped(download); |
|
1411 } |
|
1412 do_throw("The download should have blocked."); |
|
1413 } catch (ex if ex instanceof Downloads.Error && ex.becauseBlocked) { |
|
1414 do_check_true(ex.becauseBlockedByParentalControls); |
|
1415 do_check_true(download.error.becauseBlockedByParentalControls); |
|
1416 do_check_true(download.stopped); |
|
1417 } |
|
1418 |
|
1419 do_check_false(yield OS.File.exists(download.target.path)); |
|
1420 }); |
|
1421 |
|
1422 /** |
|
1423 * Check that DownloadCopySaver can always retrieve the hash. |
|
1424 * DownloadLegacySaver can only retrieve the hash when |
|
1425 * nsIExternalHelperAppService is invoked. |
|
1426 */ |
|
1427 add_task(function test_getSha256Hash() |
|
1428 { |
|
1429 if (!gUseLegacySaver) { |
|
1430 let download = yield promiseStartDownload(httpUrl("source.txt")); |
|
1431 yield promiseDownloadStopped(download); |
|
1432 do_check_true(download.stopped); |
|
1433 do_check_eq(32, download.saver.getSha256Hash().length); |
|
1434 } |
|
1435 }); |
|
1436 |
|
1437 /** |
|
1438 * Checks that application reputation blocks the download and the target file |
|
1439 * does not exist. |
|
1440 */ |
|
1441 add_task(function test_blocked_applicationReputation() |
|
1442 { |
|
1443 function cleanup() { |
|
1444 DownloadIntegration.shouldBlockInTestForApplicationReputation = false; |
|
1445 } |
|
1446 do_register_cleanup(cleanup); |
|
1447 DownloadIntegration.shouldBlockInTestForApplicationReputation = true; |
|
1448 |
|
1449 let download; |
|
1450 try { |
|
1451 if (!gUseLegacySaver) { |
|
1452 // When testing DownloadCopySaver, we want to check that the promise |
|
1453 // returned by the "start" method is rejected. |
|
1454 download = yield promiseNewDownload(); |
|
1455 yield download.start(); |
|
1456 } else { |
|
1457 // When testing DownloadLegacySaver, we cannot be sure whether we are |
|
1458 // testing the promise returned by the "start" method or we are testing |
|
1459 // the "error" property checked by promiseDownloadStopped. This happens |
|
1460 // because we don't have control over when the download is started. |
|
1461 download = yield promiseStartLegacyDownload(); |
|
1462 yield promiseDownloadStopped(download); |
|
1463 } |
|
1464 do_throw("The download should have blocked."); |
|
1465 } catch (ex if ex instanceof Downloads.Error && ex.becauseBlocked) { |
|
1466 do_check_true(ex.becauseBlockedByReputationCheck); |
|
1467 do_check_true(download.error.becauseBlockedByReputationCheck); |
|
1468 } |
|
1469 |
|
1470 // Now that the download is blocked, the target file should not exist. |
|
1471 do_check_false(yield OS.File.exists(download.target.path)); |
|
1472 cleanup(); |
|
1473 }); |
|
1474 |
|
1475 /** |
|
1476 * download.showContainingDirectory() action |
|
1477 */ |
|
1478 add_task(function test_showContainingDirectory() { |
|
1479 DownloadIntegration._deferTestShowDir = Promise.defer(); |
|
1480 |
|
1481 let targetPath = getTempFile(TEST_TARGET_FILE_NAME).path; |
|
1482 |
|
1483 let download = yield Downloads.createDownload({ |
|
1484 source: { url: httpUrl("source.txt") }, |
|
1485 target: "" |
|
1486 }); |
|
1487 |
|
1488 try { |
|
1489 yield download.showContainingDirectory(); |
|
1490 do_throw("Should have failed because of an invalid path."); |
|
1491 } catch (ex if ex instanceof Components.Exception) { |
|
1492 // Invalid paths on Windows are reported with NS_ERROR_FAILURE, |
|
1493 // but with NS_ERROR_FILE_UNRECOGNIZED_PATH on Mac/Linux |
|
1494 let validResult = ex.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH || |
|
1495 ex.result == Cr.NS_ERROR_FAILURE; |
|
1496 do_check_true(validResult); |
|
1497 } |
|
1498 |
|
1499 download = yield Downloads.createDownload({ |
|
1500 source: { url: httpUrl("source.txt") }, |
|
1501 target: targetPath |
|
1502 }); |
|
1503 |
|
1504 |
|
1505 DownloadIntegration._deferTestShowDir = Promise.defer(); |
|
1506 download.showContainingDirectory(); |
|
1507 let result = yield DownloadIntegration._deferTestShowDir.promise; |
|
1508 do_check_eq(result, "success"); |
|
1509 }); |
|
1510 |
|
1511 /** |
|
1512 * download.launch() action |
|
1513 */ |
|
1514 add_task(function test_launch() { |
|
1515 let customLauncher = getTempFile("app-launcher"); |
|
1516 |
|
1517 // Test both with and without setting a custom application. |
|
1518 for (let launcherPath of [null, customLauncher.path]) { |
|
1519 let download; |
|
1520 if (!gUseLegacySaver) { |
|
1521 // When testing DownloadCopySaver, we have control over the download, thus |
|
1522 // we can test that file is not launched if download.succeeded is not set. |
|
1523 download = yield Downloads.createDownload({ |
|
1524 source: httpUrl("source.txt"), |
|
1525 target: getTempFile(TEST_TARGET_FILE_NAME).path, |
|
1526 launcherPath: launcherPath, |
|
1527 launchWhenSucceeded: true |
|
1528 }); |
|
1529 |
|
1530 try { |
|
1531 yield download.launch(); |
|
1532 do_throw("Can't launch download file as it has not completed yet"); |
|
1533 } catch (ex) { |
|
1534 do_check_eq(ex.message, |
|
1535 "launch can only be called if the download succeeded"); |
|
1536 } |
|
1537 |
|
1538 yield download.start(); |
|
1539 } else { |
|
1540 // When testing DownloadLegacySaver, the download is already started when |
|
1541 // it is created, thus we don't test calling "launch" before starting. |
|
1542 download = yield promiseStartLegacyDownload( |
|
1543 httpUrl("source.txt"), |
|
1544 { launcherPath: launcherPath, |
|
1545 launchWhenSucceeded: true }); |
|
1546 yield promiseDownloadStopped(download); |
|
1547 } |
|
1548 |
|
1549 do_check_true(download.launchWhenSucceeded); |
|
1550 |
|
1551 DownloadIntegration._deferTestOpenFile = Promise.defer(); |
|
1552 download.launch(); |
|
1553 let result = yield DownloadIntegration._deferTestOpenFile.promise; |
|
1554 |
|
1555 // Verify that the results match the test case. |
|
1556 if (!launcherPath) { |
|
1557 // This indicates that the default handler has been chosen. |
|
1558 do_check_true(result === null); |
|
1559 } else { |
|
1560 // Check the nsIMIMEInfo instance that would have been used for launching. |
|
1561 do_check_eq(result.preferredAction, Ci.nsIMIMEInfo.useHelperApp); |
|
1562 do_check_true(result.preferredApplicationHandler |
|
1563 .QueryInterface(Ci.nsILocalHandlerApp) |
|
1564 .executable.equals(customLauncher)); |
|
1565 } |
|
1566 } |
|
1567 }); |
|
1568 |
|
1569 /** |
|
1570 * Test passing an invalid path as the launcherPath property. |
|
1571 */ |
|
1572 add_task(function test_launcherPath_invalid() { |
|
1573 let download = yield Downloads.createDownload({ |
|
1574 source: { url: httpUrl("source.txt") }, |
|
1575 target: { path: getTempFile(TEST_TARGET_FILE_NAME).path }, |
|
1576 launcherPath: " " |
|
1577 }); |
|
1578 |
|
1579 DownloadIntegration._deferTestOpenFile = Promise.defer(); |
|
1580 yield download.start(); |
|
1581 try { |
|
1582 download.launch(); |
|
1583 result = yield DownloadIntegration._deferTestOpenFile.promise; |
|
1584 do_throw("Can't launch file with invalid custom launcher") |
|
1585 } catch (ex if ex instanceof Components.Exception) { |
|
1586 // Invalid paths on Windows are reported with NS_ERROR_FAILURE, |
|
1587 // but with NS_ERROR_FILE_UNRECOGNIZED_PATH on Mac/Linux |
|
1588 let validResult = ex.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH || |
|
1589 ex.result == Cr.NS_ERROR_FAILURE; |
|
1590 do_check_true(validResult); |
|
1591 } |
|
1592 }); |
|
1593 |
|
1594 /** |
|
1595 * Tests that download.launch() is automatically called after |
|
1596 * the download finishes if download.launchWhenSucceeded = true |
|
1597 */ |
|
1598 add_task(function test_launchWhenSucceeded() { |
|
1599 let customLauncher = getTempFile("app-launcher"); |
|
1600 |
|
1601 // Test both with and without setting a custom application. |
|
1602 for (let launcherPath of [null, customLauncher.path]) { |
|
1603 DownloadIntegration._deferTestOpenFile = Promise.defer(); |
|
1604 |
|
1605 if (!gUseLegacySaver) { |
|
1606 let download = yield Downloads.createDownload({ |
|
1607 source: httpUrl("source.txt"), |
|
1608 target: getTempFile(TEST_TARGET_FILE_NAME).path, |
|
1609 launchWhenSucceeded: true, |
|
1610 launcherPath: launcherPath, |
|
1611 }); |
|
1612 yield download.start(); |
|
1613 } else { |
|
1614 let download = yield promiseStartLegacyDownload( |
|
1615 httpUrl("source.txt"), |
|
1616 { launcherPath: launcherPath, |
|
1617 launchWhenSucceeded: true }); |
|
1618 yield promiseDownloadStopped(download); |
|
1619 } |
|
1620 |
|
1621 let result = yield DownloadIntegration._deferTestOpenFile.promise; |
|
1622 |
|
1623 // Verify that the results match the test case. |
|
1624 if (!launcherPath) { |
|
1625 // This indicates that the default handler has been chosen. |
|
1626 do_check_true(result === null); |
|
1627 } else { |
|
1628 // Check the nsIMIMEInfo instance that would have been used for launching. |
|
1629 do_check_eq(result.preferredAction, Ci.nsIMIMEInfo.useHelperApp); |
|
1630 do_check_true(result.preferredApplicationHandler |
|
1631 .QueryInterface(Ci.nsILocalHandlerApp) |
|
1632 .executable.equals(customLauncher)); |
|
1633 } |
|
1634 } |
|
1635 }); |
|
1636 |
|
1637 /** |
|
1638 * Tests that the proper content type is set for a normal download. |
|
1639 */ |
|
1640 add_task(function test_contentType() { |
|
1641 let download = yield promiseStartDownload(httpUrl("source.txt")); |
|
1642 yield promiseDownloadStopped(download); |
|
1643 |
|
1644 do_check_eq("text/plain", download.contentType); |
|
1645 }); |
|
1646 |
|
1647 /** |
|
1648 * Tests that the serialization/deserialization of the startTime Date |
|
1649 * object works correctly. |
|
1650 */ |
|
1651 add_task(function test_toSerializable_startTime() |
|
1652 { |
|
1653 let download1 = yield promiseStartDownload(httpUrl("source.txt")); |
|
1654 yield promiseDownloadStopped(download1); |
|
1655 |
|
1656 let serializable = download1.toSerializable(); |
|
1657 let reserialized = JSON.parse(JSON.stringify(serializable)); |
|
1658 |
|
1659 let download2 = yield Downloads.createDownload(reserialized); |
|
1660 |
|
1661 do_check_eq(download1.startTime.constructor.name, "Date"); |
|
1662 do_check_eq(download2.startTime.constructor.name, "Date"); |
|
1663 do_check_eq(download1.startTime.toJSON(), download2.startTime.toJSON()); |
|
1664 }); |
|
1665 |
|
1666 /** |
|
1667 * This test will call the platform specific operations within |
|
1668 * DownloadPlatform::DownloadDone. While there is no test to verify the |
|
1669 * specific behaviours, this at least ensures that there is no error or crash. |
|
1670 */ |
|
1671 add_task(function test_platform_integration() |
|
1672 { |
|
1673 let downloadFiles = []; |
|
1674 function cleanup() { |
|
1675 for (let file of downloadFiles) { |
|
1676 file.remove(true); |
|
1677 } |
|
1678 } |
|
1679 do_register_cleanup(cleanup); |
|
1680 |
|
1681 for (let isPrivate of [false, true]) { |
|
1682 DownloadIntegration.downloadDoneCalled = false; |
|
1683 |
|
1684 // Some platform specific operations only operate on files outside the |
|
1685 // temporary directory or in the Downloads directory (such as setting |
|
1686 // the Windows searchable attribute, and the Mac Downloads icon bouncing), |
|
1687 // so use the system Downloads directory for the target file. |
|
1688 let targetFilePath = yield DownloadIntegration.getSystemDownloadsDirectory(); |
|
1689 targetFilePath = OS.Path.join(targetFilePath, |
|
1690 "test" + (Math.floor(Math.random() * 1000000))); |
|
1691 let targetFile = new FileUtils.File(targetFilePath); |
|
1692 downloadFiles.push(targetFile); |
|
1693 |
|
1694 let download; |
|
1695 if (gUseLegacySaver) { |
|
1696 download = yield promiseStartLegacyDownload(httpUrl("source.txt"), |
|
1697 { targetFile: targetFile }); |
|
1698 } |
|
1699 else { |
|
1700 download = yield Downloads.createDownload({ |
|
1701 source: httpUrl("source.txt"), |
|
1702 target: targetFile, |
|
1703 }); |
|
1704 download.start(); |
|
1705 } |
|
1706 |
|
1707 // Wait for the whenSucceeded promise to be resolved first. |
|
1708 // downloadDone should be called before the whenSucceeded promise is resolved. |
|
1709 yield download.whenSucceeded().then(function () { |
|
1710 do_check_true(DownloadIntegration.downloadDoneCalled); |
|
1711 }); |
|
1712 |
|
1713 // Then, wait for the promise returned by "start" to be resolved. |
|
1714 yield promiseDownloadStopped(download); |
|
1715 |
|
1716 yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT); |
|
1717 } |
|
1718 }); |
|
1719 |
|
1720 /** |
|
1721 * Checks that downloads are added to browsing history when they start. |
|
1722 */ |
|
1723 add_task(function test_history() |
|
1724 { |
|
1725 mustInterruptResponses(); |
|
1726 |
|
1727 // We will wait for the visit to be notified during the download. |
|
1728 yield promiseClearHistory(); |
|
1729 let promiseVisit = promiseWaitForVisit(httpUrl("interruptible.txt")); |
|
1730 |
|
1731 // Start a download that is not allowed to finish yet. |
|
1732 let download = yield promiseStartDownload(httpUrl("interruptible.txt")); |
|
1733 |
|
1734 // The history notifications should be received before the download completes. |
|
1735 let [time, transitionType] = yield promiseVisit; |
|
1736 do_check_eq(time, download.startTime.getTime() * 1000); |
|
1737 do_check_eq(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD); |
|
1738 |
|
1739 // Restart and complete the download after clearing history. |
|
1740 yield promiseClearHistory(); |
|
1741 download.cancel(); |
|
1742 continueResponses(); |
|
1743 yield download.start(); |
|
1744 |
|
1745 // The restart should not have added a new history visit. |
|
1746 do_check_false(yield promiseIsURIVisited(httpUrl("interruptible.txt"))); |
|
1747 }); |
|
1748 |
|
1749 /** |
|
1750 * Checks that downloads started by nsIHelperAppService are added to the |
|
1751 * browsing history when they start. |
|
1752 */ |
|
1753 add_task(function test_history_tryToKeepPartialData() |
|
1754 { |
|
1755 // We will wait for the visit to be notified during the download. |
|
1756 yield promiseClearHistory(); |
|
1757 let promiseVisit = |
|
1758 promiseWaitForVisit(httpUrl("interruptible_resumable.txt")); |
|
1759 |
|
1760 // Start a download that is not allowed to finish yet. |
|
1761 let beforeStartTimeMs = Date.now(); |
|
1762 let download = yield promiseStartDownload_tryToKeepPartialData(); |
|
1763 |
|
1764 // The history notifications should be received before the download completes. |
|
1765 let [time, transitionType] = yield promiseVisit; |
|
1766 do_check_eq(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD); |
|
1767 |
|
1768 // The time set by nsIHelperAppService may be different than the start time in |
|
1769 // the download object, thus we only check that it is a meaningful time. Note |
|
1770 // that we subtract one second from the earliest time to account for rounding. |
|
1771 do_check_true(time >= beforeStartTimeMs * 1000 - 1000000); |
|
1772 |
|
1773 // Complete the download before finishing the test. |
|
1774 continueResponses(); |
|
1775 yield promiseDownloadStopped(download); |
|
1776 }); |
|
1777 |
|
1778 /** |
|
1779 * Tests that the temp download files are removed on exit and exiting private |
|
1780 * mode after they have been launched. |
|
1781 */ |
|
1782 add_task(function test_launchWhenSucceeded_deleteTempFileOnExit() { |
|
1783 const kDeleteTempFileOnExit = "browser.helperApps.deleteTempFileOnExit"; |
|
1784 |
|
1785 let customLauncherPath = getTempFile("app-launcher").path; |
|
1786 let autoDeleteTargetPathOne = getTempFile(TEST_TARGET_FILE_NAME).path; |
|
1787 let autoDeleteTargetPathTwo = getTempFile(TEST_TARGET_FILE_NAME).path; |
|
1788 let noAutoDeleteTargetPath = getTempFile(TEST_TARGET_FILE_NAME).path; |
|
1789 |
|
1790 let autoDeleteDownloadOne = yield Downloads.createDownload({ |
|
1791 source: { url: httpUrl("source.txt"), isPrivate: true }, |
|
1792 target: autoDeleteTargetPathOne, |
|
1793 launchWhenSucceeded: true, |
|
1794 launcherPath: customLauncherPath, |
|
1795 }); |
|
1796 yield autoDeleteDownloadOne.start(); |
|
1797 |
|
1798 Services.prefs.setBoolPref(kDeleteTempFileOnExit, true); |
|
1799 let autoDeleteDownloadTwo = yield Downloads.createDownload({ |
|
1800 source: httpUrl("source.txt"), |
|
1801 target: autoDeleteTargetPathTwo, |
|
1802 launchWhenSucceeded: true, |
|
1803 launcherPath: customLauncherPath, |
|
1804 }); |
|
1805 yield autoDeleteDownloadTwo.start(); |
|
1806 |
|
1807 Services.prefs.setBoolPref(kDeleteTempFileOnExit, false); |
|
1808 let noAutoDeleteDownload = yield Downloads.createDownload({ |
|
1809 source: httpUrl("source.txt"), |
|
1810 target: noAutoDeleteTargetPath, |
|
1811 launchWhenSucceeded: true, |
|
1812 launcherPath: customLauncherPath, |
|
1813 }); |
|
1814 yield noAutoDeleteDownload.start(); |
|
1815 |
|
1816 Services.prefs.clearUserPref(kDeleteTempFileOnExit); |
|
1817 |
|
1818 do_check_true(yield OS.File.exists(autoDeleteTargetPathOne)); |
|
1819 do_check_true(yield OS.File.exists(autoDeleteTargetPathTwo)); |
|
1820 do_check_true(yield OS.File.exists(noAutoDeleteTargetPath)); |
|
1821 |
|
1822 // Simulate leaving private browsing mode |
|
1823 Services.obs.notifyObservers(null, "last-pb-context-exited", null); |
|
1824 do_check_false(yield OS.File.exists(autoDeleteTargetPathOne)); |
|
1825 |
|
1826 // Simulate browser shutdown |
|
1827 let expire = Cc["@mozilla.org/uriloader/external-helper-app-service;1"] |
|
1828 .getService(Ci.nsIObserver); |
|
1829 expire.observe(null, "profile-before-change", null); |
|
1830 do_check_false(yield OS.File.exists(autoDeleteTargetPathTwo)); |
|
1831 do_check_true(yield OS.File.exists(noAutoDeleteTargetPath)); |
|
1832 }); |