blob: 39e80deeddba712acff4b546d7de01663140c7ba [file] [log] [blame]
// Copyright 2019 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/ui/webui/settings/safety_check_handler.h"
#include "base/bind.h"
#include "base/feature_list.h"
#include "base/i18n/number_formatting.h"
#include "base/macros.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h"
#include "chrome/browser/password_manager/bulk_leak_check_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/version/version_ui.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/chromium_strings.h"
#include "chrome/grit/generated_resources.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/strings/grit/components_strings.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "extensions/browser/extension_prefs_factory.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension_id.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/strings/grit/ui_strings.h"
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
#include "base/win/registry.h"
#include "chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_controller_win.h"
#include "components/chrome_cleaner/public/constants/constants.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ui/chromeos/devicetype_utils.h"
#endif
namespace {
// Constants for communication with JS.
constexpr char kParentEvent[] = "safety-check-parent-status-changed";
constexpr char kUpdatesEvent[] = "safety-check-updates-status-changed";
constexpr char kPasswordsEvent[] = "safety-check-passwords-status-changed";
constexpr char kSafeBrowsingEvent[] =
"safety-check-safe-browsing-status-changed";
constexpr char kExtensionsEvent[] = "safety-check-extensions-status-changed";
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
constexpr char kChromeCleanerEvent[] =
"safety-check-chrome-cleaner-status-changed";
#endif
constexpr char kPerformSafetyCheck[] = "performSafetyCheck";
constexpr char kGetParentRanDisplayString[] = "getSafetyCheckRanDisplayString";
constexpr char kNewState[] = "newState";
constexpr char kDisplayString[] = "displayString";
// Converts the VersionUpdater::Status to the UpdateStatus enum to be passed
// to the safety check frontend. Note: if the VersionUpdater::Status gets
// changed, this will fail to compile. That is done intentionally to ensure
// that the states of the safety check are always in sync with the
// VersionUpdater ones.
SafetyCheckHandler::UpdateStatus ConvertToUpdateStatus(
VersionUpdater::Status status) {
switch (status) {
case VersionUpdater::CHECKING:
return SafetyCheckHandler::UpdateStatus::kChecking;
case VersionUpdater::UPDATED:
return SafetyCheckHandler::UpdateStatus::kUpdated;
case VersionUpdater::UPDATING:
return SafetyCheckHandler::UpdateStatus::kUpdating;
case VersionUpdater::NEED_PERMISSION_TO_UPDATE:
case VersionUpdater::NEARLY_UPDATED:
return SafetyCheckHandler::UpdateStatus::kRelaunch;
case VersionUpdater::DISABLED_BY_ADMIN:
return SafetyCheckHandler::UpdateStatus::kDisabledByAdmin;
// The disabled state can only be returned on non Chrome-branded browsers.
case VersionUpdater::DISABLED:
return SafetyCheckHandler::UpdateStatus::kUnknown;
case VersionUpdater::FAILED:
case VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED:
return SafetyCheckHandler::UpdateStatus::kFailed;
case VersionUpdater::FAILED_OFFLINE:
return SafetyCheckHandler::UpdateStatus::kFailedOffline;
}
}
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
SafetyCheckHandler::ChromeCleanerStatus ConvertToChromeCleanerStatus(
safe_browsing::ChromeCleanerController::State state,
safe_browsing::ChromeCleanerController::IdleReason idle_reason,
bool is_allowed_by_policy,
bool is_cct_timestamp_known) {
if (!is_allowed_by_policy) {
return SafetyCheckHandler::ChromeCleanerStatus::kDisabledByAdmin;
}
switch (state) {
case safe_browsing::ChromeCleanerController::State::kIdle:
switch (idle_reason) {
case safe_browsing::ChromeCleanerController::IdleReason::kInitial:
case safe_browsing::ChromeCleanerController::IdleReason::
kReporterFoundNothing:
case safe_browsing::ChromeCleanerController::IdleReason::
kScanningFoundNothing:
case safe_browsing::ChromeCleanerController::IdleReason::
kCleaningSucceeded:
return is_cct_timestamp_known
? SafetyCheckHandler::ChromeCleanerStatus::
kNoUwsFoundWithTimestamp
: SafetyCheckHandler::ChromeCleanerStatus::
kNoUwsFoundWithoutTimestamp;
case safe_browsing::ChromeCleanerController::IdleReason::
kReporterFailed:
case safe_browsing::ChromeCleanerController::IdleReason::
kScanningFailed:
case safe_browsing::ChromeCleanerController::IdleReason::
kCleaningFailed:
case safe_browsing::ChromeCleanerController::IdleReason::
kCleanerDownloadFailed:
return SafetyCheckHandler::ChromeCleanerStatus::kError;
case safe_browsing::ChromeCleanerController::IdleReason::
kConnectionLost:
case safe_browsing::ChromeCleanerController::IdleReason::
kUserDeclinedCleanup:
return SafetyCheckHandler::ChromeCleanerStatus::kInfected;
}
case safe_browsing::ChromeCleanerController::State::kReporterRunning:
case safe_browsing::ChromeCleanerController::State::kScanning:
return SafetyCheckHandler::ChromeCleanerStatus::kScanningForUws;
case safe_browsing::ChromeCleanerController::State::kCleaning:
return SafetyCheckHandler::ChromeCleanerStatus::kRemovingUws;
case safe_browsing::ChromeCleanerController::State::kInfected:
return SafetyCheckHandler::ChromeCleanerStatus::kInfected;
case safe_browsing::ChromeCleanerController::State::kRebootRequired:
return SafetyCheckHandler::ChromeCleanerStatus::kRebootRequired;
}
}
SafetyCheckHandler::ChromeCleanerResult fetchChromeCleanerStatus(
std::unique_ptr<TimestampDelegate>& timestamp_delegate) {
SafetyCheckHandler::ChromeCleanerResult result;
result.cct_completion_time =
timestamp_delegate->FetchChromeCleanerScanCompletionTimestamp();
result.status = ConvertToChromeCleanerStatus(
safe_browsing::ChromeCleanerController::GetInstance()->state(),
safe_browsing::ChromeCleanerController::GetInstance()->idle_reason(),
safe_browsing::ChromeCleanerController::GetInstance()
->IsAllowedByPolicy(),
!result.cct_completion_time.is_null());
return result;
}
#endif
} // namespace
base::Time TimestampDelegate::GetSystemTime() {
return base::Time::Now();
}
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
base::Time TimestampDelegate::FetchChromeCleanerScanCompletionTimestamp() {
// TODO(crbug.com/1139806): The cleaner scan completion timestamp is not
// always written to the registry. As a workaround, it is also written to a
// pref. This ensures that the timestamp is preserved in case Chrome is still
// opened when the scan completes. Remove this workaround once the timestamp
// is written to the registry in all cases.
const base::Time end_time_from_prefs =
g_browser_process->local_state()->GetTime(
prefs::kChromeCleanerScanCompletionTime);
// Read the scan completion timestamp from the registry, if it exists there.
base::win::RegKey reporter_key;
int64_t end_time = 0;
if (reporter_key.Open(HKEY_CURRENT_USER,
chrome_cleaner::kSoftwareRemovalToolRegistryKey,
KEY_QUERY_VALUE | KEY_SET_VALUE) != ERROR_SUCCESS ||
reporter_key.ReadInt64(chrome_cleaner::kEndTimeValueName, &end_time) !=
ERROR_SUCCESS) {
// TODO(crbug.com/1139806): Part of the above workaround. If the registry
// does not contain the timestamp but the pref does, then return the one
// from the pref.
if (!end_time_from_prefs.is_null()) {
return end_time_from_prefs;
}
// Reading failed. Return 'null' time.
return base::Time();
}
// TODO(crbug.com/1139806): Part of the above workaround. If the timestamp in
// prefs is null or older than the one from the registry, then return the one
// from the registry. Otherwise return the one from prefs.
base::Time end_time_from_registry = base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromMicroseconds(end_time));
if (end_time_from_prefs.is_null() ||
end_time_from_prefs < end_time_from_registry) {
return end_time_from_registry;
} else {
return end_time_from_prefs;
}
}
#endif
SafetyCheckHandler::SafetyCheckHandler() = default;
SafetyCheckHandler::~SafetyCheckHandler() {
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
// It seems |OnJavascriptDisallowed| is not always called before the
// deconstructor. Remove the CCT observer (no-op if not registered)
// also here to ensure it does not stay registered.
safe_browsing::ChromeCleanerController::GetInstance()->RemoveObserver(this);
#endif
}
void SafetyCheckHandler::SendSafetyCheckStartedWebUiUpdates() {
AllowJavascript();
// Ensure necessary delegates and helpers exist.
if (!timestamp_delegate_) {
timestamp_delegate_ = std::make_unique<TimestampDelegate>();
}
DCHECK(timestamp_delegate_);
// Reset status of parent and children, which might have been set from a
// previous run of safety check.
parent_status_ = ParentStatus::kChecking;
update_status_ = UpdateStatus::kChecking;
passwords_status_ = PasswordsStatus::kChecking;
safe_browsing_status_ = SafeBrowsingStatus::kChecking;
extensions_status_ = ExtensionsStatus::kChecking;
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
// If the Chrome cleaner status results in the child being hidden,
// then also hide it already in the "running" state.
if (fetchChromeCleanerStatus(timestamp_delegate_).status ==
SafetyCheckHandler::ChromeCleanerStatus::kHidden) {
chrome_cleaner_status_ = SafetyCheckHandler::ChromeCleanerStatus::kHidden;
} else {
chrome_cleaner_status_ = SafetyCheckHandler::ChromeCleanerStatus::kChecking;
}
#endif
// Update WebUi.
FireBasicSafetyCheckWebUiListener(kUpdatesEvent,
static_cast<int>(update_status_),
GetStringForUpdates(update_status_));
FireBasicSafetyCheckWebUiListener(
kPasswordsEvent, static_cast<int>(passwords_status_),
GetStringForPasswords(passwords_status_, Compromised(0), Weak(0), Done(0),
Total(0)));
FireBasicSafetyCheckWebUiListener(
kSafeBrowsingEvent, static_cast<int>(safe_browsing_status_),
GetStringForSafeBrowsing(safe_browsing_status_));
FireBasicSafetyCheckWebUiListener(
kExtensionsEvent, static_cast<int>(extensions_status_),
GetStringForExtensions(extensions_status_, Blocklisted(0),
ReenabledUser(0), ReenabledAdmin(0)));
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
// Construct string without timestamp, using "null time" via |base::Time()|.
FireBasicSafetyCheckWebUiListener(
kChromeCleanerEvent, static_cast<int>(chrome_cleaner_status_),
GetStringForChromeCleaner(chrome_cleaner_status_, base::Time(),
base::Time()));
#endif
// Parent update is last as it reveals the children elements.
FireBasicSafetyCheckWebUiListener(kParentEvent,
static_cast<int>(parent_status_),
GetStringForParent(parent_status_));
}
void SafetyCheckHandler::PerformSafetyCheck() {
// Checks common to desktop, Android, and iOS are handled by
// safety_check::SafetyCheck.
safety_check_.reset(new safety_check::SafetyCheck(this));
safety_check_->CheckSafeBrowsing(Profile::FromWebUI(web_ui())->GetPrefs());
if (!version_updater_) {
version_updater_.reset(VersionUpdater::Create(web_ui()->GetWebContents()));
}
DCHECK(version_updater_);
if (!update_helper_) {
update_helper_.reset(new safety_check::UpdateCheckHelper(
content::BrowserContext::GetDefaultStoragePartition(
Profile::FromWebUI(web_ui()))
->GetURLLoaderFactoryForBrowserProcess()));
}
DCHECK(update_helper_);
CheckUpdates();
if (!leak_service_) {
leak_service_ = BulkLeakCheckServiceFactory::GetForProfile(
Profile::FromWebUI(web_ui()));
}
DCHECK(leak_service_);
if (!passwords_delegate_) {
passwords_delegate_ =
extensions::PasswordsPrivateDelegateFactory::GetForBrowserContext(
Profile::FromWebUI(web_ui()), true);
}
DCHECK(passwords_delegate_);
if (!insecure_credentials_manager_) {
insecure_credentials_manager_ =
passwords_delegate_->GetInsecureCredentialsManager();
}
DCHECK(insecure_credentials_manager_);
CheckPasswords();
if (!extension_prefs_) {
extension_prefs_ = extensions::ExtensionPrefsFactory::GetForBrowserContext(
Profile::FromWebUI(web_ui()));
}
DCHECK(extension_prefs_);
if (!extension_service_) {
extension_service_ =
extensions::ExtensionSystem::Get(Profile::FromWebUI(web_ui()))
->extension_service();
}
DCHECK(extension_service_);
CheckExtensions();
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
CheckChromeCleaner();
#endif
}
SafetyCheckHandler::SafetyCheckHandler(
std::unique_ptr<safety_check::UpdateCheckHelper> update_helper,
std::unique_ptr<VersionUpdater> version_updater,
password_manager::BulkLeakCheckService* leak_service,
extensions::PasswordsPrivateDelegate* passwords_delegate,
extensions::ExtensionPrefs* extension_prefs,
extensions::ExtensionServiceInterface* extension_service,
std::unique_ptr<TimestampDelegate> timestamp_delegate)
: update_helper_(std::move(update_helper)),
version_updater_(std::move(version_updater)),
leak_service_(leak_service),
passwords_delegate_(passwords_delegate),
extension_prefs_(extension_prefs),
extension_service_(extension_service),
timestamp_delegate_(std::move(timestamp_delegate)) {}
void SafetyCheckHandler::HandlePerformSafetyCheck(const base::ListValue* args) {
SendSafetyCheckStartedWebUiUpdates();
// Run safety check after a delay. This ensures that the "running" state is
// visible to users for each safety check child, even if a child would
// otherwise complete in an instant.
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&SafetyCheckHandler::PerformSafetyCheck,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromSeconds(1));
}
void SafetyCheckHandler::HandleGetParentRanDisplayString(
const base::ListValue* args) {
const base::Value* callback_id;
CHECK(args->Get(0, &callback_id));
// Send updated timestamp-based display strings to all SC children who have
// such strings.
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
// String update for Chrome Cleaner.
base::DictionaryValue event;
event.SetIntKey(kNewState, static_cast<int>(chrome_cleaner_status_));
event.SetStringKey(
kDisplayString,
GetStringForChromeCleaner(
chrome_cleaner_status_,
timestamp_delegate_->FetchChromeCleanerScanCompletionTimestamp(),
timestamp_delegate_->GetSystemTime()));
FireWebUIListener(kChromeCleanerEvent, event);
#endif
// String update for the parent.
ResolveJavascriptCallback(
*callback_id,
base::Value(GetStringForParentRan(safety_check_completion_time_)));
}
void SafetyCheckHandler::CheckUpdates() {
// Usage of base::Unretained(this) is safe, because we own `version_updater_`.
version_updater_->CheckForUpdate(
base::BindRepeating(&SafetyCheckHandler::OnVersionUpdaterResult,
base::Unretained(this)),
VersionUpdater::PromoteCallback());
}
void SafetyCheckHandler::CheckPasswords() {
// Reset the tracking for callbacks with compromised passwords.
compromised_passwords_exist_ = false;
// Remove |this| as an existing observer for BulkLeakCheck if it is
// registered. This takes care of an edge case when safety check starts twice
// on the same page. Normally this should not happen, but if it does, the
// browser should not crash.
observed_leak_check_.RemoveAll();
observed_leak_check_.Add(leak_service_);
// Start observing the InsecureCredentialsManager.
observed_insecure_credentials_manager_.RemoveAll();
observed_insecure_credentials_manager_.Add(insecure_credentials_manager_);
passwords_delegate_->StartPasswordCheck(base::BindOnce(
&SafetyCheckHandler::OnStateChanged, weak_ptr_factory_.GetWeakPtr()));
}
void SafetyCheckHandler::CheckExtensions() {
extensions::ExtensionIdList extensions;
extension_prefs_->GetExtensions(&extensions);
int blocklisted = 0;
int reenabled_by_user = 0;
int reenabled_by_admin = 0;
for (auto extension_id : extensions) {
extensions::BlocklistState state =
extension_prefs_->GetExtensionBlocklistState(extension_id);
if (state == extensions::BLOCKLISTED_UNKNOWN) {
// If any of the extensions are in the unknown blocklist state, that means
// there was an error the last time the blocklist was fetched. That means
// the results cannot be relied upon.
OnExtensionsCheckResult(ExtensionsStatus::kError, Blocklisted(0),
ReenabledUser(0), ReenabledAdmin(0));
return;
}
if (state == extensions::NOT_BLOCKLISTED) {
continue;
}
++blocklisted;
if (!extension_service_->IsExtensionEnabled(extension_id)) {
continue;
}
if (extension_service_->UserCanDisableInstalledExtension(extension_id)) {
++reenabled_by_user;
} else {
++reenabled_by_admin;
}
}
if (blocklisted == 0) {
OnExtensionsCheckResult(ExtensionsStatus::kNoneBlocklisted, Blocklisted(0),
ReenabledUser(0), ReenabledAdmin(0));
} else if (reenabled_by_user == 0 && reenabled_by_admin == 0) {
OnExtensionsCheckResult(ExtensionsStatus::kBlocklistedAllDisabled,
Blocklisted(blocklisted), ReenabledUser(0),
ReenabledAdmin(0));
} else if (reenabled_by_user > 0 && reenabled_by_admin == 0) {
OnExtensionsCheckResult(ExtensionsStatus::kBlocklistedReenabledAllByUser,
Blocklisted(blocklisted),
ReenabledUser(reenabled_by_user),
ReenabledAdmin(0));
} else if (reenabled_by_admin > 0 && reenabled_by_user == 0) {
OnExtensionsCheckResult(ExtensionsStatus::kBlocklistedReenabledAllByAdmin,
Blocklisted(blocklisted), ReenabledUser(0),
ReenabledAdmin(reenabled_by_admin));
} else {
OnExtensionsCheckResult(ExtensionsStatus::kBlocklistedReenabledSomeByUser,
Blocklisted(blocklisted),
ReenabledUser(reenabled_by_user),
ReenabledAdmin(reenabled_by_admin));
}
}
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
void SafetyCheckHandler::CheckChromeCleaner() {
if (safe_browsing::ChromeCleanerController::GetInstance()->HasObserver(
this)) {
// Observer already registered. Just fetch the current CCT status.
OnChromeCleanerCheckResult(fetchChromeCleanerStatus(timestamp_delegate_));
} else {
// Registering the observer immediately triggers a callback with the
// current state.
safe_browsing::ChromeCleanerController::GetInstance()->AddObserver(this);
}
// Log the current status into metrics.
if (chrome_cleaner_status_ != ChromeCleanerStatus::kHidden &&
chrome_cleaner_status_ != ChromeCleanerStatus::kChecking) {
base::UmaHistogramEnumeration("Settings.SafetyCheck.ChromeCleanerResult",
chrome_cleaner_status_);
}
CompleteParentIfChildrenCompleted();
}
#endif
void SafetyCheckHandler::OnUpdateCheckResult(UpdateStatus status) {
update_status_ = status;
if (update_status_ != UpdateStatus::kChecking) {
base::UmaHistogramEnumeration("Settings.SafetyCheck.UpdatesResult",
update_status_);
}
// TODO(crbug/1072432): Since the UNKNOWN state is not present in JS in M83,
// use FAILED_OFFLINE, which uses the same icon.
FireBasicSafetyCheckWebUiListener(
kUpdatesEvent,
static_cast<int>(update_status_ != UpdateStatus::kUnknown
? update_status_
: UpdateStatus::kFailedOffline),
GetStringForUpdates(update_status_));
CompleteParentIfChildrenCompleted();
}
void SafetyCheckHandler::OnPasswordsCheckResult(PasswordsStatus status,
Compromised compromised,
Weak weak,
Done done,
Total total) {
base::DictionaryValue event;
event.SetIntKey(kNewState, static_cast<int>(status));
event.SetStringKey(kDisplayString, GetStringForPasswords(status, compromised,
weak, done, total));
FireWebUIListener(kPasswordsEvent, event);
if (status != PasswordsStatus::kChecking) {
base::UmaHistogramEnumeration("Settings.SafetyCheck.PasswordsResult",
status);
}
passwords_status_ = status;
CompleteParentIfChildrenCompleted();
}
void SafetyCheckHandler::OnExtensionsCheckResult(
ExtensionsStatus status,
Blocklisted blocklisted,
ReenabledUser reenabled_user,
ReenabledAdmin reenabled_admin) {
base::DictionaryValue event;
event.SetIntKey(kNewState, static_cast<int>(status));
event.SetStringKey(kDisplayString,
GetStringForExtensions(status, Blocklisted(blocklisted),
reenabled_user, reenabled_admin));
FireWebUIListener(kExtensionsEvent, event);
if (status != ExtensionsStatus::kChecking) {
base::UmaHistogramEnumeration("Settings.SafetyCheck.ExtensionsResult",
status);
}
extensions_status_ = status;
CompleteParentIfChildrenCompleted();
}
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
void SafetyCheckHandler::OnChromeCleanerCheckResult(
SafetyCheckHandler::ChromeCleanerResult result) {
base::DictionaryValue event;
event.SetIntKey(kNewState, static_cast<int>(result.status));
event.SetStringKey(
kDisplayString,
GetStringForChromeCleaner(result.status, result.cct_completion_time,
timestamp_delegate_->GetSystemTime()));
FireWebUIListener(kChromeCleanerEvent, event);
chrome_cleaner_status_ = result.status;
}
#endif
base::string16 SafetyCheckHandler::GetStringForParent(ParentStatus status) {
switch (status) {
case ParentStatus::kBefore:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_PARENT_PRIMARY_LABEL_BEFORE);
case ParentStatus::kChecking:
return l10n_util::GetStringUTF16(IDS_SETTINGS_SAFETY_CHECK_RUNNING);
case ParentStatus::kAfter:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_PARENT_PRIMARY_LABEL_AFTER);
}
}
base::string16 SafetyCheckHandler::GetStringForUpdates(UpdateStatus status) {
switch (status) {
case UpdateStatus::kChecking:
return base::UTF8ToUTF16("");
case UpdateStatus::kUpdated:
#if BUILDFLAG(IS_CHROMEOS_ASH)
return ui::SubstituteChromeOSDeviceType(IDS_SETTINGS_UPGRADE_UP_TO_DATE);
#else
return l10n_util::GetStringUTF16(IDS_SETTINGS_UPGRADE_UP_TO_DATE);
#endif
case UpdateStatus::kUpdating:
return l10n_util::GetStringUTF16(IDS_SETTINGS_UPGRADE_UPDATING);
case UpdateStatus::kRelaunch:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_UPGRADE_SUCCESSFUL_RELAUNCH);
case UpdateStatus::kDisabledByAdmin:
return l10n_util::GetStringFUTF16(
IDS_SETTINGS_SAFETY_CHECK_UPDATES_DISABLED_BY_ADMIN,
base::ASCIIToUTF16(chrome::kWhoIsMyAdministratorHelpURL));
case UpdateStatus::kFailedOffline:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_UPDATES_FAILED_OFFLINE);
case UpdateStatus::kFailed:
return l10n_util::GetStringFUTF16(
IDS_SETTINGS_SAFETY_CHECK_UPDATES_FAILED,
base::ASCIIToUTF16(chrome::kChromeFixUpdateProblems));
case UpdateStatus::kUnknown:
return l10n_util::GetStringFUTF16(
IDS_SETTINGS_ABOUT_PAGE_BROWSER_VERSION,
base::UTF8ToUTF16(version_info::GetVersionNumber()),
l10n_util::GetStringUTF16(version_info::IsOfficialBuild()
? IDS_VERSION_UI_OFFICIAL
: IDS_VERSION_UI_UNOFFICIAL),
base::UTF8ToUTF16(chrome::GetChannelName()),
l10n_util::GetStringUTF16(VersionUI::VersionProcessorVariation()));
// This state is only used on Android for recording metrics. This codepath
// is unreachable.
case UpdateStatus::kOutdated:
return base::UTF8ToUTF16("");
}
}
base::string16 SafetyCheckHandler::GetStringForSafeBrowsing(
SafeBrowsingStatus status) {
switch (status) {
case SafeBrowsingStatus::kChecking:
return base::UTF8ToUTF16("");
case SafeBrowsingStatus::kEnabled:
case SafeBrowsingStatus::kEnabledStandard:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_STANDARD);
case SafeBrowsingStatus::kEnabledEnhanced:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_ENHANCED);
case SafeBrowsingStatus::kDisabled:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_DISABLED);
case SafeBrowsingStatus::kDisabledByAdmin:
return l10n_util::GetStringFUTF16(
IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_DISABLED_BY_ADMIN,
base::ASCIIToUTF16(chrome::kWhoIsMyAdministratorHelpURL));
case SafeBrowsingStatus::kDisabledByExtension:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_DISABLED_BY_EXTENSION);
case SafeBrowsingStatus::kEnabledStandardAvailableEnhanced:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_SAFE_BROWSING_ENABLED_STANDARD_AVAILABLE_ENHANCED);
}
}
base::string16 SafetyCheckHandler::GetStringForPasswords(
PasswordsStatus status,
Compromised compromised,
Weak weak,
Done done,
Total total) {
switch (status) {
case PasswordsStatus::kChecking: {
// Unable to get progress for some reason.
if (total.value() == 0) {
return base::UTF8ToUTF16("");
}
return l10n_util::GetStringFUTF16(IDS_SETTINGS_CHECK_PASSWORDS_PROGRESS,
base::FormatNumber(done.value()),
base::FormatNumber(total.value()));
}
case PasswordsStatus::kSafe:
return l10n_util::GetPluralStringFUTF16(
IDS_SETTINGS_COMPROMISED_PASSWORDS_COUNT, 0);
case PasswordsStatus::kCompromisedExist:
// TODO(crbug.com/1128904): Clean up the old code path.
if (!base::FeatureList::IsEnabled(features::kSafetyCheckWeakPasswords)) {
return l10n_util::GetPluralStringFUTF16(
IDS_SETTINGS_COMPROMISED_PASSWORDS_COUNT, compromised.value());
}
if (weak.value() == 0) {
// Only compromised passwords, no weak passwords.
return l10n_util::GetPluralStringFUTF16(
IDS_SETTINGS_COMPROMISED_PASSWORDS_COUNT_SHORT,
compromised.value());
} else {
// Both compromised and weak passwords.
return l10n_util::GetStringFUTF16(
IDS_CONCAT_TWO_STRINGS_WITH_COMMA,
l10n_util::GetPluralStringFUTF16(
IDS_SETTINGS_COMPROMISED_PASSWORDS_COUNT_SHORT,
compromised.value()),
l10n_util::GetPluralStringFUTF16(
IDS_SETTINGS_WEAK_PASSWORDS_COUNT_SHORT, weak.value()));
}
case PasswordsStatus::kWeakPasswordsExist:
// Only weak passwords.
return l10n_util::GetPluralStringFUTF16(
IDS_SETTINGS_WEAK_PASSWORDS_COUNT_SHORT, weak.value());
case PasswordsStatus::kOffline:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_OFFLINE);
case PasswordsStatus::kNoPasswords:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_NO_PASSWORDS);
case PasswordsStatus::kSignedOut:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_PASSWORDS_SIGNED_OUT);
case PasswordsStatus::kQuotaLimit:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_QUOTA_LIMIT);
case PasswordsStatus::kError:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_CHECK_PASSWORDS_ERROR_GENERIC);
case PasswordsStatus::kFeatureUnavailable:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_PASSWORDS_FEATURE_UNAVAILABLE);
}
}
base::string16 SafetyCheckHandler::GetStringForExtensions(
ExtensionsStatus status,
Blocklisted blocklisted,
ReenabledUser reenabled_user,
ReenabledAdmin reenabled_admin) {
switch (status) {
case ExtensionsStatus::kChecking:
return base::UTF8ToUTF16("");
case ExtensionsStatus::kError:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_EXTENSIONS_ERROR);
case ExtensionsStatus::kNoneBlocklisted:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_EXTENSIONS_SAFE);
case ExtensionsStatus::kBlocklistedAllDisabled:
return l10n_util::GetPluralStringFUTF16(
IDS_SETTINGS_SAFETY_CHECK_EXTENSIONS_BLOCKLISTED_OFF,
blocklisted.value());
case ExtensionsStatus::kBlocklistedReenabledAllByUser:
return l10n_util::GetPluralStringFUTF16(
IDS_SETTINGS_SAFETY_CHECK_EXTENSIONS_BLOCKLISTED_ON_USER,
reenabled_user.value());
case ExtensionsStatus::kBlocklistedReenabledSomeByUser:
return l10n_util::GetStringFUTF16(
IDS_CONCAT_TWO_STRINGS_WITH_PERIODS,
l10n_util::GetPluralStringFUTF16(
IDS_SETTINGS_SAFETY_CHECK_EXTENSIONS_BLOCKLISTED_ON_USER,
reenabled_user.value()),
l10n_util::GetPluralStringFUTF16(
IDS_SETTINGS_SAFETY_CHECK_EXTENSIONS_BLOCKLISTED_ON_ADMIN,
reenabled_admin.value()));
case ExtensionsStatus::kBlocklistedReenabledAllByAdmin:
return l10n_util::GetPluralStringFUTF16(
IDS_SETTINGS_SAFETY_CHECK_EXTENSIONS_BLOCKLISTED_ON_ADMIN,
reenabled_admin.value());
}
}
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
base::string16 SafetyCheckHandler::GetStringForChromeCleaner(
ChromeCleanerStatus status,
base::Time cct_completion_time,
base::Time system_time) {
switch (status) {
case ChromeCleanerStatus::kHidden:
case ChromeCleanerStatus::kChecking:
return base::UTF8ToUTF16("");
case ChromeCleanerStatus::kInfected:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_INFECTED);
case ChromeCleanerStatus::kRebootRequired:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_RESET_CLEANUP_TITLE_RESTART);
case ChromeCleanerStatus::kScanningForUws:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_SCANNING);
case ChromeCleanerStatus::kRemovingUws:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_REMOVING);
case ChromeCleanerStatus::kDisabledByAdmin:
return l10n_util::GetStringFUTF16(
IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_DISABLED_BY_ADMIN,
base::ASCIIToUTF16(chrome::kWhoIsMyAdministratorHelpURL));
case ChromeCleanerStatus::kError:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_ERROR);
case ChromeCleanerStatus::kNoUwsFoundWithTimestamp:
return SafetyCheckHandler::GetStringForChromeCleanerRan(
cct_completion_time, system_time);
case ChromeCleanerStatus::kNoUwsFoundWithoutTimestamp:
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_NO_UWS_WITHOUT_TIMESTAMP);
}
}
#endif
base::string16 SafetyCheckHandler::GetStringForTimePassed(
base::Time completion_timestamp,
base::Time system_time,
int less_than_one_minute_ago_message_id,
int minutes_ago_message_id,
int hours_ago_message_id,
int yesterday_message_id,
int days_ago_message_id) {
base::Time::Exploded completion_time_exploded;
completion_timestamp.LocalExplode(&completion_time_exploded);
base::Time::Exploded system_time_exploded;
system_time.LocalExplode(&system_time_exploded);
const base::Time time_yesterday = system_time - base::TimeDelta::FromDays(1);
base::Time::Exploded time_yesterday_exploded;
time_yesterday.LocalExplode(&time_yesterday_exploded);
const auto time_diff = system_time - completion_timestamp;
if (completion_time_exploded.year == system_time_exploded.year &&
completion_time_exploded.month == system_time_exploded.month &&
completion_time_exploded.day_of_month ==
system_time_exploded.day_of_month) {
// The timestamp is today.
const int time_diff_in_mins = time_diff.InMinutes();
if (time_diff_in_mins == 0) {
return l10n_util::GetStringUTF16(less_than_one_minute_ago_message_id);
} else if (time_diff_in_mins < 60) {
return l10n_util::GetPluralStringFUTF16(minutes_ago_message_id,
time_diff_in_mins);
} else {
return l10n_util::GetPluralStringFUTF16(hours_ago_message_id,
time_diff_in_mins / 60);
}
} else if (completion_time_exploded.year == time_yesterday_exploded.year &&
completion_time_exploded.month == time_yesterday_exploded.month &&
completion_time_exploded.day_of_month ==
time_yesterday_exploded.day_of_month) {
// The timestamp was yesterday.
return l10n_util::GetStringUTF16(yesterday_message_id);
} else {
// The timestamp is longer ago than yesterday.
// TODO(crbug.com/1015841): While a minor issue, this is not be the ideal
// way to calculate the days passed since the timestamp. For example,
// <48 h might still be 2 days ago.
const int time_diff_in_days = time_diff.InDays();
return l10n_util::GetPluralStringFUTF16(days_ago_message_id,
time_diff_in_days);
}
}
base::string16 SafetyCheckHandler::GetStringForParentRan(
base::Time safety_check_completion_time,
base::Time system_time) {
return SafetyCheckHandler::GetStringForTimePassed(
safety_check_completion_time, system_time,
IDS_SETTINGS_SAFETY_CHECK_PARENT_PRIMARY_LABEL_AFTER,
IDS_SETTINGS_SAFETY_CHECK_PARENT_PRIMARY_LABEL_AFTER_MINS,
IDS_SETTINGS_SAFETY_CHECK_PARENT_PRIMARY_LABEL_AFTER_HOURS,
IDS_SETTINGS_SAFETY_CHECK_PARENT_PRIMARY_LABEL_AFTER_YESTERDAY,
IDS_SETTINGS_SAFETY_CHECK_PARENT_PRIMARY_LABEL_AFTER_DAYS);
}
base::string16 SafetyCheckHandler::GetStringForParentRan(
base::Time safety_check_completion_time) {
return SafetyCheckHandler::GetStringForParentRan(safety_check_completion_time,
base::Time::Now());
}
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
base::string16 SafetyCheckHandler::GetStringForChromeCleanerRan(
base::Time cct_completion_time,
base::Time system_time) {
if (cct_completion_time.is_null()) {
return l10n_util::GetStringUTF16(
IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_NO_UWS_WITHOUT_TIMESTAMP);
}
return SafetyCheckHandler::GetStringForTimePassed(
cct_completion_time, system_time,
IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_NO_UWS_WITH_TIMESTAMP_AFTER_SECONDS,
IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_NO_UWS_WITH_TIMESTAMP_AFTER_MINUTES,
IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_NO_UWS_WITH_TIMESTAMP_AFTER_HOURS,
IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_NO_UWS_WITH_TIMESTAMP_YESTERDAY,
IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_NO_UWS_WITH_TIMESTAMP_AFTER_DAYS);
}
#endif
void SafetyCheckHandler::DetermineIfOfflineOrError(bool connected) {
OnUpdateCheckResult(connected ? UpdateStatus::kFailed
: UpdateStatus::kFailedOffline);
}
void SafetyCheckHandler::DetermineIfNoPasswordsOrSafe(
const std::vector<extensions::api::passwords_private::PasswordUiEntry>&
passwords) {
OnPasswordsCheckResult(passwords.empty() ? PasswordsStatus::kNoPasswords
: PasswordsStatus::kSafe,
Compromised(0), Weak(0), Done(0), Total(0));
}
void SafetyCheckHandler::UpdatePasswordsResultOnCheckIdle() {
size_t num_compromised =
passwords_delegate_->GetCompromisedCredentials().size();
size_t num_weak = passwords_delegate_->GetWeakCredentials().size();
// TODO(crbug.com/1128904): Clean up the old code path.
if (num_compromised == 0 &&
(num_weak == 0 ||
!base::FeatureList::IsEnabled(features::kSafetyCheckWeakPasswords))) {
// If there are no |OnCredentialDone| callbacks with is_leaked = true, no
// need to wait for InsecureCredentialsManager callbacks any longer, since
// there should be none for the current password check.
if (!compromised_passwords_exist_) {
observed_insecure_credentials_manager_.RemoveAll();
}
passwords_delegate_->GetSavedPasswordsList(
base::BindOnce(&SafetyCheckHandler::DetermineIfNoPasswordsOrSafe,
base::Unretained(this)));
} else if (num_compromised > 0) {
// At least one compromised password. Treat as compromises.
OnPasswordsCheckResult(PasswordsStatus::kCompromisedExist,
Compromised(num_compromised), Weak(num_weak),
Done(0), Total(0));
} else {
// No compromised but weak passwords. Treat as weak passwords only.
OnPasswordsCheckResult(PasswordsStatus::kWeakPasswordsExist,
Compromised(num_compromised), Weak(num_weak),
Done(0), Total(0));
}
}
void SafetyCheckHandler::OnVersionUpdaterResult(VersionUpdater::Status status,
int progress,
bool rollback,
bool powerwash,
const std::string& version,
int64_t update_size,
const base::string16& message) {
if (status == VersionUpdater::FAILED) {
update_helper_->CheckConnectivity(
base::BindOnce(&SafetyCheckHandler::DetermineIfOfflineOrError,
base::Unretained(this)));
return;
}
OnUpdateCheckResult(ConvertToUpdateStatus(status));
}
void SafetyCheckHandler::OnSafeBrowsingCheckResult(
SafetyCheckHandler::SafeBrowsingStatus status) {
safe_browsing_status_ = status;
if (safe_browsing_status_ != SafeBrowsingStatus::kChecking) {
base::UmaHistogramEnumeration("Settings.SafetyCheck.SafeBrowsingResult",
safe_browsing_status_);
}
FireBasicSafetyCheckWebUiListener(
kSafeBrowsingEvent, static_cast<int>(safe_browsing_status_),
GetStringForSafeBrowsing(safe_browsing_status_));
CompleteParentIfChildrenCompleted();
}
void SafetyCheckHandler::OnStateChanged(
password_manager::BulkLeakCheckService::State state) {
using password_manager::BulkLeakCheckService;
switch (state) {
case BulkLeakCheckService::State::kIdle:
case BulkLeakCheckService::State::kCanceled: {
UpdatePasswordsResultOnCheckIdle();
observed_leak_check_.RemoveAll();
return;
}
case BulkLeakCheckService::State::kRunning:
OnPasswordsCheckResult(PasswordsStatus::kChecking, Compromised(0),
Weak(0), Done(0), Total(0));
// Non-terminal state, so nothing else needs to be done.
return;
case BulkLeakCheckService::State::kSignedOut:
OnPasswordsCheckResult(PasswordsStatus::kSignedOut, Compromised(0),
Weak(0), Done(0), Total(0));
break;
case BulkLeakCheckService::State::kNetworkError:
OnPasswordsCheckResult(PasswordsStatus::kOffline, Compromised(0), Weak(0),
Done(0), Total(0));
break;
case BulkLeakCheckService::State::kQuotaLimit:
OnPasswordsCheckResult(PasswordsStatus::kQuotaLimit, Compromised(0),
Weak(0), Done(0), Total(0));
break;
case BulkLeakCheckService::State::kTokenRequestFailure:
OnPasswordsCheckResult(PasswordsStatus::kFeatureUnavailable,
Compromised(0), Weak(0), Done(0), Total(0));
break;
case BulkLeakCheckService::State::kHashingFailure:
case BulkLeakCheckService::State::kServiceError:
OnPasswordsCheckResult(PasswordsStatus::kError, Compromised(0), Weak(0),
Done(0), Total(0));
break;
}
// Stop observing the leak service and credentials manager in all non-idle
// states.
observed_leak_check_.RemoveAll();
observed_insecure_credentials_manager_.RemoveAll();
}
void SafetyCheckHandler::OnCredentialDone(
const password_manager::LeakCheckCredential& credential,
password_manager::IsLeaked is_leaked) {
// If a leaked credential is discovered, this is guaranteed to not be a safe
// state.
if (is_leaked) {
compromised_passwords_exist_ = true;
}
extensions::api::passwords_private::PasswordCheckStatus status =
passwords_delegate_->GetPasswordCheckStatus();
// Send progress updates only if the check is still running.
if (status.state ==
extensions::api::passwords_private::PASSWORD_CHECK_STATE_RUNNING &&
status.already_processed && status.remaining_in_queue) {
Done done = Done(*(status.already_processed));
Total total = Total(*(status.remaining_in_queue) + done.value());
OnPasswordsCheckResult(PasswordsStatus::kChecking, Compromised(0), Weak(0),
done, total);
}
}
void SafetyCheckHandler::OnCompromisedCredentialsChanged(
password_manager::InsecureCredentialsManager::CredentialsView credentials) {
extensions::api::passwords_private::PasswordCheckStatus status =
passwords_delegate_->GetPasswordCheckStatus();
// Ignore the event, unless the password check is idle with no errors.
if (status.state !=
extensions::api::passwords_private::PASSWORD_CHECK_STATE_IDLE) {
return;
}
UpdatePasswordsResultOnCheckIdle();
// Stop observing the manager to avoid dynamically updating the result.
observed_insecure_credentials_manager_.RemoveAll();
}
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
void SafetyCheckHandler::OnIdle(
safe_browsing::ChromeCleanerController::IdleReason idle_reason) {
OnChromeCleanerCheckResult(fetchChromeCleanerStatus(timestamp_delegate_));
}
void SafetyCheckHandler::OnReporterRunning() {
OnChromeCleanerCheckResult(fetchChromeCleanerStatus(timestamp_delegate_));
}
void SafetyCheckHandler::OnScanning() {
OnChromeCleanerCheckResult(fetchChromeCleanerStatus(timestamp_delegate_));
}
void SafetyCheckHandler::OnInfected(
bool is_powered_by_partner,
const safe_browsing::ChromeCleanerScannerResults& scanner_results) {
OnChromeCleanerCheckResult(fetchChromeCleanerStatus(timestamp_delegate_));
}
void SafetyCheckHandler::OnCleaning(
bool is_powered_by_partner,
const safe_browsing::ChromeCleanerScannerResults& scanner_results) {
OnChromeCleanerCheckResult(fetchChromeCleanerStatus(timestamp_delegate_));
}
void SafetyCheckHandler::OnRebootRequired() {
OnChromeCleanerCheckResult(fetchChromeCleanerStatus(timestamp_delegate_));
}
void SafetyCheckHandler::OnRebootFailed() {
OnChromeCleanerCheckResult(fetchChromeCleanerStatus(timestamp_delegate_));
}
#endif
void SafetyCheckHandler::OnJavascriptAllowed() {}
void SafetyCheckHandler::OnJavascriptDisallowed() {
// If the user refreshes the Settings tab in the delay between starting safety
// check and now, then the check should no longer be run. Invalidating the
// pointer prevents the callback from returning after the delay.
weak_ptr_factory_.InvalidateWeakPtrs();
// Remove |this| as an observer for BulkLeakCheck. This takes care of an edge
// case when the page is reloaded while the password check is in progress and
// another safety check is started. Otherwise |observed_leak_check_|
// automatically calls RemoveAll() on destruction.
observed_leak_check_.RemoveAll();
// Destroy the version updater to prevent getting a callback and firing a
// WebUI event, which would cause a crash.
version_updater_.reset();
// Stop observing safety check events.
safety_check_.reset(nullptr);
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
// Remove |this| as an observer for the Chrome cleaner.
safe_browsing::ChromeCleanerController::GetInstance()->RemoveObserver(this);
#endif
}
void SafetyCheckHandler::RegisterMessages() {
// Usage of base::Unretained(this) is safe, because web_ui() owns `this` and
// won't release ownership until destruction.
web_ui()->RegisterMessageCallback(
kPerformSafetyCheck,
base::BindRepeating(&SafetyCheckHandler::HandlePerformSafetyCheck,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
kGetParentRanDisplayString,
base::BindRepeating(&SafetyCheckHandler::HandleGetParentRanDisplayString,
base::Unretained(this)));
}
void SafetyCheckHandler::CompleteParentIfChildrenCompleted() {
if (update_status_ == UpdateStatus::kChecking ||
passwords_status_ == PasswordsStatus::kChecking ||
safe_browsing_status_ == SafeBrowsingStatus::kChecking ||
extensions_status_ == ExtensionsStatus::kChecking) {
return;
}
#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
if (chrome_cleaner_status_ == ChromeCleanerStatus::kChecking) {
return;
}
#endif
// All children checks completed.
parent_status_ = ParentStatus::kAfter;
// Remember when safety check completed.
safety_check_completion_time_ = base::Time::Now();
// Update UI.
FireBasicSafetyCheckWebUiListener(kParentEvent,
static_cast<int>(parent_status_),
GetStringForParent(parent_status_));
}
void SafetyCheckHandler::FireBasicSafetyCheckWebUiListener(
const std::string& event_name,
int new_state,
const base::string16& display_string) {
base::DictionaryValue event;
event.SetIntKey(kNewState, new_state);
event.SetStringKey(kDisplayString, display_string);
FireWebUIListener(event_name, event);
}