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/focus_cycler.h"
6
7#include "ash/root_window_controller.h"
8#include "ash/shelf/shelf.h"
9#include "ash/shelf/shelf_widget.h"
10#include "ash/shell.h"
11#include "ash/shell_factory.h"
12#include "ash/shell_window_ids.h"
13#include "ash/system/status_area_widget.h"
14#include "ash/system/status_area_widget_delegate.h"
15#include "ash/system/tray/system_tray.h"
16#include "ash/test/ash_test_base.h"
17#include "ash/wm/window_util.h"
18#include "ui/aura/test/test_windows.h"
19#include "ui/aura/window.h"
20#include "ui/aura/window_event_dispatcher.h"
21#include "ui/events/test/event_generator.h"
22#include "ui/views/accessible_pane_view.h"
23#include "ui/views/controls/button/menu_button.h"
24#include "ui/views/widget/widget.h"
25
26namespace ash {
27namespace test {
28
29using aura::Window;
30
31namespace {
32
33StatusAreaWidgetDelegate* GetStatusAreaWidgetDelegate(views::Widget* widget) {
34  return static_cast<StatusAreaWidgetDelegate*>(widget->GetContentsView());
35}
36
37class PanedWidgetDelegate : public views::WidgetDelegate {
38 public:
39  PanedWidgetDelegate(views::Widget* widget) : widget_(widget) {}
40
41  void SetAccessiblePanes(const std::vector<views::View*>& panes) {
42    accessible_panes_ = panes;
43  }
44
45  // views::WidgetDelegate.
46  virtual void GetAccessiblePanes(std::vector<views::View*>* panes) OVERRIDE {
47    std::copy(accessible_panes_.begin(),
48              accessible_panes_.end(),
49              std::back_inserter(*panes));
50  }
51  virtual views::Widget* GetWidget() OVERRIDE {
52    return widget_;
53  };
54  virtual const views::Widget* GetWidget() const OVERRIDE {
55    return widget_;
56  }
57
58 private:
59  views::Widget* widget_;
60  std::vector<views::View*> accessible_panes_;
61};
62
63}  // namespace
64
65class FocusCyclerTest : public AshTestBase {
66 public:
67  FocusCyclerTest() {}
68
69  virtual void SetUp() OVERRIDE {
70    AshTestBase::SetUp();
71
72    focus_cycler_.reset(new FocusCycler());
73
74    ASSERT_TRUE(Shelf::ForPrimaryDisplay());
75  }
76
77  virtual void TearDown() OVERRIDE {
78    if (tray_) {
79      GetStatusAreaWidgetDelegate(tray_->GetWidget())->
80          SetFocusCyclerForTesting(NULL);
81      tray_.reset();
82    }
83
84    shelf_widget()->SetFocusCycler(NULL);
85
86    focus_cycler_.reset();
87
88    AshTestBase::TearDown();
89  }
90
91 protected:
92  // Creates the system tray, returning true on success.
93  bool CreateTray() {
94    if (tray_)
95      return false;
96    aura::Window* parent =
97        Shell::GetPrimaryRootWindowController()->GetContainer(
98            ash::kShellWindowId_StatusContainer);
99
100    StatusAreaWidget* widget = new StatusAreaWidget(parent);
101    widget->CreateTrayViews();
102    widget->Show();
103    tray_.reset(widget->system_tray());
104    if (!tray_->GetWidget())
105      return false;
106    focus_cycler_->AddWidget(tray()->GetWidget());
107    GetStatusAreaWidgetDelegate(tray_->GetWidget())->SetFocusCyclerForTesting(
108        focus_cycler());
109    return true;
110  }
111
112  FocusCycler* focus_cycler() { return focus_cycler_.get(); }
113
114  SystemTray* tray() { return tray_.get(); }
115
116  ShelfWidget* shelf_widget() {
117    return Shelf::ForPrimaryDisplay()->shelf_widget();
118  }
119
120  void InstallFocusCycleOnShelf() {
121    // Add the shelf.
122    shelf_widget()->SetFocusCycler(focus_cycler());
123  }
124
125 private:
126  scoped_ptr<FocusCycler> focus_cycler_;
127  scoped_ptr<SystemTray> tray_;
128
129  DISALLOW_COPY_AND_ASSIGN(FocusCyclerTest);
130};
131
132TEST_F(FocusCyclerTest, CycleFocusBrowserOnly) {
133  // Create a single test window.
134  scoped_ptr<Window> window0(CreateTestWindowInShellWithId(0));
135  wm::ActivateWindow(window0.get());
136  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
137
138  // Cycle the window
139  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
140  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
141}
142
143TEST_F(FocusCyclerTest, CycleFocusForward) {
144  ASSERT_TRUE(CreateTray());
145
146  InstallFocusCycleOnShelf();
147
148  // Create a single test window.
149  scoped_ptr<Window> window0(CreateTestWindowInShellWithId(0));
150  wm::ActivateWindow(window0.get());
151  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
152
153  // Cycle focus to the status area.
154  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
155  EXPECT_TRUE(tray()->GetWidget()->IsActive());
156
157  // Cycle focus to the shelf.
158  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
159  EXPECT_TRUE(shelf_widget()->IsActive());
160
161  // Cycle focus to the browser.
162  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
163  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
164}
165
166TEST_F(FocusCyclerTest, CycleFocusBackward) {
167  ASSERT_TRUE(CreateTray());
168
169  InstallFocusCycleOnShelf();
170
171  // Create a single test window.
172  scoped_ptr<Window> window0(CreateTestWindowInShellWithId(0));
173  wm::ActivateWindow(window0.get());
174  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
175
176  // Cycle focus to the shelf.
177  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
178  EXPECT_TRUE(shelf_widget()->IsActive());
179
180  // Cycle focus to the status area.
181  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
182  EXPECT_TRUE(tray()->GetWidget()->IsActive());
183
184  // Cycle focus to the browser.
185  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
186  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
187}
188
189TEST_F(FocusCyclerTest, CycleFocusForwardBackward) {
190  ASSERT_TRUE(CreateTray());
191
192  InstallFocusCycleOnShelf();
193
194  // Create a single test window.
195  scoped_ptr<Window> window0(CreateTestWindowInShellWithId(0));
196  wm::ActivateWindow(window0.get());
197  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
198
199  // Cycle focus to the shelf.
200  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
201  EXPECT_TRUE(shelf_widget()->IsActive());
202
203  // Cycle focus to the status area.
204  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
205  EXPECT_TRUE(tray()->GetWidget()->IsActive());
206
207  // Cycle focus to the browser.
208  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
209  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
210
211  // Cycle focus to the status area.
212  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
213  EXPECT_TRUE(tray()->GetWidget()->IsActive());
214
215  // Cycle focus to the shelf.
216  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
217  EXPECT_TRUE(shelf_widget()->IsActive());
218
219  // Cycle focus to the browser.
220  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
221  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
222}
223
224TEST_F(FocusCyclerTest, CycleFocusNoBrowser) {
225  ASSERT_TRUE(CreateTray());
226
227  InstallFocusCycleOnShelf();
228
229  // Add the shelf and focus it.
230  focus_cycler()->FocusWidget(shelf_widget());
231
232  // Cycle focus to the status area.
233  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
234  EXPECT_TRUE(tray()->GetWidget()->IsActive());
235
236  // Cycle focus to the shelf.
237  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
238  EXPECT_TRUE(shelf_widget()->IsActive());
239
240  // Cycle focus to the status area.
241  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
242  EXPECT_TRUE(tray()->GetWidget()->IsActive());
243
244  // Cycle focus to the shelf.
245  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
246  EXPECT_TRUE(shelf_widget()->IsActive());
247
248  // Cycle focus to the status area.
249  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
250  EXPECT_TRUE(tray()->GetWidget()->IsActive());
251}
252
253// Tests that focus cycles from the active browser to the status area and back.
254TEST_F(FocusCyclerTest, Shelf_CycleFocusForward) {
255  ASSERT_TRUE(CreateTray());
256  InstallFocusCycleOnShelf();
257  shelf_widget()->Hide();
258
259  // Create two test windows.
260  scoped_ptr<Window> window0(CreateTestWindowInShellWithId(0));
261  scoped_ptr<Window> window1(CreateTestWindowInShellWithId(1));
262  wm::ActivateWindow(window1.get());
263  wm::ActivateWindow(window0.get());
264  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
265
266  // Cycle focus to the status area.
267  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
268  EXPECT_TRUE(tray()->GetWidget()->IsActive());
269
270  // Cycle focus to the browser.
271  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
272  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
273
274  // Cycle focus to the status area.
275  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
276  EXPECT_TRUE(tray()->GetWidget()->IsActive());
277}
278
279TEST_F(FocusCyclerTest, Shelf_CycleFocusBackwardInvisible) {
280  ASSERT_TRUE(CreateTray());
281  InstallFocusCycleOnShelf();
282  shelf_widget()->Hide();
283
284  // Create a single test window.
285  scoped_ptr<Window> window0(CreateTestWindowInShellWithId(0));
286  wm::ActivateWindow(window0.get());
287  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
288
289  // Cycle focus to the status area.
290  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
291  EXPECT_TRUE(tray()->GetWidget()->IsActive());
292
293  // Cycle focus to the browser.
294  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
295  EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
296}
297
298TEST_F(FocusCyclerTest, CycleFocusThroughWindowWithPanes) {
299  ASSERT_TRUE(CreateTray());
300
301  InstallFocusCycleOnShelf();
302
303  scoped_ptr<PanedWidgetDelegate> test_widget_delegate;
304  scoped_ptr<views::Widget> browser_widget(new views::Widget);
305  test_widget_delegate.reset(new PanedWidgetDelegate(browser_widget.get()));
306  views::Widget::InitParams widget_params(
307      views::Widget::InitParams::TYPE_WINDOW);
308  widget_params.context = CurrentContext();
309  widget_params.delegate = test_widget_delegate.get();
310  widget_params.ownership =
311      views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
312  browser_widget->Init(widget_params);
313  browser_widget->Show();
314
315  aura::Window* browser_window = browser_widget->GetNativeView();
316
317  views::View* root_view = browser_widget->GetRootView();
318
319  views::AccessiblePaneView* pane1 = new views::AccessiblePaneView();
320  root_view->AddChildView(pane1);
321
322  views::View* view1 = new views::View;
323  view1->SetFocusable(true);
324  pane1->AddChildView(view1);
325
326  views::View* view2 = new views::View;
327  view2->SetFocusable(true);
328  pane1->AddChildView(view2);
329
330  views::AccessiblePaneView* pane2 = new views::AccessiblePaneView();
331  root_view->AddChildView(pane2);
332
333  views::View* view3 = new views::View;
334  view3->SetFocusable(true);
335  pane2->AddChildView(view3);
336
337  views::View* view4 = new views::View;
338  view4->SetFocusable(true);
339  pane2->AddChildView(view4);
340
341  std::vector<views::View*> panes;
342  panes.push_back(pane1);
343  panes.push_back(pane2);
344
345  test_widget_delegate->SetAccessiblePanes(panes);
346
347  views::FocusManager* focus_manager = browser_widget->GetFocusManager();
348
349  // Cycle focus to the status area.
350  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
351  EXPECT_TRUE(tray()->GetWidget()->IsActive());
352
353  // Cycle focus to the shelf.
354  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
355  EXPECT_TRUE(shelf_widget()->IsActive());
356
357  // Cycle focus to the first pane in the browser.
358  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
359  EXPECT_TRUE(wm::IsActiveWindow(browser_window));
360  EXPECT_EQ(focus_manager->GetFocusedView(), view1);
361
362  // Cycle focus to the second pane in the browser.
363  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
364  EXPECT_TRUE(wm::IsActiveWindow(browser_window));
365  EXPECT_EQ(focus_manager->GetFocusedView(), view3);
366
367  // Cycle focus back to the status area.
368  focus_cycler()->RotateFocus(FocusCycler::FORWARD);
369  EXPECT_TRUE(tray()->GetWidget()->IsActive());
370
371  // Reverse direction - back to the second pane in the browser.
372  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
373  EXPECT_TRUE(wm::IsActiveWindow(browser_window));
374  EXPECT_EQ(focus_manager->GetFocusedView(), view3);
375
376  // Back to the first pane in the browser.
377  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
378  EXPECT_TRUE(wm::IsActiveWindow(browser_window));
379  EXPECT_EQ(focus_manager->GetFocusedView(), view1);
380
381  // Back to the shelf.
382  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
383  EXPECT_TRUE(shelf_widget()->IsActive());
384
385  // Back to the status area.
386  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
387  EXPECT_TRUE(tray()->GetWidget()->IsActive());
388
389  // Pressing "Escape" while on the status area should
390  // deactivate it, and activate the browser window.
391  aura::Window* root = Shell::GetPrimaryRootWindow();
392  ui::test::EventGenerator event_generator(root, root);
393  event_generator.PressKey(ui::VKEY_ESCAPE, 0);
394  EXPECT_TRUE(wm::IsActiveWindow(browser_window));
395  EXPECT_EQ(focus_manager->GetFocusedView(), view1);
396
397  // Similarly, pressing "Escape" while on the shelf.
398  // should do the same thing.
399  focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
400  EXPECT_TRUE(shelf_widget()->IsActive());
401  event_generator.PressKey(ui::VKEY_ESCAPE, 0);
402  EXPECT_TRUE(wm::IsActiveWindow(browser_window));
403  EXPECT_EQ(focus_manager->GetFocusedView(), view1);
404}
405
406}  // namespace test
407}  // namespace ash
408