autofill_change_processor.cc revision 3345a6884c488ff3a535c2c9acdd33d74b37e311
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_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/profile.h"
13#include "chrome/browser/autofill/personal_data_manager.h"
14#include "chrome/browser/sync/glue/autofill_model_associator.h"
15#include "chrome/browser/sync/profile_sync_service.h"
16#include "chrome/browser/webdata/autofill_change.h"
17#include "chrome/browser/webdata/web_data_service.h"
18#include "chrome/browser/webdata/web_database.h"
19#include "chrome/common/notification_service.h"
20
21namespace browser_sync {
22
23AutofillChangeProcessor::AutofillChangeProcessor(
24    AutofillModelAssociator* model_associator,
25    WebDatabase* web_database,
26    PersonalDataManager* personal_data,
27    UnrecoverableErrorHandler* error_handler)
28    : ChangeProcessor(error_handler),
29      model_associator_(model_associator),
30      web_database_(web_database),
31      personal_data_(personal_data),
32      observing_(false) {
33  DCHECK(model_associator);
34  DCHECK(web_database);
35  DCHECK(error_handler);
36  DCHECK(personal_data);
37  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
38  StartObserving();
39}
40
41void AutofillChangeProcessor::Observe(NotificationType type,
42                                      const NotificationSource& source,
43                                      const NotificationDetails& details) {
44  LOG(INFO) << "Observed autofill change.";
45  // Ensure this notification came from our web database.
46  WebDataService* wds = Source<WebDataService>(source).ptr();
47  if (!wds || wds->GetDatabase() != web_database_)
48    return;
49
50  DCHECK(running());
51  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
52  if (!observing_)
53    return;
54
55  sync_api::WriteTransaction trans(share_handle());
56  sync_api::ReadNode autofill_root(&trans);
57  if (!autofill_root.InitByTagLookup(kAutofillTag)) {
58    error_handler()->OnUnrecoverableError(FROM_HERE,
59        "Server did not create the top-level autofill node. "
60        "We might be running against an out-of-date server.");
61    return;
62  }
63
64  switch (type.value) {
65    case NotificationType::AUTOFILL_ENTRIES_CHANGED: {
66      AutofillChangeList* changes = Details<AutofillChangeList>(details).ptr();
67      ObserveAutofillEntriesChanged(changes, &trans, autofill_root);
68      break;
69    }
70    case NotificationType::AUTOFILL_PROFILE_CHANGED: {
71      AutofillProfileChange* change =
72          Details<AutofillProfileChange>(details).ptr();
73      ObserveAutofillProfileChanged(change, &trans, autofill_root);
74      break;
75    }
76    default:
77      NOTREACHED()  << "Invalid NotificationType.";
78  }
79}
80
81void AutofillChangeProcessor::ChangeProfileLabelIfAlreadyTaken(
82    sync_api::BaseTransaction* trans,
83    const string16& pre_update_label,
84    AutoFillProfile* profile,
85    std::string* tag) {
86  DCHECK_EQ(AutofillModelAssociator::ProfileLabelToTag(profile->Label()),
87                                                       *tag);
88  sync_api::ReadNode read_node(trans);
89  if (!pre_update_label.empty() && pre_update_label == profile->Label())
90    return;
91  if (read_node.InitByClientTagLookup(syncable::AUTOFILL, *tag)) {
92    // Handle the edge case of duplicate labels.
93    string16 new_label(AutofillModelAssociator::MakeUniqueLabel(
94        profile->Label(), pre_update_label, trans));
95    if (new_label.empty()) {
96      error_handler()->OnUnrecoverableError(FROM_HERE,
97          "No unique label; can't move aside");
98      return;
99    }
100    OverrideProfileLabel(new_label, profile, tag);
101  }
102}
103
104void AutofillChangeProcessor::OverrideProfileLabel(
105    const string16& new_label,
106    AutoFillProfile* profile_to_update,
107    std::string* tag_to_update) {
108  tag_to_update->assign(AutofillModelAssociator::ProfileLabelToTag(new_label));
109
110  profile_to_update->set_label(new_label);
111  if (!web_database_->UpdateAutoFillProfile(*profile_to_update)) {
112    std::string err = "Failed to overwrite label for node ";
113    err += UTF16ToUTF8(new_label);
114    error_handler()->OnUnrecoverableError(FROM_HERE, err);
115    return;
116  }
117
118  // Notify the PersonalDataManager that it's out of date.
119  PostOptimisticRefreshTask();
120}
121
122void AutofillChangeProcessor::PostOptimisticRefreshTask() {
123  ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
124      new AutofillModelAssociator::DoOptimisticRefreshTask(
125           personal_data_));
126}
127
128void AutofillChangeProcessor::AddAutofillProfileSyncNode(
129    sync_api::WriteTransaction* trans, const sync_api::BaseNode& autofill,
130    const std::string& tag, const AutoFillProfile* profile) {
131  sync_api::WriteNode sync_node(trans);
132  if (!sync_node.InitUniqueByCreation(syncable::AUTOFILL, autofill, tag)) {
133    error_handler()->OnUnrecoverableError(FROM_HERE,
134        "Failed to create autofill sync node.");
135    return;
136  }
137  sync_node.SetTitle(UTF8ToWide(tag));
138
139  WriteAutofillProfile(*profile, &sync_node);
140  model_associator_->Associate(&tag, sync_node.GetId());
141}
142
143void AutofillChangeProcessor::ObserveAutofillProfileChanged(
144    AutofillProfileChange* change, sync_api::WriteTransaction* trans,
145    const sync_api::ReadNode& autofill_root) {
146  std::string tag(AutofillModelAssociator::ProfileLabelToTag(change->key()));
147  switch (change->type()) {
148    case AutofillProfileChange::ADD: {
149      scoped_ptr<AutoFillProfile> clone(
150          static_cast<AutoFillProfile*>(change->profile()->Clone()));
151      DCHECK_EQ(clone->Label(), change->key());
152      ChangeProfileLabelIfAlreadyTaken(trans, string16(), clone.get(), &tag);
153      AddAutofillProfileSyncNode(trans, autofill_root, tag, clone.get());
154      break;
155    }
156    case AutofillProfileChange::UPDATE: {
157      scoped_ptr<AutoFillProfile> clone(
158          static_cast<AutoFillProfile*>(change->profile()->Clone()));
159      std::string pre_update_tag = AutofillModelAssociator::ProfileLabelToTag(
160          change->pre_update_label());
161      DCHECK_EQ(clone->Label(), change->key());
162      sync_api::WriteNode sync_node(trans);
163      ChangeProfileLabelIfAlreadyTaken(trans, change->pre_update_label(),
164                                       clone.get(), &tag);
165      if (pre_update_tag != tag) {
166        // If the label changes, replace the node instead of updating it.
167        RemoveSyncNode(pre_update_tag, trans);
168        AddAutofillProfileSyncNode(trans, autofill_root, tag, clone.get());
169        return;
170      }
171      int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
172      if (sync_api::kInvalidId == sync_id) {
173        std::string err = "Unexpected notification for: " + tag;
174        error_handler()->OnUnrecoverableError(FROM_HERE, err);
175        return;
176      } else {
177        if (!sync_node.InitByIdLookup(sync_id)) {
178          error_handler()->OnUnrecoverableError(FROM_HERE,
179              "Autofill node lookup failed.");
180          return;
181        }
182        WriteAutofillProfile(*clone.get(), &sync_node);
183      }
184      break;
185    }
186    case AutofillProfileChange::REMOVE: {
187      RemoveSyncNode(tag, trans);
188      break;
189    }
190  }
191}
192
193void AutofillChangeProcessor::ObserveAutofillEntriesChanged(
194    AutofillChangeList* changes, sync_api::WriteTransaction* trans,
195    const sync_api::ReadNode& autofill_root) {
196  for (AutofillChangeList::iterator change = changes->begin();
197       change != changes->end(); ++change) {
198    switch (change->type()) {
199      case AutofillChange::ADD:
200        {
201          sync_api::WriteNode sync_node(trans);
202          std::string tag =
203              AutofillModelAssociator::KeyToTag(change->key().name(),
204                                                change->key().value());
205          if (!sync_node.InitUniqueByCreation(syncable::AUTOFILL,
206                                              autofill_root, tag)) {
207            error_handler()->OnUnrecoverableError(FROM_HERE,
208                "Failed to create autofill sync node.");
209            return;
210          }
211
212          std::vector<base::Time> timestamps;
213          if (!web_database_->GetAutofillTimestamps(
214                  change->key().name(),
215                  change->key().value(),
216                  &timestamps)) {
217            error_handler()->OnUnrecoverableError(FROM_HERE,
218                "Failed to get timestamps.");
219            return;
220          }
221
222          sync_node.SetTitle(UTF8ToWide(tag));
223
224          WriteAutofillEntry(AutofillEntry(change->key(), timestamps),
225                             &sync_node);
226          model_associator_->Associate(&tag, sync_node.GetId());
227        }
228        break;
229
230      case AutofillChange::UPDATE:
231        {
232          sync_api::WriteNode sync_node(trans);
233          std::string tag = AutofillModelAssociator::KeyToTag(
234              change->key().name(), change->key().value());
235          int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
236          if (sync_api::kInvalidId == sync_id) {
237            std::string err = "Unexpected notification for: " +
238                UTF16ToUTF8(change->key().name());
239            error_handler()->OnUnrecoverableError(FROM_HERE, err);
240            return;
241          } else {
242            if (!sync_node.InitByIdLookup(sync_id)) {
243              error_handler()->OnUnrecoverableError(FROM_HERE,
244                  "Autofill node lookup failed.");
245              return;
246            }
247          }
248
249          std::vector<base::Time> timestamps;
250          if (!web_database_->GetAutofillTimestamps(
251                   change->key().name(),
252                   change->key().value(),
253                   &timestamps)) {
254            error_handler()->OnUnrecoverableError(FROM_HERE,
255                "Failed to get timestamps.");
256            return;
257          }
258
259          WriteAutofillEntry(AutofillEntry(change->key(), timestamps),
260                             &sync_node);
261        }
262        break;
263      case AutofillChange::REMOVE: {
264        std::string tag = AutofillModelAssociator::KeyToTag(
265            change->key().name(), change->key().value());
266        RemoveSyncNode(tag, trans);
267        }
268        break;
269    }
270  }
271}
272
273void AutofillChangeProcessor::RemoveSyncNode(const std::string& tag,
274    sync_api::WriteTransaction* trans) {
275  sync_api::WriteNode sync_node(trans);
276  int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
277  if (sync_api::kInvalidId == sync_id) {
278    std::string err = "Unexpected notification for: " + tag;
279    error_handler()->OnUnrecoverableError(FROM_HERE, err);
280    return;
281  } else {
282    if (!sync_node.InitByIdLookup(sync_id)) {
283      error_handler()->OnUnrecoverableError(FROM_HERE,
284          "Autofill node lookup failed.");
285      return;
286    }
287    model_associator_->Disassociate(sync_node.GetId());
288    sync_node.Remove();
289  }
290}
291
292void AutofillChangeProcessor::ApplyChangesFromSyncModel(
293    const sync_api::BaseTransaction* trans,
294    const sync_api::SyncManager::ChangeRecord* changes,
295    int change_count) {
296  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
297  if (!running())
298    return;
299  StopObserving();
300
301  sync_api::ReadNode autofill_root(trans);
302  if (!autofill_root.InitByTagLookup(kAutofillTag)) {
303    error_handler()->OnUnrecoverableError(FROM_HERE,
304        "Autofill root node lookup failed.");
305    return;
306  }
307
308  for (int i = 0; i < change_count; ++i) {
309    sync_api::SyncManager::ChangeRecord::Action action(changes[i].action);
310    if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == action) {
311      DCHECK(changes[i].specifics.HasExtension(sync_pb::autofill))
312          << "Autofill specifics data not present on delete!";
313      const sync_pb::AutofillSpecifics& autofill =
314          changes[i].specifics.GetExtension(sync_pb::autofill);
315      if (autofill.has_value() || autofill.has_profile()) {
316        autofill_changes_.push_back(AutofillChangeRecord(changes[i].action,
317                                                         changes[i].id,
318                                                         autofill));
319      } else {
320        NOTREACHED() << "Autofill specifics has no data!";
321      }
322      continue;
323    }
324
325    // Handle an update or add.
326    sync_api::ReadNode sync_node(trans);
327    if (!sync_node.InitByIdLookup(changes[i].id)) {
328      error_handler()->OnUnrecoverableError(FROM_HERE,
329          "Autofill node lookup failed.");
330      return;
331    }
332
333    // Check that the changed node is a child of the autofills folder.
334    DCHECK(autofill_root.GetId() == sync_node.GetParentId());
335    DCHECK(syncable::AUTOFILL == sync_node.GetModelType());
336
337    const sync_pb::AutofillSpecifics& autofill(
338        sync_node.GetAutofillSpecifics());
339    int64 sync_id = sync_node.GetId();
340    if (autofill.has_value() || autofill.has_profile()) {
341      autofill_changes_.push_back(AutofillChangeRecord(changes[i].action,
342                                                       sync_id, autofill));
343    } else {
344      NOTREACHED() << "Autofill specifics has no data!";
345    }
346  }
347
348  StartObserving();
349}
350
351void AutofillChangeProcessor::CommitChangesFromSyncModel() {
352  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
353  if (!running())
354    return;
355  StopObserving();
356
357  std::vector<AutofillEntry> new_entries;
358  for (unsigned int i = 0; i < autofill_changes_.size(); i++) {
359    // Handle deletions.
360    if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
361        autofill_changes_[i].action_) {
362      if (autofill_changes_[i].autofill_.has_value()) {
363        ApplySyncAutofillEntryDelete(autofill_changes_[i].autofill_);
364      } else if (autofill_changes_[i].autofill_.has_profile()) {
365        ApplySyncAutofillProfileDelete(autofill_changes_[i].autofill_.profile(),
366                                       autofill_changes_[i].id_);
367      } else {
368        NOTREACHED() << "Autofill's CommitChanges received change with no"
369            " data!";
370      }
371      continue;
372    }
373
374    // Handle update/adds.
375    if (autofill_changes_[i].autofill_.has_value()) {
376      ApplySyncAutofillEntryChange(autofill_changes_[i].action_,
377                                   autofill_changes_[i].autofill_, &new_entries,
378                                   autofill_changes_[i].id_);
379    } else if (autofill_changes_[i].autofill_.has_profile()) {
380      ApplySyncAutofillProfileChange(autofill_changes_[i].action_,
381                                     autofill_changes_[i].autofill_.profile(),
382                                     autofill_changes_[i].id_);
383    } else {
384      NOTREACHED() << "Autofill's CommitChanges received change with no data!";
385    }
386  }
387  autofill_changes_.clear();
388
389  // Make changes
390  if (!web_database_->UpdateAutofillEntries(new_entries)) {
391    error_handler()->OnUnrecoverableError(FROM_HERE,
392                                          "Could not update autofill entries.");
393    return;
394  }
395
396  PostOptimisticRefreshTask();
397
398  StartObserving();
399}
400
401void AutofillChangeProcessor::ApplySyncAutofillEntryDelete(
402      const sync_pb::AutofillSpecifics& autofill) {
403  if (!web_database_->RemoveFormElement(
404      UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()))) {
405    error_handler()->OnUnrecoverableError(FROM_HERE,
406        "Could not remove autofill node.");
407    return;
408  }
409}
410
411void AutofillChangeProcessor::ApplySyncAutofillEntryChange(
412      sync_api::SyncManager::ChangeRecord::Action action,
413      const sync_pb::AutofillSpecifics& autofill,
414      std::vector<AutofillEntry>* new_entries,
415      int64 sync_id) {
416  DCHECK_NE(sync_api::SyncManager::ChangeRecord::ACTION_DELETE, action);
417
418  std::vector<base::Time> timestamps;
419  size_t timestamps_size = autofill.usage_timestamp_size();
420  for (size_t c = 0; c < timestamps_size; ++c) {
421    timestamps.push_back(
422        base::Time::FromInternalValue(autofill.usage_timestamp(c)));
423  }
424  AutofillKey k(UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()));
425  AutofillEntry new_entry(k, timestamps);
426
427  new_entries->push_back(new_entry);
428  std::string tag(AutofillModelAssociator::KeyToTag(k.name(), k.value()));
429  if (action == sync_api::SyncManager::ChangeRecord::ACTION_ADD)
430    model_associator_->Associate(&tag, sync_id);
431}
432
433void AutofillChangeProcessor::ApplySyncAutofillProfileChange(
434    sync_api::SyncManager::ChangeRecord::Action action,
435    const sync_pb::AutofillProfileSpecifics& profile,
436    int64 sync_id) {
437  DCHECK_NE(sync_api::SyncManager::ChangeRecord::ACTION_DELETE, action);
438
439  std::string tag(AutofillModelAssociator::ProfileLabelToTag(
440      UTF8ToUTF16(profile.label())));
441  switch (action) {
442    case sync_api::SyncManager::ChangeRecord::ACTION_ADD: {
443      PersonalDataManager* pdm = model_associator_->sync_service()->
444          profile()->GetPersonalDataManager();
445      scoped_ptr<AutoFillProfile> p(
446          pdm->CreateNewEmptyAutoFillProfileForDBThread(
447              UTF8ToUTF16(profile.label())));
448      AutofillModelAssociator::OverwriteProfileWithServerData(p.get(),
449                                                              profile);
450
451      model_associator_->Associate(&tag, sync_id);
452      if (!web_database_->AddAutoFillProfile(*p.get())) {
453        NOTREACHED() << "Couldn't add autofill profile: " << profile.label();
454        return;
455      }
456      break;
457    }
458    case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: {
459      AutoFillProfile* p = NULL;
460      string16 label = UTF8ToUTF16(profile.label());
461      if (!web_database_->GetAutoFillProfileForLabel(label, &p)) {
462        NOTREACHED() << "Couldn't retrieve autofill profile: " << label;
463        return;
464      }
465      AutofillModelAssociator::OverwriteProfileWithServerData(p, profile);
466      if (!web_database_->UpdateAutoFillProfile(*p)) {
467        NOTREACHED() << "Couldn't update autofill profile: " << label;
468        return;
469      }
470      delete p;
471      break;
472    }
473    default:
474      NOTREACHED();
475  }
476}
477
478void AutofillChangeProcessor::ApplySyncAutofillProfileDelete(
479    const sync_pb::AutofillProfileSpecifics& profile,
480    int64 sync_id) {
481  string16 label(UTF8ToUTF16(profile.label()));
482  AutoFillProfile* ptr = NULL;
483  bool get_success = web_database_->GetAutoFillProfileForLabel(label, &ptr);
484  scoped_ptr<AutoFillProfile> p(ptr);
485  if (!get_success) {
486    NOTREACHED() << "Couldn't retrieve autofill profile: " << label;
487    return;
488  }
489  if (!web_database_->RemoveAutoFillProfile(p->unique_id())) {
490    NOTREACHED() << "Couldn't remove autofill profile: " << label;
491    return;
492  }
493  model_associator_->Disassociate(sync_id);
494}
495
496void AutofillChangeProcessor::StartImpl(Profile* profile) {
497  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
498  observing_ = true;
499}
500
501void AutofillChangeProcessor::StopImpl() {
502  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
503  observing_ = false;
504}
505
506
507void AutofillChangeProcessor::StartObserving() {
508  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
509  notification_registrar_.Add(this, NotificationType::AUTOFILL_ENTRIES_CHANGED,
510                              NotificationService::AllSources());
511  notification_registrar_.Add(this, NotificationType::AUTOFILL_PROFILE_CHANGED,
512                              NotificationService::AllSources());
513}
514
515void AutofillChangeProcessor::StopObserving() {
516  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
517  notification_registrar_.RemoveAll();
518}
519
520// static
521void AutofillChangeProcessor::WriteAutofillEntry(
522    const AutofillEntry& entry,
523    sync_api::WriteNode* node) {
524  sync_pb::AutofillSpecifics autofill;
525  autofill.set_name(UTF16ToUTF8(entry.key().name()));
526  autofill.set_value(UTF16ToUTF8(entry.key().value()));
527  const std::vector<base::Time>& ts(entry.timestamps());
528  for (std::vector<base::Time>::const_iterator timestamp = ts.begin();
529       timestamp != ts.end(); ++timestamp) {
530    autofill.add_usage_timestamp(timestamp->ToInternalValue());
531  }
532  node->SetAutofillSpecifics(autofill);
533}
534
535// static
536void AutofillChangeProcessor::WriteAutofillProfile(
537    const AutoFillProfile& profile, sync_api::WriteNode* node) {
538  sync_pb::AutofillSpecifics autofill;
539  sync_pb::AutofillProfileSpecifics* s(autofill.mutable_profile());
540  s->set_label(UTF16ToUTF8(profile.Label()));
541  s->set_name_first(UTF16ToUTF8(
542      profile.GetFieldText(AutoFillType(NAME_FIRST))));
543  s->set_name_middle(UTF16ToUTF8(
544      profile.GetFieldText(AutoFillType(NAME_MIDDLE))));
545  s->set_name_last(UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_LAST))));
546  s->set_address_home_line1(
547      UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE1))));
548  s->set_address_home_line2(
549      UTF16ToUTF8(profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE2))));
550  s->set_address_home_city(UTF16ToUTF8(profile.GetFieldText(
551      AutoFillType(ADDRESS_HOME_CITY))));
552  s->set_address_home_state(UTF16ToUTF8(profile.GetFieldText(
553      AutoFillType(ADDRESS_HOME_STATE))));
554  s->set_address_home_country(UTF16ToUTF8(profile.GetFieldText(
555      AutoFillType(ADDRESS_HOME_COUNTRY))));
556  s->set_address_home_zip(UTF16ToUTF8(profile.GetFieldText(
557      AutoFillType(ADDRESS_HOME_ZIP))));
558  s->set_email_address(UTF16ToUTF8(profile.GetFieldText(
559      AutoFillType(EMAIL_ADDRESS))));
560  s->set_company_name(UTF16ToUTF8(profile.GetFieldText(
561      AutoFillType(COMPANY_NAME))));
562  s->set_phone_fax_whole_number(UTF16ToUTF8(profile.GetFieldText(
563      AutoFillType(PHONE_FAX_WHOLE_NUMBER))));
564  s->set_phone_home_whole_number(UTF16ToUTF8(profile.GetFieldText(
565      AutoFillType(PHONE_HOME_WHOLE_NUMBER))));
566  node->SetAutofillSpecifics(autofill);
567}
568
569}  // namespace browser_sync
570