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.h" 14 #include "chrome/browser/password_manager/password_store_factory.h" 15 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/browser/sync/glue/browser_thread_model_worker.h" 17 #include "chrome/browser/sync/glue/change_processor.h" 18 #include "chrome/browser/sync/glue/history_model_worker.h" 19 #include "chrome/browser/sync/glue/password_model_worker.h" 20 #include "chrome/browser/sync/glue/ui_model_worker.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 CHECK(sync_thread_->Start()); 70 } 71 72 workers_[syncer::GROUP_DB] = new DatabaseModelWorker(this); 73 workers_[syncer::GROUP_DB]->RegisterForLoopDestruction(); 74 75 workers_[syncer::GROUP_FILE] = new FileModelWorker(this); 76 workers_[syncer::GROUP_FILE]->RegisterForLoopDestruction(); 77 78 workers_[syncer::GROUP_UI] = new UIModelWorker(this); 79 workers_[syncer::GROUP_UI]->RegisterForLoopDestruction(); 80 81 // GROUP_PASSIVE worker does work on sync_loop_. But sync_loop_ is not 82 // stopped until all workers have stopped. To break the cycle, use UI loop 83 // instead. 84 workers_[syncer::GROUP_PASSIVE] = 85 new syncer::PassiveModelWorker(sync_thread_->message_loop(), this); 86 workers_[syncer::GROUP_PASSIVE]->RegisterForLoopDestruction(); 87 88 HistoryService* history_service = 89 HistoryServiceFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS); 90 if (history_service) { 91 workers_[syncer::GROUP_HISTORY] = 92 new HistoryModelWorker(history_service->AsWeakPtr(), this); 93 workers_[syncer::GROUP_HISTORY]->RegisterForLoopDestruction(); 94 95 } 96 97 scoped_refptr<PasswordStore> password_store = 98 PasswordStoreFactory::GetForProfile(profile, Profile::IMPLICIT_ACCESS); 99 if (password_store.get()) { 100 workers_[syncer::GROUP_PASSWORD] = 101 new PasswordModelWorker(password_store, this); 102 workers_[syncer::GROUP_PASSWORD]->RegisterForLoopDestruction(); 103 } 104 } 105 106 void SyncBackendRegistrar::SetInitialTypes(syncer::ModelTypeSet initial_types) { 107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 108 base::AutoLock lock(lock_); 109 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 ChangeProcessor* change_processor, 206 syncer::UserShare* user_share) { 207 DVLOG(1) << "Activate: " << syncer::ModelTypeToString(type); 208 209 CHECK(IsOnThreadForGroup(type, group)); 210 base::AutoLock lock(lock_); 211 // Ensure that the given data type is in the PASSIVE group. 212 syncer::ModelSafeRoutingInfo::iterator i = routing_info_.find(type); 213 DCHECK(i != routing_info_.end()); 214 DCHECK_EQ(i->second, syncer::GROUP_PASSIVE); 215 routing_info_[type] = group; 216 CHECK(IsCurrentThreadSafeForModel(type)); 217 218 // Add the data type's change processor to the list of change 219 // processors so it can receive updates. 220 DCHECK_EQ(processors_.count(type), 0U); 221 processors_[type] = change_processor; 222 223 // Start the change processor. 224 change_processor->Start(profile_, user_share); 225 DCHECK(GetProcessorUnsafe(type)); 226 } 227 228 void SyncBackendRegistrar::DeactivateDataType(syncer::ModelType type) { 229 DVLOG(1) << "Deactivate: " << syncer::ModelTypeToString(type); 230 231 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || IsControlType(type)); 232 base::AutoLock lock(lock_); 233 234 routing_info_.erase(type); 235 ignore_result(processors_.erase(type)); 236 DCHECK(!GetProcessorUnsafe(type)); 237 } 238 239 bool SyncBackendRegistrar::IsTypeActivatedForTest( 240 syncer::ModelType type) const { 241 return GetProcessor(type) != NULL; 242 } 243 244 void SyncBackendRegistrar::OnChangesApplied( 245 syncer::ModelType model_type, 246 int64 model_version, 247 const syncer::BaseTransaction* trans, 248 const syncer::ImmutableChangeRecordList& changes) { 249 ChangeProcessor* processor = GetProcessor(model_type); 250 if (!processor) 251 return; 252 253 processor->ApplyChangesFromSyncModel(trans, model_version, changes); 254 } 255 256 void SyncBackendRegistrar::OnChangesComplete(syncer::ModelType model_type) { 257 ChangeProcessor* processor = GetProcessor(model_type); 258 if (!processor) 259 return; 260 261 // This call just notifies the processor that it can commit; it 262 // already buffered any changes it plans to makes so needs no 263 // further information. 264 processor->CommitChangesFromSyncModel(); 265 } 266 267 void SyncBackendRegistrar::GetWorkers( 268 std::vector<syncer::ModelSafeWorker*>* out) { 269 base::AutoLock lock(lock_); 270 out->clear(); 271 for (WorkerMap::const_iterator it = workers_.begin(); 272 it != workers_.end(); ++it) { 273 out->push_back(it->second.get()); 274 } 275 } 276 277 void SyncBackendRegistrar::GetModelSafeRoutingInfo( 278 syncer::ModelSafeRoutingInfo* out) { 279 base::AutoLock lock(lock_); 280 syncer::ModelSafeRoutingInfo copy(routing_info_); 281 out->swap(copy); 282 } 283 284 ChangeProcessor* SyncBackendRegistrar::GetProcessor( 285 syncer::ModelType type) const { 286 base::AutoLock lock(lock_); 287 ChangeProcessor* processor = GetProcessorUnsafe(type); 288 if (!processor) 289 return NULL; 290 291 // We can only check if |processor| exists, as otherwise the type is 292 // mapped to syncer::GROUP_PASSIVE. 293 CHECK(IsCurrentThreadSafeForModel(type)); 294 return processor; 295 } 296 297 ChangeProcessor* SyncBackendRegistrar::GetProcessorUnsafe( 298 syncer::ModelType type) const { 299 lock_.AssertAcquired(); 300 std::map<syncer::ModelType, ChangeProcessor*>::const_iterator 301 it = processors_.find(type); 302 303 // Until model association happens for a datatype, it will not 304 // appear in the processors list. During this time, it is OK to 305 // drop changes on the floor (since model association has not 306 // happened yet). When the data type is activated, model 307 // association takes place then the change processor is added to the 308 // |processors_| list. 309 if (it == processors_.end()) 310 return NULL; 311 312 return it->second; 313 } 314 315 bool SyncBackendRegistrar::IsCurrentThreadSafeForModel( 316 syncer::ModelType model_type) const { 317 lock_.AssertAcquired(); 318 return IsOnThreadForGroup(model_type, 319 GetGroupForModelType(model_type, routing_info_)); 320 } 321 322 SyncBackendRegistrar::~SyncBackendRegistrar() { 323 DCHECK(workers_.empty()); 324 } 325 326 void SyncBackendRegistrar::OnWorkerLoopDestroyed(syncer::ModelSafeGroup group) { 327 RemoveWorker(group); 328 } 329 330 void SyncBackendRegistrar::OnWorkerUnregistrationDone( 331 syncer::ModelSafeGroup group) { 332 RemoveWorker(group); 333 } 334 335 void SyncBackendRegistrar::RemoveWorker(syncer::ModelSafeGroup group) { 336 DVLOG(1) << "Remove " << ModelSafeGroupToString(group) << " worker."; 337 338 bool last_worker = false; 339 { 340 base::AutoLock al(lock_); 341 WorkerMap::iterator it = workers_.find(group); 342 CHECK(it != workers_.end()); 343 stopped_workers_.push_back(it->second); 344 workers_.erase(it); 345 last_worker = workers_.empty(); 346 } 347 348 if (last_worker) { 349 // Self-destruction after last worker. 350 DVLOG(1) << "Destroy registrar on loop of " 351 << ModelSafeGroupToString(group); 352 delete this; 353 } 354 } 355 356 scoped_ptr<base::Thread> SyncBackendRegistrar::ReleaseSyncThread() { 357 return sync_thread_.Pass(); 358 } 359 360 void SyncBackendRegistrar::Shutdown() { 361 // All data types should have been deactivated by now. 362 DCHECK(processors_.empty()); 363 364 // Unregister worker from observing loop destruction. 365 base::AutoLock al(lock_); 366 for (WorkerMap::iterator it = workers_.begin(); 367 it != workers_.end(); ++it) { 368 it->second->UnregisterForLoopDestruction( 369 base::Bind(&SyncBackendRegistrar::OnWorkerUnregistrationDone, 370 base::Unretained(this))); 371 } 372 } 373 374 base::Thread* SyncBackendRegistrar::sync_thread() { 375 return sync_thread_.get(); 376 } 377 378 } // namespace browser_sync 379