• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BackendMigrator(ProfileSyncService * service,DataTypeManager * manager)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 
~BackendMigrator()33 BackendMigrator::~BackendMigrator() {
34   service_->RemoveObserver(this);
35 }
36 
HasStartedMigrating() const37 bool BackendMigrator::HasStartedMigrating() const {
38   return state_ >= DISABLING_TYPES;
39 }
40 
MigrateTypes(const syncable::ModelTypeSet & types)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 
OnStateChanged()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 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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 
state() const172 BackendMigrator::State BackendMigrator::state() const {
173   return state_;
174 }
175 
176 };  // namespace browser_sync
177