1 // Copyright (c) 2012 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/glue/sync_backend_registrar.h" 6 7 #include <cstddef> 8 9 #include "base/compiler_specific.h" 10 #include "base/logging.h" 11 #include "base/message_loop/message_loop.h" 12 #include "chrome/browser/history/history_service_factory.h" 13 #include "chrome/browser/password_manager/password_store_factory.h" 14 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/browser/sync/glue/browser_thread_model_worker.h" 16 #include "chrome/browser/sync/glue/history_model_worker.h" 17 #include "chrome/browser/sync/glue/password_model_worker.h" 18 #include "chrome/browser/sync/glue/ui_model_worker.h" 19 #include "components/password_manager/core/browser/password_store.h" 20 #include "components/sync_driver/change_processor.h" 21 #include "content/public/browser/browser_thread.h" 22 #include "sync/internal_api/public/engine/passive_model_worker.h" 23 #include "sync/internal_api/public/user_share.h" 24 25 using content::BrowserThread; 26 27 namespace browser_sync { 28 29 namespace { 30 31 // Returns true if the current thread is the native thread for the 32 // given group (or if it is undeterminable). 33 bool IsOnThreadForGroup(syncer::ModelType type, syncer::ModelSafeGroup group) { 34 switch (group) { 35 case syncer::GROUP_PASSIVE: 36 return IsControlType(type); 37 case syncer::GROUP_UI: 38 return BrowserThread::CurrentlyOn(BrowserThread::UI); 39 case syncer::GROUP_DB: 40 return BrowserThread::CurrentlyOn(BrowserThread::DB); 41 case syncer::GROUP_FILE: 42 return BrowserThread::CurrentlyOn(BrowserThread::FILE); 43 case syncer::GROUP_HISTORY: 44 // TODO(sync): How to check we're on the right thread? 45 return type == syncer::TYPED_URLS; 46 case syncer::GROUP_PASSWORD: 47 // TODO(sync): How to check we're on the right thread? 48 return type == syncer::PASSWORDS; 49 case syncer::MODEL_SAFE_GROUP_COUNT: 50 default: 51 return false; 52 } 53 } 54 55 } // namespace 56 57 SyncBackendRegistrar::SyncBackendRegistrar( 58 const std::string& name, 59 Profile* profile, 60 scoped_ptr<base::Thread> sync_thread) : 61 name_(name), 62 profile_(profile) { 63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 64 CHECK(profile_); 65 66 sync_thread_ = sync_thread.Pass(); 67 if (!sync_thread_) { 68 sync_thread_.reset(new base::Thread("Chrome_SyncThread")); 69 base::Thread::Options options; 70 options.timer_slack = base::TIMER_SLACK_MAXIMUM; 71 CHECK(sync_thread_->StartWithOptions(options)); 72 } 73 74 workers_[syncer::GROUP_DB] = new DatabaseModelWorker(this); 75 workers_[syncer::GROUP_DB]->RegisterForLoopDestruction(); 76 77 workers_[syncer::GROUP_FILE] = new FileModelWorker(this); 78 workers_[syncer::GROUP_FILE]->RegisterForLoopDestruction(); 79 80 workers_[syncer::GROUP_UI] = new UIModelWorker(this); 81 workers_[syncer::GROUP_UI]->RegisterForLoopDestruction(); 82 83 // GROUP_PASSIVE worker does work on sync_loop_. But sync_loop_ is not 84 // stopped until all workers have stopped. To break the cycle, use UI loop 85 // instead. 86 workers_[syncer::GROUP_PASSIVE] = 87 new syncer::PassiveModelWorker(sync_thread_->message_loop(), this); 88 workers_[syncer::GROUP_PASSIVE]->RegisterForLoopDestruction(); 89 90 HistoryService* history_service = 91 HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS); 92 if (history_service) { 93 workers_[syncer::GROUP_HISTORY] = 94 new HistoryModelWorker(history_service->AsWeakPtr(), this); 95 workers_[syncer::GROUP_HISTORY]->RegisterForLoopDestruction(); 96 97 } 98 99 scoped_refptr<password_manager::PasswordStore> password_store = 100 PasswordStoreFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS); 101 if (password_store.get()) { 102 workers_[syncer::GROUP_PASSWORD] = 103 new PasswordModelWorker(password_store, this); 104 workers_[syncer::GROUP_PASSWORD]->RegisterForLoopDestruction(); 105 } 106 } 107 108 void SyncBackendRegistrar::SetInitialTypes(syncer::ModelTypeSet initial_types) { 109 base::AutoLock lock(lock_); 110 111 // This function should be called only once, shortly after construction. The 112 // routing info at that point is expected to be empty. 113 DCHECK(routing_info_.empty()); 114 115 // Set our initial state to reflect the current status of the sync directory. 116 // This will ensure that our calculations in ConfigureDataTypes() will always 117 // return correct results. 118 for (syncer::ModelTypeSet::Iterator it = initial_types.First(); 119 it.Good(); it.Inc()) { 120 routing_info_[it.Get()] = syncer::GROUP_PASSIVE; 121 } 122 123 if (!workers_.count(syncer::GROUP_HISTORY)) { 124 LOG_IF(WARNING, initial_types.Has(syncer::TYPED_URLS)) 125 << "History store disabled, cannot sync Omnibox History"; 126 routing_info_.erase(syncer::TYPED_URLS); 127 } 128 129 if (!workers_.count(syncer::GROUP_PASSWORD)) { 130 LOG_IF(WARNING, initial_types.Has(syncer::PASSWORDS)) 131 << "Password store not initialized, cannot sync passwords"; 132 routing_info_.erase(syncer::PASSWORDS); 133 } 134 135 last_configured_types_ = syncer::GetRoutingInfoTypes(routing_info_); 136 } 137 138 bool SyncBackendRegistrar::IsNigoriEnabled() const { 139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 140 base::AutoLock lock(lock_); 141 return routing_info_.find(syncer::NIGORI) != routing_info_.end(); 142 } 143 144 syncer::ModelTypeSet SyncBackendRegistrar::ConfigureDataTypes( 145 syncer::ModelTypeSet types_to_add, 146 syncer::ModelTypeSet types_to_remove) { 147 DCHECK(Intersection(types_to_add, types_to_remove).Empty()); 148 syncer::ModelTypeSet filtered_types_to_add = types_to_add; 149 if (workers_.count(syncer::GROUP_HISTORY) == 0) { 150 LOG(WARNING) << "No history worker -- removing TYPED_URLS"; 151 filtered_types_to_add.Remove(syncer::TYPED_URLS); 152 } 153 if (workers_.count(syncer::GROUP_PASSWORD) == 0) { 154 LOG(WARNING) << "No password worker -- removing PASSWORDS"; 155 filtered_types_to_add.Remove(syncer::PASSWORDS); 156 } 157 158 base::AutoLock lock(lock_); 159 syncer::ModelTypeSet newly_added_types; 160 for (syncer::ModelTypeSet::Iterator it = 161 filtered_types_to_add.First(); 162 it.Good(); it.Inc()) { 163 // Add a newly specified data type as syncer::GROUP_PASSIVE into the 164 // routing_info, if it does not already exist. 165 if (routing_info_.count(it.Get()) == 0) { 166 routing_info_[it.Get()] = syncer::GROUP_PASSIVE; 167 newly_added_types.Put(it.Get()); 168 } 169 } 170 for (syncer::ModelTypeSet::Iterator it = types_to_remove.First(); 171 it.Good(); it.Inc()) { 172 routing_info_.erase(it.Get()); 173 } 174 175 // TODO(akalin): Use SVLOG/SLOG if we add any more logging. 176 DVLOG(1) << name_ << ": Adding types " 177 << syncer::ModelTypeSetToString(types_to_add) 178 << " (with newly-added types " 179 << syncer::ModelTypeSetToString(newly_added_types) 180 << ") and removing types " 181 << syncer::ModelTypeSetToString(types_to_remove) 182 << " to get new routing info " 183 <<syncer::ModelSafeRoutingInfoToString(routing_info_); 184 last_configured_types_ = syncer::GetRoutingInfoTypes(routing_info_); 185 186 return newly_added_types; 187 } 188 189 syncer::ModelTypeSet SyncBackendRegistrar::GetLastConfiguredTypes() const { 190 return last_configured_types_; 191 } 192 193 void SyncBackendRegistrar::RequestWorkerStopOnUIThread() { 194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 195 base::AutoLock lock(lock_); 196 for (WorkerMap::const_iterator it = workers_.begin(); 197 it != workers_.end(); ++it) { 198 it->second->RequestStop(); 199 } 200 } 201 202 void SyncBackendRegistrar::ActivateDataType( 203 syncer::ModelType type, 204 syncer::ModelSafeGroup group, 205 sync_driver::ChangeProcessor* change_processor, 206 syncer::UserShare* user_share) { 207 DVLOG(1) << "Activate: " << syncer::ModelTypeToString(type); 208 209 base::AutoLock lock(lock_); 210 // Ensure that the given data type is in the PASSIVE group. 211 syncer::ModelSafeRoutingInfo::iterator i = routing_info_.find(type); 212 DCHECK(i != routing_info_.end()); 213 DCHECK_EQ(i->second, syncer::GROUP_PASSIVE); 214 routing_info_[type] = group; 215 216 // Add the data type's change processor to the list of change 217 // processors so it can receive updates. 218 DCHECK_EQ(processors_.count(type), 0U); 219 processors_[type] = change_processor; 220 221 // Start the change processor. 222 change_processor->Start(user_share); 223 DCHECK(GetProcessorUnsafe(type)); 224 } 225 226 void SyncBackendRegistrar::DeactivateDataType(syncer::ModelType type) { 227 DVLOG(1) << "Deactivate: " << syncer::ModelTypeToString(type); 228 229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || IsControlType(type)); 230 base::AutoLock lock(lock_); 231 232 routing_info_.erase(type); 233 ignore_result(processors_.erase(type)); 234 DCHECK(!GetProcessorUnsafe(type)); 235 } 236 237 bool SyncBackendRegistrar::IsTypeActivatedForTest( 238 syncer::ModelType type) const { 239 return GetProcessor(type) != NULL; 240 } 241 242 void SyncBackendRegistrar::OnChangesApplied( 243 syncer::ModelType model_type, 244 int64 model_version, 245 const syncer::BaseTransaction* trans, 246 const syncer::ImmutableChangeRecordList& changes) { 247 sync_driver::ChangeProcessor* processor = GetProcessor(model_type); 248 if (!processor) 249 return; 250 251 processor->ApplyChangesFromSyncModel(trans, model_version, changes); 252 } 253 254 void SyncBackendRegistrar::OnChangesComplete(syncer::ModelType model_type) { 255 sync_driver::ChangeProcessor* processor = GetProcessor(model_type); 256 if (!processor) 257 return; 258 259 // This call just notifies the processor that it can commit; it 260 // already buffered any changes it plans to makes so needs no 261 // further information. 262 processor->CommitChangesFromSyncModel(); 263 } 264 265 void SyncBackendRegistrar::GetWorkers( 266 std::vector<scoped_refptr<syncer::ModelSafeWorker> >* out) { 267 base::AutoLock lock(lock_); 268 out->clear(); 269 for (WorkerMap::const_iterator it = workers_.begin(); 270 it != workers_.end(); ++it) { 271 out->push_back(it->second.get()); 272 } 273 } 274 275 void SyncBackendRegistrar::GetModelSafeRoutingInfo( 276 syncer::ModelSafeRoutingInfo* out) { 277 base::AutoLock lock(lock_); 278 syncer::ModelSafeRoutingInfo copy(routing_info_); 279 out->swap(copy); 280 } 281 282 sync_driver::ChangeProcessor* SyncBackendRegistrar::GetProcessor( 283 syncer::ModelType type) const { 284 base::AutoLock lock(lock_); 285 sync_driver::ChangeProcessor* processor = GetProcessorUnsafe(type); 286 if (!processor) 287 return NULL; 288 289 // We can only check if |processor| exists, as otherwise the type is 290 // mapped to syncer::GROUP_PASSIVE. 291 CHECK(IsCurrentThreadSafeForModel(type)); 292 return processor; 293 } 294 295 sync_driver::ChangeProcessor* SyncBackendRegistrar::GetProcessorUnsafe( 296 syncer::ModelType type) const { 297 lock_.AssertAcquired(); 298 std::map<syncer::ModelType, sync_driver::ChangeProcessor*>::const_iterator 299 it = processors_.find(type); 300 301 // Until model association happens for a datatype, it will not 302 // appear in the processors list. During this time, it is OK to 303 // drop changes on the floor (since model association has not 304 // happened yet). When the data type is activated, model 305 // association takes place then the change processor is added to the 306 // |processors_| list. 307 if (it == processors_.end()) 308 return NULL; 309 310 return it->second; 311 } 312 313 bool SyncBackendRegistrar::IsCurrentThreadSafeForModel( 314 syncer::ModelType model_type) const { 315 lock_.AssertAcquired(); 316 return IsOnThreadForGroup(model_type, 317 GetGroupForModelType(model_type, routing_info_)); 318 } 319 320 SyncBackendRegistrar::~SyncBackendRegistrar() { 321 DCHECK(workers_.empty()); 322 } 323 324 void SyncBackendRegistrar::OnWorkerLoopDestroyed(syncer::ModelSafeGroup group) { 325 RemoveWorker(group); 326 } 327 328 void SyncBackendRegistrar::OnWorkerUnregistrationDone( 329 syncer::ModelSafeGroup group) { 330 RemoveWorker(group); 331 } 332 333 void SyncBackendRegistrar::RemoveWorker(syncer::ModelSafeGroup group) { 334 DVLOG(1) << "Remove " << ModelSafeGroupToString(group) << " worker."; 335 336 bool last_worker = false; 337 { 338 base::AutoLock al(lock_); 339 WorkerMap::iterator it = workers_.find(group); 340 CHECK(it != workers_.end()); 341 stopped_workers_.push_back(it->second); 342 workers_.erase(it); 343 last_worker = workers_.empty(); 344 } 345 346 if (last_worker) { 347 // Self-destruction after last worker. 348 DVLOG(1) << "Destroy registrar on loop of " 349 << ModelSafeGroupToString(group); 350 delete this; 351 } 352 } 353 354 scoped_ptr<base::Thread> SyncBackendRegistrar::ReleaseSyncThread() { 355 return sync_thread_.Pass(); 356 } 357 358 void SyncBackendRegistrar::Shutdown() { 359 // All data types should have been deactivated by now. 360 DCHECK(processors_.empty()); 361 362 // Unregister worker from observing loop destruction. 363 base::AutoLock al(lock_); 364 for (WorkerMap::iterator it = workers_.begin(); 365 it != workers_.end(); ++it) { 366 it->second->UnregisterForLoopDestruction( 367 base::Bind(&SyncBackendRegistrar::OnWorkerUnregistrationDone, 368 base::Unretained(this))); 369 } 370 } 371 372 base::Thread* SyncBackendRegistrar::sync_thread() { 373 return sync_thread_.get(); 374 } 375 376 } // namespace browser_sync 377