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