blob: da476c19597b8c3fa66d0a89cfe91044c8a65925 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/sync/sync_ui_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/signin_error_controller_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/signin_error_controller.h"
#include "components/strings/grit/components_strings.h"
#include "components/sync/protocol/sync_protocol_error.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "services/identity/public/cpp/identity_manager.h"
#include "services/identity/public/cpp/primary_account_mutator.h"
#include "ui/base/l10n/l10n_util.h"
#if !defined(OS_CHROMEOS)
#include "chrome/browser/signin/signin_util.h"
#endif // defined(OS_CHROMEOS)
namespace sync_ui_util {
namespace {
// Returns the message that should be displayed when the user is authenticated
// and can connect to the sync server. If the user hasn't yet authenticated, an
// empty string is returned.
base::string16 GetSyncedStateStatusLabel(const syncer::SyncService* service,
StatusLabelStyle style,
bool sync_everything) {
if (!service || service->HasDisableReason(
syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY)) {
// User is signed in, but sync is disabled.
return l10n_util::GetStringUTF16(IDS_SIGNED_IN_WITH_SYNC_DISABLED);
}
if (service->HasDisableReason(
syncer::SyncService::DISABLE_REASON_USER_CHOICE)) {
// User is signed in, but sync has been stopped.
return l10n_util::GetStringUTF16(IDS_SIGNED_IN_WITH_SYNC_SUPPRESSED);
}
if (!service->IsSyncFeatureActive()) {
// User is not signed in, or sync is still initializing.
return base::string16();
}
// Message may also carry additional advice with an HTML link, if acceptable.
switch (style) {
case PLAIN_TEXT:
return l10n_util::GetStringUTF16(
sync_everything ? IDS_SYNC_ACCOUNT_SYNCING
: IDS_SYNC_ACCOUNT_SYNCING_CUSTOM_DATA_TYPES);
case WITH_HTML:
return l10n_util::GetStringFUTF16(
IDS_SYNC_ACCOUNT_SYNCING_WITH_MANAGE_LINK,
base::ASCIIToUTF16(chrome::kSyncGoogleDashboardURL));
default:
NOTREACHED();
return nullptr;
}
}
void GetStatusForActionableError(const syncer::SyncProtocolError& error,
base::string16* status_label,
base::string16* link_label,
ActionType* action_type) {
DCHECK(status_label);
DCHECK(link_label);
switch (error.action) {
case syncer::UPGRADE_CLIENT:
status_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_UPGRADE_CLIENT));
link_label->assign(
l10n_util::GetStringUTF16(IDS_SYNC_UPGRADE_CLIENT_LINK_LABEL));
*action_type = UPGRADE_CLIENT;
break;
case syncer::ENABLE_SYNC_ON_ACCOUNT:
status_label->assign(
l10n_util::GetStringUTF16(IDS_SYNC_STATUS_ENABLE_SYNC_ON_ACCOUNT));
break;
default:
status_label->clear();
break;
}
}
void GetStatusForUnrecoverableError(Profile* profile,
const syncer::SyncService* service,
base::string16* status_label,
base::string16* link_label,
ActionType* action_type) {
// Unrecoverable error is sometimes accompanied by actionable error.
// If status message is set display that message, otherwise show generic
// unrecoverable error message.
syncer::SyncStatus status;
service->QueryDetailedSyncStatus(&status);
GetStatusForActionableError(status.sync_protocol_error, status_label,
link_label, action_type);
if (status_label->empty()) {
*action_type = REAUTHENTICATE;
link_label->assign(
l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_LINK_LABEL));
#if !defined(OS_CHROMEOS)
status_label->assign(l10n_util::GetStringUTF16(
IDS_SYNC_STATUS_UNRECOVERABLE_ERROR));
// The message for managed accounts is the same as that of the cros.
if (!signin_util::IsUserSignoutAllowedForProfile(profile)) {
status_label->assign(l10n_util::GetStringUTF16(
IDS_SYNC_STATUS_UNRECOVERABLE_ERROR_NEEDS_SIGNOUT));
}
#else
status_label->assign(l10n_util::GetStringUTF16(
IDS_SYNC_STATUS_UNRECOVERABLE_ERROR_NEEDS_SIGNOUT));
#endif
}
}
// Depending on the authentication state, returns labels to be used to display
// information about the sync status.
void GetStatusForAuthError(Profile* profile,
base::string16* status_label,
base::string16* link_label,
ActionType* action_type) {
DCHECK(status_label);
DCHECK(link_label);
const GoogleServiceAuthError::State state =
SigninErrorControllerFactory::GetForProfile(profile)->
auth_error().state();
switch (state) {
case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
status_label->assign(
l10n_util::GetStringUTF16(IDS_SYNC_SERVICE_UNAVAILABLE));
break;
case GoogleServiceAuthError::CONNECTION_FAILED:
status_label->assign(
l10n_util::GetStringUTF16(IDS_SYNC_SERVER_IS_UNREACHABLE));
// Note that there is little the user can do if the server is not
// reachable. Since attempting to re-connect is done automatically by
// the Syncer, we do not show the (re)login link.
break;
case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
case GoogleServiceAuthError::SERVICE_ERROR:
case GoogleServiceAuthError::ACCOUNT_DELETED:
case GoogleServiceAuthError::ACCOUNT_DISABLED:
default:
status_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_ERROR));
link_label->assign(
l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_LINK_LABEL));
*action_type = REAUTHENTICATE;
break;
}
}
// TODO(akalin): Write unit tests for these three functions below.
// status_label and link_label must either be both null or both non-null.
MessageType GetStatusInfo(Profile* profile,
const syncer::SyncService* service,
identity::IdentityManager* identity_manager,
StatusLabelStyle style,
base::string16* status_label,
base::string16* link_label,
ActionType* action_type) {
DCHECK_EQ(status_label == nullptr, link_label == nullptr);
MessageType result_type(SYNCED);
if (!identity_manager->HasPrimaryAccount())
return PRE_SYNCED;
// Needed to check the state of the authentication process below.
auto* primary_account_mutator = identity_manager->GetPrimaryAccountMutator();
if (!service || service->GetUserSettings()->IsFirstSetupComplete() ||
service->HasDisableReason(
syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY) ||
service->HasDisableReason(
syncer::SyncService::DISABLE_REASON_USER_CHOICE)) {
// The order or priority is going to be: 1. Unrecoverable errors.
// 2. Auth errors. 3. Protocol errors. 4. Passphrase errors.
if (service && service->HasUnrecoverableError()) {
if (status_label && link_label) {
GetStatusForUnrecoverableError(profile, service, status_label,
link_label, action_type);
}
return SYNC_ERROR;
}
// For auth errors first check if an auth is in progress.
if (primary_account_mutator &&
primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress()) {
if (status_label) {
status_label->assign(
l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL));
}
return PRE_SYNCED;
}
bool sync_everything =
service->GetUserSettings()->IsSyncEverythingEnabled();
// Check for sync errors if the sync service is enabled.
if (service) {
// Since there is no auth in progress, check for an auth error first.
GoogleServiceAuthError auth_error =
SigninErrorControllerFactory::GetForProfile(profile)->auth_error();
if (auth_error.state() != GoogleServiceAuthError::NONE) {
if (status_label && link_label) {
GetStatusForAuthError(profile, status_label, link_label, action_type);
}
return SYNC_ERROR;
}
// We don't have an auth error. Check for an actionable error.
syncer::SyncStatus status;
service->QueryDetailedSyncStatus(&status);
if (status_label && link_label) {
GetStatusForActionableError(status.sync_protocol_error, status_label,
link_label, action_type);
if (!status_label->empty())
return SYNC_ERROR;
}
// Check for a passphrase error.
if (service->GetUserSettings()->IsPassphraseRequired() &&
service->GetUserSettings()->IsPassphraseRequiredForDecryption()) {
if (status_label && link_label) {
status_label->assign(
l10n_util::GetStringUTF16(IDS_SYNC_STATUS_NEEDS_PASSWORD));
link_label->assign(
l10n_util::GetStringUTF16(
IDS_SYNC_STATUS_NEEDS_PASSWORD_LINK_LABEL));
*action_type = ENTER_PASSPHRASE;
}
return SYNC_ERROR;
}
// Check to see if sync has been disabled via the dasboard and needs to be
// set up once again.
if (service->HasDisableReason(
syncer::SyncService::DISABLE_REASON_USER_CHOICE) &&
status.sync_protocol_error.error_type == syncer::NOT_MY_BIRTHDAY) {
if (status_label) {
status_label->assign(
GetSyncedStateStatusLabel(service, style, sync_everything));
}
return PRE_SYNCED;
}
}
// There is no error. Display "Last synced..." message.
if (status_label) {
status_label->assign(
GetSyncedStateStatusLabel(service, style, sync_everything));
}
return SYNCED;
} else {
// Either show auth error information with a link to re-login, auth in prog,
// or provide a link to continue with setup.
if (service->IsFirstSetupInProgress()) {
result_type = PRE_SYNCED;
syncer::SyncStatus status;
service->QueryDetailedSyncStatus(&status);
GoogleServiceAuthError auth_error =
SigninErrorControllerFactory::GetForProfile(profile)->auth_error();
if (status_label) {
status_label->assign(
l10n_util::GetStringUTF16(IDS_SYNC_NTP_SETUP_IN_PROGRESS));
}
if (primary_account_mutator &&
primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress()) {
if (status_label) {
status_label->assign(
l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL));
}
} else if (auth_error.state() != GoogleServiceAuthError::NONE &&
auth_error.state() != GoogleServiceAuthError::TWO_FACTOR) {
if (status_label && link_label) {
GetStatusForAuthError(profile, status_label, link_label, action_type);
}
result_type = SYNC_ERROR;
}
} else if (service->HasUnrecoverableError()) {
result_type = SYNC_ERROR;
if (status_label && link_label) {
GetStatusForUnrecoverableError(profile, service, status_label,
link_label, action_type);
}
} else if (identity_manager->HasPrimaryAccount()) {
if (ShouldRequestSyncConfirmation(service)) {
if (status_label && link_label) {
status_label->assign(
l10n_util::GetStringUTF16(IDS_SYNC_SETTINGS_NOT_CONFIRMED));
link_label->assign(l10n_util::GetStringUTF16(
IDS_SYNC_ERROR_USER_MENU_CONFIRM_SYNC_SETTINGS_BUTTON));
}
*action_type = CONFIRM_SYNC_SETTINGS;
result_type = SYNC_ERROR;
} else {
// The user is signed in, but sync has been stopped.
result_type = PRE_SYNCED;
if (status_label) {
status_label->assign(
l10n_util::GetStringUTF16(IDS_SIGNED_IN_WITH_SYNC_SUPPRESSED));
}
}
}
}
return result_type;
}
} // namespace
MessageType GetStatusLabels(Profile* profile,
const syncer::SyncService* service,
identity::IdentityManager* identity_manager,
base::string16* status_label,
base::string16* link_label,
ActionType* action_type) {
DCHECK(status_label);
DCHECK(link_label);
return GetStatusInfo(profile, service, identity_manager, PLAIN_TEXT,
status_label, link_label, action_type);
}
#if !defined(OS_CHROMEOS)
AvatarSyncErrorType GetMessagesForAvatarSyncError(
Profile* profile,
identity::IdentityManager* identity_manager,
int* content_string_id,
int* button_string_id) {
const syncer::SyncService* service =
ProfileSyncServiceFactory::GetSyncServiceForBrowserContext(profile);
// The order or priority is going to be: 1. Unrecoverable errors.
// 2. Auth errors. 3. Protocol errors. 4. Passphrase errors.
if (service && service->HasUnrecoverableError()) {
// An unrecoverable error is sometimes accompanied by an actionable error.
// If an actionable error is not set to be UPGRADE_CLIENT, then show a
// generic unrecoverable error message.
syncer::SyncStatus status;
service->QueryDetailedSyncStatus(&status);
if (status.sync_protocol_error.action != syncer::UPGRADE_CLIENT) {
// Display different messages and buttons for managed accounts.
if (!signin_util::IsUserSignoutAllowedForProfile(profile)) {
// For a managed user, the user is directed to the signout
// confirmation dialogue in the settings page.
*content_string_id = IDS_SYNC_ERROR_USER_MENU_SIGNOUT_MESSAGE;
*button_string_id = IDS_SYNC_ERROR_USER_MENU_SIGNOUT_BUTTON;
return MANAGED_USER_UNRECOVERABLE_ERROR;
}
// For a non-managed user, we sign out on the user's behalf and prompt
// the user to sign in again.
*content_string_id = IDS_SYNC_ERROR_USER_MENU_SIGNIN_AGAIN_MESSAGE;
*button_string_id = IDS_SYNC_ERROR_USER_MENU_SIGNIN_AGAIN_BUTTON;
return UNRECOVERABLE_ERROR;
}
}
// Check for an auth error.
SigninErrorController* signin_error_controller =
SigninErrorControllerFactory::GetForProfile(profile);
if (signin_error_controller && signin_error_controller->HasError()) {
if (profile->IsSupervised()) {
// For a supervised user, no direct action can be taken to resolve an
// auth token error.
*content_string_id = IDS_SYNC_ERROR_USER_MENU_SUPERVISED_SIGNIN_MESSAGE;
*button_string_id = 0;
return SUPERVISED_USER_AUTH_ERROR;
}
// For a non-supervised user, the user can reauth to resolve the signin
// error.
*content_string_id = IDS_SYNC_ERROR_USER_MENU_SIGNIN_MESSAGE;
*button_string_id = IDS_SYNC_ERROR_USER_MENU_SIGNIN_BUTTON;
return AUTH_ERROR;
}
// Check for sync errors if the sync service is enabled.
if (service) {
// Check for an actionable UPGRADE_CLIENT error.
syncer::SyncStatus status;
service->QueryDetailedSyncStatus(&status);
if (status.sync_protocol_error.action == syncer::UPGRADE_CLIENT) {
*content_string_id = IDS_SYNC_ERROR_USER_MENU_UPGRADE_MESSAGE;
*button_string_id = IDS_SYNC_ERROR_USER_MENU_UPGRADE_BUTTON;
return UPGRADE_CLIENT_ERROR;
}
// Check for a sync passphrase error.
if (ShouldShowPassphraseError(service)) {
*content_string_id = IDS_SYNC_ERROR_USER_MENU_PASSPHRASE_MESSAGE;
*button_string_id = IDS_SYNC_ERROR_USER_MENU_PASSPHRASE_BUTTON;
return PASSPHRASE_ERROR;
}
// Check for a sync confirmation error.
if (identity_manager->HasPrimaryAccount() &&
ShouldRequestSyncConfirmation(service)) {
*content_string_id = IDS_SYNC_SETTINGS_NOT_CONFIRMED;
*button_string_id = IDS_SYNC_ERROR_USER_MENU_CONFIRM_SYNC_SETTINGS_BUTTON;
return SETTINGS_UNCONFIRMED_ERROR;
}
}
// There is no error.
return NO_SYNC_ERROR;
}
#endif
MessageType GetStatus(Profile* profile,
const syncer::SyncService* service,
identity::IdentityManager* identity_manager) {
ActionType action_type = NO_ACTION;
return GetStatusInfo(profile, service, identity_manager, WITH_HTML, nullptr,
nullptr, &action_type);
}
bool ShouldRequestSyncConfirmation(const syncer::SyncService* service) {
return !service->IsSetupInProgress() &&
!service->GetUserSettings()->IsFirstSetupComplete() &&
!service->HasDisableReason(
syncer::SyncService::DISABLE_REASON_USER_CHOICE) &&
service->IsAuthenticatedAccountPrimary();
}
bool ShouldShowPassphraseError(const syncer::SyncService* service) {
return service->GetUserSettings()->IsFirstSetupComplete() &&
service->GetUserSettings()->IsPassphraseRequired() &&
service->GetUserSettings()->IsPassphraseRequiredForDecryption();
}
} // namespace sync_ui_util