Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / ash / wm / workspace / workspace_layout_manager_unittest.cc
1 // Copyright (c) 2012 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/workspace_layout_manager.h"
6
7 #include "ash/display/display_layout.h"
8 #include "ash/display/display_manager.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/screen_util.h"
11 #include "ash/session_state_delegate.h"
12 #include "ash/shelf/shelf_layout_manager.h"
13 #include "ash/shell.h"
14 #include "ash/shell_observer.h"
15 #include "ash/shell_window_ids.h"
16 #include "ash/test/ash_test_base.h"
17 #include "ash/wm/maximize_mode/workspace_backdrop_delegate.h"
18 #include "ash/wm/window_state.h"
19 #include "ash/wm/window_util.h"
20 #include "ash/wm/wm_event.h"
21 #include "ash/wm/workspace/workspace_window_resizer.h"
22 #include "base/basictypes.h"
23 #include "base/compiler_specific.h"
24 #include "ui/aura/client/aura_constants.h"
25 #include "ui/aura/test/test_windows.h"
26 #include "ui/aura/window.h"
27 #include "ui/aura/window_event_dispatcher.h"
28 #include "ui/base/ui_base_types.h"
29 #include "ui/gfx/insets.h"
30 #include "ui/gfx/screen.h"
31 #include "ui/views/widget/widget.h"
32 #include "ui/views/widget/widget_delegate.h"
33 #include "ui/wm/core/window_util.h"
34
35 namespace ash {
36 namespace {
37
38 class MaximizeDelegateView : public views::WidgetDelegateView {
39  public:
40   MaximizeDelegateView(const gfx::Rect& initial_bounds)
41       : initial_bounds_(initial_bounds) {
42   }
43   virtual ~MaximizeDelegateView() {}
44
45   virtual bool GetSavedWindowPlacement(
46       const views::Widget* widget,
47       gfx::Rect* bounds,
48       ui::WindowShowState* show_state) const OVERRIDE {
49     *bounds = initial_bounds_;
50     *show_state = ui::SHOW_STATE_MAXIMIZED;
51     return true;
52   }
53
54  private:
55   const gfx::Rect initial_bounds_;
56
57   DISALLOW_COPY_AND_ASSIGN(MaximizeDelegateView);
58 };
59
60 class TestShellObserver : public ShellObserver {
61  public:
62   TestShellObserver() : call_count_(0),
63                         is_fullscreen_(false) {
64     Shell::GetInstance()->AddShellObserver(this);
65   }
66
67   virtual ~TestShellObserver() {
68     Shell::GetInstance()->RemoveShellObserver(this);
69   }
70
71   virtual void OnFullscreenStateChanged(bool is_fullscreen,
72                                         aura::Window* root_window) OVERRIDE {
73     call_count_++;
74     is_fullscreen_ = is_fullscreen;
75   }
76
77   int call_count() const {
78     return call_count_;
79   }
80
81   bool is_fullscreen() const {
82     return is_fullscreen_;
83   }
84
85  private:
86   int call_count_;
87   bool is_fullscreen_;
88
89   DISALLOW_COPY_AND_ASSIGN(TestShellObserver);
90 };
91
92 }  // namespace
93
94 typedef test::AshTestBase WorkspaceLayoutManagerTest;
95
96 // Verifies that a window containing a restore coordinate will be restored to
97 // to the size prior to minimize, keeping the restore rectangle in tact (if
98 // there is one).
99 TEST_F(WorkspaceLayoutManagerTest, RestoreFromMinimizeKeepsRestore) {
100   scoped_ptr<aura::Window> window(
101       CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 3, 4)));
102   gfx::Rect bounds(10, 15, 25, 35);
103   window->SetBounds(bounds);
104
105   wm::WindowState* window_state = wm::GetWindowState(window.get());
106
107   // This will not be used for un-minimizing window.
108   window_state->SetRestoreBoundsInScreen(gfx::Rect(0, 0, 100, 100));
109   window_state->Minimize();
110   window_state->Restore();
111   EXPECT_EQ("0,0 100x100", window_state->GetRestoreBoundsInScreen().ToString());
112   EXPECT_EQ("10,15 25x35", window.get()->bounds().ToString());
113
114   if (!SupportsMultipleDisplays())
115     return;
116
117   UpdateDisplay("400x300,500x400");
118   window->SetBoundsInScreen(gfx::Rect(600, 0, 100, 100),
119                             ScreenUtil::GetSecondaryDisplay());
120   EXPECT_EQ(Shell::GetAllRootWindows()[1], window->GetRootWindow());
121   window_state->Minimize();
122   // This will not be used for un-minimizing window.
123   window_state->SetRestoreBoundsInScreen(gfx::Rect(0, 0, 100, 100));
124   window_state->Restore();
125   EXPECT_EQ("600,0 100x100", window->GetBoundsInScreen().ToString());
126
127   // Make sure the unminimized window moves inside the display when
128   // 2nd display is disconnected.
129   window_state->Minimize();
130   UpdateDisplay("400x300");
131   window_state->Restore();
132   EXPECT_EQ(Shell::GetPrimaryRootWindow(), window->GetRootWindow());
133   EXPECT_TRUE(
134       Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
135 }
136
137 TEST_F(WorkspaceLayoutManagerTest, KeepMinimumVisibilityInDisplays) {
138   if (!SupportsMultipleDisplays())
139     return;
140
141   UpdateDisplay("300x400,400x500");
142   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
143
144   DisplayLayout layout(DisplayLayout::TOP, 0);
145   Shell::GetInstance()->display_manager()->
146       SetLayoutForCurrentDisplays(layout);
147   EXPECT_EQ("0,-500 400x500", root_windows[1]->GetBoundsInScreen().ToString());
148
149   scoped_ptr<aura::Window> window1(
150       CreateTestWindowInShellWithBounds(gfx::Rect(10, -400, 200, 200)));
151   EXPECT_EQ("10,-400 200x200", window1->GetBoundsInScreen().ToString());
152
153   // Make sure the caption is visible.
154   scoped_ptr<aura::Window> window2(
155       CreateTestWindowInShellWithBounds(gfx::Rect(10, -600, 200, 200)));
156   EXPECT_EQ("10,-500 200x200", window2->GetBoundsInScreen().ToString());
157 }
158
159 TEST_F(WorkspaceLayoutManagerTest, KeepRestoredWindowInDisplay) {
160   if (!SupportsHostWindowResize())
161     return;
162   scoped_ptr<aura::Window> window(
163       CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40)));
164   wm::WindowState* window_state = wm::GetWindowState(window.get());
165
166   // Maximized -> Normal transition.
167   window_state->Maximize();
168   window_state->SetRestoreBoundsInScreen(gfx::Rect(-100, -100, 30, 40));
169   window_state->Restore();
170   EXPECT_TRUE(
171       Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
172   // Y bounds should not be negative.
173   EXPECT_EQ("-20,0 30x40", window->bounds().ToString());
174
175   // Minimized -> Normal transition.
176   window->SetBounds(gfx::Rect(-100, -100, 30, 40));
177   window_state->Minimize();
178   EXPECT_FALSE(
179       Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
180   EXPECT_EQ("-100,-100 30x40", window->bounds().ToString());
181   window->Show();
182   EXPECT_TRUE(
183       Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
184   // Y bounds should not be negative.
185   EXPECT_EQ("-20,0 30x40", window->bounds().ToString());
186
187   // Fullscreen -> Normal transition.
188   window->SetBounds(gfx::Rect(0, 0, 30, 40));  // reset bounds.
189   ASSERT_EQ("0,0 30x40", window->bounds().ToString());
190   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
191   EXPECT_EQ(window->bounds(), window->GetRootWindow()->bounds());
192   window_state->SetRestoreBoundsInScreen(gfx::Rect(-100, -100, 30, 40));
193   window_state->Restore();
194   EXPECT_TRUE(
195       Shell::GetPrimaryRootWindow()->bounds().Intersects(window->bounds()));
196   // Y bounds should not be negative.
197   EXPECT_EQ("-20,0 30x40", window->bounds().ToString());
198 }
199
200 TEST_F(WorkspaceLayoutManagerTest, MaximizeInDisplayToBeRestored) {
201   if (!SupportsMultipleDisplays())
202     return;
203   UpdateDisplay("300x400,400x500");
204
205   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
206
207   scoped_ptr<aura::Window> window(
208       CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40)));
209   EXPECT_EQ(root_windows[0], window->GetRootWindow());
210
211   wm::WindowState* window_state = wm::GetWindowState(window.get());
212   window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40));
213   // Maximize the window in 2nd display as the restore bounds
214   // is inside 2nd display.
215   window_state->Maximize();
216   EXPECT_EQ(root_windows[1], window->GetRootWindow());
217   EXPECT_EQ("300,0 400x453", window->GetBoundsInScreen().ToString());
218
219   window_state->Restore();
220   EXPECT_EQ(root_windows[1], window->GetRootWindow());
221   EXPECT_EQ("400,0 30x40", window->GetBoundsInScreen().ToString());
222
223   // If the restore bounds intersects with the current display,
224   // don't move.
225   window_state->SetRestoreBoundsInScreen(gfx::Rect(280, 0, 30, 40));
226   window_state->Maximize();
227   EXPECT_EQ(root_windows[1], window->GetRootWindow());
228   EXPECT_EQ("300,0 400x453", window->GetBoundsInScreen().ToString());
229
230   window_state->Restore();
231   EXPECT_EQ(root_windows[1], window->GetRootWindow());
232   EXPECT_EQ("280,0 30x40", window->GetBoundsInScreen().ToString());
233
234   // Restoring widget state.
235   scoped_ptr<views::Widget> w1(new views::Widget);
236   views::Widget::InitParams params;
237   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
238   params.delegate = new MaximizeDelegateView(gfx::Rect(400, 0, 30, 40));
239   params.context = root_windows[0];
240   w1->Init(params);
241   w1->Show();
242   EXPECT_TRUE(w1->IsMaximized());
243   EXPECT_EQ(root_windows[1], w1->GetNativeView()->GetRootWindow());
244   EXPECT_EQ("300,0 400x453", w1->GetWindowBoundsInScreen().ToString());
245   w1->Restore();
246   EXPECT_EQ(root_windows[1], w1->GetNativeView()->GetRootWindow());
247   EXPECT_EQ("400,0 30x40", w1->GetWindowBoundsInScreen().ToString());
248 }
249
250 TEST_F(WorkspaceLayoutManagerTest, FullscreenInDisplayToBeRestored) {
251   if (!SupportsMultipleDisplays())
252     return;
253   UpdateDisplay("300x400,400x500");
254
255   aura::Window::Windows root_windows = Shell::GetAllRootWindows();
256
257   scoped_ptr<aura::Window> window(
258       CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40)));
259   EXPECT_EQ(root_windows[0], window->GetRootWindow());
260
261   wm::WindowState* window_state = wm::GetWindowState(window.get());
262   window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40));
263   // Maximize the window in 2nd display as the restore bounds
264   // is inside 2nd display.
265   window->SetProperty(aura::client::kShowStateKey,
266                       ui::SHOW_STATE_FULLSCREEN);
267   EXPECT_EQ(root_windows[1], window->GetRootWindow());
268   EXPECT_EQ("300,0 400x500", window->GetBoundsInScreen().ToString());
269
270   window_state->Restore();
271   EXPECT_EQ(root_windows[1], window->GetRootWindow());
272   EXPECT_EQ("400,0 30x40", window->GetBoundsInScreen().ToString());
273
274   // If the restore bounds intersects with the current display,
275   // don't move.
276   window_state->SetRestoreBoundsInScreen(gfx::Rect(280, 0, 30, 40));
277   window->SetProperty(aura::client::kShowStateKey,
278                       ui::SHOW_STATE_FULLSCREEN);
279   EXPECT_EQ(root_windows[1], window->GetRootWindow());
280   EXPECT_EQ("300,0 400x500", window->GetBoundsInScreen().ToString());
281
282   window_state->Restore();
283   EXPECT_EQ(root_windows[1], window->GetRootWindow());
284   EXPECT_EQ("280,0 30x40", window->GetBoundsInScreen().ToString());
285 }
286
287 // WindowObserver implementation used by DontClobberRestoreBoundsWindowObserver.
288 // This code mirrors what BrowserFrameAsh does. In particular when this code
289 // sees the window was maximized it changes the bounds of a secondary
290 // window. The secondary window mirrors the status window.
291 class DontClobberRestoreBoundsWindowObserver : public aura::WindowObserver {
292  public:
293   DontClobberRestoreBoundsWindowObserver() : window_(NULL) {}
294
295   void set_window(aura::Window* window) { window_ = window; }
296
297   virtual void OnWindowPropertyChanged(aura::Window* window,
298                                        const void* key,
299                                        intptr_t old) OVERRIDE {
300     if (!window_)
301       return;
302
303     if (wm::GetWindowState(window)->IsMaximized()) {
304       aura::Window* w = window_;
305       window_ = NULL;
306
307       gfx::Rect shelf_bounds(Shell::GetPrimaryRootWindowController()->
308                              GetShelfLayoutManager()->GetIdealBounds());
309       const gfx::Rect& window_bounds(w->bounds());
310       w->SetBounds(gfx::Rect(window_bounds.x(), shelf_bounds.y() - 1,
311                              window_bounds.width(), window_bounds.height()));
312     }
313   }
314
315  private:
316   aura::Window* window_;
317
318   DISALLOW_COPY_AND_ASSIGN(DontClobberRestoreBoundsWindowObserver);
319 };
320
321 // Creates a window, maximized the window and from within the maximized
322 // notification sets the bounds of a window to overlap the shelf. Verifies this
323 // doesn't effect the restore bounds.
324 TEST_F(WorkspaceLayoutManagerTest, DontClobberRestoreBounds) {
325   DontClobberRestoreBoundsWindowObserver window_observer;
326   scoped_ptr<aura::Window> window(new aura::Window(NULL));
327   window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
328   window->Init(aura::WINDOW_LAYER_TEXTURED);
329   window->SetBounds(gfx::Rect(10, 20, 30, 40));
330   // NOTE: for this test to exercise the failure the observer needs to be added
331   // before the parent set. This mimics what BrowserFrameAsh does.
332   window->AddObserver(&window_observer);
333   ParentWindowInPrimaryRootWindow(window.get());
334   window->Show();
335
336   wm::WindowState* window_state = wm::GetWindowState(window.get());
337   window_state->Activate();
338
339   scoped_ptr<aura::Window> window2(
340       CreateTestWindowInShellWithBounds(gfx::Rect(12, 20, 30, 40)));
341   ::wm::AddTransientChild(window.get(), window2.get());
342   window2->Show();
343
344   window_observer.set_window(window2.get());
345   window_state->Maximize();
346   EXPECT_EQ("10,20 30x40",
347             window_state->GetRestoreBoundsInScreen().ToString());
348   window->RemoveObserver(&window_observer);
349 }
350
351 // Verifies when a window is maximized all descendant windows have a size.
352 TEST_F(WorkspaceLayoutManagerTest, ChildBoundsResetOnMaximize) {
353   scoped_ptr<aura::Window> window(
354       CreateTestWindowInShellWithBounds(gfx::Rect(10, 20, 30, 40)));
355   window->Show();
356   wm::WindowState* window_state = wm::GetWindowState(window.get());
357   window_state->Activate();
358   scoped_ptr<aura::Window> child_window(
359       aura::test::CreateTestWindowWithBounds(gfx::Rect(5, 6, 7, 8),
360                                              window.get()));
361   child_window->Show();
362   window_state->Maximize();
363   EXPECT_EQ("5,6 7x8", child_window->bounds().ToString());
364 }
365
366 // Verifies a window created with maximized state has the maximized
367 // bounds.
368 TEST_F(WorkspaceLayoutManagerTest, MaximizeWithEmptySize) {
369   scoped_ptr<aura::Window> window(
370       aura::test::CreateTestWindowWithBounds(gfx::Rect(0, 0, 0, 0),
371                                              NULL));
372   wm::GetWindowState(window.get())->Maximize();
373   aura::Window* default_container = Shell::GetContainer(
374       Shell::GetPrimaryRootWindow(),
375       internal::kShellWindowId_DefaultContainer);
376   default_container->AddChild(window.get());
377   window->Show();
378   gfx::Rect work_area(
379       Shell::GetScreen()->GetPrimaryDisplay().work_area());
380   EXPECT_EQ(work_area.ToString(), window->GetBoundsInScreen().ToString());
381 }
382
383 TEST_F(WorkspaceLayoutManagerTest, WindowShouldBeOnScreenWhenAdded) {
384   // Normal window bounds shouldn't be changed.
385   gfx::Rect window_bounds(100, 100, 200, 200);
386   scoped_ptr<aura::Window> window(
387       CreateTestWindowInShellWithBounds(window_bounds));
388   EXPECT_EQ(window_bounds, window->bounds());
389
390   // If the window is out of the workspace, it would be moved on screen.
391   gfx::Rect root_window_bounds =
392       Shell::GetInstance()->GetPrimaryRootWindow()->bounds();
393   window_bounds.Offset(root_window_bounds.width(), root_window_bounds.height());
394   ASSERT_FALSE(window_bounds.Intersects(root_window_bounds));
395   scoped_ptr<aura::Window> out_window(
396       CreateTestWindowInShellWithBounds(window_bounds));
397   EXPECT_EQ(window_bounds.size(), out_window->bounds().size());
398   gfx::Rect bounds = out_window->bounds();
399   bounds.Intersect(root_window_bounds);
400
401   // 30% of the window edge must be visible.
402   EXPECT_GT(bounds.width(), out_window->bounds().width() * 0.29);
403   EXPECT_GT(bounds.height(), out_window->bounds().height() * 0.29);
404
405   aura::Window* parent = out_window->parent();
406   parent->RemoveChild(out_window.get());
407   out_window->SetBounds(gfx::Rect(-200, -200, 200, 200));
408   // UserHasChangedWindowPositionOrSize flag shouldn't turn off this behavior.
409   wm::GetWindowState(window.get())->set_bounds_changed_by_user(true);
410   parent->AddChild(out_window.get());
411   EXPECT_GT(bounds.width(), out_window->bounds().width() * 0.29);
412   EXPECT_GT(bounds.height(), out_window->bounds().height() * 0.29);
413
414   // Make sure we always make more than 1/3 of the window edge visible even
415   // if the initial bounds intersects with display.
416   window_bounds.SetRect(-150, -150, 200, 200);
417   bounds = window_bounds;
418   bounds.Intersect(root_window_bounds);
419
420   // Make sure that the initial bounds' visible area is less than 26%
421   // so that the auto adjustment logic kicks in.
422   ASSERT_LT(bounds.width(), out_window->bounds().width() * 0.26);
423   ASSERT_LT(bounds.height(), out_window->bounds().height() * 0.26);
424   ASSERT_TRUE(window_bounds.Intersects(root_window_bounds));
425
426   scoped_ptr<aura::Window> partially_out_window(
427       CreateTestWindowInShellWithBounds(window_bounds));
428   EXPECT_EQ(window_bounds.size(), partially_out_window->bounds().size());
429   bounds = partially_out_window->bounds();
430   bounds.Intersect(root_window_bounds);
431   EXPECT_GT(bounds.width(), out_window->bounds().width() * 0.29);
432   EXPECT_GT(bounds.height(), out_window->bounds().height() * 0.29);
433
434   // Make sure the window whose 30% width/height is bigger than display
435   // will be placed correctly.
436   window_bounds.SetRect(-1900, -1900, 3000, 3000);
437   scoped_ptr<aura::Window> window_bigger_than_display(
438       CreateTestWindowInShellWithBounds(window_bounds));
439   EXPECT_GE(root_window_bounds.width(),
440             window_bigger_than_display->bounds().width());
441   EXPECT_GE(root_window_bounds.height(),
442             window_bigger_than_display->bounds().height());
443
444   bounds = window_bigger_than_display->bounds();
445   bounds.Intersect(root_window_bounds);
446   EXPECT_GT(bounds.width(), out_window->bounds().width() * 0.29);
447   EXPECT_GT(bounds.height(), out_window->bounds().height() * 0.29);
448 }
449
450 // Verifies the size of a window is enforced to be smaller than the work area.
451 TEST_F(WorkspaceLayoutManagerTest, SizeToWorkArea) {
452   // Normal window bounds shouldn't be changed.
453   gfx::Size work_area(
454       Shell::GetScreen()->GetPrimaryDisplay().work_area().size());
455   const gfx::Rect window_bounds(
456       100, 101, work_area.width() + 1, work_area.height() + 2);
457   scoped_ptr<aura::Window> window(
458       CreateTestWindowInShellWithBounds(window_bounds));
459   EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(),
460       window->bounds().ToString());
461
462   // Directly setting the bounds triggers a slightly different code path. Verify
463   // that too.
464   window->SetBounds(window_bounds);
465   EXPECT_EQ(gfx::Rect(gfx::Point(100, 101), work_area).ToString(),
466       window->bounds().ToString());
467 }
468
469 TEST_F(WorkspaceLayoutManagerTest, NotifyFullscreenChanges) {
470   TestShellObserver observer;
471   scoped_ptr<aura::Window> window1(
472       CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40)));
473   scoped_ptr<aura::Window> window2(
474       CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40)));
475   wm::WindowState* window_state1 = wm::GetWindowState(window1.get());
476   wm::WindowState* window_state2 = wm::GetWindowState(window2.get());
477   window_state2->Activate();
478
479   const wm::WMEvent toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN);
480   window_state2->OnWMEvent(&toggle_fullscreen_event);
481   EXPECT_EQ(1, observer.call_count());
482   EXPECT_TRUE(observer.is_fullscreen());
483
484   // When window1 moves to the front the fullscreen state should change.
485   window_state1->Activate();
486   EXPECT_EQ(2, observer.call_count());
487   EXPECT_FALSE(observer.is_fullscreen());
488
489   // It should change back if window2 becomes active again.
490   window_state2->Activate();
491   EXPECT_EQ(3, observer.call_count());
492   EXPECT_TRUE(observer.is_fullscreen());
493
494   window_state2->OnWMEvent(&toggle_fullscreen_event);
495   EXPECT_EQ(4, observer.call_count());
496   EXPECT_FALSE(observer.is_fullscreen());
497
498   window_state2->OnWMEvent(&toggle_fullscreen_event);
499   EXPECT_EQ(5, observer.call_count());
500   EXPECT_TRUE(observer.is_fullscreen());
501
502   // Closing the window should change the fullscreen state.
503   window2.reset();
504   EXPECT_EQ(6, observer.call_count());
505   EXPECT_FALSE(observer.is_fullscreen());
506 }
507
508 // Following tests were originally written for BaseLayoutManager.
509
510 namespace {
511
512 class WorkspaceLayoutManagerSoloTest : public test::AshTestBase {
513  public:
514   WorkspaceLayoutManagerSoloTest() {}
515   virtual ~WorkspaceLayoutManagerSoloTest() {}
516
517   virtual void SetUp() OVERRIDE {
518     test::AshTestBase::SetUp();
519     UpdateDisplay("800x600");
520     aura::Window* default_container = Shell::GetContainer(
521         Shell::GetPrimaryRootWindow(),
522         internal::kShellWindowId_DefaultContainer);
523     default_container->SetLayoutManager(new internal::WorkspaceLayoutManager(
524         Shell::GetPrimaryRootWindow()));
525   }
526
527   aura::Window* CreateTestWindow(const gfx::Rect& bounds) {
528     return CreateTestWindowInShellWithBounds(bounds);
529   }
530
531  private:
532   DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManagerSoloTest);
533 };
534
535 }  // namespace
536
537 // Tests normal->maximize->normal.
538 TEST_F(WorkspaceLayoutManagerSoloTest, Maximize) {
539   gfx::Rect bounds(100, 100, 200, 200);
540   scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
541   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
542   // Maximized window fills the work area, not the whole display.
543   EXPECT_EQ(
544       ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()).ToString(),
545       window->bounds().ToString());
546   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
547   EXPECT_EQ(bounds.ToString(), window->bounds().ToString());
548 }
549
550 // Tests normal->minimize->normal.
551 TEST_F(WorkspaceLayoutManagerSoloTest, Minimize) {
552   gfx::Rect bounds(100, 100, 200, 200);
553   scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
554   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
555   // Note: Currently minimize doesn't do anything except set the state.
556   // See crbug.com/104571.
557   EXPECT_EQ(bounds.ToString(), window->bounds().ToString());
558   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
559   EXPECT_EQ(bounds.ToString(), window->bounds().ToString());
560 }
561
562 // A WindowDelegate which sets the focus when the window
563 // becomes visible.
564 class FocusDelegate : public aura::test::TestWindowDelegate {
565  public:
566   FocusDelegate()
567       : window_(NULL),
568         show_state_(ui::SHOW_STATE_END) {
569   }
570   virtual ~FocusDelegate() {}
571
572   void set_window(aura::Window* window) { window_ = window; }
573
574   // aura::test::TestWindowDelegate overrides:
575   virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE {
576     if (window_) {
577       if (visible)
578         window_->Focus();
579       show_state_ = window_->GetProperty(aura::client::kShowStateKey);
580     }
581   }
582
583   ui::WindowShowState GetShowStateAndReset() {
584     ui::WindowShowState ret = show_state_;
585     show_state_ = ui::SHOW_STATE_END;
586     return ret;
587   }
588
589  private:
590   aura::Window* window_;
591   ui::WindowShowState show_state_;
592
593   DISALLOW_COPY_AND_ASSIGN(FocusDelegate);
594 };
595
596 // Make sure that the window's show state is correct in
597 // |WindowDelegate::OnWindowTargetVisibilityChanged|, and setting
598 // focus in this callback doesn't cause DCHECK error.  See
599 // crbug.com/168383.
600 TEST_F(WorkspaceLayoutManagerSoloTest, FocusDuringUnminimize) {
601   FocusDelegate delegate;
602   scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
603       &delegate, 0, gfx::Rect(100, 100, 100, 100)));
604   delegate.set_window(window.get());
605   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
606   EXPECT_FALSE(window->IsVisible());
607   EXPECT_EQ(ui::SHOW_STATE_MINIMIZED, delegate.GetShowStateAndReset());
608   window->Show();
609   EXPECT_TRUE(window->IsVisible());
610   EXPECT_EQ(ui::SHOW_STATE_NORMAL, delegate.GetShowStateAndReset());
611 }
612
613 // Tests maximized window size during root window resize.
614 TEST_F(WorkspaceLayoutManagerSoloTest, MaximizeRootWindowResize) {
615   gfx::Rect bounds(100, 100, 200, 200);
616   scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
617   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
618   gfx::Rect initial_work_area_bounds =
619       ScreenUtil::GetMaximizedWindowBoundsInParent(window.get());
620   EXPECT_EQ(initial_work_area_bounds.ToString(), window->bounds().ToString());
621   // Enlarge the root window.  We should still match the work area size.
622   UpdateDisplay("900x700");
623   EXPECT_EQ(
624       ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()).ToString(),
625       window->bounds().ToString());
626   EXPECT_NE(
627       initial_work_area_bounds.ToString(),
628       ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()).ToString());
629 }
630
631 // Tests normal->fullscreen->normal.
632 TEST_F(WorkspaceLayoutManagerSoloTest, Fullscreen) {
633   gfx::Rect bounds(100, 100, 200, 200);
634   scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
635   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
636   // Fullscreen window fills the whole display.
637   EXPECT_EQ(Shell::GetScreen()->GetDisplayNearestWindow(
638                 window.get()).bounds().ToString(),
639             window->bounds().ToString());
640   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
641   EXPECT_EQ(bounds.ToString(), window->bounds().ToString());
642 }
643
644 // Tests fullscreen window size during root window resize.
645 TEST_F(WorkspaceLayoutManagerSoloTest, FullscreenRootWindowResize) {
646   gfx::Rect bounds(100, 100, 200, 200);
647   scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
648   // Fullscreen window fills the whole display.
649   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
650   EXPECT_EQ(Shell::GetScreen()->GetDisplayNearestWindow(
651                 window.get()).bounds().ToString(),
652             window->bounds().ToString());
653   // Enlarge the root window.  We should still match the display size.
654   UpdateDisplay("800x600");
655   EXPECT_EQ(Shell::GetScreen()->GetDisplayNearestWindow(
656                 window.get()).bounds().ToString(),
657             window->bounds().ToString());
658 }
659
660 // Tests that when the screen gets smaller the windows aren't bigger than
661 // the screen.
662 TEST_F(WorkspaceLayoutManagerSoloTest, RootWindowResizeShrinksWindows) {
663   scoped_ptr<aura::Window> window(
664       CreateTestWindow(gfx::Rect(10, 20, 500, 400)));
665   gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow(
666       window.get()).work_area();
667   // Invariant: Window is smaller than work area.
668   EXPECT_LE(window->bounds().width(), work_area.width());
669   EXPECT_LE(window->bounds().height(), work_area.height());
670
671   // Make the root window narrower than our window.
672   UpdateDisplay("300x400");
673   work_area = Shell::GetScreen()->GetDisplayNearestWindow(
674       window.get()).work_area();
675   EXPECT_LE(window->bounds().width(), work_area.width());
676   EXPECT_LE(window->bounds().height(), work_area.height());
677
678   // Make the root window shorter than our window.
679   UpdateDisplay("300x200");
680   work_area = Shell::GetScreen()->GetDisplayNearestWindow(
681       window.get()).work_area();
682   EXPECT_LE(window->bounds().width(), work_area.width());
683   EXPECT_LE(window->bounds().height(), work_area.height());
684
685   // Enlarging the root window does not change the window bounds.
686   gfx::Rect old_bounds = window->bounds();
687   UpdateDisplay("800x600");
688   EXPECT_EQ(old_bounds.width(), window->bounds().width());
689   EXPECT_EQ(old_bounds.height(), window->bounds().height());
690 }
691
692 // Verifies maximizing sets the restore bounds, and restoring
693 // restores the bounds.
694 TEST_F(WorkspaceLayoutManagerSoloTest, MaximizeSetsRestoreBounds) {
695   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(10, 20, 30, 40)));
696   wm::WindowState* window_state = wm::GetWindowState(window.get());
697
698   // Maximize it, which will keep the previous restore bounds.
699   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
700   EXPECT_EQ("10,20 30x40", window_state->GetRestoreBoundsInParent().ToString());
701
702   // Restore it, which should restore bounds and reset restore bounds.
703   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
704   EXPECT_EQ("10,20 30x40", window->bounds().ToString());
705   EXPECT_FALSE(window_state->HasRestoreBounds());
706 }
707
708 // Verifies maximizing keeps the restore bounds if set.
709 TEST_F(WorkspaceLayoutManagerSoloTest, MaximizeResetsRestoreBounds) {
710   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(1, 2, 3, 4)));
711
712   wm::WindowState* window_state = wm::GetWindowState(window.get());
713   window_state->SetRestoreBoundsInParent(gfx::Rect(10, 11, 12, 13));
714
715   // Maximize it, which will keep the previous restore bounds.
716   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
717   EXPECT_EQ("10,11 12x13", window_state->GetRestoreBoundsInParent().ToString());
718 }
719
720 // Verifies that the restore bounds do not get reset when restoring to a
721 // maximzied state from a minimized state.
722 TEST_F(WorkspaceLayoutManagerSoloTest,
723        BoundsAfterRestoringToMaximizeFromMinimize) {
724   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(1, 2, 3, 4)));
725   gfx::Rect bounds(10, 15, 25, 35);
726   window->SetBounds(bounds);
727
728   wm::WindowState* window_state = wm::GetWindowState(window.get());
729   // Maximize it, which should reset restore bounds.
730   window_state->Maximize();
731   EXPECT_EQ(bounds.ToString(),
732             window_state->GetRestoreBoundsInParent().ToString());
733   // Minimize the window. The restore bounds should not change.
734   window_state->Minimize();
735   EXPECT_EQ(bounds.ToString(),
736             window_state->GetRestoreBoundsInParent().ToString());
737
738   // Show the window again. The window should be maximized, and the restore
739   // bounds should not change.
740   window->Show();
741   EXPECT_EQ(bounds.ToString(),
742             window_state->GetRestoreBoundsInParent().ToString());
743   EXPECT_TRUE(window_state->IsMaximized());
744
745   window_state->Restore();
746   EXPECT_EQ(bounds.ToString(), window->bounds().ToString());
747 }
748
749 // Verify if the window is not resized during screen lock. See: crbug.com/173127
750 TEST_F(WorkspaceLayoutManagerSoloTest, NotResizeWhenScreenIsLocked) {
751   SetCanLockScreen(true);
752   scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(1, 2, 3, 4)));
753   // window with AlwaysOnTop will be managed by BaseLayoutManager.
754   window->SetProperty(aura::client::kAlwaysOnTopKey, true);
755   window->Show();
756
757   internal::ShelfLayoutManager* shelf =
758       internal::ShelfLayoutManager::ForShelf(window.get());
759   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
760
761   window->SetBounds(ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()));
762   gfx::Rect window_bounds = window->bounds();
763   EXPECT_EQ(
764       ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()).ToString(),
765       window_bounds.ToString());
766
767   Shell::GetInstance()->session_state_delegate()->LockScreen();
768   shelf->UpdateVisibilityState();
769   EXPECT_NE(
770       ScreenUtil::GetMaximizedWindowBoundsInParent(window.get()).ToString(),
771       window_bounds.ToString());
772
773   Shell::GetInstance()->session_state_delegate()->UnlockScreen();
774   shelf->UpdateVisibilityState();
775   EXPECT_EQ(window_bounds.ToString(), window->bounds().ToString());
776 }
777
778 // Following tests are written to test the backdrop functionality.
779
780 namespace {
781
782 class WorkspaceLayoutManagerBackdropTest : public test::AshTestBase {
783  public:
784   WorkspaceLayoutManagerBackdropTest() {}
785   virtual ~WorkspaceLayoutManagerBackdropTest() {}
786
787   virtual void SetUp() OVERRIDE {
788     test::AshTestBase::SetUp();
789     UpdateDisplay("800x600");
790     default_container_ = Shell::GetContainer(
791         Shell::GetPrimaryRootWindow(),
792         internal::kShellWindowId_DefaultContainer);
793     // We set the size to something smaller then the display to avoid resizing
794     // issues with the shelf.
795     default_container_->SetBounds(gfx::Rect(0, 0, 800, 500));
796   }
797
798   aura::Window* CreateTestWindow(const gfx::Rect& bounds) {
799     aura::Window* window = CreateTestWindowInShellWithBounds(bounds);
800     return window;
801   }
802
803   // Turn the top window back drop on / off.
804   void ShowTopWindowBackdrop(bool show) {
805     scoped_ptr<ash::internal::WorkspaceLayoutManagerDelegate> backdrop;
806     if (show) {
807       backdrop.reset(new ash::internal::WorkspaceBackdropDelegate(
808           default_container_));
809     }
810     (static_cast<internal::WorkspaceLayoutManager*>
811         (default_container_->layout_manager()))->SetMaximizeBackdropDelegate(
812             backdrop.Pass());
813     // Closing and / or opening can be a delayed operation.
814     base::MessageLoop::current()->RunUntilIdle();
815   }
816
817   // Return the default container.
818   aura::Window* default_container() { return default_container_; }
819
820   // Return the order of windows (top most first) as they are in the default
821   // container. If the window is visible it will be a big letter, otherwise a
822   // small one. The backdrop will be an X and unknown windows will be shown as
823   // '!'.
824   std::string GetWindowOrderAsString(aura::Window* backdrop,
825                                      aura::Window* wa,
826                                      aura::Window* wb,
827                                      aura::Window* wc) {
828     std::string result;
829     for (int i = static_cast<int>(default_container()->children().size()) - 1;
830          i >= 0;
831          --i) {
832       if (!result.empty())
833         result += ",";
834       if (default_container()->children()[i] == wa)
835         result += default_container()->children()[i]->IsVisible() ? "A" : "a";
836       else if (default_container()->children()[i] == wb)
837         result += default_container()->children()[i]->IsVisible() ? "B" : "b";
838       else if (default_container()->children()[i] == wc)
839         result += default_container()->children()[i]->IsVisible() ? "C" : "c";
840       else if (default_container()->children()[i] == backdrop)
841         result += default_container()->children()[i]->IsVisible() ? "X" : "x";
842       else
843         result += "!";
844     }
845     return result;
846   }
847
848  private:
849   // The default container.
850   aura::Window* default_container_;
851
852   DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManagerBackdropTest);
853 };
854
855 }  // namespace
856
857 // Check that creating the BackDrop without destroying it does not lead into
858 // a crash.
859 TEST_F(WorkspaceLayoutManagerBackdropTest, BackdropCrashTest) {
860   ShowTopWindowBackdrop(true);
861 }
862
863 // Verify basic assumptions about the backdrop.
864 TEST_F(WorkspaceLayoutManagerBackdropTest, BasicBackdropTests) {
865   // Create a backdrop and see that there is one window (the backdrop) and
866   // that the size is the same as the default container as well as that it is
867   // not visible.
868   ShowTopWindowBackdrop(true);
869   ASSERT_EQ(1U, default_container()->children().size());
870   EXPECT_FALSE(default_container()->children()[0]->IsVisible());
871
872   {
873     // Add a window and make sure that the backdrop is the second child.
874     scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(1, 2, 3, 4)));
875     window->Show();
876     ASSERT_EQ(2U, default_container()->children().size());
877     EXPECT_TRUE(default_container()->children()[0]->IsVisible());
878     EXPECT_TRUE(default_container()->children()[1]->IsVisible());
879     EXPECT_EQ(window.get(), default_container()->children()[1]);
880     EXPECT_EQ(default_container()->bounds().ToString(),
881               default_container()->children()[0]->bounds().ToString());
882   }
883
884   // With the window gone the backdrop should be invisible again.
885   ASSERT_EQ(1U, default_container()->children().size());
886   EXPECT_FALSE(default_container()->children()[0]->IsVisible());
887
888   // Destroying the Backdrop should empty the container.
889   ShowTopWindowBackdrop(false);
890   ASSERT_EQ(0U, default_container()->children().size());
891 }
892
893 // Verify that the backdrop gets properly created and placed.
894 TEST_F(WorkspaceLayoutManagerBackdropTest, VerifyBackdropAndItsStacking) {
895   scoped_ptr<aura::Window> window1(CreateTestWindow(gfx::Rect(1, 2, 3, 4)));
896   window1->Show();
897
898   // Get the default container and check that only a single window is in there.
899   ASSERT_EQ(1U, default_container()->children().size());
900   EXPECT_EQ(window1.get(), default_container()->children()[0]);
901   EXPECT_EQ("A", GetWindowOrderAsString(NULL, window1.get(), NULL, NULL));
902
903   // Create 2 more windows and check that they are also in the container.
904   scoped_ptr<aura::Window> window2(CreateTestWindow(gfx::Rect(10, 2, 3, 4)));
905   scoped_ptr<aura::Window> window3(CreateTestWindow(gfx::Rect(20, 2, 3, 4)));
906   window2->Show();
907   window3->Show();
908
909   aura::Window* backdrop = NULL;
910   EXPECT_EQ("C,B,A",
911             GetWindowOrderAsString(backdrop, window1.get(), window2.get(),
912                                    window3.get()));
913
914   // Turn on the backdrop mode and check that the window shows up where it
915   // should be (second highest number).
916   ShowTopWindowBackdrop(true);
917   backdrop = default_container()->children()[2];
918   EXPECT_EQ("C,X,B,A",
919             GetWindowOrderAsString(backdrop, window1.get(), window2.get(),
920                                    window3.get()));
921
922   // Switch the order of windows and check that it still remains in that
923   // location.
924   default_container()->StackChildAtTop(window2.get());
925   EXPECT_EQ("B,X,C,A",
926             GetWindowOrderAsString(backdrop, window1.get(), window2.get(),
927                                    window3.get()));
928
929   // Make the top window invisible and check.
930   window2.get()->Hide();
931   EXPECT_EQ("b,C,X,A",
932             GetWindowOrderAsString(backdrop, window1.get(), window2.get(),
933                                    window3.get()));
934   // Then delete window after window and see that everything is in order.
935   window1.reset();
936   EXPECT_EQ("b,C,X",
937             GetWindowOrderAsString(backdrop, window1.get(), window2.get(),
938                                    window3.get()));
939   window3.reset();
940   EXPECT_EQ("b,x",
941             GetWindowOrderAsString(backdrop, window1.get(), window2.get(),
942                                    window3.get()));
943   ShowTopWindowBackdrop(false);
944   EXPECT_EQ("b",
945             GetWindowOrderAsString(NULL, window1.get(), window2.get(),
946                                    window3.get()));
947 }
948
949 }  // namespace ash