1 // Copyright (c) 2011 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.
4
5 #include "chrome/browser/sync/glue/autofill_change_processor.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/string_util.h"
11 #include "base/utf_string_conversions.h"
12 #include "chrome/browser/autofill/personal_data_manager.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/sync/glue/autofill_model_associator.h"
15 #include "chrome/browser/sync/glue/autofill_profile_model_associator.h"
16 #include "chrome/browser/sync/glue/do_optimistic_refresh_task.h"
17 #include "chrome/browser/sync/profile_sync_service.h"
18 #include "chrome/browser/webdata/autofill_change.h"
19 #include "chrome/browser/webdata/web_data_service.h"
20 #include "chrome/browser/webdata/web_database.h"
21 #include "chrome/common/guid.h"
22 #include "content/common/notification_service.h"
23
24 namespace browser_sync {
25
26 struct AutofillChangeProcessor::AutofillChangeRecord {
27 sync_api::SyncManager::ChangeRecord::Action action_;
28 int64 id_;
29 sync_pb::AutofillSpecifics autofill_;
AutofillChangeRecordbrowser_sync::AutofillChangeProcessor::AutofillChangeRecord30 AutofillChangeRecord(sync_api::SyncManager::ChangeRecord::Action action,
31 int64 id, const sync_pb::AutofillSpecifics& autofill)
32 : action_(action),
33 id_(id),
34 autofill_(autofill) { }
35 };
36
AutofillChangeProcessor(AutofillModelAssociator * model_associator,WebDatabase * web_database,PersonalDataManager * personal_data,UnrecoverableErrorHandler * error_handler)37 AutofillChangeProcessor::AutofillChangeProcessor(
38 AutofillModelAssociator* model_associator,
39 WebDatabase* web_database,
40 PersonalDataManager* personal_data,
41 UnrecoverableErrorHandler* error_handler)
42 : ChangeProcessor(error_handler),
43 model_associator_(model_associator),
44 web_database_(web_database),
45 personal_data_(personal_data),
46 observing_(false) {
47 DCHECK(model_associator);
48 DCHECK(web_database);
49 DCHECK(error_handler);
50 DCHECK(personal_data);
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
52 StartObserving();
53 }
54
~AutofillChangeProcessor()55 AutofillChangeProcessor::~AutofillChangeProcessor() {}
56
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)57 void AutofillChangeProcessor::Observe(NotificationType type,
58 const NotificationSource& source,
59 const NotificationDetails& details) {
60 // Ensure this notification came from our web database.
61 WebDataService* wds = Source<WebDataService>(source).ptr();
62 if (!wds || wds->GetDatabase() != web_database_)
63 return;
64
65 DCHECK(running());
66 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
67 if (!observing_)
68 return;
69
70 sync_api::WriteTransaction trans(share_handle());
71 sync_api::ReadNode autofill_root(&trans);
72 if (!autofill_root.InitByTagLookup(kAutofillTag)) {
73 error_handler()->OnUnrecoverableError(FROM_HERE,
74 "Server did not create the top-level autofill node. "
75 "We might be running against an out-of-date server.");
76 return;
77 }
78
79 DCHECK(type.value == NotificationType::AUTOFILL_ENTRIES_CHANGED);
80
81 AutofillChangeList* changes = Details<AutofillChangeList>(details).ptr();
82 ObserveAutofillEntriesChanged(changes, &trans, autofill_root);
83 }
84
PostOptimisticRefreshTask()85 void AutofillChangeProcessor::PostOptimisticRefreshTask() {
86 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
87 new DoOptimisticRefreshForAutofill(
88 personal_data_));
89 }
90
ObserveAutofillEntriesChanged(AutofillChangeList * changes,sync_api::WriteTransaction * trans,const sync_api::ReadNode & autofill_root)91 void AutofillChangeProcessor::ObserveAutofillEntriesChanged(
92 AutofillChangeList* changes, sync_api::WriteTransaction* trans,
93 const sync_api::ReadNode& autofill_root) {
94 for (AutofillChangeList::iterator change = changes->begin();
95 change != changes->end(); ++change) {
96 switch (change->type()) {
97 case AutofillChange::ADD:
98 {
99 sync_api::WriteNode sync_node(trans);
100 std::string tag =
101 AutofillModelAssociator::KeyToTag(change->key().name(),
102 change->key().value());
103 if (!sync_node.InitUniqueByCreation(syncable::AUTOFILL,
104 autofill_root, tag)) {
105 error_handler()->OnUnrecoverableError(FROM_HERE,
106 "Failed to create autofill sync node.");
107 return;
108 }
109
110 std::vector<base::Time> timestamps;
111 if (!web_database_->GetAutofillTable()->GetAutofillTimestamps(
112 change->key().name(),
113 change->key().value(),
114 ×tamps)) {
115 error_handler()->OnUnrecoverableError(FROM_HERE,
116 "Failed to get timestamps.");
117 return;
118 }
119
120 sync_node.SetTitle(UTF8ToWide(tag));
121
122 WriteAutofillEntry(AutofillEntry(change->key(), timestamps),
123 &sync_node);
124 model_associator_->Associate(&tag, sync_node.GetId());
125 }
126 break;
127
128 case AutofillChange::UPDATE:
129 {
130 sync_api::WriteNode sync_node(trans);
131 std::string tag = AutofillModelAssociator::KeyToTag(
132 change->key().name(), change->key().value());
133 int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
134 if (sync_api::kInvalidId == sync_id) {
135 std::string err = "Unexpected notification for: " +
136 UTF16ToUTF8(change->key().name());
137 error_handler()->OnUnrecoverableError(FROM_HERE, err);
138 return;
139 } else {
140 if (!sync_node.InitByIdLookup(sync_id)) {
141 error_handler()->OnUnrecoverableError(FROM_HERE,
142 "Autofill node lookup failed.");
143 return;
144 }
145 }
146
147 std::vector<base::Time> timestamps;
148 if (!web_database_->GetAutofillTable()->GetAutofillTimestamps(
149 change->key().name(),
150 change->key().value(),
151 ×tamps)) {
152 error_handler()->OnUnrecoverableError(FROM_HERE,
153 "Failed to get timestamps.");
154 return;
155 }
156
157 WriteAutofillEntry(AutofillEntry(change->key(), timestamps),
158 &sync_node);
159 }
160 break;
161 case AutofillChange::REMOVE: {
162 std::string tag = AutofillModelAssociator::KeyToTag(
163 change->key().name(), change->key().value());
164 RemoveSyncNode(tag, trans);
165 }
166 break;
167 }
168 }
169 }
170
RemoveSyncNode(const std::string & tag,sync_api::WriteTransaction * trans)171 void AutofillChangeProcessor::RemoveSyncNode(const std::string& tag,
172 sync_api::WriteTransaction* trans) {
173 sync_api::WriteNode sync_node(trans);
174 int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
175 if (sync_api::kInvalidId == sync_id) {
176 // This could happen because web db might have duplicates and when an entry
177 // and its duplicate is deleted.
178 LOG(WARNING) <<
179 "Bogus delete notification generate for autofill entry " + tag;
180 return;
181 } else {
182 if (!sync_node.InitByIdLookup(sync_id)) {
183 error_handler()->OnUnrecoverableError(FROM_HERE,
184 "Autofill node lookup failed.");
185 return;
186 }
187 model_associator_->Disassociate(sync_node.GetId());
188 sync_node.Remove();
189 }
190 }
191
ApplyChangesFromSyncModel(const sync_api::BaseTransaction * trans,const sync_api::SyncManager::ChangeRecord * changes,int change_count)192 void AutofillChangeProcessor::ApplyChangesFromSyncModel(
193 const sync_api::BaseTransaction* trans,
194 const sync_api::SyncManager::ChangeRecord* changes,
195 int change_count) {
196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
197 if (!running())
198 return;
199 StopObserving();
200
201 bool autofill_profile_not_migrated = HasNotMigratedYet(trans);
202
203 sync_api::ReadNode autofill_root(trans);
204 if (!autofill_root.InitByTagLookup(kAutofillTag)) {
205 error_handler()->OnUnrecoverableError(FROM_HERE,
206 "Autofill root node lookup failed.");
207 return;
208 }
209
210 for (int i = 0; i < change_count; ++i) {
211 sync_api::SyncManager::ChangeRecord::Action action(changes[i].action);
212 if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == action) {
213 DCHECK(changes[i].specifics.HasExtension(sync_pb::autofill))
214 << "Autofill specifics data not present on delete!";
215 const sync_pb::AutofillSpecifics& autofill =
216 changes[i].specifics.GetExtension(sync_pb::autofill);
217 if (autofill.has_value() ||
218 (autofill_profile_not_migrated && autofill.has_profile())) {
219 autofill_changes_.push_back(AutofillChangeRecord(changes[i].action,
220 changes[i].id,
221 autofill));
222 } else {
223 NOTREACHED() << "Autofill specifics has no data!";
224 }
225 continue;
226 }
227
228 // Handle an update or add.
229 sync_api::ReadNode sync_node(trans);
230 if (!sync_node.InitByIdLookup(changes[i].id)) {
231 error_handler()->OnUnrecoverableError(FROM_HERE,
232 "Autofill node lookup failed.");
233 return;
234 }
235
236 // Check that the changed node is a child of the autofills folder.
237 DCHECK(autofill_root.GetId() == sync_node.GetParentId());
238 DCHECK(syncable::AUTOFILL == sync_node.GetModelType());
239
240 const sync_pb::AutofillSpecifics& autofill(
241 sync_node.GetAutofillSpecifics());
242 int64 sync_id = sync_node.GetId();
243 if (autofill.has_value() ||
244 (autofill_profile_not_migrated && autofill.has_profile())) {
245 autofill_changes_.push_back(AutofillChangeRecord(changes[i].action,
246 sync_id, autofill));
247 } else {
248 NOTREACHED() << "Autofill specifics has no data!";
249 }
250 }
251
252 StartObserving();
253 }
254
CommitChangesFromSyncModel()255 void AutofillChangeProcessor::CommitChangesFromSyncModel() {
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
257 if (!running())
258 return;
259 StopObserving();
260
261 std::vector<AutofillEntry> new_entries;
262 for (unsigned int i = 0; i < autofill_changes_.size(); i++) {
263 // Handle deletions.
264 if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
265 autofill_changes_[i].action_) {
266 if (autofill_changes_[i].autofill_.has_value()) {
267 ApplySyncAutofillEntryDelete(autofill_changes_[i].autofill_);
268 } else if (autofill_changes_[i].autofill_.has_profile()) {
269 ApplySyncAutofillProfileDelete(autofill_changes_[i].id_);
270 } else {
271 NOTREACHED() << "Autofill's CommitChanges received change with no"
272 " data!";
273 }
274 continue;
275 }
276
277 // Handle update/adds.
278 if (autofill_changes_[i].autofill_.has_value()) {
279 ApplySyncAutofillEntryChange(autofill_changes_[i].action_,
280 autofill_changes_[i].autofill_, &new_entries,
281 autofill_changes_[i].id_);
282 } else if (autofill_changes_[i].autofill_.has_profile()) {
283 ApplySyncAutofillProfileChange(autofill_changes_[i].action_,
284 autofill_changes_[i].autofill_.profile(),
285 autofill_changes_[i].id_);
286 } else {
287 NOTREACHED() << "Autofill's CommitChanges received change with no data!";
288 }
289 }
290 autofill_changes_.clear();
291
292 // Make changes
293 if (!web_database_->GetAutofillTable()->UpdateAutofillEntries(new_entries)) {
294 error_handler()->OnUnrecoverableError(FROM_HERE,
295 "Could not update autofill entries.");
296 return;
297 }
298
299 PostOptimisticRefreshTask();
300
301 StartObserving();
302 }
303
ApplySyncAutofillEntryDelete(const sync_pb::AutofillSpecifics & autofill)304 void AutofillChangeProcessor::ApplySyncAutofillEntryDelete(
305 const sync_pb::AutofillSpecifics& autofill) {
306 if (!web_database_->GetAutofillTable()->RemoveFormElement(
307 UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()))) {
308 error_handler()->OnUnrecoverableError(FROM_HERE,
309 "Could not remove autofill node.");
310 return;
311 }
312 }
313
ApplySyncAutofillEntryChange(sync_api::SyncManager::ChangeRecord::Action action,const sync_pb::AutofillSpecifics & autofill,std::vector<AutofillEntry> * new_entries,int64 sync_id)314 void AutofillChangeProcessor::ApplySyncAutofillEntryChange(
315 sync_api::SyncManager::ChangeRecord::Action action,
316 const sync_pb::AutofillSpecifics& autofill,
317 std::vector<AutofillEntry>* new_entries,
318 int64 sync_id) {
319 DCHECK_NE(sync_api::SyncManager::ChangeRecord::ACTION_DELETE, action);
320
321 std::vector<base::Time> timestamps;
322 size_t timestamps_size = autofill.usage_timestamp_size();
323 for (size_t c = 0; c < timestamps_size; ++c) {
324 timestamps.push_back(
325 base::Time::FromInternalValue(autofill.usage_timestamp(c)));
326 }
327 AutofillKey k(UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()));
328 AutofillEntry new_entry(k, timestamps);
329
330 new_entries->push_back(new_entry);
331 std::string tag(AutofillModelAssociator::KeyToTag(k.name(), k.value()));
332 if (action == sync_api::SyncManager::ChangeRecord::ACTION_ADD)
333 model_associator_->Associate(&tag, sync_id);
334 }
335
ApplySyncAutofillProfileChange(sync_api::SyncManager::ChangeRecord::Action action,const sync_pb::AutofillProfileSpecifics & profile,int64 sync_id)336 void AutofillChangeProcessor::ApplySyncAutofillProfileChange(
337 sync_api::SyncManager::ChangeRecord::Action action,
338 const sync_pb::AutofillProfileSpecifics& profile,
339 int64 sync_id) {
340 DCHECK_NE(sync_api::SyncManager::ChangeRecord::ACTION_DELETE, action);
341
342 switch (action) {
343 case sync_api::SyncManager::ChangeRecord::ACTION_ADD: {
344 std::string guid(guid::GenerateGUID());
345 if (guid::IsValidGUID(guid) == false) {
346 DCHECK(false) << "Guid generated is invalid " << guid;
347 return;
348 }
349 scoped_ptr<AutofillProfile> p(new AutofillProfile);
350 p->set_guid(guid);
351 AutofillModelAssociator::FillProfileWithServerData(p.get(),
352 profile);
353 if (!web_database_->GetAutofillTable()->AddAutofillProfile(*p.get())) {
354 NOTREACHED() << "Couldn't add autofill profile: " << guid;
355 return;
356 }
357 model_associator_->Associate(&guid, sync_id);
358 break;
359 }
360 case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: {
361 const std::string* guid = model_associator_->GetChromeNodeFromSyncId(
362 sync_id);
363 if (guid == NULL) {
364 LOG(ERROR) << " Model association has not happened for " << sync_id;
365 error_handler()->OnUnrecoverableError(FROM_HERE,
366 "model association has not happened");
367 return;
368 }
369 AutofillProfile *temp_ptr;
370 if (!web_database_->GetAutofillTable()->GetAutofillProfile(
371 *guid, &temp_ptr)) {
372 LOG(ERROR) << "Autofill profile not found for " << *guid;
373 return;
374 }
375
376 scoped_ptr<AutofillProfile> p(temp_ptr);
377
378 AutofillModelAssociator::FillProfileWithServerData(p.get(), profile);
379 if (!web_database_->GetAutofillTable()->UpdateAutofillProfile(
380 *(p.get()))) {
381 LOG(ERROR) << "Couldn't update autofill profile: " << guid;
382 return;
383 }
384 break;
385 }
386 default:
387 NOTREACHED();
388 }
389 }
390
ApplySyncAutofillProfileDelete(int64 sync_id)391 void AutofillChangeProcessor::ApplySyncAutofillProfileDelete(
392 int64 sync_id) {
393
394 const std::string *guid = model_associator_->GetChromeNodeFromSyncId(sync_id);
395 if (guid == NULL) {
396 LOG(ERROR)<< "The profile is not associated";
397 return;
398 }
399
400 if (!web_database_->GetAutofillTable()->RemoveAutofillProfile(*guid)) {
401 LOG(ERROR) << "Could not remove the profile";
402 return;
403 }
404
405 model_associator_->Disassociate(sync_id);
406 }
407
StartImpl(Profile * profile)408 void AutofillChangeProcessor::StartImpl(Profile* profile) {
409 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
410 observing_ = true;
411 }
412
StopImpl()413 void AutofillChangeProcessor::StopImpl() {
414 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
415 observing_ = false;
416 }
417
418
StartObserving()419 void AutofillChangeProcessor::StartObserving() {
420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
421 notification_registrar_.Add(this, NotificationType::AUTOFILL_ENTRIES_CHANGED,
422 NotificationService::AllSources());
423 }
424
StopObserving()425 void AutofillChangeProcessor::StopObserving() {
426 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
427 notification_registrar_.RemoveAll();
428 }
429
430 // static
WriteAutofillEntry(const AutofillEntry & entry,sync_api::WriteNode * node)431 void AutofillChangeProcessor::WriteAutofillEntry(
432 const AutofillEntry& entry,
433 sync_api::WriteNode* node) {
434 sync_pb::AutofillSpecifics autofill;
435 autofill.set_name(UTF16ToUTF8(entry.key().name()));
436 autofill.set_value(UTF16ToUTF8(entry.key().value()));
437 const std::vector<base::Time>& ts(entry.timestamps());
438 for (std::vector<base::Time>::const_iterator timestamp = ts.begin();
439 timestamp != ts.end(); ++timestamp) {
440 autofill.add_usage_timestamp(timestamp->ToInternalValue());
441 }
442 node->SetAutofillSpecifics(autofill);
443 }
444
HasNotMigratedYet(const sync_api::BaseTransaction * trans)445 bool AutofillChangeProcessor::HasNotMigratedYet(
446 const sync_api::BaseTransaction* trans) {
447 return model_associator_->HasNotMigratedYet(trans);
448 }
449
450 } // namespace browser_sync
451