Chromium Code Reviews| Index: chrome/browser/sync/about_sync_util.cc |
| diff --git a/chrome/browser/sync/about_sync_util.cc b/chrome/browser/sync/about_sync_util.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0976d90904c8336a31a36f9b34c36e7001936cb0 |
| --- /dev/null |
| +++ b/chrome/browser/sync/about_sync_util.cc |
| @@ -0,0 +1,397 @@ |
| +// 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/about_sync_util.h" |
| + |
| +#include <string> |
| + |
| +#include "base/string16.h" |
| +#include "base/values.h" |
| +#include "chrome/browser/signin/signin_manager.h" |
| +#include "chrome/browser/sync/profile_sync_service.h" |
| +#include "chrome/common/chrome_version_info.h" |
| +#include "sync/protocol/proto_enum_conversions.h" |
| + |
| +using base::DictionaryValue; |
| +using base::ListValue; |
| + |
| +namespace { |
| + |
| +// Creates a 'section' for display on about:sync, consisting of a title and a |
| +// list of fields. Returns a pointer to the new section. Note that |
| +// |parent_list|, not the caller, owns the newly added section. |
| +ListValue* AddSection(ListValue* parent_list, const std::string& title) { |
| + DictionaryValue* section = new DictionaryValue(); |
| + ListValue* section_contents = new ListValue(); |
| + section->SetString("title", title); |
| + section->Set("data", section_contents); |
| + parent_list->Append(section); |
| + return section_contents; |
| +} |
| + |
| +// The following helper classes help manage the about:sync fields which will be |
| +// populated in method in ConstructAboutInformation. |
| +// |
| +// Each instance of one of thse classes indicates a field in about:sync. Each |
| +// field will be serialized to a DictionaryValue with entries for 'stat_name', |
| +// 'stat_value' and 'is_valid'. |
| + |
| +class StringSyncStat { |
| + public: |
| + StringSyncStat(ListValue* section, const std::string& key); |
| + void SetValue(const std::string& value); |
| + void SetValue(const string16& value); |
| + |
| + private: |
| + // Owned by the |section| passed in during construction. |
| + DictionaryValue* stat_; |
| +}; |
| + |
| +StringSyncStat::StringSyncStat(ListValue* section, const std::string& key) { |
| + stat_ = new DictionaryValue(); |
| + stat_->SetString("stat_name", key); |
| + stat_->SetString("stat_value", "Uninitialized"); |
| + stat_->SetBoolean("is_valid", false); |
| + section->Append(stat_); |
| +} |
| + |
| +void StringSyncStat::SetValue(const std::string& value) { |
| + stat_->SetString("stat_value", value); |
| + stat_->SetBoolean("is_valid", true); |
| +} |
| + |
| +void StringSyncStat::SetValue(const string16& value) { |
| + stat_->SetString("stat_value", value); |
| + stat_->SetBoolean("is_valid", true); |
| +} |
| + |
| +class BoolSyncStat { |
| + public: |
| + BoolSyncStat(ListValue* section, const std::string& key); |
| + void SetValue(bool value); |
| + |
| + private: |
| + // Owned by the |section| passed in during construction. |
| + DictionaryValue* stat_; |
| +}; |
| + |
| +BoolSyncStat::BoolSyncStat(ListValue* section, const std::string& key) { |
| + stat_ = new DictionaryValue(); |
| + stat_->SetString("stat_name", key); |
| + stat_->SetBoolean("stat_value", false); |
| + stat_->SetBoolean("is_valid", false); |
| + section->Append(stat_); |
| +} |
| + |
| +void BoolSyncStat::SetValue(bool value) { |
| + stat_->SetBoolean("stat_value", value); |
| + stat_->SetBoolean("is_valid", true); |
| +} |
| + |
| +class IntSyncStat { |
| + public: |
| + IntSyncStat(ListValue* section, const std::string& key); |
| + void SetValue(int value); |
| + |
| + private: |
| + // Owned by the |section| passed in during construction. |
| + DictionaryValue* stat_; |
| +}; |
| + |
| +IntSyncStat::IntSyncStat(ListValue* section, const std::string& key) { |
| + stat_ = new DictionaryValue(); |
| + stat_->SetString("stat_name", key); |
| + stat_->SetInteger("stat_value", 0); |
| + stat_->SetBoolean("is_valid", false); |
| + section->Append(stat_); |
| +} |
| + |
| +void IntSyncStat::SetValue(int value) { |
| + stat_->SetInteger("stat_value", value); |
| + stat_->SetBoolean("is_valid", true); |
| +} |
| + |
| +// Returns a string describing the chrome version environment. Version format: |
| +// <Build Info> <OS> <Version number> (<Last change>)<channel or "-devel"> |
| +// If version information is unavailable, returns "invalid." |
| +// TODO(zea): this approximately matches MakeUserAgentForSyncApi in |
| +// sync_backend_host.cc. Unify the two if possible. |
| +std::string GetVersionString() { |
| + // Build a version string that matches MakeUserAgentForSyncApi with the |
| + // addition of channel info and proper OS names. |
| + chrome::VersionInfo chrome_version; |
| + if (!chrome_version.is_valid()) |
| + return "invalid"; |
| + // GetVersionStringModifier returns empty string for stable channel or |
| + // unofficial builds, the channel string otherwise. We want to have "-devel" |
| + // for unofficial builds only. |
| + std::string version_modifier = |
| + chrome::VersionInfo::GetVersionStringModifier(); |
| + if (version_modifier.empty()) { |
| + if (chrome::VersionInfo::GetChannel() != |
| + chrome::VersionInfo::CHANNEL_STABLE) { |
| + version_modifier = "-devel"; |
| + } |
| + } else { |
| + version_modifier = " " + version_modifier; |
| + } |
| + return chrome_version.Name() + " " + chrome_version.OSType() + " " + |
| + chrome_version.Version() + " (" + chrome_version.LastChange() + ")" + |
| + version_modifier; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace sync_ui_util { |
| + |
| +// This function both defines the structure of the message to be returned and |
| +// its contents. Most of the message consists of simple fields in about:sync |
| +// which are grouped into sections and populated with the help of the SyncStat |
| +// classes defined above. |
| +scoped_ptr<DictionaryValue> ConstructAboutInformation( |
| + ProfileSyncService* service) { |
| + scoped_ptr<DictionaryValue> about_info(new DictionaryValue()); |
| + ListValue* stats_list = new ListValue(); // 'details': A list of sections. |
|
akalin
2012/07/12 22:51:20
two spaces before //
|
| + |
| + // The following lines define the sections and their fields. For each field, |
| + // a class is instantiated, which allows us to reference the fields in |
| + // 'setter' code later on in this function. |
| + ListValue* section_summary = AddSection(stats_list, "Summary"); |
| + StringSyncStat summary_string(section_summary, "Summary"); |
| + |
| + ListValue* section_version = AddSection(stats_list, "Version Info"); |
| + StringSyncStat client_version(section_version, "Client Version"); |
| + StringSyncStat server_url(section_version, "Server URL"); |
| + |
| + ListValue* section_credentials = AddSection(stats_list, "Credentials"); |
| + StringSyncStat client_id(section_credentials, "Client ID"); |
| + StringSyncStat username(section_credentials, "Username"); |
| + BoolSyncStat is_token_available(section_credentials, "Sync Token Available"); |
| + |
| + ListValue* section_local = AddSection(stats_list, "Local State"); |
| + StringSyncStat last_synced(section_local, "Last Synced"); |
| + BoolSyncStat is_setup_complete(section_local, |
| + "Sync First-Time Setup Complete"); |
| + BoolSyncStat is_backend_initialized(section_local, |
| + "Sync Backend Initialized"); |
| + BoolSyncStat is_download_complete(section_local, "Initial Download Complete"); |
| + BoolSyncStat is_syncing(section_local, "Syncing"); |
| + |
| + ListValue* section_network = AddSection(stats_list, "Network"); |
| + BoolSyncStat is_throttled(section_network, "Throttled"); |
| + BoolSyncStat are_notifications_enabled(section_network, |
| + "Notifications Enabled"); |
| + |
| + ListValue* section_encryption = AddSection(stats_list, "Encryption"); |
| + BoolSyncStat is_using_explicit_passphrase(section_encryption, |
| + "Explicit Passphrase"); |
| + BoolSyncStat is_passphrase_required(section_encryption, |
| + "Passphrase Required"); |
| + BoolSyncStat is_cryptographer_ready(section_encryption, |
| + "Cryptographer Ready"); |
| + BoolSyncStat has_pending_keys(section_encryption, |
| + "Cryptographer Has Pending Keys"); |
| + StringSyncStat encrypted_types(section_encryption, "Encrypted Types"); |
| + |
| + ListValue* section_last_session = AddSection( |
| + stats_list, "Status from Last Completed Session"); |
| + StringSyncStat session_source(section_last_session, "Sync Source"); |
| + StringSyncStat download_result(section_last_session, "Download Step Result"); |
| + StringSyncStat commit_result(section_last_session, "Commit Step Result"); |
| + |
| + ListValue* section_counters = AddSection(stats_list, "Running Totals"); |
| + IntSyncStat notifications_received(section_counters, |
| + "Notifications Received"); |
| + IntSyncStat empty_get_updates(section_counters, "Cycles Without Updates"); |
| + IntSyncStat non_empty_get_updates(section_counters, "Cycles With Updated"); |
| + IntSyncStat sync_cycles_without_commits(section_counters, |
| + "Cycles Without Commits"); |
| + IntSyncStat sync_cycles_with_commits(section_counters, "Cycles With Commits"); |
| + IntSyncStat useless_sync_cycles(section_counters, |
| + "Cycles Without Commits or Updates"); |
| + IntSyncStat useful_sync_cycles(section_counters, |
| + "Cycles With Commit or Update"); |
| + IntSyncStat updates_received(section_counters, "Updates Downloaded"); |
| + IntSyncStat tombstone_updates(section_counters, "Tombstone Updates"); |
| + IntSyncStat reflected_updates(section_counters, "Reflected Updates"); |
| + IntSyncStat successful_commits(section_counters, "Syccessful Commits"); |
| + IntSyncStat conflicts_resolved_local_wins(section_counters, |
| + "Conflicts Resolved: Client Wins"); |
| + IntSyncStat conflicts_resolved_server_wins(section_counters, |
| + "Conflicts Resolved: Server Wins"); |
| + |
| + ListValue *section_this_cycle = AddSection(stats_list, |
| + "Transient Counters (this cycle)"); |
| + IntSyncStat encryption_conflicts(section_this_cycle, "Encryption Conflicts"); |
| + IntSyncStat hierarchy_conflicts(section_this_cycle, "Hierarchy Conflicts"); |
| + IntSyncStat simple_conflicts(section_this_cycle, "Simple Conflicts"); |
| + IntSyncStat server_conflicts(section_this_cycle, "Server Conflicts"); |
| + IntSyncStat committed_items(section_this_cycle, "Committed Items"); |
| + IntSyncStat updates_remaining(section_this_cycle, "Updates Remaining"); |
| + |
| + ListValue* section_that_cycle = AddSection( |
| + stats_list, "Transient Counters (last cycle of last completed session)"); |
| + IntSyncStat updates_downloaded(section_that_cycle, "Updates Downloaded"); |
| + IntSyncStat committed_count(section_that_cycle, "Committed Count"); |
| + IntSyncStat entries(section_that_cycle, "Entries"); |
| + |
| + // This list of sections belongs in the 'details' field of the returned |
| + // message. |
| + about_info->Set("details", stats_list); |
| + |
| + // Populate all the fields we declared above. |
| + client_version.SetValue(GetVersionString()); |
| + |
| + if (!service) { |
| + summary_string.SetValue("Sync service does not exist"); |
| + return about_info.Pass(); |
| + } |
| + |
| + syncer::SyncStatus full_status; |
| + bool is_status_valid = service->QueryDetailedSyncStatus(&full_status); |
| + bool sync_initialized = service->sync_initialized(); |
| + const syncer::sessions::SyncSessionSnapshot& snapshot = |
| + sync_initialized ? |
| + service->GetLastSessionSnapshot() : |
| + syncer::sessions::SyncSessionSnapshot(); |
| + |
| + if (is_status_valid) |
| + summary_string.SetValue(service->QuerySyncStatusSummary()); |
| + |
| + server_url.SetValue(service->sync_service_url().spec()); |
| + |
| + if (is_status_valid && !full_status.unique_id.empty()) |
| + client_id.SetValue(full_status.unique_id); |
| + if (service->signin()) |
| + username.SetValue(service->signin()->GetAuthenticatedUsername()); |
| + is_token_available.SetValue(service->IsSyncTokenAvailable()); |
| + |
| + last_synced.SetValue(service->GetLastSyncedTimeString()); |
| + is_setup_complete.SetValue(service->HasSyncSetupCompleted()); |
| + is_backend_initialized.SetValue(sync_initialized); |
| + if (is_status_valid) { |
| + is_download_complete.SetValue(full_status.initial_sync_ended); |
| + is_syncing.SetValue(full_status.syncing); |
| + } |
| + |
| + if (snapshot.is_initialized()) |
| + is_throttled.SetValue(snapshot.is_silenced()); |
| + if (is_status_valid) { |
| + are_notifications_enabled.SetValue( |
| + full_status.notifications_enabled); |
| + } |
| + |
| + if (sync_initialized) { |
| + is_using_explicit_passphrase.SetValue( |
| + service->IsUsingSecondaryPassphrase()); |
| + is_passphrase_required.SetValue(service->IsPassphraseRequired()); |
| + } |
| + if (is_status_valid) { |
| + is_cryptographer_ready.SetValue(full_status.cryptographer_ready); |
| + has_pending_keys.SetValue(full_status.crypto_has_pending_keys); |
| + encrypted_types.SetValue( |
| + ModelTypeSetToString(full_status.encrypted_types)); |
| + } |
| + |
| + if (snapshot.is_initialized()) { |
| + session_source.SetValue( |
| + syncer::GetUpdatesSourceString(snapshot.source().updates_source)); |
| + download_result.SetValue( |
| + GetSyncerErrorString( |
| + snapshot.model_neutral_state().last_download_updates_result)); |
| + commit_result.SetValue( |
| + GetSyncerErrorString( |
| + snapshot.model_neutral_state().commit_result)); |
| + } |
| + |
| + if (is_status_valid) { |
| + notifications_received.SetValue(full_status.notifications_received); |
| + empty_get_updates.SetValue(full_status.empty_get_updates); |
| + non_empty_get_updates.SetValue(full_status.nonempty_get_updates); |
| + sync_cycles_without_commits.SetValue( |
| + full_status.sync_cycles_without_commits); |
| + sync_cycles_with_commits.SetValue( |
| + full_status.sync_cycles_with_commits); |
| + useless_sync_cycles.SetValue(full_status.useless_sync_cycles); |
| + useful_sync_cycles.SetValue(full_status.useful_sync_cycles); |
| + updates_received.SetValue(full_status.updates_received); |
| + tombstone_updates.SetValue(full_status.tombstone_updates_received); |
| + reflected_updates.SetValue(full_status.reflected_updates_received); |
| + successful_commits.SetValue(full_status.num_commits_total); |
| + conflicts_resolved_local_wins.SetValue( |
| + full_status.num_local_overwrites_total); |
| + conflicts_resolved_server_wins.SetValue( |
| + full_status.num_server_overwrites_total); |
| + } |
| + |
| + if (is_status_valid) { |
| + encryption_conflicts.SetValue(full_status.encryption_conflicts); |
| + hierarchy_conflicts.SetValue(full_status.hierarchy_conflicts); |
| + simple_conflicts.SetValue(full_status.simple_conflicts); |
| + server_conflicts.SetValue(full_status.server_conflicts); |
| + committed_items.SetValue(full_status.committed_count); |
| + updates_remaining.SetValue(full_status.updates_available); |
| + } |
| + |
| + if (snapshot.is_initialized()) { |
| + updates_downloaded.SetValue( |
| + snapshot.model_neutral_state().num_updates_downloaded_total); |
| + committed_count.SetValue( |
| + snapshot.model_neutral_state().num_successful_commits); |
| + entries.SetValue(snapshot.num_entries()); |
| + } |
| + |
| + // The values set from this point onwards do not belong in the |
| + // details list. |
| + |
| + // We don't need to check is_status_valid here. |
| + // full_status.sync_protocol_error is exported directly from the |
| + // ProfileSyncService, even if the backend doesn't exist. |
| + const bool actionable_error_detected = |
| + full_status.sync_protocol_error.error_type != syncer::UNKNOWN_ERROR && |
| + full_status.sync_protocol_error.error_type != syncer::SYNC_SUCCESS; |
| + |
| + about_info->SetBoolean("actionable_error_detected", |
| + actionable_error_detected); |
| + |
| + // NOTE: We won't bother showing any of the following values unless |
| + // actionable_error_detected is set. |
| + |
| + ListValue* actionable_error = new ListValue(); |
| + about_info->Set("actionable_error", actionable_error); |
| + |
| + StringSyncStat error_type(actionable_error, "Error Type"); |
| + StringSyncStat action(actionable_error, "Action"); |
| + StringSyncStat url(actionable_error, "URL"); |
| + StringSyncStat description(actionable_error, "Error Description"); |
| + |
| + if (actionable_error_detected) { |
| + error_type.SetValue(syncer::GetSyncErrorTypeString( |
| + full_status.sync_protocol_error.error_type)); |
| + action.SetValue(syncer::GetClientActionString( |
| + full_status.sync_protocol_error.action)); |
| + url.SetValue(full_status.sync_protocol_error.url); |
| + description.SetValue(full_status.sync_protocol_error.error_description); |
| + } |
| + |
| + about_info->SetBoolean("unrecoverable_error_detected", |
| + service->HasUnrecoverableError()); |
| + |
| + if (service->HasUnrecoverableError()) { |
| + tracked_objects::Location loc(service->unrecoverable_error_location()); |
| + std::string location_str; |
| + loc.Write(true, true, &location_str); |
| + std::string unrecoverable_error_message = |
| + "Unrecoverable error detected at " + location_str + |
| + ": " + service->unrecoverable_error_message(); |
| + about_info->SetString("unrecoverable_error_message", |
| + unrecoverable_error_message); |
| + } |
| + |
| + about_info->Set("type_status", service->GetTypeStatusMap()); |
| + |
| + return about_info.Pass(); |
| +} |
| + |
| +} // namespace sync_ui_util |