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