diff options
Diffstat (limited to 'comm/mailnews/db/msgdb/src/nsMsgDatabaseEnumerators.cpp')
-rw-r--r-- | comm/mailnews/db/msgdb/src/nsMsgDatabaseEnumerators.cpp | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/comm/mailnews/db/msgdb/src/nsMsgDatabaseEnumerators.cpp b/comm/mailnews/db/msgdb/src/nsMsgDatabaseEnumerators.cpp new file mode 100644 index 0000000000..a64ef46f57 --- /dev/null +++ b/comm/mailnews/db/msgdb/src/nsMsgDatabaseEnumerators.cpp @@ -0,0 +1,317 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsMsgDatabaseEnumerators.h" +#include "nsMsgDatabase.h" +#include "nsIMsgHdr.h" +#include "nsMsgThread.h" + +/* + * nsMsgDBEnumerator implementation + */ + +nsMsgDBEnumerator::nsMsgDBEnumerator(nsMsgDatabase* db, nsIMdbTable* table, + nsMsgDBEnumeratorFilter filter, + void* closure, bool iterateForwards) + : mDB(db), + mDone(false), + mIterateForwards(iterateForwards), + mFilter(filter), + mClosure(closure) { + mTable = table; + mRowPos = 0; + mDB->m_msgEnumerators.AppendElement(this); +} + +nsMsgDBEnumerator::~nsMsgDBEnumerator() { Invalidate(); } + +void nsMsgDBEnumerator::Invalidate() { + // Order is important here. If the database is destroyed first, releasing + // the cursor will crash (due, I think, to a disconnect between XPCOM and + // Mork internal memory management). + mRowCursor = nullptr; + mTable = nullptr; + mResultHdr = nullptr; + mDone = true; + if (mDB) { + mDB->m_msgEnumerators.RemoveElement(this); + mDB = nullptr; + } +} + +nsresult nsMsgDBEnumerator::GetRowCursor() { + mDone = false; + + if (!mDB || !mTable) return NS_ERROR_NULL_POINTER; + + if (mIterateForwards) { + mRowPos = -1; + } else { + mdb_count numRows; + mTable->GetCount(mDB->GetEnv(), &numRows); + mRowPos = numRows; // startPos is 0 relative. + } + return mTable->GetTableRowCursor(mDB->GetEnv(), mRowPos, + getter_AddRefs(mRowCursor)); +} + +NS_IMETHODIMP nsMsgDBEnumerator::GetNext(nsIMsgDBHdr** aItem) { + if (!aItem) return NS_ERROR_NULL_POINTER; + *aItem = nullptr; + + // If we've already got one ready, return it. + if (mResultHdr) { + mResultHdr.forget(aItem); + return NS_OK; + } + + // Bail out if enumerator has been invalidated. + if (!mDB) { + return NS_ERROR_FAILURE; + } + + nsresult rv = InternalGetNext(aItem); + NS_ENSURE_SUCCESS(rv, rv); + return *aItem ? NS_OK : NS_ERROR_FAILURE; +} + +nsresult nsMsgDBEnumerator::InternalGetNext(nsIMsgDBHdr** nextHdr) { + nsresult rv; + + *nextHdr = nullptr; + + if (!mRowCursor) { + rv = GetRowCursor(); + NS_ENSURE_SUCCESS(rv, rv); + } + + while (true) { + nsIMdbRow* hdrRow; + if (mIterateForwards) { + rv = mRowCursor->NextRow(mDB->GetEnv(), &hdrRow, &mRowPos); + } else { + rv = mRowCursor->PrevRow(mDB->GetEnv(), &hdrRow, &mRowPos); + } + NS_ENSURE_SUCCESS(rv, rv); + if (!hdrRow) { + // No more rows, so we're done. + *nextHdr = nullptr; + return NS_OK; + } + + // Get key from row + mdbOid outOid; + nsMsgKey key = nsMsgKey_None; + rv = hdrRow->GetOid(mDB->GetEnv(), &outOid); + NS_ENSURE_SUCCESS(rv, rv); + key = outOid.mOid_Id; + + nsCOMPtr<nsIMsgDBHdr> hdr; + rv = mDB->CreateMsgHdr(hdrRow, key, getter_AddRefs(hdr)); + NS_ENSURE_SUCCESS(rv, rv); + + // Ignore expunged messages. + uint32_t flags; + hdr->GetFlags(&flags); + if (flags & nsMsgMessageFlags::Expunged) { + continue; + } + + // Ignore anything which doesn't pass the filter func (if there is one). + if (mFilter && NS_FAILED(mFilter(hdr, mClosure))) { + continue; + } + + // If we get this far, we've found it. + hdr.forget(nextHdr); + return NS_OK; + } +} + +NS_IMETHODIMP nsMsgDBEnumerator::HasMoreElements(bool* aResult) { + if (!aResult) return NS_ERROR_NULL_POINTER; + + if (!mResultHdr) { + // Bail out if enumerator has been invalidated. + if (!mDB) { + return NS_ERROR_FAILURE; + } + + nsresult rv = InternalGetNext(getter_AddRefs(mResultHdr)); + NS_ENSURE_SUCCESS(rv, rv); + if (!mResultHdr) { + mDone = true; + } + } + + *aResult = !mDone; + return NS_OK; +} + +/* + * nsMsgFilteredDBEnumerator implementation + */ + +nsMsgFilteredDBEnumerator::nsMsgFilteredDBEnumerator(nsMsgDatabase* db, + nsIMdbTable* table, + bool reverse) + : nsMsgDBEnumerator(db, table, nullptr, nullptr, !reverse) {} + +nsMsgFilteredDBEnumerator::~nsMsgFilteredDBEnumerator() {} + +/** + * Create the search session for the enumerator, + * add the scope term for "folder" to the search session, and add the search + * terms in the array to the search session. + */ +nsresult nsMsgFilteredDBEnumerator::InitSearchSession( + const nsTArray<RefPtr<nsIMsgSearchTerm>>& searchTerms, + nsIMsgFolder* folder) { + nsresult rv; + m_searchSession = + do_CreateInstance("@mozilla.org/messenger/searchSession;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + m_searchSession->AddScopeTerm(nsMsgSearchScope::offlineMail, folder); + for (auto searchTerm : searchTerms) { + m_searchSession->AppendTerm(searchTerm); + } + return NS_OK; +} + +nsresult nsMsgFilteredDBEnumerator::InternalGetNext(nsIMsgDBHdr** nextHdr) { + nsCOMPtr<nsIMsgDBHdr> hdr; + while (true) { + nsresult rv = nsMsgDBEnumerator::InternalGetNext(getter_AddRefs(hdr)); + NS_ENSURE_SUCCESS(rv, rv); + if (!hdr) { + break; // No more. + } + bool matches; + rv = m_searchSession->MatchHdr(hdr, mDB, &matches); + NS_ENSURE_SUCCESS(rv, rv); + if (matches) { + break; // Found one! + } + } + hdr.forget(nextHdr); + return NS_OK; +} + +/* + * nsMsgDBThreadEnumerator implementation + */ + +nsMsgDBThreadEnumerator::nsMsgDBThreadEnumerator( + nsMsgDatabase* db, nsMsgDBThreadEnumeratorFilter filter) + : mDB(db), + mTableCursor(nullptr), + mResultThread(nullptr), + mDone(false), + mFilter(filter) { + mDB->m_threadEnumerators.AppendElement(this); + mNextPrefetched = false; +} + +nsMsgDBThreadEnumerator::~nsMsgDBThreadEnumerator() { Invalidate(); } + +void nsMsgDBThreadEnumerator::Invalidate() { + // Order is important here. If the database is destroyed first, releasing + // the cursor will crash (due, I think, to a disconnect between XPCOM and + // Mork internal memory management). + mTableCursor = nullptr; + mResultThread = nullptr; + mDone = true; + if (mDB) { + mDB->m_threadEnumerators.RemoveElement(this); + mDB = nullptr; + } +} + +nsresult nsMsgDBThreadEnumerator::GetTableCursor(void) { + nsresult rv = NS_OK; + + // DB might have disappeared. + if (!mDB || !mDB->m_mdbStore) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(rv)) return rv; + return NS_OK; +} + +NS_IMETHODIMP nsMsgDBThreadEnumerator::HasMoreElements(bool* aResult) { + NS_ENSURE_ARG_POINTER(aResult); + + if (!mNextPrefetched) { + PrefetchNext(); + } + *aResult = !mDone; + return NS_OK; +} + +NS_IMETHODIMP nsMsgDBThreadEnumerator::GetNext(nsIMsgThread** aItem) { + NS_ENSURE_ARG_POINTER(aItem); + + *aItem = nullptr; + nsresult rv = NS_OK; + if (!mNextPrefetched) rv = PrefetchNext(); + if (NS_SUCCEEDED(rv)) { + if (mResultThread) { + NS_ADDREF(*aItem = mResultThread); + mNextPrefetched = false; + } + } + return rv; +} + +nsresult nsMsgDBThreadEnumerator::PrefetchNext() { + nsresult rv; + + // DB might have disappeared. + if (!mDB || !mDB->m_mdbStore) { + return NS_ERROR_NULL_POINTER; + } + + if (!mTableCursor) { + rv = mDB->m_mdbStore->GetPortTableCursor( + mDB->GetEnv(), mDB->m_hdrRowScopeToken, mDB->m_threadTableKindToken, + getter_AddRefs(mTableCursor)); + NS_ENSURE_SUCCESS(rv, rv); + } + + nsCOMPtr<nsIMdbTable> table; + while (true) { + mResultThread = nullptr; + rv = mTableCursor->NextTable(mDB->GetEnv(), getter_AddRefs(table)); + if (!table) { + mDone = true; + return NS_ERROR_FAILURE; + } + if (NS_FAILED(rv)) { + mDone = true; + return rv; + } + + mdbOid tableId; + table->GetOid(mDB->GetEnv(), &tableId); + + mResultThread = mDB->FindExistingThread(tableId.mOid_Id); + if (!mResultThread) mResultThread = new nsMsgThread(mDB, table); + + if (mResultThread) { + uint32_t numChildren = 0; + mResultThread->GetNumChildren(&numChildren); + // we've got empty thread; don't tell caller about it. + if (numChildren == 0) continue; + } + if (mFilter && NS_FAILED(mFilter(mResultThread))) + continue; + else + break; + } + if (mResultThread) { + mNextPrefetched = true; + return NS_OK; + } + return NS_ERROR_FAILURE; +} |