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_change_processor.h" | |
6 | |
7 #include <string> | |
8 #include <vector> | |
9 | |
10 #include "base/string_util.h" | |
11 #include "base/tracked.h" | |
12 #include "base/utf_string_conversions.h" | |
13 #include "chrome/browser/autofill/autofill_profile.h" | |
14 #include "chrome/browser/autofill/personal_data_manager.h" | |
15 #include "chrome/browser/sync/glue/autofill_profile_model_associator.h" | |
16 #include "chrome/browser/sync/glue/change_processor.h" | |
17 #include "chrome/browser/sync/glue/do_optimistic_refresh_task.h" | |
18 #include "chrome/browser/sync/internal_api/read_node.h" | |
19 #include "chrome/browser/sync/internal_api/sync_manager.h" | |
20 #include "chrome/browser/sync/internal_api/write_node.h" | |
21 #include "chrome/browser/sync/internal_api/write_transaction.h" | |
22 #include "chrome/browser/sync/unrecoverable_error_handler.h" | |
23 #include "chrome/browser/webdata/autofill_change.h" | |
24 #include "chrome/browser/webdata/autofill_table.h" | |
25 #include "chrome/browser/webdata/web_database.h" | |
26 #include "chrome/common/chrome_notification_types.h" | |
27 #include "chrome/common/guid.h" | |
28 #include "content/common/notification_registrar.h" | |
29 #include "content/common/notification_service.h" | |
30 | |
31 namespace browser_sync { | |
32 | |
33 AutofillProfileChangeProcessor::AutofillProfileChangeProcessor( | |
34 AutofillProfileModelAssociator *model_associator, | |
35 WebDatabase* web_database, | |
36 PersonalDataManager* personal_data_manager, | |
37 UnrecoverableErrorHandler* error_handler) | |
38 : ChangeProcessor(error_handler), | |
39 model_associator_(model_associator), | |
40 observing_(false), | |
41 web_database_(web_database), | |
42 personal_data_(personal_data_manager) { | |
43 DCHECK(model_associator); | |
44 DCHECK(web_database); | |
45 DCHECK(error_handler); | |
46 DCHECK(personal_data_manager); | |
47 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
48 | |
49 StartObserving(); | |
50 } | |
51 | |
52 AutofillProfileChangeProcessor::~AutofillProfileChangeProcessor() {} | |
53 | |
54 void AutofillProfileChangeProcessor::ApplyChangesFromSyncModel( | |
55 const sync_api::BaseTransaction *write_trans, | |
56 const sync_api::SyncManager::ChangeRecord* changes, | |
57 int change_count) { | |
58 | |
59 ScopedStopObserving<AutofillProfileChangeProcessor> observer(this); | |
60 | |
61 sync_api::ReadNode autofill_profile_root(write_trans); | |
62 if (!autofill_profile_root.InitByTagLookup(kAutofillProfileTag)) { | |
63 error_handler()->OnUnrecoverableError(FROM_HERE, | |
64 "Autofill Profile root node lookup failed"); | |
65 return; | |
66 } | |
67 | |
68 for (int i = 0; i < change_count; ++i) { | |
69 if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == | |
70 changes[i].action) { | |
71 DCHECK(changes[i].specifics.HasExtension( | |
72 sync_pb::autofill_profile)); | |
73 | |
74 const sync_pb::AutofillProfileSpecifics& specifics = | |
75 changes[i].specifics.GetExtension(sync_pb::autofill_profile); | |
76 | |
77 autofill_changes_.push_back(AutofillProfileChangeRecord(changes[i].action, | |
78 changes[i].id, | |
79 specifics)); | |
80 continue; | |
81 } | |
82 | |
83 // If it is not a delete. | |
84 sync_api::ReadNode sync_node(write_trans); | |
85 if (!sync_node.InitByIdLookup(changes[i].id)) { | |
86 LOG(ERROR) << "Could not find the id in sync db " << changes[i].id; | |
87 continue; | |
88 } | |
89 | |
90 const sync_pb::AutofillProfileSpecifics& autofill( | |
91 sync_node.GetAutofillProfileSpecifics()); | |
92 | |
93 autofill_changes_.push_back(AutofillProfileChangeRecord(changes[i].action, | |
94 changes[i].id, | |
95 autofill)); | |
96 } | |
97 } | |
98 | |
99 void AutofillProfileChangeProcessor::Observe(int type, | |
100 const NotificationSource& source, | |
101 const NotificationDetails& details) { | |
102 DCHECK_EQ(type, chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED); | |
103 WebDataService* wds = Source<WebDataService>(source).ptr(); | |
104 | |
105 if (!wds || wds->GetDatabase() != web_database_) | |
106 return; | |
107 | |
108 sync_api::WriteTransaction trans(FROM_HERE, share_handle()); | |
109 sync_api::ReadNode autofill_root(&trans); | |
110 if (!autofill_root.InitByTagLookup(kAutofillProfileTag)) { | |
111 error_handler()->OnUnrecoverableError(FROM_HERE, | |
112 "Server did not create a tolp level node"); | |
113 return; | |
114 } | |
115 | |
116 AutofillProfileChange* change = Details<AutofillProfileChange>(details).ptr(); | |
117 | |
118 ActOnChange(change, &trans, autofill_root); | |
119 } | |
120 | |
121 void AutofillProfileChangeProcessor::ActOnChange( | |
122 AutofillProfileChange* change, | |
123 sync_api::WriteTransaction* trans, | |
124 sync_api::ReadNode& autofill_root) { | |
125 DCHECK(change->type() == AutofillProfileChange::REMOVE || change->profile()); | |
126 switch (change->type()) { | |
127 case AutofillProfileChange::ADD: { | |
128 AddAutofillProfileSyncNode(trans, autofill_root, *(change->profile())); | |
129 break; | |
130 } | |
131 case AutofillProfileChange::UPDATE: { | |
132 int64 sync_id = model_associator_->GetSyncIdFromChromeId(change->key()); | |
133 if (sync_api::kInvalidId == sync_id) { | |
134 LOG(ERROR) << "Sync id is not found for " << change->key(); | |
135 break; | |
136 } | |
137 sync_api::WriteNode node(trans); | |
138 if (!node.InitByIdLookup(sync_id)) { | |
139 LOG(ERROR) << "Could not find sync node for id " << sync_id; | |
140 break; | |
141 } | |
142 | |
143 WriteAutofillProfile(*(change->profile()), &node); | |
144 break; | |
145 } | |
146 case AutofillProfileChange::REMOVE: { | |
147 int64 sync_id = model_associator_->GetSyncIdFromChromeId(change->key()); | |
148 if (sync_api::kInvalidId == sync_id) { | |
149 LOG(ERROR) << "Sync id is not found for " << change->key(); | |
150 break; | |
151 } | |
152 sync_api::WriteNode node(trans); | |
153 if (!node.InitByIdLookup(sync_id)) { | |
154 LOG(ERROR) << "Could not find sync node for id " << sync_id; | |
155 break; | |
156 } | |
157 node.Remove(); | |
158 model_associator_->Disassociate(sync_id); | |
159 break; | |
160 } | |
161 default: | |
162 NOTREACHED(); | |
163 } | |
164 } | |
165 | |
166 void AutofillProfileChangeProcessor::CommitChangesFromSyncModel() { | |
167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
168 | |
169 if (!running()) | |
170 return; | |
171 | |
172 ScopedStopObserving<AutofillProfileChangeProcessor> observer(this); | |
173 | |
174 for (unsigned int i = 0;i < autofill_changes_.size(); ++i) { | |
175 if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == | |
176 autofill_changes_[i].action_) { | |
177 if (!web_database_->GetAutofillTable()->RemoveAutofillProfile( | |
178 autofill_changes_[i].profile_specifics_.guid())) { | |
179 LOG(ERROR) << "could not delete the profile " << | |
180 autofill_changes_[i].profile_specifics_.guid(); | |
181 continue; | |
182 } | |
183 continue; | |
184 } | |
185 | |
186 // Now for updates and adds. | |
187 ApplyAutofillProfileChange(autofill_changes_[i].action_, | |
188 autofill_changes_[i].profile_specifics_, | |
189 autofill_changes_[i].id_); | |
190 } | |
191 | |
192 autofill_changes_.clear(); | |
193 | |
194 PostOptimisticRefreshTask(); | |
195 } | |
196 | |
197 void AutofillProfileChangeProcessor::PostOptimisticRefreshTask() { | |
198 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
199 new DoOptimisticRefreshForAutofill( | |
200 personal_data_)); | |
201 } | |
202 | |
203 void AutofillProfileChangeProcessor::ApplyAutofillProfileChange( | |
204 sync_api::SyncManager::ChangeRecord::Action action, | |
205 const sync_pb::AutofillProfileSpecifics& profile_specifics, | |
206 int64 sync_id) { | |
207 | |
208 DCHECK_NE(sync_api::SyncManager::ChangeRecord::ACTION_DELETE, action); | |
209 switch (action) { | |
210 case sync_api::SyncManager::ChangeRecord::ACTION_ADD: { | |
211 if (guid::IsValidGUID(profile_specifics.guid()) == false) { | |
212 NOTREACHED() << "Guid from the server is invalid " << | |
213 profile_specifics.guid(); | |
214 return; | |
215 } | |
216 AutofillProfile p(profile_specifics.guid()); | |
217 AutofillProfileModelAssociator::OverwriteProfileWithServerData(&p, | |
218 profile_specifics); | |
219 if (!web_database_->GetAutofillTable()->AddAutofillProfile(p)) { | |
220 LOG(ERROR) << "could not add autofill profile for guid " << p.guid(); | |
221 break; | |
222 } | |
223 | |
224 // Now that the node has been succesfully created we can associate it. | |
225 std::string guid = p.guid(); | |
226 model_associator_->Associate(&guid, sync_id); | |
227 break; | |
228 } | |
229 case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: { | |
230 AutofillProfile *p; | |
231 if (!web_database_->GetAutofillTable()->GetAutofillProfile( | |
232 profile_specifics.guid(), &p)) { | |
233 LOG(ERROR) << "Could not find the autofill profile to update for " << | |
234 profile_specifics.guid(); | |
235 break; | |
236 } | |
237 scoped_ptr<AutofillProfile> autofill_pointer(p); | |
238 AutofillProfileModelAssociator::OverwriteProfileWithServerData( | |
239 autofill_pointer.get(), | |
240 profile_specifics); | |
241 | |
242 if (!web_database_->GetAutofillTable()->UpdateAutofillProfile( | |
243 *(autofill_pointer.get()))) { | |
244 LOG(ERROR) << "Could not update autofill profile for " << | |
245 profile_specifics.guid(); | |
246 break; | |
247 } | |
248 break; | |
249 } | |
250 default: { | |
251 NOTREACHED(); | |
252 break; | |
253 } | |
254 } | |
255 } | |
256 | |
257 void AutofillProfileChangeProcessor::RemoveSyncNode(const std::string& guid, | |
258 sync_api::WriteTransaction* trans) { | |
259 sync_api::WriteNode node(trans); | |
260 int64 sync_id = model_associator_->GetSyncIdFromChromeId(guid); | |
261 if (sync_api::kInvalidId == sync_id) { | |
262 LOG(ERROR) << "Could not find the node in associator " << guid; | |
263 return; | |
264 } | |
265 | |
266 if (!node.InitByIdLookup(sync_id)) { | |
267 LOG(ERROR) << "Could not find the sync node for " << guid; | |
268 return; | |
269 } | |
270 | |
271 model_associator_->Disassociate(sync_id); | |
272 node.Remove(); | |
273 } | |
274 | |
275 void AutofillProfileChangeProcessor::AddAutofillProfileSyncNode( | |
276 sync_api::WriteTransaction* trans, | |
277 sync_api::BaseNode& autofill_profile_root, | |
278 const AutofillProfile& profile) { | |
279 | |
280 std::string guid = profile.guid(); | |
281 | |
282 if (guid::IsValidGUID(guid) == false) { | |
283 DCHECK(false) << "Guid set on the profile is invalid " << guid; | |
284 return; | |
285 } | |
286 | |
287 sync_api::WriteNode node(trans); | |
288 if (!node.InitUniqueByCreation(syncable::AUTOFILL_PROFILE, | |
289 autofill_profile_root, | |
290 profile.guid())) { | |
291 LOG(ERROR) << "could not create a sync node "; | |
292 return; | |
293 } | |
294 | |
295 node.SetTitle(UTF8ToWide(profile.guid())); | |
296 | |
297 WriteAutofillProfile(profile, &node); | |
298 | |
299 model_associator_->Associate(&guid, node.GetId()); | |
300 } | |
301 | |
302 void AutofillProfileChangeProcessor::StartObserving() { | |
303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
304 notification_registrar_.Add(this, | |
305 chrome::NOTIFICATION_AUTOFILL_PROFILE_CHANGED, | |
306 NotificationService::AllSources()); | |
307 } | |
308 | |
309 void AutofillProfileChangeProcessor::StopObserving() { | |
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
311 notification_registrar_.RemoveAll(); | |
312 } | |
313 | |
314 void AutofillProfileChangeProcessor::WriteAutofillProfile( | |
315 const AutofillProfile& profile, | |
316 sync_api::WriteNode* node) { | |
317 sync_pb::AutofillProfileSpecifics specifics; | |
318 | |
319 // This would get compiled out in official builds. The caller is expected to | |
320 // pass in a valid profile object with valid guid.(i.e., the caller might | |
321 // have to a DCHECK and log before calling. Having to check in 2 places is | |
322 // not optimal.) | |
323 DCHECK(guid::IsValidGUID(profile.guid())); | |
324 | |
325 specifics.set_guid(profile.guid()); | |
326 specifics.set_name_first(UTF16ToUTF8(profile.GetInfo(NAME_FIRST))); | |
327 specifics.set_name_middle(UTF16ToUTF8(profile.GetInfo(NAME_MIDDLE))); | |
328 specifics.set_name_last(UTF16ToUTF8(profile.GetInfo(NAME_LAST))); | |
329 specifics.set_address_home_line1( | |
330 UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE1))); | |
331 specifics.set_address_home_line2( | |
332 UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE2))); | |
333 specifics.set_address_home_city(UTF16ToUTF8(profile.GetInfo( | |
334 ADDRESS_HOME_CITY))); | |
335 specifics.set_address_home_state(UTF16ToUTF8(profile.GetInfo( | |
336 ADDRESS_HOME_STATE))); | |
337 specifics.set_address_home_country(UTF16ToUTF8(profile.GetInfo( | |
338 ADDRESS_HOME_COUNTRY))); | |
339 specifics.set_address_home_zip(UTF16ToUTF8(profile.GetInfo( | |
340 ADDRESS_HOME_ZIP))); | |
341 specifics.set_email_address(UTF16ToUTF8(profile.GetInfo(EMAIL_ADDRESS))); | |
342 specifics.set_company_name(UTF16ToUTF8(profile.GetInfo(COMPANY_NAME))); | |
343 specifics.set_phone_fax_whole_number(UTF16ToUTF8(profile.GetInfo( | |
344 PHONE_FAX_WHOLE_NUMBER))); | |
345 specifics.set_phone_home_whole_number(UTF16ToUTF8(profile.GetInfo( | |
346 PHONE_HOME_WHOLE_NUMBER))); | |
347 node->SetAutofillProfileSpecifics(specifics); | |
348 } | |
349 | |
350 } // namespace browser_sync | |
OLD | NEW |