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