• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                   &timestamps)) {
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                    &timestamps)) {
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