/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at http://mozilla.org/MPL/2.0/. */ var gAccount; var gFolder; add_setup(() => { gAccount = createAccount(); let rootFolder = gAccount.incomingServer.rootFolder; rootFolder.createSubfolder("test0", null); gFolder = rootFolder.getChildNamed("test0"); createMessages(gFolder, 5); }); add_task(async function testExternalMessage() { // Copy eml file into the profile folder, where we can delete it during the test. let profileDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); profileDir.initWithPath(PathUtils.profileDir); let messageFile = new FileUtils.File( getTestFilePath("messages/attachedMessageSample.eml") ); messageFile.copyTo(profileDir, "attachedMessageSample.eml"); let files = { "background.js": async () => { let platformInfo = await browser.runtime.getPlatformInfo(); const emlData = { openExternalFileMessage: { headerMessageId: "sample.eml@mime.sample", author: "Batman ", ccList: ["Robin "], subject: "Attached message with attachments", attachments: 2, size: 9754, external: true, read: null, recipients: ["Heinz "], date: 958796995000, body: "This message has one normal attachment and one email attachment", }, openExternalAttachedMessage: { headerMessageId: "sample-attached.eml@mime.sample", author: "Superman ", ccList: ["Jimmy "], subject: "Test message", attachments: 3, size: platformInfo.os == "win" ? 6947 : 6825, // Line endings. external: true, read: null, recipients: ["Heinz Müller "], date: 958606367000, body: "Die Hasen und die Frösche", }, }; let [{ displayedFolder, windowId: mainWindowId }] = await browser.mailTabs.query({ active: true, currentWindow: true, }); // Open an external file, either from file or via API. async function openAndVerifyExternalMessage( actionOrMessageId, location, expected ) { let tabPromise = window.waitForEvent("tabs.onCreated"); let messagePromise = window.waitForEvent( "messageDisplay.onMessageDisplayed" ); let returnedMsgTab; if (Number.isInteger(actionOrMessageId)) { returnedMsgTab = await browser.messageDisplay.open({ messageId: actionOrMessageId, location, }); } else { await window.sendMessage(actionOrMessageId, location); } let [msgTab] = await tabPromise; let [openedMsgTab, message] = await messagePromise; if ("windowId" in expected) { browser.test.assertEq( expected.windowId, msgTab.windowId, "The opened tab should belong to the correct window" ); } else { browser.test.assertTrue( msgTab.windowId != mainWindowId, "The opened tab should not belong to the main window" ); } browser.test.assertEq( msgTab.id, openedMsgTab.id, "The opened tab should match the onMessageDisplayed event tab" ); if (Number.isInteger(actionOrMessageId)) { browser.test.assertEq( msgTab.id, returnedMsgTab.id, "The returned tab should match the onMessageDisplayed event tab" ); } if ("messageId" in expected) { browser.test.assertEq( expected.messageId, message.id, "The message should have the same ID as it did previously" ); } // Test the received message and the re-queried message. for (let msg of [message, await browser.messages.get(message.id)]) { browser.test.assertEq( message.id, msg.id, "`The opened message should be correct." ); browser.test.assertEq( expected.author, msg.author, "The author should be correct" ); browser.test.assertEq( expected.headerMessageId, msg.headerMessageId, "The headerMessageId should be correct" ); browser.test.assertEq( expected.subject, msg.subject, "The subject should be correct" ); browser.test.assertEq( expected.size, msg.size, "The size should be correct" ); browser.test.assertEq( expected.external, msg.external, "The external flag should be correct" ); browser.test.assertEq( expected.date, msg.date.getTime(), "The date should be correct" ); window.assertDeepEqual( expected.recipients, msg.recipients, "The recipients should be correct" ); window.assertDeepEqual( expected.ccList, msg.ccList, "The carbon copy recipients should be correct" ); } let raw = await browser.messages.getRaw(message.id); browser.test.assertTrue( raw.startsWith(`Message-ID: <${expected.headerMessageId}>`), "Raw msg should be correct" ); let full = await browser.messages.getFull(message.id); browser.test.assertTrue( full.headers["message-id"].includes(`<${expected.headerMessageId}>`), "Message-ID of full msg should be correct" ); browser.test.assertTrue( full.parts[0].parts[0].body.includes(expected.body), "Body of full msg should be correct" ); let attachments = await browser.messages.listAttachments(message.id); browser.test.assertEq( expected.attachments, attachments.length, "Should find the correct number of attachments" ); await browser.tabs.remove(msgTab.id); return message; } // Check API operations on the given message. async function testMessageOperations(message) { // Test copying a file message into Thunderbird. let { messages: messagesBeforeCopy } = await browser.messages.list( displayedFolder ); await browser.messages.copy([message.id], displayedFolder); let { messages: messagesAfterCopy } = await browser.messages.list( displayedFolder ); browser.test.assertEq( messagesBeforeCopy.length + 1, messagesAfterCopy.length, "The file message should have been copied into the current folder" ); let { messages } = await browser.messages.query({ folder: displayedFolder, headerMessageId: message.headerMessageId, }); browser.test.assertTrue( messages.length == 1, "A query should find the new copied file message in the current folder" ); // All other operations should fail. await browser.test.assertRejects( browser.messages.update(message.id, {}), `Error updating message: Operation not permitted for external messages`, "Updating external messages should throw." ); await browser.test.assertRejects( browser.messages.delete([message.id]), `Error deleting message: Operation not permitted for external messages`, "Deleting external messages should throw." ); await browser.test.assertRejects( browser.messages.archive([message.id]), `Error archiving message: Operation not permitted for external messages`, "Archiving external messages should throw." ); await browser.test.assertRejects( browser.messages.move([message.id], displayedFolder), `Error moving message: Operation not permitted for external messages`, "Moving external messages should throw." ); return messages[0]; } // Open an external message in a tab and check its details. let externalMessage = await openAndVerifyExternalMessage( "openExternalFileMessage", "tab", { ...emlData.openExternalFileMessage, windowId: mainWindowId } ); // Open and check the same message in a window. await openAndVerifyExternalMessage("openExternalFileMessage", "window", { ...emlData.openExternalFileMessage, messageId: externalMessage.id, }); // Open and check the same message in a tab, using the API. await openAndVerifyExternalMessage(externalMessage.id, "tab", { ...emlData.openExternalFileMessage, messageId: externalMessage.id, windowId: mainWindowId, }); // Open and check the same message in a window, using the API. await openAndVerifyExternalMessage(externalMessage.id, "window", { ...emlData.openExternalFileMessage, messageId: externalMessage.id, }); // Test operations on the external message. This will put a copy in a // folder that we can use for the next step. let copiedMessage = await testMessageOperations(externalMessage); let messagePromise = window.waitForEvent( "messageDisplay.onMessageDisplayed" ); await browser.mailTabs.setSelectedMessages([copiedMessage.id]); await messagePromise; // Open an attached message in a tab and check its details. let attachedMessage = await openAndVerifyExternalMessage( "openExternalAttachedMessage", "tab", { ...emlData.openExternalAttachedMessage, windowId: mainWindowId } ); // Open and check the same message in a window. await openAndVerifyExternalMessage( "openExternalAttachedMessage", "window", { ...emlData.openExternalAttachedMessage, messageId: attachedMessage.id, } ); // Open and check the same message in a tab, using the API. await openAndVerifyExternalMessage(attachedMessage.id, "tab", { ...emlData.openExternalAttachedMessage, messageId: attachedMessage.id, windowId: mainWindowId, }); // Open and check the same message in a window, using the API. await openAndVerifyExternalMessage(attachedMessage.id, "window", { ...emlData.openExternalAttachedMessage, messageId: attachedMessage.id, }); // Test operations on the attached message. await testMessageOperations(attachedMessage); // Delete the local eml file to trigger access errors. await window.sendMessage(`deleteExternalMessage`); await browser.test.assertRejects( browser.messages.update(externalMessage.id, {}), `Error updating message: Message not found: ${externalMessage.id}.`, "Updating a missing message should throw." ); await browser.test.assertRejects( browser.messages.delete([externalMessage.id]), `Error deleting message: Message not found: ${externalMessage.id}.`, "Deleting a missing message should throw." ); await browser.test.assertRejects( browser.messages.archive([externalMessage.id]), `Error archiving message: Message not found: ${externalMessage.id}.`, "Archiving a missing message should throw." ); await browser.test.assertRejects( browser.messages.move([externalMessage.id], displayedFolder), `Error moving message: Message not found: ${externalMessage.id}.`, "Moving a missing message should throw." ); await browser.test.assertRejects( browser.messages.copy([externalMessage.id], displayedFolder), `Error copying message: Message not found: ${externalMessage.id}.`, "Copying a missing message should throw." ); await browser.test.assertRejects( browser.messageDisplay.open({ messageId: externalMessage.id }), `Unknown or invalid messageId: ${externalMessage.id}.`, "Opening a missing message should throw." ); browser.test.notifyPass("finished"); }, "utils.js": await getUtilsJS(), }; let extension = ExtensionTestUtils.loadExtension({ files, manifest: { background: { scripts: ["utils.js", "background.js"] }, permissions: [ "accountsRead", "messagesRead", "messagesMove", "messagesDelete", ], }, }); let tabmail = document.getElementById("tabmail"); let about3Pane = tabmail.currentAbout3Pane; about3Pane.displayFolder(gFolder.URI); about3Pane.threadTree.selectedIndex = 0; extension.onMessage("openExternalFileMessage", async location => { let messagePath = PathUtils.join( PathUtils.profileDir, "attachedMessageSample.eml" ); let messageFile = new FileUtils.File(messagePath); let url = Services.io .newFileURI(messageFile) .mutate() .setQuery("type=application/x-message-display") .finalize(); Services.prefs.setIntPref( "mail.openMessageBehavior", MailConsts.OpenMessageBehavior[ location == "window" ? "NEW_WINDOW" : "NEW_TAB" ] ); MailUtils.openEMLFile(window, messageFile, url); extension.sendMessage(); }); extension.onMessage("openExternalAttachedMessage", async location => { Services.prefs.setIntPref( "mail.openMessageBehavior", MailConsts.OpenMessageBehavior[ location == "window" ? "NEW_WINDOW" : "NEW_TAB" ] ); // The message with attachment should be loaded in the 3-pane tab. let aboutMessage = tabmail.currentAboutMessage; aboutMessage.toggleAttachmentList(true); EventUtils.synthesizeMouseAtCenter( aboutMessage.document.querySelector(".attachmentItem"), { clickCount: 2 }, aboutMessage ); extension.sendMessage(); }); extension.onMessage("deleteExternalMessage", async () => { let messagePath = PathUtils.join( PathUtils.profileDir, "attachedMessageSample.eml" ); let messageFile = new FileUtils.File(messagePath); messageFile.remove(false); extension.sendMessage(); }); await extension.startup(); await extension.awaitFinish("finished"); await extension.unload(); });