1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/wm/workspace_controller.h"
9 #include "ash/ash_switches.h"
10 #include "ash/root_window_controller.h"
11 #include "ash/screen_util.h"
12 #include "ash/shelf/shelf_layout_manager.h"
13 #include "ash/shelf/shelf_widget.h"
14 #include "ash/shell.h"
15 #include "ash/shell_window_ids.h"
16 #include "ash/system/status_area_widget.h"
17 #include "ash/test/ash_test_base.h"
18 #include "ash/test/shell_test_api.h"
19 #include "ash/test/test_shelf_delegate.h"
20 #include "ash/wm/panels/panel_layout_manager.h"
21 #include "ash/wm/window_state.h"
22 #include "ash/wm/window_util.h"
23 #include "base/command_line.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "ui/aura/client/aura_constants.h"
26 #include "ui/aura/root_window.h"
27 #include "ui/aura/test/event_generator.h"
28 #include "ui/aura/test/test_window_delegate.h"
29 #include "ui/aura/test/test_windows.h"
30 #include "ui/aura/window.h"
31 #include "ui/base/hit_test.h"
32 #include "ui/base/ui_base_types.h"
33 #include "ui/compositor/layer.h"
34 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
35 #include "ui/events/event_utils.h"
36 #include "ui/gfx/screen.h"
37 #include "ui/views/corewm/window_animations.h"
38 #include "ui/views/corewm/window_util.h"
39 #include "ui/views/widget/widget.h"
46 // Returns a string containing the names of all the children of |window| (in
47 // order). Each entry is separated by a space.
48 std::string GetWindowNames(const aura::Window* window) {
50 for (size_t i = 0; i < window->children().size(); ++i) {
53 result += window->children()[i]->name();
58 // Returns a string containing the names of windows corresponding to each of the
59 // child layers of |window|'s layer. Any layers that don't correspond to a child
60 // Window of |window| are ignored. The result is ordered based on the layer
62 std::string GetLayerNames(const aura::Window* window) {
63 typedef std::map<const ui::Layer*, std::string> LayerToWindowNameMap;
64 LayerToWindowNameMap window_names;
65 for (size_t i = 0; i < window->children().size(); ++i) {
66 window_names[window->children()[i]->layer()] =
67 window->children()[i]->name();
71 const std::vector<ui::Layer*>& layers(window->layer()->children());
72 for (size_t i = 0; i < layers.size(); ++i) {
73 LayerToWindowNameMap::iterator layer_i =
74 window_names.find(layers[i]);
75 if (layer_i != window_names.end()) {
78 result += layer_i->second;
84 class WorkspaceControllerTest : public test::AshTestBase {
86 WorkspaceControllerTest() {}
87 virtual ~WorkspaceControllerTest() {}
89 aura::Window* CreateTestWindowUnparented() {
90 aura::Window* window = new aura::Window(NULL);
91 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
92 window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
93 window->Init(aura::WINDOW_LAYER_TEXTURED);
97 aura::Window* CreateTestWindow() {
98 aura::Window* window = new aura::Window(NULL);
99 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
100 window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
101 window->Init(aura::WINDOW_LAYER_TEXTURED);
102 ParentWindowInPrimaryRootWindow(window);
106 aura::Window* CreateBrowserLikeWindow(const gfx::Rect& bounds) {
107 aura::Window* window = CreateTestWindow();
108 window->SetBounds(bounds);
109 wm::WindowState* window_state = wm::GetWindowState(window);
110 window_state->set_window_position_managed(true);
115 aura::Window* CreatePopupLikeWindow(const gfx::Rect& bounds) {
116 aura::Window* window = CreateTestWindowInShellWithBounds(bounds);
121 aura::Window* CreateTestPanel(aura::WindowDelegate* delegate,
122 const gfx::Rect& bounds) {
123 aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
125 ui::wm::WINDOW_TYPE_PANEL,
128 test::TestShelfDelegate* shelf_delegate =
129 test::TestShelfDelegate::instance();
130 shelf_delegate->AddShelfItem(window);
131 PanelLayoutManager* manager =
132 static_cast<PanelLayoutManager*>(
133 Shell::GetContainer(window->GetRootWindow(),
134 internal::kShellWindowId_PanelContainer)->
140 aura::Window* GetDesktop() {
141 return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
142 kShellWindowId_DefaultContainer);
145 gfx::Rect GetFullscreenBounds(aura::Window* window) {
146 return Shell::GetScreen()->GetDisplayNearestWindow(window).bounds();
149 ShelfWidget* shelf_widget() {
150 return Shell::GetPrimaryRootWindowController()->shelf();
153 ShelfLayoutManager* shelf_layout_manager() {
154 return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
157 bool GetWindowOverlapsShelf() {
158 return shelf_layout_manager()->window_overlaps_shelf();
162 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTest);
165 // Assertions around adding a normal window.
166 TEST_F(WorkspaceControllerTest, AddNormalWindowWhenEmpty) {
167 scoped_ptr<Window> w1(CreateTestWindow());
168 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
170 wm::WindowState* window_state = wm::GetWindowState(w1.get());
172 EXPECT_FALSE(window_state->HasRestoreBounds());
176 EXPECT_FALSE(window_state->HasRestoreBounds());
178 ASSERT_TRUE(w1->layer() != NULL);
179 EXPECT_TRUE(w1->layer()->visible());
181 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
183 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
186 // Assertions around maximizing/unmaximizing.
187 TEST_F(WorkspaceControllerTest, SingleMaximizeWindow) {
188 scoped_ptr<Window> w1(CreateTestWindow());
189 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
192 wm::ActivateWindow(w1.get());
194 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
196 ASSERT_TRUE(w1->layer() != NULL);
197 EXPECT_TRUE(w1->layer()->visible());
199 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
201 // Maximize the window.
202 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
204 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
206 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
207 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).width(),
208 w1->bounds().width());
209 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).height(),
210 w1->bounds().height());
212 // Restore the window.
213 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
215 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
216 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
219 // Assertions around two windows and toggling one to be fullscreen.
220 TEST_F(WorkspaceControllerTest, FullscreenWithNormalWindow) {
221 scoped_ptr<Window> w1(CreateTestWindow());
222 scoped_ptr<Window> w2(CreateTestWindow());
223 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
226 ASSERT_TRUE(w1->layer() != NULL);
227 EXPECT_TRUE(w1->layer()->visible());
229 w2->SetBounds(gfx::Rect(0, 0, 50, 51));
230 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
232 wm::ActivateWindow(w2.get());
234 // Both windows should be in the same workspace.
235 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
236 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
239 ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()));
240 EXPECT_EQ(work_area.width(), w2->bounds().width());
241 EXPECT_EQ(work_area.height(), w2->bounds().height());
243 // Restore w2, which should then go back to one workspace.
244 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
245 EXPECT_EQ(50, w2->bounds().width());
246 EXPECT_EQ(51, w2->bounds().height());
247 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
250 // Makes sure requests to change the bounds of a normal window go through.
251 TEST_F(WorkspaceControllerTest, ChangeBoundsOfNormalWindow) {
252 scoped_ptr<Window> w1(CreateTestWindow());
255 // Setting the bounds should go through since the window is in the normal
257 w1->SetBounds(gfx::Rect(0, 0, 200, 500));
258 EXPECT_EQ(200, w1->bounds().width());
259 EXPECT_EQ(500, w1->bounds().height());
262 // Verifies the bounds is not altered when showing and grid is enabled.
263 TEST_F(WorkspaceControllerTest, SnapToGrid) {
264 scoped_ptr<Window> w1(CreateTestWindowUnparented());
265 w1->SetBounds(gfx::Rect(1, 6, 25, 30));
266 ParentWindowInPrimaryRootWindow(w1.get());
267 // We are not aligning this anymore this way. When the window gets shown
268 // the window is expected to be handled differently, but this cannot be
269 // tested with this test. So the result of this test should be that the
270 // bounds are exactly as passed in.
271 EXPECT_EQ("1,6 25x30", w1->bounds().ToString());
274 // Assertions around a fullscreen window.
275 TEST_F(WorkspaceControllerTest, SingleFullscreenWindow) {
276 scoped_ptr<Window> w1(CreateTestWindow());
277 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
278 // Make the window fullscreen.
279 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
281 wm::ActivateWindow(w1.get());
283 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
284 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
285 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
287 // Restore the window. Use SHOW_STATE_DEFAULT as that is what we'll end up
288 // with when using views::Widget.
289 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT);
290 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
292 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
293 EXPECT_EQ(250, w1->bounds().width());
294 EXPECT_EQ(251, w1->bounds().height());
296 // Back to fullscreen.
297 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
298 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
299 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
300 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
301 wm::WindowState* window_state = wm::GetWindowState(w1.get());
303 ASSERT_TRUE(window_state->HasRestoreBounds());
304 EXPECT_EQ("0,0 250x251", window_state->GetRestoreBoundsInScreen().ToString());
307 // Assertions around minimizing a single window.
308 TEST_F(WorkspaceControllerTest, MinimizeSingleWindow) {
309 scoped_ptr<Window> w1(CreateTestWindow());
313 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
314 EXPECT_FALSE(w1->layer()->IsDrawn());
318 EXPECT_TRUE(wm::GetWindowState(w1.get())->IsNormalShowState());
319 EXPECT_TRUE(w1->layer()->IsDrawn());
322 // Assertions around minimizing a fullscreen window.
323 TEST_F(WorkspaceControllerTest, MinimizeFullscreenWindow) {
324 // Two windows, w1 normal, w2 fullscreen.
325 scoped_ptr<Window> w1(CreateTestWindow());
326 scoped_ptr<Window> w2(CreateTestWindow());
328 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
331 wm::WindowState* w1_state = wm::GetWindowState(w1.get());
332 wm::WindowState* w2_state = wm::GetWindowState(w2.get());
334 w2_state->Activate();
337 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
338 EXPECT_TRUE(w1->layer()->IsDrawn());
339 EXPECT_FALSE(w2->layer()->IsDrawn());
341 // Show the window, which should trigger unminimizing.
343 w2_state->Activate();
345 EXPECT_TRUE(w2_state->IsFullscreen());
346 EXPECT_TRUE(w1->layer()->IsDrawn());
347 EXPECT_TRUE(w2->layer()->IsDrawn());
349 // Minimize the window, which should hide the window.
350 EXPECT_TRUE(w2_state->IsActive());
351 w2_state->Minimize();
352 EXPECT_FALSE(w2_state->IsActive());
353 EXPECT_FALSE(w2->layer()->IsDrawn());
354 EXPECT_TRUE(w1_state->IsActive());
356 // Make the window normal.
357 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
358 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
359 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
360 EXPECT_TRUE(w2->layer()->IsDrawn());
363 // Verifies ShelfLayoutManager's visibility/auto-hide state is correctly
365 TEST_F(WorkspaceControllerTest, ShelfStateUpdated) {
366 // Since ShelfLayoutManager queries for mouse location, move the mouse so
367 // it isn't over the shelf.
368 aura::test::EventGenerator generator(
369 Shell::GetPrimaryRootWindow(), gfx::Point());
370 generator.MoveMouseTo(0, 0);
372 scoped_ptr<Window> w1(CreateTestWindow());
373 const gfx::Rect w1_bounds(0, 1, 101, 102);
374 ShelfLayoutManager* shelf = shelf_layout_manager();
375 shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
376 const gfx::Rect touches_shelf_bounds(
377 0, shelf->GetIdealBounds().y() - 10, 101, 102);
378 // Move |w1| to overlap the shelf.
379 w1->SetBounds(touches_shelf_bounds);
380 EXPECT_FALSE(GetWindowOverlapsShelf());
382 // A visible ignored window should not trigger the overlap.
383 scoped_ptr<Window> w_ignored(CreateTestWindow());
384 w_ignored->SetBounds(touches_shelf_bounds);
385 wm::GetWindowState(&(*w_ignored))->set_ignored_by_shelf(true);
387 EXPECT_FALSE(GetWindowOverlapsShelf());
389 // Make it visible, since visible shelf overlaps should be true.
391 EXPECT_TRUE(GetWindowOverlapsShelf());
393 wm::ActivateWindow(w1.get());
394 w1->SetBounds(w1_bounds);
396 wm::ActivateWindow(w1.get());
398 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
400 // Maximize the window.
401 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
402 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
403 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
406 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
407 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
408 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
411 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
412 EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
415 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
416 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
417 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
418 EXPECT_FALSE(GetWindowOverlapsShelf());
420 // Move window so it obscures shelf.
421 w1->SetBounds(touches_shelf_bounds);
422 EXPECT_TRUE(GetWindowOverlapsShelf());
425 w1->SetBounds(w1_bounds);
426 EXPECT_FALSE(GetWindowOverlapsShelf());
429 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
430 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
431 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
434 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
435 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
437 // Since the restore from minimize will restore to the pre-minimize
438 // state (tested elsewhere), we abandon the current size and restore
439 // rect and set them to the window.
440 wm::WindowState* window_state = wm::GetWindowState(w1.get());
442 gfx::Rect restore = window_state->GetRestoreBoundsInScreen();
443 EXPECT_EQ("0,0 800x597", w1->bounds().ToString());
444 EXPECT_EQ("0,1 101x102", restore.ToString());
445 window_state->ClearRestoreBounds();
446 w1->SetBounds(restore);
449 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
450 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
451 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
453 // Create another window, maximized.
454 scoped_ptr<Window> w2(CreateTestWindow());
455 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
456 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
458 wm::ActivateWindow(w2.get());
459 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
460 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
461 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
464 wm::ActivateWindow(w1.get());
465 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
466 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
467 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(
468 w2->parent()).ToString(),
469 w2->bounds().ToString());
472 wm::ActivateWindow(w2.get());
473 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
474 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
475 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
476 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w2.get()).ToString(),
477 w2->bounds().ToString());
479 // Turn off auto-hide, switch back to w2 (maximized) and verify overlap.
480 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
481 wm::ActivateWindow(w2.get());
482 EXPECT_FALSE(GetWindowOverlapsShelf());
484 // Move w1 to overlap shelf, it shouldn't change window overlaps shelf since
485 // the window isn't in the visible workspace.
486 w1->SetBounds(touches_shelf_bounds);
487 EXPECT_FALSE(GetWindowOverlapsShelf());
489 // Activate w1. Although w1 is visible, the overlap state is still false since
491 wm::ActivateWindow(w1.get());
492 EXPECT_FALSE(GetWindowOverlapsShelf());
495 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
496 EXPECT_TRUE(GetWindowOverlapsShelf());
499 // Verifies going from maximized to minimized sets the right state for painting
500 // the background of the launcher.
501 TEST_F(WorkspaceControllerTest, MinimizeResetsVisibility) {
502 scoped_ptr<Window> w1(CreateTestWindow());
504 wm::ActivateWindow(w1.get());
505 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
506 EXPECT_EQ(SHELF_BACKGROUND_MAXIMIZED, shelf_widget()->GetBackgroundType());
508 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
509 EXPECT_EQ(SHELF_VISIBLE,
510 shelf_layout_manager()->visibility_state());
511 EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, shelf_widget()->GetBackgroundType());
514 // Verifies window visibility during various workspace changes.
515 TEST_F(WorkspaceControllerTest, VisibilityTests) {
516 scoped_ptr<Window> w1(CreateTestWindow());
518 EXPECT_TRUE(w1->IsVisible());
519 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
521 // Create another window, activate it and make it fullscreen.
522 scoped_ptr<Window> w2(CreateTestWindow());
524 wm::ActivateWindow(w2.get());
525 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
526 EXPECT_TRUE(w2->IsVisible());
527 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
528 EXPECT_TRUE(w1->IsVisible());
530 // Switch to w1. |w1| should be visible on top of |w2|.
531 wm::ActivateWindow(w1.get());
532 EXPECT_TRUE(w1->IsVisible());
533 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
534 EXPECT_TRUE(w2->IsVisible());
536 // Switch back to |w2|.
537 wm::ActivateWindow(w2.get());
538 EXPECT_TRUE(w2->IsVisible());
539 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
540 EXPECT_TRUE(w1->IsVisible());
542 // Restore |w2|, both windows should be visible.
543 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
544 EXPECT_TRUE(w1->IsVisible());
545 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
546 EXPECT_TRUE(w2->IsVisible());
547 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
549 // Make |w2| fullscreen again, then close it.
550 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
552 EXPECT_FALSE(w2->IsVisible());
553 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
554 EXPECT_TRUE(w1->IsVisible());
556 // Create |w2| and maximize it.
557 w2.reset(CreateTestWindow());
559 wm::ActivateWindow(w2.get());
560 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
561 EXPECT_TRUE(w2->IsVisible());
562 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
563 EXPECT_TRUE(w1->IsVisible());
567 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
568 EXPECT_TRUE(w1->IsVisible());
571 // Verifies windows that are offscreen don't move when switching workspaces.
572 TEST_F(WorkspaceControllerTest, DontMoveOnSwitch) {
573 aura::test::EventGenerator generator(
574 Shell::GetPrimaryRootWindow(), gfx::Point());
575 generator.MoveMouseTo(0, 0);
577 scoped_ptr<Window> w1(CreateTestWindow());
578 ShelfLayoutManager* shelf = shelf_layout_manager();
579 const gfx::Rect touches_shelf_bounds(
580 0, shelf->GetIdealBounds().y() - 10, 101, 102);
581 // Move |w1| to overlap the shelf.
582 w1->SetBounds(touches_shelf_bounds);
584 wm::ActivateWindow(w1.get());
586 // Create another window and maximize it.
587 scoped_ptr<Window> w2(CreateTestWindow());
588 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
589 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
591 wm::ActivateWindow(w2.get());
594 wm::ActivateWindow(w1.get());
595 EXPECT_EQ(touches_shelf_bounds.ToString(), w1->bounds().ToString());
598 // Verifies that windows that are completely offscreen move when switching
600 TEST_F(WorkspaceControllerTest, MoveOnSwitch) {
601 aura::test::EventGenerator generator(
602 Shell::GetPrimaryRootWindow(), gfx::Point());
603 generator.MoveMouseTo(0, 0);
605 scoped_ptr<Window> w1(CreateTestWindow());
606 ShelfLayoutManager* shelf = shelf_layout_manager();
607 const gfx::Rect w1_bounds(0, shelf->GetIdealBounds().y(), 100, 200);
608 // Move |w1| so that the top edge is the same as the top edge of the shelf.
609 w1->SetBounds(w1_bounds);
611 wm::ActivateWindow(w1.get());
612 EXPECT_EQ(w1_bounds.ToString(), w1->bounds().ToString());
614 // Create another window and maximize it.
615 scoped_ptr<Window> w2(CreateTestWindow());
616 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
617 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
619 wm::ActivateWindow(w2.get());
621 // Increase the size of the WorkAreaInsets. This would make |w1| fall
622 // completely out of the display work area.
624 Shell::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets();
625 insets.Set(0, 0, insets.bottom() + 30, 0);
626 Shell::GetInstance()->SetDisplayWorkAreaInsets(w1.get(), insets);
628 // Switch to w1. The window should have moved.
629 wm::ActivateWindow(w1.get());
630 EXPECT_NE(w1_bounds.ToString(), w1->bounds().ToString());
635 // WindowDelegate used by DontCrashOnChangeAndActivate.
636 class DontCrashOnChangeAndActivateDelegate
637 : public aura::test::TestWindowDelegate {
639 DontCrashOnChangeAndActivateDelegate() : window_(NULL) {}
641 void set_window(aura::Window* window) { window_ = window; }
643 // WindowDelegate overrides:
644 virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
645 const gfx::Rect& new_bounds) OVERRIDE {
647 wm::ActivateWindow(window_);
653 aura::Window* window_;
655 DISALLOW_COPY_AND_ASSIGN(DontCrashOnChangeAndActivateDelegate);
660 // Exercises possible crash in W2. Here's the sequence:
661 // . minimize a maximized window.
662 // . remove the window (which happens when switching displays).
663 // . add the window back.
664 // . show the window and during the bounds change activate it.
665 TEST_F(WorkspaceControllerTest, DontCrashOnChangeAndActivate) {
667 ShelfLayoutManager* shelf = shelf_layout_manager();
668 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
670 DontCrashOnChangeAndActivateDelegate delegate;
671 scoped_ptr<Window> w1(CreateTestWindowInShellWithDelegate(
672 &delegate, 1000, gfx::Rect(10, 11, 250, 251)));
675 wm::WindowState* w1_state = wm::GetWindowState(w1.get());
676 w1_state->Activate();
677 w1_state->Maximize();
678 w1_state->Minimize();
680 w1->parent()->RemoveChild(w1.get());
682 // Do this so that when we Show() the window a resize occurs and we make the
684 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
686 ParentWindowInPrimaryRootWindow(w1.get());
687 delegate.set_window(w1.get());
691 // Verifies a window with a transient parent not managed by workspace works.
692 TEST_F(WorkspaceControllerTest, TransientParent) {
693 // Normal window with no transient parent.
694 scoped_ptr<Window> w2(CreateTestWindow());
695 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
697 wm::ActivateWindow(w2.get());
699 // Window with a transient parent. We set the transient parent to the root,
700 // which would never happen but is enough to exercise the bug.
701 scoped_ptr<Window> w1(CreateTestWindowUnparented());
702 views::corewm::AddTransientChild(
703 Shell::GetInstance()->GetPrimaryRootWindow(), w1.get());
704 w1->SetBounds(gfx::Rect(10, 11, 250, 251));
705 ParentWindowInPrimaryRootWindow(w1.get());
707 wm::ActivateWindow(w1.get());
709 // The window with the transient parent should get added to the same parent as
710 // the normal window.
711 EXPECT_EQ(w2->parent(), w1->parent());
714 // Test the placement of newly created windows.
715 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnCreate) {
716 if (!SupportsHostWindowResize())
718 UpdateDisplay("1600x1200");
719 // Creating a popup handler here to make sure it does not interfere with the
721 gfx::Rect source_browser_bounds(16, 32, 640, 320);
722 scoped_ptr<aura::Window> browser_window(CreateBrowserLikeWindow(
723 source_browser_bounds));
725 // Creating a popup to make sure it does not interfere with the positioning.
726 scoped_ptr<aura::Window> browser_popup(CreatePopupLikeWindow(
727 gfx::Rect(16, 32, 128, 256)));
729 browser_window->Show();
730 browser_popup->Show();
732 { // With a shown window it's size should get returned.
733 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
734 source_browser_bounds));
735 // The position should be right flush.
736 EXPECT_EQ("960,32 640x320", new_browser_window->bounds().ToString());
739 { // With the window shown - but more on the right side then on the left
740 // side (and partially out of the screen), it should default to the other
741 // side and inside the screen.
742 gfx::Rect source_browser_bounds(gfx::Rect(1000, 600, 640, 320));
743 browser_window->SetBounds(source_browser_bounds);
745 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
746 source_browser_bounds));
747 // The position should be left & bottom flush.
748 EXPECT_EQ("0,600 640x320", new_browser_window->bounds().ToString());
750 // If the other window was already beyond the point to get right flush
751 // it will remain where it is.
752 EXPECT_EQ("1000,600 640x320", browser_window->bounds().ToString());
755 { // Make sure that popups do not get changed.
756 scoped_ptr<aura::Window> new_popup_window(CreatePopupLikeWindow(
757 gfx::Rect(50, 100, 300, 150)));
758 EXPECT_EQ("50,100 300x150", new_popup_window->bounds().ToString());
761 browser_window->Hide();
762 { // If a window is there but not shown the default should be centered.
763 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
764 gfx::Rect(50, 100, 300, 150)));
765 EXPECT_EQ("650,100 300x150", new_browser_window->bounds().ToString());
769 // Test the basic auto placement of one and or two windows in a "simulated
770 // session" of sequential window operations.
771 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnShowHide) {
772 // Test 1: In case there is no manageable window, no window should shift.
774 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
775 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
776 gfx::Rect desktop_area = window1->parent()->bounds();
778 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
779 // Trigger the auto window placement function by making it visible.
780 // Note that the bounds are getting changed while it is invisible.
782 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
785 // Check the initial position of the windows is unchanged.
786 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
787 EXPECT_EQ("32,48 256x512", window2->bounds().ToString());
789 // Remove the second window and make sure that the first window
790 // does NOT get centered.
792 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
794 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
795 // Test 2: Set up two managed windows and check their auto positioning.
796 window1_state->set_window_position_managed(true);
798 scoped_ptr<aura::Window> window3(CreateTestWindowInShellWithId(2));
799 wm::GetWindowState(window3.get())->set_window_position_managed(true);
800 // To avoid any auto window manager changes due to SetBounds, the window
801 // gets first hidden and then shown again.
803 window3->SetBounds(gfx::Rect(32, 48, 256, 512));
805 // |window1| should be flush left and |window3| flush right.
806 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
807 EXPECT_EQ(base::IntToString(
808 desktop_area.width() - window3->bounds().width()) +
809 ",48 256x512", window3->bounds().ToString());
811 // After removing |window3|, |window1| should be centered again.
815 (desktop_area.width() - window1->bounds().width()) / 2) +
816 ",32 640x320", window1->bounds().ToString());
818 // Test 3: Set up a manageable and a non manageable window and check
820 scoped_ptr<aura::Window> window4(CreateTestWindowInShellWithId(3));
821 // To avoid any auto window manager changes due to SetBounds, the window
822 // gets first hidden and then shown again.
824 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
825 window4->SetBounds(gfx::Rect(32, 48, 256, 512));
827 // |window1| should be centered and |window4| untouched.
830 (desktop_area.width() - window1->bounds().width()) / 2) +
831 ",32 640x320", window1->bounds().ToString());
832 EXPECT_EQ("32,48 256x512", window4->bounds().ToString());
834 // Test4: A single manageable window should get centered.
836 window1_state->set_bounds_changed_by_user(false);
837 // Trigger the auto window placement function by showing (and hiding) it.
840 // |window1| should be centered.
843 (desktop_area.width() - window1->bounds().width()) / 2) +
844 ",32 640x320", window1->bounds().ToString());
847 // Test the proper usage of user window movement interaction.
848 TEST_F(WorkspaceControllerTest, TestUserMovedWindowRepositioning) {
849 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
850 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
851 gfx::Rect desktop_area = window1->parent()->bounds();
852 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
853 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
856 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
857 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
859 window1_state->set_window_position_managed(true);
860 window2_state->set_window_position_managed(true);
861 EXPECT_FALSE(window1_state->bounds_changed_by_user());
862 EXPECT_FALSE(window2_state->bounds_changed_by_user());
864 // Check that the current location gets preserved if the user has
865 // positioned it previously.
866 window1_state->set_bounds_changed_by_user(true);
868 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
869 // Flag should be still set.
870 EXPECT_TRUE(window1_state->bounds_changed_by_user());
871 EXPECT_FALSE(window2_state->bounds_changed_by_user());
873 // Turn on the second window and make sure that both windows are now
874 // positionable again (user movement cleared).
877 // |window1| should be flush left and |window2| flush right.
878 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
880 base::IntToString(desktop_area.width() - window2->bounds().width()) +
881 ",48 256x512", window2->bounds().ToString());
882 // FLag should now be reset.
883 EXPECT_FALSE(window1_state->bounds_changed_by_user());
884 EXPECT_FALSE(window2_state->bounds_changed_by_user());
886 // Going back to one shown window should keep the state.
887 window1_state->set_bounds_changed_by_user(true);
889 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
890 EXPECT_TRUE(window1_state->bounds_changed_by_user());
893 // Test if the single window will be restored at original position.
894 TEST_F(WorkspaceControllerTest, TestSingleWindowsRestoredBounds) {
895 scoped_ptr<aura::Window> window1(
896 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
897 scoped_ptr<aura::Window> window2(
898 CreateTestWindowInShellWithBounds(gfx::Rect(110, 110, 100, 100)));
899 scoped_ptr<aura::Window> window3(
900 CreateTestWindowInShellWithBounds(gfx::Rect(120, 120, 100, 100)));
904 wm::GetWindowState(window1.get())->set_window_position_managed(true);
905 wm::GetWindowState(window2.get())->set_window_position_managed(true);
906 wm::GetWindowState(window3.get())->set_window_position_managed(true);
909 wm::ActivateWindow(window1.get());
911 wm::ActivateWindow(window2.get());
913 wm::ActivateWindow(window3.get());
914 EXPECT_EQ(0, window1->bounds().x());
915 EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
916 window2->bounds().right());
917 EXPECT_EQ(0, window3->bounds().x());
920 EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
921 window2->bounds().right());
922 EXPECT_EQ(0, window3->bounds().x());
924 // Being a single window will retore the original location.
926 wm::ActivateWindow(window2.get());
927 EXPECT_EQ("110,110 100x100", window2->bounds().ToString());
929 // Showing the 3rd will push the 2nd window left.
931 wm::ActivateWindow(window3.get());
932 EXPECT_EQ(0, window2->bounds().x());
933 EXPECT_EQ(window3->GetRootWindow()->bounds().right(),
934 window3->bounds().right());
936 // Being a single window will retore the original location.
938 EXPECT_EQ("120,120 100x100", window3->bounds().ToString());
941 // Test that user placed windows go back to their user placement after the user
942 // closes all other windows.
943 TEST_F(WorkspaceControllerTest, TestUserHandledWindowRestore) {
944 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
945 gfx::Rect user_pos = gfx::Rect(16, 42, 640, 320);
946 window1->SetBounds(user_pos);
947 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
949 window1_state->SetPreAutoManageWindowBounds(user_pos);
950 gfx::Rect desktop_area = window1->parent()->bounds();
952 // Create a second window to let the auto manager kick in.
953 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
954 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
957 wm::GetWindowState(window1.get())->set_window_position_managed(true);
958 wm::GetWindowState(window2.get())->set_window_position_managed(true);
960 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
963 // |window1| should be flush left and |window2| flush right.
964 EXPECT_EQ("0," + base::IntToString(user_pos.y()) +
965 " 640x320", window1->bounds().ToString());
967 base::IntToString(desktop_area.width() - window2->bounds().width()) +
968 ",48 256x512", window2->bounds().ToString());
971 // After the other window get hidden the window has to move back to the
972 // previous position and the bounds should still be set and unchanged.
973 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
974 ASSERT_TRUE(window1_state->pre_auto_manage_window_bounds());
975 EXPECT_EQ(user_pos.ToString(),
976 window1_state->pre_auto_manage_window_bounds()->ToString());
979 // Test that a window from normal to minimize will repos the remaining.
980 TEST_F(WorkspaceControllerTest, ToMinimizeRepositionsRemaining) {
981 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
982 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
983 window1_state->set_window_position_managed(true);
984 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
985 gfx::Rect desktop_area = window1->parent()->bounds();
987 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
988 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
989 window2_state->set_window_position_managed(true);
990 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
992 window1_state->Minimize();
994 // |window2| should be centered now.
995 EXPECT_TRUE(window2->IsVisible());
996 EXPECT_TRUE(window2_state->IsNormalShowState());
997 EXPECT_EQ(base::IntToString(
998 (desktop_area.width() - window2->bounds().width()) / 2) +
999 ",48 256x512", window2->bounds().ToString());
1001 window1_state->Restore();
1002 // |window1| should be flush right and |window3| flush left.
1003 EXPECT_EQ(base::IntToString(
1004 desktop_area.width() - window1->bounds().width()) +
1005 ",32 640x320", window1->bounds().ToString());
1006 EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
1009 // Test that minimizing an initially maximized window will repos the remaining.
1010 TEST_F(WorkspaceControllerTest, MaxToMinRepositionsRemaining) {
1011 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1012 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1013 window1_state->set_window_position_managed(true);
1014 gfx::Rect desktop_area = window1->parent()->bounds();
1016 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1017 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
1018 window2_state->set_window_position_managed(true);
1019 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1021 window1_state->Maximize();
1022 window1_state->Minimize();
1024 // |window2| should be centered now.
1025 EXPECT_TRUE(window2->IsVisible());
1026 EXPECT_TRUE(window2_state->IsNormalShowState());
1027 EXPECT_EQ(base::IntToString(
1028 (desktop_area.width() - window2->bounds().width()) / 2) +
1029 ",48 256x512", window2->bounds().ToString());
1032 // Test that nomral, maximize, minimizing will repos the remaining.
1033 TEST_F(WorkspaceControllerTest, NormToMaxToMinRepositionsRemaining) {
1034 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1035 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1036 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1037 window1_state->set_window_position_managed(true);
1038 gfx::Rect desktop_area = window1->parent()->bounds();
1040 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1041 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
1042 window2_state->set_window_position_managed(true);
1043 window2->SetBounds(gfx::Rect(32, 40, 256, 512));
1045 // Trigger the auto window placement function by showing (and hiding) it.
1049 // |window1| should be flush right and |window3| flush left.
1050 EXPECT_EQ(base::IntToString(
1051 desktop_area.width() - window1->bounds().width()) +
1052 ",32 640x320", window1->bounds().ToString());
1053 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1055 window1_state->Maximize();
1056 window1_state->Minimize();
1058 // |window2| should be centered now.
1059 EXPECT_TRUE(window2->IsVisible());
1060 EXPECT_TRUE(window2_state->IsNormalShowState());
1061 EXPECT_EQ(base::IntToString(
1062 (desktop_area.width() - window2->bounds().width()) / 2) +
1063 ",40 256x512", window2->bounds().ToString());
1066 // Test that nomral, maximize, normal will repos the remaining.
1067 TEST_F(WorkspaceControllerTest, NormToMaxToNormRepositionsRemaining) {
1068 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1069 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1070 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1071 window1_state->set_window_position_managed(true);
1072 gfx::Rect desktop_area = window1->parent()->bounds();
1074 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1075 wm::GetWindowState(window2.get())->set_window_position_managed(true);
1076 window2->SetBounds(gfx::Rect(32, 40, 256, 512));
1078 // Trigger the auto window placement function by showing (and hiding) it.
1082 // |window1| should be flush right and |window3| flush left.
1083 EXPECT_EQ(base::IntToString(
1084 desktop_area.width() - window1->bounds().width()) +
1085 ",32 640x320", window1->bounds().ToString());
1086 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1088 window1_state->Maximize();
1089 window1_state->Restore();
1091 // |window1| should be flush right and |window2| flush left.
1092 EXPECT_EQ(base::IntToString(
1093 desktop_area.width() - window1->bounds().width()) +
1094 ",32 640x320", window1->bounds().ToString());
1095 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1098 // Test that animations are triggered.
1099 TEST_F(WorkspaceControllerTest, AnimatedNormToMaxToNormRepositionsRemaining) {
1100 ui::ScopedAnimationDurationScaleMode normal_duration_mode(
1101 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
1102 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1104 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1105 gfx::Rect desktop_area = window1->parent()->bounds();
1106 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1108 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1110 wm::GetWindowState(window1.get())->set_window_position_managed(true);
1111 wm::GetWindowState(window2.get())->set_window_position_managed(true);
1112 // Make sure nothing is animating.
1113 window1->layer()->GetAnimator()->StopAnimating();
1114 window2->layer()->GetAnimator()->StopAnimating();
1117 // The second window should now animate.
1118 EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating());
1119 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1120 window2->layer()->GetAnimator()->StopAnimating();
1123 EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
1124 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1126 window1->layer()->GetAnimator()->StopAnimating();
1127 window2->layer()->GetAnimator()->StopAnimating();
1128 // |window1| should be flush right and |window2| flush left.
1129 EXPECT_EQ(base::IntToString(
1130 desktop_area.width() - window1->bounds().width()) +
1131 ",32 640x320", window1->bounds().ToString());
1132 EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
1135 // This tests simulates a browser and an app and verifies the ordering of the
1136 // windows and layers doesn't get out of sync as various operations occur. Its
1137 // really testing code in FocusController, but easier to simulate here. Just as
1138 // with a real browser the browser here has a transient child window
1139 // (corresponds to the status bubble).
1140 TEST_F(WorkspaceControllerTest, VerifyLayerOrdering) {
1141 scoped_ptr<Window> browser(aura::test::CreateTestWindowWithDelegate(
1142 NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL));
1143 browser->SetName("browser");
1144 ParentWindowInPrimaryRootWindow(browser.get());
1146 wm::ActivateWindow(browser.get());
1148 // |status_bubble| is made a transient child of |browser| and as a result
1149 // owned by |browser|.
1150 aura::test::TestWindowDelegate* status_bubble_delegate =
1151 aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate();
1152 status_bubble_delegate->set_can_focus(false);
1153 Window* status_bubble =
1154 aura::test::CreateTestWindowWithDelegate(status_bubble_delegate,
1155 ui::wm::WINDOW_TYPE_POPUP,
1156 gfx::Rect(5, 6, 7, 8),
1158 views::corewm::AddTransientChild(browser.get(), status_bubble);
1159 ParentWindowInPrimaryRootWindow(status_bubble);
1160 status_bubble->SetName("status_bubble");
1162 scoped_ptr<Window> app(aura::test::CreateTestWindowWithDelegate(
1163 NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL));
1164 app->SetName("app");
1165 ParentWindowInPrimaryRootWindow(app.get());
1167 aura::Window* parent = browser->parent();
1170 wm::ActivateWindow(app.get());
1171 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1173 // Minimize the app, focus should go the browser.
1174 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
1175 EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
1176 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1178 // Minimize the browser (neither windows are focused).
1179 browser->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
1180 EXPECT_FALSE(wm::IsActiveWindow(browser.get()));
1181 EXPECT_FALSE(wm::IsActiveWindow(app.get()));
1182 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1184 // Show the browser (which should restore it).
1186 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1188 // Activate the browser.
1189 ash::wm::ActivateWindow(browser.get());
1190 EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
1191 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1193 // Restore the app. This differs from above code for |browser| as internally
1194 // the app code does this. Restoring this way or using Show() should not make
1196 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1197 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1199 // Activate the app.
1200 ash::wm::ActivateWindow(app.get());
1201 EXPECT_TRUE(wm::IsActiveWindow(app.get()));
1202 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1207 // Used by DragMaximizedNonTrackedWindow to track how many times the window
1208 // hierarchy changes affecting the specified window.
1209 class DragMaximizedNonTrackedWindowObserver
1210 : public aura::WindowObserver {
1212 DragMaximizedNonTrackedWindowObserver(aura::Window* window)
1217 // Number of times OnWindowHierarchyChanged() has been received.
1218 void clear_change_count() { change_count_ = 0; }
1219 int change_count() const {
1220 return change_count_;
1223 // aura::WindowObserver overrides:
1224 // Counts number of times a window is reparented. Ignores reparenting into and
1225 // from a docked container which is expected when a tab is dragged.
1226 virtual void OnWindowHierarchyChanged(
1227 const HierarchyChangeParams& params) OVERRIDE {
1228 if (params.target != window_ ||
1229 (params.old_parent->id() == kShellWindowId_DefaultContainer &&
1230 params.new_parent->id() == kShellWindowId_DockedContainer) ||
1231 (params.old_parent->id() == kShellWindowId_DockedContainer &&
1232 params.new_parent->id() == kShellWindowId_DefaultContainer)) {
1240 aura::Window* window_;
1242 DISALLOW_COPY_AND_ASSIGN(DragMaximizedNonTrackedWindowObserver);
1247 // Verifies that a new maximized window becomes visible after its activation
1248 // is requested, even though it does not become activated because a system
1249 // modal window is active.
1250 TEST_F(WorkspaceControllerTest, SwitchFromModal) {
1251 scoped_ptr<Window> modal_window(CreateTestWindowUnparented());
1252 modal_window->SetBounds(gfx::Rect(10, 11, 21, 22));
1253 modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
1254 ParentWindowInPrimaryRootWindow(modal_window.get());
1255 modal_window->Show();
1256 wm::ActivateWindow(modal_window.get());
1258 scoped_ptr<Window> maximized_window(CreateTestWindow());
1259 maximized_window->SetProperty(
1260 aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
1261 maximized_window->Show();
1262 wm::ActivateWindow(maximized_window.get());
1263 EXPECT_TRUE(maximized_window->IsVisible());
1268 // Subclass of WorkspaceControllerTest that runs tests with docked windows
1269 // enabled and disabled.
1270 class WorkspaceControllerTestDragging
1271 : public WorkspaceControllerTest,
1272 public testing::WithParamInterface<bool> {
1274 WorkspaceControllerTestDragging() {}
1275 virtual ~WorkspaceControllerTestDragging() {}
1278 virtual void SetUp() OVERRIDE {
1279 WorkspaceControllerTest::SetUp();
1280 if (!docked_windows_enabled()) {
1281 CommandLine::ForCurrentProcess()->AppendSwitch(
1282 ash::switches::kAshDisableDockedWindows);
1286 bool docked_windows_enabled() const { return GetParam(); }
1289 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTestDragging);
1294 // Verifies that when dragging a window over the shelf overlap is detected
1295 // during and after the drag.
1296 TEST_P(WorkspaceControllerTestDragging, DragWindowOverlapShelf) {
1297 aura::test::TestWindowDelegate delegate;
1298 delegate.set_window_component(HTCAPTION);
1299 scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate(
1300 &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL));
1301 ParentWindowInPrimaryRootWindow(w1.get());
1303 ShelfLayoutManager* shelf = shelf_layout_manager();
1304 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
1306 // Drag near the shelf.
1307 aura::test::EventGenerator generator(
1308 Shell::GetPrimaryRootWindow(), gfx::Point());
1309 generator.MoveMouseTo(10, 10);
1310 generator.PressLeftButton();
1311 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 70);
1313 // Shelf should not be in overlapped state.
1314 EXPECT_FALSE(GetWindowOverlapsShelf());
1316 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 20);
1318 // Shelf should detect overlap. Overlap state stays after mouse is released.
1319 EXPECT_TRUE(GetWindowOverlapsShelf());
1320 generator.ReleaseLeftButton();
1321 EXPECT_TRUE(GetWindowOverlapsShelf());
1324 // Verifies that when dragging a window autohidden shelf stays hidden during
1325 // and after the drag.
1326 TEST_P(WorkspaceControllerTestDragging, DragWindowKeepsShelfAutohidden) {
1327 aura::test::TestWindowDelegate delegate;
1328 delegate.set_window_component(HTCAPTION);
1329 scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate(
1330 &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL));
1331 ParentWindowInPrimaryRootWindow(w1.get());
1333 ShelfLayoutManager* shelf = shelf_layout_manager();
1334 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
1335 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
1337 // Drag very little.
1338 aura::test::EventGenerator generator(
1339 Shell::GetPrimaryRootWindow(), gfx::Point());
1340 generator.MoveMouseTo(10, 10);
1341 generator.PressLeftButton();
1342 generator.MoveMouseTo(12, 12);
1344 // Shelf should be hidden during and after the drag.
1345 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
1346 generator.ReleaseLeftButton();
1347 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
1350 INSTANTIATE_TEST_CASE_P(DockedOrNot, WorkspaceControllerTestDragging,
1353 // Verifies that events are targeted properly just outside the window edges.
1354 TEST_F(WorkspaceControllerTest, WindowEdgeHitTest) {
1355 aura::test::TestWindowDelegate d_first, d_second;
1356 scoped_ptr<Window> first(aura::test::CreateTestWindowWithDelegate(&d_first,
1357 123, gfx::Rect(20, 10, 100, 50), NULL));
1358 ParentWindowInPrimaryRootWindow(first.get());
1361 scoped_ptr<Window> second(aura::test::CreateTestWindowWithDelegate(&d_second,
1362 234, gfx::Rect(30, 40, 40, 10), NULL));
1363 ParentWindowInPrimaryRootWindow(second.get());
1366 ui::EventTarget* root = first->GetRootWindow();
1367 ui::EventTargeter* targeter = root->GetEventTargeter();
1369 // The windows overlap, and |second| is on top of |first|. Events targeted
1370 // slightly outside the edges of the |second| window should still be targeted
1371 // to |second| to allow resizing the windows easily.
1373 const int kNumPoints = 4;
1375 const char* direction;
1376 gfx::Point location;
1377 } points[kNumPoints] = {
1378 { "left", gfx::Point(28, 45) }, // outside the left edge.
1379 { "top", gfx::Point(50, 38) }, // outside the top edge.
1380 { "right", gfx::Point(72, 45) }, // outside the right edge.
1381 { "bottom", gfx::Point(50, 52) }, // outside the bottom edge.
1383 // Do two iterations, first without any transform on |second|, and the second
1384 // time after applying some transform on |second| so that it doesn't get
1386 for (int times = 0; times < 2; ++times) {
1387 SCOPED_TRACE(times == 0 ? "Without transform" : "With transform");
1388 aura::Window* expected_target = times == 0 ? second.get() : first.get();
1389 for (int i = 0; i < kNumPoints; ++i) {
1390 SCOPED_TRACE(points[i].direction);
1391 const gfx::Point& location = points[i].location;
1392 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
1394 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
1395 EXPECT_EQ(expected_target, target);
1397 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
1398 ui::EventTimeForNow());
1399 target = targeter->FindTargetForEvent(root, &touch);
1400 EXPECT_EQ(expected_target, target);
1402 // Apply a transform on |second|. After the transform is applied, the window
1403 // should no longer be targeted.
1404 gfx::Transform transform;
1405 transform.Translate(70, 40);
1406 second->SetTransform(transform);
1410 // Verifies events targeting just outside the window edges for panels.
1411 TEST_F(WorkspaceControllerTest, WindowEdgeHitTestPanel) {
1412 aura::test::TestWindowDelegate delegate;
1413 scoped_ptr<Window> window(CreateTestPanel(&delegate,
1414 gfx::Rect(20, 10, 100, 50)));
1415 ui::EventTarget* root = window->GetRootWindow();
1416 ui::EventTargeter* targeter = root->GetEventTargeter();
1417 const gfx::Rect bounds = window->bounds();
1418 const int kNumPoints = 5;
1420 const char* direction;
1421 gfx::Point location;
1423 } points[kNumPoints] = {
1424 { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true },
1425 { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true },
1426 { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true },
1427 { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), true },
1428 { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false },
1430 for (int i = 0; i < kNumPoints; ++i) {
1431 SCOPED_TRACE(points[i].direction);
1432 const gfx::Point& location = points[i].location;
1433 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
1435 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
1436 if (points[i].is_target_hit)
1437 EXPECT_EQ(window.get(), target);
1439 EXPECT_NE(window.get(), target);
1441 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
1442 ui::EventTimeForNow());
1443 target = targeter->FindTargetForEvent(root, &touch);
1444 if (points[i].is_target_hit)
1445 EXPECT_EQ(window.get(), target);
1447 EXPECT_NE(window.get(), target);
1451 // Verifies events targeting just outside the window edges for docked windows.
1452 TEST_F(WorkspaceControllerTest, WindowEdgeHitTestDocked) {
1453 if (!switches::UseDockedWindows())
1455 aura::test::TestWindowDelegate delegate;
1456 // Make window smaller than the minimum docked area so that the window edges
1458 delegate.set_maximum_size(gfx::Size(180, 200));
1459 scoped_ptr<Window> window(aura::test::CreateTestWindowWithDelegate(&delegate,
1460 123, gfx::Rect(20, 10, 100, 50), NULL));
1461 ParentWindowInPrimaryRootWindow(window.get());
1462 aura::Window* docked_container = Shell::GetContainer(
1463 window->GetRootWindow(), internal::kShellWindowId_DockedContainer);
1464 docked_container->AddChild(window.get());
1466 ui::EventTarget* root = window->GetRootWindow();
1467 ui::EventTargeter* targeter = root->GetEventTargeter();
1468 const gfx::Rect bounds = window->bounds();
1469 const int kNumPoints = 5;
1471 const char* direction;
1472 gfx::Point location;
1474 } points[kNumPoints] = {
1475 { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true },
1476 { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true },
1477 { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true },
1478 { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), true },
1479 { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false },
1481 for (int i = 0; i < kNumPoints; ++i) {
1482 SCOPED_TRACE(points[i].direction);
1483 const gfx::Point& location = points[i].location;
1484 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
1486 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
1487 if (points[i].is_target_hit)
1488 EXPECT_EQ(window.get(), target);
1490 EXPECT_NE(window.get(), target);
1492 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
1493 ui::EventTimeForNow());
1494 target = targeter->FindTargetForEvent(root, &touch);
1495 if (points[i].is_target_hit)
1496 EXPECT_EQ(window.get(), target);
1498 EXPECT_NE(window.get(), target);
1502 } // namespace internal