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