ui_data_type_controller.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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/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 "sync/api/sync_error.h"
12#include "sync/api/syncable_service.h"
13#include "sync/internal_api/public/base/model_type.h"
14#include "sync/util/data_type_histogram.h"
15
16namespace browser_sync {
17
18UIDataTypeController::UIDataTypeController()
19    : DataTypeController(base::MessageLoopProxy::current(),
20                         base::Closure(),
21                         DisableTypeCallback()),
22      sync_factory_(NULL),
23      state_(NOT_RUNNING),
24      type_(syncer::UNSPECIFIED) {
25}
26
27UIDataTypeController::UIDataTypeController(
28    scoped_refptr<base::MessageLoopProxy> ui_thread,
29    const base::Closure& error_callback,
30    const DisableTypeCallback& disable_callback,
31    syncer::ModelType type,
32    SyncApiComponentFactory* sync_factory)
33    : DataTypeController(ui_thread, error_callback, disable_callback),
34      sync_factory_(sync_factory),
35      state_(NOT_RUNNING),
36      type_(type),
37      processor_factory_(new GenericChangeProcessorFactory()),
38      ui_thread_(ui_thread) {
39  DCHECK(ui_thread_->BelongsToCurrentThread());
40  DCHECK(sync_factory);
41  DCHECK(syncer::IsRealDataType(type_));
42}
43
44void UIDataTypeController::SetGenericChangeProcessorFactoryForTest(
45      scoped_ptr<GenericChangeProcessorFactory> factory) {
46  DCHECK_EQ(state_, NOT_RUNNING);
47  processor_factory_ = factory.Pass();
48}
49
50UIDataTypeController::~UIDataTypeController() {
51  DCHECK(ui_thread_->BelongsToCurrentThread());
52}
53
54void UIDataTypeController::LoadModels(
55    const ModelLoadCallback& model_load_callback) {
56  DCHECK(ui_thread_->BelongsToCurrentThread());
57  DCHECK(!model_load_callback.is_null());
58  DCHECK(syncer::IsRealDataType(type_));
59  if (state_ != NOT_RUNNING) {
60    model_load_callback.Run(type(),
61                            syncer::SyncError(FROM_HERE,
62                                              syncer::SyncError::DATATYPE_ERROR,
63                                              "Model already loaded",
64                                              type()));
65    return;
66  }
67  // Since we can't be called multiple times before Stop() is called,
68  // |shared_change_processor_| must be NULL here.
69  DCHECK(!shared_change_processor_.get());
70  shared_change_processor_ = new SharedChangeProcessor();
71
72  model_load_callback_ = model_load_callback;
73  state_ = MODEL_STARTING;
74  if (!StartModels()) {
75    // If we are waiting for some external service to load before associating
76    // or we failed to start the models, we exit early. state_ will control
77    // what we perform next.
78    DCHECK(state_ == NOT_RUNNING || state_ == MODEL_STARTING);
79    return;
80  }
81
82  state_ = MODEL_LOADED;
83  model_load_callback_.Reset();
84  model_load_callback.Run(type(), syncer::SyncError());
85}
86
87void UIDataTypeController::OnModelLoaded() {
88  DCHECK(ui_thread_->BelongsToCurrentThread());
89  DCHECK(!model_load_callback_.is_null());
90  DCHECK_EQ(state_, MODEL_STARTING);
91
92  state_ = MODEL_LOADED;
93  ModelLoadCallback model_load_callback = model_load_callback_;
94  model_load_callback_.Reset();
95  model_load_callback.Run(type(), syncer::SyncError());
96}
97
98void UIDataTypeController::StartAssociating(
99    const StartCallback& start_callback) {
100  DCHECK(ui_thread_->BelongsToCurrentThread());
101  DCHECK(!start_callback.is_null());
102  DCHECK_EQ(state_, MODEL_LOADED);
103
104  start_callback_ = start_callback;
105  state_ = ASSOCIATING;
106  Associate();
107  // It's possible StartDone(..) resulted in a Stop() call, or that association
108  // failed, so we just verify that the state has moved foward.
109  DCHECK_NE(state_, ASSOCIATING);
110}
111
112bool UIDataTypeController::StartModels() {
113  DCHECK_EQ(state_, MODEL_STARTING);
114  // By default, no additional services need to be started before we can proceed
115  // with model association.
116  return true;
117}
118
119void UIDataTypeController::Associate() {
120  DCHECK_EQ(state_, ASSOCIATING);
121  syncer::SyncMergeResult local_merge_result(type());
122  syncer::SyncMergeResult syncer_merge_result(type());
123  base::WeakPtrFactory<syncer::SyncMergeResult> weak_ptr_factory(
124      &syncer_merge_result);
125
126  // Connect |shared_change_processor_| to the syncer and get the
127  // syncer::SyncableService associated with type().
128  local_service_ = shared_change_processor_->Connect(
129      sync_factory_,
130      processor_factory_.get(),
131      user_share(),
132      this,
133      type(),
134      weak_ptr_factory.GetWeakPtr());
135  if (!local_service_.get()) {
136    syncer::SyncError error(FROM_HERE,
137                            syncer::SyncError::DATATYPE_ERROR,
138                            "Failed to connect to syncer.",
139                            type());
140    local_merge_result.set_error(error);
141    StartDone(ASSOCIATION_FAILED,
142              local_merge_result,
143              syncer_merge_result);
144    return;
145  }
146
147  if (!shared_change_processor_->CryptoReadyIfNecessary()) {
148    StartDone(NEEDS_CRYPTO,
149              local_merge_result,
150              syncer_merge_result);
151    return;
152  }
153
154  bool sync_has_nodes = false;
155  if (!shared_change_processor_->SyncModelHasUserCreatedNodes(
156          &sync_has_nodes)) {
157    syncer::SyncError error(FROM_HERE,
158                            syncer::SyncError::UNRECOVERABLE_ERROR,
159                            "Failed to load sync nodes",
160                            type());
161    local_merge_result.set_error(error);
162    StartDone(UNRECOVERABLE_ERROR,
163              local_merge_result,
164              syncer_merge_result);
165    return;
166  }
167
168  base::TimeTicks start_time = base::TimeTicks::Now();
169  syncer::SyncDataList initial_sync_data;
170  syncer::SyncError error =
171      shared_change_processor_->GetAllSyncDataReturnError(
172          type(), &initial_sync_data);
173  if (error.IsSet()) {
174    local_merge_result.set_error(error);
175    StartDone(ASSOCIATION_FAILED,
176              local_merge_result,
177              syncer_merge_result);
178    return;
179  }
180
181  std::string datatype_context;
182  if (shared_change_processor_->GetDataTypeContext(&datatype_context)) {
183    local_service_->UpdateDataTypeContext(
184        type(), syncer::SyncChangeProcessor::NO_REFRESH, datatype_context);
185  }
186
187  syncer_merge_result.set_num_items_before_association(
188      initial_sync_data.size());
189  // Passes a reference to |shared_change_processor_|.
190  local_merge_result = local_service_->MergeDataAndStartSyncing(
191      type(),
192      initial_sync_data,
193      scoped_ptr<syncer::SyncChangeProcessor>(
194          new SharedChangeProcessorRef(shared_change_processor_)),
195      scoped_ptr<syncer::SyncErrorFactory>(
196          new SharedChangeProcessorRef(shared_change_processor_)));
197  RecordAssociationTime(base::TimeTicks::Now() - start_time);
198  if (local_merge_result.error().IsSet()) {
199    StartDone(ASSOCIATION_FAILED,
200              local_merge_result,
201              syncer_merge_result);
202    return;
203  }
204
205  syncer_merge_result.set_num_items_after_association(
206      shared_change_processor_->GetSyncCount());
207
208  state_ = RUNNING;
209  StartDone(sync_has_nodes ? OK : OK_FIRST_RUN,
210            local_merge_result,
211            syncer_merge_result);
212}
213
214ChangeProcessor* UIDataTypeController::GetChangeProcessor() const {
215  DCHECK_EQ(state_, RUNNING);
216  return shared_change_processor_->generic_change_processor();
217}
218
219void UIDataTypeController::AbortModelLoad() {
220  DCHECK(ui_thread_->BelongsToCurrentThread());
221  state_ = NOT_RUNNING;
222
223  if (shared_change_processor_.get()) {
224    shared_change_processor_ = NULL;
225  }
226
227  ModelLoadCallback model_load_callback = model_load_callback_;
228  model_load_callback_.Reset();
229  model_load_callback.Run(type(),
230                          syncer::SyncError(FROM_HERE,
231                                            syncer::SyncError::DATATYPE_ERROR,
232                                            "Aborted",
233                                            type()));
234  // We don't want to continue loading models (e.g OnModelLoaded should never be
235  // called after we've decided to abort).
236  StopModels();
237}
238
239void UIDataTypeController::StartDone(
240    StartResult start_result,
241    const syncer::SyncMergeResult& local_merge_result,
242    const syncer::SyncMergeResult& syncer_merge_result) {
243  DCHECK(ui_thread_->BelongsToCurrentThread());
244
245  if (!IsSuccessfulResult(start_result)) {
246    StopModels();
247    if (start_result == ASSOCIATION_FAILED) {
248      state_ = DISABLED;
249    } else {
250      state_ = NOT_RUNNING;
251    }
252    RecordStartFailure(start_result);
253
254    if (shared_change_processor_.get()) {
255      shared_change_processor_->Disconnect();
256      shared_change_processor_ = NULL;
257    }
258  }
259
260  // We have to release the callback before we call it, since it's possible
261  // invoking the callback will trigger a call to Stop(), which will get
262  // confused by the non-NULL start_callback_.
263  StartCallback callback = start_callback_;
264  start_callback_.Reset();
265  callback.Run(start_result, local_merge_result, syncer_merge_result);
266}
267
268void UIDataTypeController::Stop() {
269  DCHECK(ui_thread_->BelongsToCurrentThread());
270  DCHECK(syncer::IsRealDataType(type_));
271
272  State prev_state = state_;
273  state_ = STOPPING;
274
275  if (shared_change_processor_.get()) {
276    shared_change_processor_->Disconnect();
277    shared_change_processor_ = NULL;
278  }
279
280  // If Stop() is called while Start() is waiting for the datatype model to
281  // load, abort the start.
282  if (prev_state == MODEL_STARTING) {
283    AbortModelLoad();
284    // We can just return here since we haven't performed association if we're
285    // still in MODEL_STARTING.
286    return;
287  }
288  DCHECK(start_callback_.is_null());
289
290  StopModels();
291
292  if (local_service_.get()) {
293    local_service_->StopSyncing(type());
294  }
295
296  state_ = NOT_RUNNING;
297}
298
299syncer::ModelType UIDataTypeController::type() const {
300  DCHECK(syncer::IsRealDataType(type_));
301  return type_;
302}
303
304void UIDataTypeController::StopModels() {
305  // Do nothing by default.
306}
307
308syncer::ModelSafeGroup UIDataTypeController::model_safe_group() const {
309  DCHECK(syncer::IsRealDataType(type_));
310  return syncer::GROUP_UI;
311}
312
313std::string UIDataTypeController::name() const {
314  // For logging only.
315  return syncer::ModelTypeToString(type());
316}
317
318DataTypeController::State UIDataTypeController::state() const {
319  return state_;
320}
321
322void UIDataTypeController::OnSingleDatatypeUnrecoverableError(
323    const tracked_objects::Location& from_here, const std::string& message) {
324  UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures",
325                            ModelTypeToHistogramInt(type()),
326                            syncer::MODEL_TYPE_COUNT);
327  // TODO(tim): We double-upload some errors.  See bug 383480.
328  if (!error_callback_.is_null())
329    error_callback_.Run();
330  if (!disable_callback().is_null())
331    disable_callback().Run(from_here, message);
332}
333
334void UIDataTypeController::RecordAssociationTime(base::TimeDelta time) {
335  DCHECK(ui_thread_->BelongsToCurrentThread());
336#define PER_DATA_TYPE_MACRO(type_str) \
337    UMA_HISTOGRAM_TIMES("Sync." type_str "AssociationTime", time);
338  SYNC_DATA_TYPE_HISTOGRAM(type());
339#undef PER_DATA_TYPE_MACRO
340}
341
342void UIDataTypeController::RecordStartFailure(StartResult result) {
343  DCHECK(ui_thread_->BelongsToCurrentThread());
344  UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures",
345                            ModelTypeToHistogramInt(type()),
346                            syncer::MODEL_TYPE_COUNT);
347#define PER_DATA_TYPE_MACRO(type_str) \
348    UMA_HISTOGRAM_ENUMERATION("Sync." type_str "StartFailure", result, \
349                              MAX_START_RESULT);
350  SYNC_DATA_TYPE_HISTOGRAM(type());
351#undef PER_DATA_TYPE_MACRO
352}
353
354}  // namespace browser_sync
355