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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/sync/glue/data_type_manager_impl.h"
6
7 #include <algorithm>
8 #include <functional>
9
10 #include "base/compiler_specific.h"
11 #include "base/logging.h"
12 #include "base/message_loop.h"
13 #include "chrome/browser/sync/glue/data_type_controller.h"
14 #include "chrome/browser/sync/glue/sync_backend_host.h"
15 #include "content/browser/browser_thread.h"
16 #include "content/common/notification_details.h"
17 #include "content/common/notification_service.h"
18 #include "content/common/notification_source.h"
19
20 namespace browser_sync {
21
22 namespace {
23
24 static const syncable::ModelType kStartOrder[] = {
25 syncable::NIGORI, // Listed for completeness.
26 syncable::BOOKMARKS,
27 syncable::PREFERENCES,
28 syncable::AUTOFILL,
29 syncable::AUTOFILL_PROFILE,
30 syncable::EXTENSIONS,
31 syncable::APPS,
32 syncable::THEMES,
33 syncable::TYPED_URLS,
34 syncable::PASSWORDS,
35 syncable::SESSIONS,
36 };
37
38 COMPILE_ASSERT(arraysize(kStartOrder) ==
39 syncable::MODEL_TYPE_COUNT - syncable::FIRST_REAL_MODEL_TYPE,
40 kStartOrder_IncorrectSize);
41
42 // Comparator used when sorting data type controllers.
43 class SortComparator : public std::binary_function<DataTypeController*,
44 DataTypeController*,
45 bool> {
46 public:
SortComparator(std::map<syncable::ModelType,int> * order)47 explicit SortComparator(std::map<syncable::ModelType, int>* order)
48 : order_(order) { }
49
50 // Returns true if lhs precedes rhs.
operator ()(DataTypeController * lhs,DataTypeController * rhs)51 bool operator() (DataTypeController* lhs, DataTypeController* rhs) {
52 return (*order_)[lhs->type()] < (*order_)[rhs->type()];
53 }
54
55 private:
56 std::map<syncable::ModelType, int>* order_;
57 };
58
59 } // namespace
60
DataTypeManagerImpl(SyncBackendHost * backend,const DataTypeController::TypeMap & controllers)61 DataTypeManagerImpl::DataTypeManagerImpl(SyncBackendHost* backend,
62 const DataTypeController::TypeMap& controllers)
63 : backend_(backend),
64 controllers_(controllers),
65 state_(DataTypeManager::STOPPED),
66 needs_reconfigure_(false),
67 method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
68 DCHECK(backend_);
69 // Ensure all data type controllers are stopped.
70 for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
71 it != controllers_.end(); ++it) {
72 DCHECK_EQ(DataTypeController::NOT_RUNNING, (*it).second->state());
73 }
74
75 // Build a ModelType -> order map for sorting.
76 for (int i = 0; i < static_cast<int>(arraysize(kStartOrder)); i++)
77 start_order_[kStartOrder[i]] = i;
78 }
79
~DataTypeManagerImpl()80 DataTypeManagerImpl::~DataTypeManagerImpl() {}
81
GetControllersNeedingStart(std::vector<DataTypeController * > * needs_start)82 bool DataTypeManagerImpl::GetControllersNeedingStart(
83 std::vector<DataTypeController*>* needs_start) {
84 // Add any data type controllers into the needs_start_ list that are
85 // currently NOT_RUNNING or STOPPING.
86 bool found_any = false;
87 for (TypeSet::const_iterator it = last_requested_types_.begin();
88 it != last_requested_types_.end(); ++it) {
89 DataTypeController::TypeMap::const_iterator dtc = controllers_.find(*it);
90 if (dtc != controllers_.end() &&
91 (dtc->second->state() == DataTypeController::NOT_RUNNING ||
92 dtc->second->state() == DataTypeController::STOPPING)) {
93 found_any = true;
94 if (needs_start)
95 needs_start->push_back(dtc->second.get());
96 }
97 }
98 return found_any;
99 }
100
Configure(const TypeSet & desired_types)101 void DataTypeManagerImpl::Configure(const TypeSet& desired_types) {
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
103 if (state_ == STOPPING) {
104 // You can not set a configuration while stopping.
105 LOG(ERROR) << "Configuration set while stopping.";
106 return;
107 }
108
109 last_requested_types_ = desired_types;
110 // Only proceed if we're in a steady state or blocked.
111 if (state_ != STOPPED && state_ != CONFIGURED && state_ != BLOCKED) {
112 VLOG(1) << "Received configure request while configuration in flight. "
113 << "Postponing until current configuration complete.";
114 needs_reconfigure_ = true;
115 return;
116 }
117
118 needs_start_.clear();
119 GetControllersNeedingStart(&needs_start_);
120 // Sort these according to kStartOrder.
121 std::sort(needs_start_.begin(),
122 needs_start_.end(),
123 SortComparator(&start_order_));
124
125 // Add any data type controllers into that needs_stop_ list that are
126 // currently MODEL_STARTING, ASSOCIATING, or RUNNING.
127 needs_stop_.clear();
128 for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
129 it != controllers_.end(); ++it) {
130 DataTypeController* dtc = (*it).second;
131 if (desired_types.count(dtc->type()) == 0 && (
132 dtc->state() == DataTypeController::MODEL_STARTING ||
133 dtc->state() == DataTypeController::ASSOCIATING ||
134 dtc->state() == DataTypeController::RUNNING)) {
135 needs_stop_.push_back(dtc);
136 VLOG(1) << "Will stop " << dtc->name();
137 }
138 }
139 // Sort these according to kStartOrder.
140 std::sort(needs_stop_.begin(),
141 needs_stop_.end(),
142 SortComparator(&start_order_));
143
144 // Restart to start/stop data types and notify the backend that the
145 // desired types have changed (need to do this even if there aren't any
146 // types to start/stop, because it could be that some types haven't
147 // started due to crypto errors but the backend host needs to know that we're
148 // disabling them anyway).
149 Restart();
150 }
151
Restart()152 void DataTypeManagerImpl::Restart() {
153 VLOG(1) << "Restarting...";
154
155 DCHECK(state_ == STOPPED || state_ == CONFIGURED || state_ == BLOCKED);
156
157 // Starting from a "steady state" (stopped or configured) state
158 // should send a start notification.
159 if (state_ == STOPPED || state_ == CONFIGURED)
160 NotifyStart();
161
162 // Stop requested data types.
163 for (size_t i = 0; i < needs_stop_.size(); ++i) {
164 VLOG(1) << "Stopping " << needs_stop_[i]->name();
165 needs_stop_[i]->Stop();
166 }
167 needs_stop_.clear();
168
169 // Tell the backend about the new set of data types we wish to sync.
170 // The task will be invoked when updates are downloaded.
171 state_ = DOWNLOAD_PENDING;
172 backend_->ConfigureDataTypes(
173 controllers_,
174 last_requested_types_,
175 method_factory_.NewRunnableMethod(&DataTypeManagerImpl::DownloadReady));
176 }
177
DownloadReady()178 void DataTypeManagerImpl::DownloadReady() {
179 DCHECK(state_ == DOWNLOAD_PENDING);
180
181 state_ = CONFIGURING;
182 StartNextType();
183 }
184
StartNextType()185 void DataTypeManagerImpl::StartNextType() {
186 // If there are any data types left to start, start the one at the
187 // front of the list.
188 if (!needs_start_.empty()) {
189 VLOG(1) << "Starting " << needs_start_[0]->name();
190 needs_start_[0]->Start(
191 NewCallback(this, &DataTypeManagerImpl::TypeStartCallback));
192 return;
193 }
194
195 DCHECK_EQ(state_, CONFIGURING);
196
197 if (needs_reconfigure_) {
198 // An attempt was made to reconfigure while we were already configuring.
199 // This can be because a passphrase was accepted or the user changed the
200 // set of desired types. Either way, |last_requested_types_| will contain
201 // the most recent set of desired types, so we just call configure.
202 // Note: we do this whether or not GetControllersNeedingStart is true,
203 // because we may need to stop datatypes.
204 SetBlockedAndNotify();
205 needs_reconfigure_ = false;
206 VLOG(1) << "Reconfiguring due to previous configure attempt occuring while"
207 << " busy.";
208
209 // Unwind the stack before executing configure. The method configure and its
210 // callees are not re-entrant.
211 MessageLoop::current()->PostTask(FROM_HERE,
212 method_factory_.NewRunnableMethod(&DataTypeManagerImpl::Configure,
213 last_requested_types_));
214 return;
215 }
216
217 // Do a fresh calculation to see if controllers need starting to account for
218 // things like encryption, which may still need to be sorted out before we
219 // can announce we're "Done" configuration entirely.
220 if (GetControllersNeedingStart(NULL)) {
221 SetBlockedAndNotify();
222 return;
223 }
224
225 // If no more data types need starting, we're done.
226 state_ = CONFIGURED;
227 NotifyDone(OK, FROM_HERE);
228 }
229
TypeStartCallback(DataTypeController::StartResult result,const tracked_objects::Location & location)230 void DataTypeManagerImpl::TypeStartCallback(
231 DataTypeController::StartResult result,
232 const tracked_objects::Location& location) {
233 // When the data type controller invokes this callback, it must be
234 // on the UI thread.
235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
236
237 if (state_ == STOPPING) {
238 // If we reach this callback while stopping, this means that
239 // DataTypeManager::Stop() was called while the current data type
240 // was starting. Now that it has finished starting, we can finish
241 // stopping the DataTypeManager. This is considered an ABORT.
242 FinishStopAndNotify(ABORTED, FROM_HERE);
243 return;
244 } else if (state_ == STOPPED) {
245 // If our state_ is STOPPED, we have already stopped all of the data
246 // types. We should not be getting callbacks from stopped data types.
247 LOG(ERROR) << "Start callback called by stopped data type!";
248 return;
249 }
250
251 // We're done with the data type at the head of the list -- remove it.
252 DataTypeController* started_dtc = needs_start_[0];
253 DCHECK(needs_start_.size());
254 DCHECK_EQ(needs_start_[0], started_dtc);
255 needs_start_.erase(needs_start_.begin());
256
257 if (result == DataTypeController::NEEDS_CRYPTO) {
258
259 }
260 // If the type started normally, continue to the next type.
261 // If the type is waiting for the cryptographer, continue to the next type.
262 // Once the cryptographer is ready, we'll attempt to restart this type.
263 if (result == DataTypeController::NEEDS_CRYPTO ||
264 result == DataTypeController::OK ||
265 result == DataTypeController::OK_FIRST_RUN) {
266 StartNextType();
267 return;
268 }
269
270 // Any other result is a fatal error. Shut down any types we've
271 // managed to start up to this point and pass the result to the
272 // callback.
273 VLOG(1) << "Failed " << started_dtc->name();
274 ConfigureResult configure_result = DataTypeManager::ABORTED;
275 switch (result) {
276 case DataTypeController::ABORTED:
277 configure_result = DataTypeManager::ABORTED;
278 break;
279 case DataTypeController::ASSOCIATION_FAILED:
280 configure_result = DataTypeManager::ASSOCIATION_FAILED;
281 break;
282 case DataTypeController::UNRECOVERABLE_ERROR:
283 configure_result = DataTypeManager::UNRECOVERABLE_ERROR;
284 break;
285 default:
286 NOTREACHED();
287 break;
288 }
289 FinishStopAndNotify(configure_result, location);
290 }
291
Stop()292 void DataTypeManagerImpl::Stop() {
293 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
294 if (state_ == STOPPED)
295 return;
296
297 // If we are currently configuring, then the current type is in a
298 // partially started state. Abort the startup of the current type,
299 // which will synchronously invoke the start callback.
300 if (state_ == CONFIGURING) {
301 state_ = STOPPING;
302
303 DCHECK_LT(0U, needs_start_.size());
304 needs_start_[0]->Stop();
305
306 // By this point, the datatype should have invoked the start callback,
307 // triggering FinishStop to be called, and the state to reach STOPPED. If we
308 // aren't STOPPED, it means that a datatype controller didn't call the start
309 // callback appropriately.
310 DCHECK_EQ(STOPPED, state_);
311 return;
312 }
313
314 const bool download_pending = state_ == DOWNLOAD_PENDING;
315 state_ = STOPPING;
316 if (download_pending) {
317 // If Stop() is called while waiting for download, cancel all
318 // outstanding tasks.
319 method_factory_.RevokeAll();
320 FinishStopAndNotify(ABORTED, FROM_HERE);
321 return;
322 }
323
324 FinishStop();
325 }
326
FinishStop()327 void DataTypeManagerImpl::FinishStop() {
328 DCHECK(state_== CONFIGURING || state_ == STOPPING || state_ == BLOCKED);
329 // Simply call the Stop() method on all running data types.
330 for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
331 it != controllers_.end(); ++it) {
332 DataTypeController* dtc = (*it).second;
333 if (dtc->state() != DataTypeController::NOT_RUNNING &&
334 dtc->state() != DataTypeController::STOPPING) {
335 dtc->Stop();
336 VLOG(1) << "Stopped " << dtc->name();
337 }
338 }
339 state_ = STOPPED;
340 }
341
FinishStopAndNotify(ConfigureResult result,const tracked_objects::Location & location)342 void DataTypeManagerImpl::FinishStopAndNotify(ConfigureResult result,
343 const tracked_objects::Location& location) {
344 FinishStop();
345 NotifyDone(result, location);
346 }
347
NotifyStart()348 void DataTypeManagerImpl::NotifyStart() {
349 NotificationService::current()->Notify(
350 NotificationType::SYNC_CONFIGURE_START,
351 Source<DataTypeManager>(this),
352 NotificationService::NoDetails());
353 }
354
NotifyDone(ConfigureResult result,const tracked_objects::Location & location)355 void DataTypeManagerImpl::NotifyDone(ConfigureResult result,
356 const tracked_objects::Location& location) {
357 ConfigureResultWithErrorLocation result_with_location(result, location,
358 last_requested_types_);
359 NotificationService::current()->Notify(
360 NotificationType::SYNC_CONFIGURE_DONE,
361 Source<DataTypeManager>(this),
362 Details<ConfigureResultWithErrorLocation>(&result_with_location));
363 }
364
controllers()365 const DataTypeController::TypeMap& DataTypeManagerImpl::controllers() {
366 return controllers_;
367 }
368
state()369 DataTypeManager::State DataTypeManagerImpl::state() {
370 return state_;
371 }
372
SetBlockedAndNotify()373 void DataTypeManagerImpl::SetBlockedAndNotify() {
374 state_ = BLOCKED;
375 NotificationService::current()->Notify(
376 NotificationType::SYNC_CONFIGURE_BLOCKED,
377 Source<DataTypeManager>(this),
378 NotificationService::NoDetails());
379 }
380
381 } // namespace browser_sync
382