Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / autofill / core / browser / webdata / autocomplete_syncable_service.cc
blobf26bbd6890bc3df0e23f1bf7f98c79d1ad8f01ba
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 "components/autofill/core/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 "net/base/escape.h"
14 #include "sync/api/sync_error.h"
15 #include "sync/api/sync_error_factory.h"
16 #include "sync/protocol/autofill_specifics.pb.h"
17 #include "sync/protocol/sync.pb.h"
19 namespace autofill {
20 namespace {
22 const char kAutofillEntryNamespaceTag[] = "autofill_entry|";
24 // Merges timestamps from the |sync_timestamps| and the |local_entry|.
25 // Returns true if they were different, false if they were the same. If the
26 // timestamps were different, fills |date_created| and |date_last_used| with the
27 // merged timestamps. The |sync_timestamps| vector is assumed to be sorted.
28 bool MergeTimestamps(
29 const google::protobuf::RepeatedField<int64_t>& sync_timestamps,
30 const AutofillEntry& local_entry,
31 base::Time* date_created,
32 base::Time* date_last_used) {
33 if (sync_timestamps.size() == 0) {
34 *date_created = local_entry.date_created();
35 *date_last_used = local_entry.date_last_used();
36 return true;
39 base::Time sync_date_created =
40 base::Time::FromInternalValue(*sync_timestamps.begin());
41 base::Time sync_date_last_used =
42 base::Time::FromInternalValue(*sync_timestamps.rbegin());
44 if (sync_date_created == local_entry.date_created() &&
45 sync_date_last_used == local_entry.date_last_used())
46 return false;
48 *date_created = std::min(local_entry.date_created(), sync_date_created);
49 *date_last_used = std::max(local_entry.date_last_used(), sync_date_last_used);
50 return true;
53 void* UserDataKey() {
54 // Use the address of a static that COMDAT folding won't ever fold
55 // with something else.
56 static int user_data_key = 0;
57 return reinterpret_cast<void*>(&user_data_key);
60 } // namespace
62 AutocompleteSyncableService::AutocompleteSyncableService(
63 AutofillWebDataBackend* web_data_backend)
64 : web_data_backend_(web_data_backend), scoped_observer_(this) {
65 DCHECK(web_data_backend_);
67 scoped_observer_.Add(web_data_backend_);
70 AutocompleteSyncableService::~AutocompleteSyncableService() {
71 DCHECK(CalledOnValidThread());
74 // static
75 void AutocompleteSyncableService::CreateForWebDataServiceAndBackend(
76 AutofillWebDataService* web_data_service,
77 AutofillWebDataBackend* web_data_backend) {
78 web_data_service->GetDBUserData()->SetUserData(
79 UserDataKey(), new AutocompleteSyncableService(web_data_backend));
82 // static
83 AutocompleteSyncableService* AutocompleteSyncableService::FromWebDataService(
84 AutofillWebDataService* web_data_service) {
85 return static_cast<AutocompleteSyncableService*>(
86 web_data_service->GetDBUserData()->GetUserData(UserDataKey()));
89 AutocompleteSyncableService::AutocompleteSyncableService()
90 : web_data_backend_(nullptr), scoped_observer_(this) {
93 void AutocompleteSyncableService::InjectStartSyncFlare(
94 const syncer::SyncableService::StartSyncFlare& flare) {
95 flare_ = flare;
98 syncer::SyncMergeResult AutocompleteSyncableService::MergeDataAndStartSyncing(
99 syncer::ModelType type,
100 const syncer::SyncDataList& initial_sync_data,
101 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
102 scoped_ptr<syncer::SyncErrorFactory> error_handler) {
103 DCHECK(CalledOnValidThread());
104 DCHECK(!sync_processor_);
105 DCHECK(sync_processor);
106 DCHECK(error_handler);
108 syncer::SyncMergeResult merge_result(type);
109 error_handler_ = error_handler.Pass();
110 std::vector<AutofillEntry> entries;
111 if (!LoadAutofillData(&entries)) {
112 merge_result.set_error(error_handler_->CreateAndUploadError(
113 FROM_HERE,
114 "Could not load autocomplete data from the WebDatabase."));
115 return merge_result;
118 AutocompleteEntryMap new_db_entries;
119 for (std::vector<AutofillEntry>::iterator it = entries.begin();
120 it != entries.end(); ++it) {
121 new_db_entries[it->key()] =
122 std::make_pair(syncer::SyncChange::ACTION_ADD, it);
125 sync_processor_ = sync_processor.Pass();
127 std::vector<AutofillEntry> new_synced_entries;
128 // Go through and check for all the entries that sync already knows about.
129 // CreateOrUpdateEntry() will remove entries that are same with the synced
130 // ones from |new_db_entries|.
131 for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin();
132 it != initial_sync_data.end(); ++it) {
133 CreateOrUpdateEntry(*it, &new_db_entries, &new_synced_entries);
136 if (!SaveChangesToWebData(new_synced_entries)) {
137 merge_result.set_error(error_handler_->CreateAndUploadError(
138 FROM_HERE,
139 "Failed to update webdata."));
140 return merge_result;
143 syncer::SyncChangeList new_changes;
144 for (AutocompleteEntryMap::iterator it = new_db_entries.begin();
145 it != new_db_entries.end(); ++it) {
146 new_changes.push_back(
147 syncer::SyncChange(FROM_HERE,
148 it->second.first,
149 CreateSyncData(*(it->second.second))));
152 merge_result.set_error(
153 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes));
155 // This will schedule a deletion operation on the DB thread, which will
156 // trigger a notification to propagate the deletion to Sync.
157 // NOTE: This must be called *after* the ProcessSyncChanges call above.
158 // Otherwise, an item that Sync is not yet aware of might expire, causing a
159 // Sync error when that item's deletion is propagated to Sync.
160 web_data_backend_->RemoveExpiredFormElements();
162 return merge_result;
165 void AutocompleteSyncableService::StopSyncing(syncer::ModelType type) {
166 DCHECK(CalledOnValidThread());
167 DCHECK_EQ(syncer::AUTOFILL, type);
169 sync_processor_.reset();
170 error_handler_.reset();
173 syncer::SyncDataList AutocompleteSyncableService::GetAllSyncData(
174 syncer::ModelType type) const {
175 DCHECK(CalledOnValidThread());
176 DCHECK(sync_processor_);
177 DCHECK_EQ(type, syncer::AUTOFILL);
179 syncer::SyncDataList current_data;
181 std::vector<AutofillEntry> entries;
182 if (!LoadAutofillData(&entries))
183 return current_data;
185 for (std::vector<AutofillEntry>::iterator it = entries.begin();
186 it != entries.end(); ++it) {
187 current_data.push_back(CreateSyncData(*it));
190 return current_data;
193 syncer::SyncError AutocompleteSyncableService::ProcessSyncChanges(
194 const tracked_objects::Location& from_here,
195 const syncer::SyncChangeList& change_list) {
196 DCHECK(CalledOnValidThread());
197 DCHECK(sync_processor_);
199 if (!sync_processor_) {
200 syncer::SyncError error(FROM_HERE,
201 syncer::SyncError::DATATYPE_ERROR,
202 "Models not yet associated.",
203 syncer::AUTOFILL);
204 return error;
207 // Data is loaded only if we get new ADD/UPDATE change.
208 std::vector<AutofillEntry> entries;
209 scoped_ptr<AutocompleteEntryMap> db_entries;
210 std::vector<AutofillEntry> new_entries;
212 syncer::SyncError list_processing_error;
214 for (syncer::SyncChangeList::const_iterator i = change_list.begin();
215 i != change_list.end() && !list_processing_error.IsSet(); ++i) {
216 DCHECK(i->IsValid());
217 switch (i->change_type()) {
218 case syncer::SyncChange::ACTION_ADD:
219 case syncer::SyncChange::ACTION_UPDATE:
220 if (!db_entries) {
221 if (!LoadAutofillData(&entries)) {
222 return error_handler_->CreateAndUploadError(
223 FROM_HERE,
224 "Could not get the autocomplete data from WebDatabase.");
226 db_entries.reset(new AutocompleteEntryMap);
227 for (std::vector<AutofillEntry>::iterator it = entries.begin();
228 it != entries.end(); ++it) {
229 (*db_entries)[it->key()] =
230 std::make_pair(syncer::SyncChange::ACTION_ADD, it);
233 CreateOrUpdateEntry(i->sync_data(), db_entries.get(), &new_entries);
234 break;
236 case syncer::SyncChange::ACTION_DELETE: {
237 DCHECK(i->sync_data().GetSpecifics().has_autofill())
238 << "Autofill specifics data not present on delete!";
239 const sync_pb::AutofillSpecifics& autofill =
240 i->sync_data().GetSpecifics().autofill();
241 if (autofill.has_value())
242 list_processing_error = AutofillEntryDelete(autofill);
243 else
244 DVLOG(1) << "Delete for old-style autofill profile being dropped!";
245 break;
248 case syncer::SyncChange::ACTION_INVALID:
249 NOTREACHED();
250 return error_handler_->CreateAndUploadError(
251 FROM_HERE,
252 "ProcessSyncChanges failed on ChangeType " +
253 syncer::SyncChange::ChangeTypeToString(i->change_type()));
257 if (!SaveChangesToWebData(new_entries)) {
258 return error_handler_->CreateAndUploadError(
259 FROM_HERE,
260 "Failed to update webdata.");
263 // This will schedule a deletion operation on the DB thread, which will
264 // trigger a notification to propagate the deletion to Sync.
265 web_data_backend_->RemoveExpiredFormElements();
267 return list_processing_error;
270 void AutocompleteSyncableService::AutofillEntriesChanged(
271 const AutofillChangeList& changes) {
272 // Check if sync is on. If we receive this notification prior to sync being
273 // started, we'll notify sync to start as soon as it can and later process all
274 // entries when MergeDataAndStartSyncing() is called. If we receive this
275 // notification after sync has exited, it will be synced the next time Chrome
276 // starts.
277 if (sync_processor_) {
278 ActOnChanges(changes);
279 } else if (!flare_.is_null()) {
280 flare_.Run(syncer::AUTOFILL);
281 flare_.Reset();
285 bool AutocompleteSyncableService::LoadAutofillData(
286 std::vector<AutofillEntry>* entries) const {
287 return GetAutofillTable()->GetAllAutofillEntries(entries);
290 bool AutocompleteSyncableService::SaveChangesToWebData(
291 const std::vector<AutofillEntry>& new_entries) {
292 DCHECK(CalledOnValidThread());
293 if (!GetAutofillTable()->UpdateAutofillEntries(new_entries))
294 return false;
296 web_data_backend_->NotifyOfMultipleAutofillChanges();
297 return true;
300 // Creates or updates an autocomplete entry based on |data|.
301 void AutocompleteSyncableService::CreateOrUpdateEntry(
302 const syncer::SyncData& data,
303 AutocompleteEntryMap* loaded_data,
304 std::vector<AutofillEntry>* new_entries) {
305 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
306 const sync_pb::AutofillSpecifics& autofill_specifics(specifics.autofill());
308 if (!autofill_specifics.has_value()) {
309 DVLOG(1) << "Add/Update for old-style autofill profile being dropped!";
310 return;
313 AutofillKey key(autofill_specifics.name().c_str(),
314 autofill_specifics.value().c_str());
315 AutocompleteEntryMap::iterator it = loaded_data->find(key);
316 const google::protobuf::RepeatedField<int64_t>& timestamps =
317 autofill_specifics.usage_timestamp();
318 if (it == loaded_data->end()) {
319 // New entry.
320 base::Time date_created, date_last_used;
321 if (timestamps.size() > 0) {
322 date_created = base::Time::FromInternalValue(*timestamps.begin());
323 date_last_used = base::Time::FromInternalValue(*timestamps.rbegin());
325 new_entries->push_back(AutofillEntry(key, date_created, date_last_used));
326 } else {
327 // Entry already present - merge if necessary.
328 base::Time date_created, date_last_used;
329 bool different = MergeTimestamps(timestamps, *it->second.second,
330 &date_created, &date_last_used);
331 if (different) {
332 AutofillEntry new_entry(
333 it->second.second->key(), date_created, date_last_used);
334 new_entries->push_back(new_entry);
335 // Update the sync db since the timestamps have changed.
336 *(it->second.second) = new_entry;
337 it->second.first = syncer::SyncChange::ACTION_UPDATE;
338 } else {
339 loaded_data->erase(it);
344 // static
345 void AutocompleteSyncableService::WriteAutofillEntry(
346 const AutofillEntry& entry, sync_pb::EntitySpecifics* autofill_specifics) {
347 sync_pb::AutofillSpecifics* autofill =
348 autofill_specifics->mutable_autofill();
349 autofill->set_name(base::UTF16ToUTF8(entry.key().name()));
350 autofill->set_value(base::UTF16ToUTF8(entry.key().value()));
351 autofill->add_usage_timestamp(entry.date_created().ToInternalValue());
352 if (entry.date_created() != entry.date_last_used())
353 autofill->add_usage_timestamp(entry.date_last_used().ToInternalValue());
356 syncer::SyncError AutocompleteSyncableService::AutofillEntryDelete(
357 const sync_pb::AutofillSpecifics& autofill) {
358 if (!GetAutofillTable()->RemoveFormElement(
359 base::UTF8ToUTF16(autofill.name()),
360 base::UTF8ToUTF16(autofill.value()))) {
361 return error_handler_->CreateAndUploadError(
362 FROM_HERE,
363 "Could not remove autocomplete entry from WebDatabase.");
365 return syncer::SyncError();
368 void AutocompleteSyncableService::ActOnChanges(
369 const AutofillChangeList& changes) {
370 DCHECK(sync_processor_);
371 syncer::SyncChangeList new_changes;
372 for (AutofillChangeList::const_iterator change = changes.begin();
373 change != changes.end(); ++change) {
374 switch (change->type()) {
375 case AutofillChange::ADD:
376 case AutofillChange::UPDATE: {
377 base::Time date_created, date_last_used;
378 bool success = GetAutofillTable()->GetAutofillTimestamps(
379 change->key().name(), change->key().value(),
380 &date_created, &date_last_used);
381 DCHECK(success);
382 AutofillEntry entry(change->key(), date_created, date_last_used);
383 syncer::SyncChange::SyncChangeType change_type =
384 (change->type() == AutofillChange::ADD) ?
385 syncer::SyncChange::ACTION_ADD :
386 syncer::SyncChange::ACTION_UPDATE;
387 new_changes.push_back(syncer::SyncChange(FROM_HERE,
388 change_type,
389 CreateSyncData(entry)));
390 break;
393 case AutofillChange::REMOVE: {
394 AutofillEntry entry(change->key(), base::Time(), base::Time());
395 new_changes.push_back(
396 syncer::SyncChange(FROM_HERE,
397 syncer::SyncChange::ACTION_DELETE,
398 CreateSyncData(entry)));
399 break;
403 syncer::SyncError error =
404 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
405 if (error.IsSet()) {
406 DVLOG(1) << "[AUTOCOMPLETE SYNC] Failed processing change. Error: "
407 << error.message();
411 syncer::SyncData AutocompleteSyncableService::CreateSyncData(
412 const AutofillEntry& entry) const {
413 sync_pb::EntitySpecifics autofill_specifics;
414 WriteAutofillEntry(entry, &autofill_specifics);
415 std::string tag(KeyToTag(base::UTF16ToUTF8(entry.key().name()),
416 base::UTF16ToUTF8(entry.key().value())));
417 return syncer::SyncData::CreateLocalData(tag, tag, autofill_specifics);
420 AutofillTable* AutocompleteSyncableService::GetAutofillTable() const {
421 return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase());
424 // static
425 std::string AutocompleteSyncableService::KeyToTag(const std::string& name,
426 const std::string& value) {
427 std::string prefix(kAutofillEntryNamespaceTag);
428 return prefix + net::EscapePath(name) + "|" + net::EscapePath(value);
431 } // namespace autofill