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/event_generator.h"
19 #include "ui/aura/test/test_windows.h"
20 #include "ui/aura/window.h"
21 #include "ui/aura/window_event_dispatcher.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
26 namespace ash {
27 namespace test {
28
29 using aura::Window;
30
31 namespace {
32
GetStatusAreaWidgetDelegate(views::Widget * widget)33 StatusAreaWidgetDelegate* GetStatusAreaWidgetDelegate(views::Widget* widget) {
34 return static_cast<StatusAreaWidgetDelegate*>(widget->GetContentsView());
35 }
36
37 class PanedWidgetDelegate : public views::WidgetDelegate {
38 public:
PanedWidgetDelegate(views::Widget * widget)39 PanedWidgetDelegate(views::Widget* widget) : widget_(widget) {}
40
SetAccessiblePanes(const std::vector<views::View * > & panes)41 void SetAccessiblePanes(const std::vector<views::View*>& panes) {
42 accessible_panes_ = panes;
43 }
44
45 // views::WidgetDelegate.
GetAccessiblePanes(std::vector<views::View * > * panes)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 }
GetWidget()51 virtual views::Widget* GetWidget() OVERRIDE {
52 return widget_;
53 };
GetWidget() const54 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
65 class FocusCyclerTest : public AshTestBase {
66 public:
FocusCyclerTest()67 FocusCyclerTest() {}
68
SetUp()69 virtual void SetUp() OVERRIDE {
70 AshTestBase::SetUp();
71
72 focus_cycler_.reset(new FocusCycler());
73
74 ASSERT_TRUE(Shelf::ForPrimaryDisplay());
75 }
76
TearDown()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.
CreateTray()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
focus_cycler()112 FocusCycler* focus_cycler() { return focus_cycler_.get(); }
113
tray()114 SystemTray* tray() { return tray_.get(); }
115
shelf_widget()116 ShelfWidget* shelf_widget() {
117 return Shelf::ForPrimaryDisplay()->shelf_widget();
118 }
119
InstallFocusCycleOnShelf()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
TEST_F(FocusCyclerTest,CycleFocusBrowserOnly)132 TEST_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
TEST_F(FocusCyclerTest,CycleFocusForward)143 TEST_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
TEST_F(FocusCyclerTest,CycleFocusBackward)166 TEST_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
TEST_F(FocusCyclerTest,CycleFocusForwardBackward)189 TEST_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
TEST_F(FocusCyclerTest,CycleFocusNoBrowser)224 TEST_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.
TEST_F(FocusCyclerTest,Shelf_CycleFocusForward)254 TEST_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
TEST_F(FocusCyclerTest,Shelf_CycleFocusBackwardInvisible)279 TEST_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
TEST_F(FocusCyclerTest,CycleFocusThroughWindowWithPanes)298 TEST_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 aura::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