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 "chrome/browser/sync/profile_sync_service.h" 6 7#include <stddef.h> 8#include <map> 9#include <ostream> 10#include <set> 11#include <utility> 12 13#include "base/basictypes.h" 14#include "base/command_line.h" 15#include "base/compiler_specific.h" 16#include "base/logging.h" 17#include "base/memory/ref_counted.h" 18#include "base/message_loop.h" 19#include "base/metrics/histogram.h" 20#include "base/string16.h" 21#include "base/stringprintf.h" 22#include "base/task.h" 23#include "base/threading/thread_restrictions.h" 24#include "chrome/browser/net/gaia/token_service.h" 25#include "chrome/browser/platform_util.h" 26#include "chrome/browser/prefs/pref_service.h" 27#include "chrome/browser/profiles/profile.h" 28#include "chrome/browser/sync/backend_migrator.h" 29#include "chrome/browser/sync/engine/syncapi.h" 30#include "chrome/browser/sync/glue/change_processor.h" 31#include "chrome/browser/sync/glue/data_type_controller.h" 32#include "chrome/browser/sync/glue/data_type_manager.h" 33#include "chrome/browser/sync/glue/session_data_type_controller.h" 34#include "chrome/browser/sync/js_arg_list.h" 35#include "chrome/browser/sync/profile_sync_factory.h" 36#include "chrome/browser/sync/signin_manager.h" 37#include "chrome/browser/ui/browser.h" 38#include "chrome/browser/ui/browser_list.h" 39#include "chrome/common/chrome_switches.h" 40#include "chrome/common/net/gaia/gaia_constants.h" 41#include "chrome/common/pref_names.h" 42#include "chrome/common/time_format.h" 43#include "chrome/common/url_constants.h" 44#include "content/common/notification_details.h" 45#include "content/common/notification_source.h" 46#include "content/common/notification_type.h" 47#include "grit/generated_resources.h" 48#include "ui/base/l10n/l10n_util.h" 49#include "ui/gfx/native_widget_types.h" 50 51using browser_sync::ChangeProcessor; 52using browser_sync::DataTypeController; 53using browser_sync::DataTypeManager; 54using browser_sync::SyncBackendHost; 55using sync_api::SyncCredentials; 56 57typedef GoogleServiceAuthError AuthError; 58 59const char* ProfileSyncService::kSyncServerUrl = 60 "https://clients4.google.com/chrome-sync"; 61 62const char* ProfileSyncService::kDevServerUrl = 63 "https://clients4.google.com/chrome-sync/dev"; 64 65static const int kSyncClearDataTimeoutInSeconds = 60; // 1 minute. 66 67ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory, 68 Profile* profile, 69 const std::string& cros_user) 70 : last_auth_error_(AuthError::None()), 71 observed_passphrase_required_(false), 72 passphrase_required_for_decryption_(false), 73 passphrase_migration_in_progress_(false), 74 factory_(factory), 75 profile_(profile), 76 cros_user_(cros_user), 77 sync_service_url_(kDevServerUrl), 78 backend_initialized_(false), 79 is_auth_in_progress_(false), 80 wizard_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), 81 unrecoverable_error_detected_(false), 82 scoped_runnable_method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), 83 expect_sync_configuration_aborted_(false), 84 clear_server_data_state_(CLEAR_NOT_STARTED) { 85 registrar_.Add(this, 86 NotificationType::SYNC_DATA_TYPES_UPDATED, 87 Source<Profile>(profile)); 88 89 // By default, dev & chromium users will go to the development servers. 90 // Dev servers have more features than standard sync servers. 91 // Chrome stable and beta builds will go to the standard sync servers. 92#if defined(GOOGLE_CHROME_BUILD) 93 // GetVersionStringModifier hits the registry. See http://crbug.com/70380. 94 base::ThreadRestrictions::ScopedAllowIO allow_io; 95 // For stable, this is "". For dev, this is "dev". For beta, this is "beta". 96 // For daily, this is "canary build". 97 // For Linux Chromium builds, this could be anything depending on the 98 // distribution, so always direct those users to dev server urls. 99 // If this is an official build, it will always be one of the above. 100 std::string channel = platform_util::GetVersionStringModifier(); 101 if (channel.empty() || channel == "beta") { 102 sync_service_url_ = GURL(kSyncServerUrl); 103 } 104#endif 105 106 tried_implicit_gaia_remove_when_bug_62103_fixed_ = false; 107} 108 109ProfileSyncService::~ProfileSyncService() { 110 Shutdown(false); 111} 112 113bool ProfileSyncService::AreCredentialsAvailable() { 114 if (IsManaged()) { 115 return false; 116 } 117 118 // CrOS user is always logged in. Chrome uses signin_ to check logged in. 119 if (!cros_user_.empty() || !signin_->GetUsername().empty()) { 120 // TODO(chron): Verify CrOS unit test behavior. 121 if (profile()->GetTokenService() && 122 profile()->GetTokenService()->HasTokenForService( 123 GaiaConstants::kSyncService)) { 124 return true; 125 } 126 } 127 return false; 128} 129 130void ProfileSyncService::Initialize() { 131 InitSettings(); 132 RegisterPreferences(); 133 134 // Watch the preference that indicates sync is managed so we can take 135 // appropriate action. 136 pref_sync_managed_.Init(prefs::kSyncManaged, profile_->GetPrefs(), this); 137 138 // For now, the only thing we can do through policy is to turn sync off. 139 if (IsManaged()) { 140 DisableForUser(); 141 return; 142 } 143 144 RegisterAuthNotifications(); 145 146 // In Chrome, we integrate a SigninManager which works with the sync 147 // setup wizard to kick off the TokenService. CrOS does its own plumbing 148 // for the TokenService. 149 if (cros_user_.empty()) { 150 // Will load tokens from DB and broadcast Token events after. 151 // Note: We rely on signin_ != NULL unless !cros_user_.empty(). 152 signin_.reset(new SigninManager()); 153 signin_->Initialize(profile_); 154 } 155 156 if (!HasSyncSetupCompleted()) { 157 DisableForUser(); // Clean up in case of previous crash / setup abort. 158 159 // Under ChromeOS, just autostart it anyway if creds are here and start 160 // is not being suppressed by preferences. 161 if (!cros_user_.empty() && 162 !profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart) && 163 AreCredentialsAvailable()) { 164 StartUp(); 165 } 166 } else if (AreCredentialsAvailable()) { 167 // If we have credentials and sync setup finished, autostart the backend. 168 // Note that if we haven't finished setting up sync, backend bring up will 169 // be done by the wizard. 170 StartUp(); 171 } 172} 173 174void ProfileSyncService::RegisterAuthNotifications() { 175 registrar_.Add(this, 176 NotificationType::TOKEN_AVAILABLE, 177 Source<TokenService>(profile_->GetTokenService())); 178 registrar_.Add(this, 179 NotificationType::TOKEN_LOADING_FINISHED, 180 Source<TokenService>(profile_->GetTokenService())); 181 registrar_.Add(this, 182 NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, 183 Source<Profile>(profile_)); 184 registrar_.Add(this, 185 NotificationType::GOOGLE_SIGNIN_FAILED, 186 Source<Profile>(profile_)); 187} 188 189void ProfileSyncService::RegisterDataTypeController( 190 DataTypeController* data_type_controller) { 191 DCHECK_EQ(data_type_controllers_.count(data_type_controller->type()), 0U); 192 data_type_controllers_[data_type_controller->type()] = 193 data_type_controller; 194} 195 196browser_sync::SessionModelAssociator* 197 ProfileSyncService::GetSessionModelAssociator() { 198 if (data_type_controllers_.find(syncable::SESSIONS) == 199 data_type_controllers_.end() || 200 data_type_controllers_.find(syncable::SESSIONS)->second->state() != 201 DataTypeController::RUNNING) { 202 return NULL; 203 } 204 return static_cast<browser_sync::SessionDataTypeController*>( 205 data_type_controllers_.find( 206 syncable::SESSIONS)->second.get())->GetModelAssociator(); 207} 208 209void ProfileSyncService::ResetClearServerDataState() { 210 clear_server_data_state_ = CLEAR_NOT_STARTED; 211} 212 213ProfileSyncService::ClearServerDataState 214 ProfileSyncService::GetClearServerDataState() { 215 return clear_server_data_state_; 216} 217 218void ProfileSyncService::GetDataTypeControllerStates( 219 browser_sync::DataTypeController::StateMap* state_map) const { 220 for (browser_sync::DataTypeController::TypeMap::const_iterator iter = 221 data_type_controllers_.begin(); iter != data_type_controllers_.end(); 222 ++iter) 223 (*state_map)[iter->first] = iter->second.get()->state(); 224} 225 226void ProfileSyncService::InitSettings() { 227 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 228 229 // Override the sync server URL from the command-line, if sync server 230 // command-line argument exists. 231 if (command_line.HasSwitch(switches::kSyncServiceURL)) { 232 std::string value(command_line.GetSwitchValueASCII( 233 switches::kSyncServiceURL)); 234 if (!value.empty()) { 235 GURL custom_sync_url(value); 236 if (custom_sync_url.is_valid()) { 237 sync_service_url_ = custom_sync_url; 238 } else { 239 LOG(WARNING) << "The following sync URL specified at the command-line " 240 << "is invalid: " << value; 241 } 242 } 243 } 244} 245 246void ProfileSyncService::RegisterPreferences() { 247 PrefService* pref_service = profile_->GetPrefs(); 248 if (pref_service->FindPreference(prefs::kSyncLastSyncedTime)) 249 return; 250 pref_service->RegisterInt64Pref(prefs::kSyncLastSyncedTime, 0); 251 pref_service->RegisterBooleanPref(prefs::kSyncHasSetupCompleted, false); 252 pref_service->RegisterBooleanPref(prefs::kSyncSuppressStart, false); 253 254 // If you've never synced before, or if you're using Chrome OS, all datatypes 255 // are on by default. 256 // TODO(nick): Perhaps a better model would be to always default to false, 257 // and explicitly call SetDataTypes() when the user shows the wizard. 258#if defined(OS_CHROMEOS) 259 bool enable_by_default = true; 260#else 261 bool enable_by_default = 262 !pref_service->HasPrefPath(prefs::kSyncHasSetupCompleted); 263#endif 264 265 pref_service->RegisterBooleanPref(prefs::kSyncBookmarks, true); 266 pref_service->RegisterBooleanPref(prefs::kSyncPasswords, enable_by_default); 267 pref_service->RegisterBooleanPref(prefs::kSyncPreferences, enable_by_default); 268 pref_service->RegisterBooleanPref(prefs::kSyncAutofill, enable_by_default); 269 pref_service->RegisterBooleanPref(prefs::kSyncThemes, enable_by_default); 270 pref_service->RegisterBooleanPref(prefs::kSyncTypedUrls, enable_by_default); 271 pref_service->RegisterBooleanPref(prefs::kSyncExtensions, enable_by_default); 272 pref_service->RegisterBooleanPref(prefs::kSyncApps, enable_by_default); 273 pref_service->RegisterBooleanPref(prefs::kSyncSessions, enable_by_default); 274 pref_service->RegisterBooleanPref(prefs::kKeepEverythingSynced, 275 enable_by_default); 276 pref_service->RegisterBooleanPref(prefs::kSyncManaged, false); 277 pref_service->RegisterStringPref(prefs::kEncryptionBootstrapToken, ""); 278 279 pref_service->RegisterBooleanPref(prefs::kSyncAutofillProfile, 280 enable_by_default); 281} 282 283void ProfileSyncService::ClearPreferences() { 284 PrefService* pref_service = profile_->GetPrefs(); 285 pref_service->ClearPref(prefs::kSyncLastSyncedTime); 286 pref_service->ClearPref(prefs::kSyncHasSetupCompleted); 287 pref_service->ClearPref(prefs::kEncryptionBootstrapToken); 288 289 // TODO(nick): The current behavior does not clear e.g. prefs::kSyncBookmarks. 290 // Is that really what we want? 291 pref_service->ScheduleSavePersistentPrefs(); 292} 293 294SyncCredentials ProfileSyncService::GetCredentials() { 295 SyncCredentials credentials; 296 credentials.email = !cros_user_.empty() ? cros_user_ : signin_->GetUsername(); 297 DCHECK(!credentials.email.empty()); 298 TokenService* service = profile_->GetTokenService(); 299 credentials.sync_token = service->GetTokenForService( 300 GaiaConstants::kSyncService); 301 return credentials; 302} 303 304void ProfileSyncService::InitializeBackend(bool delete_sync_data_folder) { 305 if (!backend_.get()) { 306 NOTREACHED(); 307 return; 308 } 309 310 syncable::ModelTypeSet types; 311 // If sync setup hasn't finished, we don't want to initialize routing info 312 // for any data types so that we don't download updates for types that the 313 // user chooses not to sync on the first DownloadUpdatesCommand. 314 if (HasSyncSetupCompleted()) { 315 GetPreferredDataTypes(&types); 316 } 317 318 SyncCredentials credentials = GetCredentials(); 319 320 backend_->Initialize(this, 321 sync_service_url_, 322 types, 323 profile_->GetRequestContext(), 324 credentials, 325 delete_sync_data_folder); 326} 327 328void ProfileSyncService::CreateBackend() { 329 backend_.reset(new SyncBackendHost(profile_)); 330} 331 332bool ProfileSyncService::IsEncryptedDatatypeEnabled() const { 333 return !encrypted_types_.empty(); 334} 335 336void ProfileSyncService::StartUp() { 337 // Don't start up multiple times. 338 if (backend_.get()) { 339 VLOG(1) << "Skipping bringing up backend host."; 340 return; 341 } 342 343 DCHECK(AreCredentialsAvailable()); 344 345 last_synced_time_ = base::Time::FromInternalValue( 346 profile_->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime)); 347 348 CreateBackend(); 349 350 // Initialize the backend. Every time we start up a new SyncBackendHost, 351 // we'll want to start from a fresh SyncDB, so delete any old one that might 352 // be there. 353 InitializeBackend(!HasSyncSetupCompleted()); 354} 355 356void ProfileSyncService::Shutdown(bool sync_disabled) { 357 // Stop all data type controllers, if needed. 358 if (data_type_manager_.get()) { 359 if (data_type_manager_->state() != DataTypeManager::STOPPED) { 360 data_type_manager_->Stop(); 361 } 362 363 registrar_.Remove(this, 364 NotificationType::SYNC_CONFIGURE_START, 365 Source<DataTypeManager>(data_type_manager_.get())); 366 registrar_.Remove(this, 367 NotificationType::SYNC_CONFIGURE_DONE, 368 Source<DataTypeManager>(data_type_manager_.get())); 369 data_type_manager_.reset(); 370 } 371 372 js_event_handlers_.RemoveBackend(); 373 374 // Move aside the backend so nobody else tries to use it while we are 375 // shutting it down. 376 scoped_ptr<SyncBackendHost> doomed_backend(backend_.release()); 377 if (doomed_backend.get()) { 378 doomed_backend->Shutdown(sync_disabled); 379 380 doomed_backend.reset(); 381 } 382 383 // Clear various flags. 384 is_auth_in_progress_ = false; 385 backend_initialized_ = false; 386 observed_passphrase_required_ = false; 387 last_attempted_user_email_.clear(); 388 last_auth_error_ = GoogleServiceAuthError::None(); 389} 390 391void ProfileSyncService::ClearServerData() { 392 clear_server_data_state_ = CLEAR_CLEARING; 393 clear_server_data_timer_.Start( 394 base::TimeDelta::FromSeconds(kSyncClearDataTimeoutInSeconds), this, 395 &ProfileSyncService::OnClearServerDataTimeout); 396 backend_->RequestClearServerData(); 397} 398 399void ProfileSyncService::DisableForUser() { 400 // Clear prefs (including SyncSetupHasCompleted) before shutting down so 401 // PSS clients don't think we're set up while we're shutting down. 402 ClearPreferences(); 403 Shutdown(true); 404 405 if (signin_.get()) { 406 signin_->SignOut(); 407 } 408 409 NotifyObservers(); 410} 411 412bool ProfileSyncService::HasSyncSetupCompleted() const { 413 return profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted); 414} 415 416void ProfileSyncService::SetSyncSetupCompleted() { 417 PrefService* prefs = profile()->GetPrefs(); 418 prefs->SetBoolean(prefs::kSyncHasSetupCompleted, true); 419 prefs->SetBoolean(prefs::kSyncSuppressStart, false); 420 421 prefs->ScheduleSavePersistentPrefs(); 422} 423 424void ProfileSyncService::UpdateLastSyncedTime() { 425 last_synced_time_ = base::Time::Now(); 426 profile_->GetPrefs()->SetInt64(prefs::kSyncLastSyncedTime, 427 last_synced_time_.ToInternalValue()); 428 profile_->GetPrefs()->ScheduleSavePersistentPrefs(); 429} 430 431void ProfileSyncService::NotifyObservers() { 432 FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); 433 // TODO(akalin): Make an Observer subclass that listens and does the 434 // event routing. 435 js_event_handlers_.RouteJsEvent( 436 "onSyncServiceStateChanged", browser_sync::JsArgList(), NULL); 437} 438 439// static 440const char* ProfileSyncService::GetPrefNameForDataType( 441 syncable::ModelType data_type) { 442 switch (data_type) { 443 case syncable::BOOKMARKS: 444 return prefs::kSyncBookmarks; 445 case syncable::PASSWORDS: 446 return prefs::kSyncPasswords; 447 case syncable::PREFERENCES: 448 return prefs::kSyncPreferences; 449 case syncable::AUTOFILL: 450 return prefs::kSyncAutofill; 451 case syncable::AUTOFILL_PROFILE: 452 return prefs::kSyncAutofillProfile; 453 case syncable::THEMES: 454 return prefs::kSyncThemes; 455 case syncable::TYPED_URLS: 456 return prefs::kSyncTypedUrls; 457 case syncable::EXTENSIONS: 458 return prefs::kSyncExtensions; 459 case syncable::APPS: 460 return prefs::kSyncApps; 461 case syncable::SESSIONS: 462 return prefs::kSyncSessions; 463 default: 464 break; 465 } 466 NOTREACHED(); 467 return NULL; 468} 469 470// An invariant has been violated. Transition to an error state where we try 471// to do as little work as possible, to avoid further corruption or crashes. 472void ProfileSyncService::OnUnrecoverableError( 473 const tracked_objects::Location& from_here, 474 const std::string& message) { 475 unrecoverable_error_detected_ = true; 476 unrecoverable_error_message_ = message; 477 unrecoverable_error_location_.reset( 478 new tracked_objects::Location(from_here.function_name(), 479 from_here.file_name(), 480 from_here.line_number())); 481 482 // Tell the wizard so it can inform the user only if it is already open. 483 wizard_.Step(SyncSetupWizard::FATAL_ERROR); 484 485 NotifyObservers(); 486 LOG(ERROR) << "Unrecoverable error detected -- ProfileSyncService unusable." 487 << message; 488 std::string location; 489 from_here.Write(true, true, &location); 490 LOG(ERROR) << location; 491 492 // Shut all data types down. 493 MessageLoop::current()->PostTask(FROM_HERE, 494 scoped_runnable_method_factory_.NewRunnableMethod( 495 &ProfileSyncService::Shutdown, true)); 496} 497 498void ProfileSyncService::OnBackendInitialized() { 499 backend_initialized_ = true; 500 501 js_event_handlers_.SetBackend(backend_->GetJsBackend()); 502 503 // The very first time the backend initializes is effectively the first time 504 // we can say we successfully "synced". last_synced_time_ will only be null 505 // in this case, because the pref wasn't restored on StartUp. 506 if (last_synced_time_.is_null()) { 507 UpdateLastSyncedTime(); 508 } 509 NotifyObservers(); 510 511 if (!cros_user_.empty()) { 512 if (profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) { 513 ShowConfigure(NULL, true); 514 } else { 515 SetSyncSetupCompleted(); 516 } 517 } 518 519 if (HasSyncSetupCompleted()) { 520 ConfigureDataTypeManager(); 521 } 522} 523 524void ProfileSyncService::OnSyncCycleCompleted() { 525 UpdateLastSyncedTime(); 526 VLOG(2) << "Notifying observers sync cycle completed"; 527 NotifyObservers(); 528} 529 530void ProfileSyncService::UpdateAuthErrorState( 531 const GoogleServiceAuthError& error) { 532 last_auth_error_ = error; 533 // Protect against the in-your-face dialogs that pop out of nowhere. 534 // Require the user to click somewhere to run the setup wizard in the case 535 // of a steady-state auth failure. 536 if (WizardIsVisible()) { 537 wizard_.Step(AuthError::NONE == last_auth_error_.state() ? 538 SyncSetupWizard::GAIA_SUCCESS : SyncSetupWizard::GAIA_LOGIN); 539 } else { 540 auth_error_time_ = base::TimeTicks::Now(); 541 } 542 543 if (!auth_start_time_.is_null()) { 544 UMA_HISTOGRAM_TIMES("Sync.AuthorizationTimeInNetwork", 545 base::TimeTicks::Now() - auth_start_time_); 546 auth_start_time_ = base::TimeTicks(); 547 } 548 549 is_auth_in_progress_ = false; 550 // Fan the notification out to interested UI-thread components. 551 NotifyObservers(); 552} 553 554void ProfileSyncService::OnAuthError() { 555 UpdateAuthErrorState(backend_->GetAuthError()); 556} 557 558void ProfileSyncService::OnStopSyncingPermanently() { 559 if (SetupInProgress()) { 560 wizard_.Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR); 561 expect_sync_configuration_aborted_ = true; 562 } 563 profile_->GetPrefs()->SetBoolean(prefs::kSyncSuppressStart, true); 564 DisableForUser(); 565} 566 567void ProfileSyncService::OnClearServerDataTimeout() { 568 if (clear_server_data_state_ != CLEAR_SUCCEEDED && 569 clear_server_data_state_ != CLEAR_FAILED) { 570 clear_server_data_state_ = CLEAR_FAILED; 571 NotifyObservers(); 572 } 573} 574 575void ProfileSyncService::OnClearServerDataFailed() { 576 clear_server_data_timer_.Stop(); 577 578 // Only once clear has succeeded there is no longer a need to transition to 579 // a failed state as sync is disabled locally. Also, no need to fire off 580 // the observers if the state didn't change (i.e. it was FAILED before). 581 if (clear_server_data_state_ != CLEAR_SUCCEEDED && 582 clear_server_data_state_ != CLEAR_FAILED) { 583 clear_server_data_state_ = CLEAR_FAILED; 584 NotifyObservers(); 585 } 586} 587 588void ProfileSyncService::OnClearServerDataSucceeded() { 589 clear_server_data_timer_.Stop(); 590 591 // Even if the timout fired, we still transition to the succeeded state as 592 // we want UI to update itself and no longer allow the user to press "clear" 593 if (clear_server_data_state_ != CLEAR_SUCCEEDED) { 594 clear_server_data_state_ = CLEAR_SUCCEEDED; 595 NotifyObservers(); 596 } 597} 598 599void ProfileSyncService::OnPassphraseRequired(bool for_decryption) { 600 DCHECK(backend_.get()); 601 DCHECK(backend_->IsNigoriEnabled()); 602 603 // TODO(lipalani) : add this check to other locations as well. 604 if (unrecoverable_error_detected_) { 605 // When unrecoverable error is detected we post a task to shutdown the 606 // backend. The task might not have executed yet. 607 return; 608 } 609 observed_passphrase_required_ = true; 610 passphrase_required_for_decryption_ = for_decryption; 611 612 // First try supplying gaia password as the passphrase. 613 if (!gaia_password_.empty()) { 614 SetPassphrase(gaia_password_, false, true); 615 gaia_password_ = std::string(); 616 return; 617 } 618 619 // If the above failed then try the custom passphrase the user might have 620 // entered in setup. 621 if (!cached_passphrase_.value.empty()) { 622 SetPassphrase(cached_passphrase_.value, 623 cached_passphrase_.is_explicit, 624 cached_passphrase_.is_creation); 625 cached_passphrase_ = CachedPassphrase(); 626 return; 627 } 628 629 // We will skip the passphrase prompt and suppress the warning 630 // if the passphrase is needed for decryption but the user is 631 // not syncing an encrypted data type on this machine. 632 // Otherwise we prompt. 633 if (!IsEncryptedDatatypeEnabled() && for_decryption) { 634 OnPassphraseAccepted(); 635 return; 636 } 637 638 if (WizardIsVisible() && for_decryption) { 639 wizard_.Step(SyncSetupWizard::ENTER_PASSPHRASE); 640 } 641 642 NotifyObservers(); 643} 644 645void ProfileSyncService::OnPassphraseAccepted() { 646 // Make sure the data types that depend on the passphrase are started at 647 // this time. 648 syncable::ModelTypeSet types; 649 GetPreferredDataTypes(&types); 650 // Reset "passphrase_required" flag before configuring the DataTypeManager 651 // since we know we no longer require the passphrase. 652 observed_passphrase_required_ = false; 653 if (data_type_manager_.get()) 654 data_type_manager_->Configure(types); 655 656 NotifyObservers(); 657 658 wizard_.Step(SyncSetupWizard::DONE); 659} 660 661void ProfileSyncService::OnEncryptionComplete( 662 const syncable::ModelTypeSet& encrypted_types) { 663 if (encrypted_types_ != encrypted_types) { 664 encrypted_types_ = encrypted_types; 665 NotifyObservers(); 666 } 667} 668 669void ProfileSyncService::OnMigrationNeededForTypes( 670 const syncable::ModelTypeSet& types) { 671 DCHECK(backend_initialized_); 672 DCHECK(data_type_manager_.get()); 673 674 // Migrator must be valid, because we don't sync until it is created and this 675 // callback originates from a sync cycle. 676 migrator_->MigrateTypes(types); 677} 678 679void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) { 680 if (WizardIsVisible()) { 681 wizard_.Focus(); 682 // Force the wizard to step to the login screen (which will only actually 683 // happen if the transition is valid). 684 wizard_.Step(SyncSetupWizard::GAIA_LOGIN); 685 return; 686 } 687 688 if (!auth_error_time_.is_null()) { 689 UMA_HISTOGRAM_LONG_TIMES("Sync.ReauthorizationTime", 690 base::TimeTicks::Now() - auth_error_time_); 691 auth_error_time_ = base::TimeTicks(); // Reset auth_error_time_ to null. 692 } 693 694 wizard_.Step(SyncSetupWizard::GAIA_LOGIN); 695 696 NotifyObservers(); 697} 698 699void ProfileSyncService::ShowErrorUI(gfx::NativeWindow parent_window) { 700 if (observed_passphrase_required()) { 701 if (IsUsingSecondaryPassphrase()) 702 PromptForExistingPassphrase(parent_window); 703 else 704 SigninForPassphraseMigration(parent_window); 705 return; 706 } 707 const GoogleServiceAuthError& error = GetAuthError(); 708 if (error.state() == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS || 709 error.state() == GoogleServiceAuthError::CAPTCHA_REQUIRED || 710 error.state() == GoogleServiceAuthError::ACCOUNT_DELETED || 711 error.state() == GoogleServiceAuthError::ACCOUNT_DISABLED || 712 error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE) { 713 ShowLoginDialog(parent_window); 714 } 715} 716 717 718void ProfileSyncService::ShowConfigure( 719 gfx::NativeWindow parent_window, bool sync_everything) { 720 if (WizardIsVisible()) { 721 wizard_.Focus(); 722 return; 723 } 724 725 if (sync_everything) 726 wizard_.Step(SyncSetupWizard::SYNC_EVERYTHING); 727 else 728 wizard_.Step(SyncSetupWizard::CONFIGURE); 729} 730 731void ProfileSyncService::PromptForExistingPassphrase( 732 gfx::NativeWindow parent_window) { 733 if (WizardIsVisible()) { 734 wizard_.Focus(); 735 return; 736 } 737 738 wizard_.Step(SyncSetupWizard::ENTER_PASSPHRASE); 739} 740 741void ProfileSyncService::SigninForPassphraseMigration( 742 gfx::NativeWindow parent_window) { 743 passphrase_migration_in_progress_ = true; 744 ShowLoginDialog(parent_window); 745} 746 747SyncBackendHost::StatusSummary ProfileSyncService::QuerySyncStatusSummary() { 748 if (backend_.get() && backend_initialized_) 749 return backend_->GetStatusSummary(); 750 else 751 return SyncBackendHost::Status::OFFLINE_UNUSABLE; 752} 753 754SyncBackendHost::Status ProfileSyncService::QueryDetailedSyncStatus() { 755 if (backend_.get() && backend_initialized_) { 756 return backend_->GetDetailedStatus(); 757 } else { 758 SyncBackendHost::Status status = 759 { SyncBackendHost::Status::OFFLINE_UNUSABLE }; 760 return status; 761 } 762} 763 764bool ProfileSyncService::SetupInProgress() const { 765 return !HasSyncSetupCompleted() && WizardIsVisible(); 766} 767 768std::string ProfileSyncService::BuildSyncStatusSummaryText( 769 const sync_api::SyncManager::Status::Summary& summary) { 770 const char* strings[] = {"INVALID", "OFFLINE", "OFFLINE_UNSYNCED", "SYNCING", 771 "READY", "CONFLICT", "OFFLINE_UNUSABLE"}; 772 COMPILE_ASSERT(arraysize(strings) == 773 sync_api::SyncManager::Status::SUMMARY_STATUS_COUNT, 774 enum_indexed_array); 775 if (summary < 0 || 776 summary >= sync_api::SyncManager::Status::SUMMARY_STATUS_COUNT) { 777 LOG(DFATAL) << "Illegal Summary Value: " << summary; 778 return "UNKNOWN"; 779 } 780 return strings[summary]; 781} 782 783bool ProfileSyncService::unrecoverable_error_detected() const { 784 return unrecoverable_error_detected_; 785} 786 787string16 ProfileSyncService::GetLastSyncedTimeString() const { 788 if (last_synced_time_.is_null()) 789 return l10n_util::GetStringUTF16(IDS_SYNC_TIME_NEVER); 790 791 base::TimeDelta last_synced = base::Time::Now() - last_synced_time_; 792 793 if (last_synced < base::TimeDelta::FromMinutes(1)) 794 return l10n_util::GetStringUTF16(IDS_SYNC_TIME_JUST_NOW); 795 796 return TimeFormat::TimeElapsed(last_synced); 797} 798 799string16 ProfileSyncService::GetAuthenticatedUsername() const { 800 if (backend_.get() && backend_initialized_) 801 return backend_->GetAuthenticatedUsername(); 802 else 803 return string16(); 804} 805 806void ProfileSyncService::OnUserSubmittedAuth( 807 const std::string& username, const std::string& password, 808 const std::string& captcha, const std::string& access_code) { 809 last_attempted_user_email_ = username; 810 is_auth_in_progress_ = true; 811 NotifyObservers(); 812 813 auth_start_time_ = base::TimeTicks::Now(); 814 815 if (!signin_.get()) { 816 // In ChromeOS we sign in during login, so we do not instantiate signin_. 817 // If this function gets called, we need to re-authenticate (e.g. for 818 // two factor signin), so instantiate signin_ here. 819 signin_.reset(new SigninManager()); 820 signin_->Initialize(profile_); 821 } 822 823 if (!access_code.empty()) { 824 signin_->ProvideSecondFactorAccessCode(access_code); 825 return; 826 } 827 828 if (!signin_->GetUsername().empty()) { 829 signin_->SignOut(); 830 } 831 832 // The user has submitted credentials, which indicates they don't 833 // want to suppress start up anymore. 834 PrefService* prefs = profile_->GetPrefs(); 835 prefs->SetBoolean(prefs::kSyncSuppressStart, false); 836 prefs->ScheduleSavePersistentPrefs(); 837 838 signin_->StartSignIn(username, 839 password, 840 last_auth_error_.captcha().token, 841 captcha); 842} 843 844void ProfileSyncService::OnUserChoseDatatypes(bool sync_everything, 845 const syncable::ModelTypeSet& chosen_types) { 846 if (!backend_.get()) { 847 NOTREACHED(); 848 return; 849 } 850 851 profile_->GetPrefs()->SetBoolean(prefs::kKeepEverythingSynced, 852 sync_everything); 853 854 ChangePreferredDataTypes(chosen_types); 855 profile_->GetPrefs()->ScheduleSavePersistentPrefs(); 856} 857 858void ProfileSyncService::OnUserCancelledDialog() { 859 if (!HasSyncSetupCompleted()) { 860 // A sync dialog was aborted before authentication. 861 // Rollback. 862 expect_sync_configuration_aborted_ = true; 863 DisableForUser(); 864 } 865 866 // Though an auth could still be in progress, once the dialog is closed we 867 // don't want the UI to stay stuck in the "waiting for authentication" state 868 // as that could take forever. We set this to false so the buttons to re- 869 // login will appear until either a) the original request finishes and 870 // succeeds, calling OnAuthError(NONE), or b) the user clicks the button, 871 // and tries to re-authenticate. (b) is a little awkward as this second 872 // request will get queued behind the first and could wind up "undoing" the 873 // good if invalid creds were provided, but it's an edge case and the user 874 // can of course get themselves out of it. 875 is_auth_in_progress_ = false; 876 NotifyObservers(); 877} 878 879void ProfileSyncService::ChangePreferredDataTypes( 880 const syncable::ModelTypeSet& preferred_types) { 881 882 // Filter out any datatypes which aren't registered, or for which 883 // the preference can't be set. 884 syncable::ModelTypeSet registered_types; 885 GetRegisteredDataTypes(®istered_types); 886 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { 887 syncable::ModelType model_type = syncable::ModelTypeFromInt(i); 888 if (!registered_types.count(model_type)) 889 continue; 890 const char* pref_name = GetPrefNameForDataType(model_type); 891 if (!pref_name) 892 continue; 893 profile_->GetPrefs()->SetBoolean(pref_name, 894 preferred_types.count(model_type) != 0); 895 if (syncable::AUTOFILL == model_type) { 896 profile_->GetPrefs()->SetBoolean(prefs::kSyncAutofillProfile, 897 preferred_types.count(model_type) != 0); 898 } 899 } 900 901 // If we haven't initialized yet, don't configure the DTM as it could cause 902 // association to start before a Directory has even been created. 903 if (backend_initialized_) 904 ConfigureDataTypeManager(); 905} 906 907void ProfileSyncService::GetPreferredDataTypes( 908 syncable::ModelTypeSet* preferred_types) const { 909 preferred_types->clear(); 910 if (profile_->GetPrefs()->GetBoolean(prefs::kKeepEverythingSynced)) { 911 GetRegisteredDataTypes(preferred_types); 912 } else { 913 // Filter out any datatypes which aren't registered, or for which 914 // the preference can't be read. 915 syncable::ModelTypeSet registered_types; 916 GetRegisteredDataTypes(®istered_types); 917 for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) { 918 syncable::ModelType model_type = syncable::ModelTypeFromInt(i); 919 if (!registered_types.count(model_type)) 920 continue; 921 if (model_type == syncable::AUTOFILL_PROFILE) 922 continue; 923 const char* pref_name = GetPrefNameForDataType(model_type); 924 if (!pref_name) 925 continue; 926 927 // We are trying to group autofill_profile tag with the same 928 // enabled/disabled state as autofill. Because the UI only shows autofill. 929 if (profile_->GetPrefs()->GetBoolean(pref_name)) { 930 preferred_types->insert(model_type); 931 if (model_type == syncable::AUTOFILL) { 932 if (!registered_types.count(syncable::AUTOFILL_PROFILE)) 933 continue; 934 preferred_types->insert(syncable::AUTOFILL_PROFILE); 935 } 936 } 937 } 938 } 939} 940 941void ProfileSyncService::GetRegisteredDataTypes( 942 syncable::ModelTypeSet* registered_types) const { 943 registered_types->clear(); 944 // The data_type_controllers_ are determined by command-line flags; that's 945 // effectively what controls the values returned here. 946 for (DataTypeController::TypeMap::const_iterator it = 947 data_type_controllers_.begin(); 948 it != data_type_controllers_.end(); ++it) { 949 registered_types->insert((*it).first); 950 } 951} 952 953bool ProfileSyncService::IsUsingSecondaryPassphrase() const { 954 return backend_.get() && (backend_->IsUsingExplicitPassphrase() || 955 (tried_implicit_gaia_remove_when_bug_62103_fixed_ && 956 observed_passphrase_required_)); 957} 958 959bool ProfileSyncService::IsCryptographerReady( 960 const sync_api::BaseTransaction* trans) const { 961 return backend_.get() && backend_->IsCryptographerReady(trans); 962} 963 964SyncBackendHost* ProfileSyncService::GetBackendForTest() { 965 // We don't check |backend_initialized_|; we assume the test class 966 // knows what it's doing. 967 return backend_.get(); 968} 969 970void ProfileSyncService::ConfigureDataTypeManager() { 971 if (!data_type_manager_.get()) { 972 data_type_manager_.reset( 973 factory_->CreateDataTypeManager(backend_.get(), 974 data_type_controllers_)); 975 registrar_.Add(this, 976 NotificationType::SYNC_CONFIGURE_START, 977 Source<DataTypeManager>(data_type_manager_.get())); 978 registrar_.Add(this, 979 NotificationType::SYNC_CONFIGURE_DONE, 980 Source<DataTypeManager>(data_type_manager_.get())); 981 982 // We create the migrator at the same time. 983 migrator_.reset( 984 new browser_sync::BackendMigrator(this, data_type_manager_.get())); 985 } 986 987 syncable::ModelTypeSet types; 988 GetPreferredDataTypes(&types); 989 // We set this special case here since it's the only datatype whose encryption 990 // status we already know. All others are set after the initial sync 991 // completes (for now). 992 // TODO(zea): Implement a better way that uses preferences for which types 993 // need encryption. 994 encrypted_types_.clear(); 995 if (types.count(syncable::PASSWORDS) > 0) 996 encrypted_types_.insert(syncable::PASSWORDS); 997 if (observed_passphrase_required_ && passphrase_required_for_decryption_) { 998 if (IsEncryptedDatatypeEnabled()) { 999 // We need a passphrase still. Prompt the user for a passphrase, and 1000 // DataTypeManager::Configure() will get called once the passphrase is 1001 // accepted. 1002 OnPassphraseRequired(true); 1003 return; 1004 } else { 1005 // We've been informed that a passphrase is required for decryption, but 1006 // now there are no encrypted data types enabled, so clear the flag 1007 // (NotifyObservers() will be called when configuration completes). 1008 observed_passphrase_required_ = false; 1009 } 1010 } 1011 data_type_manager_->Configure(types); 1012} 1013 1014sync_api::UserShare* ProfileSyncService::GetUserShare() const { 1015 if (backend_.get() && backend_initialized_) { 1016 return backend_->GetUserShare(); 1017 } 1018 NOTREACHED(); 1019 return NULL; 1020} 1021 1022const browser_sync::sessions::SyncSessionSnapshot* 1023 ProfileSyncService::GetLastSessionSnapshot() const { 1024 if (backend_.get() && backend_initialized_) { 1025 return backend_->GetLastSessionSnapshot(); 1026 } 1027 NOTREACHED(); 1028 return NULL; 1029} 1030 1031bool ProfileSyncService::HasUnsyncedItems() const { 1032 if (backend_.get() && backend_initialized_) { 1033 return backend_->HasUnsyncedItems(); 1034 } 1035 NOTREACHED(); 1036 return false; 1037} 1038 1039void ProfileSyncService::GetModelSafeRoutingInfo( 1040 browser_sync::ModelSafeRoutingInfo* out) { 1041 if (backend_.get() && backend_initialized_) { 1042 backend_->GetModelSafeRoutingInfo(out); 1043 } else { 1044 NOTREACHED(); 1045 } 1046} 1047 1048syncable::AutofillMigrationState 1049 ProfileSyncService::GetAutofillMigrationState() { 1050 if (backend_.get() && backend_initialized_) { 1051 return backend_->GetAutofillMigrationState(); 1052 } 1053 NOTREACHED(); 1054 return syncable::NOT_DETERMINED; 1055} 1056 1057void ProfileSyncService::SetAutofillMigrationState( 1058 syncable::AutofillMigrationState state) { 1059 if (backend_.get() && backend_initialized_) { 1060 backend_->SetAutofillMigrationState(state); 1061 } else { 1062 NOTREACHED(); 1063 } 1064} 1065 1066syncable::AutofillMigrationDebugInfo 1067 ProfileSyncService::GetAutofillMigrationDebugInfo() { 1068 if (backend_.get() && backend_initialized_) { 1069 return backend_->GetAutofillMigrationDebugInfo(); 1070 } 1071 NOTREACHED(); 1072 syncable::AutofillMigrationDebugInfo debug_info = { 0 }; 1073 return debug_info; 1074} 1075 1076void ProfileSyncService::SetAutofillMigrationDebugInfo( 1077 syncable::AutofillMigrationDebugInfo::PropertyToSet property_to_set, 1078 const syncable::AutofillMigrationDebugInfo& info) { 1079 if (backend_.get() && backend_initialized_) { 1080 backend_->SetAutofillMigrationDebugInfo(property_to_set, info); 1081 } else { 1082 NOTREACHED(); 1083 } 1084} 1085 1086void ProfileSyncService::ActivateDataType( 1087 DataTypeController* data_type_controller, 1088 ChangeProcessor* change_processor) { 1089 if (!backend_.get()) { 1090 NOTREACHED(); 1091 return; 1092 } 1093 DCHECK(backend_initialized_); 1094 change_processor->Start(profile(), backend_->GetUserShare()); 1095 backend_->ActivateDataType(data_type_controller, change_processor); 1096} 1097 1098void ProfileSyncService::DeactivateDataType( 1099 DataTypeController* data_type_controller, 1100 ChangeProcessor* change_processor) { 1101 change_processor->Stop(); 1102 if (backend_.get()) 1103 backend_->DeactivateDataType(data_type_controller, change_processor); 1104} 1105 1106void ProfileSyncService::SetPassphrase(const std::string& passphrase, 1107 bool is_explicit, 1108 bool is_creation) { 1109 if (ShouldPushChanges() || observed_passphrase_required_) { 1110 backend_->SetPassphrase(passphrase, is_explicit); 1111 } else { 1112 if (is_explicit) { 1113 cached_passphrase_.value = passphrase; 1114 cached_passphrase_.is_explicit = is_explicit; 1115 cached_passphrase_.is_creation = is_creation; 1116 } else { 1117 gaia_password_ = passphrase; 1118 } 1119 } 1120} 1121 1122void ProfileSyncService::EncryptDataTypes( 1123 const syncable::ModelTypeSet& encrypted_types) { 1124 backend_->EncryptDataTypes(encrypted_types); 1125} 1126 1127void ProfileSyncService::GetEncryptedDataTypes( 1128 syncable::ModelTypeSet* encrypted_types) const { 1129 *encrypted_types = encrypted_types_; 1130} 1131 1132void ProfileSyncService::Observe(NotificationType type, 1133 const NotificationSource& source, 1134 const NotificationDetails& details) { 1135 switch (type.value) { 1136 case NotificationType::SYNC_CONFIGURE_START: { 1137 NotifyObservers(); 1138 // TODO(sync): Maybe toast? 1139 break; 1140 } 1141 case NotificationType::SYNC_CONFIGURE_DONE: { 1142 DataTypeManager::ConfigureResultWithErrorLocation* result_with_location = 1143 Details<DataTypeManager::ConfigureResultWithErrorLocation>( 1144 details).ptr(); 1145 1146 DataTypeManager::ConfigureResult result = result_with_location->result; 1147 if (result == DataTypeManager::ABORTED && 1148 expect_sync_configuration_aborted_) { 1149 expect_sync_configuration_aborted_ = false; 1150 return; 1151 } 1152 // Clear out the gaia password if it is already there. 1153 gaia_password_ = std::string(); 1154 if (result != DataTypeManager::OK) { 1155 std::string message = StringPrintf("Sync Configuration failed with %d", 1156 result); 1157 OnUnrecoverableError(*(result_with_location->location), message); 1158 cached_passphrase_ = CachedPassphrase(); 1159 return; 1160 } 1161 1162 // If the user had entered a custom passphrase use it now. 1163 if (!cached_passphrase_.value.empty()) { 1164 // Don't hold on to the passphrase in raw form longer than needed. 1165 SetPassphrase(cached_passphrase_.value, 1166 cached_passphrase_.is_explicit, 1167 cached_passphrase_.is_creation); 1168 cached_passphrase_ = CachedPassphrase(); 1169 } 1170 1171 // We should never get in a state where we have no encrypted datatypes 1172 // enabled, and yet we still think we require a passphrase. 1173 DCHECK(!(observed_passphrase_required_ && 1174 passphrase_required_for_decryption_ && 1175 !IsEncryptedDatatypeEnabled())); 1176 1177 // TODO(sync): Less wizard, more toast. 1178 wizard_.Step(SyncSetupWizard::DONE); 1179 NotifyObservers(); 1180 1181 // In the old world, this would be a no-op. With new syncer thread, 1182 // this is the point where it is safe to switch from config-mode to 1183 // normal operation. 1184 backend_->StartSyncingWithServer(); 1185 break; 1186 } 1187 case NotificationType::SYNC_DATA_TYPES_UPDATED: { 1188 if (!HasSyncSetupCompleted()) break; 1189 1190 syncable::ModelTypeSet types; 1191 GetPreferredDataTypes(&types); 1192 OnUserChoseDatatypes(false, types); 1193 break; 1194 } 1195 case NotificationType::PREF_CHANGED: { 1196 std::string* pref_name = Details<std::string>(details).ptr(); 1197 if (*pref_name == prefs::kSyncManaged) { 1198 NotifyObservers(); 1199 if (*pref_sync_managed_) { 1200 DisableForUser(); 1201 } else if (HasSyncSetupCompleted() && AreCredentialsAvailable()) { 1202 StartUp(); 1203 } 1204 } 1205 break; 1206 } 1207 case NotificationType::GOOGLE_SIGNIN_SUCCESSFUL: { 1208 const GoogleServiceSigninSuccessDetails* successful = 1209 (Details<const GoogleServiceSigninSuccessDetails>(details).ptr()); 1210 // We pass 'false' to SetPassphrase to denote that this is an implicit 1211 // request and shouldn't override an explicit one. Thus, we either 1212 // update the implicit passphrase (idempotent if the passphrase didn't 1213 // actually change), or the user has an explicit passphrase set so this 1214 // becomes a no-op. 1215 tried_implicit_gaia_remove_when_bug_62103_fixed_ = true; 1216 SetPassphrase(successful->password, false, true); 1217 1218 // If this signin was to initiate a passphrase migration (on the 1219 // first computer, thus not for decryption), continue the migration. 1220 if (passphrase_migration_in_progress_ && 1221 !passphrase_required_for_decryption_) { 1222 wizard_.Step(SyncSetupWizard::PASSPHRASE_MIGRATION); 1223 passphrase_migration_in_progress_ = false; 1224 } 1225 1226 break; 1227 } 1228 case NotificationType::GOOGLE_SIGNIN_FAILED: { 1229 GoogleServiceAuthError error = 1230 *(Details<const GoogleServiceAuthError>(details).ptr()); 1231 UpdateAuthErrorState(error); 1232 break; 1233 } 1234 case NotificationType::TOKEN_AVAILABLE: { 1235 if (AreCredentialsAvailable()) { 1236 if (backend_initialized_) { 1237 backend_->UpdateCredentials(GetCredentials()); 1238 } 1239 1240 if (!profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) 1241 StartUp(); 1242 } 1243 break; 1244 } 1245 case NotificationType::TOKEN_LOADING_FINISHED: { 1246 // If not in Chrome OS, and we have a username without tokens, 1247 // the user will need to signin again, so sign out. 1248 if (cros_user_.empty() && 1249 !signin_->GetUsername().empty() && 1250 !AreCredentialsAvailable()) { 1251 DisableForUser(); 1252 } 1253 break; 1254 } 1255 default: { 1256 NOTREACHED(); 1257 } 1258 } 1259} 1260 1261void ProfileSyncService::AddObserver(Observer* observer) { 1262 observers_.AddObserver(observer); 1263} 1264 1265void ProfileSyncService::RemoveObserver(Observer* observer) { 1266 observers_.RemoveObserver(observer); 1267} 1268 1269bool ProfileSyncService::HasObserver(Observer* observer) const { 1270 return observers_.HasObserver(observer); 1271} 1272 1273browser_sync::JsFrontend* ProfileSyncService::GetJsFrontend() { 1274 return &js_event_handlers_; 1275} 1276 1277void ProfileSyncService::SyncEvent(SyncEventCodes code) { 1278 UMA_HISTOGRAM_ENUMERATION("Sync.EventCodes", code, MAX_SYNC_EVENT_CODE); 1279} 1280 1281// static 1282bool ProfileSyncService::IsSyncEnabled() { 1283 // We have switches::kEnableSync just in case we need to change back to 1284 // sync-disabled-by-default on a platform. 1285 return !CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableSync); 1286} 1287 1288bool ProfileSyncService::IsManaged() { 1289 // Some tests use ProfileSyncServiceMock which doesn't have a profile. 1290 return profile_ && profile_->GetPrefs()->GetBoolean(prefs::kSyncManaged); 1291} 1292 1293bool ProfileSyncService::ShouldPushChanges() { 1294 // True only after all bootstrapping has succeeded: the sync backend 1295 // is initialized, all enabled data types are consistent with one 1296 // another, and no unrecoverable error has transpired. 1297 if (unrecoverable_error_detected_) 1298 return false; 1299 1300 if (!data_type_manager_.get()) 1301 return false; 1302 1303 return data_type_manager_->state() == DataTypeManager::CONFIGURED; 1304} 1305