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