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