summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-20 01:59:35 +0000
committertim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-04-20 01:59:35 +0000
commitdc55625cce6709a969635777c8df19c1e9b055a2 (patch)
tree4e005970bc1382f8765c19ac5b011f10a4367261
parente0e087f07253fe3d95aeafdfa337463d4334b7fa (diff)
downloadchromium_src-dc55625cce6709a969635777c8df19c1e9b055a2.zip
chromium_src-dc55625cce6709a969635777c8df19c1e9b055a2.tar.gz
chromium_src-dc55625cce6709a969635777c8df19c1e9b055a2.tar.bz2
sync: add a BackendMigrator class to perform migration flow.
The rest of the change (more innocent than it looks) is mostly callback plumbing to make the long trek from SyncerProtoUtil up to ProfileSyncService. BUG=79247 TEST=BackendMigratorTest Review URL: http://codereview.chromium.org/6858002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@82215 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/resources/sync_internals/chrome_sync.js10
-rw-r--r--chrome/browser/resources/sync_internals/sync_log.js12
-rw-r--r--chrome/browser/sync/backend_migrator.cc170
-rw-r--r--chrome/browser/sync/backend_migrator.h72
-rw-r--r--chrome/browser/sync/backend_migrator_unittest.cc267
-rw-r--r--chrome/browser/sync/engine/all_status.cc4
-rw-r--r--chrome/browser/sync/engine/syncapi.cc6
-rw-r--r--chrome/browser/sync/engine/syncapi.h4
-rw-r--r--chrome/browser/sync/engine/syncapi_unittest.cc1
-rw-r--r--chrome/browser/sync/engine/syncer_proto_util.cc19
-rw-r--r--chrome/browser/sync/engine/syncer_proto_util.h6
-rw-r--r--chrome/browser/sync/engine/syncer_thread.cc5
-rw-r--r--chrome/browser/sync/engine/syncer_types.h6
-rw-r--r--chrome/browser/sync/glue/data_type_manager.h11
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl.cc3
-rw-r--r--chrome/browser/sync/glue/data_type_manager_mock.cc2
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.cc28
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.h10
-rw-r--r--chrome/browser/sync/glue/sync_backend_host_unittest.cc3
-rw-r--r--chrome/browser/sync/js_sync_manager_observer.cc9
-rw-r--r--chrome/browser/sync/js_sync_manager_observer.h1
-rw-r--r--chrome/browser/sync/js_sync_manager_observer_unittest.cc22
-rw-r--r--chrome/browser/sync/profile_sync_service.cc15
-rw-r--r--chrome/browser/sync/profile_sync_service.h7
-rw-r--r--chrome/browser/sync/profile_sync_service_mock.h2
-rw-r--r--chrome/browser/sync/profile_sync_service_startup_unittest.cc2
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
28 files changed, 687 insertions, 13 deletions
diff --git a/chrome/browser/resources/sync_internals/chrome_sync.js b/chrome/browser/resources/sync_internals/chrome_sync.js
index 9782ed13..0340f63 100644
--- a/chrome/browser/resources/sync_internals/chrome_sync.js
+++ b/chrome/browser/resources/sync_internals/chrome_sync.js
@@ -77,6 +77,8 @@ chrome.sync.onAuthError = new Event();
chrome.sync.onUpdatedToken = new Event();
chrome.sync.onPassphraseRequired = new Event();
chrome.sync.onPassphraseAccepted = new Event();
+chrome.sync.onEncryptionComplete = new Event();
+chrome.sync.onMigrationNeededForTypes = new Event();
chrome.sync.onInitializationComplete = new Event();
chrome.sync.onPaused = new Event();
chrome.sync.onResumed = new Event();
@@ -186,6 +188,14 @@ function onPassphraseAccepted(bootstrapToken) {
chrome.sync.onPassphraseAccepted.dispatch_(bootstrapToken);
}
+function onEncryptionComplete(encrypted_types) {
+ chrome.sync.onEncryptionComplete.dispatch_(encrypted_types);
+}
+
+function onMigrationNeededForTypes(model_types) {
+ chrome.sync.onMigrationNeededForTypes.dispatch_(model_types);
+}
+
function onInitializationComplete() {
chrome.sync.onInitializationComplete.dispatch_();
}
diff --git a/chrome/browser/resources/sync_internals/sync_log.js b/chrome/browser/resources/sync_internals/sync_log.js
index 893bac2..704fd5a 100644
--- a/chrome/browser/resources/sync_internals/sync_log.js
+++ b/chrome/browser/resources/sync_internals/sync_log.js
@@ -87,6 +87,18 @@ cr.define('chrome.sync', function() {
});
});
+ chrome.sync.onEncryptionComplete.addListener(function (encrypted_types) {
+ self.log_('manager', 'onEncryptionComplete', {
+ encrypted_types: encrypted_types
+ });
+ });
+
+ chrome.sync.onMigrationNeededForTypes.addListener(function (model_types) {
+ self.log_('manager', 'onMigrationNeededForTypes', {
+ model_types: model_types
+ });
+ });
+
chrome.sync.onInitializationComplete.addListener(function () {
self.log_('manager', 'onInitializationComplete', {});
});
diff --git a/chrome/browser/sync/backend_migrator.cc b/chrome/browser/sync/backend_migrator.cc
new file mode 100644
index 0000000..9ce0203
--- /dev/null
+++ b/chrome/browser/sync/backend_migrator.cc
@@ -0,0 +1,170 @@
+// 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 "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 (state_ >= STARTED_MIGRATING) {
+ 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;
+ to_migrate_.clear();
+ }
+}
+
+BackendMigrator::State BackendMigrator::state() const {
+ return state_;
+}
+
+}; // namespace browser_sync
diff --git a/chrome/browser/sync/backend_migrator.h b/chrome/browser/sync/backend_migrator.h
new file mode 100644
index 0000000..230e7d8
--- /dev/null
+++ b/chrome/browser/sync/backend_migrator.h
@@ -0,0 +1,72 @@
+// 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.
+
+#ifndef CHROME_BROWSER_SYNC_BACKEND_MIGRATOR_H_
+#define CHROME_BROWSER_SYNC_BACKEND_MIGRATOR_H_
+
+#include "base/task.h"
+#include "chrome/browser/sync/profile_sync_service_observer.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+
+class ProfileSyncService;
+
+namespace browser_sync {
+
+class DataTypeManager;
+
+// A class to perform migration of a datatype pursuant to the 'MIGRATION_DONE'
+// code in the sync protocol definition (protocol/sync.proto).
+class BackendMigrator : public NotificationObserver,
+ public ProfileSyncServiceObserver {
+ public:
+ enum State {
+ IDLE,
+ WAITING_TO_START, // Waiting for previous configuration to finish.
+ DISABLING_TYPES, // Exit criteria: SYNC_CONFIGURE_DONE for enabled
+ // types _excluding_ |to_migrate_|.
+ STARTED_MIGRATING = DISABLING_TYPES,
+ WAITING_FOR_PURGE, // Exit criteria: SyncCycleEnded for enabled types
+ // excluding |to_migrate|
+ REENABLING_TYPES, // Exit criteria: SYNC_CONFIGURE_DONE for enabled
+ // types.
+ };
+
+ BackendMigrator(ProfileSyncService* service, DataTypeManager* manager);
+ virtual ~BackendMigrator();
+
+ // Starts a sequence of events that will disable and reenable |types|.
+ void MigrateTypes(const syncable::ModelTypeSet& types);
+
+ // ProfileSyncServiceObserver implementation.
+ virtual void OnStateChanged();
+
+ // NotificationObserver implementation.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ State state() const;
+
+ private:
+ bool HasStartedMigrating() const;
+
+ State state_;
+ ProfileSyncService* service_;
+ DataTypeManager* manager_;
+ NotificationRegistrar registrar_;
+
+ syncable::ModelTypeSet to_migrate_;
+ bool restart_migration_;
+
+ // We use this to gracefully re-start migrations.
+ ScopedRunnableMethodFactory<BackendMigrator> method_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackendMigrator);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_BACKEND_MIGRATOR_H_
diff --git a/chrome/browser/sync/backend_migrator_unittest.cc b/chrome/browser/sync/backend_migrator_unittest.cc
new file mode 100644
index 0000000..39b2ef0
--- /dev/null
+++ b/chrome/browser/sync/backend_migrator_unittest.cc
@@ -0,0 +1,267 @@
+// 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 "chrome/browser/sync/glue/data_type_manager_mock.h"
+#include "chrome/browser/sync/profile_sync_service_mock.h"
+#include "chrome/browser/sync/sessions/session_state.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+
+namespace browser_sync {
+
+using sessions::ErrorCounters;
+using sessions::SyncerStatus;
+using sessions::SyncSessionSnapshot;
+
+class BackendMigratorTest : public testing::Test {
+ public:
+ BackendMigratorTest() { }
+ virtual ~BackendMigratorTest() { }
+
+ virtual void SetUp() {
+ Mock::VerifyAndClear(manager());
+ Mock::VerifyAndClear(&service_);
+ preferred_types_.insert(syncable::BOOKMARKS);
+ preferred_types_.insert(syncable::PREFERENCES);
+ preferred_types_.insert(syncable::AUTOFILL);
+
+ ON_CALL(service_, GetPreferredDataTypes(_)).
+ WillByDefault(SetArgumentPointee<0>(preferred_types_));
+ }
+
+ void ReturnEmptyProgressMarkersInSnapshot() {
+ ReturnNonEmptyProgressMarkersInSnapshot(syncable::ModelTypeSet());
+ }
+
+ void ReturnNonEmptyProgressMarkersInSnapshot(
+ const syncable::ModelTypeSet& for_types) {
+ std::string download_progress_markers[syncable::MODEL_TYPE_COUNT];
+ for (syncable::ModelTypeSet::const_iterator it = for_types.begin();
+ it != for_types.end(); ++it) {
+ download_progress_markers[*it] = "foobar";
+ }
+
+ snap_.reset(new SyncSessionSnapshot(SyncerStatus(), ErrorCounters(),
+ 0, false, syncable::ModelTypeBitSet(), download_progress_markers,
+ false, false, 0, 0, false, sessions::SyncSourceInfo()));
+ EXPECT_CALL(service_, GetLastSessionSnapshot())
+ .WillOnce(Return(snap_.get()));
+ }
+
+ void SendConfigureDone(DataTypeManager::ConfigureResult result,
+ const syncable::ModelTypeSet& types) {
+ DataTypeManager::ConfigureResultWithErrorLocation result_with_location(
+ result, FROM_HERE, types);
+ NotificationService::current()->Notify(
+ NotificationType::SYNC_CONFIGURE_DONE,
+ Source<DataTypeManager>(&manager_),
+ Details<DataTypeManager::ConfigureResultWithErrorLocation>(
+ &result_with_location));
+ }
+
+ ProfileSyncService* service() { return &service_; }
+ DataTypeManagerMock* manager() { return &manager_; }
+ const syncable::ModelTypeSet& preferred_types() { return preferred_types_; }
+ void RemovePreferredType(syncable::ModelType type) {
+ preferred_types_.erase(type);
+ Mock::VerifyAndClear(&service_);
+ ON_CALL(service_, GetPreferredDataTypes(_)).
+ WillByDefault(SetArgumentPointee<0>(preferred_types_));
+ }
+ private:
+ scoped_ptr<SyncSessionSnapshot> snap_;
+ syncable::ModelTypeSet preferred_types_;
+ NiceMock<ProfileSyncServiceMock> service_;
+ NiceMock<DataTypeManagerMock> manager_;
+};
+
+// Test that in the normal case a migration does transition through each state
+// and wind up back in IDLE.
+TEST_F(BackendMigratorTest, Sanity) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate, difference;
+ to_migrate.insert(syncable::PREFERENCES);
+ difference.insert(syncable::AUTOFILL);
+ difference.insert(syncable::BOOKMARKS);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(_));
+
+ migrator.MigrateTypes(to_migrate);
+ EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
+
+ SendConfigureDone(DataTypeManager::OK, difference);
+ EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
+
+ ReturnEmptyProgressMarkersInSnapshot();
+ EXPECT_CALL(*manager(), Configure(preferred_types()));
+ migrator.OnStateChanged();
+ EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator.state());
+
+ SendConfigureDone(DataTypeManager::OK, preferred_types());
+ EXPECT_EQ(BackendMigrator::IDLE, migrator.state());
+}
+
+// Test that the migrator waits for the data type manager to be idle before
+// starting a migration.
+TEST_F(BackendMigratorTest, WaitToStart) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate;
+ to_migrate.insert(syncable::PREFERENCES);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURING));
+ EXPECT_CALL(*manager(), Configure(_)).Times(0);
+ migrator.MigrateTypes(to_migrate);
+ EXPECT_EQ(BackendMigrator::WAITING_TO_START, migrator.state());
+
+ Mock::VerifyAndClearExpectations(manager());
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(_));
+ SendConfigureDone(DataTypeManager::OK, syncable::ModelTypeSet());
+
+ EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
+}
+
+// Test that the migrator can cope with a migration request while a migration
+// is in progress.
+TEST_F(BackendMigratorTest, RestartMigration) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate1, to_migrate2, bookmarks;
+ to_migrate1.insert(syncable::PREFERENCES);
+ to_migrate2.insert(syncable::AUTOFILL);
+ bookmarks.insert(syncable::BOOKMARKS);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(_)).Times(1);
+ migrator.MigrateTypes(to_migrate1);
+
+ EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
+ migrator.MigrateTypes(to_migrate2);
+
+ syncable::ModelTypeSet difference1;
+ std::set_difference(preferred_types().begin(), preferred_types().end(),
+ to_migrate1.begin(), to_migrate1.end(),
+ std::inserter(difference1, difference1.end()));
+
+ Mock::VerifyAndClearExpectations(manager());
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(bookmarks));
+ SendConfigureDone(DataTypeManager::OK, difference1);
+ EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
+
+ SendConfigureDone(DataTypeManager::OK, bookmarks);
+ EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
+}
+
+// Test that an external invocation of Configure(...) during a migration results
+// in a migration reattempt.
+TEST_F(BackendMigratorTest, InterruptedWhileDisablingTypes) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate;
+ syncable::ModelTypeSet difference;
+ to_migrate.insert(syncable::PREFERENCES);
+ difference.insert(syncable::AUTOFILL);
+ difference.insert(syncable::BOOKMARKS);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(difference));
+ migrator.MigrateTypes(to_migrate);
+ EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
+
+ Mock::VerifyAndClearExpectations(manager());
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(difference));
+ SendConfigureDone(DataTypeManager::OK, preferred_types());
+
+ EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
+}
+
+// Test that spurious OnStateChanged events don't confuse the migrator while
+// it's waiting for disabled types to have been purged from the sync db.
+TEST_F(BackendMigratorTest, WaitingForPurge) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate, difference;
+ to_migrate.insert(syncable::PREFERENCES);
+ to_migrate.insert(syncable::AUTOFILL);
+ difference.insert(syncable::BOOKMARKS);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(_));
+ migrator.MigrateTypes(to_migrate);
+ SendConfigureDone(DataTypeManager::OK, difference);
+ EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
+
+ ReturnNonEmptyProgressMarkersInSnapshot(to_migrate);
+ migrator.OnStateChanged();
+ EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
+
+ syncable::ModelTypeSet prefs;
+ prefs.insert(syncable::PREFERENCES);
+ ReturnNonEmptyProgressMarkersInSnapshot(prefs);
+ migrator.OnStateChanged();
+ EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
+
+ syncable::ModelTypeSet bookmarks;
+ bookmarks.insert(syncable::BOOKMARKS);
+ ReturnNonEmptyProgressMarkersInSnapshot(bookmarks);
+ EXPECT_CALL(*manager(), Configure(preferred_types()));
+ migrator.OnStateChanged();
+ EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator.state());
+}
+
+TEST_F(BackendMigratorTest, MigratedTypeDisabledByUserDuringMigration) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate;
+ to_migrate.insert(syncable::PREFERENCES);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(_));
+ migrator.MigrateTypes(to_migrate);
+
+ RemovePreferredType(syncable::PREFERENCES);
+ SendConfigureDone(DataTypeManager::OK, preferred_types());
+ EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
+
+ Mock::VerifyAndClearExpectations(manager());
+ ReturnEmptyProgressMarkersInSnapshot();
+ EXPECT_CALL(*manager(), Configure(preferred_types()));
+ migrator.OnStateChanged();
+
+ EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator.state());
+ SendConfigureDone(DataTypeManager::OK, preferred_types());
+ EXPECT_EQ(BackendMigrator::IDLE, migrator.state());
+}
+
+TEST_F(BackendMigratorTest, ConfigureFailure) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate;
+ to_migrate.insert(syncable::PREFERENCES);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(_)).Times(1);
+ migrator.MigrateTypes(to_migrate);
+ SendConfigureDone(DataTypeManager::ABORTED, syncable::ModelTypeSet());
+ EXPECT_EQ(BackendMigrator::IDLE, migrator.state());
+}
+
+}; // namespace browser_sync
diff --git a/chrome/browser/sync/engine/all_status.cc b/chrome/browser/sync/engine/all_status.cc
index 0b454a3..575113e 100644
--- a/chrome/browser/sync/engine/all_status.cc
+++ b/chrome/browser/sync/engine/all_status.cc
@@ -107,6 +107,10 @@ void AllStatus::OnSyncEngineEvent(const SyncEngineEvent& event) {
status_ = CalcSyncing(event);
break;
case SyncEngineEvent::STOP_SYNCING_PERMANENTLY:
+ case SyncEngineEvent::UPDATED_TOKEN:
+ case SyncEngineEvent::CLEAR_SERVER_DATA_FAILED:
+ case SyncEngineEvent::CLEAR_SERVER_DATA_SUCCEEDED:
+ case SyncEngineEvent::MIGRATION_NEEDED_FOR_TYPES:
break;
default:
LOG(ERROR) << "Unrecognized Syncer Event: " << event.what_happened;
diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc
index 41da3d7..81cc609 100644
--- a/chrome/browser/sync/engine/syncapi.cc
+++ b/chrome/browser/sync/engine/syncapi.cc
@@ -2572,6 +2572,12 @@ void SyncManager::SyncInternal::OnSyncEngineEvent(
OnUpdatedToken(event.updated_token));
return;
}
+
+ if (event.what_happened == SyncEngineEvent::MIGRATION_NEEDED_FOR_TYPES) {
+ FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
+ OnMigrationNeededForTypes(event.types_to_migrate));
+ return;
+ }
}
void SyncManager::SyncInternal::SetParentJsEventRouter(
diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h
index 1011a29..ff3a1b4 100644
--- a/chrome/browser/sync/engine/syncapi.h
+++ b/chrome/browser/sync/engine/syncapi.h
@@ -778,6 +778,10 @@ class SyncManager {
// Called when a new auth token is provided by the sync server.
virtual void OnUpdatedToken(const std::string& token) = 0;
+ // Called when migration is required for |types|.
+ virtual void OnMigrationNeededForTypes(
+ const syncable::ModelTypeSet& types) = 0;
+
// Called when user interaction is required to obtain a valid passphrase.
// If the passphrase is required to decrypt something that has
// already been encrypted (and thus has to match the existing key),
diff --git a/chrome/browser/sync/engine/syncapi_unittest.cc b/chrome/browser/sync/engine/syncapi_unittest.cc
index 6c68735..54c09bd 100644
--- a/chrome/browser/sync/engine/syncapi_unittest.cc
+++ b/chrome/browser/sync/engine/syncapi_unittest.cc
@@ -619,6 +619,7 @@ class SyncManagerObserverMock : public SyncManager::Observer {
MOCK_METHOD1(OnPassphraseAccepted, void(const std::string&)); // NOLINT
MOCK_METHOD0(OnStopSyncingPermanently, void()); // NOLINT
MOCK_METHOD1(OnUpdatedToken, void(const std::string&)); // NOLINT
+ MOCK_METHOD1(OnMigrationNeededForTypes, void(const ModelTypeSet&));
MOCK_METHOD0(OnClearServerDataFailed, void()); // NOLINT
MOCK_METHOD0(OnClearServerDataSucceeded, void()); // NOLINT
MOCK_METHOD1(OnEncryptionComplete, void(const ModelTypeSet&)); // NOLINT
diff --git a/chrome/browser/sync/engine/syncer_proto_util.cc b/chrome/browser/sync/engine/syncer_proto_util.cc
index 5dbb202..63facd4 100644
--- a/chrome/browser/sync/engine/syncer_proto_util.cc
+++ b/chrome/browser/sync/engine/syncer_proto_util.cc
@@ -13,6 +13,7 @@
#include "chrome/browser/sync/protocol/service_constants.h"
#include "chrome/browser/sync/sessions/sync_session.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/syncable/syncable-inl.h"
#include "chrome/browser/sync/syncable/syncable.h"
@@ -78,6 +79,21 @@ void LogResponseProfilingData(const ClientToServerResponse& response) {
} // namespace
+// static
+void SyncerProtoUtil::HandleMigrationDoneResponse(
+ const sync_pb::ClientToServerResponse* response,
+ sessions::SyncSession* session) {
+ LOG_IF(ERROR, 0 >= response->migrated_data_type_id_size())
+ << "MIGRATION_DONE but no types specified.";
+ syncable::ModelTypeSet to_migrate;
+ for (int i = 0; i < response->migrated_data_type_id_size(); i++) {
+ to_migrate.insert(syncable::GetModelTypeFromExtensionFieldNumber(
+ response->migrated_data_type_id(i)));
+ }
+ SyncEngineEvent event(SyncEngineEvent::MIGRATION_NEEDED_FOR_TYPES);
+ event.types_to_migrate = to_migrate;
+ session->context()->NotifyListeners(event);
+}
// static
bool SyncerProtoUtil::VerifyResponseBirthday(syncable::Directory* dir,
@@ -225,6 +241,9 @@ bool SyncerProtoUtil::PostClientToServerMessage(
return false;
case ClientToServerResponse::TRANSIENT_ERROR:
return false;
+ case ClientToServerResponse::MIGRATION_DONE:
+ HandleMigrationDoneResponse(response, session);
+ return false;
case ClientToServerResponse::USER_NOT_ACTIVATED:
case ClientToServerResponse::AUTH_INVALID:
case ClientToServerResponse::ACCESS_DENIED:
diff --git a/chrome/browser/sync/engine/syncer_proto_util.h b/chrome/browser/sync/engine/syncer_proto_util.h
index ca68ec3..d55e277 100644
--- a/chrome/browser/sync/engine/syncer_proto_util.h
+++ b/chrome/browser/sync/engine/syncer_proto_util.h
@@ -102,6 +102,12 @@ class SyncerProtoUtil {
static bool VerifyResponseBirthday(syncable::Directory* dir,
const sync_pb::ClientToServerResponse* response);
+ // Builds and sends a SyncEngineEvent to begin migration for types (specified
+ // in notification).
+ static void HandleMigrationDoneResponse(
+ const sync_pb::ClientToServerResponse* response,
+ sessions::SyncSession* session);
+
// Post the message using the scm, and do some processing on the returned
// headers. Decode the server response.
static bool PostAndProcessHeaders(browser_sync::ServerConnectionManager* scm,
diff --git a/chrome/browser/sync/engine/syncer_thread.cc b/chrome/browser/sync/engine/syncer_thread.cc
index 5bf0d4d..7eaab0f 100644
--- a/chrome/browser/sync/engine/syncer_thread.cc
+++ b/chrome/browser/sync/engine/syncer_thread.cc
@@ -510,8 +510,11 @@ void SyncerThread::SetSyncerStepsForPurpose(
void SyncerThread::DoSyncSessionJob(const SyncSessionJob& job) {
DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- if (!ShouldRunJob(job))
+ if (!ShouldRunJob(job)) {
+ LOG(WARNING) << "Dropping nudge at DoSyncSessionJob, source = "
+ << job.session->source().updates_source;
return;
+ }
if (job.purpose == SyncSessionJob::NUDGE) {
DCHECK(pending_nudge_.get());
diff --git a/chrome/browser/sync/engine/syncer_types.h b/chrome/browser/sync/engine/syncer_types.h
index c4612c6..a2647e0 100644
--- a/chrome/browser/sync/engine/syncer_types.h
+++ b/chrome/browser/sync/engine/syncer_types.h
@@ -11,6 +11,7 @@
#include "base/observer_list.h"
#include "chrome/browser/sync/util/channel.h"
+#include "chrome/browser/sync/syncable/model_type.h"
namespace syncable {
class BaseTransaction;
@@ -88,6 +89,9 @@ struct SyncEngineEvent {
// New token in updated_token.
UPDATED_TOKEN,
+ // A list of types to migrate is in |types_to_migrate|.
+ MIGRATION_NEEDED_FOR_TYPES,
+
// This is sent after the Syncer (and SyncerThread) have initiated self
// halt due to no longer being permitted to communicate with the server.
// The listener should sever the sync / browser connections and delete sync
@@ -111,6 +115,8 @@ struct SyncEngineEvent {
// Update-Client-Auth returns a new token for sync use.
std::string updated_token;
+
+ syncable::ModelTypeSet types_to_migrate;
};
class SyncEngineEventListener {
diff --git a/chrome/browser/sync/glue/data_type_manager.h b/chrome/browser/sync/glue/data_type_manager.h
index 0a42660..4c97bee 100644
--- a/chrome/browser/sync/glue/data_type_manager.h
+++ b/chrome/browser/sync/glue/data_type_manager.h
@@ -42,16 +42,24 @@ class DataTypeManager {
// during startup.
};
+ typedef std::set<syncable::ModelType> TypeSet;
+
// In case of an error the location is filled with the location the
// error originated from. In case of a success the error location value
// is to be not used.
+ // TODO(tim): We should rename this / ConfigureResult to something more
+ // flexible like SyncConfigureDoneDetails.
struct ConfigureResultWithErrorLocation {
ConfigureResult result;
+ TypeSet requested_types;
scoped_ptr<tracked_objects::Location> location;
ConfigureResultWithErrorLocation();
ConfigureResultWithErrorLocation(const ConfigureResult& result,
- const tracked_objects::Location& location) : result(result) {
+ const tracked_objects::Location& location,
+ const TypeSet& requested_types)
+ : result(result),
+ requested_types(requested_types) {
this->location.reset(new tracked_objects::Location(
location.function_name(),
location.file_name(),
@@ -61,7 +69,6 @@ class DataTypeManager {
~ConfigureResultWithErrorLocation();
};
- typedef std::set<syncable::ModelType> TypeSet;
virtual ~DataTypeManager() {}
diff --git a/chrome/browser/sync/glue/data_type_manager_impl.cc b/chrome/browser/sync/glue/data_type_manager_impl.cc
index f8aefed..a241184 100644
--- a/chrome/browser/sync/glue/data_type_manager_impl.cc
+++ b/chrome/browser/sync/glue/data_type_manager_impl.cc
@@ -348,7 +348,8 @@ void DataTypeManagerImpl::NotifyStart() {
void DataTypeManagerImpl::NotifyDone(ConfigureResult result,
const tracked_objects::Location& location) {
- ConfigureResultWithErrorLocation result_with_location(result, location);
+ ConfigureResultWithErrorLocation result_with_location(result, location,
+ last_requested_types_);
NotificationService::current()->Notify(
NotificationType::SYNC_CONFIGURE_DONE,
Source<DataTypeManager>(this),
diff --git a/chrome/browser/sync/glue/data_type_manager_mock.cc b/chrome/browser/sync/glue/data_type_manager_mock.cc
index 605c6ea..0afef59 100644
--- a/chrome/browser/sync/glue/data_type_manager_mock.cc
+++ b/chrome/browser/sync/glue/data_type_manager_mock.cc
@@ -8,7 +8,7 @@
namespace browser_sync {
DataTypeManagerMock::DataTypeManagerMock()
- : result_(OK, FROM_HERE) {
+ : result_(OK, FROM_HERE, syncable::ModelTypeSet()) {
// By default, calling Configure will send a SYNC_CONFIGURE_START
// and SYNC_CONFIGURE_DONE notification with a DataTypeManager::OK
diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc
index 9d57a8f..e813829 100644
--- a/chrome/browser/sync/glue/sync_backend_host.cc
+++ b/chrome/browser/sync/glue/sync_backend_host.cc
@@ -359,15 +359,14 @@ SyncBackendHost::PendingConfigureDataTypesState*
CancelableTask* ready_task,
ModelSafeRoutingInfo* routing_info) {
PendingConfigureDataTypesState* state = new PendingConfigureDataTypesState();
-
for (DataTypeController::TypeMap::const_iterator it =
data_type_controllers.begin();
it != data_type_controllers.end(); ++it) {
syncable::ModelType type = it->first;
-
// If a type is not specified, remove it from the routing_info.
if (types.count(type) == 0) {
- state->deleted_type = (routing_info->erase(type) > 0);
+ state->deleted_type = true;
+ routing_info->erase(type);
} else {
// Add a newly specified data type as GROUP_PASSIVE into the
// routing_info, if it does not already exist.
@@ -892,11 +891,14 @@ void SyncBackendHost::Core::HandleSyncCycleCompletedOnFrontendLoop(
found_all_added &= snapshot->initial_sync_ended.test(*it);
}
if (!found_all_added) {
- CHECK(false);
+ LOG(WARNING) << "Update didn't return updates for all types requested.";
+ // This is typically an error case and sync won't make forward progress.
+ // The exception is backend migration-during-configuration. We'll reset
+ // state below to allow for a future call to ConfigureDataTypes.
} else {
host_->pending_download_state_->ready_task->Run();
- host_->pending_download_state_.reset();
}
+ host_->pending_download_state_.reset();
}
host_->frontend_->OnSyncCycleCompleted();
}
@@ -980,6 +982,12 @@ void SyncBackendHost::Core::OnUpdatedToken(const std::string& token) {
&Core::NotifyUpdatedToken, token));
}
+void SyncBackendHost::Core::OnMigrationNeededForTypes(
+ const syncable::ModelTypeSet& types) {
+ host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &Core::HandleMigrationNeededOnFrontendLoop, types));
+}
+
void SyncBackendHost::Core::OnClearServerDataSucceeded() {
host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
&Core::HandleClearServerDataSucceededOnFrontendLoop));
@@ -1035,6 +1043,16 @@ void SyncBackendHost::Core::HandleAuthErrorEventOnFrontendLoop(
host_->frontend_->OnAuthError();
}
+void SyncBackendHost::Core::HandleMigrationNeededOnFrontendLoop(
+ const syncable::ModelTypeSet& types) {
+ if (!host_ || !host_->frontend_)
+ return;
+
+ DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_);
+
+ host_->frontend_->OnMigrationNeededForTypes(types);
+}
+
void SyncBackendHost::Core::RouteJsEventOnFrontendLoop(
const std::string& name, const JsArgList& args,
const JsEventHandler* target) {
diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h
index 586ca8d..6954dea 100644
--- a/chrome/browser/sync/glue/sync_backend_host.h
+++ b/chrome/browser/sync/glue/sync_backend_host.h
@@ -95,6 +95,10 @@ class SyncFrontend {
virtual void OnEncryptionComplete(
const syncable::ModelTypeSet& encrypted_types) = 0;
+ // Called to perform migration of |types|.
+ virtual void OnMigrationNeededForTypes(
+ const syncable::ModelTypeSet& types) = 0;
+
protected:
// Don't delete through SyncFrontend interface.
virtual ~SyncFrontend() {
@@ -274,6 +278,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
virtual void OnPassphraseAccepted(const std::string& bootstrap_token);
virtual void OnStopSyncingPermanently();
virtual void OnUpdatedToken(const std::string& token);
+ virtual void OnMigrationNeededForTypes(
+ const syncable::ModelTypeSet& types);
virtual void OnClearServerDataFailed();
virtual void OnClearServerDataSucceeded();
virtual void OnEncryptionComplete(
@@ -470,6 +476,10 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
void HandleClearServerDataSucceededOnFrontendLoop();
void HandleClearServerDataFailedOnFrontendLoop();
+ // Called to handle a migration for |types|.
+ void HandleMigrationNeededOnFrontendLoop(
+ const syncable::ModelTypeSet& types);
+
// Called from Core::OnInitializationComplete to handle updating
// frontend thread components.
void HandleInitalizationCompletedOnFrontendLoop();
diff --git a/chrome/browser/sync/glue/sync_backend_host_unittest.cc b/chrome/browser/sync/glue/sync_backend_host_unittest.cc
index 77a0139..c1b58ae 100644
--- a/chrome/browser/sync/glue/sync_backend_host_unittest.cc
+++ b/chrome/browser/sync/glue/sync_backend_host_unittest.cc
@@ -39,6 +39,7 @@ class MockSyncFrontend : public SyncFrontend {
MOCK_METHOD1(OnPassphraseRequired, void(bool));
MOCK_METHOD0(OnPassphraseAccepted, void());
MOCK_METHOD1(OnEncryptionComplete, void(const syncable::ModelTypeSet&));
+ MOCK_METHOD1(OnMigrationNeededForTypes, void(const syncable::ModelTypeSet&));
};
} // namespace
@@ -118,7 +119,7 @@ TEST_F(SyncBackendHostTest, MakePendingConfigModeState) {
EXPECT_TRUE(routing_info.empty());
EXPECT_FALSE(state->ready_task.get());
EXPECT_EQ(types, state->initial_types);
- EXPECT_FALSE(state->deleted_type);
+ EXPECT_TRUE(state->deleted_type);
EXPECT_TRUE(state->added_types.none());
}
diff --git a/chrome/browser/sync/js_sync_manager_observer.cc b/chrome/browser/sync/js_sync_manager_observer.cc
index b6266e7..ef9f34d 100644
--- a/chrome/browser/sync/js_sync_manager_observer.cc
+++ b/chrome/browser/sync/js_sync_manager_observer.cc
@@ -98,6 +98,15 @@ void JsSyncManagerObserver::OnEncryptionComplete(
JsArgList(return_args), NULL);
}
+void JsSyncManagerObserver::OnMigrationNeededForTypes(
+ const syncable::ModelTypeSet& types) {
+ ListValue return_args;
+ return_args.Append(syncable::ModelTypeSetToValue(types));
+ parent_router_->RouteJsEvent("onMigrationNeededForTypes",
+ JsArgList(return_args), NULL);
+ // TODO(akalin): Bug 79247. Hook up JS boiler plate!
+}
+
void JsSyncManagerObserver::OnInitializationComplete() {
parent_router_->RouteJsEvent("onInitializationComplete",
JsArgList(), NULL);
diff --git a/chrome/browser/sync/js_sync_manager_observer.h b/chrome/browser/sync/js_sync_manager_observer.h
index c7d09e76..9436f52 100644
--- a/chrome/browser/sync/js_sync_manager_observer.h
+++ b/chrome/browser/sync/js_sync_manager_observer.h
@@ -43,6 +43,7 @@ class JsSyncManagerObserver : public sync_api::SyncManager::Observer {
virtual void OnStopSyncingPermanently();
virtual void OnClearServerDataSucceeded();
virtual void OnClearServerDataFailed();
+ virtual void OnMigrationNeededForTypes(const syncable::ModelTypeSet& types);
private:
JsEventRouter* parent_router_;
diff --git a/chrome/browser/sync/js_sync_manager_observer_unittest.cc b/chrome/browser/sync/js_sync_manager_observer_unittest.cc
index e854690..3631963 100644
--- a/chrome/browser/sync/js_sync_manager_observer_unittest.cc
+++ b/chrome/browser/sync/js_sync_manager_observer_unittest.cc
@@ -166,6 +166,28 @@ TEST_F(JsSyncManagerObserverTest, OnEncryptionComplete) {
sync_manager_observer_.OnEncryptionComplete(encrypted_types);
}
+
+TEST_F(JsSyncManagerObserverTest, OnMigrationNeededForTypes) {
+ ListValue expected_args;
+ ListValue* type_values = new ListValue();
+ syncable::ModelTypeSet types;
+
+ expected_args.Append(type_values);
+ for (int i = syncable::FIRST_REAL_MODEL_TYPE;
+ i < syncable::MODEL_TYPE_COUNT; ++i) {
+ syncable::ModelType type = syncable::ModelTypeFromInt(i);
+ types.insert(type);
+ type_values->Append(Value::CreateStringValue(
+ syncable::ModelTypeToString(type)));
+ }
+
+ EXPECT_CALL(mock_router_,
+ RouteJsEvent("onMigrationNeededForTypes",
+ HasArgsAsList(expected_args), NULL));
+
+ sync_manager_observer_.OnMigrationNeededForTypes(types);
+}
+
namespace {
// Makes a node of the given model type. Returns the id of the
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index 3abeb75..51e4b51 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -29,6 +29,7 @@
#include "chrome/browser/sync/glue/data_type_controller.h"
#include "chrome/browser/sync/glue/data_type_manager.h"
#include "chrome/browser/sync/glue/session_data_type_controller.h"
+#include "chrome/browser/sync/backend_migrator.h"
#include "chrome/browser/sync/js_arg_list.h"
#include "chrome/browser/sync/profile_sync_factory.h"
#include "chrome/browser/sync/signin_manager.h"
@@ -655,6 +656,16 @@ void ProfileSyncService::OnEncryptionComplete(
}
}
+void ProfileSyncService::OnMigrationNeededForTypes(
+ const syncable::ModelTypeSet& types) {
+ DCHECK(backend_initialized_);
+ DCHECK(data_type_manager_.get());
+
+ // Migrator must be valid, because we don't sync until it is created and this
+ // callback originates from a sync cycle.
+ migrator_->MigrateTypes(types);
+}
+
void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) {
if (!cros_user_.empty()) {
// For ChromeOS, any login UI needs to be handled by the settings page.
@@ -967,6 +978,10 @@ void ProfileSyncService::ConfigureDataTypeManager() {
registrar_.Add(this,
NotificationType::SYNC_CONFIGURE_DONE,
Source<DataTypeManager>(data_type_manager_.get()));
+
+ // We create the migrator at the same time.
+ migrator_.reset(
+ new browser_sync::BackendMigrator(this, data_type_manager_.get()));
}
syncable::ModelTypeSet types;
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index 8ffcd82..8e525e9 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -39,6 +39,7 @@ class ProfileSyncFactory;
class TabContents;
namespace browser_sync {
+class BackendMigrator;
class SessionModelAssociator;
class JsFrontend;
} // namespace browser_sync
@@ -186,6 +187,8 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
virtual void OnPassphraseAccepted();
virtual void OnEncryptionComplete(
const syncable::ModelTypeSet& encrypted_types);
+ virtual void OnMigrationNeededForTypes(
+ const syncable::ModelTypeSet& types);
// Called when a user enters credentials through UI.
virtual void OnUserSubmittedAuth(const std::string& username,
@@ -332,7 +335,7 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// ProfileSyncServiceHarness. Figure out a different way to expose
// this info to that class, and remove these functions.
- const browser_sync::sessions::SyncSessionSnapshot*
+ virtual const browser_sync::sessions::SyncSessionSnapshot*
GetLastSessionSnapshot() const;
// Returns whether or not the underlying sync engine has made any
@@ -624,6 +627,8 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// encrypted through the OnEncryptionComplete callback of SyncFrontend.
syncable::ModelTypeSet encrypted_types_;
+ scoped_ptr<browser_sync::BackendMigrator> migrator_;
+
DISALLOW_COPY_AND_ASSIGN(ProfileSyncService);
};
diff --git a/chrome/browser/sync/profile_sync_service_mock.h b/chrome/browser/sync/profile_sync_service_mock.h
index 0727ebe..5237897 100644
--- a/chrome/browser/sync/profile_sync_service_mock.h
+++ b/chrome/browser/sync/profile_sync_service_mock.h
@@ -53,6 +53,8 @@ class ProfileSyncServiceMock : public ProfileSyncService {
void(syncable::ModelTypeSet* preferred_types));
MOCK_CONST_METHOD1(GetRegisteredDataTypes,
void(syncable::ModelTypeSet* registered_types));
+ MOCK_CONST_METHOD0(GetLastSessionSnapshot,
+ const browser_sync::sessions::SyncSessionSnapshot*());
MOCK_METHOD0(QueryDetailedSyncStatus,
browser_sync::SyncBackendHost::Status());
diff --git a/chrome/browser/sync/profile_sync_service_startup_unittest.cc b/chrome/browser/sync/profile_sync_service_startup_unittest.cc
index f8c9997..e1b732a 100644
--- a/chrome/browser/sync/profile_sync_service_startup_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_startup_unittest.cc
@@ -247,7 +247,7 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFailure)) {
DataTypeManager::ConfigureResult configure_result =
DataTypeManager::ASSOCIATION_FAILED;
browser_sync::DataTypeManager::ConfigureResultWithErrorLocation result(
- configure_result, FROM_HERE);
+ configure_result, FROM_HERE, syncable::ModelTypeSet());
EXPECT_CALL(*data_type_manager, Configure(_)).
WillRepeatedly(DoAll(NotifyFromDataTypeManager(data_type_manager,
NotificationType::SYNC_CONFIGURE_START),
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 1487d62..b37bb37 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1973,6 +1973,8 @@
'browser/sync/glue/typed_url_model_associator.h',
'browser/sync/glue/ui_model_worker.cc',
'browser/sync/glue/ui_model_worker.h',
+ 'browser/sync/backend_migrator.cc',
+ 'browser/sync/backend_migrator.h',
'browser/sync/profile_sync_factory.h',
'browser/sync/profile_sync_factory_impl.cc',
'browser/sync/profile_sync_factory_impl.h',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 04b5390..9c058d3 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1567,6 +1567,7 @@
'browser/sync/glue/theme_util_unittest.cc',
'browser/sync/glue/typed_url_model_associator_unittest.cc',
'browser/sync/glue/ui_model_worker_unittest.cc',
+ 'browser/sync/backend_migrator_unittest.cc',
'browser/sync/profile_sync_factory_impl_unittest.cc',
'browser/sync/profile_sync_factory_mock.cc',
'browser/sync/profile_sync_factory_mock.h',