Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / sync / about_sync_util.cc
blob04a2a3045f91252cd03f367be97094e39be92d46
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/about_sync_util.h"
7 #include <string>
9 #include "base/strings/string16.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/values.h"
12 #include "chrome/browser/sync/profile_sync_service.h"
13 #include "chrome/common/channel_info.h"
14 #include "components/signin/core/browser/signin_manager.h"
15 #include "components/version_info/version_info.h"
16 #include "sync/api/time.h"
17 #include "sync/internal_api/public/util/sync_string_conversions.h"
18 #include "sync/protocol/proto_enum_conversions.h"
20 using base::DictionaryValue;
21 using base::ListValue;
23 const char kIdentityTitle[] = "Identity";
24 const char kDetailsKey[] = "details";
26 namespace {
28 // Creates a 'section' for display on about:sync, consisting of a title and a
29 // list of fields. Returns a pointer to the new section. Note that
30 // |parent_list|, not the caller, owns the newly added section.
31 base::ListValue* AddSection(base::ListValue* parent_list,
32 const std::string& title) {
33 base::DictionaryValue* section = new base::DictionaryValue();
34 base::ListValue* section_contents = new base::ListValue();
35 section->SetString("title", title);
36 section->Set("data", section_contents);
37 section->SetBoolean("is_sensitive", false);
38 parent_list->Append(section);
39 return section_contents;
42 // Same as AddSection, but for data that should be elided when dumped into text
43 // form and posted in a public forum (e.g. unique identifiers).
44 base::ListValue* AddSensitiveSection(base::ListValue* parent_list,
45 const std::string& title) {
46 base::DictionaryValue* section = new base::DictionaryValue();
47 base::ListValue* section_contents = new base::ListValue();
48 section->SetString("title", title);
49 section->Set("data", section_contents);
50 section->SetBoolean("is_sensitive", true);
51 parent_list->Append(section);
52 return section_contents;
55 // The following helper classes help manage the about:sync fields which will be
56 // populated in method in ConstructAboutInformation.
58 // Each instance of one of thse classes indicates a field in about:sync. Each
59 // field will be serialized to a DictionaryValue with entries for 'stat_name',
60 // 'stat_value' and 'is_valid'.
62 class StringSyncStat {
63 public:
64 StringSyncStat(base::ListValue* section, const std::string& key);
65 void SetValue(const std::string& value);
66 void SetValue(const base::string16& value);
68 private:
69 // Owned by the |section| passed in during construction.
70 base::DictionaryValue* stat_;
73 StringSyncStat::StringSyncStat(base::ListValue* section,
74 const std::string& key) {
75 stat_ = new base::DictionaryValue();
76 stat_->SetString("stat_name", key);
77 stat_->SetString("stat_value", "Uninitialized");
78 stat_->SetBoolean("is_valid", false);
79 section->Append(stat_);
82 void StringSyncStat::SetValue(const std::string& value) {
83 stat_->SetString("stat_value", value);
84 stat_->SetBoolean("is_valid", true);
87 void StringSyncStat::SetValue(const base::string16& value) {
88 stat_->SetString("stat_value", value);
89 stat_->SetBoolean("is_valid", true);
92 class BoolSyncStat {
93 public:
94 BoolSyncStat(base::ListValue* section, const std::string& key);
95 void SetValue(bool value);
97 private:
98 // Owned by the |section| passed in during construction.
99 base::DictionaryValue* stat_;
102 BoolSyncStat::BoolSyncStat(base::ListValue* section, const std::string& key) {
103 stat_ = new base::DictionaryValue();
104 stat_->SetString("stat_name", key);
105 stat_->SetBoolean("stat_value", false);
106 stat_->SetBoolean("is_valid", false);
107 section->Append(stat_);
110 void BoolSyncStat::SetValue(bool value) {
111 stat_->SetBoolean("stat_value", value);
112 stat_->SetBoolean("is_valid", true);
115 class IntSyncStat {
116 public:
117 IntSyncStat(base::ListValue* section, const std::string& key);
118 void SetValue(int value);
120 private:
121 // Owned by the |section| passed in during construction.
122 base::DictionaryValue* stat_;
125 IntSyncStat::IntSyncStat(base::ListValue* section, const std::string& key) {
126 stat_ = new base::DictionaryValue();
127 stat_->SetString("stat_name", key);
128 stat_->SetInteger("stat_value", 0);
129 stat_->SetBoolean("is_valid", false);
130 section->Append(stat_);
133 void IntSyncStat::SetValue(int value) {
134 stat_->SetInteger("stat_value", value);
135 stat_->SetBoolean("is_valid", true);
138 // Returns a string describing the chrome version environment. Version format:
139 // <Build Info> <OS> <Version number> (<Last change>)<channel or "-devel">
140 // If version information is unavailable, returns "invalid."
141 // TODO(zea): this approximately matches MakeUserAgentForSyncApi in
142 // sync_backend_host.cc. Unify the two if possible.
143 std::string GetVersionString() {
144 // Build a version string that matches MakeUserAgentForSyncApi with the
145 // addition of channel info and proper OS names.
146 // chrome::GetChannelString() returns empty string for stable channel or
147 // unofficial builds, the channel string otherwise. We want to have "-devel"
148 // for unofficial builds only.
149 std::string version_modifier = chrome::GetChannelString();
150 if (version_modifier.empty()) {
151 if (chrome::GetChannel() != version_info::Channel::STABLE) {
152 version_modifier = "-devel";
154 } else {
155 version_modifier = " " + version_modifier;
157 return version_info::GetProductName() + " " + version_info::GetOSType() +
158 " " + version_info::GetVersionNumber() + " (" +
159 version_info::GetLastChange() + ")" + version_modifier;
162 std::string GetTimeStr(base::Time time, const std::string& default_msg) {
163 std::string time_str;
164 if (time.is_null())
165 time_str = default_msg;
166 else
167 time_str = syncer::GetTimeDebugString(time);
168 return time_str;
171 std::string GetConnectionStatus(
172 const ProfileSyncService::SyncTokenStatus& status) {
173 std::string message;
174 switch (status.connection_status) {
175 case syncer::CONNECTION_NOT_ATTEMPTED:
176 base::StringAppendF(&message, "not attempted");
177 break;
178 case syncer::CONNECTION_OK:
179 base::StringAppendF(
180 &message, "OK since %s",
181 GetTimeStr(status.connection_status_update_time, "n/a").c_str());
182 break;
183 case syncer::CONNECTION_AUTH_ERROR:
184 base::StringAppendF(
185 &message, "auth error since %s",
186 GetTimeStr(status.connection_status_update_time, "n/a").c_str());
187 break;
188 case syncer::CONNECTION_SERVER_ERROR:
189 base::StringAppendF(
190 &message, "server error since %s",
191 GetTimeStr(status.connection_status_update_time, "n/a").c_str());
192 break;
193 default:
194 NOTREACHED();
196 return message;
199 } // namespace
201 namespace sync_ui_util {
203 // This function both defines the structure of the message to be returned and
204 // its contents. Most of the message consists of simple fields in about:sync
205 // which are grouped into sections and populated with the help of the SyncStat
206 // classes defined above.
207 scoped_ptr<base::DictionaryValue> ConstructAboutInformation(
208 ProfileSyncService* service) {
209 scoped_ptr<base::DictionaryValue> about_info(new base::DictionaryValue());
211 // 'details': A list of sections.
212 base::ListValue* stats_list = new base::ListValue();
214 // The following lines define the sections and their fields. For each field,
215 // a class is instantiated, which allows us to reference the fields in
216 // 'setter' code later on in this function.
217 base::ListValue* section_summary = AddSection(stats_list, "Summary");
218 StringSyncStat summary_string(section_summary, "Summary");
220 base::ListValue* section_version = AddSection(stats_list, "Version Info");
221 StringSyncStat client_version(section_version, "Client Version");
222 StringSyncStat server_url(section_version, "Server URL");
224 base::ListValue* section_identity =
225 AddSensitiveSection(stats_list, kIdentityTitle);
226 StringSyncStat sync_id(section_identity, "Sync Client ID");
227 StringSyncStat invalidator_id(section_identity, "Invalidator Client ID");
228 StringSyncStat username(section_identity, "Username");
230 base::ListValue* section_credentials = AddSection(stats_list, "Credentials");
231 StringSyncStat request_token_time(section_credentials, "Requested Token");
232 StringSyncStat receive_token_time(section_credentials, "Received Token");
233 StringSyncStat token_request_status(section_credentials,
234 "Token Request Status");
235 StringSyncStat next_token_request(section_credentials,
236 "Next Token Request");
238 base::ListValue* section_local = AddSection(stats_list, "Local State");
239 StringSyncStat server_connection(section_local,
240 "Server Connection");
241 StringSyncStat last_synced(section_local, "Last Synced");
242 BoolSyncStat is_setup_complete(section_local,
243 "Sync First-Time Setup Complete");
244 StringSyncStat backend_initialization(section_local,
245 "Sync Backend Initialization");
246 BoolSyncStat is_syncing(section_local, "Syncing");
248 base::ListValue* section_network = AddSection(stats_list, "Network");
249 BoolSyncStat is_throttled(section_network, "Throttled");
250 StringSyncStat retry_time(section_network, "Retry time (maybe stale)");
251 BoolSyncStat are_notifications_enabled(section_network,
252 "Notifications Enabled");
254 base::ListValue* section_encryption = AddSection(stats_list, "Encryption");
255 BoolSyncStat is_using_explicit_passphrase(section_encryption,
256 "Explicit Passphrase");
257 BoolSyncStat is_passphrase_required(section_encryption,
258 "Passphrase Required");
259 BoolSyncStat is_cryptographer_ready(section_encryption,
260 "Cryptographer Ready");
261 BoolSyncStat has_pending_keys(section_encryption,
262 "Cryptographer Has Pending Keys");
263 StringSyncStat encrypted_types(section_encryption, "Encrypted Types");
264 BoolSyncStat has_keystore_key(section_encryption, "Has Keystore Key");
265 StringSyncStat keystore_migration_time(section_encryption,
266 "Keystore Migration Time");
267 StringSyncStat passphrase_type(section_encryption,
268 "Passphrase Type");
269 StringSyncStat passphrase_time(section_encryption,
270 "Passphrase Time");
272 base::ListValue* section_last_session = AddSection(
273 stats_list, "Status from Last Completed Session");
274 StringSyncStat session_source(section_last_session, "Sync Source");
275 StringSyncStat get_key_result(section_last_session, "GetKey Step Result");
276 StringSyncStat download_result(section_last_session, "Download Step Result");
277 StringSyncStat commit_result(section_last_session, "Commit Step Result");
279 base::ListValue* section_counters = AddSection(stats_list, "Running Totals");
280 IntSyncStat notifications_received(section_counters,
281 "Notifications Received");
282 IntSyncStat updates_received(section_counters, "Updates Downloaded");
283 IntSyncStat tombstone_updates(section_counters, "Tombstone Updates");
284 IntSyncStat reflected_updates(section_counters, "Reflected Updates");
285 IntSyncStat successful_commits(section_counters, "Successful Commits");
286 IntSyncStat conflicts_resolved_local_wins(section_counters,
287 "Conflicts Resolved: Client Wins");
288 IntSyncStat conflicts_resolved_server_wins(section_counters,
289 "Conflicts Resolved: Server Wins");
291 base::ListValue *section_this_cycle = AddSection(stats_list,
292 "Transient Counters (this cycle)");
293 IntSyncStat encryption_conflicts(section_this_cycle, "Encryption Conflicts");
294 IntSyncStat hierarchy_conflicts(section_this_cycle, "Hierarchy Conflicts");
295 IntSyncStat server_conflicts(section_this_cycle, "Server Conflicts");
296 IntSyncStat committed_items(section_this_cycle, "Committed Items");
298 base::ListValue* section_that_cycle = AddSection(
299 stats_list, "Transient Counters (last cycle of last completed session)");
300 IntSyncStat updates_downloaded(section_that_cycle, "Updates Downloaded");
301 IntSyncStat committed_count(section_that_cycle, "Committed Count");
302 IntSyncStat entries(section_that_cycle, "Entries");
304 base::ListValue* section_nudge_info = AddSection(
305 stats_list, "Nudge Source Counters");
306 IntSyncStat nudge_source_notification(
307 section_nudge_info, "Server Invalidations");
308 IntSyncStat nudge_source_local(section_nudge_info, "Local Changes");
309 IntSyncStat nudge_source_local_refresh(section_nudge_info, "Local Refreshes");
311 // This list of sections belongs in the 'details' field of the returned
312 // message.
313 about_info->Set(kDetailsKey, stats_list);
315 // Populate all the fields we declared above.
316 client_version.SetValue(GetVersionString());
318 if (!service) {
319 summary_string.SetValue("Sync service does not exist");
320 return about_info.Pass();
323 syncer::SyncStatus full_status;
324 bool is_status_valid = service->QueryDetailedSyncStatus(&full_status);
325 bool sync_active = service->IsSyncActive();
326 const syncer::sessions::SyncSessionSnapshot& snapshot =
327 service->GetLastSessionSnapshot();
329 if (is_status_valid)
330 summary_string.SetValue(service->QuerySyncStatusSummaryString());
332 server_url.SetValue(service->sync_service_url().spec());
334 if (is_status_valid && !full_status.sync_id.empty())
335 sync_id.SetValue(full_status.sync_id);
336 if (is_status_valid && !full_status.invalidator_client_id.empty())
337 invalidator_id.SetValue(full_status.invalidator_client_id);
338 if (service->signin())
339 username.SetValue(service->signin()->GetAuthenticatedUsername());
341 const ProfileSyncService::SyncTokenStatus& token_status =
342 service->GetSyncTokenStatus();
343 server_connection.SetValue(GetConnectionStatus(token_status));
344 request_token_time.SetValue(GetTimeStr(token_status.token_request_time,
345 "n/a"));
346 receive_token_time.SetValue(GetTimeStr(token_status.token_receive_time,
347 "n/a"));
348 std::string err = token_status.last_get_token_error.error_message();
349 token_request_status.SetValue(err.empty() ? "OK" : err);
350 next_token_request.SetValue(
351 GetTimeStr(token_status.next_token_request_time, "not scheduled"));
353 last_synced.SetValue(service->GetLastSyncedTimeString());
354 is_setup_complete.SetValue(service->HasSyncSetupCompleted());
355 backend_initialization.SetValue(
356 service->GetBackendInitializationStateString());
357 if (is_status_valid) {
358 is_syncing.SetValue(full_status.syncing);
359 retry_time.SetValue(GetTimeStr(full_status.retry_time,
360 "Scheduler is not in backoff or throttled"));
363 if (snapshot.is_initialized())
364 is_throttled.SetValue(snapshot.is_silenced());
365 if (is_status_valid) {
366 are_notifications_enabled.SetValue(
367 full_status.notifications_enabled);
370 if (sync_active) {
371 is_using_explicit_passphrase.SetValue(
372 service->IsUsingSecondaryPassphrase());
373 is_passphrase_required.SetValue(service->IsPassphraseRequired());
374 passphrase_time.SetValue(
375 GetTimeStr(service->GetExplicitPassphraseTime(), "No Passphrase Time"));
377 if (is_status_valid) {
378 is_cryptographer_ready.SetValue(full_status.cryptographer_ready);
379 has_pending_keys.SetValue(full_status.crypto_has_pending_keys);
380 encrypted_types.SetValue(
381 ModelTypeSetToString(full_status.encrypted_types));
382 has_keystore_key.SetValue(full_status.has_keystore_key);
383 keystore_migration_time.SetValue(
384 GetTimeStr(full_status.keystore_migration_time, "Not Migrated"));
385 passphrase_type.SetValue(
386 PassphraseTypeToString(full_status.passphrase_type));
389 if (snapshot.is_initialized()) {
390 if (snapshot.legacy_updates_source() !=
391 sync_pb::GetUpdatesCallerInfo::UNKNOWN) {
392 session_source.SetValue(
393 syncer::GetUpdatesSourceString(snapshot.legacy_updates_source()));
395 get_key_result.SetValue(
396 GetSyncerErrorString(
397 snapshot.model_neutral_state().last_get_key_result));
398 download_result.SetValue(
399 GetSyncerErrorString(
400 snapshot.model_neutral_state().last_download_updates_result));
401 commit_result.SetValue(
402 GetSyncerErrorString(
403 snapshot.model_neutral_state().commit_result));
406 if (is_status_valid) {
407 notifications_received.SetValue(full_status.notifications_received);
408 updates_received.SetValue(full_status.updates_received);
409 tombstone_updates.SetValue(full_status.tombstone_updates_received);
410 reflected_updates.SetValue(full_status.reflected_updates_received);
411 successful_commits.SetValue(full_status.num_commits_total);
412 conflicts_resolved_local_wins.SetValue(
413 full_status.num_local_overwrites_total);
414 conflicts_resolved_server_wins.SetValue(
415 full_status.num_server_overwrites_total);
418 if (is_status_valid) {
419 encryption_conflicts.SetValue(full_status.encryption_conflicts);
420 hierarchy_conflicts.SetValue(full_status.hierarchy_conflicts);
421 server_conflicts.SetValue(full_status.server_conflicts);
422 committed_items.SetValue(full_status.committed_count);
425 if (is_status_valid) {
426 nudge_source_notification.SetValue(full_status.nudge_source_notification);
427 nudge_source_local.SetValue(full_status.nudge_source_local);
428 nudge_source_local_refresh.SetValue(full_status.nudge_source_local_refresh);
431 if (snapshot.is_initialized()) {
432 updates_downloaded.SetValue(
433 snapshot.model_neutral_state().num_updates_downloaded_total);
434 committed_count.SetValue(
435 snapshot.model_neutral_state().num_successful_commits);
436 entries.SetValue(snapshot.num_entries());
439 // The values set from this point onwards do not belong in the
440 // details list.
442 // We don't need to check is_status_valid here.
443 // full_status.sync_protocol_error is exported directly from the
444 // ProfileSyncService, even if the backend doesn't exist.
445 const bool actionable_error_detected =
446 full_status.sync_protocol_error.error_type != syncer::UNKNOWN_ERROR &&
447 full_status.sync_protocol_error.error_type != syncer::SYNC_SUCCESS;
449 about_info->SetBoolean("actionable_error_detected",
450 actionable_error_detected);
452 // NOTE: We won't bother showing any of the following values unless
453 // actionable_error_detected is set.
455 base::ListValue* actionable_error = new base::ListValue();
456 about_info->Set("actionable_error", actionable_error);
458 StringSyncStat error_type(actionable_error, "Error Type");
459 StringSyncStat action(actionable_error, "Action");
460 StringSyncStat url(actionable_error, "URL");
461 StringSyncStat description(actionable_error, "Error Description");
463 if (actionable_error_detected) {
464 error_type.SetValue(syncer::GetSyncErrorTypeString(
465 full_status.sync_protocol_error.error_type));
466 action.SetValue(syncer::GetClientActionString(
467 full_status.sync_protocol_error.action));
468 url.SetValue(full_status.sync_protocol_error.url);
469 description.SetValue(full_status.sync_protocol_error.error_description);
472 about_info->SetBoolean("unrecoverable_error_detected",
473 service->HasUnrecoverableError());
475 if (service->HasUnrecoverableError()) {
476 tracked_objects::Location loc(service->unrecoverable_error_location());
477 std::string location_str;
478 loc.Write(true, true, &location_str);
479 std::string unrecoverable_error_message =
480 "Unrecoverable error detected at " + location_str +
481 ": " + service->unrecoverable_error_message();
482 about_info->SetString("unrecoverable_error_message",
483 unrecoverable_error_message);
486 about_info->Set("type_status", service->GetTypeStatusMap());
488 return about_info.Pass();
491 } // namespace sync_ui_util