blob: 2c3731f4857fa2aeec6f2022f0eb7b4631230bfd [file] [log] [blame]
// Copyright 2013 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/overview/overview_session.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "ash/accelerators/accelerator_controller_impl.h"
#include "ash/accelerators/exit_warning_handler.h"
#include "ash/accessibility/accessibility_controller.h"
#include "ash/accessibility/test_accessibility_controller_client.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/display/screen_orientation_controller_test_api.h"
#include "ash/drag_drop/drag_drop_controller.h"
#include "ash/public/cpp/app_types.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/fps_counter.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/screen_util.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_constants.h"
#include "ash/shelf/shelf_view_test_api.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/drag_window_resizer.h"
#include "ash/wm/overview/caption_container_view.h"
#include "ash/wm/overview/overview_constants.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_grid.h"
#include "ash/wm/overview/overview_item.h"
#include "ash/wm/overview/overview_utils.h"
#include "ash/wm/overview/overview_window_drag_controller.h"
#include "ash/wm/overview/rounded_label_widget.h"
#include "ash/wm/overview/rounded_rect_view.h"
#include "ash/wm/resize_shadow.h"
#include "ash/wm/resize_shadow_controller.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_divider.h"
#include "ash/wm/splitview/split_view_drag_indicators.h"
#include "ash/wm/splitview/split_view_utils.h"
#include "ash/wm/tablet_mode/tablet_mode_browser_window_drag_delegate.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_window_drag_controller.h"
#include "ash/wm/window_preview_view.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_event.h"
#include "ash/wm/workspace/workspace_window_resizer.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/window_types.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/hit_test.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/display/display_layout.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/transform.h"
#include "ui/gfx/transform_util.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/shadow_controller.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
constexpr const char kActiveWindowChangedFromOverview[] =
"WindowSelector_ActiveWindowChanged";
// Helper function to get the index of |child|, given its parent window
// |parent|.
int IndexOf(aura::Window* child, aura::Window* parent) {
aura::Window::Windows children = parent->children();
auto it = std::find(children.begin(), children.end(), child);
DCHECK(it != children.end());
return static_cast<int>(std::distance(children.begin(), it));
}
class TweenTester : public ui::LayerAnimationObserver {
public:
explicit TweenTester(aura::Window* window) : window_(window) {
window->layer()->GetAnimator()->AddObserver(this);
}
~TweenTester() override {
window_->layer()->GetAnimator()->RemoveObserver(this);
EXPECT_TRUE(will_animate_);
}
// ui::LayerAnimationObserver:
void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {}
void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {}
void OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) override {}
void OnAttachedToSequence(ui::LayerAnimationSequence* sequence) override {
ui::LayerAnimationObserver::OnAttachedToSequence(sequence);
if (!will_animate_) {
tween_type_ = sequence->FirstElement()->tween_type();
will_animate_ = true;
}
}
gfx::Tween::Type tween_type() const { return tween_type_; }
private:
gfx::Tween::Type tween_type_ = gfx::Tween::LINEAR;
aura::Window* window_;
bool will_animate_ = false;
DISALLOW_COPY_AND_ASSIGN(TweenTester);
};
} // namespace
// TODO(bruthig): Move all non-simple method definitions out of class
// declaration.
class OverviewSessionTest : public AshTestBase {
public:
OverviewSessionTest() = default;
~OverviewSessionTest() override = default;
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
aura::Env::GetInstance()->set_throttle_input_on_resize_for_testing(false);
shelf_view_test_api_ = std::make_unique<ShelfViewTestAPI>(
GetPrimaryShelf()->GetShelfViewForTesting());
shelf_view_test_api_->SetAnimationDuration(1);
ScopedOverviewTransformWindow::SetImmediateCloseForTests();
OverviewController::SetDoNotChangeWallpaperForTests();
FpsCounter::SetForceReportZeroAnimationForTest(true);
ash::PresentationTimeRecorder::SetReportPresentationTimeImmediatelyForTest(
true);
}
void TearDown() override {
ash::PresentationTimeRecorder::SetReportPresentationTimeImmediatelyForTest(
false);
FpsCounter::SetForceReportZeroAnimationForTest(false);
trace_names_.clear();
AshTestBase::TearDown();
}
// Enters tablet mode. Needed by tests that test dragging and or splitview,
// which are tablet mode only.
void EnterTabletMode() {
// Ensure calls to SetEnabledForTest complete.
base::RunLoop().RunUntilIdle();
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
base::RunLoop().RunUntilIdle();
}
bool WindowsOverlapping(aura::Window* window1, aura::Window* window2) {
const gfx::Rect window1_bounds = GetTransformedTargetBounds(window1);
const gfx::Rect window2_bounds = GetTransformedTargetBounds(window2);
return window1_bounds.Intersects(window2_bounds);
}
OverviewController* overview_controller() {
return Shell::Get()->overview_controller();
}
OverviewSession* overview_session() {
return overview_controller()->overview_session_.get();
}
void ToggleOverview(OverviewSession::EnterExitOverviewType type =
OverviewSession::EnterExitOverviewType::kNormal) {
if (overview_controller()->InOverviewSession())
overview_controller()->EndOverview(type);
else
overview_controller()->StartOverview(type);
}
gfx::Rect GetTransformedBounds(aura::Window* window) {
gfx::Rect bounds_in_screen = window->layer()->bounds();
::wm::ConvertRectToScreen(window->parent(), &bounds_in_screen);
gfx::RectF bounds(bounds_in_screen);
gfx::Transform transform(gfx::TransformAboutPivot(
gfx::ToFlooredPoint(bounds.origin()), window->layer()->transform()));
transform.TransformRect(&bounds);
return gfx::ToEnclosingRect(bounds);
}
gfx::Rect GetTransformedTargetBounds(aura::Window* window) {
gfx::Rect bounds_in_screen = window->layer()->GetTargetBounds();
::wm::ConvertRectToScreen(window->parent(), &bounds_in_screen);
gfx::RectF bounds(bounds_in_screen);
gfx::Transform transform(
gfx::TransformAboutPivot(gfx::ToFlooredPoint(bounds.origin()),
window->layer()->GetTargetTransform()));
transform.TransformRect(&bounds);
return gfx::ToEnclosingRect(bounds);
}
gfx::Rect GetTransformedBoundsInRootWindow(aura::Window* window) {
gfx::RectF bounds = gfx::RectF(gfx::SizeF(window->bounds().size()));
aura::Window* root = window->GetRootWindow();
CHECK(window->layer());
CHECK(root->layer());
gfx::Transform transform;
if (!window->layer()->GetTargetTransformRelativeTo(root->layer(),
&transform)) {
return gfx::Rect();
}
transform.TransformRect(&bounds);
return gfx::ToEnclosingRect(bounds);
}
void ClickWindow(aura::Window* window) {
ui::test::EventGenerator event_generator(window->GetRootWindow(), window);
event_generator.ClickLeftButton();
}
void SendKey(ui::KeyboardCode key, int flags = ui::EF_NONE) {
ui::test::EventGenerator event_generator(Shell::GetPrimaryRootWindow());
GetEventGenerator()->PressKey(key, flags);
GetEventGenerator()->ReleaseKey(key, flags);
}
bool InOverviewSession() {
return overview_controller()->InOverviewSession();
}
const std::vector<std::unique_ptr<OverviewItem>>& GetWindowItemsForRoot(
int index) {
return overview_session()->grid_list_[index]->window_list();
}
OverviewItem* GetWindowItemForWindow(int grid_index, aura::Window* window) {
const std::vector<std::unique_ptr<OverviewItem>>& windows =
GetWindowItemsForRoot(grid_index);
auto iter =
std::find_if(windows.cbegin(), windows.cend(),
[window](const std::unique_ptr<OverviewItem>& item) {
return item->Contains(window);
});
if (iter == windows.end())
return nullptr;
return iter->get();
}
OverviewItem* GetDropTarget(int grid_index) {
return overview_session()->grid_list_[grid_index]->GetDropTarget();
}
// Selects |window| in the active overview session by cycling through all
// windows in overview until it is found. Returns true if |window| was found,
// false otherwise.
bool SelectWindow(const aura::Window* window) {
if (GetSelectedWindow() == nullptr)
SendKey(ui::VKEY_TAB);
const aura::Window* start_window = GetSelectedWindow();
if (start_window == window)
return true;
do {
SendKey(ui::VKEY_TAB);
} while (GetSelectedWindow() != window &&
GetSelectedWindow() != start_window);
return GetSelectedWindow() == window;
}
const aura::Window* GetSelectedWindow() {
OverviewSession* os = overview_session();
OverviewItem* item =
os->grid_list_[os->selected_grid_index_]->SelectedWindow();
if (!item)
return nullptr;
return item->GetWindow();
}
bool selection_widget_active() {
OverviewSession* os = overview_session();
return os->grid_list_[os->selected_grid_index_]->is_selecting();
}
views::ImageButton* GetCloseButton(OverviewItem* item) {
return item->GetCloseButtonForTesting();
}
views::Label* GetLabelView(OverviewItem* item) {
return item->caption_container_view_->title_label();
}
RoundedRectView* GetBackdropView(OverviewItem* item) {
return item->caption_container_view_->backdrop_view();
}
wm::WindowPreviewView* GetPreviewView(OverviewItem* item) {
return item->caption_container_view_->preview_view();
}
// Tests that a window is contained within a given OverviewItem, and that both
// the window and its matching close button are within the same screen.
void CheckWindowAndCloseButtonInScreen(aura::Window* window,
OverviewItem* window_item) {
const gfx::Rect screen_bounds =
window_item->root_window()->GetBoundsInScreen();
EXPECT_TRUE(window_item->Contains(window));
EXPECT_TRUE(screen_bounds.Contains(GetTransformedTargetBounds(window)));
EXPECT_TRUE(screen_bounds.Contains(
GetCloseButton(window_item)->GetBoundsInScreen()));
}
void SetGridBounds(OverviewGrid* grid, const gfx::Rect& bounds) {
grid->bounds_ = bounds;
}
gfx::Rect GetGridBounds() {
if (overview_session())
return overview_session()->grid_list_[0]->bounds_;
return gfx::Rect();
}
views::Widget* item_widget(OverviewItem* item) {
return item->item_widget_.get();
}
const ScopedOverviewTransformWindow& transform_window(
OverviewItem* item) const {
return item->transform_window_;
}
bool HasMaskForItem(OverviewItem* item) const {
return !!item->transform_window_.mask_;
}
void CheckOverviewEnterExitHistogram(const char* trace,
std::vector<int>&& enter_counts,
std::vector<int>&& exit_counts) {
DCHECK(!base::Contains(trace_names_, trace)) << trace;
trace_names_.push_back(trace);
{
SCOPED_TRACE(trace + std::string(".Enter"));
CheckOverviewHistogram("Ash.Overview.AnimationSmoothness.Enter",
std::move(enter_counts));
}
{
SCOPED_TRACE(trace + std::string(".Exit"));
CheckOverviewHistogram("Ash.Overview.AnimationSmoothness.Exit",
std::move(exit_counts));
}
}
static void StubForTest(ExitWarningHandler* ewh) {
ewh->stub_timer_for_test_ = true;
}
static bool is_ui_shown(ExitWarningHandler* ewh) { return !!ewh->widget_; }
private:
void CheckOverviewHistogram(const char* histogram,
std::vector<int>&& counts) {
ASSERT_EQ(3u, counts.size());
// There should be no histogram for split view.
histograms_.ExpectTotalCount(histogram + std::string(".SplitView"), 0);
histograms_.ExpectTotalCount(histogram + std::string(".ClamshellMode"),
counts[0]);
histograms_.ExpectTotalCount(
histogram + std::string(".SingleClamshellMode"), counts[1]);
histograms_.ExpectTotalCount(histogram + std::string(".TabletMode"),
counts[2]);
}
std::unique_ptr<ShelfViewTestAPI> shelf_view_test_api_;
std::vector<std::string> trace_names_;
base::HistogramTester histograms_;
DISALLOW_COPY_AND_ASSIGN(OverviewSessionTest);
};
// Tests that an a11y alert is sent on entering overview mode.
TEST_F(OverviewSessionTest, A11yAlertOnOverviewMode) {
TestAccessibilityControllerClient client;
AccessibilityController* controller =
Shell::Get()->accessibility_controller();
controller->SetClient(client.CreateInterfacePtrAndBind());
std::unique_ptr<aura::Window> window(CreateTestWindow());
EXPECT_NE(mojom::AccessibilityAlert::WINDOW_OVERVIEW_MODE_ENTERED,
client.last_a11y_alert());
ToggleOverview();
controller->FlushMojoForTest();
EXPECT_EQ(mojom::AccessibilityAlert::WINDOW_OVERVIEW_MODE_ENTERED,
client.last_a11y_alert());
}
// Tests that there are no crashes when there is not enough screen space
// available to show all of the windows.
TEST_F(OverviewSessionTest, SmallDisplay) {
UpdateDisplay("3x1");
gfx::Rect bounds(0, 0, 1, 1);
std::unique_ptr<aura::Window> window1(CreateTestWindow(bounds));
std::unique_ptr<aura::Window> window2(CreateTestWindow(bounds));
std::unique_ptr<aura::Window> window3(CreateTestWindow(bounds));
std::unique_ptr<aura::Window> window4(CreateTestWindow(bounds));
window1->SetProperty(aura::client::kTopViewInset, 0);
window2->SetProperty(aura::client::kTopViewInset, 0);
window3->SetProperty(aura::client::kTopViewInset, 0);
window4->SetProperty(aura::client::kTopViewInset, 0);
ToggleOverview();
}
// Tests entering overview mode with two windows and selecting one by clicking.
TEST_F(OverviewSessionTest, Basic) {
// Overview disabled by default.
EXPECT_FALSE(InOverviewSession());
aura::Window* root_window = Shell::GetPrimaryRootWindow();
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
EXPECT_TRUE(WindowsOverlapping(window1.get(), window2.get()));
::wm::ActivateWindow(window2.get());
EXPECT_FALSE(wm::IsActiveWindow(window1.get()));
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
EXPECT_EQ(window2.get(), wm::GetFocusedWindow());
// Hide the cursor before entering overview to test that it will be shown.
aura::client::GetCursorClient(root_window)->HideCursor();
CheckOverviewEnterExitHistogram("Init", {0, 0, 0}, {0, 0, 0});
// In overview mode the windows should no longer overlap and the overview
// focus window should be focused.
ToggleOverview();
EXPECT_EQ(overview_session()->GetOverviewFocusWindow(),
wm::GetFocusedWindow());
EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get()));
CheckOverviewEnterExitHistogram("Enter", {1, 0, 0}, {0, 0, 0});
// Clicking window 1 should activate it.
ClickWindow(window1.get());
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
EXPECT_FALSE(wm::IsActiveWindow(window2.get()));
EXPECT_EQ(window1.get(), wm::GetFocusedWindow());
// Cursor should have been unlocked.
EXPECT_FALSE(aura::client::GetCursorClient(root_window)->IsCursorLocked());
CheckOverviewEnterExitHistogram("Exit", {1, 0, 0}, {1, 0, 0});
}
// Tests activating minimized window.
TEST_F(OverviewSessionTest, ActivateMinimized) {
std::unique_ptr<aura::Window> window(CreateTestWindow());
wm::WindowState* window_state = wm::GetWindowState(window.get());
wm::WMEvent minimize_event(wm::WM_EVENT_MINIMIZE);
window_state->OnWMEvent(&minimize_event);
EXPECT_FALSE(window->IsVisible());
EXPECT_EQ(0.f, window->layer()->GetTargetOpacity());
EXPECT_EQ(WindowStateType::kMinimized,
wm::GetWindowState(window.get())->GetStateType());
ToggleOverview();
EXPECT_FALSE(window->IsVisible());
EXPECT_EQ(0.f, window->layer()->GetTargetOpacity());
EXPECT_EQ(WindowStateType::kMinimized, window_state->GetStateType());
wm::WindowPreviewView* preview_view =
GetPreviewView(GetWindowItemForWindow(0, window.get()));
EXPECT_TRUE(preview_view);
const gfx::Point point = preview_view->GetBoundsInScreen().CenterPoint();
GetEventGenerator()->set_current_screen_location(point);
GetEventGenerator()->ClickLeftButton();
EXPECT_FALSE(InOverviewSession());
EXPECT_TRUE(window->IsVisible());
EXPECT_EQ(1.f, window->layer()->GetTargetOpacity());
EXPECT_EQ(WindowStateType::kNormal, window_state->GetStateType());
}
// Tests that the ordering of windows is stable across different overview
// sessions even when the windows have the same bounds.
TEST_F(OverviewSessionTest, WindowsOrder) {
std::unique_ptr<aura::Window> window1(CreateTestWindowInShellWithId(1));
std::unique_ptr<aura::Window> window2(CreateTestWindowInShellWithId(2));
std::unique_ptr<aura::Window> window3(CreateTestWindowInShellWithId(3));
// The order of windows in overview mode is MRU.
wm::GetWindowState(window1.get())->Activate();
ToggleOverview();
const std::vector<std::unique_ptr<OverviewItem>>& overview1 =
GetWindowItemsForRoot(0);
EXPECT_EQ(1, overview1[0]->GetWindow()->id());
EXPECT_EQ(3, overview1[1]->GetWindow()->id());
EXPECT_EQ(2, overview1[2]->GetWindow()->id());
ToggleOverview();
// Activate the second window.
wm::GetWindowState(window2.get())->Activate();
ToggleOverview();
const std::vector<std::unique_ptr<OverviewItem>>& overview2 =
GetWindowItemsForRoot(0);
// The order should be MRU.
EXPECT_EQ(2, overview2[0]->GetWindow()->id());
EXPECT_EQ(1, overview2[1]->GetWindow()->id());
EXPECT_EQ(3, overview2[2]->GetWindow()->id());
ToggleOverview();
}
// Tests selecting a window by tapping on it.
TEST_F(OverviewSessionTest, BasicGesture) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
::wm::ActivateWindow(window1.get());
EXPECT_EQ(window1.get(), wm::GetFocusedWindow());
ToggleOverview();
EXPECT_EQ(overview_session()->GetOverviewFocusWindow(),
wm::GetFocusedWindow());
GetEventGenerator()->GestureTapAt(
GetTransformedTargetBounds(window2.get()).CenterPoint());
EXPECT_EQ(window2.get(), wm::GetFocusedWindow());
}
// Tests that the user action WindowSelector_ActiveWindowChanged is
// recorded when the mouse/touchscreen/keyboard are used to select a window
// in overview mode which is different from the previously-active window.
TEST_F(OverviewSessionTest, ActiveWindowChangedUserActionRecorded) {
base::UserActionTester user_action_tester;
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
::wm::ActivateWindow(window1.get());
ToggleOverview();
// Tap on |window2| to activate it and exit overview.
GetEventGenerator()->GestureTapAt(
GetTransformedTargetBounds(window2.get()).CenterPoint());
EXPECT_EQ(
1, user_action_tester.GetActionCount(kActiveWindowChangedFromOverview));
// Click on |window2| to activate it and exit overview.
::wm::ActivateWindow(window1.get());
ToggleOverview();
ClickWindow(window2.get());
EXPECT_EQ(
2, user_action_tester.GetActionCount(kActiveWindowChangedFromOverview));
// Select |window2| using the arrow keys. Activate it (and exit overview) by
// pressing the return key.
::wm::ActivateWindow(window1.get());
ToggleOverview();
ASSERT_TRUE(SelectWindow(window2.get()));
SendKey(ui::VKEY_RETURN);
EXPECT_EQ(
3, user_action_tester.GetActionCount(kActiveWindowChangedFromOverview));
}
// Tests that the user action WindowSelector_ActiveWindowChanged is not
// recorded when the mouse/touchscreen/keyboard are used to select the
// already-active window from overview mode. Also verifies that entering and
// exiting overview without selecting a window does not record the action.
TEST_F(OverviewSessionTest, ActiveWindowChangedUserActionNotRecorded) {
base::UserActionTester user_action_tester;
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
::wm::ActivateWindow(window1.get());
ToggleOverview();
// Tap on |window1| to exit overview.
GetEventGenerator()->GestureTapAt(
GetTransformedTargetBounds(window1.get()).CenterPoint());
EXPECT_EQ(
0, user_action_tester.GetActionCount(kActiveWindowChangedFromOverview));
// |window1| remains active. Click on it to exit overview.
ASSERT_EQ(window1.get(), wm::GetFocusedWindow());
ToggleOverview();
ClickWindow(window1.get());
EXPECT_EQ(
0, user_action_tester.GetActionCount(kActiveWindowChangedFromOverview));
// |window1| remains active. Select using the keyboard.
ASSERT_EQ(window1.get(), wm::GetFocusedWindow());
ToggleOverview();
ASSERT_TRUE(SelectWindow(window1.get()));
SendKey(ui::VKEY_RETURN);
EXPECT_EQ(
0, user_action_tester.GetActionCount(kActiveWindowChangedFromOverview));
// Entering and exiting overview without user input should not record
// the action.
ToggleOverview();
ToggleOverview();
EXPECT_EQ(
0, user_action_tester.GetActionCount(kActiveWindowChangedFromOverview));
}
// Tests that the user action WindowSelector_ActiveWindowChanged is not
// recorded when overview mode exits as a result of closing its only window.
TEST_F(OverviewSessionTest, ActiveWindowChangedUserActionWindowClose) {
base::UserActionTester user_action_tester;
std::unique_ptr<views::Widget> widget(CreateTestWidget(
nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect(400, 400)));
ToggleOverview();
aura::Window* window = widget->GetNativeWindow();
const gfx::Point point = GetCloseButton(GetWindowItemForWindow(0, window))
->GetBoundsInScreen()
.CenterPoint();
ASSERT_FALSE(widget->IsClosed());
GetEventGenerator()->set_current_screen_location(point);
GetEventGenerator()->ClickLeftButton();
ASSERT_TRUE(widget->IsClosed());
EXPECT_EQ(
0, user_action_tester.GetActionCount(kActiveWindowChangedFromOverview));
}
// Tests that we do not crash and overview mode remains engaged if the desktop
// is tapped while a finger is already down over a window.
TEST_F(OverviewSessionTest, NoCrashWithDesktopTap) {
std::unique_ptr<aura::Window> window(
CreateTestWindow(gfx::Rect(200, 300, 250, 450)));
ToggleOverview();
const gfx::Rect bounds = GetTransformedBoundsInRootWindow(window.get());
GetEventGenerator()->set_current_screen_location(bounds.CenterPoint());
// Press down on the window.
const int kTouchId = 19;
GetEventGenerator()->PressTouchId(kTouchId);
// Tap on the desktop, which should not cause a crash. Overview mode should
// be disengaged.
GetEventGenerator()->GestureTapAt(gfx::Point(0, 0));
EXPECT_FALSE(InOverviewSession());
GetEventGenerator()->ReleaseTouchId(kTouchId);
}
// Tests that we do not crash and a window is selected when appropriate when
// we click on a window during touch.
TEST_F(OverviewSessionTest, ClickOnWindowDuringTouch) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
::wm::ActivateWindow(window2.get());
EXPECT_FALSE(wm::IsActiveWindow(window1.get()));
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
ToggleOverview();
gfx::Rect window1_bounds = GetTransformedBoundsInRootWindow(window1.get());
GetEventGenerator()->set_current_screen_location(
window1_bounds.CenterPoint());
// Clicking on |window2| while touching on |window1| should not cause a
// crash, it should do nothing since overview only handles one click or touch
// at a time.
const int kTouchId = 19;
GetEventGenerator()->PressTouchId(kTouchId);
GetEventGenerator()->MoveMouseToCenterOf(window2.get());
GetEventGenerator()->ClickLeftButton();
EXPECT_TRUE(InOverviewSession());
EXPECT_FALSE(wm::IsActiveWindow(window2.get()));
// Clicking on |window1| while touching on |window1| should not cause
// a crash, overview mode should be disengaged, and |window1| should
// be active.
GetEventGenerator()->MoveMouseToCenterOf(window1.get());
GetEventGenerator()->ClickLeftButton();
EXPECT_FALSE(InOverviewSession());
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
GetEventGenerator()->ReleaseTouchId(kTouchId);
}
// Tests that a window does not receive located events when in overview mode.
TEST_F(OverviewSessionTest, WindowDoesNotReceiveEvents) {
std::unique_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(400, 400)));
const gfx::Point point1 = window->bounds().CenterPoint();
ui::MouseEvent event1(ui::ET_MOUSE_PRESSED, point1, point1,
ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
aura::Window* root_window = Shell::GetPrimaryRootWindow();
ui::EventTarget* root_target = root_window;
ui::EventTargeter* targeter =
root_window->GetHost()->dispatcher()->GetDefaultEventTargeter();
// The event should target the window because we are still not in overview
// mode.
EXPECT_EQ(window.get(), targeter->FindTargetForEvent(root_target, &event1));
ToggleOverview();
// The bounds have changed, take that into account.
const gfx::Point point2 =
GetTransformedBoundsInRootWindow(window.get()).CenterPoint();
ui::MouseEvent event2(ui::ET_MOUSE_PRESSED, point2, point2,
ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
// Now the transparent window should be intercepting this event.
EXPECT_NE(window.get(), targeter->FindTargetForEvent(root_target, &event2));
}
// Tests that clicking on the close button effectively closes the window.
TEST_F(OverviewSessionTest, CloseButton) {
std::unique_ptr<views::Widget> widget(CreateTestWidget());
std::unique_ptr<views::Widget> minimized_widget(CreateTestWidget());
minimized_widget->Minimize();
ToggleOverview();
aura::Window* window = widget->GetNativeWindow();
const gfx::Point point = GetCloseButton(GetWindowItemForWindow(0, window))
->GetBoundsInScreen()
.CenterPoint();
GetEventGenerator()->set_current_screen_location(point);
EXPECT_FALSE(widget->IsClosed());
GetEventGenerator()->ClickLeftButton();
EXPECT_TRUE(widget->IsClosed());
ASSERT_TRUE(InOverviewSession());
aura::Window* minimized_window = minimized_widget->GetNativeWindow();
wm::WindowPreviewView* preview_view =
GetPreviewView(GetWindowItemForWindow(0, minimized_window));
EXPECT_TRUE(preview_view);
const gfx::Point point2 =
GetCloseButton(GetWindowItemForWindow(0, minimized_window))
->GetBoundsInScreen()
.CenterPoint();
GetEventGenerator()->MoveMouseTo(point2);
EXPECT_FALSE(minimized_widget->IsClosed());
GetEventGenerator()->ClickLeftButton();
EXPECT_TRUE(minimized_widget->IsClosed());
// All minimized windows are closed, so it should exit overview mode.
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(InOverviewSession());
}
// Tests minimizing/unminimizing in overview mode.
TEST_F(OverviewSessionTest, MinimizeUnminimize) {
std::unique_ptr<views::Widget> widget(CreateTestWidget());
aura::Window* window = widget->GetNativeWindow();
ToggleOverview();
EXPECT_FALSE(GetPreviewView(GetWindowItemForWindow(0, window)));
widget->Minimize();
EXPECT_TRUE(widget->IsMinimized());
EXPECT_TRUE(InOverviewSession());
EXPECT_TRUE(GetPreviewView(GetWindowItemForWindow(0, window)));
widget->Restore();
EXPECT_FALSE(widget->IsMinimized());
EXPECT_FALSE(GetPreviewView(GetWindowItemForWindow(0, window)));
EXPECT_TRUE(InOverviewSession());
}
// Tests that clicking on the close button on a secondary display effectively
// closes the window.
TEST_F(OverviewSessionTest, CloseButtonOnMultipleDisplay) {
UpdateDisplay("600x400,600x400");
// We need a widget for the close button to work because windows are closed
// via the widget. We also use the widget to determine if the window has been
// closed or not. Parent the window to a window in a non-primary root window.
std::unique_ptr<aura::Window> window(
CreateTestWindow(gfx::Rect(650, 300, 250, 450)));
std::unique_ptr<views::Widget> widget(CreateTestWidget());
widget->SetBounds(gfx::Rect(650, 0, 400, 400));
aura::Window* window2 = widget->GetNativeWindow();
window2->SetProperty(aura::client::kTopViewInset, kHeaderHeightDp);
views::Widget::ReparentNativeView(window2, window->parent());
ASSERT_EQ(Shell::GetAllRootWindows()[1], window2->GetRootWindow());
ToggleOverview();
gfx::Rect bounds = GetTransformedBoundsInRootWindow(window2);
gfx::Point point(bounds.right() - 5, bounds.y() + 5);
ui::test::EventGenerator event_generator(window2->GetRootWindow(), point);
EXPECT_FALSE(widget->IsClosed());
event_generator.ClickLeftButton();
EXPECT_TRUE(widget->IsClosed());
}
// Tests entering overview mode with two windows and selecting one.
TEST_F(OverviewSessionTest, FullscreenWindow) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
::wm::ActivateWindow(window1.get());
const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
wm::GetWindowState(window1.get())->OnWMEvent(&toggle_fullscreen_event);
EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen());
// Enter overview and select the fullscreen window.
ToggleOverview();
CheckOverviewEnterExitHistogram("FullscreenWindowEnter1", {0, 1, 0},
{0, 0, 0});
ClickWindow(window1.get());
EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen());
CheckOverviewEnterExitHistogram("FullscreenWindowExit1", {0, 1, 0},
{0, 1, 0});
// Entering overview and selecting another window, the previous window remains
// fullscreen.
ToggleOverview();
CheckOverviewEnterExitHistogram("FullscreenWindowEnter2", {0, 2, 0},
{0, 1, 0});
ClickWindow(window2.get());
EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen());
CheckOverviewEnterExitHistogram("FullscreenWindowExit2", {0, 2, 0},
{1, 1, 0});
}
// Tests entering overview mode with maximized window.
TEST_F(OverviewSessionTest, MaximizedWindow) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
::wm::ActivateWindow(window1.get());
const wm::WMEvent maximize_event(wm::WM_EVENT_MAXIMIZE);
wm::GetWindowState(window1.get())->OnWMEvent(&maximize_event);
EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMaximized());
// Enter overview and select the fullscreen window.
ToggleOverview();
CheckOverviewEnterExitHistogram("MaximizedWindowEnter1", {0, 1, 0},
{0, 0, 0});
ClickWindow(window1.get());
EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMaximized());
CheckOverviewEnterExitHistogram("MaximizedWindowExit1", {0, 1, 0}, {0, 1, 0});
ToggleOverview();
CheckOverviewEnterExitHistogram("MaximizedWindowEnter2", {0, 2, 0},
{0, 1, 0});
ClickWindow(window2.get());
EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMaximized());
CheckOverviewEnterExitHistogram("MaximizedWindowExit2", {0, 2, 0}, {1, 1, 0});
}
// Tests that entering overview when a fullscreen window is active in maximized
// mode correctly applies the transformations to the window and correctly
// updates the window bounds on exiting overview mode: http://crbug.com/401664.
TEST_F(OverviewSessionTest, FullscreenWindowTabletMode) {
UpdateDisplay("800x600");
const gfx::Rect bounds(400, 400);
std::unique_ptr<aura::Window> window1(CreateTestWindow(bounds));
std::unique_ptr<aura::Window> window2(CreateTestWindow(bounds));
::wm::ActivateWindow(window2.get());
::wm::ActivateWindow(window1.get());
EnterTabletMode();
gfx::Rect normal_window_bounds(window1->bounds());
const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
wm::GetWindowState(window1.get())->OnWMEvent(&toggle_fullscreen_event);
gfx::Rect fullscreen_window_bounds(window1->bounds());
EXPECT_NE(normal_window_bounds, fullscreen_window_bounds);
EXPECT_EQ(fullscreen_window_bounds, window2->GetTargetBounds());
const gfx::Rect fullscreen(800, 600);
const int shelf_inset = 600 - ShelfConstants::shelf_size();
const gfx::Rect normal_work_area(800, shelf_inset);
display::Screen* screen = display::Screen::GetScreen();
EXPECT_EQ(gfx::Rect(800, 600),
screen->GetDisplayNearestWindow(window1.get()).work_area());
ToggleOverview();
EXPECT_EQ(fullscreen,
screen->GetDisplayNearestWindow(window1.get()).work_area());
CheckOverviewEnterExitHistogram("FullscreenWindowTabletEnter1", {0, 0, 1},
{0, 0, 0});
// Window 2 would normally resize to normal window bounds on showing the shelf
// for overview but this is deferred until overview is exited.
EXPECT_EQ(fullscreen_window_bounds, window2->GetTargetBounds());
EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get()));
ToggleOverview();
EXPECT_EQ(fullscreen,
screen->GetDisplayNearestWindow(window1.get()).work_area());
// Since the fullscreen window is still active, window2 will still have the
// larger bounds.
EXPECT_EQ(fullscreen_window_bounds, window2->GetTargetBounds());
CheckOverviewEnterExitHistogram("FullscreenWindowTabletExit1", {0, 0, 1},
{0, 0, 1});
// Enter overview again and select window 2. Selecting window 2 should show
// the shelf bringing window2 back to the normal bounds.
ToggleOverview();
CheckOverviewEnterExitHistogram("FullscreenWindowTabletEnter2", {0, 0, 2},
{0, 0, 1});
ClickWindow(window2.get());
// Selecting non fullscreen window should set the work area back to normal.
EXPECT_EQ(normal_work_area,
screen->GetDisplayNearestWindow(window1.get()).work_area());
EXPECT_EQ(normal_window_bounds, window2->GetTargetBounds());
CheckOverviewEnterExitHistogram("FullscreenWindowTabletExit2", {0, 0, 2},
{0, 0, 2});
ToggleOverview();
CheckOverviewEnterExitHistogram("FullscreenWindowTabletEnter3", {0, 0, 3},
{0, 0, 2});
EXPECT_EQ(normal_work_area,
screen->GetDisplayNearestWindow(window1.get()).work_area());
ClickWindow(window1.get());
// Selecting fullscreen. The work area should be updated to fullscreen as
// well.
EXPECT_EQ(fullscreen,
screen->GetDisplayNearestWindow(window1.get()).work_area());
CheckOverviewEnterExitHistogram("FullscreenWindowTabletExit3", {0, 0, 3},
{0, 0, 3});
}
TEST_F(OverviewSessionTest, SkipOverviewWindow) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
window2->SetProperty(ash::kHideInOverviewKey, true);
// Enter overview.
ToggleOverview();
EXPECT_TRUE(window1->IsVisible());
EXPECT_FALSE(window2->IsVisible());
// Exit overview.
ToggleOverview();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(window1->IsVisible());
EXPECT_TRUE(window2->IsVisible());
}
// Tests that a minimized window's visibility and layer visibility
// stay invisible (A minimized window is cloned during overview).
TEST_F(OverviewSessionTest, MinimizedWindowState) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
wm::GetWindowState(window1.get())->Minimize();
EXPECT_FALSE(window1->IsVisible());
EXPECT_FALSE(window1->layer()->GetTargetVisibility());
ToggleOverview();
EXPECT_FALSE(window1->IsVisible());
EXPECT_FALSE(window1->layer()->GetTargetVisibility());
ToggleOverview();
EXPECT_FALSE(window1->IsVisible());
EXPECT_FALSE(window1->layer()->GetTargetVisibility());
}
// Tests that a bounds change during overview is corrected for.
TEST_F(OverviewSessionTest, BoundsChangeDuringOverview) {
std::unique_ptr<aura::Window> window(
CreateTestWindowInShellWithDelegate(nullptr, -1, gfx::Rect(400, 400)));
// Use overview headers above the window in this test.
window->SetProperty(aura::client::kTopViewInset, 0);
ToggleOverview();
gfx::Rect overview_bounds = GetTransformedTargetBounds(window.get());
window->SetBounds(gfx::Rect(200, 0, 200, 200));
gfx::Rect new_overview_bounds = GetTransformedTargetBounds(window.get());
EXPECT_EQ(overview_bounds, new_overview_bounds);
ToggleOverview();
}
// Tests that a change to the |kTopViewInset| window property during overview is
// corrected for.
TEST_F(OverviewSessionTest, TopViewInsetChangeDuringOverview) {
std::unique_ptr<aura::Window> window = CreateTestWindow(gfx::Rect(400, 400));
window->SetProperty(aura::client::kTopViewInset, 32);
ToggleOverview();
gfx::Rect overview_bounds = GetTransformedTargetBounds(window.get());
window->SetProperty(aura::client::kTopViewInset, 0);
gfx::Rect new_overview_bounds = GetTransformedTargetBounds(window.get());
EXPECT_NE(overview_bounds, new_overview_bounds);
ToggleOverview();
}
// Tests that a newly created window aborts overview.
TEST_F(OverviewSessionTest, NewWindowCancelsOverview) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
ToggleOverview();
EXPECT_TRUE(InOverviewSession());
// A window being created should exit overview mode.
std::unique_ptr<aura::Window> window2(CreateTestWindow());
EXPECT_FALSE(InOverviewSession());
}
// Tests that a window activation exits overview mode.
TEST_F(OverviewSessionTest, ActivationCancelsOverview) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
window2->Focus();
ToggleOverview();
EXPECT_TRUE(InOverviewSession());
// A window being activated should exit overview mode.
window1->Focus();
EXPECT_FALSE(InOverviewSession());
// window1 should be focused after exiting even though window2 was focused on
// entering overview because we exited due to an activation.
EXPECT_EQ(window1.get(), wm::GetFocusedWindow());
}
// Tests that if a window is dragged while overview is open, the activation
// of the dragged window does not cancel overview.
TEST_F(OverviewSessionTest, ActivateDraggedWindowNotCancelOverview) {
UpdateDisplay("800x600");
EnterTabletMode();
std::unique_ptr<aura::Window> window1(CreateTestWindow());
window1->SetProperty(aura::client::kAppType,
static_cast<int>(AppType::BROWSER));
std::unique_ptr<aura::Window> window2(CreateTestWindow());
EXPECT_FALSE(InOverviewSession());
// Start drag on |window1|.
std::unique_ptr<WindowResizer> resizer(CreateWindowResizer(
window1.get(), gfx::Point(), HTCAPTION, ::wm::WINDOW_MOVE_SOURCE_TOUCH));
EXPECT_TRUE(InOverviewSession());
resizer->Drag(gfx::Point(400, 0), 0);
EXPECT_TRUE(InOverviewSession());
wm::ActivateWindow(window1.get());
EXPECT_TRUE(InOverviewSession());
resizer->CompleteDrag();
EXPECT_FALSE(InOverviewSession());
}
// Tests that activate a non-dragged window during window drag will not cancel
// overview mode.
TEST_F(OverviewSessionTest, ActivateAnotherWindowDuringDragNotCancelOverview) {
UpdateDisplay("800x600");
EnterTabletMode();
std::unique_ptr<aura::Window> window1(CreateTestWindow());
window1->SetProperty(aura::client::kAppType,
static_cast<int>(AppType::BROWSER));
std::unique_ptr<aura::Window> window2(CreateTestWindow());
EXPECT_FALSE(InOverviewSession());
// Start drag on |window1|.
::wm::ActivateWindow(window1.get());
std::unique_ptr<WindowResizer> resizer(CreateWindowResizer(
window1.get(), gfx::Point(), HTCAPTION, ::wm::WINDOW_MOVE_SOURCE_TOUCH));
EXPECT_TRUE(InOverviewSession());
// Activate |window2| should not cancel overview mode.
::wm::ActivateWindow(window2.get());
EXPECT_FALSE(wm::GetWindowState(window2.get())->is_dragged());
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
EXPECT_TRUE(InOverviewSession());
}
// Tests that if an overview item is dragged, the activation of the
// corresponding window does not cancel overview.
TEST_F(OverviewSessionTest, ActivateDraggedOverviewWindowNotCancelOverview) {
UpdateDisplay("800x600");
EnterTabletMode();
std::unique_ptr<aura::Window> window(CreateTestWindow());
ToggleOverview();
OverviewItem* item = GetWindowItemForWindow(0, window.get());
gfx::PointF drag_point = item->target_bounds().CenterPoint();
overview_session()->InitiateDrag(item, drag_point,
/*allow_drag_to_close=*/false);
drag_point.Offset(5.f, 0.f);
overview_session()->Drag(item, drag_point);
::wm::ActivateWindow(window.get());
EXPECT_TRUE(InOverviewSession());
}
// Tests that if an overview item is dragged, the activation of the window
// corresponding to another overview item does not cancel overview.
TEST_F(OverviewSessionTest,
ActivateAnotherOverviewWindowDuringOverviewDragNotCancelOverview) {
UpdateDisplay("800x600");
EnterTabletMode();
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
ToggleOverview();
OverviewItem* item1 = GetWindowItemForWindow(0, window1.get());
gfx::PointF drag_point = item1->target_bounds().CenterPoint();
overview_session()->InitiateDrag(item1, drag_point,
/*allow_drag_to_close=*/false);
drag_point.Offset(5.f, 0.f);
overview_session()->Drag(item1, drag_point);
::wm::ActivateWindow(window2.get());
EXPECT_TRUE(InOverviewSession());
}
// Tests that if an overview item is dragged, the activation of a window
// excluded from overview does not cancel overview.
TEST_F(OverviewSessionTest,
ActivateWindowExcludedFromOverviewDuringOverviewDragNotCancelOverview) {
UpdateDisplay("800x600");
EnterTabletMode();
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(
CreateTestWindow(gfx::Rect(), aura::client::WINDOW_TYPE_POPUP));
EXPECT_TRUE(wm::ShouldExcludeForOverview(window2.get()));
ToggleOverview();
OverviewItem* item1 = GetWindowItemForWindow(0, window1.get());
gfx::PointF drag_point = item1->target_bounds().CenterPoint();
overview_session()->InitiateDrag(item1, drag_point,
/*allow_drag_to_close=*/false);
drag_point.Offset(5.f, 0.f);
overview_session()->Drag(item1, drag_point);
::wm::ActivateWindow(window2.get());
EXPECT_TRUE(InOverviewSession());
}
// Tests that exiting overview mode without selecting a window restores focus
// to the previously focused window.
TEST_F(OverviewSessionTest, CancelRestoresFocus) {
std::unique_ptr<aura::Window> window(CreateTestWindow());
::wm::ActivateWindow(window.get());
EXPECT_EQ(window.get(), wm::GetFocusedWindow());
// In overview mode, the overview focus window should be focused.
ToggleOverview();
EXPECT_EQ(overview_session()->GetOverviewFocusWindow(),
wm::GetFocusedWindow());
// If canceling overview mode, focus should be restored.
ToggleOverview();
EXPECT_EQ(window.get(), wm::GetFocusedWindow());
}
// Tests that overview mode is exited if the last remaining window is destroyed.
TEST_F(OverviewSessionTest, LastWindowDestroyed) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
ToggleOverview();
window1.reset();
window2.reset();
EXPECT_FALSE(InOverviewSession());
}
// Regression test for crash when closing the last overview mode window under
// SingleProcessMash. https://crbug.com/922293.
TEST_F(OverviewSessionTest, DontRestoreFocusToUnparentedWindow) {
const gfx::Rect bounds(400, 400);
std::unique_ptr<aura::Window> parent = CreateTestWindow(bounds);
std::unique_ptr<aura::Window> child = CreateChildWindow(parent.get(), bounds);
// Enter overview with a focused child window. This simulates an app window
// web contents RenderWidgetHostViewAura.
child->Focus();
ToggleOverview();
// Simulate the asynchronous window teardown for used by client widgets.
// Hierarchy changes are processed first, so the child is removed from its
// parent, then the windows are destroyed.
parent->RemoveChild(child.get());
parent.reset();
child.reset();
// Overview mode exits without crashing.
EXPECT_FALSE(InOverviewSession());
}
// Tests that entering overview mode restores a window to its original
// target location.
TEST_F(OverviewSessionTest, QuickReentryRestoresInitialTransform) {
std::unique_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(400, 400)));
gfx::Rect initial_bounds = GetTransformedBounds(window.get());
ToggleOverview();
// Quickly exit and reenter overview mode. The window should still be
// animating when we reenter. We cannot short circuit animations for this but
// we also don't have to wait for them to complete.
{
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
ToggleOverview();
ToggleOverview();
}
EXPECT_NE(initial_bounds, GetTransformedTargetBounds(window.get()));
ToggleOverview();
EXPECT_FALSE(InOverviewSession());
EXPECT_EQ(initial_bounds, GetTransformedTargetBounds(window.get()));
}
// Tests that windows with modal child windows are transformed with the modal
// child even though not activatable themselves.
TEST_F(OverviewSessionTest, ModalChild) {
const gfx::Rect bounds(400, 400);
std::unique_ptr<aura::Window> window(CreateTestWindow(bounds));
std::unique_ptr<aura::Window> child(CreateTestWindow(bounds));
child->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
::wm::AddTransientChild(window.get(), child.get());
EXPECT_EQ(window->parent(), child->parent());
ToggleOverview();
EXPECT_TRUE(window->IsVisible());
EXPECT_TRUE(child->IsVisible());
EXPECT_EQ(GetTransformedTargetBounds(child.get()),
GetTransformedTargetBounds(window.get()));
ToggleOverview();
}
// Tests that clicking a modal window's parent activates the modal window in
// overview.
TEST_F(OverviewSessionTest, ClickModalWindowParent) {
std::unique_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(180, 180)));
std::unique_ptr<aura::Window> child(
CreateTestWindow(gfx::Rect(200, 0, 180, 180)));
child->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_WINDOW);
::wm::AddTransientChild(window.get(), child.get());
EXPECT_FALSE(WindowsOverlapping(window.get(), child.get()));
EXPECT_EQ(window->parent(), child->parent());
ToggleOverview();
// Given that their relative positions are preserved, the windows should still
// not overlap.
EXPECT_FALSE(WindowsOverlapping(window.get(), child.get()));
ClickWindow(window.get());
EXPECT_FALSE(InOverviewSession());
// Clicking on window1 should activate child1.
EXPECT_TRUE(wm::IsActiveWindow(child.get()));
}
// Tests that windows remain on the display they are currently on in overview
// mode, and that the close buttons are on matching displays.
TEST_F(OverviewSessionTest, MultipleDisplays) {
UpdateDisplay("600x400,600x400");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
gfx::Rect bounds1(0, 0, 400, 400);
gfx::Rect bounds2(650, 0, 400, 400);
std::unique_ptr<aura::Window> window1(CreateTestWindow(bounds1));
std::unique_ptr<aura::Window> window2(CreateTestWindow(bounds1));
std::unique_ptr<aura::Window> window3(CreateTestWindow(bounds2));
std::unique_ptr<aura::Window> window4(CreateTestWindow(bounds2));
EXPECT_EQ(root_windows[0], window1->GetRootWindow());
EXPECT_EQ(root_windows[0], window2->GetRootWindow());
EXPECT_EQ(root_windows[1], window3->GetRootWindow());
EXPECT_EQ(root_windows[1], window4->GetRootWindow());
// In overview mode, each window remains in the same root window.
ToggleOverview();
EXPECT_EQ(root_windows[0], window1->GetRootWindow());
EXPECT_EQ(root_windows[0], window2->GetRootWindow());
EXPECT_EQ(root_windows[1], window3->GetRootWindow());
EXPECT_EQ(root_windows[1], window4->GetRootWindow());
// Window indices are based on top-down order. The reverse of our creation.
CheckWindowAndCloseButtonInScreen(window1.get(),
GetWindowItemForWindow(0, window1.get()));
CheckWindowAndCloseButtonInScreen(window2.get(),
GetWindowItemForWindow(0, window2.get()));
CheckWindowAndCloseButtonInScreen(window3.get(),
GetWindowItemForWindow(1, window3.get()));
CheckWindowAndCloseButtonInScreen(window4.get(),
GetWindowItemForWindow(1, window4.get()));
}
// Tests shutting down during overview.
TEST_F(OverviewSessionTest, Shutdown) {
// These windows will be deleted when the test exits and the Shell instance
// is shut down.
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
::wm::ActivateWindow(window2.get());
::wm::ActivateWindow(window1.get());
ToggleOverview();
}
// Tests removing a display during overview.
TEST_F(OverviewSessionTest, RemoveDisplay) {
UpdateDisplay("400x400,400x400");
std::unique_ptr<aura::Window> window1(CreateTestWindow(gfx::Rect(100, 100)));
std::unique_ptr<aura::Window> window2(
CreateTestWindow(gfx::Rect(450, 0, 100, 100)));
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
EXPECT_EQ(root_windows[0], window1->GetRootWindow());
EXPECT_EQ(root_windows[1], window2->GetRootWindow());
::wm::ActivateWindow(window2.get());
::wm::ActivateWindow(window1.get());
ToggleOverview();
EXPECT_TRUE(InOverviewSession());
UpdateDisplay("400x400");
EXPECT_FALSE(InOverviewSession());
}
// Tests removing a display during overview with NON_ZERO_DURATION animation.
TEST_F(OverviewSessionTest, RemoveDisplayWithAnimation) {
UpdateDisplay("400x400,400x400");
std::unique_ptr<aura::Window> window1(CreateTestWindow(gfx::Rect(100, 100)));
std::unique_ptr<aura::Window> window2(
CreateTestWindow(gfx::Rect(450, 0, 100, 100)));
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
EXPECT_EQ(root_windows[0], window1->GetRootWindow());
EXPECT_EQ(root_windows[1], window2->GetRootWindow());
::wm::ActivateWindow(window2.get());
::wm::ActivateWindow(window1.get());
ToggleOverview();
EXPECT_TRUE(InOverviewSession());
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
UpdateDisplay("400x400");
EXPECT_FALSE(InOverviewSession());
}
// Tests that dragging a window from the top of a display creates a drop target
// on that display.
TEST_F(OverviewSessionTest, DropTargetOnCorrectDisplayForDraggingFromTop) {
UpdateDisplay("600x600,600x600");
EnterTabletMode();
// DisplayConfigurationObserver enables mirror mode when tablet mode is
// enabled. Disable mirror mode to test multiple displays.
display_manager()->SetMirrorMode(display::MirrorMode::kOff, base::nullopt);
base::RunLoop().RunUntilIdle();
const aura::Window::Windows root_windows = Shell::Get()->GetAllRootWindows();
ASSERT_EQ(2u, root_windows.size());
std::unique_ptr<aura::Window> primary_screen_window =
CreateTestWindow(gfx::Rect(0, 0, 600, 600));
primary_screen_window->SetProperty(aura::client::kAppType,
static_cast<int>(AppType::BROWSER));
ASSERT_EQ(root_windows[0], primary_screen_window->GetRootWindow());
std::unique_ptr<aura::Window> secondary_screen_window =
CreateTestWindow(gfx::Rect(600, 0, 600, 600));
secondary_screen_window->SetProperty(aura::client::kAppType,
static_cast<int>(AppType::BROWSER));
ASSERT_EQ(root_windows[1], secondary_screen_window->GetRootWindow());
ASSERT_FALSE(InOverviewSession());
{
std::unique_ptr<WindowResizer> resizer =
CreateWindowResizer(primary_screen_window.get(), gfx::Point(400, 0),
HTCAPTION, ::wm::WINDOW_MOVE_SOURCE_TOUCH);
ASSERT_TRUE(InOverviewSession());
EXPECT_FALSE(GetDropTarget(1));
ASSERT_TRUE(GetDropTarget(0));
EXPECT_EQ(root_windows[0], GetDropTarget(0)->root_window());
resizer->CompleteDrag();
}
ASSERT_FALSE(InOverviewSession());
{
std::unique_ptr<WindowResizer> resizer =
CreateWindowResizer(secondary_screen_window.get(), gfx::Point(400, 0),
HTCAPTION, ::wm::WINDOW_MOVE_SOURCE_TOUCH);
ASSERT_TRUE(InOverviewSession());
EXPECT_FALSE(GetDropTarget(0));
ASSERT_TRUE(GetDropTarget(1));
EXPECT_EQ(root_windows[1], GetDropTarget(1)->root_window());
resizer->CompleteDrag();
}
ASSERT_FALSE(InOverviewSession());
}
// Tests that dragging a window from overview creates a drop target on the same
// display.
TEST_F(OverviewSessionTest, DropTargetOnCorrectDisplayForDraggingFromOverview) {
UpdateDisplay("600x600,600x600");
EnterTabletMode();
// DisplayConfigurationObserver enables mirror mode when tablet mode is
// enabled. Disable mirror mode to test multiple displays.
display_manager()->SetMirrorMode(display::MirrorMode::kOff, base::nullopt);
base::RunLoop().RunUntilIdle();
const aura::Window::Windows root_windows = Shell::Get()->GetAllRootWindows();
ASSERT_EQ(2u, root_windows.size());
std::unique_ptr<aura::Window> primary_screen_window =
CreateTestWindow(gfx::Rect(0, 0, 600, 600));
ASSERT_EQ(root_windows[0], primary_screen_window->GetRootWindow());
std::unique_ptr<aura::Window> secondary_screen_window =
CreateTestWindow(gfx::Rect(600, 0, 600, 600));
ASSERT_EQ(root_windows[1], secondary_screen_window->GetRootWindow());
ToggleOverview();
OverviewItem* primary_screen_item =
GetWindowItemForWindow(0, primary_screen_window.get());
OverviewItem* secondary_screen_item =
GetWindowItemForWindow(1, secondary_screen_window.get());
EXPECT_FALSE(GetDropTarget(0));
EXPECT_FALSE(GetDropTarget(1));
gfx::PointF drag_point = primary_screen_item->target_bounds().CenterPoint();
overview_session()->InitiateDrag(primary_screen_item, drag_point,
/*allow_drag_to_close=*/false);
EXPECT_FALSE(GetDropTarget(0));
EXPECT_FALSE(GetDropTarget(1));
drag_point.Offset(5.f, 0.f);
overview_session()->Drag(primary_screen_item, drag_point);
EXPECT_FALSE(GetDropTarget(1));
ASSERT_TRUE(GetDropTarget(0));
EXPECT_EQ(root_windows[0], GetDropTarget(0)->root_window());
overview_session()->CompleteDrag(primary_screen_item, drag_point);
EXPECT_FALSE(GetDropTarget(0));
EXPECT_FALSE(GetDropTarget(1));
drag_point = secondary_screen_item->target_bounds().CenterPoint();
overview_session()->InitiateDrag(secondary_screen_item, drag_point,
/*allow_drag_to_close=*/false);
EXPECT_FALSE(GetDropTarget(0));
EXPECT_FALSE(GetDropTarget(1));
drag_point.Offset(5.f, 0.f);
overview_session()->Drag(secondary_screen_item, drag_point);
EXPECT_FALSE(GetDropTarget(0));
ASSERT_TRUE(GetDropTarget(1));
EXPECT_EQ(root_windows[1], GetDropTarget(1)->root_window());
overview_session()->CompleteDrag(secondary_screen_item, drag_point);
EXPECT_FALSE(GetDropTarget(0));
EXPECT_FALSE(GetDropTarget(1));
}
// Test that |OverviewGrid::selected_index_| is updated with the drop target for
// dragging from overview.
TEST_F(OverviewSessionTest,
SelectionUpdatedWithDropTargetForDraggingFromOverview) {
EnterTabletMode();
std::unique_ptr<aura::Window> window4(CreateTestWindow());
std::unique_ptr<aura::Window> window3(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
std::unique_ptr<aura::Window> window1(CreateTestWindow());
ToggleOverview();
OverviewItem* item2 = GetWindowItemForWindow(0, window2.get());
const gfx::PointF drag_start_point = item2->target_bounds().CenterPoint();
const gfx::PointF drag_end_point =
drag_start_point + gfx::Vector2dF(5.f, 0.f);
const auto drag_item_and_check_selection =
[this, &drag_start_point, &drag_end_point](aura::Window* selected_window,
OverviewItem* dragged_item) {
EXPECT_TRUE(SelectWindow(selected_window));
overview_session()->InitiateDrag(dragged_item, drag_start_point,
/*allow_drag_to_close=*/false);
overview_session()->Drag(dragged_item, drag_end_point);
EXPECT_EQ(selected_window, GetSelectedWindow());
overview_session()->CompleteDrag(dragged_item, drag_end_point);
EXPECT_EQ(selected_window, GetSelectedWindow());
};
// Test the case where |OverviewGrid::selected_index_| is strictly less than
// the index of the dragged item.
drag_item_and_check_selection(window1.get(), item2);
// Test the case where |OverviewGrid::selected_index_| is equal to the index
// of the dragged item.
drag_item_and_check_selection(window2.get(), item2);
// Test the case where |OverviewGrid::selected_index_| is equal to the index
// where the drop target is to be inserted.
drag_item_and_check_selection(window3.get(), item2);
// Test the case where |OverviewGrid::selected_index_| is strictly greater
// than the index where the drop target is to be inserted.
drag_item_and_check_selection(window4.get(), item2);
// Test the case where the drop target becomes selected during the drag.
EXPECT_TRUE(SelectWindow(window4.get()));
overview_session()->InitiateDrag(item2, drag_start_point,
/*allow_drag_to_close=*/false);
overview_session()->Drag(item2, drag_end_point);
EXPECT_TRUE(SelectWindow(GetDropTarget(0)->GetWindow()));
overview_session()->CompleteDrag(item2, drag_end_point);
EXPECT_EQ(window2.get(), GetSelectedWindow());
}
namespace {
// A simple window delegate that returns the specified hit-test code when
// requested and applies a minimum size constraint if there is one.
class TestDragWindowDelegate : public aura::test::TestWindowDelegate {
public:
TestDragWindowDelegate() { set_window_component(HTCAPTION); }
~TestDragWindowDelegate() override = default;
private:
// aura::Test::TestWindowDelegate:
void OnWindowDestroyed(aura::Window* window) override { delete this; }
DISALLOW_COPY_AND_ASSIGN(TestDragWindowDelegate);
};
} // namespace
// Tests that toggling overview on and off does not cancel drag.
TEST_F(OverviewSessionTest, DragDropInProgress) {
std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
new TestDragWindowDelegate(), -1, gfx::Rect(100, 100)));
GetEventGenerator()->set_current_screen_location(
window->GetBoundsInScreen().CenterPoint());
GetEventGenerator()->PressLeftButton();
GetEventGenerator()->MoveMouseBy(10, 10);
EXPECT_EQ(gfx::Rect(10, 10, 100, 100), window->bounds());
ToggleOverview();
ASSERT_TRUE(InOverviewSession());
GetEventGenerator()->MoveMouseBy(10, 10);
ToggleOverview();
ASSERT_FALSE(InOverviewSession());
GetEventGenerator()->MoveMouseBy(10, 10);
GetEventGenerator()->ReleaseLeftButton();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(gfx::Rect(30, 30, 100, 100), window->bounds());
}
// Tests that toggling overview on removes any resize shadows that may have been
// present.
TEST_F(OverviewSessionTest, DragWindowShadow) {
std::unique_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(100, 100)));
::wm::ActivateWindow(window.get());
Shell::Get()->resize_shadow_controller()->ShowShadow(window.get(), HTTOP);
ToggleOverview();
ResizeShadow* shadow =
Shell::Get()->resize_shadow_controller()->GetShadowForWindowForTest(
window.get());
ASSERT_TRUE(shadow);
EXPECT_FALSE(shadow->GetLayerForTest()->visible());
}
// Test that a label is created under the window on entering overview mode.
TEST_F(OverviewSessionTest, CreateLabelUnderWindow) {
std::unique_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(300, 500)));
const base::string16 window_title = base::UTF8ToUTF16("My window");
window->SetTitle(window_title);
ToggleOverview();
OverviewItem* window_item = GetWindowItemsForRoot(0).back().get();
views::Label* label = GetLabelView(window_item);
ASSERT_TRUE(label);
// Verify the label matches the window title.
EXPECT_EQ(window_title, label->GetText());
// Update the window title and check that the label is updated, too.
const base::string16 updated_title = base::UTF8ToUTF16("Updated title");
window->SetTitle(updated_title);
EXPECT_EQ(updated_title, label->GetText());
// Labels are located based on target_bounds, not the actual window item
// bounds.
gfx::RectF label_bounds =
gfx::RectF(label->GetWidget()->GetWindowBoundsInScreen());
label_bounds.Inset(kWindowMargin, kWindowMargin);
EXPECT_EQ(label_bounds, window_item->target_bounds());
}
// Tests that overview updates the window positions if the display orientation
// changes.
TEST_F(OverviewSessionTest, DisplayOrientationChanged) {
aura::Window* root_window = Shell::Get()->GetPrimaryRootWindow();
UpdateDisplay("600x200");
EXPECT_EQ(gfx::Rect(600, 200), root_window->bounds());
std::vector<std::unique_ptr<aura::Window>> windows;
for (int i = 0; i < 3; i++) {
windows.push_back(
std::unique_ptr<aura::Window>(CreateTestWindow(gfx::Rect(150, 150))));
}
ToggleOverview();
for (const auto& window : windows) {
EXPECT_TRUE(root_window->bounds().Contains(
GetTransformedTargetBounds(window.get())));
}
// Rotate the display, windows should be repositioned to be within the screen
// bounds.
UpdateDisplay("600x200/r");
EXPECT_EQ(gfx::Rect(200, 600), root_window->bounds());
for (const auto& window : windows) {
EXPECT_TRUE(root_window->bounds().Contains(
GetTransformedTargetBounds(window.get())));
}
}
// Tests traversing some windows in overview mode with the tab key.
TEST_F(OverviewSessionTest, BasicTabKeyNavigation) {
std::unique_ptr<aura::Window> window2(CreateTestWindow());
std::unique_ptr<aura::Window> window1(CreateTestWindow());
ToggleOverview();
const std::vector<std::unique_ptr<OverviewItem>>& overview_windows =
GetWindowItemsForRoot(0);
SendKey(ui::VKEY_TAB);
EXPECT_EQ(GetSelectedWindow(), overview_windows[0]->GetWindow());
SendKey(ui::VKEY_TAB);
EXPECT_EQ(GetSelectedWindow(), overview_windows[1]->GetWindow());
SendKey(ui::VKEY_TAB);
EXPECT_EQ(GetSelectedWindow(), overview_windows[0]->GetWindow());
}
TEST_F(OverviewSessionTest, AcceleratorInOverviewSession) {
ToggleOverview();
auto* accelerator_controller = Shell::Get()->accelerator_controller();
auto* ewh = accelerator_controller->GetExitWarningHandlerForTest();
ASSERT_TRUE(ewh);
StubForTest(ewh);
EXPECT_FALSE(is_ui_shown(ewh));
ui::test::EventGenerator event_generator(Shell::GetPrimaryRootWindow());
event_generator.PressKey(ui::VKEY_Q, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
event_generator.ReleaseKey(ui::VKEY_Q,
ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_TRUE(is_ui_shown(ewh));
}
// Tests that pressing Ctrl+W while a window is selected in overview closes it.
TEST_F(OverviewSessionTest, CloseWindowWithKey) {
std::unique_ptr<views::Widget> widget(CreateTestWidget());
ToggleOverview();
SendKey(ui::VKEY_RIGHT);
EXPECT_EQ(widget->GetNativeWindow(), GetSelectedWindow());
SendKey(ui::VKEY_W, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(widget->IsClosed());
}
// Tests traversing some windows in overview mode with the arrow keys in every
// possible direction.
TEST_F(OverviewSessionTest, BasicArrowKeyNavigation) {
const size_t test_windows = 9;
UpdateDisplay("800x600");
std::vector<std::unique_ptr<aura::Window>> windows;
for (size_t i = test_windows; i > 0; i--) {
windows.push_back(
std::unique_ptr<aura::Window>(CreateTestWindowInShellWithId(i)));
}
ui::KeyboardCode arrow_keys[] = {ui::VKEY_RIGHT, ui::VKEY_DOWN, ui::VKEY_LEFT,
ui::VKEY_UP};
// The rows contain variable number of items making vertical navigation not
// feasible. [Down] is equivalent to [Right] and [Up] is equivalent to [Left].
int index_path_for_direction[][test_windows + 1] = {
{1, 2, 3, 4, 5, 6, 7, 8, 9, 1}, // Right
{1, 2, 3, 4, 5, 6, 7, 8, 9, 1}, // Down (same as Right)
{9, 8, 7, 6, 5, 4, 3, 2, 1, 9}, // Left
{9, 8, 7, 6, 5, 4, 3, 2, 1, 9} // Up (same as Left)
};
for (size_t key_index = 0; key_index < base::size(arrow_keys); key_index++) {
ToggleOverview();
const std::vector<std::unique_ptr<OverviewItem>>& overview_windows =
GetWindowItemsForRoot(0);
for (size_t i = 0; i < test_windows + 1; i++) {
SendKey(arrow_keys[key_index]);
// TODO(flackr): Add a more readable error message by constructing a
// string from the window IDs.
const int index = index_path_for_direction[key_index][i];
EXPECT_EQ(GetSelectedWindow()->id(),
overview_windows[index - 1]->GetWindow()->id());
}
ToggleOverview();
}
}
// Tests hitting the escape and back keys exits overview mode.
TEST_F(OverviewSessionTest, ExitOverviewWithKey) {
std::unique_ptr<aura::Window> window(CreateTestWindow());
ToggleOverview();
ASSERT_TRUE(overview_controller()->InOverviewSession());
SendKey(ui::VKEY_ESCAPE);
EXPECT_FALSE(overview_controller()->InOverviewSession());
ToggleOverview();
ASSERT_TRUE(overview_controller()->InOverviewSession());
SendKey(ui::VKEY_BROWSER_BACK);
EXPECT_FALSE(overview_controller()->InOverviewSession());
// Tests that in tablet mode, if we snap the only overview window, we cannot
// exit overview mode.
EnterTabletMode();
ToggleOverview();
ASSERT_TRUE(overview_controller()->InOverviewSession());
Shell::Get()->split_view_controller()->SnapWindow(window.get(),
SplitViewController::LEFT);
ASSERT_TRUE(overview_controller()->InOverviewSession());
SendKey(ui::VKEY_ESCAPE);
EXPECT_TRUE(overview_controller()->InOverviewSession());
SendKey(ui::VKEY_BROWSER_BACK);
EXPECT_TRUE(overview_controller()->InOverviewSession());
}
// Regression test for clusterfuzz crash. https://crbug.com/920568
TEST_F(OverviewSessionTest, TypeThenPressEscapeTwice) {
std::unique_ptr<aura::Window> window(CreateTestWindow());
ToggleOverview();
// Type some characters.
SendKey(ui::VKEY_A);
SendKey(ui::VKEY_B);
SendKey(ui::VKEY_C);
EXPECT_TRUE(overview_session()->GetOverviewFocusWindow());
// Pressing escape twice should not crash.
SendKey(ui::VKEY_ESCAPE);
SendKey(ui::VKEY_ESCAPE);
}
// Tests basic selection across multiple monitors.
TEST_F(OverviewSessionTest, BasicMultiMonitorArrowKeyNavigation) {
UpdateDisplay("400x400,400x400");
const gfx::Rect bounds1(100, 100);
const gfx::Rect bounds2(450, 0, 100, 100);
std::unique_ptr<aura::Window> window4(CreateTestWindow(bounds2));
std::unique_ptr<aura::Window> window3(CreateTestWindow(bounds2));
std::unique_ptr<aura::Window> window2(CreateTestWindow(bounds1));
std::unique_ptr<aura::Window> window1(CreateTestWindow(bounds1));
ToggleOverview();
const std::vector<std::unique_ptr<OverviewItem>>& overview_root1 =
GetWindowItemsForRoot(0);
const std::vector<std::unique_ptr<OverviewItem>>& overview_root2 =
GetWindowItemsForRoot(1);
SendKey(ui::VKEY_RIGHT);
EXPECT_EQ(GetSelectedWindow(), overview_root1[0]->GetWindow());
SendKey(ui::VKEY_RIGHT);
EXPECT_EQ(GetSelectedWindow(), overview_root1[1]->GetWindow());
SendKey(ui::VKEY_RIGHT);
EXPECT_EQ(GetSelectedWindow(), overview_root2[0]->GetWindow());
SendKey(ui::VKEY_RIGHT);
EXPECT_EQ(GetSelectedWindow(), overview_root2[1]->GetWindow());
}
// Tests first monitor when display order doesn't match left to right screen
// positions.
TEST_F(OverviewSessionTest, MultiMonitorReversedOrder) {
UpdateDisplay("400x400,400x400");
Shell::Get()->display_manager()->SetLayoutForCurrentDisplays(
display::test::CreateDisplayLayout(display_manager(),
display::DisplayPlacement::LEFT, 0));
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
std::unique_ptr<aura::Window> window2(CreateTestWindow(gfx::Rect(100, 100)));
std::unique_ptr<aura::Window> window1(
CreateTestWindow(gfx::Rect(-350, 0, 100, 100)));
EXPECT_EQ(root_windows[1], window1->GetRootWindow());
EXPECT_EQ(root_windows[0], window2->GetRootWindow());
ToggleOverview();
// Coming from the left to right, we should select window1 first being on the
// display to the left.
SendKey(ui::VKEY_RIGHT);
EXPECT_EQ(GetSelectedWindow(), window1.get());
// Exit and reenter overview.
ToggleOverview();
ToggleOverview();
// Coming from right to left, we should select window2 first being on the
// display on the right.
SendKey(ui::VKEY_LEFT);
EXPECT_EQ(GetSelectedWindow(), window2.get());
}
// Tests three monitors where the grid becomes empty on one of the monitors.
TEST_F(OverviewSessionTest, ThreeMonitor) {
UpdateDisplay("400x400,400x400,400x400");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
std::unique_ptr<aura::Window> window3(
CreateTestWindow(gfx::Rect(800, 0, 100, 100)));
std::unique_ptr<aura::Window> window2(
CreateTestWindow(gfx::Rect(400, 0, 100, 100)));
std::unique_ptr<aura::Window> window1(CreateTestWindow(gfx::Rect(100, 100)));
EXPECT_EQ(root_windows[0], window1->GetRootWindow());
EXPECT_EQ(root_windows[1], window2->GetRootWindow());
EXPECT_EQ(root_windows[2], window3->GetRootWindow());
ToggleOverview();
SendKey(ui::VKEY_RIGHT);
SendKey(ui::VKEY_RIGHT);
SendKey(ui::VKEY_RIGHT);
EXPECT_EQ(GetSelectedWindow(), window3.get());
// If the last window on a display closes it should select the previous
// display's window.
window3.reset();
EXPECT_EQ(GetSelectedWindow(), window2.get());
ToggleOverview();
window3 = CreateTestWindow(gfx::Rect(800, 0, 100, 100));
ToggleOverview();
SendKey(ui::VKEY_RIGHT);
SendKey(ui::VKEY_RIGHT);
SendKey(ui::VKEY_RIGHT);
// If the window on the second display is removed, the selected window should
// remain window3.
EXPECT_EQ(GetSelectedWindow(), window3.get());
window2.reset();
EXPECT_EQ(GetSelectedWindow(), window3.get());
}
// Tests selecting a window in overview mode with the return key.
TEST_F(OverviewSessionTest, SelectWindowWithReturnKey) {
std::unique_ptr<aura::Window> window2(CreateTestWindow());
std::unique_ptr<aura::Window> window1(CreateTestWindow());
ToggleOverview();
// Pressing the return key without a selection widget should not do anything.
SendKey(ui::VKEY_RETURN);
EXPECT_TRUE(InOverviewSession());
// Select the first window.
ASSERT_TRUE(SelectWindow(window1.get()));
SendKey(ui::VKEY_RETURN);
ASSERT_FALSE(InOverviewSession());
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
// Select the second window.
ToggleOverview();
ASSERT_TRUE(SelectWindow(window2.get()));
SendKey(ui::VKEY_RETURN);
EXPECT_FALSE(InOverviewSession());
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
}
TEST_F(OverviewSessionTest, CancelOverviewOnMouseClick) {
// Point and bounds selected so that they don't intersect. This causes
// events located at the point to be passed to WallpaperController,
// and not the window.
const gfx::Point point_in_background_page(0, 0);
std::unique_ptr<aura::Window> window(
CreateTestWindow(gfx::Rect(10, 10, 100, 100)));
// Move mouse to point in the background page. Sending an event here will pass
// it to the WallpaperController in both regular and overview mode.
GetEventGenerator()->MoveMouseTo(point_in_background_page);
// Clicking on the background page while not in overview should not toggle
// overview.
GetEventGenerator()->ClickLeftButton();
EXPECT_FALSE(InOverviewSession());
// Switch to overview mode. Clicking should now exit overview mode.
ToggleOverview();
ASSERT_TRUE(InOverviewSession());
GetEventGenerator()->ClickLeftButton();
EXPECT_FALSE(InOverviewSession());
}
// Tests tapping on the desktop itself to cancel overview mode.
TEST_F(OverviewSessionTest, CancelOverviewOnTap) {
// Point and bounds selected so that they don't intersect. This causes
// events located at the point to be passed to WallpaperController,
// and not the window.
gfx::Point point_in_background_page(0, 0);
std::unique_ptr<aura::Window> window(
CreateTestWindow(gfx::Rect(10, 10, 100, 100)));
// Tapping on the background page while not in overview should not toggle
// overview.
GetEventGenerator()->GestureTapAt(point_in_background_page);
EXPECT_FALSE(InOverviewSession());
// Switch to overview mode. Tapping should now exit overview mode.
ToggleOverview();
ASSERT_TRUE(InOverviewSession());
GetEventGenerator()->GestureTapAt(point_in_background_page);
EXPECT_FALSE(InOverviewSession());
}
// Start dragging a window and activate overview mode. This test should not
// crash or DCHECK inside aura::Window::StackChildRelativeTo().
TEST_F(OverviewSessionTest, OverviewWhileDragging) {
std::unique_ptr<aura::Window> window(CreateTestWindow());
std::unique_ptr<WindowResizer> resizer(CreateWindowResizer(
window.get(), gfx::Point(), HTCAPTION, ::wm::WINDOW_MOVE_SOURCE_MOUSE));
ASSERT_TRUE(resizer.get());
gfx::Point location = resizer->GetInitialLocation();
location.Offset(20, 20);
resizer->Drag(location, 0);
ToggleOverview();
resizer->RevertDrag();
}
// Verify that the overview no windows indicator appears when entering overview
// mode with no windows.
TEST_F(OverviewSessionTest, NoWindowsIndicator) {
// Verify that by entering overview mode without windows, the no items
// indicator appears.
ToggleOverview();
ASSERT_TRUE(overview_session());
ASSERT_EQ(0u, GetWindowItemsForRoot(0).size());
EXPECT_TRUE(overview_session()->no_windows_widget_for_testing());
}
// Verify that the overview no windows indicator position is as expected.
TEST_F(OverviewSessionTest, NoWindowsIndicatorPosition) {
UpdateDisplay("400x300");
ToggleOverview();
ASSERT_TRUE(overview_session());
RoundedLabelWidget* no_windows_widget =
overview_session()->no_windows_widget_for_testing();
ASSERT_TRUE(no_windows_widget);
// Verify that originally the label is in the center of the workspace.
// Midpoint of height minus shelf.
int expected_y = (300 - ShelfConstants::shelf_size()) / 2;
EXPECT_EQ(gfx::Point(200, expected_y),
no_windows_widget->GetWindowBoundsInScreen().CenterPoint());
// Verify that after rotating the display, the label is centered in the
// workspace 300x(400-shelf).
display::Screen* screen = display::Screen::GetScreen();
const display::Display& display = screen->GetPrimaryDisplay();
display_manager()->SetDisplayRotation(
display.id(), display::Display::ROTATE_90,
display::Display::RotationSource::ACTIVE);
expected_y = (400 - ShelfConstants::shelf_size()) / 2;
EXPECT_EQ(gfx::Point(150, (400 - ShelfConstants::shelf_size()) / 2),
no_windows_widget->GetWindowBoundsInScreen().CenterPoint());
}
TEST_F(OverviewSessionTest, NoWindowsIndicatorPositionSplitview) {
UpdateDisplay("400x300");
EnterTabletMode();
std::unique_ptr<aura::Window> window(CreateTestWindow());
ToggleOverview();
ASSERT_TRUE(overview_session());
RoundedLabelWidget* no_windows_widget =
overview_session()->no_windows_widget_for_testing();
EXPECT_FALSE(no_windows_widget);
// Tests that when snapping a window to the left in splitview, the no windows
// indicator shows up in the middle of the right side of the screen.
auto* split_view_controller = Shell::Get()->split_view_controller();
split_view_controller->SnapWindow(window.get(), SplitViewController::LEFT);
no_windows_widget = overview_session()->no_windows_widget_for_testing();
ASSERT_TRUE(no_windows_widget);
// There is a 8dp divider in splitview, the indicator should take that into
// account.
const int bounds_left = 200 + 4;
int expected_x = bounds_left + (400 - (bounds_left)) / 2;
const int expected_y = (300 - ShelfConstants::shelf_size()) / 2;
EXPECT_EQ(gfx::Point(expected_x, expected_y),
no_windows_widget->GetWindowBoundsInScreen().CenterPoint());
// Tests that when snapping a window to the right in splitview, the no windows
// indicator shows up in the middle of the left side of the screen.
split_view_controller->SnapWindow(window.get(), SplitViewController::RIGHT);
expected_x = /*bounds_right=*/(200 - 4) / 2;
EXPECT_EQ(gfx::Point(expected_x, expected_y),
no_windows_widget->GetWindowBoundsInScreen().CenterPoint());
}
// Tests that the no windows indicator shows properly after adding an item.
TEST_F(OverviewSessionTest, NoWindowsIndicatorAddItem) {
EnterTabletMode();
std::unique_ptr<aura::Window> window(CreateTestWindow());
ToggleOverview();
auto* split_view_controller = Shell::Get()->split_view_controller();
split_view_controller->SnapWindow(window.get(), SplitViewController::LEFT);
EXPECT_TRUE(overview_session()->no_windows_widget_for_testing());
overview_session()->AddItem(window.get(), /*reposition=*/true,
/*animate=*/false);
EXPECT_FALSE(overview_session()->no_windows_widget_for_testing());
}
// Verify that when opening overview mode with multiple displays, the no items
// indicator on the primary grid if there are no windows.
TEST_F(OverviewSessionTest, NoWindowsIndicatorPositionMultiDisplay) {
UpdateDisplay("400x400,400x400,400x400");
// Enter overview mode. Verify that the no windows indicator is located on the
// primary display.
ToggleOverview();
ASSERT_TRUE(overview_session());
RoundedLabelWidget* no_windows_widget =
overview_session()->no_windows_widget_for_testing();
const int expected_y = (400 - ShelfConstants::shelf_size()) / 2;
EXPECT_EQ(gfx::Point(200, expected_y),
no_windows_widget->GetWindowBoundsInScreen().CenterPoint());
}
// Tests that we do not exit overview mode until all the grids are empty.
TEST_F(OverviewSessionTest, ExitOverviewWhenAllGridsEmpty) {
UpdateDisplay("400x400,400x400,400x400");
// Create two windows with widgets (widgets are needed to close the windows
// later in the test), one each on the first two monitors.
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
std::unique_ptr<views::Widget> widget1(CreateTestWidget());
std::unique_ptr<views::Widget> widget2(CreateTestWidget());
aura::Window* window1 = widget1->GetNativeWindow();
aura::Window* window2 = widget2->GetNativeWindow();
ASSERT_TRUE(wm::MoveWindowToDisplay(window2, GetSecondaryDisplay().id()));
ASSERT_EQ(root_windows[0], window1->GetRootWindow());
ASSERT_EQ(root_windows[1], window2->GetRootWindow());
// Enter overview mode. Verify that the no windows indicator is not visible on
// any display.
ToggleOverview();
auto& grids = overview_session()->grid_list_for_testing();
ASSERT_TRUE(overview_session());
ASSERT_EQ(3u, grids.size());
EXPECT_FALSE(overview_session()->no_windows_widget_for_testing());
OverviewItem* item1 = GetWindowItemForWindow(0, window1);
OverviewItem* item2 = GetWindowItemForWindow(1, window2);
ASSERT_TRUE(item1 && item2);
// Close |item2|. Verify that we are still in overview mode because |window1|
// is still open. The non primary root grids are empty however.
item2->CloseWindow();
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(overview_session());
ASSERT_EQ(3u, grids.size());
EXPECT_FALSE(grids[0]->empty());
EXPECT_TRUE(grids[1]->empty());
EXPECT_TRUE(grids[2]->empty());
EXPECT_FALSE(overview_session()->no_windows_widget_for_testing());
// Close |item1|. Verify that since no windows are open, we exit overview
// mode.
item1->CloseWindow();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(overview_session());
}
// Tests window list animation states are correctly updated.
TEST_F(OverviewSessionTest, SetWindowListAnimationStates) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
std::unique_ptr<aura::Window> window3(CreateTestWindow());
::wm::ActivateWindow(window3.get());
::wm::ActivateWindow(window2.get());
::wm::ActivateWindow(window1.get());
EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen());
EXPECT_FALSE(wm::GetWindowState(window2.get())->IsFullscreen());
EXPECT_FALSE(wm::GetWindowState(window3.get())->IsFullscreen());
const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
wm::GetWindowState(window2.get())->OnWMEvent(&toggle_fullscreen_event);
wm::GetWindowState(window3.get())->OnWMEvent(&toggle_fullscreen_event);
EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen());
EXPECT_TRUE(wm::GetWindowState(window2.get())->IsFullscreen());
EXPECT_TRUE(wm::GetWindowState(window3.get())->IsFullscreen());
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Enter overview.
ToggleOverview();
EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window3->layer()->GetAnimator()->is_animating());
ToggleOverview();
}
// Tests window list animation states are correctly updated with selected
// window.
TEST_F(OverviewSessionTest, SetWindowListAnimationStatesWithSelectedWindow) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
std::unique_ptr<aura::Window> window3(CreateTestWindow());
::wm::ActivateWindow(window3.get());
::wm::ActivateWindow(window2.get());
::wm::ActivateWindow(window1.get());
EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen());
EXPECT_FALSE(wm::GetWindowState(window2.get())->IsFullscreen());
EXPECT_FALSE(wm::GetWindowState(window3.get())->IsFullscreen());
const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
wm::GetWindowState(window2.get())->OnWMEvent(&toggle_fullscreen_event);
wm::GetWindowState(window3.get())->OnWMEvent(&toggle_fullscreen_event);
EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen());
EXPECT_TRUE(wm::GetWindowState(window2.get())->IsFullscreen());
EXPECT_TRUE(wm::GetWindowState(window3.get())->IsFullscreen());
// Enter overview.
ToggleOverview();
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Click on |window3| to activate it and exit overview.
// Should only set |should_animate_when_exiting_| and
// |should_be_observed_when_exiting_| on window 3.
TweenTester tester1(window1.get());
TweenTester tester2(window2.get());
TweenTester tester3(window3.get());
ClickWindow(window3.get());
EXPECT_EQ(gfx::Tween::ZERO, tester1.tween_type());
EXPECT_EQ(gfx::Tween::ZERO, tester2.tween_type());
EXPECT_EQ(gfx::Tween::EASE_OUT, tester3.tween_type());
}
// Tests OverviewWindowAnimationObserver can handle deleted window.
TEST_F(OverviewSessionTest,
OverviewWindowAnimationObserverCanHandleDeletedWindow) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
std::unique_ptr<aura::Window> window3(CreateTestWindow());
::wm::ActivateWindow(window3.get());
::wm::ActivateWindow(window2.get());
::wm::ActivateWindow(window1.get());
EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen());
EXPECT_FALSE(wm::GetWindowState(window2.get())->IsFullscreen());
EXPECT_FALSE(wm::GetWindowState(window3.get())->IsFullscreen());
const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
wm::GetWindowState(window2.get())->OnWMEvent(&toggle_fullscreen_event);
wm::GetWindowState(window3.get())->OnWMEvent(&toggle_fullscreen_event);
EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen());
EXPECT_TRUE(wm::GetWindowState(window2.get())->IsFullscreen());
EXPECT_TRUE(wm::GetWindowState(window3.get())->IsFullscreen());
// Enter overview.
ToggleOverview();
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Click on |window3| to activate it and exit overview.
// Should only set |should_animate_when_exiting_| and
// |should_be_observed_when_exiting_| on window 3.
{
TweenTester tester1(window1.get());
TweenTester tester2(window2.get());
TweenTester tester3(window3.get());
ClickWindow(window3.get());
EXPECT_EQ(gfx::Tween::ZERO, tester1.tween_type());
EXPECT_EQ(gfx::Tween::ZERO, tester2.tween_type());
EXPECT_EQ(gfx::Tween::EASE_OUT, tester3.tween_type());
}
// Destroy |window1| and |window2| before |window3| finishes animation can be
// handled in OverviewWindowAnimationObserver.
window1.reset();
window2.reset();
}
// Tests can handle OverviewWindowAnimationObserver was deleted.
TEST_F(OverviewSessionTest, HandleOverviewWindowAnimationObserverWasDeleted) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
std::unique_ptr<aura::Window> window3(CreateTestWindow());
::wm::ActivateWindow(window3.get());
::wm::ActivateWindow(window2.get());
::wm::ActivateWindow(window1.get());
EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen());
EXPECT_FALSE(wm::GetWindowState(window2.get())->IsFullscreen());
EXPECT_FALSE(wm::GetWindowState(window3.get())->IsFullscreen());
const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
wm::GetWindowState(window2.get())->OnWMEvent(&toggle_fullscreen_event);
wm::GetWindowState(window3.get())->OnWMEvent(&toggle_fullscreen_event);
EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen());
EXPECT_TRUE(wm::GetWindowState(window2.get())->IsFullscreen());
EXPECT_TRUE(wm::GetWindowState(window3.get())->IsFullscreen());
// Enter overview.
ToggleOverview();
// Click on |window2| to activate it and exit overview. Should only set
// |should_animate_when_exiting_| and |should_be_observed_when_exiting_| on
// window 2. Because the animation duration is zero in test, the
// OverviewWindowAnimationObserver will delete itself immediately before
// |window3| is added to it.
ClickWindow(window2.get());
EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window2->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window3->layer()->GetAnimator()->is_animating());
}
// Tests can handle |gained_active| window is not in the |overview_grid| when
// OnWindowActivated.
TEST_F(OverviewSessionTest, HandleActiveWindowNotInOverviewGrid) {
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
std::unique_ptr<aura::Window> window3(CreateTestWindow());
::wm::ActivateWindow(window3.get());
::wm::ActivateWindow(window2.get());
::wm::ActivateWindow(window1.get());
EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen());
EXPECT_FALSE(wm::GetWindowState(window2.get())->IsFullscreen());
EXPECT_FALSE(wm::GetWindowState(window3.get())->IsFullscreen());
const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
wm::GetWindowState(window2.get())->OnWMEvent(&toggle_fullscreen_event);
wm::GetWindowState(window3.get())->OnWMEvent(&toggle_fullscreen_event);
EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen());
EXPECT_TRUE(wm::GetWindowState(window2.get())->IsFullscreen());
EXPECT_TRUE(wm::GetWindowState(window3.get())->IsFullscreen());
// Enter overview.
ToggleOverview();
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Create and active a new window should exit overview without error.
auto widget = CreateTestWidget();
TweenTester tester1(window1.get());
TweenTester tester2(window2.get());
TweenTester tester3(window3.get());
ClickWindow(widget->GetNativeWindow());
// |window1| and |window2| should animate.
EXPECT_EQ(gfx::Tween::EASE_OUT, tester1.tween_type());
EXPECT_EQ(gfx::Tween::EASE_OUT, tester2.tween_type());
EXPECT_EQ(gfx::Tween::ZERO, tester3.tween_type());
}
// Tests that AlwaysOnTopWindow can be handled correctly in new overview
// animations.
// Fails consistently; see https://crbug.com/812497.
TEST_F(OverviewSessionTest, DISABLED_HandleAlwaysOnTopWindow) {
const gfx::Rect bounds(400, 400);
std::unique_ptr<aura::Window> window1(CreateTestWindow(bounds));
std::unique_ptr<aura::Window> window2(CreateTestWindow(bounds));
std::unique_ptr<aura::Window> window3(CreateTestWindow(bounds));
std::unique_ptr<aura::Window> window4(CreateTestWindow(bounds));
std::unique_ptr<aura::Window> window5(CreateTestWindow(bounds));
std::unique_ptr<aura::Window> window6(CreateTestWindow(bounds));
std::unique_ptr<aura::Window> window7(CreateTestWindow(bounds));
std::unique_ptr<aura::Window> window8(CreateTestWindow(bounds));
window3->SetProperty(aura::client::kAlwaysOnTopKey, true);
window5->SetProperty(aura::client::kAlwaysOnTopKey, true);
// Control z order and MRU order.
::wm::ActivateWindow(window8.get());
::wm::ActivateWindow(window7.get()); // Will be fullscreen.
::wm::ActivateWindow(window6.get()); // Will be maximized.
::wm::ActivateWindow(window5.get()); // AlwaysOnTop window.
::wm::ActivateWindow(window4.get());
::wm::ActivateWindow(window3.get()); // AlwaysOnTop window.
::wm::ActivateWindow(window2.get()); // Will be fullscreen.
::wm::ActivateWindow(window1.get());
EXPECT_FALSE(wm::GetWindowState(window2.get())->IsFullscreen());
EXPECT_FALSE(wm::GetWindowState(window6.get())->IsFullscreen());
EXPECT_FALSE(wm::GetWindowState(window7.get())->IsMaximized());
const wm::WMEvent toggle_maximize_event(wm::WM_EVENT_TOGGLE_MAXIMIZE);
wm::GetWindowState(window6.get())->OnWMEvent(&toggle_maximize_event);
const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
wm::GetWindowState(window2.get())->OnWMEvent(&toggle_fullscreen_event);
wm::GetWindowState(window7.get())->OnWMEvent(&toggle_fullscreen_event);
EXPECT_TRUE(wm::GetWindowState(window2.get())->IsFullscreen());
EXPECT_TRUE(wm::GetWindowState(window7.get())->IsFullscreen());
EXPECT_TRUE(wm::GetWindowState(window6.get())->IsMaximized());
// Case 1: Click on |window1| to activate it and exit overview.
std::unique_ptr<ui::ScopedAnimationDurationScaleMode> test_duration_mode =
std::make_unique<ui::ScopedAnimationDurationScaleMode>(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
ToggleOverview();
// For entering animation, only animate |window1|, |window2|, |window3| and
// |window5| because |window3| and |window5| are AlwaysOnTop windows and
// |window2| is fullscreen.
EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window3->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window4->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window5->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window6->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window7->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window8->layer()->GetAnimator()->is_animating());
base::RunLoop().RunUntilIdle();
// Click on |window1| to activate it and exit overview.
// Should animate |window1|, |window2|, |window3| and |window5| because
// |window3| and |window5| are AlwaysOnTop windows and |window2| is
// fullscreen.
ClickWindow(window1.get());
EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window3->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window4->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window5->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window6->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window7->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window8->layer()->GetAnimator()->is_animating());
base::RunLoop().RunUntilIdle();
// Case 2: Click on |window3| to activate it and exit overview.
// Should animate |window1|, |window2|, |window3| and |window5|.
// Reset window z-order. Need to toggle fullscreen first to workaround
// https://crbug.com/816224.
wm::GetWindowState(window2.get())->OnWMEvent(&toggle_fullscreen_event);
wm::GetWindowState(window7.get())->OnWMEvent(&toggle_fullscreen_event);
::wm::ActivateWindow(window8.get());
::wm::ActivateWindow(window7.get()); // Will be fullscreen.
::wm::ActivateWindow(window6.get()); // Maximized.
::wm::ActivateWindow(window5.get()); // AlwaysOnTop window.
::wm::ActivateWindow(window4.get());
::wm::ActivateWindow(window3.get()); // AlwaysOnTop window.
::wm::ActivateWindow(window2.get()); // Will be fullscreen.
::wm::ActivateWindow(window1.get());
wm::GetWindowState(window2.get())->OnWMEvent(&toggle_fullscreen_event);
wm::GetWindowState(window7.get())->OnWMEvent(&toggle_fullscreen_event);
// Enter overview.
test_duration_mode = std::make_unique<ui::ScopedAnimationDurationScaleMode>(
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
ToggleOverview();
base::RunLoop().RunUntilIdle();
test_duration_mode = std::make_unique<ui::ScopedAnimationDurationScaleMode>(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
ClickWindow(window3.get());
EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window3->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window4->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window5->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window6->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window7->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window8->layer()->GetAnimator()->is_animating());
base::RunLoop().RunUntilIdle();
// Case 3: Click on maximized |window6| to activate it and exit overview.
// Should animate |window6|, |window3| and |window5| because |window3| and
// |window5| are AlwaysOnTop windows. |window6| is maximized.
// Reset window z-order. Need to toggle fullscreen first to workaround
// https://crbug.com/816224.
wm::GetWindowState(window2.get())->OnWMEvent(&toggle_fullscreen_event);
wm::GetWindowState(window7.get())->OnWMEvent(&toggle_fullscreen_event);
::wm::ActivateWindow(window8.get());
::wm::ActivateWindow(window7.get()); // Will be fullscreen.
::wm::ActivateWindow(window6.get()); // Maximized.
::wm::ActivateWindow(window5.get()); // AlwaysOnTop window.
::wm::ActivateWindow(window4.get());
::wm::ActivateWindow(window3.get()); // AlwaysOnTop window.
::wm::ActivateWindow(window2.get()); // Will be fullscreen.
::wm::ActivateWindow(window1.get());
wm::GetWindowState(window2.get())->OnWMEvent(&toggle_fullscreen_event);
wm::GetWindowState(window7.get())->OnWMEvent(&toggle_fullscreen_event);
// Enter overview.
test_duration_mode = std::make_unique<ui::ScopedAnimationDurationScaleMode>(
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
ToggleOverview();
base::RunLoop().RunUntilIdle();
test_duration_mode = std::make_unique<ui::ScopedAnimationDurationScaleMode>(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
ClickWindow(window6.get());
EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window2->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window3->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window4->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window5->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window6->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window7->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window8->layer()->GetAnimator()->is_animating());
base::RunLoop().RunUntilIdle();
// Case 4: Click on |window8| to activate it and exit overview.
// Should animate |window8|, |window1|, |window2|, |window3| and |window5|
// because |window3| and |window5| are AlwaysOnTop windows and |window2| is
// fullscreen.
// Reset window z-order. Need to toggle fullscreen first to workaround
// https://crbug.com/816224.
wm::GetWindowState(window2.get())->OnWMEvent(&toggle_fullscreen_event);
wm::GetWindowState(window7.get())->OnWMEvent(&toggle_fullscreen_event);
::wm::ActivateWindow(window8.get());
::wm::ActivateWindow(window7.get()); // Will be fullscreen.
::wm::ActivateWindow(window6.get()); // Maximized.
::wm::ActivateWindow(window5.get()); // AlwaysOnTop window.
::wm::ActivateWindow(window4.get());
::wm::ActivateWindow(window3.get()); // AlwaysOnTop window.
::wm::ActivateWindow(window2.get()); // Will be fullscreen.
::wm::ActivateWindow(window1.get());
wm::GetWindowState(window2.get())->OnWMEvent(&toggle_fullscreen_event);
wm::GetWindowState(window7.get())->OnWMEvent(&toggle_fullscreen_event);
// Enter overview.
test_duration_mode = std::make_unique<ui::ScopedAnimationDurationScaleMode>(
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
ToggleOverview();
base::RunLoop().RunUntilIdle();
test_duration_mode = std::make_unique<ui::ScopedAnimationDurationScaleMode>(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
ClickWindow(window8.get());
EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window3->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window4->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window5->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window6->layer()->GetAnimator()->is_animating());
EXPECT_FALSE(window7->layer()->GetAnimator()->is_animating());
EXPECT_TRUE(window8->layer()->GetAnimator()->is_animating());
base::RunLoop().RunUntilIdle();
}
// Verify that the selector item can animate after the item is dragged and
// released.
TEST_F(OverviewSessionTest, WindowItemCanAnimateOnDragRelease) {
base::HistogramTester histogram_tester;
UpdateDisplay("400x400");
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
::wm::ActivateWindow(window2.get());
::wm::ActivateWindow(window1.get());
EnterTabletMode();
ToggleOverview();
OverviewItem* item2 = GetWindowItemForWindow(0, window2.get());
// Drag |item2| in a way so that |window2| does not get activated.
ui::test::EventGenerator* generator = GetEventGenerator();
generator->MoveMouseTo(
gfx::ToRoundedPoint(item2->target_bounds().CenterPoint()));
generator->PressLeftButton();
base::RunLoop().RunUntilIdle();
generator->MoveMouseTo(gfx::Point(200, 200));
histogram_tester.ExpectTotalCount(
"Ash.Overview.WindowDrag.PresentationTime.TabletMode", 1);
histogram_tester.ExpectTotalCount(
"Ash.Overview.WindowDrag.PresentationTime.MaxLatency.TabletMode", 0);
ui::ScopedAnimationDurationScaleMode test_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
generator->ReleaseLeftButton();
EXPECT_TRUE(window2->layer()->GetAnimator()->IsAnimatingProperty(
ui::LayerAnimationElement::AnimatableProperty::TRANSFORM));
base::RunLoop().RunUntilIdle();
histogram_tester.ExpectTotalCount(
"Ash.Overview.WindowDrag.PresentationTime.TabletMode", 1);
histogram_tester.ExpectTotalCount(
"Ash.Overview.WindowDrag.PresentationTime.MaxLatency.TabletMode", 1);
}
// Verify that the overview items titlebar and close button change visibility
// when a item is being dragged.
TEST_F(OverviewSessionTest, WindowItemTitleCloseVisibilityOnDrag) {
base::HistogramTester histogram_tester;
UpdateDisplay("400x400");
std::unique_ptr<aura::Window> window1(CreateTestWindow());
std::unique_ptr<aura::Window> window2(CreateTestWindow());
EnterTabletMode();
ToggleOverview();
OverviewItem* item1 = GetWindowItemForWindow(0, window1.get());
OverviewItem* item2 = GetWindowItemForWindow(0, window2.get());
// Start the drag on |item1|. Verify the dragged item, |item1| has both the
// close button and titlebar hidden. The close button opacity however is
// opaque as its a child of the header which handles fading away the whole
// header. All other items, |item2| should only have the close button hidden.
ui::test::EventGenerator* generator = GetEventGenerator();