Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'win' && socketprocess_networking && fission OR os == 'mac' && socketprocess_networking && fission OR os == 'mac' && debug OR os == 'linux' && socketprocess_networking OR os == 'android'
- Manifest: toolkit/components/extensions/test/xpcshell/xpcshell-remote.toml includes toolkit/components/extensions/test/xpcshell/xpcshell-common.toml
- Manifest: toolkit/components/extensions/test/xpcshell/xpcshell.toml includes toolkit/components/extensions/test/xpcshell/xpcshell-common.toml
/* -*- 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"
);
const TEST_DATA = "This is 31 bytes of sample data";
const TOTAL_LEN = 31;
const PARTIAL_LEN = 15;
const server = createHttpServer();
const port = server.identity.primaryPort;
let requestCount = 0;
server.registerPathHandler("/file_download.txt", async (request, response) => {
response.setHeader("Last-Modified", "Thu, 1 Jan 2009 00:00:00 GMT", false);
response.setHeader("Content-Type", "text/plain", false);
response.setHeader("Accept-Ranges", "bytes", false);
if (!request.hasHeader("Range")) {
equal(++requestCount, 1, "First request should expect a full response");
response.processAsync();
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Length", "" + TOTAL_LEN, false);
let downloadPromise = waitForDownload();
response.write(TEST_DATA.slice(0, PARTIAL_LEN));
let download = await downloadPromise;
await waitForCreatedPartFile(download);
response.finish();
} else {
// When downloads.resume() is called for the incomplete download
// from above, we should receive a Range request.
equal(++requestCount, 2, "Second request should be a Range request");
equal(request.getHeader("Range"), `bytes=${PARTIAL_LEN}-`, "Range ok");
response.setStatusLine(request.httpVersion, 206, "Partial Content");
response.setHeader(
"Content-Range",
`${PARTIAL_LEN}-${TOTAL_LEN - 1}/${TOTAL_LEN}`,
false
);
let data = TEST_DATA.slice(PARTIAL_LEN);
response.setHeader("Content-Length", "" + data.length, false);
response.write(data);
}
});
async function waitForDownload() {
let list = await Downloads.getList(Downloads.ALL);
return new Promise(resolve => {
const view = {
onDownloadChanged(download) {
if (
download.source.url == url &&
download.currentBytes == PARTIAL_LEN
) {
list.removeView(view);
resolve(download);
}
},
};
list.addView(view);
});
}
async function waitForCreatedPartFile(download) {
const partFilePath = download.target.partFilePath;
info(`Wait for ${partFilePath} to be created`);
let lastError;
await TestUtils.waitForCondition(
() =>
IOUtils.exists(partFilePath).catch(err => {
lastError = err;
return false;
}),
`Wait for the ${partFilePath} to exists before continuing the test`
).catch(err => {
if (lastError) {
throw lastError;
}
throw err;
});
}
function backgroundScript() {
let dlid = 0;
let expectedState;
browser.test.onMessage.addListener(async msg => {
try {
if (msg.resume) {
expectedState = "complete";
await browser.downloads.resume(dlid);
} else {
expectedState = "interrupted";
dlid = await browser.downloads.download({ url: msg.url });
}
} catch (err) {
browser.test.fail(`Unexpected error in test.onMessage: ${err}`);
}
});
browser.downloads.onChanged.addListener(({ id, state }) => {
if (dlid !== id || !state || state.current !== expectedState) {
return;
}
browser.downloads.search({ id }).then(([download]) => {
browser.test.sendMessage(expectedState, download);
});
});
browser.test.sendMessage("ready");
}
async function clearDownloads() {
let list = await Downloads.getList(Downloads.ALL);
let downloads = await list.getAll();
await Promise.all(
downloads.map(async download => {
await download.finalize(true);
list.remove(download);
})
);
return downloads;
}
add_setup(() => {
const nsIFile = Ci.nsIFile;
let downloadDir = FileUtils.getDir("TmpD", ["downloads"]);
downloadDir.createUnique(nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
info(`downloadDir ${downloadDir.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 clearDownloads();
downloadDir.remove(true);
});
});
add_task(async function test_resume_download_after_network_error() {
let extension = ExtensionTestUtils.loadExtension({
background: backgroundScript,
manifest: {
permissions: ["downloads"],
},
});
await extension.startup();
await extension.awaitMessage("ready");
extension.sendMessage({ url });
let download = await extension.awaitMessage("interrupted");
equal(download.error, "NETWORK_FAILED", "download() failed");
equal(download.state, "interrupted", "download.state is correct");
equal(download.paused, false, "download.paused is correct");
equal(
download.estimatedEndTime,
undefined,
"download.estimatedEndTime is correct"
);
equal(download.canResume, true, "download.canResume is correct");
equal(
download.bytesReceived,
PARTIAL_LEN,
"download.bytesReceived is correct"
);
equal(download.totalBytes, TOTAL_LEN, "download.totalBytes is correct");
equal(download.exists, false, "download.exists is correct");
extension.sendMessage({ resume: true });
download = await extension.awaitMessage("complete");
equal(download.state, "complete", "download.state is correct");
equal(download.paused, false, "download.paused is correct");
equal(
download.estimatedEndTime,
undefined,
"download.estimatedEndTime is correct"
);
equal(download.canResume, false, "download.canResume is correct");
equal(download.error, null, "download.error is correct");
equal(download.bytesReceived, TOTAL_LEN, "download.bytesReceived is correct");
equal(download.totalBytes, TOTAL_LEN, "download.totalBytes is correct");
equal(download.exists, true, "download.exists is correct");
await extension.unload();
});