| // 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 "components/sync/driver/about_sync_util.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/values.h" |
| #include "components/signin/core/browser/signin_manager_base.h" |
| #include "components/sync/driver/sync_service.h" |
| #include "components/sync/engine/cycle/sync_cycle_snapshot.h" |
| #include "components/sync/engine/sync_status.h" |
| #include "components/sync/engine/sync_string_conversions.h" |
| #include "components/sync/model/time.h" |
| #include "components/sync/protocol/proto_enum_conversions.h" |
| |
| using base::DictionaryValue; |
| using base::ListValue; |
| |
| namespace syncer { |
| |
| namespace sync_ui_util { |
| |
| const char kIdentityTitle[] = "Identity"; |
| const char kDetailsKey[] = "details"; |
| |
| // Resource paths. |
| const char kAboutJS[] = "about.js"; |
| const char kChromeSyncJS[] = "chrome_sync.js"; |
| const char kDataJS[] = "data.js"; |
| const char kEventsJS[] = "events.js"; |
| const char kSearchJS[] = "search.js"; |
| const char kSyncIndexJS[] = "sync_index.js"; |
| const char kSyncLogJS[] = "sync_log.js"; |
| const char kSyncNodeBrowserJS[] = "sync_node_browser.js"; |
| const char kSyncSearchJS[] = "sync_search.js"; |
| const char kTypesJS[] = "types.js"; |
| const char kUserEventsJS[] = "user_events.js"; |
| |
| // Message handlers. |
| const char kDispatchEvent[] = "chrome.sync.dispatchEvent"; |
| const char kGetAllNodes[] = "getAllNodes"; |
| const char kGetAllNodesCallback[] = "chrome.sync.getAllNodesCallback"; |
| const char kRegisterForEvents[] = "registerForEvents"; |
| const char kRegisterForPerTypeCounters[] = "registerForPerTypeCounters"; |
| const char kRequestListOfTypes[] = "requestListOfTypes"; |
| const char kRequestUpdatedAboutInfo[] = "requestUpdatedAboutInfo"; |
| const char kRequestUserEventsVisibility[] = "requestUserEventsVisibility"; |
| const char kUserEventsVisibilityCallback[] = |
| "chrome.sync.userEventsVisibilityCallback"; |
| const char kWriteUserEvent[] = "writeUserEvent"; |
| |
| // Other strings. |
| const char kCommit[] = "commit"; |
| const char kCounters[] = "counters"; |
| const char kCounterType[] = "counterType"; |
| const char kModelType[] = "modelType"; |
| const char kOnAboutInfoUpdated[] = "onAboutInfoUpdated"; |
| const char kOnCountersUpdated[] = "onCountersUpdated"; |
| const char kOnProtocolEvent[] = "onProtocolEvent"; |
| const char kOnReceivedListOfTypes[] = "onReceivedListOfTypes"; |
| const char kStatus[] = "status"; |
| const char kTypes[] = "types"; |
| const char kUpdate[] = "update"; |
| |
| 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. |
| base::ListValue* AddSection(base::ListValue* parent_list, |
| const std::string& title) { |
| auto section = base::MakeUnique<base::DictionaryValue>(); |
| section->SetString("title", title); |
| base::ListValue* section_contents = |
| section->SetList("data", base::MakeUnique<base::ListValue>()); |
| section->SetBoolean("is_sensitive", false); |
| // If the following |Append| results in a reallocation, pointers to the |
| // members of |parent_list| will be invalidated. This would result in |
| // use-after-free in |*SyncStat::SetValue|. This is why the following CHECK is |
| // necessary to ensure no reallocation takes place. |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| CHECK_LT(parent_list->GetSize(), parent_list->capacity()); |
| parent_list->Append(std::move(section)); |
| return section_contents; |
| } |
| |
| // Same as AddSection, but for data that should be elided when dumped into text |
| // form and posted in a public forum (e.g. unique identifiers). |
| base::ListValue* AddSensitiveSection(base::ListValue* parent_list, |
| const std::string& title) { |
| auto section = base::MakeUnique<base::DictionaryValue>(); |
| section->SetString("title", title); |
| base::ListValue* section_contents = |
| section->SetList("data", base::MakeUnique<base::ListValue>()); |
| section->SetBoolean("is_sensitive", true); |
| // If the following |Append| results in a reallocation, pointers to |
| // |parent_list| and its members will be invalidated. This would result in |
| // use-after-free in |*SyncStat::SetValue|. This is why the following CHECK is |
| // necessary to ensure no reallocation takes place. |
| CHECK_LT(parent_list->GetSize(), parent_list->capacity()); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| parent_list->Append(std::move(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(base::ListValue* section, const std::string& key); |
| void SetValue(const std::string& value); |
| void SetValue(const base::string16& value); |
| |
| private: |
| // Owned by the |section| passed in during construction. |
| base::DictionaryValue* stat_; |
| }; |
| |
| StringSyncStat::StringSyncStat(base::ListValue* section, |
| const std::string& key) { |
| stat_ = new base::DictionaryValue(); |
| stat_->SetString("stat_name", key); |
| stat_->SetString("stat_value", "Uninitialized"); |
| stat_->SetBoolean("is_valid", false); |
| // |stat_| will be invalidated by |Append|, so it needs to be reset. |
| // Furthermore, if |Append| results in a reallocation, |stat_| members of |
| // other SyncStats will be invalidated. This is why the following check is |
| // necessary, so that it is guaranteed that a reallocation will not happen. |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| CHECK_LT(section->GetSize(), section->capacity()); |
| section->Append(base::WrapUnique(stat_)); |
| section->GetDictionary(section->GetSize() - 1, &stat_); |
| } |
| |
| void StringSyncStat::SetValue(const std::string& value) { |
| stat_->SetString("stat_value", value); |
| stat_->SetBoolean("is_valid", true); |
| } |
| |
| void StringSyncStat::SetValue(const base::string16& value) { |
| stat_->SetString("stat_value", value); |
| stat_->SetBoolean("is_valid", true); |
| } |
| |
| class BoolSyncStat { |
| public: |
| BoolSyncStat(base::ListValue* section, const std::string& key); |
| void SetValue(bool value); |
| |
| private: |
| // Owned by the |section| passed in during construction. |
| base::DictionaryValue* stat_; |
| }; |
| |
| BoolSyncStat::BoolSyncStat(base::ListValue* section, const std::string& key) { |
| stat_ = new base::DictionaryValue(); |
| stat_->SetString("stat_name", key); |
| stat_->SetBoolean("stat_value", false); |
| stat_->SetBoolean("is_valid", false); |
| // |stat_| will be invalidated by |Append|, so it needs to be reset. |
| // Furthermore, if |Append| results in a reallocation, |stat_| members of |
| // other SyncStats will be invalidated. This is why the following check is |
| // necessary, so that it is guaranteed that a reallocation will not happen. |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| CHECK_LT(section->GetSize(), section->capacity()); |
| section->Append(base::WrapUnique(stat_)); |
| section->GetDictionary(section->GetSize() - 1, &stat_); |
| } |
| |
| void BoolSyncStat::SetValue(bool value) { |
| stat_->SetBoolean("stat_value", value); |
| stat_->SetBoolean("is_valid", true); |
| } |
| |
| class IntSyncStat { |
| public: |
| IntSyncStat(base::ListValue* section, const std::string& key); |
| void SetValue(int value); |
| |
| private: |
| // Owned by the |section| passed in during construction. |
| base::DictionaryValue* stat_; |
| }; |
| |
| IntSyncStat::IntSyncStat(base::ListValue* section, const std::string& key) { |
| stat_ = new base::DictionaryValue(); |
| stat_->SetString("stat_name", key); |
| stat_->SetInteger("stat_value", 0); |
| stat_->SetBoolean("is_valid", false); |
| // |stat_| will be invalidated by |Append|, so it needs to be reset. |
| // Furthermore, if |Append| results in a reallocation, |stat_| members of |
| // other SyncStats will be invalidated. This is why the following check is |
| // necessary, so that it is guaranteed that a reallocation will not happen. |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| CHECK_LT(section->GetSize(), section->capacity()); |
| section->Append(base::WrapUnique(stat_)); |
| section->GetDictionary(section->GetSize() - 1, &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(version_info::Channel channel) { |
| // Build a version string that matches MakeUserAgentForSyncApi with the |
| // addition of channel info and proper OS names. |
| // chrome::GetChannelString() 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 = version_info::GetChannelString(channel); |
| if (version_modifier.empty()) { |
| if (channel != version_info::Channel::STABLE) { |
| version_modifier = "-devel"; |
| } |
| } else { |
| version_modifier = " " + version_modifier; |
| } |
| return version_info::GetProductName() + " " + version_info::GetOSType() + |
| " " + version_info::GetVersionNumber() + " (" + |
| version_info::GetLastChange() + ")" + version_modifier; |
| } |
| |
| std::string GetTimeStr(base::Time time, const std::string& default_msg) { |
| std::string time_str; |
| if (time.is_null()) |
| time_str = default_msg; |
| else |
| time_str = GetTimeDebugString(time); |
| return time_str; |
| } |
| |
| std::string GetConnectionStatus(const SyncService::SyncTokenStatus& status) { |
| std::string message; |
| switch (status.connection_status) { |
| case CONNECTION_NOT_ATTEMPTED: |
| base::StringAppendF(&message, "not attempted"); |
| break; |
| case CONNECTION_OK: |
| base::StringAppendF( |
| &message, "OK since %s", |
| GetTimeStr(status.connection_status_update_time, "n/a").c_str()); |
| break; |
| case CONNECTION_AUTH_ERROR: |
| base::StringAppendF( |
| &message, "auth error since %s", |
| GetTimeStr(status.connection_status_update_time, "n/a").c_str()); |
| break; |
| case CONNECTION_SERVER_ERROR: |
| base::StringAppendF( |
| &message, "server error since %s", |
| GetTimeStr(status.connection_status_update_time, "n/a").c_str()); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| return message; |
| } |
| |
| } // namespace |
| |
| // 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. |
| std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( |
| SyncService* service, |
| version_info::Channel channel) { |
| auto about_info = base::MakeUnique<base::DictionaryValue>(); |
| |
| // 'details': A list of sections. |
| auto stats_list = base::MakeUnique<base::ListValue>(); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| stats_list->Reserve(12); |
| |
| // 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. |
| base::ListValue* section_summary = AddSection(stats_list.get(), "Summary"); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| section_summary->Reserve(1); |
| StringSyncStat summary_string(section_summary, "Summary"); |
| |
| base::ListValue* section_version = |
| AddSection(stats_list.get(), "Version Info"); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| section_version->Reserve(2); |
| StringSyncStat client_version(section_version, "Client Version"); |
| StringSyncStat server_url(section_version, "Server URL"); |
| |
| base::ListValue* section_identity = |
| AddSensitiveSection(stats_list.get(), kIdentityTitle); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| section_identity->Reserve(3); |
| StringSyncStat sync_id(section_identity, "Sync Client ID"); |
| StringSyncStat invalidator_id(section_identity, "Invalidator Client ID"); |
| StringSyncStat username(section_identity, "Username"); |
| |
| base::ListValue* section_credentials = |
| AddSection(stats_list.get(), "Credentials"); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| section_credentials->Reserve(4); |
| StringSyncStat request_token_time(section_credentials, "Requested Token"); |
| StringSyncStat receive_token_time(section_credentials, "Received Token"); |
| StringSyncStat token_request_status(section_credentials, |
| "Token Request Status"); |
| StringSyncStat next_token_request(section_credentials, "Next Token Request"); |
| |
| base::ListValue* section_local = AddSection(stats_list.get(), "Local State"); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| section_local->Reserve(7); |
| StringSyncStat server_connection(section_local, "Server Connection"); |
| StringSyncStat last_synced(section_local, "Last Synced"); |
| BoolSyncStat is_setup_complete(section_local, |
| "Sync First-Time Setup Complete"); |
| StringSyncStat backend_initialization(section_local, |
| "Sync Backend Initialization"); |
| BoolSyncStat is_syncing(section_local, "Syncing"); |
| BoolSyncStat is_local_sync_enabled(section_local, |
| "Local sync backend enabled"); |
| StringSyncStat local_backend_path(section_local, "Local backend path"); |
| |
| base::ListValue* section_network = AddSection(stats_list.get(), "Network"); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| section_network->Reserve(3); |
| BoolSyncStat is_throttled(section_network, "Throttled"); |
| StringSyncStat retry_time(section_network, "Retry time (maybe stale)"); |
| BoolSyncStat are_notifications_enabled(section_network, |
| "Notifications Enabled"); |
| |
| base::ListValue* section_encryption = |
| AddSection(stats_list.get(), "Encryption"); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| section_encryption->Reserve(9); |
| 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"); |
| BoolSyncStat has_keystore_key(section_encryption, "Has Keystore Key"); |
| StringSyncStat keystore_migration_time(section_encryption, |
| "Keystore Migration Time"); |
| StringSyncStat passphrase_type(section_encryption, "Passphrase Type"); |
| StringSyncStat passphrase_time(section_encryption, "Passphrase Time"); |
| |
| base::ListValue* section_last_session = |
| AddSection(stats_list.get(), "Status from Last Completed Session"); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| section_last_session->Reserve(4); |
| StringSyncStat session_source(section_last_session, "Sync Source"); |
| StringSyncStat get_key_result(section_last_session, "GetKey Step Result"); |
| StringSyncStat download_result(section_last_session, "Download Step Result"); |
| StringSyncStat commit_result(section_last_session, "Commit Step Result"); |
| |
| base::ListValue* section_counters = |
| AddSection(stats_list.get(), "Running Totals"); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| section_counters->Reserve(7); |
| IntSyncStat notifications_received(section_counters, |
| "Notifications Received"); |
| 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, "Successful Commits"); |
| IntSyncStat conflicts_resolved_local_wins(section_counters, |
| "Conflicts Resolved: Client Wins"); |
| IntSyncStat conflicts_resolved_server_wins(section_counters, |
| "Conflicts Resolved: Server Wins"); |
| |
| base::ListValue* section_this_cycle = |
| AddSection(stats_list.get(), "Transient Counters (this cycle)"); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| section_this_cycle->Reserve(4); |
| IntSyncStat encryption_conflicts(section_this_cycle, "Encryption Conflicts"); |
| IntSyncStat hierarchy_conflicts(section_this_cycle, "Hierarchy Conflicts"); |
| IntSyncStat server_conflicts(section_this_cycle, "Server Conflicts"); |
| IntSyncStat committed_items(section_this_cycle, "Committed Items"); |
| |
| base::ListValue* section_that_cycle = |
| AddSection(stats_list.get(), |
| "Transient Counters (last cycle of last completed session)"); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| section_that_cycle->Reserve(3); |
| IntSyncStat updates_downloaded(section_that_cycle, "Updates Downloaded"); |
| IntSyncStat committed_count(section_that_cycle, "Committed Count"); |
| IntSyncStat entries(section_that_cycle, "Entries"); |
| |
| base::ListValue* section_nudge_info = |
| AddSection(stats_list.get(), "Nudge Source Counters"); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| section_nudge_info->Reserve(3); |
| IntSyncStat nudge_source_notification(section_nudge_info, |
| "Server Invalidations"); |
| IntSyncStat nudge_source_local(section_nudge_info, "Local Changes"); |
| IntSyncStat nudge_source_local_refresh(section_nudge_info, "Local Refreshes"); |
| |
| // This list of sections belongs in the 'details' field of the returned |
| // message. |
| about_info->Set(kDetailsKey, std::move(stats_list)); |
| |
| // Populate all the fields we declared above. |
| client_version.SetValue(GetVersionString(channel)); |
| |
| if (!service) { |
| summary_string.SetValue("Sync service does not exist"); |
| return about_info; |
| } |
| |
| SyncStatus full_status; |
| bool is_status_valid = service->QueryDetailedSyncStatus(&full_status); |
| bool sync_active = service->IsSyncActive(); |
| const SyncCycleSnapshot& snapshot = service->GetLastCycleSnapshot(); |
| |
| if (is_status_valid) |
| summary_string.SetValue(service->QuerySyncStatusSummaryString()); |
| |
| server_url.SetValue(service->sync_service_url().spec()); |
| |
| if (is_status_valid && !full_status.sync_id.empty()) |
| sync_id.SetValue(full_status.sync_id); |
| if (is_status_valid && !full_status.invalidator_client_id.empty()) |
| invalidator_id.SetValue(full_status.invalidator_client_id); |
| if (service->signin()) |
| username.SetValue(service->signin()->GetAuthenticatedAccountInfo().email); |
| |
| const SyncService::SyncTokenStatus& token_status = |
| service->GetSyncTokenStatus(); |
| server_connection.SetValue(GetConnectionStatus(token_status)); |
| request_token_time.SetValue( |
| GetTimeStr(token_status.token_request_time, "n/a")); |
| receive_token_time.SetValue( |
| GetTimeStr(token_status.token_receive_time, "n/a")); |
| std::string err = token_status.last_get_token_error.error_message(); |
| token_request_status.SetValue(err.empty() ? "OK" : err); |
| next_token_request.SetValue( |
| GetTimeStr(token_status.next_token_request_time, "not scheduled")); |
| |
| last_synced.SetValue(service->GetLastSyncedTimeString()); |
| is_setup_complete.SetValue(service->IsFirstSetupComplete()); |
| is_local_sync_enabled.SetValue(service->IsLocalSyncEnabled()); |
| if (service->IsLocalSyncEnabled() && is_status_valid) { |
| local_backend_path.SetValue(full_status.local_sync_folder); |
| } |
| backend_initialization.SetValue( |
| service->GetEngineInitializationStateString()); |
| if (is_status_valid) { |
| is_syncing.SetValue(full_status.syncing); |
| retry_time.SetValue(GetTimeStr(full_status.retry_time, |
| "Scheduler is not in backoff or throttled")); |
| } |
| |
| if (snapshot.is_initialized()) |
| is_throttled.SetValue(snapshot.is_silenced()); |
| if (is_status_valid) { |
| are_notifications_enabled.SetValue(full_status.notifications_enabled); |
| } |
| |
| if (sync_active) { |
| is_using_explicit_passphrase.SetValue( |
| service->IsUsingSecondaryPassphrase()); |
| is_passphrase_required.SetValue(service->IsPassphraseRequired()); |
| passphrase_time.SetValue( |
| GetTimeStr(service->GetExplicitPassphraseTime(), "No Passphrase Time")); |
| } |
| 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)); |
| has_keystore_key.SetValue(full_status.has_keystore_key); |
| keystore_migration_time.SetValue( |
| GetTimeStr(full_status.keystore_migration_time, "Not Migrated")); |
| passphrase_type.SetValue( |
| PassphraseTypeToString(full_status.passphrase_type)); |
| } |
| |
| if (snapshot.is_initialized()) { |
| if (snapshot.legacy_updates_source() != |
| sync_pb::GetUpdatesCallerInfo::UNKNOWN) { |
| session_source.SetValue( |
| ProtoEnumToString(snapshot.legacy_updates_source())); |
| } |
| get_key_result.SetValue(GetSyncerErrorString( |
| snapshot.model_neutral_state().last_get_key_result)); |
| 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); |
| 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); |
| server_conflicts.SetValue(full_status.server_conflicts); |
| committed_items.SetValue(full_status.committed_count); |
| } |
| |
| if (is_status_valid) { |
| nudge_source_notification.SetValue(full_status.nudge_source_notification); |
| nudge_source_local.SetValue(full_status.nudge_source_local); |
| nudge_source_local_refresh.SetValue(full_status.nudge_source_local_refresh); |
| } |
| |
| 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 != UNKNOWN_ERROR && |
| full_status.sync_protocol_error.error_type != 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. |
| |
| auto actionable_error = base::MakeUnique<base::ListValue>(); |
| // TODO(crbug.com/702230): Remove the usages of raw pointers in this file. |
| actionable_error->Reserve(4); |
| |
| StringSyncStat error_type(actionable_error.get(), "Error Type"); |
| StringSyncStat action(actionable_error.get(), "Action"); |
| StringSyncStat url(actionable_error.get(), "URL"); |
| StringSyncStat description(actionable_error.get(), "Error Description"); |
| about_info->Set("actionable_error", std::move(actionable_error)); |
| |
| if (actionable_error_detected) { |
| error_type.SetValue( |
| GetSyncErrorTypeString(full_status.sync_protocol_error.error_type)); |
| action.SetValue( |
| 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; |
| } |
| |
| } // namespace sync_ui_util |
| |
| } // namespace syncer |