1 // Copyright (c) 2012 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.
5 #include "chrome/browser/sync/glue/non_frontend_data_type_controller.h"
8 #include "base/callback.h"
9 #include "base/logging.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/sync/glue/chrome_report_unrecoverable_error.h"
12 #include "chrome/browser/sync/profile_sync_components_factory.h"
13 #include "chrome/browser/sync/profile_sync_service.h"
14 #include "components/sync_driver/change_processor.h"
15 #include "components/sync_driver/model_associator.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "sync/api/sync_error.h"
18 #include "sync/internal_api/public/base/model_type.h"
19 #include "sync/internal_api/public/util/weak_handle.h"
20 #include "sync/util/data_type_histogram.h"
22 using content::BrowserThread;
24 namespace browser_sync {
26 class NonFrontendDataTypeController::BackendComponentsContainer {
28 explicit BackendComponentsContainer(
29 NonFrontendDataTypeController* controller);
30 ~BackendComponentsContainer();
35 bool CreateComponents();
38 // For creating components.
39 NonFrontendDataTypeController* controller_;
40 base::Lock controller_lock_;
42 syncer::ModelType type_;
44 // For returning association results to controller on UI.
45 syncer::WeakHandle<NonFrontendDataTypeController> controller_handle_;
47 scoped_ptr<sync_driver::AssociatorInterface> model_associator_;
48 scoped_ptr<sync_driver::ChangeProcessor> change_processor_;
51 NonFrontendDataTypeController::
52 BackendComponentsContainer::BackendComponentsContainer(
53 NonFrontendDataTypeController* controller)
54 : controller_(controller),
55 type_(controller->type()) {
56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
58 syncer::MakeWeakHandle(controller_->weak_ptr_factory_.GetWeakPtr());
61 NonFrontendDataTypeController::
62 BackendComponentsContainer::~BackendComponentsContainer() {
63 if (model_associator_)
64 model_associator_->DisassociateModels();
67 void NonFrontendDataTypeController::BackendComponentsContainer::Run() {
68 DCHECK(controller_->IsOnBackendThread());
69 if (CreateComponents())
74 NonFrontendDataTypeController::BackendComponentsContainer::CreateComponents() {
75 base::AutoLock al(controller_lock_);
77 DVLOG(1) << "Controller was stopped before sync components are created.";
81 ProfileSyncComponentsFactory::SyncComponents sync_components =
82 controller_->CreateSyncComponents();
83 model_associator_.reset(sync_components.model_associator);
84 change_processor_.reset(sync_components.change_processor);
88 void NonFrontendDataTypeController::BackendComponentsContainer::Associate() {
89 CHECK(model_associator_);
91 bool succeeded = false;
93 browser_sync::NonFrontendDataTypeController::AssociationResult result(type_);
94 if (!model_associator_->CryptoReadyIfNecessary()) {
95 result.needs_crypto = true;
97 base::TimeTicks start_time = base::TimeTicks::Now();
99 if (!model_associator_->SyncModelHasUserCreatedNodes(
100 &result.sync_has_nodes)) {
101 result.unrecoverable_error = true;
102 result.error = syncer::SyncError(FROM_HERE,
103 syncer::SyncError::UNRECOVERABLE_ERROR,
104 "Failed to load sync nodes",
107 result.error = model_associator_->AssociateModels(
108 &result.local_merge_result, &result.syncer_merge_result);
110 // Return components to frontend when no error.
111 if (!result.error.IsSet()) {
113 result.change_processor = change_processor_.get();
114 result.model_associator = model_associator_.get();
117 result.association_time = base::TimeTicks::Now() - start_time;
119 result.local_merge_result.set_error(result.error);
121 // Destroy processor/associator on backend on failure.
123 base::AutoLock al(controller_lock_);
124 model_associator_->DisassociateModels();
125 change_processor_.reset();
126 model_associator_.reset();
129 controller_handle_.Call(
131 &browser_sync::NonFrontendDataTypeController::AssociationCallback,
135 void NonFrontendDataTypeController::BackendComponentsContainer::Disconnect() {
136 base::AutoLock al(controller_lock_);
139 if (change_processor_)
140 controller_->DisconnectProcessor(change_processor_.get());
141 if (model_associator_)
142 model_associator_->AbortAssociation();
147 NonFrontendDataTypeController::AssociationResult::AssociationResult(
148 syncer::ModelType type)
149 : needs_crypto(false),
150 unrecoverable_error(false),
151 sync_has_nodes(false),
152 local_merge_result(type),
153 syncer_merge_result(type),
154 change_processor(NULL),
155 model_associator(NULL) {}
157 NonFrontendDataTypeController::AssociationResult::~AssociationResult() {}
159 // TODO(tim): Legacy controllers are being left behind in componentization
160 // effort for now, hence still having a dependency on ProfileSyncService.
161 // That dep can probably be removed without too much work.
162 NonFrontendDataTypeController::NonFrontendDataTypeController(
163 scoped_refptr<base::MessageLoopProxy> ui_thread,
164 const base::Closure& error_callback,
165 ProfileSyncComponentsFactory* profile_sync_factory,
167 ProfileSyncService* sync_service)
168 : DataTypeController(ui_thread, error_callback),
170 profile_sync_factory_(profile_sync_factory),
172 profile_sync_service_(sync_service),
173 model_associator_(NULL),
174 change_processor_(NULL),
175 weak_ptr_factory_(this) {
176 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
177 DCHECK(profile_sync_factory_);
179 DCHECK(profile_sync_service_);
182 void NonFrontendDataTypeController::LoadModels(
183 const ModelLoadCallback& model_load_callback) {
184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
185 DCHECK(!model_load_callback.is_null());
186 if (state_ != NOT_RUNNING) {
187 model_load_callback.Run(type(),
188 syncer::SyncError(FROM_HERE,
189 syncer::SyncError::DATATYPE_ERROR,
190 "Model already loaded",
195 state_ = MODEL_STARTING;
196 if (!StartModels()) {
197 // We failed to start the models. There is no point in waiting.
198 // Note: This code is deprecated. The only 2 datatypes here,
199 // passwords and typed urls, dont have any special loading. So if we
200 // get a false it means they failed.
201 DCHECK(state_ == NOT_RUNNING || state_ == MODEL_STARTING
202 || state_ == DISABLED);
203 model_load_callback.Run(type(),
204 syncer::SyncError(FROM_HERE,
205 syncer::SyncError::DATATYPE_ERROR,
210 state_ = MODEL_LOADED;
212 model_load_callback.Run(type(), syncer::SyncError());
215 void NonFrontendDataTypeController::OnModelLoaded() {
219 void NonFrontendDataTypeController::StartAssociating(
220 const StartCallback& start_callback) {
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
222 DCHECK(!start_callback.is_null());
223 DCHECK(!components_container_);
224 DCHECK_EQ(state_, MODEL_LOADED);
226 // Kick off association on the thread the datatype resides on.
227 state_ = ASSOCIATING;
228 start_callback_ = start_callback;
230 components_container_.reset(new BackendComponentsContainer(this));
232 if (!PostTaskOnBackendThread(
234 base::Bind(&BackendComponentsContainer::Run,
235 base::Unretained(components_container_.get())))) {
236 syncer::SyncError error(
238 syncer::SyncError::DATATYPE_ERROR,
239 "Failed to post StartAssociation", type());
240 syncer::SyncMergeResult local_merge_result(type());
241 local_merge_result.set_error(error);
242 StartDone(ASSOCIATION_FAILED,
244 syncer::SyncMergeResult(type()));
248 void DestroyComponentsInBackend(
249 NonFrontendDataTypeController::BackendComponentsContainer *container) {
253 void NonFrontendDataTypeController::Stop() {
254 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
256 if (state_ == NOT_RUNNING)
259 // Deactivate the date type on the UI thread first to stop processing
260 // sync server changes. This needs to happen before posting task to destroy
261 // processor and associator on backend. Otherwise it could crash if syncer
262 // post work to backend after destruction task and that work is run before
264 profile_sync_service()->DeactivateDataType(type());
266 // Ignore association callback.
267 weak_ptr_factory_.InvalidateWeakPtrs();
269 // Disconnect on UI and post task to destroy on backend.
270 if (components_container_) {
271 components_container_->Disconnect();
272 PostTaskOnBackendThread(
274 base::Bind(&DestroyComponentsInBackend,
275 components_container_.release()));
276 model_associator_ = NULL;
277 change_processor_ = NULL;
280 state_ = NOT_RUNNING;
283 std::string NonFrontendDataTypeController::name() const {
285 return syncer::ModelTypeToString(type());
288 sync_driver::DataTypeController::State NonFrontendDataTypeController::state()
293 void NonFrontendDataTypeController::OnSingleDataTypeUnrecoverableError(
294 const syncer::SyncError& error) {
295 DCHECK(IsOnBackendThread());
296 DCHECK_EQ(type(), error.model_type());
297 RecordUnrecoverableError(error.location(), error.message());
298 BrowserThread::PostTask(BrowserThread::UI, error.location(),
299 base::Bind(&NonFrontendDataTypeController::DisableImpl,
304 NonFrontendDataTypeController::NonFrontendDataTypeController()
305 : DataTypeController(base::MessageLoopProxy::current(), base::Closure()),
307 profile_sync_factory_(NULL),
309 profile_sync_service_(NULL),
310 model_associator_(NULL),
311 change_processor_(NULL),
312 weak_ptr_factory_(this) {
315 NonFrontendDataTypeController::~NonFrontendDataTypeController() {
316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
317 DCHECK(!change_processor_);
318 DCHECK(!model_associator_);
321 bool NonFrontendDataTypeController::StartModels() {
322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323 DCHECK_EQ(state_, MODEL_STARTING);
324 // By default, no additional services need to be started before we can proceed
325 // with model association, so do nothing.
329 bool NonFrontendDataTypeController::IsOnBackendThread() {
330 return !BrowserThread::CurrentlyOn(BrowserThread::UI);
333 void NonFrontendDataTypeController::StartDone(
334 DataTypeController::ConfigureResult start_result,
335 const syncer::SyncMergeResult& local_merge_result,
336 const syncer::SyncMergeResult& syncer_merge_result) {
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338 DataTypeController::State new_state;
340 if (IsSuccessfulResult(start_result)) {
343 new_state = (start_result == ASSOCIATION_FAILED ? DISABLED : NOT_RUNNING);
344 if (IsUnrecoverableResult(start_result))
345 RecordUnrecoverableError(FROM_HERE, "StartFailed");
348 StartDoneImpl(start_result, new_state, local_merge_result,
349 syncer_merge_result);
352 void NonFrontendDataTypeController::StartDoneImpl(
353 DataTypeController::ConfigureResult start_result,
354 DataTypeController::State new_state,
355 const syncer::SyncMergeResult& local_merge_result,
356 const syncer::SyncMergeResult& syncer_merge_result) {
357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
360 if (state_ != RUNNING) {
362 RecordStartFailure(start_result);
365 start_callback_.Run(start_result, local_merge_result, syncer_merge_result);
368 void NonFrontendDataTypeController::DisableImpl(
369 const syncer::SyncError& error) {
370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
371 if (!start_callback_.is_null()) {
372 syncer::SyncMergeResult local_merge_result(type());
373 local_merge_result.set_error(error);
374 start_callback_.Run(RUNTIME_ERROR,
376 syncer::SyncMergeResult(type()));
380 void NonFrontendDataTypeController::RecordAssociationTime(
381 base::TimeDelta time) {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
383 #define PER_DATA_TYPE_MACRO(type_str) \
384 UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time);
385 SYNC_DATA_TYPE_HISTOGRAM(type());
386 #undef PER_DATA_TYPE_MACRO
389 void NonFrontendDataTypeController::RecordStartFailure(ConfigureResult result) {
390 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
391 UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures",
392 ModelTypeToHistogramInt(type()),
393 syncer::MODEL_TYPE_COUNT);
394 #define PER_DATA_TYPE_MACRO(type_str) \
395 UMA_HISTOGRAM_ENUMERATION("Sync." type_str "StartFailure", result, \
397 SYNC_DATA_TYPE_HISTOGRAM(type());
398 #undef PER_DATA_TYPE_MACRO
401 void NonFrontendDataTypeController::RecordUnrecoverableError(
402 const tracked_objects::Location& from_here,
403 const std::string& message) {
404 DVLOG(1) << "Datatype Controller failed for type "
405 << ModelTypeToString(type()) << " "
406 << message << " at location "
407 << from_here.ToString();
408 UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures",
409 ModelTypeToHistogramInt(type()),
410 syncer::MODEL_TYPE_COUNT);
412 if (!error_callback_.is_null())
413 error_callback_.Run();
417 ProfileSyncComponentsFactory*
418 NonFrontendDataTypeController::profile_sync_factory() const {
419 return profile_sync_factory_;
422 Profile* NonFrontendDataTypeController::profile() const {
426 ProfileSyncService* NonFrontendDataTypeController::profile_sync_service()
428 return profile_sync_service_;
431 void NonFrontendDataTypeController::set_start_callback(
432 const StartCallback& callback) {
433 start_callback_ = callback;
436 void NonFrontendDataTypeController::set_state(State state) {
440 sync_driver::AssociatorInterface* NonFrontendDataTypeController::associator()
442 return model_associator_;
445 sync_driver::ChangeProcessor*
446 NonFrontendDataTypeController::GetChangeProcessor() const {
447 return change_processor_;
450 void NonFrontendDataTypeController::AssociationCallback(
451 AssociationResult result) {
452 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
454 if (result.needs_crypto) {
455 StartDone(NEEDS_CRYPTO,
456 result.local_merge_result,
457 result.syncer_merge_result);
461 if (result.unrecoverable_error) {
462 StartDone(UNRECOVERABLE_ERROR,
463 result.local_merge_result,
464 result.syncer_merge_result);
468 RecordAssociationTime(result.association_time);
469 if (result.error.IsSet()) {
470 StartDone(ASSOCIATION_FAILED,
471 result.local_merge_result,
472 result.syncer_merge_result);
476 CHECK(result.change_processor);
477 CHECK(result.model_associator);
478 change_processor_ = result.change_processor;
479 model_associator_ = result.model_associator;
481 StartDone(!result.sync_has_nodes ? OK_FIRST_RUN : OK,
482 result.local_merge_result,
483 result.syncer_merge_result);
486 } // namespace browser_sync