/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ /** * Tests the DownloadIntegration object. */ "use strict"; // Globals /** * Notifies the prompt observers and verify the expected downloads count. * * @param aIsPrivate * Flag to know is test private observers. * @param aExpectedCount * the expected downloads count for quit and offline observers. * @param aExpectedPBCount * the expected downloads count for private browsing observer. */ function notifyPromptObservers(aIsPrivate, aExpectedCount, aExpectedPBCount) { let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance( Ci.nsISupportsPRBool ); // Notify quit application requested observer. DownloadIntegration._testPromptDownloads = -1; Services.obs.notifyObservers(cancelQuit, "quit-application-requested"); Assert.equal(DownloadIntegration._testPromptDownloads, aExpectedCount); // Notify offline requested observer. DownloadIntegration._testPromptDownloads = -1; Services.obs.notifyObservers(cancelQuit, "offline-requested"); Assert.equal(DownloadIntegration._testPromptDownloads, aExpectedCount); if (aIsPrivate) { // Notify last private browsing requested observer. DownloadIntegration._testPromptDownloads = -1; Services.obs.notifyObservers(cancelQuit, "last-pb-context-exiting"); Assert.equal(DownloadIntegration._testPromptDownloads, aExpectedPBCount); } delete DownloadIntegration._testPromptDownloads; } // Tests /** * Allows re-enabling the real download directory logic during one test. */ function allowDirectoriesInTest() { DownloadIntegration.allowDirectories = true; function cleanup() { DownloadIntegration.allowDirectories = false; } registerCleanupFunction(cleanup); return cleanup; } ChromeUtils.defineLazyGetter(this, "gStringBundle", function () { return Services.strings.createBundle( "chrome://mozapps/locale/downloads/downloads.properties" ); }); /** * Tests that getSystemDownloadsDirectory returns an existing directory or * creates a new directory depending on the platform. Instead of the real * directory, this test is executed in the temporary directory so we can safely * delete the created folder to check whether it is created again. */ add_task(async function test_getSystemDownloadsDirectory_exists_or_creates() { let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile); let downloadDir; // OSX / Linux / Windows but not XP/2k if ( Services.appinfo.OS == "Darwin" || Services.appinfo.OS == "Linux" || (Services.appinfo.OS == "WINNT" && parseFloat(Services.sysinfo.getProperty("version")) >= 6) ) { downloadDir = await DownloadIntegration.getSystemDownloadsDirectory(); Assert.equal(downloadDir, tempDir.path); Assert.ok(await IOUtils.exists(downloadDir)); let info = await IOUtils.stat(downloadDir); Assert.equal(info.type, "directory"); } else { let targetPath = PathUtils.join( tempDir.path, gStringBundle.GetStringFromName("downloadsFolder") ); try { await IOUtils.remove(targetPath); } catch (e) {} downloadDir = await DownloadIntegration.getSystemDownloadsDirectory(); Assert.equal(downloadDir, targetPath); Assert.ok(await IOUtils.exists(downloadDir)); let info = await IOUtils.stat(downloadDir); Assert.equal(info.type, "directory"); await IOUtils.remove(targetPath); } }); /** * Tests that the real directory returned by getSystemDownloadsDirectory is not * the one that is used during unit tests. Since this is the actual downloads * directory of the operating system, we don't try to delete it afterwards. */ add_task(async function test_getSystemDownloadsDirectory_real() { let fakeDownloadDir = await DownloadIntegration.getSystemDownloadsDirectory(); let cleanup = allowDirectoriesInTest(); let realDownloadDir = await DownloadIntegration.getSystemDownloadsDirectory(); cleanup(); Assert.notEqual(fakeDownloadDir, realDownloadDir); }); /** * Tests that the getPreferredDownloadsDirectory returns a valid download * directory string path. */ add_task(async function test_getPreferredDownloadsDirectory() { let cleanupDirectories = allowDirectoriesInTest(); let folderListPrefName = "browser.download.folderList"; let dirPrefName = "browser.download.dir"; function cleanupPrefs() { Services.prefs.clearUserPref(folderListPrefName); Services.prefs.clearUserPref(dirPrefName); } registerCleanupFunction(cleanupPrefs); // For legacy cloudstorage users with folderListPrefName as 3, // Should return the system downloads directory because the dir preference // is not set. Services.prefs.setIntPref(folderListPrefName, 3); let systemDir = await DownloadIntegration.getSystemDownloadsDirectory(); let downloadDir = await DownloadIntegration.getPreferredDownloadsDirectory(); Assert.notEqual(downloadDir, ""); Assert.equal(downloadDir, systemDir); // Should return the system downloads directory. Services.prefs.setIntPref(folderListPrefName, 1); systemDir = await DownloadIntegration.getSystemDownloadsDirectory(); downloadDir = await DownloadIntegration.getPreferredDownloadsDirectory(); Assert.notEqual(downloadDir, ""); Assert.equal(downloadDir, systemDir); // Should return the desktop directory. Services.prefs.setIntPref(folderListPrefName, 0); downloadDir = await DownloadIntegration.getPreferredDownloadsDirectory(); Assert.notEqual(downloadDir, ""); Assert.equal(downloadDir, Services.dirsvc.get("Desk", Ci.nsIFile).path); // Should return the system downloads directory because the dir preference // is not set. Services.prefs.setIntPref(folderListPrefName, 2); downloadDir = await DownloadIntegration.getPreferredDownloadsDirectory(); Assert.notEqual(downloadDir, ""); Assert.equal(downloadDir, systemDir); // Should return the directory which is listed in the dir preference. let time = new Date().getTime(); let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile); tempDir.append(time); Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, tempDir); downloadDir = await DownloadIntegration.getPreferredDownloadsDirectory(); Assert.notEqual(downloadDir, ""); Assert.equal(downloadDir, tempDir.path); Assert.ok(await IOUtils.exists(downloadDir)); await IOUtils.remove(tempDir.path); // Should return the system downloads directory beacause the path is invalid // in the dir preference. tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile); tempDir.append("dir_not_exist"); tempDir.append(time); Services.prefs.setComplexValue("browser.download.dir", Ci.nsIFile, tempDir); downloadDir = await DownloadIntegration.getPreferredDownloadsDirectory(); Assert.equal(downloadDir, systemDir); // Should return the system downloads directory because the folderList // preference is invalid Services.prefs.setIntPref(folderListPrefName, 999); downloadDir = await DownloadIntegration.getPreferredDownloadsDirectory(); Assert.equal(downloadDir, systemDir); cleanupPrefs(); cleanupDirectories(); }); /** * Tests that the getTemporaryDownloadsDirectory returns a valid download * directory string path. */ add_task(async function test_getTemporaryDownloadsDirectory() { let cleanup = allowDirectoriesInTest(); let downloadDir = await DownloadIntegration.getTemporaryDownloadsDirectory(); Assert.notEqual(downloadDir, ""); if ("nsILocalFileMac" in Ci) { let preferredDownloadDir = await DownloadIntegration.getPreferredDownloadsDirectory(); Assert.equal(downloadDir, preferredDownloadDir); } else { let tempDir = Services.dirsvc.get("TmpD", Ci.nsIFile); Assert.equal(downloadDir, tempDir.path); } cleanup(); }); // Tests DownloadObserver /** * Re-enables the default observers for the following tests. * * This takes effect the first time a DownloadList object is created, and lasts * until this test file has completed. */ add_task(async function test_observers_setup() { DownloadIntegration.allowObservers = true; registerCleanupFunction(function () { DownloadIntegration.allowObservers = false; }); }); /** * Tests notifications prompts when observers are notified if there are public * and private active downloads. */ add_task(async function test_notifications() { for (let isPrivate of [false, true]) { mustInterruptResponses(); let list = await promiseNewList(isPrivate); let download1 = await promiseNewDownload(httpUrl("interruptible.txt")); let download2 = await promiseNewDownload(httpUrl("interruptible.txt")); let download3 = await promiseNewDownload(httpUrl("interruptible.txt")); let promiseAttempt1 = download1.start(); let promiseAttempt2 = download2.start(); download3.start().catch(() => {}); // Add downloads to list. await list.add(download1); await list.add(download2); await list.add(download3); // Cancel third download await download3.cancel(); notifyPromptObservers(isPrivate, 2, 2); // Allow the downloads to complete. continueResponses(); await promiseAttempt1; await promiseAttempt2; // Clean up. await list.remove(download1); await list.remove(download2); await list.remove(download3); } }); /** * Tests that notifications prompts observers are not notified if there are no * public or private active downloads. */ add_task(async function test_no_notifications() { for (let isPrivate of [false, true]) { let list = await promiseNewList(isPrivate); let download1 = await promiseNewDownload(httpUrl("interruptible.txt")); let download2 = await promiseNewDownload(httpUrl("interruptible.txt")); download1.start().catch(() => {}); download2.start().catch(() => {}); // Add downloads to list. await list.add(download1); await list.add(download2); await download1.cancel(); await download2.cancel(); notifyPromptObservers(isPrivate, 0, 0); // Clean up. await list.remove(download1); await list.remove(download2); } }); /** * Tests notifications prompts when observers are notified if there are public * and private active downloads at the same time. */ add_task(async function test_mix_notifications() { mustInterruptResponses(); let publicList = await promiseNewList(); let privateList = await Downloads.getList(Downloads.PRIVATE); let download1 = await promiseNewDownload(httpUrl("interruptible.txt")); let download2 = await promiseNewDownload(httpUrl("interruptible.txt")); let promiseAttempt1 = download1.start(); let promiseAttempt2 = download2.start(); // Add downloads to lists. await publicList.add(download1); await privateList.add(download2); notifyPromptObservers(true, 2, 1); // Allow the downloads to complete. continueResponses(); await promiseAttempt1; await promiseAttempt2; // Clean up. await publicList.remove(download1); await privateList.remove(download2); }); /** * Tests suspending and resuming as well as going offline and then online again. * The downloads should stop when suspending and start again when resuming. */ add_task(async function test_suspend_resume() { // The default wake delay is 10 seconds, so set the wake delay to be much // faster for these tests. Services.prefs.setIntPref("browser.download.manager.resumeOnWakeDelay", 5); let addDownload = function (list) { return (async function () { let download = await promiseNewDownload(httpUrl("interruptible.txt")); download.start().catch(() => {}); list.add(download); return download; })(); }; let publicList = await promiseNewList(); let privateList = await promiseNewList(true); let download1 = await addDownload(publicList); let download2 = await addDownload(publicList); let download3 = await addDownload(privateList); let download4 = await addDownload(privateList); let download5 = await addDownload(publicList); // First, check that the downloads are all canceled when going to sleep. Services.obs.notifyObservers(null, "sleep_notification"); Assert.ok(download1.canceled); Assert.ok(download2.canceled); Assert.ok(download3.canceled); Assert.ok(download4.canceled); Assert.ok(download5.canceled); // Remove a download. It should not be started again. publicList.remove(download5); Assert.ok(download5.canceled); // When waking up again, the downloads start again after the wake delay. To be // more robust, don't check after a delay but instead just wait for the // downloads to finish. Services.obs.notifyObservers(null, "wake_notification"); await download1.whenSucceeded(); await download2.whenSucceeded(); await download3.whenSucceeded(); await download4.whenSucceeded(); // Downloads should no longer be canceled. However, as download5 was removed // from the public list, it will not be restarted. Assert.ok(!download1.canceled); Assert.ok(download5.canceled); // Create four new downloads and check for going offline and then online again. download1 = await addDownload(publicList); download2 = await addDownload(publicList); download3 = await addDownload(privateList); download4 = await addDownload(privateList); // Going offline should cancel the downloads. Services.obs.notifyObservers(null, "network:offline-about-to-go-offline"); Assert.ok(download1.canceled); Assert.ok(download2.canceled); Assert.ok(download3.canceled); Assert.ok(download4.canceled); // Going back online should start the downloads again. Services.obs.notifyObservers( null, "network:offline-status-changed", "online" ); await download1.whenSucceeded(); await download2.whenSucceeded(); await download3.whenSucceeded(); await download4.whenSucceeded(); Services.prefs.clearUserPref("browser.download.manager.resumeOnWakeDelay"); }); /** * Tests both the downloads list and the in-progress downloads are clear when * private browsing observer is notified. */ add_task(async function test_exit_private_browsing() { mustInterruptResponses(); let privateList = await promiseNewList(true); let download1 = await promiseNewDownload(httpUrl("source.txt")); let download2 = await promiseNewDownload(httpUrl("interruptible.txt")); let promiseAttempt1 = download1.start(); download2.start(); // Add downloads to list. await privateList.add(download1); await privateList.add(download2); // Complete the download. await promiseAttempt1; Assert.equal((await privateList.getAll()).length, 2); // Simulate exiting the private browsing. await new Promise(resolve => { DownloadIntegration._testResolveClearPrivateList = resolve; Services.obs.notifyObservers(null, "last-pb-context-exited"); }); delete DownloadIntegration._testResolveClearPrivateList; Assert.equal((await privateList.getAll()).length, 0); continueResponses(); });