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
37using aura::Window;
38
39namespace ash {
40namespace 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.
44std::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.
58std::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
80class WorkspaceControllerTest : public test::AshTestBase {
81 public:
82  WorkspaceControllerTest() {}
83  virtual ~WorkspaceControllerTest() {}
84
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
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
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
111  aura::Window* CreatePopupLikeWindow(const gfx::Rect& bounds) {
112    aura::Window* window = CreateTestWindowInShellWithBounds(bounds);
113    window->Show();
114    return window;
115  }
116
117  aura::Window* GetDesktop() {
118    return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
119                               kShellWindowId_DefaultContainer);
120  }
121
122  gfx::Rect GetFullscreenBounds(aura::Window* window) {
123    return Shell::GetScreen()->GetDisplayNearestWindow(window).bounds();
124  }
125
126  ShelfWidget* shelf_widget() {
127    return Shell::GetPrimaryRootWindowController()->shelf();
128  }
129
130  ShelfLayoutManager* shelf_layout_manager() {
131    return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
132  }
133
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.
143TEST_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.
164TEST_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.
197TEST_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.
228TEST_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.
240TEST_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.
252TEST_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.
285TEST_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.
300TEST_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.
342TEST_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.
478TEST_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.
492TEST_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.
549TEST_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.
577TEST_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
610namespace {
611
612// WindowDelegate used by DontCrashOnChangeAndActivate.
613class DontCrashOnChangeAndActivateDelegate
614    : public aura::test::TestWindowDelegate {
615 public:
616  DontCrashOnChangeAndActivateDelegate() : window_(NULL) {}
617
618  void set_window(aura::Window* window) { window_ = window; }
619
620  // WindowDelegate overrides:
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.
642TEST_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.
669TEST_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.
691TEST_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.
747TEST_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.
824TEST_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.
870TEST_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.
919TEST_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.
956TEST_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.
986TEST_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.
1009TEST_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.
1043TEST_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.
1075TEST_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).
1116TEST_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
1190namespace {
1191
1192// Used by DragMaximizedNonTrackedWindow to track how many times the window
1193// hierarchy changes affecting the specified window.
1194class DragMaximizedNonTrackedWindowObserver
1195    : public aura::WindowObserver {
1196 public:
1197  DragMaximizedNonTrackedWindowObserver(aura::Window* window)
1198  : change_count_(0),
1199    window_(window) {
1200  }
1201
1202  // Number of times OnWindowHierarchyChanged() has been received.
1203  void clear_change_count() { change_count_ = 0; }
1204  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.
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.
1235TEST_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
1251namespace {
1252
1253// Subclass of WorkspaceControllerTest that runs tests with docked windows
1254// enabled and disabled.
1255class WorkspaceControllerTestDragging
1256    : public WorkspaceControllerTest,
1257      public testing::WithParamInterface<bool> {
1258 public:
1259  WorkspaceControllerTestDragging() {}
1260  virtual ~WorkspaceControllerTestDragging() {}
1261
1262  // testing::Test:
1263  virtual void SetUp() OVERRIDE {
1264    WorkspaceControllerTest::SetUp();
1265    if (docked_windows_enabled()) {
1266      CommandLine::ForCurrentProcess()->AppendSwitch(
1267          ash::switches::kAshEnableDockedWindows);
1268    }
1269  }
1270
1271  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.
1281TEST_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
1312INSTANTIATE_TEST_CASE_P(DockedOrNot, WorkspaceControllerTestDragging,
1313                        ::testing::Bool());
1314
1315}  // namespace internal
1316}  // namespace ash
1317