Part of bug 440794 Leverage Offline capabilities to make sending email appear faster - provide partial support to ensure we finish sending emails when sending in the background and exiting. r/sr=bienvenu
authorMark Banner <bugzilla@standard8.plus.com>
Fri, 17 Apr 2009 11:36:56 +0100
changeset 2404 8ef97925ead2ec1857c93650fad8e4e0dcbb90b2
parent 2403 65c83e9fbd9233fcec83ee1b83f022455caca4a6
child 2405 d2964a9b482c0a340d7a790019dc21937d1cb2d8
push id1951
push userbugzilla@standard8.plus.com
push dateFri, 17 Apr 2009 10:37:17 +0000
treeherdercomm-central@8ef97925ead2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs440794
Part of bug 440794 Leverage Offline capabilities to make sending email appear faster - provide partial support to ensure we finish sending emails when sending in the background and exiting. r/sr=bienvenu
mailnews/compose/src/nsMsgSendLater.cpp
mailnews/compose/src/nsMsgSendLater.h
--- a/mailnews/compose/src/nsMsgSendLater.cpp
+++ b/mailnews/compose/src/nsMsgSendLater.cpp
@@ -61,22 +61,23 @@
 #include "nsIObserverService.h"
 
 // Consts for checking and sending mail in milliseconds
 
 // 1 second from mail into the unsent messages folder to initially trying to
 // send it.
 const PRUint32 kInitialMessageSendTime = 1000;
 
-NS_IMPL_ISUPPORTS5(nsMsgSendLater,
+NS_IMPL_ISUPPORTS6(nsMsgSendLater,
                    nsIMsgSendLater,
                    nsIFolderListener,
                    nsIRequestObserver,
                    nsIStreamListener,
-                   nsIObserver)
+                   nsIObserver,
+                   nsIMsgShutdownTask)
 
 nsMsgSendLater::nsMsgSendLater()
 {
   mSendingMessages = PR_FALSE;
   mTotalSentSuccessfully = 0;
   mTotalSendCount = 0;
   mLeftoverBuffer = nsnull;
 
@@ -129,16 +130,19 @@ nsMsgSendLater::Init()
   // We need to know when we're shutting down.
   nsCOMPtr<nsIObserverService> observerService =
     do_GetService("@mozilla.org/observer-service;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = observerService->AddObserver(this, "quit-application", PR_FALSE);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = observerService->AddObserver(this, "msg-shutdown", PR_FALSE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   // Subscribe to the unsent messages folder
   nsCOMPtr<nsIMsgFolder> unsentFolder;
   rv = GetUnsentMessagesFolder(nsnull, getter_AddRefs(unsentFolder));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = unsentFolder->AddFolderListener(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -148,18 +152,21 @@ nsMsgSendLater::Init()
 }
 
 NS_IMETHODIMP
 nsMsgSendLater::Observe(nsISupports *aSubject, const char* aTopic,
                         const PRUnichar *aData)
 {
   if (aSubject == mTimer && !strcmp(aTopic, "timer-callback"))
   {
-    mTimer = nsnull;
-    InternalSendMessages(PR_FALSE, nsnull);
+    mTimer->Cancel();
+    // If we've already started a send since the timer fired, don't start
+    // another
+    if (!mSendingMessages)
+      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));
@@ -170,16 +177,19 @@ nsMsgSendLater::Observe(nsISupports *aSu
 
     // Now remove ourselves from the observer service as well.
     nsCOMPtr<nsIObserverService> observerService =
       do_GetService("@mozilla.org/observer-service;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = observerService->RemoveObserver(this, "quit-application");
     NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = observerService->RemoveObserver(this, "msg-shutdown");
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMsgSendLater::SetStatusFeedback(nsIMsgStatusFeedback *aFeedback)
 {
@@ -222,17 +232,17 @@ nsMsgSendLater::OnStopRequest(nsIRequest
     printf("nsMsgSendLater: Success on getting message...\n");
 #endif
     
     // If the send operation failed..try the next one...
     if (NS_FAILED(rv))
     {
       rv = StartNextMailFileSend();
       if (NS_FAILED(rv))
-        NotifyListenersOnStopSending(rv, nsnull, mTotalSendCount, mTotalSentSuccessfully);
+        EndSendMessages(rv, nsnull, mTotalSendCount, mTotalSentSuccessfully);
     }
   }
   else
   {
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
     if(!channel) return NS_ERROR_FAILURE;
 
     // extract the prompt object to use for the alert from the url....
@@ -245,17 +255,17 @@ nsMsgSendLater::OnStopRequest(nsIRequest
       if (smtpUrl)
         smtpUrl->GetPrompt(getter_AddRefs(promptObject));
     } 
     nsMsgDisplayMessageByID(promptObject, NS_ERROR_QUEUED_DELIVERY_FAILED);
     
     // Getting the data failed, but we will still keep trying to send the rest...
     rv = StartNextMailFileSend();
     if (NS_FAILED(rv))
-      NotifyListenersOnStopSending(rv, nsnull, mTotalSendCount, mTotalSentSuccessfully);
+      EndSendMessages(rv, nsnull, mTotalSendCount, mTotalSentSuccessfully);
   }
 
   return rv;
 }
 
 char *
 FindEOL(char *inBuf, char *buf_end)
 {
@@ -447,19 +457,19 @@ SendOperationListener::OnStopSending(con
 
       mSendLater->SetOrigMsgDisposition();
       mSendLater->DeleteCurrentMessage();
 
       ++(mSendLater->mTotalSentSuccessfully);
     }
     else
     {
-      mSendLater->NotifyListenersOnStopSending(aStatus, nsnull,
-                                               mSendLater->mTotalSendCount, 
-                                               mSendLater->mTotalSentSuccessfully);
+      mSendLater->EndSendMessages(aStatus, nsnull,
+                                  mSendLater->mTotalSendCount, 
+                                  mSendLater->mTotalSentSuccessfully);
       NS_RELEASE(mSendLater);
     }
   }
 
   return rv;
 }
 
 // nsIMsgCopyServiceListener
@@ -495,19 +505,19 @@ SendOperationListener::OnStopCopy(nsresu
 {
   if (mSendLater) 
   {
     // Regardless of the success of the copy we will still keep trying
     // to send the rest...
     nsresult rv;
     rv = mSendLater->StartNextMailFileSend();
     if (NS_FAILED(rv))
-      mSendLater->NotifyListenersOnStopSending(rv, nsnull,
-                                               mSendLater->mTotalSendCount, 
-                                               mSendLater->mTotalSentSuccessfully);
+      mSendLater->EndSendMessages(rv, nsnull,
+                                  mSendLater->mTotalSendCount, 
+                                  mSendLater->mTotalSentSuccessfully);
     NS_RELEASE(mSendLater);
   }
 
   return NS_OK;
 }
 
 nsresult
 nsMsgSendLater::CompleteMailFileSend()
@@ -617,24 +627,19 @@ nsMsgSendLater::StartNextMailFileSend()
   nsresult      rv = NS_OK;
   nsCString  messageURI;
 
   PRBool hasMoreElements = PR_FALSE;
   if ((!mEnumerator) ||
       NS_FAILED(mEnumerator->HasMoreElements(&hasMoreElements)) ||
       !hasMoreElements)
   {
-    // Call any listeners on this operation and then exit cleanly
-#ifdef NS_DEBUG
-    printf("nsMsgSendLater: Finished \"Send Later\" operation.\n");
-#endif
+    // EndSendMessages resets everything for us
+    EndSendMessages(NS_OK, nsnull, mTotalSendCount, mTotalSentSuccessfully);
 
-    mMessagesToSend.Clear();
-    mSendingMessages = PR_FALSE;
-    NotifyListenersOnStopSending(NS_OK, nsnull, mTotalSendCount, mTotalSentSuccessfully);
     // XXX Should we be releasing references so that we don't hold onto items
     // unnecessarily.
     return NS_OK;
   }
 
   // Let everyone know about our progress if we've already sent more than one
   // message.
   if (mTotalSendCount)
@@ -737,16 +742,23 @@ nsMsgSendLater::SendUnsentMessages(nsIMs
 {
   return InternalSendMessages(PR_TRUE, aIdentity);
 }
 
 nsresult
 nsMsgSendLater::InternalSendMessages(PRBool aUserInitiated,
                                      nsIMsgIdentity *aIdentity)
 {
+  // Protect against being called whilst we're already sending.
+  if (mSendingMessages)
+  {
+    NS_ERROR("nsMsgSendLater is already sending messages\n");
+    return NS_ERROR_FAILURE;
+  }
+
   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);
@@ -1243,29 +1255,41 @@ nsMsgSendLater::NotifyListenersOnStartSe
 
 void
 nsMsgSendLater::NotifyListenersOnProgress(PRUint32 aCurrentMessage,
                                           PRUint32 aTotalMessage)
 {
   NOTIFY_LISTENERS(OnProgress, (aCurrentMessage, aTotalMessage));
 }
 
+/**
+ * This function is called to end sending of messages, it resets the send later
+ * system and notifies the relevant parties that we have finished.
+ */
 void
-nsMsgSendLater::NotifyListenersOnStopSending(nsresult aStatus,
-                                             const PRUnichar *aMsg,
-                                             PRUint32 aTotalTried,
-                                             PRUint32 aSuccessful)
+nsMsgSendLater::EndSendMessages(nsresult aStatus, const PRUnichar *aMsg,
+                                PRUint32 aTotalTried, PRUint32 aSuccessful)
 {
   // Catch-all, we may have had an issue sending, so we may not be calling
   // StartNextMailFileSend to fully finish the sending. Therefore set
   // mSendingMessages to false here so that we don't think we're still trying
   // to send messages
   mSendingMessages = PR_FALSE;
 
+  // Clear out our array of messages.
+  mMessagesToSend.Clear();
+
   NOTIFY_LISTENERS(OnStopSending, (aStatus, aMsg, aTotalTried, aSuccessful));
+
+  // If we've got a shutdown listener, notify it that we've finished.
+  if (mShutdownListener)
+  {
+    mShutdownListener->OnStopRunningUrl(nsnull, NS_OK);
+    mShutdownListener = nsnull;
+  }
 }
 
 // XXX todo
 // maybe this should just live in the account manager?
 nsresult
 nsMsgSendLater::GetIdentityFromKey(const char *aKey, nsIMsgIdentity  **aIdentity)
 {
   NS_ENSURE_ARG_POINTER(aIdentity);
@@ -1320,18 +1344,21 @@ nsMsgSendLater::OnItemAdded(nsIMsgFolder
   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);
+  if (!mTimer)
+  {
+    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);
   NS_ENSURE_SUCCESS(rv, NS_OK);
 
   return NS_OK;
 }
 
@@ -1379,8 +1406,43 @@ nsMsgSendLater::OnItemPropertyFlagChange
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMsgSendLater::OnItemEvent(nsIMsgFolder* aItem, nsIAtom *aEvent)
 {
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsMsgSendLater::GetNeedsToRunTask(PRBool *aResult)
+{
+  NS_ENSURE_ARG_POINTER(aResult);
+  *aResult = mSendingMessages;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::DoShutdownTask(nsIUrlListener *aListener, nsIMsgWindow *aWindow,
+                               PRBool *aResult)
+{
+  mTimer->Cancel();
+  // If we're already sending messages, nothing to do, but save the shutdown
+  // listener until we've finished.
+  if (mSendingMessages)
+  {
+    mShutdownListener = aListener;
+    return NS_OK;
+  }
+  // Else we have pending messages, we need to throw up a dialog to find out
+  // if to send them or not.
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::GetCurrentTaskName(nsAString &aResult)
+{
+  // XXX Bug 440794 will localize this, left as non-localized whilst we decide
+  // on the actual strings and try out the UI.
+  aResult = NS_LITERAL_STRING("Sending Messages");
+  return NS_OK;
+}
--- a/mailnews/compose/src/nsMsgSendLater.h
+++ b/mailnews/compose/src/nsMsgSendLater.h
@@ -42,57 +42,57 @@
 #include "nsIMsgFolder.h"
 #include "nsIMsgSendListener.h"
 #include "nsIMsgSendLaterListener.h"
 #include "nsIMsgSendLater.h"
 #include "nsIMsgStatusFeedback.h"
 #include "nsTObserverArray.h"
 #include "nsIObserver.h"
 #include "nsITimer.h"
+#include "nsIMsgShutdown.h"
 
 ////////////////////////////////////////////////////////////////////////////////////
 // This is the listener class for the send operation. We have to create this class 
 // to listen for message send completion and eventually notify the caller
 ////////////////////////////////////////////////////////////////////////////////////
 class nsMsgSendLater;
 
 class SendOperationListener : public nsIMsgSendListener,
                               public nsIMsgCopyServiceListener
 {
 public:
   SendOperationListener(nsMsgSendLater *aSendLater);
   virtual ~SendOperationListener();
 
-  // nsISupports interface
   NS_DECL_ISUPPORTS
+  NS_DECL_NSIMSGSENDLISTENER
+  NS_DECL_NSIMSGCOPYSERVICELISTENER
 
-  // nsIMsgSendListener interface
-  NS_DECL_NSIMSGSENDLISTENER
-
-  // nsIMsgCopyServiceListener interface
-  NS_DECL_NSIMSGCOPYSERVICELISTENER
 private:
   nsMsgSendLater *mSendLater;
 };
 
 class nsMsgSendLater: public nsIMsgSendLater,
                       public nsIFolderListener,
-                      public nsIObserver
+                      public nsIObserver,
+                      public nsIMsgShutdownTask
+
 {
 public:
 	nsMsgSendLater();
 	virtual     ~nsMsgSendLater();
   nsresult Init();
 
 	NS_DECL_ISUPPORTS
   NS_DECL_NSIMSGSENDLATER
   NS_DECL_NSIFOLDERLISTENER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSIOBSERVER
+  NS_DECL_NSIMSGSHUTDOWNTASK
 
   // Methods needed for implementing interface...
   nsresult                  StartNextMailFileSend();
   nsresult                  CompleteMailFileSend();
 
   nsresult                  DeleteCurrentMessage();
   nsresult                  SetOrigMsgDisposition();
   // Necessary for creating a valid list of recipients
@@ -100,18 +100,18 @@ public:
   nsresult                  DeliverQueuedLine(char *line, PRInt32 length);
   nsresult                  RebufferLeftovers(char *startBuf,  PRUint32 aLen);
   nsresult                  BuildNewBuffer(const char* aBuf, PRUint32 aCount, PRUint32 *totalBufSize);
 
   // methods for listener array processing...
   void NotifyListenersOnStartSending(PRUint32 aTotalMessageCount);
   void NotifyListenersOnProgress(PRUint32 aCurrentMessage,
                                  PRUint32 aTotalMessage);
-  void NotifyListenersOnStopSending(nsresult aStatus, const PRUnichar *aMsg, 
-                                    PRUint32 aTotalTried, PRUint32 aSuccessful);
+  void EndSendMessages(nsresult aStatus, const PRUnichar *aMsg, 
+                       PRUint32 aTotalTried, PRUint32 aSuccessful);
 
   // counters and things for enumeration 
   PRUint32                  mTotalSentSuccessfully;
   PRUint32                  mTotalSendCount;
   nsCOMArray<nsIMsgDBHdr> mMessagesToSend;
   nsCOMPtr<nsISimpleEnumerator> mEnumerator;
   nsCOMPtr<nsIMsgFolder>    mMessageFolder;
   nsCOMPtr<nsIMsgStatusFeedback> mFeedback;
@@ -120,16 +120,17 @@ public:
 private:
   nsresult GetIdentityFromKey(const char *aKey, nsIMsgIdentity **aIdentity);
   nsresult InternalSendMessages(PRBool aUserInitiated,
                                 nsIMsgIdentity *aIdentity);
 
   nsTObserverArray<nsCOMPtr<nsIMsgSendLaterListener> > mListenerArray;
   nsCOMPtr<nsIMsgDBHdr>      mMessage;
   nsCOMPtr<nsITimer> mTimer;
+  nsCOMPtr<nsIUrlListener> mShutdownListener;
 
   //
   // File output stuff...
   //
   nsCOMPtr<nsIFile>         mTempFile;
   nsCOMPtr<nsIOutputStream> mOutFile;
 
   void                      *mTagData;