1// Copyright 2014 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/display/display_manager.h"
6#include "ash/root_window_controller.h"
7#include "ash/screen_util.h"
8#include "ash/shell.h"
9#include "ash/shell_window_ids.h"
10#include "ash/test/ash_test_base.h"
11#include "ash/wm/window_state.h"
12#include "base/basictypes.h"
13#include "base/command_line.h"
14#include "ui/aura/client/aura_constants.h"
15#include "ui/aura/window.h"
16#include "ui/gfx/screen.h"
17#include "ui/keyboard/keyboard_controller.h"
18#include "ui/keyboard/keyboard_controller_proxy.h"
19#include "ui/keyboard/keyboard_switches.h"
20#include "ui/keyboard/keyboard_util.h"
21#include "ui/views/widget/widget.h"
22#include "ui/views/widget/widget_delegate.h"
23
24namespace ash {
25namespace test {
26
27namespace {
28
29const int kVirtualKeyboardHeight = 100;
30
31// A login implementation of WidgetDelegate.
32class LoginTestWidgetDelegate : public views::WidgetDelegate {
33 public:
34  explicit LoginTestWidgetDelegate(views::Widget* widget) : widget_(widget) {
35  }
36  virtual ~LoginTestWidgetDelegate() {}
37
38  // Overridden from WidgetDelegate:
39  virtual void DeleteDelegate() OVERRIDE {
40    delete this;
41  }
42  virtual views::Widget* GetWidget() OVERRIDE {
43    return widget_;
44  }
45  virtual const views::Widget* GetWidget() const OVERRIDE {
46    return widget_;
47  }
48  virtual bool CanActivate() const OVERRIDE {
49    return true;
50  }
51  virtual bool ShouldAdvanceFocusToTopLevelWidget() const OVERRIDE {
52    return true;
53  }
54
55 private:
56  views::Widget* widget_;
57
58  DISALLOW_COPY_AND_ASSIGN(LoginTestWidgetDelegate);
59};
60
61}  // namespace
62
63class LockLayoutManagerTest : public AshTestBase {
64 public:
65  virtual void SetUp() OVERRIDE {
66    // Allow a virtual keyboard (and initialize it per default).
67    CommandLine::ForCurrentProcess()->AppendSwitch(
68        keyboard::switches::kEnableVirtualKeyboard);
69    AshTestBase::SetUp();
70    Shell::GetPrimaryRootWindowController()->ActivateKeyboard(
71        keyboard::KeyboardController::GetInstance());
72  }
73
74  virtual void TearDown() OVERRIDE {
75    Shell::GetPrimaryRootWindowController()->DeactivateKeyboard(
76        keyboard::KeyboardController::GetInstance());
77    AshTestBase::TearDown();
78  }
79
80  aura::Window* CreateTestLoginWindow(views::Widget::InitParams params,
81                                      bool use_delegate) {
82    aura::Window* parent = Shell::GetPrimaryRootWindowController()->
83        GetContainer(ash::kShellWindowId_LockScreenContainer);
84    params.parent = parent;
85    views::Widget* widget = new views::Widget;
86    if (use_delegate)
87      params.delegate = new LoginTestWidgetDelegate(widget);
88    widget->Init(params);
89    widget->Show();
90    aura::Window* window = widget->GetNativeView();
91    return window;
92  }
93
94  // Show or hide the keyboard.
95  void ShowKeyboard(bool show) {
96    keyboard::KeyboardController* keyboard =
97        keyboard::KeyboardController::GetInstance();
98    ASSERT_TRUE(keyboard);
99    if (show == keyboard->keyboard_visible())
100      return;
101
102    if (show) {
103      keyboard->ShowKeyboard(true);
104      if (keyboard->proxy()->GetKeyboardWindow()->bounds().height() == 0) {
105        keyboard->proxy()->GetKeyboardWindow()->SetBounds(
106            keyboard::KeyboardBoundsFromWindowBounds(
107                keyboard->GetContainerWindow()->bounds(),
108                kVirtualKeyboardHeight));
109      }
110    } else {
111      keyboard->HideKeyboard(keyboard::KeyboardController::HIDE_REASON_MANUAL);
112    }
113
114    DCHECK_EQ(show, keyboard->keyboard_visible());
115  }
116};
117
118TEST_F(LockLayoutManagerTest, NorwmalWindowBoundsArePreserved) {
119  gfx::Rect screen_bounds = Shell::GetScreen()->GetPrimaryDisplay().bounds();
120
121  views::Widget::InitParams widget_params(
122      views::Widget::InitParams::TYPE_WINDOW);
123  const gfx::Rect bounds = gfx::Rect(10, 10, 300, 300);
124  widget_params.bounds = bounds;
125  scoped_ptr<aura::Window> window(
126      CreateTestLoginWindow(widget_params, false /* use_delegate */));
127  EXPECT_EQ(bounds.ToString(), window->GetBoundsInScreen().ToString());
128
129  gfx::Rect work_area =
130      ScreenUtil::GetDisplayWorkAreaBoundsInParent(window.get());
131  window->SetBounds(work_area);
132
133  EXPECT_EQ(work_area.ToString(), window->GetBoundsInScreen().ToString());
134  EXPECT_NE(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
135
136  const gfx::Rect bounds2 = gfx::Rect(100, 100, 200, 200);
137  window->SetBounds(bounds2);
138  EXPECT_EQ(bounds2.ToString(), window->GetBoundsInScreen().ToString());
139}
140
141TEST_F(LockLayoutManagerTest, MaximizedFullscreenWindowBoundsAreEqualToScreen) {
142  gfx::Rect screen_bounds = Shell::GetScreen()->GetPrimaryDisplay().bounds();
143
144  views::Widget::InitParams widget_params(
145      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
146  widget_params.show_state = ui::SHOW_STATE_MAXIMIZED;
147  const gfx::Rect bounds = gfx::Rect(10, 10, 300, 300);
148  widget_params.bounds = bounds;
149  // Maximized TYPE_WINDOW_FRAMELESS windows needs a delegate defined otherwise
150  // it won't get initial SetBounds event.
151  scoped_ptr<aura::Window> maximized_window(
152      CreateTestLoginWindow(widget_params, true /* use_delegate */));
153
154  widget_params.show_state = ui::SHOW_STATE_FULLSCREEN;
155  widget_params.delegate = NULL;
156  scoped_ptr<aura::Window> fullscreen_window(
157      CreateTestLoginWindow(widget_params, false  /* use_delegate */));
158
159  EXPECT_EQ(screen_bounds.ToString(),
160            maximized_window->GetBoundsInScreen().ToString());
161  EXPECT_EQ(screen_bounds.ToString(),
162            fullscreen_window->GetBoundsInScreen().ToString());
163
164  gfx::Rect work_area =
165      ScreenUtil::GetDisplayWorkAreaBoundsInParent(maximized_window.get());
166  maximized_window->SetBounds(work_area);
167
168  EXPECT_NE(work_area.ToString(),
169            maximized_window->GetBoundsInScreen().ToString());
170  EXPECT_EQ(screen_bounds.ToString(),
171            maximized_window->GetBoundsInScreen().ToString());
172
173  work_area =
174      ScreenUtil::GetDisplayWorkAreaBoundsInParent(fullscreen_window.get());
175  fullscreen_window->SetBounds(work_area);
176  EXPECT_NE(work_area.ToString(),
177            fullscreen_window->GetBoundsInScreen().ToString());
178  EXPECT_EQ(screen_bounds.ToString(),
179            fullscreen_window->GetBoundsInScreen().ToString());
180
181  const gfx::Rect bounds2 = gfx::Rect(100, 100, 200, 200);
182  maximized_window->SetBounds(bounds2);
183  fullscreen_window->SetBounds(bounds2);
184  EXPECT_EQ(screen_bounds.ToString(),
185            maximized_window->GetBoundsInScreen().ToString());
186  EXPECT_EQ(screen_bounds.ToString(),
187            fullscreen_window->GetBoundsInScreen().ToString());
188}
189
190TEST_F(LockLayoutManagerTest, KeyboardBounds) {
191  gfx::Display primary_display = Shell::GetScreen()->GetPrimaryDisplay();
192  gfx::Rect screen_bounds = primary_display.bounds();
193
194  views::Widget::InitParams widget_params(
195      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
196  widget_params.show_state = ui::SHOW_STATE_FULLSCREEN;
197  scoped_ptr<aura::Window> window(
198      CreateTestLoginWindow(widget_params, false /* use_delegate */));
199
200  EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
201
202  // When virtual keyboard overscroll is enabled keyboard bounds should not
203  // affect window bounds.
204  keyboard::SetKeyboardOverscrollOverride(
205       keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_ENABLED);
206  ShowKeyboard(true);
207  EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
208  gfx::Rect keyboard_bounds =
209      keyboard::KeyboardController::GetInstance()->current_keyboard_bounds();
210  EXPECT_NE(keyboard_bounds, gfx::Rect());
211  ShowKeyboard(false);
212
213  // When keyboard is hidden make sure that rotating the screen gives 100% of
214  // screen size to window.
215  // Repro steps for http://crbug.com/401667:
216  // 1. Set up login screen defaults: VK override disabled
217  // 2. Show/hide keyboard, make sure that no stale keyboard bounds are cached.
218  keyboard::SetKeyboardOverscrollOverride(
219       keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_DISABLED);
220  ShowKeyboard(true);
221  ShowKeyboard(false);
222  ash::DisplayManager* display_manager =
223      Shell::GetInstance()->display_manager();
224  display_manager->SetDisplayRotation(primary_display.id(),
225                                      gfx::Display::ROTATE_90);
226  primary_display = Shell::GetScreen()->GetPrimaryDisplay();
227  screen_bounds = primary_display.bounds();
228  EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
229  display_manager->SetDisplayRotation(primary_display.id(),
230                                      gfx::Display::ROTATE_0);
231
232  // When virtual keyboard overscroll is disabled keyboard bounds do
233  // affect window bounds.
234  keyboard::SetKeyboardOverscrollOverride(
235       keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_DISABLED);
236  ShowKeyboard(true);
237  keyboard::KeyboardController* keyboard =
238        keyboard::KeyboardController::GetInstance();
239  primary_display = Shell::GetScreen()->GetPrimaryDisplay();
240  screen_bounds = primary_display.bounds();
241  gfx::Rect target_bounds(screen_bounds);
242  target_bounds.set_height(target_bounds.height() -
243      keyboard->proxy()->GetKeyboardWindow()->bounds().height());
244  EXPECT_EQ(target_bounds.ToString(), window->GetBoundsInScreen().ToString());
245  ShowKeyboard(false);
246
247  keyboard::SetKeyboardOverscrollOverride(
248      keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_NONE);
249}
250
251TEST_F(LockLayoutManagerTest, MultipleMonitors) {
252  if (!SupportsMultipleDisplays())
253    return;
254
255  UpdateDisplay("300x400,400x500");
256  gfx::Rect screen_bounds = Shell::GetScreen()->GetPrimaryDisplay().bounds();
257  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
258
259  views::Widget::InitParams widget_params(
260      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
261  widget_params.show_state = ui::SHOW_STATE_FULLSCREEN;
262  scoped_ptr<aura::Window> window(
263      CreateTestLoginWindow(widget_params, false /* use_delegate */));
264  window->SetProperty(aura::client::kCanMaximizeKey, true);
265
266  EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
267
268  EXPECT_EQ(root_windows[0], window->GetRootWindow());
269
270  wm::WindowState* window_state = wm::GetWindowState(window.get());
271  window_state->SetRestoreBoundsInScreen(gfx::Rect(400, 0, 30, 40));
272
273  // Maximize the window with as the restore bounds is inside 2nd display but
274  // lock container windows are always on primary display.
275  window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
276  EXPECT_EQ(root_windows[0], window->GetRootWindow());
277  EXPECT_EQ("0,0 300x400", window->GetBoundsInScreen().ToString());
278
279  window_state->Restore();
280  EXPECT_EQ(root_windows[0], window->GetRootWindow());
281  EXPECT_EQ("0,0 300x400", window->GetBoundsInScreen().ToString());
282
283  window_state->SetRestoreBoundsInScreen(gfx::Rect(280, 0, 30, 40));
284  window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
285  EXPECT_EQ(root_windows[0], window->GetRootWindow());
286  EXPECT_EQ("0,0 300x400", window->GetBoundsInScreen().ToString());
287
288  window_state->Restore();
289  EXPECT_EQ(root_windows[0], window->GetRootWindow());
290  EXPECT_EQ("0,0 300x400", window->GetBoundsInScreen().ToString());
291
292  gfx::Rect work_area =
293      ScreenUtil::GetDisplayWorkAreaBoundsInParent(window.get());
294  window->SetBounds(work_area);
295  // Usually work_area takes Shelf into account but that doesn't affect
296  // LockScreen container windows.
297  EXPECT_NE(work_area.ToString(), window->GetBoundsInScreen().ToString());
298  EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString());
299}
300
301}  // namespace test
302}  // namespace ash
303