Home | History | Annotate | Download | only in glue
      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