1 // Copyright (c) 2012 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/webdata/autocomplete_syncable_service.h" 6 7 #include "base/location.h" 8 #include "base/logging.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "components/autofill/core/browser/webdata/autofill_table.h" 11 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" 12 #include "components/webdata/common/web_database.h" 13 #include "content/public/browser/browser_thread.h" 14 #include "net/base/escape.h" 15 #include "sync/api/sync_error.h" 16 #include "sync/api/sync_error_factory.h" 17 #include "sync/protocol/autofill_specifics.pb.h" 18 #include "sync/protocol/sync.pb.h" 19 20 using autofill::AutofillChange; 21 using autofill::AutofillChangeList; 22 using autofill::AutofillEntry; 23 using autofill::AutofillKey; 24 using autofill::AutofillTable; 25 using autofill::AutofillWebDataService; 26 using autofill::AutofillWebDataBackend; 27 using content::BrowserThread; 28 29 namespace { 30 31 const char kAutofillEntryNamespaceTag[] = "autofill_entry|"; 32 33 // Merges timestamps from the |autofill| entry and |timestamps|. Returns 34 // true if they were different, false if they were the same. 35 // All of the timestamp vectors are assummed to be sorted, resulting vector is 36 // sorted as well. Only two timestamps - the earliest and the latest are stored. 37 bool MergeTimestamps(const sync_pb::AutofillSpecifics& autofill, 38 const std::vector<base::Time>& timestamps, 39 std::vector<base::Time>* new_timestamps) { 40 DCHECK(new_timestamps); 41 42 new_timestamps->clear(); 43 size_t timestamps_count = autofill.usage_timestamp_size(); 44 if (timestamps_count == 0 && timestamps.empty()) { 45 return false; 46 } else if (timestamps_count == 0) { 47 new_timestamps->insert(new_timestamps->begin(), 48 timestamps.begin(), 49 timestamps.end()); 50 return true; 51 } else if (timestamps.empty()) { 52 new_timestamps->reserve(2); 53 new_timestamps->push_back(base::Time::FromInternalValue( 54 autofill.usage_timestamp(0))); 55 if (timestamps_count > 1) { 56 new_timestamps->push_back(base::Time::FromInternalValue( 57 autofill.usage_timestamp(timestamps_count - 1))); 58 } 59 return true; 60 } else { 61 base::Time sync_time_begin = base::Time::FromInternalValue( 62 autofill.usage_timestamp(0)); 63 base::Time sync_time_end = base::Time::FromInternalValue( 64 autofill.usage_timestamp(timestamps_count - 1)); 65 if (timestamps.front() != sync_time_begin || 66 timestamps.back() != sync_time_end) { 67 new_timestamps->push_back( 68 timestamps.front() < sync_time_begin ? timestamps.front() : 69 sync_time_begin); 70 if (new_timestamps->back() != timestamps.back() || 71 new_timestamps->back() != sync_time_end) { 72 new_timestamps->push_back( 73 timestamps.back() > sync_time_end ? timestamps.back() : 74 sync_time_end); 75 } 76 return true; 77 } else { 78 new_timestamps->insert(new_timestamps->begin(), 79 timestamps.begin(), 80 timestamps.end()); 81 return false; 82 } 83 } 84 } 85 86 void* UserDataKey() { 87 // Use the address of a static that COMDAT folding won't ever fold 88 // with something else. 89 static int user_data_key = 0; 90 return reinterpret_cast<void*>(&user_data_key); 91 } 92 93 } // namespace 94 95 AutocompleteSyncableService::AutocompleteSyncableService( 96 AutofillWebDataBackend* webdata_backend) 97 : webdata_backend_(webdata_backend), 98 scoped_observer_(this), 99 cull_expired_entries_(false) { 100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 101 DCHECK(webdata_backend_); 102 103 scoped_observer_.Add(webdata_backend_); 104 } 105 106 AutocompleteSyncableService::~AutocompleteSyncableService() { 107 DCHECK(CalledOnValidThread()); 108 } 109 110 // static 111 void AutocompleteSyncableService::CreateForWebDataServiceAndBackend( 112 AutofillWebDataService* web_data_service, 113 AutofillWebDataBackend* webdata_backend) { 114 web_data_service->GetDBUserData()->SetUserData( 115 UserDataKey(), new AutocompleteSyncableService(webdata_backend)); 116 } 117 118 // static 119 AutocompleteSyncableService* AutocompleteSyncableService::FromWebDataService( 120 AutofillWebDataService* web_data_service) { 121 return static_cast<AutocompleteSyncableService*>( 122 web_data_service->GetDBUserData()->GetUserData(UserDataKey())); 123 } 124 125 AutocompleteSyncableService::AutocompleteSyncableService() 126 : webdata_backend_(NULL), 127 scoped_observer_(this), 128 cull_expired_entries_(false) { 129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 130 } 131 132 void AutocompleteSyncableService::InjectStartSyncFlare( 133 const syncer::SyncableService::StartSyncFlare& flare) { 134 flare_ = flare; 135 } 136 137 syncer::SyncMergeResult AutocompleteSyncableService::MergeDataAndStartSyncing( 138 syncer::ModelType type, 139 const syncer::SyncDataList& initial_sync_data, 140 scoped_ptr<syncer::SyncChangeProcessor> sync_processor, 141 scoped_ptr<syncer::SyncErrorFactory> error_handler) { 142 DCHECK(CalledOnValidThread()); 143 DCHECK(!sync_processor_.get()); 144 DCHECK(sync_processor.get()); 145 DCHECK(error_handler.get()); 146 VLOG(1) << "Associating Autocomplete: MergeDataAndStartSyncing"; 147 148 syncer::SyncMergeResult merge_result(type); 149 error_handler_ = error_handler.Pass(); 150 std::vector<AutofillEntry> entries; 151 if (!LoadAutofillData(&entries)) { 152 merge_result.set_error(error_handler_->CreateAndUploadError( 153 FROM_HERE, 154 "Could not get the autocomplete data from WebDatabase.")); 155 return merge_result; 156 } 157 158 AutocompleteEntryMap new_db_entries; 159 for (std::vector<AutofillEntry>::iterator it = entries.begin(); 160 it != entries.end(); ++it) { 161 new_db_entries[it->key()] = 162 std::make_pair(syncer::SyncChange::ACTION_ADD, it); 163 } 164 165 sync_processor_ = sync_processor.Pass(); 166 167 std::vector<AutofillEntry> new_synced_entries; 168 // Go through and check for all the entries that sync already knows about. 169 // CreateOrUpdateEntry() will remove entries that are same with the synced 170 // ones from |new_db_entries|. 171 for (syncer::SyncDataList::const_iterator sync_iter = 172 initial_sync_data.begin(); 173 sync_iter != initial_sync_data.end(); ++sync_iter) { 174 CreateOrUpdateEntry(*sync_iter, &new_db_entries, &new_synced_entries); 175 } 176 177 if (!SaveChangesToWebData(new_synced_entries)) { 178 merge_result.set_error(error_handler_->CreateAndUploadError( 179 FROM_HERE, 180 "Failed to update webdata.")); 181 return merge_result; 182 } 183 184 webdata_backend_->NotifyOfMultipleAutofillChanges(); 185 186 syncer::SyncChangeList new_changes; 187 for (AutocompleteEntryMap::iterator i = new_db_entries.begin(); 188 i != new_db_entries.end(); ++i) { 189 new_changes.push_back( 190 syncer::SyncChange(FROM_HERE, 191 i->second.first, 192 CreateSyncData(*(i->second.second)))); 193 } 194 195 if (cull_expired_entries_) { 196 // This will schedule a deletion operation on the DB thread, which will 197 // trigger a notification to propagate the deletion to Sync. 198 webdata_backend_->RemoveExpiredFormElements(); 199 } 200 201 merge_result.set_error( 202 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes)); 203 return merge_result; 204 } 205 206 void AutocompleteSyncableService::StopSyncing(syncer::ModelType type) { 207 DCHECK(CalledOnValidThread()); 208 DCHECK_EQ(syncer::AUTOFILL, type); 209 210 sync_processor_.reset(NULL); 211 error_handler_.reset(); 212 } 213 214 syncer::SyncDataList AutocompleteSyncableService::GetAllSyncData( 215 syncer::ModelType type) const { 216 DCHECK(CalledOnValidThread()); 217 DCHECK(sync_processor_.get()); 218 DCHECK_EQ(type, syncer::AUTOFILL); 219 220 syncer::SyncDataList current_data; 221 222 std::vector<AutofillEntry> entries; 223 if (!LoadAutofillData(&entries)) 224 return current_data; 225 226 for (std::vector<AutofillEntry>::iterator it = entries.begin(); 227 it != entries.end(); ++it) { 228 current_data.push_back(CreateSyncData(*it)); 229 } 230 231 return current_data; 232 } 233 234 syncer::SyncError AutocompleteSyncableService::ProcessSyncChanges( 235 const tracked_objects::Location& from_here, 236 const syncer::SyncChangeList& change_list) { 237 DCHECK(CalledOnValidThread()); 238 DCHECK(sync_processor_.get()); 239 240 if (!sync_processor_.get()) { 241 syncer::SyncError error(FROM_HERE, 242 syncer::SyncError::DATATYPE_ERROR, 243 "Models not yet associated.", 244 syncer::AUTOFILL); 245 return error; 246 } 247 248 // Data is loaded only if we get new ADD/UPDATE change. 249 std::vector<AutofillEntry> entries; 250 scoped_ptr<AutocompleteEntryMap> db_entries; 251 std::vector<AutofillEntry> new_entries; 252 253 syncer::SyncError list_processing_error; 254 255 for (syncer::SyncChangeList::const_iterator i = change_list.begin(); 256 i != change_list.end() && !list_processing_error.IsSet(); ++i) { 257 DCHECK(i->IsValid()); 258 switch (i->change_type()) { 259 case syncer::SyncChange::ACTION_ADD: 260 case syncer::SyncChange::ACTION_UPDATE: 261 if (!db_entries.get()) { 262 if (!LoadAutofillData(&entries)) { 263 return error_handler_->CreateAndUploadError( 264 FROM_HERE, 265 "Could not get the autocomplete data from WebDatabase."); 266 } 267 db_entries.reset(new AutocompleteEntryMap); 268 for (std::vector<AutofillEntry>::iterator it = entries.begin(); 269 it != entries.end(); ++it) { 270 (*db_entries)[it->key()] = 271 std::make_pair(syncer::SyncChange::ACTION_ADD, it); 272 } 273 } 274 CreateOrUpdateEntry(i->sync_data(), db_entries.get(), &new_entries); 275 break; 276 case syncer::SyncChange::ACTION_DELETE: { 277 DCHECK(i->sync_data().GetSpecifics().has_autofill()) 278 << "Autofill specifics data not present on delete!"; 279 const sync_pb::AutofillSpecifics& autofill = 280 i->sync_data().GetSpecifics().autofill(); 281 if (autofill.has_value()) { 282 list_processing_error = AutofillEntryDelete(autofill); 283 } else { 284 DLOG(WARNING) 285 << "Delete for old-style autofill profile being dropped!"; 286 } 287 } break; 288 default: 289 NOTREACHED() << "Unexpected sync change state."; 290 return error_handler_->CreateAndUploadError( 291 FROM_HERE, 292 "ProcessSyncChanges failed on ChangeType " + 293 syncer::SyncChange::ChangeTypeToString(i->change_type())); 294 } 295 } 296 297 if (!SaveChangesToWebData(new_entries)) { 298 return error_handler_->CreateAndUploadError( 299 FROM_HERE, 300 "Failed to update webdata."); 301 } 302 303 webdata_backend_->NotifyOfMultipleAutofillChanges(); 304 305 if (cull_expired_entries_) { 306 // This will schedule a deletion operation on the DB thread, which will 307 // trigger a notification to propagate the deletion to Sync. 308 webdata_backend_->RemoveExpiredFormElements(); 309 } 310 311 return list_processing_error; 312 } 313 314 void AutocompleteSyncableService::AutofillEntriesChanged( 315 const AutofillChangeList& changes) { 316 // Check if sync is on. If we recieve this notification prior to sync being 317 // started, we'll notify sync to start as soon as it can and later process 318 // all entries when MergeData..() is called. If we receive this notification 319 // sync has exited, it will be synced next time Chrome starts. 320 if (sync_processor_.get()) { 321 ActOnChanges(changes); 322 } else if (!flare_.is_null()) { 323 flare_.Run(syncer::AUTOFILL); 324 flare_.Reset(); 325 } 326 } 327 328 bool AutocompleteSyncableService::LoadAutofillData( 329 std::vector<AutofillEntry>* entries) const { 330 return AutofillTable::FromWebDatabase( 331 webdata_backend_->GetDatabase())->GetAllAutofillEntries(entries); 332 } 333 334 bool AutocompleteSyncableService::SaveChangesToWebData( 335 const std::vector<AutofillEntry>& new_entries) { 336 DCHECK(CalledOnValidThread()); 337 338 if (!new_entries.empty() && 339 !AutofillTable::FromWebDatabase( 340 webdata_backend_->GetDatabase())->UpdateAutofillEntries( 341 new_entries)) { 342 return false; 343 } 344 return true; 345 } 346 347 // Creates or updates an autocomplete entry based on |data|. 348 void AutocompleteSyncableService::CreateOrUpdateEntry( 349 const syncer::SyncData& data, 350 AutocompleteEntryMap* loaded_data, 351 std::vector<AutofillEntry>* new_entries) { 352 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); 353 const sync_pb::AutofillSpecifics& autofill_specifics( 354 specifics.autofill()); 355 356 if (!autofill_specifics.has_value()) { 357 DLOG(WARNING) 358 << "Add/Update for old-style autofill profile being dropped!"; 359 return; 360 } 361 362 AutofillKey key(autofill_specifics.name().c_str(), 363 autofill_specifics.value().c_str()); 364 AutocompleteEntryMap::iterator it = loaded_data->find(key); 365 if (it == loaded_data->end()) { 366 // New entry. 367 std::vector<base::Time> timestamps; 368 size_t timestamps_count = autofill_specifics.usage_timestamp_size(); 369 timestamps.reserve(2); 370 if (timestamps_count) { 371 timestamps.push_back(base::Time::FromInternalValue( 372 autofill_specifics.usage_timestamp(0))); 373 } 374 if (timestamps_count > 1) { 375 timestamps.push_back(base::Time::FromInternalValue( 376 autofill_specifics.usage_timestamp(timestamps_count - 1))); 377 } 378 AutofillEntry new_entry(key, timestamps); 379 new_entries->push_back(new_entry); 380 } else { 381 // Entry already present - merge if necessary. 382 std::vector<base::Time> timestamps; 383 bool different = MergeTimestamps( 384 autofill_specifics, it->second.second->timestamps(), ×tamps); 385 if (different) { 386 AutofillEntry new_entry(it->second.second->key(), timestamps); 387 new_entries->push_back(new_entry); 388 // Update the sync db if the list of timestamps have changed. 389 *(it->second.second) = new_entry; 390 it->second.first = syncer::SyncChange::ACTION_UPDATE; 391 } else { 392 loaded_data->erase(it); 393 } 394 } 395 } 396 397 // static 398 void AutocompleteSyncableService::WriteAutofillEntry( 399 const AutofillEntry& entry, sync_pb::EntitySpecifics* autofill_specifics) { 400 sync_pb::AutofillSpecifics* autofill = 401 autofill_specifics->mutable_autofill(); 402 autofill->set_name(UTF16ToUTF8(entry.key().name())); 403 autofill->set_value(UTF16ToUTF8(entry.key().value())); 404 const std::vector<base::Time>& ts(entry.timestamps()); 405 for (std::vector<base::Time>::const_iterator timestamp = ts.begin(); 406 timestamp != ts.end(); ++timestamp) { 407 autofill->add_usage_timestamp(timestamp->ToInternalValue()); 408 } 409 } 410 411 syncer::SyncError AutocompleteSyncableService::AutofillEntryDelete( 412 const sync_pb::AutofillSpecifics& autofill) { 413 if (!AutofillTable::FromWebDatabase( 414 webdata_backend_->GetDatabase())->RemoveFormElement( 415 UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()))) { 416 return error_handler_->CreateAndUploadError( 417 FROM_HERE, 418 "Could not remove autocomplete entry from WebDatabase."); 419 } 420 return syncer::SyncError(); 421 } 422 423 void AutocompleteSyncableService::ActOnChanges( 424 const AutofillChangeList& changes) { 425 DCHECK(sync_processor_.get()); 426 syncer::SyncChangeList new_changes; 427 for (AutofillChangeList::const_iterator change = changes.begin(); 428 change != changes.end(); ++change) { 429 switch (change->type()) { 430 case AutofillChange::ADD: 431 case AutofillChange::UPDATE: { 432 std::vector<base::Time> timestamps; 433 WebDatabase* db = webdata_backend_->GetDatabase(); 434 if (!AutofillTable::FromWebDatabase(db)->GetAutofillTimestamps( 435 change->key().name(), 436 change->key().value(), 437 ×tamps)) { 438 NOTREACHED(); 439 return; 440 } 441 AutofillEntry entry(change->key(), timestamps); 442 syncer::SyncChange::SyncChangeType change_type = 443 (change->type() == AutofillChange::ADD) ? 444 syncer::SyncChange::ACTION_ADD : 445 syncer::SyncChange::ACTION_UPDATE; 446 new_changes.push_back(syncer::SyncChange(FROM_HERE, 447 change_type, 448 CreateSyncData(entry))); 449 break; 450 } 451 case AutofillChange::REMOVE: { 452 std::vector<base::Time> timestamps; 453 AutofillEntry entry(change->key(), timestamps); 454 new_changes.push_back( 455 syncer::SyncChange(FROM_HERE, 456 syncer::SyncChange::ACTION_DELETE, 457 CreateSyncData(entry))); 458 break; 459 } 460 default: 461 NOTREACHED(); 462 break; 463 } 464 } 465 syncer::SyncError error = 466 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); 467 if (error.IsSet()) { 468 DLOG(WARNING) << "[AUTOCOMPLETE SYNC]" 469 << " Failed processing change:" 470 << " Error:" << error.message(); 471 } 472 } 473 474 void AutocompleteSyncableService::UpdateCullSetting( 475 bool cull_expired_entries) { 476 DCHECK(CalledOnValidThread()); 477 cull_expired_entries_ = cull_expired_entries; 478 } 479 480 syncer::SyncData AutocompleteSyncableService::CreateSyncData( 481 const AutofillEntry& entry) const { 482 sync_pb::EntitySpecifics autofill_specifics; 483 WriteAutofillEntry(entry, &autofill_specifics); 484 std::string tag(KeyToTag(UTF16ToUTF8(entry.key().name()), 485 UTF16ToUTF8(entry.key().value()))); 486 return syncer::SyncData::CreateLocalData(tag, tag, autofill_specifics); 487 } 488 489 // static 490 std::string AutocompleteSyncableService::KeyToTag(const std::string& name, 491 const std::string& value) { 492 std::string ns(kAutofillEntryNamespaceTag); 493 return ns + net::EscapePath(name) + "|" + net::EscapePath(value); 494 } 495