blob: 359fa181309d3755bd076e44eccc384663f039cd [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/wm/window_util.h"
#include <memory>
#include "ash/public/cpp/ash_constants.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/root_window_controller.h"
#include "ash/scoped_animation_disabler.h"
#include "ash/session/session_controller.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/widget_finder.h"
#include "ash/wm/window_positioning_utils.h"
#include "ash/wm/window_state.h"
#include "ash/wm/wm_event.h"
#include "ash/ws/window_service_owner.h"
#include "services/ws/window_service.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_targeter.h"
#include "ui/base/hit_test.h"
#include "ui/compositor/dip_util.h"
#include "ui/compositor/layer_tree_owner.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/easy_resize_window_targeter.h"
#include "ui/wm/core/window_animations.h"
#include "ui/wm/core/window_properties.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/public/activation_client.h"
namespace ash {
namespace wm {
namespace {
// Moves |window| to the given |root| window's corresponding container, if it is
// not already in the same root window. Returns true if |window| was moved.
bool MoveWindowToRoot(aura::Window* window, aura::Window* root) {
if (!root || root == window->GetRootWindow())
return false;
aura::Window* container = RootWindowController::ForWindow(root)->GetContainer(
if (!container)
return false;
return true;
// Asks the remote client that owns |window| to close it. Returns true if there
// was a remote client for |window|, false otherwise.
bool AskRemoteClientToCloseWindow(aura::Window* window) {
ws::WindowService* window_service =
return window_service && window_service->RequestClose(window);
// This window targeter reserves space for the portion of the resize handles
// that extend within a top level window.
class InteriorResizeHandleTargeter : public aura::WindowTargeter {
InteriorResizeHandleTargeter() {
~InteriorResizeHandleTargeter() override = default;
bool GetHitTestRects(aura::Window* target,
gfx::Rect* hit_test_rect_mouse,
gfx::Rect* hit_test_rect_touch) const override {
if (target == window() && window()->parent() &&
window()->parent()->targeter()) {
// Defer to the parent's targeter on whether |window_| should be able to
// receive the event. This should be EasyResizeWindowTargeter, which is
// installed on the container window, and is necessary for
// kResizeOutsideBoundsSize to work.
return window()->parent()->targeter()->GetHitTestRects(
target, hit_test_rect_mouse, hit_test_rect_touch);
return WindowTargeter::GetHitTestRects(target, hit_test_rect_mouse,
bool ShouldUseExtendedBounds(const aura::Window* target) const override {
// The shrunken hit region only applies to children of |window()|.
return target->parent() == window();
} // namespace
// TODO(beng): replace many of these functions with the corewm versions.
void ActivateWindow(aura::Window* window) {
void DeactivateWindow(aura::Window* window) {
bool IsActiveWindow(aura::Window* window) {
return ::wm::IsActiveWindow(window);
aura::Window* GetActiveWindow() {
return ::wm::GetActivationClient(Shell::GetPrimaryRootWindow())
aura::Window* GetActivatableWindow(aura::Window* window) {
return ::wm::GetActivatableWindow(window);
bool CanActivateWindow(aura::Window* window) {
return ::wm::CanActivateWindow(window);
aura::Window* GetFocusedWindow() {
return aura::client::GetFocusClient(Shell::GetPrimaryRootWindow())
aura::Window* GetCaptureWindow() {
return aura::client::GetCaptureWindow(Shell::GetPrimaryRootWindow());
void GetBlockingContainersForRoot(aura::Window* root_window,
aura::Window** min_container,
aura::Window** system_modal_container) {
if (Shell::Get()->session_controller()->IsUserSessionBlocked()) {
*min_container =
*system_modal_container =
} else {
*min_container = nullptr;
*system_modal_container =
bool IsWindowUserPositionable(aura::Window* window) {
return window->type() == aura::client::WINDOW_TYPE_NORMAL;
void PinWindow(aura::Window* window, bool trusted) {
wm::WMEvent event(trusted ? wm::WM_EVENT_TRUSTED_PIN : wm::WM_EVENT_PIN);
void SetAutoHideShelf(aura::Window* window, bool autohide) {
for (aura::Window* root_window : Shell::GetAllRootWindows())
bool MoveWindowToDisplay(aura::Window* window, int64_t display_id) {
WindowState* window_state = GetWindowState(window);
if (window_state->allow_set_bounds_direct()) {
aura::Window* root = Shell::GetRootWindowForDisplayId(display_id);
if (root) {
gfx::Rect bounds = window->bounds();
MoveWindowToRoot(window, root);
// Client controlled won't update the bounds upon the root window
// Change. Explicitly update the bounds so that the client can
// make decision.
return true;
return false;
aura::Window* root = Shell::GetRootWindowForDisplayId(display_id);
// Update restore bounds to target root window.
if (window_state->HasRestoreBounds()) {
gfx::Rect restore_bounds = window_state->GetRestoreBoundsInParent();
::wm::ConvertRectToScreen(root, &restore_bounds);
return root && MoveWindowToRoot(window, root);
bool MoveWindowToEventRoot(aura::Window* window, const ui::Event& event) {
views::View* target = static_cast<views::View*>(;
if (!target)
return false;
aura::Window* root = target->GetWidget()->GetNativeView()->GetRootWindow();
return root && MoveWindowToRoot(window, root);
void SetSnapsChildrenToPhysicalPixelBoundary(aura::Window* container) {
<< container->GetName();
container->SetProperty(::wm::kSnapChildrenToPixelBoundary, true);
int GetNonClientComponent(aura::Window* window, const gfx::Point& location) {
return window->delegate()
? window->delegate()->GetNonClientComponent(location)
void SetChildrenUseExtendedHitRegionForWindow(aura::Window* window) {
gfx::Insets mouse_extend(-kResizeOutsideBoundsSize, -kResizeOutsideBoundsSize,
gfx::Insets touch_extend =
// TODO: EasyResizeWindowTargeter makes it so children get events outside
// their bounds. This only works in mash when mash is providing the non-client
// frame. Mus needs to support an api for the WindowManager that enables
// events to be dispatched to windows outside the windows bounds that this
// function calls into.
mouse_extend, touch_extend));
void CloseWidgetForWindow(aura::Window* window) {
if (AskRemoteClientToCloseWindow(window))
views::Widget* widget = GetInternalWidgetForWindow(window);
void InstallResizeHandleWindowTargeterForWindow(aura::Window* window) {
// For Mash, ServerWindows will override the event targeter with a
// ServerWindowTargeter, so make sure it knows about the resize insets.
bool IsDraggingTabs(const aura::Window* window) {
return window->GetProperty(ash::kIsDraggingTabsKey);
bool ShouldExcludeForBothCycleListAndOverview(const aura::Window* window) {
// Exclude windows:
// - non user positionable windows, such as extension popups.
// - windows being dragged
// - pip windows
const wm::WindowState* state = wm::GetWindowState(window);
if (!state->IsUserPositionable() || state->is_dragged() || state->IsPip())
return true;
return window->GetProperty(kHideInOverviewKey);
bool ShouldExcludeForCycleList(const aura::Window* window) {
// Exclude the AppList window, which will hide as soon as cycling starts
// anyway. It doesn't make sense to count it as a "switchable" window, yet
// a lot of code relies on the MRU list returning the app window. If we
// don't manually remove it, the window cycling UI won't crash or misbehave,
// but there will be a flicker as the target window changes. Also exclude
// unselectable windows such as extension popups.
// TODO(sammiequon): Investigate if this is needed.
for (auto* parent = window->parent(); parent; parent = parent->parent()) {
if (parent->id() == kShellWindowId_AppListContainer)
return true;
return ShouldExcludeForBothCycleListAndOverview(window);
bool ShouldExcludeForOverview(const aura::Window* window) {
// Remove the default snapped window from the window list. The default
// snapped window occupies one side of the screen, while the other windows
// occupy the other side of the screen in overview mode. The default snap
// position is the position where the window was first snapped. See
// |default_snap_position_| in SplitViewController for more detail.
if (Shell::Get()->IsSplitViewModeActive() &&
window ==
Shell::Get()->split_view_controller()->GetDefaultSnappedWindow()) {
return true;
return ShouldExcludeForBothCycleListAndOverview(window);
void RemoveTransientDescendants(std::vector<aura::Window*>* out_window_list) {
for (auto it = out_window_list->begin(); it != out_window_list->end();) {
aura::Window* transient_root = ::wm::GetTransientRoot(*it);
if (*it != transient_root &&
base::ContainsValue(*out_window_list, transient_root)) {
it = out_window_list->erase(it);
} else {
void HideAndMaybeMinimizeWithoutAnimation(std::vector<aura::Window*> windows,
bool minimize) {
for (auto* window : windows) {
ScopedAnimationDisabler disable(window);
// ARC windows are minimized asynchronously, so hide here now.
// TODO(oshima): Investigate better way to handle ARC apps immediately.
if (minimize)
if (windows.size()) {
// Disable the animations using |disable|. However, doing so will skip
// detaching the resources associated with the layer. So we have to trick
// the compositor into releasing the resources.
auto* compositor = windows[0]->layer()->GetCompositor();
bool was_visible = compositor->IsVisible();
} // namespace wm
} // namespace ash