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