• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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