Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync / glue / non_frontend_data_type_controller.cc
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.
4
5 #include "chrome/browser/sync/glue/non_frontend_data_type_controller.h"
6
7 #include "base/bind.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"
21
22 using content::BrowserThread;
23
24 namespace browser_sync {
25
26 class NonFrontendDataTypeController::BackendComponentsContainer {
27  public:
28   explicit BackendComponentsContainer(
29       NonFrontendDataTypeController* controller);
30   ~BackendComponentsContainer();
31   void Run();
32   void Disconnect();
33
34  private:
35   bool CreateComponents();
36   void Associate();
37
38   // For creating components.
39   NonFrontendDataTypeController* controller_;
40   base::Lock controller_lock_;
41
42   syncer::ModelType type_;
43
44   // For returning association results to controller on UI.
45   syncer::WeakHandle<NonFrontendDataTypeController> controller_handle_;
46
47   scoped_ptr<sync_driver::AssociatorInterface> model_associator_;
48   scoped_ptr<sync_driver::ChangeProcessor> change_processor_;
49 };
50
51 NonFrontendDataTypeController::
52 BackendComponentsContainer::BackendComponentsContainer(
53     NonFrontendDataTypeController* controller)
54     : controller_(controller),
55       type_(controller->type()) {
56   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
57   controller_handle_ =
58       syncer::MakeWeakHandle(controller_->weak_ptr_factory_.GetWeakPtr());
59 }
60
61 NonFrontendDataTypeController::
62 BackendComponentsContainer::~BackendComponentsContainer() {
63   if (model_associator_)
64     model_associator_->DisassociateModels();
65 }
66
67 void NonFrontendDataTypeController::BackendComponentsContainer::Run() {
68   DCHECK(controller_->IsOnBackendThread());
69   if (CreateComponents())
70     Associate();
71 }
72
73 bool
74 NonFrontendDataTypeController::BackendComponentsContainer::CreateComponents() {
75   base::AutoLock al(controller_lock_);
76   if (!controller_) {
77     DVLOG(1) << "Controller was stopped before sync components are created.";
78     return false;
79   }
80
81   ProfileSyncComponentsFactory::SyncComponents sync_components =
82       controller_->CreateSyncComponents();
83   model_associator_.reset(sync_components.model_associator);
84   change_processor_.reset(sync_components.change_processor);
85   return true;
86 }
87
88 void NonFrontendDataTypeController::BackendComponentsContainer::Associate() {
89   CHECK(model_associator_);
90
91   bool succeeded = false;
92
93   browser_sync::NonFrontendDataTypeController::AssociationResult result(type_);
94   if (!model_associator_->CryptoReadyIfNecessary()) {
95     result.needs_crypto = true;
96   } else {
97     base::TimeTicks start_time = base::TimeTicks::Now();
98
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",
105                                        type_);
106     } else {
107       result.error = model_associator_->AssociateModels(
108           &result.local_merge_result, &result.syncer_merge_result);
109
110       // Return components to frontend when no error.
111       if (!result.error.IsSet()) {
112         succeeded = true;
113         result.change_processor = change_processor_.get();
114         result.model_associator = model_associator_.get();
115       }
116     }
117     result.association_time = base::TimeTicks::Now() - start_time;
118   }
119   result.local_merge_result.set_error(result.error);
120
121   // Destroy processor/associator on backend on failure.
122   if (!succeeded) {
123     base::AutoLock al(controller_lock_);
124     model_associator_->DisassociateModels();
125     change_processor_.reset();
126     model_associator_.reset();
127   }
128
129   controller_handle_.Call(
130       FROM_HERE,
131       &browser_sync::NonFrontendDataTypeController::AssociationCallback,
132       result);
133 }
134
135 void NonFrontendDataTypeController::BackendComponentsContainer::Disconnect() {
136   base::AutoLock al(controller_lock_);
137   CHECK(controller_);
138
139   if (change_processor_)
140     controller_->DisconnectProcessor(change_processor_.get());
141   if (model_associator_)
142     model_associator_->AbortAssociation();
143
144   controller_ = NULL;
145 }
146
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) {}
156
157 NonFrontendDataTypeController::AssociationResult::~AssociationResult() {}
158
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,
166     Profile* profile,
167     ProfileSyncService* sync_service)
168     : DataTypeController(ui_thread, error_callback),
169       state_(NOT_RUNNING),
170       profile_sync_factory_(profile_sync_factory),
171       profile_(profile),
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_);
178   DCHECK(profile_);
179   DCHECK(profile_sync_service_);
180 }
181
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",
191                                               type()));
192     return;
193   }
194
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,
206                                               "Failed loading",
207                                               type()));
208     return;
209   }
210   state_ = MODEL_LOADED;
211
212   model_load_callback.Run(type(), syncer::SyncError());
213 }
214
215 void NonFrontendDataTypeController::OnModelLoaded() {
216   NOTREACHED();
217 }
218
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);
225
226   // Kick off association on the thread the datatype resides on.
227   state_ = ASSOCIATING;
228   start_callback_ = start_callback;
229
230   components_container_.reset(new BackendComponentsContainer(this));
231
232   if (!PostTaskOnBackendThread(
233       FROM_HERE,
234       base::Bind(&BackendComponentsContainer::Run,
235                  base::Unretained(components_container_.get())))) {
236     syncer::SyncError error(
237         FROM_HERE,
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,
243               local_merge_result,
244               syncer::SyncMergeResult(type()));
245   }
246 }
247
248 void DestroyComponentsInBackend(
249     NonFrontendDataTypeController::BackendComponentsContainer *container) {
250   delete container;
251 }
252
253 void NonFrontendDataTypeController::Stop() {
254   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
255
256   if (state_ == NOT_RUNNING)
257     return;
258
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
263   // deactivation.
264   profile_sync_service()->DeactivateDataType(type());
265
266   // Ignore association callback.
267   weak_ptr_factory_.InvalidateWeakPtrs();
268
269   // Disconnect on UI and post task to destroy on backend.
270   if (components_container_) {
271     components_container_->Disconnect();
272     PostTaskOnBackendThread(
273           FROM_HERE,
274           base::Bind(&DestroyComponentsInBackend,
275                      components_container_.release()));
276     model_associator_ = NULL;
277     change_processor_ = NULL;
278   }
279
280   state_ = NOT_RUNNING;
281 }
282
283 std::string NonFrontendDataTypeController::name() const {
284   // For logging only.
285   return syncer::ModelTypeToString(type());
286 }
287
288 sync_driver::DataTypeController::State NonFrontendDataTypeController::state()
289     const {
290   return state_;
291 }
292
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,
300                  this,
301                  error));
302 }
303
304 NonFrontendDataTypeController::NonFrontendDataTypeController()
305     : DataTypeController(base::MessageLoopProxy::current(), base::Closure()),
306       state_(NOT_RUNNING),
307       profile_sync_factory_(NULL),
308       profile_(NULL),
309       profile_sync_service_(NULL),
310       model_associator_(NULL),
311       change_processor_(NULL),
312       weak_ptr_factory_(this) {
313 }
314
315 NonFrontendDataTypeController::~NonFrontendDataTypeController() {
316   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
317   DCHECK(!change_processor_);
318   DCHECK(!model_associator_);
319 }
320
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.
326   return true;
327 }
328
329 bool NonFrontendDataTypeController::IsOnBackendThread() {
330   return !BrowserThread::CurrentlyOn(BrowserThread::UI);
331 }
332
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;
339
340   if (IsSuccessfulResult(start_result)) {
341     new_state = RUNNING;
342   } else {
343     new_state = (start_result == ASSOCIATION_FAILED ? DISABLED : NOT_RUNNING);
344     if (IsUnrecoverableResult(start_result))
345       RecordUnrecoverableError(FROM_HERE, "StartFailed");
346   }
347
348   StartDoneImpl(start_result, new_state, local_merge_result,
349                 syncer_merge_result);
350 }
351
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));
358
359   state_ = new_state;
360   if (state_ != RUNNING) {
361     // Start failed.
362     RecordStartFailure(start_result);
363   }
364
365   start_callback_.Run(start_result, local_merge_result, syncer_merge_result);
366 }
367
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,
375                         local_merge_result,
376                         syncer::SyncMergeResult(type()));
377   }
378 }
379
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
387 }
388
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, \
396                               MAX_START_RESULT);
397   SYNC_DATA_TYPE_HISTOGRAM(type());
398 #undef PER_DATA_TYPE_MACRO
399 }
400
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);
411
412   if (!error_callback_.is_null())
413     error_callback_.Run();
414 }
415
416
417 ProfileSyncComponentsFactory*
418     NonFrontendDataTypeController::profile_sync_factory() const {
419   return profile_sync_factory_;
420 }
421
422 Profile* NonFrontendDataTypeController::profile() const {
423   return profile_;
424 }
425
426 ProfileSyncService* NonFrontendDataTypeController::profile_sync_service()
427     const {
428   return profile_sync_service_;
429 }
430
431 void NonFrontendDataTypeController::set_start_callback(
432     const StartCallback& callback) {
433   start_callback_ = callback;
434 }
435
436 void NonFrontendDataTypeController::set_state(State state) {
437   state_ = state;
438 }
439
440 sync_driver::AssociatorInterface* NonFrontendDataTypeController::associator()
441     const {
442   return model_associator_;
443 }
444
445 sync_driver::ChangeProcessor*
446 NonFrontendDataTypeController::GetChangeProcessor() const {
447   return change_processor_;
448 }
449
450 void NonFrontendDataTypeController::AssociationCallback(
451     AssociationResult result) {
452   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
453
454   if (result.needs_crypto) {
455     StartDone(NEEDS_CRYPTO,
456               result.local_merge_result,
457               result.syncer_merge_result);
458     return;
459   }
460
461   if (result.unrecoverable_error) {
462     StartDone(UNRECOVERABLE_ERROR,
463               result.local_merge_result,
464               result.syncer_merge_result);
465     return;
466   }
467
468   RecordAssociationTime(result.association_time);
469   if (result.error.IsSet()) {
470     StartDone(ASSOCIATION_FAILED,
471               result.local_merge_result,
472               result.syncer_merge_result);
473     return;
474   }
475
476   CHECK(result.change_processor);
477   CHECK(result.model_associator);
478   change_processor_ = result.change_processor;
479   model_associator_ = result.model_associator;
480
481   StartDone(!result.sync_has_nodes ? OK_FIRST_RUN : OK,
482             result.local_merge_result,
483             result.syncer_merge_result);
484 }
485
486 }  // namespace browser_sync