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