Home | History | Annotate | Download | only in ash
      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 
     26 namespace ash {
     27 namespace test {
     28 
     29 using aura::Window;
     30 
     31 namespace {
     32 
     33 StatusAreaWidgetDelegate* GetStatusAreaWidgetDelegate(views::Widget* widget) {
     34   return static_cast<StatusAreaWidgetDelegate*>(widget->GetContentsView());
     35 }
     36 
     37 class 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 
     65 class 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 
    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 
    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 
    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 
    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 
    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.
    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 
    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 
    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   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