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 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<password_manager::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 base::AutoLock lock(lock_); 108 109 // This function should be called only once, shortly after construction. The 110 // routing info at that point is expected to be empty. 111 DCHECK(routing_info_.empty()); 112 113 // Set our initial state to reflect the current status of the sync directory. 114 // This will ensure that our calculations in ConfigureDataTypes() will always 115 // return correct results. 116 for (syncer::ModelTypeSet::Iterator it = initial_types.First(); 117 it.Good(); it.Inc()) { 118 routing_info_[it.Get()] = syncer::GROUP_PASSIVE; 119 } 120 121 if (!workers_.count(syncer::GROUP_HISTORY)) { 122 LOG_IF(WARNING, initial_types.Has(syncer::TYPED_URLS)) 123 << "History store disabled, cannot sync Omnibox History"; 124 routing_info_.erase(syncer::TYPED_URLS); 125 } 126 127 if (!workers_.count(syncer::GROUP_PASSWORD)) { 128 LOG_IF(WARNING, initial_types.Has(syncer::PASSWORDS)) 129 << "Password store not initialized, cannot sync passwords"; 130 routing_info_.erase(syncer::PASSWORDS); 131 } 132 133 last_configured_types_ = syncer::GetRoutingInfoTypes(routing_info_); 134 } 135 136 bool SyncBackendRegistrar::IsNigoriEnabled() const { 137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 138 base::AutoLock lock(lock_); 139 return routing_info_.find(syncer::NIGORI) != routing_info_.end(); 140 } 141 142 syncer::ModelTypeSet SyncBackendRegistrar::ConfigureDataTypes( 143 syncer::ModelTypeSet types_to_add, 144 syncer::ModelTypeSet types_to_remove) { 145 DCHECK(Intersection(types_to_add, types_to_remove).Empty()); 146 syncer::ModelTypeSet filtered_types_to_add = types_to_add; 147 if (workers_.count(syncer::GROUP_HISTORY) == 0) { 148 LOG(WARNING) << "No history worker -- removing TYPED_URLS"; 149 filtered_types_to_add.Remove(syncer::TYPED_URLS); 150 } 151 if (workers_.count(syncer::GROUP_PASSWORD) == 0) { 152 LOG(WARNING) << "No password worker -- removing PASSWORDS"; 153 filtered_types_to_add.Remove(syncer::PASSWORDS); 154 } 155 156 base::AutoLock lock(lock_); 157 syncer::ModelTypeSet newly_added_types; 158 for (syncer::ModelTypeSet::Iterator it = 159 filtered_types_to_add.First(); 160 it.Good(); it.Inc()) { 161 // Add a newly specified data type as syncer::GROUP_PASSIVE into the 162 // routing_info, if it does not already exist. 163 if (routing_info_.count(it.Get()) == 0) { 164 routing_info_[it.Get()] = syncer::GROUP_PASSIVE; 165 newly_added_types.Put(it.Get()); 166 } 167 } 168 for (syncer::ModelTypeSet::Iterator it = types_to_remove.First(); 169 it.Good(); it.Inc()) { 170 routing_info_.erase(it.Get()); 171 } 172 173 // TODO(akalin): Use SVLOG/SLOG if we add any more logging. 174 DVLOG(1) << name_ << ": Adding types " 175 << syncer::ModelTypeSetToString(types_to_add) 176 << " (with newly-added types " 177 << syncer::ModelTypeSetToString(newly_added_types) 178 << ") and removing types " 179 << syncer::ModelTypeSetToString(types_to_remove) 180 << " to get new routing info " 181 <<syncer::ModelSafeRoutingInfoToString(routing_info_); 182 last_configured_types_ = syncer::GetRoutingInfoTypes(routing_info_); 183 184 return newly_added_types; 185 } 186 187 syncer::ModelTypeSet SyncBackendRegistrar::GetLastConfiguredTypes() const { 188 return last_configured_types_; 189 } 190 191 void SyncBackendRegistrar::RequestWorkerStopOnUIThread() { 192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 193 base::AutoLock lock(lock_); 194 for (WorkerMap::const_iterator it = workers_.begin(); 195 it != workers_.end(); ++it) { 196 it->second->RequestStop(); 197 } 198 } 199 200 void SyncBackendRegistrar::ActivateDataType( 201 syncer::ModelType type, 202 syncer::ModelSafeGroup group, 203 ChangeProcessor* change_processor, 204 syncer::UserShare* user_share) { 205 DVLOG(1) << "Activate: " << syncer::ModelTypeToString(type); 206 207 base::AutoLock lock(lock_); 208 // Ensure that the given data type is in the PASSIVE group. 209 syncer::ModelSafeRoutingInfo::iterator i = routing_info_.find(type); 210 DCHECK(i != routing_info_.end()); 211 DCHECK_EQ(i->second, syncer::GROUP_PASSIVE); 212 routing_info_[type] = group; 213 214 // Add the data type's change processor to the list of change 215 // processors so it can receive updates. 216 DCHECK_EQ(processors_.count(type), 0U); 217 processors_[type] = change_processor; 218 219 // Start the change processor. 220 change_processor->Start(user_share); 221 DCHECK(GetProcessorUnsafe(type)); 222 } 223 224 void SyncBackendRegistrar::DeactivateDataType(syncer::ModelType type) { 225 DVLOG(1) << "Deactivate: " << syncer::ModelTypeToString(type); 226 227 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || IsControlType(type)); 228 base::AutoLock lock(lock_); 229 230 routing_info_.erase(type); 231 ignore_result(processors_.erase(type)); 232 DCHECK(!GetProcessorUnsafe(type)); 233 } 234 235 bool SyncBackendRegistrar::IsTypeActivatedForTest( 236 syncer::ModelType type) const { 237 return GetProcessor(type) != NULL; 238 } 239 240 void SyncBackendRegistrar::OnChangesApplied( 241 syncer::ModelType model_type, 242 int64 model_version, 243 const syncer::BaseTransaction* trans, 244 const syncer::ImmutableChangeRecordList& changes) { 245 ChangeProcessor* processor = GetProcessor(model_type); 246 if (!processor) 247 return; 248 249 processor->ApplyChangesFromSyncModel(trans, model_version, changes); 250 } 251 252 void SyncBackendRegistrar::OnChangesComplete(syncer::ModelType model_type) { 253 ChangeProcessor* processor = GetProcessor(model_type); 254 if (!processor) 255 return; 256 257 // This call just notifies the processor that it can commit; it 258 // already buffered any changes it plans to makes so needs no 259 // further information. 260 processor->CommitChangesFromSyncModel(); 261 } 262 263 void SyncBackendRegistrar::GetWorkers( 264 std::vector<scoped_refptr<syncer::ModelSafeWorker> >* out) { 265 base::AutoLock lock(lock_); 266 out->clear(); 267 for (WorkerMap::const_iterator it = workers_.begin(); 268 it != workers_.end(); ++it) { 269 out->push_back(it->second.get()); 270 } 271 } 272 273 void SyncBackendRegistrar::GetModelSafeRoutingInfo( 274 syncer::ModelSafeRoutingInfo* out) { 275 base::AutoLock lock(lock_); 276 syncer::ModelSafeRoutingInfo copy(routing_info_); 277 out->swap(copy); 278 } 279 280 ChangeProcessor* SyncBackendRegistrar::GetProcessor( 281 syncer::ModelType type) const { 282 base::AutoLock lock(lock_); 283 ChangeProcessor* processor = GetProcessorUnsafe(type); 284 if (!processor) 285 return NULL; 286 287 // We can only check if |processor| exists, as otherwise the type is 288 // mapped to syncer::GROUP_PASSIVE. 289 CHECK(IsCurrentThreadSafeForModel(type)); 290 return processor; 291 } 292 293 ChangeProcessor* SyncBackendRegistrar::GetProcessorUnsafe( 294 syncer::ModelType type) const { 295 lock_.AssertAcquired(); 296 std::map<syncer::ModelType, ChangeProcessor*>::const_iterator 297 it = processors_.find(type); 298 299 // Until model association happens for a datatype, it will not 300 // appear in the processors list. During this time, it is OK to 301 // drop changes on the floor (since model association has not 302 // happened yet). When the data type is activated, model 303 // association takes place then the change processor is added to the 304 // |processors_| list. 305 if (it == processors_.end()) 306 return NULL; 307 308 return it->second; 309 } 310 311 bool SyncBackendRegistrar::IsCurrentThreadSafeForModel( 312 syncer::ModelType model_type) const { 313 lock_.AssertAcquired(); 314 return IsOnThreadForGroup(model_type, 315 GetGroupForModelType(model_type, routing_info_)); 316 } 317 318 SyncBackendRegistrar::~SyncBackendRegistrar() { 319 DCHECK(workers_.empty()); 320 } 321 322 void SyncBackendRegistrar::OnWorkerLoopDestroyed(syncer::ModelSafeGroup group) { 323 RemoveWorker(group); 324 } 325 326 void SyncBackendRegistrar::OnWorkerUnregistrationDone( 327 syncer::ModelSafeGroup group) { 328 RemoveWorker(group); 329 } 330 331 void SyncBackendRegistrar::RemoveWorker(syncer::ModelSafeGroup group) { 332 DVLOG(1) << "Remove " << ModelSafeGroupToString(group) << " worker."; 333 334 bool last_worker = false; 335 { 336 base::AutoLock al(lock_); 337 WorkerMap::iterator it = workers_.find(group); 338 CHECK(it != workers_.end()); 339 stopped_workers_.push_back(it->second); 340 workers_.erase(it); 341 last_worker = workers_.empty(); 342 } 343 344 if (last_worker) { 345 // Self-destruction after last worker. 346 DVLOG(1) << "Destroy registrar on loop of " 347 << ModelSafeGroupToString(group); 348 delete this; 349 } 350 } 351 352 scoped_ptr<base::Thread> SyncBackendRegistrar::ReleaseSyncThread() { 353 return sync_thread_.Pass(); 354 } 355 356 void SyncBackendRegistrar::Shutdown() { 357 // All data types should have been deactivated by now. 358 DCHECK(processors_.empty()); 359 360 // Unregister worker from observing loop destruction. 361 base::AutoLock al(lock_); 362 for (WorkerMap::iterator it = workers_.begin(); 363 it != workers_.end(); ++it) { 364 it->second->UnregisterForLoopDestruction( 365 base::Bind(&SyncBackendRegistrar::OnWorkerUnregistrationDone, 366 base::Unretained(this))); 367 } 368 } 369 370 base::Thread* SyncBackendRegistrar::sync_thread() { 371 return sync_thread_.get(); 372 } 373 374 } // namespace browser_sync 375