File: web_app_command.h

package info (click to toggle)
chromium 138.0.7204.183-1~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 6,080,960 kB
  • sloc: cpp: 34,937,079; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,954; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,811; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (211 lines) | stat: -rw-r--r-- 8,650 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// 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 <iterator>
#include <memory>
#include <optional>
#include <tuple>
#include <type_traits>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/sequence_checker.h"
#include "base/strings/to_string.h"
#include "base/types/pass_key.h"
#include "base/values.h"
#include "chrome/browser/web_applications/commands/command_result.h"
#include "chrome/browser/web_applications/commands/internal/command_internal.h"
#include "components/webapps/common/web_app_id.h"

namespace content {
class WebContents;
}

namespace web_app {

class LockDescription;
class WebAppCommandManager;
class WebAppLockManager;

// 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::ScheduleCallback*
// can be used instead of creating a sub-class.
//
// To create a command sub-class, extend the below type `WebAppCommand,
// which allows specification of the type of lock to retrieve. For example:
//
// class GetAppInformationForMySystem
//    : public WebAppCommand<AppLock, CallbackArgType> {
//   GetAppInformationForMySystem(ReportBackInformationCallback callback)
//       : WebAppCommand(std::move(callback),
//         /*args_for_shutdown*/=CallbackArgType::kShutdownValue) {}
//   ...
//   void StartWithLock(std::unique_ptr<AppLock> lock) {
//     ...
//
//     ...
//     CompleteAndSelfDestruct(
//         CommandResult::kSuccess,
//         lock.<information>());
//   }
//   ...
//
//   // Implement this if installing from an external web contents.
//   WebContents* GetInstallingWebContents(...) override;
// };
//
// See the `WebAppLockManager` for information about the available locks & how
// they work.
//
// Commands can only be started by enqueueing the command in the
// WebAppCommandManager, which is done by the WebAppCommandScheduler. When a
// command is complete, it can call `CompleteAndSelfDestruct` to signal
// completion and self-destruct.
//
// Call pattern of commands:
// - StartWithLock(),
// - <subclass stuff>
// - <subclass calls CompleteAndSelfDestruct()>.
//
// The command can use the following optional features:
// - Populate the `GetMutableDebugValue()` with information that is useful for
//   debugging - this shown in chrome://web-app-internals and printed in failed
//   tests.
// - To prevent multiple installs occurring at the same time for a given
//   `WebContents`, installations that install from an external `WebContents`
//   should override `GetInstallingWebContents()` and return that WebContents.
// - `OnShutdown()` can be overridden to do stateless tasks like recording
//   metrics.
//
// 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 and the `callback` will be called with the
//   `args_for_shutdown`, if they exist.
//
// TODO(dmurph): Add an example of a CL that creates a command.
template <typename LockType, typename... CallbackArgs>
class WebAppCommand : public internal::CommandWithLock<LockType> {
 public:
  using PassKey = base::PassKey<WebAppCommand>;
  using LockDescription = LockType::LockDescription;
  using CallbackType = base::OnceCallback<void(CallbackArgs...)>;
  using ShutdownArgumentsTuple = std::tuple<std::decay_t<CallbackArgs>...>;

  // Special constructor if the callback doesn't take any arguments. There is no
  // need to specify an empty tuple.
  template <std::size_t i = sizeof...(CallbackArgs)>
    requires(i == 0)
  WebAppCommand(const std::string& name,
                LockDescription initial_lock_request,
                CallbackType callback)
      : internal::CommandWithLock<LockType>(name,
                                            std::move(initial_lock_request)),
        callback_(std::move(callback)) {
    CHECK(!callback_.is_null());
  }

  template <std::size_t i = sizeof...(CallbackArgs)>
    requires(i >= 1)
  WebAppCommand(const std::string& name,
                LockDescription initial_lock_request,
                CallbackType callback,
                ShutdownArgumentsTuple args_for_shutdown)
      : internal::CommandWithLock<LockType>(name,
                                            std::move(initial_lock_request)),
        callback_(std::move(callback)),
        args_for_shutdown_(std::move(args_for_shutdown)) {
    CHECK(!callback_.is_null());
  }

  ~WebAppCommand() override = default;

  base::OnceClosure TakeCallbackWithShutdownArgs(
      base::PassKey<WebAppCommandManager>) override {
    DCHECK_CALLED_ON_VALID_SEQUENCE(
        internal::CommandBase::command_sequence_checker_);
    CHECK(!callback_.is_null());
    internal::CommandBase::GetMutableDebugValue().Set("!command_result",
                                                      "kShutdown");
    if constexpr (sizeof...(CallbackArgs) == 0) {
      return std::move(callback_);
    } else {
      internal::CommandBase::GetMutableDebugValue().Set(
          "!result", base::ToString(args_for_shutdown_));

      // We need to call BindOnce with both the callback and the shutdown args,
      // so they must be concatenated into a tuple with both before calling
      // std::apply.
      // The below code is the C++ equivalent of
      // `callback_.bind(...args_used_on_shutdown_)` in JavaScript.
      std::tuple<CallbackType, CallbackArgs...> bind_arguments =
          std::tuple_cat<std::tuple<CallbackType>, std::tuple<CallbackArgs...>>(
              /*tuple1=*/{std::move(callback_)},
              /*tuple2=*/std::move(args_for_shutdown_));
      return std::apply(
          &base::BindOnce<base::OnceCallback<void(CallbackArgs...)>,
                          CallbackArgs...>,
          std::move(bind_arguments));
    }
  }

 protected:
  // Calling this will destroy the command and allow the next command in the
  // queue to run. This will do the following in order:
  // 1) Destroy this object.
  // 2) Call this command's `callback` with the given `args`.
  // The `result` reports if the command encountered any unknown errors.
  // TODO(dmurph): Use `result` in metrics. https://b/304553492.
  void CompleteAndSelfDestruct(CommandResult result,
                               CallbackArgs... args_for_callback,
                               const base::Location& location = FROM_HERE) {
    DCHECK_CALLED_ON_VALID_SEQUENCE(
        internal::CommandBase::command_sequence_checker_);

    base::Value::Dict* metadata =
        internal::CommandBase::GetMutableDebugValue().EnsureDict("!metadata");
    CHECK(internal::CommandBase::command_manager())
        << "Command was never given to the command manager: "
        << internal::CommandBase::GetMutableDebugValue().DebugString();
    metadata->Set("command_result",
                  result == CommandResult::kSuccess ? "kSuccess" : "kFailure");
    metadata->Set(
        "result",
        base::ToString(std::tie<CallbackArgs&...>(args_for_callback...)));
    metadata->Set("completion_location", base::ToString(location));

    // Note: `BindOnce` should correctly handle copying any ref or move
    // arguments internally. This allows the callback arguments to contain ref
    // types (which are standard for mojo callbacks) or move-only types.
    internal::CommandBase::CompleteAndSelfDestructInternal(
        result,
        base::BindOnce(std::move(callback_),
                       std::forward<CallbackArgs>(args_for_callback)...));
  }

 private:
  CallbackType callback_;
  ShutdownArgumentsTuple args_for_shutdown_;
};

}  // namespace web_app

#endif  // CHROME_BROWSER_WEB_APPLICATIONS_COMMANDS_WEB_APP_COMMAND_H_