fix 466730, make compact all compact imap offline stores, r=standard8, sr=neil
authorDavid Bienvenu <bienvenu@nventure.com>
Wed, 14 Jan 2009 17:48:07 -0800
changeset 1639 0701c0d783e7ae86e9caefb30e514fff3f247adf
parent 1638 dadf1bba728ceda9907692ad700def8b1202fc38
child 1640 c263cff9191dd79284812ba05f5c7b8e67098b52
push idunknown
push userunknown
push dateunknown
reviewersstandard8, neil
bugs466730
fix 466730, make compact all compact imap offline stores, r=standard8, sr=neil
mail/base/content/folderPane.js
mailnews/base/public/nsIMsgFolder.idl
mailnews/base/public/nsIMsgFolderCompactor.idl
mailnews/base/src/nsMsgFolderCompactor.cpp
mailnews/base/src/nsMsgFolderCompactor.h
mailnews/base/src/nsMsgFolderDataSource.cpp
mailnews/base/src/nsMsgPurgeService.cpp
mailnews/base/test/unit/test_folderCompact.js
mailnews/base/util/nsMsgDBFolder.cpp
mailnews/base/util/nsMsgDBFolder.h
mailnews/imap/src/nsImapMailFolder.cpp
mailnews/imap/src/nsImapMailFolder.h
mailnews/local/src/nsLocalMailFolder.cpp
mailnews/local/src/nsLocalMailFolder.h
mailnews/test/data/bugmail11
--- a/mail/base/content/folderPane.js
+++ b/mail/base/content/folderPane.js
@@ -1307,21 +1307,20 @@ let gFolderTreeController = {
   compactFolder: function ftc_compactFolder(aCompactAll, aFolder) {
     let folder = aFolder || gFolderTreeView.getSelectedFolders()[0];
     let isImapFolder = folder.server.type == "imap";
     // Can't compact folders that have just been compacted
     if (!isImapFolder && !folder.expungedBytes && !aCompactAll)
       return;
 
     // reset thread pane for non-imap folders.
-    if (!isImapFolder && (gDBView.msgFolder == folder || aCompactAll))
+    if (!isImapFolder && gDBView && (gDBView.msgFolder == folder || aCompactAll))
       this._resetThreadPane();
-
     if (aCompactAll)
-      folder.compactAll(null, msgWindow, null, true, null);
+      folder.compactAll(null, msgWindow, isImapFolder || folder.server.type == "news");
     else
       folder.compact(null, msgWindow);
   },
 
   /**
    * Opens the dialog to create a new virtual folder
    *
    * @param aName - the default name for the new folder
--- a/mailnews/base/public/nsIMsgFolder.idl
+++ b/mailnews/base/public/nsIMsgFolder.idl
@@ -62,17 +62,17 @@ interface nsILocalFile;
 interface nsIMsgIdentity;
 interface nsIArray;
 interface nsIMutableArray;
 
 typedef long nsMsgBiffState;
 
 // enumerated type for determining if a message has been replied to, forwarded, etc.
 typedef long nsMsgDispositionState;
-[scriptable, uuid(C5FF3B28-6A48-415A-BFB4-97D9DBE85E3B)]
+[scriptable, uuid(38444311-d7fb-4fbc-a52e-1899320ce1da)]
 interface nsIMsgFolder : nsISupports {
 
   const nsMsgBiffState nsMsgBiffState_NewMail = 0; // User has new mail waiting.
   const nsMsgBiffState nsMsgBiffState_NoMail =  1; // No new mail is waiting.
   const nsMsgBiffState nsMsgBiffState_Unknown = 2; // We dunno whether there is new mail.
 
   nsISimpleEnumerator getMessages(in nsIMsgWindow aMsgWindow);
 
@@ -159,21 +159,32 @@ interface nsIMsgFolder : nsISupports {
   /* this method ensures the storage for the folder exists.
     For local folders, it creates the berkeley mailbox if missing.
     For imap folders, it subscribes to the folder if it exists,
     or creates it if it doesn't exist
   */
   void createStorageIfMissing(in nsIUrlListener urlListener);
 
   void compact(in nsIUrlListener aListener, in nsIMsgWindow aMsgWindow);
+  /**
+   * Compact all folders in the account corresponding to this folder/
+   * Optionally compact their offline stores as well (imap/news)
+   * 
+   * @param aListener   Notified of completion, can be null.
+   * @param aMsgWindow  For progress/status, can be null.
+   * @param aCompactOfflineAlso  This controls whether we compact all 
+   *                             offline stores as well.
+   */
   void compactAll(in nsIUrlListener aListener, in nsIMsgWindow aMsgWindow,
-                  in nsISupportsArray aFolderArray, in boolean aCompactOfflineAlso,
-                  in nsISupportsArray aOfflineFolderArray);
-  void compactAllOfflineStores(in nsIMsgWindow aMsgWindow,
-                               in nsISupportsArray aOfflineFolderArray);
+                  in boolean aCompactOfflineAlso);
+
+  void compactAllOfflineStores(in nsIUrlListener aListener,
+                               in nsIMsgWindow aMsgWindow,
+                               in nsIArray aOfflineFolderArray);
+
   void emptyTrash(in nsIMsgWindow aMsgWindow, in nsIUrlListener aListener);
 
   /**
    * change the name of the folder
    *
    * @param name the new name of the folder
    */
   void rename(in AString name, in nsIMsgWindow msgWindow);
--- a/mailnews/base/public/nsIMsgFolderCompactor.idl
+++ b/mailnews/base/public/nsIMsgFolderCompactor.idl
@@ -31,23 +31,49 @@
  * 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 ***** */
 
 #include "nsISupports.idl"
-#include "nsISupportsArray.idl"
 
 interface nsIMsgFolder;
 interface nsIMsgDatabase;
 interface nsIMsgWindow;
+interface nsIUrlListener;
+interface nsIArray;
 
-[scriptable, uuid(2af7d7a2-e5b6-11d4-a5b7-0060b0fc04b7)]
+[scriptable, uuid(38c7e876-3083-4aea-8dcd-0ea0ec1753a3)]
 
-/*  Use this for any object that wants to handle compacting folders */
+/**
+ * Use this for any object that wants to handle compacting folders.
+ * Currently, the folders themselves create this object.
+ */
 
 interface nsIMsgFolderCompactor :  nsISupports
 {
-  void compact(in nsIMsgFolder aFolder, in boolean aOfflineStore, in nsIMsgWindow aMsgWindow);
-  void compactAll(in nsISupportsArray aArrayOfFoldersToCompact, in nsIMsgWindow aMsgWindow, in boolean compactOfflineAlso, in nsISupportsArray aOfflineFolderArray);
+  /**
+   * Compact the given folder, or its offline store (imap/news only)
+   *
+   * @param aFolder        The folder to compact
+   * @param aOfflineStore  Just compact the offline store?
+   * @param aListener      Notified of completion, can be null.
+   * @param aMsgWindow     Used for progress/status, can be null
+   */
+  void compact(in nsIMsgFolder aFolder, in boolean aOfflineStore,
+               in nsIUrlListener aListener, in nsIMsgWindow aMsgWindow);
+
+  /**
+   * Compact the passed in array of folders, and the passed in offline stores.
+   *
+   * @param aArrayOfFoldersToCompact  The folders to compact.
+   * @param aOfflineFolderArray  Folders whose offline stores we
+   *                             should compact, can be null.
+   * @param aListener            Notified of completion, can be null.
+   * @param aMsgWindow           Used for progress/status, can be null
+   */
+  void compactFolders(in nsIArray aArrayOfFoldersToCompact,
+                      in nsIArray aOfflineFolderArray,
+                      in nsIUrlListener aListener,
+                      in nsIMsgWindow aMsgWindow);
 };
--- a/mailnews/base/src/nsMsgFolderCompactor.cpp
+++ b/mailnews/base/src/nsMsgFolderCompactor.cpp
@@ -53,16 +53,17 @@
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIMsgLocalMailFolder.h"
 #include "nsIMsgImapMailFolder.h"
 #include "nsMailHeaders.h"
 #include "nsMsgI18N.h"
 #include "prprf.h"
 #include "nsMsgLocalFolderHdrs.h"
 #include "nsIMsgDatabase.h"
+#include "nsArrayUtils.h"
 
 //////////////////////////////////////////////////////////////////////////////
 // nsFolderCompactState
 //////////////////////////////////////////////////////////////////////////////
 
 NS_IMPL_ISUPPORTS5(nsFolderCompactState, nsIMsgFolderCompactor, nsIRequestObserver, nsIStreamListener, nsICopyMessageStreamListener, nsIUrlListener)
 
 nsFolderCompactState::nsFolderCompactState()
@@ -143,52 +144,59 @@ nsFolderCompactState::InitDB(nsIMsgDatab
       rv = msgDBService->OpenMailDBFromFile(m_file,
                                PR_TRUE, PR_TRUE,
                                getter_AddRefs(m_db));
     }
   }
   return rv;
 }
 
-NS_IMETHODIMP nsFolderCompactState::CompactAll(nsISupportsArray *aArrayOfFoldersToCompact, nsIMsgWindow *aMsgWindow, PRBool aCompactOfflineAlso, nsISupportsArray *aOfflineFolderArray)
+NS_IMETHODIMP nsFolderCompactState::CompactFolders(nsIArray *aArrayOfFoldersToCompact,
+                                                   nsIArray *aOfflineFolderArray,
+                                                   nsIUrlListener *aUrlListener,
+                                                   nsIMsgWindow *aMsgWindow)
 {
-  nsresult rv = NS_OK;
   m_window = aMsgWindow;
-  if (aArrayOfFoldersToCompact)  
-    m_folderArray =do_QueryInterface(aArrayOfFoldersToCompact, &rv);
+  m_listener = aUrlListener;
+  if (aArrayOfFoldersToCompact)
+    m_folderArray = aArrayOfFoldersToCompact;
   else if (aOfflineFolderArray)
   {
-    m_folderArray = do_QueryInterface(aOfflineFolderArray, &rv);
+    m_folderArray = aOfflineFolderArray;
     m_compactingOfflineFolders = PR_TRUE;
     aOfflineFolderArray = nsnull;
   }
-  if (NS_FAILED(rv) || !m_folderArray)
-    return rv;
+  if (!m_folderArray)
+    return NS_OK;
  
   m_compactAll = PR_TRUE;
-  m_compactOfflineAlso = aCompactOfflineAlso;
+  m_compactOfflineAlso = aOfflineFolderArray != nsnull;
   if (m_compactOfflineAlso)
-    m_offlineFolderArray = do_QueryInterface(aOfflineFolderArray);
+    m_offlineFolderArray = aOfflineFolderArray;
 
   m_folderIndex = 0;
+  nsresult rv = NS_OK;
   nsCOMPtr<nsIMsgFolder> firstFolder = do_QueryElementAt(m_folderArray,
                                                          m_folderIndex, &rv);
 
   if (NS_SUCCEEDED(rv) && firstFolder)
-    Compact(firstFolder, m_compactingOfflineFolders, aMsgWindow);   //start with first folder from here.
+    Compact(firstFolder, m_compactingOfflineFolders, aUrlListener, 
+            aMsgWindow);   //start with first folder from here.
   
   return rv;
 }
 
 NS_IMETHODIMP
-nsFolderCompactState::Compact(nsIMsgFolder *folder, PRBool aOfflineStore, nsIMsgWindow *aMsgWindow)
+nsFolderCompactState::Compact(nsIMsgFolder *folder, PRBool aOfflineStore,
+                              nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow)
 {
   NS_ENSURE_ARG_POINTER(folder);
+  m_listener = aListener;
   if (!m_compactingOfflineFolders && !aOfflineStore)
-{
+  {
     nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(folder);
     if (imapFolder)
       return folder->Compact(this, aMsgWindow);
   }
    m_window = aMsgWindow;
    nsresult rv;
    nsCOMPtr<nsIMsgDatabase> db;
    nsCOMPtr<nsIDBFolderInfo> folderInfo;
@@ -223,26 +231,26 @@ nsFolderCompactState::Compact(nsIMsgFold
          else
            return NS_OK;
        }
      }
    }
    else
    {
      rv=folder->GetMsgDatabase(nsnull, getter_AddRefs(db));
-     NS_ENSURE_SUCCESS(rv,rv);
+     NS_ENSURE_SUCCESS(rv, rv);
    }
    rv = folder->GetFilePath(getter_AddRefs(path));
-   NS_ENSURE_SUCCESS(rv,rv);
+   NS_ENSURE_SUCCESS(rv, rv);
 
    rv = folder->GetBaseMessageURI(baseMessageURI);
-   NS_ENSURE_SUCCESS(rv,rv);
+   NS_ENSURE_SUCCESS(rv, rv);
     
    rv = Init(folder, baseMessageURI.get(), db, path, m_window);
-   NS_ENSURE_SUCCESS(rv,rv);
+   NS_ENSURE_SUCCESS(rv, rv);
 
    PRBool isLocked;
    m_folder->GetLocked(&isLocked);
    if(!isLocked)
    {
      nsCOMPtr <nsISupports> supports = do_QueryInterface(static_cast<nsIMsgFolderCompactor*>(this));
      m_folder->AcquireSemaphore(supports);
      return StartCompacting();
@@ -321,30 +329,34 @@ NS_IMETHODIMP nsFolderCompactState::OnSt
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP nsFolderCompactState::OnStopRunningUrl(nsIURI *url, nsresult status)
 {
   if (m_parsingFolder)
   {
-    m_parsingFolder=PR_FALSE;
+    m_parsingFolder = PR_FALSE;
     if (NS_SUCCEEDED(status))
-      status=Compact(m_folder, m_compactingOfflineFolders, m_window);
+      status = Compact(m_folder, m_compactingOfflineFolders, this, m_window);
     else if (m_compactAll)
       CompactNextFolder();
   }
   else if (m_compactAll) // this should be the imap case only
   {
     nsCOMPtr <nsIMsgFolder> prevFolder = do_QueryElementAt(m_folderArray,
                                                            m_folderIndex);
     if (prevFolder)
       prevFolder->SetMsgDatabase(nsnull);
     CompactNextFolder();
   }
+  else if (m_listener)
+  {
+    CompactCompleted(status);
+  }
   return NS_OK;
 }
 
 nsresult nsFolderCompactState::StartCompacting()
 {
   nsresult rv = NS_OK;
   if (m_size > 0)
   {
@@ -458,22 +470,29 @@ nsFolderCompactState::FinishCompact()
   if (m_db)
     m_db->Close(PR_TRUE);
   m_db = nsnull;
 
   m_folder->NotifyCompactCompleted();
 
   if (m_compactAll)
     rv = CompactNextFolder();
-  else 
-    ShowDoneStatus();
+  else
+    CompactCompleted(NS_OK);
       
   return rv;
 }
 
+void nsFolderCompactState::CompactCompleted(nsresult exitCode)
+{
+  if (m_listener)
+    m_listener->OnStopRunningUrl(nsnull, exitCode);
+  ShowDoneStatus();
+}
+
 nsresult
 nsFolderCompactState::ReleaseFolderLock()
 {
   nsresult result = NS_OK;
   if (!m_folder) return result;
   PRBool haveSemaphore;
   nsCOMPtr <nsISupports> supports = do_QueryInterface(static_cast<nsIMsgFolderCompactor*>(this));
   result = m_folder->TestSemaphore(supports, &haveSemaphore);
@@ -491,46 +510,42 @@ void nsFolderCompactState::ShowDoneStatu
     if (!statusString.IsEmpty() && NS_SUCCEEDED(rv))
       ShowStatusMsg(statusString);
   }
 }
 
 nsresult
 nsFolderCompactState::CompactNextFolder()
 {
-   nsresult rv = NS_OK;
-   m_folderIndex++;
-   PRUint32 cnt=0;
-   rv = m_folderArray->Count(&cnt);
-   NS_ENSURE_SUCCESS(rv,rv);
-   if (m_folderIndex == cnt)
-   {
-     if (m_compactOfflineAlso)
-     {
-       m_compactingOfflineFolders = PR_TRUE;
-       nsCOMPtr<nsIMsgFolder> folder = do_QueryElementAt(m_folderArray,
-                                                         m_folderIndex-1, &rv);
-       if (NS_SUCCEEDED(rv) && folder)
-         folder->CompactAllOfflineStores(m_window, m_offlineFolderArray);
-     }
-     else
-     {
-       ShowDoneStatus();
-       return rv;
-     }
-       
-   } 
-   nsCOMPtr<nsIMsgFolder> folder = do_QueryElementAt(m_folderArray,
-                                                     m_folderIndex, &rv);
+  m_folderIndex++;
+  PRUint32 cnt = 0;
+  nsresult rv = m_folderArray->GetLength(&cnt);
+  NS_ENSURE_SUCCESS(rv,rv);
+  if (m_folderIndex == cnt)
+  {
+    if (!m_compactOfflineAlso)
+    {
+      CompactCompleted(NS_OK);
+      return rv;
+    }
+    m_compactingOfflineFolders = PR_TRUE;
+    nsCOMPtr<nsIMsgFolder> folder = do_QueryElementAt(m_folderArray,
+                                                      m_folderIndex-1, &rv);
+    if (NS_SUCCEEDED(rv) && folder)
+      folder->CompactAllOfflineStores(this, m_window, m_offlineFolderArray);
 
-   if (NS_SUCCEEDED(rv) && folder)
-     rv = Compact(folder, m_compactingOfflineFolders, m_window);                    
-    else
-      ShowDoneStatus();
-   return rv;
+  }
+  nsCOMPtr<nsIMsgFolder> folder = do_QueryElementAt(m_folderArray,
+                                                    m_folderIndex, &rv);
+
+  if (NS_SUCCEEDED(rv) && folder)
+    rv = Compact(folder, m_compactingOfflineFolders, m_listener, m_window);
+  else
+    CompactCompleted(rv);
+  return rv;
 }
 
 nsresult
 nsFolderCompactState::GetMessage(nsIMsgDBHdr **message)
 {
   return GetMsgDBHdrFromURI(m_messageUri.get(), message);
 }
 
--- a/mailnews/base/src/nsMsgFolderCompactor.h
+++ b/mailnews/base/src/nsMsgFolderCompactor.h
@@ -44,17 +44,20 @@
 #include "nsIMsgFolderCompactor.h"
 #include "nsICopyMsgStreamListener.h"
 #include "nsIMsgWindow.h"
 #include "nsIStringBundle.h"
 #include "nsIMsgMessageService.h"
 
 #define COMPACTOR_READ_BUFF_SIZE 16384
 
-class nsFolderCompactState : public nsIMsgFolderCompactor, public nsIStreamListener, public nsICopyMessageStreamListener, public nsIUrlListener
+class nsFolderCompactState : public nsIMsgFolderCompactor,
+                             public nsIStreamListener,
+                             public nsICopyMessageStreamListener,
+                             public nsIUrlListener
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSICOPYMESSAGESTREAMLISTENER
   NS_DECL_NSIURLLISTENER
   NS_DECL_NSIMSGFOLDERCOMPACTOR
@@ -70,16 +73,17 @@ protected:
 
   nsresult Init(nsIMsgFolder *aFolder, const char* aBaseMsgUri, nsIMsgDatabase *aDb,
                             nsILocalFile *aPath, nsIMsgWindow *aMsgWindow);
   nsresult GetMessage(nsIMsgDBHdr **message);
   nsresult BuildMessageURI(const char *baseURI, PRUint32 key, nsCString& uri);  
   nsresult ShowStatusMsg(const nsString& aMsg);
   nsresult ReleaseFolderLock();
   void     ShowCompactingStatusMsg();
+  void     CompactCompleted(nsresult exitCode);
   void     ShowDoneStatus();
   nsresult CompactNextFolder();
   void     AdvanceToNextLine(const char *buffer, PRUint32 &bufferOffset, PRUint32 maxBufferOffset);
   
 
   nsCString m_baseMessageUri; // base message uri
   nsCString m_messageUri; // current message uri being copy
   nsCOMPtr<nsIMsgFolder> m_folder; // current folder being compact
@@ -88,31 +92,31 @@ protected:
   nsCOMPtr <nsIOutputStream> m_fileStream; // output file stream for writing
   nsTArray<nsMsgKey> m_keyArray; // all message keys need to be copied over
   PRInt32 m_size; // size of the message key array
   PRInt32 m_curIndex; // index of the current copied message key in key array
   nsMsgKey m_startOfNewMsg; // offset in mailbox of new message
   char m_dataBuffer[COMPACTOR_READ_BUFF_SIZE + 1]; // temp data buffer for copying message
   nsresult m_status; // the status of the copying operation
   nsCOMPtr <nsIMsgMessageService> m_messageService; // message service for copying 
-  nsCOMPtr<nsISupportsArray> m_folderArray; // to store all the folders in case of compact all
+  nsCOMPtr<nsIArray> m_folderArray; // folders we are compacting, if compacting multiple.
   nsCOMPtr <nsIMsgWindow> m_window;
   nsCOMPtr <nsIMsgDBHdr> m_curSrcHdr;
   PRUint32 m_folderIndex; // tells which folder to compact in case of compact all
   PRBool m_compactAll;  //flag for compact all
   PRBool m_compactOfflineAlso; //whether to compact offline also
   PRBool m_compactingOfflineFolders; // are we in the offline folder compact phase
   PRBool m_parsingFolder; //flag for parsing local folders;
   // these members are used to add missing status lines to compacted messages.
   PRBool m_needStatusLine;
   PRBool m_startOfMsg;
   PRInt32 m_statusOffset;
   PRUint32 m_addedHeaderSize;
-  nsCOMPtr <nsISupportsArray> m_offlineFolderArray;
-
+  nsCOMPtr<nsIArray> m_offlineFolderArray;
+  nsCOMPtr<nsIUrlListener> m_listener;
 };
 
 class nsOfflineStoreCompactState : public nsFolderCompactState
 {
 public:
 
   nsOfflineStoreCompactState(void);
   virtual ~nsOfflineStoreCompactState(void);
--- a/mailnews/base/src/nsMsgFolderDataSource.cpp
+++ b/mailnews/base/src/nsMsgFolderDataSource.cpp
@@ -793,17 +793,18 @@ nsMsgFolderDataSource::DoCommand(nsISupp
         rv = folder->MarkAllMessagesRead(window);
       }
       else if ((aCommand == kNC_Compact))
       {
         rv = folder->Compact(nsnull, window);
       }
       else if ((aCommand == kNC_CompactAll))
       {
-        rv = folder->CompactAll(nsnull, window, nsnull, PR_TRUE, nsnull);
+        // this will also compact offline stores for IMAP
+        rv = folder->CompactAll(nsnull, window, PR_TRUE);
       }
       else if ((aCommand == kNC_EmptyTrash))
       {
           rv = folder->EmptyTrash(window, nsnull);
       }
       else if ((aCommand == kNC_Rename))
       {
         nsCOMPtr<nsIRDFLiteral> literal = do_QueryElementAt(aArguments, 0, &rv);
--- a/mailnews/base/src/nsMsgPurgeService.cpp
+++ b/mailnews/base/src/nsMsgPurgeService.cpp
@@ -199,17 +199,17 @@ nsresult nsMsgPurgeService::PerformPurge
           nsCOMPtr <nsIMsgFolder> rootFolder;
           rv = server->GetRootFolder(getter_AddRefs(rootFolder));
           NS_ENSURE_SUCCESS(rv, rv);
 
           nsCOMPtr <nsISupportsArray> childFolders = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);
           NS_ENSURE_SUCCESS(rv, rv);
           rv = rootFolder->ListDescendents(childFolders);
 
-          PRUint32 cnt =0;
+          PRUint32 cnt = 0;
           childFolders->Count(&cnt);
 
           nsCOMPtr<nsISupports> supports;
           nsCOMPtr<nsIUrlListener> urlListener;
           nsCOMPtr<nsIMsgFolder> childFolder;
 
           for (PRUint32 index = 0; index < cnt; index++)
           {
new file mode 100644
--- /dev/null
+++ b/mailnews/base/test/unit/test_folderCompact.js
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ *
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ /*
+  * Test suite for folder compaction
+  *
+  * Currently tested:
+  * - Compacting local folders
+  * TODO
+  * - Compacting imap offline stores.
+  */
+
+// Globals
+var gMsgFile1, gMsgFile2, gMsgFile3;
+var gLocalFolder2;
+var gLocalFolder3;
+var gLocalTrashFolder;
+var gCurTestNum;
+// After a compact (or other operation), this is what we expect the 
+// folder size to be.
+var gExpectedFolderSize;
+var gMsgHdrs = new Array();
+
+const gCopyService = Cc["@mozilla.org/messenger/messagecopyservice;1"]
+                      .getService(Ci.nsIMsgCopyService);
+
+// nsIMsgCopyServiceListener implementation
+var copyListener = 
+{
+  OnStartCopy: function() {},
+  OnProgress: function(aProgress, aProgressMax) {},
+  SetMessageKey: function(aKey)
+  {
+    let hdr = gLocalInboxFolder.GetMessageHeader(aKey);
+    gMsgHdrs.push({hdr: hdr, ID: hdr.messageId});
+  },
+  SetMessageId: function(aMessageId) {},
+  OnStopCopy: function(aStatus)
+  {
+    // Check: message successfully copied.
+    do_check_eq(aStatus, 0);
+    // Ugly hack: make sure we don't get stuck in a JS->C++->JS->C++... call stack
+    // This can happen with a bunch of synchronous functions grouped together, and
+    // can even cause tests to fail because they're still waiting for the listener
+    // to return
+    do_timeout(0, "doTest(++gCurTestNum)");
+  }
+};
+
+var urlListener =
+{
+  OnStartRunningUrl: function (aUrl) {
+  },
+  OnStopRunningUrl: function (aUrl, aExitCode) {
+    // Check: message successfully copied.
+    do_check_eq(aExitCode, 0);
+    // Ugly hack: make sure we don't get stuck in a JS->C++->JS->C++... call stack
+    // This can happen with a bunch of synchronous functions grouped together, and
+    // can even cause tests to fail because they're still waiting for the listener
+    // to return
+    do_timeout(0, "doTest(++gCurTestNum)");
+  }
+};
+
+function copyFileMessage(file, destFolder, isDraftOrTemplate)
+{
+  gCopyService.CopyFileMessage(file, destFolder, null, isDraftOrTemplate, 0, "", copyListener, null);
+}
+
+function copyMessages(items, isMove, srcFolder, destFolder)
+{
+  var array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+  items.forEach(function (item) {
+    array.appendElement(item, false);
+  });
+  gCopyService.CopyMessages(srcFolder, array, destFolder, isMove, copyListener, null, true);
+}
+
+function deleteMessages(srcFolder, items)
+{
+  var array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+  items.forEach(function (item) {
+    array.appendElement(item, false);
+  });
+  
+  srcFolder.deleteMessages(array, null, false, true, copyListener, true);
+}
+
+function calculateFolderSize(folder)
+{
+  let msgDB = folder.getMsgDatabase(null);
+  let enumerator = msgDB.EnumerateMessages();
+  let totalSize = 0;
+  if (enumerator)
+  {
+    while (enumerator.hasMoreElements())
+    {
+      var header = enumerator.getNext();
+      if (header instanceof Components.interfaces.nsIMsgDBHdr)
+        totalSize += header.messageSize;
+    }
+  }
+  return totalSize;
+}
+
+/*
+ * TESTS
+ */
+
+// Beware before commenting out a test -- later tests might just depend on earlier ones
+const gTestArray =
+[
+  // Copying messages from files
+  function testCopyFileMessage1() { copyFileMessage(gMsgFile1, gLocalInboxFolder, false); },
+  function testCopyFileMessage2() { copyFileMessage(gMsgFile2, gLocalInboxFolder, false); },
+  function testCopyFileMessage3() { copyFileMessage(gMsgFile3, gLocalInboxFolder, true); },
+
+  // Moving/copying messages
+  function testCopyMessages1() { copyMessages([gMsgHdrs[0].hdr], false, gLocalInboxFolder, gLocalFolder2); },
+  function testCopyMessages2() { copyMessages([gMsgHdrs[1].hdr, gMsgHdrs[2].hdr], false, gLocalInboxFolder, gLocalFolder2); },
+  function testMoveMessages1() { copyMessages([gMsgHdrs[0].hdr, gMsgHdrs[1].hdr], true, gLocalInboxFolder, gLocalFolder3); },
+
+  // Deleting messages
+  function testDeleteMessages1() { // delete to trash
+    // Let's take a moment to re-initialize stuff that got moved
+    var folder3DB = gLocalFolder3.getMsgDatabase(null);
+    gMsgHdrs[0].hdr = folder3DB.getMsgHdrForMessageID(gMsgHdrs[0].ID);
+
+    // Now delete the message
+    deleteMessages(gLocalFolder3, [gMsgHdrs[0].hdr], false, false);
+  },
+  function compactFolder()
+  {
+    gExpectedFolderSize = calculateFolderSize(gLocalFolder3);
+    do_check_neq(gLocalFolder3.expungedBytes, 0);
+    gLocalFolder3.compact(urlListener, null);
+  },
+  function compactAllFolders()
+  {
+    do_check_eq(gExpectedFolderSize, gLocalFolder3.filePath.fileSize);
+    gExpectedInboxSize = calculateFolderSize(gLocalInboxFolder);
+    gExpectedFolder2Size = calculateFolderSize(gLocalFolder2);
+    gExpectedFolder3Size = calculateFolderSize(gLocalFolder3);
+    gLocalInboxFolder.compactAll(urlListener, null, true);
+  },
+  function lastTestCheck()
+  {
+    do_check_eq(gExpectedInboxSize, gLocalInboxFolder.filePath.fileSize);
+    do_check_eq(gExpectedFolder2Size, gLocalFolder2.filePath.fileSize);
+    do_check_eq(gExpectedFolder3Size, gLocalFolder3.filePath.fileSize);
+    urlListener.OnStopRunningUrl(null, 0);
+  }
+];
+
+function run_test()
+{
+  loadLocalMailAccount();
+  // Load up some messages so that we can copy them in later.
+  gMsgFile1 = do_get_file("../mailnews/test/data/bugmail10");
+  gMsgFile2 = do_get_file("../mailnews/test/data/bugmail11");
+  gMsgFile3 = do_get_file("../mailnews/test/data/draft1");
+
+  // Create another folder to move and copy messages around, and force initialization.
+  var rootFolder = gLocalIncomingServer.rootMsgFolder;
+  gLocalFolder2 = rootFolder.addSubfolder("folder2");
+  var folderName = gLocalFolder2.prettiestName;
+  // Create a third folder for more testing.
+  gLocalFolder3 = rootFolder.addSubfolder("folder3");
+  folderName = gLocalFolder3.prettiestName;
+
+  // "Master" do_test_pending(), paired with a do_test_finished() at the end of all the operations.
+  do_test_pending();
+
+//  do_test_finished();
+  // Do the test.
+  doTest(1);
+}
+
+function doTest(test)
+{
+  if (test <= gTestArray.length)
+  {
+    gCurTestNum = test;
+    
+    var testFn = gTestArray[test-1];
+    // Set a limit of three seconds; if the notifications haven't arrived by then there's a problem.
+    do_timeout(10000, "if (gCurTestNum == "+test+") \
+      do_throw('Notifications not received in 10000 ms for operation "+testFn.name+", current status is '+gCurrStatus);");
+    try {
+    testFn();
+    } catch(ex) {dump(ex);}
+  }
+  else
+  {
+    do_test_finished(); // for the one in run_test()
+  }
+}
--- a/mailnews/base/util/nsMsgDBFolder.cpp
+++ b/mailnews/base/util/nsMsgDBFolder.cpp
@@ -354,19 +354,19 @@ NS_IMETHODIMP nsMsgDBFolder::OpenBackupM
   // We use a dummy message folder file so we can use
   // GetSummaryFileLocation to get the db file name
   nsCOMPtr<nsILocalFile> backupDBDummyFolder;
   rv = CreateBackupDirectory(getter_AddRefs(backupDBDummyFolder));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = backupDBDummyFolder->Append(folderName);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mBackupDatabase = do_CreateInstance(NS_MAILBOXDB_CONTRACTID, &rv);
   nsCOMPtr<nsIMsgDBService> msgDBService =
       do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
   rv = msgDBService->OpenMailDBFromFile(
       backupDBDummyFolder, PR_FALSE, PR_TRUE, getter_AddRefs(mBackupDatabase));
 
   if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
     // this is normal in reparsing
     rv = NS_OK;
   return rv;
 }
@@ -1595,22 +1595,22 @@ nsresult nsMsgDBFolder::EndNewOfflineMes
     curStorePos -= messageOffset;
     m_offlineHeader->SetOfflineMessageSize(curStorePos);
     m_offlineHeader->SetLineCount(m_numOfflineMsgLines);
   }
   m_offlineHeader = nsnull;
   return NS_OK;
 }
 
-nsresult nsMsgDBFolder::CompactOfflineStore(nsIMsgWindow *inWindow)
+nsresult nsMsgDBFolder::CompactOfflineStore(nsIMsgWindow *inWindow, nsIUrlListener *aListener)
 {
   nsresult rv;
   nsCOMPtr <nsIMsgFolderCompactor> folderCompactor =  do_CreateInstance(NS_MSGOFFLINESTORECOMPACTOR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
-  return folderCompactor->Compact(this, PR_TRUE, inWindow);
+  return folderCompactor->Compact(this, PR_TRUE, aListener, inWindow);
 }
 
 nsresult
 nsMsgDBFolder::AutoCompact(nsIMsgWindow *aWindow)
 {
   NS_ENSURE_ARG_POINTER(aWindow);
   PRBool prompt;
   nsresult rv = GetPromptPurgeThreshold(&prompt);
@@ -1621,72 +1621,72 @@ nsMsgDBFolder::AutoCompact(nsIMsgWindow 
   if (LL_CMP(timeAfterOneHourOfLastPurgeCheck, <, timeNow) && prompt)
   {
    gtimeOfLastPurgeCheck = timeNow;
    nsCOMPtr<nsIMsgAccountManager> accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
    if (NS_SUCCEEDED(rv))
    {
      nsCOMPtr<nsISupportsArray> allServers;
      accountMgr->GetAllServers(getter_AddRefs(allServers));
-     NS_ENSURE_SUCCESS(rv,rv);
+     NS_ENSURE_SUCCESS(rv, rv);
      PRUint32 numServers, serverIndex=0;
      rv = allServers->Count(&numServers);
      PRInt32 offlineSupportLevel;
      if ( numServers > 0 )
      {
        nsCOMPtr<nsIMsgIncomingServer> server = do_QueryElementAt(allServers, serverIndex);
-       NS_ENSURE_SUCCESS(rv,rv);
-       nsCOMPtr<nsISupportsArray> folderArray;
-       nsCOMPtr<nsISupportsArray> offlineFolderArray;
-       NS_NewISupportsArray(getter_AddRefs(folderArray));
-       NS_NewISupportsArray(getter_AddRefs(offlineFolderArray));
+       NS_ENSURE_SUCCESS(rv, rv);
+       nsCOMPtr<nsIMutableArray> folderArray = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+       NS_ENSURE_SUCCESS(rv, rv);
+       nsCOMPtr<nsIMutableArray> offlineFolderArray = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+       NS_ENSURE_SUCCESS(rv, rv);
        PRInt32 totalExpungedBytes = 0;
        PRInt32 offlineExpungedBytes = 0;
        PRInt32 localExpungedBytes = 0;
        do
        {
          nsCOMPtr<nsIMsgFolder> rootFolder;
          rv = server->GetRootFolder(getter_AddRefs(rootFolder));
          if(NS_SUCCEEDED(rv) && rootFolder)
          {
            rv = server->GetOfflineSupportLevel(&offlineSupportLevel);
-           NS_ENSURE_SUCCESS(rv,rv);
+           NS_ENSURE_SUCCESS(rv, rv);
            nsCOMPtr<nsISupportsArray> allDescendents;
            NS_NewISupportsArray(getter_AddRefs(allDescendents));
            rootFolder->ListDescendents(allDescendents);
-           PRUint32 cnt=0;
+           PRUint32 cnt = 0;
            rv = allDescendents->Count(&cnt);
-           NS_ENSURE_SUCCESS(rv,rv);
+           NS_ENSURE_SUCCESS(rv, rv);
            PRUint32 expungedBytes=0;
            if (offlineSupportLevel > 0)
            {
              PRUint32 flags;
-             for (PRUint32 i=0; i< cnt;i++)
+             for (PRUint32 i = 0; i < cnt; i++)
              {
                nsCOMPtr<nsIMsgFolder> folder = do_QueryElementAt(allDescendents, i);
                expungedBytes = 0;
                folder->GetFlags(&flags);
                if (flags & nsMsgFolderFlags::Offline)
                  folder->GetExpungedBytes(&expungedBytes);
                if (expungedBytes > 0 )
                {
-                 offlineFolderArray->AppendElement(folder);
+                 offlineFolderArray->AppendElement(folder, PR_FALSE);
                  offlineExpungedBytes += expungedBytes;
                }
              }
            }
            else  //pop or local
            {
-             for (PRUint32 i=0; i< cnt;i++)
+             for (PRUint32 i = 0; i < cnt; i++)
              {
                nsCOMPtr<nsIMsgFolder> folder = do_QueryElementAt(allDescendents, i);
                folder->GetExpungedBytes(&expungedBytes);
                if (expungedBytes > 0 )
                {
-                 folderArray->AppendElement(folder);
+                 folderArray->AppendElement(folder, PR_FALSE);
                  localExpungedBytes += expungedBytes;
                }
              }
            }
          }
          server = do_QueryElementAt(allServers, ++serverIndex);
        }
        while (serverIndex < numServers);
@@ -1748,41 +1748,44 @@ nsMsgDBFolder::AutoCompact(nsIMsgWindow 
 
          if (okToCompact)
          {
             nsCOMPtr <nsIAtom> aboutToCompactAtom = do_GetAtom("AboutToCompact");
             NotifyFolderEvent(aboutToCompactAtom);
 
            if ( localExpungedBytes > 0)
            {
-             nsCOMPtr <nsIMsgFolder> msgFolder = do_QueryElementAt(folderArray, 0, &rv);
-             if (msgFolder && NS_SUCCEEDED(rv))
+               nsCOMPtr <nsIMsgFolderCompactor> folderCompactor =  do_CreateInstance(NS_MSGOFFLINESTORECOMPACTOR_CONTRACTID, &rv);
+               NS_ENSURE_SUCCESS(rv, rv);
+
                if (offlineExpungedBytes > 0)
-                 msgFolder->CompactAll(nsnull, aWindow, folderArray, PR_TRUE, offlineFolderArray);
+                 folderCompactor->CompactFolders(folderArray, offlineFolderArray, nsnull, aWindow);
                else
-                 msgFolder->CompactAll(nsnull, aWindow, folderArray, PR_FALSE, nsnull);
+                 folderCompactor->CompactFolders(folderArray, nsnull, nsnull, aWindow);
            }
            else if (offlineExpungedBytes > 0)
-             CompactAllOfflineStores(aWindow, offlineFolderArray);
+             CompactAllOfflineStores(nsnull, aWindow, offlineFolderArray);
          }
        }
      }
    }
   }
   return rv;
 }
 
 NS_IMETHODIMP
-nsMsgDBFolder::CompactAllOfflineStores(nsIMsgWindow *aWindow, nsISupportsArray *aOfflineFolderArray)
-{
-  nsresult rv= NS_OK;
-  nsCOMPtr <nsIMsgFolderCompactor> folderCompactor;
-  folderCompactor = do_CreateInstance(NS_MSGOFFLINESTORECOMPACTOR_CONTRACTID, &rv);
+nsMsgDBFolder::CompactAllOfflineStores(nsIUrlListener *aUrlListener,
+                                       nsIMsgWindow *aWindow,
+                                       nsIArray *aOfflineFolderArray)
+{
+  nsresult rv;
+  nsCOMPtr<nsIMsgFolderCompactor> folderCompactor
+    = folderCompactor = do_CreateInstance(NS_MSGOFFLINESTORECOMPACTOR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
-  return folderCompactor->CompactAll(nsnull, aWindow, PR_TRUE, aOfflineFolderArray);
+  return folderCompactor->CompactFolders(nsnull, aOfflineFolderArray, aUrlListener, aWindow);
 }
 
 nsresult
 nsMsgDBFolder::GetPromptPurgeThreshold(PRBool *aPrompt)
 {
   NS_ENSURE_ARG(aPrompt);
   nsresult rv;
   nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
@@ -3253,17 +3256,17 @@ NS_IMETHODIMP nsMsgDBFolder::AddSubfolde
   return NS_OK;
 }
 
 NS_IMETHODIMP nsMsgDBFolder::Compact(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-NS_IMETHODIMP nsMsgDBFolder::CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow, nsISupportsArray *aFolderArray, PRBool aCompactOfflineAlso, nsISupportsArray *aCompactOfflineArray)
+NS_IMETHODIMP nsMsgDBFolder::CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow, PRBool aCompactOfflineAlso)
 {
   NS_ASSERTION(PR_FALSE, "should be overridden by child class");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP nsMsgDBFolder::EmptyTrash(nsIMsgWindow *msgWindow, nsIUrlListener *aListener)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
--- a/mailnews/base/util/nsMsgDBFolder.h
+++ b/mailnews/base/util/nsMsgDBFolder.h
@@ -148,17 +148,17 @@ protected:
   nsresult AddDirectorySeparator(nsILocalFile *path);
   nsresult CheckIfFolderExists(const nsAString& newFolderName, nsIMsgFolder *parentFolder, nsIMsgWindow *msgWindow);
 
   nsresult PromptForCachePassword(nsIMsgIncomingServer *server, nsIMsgWindow *aWindow, PRBool &passwordCorrect);
   // offline support methods.
   nsresult StartNewOfflineMessage();
   nsresult WriteStartOfNewLocalMessage();
   nsresult EndNewOfflineMessage();
-  nsresult CompactOfflineStore(nsIMsgWindow *inWindow);
+  nsresult CompactOfflineStore(nsIMsgWindow *inWindow, nsIUrlListener *aUrlListener);
   nsresult AutoCompact(nsIMsgWindow *aWindow);
   // this is a helper routine that ignores whether MSG_FLAG_OFFLINE is set for the folder
   nsresult MsgFitsDownloadCriteria(nsMsgKey msgKey, PRBool *result);
   nsresult GetPromptPurgeThreshold(PRBool *aPrompt);
   nsresult GetPurgeThreshold(PRInt32 *aThreshold);
   nsresult ApplyRetentionSettings(PRBool deleteViaFolder);
 
   nsresult PerformBiffNotifications(void); // if there are new, non spam messages, do biff
--- a/mailnews/imap/src/nsImapMailFolder.cpp
+++ b/mailnews/imap/src/nsImapMailFolder.cpp
@@ -1214,55 +1214,60 @@ NS_IMETHODIMP nsImapMailFolder::Compact(
   // with the online compact. Offline stores should get compacted in the
   // background.
   nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv,rv);
 
   return  imapService->Expunge(m_thread, this, aListener, nsnull);
 }
 
-NS_IMETHODIMP nsImapMailFolder::CompactAll(nsIUrlListener *aListener,  nsIMsgWindow *aMsgWindow, nsISupportsArray *aFolderArray,
-                                           PRBool aCompactOfflineAlso, nsISupportsArray *aOfflineFolderArray)
-{
-  NS_ASSERTION(!aOfflineFolderArray, "compacting automatically compacts offline stores");
+NS_IMETHODIMP nsImapMailFolder::CompactAll(nsIUrlListener *aListener,
+                                               nsIMsgWindow *aMsgWindow,
+                                               PRBool aCompactOfflineAlso)
+{
   nsresult rv;
-  nsCOMPtr<nsISupportsArray> folderArray;
-
-  if (!aFolderArray)
-  {
-    nsCOMPtr<nsIMsgFolder> rootFolder;
-    nsCOMPtr<nsISupportsArray> allDescendents;
-    rv = GetRootFolder(getter_AddRefs(rootFolder));
-    if (NS_SUCCEEDED(rv) && rootFolder)
-    {
-      allDescendents = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);
-      NS_ENSURE_TRUE(allDescendents, rv);
-      rootFolder->ListDescendents(allDescendents);
-      PRUint32 cnt =0;
-      rv = allDescendents->Count(&cnt);
-      NS_ENSURE_SUCCESS(rv,rv);
-      folderArray = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);
-      NS_ENSURE_TRUE(folderArray, rv);
-      for (PRUint32 i=0; i < cnt;i++)
-      {
-        nsCOMPtr<nsISupports> supports = getter_AddRefs(allDescendents->ElementAt(i));
-        nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(supports, &rv);
-        NS_ENSURE_SUCCESS(rv,rv);
-        rv = folderArray->AppendElement(supports);
-      }
-      rv = folderArray->Count(&cnt);
-      NS_ENSURE_SUCCESS(rv,rv);
-      if (cnt == 0)
-        return NotifyCompactCompleted();
-    }
-  }
-  nsCOMPtr <nsIMsgFolderCompactor> folderCompactor =  do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
+  nsCOMPtr<nsIMutableArray> folderArray, offlineFolderArray;
+
+  nsCOMPtr<nsIMsgFolder> rootFolder;
+  nsCOMPtr<nsISupportsArray> allDescendents;
+  rv = GetRootFolder(getter_AddRefs(rootFolder));
+  if (NS_SUCCEEDED(rv) && rootFolder)
+  {
+    allDescendents = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);
+    NS_ENSURE_TRUE(allDescendents, rv);
+    rootFolder->ListDescendents(allDescendents);
+    PRUint32 cnt = 0;
+    rv = allDescendents->Count(&cnt);
+    NS_ENSURE_SUCCESS(rv, rv);
+    folderArray = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+    NS_ENSURE_TRUE(folderArray, rv);
+    if (aCompactOfflineAlso)
+    {
+      offlineFolderArray = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+      NS_ENSURE_TRUE(offlineFolderArray, rv);
+    }
+    for (PRUint32 i = 0; i < cnt; i++)
+    {
+      nsCOMPtr<nsISupports> supports = dont_AddRef(allDescendents->ElementAt(i));
+      nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(supports, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = folderArray->AppendElement(supports, PR_FALSE);
+      if (aCompactOfflineAlso)
+        offlineFolderArray->AppendElement(supports, PR_FALSE);
+    }
+    rv = folderArray->GetLength(&cnt);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (cnt == 0)
+      return NotifyCompactCompleted();
+  }
+  nsCOMPtr <nsIMsgFolderCompactor> folderCompactor =
+    do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
-  return folderCompactor->CompactAll(aFolderArray ? aFolderArray : folderArray.get(), aMsgWindow,
-                                     aCompactOfflineAlso, aOfflineFolderArray);
+  return folderCompactor->CompactFolders(folderArray, offlineFolderArray,
+                                         aListener, aMsgWindow);
 }
 
 NS_IMETHODIMP nsImapMailFolder::UpdateStatus(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow)
 {
   nsresult rv;
   nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv,rv);
 
--- a/mailnews/imap/src/nsImapMailFolder.h
+++ b/mailnews/imap/src/nsImapMailFolder.h
@@ -65,16 +65,17 @@
 #include "nsDataHashtable.h"
 #include "nsIMutableArray.h"
 #include "nsITimer.h"
 #include "nsCOMArray.h"
 #include "nsAutoSyncState.h"
 
 class nsImapMoveCoalescer;
 class nsIMsgIdentity;
+class nsIMsgOfflineImapOperation;
 
 #define COPY_BUFFER_SIZE 16384
 
 /* b64534f0-3d53-11d3-ac2a-00805f8ac968 */
 
 #define NS_IMAPMAILCOPYSTATE_IID \
 { 0xb64534f0, 0x3d53, 0x11d3, \
     { 0xac, 0x2a, 0x00, 0x80, 0x5f, 0x8a, 0xc9, 0x68 } }
@@ -236,18 +237,18 @@ public:
   NS_IMETHOD GetMessages(nsIMsgWindow *aMsgWindow, nsISimpleEnumerator* *result);
   NS_IMETHOD UpdateFolder(nsIMsgWindow *aWindow);
 
   NS_IMETHOD CreateSubfolder(const nsAString& folderName,nsIMsgWindow *msgWindow );
   NS_IMETHOD AddSubfolder(const nsAString& aName, nsIMsgFolder** aChild);
   NS_IMETHODIMP CreateStorageIfMissing(nsIUrlListener* urlListener);
 
   NS_IMETHOD Compact(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow);
-  NS_IMETHOD CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow, nsISupportsArray *aFolderArray,
-                        PRBool aCompactOfflineAlso, nsISupportsArray *aOfflineFolderArray);
+  NS_IMETHOD CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow,
+                        PRBool aCompactOfflineAlso);
   NS_IMETHOD EmptyTrash(nsIMsgWindow *msgWindow, nsIUrlListener *aListener);
   NS_IMETHOD CopyDataToOutputStreamForAppend(nsIInputStream *aIStream,
                      PRInt32 aLength, nsIOutputStream *outputStream);
   NS_IMETHOD CopyDataDone();
   NS_IMETHOD Delete ();
   NS_IMETHOD Rename (const nsAString& newName, nsIMsgWindow *msgWindow);
   NS_IMETHOD RenameSubFolders(nsIMsgWindow *msgWindow, nsIMsgFolder *oldFolder);
   NS_IMETHOD GetNoSelect(PRBool *aResult);
--- a/mailnews/local/src/nsLocalMailFolder.cpp
+++ b/mailnews/local/src/nsLocalMailFolder.cpp
@@ -826,76 +826,72 @@ nsMsgLocalMailFolder::CreateSubfolderInt
     child->SetPrettyName(folderName);  //because empty trash will create a new trash folder
     NotifyItemAdded(child);
     if (aNewFolder)
       child.swap(*aNewFolder);
   }
   return rv;
 }
 
-NS_IMETHODIMP nsMsgLocalMailFolder::CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow, nsISupportsArray *aFolderArray,
-                                               PRBool aCompactOfflineAlso, nsISupportsArray *aOfflineFolderArray)
+NS_IMETHODIMP nsMsgLocalMailFolder::CompactAll(nsIUrlListener *aListener,
+                                               nsIMsgWindow *aMsgWindow,
+                                               PRBool aCompactOfflineAlso)
 {
   nsresult rv = NS_OK;
-  nsCOMPtr<nsISupportsArray> folderArray;
-  if (!aFolderArray)
+  nsCOMPtr<nsIMutableArray> folderArray;
+  nsCOMPtr<nsIMsgFolder> rootFolder;
+  nsCOMPtr<nsISupportsArray> allDescendents;
+  rv = GetRootFolder(getter_AddRefs(rootFolder));
+  if (NS_SUCCEEDED(rv) && rootFolder)
   {
-    nsCOMPtr<nsIMsgFolder> rootFolder;
-    nsCOMPtr<nsISupportsArray> allDescendents;
-    rv = GetRootFolder(getter_AddRefs(rootFolder));
-    if (NS_SUCCEEDED(rv) && rootFolder)
+    NS_NewISupportsArray(getter_AddRefs(allDescendents));
+    rootFolder->ListDescendents(allDescendents);
+    PRUint32 cnt =0;
+    rv = allDescendents->Count(&cnt);
+    NS_ENSURE_SUCCESS(rv, rv);
+    folderArray = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+    PRUint32 expungedBytes = 0;
+    for (PRUint32 i = 0; i < cnt; i++)
     {
-      NS_NewISupportsArray(getter_AddRefs(allDescendents));
-      rootFolder->ListDescendents(allDescendents);
-      PRUint32 cnt =0;
-      rv = allDescendents->Count(&cnt);
-      NS_ENSURE_SUCCESS(rv,rv);
-      NS_NewISupportsArray(getter_AddRefs(folderArray));
-      PRUint32 expungedBytes=0;
-      for (PRUint32 i = 0; i < cnt; i++)
-      {
-        nsCOMPtr<nsISupports> supports = getter_AddRefs(allDescendents->ElementAt(i));
-        nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(supports, &rv);
-        NS_ENSURE_SUCCESS(rv,rv);
-
-        expungedBytes=0;
-        if (folder)
-          rv = folder->GetExpungedBytes(&expungedBytes);
-
-        NS_ENSURE_SUCCESS(rv,rv);
-
-        if (expungedBytes > 0)
-          rv = folderArray->AppendElement(supports);
-      }
-      rv = folderArray->Count(&cnt);
-      NS_ENSURE_SUCCESS(rv,rv);
-      if (cnt == 0 )
-        return NotifyCompactCompleted();
+      nsCOMPtr<nsISupports> supports = dont_AddRef(allDescendents->ElementAt(i));
+      nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(supports, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      expungedBytes = 0;
+      if (folder)
+        rv = folder->GetExpungedBytes(&expungedBytes);
+
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (expungedBytes > 0)
+        rv = folderArray->AppendElement(supports, PR_FALSE);
     }
+    rv = folderArray->GetLength(&cnt);
+    NS_ENSURE_SUCCESS(rv,rv);
+    if (cnt == 0)
+      return NotifyCompactCompleted();
   }
   nsCOMPtr <nsIMsgFolderCompactor> folderCompactor =  do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
-  if (aFolderArray)
-     rv = folderCompactor->CompactAll(aFolderArray, aMsgWindow, aCompactOfflineAlso, aOfflineFolderArray);
-  else if (folderArray)
-     rv = folderCompactor->CompactAll(folderArray, aMsgWindow, aCompactOfflineAlso, aOfflineFolderArray);
-  return rv;
+  return folderCompactor->CompactFolders(folderArray,
+                                      nsnull,
+                                      aListener, aMsgWindow);
 }
 
 NS_IMETHODIMP nsMsgLocalMailFolder::Compact(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow)
 {
   nsresult rv;
   nsCOMPtr <nsIMsgFolderCompactor> folderCompactor =  do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRUint32 expungedBytes = 0;
   GetExpungedBytes(&expungedBytes);
   // check if we need to compact the folder
   if (expungedBytes > 0)
-    rv = folderCompactor->Compact(this, PR_FALSE, aMsgWindow);
+    rv = folderCompactor->Compact(this, PR_FALSE, aListener, aMsgWindow);
   else
     rv = NotifyCompactCompleted();
   return rv;
 }
 
 NS_IMETHODIMP nsMsgLocalMailFolder::EmptyTrash(nsIMsgWindow *msgWindow,
                                                nsIUrlListener *aListener)
 {
--- a/mailnews/local/src/nsLocalMailFolder.h
+++ b/mailnews/local/src/nsLocalMailFolder.h
@@ -142,17 +142,17 @@ public:
   NS_IMETHOD OnAnnouncerGoingAway(nsIDBChangeAnnouncer *instigator);
   NS_IMETHOD GetMessages(nsIMsgWindow *aMsgWindow, nsISimpleEnumerator* *result);
   NS_IMETHOD UpdateFolder(nsIMsgWindow *aWindow);
 
   NS_IMETHOD CreateSubfolder(const nsAString& folderName ,nsIMsgWindow *msgWindow);
   NS_IMETHOD AddSubfolder(const nsAString& folderName, nsIMsgFolder** newFolder);
 
   NS_IMETHOD Compact(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow);
-  NS_IMETHOD CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow, nsISupportsArray *aFolderArray, PRBool aCompactOfflineAlso, nsISupportsArray *aOfflineFolderArray);
+  NS_IMETHOD CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow, PRBool aCompactOfflineAlso);
   NS_IMETHOD EmptyTrash(nsIMsgWindow *msgWindow, nsIUrlListener *aListener);
   NS_IMETHOD Delete ();
   NS_IMETHOD DeleteSubFolders(nsIArray *folders, nsIMsgWindow *msgWindow);
   NS_IMETHOD CreateStorageIfMissing(nsIUrlListener* urlListener);
   NS_IMETHOD Rename (const nsAString& aNewName, nsIMsgWindow *msgWindow);
   NS_IMETHOD RenameSubFolders (nsIMsgWindow *msgWindow, nsIMsgFolder *oldFolder);
 
   NS_IMETHOD GetPrettyName(nsAString& prettyName); // Override of the base, for top-level mail folder
--- a/mailnews/test/data/bugmail11
+++ b/mailnews/test/data/bugmail11
@@ -1,11 +1,12 @@
 From - Mon Jun 02 19:00:00 2008
 X-Mozilla-Status: 0001
 X-Mozilla-Status2: 00000000
+X-Mozilla-Keys:                                                                                 
 Return-path: <example@example.com>
 Delivered-To: bugmail@example.org
 Received: by 10.114.166.12 with SMTP id o12cs163262wae;
         Fri, 11 Apr 2008 07:17:31 -0700 (PDT)
 Received: by 10.115.60.1 with SMTP id n1mr214763wak.181.1207923450166;
         Fri, 11 Apr 2008 07:17:30 -0700 (PDT)
 Return-Path: <bugzilla-daemon@mozilla.org>
 Received: from webapp-out.mozilla.org (webapp01.sj.mozilla.com [63.245.208.146])