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/prefs/pref_service.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "chrome/browser/signin/signin_global_error.h"
15 #include "chrome/browser/signin/signin_manager_base.h"
16 #include "chrome/browser/signin/signin_ui_util.h"
17 #include "chrome/browser/sync/profile_sync_service.h"
18 #include "chrome/browser/sync/profile_sync_service_factory.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
22 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chrome/common/pref_names.h"
25 #include "chrome/common/url_constants.h"
26 #include "google_apis/gaia/google_service_auth_error.h"
27 #include "grit/browser_resources.h"
28 #include "grit/chromium_strings.h"
29 #include "grit/generated_resources.h"
30 #include "grit/locale_settings.h"
31 #include "sync/internal_api/public/base/model_type.h"
32 #include "sync/internal_api/public/sessions/sync_session_snapshot.h"
33 #include "sync/protocol/proto_enum_conversions.h"
34 #include "sync/protocol/sync_protocol_error.h"
35 #include "ui/base/l10n/l10n_util.h"
36 #include "ui/base/resource/resource_bundle.h"
38 typedef GoogleServiceAuthError AuthError
;
40 namespace sync_ui_util
{
44 // Returns the message that should be displayed when the user is authenticated
45 // and can connect to the sync server. If the user hasn't yet authenticated, an
46 // empty string is returned.
47 base::string16
GetSyncedStateStatusLabel(ProfileSyncService
* service
,
48 const SigninManagerBase
& signin
,
49 StatusLabelStyle style
) {
50 base::string16 user_name
=
51 base::UTF8ToUTF16(signin
.GetAuthenticatedUsername());
53 if (!user_name
.empty()) {
54 if (!service
|| service
->IsManaged()) {
55 // User is signed in, but sync is disabled.
56 return l10n_util::GetStringFUTF16(IDS_SIGNED_IN_WITH_SYNC_DISABLED
,
58 } else if (service
->IsStartSuppressed()) {
59 // User is signed in, but sync has been stopped.
60 return l10n_util::GetStringFUTF16(IDS_SIGNED_IN_WITH_SYNC_SUPPRESSED
,
65 if (!service
|| !service
->sync_initialized()) {
66 // User is not signed in, or sync is still initializing.
67 return base::string16();
70 DCHECK(!user_name
.empty());
72 // Message may also carry additional advice with an HTML link, if acceptable.
75 return l10n_util::GetStringFUTF16(
76 IDS_SYNC_ACCOUNT_SYNCING_TO_USER
,
79 return l10n_util::GetStringFUTF16(
80 IDS_SYNC_ACCOUNT_SYNCING_TO_USER_WITH_MANAGE_LINK
,
82 base::ASCIIToUTF16(chrome::kSyncGoogleDashboardURL
));
89 void GetStatusForActionableError(
90 const syncer::SyncProtocolError
& error
,
91 base::string16
* status_label
) {
93 switch (error
.action
) {
94 case syncer::STOP_AND_RESTART_SYNC
:
96 l10n_util::GetStringUTF16(IDS_SYNC_STOP_AND_RESTART_SYNC
));
98 case syncer::UPGRADE_CLIENT
:
100 l10n_util::GetStringFUTF16(IDS_SYNC_UPGRADE_CLIENT
,
101 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
)));
103 case syncer::ENABLE_SYNC_ON_ACCOUNT
:
104 status_label
->assign(
105 l10n_util::GetStringUTF16(IDS_SYNC_ENABLE_SYNC_ON_ACCOUNT
));
107 case syncer::CLEAR_USER_DATA_AND_RESYNC
:
108 status_label
->assign(
109 l10n_util::GetStringUTF16(IDS_SYNC_CLEAR_USER_DATA
));
116 // TODO(akalin): Write unit tests for these three functions below.
118 // status_label and link_label must either be both NULL or both non-NULL.
119 MessageType
GetStatusInfo(ProfileSyncService
* service
,
120 const SigninManagerBase
& signin
,
121 StatusLabelStyle style
,
122 base::string16
* status_label
,
123 base::string16
* link_label
) {
124 DCHECK_EQ(status_label
== NULL
, link_label
== NULL
);
126 MessageType
result_type(SYNCED
);
128 if (signin
.GetAuthenticatedUsername().empty())
131 if (!service
|| service
->IsManaged() || service
->HasSyncSetupCompleted() ||
132 service
->IsStartSuppressed()) {
133 // The order or priority is going to be: 1. Unrecoverable errors.
134 // 2. Auth errors. 3. Protocol errors. 4. Passphrase errors.
136 if (service
&& service
->HasUnrecoverableError()) {
138 status_label
->assign(l10n_util::GetStringFUTF16(
139 IDS_SYNC_STATUS_UNRECOVERABLE_ERROR
,
140 l10n_util::GetStringUTF16(IDS_SYNC_UNRECOVERABLE_ERROR_HELP_URL
)));
145 // For auth errors first check if an auth is in progress.
146 if (signin
.AuthInProgress()) {
148 status_label
->assign(
149 l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL
));
154 // Check for sync errors if the sync service is enabled.
156 // Since there is no auth in progress, check for an auth error first.
157 AuthError auth_error
=
158 SigninGlobalError::GetForProfile(service
->profile())->
160 if (auth_error
.state() != AuthError::NONE
) {
161 if (status_label
&& link_label
)
162 signin_ui_util::GetStatusLabelsForAuthError(
163 service
->profile(), signin
, status_label
, link_label
);
167 // We don't have an auth error. Check for an actionable error.
168 ProfileSyncService::Status status
;
169 service
->QueryDetailedSyncStatus(&status
);
170 if (ShouldShowActionOnUI(status
.sync_protocol_error
)) {
172 GetStatusForActionableError(status
.sync_protocol_error
,
178 // Check for a passphrase error.
179 if (service
->IsPassphraseRequired()) {
180 if (service
->IsPassphraseRequiredForDecryption()) {
181 // TODO(lipalani) : Ask tim if this is still needed.
182 // NOT first machine.
183 // Show a link ("needs attention"), but still indicate the
184 // current synced status. Return SYNC_PROMO so that
185 // the configure link will still be shown.
186 if (status_label
&& link_label
) {
187 status_label
->assign(GetSyncedStateStatusLabel(
188 service
, signin
, style
));
190 l10n_util::GetStringUTF16(IDS_SYNC_PASSWORD_SYNC_ATTENTION
));
196 // Check to see if sync has been disabled via the dasboard and needs to be
197 // set up once again.
198 if (service
->IsStartSuppressed() &&
199 status
.sync_protocol_error
.error_type
== syncer::NOT_MY_BIRTHDAY
) {
201 status_label
->assign(GetSyncedStateStatusLabel(service
,
209 // There is no error. Display "Last synced..." message.
211 status_label
->assign(GetSyncedStateStatusLabel(service
, signin
, style
));
214 // Either show auth error information with a link to re-login, auth in prog,
215 // or provide a link to continue with setup.
216 if (service
->FirstSetupInProgress()) {
217 result_type
= PRE_SYNCED
;
218 ProfileSyncService::Status status
;
219 service
->QueryDetailedSyncStatus(&status
);
220 AuthError auth_error
=
221 SigninGlobalError::GetForProfile(
222 service
->profile())->GetLastAuthError();
224 status_label
->assign(
225 l10n_util::GetStringUTF16(IDS_SYNC_NTP_SETUP_IN_PROGRESS
));
227 if (signin
.AuthInProgress()) {
229 status_label
->assign(
230 l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL
));
232 } else if (auth_error
.state() != AuthError::NONE
&&
233 auth_error
.state() != AuthError::TWO_FACTOR
) {
234 if (status_label
&& link_label
) {
235 status_label
->clear();
236 signin_ui_util::GetStatusLabelsForAuthError(
237 service
->profile(), signin
, status_label
, link_label
);
239 result_type
= SYNC_ERROR
;
241 } else if (service
->HasUnrecoverableError()) {
242 result_type
= SYNC_ERROR
;
243 ProfileSyncService::Status status
;
244 service
->QueryDetailedSyncStatus(&status
);
245 if (ShouldShowActionOnUI(status
.sync_protocol_error
)) {
247 GetStatusForActionableError(status
.sync_protocol_error
,
250 } else if (status_label
) {
251 status_label
->assign(l10n_util::GetStringUTF16(IDS_SYNC_SETUP_ERROR
));
253 } else if (!signin
.GetAuthenticatedUsername().empty()) {
254 // The user is signed in, but sync has been stopped.
256 base::string16 label
= l10n_util::GetStringFUTF16(
257 IDS_SIGNED_IN_WITH_SYNC_SUPPRESSED
,
258 base::UTF8ToUTF16(signin
.GetAuthenticatedUsername()));
259 status_label
->assign(label
);
260 result_type
= PRE_SYNCED
;
267 // Returns the status info for use on the new tab page, where we want slightly
268 // different information than in the settings panel.
269 MessageType
GetStatusInfoForNewTabPage(ProfileSyncService
* service
,
270 const SigninManagerBase
& signin
,
271 base::string16
* status_label
,
272 base::string16
* link_label
) {
273 DCHECK(status_label
);
276 if (service
->HasSyncSetupCompleted() &&
277 service
->IsPassphraseRequired()) {
278 if (service
->passphrase_required_reason() == syncer::REASON_ENCRYPTION
) {
279 // First machine migrating to passwords. Show as a promotion.
280 if (status_label
&& link_label
) {
281 status_label
->assign(
282 l10n_util::GetStringFUTF16(
283 IDS_SYNC_NTP_PASSWORD_PROMO
,
284 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
)));
286 l10n_util::GetStringUTF16(IDS_SYNC_NTP_PASSWORD_ENABLE
));
290 // NOT first machine.
291 // Show a link and present as an error ("needs attention").
292 if (status_label
&& link_label
) {
293 status_label
->assign(base::string16());
295 l10n_util::GetStringUTF16(IDS_SYNC_CONFIGURE_ENCRYPTION
));
301 // Fallback to default.
302 return GetStatusInfo(service
, signin
, WITH_HTML
, status_label
, link_label
);
307 MessageType
GetStatusLabels(ProfileSyncService
* service
,
308 const SigninManagerBase
& signin
,
309 StatusLabelStyle style
,
310 base::string16
* status_label
,
311 base::string16
* link_label
) {
312 DCHECK(status_label
);
314 return sync_ui_util::GetStatusInfo(
315 service
, signin
, style
, status_label
, link_label
);
318 MessageType
GetStatusLabelsForNewTabPage(ProfileSyncService
* service
,
319 const SigninManagerBase
& signin
,
320 base::string16
* status_label
,
321 base::string16
* link_label
) {
322 DCHECK(status_label
);
324 return sync_ui_util::GetStatusInfoForNewTabPage(
325 service
, signin
, status_label
, link_label
);
328 void GetStatusLabelsForSyncGlobalError(ProfileSyncService
* service
,
329 const SigninManagerBase
& signin
,
330 base::string16
* menu_label
,
331 base::string16
* bubble_message
,
332 base::string16
* bubble_accept_label
) {
334 DCHECK(bubble_message
);
335 DCHECK(bubble_accept_label
);
336 *menu_label
= base::string16();
337 *bubble_message
= base::string16();
338 *bubble_accept_label
= base::string16();
340 // Only display an error if we've completed sync setup.
341 if (!service
->HasSyncSetupCompleted())
344 // Display a passphrase error if we have one.
345 if (service
->IsPassphraseRequired() &&
346 service
->IsPassphraseRequiredForDecryption()) {
347 // This is not the first machine so ask user to enter passphrase.
348 *menu_label
= l10n_util::GetStringUTF16(
349 IDS_SYNC_PASSPHRASE_ERROR_WRENCH_MENU_ITEM
);
350 base::string16 product_name
= l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
);
351 *bubble_message
= l10n_util::GetStringFUTF16(
352 IDS_SYNC_PASSPHRASE_ERROR_BUBBLE_VIEW_MESSAGE
, product_name
);
353 *bubble_accept_label
= l10n_util::GetStringUTF16(
354 IDS_SYNC_PASSPHRASE_ERROR_BUBBLE_VIEW_ACCEPT
);
359 MessageType
GetStatus(
360 ProfileSyncService
* service
, const SigninManagerBase
& signin
) {
361 return sync_ui_util::GetStatusInfo(service
, signin
, WITH_HTML
, NULL
, NULL
);
364 base::string16
ConstructTime(int64 time_in_int
) {
365 base::Time time
= base::Time::FromInternalValue(time_in_int
);
367 // If time is null the format function returns a time in 1969.
369 return base::string16();
370 return base::TimeFormatFriendlyDateAndTime(time
);
373 } // namespace sync_ui_util