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