NS_IMETHODIMP
nsMsgXFVirtualFolderDBView::OnNewSearch()
{
  PRInt32 oldSize = GetSize();

  RemovePendingDBListeners();

  m_doingSearch = PR_TRUE;

  m_folders->Clear();
  m_keys.Clear();
  m_levels.Clear();
  m_flags.Clear();

  // needs to happen after we remove the keys, since RowCountChanged() will call our GetRowCount()
  if (mTree)
    mTree->RowCountChanged(0, -oldSize);

  // to use the search results cache, we'll need to iterate over the scopes in the
  // search session, calling getNthSearchScope for i = 0; i < searchSession.countSearchScopes; i++
  // and for each folder, then open the db and pull out the cached hits, add them to the view.
  // For each hit in a new folder, we'll then clean up the stale hits from the previous folder(s).

  PRInt32 scopeCount;
  nsCOMPtr <nsIMsgSearchSession> searchSession = do_QueryReferent(m_searchSession);
  nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID);
  searchSession->CountSearchScopes(&scopeCount);
  if (mTree)
    mTree->BeginUpdateBatch();
  for (PRInt32 i = 0; i < scopeCount; i++)
  {
    nsMsgSearchScopeValue scopeId;
    nsCOMPtr<nsIMsgFolder> searchFolder;
    searchSession->GetNthSearchScope(i, &scopeId, getter_AddRefs(searchFolder));
    if (searchFolder)
    {
      nsCOMPtr<nsISimpleEnumerator> cachedHits;
      nsCOMPtr<nsIMsgDatabase> searchDB;
      nsCString searchUri;
      m_viewFolder->GetURI(searchUri);
      nsresult rv = searchFolder->GetMsgDatabase(nsnull, getter_AddRefs(searchDB));
      if (NS_SUCCEEDED(rv) && searchDB)
      {
        if (msgDBService)
          msgDBService->RegisterPendingListener(searchFolder, this);

        m_foldersSearchingOver.AppendObject(searchFolder);
        searchDB->GetCachedHits(searchUri.get(), getter_AddRefs(cachedHits));
        PRBool hasMore;
        if (cachedHits)
        {
          cachedHits->HasMoreElements(&hasMore);
          if (hasMore)
          {
             nsMsgKey prevKey = nsMsgKey_None;
            while (hasMore)
            {
              nsCOMPtr <nsIMsgDBHdr> pHeader;
              nsresult rv = cachedHits->GetNext(getter_AddRefs(pHeader));
              NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken");
              if (pHeader && NS_SUCCEEDED(rv))
              {
                nsMsgKey msgKey;
                pHeader->GetMessageKey(&msgKey);
                NS_ASSERTION(prevKey == nsMsgKey_None || msgKey > prevKey, "cached Hits not sorted");
                prevKey = msgKey;
                AddHdrFromFolder(pHeader, searchFolder); // need to QI to nsISupports?
              }
              else
                break;
              cachedHits->HasMoreElements(&hasMore);
            }
          }
        }
      }
    }
  }
  if (mTree)
    mTree->EndUpdateBatch();

  m_curFolderStartKeyIndex = 0;
  m_curFolderGettingHits = nsnull;
  m_curFolderHasCachedHits = PR_FALSE;

  // if we have cached hits, sort them.
  if (GetSize() > 0)
  {
    if (m_sortType != nsMsgViewSortType::byThread)
    {
      m_sortValid = PR_FALSE;       //sort the results
      Sort(m_sortType, m_sortOrder);
    }
  }
//    mSearchResults->Clear();
    return NS_OK;
}
NS_IMETHODIMP nsMsgXFVirtualFolderDBView::Close()
{
  RemovePendingDBListeners();
  return nsMsgSearchDBView::Close();
}
NS_IMETHODIMP
nsMsgXFVirtualFolderDBView::OnNewSearch()
{
  int32_t oldSize = GetSize();

  RemovePendingDBListeners();
  m_doingSearch = true;
  m_totalMessagesInView = 0;
  m_folders.Clear();
  m_keys.Clear();
  m_levels.Clear();
  m_flags.Clear();

  // needs to happen after we remove the keys, since RowCountChanged() will call our GetRowCount()
  if (mTree)
    mTree->RowCountChanged(0, -oldSize);

  // to use the search results cache, we'll need to iterate over the scopes in the
  // search session, calling getNthSearchScope for i = 0; i < searchSession.countSearchScopes; i++
  // and for each folder, then open the db and pull out the cached hits, add them to the view.
  // For each hit in a new folder, we'll then clean up the stale hits from the previous folder(s).

  int32_t scopeCount;
  nsCOMPtr<nsIMsgSearchSession> searchSession = do_QueryReferent(m_searchSession);
  NS_ENSURE_TRUE(searchSession, NS_OK); // just ignore
  nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID);
  searchSession->CountSearchScopes(&scopeCount);

  // Figure out how many search terms the virtual folder has.
  nsCOMPtr<nsIMsgDatabase> virtDatabase;
  nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
  nsresult rv = m_viewFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(virtDatabase));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCString terms;
  dbFolderInfo->GetCharProperty("searchStr", terms);
  nsCOMPtr<nsISupportsArray> searchTerms;
  rv = searchSession->GetSearchTerms(getter_AddRefs(searchTerms));
  NS_ENSURE_SUCCESS(rv, rv);
  nsCString curSearchAsString;

  rv = MsgTermListToString(searchTerms, curSearchAsString);
  // Trim off the initial AND/OR, which is irrelevant and inconsistent between
  // what searchSpec.js generates, and what's in virtualFolders.dat.
  curSearchAsString.Cut(0, StringBeginsWith(curSearchAsString, NS_LITERAL_CSTRING("AND")) ? 3 : 2);
  terms.Cut(0, StringBeginsWith(terms, NS_LITERAL_CSTRING("AND")) ? 3 : 2);

  NS_ENSURE_SUCCESS(rv, rv);
  // If the search session search string doesn't match the vf search str, then we're doing
  // quick search, which means we don't want to invalidate cached results, or
  // used cached results.
  m_doingQuickSearch = !curSearchAsString.Equals(terms);

  if (mTree && !m_doingQuickSearch)
    mTree->BeginUpdateBatch();

  for (int32_t i = 0; i < scopeCount; i++)
  {
    nsMsgSearchScopeValue scopeId;
    nsCOMPtr<nsIMsgFolder> searchFolder;
    searchSession->GetNthSearchScope(i, &scopeId, getter_AddRefs(searchFolder));
    if (searchFolder)
    {
      nsCOMPtr<nsISimpleEnumerator> cachedHits;
      nsCOMPtr<nsIMsgDatabase> searchDB;
      nsCString searchUri;
      m_viewFolder->GetURI(searchUri);
      nsresult rv = searchFolder->GetMsgDatabase(getter_AddRefs(searchDB));
      if (NS_SUCCEEDED(rv) && searchDB)
      {
        if (msgDBService)
          msgDBService->RegisterPendingListener(searchFolder, this);

        m_foldersSearchingOver.AppendObject(searchFolder);
        if (m_doingQuickSearch) // ignore cached hits in quick search case.
          continue;
        searchDB->GetCachedHits(searchUri.get(), getter_AddRefs(cachedHits));
        bool hasMore;
        if (cachedHits)
        {
          cachedHits->HasMoreElements(&hasMore);
          if (hasMore)
          {
            nsMsgKey prevKey = nsMsgKey_None;
            while (hasMore)
            {
              nsCOMPtr <nsIMsgDBHdr> pHeader;
              nsresult rv = cachedHits->GetNext(getter_AddRefs(pHeader));
              NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken");
              if (pHeader && NS_SUCCEEDED(rv))
              {
                nsMsgKey msgKey;
                pHeader->GetMessageKey(&msgKey);
                NS_ASSERTION(prevKey == nsMsgKey_None || msgKey > prevKey, "cached Hits not sorted");
                prevKey = msgKey;
                AddHdrFromFolder(pHeader, searchFolder);
              }
              else
                break;
              cachedHits->HasMoreElements(&hasMore);
            }
          }
        }
      }
    }
  }
  if (mTree && !m_doingQuickSearch)
    mTree->EndUpdateBatch();

  m_curFolderStartKeyIndex = 0;
  m_curFolderGettingHits = nullptr;
  m_curFolderHasCachedHits = false;

  // if we have cached hits, sort them.
  if (GetSize() > 0)
  {
    // currently, we keep threaded views sorted while we build them.
    if (m_sortType != nsMsgViewSortType::byThread &&
      !(m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay))
    {
      m_sortValid = false;       //sort the results
      Sort(m_sortType, m_sortOrder);
    }
  }
  return NS_OK;
}