Revision control
Copy as Markdown
Other Tools
/* Any copyright is dedicated to the Public Domain.
"use strict";
//
// Opening a PDF attachment from the compose window routes it through
// nsIURILoader.openURI with a system principal and NO browsing context (see
// OpenSelectedAttachment in MsgComposeCommands.js). When PDF handling is set to
// "always ask" / an external application, the PDF must be handed to the
// external helper (the unknownContentType dialog), NOT claimed by PDF.js:
// PDF.js cannot render without a browsing context, and it previously still
// rewrote the channel's type to text/html, so the attachment was mishandled
// (opened as "nsmail.pdf" in a browser on Linux; an error on Windows).
//
// This exercises the shared toolkit/components/pdfjs guard end-to-end through
// Thunderbird's actual attachment-open path.
const gMimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
const gHandlerService = Cc[
"@mozilla.org/uriloader/handler-service;1"
].getService(Ci.nsIHandlerService);
let gIdentity;
add_setup(async function () {
const { smtpAccount, smtpIdentity } = createSMTPAccount();
gIdentity = smtpIdentity;
MailServices.accounts.defaultAccount = smtpAccount;
// Configure PDFs for "always ask" -- the handler state that triggers the bug.
// Anything other than handleInternally sends OpenSelectedAttachment down the
// windowless nsIURILoader.openURI path.
const handlerInfo = gMimeService.getFromTypeAndExtension(
"application/pdf",
"pdf"
);
const previous = [
handlerInfo.preferredAction,
handlerInfo.alwaysAskBeforeHandling,
];
handlerInfo.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
handlerInfo.alwaysAskBeforeHandling = true;
gHandlerService.store(handlerInfo);
registerCleanupFunction(function () {
const info = gMimeService.getFromTypeAndExtension("application/pdf", "pdf");
info.preferredAction = previous[0];
info.alwaysAskBeforeHandling = previous[1];
gHandlerService.store(info);
MailServices.accounts.removeAccount(smtpAccount, false);
});
});
add_task(async function test_open_pdf_attachment_uses_external_handler() {
const { composeWindow } = await newComposeWindow(gIdentity);
const pdfFile = new FileUtils.File(getTestFilePath("data/attachment.pdf"));
const attachment = Cc[
"@mozilla.org/messengercompose/attachment;1"
].createInstance(Ci.nsIMsgAttachment);
attachment.name = "attachment.pdf";
attachment.contentType = "application/pdf";
attachment.url = Services.io.newFileURI(pdfFile).spec;
attachment.size = pdfFile.fileSize;
await composeWindow.AddAttachments([attachment]);
const bucket = composeWindow.gAttachmentBucket;
bucket.selectedItem = bucket.getItemAtIndex(0);
Assert.equal(bucket.selectedItems.length, 1, "the attachment is selected");
// With the fix, the windowless PDF load is not claimed by PDF.js and falls
// through to the external helper, which -- because we set always-ask -- shows
// the unknownContentType dialog. Without the fix, PDF.js claims the channel,
// rewrites it to text/html, and this dialog never appears, so the test fails.
const dialogPromise = BrowserTestUtils.promiseAlertDialog(
"cancel",
);
composeWindow.OpenSelectedAttachment();
await dialogPromise;
Assert.ok(
true,
"the PDF attachment was routed to the external handler (unknownContentType dialog)"
);
await BrowserTestUtils.closeWindow(composeWindow);
});