1 // Copyright (c) 2011 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/compiler_specific.h" 11 #include "base/file_util.h" 12 #include "base/task.h" 13 #include "base/threading/thread_restrictions.h" 14 #include "base/utf_string_conversions.h" 15 #include "chrome/browser/net/gaia/token_service.h" 16 #include "chrome/browser/prefs/pref_service.h" 17 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/browser/sync/engine/syncapi.h" 19 #include "chrome/browser/sync/glue/autofill_model_associator.h" 20 #include "chrome/browser/sync/glue/autofill_profile_model_associator.h" 21 #include "chrome/browser/sync/glue/change_processor.h" 22 #include "chrome/browser/sync/glue/database_model_worker.h" 23 #include "chrome/browser/sync/glue/history_model_worker.h" 24 #include "chrome/browser/sync/glue/http_bridge.h" 25 #include "chrome/browser/sync/glue/password_model_worker.h" 26 #include "chrome/browser/sync/glue/sync_backend_host.h" 27 #include "chrome/browser/sync/js_arg_list.h" 28 #include "chrome/browser/sync/notifier/sync_notifier.h" 29 #include "chrome/browser/sync/notifier/sync_notifier_factory.h" 30 #include "chrome/browser/sync/sessions/session_state.h" 31 // TODO(tim): Remove this! We should have a syncapi pass-thru instead. 32 #include "chrome/browser/sync/syncable/directory_manager.h" // Cryptographer. 33 #include "chrome/browser/sync/syncable/model_type.h" 34 #include "chrome/browser/sync/syncable/nigori_util.h" 35 #include "chrome/common/chrome_switches.h" 36 #include "chrome/common/chrome_version_info.h" 37 #include "chrome/common/net/gaia/gaia_constants.h" 38 #include "chrome/common/pref_names.h" 39 #include "content/browser/browser_thread.h" 40 #include "content/common/notification_service.h" 41 #include "content/common/notification_type.h" 42 #include "googleurl/src/gurl.h" 43 #include "webkit/glue/webkit_glue.h" 44 45 static const int kSaveChangesIntervalSeconds = 10; 46 static const FilePath::CharType kSyncDataFolderName[] = 47 FILE_PATH_LITERAL("Sync Data"); 48 49 using browser_sync::DataTypeController; 50 using sync_notifier::SyncNotifierFactory; 51 typedef TokenService::TokenAvailableDetails TokenAvailableDetails; 52 53 typedef GoogleServiceAuthError AuthError; 54 55 namespace browser_sync { 56 57 using sessions::SyncSessionSnapshot; 58 using sync_api::SyncCredentials; 59 60 SyncBackendHost::SyncBackendHost(Profile* profile) 61 : core_(new Core(ALLOW_THIS_IN_INITIALIZER_LIST(this))), 62 core_thread_("Chrome_SyncCoreThread"), 63 frontend_loop_(MessageLoop::current()), 64 profile_(profile), 65 frontend_(NULL), 66 sync_data_folder_path_( 67 profile_->GetPath().Append(kSyncDataFolderName)), 68 last_auth_error_(AuthError::None()), 69 syncapi_initialized_(false) { 70 } 71 72 SyncBackendHost::SyncBackendHost() 73 : core_thread_("Chrome_SyncCoreThread"), 74 frontend_loop_(MessageLoop::current()), 75 profile_(NULL), 76 frontend_(NULL), 77 last_auth_error_(AuthError::None()), 78 syncapi_initialized_(false) { 79 } 80 81 SyncBackendHost::~SyncBackendHost() { 82 DCHECK(!core_ && !frontend_) << "Must call Shutdown before destructor."; 83 DCHECK(registrar_.workers.empty()); 84 } 85 86 void SyncBackendHost::Initialize( 87 SyncFrontend* frontend, 88 const GURL& sync_service_url, 89 const syncable::ModelTypeSet& types, 90 net::URLRequestContextGetter* baseline_context_getter, 91 const SyncCredentials& credentials, 92 bool delete_sync_data_folder) { 93 if (!core_thread_.Start()) 94 return; 95 96 frontend_ = frontend; 97 DCHECK(frontend); 98 99 // Create a worker for the UI thread and route bookmark changes to it. 100 // TODO(tim): Pull this into a method to reuse. For now we don't even 101 // need to lock because we init before the syncapi exists and we tear down 102 // after the syncapi is destroyed. Make sure to NULL-check workers_ indices 103 // when a new type is synced as the worker may already exist and you just 104 // need to update routing_info_. 105 registrar_.workers[GROUP_DB] = new DatabaseModelWorker(); 106 registrar_.workers[GROUP_UI] = new UIModelWorker(); 107 registrar_.workers[GROUP_PASSIVE] = new ModelSafeWorker(); 108 109 if (CommandLine::ForCurrentProcess()->HasSwitch( 110 switches::kEnableSyncTypedUrls) || types.count(syncable::TYPED_URLS)) { 111 // TODO(tim): Bug 53916. HistoryModelWorker crashes, so avoid adding it 112 // unless specifically requested until bug is fixed. 113 registrar_.workers[GROUP_HISTORY] = 114 new HistoryModelWorker( 115 profile_->GetHistoryService(Profile::IMPLICIT_ACCESS)); 116 } 117 118 // Any datatypes that we want the syncer to pull down must 119 // be in the routing_info map. We set them to group passive, meaning that 120 // updates will be applied, but not dispatched to the UI thread yet. 121 for (syncable::ModelTypeSet::const_iterator it = types.begin(); 122 it != types.end(); ++it) { 123 registrar_.routing_info[(*it)] = GROUP_PASSIVE; 124 } 125 126 PasswordStore* password_store = 127 profile_->GetPasswordStore(Profile::IMPLICIT_ACCESS); 128 if (password_store) { 129 registrar_.workers[GROUP_PASSWORD] = 130 new PasswordModelWorker(password_store); 131 } else { 132 LOG_IF(WARNING, types.count(syncable::PASSWORDS) > 0) << "Password store " 133 << "not initialized, cannot sync passwords"; 134 registrar_.routing_info.erase(syncable::PASSWORDS); 135 } 136 137 // Nigori is populated by default now. 138 registrar_.routing_info[syncable::NIGORI] = GROUP_PASSIVE; 139 140 // TODO(akalin): Create SyncNotifier here and pass it in as part of 141 // DoInitializeOptions. 142 core_->CreateSyncNotifier(baseline_context_getter); 143 144 InitCore(Core::DoInitializeOptions( 145 sync_service_url, 146 MakeHttpBridgeFactory(baseline_context_getter), 147 credentials, 148 delete_sync_data_folder, 149 RestoreEncryptionBootstrapToken(), 150 false)); 151 } 152 153 void SyncBackendHost::PersistEncryptionBootstrapToken( 154 const std::string& token) { 155 PrefService* prefs = profile_->GetPrefs(); 156 157 prefs->SetString(prefs::kEncryptionBootstrapToken, token); 158 prefs->ScheduleSavePersistentPrefs(); 159 } 160 161 std::string SyncBackendHost::RestoreEncryptionBootstrapToken() { 162 PrefService* prefs = profile_->GetPrefs(); 163 std::string token = prefs->GetString(prefs::kEncryptionBootstrapToken); 164 return token; 165 } 166 167 bool SyncBackendHost::IsNigoriEnabled() const { 168 base::AutoLock lock(registrar_lock_); 169 // Note that NIGORI is only ever added/removed from routing_info once, 170 // during initialization / first configuration, so there is no real 'race' 171 // possible here or possibility of stale return value. 172 return registrar_.routing_info.find(syncable::NIGORI) != 173 registrar_.routing_info.end(); 174 } 175 176 bool SyncBackendHost::IsUsingExplicitPassphrase() { 177 return IsNigoriEnabled() && syncapi_initialized_ && 178 core_->syncapi()->InitialSyncEndedForAllEnabledTypes() && 179 core_->syncapi()->IsUsingExplicitPassphrase(); 180 } 181 182 bool SyncBackendHost::IsCryptographerReady( 183 const sync_api::BaseTransaction* trans) const { 184 return syncapi_initialized_ && trans->GetCryptographer()->is_ready(); 185 } 186 187 JsBackend* SyncBackendHost::GetJsBackend() { 188 if (syncapi_initialized_) { 189 return core_.get(); 190 } else { 191 NOTREACHED(); 192 return NULL; 193 } 194 } 195 196 sync_api::HttpPostProviderFactory* SyncBackendHost::MakeHttpBridgeFactory( 197 net::URLRequestContextGetter* getter) { 198 return new HttpBridgeFactory(getter); 199 } 200 201 void SyncBackendHost::InitCore(const Core::DoInitializeOptions& options) { 202 core_thread_.message_loop()->PostTask(FROM_HERE, 203 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoInitialize, 204 options)); 205 } 206 207 void SyncBackendHost::UpdateCredentials(const SyncCredentials& credentials) { 208 core_thread_.message_loop()->PostTask(FROM_HERE, 209 NewRunnableMethod(core_.get(), 210 &SyncBackendHost::Core::DoUpdateCredentials, 211 credentials)); 212 } 213 214 void SyncBackendHost::StartSyncingWithServer() { 215 core_thread_.message_loop()->PostTask(FROM_HERE, 216 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoStartSyncing)); 217 } 218 219 void SyncBackendHost::SetPassphrase(const std::string& passphrase, 220 bool is_explicit) { 221 if (!IsNigoriEnabled()) { 222 LOG(WARNING) << "Silently dropping SetPassphrase request."; 223 return; 224 } 225 226 // This should only be called by the frontend. 227 DCHECK_EQ(MessageLoop::current(), frontend_loop_); 228 if (core_->processing_passphrase()) { 229 VLOG(1) << "Attempted to call SetPassphrase while already waiting for " 230 << " result from previous SetPassphrase call. Silently dropping."; 231 return; 232 } 233 core_->set_processing_passphrase(); 234 235 // If encryption is enabled and we've got a SetPassphrase 236 core_thread_.message_loop()->PostTask(FROM_HERE, 237 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoSetPassphrase, 238 passphrase, is_explicit)); 239 } 240 241 void SyncBackendHost::Shutdown(bool sync_disabled) { 242 // Thread shutdown should occur in the following order: 243 // - SyncerThread 244 // - CoreThread 245 // - UI Thread (stops some time after we return from this call). 246 if (core_thread_.IsRunning()) { // Not running in tests. 247 core_thread_.message_loop()->PostTask(FROM_HERE, 248 NewRunnableMethod(core_.get(), 249 &SyncBackendHost::Core::DoShutdown, 250 sync_disabled)); 251 } 252 253 // Before joining the core_thread_, we wait for the UIModelWorker to 254 // give us the green light that it is not depending on the frontend_loop_ to 255 // process any more tasks. Stop() blocks until this termination condition 256 // is true. 257 if (ui_worker()) 258 ui_worker()->Stop(); 259 260 // Stop will return once the thread exits, which will be after DoShutdown 261 // runs. DoShutdown needs to run from core_thread_ because the sync backend 262 // requires any thread that opened sqlite handles to relinquish them 263 // personally. We need to join threads, because otherwise the main Chrome 264 // thread (ui loop) can exit before DoShutdown finishes, at which point 265 // virtually anything the sync backend does (or the post-back to 266 // frontend_loop_ by our Core) will epically fail because the CRT won't be 267 // initialized. 268 // Since we are blocking the UI thread here, we need to turn ourselves in 269 // with the ThreadRestriction police. For sentencing and how we plan to fix 270 // this, see bug 19757. 271 { 272 base::ThreadRestrictions::ScopedAllowIO allow_io; 273 core_thread_.Stop(); 274 } 275 276 registrar_.routing_info.clear(); 277 registrar_.workers[GROUP_DB] = NULL; 278 registrar_.workers[GROUP_HISTORY] = NULL; 279 registrar_.workers[GROUP_UI] = NULL; 280 registrar_.workers[GROUP_PASSIVE] = NULL; 281 registrar_.workers[GROUP_PASSWORD] = NULL; 282 registrar_.workers.erase(GROUP_DB); 283 registrar_.workers.erase(GROUP_HISTORY); 284 registrar_.workers.erase(GROUP_UI); 285 registrar_.workers.erase(GROUP_PASSIVE); 286 registrar_.workers.erase(GROUP_PASSWORD); 287 frontend_ = NULL; 288 core_ = NULL; // Releases reference to core_. 289 } 290 291 syncable::AutofillMigrationState 292 SyncBackendHost::GetAutofillMigrationState() { 293 return core_->syncapi()->GetAutofillMigrationState(); 294 } 295 296 void SyncBackendHost::SetAutofillMigrationState( 297 syncable::AutofillMigrationState state) { 298 return core_->syncapi()->SetAutofillMigrationState(state); 299 } 300 301 syncable::AutofillMigrationDebugInfo 302 SyncBackendHost::GetAutofillMigrationDebugInfo() { 303 return core_->syncapi()->GetAutofillMigrationDebugInfo(); 304 } 305 306 void SyncBackendHost::SetAutofillMigrationDebugInfo( 307 syncable::AutofillMigrationDebugInfo::PropertyToSet property_to_set, 308 const syncable::AutofillMigrationDebugInfo& info) { 309 return core_->syncapi()->SetAutofillMigrationDebugInfo(property_to_set, info); 310 } 311 312 void SyncBackendHost::ConfigureAutofillMigration() { 313 if (GetAutofillMigrationState() == syncable::NOT_DETERMINED) { 314 sync_api::ReadTransaction trans(GetUserShare()); 315 sync_api::ReadNode autofil_root_node(&trans); 316 317 // Check for the presence of autofill node. 318 if (!autofil_root_node.InitByTagLookup(browser_sync::kAutofillTag)) { 319 SetAutofillMigrationState(syncable::INSUFFICIENT_INFO_TO_DETERMINE); 320 return; 321 } 322 323 // Check for children under autofill node. 324 if (autofil_root_node.GetFirstChildId() == static_cast<int64>(0)) { 325 SetAutofillMigrationState(syncable::INSUFFICIENT_INFO_TO_DETERMINE); 326 return; 327 } 328 329 sync_api::ReadNode autofill_profile_root_node(&trans); 330 331 // Check for the presence of autofill profile root node. 332 if (!autofill_profile_root_node.InitByTagLookup( 333 browser_sync::kAutofillProfileTag)) { 334 SetAutofillMigrationState(syncable::NOT_MIGRATED); 335 return; 336 } 337 338 // If our state is not determined then we should not have the autofill 339 // profile node. 340 DCHECK(false); 341 342 // just set it as not migrated. 343 SetAutofillMigrationState(syncable::NOT_MIGRATED); 344 return; 345 } 346 } 347 348 SyncBackendHost::PendingConfigureDataTypesState:: 349 PendingConfigureDataTypesState() : deleted_type(false) {} 350 351 SyncBackendHost::PendingConfigureDataTypesState:: 352 ~PendingConfigureDataTypesState() {} 353 354 // static 355 SyncBackendHost::PendingConfigureDataTypesState* 356 SyncBackendHost::MakePendingConfigModeState( 357 const DataTypeController::TypeMap& data_type_controllers, 358 const syncable::ModelTypeSet& types, 359 CancelableTask* ready_task, 360 ModelSafeRoutingInfo* routing_info) { 361 PendingConfigureDataTypesState* state = new PendingConfigureDataTypesState(); 362 for (DataTypeController::TypeMap::const_iterator it = 363 data_type_controllers.begin(); 364 it != data_type_controllers.end(); ++it) { 365 syncable::ModelType type = it->first; 366 // If a type is not specified, remove it from the routing_info. 367 if (types.count(type) == 0) { 368 state->deleted_type = true; 369 routing_info->erase(type); 370 } else { 371 // Add a newly specified data type as GROUP_PASSIVE into the 372 // routing_info, if it does not already exist. 373 if (routing_info->count(type) == 0) { 374 (*routing_info)[type] = GROUP_PASSIVE; 375 state->added_types.set(type); 376 } 377 } 378 } 379 380 state->ready_task.reset(ready_task); 381 state->initial_types = types; 382 return state; 383 } 384 385 void SyncBackendHost::ConfigureDataTypes( 386 const DataTypeController::TypeMap& data_type_controllers, 387 const syncable::ModelTypeSet& types, 388 CancelableTask* ready_task) { 389 // Only one configure is allowed at a time. 390 DCHECK(!pending_config_mode_state_.get()); 391 DCHECK(!pending_download_state_.get()); 392 DCHECK(syncapi_initialized_); 393 394 if (types.count(syncable::AUTOFILL_PROFILE) != 0) { 395 ConfigureAutofillMigration(); 396 } 397 398 { 399 base::AutoLock lock(registrar_lock_); 400 pending_config_mode_state_.reset( 401 MakePendingConfigModeState(data_type_controllers, types, ready_task, 402 ®istrar_.routing_info)); 403 } 404 405 StartConfiguration(NewCallback(core_.get(), 406 &SyncBackendHost::Core::FinishConfigureDataTypes)); 407 } 408 409 void SyncBackendHost::StartConfiguration(Callback0::Type* callback) { 410 // Put syncer in the config mode. DTM will put us in normal mode once it is. 411 // done. This is to ensure we dont do a normal sync when we are doing model 412 // association. 413 core_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( 414 core_.get(),&SyncBackendHost::Core::DoStartConfiguration, callback)); 415 } 416 417 void SyncBackendHost::FinishConfigureDataTypesOnFrontendLoop() { 418 DCHECK_EQ(MessageLoop::current(), frontend_loop_); 419 // Nudge the syncer. This is necessary for both datatype addition/deletion. 420 // 421 // Deletions need a nudge in order to ensure the deletion occurs in a timely 422 // manner (see issue 56416). 423 // 424 // In the case of additions, on the next sync cycle, the syncer should 425 // notice that the routing info has changed and start the process of 426 // downloading updates for newly added data types. Once this is 427 // complete, the configure_state_.ready_task_ is run via an 428 // OnInitializationComplete notification. 429 430 if (pending_config_mode_state_->deleted_type) { 431 core_thread_.message_loop()->PostTask(FROM_HERE, 432 NewRunnableMethod(core_.get(), 433 &SyncBackendHost::Core::DeferNudgeForCleanup)); 434 } 435 436 if (pending_config_mode_state_->added_types.none() && 437 !core_->syncapi()->InitialSyncEndedForAllEnabledTypes()) { 438 LOG(WARNING) << "No new types, but initial sync not finished." 439 << "Possible sync db corruption / removal."; 440 // TODO(tim): Log / UMA / count this somehow? 441 // TODO(tim): If no added types, we could (should?) config only for 442 // types that are needed... but this is a rare corruption edge case or 443 // implies the user mucked around with their syncdb, so for now do all. 444 pending_config_mode_state_->added_types = 445 syncable::ModelTypeBitSetFromSet( 446 pending_config_mode_state_->initial_types); 447 } 448 449 // If we've added types, we always want to request a nudge/config (even if 450 // the initial sync is ended), in case we could not decrypt the data. 451 if (pending_config_mode_state_->added_types.none()) { 452 // No new types - just notify the caller that the types are available. 453 pending_config_mode_state_->ready_task->Run(); 454 } else { 455 pending_download_state_.reset(pending_config_mode_state_.release()); 456 457 syncable::ModelTypeBitSet types_copy(pending_download_state_->added_types); 458 if (IsNigoriEnabled()) 459 types_copy.set(syncable::NIGORI); 460 core_thread_.message_loop()->PostTask(FROM_HERE, 461 NewRunnableMethod(core_.get(), 462 &SyncBackendHost::Core::DoRequestConfig, 463 types_copy)); 464 } 465 466 pending_config_mode_state_.reset(); 467 468 // Notify the SyncManager about the new types. 469 core_thread_.message_loop()->PostTask(FROM_HERE, 470 NewRunnableMethod(core_.get(), 471 &SyncBackendHost::Core::DoUpdateEnabledTypes)); 472 } 473 474 void SyncBackendHost::EncryptDataTypes( 475 const syncable::ModelTypeSet& encrypted_types) { 476 core_thread_.message_loop()->PostTask(FROM_HERE, 477 NewRunnableMethod(core_.get(), 478 &SyncBackendHost::Core::DoEncryptDataTypes, 479 encrypted_types)); 480 } 481 482 void SyncBackendHost::RequestNudge(const tracked_objects::Location& location) { 483 core_thread_.message_loop()->PostTask(FROM_HERE, 484 NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestNudge, 485 location)); 486 } 487 488 void SyncBackendHost::ActivateDataType( 489 DataTypeController* data_type_controller, 490 ChangeProcessor* change_processor) { 491 base::AutoLock lock(registrar_lock_); 492 493 // Ensure that the given data type is in the PASSIVE group. 494 browser_sync::ModelSafeRoutingInfo::iterator i = 495 registrar_.routing_info.find(data_type_controller->type()); 496 DCHECK(i != registrar_.routing_info.end()); 497 DCHECK((*i).second == GROUP_PASSIVE); 498 syncable::ModelType type = data_type_controller->type(); 499 // Change the data type's routing info to its group. 500 registrar_.routing_info[type] = data_type_controller->model_safe_group(); 501 502 // Add the data type's change processor to the list of change 503 // processors so it can receive updates. 504 DCHECK_EQ(processors_.count(type), 0U); 505 processors_[type] = change_processor; 506 } 507 508 void SyncBackendHost::DeactivateDataType( 509 DataTypeController* data_type_controller, 510 ChangeProcessor* change_processor) { 511 base::AutoLock lock(registrar_lock_); 512 registrar_.routing_info.erase(data_type_controller->type()); 513 514 std::map<syncable::ModelType, ChangeProcessor*>::size_type erased = 515 processors_.erase(data_type_controller->type()); 516 DCHECK_EQ(erased, 1U); 517 } 518 519 bool SyncBackendHost::RequestClearServerData() { 520 core_thread_.message_loop()->PostTask(FROM_HERE, 521 NewRunnableMethod(core_.get(), 522 &SyncBackendHost::Core::DoRequestClearServerData)); 523 return true; 524 } 525 526 SyncBackendHost::Core::~Core() { 527 } 528 529 void SyncBackendHost::Core::NotifyPassphraseRequired(bool for_decryption) { 530 if (!host_ || !host_->frontend_) 531 return; 532 533 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 534 535 if (processing_passphrase_) { 536 VLOG(1) << "Core received OnPassphraseRequired while processing a " 537 << "passphrase. Silently dropping."; 538 return; 539 } 540 host_->frontend_->OnPassphraseRequired(for_decryption); 541 } 542 543 void SyncBackendHost::Core::NotifyPassphraseFailed() { 544 if (!host_ || !host_->frontend_) 545 return; 546 547 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 548 549 // When a passphrase fails, we just unset our waiting flag and trigger a 550 // OnPassphraseRequired(true). 551 processing_passphrase_ = false; 552 host_->frontend_->OnPassphraseRequired(true); 553 } 554 555 void SyncBackendHost::Core::NotifyPassphraseAccepted( 556 const std::string& bootstrap_token) { 557 if (!host_ || !host_->frontend_) 558 return; 559 560 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 561 562 processing_passphrase_ = false; 563 host_->PersistEncryptionBootstrapToken(bootstrap_token); 564 host_->frontend_->OnPassphraseAccepted(); 565 } 566 567 void SyncBackendHost::Core::NotifyUpdatedToken(const std::string& token) { 568 if (!host_) 569 return; 570 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 571 TokenAvailableDetails details(GaiaConstants::kSyncService, token); 572 NotificationService::current()->Notify( 573 NotificationType::TOKEN_UPDATED, 574 NotificationService::AllSources(), 575 Details<const TokenAvailableDetails>(&details)); 576 } 577 578 void SyncBackendHost::Core::NotifyEncryptionComplete( 579 const syncable::ModelTypeSet& encrypted_types) { 580 if (!host_) 581 return; 582 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 583 host_->frontend_->OnEncryptionComplete(encrypted_types); 584 } 585 586 void SyncBackendHost::Core::FinishConfigureDataTypes() { 587 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 588 &SyncBackendHost::Core::FinishConfigureDataTypesOnFrontendLoop)); 589 } 590 591 void SyncBackendHost::Core::FinishConfigureDataTypesOnFrontendLoop() { 592 host_->FinishConfigureDataTypesOnFrontendLoop(); 593 } 594 595 596 void SyncBackendHost::Core::CreateSyncNotifier( 597 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter) { 598 const std::string& client_info = webkit_glue::GetUserAgent(GURL()); 599 SyncNotifierFactory sync_notifier_factory(client_info); 600 sync_notifier_.reset(sync_notifier_factory.CreateSyncNotifier( 601 *CommandLine::ForCurrentProcess(), 602 request_context_getter)); 603 } 604 605 SyncBackendHost::Core::DoInitializeOptions::DoInitializeOptions( 606 const GURL& service_url, 607 sync_api::HttpPostProviderFactory* http_bridge_factory, 608 const sync_api::SyncCredentials& credentials, 609 bool delete_sync_data_folder, 610 const std::string& restored_key_for_bootstrapping, 611 bool setup_for_test_mode) 612 : service_url(service_url), 613 http_bridge_factory(http_bridge_factory), 614 credentials(credentials), 615 delete_sync_data_folder(delete_sync_data_folder), 616 restored_key_for_bootstrapping(restored_key_for_bootstrapping), 617 setup_for_test_mode(setup_for_test_mode) { 618 } 619 620 SyncBackendHost::Core::DoInitializeOptions::~DoInitializeOptions() {} 621 622 sync_api::UserShare* SyncBackendHost::GetUserShare() const { 623 DCHECK(syncapi_initialized_); 624 return core_->syncapi()->GetUserShare(); 625 } 626 627 SyncBackendHost::Status SyncBackendHost::GetDetailedStatus() { 628 DCHECK(syncapi_initialized_); 629 return core_->syncapi()->GetDetailedStatus(); 630 } 631 632 SyncBackendHost::StatusSummary SyncBackendHost::GetStatusSummary() { 633 DCHECK(syncapi_initialized_); 634 return core_->syncapi()->GetStatusSummary(); 635 } 636 637 string16 SyncBackendHost::GetAuthenticatedUsername() const { 638 DCHECK(syncapi_initialized_); 639 return UTF8ToUTF16(core_->syncapi()->GetAuthenticatedUsername()); 640 } 641 642 const GoogleServiceAuthError& SyncBackendHost::GetAuthError() const { 643 return last_auth_error_; 644 } 645 646 const SyncSessionSnapshot* SyncBackendHost::GetLastSessionSnapshot() const { 647 return last_snapshot_.get(); 648 } 649 650 void SyncBackendHost::GetWorkers(std::vector<ModelSafeWorker*>* out) { 651 base::AutoLock lock(registrar_lock_); 652 out->clear(); 653 for (WorkerMap::const_iterator it = registrar_.workers.begin(); 654 it != registrar_.workers.end(); ++it) { 655 out->push_back((*it).second); 656 } 657 } 658 659 void SyncBackendHost::GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) { 660 base::AutoLock lock(registrar_lock_); 661 ModelSafeRoutingInfo copy(registrar_.routing_info); 662 out->swap(copy); 663 } 664 665 bool SyncBackendHost::HasUnsyncedItems() const { 666 DCHECK(syncapi_initialized_); 667 return core_->syncapi()->HasUnsyncedItems(); 668 } 669 670 SyncBackendHost::Core::Core(SyncBackendHost* backend) 671 : host_(backend), 672 syncapi_(new sync_api::SyncManager()), 673 sync_manager_observer_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), 674 parent_router_(NULL), 675 processing_passphrase_(false), 676 deferred_nudge_for_cleanup_requested_(false) { 677 } 678 679 // Helper to construct a user agent string (ASCII) suitable for use by 680 // the syncapi for any HTTP communication. This string is used by the sync 681 // backend for classifying client types when calculating statistics. 682 std::string MakeUserAgentForSyncapi() { 683 std::string user_agent; 684 user_agent = "Chrome "; 685 #if defined(OS_WIN) 686 user_agent += "WIN "; 687 #elif defined(OS_LINUX) 688 user_agent += "LINUX "; 689 #elif defined(OS_FREEBSD) 690 user_agent += "FREEBSD "; 691 #elif defined(OS_OPENBSD) 692 user_agent += "OPENBSD "; 693 #elif defined(OS_MACOSX) 694 user_agent += "MAC "; 695 #endif 696 chrome::VersionInfo version_info; 697 if (!version_info.is_valid()) { 698 DLOG(ERROR) << "Unable to create chrome::VersionInfo object"; 699 return user_agent; 700 } 701 702 user_agent += version_info.Version(); 703 user_agent += " (" + version_info.LastChange() + ")"; 704 if (!version_info.IsOfficialBuild()) 705 user_agent += "-devel"; 706 return user_agent; 707 } 708 709 void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) { 710 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); 711 processing_passphrase_ = false; 712 713 // Blow away the partial or corrupt sync data folder before doing any more 714 // initialization, if necessary. 715 if (options.delete_sync_data_folder) { 716 DeleteSyncDataFolder(); 717 } 718 719 // Make sure that the directory exists before initializing the backend. 720 // If it already exists, this will do no harm. 721 bool success = file_util::CreateDirectory(host_->sync_data_folder_path()); 722 DCHECK(success); 723 724 syncapi_->AddObserver(this); 725 const FilePath& path_str = host_->sync_data_folder_path(); 726 success = syncapi_->Init( 727 path_str, 728 (options.service_url.host() + options.service_url.path()).c_str(), 729 options.service_url.EffectiveIntPort(), 730 options.service_url.SchemeIsSecure(), 731 options.http_bridge_factory, 732 host_, // ModelSafeWorkerRegistrar. 733 MakeUserAgentForSyncapi().c_str(), 734 options.credentials, 735 sync_notifier_.get(), 736 options.restored_key_for_bootstrapping, 737 options.setup_for_test_mode); 738 DCHECK(success) << "Syncapi initialization failed!"; 739 } 740 741 void SyncBackendHost::Core::DoUpdateCredentials( 742 const SyncCredentials& credentials) { 743 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); 744 syncapi_->UpdateCredentials(credentials); 745 } 746 747 void SyncBackendHost::Core::DoUpdateEnabledTypes() { 748 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); 749 syncapi_->UpdateEnabledTypes(); 750 } 751 752 void SyncBackendHost::Core::DoStartSyncing() { 753 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); 754 syncapi_->StartSyncing(); 755 if (deferred_nudge_for_cleanup_requested_) 756 syncapi_->RequestNudge(FROM_HERE); 757 deferred_nudge_for_cleanup_requested_ = false; 758 } 759 760 void SyncBackendHost::Core::DoSetPassphrase(const std::string& passphrase, 761 bool is_explicit) { 762 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); 763 syncapi_->SetPassphrase(passphrase, is_explicit); 764 } 765 766 bool SyncBackendHost::Core::processing_passphrase() const { 767 DCHECK(MessageLoop::current() == host_->frontend_loop_); 768 return processing_passphrase_; 769 } 770 771 void SyncBackendHost::Core::set_processing_passphrase() { 772 DCHECK(MessageLoop::current() == host_->frontend_loop_); 773 processing_passphrase_ = true; 774 } 775 776 void SyncBackendHost::Core::DoEncryptDataTypes( 777 const syncable::ModelTypeSet& encrypted_types) { 778 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); 779 syncapi_->EncryptDataTypes(encrypted_types); 780 } 781 782 void SyncBackendHost::Core::DoRequestConfig( 783 const syncable::ModelTypeBitSet& added_types) { 784 syncapi_->RequestConfig(added_types); 785 } 786 787 void SyncBackendHost::Core::DoStartConfiguration(Callback0::Type* callback) { 788 syncapi_->StartConfigurationMode(callback); 789 } 790 791 UIModelWorker* SyncBackendHost::ui_worker() { 792 ModelSafeWorker* w = registrar_.workers[GROUP_UI]; 793 if (w == NULL) 794 return NULL; 795 if (w->GetModelSafeGroup() != GROUP_UI) 796 NOTREACHED(); 797 return static_cast<UIModelWorker*>(w); 798 } 799 800 void SyncBackendHost::Core::DoShutdown(bool sync_disabled) { 801 DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); 802 803 save_changes_timer_.Stop(); 804 syncapi_->Shutdown(); // Stops the SyncerThread. 805 syncapi_->RemoveObserver(this); 806 DisconnectChildJsEventRouter(); 807 host_->ui_worker()->OnSyncerShutdownComplete(); 808 809 if (sync_disabled) 810 DeleteSyncDataFolder(); 811 812 host_ = NULL; 813 } 814 815 ChangeProcessor* SyncBackendHost::Core::GetProcessor( 816 syncable::ModelType model_type) { 817 std::map<syncable::ModelType, ChangeProcessor*>::const_iterator it = 818 host_->processors_.find(model_type); 819 820 // Until model association happens for a datatype, it will not appear in 821 // the processors list. During this time, it is OK to drop changes on 822 // the floor (since model association has not happened yet). When the 823 // data type is activated, model association takes place then the change 824 // processor is added to the processors_ list. This all happens on 825 // the UI thread so we will never drop any changes after model 826 // association. 827 if (it == host_->processors_.end()) 828 return NULL; 829 830 if (!IsCurrentThreadSafeForModel(model_type)) { 831 NOTREACHED() << "Changes applied on wrong thread."; 832 return NULL; 833 } 834 835 // Now that we're sure we're on the correct thread, we can access the 836 // ChangeProcessor. 837 ChangeProcessor* processor = it->second; 838 839 // Ensure the change processor is willing to accept changes. 840 if (!processor->IsRunning()) 841 return NULL; 842 843 return processor; 844 } 845 846 void SyncBackendHost::Core::OnChangesApplied( 847 syncable::ModelType model_type, 848 const sync_api::BaseTransaction* trans, 849 const sync_api::SyncManager::ChangeRecord* changes, 850 int change_count) { 851 if (!host_ || !host_->frontend_) { 852 DCHECK(false) << "OnChangesApplied called after Shutdown?"; 853 return; 854 } 855 ChangeProcessor* processor = GetProcessor(model_type); 856 if (!processor) 857 return; 858 859 processor->ApplyChangesFromSyncModel(trans, changes, change_count); 860 } 861 862 void SyncBackendHost::Core::OnChangesComplete( 863 syncable::ModelType model_type) { 864 if (!host_ || !host_->frontend_) { 865 DCHECK(false) << "OnChangesComplete called after Shutdown?"; 866 return; 867 } 868 869 ChangeProcessor* processor = GetProcessor(model_type); 870 if (!processor) 871 return; 872 873 // This call just notifies the processor that it can commit, it already 874 // buffered any changes it plans to makes so needs no further information. 875 processor->CommitChangesFromSyncModel(); 876 } 877 878 879 void SyncBackendHost::Core::OnSyncCycleCompleted( 880 const SyncSessionSnapshot* snapshot) { 881 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 882 &Core::HandleSyncCycleCompletedOnFrontendLoop, 883 new SyncSessionSnapshot(*snapshot))); 884 } 885 886 void SyncBackendHost::Core::HandleSyncCycleCompletedOnFrontendLoop( 887 SyncSessionSnapshot* snapshot) { 888 if (!host_ || !host_->frontend_) 889 return; 890 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 891 892 host_->last_snapshot_.reset(snapshot); 893 894 const syncable::ModelTypeSet& to_migrate = 895 snapshot->syncer_status.types_needing_local_migration; 896 if (!to_migrate.empty()) 897 host_->frontend_->OnMigrationNeededForTypes(to_migrate); 898 899 // If we are waiting for a configuration change, check here to see 900 // if this sync cycle has initialized all of the types we've been 901 // waiting for. 902 if (host_->pending_download_state_.get()) { 903 bool found_all_added = true; 904 for (syncable::ModelTypeSet::const_iterator it = 905 host_->pending_download_state_->initial_types.begin(); 906 it != host_->pending_download_state_->initial_types.end(); 907 ++it) { 908 if (host_->pending_download_state_->added_types.test(*it)) 909 found_all_added &= snapshot->initial_sync_ended.test(*it); 910 } 911 if (!found_all_added) { 912 NOTREACHED() << "Update didn't return updates for all types requested."; 913 } else { 914 host_->pending_download_state_->ready_task->Run(); 915 } 916 host_->pending_download_state_.reset(); 917 } 918 host_->frontend_->OnSyncCycleCompleted(); 919 } 920 921 void SyncBackendHost::Core::OnInitializationComplete() { 922 if (!host_ || !host_->frontend_) 923 return; // We may have been told to Shutdown before initialization 924 // completed. 925 926 // We could be on some random sync backend thread, so MessageLoop::current() 927 // can definitely be null in here. 928 host_->frontend_loop_->PostTask(FROM_HERE, 929 NewRunnableMethod(this, 930 &Core::HandleInitalizationCompletedOnFrontendLoop)); 931 932 // Initialization is complete, so we can schedule recurring SaveChanges. 933 host_->core_thread_.message_loop()->PostTask(FROM_HERE, 934 NewRunnableMethod(this, &Core::StartSavingChanges)); 935 } 936 937 void SyncBackendHost::Core::HandleInitalizationCompletedOnFrontendLoop() { 938 if (!host_) 939 return; 940 host_->HandleInitializationCompletedOnFrontendLoop(); 941 } 942 943 void SyncBackendHost::HandleInitializationCompletedOnFrontendLoop() { 944 if (!frontend_) 945 return; 946 syncapi_initialized_ = true; 947 frontend_->OnBackendInitialized(); 948 } 949 950 bool SyncBackendHost::Core::IsCurrentThreadSafeForModel( 951 syncable::ModelType model_type) { 952 base::AutoLock lock(host_->registrar_lock_); 953 954 browser_sync::ModelSafeRoutingInfo::const_iterator routing_it = 955 host_->registrar_.routing_info.find(model_type); 956 if (routing_it == host_->registrar_.routing_info.end()) 957 return false; 958 browser_sync::ModelSafeGroup group = routing_it->second; 959 WorkerMap::const_iterator worker_it = host_->registrar_.workers.find(group); 960 if (worker_it == host_->registrar_.workers.end()) 961 return false; 962 ModelSafeWorker* worker = worker_it->second; 963 return worker->CurrentThreadIsWorkThread(); 964 } 965 966 void SyncBackendHost::Core::OnAuthError(const AuthError& auth_error) { 967 // Post to our core loop so we can modify state. Could be on another thread. 968 host_->frontend_loop_->PostTask(FROM_HERE, 969 NewRunnableMethod(this, &Core::HandleAuthErrorEventOnFrontendLoop, 970 auth_error)); 971 } 972 973 void SyncBackendHost::Core::OnPassphraseRequired(bool for_decryption) { 974 host_->frontend_loop_->PostTask(FROM_HERE, 975 NewRunnableMethod(this, &Core::NotifyPassphraseRequired, for_decryption)); 976 } 977 978 void SyncBackendHost::Core::OnPassphraseFailed() { 979 host_->frontend_loop_->PostTask(FROM_HERE, 980 NewRunnableMethod(this, &Core::NotifyPassphraseFailed)); 981 } 982 983 void SyncBackendHost::Core::OnPassphraseAccepted( 984 const std::string& bootstrap_token) { 985 host_->frontend_loop_->PostTask(FROM_HERE, 986 NewRunnableMethod(this, &Core::NotifyPassphraseAccepted, 987 bootstrap_token)); 988 } 989 990 void SyncBackendHost::Core::OnStopSyncingPermanently() { 991 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 992 &Core::HandleStopSyncingPermanentlyOnFrontendLoop)); 993 } 994 995 void SyncBackendHost::Core::OnUpdatedToken(const std::string& token) { 996 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 997 &Core::NotifyUpdatedToken, token)); 998 } 999 1000 void SyncBackendHost::Core::OnClearServerDataSucceeded() { 1001 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 1002 &Core::HandleClearServerDataSucceededOnFrontendLoop)); 1003 } 1004 1005 void SyncBackendHost::Core::OnClearServerDataFailed() { 1006 host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, 1007 &Core::HandleClearServerDataFailedOnFrontendLoop)); 1008 } 1009 1010 void SyncBackendHost::Core::OnEncryptionComplete( 1011 const syncable::ModelTypeSet& encrypted_types) { 1012 host_->frontend_loop_->PostTask( 1013 FROM_HERE, 1014 NewRunnableMethod(this, &Core::NotifyEncryptionComplete, 1015 encrypted_types)); 1016 } 1017 1018 void SyncBackendHost::Core::RouteJsEvent( 1019 const std::string& name, const JsArgList& args, 1020 const JsEventHandler* target) { 1021 host_->frontend_loop_->PostTask( 1022 FROM_HERE, NewRunnableMethod( 1023 this, &Core::RouteJsEventOnFrontendLoop, name, args, target)); 1024 } 1025 1026 void SyncBackendHost::Core::HandleStopSyncingPermanentlyOnFrontendLoop() { 1027 if (!host_ || !host_->frontend_) 1028 return; 1029 host_->frontend_->OnStopSyncingPermanently(); 1030 } 1031 1032 void SyncBackendHost::Core::HandleClearServerDataSucceededOnFrontendLoop() { 1033 if (!host_ || !host_->frontend_) 1034 return; 1035 host_->frontend_->OnClearServerDataSucceeded(); 1036 } 1037 1038 void SyncBackendHost::Core::HandleClearServerDataFailedOnFrontendLoop() { 1039 if (!host_ || !host_->frontend_) 1040 return; 1041 host_->frontend_->OnClearServerDataFailed(); 1042 } 1043 1044 void SyncBackendHost::Core::HandleAuthErrorEventOnFrontendLoop( 1045 const GoogleServiceAuthError& new_auth_error) { 1046 if (!host_ || !host_->frontend_) 1047 return; 1048 1049 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 1050 1051 host_->last_auth_error_ = new_auth_error; 1052 host_->frontend_->OnAuthError(); 1053 } 1054 1055 void SyncBackendHost::Core::RouteJsEventOnFrontendLoop( 1056 const std::string& name, const JsArgList& args, 1057 const JsEventHandler* target) { 1058 if (!host_ || !parent_router_) 1059 return; 1060 1061 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 1062 1063 parent_router_->RouteJsEvent(name, args, target); 1064 } 1065 1066 void SyncBackendHost::Core::StartSavingChanges() { 1067 save_changes_timer_.Start( 1068 base::TimeDelta::FromSeconds(kSaveChangesIntervalSeconds), 1069 this, &Core::SaveChanges); 1070 } 1071 1072 void SyncBackendHost::Core::DoRequestNudge( 1073 const tracked_objects::Location& nudge_location) { 1074 syncapi_->RequestNudge(nudge_location); 1075 } 1076 1077 void SyncBackendHost::Core::DoRequestClearServerData() { 1078 syncapi_->RequestClearServerData(); 1079 } 1080 1081 void SyncBackendHost::Core::SaveChanges() { 1082 syncapi_->SaveChanges(); 1083 } 1084 1085 void SyncBackendHost::Core::DeleteSyncDataFolder() { 1086 if (file_util::DirectoryExists(host_->sync_data_folder_path())) { 1087 if (!file_util::Delete(host_->sync_data_folder_path(), true)) 1088 LOG(DFATAL) << "Could not delete the Sync Data folder."; 1089 } 1090 } 1091 1092 void SyncBackendHost::Core::SetParentJsEventRouter(JsEventRouter* router) { 1093 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 1094 DCHECK(router); 1095 parent_router_ = router; 1096 MessageLoop* core_message_loop = host_->core_thread_.message_loop(); 1097 CHECK(core_message_loop); 1098 core_message_loop->PostTask( 1099 FROM_HERE, 1100 NewRunnableMethod(this, 1101 &SyncBackendHost::Core::ConnectChildJsEventRouter)); 1102 } 1103 1104 void SyncBackendHost::Core::RemoveParentJsEventRouter() { 1105 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 1106 parent_router_ = NULL; 1107 MessageLoop* core_message_loop = host_->core_thread_.message_loop(); 1108 CHECK(core_message_loop); 1109 core_message_loop->PostTask( 1110 FROM_HERE, 1111 NewRunnableMethod(this, 1112 &SyncBackendHost::Core::DisconnectChildJsEventRouter)); 1113 } 1114 1115 const JsEventRouter* SyncBackendHost::Core::GetParentJsEventRouter() const { 1116 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 1117 return parent_router_; 1118 } 1119 1120 void SyncBackendHost::Core::ProcessMessage( 1121 const std::string& name, const JsArgList& args, 1122 const JsEventHandler* sender) { 1123 DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); 1124 MessageLoop* core_message_loop = host_->core_thread_.message_loop(); 1125 CHECK(core_message_loop); 1126 core_message_loop->PostTask( 1127 FROM_HERE, 1128 NewRunnableMethod(this, 1129 &SyncBackendHost::Core::DoProcessMessage, 1130 name, args, sender)); 1131 } 1132 1133 void SyncBackendHost::Core::ConnectChildJsEventRouter() { 1134 DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop()); 1135 // We need this check since AddObserver() can be called at most once 1136 // for a given observer. 1137 if (!syncapi_->GetJsBackend()->GetParentJsEventRouter()) { 1138 syncapi_->GetJsBackend()->SetParentJsEventRouter(this); 1139 syncapi_->AddObserver(&sync_manager_observer_); 1140 } 1141 } 1142 1143 void SyncBackendHost::Core::DisconnectChildJsEventRouter() { 1144 DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop()); 1145 syncapi_->GetJsBackend()->RemoveParentJsEventRouter(); 1146 syncapi_->RemoveObserver(&sync_manager_observer_); 1147 } 1148 1149 void SyncBackendHost::Core::DoProcessMessage( 1150 const std::string& name, const JsArgList& args, 1151 const JsEventHandler* sender) { 1152 DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop()); 1153 syncapi_->GetJsBackend()->ProcessMessage(name, args, sender); 1154 } 1155 1156 void SyncBackendHost::Core::DeferNudgeForCleanup() { 1157 DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop()); 1158 deferred_nudge_for_cleanup_requested_ = true; 1159 } 1160 1161 } // namespace browser_sync 1162