Bug 1418616 - Add default extension to downloads saveAs dialogs, r?rpl
MozReview-Commit-ID: 9WOfZoc7wa6
--- a/toolkit/components/extensions/parent/ext-downloads.js
+++ b/toolkit/components/extensions/parent/ext-downloads.js
@@ -514,23 +514,29 @@ this.downloads = class extends Extension
break;
}
}
if (!saveAs) {
return target;
}
+ const window = Services.wm.getMostRecentWindow("navigator:browser");
+ const basename = OS.Path.basename(target);
+ const ext = basename.match(/\.([^.]+)$/);
+
// Setup the file picker Save As dialog.
const picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
- const window = Services.wm.getMostRecentWindow("navigator:browser");
picker.init(window, null, Ci.nsIFilePicker.modeSave);
picker.displayDirectory = new FileUtils.File(dir);
picker.appendFilters(Ci.nsIFilePicker.filterAll);
- picker.defaultString = OS.Path.basename(target);
+ picker.defaultString = basename;
+
+ // Configure a default file extension, used as fallback on Windows.
+ picker.defaultExtension = ext && ext[1];
// Open the dialog and resolve/reject with the result.
return new Promise((resolve, reject) => {
picker.open(result => {
if (result === Ci.nsIFilePicker.returnCancel) {
reject({message: "Download canceled by the user"});
} else {
resolve(picker.file.path);
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_downloads_saveAs.html
+++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_downloads_saveAs.html
@@ -44,28 +44,33 @@ add_task(async function setup() {
await SpecialPowers.popPrefEnv();
pickerDir.remove(true);
defaultDir.remove(true);
});
});
add_task(async function test_downloads_saveAs() {
const pickerFile = pickerDir.clone();
- pickerFile.append("file_download.txt");
+ pickerFile.append("file_download.nonext.txt");
const defaultFile = defaultDir.clone();
- defaultFile.append("file_download.txt");
+ defaultFile.append("file_download.nonext.txt");
const {MockFilePicker} = SpecialPowers;
MockFilePicker.init(window);
MockFilePicker.showCallback = fp => {
// On picker 'show' event, choose the filename that was set as the default
// and append it to the picker's download directory
let file = pickerDir.clone();
+
+ // Assert that the downloads API configures both default properties.
+ is(fp.defaultString, "file_download.nonext.txt", "Got the expected FilePicker defaultString");
+ is(fp.defaultExtension, "txt", "Got the expected FilePicker defaultExtension");
+
file.append(fp.defaultString);
MockFilePicker.setFiles([file]);
};
function background() {
const url = URL.createObjectURL(new Blob(["file content"]));
browser.test.onMessage.addListener(async (filename, saveAs) => {
try {
@@ -96,39 +101,39 @@ add_task(async function test_downloads_s
await extension.startup();
await extension.awaitMessage("ready");
async function testExpectFilePicker(saveAs) {
ok(!pickerFile.exists(), "the file should have been cleaned up properly previously");
MockFilePicker.returnValue = MockFilePicker.returnOK;
- extension.sendMessage("file_download.txt", saveAs);
+ extension.sendMessage("file_download.nonext.txt", saveAs);
let result = await extension.awaitMessage("done");
ok(result.ok, `downloads.download() works with saveAs=${saveAs}`);
ok(pickerFile.exists(), "the file exists.");
is(pickerFile.fileSize, 12, "downloaded file is the correct size");
pickerFile.remove(false);
// Test the user canceling the save dialog.
MockFilePicker.returnValue = MockFilePicker.returnCancel;
- extension.sendMessage("file_download.txt", saveAs);
+ extension.sendMessage("file_download.nonext.txt", saveAs);
result = await extension.awaitMessage("done");
ok(!result.ok, "download rejected if the user cancels the dialog");
is(result.message, "Download canceled by the user", "with the correct message");
ok(!pickerFile.exists(), "file was not downloaded");
}
async function testNoFilePicker(saveAs) {
ok(!defaultFile.exists(), "the file should have been cleaned up properly previously");
- extension.sendMessage("file_download.txt", saveAs);
+ extension.sendMessage("file_download.nonext.txt", saveAs);
let result = await extension.awaitMessage("done");
ok(result.ok, `downloads.download() works with saveAs=${saveAs}`);
ok(defaultFile.exists(), "the file exists.");
is(defaultFile.fileSize, 12, "downloaded file is the correct size");
defaultFile.remove(false);
}