1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/wm/window_util.h"
6
7 #include <memory>
8
9 #include "ash/public/cpp/app_types.h"
10 #include "ash/public/cpp/ash_features.h"
11 #include "ash/public/cpp/shell_window_ids.h"
12 #include "ash/public/cpp/tablet_mode_observer.h"
13 #include "ash/public/cpp/window_properties.h"
14 #include "ash/root_window_controller.h"
15 #include "ash/scoped_animation_disabler.h"
16 #include "ash/screen_util.h"
17 #include "ash/session/session_controller_impl.h"
18 #include "ash/shelf/shelf.h"
19 #include "ash/shell.h"
20 #include "ash/shell_delegate.h"
21 #include "ash/wm/mru_window_tracker.h"
22 #include "ash/wm/overview/overview_controller.h"
23 #include "ash/wm/overview/overview_session.h"
24 #include "ash/wm/splitview/split_view_controller.h"
25 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
26 #include "ash/wm/window_positioning_utils.h"
27 #include "ash/wm/window_state.h"
28 #include "ash/wm/wm_event.h"
29 #include "chromeos/ui/base/chromeos_ui_constants.h"
30 #include "ui/aura/client/aura_constants.h"
31 #include "ui/aura/client/capture_client.h"
32 #include "ui/aura/client/focus_client.h"
33 #include "ui/aura/window.h"
34 #include "ui/aura/window_delegate.h"
35 #include "ui/aura/window_event_dispatcher.h"
36 #include "ui/aura/window_targeter.h"
37 #include "ui/base/hit_test.h"
38 #include "ui/compositor/layer_tree_owner.h"
39 #include "ui/display/display.h"
40 #include "ui/display/screen.h"
41 #include "ui/gfx/geometry/rect.h"
42 #include "ui/gfx/geometry/size.h"
43 #include "ui/gfx/transform_util.h"
44 #include "ui/views/view.h"
45 #include "ui/views/widget/widget.h"
46 #include "ui/wm/core/coordinate_conversion.h"
47 #include "ui/wm/core/easy_resize_window_targeter.h"
48 #include "ui/wm/core/window_animations.h"
49 #include "ui/wm/public/activation_client.h"
50
51 namespace ash {
52 namespace window_util {
53 namespace {
54
55 // This window targeter reserves space for the portion of the resize handles
56 // that extend within a top level window.
57 class InteriorResizeHandleTargeter : public aura::WindowTargeter {
58 public:
InteriorResizeHandleTargeter()59 InteriorResizeHandleTargeter() {
60 SetInsets(gfx::Insets(chromeos::kResizeInsideBoundsSize));
61 }
62
63 ~InteriorResizeHandleTargeter() override = default;
64
GetHitTestRects(aura::Window * target,gfx::Rect * hit_test_rect_mouse,gfx::Rect * hit_test_rect_touch) const65 bool GetHitTestRects(aura::Window* target,
66 gfx::Rect* hit_test_rect_mouse,
67 gfx::Rect* hit_test_rect_touch) const override {
68 if (target == window() && window()->parent() &&
69 window()->parent()->targeter()) {
70 // Defer to the parent's targeter on whether |window_| should be able to
71 // receive the event. This should be EasyResizeWindowTargeter, which is
72 // installed on the container window, and is necessary for
73 // kResizeOutsideBoundsSize to work.
74 return window()->parent()->targeter()->GetHitTestRects(
75 target, hit_test_rect_mouse, hit_test_rect_touch);
76 }
77
78 return WindowTargeter::GetHitTestRects(target, hit_test_rect_mouse,
79 hit_test_rect_touch);
80 }
81
ShouldUseExtendedBounds(const aura::Window * target) const82 bool ShouldUseExtendedBounds(const aura::Window* target) const override {
83 // Fullscreen/maximized windows can't be drag-resized.
84 const WindowState* window_state = WindowState::Get(window());
85 if (window_state && window_state->IsMaximizedOrFullscreenOrPinned())
86 return false;
87 // The shrunken hit region only applies to children of |window()|.
88 return target->parent() == window();
89 }
90
91 private:
92 DISALLOW_COPY_AND_ASSIGN(InteriorResizeHandleTargeter);
93 };
94
95 } // namespace
96
GetActiveWindow()97 aura::Window* GetActiveWindow() {
98 return ::wm::GetActivationClient(Shell::GetPrimaryRootWindow())
99 ->GetActiveWindow();
100 }
101
GetFocusedWindow()102 aura::Window* GetFocusedWindow() {
103 return aura::client::GetFocusClient(Shell::GetPrimaryRootWindow())
104 ->GetFocusedWindow();
105 }
106
GetCaptureWindow()107 aura::Window* GetCaptureWindow() {
108 return aura::client::GetCaptureWindow(Shell::GetPrimaryRootWindow());
109 }
110
GetBlockingContainersForRoot(aura::Window * root_window,aura::Window ** min_container,aura::Window ** system_modal_container)111 void GetBlockingContainersForRoot(aura::Window* root_window,
112 aura::Window** min_container,
113 aura::Window** system_modal_container) {
114 if (Shell::Get()->session_controller()->IsUserSessionBlocked()) {
115 *min_container =
116 root_window->GetChildById(kShellWindowId_LockScreenContainersContainer);
117 *system_modal_container =
118 root_window->GetChildById(kShellWindowId_LockSystemModalContainer);
119 } else {
120 *min_container = nullptr;
121 *system_modal_container =
122 root_window->GetChildById(kShellWindowId_SystemModalContainer);
123 }
124 }
125
IsWindowUserPositionable(aura::Window * window)126 bool IsWindowUserPositionable(aura::Window* window) {
127 return window->type() == aura::client::WINDOW_TYPE_NORMAL;
128 }
129
PinWindow(aura::Window * window,bool trusted)130 void PinWindow(aura::Window* window, bool trusted) {
131 WMEvent event(trusted ? WM_EVENT_TRUSTED_PIN : WM_EVENT_PIN);
132 WindowState::Get(window)->OnWMEvent(&event);
133 }
134
SetAutoHideShelf(aura::Window * window,bool autohide)135 void SetAutoHideShelf(aura::Window* window, bool autohide) {
136 WindowState::Get(window)->set_autohide_shelf_when_maximized_or_fullscreen(
137 autohide);
138 for (aura::Window* root_window : Shell::GetAllRootWindows())
139 Shelf::ForWindow(root_window)->UpdateVisibilityState();
140 }
141
MoveWindowToDisplay(aura::Window * window,int64_t display_id)142 bool MoveWindowToDisplay(aura::Window* window, int64_t display_id) {
143 DCHECK(window);
144
145 aura::Window* root = Shell::GetRootWindowForDisplayId(display_id);
146 if (!root || root == window->GetRootWindow()) {
147 NOTREACHED();
148 return false;
149 }
150
151 WindowState* window_state = WindowState::Get(window);
152 if (window_state->allow_set_bounds_direct()) {
153 display::Display display;
154 if (!display::Screen::GetScreen()->GetDisplayWithDisplayId(display_id,
155 &display))
156 return false;
157 gfx::Rect bounds = window->bounds();
158 gfx::Rect work_area_in_display(display.size());
159 work_area_in_display.Inset(display.GetWorkAreaInsets());
160 AdjustBoundsToEnsureMinimumWindowVisibility(work_area_in_display, &bounds);
161 SetBoundsWMEvent event(bounds, display_id);
162 window_state->OnWMEvent(&event);
163 return true;
164 }
165
166 // Moves |window| to the given |root| window's corresponding container.
167 aura::Window* container = RootWindowController::ForWindow(root)->GetContainer(
168 window->parent()->id());
169 if (!container)
170 return false;
171
172 // Update restore bounds to target root window.
173 if (window_state->HasRestoreBounds()) {
174 gfx::Rect restore_bounds = window_state->GetRestoreBoundsInParent();
175 ::wm::ConvertRectToScreen(root, &restore_bounds);
176 window_state->SetRestoreBoundsInScreen(restore_bounds);
177 }
178
179 container->AddChild(window);
180 return true;
181 }
182
GetNonClientComponent(aura::Window * window,const gfx::Point & location)183 int GetNonClientComponent(aura::Window* window, const gfx::Point& location) {
184 return window->delegate()
185 ? window->delegate()->GetNonClientComponent(location)
186 : HTNOWHERE;
187 }
188
SetChildrenUseExtendedHitRegionForWindow(aura::Window * window)189 void SetChildrenUseExtendedHitRegionForWindow(aura::Window* window) {
190 gfx::Insets mouse_extend(
191 -chromeos::kResizeOutsideBoundsSize, -chromeos::kResizeOutsideBoundsSize,
192 -chromeos::kResizeOutsideBoundsSize, -chromeos::kResizeOutsideBoundsSize);
193 gfx::Insets touch_extend = gfx::ScaleToFlooredInsets(
194 mouse_extend, chromeos::kResizeOutsideBoundsScaleForTouch);
195 window->SetEventTargeter(std::make_unique<::wm::EasyResizeWindowTargeter>(
196 mouse_extend, touch_extend));
197 }
198
CloseWidgetForWindow(aura::Window * window)199 void CloseWidgetForWindow(aura::Window* window) {
200 views::Widget* widget = views::Widget::GetWidgetForNativeView(window);
201 DCHECK(widget);
202 widget->Close();
203 }
204
InstallResizeHandleWindowTargeterForWindow(aura::Window * window)205 void InstallResizeHandleWindowTargeterForWindow(aura::Window* window) {
206 window->SetEventTargeter(std::make_unique<InteriorResizeHandleTargeter>());
207 }
208
IsDraggingTabs(const aura::Window * window)209 bool IsDraggingTabs(const aura::Window* window) {
210 return window->GetProperty(ash::kIsDraggingTabsKey);
211 }
212
ShouldExcludeForCycleList(const aura::Window * window)213 bool ShouldExcludeForCycleList(const aura::Window* window) {
214 // Exclude windows:
215 // - non user positionable windows, such as extension popups.
216 // - windows being dragged
217 // - pip windows
218 const WindowState* state = WindowState::Get(window);
219 if (!state->IsUserPositionable() || state->is_dragged() || state->IsPip())
220 return true;
221
222 // Exclude the AppList window, which will hide as soon as cycling starts
223 // anyway. It doesn't make sense to count it as a "switchable" window, yet
224 // a lot of code relies on the MRU list returning the app window. If we
225 // don't manually remove it, the window cycling UI won't crash or misbehave,
226 // but there will be a flicker as the target window changes. Also exclude
227 // unselectable windows such as extension popups.
228 for (auto* parent = window->parent(); parent; parent = parent->parent()) {
229 if (parent->id() == kShellWindowId_AppListContainer)
230 return true;
231 }
232
233 return window->GetProperty(kHideInOverviewKey);
234 }
235
ShouldExcludeForOverview(const aura::Window * window)236 bool ShouldExcludeForOverview(const aura::Window* window) {
237 // If we're currently in tablet splitview, remove the default snapped window
238 // from the window list. The default snapped window occupies one side of the
239 // screen, while the other windows occupy the other side of the screen in
240 // overview mode. The default snap position is the position where the window
241 // was first snapped. See |default_snap_position_| in SplitViewController for
242 // more detail.
243 auto* split_view_controller =
244 SplitViewController::Get(Shell::GetPrimaryRootWindow());
245 if (split_view_controller->InTabletSplitViewMode() &&
246 window == split_view_controller->GetDefaultSnappedWindow()) {
247 return true;
248 }
249
250 // Remove everything cycle list should not have.
251 return ShouldExcludeForCycleList(window);
252 }
253
EnsureTransientRoots(std::vector<aura::Window * > * out_window_list)254 void EnsureTransientRoots(std::vector<aura::Window*>* out_window_list) {
255 for (auto it = out_window_list->begin(); it != out_window_list->end();) {
256 aura::Window* transient_root = ::wm::GetTransientRoot(*it);
257 if (*it != transient_root) {
258 if (base::Contains(*out_window_list, transient_root)) {
259 it = out_window_list->erase(it);
260 } else {
261 *it = transient_root;
262 ++it;
263 }
264 } else {
265 ++it;
266 }
267 }
268 }
269
MinimizeAndHideWithoutAnimation(const std::vector<aura::Window * > & windows)270 void MinimizeAndHideWithoutAnimation(
271 const std::vector<aura::Window*>& windows) {
272 for (auto* window : windows) {
273 ScopedAnimationDisabler disable(window);
274
275 // ARC windows are minimized asynchronously, so we hide them after
276 // minimization. We minimize ARC windows first so they receive occlusion
277 // updates before losing focus from being hidden. See crbug.com/910304.
278 // TODO(oshima): Investigate better way to handle ARC apps immediately.
279 WindowState::Get(window)->Minimize();
280
281 window->Hide();
282 }
283 if (windows.size()) {
284 // Disable the animations using |disable|. However, doing so will skip
285 // detaching the resources associated with the layer. So we have to trick
286 // the compositor into releasing the resources.
287 // crbug.com/924802.
288 auto* compositor = windows[0]->layer()->GetCompositor();
289 bool was_visible = compositor->IsVisible();
290 compositor->SetVisible(false);
291 compositor->SetVisible(was_visible);
292 }
293 }
294
GetRootWindowAt(const gfx::Point & point_in_screen)295 aura::Window* GetRootWindowAt(const gfx::Point& point_in_screen) {
296 const display::Display& display =
297 display::Screen::GetScreen()->GetDisplayNearestPoint(point_in_screen);
298 DCHECK(display.is_valid());
299 RootWindowController* root_window_controller =
300 Shell::GetRootWindowControllerWithDisplayId(display.id());
301 return root_window_controller ? root_window_controller->GetRootWindow()
302 : nullptr;
303 }
304
GetRootWindowMatching(const gfx::Rect & rect_in_screen)305 aura::Window* GetRootWindowMatching(const gfx::Rect& rect_in_screen) {
306 const display::Display& display =
307 display::Screen::GetScreen()->GetDisplayMatching(rect_in_screen);
308 RootWindowController* root_window_controller =
309 Shell::GetRootWindowControllerWithDisplayId(display.id());
310 return root_window_controller ? root_window_controller->GetRootWindow()
311 : nullptr;
312 }
313
IsArcWindow(const aura::Window * window)314 bool IsArcWindow(const aura::Window* window) {
315 return window->GetProperty(aura::client::kAppType) ==
316 static_cast<int>(ash::AppType::ARC_APP);
317 }
318
IsArcPipWindow(const aura::Window * window)319 bool IsArcPipWindow(const aura::Window* window) {
320 return IsArcWindow(window) && WindowState::Get(window)->IsPip();
321 }
322
ExpandArcPipWindow()323 void ExpandArcPipWindow() {
324 auto* pip_container = Shell::GetContainer(Shell::GetPrimaryRootWindow(),
325 kShellWindowId_PipContainer);
326 if (!pip_container)
327 return;
328
329 auto pip_window_iter =
330 std::find_if(pip_container->children().begin(),
331 pip_container->children().end(), IsArcPipWindow);
332 if (pip_window_iter == pip_container->children().end())
333 return;
334
335 auto* window_state = WindowState::Get(*pip_window_iter);
336 window_state->Restore();
337 }
338
IsAnyWindowDragged()339 bool IsAnyWindowDragged() {
340 OverviewController* overview_controller = Shell::Get()->overview_controller();
341 if (overview_controller->InOverviewSession() &&
342 overview_controller->overview_session()
343 ->GetCurrentDraggedOverviewItem()) {
344 return true;
345 }
346
347 for (aura::Window* window :
348 Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk)) {
349 if (WindowState::Get(window)->is_dragged())
350 return true;
351 }
352 return false;
353 }
354
GetTopWindow()355 aura::Window* GetTopWindow() {
356 MruWindowTracker::WindowList windows =
357 Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kActiveDesk);
358
359 return windows.empty() ? nullptr : windows[0];
360 }
361
ShouldMinimizeTopWindowOnBack()362 bool ShouldMinimizeTopWindowOnBack() {
363 Shell* shell = Shell::Get();
364 if (!shell->tablet_mode_controller()->InTabletMode())
365 return false;
366
367 aura::Window* window = GetTopWindow();
368 if (!window)
369 return false;
370
371 // Do not minimize the window if it is in overview. This can avoid unnecessary
372 // window minimize animation.
373 OverviewController* overview_controller = Shell::Get()->overview_controller();
374 if (overview_controller->InOverviewSession() &&
375 overview_controller->overview_session()->IsWindowInOverview(window)) {
376 return false;
377 }
378
379 // ARC and crostini apps will handle the back event that follows on the client
380 // side and will minimize/close the window there.
381 const int app_type = window->GetProperty(aura::client::kAppType);
382 if (app_type == static_cast<int>(AppType::ARC_APP) ||
383 app_type == static_cast<int>(AppType::CROSTINI_APP)) {
384 return false;
385 }
386
387 // Use the value of |kMinimizeOnBackKey| if it is provided. It can be provided
388 // by windows with custom web contents.
389 bool* can_minimize_on_back_key = window->GetProperty(kMinimizeOnBackKey);
390 if (can_minimize_on_back_key)
391 return *can_minimize_on_back_key;
392
393 // Minimize the window if it is at the bottom page.
394 return !shell->shell_delegate()->CanGoBack(window);
395 }
396
SendBackKeyEvent(aura::Window * root_window)397 void SendBackKeyEvent(aura::Window* root_window) {
398 // Send up event as well as down event as ARC++ clients expect this
399 // sequence.
400 // TODO: Investigate if we should be using the current modifiers.
401 ui::KeyEvent press_key_event(ui::ET_KEY_PRESSED, ui::VKEY_BROWSER_BACK,
402 ui::EF_NONE);
403 ignore_result(root_window->GetHost()->SendEventToSink(&press_key_event));
404 ui::KeyEvent release_key_event(ui::ET_KEY_RELEASED, ui::VKEY_BROWSER_BACK,
405 ui::EF_NONE);
406 ignore_result(root_window->GetHost()->SendEventToSink(&release_key_event));
407 }
408
GetVisibleTransientTreeIterator(aura::Window * window)409 WindowTransientDescendantIteratorRange GetVisibleTransientTreeIterator(
410 aura::Window* window) {
411 auto hide_predicate = [](aura::Window* window) {
412 return window->GetProperty(kHideInOverviewKey);
413 };
414 return GetTransientTreeIterator(window, base::BindRepeating(hide_predicate));
415 }
416
GetTransformedBounds(aura::Window * transformed_window,int top_inset)417 gfx::RectF GetTransformedBounds(aura::Window* transformed_window,
418 int top_inset) {
419 gfx::RectF bounds;
420 for (auto* window : GetVisibleTransientTreeIterator(transformed_window)) {
421 // Ignore other window types when computing bounding box of overview target
422 // item.
423 if (window != transformed_window &&
424 window->type() != aura::client::WINDOW_TYPE_NORMAL) {
425 continue;
426 }
427 gfx::RectF window_bounds(window->GetTargetBounds());
428 gfx::Transform new_transform =
429 TransformAboutPivot(gfx::ToRoundedPoint(window_bounds.origin()),
430 window->layer()->GetTargetTransform());
431 new_transform.TransformRect(&window_bounds);
432
433 // The preview title is shown above the preview window. Hide the window
434 // header for apps or browser windows with no tabs (web apps) to avoid
435 // showing both the window header and the preview title.
436 if (top_inset > 0) {
437 gfx::RectF header_bounds(window_bounds);
438 header_bounds.set_height(top_inset);
439 new_transform.TransformRect(&header_bounds);
440 window_bounds.Inset(0, header_bounds.height(), 0, 0);
441 }
442 ::wm::TranslateRectToScreen(window->parent(), &window_bounds);
443 bounds.Union(window_bounds);
444 }
445 return bounds;
446 }
447
448 } // namespace window_util
449 } // namespace ash
450