blob: edbb82b3ffba38ce766fa996483888a7e5a19757 [file] [log] [blame]
// Copyright 2013 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/reset_settings_handler.h"
#include <string>
#include <utility>
#include "base/containers/flat_set.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/google/google_brand.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/prefs/chrome_pref_service_factory.h"
#include "chrome/browser/profile_resetter/profile_resetter.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/prefs/pref_service.h"
#include "components/search_engines/default_search_manager.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "extensions/browser/pref_names.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "ash/constants/ash_features.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
#include "chrome/browser/ui/webui/ash/settings/pref_names.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
#endif // BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_WIN)
#include "chrome/browser/profile_resetter/triggered_profile_resetter.h"
#include "chrome/browser/profile_resetter/triggered_profile_resetter_factory.h"
#endif // BUILDFLAG(IS_WIN)
namespace settings {
namespace {
reset_report::ChromeResetReport::ResetRequestOrigin
ResetRequestOriginFromString(const std::string& request_origin) {
static const char kOriginUserClick[] = "userclick";
static const char kOriginTriggeredReset[] = "triggeredreset";
if (request_origin == kOriginUserClick) {
return reset_report::ChromeResetReport::RESET_REQUEST_ORIGIN_USER_CLICK;
}
if (request_origin == kOriginTriggeredReset) {
return reset_report::ChromeResetReport::
RESET_REQUEST_ORIGIN_TRIGGERED_RESET;
}
if (!request_origin.empty()) {
NOTREACHED();
}
return reset_report::ChromeResetReport::RESET_REQUEST_ORIGIN_UNKNOWN;
}
} // namespace
#if BUILDFLAG(IS_CHROMEOS)
// static
const char ResetSettingsHandler::kCctResetSettingsHash[] = "cct";
// static
void ResetSettingsHandler::RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(ash::settings::prefs::kSanitizeCompleted,
false);
}
#endif // BUILDFLAG(IS_CHROMEOS)
// static
bool ResetSettingsHandler::ShouldShowResetProfileBanner(Profile* profile) {
const base::Time reset_time = chrome_prefs::GetResetTime(profile);
// If there is no reset time, do not show the banner.
if (reset_time.is_null()) {
return false;
}
// Otherwise, only show the banner if it has been less than |kBannerShowTime|
// since reset.
static constexpr base::TimeDelta kBannerShowTime = base::Days(5);
const base::TimeDelta since_reset = base::Time::Now() - reset_time;
return since_reset < kBannerShowTime;
}
ResetSettingsHandler::ResetSettingsHandler(Profile* profile)
: profile_(profile),
resetter_(std::make_unique<ProfileResetter>(profile_)) {}
ResetSettingsHandler::~ResetSettingsHandler() = default;
void ResetSettingsHandler::OnJavascriptDisallowed() {
callback_weak_ptr_factory_.InvalidateWeakPtrs();
}
void ResetSettingsHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
"performResetProfileSettings",
base::BindRepeating(&ResetSettingsHandler::HandleResetProfileSettings,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"onShowResetProfileDialog",
base::BindRepeating(&ResetSettingsHandler::OnShowResetProfileDialog,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getReportedSettings",
base::BindRepeating(&ResetSettingsHandler::HandleGetReportedSettings,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getTamperedPreferencePaths",
base::BindRepeating(
&ResetSettingsHandler::HandleGetTamperedPreferencePaths,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"onHideResetProfileDialog",
base::BindRepeating(&ResetSettingsHandler::OnHideResetProfileDialog,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"onHideResetProfileBanner",
base::BindRepeating(&ResetSettingsHandler::OnHideResetProfileBanner,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"getTriggeredResetToolName",
base::BindRepeating(
&ResetSettingsHandler::HandleGetTriggeredResetToolName,
base::Unretained(this)));
#if BUILDFLAG(IS_CHROMEOS)
web_ui()->RegisterMessageCallback(
"onShowSanitizeDialog",
base::BindRepeating(&ResetSettingsHandler::OnShowSanitizeDialog,
base::Unretained(this)));
#endif // BUILDFLAG(IS_CHROMEOS)
}
void ResetSettingsHandler::HandleResetProfileSettings(
const base::Value::List& args) {
AllowJavascript();
CHECK_EQ(3U, args.size());
const std::string& callback_id = args[0].GetString();
const bool& send_settings = args[1].GetBool();
const std::string& request_origin_string = args[2].GetString();
reset_report::ChromeResetReport::ResetRequestOrigin request_origin =
ResetRequestOriginFromString(request_origin_string);
ResetProfile(callback_id, send_settings, request_origin);
}
void ResetSettingsHandler::OnResetProfileSettingsDone(
std::string callback_id,
bool send_feedback,
reset_report::ChromeResetReport::ResetRequestOrigin request_origin) {
ResolveJavascriptCallback(base::Value(callback_id), base::Value());
if (send_feedback && setting_snapshot_) {
ResettableSettingsSnapshot current_snapshot(profile_);
int difference = setting_snapshot_->FindDifferentFields(current_snapshot);
if (difference) {
setting_snapshot_->Subtract(current_snapshot);
std::unique_ptr<reset_report::ChromeResetReport> report_proto =
SerializeSettingsReportToProto(*setting_snapshot_, difference);
if (report_proto) {
report_proto->set_reset_request_origin(request_origin);
SendSettingsFeedbackProto(*report_proto, profile_);
}
}
}
setting_snapshot_.reset();
}
void ResetSettingsHandler::HandleGetReportedSettings(
const base::Value::List& args) {
AllowJavascript();
CHECK_EQ(1U, args.size());
const std::string& callback_id = args[0].GetString();
setting_snapshot_->RequestShortcuts(
base::BindOnce(&ResetSettingsHandler::OnGetReportedSettingsDone,
callback_weak_ptr_factory_.GetWeakPtr(), callback_id));
}
void ResetSettingsHandler::HandleGetTamperedPreferencePaths(
const base::Value::List& args) {
AllowJavascript();
// We check for expiration before sending the pref list to the UI.
const base::Time reset_time = chrome_prefs::GetResetTime(profile_);
if (!reset_time.is_null()) {
static constexpr base::TimeDelta kBannerShowTime = base::Days(5);
const base::TimeDelta since_reset = base::Time::Now() - reset_time;
if (since_reset >= kBannerShowTime) {
// The banner has expired. Clear both prefs.
chrome_prefs::ClearResetTime(profile_);
chrome_prefs::ClearTamperedPrefList(profile_);
}
}
CHECK_EQ(1U, args.size());
const base::Value& callback_id = args[0];
if (!base::FeatureList::IsEnabled(features::kShowResetProfileBannerV2)) {
ResolveJavascriptCallback(callback_id, base::Value(base::Value::List()));
return;
}
base::Value::List tampered_paths;
const base::Value::List& tampered_prefs =
chrome_prefs::GetTamperedPrefList(profile_);
// Using a flat_set to avoid duplicates.
base::flat_set<std::u16string> changed_settings;
for (const auto& pref_value : tampered_prefs) {
const std::string* pref_path = pref_value.GetIfString();
if (*pref_path ==
DefaultSearchManager::kDefaultSearchProviderDataPrefName) {
changed_settings.insert(
l10n_util::GetStringUTF16(IDS_SETTINGS_RESET_DSE));
} else if (*pref_path == prefs::kShowHomeButton) {
changed_settings.insert(
l10n_util::GetStringUTF16(IDS_SETTINGS_SHOW_HOME_BUTTON));
} else if (*pref_path == prefs::kHomePage) {
changed_settings.insert(
l10n_util::GetStringUTF16(IDS_SETTINGS_RESET_HOMEPAGE));
} else if (*pref_path == prefs::kPinnedTabs) {
changed_settings.insert(
l10n_util::GetStringUTF16(IDS_SETTINGS_RESET_PINNED_TABS));
} else if (base::StartsWith(*pref_path,
extensions::pref_names::kExtensions)) {
changed_settings.insert(
l10n_util::GetStringUTF16(IDS_SETTINGS_RESET_EXTENSIONS));
}
}
base::Value::List result;
for (const auto& setting : changed_settings) {
result.Append(setting);
}
ResolveJavascriptCallback(callback_id, result);
}
void ResetSettingsHandler::OnGetReportedSettingsDone(std::string callback_id) {
base::Value::List list =
GetReadableFeedbackForSnapshot(profile_, *setting_snapshot_);
ResolveJavascriptCallback(base::Value(callback_id), list);
}
void ResetSettingsHandler::OnShowResetProfileDialog(
const base::Value::List& args) {
if (!GetResetter()->IsActive()) {
setting_snapshot_ = std::make_unique<ResettableSettingsSnapshot>(profile_);
}
}
void ResetSettingsHandler::OnHideResetProfileDialog(
const base::Value::List& args) {
if (!GetResetter()->IsActive()) {
setting_snapshot_.reset();
}
}
void ResetSettingsHandler::OnHideResetProfileBanner(
const base::Value::List& args) {
chrome_prefs::ClearResetTime(profile_);
chrome_prefs::ClearTamperedPrefList(profile_);
}
void ResetSettingsHandler::ResetProfile(
const std::string& callback_id,
bool send_settings,
reset_report::ChromeResetReport::ResetRequestOrigin request_origin) {
GetResetter()->ResetSettings(
ProfileResetter::PROFILE_RESETS, nullptr,
base::BindOnce(&ResetSettingsHandler::OnResetProfileSettingsDone,
callback_weak_ptr_factory_.GetWeakPtr(), callback_id,
send_settings, request_origin));
base::RecordAction(base::UserMetricsAction("ResetProfile"));
}
ProfileResetter* ResetSettingsHandler::GetResetter() {
if (!resetter_) {
resetter_ = std::make_unique<ProfileResetter>(profile_);
}
return resetter_.get();
}
void ResetSettingsHandler::HandleGetTriggeredResetToolName(
const base::Value::List& args) {
AllowJavascript();
CHECK_EQ(1U, args.size());
const base::Value& callback_id = args[0];
// Set up the localized strings for the triggered profile reset dialog.
// Custom reset tool names are supported on Windows only.
std::u16string reset_tool_name;
#if BUILDFLAG(IS_WIN)
Profile* profile = Profile::FromWebUI(web_ui());
TriggeredProfileResetter* triggered_profile_resetter =
TriggeredProfileResetterFactory::GetForBrowserContext(profile);
// TriggeredProfileResetter instance will be nullptr for incognito profiles.
if (triggered_profile_resetter) {
reset_tool_name = triggered_profile_resetter->GetResetToolName();
// Now that a reset UI has been shown, don't trigger again for this profile.
triggered_profile_resetter->ClearResetTrigger();
}
#endif // BUILDFLAG(IS_WIN)
if (reset_tool_name.empty()) {
reset_tool_name = l10n_util::GetStringUTF16(
IDS_TRIGGERED_RESET_PROFILE_SETTINGS_DEFAULT_TOOL_NAME);
}
base::Value string_value(reset_tool_name);
ResolveJavascriptCallback(callback_id, string_value);
}
#if BUILDFLAG(IS_CHROMEOS)
void ResetSettingsHandler::OnShowSanitizeDialog(const base::Value::List& args) {
// TODO(b/357057195) move sanitize functionality functions out of
// ResetSettingsHandler and only leave the UI parts for ResetSettingsHandler.
if (base::FeatureList::IsEnabled(ash::features::kSanitize)) {
ash::SystemAppLaunchParams params;
params.launch_source = apps::LaunchSource::kUnknown;
ash::LaunchSystemWebAppAsync(ProfileManager::GetPrimaryUserProfile(),
ash::SystemWebAppType::OS_SANITIZE, params);
}
}
#endif // BUILDFLAG(IS_CHROMEOS)
} // namespace settings