| Index: chrome/browser/chromeos/contacts/contact_database.cc
|
| diff --git a/chrome/browser/chromeos/contacts/contact_database.cc b/chrome/browser/chromeos/contacts/contact_database.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3d94751b38b3f1415e2a4d63f2060dcd9b990998
|
| --- /dev/null
|
| +++ b/chrome/browser/chromeos/contacts/contact_database.cc
|
| @@ -0,0 +1,218 @@
|
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/chromeos/contacts/contact_database.h"
|
| +
|
| +#include <set>
|
| +
|
| +#include "base/file_util.h"
|
| +#include "base/sequenced_task_runner.h"
|
| +#include "base/threading/sequenced_worker_pool.h"
|
| +#include "chrome/browser/chromeos/contacts/contact.pb.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "leveldb/db.h"
|
| +#include "leveldb/iterator.h"
|
| +#include "leveldb/options.h"
|
| +#include "leveldb/slice.h"
|
| +#include "leveldb/status.h"
|
| +#include "leveldb/write_batch.h"
|
| +
|
| +using content::BrowserThread;
|
| +
|
| +namespace contacts {
|
| +
|
| +ContactDatabase::ContactDatabase() : weak_ptr_factory_(this) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
|
| + task_runner_ = pool->GetSequencedTaskRunner(pool->GetSequenceToken());
|
| +}
|
| +
|
| +void ContactDatabase::DestroyOnUIThread() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + weak_ptr_factory_.InvalidateWeakPtrs();
|
| + task_runner_->PostNonNestableTask(
|
| + FROM_HERE,
|
| + base::Bind(&ContactDatabase::DestroyFromTaskRunner,
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +void ContactDatabase::Init(const FilePath& database_dir,
|
| + InitCallback callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + bool* success = new bool(false);
|
| + task_runner_->PostTaskAndReply(
|
| + FROM_HERE,
|
| + base::Bind(&ContactDatabase::InitFromTaskRunner,
|
| + base::Unretained(this),
|
| + database_dir, success),
|
| + base::Bind(&ContactDatabase::RunCallback, weak_ptr_factory_.GetWeakPtr(),
|
| + base::Bind(callback, base::Owned(success))));
|
| +}
|
| +
|
| +void ContactDatabase::SaveContacts(scoped_ptr<ContactPointers> contacts,
|
| + bool is_full_update,
|
| + SaveCallback callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + bool* success = new bool(false);
|
| + task_runner_->PostTaskAndReply(
|
| + FROM_HERE,
|
| + base::Bind(&ContactDatabase::SaveContactsFromTaskRunner,
|
| + base::Unretained(this),
|
| + base::Passed(contacts.Pass()), is_full_update, success),
|
| + base::Bind(&ContactDatabase::RunCallback, weak_ptr_factory_.GetWeakPtr(),
|
| + base::Bind(callback, base::Owned(success))));
|
| +}
|
| +
|
| +void ContactDatabase::LoadContacts(LoadCallback callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + bool* success = new bool(false);
|
| + scoped_ptr<ScopedVector<Contact> > contacts(new ScopedVector<Contact>);
|
| + ScopedVector<Contact>* contacts_ptr = contacts.get();
|
| + task_runner_->PostTaskAndReply(
|
| + FROM_HERE,
|
| + base::Bind(&ContactDatabase::LoadContactsFromTaskRunner,
|
| + base::Unretained(this),
|
| + success, contacts_ptr),
|
| + base::Bind(&ContactDatabase::RunCallback, weak_ptr_factory_.GetWeakPtr(),
|
| + base::Bind(callback,
|
| + base::Owned(success),
|
| + base::Passed(contacts.Pass()))));
|
| +}
|
| +
|
| +ContactDatabase::~ContactDatabase() {
|
| + DCHECK(IsRunByTaskRunner());
|
| +}
|
| +
|
| +bool ContactDatabase::IsRunByTaskRunner() const {
|
| + return BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread();
|
| +}
|
| +
|
| +void ContactDatabase::DestroyFromTaskRunner() {
|
| + DCHECK(IsRunByTaskRunner());
|
| + delete this;
|
| +}
|
| +
|
| +void ContactDatabase::RunCallback(base::Closure closure) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + closure.Run();
|
| +}
|
| +
|
| +void ContactDatabase::InitFromTaskRunner(const FilePath& database_dir,
|
| + bool* success) {
|
| + DCHECK(IsRunByTaskRunner());
|
| + DCHECK(success);
|
| + VLOG(1) << "Opening " << database_dir.value();
|
| +
|
| + *success = false;
|
| +
|
| + leveldb::Options options;
|
| + options.create_if_missing = true;
|
| + bool delete_and_retry_on_corruption = true;
|
| +
|
| + while (true) {
|
| + leveldb::DB* db = NULL;
|
| + leveldb::Status status =
|
| + leveldb::DB::Open(options, database_dir.value(), &db);
|
| + if (status.ok()) {
|
| + CHECK(db);
|
| + db_.reset(db);
|
| + *success = true;
|
| + return;
|
| + }
|
| +
|
| + LOG(WARNING) << "Unable to open " << database_dir.value() << ": "
|
| + << status.ToString();
|
| +
|
| + // Delete the existing database and try again (just once, though).
|
| + if (status.IsCorruption() && delete_and_retry_on_corruption) {
|
| + LOG(WARNING) << "Deleting possibly-corrupt database";
|
| + file_util::Delete(database_dir, true);
|
| + delete_and_retry_on_corruption = false;
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void ContactDatabase::SaveContactsFromTaskRunner(
|
| + scoped_ptr<ContactPointers> contacts,
|
| + bool is_full_update,
|
| + bool* success) {
|
| + DCHECK(IsRunByTaskRunner());
|
| + DCHECK(success);
|
| + VLOG(1) << "Saving " << contacts->size() << " contact(s) to database as "
|
| + << (is_full_update ? "full" : "partial") << " update";
|
| +
|
| + *success = false;
|
| +
|
| + // If we're doing a full update, find all of the existing keys first so we can
|
| + // delete ones that aren't present in the new set of contacts.
|
| + std::set<std::string> keys_to_delete;
|
| + if (is_full_update) {
|
| + leveldb::ReadOptions options;
|
| + scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
|
| + db_iterator->SeekToFirst();
|
| + while (db_iterator->Valid()) {
|
| + keys_to_delete.insert(db_iterator->key().ToString());
|
| + db_iterator->Next();
|
| + }
|
| + }
|
| +
|
| + // TODO(derat): Serializing all of the contacts and so we can write them in a
|
| + // single batch may be expensive, memory-wise. Consider writing them in
|
| + // several batches instead. (To avoid using partial writes in the event of a
|
| + // crash, maybe add a dummy "write completed" contact that's removed in the
|
| + // first batch and added in the last.)
|
| + leveldb::WriteBatch updates;
|
| + for (ContactPointers::const_iterator it = contacts->begin();
|
| + it != contacts->end(); ++it) {
|
| + const contacts::Contact& contact = **it;
|
| + updates.Put(leveldb::Slice(contact.provider_id()),
|
| + leveldb::Slice(contact.SerializeAsString()));
|
| + keys_to_delete.erase(contact.provider_id());
|
| + }
|
| +
|
| + for (std::set<std::string>::const_iterator it = keys_to_delete.begin();
|
| + it != keys_to_delete.end(); ++it) {
|
| + updates.Delete(leveldb::Slice(*it));
|
| + }
|
| +
|
| + leveldb::WriteOptions options;
|
| + options.sync = true;
|
| + leveldb::Status status = db_->Write(options, &updates);
|
| + if (status.ok())
|
| + *success = true;
|
| + else
|
| + LOG(WARNING) << "Failed writing contacts: " << status.ToString();
|
| +}
|
| +
|
| +void ContactDatabase::LoadContactsFromTaskRunner(
|
| + bool* success,
|
| + ScopedVector<Contact>* contacts_out) {
|
| + DCHECK(IsRunByTaskRunner());
|
| + DCHECK(success);
|
| + DCHECK(contacts_out);
|
| +
|
| + *success = false;
|
| + contacts_out->clear();
|
| +
|
| + leveldb::ReadOptions options;
|
| + scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
|
| + db_iterator->SeekToFirst();
|
| + while (db_iterator->Valid()) {
|
| + scoped_ptr<Contact> contact(new Contact);
|
| + leveldb::Slice value_slice = db_iterator->value();
|
| + if (!contact->ParseFromArray(value_slice.data(), value_slice.size())) {
|
| + LOG(WARNING) << "Unable to parse contact "
|
| + << db_iterator->key().ToString();
|
| + return;
|
| + }
|
| + contacts_out->push_back(contact.release());
|
| + db_iterator->Next();
|
| + }
|
| +
|
| + *success = true;
|
| +}
|
| +
|
| +} // namespace contacts
|
|
|