Bug 440794 Leverage Offline capabilities to make sending email appear faster - revise previous work so that background send won't affect patches that have been queued for send later. r/sr=bienvenu
authorMark Banner <bugzilla@standard8.plus.com>
Tue, 31 Mar 2009 09:59:07 +0100
changeset 2310 fd3d5ede674b5c3af87fd27793f67e3511ae45f9
parent 2309 cc6f581a45b12d64d1f052fa6c4747dbeaa451f5
child 2311 aadcf678e4561309933859be44ed0c4bd5185b99
push idunknown
push userunknown
push dateunknown
bugs440794
Bug 440794 Leverage Offline capabilities to make sending email appear faster - revise previous work so that background send won't affect patches that have been queued for send later. r/sr=bienvenu
mail/components/compose/content/MsgComposeCommands.js
mail/components/compose/content/messengercompose.xul
mailnews/compose/public/nsIMsgCompose.idl
mailnews/compose/public/nsIMsgSend.idl
mailnews/compose/resources/content/sendProgress.js
mailnews/compose/src/nsMsgCompUtils.cpp
mailnews/compose/src/nsMsgCompose.cpp
mailnews/compose/src/nsMsgCopy.cpp
mailnews/compose/src/nsMsgSend.cpp
mailnews/compose/src/nsMsgSend.h
mailnews/compose/src/nsMsgSendLater.cpp
mailnews/compose/src/nsMsgSendLater.h
mailnews/compose/test/unit/test_sendBackground.js
--- a/mail/components/compose/content/MsgComposeCommands.js
+++ b/mail/components/compose/content/MsgComposeCommands.js
@@ -1,9 +1,9 @@
-# -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+# -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 # ***** BEGIN LICENSE BLOCK *****
 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
 #
 # The contents of this file are subject to the Mozilla Public License Version
 # 1.1 (the "License"); you may not use this file except in compliance with
 # the License. You may obtain a copy of the License at
 # http://www.mozilla.org/MPL/
 #
@@ -80,17 +80,16 @@ var defaultSaveOperation;
 var gSendOrSaveOperationInProgress;
 var gCloseWindowAfterSave;
 var gIsOffline;
 var gSessionAdded;
 var gCurrentAutocompleteDirectory;
 var gSetupLdapAutocomplete;
 var gLDAPSession;
 var gSavedSendNowKey;
-var gSendInBackground;
 var gSendFormat;
 
 var gMsgIdentityElement;
 var gMsgAddressingWidgetTreeElement;
 var gMsgSubjectElement;
 var gMsgAttachmentElement;
 var gMsgHeadersToolbarElement;
 
@@ -138,20 +137,16 @@ function InitializeGlobalVariables()
   gCharsetConvertManager = Components.classes['@mozilla.org/charset-converter-manager;1'].getService(Components.interfaces.nsICharsetConverterManager);
   gMailSession = Components.classes["@mozilla.org/messenger/services/session;1"].getService(Components.interfaces.nsIMsgMailSession);
   gHideMenus = false;
 
   gLastWindowToHaveFocus = null;
   gReceiptOptionChanged = false;
   gDSNOptionChanged = false;
   gAttachVCardOptionChanged = false;
-
-  gSendInBackground = Components.classes["@mozilla.org/preferences-service;1"]
-                                .getService(Components.interfaces.nsIPrefBranch)
-                                .getBoolPref("mailnews.sendInBackground");
 }
 InitializeGlobalVariables();
 
 function ReleaseGlobalVariables()
 {
   gAccountManager = null;
   gIOService = null;
   gPromptService = null;
@@ -485,28 +480,23 @@ var defaultController =
       case "cmd_close"              : DoCommandClose();       break;
       case "cmd_saveDefault"        : Save();                 break;
       case "cmd_saveAsFile"         : SaveAsFile(true);       break;
       case "cmd_saveAsDraft"        : SaveAsDraft();          break;
       case "cmd_saveAsTemplate"     : SaveAsTemplate();       break;
       case "cmd_sendButton"         :
         if (defaultController.isCommandEnabled(command))
         {
-          if ((gIOService && gIOService.offline) || gSendInBackground)
+          if (gIOService && gIOService.offline)
             SendMessageLater();
           else
             SendMessage();
         }
         break;
-      case "cmd_sendNow"            :
-        if (defaultController.isCommandEnabled(command))
-        {
-          gSendInBackground ? SendMessageLater() : SendMessage();
-          break;
-        }
+      case "cmd_sendNow"            : if (defaultController.isCommandEnabled(command)) SendMessage();          break;
       case "cmd_sendWithCheck"   : if (defaultController.isCommandEnabled(command)) SendMessageWithCheck();          break;
       case "cmd_sendLater"          : if (defaultController.isCommandEnabled(command)) SendMessageLater();     break;
       case "cmd_printSetup"         : PrintUtils.showPageSetup(); break;
       case "cmd_print"              : DoCommandPrint(); break;
 
       //Edit Menu
       case "cmd_delete"             : if (MessageGetNumSelectedAttachments()) RemoveSelectedAttachment();         break;
       case "cmd_renameAttachment"   : if (MessageGetNumSelectedAttachments() == 1) RenameSelectedAttachment(); break;
@@ -680,63 +670,53 @@ var messageComposeOfflineObserver =
 function AddMessageComposeOfflineObserver()
 {
   var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
   observerService.addObserver(messageComposeOfflineObserver, "network:offline-status-changed", false);
   
   gIsOffline = gIOService.offline;
   // set the initial state of the send button
   MessageComposeOfflineStateChanged(gIsOffline);
-
-  let sendNowMenuItem = document.getElementById("menu-item-send-now");
-  let sendLaterMenuItem = document.getElementById("menu-item-send-later");
-
-  // XXX Send in background is still WIP (bug 440794) whilst that is the case
-  // we need to check for it.
-  if (gSendInBackground) {
-    // We need to send-later, but show everything as "send"
-    sendNowMenuItem.setAttribute("command", "cmd_sendLater");
-    sendNowMenuItem.setAttribute("key", "key_sendLater");
-    sendLaterMenuItem.hidden = true;
-  }
-  // Else leave as default.
 }
 
 function RemoveMessageComposeOfflineObserver()
 {
   var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
   observerService.removeObserver(messageComposeOfflineObserver,"network:offline-status-changed");
 }
 
 function MessageComposeOfflineStateChanged(goingOffline)
 {
-  let sendButton = document.getElementById("button-send");
-  let sendNowMenuItem = document.getElementById("menu-item-send-now");
-
-  if (!gSavedSendNowKey) {
-    gSavedSendNowKey = sendNowMenuItem.getAttribute('key');
-  }
-
-  // Don't use goUpdateCommand here ... the defaultController might not be
-  // installed yet.
-  goSetCommandEnabled("cmd_sendNow", defaultController.isCommandEnabled("cmd_sendNow"));
-
-  if (goingOffline)
-  {
-    sendButton.label = sendButton.getAttribute('later_label');
-    sendButton.setAttribute('tooltiptext', sendButton.getAttribute('later_tooltiptext'));
-    sendNowMenuItem.removeAttribute('key');
-  }
-  else
-  {
-    sendButton.label = sendButton.getAttribute('now_label');
-    sendButton.setAttribute('tooltiptext', sendButton.getAttribute('now_tooltiptext'));
-    if (gSavedSendNowKey)
-      sendNowMenuItem.setAttribute('key', gSavedSendNowKey);
-  }
+  try {
+    var sendButton = document.getElementById("button-send");
+    var sendNowMenuItem = document.getElementById("menu-item-send-now");
+
+    if (!gSavedSendNowKey) {
+      gSavedSendNowKey = sendNowMenuItem.getAttribute('key');
+    }
+
+    // don't use goUpdateCommand here ... the defaultController might not be installed yet
+    goSetCommandEnabled("cmd_sendNow", defaultController.isCommandEnabled("cmd_sendNow"));
+
+    if (goingOffline)
+    {
+      sendButton.label = sendButton.getAttribute('later_label');
+      sendButton.setAttribute('tooltiptext', sendButton.getAttribute('later_tooltiptext'));
+      sendNowMenuItem.removeAttribute('key');
+    }
+    else
+    {
+      sendButton.label = sendButton.getAttribute('now_label');
+      sendButton.setAttribute('tooltiptext', sendButton.getAttribute('now_tooltiptext'));
+      if (gSavedSendNowKey) {
+        sendNowMenuItem.setAttribute('key', gSavedSendNowKey);
+      }
+    }
+
+  } catch(e) {}
 }
 
 var directoryServerObserver = {
   observe: function(subject, topic, value) {
       try {
           setupLdapAutocompleteSession();
       } catch (ex) {
           // catch the exception and ignore it, so that if LDAP setup 
@@ -1518,21 +1498,16 @@ function ComposeLoad()
   toolbox.customizeDone = function(aEvent) { MailToolboxCustomizeDone(aEvent, "CustomizeComposeToolbar"); };
 
   var toolbarset = document.getElementById('customToolbars');
   toolbox.toolbarset = toolbarset;
 
   // Prevent resizing the subject and format toolbar over the addressswidget.
   var headerToolbar = document.getElementById("MsgHeadersToolbar");
   headerToolbar.minHeight = headerToolbar.boxObject.height;
-
-  // We don't do send in background by default yet, however if the pref is
-  // set (for testing), set up the UI so that we'll effectively send later
-  // anyway.
-  
 }
 
 function ComposeUnload()
 {
   UnloadCommandUpdateHandlers();
 
   // Stop InlineSpellCheckerUI so personal dictionary is saved
   enableInlineSpellCheck(false);
@@ -1655,17 +1630,19 @@ function GenericSendMessage( msgType )
     var msgCompFields = gMsgCompose.compFields;
     if (msgCompFields)
     {
       Recipients2CompFields(msgCompFields);
       var subject = GetMsgSubjectElement().value;
       msgCompFields.subject = subject;
       Attachments2CompFields(msgCompFields);
 
-      if (msgType == nsIMsgCompDeliverMode.Now || msgType == nsIMsgCompDeliverMode.Later)
+      if (msgType == nsIMsgCompDeliverMode.Now ||
+          msgType == nsIMsgCompDeliverMode.Later ||
+          msgType == nsIMsgCompDeliverMode.Background)
       {
         //Do we need to check the spelling?
         if (getPref("mail.SpellCheckBeforeSend"))
         {
           // We disable spellcheck for the following -subject line, attachment pane, identity and addressing widget
           // therefore we need to explicitly focus on the mail body when we have to do a spellcheck.
           SetMsgBodyFrameFocus();
           window.cancelSendMessage = false;
@@ -1794,16 +1771,17 @@ function GenericSendMessage( msgType )
       // hook for extra compose pre-processing
       var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
       observerService.notifyObservers(window, "mail:composeOnSend", null);
 
       var originalCharset = gMsgCompose.compFields.characterSet;
       // Check if the headers of composing mail can be converted to a mail charset.
       if (msgType == nsIMsgCompDeliverMode.Now || 
         msgType == nsIMsgCompDeliverMode.Later ||
+        msgType == nsIMsgCompDeliverMode.Background ||
         msgType == nsIMsgCompDeliverMode.Save || 
         msgType == nsIMsgCompDeliverMode.SaveAsDraft || 
         msgType == nsIMsgCompDeliverMode.AutoSaveAsDraft || 
         msgType == nsIMsgCompDeliverMode.SaveAsTemplate) 
       {
         var fallbackCharset = new Object;
         // Check encoding, switch to UTF-8 if the default encoding doesn't fit
         // and disable_fallback_to_utf8 isn't set for this encoding.
@@ -1895,17 +1873,24 @@ function CheckValidEmailAddress(to, cc, 
       gPromptService.alert(window, errorTitle, errorMsg);
     return false;
   } 
   return true;
 }
 
 function SendMessage()
 {
-  GenericSendMessage(nsIMsgCompDeliverMode.Now);
+  let sendInBackground =
+    Components.classes["@mozilla.org/preferences-service;1"]
+              .getService(Components.interfaces.nsIPrefBranch)
+              .getBoolPref("mailnews.sendInBackground");
+
+  GenericSendMessage(sendInBackground ?
+                     nsIMsgCompDeliverMode.Background :
+                     nsIMsgCompDeliverMode.Now);
 }
 
 function SendMessageWithCheck()
 {
     var warn = getPref("mail.warn_on_send_accel_key");
 
     if (warn) {
         var checkValue = {value:false};
@@ -1925,19 +1910,25 @@ function SendMessageWithCheck()
         if (checkValue.value) {
             var branch = Components.classes["@mozilla.org/preferences-service;1"]
                                    .getService(Components.interfaces.nsIPrefBranch);
 
             branch.setBoolPref("mail.warn_on_send_accel_key", false);
         }
     }
 
-  GenericSendMessage(gIsOffline || gSendInBackground ?
-                     nsIMsgCompDeliverMode.Later :
-                     nsIMsgCompDeliverMode.Now);
+  let sendInBackground =
+    Components.classes["@mozilla.org/preferences-service;1"]
+              .getService(Components.interfaces.nsIPrefBranch)
+              .getBoolPref("mailnews.sendInBackground");
+
+  GenericSendMessage(gIsOffline ? nsIMsgCompDeliverMode.Later :
+                     (sendInBackground ?
+                      nsIMsgCompDeliverMode.Background :
+                      nsIMsgCompDeliverMode.Now));
 }
 
 function SendMessageLater()
 {
   GenericSendMessage(nsIMsgCompDeliverMode.Later);
 }
 
 function Save()
--- a/mail/components/compose/content/messengercompose.xul
+++ b/mail/components/compose/content/messengercompose.xul
@@ -343,17 +343,17 @@
                         command="cmd_saveAsFile"/>
               <menuseparator/>
               <menuitem label="&saveAsDraftCmd.label;" accesskey="&saveAsDraftCmd.accesskey;" command="cmd_saveAsDraft"/>
               <menuitem label="&saveAsTemplateCmd.label;" accesskey="&saveAsTemplateCmd.accesskey;" command="cmd_saveAsTemplate"/>
             </menupopup>
           </menu>
           <menuseparator/>
           <menuitem label="&sendNowCmd.label;" accesskey="&sendNowCmd.accesskey;" key="key_send" command="cmd_sendNow" id="menu-item-send-now"/>
-          <menuitem label="&sendLaterCmd.label;" accesskey="&sendLaterCmd.accesskey;" key="key_sendLater" command="cmd_sendLater" id="menu-item-send-later"/>
+          <menuitem label="&sendLaterCmd.label;" accesskey="&sendLaterCmd.accesskey;" key="key_sendLater" command="cmd_sendLater"/>
           <menuseparator/>
           <menuitem id="printSetupMenuItem" label="&printSetupCmd.label;" accesskey="&printSetupCmd.accesskey;" command="cmd_printSetup"/>
           <menuitem id="printMenuItem" label="&printCmd.label;" accesskey="&printCmd.accesskey;" key="key_print" command="cmd_print"/>
           <menuseparator id="menu_FileCloseSeparator"/>
           <menuitem id="menu_close"
                     label="&closeCmd.label;"
                     key="key_close"
                     accesskey="&closeCmd.accesskey;"
--- a/mailnews/compose/public/nsIMsgCompose.idl
+++ b/mailnews/compose/public/nsIMsgCompose.idl
@@ -70,16 +70,17 @@ interface nsIMsgCompDeliverMode {
     const long Now = 0;
     const long Later = 1;
     const long Save = 2;
     const long SaveAs = 3;
     const long SaveAsDraft = 4;
     const long SaveAsTemplate = 5;
     const long SendUnsent = 6;
     const long AutoSaveAsDraft = 7;
+    const long Background = 8;
 };
 
 [scriptable, uuid(f38ea280-e090-11d3-a449-e3153319347c)]
 interface nsIMsgCompSendFormat {
     const long AskUser = 4;     /* Hack: Bug 44512. If this is 0 and passed
                                    as results.action to the askSendFormat
                                    dialog, the args object gets destroyed.*/
     const long PlainText = 1;
--- a/mailnews/compose/public/nsIMsgSend.idl
+++ b/mailnews/compose/public/nsIMsgSend.idl
@@ -156,36 +156,44 @@ typedef struct nsMsgAttachedFile
 } nsMsgAttachedFile;
 %}
 
 
 [ptr] native nsMsgAttachmentData(nsMsgAttachmentData);
 [ptr] native nsMsgAttachedFile(nsMsgAttachedFile);
 [ptr] native nsMsgAttachmentHandler(nsMsgAttachmentHandler);
 
-[scriptable, uuid(435AD670-33E0-4759-96C5-772B6B62276B)]
+[scriptable, uuid(1313d4e4-3ac4-4fe7-bae5-e7049018c331)]
 interface nsIMsgSend : nsISupports 
 {
   //
   // This is the primary interface for creating and sending RFC822 messages
   // in the new architecture. Currently, this method supports many arguments
   // that change the behavior of the operation. This will change in time to 
   // be separate calls that will be more singluar in nature.
   //
   // NOTE: when aEditor is non-null, a multipart related MHTML message will 
   // be created
   //
-    
-    const nsMsgDeliverMode nsMsgDeliverNow = 0;
-    const nsMsgDeliverMode nsMsgQueueForLater = 1;
-    const nsMsgDeliverMode nsMsgSave = 2;
-    const nsMsgDeliverMode nsMsgSaveAs = 3;
-    const nsMsgDeliverMode nsMsgSaveAsDraft = 4;
-    const nsMsgDeliverMode nsMsgSaveAsTemplate = 5;
-    const nsMsgDeliverMode nsMsgSendUnsent = 6;
+
+  /// Send the message straight away.
+  const nsMsgDeliverMode nsMsgDeliverNow = 0;
+  /**
+   * Queue the message for sending later, but then wait for the user to
+   * request to send it.
+   */
+  const nsMsgDeliverMode nsMsgQueueForLater = 1;
+  const nsMsgDeliverMode nsMsgSave = 2;
+  const nsMsgDeliverMode nsMsgSaveAs = 3;
+  const nsMsgDeliverMode nsMsgSaveAsDraft = 4;
+  const nsMsgDeliverMode nsMsgSaveAsTemplate = 5;
+  const nsMsgDeliverMode nsMsgSendUnsent = 6;
+
+  /// Queue the message in the unsent folder and send it in the background.
+  const nsMsgDeliverMode nsMsgDeliverBackground = 8;
 
     [noscript]
     void createAndSendMessage(in nsIEditor                    aEditor,
                               in nsIMsgIdentity               aUserIdentity,
                               in string                       aAccountKey,
                               in nsIMsgCompFields             fields,
                               in PRBool                       digest_p,
                               in PRBool                       dont_deliver_p,
--- a/mailnews/compose/resources/content/sendProgress.js
+++ b/mailnews/compose/resources/content/sendProgress.js
@@ -35,17 +35,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-var msgCompDeliverMode = Components.interfaces.nsIMsgCompDeliverMode;
+var nsIMsgCompDeliverMode = Components.interfaces.nsIMsgCompDeliverMode;
 
 // dialog is just an array we'll use to store various properties from the dialog document...
 var dialog;
 
 // the msgProgress is a nsIMsgProgress object
 var msgProgress = null; 
 
 // random global variables...
@@ -144,17 +144,17 @@ function onLoad()
     var subject = "";
     gSendProgressStringBundle = document.getElementById("sendProgressStringBundle");
     msgProgress = window.arguments[0];
     if (window.arguments[1])
     {
       var progressParams = window.arguments[1].QueryInterface(Components.interfaces.nsIMsgComposeProgressParams)
       if (progressParams)
       {
-        itsASaveOperation = (progressParams.deliveryMode != msgCompDeliverMode.Now);
+        itsASaveOperation = (progressParams.deliveryMode != nsIMsgCompDeliverMode.Now);
         subject = progressParams.subject;
       }
     }
 
     if (!msgProgress)
     {
       dump("Invalid argument to sendProgress.xul\n");
       window.close()
--- a/mailnews/compose/src/nsMsgCompUtils.cpp
+++ b/mailnews/compose/src/nsMsgCompUtils.cpp
@@ -289,19 +289,20 @@ mime_generate_headers (nsMsgCompFields *
     *status = rv;
     return nsnull;
   }
 
   PRBool usemime = nsMsgMIMEGetConformToStandard();
   PRInt32 size = 0;
   char *buffer = 0, *buffer_tail = 0;
   PRBool isDraft =
-  deliver_mode == nsIMsgSend::nsMsgSaveAsDraft ||
-  deliver_mode == nsIMsgSend::nsMsgSaveAsTemplate ||
-  deliver_mode == nsIMsgSend::nsMsgQueueForLater;
+    deliver_mode == nsIMsgSend::nsMsgSaveAsDraft ||
+    deliver_mode == nsIMsgSend::nsMsgSaveAsTemplate ||
+    deliver_mode == nsIMsgSend::nsMsgQueueForLater ||
+    deliver_mode == nsIMsgSend::nsMsgDeliverBackground;
 
   const char* pFrom;
   const char* pTo;
   const char* pCc;
   const char* pMessageID;
   const char* pReplyTo;
   const char* pOrg;
   const char* pNewsGrp;
@@ -1921,17 +1922,19 @@ mime_gen_content_id(PRUint32 aPartNum, c
 }
 
 void
 GetFolderURIFromUserPrefs(nsMsgDeliverMode aMode, nsIMsgIdentity* identity, nsCString& uri)
 {
   nsresult rv;
   uri.Truncate();
 
-  if (aMode == nsIMsgSend::nsMsgQueueForLater)       // QueueForLater (Outbox)
+  // QueueForLater (Outbox)
+  if (aMode == nsIMsgSend::nsMsgQueueForLater ||
+      aMode == nsIMsgSend::nsMsgDeliverBackground)
   {
     nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
     if (NS_FAILED(rv))
       return;
     rv = prefs->GetCharPref("mail.default_sendlater_uri", getter_Copies(uri));
     if (NS_FAILED(rv) || uri.IsEmpty())
       uri.AssignLiteral(ANY_SERVER);
     else
--- a/mailnews/compose/src/nsMsgCompose.cpp
+++ b/mailnews/compose/src/nsMsgCompose.cpp
@@ -920,18 +920,22 @@ NS_IMETHODIMP nsMsgCompose::RemoveMsgSen
   return mExternalSendListeners.RemoveElement(aMsgSendListener) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult nsMsgCompose::_SendMsg(MSG_DeliverMode deliverMode, nsIMsgIdentity *identity, 
                                 const char *accountKey, PRBool entityConversionDone)
 {
   nsresult rv = NS_OK;
 
+  printf("deliver mode: %d\n", deliverMode);
+
   // clear saved message id if sending, so we don't send out the same message-id.
-  if (deliverMode == nsIMsgCompDeliverMode::Now || deliverMode == nsIMsgCompDeliverMode::Later)
+  if (deliverMode == nsIMsgCompDeliverMode::Now ||
+      deliverMode == nsIMsgCompDeliverMode::Later ||
+      deliverMode == nsIMsgCompDeliverMode::Background)
     m_compFields->SetMessageId("");
 
   if (m_compFields && identity)
   {
     // Pref values are supposed to be stored as UTF-8, so no conversion
     nsCString email;
     nsString fullName;
     nsString organization;
@@ -1170,17 +1174,20 @@ NS_IMETHODIMP nsMsgCompose::SendMsg(MSG_
 
     mProgress->OnStateChange(nsnull, nsnull, nsIWebProgressListener::STATE_START, NS_OK);
   }
 
   PRBool attachVCard = PR_FALSE;
   if (m_compFields)
       m_compFields->GetAttachVCard(&attachVCard);
 
-  if (attachVCard && identity && (deliverMode == nsIMsgCompDeliverMode::Now || deliverMode == nsIMsgCompDeliverMode::Later))
+  if (attachVCard && identity &&
+      (deliverMode == nsIMsgCompDeliverMode::Now ||
+       deliverMode == nsIMsgCompDeliverMode::Later ||
+       deliverMode == nsIMsgCompDeliverMode::Background))
   {
       nsCString escapedVCard;
       // make sure, if there is no card, this returns an empty string, or NS_ERROR_FAILURE
       rv = identity->GetEscapedVCard(escapedVCard);
 
       if (NS_SUCCEEDED(rv) && !escapedVCard.IsEmpty())
       {
           nsCString vCardUrl;
@@ -3452,16 +3459,17 @@ nsMsgComposeSendListener::OnProgress(PRU
 nsresult
 nsMsgComposeSendListener::OnStopCopy(nsresult aStatus)
 {
   nsresult rv = NS_OK;
   nsCOMPtr<nsIMsgCompose> msgCompose = do_QueryReferent(mWeakComposeObj, &rv);
   if (msgCompose)
   {
     if (mDeliverMode == nsIMsgSend::nsMsgQueueForLater ||
+        mDeliverMode == nsIMsgSend::nsMsgDeliverBackground ||
         mDeliverMode == nsIMsgSend::nsMsgSaveAsDraft)
     {
       msgCompose->RememberQueuedDisposition();
     }
 
     // Ok, if we are here, we are done with the send/copy operation so
     // we have to do something with the window....SHOW if failed, Close
     // if succeeded
@@ -3490,17 +3498,18 @@ nsMsgComposeSendListener::OnStopCopy(nsr
         msgCompose->NotifyStateListeners(nsIMsgComposeNotificationType::SaveInFolderDone, aStatus);
         // Remove the current draft msg when saving as draft/template is done.
         msgCompose->SetDeleteDraft(PR_TRUE);
         RemoveCurrentDraftMessage(msgCompose, PR_TRUE);
       }
       else
       {
         // Remove (possible) draft if we're in send later mode
-        if (mDeliverMode == nsIMsgSend::nsMsgQueueForLater) 
+        if (mDeliverMode == nsIMsgSend::nsMsgQueueForLater ||
+            mDeliverMode == nsIMsgSend::nsMsgDeliverBackground)
         {
           msgCompose->SetDeleteDraft(PR_TRUE);
           RemoveCurrentDraftMessage(msgCompose, PR_TRUE);
         }
         msgCompose->CloseWindow(PR_TRUE);
       }
     }
 #ifdef NS_DEBUG
--- a/mailnews/compose/src/nsMsgCopy.cpp
+++ b/mailnews/compose/src/nsMsgCopy.cpp
@@ -202,17 +202,20 @@ nsMsgCopy::StartCopyOperation(nsIMsgIden
 
   // Store away the server location...
   if (aSavePref)
     mSavePref = PL_strdup(aSavePref);
 
   //
   // Vars for implementation...
   //
-  if (aMode == nsIMsgSend::nsMsgQueueForLater)       // QueueForLater (Outbox)
+
+  // QueueForLater (Outbox)
+  if (aMode == nsIMsgSend::nsMsgQueueForLater ||
+      aMode == nsIMsgSend::nsMsgDeliverBackground)
   {
     rv = GetUnsentMessagesFolder(aUserIdentity, getter_AddRefs(dstFolder), &waitForUrl);
     isDraft = PR_FALSE;
     if (!dstFolder || NS_FAILED(rv)) {
       return NS_MSG_UNABLE_TO_SEND_LATER;
     }
   }
   else if (aMode == nsIMsgSend::nsMsgSaveAsDraft)    // SaveAsDraft (Drafts)
@@ -499,18 +502,20 @@ LocateMessageFolder(nsIMsgIdentity   *us
 
       nsCOMPtr<nsIMsgFolder> rootFolder;
       rv = inServer->GetRootFolder(getter_AddRefs(rootFolder));
 
       if(NS_FAILED(rv) || (!rootFolder))
         continue;
 
       // use the defaults by getting the folder by flags
-      if (aFolderType == nsIMsgSend::nsMsgQueueForLater)       // QueueForLater (Outbox)
+      if (aFolderType == nsIMsgSend::nsMsgQueueForLater ||
+          aFolderType == nsIMsgSend::nsMsgDeliverBackground)
       {
+        // QueueForLater (Outbox)
         rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Queue, msgFolder);
       }
       else if (aFolderType == nsIMsgSend::nsMsgSaveAsDraft)    // SaveAsDraft (Drafts)
       {
         rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Drafts, msgFolder);
       }
       else if (aFolderType == nsIMsgSend::nsMsgSaveAsTemplate) // SaveAsTemplate (Templates)
       {
--- a/mailnews/compose/src/nsMsgSend.cpp
+++ b/mailnews/compose/src/nsMsgSend.cpp
@@ -3583,22 +3583,21 @@ nsMsgComposeAndSend::DeliverMessage()
       return NS_ERROR_ABORT;
   }
 
   PRBool mail_p = ((mCompFields->GetTo() && *mCompFields->GetTo()) ||
           (mCompFields->GetCc() && *mCompFields->GetCc()) ||
           (mCompFields->GetBcc() && *mCompFields->GetBcc()));
   PRBool news_p = mCompFields->GetNewsgroups() && *(mCompFields->GetNewsgroups());
   NS_ASSERTION(!( m_deliver_mode != nsMsgSaveAsDraft && m_deliver_mode != nsMsgSaveAsTemplate)  || (mail_p || news_p), "message without destination");
-  if (m_deliver_mode == nsMsgQueueForLater)
-    return QueueForLater();
-  else if (m_deliver_mode == nsMsgSaveAsDraft)
-    return SaveAsDraft();
-  else if (m_deliver_mode == nsMsgSaveAsTemplate)
-    return SaveAsTemplate();
+  if (m_deliver_mode == nsMsgQueueForLater ||
+      m_deliver_mode == nsMsgDeliverBackground ||
+      m_deliver_mode == nsMsgSaveAsDraft ||
+      m_deliver_mode == nsMsgSaveAsTemplate)
+    return SendToMagicFolder(m_deliver_mode);
 
   //
   // Ok, we are about to send the file that we have built up...but what
   // if this is a mongo email...we should have a way to warn the user that
   // they are about to do something they may not want to do.
   //
   PRInt64 fileSize;
   nsresult rv = mTempFile->GetFileSize(&fileSize);
@@ -4475,52 +4474,16 @@ nsMsgComposeAndSend::SendToMagicFolder(n
     // The caller of MimeDoFCC needs to deal with failure.
     //
     if (NS_FAILED(rv))
       rv = NotifyListenerOnStopCopy(rv);
 
     return rv;
 }
 
-//
-// Queues the message for later delivery, and runs the completion/failure
-// callback.
-//
-nsresult
-nsMsgComposeAndSend::QueueForLater()
-{
-  return SendToMagicFolder(nsMsgQueueForLater);
-}
-
-//
-// Save the message to the Drafts folder, and runs the completion/failure
-// callback.
-//
-nsresult
-nsMsgComposeAndSend::SaveAsDraft()
-{
-  return SendToMagicFolder(nsMsgSaveAsDraft);
-}
-
-//
-// Save the message to the Template folder, and runs the completion/failure
-// callback.
-//
-nsresult
-nsMsgComposeAndSend::SaveAsTemplate()
-{
-  return SendToMagicFolder(nsMsgSaveAsTemplate);
-}
-
-nsresult
-nsMsgComposeAndSend::SaveInSentFolder()
-{
-  return SendToMagicFolder(nsMsgDeliverNow);
-}
-
 char*
 nsMsgGetEnvelopeLine(void)
 {
   static char       result[75] = "";
   PRExplodedTime    now;
   char              buffer[128] = "";
 
   // Generate envelope line in format of:  From - Sat Apr 18 20:01:49 1998
@@ -4706,28 +4669,31 @@ nsMsgComposeAndSend::MimeDoFCC(nsIFile  
   // is set.
   //
   // For FCC files, we don't necessarily need one, but we might as well put
   // one in so that it's marked as read already.
   //
   //
   // Need to add these lines for POP3 ONLY! IMAP servers will handle
   // this status information for summary file regeneration for us.
-  if ( (mode == nsMsgQueueForLater  || mode == nsMsgSaveAsDraft ||
-        mode == nsMsgSaveAsTemplate || mode == nsMsgDeliverNow ||
-        mode == nsMsgSendUnsent) && folderIsLocal)
+  if ((mode == nsMsgQueueForLater || mode == nsMsgSaveAsDraft ||
+       mode == nsMsgSaveAsTemplate || mode == nsMsgDeliverNow ||
+       mode == nsMsgSendUnsent || mode == nsMsgDeliverBackground) &&
+      folderIsLocal)
   {
     char       *buf = 0;
     PRUint16   flags = 0;
 
     // for save as draft and send later, we want to leave the message as unread.
     // See Bug #198087
+    // Messages sent with mode nsMsgDeliverBackground must not have the Queued
+    // flag sent so that they get picked up by the background send function.
     if (mode == nsMsgQueueForLater)
       flags |= nsMsgMessageFlags::Queued;
-    else if (mode != nsMsgSaveAsDraft)
+    else if (mode != nsMsgSaveAsDraft && mode != nsMsgDeliverBackground)
       flags |= nsMsgMessageFlags::Read;
     buf = PR_smprintf(X_MOZILLA_STATUS_FORMAT CRLF, flags);
     if (buf)
     {
       PRUint32   len = PL_strlen(buf);
       rv = tempOutfile->Write(buf, len, &n);
       PR_Free(buf);
       if (NS_FAILED(rv) || n != len)
@@ -4781,18 +4747,19 @@ nsMsgComposeAndSend::MimeDoFCC(nsIFile  
   // information, and should perhaps not be stored at all.
   //
   // Thus the consultation of the #define SAVE_BCC_IN_FCC_FILE.
   //
   // (Note that, if there is a BCC header present in a message in some random
   // folder, and that message is forwarded to someone, then the attachment
   // code will strip out the BCC header before forwarding it.)
   //
-  if ((mode == nsMsgQueueForLater || mode == nsMsgSaveAsDraft ||
-       mode == nsMsgSaveAsTemplate) && fcc_header && *fcc_header)
+  if ((mode == nsMsgQueueForLater || mode == nsMsgDeliverBackground ||
+       mode == nsMsgSaveAsDraft || mode == nsMsgSaveAsTemplate) &&
+      fcc_header && *fcc_header)
   {
     PRInt32 L = PL_strlen(fcc_header) + 20;
     char  *buf = (char *) PR_Malloc (L);
     if (!buf)
     {
       status = NS_ERROR_OUT_OF_MEMORY;
       goto FAIL;
     }
@@ -4807,22 +4774,19 @@ nsMsgComposeAndSend::MimeDoFCC(nsIFile  
       goto FAIL;
     }
   }
 
   //
   // Ok, now I want to get the identity key and write it out if this is for a
   // nsMsgQueueForLater operation!
   //
-  if (  (  ( nsMsgQueueForLater == mode )
-        || ( nsMsgSaveAsDraft == mode )
-        || ( nsMsgSaveAsTemplate == mode )
-        )
-     && ( mUserIdentity )
-     )
+  if ((nsMsgQueueForLater == mode || nsMsgSaveAsDraft == mode ||
+       nsMsgDeliverBackground == mode || nsMsgSaveAsTemplate == mode) &&
+      mUserIdentity)
   {
     char *buf = nsnull;
     nsCString key;
 
     if (NS_SUCCEEDED(mUserIdentity->GetKey(key)) && !key.IsEmpty())
     {
       buf = PR_smprintf(HEADER_X_MOZILLA_IDENTITY_KEY ": %s" CRLF, key.get());
       if (buf)
@@ -4892,17 +4856,18 @@ nsMsgComposeAndSend::MimeDoFCC(nsIFile  
   // This is done only when writing to the queue file, not the FCC file.
   // We need this to complement the "Newsgroups" header for the case of
   // queueing a message for a non-default news host.
   //
   // Convert a URL like "snews://host:123/" to the form "host:123/secure"
   // or "news://user@host:222" to simply "host:222".
   //
   if ((mode == nsMsgQueueForLater || mode == nsMsgSaveAsDraft ||
-       mode == nsMsgSaveAsTemplate) && news_url && *news_url)
+       mode == nsMsgSaveAsTemplate || mode == nsMsgDeliverBackground) &&
+      news_url && *news_url)
   {
     PRBool secure_p = (news_url[0] == 's' || news_url[0] == 'S');
     char *orig_hap = nsMsgParseURLHost (news_url);
     char *host_and_port = orig_hap;
     if (host_and_port)
     {
       // There may be authinfo at the front of the host - it could be of
       // the form "user:password@host:port", so take off everything before
--- a/mailnews/compose/src/nsMsgSend.h
+++ b/mailnews/compose/src/nsMsgSend.h
@@ -216,21 +216,17 @@ public:
 
   nsresult    DoFcc();
   nsresult    StartMessageCopyOperation(nsIFile          *aFileSpec,
                                         nsMsgDeliverMode mode,
                                         const nsCString& dest_uri);
 
   void        Clear();
 
-  NS_METHOD   SendToMagicFolder (nsMsgDeliverMode flag);
-  nsresult    QueueForLater();
-  nsresult    SaveAsDraft();
-  nsresult    SaveInSentFolder();
-  nsresult    SaveAsTemplate();
+  nsresult SendToMagicFolder(nsMsgDeliverMode flag);
 
   // Check to see if it's ok to save msgs to the configured folder.
   PRBool CanSaveMessagesToFolder(const char *folderURL);
 
   //
   // FCC operations...
   //
   nsresult    MimeDoFCC (nsIFile *input_file,
--- a/mailnews/compose/src/nsMsgSendLater.cpp
+++ b/mailnews/compose/src/nsMsgSendLater.cpp
@@ -148,17 +148,18 @@ nsMsgSendLater::Init()
 }
 
 NS_IMETHODIMP
 nsMsgSendLater::Observe(nsISupports *aSubject, const char* aTopic,
                         const PRUnichar *aData)
 {
   if (aSubject == mTimer && !strcmp(aTopic, "timer-callback"))
   {
-    SendUnsentMessages(nsnull);
+    mTimer = nsnull;
+    InternalSendMessages(PR_FALSE, nsnull);
   }
   else if (!strcmp(aTopic, "quit-application"))
   {
     // We're shutting down. Unsubscribe from the unsentFolder notifications
     // they aren't any use to us now, we don't want to start sending more
     // messages.
     nsCOMPtr<nsIMsgFolder> unsentFolder;
     nsresult rv = GetUnsentMessagesFolder(nsnull, getter_AddRefs(unsentFolder));
@@ -729,16 +730,23 @@ nsMsgSendLater::HasUnsentMessages(nsIMsg
 //          when send is complete
 //            Copy from Outbox to FCC folder
 //            Delete from Outbox folder
 //
 //
 NS_IMETHODIMP 
 nsMsgSendLater::SendUnsentMessages(nsIMsgIdentity *aIdentity)
 {
+  return InternalSendMessages(PR_TRUE, aIdentity);
+}
+
+nsresult
+nsMsgSendLater::InternalSendMessages(PRBool aUserInitiated,
+                                     nsIMsgIdentity *aIdentity)
+{
   nsresult rv = GetUnsentMessagesFolder(aIdentity,
                                         getter_AddRefs(mMessageFolder));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // ### fix me - if we need to reparse the folder, this will be asynchronous
   nsCOMPtr<nsISimpleEnumerator> enumerator;
   rv = mMessageFolder->GetMessages(getter_AddRefs(enumerator));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -750,17 +758,29 @@ nsMsgSendLater::SendUnsentMessages(nsIMs
   PRBool hasMoreElements = PR_FALSE;
   while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) && hasMoreElements)
   {
     rv = enumerator->GetNext(getter_AddRefs(currentItem));
     if (NS_SUCCEEDED(rv))
     {
       messageHeader = do_QueryInterface(currentItem, &rv);
       if (NS_SUCCEEDED(rv))
-        mMessagesToSend.AppendObject(messageHeader);
+      {
+        if (aUserInitiated)
+          // If the user initiated the send, add all messages
+          mMessagesToSend.AppendObject(messageHeader);
+        else
+        {
+          // Else just send those that are NOT marked as Queued.
+          PRUint32 flags;
+          rv = messageHeader->GetFlags(&flags);
+          if (NS_SUCCEEDED(rv) && !(flags & nsMsgMessageFlags::Queued))
+            mMessagesToSend.AppendObject(messageHeader);
+        }  
+      }
     }
   }
 
   // Now get an enumerator for our array.
   rv = NS_NewArrayEnumerator(getter_AddRefs(mEnumerator), mMessagesToSend);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We're now sending messages so its time to signal that and reset our counts.
@@ -1291,16 +1311,22 @@ nsMsgSendLater::GetIdentityFromKey(const
   rv = defaultAccount->GetDefaultIdentity(aIdentity);
   NS_ENSURE_SUCCESS(rv,rv);
   return rv;
 }
 
 NS_IMETHODIMP
 nsMsgSendLater::OnItemAdded(nsIMsgFolder *aParentItem, nsISupports *aItem)
 {
+  // No need to trigger if timer is already set
+  if (mTimer)
+    return NS_OK;
+
+  // XXX only trigger for non-queued headers
+
   // Items from this function return NS_OK because the callee won't care about
   // the result anyway.
   nsresult rv;
   mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
   NS_ENSURE_SUCCESS(rv, NS_OK);
 
   rv = mTimer->Init(static_cast<nsIObserver*>(this), kInitialMessageSendTime,
                     nsITimer::TYPE_ONE_SHOT);
--- a/mailnews/compose/src/nsMsgSendLater.h
+++ b/mailnews/compose/src/nsMsgSendLater.h
@@ -114,16 +114,18 @@ public:
   nsCOMArray<nsIMsgDBHdr> mMessagesToSend;
   nsCOMPtr<nsISimpleEnumerator> mEnumerator;
   nsCOMPtr<nsIMsgFolder>    mMessageFolder;
   nsCOMPtr<nsIMsgStatusFeedback> mFeedback;
  
   // Private Information
 private:
   nsresult GetIdentityFromKey(const char *aKey, nsIMsgIdentity **aIdentity);
+  nsresult InternalSendMessages(PRBool aUserInitiated,
+                                nsIMsgIdentity *aIdentity);
 
   nsTObserverArray<nsCOMPtr<nsIMsgSendLaterListener> > mListenerArray;
   nsCOMPtr<nsIMsgDBHdr>      mMessage;
   nsCOMPtr<nsITimer> mTimer;
 
   //
   // File output stuff...
   //
--- a/mailnews/compose/test/unit/test_sendBackground.js
+++ b/mailnews/compose/test/unit/test_sendBackground.js
@@ -5,17 +5,18 @@
 var type = null;
 var test = null;
 var server;
 var sentFolder;
 var transaction;
 var originalData;
 var finished = false;
 var identity = null;
-var testFile = do_get_file("data/429891_testcase.eml");
+var testFile1 = do_get_file("data/429891_testcase.eml");
+var testFile2 = do_get_file("data/message1.eml");
 
 const kSender = "from@invalid.com";
 const kTo = "to@invalid.com";
 
 var gMsgSendLater;
 
 // This listener handles the post-sending of the actual message and checks the
 // sequence and ensures the data is correct.
@@ -24,16 +25,17 @@ function msll() {
 
 msll.prototype = {
   _initialTotal: 0,
 
   // nsIMsgSendLaterListener
   onStartSending: function (aTotal) {
     this._initialTotal = 1;
     do_check_eq(gMsgSendLater.sendingMessages, true);
+    do_check_eq(aTotal, 1);
   },
   onProgress: function (aCurrentMessage, aTotal) {
     // XXX Enable this function
   },
   onStopSending: function (aStatus, aMsg, aTotal, aSuccessful) {
     do_test_finished();
     print("msll onStopSending\n");
     try {
@@ -47,16 +49,20 @@ msll.prototype = {
                            ["EHLO test",
                             "MAIL FROM:<" + kSender + "> SIZE=" + originalData.length,
                             "RCPT TO:<" + kTo + ">",
                             "DATA"]);
 
       // Compare data file to what the server received
       do_check_eq(originalData, server._handler.post);
 
+      // check there's still one message left in the folder
+      do_check_eq(gMsgSendLater.getUnsentMessagesFolder(null)
+                               .getTotalMessages(false), 1);
+
       finished = true;
     } catch (e) {
       do_throw(e);
     } finally {
       server.stop();
 
       var thread = gThreadManager.currentThread;
       while (thread.hasPendingEvents())
@@ -77,17 +83,17 @@ function run_test() {
   // servers and identities.
   loadLocalMailAccount();
 
   // Now load (and internally initialize) the send later service
   gMsgSendLater = Cc["@mozilla.org/messengercompose/sendlater;1"]
                     .getService(Ci.nsIMsgSendLater);
 
   // Test file - for bug 429891
-  originalData = loadFileToString(testFile);
+  originalData = loadFileToString(testFile1);
 
   // Check that the send later service thinks we don't have messages to send
   do_check_eq(gMsgSendLater.hasUnsentMessages(identity), false);
 
   var acctMgr = Cc["@mozilla.org/messenger/account-manager;1"]
                   .getService(Ci.nsIMsgAccountManager);
   acctMgr.setSpecialFolders();
 
@@ -131,21 +137,26 @@ function run_test() {
     // A test to check that we are sending files correctly, including checking
     // what the server receives and what we output.
     test = "sendMessageLater";
 
     var messageListener = new msll();
 
     gMsgSendLater.addListener(messageListener);
 
+    // Send this message later - it shouldn't get sent
+    msgSend.sendMessageFile(identity, "", compFields, testFile2,
+                            false, false, Ci.nsIMsgSend.nsMsgQueueForLater,
+                            null, null, null, null);
+
     // Send the unsent message in the background, because we have
     // mailnews.sendInBackground set, nsMsgSendLater should just send it for
     // us.
-    msgSend.sendMessageFile(identity, "", compFields, testFile,
-                            false, false, Ci.nsIMsgSend.nsMsgQueueForLater,
+    msgSend.sendMessageFile(identity, "", compFields, testFile1,
+                            false, false, Ci.nsIMsgSend.nsMsgDeliverBackground,
                             null, null, null, null);
 
     server.performTest();
 
     transaction = server.playTransaction();
 
     do_timeout(10000, "if (!finished) do_throw('Notifications of message send/copy not received');");