blob: 390c49b4606d87acb819c1ca4ef7ae7e3929efad [file] [log] [blame]
// Copyright 2017 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/safe_browsing/chrome_cleaner/settings_resetter_win.h"
#include <algorithm>
#include <iterator>
#include <memory>
#include <utility>
#include <vector>
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback_helpers.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_macros.h"
#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
#include "base/win/registry.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profile_resetter/profile_resetter.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/common/pref_names.h"
#include "components/chrome_cleaner/public/constants/constants.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
namespace safe_browsing {
namespace {
// Returns the post-cleanup reset pending prefs for |profile|.
bool ResetPending(Profile* profile) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(PostCleanupSettingsResetter::IsEnabled());
DCHECK(profile);
PrefService* prefs = profile->GetPrefs();
return prefs->GetBoolean(prefs::kChromeCleanerResetPending);
}
// Updates the post-cleanup reset pending prefs for |profile|.
void RecordResetPending(bool value, Profile* profile) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(PostCleanupSettingsResetter::IsEnabled());
DCHECK(profile);
PrefService* prefs = profile->GetPrefs();
prefs->SetBoolean(prefs::kChromeCleanerResetPending, value);
}
bool CopyProfilesToReset(const std::vector<Profile*>& profiles,
std::vector<Profile*>* profiles_to_reset) {
std::copy_if(profiles.begin(), profiles.end(),
std::back_inserter(*profiles_to_reset),
[](Profile* profile) -> bool { return ResetPending(profile); });
return !profiles_to_reset->empty();
}
// Manages post-cleanup settings reset for a list of profiles.
// An instance of this class is created by ResetTaggedProfiles() and will
// self-delete once all profiles in the list have been reset.
class SettingsResetter : public base::RefCounted<SettingsResetter> {
public:
SettingsResetter(
std::vector<Profile*> profiles_to_reset,
std::unique_ptr<PostCleanupSettingsResetter::Delegate> delegate,
base::OnceClosure done_callback);
// Resets settings for all profiles in |profiles_to_reset_| and invokes
// |done_callback_| when done.
void Run();
protected:
virtual ~SettingsResetter();
private:
friend class base::RefCounted<SettingsResetter>;
// Resets settings for |profile| according to default values given by
// |master_settings|. Used as a callback for
// DefaultSettingsFetcher::FetchDefaultSettings().
void OnFetchCompleted(
Profile* profile,
std::unique_ptr<BrandcodedDefaultSettings> master_settings);
// Removes the settings reset tag for |profile|. If there are no more
// profiles to reset, invokes |done_callback_| and deletes this object.
void OnResetCompleted(Profile* profile);
// The profiles to be reset.
std::vector<Profile*> profiles_to_reset_;
// The ProfileResetter objects that are used to reset each profile. We need to
// hold on to these until each reset operation has been completed.
std::vector<std::unique_ptr<ProfileResetter>> profile_resetters_;
// Used to check that modifications to |profile_resetters_| are sequenced
// correctly.
SEQUENCE_CHECKER(sequence_checker_);
// The number of profiles that need to be reset.
int num_pending_resets_;
// The callback to be invoked once settings reset completes.
base::OnceClosure done_callback_;
std::unique_ptr<PostCleanupSettingsResetter::Delegate> delegate_;
DISALLOW_COPY_AND_ASSIGN(SettingsResetter);
};
SettingsResetter::SettingsResetter(
std::vector<Profile*> profiles_to_reset,
std::unique_ptr<PostCleanupSettingsResetter::Delegate> delegate,
base::OnceClosure done_callback)
: profiles_to_reset_(std::move(profiles_to_reset)),
num_pending_resets_(profiles_to_reset_.size()),
done_callback_(std::move(done_callback)),
delegate_(std::move(delegate)) {
DCHECK_LT(0, num_pending_resets_);
DCHECK(done_callback_);
DCHECK(delegate_);
DETACH_FROM_SEQUENCE(sequence_checker_);
}
SettingsResetter::~SettingsResetter() {
DCHECK(!done_callback_);
DCHECK(!num_pending_resets_);
}
void SettingsResetter::Run() {
for (Profile* profile : profiles_to_reset_) {
delegate_->FetchDefaultSettings(
base::Bind(&SettingsResetter::OnFetchCompleted, this, profile));
}
}
void SettingsResetter::OnFetchCompleted(
Profile* profile,
std::unique_ptr<BrandcodedDefaultSettings> master_settings) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
static const ProfileResetter::ResettableFlags kSettingsToReset =
ProfileResetter::DEFAULT_SEARCH_ENGINE | ProfileResetter::HOMEPAGE |
ProfileResetter::EXTENSIONS | ProfileResetter::STARTUP_PAGES |
ProfileResetter::SHORTCUTS;
profile_resetters_.push_back(delegate_->GetProfileResetter(profile));
profile_resetters_.back()->Reset(
kSettingsToReset, std::move(master_settings),
base::Bind(&SettingsResetter::OnResetCompleted, this, profile));
}
void SettingsResetter::OnResetCompleted(Profile* profile) {
DCHECK_LT(0, num_pending_resets_);
RecordResetPending(false, profile);
--num_pending_resets_;
if (!num_pending_resets_)
std::move(done_callback_).Run();
}
// Returns true if there is information of a completed cleanup in the registry.
bool CleanupCompletedFromRegistry() {
base::string16 cleaner_key_path(
chrome_cleaner::kSoftwareRemovalToolRegistryKey);
cleaner_key_path.append(L"\\").append(chrome_cleaner::kCleanerSubKey);
base::win::RegKey srt_cleaner_key(HKEY_CURRENT_USER, cleaner_key_path.c_str(),
KEY_QUERY_VALUE);
DWORD cleanup_completed = 0;
return srt_cleaner_key.Valid() &&
srt_cleaner_key.ReadValueDW(chrome_cleaner::kCleanupCompletedValueName,
&cleanup_completed) == ERROR_SUCCESS &&
cleanup_completed == 1;
}
} // namespace
PostCleanupSettingsResetter::Delegate::Delegate() {}
PostCleanupSettingsResetter::Delegate::~Delegate() {}
void PostCleanupSettingsResetter::Delegate::FetchDefaultSettings(
DefaultSettingsFetcher::SettingsCallback callback) {
DefaultSettingsFetcher::FetchDefaultSettings(std::move(callback));
}
PostCleanupSettingsResetter::PostCleanupSettingsResetter() = default;
PostCleanupSettingsResetter::~PostCleanupSettingsResetter() = default;
// static
bool PostCleanupSettingsResetter::IsEnabled() {
#if defined(OS_WIN)
return true;
#else
return false;
#endif
}
std::unique_ptr<ProfileResetter>
PostCleanupSettingsResetter::Delegate::GetProfileResetter(Profile* profile) {
return std::make_unique<ProfileResetter>(profile);
}
void PostCleanupSettingsResetter::TagForResetting(Profile* profile) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(IsEnabled());
DCHECK(profile);
RecordResetPending(true, profile);
UMA_HISTOGRAM_BOOLEAN("SoftwareReporter.TaggedProfileForResetting", true);
}
void PostCleanupSettingsResetter::ResetTaggedProfiles(
std::vector<Profile*> profiles,
base::OnceClosure done_callback,
std::unique_ptr<PostCleanupSettingsResetter::Delegate> delegate) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(IsEnabled());
DCHECK(delegate);
std::vector<Profile*> profiles_to_reset;
if (!CopyProfilesToReset(profiles, &profiles_to_reset) ||
!CleanupCompletedFromRegistry()) {
std::move(done_callback).Run();
return;
}
UMA_HISTOGRAM_EXACT_LINEAR("SoftwareReporter.PostCleanupSettingsReset",
profiles_to_reset.size(), 10);
// The SettingsResetter object will self-delete once |done_callback| is
// invoked.
base::WrapRefCounted(new SettingsResetter(std::move(profiles_to_reset),
std::move(delegate),
std::move(done_callback)))
->Run();
}
// static
void PostCleanupSettingsResetter::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
DCHECK(registry);
registry->RegisterBooleanPref(prefs::kChromeCleanerResetPending, false);
}
} // namespace safe_browsing