From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../mochitest/browser_download_preferred_action.js | 296 +++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 uriloader/exthandler/tests/mochitest/browser_download_preferred_action.js (limited to 'uriloader/exthandler/tests/mochitest/browser_download_preferred_action.js') diff --git a/uriloader/exthandler/tests/mochitest/browser_download_preferred_action.js b/uriloader/exthandler/tests/mochitest/browser_download_preferred_action.js new file mode 100644 index 0000000000..5ebcc568fd --- /dev/null +++ b/uriloader/exthandler/tests/mochitest/browser_download_preferred_action.js @@ -0,0 +1,296 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { DownloadIntegration } = ChromeUtils.importESModule( + "resource://gre/modules/DownloadIntegration.sys.mjs" +); +const { FileTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/FileTestUtils.sys.mjs" +); +const gHandlerService = Cc[ + "@mozilla.org/uriloader/handler-service;1" +].getService(Ci.nsIHandlerService); +const gMIMEService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); +const localHandlerAppFactory = Cc["@mozilla.org/uriloader/local-handler-app;1"]; + +const ROOT_URL = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); + +const FILE_TYPES_MIME_SETTINGS = {}; + +// File types to test +const FILE_TYPES_TO_TEST = [ + // application/ms-word files cannot render in the browser so + // handleInternally does not work for it + { + extension: "doc", + mimeType: "application/ms-word", + blockHandleInternally: true, + }, + { + extension: "pdf", + mimeType: "application/pdf", + }, + { + extension: "pdf", + mimeType: "application/unknown", + }, + { + extension: "pdf", + mimeType: "binary/octet-stream", + }, + // text/plain files automatically render in the browser unless + // the CD header explicitly tells the browser to download it + { + extension: "txt", + mimeType: "text/plain", + requireContentDispositionHeader: true, + }, + { + extension: "xml", + mimeType: "binary/octet-stream", + }, +].map(file => { + return { + ...file, + url: `${ROOT_URL}mime_type_download.sjs?contentType=${file.mimeType}&extension=${file.extension}`, + }; +}); + +// Preferred action types to apply to each downloaded file +const PREFERRED_ACTIONS = [ + "saveToDisk", + "alwaysAsk", + "useHelperApp", + "handleInternally", + "useSystemDefault", +].map(property => { + let label = property.replace(/([A-Z])/g, " $1"); + label = label.charAt(0).toUpperCase() + label.slice(1); + return { + id: Ci.nsIHandlerInfo[property], + label, + }; +}); + +async function createDownloadTest( + downloadList, + localHandlerApp, + file, + action, + useContentDispositionHeader +) { + // Skip handleInternally case for files that cannot be handled internally + if ( + action.id === Ci.nsIHandlerInfo.handleInternally && + file.blockHandleInternally + ) { + return; + } + let skipDownload = + action.id === Ci.nsIHandlerInfo.handleInternally && + file.mimeType === "application/pdf"; + // Types that require the CD header only display as handleInternally + // when the CD header is missing + if (file.requireContentDispositionHeader && !useContentDispositionHeader) { + if (action.id === Ci.nsIHandlerInfo.handleInternally) { + skipDownload = true; + } else { + return; + } + } + info( + `Testing download with mime-type ${file.mimeType} and extension ${ + file.extension + }, preferred action "${action.label}," and ${ + useContentDispositionHeader + ? "Content-Disposition: attachment" + : "no Content-Disposition" + } header.` + ); + info("Preparing for download..."); + // apply preferredAction settings + let mimeSettings = gMIMEService.getFromTypeAndExtension( + file.mimeType, + file.extension + ); + mimeSettings.preferredAction = action.id; + mimeSettings.alwaysAskBeforeHandling = + action.id === Ci.nsIHandlerInfo.alwaysAsk; + if (action.id === Ci.nsIHandlerInfo.useHelperApp) { + mimeSettings.preferredApplicationHandler = localHandlerApp; + } + gHandlerService.store(mimeSettings); + // delayed check for files opened in a new tab, except for skipped downloads + let expectViewInBrowserTab = + action.id === Ci.nsIHandlerInfo.handleInternally && !skipDownload; + let viewInBrowserTabOpened = null; + if (expectViewInBrowserTab) { + viewInBrowserTabOpened = BrowserTestUtils.waitForNewTab( + gBrowser, + uri => uri.includes("file://"), + true + ); + } + // delayed check for launched files + let expectLaunch = + action.id === Ci.nsIHandlerInfo.useSystemDefault || + action.id === Ci.nsIHandlerInfo.useHelperApp; + let oldLaunchFile = DownloadIntegration.launchFile; + let fileLaunched = null; + if (expectLaunch) { + fileLaunched = PromiseUtils.defer(); + DownloadIntegration.launchFile = () => { + ok( + expectLaunch, + `The file ${file.mimeType} should be launched with an external application.` + ); + fileLaunched.resolve(); + }; + } + info(`Start download of ${file.url}`); + let dialogWindowPromise = BrowserTestUtils.domWindowOpenedAndLoaded(); + let downloadFinishedPromise = skipDownload + ? null + : promiseDownloadFinished(downloadList); + BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, file.url); + if (action.id === Ci.nsIHandlerInfo.alwaysAsk) { + info("Check Always Ask dialog."); + let dialogWindow = await dialogWindowPromise; + is( + dialogWindow.location.href, + "chrome://mozapps/content/downloads/unknownContentType.xhtml", + "Should show unknownContentType dialog for Always Ask preferred actions." + ); + let doc = dialogWindow.document; + let dialog = doc.querySelector("#unknownContentType"); + let acceptButton = dialog.getButton("accept"); + acceptButton.disabled = false; + let saveItem = doc.querySelector("#save"); + saveItem.disabled = false; + saveItem.click(); + dialog.acceptDialog(); + } + let download = null; + let downloadPath = null; + if (!skipDownload) { + info("Wait for download to finish..."); + download = await downloadFinishedPromise; + downloadPath = download.target.path; + } + // check delayed assertions + if (expectLaunch) { + info("Wait for file to be launched in external application..."); + await fileLaunched.promise; + } + if (expectViewInBrowserTab) { + info("Wait for file to be opened in new tab..."); + let viewInBrowserTab = await viewInBrowserTabOpened; + ok( + viewInBrowserTab, + `The file ${file.mimeType} should be opened in a new tab.` + ); + BrowserTestUtils.removeTab(viewInBrowserTab); + } + info("Checking for saved file..."); + let saveFound = downloadPath && (await IOUtils.exists(downloadPath)); + info("Cleaning up..."); + if (saveFound) { + try { + info(`Deleting file ${downloadPath}...`); + await IOUtils.remove(downloadPath); + } catch (ex) { + info(`Error: ${ex}`); + } + } + info("Removing download from list..."); + await downloadList.removeFinished(); + info("Clearing settings..."); + DownloadIntegration.launchFile = oldLaunchFile; + info("Asserting results..."); + if (download) { + ok(download.succeeded, "Download should complete successfully"); + ok( + !download._launchedFromPanel, + "Download should never be launched from panel" + ); + } + if (skipDownload) { + ok(!saveFound, "Download should not be saved to disk"); + } else { + ok(saveFound, "Download should be saved to disk"); + } +} + +add_task(async function test_download_preferred_action() { + // Prepare tests + for (const index in FILE_TYPES_TO_TEST) { + let file = FILE_TYPES_TO_TEST[index]; + let originalMimeSettings = gMIMEService.getFromTypeAndExtension( + file.mimeType, + file.extension + ); + if (gHandlerService.exists(originalMimeSettings)) { + FILE_TYPES_MIME_SETTINGS[index] = originalMimeSettings; + } + } + let downloadList = await Downloads.getList(Downloads.PUBLIC); + let oldLaunchFile = DownloadIntegration.launchFile; + registerCleanupFunction(async function () { + await removeAllDownloads(); + DownloadIntegration.launchFile = oldLaunchFile; + Services.prefs.clearUserPref( + "browser.download.always_ask_before_handling_new_types" + ); + BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, "about:home"); + for (const index in FILE_TYPES_TO_TEST) { + let file = FILE_TYPES_TO_TEST[index]; + let mimeSettings = gMIMEService.getFromTypeAndExtension( + file.mimeType, + file.extension + ); + if (FILE_TYPES_MIME_SETTINGS[index]) { + gHandlerService.store(FILE_TYPES_MIME_SETTINGS[index]); + } else { + gHandlerService.remove(mimeSettings); + } + } + }); + await SpecialPowers.pushPrefEnv({ + set: [["browser.download.always_ask_before_handling_new_types", false]], + }); + let launcherPath = FileTestUtils.getTempFile("app-launcher").path; + let localHandlerApp = localHandlerAppFactory.createInstance( + Ci.nsILocalHandlerApp + ); + localHandlerApp.executable = new FileUtils.File(launcherPath); + localHandlerApp.executable.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o755); + // run tests + for (const file of FILE_TYPES_TO_TEST) { + // The CD header specifies the download file extension on download + let fileNoHeader = file; + let fileWithHeader = structuredClone(file); + fileWithHeader.url += "&withHeader"; + for (const action of PREFERRED_ACTIONS) { + // Clone file objects to prevent side-effects between iterations + await createDownloadTest( + downloadList, + localHandlerApp, + structuredClone(fileWithHeader), + action, + true + ); + await createDownloadTest( + downloadList, + localHandlerApp, + structuredClone(fileNoHeader), + action, + false + ); + } + } +}); -- cgit v1.2.3