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.
4
5 #include "ash/wm/workspace_controller.h"
6
7 #include <map>
8
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/window_state.h"
20 #include "ash/wm/window_util.h"
21 #include "base/command_line.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "ui/aura/client/aura_constants.h"
24 #include "ui/aura/root_window.h"
25 #include "ui/aura/test/event_generator.h"
26 #include "ui/aura/test/test_window_delegate.h"
27 #include "ui/aura/test/test_windows.h"
28 #include "ui/aura/window.h"
29 #include "ui/base/hit_test.h"
30 #include "ui/base/ui_base_types.h"
31 #include "ui/compositor/layer.h"
32 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
33 #include "ui/gfx/screen.h"
34 #include "ui/views/corewm/window_animations.h"
35 #include "ui/views/widget/widget.h"
36
37 using aura::Window;
38
39 namespace ash {
40 namespace internal {
41
42 // Returns a string containing the names of all the children of |window| (in
43 // order). Each entry is separated by a space.
GetWindowNames(const aura::Window * window)44 std::string GetWindowNames(const aura::Window* window) {
45 std::string result;
46 for (size_t i = 0; i < window->children().size(); ++i) {
47 if (i != 0)
48 result += " ";
49 result += window->children()[i]->name();
50 }
51 return result;
52 }
53
54 // Returns a string containing the names of windows corresponding to each of the
55 // child layers of |window|'s layer. Any layers that don't correspond to a child
56 // Window of |window| are ignored. The result is ordered based on the layer
57 // ordering.
GetLayerNames(const aura::Window * window)58 std::string GetLayerNames(const aura::Window* window) {
59 typedef std::map<const ui::Layer*, std::string> LayerToWindowNameMap;
60 LayerToWindowNameMap window_names;
61 for (size_t i = 0; i < window->children().size(); ++i) {
62 window_names[window->children()[i]->layer()] =
63 window->children()[i]->name();
64 }
65
66 std::string result;
67 const std::vector<ui::Layer*>& layers(window->layer()->children());
68 for (size_t i = 0; i < layers.size(); ++i) {
69 LayerToWindowNameMap::iterator layer_i =
70 window_names.find(layers[i]);
71 if (layer_i != window_names.end()) {
72 if (!result.empty())
73 result += " ";
74 result += layer_i->second;
75 }
76 }
77 return result;
78 }
79
80 class WorkspaceControllerTest : public test::AshTestBase {
81 public:
WorkspaceControllerTest()82 WorkspaceControllerTest() {}
~WorkspaceControllerTest()83 virtual ~WorkspaceControllerTest() {}
84
CreateTestWindowUnparented()85 aura::Window* CreateTestWindowUnparented() {
86 aura::Window* window = new aura::Window(NULL);
87 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
88 window->SetType(aura::client::WINDOW_TYPE_NORMAL);
89 window->Init(ui::LAYER_TEXTURED);
90 return window;
91 }
92
CreateTestWindow()93 aura::Window* CreateTestWindow() {
94 aura::Window* window = new aura::Window(NULL);
95 window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
96 window->SetType(aura::client::WINDOW_TYPE_NORMAL);
97 window->Init(ui::LAYER_TEXTURED);
98 ParentWindowInPrimaryRootWindow(window);
99 return window;
100 }
101
CreateBrowserLikeWindow(const gfx::Rect & bounds)102 aura::Window* CreateBrowserLikeWindow(const gfx::Rect& bounds) {
103 aura::Window* window = CreateTestWindow();
104 window->SetBounds(bounds);
105 wm::WindowState* window_state = wm::GetWindowState(window);
106 window_state->set_window_position_managed(true);
107 window->Show();
108 return window;
109 }
110
CreatePopupLikeWindow(const gfx::Rect & bounds)111 aura::Window* CreatePopupLikeWindow(const gfx::Rect& bounds) {
112 aura::Window* window = CreateTestWindowInShellWithBounds(bounds);
113 window->Show();
114 return window;
115 }
116
GetDesktop()117 aura::Window* GetDesktop() {
118 return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
119 kShellWindowId_DefaultContainer);
120 }
121
GetFullscreenBounds(aura::Window * window)122 gfx::Rect GetFullscreenBounds(aura::Window* window) {
123 return Shell::GetScreen()->GetDisplayNearestWindow(window).bounds();
124 }
125
shelf_widget()126 ShelfWidget* shelf_widget() {
127 return Shell::GetPrimaryRootWindowController()->shelf();
128 }
129
shelf_layout_manager()130 ShelfLayoutManager* shelf_layout_manager() {
131 return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
132 }
133
GetWindowOverlapsShelf()134 bool GetWindowOverlapsShelf() {
135 return shelf_layout_manager()->window_overlaps_shelf();
136 }
137
138 private:
139 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTest);
140 };
141
142 // Assertions around adding a normal window.
TEST_F(WorkspaceControllerTest,AddNormalWindowWhenEmpty)143 TEST_F(WorkspaceControllerTest, AddNormalWindowWhenEmpty) {
144 scoped_ptr<Window> w1(CreateTestWindow());
145 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
146
147 wm::WindowState* window_state = wm::GetWindowState(w1.get());
148
149 EXPECT_FALSE(window_state->HasRestoreBounds());
150
151 w1->Show();
152
153 EXPECT_FALSE(window_state->HasRestoreBounds());
154
155 ASSERT_TRUE(w1->layer() != NULL);
156 EXPECT_TRUE(w1->layer()->visible());
157
158 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
159
160 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
161 }
162
163 // Assertions around maximizing/unmaximizing.
TEST_F(WorkspaceControllerTest,SingleMaximizeWindow)164 TEST_F(WorkspaceControllerTest, SingleMaximizeWindow) {
165 scoped_ptr<Window> w1(CreateTestWindow());
166 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
167
168 w1->Show();
169 wm::ActivateWindow(w1.get());
170
171 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
172
173 ASSERT_TRUE(w1->layer() != NULL);
174 EXPECT_TRUE(w1->layer()->visible());
175
176 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
177
178 // Maximize the window.
179 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
180
181 EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
182
183 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
184 EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()).width(),
185 w1->bounds().width());
186 EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()).height(),
187 w1->bounds().height());
188
189 // Restore the window.
190 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
191
192 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
193 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
194 }
195
196 // Assertions around two windows and toggling one to be fullscreen.
TEST_F(WorkspaceControllerTest,FullscreenWithNormalWindow)197 TEST_F(WorkspaceControllerTest, FullscreenWithNormalWindow) {
198 scoped_ptr<Window> w1(CreateTestWindow());
199 scoped_ptr<Window> w2(CreateTestWindow());
200 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
201 w1->Show();
202
203 ASSERT_TRUE(w1->layer() != NULL);
204 EXPECT_TRUE(w1->layer()->visible());
205
206 w2->SetBounds(gfx::Rect(0, 0, 50, 51));
207 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
208 w2->Show();
209 wm::ActivateWindow(w2.get());
210
211 // Both windows should be in the same workspace.
212 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
213 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
214
215 gfx::Rect work_area(
216 ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()));
217 EXPECT_EQ(work_area.width(), w2->bounds().width());
218 EXPECT_EQ(work_area.height(), w2->bounds().height());
219
220 // Restore w2, which should then go back to one workspace.
221 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
222 EXPECT_EQ(50, w2->bounds().width());
223 EXPECT_EQ(51, w2->bounds().height());
224 EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
225 }
226
227 // Makes sure requests to change the bounds of a normal window go through.
TEST_F(WorkspaceControllerTest,ChangeBoundsOfNormalWindow)228 TEST_F(WorkspaceControllerTest, ChangeBoundsOfNormalWindow) {
229 scoped_ptr<Window> w1(CreateTestWindow());
230 w1->Show();
231
232 // Setting the bounds should go through since the window is in the normal
233 // workspace.
234 w1->SetBounds(gfx::Rect(0, 0, 200, 500));
235 EXPECT_EQ(200, w1->bounds().width());
236 EXPECT_EQ(500, w1->bounds().height());
237 }
238
239 // Verifies the bounds is not altered when showing and grid is enabled.
TEST_F(WorkspaceControllerTest,SnapToGrid)240 TEST_F(WorkspaceControllerTest, SnapToGrid) {
241 scoped_ptr<Window> w1(CreateTestWindowUnparented());
242 w1->SetBounds(gfx::Rect(1, 6, 25, 30));
243 ParentWindowInPrimaryRootWindow(w1.get());
244 // We are not aligning this anymore this way. When the window gets shown
245 // the window is expected to be handled differently, but this cannot be
246 // tested with this test. So the result of this test should be that the
247 // bounds are exactly as passed in.
248 EXPECT_EQ("1,6 25x30", w1->bounds().ToString());
249 }
250
251 // Assertions around a fullscreen window.
TEST_F(WorkspaceControllerTest,SingleFullscreenWindow)252 TEST_F(WorkspaceControllerTest, SingleFullscreenWindow) {
253 scoped_ptr<Window> w1(CreateTestWindow());
254 w1->SetBounds(gfx::Rect(0, 0, 250, 251));
255 // Make the window fullscreen.
256 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
257 w1->Show();
258 wm::ActivateWindow(w1.get());
259
260 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
261 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
262 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
263
264 // Restore the window. Use SHOW_STATE_DEFAULT as that is what we'll end up
265 // with when using views::Widget.
266 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT);
267 EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
268
269 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
270 EXPECT_EQ(250, w1->bounds().width());
271 EXPECT_EQ(251, w1->bounds().height());
272
273 // Back to fullscreen.
274 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
275 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
276 EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
277 EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
278 wm::WindowState* window_state = wm::GetWindowState(w1.get());
279
280 ASSERT_TRUE(window_state->HasRestoreBounds());
281 EXPECT_EQ("0,0 250x251", window_state->GetRestoreBoundsInScreen().ToString());
282 }
283
284 // Assertions around minimizing a single window.
TEST_F(WorkspaceControllerTest,MinimizeSingleWindow)285 TEST_F(WorkspaceControllerTest, MinimizeSingleWindow) {
286 scoped_ptr<Window> w1(CreateTestWindow());
287
288 w1->Show();
289
290 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
291 EXPECT_FALSE(w1->layer()->IsDrawn());
292
293 // Show the window.
294 w1->Show();
295 EXPECT_TRUE(wm::GetWindowState(w1.get())->IsNormalShowState());
296 EXPECT_TRUE(w1->layer()->IsDrawn());
297 }
298
299 // Assertions around minimizing a fullscreen window.
TEST_F(WorkspaceControllerTest,MinimizeFullscreenWindow)300 TEST_F(WorkspaceControllerTest, MinimizeFullscreenWindow) {
301 // Two windows, w1 normal, w2 fullscreen.
302 scoped_ptr<Window> w1(CreateTestWindow());
303 scoped_ptr<Window> w2(CreateTestWindow());
304 w1->Show();
305 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
306 w2->Show();
307
308 wm::WindowState* w1_state = wm::GetWindowState(w1.get());
309 wm::WindowState* w2_state = wm::GetWindowState(w2.get());
310
311 w2_state->Activate();
312
313 // Minimize w2.
314 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
315 EXPECT_TRUE(w1->layer()->IsDrawn());
316 EXPECT_FALSE(w2->layer()->IsDrawn());
317
318 // Show the window, which should trigger unminimizing.
319 w2->Show();
320 w2_state->Activate();
321
322 EXPECT_TRUE(w2_state->IsFullscreen());
323 EXPECT_TRUE(w1->layer()->IsDrawn());
324 EXPECT_TRUE(w2->layer()->IsDrawn());
325
326 // Minimize the window, which should hide the window.
327 EXPECT_TRUE(w2_state->IsActive());
328 w2_state->Minimize();
329 EXPECT_FALSE(w2_state->IsActive());
330 EXPECT_FALSE(w2->layer()->IsDrawn());
331 EXPECT_TRUE(w1_state->IsActive());
332
333 // Make the window normal.
334 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
335 EXPECT_EQ(w1.get(), GetDesktop()->children()[0]);
336 EXPECT_EQ(w2.get(), GetDesktop()->children()[1]);
337 EXPECT_TRUE(w2->layer()->IsDrawn());
338 }
339
340 // Verifies ShelfLayoutManager's visibility/auto-hide state is correctly
341 // updated.
TEST_F(WorkspaceControllerTest,ShelfStateUpdated)342 TEST_F(WorkspaceControllerTest, ShelfStateUpdated) {
343 // Since ShelfLayoutManager queries for mouse location, move the mouse so
344 // it isn't over the shelf.
345 aura::test::EventGenerator generator(
346 Shell::GetPrimaryRootWindow(), gfx::Point());
347 generator.MoveMouseTo(0, 0);
348
349 scoped_ptr<Window> w1(CreateTestWindow());
350 const gfx::Rect w1_bounds(0, 1, 101, 102);
351 ShelfLayoutManager* shelf = shelf_layout_manager();
352 shelf->SetAutoHideBehavior(ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
353 const gfx::Rect touches_shelf_bounds(
354 0, shelf->GetIdealBounds().y() - 10, 101, 102);
355 // Move |w1| to overlap the shelf.
356 w1->SetBounds(touches_shelf_bounds);
357 EXPECT_FALSE(GetWindowOverlapsShelf());
358
359 // A visible ignored window should not trigger the overlap.
360 scoped_ptr<Window> w_ignored(CreateTestWindow());
361 w_ignored->SetBounds(touches_shelf_bounds);
362 wm::GetWindowState(&(*w_ignored))->set_ignored_by_shelf(true);
363 w_ignored->Show();
364 EXPECT_FALSE(GetWindowOverlapsShelf());
365
366 // Make it visible, since visible shelf overlaps should be true.
367 w1->Show();
368 EXPECT_TRUE(GetWindowOverlapsShelf());
369
370 wm::ActivateWindow(w1.get());
371 w1->SetBounds(w1_bounds);
372 w1->Show();
373 wm::ActivateWindow(w1.get());
374
375 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
376
377 // Maximize the window.
378 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
379 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
380 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
381
382 // Restore.
383 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
384 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
385 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
386
387 // Fullscreen.
388 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
389 EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
390
391 // Normal.
392 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
393 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
394 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
395 EXPECT_FALSE(GetWindowOverlapsShelf());
396
397 // Move window so it obscures shelf.
398 w1->SetBounds(touches_shelf_bounds);
399 EXPECT_TRUE(GetWindowOverlapsShelf());
400
401 // Move it back.
402 w1->SetBounds(w1_bounds);
403 EXPECT_FALSE(GetWindowOverlapsShelf());
404
405 // Maximize again.
406 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
407 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
408 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
409
410 // Minimize.
411 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
412 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
413
414 // Since the restore from minimize will restore to the pre-minimize
415 // state (tested elsewhere), we abandon the current size and restore
416 // rect and set them to the window.
417 wm::WindowState* window_state = wm::GetWindowState(w1.get());
418
419 gfx::Rect restore = window_state->GetRestoreBoundsInScreen();
420 EXPECT_EQ("0,0 800x597", w1->bounds().ToString());
421 EXPECT_EQ("0,1 101x102", restore.ToString());
422 window_state->ClearRestoreBounds();
423 w1->SetBounds(restore);
424
425 // Restore.
426 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
427 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
428 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
429
430 // Create another window, maximized.
431 scoped_ptr<Window> w2(CreateTestWindow());
432 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
433 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
434 w2->Show();
435 wm::ActivateWindow(w2.get());
436 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
437 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
438 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
439
440 // Switch to w1.
441 wm::ActivateWindow(w1.get());
442 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
443 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
444 EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(
445 w2->parent()).ToString(),
446 w2->bounds().ToString());
447
448 // Switch to w2.
449 wm::ActivateWindow(w2.get());
450 EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state());
451 EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
452 EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
453 EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w2.get()).ToString(),
454 w2->bounds().ToString());
455
456 // Turn off auto-hide, switch back to w2 (maximized) and verify overlap.
457 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
458 wm::ActivateWindow(w2.get());
459 EXPECT_FALSE(GetWindowOverlapsShelf());
460
461 // Move w1 to overlap shelf, it shouldn't change window overlaps shelf since
462 // the window isn't in the visible workspace.
463 w1->SetBounds(touches_shelf_bounds);
464 EXPECT_FALSE(GetWindowOverlapsShelf());
465
466 // Activate w1. Although w1 is visible, the overlap state is still false since
467 // w2 is maximized.
468 wm::ActivateWindow(w1.get());
469 EXPECT_FALSE(GetWindowOverlapsShelf());
470
471 // Restore w2.
472 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
473 EXPECT_TRUE(GetWindowOverlapsShelf());
474 }
475
476 // Verifies going from maximized to minimized sets the right state for painting
477 // the background of the launcher.
TEST_F(WorkspaceControllerTest,MinimizeResetsVisibility)478 TEST_F(WorkspaceControllerTest, MinimizeResetsVisibility) {
479 scoped_ptr<Window> w1(CreateTestWindow());
480 w1->Show();
481 wm::ActivateWindow(w1.get());
482 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
483 EXPECT_EQ(SHELF_BACKGROUND_MAXIMIZED, shelf_widget()->GetBackgroundType());
484
485 w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
486 EXPECT_EQ(SHELF_VISIBLE,
487 shelf_layout_manager()->visibility_state());
488 EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, shelf_widget()->GetBackgroundType());
489 }
490
491 // Verifies window visibility during various workspace changes.
TEST_F(WorkspaceControllerTest,VisibilityTests)492 TEST_F(WorkspaceControllerTest, VisibilityTests) {
493 scoped_ptr<Window> w1(CreateTestWindow());
494 w1->Show();
495 EXPECT_TRUE(w1->IsVisible());
496 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
497
498 // Create another window, activate it and make it fullscreen.
499 scoped_ptr<Window> w2(CreateTestWindow());
500 w2->Show();
501 wm::ActivateWindow(w2.get());
502 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
503 EXPECT_TRUE(w2->IsVisible());
504 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
505 EXPECT_TRUE(w1->IsVisible());
506
507 // Switch to w1. |w1| should be visible on top of |w2|.
508 wm::ActivateWindow(w1.get());
509 EXPECT_TRUE(w1->IsVisible());
510 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
511 EXPECT_TRUE(w2->IsVisible());
512
513 // Switch back to |w2|.
514 wm::ActivateWindow(w2.get());
515 EXPECT_TRUE(w2->IsVisible());
516 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
517 EXPECT_TRUE(w1->IsVisible());
518
519 // Restore |w2|, both windows should be visible.
520 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
521 EXPECT_TRUE(w1->IsVisible());
522 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
523 EXPECT_TRUE(w2->IsVisible());
524 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
525
526 // Make |w2| fullscreen again, then close it.
527 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
528 w2->Hide();
529 EXPECT_FALSE(w2->IsVisible());
530 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
531 EXPECT_TRUE(w1->IsVisible());
532
533 // Create |w2| and maximize it.
534 w2.reset(CreateTestWindow());
535 w2->Show();
536 wm::ActivateWindow(w2.get());
537 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
538 EXPECT_TRUE(w2->IsVisible());
539 EXPECT_EQ(1.0f, w2->layer()->GetCombinedOpacity());
540 EXPECT_TRUE(w1->IsVisible());
541
542 // Close |w2|.
543 w2.reset();
544 EXPECT_EQ(1.0f, w1->layer()->GetCombinedOpacity());
545 EXPECT_TRUE(w1->IsVisible());
546 }
547
548 // Verifies windows that are offscreen don't move when switching workspaces.
TEST_F(WorkspaceControllerTest,DontMoveOnSwitch)549 TEST_F(WorkspaceControllerTest, DontMoveOnSwitch) {
550 aura::test::EventGenerator generator(
551 Shell::GetPrimaryRootWindow(), gfx::Point());
552 generator.MoveMouseTo(0, 0);
553
554 scoped_ptr<Window> w1(CreateTestWindow());
555 ShelfLayoutManager* shelf = shelf_layout_manager();
556 const gfx::Rect touches_shelf_bounds(
557 0, shelf->GetIdealBounds().y() - 10, 101, 102);
558 // Move |w1| to overlap the shelf.
559 w1->SetBounds(touches_shelf_bounds);
560 w1->Show();
561 wm::ActivateWindow(w1.get());
562
563 // Create another window and maximize it.
564 scoped_ptr<Window> w2(CreateTestWindow());
565 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
566 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
567 w2->Show();
568 wm::ActivateWindow(w2.get());
569
570 // Switch to w1.
571 wm::ActivateWindow(w1.get());
572 EXPECT_EQ(touches_shelf_bounds.ToString(), w1->bounds().ToString());
573 }
574
575 // Verifies that windows that are completely offscreen move when switching
576 // workspaces.
TEST_F(WorkspaceControllerTest,MoveOnSwitch)577 TEST_F(WorkspaceControllerTest, MoveOnSwitch) {
578 aura::test::EventGenerator generator(
579 Shell::GetPrimaryRootWindow(), gfx::Point());
580 generator.MoveMouseTo(0, 0);
581
582 scoped_ptr<Window> w1(CreateTestWindow());
583 ShelfLayoutManager* shelf = shelf_layout_manager();
584 const gfx::Rect w1_bounds(0, shelf->GetIdealBounds().y(), 100, 200);
585 // Move |w1| so that the top edge is the same as the top edge of the shelf.
586 w1->SetBounds(w1_bounds);
587 w1->Show();
588 wm::ActivateWindow(w1.get());
589 EXPECT_EQ(w1_bounds.ToString(), w1->bounds().ToString());
590
591 // Create another window and maximize it.
592 scoped_ptr<Window> w2(CreateTestWindow());
593 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
594 w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
595 w2->Show();
596 wm::ActivateWindow(w2.get());
597
598 // Increase the size of the WorkAreaInsets. This would make |w1| fall
599 // completely out of the display work area.
600 gfx::Insets insets =
601 Shell::GetScreen()->GetPrimaryDisplay().GetWorkAreaInsets();
602 insets.Set(0, 0, insets.bottom() + 30, 0);
603 Shell::GetInstance()->SetDisplayWorkAreaInsets(w1.get(), insets);
604
605 // Switch to w1. The window should have moved.
606 wm::ActivateWindow(w1.get());
607 EXPECT_NE(w1_bounds.ToString(), w1->bounds().ToString());
608 }
609
610 namespace {
611
612 // WindowDelegate used by DontCrashOnChangeAndActivate.
613 class DontCrashOnChangeAndActivateDelegate
614 : public aura::test::TestWindowDelegate {
615 public:
DontCrashOnChangeAndActivateDelegate()616 DontCrashOnChangeAndActivateDelegate() : window_(NULL) {}
617
set_window(aura::Window * window)618 void set_window(aura::Window* window) { window_ = window; }
619
620 // WindowDelegate overrides:
OnBoundsChanged(const gfx::Rect & old_bounds,const gfx::Rect & new_bounds)621 virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
622 const gfx::Rect& new_bounds) OVERRIDE {
623 if (window_) {
624 wm::ActivateWindow(window_);
625 window_ = NULL;
626 }
627 }
628
629 private:
630 aura::Window* window_;
631
632 DISALLOW_COPY_AND_ASSIGN(DontCrashOnChangeAndActivateDelegate);
633 };
634
635 } // namespace
636
637 // Exercises possible crash in W2. Here's the sequence:
638 // . minimize a maximized window.
639 // . remove the window (which happens when switching displays).
640 // . add the window back.
641 // . show the window and during the bounds change activate it.
TEST_F(WorkspaceControllerTest,DontCrashOnChangeAndActivate)642 TEST_F(WorkspaceControllerTest, DontCrashOnChangeAndActivate) {
643 // Force the shelf
644 ShelfLayoutManager* shelf = shelf_layout_manager();
645 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
646
647 DontCrashOnChangeAndActivateDelegate delegate;
648 scoped_ptr<Window> w1(CreateTestWindowInShellWithDelegate(
649 &delegate, 1000, gfx::Rect(10, 11, 250, 251)));
650
651 w1->Show();
652 wm::WindowState* w1_state = wm::GetWindowState(w1.get());
653 w1_state->Activate();
654 w1_state->Maximize();
655 w1_state->Minimize();
656
657 w1->parent()->RemoveChild(w1.get());
658
659 // Do this so that when we Show() the window a resize occurs and we make the
660 // window active.
661 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
662
663 ParentWindowInPrimaryRootWindow(w1.get());
664 delegate.set_window(w1.get());
665 w1->Show();
666 }
667
668 // Verifies a window with a transient parent not managed by workspace works.
TEST_F(WorkspaceControllerTest,TransientParent)669 TEST_F(WorkspaceControllerTest, TransientParent) {
670 // Normal window with no transient parent.
671 scoped_ptr<Window> w2(CreateTestWindow());
672 w2->SetBounds(gfx::Rect(10, 11, 250, 251));
673 w2->Show();
674 wm::ActivateWindow(w2.get());
675
676 // Window with a transient parent. We set the transient parent to the root,
677 // which would never happen but is enough to exercise the bug.
678 scoped_ptr<Window> w1(CreateTestWindowUnparented());
679 Shell::GetInstance()->GetPrimaryRootWindow()->AddTransientChild(w1.get());
680 w1->SetBounds(gfx::Rect(10, 11, 250, 251));
681 ParentWindowInPrimaryRootWindow(w1.get());
682 w1->Show();
683 wm::ActivateWindow(w1.get());
684
685 // The window with the transient parent should get added to the same parent as
686 // the normal window.
687 EXPECT_EQ(w2->parent(), w1->parent());
688 }
689
690 // Test the placement of newly created windows.
TEST_F(WorkspaceControllerTest,BasicAutoPlacingOnCreate)691 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnCreate) {
692 if (!SupportsHostWindowResize())
693 return;
694 UpdateDisplay("1600x1200");
695 // Creating a popup handler here to make sure it does not interfere with the
696 // existing windows.
697 gfx::Rect source_browser_bounds(16, 32, 640, 320);
698 scoped_ptr<aura::Window> browser_window(CreateBrowserLikeWindow(
699 source_browser_bounds));
700
701 // Creating a popup to make sure it does not interfere with the positioning.
702 scoped_ptr<aura::Window> browser_popup(CreatePopupLikeWindow(
703 gfx::Rect(16, 32, 128, 256)));
704
705 browser_window->Show();
706 browser_popup->Show();
707
708 { // With a shown window it's size should get returned.
709 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
710 source_browser_bounds));
711 // The position should be right flush.
712 EXPECT_EQ("960,32 640x320", new_browser_window->bounds().ToString());
713 }
714
715 { // With the window shown - but more on the right side then on the left
716 // side (and partially out of the screen), it should default to the other
717 // side and inside the screen.
718 gfx::Rect source_browser_bounds(gfx::Rect(1000, 600, 640, 320));
719 browser_window->SetBounds(source_browser_bounds);
720
721 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
722 source_browser_bounds));
723 // The position should be left & bottom flush.
724 EXPECT_EQ("0,600 640x320", new_browser_window->bounds().ToString());
725
726 // If the other window was already beyond the point to get right flush
727 // it will remain where it is.
728 EXPECT_EQ("1000,600 640x320", browser_window->bounds().ToString());
729 }
730
731 { // Make sure that popups do not get changed.
732 scoped_ptr<aura::Window> new_popup_window(CreatePopupLikeWindow(
733 gfx::Rect(50, 100, 300, 150)));
734 EXPECT_EQ("50,100 300x150", new_popup_window->bounds().ToString());
735 }
736
737 browser_window->Hide();
738 { // If a window is there but not shown the default should be centered.
739 scoped_ptr<aura::Window> new_browser_window(CreateBrowserLikeWindow(
740 gfx::Rect(50, 100, 300, 150)));
741 EXPECT_EQ("650,100 300x150", new_browser_window->bounds().ToString());
742 }
743 }
744
745 // Test the basic auto placement of one and or two windows in a "simulated
746 // session" of sequential window operations.
TEST_F(WorkspaceControllerTest,BasicAutoPlacingOnShowHide)747 TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnShowHide) {
748 // Test 1: In case there is no manageable window, no window should shift.
749
750 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
751 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
752 gfx::Rect desktop_area = window1->parent()->bounds();
753
754 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
755 // Trigger the auto window placement function by making it visible.
756 // Note that the bounds are getting changed while it is invisible.
757 window2->Hide();
758 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
759 window2->Show();
760
761 // Check the initial position of the windows is unchanged.
762 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
763 EXPECT_EQ("32,48 256x512", window2->bounds().ToString());
764
765 // Remove the second window and make sure that the first window
766 // does NOT get centered.
767 window2.reset();
768 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
769
770 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
771 // Test 2: Set up two managed windows and check their auto positioning.
772 window1_state->set_window_position_managed(true);
773
774 scoped_ptr<aura::Window> window3(CreateTestWindowInShellWithId(2));
775 wm::GetWindowState(window3.get())->set_window_position_managed(true);
776 // To avoid any auto window manager changes due to SetBounds, the window
777 // gets first hidden and then shown again.
778 window3->Hide();
779 window3->SetBounds(gfx::Rect(32, 48, 256, 512));
780 window3->Show();
781 // |window1| should be flush left and |window3| flush right.
782 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
783 EXPECT_EQ(base::IntToString(
784 desktop_area.width() - window3->bounds().width()) +
785 ",48 256x512", window3->bounds().ToString());
786
787 // After removing |window3|, |window1| should be centered again.
788 window3.reset();
789 EXPECT_EQ(
790 base::IntToString(
791 (desktop_area.width() - window1->bounds().width()) / 2) +
792 ",32 640x320", window1->bounds().ToString());
793
794 // Test 3: Set up a manageable and a non manageable window and check
795 // positioning.
796 scoped_ptr<aura::Window> window4(CreateTestWindowInShellWithId(3));
797 // To avoid any auto window manager changes due to SetBounds, the window
798 // gets first hidden and then shown again.
799 window1->Hide();
800 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
801 window4->SetBounds(gfx::Rect(32, 48, 256, 512));
802 window1->Show();
803 // |window1| should be centered and |window4| untouched.
804 EXPECT_EQ(
805 base::IntToString(
806 (desktop_area.width() - window1->bounds().width()) / 2) +
807 ",32 640x320", window1->bounds().ToString());
808 EXPECT_EQ("32,48 256x512", window4->bounds().ToString());
809
810 // Test4: A single manageable window should get centered.
811 window4.reset();
812 window1_state->set_bounds_changed_by_user(false);
813 // Trigger the auto window placement function by showing (and hiding) it.
814 window1->Hide();
815 window1->Show();
816 // |window1| should be centered.
817 EXPECT_EQ(
818 base::IntToString(
819 (desktop_area.width() - window1->bounds().width()) / 2) +
820 ",32 640x320", window1->bounds().ToString());
821 }
822
823 // Test the proper usage of user window movement interaction.
TEST_F(WorkspaceControllerTest,TestUserMovedWindowRepositioning)824 TEST_F(WorkspaceControllerTest, TestUserMovedWindowRepositioning) {
825 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
826 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
827 gfx::Rect desktop_area = window1->parent()->bounds();
828 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
829 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
830 window1->Hide();
831 window2->Hide();
832 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
833 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
834
835 window1_state->set_window_position_managed(true);
836 window2_state->set_window_position_managed(true);
837 EXPECT_FALSE(window1_state->bounds_changed_by_user());
838 EXPECT_FALSE(window2_state->bounds_changed_by_user());
839
840 // Check that the current location gets preserved if the user has
841 // positioned it previously.
842 window1_state->set_bounds_changed_by_user(true);
843 window1->Show();
844 EXPECT_EQ("16,32 640x320", window1->bounds().ToString());
845 // Flag should be still set.
846 EXPECT_TRUE(window1_state->bounds_changed_by_user());
847 EXPECT_FALSE(window2_state->bounds_changed_by_user());
848
849 // Turn on the second window and make sure that both windows are now
850 // positionable again (user movement cleared).
851 window2->Show();
852
853 // |window1| should be flush left and |window2| flush right.
854 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
855 EXPECT_EQ(
856 base::IntToString(desktop_area.width() - window2->bounds().width()) +
857 ",48 256x512", window2->bounds().ToString());
858 // FLag should now be reset.
859 EXPECT_FALSE(window1_state->bounds_changed_by_user());
860 EXPECT_FALSE(window2_state->bounds_changed_by_user());
861
862 // Going back to one shown window should keep the state.
863 window1_state->set_bounds_changed_by_user(true);
864 window2->Hide();
865 EXPECT_EQ("0,32 640x320", window1->bounds().ToString());
866 EXPECT_TRUE(window1_state->bounds_changed_by_user());
867 }
868
869 // Test if the single window will be restored at original position.
TEST_F(WorkspaceControllerTest,TestSingleWindowsRestoredBounds)870 TEST_F(WorkspaceControllerTest, TestSingleWindowsRestoredBounds) {
871 scoped_ptr<aura::Window> window1(
872 CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
873 scoped_ptr<aura::Window> window2(
874 CreateTestWindowInShellWithBounds(gfx::Rect(110, 110, 100, 100)));
875 scoped_ptr<aura::Window> window3(
876 CreateTestWindowInShellWithBounds(gfx::Rect(120, 120, 100, 100)));
877 window1->Hide();
878 window2->Hide();
879 window3->Hide();
880 wm::GetWindowState(window1.get())->set_window_position_managed(true);
881 wm::GetWindowState(window2.get())->set_window_position_managed(true);
882 wm::GetWindowState(window3.get())->set_window_position_managed(true);
883
884 window1->Show();
885 wm::ActivateWindow(window1.get());
886 window2->Show();
887 wm::ActivateWindow(window2.get());
888 window3->Show();
889 wm::ActivateWindow(window3.get());
890 EXPECT_EQ(0, window1->bounds().x());
891 EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
892 window2->bounds().right());
893 EXPECT_EQ(0, window3->bounds().x());
894
895 window1->Hide();
896 EXPECT_EQ(window2->GetRootWindow()->bounds().right(),
897 window2->bounds().right());
898 EXPECT_EQ(0, window3->bounds().x());
899
900 // Being a single window will retore the original location.
901 window3->Hide();
902 wm::ActivateWindow(window2.get());
903 EXPECT_EQ("110,110 100x100", window2->bounds().ToString());
904
905 // Showing the 3rd will push the 2nd window left.
906 window3->Show();
907 wm::ActivateWindow(window3.get());
908 EXPECT_EQ(0, window2->bounds().x());
909 EXPECT_EQ(window3->GetRootWindow()->bounds().right(),
910 window3->bounds().right());
911
912 // Being a single window will retore the original location.
913 window2->Hide();
914 EXPECT_EQ("120,120 100x100", window3->bounds().ToString());
915 }
916
917 // Test that user placed windows go back to their user placement after the user
918 // closes all other windows.
TEST_F(WorkspaceControllerTest,TestUserHandledWindowRestore)919 TEST_F(WorkspaceControllerTest, TestUserHandledWindowRestore) {
920 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
921 gfx::Rect user_pos = gfx::Rect(16, 42, 640, 320);
922 window1->SetBounds(user_pos);
923 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
924
925 window1_state->SetPreAutoManageWindowBounds(user_pos);
926 gfx::Rect desktop_area = window1->parent()->bounds();
927
928 // Create a second window to let the auto manager kick in.
929 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
930 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
931 window1->Hide();
932 window2->Hide();
933 wm::GetWindowState(window1.get())->set_window_position_managed(true);
934 wm::GetWindowState(window2.get())->set_window_position_managed(true);
935 window1->Show();
936 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
937 window2->Show();
938
939 // |window1| should be flush left and |window2| flush right.
940 EXPECT_EQ("0," + base::IntToString(user_pos.y()) +
941 " 640x320", window1->bounds().ToString());
942 EXPECT_EQ(
943 base::IntToString(desktop_area.width() - window2->bounds().width()) +
944 ",48 256x512", window2->bounds().ToString());
945 window2->Hide();
946
947 // After the other window get hidden the window has to move back to the
948 // previous position and the bounds should still be set and unchanged.
949 EXPECT_EQ(user_pos.ToString(), window1->bounds().ToString());
950 ASSERT_TRUE(window1_state->pre_auto_manage_window_bounds());
951 EXPECT_EQ(user_pos.ToString(),
952 window1_state->pre_auto_manage_window_bounds()->ToString());
953 }
954
955 // Test that a window from normal to minimize will repos the remaining.
TEST_F(WorkspaceControllerTest,ToMinimizeRepositionsRemaining)956 TEST_F(WorkspaceControllerTest, ToMinimizeRepositionsRemaining) {
957 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
958 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
959 window1_state->set_window_position_managed(true);
960 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
961 gfx::Rect desktop_area = window1->parent()->bounds();
962
963 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
964 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
965 window2_state->set_window_position_managed(true);
966 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
967
968 window1_state->Minimize();
969
970 // |window2| should be centered now.
971 EXPECT_TRUE(window2->IsVisible());
972 EXPECT_TRUE(window2_state->IsNormalShowState());
973 EXPECT_EQ(base::IntToString(
974 (desktop_area.width() - window2->bounds().width()) / 2) +
975 ",48 256x512", window2->bounds().ToString());
976
977 window1_state->Restore();
978 // |window1| should be flush right and |window3| flush left.
979 EXPECT_EQ(base::IntToString(
980 desktop_area.width() - window1->bounds().width()) +
981 ",32 640x320", window1->bounds().ToString());
982 EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
983 }
984
985 // Test that minimizing an initially maximized window will repos the remaining.
TEST_F(WorkspaceControllerTest,MaxToMinRepositionsRemaining)986 TEST_F(WorkspaceControllerTest, MaxToMinRepositionsRemaining) {
987 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
988 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
989 window1_state->set_window_position_managed(true);
990 gfx::Rect desktop_area = window1->parent()->bounds();
991
992 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
993 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
994 window2_state->set_window_position_managed(true);
995 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
996
997 window1_state->Maximize();
998 window1_state->Minimize();
999
1000 // |window2| should be centered now.
1001 EXPECT_TRUE(window2->IsVisible());
1002 EXPECT_TRUE(window2_state->IsNormalShowState());
1003 EXPECT_EQ(base::IntToString(
1004 (desktop_area.width() - window2->bounds().width()) / 2) +
1005 ",48 256x512", window2->bounds().ToString());
1006 }
1007
1008 // Test that nomral, maximize, minimizing will repos the remaining.
TEST_F(WorkspaceControllerTest,NormToMaxToMinRepositionsRemaining)1009 TEST_F(WorkspaceControllerTest, NormToMaxToMinRepositionsRemaining) {
1010 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1011 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1012 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1013 window1_state->set_window_position_managed(true);
1014 gfx::Rect desktop_area = window1->parent()->bounds();
1015
1016 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1017 wm::WindowState* window2_state = wm::GetWindowState(window2.get());
1018 window2_state->set_window_position_managed(true);
1019 window2->SetBounds(gfx::Rect(32, 40, 256, 512));
1020
1021 // Trigger the auto window placement function by showing (and hiding) it.
1022 window1->Hide();
1023 window1->Show();
1024
1025 // |window1| should be flush right and |window3| flush left.
1026 EXPECT_EQ(base::IntToString(
1027 desktop_area.width() - window1->bounds().width()) +
1028 ",32 640x320", window1->bounds().ToString());
1029 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1030
1031 window1_state->Maximize();
1032 window1_state->Minimize();
1033
1034 // |window2| should be centered now.
1035 EXPECT_TRUE(window2->IsVisible());
1036 EXPECT_TRUE(window2_state->IsNormalShowState());
1037 EXPECT_EQ(base::IntToString(
1038 (desktop_area.width() - window2->bounds().width()) / 2) +
1039 ",40 256x512", window2->bounds().ToString());
1040 }
1041
1042 // Test that nomral, maximize, normal will repos the remaining.
TEST_F(WorkspaceControllerTest,NormToMaxToNormRepositionsRemaining)1043 TEST_F(WorkspaceControllerTest, NormToMaxToNormRepositionsRemaining) {
1044 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1045 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1046 wm::WindowState* window1_state = wm::GetWindowState(window1.get());
1047 window1_state->set_window_position_managed(true);
1048 gfx::Rect desktop_area = window1->parent()->bounds();
1049
1050 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1051 wm::GetWindowState(window2.get())->set_window_position_managed(true);
1052 window2->SetBounds(gfx::Rect(32, 40, 256, 512));
1053
1054 // Trigger the auto window placement function by showing (and hiding) it.
1055 window1->Hide();
1056 window1->Show();
1057
1058 // |window1| should be flush right and |window3| flush left.
1059 EXPECT_EQ(base::IntToString(
1060 desktop_area.width() - window1->bounds().width()) +
1061 ",32 640x320", window1->bounds().ToString());
1062 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1063
1064 window1_state->Maximize();
1065 window1_state->Restore();
1066
1067 // |window1| should be flush right and |window2| flush left.
1068 EXPECT_EQ(base::IntToString(
1069 desktop_area.width() - window1->bounds().width()) +
1070 ",32 640x320", window1->bounds().ToString());
1071 EXPECT_EQ("0,40 256x512", window2->bounds().ToString());
1072 }
1073
1074 // Test that animations are triggered.
TEST_F(WorkspaceControllerTest,AnimatedNormToMaxToNormRepositionsRemaining)1075 TEST_F(WorkspaceControllerTest, AnimatedNormToMaxToNormRepositionsRemaining) {
1076 ui::ScopedAnimationDurationScaleMode normal_duration_mode(
1077 ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
1078 scoped_ptr<aura::Window> window1(CreateTestWindowInShellWithId(0));
1079 window1->Hide();
1080 window1->SetBounds(gfx::Rect(16, 32, 640, 320));
1081 gfx::Rect desktop_area = window1->parent()->bounds();
1082 scoped_ptr<aura::Window> window2(CreateTestWindowInShellWithId(1));
1083 window2->Hide();
1084 window2->SetBounds(gfx::Rect(32, 48, 256, 512));
1085
1086 wm::GetWindowState(window1.get())->set_window_position_managed(true);
1087 wm::GetWindowState(window2.get())->set_window_position_managed(true);
1088 // Make sure nothing is animating.
1089 window1->layer()->GetAnimator()->StopAnimating();
1090 window2->layer()->GetAnimator()->StopAnimating();
1091 window2->Show();
1092
1093 // The second window should now animate.
1094 EXPECT_FALSE(window1->layer()->GetAnimator()->is_animating());
1095 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1096 window2->layer()->GetAnimator()->StopAnimating();
1097
1098 window1->Show();
1099 EXPECT_TRUE(window1->layer()->GetAnimator()->is_animating());
1100 EXPECT_TRUE(window2->layer()->GetAnimator()->is_animating());
1101
1102 window1->layer()->GetAnimator()->StopAnimating();
1103 window2->layer()->GetAnimator()->StopAnimating();
1104 // |window1| should be flush right and |window2| flush left.
1105 EXPECT_EQ(base::IntToString(
1106 desktop_area.width() - window1->bounds().width()) +
1107 ",32 640x320", window1->bounds().ToString());
1108 EXPECT_EQ("0,48 256x512", window2->bounds().ToString());
1109 }
1110
1111 // This tests simulates a browser and an app and verifies the ordering of the
1112 // windows and layers doesn't get out of sync as various operations occur. Its
1113 // really testing code in FocusController, but easier to simulate here. Just as
1114 // with a real browser the browser here has a transient child window
1115 // (corresponds to the status bubble).
TEST_F(WorkspaceControllerTest,VerifyLayerOrdering)1116 TEST_F(WorkspaceControllerTest, VerifyLayerOrdering) {
1117 scoped_ptr<Window> browser(
1118 aura::test::CreateTestWindowWithDelegate(
1119 NULL,
1120 aura::client::WINDOW_TYPE_NORMAL,
1121 gfx::Rect(5, 6, 7, 8),
1122 NULL));
1123 browser->SetName("browser");
1124 ParentWindowInPrimaryRootWindow(browser.get());
1125 browser->Show();
1126 wm::ActivateWindow(browser.get());
1127
1128 // |status_bubble| is made a transient child of |browser| and as a result
1129 // owned by |browser|.
1130 aura::test::TestWindowDelegate* status_bubble_delegate =
1131 aura::test::TestWindowDelegate::CreateSelfDestroyingDelegate();
1132 status_bubble_delegate->set_can_focus(false);
1133 Window* status_bubble =
1134 aura::test::CreateTestWindowWithDelegate(
1135 status_bubble_delegate,
1136 aura::client::WINDOW_TYPE_POPUP,
1137 gfx::Rect(5, 6, 7, 8),
1138 NULL);
1139 browser->AddTransientChild(status_bubble);
1140 ParentWindowInPrimaryRootWindow(status_bubble);
1141 status_bubble->SetName("status_bubble");
1142
1143 scoped_ptr<Window> app(
1144 aura::test::CreateTestWindowWithDelegate(
1145 NULL,
1146 aura::client::WINDOW_TYPE_NORMAL,
1147 gfx::Rect(5, 6, 7, 8),
1148 NULL));
1149 app->SetName("app");
1150 ParentWindowInPrimaryRootWindow(app.get());
1151
1152 aura::Window* parent = browser->parent();
1153
1154 app->Show();
1155 wm::ActivateWindow(app.get());
1156 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1157
1158 // Minimize the app, focus should go the browser.
1159 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
1160 EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
1161 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1162
1163 // Minimize the browser (neither windows are focused).
1164 browser->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
1165 EXPECT_FALSE(wm::IsActiveWindow(browser.get()));
1166 EXPECT_FALSE(wm::IsActiveWindow(app.get()));
1167 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1168
1169 // Show the browser (which should restore it).
1170 browser->Show();
1171 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1172
1173 // Activate the browser.
1174 ash::wm::ActivateWindow(browser.get());
1175 EXPECT_TRUE(wm::IsActiveWindow(browser.get()));
1176 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1177
1178 // Restore the app. This differs from above code for |browser| as internally
1179 // the app code does this. Restoring this way or using Show() should not make
1180 // a difference.
1181 app->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
1182 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1183
1184 // Activate the app.
1185 ash::wm::ActivateWindow(app.get());
1186 EXPECT_TRUE(wm::IsActiveWindow(app.get()));
1187 EXPECT_EQ(GetWindowNames(parent), GetLayerNames(parent));
1188 }
1189
1190 namespace {
1191
1192 // Used by DragMaximizedNonTrackedWindow to track how many times the window
1193 // hierarchy changes affecting the specified window.
1194 class DragMaximizedNonTrackedWindowObserver
1195 : public aura::WindowObserver {
1196 public:
DragMaximizedNonTrackedWindowObserver(aura::Window * window)1197 DragMaximizedNonTrackedWindowObserver(aura::Window* window)
1198 : change_count_(0),
1199 window_(window) {
1200 }
1201
1202 // Number of times OnWindowHierarchyChanged() has been received.
clear_change_count()1203 void clear_change_count() { change_count_ = 0; }
change_count() const1204 int change_count() const {
1205 return change_count_;
1206 }
1207
1208 // aura::WindowObserver overrides:
1209 // Counts number of times a window is reparented. Ignores reparenting into and
1210 // from a docked container which is expected when a tab is dragged.
OnWindowHierarchyChanged(const HierarchyChangeParams & params)1211 virtual void OnWindowHierarchyChanged(
1212 const HierarchyChangeParams& params) OVERRIDE {
1213 if (params.target != window_ ||
1214 (params.old_parent->id() == kShellWindowId_DefaultContainer &&
1215 params.new_parent->id() == kShellWindowId_DockedContainer) ||
1216 (params.old_parent->id() == kShellWindowId_DockedContainer &&
1217 params.new_parent->id() == kShellWindowId_DefaultContainer)) {
1218 return;
1219 }
1220 change_count_++;
1221 }
1222
1223 private:
1224 int change_count_;
1225 aura::Window* window_;
1226
1227 DISALLOW_COPY_AND_ASSIGN(DragMaximizedNonTrackedWindowObserver);
1228 };
1229
1230 } // namespace
1231
1232 // Verifies that a new maximized window becomes visible after its activation
1233 // is requested, even though it does not become activated because a system
1234 // modal window is active.
TEST_F(WorkspaceControllerTest,SwitchFromModal)1235 TEST_F(WorkspaceControllerTest, SwitchFromModal) {
1236 scoped_ptr<Window> modal_window(CreateTestWindowUnparented());
1237 modal_window->SetBounds(gfx::Rect(10, 11, 21, 22));
1238 modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
1239 ParentWindowInPrimaryRootWindow(modal_window.get());
1240 modal_window->Show();
1241 wm::ActivateWindow(modal_window.get());
1242
1243 scoped_ptr<Window> maximized_window(CreateTestWindow());
1244 maximized_window->SetProperty(
1245 aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
1246 maximized_window->Show();
1247 wm::ActivateWindow(maximized_window.get());
1248 EXPECT_TRUE(maximized_window->IsVisible());
1249 }
1250
1251 namespace {
1252
1253 // Subclass of WorkspaceControllerTest that runs tests with docked windows
1254 // enabled and disabled.
1255 class WorkspaceControllerTestDragging
1256 : public WorkspaceControllerTest,
1257 public testing::WithParamInterface<bool> {
1258 public:
WorkspaceControllerTestDragging()1259 WorkspaceControllerTestDragging() {}
~WorkspaceControllerTestDragging()1260 virtual ~WorkspaceControllerTestDragging() {}
1261
1262 // testing::Test:
SetUp()1263 virtual void SetUp() OVERRIDE {
1264 WorkspaceControllerTest::SetUp();
1265 if (docked_windows_enabled()) {
1266 CommandLine::ForCurrentProcess()->AppendSwitch(
1267 ash::switches::kAshEnableDockedWindows);
1268 }
1269 }
1270
docked_windows_enabled() const1271 bool docked_windows_enabled() const { return GetParam(); }
1272
1273 private:
1274 DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTestDragging);
1275 };
1276
1277 } // namespace
1278
1279 // Verifies that when dragging a window over the shelf overlap is detected
1280 // during and after the drag.
TEST_P(WorkspaceControllerTestDragging,DragWindowOverlapShelf)1281 TEST_P(WorkspaceControllerTestDragging, DragWindowOverlapShelf) {
1282 aura::test::TestWindowDelegate delegate;
1283 delegate.set_window_component(HTCAPTION);
1284 scoped_ptr<Window> w1(
1285 aura::test::CreateTestWindowWithDelegate(&delegate,
1286 aura::client::WINDOW_TYPE_NORMAL,
1287 gfx::Rect(5, 5, 100, 50),
1288 NULL));
1289 ParentWindowInPrimaryRootWindow(w1.get());
1290
1291 ShelfLayoutManager* shelf = shelf_layout_manager();
1292 shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
1293
1294 // Drag near the shelf
1295 aura::test::EventGenerator generator(
1296 Shell::GetPrimaryRootWindow(), gfx::Point());
1297 generator.MoveMouseTo(10, 10);
1298 generator.PressLeftButton();
1299 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 70);
1300
1301 // Shelf should not be in overlapped state.
1302 EXPECT_FALSE(GetWindowOverlapsShelf());
1303
1304 generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 20);
1305
1306 // Shelf should detect overlap. Overlap state stays after mouse is released.
1307 EXPECT_TRUE(GetWindowOverlapsShelf());
1308 generator.ReleaseLeftButton();
1309 EXPECT_TRUE(GetWindowOverlapsShelf());
1310 }
1311
1312 INSTANTIATE_TEST_CASE_P(DockedOrNot, WorkspaceControllerTestDragging,
1313 ::testing::Bool());
1314
1315 } // namespace internal
1316 } // namespace ash
1317