autofill_model_associator.cc revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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 <vector>
8
9#include "base/task.h"
10#include "base/time.h"
11#include "base/string_number_conversions.h"
12#include "base/utf_string_conversions.h"
13#include "chrome/browser/autofill/autofill_profile.h"
14#include "chrome/browser/browser_thread.h"
15#include "chrome/browser/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/profile_sync_service.h"
19#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
20#include "chrome/browser/webdata/web_database.h"
21#include "net/base/escape.h"
22
23using base::TimeTicks;
24
25namespace browser_sync {
26
27const char kAutofillTag[] = "google_chrome_autofill";
28const char kAutofillEntryNamespaceTag[] = "autofill_entry|";
29const char kAutofillProfileNamespaceTag[] = "autofill_profile|";
30
31static const int kMaxNumAttemptsToFindUniqueLabel = 100;
32
33struct AutofillModelAssociator::DataBundle {
34  std::set<AutofillKey> current_entries;
35  std::vector<AutofillEntry> new_entries;
36  std::set<string16> current_profiles;
37  std::vector<AutoFillProfile*> updated_profiles;
38  std::vector<AutoFillProfile*> new_profiles;  // We own these pointers.
39  ~DataBundle() { STLDeleteElements(&new_profiles); }
40};
41
42AutofillModelAssociator::DoOptimisticRefreshTask::DoOptimisticRefreshTask(
43    PersonalDataManager* pdm) : pdm_(pdm) {}
44
45AutofillModelAssociator::DoOptimisticRefreshTask::~DoOptimisticRefreshTask() {}
46
47void AutofillModelAssociator::DoOptimisticRefreshTask::Run() {
48  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
49  pdm_->Refresh();
50}
51
52AutofillModelAssociator::AutofillModelAssociator(
53    ProfileSyncService* sync_service,
54    WebDatabase* web_database,
55    PersonalDataManager* personal_data)
56    : sync_service_(sync_service),
57      web_database_(web_database),
58      personal_data_(personal_data),
59      autofill_node_id_(sync_api::kInvalidId),
60      abort_association_pending_(false) {
61  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
62  DCHECK(sync_service_);
63  DCHECK(web_database_);
64  DCHECK(personal_data_);
65}
66
67AutofillModelAssociator::~AutofillModelAssociator() {
68  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
69}
70
71bool AutofillModelAssociator::TraverseAndAssociateChromeAutofillEntries(
72    sync_api::WriteTransaction* write_trans,
73    const sync_api::ReadNode& autofill_root,
74    const std::vector<AutofillEntry>& all_entries_from_db,
75    std::set<AutofillKey>* current_entries,
76    std::vector<AutofillEntry>* new_entries) {
77
78  const std::vector<AutofillEntry>& entries = all_entries_from_db;
79  for (std::vector<AutofillEntry>::const_iterator ix = entries.begin();
80       ix != entries.end(); ++ix) {
81    std::string tag = KeyToTag(ix->key().name(), ix->key().value());
82    if (id_map_.find(tag) != id_map_.end()) {
83      // It seems that name/value pairs are not unique in the web database.
84      // As a result, we have to filter out duplicates here.  This is probably
85      // a bug in the database.
86      continue;
87    }
88
89    sync_api::ReadNode node(write_trans);
90    if (node.InitByClientTagLookup(syncable::AUTOFILL, tag)) {
91      const sync_pb::AutofillSpecifics& autofill(node.GetAutofillSpecifics());
92      DCHECK_EQ(tag, KeyToTag(UTF8ToUTF16(autofill.name()),
93                              UTF8ToUTF16(autofill.value())));
94
95      std::vector<base::Time> timestamps;
96      if (MergeTimestamps(autofill, ix->timestamps(), &timestamps)) {
97        AutofillEntry new_entry(ix->key(), timestamps);
98        new_entries->push_back(new_entry);
99
100        sync_api::WriteNode write_node(write_trans);
101        if (!write_node.InitByClientTagLookup(syncable::AUTOFILL, tag)) {
102          LOG(ERROR) << "Failed to write autofill sync node.";
103          return false;
104        }
105        AutofillChangeProcessor::WriteAutofillEntry(new_entry, &write_node);
106      }
107
108      Associate(&tag, node.GetId());
109    } else {
110      sync_api::WriteNode node(write_trans);
111      if (!node.InitUniqueByCreation(syncable::AUTOFILL,
112                                     autofill_root, tag)) {
113        LOG(ERROR) << "Failed to create autofill sync node.";
114        return false;
115      }
116      node.SetTitle(UTF8ToWide(tag));
117      AutofillChangeProcessor::WriteAutofillEntry(*ix, &node);
118      Associate(&tag, node.GetId());
119    }
120
121    current_entries->insert(ix->key());
122  }
123  return true;
124}
125
126bool AutofillModelAssociator::TraverseAndAssociateChromeAutoFillProfiles(
127    sync_api::WriteTransaction* write_trans,
128    const sync_api::ReadNode& autofill_root,
129    const std::vector<AutoFillProfile*>& all_profiles_from_db,
130    std::set<string16>* current_profiles,
131    std::vector<AutoFillProfile*>* updated_profiles) {
132  const std::vector<AutoFillProfile*>& profiles = all_profiles_from_db;
133  for (std::vector<AutoFillProfile*>::const_iterator ix = profiles.begin();
134       ix != profiles.end(); ++ix) {
135    string16 label((*ix)->Label());
136    std::string tag(ProfileLabelToTag(label));
137
138    sync_api::ReadNode node(write_trans);
139    if (node.InitByClientTagLookup(syncable::AUTOFILL, tag)) {
140      const sync_pb::AutofillSpecifics& autofill(node.GetAutofillSpecifics());
141      DCHECK(autofill.has_profile());
142      DCHECK_EQ(ProfileLabelToTag(UTF8ToUTF16(autofill.profile().label())),
143                tag);
144      int64 sync_id = node.GetId();
145      if (id_map_.find(tag) != id_map_.end()) {
146        // We just looked up something we already associated.  Move aside.
147        label = MakeUniqueLabel(label, string16(), write_trans);
148        if (label.empty()) {
149          return false;
150        }
151        tag = ProfileLabelToTag(label);
152        // TODO(dhollowa): Replace with |AutoFillProfile::set_guid|.
153        // http://crbug.com/58813
154        (*ix)->set_label(label);
155        if (!MakeNewAutofillProfileSyncNode(write_trans, autofill_root,
156                                            tag, **ix, &sync_id)) {
157          return false;
158        }
159        updated_profiles->push_back(*ix);
160      } else {
161        // Overwrite local with cloud state.
162        if (OverwriteProfileWithServerData(*ix, autofill.profile()))
163          updated_profiles->push_back(*ix);
164        sync_id = node.GetId();
165      }
166
167      Associate(&tag, sync_id);
168    } else {
169      int64 id;
170      if (!MakeNewAutofillProfileSyncNode(write_trans, autofill_root,
171                                          tag, **ix, &id)) {
172        return false;
173      }
174      Associate(&tag, id);
175    }
176    current_profiles->insert(label);
177  }
178  return true;
179}
180
181// static
182string16 AutofillModelAssociator::MakeUniqueLabel(
183    const string16& non_unique_label,
184    const string16& existing_unique_label,
185    sync_api::BaseTransaction* trans) {
186  if (!non_unique_label.empty() && non_unique_label == existing_unique_label) {
187    return existing_unique_label;
188  }
189  int unique_id = 1;  // Priming so we start by appending "2".
190  while (unique_id++ < kMaxNumAttemptsToFindUniqueLabel) {
191    string16 suffix(base::IntToString16(unique_id));
192    string16 unique_label = non_unique_label + suffix;
193    if (unique_label == existing_unique_label)
194      return unique_label;  // We'll use the one we already have.
195    sync_api::ReadNode node(trans);
196    if (node.InitByClientTagLookup(syncable::AUTOFILL,
197                                   ProfileLabelToTag(unique_label))) {
198      continue;
199    }
200    return unique_label;
201  }
202
203  LOG(ERROR) << "Couldn't create unique tag for autofill node. Srsly?!";
204  return string16();
205}
206
207bool AutofillModelAssociator::MakeNewAutofillProfileSyncNode(
208    sync_api::WriteTransaction* trans, const sync_api::BaseNode& autofill_root,
209    const std::string& tag, const AutoFillProfile& profile, int64* sync_id) {
210  sync_api::WriteNode node(trans);
211  if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag)) {
212    LOG(ERROR) << "Failed to create autofill sync node.";
213    return false;
214  }
215  node.SetTitle(UTF8ToWide(tag));
216  AutofillChangeProcessor::WriteAutofillProfile(profile, &node);
217  *sync_id = node.GetId();
218  return true;
219}
220
221
222bool AutofillModelAssociator::LoadAutofillData(
223    std::vector<AutofillEntry>* entries,
224    std::vector<AutoFillProfile*>* profiles) {
225  if (IsAbortPending())
226    return false;
227  if (!web_database_->GetAllAutofillEntries(entries))
228    return false;
229
230  if (IsAbortPending())
231    return false;
232  if (!web_database_->GetAutoFillProfiles(profiles))
233    return false;
234
235  return true;
236}
237
238bool AutofillModelAssociator::AssociateModels() {
239  VLOG(1) << "Associating Autofill Models";
240  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
241  {
242    AutoLock lock(abort_association_pending_lock_);
243    abort_association_pending_ = false;
244  }
245
246  // TODO(zork): Attempt to load the model association from storage.
247  std::vector<AutofillEntry> entries;
248  ScopedVector<AutoFillProfile> profiles;
249
250  if (!LoadAutofillData(&entries, &profiles.get())) {
251    LOG(ERROR) << "Could not get the autofill data from WebDatabase.";
252    return false;
253  }
254
255  DataBundle bundle;
256  {
257    sync_api::WriteTransaction trans(
258        sync_service_->backend()->GetUserShareHandle());
259
260    sync_api::ReadNode autofill_root(&trans);
261    if (!autofill_root.InitByTagLookup(kAutofillTag)) {
262      LOG(ERROR) << "Server did not create the top-level autofill node. We "
263                 << "might be running against an out-of-date server.";
264      return false;
265    }
266
267    if (!TraverseAndAssociateChromeAutofillEntries(&trans, autofill_root,
268            entries, &bundle.current_entries, &bundle.new_entries) ||
269        !TraverseAndAssociateChromeAutoFillProfiles(&trans, autofill_root,
270            profiles.get(), &bundle.current_profiles,
271            &bundle.updated_profiles) ||
272        !TraverseAndAssociateAllSyncNodes(&trans, autofill_root, &bundle)) {
273      return false;
274    }
275  }
276
277  // Since we're on the DB thread, we don't have to worry about updating
278  // the autofill database after closing the write transaction, since
279  // this is the only thread that writes to the database.  We also don't have
280  // to worry about the sync model getting out of sync, because changes are
281  // propogated to the ChangeProcessor on this thread.
282  if (!SaveChangesToWebData(bundle)) {
283    LOG(ERROR) << "Failed to update autofill entries.";
284    return false;
285  }
286
287  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
288      new DoOptimisticRefreshTask(personal_data_));
289  return true;
290}
291
292bool AutofillModelAssociator::SaveChangesToWebData(const DataBundle& bundle) {
293  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
294
295  if (IsAbortPending())
296    return false;
297
298  if (bundle.new_entries.size() &&
299      !web_database_->UpdateAutofillEntries(bundle.new_entries)) {
300    return false;
301  }
302
303  for (size_t i = 0; i < bundle.new_profiles.size(); i++) {
304    if (IsAbortPending())
305      return false;
306    if (!web_database_->AddAutoFillProfile(*bundle.new_profiles[i]))
307      return false;
308  }
309
310  for (size_t i = 0; i < bundle.updated_profiles.size(); i++) {
311    if (IsAbortPending())
312      return false;
313    if (!web_database_->UpdateAutoFillProfile(*bundle.updated_profiles[i]))
314      return false;
315  }
316  return true;
317}
318
319bool AutofillModelAssociator::TraverseAndAssociateAllSyncNodes(
320    sync_api::WriteTransaction* write_trans,
321    const sync_api::ReadNode& autofill_root,
322    DataBundle* bundle) {
323  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
324
325  int64 sync_child_id = autofill_root.GetFirstChildId();
326  while (sync_child_id != sync_api::kInvalidId) {
327    sync_api::ReadNode sync_child(write_trans);
328    if (!sync_child.InitByIdLookup(sync_child_id)) {
329      LOG(ERROR) << "Failed to fetch child node.";
330      return false;
331    }
332    const sync_pb::AutofillSpecifics& autofill(
333        sync_child.GetAutofillSpecifics());
334
335    if (autofill.has_value())
336      AddNativeEntryIfNeeded(autofill, bundle, sync_child);
337    else if (autofill.has_profile())
338      AddNativeProfileIfNeeded(autofill.profile(), bundle, sync_child);
339    else
340      NOTREACHED() << "AutofillSpecifics has no autofill data!";
341
342    sync_child_id = sync_child.GetSuccessorId();
343  }
344  return true;
345}
346
347void AutofillModelAssociator::AddNativeEntryIfNeeded(
348    const sync_pb::AutofillSpecifics& autofill, DataBundle* bundle,
349    const sync_api::ReadNode& node) {
350  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
351  AutofillKey key(UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()));
352
353  if (bundle->current_entries.find(key) == bundle->current_entries.end()) {
354    std::vector<base::Time> timestamps;
355    int timestamps_count = autofill.usage_timestamp_size();
356    for (int c = 0; c < timestamps_count; ++c) {
357      timestamps.push_back(base::Time::FromInternalValue(
358          autofill.usage_timestamp(c)));
359    }
360    std::string tag(KeyToTag(key.name(), key.value()));
361    Associate(&tag, node.GetId());
362    bundle->new_entries.push_back(AutofillEntry(key, timestamps));
363  }
364}
365
366void AutofillModelAssociator::AddNativeProfileIfNeeded(
367    const sync_pb::AutofillProfileSpecifics& profile, DataBundle* bundle,
368    const sync_api::ReadNode& node) {
369  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
370  if (bundle->current_profiles.find(UTF8ToUTF16(profile.label())) ==
371      bundle->current_profiles.end()) {
372    std::string tag(ProfileLabelToTag(UTF8ToUTF16(profile.label())));
373    Associate(&tag, node.GetId());
374    AutoFillProfile* p = personal_data_->
375        CreateNewEmptyAutoFillProfileForDBThread(UTF8ToUTF16(profile.label()));
376    OverwriteProfileWithServerData(p, profile);
377    bundle->new_profiles.push_back(p);
378  }
379}
380
381bool AutofillModelAssociator::DisassociateModels() {
382  id_map_.clear();
383  id_map_inverse_.clear();
384  return true;
385}
386
387bool AutofillModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
388  DCHECK(has_nodes);
389  *has_nodes = false;
390  int64 autofill_sync_id;
391  if (!GetSyncIdForTaggedNode(kAutofillTag, &autofill_sync_id)) {
392    LOG(ERROR) << "Server did not create the top-level autofill node. We "
393               << "might be running against an out-of-date server.";
394    return false;
395  }
396  sync_api::ReadTransaction trans(
397      sync_service_->backend()->GetUserShareHandle());
398
399  sync_api::ReadNode autofill_node(&trans);
400  if (!autofill_node.InitByIdLookup(autofill_sync_id)) {
401    LOG(ERROR) << "Server did not create the top-level autofill node. We "
402               << "might be running against an out-of-date server.";
403    return false;
404  }
405
406  // The sync model has user created nodes if the autofill folder has any
407  // children.
408  *has_nodes = sync_api::kInvalidId != autofill_node.GetFirstChildId();
409  return true;
410}
411
412void AutofillModelAssociator::AbortAssociation() {
413  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
414  AutoLock lock(abort_association_pending_lock_);
415  abort_association_pending_ = true;
416}
417
418const std::string*
419AutofillModelAssociator::GetChromeNodeFromSyncId(int64 sync_id) {
420  return NULL;
421}
422
423bool AutofillModelAssociator::InitSyncNodeFromChromeId(
424    std::string node_id,
425    sync_api::BaseNode* sync_node) {
426  return false;
427}
428
429int64 AutofillModelAssociator::GetSyncIdFromChromeId(
430    const std::string autofill) {
431  AutofillToSyncIdMap::const_iterator iter = id_map_.find(autofill);
432  return iter == id_map_.end() ? sync_api::kInvalidId : iter->second;
433}
434
435void AutofillModelAssociator::Associate(
436    const std::string* autofill, int64 sync_id) {
437  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
438  DCHECK_NE(sync_api::kInvalidId, sync_id);
439  DCHECK(id_map_.find(*autofill) == id_map_.end());
440  DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end());
441  id_map_[*autofill] = sync_id;
442  id_map_inverse_[sync_id] = *autofill;
443}
444
445void AutofillModelAssociator::Disassociate(int64 sync_id) {
446  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
447  SyncIdToAutofillMap::iterator iter = id_map_inverse_.find(sync_id);
448  if (iter == id_map_inverse_.end())
449    return;
450  CHECK(id_map_.erase(iter->second));
451  id_map_inverse_.erase(iter);
452}
453
454bool AutofillModelAssociator::GetSyncIdForTaggedNode(const std::string& tag,
455                                                     int64* sync_id) {
456  sync_api::ReadTransaction trans(
457      sync_service_->backend()->GetUserShareHandle());
458  sync_api::ReadNode sync_node(&trans);
459  if (!sync_node.InitByTagLookup(tag.c_str()))
460    return false;
461  *sync_id = sync_node.GetId();
462  return true;
463}
464
465bool AutofillModelAssociator::IsAbortPending() {
466  AutoLock lock(abort_association_pending_lock_);
467  return abort_association_pending_;
468}
469
470// static
471std::string AutofillModelAssociator::KeyToTag(const string16& name,
472                                              const string16& value) {
473  std::string ns(kAutofillEntryNamespaceTag);
474  return ns + EscapePath(UTF16ToUTF8(name)) + "|" +
475         EscapePath(UTF16ToUTF8(value));
476}
477
478// static
479std::string AutofillModelAssociator::ProfileLabelToTag(const string16& label) {
480  std::string ns(kAutofillProfileNamespaceTag);
481  return ns + EscapePath(UTF16ToUTF8(label));
482}
483
484// static
485bool AutofillModelAssociator::MergeTimestamps(
486    const sync_pb::AutofillSpecifics& autofill,
487    const std::vector<base::Time>& timestamps,
488    std::vector<base::Time>* new_timestamps) {
489  DCHECK(new_timestamps);
490  std::set<base::Time> timestamp_union(timestamps.begin(),
491                                       timestamps.end());
492
493  size_t timestamps_count = autofill.usage_timestamp_size();
494
495  bool different = timestamps.size() != timestamps_count;
496  for (size_t c = 0; c < timestamps_count; ++c) {
497    if (timestamp_union.insert(base::Time::FromInternalValue(
498            autofill.usage_timestamp(c))).second) {
499      different = true;
500    }
501  }
502
503  if (different) {
504    new_timestamps->insert(new_timestamps->begin(),
505                           timestamp_union.begin(),
506                           timestamp_union.end());
507  }
508  return different;
509}
510
511// Helper to compare the local value and cloud value of a field, merge into
512// the local value if they differ, and return whether the merge happened.
513bool MergeField(FormGroup* f, AutoFillFieldType t,
514                const std::string& specifics_field) {
515  if (UTF16ToUTF8(f->GetFieldText(AutoFillType(t))) == specifics_field)
516    return false;
517  f->SetInfo(AutoFillType(t), UTF8ToUTF16(specifics_field));
518  return true;
519}
520
521// static
522bool AutofillModelAssociator::OverwriteProfileWithServerData(
523    AutoFillProfile* merge_into,
524    const sync_pb::AutofillProfileSpecifics& specifics) {
525  bool diff = false;
526  AutoFillProfile* p = merge_into;
527  const sync_pb::AutofillProfileSpecifics& s(specifics);
528  diff = MergeField(p, NAME_FIRST, s.name_first()) || diff;
529  diff = MergeField(p, NAME_LAST, s.name_last()) || diff;
530  diff = MergeField(p, NAME_MIDDLE, s.name_middle()) || diff;
531  diff = MergeField(p, ADDRESS_HOME_LINE1, s.address_home_line1()) || diff;
532  diff = MergeField(p, ADDRESS_HOME_LINE2, s.address_home_line2()) || diff;
533  diff = MergeField(p, ADDRESS_HOME_CITY, s.address_home_city()) || diff;
534  diff = MergeField(p, ADDRESS_HOME_STATE, s.address_home_state()) || diff;
535  diff = MergeField(p, ADDRESS_HOME_COUNTRY, s.address_home_country()) || diff;
536  diff = MergeField(p, ADDRESS_HOME_ZIP, s.address_home_zip()) || diff;
537  diff = MergeField(p, EMAIL_ADDRESS, s.email_address()) || diff;
538  diff = MergeField(p, COMPANY_NAME, s.company_name()) || diff;
539  diff = MergeField(p, PHONE_FAX_WHOLE_NUMBER, s.phone_fax_whole_number())
540      || diff;
541  diff = MergeField(p, PHONE_HOME_WHOLE_NUMBER, s.phone_home_whole_number())
542      || diff;
543  return diff;
544}
545
546}  // namespace browser_sync
547