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