diff options
Diffstat (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_downloads_urlencoded.js')
-rw-r--r-- | toolkit/components/extensions/test/xpcshell/test_ext_downloads_urlencoded.js | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_downloads_urlencoded.js b/toolkit/components/extensions/test/xpcshell/test_ext_downloads_urlencoded.js new file mode 100644 index 0000000000..03288fb5d5 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_downloads_urlencoded.js @@ -0,0 +1,257 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +const { Downloads } = ChromeUtils.importESModule( + "resource://gre/modules/Downloads.sys.mjs" +); + +function backgroundScript() { + let complete = new Map(); + + function waitForComplete(id) { + if (complete.has(id)) { + return complete.get(id).promise; + } + + let promise = new Promise(resolve => { + complete.set(id, { resolve }); + }); + complete.get(id).promise = promise; + return promise; + } + + browser.downloads.onChanged.addListener(change => { + if (change.state && change.state.current == "complete") { + // Make sure we have a promise. + waitForComplete(change.id); + complete.get(change.id).resolve(); + } + }); + + browser.test.onMessage.addListener(async (msg, ...args) => { + if (msg == "download.request") { + try { + let id = await browser.downloads.download(args[0]); + browser.test.sendMessage("download.done", { status: "success", id }); + } catch (error) { + browser.test.sendMessage("download.done", { + status: "error", + errmsg: error.message, + }); + } + } else if (msg == "search.request") { + try { + let downloads = await browser.downloads.search(args[0]); + browser.test.sendMessage("search.done", { + status: "success", + downloads, + }); + } catch (error) { + browser.test.sendMessage("search.done", { + status: "error", + errmsg: error.message, + }); + } + } else if (msg == "waitForComplete.request") { + await waitForComplete(args[0]); + browser.test.sendMessage("waitForComplete.done"); + } + }); + + browser.test.sendMessage("ready"); +} + +async function clearDownloads(callback) { + let list = await Downloads.getList(Downloads.ALL); + let downloads = await list.getAll(); + + await Promise.all(downloads.map(download => list.remove(download))); + + return downloads; +} + +add_task(async function test_decoded_filename_download() { + const server = createHttpServer(); + server.registerPrefixHandler("/data/", (_, res) => res.write("length=8")); + + const BASE = `http://localhost:${server.identity.primaryPort}/data`; + const FILE_NAME_ENCODED_1 = "file%2Fencode.txt"; + const FILE_NAME_DECODED_1 = "file_encode.txt"; + const FILE_NAME_ENCODED_URL_1 = BASE + "/" + FILE_NAME_ENCODED_1; + const FILE_NAME_ENCODED_2 = "file%F0%9F%9A%B2encoded.txt"; + const FILE_NAME_DECODED_2 = "file\u{0001F6B2}encoded.txt"; + const FILE_NAME_ENCODED_URL_2 = BASE + "/" + FILE_NAME_ENCODED_2; + const FILE_NAME_ENCODED_3 = "file%X%20encode.txt"; + const FILE_NAME_DECODED_3 = "file%X encode.txt"; + const FILE_NAME_ENCODED_URL_3 = BASE + "/" + FILE_NAME_ENCODED_3; + const FILE_NAME_ENCODED_4 = "file%E3%80%82encode.txt"; + const FILE_NAME_DECODED_4 = "file\u3002encode.txt"; + const FILE_NAME_ENCODED_URL_4 = BASE + "/" + FILE_NAME_ENCODED_4; + const FILE_ENCODED_LEN = 8; + + const nsIFile = Ci.nsIFile; + let downloadDir = FileUtils.getDir("TmpD", ["downloads"]); + downloadDir.createUnique(nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); + info(`downloadDir ${downloadDir.path}`); + + function downloadPath(filename) { + let path = downloadDir.clone(); + path.append(filename); + return path.path; + } + + Services.prefs.setIntPref("browser.download.folderList", 2); + Services.prefs.setComplexValue("browser.download.dir", nsIFile, downloadDir); + + registerCleanupFunction(async () => { + Services.prefs.clearUserPref("browser.download.folderList"); + Services.prefs.clearUserPref("browser.download.dir"); + await cleanupDir(downloadDir); + await clearDownloads(); + }); + + await clearDownloads().then(downloads => { + info(`removed ${downloads.length} pre-existing downloads from history`); + }); + + let extension = ExtensionTestUtils.loadExtension({ + background: backgroundScript, + manifest: { + permissions: ["downloads"], + }, + }); + + async function download(options) { + extension.sendMessage("download.request", options); + let result = await extension.awaitMessage("download.done"); + + if (result.status == "success") { + info(`wait for onChanged event to indicate ${result.id} is complete`); + extension.sendMessage("waitForComplete.request", result.id); + + await extension.awaitMessage("waitForComplete.done"); + } + + return result; + } + + function search(query) { + extension.sendMessage("search.request", query); + return extension.awaitMessage("search.done"); + } + + await extension.startup(); + await extension.awaitMessage("ready"); + + let downloadIds = {}; + let msg = await download({ url: FILE_NAME_ENCODED_URL_1 }); + equal(msg.status, "success", "download() succeeded"); + downloadIds.fileEncoded1 = msg.id; + + msg = await download({ url: FILE_NAME_ENCODED_URL_2 }); + equal(msg.status, "success", "download() succeeded"); + downloadIds.fileEncoded2 = msg.id; + + msg = await download({ url: FILE_NAME_ENCODED_URL_3 }); + equal(msg.status, "success", "download() succeeded"); + downloadIds.fileEncoded3 = msg.id; + + msg = await download({ url: FILE_NAME_ENCODED_URL_4 }); + equal(msg.status, "success", "download() succeeded"); + downloadIds.fileEncoded4 = msg.id; + + // Search for each individual download and check + // the corresponding DownloadItem. + async function checkDownloadItem(id, expect) { + let item = await search({ id }); + equal(item.status, "success", "search() succeeded"); + equal(item.downloads.length, 1, "search() found exactly 1 download"); + Object.keys(expect).forEach(function (field) { + equal( + item.downloads[0][field], + expect[field], + `DownloadItem.${field} is correct"` + ); + }); + } + + await checkDownloadItem(downloadIds.fileEncoded1, { + url: FILE_NAME_ENCODED_URL_1, + filename: downloadPath(FILE_NAME_DECODED_1), + state: "complete", + bytesReceived: FILE_ENCODED_LEN, + totalBytes: FILE_ENCODED_LEN, + fileSize: FILE_ENCODED_LEN, + exists: true, + }); + + await checkDownloadItem(downloadIds.fileEncoded2, { + url: FILE_NAME_ENCODED_URL_2, + filename: downloadPath(FILE_NAME_DECODED_2), + state: "complete", + bytesReceived: FILE_ENCODED_LEN, + totalBytes: FILE_ENCODED_LEN, + fileSize: FILE_ENCODED_LEN, + exists: true, + }); + + await checkDownloadItem(downloadIds.fileEncoded3, { + url: FILE_NAME_ENCODED_URL_3, + filename: downloadPath(FILE_NAME_DECODED_3), + state: "complete", + bytesReceived: FILE_ENCODED_LEN, + totalBytes: FILE_ENCODED_LEN, + fileSize: FILE_ENCODED_LEN, + exists: true, + }); + + await checkDownloadItem(downloadIds.fileEncoded4, { + url: FILE_NAME_ENCODED_URL_4, + filename: downloadPath(FILE_NAME_DECODED_4), + state: "complete", + bytesReceived: FILE_ENCODED_LEN, + totalBytes: FILE_ENCODED_LEN, + fileSize: FILE_ENCODED_LEN, + exists: true, + }); + + // Searching for downloads by the decoded filename works correctly. + async function checkSearch(query, expected, description) { + let item = await search(query); + equal(item.status, "success", "search() succeeded"); + equal( + item.downloads.length, + expected.length, + `search() for ${description} found exactly ${expected.length} downloads` + ); + equal( + item.downloads[0].id, + downloadIds[expected[0]], + `search() for ${description} returned ${expected[0]} in position ${0}` + ); + } + + await checkSearch( + { filename: downloadPath(FILE_NAME_DECODED_1) }, + ["fileEncoded1"], + "filename" + ); + await checkSearch( + { filename: downloadPath(FILE_NAME_DECODED_2) }, + ["fileEncoded2"], + "filename" + ); + await checkSearch( + { filename: downloadPath(FILE_NAME_DECODED_3) }, + ["fileEncoded3"], + "filename" + ); + await checkSearch( + { filename: downloadPath(FILE_NAME_DECODED_4) }, + ["fileEncoded4"], + "filename" + ); + + await extension.unload(); +}); |