Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

ChromeUtils.defineESModuleGetters(this, {
Downloads: "resource://gre/modules/Downloads.sys.mjs",
DownloadsCommon: "resource:///modules/DownloadsCommon.sys.mjs",
});
const HandlerService = Cc[
"@mozilla.org/uriloader/handler-service;1"
].getService(Ci.nsIHandlerService);
const MIMEService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
let INSECURE_BASE_URL =
getRootDirectory(gTestPath).replace(
) + "download_page.html";
let SECURE_BASE_URL =
getRootDirectory(gTestPath).replace(
) + "download_page.html";
function promiseFocus() {
return new Promise(resolve => {
waitForFocus(resolve);
});
}
function promisePanelOpened() {
if (DownloadsPanel.panel && DownloadsPanel.panel.state == "open") {
return Promise.resolve();
}
return BrowserTestUtils.waitForEvent(DownloadsPanel.panel, "popupshown");
}
async function task_openPanel() {
await promiseFocus();
let promise = promisePanelOpened();
DownloadsPanel.showPanel();
await promise;
}
const downloadMonitoringView = {
_listeners: [],
onDownloadAdded(download) {
for (let listener of this._listeners) {
listener(download);
}
this._listeners = [];
},
waitForDownload(listener) {
this._listeners.push(listener);
},
};
/**
* Waits until a download is triggered.
* Unless the always_ask_before_handling_new_types pref is true, the download
* will simply be saved, so resolve when the view is notified of the new
* download. Otherwise, it waits until a prompt is shown, selects the choosen
* <action>, then accepts the dialog
* @param [action] Which action to select, either:
* "handleInternally", "save" or "open".
* @returns {Promise} Resolved once done.
*/
function shouldTriggerDownload(action = "save") {
if (
Services.prefs.getBoolPref(
"browser.download.always_ask_before_handling_new_types"
)
) {
return new Promise((resolve, reject) => {
Services.wm.addListener({
onOpenWindow(xulWin) {
Services.wm.removeListener(this);
let win = xulWin.docShell.domWindow;
waitForFocus(() => {
if (
win.location ==
"chrome://mozapps/content/downloads/unknownContentType.xhtml"
) {
let dialog = win.document.getElementById("unknownContentType");
let button = dialog.getButton("accept");
let actionRadio = win.document.getElementById(action);
actionRadio.click();
button.disabled = false;
dialog.acceptDialog();
resolve();
} else {
reject();
}
}, win);
},
});
});
}
return new Promise(res => {
downloadMonitoringView.waitForDownload(res);
});
}
const CONSOLE_ERROR_MESSAGE = "Blocked downloading insecure content";
function shouldConsoleError() {
// Waits until CONSOLE_ERROR_MESSAGE was logged
return new Promise((resolve, reject) => {
function listener(msgObj) {
let text = msgObj.message;
if (text.includes(CONSOLE_ERROR_MESSAGE)) {
Services.console.unregisterListener(listener);
resolve();
}
}
Services.console.registerListener(listener);
});
}
async function resetDownloads() {
// Removes all downloads from the download List
const types = new Set();
let publicList = await Downloads.getList(Downloads.PUBLIC);
let downloads = await publicList.getAll();
for (let download of downloads) {
if (download.contentType) {
types.add(download.contentType);
}
publicList.remove(download);
await download.finalize(true);
}
if (types.size) {
// reset handlers for the contentTypes of any files previously downloaded
for (let type of types) {
const mimeInfo = MIMEService.getFromTypeAndExtension(type, "");
info("resetting handler for type: " + type);
HandlerService.remove(mimeInfo);
}
}
}
function shouldNotifyDownloadUI() {
return new Promise(res => {
downloadMonitoringView.waitForDownload(async aDownload => {
let { error } = aDownload;
if (
error.becauseBlockedByReputationCheck &&
error.reputationCheckVerdict == Downloads.Error.BLOCK_VERDICT_INSECURE
) {
// It's an insecure Download, now Check that it has been cleaned up properly
if ((await IOUtils.stat(aDownload.target.path)).size != 0) {
throw new Error(`Download target is not empty!`);
}
if ((await IOUtils.stat(aDownload.target.path)).size != 0) {
throw new Error(`Download partFile was not cleaned up properly`);
}
// Assert that the Referrer is presnt
if (!aDownload.source.referrerInfo) {
throw new Error("The Blocked download is missing the ReferrerInfo");
}
res(aDownload);
} else {
ok(false, "No error for download that was expected to error!");
}
});
});
}
async function runTest(url, link, checkFunction, description) {
await SpecialPowers.pushPrefEnv({
set: [["dom.block_download_insecure", true]],
});
await resetDownloads();
let tab = BrowserTestUtils.addTab(gBrowser, url);
gBrowser.selectedTab = tab;
let browser = gBrowser.getBrowserForTab(tab);
await BrowserTestUtils.browserLoaded(browser);
info("Checking: " + description);
let checkPromise = checkFunction();
// Click the Link to trigger the download
SpecialPowers.spawn(gBrowser.selectedBrowser, [link], contentLink => {
content.document.getElementById(contentLink).click();
});
await checkPromise;
ok(true, description);
BrowserTestUtils.removeTab(tab);
await SpecialPowers.popPrefEnv();
}
add_setup(async () => {
let list = await Downloads.getList(Downloads.ALL);
list.addView(downloadMonitoringView);
registerCleanupFunction(() => list.removeView(downloadMonitoringView));
});
// Test Blocking
add_task(async function test_blocking() {
for (let prefVal of [true, false]) {
await SpecialPowers.pushPrefEnv({
set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
});
await runTest(
INSECURE_BASE_URL,
"insecure",
shouldTriggerDownload,
"Insecure -> Insecure should download"
);
await runTest(
INSECURE_BASE_URL,
"secure",
shouldTriggerDownload,
"Insecure -> Secure should download"
);
await runTest(
SECURE_BASE_URL,
"insecure",
() =>
Promise.all([
shouldTriggerDownload(),
shouldNotifyDownloadUI(),
shouldConsoleError(),
]),
"Secure -> Insecure should Error"
);
await runTest(
SECURE_BASE_URL,
"secure",
shouldTriggerDownload,
"Secure -> Secure should Download"
);
await SpecialPowers.popPrefEnv();
}
});
// Test Manual Unblocking
add_task(async function test_manual_unblocking() {
for (let prefVal of [true, false]) {
await SpecialPowers.pushPrefEnv({
set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
});
await runTest(
SECURE_BASE_URL,
"insecure",
async () => {
let [, download] = await Promise.all([
shouldTriggerDownload(),
shouldNotifyDownloadUI(),
]);
await download.unblock();
Assert.equal(
download.error,
null,
"There should be no error after unblocking"
);
},
"A Blocked Download Should succeeded to Download after a Manual unblock"
);
await SpecialPowers.popPrefEnv();
}
});
// Test Unblock Download Visible
add_task(async function test_unblock_download_visible() {
for (let prefVal of [true, false]) {
await SpecialPowers.pushPrefEnv({
set: [["browser.download.always_ask_before_handling_new_types", prefVal]],
});
// Focus, open and close the panel once
// to make sure the panel is loaded and ready
await promiseFocus();
await runTest(
SECURE_BASE_URL,
"insecure",
async () => {
let panelHasOpened = promisePanelOpened();
info("awaiting that the download is triggered and added to the list");
await Promise.all([shouldTriggerDownload(), shouldNotifyDownloadUI()]);
info("awaiting that the Download list shows itself");
await panelHasOpened;
DownloadsPanel.hidePanel();
ok(true, "The Download Panel should have opened on blocked download");
},
"A Blocked Download Should open the Download Panel"
);
await SpecialPowers.popPrefEnv();
}
});
// Test Download an insecure svg and choose "Open with Firefox"
add_task(async function download_open_insecure_SVG() {
const mimeInfo = MIMEService.getFromTypeAndExtension("image/svg+xml", "svg");
mimeInfo.alwaysAskBeforeHandling = false;
mimeInfo.preferredAction = mimeInfo.handleInternally;
HandlerService.store(mimeInfo);
await SpecialPowers.pushPrefEnv({
set: [["browser.download.always_ask_before_handling_new_types", false]],
});
await promiseFocus();
await runTest(
SECURE_BASE_URL,
"insecureSVG",
async () => {
info("awaiting that the download is triggered and added to the list");
let [_, download] = await Promise.all([
shouldTriggerDownload("handleInternally"),
shouldNotifyDownloadUI(),
]);
let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
await download.unblock();
Assert.equal(
download.error,
null,
"There should be no error after unblocking"
);
let tab = await newTabPromise;
ok(
tab.linkedBrowser._documentURI.filePath.includes(".svg"),
"The download target was opened"
);
BrowserTestUtils.removeTab(tab);
ok(true, "The Content was opened in a new tab");
await SpecialPowers.popPrefEnv();
},
"A Blocked SVG can be opened internally"
);
HandlerService.remove(mimeInfo);
});