Bug 1608304 part 3 - Fix offline replication of LDAP address books; r=mkmelin

--HG--
extra : rebase_source : 5b1105f79ccb2a9c24a969c40a33f6ef6f0c71df
extra : histedit_source : 56d769ac55fd52663fcec0d1a5512f549416ee05
This commit is contained in:
Geoff Lankow 2020-01-03 16:10:09 +13:00
Родитель decc05d54d
Коммит 5fc8ad38d2
5 изменённых файлов: 142 добавлений и 155 удалений

Просмотреть файл

@ -100,15 +100,21 @@ AddrBookDirectory.prototype = {
// nothing else ever tells us to close them.
var connections = new Map();
var closeObserver = {
observe() {
for (let connection of connections.values()) {
connection.asyncClose();
}
connections.clear();
},
};
Services.obs.addObserver(closeObserver, "quit-application");
Services.obs.addObserver(() => {
for (let connection of connections.values()) {
connection.asyncClose();
}
connections.clear();
directories.clear();
}, "quit-application");
// Close a connection on demand. This serves as an escape hatch from C++ code.
Services.obs.addObserver(async file => {
file.QueryInterface(Ci.nsIFile);
await closeConnectionTo(file);
Services.obs.notifyObservers(file, "addrbook-close-ab-complete");
}, "addrbook-close-ab");
/**
* Opens an SQLite connection to `file`, caches the connection, and upgrades

Просмотреть файл

@ -60,6 +60,12 @@
} \
}
//
// JS/SQLite address book
//
#define NS_ABJSDIRECTORY_CONTRACTID \
NS_AB_DIRECTORY_TYPE_CONTRACTID_PREFIX "jsaddrbook"
//
// nsAddressBookDB
//

Просмотреть файл

@ -125,8 +125,11 @@ NS_IMETHODIMP nsAbLDAPDirectory::GetChildCards(nsISimpleEnumerator **result) {
do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAbDirectory> directory;
rv = abManager->GetDirectory(localDirectoryURI, getter_AddRefs(directory));
nsCOMPtr<nsIAbDirectory> directory =
do_CreateInstance(NS_ABJSDIRECTORY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = directory->Init(localDirectoryURI.get());
NS_ENSURE_SUCCESS(rv, rv);
rv = directory->GetChildCards(result);

Просмотреть файл

@ -5,7 +5,6 @@
#include "nsILDAPMessage.h"
#include "nsAbLDAPReplicationData.h"
#include "nsIAbManager.h"
#include "nsIAbCard.h"
#include "nsAbBaseCID.h"
#include "nsAbUtils.h"
@ -13,20 +12,25 @@
#include "nsILDAPErrors.h"
#include "nsComponentManagerUtils.h"
#include "nsMsgUtils.h"
#include "nsIObserverService.h"
#include "nsIObserver.h"
#include "mozilla/Services.h"
using namespace mozilla;
// once bug # 101252 gets fixed, this should be reverted back to be non
// threadsafe implementation is not really thread safe since each object should
// exist independently along with its related independent
// nsAbLDAPReplicationQuery object.
NS_IMPL_ISUPPORTS(nsAbLDAPProcessReplicationData,
nsIAbLDAPProcessReplicationData, nsILDAPMessageListener)
nsIAbLDAPProcessReplicationData, nsILDAPMessageListener,
nsIObserver)
nsAbLDAPProcessReplicationData::nsAbLDAPProcessReplicationData()
: nsAbLDAPListenerBase(),
mState(kIdle),
mProtocol(-1),
mCount(0),
mDBOpen(false),
mInitialized(false) {}
nsAbLDAPProcessReplicationData::~nsAbLDAPProcessReplicationData() {}
@ -127,24 +131,7 @@ NS_IMETHODIMP nsAbLDAPProcessReplicationData::Abort() {
if (NS_SUCCEEDED(rv)) mState = kIdle;
}
if (mReplicationDB && mDBOpen) {
mDBOpen = false;
// delete the unsaved replication file
if (mReplicationFile) {
rv = mReplicationFile->Remove(false);
if (NS_SUCCEEDED(rv) && mDirectory) {
nsAutoCString fileName;
rv = mDirectory->GetReplicationFileName(fileName);
// now put back the backed up replicated file if aborted
if (NS_SUCCEEDED(rv) && mBackupReplicationFile)
rv = mBackupReplicationFile->MoveToNative(nullptr, fileName);
}
}
}
Done(false);
return rv;
}
@ -153,7 +140,7 @@ nsresult nsAbLDAPProcessReplicationData::DoTask() {
nsresult rv = OpenABForReplicatedDir(true);
if (NS_FAILED(rv))
// do not call done here since it is called by OpenABForReplicationDir
// do not call done here since it is called by OpenABForReplicatedDir
return rv;
mOperation = do_CreateInstance(NS_LDAPOPERATION_CONTRACTID, &rv);
@ -203,7 +190,7 @@ nsresult nsAbLDAPProcessReplicationData::OnLDAPSearchEntry(
if (!mInitialized) return NS_ERROR_NOT_INITIALIZED;
// since this runs on the main thread and is single threaded, this will
// take care of entries returned by LDAP Connection thread after Abort.
if (!mReplicationDB || !mDBOpen) return NS_ERROR_FAILURE;
if (!mReplicationDB) return NS_ERROR_FAILURE;
nsresult rv = NS_OK;
@ -272,52 +259,21 @@ nsresult nsAbLDAPProcessReplicationData::OnLDAPSearchResult(
if (errorCode == nsILDAPErrors::SUCCESS ||
errorCode == nsILDAPErrors::SIZELIMIT_EXCEEDED) {
Done(true);
if (mReplicationDB && mDBOpen) {
mDBOpen = false;
// once we have saved the new replication file, delete the backup file
if (mBackupReplicationFile) {
rv = mBackupReplicationFile->Remove(false);
NS_ASSERTION(NS_SUCCEEDED(rv),
"Replication BackupFile Remove on Success failed");
}
}
return NS_OK;
}
}
// in case if GetErrorCode returned error or errorCode is not SUCCESS /
// SIZELIMIT_EXCEEDED
if (mReplicationDB && mDBOpen) {
// if error result is returned close the DB without saving ???
// should we commit anyway ??? whatever is returned is not lost then !!
mDBOpen = false;
// if error result is returned remove the replicated file
if (mReplicationFile) {
rv = mReplicationFile->Remove(false);
NS_ASSERTION(NS_SUCCEEDED(rv),
"Replication File Remove on Failure failed");
if (NS_SUCCEEDED(rv)) {
// now put back the backed up replicated file
if (mBackupReplicationFile && mDirectory) {
nsAutoCString fileName;
rv = mDirectory->GetReplicationFileName(fileName);
if (NS_SUCCEEDED(rv) && !fileName.IsEmpty()) {
rv = mBackupReplicationFile->MoveToNative(nullptr, fileName);
NS_ASSERTION(NS_SUCCEEDED(rv),
"Replication Backup File Move back on Failure failed");
}
}
}
}
Done(false);
}
Done(false);
return NS_OK;
}
nsresult nsAbLDAPProcessReplicationData::OpenABForReplicatedDir(bool aCreate) {
if (!mInitialized) return NS_ERROR_NOT_INITIALIZED;
mDirectory->GetReplicationFileName(mReplicationFileName);
nsresult rv =
mDirectory->GetReplicationFile(getter_AddRefs(mReplicationFile));
if (NS_FAILED(rv)) {
@ -325,112 +281,122 @@ nsresult nsAbLDAPProcessReplicationData::OpenABForReplicatedDir(bool aCreate) {
return NS_ERROR_FAILURE;
}
// If the database file already exists, create a new one and populate it,
// then replace the old file with the new one. Otherwise, create the
// database in place.
bool fileExists;
rv = mReplicationFile->Exists(&fileExists);
if (NS_SUCCEEDED(rv) && fileExists) {
rv = mReplicationFile->Clone(getter_AddRefs(mOldReplicationFile));
if (NS_FAILED(rv)) {
Done(false);
return rv;
}
rv = mReplicationFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0777);
if (NS_FAILED(rv)) {
Done(false);
return rv;
}
mReplicationFile->Remove(false);
}
nsCString fileName;
rv = mReplicationFile->GetNativeLeafName(fileName);
if (NS_FAILED(rv)) {
Done(false);
return rv;
}
// if the AB DB already exists backup existing one,
// in case if the user cancels or Abort put back the backed up file
bool fileExists;
rv = mReplicationFile->Exists(&fileExists);
if (NS_SUCCEEDED(rv) && fileExists) {
// create the backup file object same as the Replication file object.
// we create a backup file here since we need to cleanup the existing file
// for create and then commit so instead of deleting existing cards we just
// clone the existing one for a much better performance - for Download All.
// And also important in case if replication fails we donot lose user's
// existing replicated data for both Download all and Changelog.
nsCOMPtr<nsIFile> clone;
rv = mReplicationFile->Clone(getter_AddRefs(clone));
if (NS_FAILED(rv)) {
Done(false);
return rv;
}
mBackupReplicationFile = clone;
rv = mBackupReplicationFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0777);
if (NS_FAILED(rv)) {
Done(false);
return rv;
}
nsAutoString backupFileLeafName;
rv = mBackupReplicationFile->GetLeafName(backupFileLeafName);
if (NS_FAILED(rv)) {
Done(false);
return rv;
}
// remove the newly created unique backup file so that move and copy
// succeeds.
rv = mBackupReplicationFile->Remove(false);
if (NS_FAILED(rv)) {
Done(false);
return rv;
}
if (aCreate) {
// set backup file to existing replication file for move
mBackupReplicationFile->SetNativeLeafName(fileName);
rv = mBackupReplicationFile->MoveTo(nullptr, backupFileLeafName);
// set the backup file leaf name now
if (NS_SUCCEEDED(rv))
mBackupReplicationFile->SetLeafName(backupFileLeafName);
} else {
// set backup file to existing replication file for copy
mBackupReplicationFile->SetNativeLeafName(fileName);
// specify the parent here specifically,
// passing nullptr to copy to the same dir actually renames existing file
// instead of making another copy of the existing file.
nsCOMPtr<nsIFile> parent;
rv = mBackupReplicationFile->GetParent(getter_AddRefs(parent));
if (NS_SUCCEEDED(rv))
rv = mBackupReplicationFile->CopyTo(parent, backupFileLeafName);
// set the backup file leaf name now
if (NS_SUCCEEDED(rv))
mBackupReplicationFile->SetLeafName(backupFileLeafName);
}
if (NS_FAILED(rv)) {
Done(false);
return rv;
}
}
mDirectory->SetReplicationFileName(fileName);
nsCString uri(kJSDirectoryRoot);
uri.Append(fileName);
nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
rv = abManager->GetDirectory(uri, getter_AddRefs(mReplicationDB));
NS_ENSURE_SUCCESS(rv, rv);
mReplicationDB = do_CreateInstance(NS_ABJSDIRECTORY_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
Done(false);
return rv;
}
rv = mReplicationDB->Init(uri.get());
if (NS_FAILED(rv)) {
Done(false);
if (mBackupReplicationFile) mBackupReplicationFile->Remove(false);
return rv;
}
mDBOpen = true; // replication DB is now Open
return rv;
}
NS_IMETHODIMP nsAbLDAPProcessReplicationData::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData) {
if (mDatabaseClosedPromise) {
mDatabaseClosedPromise->Resolve(true, __func__);
}
return NS_OK;
}
RefPtr<GenericPromise> nsAbLDAPProcessReplicationData::PromiseDatabaseClosed(
nsIFile *file) {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->NotifyObservers(file, "addrbook-close-ab", nullptr);
mDatabaseClosedPromise = new GenericPromise::Private(__func__);
return mDatabaseClosedPromise;
}
void nsAbLDAPProcessReplicationData::Done(bool aSuccess) {
if (!mInitialized) return;
mState = kReplicationDone;
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->AddObserver(this, "addrbook-close-ab-complete", false);
RefPtr<GenericPromise> promise;
if (mQuery) mQuery->Done(aSuccess);
if (aSuccess) {
if (mOldReplicationFile) {
// Close mReplicationFile.
promise =
this->PromiseDatabaseClosed(mReplicationFile)
->Then(GetCurrentThreadSerialEventTarget(), __func__,
[&] {
// Close mOldReplicationFile.
return this->PromiseDatabaseClosed(mOldReplicationFile);
})
->Then(GetCurrentThreadSerialEventTarget(), __func__, [&] {
// Remove mOldReplicationFile.
mOldReplicationFile->Remove(false);
// Move mReplicationFile to the right place.
mReplicationFile->MoveToNative(nullptr, mReplicationFileName);
return GenericPromise::CreateAndResolve(true, __func__);
});
} else {
promise = GenericPromise::CreateAndResolve(true, __func__);
}
} else {
// Close mReplicationFile.
mReplicationFile->Remove(false);
promise = this->PromiseDatabaseClosed(mReplicationFile);
}
if (mListener)
// XXX Cast from bool to nsresult
mListener->OnStateChange(nullptr, nullptr,
nsIWebProgressListener::STATE_STOP,
static_cast<nsresult>(aSuccess));
promise->Then(GetCurrentThreadSerialEventTarget(), __func__, [&] {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->RemoveObserver(this, "addrbook-close-ab-complete");
// since this is called when all is done here, either on success,
// failure or abort release the query now.
mQuery = nullptr;
mDirectory->SetReplicationFileName(mReplicationFileName);
mState = kReplicationDone;
if (mQuery) mQuery->Done(aSuccess);
if (mListener)
// XXX Cast from bool to nsresult
mListener->OnStateChange(nullptr, nullptr,
nsIWebProgressListener::STATE_STOP,
aSuccess ? NS_OK : NS_ERROR_FAILURE);
// since this is called when all is done here, either on success,
// failure or abort release the query now.
mQuery = nullptr;
});
}

Просмотреть файл

@ -10,18 +10,21 @@
#include "nsIWebProgressListener.h"
#include "nsIAbLDAPReplicationQuery.h"
#include "nsAbLDAPListenerBase.h"
#include "nsIAddrDatabase.h"
#include "nsIAbDirectory.h"
#include "nsIFile.h"
#include "nsDirPrefs.h"
#include "nsIAbLDAPAttributeMap.h"
#include "nsIAbLDAPDirectory.h"
#include "nsString.h"
#include "mozilla/MozPromise.h"
class nsAbLDAPProcessReplicationData : public nsIAbLDAPProcessReplicationData,
public nsAbLDAPListenerBase {
public nsAbLDAPListenerBase,
public nsIObserver {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIABLDAPPROCESSREPLICATIONDATA
NS_DECL_NSIOBSERVER
nsAbLDAPProcessReplicationData();
@ -40,13 +43,13 @@ class nsAbLDAPProcessReplicationData : public nsIAbLDAPProcessReplicationData,
nsCOMPtr<nsIAbDirectory> mReplicationDB;
nsCOMPtr<nsIFile> mReplicationFile;
nsCOMPtr<nsIFile> mBackupReplicationFile;
nsCOMPtr<nsIFile> mOldReplicationFile;
nsCString mReplicationFileName;
// state of processing, protocol used and count of results
int32_t mState;
int32_t mProtocol;
int32_t mCount;
bool mDBOpen;
bool mInitialized;
nsCOMPtr<nsIAbLDAPDirectory> mDirectory;
@ -57,6 +60,9 @@ class nsAbLDAPProcessReplicationData : public nsIAbLDAPProcessReplicationData,
nsresult OpenABForReplicatedDir(bool bCreate);
void Done(bool aSuccess);
RefPtr<mozilla::GenericPromise> PromiseDatabaseClosed(nsIFile *file);
RefPtr<mozilla::GenericPromise::Private> mDatabaseClosedPromise;
};
#endif // nsAbLDAPReplicationData_h__