blob: 9e5cbf837f09f2faa860fd16123558249435a3c9 [file] [log] [blame]
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_WEB_APP_COMMAND_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_WEB_APP_COMMAND_H_
#include "base/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/values.h"
#include "chrome/browser/web_applications/web_app_constants.h"
#include "chrome/browser/web_applications/web_app_id.h"
#include "components/services/storage/indexed_db/locks/disjoint_range_lock_manager.h"
#include "components/services/storage/indexed_db/locks/leveled_lock_manager.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace content {
class WebContents;
}
namespace web_app {
class WebAppCommandManager;
class WebAppCommandLock {
public:
using LockRequestSet =
base::flat_set<content::DisjointRangeLockManager::LeveledLockRequest>;
WebAppCommandLock(WebAppCommandLock&&);
WebAppCommandLock& operator=(WebAppCommandLock&&);
WebAppCommandLock(const WebAppCommandLock&) = delete;
WebAppCommandLock& operator=(const WebAppCommandLock&) = delete;
~WebAppCommandLock();
// Creates a lock that guarantees isolation against all commands.
// This makes sure that all commands are finished before this command is
// started, and no command will be started during execution of the command.
static WebAppCommandLock CreateForFullSystemLock();
// Creates a lock that guarantees isolation against all
// commands that also use this lock type. Background web app installations can
// use this lock to run tasks sequentially that loads a `WebContents` in the
// background to avoid adding too much load to the system.
static WebAppCommandLock CreateForBackgroundWebContentsLock();
// Creates a lock that guarantees isolation against all commands that has lock
// on any of the `app_ids`.
static WebAppCommandLock CreateForAppLock(base::flat_set<AppId> app_ids);
// Creates a lock that guarantees isolation against all commands that has lock
// on any of the `app_ids` and also grants access to the shared web contents.
static WebAppCommandLock CreateForAppAndWebContentsLock(
base::flat_set<AppId> app_ids);
// Creates a no-op lock that doesn't lock on anything. This is useful to
// create commands that doesn't need isolation protection but would like to be
// managed by WebAppCommandManager for consistent lifecycle management.
static WebAppCommandLock CreateForNoOpLock();
const LockRequestSet& GetLockRequests() const { return lock_requests_; }
bool IsAppLocked(const AppId& app_id) const;
bool IncludesSharedWebContents() const;
const base::flat_set<AppId>& app_ids() const { return app_ids_; }
friend class WebAppCommandManager;
private:
enum class LockType {
kFullSystem,
kApp,
kAppAndWebContents,
kBackgroundWebContents,
kNoOp,
};
explicit WebAppCommandLock(base::flat_set<AppId> app_ids,
LockType lock_type,
LockRequestSet lock_requests);
// Exposed for usage in the WebAppCommandManager to determine if the shared
// web contents can be destroyed.
static content::LeveledLockManager::LeveledLockRequest
GetSharedWebContentsLock();
enum class LockLevel {
kStatic = 0,
kApp = 1,
kMaxValue = kApp,
};
base::flat_set<AppId> app_ids_{};
LockType lock_type_;
LockRequestSet lock_requests_{};
};
enum class CommandResult { kSuccess, kFailure, kShutdown };
// Each command has a queue id, which is either an `AppId` corresponding to a
// specific web app, or `absl::nullopt` for the global queue. The global queue
// is independent (does not block) of other queues.
using WebAppCommandQueueId = absl::optional<AppId>;
// Encapsulates code that reads or modifies the WebAppProvider system. All
// reading or writing to the system should occur in a WebAppCommand to ensure
// that it is isolated. Reading can also happen in any WebAppRegistrar observer.
//
// Commands can only be started by either enqueueing the command in the
// WebAppCommandManager or by having the command be "chained" from another
// command.
// When a command is complete, it can call `SignalCompletionAndSelfDestruct` to
// signal completion and self-destruct. The command can pass a list of "chained"
// commands to run next as part of this operation. This allows for commands to
// re-use each other easily.
//
// Invariants:
// * Destruction can occur without `Start()` being called. If the system shuts
// down and the command was never started, then it will simply be destructed.
// * `OnShutdown()` and `OnSyncSourceRemoved()` are only called if
// the command has been started.
// * `SignalCompletionAndSelfDestruct()` can ONLY be called if `Start()` has
// been called. Otherwise it will CHECK-fail.
class WebAppCommand {
public:
using Id = int;
explicit WebAppCommand(WebAppCommandLock command_lock);
virtual ~WebAppCommand();
// Unique id generated for this command. Currently only used for debug values.
Id id() const { return id_; }
// The command lock that contains isolation information.
const WebAppCommandLock& lock() const { return command_lock_; }
// Returns if the command has been started yet.
bool IsStarted() const { return command_manager_ != nullptr; }
// Returns the pre-existing web contents the installation was
// initiated with. Only implements this when the command is used for
// installation and uses a pre-existing web contents.
virtual content::WebContents* GetInstallingWebContents();
// Returns a debug value to log the state of the command. Used in
// chrome://web-app-internals.
virtual base::Value ToDebugValue() const = 0;
protected:
// Triggered by the WebAppCommandManager. Signals that this command can
// start its operations. When this command is complete, it should call
// `SignalCompletionAndSelfDestruct` to signal it's completion and destruct
// itself. Note: It is not guaranteed that the web app this command was
// created for is still installed. All state must be re-checked when method
// this is called.
virtual void Start() = 0;
// This is called when the sync system has triggered an uninstall for an app
// id that is relevant to this command and this command is running (`Start()
// has been called). Relevance is determined by the
// `WebAppCommandLock::IsAppLocked()` function for this command's lock). The
// web app should still be in the registry, but it will no longer have the
// `WebAppManagement::kSync` source and `is_uninstalling()` will return true.
virtual void OnSyncSourceRemoved() = 0;
// Signals the system is shutting down. Used to cancel any pending operations,
// if possible, to prevent re-entry. Only called if the command has been
// started.
virtual void OnShutdown() = 0;
// Calling this will destroy the command and allow the next command in the
// queue to run. The caller can optionally schedule chained commands.
// Arguments:
// `call_after_destruction`: If the command has a closure that
// needs to be called on the completion of the
// command, it can be passed here to ensure it is
// called after this command is destructed and any
// chained commands are queued.
// Note: This can ONLY be called if `Start()` has been called (`IsStarted()`
// is true). Otherwise it will CHECK-fail.
void SignalCompletionAndSelfDestruct(
CommandResult result,
base::OnceClosure call_after_destruction);
WebAppCommandManager* command_manager() const { return command_manager_; }
// If the `lock()` includes the lock for the kBackgroundWebContents, then this
// will be populated when `Start()` is called.
// Commands can assume that this WebContents will outlive them.
content::WebContents* shared_web_contents() const {
return shared_web_contents_;
}
SEQUENCE_CHECKER(command_sequence_checker_);
private:
friend class WebAppCommandManager;
// Start called by the WebAppCommandManager.
void Start(WebAppCommandManager* command_manager);
base::WeakPtr<WebAppCommand> AsWeakPtr();
Id id_;
WebAppCommandLock command_lock_;
raw_ptr<WebAppCommandManager> command_manager_ = nullptr;
// Because this is owned by the command manager, it will always outlive this
// object. Thus a raw pointer is save.
//
// TODO(crbug.com/1298696): unit_tests breaks with MTECheckedPtr
// enabled. Triage.
raw_ptr<content::WebContents, DegradeToNoOpWhenMTE> shared_web_contents_;
base::WeakPtrFactory<WebAppCommand> weak_factory_{this};
};
} // namespace web_app
#endif // CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_WEB_APP_COMMAND_H_