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: 47 explicit SortComparator(std::map<syncable::ModelType, int>* order) 48 : order_(order) { } 49 50 // Returns true if lhs precedes 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 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 80 DataTypeManagerImpl::~DataTypeManagerImpl() {} 81 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 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 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 178 void DataTypeManagerImpl::DownloadReady() { 179 DCHECK(state_ == DOWNLOAD_PENDING); 180 181 state_ = CONFIGURING; 182 StartNextType(); 183 } 184 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 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 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 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 342 void DataTypeManagerImpl::FinishStopAndNotify(ConfigureResult result, 343 const tracked_objects::Location& location) { 344 FinishStop(); 345 NotifyDone(result, location); 346 } 347 348 void DataTypeManagerImpl::NotifyStart() { 349 NotificationService::current()->Notify( 350 NotificationType::SYNC_CONFIGURE_START, 351 Source<DataTypeManager>(this), 352 NotificationService::NoDetails()); 353 } 354 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 365 const DataTypeController::TypeMap& DataTypeManagerImpl::controllers() { 366 return controllers_; 367 } 368 369 DataTypeManager::State DataTypeManagerImpl::state() { 370 return state_; 371 } 372 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