Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / components / sync_driver / non_ui_data_type_controller.cc
1 // Copyright 2014 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 "components/sync_driver/non_ui_data_type_controller.h"
6
7 #include "base/logging.h"
8 #include "base/memory/weak_ptr.h"
9 #include "components/sync_driver/generic_change_processor_factory.h"
10 #include "components/sync_driver/shared_change_processor_ref.h"
11 #include "components/sync_driver/sync_api_component_factory.h"
12 #include "sync/api/sync_error.h"
13 #include "sync/api/syncable_service.h"
14 #include "sync/internal_api/public/base/model_type.h"
15 #include "sync/util/data_type_histogram.h"
16
17 namespace sync_driver {
18
19 SharedChangeProcessor*
20 NonUIDataTypeController::CreateSharedChangeProcessor() {
21   return new SharedChangeProcessor();
22 }
23
24 NonUIDataTypeController::NonUIDataTypeController(
25     scoped_refptr<base::MessageLoopProxy> ui_thread,
26     const base::Closure& error_callback,
27     SyncApiComponentFactory* sync_factory)
28     : DataTypeController(ui_thread, error_callback),
29       sync_factory_(sync_factory),
30       state_(NOT_RUNNING),
31       ui_thread_(ui_thread) {
32 }
33
34 void NonUIDataTypeController::LoadModels(
35     const ModelLoadCallback& model_load_callback) {
36   DCHECK(ui_thread_->BelongsToCurrentThread());
37   DCHECK(!model_load_callback.is_null());
38   if (state() != NOT_RUNNING) {
39     model_load_callback.Run(type(),
40                             syncer::SyncError(FROM_HERE,
41                                               syncer::SyncError::DATATYPE_ERROR,
42                                               "Model already running",
43                                               type()));
44     return;
45   }
46
47   state_ = MODEL_STARTING;
48   // Since we can't be called multiple times before Stop() is called,
49   // |shared_change_processor_| must be NULL here.
50   DCHECK(!shared_change_processor_.get());
51   shared_change_processor_ = CreateSharedChangeProcessor();
52   DCHECK(shared_change_processor_.get());
53   model_load_callback_ = model_load_callback;
54   if (!StartModels()) {
55     // If we are waiting for some external service to load before associating
56     // or we failed to start the models, we exit early.
57     DCHECK(state() == MODEL_STARTING || state() == NOT_RUNNING);
58     return;
59   }
60
61   OnModelLoaded();
62 }
63
64 void NonUIDataTypeController::OnModelLoaded() {
65   DCHECK_EQ(state_, MODEL_STARTING);
66   DCHECK(!model_load_callback_.is_null());
67   state_ = MODEL_LOADED;
68
69   ModelLoadCallback model_load_callback = model_load_callback_;
70   model_load_callback_.Reset();
71   model_load_callback.Run(type(), syncer::SyncError());
72 }
73
74 bool NonUIDataTypeController::StartModels() {
75   DCHECK_EQ(state_, MODEL_STARTING);
76   // By default, no additional services need to be started before we can proceed
77   // with model association.
78   return true;
79 }
80
81 void NonUIDataTypeController::StopModels() {
82   DCHECK(ui_thread_->BelongsToCurrentThread());
83 }
84
85 void NonUIDataTypeController::StartAssociating(
86     const StartCallback& start_callback) {
87   DCHECK(ui_thread_->BelongsToCurrentThread());
88   DCHECK(!start_callback.is_null());
89   DCHECK_EQ(state_, MODEL_LOADED);
90   state_ = ASSOCIATING;
91
92   start_callback_ = start_callback;
93   if (!StartAssociationAsync()) {
94     syncer::SyncError error(
95         FROM_HERE,
96         syncer::SyncError::DATATYPE_ERROR,
97         "Failed to post StartAssociation",
98         type());
99     syncer::SyncMergeResult local_merge_result(type());
100     local_merge_result.set_error(error);
101     StartDoneImpl(ASSOCIATION_FAILED,
102                   NOT_RUNNING,
103                   local_merge_result,
104                   syncer::SyncMergeResult(type()));
105     // StartDoneImpl should have called ClearSharedChangeProcessor();
106     DCHECK(!shared_change_processor_.get());
107     return;
108   }
109 }
110
111 void NonUIDataTypeController::Stop() {
112   DCHECK(ui_thread_->BelongsToCurrentThread());
113
114   if (state() == NOT_RUNNING)
115     return;
116
117   // Disconnect the change processor. At this point, the
118   // syncer::SyncableService can no longer interact with the Syncer, even if
119   // it hasn't finished MergeDataAndStartSyncing.
120   ClearSharedChangeProcessor();
121
122   // If we haven't finished starting, we need to abort the start.
123   switch (state()) {
124     case MODEL_STARTING:
125       state_ = STOPPING;
126       AbortModelLoad();
127       return;  // The datatype was never activated, we're done.
128     case ASSOCIATING:
129       state_ = STOPPING;
130       // We continue on to deactivate the datatype and stop the local service.
131       break;
132     case MODEL_LOADED:
133     case DISABLED:
134       // If DTC is loaded or disabled, we never attempted or succeeded
135       // associating and never activated the datatype. We would have already
136       // stopped the local service in StartDoneImpl(..).
137       state_ = NOT_RUNNING;
138       StopModels();
139       return;
140     default:
141       // Datatype was fully started. Need to deactivate and stop the local
142       // service.
143       DCHECK_EQ(state(), RUNNING);
144       state_ = STOPPING;
145       StopModels();
146       break;
147   }
148
149   // Stop the local service and release our references to it and the
150   // shared change processor (posts a task to the datatype's thread).
151   StopLocalServiceAsync();
152
153   state_ = NOT_RUNNING;
154 }
155
156 std::string NonUIDataTypeController::name() const {
157   // For logging only.
158   return syncer::ModelTypeToString(type());
159 }
160
161 DataTypeController::State NonUIDataTypeController::state() const {
162   return state_;
163 }
164
165 void NonUIDataTypeController::OnSingleDataTypeUnrecoverableError(
166     const syncer::SyncError& error) {
167   DCHECK(!ui_thread_->BelongsToCurrentThread());
168   // TODO(tim): We double-upload some errors.  See bug 383480.
169   if (!error_callback_.is_null())
170     error_callback_.Run();
171   ui_thread_->PostTask(error.location(),
172       base::Bind(&NonUIDataTypeController::DisableImpl,
173                  this,
174                  error));
175 }
176
177 NonUIDataTypeController::NonUIDataTypeController()
178     : DataTypeController(base::MessageLoopProxy::current(), base::Closure()),
179       sync_factory_(NULL) {}
180
181 NonUIDataTypeController::~NonUIDataTypeController() {}
182
183 void NonUIDataTypeController::StartDone(
184     DataTypeController::ConfigureResult start_result,
185     const syncer::SyncMergeResult& local_merge_result,
186     const syncer::SyncMergeResult& syncer_merge_result) {
187   DCHECK(!ui_thread_->BelongsToCurrentThread());
188
189   DataTypeController::State new_state;
190   if (IsSuccessfulResult(start_result)) {
191     new_state = RUNNING;
192   } else {
193     new_state = (start_result == ASSOCIATION_FAILED ? DISABLED : NOT_RUNNING);
194   }
195
196   ui_thread_->PostTask(FROM_HERE,
197       base::Bind(&NonUIDataTypeController::StartDoneImpl,
198                  this,
199                  start_result,
200                  new_state,
201                  local_merge_result,
202                  syncer_merge_result));
203 }
204
205 void NonUIDataTypeController::StartDoneImpl(
206     DataTypeController::ConfigureResult start_result,
207     DataTypeController::State new_state,
208     const syncer::SyncMergeResult& local_merge_result,
209     const syncer::SyncMergeResult& syncer_merge_result) {
210   DCHECK(ui_thread_->BelongsToCurrentThread());
211
212   // If we failed to start up, and we haven't been stopped yet, we need to
213   // ensure we clean up the local service and shared change processor properly.
214   if (new_state != RUNNING && state() != NOT_RUNNING && state() != STOPPING) {
215     ClearSharedChangeProcessor();
216     StopLocalServiceAsync();
217   }
218
219   // It's possible to have StartDoneImpl called first from the UI thread
220   // (due to Stop being called) and then posted from the non-UI thread. In
221   // this case, we drop the second call because we've already been stopped.
222   if (state_ == NOT_RUNNING) {
223     return;
224   }
225
226   state_ = new_state;
227   if (state_ != RUNNING) {
228     // Start failed.
229     StopModels();
230     RecordStartFailure(start_result);
231   }
232
233   start_callback_.Run(start_result, local_merge_result, syncer_merge_result);
234 }
235
236 void NonUIDataTypeController::RecordAssociationTime(base::TimeDelta time) {
237   DCHECK(!ui_thread_->BelongsToCurrentThread());
238 #define PER_DATA_TYPE_MACRO(type_str) \
239     UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time);
240   SYNC_DATA_TYPE_HISTOGRAM(type());
241 #undef PER_DATA_TYPE_MACRO
242 }
243
244 void NonUIDataTypeController::RecordStartFailure(ConfigureResult result) {
245   DCHECK(ui_thread_->BelongsToCurrentThread());
246   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures",
247                             ModelTypeToHistogramInt(type()),
248                             syncer::MODEL_TYPE_COUNT);
249 #define PER_DATA_TYPE_MACRO(type_str) \
250     UMA_HISTOGRAM_ENUMERATION("Sync." type_str "StartFailure", result, \
251                               MAX_START_RESULT);
252   SYNC_DATA_TYPE_HISTOGRAM(type());
253 #undef PER_DATA_TYPE_MACRO
254 }
255
256 void NonUIDataTypeController::AbortModelLoad() {
257   state_ = NOT_RUNNING;
258   StopModels();
259   ModelLoadCallback model_load_callback = model_load_callback_;
260   model_load_callback_.Reset();
261   model_load_callback.Run(type(),
262                           syncer::SyncError(FROM_HERE,
263                                             syncer::SyncError::DATATYPE_ERROR,
264                                             "ABORTED",
265                                             type()));
266 }
267
268 void NonUIDataTypeController::DisableImpl(
269     const syncer::SyncError& error) {
270   DCHECK(ui_thread_->BelongsToCurrentThread());
271   UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures",
272                             ModelTypeToHistogramInt(type()),
273                             syncer::MODEL_TYPE_COUNT);
274   if (!start_callback_.is_null()) {
275     syncer::SyncMergeResult local_merge_result(type());
276     local_merge_result.set_error(error);
277     start_callback_.Run(RUNTIME_ERROR,
278                         local_merge_result,
279                         syncer::SyncMergeResult(type()));
280   }
281 }
282
283 bool NonUIDataTypeController::StartAssociationAsync() {
284   DCHECK(ui_thread_->BelongsToCurrentThread());
285   DCHECK_EQ(state(), ASSOCIATING);
286   return PostTaskOnBackendThread(
287       FROM_HERE,
288       base::Bind(
289           &NonUIDataTypeController::StartAssociationWithSharedChangeProcessor,
290           this,
291           shared_change_processor_));
292 }
293
294 ChangeProcessor* NonUIDataTypeController::GetChangeProcessor() const {
295   DCHECK_EQ(state_, RUNNING);
296   return shared_change_processor_->generic_change_processor();
297 }
298
299 // This method can execute after we've already stopped (and possibly even
300 // destroyed) both the Syncer and the SyncableService. As a result, all actions
301 // must either have no side effects outside of the DTC or must be protected
302 // by |shared_change_processor|, which is guaranteed to have been Disconnected
303 // if the syncer shut down.
304 void NonUIDataTypeController::
305     StartAssociationWithSharedChangeProcessor(
306         const scoped_refptr<SharedChangeProcessor>& shared_change_processor) {
307   DCHECK(!ui_thread_->BelongsToCurrentThread());
308   DCHECK(shared_change_processor.get());
309   syncer::SyncMergeResult local_merge_result(type());
310   syncer::SyncMergeResult syncer_merge_result(type());
311   base::WeakPtrFactory<syncer::SyncMergeResult> weak_ptr_factory(
312       &syncer_merge_result);
313
314   // Connect |shared_change_processor| to the syncer and get the
315   // syncer::SyncableService associated with type().
316   // Note that it's possible the shared_change_processor has already been
317   // disconnected at this point, so all our accesses to the syncer from this
318   // point on are through it.
319   GenericChangeProcessorFactory factory;
320   local_service_ = shared_change_processor->Connect(
321       sync_factory_,
322       &factory,
323       user_share(),
324       this,
325       type(),
326       weak_ptr_factory.GetWeakPtr());
327   if (!local_service_.get()) {
328     syncer::SyncError error(FROM_HERE,
329                             syncer::SyncError::DATATYPE_ERROR,
330                             "Failed to connect to syncer.",
331                             type());
332     local_merge_result.set_error(error);
333     StartDone(ASSOCIATION_FAILED,
334               local_merge_result,
335               syncer_merge_result);
336     return;
337   }
338
339   if (!shared_change_processor->CryptoReadyIfNecessary()) {
340     syncer::SyncError error(FROM_HERE,
341                             syncer::SyncError::CRYPTO_ERROR,
342                             "",
343                             type());
344     local_merge_result.set_error(error);
345     StartDone(NEEDS_CRYPTO,
346               local_merge_result,
347               syncer_merge_result);
348     return;
349   }
350
351   bool sync_has_nodes = false;
352   if (!shared_change_processor->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
353     syncer::SyncError error(FROM_HERE,
354                             syncer::SyncError::UNRECOVERABLE_ERROR,
355                             "Failed to load sync nodes",
356                             type());
357     local_merge_result.set_error(error);
358     StartDone(UNRECOVERABLE_ERROR,
359               local_merge_result,
360               syncer_merge_result);
361     return;
362   }
363
364   base::TimeTicks start_time = base::TimeTicks::Now();
365   syncer::SyncDataList initial_sync_data;
366   syncer::SyncError error =
367       shared_change_processor->GetAllSyncDataReturnError(
368           type(), &initial_sync_data);
369   if (error.IsSet()) {
370     local_merge_result.set_error(error);
371     StartDone(ASSOCIATION_FAILED,
372               local_merge_result,
373               syncer_merge_result);
374     return;
375   }
376
377   std::string datatype_context;
378   if (shared_change_processor->GetDataTypeContext(&datatype_context)) {
379     local_service_->UpdateDataTypeContext(
380         type(), syncer::SyncChangeProcessor::NO_REFRESH, datatype_context);
381   }
382
383   syncer_merge_result.set_num_items_before_association(
384       initial_sync_data.size());
385   // Passes a reference to |shared_change_processor|.
386   local_merge_result =
387       local_service_->MergeDataAndStartSyncing(
388           type(),
389           initial_sync_data,
390           scoped_ptr<syncer::SyncChangeProcessor>(
391               new SharedChangeProcessorRef(shared_change_processor)),
392           scoped_ptr<syncer::SyncErrorFactory>(
393               new SharedChangeProcessorRef(shared_change_processor)));
394   RecordAssociationTime(base::TimeTicks::Now() - start_time);
395   if (local_merge_result.error().IsSet()) {
396     StartDone(ASSOCIATION_FAILED,
397               local_merge_result,
398               syncer_merge_result);
399     return;
400   }
401
402   syncer_merge_result.set_num_items_after_association(
403       shared_change_processor->GetSyncCount());
404
405   StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK,
406             local_merge_result,
407             syncer_merge_result);
408 }
409
410 void NonUIDataTypeController::ClearSharedChangeProcessor() {
411   DCHECK(ui_thread_->BelongsToCurrentThread());
412   // |shared_change_processor_| can already be NULL if Stop() is
413   // called after StartDoneImpl(_, DISABLED, _).
414   if (shared_change_processor_.get()) {
415     shared_change_processor_->Disconnect();
416     shared_change_processor_ = NULL;
417   }
418 }
419
420 void NonUIDataTypeController::StopLocalServiceAsync() {
421   DCHECK(ui_thread_->BelongsToCurrentThread());
422   PostTaskOnBackendThread(
423       FROM_HERE,
424       base::Bind(&NonUIDataTypeController::StopLocalService, this));
425 }
426
427 void NonUIDataTypeController::StopLocalService() {
428   DCHECK(!ui_thread_->BelongsToCurrentThread());
429   if (local_service_.get())
430     local_service_->StopSyncing(type());
431   local_service_.reset();
432 }
433
434 }  // namespace sync_driver