File: instance_registry.h

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (294 lines) | stat: -rw-r--r-- 11,890 bytes parent folder | download | duplicates (6)
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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INSTANCE_REGISTRY_H_
#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INSTANCE_REGISTRY_H_

#include <list>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>

#include "ash/public/cpp/shelf_types.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "components/services/app_service/public/cpp/instance.h"
#include "components/services/app_service/public/cpp/instance_update.h"
#include "ui/aura/window.h"

class InstanceRegistryTest;

namespace apps {

// The parameters to create or update the instance for the `app_id` and
// `window`, when calling InstanceRegistry::CreateOrUpdateInstance.
struct InstanceParams {
  InstanceParams(const std::string& app_id, aura::Window* window);

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

  ~InstanceParams();

  const std::string app_id;
  raw_ptr<aura::Window> window;
  std::optional<std::string> launch_id;
  std::optional<std::pair<InstanceState, base::Time>> state;
  std::optional<content::BrowserContext*> browser_context;
};

// An in-memory store of all the Instances (i.e. running apps) seen by
// AppServiceProxy. Can be queried synchronously for information about the
// currently running instances, and can be observed to receive updates about
// changes to Instance state.
//
// InstanceRegistry receives a stream of `app::Instance` delta updates from App
// Service, and stores the "sum" of these updates. When a new `apps::Instance`
// is received, observers are notified about the update, and then the delta is
// "added" to the stored state.
//
// This class is not thread-safe.
class InstanceRegistry {
 public:
  class Observer : public base::CheckedObserver {
   public:
    // Called whenever the InstanceRegistry receives an update for any
    // instance. `update` exposes the latest field values and whether they have
    // changed in this update (as per the docs on `apps::InstanceUpdate`). The
    // `update` argument shouldn't be accessed after OnAppUpdate returns.
    virtual void OnInstanceUpdate(const InstanceUpdate& update) = 0;

    // Called when the InstanceRegistry object (the thing that this observer
    // observes) will be destroyed. In response, the observer, |this|, should
    // call "instance_registry->RemoveObserver(this)", whether directly or
    // indirectly (e.g. via base::ScopedObservation::Reset).
    virtual void OnInstanceRegistryWillBeDestroyed(InstanceRegistry* cache) = 0;

   protected:
    ~Observer() override;
  };

  InstanceRegistry();
  ~InstanceRegistry();

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

  // Prefer using a base::ScopedObservation to safely manage the observation,
  // instead of calling these methods directly.
  void AddObserver(Observer* observer);
  void RemoveObserver(Observer* observer);

  using InstancePtr = std::unique_ptr<Instance>;
  using InstanceIds = std::set<base::UnguessableToken>;

  // Creates a new instance for the `app_id` and `window` with a new instance id
  // if there is no exist instance. Otherwise, reuse the existing instance id
  // with `param` to update the instance.
  //
  // This function calls OnInstance to add the new instance or update the
  // existing instance.
  //
  // Note: For Lacros windows having multiple tabs/nstances, this interface
  // should not be called, since `window` might have multiple instances.
  void CreateOrUpdateInstance(InstanceParams&& param);

  // Notification and merging might be delayed until after OnInstance returns.
  // For example, suppose that the initial set of states is (a0, b0, c0) for
  // three app_id's ("a", "b", "c"). Now suppose OnInstance is called with an
  // update (b1), and when notified of b1, an observer calls OnInstance
  // again with (b2). The b1 delta should be processed before the b2 delta,
  // as it was sent first, and both b1 and b2 will be updated to the observer
  // following the sequence. This means that processing b2 (scheduled by the
  // second OnInstance call) should wait until the first OnInstance call has
  // finished processing b1, and then b2, which means that processing b2 is
  // delayed until after the second OnInstance call returns.
  //
  // The caller presumably calls OnInstance(std::move(delta)).
  void OnInstance(InstancePtr delta);

  // Returns instances for the |app_id|.
  std::set<raw_ptr<const Instance, SetExperimental>> GetInstances(
      const std::string& app_id);

  // Returns one state for the `window`.
  //
  // Note: This interface is used for the standalone window, or the ash Chrome
  // browser tab window, which has one instance only. For Lacros windows which
  // might have multiple instances for tabs, this interface should not be
  // called, since `window` might have multiple instances, and the InstanceState
  // returned in these cases will be arbitrary.
  InstanceState GetState(const aura::Window* window) const;

  // Returns the shelf id for the `window`.
  //
  // Note: This interface is used for the standalone window, or the ash Chrome
  // browser tab window, which has one instance only. For Lacros windows which
  // might have multiple instances for tabs, this interface should not be
  // called, since `window` might have multiple instances, and the ShelfID
  // returned in these cases will be arbitrary.
  ash::ShelfID GetShelfId(const aura::Window* window) const;

  // Return true if there is an instance for the `window`.
  bool Exists(const aura::Window* window) const;

  // Return true if there is any instance in the InstanceRegistry for |app_id|.
  bool ContainsAppId(const std::string& app_id) const;

  // Calls f, a void-returning function whose arguments are (const
  // apps::InstanceUpdate&), on each window in the instance_registry.
  //
  // f's argument is an apps::InstanceUpdate instead of an Instance* so that
  // callers can more easily share code with Observer::OnInstanceUpdate (which
  // also takes an apps::InstanceUpdate), and an apps::InstanceUpdate also has a
  // StateIsNull method.
  //
  // The apps::InstanceUpdate argument to f shouldn't be accessed after f
  // returns.
  //
  // f must be synchronous, and if it asynchronously calls ForEachInstance
  // again, it's not guaranteed to see a consistent state.
  template <typename FunctionType>
  void ForEachInstance(FunctionType f) const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);

    for (const auto& s_iter : states_) {
      const apps::Instance* state = s_iter.second.get();
      f(apps::InstanceUpdate(state, nullptr));
    }
  }

  // Calls f, a void-returning function whose arguments are (const
  // apps::InstanceUpdate&), on the instance in the instance_registry with the
  // given instance id. It will return true (and call f) if there is such an
  // instance, otherwise it will return false (and not call f). The
  // InstanceUpdate argument to f has the same semantics as for ForEachInstance,
  // above.
  //
  // f must be synchronous, and if it asynchronously calls ForOneInstance again,
  // it's not guaranteed to see a consistent state.
  template <typename FunctionType>
  bool ForOneInstance(const base::UnguessableToken& instance_id,
                      FunctionType f) const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);

    auto s_iter = states_.find(instance_id);
    apps::Instance* state =
        (s_iter != states_.end()) ? s_iter->second.get() : nullptr;
    if (state) {
      f(apps::InstanceUpdate(state, nullptr));
      return true;
    }
    return false;
  }

  // Calls f, a void-returning function whose arguments are (const
  // apps::InstanceUpdate&), on instances in the instance_registry with the
  // given window. It will return true (and call f) if there is such an
  // instance, otherwise it will return false (and not call f). The
  // InstanceUpdate argument to f has the same semantics as for ForEachInstance,
  // above.
  //
  // f must be synchronous, and if it asynchronously calls ForOneInstance again,
  // it's not guaranteed to see a consistent state.
  template <typename FunctionType>
  bool ForInstancesWithWindow(const aura::Window* window,
                              FunctionType f) const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);

    InstanceIds instance_ids;
    auto it = window_to_instance_ids_.find(window);
    if (it != window_to_instance_ids_.end()) {
      instance_ids = it->second;
    }

    // There could be some instances in `deltas_pending_`.
    for (const auto& delta : deltas_pending_) {
      if (delta->Window() == window) {
        instance_ids.insert(delta->InstanceId());
      }
    }

    // `old_state_` might be an instance to be deleted. But in OnInstanceUpdate,
    // the caller might still need that instance to know which instance will be
    // deleted.
    if (old_state_ && old_state_.get()) {
      instance_ids.insert(old_state_->InstanceId());
    }

    if (instance_ids.empty()) {
      return false;
    }

    for (const auto& instance_id : instance_ids) {
      auto s_iter = states_.find(instance_id);
      apps::Instance* state =
          (s_iter != states_.end()) ? s_iter->second.get() : nullptr;
      if (state) {
        f(apps::InstanceUpdate(state, nullptr));
      }
    }
    return true;
  }

 private:
  friend class InstanceRegistryTest;

  void DoOnInstance(InstancePtr deltas);

  void MaybeRemoveInstance(const Instance* delta);

  void MaybeRemoveInstanceId(const base::UnguessableToken& instance_id,
                             aura::Window* window);

  base::ObserverList<Observer> observers_;

  // OnInstance calls DoOnInstance zero or more times. If we're nested,
  // in_progress is true, so that there's multiple OnInstance call to this
  // InstanceRegistry in the call stack, the deeper OnInstances call simply adds
  // work to deltas_pending_ and returns without calling DoOnInstance. If we're
  // not nested, in_progress is false, OnInstance calls DoOnInstance one or
  // more times; "more times" happens if DoOnInstance notifying observers leads
  // to more OnInstance calls that enqueue deltas_pending_ work.
  //
  // Nested OnInstance calls are expected to be rare (but still dealt with
  // sensibly). In the typical case, OnInstance should call DoOnInstance
  // exactly once, and deltas_pending_ will stay empty.
  bool in_progress_ = false;

  // Maps from the instance id to the latest state: the "sum" of all previous
  // deltas.
  std::map<const base::UnguessableToken, InstancePtr> states_;

  std::list<InstancePtr> deltas_pending_;

  // Maps from window to a set of instance id.
  std::map<const aura::Window*, InstanceIds> window_to_instance_ids_;

  // Maps from instance id to window, to check whether the window is changed for
  // the instance id. When a tab is pulled to a new Lacros window, the window
  // might be changed, and the instance id should be removed from
  // `window_to_instance_ids_`. `states_` can't be used to check window, because
  // some instances might be in `deltas_pending_`.
  std::map<const base::UnguessableToken, raw_ptr<aura::Window, CtnExperimental>>
      instance_id_to_window_;

  // Maps from app id to instances.
  std::map<const std::string,
           std::set<raw_ptr<const Instance, SetExperimental>>>
      app_id_to_instances_;

  std::unique_ptr<Instance> old_state_;

  SEQUENCE_CHECKER(my_sequence_checker_);
};

}  // namespace apps

#endif  // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_INSTANCE_REGISTRY_H_