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
ash / components / arc / window / arc_window_watcher.cc [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/components/arc/window/arc_window_watcher.h"
#include <algorithm>
#include "ash/public/cpp/app_types_util.h"
#include "ash/public/cpp/window_properties.h"
#include "base/memory/raw_ptr.h"
#include "chromeos/ui/base/app_types.h"
#include "chromeos/ui/base/window_properties.h"
#include "ui/aura/window.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
class Tracker : public aura::WindowObserver {
public:
explicit Tracker(aura::Window* window) : window_(window) {
window->AddObserver(this);
// If the app type of `window` is ARC, add it to `arc_window_`. Note that
// the app type might be not set yet at this point. In such case, `window`
// will be set to `arc_window_` when kAppTyeKey property is updated.
if (ash::IsArcWindow(window)) {
arc_window_ = window;
ash::ArcWindowWatcher::instance()->OnArcWindowAdded();
}
}
Tracker(const Tracker&) = delete;
Tracker& operator=(const Tracker&) = delete;
~Tracker() override { window_->RemoveObserver(this); }
void OnPackageNameChanged() {
if (display_reported_) {
// Must not do this more than once
return;
}
const auto* pkg_name = arc_window_->GetProperty(ash::kArcPackageNameKey);
if (!pkg_name || pkg_name->empty()) {
return;
}
display_reported_ = true;
ash::ArcWindowWatcher::instance()->BroadcastArcWindowDisplay(*pkg_name);
}
// aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override {
ash::ArcWindowWatcher::instance()->OnTrackerRemoved(
this, arc_window_ /*may be nullptr*/);
// WARNING: this is deleted here - must return immediately.
}
void OnWindowPropertyChanged(aura::Window* window,
const void* key,
intptr_t old) override {
CHECK_EQ(window_, window);
if (arc_window_) {
if (key == ash::kArcPackageNameKey) {
OnPackageNameChanged();
}
return;
}
// No additional step needed if the updated property is not AppType.
if (key != chromeos::kAppTypeKey) {
return;
}
// We should ignore non-Arc window.
if (!ash::IsArcWindow(window)) {
ash::ArcWindowWatcher::instance()->OnTrackerRemoved(this, nullptr);
// WARNING: this is deleted here - must return immediately.
return;
}
// Set `window` to `arc_window_` if the app type just became ARC.
arc_window_ = window;
ash::ArcWindowWatcher::instance()->OnArcWindowAdded();
}
private:
raw_ptr<aura::Window> window_;
raw_ptr<aura::Window> arc_window_ =
nullptr; // set to window_ when we know it is ARC.
bool display_reported_ = false;
};
} // namespace
// static
ArcWindowWatcher* ArcWindowWatcher::instance_ = nullptr;
ArcWindowWatcher::ArcWindowWatcher() {
DCHECK(!instance_);
instance_ = this;
aura::Env::GetInstance()->AddObserver(this);
}
ArcWindowWatcher::~ArcWindowWatcher() {
DCHECK(instance_ == this);
// Stop observing Env, to ensure no new trackers are created.
aura::Env::GetInstance()->RemoveObserver(this);
// Then remove all existing trackers in one shot.
trackers_.clear();
// Tell observers, so they have a chance to un-subscribe.
for (auto& observer : arc_window_display_observers_) {
observer.OnWillDestroyWatcher();
}
for (auto& observer : arc_window_count_observers_) {
observer.OnWillDestroyWatcher();
}
instance_ = nullptr;
}
uint32_t ArcWindowWatcher::GetArcWindowCount() const {
return arc_window_count_;
}
void ArcWindowWatcher::OnWindowInitialized(aura::Window* window) {
// Filter a large set of controls that cannot be ARC windows.
if (window->GetType() != aura::client::WINDOW_TYPE_NORMAL ||
!window->delegate()) {
return;
}
views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
if (!widget || !widget->is_top_level()) {
return;
}
trackers_.push_back(std::make_unique<Tracker>(window));
}
// This is the main "plus" point, where we know an ARC window is born.
void ArcWindowWatcher::OnArcWindowAdded() {
++arc_window_count_;
BroadcastArcWindowCount(arc_window_count_);
}
// This is the main "minus" point, where we know an ARC window is gone.
void ArcWindowWatcher::OnArcWindowRemoved() {
--arc_window_count_;
BroadcastArcWindowCount(arc_window_count_);
}
void ArcWindowWatcher::OnTrackerRemoved(Tracker* tracker,
aura::Window* arc_window) {
// Order N -- we opted for simplicit of code rather than fastest performance,
// as the number of elements is small (number of top windows on the system).
// alternative considered: keep iterator when adding, and use a container
// such as list, where elements can be removed from the middle with order 1.
// Note that this would increase the # of dynamic allocations and fragment
// memory.
auto tracker_iterator =
std::find_if(trackers_.begin(), trackers_.end(),
[tracker](const auto& e) { return e.get() == tracker; });
DCHECK(tracker_iterator != trackers_.end());
trackers_.erase(tracker_iterator);
if (arc_window) {
OnArcWindowRemoved();
}
}
// Manage the list of arc-window-count observers.
void ArcWindowWatcher::AddObserver(ArcWindowCountObserver* observer) {
arc_window_count_observers_.AddObserver(observer);
}
void ArcWindowWatcher::RemoveObserver(ArcWindowCountObserver* observer) {
arc_window_count_observers_.RemoveObserver(observer);
}
void ArcWindowWatcher::BroadcastArcWindowCount(uint32_t count) {
for (auto& observer : arc_window_count_observers_) {
observer.OnArcWindowCountChanged(count);
}
}
// Manage the list of arc-window-display observers.
void ArcWindowWatcher::AddObserver(ArcWindowDisplayObserver* observer) {
arc_window_display_observers_.AddObserver(observer);
}
void ArcWindowWatcher::RemoveObserver(ArcWindowDisplayObserver* observer) {
arc_window_display_observers_.RemoveObserver(observer);
}
void ArcWindowWatcher::BroadcastArcWindowDisplay(const std::string& pkg_name) {
for (auto& observer : arc_window_display_observers_) {
observer.OnArcWindowDisplayed(pkg_name);
}
}
bool ArcWindowWatcher::HasCountObserver(
ArcWindowCountObserver* observer) const {
return arc_window_count_observers_.HasObserver(observer);
}
bool ArcWindowWatcher::HasDisplayObserver(
ArcWindowDisplayObserver* observer) const {
return arc_window_display_observers_.HasObserver(observer);
}
} // namespace ash