1 // Copyright (c) 2012 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.
5 #include "chrome/browser/sync/sync_ui_util.h"
7 #include "base/i18n/number_formatting.h"
8 #include "base/i18n/time_formatting.h"
9 #include "base/string_util.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/prefs/pref_service.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "chrome/browser/signin/signin_manager.h"
15 #include "chrome/browser/sync/profile_sync_service.h"
16 #include "chrome/browser/sync/profile_sync_service_factory.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/browser_window.h"
19 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
20 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/common/url_constants.h"
24 #include "google_apis/gaia/google_service_auth_error.h"
25 #include "grit/browser_resources.h"
26 #include "grit/chromium_strings.h"
27 #include "grit/generated_resources.h"
28 #include "grit/locale_settings.h"
29 #include "sync/internal_api/public/base/model_type.h"
30 #include "sync/internal_api/public/sessions/sync_session_snapshot.h"
31 #include "sync/protocol/proto_enum_conversions.h"
32 #include "sync/protocol/sync_protocol_error.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/base/resource/resource_bundle.h"
36 typedef GoogleServiceAuthError AuthError
;
38 namespace sync_ui_util
{
42 // Given an authentication state this helper function returns various labels
43 // that can be used to display information about the state.
44 void GetStatusLabelsForAuthError(const AuthError
& auth_error
,
45 const ProfileSyncService
& service
,
46 string16
* status_label
,
48 string16
* global_error_menu_label
,
49 string16
* global_error_bubble_message
,
50 string16
* global_error_bubble_accept_label
) {
51 string16 username
= UTF8ToUTF16(service
.profile()->GetPrefs()->GetString(
52 prefs::kGoogleServicesUsername
));
53 string16 product_name
= l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
);
55 link_label
->assign(l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_LINK_LABEL
));
57 switch (auth_error
.state()) {
58 case AuthError::INVALID_GAIA_CREDENTIALS
:
59 case AuthError::ACCOUNT_DELETED
:
60 case AuthError::ACCOUNT_DISABLED
:
61 // If the user name is empty then the first login failed, otherwise the
62 // credentials are out-of-date.
63 if (username
.empty()) {
66 l10n_util::GetStringUTF16(IDS_SYNC_INVALID_USER_CREDENTIALS
));
71 l10n_util::GetStringUTF16(IDS_SYNC_LOGIN_INFO_OUT_OF_DATE
));
73 if (global_error_menu_label
) {
74 global_error_menu_label
->assign(l10n_util::GetStringUTF16(
75 IDS_SYNC_SIGN_IN_ERROR_WRENCH_MENU_ITEM
));
77 if (global_error_bubble_message
) {
78 global_error_bubble_message
->assign(l10n_util::GetStringFUTF16(
79 IDS_SYNC_SIGN_IN_ERROR_BUBBLE_VIEW_MESSAGE
, product_name
));
81 if (global_error_bubble_accept_label
) {
82 global_error_bubble_accept_label
->assign(l10n_util::GetStringUTF16(
83 IDS_SYNC_SIGN_IN_ERROR_BUBBLE_VIEW_ACCEPT
));
87 case AuthError::SERVICE_UNAVAILABLE
:
90 l10n_util::GetStringUTF16(IDS_SYNC_SERVICE_UNAVAILABLE
));
94 if (global_error_menu_label
) {
95 global_error_menu_label
->assign(l10n_util::GetStringUTF16(
96 IDS_SYNC_SIGN_IN_ERROR_WRENCH_MENU_ITEM
));
98 if (global_error_bubble_message
) {
99 global_error_bubble_message
->assign(l10n_util::GetStringFUTF16(
100 IDS_SYNC_UNAVAILABLE_ERROR_BUBBLE_VIEW_MESSAGE
, product_name
));
102 if (global_error_bubble_accept_label
) {
103 global_error_bubble_accept_label
->assign(l10n_util::GetStringUTF16(
104 IDS_SYNC_UNAVAILABLE_ERROR_BUBBLE_VIEW_ACCEPT
));
107 case AuthError::CONNECTION_FAILED
:
108 // Note that there is little the user can do if the server is not
109 // reachable. Since attempting to re-connect is done automatically by
110 // the Syncer, we do not show the (re)login link.
112 status_label
->assign(
113 l10n_util::GetStringFUTF16(IDS_SYNC_SERVER_IS_UNREACHABLE
,
119 status_label
->assign(l10n_util::GetStringUTF16(
120 IDS_SYNC_ERROR_SIGNING_IN
));
122 if (global_error_menu_label
) {
123 global_error_menu_label
->assign(l10n_util::GetStringUTF16(
124 IDS_SYNC_SIGN_IN_ERROR_WRENCH_MENU_ITEM
));
126 if (global_error_bubble_message
) {
127 global_error_bubble_message
->assign(l10n_util::GetStringFUTF16(
128 IDS_SYNC_OTHER_SIGN_IN_ERROR_BUBBLE_VIEW_MESSAGE
, product_name
));
130 if (global_error_bubble_accept_label
) {
131 global_error_bubble_accept_label
->assign(l10n_util::GetStringUTF16(
132 IDS_SYNC_SIGN_IN_ERROR_BUBBLE_VIEW_ACCEPT
));
138 // Returns the message that should be displayed when the user is authenticated
139 // and can connect to the sync server. If the user hasn't yet authenticated, an
140 // empty string is returned.
141 string16
GetSyncedStateStatusLabel(ProfileSyncService
* service
,
142 StatusLabelStyle style
) {
143 if (!service
->sync_initialized())
146 string16 user_name
= UTF8ToUTF16(service
->profile()->GetPrefs()->GetString(
147 prefs::kGoogleServicesUsername
));
148 DCHECK(!user_name
.empty());
150 // Message may also carry additional advice with an HTML link, if acceptable.
153 return l10n_util::GetStringFUTF16(
154 IDS_SYNC_ACCOUNT_SYNCING_TO_USER
,
157 return l10n_util::GetStringFUTF16(
158 IDS_SYNC_ACCOUNT_SYNCING_TO_USER_WITH_MANAGE_LINK
,
160 ASCIIToUTF16(chrome::kSyncGoogleDashboardURL
));
167 void GetStatusForActionableError(
168 const syncer::SyncProtocolError
& error
,
169 string16
* status_label
) {
170 DCHECK(status_label
);
171 switch (error
.action
) {
172 case syncer::STOP_AND_RESTART_SYNC
:
173 status_label
->assign(
174 l10n_util::GetStringUTF16(IDS_SYNC_STOP_AND_RESTART_SYNC
));
176 case syncer::UPGRADE_CLIENT
:
177 status_label
->assign(
178 l10n_util::GetStringFUTF16(IDS_SYNC_UPGRADE_CLIENT
,
179 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
)));
181 case syncer::ENABLE_SYNC_ON_ACCOUNT
:
182 status_label
->assign(
183 l10n_util::GetStringUTF16(IDS_SYNC_ENABLE_SYNC_ON_ACCOUNT
));
185 case syncer::CLEAR_USER_DATA_AND_RESYNC
:
186 status_label
->assign(
187 l10n_util::GetStringUTF16(IDS_SYNC_CLEAR_USER_DATA
));
194 // TODO(akalin): Write unit tests for these three functions below.
196 // status_label and link_label must either be both NULL or both non-NULL.
197 MessageType
GetStatusInfo(ProfileSyncService
* service
,
198 const SigninManager
& signin
,
199 StatusLabelStyle style
,
200 string16
* status_label
,
201 string16
* link_label
) {
202 DCHECK_EQ(status_label
== NULL
, link_label
== NULL
);
204 MessageType
result_type(SYNCED
);
210 if (service
->HasSyncSetupCompleted()) {
211 ProfileSyncService::Status status
;
212 service
->QueryDetailedSyncStatus(&status
);
213 const AuthError
& auth_error
= service
->GetAuthError();
215 // The order or priority is going to be: 1. Unrecoverable errors.
216 // 2. Auth errors. 3. Protocol errors. 4. Passphrase errors.
218 if (service
->HasUnrecoverableError()) {
220 status_label
->assign(l10n_util::GetStringFUTF16(
221 IDS_SYNC_STATUS_UNRECOVERABLE_ERROR
,
222 l10n_util::GetStringUTF16(IDS_SYNC_UNRECOVERABLE_ERROR_HELP_URL
)));
227 // For auth errors first check if an auth is in progress.
228 if (signin
.AuthInProgress()) {
230 status_label
->assign(
231 l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL
));
236 // No auth in progress check for an auth error.
237 if (auth_error
.state() != AuthError::NONE
) {
238 if (status_label
&& link_label
) {
239 GetStatusLabelsForAuthError(auth_error
, *service
,
240 status_label
, link_label
, NULL
, NULL
, NULL
);
245 // We dont have an auth error. Check for protocol error.
246 if (ShouldShowActionOnUI(status
.sync_protocol_error
)) {
248 GetStatusForActionableError(status
.sync_protocol_error
,
254 // Now finally passphrase error.
255 if (service
->IsPassphraseRequired()) {
256 if (service
->IsPassphraseRequiredForDecryption()) {
257 // TODO(lipalani) : Ask tim if this is still needed.
258 // NOT first machine.
259 // Show a link ("needs attention"), but still indicate the
260 // current synced status. Return SYNC_PROMO so that
261 // the configure link will still be shown.
262 if (status_label
&& link_label
) {
263 status_label
->assign(GetSyncedStateStatusLabel(service
, style
));
265 l10n_util::GetStringUTF16(IDS_SYNC_PASSWORD_SYNC_ATTENTION
));
271 // There is no error. Display "Last synced..." message.
273 status_label
->assign(GetSyncedStateStatusLabel(service
, style
));
276 // Either show auth error information with a link to re-login, auth in prog,
277 // or provide a link to continue with setup.
278 result_type
= PRE_SYNCED
;
279 if (service
->FirstSetupInProgress()) {
280 ProfileSyncService::Status status
;
281 service
->QueryDetailedSyncStatus(&status
);
282 const AuthError
& auth_error
= service
->GetAuthError();
284 status_label
->assign(
285 l10n_util::GetStringUTF16(IDS_SYNC_NTP_SETUP_IN_PROGRESS
));
287 if (signin
.AuthInProgress()) {
289 status_label
->assign(
290 l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL
));
292 } else if (auth_error
.state() != AuthError::NONE
&&
293 auth_error
.state() != AuthError::TWO_FACTOR
) {
295 status_label
->clear();
296 GetStatusLabelsForAuthError(auth_error
, *service
, status_label
, NULL
,
299 result_type
= SYNC_ERROR
;
301 } else if (service
->HasUnrecoverableError()) {
302 result_type
= SYNC_ERROR
;
303 ProfileSyncService::Status status
;
304 service
->QueryDetailedSyncStatus(&status
);
305 if (ShouldShowActionOnUI(status
.sync_protocol_error
)) {
307 GetStatusForActionableError(status
.sync_protocol_error
,
310 } else if (status_label
) {
311 status_label
->assign(l10n_util::GetStringUTF16(IDS_SYNC_SETUP_ERROR
));
318 // Returns the status info for use on the new tab page, where we want slightly
319 // different information than in the settings panel.
320 MessageType
GetStatusInfoForNewTabPage(ProfileSyncService
* service
,
321 const SigninManager
& signin
,
322 string16
* status_label
,
323 string16
* link_label
) {
324 DCHECK(status_label
);
327 if (service
->HasSyncSetupCompleted() &&
328 service
->IsPassphraseRequired()) {
329 if (service
->passphrase_required_reason() == syncer::REASON_ENCRYPTION
) {
330 // First machine migrating to passwords. Show as a promotion.
331 if (status_label
&& link_label
) {
332 status_label
->assign(
333 l10n_util::GetStringFUTF16(
334 IDS_SYNC_NTP_PASSWORD_PROMO
,
335 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
)));
337 l10n_util::GetStringUTF16(IDS_SYNC_NTP_PASSWORD_ENABLE
));
341 // NOT first machine.
342 // Show a link and present as an error ("needs attention").
343 if (status_label
&& link_label
) {
344 status_label
->assign(string16());
346 l10n_util::GetStringUTF16(IDS_SYNC_CONFIGURE_ENCRYPTION
));
352 // Fallback to default.
353 return GetStatusInfo(service
, signin
, WITH_HTML
, status_label
, link_label
);
358 MessageType
GetStatusLabels(ProfileSyncService
* service
,
359 const SigninManager
& signin
,
360 StatusLabelStyle style
,
361 string16
* status_label
,
362 string16
* link_label
) {
363 DCHECK(status_label
);
365 return sync_ui_util::GetStatusInfo(
366 service
, signin
, style
, status_label
, link_label
);
369 MessageType
GetStatusLabelsForNewTabPage(ProfileSyncService
* service
,
370 const SigninManager
& signin
,
371 string16
* status_label
,
372 string16
* link_label
) {
373 DCHECK(status_label
);
375 return sync_ui_util::GetStatusInfoForNewTabPage(
376 service
, signin
, status_label
, link_label
);
379 void GetStatusLabelsForSyncGlobalError(ProfileSyncService
* service
,
380 const SigninManager
& signin
,
381 string16
* menu_label
,
382 string16
* bubble_message
,
383 string16
* bubble_accept_label
) {
385 DCHECK(bubble_message
);
386 DCHECK(bubble_accept_label
);
387 *menu_label
= string16();
388 *bubble_message
= string16();
389 *bubble_accept_label
= string16();
391 if (!service
->HasSyncSetupCompleted())
394 MessageType status
= GetStatus(service
, signin
);
395 if (status
== SYNC_ERROR
) {
396 const AuthError
& auth_error
= service
->GetAuthError();
397 if (auth_error
.state() != AuthError::NONE
) {
398 GetStatusLabelsForAuthError(auth_error
, *service
, NULL
, NULL
,
399 menu_label
, bubble_message
, bubble_accept_label
);
400 // If we have an actionable auth error, display it.
401 if (!menu_label
->empty())
406 // No actionable auth error - display the passphrase error.
407 if (service
->IsPassphraseRequired() &&
408 service
->IsPassphraseRequiredForDecryption()) {
409 // This is not the first machine so ask user to enter passphrase.
410 *menu_label
= l10n_util::GetStringUTF16(
411 IDS_SYNC_PASSPHRASE_ERROR_WRENCH_MENU_ITEM
);
412 string16 product_name
= l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
);
413 *bubble_message
= l10n_util::GetStringFUTF16(
414 IDS_SYNC_PASSPHRASE_ERROR_BUBBLE_VIEW_MESSAGE
, product_name
);
415 *bubble_accept_label
= l10n_util::GetStringUTF16(
416 IDS_SYNC_PASSPHRASE_ERROR_BUBBLE_VIEW_ACCEPT
);
421 MessageType
GetStatus(
422 ProfileSyncService
* service
, const SigninManager
& signin
) {
423 return sync_ui_util::GetStatusInfo(service
, signin
, WITH_HTML
, NULL
, NULL
);
426 string16
GetSyncMenuLabel(
427 ProfileSyncService
* service
, const SigninManager
& signin
) {
428 MessageType type
= GetStatus(service
, signin
);
430 if (type
== sync_ui_util::SYNCED
)
431 return l10n_util::GetStringUTF16(IDS_SYNC_MENU_SYNCED_LABEL
);
432 else if (type
== sync_ui_util::SYNC_ERROR
)
433 return l10n_util::GetStringUTF16(IDS_SYNC_MENU_SYNC_ERROR_LABEL
);
435 return l10n_util::GetStringUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL
);
438 string16
ConstructTime(int64 time_in_int
) {
439 base::Time time
= base::Time::FromInternalValue(time_in_int
);
441 // If time is null the format function returns a time in 1969.
444 return base::TimeFormatFriendlyDateAndTime(time
);
447 std::string
MakeSyncAuthErrorText(
448 const GoogleServiceAuthError::State
& state
) {
450 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS
:
451 case GoogleServiceAuthError::ACCOUNT_DELETED
:
452 case GoogleServiceAuthError::ACCOUNT_DISABLED
:
453 case GoogleServiceAuthError::SERVICE_UNAVAILABLE
:
454 return "INVALID_GAIA_CREDENTIALS";
455 case GoogleServiceAuthError::USER_NOT_SIGNED_UP
:
456 return "USER_NOT_SIGNED_UP";
457 case GoogleServiceAuthError::CONNECTION_FAILED
:
458 return "CONNECTION_FAILED";
460 return std::string();
464 } // namespace sync_ui_util