Windows should animate when they are about to get docked at screen edges.
[chromium-blink-merge.git] / ash / wm / workspace_controller_unittest.cc
blob7dca75a08ba7f5d88e3d33977d1897f10cd568ba
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_ash.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/wm/activation_controller.h"
20 #include "ash/wm/property_util.h"
21 #include "ash/wm/window_properties.h"
22 #include "ash/wm/window_util.h"
23 #include "base/command_line.h"
24 #include "base/strings/string_number_conversions.h"
25 #include "ui/aura/client/aura_constants.h"
26 #include "ui/aura/root_window.h"
27 #include "ui/aura/test/event_generator.h"
28 #include "ui/aura/test/test_window_delegate.h"
29 #include "ui/aura/test/test_windows.h"
30 #include "ui/aura/window.h"
31 #include "ui/base/hit_test.h"
32 #include "ui/base/ui_base_types.h"
33 #include "ui/compositor/layer.h"
34 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
35 #include "ui/gfx/screen.h"
36 #include "ui/views/corewm/window_animations.h"
37 #include "ui/views/widget/widget.h"
39 using aura::Window;
41 namespace ash {
42 namespace internal {
44 // Returns a string containing the names of all the children of |window| (in
45 // order). Each entry is separated by a space.
46 std::string GetWindowNames(const aura::Window* window) {
47 std::string result;
48 for (size_t i = 0; i < window->children().size(); ++i) {
49 if (i != 0)
50 result += " ";
51 result += window->children()[i]->name();
53 return result;
56 // Returns a string containing the names of windows corresponding to each of the
57 // child layers of |window|'s layer. Any layers that don't correspond to a child
58 // Window of |window| are ignored. The result is ordered based on the layer
59 // ordering.
60 std::string GetLayerNames(const aura::Window* window) {
61 typedef std::map<const ui::Layer*, std::string> LayerToWindowNameMap;
62 LayerToWindowNameMap window_names;
63 for (size_t i = 0; i < window->children().size(); ++i) {
64 window_names[window->children()[i]->layer()] =
65 window->children()[i]->name();
68 std::string result;
69 const std::vector<ui::Layer*>& layers(window->layer()->children());
70 for (size_t i = 0; i < layers.size(); ++i) {
71 LayerToWindowNameMap::iterator layer_i =
72 window_names.find(layers[i]);
73 if (layer_i != window_names.end()) {
74 if (!result.empty())
75 result += " ";
76 result += layer_i->second;
79 return result;
82 class WorkspaceControllerTest : public test::AshTestBase {
83 public:
84 WorkspaceControllerTest() {}
85 virtual ~WorkspaceControllerTest() {}
87 aura::Window* CreateTestWindowUnparented() {
88 aura::Window* window = new aura::Window(NULL);
89 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
90 window->SetType(aura::client::WINDOW_TYPE_NORMAL);
91 window->Init(ui::LAYER_TEXTURED);
92 return window;
95 aura::Window* CreateTestWindow() {
96 aura::Window* window = new aura::Window(NULL);
97 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
98 window->SetType(aura::client::WINDOW_TYPE_NORMAL);
99 window->Init(ui::LAYER_TEXTURED);
100 SetDefaultParentByPrimaryRootWindow(window);
101 return window;
104 aura::Window* CreateAppTestWindow(aura::Window* parent) {
105 aura::Window* window = new aura::Window(NULL);
106 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
107 window->SetType(aura::client::WINDOW_TYPE_POPUP);
108 window->Init(ui::LAYER_TEXTURED);
109 if (!parent)
110 SetDefaultParentByPrimaryRootWindow(window);
111 else
112 parent->AddChild(window);
113 return window;
116 aura::Window* GetDesktop() {
117 return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
118 kShellWindowId_DefaultContainer);
121 gfx::Rect GetFullscreenBounds(aura::Window* window) {
122 return Shell::GetScreen()->GetDisplayNearestWindow(window).bounds();
125 ShelfWidget* shelf_widget() {
126 return Shell::GetPrimaryRootWindowController()->shelf();
129 ShelfLayoutManager* shelf_layout_manager() {
130 return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
133 bool GetWindowOverlapsShelf() {
134 return shelf_layout_manager()->window_overlaps_shelf();
137 private:
138 scoped_ptr<ActivationController> activation_controller_;
140 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTest);
143 // Assertions around adding a normal window.
144 TEST_F(WorkspaceControllerTest, AddNormalWindowWhenEmpty) {
145 scoped_ptr<Window> w1(CreateTestWindow());
146 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
148 EXPECT_TRUE(GetRestoreBoundsInScreen(w1.get()) == NULL);
150 w1->Show();
152 EXPECT_TRUE(GetRestoreBoundsInScreen(w1.get()) == NULL);
154 ASSERT_TRUE(w1->layer() != NULL);
155 EXPECT_TRUE(w1->layer()->visible());
157 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
159 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
162 // Assertions around maximizing/unmaximizing.
163 TEST_F(WorkspaceControllerTest, SingleMaximizeWindow) {
164 scoped_ptr<Window> w1(CreateTestWindow());
165 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
167 w1->Show();
168 wm::ActivateWindow(w1.get());
170 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
172 ASSERT_TRUE(w1->layer() != NULL);
173 EXPECT_TRUE(w1->layer()->visible());
175 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
177 // Maximize the window.
178 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
180 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
182 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
183 EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()).width(),
184 w1->bounds().width());
185 EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()).height(),
186 w1->bounds().height());
188 // Restore the window.
189 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
191 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
192 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
195 // Assertions around two windows and toggling one to be fullscreen.
196 TEST_F(WorkspaceControllerTest, FullscreenWithNormalWindow) {
197 scoped_ptr<Window> w1(CreateTestWindow());
198 scoped_ptr<Window> w2(CreateTestWindow());
199 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
200 w1->Show();
202 ASSERT_TRUE(w1->layer() != NULL);
203 EXPECT_TRUE(w1->layer()->visible());
205 w2->SetBounds(gfx::Rect(0, 0, 50, 51));
206 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
207 w2->Show();
208 wm::ActivateWindow(w2.get());
210 // Both windows should be in the same workspace.
211 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
212 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
214 gfx::Rect work_area(
215 ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()));
216 EXPECT_EQ(work_area.width(), w2->bounds().width());
217 EXPECT_EQ(work_area.height(), w2->bounds().height());
219 // Restore w2, which should then go back to one workspace.
220 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
221 EXPECT_EQ(50, w2->bounds().width());
222 EXPECT_EQ(51, w2->bounds().height());
223 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
226 // Makes sure requests to change the bounds of a normal window go through.
227 TEST_F(WorkspaceControllerTest, ChangeBoundsOfNormalWindow) {
228 scoped_ptr<Window> w1(CreateTestWindow());
229 w1->Show();
231 // Setting the bounds should go through since the window is in the normal
232 // workspace.
233 w1->SetBounds(gfx::Rect(0, 0, 200, 500));
234 EXPECT_EQ(200, w1->bounds().width());
235 EXPECT_EQ(500, w1->bounds().height());
238 // Verifies the bounds is not altered when showing and grid is enabled.
239 TEST_F(WorkspaceControllerTest, SnapToGrid) {
240 scoped_ptr<Window> w1(CreateTestWindowUnparented());
241 w1->SetBounds(gfx::Rect(1, 6, 25, 30));
242 SetDefaultParentByPrimaryRootWindow(w1.get());
243 // We are not aligning this anymore this way. When the window gets shown
244 // the window is expected to be handled differently, but this cannot be
245 // tested with this test. So the result of this test should be that the
246 // bounds are exactly as passed in.
247 EXPECT_EQ("1,6 25x30", w1->bounds().ToString());
250 // Assertions around a fullscreen window.
251 TEST_F(WorkspaceControllerTest, SingleFullscreenWindow) {
252 scoped_ptr<Window> w1(CreateTestWindow());
253 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
254 // Make the window fullscreen.
255 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
256 w1->Show();
257 wm::ActivateWindow(w1.get());
259 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
260 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
261 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
263 // Restore the window. Use SHOW_STATE_DEFAULT as that is what we'll end up
264 // with when using views::Widget.
265 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT);
266 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
268 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
269 EXPECT_EQ(250, w1->bounds().width());
270 EXPECT_EQ(251, w1->bounds().height());
272 // Back to fullscreen.
273 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
274 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
275 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
276 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
277 ASSERT_TRUE(GetRestoreBoundsInScreen(w1.get()));
278 EXPECT_EQ("0,0 250x251", GetRestoreBoundsInScreen(w1.get())->ToString());
281 // Assertions around minimizing a single window.
282 TEST_F(WorkspaceControllerTest, MinimizeSingleWindow) {
283 scoped_ptr<Window> w1(CreateTestWindow());
285 w1->Show();
287 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
288 EXPECT_FALSE(w1->layer()->IsDrawn());
290 // Show the window.
291 w1->Show();
292 EXPECT_TRUE(wm::IsWindowNormal(w1.get()));
293 EXPECT_TRUE(w1->layer()->IsDrawn());
296 // Assertions around minimizing a fullscreen window.
297 TEST_F(WorkspaceControllerTest, MinimizeFullscreenWindow) {
298 // Two windows, w1 normal, w2 fullscreen.
299 scoped_ptr<Window> w1(CreateTestWindow());
300 scoped_ptr<Window> w2(CreateTestWindow());
301 w1->Show();
302 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
303 w2->Show();
304 wm::ActivateWindow(w2.get());
306 // Minimize w2.
307 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
308 EXPECT_TRUE(w1->layer()->IsDrawn());
309 EXPECT_FALSE(w2->layer()->IsDrawn());
311 // Show the window, which should trigger unminimizing.
312 w2->Show();
313 wm::ActivateWindow(w2.get());
315 EXPECT_TRUE(wm::IsWindowFullscreen(w2.get()));
316 EXPECT_TRUE(w1->layer()->IsDrawn());
317 EXPECT_TRUE(w2->layer()->IsDrawn());
319 // Minimize the window, which should hide the window.
320 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
321 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
322 EXPECT_FALSE(wm::IsActiveWindow(w2.get()));
323 EXPECT_FALSE(w2->layer()->IsDrawn());
324 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
326 // Make the window normal.
327 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
328 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
329 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
330 EXPECT_TRUE(w2->layer()->IsDrawn());
333 // Verifies ShelfLayoutManager's visibility/auto-hide state is correctly
334 // updated.
335 TEST_F(WorkspaceControllerTest, ShelfStateUpdated) {
336 // Since ShelfLayoutManager queries for mouse location, move the mouse so
337 // it isn't over the shelf.
338 aura::test::EventGenerator generator(
339 Shell::GetPrimaryRootWindow(), gfx::Point());
340 generator.MoveMouseTo(0, 0);
342 scoped_ptr<Window> w1(CreateTestWindow());
343 const gfx::Rect w1_bounds(0, 1, 101, 102);
344 ShelfLayoutManager* shelf = shelf_layout_manager();
345 shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
346 const gfx::Rect touches_shelf_bounds(
347 0, shelf->GetIdealBounds().y() - 10, 101, 102);
348 // Move |w1| to overlap the shelf.
349 w1->SetBounds(touches_shelf_bounds);
350 EXPECT_FALSE(GetWindowOverlapsShelf());
352 // A visible ignored window should not trigger the overlap.
353 scoped_ptr<Window> w_ignored(CreateTestWindow());
354 w_ignored->SetBounds(touches_shelf_bounds);
355 SetIgnoredByShelf(&(*w_ignored), true);
356 w_ignored->Show();
357 EXPECT_FALSE(GetWindowOverlapsShelf());
359 // Make it visible, since visible shelf overlaps should be true.
360 w1->Show();
361 EXPECT_TRUE(GetWindowOverlapsShelf());
363 wm::ActivateWindow(w1.get());
364 w1->SetBounds(w1_bounds);
365 w1->Show();
366 wm::ActivateWindow(w1.get());
368 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
370 // Maximize the window.
371 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
372 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
373 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
375 // Restore.
376 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
377 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
378 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
380 // Fullscreen.
381 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
382 EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
384 // Normal.
385 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
386 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
387 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
388 EXPECT_FALSE(GetWindowOverlapsShelf());
390 // Move window so it obscures shelf.
391 w1->SetBounds(touches_shelf_bounds);
392 EXPECT_TRUE(GetWindowOverlapsShelf());
394 // Move it back.
395 w1->SetBounds(w1_bounds);
396 EXPECT_FALSE(GetWindowOverlapsShelf());
398 // Maximize again.
399 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
400 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
401 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
403 // Minimize.
404 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
405 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
407 // Since the restore from minimize will restore to the pre-minimize
408 // state (tested elsewhere), we abandon the current size and restore
409 // rect and set them to the window.
410 gfx::Rect restore = *GetRestoreBoundsInScreen(w1.get());
411 EXPECT_EQ("0,0 800x597", w1->bounds().ToString());
412 EXPECT_EQ("0,1 101x102", restore.ToString());
413 ClearRestoreBounds(w1.get());
414 w1->SetBounds(restore);
416 // Restore.
417 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
418 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
419 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
421 // Create another window, maximized.
422 scoped_ptr<Window> w2(CreateTestWindow());
423 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
424 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
425 w2->Show();
426 wm::ActivateWindow(w2.get());
427 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
428 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
429 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
431 // Switch to w1.
432 wm::ActivateWindow(w1.get());
433 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
434 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
435 EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(
436 w2->parent()).ToString(),
437 w2->bounds().ToString());
439 // Switch to w2.
440 wm::ActivateWindow(w2.get());
441 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
442 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
443 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
444 EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w2.get()).ToString(),
445 w2->bounds().ToString());
447 // Turn off auto-hide, switch back to w2 (maximized) and verify overlap.
448 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
449 wm::ActivateWindow(w2.get());
450 EXPECT_FALSE(GetWindowOverlapsShelf());
452 // Move w1 to overlap shelf, it shouldn't change window overlaps shelf since
453 // the window isn't in the visible workspace.
454 w1->SetBounds(touches_shelf_bounds);
455 EXPECT_FALSE(GetWindowOverlapsShelf());
457 // Activate w1. Although w1 is visible, the overlap state is still false since
458 // w2 is maximized.
459 wm::ActivateWindow(w1.get());
460 EXPECT_FALSE(GetWindowOverlapsShelf());
462 // Restore w2.
463 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
464 EXPECT_TRUE(GetWindowOverlapsShelf());
467 // Verifies going from maximized to minimized sets the right state for painting
468 // the background of the launcher.
469 TEST_F(WorkspaceControllerTest, MinimizeResetsVisibility) {
470 scoped_ptr<Window> w1(CreateTestWindow());
471 w1->Show();
472 wm::ActivateWindow(w1.get());
473 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
474 EXPECT_EQ(SHELF_BACKGROUND_MAXIMIZED, shelf_widget()->GetBackgroundType());
476 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
477 EXPECT_EQ(SHELF_VISIBLE,
478 shelf_layout_manager()->visibility_state());
479 EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, shelf_widget()->GetBackgroundType());
482 // Verifies window visibility during various workspace changes.
483 TEST_F(WorkspaceControllerTest, VisibilityTests) {
484 scoped_ptr<Window> w1(CreateTestWindow());
485 w1->Show();
486 EXPECT_TRUE(w1->IsVisible());
487 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
489 // Create another window, activate it and make it fullscreen.
490 scoped_ptr<Window> w2(CreateTestWindow());
491 w2->Show();
492 wm::ActivateWindow(w2.get());
493 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
494 EXPECT_TRUE(w2->IsVisible());
495 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
496 EXPECT_TRUE(w1->IsVisible());
498 // Switch to w1. |w1| should be visible on top of |w2|.
499 wm::ActivateWindow(w1.get());
500 EXPECT_TRUE(w1->IsVisible());
501 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
502 EXPECT_TRUE(w2->IsVisible());
504 // Switch back to |w2|.
505 wm::ActivateWindow(w2.get());
506 EXPECT_TRUE(w2->IsVisible());
507 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
508 EXPECT_TRUE(w1->IsVisible());
510 // Restore |w2|, both windows should be visible.
511 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
512 EXPECT_TRUE(w1->IsVisible());
513 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
514 EXPECT_TRUE(w2->IsVisible());
515 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
517 // Make |w2| fullscreen again, then close it.
518 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
519 w2->Hide();
520 EXPECT_FALSE(w2->IsVisible());
521 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
522 EXPECT_TRUE(w1->IsVisible());
524 // Create |w2| and maximize it.
525 w2.reset(CreateTestWindow());
526 w2->Show();
527 wm::ActivateWindow(w2.get());
528 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
529 EXPECT_TRUE(w2->IsVisible());
530 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
531 EXPECT_TRUE(w1->IsVisible());
533 // Close |w2|.
534 w2.reset();
535 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
536 EXPECT_TRUE(w1->IsVisible());
539 // Verifies windows that are offscreen don't move when switching workspaces.
540 TEST_F(WorkspaceControllerTest, DontMoveOnSwitch) {
541 aura::test::EventGenerator generator(
542 Shell::GetPrimaryRootWindow(), gfx::Point());
543 generator.MoveMouseTo(0, 0);
545 scoped_ptr<Window> w1(CreateTestWindow());
546 ShelfLayoutManager* shelf = shelf_layout_manager();
547 const gfx::Rect touches_shelf_bounds(
548 0, shelf->GetIdealBounds().y() - 10, 101, 102);
549 // Move |w1| to overlap the shelf.
550 w1->SetBounds(touches_shelf_bounds);
551 w1->Show();
552 wm::ActivateWindow(w1.get());
554 // Create another window and maximize it.
555 scoped_ptr<Window> w2(CreateTestWindow());
556 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
557 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
558 w2->Show();
559 wm::ActivateWindow(w2.get());
561 // Switch to w1.
562 wm::ActivateWindow(w1.get());
563 EXPECT_EQ(touches_shelf_bounds.ToString(), w1->bounds().ToString());
566 // Verifies that windows that are completely offscreen move when switching
567 // workspaces.
568 TEST_F(WorkspaceControllerTest, MoveOnSwitch) {
569 aura::test::EventGenerator generator(
570 Shell::GetPrimaryRootWindow(), gfx::Point());
571 generator.MoveMouseTo(0, 0);
573 scoped_ptr<Window> w1(CreateTestWindow());
574 ShelfLayoutManager* shelf = shelf_layout_manager();
575 const gfx::Rect w1_bounds(0, shelf->GetIdealBounds().y(), 100, 200);
576 // Move |w1| so that the top edge is the same as the top edge of the shelf.
577 w1->SetBounds(w1_bounds);
578 w1->Show();
579 wm::ActivateWindow(w1.get());
580 EXPECT_EQ(w1_bounds.ToString(), w1->bounds().ToString());
582 // Create another window and maximize it.
583 scoped_ptr<Window> w2(CreateTestWindow());
584 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
585 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
586 w2->Show();
587 wm::ActivateWindow(w2.get());
589 // Increase the size of the WorkAreaInsets. This would make |w1| fall
590 // completely out of the display work area.
591 gfx::Insets insets =
592 Shell::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets();
593 insets.Set(0, 0, insets.bottom() + 30, 0);
594 Shell::GetInstance()->SetDisplayWorkAreaInsets(w1.get(), insets);
596 // Switch to w1. The window should have moved.
597 wm::ActivateWindow(w1.get());
598 EXPECT_NE(w1_bounds.ToString(), w1->bounds().ToString());
601 namespace {
603 // WindowDelegate used by DontCrashOnChangeAndActivate.
604 class DontCrashOnChangeAndActivateDelegate
605 : public aura::test::TestWindowDelegate {
606 public:
607 DontCrashOnChangeAndActivateDelegate() : window_(NULL) {}
609 void set_window(aura::Window* window) { window_ = window; }
611 // WindowDelegate overrides:
612 virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
613 const gfx::Rect& new_bounds) OVERRIDE {
614 if (window_) {
615 wm::ActivateWindow(window_);
616 window_ = NULL;
620 private:
621 aura::Window* window_;
623 DISALLOW_COPY_AND_ASSIGN(DontCrashOnChangeAndActivateDelegate);
626 } // namespace
628 // Exercises possible crash in W2. Here's the sequence:
629 // . minimize a maximized window.
630 // . remove the window (which happens when switching displays).
631 // . add the window back.
632 // . show the window and during the bounds change activate it.
633 TEST_F(WorkspaceControllerTest, DontCrashOnChangeAndActivate) {
634 // Force the shelf
635 ShelfLayoutManager* shelf = shelf_layout_manager();
636 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
638 DontCrashOnChangeAndActivateDelegate delegate;
639 scoped_ptr<Window> w1(CreateTestWindowInShellWithDelegate(
640 &delegate, 1000, gfx::Rect(10, 11, 250, 251)));
642 w1->Show();
643 wm::ActivateWindow(w1.get());
644 wm::MaximizeWindow(w1.get());
645 wm::MinimizeWindow(w1.get());
647 w1->parent()->RemoveChild(w1.get());
649 // Do this so that when we Show() the window a resize occurs and we make the
650 // window active.
651 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
653 SetDefaultParentByPrimaryRootWindow(w1.get());
654 delegate.set_window(w1.get());
655 w1->Show();
658 // Verifies a window with a transient parent not managed by workspace works.
659 TEST_F(WorkspaceControllerTest, TransientParent) {
660 // Normal window with no transient parent.
661 scoped_ptr<Window> w2(CreateTestWindow());
662 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
663 w2->Show();
664 wm::ActivateWindow(w2.get());
666 // Window with a transient parent. We set the transient parent to the root,
667 // which would never happen but is enough to exercise the bug.
668 scoped_ptr<Window> w1(CreateTestWindowUnparented());
669 Shell::GetInstance()->GetPrimaryRootWindow()->AddTransientChild(w1.get());
670 w1->SetBounds(gfx::Rect(10, 11, 250, 251));
671 SetDefaultParentByPrimaryRootWindow(w1.get());
672 w1->Show();
673 wm::ActivateWindow(w1.get());
675 // The window with the transient parent should get added to the same parent as
676 // the normal window.
677 EXPECT_EQ(w2->parent(), w1->parent());
680 // Verifies changing TrackedByWorkspace works.
681 TEST_F(WorkspaceControllerTest, TrackedByWorkspace) {
682 // Create a fullscreen window.
683 scoped_ptr<Window> w1(CreateTestWindow());
684 w1->Show();
685 wm::ActivateWindow(w1.get());
686 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
687 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
688 EXPECT_TRUE(w1->IsVisible());
690 // Create a second fullscreen window and mark it not tracked by workspace
691 // manager.
692 scoped_ptr<Window> w2(CreateTestWindowUnparented());
693 w2->SetBounds(gfx::Rect(1, 6, 25, 30));
694 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
695 SetDefaultParentByPrimaryRootWindow(w2.get());
696 w2->Show();
697 SetTrackedByWorkspace(w2.get(), false);
698 wm::ActivateWindow(w2.get());
700 // Activating |w2| should force it to have the same parent as |w1|.
701 EXPECT_EQ(w1->parent(), w2->parent());
702 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
703 EXPECT_TRUE(w1->IsVisible());
704 EXPECT_TRUE(w2->IsVisible());
706 // Because |w2| isn't tracked we should be able to set the bounds of it.
707 gfx::Rect bounds(w2->bounds());
708 bounds.Offset(4, 5);
709 w2->SetBounds(bounds);
710 EXPECT_EQ(bounds.ToString(), w2->bounds().ToString());
712 // Transition it to tracked by worskpace. It should end up in the desktop
713 // workspace.
714 SetTrackedByWorkspace(w2.get(), true);
715 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
716 EXPECT_TRUE(w1->IsVisible());
717 EXPECT_TRUE(w2->IsVisible());
718 EXPECT_EQ(w1->parent(), w2->parent());
721 // Test the basic auto placement of one and or two windows in a "simulated
722 // session" of sequential window operations.
723 TEST_F(WorkspaceControllerTest, BasicAutoPlacing) {
724 // Test 1: In case there is no manageable window, no window should shift.
726 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
727 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
728 gfx::Rect desktop_area = window1->parent()->bounds();
730 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
731 // Trigger the auto window placement function by making it visible.
732 // Note that the bounds are getting changed while it is invisible.
733 window2->Hide();
734 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
735 window2->Show();
737 // Check the initial position of the windows is unchanged.
738 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
739 EXPECT_EQ("32,48 256x512", window2->bounds().ToString());
741 // Remove the second window and make sure that the first window
742 // does NOT get centered.
743 window2.reset();
744 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
746 // Test 2: Set up two managed windows and check their auto positioning.
747 ash::wm::SetWindowPositionManaged(window1.get(), true);
748 scoped_ptr<aura::Window> window3(CreateTestWindowInShellWithId(2));
749 ash::wm::SetWindowPositionManaged(window3.get(), true);
750 // To avoid any auto window manager changes due to SetBounds, the window
751 // gets first hidden and then shown again.
752 window3->Hide();
753 window3->SetBounds(gfx::Rect(32, 48, 256, 512));
754 window3->Show();
755 // |window1| should be flush right and |window3| flush left.
756 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
757 EXPECT_EQ(base::IntToString(
758 desktop_area.width() - window3->bounds().width()) +
759 ",48 256x512", window3->bounds().ToString());
761 // After removing |window3|, |window1| should be centered again.
762 window3.reset();
763 EXPECT_EQ(
764 base::IntToString(
765 (desktop_area.width() - window1->bounds().width()) / 2) +
766 ",32 640x320", window1->bounds().ToString());
768 // Test 3: Set up a manageable and a non manageable window and check
769 // positioning.
770 scoped_ptr<aura::Window> window4(CreateTestWindowInShellWithId(3));
771 // To avoid any auto window manager changes due to SetBounds, the window
772 // gets first hidden and then shown again.
773 window1->Hide();
774 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
775 window4->SetBounds(gfx::Rect(32, 48, 256, 512));
776 window1->Show();
777 // |window1| should be centered and |window4| untouched.
778 EXPECT_EQ(
779 base::IntToString(
780 (desktop_area.width() - window1->bounds().width()) / 2) +
781 ",32 640x320", window1->bounds().ToString());
782 EXPECT_EQ("32,48 256x512", window4->bounds().ToString());
784 // Test4: A single manageable window should get centered.
785 window4.reset();
786 ash::wm::SetUserHasChangedWindowPositionOrSize(window1.get(), false);
787 // Trigger the auto window placement function by showing (and hiding) it.
788 window1->Hide();
789 window1->Show();
790 // |window1| should be centered.
791 EXPECT_EQ(
792 base::IntToString(
793 (desktop_area.width() - window1->bounds().width()) / 2) +
794 ",32 640x320", window1->bounds().ToString());
797 // Test the proper usage of user window movement interaction.
798 TEST_F(WorkspaceControllerTest, TestUserMovedWindowRepositioning) {
799 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
800 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
801 gfx::Rect desktop_area = window1->parent()->bounds();
802 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
803 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
804 window1->Hide();
805 window2->Hide();
806 ash::wm::SetWindowPositionManaged(window1.get(), true);
807 ash::wm::SetWindowPositionManaged(window2.get(), true);
808 EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
809 EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window2.get()));
811 // Check that the current location gets preserved if the user has
812 // positioned it previously.
813 ash::wm::SetUserHasChangedWindowPositionOrSize(window1.get(), true);
814 window1->Show();
815 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
816 // Flag should be still set.
817 EXPECT_TRUE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
818 EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window2.get()));
820 // Turn on the second window and make sure that both windows are now
821 // positionable again (user movement cleared).
822 window2->Show();
824 // |window1| should be flush left and |window3| flush right.
825 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
826 EXPECT_EQ(
827 base::IntToString(desktop_area.width() - window2->bounds().width()) +
828 ",48 256x512", window2->bounds().ToString());
829 // FLag should now be reset.
830 EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
831 EXPECT_FALSE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
833 // Going back to one shown window should keep the state.
834 ash::wm::SetUserHasChangedWindowPositionOrSize(window1.get(), true);
835 window2->Hide();
836 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
837 EXPECT_TRUE(ash::wm::HasUserChangedWindowPositionOrSize(window1.get()));
840 // Test that user placed windows go back to their user placement after the user
841 // closes all other windows.
842 TEST_F(WorkspaceControllerTest, TestUserHandledWindowRestore) {
843 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
844 gfx::Rect user_pos = gfx::Rect(16, 42, 640, 320);
845 window1->SetBounds(user_pos);
846 ash::wm::SetPreAutoManageWindowBounds(window1.get(), user_pos);
847 gfx::Rect desktop_area = window1->parent()->bounds();
849 // Create a second window to let the auto manager kick in.
850 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
851 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
852 window1->Hide();
853 window2->Hide();
854 ash::wm::SetWindowPositionManaged(window1.get(), true);
855 ash::wm::SetWindowPositionManaged(window2.get(), true);
856 window1->Show();
857 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
858 window2->Show();
860 // |window1| should be flush left and |window2| flush right.
861 EXPECT_EQ("0," + base::IntToString(user_pos.y()) +
862 " 640x320", window1->bounds().ToString());
863 EXPECT_EQ(
864 base::IntToString(desktop_area.width() - window2->bounds().width()) +
865 ",48 256x512", window2->bounds().ToString());
866 window2->Hide();
868 // After the other window get hidden the window has to move back to the
869 // previous position and the bounds should still be set and unchanged.
870 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
871 ASSERT_TRUE(ash::wm::GetPreAutoManageWindowBounds(window1.get()));
872 EXPECT_EQ(user_pos.ToString(),
873 ash::wm::GetPreAutoManageWindowBounds(window1.get())->ToString());
876 // Test that a window from normal to minimize will repos the remaining.
877 TEST_F(WorkspaceControllerTest, ToMinimizeRepositionsRemaining) {
878 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
879 ash::wm::SetWindowPositionManaged(window1.get(), true);
880 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
881 gfx::Rect desktop_area = window1->parent()->bounds();
883 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
884 ash::wm::SetWindowPositionManaged(window2.get(), true);
885 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
887 ash::wm::MinimizeWindow(window1.get());
889 // |window2| should be centered now.
890 EXPECT_TRUE(window2->IsVisible());
891 EXPECT_TRUE(ash::wm::IsWindowNormal(window2.get()));
892 EXPECT_EQ(base::IntToString(
893 (desktop_area.width() - window2->bounds().width()) / 2) +
894 ",48 256x512", window2->bounds().ToString());
896 ash::wm::RestoreWindow(window1.get());
897 // |window1| should be flush right and |window3| flush left.
898 EXPECT_EQ(base::IntToString(
899 desktop_area.width() - window1->bounds().width()) +
900 ",32 640x320", window1->bounds().ToString());
901 EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
904 // Test that minimizing an initially maximized window will repos the remaining.
905 TEST_F(WorkspaceControllerTest, MaxToMinRepositionsRemaining) {
906 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
907 ash::wm::SetWindowPositionManaged(window1.get(), true);
908 gfx::Rect desktop_area = window1->parent()->bounds();
910 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
911 ash::wm::SetWindowPositionManaged(window2.get(), true);
912 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
914 ash::wm::MaximizeWindow(window1.get());
915 ash::wm::MinimizeWindow(window1.get());
917 // |window2| should be centered now.
918 EXPECT_TRUE(window2->IsVisible());
919 EXPECT_TRUE(ash::wm::IsWindowNormal(window2.get()));
920 EXPECT_EQ(base::IntToString(
921 (desktop_area.width() - window2->bounds().width()) / 2) +
922 ",48 256x512", window2->bounds().ToString());
925 // Test that nomral, maximize, minimizing will repos the remaining.
926 TEST_F(WorkspaceControllerTest, NormToMaxToMinRepositionsRemaining) {
927 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
928 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
929 ash::wm::SetWindowPositionManaged(window1.get(), true);
930 gfx::Rect desktop_area = window1->parent()->bounds();
932 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
933 ash::wm::SetWindowPositionManaged(window2.get(), true);
934 window2->SetBounds(gfx::Rect(32, 40, 256, 512));
936 // Trigger the auto window placement function by showing (and hiding) it.
937 window1->Hide();
938 window1->Show();
940 // |window1| should be flush right and |window3| flush left.
941 EXPECT_EQ(base::IntToString(
942 desktop_area.width() - window1->bounds().width()) +
943 ",32 640x320", window1->bounds().ToString());
944 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
946 ash::wm::MaximizeWindow(window1.get());
947 ash::wm::MinimizeWindow(window1.get());
949 // |window2| should be centered now.
950 EXPECT_TRUE(window2->IsVisible());
951 EXPECT_TRUE(ash::wm::IsWindowNormal(window2.get()));
952 EXPECT_EQ(base::IntToString(
953 (desktop_area.width() - window2->bounds().width()) / 2) +
954 ",40 256x512", window2->bounds().ToString());
957 // Test that nomral, maximize, normal will repos the remaining.
958 TEST_F(WorkspaceControllerTest, NormToMaxToNormRepositionsRemaining) {
959 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
960 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
961 ash::wm::SetWindowPositionManaged(window1.get(), true);
962 gfx::Rect desktop_area = window1->parent()->bounds();
964 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
965 ash::wm::SetWindowPositionManaged(window2.get(), true);
966 window2->SetBounds(gfx::Rect(32, 40, 256, 512));
968 // Trigger the auto window placement function by showing (and hiding) it.
969 window1->Hide();
970 window1->Show();
972 // |window1| should be flush right and |window3| flush left.
973 EXPECT_EQ(base::IntToString(
974 desktop_area.width() - window1->bounds().width()) +
975 ",32 640x320", window1->bounds().ToString());
976 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
978 ash::wm::MaximizeWindow(window1.get());
979 ash::wm::RestoreWindow(window1.get());
981 // |window1| should be flush right and |window2| flush left.
982 EXPECT_EQ(base::IntToString(
983 desktop_area.width() - window1->bounds().width()) +
984 ",32 640x320", window1->bounds().ToString());
985 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
988 // Test that animations are triggered.
989 TEST_F(WorkspaceControllerTest, AnimatedNormToMaxToNormRepositionsRemaining) {
990 ui::ScopedAnimationDurationScaleMode normal_duration_mode(
991 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
992 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
993 window1->Hide();
994 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
995 gfx::Rect desktop_area = window1->parent()->bounds();
996 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
997 window2->Hide();
998 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1000 ash::wm::SetWindowPositionManaged(window1.get(), true);
1001 ash::wm::SetWindowPositionManaged(window2.get(), true);
1002 // Make sure nothing is animating.
1003 window1->layer()->GetAnimator()->StopAnimating();
1004 window2->layer()->GetAnimator()->StopAnimating();
1005 window2->Show();
1007 // The second window should now animate.
1008 EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating());
1009 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1010 window2->layer()->GetAnimator()->StopAnimating();
1012 window1->Show();
1013 EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
1014 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1016 window1->layer()->GetAnimator()->StopAnimating();
1017 window2->layer()->GetAnimator()->StopAnimating();
1018 // |window1| should be flush right and |window2| flush left.
1019 EXPECT_EQ(base::IntToString(
1020 desktop_area.width() - window1->bounds().width()) +
1021 ",32 640x320", window1->bounds().ToString());
1022 EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
1025 // This tests simulates a browser and an app and verifies the ordering of the
1026 // windows and layers doesn't get out of sync as various operations occur. Its
1027 // really testing code in FocusController, but easier to simulate here. Just as
1028 // with a real browser the browser here has a transient child window
1029 // (corresponds to the status bubble).
1030 TEST_F(WorkspaceControllerTest, VerifyLayerOrdering) {
1031 scoped_ptr<Window> browser(
1032 aura::test::CreateTestWindowWithDelegate(
1033 NULL,
1034 aura::client::WINDOW_TYPE_NORMAL,
1035 gfx::Rect(5, 6, 7, 8),
1036 NULL));
1037 browser->SetName("browser");
1038 SetDefaultParentByPrimaryRootWindow(browser.get());
1039 browser->Show();
1040 wm::ActivateWindow(browser.get());
1042 // |status_bubble| is made a transient child of |browser| and as a result
1043 // owned by |browser|.
1044 aura::test::TestWindowDelegate* status_bubble_delegate =
1045 aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate();
1046 status_bubble_delegate->set_can_focus(false);
1047 Window* status_bubble =
1048 aura::test::CreateTestWindowWithDelegate(
1049 status_bubble_delegate,
1050 aura::client::WINDOW_TYPE_POPUP,
1051 gfx::Rect(5, 6, 7, 8),
1052 NULL);
1053 browser->AddTransientChild(status_bubble);
1054 SetDefaultParentByPrimaryRootWindow(status_bubble);
1055 status_bubble->SetName("status_bubble");
1057 scoped_ptr<Window> app(
1058 aura::test::CreateTestWindowWithDelegate(
1059 NULL,
1060 aura::client::WINDOW_TYPE_NORMAL,
1061 gfx::Rect(5, 6, 7, 8),
1062 NULL));
1063 app->SetName("app");
1064 SetDefaultParentByPrimaryRootWindow(app.get());
1066 aura::Window* parent = browser->parent();
1068 app->Show();
1069 wm::ActivateWindow(app.get());
1070 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1072 // Minimize the app, focus should go the browser.
1073 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
1074 EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
1075 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1077 // Minimize the browser (neither windows are focused).
1078 browser->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
1079 EXPECT_FALSE(wm::IsActiveWindow(browser.get()));
1080 EXPECT_FALSE(wm::IsActiveWindow(app.get()));
1081 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1083 // Show the browser (which should restore it).
1084 browser->Show();
1085 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1087 // Activate the browser.
1088 ash::wm::ActivateWindow(browser.get());
1089 EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
1090 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1092 // Restore the app. This differs from above code for |browser| as internally
1093 // the app code does this. Restoring this way or using Show() should not make
1094 // a difference.
1095 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1096 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1098 // Activate the app.
1099 ash::wm::ActivateWindow(app.get());
1100 EXPECT_TRUE(wm::IsActiveWindow(app.get()));
1101 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1104 namespace {
1106 // Used by DragMaximizedNonTrackedWindow to track how many times the window
1107 // hierarchy changes.
1108 class DragMaximizedNonTrackedWindowObserver
1109 : public aura::WindowObserver {
1110 public:
1111 DragMaximizedNonTrackedWindowObserver() : change_count_(0) {
1114 // Number of times OnWindowHierarchyChanged() has been received.
1115 void clear_change_count() { change_count_ = 0; }
1116 int change_count() const {
1117 return change_count_;
1120 // aura::WindowObserver overrides:
1121 // Counts number of times a window is reparented. Ignores reparenting into and
1122 // from a docked container which is expected when a tab is dragged.
1123 virtual void OnWindowHierarchyChanged(
1124 const HierarchyChangeParams& params) OVERRIDE {
1125 if ((params.old_parent->id() == kShellWindowId_DefaultContainer &&
1126 params.new_parent->id() == kShellWindowId_DockedContainer) ||
1127 (params.old_parent->id() == kShellWindowId_DockedContainer &&
1128 params.new_parent->id() == kShellWindowId_DefaultContainer)) {
1129 return;
1131 change_count_++;
1134 private:
1135 int change_count_;
1137 DISALLOW_COPY_AND_ASSIGN(DragMaximizedNonTrackedWindowObserver);
1140 } // namespace
1142 // Verifies setting tracked by workspace to false and then dragging a fullscreen
1143 // window doesn't result in changing the window hierarchy (which typically
1144 // indicates new workspaces have been created).
1145 TEST_F(WorkspaceControllerTest, DragFullscreenNonTrackedWindow) {
1146 aura::test::EventGenerator generator(
1147 Shell::GetPrimaryRootWindow(), gfx::Point());
1148 generator.MoveMouseTo(5, 5);
1150 aura::test::TestWindowDelegate delegate;
1151 delegate.set_window_component(HTCAPTION);
1152 scoped_ptr<Window> w1(
1153 aura::test::CreateTestWindowWithDelegate(&delegate,
1154 aura::client::WINDOW_TYPE_NORMAL,
1155 gfx::Rect(5, 6, 7, 8),
1156 NULL));
1157 SetDefaultParentByPrimaryRootWindow(w1.get());
1158 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
1159 w1->Show();
1160 wm::ActivateWindow(w1.get());
1161 DragMaximizedNonTrackedWindowObserver observer;
1162 w1->parent()->parent()->AddObserver(&observer);
1163 const gfx::Rect max_bounds(w1->bounds());
1165 generator.PressLeftButton();
1166 generator.MoveMouseTo(100, 100);
1167 // The bounds shouldn't change (drag should result in nothing happening
1168 // now.
1169 EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
1171 generator.ReleaseLeftButton();
1172 EXPECT_EQ(0, observer.change_count());
1174 // Set tracked to false and repeat, now the window should move.
1175 SetTrackedByWorkspace(w1.get(), false);
1176 generator.MoveMouseTo(5, 5);
1177 generator.PressLeftButton();
1178 generator.MoveMouseBy(100, 100);
1179 EXPECT_EQ(gfx::Rect(max_bounds.x() + 100, max_bounds.y() + 100,
1180 max_bounds.width(), max_bounds.height()).ToString(),
1181 w1->bounds().ToString());
1183 generator.ReleaseLeftButton();
1184 SetTrackedByWorkspace(w1.get(), true);
1185 // Marking the window tracked again should snap back to origin.
1186 EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
1187 EXPECT_EQ(0, observer.change_count());
1189 w1->parent()->parent()->RemoveObserver(&observer);
1192 // Verifies setting tracked by workspace to false and then dragging a maximized
1193 // window can change the bound.
1194 TEST_F(WorkspaceControllerTest, DragMaximizedNonTrackedWindow) {
1195 aura::test::EventGenerator generator(
1196 Shell::GetPrimaryRootWindow(), gfx::Point());
1197 generator.MoveMouseTo(5, 5);
1199 aura::test::TestWindowDelegate delegate;
1200 delegate.set_window_component(HTCAPTION);
1201 scoped_ptr<Window> w1(
1202 aura::test::CreateTestWindowWithDelegate(&delegate,
1203 aura::client::WINDOW_TYPE_NORMAL,
1204 gfx::Rect(5, 6, 7, 8),
1205 NULL));
1206 SetDefaultParentByPrimaryRootWindow(w1.get());
1207 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
1208 w1->Show();
1209 wm::ActivateWindow(w1.get());
1210 DragMaximizedNonTrackedWindowObserver observer;
1211 w1->parent()->parent()->AddObserver(&observer);
1212 const gfx::Rect max_bounds(w1->bounds());
1214 generator.PressLeftButton();
1215 generator.MoveMouseTo(100, 100);
1216 // The bounds shouldn't change (drag should result in nothing happening
1217 // now.
1218 EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
1220 generator.ReleaseLeftButton();
1221 EXPECT_EQ(0, observer.change_count());
1223 // Set tracked to false and repeat, now the window should move.
1224 SetTrackedByWorkspace(w1.get(), false);
1225 generator.MoveMouseTo(5, 5);
1226 generator.PressLeftButton();
1227 generator.MoveMouseBy(100, 100);
1228 EXPECT_EQ(gfx::Rect(max_bounds.x() + 100, max_bounds.y() + 100,
1229 max_bounds.width(), max_bounds.height()).ToString(),
1230 w1->bounds().ToString());
1232 generator.ReleaseLeftButton();
1233 SetTrackedByWorkspace(w1.get(), true);
1234 // Marking the window tracked again should snap back to origin.
1235 EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
1236 EXPECT_EQ(0, observer.change_count());
1238 w1->parent()->parent()->RemoveObserver(&observer);
1241 // Verifies that a new maximized window becomes visible after its activation
1242 // is requested, even though it does not become activated because a system
1243 // modal window is active.
1244 TEST_F(WorkspaceControllerTest, SwitchFromModal) {
1245 scoped_ptr<Window> modal_window(CreateTestWindowUnparented());
1246 modal_window->SetBounds(gfx::Rect(10, 11, 21, 22));
1247 modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
1248 SetDefaultParentByPrimaryRootWindow(modal_window.get());
1249 modal_window->Show();
1250 wm::ActivateWindow(modal_window.get());
1252 scoped_ptr<Window> maximized_window(CreateTestWindow());
1253 maximized_window->SetProperty(
1254 aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
1255 maximized_window->Show();
1256 wm::ActivateWindow(maximized_window.get());
1257 EXPECT_TRUE(maximized_window->IsVisible());
1260 } // namespace internal
1261 } // namespace ash