Revert 168224 - Update V8 to version 3.15.4.
[chromium-blink-merge.git] / chrome / browser / chromeos / contacts / contact_database.cc
blobe25d16390ab0e3945f0704a59d3b6ce0cf388cc4
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/contacts/contact_database.h"
7 #include <set>
9 #include "base/file_util.h"
10 #include "base/metrics/histogram.h"
11 #include "base/sequenced_task_runner.h"
12 #include "base/threading/sequenced_worker_pool.h"
13 #include "chrome/browser/chromeos/contacts/contact.pb.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "leveldb/db.h"
16 #include "leveldb/iterator.h"
17 #include "leveldb/options.h"
18 #include "leveldb/slice.h"
19 #include "leveldb/status.h"
20 #include "leveldb/write_batch.h"
22 using content::BrowserThread;
24 namespace contacts {
26 namespace {
28 // Initialization results reported via the "Contacts.DatabaseInitResult"
29 // histogram.
30 enum HistogramInitResult {
31 HISTOGRAM_INIT_RESULT_SUCCESS = 0,
32 HISTOGRAM_INIT_RESULT_FAILURE = 1,
33 HISTOGRAM_INIT_RESULT_DELETED_CORRUPTED = 2,
34 HISTOGRAM_INIT_RESULT_MAX_VALUE = 3,
37 // Save results reported via the "Contacts.DatabaseSaveResult" histogram.
38 enum HistogramSaveResult {
39 HISTOGRAM_SAVE_RESULT_SUCCESS = 0,
40 HISTOGRAM_SAVE_RESULT_FAILURE = 1,
41 HISTOGRAM_SAVE_RESULT_MAX_VALUE = 2,
44 // Load results reported via the "Contacts.DatabaseLoadResult" histogram.
45 enum HistogramLoadResult {
46 HISTOGRAM_LOAD_RESULT_SUCCESS = 0,
47 HISTOGRAM_LOAD_RESULT_METADATA_PARSE_FAILURE = 1,
48 HISTOGRAM_LOAD_RESULT_CONTACT_PARSE_FAILURE = 2,
49 HISTOGRAM_LOAD_RESULT_MAX_VALUE = 3,
52 // LevelDB key used for storing UpdateMetadata messages.
53 const char kUpdateMetadataKey[] = "__chrome_update_metadata__";
55 } // namespace
57 ContactDatabase::ContactDatabase()
58 : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
60 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
61 task_runner_ = pool->GetSequencedTaskRunner(pool->GetSequenceToken());
64 void ContactDatabase::DestroyOnUIThread() {
65 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
66 weak_ptr_factory_.InvalidateWeakPtrs();
67 task_runner_->PostNonNestableTask(
68 FROM_HERE,
69 base::Bind(&ContactDatabase::DestroyFromTaskRunner,
70 base::Unretained(this)));
73 void ContactDatabase::Init(const FilePath& database_dir,
74 InitCallback callback) {
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
76 bool* success = new bool(false);
77 task_runner_->PostTaskAndReply(
78 FROM_HERE,
79 base::Bind(&ContactDatabase::InitFromTaskRunner,
80 base::Unretained(this),
81 database_dir,
82 success),
83 base::Bind(&ContactDatabase::RunInitCallback,
84 weak_ptr_factory_.GetWeakPtr(),
85 callback,
86 base::Owned(success)));
89 void ContactDatabase::SaveContacts(scoped_ptr<ContactPointers> contacts_to_save,
90 scoped_ptr<ContactIds> contact_ids_to_delete,
91 scoped_ptr<UpdateMetadata> metadata,
92 bool is_full_update,
93 SaveCallback callback) {
94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
95 bool* success = new bool(false);
96 task_runner_->PostTaskAndReply(
97 FROM_HERE,
98 base::Bind(&ContactDatabase::SaveContactsFromTaskRunner,
99 base::Unretained(this),
100 base::Passed(contacts_to_save.Pass()),
101 base::Passed(contact_ids_to_delete.Pass()),
102 base::Passed(metadata.Pass()),
103 is_full_update,
104 success),
105 base::Bind(&ContactDatabase::RunSaveCallback,
106 weak_ptr_factory_.GetWeakPtr(),
107 callback,
108 base::Owned(success)));
111 void ContactDatabase::LoadContacts(LoadCallback callback) {
112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
114 bool* success = new bool(false);
115 scoped_ptr<ScopedVector<Contact> > contacts(new ScopedVector<Contact>);
116 scoped_ptr<UpdateMetadata> metadata(new UpdateMetadata);
118 // Extract pointers before we calling Pass() so we can use them below.
119 ScopedVector<Contact>* contacts_ptr = contacts.get();
120 UpdateMetadata* metadata_ptr = metadata.get();
122 task_runner_->PostTaskAndReply(
123 FROM_HERE,
124 base::Bind(&ContactDatabase::LoadContactsFromTaskRunner,
125 base::Unretained(this),
126 success,
127 contacts_ptr,
128 metadata_ptr),
129 base::Bind(&ContactDatabase::RunLoadCallback,
130 weak_ptr_factory_.GetWeakPtr(),
131 callback,
132 base::Owned(success),
133 base::Passed(contacts.Pass()),
134 base::Passed(metadata.Pass())));
137 ContactDatabase::~ContactDatabase() {
138 DCHECK(IsRunByTaskRunner());
141 bool ContactDatabase::IsRunByTaskRunner() const {
142 return BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread();
145 void ContactDatabase::DestroyFromTaskRunner() {
146 DCHECK(IsRunByTaskRunner());
147 delete this;
150 void ContactDatabase::RunInitCallback(InitCallback callback,
151 const bool* success) {
152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
153 callback.Run(*success);
156 void ContactDatabase::RunSaveCallback(SaveCallback callback,
157 const bool* success) {
158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159 callback.Run(*success);
162 void ContactDatabase::RunLoadCallback(
163 LoadCallback callback,
164 const bool* success,
165 scoped_ptr<ScopedVector<Contact> > contacts,
166 scoped_ptr<UpdateMetadata> metadata) {
167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
168 callback.Run(*success, contacts.Pass(), metadata.Pass());
171 void ContactDatabase::InitFromTaskRunner(const FilePath& database_dir,
172 bool* success) {
173 DCHECK(IsRunByTaskRunner());
174 DCHECK(success);
176 VLOG(1) << "Opening " << database_dir.value();
177 UMA_HISTOGRAM_MEMORY_KB("Contacts.DatabaseSizeBytes",
178 file_util::ComputeDirectorySize(database_dir));
179 *success = false;
180 HistogramInitResult histogram_result = HISTOGRAM_INIT_RESULT_SUCCESS;
182 leveldb::Options options;
183 options.create_if_missing = true;
184 bool delete_and_retry_on_corruption = true;
186 while (true) {
187 leveldb::DB* db = NULL;
188 leveldb::Status status =
189 leveldb::DB::Open(options, database_dir.value(), &db);
190 if (status.ok()) {
191 CHECK(db);
192 db_.reset(db);
193 *success = true;
194 return;
197 LOG(WARNING) << "Unable to open " << database_dir.value() << ": "
198 << status.ToString();
200 // Delete the existing database and try again (just once, though).
201 if (status.IsCorruption() && delete_and_retry_on_corruption) {
202 LOG(WARNING) << "Deleting possibly-corrupt database";
203 file_util::Delete(database_dir, true);
204 delete_and_retry_on_corruption = false;
205 histogram_result = HISTOGRAM_INIT_RESULT_DELETED_CORRUPTED;
206 } else {
207 histogram_result = HISTOGRAM_INIT_RESULT_FAILURE;
208 break;
212 UMA_HISTOGRAM_ENUMERATION("Contacts.DatabaseInitResult",
213 histogram_result,
214 HISTOGRAM_INIT_RESULT_MAX_VALUE);
217 void ContactDatabase::SaveContactsFromTaskRunner(
218 scoped_ptr<ContactPointers> contacts_to_save,
219 scoped_ptr<ContactIds> contact_ids_to_delete,
220 scoped_ptr<UpdateMetadata> metadata,
221 bool is_full_update,
222 bool* success) {
223 DCHECK(IsRunByTaskRunner());
224 DCHECK(success);
225 VLOG(1) << "Saving " << contacts_to_save->size() << " contact(s) to database "
226 << "and deleting " << contact_ids_to_delete->size() << " as "
227 << (is_full_update ? "full" : "incremental") << " update";
229 *success = false;
231 // If we're doing a full update, find all of the existing keys first so we can
232 // delete ones that aren't present in the new set of contacts.
233 std::set<std::string> keys_to_delete;
234 if (is_full_update) {
235 leveldb::ReadOptions options;
236 scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
237 db_iterator->SeekToFirst();
238 while (db_iterator->Valid()) {
239 std::string key = db_iterator->key().ToString();
240 if (key != kUpdateMetadataKey)
241 keys_to_delete.insert(key);
242 db_iterator->Next();
244 } else {
245 for (ContactIds::const_iterator it = contact_ids_to_delete->begin();
246 it != contact_ids_to_delete->end(); ++it) {
247 keys_to_delete.insert(*it);
251 // TODO(derat): Serializing all of the contacts and so we can write them in a
252 // single batch may be expensive, memory-wise. Consider writing them in
253 // several batches instead. (To avoid using partial writes in the event of a
254 // crash, maybe add a dummy "write completed" contact that's removed in the
255 // first batch and added in the last.)
256 leveldb::WriteBatch updates;
257 for (ContactPointers::const_iterator it = contacts_to_save->begin();
258 it != contacts_to_save->end(); ++it) {
259 const contacts::Contact& contact = **it;
260 if (contact.contact_id() == kUpdateMetadataKey) {
261 LOG(WARNING) << "Skipping contact with reserved ID "
262 << contact.contact_id();
263 continue;
265 updates.Put(leveldb::Slice(contact.contact_id()),
266 leveldb::Slice(contact.SerializeAsString()));
267 if (is_full_update)
268 keys_to_delete.erase(contact.contact_id());
271 for (std::set<std::string>::const_iterator it = keys_to_delete.begin();
272 it != keys_to_delete.end(); ++it) {
273 updates.Delete(leveldb::Slice(*it));
276 updates.Put(leveldb::Slice(kUpdateMetadataKey),
277 leveldb::Slice(metadata->SerializeAsString()));
279 leveldb::WriteOptions options;
280 options.sync = true;
281 leveldb::Status status = db_->Write(options, &updates);
282 if (status.ok())
283 *success = true;
284 else
285 LOG(WARNING) << "Failed writing contacts: " << status.ToString();
287 UMA_HISTOGRAM_ENUMERATION("Contacts.DatabaseSaveResult",
288 *success ?
289 HISTOGRAM_SAVE_RESULT_SUCCESS :
290 HISTOGRAM_SAVE_RESULT_FAILURE,
291 HISTOGRAM_SAVE_RESULT_MAX_VALUE);
294 void ContactDatabase::LoadContactsFromTaskRunner(
295 bool* success,
296 ScopedVector<Contact>* contacts,
297 UpdateMetadata* metadata) {
298 DCHECK(IsRunByTaskRunner());
299 DCHECK(success);
300 DCHECK(contacts);
301 DCHECK(metadata);
303 *success = false;
304 contacts->clear();
305 metadata->Clear();
307 leveldb::ReadOptions options;
308 scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
309 db_iterator->SeekToFirst();
310 while (db_iterator->Valid()) {
311 leveldb::Slice value_slice = db_iterator->value();
313 if (db_iterator->key().ToString() == kUpdateMetadataKey) {
314 if (!metadata->ParseFromArray(value_slice.data(), value_slice.size())) {
315 LOG(WARNING) << "Unable to parse metadata";
316 UMA_HISTOGRAM_ENUMERATION("Contacts.DatabaseLoadResult",
317 HISTOGRAM_LOAD_RESULT_METADATA_PARSE_FAILURE,
318 HISTOGRAM_LOAD_RESULT_MAX_VALUE);
319 return;
321 } else {
322 scoped_ptr<Contact> contact(new Contact);
323 if (!contact->ParseFromArray(value_slice.data(), value_slice.size())) {
324 LOG(WARNING) << "Unable to parse contact "
325 << db_iterator->key().ToString();
326 UMA_HISTOGRAM_ENUMERATION("Contacts.DatabaseLoadResult",
327 HISTOGRAM_LOAD_RESULT_CONTACT_PARSE_FAILURE,
328 HISTOGRAM_LOAD_RESULT_MAX_VALUE);
329 return;
331 contacts->push_back(contact.release());
333 db_iterator->Next();
336 *success = true;
337 UMA_HISTOGRAM_ENUMERATION("Contacts.DatabaseLoadResult",
338 HISTOGRAM_LOAD_RESULT_SUCCESS,
339 HISTOGRAM_LOAD_RESULT_MAX_VALUE);
342 } // namespace contacts