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_profile_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/autofill_profile.h"
     13 #include "chrome/browser/autofill/personal_data_manager.h"
     14 #include "chrome/browser/sync/engine/syncapi.h"
     15 #include "chrome/browser/sync/glue/autofill_profile_model_associator.h"
     16 #include "chrome/browser/sync/glue/change_processor.h"
     17 #include "chrome/browser/sync/glue/do_optimistic_refresh_task.h"
     18 #include "chrome/browser/sync/unrecoverable_error_handler.h"
     19 #include "chrome/browser/webdata/autofill_change.h"
     20 #include "chrome/browser/webdata/web_database.h"
     21 #include "chrome/common/guid.h"
     22 #include "content/common/notification_registrar.h"
     23 #include "content/common/notification_service.h"
     24 #include "content/common/notification_type.h"
     25 
     26 namespace browser_sync {
     27 
     28 AutofillProfileChangeProcessor::AutofillProfileChangeProcessor(
     29       AutofillProfileModelAssociator *model_associator,
     30       WebDatabase* web_database,
     31       PersonalDataManager* personal_data_manager,
     32       UnrecoverableErrorHandler* error_handler)
     33       : ChangeProcessor(error_handler),
     34         model_associator_(model_associator),
     35         observing_(false),
     36         web_database_(web_database),
     37         personal_data_(personal_data_manager) {
     38   DCHECK(model_associator);
     39   DCHECK(web_database);
     40   DCHECK(error_handler);
     41   DCHECK(personal_data_manager);
     42   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
     43 
     44   StartObserving();
     45 }
     46 
     47 AutofillProfileChangeProcessor::~AutofillProfileChangeProcessor() {}
     48 
     49 AutofillProfileChangeProcessor::ScopedStopObserving::ScopedStopObserving(
     50     AutofillProfileChangeProcessor* processor) {
     51   processor_ = processor;
     52   processor_->StopObserving();
     53 }
     54 
     55 AutofillProfileChangeProcessor::ScopedStopObserving::~ScopedStopObserving() {
     56   processor_->StartObserving();
     57 }
     58 
     59 void AutofillProfileChangeProcessor::ApplyChangesFromSyncModel(
     60     const sync_api::BaseTransaction *write_trans,
     61     const sync_api::SyncManager::ChangeRecord* changes,
     62     int change_count) {
     63 
     64   ScopedStopObserving observer(this);
     65 
     66   sync_api::ReadNode autofill_profile_root(write_trans);
     67   if (!autofill_profile_root.InitByTagLookup(kAutofillProfileTag)) {
     68     error_handler()->OnUnrecoverableError(FROM_HERE,
     69       "Autofill Profile root node lookup failed");
     70     return;
     71   }
     72 
     73   for (int i = 0; i < change_count; ++i) {
     74     if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
     75         changes[i].action) {
     76       DCHECK(changes[i].specifics.HasExtension(
     77           sync_pb::autofill_profile));
     78 
     79       const sync_pb::AutofillProfileSpecifics& specifics =
     80           changes[i].specifics.GetExtension(sync_pb::autofill_profile);
     81 
     82       autofill_changes_.push_back(AutofillProfileChangeRecord(changes[i].action,
     83           changes[i].id,
     84           specifics));
     85       continue;
     86     }
     87 
     88     // If it is not a delete.
     89     sync_api::ReadNode sync_node(write_trans);
     90     if (!sync_node.InitByIdLookup(changes[i].id)) {
     91       LOG(ERROR) << "Could not find the id in sync db " << changes[i].id;
     92       continue;
     93     }
     94 
     95     const sync_pb::AutofillProfileSpecifics& autofill(
     96         sync_node.GetAutofillProfileSpecifics());
     97 
     98     autofill_changes_.push_back(AutofillProfileChangeRecord(changes[i].action,
     99         changes[i].id,
    100         autofill));
    101   }
    102 }
    103 
    104 void AutofillProfileChangeProcessor::Observe(NotificationType type,
    105     const NotificationSource& source,
    106     const NotificationDetails& details) {
    107   DCHECK_EQ(type.value, NotificationType::AUTOFILL_PROFILE_CHANGED);
    108   WebDataService* wds = Source<WebDataService>(source).ptr();
    109 
    110   if (!wds || wds->GetDatabase() != web_database_)
    111     return;
    112 
    113   sync_api::WriteTransaction trans(share_handle());
    114   sync_api::ReadNode autofill_root(&trans);
    115   if (!autofill_root.InitByTagLookup(kAutofillProfileTag)) {
    116     error_handler()->OnUnrecoverableError(FROM_HERE,
    117         "Server did not create a tolp level node");
    118     return;
    119   }
    120 
    121   AutofillProfileChange* change = Details<AutofillProfileChange>(details).ptr();
    122 
    123   ActOnChange(change, &trans, autofill_root);
    124 }
    125 
    126 void AutofillProfileChangeProcessor::ActOnChange(
    127      AutofillProfileChange* change,
    128      sync_api::WriteTransaction* trans,
    129      sync_api::ReadNode& autofill_root) {
    130   DCHECK(change->type() == AutofillProfileChange::REMOVE || change->profile());
    131   switch (change->type()) {
    132     case AutofillProfileChange::ADD: {
    133       AddAutofillProfileSyncNode(trans, autofill_root, *(change->profile()));
    134       break;
    135     }
    136     case AutofillProfileChange::UPDATE: {
    137       int64 sync_id = model_associator_->GetSyncIdFromChromeId(change->key());
    138       if (sync_api::kInvalidId == sync_id) {
    139         LOG(ERROR) << "Sync id is not found for " << change->key();
    140         break;
    141       }
    142       sync_api::WriteNode node(trans);
    143       if (!node.InitByIdLookup(sync_id)) {
    144         LOG(ERROR) << "Could not find sync node for id " << sync_id;
    145         break;
    146       }
    147 
    148       WriteAutofillProfile(*(change->profile()), &node);
    149       break;
    150     }
    151     case AutofillProfileChange::REMOVE: {
    152       int64 sync_id = model_associator_->GetSyncIdFromChromeId(change->key());
    153       if (sync_api::kInvalidId == sync_id) {
    154         LOG(ERROR) << "Sync id is not found for " << change->key();
    155         break;
    156       }
    157       sync_api::WriteNode node(trans);
    158       if (!node.InitByIdLookup(sync_id)) {
    159         LOG(ERROR) << "Could not find sync node for id " << sync_id;
    160         break;
    161       }
    162       node.Remove();
    163       model_associator_->Disassociate(sync_id);
    164       break;
    165     }
    166     default:
    167       NOTREACHED();
    168   }
    169 }
    170 
    171 void AutofillProfileChangeProcessor::CommitChangesFromSyncModel() {
    172   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    173 
    174   if (!running())
    175     return;
    176 
    177   ScopedStopObserving observer(this);
    178 
    179   for (unsigned int i = 0;i < autofill_changes_.size(); ++i) {
    180     if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
    181         autofill_changes_[i].action_) {
    182       if (!web_database_->GetAutofillTable()->RemoveAutofillProfile(
    183           autofill_changes_[i].profile_specifics_.guid())) {
    184         LOG(ERROR) << "could not delete the profile " <<
    185            autofill_changes_[i].profile_specifics_.guid();
    186         continue;
    187       }
    188         continue;
    189     }
    190 
    191     // Now for updates and adds.
    192     ApplyAutofillProfileChange(autofill_changes_[i].action_,
    193         autofill_changes_[i].profile_specifics_,
    194         autofill_changes_[i].id_);
    195   }
    196 
    197   autofill_changes_.clear();
    198 
    199   PostOptimisticRefreshTask();
    200 }
    201 
    202 void AutofillProfileChangeProcessor::PostOptimisticRefreshTask() {
    203   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    204       new DoOptimisticRefreshForAutofill(
    205            personal_data_));
    206 }
    207 
    208 void AutofillProfileChangeProcessor::ApplyAutofillProfileChange(
    209     sync_api::SyncManager::ChangeRecord::Action action,
    210     const sync_pb::AutofillProfileSpecifics& profile_specifics,
    211     int64 sync_id) {
    212 
    213   DCHECK_NE(sync_api::SyncManager::ChangeRecord::ACTION_DELETE, action);
    214   switch (action) {
    215     case sync_api::SyncManager::ChangeRecord::ACTION_ADD: {
    216       if (guid::IsValidGUID(profile_specifics.guid()) == false) {
    217         NOTREACHED() << "Guid from the server is invalid " <<
    218             profile_specifics.guid();
    219         return;
    220       }
    221       AutofillProfile p(profile_specifics.guid());
    222       AutofillProfileModelAssociator::OverwriteProfileWithServerData(&p,
    223           profile_specifics);
    224       if (!web_database_->GetAutofillTable()->AddAutofillProfile(p)) {
    225         LOG(ERROR) << "could not add autofill profile for guid " << p.guid();
    226         break;
    227       }
    228 
    229       // Now that the node has been succesfully created we can associate it.
    230       std::string guid = p.guid();
    231       model_associator_->Associate(&guid, sync_id);
    232       break;
    233     }
    234     case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: {
    235       AutofillProfile *p;
    236       if (!web_database_->GetAutofillTable()->GetAutofillProfile(
    237           profile_specifics.guid(), &p)) {
    238         LOG(ERROR) << "Could not find the autofill profile to update for " <<
    239             profile_specifics.guid();
    240         break;
    241       }
    242       scoped_ptr<AutofillProfile> autofill_pointer(p);
    243       AutofillProfileModelAssociator::OverwriteProfileWithServerData(
    244           autofill_pointer.get(),
    245           profile_specifics);
    246 
    247       if (!web_database_->GetAutofillTable()->UpdateAutofillProfile(
    248           *(autofill_pointer.get()))) {
    249         LOG(ERROR) << "Could not update autofill profile for " <<
    250             profile_specifics.guid();
    251         break;
    252       }
    253       break;
    254     }
    255     default: {
    256       NOTREACHED();
    257       break;
    258     }
    259   }
    260 }
    261 
    262 void AutofillProfileChangeProcessor::RemoveSyncNode(const std::string& guid,
    263     sync_api::WriteTransaction* trans) {
    264   sync_api::WriteNode node(trans);
    265   int64 sync_id = model_associator_->GetSyncIdFromChromeId(guid);
    266   if (sync_api::kInvalidId == sync_id) {
    267     LOG(ERROR) << "Could not find the node in associator " << guid;
    268     return;
    269   }
    270 
    271   if (!node.InitByIdLookup(sync_id)) {
    272     LOG(ERROR) << "Could not find the sync node for " << guid;
    273     return;
    274   }
    275 
    276   model_associator_->Disassociate(sync_id);
    277   node.Remove();
    278 }
    279 
    280 void AutofillProfileChangeProcessor::AddAutofillProfileSyncNode(
    281     sync_api::WriteTransaction* trans,
    282     sync_api::BaseNode& autofill_profile_root,
    283     const AutofillProfile& profile) {
    284 
    285   std::string guid = profile.guid();
    286 
    287   if (guid::IsValidGUID(guid) == false) {
    288     DCHECK(false) << "Guid set on the profile is invalid " << guid;
    289     return;
    290   }
    291 
    292   sync_api::WriteNode node(trans);
    293   if (!node.InitUniqueByCreation(syncable::AUTOFILL_PROFILE,
    294       autofill_profile_root,
    295       profile.guid())) {
    296     LOG(ERROR) << "could not create a sync node ";
    297     return;
    298   }
    299 
    300   node.SetTitle(UTF8ToWide(profile.guid()));
    301 
    302   WriteAutofillProfile(profile, &node);
    303 
    304   model_associator_->Associate(&guid, node.GetId());
    305 }
    306 
    307 void AutofillProfileChangeProcessor::StartObserving() {
    308   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    309   notification_registrar_.Add(this,
    310       NotificationType::AUTOFILL_PROFILE_CHANGED,
    311       NotificationService::AllSources());
    312 }
    313 
    314 void AutofillProfileChangeProcessor::StopObserving() {
    315   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
    316   notification_registrar_.RemoveAll();
    317 }
    318 
    319 void AutofillProfileChangeProcessor::WriteAutofillProfile(
    320     const AutofillProfile& profile,
    321     sync_api::WriteNode* node) {
    322   sync_pb::AutofillProfileSpecifics specifics;
    323 
    324   // This would get compiled out in official builds. The caller is expected to
    325   // pass in a valid profile object with valid guid.(i.e., the caller might
    326   // have to a DCHECK and log before calling. Having to check in 2 places is
    327   // not optimal.)
    328   DCHECK(guid::IsValidGUID(profile.guid()));
    329 
    330   specifics.set_guid(profile.guid());
    331   specifics.set_name_first(UTF16ToUTF8(profile.GetInfo(NAME_FIRST)));
    332   specifics.set_name_middle(UTF16ToUTF8(profile.GetInfo(NAME_MIDDLE)));
    333   specifics.set_name_last(UTF16ToUTF8(profile.GetInfo(NAME_LAST)));
    334   specifics.set_address_home_line1(
    335       UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE1)));
    336   specifics.set_address_home_line2(
    337       UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE2)));
    338   specifics.set_address_home_city(UTF16ToUTF8(profile.GetInfo(
    339       ADDRESS_HOME_CITY)));
    340   specifics.set_address_home_state(UTF16ToUTF8(profile.GetInfo(
    341       ADDRESS_HOME_STATE)));
    342   specifics.set_address_home_country(UTF16ToUTF8(profile.GetInfo(
    343       ADDRESS_HOME_COUNTRY)));
    344   specifics.set_address_home_zip(UTF16ToUTF8(profile.GetInfo(
    345       ADDRESS_HOME_ZIP)));
    346   specifics.set_email_address(UTF16ToUTF8(profile.GetInfo(EMAIL_ADDRESS)));
    347   specifics.set_company_name(UTF16ToUTF8(profile.GetInfo(COMPANY_NAME)));
    348   specifics.set_phone_fax_whole_number(UTF16ToUTF8(profile.GetInfo(
    349       PHONE_FAX_WHOLE_NUMBER)));
    350   specifics.set_phone_home_whole_number(UTF16ToUTF8(profile.GetInfo(
    351       PHONE_HOME_WHOLE_NUMBER)));
    352   node->SetAutofillProfileSpecifics(specifics);
    353 }
    354 
    355 }  // namespace browser_sync
    356