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 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 CHECK(IsOnThreadForGroup(type, group)); 208 base::AutoLock lock(lock_); 209 // Ensure that the given data type is in the PASSIVE group. 210 syncer::ModelSafeRoutingInfo::iterator i = routing_info_.find(type); 211 DCHECK(i != routing_info_.end()); 212 DCHECK_EQ(i->second, syncer::GROUP_PASSIVE); 213 routing_info_[type] = group; 214 CHECK(IsCurrentThreadSafeForModel(type)); 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(profile_, 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 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 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<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 ChangeProcessor* SyncBackendRegistrar::GetProcessor( 283 syncer::ModelType type) const { 284 base::AutoLock lock(lock_); 285 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 ChangeProcessor* SyncBackendRegistrar::GetProcessorUnsafe( 296 syncer::ModelType type) const { 297 lock_.AssertAcquired(); 298 std::map<syncer::ModelType, 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