Prevents double-clicks on a tab close button from aslo maximizing the browser
[chromium-blink-merge.git] / ash / wm / workspace_controller_unittest.cc
blob6056a2f85999559557a4a5df73bca01014cded40
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"
7 #include <map>
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/test/event_generator.h"
27 #include "ui/aura/test/test_window_delegate.h"
28 #include "ui/aura/test/test_windows.h"
29 #include "ui/aura/window.h"
30 #include "ui/aura/window_event_dispatcher.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/widget/widget.h"
38 #include "ui/wm/core/window_animations.h"
39 #include "ui/wm/core/window_util.h"
41 using aura::Window;
43 namespace ash {
45 // Returns a string containing the names of all the children of |window| (in
46 // order). Each entry is separated by a space.
47 std::string GetWindowNames(const aura::Window* window) {
48 std::string result;
49 for (size_t i = 0; i < window->children().size(); ++i) {
50 if (i != 0)
51 result += " ";
52 result += window->children()[i]->name();
54 return result;
57 // Returns a string containing the names of windows corresponding to each of the
58 // child layers of |window|'s layer. Any layers that don't correspond to a child
59 // Window of |window| are ignored. The result is ordered based on the layer
60 // ordering.
61 std::string GetLayerNames(const aura::Window* window) {
62 typedef std::map<const ui::Layer*, std::string> LayerToWindowNameMap;
63 LayerToWindowNameMap window_names;
64 for (size_t i = 0; i < window->children().size(); ++i) {
65 window_names[window->children()[i]->layer()] =
66 window->children()[i]->name();
69 std::string result;
70 const std::vector<ui::Layer*>& layers(window->layer()->children());
71 for (size_t i = 0; i < layers.size(); ++i) {
72 LayerToWindowNameMap::iterator layer_i =
73 window_names.find(layers[i]);
74 if (layer_i != window_names.end()) {
75 if (!result.empty())
76 result += " ";
77 result += layer_i->second;
80 return result;
83 class WorkspaceControllerTest : public test::AshTestBase {
84 public:
85 WorkspaceControllerTest() {}
86 virtual ~WorkspaceControllerTest() {}
88 aura::Window* CreateTestWindowUnparented() {
89 aura::Window* window = new aura::Window(NULL);
90 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
91 window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
92 window->Init(aura::WINDOW_LAYER_TEXTURED);
93 return window;
96 aura::Window* CreateTestWindow() {
97 aura::Window* window = new aura::Window(NULL);
98 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
99 window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
100 window->Init(aura::WINDOW_LAYER_TEXTURED);
101 ParentWindowInPrimaryRootWindow(window);
102 return window;
105 aura::Window* CreateBrowserLikeWindow(const gfx::Rect& bounds) {
106 aura::Window* window = CreateTestWindow();
107 window->SetBounds(bounds);
108 wm::WindowState* window_state = wm::GetWindowState(window);
109 window_state->set_window_position_managed(true);
110 window->Show();
111 return window;
114 aura::Window* CreatePopupLikeWindow(const gfx::Rect& bounds) {
115 aura::Window* window = CreateTestWindowInShellWithBounds(bounds);
116 window->Show();
117 return window;
120 aura::Window* CreateTestPanel(aura::WindowDelegate* delegate,
121 const gfx::Rect& bounds) {
122 aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
123 delegate,
124 ui::wm::WINDOW_TYPE_PANEL,
126 bounds);
127 test::TestShelfDelegate* shelf_delegate =
128 test::TestShelfDelegate::instance();
129 shelf_delegate->AddShelfItem(window);
130 PanelLayoutManager* manager = static_cast<PanelLayoutManager*>(
131 Shell::GetContainer(window->GetRootWindow(),
132 kShellWindowId_PanelContainer)->layout_manager());
133 manager->Relayout();
134 return window;
137 aura::Window* GetDesktop() {
138 return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
139 kShellWindowId_DefaultContainer);
142 gfx::Rect GetFullscreenBounds(aura::Window* window) {
143 return Shell::GetScreen()->GetDisplayNearestWindow(window).bounds();
146 ShelfWidget* shelf_widget() {
147 return Shell::GetPrimaryRootWindowController()->shelf();
150 ShelfLayoutManager* shelf_layout_manager() {
151 return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
154 bool GetWindowOverlapsShelf() {
155 return shelf_layout_manager()->window_overlaps_shelf();
158 private:
159 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTest);
162 // Assertions around adding a normal window.
163 TEST_F(WorkspaceControllerTest, AddNormalWindowWhenEmpty) {
164 scoped_ptr<Window> w1(CreateTestWindow());
165 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
167 wm::WindowState* window_state = wm::GetWindowState(w1.get());
169 EXPECT_FALSE(window_state->HasRestoreBounds());
171 w1->Show();
173 EXPECT_FALSE(window_state->HasRestoreBounds());
175 ASSERT_TRUE(w1->layer() != NULL);
176 EXPECT_TRUE(w1->layer()->visible());
178 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
180 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
183 // Assertions around maximizing/unmaximizing.
184 TEST_F(WorkspaceControllerTest, SingleMaximizeWindow) {
185 scoped_ptr<Window> w1(CreateTestWindow());
186 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
188 w1->Show();
189 wm::ActivateWindow(w1.get());
191 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
193 ASSERT_TRUE(w1->layer() != NULL);
194 EXPECT_TRUE(w1->layer()->visible());
196 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
198 // Maximize the window.
199 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
201 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
203 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
204 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).width(),
205 w1->bounds().width());
206 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).height(),
207 w1->bounds().height());
209 // Restore the window.
210 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
212 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
213 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
216 // Assertions around two windows and toggling one to be fullscreen.
217 TEST_F(WorkspaceControllerTest, FullscreenWithNormalWindow) {
218 scoped_ptr<Window> w1(CreateTestWindow());
219 scoped_ptr<Window> w2(CreateTestWindow());
220 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
221 w1->Show();
223 ASSERT_TRUE(w1->layer() != NULL);
224 EXPECT_TRUE(w1->layer()->visible());
226 w2->SetBounds(gfx::Rect(0, 0, 50, 51));
227 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
228 w2->Show();
229 wm::ActivateWindow(w2.get());
231 // Both windows should be in the same workspace.
232 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
233 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
235 gfx::Rect work_area(
236 ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()));
237 EXPECT_EQ(work_area.width(), w2->bounds().width());
238 EXPECT_EQ(work_area.height(), w2->bounds().height());
240 // Restore w2, which should then go back to one workspace.
241 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
242 EXPECT_EQ(50, w2->bounds().width());
243 EXPECT_EQ(51, w2->bounds().height());
244 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
247 // Makes sure requests to change the bounds of a normal window go through.
248 TEST_F(WorkspaceControllerTest, ChangeBoundsOfNormalWindow) {
249 scoped_ptr<Window> w1(CreateTestWindow());
250 w1->Show();
252 // Setting the bounds should go through since the window is in the normal
253 // workspace.
254 w1->SetBounds(gfx::Rect(0, 0, 200, 500));
255 EXPECT_EQ(200, w1->bounds().width());
256 EXPECT_EQ(500, w1->bounds().height());
259 // Verifies the bounds is not altered when showing and grid is enabled.
260 TEST_F(WorkspaceControllerTest, SnapToGrid) {
261 scoped_ptr<Window> w1(CreateTestWindowUnparented());
262 w1->SetBounds(gfx::Rect(1, 6, 25, 30));
263 ParentWindowInPrimaryRootWindow(w1.get());
264 // We are not aligning this anymore this way. When the window gets shown
265 // the window is expected to be handled differently, but this cannot be
266 // tested with this test. So the result of this test should be that the
267 // bounds are exactly as passed in.
268 EXPECT_EQ("1,6 25x30", w1->bounds().ToString());
271 // Assertions around a fullscreen window.
272 TEST_F(WorkspaceControllerTest, SingleFullscreenWindow) {
273 scoped_ptr<Window> w1(CreateTestWindow());
274 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
275 // Make the window fullscreen.
276 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
277 w1->Show();
278 wm::ActivateWindow(w1.get());
280 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
281 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
282 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
284 // Restore the window. Use SHOW_STATE_DEFAULT as that is what we'll end up
285 // with when using views::Widget.
286 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT);
287 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
289 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
290 EXPECT_EQ(250, w1->bounds().width());
291 EXPECT_EQ(251, w1->bounds().height());
293 // Back to fullscreen.
294 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
295 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
296 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
297 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
298 wm::WindowState* window_state = wm::GetWindowState(w1.get());
300 ASSERT_TRUE(window_state->HasRestoreBounds());
301 EXPECT_EQ("0,0 250x251", window_state->GetRestoreBoundsInScreen().ToString());
304 // Assertions around minimizing a single window.
305 TEST_F(WorkspaceControllerTest, MinimizeSingleWindow) {
306 scoped_ptr<Window> w1(CreateTestWindow());
308 w1->Show();
310 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
311 EXPECT_FALSE(w1->layer()->IsDrawn());
312 EXPECT_TRUE(w1->layer()->GetTargetTransform().IsIdentity());
314 // Show the window.
315 w1->Show();
316 EXPECT_TRUE(wm::GetWindowState(w1.get())->IsNormalStateType());
317 EXPECT_TRUE(w1->layer()->IsDrawn());
320 // Assertions around minimizing a fullscreen window.
321 TEST_F(WorkspaceControllerTest, MinimizeFullscreenWindow) {
322 // Two windows, w1 normal, w2 fullscreen.
323 scoped_ptr<Window> w1(CreateTestWindow());
324 scoped_ptr<Window> w2(CreateTestWindow());
325 w1->Show();
326 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
327 w2->Show();
329 wm::WindowState* w1_state = wm::GetWindowState(w1.get());
330 wm::WindowState* w2_state = wm::GetWindowState(w2.get());
332 w2_state->Activate();
334 // Minimize w2.
335 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
336 EXPECT_TRUE(w1->layer()->IsDrawn());
337 EXPECT_FALSE(w2->layer()->IsDrawn());
339 // Show the window, which should trigger unminimizing.
340 w2->Show();
341 w2_state->Activate();
343 EXPECT_TRUE(w2_state->IsFullscreen());
344 EXPECT_TRUE(w1->layer()->IsDrawn());
345 EXPECT_TRUE(w2->layer()->IsDrawn());
347 // Minimize the window, which should hide the window.
348 EXPECT_TRUE(w2_state->IsActive());
349 w2_state->Minimize();
350 EXPECT_FALSE(w2_state->IsActive());
351 EXPECT_FALSE(w2->layer()->IsDrawn());
352 EXPECT_TRUE(w1_state->IsActive());
353 EXPECT_EQ(w2.get(), GetDesktop()->children()[0]);
354 EXPECT_EQ(w1.get(), GetDesktop()->children()[1]);
356 // Make the window normal.
357 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
358 // Setting back to normal doesn't change the activation.
359 EXPECT_FALSE(w2_state->IsActive());
360 EXPECT_TRUE(w1_state->IsActive());
361 EXPECT_EQ(w2.get(), GetDesktop()->children()[0]);
362 EXPECT_EQ(w1.get(), GetDesktop()->children()[1]);
363 EXPECT_TRUE(w2->layer()->IsDrawn());
366 // Verifies ShelfLayoutManager's visibility/auto-hide state is correctly
367 // updated.
368 TEST_F(WorkspaceControllerTest, ShelfStateUpdated) {
369 // Since ShelfLayoutManager queries for mouse location, move the mouse so
370 // it isn't over the shelf.
371 aura::test::EventGenerator generator(
372 Shell::GetPrimaryRootWindow(), gfx::Point());
373 generator.MoveMouseTo(0, 0);
375 scoped_ptr<Window> w1(CreateTestWindow());
376 const gfx::Rect w1_bounds(0, 1, 101, 102);
377 ShelfLayoutManager* shelf = shelf_layout_manager();
378 shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
379 const gfx::Rect touches_shelf_bounds(
380 0, shelf->GetIdealBounds().y() - 10, 101, 102);
381 // Move |w1| to overlap the shelf.
382 w1->SetBounds(touches_shelf_bounds);
383 EXPECT_FALSE(GetWindowOverlapsShelf());
385 // A visible ignored window should not trigger the overlap.
386 scoped_ptr<Window> w_ignored(CreateTestWindow());
387 w_ignored->SetBounds(touches_shelf_bounds);
388 wm::GetWindowState(&(*w_ignored))->set_ignored_by_shelf(true);
389 w_ignored->Show();
390 EXPECT_FALSE(GetWindowOverlapsShelf());
392 // Make it visible, since visible shelf overlaps should be true.
393 w1->Show();
394 EXPECT_TRUE(GetWindowOverlapsShelf());
396 wm::ActivateWindow(w1.get());
397 w1->SetBounds(w1_bounds);
398 w1->Show();
399 wm::ActivateWindow(w1.get());
401 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
403 // Maximize the window.
404 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
405 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
406 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
408 // Restore.
409 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
410 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
411 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
413 // Fullscreen.
414 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
415 EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
417 // Normal.
418 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
419 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
420 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
421 EXPECT_FALSE(GetWindowOverlapsShelf());
423 // Move window so it obscures shelf.
424 w1->SetBounds(touches_shelf_bounds);
425 EXPECT_TRUE(GetWindowOverlapsShelf());
427 // Move it back.
428 w1->SetBounds(w1_bounds);
429 EXPECT_FALSE(GetWindowOverlapsShelf());
431 // Maximize again.
432 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
433 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
434 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
436 // Minimize.
437 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
438 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
440 // Since the restore from minimize will restore to the pre-minimize
441 // state (tested elsewhere), we abandon the current size and restore
442 // rect and set them to the window.
443 wm::WindowState* window_state = wm::GetWindowState(w1.get());
445 gfx::Rect restore = window_state->GetRestoreBoundsInScreen();
446 EXPECT_EQ("0,0 800x597", w1->bounds().ToString());
447 EXPECT_EQ("0,1 101x102", restore.ToString());
448 window_state->ClearRestoreBounds();
449 w1->SetBounds(restore);
451 // Restore.
452 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
453 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
454 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
456 // Create another window, maximized.
457 scoped_ptr<Window> w2(CreateTestWindow());
458 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
459 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
460 w2->Show();
461 wm::ActivateWindow(w2.get());
462 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
463 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
464 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
465 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(
466 w2->parent()).ToString(),
467 w2->bounds().ToString());
469 // Switch to w1.
470 wm::ActivateWindow(w1.get());
471 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
472 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
473 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(
474 w2->parent()).ToString(),
475 w2->bounds().ToString());
477 // Switch to w2.
478 wm::ActivateWindow(w2.get());
479 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
480 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
481 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
482 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w2.get()).ToString(),
483 w2->bounds().ToString());
485 // Turn off auto-hide, switch back to w2 (maximized) and verify overlap.
486 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
487 wm::ActivateWindow(w2.get());
488 EXPECT_FALSE(GetWindowOverlapsShelf());
490 // Move w1 to overlap shelf, it shouldn't change window overlaps shelf since
491 // the window isn't in the visible workspace.
492 w1->SetBounds(touches_shelf_bounds);
493 EXPECT_FALSE(GetWindowOverlapsShelf());
495 // Activate w1. Although w1 is visible, the overlap state is still false since
496 // w2 is maximized.
497 wm::ActivateWindow(w1.get());
498 EXPECT_FALSE(GetWindowOverlapsShelf());
500 // Restore w2.
501 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
502 EXPECT_TRUE(GetWindowOverlapsShelf());
505 // Verifies going from maximized to minimized sets the right state for painting
506 // the background of the launcher.
507 TEST_F(WorkspaceControllerTest, MinimizeResetsVisibility) {
508 scoped_ptr<Window> w1(CreateTestWindow());
509 w1->Show();
510 wm::ActivateWindow(w1.get());
511 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
512 EXPECT_EQ(SHELF_BACKGROUND_MAXIMIZED, shelf_widget()->GetBackgroundType());
514 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
515 EXPECT_EQ(SHELF_VISIBLE,
516 shelf_layout_manager()->visibility_state());
517 EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, shelf_widget()->GetBackgroundType());
520 // Verifies window visibility during various workspace changes.
521 TEST_F(WorkspaceControllerTest, VisibilityTests) {
522 scoped_ptr<Window> w1(CreateTestWindow());
523 w1->Show();
524 EXPECT_TRUE(w1->IsVisible());
525 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
527 // Create another window, activate it and make it fullscreen.
528 scoped_ptr<Window> w2(CreateTestWindow());
529 w2->Show();
530 wm::ActivateWindow(w2.get());
531 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
532 EXPECT_TRUE(w2->IsVisible());
533 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
534 EXPECT_TRUE(w1->IsVisible());
536 // Switch to w1. |w1| should be visible on top of |w2|.
537 wm::ActivateWindow(w1.get());
538 EXPECT_TRUE(w1->IsVisible());
539 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
540 EXPECT_TRUE(w2->IsVisible());
542 // Switch back to |w2|.
543 wm::ActivateWindow(w2.get());
544 EXPECT_TRUE(w2->IsVisible());
545 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
546 EXPECT_TRUE(w1->IsVisible());
548 // Restore |w2|, both windows should be visible.
549 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
550 EXPECT_TRUE(w1->IsVisible());
551 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
552 EXPECT_TRUE(w2->IsVisible());
553 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
555 // Make |w2| fullscreen again, then close it.
556 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
557 w2->Hide();
558 EXPECT_FALSE(w2->IsVisible());
559 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
560 EXPECT_TRUE(w1->IsVisible());
562 // Create |w2| and maximize it.
563 w2.reset(CreateTestWindow());
564 w2->Show();
565 wm::ActivateWindow(w2.get());
566 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
567 EXPECT_TRUE(w2->IsVisible());
568 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
569 EXPECT_TRUE(w1->IsVisible());
571 // Close |w2|.
572 w2.reset();
573 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
574 EXPECT_TRUE(w1->IsVisible());
577 // Verifies windows that are offscreen don't move when switching workspaces.
578 TEST_F(WorkspaceControllerTest, DontMoveOnSwitch) {
579 aura::test::EventGenerator generator(
580 Shell::GetPrimaryRootWindow(), gfx::Point());
581 generator.MoveMouseTo(0, 0);
583 scoped_ptr<Window> w1(CreateTestWindow());
584 ShelfLayoutManager* shelf = shelf_layout_manager();
585 const gfx::Rect touches_shelf_bounds(
586 0, shelf->GetIdealBounds().y() - 10, 101, 102);
587 // Move |w1| to overlap the shelf.
588 w1->SetBounds(touches_shelf_bounds);
589 w1->Show();
590 wm::ActivateWindow(w1.get());
592 // Create another window and maximize it.
593 scoped_ptr<Window> w2(CreateTestWindow());
594 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
595 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
596 w2->Show();
597 wm::ActivateWindow(w2.get());
599 // Switch to w1.
600 wm::ActivateWindow(w1.get());
601 EXPECT_EQ(touches_shelf_bounds.ToString(), w1->bounds().ToString());
604 // Verifies that windows that are completely offscreen move when switching
605 // workspaces.
606 TEST_F(WorkspaceControllerTest, MoveOnSwitch) {
607 aura::test::EventGenerator generator(
608 Shell::GetPrimaryRootWindow(), gfx::Point());
609 generator.MoveMouseTo(0, 0);
611 scoped_ptr<Window> w1(CreateTestWindow());
612 ShelfLayoutManager* shelf = shelf_layout_manager();
613 const gfx::Rect w1_bounds(0, shelf->GetIdealBounds().y(), 100, 200);
614 // Move |w1| so that the top edge is the same as the top edge of the shelf.
615 w1->SetBounds(w1_bounds);
616 w1->Show();
617 wm::ActivateWindow(w1.get());
618 EXPECT_EQ(w1_bounds.ToString(), w1->bounds().ToString());
620 // Create another window and maximize it.
621 scoped_ptr<Window> w2(CreateTestWindow());
622 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
623 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
624 w2->Show();
625 wm::ActivateWindow(w2.get());
627 // Increase the size of the WorkAreaInsets. This would make |w1| fall
628 // completely out of the display work area.
629 gfx::Insets insets =
630 Shell::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets();
631 insets.Set(0, 0, insets.bottom() + 30, 0);
632 Shell::GetInstance()->SetDisplayWorkAreaInsets(w1.get(), insets);
634 // Switch to w1. The window should have moved.
635 wm::ActivateWindow(w1.get());
636 EXPECT_NE(w1_bounds.ToString(), w1->bounds().ToString());
639 namespace {
641 // WindowDelegate used by DontCrashOnChangeAndActivate.
642 class DontCrashOnChangeAndActivateDelegate
643 : public aura::test::TestWindowDelegate {
644 public:
645 DontCrashOnChangeAndActivateDelegate() : window_(NULL) {}
647 void set_window(aura::Window* window) { window_ = window; }
649 // WindowDelegate overrides:
650 virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
651 const gfx::Rect& new_bounds) OVERRIDE {
652 if (window_) {
653 wm::ActivateWindow(window_);
654 window_ = NULL;
658 private:
659 aura::Window* window_;
661 DISALLOW_COPY_AND_ASSIGN(DontCrashOnChangeAndActivateDelegate);
664 } // namespace
666 // Exercises possible crash in W2. Here's the sequence:
667 // . minimize a maximized window.
668 // . remove the window (which happens when switching displays).
669 // . add the window back.
670 // . show the window and during the bounds change activate it.
671 TEST_F(WorkspaceControllerTest, DontCrashOnChangeAndActivate) {
672 // Force the shelf
673 ShelfLayoutManager* shelf = shelf_layout_manager();
674 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
676 DontCrashOnChangeAndActivateDelegate delegate;
677 scoped_ptr<Window> w1(CreateTestWindowInShellWithDelegate(
678 &delegate, 1000, gfx::Rect(10, 11, 250, 251)));
680 w1->Show();
681 wm::WindowState* w1_state = wm::GetWindowState(w1.get());
682 w1_state->Activate();
683 w1_state->Maximize();
684 w1_state->Minimize();
686 w1->parent()->RemoveChild(w1.get());
688 // Do this so that when we Show() the window a resize occurs and we make the
689 // window active.
690 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
692 ParentWindowInPrimaryRootWindow(w1.get());
693 delegate.set_window(w1.get());
694 w1->Show();
697 // Verifies a window with a transient parent not managed by workspace works.
698 TEST_F(WorkspaceControllerTest, TransientParent) {
699 // Normal window with no transient parent.
700 scoped_ptr<Window> w2(CreateTestWindow());
701 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
702 w2->Show();
703 wm::ActivateWindow(w2.get());
705 // Window with a transient parent. We set the transient parent to the root,
706 // which would never happen but is enough to exercise the bug.
707 scoped_ptr<Window> w1(CreateTestWindowUnparented());
708 ::wm::AddTransientChild(
709 Shell::GetInstance()->GetPrimaryRootWindow(), w1.get());
710 w1->SetBounds(gfx::Rect(10, 11, 250, 251));
711 ParentWindowInPrimaryRootWindow(w1.get());
712 w1->Show();
713 wm::ActivateWindow(w1.get());
715 // The window with the transient parent should get added to the same parent as
716 // the normal window.
717 EXPECT_EQ(w2->parent(), w1->parent());
720 // Test the placement of newly created windows.
721 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnCreate) {
722 if (!SupportsHostWindowResize())
723 return;
724 UpdateDisplay("1600x1200");
725 // Creating a popup handler here to make sure it does not interfere with the
726 // existing windows.
727 gfx::Rect source_browser_bounds(16, 32, 640, 320);
728 scoped_ptr<aura::Window> browser_window(CreateBrowserLikeWindow(
729 source_browser_bounds));
731 // Creating a popup to make sure it does not interfere with the positioning.
732 scoped_ptr<aura::Window> browser_popup(CreatePopupLikeWindow(
733 gfx::Rect(16, 32, 128, 256)));
735 browser_window->Show();
736 browser_popup->Show();
738 { // With a shown window it's size should get returned.
739 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
740 source_browser_bounds));
741 // The position should be right flush.
742 EXPECT_EQ("960,32 640x320", new_browser_window->bounds().ToString());
745 { // With the window shown - but more on the right side then on the left
746 // side (and partially out of the screen), it should default to the other
747 // side and inside the screen.
748 gfx::Rect source_browser_bounds(gfx::Rect(1000, 600, 640, 320));
749 browser_window->SetBounds(source_browser_bounds);
751 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
752 source_browser_bounds));
753 // The position should be left & bottom flush.
754 EXPECT_EQ("0,600 640x320", new_browser_window->bounds().ToString());
756 // If the other window was already beyond the point to get right flush
757 // it will remain where it is.
758 EXPECT_EQ("1000,600 640x320", browser_window->bounds().ToString());
761 { // Make sure that popups do not get changed.
762 scoped_ptr<aura::Window> new_popup_window(CreatePopupLikeWindow(
763 gfx::Rect(50, 100, 300, 150)));
764 EXPECT_EQ("50,100 300x150", new_popup_window->bounds().ToString());
767 browser_window->Hide();
768 { // If a window is there but not shown the default should be centered.
769 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
770 gfx::Rect(50, 100, 300, 150)));
771 EXPECT_EQ("650,100 300x150", new_browser_window->bounds().ToString());
775 // Test that adding a second window shifts both the first window and its
776 // transient child.
777 TEST_F(WorkspaceControllerTest, AutoPlacingMovesTransientChild) {
778 // Create an auto-positioned window.
779 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
780 gfx::Rect desktop_area = window1->parent()->bounds();
781 wm::GetWindowState(window1.get())->set_window_position_managed(true);
782 // Hide and then show |window1| to trigger auto-positioning logic.
783 window1->Hide();
784 window1->SetBounds(gfx::Rect(16, 32, 300, 300));
785 window1->Show();
787 // |window1| should be horizontally centered.
788 int x_window1 = (desktop_area.width() - 300) / 2;
789 EXPECT_EQ(base::IntToString(x_window1) + ",32 300x300",
790 window1->bounds().ToString());
792 // Create a |child| window and make it a transient child of |window1|.
793 scoped_ptr<Window> child(CreateTestWindowUnparented());
794 ::wm::AddTransientChild(window1.get(), child.get());
795 const int x_child = x_window1 + 50;
796 child->SetBounds(gfx::Rect(x_child, 20, 200, 200));
797 ParentWindowInPrimaryRootWindow(child.get());
798 child->Show();
799 wm::ActivateWindow(child.get());
801 // The |child| should be where it was created.
802 EXPECT_EQ(base::IntToString(x_child) + ",20 200x200",
803 child->bounds().ToString());
805 // Create and show a second window forcing the first window and its child to
806 // move.
807 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
808 wm::GetWindowState(window2.get())->set_window_position_managed(true);
809 // Hide and then show |window2| to trigger auto-positioning logic.
810 window2->Hide();
811 window2->SetBounds(gfx::Rect(32, 48, 250, 250));
812 window2->Show();
814 // Check that both |window1| and |child| have moved left.
815 EXPECT_EQ("0,32 300x300", window1->bounds().ToString());
816 int x = x_child - x_window1;
817 EXPECT_EQ(base::IntToString(x) + ",20 200x200", child->bounds().ToString());
818 // Check that |window2| has moved right.
819 x = desktop_area.width() - window2->bounds().width();
820 EXPECT_EQ(base::IntToString(x) + ",48 250x250", window2->bounds().ToString());
823 // Test the basic auto placement of one and or two windows in a "simulated
824 // session" of sequential window operations.
825 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnShowHide) {
826 // Test 1: In case there is no manageable window, no window should shift.
828 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
829 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
830 gfx::Rect desktop_area = window1->parent()->bounds();
832 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
833 // Trigger the auto window placement function by making it visible.
834 // Note that the bounds are getting changed while it is invisible.
835 window2->Hide();
836 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
837 window2->Show();
839 // Check the initial position of the windows is unchanged.
840 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
841 EXPECT_EQ("32,48 256x512", window2->bounds().ToString());
843 // Remove the second window and make sure that the first window
844 // does NOT get centered.
845 window2.reset();
846 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
848 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
849 // Test 2: Set up two managed windows and check their auto positioning.
850 window1_state->set_window_position_managed(true);
852 scoped_ptr<aura::Window> window3(CreateTestWindowInShellWithId(2));
853 wm::GetWindowState(window3.get())->set_window_position_managed(true);
854 // To avoid any auto window manager changes due to SetBounds, the window
855 // gets first hidden and then shown again.
856 window3->Hide();
857 window3->SetBounds(gfx::Rect(32, 48, 256, 512));
858 window3->Show();
859 // |window1| should be flush left and |window3| flush right.
860 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
861 EXPECT_EQ(base::IntToString(
862 desktop_area.width() - window3->bounds().width()) +
863 ",48 256x512", window3->bounds().ToString());
865 // After removing |window3|, |window1| should be centered again.
866 window3.reset();
867 EXPECT_EQ(
868 base::IntToString(
869 (desktop_area.width() - window1->bounds().width()) / 2) +
870 ",32 640x320", window1->bounds().ToString());
872 // Test 3: Set up a manageable and a non manageable window and check
873 // positioning.
874 scoped_ptr<aura::Window> window4(CreateTestWindowInShellWithId(3));
875 // To avoid any auto window manager changes due to SetBounds, the window
876 // gets first hidden and then shown again.
877 window1->Hide();
878 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
879 window4->SetBounds(gfx::Rect(32, 48, 256, 512));
880 window1->Show();
881 // |window1| should be centered and |window4| untouched.
882 EXPECT_EQ(
883 base::IntToString(
884 (desktop_area.width() - window1->bounds().width()) / 2) +
885 ",32 640x320", window1->bounds().ToString());
886 EXPECT_EQ("32,48 256x512", window4->bounds().ToString());
888 // Test4: A single manageable window should get centered.
889 window4.reset();
890 window1_state->set_bounds_changed_by_user(false);
891 // Trigger the auto window placement function by showing (and hiding) it.
892 window1->Hide();
893 window1->Show();
894 // |window1| should be centered.
895 EXPECT_EQ(
896 base::IntToString(
897 (desktop_area.width() - window1->bounds().width()) / 2) +
898 ",32 640x320", window1->bounds().ToString());
901 // Test the proper usage of user window movement interaction.
902 TEST_F(WorkspaceControllerTest, TestUserMovedWindowRepositioning) {
903 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
904 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
905 gfx::Rect desktop_area = window1->parent()->bounds();
906 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
907 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
908 window1->Hide();
909 window2->Hide();
910 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
911 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
913 window1_state->set_window_position_managed(true);
914 window2_state->set_window_position_managed(true);
915 EXPECT_FALSE(window1_state->bounds_changed_by_user());
916 EXPECT_FALSE(window2_state->bounds_changed_by_user());
918 // Check that the current location gets preserved if the user has
919 // positioned it previously.
920 window1_state->set_bounds_changed_by_user(true);
921 window1->Show();
922 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
923 // Flag should be still set.
924 EXPECT_TRUE(window1_state->bounds_changed_by_user());
925 EXPECT_FALSE(window2_state->bounds_changed_by_user());
927 // Turn on the second window and make sure that both windows are now
928 // positionable again (user movement cleared).
929 window2->Show();
931 // |window1| should be flush left and |window2| flush right.
932 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
933 EXPECT_EQ(
934 base::IntToString(desktop_area.width() - window2->bounds().width()) +
935 ",48 256x512", window2->bounds().ToString());
936 // FLag should now be reset.
937 EXPECT_FALSE(window1_state->bounds_changed_by_user());
938 EXPECT_FALSE(window2_state->bounds_changed_by_user());
940 // Going back to one shown window should keep the state.
941 window1_state->set_bounds_changed_by_user(true);
942 window2->Hide();
943 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
944 EXPECT_TRUE(window1_state->bounds_changed_by_user());
947 // Test if the single window will be restored at original position.
948 TEST_F(WorkspaceControllerTest, TestSingleWindowsRestoredBounds) {
949 scoped_ptr<aura::Window> window1(
950 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
951 scoped_ptr<aura::Window> window2(
952 CreateTestWindowInShellWithBounds(gfx::Rect(110, 110, 100, 100)));
953 scoped_ptr<aura::Window> window3(
954 CreateTestWindowInShellWithBounds(gfx::Rect(120, 120, 100, 100)));
955 window1->Hide();
956 window2->Hide();
957 window3->Hide();
958 wm::GetWindowState(window1.get())->set_window_position_managed(true);
959 wm::GetWindowState(window2.get())->set_window_position_managed(true);
960 wm::GetWindowState(window3.get())->set_window_position_managed(true);
962 window1->Show();
963 wm::ActivateWindow(window1.get());
964 window2->Show();
965 wm::ActivateWindow(window2.get());
966 window3->Show();
967 wm::ActivateWindow(window3.get());
968 EXPECT_EQ(0, window1->bounds().x());
969 EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
970 window2->bounds().right());
971 EXPECT_EQ(0, window3->bounds().x());
973 window1->Hide();
974 EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
975 window2->bounds().right());
976 EXPECT_EQ(0, window3->bounds().x());
978 // Being a single window will retore the original location.
979 window3->Hide();
980 wm::ActivateWindow(window2.get());
981 EXPECT_EQ("110,110 100x100", window2->bounds().ToString());
983 // Showing the 3rd will push the 2nd window left.
984 window3->Show();
985 wm::ActivateWindow(window3.get());
986 EXPECT_EQ(0, window2->bounds().x());
987 EXPECT_EQ(window3->GetRootWindow()->bounds().right(),
988 window3->bounds().right());
990 // Being a single window will retore the original location.
991 window2->Hide();
992 EXPECT_EQ("120,120 100x100", window3->bounds().ToString());
995 // Test that user placed windows go back to their user placement after the user
996 // closes all other windows.
997 TEST_F(WorkspaceControllerTest, TestUserHandledWindowRestore) {
998 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
999 gfx::Rect user_pos = gfx::Rect(16, 42, 640, 320);
1000 window1->SetBounds(user_pos);
1001 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1003 window1_state->SetPreAutoManageWindowBounds(user_pos);
1004 gfx::Rect desktop_area = window1->parent()->bounds();
1006 // Create a second window to let the auto manager kick in.
1007 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1008 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1009 window1->Hide();
1010 window2->Hide();
1011 wm::GetWindowState(window1.get())->set_window_position_managed(true);
1012 wm::GetWindowState(window2.get())->set_window_position_managed(true);
1013 window1->Show();
1014 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
1015 window2->Show();
1017 // |window1| should be flush left and |window2| flush right.
1018 EXPECT_EQ("0," + base::IntToString(user_pos.y()) +
1019 " 640x320", window1->bounds().ToString());
1020 EXPECT_EQ(
1021 base::IntToString(desktop_area.width() - window2->bounds().width()) +
1022 ",48 256x512", window2->bounds().ToString());
1023 window2->Hide();
1025 // After the other window get hidden the window has to move back to the
1026 // previous position and the bounds should still be set and unchanged.
1027 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
1028 ASSERT_TRUE(window1_state->pre_auto_manage_window_bounds());
1029 EXPECT_EQ(user_pos.ToString(),
1030 window1_state->pre_auto_manage_window_bounds()->ToString());
1033 // Test that a window from normal to minimize will repos the remaining.
1034 TEST_F(WorkspaceControllerTest, ToMinimizeRepositionsRemaining) {
1035 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1036 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1037 window1_state->set_window_position_managed(true);
1038 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1039 gfx::Rect desktop_area = window1->parent()->bounds();
1041 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1042 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
1043 window2_state->set_window_position_managed(true);
1044 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1046 window1_state->Minimize();
1048 // |window2| should be centered now.
1049 EXPECT_TRUE(window2->IsVisible());
1050 EXPECT_TRUE(window2_state->IsNormalStateType());
1051 EXPECT_EQ(base::IntToString(
1052 (desktop_area.width() - window2->bounds().width()) / 2) +
1053 ",48 256x512", window2->bounds().ToString());
1055 window1_state->Restore();
1056 // |window1| should be flush right and |window3| flush left.
1057 EXPECT_EQ(base::IntToString(
1058 desktop_area.width() - window1->bounds().width()) +
1059 ",32 640x320", window1->bounds().ToString());
1060 EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
1063 // Test that minimizing an initially maximized window will repos the remaining.
1064 TEST_F(WorkspaceControllerTest, MaxToMinRepositionsRemaining) {
1065 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1066 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1067 window1_state->set_window_position_managed(true);
1068 gfx::Rect desktop_area = window1->parent()->bounds();
1070 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1071 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
1072 window2_state->set_window_position_managed(true);
1073 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1075 window1_state->Maximize();
1076 window1_state->Minimize();
1078 // |window2| should be centered now.
1079 EXPECT_TRUE(window2->IsVisible());
1080 EXPECT_TRUE(window2_state->IsNormalStateType());
1081 EXPECT_EQ(base::IntToString(
1082 (desktop_area.width() - window2->bounds().width()) / 2) +
1083 ",48 256x512", window2->bounds().ToString());
1086 // Test that nomral, maximize, minimizing will repos the remaining.
1087 TEST_F(WorkspaceControllerTest, NormToMaxToMinRepositionsRemaining) {
1088 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1089 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1090 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1091 window1_state->set_window_position_managed(true);
1092 gfx::Rect desktop_area = window1->parent()->bounds();
1094 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1095 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
1096 window2_state->set_window_position_managed(true);
1097 window2->SetBounds(gfx::Rect(32, 40, 256, 512));
1099 // Trigger the auto window placement function by showing (and hiding) it.
1100 window1->Hide();
1101 window1->Show();
1103 // |window1| should be flush right and |window3| flush left.
1104 EXPECT_EQ(base::IntToString(
1105 desktop_area.width() - window1->bounds().width()) +
1106 ",32 640x320", window1->bounds().ToString());
1107 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1109 window1_state->Maximize();
1110 window1_state->Minimize();
1112 // |window2| should be centered now.
1113 EXPECT_TRUE(window2->IsVisible());
1114 EXPECT_TRUE(window2_state->IsNormalStateType());
1115 EXPECT_EQ(base::IntToString(
1116 (desktop_area.width() - window2->bounds().width()) / 2) +
1117 ",40 256x512", window2->bounds().ToString());
1120 // Test that nomral, maximize, normal will repos the remaining.
1121 TEST_F(WorkspaceControllerTest, NormToMaxToNormRepositionsRemaining) {
1122 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1123 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1124 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1125 window1_state->set_window_position_managed(true);
1126 gfx::Rect desktop_area = window1->parent()->bounds();
1128 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1129 wm::GetWindowState(window2.get())->set_window_position_managed(true);
1130 window2->SetBounds(gfx::Rect(32, 40, 256, 512));
1132 // Trigger the auto window placement function by showing (and hiding) it.
1133 window1->Hide();
1134 window1->Show();
1136 // |window1| should be flush right and |window3| flush left.
1137 EXPECT_EQ(base::IntToString(
1138 desktop_area.width() - window1->bounds().width()) +
1139 ",32 640x320", window1->bounds().ToString());
1140 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1142 window1_state->Maximize();
1143 window1_state->Restore();
1145 // |window1| should be flush right and |window2| 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());
1152 // Test that animations are triggered.
1153 TEST_F(WorkspaceControllerTest, AnimatedNormToMaxToNormRepositionsRemaining) {
1154 ui::ScopedAnimationDurationScaleMode normal_duration_mode(
1155 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
1156 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1157 window1->Hide();
1158 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1159 gfx::Rect desktop_area = window1->parent()->bounds();
1160 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1161 window2->Hide();
1162 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1164 wm::GetWindowState(window1.get())->set_window_position_managed(true);
1165 wm::GetWindowState(window2.get())->set_window_position_managed(true);
1166 // Make sure nothing is animating.
1167 window1->layer()->GetAnimator()->StopAnimating();
1168 window2->layer()->GetAnimator()->StopAnimating();
1169 window2->Show();
1171 // The second window should now animate.
1172 EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating());
1173 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1174 window2->layer()->GetAnimator()->StopAnimating();
1176 window1->Show();
1177 EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
1178 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1180 window1->layer()->GetAnimator()->StopAnimating();
1181 window2->layer()->GetAnimator()->StopAnimating();
1182 // |window1| should be flush right and |window2| flush left.
1183 EXPECT_EQ(base::IntToString(
1184 desktop_area.width() - window1->bounds().width()) +
1185 ",32 640x320", window1->bounds().ToString());
1186 EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
1189 // This tests simulates a browser and an app and verifies the ordering of the
1190 // windows and layers doesn't get out of sync as various operations occur. Its
1191 // really testing code in FocusController, but easier to simulate here. Just as
1192 // with a real browser the browser here has a transient child window
1193 // (corresponds to the status bubble).
1194 TEST_F(WorkspaceControllerTest, VerifyLayerOrdering) {
1195 scoped_ptr<Window> browser(aura::test::CreateTestWindowWithDelegate(
1196 NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL));
1197 browser->SetName("browser");
1198 ParentWindowInPrimaryRootWindow(browser.get());
1199 browser->Show();
1200 wm::ActivateWindow(browser.get());
1202 // |status_bubble| is made a transient child of |browser| and as a result
1203 // owned by |browser|.
1204 aura::test::TestWindowDelegate* status_bubble_delegate =
1205 aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate();
1206 status_bubble_delegate->set_can_focus(false);
1207 Window* status_bubble =
1208 aura::test::CreateTestWindowWithDelegate(status_bubble_delegate,
1209 ui::wm::WINDOW_TYPE_POPUP,
1210 gfx::Rect(5, 6, 7, 8),
1211 NULL);
1212 ::wm::AddTransientChild(browser.get(), status_bubble);
1213 ParentWindowInPrimaryRootWindow(status_bubble);
1214 status_bubble->SetName("status_bubble");
1216 scoped_ptr<Window> app(aura::test::CreateTestWindowWithDelegate(
1217 NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL));
1218 app->SetName("app");
1219 ParentWindowInPrimaryRootWindow(app.get());
1221 aura::Window* parent = browser->parent();
1223 app->Show();
1224 wm::ActivateWindow(app.get());
1225 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1227 // Minimize the app, focus should go the browser.
1228 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
1229 EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
1230 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1232 // Minimize the browser (neither windows are focused).
1233 browser->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
1234 EXPECT_FALSE(wm::IsActiveWindow(browser.get()));
1235 EXPECT_FALSE(wm::IsActiveWindow(app.get()));
1236 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1238 // Show the browser (which should restore it).
1239 browser->Show();
1240 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1242 // Activate the browser.
1243 ash::wm::ActivateWindow(browser.get());
1244 EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
1245 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1247 // Restore the app. This differs from above code for |browser| as internally
1248 // the app code does this. Restoring this way or using Show() should not make
1249 // a difference.
1250 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1251 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1253 // Activate the app.
1254 ash::wm::ActivateWindow(app.get());
1255 EXPECT_TRUE(wm::IsActiveWindow(app.get()));
1256 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1259 namespace {
1261 // Used by DragMaximizedNonTrackedWindow to track how many times the window
1262 // hierarchy changes affecting the specified window.
1263 class DragMaximizedNonTrackedWindowObserver
1264 : public aura::WindowObserver {
1265 public:
1266 DragMaximizedNonTrackedWindowObserver(aura::Window* window)
1267 : change_count_(0),
1268 window_(window) {
1271 // Number of times OnWindowHierarchyChanged() has been received.
1272 void clear_change_count() { change_count_ = 0; }
1273 int change_count() const {
1274 return change_count_;
1277 // aura::WindowObserver overrides:
1278 // Counts number of times a window is reparented. Ignores reparenting into and
1279 // from a docked container which is expected when a tab is dragged.
1280 virtual void OnWindowHierarchyChanged(
1281 const HierarchyChangeParams& params) OVERRIDE {
1282 if (params.target != window_ ||
1283 (params.old_parent->id() == kShellWindowId_DefaultContainer &&
1284 params.new_parent->id() == kShellWindowId_DockedContainer) ||
1285 (params.old_parent->id() == kShellWindowId_DockedContainer &&
1286 params.new_parent->id() == kShellWindowId_DefaultContainer)) {
1287 return;
1289 change_count_++;
1292 private:
1293 int change_count_;
1294 aura::Window* window_;
1296 DISALLOW_COPY_AND_ASSIGN(DragMaximizedNonTrackedWindowObserver);
1299 } // namespace
1301 // Verifies that a new maximized window becomes visible after its activation
1302 // is requested, even though it does not become activated because a system
1303 // modal window is active.
1304 TEST_F(WorkspaceControllerTest, SwitchFromModal) {
1305 scoped_ptr<Window> modal_window(CreateTestWindowUnparented());
1306 modal_window->SetBounds(gfx::Rect(10, 11, 21, 22));
1307 modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
1308 ParentWindowInPrimaryRootWindow(modal_window.get());
1309 modal_window->Show();
1310 wm::ActivateWindow(modal_window.get());
1312 scoped_ptr<Window> maximized_window(CreateTestWindow());
1313 maximized_window->SetProperty(
1314 aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
1315 maximized_window->Show();
1316 wm::ActivateWindow(maximized_window.get());
1317 EXPECT_TRUE(maximized_window->IsVisible());
1320 namespace {
1322 // Subclass of WorkspaceControllerTest that runs tests with docked windows
1323 // enabled and disabled.
1324 class WorkspaceControllerTestDragging
1325 : public WorkspaceControllerTest,
1326 public testing::WithParamInterface<bool> {
1327 public:
1328 WorkspaceControllerTestDragging() {}
1329 virtual ~WorkspaceControllerTestDragging() {}
1331 // testing::Test:
1332 virtual void SetUp() OVERRIDE {
1333 WorkspaceControllerTest::SetUp();
1334 if (!docked_windows_enabled()) {
1335 CommandLine::ForCurrentProcess()->AppendSwitch(
1336 ash::switches::kAshDisableDockedWindows);
1340 bool docked_windows_enabled() const { return GetParam(); }
1342 private:
1343 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTestDragging);
1346 } // namespace
1348 // Verifies that when dragging a window over the shelf overlap is detected
1349 // during and after the drag.
1350 TEST_P(WorkspaceControllerTestDragging, DragWindowOverlapShelf) {
1351 aura::test::TestWindowDelegate delegate;
1352 delegate.set_window_component(HTCAPTION);
1353 scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate(
1354 &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL));
1355 ParentWindowInPrimaryRootWindow(w1.get());
1357 ShelfLayoutManager* shelf = shelf_layout_manager();
1358 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
1360 // Drag near the shelf.
1361 aura::test::EventGenerator generator(
1362 Shell::GetPrimaryRootWindow(), gfx::Point());
1363 generator.MoveMouseTo(10, 10);
1364 generator.PressLeftButton();
1365 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 70);
1367 // Shelf should not be in overlapped state.
1368 EXPECT_FALSE(GetWindowOverlapsShelf());
1370 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 20);
1372 // Shelf should detect overlap. Overlap state stays after mouse is released.
1373 EXPECT_TRUE(GetWindowOverlapsShelf());
1374 generator.ReleaseLeftButton();
1375 EXPECT_TRUE(GetWindowOverlapsShelf());
1378 // Verifies that when dragging a window autohidden shelf stays hidden during
1379 // and after the drag.
1380 TEST_P(WorkspaceControllerTestDragging, DragWindowKeepsShelfAutohidden) {
1381 aura::test::TestWindowDelegate delegate;
1382 delegate.set_window_component(HTCAPTION);
1383 scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate(
1384 &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL));
1385 ParentWindowInPrimaryRootWindow(w1.get());
1387 ShelfLayoutManager* shelf = shelf_layout_manager();
1388 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
1389 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
1391 // Drag very little.
1392 aura::test::EventGenerator generator(
1393 Shell::GetPrimaryRootWindow(), gfx::Point());
1394 generator.MoveMouseTo(10, 10);
1395 generator.PressLeftButton();
1396 generator.MoveMouseTo(12, 12);
1398 // Shelf should be hidden during and after the drag.
1399 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
1400 generator.ReleaseLeftButton();
1401 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
1404 INSTANTIATE_TEST_CASE_P(DockedOrNot, WorkspaceControllerTestDragging,
1405 ::testing::Bool());
1407 // Verifies that events are targeted properly just outside the window edges.
1408 TEST_F(WorkspaceControllerTest, WindowEdgeHitTest) {
1409 aura::test::TestWindowDelegate d_first, d_second;
1410 scoped_ptr<Window> first(aura::test::CreateTestWindowWithDelegate(&d_first,
1411 123, gfx::Rect(20, 10, 100, 50), NULL));
1412 ParentWindowInPrimaryRootWindow(first.get());
1413 first->Show();
1415 scoped_ptr<Window> second(aura::test::CreateTestWindowWithDelegate(&d_second,
1416 234, gfx::Rect(30, 40, 40, 10), NULL));
1417 ParentWindowInPrimaryRootWindow(second.get());
1418 second->Show();
1420 ui::EventTarget* root = first->GetRootWindow();
1421 ui::EventTargeter* targeter = root->GetEventTargeter();
1423 // The windows overlap, and |second| is on top of |first|. Events targeted
1424 // slightly outside the edges of the |second| window should still be targeted
1425 // to |second| to allow resizing the windows easily.
1427 const int kNumPoints = 4;
1428 struct {
1429 const char* direction;
1430 gfx::Point location;
1431 } points[kNumPoints] = {
1432 { "left", gfx::Point(28, 45) }, // outside the left edge.
1433 { "top", gfx::Point(50, 38) }, // outside the top edge.
1434 { "right", gfx::Point(72, 45) }, // outside the right edge.
1435 { "bottom", gfx::Point(50, 52) }, // outside the bottom edge.
1437 // Do two iterations, first without any transform on |second|, and the second
1438 // time after applying some transform on |second| so that it doesn't get
1439 // targeted.
1440 for (int times = 0; times < 2; ++times) {
1441 SCOPED_TRACE(times == 0 ? "Without transform" : "With transform");
1442 aura::Window* expected_target = times == 0 ? second.get() : first.get();
1443 for (int i = 0; i < kNumPoints; ++i) {
1444 SCOPED_TRACE(points[i].direction);
1445 const gfx::Point& location = points[i].location;
1446 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
1447 ui::EF_NONE);
1448 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
1449 EXPECT_EQ(expected_target, target);
1451 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
1452 ui::EventTimeForNow());
1453 target = targeter->FindTargetForEvent(root, &touch);
1454 EXPECT_EQ(expected_target, target);
1456 // Apply a transform on |second|. After the transform is applied, the window
1457 // should no longer be targeted.
1458 gfx::Transform transform;
1459 transform.Translate(70, 40);
1460 second->SetTransform(transform);
1464 // Verifies events targeting just outside the window edges for panels.
1465 TEST_F(WorkspaceControllerTest, WindowEdgeHitTestPanel) {
1466 aura::test::TestWindowDelegate delegate;
1467 scoped_ptr<Window> window(CreateTestPanel(&delegate,
1468 gfx::Rect(20, 10, 100, 50)));
1469 ui::EventTarget* root = window->GetRootWindow();
1470 ui::EventTargeter* targeter = root->GetEventTargeter();
1471 const gfx::Rect bounds = window->bounds();
1472 const int kNumPoints = 5;
1473 struct {
1474 const char* direction;
1475 gfx::Point location;
1476 bool is_target_hit;
1477 } points[kNumPoints] = {
1478 { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true },
1479 { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true },
1480 { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true },
1481 { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), true },
1482 { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false },
1484 for (int i = 0; i < kNumPoints; ++i) {
1485 SCOPED_TRACE(points[i].direction);
1486 const gfx::Point& location = points[i].location;
1487 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
1488 ui::EF_NONE);
1489 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
1490 if (points[i].is_target_hit)
1491 EXPECT_EQ(window.get(), target);
1492 else
1493 EXPECT_NE(window.get(), target);
1495 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
1496 ui::EventTimeForNow());
1497 target = targeter->FindTargetForEvent(root, &touch);
1498 if (points[i].is_target_hit)
1499 EXPECT_EQ(window.get(), target);
1500 else
1501 EXPECT_NE(window.get(), target);
1505 // Verifies events targeting just outside the window edges for docked windows.
1506 TEST_F(WorkspaceControllerTest, WindowEdgeHitTestDocked) {
1507 if (!switches::UseDockedWindows())
1508 return;
1509 aura::test::TestWindowDelegate delegate;
1510 // Make window smaller than the minimum docked area so that the window edges
1511 // are exposed.
1512 delegate.set_maximum_size(gfx::Size(180, 200));
1513 scoped_ptr<Window> window(aura::test::CreateTestWindowWithDelegate(&delegate,
1514 123, gfx::Rect(20, 10, 100, 50), NULL));
1515 ParentWindowInPrimaryRootWindow(window.get());
1516 aura::Window* docked_container = Shell::GetContainer(
1517 window->GetRootWindow(), kShellWindowId_DockedContainer);
1518 docked_container->AddChild(window.get());
1519 window->Show();
1520 ui::EventTarget* root = window->GetRootWindow();
1521 ui::EventTargeter* targeter = root->GetEventTargeter();
1522 const gfx::Rect bounds = window->bounds();
1523 const int kNumPoints = 5;
1524 struct {
1525 const char* direction;
1526 gfx::Point location;
1527 bool is_target_hit;
1528 } points[kNumPoints] = {
1529 { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true },
1530 { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true },
1531 { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true },
1532 { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), true },
1533 { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false },
1535 for (int i = 0; i < kNumPoints; ++i) {
1536 SCOPED_TRACE(points[i].direction);
1537 const gfx::Point& location = points[i].location;
1538 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
1539 ui::EF_NONE);
1540 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
1541 if (points[i].is_target_hit)
1542 EXPECT_EQ(window.get(), target);
1543 else
1544 EXPECT_NE(window.get(), target);
1546 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
1547 ui::EventTimeForNow());
1548 target = targeter->FindTargetForEvent(root, &touch);
1549 if (points[i].is_target_hit)
1550 EXPECT_EQ(window.get(), target);
1551 else
1552 EXPECT_NE(window.get(), target);
1556 } // namespace ash