autofill_model_associator.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
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_model_associator.h"
6
7#include <functional>
8#include <vector>
9
10#include "base/string_number_conversions.h"
11#include "base/task.h"
12#include "base/time.h"
13#include "base/utf_string_conversions.h"
14#include "chrome/browser/autofill/autofill_profile.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/sync/engine/syncapi.h"
17#include "chrome/browser/sync/glue/autofill_change_processor.h"
18#include "chrome/browser/sync/glue/autofill_profile_model_associator.h"
19#include "chrome/browser/sync/glue/do_optimistic_refresh_task.h"
20#include "chrome/browser/sync/profile_sync_service.h"
21#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
22#include "chrome/browser/webdata/web_database.h"
23#include "chrome/common/guid.h"
24#include "content/browser/browser_thread.h"
25#include "net/base/escape.h"
26
27using base::TimeTicks;
28
29namespace browser_sync {
30
31const char kAutofillTag[] = "google_chrome_autofill";
32const char kAutofillEntryNamespaceTag[] = "autofill_entry|";
33
34struct AutofillModelAssociator::DataBundle {
35  std::set<AutofillKey> current_entries;
36  std::vector<AutofillEntry> new_entries;
37  std::set<string16> current_profiles;
38  std::vector<AutofillProfile*> updated_profiles;
39  std::vector<AutofillProfile*> new_profiles;  // We own these pointers.
40  ~DataBundle() { STLDeleteElements(&new_profiles); }
41};
42
43AutofillModelAssociator::AutofillModelAssociator(
44    ProfileSyncService* sync_service,
45    WebDatabase* web_database,
46    PersonalDataManager* personal_data)
47    : sync_service_(sync_service),
48      web_database_(web_database),
49      personal_data_(personal_data),
50      autofill_node_id_(sync_api::kInvalidId),
51      abort_association_pending_(false),
52      number_of_entries_created_(0) {
53  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
54  DCHECK(sync_service_);
55  DCHECK(web_database_);
56  DCHECK(personal_data_);
57}
58
59AutofillModelAssociator::~AutofillModelAssociator() {
60  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
61}
62
63bool AutofillModelAssociator::TraverseAndAssociateChromeAutofillEntries(
64    sync_api::WriteTransaction* write_trans,
65    const sync_api::ReadNode& autofill_root,
66    const std::vector<AutofillEntry>& all_entries_from_db,
67    std::set<AutofillKey>* current_entries,
68    std::vector<AutofillEntry>* new_entries) {
69
70  const std::vector<AutofillEntry>& entries = all_entries_from_db;
71  for (std::vector<AutofillEntry>::const_iterator ix = entries.begin();
72       ix != entries.end(); ++ix) {
73    std::string tag = KeyToTag(ix->key().name(), ix->key().value());
74    if (id_map_.find(tag) != id_map_.end()) {
75      // It seems that name/value pairs are not unique in the web database.
76      // As a result, we have to filter out duplicates here.  This is probably
77      // a bug in the database.
78      continue;
79    }
80
81    sync_api::ReadNode node(write_trans);
82    if (node.InitByClientTagLookup(syncable::AUTOFILL, tag)) {
83      const sync_pb::AutofillSpecifics& autofill(node.GetAutofillSpecifics());
84      DCHECK_EQ(tag, KeyToTag(UTF8ToUTF16(autofill.name()),
85                              UTF8ToUTF16(autofill.value())));
86
87      std::vector<base::Time> timestamps;
88      if (MergeTimestamps(autofill, ix->timestamps(), &timestamps)) {
89        AutofillEntry new_entry(ix->key(), timestamps);
90        new_entries->push_back(new_entry);
91
92        sync_api::WriteNode write_node(write_trans);
93        if (!write_node.InitByClientTagLookup(syncable::AUTOFILL, tag)) {
94          LOG(ERROR) << "Failed to write autofill sync node.";
95          return false;
96        }
97        AutofillChangeProcessor::WriteAutofillEntry(new_entry, &write_node);
98      }
99
100      Associate(&tag, node.GetId());
101    } else {
102      sync_api::WriteNode node(write_trans);
103      if (!node.InitUniqueByCreation(syncable::AUTOFILL,
104                                     autofill_root, tag)) {
105        LOG(ERROR) << "Failed to create autofill sync node.";
106        return false;
107      }
108      node.SetTitle(UTF8ToWide(tag));
109      AutofillChangeProcessor::WriteAutofillEntry(*ix, &node);
110      Associate(&tag, node.GetId());
111      number_of_entries_created_++;
112    }
113
114    current_entries->insert(ix->key());
115  }
116  return true;
117}
118
119bool AutofillModelAssociator::LoadAutofillData(
120    std::vector<AutofillEntry>* entries,
121    std::vector<AutofillProfile*>* profiles) {
122  if (IsAbortPending())
123    return false;
124  if (!web_database_->GetAutofillTable()->GetAllAutofillEntries(entries))
125    return false;
126
127  if (IsAbortPending())
128    return false;
129  if (!web_database_->GetAutofillTable()->GetAutofillProfiles(profiles))
130    return false;
131
132  return true;
133}
134
135bool AutofillModelAssociator::AssociateModels() {
136  VLOG(1) << "Associating Autofill Models";
137  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
138  {
139    base::AutoLock lock(abort_association_pending_lock_);
140    abort_association_pending_ = false;
141  }
142
143  // TODO(zork): Attempt to load the model association from storage.
144  std::vector<AutofillEntry> entries;
145  ScopedVector<AutofillProfile> profiles;
146
147  if (!LoadAutofillData(&entries, &profiles.get())) {
148    LOG(ERROR) << "Could not get the autofill data from WebDatabase.";
149    return false;
150  }
151
152  DataBundle bundle;
153  {
154    sync_api::WriteTransaction trans(sync_service_->GetUserShare());
155
156    sync_api::ReadNode autofill_root(&trans);
157    if (!autofill_root.InitByTagLookup(kAutofillTag)) {
158      LOG(ERROR) << "Server did not create the top-level autofill node. We "
159                 << "might be running against an out-of-date server.";
160      return false;
161    }
162
163    if (!TraverseAndAssociateChromeAutofillEntries(&trans, autofill_root,
164        entries, &bundle.current_entries, &bundle.new_entries)) {
165      return false;
166    }
167
168    if (!TraverseAndAssociateAllSyncNodes(
169        &trans,
170        autofill_root,
171        &bundle,
172        profiles.get())) {
173      return false;
174    }
175  }
176
177  // Since we're on the DB thread, we don't have to worry about updating
178  // the autofill database after closing the write transaction, since
179  // this is the only thread that writes to the database.  We also don't have
180  // to worry about the sync model getting out of sync, because changes are
181  // propagated to the ChangeProcessor on this thread.
182  if (!SaveChangesToWebData(bundle)) {
183    LOG(ERROR) << "Failed to update autofill entries.";
184    return false;
185  }
186
187  if (sync_service_->GetAutofillMigrationState() !=
188      syncable::MIGRATED) {
189    syncable::AutofillMigrationDebugInfo debug_info;
190    debug_info.autofill_entries_added_during_migration =
191        number_of_entries_created_;
192    sync_service_->SetAutofillMigrationDebugInfo(
193        syncable::AutofillMigrationDebugInfo::ENTRIES_ADDED,
194        debug_info);
195  }
196
197  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
198      new DoOptimisticRefreshForAutofill(personal_data_));
199  return true;
200}
201
202bool AutofillModelAssociator::SaveChangesToWebData(const DataBundle& bundle) {
203  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
204
205  if (IsAbortPending())
206    return false;
207
208  if (bundle.new_entries.size() &&
209      !web_database_->GetAutofillTable()->UpdateAutofillEntries(
210          bundle.new_entries)) {
211    return false;
212  }
213
214  for (size_t i = 0; i < bundle.new_profiles.size(); i++) {
215    if (IsAbortPending())
216      return false;
217    if (!web_database_->GetAutofillTable()->AddAutofillProfile(
218        *bundle.new_profiles[i]))
219      return false;
220  }
221
222  for (size_t i = 0; i < bundle.updated_profiles.size(); i++) {
223    if (IsAbortPending())
224      return false;
225    if (!web_database_->GetAutofillTable()->UpdateAutofillProfile(
226        *bundle.updated_profiles[i]))
227      return false;
228  }
229  return true;
230}
231
232bool AutofillModelAssociator::TraverseAndAssociateAllSyncNodes(
233    sync_api::WriteTransaction* write_trans,
234    const sync_api::ReadNode& autofill_root,
235    DataBundle* bundle,
236    const std::vector<AutofillProfile*>& all_profiles_from_db) {
237  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
238
239  bool autofill_profile_not_migrated = HasNotMigratedYet(write_trans);
240
241  if (VLOG_IS_ON(2) && autofill_profile_not_migrated) {
242    VLOG(2) << "[AUTOFILL MIGRATION]"
243            << "Printing profiles from web db";
244
245    for (std::vector<AutofillProfile*>::const_iterator ix =
246        all_profiles_from_db.begin(); ix != all_profiles_from_db.end(); ++ix) {
247      AutofillProfile* p = *ix;
248      VLOG(2) << "[AUTOFILL MIGRATION]  "
249              << p->GetInfo(NAME_FIRST)
250              << p->GetInfo(NAME_LAST);
251    }
252  }
253
254  if (autofill_profile_not_migrated) {
255    VLOG(1) << "[AUTOFILL MIGRATION]"
256            << "Iterating over sync db";
257  }
258
259  int64 sync_child_id = autofill_root.GetFirstChildId();
260  while (sync_child_id != sync_api::kInvalidId) {
261    sync_api::ReadNode sync_child(write_trans);
262    if (!sync_child.InitByIdLookup(sync_child_id)) {
263      LOG(ERROR) << "Failed to fetch child node.";
264      return false;
265    }
266    const sync_pb::AutofillSpecifics& autofill(
267        sync_child.GetAutofillSpecifics());
268
269    if (autofill.has_value()) {
270      AddNativeEntryIfNeeded(autofill, bundle, sync_child);
271    } else if (autofill.has_profile()) {
272      // Ignore autofill profiles if we are not upgrading.
273      if (autofill_profile_not_migrated) {
274        VLOG(2) << "[AUTOFILL MIGRATION] Looking for "
275                << autofill.profile().name_first()
276                << autofill.profile().name_last();
277        AddNativeProfileIfNeeded(
278            autofill.profile(),
279            bundle,
280            sync_child,
281            all_profiles_from_db);
282      }
283    } else {
284      NOTREACHED() << "AutofillSpecifics has no autofill data!";
285    }
286
287    sync_child_id = sync_child.GetSuccessorId();
288  }
289  return true;
290}
291
292// Define the functor to be used as the predicate in find_if call.
293struct CompareProfiles
294  : public std::binary_function<AutofillProfile*, AutofillProfile*, bool> {
295  bool operator() (AutofillProfile* p1, AutofillProfile* p2) const {
296    if (p1->Compare(*p2) == 0)
297      return true;
298    else
299      return false;
300  }
301};
302
303AutofillProfile* AutofillModelAssociator::FindCorrespondingNodeFromWebDB(
304    const sync_pb::AutofillProfileSpecifics& profile,
305    const std::vector<AutofillProfile*>& all_profiles_from_db) {
306  static std::string guid(guid::GenerateGUID());
307  AutofillProfile p;
308  p.set_guid(guid);
309  if (!FillProfileWithServerData(&p, profile)) {
310    // Not a big deal. We encountered an error. Just say this profile does not
311    // exist.
312    LOG(ERROR) << " Profile could not be associated";
313    return NULL;
314  }
315
316  // Now instantiate the functor and call find_if.
317  std::vector<AutofillProfile*>::const_iterator ix =
318      std::find_if(all_profiles_from_db.begin(),
319                   all_profiles_from_db.end(),
320                   std::bind2nd(CompareProfiles(), &p));
321
322  return (ix == all_profiles_from_db.end()) ? NULL : *ix;
323}
324
325void AutofillModelAssociator::AddNativeEntryIfNeeded(
326    const sync_pb::AutofillSpecifics& autofill, DataBundle* bundle,
327    const sync_api::ReadNode& node) {
328  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
329  AutofillKey key(UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()));
330
331  if (bundle->current_entries.find(key) == bundle->current_entries.end()) {
332    std::vector<base::Time> timestamps;
333    int timestamps_count = autofill.usage_timestamp_size();
334    for (int c = 0; c < timestamps_count; ++c) {
335      timestamps.push_back(base::Time::FromInternalValue(
336          autofill.usage_timestamp(c)));
337    }
338    std::string tag(KeyToTag(key.name(), key.value()));
339    Associate(&tag, node.GetId());
340    bundle->new_entries.push_back(AutofillEntry(key, timestamps));
341  }
342}
343
344void AutofillModelAssociator::AddNativeProfileIfNeeded(
345    const sync_pb::AutofillProfileSpecifics& profile,
346    DataBundle* bundle,
347    const sync_api::ReadNode& node,
348    const std::vector<AutofillProfile*>& all_profiles_from_db) {
349
350  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
351
352  AutofillProfile* profile_in_web_db = FindCorrespondingNodeFromWebDB(
353      profile, all_profiles_from_db);
354
355  if (profile_in_web_db != NULL) {
356    VLOG(1) << "[AUTOFILL MIGRATION]"
357            << "Node found in web db. So associating";
358    int64 sync_id = node.GetId();
359    std::string guid = profile_in_web_db->guid();
360    Associate(&guid, sync_id);
361    return;
362  } else {  // Create a new node.
363    VLOG(1) << "[AUTOFILL MIGRATION]"
364            << "Node not found in web db so creating and associating";
365    std::string guid = guid::GenerateGUID();
366    if (guid::IsValidGUID(guid) == false) {
367      DCHECK(false) << "Guid generated is invalid " << guid;
368      return;
369    }
370    Associate(&guid, node.GetId());
371    AutofillProfile* p = new AutofillProfile(guid);
372    FillProfileWithServerData(p, profile);
373    bundle->new_profiles.push_back(p);
374  }
375}
376
377bool AutofillModelAssociator::DisassociateModels() {
378  id_map_.clear();
379  id_map_inverse_.clear();
380  return true;
381}
382
383bool AutofillModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
384  DCHECK(has_nodes);
385  *has_nodes = false;
386  int64 autofill_sync_id;
387  if (!GetSyncIdForTaggedNode(kAutofillTag, &autofill_sync_id)) {
388    LOG(ERROR) << "Server did not create the top-level autofill node. We "
389               << "might be running against an out-of-date server.";
390    return false;
391  }
392  sync_api::ReadTransaction trans(sync_service_->GetUserShare());
393
394  sync_api::ReadNode autofill_node(&trans);
395  if (!autofill_node.InitByIdLookup(autofill_sync_id)) {
396    LOG(ERROR) << "Server did not create the top-level autofill node. We "
397               << "might be running against an out-of-date server.";
398    return false;
399  }
400
401  // The sync model has user created nodes if the autofill folder has any
402  // children.
403  *has_nodes = sync_api::kInvalidId != autofill_node.GetFirstChildId();
404  return true;
405}
406
407void AutofillModelAssociator::AbortAssociation() {
408  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
409  base::AutoLock lock(abort_association_pending_lock_);
410  abort_association_pending_ = true;
411}
412
413const std::string*
414AutofillModelAssociator::GetChromeNodeFromSyncId(int64 sync_id) {
415  SyncIdToAutofillMap::const_iterator iter = id_map_inverse_.find(sync_id);
416  return iter == id_map_inverse_.end() ? NULL : &(iter->second);
417}
418
419bool AutofillModelAssociator::InitSyncNodeFromChromeId(
420    const std::string& node_id,
421    sync_api::BaseNode* sync_node) {
422  return false;
423}
424
425int64 AutofillModelAssociator::GetSyncIdFromChromeId(
426    const std::string& autofill) {
427  AutofillToSyncIdMap::const_iterator iter = id_map_.find(autofill);
428  return iter == id_map_.end() ? sync_api::kInvalidId : iter->second;
429}
430
431void AutofillModelAssociator::Associate(
432    const std::string* autofill, int64 sync_id) {
433  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
434  DCHECK_NE(sync_api::kInvalidId, sync_id);
435  DCHECK(id_map_.find(*autofill) == id_map_.end());
436  DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end());
437  id_map_[*autofill] = sync_id;
438  id_map_inverse_[sync_id] = *autofill;
439}
440
441void AutofillModelAssociator::Disassociate(int64 sync_id) {
442  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
443  SyncIdToAutofillMap::iterator iter = id_map_inverse_.find(sync_id);
444  if (iter == id_map_inverse_.end())
445    return;
446  CHECK(id_map_.erase(iter->second));
447  id_map_inverse_.erase(iter);
448}
449
450bool AutofillModelAssociator::GetSyncIdForTaggedNode(const std::string& tag,
451                                                     int64* sync_id) {
452  sync_api::ReadTransaction trans(sync_service_->GetUserShare());
453  sync_api::ReadNode sync_node(&trans);
454  if (!sync_node.InitByTagLookup(tag.c_str()))
455    return false;
456  *sync_id = sync_node.GetId();
457  return true;
458}
459
460bool AutofillModelAssociator::IsAbortPending() {
461  base::AutoLock lock(abort_association_pending_lock_);
462  return abort_association_pending_;
463}
464
465// static
466std::string AutofillModelAssociator::KeyToTag(const string16& name,
467                                              const string16& value) {
468  std::string ns(kAutofillEntryNamespaceTag);
469  return ns + EscapePath(UTF16ToUTF8(name)) + "|" +
470         EscapePath(UTF16ToUTF8(value));
471}
472
473// static
474bool AutofillModelAssociator::MergeTimestamps(
475    const sync_pb::AutofillSpecifics& autofill,
476    const std::vector<base::Time>& timestamps,
477    std::vector<base::Time>* new_timestamps) {
478  DCHECK(new_timestamps);
479  std::set<base::Time> timestamp_union(timestamps.begin(),
480                                       timestamps.end());
481
482  size_t timestamps_count = autofill.usage_timestamp_size();
483
484  bool different = timestamps.size() != timestamps_count;
485  for (size_t c = 0; c < timestamps_count; ++c) {
486    if (timestamp_union.insert(base::Time::FromInternalValue(
487            autofill.usage_timestamp(c))).second) {
488      different = true;
489    }
490  }
491
492  if (different) {
493    new_timestamps->insert(new_timestamps->begin(),
494                           timestamp_union.begin(),
495                           timestamp_union.end());
496  }
497  return different;
498}
499
500// Helper to compare the local value and cloud value of a field, merge into
501// the local value if they differ, and return whether the merge happened.
502bool MergeField(FormGroup* f, AutofillFieldType t,
503                const std::string& specifics_field) {
504  if (UTF16ToUTF8(f->GetInfo(t)) == specifics_field)
505    return false;
506  f->SetInfo(t, UTF8ToUTF16(specifics_field));
507  return true;
508}
509
510// static
511bool AutofillModelAssociator::FillProfileWithServerData(
512    AutofillProfile* merge_into,
513    const sync_pb::AutofillProfileSpecifics& specifics) {
514  bool diff = false;
515  AutofillProfile* p = merge_into;
516  const sync_pb::AutofillProfileSpecifics& s(specifics);
517  diff = MergeField(p, NAME_FIRST, s.name_first()) || diff;
518  diff = MergeField(p, NAME_LAST, s.name_last()) || diff;
519  diff = MergeField(p, NAME_MIDDLE, s.name_middle()) || diff;
520  diff = MergeField(p, ADDRESS_HOME_LINE1, s.address_home_line1()) || diff;
521  diff = MergeField(p, ADDRESS_HOME_LINE2, s.address_home_line2()) || diff;
522  diff = MergeField(p, ADDRESS_HOME_CITY, s.address_home_city()) || diff;
523  diff = MergeField(p, ADDRESS_HOME_STATE, s.address_home_state()) || diff;
524  diff = MergeField(p, ADDRESS_HOME_COUNTRY, s.address_home_country()) || diff;
525  diff = MergeField(p, ADDRESS_HOME_ZIP, s.address_home_zip()) || diff;
526  diff = MergeField(p, EMAIL_ADDRESS, s.email_address()) || diff;
527  diff = MergeField(p, COMPANY_NAME, s.company_name()) || diff;
528  diff = MergeField(p, PHONE_FAX_WHOLE_NUMBER, s.phone_fax_whole_number())
529      || diff;
530  diff = MergeField(p, PHONE_HOME_WHOLE_NUMBER, s.phone_home_whole_number())
531      || diff;
532  return diff;
533}
534
535bool AutofillModelAssociator::HasNotMigratedYet(
536    const sync_api::BaseTransaction* trans) {
537
538  // Now read the current value from the directory.
539  syncable::AutofillMigrationState autofill_migration_state =
540      sync_service_->GetAutofillMigrationState();
541
542  DCHECK_NE(autofill_migration_state, syncable::NOT_DETERMINED);
543
544  if (autofill_migration_state== syncable::NOT_DETERMINED) {
545    VLOG(1) << "Autofill migration state is not determined inside "
546            << " model associator";
547  }
548
549  if (autofill_migration_state == syncable::NOT_MIGRATED) {
550    return true;
551  }
552
553  if (autofill_migration_state == syncable::INSUFFICIENT_INFO_TO_DETERMINE) {
554      VLOG(1) << "[AUTOFILL MIGRATION]"
555              << "current autofill migration state is insufficient info to"
556              << "determine.";
557      sync_api::ReadNode autofill_profile_root_node(trans);
558      if (!autofill_profile_root_node.InitByTagLookup(
559          browser_sync::kAutofillProfileTag) ||
560          autofill_profile_root_node.GetFirstChildId()==
561            static_cast<int64>(0)) {
562        sync_service_->SetAutofillMigrationState(
563            syncable::NOT_MIGRATED);
564
565        VLOG(1) << "[AUTOFILL MIGRATION]"
566                << "Current autofill migration state is NOT Migrated because"
567                << "legacy autofill root node is present whereas new "
568                << "Autofill profile root node is absent.";
569        return true;
570      }
571
572      sync_service_->SetAutofillMigrationState(syncable::MIGRATED);
573
574      VLOG(1) << "[AUTOFILL MIGRATION]"
575              << "Current autofill migration state is migrated.";
576  }
577
578  return false;
579}
580
581bool AutofillModelAssociator::CryptoReadyIfNecessary() {
582  // We only access the cryptographer while holding a transaction.
583  sync_api::ReadTransaction trans(sync_service_->GetUserShare());
584  syncable::ModelTypeSet encrypted_types;
585  sync_service_->GetEncryptedDataTypes(&encrypted_types);
586  return encrypted_types.count(syncable::AUTOFILL) == 0 ||
587         sync_service_->IsCryptographerReady(&trans);
588}
589
590}  // namespace browser_sync
591