From 43a1f57484b8d03bd738316c7de59119bb68cdc1 Mon Sep 17 00:00:00 2001 From: Andreas Farre Date: Mon, 28 Apr 2025 18:36:45 +0000 Subject: [PATCH] Bug 1941725 - Test iframe sandboxed PDFs. r=smaug PDFs in sandboxed iframes are blocked when handled internally, regardless of sandbox flag. If the sandbox flag is `allow-downloads` and the default action is to not show the PDF inline, allow the action. Differential Revision: https://phabricator.services.mozilla.com/D245896 --- .../exthandler/tests/mochitest/browser.toml | 3 + .../mochitest/browser_pdf_sandboxed_iframe.js | 192 ++++++++++++++++++ .../exthandler/tests/mochitest/file_pdf.pdf | 0 3 files changed, 195 insertions(+) create mode 100644 uriloader/exthandler/tests/mochitest/browser_pdf_sandboxed_iframe.js create mode 100644 uriloader/exthandler/tests/mochitest/file_pdf.pdf diff --git a/uriloader/exthandler/tests/mochitest/browser.toml b/uriloader/exthandler/tests/mochitest/browser.toml index afe441833863..b3343d0e7919 100644 --- a/uriloader/exthandler/tests/mochitest/browser.toml +++ b/uriloader/exthandler/tests/mochitest/browser.toml @@ -118,6 +118,9 @@ support-files = [ "file_pdf_application_pdf.pdf^headers^", ] +["browser_pdf_sandboxed_iframe.js"] +support-files = ["file_pdf.pdf"] + ["browser_pdf_save_as.js"] ["browser_protocol_ask_dialog.js"] diff --git a/uriloader/exthandler/tests/mochitest/browser_pdf_sandboxed_iframe.js b/uriloader/exthandler/tests/mochitest/browser_pdf_sandboxed_iframe.js new file mode 100644 index 000000000000..56c557b586dd --- /dev/null +++ b/uriloader/exthandler/tests/mochitest/browser_pdf_sandboxed_iframe.js @@ -0,0 +1,192 @@ +/* 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 HandlerService = Cc[ + "@mozilla.org/uriloader/handler-service;1" +].getService(Ci.nsIHandlerService); +const MIMEService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); + +const { saveToDisk, alwaysAsk, handleInternally, useSystemDefault } = + Ci.nsIHandlerInfo; + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.download.always_ask_before_handling_new_types", false], + ["browser.download.useDownloadDir", false], + ], + }); + + registerCleanupFunction(async () => { + let publicList = await Downloads.getList(Downloads.PUBLIC); + await publicList.removeFinished(); + + if (DownloadsPanel.isVisible) { + let hiddenPromise = BrowserTestUtils.waitForEvent( + DownloadsPanel.panel, + "popuphidden" + ); + DownloadsPanel.hidePanel(); + await hiddenPromise; + } + + let mimeInfo = MIMEService.getFromTypeAndExtension( + "application/pdf", + "pdf" + ); + let existed = HandlerService.exists(mimeInfo); + if (existed) { + HandlerService.store(mimeInfo); + } else { + HandlerService.remove(mimeInfo); + } + }); +}); + +async function onFilePickerShown(test) { + const { MockFilePicker } = SpecialPowers; + + MockFilePicker.init(window.browsingContext); + MockFilePicker.returnValue = MockFilePicker.returnOK; + + const promise = new Promise(resolve => { + MockFilePicker.showCallback = fp => { + ok(true, "filepicker should be visible"); + Assert.strictEqual( + fp.defaultExtension, + "pdf", + "Default extension in filepicker should be pdf" + ); + Assert.strictEqual( + fp.defaultString, + "file_pdf.pdf", + "Default string name in filepicker should have the correct pdf file name" + ); + setTimeout(resolve, 0); + return Ci.nsIFilePicker.returnCancel; + }; + }); + + test(); + + await promise; + MockFilePicker.reset(); +} + +function waitForAcceptButtonToGetEnabled(doc) { + let dialog = doc.querySelector("#unknownContentType"); + let button = dialog.getButton("accept"); + return TestUtils.waitForCondition( + () => !button.disabled, + "Wait for Accept button to get enabled" + ); +} + +async function onExternalApplication(test) { + let loaded = BrowserTestUtils.domWindowOpenedAndLoaded(); + test(); + const win = await loaded; + is( + win.location.href, + "chrome://mozapps/content/downloads/unknownContentType.xhtml", + "Should have seen the unknown content dialogWindow." + ); + + const doc = win.document; + await waitForAcceptButtonToGetEnabled(doc); + const dialog = doc.querySelector("#unknownContentType"); + dialog.cancelDialog(); +} + +async function onDownload(test) { + const downloadsList = await Downloads.getList(Downloads.PUBLIC); + const savePromise = promiseDownloadFinished(downloadsList, true); + test(); + await savePromise; + ok(true, "Download finished"); +} + +async function onBlockedBySandbox(test) { + const expected = `Download of “${TEST_PATH}file_pdf.pdf” was blocked because the triggering iframe has the sandbox flag set.`; + return new Promise(resolve => { + Services.console.registerListener(function onMessage(msg) { + let { message, logLevel } = msg; + if (logLevel != Ci.nsIConsoleMessage.warn) { + return; + } + if (!message.includes(expected)) { + return; + } + Services.console.unregisterListener(onMessage); + resolve(); + }); + + test(); + }); +} + +function autopass(test) { + ok(true); + test(); +} + +const tests = [ + { + preferredAction: alwaysAsk, + runTest: onExternalApplication, + header: "preferredAction = alwaysAsk", + }, + { + preferredAction: saveToDisk, + runTest: onFilePickerShown, + header: "preferredAction = saveToDisk", + }, + { + preferredAction: handleInternally, + runTest: onBlockedBySandbox, + header: "preferredAction = handleInternally", + }, + { + preferredAction: useSystemDefault, + runTest: onDownload, + header: "preferredAction = useSystemDefault", + }, +]; + +/** + * Tests that selecting the context menu item `Save Link As…` on a PDF link + * opens the file picker when always_ask_before_handling_new_types is disabled, + * regardless of preferredAction. + */ +add_task(async function test_pdf_save_as_link() { + let mimeInfo; + + for (let { preferredAction, runTest, header } of tests) { + mimeInfo = MIMEService.getFromTypeAndExtension("application/pdf", "pdf"); + mimeInfo.alwaysAskBeforeHandling = preferredAction === alwaysAsk; + mimeInfo.preferredAction = preferredAction; + HandlerService.store(mimeInfo); + + info(`Running test: ${header}`); + + await runTest(() => { + gBrowser.selectedTab = BrowserTestUtils.addTab( + gBrowser, + `data:text/html,` + ); + }); + + BrowserTestUtils.removeTab(gBrowser.selectedTab); + } +}); diff --git a/uriloader/exthandler/tests/mochitest/file_pdf.pdf b/uriloader/exthandler/tests/mochitest/file_pdf.pdf new file mode 100644 index 000000000000..e69de29bb2d1