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

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

Issue 4683003: Sending the proto files for review to unblcok the server team Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Created 10 years, 1 month 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) 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(), &timestamps)) {
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
OLDNEW
« no previous file with comments | « chrome/browser/sync/glue/autofill_model_associator2.h ('k') | chrome/browser/sync/glue/autofill_model_associator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698