| // Copyright 2022 The Chromium Authors |
| // 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_hub_handler.h" |
| |
| #include <memory> |
| #include <string_view> |
| |
| #include "base/check.h" |
| #include "base/json/values_util.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/thread_checker.h" |
| #include "base/time/default_clock.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/extensions/cws_info_service.h" |
| #include "chrome/browser/extensions/cws_info_service_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/signin/identity_manager_factory.h" |
| #include "chrome/browser/ui/promos/ios_promo_trigger_service.h" |
| #include "chrome/browser/ui/promos/ios_promo_trigger_service_factory.h" |
| #include "chrome/browser/ui/safety_hub/extensions_result.h" |
| #include "chrome/browser/ui/safety_hub/menu_notification_service_factory.h" |
| #include "chrome/browser/ui/safety_hub/notification_permission_review_service.h" |
| #include "chrome/browser/ui/safety_hub/notification_permission_review_service_factory.h" |
| #include "chrome/browser/ui/safety_hub/password_status_check_service.h" |
| #include "chrome/browser/ui/safety_hub/password_status_check_service_factory.h" |
| #include "chrome/browser/ui/safety_hub/revoked_permissions_service.h" |
| #include "chrome/browser/ui/safety_hub/revoked_permissions_service_factory.h" |
| #include "chrome/browser/ui/safety_hub/safe_browsing_result.h" |
| #include "chrome/browser/ui/safety_hub/safety_hub_constants.h" |
| #include "chrome/browser/ui/safety_hub/safety_hub_hats_service.h" |
| #include "chrome/browser/ui/safety_hub/safety_hub_hats_service_factory.h" |
| #include "chrome/browser/ui/safety_hub/safety_hub_result.h" |
| #include "chrome/browser/ui/safety_hub/safety_hub_util.h" |
| #include "chrome/browser/ui/webui/settings/site_settings_helper.h" |
| #include "chrome/browser/ui/webui/version/version_ui.h" |
| #include "chrome/browser/upgrade_detector/build_state.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/grit/branded_strings.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/content_settings/core/common/content_settings.h" |
| #include "components/content_settings/core/common/content_settings_constraints.h" |
| #include "components/content_settings/core/common/content_settings_pattern.h" |
| #include "components/content_settings/core/common/content_settings_types.h" |
| #include "components/content_settings/core/common/features.h" |
| #include "components/password_manager/core/common/password_manager_pref_names.h" |
| #include "components/permissions/constants.h" |
| #include "components/safe_browsing/core/common/features.h" |
| #include "components/safe_browsing/core/common/safe_browsing_prefs.h" |
| #include "components/sharing_message/features.h" |
| #include "components/signin/public/base/consent_level.h" |
| #include "components/signin/public/identity_manager/identity_manager.h" |
| #include "components/site_engagement/content/site_engagement_service.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_prefs_factory.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/common/manifest.h" |
| #include "safety_hub_handler.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/l10n/time_format.h" |
| #include "url/gurl.h" |
| |
| using extensions::ExtensionPrefs; |
| using extensions::ExtensionRegistry; |
| using safety_hub::SafetyHubCardState; |
| |
| namespace { |
| |
| const char kRevocationTypeKey[] = "revocation_type"; |
| |
| // Get values from |UnusedSitePermission| object in |
| // safety_hub_browser_proxy.ts. |
| PermissionsData GetUnusedSitePermissionsFromDict( |
| const base::Value::Dict& unused_site_permissions) { |
| PermissionsData permissions_data; |
| const std::string* origin_str = |
| unused_site_permissions.FindString(site_settings::kOrigin); |
| CHECK(origin_str); |
| permissions_data.primary_pattern = |
| ContentSettingsPattern::FromString(*origin_str); |
| |
| const base::Value::List* permissions = |
| unused_site_permissions.FindList(site_settings::kPermissions); |
| CHECK(permissions); |
| for (const auto& permission : *permissions) { |
| CHECK(permission.is_string()); |
| const std::string& type_string = permission.GetString(); |
| ContentSettingsType type = |
| site_settings::ContentSettingsTypeFromGroupName(type_string); |
| CHECK(type != ContentSettingsType::DEFAULT) |
| << type_string << " is not expected to have a UI representation."; |
| permissions_data.permission_types.insert(type); |
| } |
| |
| const base::Value::Dict* chooser_permissions_data = |
| unused_site_permissions.FindDict( |
| safety_hub::kSafetyHubChooserPermissionsData); |
| permissions_data.chooser_permissions_data = |
| chooser_permissions_data ? chooser_permissions_data->Clone() |
| : base::Value::Dict(); |
| |
| // Handle expiration and lifetime for revoked permission. |
| const base::Value* js_expiration = |
| unused_site_permissions.Find(safety_hub::kExpirationKey); |
| CHECK(js_expiration); |
| base::Time expiration = base::ValueToTime(js_expiration).value(); |
| |
| const base::Value* js_lifetime = |
| unused_site_permissions.Find(safety_hub::kLifetimeKey); |
| base::TimeDelta lifetime = content_settings::RuleMetaData::ComputeLifetime( |
| /*lifetime=*/ |
| base::ValueToTimeDelta(js_lifetime).value_or(base::TimeDelta()), |
| /*expiration=*/expiration); |
| permissions_data.constraints = |
| content_settings::ContentSettingConstraints(expiration - lifetime); |
| permissions_data.constraints.set_lifetime(lifetime); |
| |
| permissions_data.revocation_type = static_cast<PermissionsRevocationType>( |
| unused_site_permissions.FindInt(kRevocationTypeKey).value_or(0)); |
| |
| return permissions_data; |
| } |
| |
| // Returns true if the card dict indicates there is something actionable for the |
| // user. |
| bool CardHasRecommendations(base::Value::Dict card_data) { |
| std::optional<int> state = card_data.FindInt(safety_hub::kCardStateKey); |
| CHECK(state.has_value()); |
| SafetyHubCardState card_state = |
| static_cast<SafetyHubCardState>(state.value()); |
| |
| return card_state == SafetyHubCardState::kWarning || |
| card_state == SafetyHubCardState::kWeak; |
| } |
| |
| void AppendModuleNameToString(std::u16string& str, |
| int uppercase_id, |
| int lowercase_id = 0) { |
| if (str.empty()) { |
| str.append(l10n_util::GetStringUTF16(uppercase_id)); |
| return; |
| } |
| |
| if (lowercase_id == 0) { |
| lowercase_id = uppercase_id; |
| } |
| |
| str.append( |
| l10n_util::GetStringUTF16(IDS_SETTINGS_SAFETY_HUB_MODULE_NAME_SEPARATOR)); |
| str.append(u" "); |
| str.append(l10n_util::GetStringUTF16(lowercase_id)); |
| } |
| |
| // Converts the entry point data into a base::Value::Dict. |
| base::Value::Dict EntryPointDataToValue(bool has_recommendations, |
| std::string header, |
| std::string subheader) { |
| base::Value::Dict dict_data; |
| |
| dict_data.Set("hasRecommendations", has_recommendations); |
| dict_data.Set("header", header); |
| dict_data.Set("subheader", subheader); |
| |
| return dict_data; |
| } |
| } // namespace |
| |
| SafetyHubHandler::SafetyHubHandler(Profile* profile) |
| : profile_(profile), clock_(base::DefaultClock::GetInstance()) { |
| prefs_observation_.Observe(ExtensionPrefs::Get(profile_)); |
| extension_registry_observation_.Observe(ExtensionRegistry::Get(profile_)); |
| pref_change_registrar_.Init(profile_->GetPrefs()); |
| pref_change_registrar_.Add( |
| prefs::kSafeBrowsingEnhanced, |
| base::BindRepeating(&SafetyHubHandler::OnSafeBrowsingEnhancedChanged, |
| base::Unretained(this))); |
| } |
| SafetyHubHandler::~SafetyHubHandler() = default; |
| |
| // static |
| std::unique_ptr<SafetyHubHandler> SafetyHubHandler::GetForProfile( |
| Profile* profile) { |
| return std::make_unique<SafetyHubHandler>(profile); |
| } |
| |
| void SafetyHubHandler::HandleGetRevokedUnusedSitePermissionsList( |
| const base::Value::List& args) { |
| AllowJavascript(); |
| |
| CHECK_EQ(1U, args.size()); |
| const base::Value& callback_id = args[0]; |
| |
| base::Value::List result = PopulateUnusedSitePermissionsData(); |
| |
| ResolveJavascriptCallback(callback_id, base::Value(std::move(result))); |
| } |
| |
| void SafetyHubHandler::HandleAllowPermissionsAgainForUnusedSite( |
| const base::Value::List& args) { |
| CHECK_EQ(1U, args.size()); |
| CHECK(args[0].is_string()); |
| const std::string& origin_str = args[0].GetString(); |
| |
| RevokedPermissionsService* service = |
| RevokedPermissionsServiceFactory::GetForProfile(profile_); |
| CHECK(service); |
| |
| url::Origin origin = url::Origin::Create(GURL(origin_str)); |
| |
| service->RegrantPermissionsForOrigin(origin); |
| SendUnusedSitePermissionsReviewList(); |
| } |
| |
| void SafetyHubHandler::HandleUndoAllowPermissionsAgainForUnusedSite( |
| const base::Value::List& args) { |
| CHECK_EQ(1U, args.size()); |
| CHECK(args[0].is_dict()); |
| |
| PermissionsData permissions_data = |
| GetUnusedSitePermissionsFromDict(args[0].GetDict()); |
| RevokedPermissionsService* service = |
| RevokedPermissionsServiceFactory::GetForProfile(profile_); |
| CHECK(service); |
| |
| service->UndoRegrantPermissionsForOrigin(permissions_data); |
| |
| SendUnusedSitePermissionsReviewList(); |
| } |
| |
| void SafetyHubHandler::HandleAcknowledgeRevokedUnusedSitePermissionsList( |
| const base::Value::List& args) { |
| RevokedPermissionsService* service = |
| RevokedPermissionsServiceFactory::GetForProfile(profile_); |
| CHECK(service); |
| service->ClearRevokedPermissionsList(); |
| |
| SendUnusedSitePermissionsReviewList(); |
| } |
| |
| void SafetyHubHandler::HandleUndoAcknowledgeRevokedUnusedSitePermissionsList( |
| const base::Value::List& args) { |
| CHECK_EQ(1U, args.size()); |
| CHECK(args[0].is_list()); |
| |
| const base::Value::List& unused_site_permissions_list = args[0].GetList(); |
| RevokedPermissionsService* service = |
| RevokedPermissionsServiceFactory::GetForProfile(profile_); |
| CHECK(service); |
| |
| std::vector<PermissionsData> permissions_data_list; |
| for (const auto& unused_site_permissions_js : unused_site_permissions_list) { |
| CHECK(unused_site_permissions_js.is_dict()); |
| permissions_data_list.push_back( |
| GetUnusedSitePermissionsFromDict(unused_site_permissions_js.GetDict())); |
| } |
| service->RestoreDeletedRevokedPermissionsList(permissions_data_list); |
| |
| SendUnusedSitePermissionsReviewList(); |
| } |
| |
| base::Value::List SafetyHubHandler::PopulateUnusedSitePermissionsData() { |
| base::Value::List result; |
| RevokedPermissionsService* service = |
| RevokedPermissionsServiceFactory::GetForProfile(profile_); |
| CHECK(service); |
| std::unique_ptr<RevokedPermissionsResult> service_result = |
| service->GetRevokedPermissions(); |
| for (const auto& permissions_data : service_result->GetRevokedPermissions()) { |
| base::Value::Dict revoked_permission_value; |
| revoked_permission_value.Set(site_settings::kOrigin, |
| permissions_data.primary_pattern.ToString()); |
| |
| base::Value::List permissions_value_list; |
| for (ContentSettingsType type : permissions_data.permission_types) { |
| std::string_view permission_str = |
| site_settings::ContentSettingsTypeToGroupName(type); |
| if (!permission_str.empty()) { |
| permissions_value_list.Append(permission_str); |
| } |
| } |
| |
| // Some permissions have no readable name, although Safety Hub revokes them. |
| // To prevent crashes, if there is no permission to be shown in the UI, the |
| // origin will not be added to the revoked permissions list. |
| // TODO(crbug.com/40066645): Remove this after adding check for |
| // ContentSettingsTypeToGroupName. |
| if (permissions_value_list.empty()) { |
| continue; |
| } |
| |
| revoked_permission_value.Set( |
| site_settings::kPermissions, |
| base::Value(std::move(permissions_value_list))); |
| |
| revoked_permission_value.Set( |
| safety_hub::kExpirationKey, |
| base::TimeToValue(permissions_data.constraints.expiration())); |
| |
| revoked_permission_value.Set( |
| safety_hub::kLifetimeKey, |
| base::TimeDeltaToValue(permissions_data.constraints.lifetime())); |
| |
| revoked_permission_value.Set( |
| safety_hub::kSafetyHubChooserPermissionsData, |
| base::Value(permissions_data.chooser_permissions_data.Clone())); |
| |
| revoked_permission_value.Set( |
| kRevocationTypeKey, static_cast<int>(permissions_data.revocation_type)); |
| |
| result.Append(std::move(revoked_permission_value)); |
| } |
| |
| return result; |
| } |
| |
| void SafetyHubHandler::HandleGetNotificationPermissionReviewList( |
| const base::Value::List& args) { |
| AllowJavascript(); |
| |
| const base::Value& callback_id = args[0]; |
| |
| NotificationPermissionsReviewService* service = |
| NotificationPermissionsReviewServiceFactory::GetForProfile(profile_); |
| DCHECK(service); |
| |
| if (!service) { |
| RejectJavascriptCallback(callback_id, base::Value()); |
| } |
| |
| base::Value::List result = |
| service->PopulateNotificationPermissionReviewData(); |
| |
| ResolveJavascriptCallback(callback_id, base::Value(std::move(result))); |
| } |
| |
| void SafetyHubHandler::HandleIgnoreOriginsForNotificationPermissionReview( |
| const base::Value::List& args) { |
| CHECK_EQ(1U, args.size()); |
| const base::Value::List& origins = args[0].GetList(); |
| |
| NotificationPermissionsReviewService* service = |
| NotificationPermissionsReviewServiceFactory::GetForProfile(profile_); |
| CHECK(service); |
| |
| for (const auto& origin : origins) { |
| const ContentSettingsPattern primary_pattern = |
| ContentSettingsPattern::FromString(origin.GetString()); |
| service->AddPatternToNotificationPermissionReviewBlocklist( |
| primary_pattern, ContentSettingsPattern::Wildcard()); |
| } |
| |
| SendNotificationPermissionReviewList(); |
| } |
| |
| void SafetyHubHandler::HandleResetNotificationPermissionForOrigins( |
| const base::Value::List& args) { |
| CHECK_EQ(1U, args.size()); |
| const base::Value::List& origins = args[0].GetList(); |
| |
| NotificationPermissionsReviewService* service = |
| NotificationPermissionsReviewServiceFactory::GetForProfile(profile_); |
| CHECK(service); |
| |
| for (const auto& origin : origins) { |
| service->SetNotificationPermissionsForOrigin(origin.GetString(), |
| CONTENT_SETTING_DEFAULT); |
| } |
| |
| SendNotificationPermissionReviewList(); |
| } |
| |
| void SafetyHubHandler::HandleDismissActiveMenuNotification( |
| const base::Value::List& args) { |
| SafetyHubMenuNotificationServiceFactory::GetForProfile(profile_) |
| ->DismissActiveNotification(); |
| } |
| |
| void SafetyHubHandler::HandleDismissPasswordMenuNotification( |
| const base::Value::List& args) { |
| SafetyHubMenuNotificationServiceFactory::GetForProfile(profile_) |
| ->DismissActiveNotificationOfModule( |
| safety_hub::SafetyHubModuleType::PASSWORDS); |
| } |
| |
| void SafetyHubHandler::HandleDismissExtensionsMenuNotification( |
| const base::Value::List& args) { |
| SafetyHubMenuNotificationServiceFactory::GetForProfile(profile_) |
| ->DismissActiveNotificationOfModule( |
| safety_hub::SafetyHubModuleType::EXTENSIONS); |
| } |
| |
| void SafetyHubHandler::HandleBlockNotificationPermissionForOrigins( |
| const base::Value::List& args) { |
| CHECK_EQ(1U, args.size()); |
| const base::Value::List& origins = args[0].GetList(); |
| |
| NotificationPermissionsReviewService* service = |
| NotificationPermissionsReviewServiceFactory::GetForProfile(profile_); |
| CHECK(service); |
| |
| for (const auto& origin : origins) { |
| service->SetNotificationPermissionsForOrigin(origin.GetString(), |
| CONTENT_SETTING_BLOCK); |
| } |
| |
| SendNotificationPermissionReviewList(); |
| } |
| |
| void SafetyHubHandler::HandleAllowNotificationPermissionForOrigins( |
| const base::Value::List& args) { |
| CHECK_EQ(1U, args.size()); |
| const base::Value::List& origins = args[0].GetList(); |
| |
| NotificationPermissionsReviewService* service = |
| NotificationPermissionsReviewServiceFactory::GetForProfile(profile_); |
| CHECK(service); |
| |
| for (const auto& origin : origins) { |
| service->SetNotificationPermissionsForOrigin(origin.GetString(), |
| CONTENT_SETTING_ALLOW); |
| } |
| |
| SendNotificationPermissionReviewList(); |
| } |
| |
| void SafetyHubHandler::HandleUndoIgnoreOriginsForNotificationPermissionReview( |
| const base::Value::List& args) { |
| CHECK_EQ(1U, args.size()); |
| const base::Value::List& origins = args[0].GetList(); |
| NotificationPermissionsReviewService* service = |
| NotificationPermissionsReviewServiceFactory::GetForProfile(profile_); |
| CHECK(service); |
| |
| for (const auto& origin : origins) { |
| const ContentSettingsPattern& primary_pattern = |
| ContentSettingsPattern::FromString(origin.GetString()); |
| service->RemovePatternFromNotificationPermissionReviewBlocklist( |
| primary_pattern, ContentSettingsPattern::Wildcard()); |
| } |
| SendNotificationPermissionReviewList(); |
| } |
| |
| void SafetyHubHandler::HandleGetSafeBrowsingCardData( |
| const base::Value::List& args) { |
| AllowJavascript(); |
| |
| CHECK_EQ(1U, args.size()); |
| const base::Value& callback_id = args[0]; |
| |
| ResolveJavascriptCallback( |
| callback_id, SafetyHubSafeBrowsingResult::GetSafeBrowsingCardData( |
| profile_->GetPrefs())); |
| } |
| |
| void SafetyHubHandler::HandleGetNumberOfExtensionsThatNeedReview( |
| const base::Value::List& args) { |
| const base::Value& callback_id = args[0]; |
| AllowJavascript(); |
| ResolveJavascriptCallback(callback_id, |
| base::Value(GetNumberOfExtensionsThatNeedReview())); |
| } |
| |
| void SafetyHubHandler::HandleGetPasswordCardData( |
| const base::Value::List& args) { |
| AllowJavascript(); |
| |
| CHECK_EQ(1U, args.size()); |
| const base::Value& callback_id = args[0]; |
| |
| PasswordStatusCheckService* service = |
| PasswordStatusCheckServiceFactory::GetForProfile(profile_); |
| CHECK(service); |
| |
| ResolveJavascriptCallback(callback_id, |
| base::Value(service->GetPasswordCardData())); |
| } |
| |
| void SafetyHubHandler::HandleGetVersionCardData(const base::Value::List& args) { |
| AllowJavascript(); |
| |
| CHECK_EQ(1U, args.size()); |
| const base::Value& callback_id = args[0]; |
| |
| ResolveJavascriptCallback(callback_id, |
| base::Value(safety_hub_util::GetVersionCardData())); |
| } |
| |
| void SafetyHubHandler::HandleGetSafetyHubEntryPointData( |
| const base::Value::List& args) { |
| AllowJavascript(); |
| |
| CHECK_EQ(1U, args.size()); |
| const base::Value& callback_id = args[0]; |
| |
| std::set<SafetyHubModule> modules = GetSafetyHubModulesWithRecommendations(); |
| |
| // If there is no module that needs attention, a static string will be used |
| // for the subheader. |
| if (modules.empty()) { |
| ResolveJavascriptCallback( |
| callback_id, |
| base::Value(EntryPointDataToValue( |
| false, "", |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_ENTRY_POINT_NOTHING_TO_DO)))); |
| return; |
| } |
| |
| // Modules in subheader should be added in the following order: Passwords, |
| // Version, Safe Browsing, Extensions, Notifications, Permissions. |
| std::u16string subheader = u""; |
| |
| if (modules.contains(SafetyHubModule::kPasswords)) { |
| AppendModuleNameToString(subheader, |
| IDS_SETTINGS_SAFETY_HUB_PASSWORDS_MODULE_NAME); |
| } |
| |
| if (modules.contains(SafetyHubModule::kVersion)) { |
| AppendModuleNameToString( |
| subheader, IDS_SETTINGS_SAFETY_HUB_VERSION_MODULE_UPPERCASE_NAME, |
| IDS_SETTINGS_SAFETY_HUB_VERSION_MODULE_LOWERCASE_NAME); |
| } |
| |
| if (modules.contains(SafetyHubModule::kSafeBrowsing)) { |
| AppendModuleNameToString(subheader, |
| IDS_SETTINGS_SAFETY_HUB_SAFE_BROWSING_MODULE_NAME); |
| } |
| |
| if (modules.contains(SafetyHubModule::kExtensions)) { |
| AppendModuleNameToString( |
| subheader, IDS_SETTINGS_SAFETY_HUB_EXTENSIONS_MODULE_UPPERCASE_NAME, |
| IDS_SETTINGS_SAFETY_HUB_EXTENSIONS_MODULE_LOWERCASE_NAME); |
| } |
| |
| if (modules.contains(SafetyHubModule::kNotifications)) { |
| AppendModuleNameToString( |
| subheader, IDS_SETTINGS_SAFETY_HUB_NOTIFICATIONS_MODULE_UPPERCASE_NAME, |
| IDS_SETTINGS_SAFETY_HUB_NOTIFICATIONS_MODULE_LOWERCASE_NAME); |
| } |
| |
| if (modules.contains(SafetyHubModule::kUnusedSitePermissions)) { |
| AppendModuleNameToString( |
| subheader, IDS_SETTINGS_SAFETY_HUB_PERMISSIONS_MODULE_UPPERCASE_NAME, |
| IDS_SETTINGS_SAFETY_HUB_PERMISSIONS_MODULE_LOWERCASE_NAME); |
| } |
| |
| ResolveJavascriptCallback( |
| callback_id, |
| base::Value(EntryPointDataToValue( |
| true, |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_ENTRY_POINT_HEADER), |
| base::UTF16ToUTF8(subheader)))); |
| } |
| |
| std::set<SafetyHubHandler::SafetyHubModule> |
| SafetyHubHandler::GetSafetyHubModulesWithRecommendations() { |
| std::set<SafetyHubModule> modules; |
| |
| // Passwords module |
| PasswordStatusCheckService* psc_service = |
| PasswordStatusCheckServiceFactory::GetForProfile(profile_); |
| CHECK(psc_service); |
| if (CardHasRecommendations(psc_service->GetPasswordCardData())) { |
| modules.insert(SafetyHubModule::kPasswords); |
| } |
| // Version module |
| if (CardHasRecommendations(safety_hub_util::GetVersionCardData())) { |
| modules.insert(SafetyHubModule::kVersion); |
| } |
| // SafeBrowsing module |
| if (CardHasRecommendations( |
| SafetyHubSafeBrowsingResult::GetSafeBrowsingCardData( |
| profile_->GetPrefs()))) { |
| modules.insert(SafetyHubModule::kSafeBrowsing); |
| } |
| // Extensions module |
| if (GetNumberOfExtensionsThatNeedReview() > 0) { |
| modules.insert(SafetyHubModule::kExtensions); |
| } |
| // Notifications module |
| NotificationPermissionsReviewService* npr_service = |
| NotificationPermissionsReviewServiceFactory::GetForProfile(profile_); |
| CHECK(npr_service); |
| if (!npr_service->PopulateNotificationPermissionReviewData().empty()) { |
| modules.insert(SafetyHubModule::kNotifications); |
| } |
| // Unused site permission module |
| if (!PopulateUnusedSitePermissionsData().empty()) { |
| modules.insert(SafetyHubModule::kUnusedSitePermissions); |
| } |
| |
| return modules; |
| } |
| |
| void SafetyHubHandler::HandleRecordSafetyHubVisit( |
| const base::Value::List& args) { |
| if (SafetyHubHatsService* hats_service = |
| SafetyHubHatsServiceFactory::GetForProfile(profile_)) { |
| hats_service->SafetyHubVisited(); |
| } |
| } |
| |
| void SafetyHubHandler::HandleRecordSafetyHubInteraction( |
| const base::Value::List& args) { |
| if (SafetyHubHatsService* hats_service = |
| SafetyHubHatsServiceFactory::GetForProfile(profile_)) { |
| hats_service->SafetyHubModuleInteracted(); |
| } |
| } |
| |
| void SafetyHubHandler::RegisterMessages() { |
| // Usage of base::Unretained(this) is safe, because web_ui() owns `this` and |
| // won't release ownership until destruction. |
| web_ui()->RegisterMessageCallback( |
| "getRevokedUnusedSitePermissionsList", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleGetRevokedUnusedSitePermissionsList, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "allowPermissionsAgainForUnusedSite", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleAllowPermissionsAgainForUnusedSite, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "undoAllowPermissionsAgainForUnusedSite", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleUndoAllowPermissionsAgainForUnusedSite, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "acknowledgeRevokedUnusedSitePermissionsList", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleAcknowledgeRevokedUnusedSitePermissionsList, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "undoAcknowledgeRevokedUnusedSitePermissionsList", |
| base::BindRepeating( |
| &SafetyHubHandler:: |
| HandleUndoAcknowledgeRevokedUnusedSitePermissionsList, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "getNotificationPermissionReview", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleGetNotificationPermissionReviewList, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "ignoreNotificationPermissionReviewForOrigins", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleIgnoreOriginsForNotificationPermissionReview, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "resetNotificationPermissionForOrigins", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleResetNotificationPermissionForOrigins, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "dismissActiveMenuNotification", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleDismissActiveMenuNotification, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "dismissSafetyHubPasswordMenuNotification", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleDismissPasswordMenuNotification, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "dismissSafetyHubExtensionsMenuNotification", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleDismissExtensionsMenuNotification, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "blockNotificationPermissionForOrigins", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleBlockNotificationPermissionForOrigins, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "allowNotificationPermissionForOrigins", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleAllowNotificationPermissionForOrigins, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "undoIgnoreNotificationPermissionReviewForOrigins", |
| base::BindRepeating( |
| &SafetyHubHandler:: |
| HandleUndoIgnoreOriginsForNotificationPermissionReview, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "getSafeBrowsingCardData", |
| base::BindRepeating(&SafetyHubHandler::HandleGetSafeBrowsingCardData, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "getPasswordCardData", |
| base::BindRepeating(&SafetyHubHandler::HandleGetPasswordCardData, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "getVersionCardData", |
| base::BindRepeating(&SafetyHubHandler::HandleGetVersionCardData, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "getSafetyHubEntryPointData", |
| base::BindRepeating(&SafetyHubHandler::HandleGetSafetyHubEntryPointData, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "getNumberOfExtensionsThatNeedReview", |
| base::BindRepeating( |
| &SafetyHubHandler::HandleGetNumberOfExtensionsThatNeedReview, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "recordSafetyHubPageVisit", |
| base::BindRepeating(&SafetyHubHandler::HandleRecordSafetyHubVisit, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback( |
| "recordSafetyHubInteraction", |
| base::BindRepeating(&SafetyHubHandler::HandleRecordSafetyHubInteraction, |
| base::Unretained(this))); |
| } |
| |
| void SafetyHubHandler::SendUnusedSitePermissionsReviewList() { |
| // Notify observers that the unused site permission review list could have |
| // changed. Note that the list is not guaranteed to have changed. In places |
| // where determining whether the list has changed is cause for performance |
| // concerns, an unchanged list may be sent. |
| FireWebUIListener("unused-permission-review-list-maybe-changed", |
| PopulateUnusedSitePermissionsData()); |
| } |
| |
| void SafetyHubHandler::SendNotificationPermissionReviewList() { |
| NotificationPermissionsReviewService* service = |
| NotificationPermissionsReviewServiceFactory::GetForProfile(profile_); |
| if (!service) { |
| return; |
| } |
| |
| // Notify observers that the permission review list could have changed. Note |
| // that the list is not guaranteed to have changed. |
| FireWebUIListener( |
| site_settings::kNotificationPermissionsReviewListMaybeChangedEvent, |
| service->PopulateNotificationPermissionReviewData()); |
| } |
| |
| void SafetyHubHandler::InitSafetyHubExtensionResults() { |
| std::optional<std::unique_ptr<SafetyHubResult>> sh_result = |
| SafetyHubExtensionsResult::GetResult(profile_, false); |
| if (sh_result.has_value()) { |
| extension_sh_result_ = std::make_unique<SafetyHubExtensionsResult>( |
| *static_cast<SafetyHubExtensionsResult*>(sh_result->get())); |
| } |
| } |
| |
| int SafetyHubHandler::GetNumberOfExtensionsThatNeedReview() { |
| if (!extension_sh_result_) { |
| InitSafetyHubExtensionResults(); |
| } |
| if (extension_sh_result_) { |
| return extension_sh_result_->GetNumTriggeringExtensions(); |
| } else { |
| return 0; |
| } |
| } |
| |
| void SafetyHubHandler::UpdateNumberOfExtensionsThatNeedReview( |
| int num_extension_need_review_before, |
| int num_extension_need_review_after) { |
| if (num_extension_need_review_before != num_extension_need_review_after) { |
| AllowJavascript(); |
| FireWebUIListener("extensions-review-list-maybe-changed", |
| num_extension_need_review_after); |
| } |
| } |
| |
| void SafetyHubHandler::OnExtensionPrefsUpdated( |
| const std::string& extension_id) { |
| if (!extension_sh_result_) { |
| return; |
| } |
| int num_extension_need_review_before = GetNumberOfExtensionsThatNeedReview(); |
| extension_sh_result_->OnExtensionPrefsUpdated(extension_id, profile_); |
| int num_extension_need_review_after = GetNumberOfExtensionsThatNeedReview(); |
| UpdateNumberOfExtensionsThatNeedReview(num_extension_need_review_before, |
| num_extension_need_review_after); |
| } |
| |
| void SafetyHubHandler::OnExtensionUninstalled( |
| content::BrowserContext* browser_context, |
| const extensions::Extension* extension, |
| extensions::UninstallReason reason) { |
| if (!extension_sh_result_) { |
| return; |
| } |
| int num_extension_need_review_before = GetNumberOfExtensionsThatNeedReview(); |
| extension_sh_result_->OnExtensionUninstalled(browser_context, extension, |
| reason); |
| int num_extension_need_review_after = GetNumberOfExtensionsThatNeedReview(); |
| UpdateNumberOfExtensionsThatNeedReview(num_extension_need_review_before, |
| num_extension_need_review_after); |
| } |
| |
| void SafetyHubHandler::OnExtensionPrefsWillBeDestroyed(ExtensionPrefs* prefs) { |
| DCHECK(prefs_observation_.IsObservingSource(prefs)); |
| prefs_observation_.Reset(); |
| } |
| |
| void SafetyHubHandler::OnShutdown(extensions::ExtensionRegistry* registry) { |
| extension_registry_observation_.Reset(); |
| } |
| |
| void SafetyHubHandler::SetClockForTesting(base::Clock* clock) { |
| clock_ = clock; |
| } |
| |
| void SafetyHubHandler::ClearExtensionResultsForTesting() { |
| GetNumberOfExtensionsThatNeedReview(); |
| if (extension_sh_result_) { |
| extension_sh_result_->ClearTriggeringExtensionsForTesting(); // IN-TEST |
| } |
| } |
| |
| void SafetyHubHandler::SetTriggeringExtensionForTesting( |
| std::string extension_id) { |
| GetNumberOfExtensionsThatNeedReview(); |
| if (extension_sh_result_) { |
| extension_sh_result_->SetTriggeringExtensionForTesting( // IN-TEST |
| extension_id); // IN-TEST |
| } |
| } |
| |
| void SafetyHubHandler::OnJavascriptAllowed() {} |
| |
| void SafetyHubHandler::OnJavascriptDisallowed() {} |
| |
| void SafetyHubHandler::OnSafeBrowsingEnhancedChanged() { |
| if (profile_->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnhanced) && |
| MobilePromoOnDesktopTypeEnabled() == |
| MobilePromoOnDesktopPromoType::kESBPromo) { |
| IOSPromoTriggerService* service = |
| IOSPromoTriggerServiceFactory::GetForProfile(profile_); |
| if (service) { |
| service->NotifyPromoShouldBeShown(IOSPromoType::kEnhancedBrowsing); |
| } |
| } |
| } |