// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/sync/backend_migrator.h"
#include <algorithm>
#include "base/string_number_conversions.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/glue/data_type_manager.h"
#include "chrome/browser/sync/sessions/session_state.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_details.h"
#include "content/common/notification_source.h"
using syncable::ModelTypeSet;
namespace browser_sync {
using sessions::SyncSessionSnapshot;
BackendMigrator::BackendMigrator(ProfileSyncService* service,
DataTypeManager* manager)
: state_(IDLE), service_(service), manager_(manager),
restart_migration_(false),
method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
registrar_.Add(this, NotificationType::SYNC_CONFIGURE_DONE,
Source<DataTypeManager>(manager_));
service_->AddObserver(this);
}
BackendMigrator::~BackendMigrator() {
service_->RemoveObserver(this);
}
bool BackendMigrator::HasStartedMigrating() const {
return state_ >= DISABLING_TYPES;
}
void BackendMigrator::MigrateTypes(const syncable::ModelTypeSet& types) {
{
ModelTypeSet temp;
std::set_union(to_migrate_.begin(), to_migrate_.end(),
types.begin(), types.end(),
std::inserter(temp, temp.end()));
to_migrate_ = temp;
}
if (HasStartedMigrating()) {
VLOG(1) << "BackendMigrator::MigrateTypes: STARTED_MIGRATING early-out.";
restart_migration_ = true;
return;
}
if (manager_->state() != DataTypeManager::CONFIGURED) {
VLOG(1) << "BackendMigrator::MigrateTypes: manager CONFIGURED early-out.";
state_ = WAITING_TO_START;
return;
}
// We'll now disable any running types that need to be migrated.
state_ = DISABLING_TYPES;
ModelTypeSet full_set;
service_->GetPreferredDataTypes(&full_set);
ModelTypeSet difference;
std::set_difference(full_set.begin(), full_set.end(),
to_migrate_.begin(), to_migrate_.end(),
std::inserter(difference, difference.end()));
VLOG(1) << "BackendMigrator disabling types; calling Configure.";
manager_->Configure(difference);
}
void BackendMigrator::OnStateChanged() {
if (restart_migration_ == true) {
VLOG(1) << "BackendMigrator restarting migration in OnStateChanged.";
state_ = WAITING_TO_START;
restart_migration_ = false;
MigrateTypes(to_migrate_);
return;
}
if (state_ != WAITING_FOR_PURGE)
return;
size_t num_empty_migrated_markers = 0;
const SyncSessionSnapshot* snap = service_->GetLastSessionSnapshot();
for (ModelTypeSet::const_iterator it = to_migrate_.begin();
it != to_migrate_.end(); ++it) {
if (snap->download_progress_markers[*it].empty())
num_empty_migrated_markers++;
}
if (num_empty_migrated_markers < to_migrate_.size())
return;
state_ = REENABLING_TYPES;
ModelTypeSet full_set;
service_->GetPreferredDataTypes(&full_set);
VLOG(1) << "BackendMigrator re-enabling types.";
// Don't use |to_migrate_| for the re-enabling because the user may have
// chosen to disable types during the migration.
manager_->Configure(full_set);
}
void BackendMigrator::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
DCHECK_EQ(NotificationType::SYNC_CONFIGURE_DONE, type.value);
if (state_ == IDLE)
return;
DataTypeManager::ConfigureResultWithErrorLocation* result =
Details<DataTypeManager::ConfigureResultWithErrorLocation>(
details).ptr();
ModelTypeSet intersection;
std::set_intersection(result->requested_types.begin(),
result->requested_types.end(), to_migrate_.begin(), to_migrate_.end(),
std::inserter(intersection, intersection.end()));
// The intersection check is to determine if our disable request was
// interrupted by a user changing preferred types. May still need to purge.
// It's pretty wild if we're in WAITING_FOR_PURGE here, because it would mean
// that after our disable-config finished but before the purge, another config
// was posted externally _and completed_, which means somehow the nudge to
// purge was dropped, yet nudges are reliable.
if (state_ == WAITING_TO_START || state_ == WAITING_FOR_PURGE ||
(state_ == DISABLING_TYPES && !intersection.empty())) {
state_ = WAITING_TO_START;
restart_migration_ = false;
VLOG(1) << "BackendMigrator::Observe posting MigrateTypes.";
if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
method_factory_.NewRunnableMethod(&BackendMigrator::MigrateTypes,
to_migrate_))) {
// Unittests need this.
// TODO(tim): Clean this up.
MigrateTypes(to_migrate_);
}
return;
}
if (result->result != DataTypeManager::OK) {
// If this fails, and we're disabling types, a type may or may not be
// disabled until the user restarts the browser. If this wasn't an abort,
// any failure will be reported as an unrecoverable error to the UI. If it
// was an abort, then typically things are shutting down anyway. There isn't
// much we can do in any case besides wait until a restart to try again.
// The server will send down MIGRATION_DONE again for types needing
// migration as the type will still be enabled on restart.
LOG(WARNING) << "Unable to migrate, configuration failed!";
state_ = IDLE;
to_migrate_.clear();
return;
}
if (state_ == DISABLING_TYPES) {
state_ = WAITING_FOR_PURGE;
VLOG(1) << "BackendMigrator waiting for purge.";
} else if (state_ == REENABLING_TYPES) {
// We're done!
state_ = IDLE;
std::stringstream ss;
std::copy(to_migrate_.begin(), to_migrate_.end(),
std::ostream_iterator<syncable::ModelType>(ss, ","));
VLOG(1) << "BackendMigrator: Migration complete for: " << ss.str();
to_migrate_.clear();
}
}
BackendMigrator::State BackendMigrator::state() const {
return state_;
}
}; // namespace browser_sync