autofill_model_associator.cc revision 513209b27ff55e2841eac0e4120199c23acce758
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_model_associator.h" 6 7#include <vector> 8 9#include "base/task.h" 10#include "base/time.h" 11#include "base/string_number_conversions.h" 12#include "base/utf_string_conversions.h" 13#include "chrome/browser/autofill/autofill_profile.h" 14#include "chrome/browser/browser_thread.h" 15#include "chrome/browser/profile.h" 16#include "chrome/browser/sync/engine/syncapi.h" 17#include "chrome/browser/sync/glue/autofill_change_processor.h" 18#include "chrome/browser/sync/profile_sync_service.h" 19#include "chrome/browser/sync/protocol/autofill_specifics.pb.h" 20#include "chrome/browser/webdata/web_database.h" 21#include "net/base/escape.h" 22 23using base::TimeTicks; 24 25namespace browser_sync { 26 27const char kAutofillTag[] = "google_chrome_autofill"; 28const char kAutofillEntryNamespaceTag[] = "autofill_entry|"; 29const char kAutofillProfileNamespaceTag[] = "autofill_profile|"; 30 31static const int kMaxNumAttemptsToFindUniqueLabel = 100; 32 33struct AutofillModelAssociator::DataBundle { 34 std::set<AutofillKey> current_entries; 35 std::vector<AutofillEntry> new_entries; 36 std::set<string16> current_profiles; 37 std::vector<AutoFillProfile*> updated_profiles; 38 std::vector<AutoFillProfile*> new_profiles; // We own these pointers. 39 ~DataBundle() { STLDeleteElements(&new_profiles); } 40}; 41 42AutofillModelAssociator::DoOptimisticRefreshTask::DoOptimisticRefreshTask( 43 PersonalDataManager* pdm) : pdm_(pdm) {} 44 45AutofillModelAssociator::DoOptimisticRefreshTask::~DoOptimisticRefreshTask() {} 46 47void AutofillModelAssociator::DoOptimisticRefreshTask::Run() { 48 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 49 pdm_->Refresh(); 50} 51 52AutofillModelAssociator::AutofillModelAssociator( 53 ProfileSyncService* sync_service, 54 WebDatabase* web_database, 55 PersonalDataManager* personal_data) 56 : sync_service_(sync_service), 57 web_database_(web_database), 58 personal_data_(personal_data), 59 autofill_node_id_(sync_api::kInvalidId), 60 abort_association_pending_(false) { 61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 62 DCHECK(sync_service_); 63 DCHECK(web_database_); 64 DCHECK(personal_data_); 65} 66 67AutofillModelAssociator::~AutofillModelAssociator() { 68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 69} 70 71bool AutofillModelAssociator::TraverseAndAssociateChromeAutofillEntries( 72 sync_api::WriteTransaction* write_trans, 73 const sync_api::ReadNode& autofill_root, 74 const std::vector<AutofillEntry>& all_entries_from_db, 75 std::set<AutofillKey>* current_entries, 76 std::vector<AutofillEntry>* new_entries) { 77 78 const std::vector<AutofillEntry>& entries = all_entries_from_db; 79 for (std::vector<AutofillEntry>::const_iterator ix = entries.begin(); 80 ix != entries.end(); ++ix) { 81 std::string tag = KeyToTag(ix->key().name(), ix->key().value()); 82 if (id_map_.find(tag) != id_map_.end()) { 83 // It seems that name/value pairs are not unique in the web database. 84 // As a result, we have to filter out duplicates here. This is probably 85 // a bug in the database. 86 continue; 87 } 88 89 sync_api::ReadNode node(write_trans); 90 if (node.InitByClientTagLookup(syncable::AUTOFILL, tag)) { 91 const sync_pb::AutofillSpecifics& autofill(node.GetAutofillSpecifics()); 92 DCHECK_EQ(tag, KeyToTag(UTF8ToUTF16(autofill.name()), 93 UTF8ToUTF16(autofill.value()))); 94 95 std::vector<base::Time> timestamps; 96 if (MergeTimestamps(autofill, ix->timestamps(), ×tamps)) { 97 AutofillEntry new_entry(ix->key(), timestamps); 98 new_entries->push_back(new_entry); 99 100 sync_api::WriteNode write_node(write_trans); 101 if (!write_node.InitByClientTagLookup(syncable::AUTOFILL, tag)) { 102 LOG(ERROR) << "Failed to write autofill sync node."; 103 return false; 104 } 105 AutofillChangeProcessor::WriteAutofillEntry(new_entry, &write_node); 106 } 107 108 Associate(&tag, node.GetId()); 109 } else { 110 sync_api::WriteNode node(write_trans); 111 if (!node.InitUniqueByCreation(syncable::AUTOFILL, 112 autofill_root, tag)) { 113 LOG(ERROR) << "Failed to create autofill sync node."; 114 return false; 115 } 116 node.SetTitle(UTF8ToWide(tag)); 117 AutofillChangeProcessor::WriteAutofillEntry(*ix, &node); 118 Associate(&tag, node.GetId()); 119 } 120 121 current_entries->insert(ix->key()); 122 } 123 return true; 124} 125 126bool AutofillModelAssociator::TraverseAndAssociateChromeAutoFillProfiles( 127 sync_api::WriteTransaction* write_trans, 128 const sync_api::ReadNode& autofill_root, 129 const std::vector<AutoFillProfile*>& all_profiles_from_db, 130 std::set<string16>* current_profiles, 131 std::vector<AutoFillProfile*>* updated_profiles) { 132 const std::vector<AutoFillProfile*>& profiles = all_profiles_from_db; 133 for (std::vector<AutoFillProfile*>::const_iterator ix = profiles.begin(); 134 ix != profiles.end(); ++ix) { 135 string16 label((*ix)->Label()); 136 std::string tag(ProfileLabelToTag(label)); 137 138 sync_api::ReadNode node(write_trans); 139 if (node.InitByClientTagLookup(syncable::AUTOFILL, tag)) { 140 const sync_pb::AutofillSpecifics& autofill(node.GetAutofillSpecifics()); 141 DCHECK(autofill.has_profile()); 142 DCHECK_EQ(ProfileLabelToTag(UTF8ToUTF16(autofill.profile().label())), 143 tag); 144 int64 sync_id = node.GetId(); 145 if (id_map_.find(tag) != id_map_.end()) { 146 // We just looked up something we already associated. Move aside. 147 label = MakeUniqueLabel(label, string16(), write_trans); 148 if (label.empty()) { 149 return false; 150 } 151 tag = ProfileLabelToTag(label); 152 // TODO(dhollowa): Replace with |AutoFillProfile::set_guid|. 153 // http://crbug.com/58813 154 (*ix)->set_label(label); 155 if (!MakeNewAutofillProfileSyncNode(write_trans, autofill_root, 156 tag, **ix, &sync_id)) { 157 return false; 158 } 159 updated_profiles->push_back(*ix); 160 } else { 161 // Overwrite local with cloud state. 162 if (OverwriteProfileWithServerData(*ix, autofill.profile())) 163 updated_profiles->push_back(*ix); 164 sync_id = node.GetId(); 165 } 166 167 Associate(&tag, sync_id); 168 } else { 169 int64 id; 170 if (!MakeNewAutofillProfileSyncNode(write_trans, autofill_root, 171 tag, **ix, &id)) { 172 return false; 173 } 174 Associate(&tag, id); 175 } 176 current_profiles->insert(label); 177 } 178 return true; 179} 180 181// static 182string16 AutofillModelAssociator::MakeUniqueLabel( 183 const string16& non_unique_label, 184 const string16& existing_unique_label, 185 sync_api::BaseTransaction* trans) { 186 if (!non_unique_label.empty() && non_unique_label == existing_unique_label) { 187 return existing_unique_label; 188 } 189 int unique_id = 1; // Priming so we start by appending "2". 190 while (unique_id++ < kMaxNumAttemptsToFindUniqueLabel) { 191 string16 suffix(base::IntToString16(unique_id)); 192 string16 unique_label = non_unique_label + suffix; 193 if (unique_label == existing_unique_label) 194 return unique_label; // We'll use the one we already have. 195 sync_api::ReadNode node(trans); 196 if (node.InitByClientTagLookup(syncable::AUTOFILL, 197 ProfileLabelToTag(unique_label))) { 198 continue; 199 } 200 return unique_label; 201 } 202 203 LOG(ERROR) << "Couldn't create unique tag for autofill node. Srsly?!"; 204 return string16(); 205} 206 207bool AutofillModelAssociator::MakeNewAutofillProfileSyncNode( 208 sync_api::WriteTransaction* trans, const sync_api::BaseNode& autofill_root, 209 const std::string& tag, const AutoFillProfile& profile, int64* sync_id) { 210 sync_api::WriteNode node(trans); 211 if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag)) { 212 LOG(ERROR) << "Failed to create autofill sync node."; 213 return false; 214 } 215 node.SetTitle(UTF8ToWide(tag)); 216 AutofillChangeProcessor::WriteAutofillProfile(profile, &node); 217 *sync_id = node.GetId(); 218 return true; 219} 220 221 222bool AutofillModelAssociator::LoadAutofillData( 223 std::vector<AutofillEntry>* entries, 224 std::vector<AutoFillProfile*>* profiles) { 225 if (IsAbortPending()) 226 return false; 227 if (!web_database_->GetAllAutofillEntries(entries)) 228 return false; 229 230 if (IsAbortPending()) 231 return false; 232 if (!web_database_->GetAutoFillProfiles(profiles)) 233 return false; 234 235 return true; 236} 237 238bool AutofillModelAssociator::AssociateModels() { 239 VLOG(1) << "Associating Autofill Models"; 240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 241 { 242 AutoLock lock(abort_association_pending_lock_); 243 abort_association_pending_ = false; 244 } 245 246 // TODO(zork): Attempt to load the model association from storage. 247 std::vector<AutofillEntry> entries; 248 ScopedVector<AutoFillProfile> profiles; 249 250 if (!LoadAutofillData(&entries, &profiles.get())) { 251 LOG(ERROR) << "Could not get the autofill data from WebDatabase."; 252 return false; 253 } 254 255 DataBundle bundle; 256 { 257 sync_api::WriteTransaction trans( 258 sync_service_->backend()->GetUserShareHandle()); 259 260 sync_api::ReadNode autofill_root(&trans); 261 if (!autofill_root.InitByTagLookup(kAutofillTag)) { 262 LOG(ERROR) << "Server did not create the top-level autofill node. We " 263 << "might be running against an out-of-date server."; 264 return false; 265 } 266 267 if (!TraverseAndAssociateChromeAutofillEntries(&trans, autofill_root, 268 entries, &bundle.current_entries, &bundle.new_entries) || 269 !TraverseAndAssociateChromeAutoFillProfiles(&trans, autofill_root, 270 profiles.get(), &bundle.current_profiles, 271 &bundle.updated_profiles) || 272 !TraverseAndAssociateAllSyncNodes(&trans, autofill_root, &bundle)) { 273 return false; 274 } 275 } 276 277 // Since we're on the DB thread, we don't have to worry about updating 278 // the autofill database after closing the write transaction, since 279 // this is the only thread that writes to the database. We also don't have 280 // to worry about the sync model getting out of sync, because changes are 281 // propogated to the ChangeProcessor on this thread. 282 if (!SaveChangesToWebData(bundle)) { 283 LOG(ERROR) << "Failed to update autofill entries."; 284 return false; 285 } 286 287 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 288 new DoOptimisticRefreshTask(personal_data_)); 289 return true; 290} 291 292bool AutofillModelAssociator::SaveChangesToWebData(const DataBundle& bundle) { 293 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 294 295 if (IsAbortPending()) 296 return false; 297 298 if (bundle.new_entries.size() && 299 !web_database_->UpdateAutofillEntries(bundle.new_entries)) { 300 return false; 301 } 302 303 for (size_t i = 0; i < bundle.new_profiles.size(); i++) { 304 if (IsAbortPending()) 305 return false; 306 if (!web_database_->AddAutoFillProfile(*bundle.new_profiles[i])) 307 return false; 308 } 309 310 for (size_t i = 0; i < bundle.updated_profiles.size(); i++) { 311 if (IsAbortPending()) 312 return false; 313 if (!web_database_->UpdateAutoFillProfile(*bundle.updated_profiles[i])) 314 return false; 315 } 316 return true; 317} 318 319bool AutofillModelAssociator::TraverseAndAssociateAllSyncNodes( 320 sync_api::WriteTransaction* write_trans, 321 const sync_api::ReadNode& autofill_root, 322 DataBundle* bundle) { 323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 324 325 int64 sync_child_id = autofill_root.GetFirstChildId(); 326 while (sync_child_id != sync_api::kInvalidId) { 327 sync_api::ReadNode sync_child(write_trans); 328 if (!sync_child.InitByIdLookup(sync_child_id)) { 329 LOG(ERROR) << "Failed to fetch child node."; 330 return false; 331 } 332 const sync_pb::AutofillSpecifics& autofill( 333 sync_child.GetAutofillSpecifics()); 334 335 if (autofill.has_value()) 336 AddNativeEntryIfNeeded(autofill, bundle, sync_child); 337 else if (autofill.has_profile()) 338 AddNativeProfileIfNeeded(autofill.profile(), bundle, sync_child); 339 else 340 NOTREACHED() << "AutofillSpecifics has no autofill data!"; 341 342 sync_child_id = sync_child.GetSuccessorId(); 343 } 344 return true; 345} 346 347void AutofillModelAssociator::AddNativeEntryIfNeeded( 348 const sync_pb::AutofillSpecifics& autofill, DataBundle* bundle, 349 const sync_api::ReadNode& node) { 350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 351 AutofillKey key(UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value())); 352 353 if (bundle->current_entries.find(key) == bundle->current_entries.end()) { 354 std::vector<base::Time> timestamps; 355 int timestamps_count = autofill.usage_timestamp_size(); 356 for (int c = 0; c < timestamps_count; ++c) { 357 timestamps.push_back(base::Time::FromInternalValue( 358 autofill.usage_timestamp(c))); 359 } 360 std::string tag(KeyToTag(key.name(), key.value())); 361 Associate(&tag, node.GetId()); 362 bundle->new_entries.push_back(AutofillEntry(key, timestamps)); 363 } 364} 365 366void AutofillModelAssociator::AddNativeProfileIfNeeded( 367 const sync_pb::AutofillProfileSpecifics& profile, DataBundle* bundle, 368 const sync_api::ReadNode& node) { 369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 370 if (bundle->current_profiles.find(UTF8ToUTF16(profile.label())) == 371 bundle->current_profiles.end()) { 372 std::string tag(ProfileLabelToTag(UTF8ToUTF16(profile.label()))); 373 Associate(&tag, node.GetId()); 374 AutoFillProfile* p = personal_data_-> 375 CreateNewEmptyAutoFillProfileForDBThread(UTF8ToUTF16(profile.label())); 376 OverwriteProfileWithServerData(p, profile); 377 bundle->new_profiles.push_back(p); 378 } 379} 380 381bool AutofillModelAssociator::DisassociateModels() { 382 id_map_.clear(); 383 id_map_inverse_.clear(); 384 return true; 385} 386 387bool AutofillModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) { 388 DCHECK(has_nodes); 389 *has_nodes = false; 390 int64 autofill_sync_id; 391 if (!GetSyncIdForTaggedNode(kAutofillTag, &autofill_sync_id)) { 392 LOG(ERROR) << "Server did not create the top-level autofill node. We " 393 << "might be running against an out-of-date server."; 394 return false; 395 } 396 sync_api::ReadTransaction trans( 397 sync_service_->backend()->GetUserShareHandle()); 398 399 sync_api::ReadNode autofill_node(&trans); 400 if (!autofill_node.InitByIdLookup(autofill_sync_id)) { 401 LOG(ERROR) << "Server did not create the top-level autofill node. We " 402 << "might be running against an out-of-date server."; 403 return false; 404 } 405 406 // The sync model has user created nodes if the autofill folder has any 407 // children. 408 *has_nodes = sync_api::kInvalidId != autofill_node.GetFirstChildId(); 409 return true; 410} 411 412void AutofillModelAssociator::AbortAssociation() { 413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 414 AutoLock lock(abort_association_pending_lock_); 415 abort_association_pending_ = true; 416} 417 418const std::string* 419AutofillModelAssociator::GetChromeNodeFromSyncId(int64 sync_id) { 420 return NULL; 421} 422 423bool AutofillModelAssociator::InitSyncNodeFromChromeId( 424 std::string node_id, 425 sync_api::BaseNode* sync_node) { 426 return false; 427} 428 429int64 AutofillModelAssociator::GetSyncIdFromChromeId( 430 const std::string autofill) { 431 AutofillToSyncIdMap::const_iterator iter = id_map_.find(autofill); 432 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; 433} 434 435void AutofillModelAssociator::Associate( 436 const std::string* autofill, int64 sync_id) { 437 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 438 DCHECK_NE(sync_api::kInvalidId, sync_id); 439 DCHECK(id_map_.find(*autofill) == id_map_.end()); 440 DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end()); 441 id_map_[*autofill] = sync_id; 442 id_map_inverse_[sync_id] = *autofill; 443} 444 445void AutofillModelAssociator::Disassociate(int64 sync_id) { 446 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 447 SyncIdToAutofillMap::iterator iter = id_map_inverse_.find(sync_id); 448 if (iter == id_map_inverse_.end()) 449 return; 450 CHECK(id_map_.erase(iter->second)); 451 id_map_inverse_.erase(iter); 452} 453 454bool AutofillModelAssociator::GetSyncIdForTaggedNode(const std::string& tag, 455 int64* sync_id) { 456 sync_api::ReadTransaction trans( 457 sync_service_->backend()->GetUserShareHandle()); 458 sync_api::ReadNode sync_node(&trans); 459 if (!sync_node.InitByTagLookup(tag.c_str())) 460 return false; 461 *sync_id = sync_node.GetId(); 462 return true; 463} 464 465bool AutofillModelAssociator::IsAbortPending() { 466 AutoLock lock(abort_association_pending_lock_); 467 return abort_association_pending_; 468} 469 470// static 471std::string AutofillModelAssociator::KeyToTag(const string16& name, 472 const string16& value) { 473 std::string ns(kAutofillEntryNamespaceTag); 474 return ns + EscapePath(UTF16ToUTF8(name)) + "|" + 475 EscapePath(UTF16ToUTF8(value)); 476} 477 478// static 479std::string AutofillModelAssociator::ProfileLabelToTag(const string16& label) { 480 std::string ns(kAutofillProfileNamespaceTag); 481 return ns + EscapePath(UTF16ToUTF8(label)); 482} 483 484// static 485bool AutofillModelAssociator::MergeTimestamps( 486 const sync_pb::AutofillSpecifics& autofill, 487 const std::vector<base::Time>& timestamps, 488 std::vector<base::Time>* new_timestamps) { 489 DCHECK(new_timestamps); 490 std::set<base::Time> timestamp_union(timestamps.begin(), 491 timestamps.end()); 492 493 size_t timestamps_count = autofill.usage_timestamp_size(); 494 495 bool different = timestamps.size() != timestamps_count; 496 for (size_t c = 0; c < timestamps_count; ++c) { 497 if (timestamp_union.insert(base::Time::FromInternalValue( 498 autofill.usage_timestamp(c))).second) { 499 different = true; 500 } 501 } 502 503 if (different) { 504 new_timestamps->insert(new_timestamps->begin(), 505 timestamp_union.begin(), 506 timestamp_union.end()); 507 } 508 return different; 509} 510 511// Helper to compare the local value and cloud value of a field, merge into 512// the local value if they differ, and return whether the merge happened. 513bool MergeField(FormGroup* f, AutoFillFieldType t, 514 const std::string& specifics_field) { 515 if (UTF16ToUTF8(f->GetFieldText(AutoFillType(t))) == specifics_field) 516 return false; 517 f->SetInfo(AutoFillType(t), UTF8ToUTF16(specifics_field)); 518 return true; 519} 520 521// static 522bool AutofillModelAssociator::OverwriteProfileWithServerData( 523 AutoFillProfile* merge_into, 524 const sync_pb::AutofillProfileSpecifics& specifics) { 525 bool diff = false; 526 AutoFillProfile* p = merge_into; 527 const sync_pb::AutofillProfileSpecifics& s(specifics); 528 diff = MergeField(p, NAME_FIRST, s.name_first()) || diff; 529 diff = MergeField(p, NAME_LAST, s.name_last()) || diff; 530 diff = MergeField(p, NAME_MIDDLE, s.name_middle()) || diff; 531 diff = MergeField(p, ADDRESS_HOME_LINE1, s.address_home_line1()) || diff; 532 diff = MergeField(p, ADDRESS_HOME_LINE2, s.address_home_line2()) || diff; 533 diff = MergeField(p, ADDRESS_HOME_CITY, s.address_home_city()) || diff; 534 diff = MergeField(p, ADDRESS_HOME_STATE, s.address_home_state()) || diff; 535 diff = MergeField(p, ADDRESS_HOME_COUNTRY, s.address_home_country()) || diff; 536 diff = MergeField(p, ADDRESS_HOME_ZIP, s.address_home_zip()) || diff; 537 diff = MergeField(p, EMAIL_ADDRESS, s.email_address()) || diff; 538 diff = MergeField(p, COMPANY_NAME, s.company_name()) || diff; 539 diff = MergeField(p, PHONE_FAX_WHOLE_NUMBER, s.phone_fax_whole_number()) 540 || diff; 541 diff = MergeField(p, PHONE_HOME_WHOLE_NUMBER, s.phone_home_whole_number()) 542 || diff; 543 return diff; 544} 545 546} // namespace browser_sync 547