Bug 1732554 - Make GenericSendMessage async. r=mkmelin
Differential Revision:
https://phabricator.services.mozilla.com/D146620
--- a/mail/components/compose/content/MsgComposeCommands.js
+++ b/mail/components/compose/content/MsgComposeCommands.js
@@ -5488,17 +5488,17 @@ function SetComposeDetails(newValues) {
gContentChanged = true;
}
/**
* Handles message sending operations.
*
* @param {nsIMsgCompDeliverMode} mode - The delivery mode of the operation.
*/
-function GenericSendMessage(msgType) {
+async function GenericSendMessage(msgType) {
let msgCompFields = GetComposeDetails();
let subject = msgCompFields.subject;
// Some other msgCompFields have already been updated instantly in their
// respective toggle functions, e.g. ToggleReturnReceipt(), ToggleDSN(),
// ToggleAttachVCard(), and toggleAttachmentReminder().
let sending =
@@ -5716,32 +5716,43 @@ function GenericSendMessage(msgType) {
case Ci.nsIMsgCompSendFormat.Both:
msgCompFields.forcePlainText = false;
msgCompFields.useMultipartAlternative = true;
break;
default:
throw new Error(`Invalid send format ${sendFormat}`);
}
- let beforeSendEvent = new CustomEvent("beforesend", {
- cancelable: true,
- detail: msgType,
- });
- window.dispatchEvent(beforeSendEvent);
- if (beforeSendEvent.defaultPrevented) {
+ try {
+ await new Promise((resolve, reject) => {
+ let beforeSendEvent = new CustomEvent("beforesend", {
+ cancelable: true,
+ detail: {
+ resolve,
+ reject,
+ },
+ });
+ window.dispatchEvent(beforeSendEvent);
+ if (!beforeSendEvent.defaultPrevented) {
+ resolve();
+ }
+ });
+ } catch (ex) {
return;
}
}
- CompleteGenericSendMessage(msgType);
+ await CompleteGenericSendMessage(msgType);
}
/**
* Finishes message sending. This should ONLY be called directly from
- * GenericSendMessage, or if GenericSendMessage was interrupted by your code.
+ * GenericSendMessage. This is a separate function so that it can be easily mocked
+ * in tests.
+ *
* @param msgType nsIMsgCompDeliverMode of the operation.
*/
async function CompleteGenericSendMessage(msgType) {
// hook for extra compose pre-processing
Services.obs.notifyObservers(window, "mail:composeOnSend");
if (!gSelectedTechnologyIsPGP) {
gMsgCompose.compFields.composeSecure.requireEncryptMessage = gSendEncrypted;
@@ -6273,37 +6284,37 @@ async function checkEncryptedBccRecipien
ignoreButton.callback();
}
},
},
[ignoreButton]
);
}
-function SendMessage() {
+async function SendMessage() {
pillifyRecipients();
let sendInBackground = Services.prefs.getBoolPref(
"mailnews.sendInBackground"
);
if (sendInBackground && AppConstants.platform != "macosx") {
let count = [...Services.wm.getEnumerator(null)].length;
if (count == 1) {
sendInBackground = false;
}
}
- GenericSendMessage(
+ await GenericSendMessage(
sendInBackground
? Ci.nsIMsgCompDeliverMode.Background
: Ci.nsIMsgCompDeliverMode.Now
);
ExitFullscreenMode();
}
-function SendMessageWithCheck() {
+async function SendMessageWithCheck() {
pillifyRecipients();
var warn = Services.prefs.getBoolPref("mail.warn_on_send_accel_key");
if (warn) {
let bundle = getComposeBundle();
let checkValue = { value: false };
let buttonPressed = Services.prompt.confirmEx(
window,
@@ -6332,23 +6343,23 @@ function SendMessageWithCheck() {
let mode;
if (Services.io.offline) {
mode = Ci.nsIMsgCompDeliverMode.Later;
} else {
mode = sendInBackground
? Ci.nsIMsgCompDeliverMode.Background
: Ci.nsIMsgCompDeliverMode.Now;
}
- GenericSendMessage(mode);
+ await GenericSendMessage(mode);
ExitFullscreenMode();
}
-function SendMessageLater() {
+async function SendMessageLater() {
pillifyRecipients();
- GenericSendMessage(Ci.nsIMsgCompDeliverMode.Later);
+ await GenericSendMessage(Ci.nsIMsgCompDeliverMode.Later);
ExitFullscreenMode();
}
function ExitFullscreenMode() {
// On OS X we need to deliberately exit full screen mode after sending.
if (AppConstants.platform == "macosx") {
window.fullScreen = false;
}
@@ -6376,40 +6387,40 @@ function SaveAsFile(saveAs) {
if (gMsgCompose.bodyConvertible() == Ci.nsIMsgCompConvertible.Plain) {
SaveDocument(saveAs, false, "text/plain");
} else {
SaveDocument(saveAs, false, "text/html");
}
defaultSaveOperation = "file";
}
-function SaveAsDraft() {
+async function SaveAsDraft() {
gAutoSaveKickedIn = false;
gEditingDraft = true;
- GenericSendMessage(Ci.nsIMsgCompDeliverMode.SaveAsDraft);
+ await GenericSendMessage(Ci.nsIMsgCompDeliverMode.SaveAsDraft);
defaultSaveOperation = "draft";
}
-function SaveAsTemplate() {
+async function SaveAsTemplate() {
gAutoSaveKickedIn = false;
gEditingDraft = false;
let savedReferences = null;
if (gMsgCompose && gMsgCompose.compFields) {
// Clear References header. When we use the template, we don't want that
// header, yet, "edit as new message" maintains it. So we need to clear
// it when saving the template.
// Note: The In-Reply-To header is the last entry in the references header,
// so it will get cleared as well.
savedReferences = gMsgCompose.compFields.references;
gMsgCompose.compFields.references = null;
}
- GenericSendMessage(Ci.nsIMsgCompDeliverMode.SaveAsTemplate);
+ await GenericSendMessage(Ci.nsIMsgCompDeliverMode.SaveAsTemplate);
defaultSaveOperation = "template";
if (savedReferences) {
gMsgCompose.compFields.references = savedReferences;
}
}
// Sets the additional FCC, in addition to the default FCC.
@@ -7192,21 +7203,19 @@ function ComposeCanClose() {
// we close the window ourselves after the save is done.
gCloseWindowAfterSave = true;
// We catch the exception because we need to tell toolkit that it
// shouldn't close the window, because we're going to close it
// ourselves. If we don't tell toolkit that, and then close the window
// ourselves, the toolkit code that keeps track of the open windows
// gets off by one and the app can close unexpectedly on os's that
// shutdown the app when the last window is closed.
- try {
- GenericSendMessage(Ci.nsIMsgCompDeliverMode.AutoSaveAsDraft);
- } catch (ex) {
- Cu.reportError(ex);
- }
+ GenericSendMessage(Ci.nsIMsgCompDeliverMode.AutoSaveAsDraft).catch(
+ Cu.reportError
+ );
return false;
case 1: // Cancel
return false;
case 2: // Don't Save
// don't delete the draft if we didn't start off editing a draft
// and the user hasn't explicitly saved it.
if (!gEditingDraft && gAutoSaveKickedIn) {
RemoveDraft();
@@ -10045,24 +10054,24 @@ function loadHTMLMsgPrefs() {
gDefaultBackgroundColor = bgColor;
document
.getElementById("cmd_backgroundColor")
.setAttribute("state", bgColor);
onBackgroundColorChange();
}
}
-function AutoSave() {
+async function AutoSave() {
if (
gMsgCompose.editor &&
(gContentChanged || gMsgCompose.bodyModified) &&
!gSendOperationInProgress &&
!gSaveOperationInProgress
) {
- GenericSendMessage(Ci.nsIMsgCompDeliverMode.AutoSaveAsDraft);
+ await GenericSendMessage(Ci.nsIMsgCompDeliverMode.AutoSaveAsDraft);
gAutoSaveKickedIn = true;
}
gAutoSaveTimeout = setTimeout(AutoSave, gAutoSaveInterval);
}
/**
* Periodically check for keywords in the message.
--- a/mail/components/extensions/parent/ext-compose.js
+++ b/mail/components/extensions/parent/ext-compose.js
@@ -804,45 +804,48 @@ var composeEventTracker = {
this.listeners.delete(listener);
if (this.listeners.size == 0) {
windowTracker.removeListener("beforesend", this);
}
},
async handleEvent(event) {
event.preventDefault();
- let msgType = event.detail;
let composeWindow = event.target;
+ composeWindow.ToggleWindowLock(true);
- composeWindow.ToggleWindowLock(true);
+ // Send process waits till sendPromise.resolve() or sendPromise.reject() is
+ // called.
+ let sendPromise = event.detail;
for (let { handler, extension } of this.listeners) {
let result = await handler(
composeWindow,
await getComposeDetails(composeWindow, extension)
);
if (!result) {
continue;
}
if (result.cancel) {
composeWindow.ToggleWindowLock(false);
+ sendPromise.reject();
return;
}
if (result.details) {
await setComposeDetails(composeWindow, result.details, extension);
}
}
// Load the new details into gMsgCompose.compFields for sending.
composeWindow.GetComposeDetails();
// Calling getComposeDetails collapses mailing lists. Expand them again.
composeWindow.expandRecipients();
composeWindow.ToggleWindowLock(false);
- await composeWindow.CompleteGenericSendMessage(msgType);
+ sendPromise.resolve();
},
};
var composeAttachmentTracker = {
_nextId: 1,
_attachments: new Map(),
_attachmentIds: new Map(),