Part of bug 440794 - Leverage Offline capabilities to make sending email appear faster - protect against shutting down whilst sending email when quit-application-requested has not been received. r/sr=bienvenu

This commit is contained in:
Mark Banner 2009-05-13 09:12:27 +01:00
Родитель f635ec258d
Коммит 0f0f5d8063
4 изменённых файлов: 140 добавлений и 29 удалений

Просмотреть файл

@ -55,6 +55,8 @@
#include "nsIWindowMediator.h"
#include "nsIWindowWatcher.h"
#include "nsIMsgMailNewsUrl.h"
#include "prcmon.h"
#include "nsThreadUtils.h"
NS_IMPL_THREADSAFE_ADDREF(nsMsgMailSession)
NS_IMPL_THREADSAFE_RELEASE(nsMsgMailSession)
@ -525,17 +527,28 @@ nsMsgMailSession::GetDataFilesDir(const char* dirName, nsIFile **dataFilesDir)
NS_IMPL_ISUPPORTS3(nsMsgShutdownService, nsIMsgShutdownService, nsIUrlListener, nsIObserver)
nsMsgShutdownService::nsMsgShutdownService()
: mProcessedShutdown(PR_FALSE),
mQuitForced(PR_FALSE),
mReadyToQuit(PR_FALSE)
{
nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
if (observerService)
{
observerService->AddObserver(this, "quit-application-requested", PR_FALSE);
observerService->AddObserver(this, "quit-application-granted", PR_FALSE);
observerService->AddObserver(this, "quit-application", PR_FALSE);
}
}
nsMsgShutdownService::~nsMsgShutdownService()
{
nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
if (observerService)
{
observerService->RemoveObserver(this, "quit-application-requested");
observerService->RemoveObserver(this, "quit-application-granted");
observerService->RemoveObserver(this, "quit-application");
}
}
nsresult nsMsgShutdownService::ProcessNextTask()
@ -578,11 +591,22 @@ nsresult nsMsgShutdownService::ProcessNextTask()
return NS_OK;
}
nsresult nsMsgShutdownService::AttemptShutdown()
void nsMsgShutdownService::AttemptShutdown()
{
nsCOMPtr<nsIAppStartup> appStartup = do_GetService(NS_APPSTARTUP_CONTRACTID);
NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE);
return appStartup->Quit(nsIAppStartup::eAttemptQuit);
if (mQuitForced)
{
PR_CEnterMonitor(this);
mReadyToQuit = PR_TRUE;
PR_CNotifyAll(this);
PR_CExitMonitor(this);
}
else
{
nsCOMPtr<nsIAppStartup> appStartup =
do_GetService(NS_APPSTARTUP_CONTRACTID);
NS_ENSURE_TRUE(appStartup, );
NS_ENSURE_SUCCESS(appStartup->Quit(nsIAppStartup::eAttemptQuit), );
}
}
NS_IMETHODIMP nsMsgShutdownService::SetShutdownListener(nsIWebProgressListener *inListener)
@ -596,6 +620,27 @@ NS_IMETHODIMP nsMsgShutdownService::Observe(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *aData)
{
// Due to bug 459376 we don't always get quit-application-requested and
// quit-application-granted. quit-application-requested is preferred, but if
// we don't then we have to hook onto quit-application, but we don't want
// to do the checking twice so we set some flags to prevent that.
if (!strcmp(aTopic, "quit-application-granted"))
{
// Quit application has been requested and granted, therefore we will shut
// down.
mProcessedShutdown = PR_TRUE;
return NS_OK;
}
// If we've already processed a shutdown notification, no need to do it again.
if (!strcmp(aTopic, "quit-application"))
{
if (mProcessedShutdown)
return NS_OK;
else
mQuitForced = PR_TRUE;
}
nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
NS_ENSURE_STATE(observerService);
@ -624,7 +669,7 @@ NS_IMETHODIMP nsMsgShutdownService::Observe(nsISupports *aSubject,
listenerEnum->HasMoreElements(&hasMore);
}
if (mShutdownTasks.Count() < 1)
return NS_ERROR_FAILURE;
@ -657,13 +702,31 @@ NS_IMETHODIMP nsMsgShutdownService::Observe(nsISupports *aSubject,
NS_ENSURE_TRUE(internalDomWin, NS_ERROR_FAILURE); // bail if we don't get a window.
}
}
nsCOMPtr<nsISupportsPRBool> stopShutdown = do_QueryInterface(aSubject);
stopShutdown->SetData(PR_TRUE);
if (!mQuitForced)
{
nsCOMPtr<nsISupportsPRBool> stopShutdown = do_QueryInterface(aSubject);
stopShutdown->SetData(PR_TRUE);
}
mMsgProgress->OpenProgressDialog(internalDomWin, topMsgWindow,
"chrome://messenger/content/shutdownWindow.xul",
PR_FALSE, nsnull);
if (mQuitForced)
{
nsIThread *thread = NS_GetCurrentThread();
mReadyToQuit = PR_FALSE;
while (!mReadyToQuit)
{
PR_CEnterMonitor(this);
// Waiting for 50 milliseconds
PR_CWait(this, PR_MicrosecondsToInterval(50000UL));
PR_CExitMonitor(this);
NS_ProcessPendingEvents(thread);
}
}
}
return NS_OK;
@ -700,7 +763,8 @@ NS_IMETHODIMP nsMsgShutdownService::StartShutdownTasks()
NS_IMETHODIMP nsMsgShutdownService::CancelShutdownTasks()
{
return AttemptShutdown();
AttemptShutdown();
return NS_OK;
}
NS_IMETHODIMP nsMsgShutdownService::SetStatusText(const nsAString & inStatusString)

Просмотреть файл

@ -122,12 +122,15 @@ public:
protected:
nsresult ProcessNextTask();
nsresult AttemptShutdown();
void AttemptShutdown();
private:
nsCOMArray<nsIMsgShutdownTask> mShutdownTasks;
nsCOMPtr<nsIMsgProgress> mMsgProgress;
PRUint32 mTaskIndex;
PRPackedBool mProcessedShutdown;
PRPackedBool mQuitForced;
PRPackedBool mReadyToQuit;
};
#endif /* nsMsgMailSession_h__ */

Просмотреть файл

@ -50,8 +50,8 @@ interface nsIMsgFolder;
* can be sent by calling sendUnsentMessages.
*
* Although the service supports passing identities as parameters, until bug
* 317803 is fixed, all identities use the same folder, and hence currently it
* has no effect.
* 317803 is fixed, all identities use the same folder, and hence the option
* currently doesn't work.
*/
[scriptable, uuid(fa324a4b-4b87-4e9a-a3c0-af9071a358df)]
interface nsIMsgSendLater : nsIStreamListener

Просмотреть файл

@ -133,6 +133,9 @@ nsMsgSendLater::Init()
do_GetService("@mozilla.org/observer-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = observerService->AddObserver(this, "xpcom-shutdown", PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
rv = observerService->AddObserver(this, "quit-application", PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
@ -140,11 +143,12 @@ nsMsgSendLater::Init()
NS_ENSURE_SUCCESS(rv, rv);
// Subscribe to the unsent messages folder
nsCOMPtr<nsIMsgFolder> unsentFolder;
rv = GetUnsentMessagesFolder(nsnull, getter_AddRefs(unsentFolder));
// XXX This code should be set up for multiple unsent folders, however we
// don't support that at the moment, so for now just assume one folder.
rv = GetUnsentMessagesFolder(nsnull, getter_AddRefs(mMessageFolder));
NS_ENSURE_SUCCESS(rv, rv);
rv = unsentFolder->AddFolderListener(this);
rv = mMessageFolder->AddFolderListener(this);
NS_ENSURE_SUCCESS(rv, rv);
// XXX may want to send messages X seconds after startup if there are any.
@ -170,22 +174,34 @@ nsMsgSendLater::Observe(nsISupports *aSubject, const char* aTopic,
InternalSendMessages(PR_FALSE, nsnull);
}
else if (!strcmp(aTopic, "quit-application"))
{
// If the timer is set, cancel it - we're quitting, the shutdown service
// interfaces will sort out sending etc.
if (mTimer)
mTimer->Cancel();
mTimerSet = PR_FALSE;
}
else if (!strcmp(aTopic, "xpcom-shutdown"))
{
// 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));
NS_ENSURE_SUCCESS(rv, rv);
rv = unsentFolder->RemoveFolderListener(this);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv;
if (mMessageFolder)
{
rv = mMessageFolder->RemoveFolderListener(this);
NS_ENSURE_SUCCESS(rv, rv);
}
// 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, "xpcom-shutdown");
NS_ENSURE_SUCCESS(rv, rv);
rv = observerService->RemoveObserver(this, "quit-application");
NS_ENSURE_SUCCESS(rv, rv);
@ -659,6 +675,9 @@ nsMsgSendLater::StartNextMailFileSend()
if (!mMessage)
return NS_ERROR_NOT_AVAILABLE;
if (!mMessageFolder)
return NS_ERROR_UNEXPECTED;
mMessageFolder->GetUriForMsg(mMessage, messageURI);
rv = nsMsgCreateTempFile("nsqmail.tmp", getter_AddRefs(mTempFile));
@ -707,13 +726,19 @@ NS_IMETHODIMP
nsMsgSendLater::HasUnsentMessages(nsIMsgIdentity *aIdentity, PRBool *aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
nsresult rv;
nsCOMPtr<nsIMsgFolder> msgFolder;
nsresult rv = GetUnsentMessagesFolder(aIdentity, getter_AddRefs(msgFolder));
NS_ENSURE_SUCCESS(rv, rv);
// XXX This code should be set up for multiple unsent folders, however we
// don't support that at the moment, so for now just assume one folder.
if (!mMessageFolder)
{
rv = GetUnsentMessagesFolder(nsnull,
getter_AddRefs(mMessageFolder));
NS_ENSURE_SUCCESS(rv, rv);
}
PRInt32 totalMessages;
rv = msgFolder->GetTotalMessages(PR_FALSE, &totalMessages);
rv = mMessageFolder->GetTotalMessages(PR_FALSE, &totalMessages);
NS_ENSURE_SUCCESS(rv, rv);
*aResult = totalMessages > 0;
@ -760,9 +785,16 @@ nsMsgSendLater::InternalSendMessages(PRBool aUserInitiated,
return NS_ERROR_FAILURE;
}
nsresult rv = GetUnsentMessagesFolder(aIdentity,
getter_AddRefs(mMessageFolder));
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv;
// XXX This code should be set up for multiple unsent folders, however we
// don't support that at the moment, so for now just assume one folder.
if (!mMessageFolder)
{
rv = GetUnsentMessagesFolder(nsnull,
getter_AddRefs(mMessageFolder));
NS_ENSURE_SUCCESS(rv, rv);
}
// ### fix me - if we need to reparse the folder, this will be asynchronous
nsCOMPtr<nsISimpleEnumerator> enumerator;
@ -867,7 +899,11 @@ nsMsgSendLater::DeleteCurrentMessage()
if (!msgArray)
return NS_ERROR_FACTORY_NOT_LOADED;
if (!mMessageFolder)
return NS_ERROR_UNEXPECTED;
msgArray->InsertElementAt(mMessage, 0, PR_FALSE);
nsresult res = mMessageFolder->DeleteMessages(msgArray, nsnull, PR_TRUE, PR_FALSE, nsnull, PR_FALSE /*allowUndo*/);
if (NS_FAILED(res))
return NS_ERROR_FAILURE;
@ -1283,6 +1319,14 @@ nsMsgSendLater::EndSendMessages(nsresult aStatus, const PRUnichar *aMsg,
// Clear out our array of messages.
mMessagesToSend.Clear();
// We don't need to keep hold of the database now we've finished sending.
(void)mMessageFolder->SetMsgDatabase(nsnull);
// or the enumerator, temp file or output stream
mEnumerator = nsnull;
mTempFile = nsnull;
mOutFile = nsnull;
NOTIFY_LISTENERS(OnStopSending, (aStatus, aMsg, aTotalTried, aSuccessful));
// If we've got a shutdown listener, notify it that we've finished.