|
OLD | NEW |
---|---|
(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_syncable_service.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/do_optimistic_refresh_task.h" | |
11 #include "chrome/browser/webdata/autofill_table.h" | |
12 #include "chrome/browser/webdata/web_database.h" | |
13 #include "chrome/common/chrome_notification_types.h" | |
14 #include "chrome/common/guid.h" | |
15 #include "content/common/notification_service.h" | |
16 | |
17 namespace browser_sync { | |
18 | |
19 const char kAutofillProfileTag[] = "google_chrome_autofill_profiles"; | |
20 | |
21 AutofillProfileSyncableService::AutofillProfileSyncableService( | |
22 WebDatabase* web_database, | |
23 PersonalDataManager* personal_data) | |
24 : web_database_(web_database), | |
25 personal_data_(personal_data), | |
26 sync_processor_(NULL), | |
27 number_of_profiles_created_(0) { | |
28 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
29 DCHECK(web_database_); | |
30 DCHECK(personal_data_); | |
31 notification_registrar_.Add(this, | |
32 chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED, | |
33 NotificationService::AllSources()); | |
Ilya Sherman
2011/09/01 07:22:31
Are you sure this should be AllSources() rather th
dhollowa
2011/09/01 23:11:46
Yes, it seems like this code should only listen/re
GeorgeY
2011/09/02 04:34:12
That call is moved from original change processor,
| |
34 } | |
35 | |
36 AutofillProfileSyncableService::~AutofillProfileSyncableService() { | |
37 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
dhollowa
2011/09/01 23:11:46
It is fine to leave this here... but I believe it
GeorgeY
2011/09/02 04:34:12
Yes, could be changed to DCHECK(CalledOnValidThrea
| |
38 } | |
39 | |
40 AutofillProfileSyncableService::AutofillProfileSyncableService() | |
41 : web_database_(NULL), | |
42 personal_data_(NULL), | |
43 sync_processor_(NULL), | |
44 number_of_profiles_created_(0) { | |
45 notification_registrar_.Add(this, | |
46 chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED, | |
47 NotificationService::AllSources()); | |
Ilya Sherman
2011/09/01 07:22:31
Ditto.
GeorgeY
2011/09/02 04:34:12
Ditto.
| |
48 } | |
49 | |
50 bool AutofillProfileSyncableService::LoadAutofillData( | |
51 std::vector<AutofillProfile*>* profiles) const { | |
52 // const_cast it as GetAutofillProfiles() is a really const accessor, though | |
53 // not so declared. | |
Ilya Sherman
2011/09/01 07:22:31
nit: Can we propagate const properly rather than c
dhollowa
2011/09/01 23:11:46
+1
GeorgeY
2011/09/02 04:34:12
Didn't do it because:
#1. The internal state of db
| |
54 if (!const_cast<WebDatabase*>(web_database_)-> | |
dhollowa
2011/09/01 23:11:46
nit: how about simply:
return web_database_->...
dhollowa
2011/09/07 01:48:15
Ping.
GeorgeY
2011/09/07 20:55:07
sure
| |
55 GetAutofillTable()->GetAutofillProfiles(profiles)) | |
56 return false; | |
57 | |
58 return true; | |
59 } | |
60 | |
61 SyncError AutofillProfileSyncableService::MergeDataAndStartSyncing( | |
62 syncable::ModelType type, | |
63 const SyncDataList& initial_sync_data, | |
64 SyncChangeProcessor* sync_processor) { | |
65 DCHECK(CalledOnValidThread()); | |
66 DCHECK(sync_processor_ == NULL); | |
67 VLOG(1) << "Associating Autofill: MergeDataAndStartSyncing"; | |
68 | |
69 ScopedVector<AutofillProfile> profiles; | |
70 | |
71 if (!LoadAutofillData(&profiles.get())) { | |
72 return SyncError( | |
73 FROM_HERE, "Could not get the autofill data from WebDatabase.", | |
74 model_type()); | |
75 } | |
76 | |
77 if (VLOG_IS_ON(2)) { | |
78 VLOG(2) << "[AUTOFILL MIGRATION]" | |
79 << "Printing profiles from web db"; | |
80 | |
81 for (ScopedVector<AutofillProfile>::const_iterator ix = | |
82 profiles.begin(); ix != profiles.end(); ++ix) { | |
83 AutofillProfile* p = *ix; | |
84 VLOG(2) << "[AUTOFILL MIGRATION] " | |
85 << p->GetInfo(NAME_FIRST) | |
86 << p->GetInfo(NAME_LAST) | |
87 << p->guid(); | |
88 } | |
89 } | |
90 | |
91 sync_processor_ = sync_processor; | |
92 | |
93 GuidToProfileMap remaining_profiles; | |
94 CreateGuidToProfileMap(&profiles, &remaining_profiles); | |
95 | |
96 DataBundle bundle; | |
97 // Go through and check for all the profiles that sync already knows about. | |
98 for (SyncDataList::const_iterator sync_iter = initial_sync_data.begin(); | |
99 sync_iter != initial_sync_data.end(); | |
100 ++sync_iter) | |
101 CreateOrUpdateProfile(*sync_iter, &profiles, &remaining_profiles, &bundle); | |
dhollowa
2011/09/01 23:11:46
nit: style guide requires { } for multi-line for()
GeorgeY
2011/09/02 04:34:12
AFAIK, {} are required *only* if executing statem
dhollowa
2011/09/07 01:48:15
src/base/... is littered with examples of this. A
GeorgeY
2011/09/07 20:55:07
Have you checked the last code - it is already th
dhollowa
2011/09/07 21:10:24
It is helpful to the reviewer if you mark "Done".
GeorgeY
2011/09/07 23:26:24
Done. :)
| |
102 | |
103 if (!SaveChangesToWebData(bundle)) | |
104 return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); | |
105 | |
106 SyncChangeList new_changes; | |
107 for (GuidToProfileMap::iterator i = remaining_profiles.begin(); | |
108 i != remaining_profiles.end(); ++i) { | |
109 new_changes.push_back( | |
110 CreateChange(SyncChange::ACTION_ADD, *(*(i->second)))); | |
111 } | |
112 | |
113 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); | |
114 | |
115 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
116 new DoOptimisticRefreshForAutofill(personal_data_)); | |
117 | |
118 return error; | |
119 } | |
120 | |
121 void AutofillProfileSyncableService::StopSyncing(syncable::ModelType type) { | |
122 DCHECK(CalledOnValidThread()); | |
123 DCHECK(sync_processor_ != NULL); | |
124 DCHECK_EQ(type, syncable::AUTOFILL_PROFILE); | |
125 | |
126 sync_processor_ = NULL; | |
127 } | |
128 | |
129 SyncDataList AutofillProfileSyncableService::GetAllSyncData( | |
130 syncable::ModelType type) const { | |
131 DCHECK(CalledOnValidThread()); | |
132 DCHECK(sync_processor_ != NULL); | |
133 DCHECK_EQ(type, syncable::AUTOFILL_PROFILE); | |
134 | |
135 SyncDataList current_data; | |
136 | |
137 ScopedVector<AutofillProfile> profiles; | |
138 | |
139 if (!LoadAutofillData(&profiles.get())) | |
140 return current_data; | |
141 | |
142 for (ScopedVector<AutofillProfile>::iterator i = profiles.begin(); | |
143 i != profiles.end(); ++i) { | |
144 sync_pb::EntitySpecifics specifics; | |
145 WriteAutofillProfile(*(*i), &specifics); | |
146 current_data.push_back(SyncData::CreateLocalData( | |
147 (*i)->guid(), (*i)->guid(), specifics)); | |
148 } | |
149 return current_data; | |
150 } | |
151 | |
152 SyncError AutofillProfileSyncableService::ProcessSyncChanges( | |
153 const tracked_objects::Location& from_here, | |
154 const SyncChangeList& change_list) { | |
155 DCHECK(CalledOnValidThread()); | |
156 DCHECK(sync_processor_ != NULL); | |
157 if (sync_processor_ == NULL) { | |
158 SyncError error(FROM_HERE, "Models not yet associated.", | |
159 syncable::AUTOFILL_PROFILE); | |
160 return error; | |
161 } | |
lipalani1
2011/09/01 05:06:02
I am concerned about the perf impact here Cam you
GeorgeY
2011/09/02 04:34:12
Done.
| |
162 | |
163 ScopedVector<AutofillProfile> profiles; | |
164 if (!LoadAutofillData(&profiles.get())) { | |
165 SyncError error(FROM_HERE, "Failed to read Web DB.", | |
166 syncable::AUTOFILL_PROFILE); | |
167 return error; | |
168 } | |
169 | |
170 GuidToProfileMap remaining_profiles; | |
171 CreateGuidToProfileMap(&profiles, &remaining_profiles); | |
172 | |
173 std::vector<std::string> profiles_to_delete; | |
174 DataBundle bundle; | |
175 | |
176 for (SyncChangeList::const_iterator i = change_list.begin(); | |
177 i != change_list.end(); ++i) { | |
178 DCHECK(i->IsValid()); | |
179 switch (i->change_type()) { | |
180 case SyncChange::ACTION_ADD: | |
181 case SyncChange::ACTION_UPDATE: | |
182 CreateOrUpdateProfile(i->sync_data(), &profiles, &remaining_profiles, | |
183 &bundle); | |
184 break; | |
185 case SyncChange::ACTION_DELETE: | |
186 bundle.profiles_to_delete.push_back( | |
187 i->sync_data().GetSpecifics(). | |
188 GetExtension(sync_pb::autofill_profile).guid()); | |
189 break; | |
190 default: | |
191 NOTREACHED() << "Unexpected sync change state."; | |
192 return SyncError(FROM_HERE, "ProcessSyncChanges failed on ChangeType " + | |
193 SyncChange::ChangeTypeToString(i->change_type()), | |
194 syncable::AUTOFILL_PROFILE); | |
195 } | |
196 } | |
197 | |
198 if (!SaveChangesToWebData(bundle)) | |
199 return SyncError(FROM_HERE, "Failed to update webdata.", model_type()); | |
200 | |
201 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
202 new DoOptimisticRefreshForAutofill(personal_data_)); | |
203 return SyncError(); | |
204 } | |
205 | |
206 void AutofillProfileSyncableService::Observe(int type, | |
207 const NotificationSource& source, | |
208 const NotificationDetails& details) { | |
209 DCHECK_EQ(type, chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED); | |
210 WebDataService* wds = Source<WebDataService>(source).ptr(); | |
211 | |
212 if (!wds || wds->GetDatabase() != web_database_) | |
213 return; | |
214 | |
215 AutofillProfileChange* change = Details<AutofillProfileChange>(details).ptr(); | |
216 | |
217 ActOnChange(change); | |
218 } | |
219 | |
220 // Helper to compare the local value and cloud value of a field, merge into | |
221 // the local value if they differ, and return whether the merge happened. | |
222 bool AutofillProfileSyncableService::MergeField(FormGroup* f, | |
223 AutofillFieldType t, | |
224 const std::string& specifics_field) { | |
225 if (UTF16ToUTF8(f->GetInfo(t)) == specifics_field) | |
226 return false; | |
227 f->SetInfo(t, UTF8ToUTF16(specifics_field)); | |
228 return true; | |
229 } | |
230 | |
231 // static | |
232 bool AutofillProfileSyncableService::OverwriteProfileWithServerData( | |
233 AutofillProfile* merge_into, | |
dhollowa
2011/09/01 20:17:49
Rename |merge_into| as |profile| and get rid of te
GeorgeY
2011/09/02 04:34:12
Done.
| |
234 const sync_pb::AutofillProfileSpecifics& specifics) { | |
235 bool diff = false; | |
236 AutofillProfile* p = merge_into; | |
237 const sync_pb::AutofillProfileSpecifics& s(specifics); | |
238 diff = MergeField(p, NAME_FIRST, s.name_first()) || diff; | |
239 diff = MergeField(p, NAME_LAST, s.name_last()) || diff; | |
240 diff = MergeField(p, NAME_MIDDLE, s.name_middle()) || diff; | |
241 diff = MergeField(p, ADDRESS_HOME_LINE1, s.address_home_line1()) || diff; | |
242 diff = MergeField(p, ADDRESS_HOME_LINE2, s.address_home_line2()) || diff; | |
243 diff = MergeField(p, ADDRESS_HOME_CITY, s.address_home_city()) || diff; | |
244 diff = MergeField(p, ADDRESS_HOME_STATE, s.address_home_state()) || diff; | |
245 diff = MergeField(p, ADDRESS_HOME_COUNTRY, s.address_home_country()) || diff; | |
246 diff = MergeField(p, ADDRESS_HOME_ZIP, s.address_home_zip()) || diff; | |
247 diff = MergeField(p, EMAIL_ADDRESS, s.email_address()) || diff; | |
248 diff = MergeField(p, COMPANY_NAME, s.company_name()) || diff; | |
249 diff = MergeField(p, PHONE_FAX_WHOLE_NUMBER, s.phone_fax_whole_number()) | |
250 || diff; | |
251 diff = MergeField(p, PHONE_HOME_WHOLE_NUMBER, s.phone_home_whole_number()) | |
252 || diff; | |
253 return diff; | |
254 } | |
255 | |
256 SyncChange AutofillProfileSyncableService::CreateChange( | |
257 SyncChange::SyncChangeType action, | |
258 const AutofillProfile& profile) { | |
259 return SyncChange(action, CreateData(profile)); | |
260 } | |
261 | |
262 SyncChange AutofillProfileSyncableService::CreateDeleteChange( | |
263 const std::string& guid) { | |
264 AutofillProfile empty_profile(guid); | |
265 return SyncChange(SyncChange::ACTION_DELETE, CreateData(empty_profile)); | |
266 } | |
267 | |
268 SyncData AutofillProfileSyncableService::CreateData( | |
269 const AutofillProfile& profile) { | |
270 sync_pb::EntitySpecifics specifics; | |
271 WriteAutofillProfile(profile, &specifics); | |
272 return SyncData::CreateLocalData(profile.guid(), profile.guid(), specifics); | |
273 } | |
274 | |
275 // static | |
276 void AutofillProfileSyncableService::WriteAutofillProfile( | |
dhollowa
2011/09/01 20:17:49
nit: please reorder method to match the .h file.
GeorgeY
2011/09/02 04:34:12
Done.
| |
277 const AutofillProfile& profile, | |
278 sync_pb::EntitySpecifics* profile_specifics) { | |
279 sync_pb::AutofillProfileSpecifics* specifics = | |
280 profile_specifics->MutableExtension(sync_pb::autofill_profile); | |
281 | |
282 // This would get compiled out in official builds. The caller is expected to | |
283 // pass in a valid profile object with valid guid.(i.e., the caller might | |
284 // have to a DCHECK and log before calling. Having to check in 2 places is | |
285 // not optimal.) | |
286 DCHECK(guid::IsValidGUID(profile.guid())); | |
287 | |
288 specifics->set_guid(profile.guid()); | |
289 specifics->set_name_first(UTF16ToUTF8(profile.GetInfo(NAME_FIRST))); | |
290 specifics->set_name_middle(UTF16ToUTF8(profile.GetInfo(NAME_MIDDLE))); | |
291 specifics->set_name_last(UTF16ToUTF8(profile.GetInfo(NAME_LAST))); | |
292 specifics->set_address_home_line1( | |
293 UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE1))); | |
294 specifics->set_address_home_line2( | |
295 UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE2))); | |
296 specifics->set_address_home_city(UTF16ToUTF8(profile.GetInfo( | |
297 ADDRESS_HOME_CITY))); | |
298 specifics->set_address_home_state(UTF16ToUTF8(profile.GetInfo( | |
299 ADDRESS_HOME_STATE))); | |
300 specifics->set_address_home_country(UTF16ToUTF8(profile.GetInfo( | |
301 ADDRESS_HOME_COUNTRY))); | |
302 specifics->set_address_home_zip(UTF16ToUTF8(profile.GetInfo( | |
303 ADDRESS_HOME_ZIP))); | |
304 specifics->set_email_address(UTF16ToUTF8(profile.GetInfo(EMAIL_ADDRESS))); | |
305 specifics->set_company_name(UTF16ToUTF8(profile.GetInfo(COMPANY_NAME))); | |
306 specifics->set_phone_fax_whole_number(UTF16ToUTF8(profile.GetInfo( | |
307 PHONE_FAX_WHOLE_NUMBER))); | |
308 specifics->set_phone_home_whole_number(UTF16ToUTF8(profile.GetInfo( | |
309 PHONE_HOME_WHOLE_NUMBER))); | |
310 } | |
311 | |
312 void AutofillProfileSyncableService::CreateGuidToProfileMap( | |
313 ScopedVector<AutofillProfile>* profiles, GuidToProfileMap* profile_map) { | |
Ilya Sherman
2011/09/01 07:22:31
nit: |profiles| should probably be passed by const
GeorgeY
2011/09/02 04:34:12
Nope. I needed ::iterator, not ::const_iterator.
C
| |
314 DCHECK(profile_map); | |
315 profile_map->clear(); | |
316 for (ScopedVector<AutofillProfile>::iterator i = profiles->begin(); | |
317 i != profiles->end(); ++i) | |
318 (*profile_map)[(*i)->guid()] = i; | |
319 } | |
320 | |
321 bool AutofillProfileSyncableService::CreateOrUpdateProfile( | |
dhollowa
2011/09/01 23:11:46
The name of this function doesn't really reflect w
| |
322 const SyncData& data, ScopedVector<AutofillProfile>* profiles, | |
Ilya Sherman
2011/09/01 07:22:31
nit: |profiles| does not seem to be used in this f
GeorgeY
2011/09/02 04:34:12
Was left from older version - removed.
| |
323 GuidToProfileMap* profile_map, DataBundle *bundle) { | |
dhollowa
2011/09/01 20:17:49
nit: s/ */* /
GeorgeY
2011/09/02 04:34:12
Done.
| |
324 DCHECK(profiles); | |
325 DCHECK(profile_map); | |
326 DCHECK(bundle); | |
327 | |
328 DCHECK_EQ(syncable::AUTOFILL_PROFILE, data.GetDataType()); | |
329 | |
330 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics(); | |
331 const sync_pb::AutofillProfileSpecifics& autofill_specifics( | |
332 specifics.GetExtension(sync_pb::autofill_profile)); | |
333 | |
334 bool added_new_profile = false; | |
335 | |
336 GuidToProfileMap::iterator it = profile_map->find( | |
337 autofill_specifics.guid()); | |
338 if (it != profile_map->end()) { | |
339 // Some profile that already present is synced. | |
340 if (OverwriteProfileWithServerData(*(it->second), autofill_specifics)) | |
341 bundle->updated_profiles.push_back(*(it->second)); | |
342 profile_map->erase(autofill_specifics.guid()); | |
343 } else { | |
344 // New profile synced. | |
345 AutofillProfile *p(new AutofillProfile(autofill_specifics.guid())); | |
dhollowa
2011/09/01 20:17:49
nit: s/ */* /
GeorgeY
2011/09/02 04:34:12
Done.
| |
346 OverwriteProfileWithServerData(p, autofill_specifics); | |
347 | |
348 // Check if profile appears under a different guid. | |
349 GuidToProfileMap::iterator i; | |
350 for (i = profile_map->begin(); i != profile_map->end(); ++i) { | |
351 if ((*(i->second))->Compare(*p) == 0) { | |
352 bundle->profiles_to_delete.push_back((*(i->second))->guid()); | |
353 VLOG(2) << "[AUTOFILL SYNC]" | |
354 << "Found in sync db but with a different guid: " | |
355 << UTF16ToUTF8((*(i->second))->GetInfo(NAME_FIRST)) | |
356 << UTF16ToUTF8((*(i->second))->GetInfo(NAME_LAST)) | |
357 << "New guid " << p->guid() << ". Profile to be deleted " | |
358 << (*(i->second))->guid(); | |
359 profile_map->erase(i); | |
lipalani1
2011/09/01 05:06:02
Profile is only erased from the map. Who is erasin
GeorgeY
2011/09/02 04:34:12
It is erased from web_db with a call to DataBundle
| |
360 break; | |
361 } | |
362 } | |
363 // Bundle takes ownership of new profiles only. | |
364 bundle->new_profiles.push_back(p); | |
365 added_new_profile = true; | |
Ilya Sherman
2011/09/01 07:22:31
I'm a little confused here: If the profile appear
GeorgeY
2011/09/02 04:34:12
We do not want to have multiple copies of a profil
| |
366 } | |
367 return added_new_profile; | |
368 } | |
369 | |
370 void AutofillProfileSyncableService::ActOnChange( | |
371 AutofillProfileChange* change) { | |
372 DCHECK(change->type() == AutofillProfileChange::REMOVE || change->profile()); | |
dhollowa
2011/09/01 23:11:46
This DCHECK is not quite accurate. Better would b
GeorgeY
2011/09/02 04:34:12
Further down we assert on any change that is not A
dhollowa
2011/09/07 01:48:15
Yes, but your checks wouldn't catch the case where
GeorgeY
2011/09/07 20:55:07
But that case is not and error - AFAIK, profile is
dhollowa
2011/09/07 21:10:24
That is not correct. See autofill_change.h:51
GeorgeY
2011/09/07 23:26:24
Done.
| |
373 DCHECK(sync_processor_); | |
374 SyncChangeList new_changes; | |
375 switch (change->type()) { | |
376 case AutofillProfileChange::ADD: | |
377 new_changes.push_back( | |
378 CreateChange(SyncChange::ACTION_ADD, *(change->profile()))); | |
dhollowa
2011/09/01 23:11:46
These CreateChange and CreateDeleteChange helpers
GeorgeY
2011/09/02 04:34:12
sure
| |
379 break; | |
380 case AutofillProfileChange::UPDATE: | |
381 new_changes.push_back( | |
382 CreateChange(SyncChange::ACTION_UPDATE, *(change->profile()))); | |
383 break; | |
384 case AutofillProfileChange::REMOVE: | |
385 new_changes.push_back(CreateDeleteChange(change->key())); | |
386 break; | |
387 default: | |
388 NOTREACHED(); | |
389 } | |
390 SyncError error = sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes); | |
391 if (error.IsSet()) { | |
lipalani1
2011/09/01 05:06:02
may be this is a vlog(warning)?
GeorgeY
2011/09/02 04:34:12
Sure.
| |
392 VLOG(2) << "[AUTOFILL SYNC]" | |
393 << " Failed processing change:" | |
394 << " Error:" << error.message() | |
395 << " Guid:" << change->profile()->guid(); | |
396 } | |
397 } | |
398 | |
399 bool AutofillProfileSyncableService::SaveChangesToWebData( | |
400 const DataBundle& bundle) { | |
401 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
402 | |
403 for (size_t i = 0; i < bundle.new_profiles.size(); i++) { | |
404 if (!web_database_->GetAutofillTable()->AddAutofillProfile( | |
405 *bundle.new_profiles[i])) | |
406 return false; | |
407 } | |
408 | |
409 for (size_t i = 0; i < bundle.updated_profiles.size(); i++) { | |
410 if (!web_database_->GetAutofillTable()->UpdateAutofillProfile( | |
411 *bundle.updated_profiles[i])) | |
412 return false; | |
413 } | |
414 | |
415 for (size_t i = 0; i< bundle.profiles_to_delete.size(); ++i) { | |
416 if (!web_database_->GetAutofillTable()->RemoveAutofillProfile( | |
417 bundle.profiles_to_delete[i])) | |
418 return false; | |
419 } | |
420 return true; | |
421 } | |
422 | |
423 AutofillProfileSyncableService::DataBundle::DataBundle() {} | |
424 | |
425 AutofillProfileSyncableService::DataBundle::~DataBundle() { | |
426 STLDeleteElements(&new_profiles); | |
427 } | |
428 | |
429 } // namespace browser_sync | |
430 | |
OLD | NEW |