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