root_window_controller_unittest.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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/root_window_controller.h"
6
7#include "ash/session/session_state_delegate.h"
8#include "ash/shelf/shelf_layout_manager.h"
9#include "ash/shell.h"
10#include "ash/shell_window_ids.h"
11#include "ash/system/tray/system_tray_delegate.h"
12#include "ash/test/ash_test_base.h"
13#include "ash/wm/system_modal_container_layout_manager.h"
14#include "ash/wm/window_properties.h"
15#include "ash/wm/window_state.h"
16#include "ash/wm/window_util.h"
17#include "base/command_line.h"
18#include "ui/aura/client/focus_change_observer.h"
19#include "ui/aura/client/focus_client.h"
20#include "ui/aura/client/window_tree_client.h"
21#include "ui/aura/env.h"
22#include "ui/aura/test/event_generator.h"
23#include "ui/aura/test/test_window_delegate.h"
24#include "ui/aura/test/test_windows.h"
25#include "ui/aura/window.h"
26#include "ui/aura/window_event_dispatcher.h"
27#include "ui/aura/window_tracker.h"
28#include "ui/base/ime/dummy_text_input_client.h"
29#include "ui/base/ime/input_method.h"
30#include "ui/base/ime/text_input_client.h"
31#include "ui/base/ime/text_input_focus_manager.h"
32#include "ui/base/ui_base_switches_util.h"
33#include "ui/events/test/test_event_handler.h"
34#include "ui/keyboard/keyboard_controller_proxy.h"
35#include "ui/keyboard/keyboard_switches.h"
36#include "ui/keyboard/keyboard_util.h"
37#include "ui/views/controls/menu/menu_controller.h"
38#include "ui/views/widget/widget.h"
39#include "ui/views/widget/widget_delegate.h"
40
41using aura::Window;
42using views::Widget;
43
44namespace ash {
45namespace {
46
47class TestDelegate : public views::WidgetDelegateView {
48 public:
49  explicit TestDelegate(bool system_modal) : system_modal_(system_modal) {}
50  virtual ~TestDelegate() {}
51
52  // Overridden from views::WidgetDelegate:
53  virtual views::View* GetContentsView() OVERRIDE {
54    return this;
55  }
56
57  virtual ui::ModalType GetModalType() const OVERRIDE {
58    return system_modal_ ? ui::MODAL_TYPE_SYSTEM : ui::MODAL_TYPE_NONE;
59  }
60
61 private:
62  bool system_modal_;
63
64  DISALLOW_COPY_AND_ASSIGN(TestDelegate);
65};
66
67class DeleteOnBlurDelegate : public aura::test::TestWindowDelegate,
68                             public aura::client::FocusChangeObserver {
69 public:
70  DeleteOnBlurDelegate() : window_(NULL) {}
71  virtual ~DeleteOnBlurDelegate() {}
72
73  void SetWindow(aura::Window* window) {
74    window_ = window;
75    aura::client::SetFocusChangeObserver(window_, this);
76  }
77
78 private:
79  // aura::test::TestWindowDelegate overrides:
80  virtual bool CanFocus() OVERRIDE {
81    return true;
82  }
83
84  // aura::client::FocusChangeObserver implementation:
85  virtual void OnWindowFocused(aura::Window* gained_focus,
86                               aura::Window* lost_focus) OVERRIDE {
87    if (window_ == lost_focus)
88      delete window_;
89  }
90
91  aura::Window* window_;
92
93  DISALLOW_COPY_AND_ASSIGN(DeleteOnBlurDelegate);
94};
95
96}  // namespace
97
98namespace test {
99
100class RootWindowControllerTest : public test::AshTestBase {
101 public:
102  views::Widget* CreateTestWidget(const gfx::Rect& bounds) {
103    views::Widget* widget = views::Widget::CreateWindowWithContextAndBounds(
104        NULL, CurrentContext(), bounds);
105    widget->Show();
106    return widget;
107  }
108
109  views::Widget* CreateModalWidget(const gfx::Rect& bounds) {
110    views::Widget* widget = views::Widget::CreateWindowWithContextAndBounds(
111        new TestDelegate(true), CurrentContext(), bounds);
112    widget->Show();
113    return widget;
114  }
115
116  views::Widget* CreateModalWidgetWithParent(const gfx::Rect& bounds,
117                                             gfx::NativeWindow parent) {
118    views::Widget* widget =
119        views::Widget::CreateWindowWithParentAndBounds(new TestDelegate(true),
120                                                       parent,
121                                                       bounds);
122    widget->Show();
123    return widget;
124  }
125
126  aura::Window* GetModalContainer(aura::Window* root_window) {
127    return Shell::GetContainer(root_window,
128                               ash::kShellWindowId_SystemModalContainer);
129  }
130};
131
132TEST_F(RootWindowControllerTest, MoveWindows_Basic) {
133  if (!SupportsMultipleDisplays())
134    return;
135  // Windows origin should be doubled when moved to the 1st display.
136  UpdateDisplay("600x600,300x300");
137  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
138  RootWindowController* controller = Shell::GetPrimaryRootWindowController();
139  ShelfLayoutManager* shelf_layout_manager =
140      controller->GetShelfLayoutManager();
141  shelf_layout_manager->SetAutoHideBehavior(
142      ash::SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
143
144  views::Widget* normal = CreateTestWidget(gfx::Rect(650, 10, 100, 100));
145  EXPECT_EQ(root_windows[1], normal->GetNativeView()->GetRootWindow());
146  EXPECT_EQ("650,10 100x100", normal->GetWindowBoundsInScreen().ToString());
147  EXPECT_EQ("50,10 100x100",
148            normal->GetNativeView()->GetBoundsInRootWindow().ToString());
149
150  views::Widget* maximized = CreateTestWidget(gfx::Rect(700, 10, 100, 100));
151  maximized->Maximize();
152  EXPECT_EQ(root_windows[1], maximized->GetNativeView()->GetRootWindow());
153  EXPECT_EQ("600,0 300x253", maximized->GetWindowBoundsInScreen().ToString());
154  EXPECT_EQ("0,0 300x253",
155            maximized->GetNativeView()->GetBoundsInRootWindow().ToString());
156
157  views::Widget* minimized = CreateTestWidget(gfx::Rect(800, 10, 100, 100));
158  minimized->Minimize();
159  EXPECT_EQ(root_windows[1], minimized->GetNativeView()->GetRootWindow());
160  EXPECT_EQ("800,10 100x100",
161            minimized->GetWindowBoundsInScreen().ToString());
162
163  views::Widget* fullscreen = CreateTestWidget(gfx::Rect(850, 10, 100, 100));
164  fullscreen->SetFullscreen(true);
165  EXPECT_EQ(root_windows[1], fullscreen->GetNativeView()->GetRootWindow());
166
167  EXPECT_EQ("600,0 300x300",
168            fullscreen->GetWindowBoundsInScreen().ToString());
169  EXPECT_EQ("0,0 300x300",
170            fullscreen->GetNativeView()->GetBoundsInRootWindow().ToString());
171
172  views::Widget* unparented_control = new Widget;
173  Widget::InitParams params;
174  params.bounds = gfx::Rect(650, 10, 100, 100);
175  params.context = CurrentContext();
176  params.type = Widget::InitParams::TYPE_CONTROL;
177  unparented_control->Init(params);
178  EXPECT_EQ(root_windows[1],
179            unparented_control->GetNativeView()->GetRootWindow());
180  EXPECT_EQ(kShellWindowId_UnparentedControlContainer,
181            unparented_control->GetNativeView()->parent()->id());
182
183  aura::Window* panel = CreateTestWindowInShellWithDelegateAndType(
184      NULL, ui::wm::WINDOW_TYPE_PANEL, 0, gfx::Rect(700, 100, 100, 100));
185  EXPECT_EQ(root_windows[1], panel->GetRootWindow());
186  EXPECT_EQ(kShellWindowId_PanelContainer, panel->parent()->id());
187
188  // Make sure a window that will delete itself when losing focus
189  // will not crash.
190  aura::WindowTracker tracker;
191  DeleteOnBlurDelegate delete_on_blur_delegate;
192  aura::Window* d2 = CreateTestWindowInShellWithDelegate(
193      &delete_on_blur_delegate, 0, gfx::Rect(50, 50, 100, 100));
194  delete_on_blur_delegate.SetWindow(d2);
195  aura::client::GetFocusClient(root_windows[0])->FocusWindow(d2);
196  tracker.Add(d2);
197
198  UpdateDisplay("600x600");
199
200  // d2 must have been deleted.
201  EXPECT_FALSE(tracker.Contains(d2));
202
203  EXPECT_EQ(root_windows[0], normal->GetNativeView()->GetRootWindow());
204  EXPECT_EQ("100,20 100x100", normal->GetWindowBoundsInScreen().ToString());
205  EXPECT_EQ("100,20 100x100",
206            normal->GetNativeView()->GetBoundsInRootWindow().ToString());
207
208  // Maximized area on primary display has 3px (given as
209  // kAutoHideSize in shelf_layout_manager.cc) inset at the bottom.
210
211  // First clear fullscreen status, since both fullscreen and maximized windows
212  // share the same desktop workspace, which cancels the shelf status.
213  fullscreen->SetFullscreen(false);
214  EXPECT_EQ(root_windows[0], maximized->GetNativeView()->GetRootWindow());
215  EXPECT_EQ("0,0 600x597",
216            maximized->GetWindowBoundsInScreen().ToString());
217  EXPECT_EQ("0,0 600x597",
218            maximized->GetNativeView()->GetBoundsInRootWindow().ToString());
219
220  // Set fullscreen to true. In that case the 3px inset becomes invisible so
221  // the maximized window can also use the area fully.
222  fullscreen->SetFullscreen(true);
223  EXPECT_EQ(root_windows[0], maximized->GetNativeView()->GetRootWindow());
224  EXPECT_EQ("0,0 600x600",
225            maximized->GetWindowBoundsInScreen().ToString());
226  EXPECT_EQ("0,0 600x600",
227            maximized->GetNativeView()->GetBoundsInRootWindow().ToString());
228
229  EXPECT_EQ(root_windows[0], minimized->GetNativeView()->GetRootWindow());
230  EXPECT_EQ("400,20 100x100",
231            minimized->GetWindowBoundsInScreen().ToString());
232
233  EXPECT_EQ(root_windows[0], fullscreen->GetNativeView()->GetRootWindow());
234  EXPECT_TRUE(fullscreen->IsFullscreen());
235  EXPECT_EQ("0,0 600x600",
236            fullscreen->GetWindowBoundsInScreen().ToString());
237  EXPECT_EQ("0,0 600x600",
238            fullscreen->GetNativeView()->GetBoundsInRootWindow().ToString());
239
240  // Test if the restore bounds are correctly updated.
241  wm::GetWindowState(maximized->GetNativeView())->Restore();
242  EXPECT_EQ("200,20 100x100", maximized->GetWindowBoundsInScreen().ToString());
243  EXPECT_EQ("200,20 100x100",
244            maximized->GetNativeView()->GetBoundsInRootWindow().ToString());
245
246  fullscreen->SetFullscreen(false);
247  EXPECT_EQ("500,20 100x100",
248            fullscreen->GetWindowBoundsInScreen().ToString());
249  EXPECT_EQ("500,20 100x100",
250            fullscreen->GetNativeView()->GetBoundsInRootWindow().ToString());
251
252  // Test if the unparented widget has moved.
253  EXPECT_EQ(root_windows[0],
254            unparented_control->GetNativeView()->GetRootWindow());
255  EXPECT_EQ(kShellWindowId_UnparentedControlContainer,
256            unparented_control->GetNativeView()->parent()->id());
257
258  // Test if the panel has moved.
259  EXPECT_EQ(root_windows[0], panel->GetRootWindow());
260  EXPECT_EQ(kShellWindowId_PanelContainer, panel->parent()->id());
261}
262
263TEST_F(RootWindowControllerTest, MoveWindows_Modal) {
264  if (!SupportsMultipleDisplays())
265    return;
266
267  UpdateDisplay("500x500,500x500");
268
269  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
270  // Emulate virtual screen coordinate system.
271  root_windows[0]->SetBounds(gfx::Rect(0, 0, 500, 500));
272  root_windows[1]->SetBounds(gfx::Rect(500, 0, 500, 500));
273
274  views::Widget* normal = CreateTestWidget(gfx::Rect(300, 10, 100, 100));
275  EXPECT_EQ(root_windows[0], normal->GetNativeView()->GetRootWindow());
276  EXPECT_TRUE(wm::IsActiveWindow(normal->GetNativeView()));
277
278  views::Widget* modal = CreateModalWidget(gfx::Rect(650, 10, 100, 100));
279  EXPECT_EQ(root_windows[1], modal->GetNativeView()->GetRootWindow());
280  EXPECT_TRUE(GetModalContainer(root_windows[1])->Contains(
281      modal->GetNativeView()));
282  EXPECT_TRUE(wm::IsActiveWindow(modal->GetNativeView()));
283
284  aura::test::EventGenerator generator_1st(root_windows[0]);
285  generator_1st.ClickLeftButton();
286  EXPECT_TRUE(wm::IsActiveWindow(modal->GetNativeView()));
287
288  UpdateDisplay("500x500");
289  EXPECT_EQ(root_windows[0], modal->GetNativeView()->GetRootWindow());
290  EXPECT_TRUE(wm::IsActiveWindow(modal->GetNativeView()));
291  generator_1st.ClickLeftButton();
292  EXPECT_TRUE(wm::IsActiveWindow(modal->GetNativeView()));
293}
294
295TEST_F(RootWindowControllerTest, ModalContainer) {
296  UpdateDisplay("600x600");
297  Shell* shell = Shell::GetInstance();
298  RootWindowController* controller = shell->GetPrimaryRootWindowController();
299  EXPECT_EQ(user::LOGGED_IN_USER,
300            shell->system_tray_delegate()->GetUserLoginStatus());
301  EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer)
302                ->layout_manager(),
303            controller->GetSystemModalLayoutManager(NULL));
304
305  views::Widget* session_modal_widget =
306      CreateModalWidget(gfx::Rect(300, 10, 100, 100));
307  EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer)
308                ->layout_manager(),
309            controller->GetSystemModalLayoutManager(
310                session_modal_widget->GetNativeView()));
311
312  shell->session_state_delegate()->LockScreen();
313  EXPECT_EQ(user::LOGGED_IN_LOCKED,
314            shell->system_tray_delegate()->GetUserLoginStatus());
315  EXPECT_EQ(controller->GetContainer(kShellWindowId_LockSystemModalContainer)
316                ->layout_manager(),
317            controller->GetSystemModalLayoutManager(NULL));
318
319  aura::Window* lock_container =
320      controller->GetContainer(kShellWindowId_LockScreenContainer);
321  views::Widget* lock_modal_widget =
322      CreateModalWidgetWithParent(gfx::Rect(300, 10, 100, 100), lock_container);
323  EXPECT_EQ(controller->GetContainer(kShellWindowId_LockSystemModalContainer)
324                ->layout_manager(),
325            controller->GetSystemModalLayoutManager(
326                lock_modal_widget->GetNativeView()));
327  EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer)
328                ->layout_manager(),
329            controller->GetSystemModalLayoutManager(
330                session_modal_widget->GetNativeView()));
331
332  shell->session_state_delegate()->UnlockScreen();
333}
334
335TEST_F(RootWindowControllerTest, ModalContainerNotLoggedInLoggedIn) {
336  UpdateDisplay("600x600");
337  Shell* shell = Shell::GetInstance();
338
339  // Configure login screen environment.
340  SetUserLoggedIn(false);
341  EXPECT_EQ(user::LOGGED_IN_NONE,
342            shell->system_tray_delegate()->GetUserLoginStatus());
343  EXPECT_EQ(0, shell->session_state_delegate()->NumberOfLoggedInUsers());
344  EXPECT_FALSE(shell->session_state_delegate()->IsActiveUserSessionStarted());
345
346  RootWindowController* controller = shell->GetPrimaryRootWindowController();
347  EXPECT_EQ(controller->GetContainer(kShellWindowId_LockSystemModalContainer)
348                ->layout_manager(),
349            controller->GetSystemModalLayoutManager(NULL));
350
351  aura::Window* lock_container =
352      controller->GetContainer(kShellWindowId_LockScreenContainer);
353  views::Widget* login_modal_widget =
354      CreateModalWidgetWithParent(gfx::Rect(300, 10, 100, 100), lock_container);
355  EXPECT_EQ(controller->GetContainer(kShellWindowId_LockSystemModalContainer)
356                ->layout_manager(),
357            controller->GetSystemModalLayoutManager(
358                login_modal_widget->GetNativeView()));
359  login_modal_widget->Close();
360
361  // Configure user session environment.
362  SetUserLoggedIn(true);
363  SetSessionStarted(true);
364  EXPECT_EQ(user::LOGGED_IN_USER,
365            shell->system_tray_delegate()->GetUserLoginStatus());
366  EXPECT_EQ(1, shell->session_state_delegate()->NumberOfLoggedInUsers());
367  EXPECT_TRUE(shell->session_state_delegate()->IsActiveUserSessionStarted());
368  EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer)
369                ->layout_manager(),
370            controller->GetSystemModalLayoutManager(NULL));
371
372  views::Widget* session_modal_widget =
373        CreateModalWidget(gfx::Rect(300, 10, 100, 100));
374  EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer)
375                ->layout_manager(),
376            controller->GetSystemModalLayoutManager(
377                session_modal_widget->GetNativeView()));
378}
379
380TEST_F(RootWindowControllerTest, ModalContainerBlockedSession) {
381  UpdateDisplay("600x600");
382  Shell* shell = Shell::GetInstance();
383  RootWindowController* controller = shell->GetPrimaryRootWindowController();
384  aura::Window* lock_container =
385      controller->GetContainer(kShellWindowId_LockScreenContainer);
386  for (int block_reason = FIRST_BLOCK_REASON;
387       block_reason < NUMBER_OF_BLOCK_REASONS;
388       ++block_reason) {
389    views::Widget* session_modal_widget =
390          CreateModalWidget(gfx::Rect(300, 10, 100, 100));
391    EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer)
392                  ->layout_manager(),
393              controller->GetSystemModalLayoutManager(
394                  session_modal_widget->GetNativeView()));
395    EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer)
396                  ->layout_manager(),
397              controller->GetSystemModalLayoutManager(NULL));
398    session_modal_widget->Close();
399
400    BlockUserSession(static_cast<UserSessionBlockReason>(block_reason));
401
402    EXPECT_EQ(controller->GetContainer(kShellWindowId_LockSystemModalContainer)
403                  ->layout_manager(),
404              controller->GetSystemModalLayoutManager(NULL));
405
406    views::Widget* lock_modal_widget =
407        CreateModalWidgetWithParent(gfx::Rect(300, 10, 100, 100),
408                                    lock_container);
409    EXPECT_EQ(controller->GetContainer(kShellWindowId_LockSystemModalContainer)
410                  ->layout_manager(),
411              controller->GetSystemModalLayoutManager(
412                  lock_modal_widget->GetNativeView()));
413
414    session_modal_widget =
415          CreateModalWidget(gfx::Rect(300, 10, 100, 100));
416    EXPECT_EQ(controller->GetContainer(kShellWindowId_SystemModalContainer)
417                  ->layout_manager(),
418              controller->GetSystemModalLayoutManager(
419                  session_modal_widget->GetNativeView()));
420    session_modal_widget->Close();
421
422    lock_modal_widget->Close();
423    UnblockUserSession();
424  }
425}
426
427TEST_F(RootWindowControllerTest, GetWindowForFullscreenMode) {
428  UpdateDisplay("600x600");
429  RootWindowController* controller =
430      Shell::GetInstance()->GetPrimaryRootWindowController();
431
432  Widget* w1 = CreateTestWidget(gfx::Rect(0, 0, 100, 100));
433  w1->Maximize();
434  Widget* w2 = CreateTestWidget(gfx::Rect(0, 0, 100, 100));
435  w2->SetFullscreen(true);
436  // |w3| is a transient child of |w2|.
437  Widget* w3 = Widget::CreateWindowWithParentAndBounds(NULL,
438      w2->GetNativeWindow(), gfx::Rect(0, 0, 100, 100));
439
440  // Test that GetWindowForFullscreenMode() finds the fullscreen window when one
441  // of its transient children is active.
442  w3->Activate();
443  EXPECT_EQ(w2->GetNativeWindow(), controller->GetWindowForFullscreenMode());
444
445  // If the topmost window is not fullscreen, it returns NULL.
446  w1->Activate();
447  EXPECT_EQ(NULL, controller->GetWindowForFullscreenMode());
448  w1->Close();
449  w3->Close();
450
451  // Only w2 remains, if minimized GetWindowForFullscreenMode should return
452  // NULL.
453  w2->Activate();
454  EXPECT_EQ(w2->GetNativeWindow(), controller->GetWindowForFullscreenMode());
455  w2->Minimize();
456  EXPECT_EQ(NULL, controller->GetWindowForFullscreenMode());
457}
458
459TEST_F(RootWindowControllerTest, MultipleDisplaysGetWindowForFullscreenMode) {
460  if (!SupportsMultipleDisplays())
461    return;
462
463  UpdateDisplay("600x600,600x600");
464  Shell::RootWindowControllerList controllers =
465      Shell::GetInstance()->GetAllRootWindowControllers();
466
467  Widget* w1 = CreateTestWidget(gfx::Rect(0, 0, 100, 100));
468  w1->Maximize();
469  Widget* w2 = CreateTestWidget(gfx::Rect(0, 0, 100, 100));
470  w2->SetFullscreen(true);
471  Widget* w3 = CreateTestWidget(gfx::Rect(600, 0, 100, 100));
472
473  EXPECT_EQ(w1->GetNativeWindow()->GetRootWindow(),
474            controllers[0]->GetRootWindow());
475  EXPECT_EQ(w2->GetNativeWindow()->GetRootWindow(),
476            controllers[0]->GetRootWindow());
477  EXPECT_EQ(w3->GetNativeWindow()->GetRootWindow(),
478            controllers[1]->GetRootWindow());
479
480  w1->Activate();
481  EXPECT_EQ(NULL, controllers[0]->GetWindowForFullscreenMode());
482  EXPECT_EQ(NULL, controllers[1]->GetWindowForFullscreenMode());
483
484  w2->Activate();
485  EXPECT_EQ(w2->GetNativeWindow(),
486            controllers[0]->GetWindowForFullscreenMode());
487  EXPECT_EQ(NULL, controllers[1]->GetWindowForFullscreenMode());
488
489  // Verify that the first root window controller remains in fullscreen mode
490  // when a window on the other display is activated.
491  w3->Activate();
492  EXPECT_EQ(w2->GetNativeWindow(),
493            controllers[0]->GetWindowForFullscreenMode());
494  EXPECT_EQ(NULL, controllers[1]->GetWindowForFullscreenMode());
495}
496
497// Test that user session window can't be focused if user session blocked by
498// some overlapping UI.
499TEST_F(RootWindowControllerTest, FocusBlockedWindow) {
500  UpdateDisplay("600x600");
501  RootWindowController* controller =
502      Shell::GetInstance()->GetPrimaryRootWindowController();
503  aura::Window* lock_container =
504      controller->GetContainer(kShellWindowId_LockScreenContainer);
505  aura::Window* lock_window = Widget::CreateWindowWithParentAndBounds(NULL,
506      lock_container, gfx::Rect(0, 0, 100, 100))->GetNativeView();
507  lock_window->Show();
508  aura::Window* session_window =
509      CreateTestWidget(gfx::Rect(0, 0, 100, 100))->GetNativeView();
510  session_window->Show();
511
512  for (int block_reason = FIRST_BLOCK_REASON;
513       block_reason < NUMBER_OF_BLOCK_REASONS;
514       ++block_reason) {
515    BlockUserSession(static_cast<UserSessionBlockReason>(block_reason));
516    lock_window->Focus();
517    EXPECT_TRUE(lock_window->HasFocus());
518    session_window->Focus();
519    EXPECT_FALSE(session_window->HasFocus());
520    UnblockUserSession();
521  }
522}
523
524// Tracks whether OnWindowDestroying() has been invoked.
525class DestroyedWindowObserver : public aura::WindowObserver {
526 public:
527  DestroyedWindowObserver() : destroyed_(false), window_(NULL) {}
528  virtual ~DestroyedWindowObserver() {
529    Shutdown();
530  }
531
532  void SetWindow(Window* window) {
533    window_ = window;
534    window->AddObserver(this);
535  }
536
537  bool destroyed() const { return destroyed_; }
538
539  // WindowObserver overrides:
540  virtual void OnWindowDestroying(Window* window) OVERRIDE {
541    destroyed_ = true;
542    Shutdown();
543  }
544
545 private:
546  void Shutdown() {
547    if (!window_)
548      return;
549    window_->RemoveObserver(this);
550    window_ = NULL;
551  }
552
553  bool destroyed_;
554  Window* window_;
555
556  DISALLOW_COPY_AND_ASSIGN(DestroyedWindowObserver);
557};
558
559// Verifies shutdown doesn't delete windows that are not owned by the parent.
560TEST_F(RootWindowControllerTest, DontDeleteWindowsNotOwnedByParent) {
561  DestroyedWindowObserver observer1;
562  aura::test::TestWindowDelegate delegate1;
563  aura::Window* window1 = new aura::Window(&delegate1);
564  window1->SetType(ui::wm::WINDOW_TYPE_CONTROL);
565  window1->set_owned_by_parent(false);
566  observer1.SetWindow(window1);
567  window1->Init(aura::WINDOW_LAYER_NOT_DRAWN);
568  aura::client::ParentWindowWithContext(
569      window1, Shell::GetInstance()->GetPrimaryRootWindow(), gfx::Rect());
570
571  DestroyedWindowObserver observer2;
572  aura::Window* window2 = new aura::Window(NULL);
573  window2->set_owned_by_parent(false);
574  observer2.SetWindow(window2);
575  window2->Init(aura::WINDOW_LAYER_NOT_DRAWN);
576  Shell::GetInstance()->GetPrimaryRootWindow()->AddChild(window2);
577
578  Shell::GetInstance()->GetPrimaryRootWindowController()->CloseChildWindows();
579
580  ASSERT_FALSE(observer1.destroyed());
581  delete window1;
582
583  ASSERT_FALSE(observer2.destroyed());
584  delete window2;
585}
586
587typedef test::NoSessionAshTestBase NoSessionRootWindowControllerTest;
588
589// Make sure that an event handler exists for entire display area.
590TEST_F(NoSessionRootWindowControllerTest, Event) {
591  // Hide the shelf since it might otherwise get an event target.
592  RootWindowController* controller = Shell::GetPrimaryRootWindowController();
593  ShelfLayoutManager* shelf_layout_manager =
594      controller->GetShelfLayoutManager();
595  shelf_layout_manager->SetAutoHideBehavior(
596      ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN);
597
598  aura::Window* root = Shell::GetPrimaryRootWindow();
599  const gfx::Size size = root->bounds().size();
600  aura::Window* event_target = root->GetEventHandlerForPoint(gfx::Point(0, 0));
601  EXPECT_TRUE(event_target);
602  EXPECT_EQ(event_target,
603            root->GetEventHandlerForPoint(gfx::Point(0, size.height() - 1)));
604  EXPECT_EQ(event_target,
605            root->GetEventHandlerForPoint(gfx::Point(size.width() - 1, 0)));
606  EXPECT_EQ(event_target,
607            root->GetEventHandlerForPoint(gfx::Point(0, size.height() - 1)));
608  EXPECT_EQ(event_target,
609            root->GetEventHandlerForPoint(
610                gfx::Point(size.width() - 1, size.height() - 1)));
611}
612
613class VirtualKeyboardRootWindowControllerTest : public RootWindowControllerTest
614{
615 public:
616  VirtualKeyboardRootWindowControllerTest() {};
617  virtual ~VirtualKeyboardRootWindowControllerTest() {};
618
619  virtual void SetUp() OVERRIDE {
620    CommandLine::ForCurrentProcess()->AppendSwitch(
621        keyboard::switches::kEnableVirtualKeyboard);
622    test::AshTestBase::SetUp();
623    Shell::GetPrimaryRootWindowController()->ActivateKeyboard(
624        keyboard::KeyboardController::GetInstance());
625  }
626
627 private:
628  DISALLOW_COPY_AND_ASSIGN(VirtualKeyboardRootWindowControllerTest);
629};
630
631class MockTextInputClient : public ui::DummyTextInputClient {
632 public:
633  MockTextInputClient() :
634      ui::DummyTextInputClient(ui::TEXT_INPUT_TYPE_TEXT) {}
635
636  virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE {
637    visible_rect_ = rect;
638  }
639
640  const gfx::Rect& visible_rect() const {
641    return visible_rect_;
642  }
643
644 private:
645  gfx::Rect visible_rect_;
646
647  DISALLOW_COPY_AND_ASSIGN(MockTextInputClient);
648};
649
650// Test for http://crbug.com/297858. Virtual keyboard container should only show
651// on primary root window.
652TEST_F(VirtualKeyboardRootWindowControllerTest,
653       VirtualKeyboardOnPrimaryRootWindowOnly) {
654  if (!SupportsMultipleDisplays())
655    return;
656
657  UpdateDisplay("500x500,500x500");
658
659  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
660  aura::Window* primary_root_window = Shell::GetPrimaryRootWindow();
661  aura::Window* secondary_root_window =
662      root_windows[0] == primary_root_window ?
663          root_windows[1] : root_windows[0];
664
665  ASSERT_TRUE(Shell::GetContainer(primary_root_window,
666                                  kShellWindowId_VirtualKeyboardContainer));
667  ASSERT_FALSE(Shell::GetContainer(secondary_root_window,
668                                   kShellWindowId_VirtualKeyboardContainer));
669}
670
671// Test for http://crbug.com/263599. Virtual keyboard should be able to receive
672// events at blocked user session.
673TEST_F(VirtualKeyboardRootWindowControllerTest,
674       ClickVirtualKeyboardInBlockedWindow) {
675  aura::Window* root_window = Shell::GetPrimaryRootWindow();
676  aura::Window* keyboard_container =
677      Shell::GetContainer(root_window, kShellWindowId_VirtualKeyboardContainer);
678  ASSERT_TRUE(keyboard_container);
679  keyboard_container->Show();
680
681  aura::Window* keyboard_window = keyboard::KeyboardController::GetInstance()->
682      proxy()->GetKeyboardWindow();
683  keyboard_container->AddChild(keyboard_window);
684  keyboard_window->set_owned_by_parent(false);
685  keyboard_window->SetBounds(gfx::Rect());
686  keyboard_window->Show();
687
688  ui::test::TestEventHandler handler;
689  root_window->AddPreTargetHandler(&handler);
690
691  aura::test::EventGenerator event_generator(root_window, keyboard_window);
692  event_generator.ClickLeftButton();
693  int expected_mouse_presses = 1;
694  EXPECT_EQ(expected_mouse_presses, handler.num_mouse_events() / 2);
695
696  for (int block_reason = FIRST_BLOCK_REASON;
697       block_reason < NUMBER_OF_BLOCK_REASONS;
698       ++block_reason) {
699    BlockUserSession(static_cast<UserSessionBlockReason>(block_reason));
700    event_generator.ClickLeftButton();
701    expected_mouse_presses++;
702    EXPECT_EQ(expected_mouse_presses, handler.num_mouse_events() / 2);
703    UnblockUserSession();
704  }
705  root_window->RemovePreTargetHandler(&handler);
706}
707
708// Test for http://crbug.com/299787. RootWindowController should delete
709// the old container since the keyboard controller creates a new window in
710// GetWindowContainer().
711TEST_F(VirtualKeyboardRootWindowControllerTest,
712       DeleteOldContainerOnVirtualKeyboardInit) {
713  aura::Window* root_window = Shell::GetPrimaryRootWindow();
714  aura::Window* keyboard_container =
715      Shell::GetContainer(root_window, kShellWindowId_VirtualKeyboardContainer);
716  ASSERT_TRUE(keyboard_container);
717  // Track the keyboard container window.
718  aura::WindowTracker tracker;
719  tracker.Add(keyboard_container);
720  // Mock a login user profile change to reinitialize the keyboard.
721  ash::Shell::GetInstance()->OnLoginUserProfilePrepared();
722  // keyboard_container should no longer be present.
723  EXPECT_FALSE(tracker.Contains(keyboard_container));
724}
725
726// Test for crbug.com/342524. After user login, the work space should restore to
727// full screen.
728TEST_F(VirtualKeyboardRootWindowControllerTest, RestoreWorkspaceAfterLogin) {
729  aura::Window* root_window = Shell::GetPrimaryRootWindow();
730  aura::Window* keyboard_container =
731      Shell::GetContainer(root_window, kShellWindowId_VirtualKeyboardContainer);
732  keyboard_container->Show();
733  keyboard::KeyboardController* controller =
734      keyboard::KeyboardController::GetInstance();
735  aura::Window* keyboard_window = controller->proxy()->GetKeyboardWindow();
736  keyboard_container->AddChild(keyboard_window);
737  keyboard_window->set_owned_by_parent(false);
738  keyboard_window->SetBounds(keyboard::KeyboardBoundsFromWindowBounds(
739      keyboard_container->bounds(), 100));
740  keyboard_window->Show();
741
742  gfx::Rect before = ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
743
744  // Notify keyboard bounds changing.
745  controller->NotifyKeyboardBoundsChanging(
746      controller->proxy()->GetKeyboardWindow()->bounds());
747
748  if (!keyboard::IsKeyboardOverscrollEnabled()) {
749    gfx::Rect after = ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
750    EXPECT_LT(after, before);
751  }
752
753  // Mock a login user profile change to reinitialize the keyboard.
754  ash::Shell::GetInstance()->OnLoginUserProfilePrepared();
755  EXPECT_EQ(ash::Shell::GetScreen()->GetPrimaryDisplay().work_area(), before);
756}
757
758// Ensure that system modal dialogs do not block events targeted at the virtual
759// keyboard.
760TEST_F(VirtualKeyboardRootWindowControllerTest, ClickWithActiveModalDialog) {
761  aura::Window* root_window = Shell::GetPrimaryRootWindow();
762  aura::Window* keyboard_container =
763      Shell::GetContainer(root_window, kShellWindowId_VirtualKeyboardContainer);
764  ASSERT_TRUE(keyboard_container);
765  keyboard_container->Show();
766
767  aura::Window* keyboard_window = keyboard::KeyboardController::GetInstance()->
768      proxy()->GetKeyboardWindow();
769  keyboard_container->AddChild(keyboard_window);
770  keyboard_window->set_owned_by_parent(false);
771  keyboard_window->SetBounds(keyboard::KeyboardBoundsFromWindowBounds(
772      keyboard_container->bounds(), 100));
773
774  ui::test::TestEventHandler handler;
775  root_window->AddPreTargetHandler(&handler);
776  aura::test::EventGenerator root_window_event_generator(root_window);
777  aura::test::EventGenerator keyboard_event_generator(root_window,
778                                                      keyboard_window);
779
780  views::Widget* modal_widget =
781      CreateModalWidget(gfx::Rect(300, 10, 100, 100));
782
783  // Verify that mouse events to the root window are block with a visble modal
784  // dialog.
785  root_window_event_generator.ClickLeftButton();
786  EXPECT_EQ(0, handler.num_mouse_events());
787
788  // Verify that event dispatch to the virtual keyboard is unblocked.
789  keyboard_event_generator.ClickLeftButton();
790  EXPECT_EQ(1, handler.num_mouse_events() / 2);
791
792  modal_widget->Close();
793
794  // Verify that mouse events are now unblocked to the root window.
795  root_window_event_generator.ClickLeftButton();
796  EXPECT_EQ(2, handler.num_mouse_events() / 2);
797  root_window->RemovePreTargetHandler(&handler);
798}
799
800// Ensure that the visible area for scrolling the text caret excludes the
801// region occluded by the on-screen keyboard.
802TEST_F(VirtualKeyboardRootWindowControllerTest, EnsureCaretInWorkArea) {
803  keyboard::KeyboardController* keyboard_controller =
804      keyboard::KeyboardController::GetInstance();
805  keyboard::KeyboardControllerProxy* proxy = keyboard_controller->proxy();
806
807  MockTextInputClient text_input_client;
808  ui::InputMethod* input_method = proxy->GetInputMethod();
809  ASSERT_TRUE(input_method);
810  if (switches::IsTextInputFocusManagerEnabled()) {
811    ui::TextInputFocusManager::GetInstance()->FocusTextInputClient(
812        &text_input_client);
813  } else {
814    input_method->SetFocusedTextInputClient(&text_input_client);
815  }
816
817  aura::Window* root_window = Shell::GetPrimaryRootWindow();
818  aura::Window* keyboard_container =
819      Shell::GetContainer(root_window, kShellWindowId_VirtualKeyboardContainer);
820  ASSERT_TRUE(keyboard_container);
821  keyboard_container->Show();
822
823  const int keyboard_height = 100;
824  aura::Window* keyboard_window =proxy->GetKeyboardWindow();
825  keyboard_container->AddChild(keyboard_window);
826  keyboard_window->set_owned_by_parent(false);
827  keyboard_window->SetBounds(keyboard::KeyboardBoundsFromWindowBounds(
828      keyboard_container->bounds(), keyboard_height));
829
830  proxy->EnsureCaretInWorkArea();
831  ASSERT_EQ(keyboard_container->bounds().width(),
832            text_input_client.visible_rect().width());
833  ASSERT_EQ(keyboard_container->bounds().height() - keyboard_height,
834            text_input_client.visible_rect().height());
835
836  if (switches::IsTextInputFocusManagerEnabled()) {
837    ui::TextInputFocusManager::GetInstance()->BlurTextInputClient(
838        &text_input_client);
839  } else {
840    input_method->SetFocusedTextInputClient(NULL);
841  }
842}
843
844}  // namespace test
845}  // namespace ash
846