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
;
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.
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();
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())
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
);
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
);
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());
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
));
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
) {
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(
127 "Could not load autocomplete data from the WebDatabase."));
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(
152 "Failed to update webdata."));
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
,
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();
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
))
198 for (std::vector
<AutofillEntry
>::iterator it
= entries
.begin();
199 it
!= entries
.end(); ++it
) {
200 current_data
.push_back(CreateSyncData(*it
));
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.",
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
:
234 if (!LoadAutofillData(&entries
)) {
235 return error_handler_
->CreateAndUploadError(
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
);
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
);
257 DVLOG(1) << "Delete for old-style autofill profile being dropped!";
261 case syncer::SyncChange::ACTION_INVALID
:
263 return error_handler_
->CreateAndUploadError(
265 "ProcessSyncChanges failed on ChangeType " +
266 syncer::SyncChange::ChangeTypeToString(i
->change_type()));
270 if (!SaveChangesToWebData(new_entries
)) {
271 return error_handler_
->CreateAndUploadError(
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
290 if (sync_processor_
) {
291 ActOnChanges(changes
);
292 } else if (!flare_
.is_null()) {
293 flare_
.Run(syncer::AUTOFILL
);
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
))
309 web_data_backend_
->NotifyOfMultipleAutofillChanges();
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!";
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()) {
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
));
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
);
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
;
352 loaded_data
->erase(it
);
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(
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
);
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
,
402 CreateSyncData(entry
)));
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
)));
416 syncer::SyncError error
=
417 sync_processor_
->ProcessSyncChanges(FROM_HERE
, new_changes
);
419 DVLOG(1) << "[AUTOCOMPLETE SYNC] Failed processing change. Error: "
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());
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
);