// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/content_suggestions/safety_check/utils.h"
#import "base/metrics/user_metrics.h"
#import "components/password_manager/core/browser/ui/credential_ui_entry.h"
#import "components/password_manager/core/browser/ui/password_check_referrer.h"
#import "components/version_info/version_info.h"
#import "ios/chrome/browser/passwords/model/password_checkup_utils.h"
#import "ios/chrome/browser/shared/public/commands/application_commands.h"
#import "ios/chrome/browser/shared/public/commands/open_new_tab_command.h"
#import "ios/chrome/browser/shared/public/commands/settings_commands.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_state.h"
#import "ios/chrome/browser/ui/content_suggestions/safety_check/utils.h"
#import "ios/chrome/common/channel_info.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util.h"
#import "ui/base/l10n/l10n_util_mac.h"
#import "ui/base/l10n/time_format.h"
#import "url/gurl.h"
namespace {
// The amount of time after which the last run timestamp is shown, instead of
// displaying the last run "just now" text.
constexpr base::TimeDelta kDisplayTimestampThreshold = base::Minutes(1);
// Returns the number of unique warning types found in `counts`.
//
// NOTE: Only considers compromised, reused, and weak passwords. (Does not
// consider dismissed passwords.)
int UniqueWarningTypeCount(
const std::vector<password_manager::CredentialUIEntry>&
compromised_credentials) {
password_manager::InsecurePasswordCounts counts =
password_manager::CountInsecurePasswordsPerInsecureType(
compromised_credentials);
int type_count = 0;
if (counts.compromised_count > 0) {
type_count++;
}
if (counts.reused_count > 0) {
type_count++;
}
if (counts.weak_count > 0) {
type_count++;
}
return type_count;
}
} // namespace
using password_manager::WarningType;
using password_manager::WarningType::kCompromisedPasswordsWarning;
void HandleSafetyCheckUpdateChromeTap(
const GURL& chrome_upgrade_url,
id<ApplicationCommands> applicationHandler) {
switch (::GetChannel()) {
case version_info::Channel::STABLE:
case version_info::Channel::BETA:
case version_info::Channel::DEV:
case version_info::Channel::CANARY: {
OpenNewTabCommand* command =
[OpenNewTabCommand commandWithURLFromChrome:chrome_upgrade_url];
[applicationHandler openURLInNewTab:command];
break;
}
case version_info::Channel::UNKNOWN:
break;
}
}
void HandleSafetyCheckPasswordTap(
std::vector<password_manager::CredentialUIEntry>& compromised_credentials,
id<ApplicationCommands> applicationHandler,
id<SettingsCommands> settingsHandler) {
// If there's only one compromised credential, navigate users to the detail
// view for that particular credential.
if (compromised_credentials.size() == 1) {
password_manager::CredentialUIEntry credential =
compromised_credentials.front();
[settingsHandler showPasswordDetailsForCredential:credential
inEditMode:NO
showCancelButton:YES];
return;
}
int unique_warning_type_count =
UniqueWarningTypeCount(compromised_credentials);
// If there are multiple passwords (of the same warning type),
// navigate users to the Password Checkup overview screen for that particular
// warning type.
if (unique_warning_type_count == 1) {
WarningType type =
password_manager::GetWarningOfHighestPriority(compromised_credentials);
[applicationHandler
showPasswordIssuesWithWarningType:type
referrer:password_manager::
PasswordCheckReferrer::
kSafetyCheckMagicStack];
return;
}
// If there are multiple passwords (with multiple warning types), or no
// compromised credentials at all, navigate users to the Password Checkup
// overview screen.
base::RecordAction(
base::UserMetricsAction("MobileMagicStackOpenPasswordCheckup"));
[applicationHandler
showPasswordCheckupPageForReferrer:
password_manager::PasswordCheckReferrer::kSafetyCheckMagicStack];
}
bool InvalidUpdateChromeState(UpdateChromeSafetyCheckState state) {
return state == UpdateChromeSafetyCheckState::kOutOfDate;
}
bool InvalidPasswordState(PasswordSafetyCheckState state) {
return state == PasswordSafetyCheckState::kUnmutedCompromisedPasswords ||
state == PasswordSafetyCheckState::kReusedPasswords ||
state == PasswordSafetyCheckState::kWeakPasswords;
}
bool InvalidSafeBrowsingState(SafeBrowsingSafetyCheckState state) {
return state == SafeBrowsingSafetyCheckState::kUnsafe;
}
bool CanRunSafetyCheck(std::optional<base::Time> last_run_time) {
// The Safety Check should be run if it's never been run before.
if (!last_run_time.has_value()) {
return true;
}
base::TimeDelta last_run_age = base::Time::Now() - last_run_time.value();
return last_run_age > TimeDelayForSafetyCheckAutorun();
}
NSString* FormatElapsedTimeSinceLastSafetyCheck(
std::optional<base::Time> last_run_time) {
if (!last_run_time.has_value()) {
return l10n_util::GetNSString(IDS_IOS_CHECK_NEVER_RUN);
}
base::TimeDelta elapsed_time = base::Time::Now() - last_run_time.value();
// If the latest Safety Check run happened less than
// `kDisplayTimestampThreshold` ago, show "Checked just now" instead of the
// timestamp.
if (elapsed_time < kDisplayTimestampThreshold) {
return l10n_util::GetNSString(IDS_IOS_CHECK_FINISHED_JUST_NOW);
}
std::u16string timestamp = ui::TimeFormat::SimpleWithMonthAndYear(
ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_SHORT,
elapsed_time, true);
return l10n_util::GetNSStringF(IDS_IOS_SAFETY_CHECK_LAST_COMPLETED_CHECK,
timestamp);
}