• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/sync_ui_util.h"
6 
7 #include "base/command_line.h"
8 #include "base/i18n/number_formatting.h"
9 #include "base/i18n/time_formatting.h"
10 #include "base/string_util.h"
11 #include "base/utf_string_conversions.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/sync/profile_sync_service.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_window.h"
16 #include "chrome/browser/ui/options/options_window.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/net/gaia/google_service_auth_error.h"
19 #include "chrome/common/url_constants.h"
20 #include "grit/browser_resources.h"
21 #include "grit/chromium_strings.h"
22 #include "grit/generated_resources.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "ui/base/resource/resource_bundle.h"
25 
26 typedef GoogleServiceAuthError AuthError;
27 
28 namespace sync_ui_util {
29 
30 namespace {
31 
32 // Given an authentication state, this helper function returns the appropriate
33 // status message and, if necessary, the text that should appear in the
34 // re-login link.
GetStatusLabelsForAuthError(const AuthError & auth_error,ProfileSyncService * service,string16 * status_label,string16 * link_label)35 void GetStatusLabelsForAuthError(const AuthError& auth_error,
36     ProfileSyncService* service, string16* status_label,
37     string16* link_label) {
38   if (link_label)
39     link_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_LINK_LABEL));
40   if (auth_error.state() == AuthError::INVALID_GAIA_CREDENTIALS ||
41       auth_error.state() == AuthError::ACCOUNT_DELETED ||
42       auth_error.state() == AuthError::ACCOUNT_DISABLED) {
43     // If the user name is empty then the first login failed, otherwise the
44     // credentials are out-of-date.
45     if (service->GetAuthenticatedUsername().empty())
46       status_label->assign(
47           l10n_util::GetStringUTF16(IDS_SYNC_INVALID_USER_CREDENTIALS));
48     else
49       status_label->assign(
50           l10n_util::GetStringUTF16(IDS_SYNC_LOGIN_INFO_OUT_OF_DATE));
51   } else if (auth_error.state() == AuthError::SERVICE_UNAVAILABLE) {
52     DCHECK(service->GetAuthenticatedUsername().empty());
53     status_label->assign(
54         l10n_util::GetStringUTF16(IDS_SYNC_SERVICE_UNAVAILABLE));
55   } else if (auth_error.state() == AuthError::CONNECTION_FAILED) {
56     // Note that there is little the user can do if the server is not
57     // reachable. Since attempting to re-connect is done automatically by
58     // the Syncer, we do not show the (re)login link.
59     status_label->assign(
60         l10n_util::GetStringFUTF16(IDS_SYNC_SERVER_IS_UNREACHABLE,
61                               l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
62     if (link_label)
63       link_label->clear();
64   } else {
65     status_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_ERROR_SIGNING_IN));
66   }
67 }
68 
69 // Returns the message that should be displayed when the user is authenticated
70 // and can connect to the sync server. If the user hasn't yet authenticated, an
71 // empty string is returned.
GetSyncedStateStatusLabel(ProfileSyncService * service)72 string16 GetSyncedStateStatusLabel(ProfileSyncService* service) {
73   string16 label;
74   string16 user_name(service->GetAuthenticatedUsername());
75   if (user_name.empty())
76     return label;
77 
78   const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
79   return l10n_util::GetStringFUTF16(
80       browser_command_line.HasSwitch(switches::kMultiProfiles) ?
81           IDS_PROFILES_SYNCED_TO_USER_WITH_TIME :
82           IDS_SYNC_ACCOUNT_SYNCED_TO_USER_WITH_TIME,
83       user_name,
84       service->GetLastSyncedTimeString());
85 }
86 
87 // TODO(akalin): Write unit tests for these three functions below.
88 
89 // status_label and link_label must either be both NULL or both non-NULL.
GetStatusInfo(ProfileSyncService * service,string16 * status_label,string16 * link_label)90 MessageType GetStatusInfo(ProfileSyncService* service,
91                           string16* status_label,
92                           string16* link_label) {
93   DCHECK_EQ(status_label == NULL, link_label == NULL);
94 
95   MessageType result_type(SYNCED);
96 
97   if (!service) {
98     return PRE_SYNCED;
99   }
100 
101   if (service->HasSyncSetupCompleted()) {
102     ProfileSyncService::Status status(service->QueryDetailedSyncStatus());
103     const AuthError& auth_error = service->GetAuthError();
104 
105     // Either show auth error information with a link to re-login, auth in prog,
106     // or note that everything is OK with the last synced time.
107     if (status.authenticated && !service->observed_passphrase_required()) {
108       // Everything is peachy.
109       if (status_label) {
110         status_label->assign(GetSyncedStateStatusLabel(service));
111       }
112       DCHECK_EQ(auth_error.state(), AuthError::NONE);
113     } else if (service->UIShouldDepictAuthInProgress()) {
114       if (status_label) {
115         status_label->assign(
116           l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL));
117       }
118       result_type = PRE_SYNCED;
119     } else if (service->observed_passphrase_required()) {
120       if (service->passphrase_required_for_decryption()) {
121         // NOT first machine.
122         // Show a link ("needs attention"), but still indicate the
123         // current synced status.  Return SYNC_PROMO so that
124         // the configure link will still be shown.
125         if (status_label && link_label) {
126           status_label->assign(GetSyncedStateStatusLabel(service));
127           link_label->assign(
128               l10n_util::GetStringUTF16(IDS_SYNC_PASSWORD_SYNC_ATTENTION));
129         }
130         result_type = SYNC_PROMO;
131       } else {
132         // First machine.  Don't show promotion, just show everything
133         // normal.
134         if (status_label)
135           status_label->assign(GetSyncedStateStatusLabel(service));
136       }
137     } else if (auth_error.state() != AuthError::NONE) {
138       if (status_label && link_label) {
139         GetStatusLabelsForAuthError(auth_error, service,
140                                     status_label, link_label);
141       }
142       result_type = SYNC_ERROR;
143     }
144   } else {
145     // Either show auth error information with a link to re-login, auth in prog,
146     // or provide a link to continue with setup.
147     result_type = PRE_SYNCED;
148     if (service->SetupInProgress()) {
149       ProfileSyncService::Status status(service->QueryDetailedSyncStatus());
150       const AuthError& auth_error = service->GetAuthError();
151       if (status_label) {
152         status_label->assign(
153             l10n_util::GetStringUTF16(IDS_SYNC_NTP_SETUP_IN_PROGRESS));
154       }
155       if (service->UIShouldDepictAuthInProgress()) {
156         if (status_label) {
157           status_label->assign(
158               l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL));
159         }
160       } else if (auth_error.state() != AuthError::NONE) {
161         if (status_label) {
162           status_label->clear();
163           GetStatusLabelsForAuthError(auth_error, service, status_label, NULL);
164         }
165         result_type = SYNC_ERROR;
166       } else if (!status.authenticated) {
167         if (status_label) {
168           status_label->assign(
169               l10n_util::GetStringUTF16(IDS_SYNC_ACCOUNT_DETAILS_NOT_ENTERED));
170         }
171       }
172     } else if (service->unrecoverable_error_detected()) {
173       result_type = SYNC_ERROR;
174       if (status_label) {
175         status_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_SETUP_ERROR));
176       }
177     }
178   }
179   return result_type;
180 }
181 
182 // Returns the status info for use on the new tab page, where we want slightly
183 // different information than in the settings panel.
GetStatusInfoForNewTabPage(ProfileSyncService * service,string16 * status_label,string16 * link_label)184 MessageType GetStatusInfoForNewTabPage(ProfileSyncService* service,
185                                        string16* status_label,
186                                        string16* link_label) {
187   DCHECK(status_label);
188   DCHECK(link_label);
189 
190   if (service->HasSyncSetupCompleted() &&
191       service->observed_passphrase_required()) {
192     if (!service->passphrase_required_for_decryption()) {
193       // First machine migrating to passwords.  Show as a promotion.
194       if (status_label && link_label) {
195         status_label->assign(
196             l10n_util::GetStringFUTF16(
197                 IDS_SYNC_NTP_PASSWORD_PROMO,
198                 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
199         link_label->assign(
200             l10n_util::GetStringUTF16(IDS_SYNC_NTP_PASSWORD_ENABLE));
201       }
202       return SYNC_PROMO;
203     } else {
204       // NOT first machine.
205       // Show a link and present as an error ("needs attention").
206       if (status_label && link_label) {
207         status_label->assign(string16());
208         link_label->assign(
209             l10n_util::GetStringUTF16(IDS_SYNC_CONFIGURE_ENCRYPTION));
210       }
211       return SYNC_ERROR;
212     }
213   }
214 
215   // Fallback to default.
216   return GetStatusInfo(service, status_label, link_label);
217 }
218 
219 }  // namespace
220 
GetStatusLabels(ProfileSyncService * service,string16 * status_label,string16 * link_label)221 MessageType GetStatusLabels(ProfileSyncService* service,
222                             string16* status_label,
223                             string16* link_label) {
224   DCHECK(status_label);
225   DCHECK(link_label);
226   return sync_ui_util::GetStatusInfo(service, status_label, link_label);
227 }
228 
GetStatusLabelsForNewTabPage(ProfileSyncService * service,string16 * status_label,string16 * link_label)229 MessageType GetStatusLabelsForNewTabPage(ProfileSyncService* service,
230                                          string16* status_label,
231                                          string16* link_label) {
232   DCHECK(status_label);
233   DCHECK(link_label);
234   return sync_ui_util::GetStatusInfoForNewTabPage(
235       service, status_label, link_label);
236 }
237 
GetStatus(ProfileSyncService * service)238 MessageType GetStatus(ProfileSyncService* service) {
239   return sync_ui_util::GetStatusInfo(service, NULL, NULL);
240 }
241 
ShouldShowSyncErrorButton(ProfileSyncService * service)242 bool ShouldShowSyncErrorButton(ProfileSyncService* service) {
243   return service &&
244          ((!service->IsManaged() &&
245            service->HasSyncSetupCompleted()) &&
246          (GetStatus(service) == sync_ui_util::SYNC_ERROR ||
247           service->observed_passphrase_required()));
248 }
249 
GetSyncMenuLabel(ProfileSyncService * service)250 string16 GetSyncMenuLabel(ProfileSyncService* service) {
251   MessageType type = GetStatus(service);
252 
253   if (type == sync_ui_util::SYNCED)
254     return l10n_util::GetStringUTF16(IDS_SYNC_MENU_SYNCED_LABEL);
255   else if (type == sync_ui_util::SYNC_ERROR)
256     return l10n_util::GetStringUTF16(IDS_SYNC_MENU_SYNC_ERROR_LABEL);
257   else
258     return l10n_util::GetStringUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL);
259 }
260 
OpenSyncMyBookmarksDialog(Profile * profile,Browser * browser,ProfileSyncService::SyncEventCodes code)261 void OpenSyncMyBookmarksDialog(Profile* profile,
262                                Browser* browser,
263                                ProfileSyncService::SyncEventCodes code) {
264   ProfileSyncService* service =
265     profile->GetOriginalProfile()->GetProfileSyncService();
266   if (!service || !service->IsSyncEnabled()) {
267     LOG(DFATAL) << "OpenSyncMyBookmarksDialog called with sync disabled";
268     return;
269   }
270 
271   if (service->HasSyncSetupCompleted()) {
272     bool create_window = browser == NULL;
273     if (create_window)
274       browser = Browser::Create(profile);
275     browser->ShowOptionsTab(chrome::kPersonalOptionsSubPage);
276     if (create_window)
277       browser->window()->Show();
278   } else {
279     service->ShowLoginDialog(NULL);
280     ProfileSyncService::SyncEvent(code);  // UMA stats
281   }
282 }
283 
AddBoolSyncDetail(ListValue * details,const std::string & stat_name,bool stat_value)284 void AddBoolSyncDetail(ListValue* details,
285                        const std::string& stat_name,
286                        bool stat_value) {
287   DictionaryValue* val = new DictionaryValue;
288   val->SetString("stat_name", stat_name);
289   val->SetBoolean("stat_value", stat_value);
290   details->Append(val);
291 }
292 
AddIntSyncDetail(ListValue * details,const std::string & stat_name,int64 stat_value)293 void AddIntSyncDetail(ListValue* details, const std::string& stat_name,
294                       int64 stat_value) {
295   DictionaryValue* val = new DictionaryValue;
296   val->SetString("stat_name", stat_name);
297   val->SetString("stat_value", base::FormatNumber(stat_value));
298   details->Append(val);
299 }
300 
ConstructTime(int64 time_in_int)301 string16 ConstructTime(int64 time_in_int) {
302   base::Time time = base::Time::FromInternalValue(time_in_int);
303 
304   // If time is null the format function returns a time in 1969.
305   if (time.is_null())
306     return string16();
307   return base::TimeFormatFriendlyDateAndTime(time);
308 }
309 
MakeSyncAuthErrorText(const GoogleServiceAuthError::State & state)310 std::string MakeSyncAuthErrorText(
311     const GoogleServiceAuthError::State& state) {
312   switch (state) {
313     case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
314     case GoogleServiceAuthError::ACCOUNT_DELETED:
315     case GoogleServiceAuthError::ACCOUNT_DISABLED:
316     case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
317       return "INVALID_GAIA_CREDENTIALS";
318     case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
319       return "USER_NOT_SIGNED_UP";
320     case GoogleServiceAuthError::CONNECTION_FAILED:
321       return "CONNECTION_FAILED";
322     default:
323       return std::string();
324   }
325 }
326 
ConstructAboutInformation(ProfileSyncService * service,DictionaryValue * strings)327 void ConstructAboutInformation(ProfileSyncService* service,
328                                DictionaryValue* strings) {
329   CHECK(strings);
330   if (!service || !service->HasSyncSetupCompleted()) {
331     strings->SetString("summary", "SYNC DISABLED");
332   } else {
333     sync_api::SyncManager::Status full_status(
334         service->QueryDetailedSyncStatus());
335 
336     strings->SetString("service_url", service->sync_service_url().spec());
337     strings->SetString("summary",
338                        ProfileSyncService::BuildSyncStatusSummaryText(
339                        full_status.summary));
340 
341     strings->Set("authenticated",
342                  new FundamentalValue(full_status.authenticated));
343     strings->SetString("auth_problem",
344                        sync_ui_util::MakeSyncAuthErrorText(
345                        service->GetAuthError().state()));
346 
347     strings->SetString("time_since_sync", service->GetLastSyncedTimeString());
348 
349     ListValue* details = new ListValue();
350     strings->Set("details", details);
351     sync_ui_util::AddBoolSyncDetail(details,
352                                     "Server Up",
353                                     full_status.server_up);
354     sync_ui_util::AddBoolSyncDetail(details,
355                                     "Server Reachable",
356                                     full_status.server_reachable);
357     sync_ui_util::AddBoolSyncDetail(details,
358                                     "Server Broken",
359                                     full_status.server_broken);
360     sync_ui_util::AddBoolSyncDetail(details,
361                                     "Notifications Enabled",
362                                     full_status.notifications_enabled);
363     sync_ui_util::AddIntSyncDetail(details,
364                                    "Notifications Received",
365                                    full_status.notifications_received);
366     sync_ui_util::AddIntSyncDetail(details,
367                                    "Notifications Sent",
368                                    full_status.notifications_sent);
369     sync_ui_util::AddIntSyncDetail(details,
370                                    "Unsynced Count",
371                                    full_status.unsynced_count);
372     sync_ui_util::AddIntSyncDetail(details,
373                                    "Conflicting Count",
374                                    full_status.conflicting_count);
375     sync_ui_util::AddBoolSyncDetail(details, "Syncing", full_status.syncing);
376     sync_ui_util::AddBoolSyncDetail(details,
377                                     "Initial Sync Ended",
378                                     full_status.initial_sync_ended);
379     sync_ui_util::AddBoolSyncDetail(details,
380                                     "Syncer Stuck",
381                                     full_status.syncer_stuck);
382     sync_ui_util::AddIntSyncDetail(details,
383                                    "Updates Available",
384                                    full_status.updates_available);
385     sync_ui_util::AddIntSyncDetail(details,
386                                    "Updates Downloaded (All)",
387                                    full_status.updates_received);
388     sync_ui_util::AddIntSyncDetail(details,
389                                    "Updates Downloaded (Tombstones)",
390                                    full_status.tombstone_updates_received);
391     sync_ui_util::AddBoolSyncDetail(details,
392                                     "Disk Full",
393                                     full_status.disk_full);
394     sync_ui_util::AddIntSyncDetail(details,
395                                    "Max Consecutive Errors",
396                                    full_status.max_consecutive_errors);
397 
398     if (service->unrecoverable_error_detected()) {
399       strings->Set("unrecoverable_error_detected", new FundamentalValue(true));
400       strings->SetString("unrecoverable_error_message",
401                          service->unrecoverable_error_message());
402       tracked_objects::Location loc(service->unrecoverable_error_location());
403       std::string location_str;
404       loc.Write(true, true, &location_str);
405       strings->SetString("unrecoverable_error_location", location_str);
406     } else if (!service->sync_initialized()) {
407       strings->SetString("summary", "Sync not yet initialized");
408     } else {
409       browser_sync::ModelSafeRoutingInfo routes;
410       service->GetModelSafeRoutingInfo(&routes);
411       ListValue* routing_info = new ListValue();
412       strings->Set("routing_info", routing_info);
413       browser_sync::ModelSafeRoutingInfo::const_iterator it = routes.begin();
414       for (; it != routes.end(); ++it) {
415         DictionaryValue* val = new DictionaryValue;
416         val->SetString("model_type", ModelTypeToString(it->first));
417         val->SetString("group", ModelSafeGroupToString(it->second));
418         routing_info->Append(val);
419       }
420 
421       sync_ui_util::AddBoolSyncDetail(details,
422           "Autofill Migrated",
423           service->GetAutofillMigrationState() ==
424           syncable::MIGRATED);
425       syncable::AutofillMigrationDebugInfo info =
426           service->GetAutofillMigrationDebugInfo();
427 
428       sync_ui_util::AddIntSyncDetail(details,
429                                      "Bookmarks created during migration",
430                                      info.bookmarks_added_during_migration);
431       sync_ui_util::AddIntSyncDetail(details,
432           "Autofill entries created during migration",
433           info.autofill_entries_added_during_migration);
434       sync_ui_util::AddIntSyncDetail(details,
435           "Autofill Profiles created during migration",
436           info.autofill_profile_added_during_migration);
437 
438       DictionaryValue* val = new DictionaryValue;
439       val->SetString("stat_name", "Autofill Migration Time");
440       val->SetString("stat_value", ConstructTime(info.autofill_migration_time));
441       details->Append(val);
442     }
443   }
444 }
445 
446 }  // namespace sync_ui_util
447