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 "ash/wm/workspace/workspace_window_resizer.h"
24 #include "base/command_line.h"
25 #include "base/strings/string_number_conversions.h"
26 #include "ui/aura/client/aura_constants.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/aura/window_event_dispatcher.h"
32 #include "ui/base/hit_test.h"
33 #include "ui/base/ui_base_types.h"
34 #include "ui/compositor/layer.h"
35 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
36 #include "ui/events/event_utils.h"
37 #include "ui/gfx/screen.h"
38 #include "ui/views/widget/widget.h"
39 #include "ui/wm/core/window_animations.h"
40 #include "ui/wm/core/window_util.h"
47 // Returns a string containing the names of all the children of |window| (in
48 // order). Each entry is separated by a space.
49 std::string GetWindowNames(const aura::Window* window) {
51 for (size_t i = 0; i < window->children().size(); ++i) {
54 result += window->children()[i]->name();
59 // Returns a string containing the names of windows corresponding to each of the
60 // child layers of |window|'s layer. Any layers that don't correspond to a child
61 // Window of |window| are ignored. The result is ordered based on the layer
63 std::string GetLayerNames(const aura::Window* window) {
64 typedef std::map<const ui::Layer*, std::string> LayerToWindowNameMap;
65 LayerToWindowNameMap window_names;
66 for (size_t i = 0; i < window->children().size(); ++i) {
67 window_names[window->children()[i]->layer()] =
68 window->children()[i]->name();
72 const std::vector<ui::Layer*>& layers(window->layer()->children());
73 for (size_t i = 0; i < layers.size(); ++i) {
74 LayerToWindowNameMap::iterator layer_i =
75 window_names.find(layers[i]);
76 if (layer_i != window_names.end()) {
79 result += layer_i->second;
85 class WorkspaceControllerTest : public test::AshTestBase {
87 WorkspaceControllerTest() {}
88 virtual ~WorkspaceControllerTest() {}
90 aura::Window* CreateTestWindowUnparented() {
91 aura::Window* window = new aura::Window(NULL);
92 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
93 window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
94 window->Init(aura::WINDOW_LAYER_TEXTURED);
98 aura::Window* CreateTestWindow() {
99 aura::Window* window = new aura::Window(NULL);
100 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
101 window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
102 window->Init(aura::WINDOW_LAYER_TEXTURED);
103 ParentWindowInPrimaryRootWindow(window);
107 aura::Window* CreateBrowserLikeWindow(const gfx::Rect& bounds) {
108 aura::Window* window = CreateTestWindow();
109 window->SetBounds(bounds);
110 wm::WindowState* window_state = wm::GetWindowState(window);
111 window_state->set_window_position_managed(true);
116 aura::Window* CreatePopupLikeWindow(const gfx::Rect& bounds) {
117 aura::Window* window = CreateTestWindowInShellWithBounds(bounds);
122 aura::Window* CreateTestPanel(aura::WindowDelegate* delegate,
123 const gfx::Rect& bounds) {
124 aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
126 ui::wm::WINDOW_TYPE_PANEL,
129 test::TestShelfDelegate* shelf_delegate =
130 test::TestShelfDelegate::instance();
131 shelf_delegate->AddShelfItem(window);
132 PanelLayoutManager* manager =
133 static_cast<PanelLayoutManager*>(
134 Shell::GetContainer(window->GetRootWindow(),
135 internal::kShellWindowId_PanelContainer)->
141 aura::Window* GetDesktop() {
142 return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
143 kShellWindowId_DefaultContainer);
146 gfx::Rect GetFullscreenBounds(aura::Window* window) {
147 return Shell::GetScreen()->GetDisplayNearestWindow(window).bounds();
150 ShelfWidget* shelf_widget() {
151 return Shell::GetPrimaryRootWindowController()->shelf();
154 ShelfLayoutManager* shelf_layout_manager() {
155 return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
158 bool GetWindowOverlapsShelf() {
159 return shelf_layout_manager()->window_overlaps_shelf();
163 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTest);
166 // Assertions around adding a normal window.
167 TEST_F(WorkspaceControllerTest, AddNormalWindowWhenEmpty) {
168 scoped_ptr<Window> w1(CreateTestWindow());
169 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
171 wm::WindowState* window_state = wm::GetWindowState(w1.get());
173 EXPECT_FALSE(window_state->HasRestoreBounds());
177 EXPECT_FALSE(window_state->HasRestoreBounds());
179 ASSERT_TRUE(w1->layer() != NULL);
180 EXPECT_TRUE(w1->layer()->visible());
182 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
184 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
187 // Assertions around maximizing/unmaximizing.
188 TEST_F(WorkspaceControllerTest, SingleMaximizeWindow) {
189 scoped_ptr<Window> w1(CreateTestWindow());
190 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
193 wm::ActivateWindow(w1.get());
195 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
197 ASSERT_TRUE(w1->layer() != NULL);
198 EXPECT_TRUE(w1->layer()->visible());
200 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
202 // Maximize the window.
203 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
205 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
207 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
208 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).width(),
209 w1->bounds().width());
210 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).height(),
211 w1->bounds().height());
213 // Restore the window.
214 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
216 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
217 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
220 // Assertions around two windows and toggling one to be fullscreen.
221 TEST_F(WorkspaceControllerTest, FullscreenWithNormalWindow) {
222 scoped_ptr<Window> w1(CreateTestWindow());
223 scoped_ptr<Window> w2(CreateTestWindow());
224 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
227 ASSERT_TRUE(w1->layer() != NULL);
228 EXPECT_TRUE(w1->layer()->visible());
230 w2->SetBounds(gfx::Rect(0, 0, 50, 51));
231 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
233 wm::ActivateWindow(w2.get());
235 // Both windows should be in the same workspace.
236 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
237 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
240 ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()));
241 EXPECT_EQ(work_area.width(), w2->bounds().width());
242 EXPECT_EQ(work_area.height(), w2->bounds().height());
244 // Restore w2, which should then go back to one workspace.
245 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
246 EXPECT_EQ(50, w2->bounds().width());
247 EXPECT_EQ(51, w2->bounds().height());
248 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
251 // Makes sure requests to change the bounds of a normal window go through.
252 TEST_F(WorkspaceControllerTest, ChangeBoundsOfNormalWindow) {
253 scoped_ptr<Window> w1(CreateTestWindow());
256 // Setting the bounds should go through since the window is in the normal
258 w1->SetBounds(gfx::Rect(0, 0, 200, 500));
259 EXPECT_EQ(200, w1->bounds().width());
260 EXPECT_EQ(500, w1->bounds().height());
263 // Verifies the bounds is not altered when showing and grid is enabled.
264 TEST_F(WorkspaceControllerTest, SnapToGrid) {
265 scoped_ptr<Window> w1(CreateTestWindowUnparented());
266 w1->SetBounds(gfx::Rect(1, 6, 25, 30));
267 ParentWindowInPrimaryRootWindow(w1.get());
268 // We are not aligning this anymore this way. When the window gets shown
269 // the window is expected to be handled differently, but this cannot be
270 // tested with this test. So the result of this test should be that the
271 // bounds are exactly as passed in.
272 EXPECT_EQ("1,6 25x30", w1->bounds().ToString());
275 // Assertions around a fullscreen window.
276 TEST_F(WorkspaceControllerTest, SingleFullscreenWindow) {
277 scoped_ptr<Window> w1(CreateTestWindow());
278 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
279 // Make the window fullscreen.
280 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
282 wm::ActivateWindow(w1.get());
284 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
285 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
286 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
288 // Restore the window. Use SHOW_STATE_DEFAULT as that is what we'll end up
289 // with when using views::Widget.
290 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT);
291 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
293 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
294 EXPECT_EQ(250, w1->bounds().width());
295 EXPECT_EQ(251, w1->bounds().height());
297 // Back to fullscreen.
298 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
299 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
300 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
301 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
302 wm::WindowState* window_state = wm::GetWindowState(w1.get());
304 ASSERT_TRUE(window_state->HasRestoreBounds());
305 EXPECT_EQ("0,0 250x251", window_state->GetRestoreBoundsInScreen().ToString());
308 // Assertions around minimizing a single window.
309 TEST_F(WorkspaceControllerTest, MinimizeSingleWindow) {
310 scoped_ptr<Window> w1(CreateTestWindow());
314 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
315 EXPECT_FALSE(w1->layer()->IsDrawn());
316 EXPECT_TRUE(w1->layer()->GetTargetTransform().IsIdentity());
320 EXPECT_TRUE(wm::GetWindowState(w1.get())->IsNormalStateType());
321 EXPECT_TRUE(w1->layer()->IsDrawn());
324 // Assertions around minimizing a fullscreen window.
325 TEST_F(WorkspaceControllerTest, MinimizeFullscreenWindow) {
326 // Two windows, w1 normal, w2 fullscreen.
327 scoped_ptr<Window> w1(CreateTestWindow());
328 scoped_ptr<Window> w2(CreateTestWindow());
330 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
333 wm::WindowState* w1_state = wm::GetWindowState(w1.get());
334 wm::WindowState* w2_state = wm::GetWindowState(w2.get());
336 w2_state->Activate();
339 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
340 EXPECT_TRUE(w1->layer()->IsDrawn());
341 EXPECT_FALSE(w2->layer()->IsDrawn());
343 // Show the window, which should trigger unminimizing.
345 w2_state->Activate();
347 EXPECT_TRUE(w2_state->IsFullscreen());
348 EXPECT_TRUE(w1->layer()->IsDrawn());
349 EXPECT_TRUE(w2->layer()->IsDrawn());
351 // Minimize the window, which should hide the window.
352 EXPECT_TRUE(w2_state->IsActive());
353 w2_state->Minimize();
354 EXPECT_FALSE(w2_state->IsActive());
355 EXPECT_FALSE(w2->layer()->IsDrawn());
356 EXPECT_TRUE(w1_state->IsActive());
357 EXPECT_EQ(w2.get(), GetDesktop()->children()[0]);
358 EXPECT_EQ(w1.get(), GetDesktop()->children()[1]);
360 // Make the window normal.
361 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
362 // Setting back to normal doesn't change the activation.
363 EXPECT_FALSE(w2_state->IsActive());
364 EXPECT_TRUE(w1_state->IsActive());
365 EXPECT_EQ(w2.get(), GetDesktop()->children()[0]);
366 EXPECT_EQ(w1.get(), GetDesktop()->children()[1]);
367 EXPECT_TRUE(w2->layer()->IsDrawn());
370 // Verifies ShelfLayoutManager's visibility/auto-hide state is correctly
372 TEST_F(WorkspaceControllerTest, ShelfStateUpdated) {
373 // Since ShelfLayoutManager queries for mouse location, move the mouse so
374 // it isn't over the shelf.
375 aura::test::EventGenerator generator(
376 Shell::GetPrimaryRootWindow(), gfx::Point());
377 generator.MoveMouseTo(0, 0);
379 scoped_ptr<Window> w1(CreateTestWindow());
380 const gfx::Rect w1_bounds(0, 1, 101, 102);
381 ShelfLayoutManager* shelf = shelf_layout_manager();
382 shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
383 const gfx::Rect touches_shelf_bounds(
384 0, shelf->GetIdealBounds().y() - 10, 101, 102);
385 // Move |w1| to overlap the shelf.
386 w1->SetBounds(touches_shelf_bounds);
387 EXPECT_FALSE(GetWindowOverlapsShelf());
389 // A visible ignored window should not trigger the overlap.
390 scoped_ptr<Window> w_ignored(CreateTestWindow());
391 w_ignored->SetBounds(touches_shelf_bounds);
392 wm::GetWindowState(&(*w_ignored))->set_ignored_by_shelf(true);
394 EXPECT_FALSE(GetWindowOverlapsShelf());
396 // Make it visible, since visible shelf overlaps should be true.
398 EXPECT_TRUE(GetWindowOverlapsShelf());
400 wm::ActivateWindow(w1.get());
401 w1->SetBounds(w1_bounds);
403 wm::ActivateWindow(w1.get());
405 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
407 // Maximize the window.
408 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
409 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
410 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
413 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
414 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
415 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
418 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
419 EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
422 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
423 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
424 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
425 EXPECT_FALSE(GetWindowOverlapsShelf());
427 // Move window so it obscures shelf.
428 w1->SetBounds(touches_shelf_bounds);
429 EXPECT_TRUE(GetWindowOverlapsShelf());
432 w1->SetBounds(w1_bounds);
433 EXPECT_FALSE(GetWindowOverlapsShelf());
436 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
437 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
438 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
441 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
442 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
444 // Since the restore from minimize will restore to the pre-minimize
445 // state (tested elsewhere), we abandon the current size and restore
446 // rect and set them to the window.
447 wm::WindowState* window_state = wm::GetWindowState(w1.get());
449 gfx::Rect restore = window_state->GetRestoreBoundsInScreen();
450 EXPECT_EQ("0,0 800x597", w1->bounds().ToString());
451 EXPECT_EQ("0,1 101x102", restore.ToString());
452 window_state->ClearRestoreBounds();
453 w1->SetBounds(restore);
456 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
457 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
458 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
460 // Create another window, maximized.
461 scoped_ptr<Window> w2(CreateTestWindow());
462 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
463 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
465 wm::ActivateWindow(w2.get());
466 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
467 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
468 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
469 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(
470 w2->parent()).ToString(),
471 w2->bounds().ToString());
474 wm::ActivateWindow(w1.get());
475 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
476 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
477 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(
478 w2->parent()).ToString(),
479 w2->bounds().ToString());
482 wm::ActivateWindow(w2.get());
483 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
484 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
485 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
486 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w2.get()).ToString(),
487 w2->bounds().ToString());
489 // Turn off auto-hide, switch back to w2 (maximized) and verify overlap.
490 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
491 wm::ActivateWindow(w2.get());
492 EXPECT_FALSE(GetWindowOverlapsShelf());
494 // Move w1 to overlap shelf, it shouldn't change window overlaps shelf since
495 // the window isn't in the visible workspace.
496 w1->SetBounds(touches_shelf_bounds);
497 EXPECT_FALSE(GetWindowOverlapsShelf());
499 // Activate w1. Although w1 is visible, the overlap state is still false since
501 wm::ActivateWindow(w1.get());
502 EXPECT_FALSE(GetWindowOverlapsShelf());
505 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
506 EXPECT_TRUE(GetWindowOverlapsShelf());
509 // Verifies going from maximized to minimized sets the right state for painting
510 // the background of the launcher.
511 TEST_F(WorkspaceControllerTest, MinimizeResetsVisibility) {
512 scoped_ptr<Window> w1(CreateTestWindow());
514 wm::ActivateWindow(w1.get());
515 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
516 EXPECT_EQ(SHELF_BACKGROUND_MAXIMIZED, shelf_widget()->GetBackgroundType());
518 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
519 EXPECT_EQ(SHELF_VISIBLE,
520 shelf_layout_manager()->visibility_state());
521 EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, shelf_widget()->GetBackgroundType());
524 // Verifies window visibility during various workspace changes.
525 TEST_F(WorkspaceControllerTest, VisibilityTests) {
526 scoped_ptr<Window> w1(CreateTestWindow());
528 EXPECT_TRUE(w1->IsVisible());
529 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
531 // Create another window, activate it and make it fullscreen.
532 scoped_ptr<Window> w2(CreateTestWindow());
534 wm::ActivateWindow(w2.get());
535 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
536 EXPECT_TRUE(w2->IsVisible());
537 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
538 EXPECT_TRUE(w1->IsVisible());
540 // Switch to w1. |w1| should be visible on top of |w2|.
541 wm::ActivateWindow(w1.get());
542 EXPECT_TRUE(w1->IsVisible());
543 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
544 EXPECT_TRUE(w2->IsVisible());
546 // Switch back to |w2|.
547 wm::ActivateWindow(w2.get());
548 EXPECT_TRUE(w2->IsVisible());
549 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
550 EXPECT_TRUE(w1->IsVisible());
552 // Restore |w2|, both windows should be visible.
553 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
554 EXPECT_TRUE(w1->IsVisible());
555 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
556 EXPECT_TRUE(w2->IsVisible());
557 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
559 // Make |w2| fullscreen again, then close it.
560 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
562 EXPECT_FALSE(w2->IsVisible());
563 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
564 EXPECT_TRUE(w1->IsVisible());
566 // Create |w2| and maximize it.
567 w2.reset(CreateTestWindow());
569 wm::ActivateWindow(w2.get());
570 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
571 EXPECT_TRUE(w2->IsVisible());
572 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
573 EXPECT_TRUE(w1->IsVisible());
577 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
578 EXPECT_TRUE(w1->IsVisible());
581 // Verifies windows that are offscreen don't move when switching workspaces.
582 TEST_F(WorkspaceControllerTest, DontMoveOnSwitch) {
583 aura::test::EventGenerator generator(
584 Shell::GetPrimaryRootWindow(), gfx::Point());
585 generator.MoveMouseTo(0, 0);
587 scoped_ptr<Window> w1(CreateTestWindow());
588 ShelfLayoutManager* shelf = shelf_layout_manager();
589 const gfx::Rect touches_shelf_bounds(
590 0, shelf->GetIdealBounds().y() - 10, 101, 102);
591 // Move |w1| to overlap the shelf.
592 w1->SetBounds(touches_shelf_bounds);
594 wm::ActivateWindow(w1.get());
596 // Create another window and maximize it.
597 scoped_ptr<Window> w2(CreateTestWindow());
598 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
599 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
601 wm::ActivateWindow(w2.get());
604 wm::ActivateWindow(w1.get());
605 EXPECT_EQ(touches_shelf_bounds.ToString(), w1->bounds().ToString());
608 // Verifies that windows that are completely offscreen move when switching
610 TEST_F(WorkspaceControllerTest, MoveOnSwitch) {
611 aura::test::EventGenerator generator(
612 Shell::GetPrimaryRootWindow(), gfx::Point());
613 generator.MoveMouseTo(0, 0);
615 scoped_ptr<Window> w1(CreateTestWindow());
616 ShelfLayoutManager* shelf = shelf_layout_manager();
617 const gfx::Rect w1_bounds(0, shelf->GetIdealBounds().y(), 100, 200);
618 // Move |w1| so that the top edge is the same as the top edge of the shelf.
619 w1->SetBounds(w1_bounds);
621 wm::ActivateWindow(w1.get());
622 EXPECT_EQ(w1_bounds.ToString(), w1->bounds().ToString());
624 // Create another window and maximize it.
625 scoped_ptr<Window> w2(CreateTestWindow());
626 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
627 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
629 wm::ActivateWindow(w2.get());
631 // Increase the size of the WorkAreaInsets. This would make |w1| fall
632 // completely out of the display work area.
634 Shell::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets();
635 insets.Set(0, 0, insets.bottom() + 30, 0);
636 Shell::GetInstance()->SetDisplayWorkAreaInsets(w1.get(), insets);
638 // Switch to w1. The window should have moved.
639 wm::ActivateWindow(w1.get());
640 EXPECT_NE(w1_bounds.ToString(), w1->bounds().ToString());
645 // WindowDelegate used by DontCrashOnChangeAndActivate.
646 class DontCrashOnChangeAndActivateDelegate
647 : public aura::test::TestWindowDelegate {
649 DontCrashOnChangeAndActivateDelegate() : window_(NULL) {}
651 void set_window(aura::Window* window) { window_ = window; }
653 // WindowDelegate overrides:
654 virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
655 const gfx::Rect& new_bounds) OVERRIDE {
657 wm::ActivateWindow(window_);
663 aura::Window* window_;
665 DISALLOW_COPY_AND_ASSIGN(DontCrashOnChangeAndActivateDelegate);
670 // Exercises possible crash in W2. Here's the sequence:
671 // . minimize a maximized window.
672 // . remove the window (which happens when switching displays).
673 // . add the window back.
674 // . show the window and during the bounds change activate it.
675 TEST_F(WorkspaceControllerTest, DontCrashOnChangeAndActivate) {
677 ShelfLayoutManager* shelf = shelf_layout_manager();
678 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
680 DontCrashOnChangeAndActivateDelegate delegate;
681 scoped_ptr<Window> w1(CreateTestWindowInShellWithDelegate(
682 &delegate, 1000, gfx::Rect(10, 11, 250, 251)));
685 wm::WindowState* w1_state = wm::GetWindowState(w1.get());
686 w1_state->Activate();
687 w1_state->Maximize();
688 w1_state->Minimize();
690 w1->parent()->RemoveChild(w1.get());
692 // Do this so that when we Show() the window a resize occurs and we make the
694 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
696 ParentWindowInPrimaryRootWindow(w1.get());
697 delegate.set_window(w1.get());
701 // Verifies a window with a transient parent not managed by workspace works.
702 TEST_F(WorkspaceControllerTest, TransientParent) {
703 // Normal window with no transient parent.
704 scoped_ptr<Window> w2(CreateTestWindow());
705 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
707 wm::ActivateWindow(w2.get());
709 // Window with a transient parent. We set the transient parent to the root,
710 // which would never happen but is enough to exercise the bug.
711 scoped_ptr<Window> w1(CreateTestWindowUnparented());
712 ::wm::AddTransientChild(
713 Shell::GetInstance()->GetPrimaryRootWindow(), w1.get());
714 w1->SetBounds(gfx::Rect(10, 11, 250, 251));
715 ParentWindowInPrimaryRootWindow(w1.get());
717 wm::ActivateWindow(w1.get());
719 // The window with the transient parent should get added to the same parent as
720 // the normal window.
721 EXPECT_EQ(w2->parent(), w1->parent());
724 // Test the placement of newly created windows.
725 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnCreate) {
726 if (!SupportsHostWindowResize())
728 UpdateDisplay("1600x1200");
729 // Creating a popup handler here to make sure it does not interfere with the
731 gfx::Rect source_browser_bounds(16, 32, 640, 320);
732 scoped_ptr<aura::Window> browser_window(CreateBrowserLikeWindow(
733 source_browser_bounds));
735 // Creating a popup to make sure it does not interfere with the positioning.
736 scoped_ptr<aura::Window> browser_popup(CreatePopupLikeWindow(
737 gfx::Rect(16, 32, 128, 256)));
739 browser_window->Show();
740 browser_popup->Show();
742 { // With a shown window it's size should get returned.
743 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
744 source_browser_bounds));
745 // The position should be right flush.
746 EXPECT_EQ("960,32 640x320", new_browser_window->bounds().ToString());
749 { // With the window shown - but more on the right side then on the left
750 // side (and partially out of the screen), it should default to the other
751 // side and inside the screen.
752 gfx::Rect source_browser_bounds(gfx::Rect(1000, 600, 640, 320));
753 browser_window->SetBounds(source_browser_bounds);
755 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
756 source_browser_bounds));
757 // The position should be left & bottom flush.
758 EXPECT_EQ("0,600 640x320", new_browser_window->bounds().ToString());
760 // If the other window was already beyond the point to get right flush
761 // it will remain where it is.
762 EXPECT_EQ("1000,600 640x320", browser_window->bounds().ToString());
765 { // Make sure that popups do not get changed.
766 scoped_ptr<aura::Window> new_popup_window(CreatePopupLikeWindow(
767 gfx::Rect(50, 100, 300, 150)));
768 EXPECT_EQ("50,100 300x150", new_popup_window->bounds().ToString());
771 browser_window->Hide();
772 { // If a window is there but not shown the default should be centered.
773 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
774 gfx::Rect(50, 100, 300, 150)));
775 EXPECT_EQ("650,100 300x150", new_browser_window->bounds().ToString());
779 // Test the basic auto placement of one and or two windows in a "simulated
780 // session" of sequential window operations.
781 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnShowHide) {
782 // Test 1: In case there is no manageable window, no window should shift.
784 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
785 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
786 gfx::Rect desktop_area = window1->parent()->bounds();
788 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
789 // Trigger the auto window placement function by making it visible.
790 // Note that the bounds are getting changed while it is invisible.
792 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
795 // Check the initial position of the windows is unchanged.
796 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
797 EXPECT_EQ("32,48 256x512", window2->bounds().ToString());
799 // Remove the second window and make sure that the first window
800 // does NOT get centered.
802 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
804 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
805 // Test 2: Set up two managed windows and check their auto positioning.
806 window1_state->set_window_position_managed(true);
808 scoped_ptr<aura::Window> window3(CreateTestWindowInShellWithId(2));
809 wm::GetWindowState(window3.get())->set_window_position_managed(true);
810 // To avoid any auto window manager changes due to SetBounds, the window
811 // gets first hidden and then shown again.
813 window3->SetBounds(gfx::Rect(32, 48, 256, 512));
815 // |window1| should be flush left and |window3| flush right.
816 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
817 EXPECT_EQ(base::IntToString(
818 desktop_area.width() - window3->bounds().width()) +
819 ",48 256x512", window3->bounds().ToString());
821 // After removing |window3|, |window1| should be centered again.
825 (desktop_area.width() - window1->bounds().width()) / 2) +
826 ",32 640x320", window1->bounds().ToString());
828 // Test 3: Set up a manageable and a non manageable window and check
830 scoped_ptr<aura::Window> window4(CreateTestWindowInShellWithId(3));
831 // To avoid any auto window manager changes due to SetBounds, the window
832 // gets first hidden and then shown again.
834 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
835 window4->SetBounds(gfx::Rect(32, 48, 256, 512));
837 // |window1| should be centered and |window4| untouched.
840 (desktop_area.width() - window1->bounds().width()) / 2) +
841 ",32 640x320", window1->bounds().ToString());
842 EXPECT_EQ("32,48 256x512", window4->bounds().ToString());
844 // Test4: A single manageable window should get centered.
846 window1_state->set_bounds_changed_by_user(false);
847 // Trigger the auto window placement function by showing (and hiding) it.
850 // |window1| should be centered.
853 (desktop_area.width() - window1->bounds().width()) / 2) +
854 ",32 640x320", window1->bounds().ToString());
857 // Test the proper usage of user window movement interaction.
858 TEST_F(WorkspaceControllerTest, TestUserMovedWindowRepositioning) {
859 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
860 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
861 gfx::Rect desktop_area = window1->parent()->bounds();
862 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
863 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
866 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
867 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
869 window1_state->set_window_position_managed(true);
870 window2_state->set_window_position_managed(true);
871 EXPECT_FALSE(window1_state->bounds_changed_by_user());
872 EXPECT_FALSE(window2_state->bounds_changed_by_user());
874 // Check that the current location gets preserved if the user has
875 // positioned it previously.
876 window1_state->set_bounds_changed_by_user(true);
878 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
879 // Flag should be still set.
880 EXPECT_TRUE(window1_state->bounds_changed_by_user());
881 EXPECT_FALSE(window2_state->bounds_changed_by_user());
883 // Turn on the second window and make sure that both windows are now
884 // positionable again (user movement cleared).
887 // |window1| should be flush left and |window2| flush right.
888 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
890 base::IntToString(desktop_area.width() - window2->bounds().width()) +
891 ",48 256x512", window2->bounds().ToString());
892 // FLag should now be reset.
893 EXPECT_FALSE(window1_state->bounds_changed_by_user());
894 EXPECT_FALSE(window2_state->bounds_changed_by_user());
896 // Going back to one shown window should keep the state.
897 window1_state->set_bounds_changed_by_user(true);
899 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
900 EXPECT_TRUE(window1_state->bounds_changed_by_user());
903 // Test if the single window will be restored at original position.
904 TEST_F(WorkspaceControllerTest, TestSingleWindowsRestoredBounds) {
905 scoped_ptr<aura::Window> window1(
906 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
907 scoped_ptr<aura::Window> window2(
908 CreateTestWindowInShellWithBounds(gfx::Rect(110, 110, 100, 100)));
909 scoped_ptr<aura::Window> window3(
910 CreateTestWindowInShellWithBounds(gfx::Rect(120, 120, 100, 100)));
914 wm::GetWindowState(window1.get())->set_window_position_managed(true);
915 wm::GetWindowState(window2.get())->set_window_position_managed(true);
916 wm::GetWindowState(window3.get())->set_window_position_managed(true);
919 wm::ActivateWindow(window1.get());
921 wm::ActivateWindow(window2.get());
923 wm::ActivateWindow(window3.get());
924 EXPECT_EQ(0, window1->bounds().x());
925 EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
926 window2->bounds().right());
927 EXPECT_EQ(0, window3->bounds().x());
930 EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
931 window2->bounds().right());
932 EXPECT_EQ(0, window3->bounds().x());
934 // Being a single window will retore the original location.
936 wm::ActivateWindow(window2.get());
937 EXPECT_EQ("110,110 100x100", window2->bounds().ToString());
939 // Showing the 3rd will push the 2nd window left.
941 wm::ActivateWindow(window3.get());
942 EXPECT_EQ(0, window2->bounds().x());
943 EXPECT_EQ(window3->GetRootWindow()->bounds().right(),
944 window3->bounds().right());
946 // Being a single window will retore the original location.
948 EXPECT_EQ("120,120 100x100", window3->bounds().ToString());
951 // Test that user placed windows go back to their user placement after the user
952 // closes all other windows.
953 TEST_F(WorkspaceControllerTest, TestUserHandledWindowRestore) {
954 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
955 gfx::Rect user_pos = gfx::Rect(16, 42, 640, 320);
956 window1->SetBounds(user_pos);
957 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
959 window1_state->SetPreAutoManageWindowBounds(user_pos);
960 gfx::Rect desktop_area = window1->parent()->bounds();
962 // Create a second window to let the auto manager kick in.
963 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
964 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
967 wm::GetWindowState(window1.get())->set_window_position_managed(true);
968 wm::GetWindowState(window2.get())->set_window_position_managed(true);
970 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
973 // |window1| should be flush left and |window2| flush right.
974 EXPECT_EQ("0," + base::IntToString(user_pos.y()) +
975 " 640x320", window1->bounds().ToString());
977 base::IntToString(desktop_area.width() - window2->bounds().width()) +
978 ",48 256x512", window2->bounds().ToString());
981 // After the other window get hidden the window has to move back to the
982 // previous position and the bounds should still be set and unchanged.
983 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
984 ASSERT_TRUE(window1_state->pre_auto_manage_window_bounds());
985 EXPECT_EQ(user_pos.ToString(),
986 window1_state->pre_auto_manage_window_bounds()->ToString());
989 // Solo window should be restored to the bounds where a user moved to.
990 TEST_F(WorkspaceControllerTest, TestRestoreToUserModifiedBounds) {
991 if (!SupportsHostWindowResize())
994 UpdateDisplay("400x300");
995 gfx::Rect default_bounds(10, 0, 100, 100);
996 scoped_ptr<aura::Window> window1(
997 CreateTestWindowInShellWithBounds(default_bounds));
998 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1000 window1_state->set_window_position_managed(true);
1002 // First window is centered.
1003 EXPECT_EQ("150,0 100x100", window1->bounds().ToString());
1004 scoped_ptr<aura::Window> window2(
1005 CreateTestWindowInShellWithBounds(default_bounds));
1006 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
1008 window2_state->set_window_position_managed(true);
1011 // Auto positioning pushes windows to each sides.
1012 EXPECT_EQ("0,0 100x100", window1->bounds().ToString());
1013 EXPECT_EQ("300,0 100x100", window2->bounds().ToString());
1016 // Restores to the center.
1017 EXPECT_EQ("150,0 100x100", window1->bounds().ToString());
1019 // A user moved the window.
1020 scoped_ptr<WindowResizer> resizer(CreateWindowResizer(
1024 aura::client::WINDOW_MOVE_SOURCE_MOUSE).release());
1025 gfx::Point location = resizer->GetInitialLocation();
1026 location.Offset(-50, 0);
1027 resizer->Drag(location, 0);
1028 resizer->CompleteDrag();
1030 window1_state->set_bounds_changed_by_user(true);
1031 window1->SetBounds(gfx::Rect(100, 0, 100, 100));
1034 EXPECT_EQ("0,0 100x100", window1->bounds().ToString());
1035 EXPECT_EQ("300,0 100x100", window2->bounds().ToString());
1037 // Window 1 should be restored to the user modified bounds.
1039 EXPECT_EQ("100,0 100x100", window1->bounds().ToString());
1042 // Test that a window from normal to minimize will repos the remaining.
1043 TEST_F(WorkspaceControllerTest, ToMinimizeRepositionsRemaining) {
1044 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1045 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1046 window1_state->set_window_position_managed(true);
1047 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1048 gfx::Rect desktop_area = window1->parent()->bounds();
1050 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1051 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
1052 window2_state->set_window_position_managed(true);
1053 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1055 window1_state->Minimize();
1057 // |window2| should be centered now.
1058 EXPECT_TRUE(window2->IsVisible());
1059 EXPECT_TRUE(window2_state->IsNormalStateType());
1060 EXPECT_EQ(base::IntToString(
1061 (desktop_area.width() - window2->bounds().width()) / 2) +
1062 ",48 256x512", window2->bounds().ToString());
1064 window1_state->Restore();
1065 // |window1| should be flush right and |window3| flush left.
1066 EXPECT_EQ(base::IntToString(
1067 desktop_area.width() - window1->bounds().width()) +
1068 ",32 640x320", window1->bounds().ToString());
1069 EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
1072 // Test that minimizing an initially maximized window will repos the remaining.
1073 TEST_F(WorkspaceControllerTest, MaxToMinRepositionsRemaining) {
1074 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1075 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1076 window1_state->set_window_position_managed(true);
1077 gfx::Rect desktop_area = window1->parent()->bounds();
1079 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1080 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
1081 window2_state->set_window_position_managed(true);
1082 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1084 window1_state->Maximize();
1085 window1_state->Minimize();
1087 // |window2| should be centered now.
1088 EXPECT_TRUE(window2->IsVisible());
1089 EXPECT_TRUE(window2_state->IsNormalStateType());
1090 EXPECT_EQ(base::IntToString(
1091 (desktop_area.width() - window2->bounds().width()) / 2) +
1092 ",48 256x512", window2->bounds().ToString());
1095 // Test that nomral, maximize, minimizing will repos the remaining.
1096 TEST_F(WorkspaceControllerTest, NormToMaxToMinRepositionsRemaining) {
1097 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1098 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1099 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1100 window1_state->set_window_position_managed(true);
1101 gfx::Rect desktop_area = window1->parent()->bounds();
1103 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1104 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
1105 window2_state->set_window_position_managed(true);
1106 window2->SetBounds(gfx::Rect(32, 40, 256, 512));
1108 // Trigger the auto window placement function by showing (and hiding) it.
1112 // |window1| should be flush right and |window3| flush left.
1113 EXPECT_EQ(base::IntToString(
1114 desktop_area.width() - window1->bounds().width()) +
1115 ",32 640x320", window1->bounds().ToString());
1116 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1118 window1_state->Maximize();
1119 window1_state->Minimize();
1121 // |window2| should be centered now.
1122 EXPECT_TRUE(window2->IsVisible());
1123 EXPECT_TRUE(window2_state->IsNormalStateType());
1124 EXPECT_EQ(base::IntToString(
1125 (desktop_area.width() - window2->bounds().width()) / 2) +
1126 ",40 256x512", window2->bounds().ToString());
1129 // Test that nomral, maximize, normal will repos the remaining.
1130 TEST_F(WorkspaceControllerTest, NormToMaxToNormRepositionsRemaining) {
1131 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1132 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1133 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1134 window1_state->set_window_position_managed(true);
1135 gfx::Rect desktop_area = window1->parent()->bounds();
1137 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1138 wm::GetWindowState(window2.get())->set_window_position_managed(true);
1139 window2->SetBounds(gfx::Rect(32, 40, 256, 512));
1141 // Trigger the auto window placement function by showing (and hiding) it.
1145 // |window1| should be flush right and |window3| flush left.
1146 EXPECT_EQ(base::IntToString(
1147 desktop_area.width() - window1->bounds().width()) +
1148 ",32 640x320", window1->bounds().ToString());
1149 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1151 window1_state->Maximize();
1152 window1_state->Restore();
1154 // |window1| should be flush right and |window2| flush left.
1155 EXPECT_EQ(base::IntToString(
1156 desktop_area.width() - window1->bounds().width()) +
1157 ",32 640x320", window1->bounds().ToString());
1158 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1161 // Test that animations are triggered.
1162 TEST_F(WorkspaceControllerTest, AnimatedNormToMaxToNormRepositionsRemaining) {
1163 ui::ScopedAnimationDurationScaleMode normal_duration_mode(
1164 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
1165 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1167 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1168 gfx::Rect desktop_area = window1->parent()->bounds();
1169 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1171 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1173 wm::GetWindowState(window1.get())->set_window_position_managed(true);
1174 wm::GetWindowState(window2.get())->set_window_position_managed(true);
1175 // Make sure nothing is animating.
1176 window1->layer()->GetAnimator()->StopAnimating();
1177 window2->layer()->GetAnimator()->StopAnimating();
1180 // The second window should now animate.
1181 EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating());
1182 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1183 window2->layer()->GetAnimator()->StopAnimating();
1186 EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
1187 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1189 window1->layer()->GetAnimator()->StopAnimating();
1190 window2->layer()->GetAnimator()->StopAnimating();
1191 // |window1| should be flush right and |window2| flush left.
1192 EXPECT_EQ(base::IntToString(
1193 desktop_area.width() - window1->bounds().width()) +
1194 ",32 640x320", window1->bounds().ToString());
1195 EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
1198 // This tests simulates a browser and an app and verifies the ordering of the
1199 // windows and layers doesn't get out of sync as various operations occur. Its
1200 // really testing code in FocusController, but easier to simulate here. Just as
1201 // with a real browser the browser here has a transient child window
1202 // (corresponds to the status bubble).
1203 TEST_F(WorkspaceControllerTest, VerifyLayerOrdering) {
1204 scoped_ptr<Window> browser(aura::test::CreateTestWindowWithDelegate(
1205 NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL));
1206 browser->SetName("browser");
1207 ParentWindowInPrimaryRootWindow(browser.get());
1209 wm::ActivateWindow(browser.get());
1211 // |status_bubble| is made a transient child of |browser| and as a result
1212 // owned by |browser|.
1213 aura::test::TestWindowDelegate* status_bubble_delegate =
1214 aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate();
1215 status_bubble_delegate->set_can_focus(false);
1216 Window* status_bubble =
1217 aura::test::CreateTestWindowWithDelegate(status_bubble_delegate,
1218 ui::wm::WINDOW_TYPE_POPUP,
1219 gfx::Rect(5, 6, 7, 8),
1221 ::wm::AddTransientChild(browser.get(), status_bubble);
1222 ParentWindowInPrimaryRootWindow(status_bubble);
1223 status_bubble->SetName("status_bubble");
1225 scoped_ptr<Window> app(aura::test::CreateTestWindowWithDelegate(
1226 NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL));
1227 app->SetName("app");
1228 ParentWindowInPrimaryRootWindow(app.get());
1230 aura::Window* parent = browser->parent();
1233 wm::ActivateWindow(app.get());
1234 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1236 // Minimize the app, focus should go the browser.
1237 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
1238 EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
1239 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1241 // Minimize the browser (neither windows are focused).
1242 browser->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
1243 EXPECT_FALSE(wm::IsActiveWindow(browser.get()));
1244 EXPECT_FALSE(wm::IsActiveWindow(app.get()));
1245 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1247 // Show the browser (which should restore it).
1249 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1251 // Activate the browser.
1252 ash::wm::ActivateWindow(browser.get());
1253 EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
1254 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1256 // Restore the app. This differs from above code for |browser| as internally
1257 // the app code does this. Restoring this way or using Show() should not make
1259 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1260 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1262 // Activate the app.
1263 ash::wm::ActivateWindow(app.get());
1264 EXPECT_TRUE(wm::IsActiveWindow(app.get()));
1265 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1270 // Used by DragMaximizedNonTrackedWindow to track how many times the window
1271 // hierarchy changes affecting the specified window.
1272 class DragMaximizedNonTrackedWindowObserver
1273 : public aura::WindowObserver {
1275 DragMaximizedNonTrackedWindowObserver(aura::Window* window)
1280 // Number of times OnWindowHierarchyChanged() has been received.
1281 void clear_change_count() { change_count_ = 0; }
1282 int change_count() const {
1283 return change_count_;
1286 // aura::WindowObserver overrides:
1287 // Counts number of times a window is reparented. Ignores reparenting into and
1288 // from a docked container which is expected when a tab is dragged.
1289 virtual void OnWindowHierarchyChanged(
1290 const HierarchyChangeParams& params) OVERRIDE {
1291 if (params.target != window_ ||
1292 (params.old_parent->id() == kShellWindowId_DefaultContainer &&
1293 params.new_parent->id() == kShellWindowId_DockedContainer) ||
1294 (params.old_parent->id() == kShellWindowId_DockedContainer &&
1295 params.new_parent->id() == kShellWindowId_DefaultContainer)) {
1303 aura::Window* window_;
1305 DISALLOW_COPY_AND_ASSIGN(DragMaximizedNonTrackedWindowObserver);
1310 // Verifies that a new maximized window becomes visible after its activation
1311 // is requested, even though it does not become activated because a system
1312 // modal window is active.
1313 TEST_F(WorkspaceControllerTest, SwitchFromModal) {
1314 scoped_ptr<Window> modal_window(CreateTestWindowUnparented());
1315 modal_window->SetBounds(gfx::Rect(10, 11, 21, 22));
1316 modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
1317 ParentWindowInPrimaryRootWindow(modal_window.get());
1318 modal_window->Show();
1319 wm::ActivateWindow(modal_window.get());
1321 scoped_ptr<Window> maximized_window(CreateTestWindow());
1322 maximized_window->SetProperty(
1323 aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
1324 maximized_window->Show();
1325 wm::ActivateWindow(maximized_window.get());
1326 EXPECT_TRUE(maximized_window->IsVisible());
1331 // Subclass of WorkspaceControllerTest that runs tests with docked windows
1332 // enabled and disabled.
1333 class WorkspaceControllerTestDragging
1334 : public WorkspaceControllerTest,
1335 public testing::WithParamInterface<bool> {
1337 WorkspaceControllerTestDragging() {}
1338 virtual ~WorkspaceControllerTestDragging() {}
1341 virtual void SetUp() OVERRIDE {
1342 WorkspaceControllerTest::SetUp();
1343 if (!docked_windows_enabled()) {
1344 CommandLine::ForCurrentProcess()->AppendSwitch(
1345 ash::switches::kAshDisableDockedWindows);
1349 bool docked_windows_enabled() const { return GetParam(); }
1352 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTestDragging);
1357 // Verifies that when dragging a window over the shelf overlap is detected
1358 // during and after the drag.
1359 TEST_P(WorkspaceControllerTestDragging, DragWindowOverlapShelf) {
1360 aura::test::TestWindowDelegate delegate;
1361 delegate.set_window_component(HTCAPTION);
1362 scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate(
1363 &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL));
1364 ParentWindowInPrimaryRootWindow(w1.get());
1366 ShelfLayoutManager* shelf = shelf_layout_manager();
1367 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
1369 // Drag near the shelf.
1370 aura::test::EventGenerator generator(
1371 Shell::GetPrimaryRootWindow(), gfx::Point());
1372 generator.MoveMouseTo(10, 10);
1373 generator.PressLeftButton();
1374 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 70);
1376 // Shelf should not be in overlapped state.
1377 EXPECT_FALSE(GetWindowOverlapsShelf());
1379 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 20);
1381 // Shelf should detect overlap. Overlap state stays after mouse is released.
1382 EXPECT_TRUE(GetWindowOverlapsShelf());
1383 generator.ReleaseLeftButton();
1384 EXPECT_TRUE(GetWindowOverlapsShelf());
1387 // Verifies that when dragging a window autohidden shelf stays hidden during
1388 // and after the drag.
1389 TEST_P(WorkspaceControllerTestDragging, DragWindowKeepsShelfAutohidden) {
1390 aura::test::TestWindowDelegate delegate;
1391 delegate.set_window_component(HTCAPTION);
1392 scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate(
1393 &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL));
1394 ParentWindowInPrimaryRootWindow(w1.get());
1396 ShelfLayoutManager* shelf = shelf_layout_manager();
1397 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
1398 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
1400 // Drag very little.
1401 aura::test::EventGenerator generator(
1402 Shell::GetPrimaryRootWindow(), gfx::Point());
1403 generator.MoveMouseTo(10, 10);
1404 generator.PressLeftButton();
1405 generator.MoveMouseTo(12, 12);
1407 // Shelf should be hidden during and after the drag.
1408 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
1409 generator.ReleaseLeftButton();
1410 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
1413 INSTANTIATE_TEST_CASE_P(DockedOrNot, WorkspaceControllerTestDragging,
1416 // Verifies that events are targeted properly just outside the window edges.
1417 TEST_F(WorkspaceControllerTest, WindowEdgeHitTest) {
1418 aura::test::TestWindowDelegate d_first, d_second;
1419 scoped_ptr<Window> first(aura::test::CreateTestWindowWithDelegate(&d_first,
1420 123, gfx::Rect(20, 10, 100, 50), NULL));
1421 ParentWindowInPrimaryRootWindow(first.get());
1424 scoped_ptr<Window> second(aura::test::CreateTestWindowWithDelegate(&d_second,
1425 234, gfx::Rect(30, 40, 40, 10), NULL));
1426 ParentWindowInPrimaryRootWindow(second.get());
1429 ui::EventTarget* root = first->GetRootWindow();
1430 ui::EventTargeter* targeter = root->GetEventTargeter();
1432 // The windows overlap, and |second| is on top of |first|. Events targeted
1433 // slightly outside the edges of the |second| window should still be targeted
1434 // to |second| to allow resizing the windows easily.
1436 const int kNumPoints = 4;
1438 const char* direction;
1439 gfx::Point location;
1440 } points[kNumPoints] = {
1441 { "left", gfx::Point(28, 45) }, // outside the left edge.
1442 { "top", gfx::Point(50, 38) }, // outside the top edge.
1443 { "right", gfx::Point(72, 45) }, // outside the right edge.
1444 { "bottom", gfx::Point(50, 52) }, // outside the bottom edge.
1446 // Do two iterations, first without any transform on |second|, and the second
1447 // time after applying some transform on |second| so that it doesn't get
1449 for (int times = 0; times < 2; ++times) {
1450 SCOPED_TRACE(times == 0 ? "Without transform" : "With transform");
1451 aura::Window* expected_target = times == 0 ? second.get() : first.get();
1452 for (int i = 0; i < kNumPoints; ++i) {
1453 SCOPED_TRACE(points[i].direction);
1454 const gfx::Point& location = points[i].location;
1455 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
1457 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
1458 EXPECT_EQ(expected_target, target);
1460 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
1461 ui::EventTimeForNow());
1462 target = targeter->FindTargetForEvent(root, &touch);
1463 EXPECT_EQ(expected_target, target);
1465 // Apply a transform on |second|. After the transform is applied, the window
1466 // should no longer be targeted.
1467 gfx::Transform transform;
1468 transform.Translate(70, 40);
1469 second->SetTransform(transform);
1473 // Verifies events targeting just outside the window edges for panels.
1474 TEST_F(WorkspaceControllerTest, WindowEdgeHitTestPanel) {
1475 aura::test::TestWindowDelegate delegate;
1476 scoped_ptr<Window> window(CreateTestPanel(&delegate,
1477 gfx::Rect(20, 10, 100, 50)));
1478 ui::EventTarget* root = window->GetRootWindow();
1479 ui::EventTargeter* targeter = root->GetEventTargeter();
1480 const gfx::Rect bounds = window->bounds();
1481 const int kNumPoints = 5;
1483 const char* direction;
1484 gfx::Point location;
1486 } points[kNumPoints] = {
1487 { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true },
1488 { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true },
1489 { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true },
1490 { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), true },
1491 { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false },
1493 for (int i = 0; i < kNumPoints; ++i) {
1494 SCOPED_TRACE(points[i].direction);
1495 const gfx::Point& location = points[i].location;
1496 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
1498 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
1499 if (points[i].is_target_hit)
1500 EXPECT_EQ(window.get(), target);
1502 EXPECT_NE(window.get(), target);
1504 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
1505 ui::EventTimeForNow());
1506 target = targeter->FindTargetForEvent(root, &touch);
1507 if (points[i].is_target_hit)
1508 EXPECT_EQ(window.get(), target);
1510 EXPECT_NE(window.get(), target);
1514 // Verifies events targeting just outside the window edges for docked windows.
1515 TEST_F(WorkspaceControllerTest, WindowEdgeHitTestDocked) {
1516 if (!switches::UseDockedWindows())
1518 aura::test::TestWindowDelegate delegate;
1519 // Make window smaller than the minimum docked area so that the window edges
1521 delegate.set_maximum_size(gfx::Size(180, 200));
1522 scoped_ptr<Window> window(aura::test::CreateTestWindowWithDelegate(&delegate,
1523 123, gfx::Rect(20, 10, 100, 50), NULL));
1524 ParentWindowInPrimaryRootWindow(window.get());
1525 aura::Window* docked_container = Shell::GetContainer(
1526 window->GetRootWindow(), internal::kShellWindowId_DockedContainer);
1527 docked_container->AddChild(window.get());
1529 ui::EventTarget* root = window->GetRootWindow();
1530 ui::EventTargeter* targeter = root->GetEventTargeter();
1531 const gfx::Rect bounds = window->bounds();
1532 const int kNumPoints = 5;
1534 const char* direction;
1535 gfx::Point location;
1537 } points[kNumPoints] = {
1538 { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true },
1539 { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true },
1540 { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true },
1541 { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), true },
1542 { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false },
1544 for (int i = 0; i < kNumPoints; ++i) {
1545 SCOPED_TRACE(points[i].direction);
1546 const gfx::Point& location = points[i].location;
1547 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
1549 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
1550 if (points[i].is_target_hit)
1551 EXPECT_EQ(window.get(), target);
1553 EXPECT_NE(window.get(), target);
1555 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
1556 ui::EventTimeForNow());
1557 target = targeter->FindTargetForEvent(root, &touch);
1558 if (points[i].is_target_hit)
1559 EXPECT_EQ(window.get(), target);
1561 EXPECT_NE(window.get(), target);
1565 } // namespace internal