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