Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(320)

Side by Side Diff: chrome/browser/sync/glue/autofill_profile_model_associator.cc

Issue 7819002: Migrate AutofillProfile sync to new API. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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/tracked.h"
8 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/sync/api/sync_error.h"
10 #include "chrome/browser/sync/glue/autofill_profile_change_processor.h"
11 #include "chrome/browser/sync/glue/do_optimistic_refresh_task.h"
12 #include "chrome/browser/sync/internal_api/read_node.h"
13 #include "chrome/browser/sync/internal_api/read_transaction.h"
14 #include "chrome/browser/sync/internal_api/write_node.h"
15 #include "chrome/browser/sync/internal_api/write_transaction.h"
16 #include "chrome/browser/sync/profile_sync_service.h"
17 #include "chrome/browser/webdata/autofill_table.h"
18 #include "chrome/browser/webdata/web_database.h"
19 #include "chrome/common/guid.h"
20
21 using sync_api::ReadNode;
22 namespace browser_sync {
23
24 const char kAutofillProfileTag[] = "google_chrome_autofill_profiles";
25
26 AutofillProfileModelAssociator::AutofillProfileModelAssociator(
27 ProfileSyncService* sync_service,
28 WebDatabase* web_database,
29 PersonalDataManager* personal_data)
30 : sync_service_(sync_service),
31 web_database_(web_database),
32 personal_data_(personal_data),
33 autofill_node_id_(sync_api::kInvalidId),
34 abort_association_pending_(false),
35 number_of_profiles_created_(0) {
36 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
37 DCHECK(sync_service_);
38 DCHECK(web_database_);
39 DCHECK(personal_data_);
40 }
41
42 AutofillProfileModelAssociator::~AutofillProfileModelAssociator() {
43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
44 }
45
46 AutofillProfileModelAssociator::AutofillProfileModelAssociator()
47 : sync_service_(NULL),
48 web_database_(NULL),
49 personal_data_(NULL),
50 autofill_node_id_(0),
51 abort_association_pending_(false),
52 number_of_profiles_created_(0) {
53 }
54
55 bool AutofillProfileModelAssociator::TraverseAndAssociateChromeAutofillProfiles(
56 sync_api::WriteTransaction* write_trans,
57 const sync_api::ReadNode& autofill_root,
58 const std::vector<AutofillProfile*>& all_profiles_from_db,
59 std::set<std::string>* current_profiles,
60 std::vector<AutofillProfile*>* updated_profiles,
61 std::vector<AutofillProfile*>* new_profiles,
62 std::vector<std::string>* profiles_to_delete) {
63
64 if (VLOG_IS_ON(2)) {
65 VLOG(2) << "[AUTOFILL MIGRATION]"
66 << "Printing profiles from web db";
67
68 for (std::vector<AutofillProfile*>::const_iterator ix =
69 all_profiles_from_db.begin(); ix != all_profiles_from_db.end(); ++ix) {
70 AutofillProfile* p = *ix;
71 VLOG(2) << "[AUTOFILL MIGRATION] "
72 << p->GetInfo(NAME_FIRST)
73 << p->GetInfo(NAME_LAST)
74 << p->guid();
75 }
76 }
77
78 VLOG(1) << "[AUTOFILL MIGRATION]"
79 << "Looking for the above data in sync db..";
80
81 // Alias the all_profiles_from_db so we fit in 80 characters
82 const std::vector<AutofillProfile*>& profiles(all_profiles_from_db);
83 for (std::vector<AutofillProfile*>::const_iterator ix = profiles.begin();
84 ix != profiles.end();
85 ++ix) {
86 std::string guid((*ix)->guid());
87 if (guid::IsValidGUID(guid) == false) {
88 DCHECK(false) << "Guid in the web db is invalid " << guid;
89 continue;
90 }
91
92 ReadNode node(write_trans);
93 if (node.InitByClientTagLookup(syncable::AUTOFILL_PROFILE, guid) &&
94 // The following check is to ensure the given sync node is not already
95 // associated with another profile. That could happen if the user has
96 // the same profile duplicated.
97 current_profiles->find(guid) == current_profiles->end()) {
98 VLOG(2) << "[AUTOFILL MIGRATION]"
99 << " Found in sync db: "
100 << (*ix)->GetInfo(NAME_FIRST)
101 << (*ix)->GetInfo(NAME_LAST)
102 << (*ix)->guid()
103 << " so associating with node id " << node.GetId();
104 const sync_pb::AutofillProfileSpecifics& autofill(
105 node.GetAutofillProfileSpecifics());
106 if (OverwriteProfileWithServerData(*ix, autofill)) {
107 updated_profiles->push_back(*ix);
108 }
109 Associate(&guid, node.GetId());
110 current_profiles->insert(guid);
111 } else {
112 MakeNewAutofillProfileSyncNodeIfNeeded(write_trans,
113 autofill_root,
114 (**ix),
115 new_profiles,
116 current_profiles,
117 profiles_to_delete);
118 }
119 }
120 return true;
121 }
122
123 bool AutofillProfileModelAssociator::GetSyncIdForTaggedNode(
124 const std::string& tag,
125 int64* sync_id) {
126 sync_api::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
127 sync_api::ReadNode sync_node(&trans);
128 if (!sync_node.InitByTagLookup(tag.c_str()))
129 return false;
130 *sync_id = sync_node.GetId();
131 return true;
132 }
133
134 bool AutofillProfileModelAssociator::LoadAutofillData(
135 std::vector<AutofillProfile*>* profiles) {
136 if (IsAbortPending())
137 return false;
138
139 if (!web_database_->GetAutofillTable()->GetAutofillProfiles(profiles))
140 return false;
141
142 return true;
143 }
144
145 bool AutofillProfileModelAssociator::AssociateModels(SyncError* error) {
146 VLOG(1) << "Associating Autofill Models";
147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
148 {
149 base::AutoLock lock(abort_association_pending_lock_);
150 abort_association_pending_ = false;
151 }
152
153 ScopedVector<AutofillProfile> profiles;
154
155 if (!LoadAutofillData(&profiles.get())) {
156 error->Reset(FROM_HERE,
157 "Could not get the autofill data from WebDatabase.",
158 model_type());
159 return false;
160 }
161
162 VLOG(1) << "[AUTOFILL MIGRATION]"
163 << " Now associating to the new autofill profile model associator"
164 << " root node";
165 DataBundle bundle;
166 {
167 // The write transaction lock is held inside this block.
168 // We do all the web db operations outside this block.
169 sync_api::WriteTransaction trans(FROM_HERE, sync_service_->GetUserShare());
170
171 sync_api::ReadNode autofill_root(&trans);
172 if (!autofill_root.InitByTagLookup(kAutofillProfileTag)) {
173 error->Reset(FROM_HERE,
174 "Server did not create the top-level autofill node. We "
175 "might be running against an out-of-date server.",
176 model_type());
177 return false;
178 }
179
180 if (!TraverseAndAssociateChromeAutofillProfiles(&trans, autofill_root,
181 profiles.get(), &bundle.current_profiles,
182 &bundle.updated_profiles,
183 &bundle.new_profiles,
184 &bundle.profiles_to_delete) ||
185 !TraverseAndAssociateAllSyncNodes(&trans, autofill_root, &bundle)) {
186 error->Reset(FROM_HERE,
187 "Failed to associate all sync nodes.",
188 model_type());
189 return false;
190 }
191 }
192
193 if (!SaveChangesToWebData(bundle)) {
194 error->Reset(FROM_HERE, "Failed to update webdata.", model_type());
195 return false;
196 }
197
198 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
199 new DoOptimisticRefreshForAutofill(personal_data_));
200 return true;
201 }
202
203 bool AutofillProfileModelAssociator::DisassociateModels(SyncError* error) {
204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
205 id_map_.clear();
206 id_map_inverse_.clear();
207 return true;
208 }
209
210 // Helper to compare the local value and cloud value of a field, merge into
211 // the local value if they differ, and return whether the merge happened.
212 bool AutofillProfileModelAssociator::MergeField(FormGroup* f,
213 AutofillFieldType t,
214 const std::string& specifics_field) {
215 if (UTF16ToUTF8(f->GetInfo(t)) == specifics_field)
216 return false;
217 f->SetInfo(t, UTF8ToUTF16(specifics_field));
218 return true;
219 }
220 bool AutofillProfileModelAssociator::SyncModelHasUserCreatedNodes(
221 bool *has_nodes) {
222 CHECK_NE(has_nodes, reinterpret_cast<bool*>(NULL));
223 sync_api::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
224
225 sync_api::ReadNode node(&trans);
226
227 if (!node.InitByTagLookup(kAutofillProfileTag)) {
228 LOG(ERROR) << "Sever did not create a top level node"
229 << "Out of data server or autofill type not enabled";
230 return false;
231 }
232
233 *has_nodes = sync_api::kInvalidId != node.GetFirstChildId();
234 return true;
235 }
236 // static
237 bool AutofillProfileModelAssociator::OverwriteProfileWithServerData(
238 AutofillProfile* merge_into,
239 const sync_pb::AutofillProfileSpecifics& specifics) {
240 bool diff = false;
241 AutofillProfile* p = merge_into;
242 const sync_pb::AutofillProfileSpecifics& s(specifics);
243 diff = MergeField(p, NAME_FIRST, s.name_first()) || diff;
244 diff = MergeField(p, NAME_LAST, s.name_last()) || diff;
245 diff = MergeField(p, NAME_MIDDLE, s.name_middle()) || diff;
246 diff = MergeField(p, ADDRESS_HOME_LINE1, s.address_home_line1()) || diff;
247 diff = MergeField(p, ADDRESS_HOME_LINE2, s.address_home_line2()) || diff;
248 diff = MergeField(p, ADDRESS_HOME_CITY, s.address_home_city()) || diff;
249 diff = MergeField(p, ADDRESS_HOME_STATE, s.address_home_state()) || diff;
250 diff = MergeField(p, ADDRESS_HOME_COUNTRY, s.address_home_country()) || diff;
251 diff = MergeField(p, ADDRESS_HOME_ZIP, s.address_home_zip()) || diff;
252 diff = MergeField(p, EMAIL_ADDRESS, s.email_address()) || diff;
253 diff = MergeField(p, COMPANY_NAME, s.company_name()) || diff;
254 diff = MergeField(p, PHONE_FAX_WHOLE_NUMBER, s.phone_fax_whole_number())
255 || diff;
256 diff = MergeField(p, PHONE_HOME_WHOLE_NUMBER, s.phone_home_whole_number())
257 || diff;
258 return diff;
259 }
260
261
262 int64 AutofillProfileModelAssociator::FindSyncNodeWithProfile(
263 sync_api::WriteTransaction* trans,
264 const sync_api::BaseNode& autofill_root,
265 const AutofillProfile& profile_from_db,
266 std::set<std::string>* current_profiles) {
267 int64 sync_child_id = autofill_root.GetFirstChildId();
268 while (sync_child_id != sync_api::kInvalidId) {
269 ReadNode read_node(trans);
270 AutofillProfile p;
271 if (!read_node.InitByIdLookup(sync_child_id)) {
272 LOG(ERROR) << "unable to find the id given by getfirst child " <<
273 sync_child_id;
274 return sync_api::kInvalidId;
275 }
276 const sync_pb::AutofillProfileSpecifics& autofill_specifics(
277 read_node.GetAutofillProfileSpecifics());
278
279 // This find should be fast as the set uses tree.
280 if (current_profiles->find(autofill_specifics.guid())
281 == current_profiles->end()) {
282 OverwriteProfileWithServerData(&p, autofill_specifics);
283 if (p.Compare(profile_from_db) == 0) {
284 return sync_child_id;
285 }
286 }
287 sync_child_id = read_node.GetSuccessorId();
288 }
289
290 return sync_api::kInvalidId;
291 }
292 bool AutofillProfileModelAssociator::MakeNewAutofillProfileSyncNodeIfNeeded(
293 sync_api::WriteTransaction* trans,
294 const sync_api::BaseNode& autofill_root,
295 const AutofillProfile& profile,
296 std::vector<AutofillProfile*>* new_profiles,
297 std::set<std::string>* current_profiles,
298 std::vector<std::string>* profiles_to_delete) {
299
300 int64 sync_node_id = FindSyncNodeWithProfile(trans,
301 autofill_root,
302 profile,
303 current_profiles);
304 if (sync_node_id != sync_api::kInvalidId) {
305 // In case of duplicates throw away the local profile and apply the
306 // server profile.(The only difference between the 2 profiles are the guids)
307 profiles_to_delete->push_back(profile.guid());
308 sync_api::ReadNode read_node(trans);
309 if (!read_node.InitByIdLookup(sync_node_id)) {
310 LOG(ERROR);
311 return false;
312 }
313 const sync_pb::AutofillProfileSpecifics& autofill_specifics(
314 read_node.GetAutofillProfileSpecifics());
315 if (guid::IsValidGUID(autofill_specifics.guid()) == false) {
316 NOTREACHED() << "Guid in the web db is invalid " <<
317 autofill_specifics.guid();
318 return false;
319 }
320 AutofillProfile* p = new AutofillProfile(autofill_specifics.guid());
321 OverwriteProfileWithServerData(p, autofill_specifics);
322 new_profiles->push_back(p);
323 std::string guid = autofill_specifics.guid();
324 Associate(&guid, sync_node_id);
325 current_profiles->insert(autofill_specifics.guid());
326 VLOG(2) << "[AUTOFILL MIGRATION]"
327 << "Found in sync db but with a different guid: "
328 << UTF16ToUTF8(profile.GetInfo(NAME_FIRST))
329 << UTF16ToUTF8(profile.GetInfo(NAME_LAST))
330 << "New guid " << autofill_specifics.guid() << " sync node id "
331 << sync_node_id << " so associating. Profile to be deleted "
332 << profile.guid();
333 } else {
334 sync_api::WriteNode node(trans);
335
336 // The profile.guid() is expected to be a valid guid. The caller is expected
337 // to pass in a valid profile object with a valid guid. Having to check in
338 // 2 places(the caller and here) is not optimal.
339 if (!node.InitUniqueByCreation(
340 syncable::AUTOFILL_PROFILE, autofill_root, profile.guid())) {
341 LOG(ERROR) << "Failed to create autofill sync node.";
342 return false;
343 }
344 node.SetTitle(UTF8ToWide(profile.guid()));
345 VLOG(2) << "[AUTOFILL MIGRATION]"
346 << "NOT Found in sync db "
347 << UTF16ToUTF8(profile.GetInfo(NAME_FIRST))
348 << UTF16ToUTF8(profile.GetInfo(NAME_LAST))
349 << profile.guid()
350 << " so creating a new sync node. Sync node id "
351 << node.GetId();
352 AutofillProfileChangeProcessor::WriteAutofillProfile(profile, &node);
353 current_profiles->insert(profile.guid());
354 std::string guid = profile.guid();
355 Associate(&guid, node.GetId());
356 number_of_profiles_created_++;
357 }
358 return true;
359 }
360
361 bool AutofillProfileModelAssociator::TraverseAndAssociateAllSyncNodes(
362 sync_api::WriteTransaction* write_trans,
363 const sync_api::ReadNode& autofill_root,
364 DataBundle* bundle) {
365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
366 VLOG(1) << "[AUTOFILL MIGRATION] "
367 << " Iterating over sync nodes of autofill profile root node";
368
369 int64 sync_child_id = autofill_root.GetFirstChildId();
370 while (sync_child_id != sync_api::kInvalidId) {
371 ReadNode sync_child(write_trans);
372 if (!sync_child.InitByIdLookup(sync_child_id)) {
373 LOG(ERROR) << "Failed to fetch child node.";
374 return false;
375 }
376 const sync_pb::AutofillProfileSpecifics& autofill(
377 sync_child.GetAutofillProfileSpecifics());
378
379 AddNativeProfileIfNeeded(autofill, bundle, sync_child);
380
381 sync_child_id = sync_child.GetSuccessorId();
382 }
383 return true;
384 }
385
386 void AutofillProfileModelAssociator::AddNativeProfileIfNeeded(
387 const sync_pb::AutofillProfileSpecifics& profile,
388 DataBundle* bundle,
389 const sync_api::ReadNode& node) {
390 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
391
392 VLOG(2) << "[AUTOFILL MIGRATION] "
393 << "Trying to lookup "
394 << profile.name_first()
395 << " "
396 << profile.name_last()
397 << "sync node id " << node.GetId()
398 << " Guid " << profile.guid()
399 << " in the web db";
400
401 if (guid::IsValidGUID(profile.guid()) == false) {
402 DCHECK(false) << "Guid in the sync db is invalid " << profile.guid();
403 return;
404 }
405
406 if (bundle->current_profiles.find(profile.guid()) ==
407 bundle->current_profiles.end()) {
408 std::string guid(profile.guid());
409 Associate(&guid, node.GetId());
410 AutofillProfile* p = new AutofillProfile(profile.guid());
411 OverwriteProfileWithServerData(p, profile);
412 bundle->new_profiles.push_back(p);
413 VLOG(2) << "[AUTOFILL MIGRATION] "
414 << " Did not find one so creating it on web db";
415 } else {
416 VLOG(2) << "[AUTOFILL MIGRATION] "
417 << " Found it on web db. Moving on ";
418 }
419 }
420
421 bool AutofillProfileModelAssociator::SaveChangesToWebData(
422 const DataBundle& bundle) {
423 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
424
425 if (IsAbortPending())
426 return false;
427
428 for (size_t i = 0; i < bundle.new_profiles.size(); i++) {
429 if (IsAbortPending())
430 return false;
431 if (!web_database_->GetAutofillTable()->AddAutofillProfile(
432 *bundle.new_profiles[i]))
433 return false;
434 }
435
436 for (size_t i = 0; i < bundle.updated_profiles.size(); i++) {
437 if (IsAbortPending())
438 return false;
439 if (!web_database_->GetAutofillTable()->UpdateAutofillProfile(
440 *bundle.updated_profiles[i]))
441 return false;
442 }
443
444 for (size_t i = 0; i< bundle.profiles_to_delete.size(); ++i) {
445 if (IsAbortPending())
446 return false;
447 if (!web_database_->GetAutofillTable()->RemoveAutofillProfile(
448 bundle.profiles_to_delete[i]))
449 return false;
450 }
451 return true;
452 }
453
454 bool AutofillProfileModelAssociator::InitSyncNodeFromChromeId(
455 const std::string& node_id,
456 sync_api::BaseNode* sync_node) {
457 return false;
458 }
459
460 void AutofillProfileModelAssociator::Associate(
461 const std::string* autofill,
462 int64 sync_id) {
463 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
464 DCHECK_NE(sync_api::kInvalidId, sync_id);
465 DCHECK(id_map_.find(*autofill) == id_map_.end());
466 DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end());
467 id_map_[*autofill] = sync_id;
468 id_map_inverse_[sync_id] = *autofill;
469 }
470
471 void AutofillProfileModelAssociator::Disassociate(int64 sync_id) {
472 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
473 SyncIdToAutofillMap::iterator iter = id_map_inverse_.find(sync_id);
474 if (iter == id_map_inverse_.end())
475 return;
476 CHECK(id_map_.erase(iter->second));
477 id_map_inverse_.erase(iter);
478 }
479
480 int64 AutofillProfileModelAssociator::GetSyncIdFromChromeId(
481 const std::string& autofill) {
482 AutofillToSyncIdMap::const_iterator iter = id_map_.find(autofill);
483 return iter == id_map_.end() ? sync_api::kInvalidId : iter->second;
484 }
485
486 void AutofillProfileModelAssociator::AbortAssociation() {
487 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
488 base::AutoLock lock(abort_association_pending_lock_);
489 abort_association_pending_ = true;
490 }
491
492 const std::string* AutofillProfileModelAssociator::GetChromeNodeFromSyncId(
493 int64 sync_id) {
494 SyncIdToAutofillMap::const_iterator iter = id_map_inverse_.find(sync_id);
495 return iter == id_map_inverse_.end() ? NULL : &(iter->second);
496 }
497
498 bool AutofillProfileModelAssociator::IsAbortPending() {
499 base::AutoLock lock(abort_association_pending_lock_);
500 return abort_association_pending_;
501 }
502
503 AutofillProfileModelAssociator::DataBundle::DataBundle() {}
504
505 AutofillProfileModelAssociator::DataBundle::~DataBundle() {
506 STLDeleteElements(&new_profiles);
507 }
508
509 bool AutofillProfileModelAssociator::CryptoReadyIfNecessary() {
510 // We only access the cryptographer while holding a transaction.
511 sync_api::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare());
512 syncable::ModelTypeSet encrypted_types;
513 encrypted_types = sync_api::GetEncryptedTypes(&trans);
514 return encrypted_types.count(syncable::AUTOFILL_PROFILE) == 0 ||
515 sync_service_->IsCryptographerReady(&trans);
516 }
517
518 } // namespace browser_sync
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698