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
ash / focus / ash_focus_rules.cc [blame]
// Copyright 2012 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/focus/ash_focus_rules.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/wm/container_finder.h"
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/float/float_controller.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/window_restore/window_restore_controller.h"
#include "ash/wm/window_state.h"
#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "components/app_restore/full_restore_utils.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/events/event.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
bool BelongsToContainerWithEqualOrGreaterId(const aura::Window* window,
int container_id) {
for (; window; window = window->parent()) {
if (window->GetId() >= container_id) {
return true;
}
}
return false;
}
bool BelongsToContainerWithId(const aura::Window* window, int container_id) {
for (; window; window = window->parent()) {
if (window->GetId() == container_id) {
return true;
}
}
return false;
}
bool IsInactiveDeskContainerId(int id) {
return desks_util::IsDeskContainerId(id) &&
id != desks_util::GetActiveDeskContainerId();
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// AshFocusRules, public:
AshFocusRules::AshFocusRules()
: activatable_container_ids_(GetActivatableShellWindowIds()) {}
AshFocusRules::~AshFocusRules() = default;
////////////////////////////////////////////////////////////////////////////////
// AshFocusRules, ::wm::FocusRules:
bool AshFocusRules::IsToplevelWindow(const aura::Window* window) const {
DCHECK(window);
// The window must be in a valid hierarchy.
if (!window->GetRootWindow() || !window->parent()) {
return false;
}
// The window must exist within a container that supports activation.
// The window cannot be blocked by a modal transient.
return base::Contains(activatable_container_ids_, window->parent()->GetId());
}
bool AshFocusRules::SupportsChildActivation(const aura::Window* window) const {
return base::Contains(activatable_container_ids_, window->GetId());
}
bool AshFocusRules::IsWindowConsideredVisibleForActivation(
const aura::Window* window) const {
DCHECK(window);
Shell* shell = Shell::Get();
// If the |window| doesn't belong to the current active user and also doesn't
// show for the current active user, then it should not be activated.
if (!shell->shell_delegate()->CanShowWindowForUser(window)) {
return false;
}
if (window->IsVisible()) {
return true;
}
const WindowState* window_state = WindowState::Get(window);
// Minimized windows are hidden in their minimized state, but they can always
// be activated.
if (window_state->IsMinimized()) {
return true;
}
if (window_state->IsFloated()) {
auto* float_controller = shell->float_controller();
// Floated windows are hidden if they belong to inactive desks, but they can
// always be activated.
if (float_controller->FindDeskOfFloatedWindow(window) !=
DesksController::Get()->active_desk()) {
return true;
}
// Tucked windows are hidden offscreen, but they can be activated.
if (float_controller->IsFloatedWindowTuckedForTablet(window)) {
return true;
}
}
if (!window->TargetVisibility()) {
return false;
}
const aura::Window* const parent = window->parent();
return desks_util::IsDeskContainer(parent) ||
parent->GetId() == kShellWindowId_LockScreenContainer;
}
bool AshFocusRules::CanActivateWindow(const aura::Window* window) const {
// Clearing activation is always permissible.
if (!window) {
return true;
}
if (!WindowRestoreController::CanActivateRestoredWindow(window)) {
return false;
}
// Special case during Full Restore that prevents the app list from being
// activated during tablet mode if the topmost window of any root window is a
// Full Restore'd window. See http://crbug/1202923.
if (!WindowRestoreController::CanActivateAppList(window)) {
return false;
}
if (!BaseFocusRules::CanActivateWindow(window)) {
return false;
}
// Special case to allow the login shelf to be activatable when the OOBE
// modal is visible. See http://crbug/871184
// TODO: remove this special case once login shelf is moved into a child
// widget of the lock screen (https://crbug.com/767235).
if (Shell::Get()->session_controller()->IsUserSessionBlocked() &&
BelongsToContainerWithId(window, kShellWindowId_ShelfContainer)) {
return true;
}
int modal_container_id = Shell::GetOpenSystemModalWindowContainerId();
if (modal_container_id >= 0) {
return BelongsToContainerWithEqualOrGreaterId(window, modal_container_id);
}
return true;
}
bool AshFocusRules::CanFocusWindow(const aura::Window* window,
const ui::Event* event) const {
if (!window) {
return true;
}
if (event && (event->IsMouseEvent() || event->IsGestureEvent()) &&
!window->GetProperty(aura::client::kActivateOnPointerKey)) {
return false;
}
return BaseFocusRules::CanFocusWindow(window, event);
}
aura::Window* AshFocusRules::GetNextActivatableWindow(
aura::Window* ignore) const {
DCHECK(ignore);
// If the window that lost activation should be ignored, no need to change
// window activation.
if (ignore->GetProperty(kIgnoreWindowActivationKey)) {
return nullptr;
}
// If the window that just lost focus |ignore| has a transient parent, then
// start from the container of that parent, otherwise start from the container
// of the most-recently-used window. If the list of MRU windows is empty, then
// start from the container of |ignore|.
aura::Window* starting_window = nullptr;
aura::Window* transient_parent = ::wm::GetTransientParent(ignore);
OverviewController* overview_controller = Shell::Get()->overview_controller();
// It's possible for this to be called either on shutdown or when a session is
// not yet active, so we need to check for the existence of the overview
// controller.
if (overview_controller && overview_controller->InOverviewSession() &&
overview_controller->overview_session()->IsSavedDeskUiLosingActivation(
ignore)) {
starting_window =
overview_controller->overview_session()->GetOverviewFocusWindow();
} else if (transient_parent) {
starting_window = transient_parent;
} else {
MruWindowTracker* mru = Shell::Get()->mru_window_tracker();
aura::Window::Windows windows = mru->BuildMruWindowList(kActiveDesk);
starting_window = windows.empty() ? ignore : windows[0].get();
}
DCHECK(starting_window);
// Look for windows to focus in |starting_window|'s container. If none are
// found, we look in all the containers in front of |starting_window|'s
// container, then all behind.
int starting_container_index = 0;
aura::Window* root = starting_window->GetRootWindow();
if (!root) {
root = Shell::GetRootWindowForNewWindows();
}
const int container_count = activatable_container_ids_.size();
for (int i = 0; i < container_count; i++) {
aura::Window* container =
Shell::GetContainer(root, activatable_container_ids_[i]);
if (container && container->Contains(starting_window)) {
starting_container_index = i;
break;
}
}
aura::Window* window = nullptr;
for (int i = starting_container_index; !window && i < container_count; i++) {
window = GetTopmostWindowToActivateForContainerIndex(i, ignore, root);
}
if (!window && starting_container_index > 0) {
for (int i = starting_container_index - 1; !window && i >= 0; i--) {
window = GetTopmostWindowToActivateForContainerIndex(i, ignore, root);
}
}
if (window) {
DCHECK(!window->GetProperty(kIgnoreWindowActivationKey));
}
return window;
}
////////////////////////////////////////////////////////////////////////////////
// AshFocusRules, private:
aura::Window* AshFocusRules::GetTopmostWindowToActivateForContainerIndex(
int index,
aura::Window* ignore,
aura::Window* priority_root) const {
const int container_id = activatable_container_ids_[index];
// Inactive desk containers should be ignored, since windows in them should
// never be returned as a next activatable window.
if (IsInactiveDeskContainerId(container_id)) {
return nullptr;
}
aura::Window* window = nullptr;
aura::Window::Windows containers =
GetContainersForAllRootWindows(container_id, priority_root);
// Favor the top-most window (if any) on `priority_root`, since
// `GetContainersForAllRootWindows()` will put the container belonging to
// `priority_root` first.
for (aura::Window* container : containers) {
window = GetTopmostWindowToActivateInContainer(container, ignore);
if (window) {
return window;
}
}
return window;
}
aura::Window* AshFocusRules::GetTopmostWindowToActivateInContainer(
aura::Window* container,
aura::Window* ignore) const {
for (aura::Window* child : base::Reversed(container->children())) {
WindowState* window_state = WindowState::Get(child);
// A floated window should not be activatable if it's hidden on an inactive
// desk.
if (child != ignore && window_state->CanActivate() &&
!window_state->IsMinimized() &&
!(window_state->IsFloated() && !child->IsVisible()) &&
!child->GetProperty(kIgnoreWindowActivationKey)) {
return child;
}
}
return nullptr;
}
} // namespace ash