OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/sync/glue/autofill_data_type_controller.h" | 5 #include "chrome/browser/sync/glue/autofill_data_type_controller.h" |
6 | 6 |
7 #include "base/logging.h" | |
8 #include "base/metrics/histogram.h" | 7 #include "base/metrics/histogram.h" |
9 #include "base/task.h" | 8 #include "base/task.h" |
10 #include "base/time.h" | |
11 #include "chrome/browser/profiles/profile.h" | 9 #include "chrome/browser/profiles/profile.h" |
12 #include "chrome/browser/sync/glue/autofill_change_processor.h" | |
13 #include "chrome/browser/sync/glue/autofill_model_associator.h" | |
14 #include "chrome/browser/sync/profile_sync_factory.h" | 10 #include "chrome/browser/sync/profile_sync_factory.h" |
15 #include "chrome/browser/sync/profile_sync_service.h" | 11 #include "chrome/browser/sync/profile_sync_service.h" |
16 #include "chrome/browser/webdata/web_data_service.h" | 12 #include "chrome/browser/webdata/web_data_service.h" |
17 #include "content/browser/browser_thread.h" | 13 #include "content/browser/browser_thread.h" |
18 #include "content/common/notification_service.h" | 14 #include "content/common/notification_service.h" |
| 15 #include "content/common/notification_source.h" |
| 16 #include "content/common/notification_type.h" |
19 | 17 |
20 namespace browser_sync { | 18 namespace browser_sync { |
21 | 19 |
22 AutofillDataTypeController::AutofillDataTypeController( | 20 AutofillDataTypeController::AutofillDataTypeController( |
23 ProfileSyncFactory* profile_sync_factory, | 21 ProfileSyncFactory* profile_sync_factory, |
24 Profile* profile, | 22 Profile* profile) |
25 ProfileSyncService* sync_service) | 23 : NonFrontendDataTypeController(profile_sync_factory, |
26 : profile_sync_factory_(profile_sync_factory), | 24 profile), |
27 profile_(profile), | 25 personal_data_(NULL) { |
28 sync_service_(sync_service), | |
29 state_(NOT_RUNNING), | |
30 personal_data_(NULL), | |
31 abort_association_(false), | |
32 abort_association_complete_(false, false), | |
33 datatype_stopped_(false, false) { | |
34 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
35 DCHECK(profile_sync_factory); | |
36 DCHECK(profile); | |
37 DCHECK(sync_service); | |
38 } | 26 } |
39 | 27 |
40 AutofillDataTypeController::~AutofillDataTypeController() { | 28 AutofillDataTypeController::~AutofillDataTypeController() { |
41 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 29 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
42 | |
43 // TODO(zea): remove once crbug.com/61804 is resolved. | |
44 CHECK_EQ(state_, NOT_RUNNING) << "AutofillDataTypeController destroyed " | |
45 << "without being stopped."; | |
46 CHECK(!change_processor_.get()) << "AutofillDataTypeController destroyed " | |
47 << "while holding a change processor."; | |
48 } | 30 } |
49 | 31 |
50 void AutofillDataTypeController::Start(StartCallback* start_callback) { | 32 bool AutofillDataTypeController::StartModels() { |
51 VLOG(1) << "Starting autofill data controller."; | |
52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 33 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
53 DCHECK(start_callback); | 34 DCHECK_EQ(state(), MODEL_STARTING); |
54 if (state() != NOT_RUNNING) { | |
55 start_callback->Run(BUSY, FROM_HERE); | |
56 delete start_callback; | |
57 return; | |
58 } | |
59 | |
60 start_callback_.reset(start_callback); | |
61 abort_association_ = false; | |
62 | |
63 // Waiting for the personal data is subtle: we do this as the PDM resets | 35 // Waiting for the personal data is subtle: we do this as the PDM resets |
64 // its cache of unique IDs once it gets loaded. If we were to proceed with | 36 // its cache of unique IDs once it gets loaded. If we were to proceed with |
65 // association, the local ids in the mappings would wind up colliding. | 37 // association, the local ids in the mappings would wind up colliding. |
66 personal_data_ = profile_->GetPersonalDataManager(); | 38 personal_data_ = profile()->GetPersonalDataManager(); |
67 if (!personal_data_->IsDataLoaded()) { | 39 if (!personal_data_->IsDataLoaded()) { |
68 set_state(MODEL_STARTING); | |
69 personal_data_->SetObserver(this); | 40 personal_data_->SetObserver(this); |
70 return; | 41 return false; |
71 } | 42 } |
72 | 43 |
73 ContinueStartAfterPersonalDataLoaded(); | 44 web_data_service_ = profile()->GetWebDataService(Profile::IMPLICIT_ACCESS); |
| 45 if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) { |
| 46 return true; |
| 47 } else { |
| 48 notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED, |
| 49 NotificationService::AllSources()); |
| 50 return false; |
| 51 } |
74 } | 52 } |
75 | 53 |
76 void AutofillDataTypeController::ContinueStartAfterPersonalDataLoaded() { | 54 void AutofillDataTypeController::OnPersonalDataLoaded() { |
77 web_data_service_ = profile_->GetWebDataService(Profile::IMPLICIT_ACCESS); | 55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 56 DCHECK_EQ(state(), MODEL_STARTING); |
| 57 personal_data_->RemoveObserver(this); |
| 58 web_data_service_ = profile()->GetWebDataService(Profile::IMPLICIT_ACCESS); |
78 if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) { | 59 if (web_data_service_.get() && web_data_service_->IsDatabaseLoaded()) { |
79 set_state(ASSOCIATING); | 60 set_state(ASSOCIATING); |
80 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, | 61 if (!StartAssociationAsync()) { |
81 NewRunnableMethod( | 62 StartDoneImpl(ASSOCIATION_FAILED, NOT_RUNNING, FROM_HERE); |
82 this, | 63 } |
83 &AutofillDataTypeController::StartImpl)); | |
84 } else { | 64 } else { |
85 set_state(MODEL_STARTING); | |
86 notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED, | 65 notification_registrar_.Add(this, NotificationType::WEB_DATABASE_LOADED, |
87 NotificationService::AllSources()); | 66 NotificationService::AllSources()); |
88 } | 67 } |
89 } | 68 } |
90 | 69 |
91 void AutofillDataTypeController::OnPersonalDataLoaded() { | |
92 DCHECK_EQ(state_, MODEL_STARTING); | |
93 personal_data_->RemoveObserver(this); | |
94 ContinueStartAfterPersonalDataLoaded(); | |
95 } | |
96 | |
97 void AutofillDataTypeController::Observe(NotificationType type, | 70 void AutofillDataTypeController::Observe(NotificationType type, |
98 const NotificationSource& source, | 71 const NotificationSource& source, |
99 const NotificationDetails& details) { | 72 const NotificationDetails& details) { |
100 VLOG(1) << "Web database loaded observed."; | 73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 74 DCHECK_EQ(state(), MODEL_STARTING); |
101 notification_registrar_.RemoveAll(); | 75 notification_registrar_.RemoveAll(); |
102 set_state(ASSOCIATING); | 76 set_state(ASSOCIATING); |
103 BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, | 77 if (!StartAssociationAsync()) { |
104 NewRunnableMethod( | 78 StartDoneImpl(ASSOCIATION_FAILED, NOT_RUNNING, FROM_HERE); |
105 this, | 79 } |
106 &AutofillDataTypeController::StartImpl)); | |
107 } | 80 } |
108 | 81 |
109 // TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of | 82 bool AutofillDataTypeController::StartAssociationAsync() { |
110 // distinguishing chrome shutdown from sync shutdown, we should be able to avoid | |
111 // this (http://crbug.com/55662). Further, all this functionality should be | |
112 // abstracted to a higher layer, where we could ensure all datatypes are doing | |
113 // the same thing (http://crbug.com/76232). | |
114 void AutofillDataTypeController::Stop() { | |
115 VLOG(1) << "Stopping autofill data type controller."; | |
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 84 DCHECK_EQ(state(), ASSOCIATING); |
| 85 return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 86 NewRunnableMethod( |
| 87 this, |
| 88 &AutofillDataTypeController::StartAssociation)); |
| 89 } |
117 | 90 |
118 // If Stop() is called while Start() is waiting for association to | 91 void AutofillDataTypeController::CreateSyncComponents() { |
119 // complete, we need to abort the association and wait for the DB | 92 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
120 // thread to finish the StartImpl() task. | 93 DCHECK_EQ(state(), ASSOCIATING); |
121 if (state_ == ASSOCIATING) { | 94 ProfileSyncFactory::SyncComponents sync_components = |
122 { | 95 profile_sync_factory()-> |
123 base::AutoLock lock(abort_association_lock_); | 96 CreateAutofillSyncComponents( |
124 abort_association_ = true; | 97 profile_sync_service(), |
125 if (model_associator_.get()) | 98 web_data_service_->GetDatabase(), |
126 model_associator_->AbortAssociation(); | 99 personal_data_, |
127 } | 100 this); |
128 // Wait for the model association to abort. | 101 set_model_associator(sync_components.model_associator); |
129 abort_association_complete_.Wait(); | 102 set_change_processor(sync_components.change_processor); |
130 StartDoneImpl(ABORTED, STOPPING, FROM_HERE); | 103 } |
131 } | |
132 | 104 |
133 // If Stop() is called while Start() is waiting for the personal | 105 void AutofillDataTypeController::StopModels() { |
134 // data manager or web data service to load, abort the start. | 106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
135 if (state_ == MODEL_STARTING) | 107 DCHECK(state() == STOPPING || state() == NOT_RUNNING); |
136 StartDoneImpl(ABORTED, STOPPING, FROM_HERE); | |
137 | |
138 DCHECK(!start_callback_.get()); | |
139 | |
140 // Deactivate the change processor on the UI thread. We dont want to listen | |
141 // for any more changes or process them from server. | |
142 notification_registrar_.RemoveAll(); | 108 notification_registrar_.RemoveAll(); |
143 personal_data_->RemoveObserver(this); | 109 personal_data_->RemoveObserver(this); |
144 if (change_processor_ != NULL && change_processor_->IsRunning()) | |
145 sync_service_->DeactivateDataType(this, change_processor_.get()); | |
146 | |
147 set_state(NOT_RUNNING); | |
148 if (BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, | |
149 NewRunnableMethod( | |
150 this, | |
151 &AutofillDataTypeController::StopImpl))) { | |
152 // We need to ensure the data type has fully stoppped before continuing. In | |
153 // particular, during shutdown we may attempt to destroy the | |
154 // profile_sync_service before we've removed its observers (BUG 61804). | |
155 datatype_stopped_.Wait(); | |
156 } else if (change_processor_.get()) { | |
157 // TODO(zea): remove once crbug.com/61804 is resolved. | |
158 LOG(FATAL) << "AutofillDataTypeController::Stop() called after DB thread" | |
159 << " killed."; | |
160 } | |
161 CHECK(!change_processor_.get()) << "AutofillChangeProcessor not released."; | |
162 } | 110 } |
163 | 111 |
164 bool AutofillDataTypeController::enabled() { | 112 bool AutofillDataTypeController::StopAssociationAsync() { |
165 return true; | 113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 114 DCHECK_EQ(state(), STOPPING); |
| 115 return BrowserThread::PostTask(BrowserThread::DB, FROM_HERE, |
| 116 NewRunnableMethod( |
| 117 this, |
| 118 &AutofillDataTypeController::StopAssociation)); |
166 } | 119 } |
167 | 120 |
168 syncable::ModelType AutofillDataTypeController::type() const { | 121 syncable::ModelType AutofillDataTypeController::type() const { |
169 return syncable::AUTOFILL; | 122 return syncable::AUTOFILL; |
170 } | 123 } |
171 | 124 |
172 browser_sync::ModelSafeGroup AutofillDataTypeController::model_safe_group() | 125 browser_sync::ModelSafeGroup AutofillDataTypeController::model_safe_group() |
173 const { | 126 const { |
174 return browser_sync::GROUP_DB; | 127 return browser_sync::GROUP_DB; |
175 } | 128 } |
176 | 129 |
177 std::string AutofillDataTypeController::name() const { | 130 void AutofillDataTypeController::RecordUnrecoverableError( |
178 // For logging only. | |
179 return "autofill"; | |
180 } | |
181 | |
182 DataTypeController::State AutofillDataTypeController::state() const { | |
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
184 return state_; | |
185 } | |
186 | |
187 ProfileSyncFactory::SyncComponents | |
188 AutofillDataTypeController::CreateSyncComponents( | |
189 ProfileSyncService* profile_sync_service, | |
190 WebDatabase* web_database, | |
191 PersonalDataManager* personal_data, | |
192 browser_sync::UnrecoverableErrorHandler* error_handler) { | |
193 return profile_sync_factory_->CreateAutofillSyncComponents( | |
194 profile_sync_service, | |
195 web_database, | |
196 personal_data, | |
197 this); | |
198 } | |
199 | |
200 void AutofillDataTypeController::StartImpl() { | |
201 VLOG(1) << "Autofill data type controller StartImpl called."; | |
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
203 // No additional services need to be started before we can proceed | |
204 // with model association. | |
205 { | |
206 base::AutoLock lock(abort_association_lock_); | |
207 if (abort_association_) { | |
208 abort_association_complete_.Signal(); | |
209 return; | |
210 } | |
211 ProfileSyncFactory::SyncComponents sync_components = | |
212 CreateSyncComponents( | |
213 sync_service_, | |
214 web_data_service_->GetDatabase(), | |
215 profile_->GetPersonalDataManager(), | |
216 this); | |
217 model_associator_.reset(sync_components.model_associator); | |
218 change_processor_.reset(sync_components.change_processor); | |
219 } | |
220 | |
221 if (!model_associator_->CryptoReadyIfNecessary()) { | |
222 StartFailed(NEEDS_CRYPTO); | |
223 return; | |
224 } | |
225 | |
226 bool sync_has_nodes = false; | |
227 if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) { | |
228 StartFailed(UNRECOVERABLE_ERROR); | |
229 return; | |
230 } | |
231 | |
232 base::TimeTicks start_time = base::TimeTicks::Now(); | |
233 bool merge_success = model_associator_->AssociateModels(); | |
234 UMA_HISTOGRAM_TIMES("Sync.AutofillAssociationTime", | |
235 base::TimeTicks::Now() - start_time); | |
236 VLOG(1) << "Autofill association time: " << | |
237 (base::TimeTicks::Now() - start_time).InSeconds(); | |
238 if (!merge_success) { | |
239 StartFailed(ASSOCIATION_FAILED); | |
240 return; | |
241 } | |
242 | |
243 sync_service_->ActivateDataType(this, change_processor_.get()); | |
244 StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING); | |
245 } | |
246 | |
247 void AutofillDataTypeController::StartDone( | |
248 DataTypeController::StartResult result, | |
249 DataTypeController::State new_state) { | |
250 VLOG(1) << "Autofill data type controller StartDone called."; | |
251 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
252 | |
253 abort_association_complete_.Signal(); | |
254 base::AutoLock lock(abort_association_lock_); | |
255 if (!abort_association_) { | |
256 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
257 NewRunnableMethod( | |
258 this, | |
259 &AutofillDataTypeController::StartDoneImpl, | |
260 result, | |
261 new_state, | |
262 FROM_HERE)); | |
263 } | |
264 } | |
265 | |
266 void AutofillDataTypeController::StartDoneImpl( | |
267 DataTypeController::StartResult result, | |
268 DataTypeController::State new_state, | |
269 const tracked_objects::Location& location) { | |
270 VLOG(1) << "Autofill data type controller StartDoneImpl called."; | |
271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
272 | |
273 set_state(new_state); | |
274 start_callback_->Run(result, location); | |
275 start_callback_.reset(); | |
276 | |
277 if (result == UNRECOVERABLE_ERROR || result == ASSOCIATION_FAILED) { | |
278 UMA_HISTOGRAM_ENUMERATION("Sync.AutofillStartFailures", | |
279 result, | |
280 MAX_START_RESULT); | |
281 } | |
282 } | |
283 | |
284 void AutofillDataTypeController::StopImpl() { | |
285 VLOG(1) << "Autofill data type controller StopImpl called."; | |
286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
287 | |
288 if (model_associator_ != NULL) | |
289 model_associator_->DisassociateModels(); | |
290 | |
291 change_processor_.reset(); | |
292 model_associator_.reset(); | |
293 | |
294 datatype_stopped_.Signal(); | |
295 } | |
296 | |
297 void AutofillDataTypeController::StartFailed(StartResult result) { | |
298 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
299 change_processor_.reset(); | |
300 model_associator_.reset(); | |
301 StartDone(result, NOT_RUNNING); | |
302 } | |
303 | |
304 void AutofillDataTypeController::OnUnrecoverableError( | |
305 const tracked_objects::Location& from_here, | 131 const tracked_objects::Location& from_here, |
306 const std::string& message) { | 132 const std::string& message) { |
307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); | 133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
308 BrowserThread::PostTask( | |
309 BrowserThread::UI, FROM_HERE, | |
310 NewRunnableMethod(this, | |
311 &AutofillDataTypeController::OnUnrecoverableErrorImpl, | |
312 from_here, message)); | |
313 UMA_HISTOGRAM_COUNTS("Sync.AutofillRunFailures", 1); | 134 UMA_HISTOGRAM_COUNTS("Sync.AutofillRunFailures", 1); |
314 } | 135 } |
315 | 136 |
316 void AutofillDataTypeController::OnUnrecoverableErrorImpl( | 137 void AutofillDataTypeController::RecordAssociationTime(base::TimeDelta time) { |
317 const tracked_objects::Location& from_here, | 138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
318 const std::string& message) { | 139 UMA_HISTOGRAM_TIMES("Sync.AutofillAssociationTime", time); |
| 140 } |
| 141 |
| 142 void AutofillDataTypeController::RecordStartFailure(StartResult result) { |
319 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
320 sync_service_->OnUnrecoverableError(from_here, message); | 144 UMA_HISTOGRAM_ENUMERATION("Sync.AutofillStartFailures", |
| 145 result, |
| 146 MAX_START_RESULT); |
| 147 } |
| 148 |
| 149 PersonalDataManager* AutofillDataTypeController::personal_data() const { |
| 150 return personal_data_; |
| 151 } |
| 152 |
| 153 WebDataService* AutofillDataTypeController::web_data_service() const { |
| 154 return web_data_service_; |
321 } | 155 } |
322 | 156 |
323 } // namespace browser_sync | 157 } // namespace browser_sync |
OLD | NEW |