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"
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.
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();
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())
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
);
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
);
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());
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
));
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
) {
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(
114 "Could not load autocomplete data from the WebDatabase."));
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(
139 "Failed to update webdata."));
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
,
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();
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
))
185 for (std::vector
<AutofillEntry
>::iterator it
= entries
.begin();
186 it
!= entries
.end(); ++it
) {
187 current_data
.push_back(CreateSyncData(*it
));
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.",
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
:
221 if (!LoadAutofillData(&entries
)) {
222 return error_handler_
->CreateAndUploadError(
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
);
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
);
244 DVLOG(1) << "Delete for old-style autofill profile being dropped!";
248 case syncer::SyncChange::ACTION_INVALID
:
250 return error_handler_
->CreateAndUploadError(
252 "ProcessSyncChanges failed on ChangeType " +
253 syncer::SyncChange::ChangeTypeToString(i
->change_type()));
257 if (!SaveChangesToWebData(new_entries
)) {
258 return error_handler_
->CreateAndUploadError(
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
277 if (sync_processor_
) {
278 ActOnChanges(changes
);
279 } else if (!flare_
.is_null()) {
280 flare_
.Run(syncer::AUTOFILL
);
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
))
296 web_data_backend_
->NotifyOfMultipleAutofillChanges();
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!";
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()) {
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
));
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
);
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
;
339 loaded_data
->erase(it
);
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(
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
);
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
,
389 CreateSyncData(entry
)));
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
)));
403 syncer::SyncError error
=
404 sync_processor_
->ProcessSyncChanges(FROM_HERE
, new_changes
);
406 DVLOG(1) << "[AUTOCOMPLETE SYNC] Failed processing change. Error: "
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());
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