910e62b5创建于 1月15日历史提交
// 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