Commit 391e9004 authored by Patrick Monette's avatar Patrick Monette Committed by Commit Bot

[3p-Conflicts] Make ModuleListFilter initialize on a background sequence

This is to avoid doing IO access on the UI thread.

Bug: 819791
Change-Id: I0f92f316ebc29d0c4270af46b3a6ccc854adfded
Reviewed-on: https://chromium-review.googlesource.com/955670
Commit-Queue: Patrick Monette <pmonette@chromium.org>
Reviewed-by: default avatarGreg Thompson <grt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#543085}
parent a42f0327
...@@ -9,12 +9,10 @@ ...@@ -9,12 +9,10 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "base/callback_forward.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/win/windows_types.h"
class MsiUtil; class MsiUtil;
...@@ -49,8 +47,6 @@ class MsiUtil; ...@@ -49,8 +47,6 @@ class MsiUtil;
// - The uninstall entry has no display name. // - The uninstall entry has no display name.
// - The uninstall entry has no UninstallString. // - The uninstall entry has no UninstallString.
// //
// This class is not thread safe and should be only accessed on the same
// sequence.
class InstalledPrograms { class InstalledPrograms {
public: public:
struct ProgramInfo { struct ProgramInfo {
...@@ -62,73 +58,53 @@ class InstalledPrograms { ...@@ -62,73 +58,53 @@ class InstalledPrograms {
REGSAM registry_wow64_access; REGSAM registry_wow64_access;
}; };
// The guts of the class, this structure is populated on a background sequence // Initializes this instance with the list of installed programs. While the
// and moved back to this instance after initialization. // constructor must be called in a sequence that allows blocking, its public
struct ProgramsData { // method can be used without such restrictions.
ProgramsData();
~ProgramsData();
// Programs are stored in this vector because multiple entries in
// |installed_files| could point to the same one. This is to avoid
// duplicating them.
std::vector<ProgramInfo> programs;
// Contains all the files from programs installed via Microsoft Installer.
// The second part of the pair is the index into |programs|.
std::vector<std::pair<base::FilePath, size_t>> installed_files;
// For some programs, the best information available is the directory of the
// installation. The compare functor treats file paths where one is the
// parent of the other as equal.
// The second part of the pair is the index into |programs|.
std::vector<std::pair<base::FilePath, size_t>> install_directories;
};
InstalledPrograms(); InstalledPrograms();
virtual ~InstalledPrograms();
// Initializes this class on a background sequence. virtual ~InstalledPrograms();
void Initialize(base::OnceClosure on_initialized_callback);
// Given a |file|, checks if it matches an installed program on the user's // Given a |file|, checks if it matches an installed program on the user's
// machine and appends all the matching programs to |programs|. Do not call // machine and appends all the matching programs to |programs|.
// this before initialization completes.
// Virtual to allow mocking. // Virtual to allow mocking.
virtual bool GetInstalledPrograms(const base::FilePath& file, virtual bool GetInstalledPrograms(const base::FilePath& file,
std::vector<ProgramInfo>* programs) const; std::vector<ProgramInfo>* programs) const;
// Returns true if this instance is initialized and GetInstalledPrograms() can
// be called.
bool initialized() const { return initialized_; }
protected: protected:
// Protected so that tests can subclass InstalledPrograms and access it. // Protected so that tests can subclass InstalledPrograms and access it.
void Initialize(base::OnceClosure on_initialized_callback, explicit InstalledPrograms(std::unique_ptr<MsiUtil> msi_util);
std::unique_ptr<MsiUtil> msi_util);
private: private:
// If the registry key references a valid installed program, this function
// adds an entry to |programs_| with its list of files or installation
// directory to their associated vector.
void CheckRegistryKeyForInstalledProgram(HKEY hkey,
const base::string16& key_path,
REGSAM wow64access,
const base::string16& key_name,
const MsiUtil& msi_util);
bool GetProgramsFromInstalledFiles(const base::FilePath& file, bool GetProgramsFromInstalledFiles(const base::FilePath& file,
std::vector<ProgramInfo>* programs) const; std::vector<ProgramInfo>* programs) const;
bool GetProgramsFromInstallDirectories( bool GetProgramsFromInstallDirectories(
const base::FilePath& file, const base::FilePath& file,
std::vector<ProgramInfo>* programs) const; std::vector<ProgramInfo>* programs) const;
// Takes the result from the initialization and moves it to this instance, // Programs are stored in this vector because multiple entries in
// then calls |on_initialized_callback| to indicate that the initialization // |installed_files| could point to the same one. This is to avoid
// is done. // duplicating them.
void OnInitializationDone(base::OnceClosure on_initialized_callback, std::vector<ProgramInfo> programs_;
std::unique_ptr<ProgramsData> programs_data);
// Used to assert that initialization was completed before calling
// GetInstalledPrograms().
bool initialized_;
std::unique_ptr<ProgramsData> programs_data_;
// Make sure this class isn't accessed via multiple sequences. // Contains all the files from programs installed via Microsoft Installer.
SEQUENCE_CHECKER(sequence_checker_); // The second part of the pair is the index into |programs|.
std::vector<std::pair<base::FilePath, size_t>> installed_files_;
base::WeakPtrFactory<InstalledPrograms> weak_ptr_factory_; // For some programs, the best information available is the directory of the
// installation. The compare functor treats file paths where one is the
// parent of the other as equal.
// The second part of the pair is the index into |programs|.
std::vector<std::pair<base::FilePath, size_t>> install_directories_;
DISALLOW_COPY_AND_ASSIGN(InstalledPrograms); DISALLOW_COPY_AND_ASSIGN(InstalledPrograms);
}; };
......
...@@ -7,9 +7,7 @@ ...@@ -7,9 +7,7 @@
#include <map> #include <map>
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_reg_util_win.h" #include "base/test/test_reg_util_win.h"
#include "base/win/registry.h" #include "base/win/registry.h"
#include "chrome/browser/conflicts/msi_util_win.h" #include "chrome/browser/conflicts/msi_util_win.h"
...@@ -62,7 +60,8 @@ class MockMsiUtil : public MsiUtil { ...@@ -62,7 +60,8 @@ class MockMsiUtil : public MsiUtil {
class TestInstalledPrograms : public InstalledPrograms { class TestInstalledPrograms : public InstalledPrograms {
public: public:
using InstalledPrograms::Initialize; explicit TestInstalledPrograms(std::unique_ptr<MsiUtil> msi_util)
: InstalledPrograms(std::move(msi_util)) {}
}; };
class InstalledProgramsTest : public testing::Test { class InstalledProgramsTest : public testing::Test {
...@@ -114,22 +113,18 @@ class InstalledProgramsTest : public testing::Test { ...@@ -114,22 +113,18 @@ class InstalledProgramsTest : public testing::Test {
program_info.install_location.c_str()); program_info.install_location.c_str());
} }
TestInstalledPrograms& installed_programs() { return installed_programs_; } TestInstalledPrograms& installed_programs() { return *installed_programs_; }
void InitializeInstalledProgramsSynchronously() { void InitializeInstalledPrograms() {
installed_programs_.Initialize( installed_programs_ = std::make_unique<TestInstalledPrograms>(
base::Closure(), std::make_unique<MockMsiUtil>(component_paths_map_)); std::make_unique<MockMsiUtil>(component_paths_map_));
scoped_task_environment_.RunUntilIdle();
} }
private: private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
registry_util::RegistryOverrideManager registry_override_manager_; registry_util::RegistryOverrideManager registry_override_manager_;
TestInstalledPrograms installed_programs_; std::unique_ptr<TestInstalledPrograms> installed_programs_;
// This should not get modified after InitializeInstalledPrograms() is called
// because it will end up being accessed on a different thread by MockMsiUtil.
std::map<base::string16, std::vector<base::string16>> component_paths_map_; std::map<base::string16, std::vector<base::string16>> component_paths_map_;
DISALLOW_COPY_AND_ASSIGN(InstalledProgramsTest); DISALLOW_COPY_AND_ASSIGN(InstalledProgramsTest);
...@@ -175,7 +170,7 @@ TEST_F(InstalledProgramsTest, InvalidEntries) { ...@@ -175,7 +170,7 @@ TEST_F(InstalledProgramsTest, InvalidEntries) {
for (const auto& test_case : kTestCases) for (const auto& test_case : kTestCases)
AddFakeProgram(test_case); AddFakeProgram(test_case);
InitializeInstalledProgramsSynchronously(); InitializeInstalledPrograms();
// None of the invalid entries were picked up. // None of the invalid entries were picked up.
const base::FilePath valid_child_file = const base::FilePath valid_child_file =
...@@ -201,7 +196,7 @@ TEST_F(InstalledProgramsTest, InstallLocation) { ...@@ -201,7 +196,7 @@ TEST_F(InstalledProgramsTest, InstallLocation) {
AddFakeProgram(kTestCase); AddFakeProgram(kTestCase);
InitializeInstalledProgramsSynchronously(); InitializeInstalledPrograms();
// Child file path. // Child file path.
const base::FilePath valid_child_file = const base::FilePath valid_child_file =
...@@ -242,7 +237,7 @@ TEST_F(InstalledProgramsTest, Msi) { ...@@ -242,7 +237,7 @@ TEST_F(InstalledProgramsTest, Msi) {
AddFakeProgram(kTestCase); AddFakeProgram(kTestCase);
InitializeInstalledProgramsSynchronously(); InitializeInstalledPrograms();
// Checks that all the files match the program. // Checks that all the files match the program.
for (const auto& component : kTestCase.components) { for (const auto& component : kTestCase.components) {
...@@ -293,7 +288,7 @@ TEST_F(InstalledProgramsTest, PrioritizeMsi) { ...@@ -293,7 +288,7 @@ TEST_F(InstalledProgramsTest, PrioritizeMsi) {
AddFakeProgram(kInstallLocationFakeProgram); AddFakeProgram(kInstallLocationFakeProgram);
AddFakeProgram(kMsiFakeProgram); AddFakeProgram(kMsiFakeProgram);
InitializeInstalledProgramsSynchronously(); InitializeInstalledPrograms();
std::vector<InstalledPrograms::ProgramInfo> programs; std::vector<InstalledPrograms::ProgramInfo> programs;
EXPECT_TRUE(installed_programs().GetInstalledPrograms( EXPECT_TRUE(installed_programs().GetInstalledPrograms(
...@@ -330,7 +325,7 @@ TEST_F(InstalledProgramsTest, ConflictingInstallLocations) { ...@@ -330,7 +325,7 @@ TEST_F(InstalledProgramsTest, ConflictingInstallLocations) {
AddFakeProgram(kFakeProgram1); AddFakeProgram(kFakeProgram1);
AddFakeProgram(kFakeProgram2); AddFakeProgram(kFakeProgram2);
InitializeInstalledProgramsSynchronously(); InitializeInstalledPrograms();
std::vector<InstalledPrograms::ProgramInfo> programs; std::vector<InstalledPrograms::ProgramInfo> programs;
EXPECT_FALSE(installed_programs().GetInstalledPrograms(base::FilePath(kFile), EXPECT_FALSE(installed_programs().GetInstalledPrograms(base::FilePath(kFile),
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/i18n/case_conversion.h" #include "base/i18n/case_conversion.h"
#include "base/logging.h"
#include "base/sha1.h" #include "base/sha1.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
......
...@@ -6,11 +6,9 @@ ...@@ -6,11 +6,9 @@
#define CHROME_BROWSER_CONFLICTS_MODULE_LIST_FILTER_WIN_H_ #define CHROME_BROWSER_CONFLICTS_MODULE_LIST_FILTER_WIN_H_
#include <memory> #include <memory>
#include <string>
#include "base/macros.h" #include "base/macros.h"
#include "chrome/browser/conflicts/proto/module_list.pb.h" #include "chrome/browser/conflicts/proto/module_list.pb.h"
#include "url/gurl.h"
struct ModuleInfoKey; struct ModuleInfoKey;
struct ModuleInfoData; struct ModuleInfoData;
...@@ -26,9 +24,6 @@ class ModuleListFilter { ...@@ -26,9 +24,6 @@ class ModuleListFilter {
ModuleListFilter(); ModuleListFilter();
virtual ~ModuleListFilter(); virtual ~ModuleListFilter();
// Initializes the filter with the serialized proto at |module_list_path|.
// This must be invoked before any calls to IsWhitelisted() and
// IsBlacklisted().
bool Initialize(const base::FilePath& module_list_path); bool Initialize(const base::FilePath& module_list_path);
// Returns true if the module is whitelisted. // Returns true if the module is whitelisted.
......
...@@ -139,7 +139,7 @@ constexpr wchar_t kDllPath2[] = L"c:\\some\\shellextension.dll"; ...@@ -139,7 +139,7 @@ constexpr wchar_t kDllPath2[] = L"c:\\some\\shellextension.dll";
} // namespace } // namespace
class ModuleListFilterTest : public ::testing::Test { class ModuleListFilterTest : public ::testing::Test {
public: protected:
ModuleListFilterTest() : dll1_(kDllPath1), dll2_(kDllPath2) {} ModuleListFilterTest() : dll1_(kDllPath1), dll2_(kDllPath2) {}
~ModuleListFilterTest() override = default; ~ModuleListFilterTest() override = default;
......
...@@ -7,57 +7,98 @@ ...@@ -7,57 +7,98 @@
#include <utility> #include <utility>
#include "base/bind.h" #include "base/bind.h"
#include "base/location.h"
#include "base/task_scheduler/post_task.h"
#include "chrome/browser/conflicts/installed_programs_win.h"
#include "chrome/browser/conflicts/module_database_win.h" #include "chrome/browser/conflicts/module_database_win.h"
#include "chrome/browser/conflicts/module_list_filter_win.h" #include "chrome/browser/conflicts/module_list_filter_win.h"
#include "chrome/browser/conflicts/problematic_programs_updater_win.h" #include "chrome/browser/conflicts/problematic_programs_updater_win.h"
namespace {
std::unique_ptr<ModuleListFilter> CreateModuleListFilter(
const base::FilePath& module_list_path) {
auto module_list_filter = std::make_unique<ModuleListFilter>();
if (!module_list_filter->Initialize(module_list_path))
return nullptr;
return module_list_filter;
}
} // namespace
ThirdPartyConflictsManager::ThirdPartyConflictsManager( ThirdPartyConflictsManager::ThirdPartyConflictsManager(
ModuleDatabase* module_database) ModuleDatabase* module_database)
: module_database_(module_database), module_list_received_(false) {} : module_database_(module_database),
module_list_received_(false),
on_module_database_idle_called_(false),
weak_ptr_factory_(this) {}
ThirdPartyConflictsManager::~ThirdPartyConflictsManager() = default; ThirdPartyConflictsManager::~ThirdPartyConflictsManager() = default;
void ThirdPartyConflictsManager::OnModuleDatabaseIdle() { void ThirdPartyConflictsManager::OnModuleDatabaseIdle() {
if (installed_programs_.initialized()) if (on_module_database_idle_called_)
return; return;
// ThirdPartyConflictsManager owns |installed_programs_|, so it is safe to use on_module_database_idle_called_ = true;
// base::Unretained().
installed_programs_.Initialize(base::BindOnce( base::PostTaskWithTraitsAndReplyWithResult(
&ThirdPartyConflictsManager::OnInstalledProgramsInitialized, FROM_HERE,
base::Unretained(this))); {base::MayBlock(), base::TaskPriority::BACKGROUND,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce([]() { return std::make_unique<InstalledPrograms>(); }),
base::BindOnce(&ThirdPartyConflictsManager::OnInstalledProgramsCreated,
weak_ptr_factory_.GetWeakPtr()));
} }
void ThirdPartyConflictsManager::LoadModuleList(const base::FilePath& path) { void ThirdPartyConflictsManager::LoadModuleList(const base::FilePath& path) {
// No attempt is made to dynamically reconcile a new module list version. The
// next Chrome launch will pick it up.
if (module_list_received_) if (module_list_received_)
return; return;
auto module_list_filter = std::make_unique<ModuleListFilter>(); module_list_received_ = true;
if (!module_list_filter->Initialize(path))
return; base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BACKGROUND,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&CreateModuleListFilter, path),
base::BindOnce(&ThirdPartyConflictsManager::OnModuleListFilterCreated,
weak_ptr_factory_.GetWeakPtr()));
}
void ThirdPartyConflictsManager::OnModuleListFilterCreated(
std::unique_ptr<ModuleListFilter> module_list_filter) {
module_list_filter_ = std::move(module_list_filter); module_list_filter_ = std::move(module_list_filter);
// Mark the module list as received here so that if Initialize() fails, // A valid |module_list_filter_| is critical to the blocking of third-party
// another attempt will be made with a newer version. // modules. By returning early here, the |problematic_programs_updater_|
module_list_received_ = true; // instance never gets created, thus disabling the identification of
// incompatible applications.
if (!module_list_filter_) {
// Mark the module list as not received so that a new one may trigger the
// creation of a valid filter.
module_list_received_ = false;
return;
}
if (installed_programs_.initialized()) if (installed_programs_)
InitializeProblematicProgramsUpdater(); InitializeProblematicProgramsUpdater();
} }
void ThirdPartyConflictsManager::OnInstalledProgramsInitialized() { void ThirdPartyConflictsManager::OnInstalledProgramsCreated(
if (module_list_received_) std::unique_ptr<InstalledPrograms> installed_programs) {
installed_programs_ = std::move(installed_programs);
if (module_list_filter_)
InitializeProblematicProgramsUpdater(); InitializeProblematicProgramsUpdater();
} }
void ThirdPartyConflictsManager::InitializeProblematicProgramsUpdater() { void ThirdPartyConflictsManager::InitializeProblematicProgramsUpdater() {
DCHECK(module_list_received_); DCHECK(module_list_filter_);
DCHECK(installed_programs_.initialized()); DCHECK(installed_programs_);
problematic_programs_updater_ = ProblematicProgramsUpdater::MaybeCreate( problematic_programs_updater_ = ProblematicProgramsUpdater::MaybeCreate(
*module_list_filter_, installed_programs_); *module_list_filter_, *installed_programs_);
if (problematic_programs_updater_) if (problematic_programs_updater_)
module_database_->AddObserver(problematic_programs_updater_.get()); module_database_->AddObserver(problematic_programs_updater_.get());
} }
...@@ -8,11 +8,12 @@ ...@@ -8,11 +8,12 @@
#include <memory> #include <memory>
#include "base/macros.h" #include "base/macros.h"
#include "chrome/browser/conflicts/installed_programs_win.h" #include "base/memory/weak_ptr.h"
#include "chrome/browser/conflicts/module_database_observer_win.h" #include "chrome/browser/conflicts/module_database_observer_win.h"
class ModuleDatabase; class ModuleDatabase;
class ModuleListFilter; class ModuleListFilter;
class InstalledPrograms;
class ProblematicProgramsUpdater; class ProblematicProgramsUpdater;
namespace base { namespace base {
...@@ -33,8 +34,12 @@ class ThirdPartyConflictsManager : public ModuleDatabaseObserver { ...@@ -33,8 +34,12 @@ class ThirdPartyConflictsManager : public ModuleDatabaseObserver {
void LoadModuleList(const base::FilePath& path); void LoadModuleList(const base::FilePath& path);
private: private:
// Called when |module_list_filter_| finishes its initialization.
void OnModuleListFilterCreated(std::unique_ptr<ModuleListFilter>);
// Called when |installed_programs_| finishes its initialization. // Called when |installed_programs_| finishes its initialization.
void OnInstalledProgramsInitialized(); void OnInstalledProgramsCreated(
std::unique_ptr<InstalledPrograms> installed_programs);
// Initializes |problematic_programs_updater_| when both the ModuleListFilter // Initializes |problematic_programs_updater_| when both the ModuleListFilter
// and the InstalledPrograms are available. // and the InstalledPrograms are available.
...@@ -42,18 +47,26 @@ class ThirdPartyConflictsManager : public ModuleDatabaseObserver { ...@@ -42,18 +47,26 @@ class ThirdPartyConflictsManager : public ModuleDatabaseObserver {
ModuleDatabase* module_database_; ModuleDatabase* module_database_;
// Indicates if the initial Module List has been received. // Indicates if the initial Module List has been received. Used to prevent the
// creation of multiple ModuleListFilter instances.
bool module_list_received_; bool module_list_received_;
// Indicates if the OnModuleDatabaseIdle() function has been called once
// already. Used to prevent the creation of multiple InstalledPrograms
// instances.
bool on_module_database_idle_called_;
// Filters third-party modules against a whitelist and a blacklist. // Filters third-party modules against a whitelist and a blacklist.
std::unique_ptr<ModuleListFilter> module_list_filter_; std::unique_ptr<ModuleListFilter> module_list_filter_;
// Retrieves the list of installed programs. // Retrieves the list of installed programs.
InstalledPrograms installed_programs_; std::unique_ptr<InstalledPrograms> installed_programs_;
// Maintains the cache of problematic programs. // Maintains the cache of problematic programs.
std::unique_ptr<ProblematicProgramsUpdater> problematic_programs_updater_; std::unique_ptr<ProblematicProgramsUpdater> problematic_programs_updater_;
base::WeakPtrFactory<ThirdPartyConflictsManager> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ThirdPartyConflictsManager); DISALLOW_COPY_AND_ASSIGN(ThirdPartyConflictsManager);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment