Reland "Wire-up the ThirdPartyBlocking group policy"

This is a reland of a54726ff975e09efe95d241091a32954bdd58468

Original change's description:
> Wire-up the ThirdPartyBlocking group policy
>
> To make this happen, the ProblematicProgramsUpdater class needed a
> refactoring. This is because we do not support dynamic refresh of the
> group policy, so its value needed to be cached somehow (as it can still
> change during runtime).
>
> Now, the presence of the ThirdPartyConflictsManager indicates if the
> feature is enabled or not.
>
> Bug: 822399
> Change-Id: Ia9b0b5e52df2c777b343ccc3e524ee17a1ab0e57
> Reviewed-on: https://chromium-review.googlesource.com/964785
> Commit-Queue: Patrick Monette <[email protected]>
> Reviewed-by: Jochen Eisinger <[email protected]>
> Reviewed-by: Greg Thompson <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#544439}

TBR: [email protected], [email protected]
Bug: 822399
Change-Id: Ic9236dc69b3c89d882894c24bd7d172f110dc27a
Reviewed-on: https://chromium-review.googlesource.com/971921
Reviewed-by: Patrick Monette <[email protected]>
Commit-Queue: Patrick Monette <[email protected]>
Cr-Commit-Position: refs/heads/master@{#544579}
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 571970b..4aa708e 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -927,11 +927,6 @@
   registry->RegisterBooleanPref(prefs::kSitePerProcess, false);
   registry->RegisterBooleanPref(prefs::kWebDriverOverridesIncompatiblePolicies,
                                 false);
-#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
-  // TODO(chrisha): Move this to chrome/browser/conflicts as we build the
-  // logic that responds to this pref.
-  registry->RegisterBooleanPref(prefs::kThirdPartyBlockingEnabled, true);
-#endif
 }
 
 // static
diff --git a/chrome/browser/component_updater/third_party_module_list_component_installer_win.cc b/chrome/browser/component_updater/third_party_module_list_component_installer_win.cc
index 0741121..bd1dc33d 100644
--- a/chrome/browser/component_updater/third_party_module_list_component_installer_win.cc
+++ b/chrome/browser/component_updater/third_party_module_list_component_installer_win.cc
@@ -19,6 +19,7 @@
 #include "base/values.h"
 #include "base/version.h"
 #include "chrome/browser/conflicts/module_database_win.h"
+#include "chrome/browser/conflicts/third_party_conflicts_manager_win.h"
 #include "components/component_updater/component_updater_paths.h"
 #include "content/public/browser/browser_thread.h"
 
diff --git a/chrome/browser/conflicts/module_database_win.cc b/chrome/browser/conflicts/module_database_win.cc
index 6ad367cb..c66a219 100644
--- a/chrome/browser/conflicts/module_database_win.cc
+++ b/chrome/browser/conflicts/module_database_win.cc
@@ -13,6 +13,16 @@
 #include "base/win/windows_version.h"
 #include "chrome/browser/conflicts/module_database_observer_win.h"
 
+#if defined(GOOGLE_CHROME_BUILD)
+#include "base/feature_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/conflicts/third_party_conflicts_manager_win.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#endif
+
 namespace {
 
 // Document the assumptions made on the ProcessType enum in order to convert
@@ -49,11 +59,7 @@
   AddObserver(&third_party_metrics_);
 
 #if defined(GOOGLE_CHROME_BUILD)
-  if (base::win::GetVersion() >= base::win::VERSION_WIN10) {
-    third_party_conflicts_manager_ =
-        std::make_unique<ThirdPartyConflictsManager>(this);
-    AddObserver(third_party_conflicts_manager_.get());
-  }
+  MaybeInitializeThirdPartyConflictsManager();
 #endif
 }
 
@@ -172,6 +178,15 @@
   module_inspector_.IncreaseInspectionPriority();
 }
 
+#if defined(GOOGLE_CHROME_BUILD)
+// static
+void ModuleDatabase::RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
+  // Register the pref used to disable the Incompatible Applications warning
+  // using group policy.
+  registry->RegisterBooleanPref(prefs::kThirdPartyBlockingEnabled, true);
+}
+#endif
+
 // static
 uint32_t ModuleDatabase::ProcessTypeToBit(content::ProcessType process_type) {
   uint32_t bit_index =
@@ -258,3 +273,23 @@
       observer->OnNewModuleFound(module.first, module.second);
   }
 }
+
+#if defined(GOOGLE_CHROME_BUILD)
+void ModuleDatabase::MaybeInitializeThirdPartyConflictsManager() {
+  // Early exit if disabled via group policy.
+  const PrefService::Preference* third_party_blocking_enabled_pref =
+      g_browser_process->local_state()->FindPreference(
+          prefs::kThirdPartyBlockingEnabled);
+  if (third_party_blocking_enabled_pref->IsManaged() &&
+      !third_party_blocking_enabled_pref->GetValue()->GetBool())
+    return;
+
+  if (base::FeatureList::IsEnabled(
+          features::kIncompatibleApplicationsWarning) &&
+      base::win::GetVersion() >= base::win::VERSION_WIN10) {
+    third_party_conflicts_manager_ =
+        std::make_unique<ThirdPartyConflictsManager>(this);
+    AddObserver(third_party_conflicts_manager_.get());
+  }
+}
+#endif
diff --git a/chrome/browser/conflicts/module_database_win.h b/chrome/browser/conflicts/module_database_win.h
index e9583721..80bb624 100644
--- a/chrome/browser/conflicts/module_database_win.h
+++ b/chrome/browser/conflicts/module_database_win.h
@@ -19,12 +19,13 @@
 #include "chrome/browser/conflicts/third_party_metrics_recorder_win.h"
 #include "content/public/common/process_type.h"
 
-#if defined(GOOGLE_CHROME_BUILD)
-#include "chrome/browser/conflicts/third_party_conflicts_manager_win.h"
-#endif
-
 class ModuleDatabaseObserver;
 
+#if defined(GOOGLE_CHROME_BUILD)
+class PrefRegistrySimple;
+class ThirdPartyConflictsManager;
+#endif
+
 namespace base {
 class FilePath;
 }
@@ -115,9 +116,11 @@
   void IncreaseInspectionPriority();
 
 #if defined(GOOGLE_CHROME_BUILD)
+  static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
+
   // Accessor for the third party conflicts manager. This is exposed so that the
   // manager can be wired up to the ThirdPartyModuleListComponentInstaller.
-  // Returns returns null on Windows 8.1 and lower.
+  // Returns null if the tracking of incompatible applications is disabled.
   ThirdPartyConflictsManager* third_party_conflicts_manager() {
     return third_party_conflicts_manager_.get();
   }
@@ -170,6 +173,15 @@
   // OnNewModuleFound().
   void NotifyLoadedModules(ModuleDatabaseObserver* observer);
 
+#if defined(GOOGLE_CHROME_BUILD)
+  // Initializes the ThirdPartyConflictsManager, which controls the warning of
+  // incompatible applications that injects into Chrome.
+  // The manager is not initialized if it is disabled via a base::Feature or a
+  // group policy. Note that it is also not initialized on Windows version
+  // 8.1 and less.
+  void MaybeInitializeThirdPartyConflictsManager();
+#endif
+
   // The task runner to which this object is bound.
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
diff --git a/chrome/browser/conflicts/module_event_sink_impl_win_unittest.cc b/chrome/browser/conflicts/module_event_sink_impl_win_unittest.cc
index a4880258..e90c718 100644
--- a/chrome/browser/conflicts/module_event_sink_impl_win_unittest.cc
+++ b/chrome/browser/conflicts/module_event_sink_impl_win_unittest.cc
@@ -11,6 +11,8 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/conflicts/module_database_win.h"
 #include "chrome/common/conflicts/module_watcher_win.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #include <windows.h>
@@ -29,7 +31,8 @@
 class ModuleEventSinkImplTest : public testing::Test {
  protected:
   ModuleEventSinkImplTest()
-      : module_database_(std::make_unique<ModuleDatabase>(
+      : scoped_testing_local_state_(TestingBrowserProcess::GetGlobal()),
+        module_database_(std::make_unique<ModuleDatabase>(
             base::SequencedTaskRunnerHandle::Get())) {}
 
   void CreateModuleSinkImpl() {
@@ -44,6 +47,7 @@
 
   // Must be before |module_database_|.
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  ScopedTestingLocalState scoped_testing_local_state_;
   std::unique_ptr<ModuleDatabase> module_database_;
   std::unique_ptr<ModuleEventSinkImpl> module_event_sink_impl_;
 
diff --git a/chrome/browser/conflicts/problematic_programs_updater_win.cc b/chrome/browser/conflicts/problematic_programs_updater_win.cc
index 07fa38b..805b2413 100644
--- a/chrome/browser/conflicts/problematic_programs_updater_win.cc
+++ b/chrome/browser/conflicts/problematic_programs_updater_win.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/conflicts/third_party_metrics_recorder_win.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -139,9 +140,6 @@
 
 }  // namespace
 
-const base::Feature kIncompatibleApplicationsWarning{
-    "IncompatibleApplicationsWarning", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // ProblematicProgram ----------------------------------------------------------
 
 ProblematicProgramsUpdater::ProblematicProgram::ProblematicProgram(
@@ -160,6 +158,14 @@
 
 // ProblematicProgramsUpdater --------------------------------------------------
 
+ProblematicProgramsUpdater::ProblematicProgramsUpdater(
+    const ModuleListFilter& module_list_filter,
+    const InstalledPrograms& installed_programs)
+    : module_list_filter_(module_list_filter),
+      installed_programs_(installed_programs) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+}
+
 ProblematicProgramsUpdater::~ProblematicProgramsUpdater() = default;
 
 // static
@@ -169,37 +175,16 @@
 }
 
 // static
-std::unique_ptr<ProblematicProgramsUpdater>
-ProblematicProgramsUpdater::MaybeCreate(
-    const ModuleListFilter& module_list_filter,
-    const InstalledPrograms& installed_programs) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  std::unique_ptr<ProblematicProgramsUpdater> instance;
-
-  if (base::FeatureList::IsEnabled(kIncompatibleApplicationsWarning)) {
-    instance.reset(
-        new ProblematicProgramsUpdater(module_list_filter, installed_programs));
-  }
-
-  return instance;
+bool ProblematicProgramsUpdater::IsIncompatibleApplicationsWarningEnabled() {
+  return ModuleDatabase::GetInstance() &&
+         ModuleDatabase::GetInstance()->third_party_conflicts_manager();
 }
 
 // static
 bool ProblematicProgramsUpdater::HasCachedPrograms() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  if (!base::FeatureList::IsEnabled(kIncompatibleApplicationsWarning))
-    return false;
-
-  std::vector<ProblematicProgram> programs = ConvertToProblematicProgramsVector(
-      *g_browser_process->local_state()
-           ->FindPreference(prefs::kProblematicPrograms)
-           ->GetValue());
-
-  RemoveStaleEntriesAndUpdateCache(&programs);
-
-  return !programs.empty();
+  return !GetCachedPrograms().empty();
 }
 
 // static
@@ -207,12 +192,7 @@
 ProblematicProgramsUpdater::GetCachedPrograms() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  std::vector<ProblematicProgram> programs;
-
-  if (!base::FeatureList::IsEnabled(kIncompatibleApplicationsWarning))
-    return programs;
-
-  programs = ConvertToProblematicProgramsVector(
+  std::vector<ProblematicProgram> programs = ConvertToProblematicProgramsVector(
       *g_browser_process->local_state()
            ->FindPreference(prefs::kProblematicPrograms)
            ->GetValue());
@@ -294,9 +274,3 @@
                               std::move(element.second));
   }
 }
-
-ProblematicProgramsUpdater::ProblematicProgramsUpdater(
-    const ModuleListFilter& module_list_filter,
-    const InstalledPrograms& installed_programs)
-    : module_list_filter_(module_list_filter),
-      installed_programs_(installed_programs) {}
diff --git a/chrome/browser/conflicts/problematic_programs_updater_win.h b/chrome/browser/conflicts/problematic_programs_updater_win.h
index 90ffabe3..f0f579a 100644
--- a/chrome/browser/conflicts/problematic_programs_updater_win.h
+++ b/chrome/browser/conflicts/problematic_programs_updater_win.h
@@ -8,28 +8,19 @@
 #include <memory>
 #include <vector>
 
-#include "base/feature_list.h"
 #include "base/macros.h"
 #include "chrome/browser/conflicts/installed_programs_win.h"
 #include "chrome/browser/conflicts/module_database_observer_win.h"
 #include "chrome/browser/conflicts/proto/module_list.pb.h"
-#include "url/gurl.h"
 
 class ModuleListFilter;
 class PrefRegistrySimple;
 
-// A feature that controls whether Chrome warns about incompatible applications.
-extern const base::Feature kIncompatibleApplicationsWarning;
-
 // Maintains a list of problematic programs that are installed on the machine.
 // These programs cause unwanted DLLs to be loaded into Chrome.
 //
 // Because the list is expensive to build, it is cached into the Local State
 // file so that it is available at startup.
-//
-// When kIncompatibleApplicationsWarning is disabled, this class always behaves
-// as-if there are no problematic programs on the computer. This makes it safe
-// to use all of the class' static functions unconditionally.
 class ProblematicProgramsUpdater : public ModuleDatabaseObserver {
  public:
   struct ProblematicProgram {
@@ -46,22 +37,24 @@
     std::unique_ptr<chrome::conflicts::BlacklistAction> blacklist_action;
   };
 
+  // Creates an instance of the updater. |module_list_filter| and
+  // |installed_programs| must outlive the lifetime of this class.
+  ProblematicProgramsUpdater(const ModuleListFilter& module_list_filter,
+                             const InstalledPrograms& installed_programs);
   ~ProblematicProgramsUpdater() override;
 
   static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
 
-  // Creates an instance of the updater. Returns nullptr if the
-  // kIncompatibleApplicationsWarning experiment is disabled.
-  //
-  // |installed_programs| must outlive the lifetime of this class.
-  static std::unique_ptr<ProblematicProgramsUpdater> MaybeCreate(
-      const ModuleListFilter& module_list_filter,
-      const InstalledPrograms& installed_programs);
+  // Returns true if the tracking of incompatible applications is enabled. The
+  // return value will not change throughout the lifetime of the process.
+  static bool IsIncompatibleApplicationsWarningEnabled();
 
   // Returns true if the cache contains at least one problematic program.
+  // Only call this if IsIncompatibleApplicationsWarningEnabled() returns true.
   static bool HasCachedPrograms();
 
   // Returns all the cached problematic programs.
+  // Only call this if IsIncompatibleApplicationsWarningEnabled() returns true.
   static std::vector<ProblematicProgram> GetCachedPrograms();
 
   // ModuleDatabaseObserver:
@@ -70,9 +63,6 @@
   void OnModuleDatabaseIdle() override;
 
  private:
-  ProblematicProgramsUpdater(const ModuleListFilter& module_list_filter,
-                             const InstalledPrograms& installed_programs);
-
   const ModuleListFilter& module_list_filter_;
 
   const InstalledPrograms& installed_programs_;
diff --git a/chrome/browser/conflicts/problematic_programs_updater_win_unittest.cc b/chrome/browser/conflicts/problematic_programs_updater_win_unittest.cc
index faa57f8..719aeaf3 100644
--- a/chrome/browser/conflicts/problematic_programs_updater_win_unittest.cc
+++ b/chrome/browser/conflicts/problematic_programs_updater_win_unittest.cc
@@ -10,7 +10,6 @@
 
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/test_reg_util_win.h"
 #include "base/win/registry.h"
 #include "chrome/browser/conflicts/module_info_win.h"
@@ -96,7 +95,6 @@
   void SetUp() override {
     ASSERT_NO_FATAL_FAILURE(
         registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER));
-    scoped_feature_list_.InitAndEnableFeature(kIncompatibleApplicationsWarning);
   }
 
   enum class Option {
@@ -130,15 +128,10 @@
 
  private:
   content::TestBrowserThreadBundle test_browser_thread_bundle_;
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   ScopedTestingLocalState scoped_testing_local_state_;
-
   registry_util::RegistryOverrideManager registry_override_manager_;
 
   MockModuleListFilter module_list_filter_;
-
   MockInstalledPrograms installed_programs_;
 
   DISALLOW_COPY_AND_ASSIGN(ProblematicProgramsUpdaterTest);
@@ -154,8 +147,9 @@
 // ProblematicProgramsUpdater doesn't do anything when there is no registered
 // installed programs.
 TEST_F(ProblematicProgramsUpdaterTest, NoProblematicPrograms) {
-  auto problematic_programs_updater = ProblematicProgramsUpdater::MaybeCreate(
-      module_list_filter(), installed_programs());
+  auto problematic_programs_updater =
+      std::make_unique<ProblematicProgramsUpdater>(module_list_filter(),
+                                                   installed_programs());
 
   // Simulate some arbitrary module loading into the process.
   problematic_programs_updater->OnNewModuleFound(ModuleInfoKey(dll1_, 0, 0, 0),
@@ -169,8 +163,9 @@
 TEST_F(ProblematicProgramsUpdaterTest, OneConflict) {
   AddProblematicProgram(dll1_, L"Foo", Option::ADD_REGISTRY_ENTRY);
 
-  auto problematic_programs_updater = ProblematicProgramsUpdater::MaybeCreate(
-      module_list_filter(), installed_programs());
+  auto problematic_programs_updater =
+      std::make_unique<ProblematicProgramsUpdater>(module_list_filter(),
+                                                   installed_programs());
 
   // Simulate the module loading into the process.
   problematic_programs_updater->OnNewModuleFound(ModuleInfoKey(dll1_, 0, 0, 0),
@@ -187,8 +182,9 @@
   AddProblematicProgram(dll1_, L"Foo", Option::ADD_REGISTRY_ENTRY);
   AddProblematicProgram(dll2_, L"Bar", Option::ADD_REGISTRY_ENTRY);
 
-  auto problematic_programs_updater = ProblematicProgramsUpdater::MaybeCreate(
-      module_list_filter(), installed_programs());
+  auto problematic_programs_updater =
+      std::make_unique<ProblematicProgramsUpdater>(module_list_filter(),
+                                                   installed_programs());
 
   // Simulate the module loading into the process.
   problematic_programs_updater->OnNewModuleFound(ModuleInfoKey(dll1_, 0, 0, 0),
@@ -214,8 +210,9 @@
 TEST_F(ProblematicProgramsUpdaterTest, PersistsThroughRestarts) {
   AddProblematicProgram(dll1_, L"Foo", Option::ADD_REGISTRY_ENTRY);
 
-  auto problematic_programs_updater = ProblematicProgramsUpdater::MaybeCreate(
-      module_list_filter(), installed_programs());
+  auto problematic_programs_updater =
+      std::make_unique<ProblematicProgramsUpdater>(module_list_filter(),
+                                                   installed_programs());
 
   // Simulate the module loading into the process.
   problematic_programs_updater->OnNewModuleFound(ModuleInfoKey(dll1_, 0, 0, 0),
@@ -235,8 +232,9 @@
   AddProblematicProgram(dll1_, L"Foo", Option::ADD_REGISTRY_ENTRY);
   AddProblematicProgram(dll2_, L"Bar", Option::NO_REGISTRY_ENTRY);
 
-  auto problematic_programs_updater = ProblematicProgramsUpdater::MaybeCreate(
-      module_list_filter(), installed_programs());
+  auto problematic_programs_updater =
+      std::make_unique<ProblematicProgramsUpdater>(module_list_filter(),
+                                                   installed_programs());
 
   // Simulate the modules loading into the process.
   problematic_programs_updater->OnNewModuleFound(ModuleInfoKey(dll1_, 0, 0, 0),
diff --git a/chrome/browser/conflicts/third_party_conflicts_manager_win.cc b/chrome/browser/conflicts/third_party_conflicts_manager_win.cc
index 0f1e100..08b85293 100644
--- a/chrome/browser/conflicts/third_party_conflicts_manager_win.cc
+++ b/chrome/browser/conflicts/third_party_conflicts_manager_win.cc
@@ -97,8 +97,8 @@
 void ThirdPartyConflictsManager::InitializeProblematicProgramsUpdater() {
   DCHECK(module_list_filter_);
   DCHECK(installed_programs_);
-  problematic_programs_updater_ = ProblematicProgramsUpdater::MaybeCreate(
+
+  problematic_programs_updater_ = std::make_unique<ProblematicProgramsUpdater>(
       *module_list_filter_, *installed_programs_);
-  if (problematic_programs_updater_)
-    module_database_->AddObserver(problematic_programs_updater_.get());
+  module_database_->AddObserver(problematic_programs_updater_.get());
 }
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index a1bb49f..91b4eb6 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -272,6 +272,7 @@
 #include "chrome/browser/apps/app_launch_for_metro_restart_win.h"
 #include "chrome/browser/component_updater/sw_reporter_installer_win.h"
 #if defined(GOOGLE_CHROME_BUILD)
+#include "chrome/browser/conflicts/module_database_win.h"
 #include "chrome/browser/conflicts/problematic_programs_updater_win.h"
 #endif  // defined(GOOGLE_CHROME_BUILD)
 #include "chrome/browser/safe_browsing/chrome_cleaner/settings_resetter_win.h"
@@ -466,6 +467,7 @@
   desktop_ios_promotion::RegisterLocalPrefs(registry);
   password_manager::PasswordManager::RegisterLocalPrefs(registry);
 #if defined(GOOGLE_CHROME_BUILD)
+  ModuleDatabase::RegisterLocalStatePrefs(registry);
   ProblematicProgramsUpdater::RegisterLocalStatePrefs(registry);
 #endif  // defined(GOOGLE_CHROME_BUILD)
 #endif
diff --git a/chrome/browser/ui/startup/startup_browser_creator_impl.cc b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
index fe6b7334..c146cdf7 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_impl.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_impl.cc
@@ -679,6 +679,8 @@
     // Check if there are any incompatible applications cached from the last
     // Chrome run.
     has_incompatible_applications =
+        ProblematicProgramsUpdater::
+            IsIncompatibleApplicationsWarningEnabled() &&
         ProblematicProgramsUpdater::HasCachedPrograms();
   }
 #endif
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/md_settings_ui.cc
index 2475c9d..c3d1a1d2 100644
--- a/chrome/browser/ui/webui/settings/md_settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_ui.cc
@@ -230,6 +230,7 @@
 
 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
   bool has_incompatible_applications =
+      ProblematicProgramsUpdater::IsIncompatibleApplicationsWarningEnabled() &&
       ProblematicProgramsUpdater::HasCachedPrograms();
   html_source->AddBoolean("showIncompatibleApplications",
                           has_incompatible_applications);
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 5682b22..1eb24b9 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -314,6 +314,12 @@
 const base::Feature kImprovedRecoveryComponent{
     "ImprovedRecoveryComponent", base::FEATURE_DISABLED_BY_DEFAULT};
 
+#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
+// A feature that controls whether Chrome warns about incompatible applications.
+const base::Feature kIncompatibleApplicationsWarning{
+    "IncompatibleApplicationsWarning", base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
+
 #if !defined(OS_ANDROID)
 // Enables Casting a Presentation API-enabled website to a secondary display.
 const base::Feature kLocalScreenCasting{"LocalScreenCasting",
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index b64e64dc..f86990b 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -172,6 +172,10 @@
 
 extern const base::Feature kImprovedRecoveryComponent;
 
+#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
+extern const base::Feature kIncompatibleApplicationsWarning;
+#endif
+
 #if !defined(OS_ANDROID)
 extern const base::Feature kLocalScreenCasting;
 #endif