Home | History | Annotate | Download | only in sync
      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/backend_migrator.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/string_number_conversions.h"
     10 #include "chrome/browser/sync/profile_sync_service.h"
     11 #include "chrome/browser/sync/glue/data_type_manager.h"
     12 #include "chrome/browser/sync/sessions/session_state.h"
     13 #include "content/browser/browser_thread.h"
     14 #include "content/common/notification_details.h"
     15 #include "content/common/notification_source.h"
     16 
     17 using syncable::ModelTypeSet;
     18 
     19 namespace browser_sync {
     20 
     21 using sessions::SyncSessionSnapshot;
     22 
     23 BackendMigrator::BackendMigrator(ProfileSyncService* service,
     24                                  DataTypeManager* manager)
     25     : state_(IDLE), service_(service), manager_(manager),
     26       restart_migration_(false),
     27       method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
     28   registrar_.Add(this, NotificationType::SYNC_CONFIGURE_DONE,
     29                  Source<DataTypeManager>(manager_));
     30   service_->AddObserver(this);
     31 }
     32 
     33 BackendMigrator::~BackendMigrator() {
     34   service_->RemoveObserver(this);
     35 }
     36 
     37 bool BackendMigrator::HasStartedMigrating() const {
     38   return state_ >= DISABLING_TYPES;
     39 }
     40 
     41 void BackendMigrator::MigrateTypes(const syncable::ModelTypeSet& types) {
     42   {
     43     ModelTypeSet temp;
     44     std::set_union(to_migrate_.begin(), to_migrate_.end(),
     45                    types.begin(), types.end(),
     46                    std::inserter(temp, temp.end()));
     47     to_migrate_ = temp;
     48   }
     49 
     50   if (HasStartedMigrating()) {
     51     VLOG(1) << "BackendMigrator::MigrateTypes: STARTED_MIGRATING early-out.";
     52     restart_migration_ = true;
     53     return;
     54   }
     55 
     56   if (manager_->state() != DataTypeManager::CONFIGURED) {
     57     VLOG(1) << "BackendMigrator::MigrateTypes: manager CONFIGURED early-out.";
     58     state_ = WAITING_TO_START;
     59     return;
     60   }
     61 
     62   // We'll now disable any running types that need to be migrated.
     63   state_ = DISABLING_TYPES;
     64   ModelTypeSet full_set;
     65   service_->GetPreferredDataTypes(&full_set);
     66   ModelTypeSet difference;
     67   std::set_difference(full_set.begin(), full_set.end(),
     68                       to_migrate_.begin(), to_migrate_.end(),
     69                       std::inserter(difference, difference.end()));
     70   VLOG(1) << "BackendMigrator disabling types; calling Configure.";
     71   manager_->Configure(difference);
     72 }
     73 
     74 void BackendMigrator::OnStateChanged() {
     75   if (restart_migration_ == true) {
     76     VLOG(1) << "BackendMigrator restarting migration in OnStateChanged.";
     77     state_ = WAITING_TO_START;
     78     restart_migration_ = false;
     79     MigrateTypes(to_migrate_);
     80     return;
     81   }
     82 
     83   if (state_ != WAITING_FOR_PURGE)
     84     return;
     85 
     86   size_t num_empty_migrated_markers = 0;
     87   const SyncSessionSnapshot* snap = service_->GetLastSessionSnapshot();
     88   for (ModelTypeSet::const_iterator it = to_migrate_.begin();
     89        it != to_migrate_.end(); ++it) {
     90     if (snap->download_progress_markers[*it].empty())
     91       num_empty_migrated_markers++;
     92   }
     93 
     94   if (num_empty_migrated_markers < to_migrate_.size())
     95     return;
     96 
     97   state_ = REENABLING_TYPES;
     98   ModelTypeSet full_set;
     99   service_->GetPreferredDataTypes(&full_set);
    100   VLOG(1) << "BackendMigrator re-enabling types.";
    101   // Don't use |to_migrate_| for the re-enabling because the user may have
    102   // chosen to disable types during the migration.
    103   manager_->Configure(full_set);
    104 }
    105 
    106 void BackendMigrator::Observe(NotificationType type,
    107                               const NotificationSource& source,
    108                               const NotificationDetails& details) {
    109   DCHECK_EQ(NotificationType::SYNC_CONFIGURE_DONE, type.value);
    110   if (state_ == IDLE)
    111     return;
    112 
    113   DataTypeManager::ConfigureResultWithErrorLocation* result =
    114       Details<DataTypeManager::ConfigureResultWithErrorLocation>(
    115           details).ptr();
    116 
    117   ModelTypeSet intersection;
    118   std::set_intersection(result->requested_types.begin(),
    119       result->requested_types.end(), to_migrate_.begin(), to_migrate_.end(),
    120       std::inserter(intersection, intersection.end()));
    121 
    122   // The intersection check is to determine if our disable request was
    123   // interrupted by a user changing preferred types.  May still need to purge.
    124   // It's pretty wild if we're in WAITING_FOR_PURGE here, because it would mean
    125   // that after our disable-config finished but before the purge, another config
    126   // was posted externally _and completed_, which means somehow the nudge to
    127   // purge was dropped, yet nudges are reliable.
    128   if (state_ == WAITING_TO_START || state_ == WAITING_FOR_PURGE ||
    129       (state_ == DISABLING_TYPES && !intersection.empty())) {
    130     state_ = WAITING_TO_START;
    131     restart_migration_ = false;
    132     VLOG(1) << "BackendMigrator::Observe posting MigrateTypes.";
    133     if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    134         method_factory_.NewRunnableMethod(&BackendMigrator::MigrateTypes,
    135                                           to_migrate_))) {
    136       // Unittests need this.
    137       // TODO(tim): Clean this up.
    138       MigrateTypes(to_migrate_);
    139     }
    140     return;
    141   }
    142 
    143   if (result->result != DataTypeManager::OK) {
    144     // If this fails, and we're disabling types, a type may or may not be
    145     // disabled until the user restarts the browser.  If this wasn't an abort,
    146     // any failure will be reported as an unrecoverable error to the UI. If it
    147     // was an abort, then typically things are shutting down anyway. There isn't
    148     // much we can do in any case besides wait until a restart to try again.
    149     // The server will send down MIGRATION_DONE again for types needing
    150     // migration as the type will still be enabled on restart.
    151     LOG(WARNING) << "Unable to migrate, configuration failed!";
    152     state_ = IDLE;
    153     to_migrate_.clear();
    154     return;
    155   }
    156 
    157   if (state_ == DISABLING_TYPES) {
    158     state_ = WAITING_FOR_PURGE;
    159     VLOG(1) << "BackendMigrator waiting for purge.";
    160   } else if (state_ == REENABLING_TYPES) {
    161     // We're done!
    162     state_ = IDLE;
    163 
    164     std::stringstream ss;
    165     std::copy(to_migrate_.begin(), to_migrate_.end(),
    166               std::ostream_iterator<syncable::ModelType>(ss, ","));
    167     VLOG(1) << "BackendMigrator: Migration complete for: " << ss.str();
    168     to_migrate_.clear();
    169   }
    170 }
    171 
    172 BackendMigrator::State BackendMigrator::state() const {
    173   return state_;
    174 }
    175 
    176 };  // namespace browser_sync
    177