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_profile_model_associator.h" |
| 6 |
| 7 #include "base/utf_string_conversions.h" |
| 8 #include "chrome/browser/sync/profile_sync_service.h" |
| 9 #include "chrome/browser/webdata/web_database.h" |
| 10 |
| 11 namespace browser_sync { |
| 12 |
| 13 const char kAutofillProfileTag[] = "google_chrome_autofill_profile"; |
| 14 |
| 15 AutofillProfileModelAssociator::AutofillProfileModelAssociator( |
| 16 ProfileSyncService* sync_service, |
| 17 WebDatabase* web_database, |
| 18 PersonalDataManager* personal_data) |
| 19 : sync_service_(sync_service), |
| 20 web_database_(web_database), |
| 21 personal_data_(personal_data), |
| 22 autofill_node_id_(sync_api::kInvalidId), |
| 23 abort_association_pending_(false), |
| 24 dontCheckThreadOnExit_(false) { |
| 25 DCHECK(EnsureOnThread(BrowserThread::DB)); |
| 26 DCHECK(sync_service_); |
| 27 DCHECK(web_database_); |
| 28 DCHECK(personal_data_); |
| 29 } |
| 30 |
| 31 AutofillProfileModelAssociator::~AutofillProfileModelAssociator() { |
| 32 DCHECK(EnsureOnThread(BrowserThread::DB)); |
| 33 } |
| 34 sync_api::ReadNode* AutofillProfileModelAssociator::GetReadNode( |
| 35 sync_api::WriteTransaction* trans) { |
| 36 return new sync_api::ReadNode(trans); |
| 37 } |
| 38 |
| 39 bool AutofillProfileModelAssociator::TraverseAndAssociateChromeAutoFillProfiles( |
| 40 sync_api::WriteTransaction* write_trans, |
| 41 const sync_api::ReadNode& autofill_root, |
| 42 const std::vector<AutoFillProfile*>& all_profiles_from_db, |
| 43 std::set<std::string>* current_profiles, |
| 44 std::vector<AutoFillProfile*>* updated_profiles) { |
| 45 scoped_ptr<sync_api::ReadNode> node(GetReadNode(write_trans)); |
| 46 |
| 47 // Alias the all_profiles_from_db so we fit in 80 characters |
| 48 const std::vector<AutoFillProfile*>& profiles(all_profiles_from_db); |
| 49 for (std::vector<AutoFillProfile*>::const_iterator ix = profiles.begin(); |
| 50 ix != profiles.end(); |
| 51 ++ix) { |
| 52 std::string guid((*ix)->guid()); |
| 53 |
| 54 node->Reset(); |
| 55 if (node->InitByClientTagLookup(syncable::AUTOFILL_PROFILE, guid)) { |
| 56 const sync_pb::AutofillProfile2Specifics& autofill( |
| 57 node->GetAutofillProfileSpecifics()); |
| 58 if (OverwriteProfileWithServerData(*ix, autofill)){ |
| 59 updated_profiles->push_back(*ix); |
| 60 } |
| 61 Associate(&guid, node->GetId()); |
| 62 } else { |
| 63 int64 id; |
| 64 if (!MakeNewAutofillProfileSyncNode(write_trans, |
| 65 autofill_root, |
| 66 (**ix), |
| 67 &id)) |
| 68 { |
| 69 LOG(ERROR) << "Failed making an autofill item on the sync database"; |
| 70 } |
| 71 Associate(&guid, id); |
| 72 } |
| 73 current_profiles->insert(guid); |
| 74 } |
| 75 |
| 76 return true; |
| 77 } |
| 78 |
| 79 bool AutofillProfileModelAssociator::LoadAutofillData( |
| 80 std::vector<AutoFillProfile*>* profiles) { |
| 81 if (IsAbortPending()) |
| 82 return false; |
| 83 |
| 84 if (!web_database_->GetAutoFillProfiles(profiles)) |
| 85 return false; |
| 86 |
| 87 return true; |
| 88 } |
| 89 |
| 90 bool AutofillProfileModelAssociator::EnsureOnThread(BrowserThread::ID id) { |
| 91 return dontCheckThreadOnExit_ || BrowserThread::CurrentlyOn(id); |
| 92 } |
| 93 |
| 94 bool AutofillProfileModelAssociator::AssociateModels() { |
| 95 VLOG(1) << "Associating Autofill Models"; |
| 96 DCHECK(EnsureOnThread(BrowserThread::DB)); |
| 97 { |
| 98 AutoLock lock(abort_association_pending_lock_); |
| 99 abort_association_pending_ = false; |
| 100 } |
| 101 |
| 102 // TODO(lipalani): Attempt to load the model association from storage. |
| 103 ScopedVector<AutoFillProfile> profiles; |
| 104 |
| 105 if (!LoadAutofillData(&profiles.get())) { |
| 106 LOG(ERROR) << "Could not get the autofill data from WebDatabase."; |
| 107 return false; |
| 108 } |
| 109 |
| 110 DataBundle bundle; |
| 111 { |
| 112 // The write transaction lock is held inside this block. |
| 113 // We do all the web db operations outside this block. |
| 114 sync_api::WriteTransaction trans( |
| 115 sync_service_->backend()->GetUserShareHandle()); |
| 116 |
| 117 sync_api::ReadNode autofill_root(&trans); |
| 118 if (!autofill_root.InitByTagLookup(kAutofillProfileTag)) { |
| 119 LOG(ERROR) << "Server did not create the top-level autofill node. We " |
| 120 << "might be running against an out-of-date server."; |
| 121 return false; |
| 122 } |
| 123 |
| 124 if (!TraverseAndAssociateChromeAutoFillProfiles(&trans, autofill_root, |
| 125 profiles.get(), &bundle.current_profiles, |
| 126 &bundle.updated_profiles) || |
| 127 !TraverseAndAssociateAllSyncNodes(&trans, autofill_root, &bundle)) { |
| 128 return false; |
| 129 } |
| 130 } |
| 131 |
| 132 if (!SaveChangesToWebData(bundle)) { |
| 133 LOG(ERROR) << "Failed to update autofill entries."; |
| 134 return false; |
| 135 } |
| 136 |
| 137 // [TODO] - split out the OptimisticRefreshTask into its own class |
| 138 // from autofill_model_associator |
| 139 // Will be done as part of the autofill_model_associator work. |
| 140 // BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 141 // new DoOptimisticRefreshTask(personal_data_)); |
| 142 return true; |
| 143 } |
| 144 |
| 145 bool AutofillProfileModelAssociator::DisassociateModels() { |
| 146 id_map_.clear(); |
| 147 id_map_inverse_.clear(); |
| 148 return true; |
| 149 } |
| 150 |
| 151 // Helper to compare the local value and cloud value of a field, merge into |
| 152 // the local value if they differ, and return whether the merge happened. |
| 153 bool AutofillProfileModelAssociator::MergeField(FormGroup* f, |
| 154 AutoFillFieldType t, |
| 155 const std::string& specifics_field) { |
| 156 if (UTF16ToUTF8(f->GetFieldText(AutoFillType(t))) == specifics_field) |
| 157 return false; |
| 158 f->SetInfo(AutoFillType(t), UTF8ToUTF16(specifics_field)); |
| 159 return true; |
| 160 } |
| 161 bool AutofillProfileModelAssociator::SyncModelHasUserCreatedNodes( |
| 162 bool *has_nodes) |
| 163 { |
| 164 CHECK(has_nodes != NULL); |
| 165 sync_api::ReadTransaction trans( |
| 166 sync_service_->backend()->GetUserShareHandle()); |
| 167 |
| 168 sync_api::ReadNode node(&trans); |
| 169 |
| 170 if (!node.InitByClientTagLookup( |
| 171 syncable::AUTOFILL_PROFILE, |
| 172 kAutofillProfileTag)) { |
| 173 LOG(ERROR) << "Sever did not create a top level node" |
| 174 << "Out of data server or autofill type not enabled"; |
| 175 return false; |
| 176 } |
| 177 |
| 178 *has_nodes = sync_api::kInvalidId != node.GetFirstChildId(); |
| 179 return true; |
| 180 } |
| 181 // static |
| 182 bool AutofillProfileModelAssociator::OverwriteProfileWithServerData( |
| 183 AutoFillProfile* merge_into, |
| 184 const sync_pb::AutofillProfile2Specifics& specifics) { |
| 185 bool diff = false; |
| 186 AutoFillProfile* p = merge_into; |
| 187 const sync_pb::AutofillProfile2Specifics& s(specifics); |
| 188 diff = MergeField(p, NAME_FIRST, s.name_first()) || diff; |
| 189 diff = MergeField(p, NAME_LAST, s.name_last()) || diff; |
| 190 diff = MergeField(p, NAME_MIDDLE, s.name_middle()) || diff; |
| 191 diff = MergeField(p, ADDRESS_HOME_LINE1, s.address_home_line1()) || diff; |
| 192 diff = MergeField(p, ADDRESS_HOME_LINE2, s.address_home_line2()) || diff; |
| 193 diff = MergeField(p, ADDRESS_HOME_CITY, s.address_home_city()) || diff; |
| 194 diff = MergeField(p, ADDRESS_HOME_STATE, s.address_home_state()) || diff; |
| 195 diff = MergeField(p, ADDRESS_HOME_COUNTRY, s.address_home_country()) || diff; |
| 196 diff = MergeField(p, ADDRESS_HOME_ZIP, s.address_home_zip()) || diff; |
| 197 diff = MergeField(p, EMAIL_ADDRESS, s.email_address()) || diff; |
| 198 diff = MergeField(p, COMPANY_NAME, s.company_name()) || diff; |
| 199 diff = MergeField(p, PHONE_FAX_WHOLE_NUMBER, s.phone_fax_whole_number()) |
| 200 || diff; |
| 201 diff = MergeField(p, PHONE_HOME_WHOLE_NUMBER, s.phone_home_whole_number()) |
| 202 || diff; |
| 203 return diff; |
| 204 } |
| 205 |
| 206 bool AutofillProfileModelAssociator::MakeNewAutofillProfileSyncNode( |
| 207 sync_api::WriteTransaction* trans, |
| 208 const sync_api::BaseNode& autofill_root, |
| 209 const AutoFillProfile& profile, |
| 210 int64* sync_id) { |
| 211 sync_api::WriteNode node(trans); |
| 212 if (!node.InitUniqueByCreation( |
| 213 syncable::AUTOFILL_PROFILE, autofill_root, profile.guid())) { |
| 214 LOG(ERROR) << "Failed to create autofill sync node."; |
| 215 return false; |
| 216 } |
| 217 node.SetTitle(UTF8ToWide(profile.guid())); |
| 218 |
| 219 // [TODO] This needs rewriting. This will be tackled |
| 220 // when rewriting autofill change processor. |
| 221 // AutofillChangeProcessor::WriteAutofillProfile(profile, &node); |
| 222 *sync_id = node.GetId(); |
| 223 return true; |
| 224 } |
| 225 |
| 226 bool AutofillProfileModelAssociator::TraverseAndAssociateAllSyncNodes( |
| 227 sync_api::WriteTransaction* write_trans, |
| 228 const sync_api::ReadNode& autofill_root, |
| 229 DataBundle* bundle) { |
| 230 DCHECK(EnsureOnThread(BrowserThread::DB)); |
| 231 |
| 232 int64 sync_child_id = autofill_root.GetFirstChildId(); |
| 233 scoped_ptr<sync_api::ReadNode> sync_child(GetReadNode(write_trans)); |
| 234 while (sync_child_id != sync_api::kInvalidId) { |
| 235 sync_child->Reset(); |
| 236 if (!sync_child->InitByIdLookup(sync_child_id)) { |
| 237 LOG(ERROR) << "Failed to fetch child node."; |
| 238 return false; |
| 239 } |
| 240 const sync_pb::AutofillProfile2Specifics& autofill( |
| 241 sync_child->GetAutofillProfileSpecifics()); |
| 242 |
| 243 AddNativeProfileIfNeeded(autofill, bundle, *sync_child); |
| 244 |
| 245 sync_child_id = sync_child->GetSuccessorId(); |
| 246 } |
| 247 return true; |
| 248 } |
| 249 |
| 250 void AutofillProfileModelAssociator::AddNativeProfileIfNeeded( |
| 251 const sync_pb::AutofillProfile2Specifics& profile, |
| 252 DataBundle* bundle, |
| 253 const sync_api::ReadNode& node) { |
| 254 DCHECK(EnsureOnThread(BrowserThread::DB)); |
| 255 |
| 256 // [TODO] this looping through is costly. Replace this with a binary tree |
| 257 // at least in case of auto fill entries. |
| 258 if (bundle->current_profiles.find(profile.guid()) == |
| 259 bundle->current_profiles.end()) { |
| 260 std::string guid(profile.guid()); |
| 261 Associate(&guid, node.GetId()); |
| 262 AutoFillProfile* p = new AutoFillProfile(profile.guid()); |
| 263 OverwriteProfileWithServerData(p, profile); |
| 264 bundle->new_profiles.push_back(p); |
| 265 } |
| 266 } |
| 267 |
| 268 bool AutofillProfileModelAssociator::SaveChangesToWebData( |
| 269 const DataBundle& bundle) { |
| 270 DCHECK(EnsureOnThread(BrowserThread::DB)); |
| 271 |
| 272 if (IsAbortPending()) |
| 273 return false; |
| 274 |
| 275 for (size_t i = 0; i < bundle.new_profiles.size(); i++) { |
| 276 if (IsAbortPending()) |
| 277 return false; |
| 278 if (!web_database_->AddAutoFillProfile(*bundle.new_profiles[i])) |
| 279 return false; |
| 280 } |
| 281 |
| 282 for (size_t i = 0; i < bundle.updated_profiles.size(); i++) { |
| 283 if (IsAbortPending()) |
| 284 return false; |
| 285 if (!web_database_->UpdateAutoFillProfile(*bundle.updated_profiles[i])) |
| 286 return false; |
| 287 } |
| 288 return true; |
| 289 } |
| 290 |
| 291 void AutofillProfileModelAssociator::Associate( |
| 292 const std::string* autofill, |
| 293 int64 sync_id) { |
| 294 DCHECK(EnsureOnThread(BrowserThread::DB)); |
| 295 DCHECK_NE(sync_api::kInvalidId, sync_id); |
| 296 DCHECK(id_map_.find(*autofill) == id_map_.end()); |
| 297 DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end()); |
| 298 id_map_[*autofill] = sync_id; |
| 299 id_map_inverse_[sync_id] = *autofill; |
| 300 } |
| 301 |
| 302 void AutofillProfileModelAssociator::Disassociate(int64 sync_id) { |
| 303 DCHECK(EnsureOnThread(BrowserThread::DB)); |
| 304 SyncIdToAutofillMap::iterator iter = id_map_inverse_.find(sync_id); |
| 305 if (iter == id_map_inverse_.end()) |
| 306 return; |
| 307 CHECK(id_map_.erase(iter->second)); |
| 308 id_map_inverse_.erase(iter); |
| 309 } |
| 310 |
| 311 int64 AutofillProfileModelAssociator::GetSyncIdFromChromeId( |
| 312 const std::string autofill) { |
| 313 AutofillToSyncIdMap::const_iterator iter = id_map_.find(autofill); |
| 314 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second; |
| 315 } |
| 316 |
| 317 void AutofillProfileModelAssociator::AbortAssociation() { |
| 318 DCHECK(EnsureOnThread(BrowserThread::UI)); |
| 319 AutoLock lock(abort_association_pending_lock_); |
| 320 abort_association_pending_ = true; |
| 321 } |
| 322 |
| 323 bool AutofillProfileModelAssociator::IsAbortPending() { |
| 324 AutoLock lock(abort_association_pending_lock_); |
| 325 return abort_association_pending_; |
| 326 } |
| 327 |
| 328 } // namespace browser_sync |
OLD | NEW |