OLD | NEW |
(Empty) | |
| 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_associator2.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 |
| 23 using base::TimeTicks; |
| 24 |
| 25 namespace browser_sync { |
| 26 |
| 27 extern const char kAutofillTag[]; |
| 28 extern const char kAutofillEntryNamespaceTag[]; |
| 29 const char kAutofillProfileNamespaceTag[] = "autofill_profile|"; |
| 30 |
| 31 static const int kMaxNumAttemptsToFindUniqueLabel = 100; |
| 32 |
| 33 struct AutofillModelAssociator2::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 |
| 42 AutofillModelAssociator2::DoOptimisticRefreshTask::DoOptimisticRefreshTask( |
| 43 PersonalDataManager* pdm) : pdm_(pdm) {} |
| 44 |
| 45 AutofillModelAssociator2::DoOptimisticRefreshTask::~DoOptimisticRefreshTask() {} |
| 46 |
| 47 void AutofillModelAssociator2::DoOptimisticRefreshTask::Run() { |
| 48 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 49 pdm_->Refresh(); |
| 50 } |
| 51 |
| 52 AutofillModelAssociator2::AutofillModelAssociator2( |
| 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 |
| 67 AutofillModelAssociator2::~AutofillModelAssociator2() { |
| 68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| 69 } |
| 70 |
| 71 bool AutofillModelAssociator2::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 |
| 126 bool AutofillModelAssociator2::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 |
| 182 string16 AutofillModelAssociator2::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 |
| 207 bool AutofillModelAssociator2::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 |
| 222 bool AutofillModelAssociator2::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 |
| 238 bool AutofillModelAssociator2::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 |
| 292 bool AutofillModelAssociator2::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 |
| 319 bool AutofillModelAssociator2::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 |
| 347 void AutofillModelAssociator2::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 |
| 366 void AutofillModelAssociator2::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 |
| 381 bool AutofillModelAssociator2::DisassociateModels() { |
| 382 id_map_.clear(); |
| 383 id_map_inverse_.clear(); |
| 384 return true; |
| 385 } |
| 386 |
| 387 bool AutofillModelAssociator2::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 |
| 412 void AutofillModelAssociator2::AbortAssociation() { |
| 413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 414 AutoLock lock(abort_association_pending_lock_); |
| 415 abort_association_pending_ = true; |
| 416 } |
| 417 |
| 418 const std::string* |
| 419 AutofillModelAssociator2::GetChromeNodeFromSyncId(int64 sync_id) { |
| 420 return NULL; |
| 421 } |
| 422 |
| 423 bool AutofillModelAssociator2::InitSyncNodeFromChromeId( |
| 424 std::string node_id, |
| 425 sync_api::BaseNode* sync_node) { |
| 426 return false; |
| 427 } |
| 428 |
| 429 int64 AutofillModelAssociator2::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 |
| 435 void AutofillModelAssociator2::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 |
| 445 void AutofillModelAssociator2::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 |
| 454 bool AutofillModelAssociator2::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 |
| 465 bool AutofillModelAssociator2::IsAbortPending() { |
| 466 AutoLock lock(abort_association_pending_lock_); |
| 467 return abort_association_pending_; |
| 468 } |
| 469 |
| 470 // static |
| 471 std::string AutofillModelAssociator2::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 |
| 479 std::string AutofillModelAssociator2::ProfileLabelToTag(const string16& label) { |
| 480 std::string ns(kAutofillProfileNamespaceTag); |
| 481 return ns + EscapePath(UTF16ToUTF8(label)); |
| 482 } |
| 483 |
| 484 // static |
| 485 bool AutofillModelAssociator2::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. |
| 513 bool MergeField2(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 |
| 522 bool AutofillModelAssociator2::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 = MergeField2(p, NAME_FIRST, s.name_first()) || diff; |
| 529 diff = MergeField2(p, NAME_LAST, s.name_last()) || diff; |
| 530 diff = MergeField2(p, NAME_MIDDLE, s.name_middle()) || diff; |
| 531 diff = MergeField2(p, ADDRESS_HOME_LINE1, s.address_home_line1()) || diff; |
| 532 diff = MergeField2(p, ADDRESS_HOME_LINE2, s.address_home_line2()) || diff; |
| 533 diff = MergeField2(p, ADDRESS_HOME_CITY, s.address_home_city()) || diff; |
| 534 diff = MergeField2(p, ADDRESS_HOME_STATE, s.address_home_state()) || diff; |
| 535 diff = MergeField2(p, ADDRESS_HOME_COUNTRY, s.address_home_country()) || diff; |
| 536 diff = MergeField2(p, ADDRESS_HOME_ZIP, s.address_home_zip()) || diff; |
| 537 diff = MergeField2(p, EMAIL_ADDRESS, s.email_address()) || diff; |
| 538 diff = MergeField2(p, COMPANY_NAME, s.company_name()) || diff; |
| 539 diff = MergeField2(p, PHONE_FAX_WHOLE_NUMBER, s.phone_fax_whole_number()) |
| 540 || diff; |
| 541 diff = MergeField2(p, PHONE_HOME_WHOLE_NUMBER, s.phone_home_whole_number()) |
| 542 || diff; |
| 543 return diff; |
| 544 } |
| 545 |
| 546 } // namespace browser_sync |
OLD | NEW |