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