Bug 1612239 - Remove nsIArray usage from various calls related to message deletion. r=mkmelin
authorBen Campbell <benc@thunderbird.net>
Sun, 19 Apr 2020 12:52:38 +0300
changeset 38879 c1a719a87041c69ccbea9039467e3232f1a49048
parent 38878 ba6ccb3d8aec28505ace35a9e59c86089f38859b
child 38880 dcb1390f000bb09967f66999cb236667c30c5ec1
push id401
push userclokep@gmail.com
push dateMon, 01 Jun 2020 20:41:59 +0000
reviewersmkmelin
bugs1612239
Bug 1612239 - Remove nsIArray usage from various calls related to message deletion. r=mkmelin
mail/components/activity/modules/moveCopy.jsm
mail/components/search/SearchIntegration.jsm
mailnews/base/public/nsIMsgFolderListener.idl
mailnews/base/public/nsIMsgFolderNotificationService.idl
mailnews/base/public/nsIMsgPluggableStore.idl
mailnews/base/src/nsMsgFolderNotificationService.cpp
mailnews/base/test/unit/test_nsIMsgFolderListener.js
mailnews/base/util/nsMsgUtils.cpp
mailnews/base/util/nsMsgUtils.h
mailnews/db/gloda/modules/IndexMsg.jsm
mailnews/imap/src/nsImapMailFolder.cpp
mailnews/imap/src/nsImapMailFolder.h
mailnews/local/src/nsLocalMailFolder.cpp
mailnews/local/src/nsMsgBrkMBoxStore.cpp
mailnews/local/src/nsMsgBrkMBoxStore.h
mailnews/local/src/nsMsgMaildirStore.cpp
mailnews/local/src/nsRssIncomingServer.cpp
mailnews/news/src/nsNewsFolder.cpp
mailnews/test/resources/folderEventLogHelper.js
mailnews/test/resources/msgFolderListenerSetup.js
--- a/mail/components/activity/modules/moveCopy.jsm
+++ b/mail/components/activity/modules/moveCopy.jsm
@@ -54,24 +54,23 @@ var moveCopyModule = {
     }
   },
 
   msgAdded(aMsg) {},
 
   msgsDeleted(aMsgList) {
     this.log.info("in msgsDeleted");
 
-    let count = aMsgList.length;
-    if (count <= 0) {
+    if (aMsgList.length <= 0) {
       return;
     }
 
-    let displayCount = count;
+    let displayCount = aMsgList.length;
     // get the folder of the deleted messages
-    let folder = aMsgList.queryElementAt(0, Ci.nsIMsgDBHdr).folder;
+    let folder = aMsgList[0].folder;
 
     let activities = this.activityMgr.getActivities();
     if (
       activities.length > 0 &&
       activities[activities.length - 1].id == this.lastMessage.id &&
       this.lastMessage.type == "deleteMail" &&
       this.lastMessage.folder == folder.prettyName
     ) {
@@ -98,18 +97,17 @@ var moveCopyModule = {
       statusText,
       Date.now(), // start time
       Date.now()
     ); // completion time
 
     event.iconClass = "deleteMail";
     this.lastMessage.type = event.iconClass;
 
-    for (let i = 0; i < count; i++) {
-      let msgHdr = aMsgList.queryElementAt(i, Ci.nsIMsgDBHdr);
+    for (let msgHdr of aMsgList) {
       event.addSubject(msgHdr.messageId);
     }
 
     this.lastMessage.id = this.activityMgr.addActivity(event);
   },
 
   msgsMoveCopyCompleted(aMove, aSrcMsgList, aDestFolder) {
     try {
--- a/mail/components/search/SearchIntegration.jsm
+++ b/mail/components/search/SearchIntegration.jsm
@@ -602,21 +602,18 @@ var SearchSupport = {
           aMsg,
           SearchIntegration._getLastReindexTime(aMsg.folder)
         );
       }
     },
 
     msgsDeleted(aMsgs) {
       SearchIntegration._log.info("in msgsDeleted");
-      let count = aMsgs.length;
-      for (let i = 0; i < count; i++) {
-        let file = SearchIntegration._getSupportFile(
-          aMsgs.queryElementAt(i, Ci.nsIMsgDBHdr)
-        );
+      for (let msgHdr of aMsgs) {
+        let file = SearchIntegration._getSupportFile(msgHdr);
         if (file.exists()) {
           file.remove(false);
         }
       }
     },
 
     msgsMoveCopyCompleted(aMove, aSrcMsgs, aDestFolder) {
       SearchIntegration._log.info("in msgsMoveCopyCompleted, aMove = " + aMove);
--- a/mailnews/base/public/nsIMsgFolderListener.idl
+++ b/mailnews/base/public/nsIMsgFolderListener.idl
@@ -77,17 +77,17 @@ interface nsIMsgFolderListener : nsISupp
    * @note
    * "Deleting" to a trash folder is actually a move, and is covered by msgsMoveCopyCompleted()
    *
    * @note
    * If the user has selected the IMAP delete model (marking messages as deleted, then purging them
    * later) for an IMAP account, this notification will not take place on the delete. This will only
    * take place on the purge.
    */
-  void msgsDeleted(in nsIArray aMsgs);
+  void msgsDeleted(in Array<nsIMsgDBHdr> aMsgs);
 
   /**
    * Notified after a command to move or copy a group of messages completes. In
    * case of a move, this is before the messages have been deleted from the
    * source folder.
    *
    * @param aMove true if a move, false if a copy
    * @param aSrcMsgs An array of the message headers in the source folder
--- a/mailnews/base/public/nsIMsgFolderNotificationService.idl
+++ b/mailnews/base/public/nsIMsgFolderNotificationService.idl
@@ -57,17 +57,17 @@ interface nsIMsgFolderNotificationServic
   void removeListener(in nsIMsgFolderListener aListener);
 
   // message-specific functions
   // single message for added, array for delete/move/copy
   void notifyMsgAdded(in nsIMsgDBHdr aMsg);
   void notifyMsgsClassified(in nsIArray aMsgs,
                             in boolean aJunkProcessed,
                             in boolean aTraitProcessed);
-  void notifyMsgsDeleted(in nsIArray aMsgs);
+  void notifyMsgsDeleted(in Array<nsIMsgDBHdr> aMsgs);
   void notifyMsgsMoveCopyCompleted(in boolean aMove,
                                    in nsIArray aSrcMsgs,
                                    in nsIMsgFolder aDestFolder,
                                    in nsIArray aDestMsgs);
 
   /**
    * Notify listeners that the msg key for a header has changed. Currently,
    * this is used when we create a header for an offline imap move result,
--- a/mailnews/base/public/nsIMsgPluggableStore.idl
+++ b/mailnews/base/public/nsIMsgPluggableStore.idl
@@ -191,17 +191,17 @@ interface nsIMsgPluggableStore : nsISupp
                                    [optional] in nsIMsgDBHdr aHdr,
                                    [optional] out boolean aReusable);
 
   /**
    * Delete the passed in messages. These message should all be in the
    * same folder.
    * @param aHdrArray array of nsIMsgDBHdr's.
    */
-  void deleteMessages(in nsIArray aHdrArray);
+  void deleteMessages(in Array<nsIMsgDBHdr> aHdrArray);
 
   /**
    * This allows the store to handle a msg move/copy if it wants. This lets
    * it optimize move/copies within the same store. E.g., for maildir, a
    * msg move mostly entails moving the file containing the message, and
    * updating the db. If the store does not want to implement this, the core
    * code will use getMsgInputStream on the source message,
    * getNewMsgOutputStream for the dest message, and stream the input to
@@ -304,17 +304,17 @@ interface nsIMsgPluggableStore : nsISupp
                     in nsIMsgWindow aMsgWindow, in nsIUrlListener aListener);
 
   /**
    * Sets/Clears the passed flags on the passed messages.
    * @param aHdrArray array of nsIMsgDBHdr's
    * @param aFlags flags to set/clear
    * @param aSet true to set the flag(s), false to clear.
    */
-  void changeFlags(in nsIArray aHdrArray, in unsigned long aFlags,
+  void changeFlags(in Array<nsIMsgDBHdr> aHdrArray, in unsigned long aFlags,
                    in boolean aSet);
   /**
    *Sets/Clears the passed keywords on the passed messages.
    * @param aHdrArray array of nsIMsgDBHdr's
    * @param aKeywords keywords to set/clear
    * @param aAdd true to add the keyword(s), false to remove.
    */
   void changeKeywords(in nsIArray aHdrArray, in ACString aKeywords,
--- a/mailnews/base/src/nsMsgFolderNotificationService.cpp
+++ b/mailnews/base/src/nsMsgFolderNotificationService.cpp
@@ -66,17 +66,17 @@ NS_IMETHODIMP nsMsgFolderNotificationSer
 NS_IMETHODIMP nsMsgFolderNotificationService::NotifyMsgsClassified(
     nsIArray *aMsgs, bool aJunkProcessed, bool aTraitProcessed) {
   NOTIFY_MSGFOLDER_LISTENERS(msgsClassified, MsgsClassified,
                              (aMsgs, aJunkProcessed, aTraitProcessed));
   return NS_OK;
 }
 
 NS_IMETHODIMP nsMsgFolderNotificationService::NotifyMsgsDeleted(
-    nsIArray *aMsgs) {
+    const nsTArray<RefPtr<nsIMsgDBHdr>> &aMsgs) {
   NOTIFY_MSGFOLDER_LISTENERS(msgsDeleted, MsgsDeleted, (aMsgs));
   return NS_OK;
 }
 
 NS_IMETHODIMP nsMsgFolderNotificationService::NotifyMsgsMoveCopyCompleted(
     bool aMove, nsIArray *aSrcMsgs, nsIMsgFolder *aDestFolder,
     nsIArray *aDestMsgs) {
   uint32_t count = mListeners.Length();
--- a/mailnews/base/test/unit/test_nsIMsgFolderListener.js
+++ b/mailnews/base/test/unit/test_nsIMsgFolderListener.js
@@ -113,17 +113,17 @@ gMFListener.prototype = {
       MailServices.mfn.removeListener(this);
     }
   },
 };
 
 function NotifyMsgFolderListeners() {
   MailServices.mfn.notifyMsgAdded(null);
   MailServices.mfn.notifyMsgsClassified(null, null, null);
-  MailServices.mfn.notifyMsgsDeleted(null);
+  MailServices.mfn.notifyMsgsDeleted([]);
   MailServices.mfn.notifyMsgsMoveCopyCompleted(null, null, null, null);
   MailServices.mfn.notifyMsgKeyChanged(null, null);
   MailServices.mfn.notifyFolderAdded(null);
   MailServices.mfn.notifyFolderDeleted(null);
   MailServices.mfn.notifyFolderMoveCopyCompleted(null, null, null);
   MailServices.mfn.notifyFolderRenamed(null, null);
   MailServices.mfn.notifyItemEvent(null, null, null, null);
 }
--- a/mailnews/base/util/nsMsgUtils.cpp
+++ b/mailnews/base/util/nsMsgUtils.cpp
@@ -1499,16 +1499,41 @@ NS_MSG_BASE nsresult MsgGetHeadersFromKe
 
       aHeaders->AppendElement(msgHdr);
     }
   }
 
   return rv;
 }
 
+// Stopgap while we complete removal of nsIArray usage (Bug 1583030).
+// Then this function can replace MsgGetHeadersFromKeys().
+NS_MSG_BASE nsresult
+MsgGetHeadersFromKeys2(nsIMsgDatabase *aDB, const nsTArray<nsMsgKey> &aMsgKeys,
+                       nsTArray<RefPtr<nsIMsgDBHdr>> &aHeaders) {
+  NS_ENSURE_ARG_POINTER(aDB);
+  aHeaders.Clear();
+  aHeaders.SetCapacity(aMsgKeys.Length());
+
+  for (auto key : aMsgKeys) {
+    // This function silently skips when the key is not found. This is an
+    // expected case.
+    bool hasKey;
+    nsresult rv = aDB->ContainsKey(key, &hasKey);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (hasKey) {
+      nsCOMPtr<nsIMsgDBHdr> msgHdr;
+      rv = aDB->GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
+      NS_ENSURE_SUCCESS(rv, rv);
+      aHeaders.AppendElement(msgHdr);
+    }
+  }
+  return NS_OK;
+}
+
 bool MsgAdvanceToNextLine(const char *buffer, uint32_t &bufferOffset,
                           uint32_t maxBufferOffset) {
   bool result = false;
   for (; bufferOffset < maxBufferOffset; bufferOffset++) {
     if (buffer[bufferOffset] == '\r' || buffer[bufferOffset] == '\n') {
       bufferOffset++;
       if (buffer[bufferOffset - 1] == '\r' && buffer[bufferOffset] == '\n')
         bufferOffset++;
--- a/mailnews/base/util/nsMsgUtils.h
+++ b/mailnews/base/util/nsMsgUtils.h
@@ -242,16 +242,22 @@ NS_MSG_BASE nsresult MsgEscapeURL(const 
                                   nsACString &aResult);
 
 // Converts an nsTArray of nsMsgKeys plus a database, to an array of
 // nsIMsgDBHdrs.
 NS_MSG_BASE nsresult MsgGetHeadersFromKeys(nsIMsgDatabase *aDB,
                                            const nsTArray<nsMsgKey> &aKeys,
                                            nsIMutableArray *aHeaders);
 
+// Stopgap while we complete removal of nsIArray usage (Bug 1583030).
+// Then this function can replace MsgGetHeadersFromKeys().
+NS_MSG_BASE nsresult
+MsgGetHeadersFromKeys2(nsIMsgDatabase *aDB, const nsTArray<nsMsgKey> &aKeys,
+                       nsTArray<RefPtr<nsIMsgDBHdr>> &aHeaders);
+
 NS_MSG_BASE nsresult MsgExamineForProxyAsync(nsIChannel *channel,
                                              nsIProtocolProxyCallback *listener,
                                              nsICancelable **result);
 
 NS_MSG_BASE int32_t MsgFindCharInSet(const nsCString &aString,
                                      const char *aChars, uint32_t aOffset = 0);
 NS_MSG_BASE int32_t MsgFindCharInSet(const nsString &aString,
                                      const char *aChars, uint32_t aOffset = 0);
--- a/mailnews/db/gloda/modules/IndexMsg.jsm
+++ b/mailnews/db/gloda/modules/IndexMsg.jsm
@@ -2361,18 +2361,17 @@ var GlodaMsgIndexer = {
      *  should perform a deletion pass.  If it turns out the messages are coming
      *  back, the fact that deletion is thus deferred can be handy, as we can
      *  reuse the existing gloda message.
      */
     msgsDeleted(aMsgHdrs) {
       this.indexer._log.debug("msgsDeleted notification");
       let glodaMessageIds = [];
 
-      for (let iMsgHdr = 0; iMsgHdr < aMsgHdrs.length; iMsgHdr++) {
-        let msgHdr = aMsgHdrs.queryElementAt(iMsgHdr, Ci.nsIMsgDBHdr);
+      for (let msgHdr of aMsgHdrs) {
         let [glodaId, glodaDirty] = PendingCommitTracker.getGlodaState(msgHdr);
         if (
           glodaId >= GLODA_FIRST_VALID_MESSAGE_ID &&
           glodaDirty != GlodaMsgIndexer.kMessageFilthy
         ) {
           glodaMessageIds.push(glodaId);
         }
       }
@@ -2433,17 +2432,17 @@ var GlodaMsgIndexer = {
       this.indexer._log.debug("MoveCopy notification.  Move: " + aMove);
       try {
         // ---- Move
         if (aMove) {
           // -- Effectively a deletion?
           // If the destination folder is not indexed, it's like these messages
           //  are being deleted.
           if (!GlodaMsgIndexer.shouldIndexFolder(aDestFolder)) {
-            this.msgsDeleted(aSrcMsgHdrs);
+            this.msgsDeleted([...fixIterator(aSrcMsgHdrs, Ci.nsIMsgDBHdr)]);
             return;
           }
 
           // -- Avoid propagation of filthy gloda-id's.
           // If the source folder is filthy or should not be indexed (and so
           //  any gloda-id's found in there are gibberish), our only job is to
           //  strip the gloda-id's off of all the destination headers because
           //  none of the gloda-id's are valid (and so we certainly don't want
--- a/mailnews/imap/src/nsImapMailFolder.cpp
+++ b/mailnews/imap/src/nsImapMailFolder.cpp
@@ -1996,16 +1996,26 @@ nsresult nsImapMailFolder::MarkMessagesI
     db->MarkImapDeleted(key, deleted, nullptr);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP nsImapMailFolder::DeleteMessages(
     nsIArray *messages, nsIMsgWindow *msgWindow, bool deleteStorage,
     bool isMove, nsIMsgCopyServiceListener *listener, bool allowUndo) {
+  // Stopgap. Build a parallel array of message headers while we complete
+  // removal of nsIArray usage (Bug 1583030).
+  uint32_t messageCount;
+  messages->GetLength(&messageCount);
+  nsTArray<RefPtr<nsIMsgDBHdr>> msgHeaders;
+  msgHeaders.SetCapacity(messageCount);
+  for (uint32_t i = 0; i < messageCount; ++i) {
+    msgHeaders.AppendElement(do_QueryElementAt(messages, i));
+  }
+
   // *** jt - assuming delete is move to the trash folder for now
   nsAutoCString uri;
   bool deleteImmediatelyNoTrash = false;
   nsAutoCString messageIds;
   nsTArray<nsMsgKey> srcKeyArray;
   bool deleteMsgs = true;  // used for toggling delete status - default is true
   nsMsgImapDeleteModel deleteModel = nsMsgImapDeleteModels::MoveToTrash;
   imapMessageFlagsType messageFlags = kImapMsgDeletedFlag;
@@ -2089,19 +2099,19 @@ NS_IMETHODIMP nsImapMailFolder::DeleteMe
           MarkMessagesImapDeleted(&srcKeyArray, deleteMsgs, database);
         else {
           EnableNotifications(allMessageCountNotifications,
                               false);  //"remove it immediately" model
           // Notify if this is an actual delete.
           if (!isMove) {
             nsCOMPtr<nsIMsgFolderNotificationService> notifier(
                 do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
-            if (notifier) notifier->NotifyMsgsDeleted(messages);
+            if (notifier) notifier->NotifyMsgsDeleted(msgHeaders);
           }
-          DeleteStoreMessages(messages);
+          DeleteStoreMessages(msgHeaders);
           database->DeleteMessages(srcKeyArray, nullptr);
           EnableNotifications(allMessageCountNotifications, true);
         }
         if (listener) {
           listener->OnStartCopy();
           listener->OnStopCopy(NS_OK);
         }
         NotifyFolderEvent(kDeleteOrMoveMsgCompleted);
@@ -2109,18 +2119,16 @@ NS_IMETHODIMP nsImapMailFolder::DeleteMe
     }
     return rv;
   }
 
   // have to move the messages to the trash
   if (trashFolder) {
     nsCOMPtr<nsIMsgFolder> srcFolder;
     nsCOMPtr<nsISupports> srcSupport;
-    uint32_t count = 0;
-    rv = messages->GetLength(&count);
 
     rv = QueryInterface(NS_GET_IID(nsIMsgFolder), getter_AddRefs(srcFolder));
     nsCOMPtr<nsIMsgCopyService> copyService =
         do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = copyService->CopyMessages(srcFolder, messages, trashFolder, true,
                                    listener, msgWindow, allowUndo);
   }
@@ -2561,24 +2569,21 @@ NS_IMETHODIMP nsImapMailFolder::UpdateIm
     existingKeys.Sort();
     FindKeysToDelete(existingKeys, keysToDelete, flagState, boxFlags);
     // if this is the result of an expunge then don't grab headers
     if (!(boxFlags & kJustExpunged))
       FindKeysToAdd(existingKeys, m_keysToFetch, numNewUnread, flagState);
   }
   m_totalKeysToFetch = m_keysToFetch.Length();
   if (!keysToDelete.IsEmpty() && mDatabase) {
-    nsCOMPtr<nsIMutableArray> hdrsToDelete(
-        do_CreateInstance(NS_ARRAY_CONTRACTID));
-    MsgGetHeadersFromKeys(mDatabase, keysToDelete, hdrsToDelete);
+    nsTArray<RefPtr<nsIMsgDBHdr>> hdrsToDelete;
+    MsgGetHeadersFromKeys2(mDatabase, keysToDelete, hdrsToDelete);
     // Notify nsIMsgFolderListeners of a mass delete, but only if we actually
     // have headers
-    uint32_t numHdrs;
-    hdrsToDelete->GetLength(&numHdrs);
-    if (numHdrs) {
+    if (!hdrsToDelete.IsEmpty()) {
       nsCOMPtr<nsIMsgFolderNotificationService> notifier(
           do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
       if (notifier) notifier->NotifyMsgsDeleted(hdrsToDelete);
     }
     DeleteStoreMessages(hdrsToDelete);
     EnableNotifications(nsIMsgFolder::allMessageCountNotifications, false);
     mDatabase->DeleteMessages(keysToDelete, nullptr);
     EnableNotifications(nsIMsgFolder::allMessageCountNotifications, true);
@@ -4933,18 +4938,30 @@ nsImapMailFolder::OnStopRunningUrl(nsIUR
           do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
       if (notifier && m_copyState) {
         if (imapAction == nsIImapUrl::nsImapOnlineMove)
           notifier->NotifyMsgsMoveCopyCompleted(true, m_copyState->m_messages,
                                                 this, nullptr);
         else if (imapAction == nsIImapUrl::nsImapOnlineCopy)
           notifier->NotifyMsgsMoveCopyCompleted(false, m_copyState->m_messages,
                                                 this, nullptr);
-        else if (imapAction == nsIImapUrl::nsImapDeleteMsg)
-          notifier->NotifyMsgsDeleted(m_copyState->m_messages);
+        else if (imapAction == nsIImapUrl::nsImapDeleteMsg) {
+          // Stopgap. Build a parallel array of message headers while we
+          // complete removal of nsIArray usage (Bug 1583030).
+          uint32_t messageCount;
+          m_copyState->m_messages->GetLength(&messageCount);
+          nsTArray<RefPtr<nsIMsgDBHdr>> msgHeaders;
+          msgHeaders.SetCapacity(messageCount);
+          for (uint32_t i = 0; i < messageCount; ++i) {
+            msgHeaders.AppendElement(
+                do_QueryElementAt(m_copyState->m_messages, i));
+          }
+
+          notifier->NotifyMsgsDeleted(msgHeaders);
+        }
       }
 
       switch (imapAction) {
         case nsIImapUrl::nsImapDeleteMsg:
         case nsIImapUrl::nsImapOnlineMove:
         case nsIImapUrl::nsImapOnlineCopy:
           if (NS_SUCCEEDED(aExitCode)) {
             if (folderOpen)
@@ -5072,40 +5089,36 @@ nsImapMailFolder::OnStopRunningUrl(nsIUR
                 nsTArray<nsMsgKey> keyArray;
                 nsCString keyString;
                 imapUrl->GetListOfMessageIds(keyString);
                 ParseUidString(keyString.get(), keyArray);
 
                 // For pluggable stores that do not support compaction, we need
                 // to delete the messages now.
                 bool supportsCompaction = false;
-                uint32_t numHdrs = 0;
                 nsCOMPtr<nsIMsgPluggableStore> offlineStore;
                 (void)GetMsgStore(getter_AddRefs(offlineStore));
                 if (offlineStore)
                   offlineStore->GetSupportsCompaction(&supportsCompaction);
 
-                nsCOMPtr<nsIMutableArray> msgHdrs;
+                nsTArray<RefPtr<nsIMsgDBHdr>> msgHdrs;
                 if (notifier || !supportsCompaction) {
-                  msgHdrs = do_CreateInstance(NS_ARRAY_CONTRACTID);
-                  NS_ENSURE_STATE(msgHdrs);
-                  MsgGetHeadersFromKeys(db, keyArray, msgHdrs);
-                  msgHdrs->GetLength(&numHdrs);
+                  MsgGetHeadersFromKeys2(db, keyArray, msgHdrs);
                 }
 
                 // Notify listeners of delete.
-                if (notifier && numHdrs) {
+                if (notifier && !msgHdrs.IsEmpty()) {
                   // XXX Currently, the DeleteMessages below gets executed twice
                   // on deletes. Once in DeleteMessages, once here. The second
                   // time, it silently fails to delete. This is why we're also
                   // checking whether the array is empty.
                   notifier->NotifyMsgsDeleted(msgHdrs);
                 }
 
-                if (!supportsCompaction && numHdrs)
+                if (!supportsCompaction && !msgHdrs.IsEmpty())
                   DeleteStoreMessages(msgHdrs);
 
                 db->DeleteMessages(keyArray, nullptr);
                 db->SetSummaryValid(true);
                 db->Commit(nsMsgDBCommitType::kLargeCommit);
               }
             }
           }
@@ -9024,47 +9037,48 @@ NS_IMETHODIMP nsImapMailFolder::GetOffli
   return NS_OK;
 }
 
 NS_IMETHODIMP nsImapMailFolder::GetIncomingServerType(nsACString &serverType) {
   serverType.AssignLiteral("imap");
   return NS_OK;
 }
 
-void nsImapMailFolder::DeleteStoreMessages(nsIArray *aMessages) {
+void nsImapMailFolder::DeleteStoreMessages(
+    const nsTArray<RefPtr<nsIMsgDBHdr>> &aMessages) {
   // Delete messages for pluggable stores that do not support compaction.
   nsCOMPtr<nsIMsgPluggableStore> offlineStore;
   (void)GetMsgStore(getter_AddRefs(offlineStore));
 
   if (offlineStore) {
     bool supportsCompaction;
     offlineStore->GetSupportsCompaction(&supportsCompaction);
     if (!supportsCompaction) offlineStore->DeleteMessages(aMessages);
   }
 }
 
-void nsImapMailFolder::DeleteStoreMessages(nsTArray<nsMsgKey> &aMessages) {
+void nsImapMailFolder::DeleteStoreMessages(
+    const nsTArray<nsMsgKey> &aMessages) {
   DeleteStoreMessages(aMessages, this);
 }
 
-void nsImapMailFolder::DeleteStoreMessages(nsTArray<nsMsgKey> &aMessages,
+void nsImapMailFolder::DeleteStoreMessages(const nsTArray<nsMsgKey> &aMessages,
                                            nsIMsgFolder *aFolder) {
   // Delete messages for pluggable stores that do not support compaction.
   NS_ASSERTION(aFolder, "Missing Source Folder");
   nsCOMPtr<nsIMsgPluggableStore> offlineStore;
   (void)aFolder->GetMsgStore(getter_AddRefs(offlineStore));
   if (offlineStore) {
     bool supportsCompaction;
     offlineStore->GetSupportsCompaction(&supportsCompaction);
     if (!supportsCompaction) {
       nsCOMPtr<nsIMsgDatabase> db;
       aFolder->GetMsgDatabase(getter_AddRefs(db));
       nsresult rv = NS_ERROR_FAILURE;
-      nsCOMPtr<nsIMutableArray> messages(
-          do_CreateInstance(NS_ARRAY_CONTRACTID));
-      if (db) rv = MsgGetHeadersFromKeys(db, aMessages, messages);
+      nsTArray<RefPtr<nsIMsgDBHdr>> messages;
+      if (db) rv = MsgGetHeadersFromKeys2(db, aMessages, messages);
       if (NS_SUCCEEDED(rv))
         offlineStore->DeleteMessages(messages);
       else
         NS_WARNING("Failed to get database");
     }
   }
 }
--- a/mailnews/imap/src/nsImapMailFolder.h
+++ b/mailnews/imap/src/nsImapMailFolder.h
@@ -585,14 +585,14 @@ class nsImapMailFolder : public nsMsgDBF
 
   /**
    * delete if appropriate local storage for messages in this folder
    *
    * @parm aMessages array (of nsIMsgDBHdr) of messages to delete
    *       (or an array of message keys)
    * @parm aSrcFolder the folder containing the messages (optional)
    */
-  void DeleteStoreMessages(nsIArray *aMessages);
-  void DeleteStoreMessages(nsTArray<nsMsgKey> &aMessages);
-  static void DeleteStoreMessages(nsTArray<nsMsgKey> &aMessages,
+  void DeleteStoreMessages(const nsTArray<RefPtr<nsIMsgDBHdr>> &aMessages);
+  void DeleteStoreMessages(const nsTArray<nsMsgKey> &aMessages);
+  static void DeleteStoreMessages(const nsTArray<nsMsgKey> &aMessages,
                                   nsIMsgFolder *aFolder);
 };
 #endif
--- a/mailnews/local/src/nsLocalMailFolder.cpp
+++ b/mailnews/local/src/nsLocalMailFolder.cpp
@@ -991,16 +991,24 @@ nsMsgLocalMailFolder::DeleteMessages(nsI
                                      nsIMsgCopyServiceListener *listener,
                                      bool allowUndo) {
   NS_ENSURE_ARG_POINTER(messages);
 
   uint32_t messageCount;
   nsresult rv = messages->GetLength(&messageCount);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Stopgap. Build a parallel array of message headers while we complete
+  // removal of nsIArray usage (Bug 1583030).
+  nsTArray<RefPtr<nsIMsgDBHdr>> msgHeaders;
+  msgHeaders.SetCapacity(messageCount);
+  for (uint32_t i = 0; i < messageCount; ++i) {
+    msgHeaders.AppendElement(do_QueryElementAt(messages, i));
+  }
+
   // shift delete case - (delete to trash is handled in EndMove)
   // this is also the case when applying retention settings.
   if (deleteStorage && !isMove) {
     MarkMsgsOnPop3Server(messages, POP3_DELETE);
   }
 
   bool isTrashFolder = mFlags & nsMsgFolderFlags::Trash;
 
@@ -1008,17 +1016,17 @@ nsMsgLocalMailFolder::DeleteMessages(nsI
   if (!isMove && (deleteStorage || isTrashFolder)) {
     nsCOMPtr<nsIMsgFolderNotificationService> notifier(
         do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
     if (notifier) {
       if (listener) {
         listener->OnStartCopy();
         listener->OnStopCopy(NS_OK);
       }
-      notifier->NotifyMsgsDeleted(messages);
+      notifier->NotifyMsgsDeleted(msgHeaders);
     }
   }
 
   if (!deleteStorage && !isTrashFolder) {
     nsCOMPtr<nsIMsgFolder> trashFolder;
     rv = GetTrashFolder(getter_AddRefs(trashFolder));
     if (NS_SUCCEEDED(rv)) {
       nsCOMPtr<nsIMsgCopyService> copyService =
@@ -1035,21 +1043,19 @@ nsMsgLocalMailFolder::DeleteMessages(nsI
         MarkMsgsOnPop3Server(messages, POP3_DELETE);
 
       nsCOMPtr<nsISupports> msgSupport;
       rv = EnableNotifications(allMessageCountNotifications, false);
       if (NS_SUCCEEDED(rv)) {
         nsCOMPtr<nsIMsgPluggableStore> msgStore;
         rv = GetMsgStore(getter_AddRefs(msgStore));
         if (NS_SUCCEEDED(rv)) {
-          rv = msgStore->DeleteMessages(messages);
-          nsCOMPtr<nsIMsgDBHdr> msgDBHdr;
-          for (uint32_t i = 0; i < messageCount; ++i) {
-            msgDBHdr = do_QueryElementAt(messages, i, &rv);
-            rv = msgDB->DeleteHeader(msgDBHdr, nullptr, false, true);
+          rv = msgStore->DeleteMessages(msgHeaders);
+          for (auto hdr : msgHeaders) {
+            rv = msgDB->DeleteHeader(hdr, nullptr, false, true);
           }
         }
       } else if (rv == NS_MSG_FOLDER_BUSY)
         ThrowAlertMsg("deletingMsgsFailed", msgWindow);
 
       // we are the source folder here for a move or shift delete
       // enable notifications because that will close the file stream
       // we've been caching, mark the db as valid, and commit it.
@@ -1089,42 +1095,60 @@ nsMsgLocalMailFolder::AddMessageDisposit
 
   nsresult rv =
       nsMsgDBFolder::AddMessageDispositionState(aMessage, aDispositionFlag);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIMsgPluggableStore> msgStore;
   rv = GetMsgStore(getter_AddRefs(msgStore));
   NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIMutableArray> messages(
-      do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
-  messages->AppendElement(aMessage);
-  return msgStore->ChangeFlags(messages, msgFlag, true);
+  return msgStore->ChangeFlags({aMessage}, msgFlag, true);
 }
 
 NS_IMETHODIMP
 nsMsgLocalMailFolder::MarkMessagesRead(nsIArray *aMessages, bool aMarkRead) {
   nsresult rv = nsMsgDBFolder::MarkMessagesRead(aMessages, aMarkRead);
   NS_ENSURE_SUCCESS(rv, rv);
+
+  // Stopgap. Build a parallel array of message headers while we complete
+  // removal of nsIArray usage (Bug 1583030).
+  uint32_t messageCount;
+  aMessages->GetLength(&messageCount);
+  nsTArray<RefPtr<nsIMsgDBHdr>> msgHeaders;
+  msgHeaders.SetCapacity(messageCount);
+  for (uint32_t i = 0; i < messageCount; ++i) {
+    msgHeaders.AppendElement(do_QueryElementAt(aMessages, i));
+  }
+
   nsCOMPtr<nsIMsgPluggableStore> msgStore;
   rv = GetMsgStore(getter_AddRefs(msgStore));
   NS_ENSURE_SUCCESS(rv, rv);
-  return msgStore->ChangeFlags(aMessages, nsMsgMessageFlags::Read, aMarkRead);
+  return msgStore->ChangeFlags(msgHeaders, nsMsgMessageFlags::Read, aMarkRead);
 }
 
 NS_IMETHODIMP
 nsMsgLocalMailFolder::MarkMessagesFlagged(nsIArray *aMessages,
                                           bool aMarkFlagged) {
   nsresult rv = nsMsgDBFolder::MarkMessagesFlagged(aMessages, aMarkFlagged);
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIMsgPluggableStore> msgStore;
   rv = GetMsgStore(getter_AddRefs(msgStore));
   NS_ENSURE_SUCCESS(rv, rv);
-  return msgStore->ChangeFlags(aMessages, nsMsgMessageFlags::Marked,
+
+  // Stopgap. Build a parallel array of message headers while we complete
+  // removal of nsIArray usage (Bug 1583030).
+  uint32_t messageCount;
+  aMessages->GetLength(&messageCount);
+  nsTArray<RefPtr<nsIMsgDBHdr>> msgHeaders;
+  msgHeaders.SetCapacity(messageCount);
+  for (uint32_t i = 0; i < messageCount; ++i) {
+    msgHeaders.AppendElement(do_QueryElementAt(aMessages, i));
+  }
+
+  return msgStore->ChangeFlags(msgHeaders, nsMsgMessageFlags::Marked,
                                aMarkFlagged);
 }
 
 NS_IMETHODIMP
 nsMsgLocalMailFolder::MarkAllMessagesRead(nsIMsgWindow *aMsgWindow) {
   nsresult rv = GetDatabase();
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1133,18 +1157,18 @@ nsMsgLocalMailFolder::MarkAllMessagesRea
   rv = mDatabase->MarkAllRead(thoseMarked);
   EnableNotifications(allMessageCountNotifications, true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (thoseMarked.IsEmpty()) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID));
-  rv = MsgGetHeadersFromKeys(mDatabase, thoseMarked, messages);
+  nsTArray<RefPtr<nsIMsgDBHdr>> messages;
+  rv = MsgGetHeadersFromKeys2(mDatabase, thoseMarked, messages);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIMsgPluggableStore> msgStore;
   rv = GetMsgStore(getter_AddRefs(msgStore));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = msgStore->ChangeFlags(messages, nsMsgMessageFlags::Read, true);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1165,18 +1189,18 @@ NS_IMETHODIMP nsMsgLocalMailFolder::Mark
 
   nsTArray<nsMsgKey> thoseMarked;
   rv = mDatabase->MarkThreadRead(thread, nullptr, thoseMarked);
   NS_ENSURE_SUCCESS(rv, rv);
   if (thoseMarked.IsEmpty()) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID));
-  rv = MsgGetHeadersFromKeys(mDatabase, thoseMarked, messages);
+  nsTArray<RefPtr<nsIMsgDBHdr>> messages;
+  rv = MsgGetHeadersFromKeys2(mDatabase, thoseMarked, messages);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIMsgPluggableStore> msgStore;
   rv = GetMsgStore(getter_AddRefs(msgStore));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = msgStore->ChangeFlags(messages, nsMsgMessageFlags::Read, true);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/mailnews/local/src/nsMsgBrkMBoxStore.cpp
+++ b/mailnews/local/src/nsMsgBrkMBoxStore.cpp
@@ -689,17 +689,18 @@ nsMsgBrkMBoxStore::GetMsgInputStream(nsI
     *aOffset = ParseUint64Str(PromiseFlatCString(aMsgToken).get());
   *aReusable = true;
   nsCOMPtr<nsIFile> mboxFile;
   nsresult rv = aMsgFolder->GetFilePath(getter_AddRefs(mboxFile));
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_NewLocalFileInputStream(aResult, mboxFile);
 }
 
-NS_IMETHODIMP nsMsgBrkMBoxStore::DeleteMessages(nsIArray *aHdrArray) {
+NS_IMETHODIMP nsMsgBrkMBoxStore::DeleteMessages(
+    const nsTArray<RefPtr<nsIMsgDBHdr>> &aHdrArray) {
   return ChangeFlags(aHdrArray, nsMsgMessageFlags::Expunged, true);
 }
 
 NS_IMETHODIMP
 nsMsgBrkMBoxStore::CopyMessages(bool isMove, nsIArray *aHdrArray,
                                 nsIMsgFolder *aDstFolder,
                                 nsIMsgCopyServiceListener *aListener,
                                 nsIArray **aDstHdrs,
@@ -765,23 +766,20 @@ NS_IMETHODIMP nsMsgBrkMBoxStore::Rebuild
 
   rv = mailboxService->ParseMailbox(aMsgWindow, pathFile, parser, aListener,
                                     nullptr);
   if (NS_SUCCEEDED(rv)) ResetForceReparse(aMsgDB);
   return rv;
 }
 
 nsresult nsMsgBrkMBoxStore::GetOutputStream(
-    nsIArray *aHdrArray, nsCOMPtr<nsIOutputStream> &outputStream,
+    nsIMsgDBHdr *aHdr, nsCOMPtr<nsIOutputStream> &outputStream,
     nsCOMPtr<nsISeekableStream> &seekableStream, int64_t &restorePos) {
-  nsresult rv;
-  nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, 0, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIMsgFolder> folder;
-  msgHdr->GetFolder(getter_AddRefs(folder));
+  nsresult rv = aHdr->GetFolder(getter_AddRefs(folder));
   NS_ENSURE_SUCCESS(rv, rv);
   nsCString URI;
   folder->GetURI(URI);
   restorePos = -1;
   if (m_outputStreams.Get(URI, getter_AddRefs(outputStream))) {
     seekableStream = do_QueryInterface(outputStream);
     rv = seekableStream->Tell(&restorePos);
     if (NS_FAILED(rv)) {
@@ -805,68 +803,61 @@ void nsMsgBrkMBoxStore::SetDBValid(nsIMs
   aHdr->GetFolder(getter_AddRefs(folder));
   if (folder) {
     nsCOMPtr<nsIMsgDatabase> db;
     folder->GetMsgDatabase(getter_AddRefs(db));
     if (db) SetSummaryFileValid(folder, db, true);
   }
 }
 
-NS_IMETHODIMP nsMsgBrkMBoxStore::ChangeFlags(nsIArray *aHdrArray,
-                                             uint32_t aFlags, bool aSet) {
-  NS_ENSURE_ARG_POINTER(aHdrArray);
+NS_IMETHODIMP nsMsgBrkMBoxStore::ChangeFlags(
+    const nsTArray<RefPtr<nsIMsgDBHdr>> &aHdrArray, uint32_t aFlags,
+    bool aSet) {
   nsCOMPtr<nsIOutputStream> outputStream;
   nsCOMPtr<nsISeekableStream> seekableStream;
   int64_t restoreStreamPos;
 
-  uint32_t messageCount;
-  nsresult rv = aHdrArray->GetLength(&messageCount);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (!messageCount) return NS_ERROR_INVALID_ARG;
+  if (aHdrArray.IsEmpty()) return NS_ERROR_INVALID_ARG;
 
-  rv = GetOutputStream(aHdrArray, outputStream, seekableStream,
-                       restoreStreamPos);
+  nsresult rv = GetOutputStream(aHdrArray[0], outputStream, seekableStream,
+                                restoreStreamPos);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIMsgDBHdr> msgHdr;
-  for (uint32_t i = 0; i < messageCount; i++) {
-    msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
+  for (auto msgHdr : aHdrArray) {
     // Seek to x-mozilla-status offset and rewrite value.
     rv = UpdateFolderFlag(msgHdr, aSet, aFlags, outputStream);
     if (NS_FAILED(rv)) {
       NS_WARNING("updateFolderFlag failed");
       break;
     }
   }
   if (restoreStreamPos != -1)
     seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, restoreStreamPos);
   else if (outputStream)
     outputStream->Close();
-  if (messageCount > 0) {
-    msgHdr = do_QueryElementAt(aHdrArray, 0);
-    SetDBValid(msgHdr);
-  }
+  SetDBValid(aHdrArray[0]);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsMsgBrkMBoxStore::ChangeKeywords(nsIArray *aHdrArray,
                                                 const nsACString &aKeywords,
                                                 bool aAdd) {
   NS_ENSURE_ARG_POINTER(aHdrArray);
   nsCOMPtr<nsIOutputStream> outputStream;
   nsCOMPtr<nsISeekableStream> seekableStream;
   int64_t restoreStreamPos;
 
   uint32_t messageCount;
   nsresult rv = aHdrArray->GetLength(&messageCount);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!messageCount) return NS_ERROR_INVALID_ARG;
-
-  rv = GetOutputStream(aHdrArray, outputStream, seekableStream,
-                       restoreStreamPos);
+  nsCOMPtr<nsIMsgDBHdr> firstHdr = do_QueryElementAt(aHdrArray, 0);
+  rv =
+      GetOutputStream(firstHdr, outputStream, seekableStream, restoreStreamPos);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(outputStream, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mozilla::UniquePtr<nsLineBuffer<char>> lineBuffer(new nsLineBuffer<char>);
 
   // For each message, we seek to the beginning of the x-mozilla-status header,
--- a/mailnews/local/src/nsMsgBrkMBoxStore.h
+++ b/mailnews/local/src/nsMsgBrkMBoxStore.h
@@ -26,17 +26,17 @@ class nsMsgBrkMBoxStore final : public n
 
  private:
   ~nsMsgBrkMBoxStore();
 
  protected:
   nsresult AddSubFolders(nsIMsgFolder *parent, nsCOMPtr<nsIFile> &path,
                          bool deep);
   nsresult CreateDirectoryForFolder(nsIFile *path);
-  nsresult GetOutputStream(nsIArray *aHdrArray,
+  nsresult GetOutputStream(nsIMsgDBHdr *aHdr,
                            nsCOMPtr<nsIOutputStream> &outputStream,
                            nsCOMPtr<nsISeekableStream> &seekableStream,
                            int64_t &restorePos);
   void GetMailboxModProperties(nsIMsgFolder *aFolder, int64_t *aSize,
                                uint32_t *aDate);
   void SetDBValid(nsIMsgDBHdr *aHdr);
   // We don't want to keep re-opening an output stream when downloading
   // multiple pop3 messages, or adjusting x-mozilla-status headers, so
--- a/mailnews/local/src/nsMsgMaildirStore.cpp
+++ b/mailnews/local/src/nsMsgMaildirStore.cpp
@@ -927,28 +927,24 @@ nsMsgMaildirStore::GetMsgInputStream(nsI
     rv = path->Create(nsIFile::DIRECTORY_TYPE, 0755);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   path->AppendNative(aMsgToken);
   return NS_NewLocalFileInputStream(aResult, path);
 }
 
-NS_IMETHODIMP nsMsgMaildirStore::DeleteMessages(nsIArray *aHdrArray) {
-  uint32_t messageCount;
-  nsresult rv = aHdrArray->GetLength(&messageCount);
-  NS_ENSURE_SUCCESS(rv, rv);
+NS_IMETHODIMP nsMsgMaildirStore::DeleteMessages(
+    const nsTArray<RefPtr<nsIMsgDBHdr>> &aHdrArray) {
   nsCOMPtr<nsIMsgFolder> folder;
 
-  for (uint32_t i = 0; i < messageCount; i++) {
-    nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
-    if (NS_FAILED(rv)) continue;
+  for (auto msgHdr : aHdrArray) {
     msgHdr->GetFolder(getter_AddRefs(folder));
     nsCOMPtr<nsIFile> path;
-    rv = folder->GetFilePath(getter_AddRefs(path));
+    nsresult rv = folder->GetFilePath(getter_AddRefs(path));
     NS_ENSURE_SUCCESS(rv, rv);
     nsAutoCString fileName;
     msgHdr->GetStringProperty("storeToken", getter_Copies(fileName));
 
     if (fileName.IsEmpty()) {
       MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
               ("DeleteMessages - empty storeToken!!"));
       // Perhaps an offline store has not downloaded this particular message.
@@ -1280,29 +1276,23 @@ NS_IMETHODIMP nsMsgMaildirStore::Rebuild
   MaildirStoreParser *fileParser =
       new MaildirStoreParser(aFolder, aMsgDB, directoryEnumerator, aListener);
   NS_ENSURE_TRUE(fileParser, NS_ERROR_OUT_OF_MEMORY);
   fileParser->StartTimer();
   ResetForceReparse(aMsgDB);
   return NS_OK;
 }
 
-NS_IMETHODIMP nsMsgMaildirStore::ChangeFlags(nsIArray *aHdrArray,
-                                             uint32_t aFlags, bool aSet) {
-  NS_ENSURE_ARG_POINTER(aHdrArray);
-
-  uint32_t messageCount;
-  nsresult rv = aHdrArray->GetLength(&messageCount);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  for (uint32_t i = 0; i < messageCount; i++) {
-    nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
+NS_IMETHODIMP nsMsgMaildirStore::ChangeFlags(
+    const nsTArray<RefPtr<nsIMsgDBHdr>> &aHdrArray, uint32_t aFlags,
+    bool aSet) {
+  for (auto msgHdr : aHdrArray) {
     // get output stream for header
     nsCOMPtr<nsIOutputStream> outputStream;
-    rv = GetOutputStream(msgHdr, outputStream);
+    nsresult rv = GetOutputStream(msgHdr, outputStream);
     NS_ENSURE_SUCCESS(rv, rv);
     // Seek to x-mozilla-status offset and rewrite value.
     rv = UpdateFolderFlag(msgHdr, aSet, aFlags, outputStream);
     if (NS_FAILED(rv)) NS_WARNING("updateFolderFlag failed");
   }
   return NS_OK;
 }
 
@@ -1338,17 +1328,17 @@ NS_IMETHODIMP nsMsgMaildirStore::ChangeK
   nsCOMPtr<nsIOutputStream> outputStream;
   nsCOMPtr<nsISeekableStream> seekableStream;
 
   uint32_t messageCount;
   nsresult rv = aHdrArray->GetLength(&messageCount);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!messageCount) return NS_ERROR_INVALID_ARG;
 
-  mozilla::UniquePtr<nsLineBuffer<char> > lineBuffer(new nsLineBuffer<char>);
+  mozilla::UniquePtr<nsLineBuffer<char>> lineBuffer(new nsLineBuffer<char>);
 
   nsTArray<nsCString> keywordArray;
   ParseString(aKeywords, ' ', keywordArray);
 
   for (uint32_t i = 0; i < messageCount; ++i)  // for each message
   {
     nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aHdrArray, i, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/mailnews/local/src/nsRssIncomingServer.cpp
+++ b/mailnews/local/src/nsRssIncomingServer.cpp
@@ -159,17 +159,18 @@ NS_IMETHODIMP nsRssIncomingServer::MsgAd
 }
 
 NS_IMETHODIMP nsRssIncomingServer::MsgsClassified(nsIArray *aMsgs,
                                                   bool aJunkProcessed,
                                                   bool aTraitProcessed) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-NS_IMETHODIMP nsRssIncomingServer::MsgsDeleted(nsIArray *aMsgs) {
+NS_IMETHODIMP nsRssIncomingServer::MsgsDeleted(
+    const nsTArray<RefPtr<nsIMsgDBHdr>> &aMsgs) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP nsRssIncomingServer::MsgsMoveCopyCompleted(
     bool aMove, nsIArray *aSrcMsgs, nsIMsgFolder *aDestFolder,
     nsIArray *aDestMsgs) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/mailnews/news/src/nsNewsFolder.cpp
+++ b/mailnews/news/src/nsNewsFolder.cpp
@@ -697,34 +697,42 @@ nsMsgNewsFolder::DeleteMessages(nsIArray
                                 bool deleteStorage, bool isMove,
                                 nsIMsgCopyServiceListener *listener,
                                 bool allowUndo) {
   nsresult rv = NS_OK;
 
   NS_ENSURE_ARG_POINTER(messages);
   NS_ENSURE_ARG_POINTER(aMsgWindow);
 
+  // Stopgap. Build a parallel array of message headers while we complete
+  // removal of nsIArray usage (Bug 1583030).
+  uint32_t messageCount;
+  messages->GetLength(&messageCount);
+  nsTArray<RefPtr<nsIMsgDBHdr>> msgHeaders;
+  msgHeaders.SetCapacity(messageCount);
+  for (uint32_t i = 0; i < messageCount; ++i) {
+    msgHeaders.AppendElement(do_QueryElementAt(messages, i));
+  }
+
   if (!isMove) {
     nsCOMPtr<nsIMsgFolderNotificationService> notifier(
         do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
-    if (notifier) notifier->NotifyMsgsDeleted(messages);
+    if (notifier) notifier->NotifyMsgsDeleted(msgHeaders);
   }
 
   rv = GetDatabase();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = EnableNotifications(allMessageCountNotifications, false);
   if (NS_SUCCEEDED(rv)) {
-    uint32_t count = 0;
-    rv = messages->GetLength(&count);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    for (uint32_t i = 0; i < count && NS_SUCCEEDED(rv); i++) {
-      nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(messages, i, &rv);
-      if (msgHdr) rv = mDatabase->DeleteHeader(msgHdr, nullptr, true, true);
+    for (auto msgHdr : msgHeaders) {
+      rv = mDatabase->DeleteHeader(msgHdr, nullptr, true, true);
+      if (!NS_FAILED(rv)) {
+        break;
+      }
     }
     EnableNotifications(allMessageCountNotifications, true);
   }
 
   if (!isMove)
     NotifyFolderEvent(NS_SUCCEEDED(rv) ? kDeleteOrMoveMsgCompleted
                                        : kDeleteOrMoveMsgFailed);
 
@@ -1367,39 +1375,34 @@ NS_IMETHODIMP nsMsgNewsFolder::RemoveMes
 
   // Notify listeners of a delete for a single message
   nsCOMPtr<nsIMsgFolderNotificationService> notifier(
       do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
   if (notifier) {
     nsCOMPtr<nsIMsgDBHdr> msgHdr;
     rv = mDatabase->GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
     NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIMutableArray> msgHdrs(do_CreateInstance(NS_ARRAY_CONTRACTID));
-    msgHdrs->AppendElement(msgHdr);
-
-    notifier->NotifyMsgsDeleted(msgHdrs);
+    notifier->NotifyMsgsDeleted({msgHdr.get()});
   }
   return mDatabase->DeleteMessage(key, nullptr, false);
 }
 
 NS_IMETHODIMP nsMsgNewsFolder::RemoveMessages(nsTArray<nsMsgKey> &aMsgKeys) {
   nsresult rv = GetDatabase();
   NS_ENSURE_SUCCESS(rv,
                     rv);  // if GetDatabase succeeds, mDatabase will be non-null
 
   // Notify listeners of a multiple message delete
   nsCOMPtr<nsIMsgFolderNotificationService> notifier(
       do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
 
   if (notifier) {
-    nsCOMPtr<nsIMutableArray> msgHdrs(do_CreateInstance(NS_ARRAY_CONTRACTID));
-    rv = MsgGetHeadersFromKeys(mDatabase, aMsgKeys, msgHdrs);
+    nsTArray<RefPtr<nsIMsgDBHdr>> msgHdrs;
+    rv = MsgGetHeadersFromKeys2(mDatabase, aMsgKeys, msgHdrs);
     NS_ENSURE_SUCCESS(rv, rv);
-
     notifier->NotifyMsgsDeleted(msgHdrs);
   }
 
   return mDatabase->DeleteMessages(aMsgKeys, nullptr);
 }
 
 NS_IMETHODIMP nsMsgNewsFolder::CancelComplete() {
   NotifyFolderEvent(kDeleteOrMoveMsgCompleted);
--- a/mailnews/test/resources/folderEventLogHelper.js
+++ b/mailnews/test/resources/folderEventLogHelper.js
@@ -65,24 +65,20 @@ var _folderEventLogHelper_msgFolderListe
     ];
     for (let msgHdr of fixIterator(aMsgs, Ci.nsIMsgDBHdr)) {
       args.push(msgHdr);
     }
     mark_action("msgEvent", "msgsClassified", args);
   },
 
   /**
-   * @param {nsIArray} aMsgs
+   * @param {Array<nsIMsgDBHdr>} aMsgs
    */
   msgsDeleted(aMsgs) {
-    let args = [];
-    for (let msgHdr of fixIterator(aMsgs, Ci.nsIMsgDBHdr)) {
-      args.push(msgHdr);
-    }
-    mark_action("msgEvent", "msgsDeleted", args);
+    mark_action("msgEvent", "msgsDeleted", aMsgs);
   },
 
   /**
    * @param {boolean} aMove
    * @param {nsIArray} aSrcMsgs
    * @param {nsIMsgFolder} aDestFolder
    * @param {nsIArray} aDestMsgs
    */
--- a/mailnews/test/resources/msgFolderListenerSetup.js
+++ b/mailnews/test/resources/msgFolderListenerSetup.js
@@ -238,16 +238,26 @@ function hasExactlyElements(array, eleme
         } catch (e) {}
       }
       Assert.equal(typeof currElement, "object");
       Assert.notEqual(
         mailTestUtils.non_strict_index_of(array, currElement),
         -1
       );
     }
+  } else if (Array.isArray(elements)) {
+    Assert.equal(elements.length, array.length);
+    for (let el of elements) {
+      Assert.equal(typeof el, "object");
+      Assert.equal(
+        el instanceof Ci.nsIMsgDBHdr || el instanceof Ci.nsIMsgFolder,
+        true
+      );
+      Assert.notEqual(mailTestUtils.non_strict_index_of(array, el), -1);
+    }
   } else if (
     elements instanceof Ci.nsIMsgDBHdr ||
     elements instanceof Ci.nsIMsgFolder
   ) {
     // If a single header or a folder
 
     // Check: there should be only one element in the array.
     Assert.equal(array.length, 1);