Home | History | Annotate | Download | only in glue
      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_;
     30   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 
     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 
     55 AutofillChangeProcessor::~AutofillChangeProcessor() {}
     56 
     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 
     85 void AutofillChangeProcessor::PostOptimisticRefreshTask() {
     86   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
     87       new DoOptimisticRefreshForAutofill(
     88            personal_data_));
     89 }
     90 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    408 void AutofillChangeProcessor::StartImpl(Profile* profile) {
    409   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    410   observing_ = true;
    411 }
    412 
    413 void AutofillChangeProcessor::StopImpl() {
    414   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    415   observing_ = false;
    416 }
    417 
    418 
    419 void AutofillChangeProcessor::StartObserving() {
    420   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    421   notification_registrar_.Add(this, NotificationType::AUTOFILL_ENTRIES_CHANGED,
    422                               NotificationService::AllSources());
    423 }
    424 
    425 void AutofillChangeProcessor::StopObserving() {
    426   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    427   notification_registrar_.RemoveAll();
    428 }
    429 
    430 // static
    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 
    445 bool AutofillChangeProcessor::HasNotMigratedYet(
    446     const sync_api::BaseTransaction* trans) {
    447   return model_associator_->HasNotMigratedYet(trans);
    448 }
    449 
    450 }  // namespace browser_sync
    451