Move render_view_context_menu.* and related files out of tab_contents.
[chromium-blink-merge.git] / ash / wm / workspace_controller_unittest.cc
blob68e4f878cad1f039bef6017a4cfcf73cdce10ef1
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/corewm/window_animations.h"
38 #include "ui/views/corewm/window_util.h"
39 #include "ui/views/widget/widget.h"
41 using aura::Window;
43 namespace ash {
44 namespace internal {
46 // Returns a string containing the names of all the children of |window| (in
47 // order). Each entry is separated by a space.
48 std::string GetWindowNames(const aura::Window* window) {
49 std::string result;
50 for (size_t i = 0; i < window->children().size(); ++i) {
51 if (i != 0)
52 result += " ";
53 result += window->children()[i]->name();
55 return result;
58 // Returns a string containing the names of windows corresponding to each of the
59 // child layers of |window|'s layer. Any layers that don't correspond to a child
60 // Window of |window| are ignored. The result is ordered based on the layer
61 // ordering.
62 std::string GetLayerNames(const aura::Window* window) {
63 typedef std::map<const ui::Layer*, std::string> LayerToWindowNameMap;
64 LayerToWindowNameMap window_names;
65 for (size_t i = 0; i < window->children().size(); ++i) {
66 window_names[window->children()[i]->layer()] =
67 window->children()[i]->name();
70 std::string result;
71 const std::vector<ui::Layer*>& layers(window->layer()->children());
72 for (size_t i = 0; i < layers.size(); ++i) {
73 LayerToWindowNameMap::iterator layer_i =
74 window_names.find(layers[i]);
75 if (layer_i != window_names.end()) {
76 if (!result.empty())
77 result += " ";
78 result += layer_i->second;
81 return result;
84 class WorkspaceControllerTest : public test::AshTestBase {
85 public:
86 WorkspaceControllerTest() {}
87 virtual ~WorkspaceControllerTest() {}
89 aura::Window* CreateTestWindowUnparented() {
90 aura::Window* window = new aura::Window(NULL);
91 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
92 window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
93 window->Init(aura::WINDOW_LAYER_TEXTURED);
94 return window;
97 aura::Window* CreateTestWindow() {
98 aura::Window* window = new aura::Window(NULL);
99 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
100 window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
101 window->Init(aura::WINDOW_LAYER_TEXTURED);
102 ParentWindowInPrimaryRootWindow(window);
103 return window;
106 aura::Window* CreateBrowserLikeWindow(const gfx::Rect& bounds) {
107 aura::Window* window = CreateTestWindow();
108 window->SetBounds(bounds);
109 wm::WindowState* window_state = wm::GetWindowState(window);
110 window_state->set_window_position_managed(true);
111 window->Show();
112 return window;
115 aura::Window* CreatePopupLikeWindow(const gfx::Rect& bounds) {
116 aura::Window* window = CreateTestWindowInShellWithBounds(bounds);
117 window->Show();
118 return window;
121 aura::Window* CreateTestPanel(aura::WindowDelegate* delegate,
122 const gfx::Rect& bounds) {
123 aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
124 delegate,
125 ui::wm::WINDOW_TYPE_PANEL,
127 bounds);
128 test::TestShelfDelegate* shelf_delegate =
129 test::TestShelfDelegate::instance();
130 shelf_delegate->AddShelfItem(window);
131 PanelLayoutManager* manager =
132 static_cast<PanelLayoutManager*>(
133 Shell::GetContainer(window->GetRootWindow(),
134 internal::kShellWindowId_PanelContainer)->
135 layout_manager());
136 manager->Relayout();
137 return window;
140 aura::Window* GetDesktop() {
141 return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
142 kShellWindowId_DefaultContainer);
145 gfx::Rect GetFullscreenBounds(aura::Window* window) {
146 return Shell::GetScreen()->GetDisplayNearestWindow(window).bounds();
149 ShelfWidget* shelf_widget() {
150 return Shell::GetPrimaryRootWindowController()->shelf();
153 ShelfLayoutManager* shelf_layout_manager() {
154 return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
157 bool GetWindowOverlapsShelf() {
158 return shelf_layout_manager()->window_overlaps_shelf();
161 private:
162 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTest);
165 // Assertions around adding a normal window.
166 TEST_F(WorkspaceControllerTest, AddNormalWindowWhenEmpty) {
167 scoped_ptr<Window> w1(CreateTestWindow());
168 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
170 wm::WindowState* window_state = wm::GetWindowState(w1.get());
172 EXPECT_FALSE(window_state->HasRestoreBounds());
174 w1->Show();
176 EXPECT_FALSE(window_state->HasRestoreBounds());
178 ASSERT_TRUE(w1->layer() != NULL);
179 EXPECT_TRUE(w1->layer()->visible());
181 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
183 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
186 // Assertions around maximizing/unmaximizing.
187 TEST_F(WorkspaceControllerTest, SingleMaximizeWindow) {
188 scoped_ptr<Window> w1(CreateTestWindow());
189 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
191 w1->Show();
192 wm::ActivateWindow(w1.get());
194 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
196 ASSERT_TRUE(w1->layer() != NULL);
197 EXPECT_TRUE(w1->layer()->visible());
199 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
201 // Maximize the window.
202 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
204 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
206 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
207 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).width(),
208 w1->bounds().width());
209 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()).height(),
210 w1->bounds().height());
212 // Restore the window.
213 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
215 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
216 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
219 // Assertions around two windows and toggling one to be fullscreen.
220 TEST_F(WorkspaceControllerTest, FullscreenWithNormalWindow) {
221 scoped_ptr<Window> w1(CreateTestWindow());
222 scoped_ptr<Window> w2(CreateTestWindow());
223 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
224 w1->Show();
226 ASSERT_TRUE(w1->layer() != NULL);
227 EXPECT_TRUE(w1->layer()->visible());
229 w2->SetBounds(gfx::Rect(0, 0, 50, 51));
230 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
231 w2->Show();
232 wm::ActivateWindow(w2.get());
234 // Both windows should be in the same workspace.
235 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
236 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
238 gfx::Rect work_area(
239 ScreenUtil::GetMaximizedWindowBoundsInParent(w1.get()));
240 EXPECT_EQ(work_area.width(), w2->bounds().width());
241 EXPECT_EQ(work_area.height(), w2->bounds().height());
243 // Restore w2, which should then go back to one workspace.
244 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
245 EXPECT_EQ(50, w2->bounds().width());
246 EXPECT_EQ(51, w2->bounds().height());
247 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
250 // Makes sure requests to change the bounds of a normal window go through.
251 TEST_F(WorkspaceControllerTest, ChangeBoundsOfNormalWindow) {
252 scoped_ptr<Window> w1(CreateTestWindow());
253 w1->Show();
255 // Setting the bounds should go through since the window is in the normal
256 // workspace.
257 w1->SetBounds(gfx::Rect(0, 0, 200, 500));
258 EXPECT_EQ(200, w1->bounds().width());
259 EXPECT_EQ(500, w1->bounds().height());
262 // Verifies the bounds is not altered when showing and grid is enabled.
263 TEST_F(WorkspaceControllerTest, SnapToGrid) {
264 scoped_ptr<Window> w1(CreateTestWindowUnparented());
265 w1->SetBounds(gfx::Rect(1, 6, 25, 30));
266 ParentWindowInPrimaryRootWindow(w1.get());
267 // We are not aligning this anymore this way. When the window gets shown
268 // the window is expected to be handled differently, but this cannot be
269 // tested with this test. So the result of this test should be that the
270 // bounds are exactly as passed in.
271 EXPECT_EQ("1,6 25x30", w1->bounds().ToString());
274 // Assertions around a fullscreen window.
275 TEST_F(WorkspaceControllerTest, SingleFullscreenWindow) {
276 scoped_ptr<Window> w1(CreateTestWindow());
277 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
278 // Make the window fullscreen.
279 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
280 w1->Show();
281 wm::ActivateWindow(w1.get());
283 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
284 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
285 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
287 // Restore the window. Use SHOW_STATE_DEFAULT as that is what we'll end up
288 // with when using views::Widget.
289 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT);
290 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
292 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
293 EXPECT_EQ(250, w1->bounds().width());
294 EXPECT_EQ(251, w1->bounds().height());
296 // Back to fullscreen.
297 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
298 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
299 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
300 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
301 wm::WindowState* window_state = wm::GetWindowState(w1.get());
303 ASSERT_TRUE(window_state->HasRestoreBounds());
304 EXPECT_EQ("0,0 250x251", window_state->GetRestoreBoundsInScreen().ToString());
307 // Assertions around minimizing a single window.
308 TEST_F(WorkspaceControllerTest, MinimizeSingleWindow) {
309 scoped_ptr<Window> w1(CreateTestWindow());
311 w1->Show();
313 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
314 EXPECT_FALSE(w1->layer()->IsDrawn());
315 EXPECT_TRUE(w1->layer()->GetTargetTransform().IsIdentity());
317 // Show the window.
318 w1->Show();
319 EXPECT_TRUE(wm::GetWindowState(w1.get())->IsNormalShowType());
320 EXPECT_TRUE(w1->layer()->IsDrawn());
323 // Assertions around minimizing a fullscreen window.
324 TEST_F(WorkspaceControllerTest, MinimizeFullscreenWindow) {
325 // Two windows, w1 normal, w2 fullscreen.
326 scoped_ptr<Window> w1(CreateTestWindow());
327 scoped_ptr<Window> w2(CreateTestWindow());
328 w1->Show();
329 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
330 w2->Show();
332 wm::WindowState* w1_state = wm::GetWindowState(w1.get());
333 wm::WindowState* w2_state = wm::GetWindowState(w2.get());
335 w2_state->Activate();
337 // Minimize w2.
338 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
339 EXPECT_TRUE(w1->layer()->IsDrawn());
340 EXPECT_FALSE(w2->layer()->IsDrawn());
342 // Show the window, which should trigger unminimizing.
343 w2->Show();
344 w2_state->Activate();
346 EXPECT_TRUE(w2_state->IsFullscreen());
347 EXPECT_TRUE(w1->layer()->IsDrawn());
348 EXPECT_TRUE(w2->layer()->IsDrawn());
350 // Minimize the window, which should hide the window.
351 EXPECT_TRUE(w2_state->IsActive());
352 w2_state->Minimize();
353 EXPECT_FALSE(w2_state->IsActive());
354 EXPECT_FALSE(w2->layer()->IsDrawn());
355 EXPECT_TRUE(w1_state->IsActive());
357 // Make the window normal.
358 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
359 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
360 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
361 EXPECT_TRUE(w2->layer()->IsDrawn());
364 // Verifies ShelfLayoutManager's visibility/auto-hide state is correctly
365 // updated.
366 TEST_F(WorkspaceControllerTest, ShelfStateUpdated) {
367 // Since ShelfLayoutManager queries for mouse location, move the mouse so
368 // it isn't over the shelf.
369 aura::test::EventGenerator generator(
370 Shell::GetPrimaryRootWindow(), gfx::Point());
371 generator.MoveMouseTo(0, 0);
373 scoped_ptr<Window> w1(CreateTestWindow());
374 const gfx::Rect w1_bounds(0, 1, 101, 102);
375 ShelfLayoutManager* shelf = shelf_layout_manager();
376 shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
377 const gfx::Rect touches_shelf_bounds(
378 0, shelf->GetIdealBounds().y() - 10, 101, 102);
379 // Move |w1| to overlap the shelf.
380 w1->SetBounds(touches_shelf_bounds);
381 EXPECT_FALSE(GetWindowOverlapsShelf());
383 // A visible ignored window should not trigger the overlap.
384 scoped_ptr<Window> w_ignored(CreateTestWindow());
385 w_ignored->SetBounds(touches_shelf_bounds);
386 wm::GetWindowState(&(*w_ignored))->set_ignored_by_shelf(true);
387 w_ignored->Show();
388 EXPECT_FALSE(GetWindowOverlapsShelf());
390 // Make it visible, since visible shelf overlaps should be true.
391 w1->Show();
392 EXPECT_TRUE(GetWindowOverlapsShelf());
394 wm::ActivateWindow(w1.get());
395 w1->SetBounds(w1_bounds);
396 w1->Show();
397 wm::ActivateWindow(w1.get());
399 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
401 // Maximize the window.
402 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
403 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
404 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
406 // Restore.
407 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
408 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
409 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
411 // Fullscreen.
412 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
413 EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
415 // Normal.
416 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
417 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
418 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
419 EXPECT_FALSE(GetWindowOverlapsShelf());
421 // Move window so it obscures shelf.
422 w1->SetBounds(touches_shelf_bounds);
423 EXPECT_TRUE(GetWindowOverlapsShelf());
425 // Move it back.
426 w1->SetBounds(w1_bounds);
427 EXPECT_FALSE(GetWindowOverlapsShelf());
429 // Maximize again.
430 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
431 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
432 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
434 // Minimize.
435 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
436 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
438 // Since the restore from minimize will restore to the pre-minimize
439 // state (tested elsewhere), we abandon the current size and restore
440 // rect and set them to the window.
441 wm::WindowState* window_state = wm::GetWindowState(w1.get());
443 gfx::Rect restore = window_state->GetRestoreBoundsInScreen();
444 EXPECT_EQ("0,0 800x597", w1->bounds().ToString());
445 EXPECT_EQ("0,1 101x102", restore.ToString());
446 window_state->ClearRestoreBounds();
447 w1->SetBounds(restore);
449 // Restore.
450 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
451 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
452 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
454 // Create another window, maximized.
455 scoped_ptr<Window> w2(CreateTestWindow());
456 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
457 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
458 w2->Show();
459 wm::ActivateWindow(w2.get());
460 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
461 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
462 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
463 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(
464 w2->parent()).ToString(),
465 w2->bounds().ToString());
467 // Switch to w1.
468 wm::ActivateWindow(w1.get());
469 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
470 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
471 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(
472 w2->parent()).ToString(),
473 w2->bounds().ToString());
475 // Switch to w2.
476 wm::ActivateWindow(w2.get());
477 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
478 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
479 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
480 EXPECT_EQ(ScreenUtil::GetMaximizedWindowBoundsInParent(w2.get()).ToString(),
481 w2->bounds().ToString());
483 // Turn off auto-hide, switch back to w2 (maximized) and verify overlap.
484 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
485 wm::ActivateWindow(w2.get());
486 EXPECT_FALSE(GetWindowOverlapsShelf());
488 // Move w1 to overlap shelf, it shouldn't change window overlaps shelf since
489 // the window isn't in the visible workspace.
490 w1->SetBounds(touches_shelf_bounds);
491 EXPECT_FALSE(GetWindowOverlapsShelf());
493 // Activate w1. Although w1 is visible, the overlap state is still false since
494 // w2 is maximized.
495 wm::ActivateWindow(w1.get());
496 EXPECT_FALSE(GetWindowOverlapsShelf());
498 // Restore w2.
499 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
500 EXPECT_TRUE(GetWindowOverlapsShelf());
503 // Verifies going from maximized to minimized sets the right state for painting
504 // the background of the launcher.
505 TEST_F(WorkspaceControllerTest, MinimizeResetsVisibility) {
506 scoped_ptr<Window> w1(CreateTestWindow());
507 w1->Show();
508 wm::ActivateWindow(w1.get());
509 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
510 EXPECT_EQ(SHELF_BACKGROUND_MAXIMIZED, shelf_widget()->GetBackgroundType());
512 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
513 EXPECT_EQ(SHELF_VISIBLE,
514 shelf_layout_manager()->visibility_state());
515 EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, shelf_widget()->GetBackgroundType());
518 // Verifies window visibility during various workspace changes.
519 TEST_F(WorkspaceControllerTest, VisibilityTests) {
520 scoped_ptr<Window> w1(CreateTestWindow());
521 w1->Show();
522 EXPECT_TRUE(w1->IsVisible());
523 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
525 // Create another window, activate it and make it fullscreen.
526 scoped_ptr<Window> w2(CreateTestWindow());
527 w2->Show();
528 wm::ActivateWindow(w2.get());
529 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
530 EXPECT_TRUE(w2->IsVisible());
531 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
532 EXPECT_TRUE(w1->IsVisible());
534 // Switch to w1. |w1| should be visible on top of |w2|.
535 wm::ActivateWindow(w1.get());
536 EXPECT_TRUE(w1->IsVisible());
537 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
538 EXPECT_TRUE(w2->IsVisible());
540 // Switch back to |w2|.
541 wm::ActivateWindow(w2.get());
542 EXPECT_TRUE(w2->IsVisible());
543 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
544 EXPECT_TRUE(w1->IsVisible());
546 // Restore |w2|, both windows should be visible.
547 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
548 EXPECT_TRUE(w1->IsVisible());
549 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
550 EXPECT_TRUE(w2->IsVisible());
551 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
553 // Make |w2| fullscreen again, then close it.
554 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
555 w2->Hide();
556 EXPECT_FALSE(w2->IsVisible());
557 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
558 EXPECT_TRUE(w1->IsVisible());
560 // Create |w2| and maximize it.
561 w2.reset(CreateTestWindow());
562 w2->Show();
563 wm::ActivateWindow(w2.get());
564 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
565 EXPECT_TRUE(w2->IsVisible());
566 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
567 EXPECT_TRUE(w1->IsVisible());
569 // Close |w2|.
570 w2.reset();
571 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
572 EXPECT_TRUE(w1->IsVisible());
575 // Verifies windows that are offscreen don't move when switching workspaces.
576 TEST_F(WorkspaceControllerTest, DontMoveOnSwitch) {
577 aura::test::EventGenerator generator(
578 Shell::GetPrimaryRootWindow(), gfx::Point());
579 generator.MoveMouseTo(0, 0);
581 scoped_ptr<Window> w1(CreateTestWindow());
582 ShelfLayoutManager* shelf = shelf_layout_manager();
583 const gfx::Rect touches_shelf_bounds(
584 0, shelf->GetIdealBounds().y() - 10, 101, 102);
585 // Move |w1| to overlap the shelf.
586 w1->SetBounds(touches_shelf_bounds);
587 w1->Show();
588 wm::ActivateWindow(w1.get());
590 // Create another window and maximize it.
591 scoped_ptr<Window> w2(CreateTestWindow());
592 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
593 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
594 w2->Show();
595 wm::ActivateWindow(w2.get());
597 // Switch to w1.
598 wm::ActivateWindow(w1.get());
599 EXPECT_EQ(touches_shelf_bounds.ToString(), w1->bounds().ToString());
602 // Verifies that windows that are completely offscreen move when switching
603 // workspaces.
604 TEST_F(WorkspaceControllerTest, MoveOnSwitch) {
605 aura::test::EventGenerator generator(
606 Shell::GetPrimaryRootWindow(), gfx::Point());
607 generator.MoveMouseTo(0, 0);
609 scoped_ptr<Window> w1(CreateTestWindow());
610 ShelfLayoutManager* shelf = shelf_layout_manager();
611 const gfx::Rect w1_bounds(0, shelf->GetIdealBounds().y(), 100, 200);
612 // Move |w1| so that the top edge is the same as the top edge of the shelf.
613 w1->SetBounds(w1_bounds);
614 w1->Show();
615 wm::ActivateWindow(w1.get());
616 EXPECT_EQ(w1_bounds.ToString(), w1->bounds().ToString());
618 // Create another window and maximize it.
619 scoped_ptr<Window> w2(CreateTestWindow());
620 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
621 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
622 w2->Show();
623 wm::ActivateWindow(w2.get());
625 // Increase the size of the WorkAreaInsets. This would make |w1| fall
626 // completely out of the display work area.
627 gfx::Insets insets =
628 Shell::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets();
629 insets.Set(0, 0, insets.bottom() + 30, 0);
630 Shell::GetInstance()->SetDisplayWorkAreaInsets(w1.get(), insets);
632 // Switch to w1. The window should have moved.
633 wm::ActivateWindow(w1.get());
634 EXPECT_NE(w1_bounds.ToString(), w1->bounds().ToString());
637 namespace {
639 // WindowDelegate used by DontCrashOnChangeAndActivate.
640 class DontCrashOnChangeAndActivateDelegate
641 : public aura::test::TestWindowDelegate {
642 public:
643 DontCrashOnChangeAndActivateDelegate() : window_(NULL) {}
645 void set_window(aura::Window* window) { window_ = window; }
647 // WindowDelegate overrides:
648 virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
649 const gfx::Rect& new_bounds) OVERRIDE {
650 if (window_) {
651 wm::ActivateWindow(window_);
652 window_ = NULL;
656 private:
657 aura::Window* window_;
659 DISALLOW_COPY_AND_ASSIGN(DontCrashOnChangeAndActivateDelegate);
662 } // namespace
664 // Exercises possible crash in W2. Here's the sequence:
665 // . minimize a maximized window.
666 // . remove the window (which happens when switching displays).
667 // . add the window back.
668 // . show the window and during the bounds change activate it.
669 TEST_F(WorkspaceControllerTest, DontCrashOnChangeAndActivate) {
670 // Force the shelf
671 ShelfLayoutManager* shelf = shelf_layout_manager();
672 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
674 DontCrashOnChangeAndActivateDelegate delegate;
675 scoped_ptr<Window> w1(CreateTestWindowInShellWithDelegate(
676 &delegate, 1000, gfx::Rect(10, 11, 250, 251)));
678 w1->Show();
679 wm::WindowState* w1_state = wm::GetWindowState(w1.get());
680 w1_state->Activate();
681 w1_state->Maximize();
682 w1_state->Minimize();
684 w1->parent()->RemoveChild(w1.get());
686 // Do this so that when we Show() the window a resize occurs and we make the
687 // window active.
688 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
690 ParentWindowInPrimaryRootWindow(w1.get());
691 delegate.set_window(w1.get());
692 w1->Show();
695 // Verifies a window with a transient parent not managed by workspace works.
696 TEST_F(WorkspaceControllerTest, TransientParent) {
697 // Normal window with no transient parent.
698 scoped_ptr<Window> w2(CreateTestWindow());
699 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
700 w2->Show();
701 wm::ActivateWindow(w2.get());
703 // Window with a transient parent. We set the transient parent to the root,
704 // which would never happen but is enough to exercise the bug.
705 scoped_ptr<Window> w1(CreateTestWindowUnparented());
706 views::corewm::AddTransientChild(
707 Shell::GetInstance()->GetPrimaryRootWindow(), w1.get());
708 w1->SetBounds(gfx::Rect(10, 11, 250, 251));
709 ParentWindowInPrimaryRootWindow(w1.get());
710 w1->Show();
711 wm::ActivateWindow(w1.get());
713 // The window with the transient parent should get added to the same parent as
714 // the normal window.
715 EXPECT_EQ(w2->parent(), w1->parent());
718 // Test the placement of newly created windows.
719 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnCreate) {
720 if (!SupportsHostWindowResize())
721 return;
722 UpdateDisplay("1600x1200");
723 // Creating a popup handler here to make sure it does not interfere with the
724 // existing windows.
725 gfx::Rect source_browser_bounds(16, 32, 640, 320);
726 scoped_ptr<aura::Window> browser_window(CreateBrowserLikeWindow(
727 source_browser_bounds));
729 // Creating a popup to make sure it does not interfere with the positioning.
730 scoped_ptr<aura::Window> browser_popup(CreatePopupLikeWindow(
731 gfx::Rect(16, 32, 128, 256)));
733 browser_window->Show();
734 browser_popup->Show();
736 { // With a shown window it's size should get returned.
737 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
738 source_browser_bounds));
739 // The position should be right flush.
740 EXPECT_EQ("960,32 640x320", new_browser_window->bounds().ToString());
743 { // With the window shown - but more on the right side then on the left
744 // side (and partially out of the screen), it should default to the other
745 // side and inside the screen.
746 gfx::Rect source_browser_bounds(gfx::Rect(1000, 600, 640, 320));
747 browser_window->SetBounds(source_browser_bounds);
749 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
750 source_browser_bounds));
751 // The position should be left & bottom flush.
752 EXPECT_EQ("0,600 640x320", new_browser_window->bounds().ToString());
754 // If the other window was already beyond the point to get right flush
755 // it will remain where it is.
756 EXPECT_EQ("1000,600 640x320", browser_window->bounds().ToString());
759 { // Make sure that popups do not get changed.
760 scoped_ptr<aura::Window> new_popup_window(CreatePopupLikeWindow(
761 gfx::Rect(50, 100, 300, 150)));
762 EXPECT_EQ("50,100 300x150", new_popup_window->bounds().ToString());
765 browser_window->Hide();
766 { // If a window is there but not shown the default should be centered.
767 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
768 gfx::Rect(50, 100, 300, 150)));
769 EXPECT_EQ("650,100 300x150", new_browser_window->bounds().ToString());
773 // Test the basic auto placement of one and or two windows in a "simulated
774 // session" of sequential window operations.
775 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnShowHide) {
776 // Test 1: In case there is no manageable window, no window should shift.
778 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
779 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
780 gfx::Rect desktop_area = window1->parent()->bounds();
782 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
783 // Trigger the auto window placement function by making it visible.
784 // Note that the bounds are getting changed while it is invisible.
785 window2->Hide();
786 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
787 window2->Show();
789 // Check the initial position of the windows is unchanged.
790 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
791 EXPECT_EQ("32,48 256x512", window2->bounds().ToString());
793 // Remove the second window and make sure that the first window
794 // does NOT get centered.
795 window2.reset();
796 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
798 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
799 // Test 2: Set up two managed windows and check their auto positioning.
800 window1_state->set_window_position_managed(true);
802 scoped_ptr<aura::Window> window3(CreateTestWindowInShellWithId(2));
803 wm::GetWindowState(window3.get())->set_window_position_managed(true);
804 // To avoid any auto window manager changes due to SetBounds, the window
805 // gets first hidden and then shown again.
806 window3->Hide();
807 window3->SetBounds(gfx::Rect(32, 48, 256, 512));
808 window3->Show();
809 // |window1| should be flush left and |window3| flush right.
810 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
811 EXPECT_EQ(base::IntToString(
812 desktop_area.width() - window3->bounds().width()) +
813 ",48 256x512", window3->bounds().ToString());
815 // After removing |window3|, |window1| should be centered again.
816 window3.reset();
817 EXPECT_EQ(
818 base::IntToString(
819 (desktop_area.width() - window1->bounds().width()) / 2) +
820 ",32 640x320", window1->bounds().ToString());
822 // Test 3: Set up a manageable and a non manageable window and check
823 // positioning.
824 scoped_ptr<aura::Window> window4(CreateTestWindowInShellWithId(3));
825 // To avoid any auto window manager changes due to SetBounds, the window
826 // gets first hidden and then shown again.
827 window1->Hide();
828 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
829 window4->SetBounds(gfx::Rect(32, 48, 256, 512));
830 window1->Show();
831 // |window1| should be centered and |window4| untouched.
832 EXPECT_EQ(
833 base::IntToString(
834 (desktop_area.width() - window1->bounds().width()) / 2) +
835 ",32 640x320", window1->bounds().ToString());
836 EXPECT_EQ("32,48 256x512", window4->bounds().ToString());
838 // Test4: A single manageable window should get centered.
839 window4.reset();
840 window1_state->set_bounds_changed_by_user(false);
841 // Trigger the auto window placement function by showing (and hiding) it.
842 window1->Hide();
843 window1->Show();
844 // |window1| should be centered.
845 EXPECT_EQ(
846 base::IntToString(
847 (desktop_area.width() - window1->bounds().width()) / 2) +
848 ",32 640x320", window1->bounds().ToString());
851 // Test the proper usage of user window movement interaction.
852 TEST_F(WorkspaceControllerTest, TestUserMovedWindowRepositioning) {
853 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
854 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
855 gfx::Rect desktop_area = window1->parent()->bounds();
856 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
857 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
858 window1->Hide();
859 window2->Hide();
860 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
861 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
863 window1_state->set_window_position_managed(true);
864 window2_state->set_window_position_managed(true);
865 EXPECT_FALSE(window1_state->bounds_changed_by_user());
866 EXPECT_FALSE(window2_state->bounds_changed_by_user());
868 // Check that the current location gets preserved if the user has
869 // positioned it previously.
870 window1_state->set_bounds_changed_by_user(true);
871 window1->Show();
872 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
873 // Flag should be still set.
874 EXPECT_TRUE(window1_state->bounds_changed_by_user());
875 EXPECT_FALSE(window2_state->bounds_changed_by_user());
877 // Turn on the second window and make sure that both windows are now
878 // positionable again (user movement cleared).
879 window2->Show();
881 // |window1| should be flush left and |window2| flush right.
882 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
883 EXPECT_EQ(
884 base::IntToString(desktop_area.width() - window2->bounds().width()) +
885 ",48 256x512", window2->bounds().ToString());
886 // FLag should now be reset.
887 EXPECT_FALSE(window1_state->bounds_changed_by_user());
888 EXPECT_FALSE(window2_state->bounds_changed_by_user());
890 // Going back to one shown window should keep the state.
891 window1_state->set_bounds_changed_by_user(true);
892 window2->Hide();
893 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
894 EXPECT_TRUE(window1_state->bounds_changed_by_user());
897 // Test if the single window will be restored at original position.
898 TEST_F(WorkspaceControllerTest, TestSingleWindowsRestoredBounds) {
899 scoped_ptr<aura::Window> window1(
900 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
901 scoped_ptr<aura::Window> window2(
902 CreateTestWindowInShellWithBounds(gfx::Rect(110, 110, 100, 100)));
903 scoped_ptr<aura::Window> window3(
904 CreateTestWindowInShellWithBounds(gfx::Rect(120, 120, 100, 100)));
905 window1->Hide();
906 window2->Hide();
907 window3->Hide();
908 wm::GetWindowState(window1.get())->set_window_position_managed(true);
909 wm::GetWindowState(window2.get())->set_window_position_managed(true);
910 wm::GetWindowState(window3.get())->set_window_position_managed(true);
912 window1->Show();
913 wm::ActivateWindow(window1.get());
914 window2->Show();
915 wm::ActivateWindow(window2.get());
916 window3->Show();
917 wm::ActivateWindow(window3.get());
918 EXPECT_EQ(0, window1->bounds().x());
919 EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
920 window2->bounds().right());
921 EXPECT_EQ(0, window3->bounds().x());
923 window1->Hide();
924 EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
925 window2->bounds().right());
926 EXPECT_EQ(0, window3->bounds().x());
928 // Being a single window will retore the original location.
929 window3->Hide();
930 wm::ActivateWindow(window2.get());
931 EXPECT_EQ("110,110 100x100", window2->bounds().ToString());
933 // Showing the 3rd will push the 2nd window left.
934 window3->Show();
935 wm::ActivateWindow(window3.get());
936 EXPECT_EQ(0, window2->bounds().x());
937 EXPECT_EQ(window3->GetRootWindow()->bounds().right(),
938 window3->bounds().right());
940 // Being a single window will retore the original location.
941 window2->Hide();
942 EXPECT_EQ("120,120 100x100", window3->bounds().ToString());
945 // Test that user placed windows go back to their user placement after the user
946 // closes all other windows.
947 TEST_F(WorkspaceControllerTest, TestUserHandledWindowRestore) {
948 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
949 gfx::Rect user_pos = gfx::Rect(16, 42, 640, 320);
950 window1->SetBounds(user_pos);
951 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
953 window1_state->SetPreAutoManageWindowBounds(user_pos);
954 gfx::Rect desktop_area = window1->parent()->bounds();
956 // Create a second window to let the auto manager kick in.
957 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
958 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
959 window1->Hide();
960 window2->Hide();
961 wm::GetWindowState(window1.get())->set_window_position_managed(true);
962 wm::GetWindowState(window2.get())->set_window_position_managed(true);
963 window1->Show();
964 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
965 window2->Show();
967 // |window1| should be flush left and |window2| flush right.
968 EXPECT_EQ("0," + base::IntToString(user_pos.y()) +
969 " 640x320", window1->bounds().ToString());
970 EXPECT_EQ(
971 base::IntToString(desktop_area.width() - window2->bounds().width()) +
972 ",48 256x512", window2->bounds().ToString());
973 window2->Hide();
975 // After the other window get hidden the window has to move back to the
976 // previous position and the bounds should still be set and unchanged.
977 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
978 ASSERT_TRUE(window1_state->pre_auto_manage_window_bounds());
979 EXPECT_EQ(user_pos.ToString(),
980 window1_state->pre_auto_manage_window_bounds()->ToString());
983 // Test that a window from normal to minimize will repos the remaining.
984 TEST_F(WorkspaceControllerTest, ToMinimizeRepositionsRemaining) {
985 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
986 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
987 window1_state->set_window_position_managed(true);
988 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
989 gfx::Rect desktop_area = window1->parent()->bounds();
991 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
992 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
993 window2_state->set_window_position_managed(true);
994 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
996 window1_state->Minimize();
998 // |window2| should be centered now.
999 EXPECT_TRUE(window2->IsVisible());
1000 EXPECT_TRUE(window2_state->IsNormalShowType());
1001 EXPECT_EQ(base::IntToString(
1002 (desktop_area.width() - window2->bounds().width()) / 2) +
1003 ",48 256x512", window2->bounds().ToString());
1005 window1_state->Restore();
1006 // |window1| should be flush right and |window3| flush left.
1007 EXPECT_EQ(base::IntToString(
1008 desktop_area.width() - window1->bounds().width()) +
1009 ",32 640x320", window1->bounds().ToString());
1010 EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
1013 // Test that minimizing an initially maximized window will repos the remaining.
1014 TEST_F(WorkspaceControllerTest, MaxToMinRepositionsRemaining) {
1015 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1016 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1017 window1_state->set_window_position_managed(true);
1018 gfx::Rect desktop_area = window1->parent()->bounds();
1020 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1021 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
1022 window2_state->set_window_position_managed(true);
1023 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1025 window1_state->Maximize();
1026 window1_state->Minimize();
1028 // |window2| should be centered now.
1029 EXPECT_TRUE(window2->IsVisible());
1030 EXPECT_TRUE(window2_state->IsNormalShowType());
1031 EXPECT_EQ(base::IntToString(
1032 (desktop_area.width() - window2->bounds().width()) / 2) +
1033 ",48 256x512", window2->bounds().ToString());
1036 // Test that nomral, maximize, minimizing will repos the remaining.
1037 TEST_F(WorkspaceControllerTest, NormToMaxToMinRepositionsRemaining) {
1038 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1039 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1040 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1041 window1_state->set_window_position_managed(true);
1042 gfx::Rect desktop_area = window1->parent()->bounds();
1044 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1045 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
1046 window2_state->set_window_position_managed(true);
1047 window2->SetBounds(gfx::Rect(32, 40, 256, 512));
1049 // Trigger the auto window placement function by showing (and hiding) it.
1050 window1->Hide();
1051 window1->Show();
1053 // |window1| should be flush right and |window3| flush left.
1054 EXPECT_EQ(base::IntToString(
1055 desktop_area.width() - window1->bounds().width()) +
1056 ",32 640x320", window1->bounds().ToString());
1057 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1059 window1_state->Maximize();
1060 window1_state->Minimize();
1062 // |window2| should be centered now.
1063 EXPECT_TRUE(window2->IsVisible());
1064 EXPECT_TRUE(window2_state->IsNormalShowType());
1065 EXPECT_EQ(base::IntToString(
1066 (desktop_area.width() - window2->bounds().width()) / 2) +
1067 ",40 256x512", window2->bounds().ToString());
1070 // Test that nomral, maximize, normal will repos the remaining.
1071 TEST_F(WorkspaceControllerTest, NormToMaxToNormRepositionsRemaining) {
1072 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1073 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1074 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1075 window1_state->set_window_position_managed(true);
1076 gfx::Rect desktop_area = window1->parent()->bounds();
1078 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1079 wm::GetWindowState(window2.get())->set_window_position_managed(true);
1080 window2->SetBounds(gfx::Rect(32, 40, 256, 512));
1082 // Trigger the auto window placement function by showing (and hiding) it.
1083 window1->Hide();
1084 window1->Show();
1086 // |window1| should be flush right and |window3| flush left.
1087 EXPECT_EQ(base::IntToString(
1088 desktop_area.width() - window1->bounds().width()) +
1089 ",32 640x320", window1->bounds().ToString());
1090 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1092 window1_state->Maximize();
1093 window1_state->Restore();
1095 // |window1| should be flush right and |window2| flush left.
1096 EXPECT_EQ(base::IntToString(
1097 desktop_area.width() - window1->bounds().width()) +
1098 ",32 640x320", window1->bounds().ToString());
1099 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1102 // Test that animations are triggered.
1103 TEST_F(WorkspaceControllerTest, AnimatedNormToMaxToNormRepositionsRemaining) {
1104 ui::ScopedAnimationDurationScaleMode normal_duration_mode(
1105 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
1106 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1107 window1->Hide();
1108 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1109 gfx::Rect desktop_area = window1->parent()->bounds();
1110 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1111 window2->Hide();
1112 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1114 wm::GetWindowState(window1.get())->set_window_position_managed(true);
1115 wm::GetWindowState(window2.get())->set_window_position_managed(true);
1116 // Make sure nothing is animating.
1117 window1->layer()->GetAnimator()->StopAnimating();
1118 window2->layer()->GetAnimator()->StopAnimating();
1119 window2->Show();
1121 // The second window should now animate.
1122 EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating());
1123 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1124 window2->layer()->GetAnimator()->StopAnimating();
1126 window1->Show();
1127 EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
1128 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1130 window1->layer()->GetAnimator()->StopAnimating();
1131 window2->layer()->GetAnimator()->StopAnimating();
1132 // |window1| should be flush right and |window2| flush left.
1133 EXPECT_EQ(base::IntToString(
1134 desktop_area.width() - window1->bounds().width()) +
1135 ",32 640x320", window1->bounds().ToString());
1136 EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
1139 // This tests simulates a browser and an app and verifies the ordering of the
1140 // windows and layers doesn't get out of sync as various operations occur. Its
1141 // really testing code in FocusController, but easier to simulate here. Just as
1142 // with a real browser the browser here has a transient child window
1143 // (corresponds to the status bubble).
1144 TEST_F(WorkspaceControllerTest, VerifyLayerOrdering) {
1145 scoped_ptr<Window> browser(aura::test::CreateTestWindowWithDelegate(
1146 NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL));
1147 browser->SetName("browser");
1148 ParentWindowInPrimaryRootWindow(browser.get());
1149 browser->Show();
1150 wm::ActivateWindow(browser.get());
1152 // |status_bubble| is made a transient child of |browser| and as a result
1153 // owned by |browser|.
1154 aura::test::TestWindowDelegate* status_bubble_delegate =
1155 aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate();
1156 status_bubble_delegate->set_can_focus(false);
1157 Window* status_bubble =
1158 aura::test::CreateTestWindowWithDelegate(status_bubble_delegate,
1159 ui::wm::WINDOW_TYPE_POPUP,
1160 gfx::Rect(5, 6, 7, 8),
1161 NULL);
1162 views::corewm::AddTransientChild(browser.get(), status_bubble);
1163 ParentWindowInPrimaryRootWindow(status_bubble);
1164 status_bubble->SetName("status_bubble");
1166 scoped_ptr<Window> app(aura::test::CreateTestWindowWithDelegate(
1167 NULL, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 6, 7, 8), NULL));
1168 app->SetName("app");
1169 ParentWindowInPrimaryRootWindow(app.get());
1171 aura::Window* parent = browser->parent();
1173 app->Show();
1174 wm::ActivateWindow(app.get());
1175 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1177 // Minimize the app, focus should go the browser.
1178 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
1179 EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
1180 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1182 // Minimize the browser (neither windows are focused).
1183 browser->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
1184 EXPECT_FALSE(wm::IsActiveWindow(browser.get()));
1185 EXPECT_FALSE(wm::IsActiveWindow(app.get()));
1186 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1188 // Show the browser (which should restore it).
1189 browser->Show();
1190 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1192 // Activate the browser.
1193 ash::wm::ActivateWindow(browser.get());
1194 EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
1195 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1197 // Restore the app. This differs from above code for |browser| as internally
1198 // the app code does this. Restoring this way or using Show() should not make
1199 // a difference.
1200 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1201 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1203 // Activate the app.
1204 ash::wm::ActivateWindow(app.get());
1205 EXPECT_TRUE(wm::IsActiveWindow(app.get()));
1206 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1209 namespace {
1211 // Used by DragMaximizedNonTrackedWindow to track how many times the window
1212 // hierarchy changes affecting the specified window.
1213 class DragMaximizedNonTrackedWindowObserver
1214 : public aura::WindowObserver {
1215 public:
1216 DragMaximizedNonTrackedWindowObserver(aura::Window* window)
1217 : change_count_(0),
1218 window_(window) {
1221 // Number of times OnWindowHierarchyChanged() has been received.
1222 void clear_change_count() { change_count_ = 0; }
1223 int change_count() const {
1224 return change_count_;
1227 // aura::WindowObserver overrides:
1228 // Counts number of times a window is reparented. Ignores reparenting into and
1229 // from a docked container which is expected when a tab is dragged.
1230 virtual void OnWindowHierarchyChanged(
1231 const HierarchyChangeParams& params) OVERRIDE {
1232 if (params.target != window_ ||
1233 (params.old_parent->id() == kShellWindowId_DefaultContainer &&
1234 params.new_parent->id() == kShellWindowId_DockedContainer) ||
1235 (params.old_parent->id() == kShellWindowId_DockedContainer &&
1236 params.new_parent->id() == kShellWindowId_DefaultContainer)) {
1237 return;
1239 change_count_++;
1242 private:
1243 int change_count_;
1244 aura::Window* window_;
1246 DISALLOW_COPY_AND_ASSIGN(DragMaximizedNonTrackedWindowObserver);
1249 } // namespace
1251 // Verifies that a new maximized window becomes visible after its activation
1252 // is requested, even though it does not become activated because a system
1253 // modal window is active.
1254 TEST_F(WorkspaceControllerTest, SwitchFromModal) {
1255 scoped_ptr<Window> modal_window(CreateTestWindowUnparented());
1256 modal_window->SetBounds(gfx::Rect(10, 11, 21, 22));
1257 modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
1258 ParentWindowInPrimaryRootWindow(modal_window.get());
1259 modal_window->Show();
1260 wm::ActivateWindow(modal_window.get());
1262 scoped_ptr<Window> maximized_window(CreateTestWindow());
1263 maximized_window->SetProperty(
1264 aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
1265 maximized_window->Show();
1266 wm::ActivateWindow(maximized_window.get());
1267 EXPECT_TRUE(maximized_window->IsVisible());
1270 namespace {
1272 // Subclass of WorkspaceControllerTest that runs tests with docked windows
1273 // enabled and disabled.
1274 class WorkspaceControllerTestDragging
1275 : public WorkspaceControllerTest,
1276 public testing::WithParamInterface<bool> {
1277 public:
1278 WorkspaceControllerTestDragging() {}
1279 virtual ~WorkspaceControllerTestDragging() {}
1281 // testing::Test:
1282 virtual void SetUp() OVERRIDE {
1283 WorkspaceControllerTest::SetUp();
1284 if (!docked_windows_enabled()) {
1285 CommandLine::ForCurrentProcess()->AppendSwitch(
1286 ash::switches::kAshDisableDockedWindows);
1290 bool docked_windows_enabled() const { return GetParam(); }
1292 private:
1293 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTestDragging);
1296 } // namespace
1298 // Verifies that when dragging a window over the shelf overlap is detected
1299 // during and after the drag.
1300 TEST_P(WorkspaceControllerTestDragging, DragWindowOverlapShelf) {
1301 aura::test::TestWindowDelegate delegate;
1302 delegate.set_window_component(HTCAPTION);
1303 scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate(
1304 &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL));
1305 ParentWindowInPrimaryRootWindow(w1.get());
1307 ShelfLayoutManager* shelf = shelf_layout_manager();
1308 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
1310 // Drag near the shelf.
1311 aura::test::EventGenerator generator(
1312 Shell::GetPrimaryRootWindow(), gfx::Point());
1313 generator.MoveMouseTo(10, 10);
1314 generator.PressLeftButton();
1315 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 70);
1317 // Shelf should not be in overlapped state.
1318 EXPECT_FALSE(GetWindowOverlapsShelf());
1320 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 20);
1322 // Shelf should detect overlap. Overlap state stays after mouse is released.
1323 EXPECT_TRUE(GetWindowOverlapsShelf());
1324 generator.ReleaseLeftButton();
1325 EXPECT_TRUE(GetWindowOverlapsShelf());
1328 // Verifies that when dragging a window autohidden shelf stays hidden during
1329 // and after the drag.
1330 TEST_P(WorkspaceControllerTestDragging, DragWindowKeepsShelfAutohidden) {
1331 aura::test::TestWindowDelegate delegate;
1332 delegate.set_window_component(HTCAPTION);
1333 scoped_ptr<Window> w1(aura::test::CreateTestWindowWithDelegate(
1334 &delegate, ui::wm::WINDOW_TYPE_NORMAL, gfx::Rect(5, 5, 100, 50), NULL));
1335 ParentWindowInPrimaryRootWindow(w1.get());
1337 ShelfLayoutManager* shelf = shelf_layout_manager();
1338 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
1339 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
1341 // Drag very little.
1342 aura::test::EventGenerator generator(
1343 Shell::GetPrimaryRootWindow(), gfx::Point());
1344 generator.MoveMouseTo(10, 10);
1345 generator.PressLeftButton();
1346 generator.MoveMouseTo(12, 12);
1348 // Shelf should be hidden during and after the drag.
1349 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
1350 generator.ReleaseLeftButton();
1351 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
1354 INSTANTIATE_TEST_CASE_P(DockedOrNot, WorkspaceControllerTestDragging,
1355 ::testing::Bool());
1357 // Verifies that events are targeted properly just outside the window edges.
1358 TEST_F(WorkspaceControllerTest, WindowEdgeHitTest) {
1359 aura::test::TestWindowDelegate d_first, d_second;
1360 scoped_ptr<Window> first(aura::test::CreateTestWindowWithDelegate(&d_first,
1361 123, gfx::Rect(20, 10, 100, 50), NULL));
1362 ParentWindowInPrimaryRootWindow(first.get());
1363 first->Show();
1365 scoped_ptr<Window> second(aura::test::CreateTestWindowWithDelegate(&d_second,
1366 234, gfx::Rect(30, 40, 40, 10), NULL));
1367 ParentWindowInPrimaryRootWindow(second.get());
1368 second->Show();
1370 ui::EventTarget* root = first->GetRootWindow();
1371 ui::EventTargeter* targeter = root->GetEventTargeter();
1373 // The windows overlap, and |second| is on top of |first|. Events targeted
1374 // slightly outside the edges of the |second| window should still be targeted
1375 // to |second| to allow resizing the windows easily.
1377 const int kNumPoints = 4;
1378 struct {
1379 const char* direction;
1380 gfx::Point location;
1381 } points[kNumPoints] = {
1382 { "left", gfx::Point(28, 45) }, // outside the left edge.
1383 { "top", gfx::Point(50, 38) }, // outside the top edge.
1384 { "right", gfx::Point(72, 45) }, // outside the right edge.
1385 { "bottom", gfx::Point(50, 52) }, // outside the bottom edge.
1387 // Do two iterations, first without any transform on |second|, and the second
1388 // time after applying some transform on |second| so that it doesn't get
1389 // targeted.
1390 for (int times = 0; times < 2; ++times) {
1391 SCOPED_TRACE(times == 0 ? "Without transform" : "With transform");
1392 aura::Window* expected_target = times == 0 ? second.get() : first.get();
1393 for (int i = 0; i < kNumPoints; ++i) {
1394 SCOPED_TRACE(points[i].direction);
1395 const gfx::Point& location = points[i].location;
1396 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
1397 ui::EF_NONE);
1398 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
1399 EXPECT_EQ(expected_target, target);
1401 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
1402 ui::EventTimeForNow());
1403 target = targeter->FindTargetForEvent(root, &touch);
1404 EXPECT_EQ(expected_target, target);
1406 // Apply a transform on |second|. After the transform is applied, the window
1407 // should no longer be targeted.
1408 gfx::Transform transform;
1409 transform.Translate(70, 40);
1410 second->SetTransform(transform);
1414 // Verifies events targeting just outside the window edges for panels.
1415 TEST_F(WorkspaceControllerTest, WindowEdgeHitTestPanel) {
1416 aura::test::TestWindowDelegate delegate;
1417 scoped_ptr<Window> window(CreateTestPanel(&delegate,
1418 gfx::Rect(20, 10, 100, 50)));
1419 ui::EventTarget* root = window->GetRootWindow();
1420 ui::EventTargeter* targeter = root->GetEventTargeter();
1421 const gfx::Rect bounds = window->bounds();
1422 const int kNumPoints = 5;
1423 struct {
1424 const char* direction;
1425 gfx::Point location;
1426 bool is_target_hit;
1427 } points[kNumPoints] = {
1428 { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true },
1429 { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true },
1430 { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true },
1431 { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), true },
1432 { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false },
1434 for (int i = 0; i < kNumPoints; ++i) {
1435 SCOPED_TRACE(points[i].direction);
1436 const gfx::Point& location = points[i].location;
1437 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
1438 ui::EF_NONE);
1439 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
1440 if (points[i].is_target_hit)
1441 EXPECT_EQ(window.get(), target);
1442 else
1443 EXPECT_NE(window.get(), target);
1445 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
1446 ui::EventTimeForNow());
1447 target = targeter->FindTargetForEvent(root, &touch);
1448 if (points[i].is_target_hit)
1449 EXPECT_EQ(window.get(), target);
1450 else
1451 EXPECT_NE(window.get(), target);
1455 // Verifies events targeting just outside the window edges for docked windows.
1456 TEST_F(WorkspaceControllerTest, WindowEdgeHitTestDocked) {
1457 if (!switches::UseDockedWindows())
1458 return;
1459 aura::test::TestWindowDelegate delegate;
1460 // Make window smaller than the minimum docked area so that the window edges
1461 // are exposed.
1462 delegate.set_maximum_size(gfx::Size(180, 200));
1463 scoped_ptr<Window> window(aura::test::CreateTestWindowWithDelegate(&delegate,
1464 123, gfx::Rect(20, 10, 100, 50), NULL));
1465 ParentWindowInPrimaryRootWindow(window.get());
1466 aura::Window* docked_container = Shell::GetContainer(
1467 window->GetRootWindow(), internal::kShellWindowId_DockedContainer);
1468 docked_container->AddChild(window.get());
1469 window->Show();
1470 ui::EventTarget* root = window->GetRootWindow();
1471 ui::EventTargeter* targeter = root->GetEventTargeter();
1472 const gfx::Rect bounds = window->bounds();
1473 const int kNumPoints = 5;
1474 struct {
1475 const char* direction;
1476 gfx::Point location;
1477 bool is_target_hit;
1478 } points[kNumPoints] = {
1479 { "left", gfx::Point(bounds.x() - 2, bounds.y() + 10), true },
1480 { "top", gfx::Point(bounds.x() + 10, bounds.y() - 2), true },
1481 { "right", gfx::Point(bounds.right() + 2, bounds.y() + 10), true },
1482 { "bottom", gfx::Point(bounds.x() + 10, bounds.bottom() + 2), true },
1483 { "outside", gfx::Point(bounds.x() + 10, bounds.y() - 31), false },
1485 for (int i = 0; i < kNumPoints; ++i) {
1486 SCOPED_TRACE(points[i].direction);
1487 const gfx::Point& location = points[i].location;
1488 ui::MouseEvent mouse(ui::ET_MOUSE_MOVED, location, location, ui::EF_NONE,
1489 ui::EF_NONE);
1490 ui::EventTarget* target = targeter->FindTargetForEvent(root, &mouse);
1491 if (points[i].is_target_hit)
1492 EXPECT_EQ(window.get(), target);
1493 else
1494 EXPECT_NE(window.get(), target);
1496 ui::TouchEvent touch(ui::ET_TOUCH_PRESSED, location, 0,
1497 ui::EventTimeForNow());
1498 target = targeter->FindTargetForEvent(root, &touch);
1499 if (points[i].is_target_hit)
1500 EXPECT_EQ(window.get(), target);
1501 else
1502 EXPECT_NE(window.get(), target);
1506 } // namespace internal
1507 } // namespace ash