Fix sorting issues in chrome_browser.gypi restructuring
[chromium-blink-merge.git] / chrome / browser / webdata / autocomplete_syncable_service.cc
blobb2b19537f8789eb2e907cb97a7cb5da7aedb345f
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/webdata/autocomplete_syncable_service.h"
7 #include "base/location.h"
8 #include "base/logging.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "components/autofill/core/browser/webdata/autofill_table.h"
11 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
12 #include "components/webdata/common/web_database.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "net/base/escape.h"
15 #include "sync/api/sync_error.h"
16 #include "sync/api/sync_error_factory.h"
17 #include "sync/protocol/autofill_specifics.pb.h"
18 #include "sync/protocol/sync.pb.h"
20 using autofill::AutofillChange;
21 using autofill::AutofillChangeList;
22 using autofill::AutofillEntry;
23 using autofill::AutofillKey;
24 using autofill::AutofillTable;
25 using autofill::AutofillWebDataService;
26 using autofill::AutofillWebDataBackend;
27 using content::BrowserThread;
29 namespace {
31 const char kAutofillEntryNamespaceTag[] = "autofill_entry|";
33 // Merges timestamps from the |sync_timestamps| and the |local_entry|.
34 // Returns true if they were different, false if they were the same. If the
35 // timestamps were different, fills |date_created| and |date_last_used| with the
36 // merged timestamps. The |sync_timestamps| vector is assumed to be sorted.
37 bool MergeTimestamps(
38 const google::protobuf::RepeatedField<int64_t>& sync_timestamps,
39 const AutofillEntry& local_entry,
40 base::Time* date_created,
41 base::Time* date_last_used) {
42 if (sync_timestamps.size() == 0) {
43 *date_created = local_entry.date_created();
44 *date_last_used = local_entry.date_last_used();
45 return true;
48 base::Time sync_date_created =
49 base::Time::FromInternalValue(*sync_timestamps.begin());
50 base::Time sync_date_last_used =
51 base::Time::FromInternalValue(*sync_timestamps.rbegin());
53 if (sync_date_created == local_entry.date_created() &&
54 sync_date_last_used == local_entry.date_last_used())
55 return false;
57 *date_created = std::min(local_entry.date_created(), sync_date_created);
58 *date_last_used = std::max(local_entry.date_last_used(), sync_date_last_used);
59 return true;
62 void* UserDataKey() {
63 // Use the address of a static that COMDAT folding won't ever fold
64 // with something else.
65 static int user_data_key = 0;
66 return reinterpret_cast<void*>(&user_data_key);
69 } // namespace
71 AutocompleteSyncableService::AutocompleteSyncableService(
72 AutofillWebDataBackend* web_data_backend)
73 : web_data_backend_(web_data_backend),
74 scoped_observer_(this) {
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
76 DCHECK(web_data_backend_);
78 scoped_observer_.Add(web_data_backend_);
81 AutocompleteSyncableService::~AutocompleteSyncableService() {
82 DCHECK(CalledOnValidThread());
85 // static
86 void AutocompleteSyncableService::CreateForWebDataServiceAndBackend(
87 AutofillWebDataService* web_data_service,
88 AutofillWebDataBackend* web_data_backend) {
89 web_data_service->GetDBUserData()->SetUserData(
90 UserDataKey(), new AutocompleteSyncableService(web_data_backend));
93 // static
94 AutocompleteSyncableService* AutocompleteSyncableService::FromWebDataService(
95 AutofillWebDataService* web_data_service) {
96 return static_cast<AutocompleteSyncableService*>(
97 web_data_service->GetDBUserData()->GetUserData(UserDataKey()));
100 AutocompleteSyncableService::AutocompleteSyncableService()
101 : web_data_backend_(NULL),
102 scoped_observer_(this) {
103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
106 void AutocompleteSyncableService::InjectStartSyncFlare(
107 const syncer::SyncableService::StartSyncFlare& flare) {
108 flare_ = flare;
111 syncer::SyncMergeResult AutocompleteSyncableService::MergeDataAndStartSyncing(
112 syncer::ModelType type,
113 const syncer::SyncDataList& initial_sync_data,
114 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
115 scoped_ptr<syncer::SyncErrorFactory> error_handler) {
116 DCHECK(CalledOnValidThread());
117 DCHECK(!sync_processor_);
118 DCHECK(sync_processor);
119 DCHECK(error_handler);
121 syncer::SyncMergeResult merge_result(type);
122 error_handler_ = error_handler.Pass();
123 std::vector<AutofillEntry> entries;
124 if (!LoadAutofillData(&entries)) {
125 merge_result.set_error(error_handler_->CreateAndUploadError(
126 FROM_HERE,
127 "Could not load autocomplete data from the WebDatabase."));
128 return merge_result;
131 AutocompleteEntryMap new_db_entries;
132 for (std::vector<AutofillEntry>::iterator it = entries.begin();
133 it != entries.end(); ++it) {
134 new_db_entries[it->key()] =
135 std::make_pair(syncer::SyncChange::ACTION_ADD, it);
138 sync_processor_ = sync_processor.Pass();
140 std::vector<AutofillEntry> new_synced_entries;
141 // Go through and check for all the entries that sync already knows about.
142 // CreateOrUpdateEntry() will remove entries that are same with the synced
143 // ones from |new_db_entries|.
144 for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin();
145 it != initial_sync_data.end(); ++it) {
146 CreateOrUpdateEntry(*it, &new_db_entries, &new_synced_entries);
149 if (!SaveChangesToWebData(new_synced_entries)) {
150 merge_result.set_error(error_handler_->CreateAndUploadError(
151 FROM_HERE,
152 "Failed to update webdata."));
153 return merge_result;
156 syncer::SyncChangeList new_changes;
157 for (AutocompleteEntryMap::iterator it = new_db_entries.begin();
158 it != new_db_entries.end(); ++it) {
159 new_changes.push_back(
160 syncer::SyncChange(FROM_HERE,
161 it->second.first,
162 CreateSyncData(*(it->second.second))));
165 merge_result.set_error(
166 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes));
168 // This will schedule a deletion operation on the DB thread, which will
169 // trigger a notification to propagate the deletion to Sync.
170 // NOTE: This must be called *after* the ProcessSyncChanges call above.
171 // Otherwise, an item that Sync is not yet aware of might expire, causing a
172 // Sync error when that item's deletion is propagated to Sync.
173 web_data_backend_->RemoveExpiredFormElements();
175 return merge_result;
178 void AutocompleteSyncableService::StopSyncing(syncer::ModelType type) {
179 DCHECK(CalledOnValidThread());
180 DCHECK_EQ(syncer::AUTOFILL, type);
182 sync_processor_.reset();
183 error_handler_.reset();
186 syncer::SyncDataList AutocompleteSyncableService::GetAllSyncData(
187 syncer::ModelType type) const {
188 DCHECK(CalledOnValidThread());
189 DCHECK(sync_processor_);
190 DCHECK_EQ(type, syncer::AUTOFILL);
192 syncer::SyncDataList current_data;
194 std::vector<AutofillEntry> entries;
195 if (!LoadAutofillData(&entries))
196 return current_data;
198 for (std::vector<AutofillEntry>::iterator it = entries.begin();
199 it != entries.end(); ++it) {
200 current_data.push_back(CreateSyncData(*it));
203 return current_data;
206 syncer::SyncError AutocompleteSyncableService::ProcessSyncChanges(
207 const tracked_objects::Location& from_here,
208 const syncer::SyncChangeList& change_list) {
209 DCHECK(CalledOnValidThread());
210 DCHECK(sync_processor_);
212 if (!sync_processor_) {
213 syncer::SyncError error(FROM_HERE,
214 syncer::SyncError::DATATYPE_ERROR,
215 "Models not yet associated.",
216 syncer::AUTOFILL);
217 return error;
220 // Data is loaded only if we get new ADD/UPDATE change.
221 std::vector<AutofillEntry> entries;
222 scoped_ptr<AutocompleteEntryMap> db_entries;
223 std::vector<AutofillEntry> new_entries;
225 syncer::SyncError list_processing_error;
227 for (syncer::SyncChangeList::const_iterator i = change_list.begin();
228 i != change_list.end() && !list_processing_error.IsSet(); ++i) {
229 DCHECK(i->IsValid());
230 switch (i->change_type()) {
231 case syncer::SyncChange::ACTION_ADD:
232 case syncer::SyncChange::ACTION_UPDATE:
233 if (!db_entries) {
234 if (!LoadAutofillData(&entries)) {
235 return error_handler_->CreateAndUploadError(
236 FROM_HERE,
237 "Could not get the autocomplete data from WebDatabase.");
239 db_entries.reset(new AutocompleteEntryMap);
240 for (std::vector<AutofillEntry>::iterator it = entries.begin();
241 it != entries.end(); ++it) {
242 (*db_entries)[it->key()] =
243 std::make_pair(syncer::SyncChange::ACTION_ADD, it);
246 CreateOrUpdateEntry(i->sync_data(), db_entries.get(), &new_entries);
247 break;
249 case syncer::SyncChange::ACTION_DELETE: {
250 DCHECK(i->sync_data().GetSpecifics().has_autofill())
251 << "Autofill specifics data not present on delete!";
252 const sync_pb::AutofillSpecifics& autofill =
253 i->sync_data().GetSpecifics().autofill();
254 if (autofill.has_value())
255 list_processing_error = AutofillEntryDelete(autofill);
256 else
257 DVLOG(1) << "Delete for old-style autofill profile being dropped!";
258 break;
261 case syncer::SyncChange::ACTION_INVALID:
262 NOTREACHED();
263 return error_handler_->CreateAndUploadError(
264 FROM_HERE,
265 "ProcessSyncChanges failed on ChangeType " +
266 syncer::SyncChange::ChangeTypeToString(i->change_type()));
270 if (!SaveChangesToWebData(new_entries)) {
271 return error_handler_->CreateAndUploadError(
272 FROM_HERE,
273 "Failed to update webdata.");
276 // This will schedule a deletion operation on the DB thread, which will
277 // trigger a notification to propagate the deletion to Sync.
278 web_data_backend_->RemoveExpiredFormElements();
280 return list_processing_error;
283 void AutocompleteSyncableService::AutofillEntriesChanged(
284 const AutofillChangeList& changes) {
285 // Check if sync is on. If we receive this notification prior to sync being
286 // started, we'll notify sync to start as soon as it can and later process all
287 // entries when MergeDataAndStartSyncing() is called. If we receive this
288 // notification after sync has exited, it will be synced the next time Chrome
289 // starts.
290 if (sync_processor_) {
291 ActOnChanges(changes);
292 } else if (!flare_.is_null()) {
293 flare_.Run(syncer::AUTOFILL);
294 flare_.Reset();
298 bool AutocompleteSyncableService::LoadAutofillData(
299 std::vector<AutofillEntry>* entries) const {
300 return GetAutofillTable()->GetAllAutofillEntries(entries);
303 bool AutocompleteSyncableService::SaveChangesToWebData(
304 const std::vector<AutofillEntry>& new_entries) {
305 DCHECK(CalledOnValidThread());
306 if (!GetAutofillTable()->UpdateAutofillEntries(new_entries))
307 return false;
309 web_data_backend_->NotifyOfMultipleAutofillChanges();
310 return true;
313 // Creates or updates an autocomplete entry based on |data|.
314 void AutocompleteSyncableService::CreateOrUpdateEntry(
315 const syncer::SyncData& data,
316 AutocompleteEntryMap* loaded_data,
317 std::vector<AutofillEntry>* new_entries) {
318 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
319 const sync_pb::AutofillSpecifics& autofill_specifics(specifics.autofill());
321 if (!autofill_specifics.has_value()) {
322 DVLOG(1) << "Add/Update for old-style autofill profile being dropped!";
323 return;
326 AutofillKey key(autofill_specifics.name().c_str(),
327 autofill_specifics.value().c_str());
328 AutocompleteEntryMap::iterator it = loaded_data->find(key);
329 const google::protobuf::RepeatedField<int64_t>& timestamps =
330 autofill_specifics.usage_timestamp();
331 if (it == loaded_data->end()) {
332 // New entry.
333 base::Time date_created, date_last_used;
334 if (timestamps.size() > 0) {
335 date_created = base::Time::FromInternalValue(*timestamps.begin());
336 date_last_used = base::Time::FromInternalValue(*timestamps.rbegin());
338 new_entries->push_back(AutofillEntry(key, date_created, date_last_used));
339 } else {
340 // Entry already present - merge if necessary.
341 base::Time date_created, date_last_used;
342 bool different = MergeTimestamps(timestamps, *it->second.second,
343 &date_created, &date_last_used);
344 if (different) {
345 AutofillEntry new_entry(
346 it->second.second->key(), date_created, date_last_used);
347 new_entries->push_back(new_entry);
348 // Update the sync db since the timestamps have changed.
349 *(it->second.second) = new_entry;
350 it->second.first = syncer::SyncChange::ACTION_UPDATE;
351 } else {
352 loaded_data->erase(it);
357 // static
358 void AutocompleteSyncableService::WriteAutofillEntry(
359 const AutofillEntry& entry, sync_pb::EntitySpecifics* autofill_specifics) {
360 sync_pb::AutofillSpecifics* autofill =
361 autofill_specifics->mutable_autofill();
362 autofill->set_name(base::UTF16ToUTF8(entry.key().name()));
363 autofill->set_value(base::UTF16ToUTF8(entry.key().value()));
364 autofill->add_usage_timestamp(entry.date_created().ToInternalValue());
365 if (entry.date_created() != entry.date_last_used())
366 autofill->add_usage_timestamp(entry.date_last_used().ToInternalValue());
369 syncer::SyncError AutocompleteSyncableService::AutofillEntryDelete(
370 const sync_pb::AutofillSpecifics& autofill) {
371 if (!GetAutofillTable()->RemoveFormElement(
372 base::UTF8ToUTF16(autofill.name()),
373 base::UTF8ToUTF16(autofill.value()))) {
374 return error_handler_->CreateAndUploadError(
375 FROM_HERE,
376 "Could not remove autocomplete entry from WebDatabase.");
378 return syncer::SyncError();
381 void AutocompleteSyncableService::ActOnChanges(
382 const AutofillChangeList& changes) {
383 DCHECK(sync_processor_);
384 syncer::SyncChangeList new_changes;
385 for (AutofillChangeList::const_iterator change = changes.begin();
386 change != changes.end(); ++change) {
387 switch (change->type()) {
388 case AutofillChange::ADD:
389 case AutofillChange::UPDATE: {
390 base::Time date_created, date_last_used;
391 bool success = GetAutofillTable()->GetAutofillTimestamps(
392 change->key().name(), change->key().value(),
393 &date_created, &date_last_used);
394 DCHECK(success);
395 AutofillEntry entry(change->key(), date_created, date_last_used);
396 syncer::SyncChange::SyncChangeType change_type =
397 (change->type() == AutofillChange::ADD) ?
398 syncer::SyncChange::ACTION_ADD :
399 syncer::SyncChange::ACTION_UPDATE;
400 new_changes.push_back(syncer::SyncChange(FROM_HERE,
401 change_type,
402 CreateSyncData(entry)));
403 break;
406 case AutofillChange::REMOVE: {
407 AutofillEntry entry(change->key(), base::Time(), base::Time());
408 new_changes.push_back(
409 syncer::SyncChange(FROM_HERE,
410 syncer::SyncChange::ACTION_DELETE,
411 CreateSyncData(entry)));
412 break;
416 syncer::SyncError error =
417 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
418 if (error.IsSet()) {
419 DVLOG(1) << "[AUTOCOMPLETE SYNC] Failed processing change. Error: "
420 << error.message();
424 syncer::SyncData AutocompleteSyncableService::CreateSyncData(
425 const AutofillEntry& entry) const {
426 sync_pb::EntitySpecifics autofill_specifics;
427 WriteAutofillEntry(entry, &autofill_specifics);
428 std::string tag(KeyToTag(base::UTF16ToUTF8(entry.key().name()),
429 base::UTF16ToUTF8(entry.key().value())));
430 return syncer::SyncData::CreateLocalData(tag, tag, autofill_specifics);
433 AutofillTable* AutocompleteSyncableService::GetAutofillTable() const {
434 return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase());
437 // static
438 std::string AutocompleteSyncableService::KeyToTag(const std::string& name,
439 const std::string& value) {
440 std::string prefix(kAutofillEntryNamespaceTag);
441 return prefix + net::EscapePath(name) + "|" + net::EscapePath(value);