// 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_