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 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: 42 PanedWidgetDelegate(views::Widget* widget) : widget_(widget) {} 43 44 void SetAccessiblePanes(const std::vector<views::View*>& panes) { 45 accessible_panes_ = panes; 46 } 47 48 // views::WidgetDelegate. 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 } 54 virtual views::Widget* GetWidget() OVERRIDE { 55 return widget_; 56 }; 57 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: 70 FocusCyclerTest() {} 71 72 virtual void SetUp() OVERRIDE { 73 AshTestBase::SetUp(); 74 75 focus_cycler_.reset(new FocusCycler()); 76 77 ASSERT_TRUE(Launcher::ForPrimaryDisplay()); 78 } 79 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. 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 114 FocusCycler* focus_cycler() { return focus_cycler_.get(); } 115 116 SystemTray* tray() { return tray_.get(); } 117 118 ShelfWidget* shelf_widget() { 119 return Launcher::ForPrimaryDisplay()->shelf_widget(); 120 } 121 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 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 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 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 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 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 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 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 293 TEST_F(FocusCyclerTest, CycleFocusThroughWindowWithPanes) { 294 ASSERT_TRUE(CreateTray()); 295 296 InstallFocusCycleOnShelf(); 297 298 scoped_ptr<views::Widget> browser_widget(new views::Widget); 299 PanedWidgetDelegate* test_widget_delegate = 300 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; 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->set_focusable(true); 319 pane1->AddChildView(view1); 320 321 views::View* view2 = new views::View; 322 view2->set_focusable(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->set_focusable(true); 330 pane2->AddChildView(view3); 331 332 views::View* view4 = new views::View; 333 view4->set_focusable(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::RootWindow* 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