Index: chrome/browser/sync/profile_sync_service.cc |
=================================================================== |
--- chrome/browser/sync/profile_sync_service.cc (revision 58702) |
+++ chrome/browser/sync/profile_sync_service.cc (working copy) |
@@ -30,7 +30,10 @@ |
#include "chrome/browser/sync/glue/session_data_type_controller.h" |
#include "chrome/browser/sync/profile_sync_factory.h" |
#include "chrome/browser/sync/syncable/directory_manager.h" |
+#include "chrome/browser/sync/token_migrator.h" |
+#include "chrome/browser/sync/util/user_settings.h" |
#include "chrome/common/chrome_switches.h" |
+#include "chrome/common/net/gaia/gaia_constants.h" |
#include "chrome/common/notification_details.h" |
#include "chrome/common/notification_service.h" |
#include "chrome/common/notification_source.h" |
@@ -44,6 +47,7 @@ |
using browser_sync::DataTypeController; |
using browser_sync::DataTypeManager; |
using browser_sync::SyncBackendHost; |
+using sync_api::SyncCredentials; |
typedef GoogleServiceAuthError AuthError; |
@@ -55,20 +59,20 @@ |
ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory, |
Profile* profile, |
- bool bootstrap_sync_authentication) |
+ const std::string& cros_user) |
: last_auth_error_(AuthError::None()), |
factory_(factory), |
profile_(profile), |
- bootstrap_sync_authentication_(bootstrap_sync_authentication), |
+ cros_user_(cros_user), |
sync_service_url_(kDevServerUrl), |
backend_initialized_(false), |
- expecting_first_run_auth_needed_event_(false), |
is_auth_in_progress_(false), |
ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)), |
unrecoverable_error_detected_(false), |
use_chrome_async_socket_(false), |
notification_method_(browser_sync::kDefaultNotificationMethod), |
- ALLOW_THIS_IN_INITIALIZER_LIST(scoped_runnable_method_factory_(this)) { |
+ ALLOW_THIS_IN_INITIALIZER_LIST(scoped_runnable_method_factory_(this)), |
+ token_migrator_(NULL) { |
DCHECK(factory); |
DCHECK(profile); |
registrar_.Add(this, |
@@ -109,10 +113,8 @@ |
: last_auth_error_(AuthError::None()), |
factory_(NULL), |
profile_(NULL), |
- bootstrap_sync_authentication_(false), |
sync_service_url_(kSyncServerUrl), |
backend_initialized_(false), |
- expecting_first_run_auth_needed_event_(false), |
is_auth_in_progress_(false), |
ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)), |
unrecoverable_error_detected_(false), |
@@ -126,6 +128,37 @@ |
Shutdown(false); |
} |
+bool ProfileSyncService::AreCredentialsAvailable() { |
+ if (IsManaged()) { |
+ return false; |
+ } |
+ |
+ if (profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) { |
+ return false; |
+ } |
+ |
+ // CrOS user is always logged in. Chrome uses signin_ to check logged in. |
+ if (!cros_user_.empty() || !signin_.GetUsername().empty()) { |
+ // TODO(chron): Verify CrOS unit test behavior. |
+ if (profile()->GetTokenService() && |
+ profile()->GetTokenService()->HasTokenForService( |
+ GaiaConstants::kSyncService)) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+void ProfileSyncService::LoadMigratedCredentials(const std::string& username, |
+ const std::string& token) { |
+ signin_.SetUsername(username); |
+ profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername, username); |
+ profile()->GetTokenService()->OnIssueAuthTokenSuccess( |
+ GaiaConstants::kSyncService, token); |
+ profile()->GetPrefs()->SetBoolean(prefs::kSyncCredentialsMigrated, true); |
+ token_migrator_.reset(); |
+} |
+ |
void ProfileSyncService::Initialize() { |
LOG(INFO) << "Starting ProfileSyncService."; |
InitSettings(); |
@@ -141,26 +174,51 @@ |
return; |
} |
- if (!profile()->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted)) { |
+ RegisterAuthNotifications(); |
+ |
+ // In Chrome, we integrate a SigninManager which works with the sync |
+ // setup wizard to kick off the TokenService. CrOS does its own plumbing |
+ // for the TokenService. |
+ if (cros_user_.empty()) { |
+ // Will load tokens from DB and broadcast Token events after. |
+ signin_.Initialize(profile_); |
+ } |
+ |
+ if (!HasSyncSetupCompleted()) { |
DisableForUser(); // Clean up in case of previous crash / setup abort. |
- |
- // Automatically start sync in Chromium OS. |
- if (bootstrap_sync_authentication_ && |
- !profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) { |
- // If the LSID is empty, we're in a CrOS UI test that is not testing sync |
- // behavior, so we don't want the sync service to start. |
- if (profile()->GetTokenService() && |
- !profile()->GetTokenService()->HasLsid()) { |
- LOG(WARNING) << "Skipping CrOS sync startup, no LSID present."; |
- return; |
- } |
- StartUp(); |
+ if (!cros_user_.empty() && AreCredentialsAvailable()) { |
+ StartUp(); // Under ChromeOS, just autostart it anyway if creds are here. |
} |
+ } else if (AreCredentialsAvailable()) { |
+ // If we have credentials and sync setup finished, autostart the backend. |
+ // Note that if we haven't finished setting up sync, backend bring up will |
+ // be done by the wizard. |
+ StartUp(); |
} else { |
- StartUp(); |
+ // Try to migrate the tokens (if that hasn't already succeeded). |
+ if (!profile()->GetPrefs()->GetBoolean(prefs::kSyncCredentialsMigrated)) { |
+ token_migrator_.reset(new TokenMigrator(this, profile_->GetPath())); |
+ token_migrator_->TryMigration(); |
+ } |
} |
+ |
} |
+void ProfileSyncService::RegisterAuthNotifications() { |
+ registrar_.Add(this, |
+ NotificationType::TOKEN_AVAILABLE, |
+ NotificationService::AllSources()); |
+ registrar_.Add(this, |
+ NotificationType::TOKEN_LOADING_FINISHED, |
+ NotificationService::AllSources()); |
+ registrar_.Add(this, |
+ NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, |
+ NotificationService::AllSources()); |
+ registrar_.Add(this, |
+ NotificationType::GOOGLE_SIGNIN_FAILED, |
+ NotificationService::AllSources()); |
+} |
+ |
void ProfileSyncService::RegisterDataTypeController( |
DataTypeController* data_type_controller) { |
DCHECK_EQ(data_type_controllers_.count(data_type_controller->type()), 0U); |
@@ -232,6 +290,7 @@ |
pref_service->RegisterInt64Pref(prefs::kSyncLastSyncedTime, 0); |
pref_service->RegisterBooleanPref(prefs::kSyncHasSetupCompleted, false); |
pref_service->RegisterBooleanPref(prefs::kSyncSuppressStart, false); |
+ pref_service->RegisterBooleanPref(prefs::kSyncCredentialsMigrated, false); |
// If you've never synced before, or if you're using Chrome OS, all datatypes |
// are on by default. |
@@ -269,23 +328,29 @@ |
pref_service->ScheduleSavePersistentPrefs(); |
} |
+SyncCredentials ProfileSyncService::GetCredentials() { |
+ SyncCredentials credentials; |
+ credentials.email = !cros_user_.empty() ? cros_user_ : signin_.GetUsername(); |
+ DCHECK(!credentials.email.empty()); |
+ TokenService* service = profile_->GetTokenService(); |
+ credentials.sync_token = service->GetTokenForService( |
+ GaiaConstants::kSyncService); |
+ return credentials; |
+} |
+ |
void ProfileSyncService::InitializeBackend(bool delete_sync_data_folder) { |
if (!backend_.get()) { |
NOTREACHED(); |
return; |
} |
- // TODO(akalin): Gather all the command-line-controlled switches |
- // into an Options struct to make passing them down less annoying. |
+ // TODO(chron): Reimplement invalidate XMPP login / Sync login |
+ // command line switches. Perhaps make it a command |
+ // line in the TokenService itself to pass an arbitrary |
+ // token. |
- bool invalidate_sync_login = false; |
- bool invalidate_sync_xmpp_login = false; |
bool try_ssltcp_first = false; |
#if !defined(NDEBUG) |
- invalidate_sync_login = CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kInvalidateSyncLogin); |
- invalidate_sync_xmpp_login = CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kInvalidateSyncXmppLogin); |
try_ssltcp_first = CommandLine::ForCurrentProcess()->HasSwitch( |
switches::kSyncUseSslTcp); |
#endif |
@@ -294,15 +359,17 @@ |
// If sync setup hasn't finished, we don't want to initialize routing info |
// for any data types so that we don't download updates for types that the |
// user chooses not to sync on the first DownloadUpdatesCommand. |
- if (HasSyncSetupCompleted()) |
+ if (HasSyncSetupCompleted()) { |
GetPreferredDataTypes(&types); |
+ } |
+ |
+ SyncCredentials credentials = GetCredentials(); |
+ |
backend_->Initialize(sync_service_url_, |
types, |
profile_->GetRequestContext(), |
- profile_->GetTokenService()->GetLsid(), |
+ credentials, |
delete_sync_data_folder, |
- invalidate_sync_login, |
- invalidate_sync_xmpp_login, |
use_chrome_async_socket_, |
try_ssltcp_first, |
notification_method_); |
@@ -321,8 +388,10 @@ |
return; |
} |
- LOG(INFO) << "ProfileSyncSerivce bringing up backend host."; |
+ DCHECK(AreCredentialsAvailable()); |
+ LOG(INFO) << "ProfileSyncService bringing up backend host."; |
+ |
last_synced_time_ = base::Time::FromInternalValue( |
profile_->GetPrefs()->GetInt64(prefs::kSyncLastSyncedTime)); |
@@ -355,31 +424,21 @@ |
// Clear various flags. |
is_auth_in_progress_ = false; |
backend_initialized_ = false; |
- expecting_first_run_auth_needed_event_ = false; |
last_attempted_user_email_.clear(); |
} |
-void ProfileSyncService::EnableForUser(gfx::NativeWindow parent_window) { |
- if (WizardIsVisible()) { |
- wizard_.Focus(); |
- return; |
- } |
- expecting_first_run_auth_needed_event_ = true; |
- DCHECK(!data_type_manager_.get()); |
- |
- wizard_.SetParent(parent_window); |
- StartUp(); |
- FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); |
-} |
- |
void ProfileSyncService::DisableForUser() { |
- LOG(INFO) << "Clearing Sync DB."; |
+ LOG(INFO) << "Disabling sync for user."; |
- // Clear prefs (including SyncSetupHasCompleted) before shutting down so |
+ // Clear prefs (including SyncSetupHasCompleted) before shutting down so |
// PSS clients don't think we're set up while we're shutting down. |
ClearPreferences(); |
Shutdown(true); |
+ if (cros_user_.empty()) { |
+ signin_.SignOut(); |
+ } |
+ |
FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); |
} |
@@ -441,29 +500,20 @@ |
from_here.file_name(), |
from_here.line_number())); |
- // Shut all data types down. |
- if (data_type_manager_.get()) |
- data_type_manager_->Stop(); |
- |
// Tell the wizard so it can inform the user only if it is already open. |
wizard_.Step(SyncSetupWizard::FATAL_ERROR); |
FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); |
- LOG(ERROR) << "Unrecoverable error detected -- ProfileSyncService unusable."; |
+ LOG(ERROR) << "Unrecoverable error detected -- ProfileSyncService unusable." |
+ << message; |
std::string location; |
from_here.Write(true, true, &location); |
LOG(ERROR) << location; |
- if (SetupInProgress()) { |
- // We've hit an error in the middle of a startup process- shutdown all the |
- // backend stuff, and then restart it, so we're in the same state as before. |
- MessageLoop::current()->PostTask(FROM_HERE, |
+ // Shut all data types down. |
+ MessageLoop::current()->PostTask(FROM_HERE, |
scoped_runnable_method_factory_.NewRunnableMethod( |
&ProfileSyncService::Shutdown, true)); |
- MessageLoop::current()->PostTask(FROM_HERE, |
- scoped_runnable_method_factory_.NewRunnableMethod( |
- &ProfileSyncService::StartUp)); |
- } |
} |
void ProfileSyncService::OnBackendInitialized() { |
@@ -472,11 +522,12 @@ |
// The very first time the backend initializes is effectively the first time |
// we can say we successfully "synced". last_synced_time_ will only be null |
// in this case, because the pref wasn't restored on StartUp. |
- if (last_synced_time_.is_null()) |
+ if (last_synced_time_.is_null()) { |
UpdateLastSyncedTime(); |
+ } |
FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); |
- if (bootstrap_sync_authentication_) { |
+ if (!cros_user_.empty()) { |
if (profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) { |
ShowChooseDataTypes(NULL); |
} else { |
@@ -484,8 +535,9 @@ |
} |
} |
- if (HasSyncSetupCompleted()) |
+ if (HasSyncSetupCompleted()) { |
ConfigureDataTypeManager(); |
+ } |
} |
void ProfileSyncService::OnSyncCycleCompleted() { |
@@ -493,22 +545,16 @@ |
FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); |
} |
-void ProfileSyncService::OnAuthError() { |
- last_auth_error_ = backend_->GetAuthError(); |
+void ProfileSyncService::UpdateAuthErrorState( |
+ const GoogleServiceAuthError& error) { |
+ last_auth_error_ = error; |
// Protect against the in-your-face dialogs that pop out of nowhere. |
// Require the user to click somewhere to run the setup wizard in the case |
// of a steady-state auth failure. |
- if (WizardIsVisible() || expecting_first_run_auth_needed_event_) { |
+ if (WizardIsVisible()) { |
wizard_.Step(AuthError::NONE == last_auth_error_.state() ? |
SyncSetupWizard::GAIA_SUCCESS : SyncSetupWizard::GAIA_LOGIN); |
- } |
- |
- if (expecting_first_run_auth_needed_event_) { |
- last_auth_error_ = AuthError::None(); |
- expecting_first_run_auth_needed_event_ = false; |
- } |
- |
- if (!WizardIsVisible()) { |
+ } else { |
auth_error_time_ == base::TimeTicks::Now(); |
} |
@@ -523,6 +569,10 @@ |
FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); |
} |
+void ProfileSyncService::OnAuthError() { |
+ UpdateAuthErrorState(backend_->GetAuthError()); |
+ } |
+ |
void ProfileSyncService::OnStopSyncingPermanently() { |
if (SetupInProgress()) { |
wizard_.Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR); |
@@ -533,6 +583,12 @@ |
} |
void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) { |
+ // TODO(johnnyg): File a bug to make sure this doesn't happen. |
+ if (!cros_user_.empty()) { |
+ LOG(WARNING) << "ShowLoginDialog called on Chrome OS."; |
+ return; |
+ } |
+ |
if (WizardIsVisible()) { |
wizard_.Focus(); |
return; |
@@ -544,10 +600,10 @@ |
auth_error_time_ = base::TimeTicks(); // Reset auth_error_time_ to null. |
} |
- if (last_auth_error_.state() != AuthError::NONE) { |
- wizard_.SetParent(parent_window); |
- wizard_.Step(SyncSetupWizard::GAIA_LOGIN); |
- } |
+ wizard_.SetParent(parent_window); |
+ wizard_.Step(SyncSetupWizard::GAIA_LOGIN); |
+ |
+ FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); |
} |
void ProfileSyncService::ShowChooseDataTypes(gfx::NativeWindow parent_window) { |
@@ -623,16 +679,26 @@ |
void ProfileSyncService::OnUserSubmittedAuth( |
const std::string& username, const std::string& password, |
const std::string& captcha) { |
- if (!backend_.get()) { |
- NOTREACHED(); |
- return; |
- } |
last_attempted_user_email_ = username; |
is_auth_in_progress_ = true; |
FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); |
auth_start_time_ = base::TimeTicks::Now(); |
- backend_->Authenticate(username, password, captcha); |
+ |
+ // TODO(chron): Mechanism for ChromeOS auth renewal? |
+ // (maybe just run the dialog anyway?) |
+ // or send it to the CrOS login somehow? |
+ if (!cros_user_.empty()) { |
+ LOG(WARNING) << "No mechanism on ChromeOS yet. See http://crbug.com/50292"; |
+ } |
+ |
+ if (!signin_.GetUsername().empty()) { |
+ signin_.SignOut(); |
+ } |
+ signin_.StartSignIn(username, |
+ password, |
+ last_auth_error_.captcha().token, |
+ captcha); |
} |
void ProfileSyncService::OnUserChoseDatatypes(bool sync_everything, |
@@ -649,7 +715,7 @@ |
} |
void ProfileSyncService::OnUserCancelledDialog() { |
- if (!profile_->GetPrefs()->GetBoolean(prefs::kSyncHasSetupCompleted)) { |
+ if (!HasSyncSetupCompleted()) { |
// A sync dialog was aborted before authentication. |
// Rollback. |
expect_sync_configuration_aborted_ = true; |
@@ -815,13 +881,46 @@ |
std::string* pref_name = Details<std::string>(details).ptr(); |
if (*pref_name == prefs::kSyncManaged) { |
FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); |
- if (*pref_sync_managed_) |
+ if (*pref_sync_managed_) { |
DisableForUser(); |
- else if (HasSyncSetupCompleted()) |
+ } else if (HasSyncSetupCompleted() && AreCredentialsAvailable()) { |
StartUp(); |
+ } |
} |
break; |
} |
+ case NotificationType::GOOGLE_SIGNIN_SUCCESSFUL: { |
+ LOG(INFO) << "Signin OK. Waiting on tokens."; |
+ // TODO(chron): UI update? |
+ // TODO(chron): Timeout? |
+ break; |
+ } |
+ case NotificationType::GOOGLE_SIGNIN_FAILED: { |
+ GoogleServiceAuthError error = |
+ *(Details<GoogleServiceAuthError>(details).ptr()); |
+ UpdateAuthErrorState(error); |
+ break; |
+ } |
+ case NotificationType::TOKEN_AVAILABLE: { |
+ if (AreCredentialsAvailable()) { |
+ if (backend_initialized_) { |
+ backend_->UpdateCredentials(GetCredentials()); |
+ } |
+ |
+ StartUp(); |
+ } |
+ break; |
+ } |
+ case NotificationType::TOKEN_LOADING_FINISHED: { |
+ // If not in Chrome OS, and we have a username without tokens, |
+ // the user will need to signin again, so sign out. |
+ if (cros_user_.empty() && |
+ !signin_.GetUsername().empty() && |
+ !AreCredentialsAvailable()) { |
+ DisableForUser(); |
+ } |
+ break; |
+ } |
default: { |
NOTREACHED(); |
} |