chromium/chrome/browser/ash/crosapi/browser_loader.h

// Copyright 2020 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_ASH_CROSAPI_BROWSER_LOADER_H_
#define CHROME_BROWSER_ASH_CROSAPI_BROWSER_LOADER_H_

#include <memory>
#include <optional>
#include <vector>

#include "base/files/file_path.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/version.h"
#include "chromeos/ash/components/standalone_browser/lacros_selection.h"

namespace component_updater {
class ComponentManagerAsh;
}  // namespace component_updater

namespace crosapi {

class LacrosSelectionLoader;
class LacrosSelectionLoaderFactory;
using ash::standalone_browser::LacrosSelection;

// Manages download of the lacros-chrome binary.
// This class is a part of ash-chrome.
class BrowserLoader {
 public:
  explicit BrowserLoader(
      scoped_refptr<component_updater::ComponentManagerAsh> manager);

  // Constructor for testing.
  explicit BrowserLoader(std::unique_ptr<LacrosSelectionLoaderFactory> factory);

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

  virtual ~BrowserLoader();

  // Returns true if the browser loader will try to load stateful lacros-chrome
  // builds from the component manager. This may return false if the user
  // specifies the lacros-chrome binary on the command line or the user has
  // forced the lacros selection to rootfs.
  // If this returns false subsequent loads of lacros-chrome will never load
  // a newer lacros-chrome version and update checking can be skipped.
  static bool WillLoadStatefulComponentBuilds();

  struct LacrosSelectionVersion {
    LacrosSelectionVersion(LacrosSelection selection, base::Version version);

    // LacrosSelection::kRootfs or kStateful should be set here, not
    // kDeployedLocally.
    LacrosSelection selection;

    base::Version version;
  };

  // Indicates how lacros is selected.
  enum class LacrosSelectionSource {
    kUnknown,

    // Selected by comparing the version to choose newer one or either of the
    // lacros selection is not available.
    kCompatibilityCheck,

    // Forced by the selection policy or about:flags. See
    // browser_util::DetermineLacrosSelection for the detailed condition when
    // the lacros selection is forced.
    kForced,

    // Forced by registered lacros-chrome path.
    kDeployedPath,
  };

  // Starts to load lacros-chrome binary or the rootfs lacros-chrome binary.
  // `callback` is called on completion with the path to the lacros-chrome on
  // success, or an empty filepath on failure, and the loaded lacros selection
  // which is either 'rootfs' or 'stateful'.
  //
  // If Load is called during Load, the previous load requests are discarded
  // and immediately begin loading. LoadCompletionCallback for the previous
  // request will NOT be called in this scenario.
  // TODO(elkurin): We should run Unload for previous LacroSelectionLoader.
  //
  // Load should NOT be called once Unlaod is requested. Note that per-
  // LacrosSelectionLoader unload that is requested inside BrowserManager class
  // may run before or while calling Load.
  // If Load is called during per-loader unload, Load request will be processed
  // after all ongoing per-loader unload requests complete that are at most 2.
  // Currently, this happens only when selecting stateful lacros-chrome and
  // unloading rootfs.
  using LoadCompletionCallback = base::OnceCallback<
      void(const base::FilePath&, LacrosSelection, base::Version)>;
  virtual void Load(LoadCompletionCallback callback);

  // Starts to unload lacros-chrome binary.
  // Once Unload is called, BrowserLoader should NOT accept Load requests.
  //
  // If Unload is called during Load, stops loading procedure on main thread and
  // requests LacrosSelectionLoaders to unload. Each loader will start unloading
  // after the current task completed.
  //
  // If Unload is called during Unload, no need to unload again so the
  // coming unload request will be ignored.
  virtual void Unload();

 private:
  FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest,
                           OnLoadSelectionQuicklyChooseRootfs);
  FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest,
                           OnLoadVersionSelectionNeitherIsAvailable);
  FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest,
                           OnLoadVersionSelectionStatefulIsUnavailable);
  FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest,
                           OnLoadVersionSelectionRootfsIsUnavailable);
  FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest,
                           OnLoadVersionSelectionRootfsIsNewer);
  FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest,
                           OnLoadVersionSelectionRootfsIsOlder);
  FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest,
                           OnLoadVersionSelectionSameVersions);
  FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest, OnLoadSelectionPolicyIsRootfs);
  FRIEND_TEST_ALL_PREFIXES(
      BrowserLoaderTest,
      OnLoadSelectionPolicyIsUserChoiceAndCommandLineIsRootfs);
  FRIEND_TEST_ALL_PREFIXES(
      BrowserLoaderTest,
      OnLoadSelectionPolicyIsUserChoiceAndCommandLineIsStateful);
  FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest,
                           OnLoadLacrosBinarySpecifiedBySwitch);
  FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest,
                           OnLoadLacrosDirectorySpecifiedBySwitch);
  FRIEND_TEST_ALL_PREFIXES(BrowserLoaderTest, LoadWhileUnloading);

  // Starts loading now/
  void LoadNow(LoadCompletionCallback callback);

  // `source` indicates why rootfs/stateful is selected. `source` is only used
  // for logging.
  void SelectRootfsLacros(LoadCompletionCallback callback,
                          LacrosSelectionSource source);
  void SelectStatefulLacros(LoadCompletionCallback callback,
                            LacrosSelectionSource source);

  // Called on GetVersion for each rootfs and stateful lacros loader.
  void OnGetVersion(
      LacrosSelection selection,
      base::OnceCallback<void(LacrosSelectionVersion)> barrier_callback,
      const base::Version& version);

  // Called to determine which lacros to load based on version (rootfs vs
  // stateful).
  // `versions` consists of 2 elements, one for rootfs lacros, one for stateful
  // lacros. The order is not specified.
  void OnLoadVersions(LoadCompletionCallback callback,
                      std::vector<LacrosSelectionVersion> versions);

  // Called on the completion of loading.
  void OnLoadComplete(LoadCompletionCallback callback,
                      LacrosSelection selection,
                      base::Version version,
                      const base::FilePath& path);
  void FinishOnLoadComplete(LoadCompletionCallback callback,
                            const base::FilePath& path,
                            LacrosSelection selection,
                            base::Version version,
                            const base::FilePath& lacros_binary);

  // Called on unload completed.
  void OnUnloadCompleted(LacrosSelection selection);

  // Loader for rootfs lacros and stateful lacros. Loader objects are
  // constructed on start loading and reset on unload completion or on reload.
  std::unique_ptr<LacrosSelectionLoader> rootfs_lacros_loader_;
  std::unique_ptr<LacrosSelectionLoader> stateful_lacros_loader_;

  std::unique_ptr<LacrosSelectionLoaderFactory> factory_;

  // Time when the lacros component was loaded.
  base::TimeTicks lacros_start_load_time_;

  // Called when Unload is completed for both rootfs and stateful lacros.
  base::OnceClosure callback_on_unload_completion_;

  // Set to true if BrowserLoader::Unload is requested.
  bool is_unload_requested_ = false;

  // Used for DCHECKs to ensure method calls executed in the correct thread.
  SEQUENCE_CHECKER(sequence_checker_);

  base::WeakPtrFactory<BrowserLoader> weak_factory_{this};
};

std::ostream& operator<<(std::ostream&, BrowserLoader::LacrosSelectionSource);

}  // namespace crosapi

#endif  // CHROME_BROWSER_ASH_CROSAPI_BROWSER_LOADER_H_