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 ×tamps)) { 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 ×tamps)) { 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