chromium/chrome/browser/win/conflicts/module_database.h

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_WIN_CONFLICTS_MODULE_DATABASE_H_
#define CHROME_BROWSER_WIN_CONFLICTS_MODULE_DATABASE_H_

#include <map>
#include <memory>

#include "base/memory/scoped_refptr.h"
#include "base/observer_list.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/branding_buildflags.h"
#include "chrome/browser/win/conflicts/installed_applications.h"
#include "chrome/browser/win/conflicts/module_info.h"
#include "chrome/browser/win/conflicts/module_inspector.h"
#include "chrome/browser/win/conflicts/third_party_metrics_recorder.h"
#include "content/public/common/process_type.h"

class ModuleDatabaseObserver;

namespace base {
class FilePath;
class SequencedTaskRunner;
}  // namespace base

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
class ModuleLoadAttemptLogListener;
class PrefChangeRegistrar;
class PrefRegistrySimple;
class ThirdPartyConflictsManager;

namespace base {
struct OnTaskRunnerDeleter;
}
#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)

// A class that keeps track of all modules loaded across Chrome processes.
//
// It is also the main class behind third-party modules tracking, and owns the
// different classes that required to identify incompatible applications and
// record metrics.
//
// This is effectively a singleton, but doesn't use base::Singleton. The intent
// is for the object to be created when Chrome is single-threaded, and for it
// be set as the process-wide singleton via SetInstance.
class ModuleDatabase : public ModuleDatabaseEventSource {
 public:
  // Structures for maintaining information about modules.
  using ModuleMap = std::map<ModuleInfoKey, ModuleInfoData>;
  using ModuleInfo = ModuleMap::value_type;

  // The Module Database becomes idle after this timeout expires without any
  // module events.
  static constexpr base::TimeDelta kIdleTimeout = base::Seconds(10);

  // Creates the ModuleDatabase. Must be created and set on the sequence
  // returned by GetTaskRunner().
  explicit ModuleDatabase(bool third_party_blocking_policy_enabled);

  ModuleDatabase(const ModuleDatabase&) = delete;
  ModuleDatabase& operator=(const ModuleDatabase&) = delete;

  ~ModuleDatabase() override;

  // Returns the SequencedTaskRunner on which the ModuleDatabase lives. Can be
  // called on any thread.
  static scoped_refptr<base::SequencedTaskRunner> GetTaskRunner();

  // Retrieves the singleton global instance of the ModuleDatabase. Must only be
  // called on the sequence returned by GetTaskRunner().
  static ModuleDatabase* GetInstance();

  // Sets the global instance of the ModuleDatabase. Ownership is passed to the
  // global instance and deliberately leaked, unless manually cleaned up. This
  // has no locking and should be called when Chrome is single threaded.
  static void SetInstance(std::unique_ptr<ModuleDatabase> module_database);

  // Initializes the ModuleLoadAttemptLogListener instance. This function is a
  // noop on non-GOOGLE_CHROME_BRANDING configurations because it is used only
  // for third-party software blocking, which is only enabled in Google Chrome
  // builds.
  void StartDrainingModuleLoadAttemptsLog();

  // Returns true if the ModuleDatabase is idle. This means that no modules are
  // currently being inspected, and no new module events have been observed in
  // the last 10 seconds.
  bool IsIdle();

  // Indicates that a new registered shell extension was found.
  void OnShellExtensionEnumerated(const base::FilePath& path,
                                  uint32_t size_of_image,
                                  uint32_t time_date_stamp);

  // Indicates that all shell extensions have been enumerated.
  void OnShellExtensionEnumerationFinished();

  // Indicates that a new registered input method editor was found.
  void OnImeEnumerated(const base::FilePath& path,
                       uint32_t size_of_image,
                       uint32_t time_date_stamp);

  // Indicates that all input method editors have been enumerated.
  void OnImeEnumerationFinished();

  // Indicates that a module has been loaded. The data passed to this function
  // is taken as gospel, so if it originates from a remote process it should be
  // independently validated first. (In practice, see ModuleEventSinkImpl for
  // details of where this happens.)
  void OnModuleLoad(content::ProcessType process_type,
                    const base::FilePath& module_path,
                    uint32_t module_size,
                    uint32_t module_time_date_stamp);

  // Forwards the module load event to the ModuleDatabase global instance via
  // OnModuleLoad() on the ModuleDatabase task runner. Can be called on any
  // threads. Provided for convenience.
  static void HandleModuleLoadEvent(content::ProcessType process_type,
                                    const base::FilePath& module_path,
                                    uint32_t module_size,
                                    uint32_t module_time_date_stamp);

  void OnModuleBlocked(const base::FilePath& module_path,
                       uint32_t module_size,
                       uint32_t module_time_date_stamp);

  // Marks the module as added to the module blocklist cache, which means it
  // will be blocked on the next browser launch.
  void OnModuleAddedToBlocklist(const base::FilePath& module_path,
                                uint32_t module_size,
                                uint32_t module_time_date_stamp);

  // TODO(chrisha): Module analysis code, and various accessors for use by
  // chrome://conflicts.

  // Adds or removes an observer.
  // Note that when adding an observer, OnNewModuleFound() will immediately be
  // called once for all modules that are already loaded before returning to the
  // caller. In addition, if the ModuleDatabase is currently idle,
  // OnModuleDatabaseIdle() will also be invoked.
  //
  // ModuleDatabaseEventSource:
  void AddObserver(ModuleDatabaseObserver* observer) override;
  void RemoveObserver(ModuleDatabaseObserver* observer) override;

  void StartInspection();

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  // Similar with the GetInstance() but overwriting third party conflicts
  // manager's installed_applications_ for testing.
  static ModuleDatabase* GetInstanceForTesting(
      std::unique_ptr<InstalledApplications>);

  static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);

  // Returns false if third-party modules blocking is disabled via
  // administrative policy.
  static bool IsThirdPartyBlockingPolicyEnabled();

  // Disables the blocking of third-party modules in the browser process. It is
  // safe to invoke this function from any thread.
  // This function is meant to be used only as a workaround for the in-process
  // printing code that may require that third-party DLLs be successfully
  // loaded into the process to work correctly.
  // TODO(pmonette): Remove this workaround when printing is moved to a utility
  //                 process. See https://crbug.com/892294.
  static void DisableThirdPartyBlocking();

  // Destroys the |third_party_conflicts_manager_| instance. Invoked by
  // the |pref_change_registrar_| when it detects that the policy was disabled.
  // Note: This is distinct from OnThirdPartyBlockingDisabled(). When the policy
  //       is disabled, the |third_party_conflicts_manager_| is destroyed as if
  //       it was never initialized.
  void OnThirdPartyBlockingPolicyDisabled();

  // Accessor for the third party conflicts manager.
  // Returns null if both the tracking of incompatible applications and the
  // blocking of third-party modules are disabled.
  // Do not hold a pointer to the manager because it can be destroyed if the
  // ThirdPartyBlocking policy is later disabled.
  ThirdPartyConflictsManager* third_party_conflicts_manager() {
    return third_party_conflicts_manager_.get();
  }
#endif

 private:
  friend class TestModuleDatabase;
  friend class ModuleDatabaseTest;
  friend class ModuleEventSinkImplTest;

  ModuleInfo* CreateModuleInfo(const base::FilePath& module_path,
                               uint32_t module_size,
                               uint32_t module_time_date_stamp);

  // Finds or creates a mutable ModuleInfo entry. Returns true if the module
  // info was created.
  bool FindOrCreateModuleInfo(const base::FilePath& module_path,
                              uint32_t module_size,
                              uint32_t module_time_date_stamp,
                              ModuleInfo** module_info);

  // Returns true if the enumeration of the IMEs and the shell extensions is
  // finished.
  //
  // To avoid sending an improperly tagged module to an observer (in case a race
  // condition happens and the module is loaded before the enumeration is done),
  // it's important that this function returns true before any calls to
  // OnNewModuleFound() is made.
  bool RegisteredModulesEnumerated();

  // Called when RegisteredModulesEnumerated() becomes true. Notifies the
  // observers of each already inspected modules and checks if the idle state
  // should be entered.
  void OnRegisteredModulesEnumerated();

  // Callback for ModuleInspector.
  void OnModuleInspected(const ModuleInfoKey& module_key,
                         ModuleInspectionResult inspection_result);

  // If the ModuleDatabase is truly idle, calls EnterIdleState().
  void OnDelayExpired();

  // Notifies the observers that ModuleDatabase is now idle.
  void EnterIdleState();

  // Notifies the |observer| of already found and inspected modules via
  // OnNewModuleFound().
  void NotifyLoadedModules(ModuleDatabaseObserver* observer);

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  // Called by DisableThirdPartyBlocking() to disable the analysis of loaded
  // modules.
  // Note: This is distinct from OnThirdPartyBlockingPolicyDisabled() because
  //       they have a different effect. OnThirdPartyBlockingDisabled() keeps
  //       the |third_party_conflicts_manager_| instance alive.
  // TODO(pmonette): Remove this workaround when printing is moved to a utility
  //                 process. See https://crbug.com/892294.
  void OnThirdPartyBlockingDisabled();

  // Initializes the ThirdPartyConflictsManager, which controls showing warnings
  // for incompatible applications that inject into Chrome and the blocking of
  // third-party modules. The manager is only initialized if either or both of
  // the ThirdPartyModulesBlocking and IncompatibleApplicationsWarning features
  // are enabled.
  void MaybeInitializeThirdPartyConflictsManager(
      bool third_party_blocking_policy_enabled);
#endif

  // A map of all known modules.
  ModuleMap modules_;

  base::RetainingOneShotTimer idle_timer_;

  // Indicates if the ModuleDatabase has started processing module load events.
  bool has_started_processing_;

  // Indicates if all shell extensions have been enumerated.
  bool shell_extensions_enumerated_;

  // Indicates if all input method editors have been enumerated.
  bool ime_enumerated_;

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  std::unique_ptr<ModuleLoadAttemptLogListener>
      module_load_attempt_log_listener_;

  // Observes the ThirdPartyBlockingEnabled group policy on the UI thread.
  std::unique_ptr<PrefChangeRegistrar, base::OnTaskRunnerDeleter>
      pref_change_registrar_;
#endif

  // Inspects new modules on a blocking task runner.
  ModuleInspector module_inspector_;

  // Holds observers.
  base::ObserverList<ModuleDatabaseObserver>::Unchecked observer_list_;

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  std::unique_ptr<ThirdPartyConflictsManager> third_party_conflicts_manager_;
#endif

  // Records metrics on third-party modules.
  ThirdPartyMetricsRecorder third_party_metrics_;

  SEQUENCE_CHECKER(sequence_checker_);
};

#endif  // CHROME_BROWSER_WIN_CONFLICTS_MODULE_DATABASE_H_