Bug 1715936 - Use nsTArray to pass around selections in nsMsgDBView. r=mkmelin
authorBen Campbell <benc@thunderbird.net>
Fri, 11 Jun 2021 13:39:52 +0300
changeset 32795 a009a421cf65ade8584794be8121857ee0870993
parent 32794 b88e52a88b7d5e6bf5c0a390b01c421ba2d2cb9a
child 32796 97a11bfda3c99a1e280eb9532d40bbe8bcda2f4e
push id18866
push usermkmelin@iki.fi
push dateFri, 11 Jun 2021 10:41:09 +0000
treeherdercomm-central@a009a421cf65 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin
bugs1715936
Bug 1715936 - Use nsTArray to pass around selections in nsMsgDBView. r=mkmelin Differential Revision: https://phabricator.services.mozilla.com/D117496
mailnews/base/src/nsMsgDBView.cpp
mailnews/base/src/nsMsgDBView.h
mailnews/base/src/nsMsgQuickSearchDBView.cpp
mailnews/base/src/nsMsgQuickSearchDBView.h
mailnews/base/src/nsMsgSearchDBView.cpp
mailnews/base/src/nsMsgSearchDBView.h
--- a/mailnews/base/src/nsMsgDBView.cpp
+++ b/mailnews/base/src/nsMsgDBView.cpp
@@ -1085,73 +1085,69 @@ nsMsgDBView::LoadMessageByUrl(const char
 }
 
 NS_IMETHODIMP
 nsMsgDBView::SelectionChangedXPCOM() {
   // If the currentSelection changed then we have a message to display -
   // not if we are in the middle of deleting rows.
   if (m_deletingRows) return NS_OK;
 
-  uint32_t numSelected = 0;
-
   nsMsgViewIndexArray selection;
   GetIndicesForSelection(selection);
-  nsMsgViewIndex* indices = selection.Elements();
-  numSelected = selection.Length();
 
   bool commandsNeedDisablingBecauseOfSelection = false;
 
-  if (indices) {
+  if (!selection.IsEmpty()) {
     if (WeAreOffline())
-      commandsNeedDisablingBecauseOfSelection =
-          !OfflineMsgSelected(indices, numSelected);
-
-    if (!NonDummyMsgSelected(indices, numSelected))
+      commandsNeedDisablingBecauseOfSelection = !OfflineMsgSelected(selection);
+
+    if (!NonDummyMsgSelected(selection))
       commandsNeedDisablingBecauseOfSelection = true;
   }
 
   bool selectionSummarized = false;
   mSummarizeFailed = false;
   // Let the front-end adjust the message pane appropriately with either
   // the message body, or a summary of the selection.
   if (mCommandUpdater) {
     mCommandUpdater->SummarizeSelection(&selectionSummarized);
     // Check if the selection was not summarized, but we expected it to be,
     // and if so, remember it so GetHeadersFromSelection won't include
     // the messages in collapsed threads.
     if (!selectionSummarized &&
-        (numSelected > 1 ||
-         (numSelected == 1 && m_flags[indices[0]] & nsMsgMessageFlags::Elided &&
+        (selection.Length() > 1 ||
+         (selection.Length() == 1 &&
+          m_flags[selection[0]] & nsMsgMessageFlags::Elided &&
           OperateOnMsgsInCollapsedThreads()))) {
       mSummarizeFailed = true;
     }
   }
 
   bool summaryStateChanged = selectionSummarized != mSelectionSummarized;
   mSelectionSummarized = selectionSummarized;
 
   // If only one item is selected then we want to display a message.
-  if (mTreeSelection && numSelected == 1 && !selectionSummarized) {
+  if (mTreeSelection && selection.Length() == 1 && !selectionSummarized) {
     int32_t startRange;
     int32_t endRange;
     nsresult rv = mTreeSelection->GetRangeAt(0, &startRange, &endRange);
     // Tree doesn't care if we failed.
     NS_ENSURE_SUCCESS(rv, NS_OK);
 
     if (startRange >= 0 && startRange == endRange &&
         uint32_t(startRange) < GetSize()) {
       if (!mRemovingRow) {
         if (!mSuppressMsgDisplay)
           LoadMessageByViewIndex(startRange);
         else
           UpdateDisplayMessage(startRange);
       }
     } else {
       // Selection seems bogus, so set to 0.
-      numSelected = 0;
+      selection.Clear();
     }
   } else {
     // If we have zero or multiple items selected, we shouldn't be displaying
     // any message.
     m_currentlyDisplayedMsgKey = nsMsgKey_None;
     m_currentlyDisplayedMsgUri.Truncate();
     m_currentlyDisplayedViewIndex = nsMsgViewIndex_None;
   }
@@ -1173,33 +1169,33 @@ nsMsgDBView::SelectionChangedXPCOM() {
   // enabled/should be enabled, and when this changes, force a command update.
 
   bool enableGoForward = false;
   bool enableGoBack = false;
 
   NavigateStatus(nsMsgNavigationType::forward, &enableGoForward);
   NavigateStatus(nsMsgNavigationType::back, &enableGoBack);
   if (!summaryStateChanged &&
-      (numSelected == mNumSelectedRows ||
-       (numSelected > 1 && mNumSelectedRows > 1)) &&
+      (selection.Length() == mNumSelectedRows ||
+       (selection.Length() > 1 && mNumSelectedRows > 1)) &&
       commandsNeedDisablingBecauseOfSelection ==
           mCommandsNeedDisablingBecauseOfSelection &&
       enableGoForward == mGoForwardEnabled && enableGoBack == mGoBackEnabled) {
     // Don't update commands if we're suppressing them, or if we're removing
     // rows, unless it was the last row.
   } else if (!mSuppressCommandUpdating && mCommandUpdater &&
              (!mRemovingRow || GetSize() == 0)) {
     mCommandUpdater->UpdateCommandStatus();
   }
 
   mCommandsNeedDisablingBecauseOfSelection =
       commandsNeedDisablingBecauseOfSelection;
   mGoForwardEnabled = enableGoForward;
   mGoBackEnabled = enableGoBack;
-  mNumSelectedRows = numSelected;
+  mNumSelectedRows = selection.Length();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMsgDBView::GetRowProperties(int32_t index, nsAString& properties) {
   if (!IsValidIndex(index)) return NS_MSG_INVALID_DBVIEW_INDEX;
 
   // This is where we tell the tree to apply styles to a particular row.
@@ -2003,17 +1999,19 @@ NS_IMETHODIMP
 nsMsgDBView::CycleHeader(nsTreeColumn* aCol) {
   // Let HandleColumnClick() in threadPane.js handle it
   // since it will set / clear the sort indicators.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMsgDBView::CycleCell(int32_t row, nsTreeColumn* col) {
-  if (!IsValidIndex(row)) return NS_MSG_INVALID_DBVIEW_INDEX;
+  if (!IsValidIndex(row)) {
+    return NS_MSG_INVALID_DBVIEW_INDEX;
+  }
 
   const nsAString& colID = col->GetId();
 
   // Attempt to retrieve a custom column handler. If it exists call it and
   // return.
   nsIMsgCustomColumnHandler* colHandler = GetColumnHandler(colID);
 
   if (colHandler) {
@@ -2026,59 +2024,62 @@ nsMsgDBView::CycleCell(int32_t row, nsTr
   if (m_viewFlags & nsMsgViewFlagsType::kGroupBySort &&
       m_flags[row] & MSG_VIEW_FLAG_DUMMY)
     return NS_OK;
 
   if (colID.IsEmpty()) return NS_OK;
 
   switch (colID.First()) {
     case 'u':
-      if (colID.EqualsLiteral("unreadButtonColHeader"))
+      if (colID.EqualsLiteral("unreadButtonColHeader")) {
         ApplyCommandToIndices(nsMsgViewCommandType::toggleMessageRead,
-                              (nsMsgViewIndex*)&row, 1);
+                              {(nsMsgViewIndex)row});
+      }
       break;
     case 't':
       if (colID.EqualsLiteral("threadCol")) {
         ExpandAndSelectThreadByIndex(row, false);
       } else if (colID.EqualsLiteral("tagsCol")) {
         // XXX Do we want to keep this behaviour but switch it to tags?
         // We could enumerate over the tags and go to the next one - it looks
         // to me like this wasn't working before tags landed, so maybe not
         // worth bothering with.
       }
       break;
     case 'f':
       if (colID.EqualsLiteral("flaggedCol")) {
         // toggle the flagged status of the element at row.
-        if (m_flags[row] & nsMsgMessageFlags::Marked)
+        if (m_flags[row] & nsMsgMessageFlags::Marked) {
           ApplyCommandToIndices(nsMsgViewCommandType::unflagMessages,
-                                (nsMsgViewIndex*)&row, 1);
-        else
+                                {(nsMsgViewIndex)row});
+        } else {
           ApplyCommandToIndices(nsMsgViewCommandType::flagMessages,
-                                (nsMsgViewIndex*)&row, 1);
+                                {(nsMsgViewIndex)row});
+        }
       }
       break;
     case 'j': {
-      if (!colID.EqualsLiteral("junkStatusCol") || !JunkControlsEnabled(row))
+      if (!colID.EqualsLiteral("junkStatusCol") || !JunkControlsEnabled(row)) {
         return NS_OK;
+      }
 
       nsCOMPtr<nsIMsgDBHdr> msgHdr;
       nsresult rv = GetMsgHdrForViewIndex(row, getter_AddRefs(msgHdr));
       if (NS_SUCCEEDED(rv) && msgHdr) {
         nsCString junkScoreStr;
         rv =
             msgHdr->GetStringProperty("junkscore", getter_Copies(junkScoreStr));
         if (junkScoreStr.IsEmpty() ||
-            (junkScoreStr.ToInteger(&rv) == nsIJunkMailPlugin::IS_HAM_SCORE))
+            (junkScoreStr.ToInteger(&rv) == nsIJunkMailPlugin::IS_HAM_SCORE)) {
           ApplyCommandToIndices(nsMsgViewCommandType::junk,
-                                (nsMsgViewIndex*)&row, 1);
-        else
+                                {(nsMsgViewIndex)row});
+        } else {
           ApplyCommandToIndices(nsMsgViewCommandType::unjunk,
-                                (nsMsgViewIndex*)&row, 1);
-
+                                {(nsMsgViewIndex)row});
+        }
         NS_ASSERTION(NS_SUCCEEDED(rv),
                      "Converting junkScore to integer failed.");
       }
       break;
     }
     default:
       break;
   }
@@ -2333,23 +2334,24 @@ nsMsgDBView::GetIndicesForSelection(nsTA
 
 // Array<nsIMsgDBHdr> getSelectedMsgHdrs();
 NS_IMETHODIMP
 nsMsgDBView::GetSelectedMsgHdrs(nsTArray<RefPtr<nsIMsgDBHdr>>& aResult) {
   nsresult rv;
 
   nsMsgViewIndexArray selection;
   GetIndicesForSelection(selection);
-  uint32_t numIndices = selection.Length();
-  if (!numIndices) return NS_OK;
+  if (selection.IsEmpty()) {
+    return NS_OK;
+  }
 
   nsCOMPtr<nsIMutableArray> messages(
       do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = GetHeadersFromSelection(selection.Elements(), numIndices, messages);
+  rv = GetHeadersFromSelection(selection, messages);
   NS_ENSURE_SUCCESS(rv, rv);
   uint32_t numMsgsSelected;
   messages->GetLength(&numMsgsSelected);
 
   aResult.Clear();
   aResult.SetCapacity(numMsgsSelected);
   for (uint32_t i = 0; i < numMsgsSelected; i++) {
     nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(messages, i, &rv);
@@ -2361,23 +2363,24 @@ nsMsgDBView::GetSelectedMsgHdrs(nsTArray
 
 NS_IMETHODIMP
 nsMsgDBView::GetURIsForSelection(nsTArray<nsCString>& uris) {
   nsresult rv = NS_OK;
 
   uris.Clear();
   nsMsgViewIndexArray selection;
   GetIndicesForSelection(selection);
-  uint32_t numIndices = selection.Length();
-  if (!numIndices) return NS_OK;
+  if (selection.IsEmpty()) {
+    return NS_OK;
+  }
 
   nsCOMPtr<nsIMutableArray> messages(
       do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = GetHeadersFromSelection(selection.Elements(), numIndices, messages);
+  rv = GetHeadersFromSelection(selection, messages);
   NS_ENSURE_SUCCESS(rv, rv);
   uint32_t numMsgsSelected;
   messages->GetLength(&numMsgsSelected);
   uris.SetCapacity(numMsgsSelected);
   for (uint32_t i = 0; i < numMsgsSelected; i++) {
     nsCString tmpUri;
     nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(messages, i, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -2411,67 +2414,59 @@ nsMsgDBView::GetURIForViewIndex(nsMsgVie
 }
 
 NS_IMETHODIMP
 nsMsgDBView::DoCommandWithFolder(nsMsgViewCommandTypeValue command,
                                  nsIMsgFolder* destFolder) {
   NS_ENSURE_ARG_POINTER(destFolder);
 
   nsMsgViewIndexArray selection;
-
   GetIndicesForSelection(selection);
 
-  nsMsgViewIndex* indices = selection.Elements();
-  int32_t numIndices = selection.Length();
-
   nsresult rv = NS_OK;
   switch (command) {
     case nsMsgViewCommandType::copyMessages:
     case nsMsgViewCommandType::moveMessages:
-      rv = ApplyCommandToIndicesWithFolder(command, indices, numIndices,
-                                           destFolder);
+      rv = ApplyCommandToIndicesWithFolder(command, selection, destFolder);
       NoteChange(0, 0, nsMsgViewNotificationCode::none);
       break;
     default:
       NS_ASSERTION(false, "invalid command type");
       rv = NS_ERROR_UNEXPECTED;
       break;
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsMsgDBView::DoCommand(nsMsgViewCommandTypeValue command) {
   nsMsgViewIndexArray selection;
-
   GetIndicesForSelection(selection);
 
-  nsMsgViewIndex* indices = selection.Elements();
-  int32_t numIndices = selection.Length();
   nsCOMPtr<nsIMsgWindow> msgWindow(do_QueryReferent(mMsgWindowWeak));
 
   nsresult rv = NS_OK;
   switch (command) {
     case nsMsgViewCommandType::downloadSelectedForOffline:
-      return DownloadForOffline(msgWindow, indices, numIndices);
+      return DownloadForOffline(msgWindow, selection);
     case nsMsgViewCommandType::downloadFlaggedForOffline:
       return DownloadFlaggedForOffline(msgWindow);
     case nsMsgViewCommandType::markMessagesRead:
     case nsMsgViewCommandType::markMessagesUnread:
     case nsMsgViewCommandType::toggleMessageRead:
     case nsMsgViewCommandType::flagMessages:
     case nsMsgViewCommandType::unflagMessages:
     case nsMsgViewCommandType::deleteMsg:
     case nsMsgViewCommandType::undeleteMsg:
     case nsMsgViewCommandType::deleteNoTrash:
     case nsMsgViewCommandType::markThreadRead:
     case nsMsgViewCommandType::junk:
     case nsMsgViewCommandType::unjunk:
-      rv = ApplyCommandToIndices(command, indices, numIndices);
+      rv = ApplyCommandToIndices(command, selection);
       NoteChange(0, 0, nsMsgViewNotificationCode::none);
       break;
     case nsMsgViewCommandType::selectAll:
       if (mTreeSelection) {
         // If in threaded mode, we need to expand all before selecting.
         if (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)
           rv = ExpandAll();
 
@@ -2484,35 +2479,35 @@ nsMsgDBView::DoCommand(nsMsgViewCommandT
       break;
     case nsMsgViewCommandType::selectFlagged:
       if (!mTreeSelection) {
         rv = NS_ERROR_UNEXPECTED;
       } else {
         mTreeSelection->SetSelectEventsSuppressed(true);
         mTreeSelection->ClearSelection();
         // XXX ExpandAll?
-        nsMsgViewIndex numIndices = GetSize();
-        for (nsMsgViewIndex curIndex = 0; curIndex < numIndices; curIndex++) {
+        uint32_t numIndices = GetSize();
+        for (uint32_t curIndex = 0; curIndex < numIndices; curIndex++) {
           if (m_flags[curIndex] & nsMsgMessageFlags::Marked)
             mTreeSelection->ToggleSelect(curIndex);
         }
 
         mTreeSelection->SetSelectEventsSuppressed(false);
       }
       break;
     case nsMsgViewCommandType::markAllRead:
       if (m_folder) {
         SetSuppressChangeNotifications(true);
         rv = m_folder->MarkAllMessagesRead(msgWindow);
         SetSuppressChangeNotifications(false);
         if (mTree) mTree->Invalidate();
       }
       break;
     case nsMsgViewCommandType::toggleThreadWatched:
-      rv = ToggleWatched(indices, numIndices);
+      rv = ToggleWatched(selection);
       break;
     case nsMsgViewCommandType::expandAll:
       rv = ExpandAll();
       m_viewFlags |= nsMsgViewFlagsType::kExpandAll;
       SetViewFlags(m_viewFlags);
       if (mTree) mTree->Invalidate();
 
       break;
@@ -2556,24 +2551,22 @@ nsMsgDBView::GetCommandStatus(nsMsgViewC
                               bool* selectable_p,
                               nsMsgViewCommandCheckStateValue* selected_p) {
   nsresult rv = NS_OK;
 
   bool haveSelection;
   int32_t rangeCount;
   nsMsgViewIndexArray selection;
   GetIndicesForSelection(selection);
-  int32_t numIndices = selection.Length();
-  nsMsgViewIndex* indices = selection.Elements();
   // If range count is non-zero, we have at least one item selected, so we
   // have a selection.
   if (mTreeSelection &&
       NS_SUCCEEDED(mTreeSelection->GetRangeCount(&rangeCount)) &&
       rangeCount > 0) {
-    haveSelection = NonDummyMsgSelected(indices, numIndices);
+    haveSelection = NonDummyMsgSelected(selection);
   } else {
     // If we don't have a tree selection we must be in stand alone mode.
     haveSelection = IsValidIndex(m_currentlyDisplayedViewIndex);
   }
 
   switch (command) {
     case nsMsgViewCommandType::deleteMsg:
     case nsMsgViewCommandType::deleteNoTrash: {
@@ -2615,23 +2608,22 @@ nsMsgDBView::GetCommandStatus(nsMsgViewC
     case nsMsgViewCommandType::unflagMessages:
     case nsMsgViewCommandType::toggleThreadWatched:
     case nsMsgViewCommandType::markThreadRead:
     case nsMsgViewCommandType::downloadSelectedForOffline:
       *selectable_p = haveSelection;
       break;
     case nsMsgViewCommandType::junk:
     case nsMsgViewCommandType::unjunk:
-      *selectable_p =
-          haveSelection && numIndices && JunkControlsEnabled(selection[0]);
+      *selectable_p = haveSelection && !selection.IsEmpty() &&
+                      JunkControlsEnabled(selection[0]);
       break;
     case nsMsgViewCommandType::cmdRequiringMsgBody:
       *selectable_p =
-          haveSelection &&
-          (!WeAreOffline() || OfflineMsgSelected(indices, numIndices));
+          haveSelection && (!WeAreOffline() || OfflineMsgSelected(selection));
       break;
     case nsMsgViewCommandType::downloadFlaggedForOffline:
     case nsMsgViewCommandType::markAllRead:
       *selectable_p = true;
       break;
     default:
       NS_ASSERTION(false, "invalid command type");
       rv = NS_ERROR_FAILURE;
@@ -2685,30 +2677,32 @@ bool nsMsgDBView::OperateOnMsgsInCollaps
   NS_ENSURE_SUCCESS(rv, false);
 
   bool includeCollapsedMsgs = false;
   prefBranch->GetBoolPref("mail.operate_on_msgs_in_collapsed_threads",
                           &includeCollapsedMsgs);
   return includeCollapsedMsgs;
 }
 
-nsresult nsMsgDBView::GetHeadersFromSelection(uint32_t* indices,
-                                              uint32_t numIndices,
-                                              nsIMutableArray* messageArray) {
+nsresult nsMsgDBView::GetHeadersFromSelection(
+    nsTArray<nsMsgViewIndex> const& selection, nsIMutableArray* messageArray) {
   nsresult rv = NS_OK;
 
   // Don't include collapsed messages if the front end failed to summarize
   // the selection.
   bool includeCollapsedMsgs =
       OperateOnMsgsInCollapsedThreads() && !mSummarizeFailed;
 
-  for (uint32_t index = 0;
-       index < (nsMsgViewIndex)numIndices && NS_SUCCEEDED(rv); index++) {
-    nsMsgViewIndex viewIndex = indices[index];
-    if (viewIndex == nsMsgViewIndex_None) continue;
+  for (nsMsgViewIndex viewIndex : selection) {
+    if (NS_FAILED(rv)) {
+      break;
+    }
+    if (viewIndex == nsMsgViewIndex_None) {
+      continue;
+    }
 
     uint32_t viewIndexFlags = m_flags[viewIndex];
     if (viewIndexFlags & MSG_VIEW_FLAG_DUMMY) {
       // If collapsed dummy header selected, list its children.
       if (includeCollapsedMsgs && viewIndexFlags & nsMsgMessageFlags::Elided &&
           m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)
         rv = ListCollapsedChildren(viewIndex, messageArray);
 
@@ -2727,95 +2721,94 @@ nsresult nsMsgDBView::GetHeadersFromSele
       }
     }
   }
 
   return rv;
 }
 
 nsresult nsMsgDBView::CopyMessages(nsIMsgWindow* window,
-                                   nsMsgViewIndex* indices, int32_t numIndices,
+                                   nsTArray<nsMsgViewIndex> const& selection,
                                    bool isMove, nsIMsgFolder* destFolder) {
   if (m_deletingRows) {
     NS_ASSERTION(false, "Last move did not complete");
     return NS_OK;
   }
 
   nsresult rv;
   NS_ENSURE_ARG_POINTER(destFolder);
   nsCOMPtr<nsIMutableArray> messageArray(
       do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = GetHeadersFromSelection(indices, numIndices, messageArray);
+  rv = GetHeadersFromSelection(selection, messageArray);
   NS_ENSURE_SUCCESS(rv, rv);
 
   m_deletingRows = isMove && mDeleteModel != nsMsgImapDeleteModels::IMAPDelete;
-  if (m_deletingRows) mIndicesToNoteChange.AppendElements(indices, numIndices);
+  if (m_deletingRows) {
+    mIndicesToNoteChange.AppendElements(selection);
+  }
 
   nsCOMPtr<nsIMsgCopyService> copyService =
       do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsTArray<RefPtr<nsIMsgDBHdr>> tmpHdrs;
   MsgHdrsToTArray(messageArray, tmpHdrs);
   return copyService->CopyMessages(m_folder /* source folder */, tmpHdrs,
                                    destFolder, isMove, nullptr /* listener */,
                                    window, true /* allow Undo */);
 }
 
 nsresult nsMsgDBView::ApplyCommandToIndicesWithFolder(
-    nsMsgViewCommandTypeValue command, nsMsgViewIndex* indices,
-    int32_t numIndices, nsIMsgFolder* destFolder) {
+    nsMsgViewCommandTypeValue command,
+    nsTArray<nsMsgViewIndex> const& selection, nsIMsgFolder* destFolder) {
   nsresult rv = NS_OK;
   NS_ENSURE_ARG_POINTER(destFolder);
 
   nsCOMPtr<nsIMsgWindow> msgWindow(do_QueryReferent(mMsgWindowWeak));
   switch (command) {
     case nsMsgViewCommandType::copyMessages:
       NS_ASSERTION(!(m_folder == destFolder),
                    "The source folder and the destination folder are the same");
       if (m_folder != destFolder)
-        rv = CopyMessages(msgWindow, indices, numIndices, false /* isMove */,
-                          destFolder);
+        rv = CopyMessages(msgWindow, selection, false /* isMove */, destFolder);
 
       break;
     case nsMsgViewCommandType::moveMessages:
       NS_ASSERTION(!(m_folder == destFolder),
                    "The source folder and the destination folder are the same");
       if (m_folder != destFolder)
-        rv = CopyMessages(msgWindow, indices, numIndices, true /* isMove */,
-                          destFolder);
+        rv = CopyMessages(msgWindow, selection, true /* isMove */, destFolder);
 
       break;
     default:
       NS_ASSERTION(false, "unhandled command");
       rv = NS_ERROR_UNEXPECTED;
       break;
   }
 
   return rv;
 }
 
-nsresult nsMsgDBView::ApplyCommandToIndices(nsMsgViewCommandTypeValue command,
-                                            nsMsgViewIndex* indices,
-                                            int32_t numIndices) {
-  NS_ASSERTION(numIndices >= 0,
-               "nsMsgDBView::ApplyCommandToIndices(): numIndices is negative!");
-
-  // Return quietly, just in case/
-  if (numIndices == 0) return NS_OK;
+nsresult nsMsgDBView::ApplyCommandToIndices(
+    nsMsgViewCommandTypeValue command,
+    nsTArray<nsMsgViewIndex> const& selection) {
+  if (selection.IsEmpty()) {
+    // Return quietly, just in case/
+    return NS_OK;
+  }
 
   nsCOMPtr<nsIMsgFolder> folder;
-  nsresult rv = GetFolderForViewIndex(indices[0], getter_AddRefs(folder));
+  nsresult rv = GetFolderForViewIndex(selection[0], getter_AddRefs(folder));
   nsCOMPtr<nsIMsgWindow> msgWindow(do_QueryReferent(mMsgWindowWeak));
   if (command == nsMsgViewCommandType::deleteMsg)
-    return DeleteMessages(msgWindow, indices, numIndices, false);
+    return DeleteMessages(msgWindow, selection, false);
 
   if (command == nsMsgViewCommandType::deleteNoTrash)
-    return DeleteMessages(msgWindow, indices, numIndices, true);
+    return DeleteMessages(msgWindow, selection, true);
 
   nsTArray<nsMsgKey> imapUids;
   nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(folder);
   bool thisIsImapFolder = (imapFolder != nullptr);
   nsCOMPtr<nsIJunkMailPlugin> junkPlugin;
 
   // If this is a junk command, get the junk plugin.
   if (command == nsMsgViewCommandType::junk ||
@@ -2837,26 +2830,27 @@ nsresult nsMsgDBView::ApplyCommandToIndi
   }
 
   folder->EnableNotifications(nsIMsgFolder::allMessageCountNotifications,
                               false);
 
   // No sense going through the code that handles messages in collasped threads
   // for mark thread read.
   if (command == nsMsgViewCommandType::markThreadRead) {
-    for (int32_t index = 0; index < numIndices; index++)
-      SetThreadOfMsgReadByIndex(indices[index], imapUids, true);
+    for (nsMsgViewIndex viewIndex : selection) {
+      SetThreadOfMsgReadByIndex(viewIndex, imapUids, true);
+    }
   } else {
     // Turn the selection into an array of msg hdrs. This may include messages
     // in collapsed threads
     uint32_t length;
     nsCOMPtr<nsIMutableArray> messageArray(
         do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
     NS_ENSURE_SUCCESS(rv, rv);
-    rv = GetHeadersFromSelection(indices, numIndices, messageArray);
+    rv = GetHeadersFromSelection(selection, messageArray);
     NS_ENSURE_SUCCESS(rv, rv);
     messageArray->GetLength(&length);
 
     if (thisIsImapFolder) imapUids.SetLength(length);
 
     nsTArray<RefPtr<nsIMsgDBHdr>> messages(length);
     MsgHdrsToTArray(messageArray, messages);
 
@@ -2990,28 +2984,28 @@ nsresult nsMsgDBView::RemoveByIndex(nsMs
   // An example where view is not the listener - D&D messages.
   if (!m_deletingRows)
     NoteChange(index, -1, nsMsgViewNotificationCode::insertOrDelete);
 
   return NS_OK;
 }
 
 nsresult nsMsgDBView::DeleteMessages(nsIMsgWindow* window,
-                                     nsMsgViewIndex* indices,
-                                     int32_t numIndices, bool deleteStorage) {
+                                     nsTArray<nsMsgViewIndex> const& selection,
+                                     bool deleteStorage) {
   if (m_deletingRows) {
     NS_WARNING("Last delete did not complete");
     return NS_OK;
   }
 
   nsresult rv;
   nsCOMPtr<nsIMutableArray> messageArray(
       do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = GetHeadersFromSelection(indices, numIndices, messageArray);
+  rv = GetHeadersFromSelection(selection, messageArray);
   NS_ENSURE_SUCCESS(rv, rv);
   uint32_t numMsgs;
   messageArray->GetLength(&numMsgs);
 
   const char* warnCollapsedPref = "mail.warn_on_collapsed_thread_operation";
   const char* warnShiftDelPref = "mail.warn_on_shift_delete";
   const char* warnNewsPref = "news.warn_on_delete";
   const char* warnTrashDelPref = "mail.warn_on_delete_from_trash";
@@ -3029,17 +3023,17 @@ nsresult nsMsgDBView::DeleteMessages(nsI
     bool pref = false;
     prefBranch->GetBoolPref(warnTrashDelPref, &pref);
     if (pref) {
       activePref = warnTrashDelPref;
       warningName.AssignLiteral("confirmMsgDelete.deleteFromTrash.desc");
     }
   }
 
-  if (!activePref && (uint32_t(numIndices) != numMsgs)) {
+  if (!activePref && (selection.Length() != numMsgs)) {
     bool pref = false;
     prefBranch->GetBoolPref(warnCollapsedPref, &pref);
     if (pref) {
       activePref = warnCollapsedPref;
       warningName.AssignLiteral("confirmMsgDelete.collapsed.desc");
     }
   }
 
@@ -3094,34 +3088,33 @@ nsresult nsMsgDBView::DeleteMessages(nsI
     NS_ENSURE_SUCCESS(rv, rv);
     if (buttonPressed) return NS_ERROR_FAILURE;
 
     if (dontAsk) prefBranch->SetBoolPref(activePref, false);
   }
 
   if (mDeleteModel != nsMsgImapDeleteModels::IMAPDelete) m_deletingRows = true;
 
-  if (m_deletingRows) mIndicesToNoteChange.AppendElements(indices, numIndices);
+  if (m_deletingRows) mIndicesToNoteChange.AppendElements(selection);
 
   nsTArray<RefPtr<nsIMsgDBHdr>> tmp;
   MsgHdrsToTArray(messageArray, tmp);
   rv = m_folder->DeleteMessages(tmp, window, deleteStorage, false, nullptr,
                                 true /* allow Undo */);
   if (NS_FAILED(rv)) m_deletingRows = false;
 
   return rv;
 }
 
-nsresult nsMsgDBView::DownloadForOffline(nsIMsgWindow* window,
-                                         nsMsgViewIndex* indices,
-                                         int32_t numIndices) {
+nsresult nsMsgDBView::DownloadForOffline(
+    nsIMsgWindow* window, nsTArray<nsMsgViewIndex> const& selection) {
   nsresult rv = NS_OK;
   nsTArray<RefPtr<nsIMsgDBHdr>> messages;
-  for (nsMsgViewIndex index = 0; index < (nsMsgViewIndex)numIndices; index++) {
-    nsMsgKey key = m_keys[indices[index]];
+  for (nsMsgViewIndex viewIndex : selection) {
+    nsMsgKey key = m_keys[viewIndex];
     nsCOMPtr<nsIMsgDBHdr> msgHdr;
     rv = m_db->GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
     NS_ENSURE_SUCCESS(rv, rv);
     if (msgHdr) {
       uint32_t flags;
       msgHdr->GetFlags(&flags);
       if (!(flags & nsMsgMessageFlags::Offline)) messages.AppendElement(msgHdr);
     }
@@ -6275,44 +6268,43 @@ nsresult nsMsgDBView::NavigateFromPos(ns
         }
 
         if (pThreadIndex) *pThreadIndex = threadIndex;
       }
       break;
     case nsMsgNavigationType::lastUnreadMessage:
       break;
     case nsMsgNavigationType::nextUnreadThread:
-      if (startIndex != nsMsgViewIndex_None)
-        ApplyCommandToIndices(nsMsgViewCommandType::markThreadRead, &startIndex,
-                              1);
+      if (startIndex != nsMsgViewIndex_None) {
+        ApplyCommandToIndices(nsMsgViewCommandType::markThreadRead,
+                              {startIndex});
+      }
 
       return NavigateFromPos(nsMsgNavigationType::nextUnreadMessage, startIndex,
                              pResultKey, pResultIndex, pThreadIndex, true);
     case nsMsgNavigationType::toggleThreadKilled: {
       bool resultKilled;
       nsMsgViewIndexArray selection;
       GetIndicesForSelection(selection);
-      ToggleIgnored(selection.Elements(), selection.Length(), &threadIndex,
-                    &resultKilled);
+      ToggleIgnored(selection, &threadIndex, &resultKilled);
       if (resultKilled) {
         return NavigateFromPos(nsMsgNavigationType::nextUnreadThread,
                                threadIndex, pResultKey, pResultIndex,
                                pThreadIndex, true);
       } else {
         *pResultIndex = nsMsgViewIndex_None;
         *pResultKey = nsMsgKey_None;
         return NS_OK;
       }
     }
     case nsMsgNavigationType::toggleSubthreadKilled: {
       bool resultKilled;
       nsMsgViewIndexArray selection;
       GetIndicesForSelection(selection);
-      ToggleMessageKilled(selection.Elements(), selection.Length(),
-                          &threadIndex, &resultKilled);
+      ToggleMessageKilled(selection, &threadIndex, &resultKilled);
       if (resultKilled) {
         return NavigateFromPos(nsMsgNavigationType::nextUnreadMessage,
                                threadIndex, pResultKey, pResultIndex,
                                pThreadIndex, true);
       } else {
         *pResultIndex = nsMsgViewIndex_None;
         *pResultKey = nsMsgKey_None;
         return NS_OK;
@@ -6587,71 +6579,72 @@ nsresult nsMsgDBView::AndExtraFlag(nsMsg
 nsresult nsMsgDBView::SetExtraFlag(nsMsgViewIndex index, uint32_t extraflag) {
   if (!IsValidIndex(index)) return NS_MSG_INVALID_DBVIEW_INDEX;
 
   m_flags[index] = extraflag;
   OnExtraFlagChanged(index, extraflag);
   return NS_OK;
 }
 
-nsresult nsMsgDBView::ToggleIgnored(nsMsgViewIndex *indices, int32_t numIndices,
+nsresult nsMsgDBView::ToggleIgnored(nsTArray<nsMsgViewIndex> const& selection,
                                     nsMsgViewIndex *resultIndex,
                                     bool *resultToggleState) {
   nsCOMPtr<nsIMsgThread> thread;
 
   // Ignored state is toggled based on the first selected thread.
   nsMsgViewIndex threadIndex =
-      GetThreadFromMsgIndex(indices[0], getter_AddRefs(thread));
+      GetThreadFromMsgIndex(selection[0], getter_AddRefs(thread));
   uint32_t threadFlags;
   thread->GetFlags(&threadFlags);
   uint32_t ignored = threadFlags & nsMsgMessageFlags::Ignored;
 
   // Process threads in reverse order.
   // Otherwise collapsing the threads will invalidate the indices.
   threadIndex = nsMsgViewIndex_None;
+  uint32_t numIndices = selection.Length();
   while (numIndices) {
     numIndices--;
-    if (indices[numIndices] < threadIndex) {
+    if (selection[numIndices] < threadIndex) {
       threadIndex =
-          GetThreadFromMsgIndex(indices[numIndices], getter_AddRefs(thread));
+          GetThreadFromMsgIndex(selection[numIndices], getter_AddRefs(thread));
       thread->GetFlags(&threadFlags);
       if ((threadFlags & nsMsgMessageFlags::Ignored) == ignored)
         SetThreadIgnored(thread, threadIndex, !ignored);
     }
   }
 
   if (resultIndex) *resultIndex = threadIndex;
 
   if (resultToggleState) *resultToggleState = !ignored;
 
   return NS_OK;
 }
 
-nsresult nsMsgDBView::ToggleMessageKilled(nsMsgViewIndex *indices,
-                                          int32_t numIndices,
+nsresult nsMsgDBView::ToggleMessageKilled(nsTArray<nsMsgViewIndex> const& selection,
                                           nsMsgViewIndex *resultIndex,
                                           bool *resultToggleState) {
   NS_ENSURE_ARG_POINTER(resultToggleState);
 
   nsCOMPtr<nsIMsgDBHdr> header;
   // Ignored state is toggled based on the first selected message.
-  nsresult rv = GetMsgHdrForViewIndex(indices[0], getter_AddRefs(header));
+  nsresult rv = GetMsgHdrForViewIndex(selection[0], getter_AddRefs(header));
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint32_t msgFlags;
   header->GetFlags(&msgFlags);
   uint32_t ignored = msgFlags & nsMsgMessageFlags::Ignored;
 
   // Process messages in reverse order.
   // Otherwise the indices may be invalidated.
   nsMsgViewIndex msgIndex = nsMsgViewIndex_None;
+  uint32_t numIndices = selection.Length();
   while (numIndices) {
     numIndices--;
-    if (indices[numIndices] < msgIndex) {
-      msgIndex = indices[numIndices];
+    if (selection[numIndices] < msgIndex) {
+      msgIndex = selection[numIndices];
       rv = GetMsgHdrForViewIndex(msgIndex, getter_AddRefs(header));
       NS_ENSURE_SUCCESS(rv, rv);
       header->GetFlags(&msgFlags);
       if ((msgFlags & nsMsgMessageFlags::Ignored) == ignored)
         SetSubthreadKilled(header, msgIndex, !ignored);
     }
   }
 
@@ -6679,34 +6672,35 @@ nsMsgViewIndex nsMsgDBView::GetThreadFro
   if (msgKey != threadKey)
     threadIndex = GetIndexOfFirstDisplayedKeyInThread(*threadHdr);
   else
     threadIndex = index;
 
   return threadIndex;
 }
 
-nsresult nsMsgDBView::ToggleWatched(nsMsgViewIndex *indices,
-                                    int32_t numIndices) {
+nsresult nsMsgDBView::ToggleWatched(nsTArray<nsMsgViewIndex> const& selection) {
+  MOZ_ASSERT(!selection.IsEmpty());
   nsCOMPtr<nsIMsgThread> thread;
 
   // Watched state is toggled based on the first selected thread.
   nsMsgViewIndex threadIndex =
-      GetThreadFromMsgIndex(indices[0], getter_AddRefs(thread));
+      GetThreadFromMsgIndex(selection[0], getter_AddRefs(thread));
   uint32_t threadFlags;
   thread->GetFlags(&threadFlags);
   uint32_t watched = threadFlags & nsMsgMessageFlags::Watched;
 
   // Process threads in reverse order for consistency with ToggleIgnored.
   threadIndex = nsMsgViewIndex_None;
+  uint32_t numIndices = selection.Length();
   while (numIndices) {
     numIndices--;
-    if (indices[numIndices] < threadIndex) {
+    if (selection[numIndices] < threadIndex) {
       threadIndex =
-          GetThreadFromMsgIndex(indices[numIndices], getter_AddRefs(thread));
+          GetThreadFromMsgIndex(selection[numIndices], getter_AddRefs(thread));
       thread->GetFlags(&threadFlags);
       if ((threadFlags & nsMsgMessageFlags::Watched) == watched)
         SetThreadWatched(thread, threadIndex, !watched);
     }
   }
 
   return NS_OK;
 }
@@ -7024,49 +7018,54 @@ nsMsgDBView::OnDeleteCompleted(bool aSuc
 
 NS_IMETHODIMP
 nsMsgDBView::GetDb(nsIMsgDatabase **aDB) {
   NS_ENSURE_ARG_POINTER(aDB);
   NS_IF_ADDREF(*aDB = m_db);
   return NS_OK;
 }
 
-bool nsMsgDBView::OfflineMsgSelected(nsMsgViewIndex *indices,
-                                     int32_t numIndices) {
+bool nsMsgDBView::OfflineMsgSelected(nsTArray<nsMsgViewIndex> const& selection) {
   nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
-  if (localFolder) return true;
-
-  for (nsMsgViewIndex index = 0; index < (nsMsgViewIndex)numIndices; index++) {
+  if (localFolder) {
+    return true;
+  }
+
+  for (nsMsgViewIndex viewIndex : selection) {
     // For cross-folder saved searches, we need to check if any message
     // is in a local folder.
     if (!m_folder) {
       nsCOMPtr<nsIMsgFolder> folder;
-      GetFolderForViewIndex(indices[index], getter_AddRefs(folder));
+      GetFolderForViewIndex(viewIndex, getter_AddRefs(folder));
       nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(folder);
-      if (localFolder) return true;
+      if (localFolder) {
+        return true;
+      }
     }
 
-    uint32_t flags = m_flags[indices[index]];
-    if ((flags & nsMsgMessageFlags::Offline)) return true;
+    uint32_t flags = m_flags[viewIndex];
+    if ((flags & nsMsgMessageFlags::Offline)) {
+      return true;
+    }
   }
 
   return false;
 }
 
-bool nsMsgDBView::NonDummyMsgSelected(nsMsgViewIndex *indices,
-                                      int32_t numIndices) {
+bool nsMsgDBView::NonDummyMsgSelected(nsTArray<nsMsgViewIndex> const& selection) {
   bool includeCollapsedMsgs = OperateOnMsgsInCollapsedThreads();
 
-  for (nsMsgViewIndex index = 0; index < (nsMsgViewIndex)numIndices; index++) {
-    uint32_t flags = m_flags[indices[index]];
+  for (nsMsgViewIndex viewIndex : selection) {
+    uint32_t flags = m_flags[viewIndex];
     // We now treat having a collapsed dummy message selected as if
     // the whole group was selected so we can apply commands to the group.
     if (!(flags & MSG_VIEW_FLAG_DUMMY) ||
-        (flags & nsMsgMessageFlags::Elided && includeCollapsedMsgs))
+        (flags & nsMsgMessageFlags::Elided && includeCollapsedMsgs)) {
       return true;
+    }
   }
 
   return false;
 }
 
 NS_IMETHODIMP
 nsMsgDBView::GetViewIndexForFirstSelectedMsg(nsMsgViewIndex *aViewIndex) {
   NS_ENSURE_ARG_POINTER(aViewIndex);
--- a/mailnews/base/src/nsMsgDBView.h
+++ b/mailnews/base/src/nsMsgDBView.h
@@ -288,28 +288,29 @@ class nsMsgDBView : public nsIMsgDBView,
                                     nsMsgViewIndex startOfThreadViewIndex);
   virtual nsresult ListIdsInThreadOrder(nsIMsgThread* threadHdr,
                                         nsMsgKey parentKey, uint32_t level,
                                         nsMsgViewIndex* viewIndex,
                                         uint32_t* pNumListed);
   uint32_t GetSize(void) { return (m_keys.Length()); }
 
   // For commands.
-  virtual nsresult ApplyCommandToIndices(nsMsgViewCommandTypeValue command,
-                                         nsMsgViewIndex* indices,
-                                         int32_t numIndices);
+  virtual nsresult ApplyCommandToIndices(
+      nsMsgViewCommandTypeValue command,
+      nsTArray<nsMsgViewIndex> const& selection);
   virtual nsresult ApplyCommandToIndicesWithFolder(
-      nsMsgViewCommandTypeValue command, nsMsgViewIndex* indices,
-      int32_t numIndices, nsIMsgFolder* destFolder);
-  virtual nsresult CopyMessages(nsIMsgWindow* window, nsMsgViewIndex* indices,
-                                int32_t numIndices, bool isMove,
-                                nsIMsgFolder* destFolder);
-  virtual nsresult DeleteMessages(nsIMsgWindow* window, nsMsgViewIndex* indices,
-                                  int32_t numIndices, bool deleteStorage);
-  nsresult GetHeadersFromSelection(uint32_t* indices, uint32_t numIndices,
+      nsMsgViewCommandTypeValue command,
+      nsTArray<nsMsgViewIndex> const& selection, nsIMsgFolder* destFolder);
+  virtual nsresult CopyMessages(nsIMsgWindow* window,
+                                nsTArray<nsMsgViewIndex> const& selection,
+                                bool isMove, nsIMsgFolder* destFolder);
+  virtual nsresult DeleteMessages(nsIMsgWindow* window,
+                                  nsTArray<nsMsgViewIndex> const& selection,
+                                  bool deleteStorage);
+  nsresult GetHeadersFromSelection(nsTArray<nsMsgViewIndex> const& selection,
                                    nsIMutableArray* messageArray);
   virtual nsresult ListCollapsedChildren(nsMsgViewIndex viewIndex,
                                          nsIMutableArray* messageArray);
 
   nsresult SetMsgHdrJunkStatus(nsIJunkMailPlugin* aJunkPlugin,
                                nsIMsgDBHdr* aMsgHdr,
                                nsMsgJunkStatus aNewClassification);
   nsresult ToggleReadByIndex(nsMsgViewIndex index);
@@ -321,25 +322,25 @@ class nsMsgDBView : public nsIMsgDBView,
   nsresult SetLabelByIndex(nsMsgViewIndex index, nsMsgLabelValue label);
   nsresult OrExtraFlag(nsMsgViewIndex index, uint32_t orflag);
   nsresult AndExtraFlag(nsMsgViewIndex index, uint32_t andflag);
   nsresult SetExtraFlag(nsMsgViewIndex index, uint32_t extraflag);
   virtual nsresult RemoveByIndex(nsMsgViewIndex index);
   virtual void OnExtraFlagChanged(nsMsgViewIndex /*index*/,
                                   uint32_t /*extraFlag*/) {}
   virtual void OnHeaderAddedOrDeleted() {}
-  nsresult ToggleWatched(nsMsgViewIndex* indices, int32_t numIndices);
+  nsresult ToggleWatched(nsTArray<nsMsgViewIndex> const& selection);
   nsresult SetThreadWatched(nsIMsgThread* thread, nsMsgViewIndex index,
                             bool watched);
   nsresult SetThreadIgnored(nsIMsgThread* thread, nsMsgViewIndex threadIndex,
                             bool ignored);
   nsresult SetSubthreadKilled(nsIMsgDBHdr* header, nsMsgViewIndex msgIndex,
                               bool ignored);
-  nsresult DownloadForOffline(nsIMsgWindow* window, nsMsgViewIndex* indices,
-                              int32_t numIndices);
+  nsresult DownloadForOffline(nsIMsgWindow* window,
+                              nsTArray<nsMsgViewIndex> const& selection);
   nsresult DownloadFlaggedForOffline(nsIMsgWindow* window);
   nsMsgViewIndex GetThreadFromMsgIndex(nsMsgViewIndex index,
                                        nsIMsgThread** threadHdr);
   /// Should junk commands be enabled for the current message in the view?
   bool JunkControlsEnabled(nsMsgViewIndex aViewIndex);
 
   // For sorting.
   nsresult GetFieldTypeAndLenForSort(
@@ -376,23 +377,23 @@ class nsMsgDBView : public nsIMsgDBView,
   nsresult FindFirstFlagged(nsMsgViewIndex* pResultIndex);
   nsresult FindPrevFlagged(nsMsgViewIndex startIndex,
                            nsMsgViewIndex* pResultIndex);
   nsresult MarkThreadOfMsgRead(nsMsgKey msgId, nsMsgViewIndex msgIndex,
                                nsTArray<nsMsgKey>& idsMarkedRead, bool bRead);
   nsresult MarkThreadRead(nsIMsgThread* threadHdr, nsMsgViewIndex threadIndex,
                           nsTArray<nsMsgKey>& idsMarkedRead, bool bRead);
   bool IsValidIndex(nsMsgViewIndex index);
-  nsresult ToggleIgnored(nsMsgViewIndex* indices, int32_t numIndices,
+  nsresult ToggleIgnored(nsTArray<nsMsgViewIndex> const& selection,
                          nsMsgViewIndex* resultIndex, bool* resultToggleState);
-  nsresult ToggleMessageKilled(nsMsgViewIndex* indices, int32_t numIndices,
+  nsresult ToggleMessageKilled(nsTArray<nsMsgViewIndex> const& selection,
                                nsMsgViewIndex* resultIndex,
                                bool* resultToggleState);
-  bool OfflineMsgSelected(nsMsgViewIndex* indices, int32_t numIndices);
-  bool NonDummyMsgSelected(nsMsgViewIndex* indices, int32_t numIndices);
+  bool OfflineMsgSelected(nsTArray<nsMsgViewIndex> const& selection);
+  bool NonDummyMsgSelected(nsTArray<nsMsgViewIndex> const& selection);
   char16_t* GetString(const char16_t* aStringName);
   nsresult GetPrefLocalizedString(const char* aPrefName, nsString& aResult);
   nsresult AppendKeywordProperties(const nsACString& keywords,
                                    nsAString& properties, bool* tagAdded);
   nsresult InitLabelStrings(void);
   nsresult CopyDBView(nsMsgDBView* aNewMsgDBView,
                       nsIMessenger* aMessengerInstance,
                       nsIMsgWindow* aMsgWindow,
--- a/mailnews/base/src/nsMsgQuickSearchDBView.cpp
+++ b/mailnews/base/src/nsMsgQuickSearchDBView.cpp
@@ -67,28 +67,27 @@ nsMsgQuickSearchDBView::CopyDBView(nsMsg
                                   aCmdUpdater);
   nsMsgQuickSearchDBView* newMsgDBView = (nsMsgQuickSearchDBView*)aNewMsgDBView;
 
   // now copy all of our private member data
   newMsgDBView->m_origKeys = m_origKeys.Clone();
   return NS_OK;
 }
 
-nsresult nsMsgQuickSearchDBView::DeleteMessages(nsIMsgWindow* window,
-                                                nsMsgViewIndex* indices,
-                                                int32_t numIndices,
-                                                bool deleteStorage) {
-  for (nsMsgViewIndex i = 0; i < (nsMsgViewIndex)numIndices; i++) {
+nsresult nsMsgQuickSearchDBView::DeleteMessages(
+    nsIMsgWindow* window, nsTArray<nsMsgViewIndex> const& selection,
+    bool deleteStorage) {
+  for (nsMsgViewIndex viewIndex : selection) {
     nsCOMPtr<nsIMsgDBHdr> msgHdr;
-    (void)GetMsgHdrForViewIndex(indices[i], getter_AddRefs(msgHdr));
-    if (msgHdr) RememberDeletedMsgHdr(msgHdr);
+    (void)GetMsgHdrForViewIndex(viewIndex, getter_AddRefs(msgHdr));
+    if (msgHdr) {
+      RememberDeletedMsgHdr(msgHdr);
+    }
   }
-
-  return nsMsgDBView::DeleteMessages(window, indices, numIndices,
-                                     deleteStorage);
+  return nsMsgDBView::DeleteMessages(window, selection, deleteStorage);
 }
 
 NS_IMETHODIMP nsMsgQuickSearchDBView::DoCommand(
     nsMsgViewCommandTypeValue aCommand) {
   if (aCommand == nsMsgViewCommandType::markAllRead) {
     nsresult rv = NS_OK;
     m_folder->EnableNotifications(nsIMsgFolder::allMessageCountNotifications,
                                   false);
--- a/mailnews/base/src/nsMsgQuickSearchDBView.h
+++ b/mailnews/base/src/nsMsgQuickSearchDBView.h
@@ -61,18 +61,18 @@ class nsMsgQuickSearchDBView : public ns
   nsTArray<nsMsgKey> m_origKeys;
   bool m_usingCachedHits;
   bool m_cacheEmpty;
   nsCOMArray<nsIMsgDBHdr> m_hdrHits;
   virtual nsresult AddHdr(nsIMsgDBHdr* msgHdr,
                           nsMsgViewIndex* resultIndex = nullptr) override;
   virtual nsresult OnNewHeader(nsIMsgDBHdr* newHdr, nsMsgKey aParentKey,
                                bool ensureListed) override;
-  virtual nsresult DeleteMessages(nsIMsgWindow* window, nsMsgViewIndex* indices,
-                                  int32_t numIndices,
+  virtual nsresult DeleteMessages(nsIMsgWindow* window,
+                                  nsTArray<nsMsgViewIndex> const& selection,
                                   bool deleteStorage) override;
   virtual nsresult SortThreads(nsMsgViewSortTypeValue sortType,
                                nsMsgViewSortOrderValue sortOrder) override;
   virtual nsresult GetFirstMessageHdrToDisplayInThread(
       nsIMsgThread* threadHdr, nsIMsgDBHdr** result) override;
   virtual nsresult ExpansionDelta(nsMsgViewIndex index,
                                   int32_t* expansionDelta) override;
   virtual nsresult ListCollapsedChildren(
--- a/mailnews/base/src/nsMsgSearchDBView.cpp
+++ b/mailnews/base/src/nsMsgSearchDBView.cpp
@@ -739,31 +739,26 @@ NS_IMETHODIMP nsMsgSearchDBView::DoComma
       command == nsMsgViewCommandType::expandAll ||
       command == nsMsgViewCommandType::collapseAll)
     return nsMsgDBView::DoCommand(command);
 
   nsresult rv = NS_OK;
   nsMsgViewIndexArray selection;
   GetIndicesForSelection(selection);
 
-  nsMsgViewIndex* indices = selection.Elements();
-  int32_t numIndices = selection.Length();
-
   // We need to break apart the selection by folders, and then call
   // ApplyCommandToIndices with the command and the indices in the
   // selection that are from that folder.
 
-  mozilla::UniquePtr<nsTArray<uint32_t>[]> indexArrays;
+  mozilla::UniquePtr<nsTArray<nsMsgViewIndex>[]> indexArrays;
   int32_t numArrays;
-  rv = PartitionSelectionByFolder(indices, numIndices, indexArrays, &numArrays);
+  rv = PartitionSelectionByFolder(selection, indexArrays, &numArrays);
   NS_ENSURE_SUCCESS(rv, rv);
   for (int32_t folderIndex = 0; folderIndex < numArrays; folderIndex++) {
-    rv = ApplyCommandToIndices(command,
-                               (indexArrays.get())[folderIndex].Elements(),
-                               indexArrays[folderIndex].Length());
+    rv = ApplyCommandToIndices(command, (indexArrays.get())[folderIndex]);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return rv;
 }
 
 // This method removes the specified line from the view, and adjusts the
 // various flags and levels of affected messages.
@@ -808,100 +803,101 @@ nsresult nsMsgSearchDBView::RemoveByInde
       }
     }
   }
 
   m_folders.RemoveObjectAt(index);
   return nsMsgDBView::RemoveByIndex(index);
 }
 
-nsresult nsMsgSearchDBView::DeleteMessages(nsIMsgWindow* window,
-                                           nsMsgViewIndex* indices,
-                                           int32_t numIndices,
-                                           bool deleteStorage) {
-  nsresult rv = GetFoldersAndHdrsForSelection(indices, numIndices);
+nsresult nsMsgSearchDBView::DeleteMessages(
+    nsIMsgWindow* window, nsTArray<nsMsgViewIndex> const& selection,
+    bool deleteStorage) {
+  nsresult rv = GetFoldersAndHdrsForSelection(selection);
   NS_ENSURE_SUCCESS(rv, rv);
   if (mDeleteModel != nsMsgImapDeleteModels::MoveToTrash) deleteStorage = true;
 
   if (mDeleteModel != nsMsgImapDeleteModels::IMAPDelete) m_deletingRows = true;
 
   // Remember the deleted messages in case the user undoes the delete,
   // and we want to restore the hdr to the view, even if it no
   // longer matches the search criteria.
-  for (nsMsgViewIndex i = 0; i < (nsMsgViewIndex)numIndices; i++) {
+  for (nsMsgViewIndex viewIndex : selection) {
     nsCOMPtr<nsIMsgDBHdr> msgHdr;
-    (void)GetMsgHdrForViewIndex(indices[i], getter_AddRefs(msgHdr));
-    if (msgHdr) RememberDeletedMsgHdr(msgHdr);
+    (void)GetMsgHdrForViewIndex(viewIndex, getter_AddRefs(msgHdr));
+    if (msgHdr) {
+      RememberDeletedMsgHdr(msgHdr);
+    }
 
     // If we are deleting rows, save off the view indices.
-    if (m_deletingRows) mIndicesToNoteChange.AppendElement(indices[i]);
+    if (m_deletingRows) {
+      mIndicesToNoteChange.AppendElement(viewIndex);
+    }
   }
   rv = deleteStorage ? ProcessRequestsInAllFolders(window)
                      : ProcessRequestsInOneFolder(window);
   if (NS_FAILED(rv)) m_deletingRows = false;
 
   return rv;
 }
 
-nsresult nsMsgSearchDBView::CopyMessages(nsIMsgWindow* window,
-                                         nsMsgViewIndex* indices,
-                                         int32_t numIndices, bool isMove,
-                                         nsIMsgFolder* destFolder) {
-  GetFoldersAndHdrsForSelection(indices, numIndices);
+nsresult nsMsgSearchDBView::CopyMessages(
+    nsIMsgWindow* window, nsTArray<nsMsgViewIndex> const& selection,
+    bool isMove, nsIMsgFolder* destFolder) {
+  GetFoldersAndHdrsForSelection(selection);
   return ProcessRequestsInOneFolder(window);
 }
 
 nsresult nsMsgSearchDBView::PartitionSelectionByFolder(
-    nsMsgViewIndex* indices, int32_t numIndices,
-    mozilla::UniquePtr<nsTArray<uint32_t>[]>& indexArrays, int32_t* numArrays) {
-  nsMsgViewIndex i;
-  int32_t folderIndex;
+    nsTArray<nsMsgViewIndex> const& selection,
+    mozilla::UniquePtr<nsTArray<nsMsgViewIndex>[]>& indexArrays,
+    int32_t* numArrays) {
   nsCOMArray<nsIMsgFolder> uniqueFoldersSelected;
   nsTArray<uint32_t> numIndicesSelected;
   mCurIndex = 0;
 
   // Build unique folder list based on headers selected by the user.
-  for (i = 0; i < (nsMsgViewIndex)numIndices; i++) {
-    nsIMsgFolder* curFolder = m_folders[indices[i]];
-    folderIndex = uniqueFoldersSelected.IndexOf(curFolder);
+  for (nsMsgViewIndex viewIndex : selection) {
+    nsIMsgFolder* curFolder = m_folders[viewIndex];
+    int32_t folderIndex = uniqueFoldersSelected.IndexOf(curFolder);
     if (folderIndex < 0) {
       uniqueFoldersSelected.AppendObject(curFolder);
       numIndicesSelected.AppendElement(1);
     } else {
       numIndicesSelected[folderIndex]++;
     }
   }
 
   int32_t numFolders = uniqueFoldersSelected.Count();
-  indexArrays = mozilla::MakeUnique<nsTArray<uint32_t>[]>(numFolders);
+  indexArrays = mozilla::MakeUnique<nsTArray<nsMsgViewIndex>[]>(numFolders);
   *numArrays = numFolders;
   NS_ENSURE_TRUE(indexArrays, NS_ERROR_OUT_OF_MEMORY);
-  for (folderIndex = 0; folderIndex < numFolders; folderIndex++) {
+  for (int32_t folderIndex = 0; folderIndex < numFolders; folderIndex++) {
     (indexArrays.get())[folderIndex].SetCapacity(
         numIndicesSelected[folderIndex]);
   }
-  for (i = 0; i < (nsMsgViewIndex)numIndices; i++) {
-    nsIMsgFolder* curFolder = m_folders[indices[i]];
+  for (nsMsgViewIndex viewIndex : selection) {
+    nsIMsgFolder* curFolder = m_folders[viewIndex];
     int32_t folderIndex = uniqueFoldersSelected.IndexOf(curFolder);
-    (indexArrays.get())[folderIndex].AppendElement(indices[i]);
+    (indexArrays.get())[folderIndex].AppendElement(viewIndex);
   }
   return NS_OK;
 }
 
 nsresult nsMsgSearchDBView::GetFoldersAndHdrsForSelection(
-    nsMsgViewIndex* indices, int32_t numIndices) {
+    nsTArray<nsMsgViewIndex> const& selection) {
   nsresult rv = NS_OK;
   mCurIndex = 0;
   m_uniqueFoldersSelected.Clear();
   m_hdrsForEachFolder.Clear();
 
   nsCOMPtr<nsIMutableArray> messages(
       do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = GetHeadersFromSelection(indices, numIndices, messages);
+  rv = GetHeadersFromSelection(selection, messages);
   NS_ENSURE_SUCCESS(rv, rv);
   uint32_t numMsgs;
   messages->GetLength(&numMsgs);
 
   uint32_t i;
   // Build unique folder list based on headers selected by the user.
   for (i = 0; i < numMsgs; i++) {
     nsCOMPtr<nsIMsgDBHdr> hdr = do_QueryElementAt(messages, i, &rv);
@@ -931,22 +927,22 @@ nsresult nsMsgSearchDBView::GetFoldersAn
 
     m_hdrsForEachFolder.AppendElement(msgHdrsForOneFolder.Clone());
   }
 
   return rv;
 }
 
 nsresult nsMsgSearchDBView::ApplyCommandToIndicesWithFolder(
-    nsMsgViewCommandTypeValue command, nsMsgViewIndex* indices,
-    int32_t numIndices, nsIMsgFolder* destFolder) {
+    nsMsgViewCommandTypeValue command,
+    nsTArray<nsMsgViewIndex> const& selection, nsIMsgFolder* destFolder) {
   mCommand = command;
   mDestFolder = destFolder;
-  return nsMsgDBView::ApplyCommandToIndicesWithFolder(command, indices,
-                                                      numIndices, destFolder);
+  return nsMsgDBView::ApplyCommandToIndicesWithFolder(command, selection,
+                                                      destFolder);
 }
 
 // nsIMsgCopyServiceListener methods
 
 NS_IMETHODIMP
 nsMsgSearchDBView::OnStartCopy() { return NS_OK; }
 
 NS_IMETHODIMP
--- a/mailnews/base/src/nsMsgSearchDBView.h
+++ b/mailnews/base/src/nsMsgSearchDBView.h
@@ -91,45 +91,46 @@ class nsMsgSearchDBView : public nsMsgGr
   virtual nsresult ListIdsInThread(nsIMsgThread* threadHdr,
                                    nsMsgViewIndex startOfThreadViewIndex,
                                    uint32_t* pNumListed) override;
   nsresult FetchLocation(int32_t aRow, nsAString& aLocationString);
   virtual nsresult AddHdrFromFolder(nsIMsgDBHdr* msgHdr, nsIMsgFolder* folder);
   virtual nsresult GetDBForViewIndex(nsMsgViewIndex index,
                                      nsIMsgDatabase** db) override;
   virtual nsresult RemoveByIndex(nsMsgViewIndex index) override;
-  virtual nsresult CopyMessages(nsIMsgWindow* window, nsMsgViewIndex* indices,
-                                int32_t numIndices, bool isMove,
-                                nsIMsgFolder* destFolder) override;
-  virtual nsresult DeleteMessages(nsIMsgWindow* window, nsMsgViewIndex* indices,
-                                  int32_t numIndices,
+  virtual nsresult CopyMessages(nsIMsgWindow* window,
+                                nsTArray<nsMsgViewIndex> const& selection,
+                                bool isMove, nsIMsgFolder* destFolder) override;
+  virtual nsresult DeleteMessages(nsIMsgWindow* window,
+                                  nsTArray<nsMsgViewIndex> const& selection,
                                   bool deleteStorage) override;
   virtual void InsertMsgHdrAt(nsMsgViewIndex index, nsIMsgDBHdr* hdr,
                               nsMsgKey msgKey, uint32_t flags,
                               uint32_t level) override;
   virtual void SetMsgHdrAt(nsIMsgDBHdr* hdr, nsMsgViewIndex index,
                            nsMsgKey msgKey, uint32_t flags,
                            uint32_t level) override;
   virtual void InsertEmptyRows(nsMsgViewIndex viewIndex,
                                int32_t numRows) override;
   virtual void RemoveRows(nsMsgViewIndex viewIndex, int32_t numRows) override;
   virtual nsMsgViewIndex FindHdr(nsIMsgDBHdr* msgHdr,
                                  nsMsgViewIndex startIndex = 0,
                                  bool allowDummy = false) override;
-  nsresult GetFoldersAndHdrsForSelection(nsMsgViewIndex* indices,
-                                         int32_t numIndices);
+  nsresult GetFoldersAndHdrsForSelection(
+      nsTArray<nsMsgViewIndex> const& selection);
   nsresult GroupSearchResultsByFolder();
   nsresult PartitionSelectionByFolder(
-      nsMsgViewIndex* indices, int32_t numIndices,
+      nsTArray<nsMsgViewIndex> const& selection,
       mozilla::UniquePtr<nsTArray<uint32_t>[]>& indexArrays,
       int32_t* numArrays);
 
   virtual nsresult ApplyCommandToIndicesWithFolder(
-      nsMsgViewCommandTypeValue command, nsMsgViewIndex* indices,
-      int32_t numIndices, nsIMsgFolder* destFolder) override;
+      nsMsgViewCommandTypeValue command,
+      nsTArray<nsMsgViewIndex> const& selection,
+      nsIMsgFolder* destFolder) override;
   void MoveThreadAt(nsMsgViewIndex threadIndex);
 
   virtual nsresult GetMessageEnumerator(nsIMsgEnumerator** enumerator) override;
   virtual nsresult InsertHdrFromFolder(nsIMsgDBHdr* msgHdr,
                                        nsIMsgFolder* folder);
 
   // Holds the original folder of each message in this view.
   // Augments the existing arrays in nsMsgDBView (m_keys, m_flags and m_levels),