• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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