diff --git a/toolkit/components/extensions/ext-downloads.js b/toolkit/components/extensions/ext-downloads.js index 8bd2cab15d17..7b48840dc655 100644 --- a/toolkit/components/extensions/ext-downloads.js +++ b/toolkit/components/extensions/ext-downloads.js @@ -474,6 +474,23 @@ extensions.registerSchemaAPI("downloads", "downloads", (extension, context) => { }); }, + removeFile(id) { + return DownloadMap.lazyInit().then(() => { + let item; + try { + item = DownloadMap.fromId(id); + } catch (err) { + return Promise.reject({message: `Invalid download id ${id}`}); + } + if (item.state !== "complete") { + return Promise.reject({message: `Cannot remove incomplete download id ${id}`}); + } + return OS.File.remove(item.filename, {ignoreAbsent: false}).catch((err) => { + return Promise.reject({message: `Could not remove download id ${item.id} because the file doesn't exist`}); + }); + }); + }, + search(query) { return queryHelper(query) .then(items => items.map(item => item.serialize())); diff --git a/toolkit/components/extensions/schemas/downloads.json b/toolkit/components/extensions/schemas/downloads.json index 20da3e1e77bc..2579c583d591 100644 --- a/toolkit/components/extensions/schemas/downloads.json +++ b/toolkit/components/extensions/schemas/downloads.json @@ -627,8 +627,8 @@ }, { "name": "removeFile", + "async": "callback", "type": "function", - "unsupported": true, "parameters": [ { "name": "downloadId", diff --git a/toolkit/components/extensions/test/mochitest/test_chrome_ext_downloads_misc.html b/toolkit/components/extensions/test/mochitest/test_chrome_ext_downloads_misc.html index 92ca6836da1a..986476fadc95 100644 --- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_downloads_misc.html +++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_downloads_misc.html @@ -465,6 +465,125 @@ add_task(function* test_pausecancel() { is(msg.result[0].exists, false, "download.exists is correct"); }); +add_task(function* test_file_removal() { + let msg = yield runInExtension("download", {url: TXT_URL}); + is(msg.status, "success", "download() succeeded"); + const id = msg.result; + + msg = yield runInExtension("waitForEvents", [ + {type: "onCreated", data: {id, url: TXT_URL}}, + { + type: "onChanged", + data: { + id, + state: { + previous: "in_progress", + current: "complete", + }, + }, + }, + ]); + + is(msg.status, "success", "got onCreated and onChanged events"); + + msg = yield runInExtension("removeFile", id); + is(msg.status, "success", "removeFile() succeeded"); + + msg = yield runInExtension("removeFile", id); + is(msg.status, "error", "removeFile() fails since the file was already removed."); + ok(/file doesn't exist/.test(msg.errmsg), "removeFile() failed on removed file."); + + msg = yield runInExtension("removeFile", 1000); + ok(/Invalid download id/.test(msg.errmsg), "removeFile() failed due to non-existent id"); +}); + +add_task(function* test_removal_of_incomplete_download() { + let msg = yield runInExtension("download", {url: INTERRUPTIBLE_URL}); + is(msg.status, "success", "download() succeeded"); + const id = msg.result; + + let progressPromise = waitForProgress(INTERRUPTIBLE_URL, INT_PARTIAL_LEN); + + msg = yield runInExtension("waitForEvents", [ + {type: "onCreated", data: {id}}, + ]); + is(msg.status, "success", "got created and changed events"); + + yield progressPromise; + info(`download reached ${INT_PARTIAL_LEN} bytes`); + + msg = yield runInExtension("pause", id); + is(msg.status, "success", "pause() succeeded"); + + msg = yield runInExtension("waitForEvents", [ + { + type: "onChanged", + data: { + id, + state: { + previous: "in_progress", + current: "interrupted", + }, + paused: { + previous: false, + current: true, + }, + canResume: { + previous: false, + current: true, + }, + }, + }]); + is(msg.status, "success", "got onChanged event corresponding to pause"); + + msg = yield runInExtension("removeFile", id); + is(msg.status, "error", "removeFile() on paused download failed"); + + ok(/Cannot remove incomplete download/.test(msg.errmsg), "removeFile() failed due to download being incomplete"); + + msg = yield runInExtension("resume", id); + is(msg.status, "success", "resume() succeeded"); + + msg = yield runInExtension("waitForEvents", [ + { + type: "onChanged", + data: { + id, + state: { + previous: "interrupted", + current: "in_progress", + }, + paused: { + previous: true, + current: false, + }, + canResume: { + previous: true, + current: false, + }, + error: { + previous: "USER_CANCELED", + current: null, + }, + }, + }, + { + type: "onChanged", + data: { + id, + state: { + previous: "in_progress", + current: "complete", + }, + }, + }, + ]); + is(msg.status, "success", "got onChanged events for resume and complete"); + + msg = yield runInExtension("removeFile", id); + is(msg.status, "success", "removeFile() succeeded following completion of resumed download."); +}); + // Test erase(). We don't do elaborate testing of the query handling // since it uses the exact same engine as search() which is tested // more thoroughly in test_chrome_ext_downloads_search.html