Index: chrome/browser/sync/glue/autofill_profile_model_associator.cc |
diff --git a/chrome/browser/sync/glue/autofill_profile_model_associator.cc b/chrome/browser/sync/glue/autofill_profile_model_associator.cc |
new file mode 100755 |
index 0000000000000000000000000000000000000000..e50d25ad421f459525d08e2676e37b308277bf98 |
--- /dev/null |
+++ b/chrome/browser/sync/glue/autofill_profile_model_associator.cc |
@@ -0,0 +1,328 @@ |
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/sync/glue/autofill_profile_model_associator.h" |
+ |
+#include "base/utf_string_conversions.h" |
+#include "chrome/browser/sync/profile_sync_service.h" |
+#include "chrome/browser/webdata/web_database.h" |
+ |
+namespace browser_sync { |
+ |
+const char kAutofillProfileTag[] = "google_chrome_autofill_profile"; |
+ |
+AutofillProfileModelAssociator::AutofillProfileModelAssociator( |
+ ProfileSyncService* sync_service, |
+ WebDatabase* web_database, |
+ PersonalDataManager* personal_data) |
+ : sync_service_(sync_service), |
+ web_database_(web_database), |
+ personal_data_(personal_data), |
+ autofill_node_id_(sync_api::kInvalidId), |
+ abort_association_pending_(false), |
+ dontCheckThreadOnExit_(false) { |
+ DCHECK(EnsureOnThread(BrowserThread::DB)); |
+ DCHECK(sync_service_); |
+ DCHECK(web_database_); |
+ DCHECK(personal_data_); |
+} |
+ |
+AutofillProfileModelAssociator::~AutofillProfileModelAssociator() { |
+ DCHECK(EnsureOnThread(BrowserThread::DB)); |
+} |
+sync_api::ReadNode* AutofillProfileModelAssociator::GetReadNode( |
+ sync_api::WriteTransaction* trans) { |
+ return new sync_api::ReadNode(trans); |
+} |
+ |
+bool AutofillProfileModelAssociator::TraverseAndAssociateChromeAutoFillProfiles( |
+ sync_api::WriteTransaction* write_trans, |
+ const sync_api::ReadNode& autofill_root, |
+ const std::vector<AutoFillProfile*>& all_profiles_from_db, |
+ std::set<std::string>* current_profiles, |
+ std::vector<AutoFillProfile*>* updated_profiles) { |
+ scoped_ptr<sync_api::ReadNode> node(GetReadNode(write_trans)); |
+ |
+ // Alias the all_profiles_from_db so we fit in 80 characters |
+ const std::vector<AutoFillProfile*>& profiles(all_profiles_from_db); |
+ for (std::vector<AutoFillProfile*>::const_iterator ix = profiles.begin(); |
+ ix != profiles.end(); |
+ ++ix) { |
+ std::string guid((*ix)->guid()); |
+ |
+ node->Reset(); |
+ if (node->InitByClientTagLookup(syncable::AUTOFILL_PROFILE, guid)) { |
+ const sync_pb::AutofillProfile2Specifics& autofill( |
+ node->GetAutofillProfileSpecifics()); |
+ if (OverwriteProfileWithServerData(*ix, autofill)){ |
+ updated_profiles->push_back(*ix); |
+ } |
+ Associate(&guid, node->GetId()); |
+ } else { |
+ int64 id; |
+ if (!MakeNewAutofillProfileSyncNode(write_trans, |
+ autofill_root, |
+ (**ix), |
+ &id)) |
+ { |
+ LOG(ERROR) << "Failed making an autofill item on the sync database"; |
+ } |
+ Associate(&guid, id); |
+ } |
+ current_profiles->insert(guid); |
+ } |
+ |
+ return true; |
+} |
+ |
+bool AutofillProfileModelAssociator::LoadAutofillData( |
+ std::vector<AutoFillProfile*>* profiles) { |
+ if (IsAbortPending()) |
+ return false; |
+ |
+ if (!web_database_->GetAutoFillProfiles(profiles)) |
+ return false; |
+ |
+ return true; |
+} |
+ |
+bool AutofillProfileModelAssociator::EnsureOnThread(BrowserThread::ID id) { |
+ return dontCheckThreadOnExit_ || BrowserThread::CurrentlyOn(id); |
+} |
+ |
+bool AutofillProfileModelAssociator::AssociateModels() { |
+ VLOG(1) << "Associating Autofill Models"; |
+ DCHECK(EnsureOnThread(BrowserThread::DB)); |
+ { |
+ AutoLock lock(abort_association_pending_lock_); |
+ abort_association_pending_ = false; |
+ } |
+ |
+ // TODO(lipalani): Attempt to load the model association from storage. |
+ ScopedVector<AutoFillProfile> profiles; |
+ |
+ if (!LoadAutofillData(&profiles.get())) { |
+ LOG(ERROR) << "Could not get the autofill data from WebDatabase."; |
+ return false; |
+ } |
+ |
+ DataBundle bundle; |
+ { |
+ // The write transaction lock is held inside this block. |
+ // We do all the web db operations outside this block. |
+ sync_api::WriteTransaction trans( |
+ sync_service_->backend()->GetUserShareHandle()); |
+ |
+ sync_api::ReadNode autofill_root(&trans); |
+ if (!autofill_root.InitByTagLookup(kAutofillProfileTag)) { |
+ LOG(ERROR) << "Server did not create the top-level autofill node. We " |
+ << "might be running against an out-of-date server."; |
+ return false; |
+ } |
+ |
+ if (!TraverseAndAssociateChromeAutoFillProfiles(&trans, autofill_root, |
+ profiles.get(), &bundle.current_profiles, |
+ &bundle.updated_profiles) || |
+ !TraverseAndAssociateAllSyncNodes(&trans, autofill_root, &bundle)) { |
+ return false; |
+ } |
+ } |
+ |
+ if (!SaveChangesToWebData(bundle)) { |
+ LOG(ERROR) << "Failed to update autofill entries."; |
+ return false; |
+ } |
+ |
+ // [TODO] - split out the OptimisticRefreshTask into its own class |
+ // from autofill_model_associator |
+ // Will be done as part of the autofill_model_associator work. |
+ // BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
+ // new DoOptimisticRefreshTask(personal_data_)); |
+ return true; |
+} |
+ |
+bool AutofillProfileModelAssociator::DisassociateModels() { |
+ id_map_.clear(); |
+ id_map_inverse_.clear(); |
+ return true; |
+} |
+ |
+// Helper to compare the local value and cloud value of a field, merge into |
+// the local value if they differ, and return whether the merge happened. |
+bool AutofillProfileModelAssociator::MergeField(FormGroup* f, |
+ AutoFillFieldType t, |
+ const std::string& specifics_field) { |
+ if (UTF16ToUTF8(f->GetFieldText(AutoFillType(t))) == specifics_field) |
+ return false; |
+ f->SetInfo(AutoFillType(t), UTF8ToUTF16(specifics_field)); |
+ return true; |
+} |
+bool AutofillProfileModelAssociator::SyncModelHasUserCreatedNodes( |
+ bool *has_nodes) |
+{ |
+ CHECK(has_nodes != NULL); |
+ sync_api::ReadTransaction trans( |
+ sync_service_->backend()->GetUserShareHandle()); |
+ |
+ sync_api::ReadNode node(&trans); |
+ |
+ if (!node.InitByClientTagLookup( |
+ syncable::AUTOFILL_PROFILE, |
+ kAutofillProfileTag)) { |
+ LOG(ERROR) << "Sever did not create a top level node" |
+ << "Out of data server or autofill type not enabled"; |
+ return false; |
+ } |
+ |
+ *has_nodes = sync_api::kInvalidId != node.GetFirstChildId(); |
+ return true; |
+} |
+// static |
+bool AutofillProfileModelAssociator::OverwriteProfileWithServerData( |
+ AutoFillProfile* merge_into, |
+ const sync_pb::AutofillProfile2Specifics& specifics) { |
+ bool diff = false; |
+ AutoFillProfile* p = merge_into; |
+ const sync_pb::AutofillProfile2Specifics& s(specifics); |
+ diff = MergeField(p, NAME_FIRST, s.name_first()) || diff; |
+ diff = MergeField(p, NAME_LAST, s.name_last()) || diff; |
+ diff = MergeField(p, NAME_MIDDLE, s.name_middle()) || diff; |
+ diff = MergeField(p, ADDRESS_HOME_LINE1, s.address_home_line1()) || diff; |
+ diff = MergeField(p, ADDRESS_HOME_LINE2, s.address_home_line2()) || diff; |
+ diff = MergeField(p, ADDRESS_HOME_CITY, s.address_home_city()) || diff; |
+ diff = MergeField(p, ADDRESS_HOME_STATE, s.address_home_state()) || diff; |
+ diff = MergeField(p, ADDRESS_HOME_COUNTRY, s.address_home_country()) || diff; |
+ diff = MergeField(p, ADDRESS_HOME_ZIP, s.address_home_zip()) || diff; |
+ diff = MergeField(p, EMAIL_ADDRESS, s.email_address()) || diff; |
+ diff = MergeField(p, COMPANY_NAME, s.company_name()) || diff; |
+ diff = MergeField(p, PHONE_FAX_WHOLE_NUMBER, s.phone_fax_whole_number()) |
+ || diff; |
+ diff = MergeField(p, PHONE_HOME_WHOLE_NUMBER, s.phone_home_whole_number()) |
+ || diff; |
+ return diff; |
+} |
+ |
+bool AutofillProfileModelAssociator::MakeNewAutofillProfileSyncNode( |
+ sync_api::WriteTransaction* trans, |
+ const sync_api::BaseNode& autofill_root, |
+ const AutoFillProfile& profile, |
+ int64* sync_id) { |
+ sync_api::WriteNode node(trans); |
+ if (!node.InitUniqueByCreation( |
+ syncable::AUTOFILL_PROFILE, autofill_root, profile.guid())) { |
+ LOG(ERROR) << "Failed to create autofill sync node."; |
+ return false; |
+ } |
+ node.SetTitle(UTF8ToWide(profile.guid())); |
+ |
+ // [TODO] This needs rewriting. This will be tackled |
+ // when rewriting autofill change processor. |
+ // AutofillChangeProcessor::WriteAutofillProfile(profile, &node); |
+ *sync_id = node.GetId(); |
+ return true; |
+} |
+ |
+bool AutofillProfileModelAssociator::TraverseAndAssociateAllSyncNodes( |
+ sync_api::WriteTransaction* write_trans, |
+ const sync_api::ReadNode& autofill_root, |
+ DataBundle* bundle) { |
+ DCHECK(EnsureOnThread(BrowserThread::DB)); |
+ |
+ int64 sync_child_id = autofill_root.GetFirstChildId(); |
+ scoped_ptr<sync_api::ReadNode> sync_child(GetReadNode(write_trans)); |
+ while (sync_child_id != sync_api::kInvalidId) { |
+ sync_child->Reset(); |
+ if (!sync_child->InitByIdLookup(sync_child_id)) { |
+ LOG(ERROR) << "Failed to fetch child node."; |
+ return false; |
+ } |
+ const sync_pb::AutofillProfile2Specifics& autofill( |
+ sync_child->GetAutofillProfileSpecifics()); |
+ |
+ AddNativeProfileIfNeeded(autofill, bundle, *sync_child); |
+ |
+ sync_child_id = sync_child->GetSuccessorId(); |
+ } |
+ return true; |
+} |
+ |
+void AutofillProfileModelAssociator::AddNativeProfileIfNeeded( |
+ const sync_pb::AutofillProfile2Specifics& profile, |
+ DataBundle* bundle, |
+ const sync_api::ReadNode& node) { |
+ DCHECK(EnsureOnThread(BrowserThread::DB)); |
+ |
+ // [TODO] this looping through is costly. Replace this with a binary tree |
+ // at least in case of auto fill entries. |
+ if (bundle->current_profiles.find(profile.guid()) == |
+ bundle->current_profiles.end()) { |
+ std::string guid(profile.guid()); |
+ Associate(&guid, node.GetId()); |
+ AutoFillProfile* p = new AutoFillProfile(profile.guid()); |
+ OverwriteProfileWithServerData(p, profile); |
+ bundle->new_profiles.push_back(p); |
+ } |
+} |
+ |
+bool AutofillProfileModelAssociator::SaveChangesToWebData( |
+ const DataBundle& bundle) { |
+ DCHECK(EnsureOnThread(BrowserThread::DB)); |
+ |
+ if (IsAbortPending()) |
+ return false; |
+ |
+ for (size_t i = 0; i < bundle.new_profiles.size(); i++) { |
+ if (IsAbortPending()) |
+ return false; |
+ if (!web_database_->AddAutoFillProfile(*bundle.new_profiles[i])) |
+ return false; |
+ } |
+ |
+ for (size_t i = 0; i < bundle.updated_profiles.size(); i++) { |
+ if (IsAbortPending()) |
+ return false; |
+ if (!web_database_->UpdateAutoFillProfile(*bundle.updated_profiles[i])) |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+void AutofillProfileModelAssociator::Associate( |
+ const std::string* autofill, |
+ int64 sync_id) { |
+ DCHECK(EnsureOnThread(BrowserThread::DB)); |
+ DCHECK_NE(sync_api::kInvalidId, sync_id); |
+ DCHECK(id_map_.find(*autofill) == id_map_.end()); |
+ DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end()); |
+ id_map_[*autofill] = sync_id; |
+ id_map_inverse_[sync_id] = *autofill; |
+} |
+ |
+void AutofillProfileModelAssociator::Disassociate(int64 sync_id) { |
+ DCHECK(EnsureOnThread(BrowserThread::DB)); |
+ SyncIdToAutofillMap::iterator iter = id_map_inverse_.find(sync_id); |
+ if (iter == id_map_inverse_.end()) |
+ return; |
+ CHECK(id_map_.erase(iter->second)); |
+ id_map_inverse_.erase(iter); |
+} |
+ |
+int64 AutofillProfileModelAssociator::GetSyncIdFromChromeId( |
+ const std::string autofill) { |
+ AutofillToSyncIdMap::const_iterator iter = id_map_.find(autofill); |
+ return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; |
+} |
+ |
+void AutofillProfileModelAssociator::AbortAssociation() { |
+ DCHECK(EnsureOnThread(BrowserThread::UI)); |
+ AutoLock lock(abort_association_pending_lock_); |
+ abort_association_pending_ = true; |
+} |
+ |
+bool AutofillProfileModelAssociator::IsAbortPending() { |
+ AutoLock lock(abort_association_pending_lock_); |
+ return abort_association_pending_; |
+} |
+ |
+} // namespace browser_sync |