blob: e3a6a85b21427634a35580eaf7b2d3f63da2d265 [file] [log] [blame]
// Copyright 2022 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_WEB_APPLICATIONS_COMMANDS_WEB_APP_COMMAND_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_WEB_APP_COMMAND_H_
#include <memory>
#include "base/functional/callback_forward.h"
#include "base/location.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_id.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace content {
class WebContents;
}
namespace web_app {
class LockDescription;
class WebAppCommandManager;
class WebAppLockManager;
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 allow an operation to:
// - Ensure that resources are not being used by another operation (e.g. no
// other operation is operating on the given app id).
// - Automatically handles edge cases like profile shutdown.
// - Prevent any possible re-entry bugs by allowing any final callback to be
// called after the command is destructed.
// - Record detailed logs that are exposed in chrome://web-app-internals.
//
// For simple operations that require holding on to lock only for single
// synchronous function calls, WebAppCommandScheduler::ScheduleCallbackWithLock
// can be used instead of creating a sub-class.
//
// To create a command sub-class, extend the below type `WebAppCommandTemplate`,
// which allows specification of the type of lock to retrieve. For example:
//
// class GetAppInformationForMySystem : public WebAppCommandTemplate<AppLock> {
// GetAppInformationForMySystem(ReportBackInformationCallback callback)
// : callback_(callback) {}
// ...
// void StartWithLock(std::unique_ptr<AppLock> lock) {
// ...
//
// ...
// SignalCompletionAndSelfDestruct(
// CommandResult::kSuccess,
// base::Bind(std::move(callback_), lock.<information>()));
// }
// ...
// };
//
// See the `WebAppLockManager` for information about the available locks & how
// they work.
//
// Commands can only be started by either enqueueing the command in the
// WebAppCommandManager, which is done by the WebAppCommandScheduler. When a
// command is complete, it can call `SignalCompletionAndSelfDestruct` to signal
// completion and self-destruct.
//
// Invariants:
// * Destruction can occur without `StartWithLock()` 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 `StartWithLock()`
// has been called. Otherwise it will CHECK-fail.
class WebAppCommand {
public:
using Id = int;
explicit WebAppCommand(const std::string& name);
virtual ~WebAppCommand();
// Returns if the command has been started yet.
bool IsStarted() const { return command_manager() != nullptr; }
// Unique id generated for this command.
Id id() const { return id_; }
const std::string& name() const { return name_; }
const absl::optional<base::Location>& scheduled_location() const {
return scheduled_location_;
}
// Returns a debug value to log the state of the command. Used in
// chrome://web-app-internals.
virtual base::Value ToDebugValue() const = 0;
protected:
virtual const LockDescription& lock_description() const = 0;
// 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();
using LockAcquiredCallback =
base::OnceCallback<void(base::OnceClosure start_command)>;
// Triggered by the WebAppCommandManager. Request lock and start the command
// after the lock is acquired.
virtual void RequestLock(WebAppCommandManager* command_manager,
WebAppLockManager* lock_manager,
LockAcquiredCallback on_lock_acquired,
const base::Location& location) = 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 `StartWithLock()` has been called
// (`IsStarted()` is true). Otherwise it will CHECK-fail.
void SignalCompletionAndSelfDestruct(
CommandResult result,
base::OnceClosure call_after_destruction);
virtual WebAppCommandManager* command_manager() const;
SEQUENCE_CHECKER(command_sequence_checker_);
private:
friend class WebAppCommandManager;
void SetScheduledLocation(const base::Location& location);
base::WeakPtr<WebAppCommand> AsWeakPtr();
Id id_;
std::string name_;
absl::optional<base::Location> scheduled_location_;
raw_ptr<WebAppCommandManager> command_manager_ = nullptr;
base::WeakPtrFactory<WebAppCommand> weak_factory_{this};
};
template <typename LockType>
class WebAppCommandTemplate : public WebAppCommand {
public:
explicit WebAppCommandTemplate(const std::string& name);
~WebAppCommandTemplate() override;
// Triggered after lock is acquired. 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 this
// method is called.
virtual void StartWithLock(std::unique_ptr<LockType> lock) = 0;
protected:
// WebAppCommand:
WebAppCommandManager* command_manager() const override {
return command_manager_;
}
private:
// WebAppCommand:
void RequestLock(WebAppCommandManager* command_manager,
WebAppLockManager* lock_manager,
LockAcquiredCallback on_lock_acquired,
const base::Location& location) override;
void PrepareForStart(WebAppCommandManager* command_manager,
LockAcquiredCallback on_lock_acquired,
std::unique_ptr<LockType> lock);
raw_ptr<WebAppCommandManager> command_manager_ = nullptr;
base::WeakPtrFactory<WebAppCommandTemplate> weak_factory_{this};
};
} // namespace web_app
#endif // CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_WEB_APP_COMMAND_H_