sync_backend_host.cc revision 201ade2fbba22bfb27ae029f4d23fca6ded109a0
1// Copyright (c) 2010 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 "build/build_config.h" 6 7#include <algorithm> 8 9#include "base/command_line.h" 10#include "base/file_util.h" 11#include "base/task.h" 12#include "base/utf_string_conversions.h" 13#include "chrome/browser/browser_thread.h" 14#include "chrome/browser/net/gaia/token_service.h" 15#include "chrome/browser/prefs/pref_service.h" 16#include "chrome/browser/profile.h" 17#include "chrome/browser/sync/engine/syncapi.h" 18#include "chrome/browser/sync/glue/change_processor.h" 19#include "chrome/browser/sync/glue/database_model_worker.h" 20#include "chrome/browser/sync/glue/history_model_worker.h" 21#include "chrome/browser/sync/glue/sync_backend_host.h" 22#include "chrome/browser/sync/glue/http_bridge.h" 23#include "chrome/browser/sync/glue/password_model_worker.h" 24#include "chrome/browser/sync/sessions/session_state.h" 25// TODO(tim): Remove this! We should have a syncapi pass-thru instead. 26#include "chrome/browser/sync/syncable/directory_manager.h" // Cryptographer. 27#include "chrome/common/chrome_switches.h" 28#include "chrome/common/chrome_version_info.h" 29#include "chrome/common/net/gaia/gaia_constants.h" 30#include "chrome/common/notification_service.h" 31#include "chrome/common/notification_type.h" 32#include "chrome/common/pref_names.h" 33#include "webkit/glue/webkit_glue.h" 34 35static const int kSaveChangesIntervalSeconds = 10; 36static const FilePath::CharType kSyncDataFolderName[] = 37 FILE_PATH_LITERAL("Sync Data"); 38 39using browser_sync::DataTypeController; 40typedef TokenService::TokenAvailableDetails TokenAvailableDetails; 41 42typedef GoogleServiceAuthError AuthError; 43 44namespace browser_sync { 45 46using sessions::SyncSessionSnapshot; 47using sync_api::SyncCredentials; 48 49SyncBackendHost::SyncBackendHost( 50 SyncFrontend* frontend, 51 Profile* profile, 52 const FilePath& profile_path, 53 const DataTypeController::TypeMap& data_type_controllers) 54 : core_thread_("Chrome_SyncCoreThread"), 55 frontend_loop_(MessageLoop::current()), 56 profile_(profile), 57 frontend_(frontend), 58 sync_data_folder_path_(profile_path.Append(kSyncDataFolderName)), 59 data_type_controllers_(data_type_controllers), 60 last_auth_error_(AuthError::None()), 61 syncapi_initialized_(false) { 62 63 core_ = new Core(this); 64} 65 66SyncBackendHost::SyncBackendHost() 67 : core_thread_("Chrome_SyncCoreThread"), 68 frontend_loop_(MessageLoop::current()), 69 profile_(NULL), 70 frontend_(NULL), 71 last_auth_error_(AuthError::None()), 72 syncapi_initialized_(false) { 73} 74 75SyncBackendHost::~SyncBackendHost() { 76 DCHECK(!core_ && !frontend_) << "Must call Shutdown before destructor."; 77 DCHECK(registrar_.workers.empty()); 78} 79 80void SyncBackendHost::Initialize( 81 const GURL& sync_service_url, 82 const syncable::ModelTypeSet& types, 83 URLRequestContextGetter* baseline_context_getter, 84 const SyncCredentials& credentials, 85 bool delete_sync_data_folder, 86 const notifier::NotifierOptions& notifier_options) { 87 if (!core_thread_.Start()) 88 return; 89 90 // Create a worker for the UI thread and route bookmark changes to it. 91 // TODO(tim): Pull this into a method to reuse. For now we don't even 92 // need to lock because we init before the syncapi exists and we tear down 93 // after the syncapi is destroyed. Make sure to NULL-check workers_ indices 94 // when a new type is synced as the worker may already exist and you just 95 // need to update routing_info_. 96 registrar_.workers[GROUP_DB] = new DatabaseModelWorker(); 97 registrar_.workers[GROUP_HISTORY] = 98 new HistoryModelWorker( 99 profile_->GetHistoryService(Profile::IMPLICIT_ACCESS)); 100 registrar_.workers[GROUP_UI] = new UIModelWorker(frontend_loop_); 101 registrar_.workers[GROUP_PASSIVE] = new ModelSafeWorker(); 102 103 PasswordStore* password_store = 104 profile_->GetPasswordStore(Profile::IMPLICIT_ACCESS); 105 if (password_store) { 106 registrar_.workers[GROUP_PASSWORD] = 107 new PasswordModelWorker(password_store); 108 } else { 109 LOG(WARNING) << "Password store not initialized, cannot sync passwords"; 110 } 111 112 // Any datatypes that we want the syncer to pull down must 113 // be in the routing_info map. We set them to group passive, meaning that 114 // updates will be applied, but not dispatched to the UI thread yet. 115 for (syncable::ModelTypeSet::const_iterator it = types.begin(); 116 it != types.end(); ++it) { 117 registrar_.routing_info[(*it)] = GROUP_PASSIVE; 118 } 119 120 // TODO(tim): Remove this special case once NIGORI is populated by 121 // default. We piggy back off of the passwords flag for now to not 122 // require both encryption and passwords flags. 123 bool enable_encryption = CommandLine::ForCurrentProcess()->HasSwitch( 124 switches::kEnableSyncPasswords) || types.count(syncable::PASSWORDS); 125 if (enable_encryption) 126 registrar_.routing_info[syncable::NIGORI] = GROUP_PASSIVE; 127 128 InitCore(Core::DoInitializeOptions( 129 sync_service_url, 130 MakeHttpBridgeFactory(baseline_context_getter), 131 credentials, 132 delete_sync_data_folder, 133 notifier_options, 134 RestoreEncryptionBootstrapToken(), 135 false)); 136} 137 138void SyncBackendHost::PersistEncryptionBootstrapToken( 139 const std::string& token) { 140 PrefService* prefs = profile_->GetPrefs(); 141 142 prefs->SetString(prefs::kEncryptionBootstrapToken, token); 143 prefs->ScheduleSavePersistentPrefs(); 144} 145 146std::string SyncBackendHost::RestoreEncryptionBootstrapToken() { 147 PrefService* prefs = profile_->GetPrefs(); 148 std::string token = prefs->GetString(prefs::kEncryptionBootstrapToken); 149 return token; 150} 151 152bool SyncBackendHost::IsNigoriEnabled() const { 153 AutoLock lock(registrar_lock_); 154 // Note that NIGORI is only ever added/removed from routing_info once, 155 // during initialization / first configuration, so there is no real 'race' 156 // possible here or possibility of stale return value. 157 return registrar_.routing_info.find(syncable::NIGORI) != 158 registrar_.routing_info.end(); 159} 160 161bool SyncBackendHost::IsUsingExplicitPassphrase() { 162 return IsNigoriEnabled() && syncapi_initialized_ && 163 core_->syncapi()->InitialSyncEndedForAllEnabledTypes() && 164 core_->syncapi()->IsUsingExplicitPassphrase(); 165} 166 167bool SyncBackendHost::IsCryptographerReady() const { 168 return syncapi_initialized_ && 169 GetUserShareHandle()->dir_manager->cryptographer()->is_ready(); 170} 171 172sync_api::HttpPostProviderFactory* SyncBackendHost::MakeHttpBridgeFactory( 173 URLRequestContextGetter* getter) { 174 return new HttpBridgeFactory(getter); 175} 176 177void SyncBackendHost::InitCore(const Core::DoInitializeOptions& options) { 178 core_thread_.message_loop()->PostTask(FROM_HERE, 179 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoInitialize, 180 options)); 181} 182 183void SyncBackendHost::UpdateCredentials(const SyncCredentials& credentials) { 184 core_thread_.message_loop()->PostTask(FROM_HERE, 185 NewRunnableMethod(core_.get(), 186 &SyncBackendHost::Core::DoUpdateCredentials, 187 credentials)); 188} 189 190void SyncBackendHost::StartSyncingWithServer() { 191 core_thread_.message_loop()->PostTask(FROM_HERE, 192 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoStartSyncing)); 193} 194 195void SyncBackendHost::SetPassphrase(const std::string& passphrase, 196 bool is_explicit) { 197 if (!IsNigoriEnabled()) { 198 LOG(WARNING) << "Silently dropping SetPassphrase request."; 199 return; 200 } 201 202 // If encryption is enabled and we've got a SetPassphrase 203 core_thread_.message_loop()->PostTask(FROM_HERE, 204 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoSetPassphrase, 205 passphrase, is_explicit)); 206} 207 208void SyncBackendHost::Shutdown(bool sync_disabled) { 209 // Thread shutdown should occur in the following order: 210 // - SyncerThread 211 // - CoreThread 212 // - UI Thread (stops some time after we return from this call). 213 if (core_thread_.IsRunning()) { // Not running in tests. 214 core_thread_.message_loop()->PostTask(FROM_HERE, 215 NewRunnableMethod(core_.get(), 216 &SyncBackendHost::Core::DoShutdown, 217 sync_disabled)); 218 } 219 220 // Before joining the core_thread_, we wait for the UIModelWorker to 221 // give us the green light that it is not depending on the frontend_loop_ to 222 // process any more tasks. Stop() blocks until this termination condition 223 // is true. 224 if (ui_worker()) 225 ui_worker()->Stop(); 226 227 // Stop will return once the thread exits, which will be after DoShutdown 228 // runs. DoShutdown needs to run from core_thread_ because the sync backend 229 // requires any thread that opened sqlite handles to relinquish them 230 // personally. We need to join threads, because otherwise the main Chrome 231 // thread (ui loop) can exit before DoShutdown finishes, at which point 232 // virtually anything the sync backend does (or the post-back to 233 // frontend_loop_ by our Core) will epically fail because the CRT won't be 234 // initialized. For now this only ever happens at sync-enabled-Chrome exit, 235 // meaning bug 1482548 applies to prolonged "waiting" that may occur in 236 // DoShutdown. 237 core_thread_.Stop(); 238 239 registrar_.routing_info.clear(); 240 registrar_.workers[GROUP_DB] = NULL; 241 registrar_.workers[GROUP_HISTORY] = NULL; 242 registrar_.workers[GROUP_UI] = NULL; 243 registrar_.workers[GROUP_PASSIVE] = NULL; 244 registrar_.workers[GROUP_PASSWORD] = NULL; 245 registrar_.workers.erase(GROUP_DB); 246 registrar_.workers.erase(GROUP_HISTORY); 247 registrar_.workers.erase(GROUP_UI); 248 registrar_.workers.erase(GROUP_PASSIVE); 249 registrar_.workers.erase(GROUP_PASSWORD); 250 frontend_ = NULL; 251 core_ = NULL; // Releases reference to core_. 252} 253 254void SyncBackendHost::ConfigureDataTypes(const syncable::ModelTypeSet& types, 255 CancelableTask* ready_task) { 256 // Only one configure is allowed at a time. 257 DCHECK(!configure_ready_task_.get()); 258 DCHECK(syncapi_initialized_); 259 260 bool deleted_type = false; 261 262 { 263 AutoLock lock(registrar_lock_); 264 for (DataTypeController::TypeMap::const_iterator it = 265 data_type_controllers_.begin(); 266 it != data_type_controllers_.end(); ++it) { 267 syncable::ModelType type = (*it).first; 268 269 // If a type is not specified, remove it from the routing_info. 270 if (types.count(type) == 0) { 271 registrar_.routing_info.erase(type); 272 deleted_type = true; 273 } else { 274 // Add a newly specified data type as GROUP_PASSIVE into the 275 // routing_info, if it does not already exist. 276 if (registrar_.routing_info.count(type) == 0) { 277 registrar_.routing_info[type] = GROUP_PASSIVE; 278 } 279 } 280 } 281 } 282 283 // If no new data types were added to the passive group, no need to 284 // wait for the syncer. 285 if (core_->syncapi()->InitialSyncEndedForAllEnabledTypes()) { 286 ready_task->Run(); 287 delete ready_task; 288 } else { 289 // Save the task here so we can run it when the syncer finishes 290 // initializing the new data types. It will be run only when the 291 // set of initially synced data types matches the types requested in 292 // this configure. 293 configure_ready_task_.reset(ready_task); 294 configure_initial_sync_types_ = types; 295 } 296 297 // Nudge the syncer. This is necessary for both datatype addition/deletion. 298 // 299 // Deletions need a nudge in order to ensure the deletion occurs in a timely 300 // manner (see issue 56416). 301 // 302 // In the case of additions, on the next sync cycle, the syncer should 303 // notice that the routing info has changed and start the process of 304 // downloading updates for newly added data types. Once this is 305 // complete, the configure_ready_task_ is run via an 306 // OnInitializationComplete notification. 307 if (deleted_type || !core_->syncapi()->InitialSyncEndedForAllEnabledTypes()) 308 // We can only nudge when we've either deleted a dataype or added one, else 309 // we break all the profile sync unit tests. 310 RequestNudge(); 311} 312 313void SyncBackendHost::RequestNudge() { 314 core_thread_.message_loop()->PostTask(FROM_HERE, 315 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestNudge)); 316} 317 318void SyncBackendHost::ActivateDataType( 319 DataTypeController* data_type_controller, 320 ChangeProcessor* change_processor) { 321 AutoLock lock(registrar_lock_); 322 323 // Ensure that the given data type is in the PASSIVE group. 324 browser_sync::ModelSafeRoutingInfo::iterator i = 325 registrar_.routing_info.find(data_type_controller->type()); 326 DCHECK(i != registrar_.routing_info.end()); 327 DCHECK((*i).second == GROUP_PASSIVE); 328 syncable::ModelType type = data_type_controller->type(); 329 // Change the data type's routing info to its group. 330 registrar_.routing_info[type] = data_type_controller->model_safe_group(); 331 332 // Add the data type's change processor to the list of change 333 // processors so it can receive updates. 334 DCHECK_EQ(processors_.count(type), 0U); 335 processors_[type] = change_processor; 336} 337 338void SyncBackendHost::DeactivateDataType( 339 DataTypeController* data_type_controller, 340 ChangeProcessor* change_processor) { 341 AutoLock lock(registrar_lock_); 342 registrar_.routing_info.erase(data_type_controller->type()); 343 344 std::map<syncable::ModelType, ChangeProcessor*>::size_type erased = 345 processors_.erase(data_type_controller->type()); 346 DCHECK_EQ(erased, 1U); 347 348 // TODO(sync): At this point we need to purge the data associated 349 // with this data type from the sync db. 350} 351 352bool SyncBackendHost::RequestPause() { 353 core_thread_.message_loop()->PostTask(FROM_HERE, 354 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestPause)); 355 return true; 356} 357 358bool SyncBackendHost::RequestResume() { 359 core_thread_.message_loop()->PostTask(FROM_HERE, 360 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestResume)); 361 return true; 362} 363 364bool SyncBackendHost::RequestClearServerData() { 365 core_thread_.message_loop()->PostTask(FROM_HERE, 366 NewRunnableMethod(core_.get(), 367 &SyncBackendHost::Core::DoRequestClearServerData)); 368 return true; 369} 370 371SyncBackendHost::Core::~Core() { 372} 373 374void SyncBackendHost::Core::NotifyPaused() { 375 NotificationService::current()->Notify(NotificationType::SYNC_PAUSED, 376 NotificationService::AllSources(), 377 NotificationService::NoDetails()); 378} 379 380void SyncBackendHost::Core::NotifyResumed() { 381 NotificationService::current()->Notify(NotificationType::SYNC_RESUMED, 382 NotificationService::AllSources(), 383 NotificationService::NoDetails()); 384} 385 386void SyncBackendHost::Core::NotifyPassphraseRequired(bool for_decryption) { 387 NotificationService::current()->Notify( 388 NotificationType::SYNC_PASSPHRASE_REQUIRED, 389 Source<SyncBackendHost>(host_), 390 Details<bool>(&for_decryption)); 391} 392 393void SyncBackendHost::Core::NotifyPassphraseAccepted( 394 const std::string& bootstrap_token) { 395 if (!host_) 396 return; 397 host_->PersistEncryptionBootstrapToken(bootstrap_token); 398 NotificationService::current()->Notify( 399 NotificationType::SYNC_PASSPHRASE_ACCEPTED, 400 Source<SyncBackendHost>(host_), 401 NotificationService::NoDetails()); 402} 403 404void SyncBackendHost::Core::NotifyUpdatedToken(const std::string& token) { 405 if (!host_) 406 return; 407 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 408 TokenAvailableDetails details(GaiaConstants::kSyncService, token); 409 NotificationService::current()->Notify( 410 NotificationType::TOKEN_UPDATED, 411 NotificationService::AllSources(), 412 Details<const TokenAvailableDetails>(&details)); 413} 414 415SyncBackendHost::UserShareHandle SyncBackendHost::GetUserShareHandle() const { 416 DCHECK(syncapi_initialized_); 417 return core_->syncapi()->GetUserShare(); 418} 419 420SyncBackendHost::Status SyncBackendHost::GetDetailedStatus() { 421 DCHECK(syncapi_initialized_); 422 return core_->syncapi()->GetDetailedStatus(); 423} 424 425SyncBackendHost::StatusSummary SyncBackendHost::GetStatusSummary() { 426 DCHECK(syncapi_initialized_); 427 return core_->syncapi()->GetStatusSummary(); 428} 429 430string16 SyncBackendHost::GetAuthenticatedUsername() const { 431 DCHECK(syncapi_initialized_); 432 return UTF8ToUTF16(core_->syncapi()->GetAuthenticatedUsername()); 433} 434 435const GoogleServiceAuthError& SyncBackendHost::GetAuthError() const { 436 return last_auth_error_; 437} 438 439const SyncSessionSnapshot* SyncBackendHost::GetLastSessionSnapshot() const { 440 return last_snapshot_.get(); 441} 442 443void SyncBackendHost::GetWorkers(std::vector<ModelSafeWorker*>* out) { 444 AutoLock lock(registrar_lock_); 445 out->clear(); 446 for (WorkerMap::const_iterator it = registrar_.workers.begin(); 447 it != registrar_.workers.end(); ++it) { 448 out->push_back((*it).second); 449 } 450} 451 452void SyncBackendHost::GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) { 453 AutoLock lock(registrar_lock_); 454 ModelSafeRoutingInfo copy(registrar_.routing_info); 455 out->swap(copy); 456} 457 458bool SyncBackendHost::HasUnsyncedItems() const { 459 DCHECK(syncapi_initialized_); 460 return core_->syncapi()->HasUnsyncedItems(); 461} 462 463SyncBackendHost::Core::Core(SyncBackendHost* backend) 464 : host_(backend), 465 syncapi_(new sync_api::SyncManager()) { 466} 467 468// Helper to construct a user agent string (ASCII) suitable for use by 469// the syncapi for any HTTP communication. This string is used by the sync 470// backend for classifying client types when calculating statistics. 471std::string MakeUserAgentForSyncapi() { 472 std::string user_agent; 473 user_agent = "Chrome "; 474#if defined(OS_WIN) 475 user_agent += "WIN "; 476#elif defined(OS_LINUX) 477 user_agent += "LINUX "; 478#elif defined(OS_FREEBSD) 479 user_agent += "FREEBSD "; 480#elif defined(OS_OPENBSD) 481 user_agent += "OPENBSD "; 482#elif defined(OS_MACOSX) 483 user_agent += "MAC "; 484#endif 485 chrome::VersionInfo version_info; 486 if (!version_info.is_valid()) { 487 DLOG(ERROR) << "Unable to create chrome::VersionInfo object"; 488 return user_agent; 489 } 490 491 user_agent += version_info.Version(); 492 user_agent += " (" + version_info.LastChange() + ")"; 493 if (!version_info.IsOfficialBuild()) 494 user_agent += "-devel"; 495 return user_agent; 496} 497 498void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) { 499 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); 500 501 // Blow away the partial or corrupt sync data folder before doing any more 502 // initialization, if necessary. 503 if (options.delete_sync_data_folder) { 504 DeleteSyncDataFolder(); 505 } 506 507 // Make sure that the directory exists before initializing the backend. 508 // If it already exists, this will do no harm. 509 bool success = file_util::CreateDirectory(host_->sync_data_folder_path()); 510 DCHECK(success); 511 512 syncapi_->SetObserver(this); 513 const FilePath& path_str = host_->sync_data_folder_path(); 514 success = syncapi_->Init( 515 path_str, 516 (options.service_url.host() + options.service_url.path()).c_str(), 517 options.service_url.EffectiveIntPort(), 518 options.service_url.SchemeIsSecure(), 519 options.http_bridge_factory, 520 host_, // ModelSafeWorkerRegistrar. 521 MakeUserAgentForSyncapi().c_str(), 522 options.credentials, 523 options.notifier_options, 524 options.restored_key_for_bootstrapping, 525 options.setup_for_test_mode); 526 DCHECK(success) << "Syncapi initialization failed!"; 527} 528 529void SyncBackendHost::Core::DoUpdateCredentials( 530 const SyncCredentials& credentials) { 531 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); 532 syncapi_->UpdateCredentials(credentials); 533} 534 535void SyncBackendHost::Core::DoStartSyncing() { 536 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); 537 syncapi_->StartSyncing(); 538} 539 540void SyncBackendHost::Core::DoSetPassphrase(const std::string& passphrase, 541 bool is_explicit) { 542 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); 543 syncapi_->SetPassphrase(passphrase, is_explicit); 544} 545 546UIModelWorker* SyncBackendHost::ui_worker() { 547 ModelSafeWorker* w = registrar_.workers[GROUP_UI]; 548 if (w == NULL) 549 return NULL; 550 if (w->GetModelSafeGroup() != GROUP_UI) 551 NOTREACHED(); 552 return static_cast<UIModelWorker*>(w); 553} 554 555void SyncBackendHost::Core::DoShutdown(bool sync_disabled) { 556 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); 557 558 save_changes_timer_.Stop(); 559 syncapi_->Shutdown(); // Stops the SyncerThread. 560 syncapi_->RemoveObserver(); 561 host_->ui_worker()->OnSyncerShutdownComplete(); 562 563 if (sync_disabled) 564 DeleteSyncDataFolder(); 565 566 host_ = NULL; 567} 568 569ChangeProcessor* SyncBackendHost::Core::GetProcessor( 570 syncable::ModelType model_type) { 571 std::map<syncable::ModelType, ChangeProcessor*>::const_iterator it = 572 host_->processors_.find(model_type); 573 574 // Until model association happens for a datatype, it will not appear in 575 // the processors list. During this time, it is OK to drop changes on 576 // the floor (since model association has not happened yet). When the 577 // data type is activated, model association takes place then the change 578 // processor is added to the processors_ list. This all happens on 579 // the UI thread so we will never drop any changes after model 580 // association. 581 if (it == host_->processors_.end()) 582 return NULL; 583 584 if (!IsCurrentThreadSafeForModel(model_type)) { 585 NOTREACHED() << "Changes applied on wrong thread."; 586 return NULL; 587 } 588 589 // Now that we're sure we're on the correct thread, we can access the 590 // ChangeProcessor. 591 ChangeProcessor* processor = it->second; 592 593 // Ensure the change processor is willing to accept changes. 594 if (!processor->IsRunning()) 595 return NULL; 596 597 return processor; 598} 599 600void SyncBackendHost::Core::OnChangesApplied( 601 syncable::ModelType model_type, 602 const sync_api::BaseTransaction* trans, 603 const sync_api::SyncManager::ChangeRecord* changes, 604 int change_count) { 605 if (!host_ || !host_->frontend_) { 606 DCHECK(false) << "OnChangesApplied called after Shutdown?"; 607 return; 608 } 609 ChangeProcessor* processor = GetProcessor(model_type); 610 if (!processor) 611 return; 612 613 processor->ApplyChangesFromSyncModel(trans, changes, change_count); 614} 615 616void SyncBackendHost::Core::OnChangesComplete( 617 syncable::ModelType model_type) { 618 if (!host_ || !host_->frontend_) { 619 DCHECK(false) << "OnChangesComplete called after Shutdown?"; 620 return; 621 } 622 623 ChangeProcessor* processor = GetProcessor(model_type); 624 if (!processor) 625 return; 626 627 // This call just notifies the processor that it can commit, it already 628 // buffered any changes it plans to makes so needs no further information. 629 processor->CommitChangesFromSyncModel(); 630} 631 632 633void SyncBackendHost::Core::OnSyncCycleCompleted( 634 const SyncSessionSnapshot* snapshot) { 635 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 636 &Core::HandleSyncCycleCompletedOnFrontendLoop, 637 new SyncSessionSnapshot(*snapshot))); 638} 639 640void SyncBackendHost::Core::HandleSyncCycleCompletedOnFrontendLoop( 641 SyncSessionSnapshot* snapshot) { 642 if (!host_ || !host_->frontend_) 643 return; 644 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 645 646 host_->last_snapshot_.reset(snapshot); 647 648 // If we are waiting for a configuration change, check here to see 649 // if this sync cycle has initialized all of the types we've been 650 // waiting for. 651 if (host_->configure_ready_task_.get()) { 652 bool found_all = true; 653 for (syncable::ModelTypeSet::const_iterator it = 654 host_->configure_initial_sync_types_.begin(); 655 it != host_->configure_initial_sync_types_.end(); ++it) { 656 found_all &= snapshot->initial_sync_ended.test(*it); 657 } 658 659 if (found_all) { 660 host_->configure_ready_task_->Run(); 661 host_->configure_ready_task_.reset(); 662 host_->configure_initial_sync_types_.clear(); 663 } 664 } 665 host_->frontend_->OnSyncCycleCompleted(); 666} 667 668void SyncBackendHost::Core::OnInitializationComplete() { 669 if (!host_ || !host_->frontend_) 670 return; // We may have been told to Shutdown before initialization 671 // completed. 672 673 // We could be on some random sync backend thread, so MessageLoop::current() 674 // can definitely be null in here. 675 host_->frontend_loop_->PostTask(FROM_HERE, 676 NewRunnableMethod(this, 677 &Core::HandleInitalizationCompletedOnFrontendLoop)); 678 679 // Initialization is complete, so we can schedule recurring SaveChanges. 680 host_->core_thread_.message_loop()->PostTask(FROM_HERE, 681 NewRunnableMethod(this, &Core::StartSavingChanges)); 682} 683 684void SyncBackendHost::Core::HandleInitalizationCompletedOnFrontendLoop() { 685 if (!host_) 686 return; 687 host_->HandleInitializationCompletedOnFrontendLoop(); 688} 689 690void SyncBackendHost::HandleInitializationCompletedOnFrontendLoop() { 691 if (!frontend_) 692 return; 693 syncapi_initialized_ = true; 694 frontend_->OnBackendInitialized(); 695} 696 697bool SyncBackendHost::Core::IsCurrentThreadSafeForModel( 698 syncable::ModelType model_type) { 699 AutoLock lock(host_->registrar_lock_); 700 701 browser_sync::ModelSafeRoutingInfo::const_iterator routing_it = 702 host_->registrar_.routing_info.find(model_type); 703 if (routing_it == host_->registrar_.routing_info.end()) 704 return false; 705 browser_sync::ModelSafeGroup group = routing_it->second; 706 WorkerMap::const_iterator worker_it = host_->registrar_.workers.find(group); 707 if (worker_it == host_->registrar_.workers.end()) 708 return false; 709 ModelSafeWorker* worker = worker_it->second; 710 return worker->CurrentThreadIsWorkThread(); 711} 712 713 714void SyncBackendHost::Core::OnAuthError(const AuthError& auth_error) { 715 // Post to our core loop so we can modify state. Could be on another thread. 716 host_->frontend_loop_->PostTask(FROM_HERE, 717 NewRunnableMethod(this, &Core::HandleAuthErrorEventOnFrontendLoop, 718 auth_error)); 719} 720 721void SyncBackendHost::Core::OnPassphraseRequired(bool for_decryption) { 722 host_->frontend_loop_->PostTask(FROM_HERE, 723 NewRunnableMethod(this, &Core::NotifyPassphraseRequired, for_decryption)); 724} 725 726void SyncBackendHost::Core::OnPassphraseAccepted( 727 const std::string& bootstrap_token) { 728 host_->frontend_loop_->PostTask(FROM_HERE, 729 NewRunnableMethod(this, &Core::NotifyPassphraseAccepted, 730 bootstrap_token)); 731} 732 733void SyncBackendHost::Core::OnPaused() { 734 host_->frontend_loop_->PostTask( 735 FROM_HERE, 736 NewRunnableMethod(this, &Core::NotifyPaused)); 737} 738 739void SyncBackendHost::Core::OnResumed() { 740 host_->frontend_loop_->PostTask( 741 FROM_HERE, 742 NewRunnableMethod(this, &Core::NotifyResumed)); 743} 744 745void SyncBackendHost::Core::OnStopSyncingPermanently() { 746 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 747 &Core::HandleStopSyncingPermanentlyOnFrontendLoop)); 748} 749 750void SyncBackendHost::Core::OnUpdatedToken(const std::string& token) { 751 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 752 &Core::NotifyUpdatedToken, token)); 753} 754 755void SyncBackendHost::Core::OnClearServerDataSucceeded() { 756 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 757 &Core::HandleClearServerDataSucceededOnFrontendLoop)); 758} 759 760void SyncBackendHost::Core::OnClearServerDataFailed() { 761 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 762 &Core::HandleClearServerDataFailedOnFrontendLoop)); 763} 764 765void SyncBackendHost::Core::HandleStopSyncingPermanentlyOnFrontendLoop() { 766 if (!host_ || !host_->frontend_) 767 return; 768 host_->frontend_->OnStopSyncingPermanently(); 769} 770 771void SyncBackendHost::Core::HandleClearServerDataSucceededOnFrontendLoop() { 772 if (!host_ || !host_->frontend_) 773 return; 774 host_->frontend_->OnClearServerDataSucceeded(); 775} 776 777void SyncBackendHost::Core::HandleClearServerDataFailedOnFrontendLoop() { 778 if (!host_ || !host_->frontend_) 779 return; 780 host_->frontend_->OnClearServerDataFailed(); 781} 782 783void SyncBackendHost::Core::HandleAuthErrorEventOnFrontendLoop( 784 const GoogleServiceAuthError& new_auth_error) { 785 if (!host_ || !host_->frontend_) 786 return; 787 788 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 789 790 host_->last_auth_error_ = new_auth_error; 791 host_->frontend_->OnAuthError(); 792} 793 794void SyncBackendHost::Core::StartSavingChanges() { 795 save_changes_timer_.Start( 796 base::TimeDelta::FromSeconds(kSaveChangesIntervalSeconds), 797 this, &Core::SaveChanges); 798} 799 800void SyncBackendHost::Core::DoRequestNudge() { 801 syncapi_->RequestNudge(); 802} 803 804void SyncBackendHost::Core::DoRequestClearServerData() { 805 syncapi_->RequestClearServerData(); 806} 807 808void SyncBackendHost::Core::DoRequestResume() { 809 syncapi_->RequestResume(); 810} 811 812void SyncBackendHost::Core::DoRequestPause() { 813 syncapi()->RequestPause(); 814} 815 816void SyncBackendHost::Core::SaveChanges() { 817 syncapi_->SaveChanges(); 818} 819 820void SyncBackendHost::Core::DeleteSyncDataFolder() { 821 if (file_util::DirectoryExists(host_->sync_data_folder_path())) { 822 if (!file_util::Delete(host_->sync_data_folder_path(), true)) 823 LOG(DFATAL) << "Could not delete the Sync Data folder."; 824 } 825} 826 827} // namespace browser_sync 828