summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-30 01:04:08 +0000
committerdmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-30 01:04:08 +0000
commit242aa4f94fa0bcc9558e17217c464cf5a98f790b (patch)
tree49c51ac5c88c2ead11294d9406dcb6026e50b6a3
parentda0ade73522ccbb0284cedf2764a68465f691dc5 (diff)
downloadchromium_src-242aa4f94fa0bcc9558e17217c464cf5a98f790b.zip
chromium_src-242aa4f94fa0bcc9558e17217c464cf5a98f790b.tar.gz
chromium_src-242aa4f94fa0bcc9558e17217c464cf5a98f790b.tar.bz2
Fix cycling of focusable panes in Chrome OS.
Update the ash window cycle controller so it can tell aura::Window to rotate pane focus and pass it the cycle direction, rather than just activating each window. The end result is that when you press Ctrl+Back and Ctrl+Forward you get a consistent cycle now, going through all panes within the most recently active browser window and then the launcher and status area. BUG=154598 Review URL: https://chromiumcodereview.appspot.com/13861032 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@197199 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ash/accelerators/accelerator_controller.cc19
-rw-r--r--ash/focus_cycler.cc35
-rw-r--r--ash/focus_cycler_unittest.cc136
-rw-r--r--ash/launcher/launcher.cc4
-rw-r--r--ash/launcher/launcher.h2
-rw-r--r--ash/shell/shell_delegate_impl.cc4
-rw-r--r--ash/shell/shell_delegate_impl.h1
-rw-r--r--ash/shell_delegate.h4
-rw-r--r--ash/system/status_area_widget.cc4
-rw-r--r--ash/system/status_area_widget.h2
-rw-r--r--ash/test/test_shell_delegate.cc4
-rw-r--r--ash/test/test_shell_delegate.h1
-rw-r--r--chrome/browser/chromeos/system/ash_focus_cycle_browsertest.cc110
-rw-r--r--chrome/browser/ui/ash/chrome_shell_delegate.cc20
-rw-r--r--chrome/browser/ui/ash/chrome_shell_delegate.h1
-rw-r--r--chrome/browser/ui/views/frame/browser_frame_aura.cc5
-rw-r--r--chrome/browser/ui/views/frame/browser_frame_aura.h2
-rw-r--r--chrome/browser/ui/views/frame/browser_view.cc104
-rw-r--r--chrome/browser/ui/views/frame/browser_view.h1
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--ui/views/accessible_pane_view.cc4
-rw-r--r--ui/views/accessible_pane_view.h1
-rw-r--r--ui/views/focus/focus_manager.cc59
-rw-r--r--ui/views/focus/focus_manager.h20
-rw-r--r--ui/views/focus/focus_manager_test.cc11
-rw-r--r--ui/views/focus/focus_manager_test.h5
-rw-r--r--ui/views/focus/focus_manager_unittest.cc73
-rw-r--r--ui/views/view.h2
-rw-r--r--ui/views/widget/widget_delegate.h5
29 files changed, 391 insertions, 249 deletions
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index 19f3f3f..d813b41 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -121,17 +121,14 @@ bool HandleToggleSpokenFeedback() {
#endif // defined(OS_CHROMEOS)
bool HandleRotatePaneFocus(Shell::Direction direction) {
- if (!Shell::GetInstance()->delegate()->RotatePaneFocus(direction)) {
- // No browser window is available. Focus the launcher.
- Shell* shell = Shell::GetInstance();
- switch (direction) {
- case Shell::FORWARD:
- shell->focus_cycler()->RotateFocus(internal::FocusCycler::FORWARD);
- break;
- case Shell::BACKWARD:
- shell->focus_cycler()->RotateFocus(internal::FocusCycler::BACKWARD);
- break;
- }
+ Shell* shell = Shell::GetInstance();
+ switch (direction) {
+ case Shell::FORWARD:
+ shell->focus_cycler()->RotateFocus(internal::FocusCycler::FORWARD);
+ break;
+ case Shell::BACKWARD:
+ shell->focus_cycler()->RotateFocus(internal::FocusCycler::BACKWARD);
+ break;
}
return true;
}
diff --git a/ash/focus_cycler.cc b/ash/focus_cycler.cc
index ee9d22f..81611dc 100644
--- a/ash/focus_cycler.cc
+++ b/ash/focus_cycler.cc
@@ -6,6 +6,7 @@
#include "ash/shell.h"
#include "ash/wm/window_cycle_controller.h"
+#include "ash/wm/window_util.h"
#include "ui/aura/client/activation_client.h"
#include "ui/aura/window.h"
#include "ui/views/accessible_pane_view.h"
@@ -35,6 +36,19 @@ void FocusCycler::AddWidget(views::Widget* widget) {
}
void FocusCycler::RotateFocus(Direction direction) {
+ aura::Window* window = ash::wm::GetActiveWindow();
+ if (window) {
+ views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
+ // First try to rotate focus within the active widget. If that succeeds,
+ // we're done.
+ if (widget && widget->GetFocusManager()->RotatePaneFocus(
+ direction == BACKWARD ?
+ views::FocusManager::kBackward : views::FocusManager::kForward,
+ views::FocusManager::kNoWrap)) {
+ return;
+ }
+ }
+
const bool has_window = HasFocusableWindow();
int index = 0;
int count = static_cast<int>(widgets_.size());
@@ -61,12 +75,23 @@ void FocusCycler::RotateFocus(Direction direction) {
break;
if (index == browser_index) {
- // Activate the first window.
- WindowCycleController::Direction window_direction =
- direction == FORWARD ? WindowCycleController::FORWARD :
- WindowCycleController::BACKWARD;
+ // Activate the most recently active browser window.
ash::Shell::GetInstance()->window_cycle_controller()->HandleCycleWindow(
- window_direction, false);
+ WindowCycleController::FORWARD, false);
+
+ // Rotate pane focus within that window.
+ aura::Window* window = ash::wm::GetActiveWindow();
+ if (!window)
+ break;
+ views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
+ if (!widget)
+ break;
+ views::FocusManager* focus_manager = widget->GetFocusManager();
+ focus_manager->ClearFocus();
+ focus_manager->RotatePaneFocus(
+ direction == BACKWARD ?
+ views::FocusManager::kBackward : views::FocusManager::kForward,
+ views::FocusManager::kWrap);
break;
} else {
if (FocusWidget(widgets_[index]))
diff --git a/ash/focus_cycler_unittest.cc b/ash/focus_cycler_unittest.cc
index 0ff7a9b..5854f8d 100644
--- a/ash/focus_cycler_unittest.cc
+++ b/ash/focus_cycler_unittest.cc
@@ -15,8 +15,11 @@
#include "ash/wm/window_util.h"
#include "ash/test/ash_test_base.h"
#include "ash/shell_factory.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/test/event_generator.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/window.h"
+#include "ui/views/accessible_pane_view.h"
#include "ui/views/controls/button/menu_button.h"
#include "ui/views/widget/widget.h"
@@ -34,6 +37,31 @@ internal::StatusAreaWidgetDelegate* GetStatusAreaWidgetDelegate(
widget->GetContentsView());
}
+class PanedWidgetDelegate : public views::WidgetDelegate {
+ public:
+ PanedWidgetDelegate(views::Widget* widget) : widget_(widget) {}
+
+ void SetAccessiblePanes(const std::vector<views::View*>& panes) {
+ accessible_panes_ = panes;
+ }
+
+ // views::WidgetDelegate.
+ virtual void GetAccessiblePanes(std::vector<views::View*>* panes) {
+ std::copy(accessible_panes_.begin(), accessible_panes_.end(),
+ std::back_inserter(*panes));
+ }
+ virtual views::Widget* GetWidget() OVERRIDE {
+ return widget_;
+ };
+ virtual const views::Widget* GetWidget() const OVERRIDE {
+ return widget_;
+ }
+
+ private:
+ views::Widget* widget_;
+ std::vector<views::View*> accessible_panes_;
+};
+
} // namespace
class FocusCyclerTest : public AshTestBase {
@@ -261,5 +289,113 @@ TEST_F(FocusCyclerTest, Shelf_CycleFocusBackwardInvisible) {
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
}
+TEST_F(FocusCyclerTest, CycleFocusThroughWindowWithPanes) {
+ ASSERT_TRUE(CreateTray());
+
+ InstallFocusCycleOnShelf();
+
+ scoped_ptr<views::Widget> browser_widget(new views::Widget);
+ PanedWidgetDelegate* test_widget_delegate =
+ new PanedWidgetDelegate(browser_widget.get());
+ views::Widget::InitParams widget_params(
+ views::Widget::InitParams::TYPE_WINDOW);
+ widget_params.context = CurrentContext();
+ widget_params.delegate = test_widget_delegate;
+ widget_params.ownership =
+ views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ browser_widget->Init(widget_params);
+ browser_widget->Show();
+
+ aura::Window* browser_window = browser_widget->GetNativeView();
+
+ views::View* root_view = browser_widget->GetRootView();
+
+ views::AccessiblePaneView* pane1 = new views::AccessiblePaneView();
+ root_view->AddChildView(pane1);
+
+ views::View* view1 = new views::View;
+ view1->set_focusable(true);
+ pane1->AddChildView(view1);
+
+ views::View* view2 = new views::View;
+ view2->set_focusable(true);
+ pane1->AddChildView(view2);
+
+ views::AccessiblePaneView* pane2 = new views::AccessiblePaneView();
+ root_view->AddChildView(pane2);
+
+ views::View* view3 = new views::View;
+ view3->set_focusable(true);
+ pane2->AddChildView(view3);
+
+ views::View* view4 = new views::View;
+ view4->set_focusable(true);
+ pane2->AddChildView(view4);
+
+ std::vector<views::View*> panes;
+ panes.push_back(pane1);
+ panes.push_back(pane2);
+
+ test_widget_delegate->SetAccessiblePanes(panes);
+
+ views::FocusManager* focus_manager = browser_widget->GetFocusManager();
+
+ // Cycle focus to the status area.
+ focus_cycler()->RotateFocus(FocusCycler::FORWARD);
+ EXPECT_TRUE(tray()->GetWidget()->IsActive());
+
+ // Cycle focus to the shelf.
+ focus_cycler()->RotateFocus(FocusCycler::FORWARD);
+ EXPECT_TRUE(shelf_widget()->IsActive());
+
+ // Cycle focus to the first pane in the browser.
+ focus_cycler()->RotateFocus(FocusCycler::FORWARD);
+ EXPECT_TRUE(wm::IsActiveWindow(browser_window));
+ EXPECT_EQ(focus_manager->GetFocusedView(), view1);
+
+ // Cycle focus to the second pane in the browser.
+ focus_cycler()->RotateFocus(FocusCycler::FORWARD);
+ EXPECT_TRUE(wm::IsActiveWindow(browser_window));
+ EXPECT_EQ(focus_manager->GetFocusedView(), view3);
+
+ // Cycle focus back to the status area.
+ focus_cycler()->RotateFocus(FocusCycler::FORWARD);
+ EXPECT_TRUE(tray()->GetWidget()->IsActive());
+
+ // Reverse direction - back to the second pane in the browser.
+ focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
+ EXPECT_TRUE(wm::IsActiveWindow(browser_window));
+ EXPECT_EQ(focus_manager->GetFocusedView(), view3);
+
+ // Back to the first pane in the browser.
+ focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
+ EXPECT_TRUE(wm::IsActiveWindow(browser_window));
+ EXPECT_EQ(focus_manager->GetFocusedView(), view1);
+
+ // Back to the shelf.
+ focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
+ EXPECT_TRUE(shelf_widget()->IsActive());
+
+ // Back to the status area.
+ focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
+ EXPECT_TRUE(tray()->GetWidget()->IsActive());
+
+ // Pressing "Escape" while on the status area should
+ // deactivate it, and activate the browser window.
+ aura::RootWindow* root = Shell::GetPrimaryRootWindow();
+ aura::test::EventGenerator event_generator(root, root);
+ event_generator.PressKey(ui::VKEY_ESCAPE, 0);
+ EXPECT_TRUE(wm::IsActiveWindow(browser_window));
+ EXPECT_EQ(focus_manager->GetFocusedView(), view1);
+
+ // Similarly, pressing "Escape" while on the shelf.
+ // should do the same thing.
+ focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
+ EXPECT_TRUE(shelf_widget()->IsActive());
+ event_generator.PressKey(ui::VKEY_ESCAPE, 0);
+ EXPECT_TRUE(wm::IsActiveWindow(browser_window));
+ EXPECT_EQ(focus_manager->GetFocusedView(), view1);
+}
+
} // namespace test
} // namespace ash
diff --git a/ash/launcher/launcher.cc b/ash/launcher/launcher.cc
index 226432c..57d9184 100644
--- a/ash/launcher/launcher.cc
+++ b/ash/launcher/launcher.cc
@@ -38,6 +38,8 @@
namespace ash {
+const char Launcher::kNativeViewName[] = "LauncherView";
+
Launcher::Launcher(LauncherModel* launcher_model,
LauncherDelegate* launcher_delegate,
ShelfWidget* shelf_widget)
@@ -49,7 +51,7 @@ Launcher::Launcher(LauncherModel* launcher_model,
launcher_model, delegate_, shelf_widget_->shelf_layout_manager());
launcher_view_->Init();
shelf_widget_->GetContentsView()->AddChildView(launcher_view_);
- shelf_widget_->GetNativeView()->SetName("LauncherView");
+ shelf_widget_->GetNativeView()->SetName(kNativeViewName);
shelf_widget_->GetNativeView()->SetProperty(
internal::kStayInSameRootWindowKey, true);
delegate_->OnLauncherCreated(this);
diff --git a/ash/launcher/launcher.h b/ash/launcher/launcher.h
index 8c61ce3..7d7f3cd 100644
--- a/ash/launcher/launcher.h
+++ b/ash/launcher/launcher.h
@@ -40,6 +40,8 @@ class ShelfWidget;
class ASH_EXPORT Launcher {
public:
+ static const char kNativeViewName[];
+
Launcher(LauncherModel* launcher_model,
LauncherDelegate* launcher_delegate,
ShelfWidget* shelf_widget);
diff --git a/ash/shell/shell_delegate_impl.cc b/ash/shell/shell_delegate_impl.cc
index 25d092a..cabd683 100644
--- a/ash/shell/shell_delegate_impl.cc
+++ b/ash/shell/shell_delegate_impl.cc
@@ -119,10 +119,6 @@ void ShellDelegateImpl::OpenMobileSetup(const std::string& service_path) {
void ShellDelegateImpl::RestoreTab() {
}
-bool ShellDelegateImpl::RotatePaneFocus(Shell::Direction direction) {
- return true;
-}
-
void ShellDelegateImpl::ShowKeyboardOverlay() {
}
diff --git a/ash/shell/shell_delegate_impl.h b/ash/shell/shell_delegate_impl.h
index ddd6b95..a74fd7f 100644
--- a/ash/shell/shell_delegate_impl.h
+++ b/ash/shell/shell_delegate_impl.h
@@ -41,7 +41,6 @@ class ShellDelegateImpl : public ash::ShellDelegate {
virtual void OpenCrosh() OVERRIDE;
virtual void OpenMobileSetup(const std::string& service_path) OVERRIDE;
virtual void RestoreTab() OVERRIDE;
- virtual bool RotatePaneFocus(Shell::Direction direction) OVERRIDE;
virtual void ShowKeyboardOverlay() OVERRIDE;
virtual keyboard::KeyboardControllerProxy*
CreateKeyboardControllerProxy() OVERRIDE;
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index c561bf1..5edc151 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -145,10 +145,6 @@ class ASH_EXPORT ShellDelegate {
// Invoked when the user uses Shift+Ctrl+T to restore the closed tab.
virtual void RestoreTab() = 0;
- // Moves keyboard focus to the next pane. Returns false if no browser window
- // is created.
- virtual bool RotatePaneFocus(Shell::Direction direction) = 0;
-
// Shows the keyboard shortcut overlay.
virtual void ShowKeyboardOverlay() = 0;
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index f818cbf..1430bd1 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -24,6 +24,8 @@ namespace ash {
namespace internal {
+const char StatusAreaWidget::kNativeViewName[] = "StatusAreaWidget";
+
StatusAreaWidget::StatusAreaWidget(aura::Window* status_container)
: status_area_widget_delegate_(new internal::StatusAreaWidgetDelegate),
system_tray_(NULL),
@@ -37,7 +39,7 @@ StatusAreaWidget::StatusAreaWidget(aura::Window* status_container)
Init(params);
set_focus_on_creation(false);
SetContentsView(status_area_widget_delegate_);
- GetNativeView()->SetName("StatusAreaWidget");
+ GetNativeView()->SetName(kNativeViewName);
GetNativeView()->SetProperty(internal::kStayInSameRootWindowKey, true);
}
diff --git a/ash/system/status_area_widget.h b/ash/system/status_area_widget.h
index 78a959d..4056710 100644
--- a/ash/system/status_area_widget.h
+++ b/ash/system/status_area_widget.h
@@ -22,6 +22,8 @@ class StatusAreaWidgetDelegate;
class ASH_EXPORT StatusAreaWidget : public views::Widget {
public:
+ static const char kNativeViewName[];
+
explicit StatusAreaWidget(aura::Window* status_container);
virtual ~StatusAreaWidget();
diff --git a/ash/test/test_shell_delegate.cc b/ash/test/test_shell_delegate.cc
index 17792c7..f37f851 100644
--- a/ash/test/test_shell_delegate.cc
+++ b/ash/test/test_shell_delegate.cc
@@ -82,10 +82,6 @@ void TestShellDelegate::OpenMobileSetup(const std::string& service_path) {
void TestShellDelegate::RestoreTab() {
}
-bool TestShellDelegate::RotatePaneFocus(Shell::Direction direction) {
- return true;
-}
-
void TestShellDelegate::ShowKeyboardOverlay() {
}
diff --git a/ash/test/test_shell_delegate.h b/ash/test/test_shell_delegate.h
index 04f2997..2603a2a 100644
--- a/ash/test/test_shell_delegate.h
+++ b/ash/test/test_shell_delegate.h
@@ -40,7 +40,6 @@ class TestShellDelegate : public ShellDelegate {
virtual void OpenCrosh() OVERRIDE;
virtual void OpenMobileSetup(const std::string& service_path) OVERRIDE;
virtual void RestoreTab() OVERRIDE;
- virtual bool RotatePaneFocus(Shell::Direction direction) OVERRIDE;
virtual void ShowKeyboardOverlay() OVERRIDE;
virtual keyboard::KeyboardControllerProxy*
CreateKeyboardControllerProxy() OVERRIDE;
diff --git a/chrome/browser/chromeos/system/ash_focus_cycle_browsertest.cc b/chrome/browser/chromeos/system/ash_focus_cycle_browsertest.cc
deleted file mode 100644
index 6a909cf..0000000
--- a/chrome/browser/chromeos/system/ash_focus_cycle_browsertest.cc
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/focus_cycler.h"
-#include "ash/shell.h"
-#include "ash/wm/window_util.h"
-#include "base/bind.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/run_loop.h"
-#include "base/stringprintf.h"
-#include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h"
-#include "chrome/test/base/ui_controls.h"
-#include "content/public/test/test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/aura/window.h"
-#include "ui/base/accessibility/accessibility_types.h"
-#include "ui/base/accessibility/accessible_view_state.h"
-#include "ui/views/focus/focus_manager.h"
-#include "ui/views/widget/widget.h"
-
-namespace chromeos {
-
-class AshFocusCycleTest : public CrosInProcessBrowserTest {
- protected:
- AshFocusCycleTest() {}
- virtual ~AshFocusCycleTest() {}
-
- void CheckFocusedViewIsAccessible() {
- SCOPED_TRACE(base::StringPrintf(
- "while active window is %s",
- ash::wm::GetActiveWindow()->name().c_str()));
- aura::Window* window = ash::wm::GetActiveWindow();
- CHECK(window);
- views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
- CHECK(widget);
- views::FocusManager* focus_manager = widget->GetFocusManager();
- CHECK(focus_manager);
- views::View* view = focus_manager->GetFocusedView();
- ASSERT_TRUE(view != NULL);
- ui::AccessibleViewState state;
- view->GetAccessibleState(&state);
- ASSERT_NE(state.role, ui::AccessibilityTypes::ROLE_CLIENT);
- ASSERT_FALSE(state.name.empty());
- }
-
- void PressEscape() {
- aura::Window* window = ash::wm::GetActiveWindow();
- CHECK(window);
- wait_for_key_press_.reset(new base::RunLoop());
- ui_controls::SendKeyPressNotifyWhenDone(
- window, ui::VKEY_ESCAPE,
- false, false, false, false,
- base::Bind(&AshFocusCycleTest::QuitWaiting, base::Unretained(this)));
- wait_for_key_press_->Run();
- }
-
- private:
- scoped_ptr<base::RunLoop> wait_for_key_press_;
-
- void QuitWaiting() {
- wait_for_key_press_->Quit();
- }
-};
-
-IN_PROC_BROWSER_TEST_F(AshFocusCycleTest, CycleFocus) {
- // Initially, the browser frame should have focus.
- ASSERT_STREQ("BrowserFrameAura", ash::wm::GetActiveWindow()->name().c_str());
- CheckFocusedViewIsAccessible();
-
- // Rotate focus, as if the user pressed Ctrl+Forward (Ctrl+F2).
- // The first item focused should be in the system tray.
- ash::Shell* shell = ash::Shell::GetInstance();
- shell->RotateFocus(ash::Shell::FORWARD);
- ASSERT_STREQ("StatusAreaWidget", ash::wm::GetActiveWindow()->name().c_str());
- CheckFocusedViewIsAccessible();
-
- // Rotate focus again, now we should have something in the launcher focused.
- shell->RotateFocus(ash::Shell::FORWARD);
- ASSERT_STREQ("LauncherView", ash::wm::GetActiveWindow()->name().c_str());
- CheckFocusedViewIsAccessible();
-
- // Finally we should be back at the browser frame.
- shell->RotateFocus(ash::Shell::FORWARD);
- ASSERT_STREQ("BrowserFrameAura", ash::wm::GetActiveWindow()->name().c_str());
- CheckFocusedViewIsAccessible();
-}
-
-IN_PROC_BROWSER_TEST_F(AshFocusCycleTest, EscapeFromSystemTray) {
- ash::Shell* shell = ash::Shell::GetInstance();
- shell->RotateFocus(ash::Shell::FORWARD);
- ASSERT_STREQ("StatusAreaWidget", ash::wm::GetActiveWindow()->name().c_str());
-
- PressEscape();
- ASSERT_STREQ("BrowserFrameAura", ash::wm::GetActiveWindow()->name().c_str());
- CheckFocusedViewIsAccessible();
-}
-
-IN_PROC_BROWSER_TEST_F(AshFocusCycleTest, EscapeFromLauncher) {
- ash::Shell* shell = ash::Shell::GetInstance();
- shell->RotateFocus(ash::Shell::FORWARD);
- shell->RotateFocus(ash::Shell::FORWARD);
- ASSERT_STREQ("LauncherView", ash::wm::GetActiveWindow()->name().c_str());
-
- PressEscape();
- ASSERT_STREQ("BrowserFrameAura", ash::wm::GetActiveWindow()->name().c_str());
- CheckFocusedViewIsAccessible();
-}
-
-} // namespace chromeos
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index d6de758..07cf21f 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -168,26 +168,6 @@ void ChromeShellDelegate::RestoreTab() {
}
}
-bool ChromeShellDelegate::RotatePaneFocus(ash::Shell::Direction direction) {
- aura::Window* window = ash::wm::GetActiveWindow();
- if (!window)
- return false;
-
- Browser* browser = chrome::FindBrowserWithWindow(window);
- if (!browser)
- return false;
-
- switch (direction) {
- case ash::Shell::FORWARD:
- chrome::FocusNextPane(browser);
- break;
- case ash::Shell::BACKWARD:
- chrome::FocusPreviousPane(browser);
- break;
- }
- return true;
-}
-
void ChromeShellDelegate::ShowTaskManager() {
chrome::OpenTaskManager(NULL, false);
}
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h
index 7357130..7ec4365 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.h
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -61,7 +61,6 @@ class ChromeShellDelegate : public ash::ShellDelegate,
virtual void OpenCrosh() OVERRIDE;
virtual void OpenMobileSetup(const std::string& service_path) OVERRIDE;
virtual void RestoreTab() OVERRIDE;
- virtual bool RotatePaneFocus(ash::Shell::Direction direction) OVERRIDE;
virtual void ShowKeyboardOverlay() OVERRIDE;
virtual keyboard::KeyboardControllerProxy*
CreateKeyboardControllerProxy() OVERRIDE;
diff --git a/chrome/browser/ui/views/frame/browser_frame_aura.cc b/chrome/browser/ui/views/frame/browser_frame_aura.cc
index 61fd350..a9688e1 100644
--- a/chrome/browser/ui/views/frame/browser_frame_aura.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_aura.cc
@@ -91,12 +91,15 @@ class BrowserFrameAura::WindowPropertyWatcher : public aura::WindowObserver {
///////////////////////////////////////////////////////////////////////////////
// BrowserFrameAura, public:
+// static
+const char BrowserFrameAura::kWindowName[] = "BrowserFrameAura";
+
BrowserFrameAura::BrowserFrameAura(BrowserFrame* browser_frame,
BrowserView* browser_view)
: views::NativeWidgetAura(browser_frame),
browser_view_(browser_view),
window_property_watcher_(new WindowPropertyWatcher(this, browser_frame)) {
- GetNativeWindow()->SetName("BrowserFrameAura");
+ GetNativeWindow()->SetName(kWindowName);
GetNativeWindow()->AddObserver(window_property_watcher_.get());
#if defined(USE_ASH)
bool gets_own_workspace = false;
diff --git a/chrome/browser/ui/views/frame/browser_frame_aura.h b/chrome/browser/ui/views/frame/browser_frame_aura.h
index 8b9b6b1..97bc371 100644
--- a/chrome/browser/ui/views/frame/browser_frame_aura.h
+++ b/chrome/browser/ui/views/frame/browser_frame_aura.h
@@ -22,6 +22,8 @@ class BrowserView;
class BrowserFrameAura : public views::NativeWidgetAura,
public NativeBrowserFrame {
public:
+ static const char kWindowName[];
+
BrowserFrameAura(BrowserFrame* browser_frame, BrowserView* browser_view);
BrowserView* browser_view() const { return browser_view_; }
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 4924567..3a994a2 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -1034,75 +1034,10 @@ void BrowserView::FocusAppMenu() {
}
void BrowserView::RotatePaneFocus(bool forwards) {
- // This gets called when the user presses F6 (forwards) or Shift+F6
- // (backwards) to rotate to the next pane. Here, our "panes" are the
- // tab contents and each of our accessible toolbars, infobars, downloads
- // shelf, etc. When a pane has focus, all of its controls are accessible
- // in the tab traversal, and the tab traversal is "trapped" within that pane.
- //
- // Get a vector of all panes in the order we want them to be focused,
- // with NULL to represent the tab contents getting focus. If one of these
- // is currently invisible or has no focusable children it will be
- // automatically skipped.
- std::vector<views::AccessiblePaneView*> accessible_panes;
- GetAccessiblePanes(&accessible_panes);
- int pane_count = static_cast<int>(accessible_panes.size());
- int special_index = -1;
-
- std::vector<views::View*> accessible_views(
- accessible_panes.begin(), accessible_panes.end());
- accessible_views.push_back(GetTabContentsContainerView());
- if (devtools_container_->visible())
- accessible_views.push_back(devtools_container_);
- int count = static_cast<int>(accessible_views.size());
-
- // Figure out which view (if any) currently has the focus.
- const views::View* focused_view = GetFocusManager()->GetFocusedView();
- int index = -1;
- if (focused_view) {
- for (int i = 0; i < count; ++i) {
- if (accessible_views[i] == focused_view ||
- accessible_views[i]->Contains(focused_view)) {
- index = i;
- break;
- }
- }
- }
-
- // If the focus isn't currently in a pane, save the focus so we
- // can restore it if the user presses Escape.
- if (focused_view && index >= pane_count)
- GetFocusManager()->StoreFocusedView(true);
-
-#if defined(OS_CHROMEOS) && defined(USE_AURA)
- // Add the special panes to the rotation.
- special_index = count;
- ++count;
-#endif
-
- // Try to focus the next pane; if SetPaneFocusAndFocusDefault returns
- // false it means the pane didn't have any focusable controls, so skip
- // it and try the next one.
- for (;;) {
- if (forwards)
- index = (index + 1) % count;
- else
- index = ((index - 1) + count) % count;
-
- if (index == special_index) {
-#if defined(USE_ASH)
- ash::Shell::GetInstance()->RotateFocus(
- forwards ? ash::Shell::FORWARD : ash::Shell::BACKWARD);
-#endif
- break;
- } else if (index < pane_count) {
- if (accessible_panes[index]->SetPaneFocusAndFocusDefault())
- break;
- } else {
- accessible_views[index]->RequestFocus();
- break;
- }
- }
+ GetWidget()->GetFocusManager()->RotatePaneFocus(
+ forwards ?
+ views::FocusManager::kForward : views::FocusManager::kBackward,
+ views::FocusManager::kWrap);
}
void BrowserView::DestroyBrowser() {
@@ -1802,6 +1737,23 @@ const views::Widget* BrowserView::GetWidget() const {
return View::GetWidget();
}
+void BrowserView::GetAccessiblePanes(std::vector<views::View*>* panes) {
+ // This should be in the order of pane traversal of the panes using F6
+ // (Windows) or Ctrl+Back/Forward (Chrome OS). If one of these is
+ // invisible or has no focusable children, it will be automatically
+ // skipped.
+ panes->push_back(toolbar_);
+ if (bookmark_bar_view_.get())
+ panes->push_back(bookmark_bar_view_.get());
+ if (infobar_container_)
+ panes->push_back(infobar_container_);
+ if (download_shelf_.get())
+ panes->push_back(download_shelf_.get());
+ panes->push_back(GetTabContentsContainerView());
+ if (devtools_container_->visible())
+ panes->push_back(devtools_container_);
+}
+
///////////////////////////////////////////////////////////////////////////////
// BrowserView, views::ClientView overrides:
@@ -1917,20 +1869,6 @@ bool BrowserView::AcceleratorPressed(const ui::Accelerator& accelerator) {
///////////////////////////////////////////////////////////////////////////////
// BrowserView, private
-void BrowserView::GetAccessiblePanes(
- std::vector<views::AccessiblePaneView*>* panes) {
- // This should be in the order of pane traversal of the panes using F6.
- // If one of these is invisible or has no focusable children, it will be
- // automatically skipped.
- panes->push_back(toolbar_);
- if (bookmark_bar_view_.get())
- panes->push_back(bookmark_bar_view_.get());
- if (infobar_container_)
- panes->push_back(infobar_container_);
- if (download_shelf_.get())
- panes->push_back(download_shelf_.get());
-}
-
SkColor BrowserView::GetInfoBarSeparatorColor() const {
// NOTE: Keep this in sync with ToolbarView::OnPaint()!
return (IsTabStripVisible() || !frame_->ShouldUseNativeFrame()) ?
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 296dfde..2b9c438 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -408,6 +408,7 @@ class BrowserView : public BrowserWindow,
virtual void OnWidgetMove() OVERRIDE;
virtual views::Widget* GetWidget() OVERRIDE;
virtual const views::Widget* GetWidget() const OVERRIDE;
+ virtual void GetAccessiblePanes(std::vector<View*>* panes) OVERRIDE;
// Overridden from views::WidgetObserver:
virtual void OnWidgetActivationChanged(views::Widget* widget,
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index db677e0..2ee6816 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -392,7 +392,6 @@
'browser/chromeos/login/screen_locker_tester.cc',
'browser/chromeos/login/screen_locker_tester.h',
'browser/chromeos/login/wallpaper_manager_browsertest.cc',
- 'browser/chromeos/system/ash_focus_cycle_browsertest.cc',
'test/data/chromeos/service_login.html',
],
'sources!': [
diff --git a/ui/views/accessible_pane_view.cc b/ui/views/accessible_pane_view.cc
index d344471..31b0e92 100644
--- a/ui/views/accessible_pane_view.cc
+++ b/ui/views/accessible_pane_view.cc
@@ -211,6 +211,10 @@ void AccessiblePaneView::GetAccessibleState(ui::AccessibleViewState* state) {
state->role = ui::AccessibilityTypes::ROLE_PANE;
}
+void AccessiblePaneView::RequestFocus() {
+ SetPaneFocusAndFocusDefault();
+}
+
////////////////////////////////////////////////////////////////////////////////
// FocusChangeListener overrides:
diff --git a/ui/views/accessible_pane_view.h b/ui/views/accessible_pane_view.h
index a189919..99c392c 100644
--- a/ui/views/accessible_pane_view.h
+++ b/ui/views/accessible_pane_view.h
@@ -45,6 +45,7 @@ class VIEWS_EXPORT AccessiblePaneView : public View,
OVERRIDE;
virtual void SetVisible(bool flag) OVERRIDE;
virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
+ virtual void RequestFocus() OVERRIDE;
// Overridden from FocusChangeListener:
virtual void OnWillChangeFocus(View* focused_before,
diff --git a/ui/views/focus/focus_manager.cc b/ui/views/focus/focus_manager.cc
index bee68c2..03f050a 100644
--- a/ui/views/focus/focus_manager.cc
+++ b/ui/views/focus/focus_manager.cc
@@ -19,6 +19,7 @@
#include "ui/views/view.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
namespace views {
@@ -154,6 +155,64 @@ void FocusManager::ClearNativeFocus() {
widget_->ClearNativeFocus();
}
+bool FocusManager::RotatePaneFocus(Direction direction,
+ FocusCycleWrappingBehavior wrap) {
+ // Get the list of all accessible panes.
+ std::vector<View*> panes;
+ widget_->widget_delegate()->GetAccessiblePanes(&panes);
+
+ // Count the number of panes and set the default index if no pane
+ // is initially focused.
+ int count = static_cast<int>(panes.size());
+ if (count == 0)
+ return false;
+
+ // Initialize |index| to an appropriate starting index if nothing is
+ // focused initially.
+ int index = direction == kBackward ? 0 : count - 1;
+
+ // Check to see if a pane already has focus and update the index accordingly.
+ const views::View* focused_view = GetFocusedView();
+ if (focused_view) {
+ for (int i = 0; i < count; i++) {
+ if (panes[i] && panes[i]->Contains(focused_view)) {
+ index = i;
+ break;
+ }
+ }
+ }
+
+ // Rotate focus.
+ int start_index = index;
+ for (;;) {
+ if (direction == kBackward)
+ index--;
+ else
+ index++;
+
+ if (wrap == kNoWrap && (index >= count || index < 0))
+ return false;
+ index = (index + count) % count;
+
+ // Ensure that we don't loop more than once.
+ if (index == start_index)
+ break;
+
+ views::View* pane = panes[index];
+ DCHECK(pane);
+
+ if (!pane->visible())
+ continue;
+
+ pane->RequestFocus();
+ focused_view = GetFocusedView();
+ if (pane == focused_view || pane->Contains(focused_view))
+ return true;
+ }
+
+ return false;
+}
+
View* FocusManager::GetNextFocusableView(View* original_starting_view,
bool reverse,
bool dont_loop) {
diff --git a/ui/views/focus/focus_manager.h b/ui/views/focus/focus_manager.h
index 55310cf..7e92f00 100644
--- a/ui/views/focus/focus_manager.h
+++ b/ui/views/focus/focus_manager.h
@@ -136,6 +136,17 @@ class VIEWS_EXPORT FocusManager {
kReasonDirectFocusChange
};
+ // TODO: use Direction in place of bool reverse throughout.
+ enum Direction {
+ kForward,
+ kBackward
+ };
+
+ enum FocusCycleWrappingBehavior {
+ kWrap,
+ kNoWrap
+ };
+
FocusManager(Widget* widget, FocusManagerDelegate* delegate);
virtual ~FocusManager();
@@ -267,6 +278,15 @@ class VIEWS_EXPORT FocusManager {
// Clears the native view having the focus.
virtual void ClearNativeFocus();
+ // Focuses the next keyboard-accessible pane, taken from the list of
+ // views returned by WidgetDelegate::GetAccessiblePanes(). If there are
+ // no panes, the widget's root view is treated as a single pane.
+ // A keyboard-accessible pane should subclass from AccessiblePaneView in
+ // order to trap keyboard focus within that pane. If |wrap| is kWrap,
+ // it keeps cycling within this widget, otherwise it returns false after
+ // reaching the last pane so that focus can cycle to another widget.
+ bool RotatePaneFocus(Direction direction, FocusCycleWrappingBehavior wrap);
+
// Convenience method that returns true if the passed |key_event| should
// trigger tab traversal (if it is a TAB key press with or without SHIFT
// pressed).
diff --git a/ui/views/focus/focus_manager_test.cc b/ui/views/focus/focus_manager_test.cc
index 2868112..528f345 100644
--- a/ui/views/focus/focus_manager_test.cc
+++ b/ui/views/focus/focus_manager_test.cc
@@ -4,6 +4,8 @@
#include "ui/views/focus/focus_manager_test.h"
+#include <algorithm>
+
#include "ui/views/focus/focus_manager.h"
#include "ui/views/widget/widget.h"
@@ -70,6 +72,11 @@ const Widget* FocusManagerTest::GetWidget() const {
return contents_view_->GetWidget();
}
+void FocusManagerTest::GetAccessiblePanes(std::vector<View*>* panes) {
+ std::copy(accessible_panes_.begin(), accessible_panes_.end(),
+ std::back_inserter(*panes));
+}
+
////////////////////////////////////////////////////////////////////////////////
// FocusManagerTest, protected:
@@ -89,6 +96,10 @@ void FocusManagerTest::AddWidgetFocusChangeListener(
WidgetFocusManager::GetInstance()->AddFocusChangeListener(listener);
}
+void FocusManagerTest::SetAccessiblePanes(const std::vector<View*>& panes) {
+ accessible_panes_ = panes;
+}
+
#if defined(OS_WIN) && !defined(USE_AURA)
void FocusManagerTest::SimulateActivateWindow() {
SendMessage(GetWidget()->GetNativeWindow(), WM_ACTIVATE, WA_ACTIVE, NULL);
diff --git a/ui/views/focus/focus_manager_test.h b/ui/views/focus/focus_manager_test.h
index f2c0102..53e7b00 100644
--- a/ui/views/focus/focus_manager_test.h
+++ b/ui/views/focus/focus_manager_test.h
@@ -31,6 +31,7 @@ class FocusManagerTest : public ViewsTestBase,
virtual View* GetContentsView() OVERRIDE;
virtual Widget* GetWidget() OVERRIDE;
virtual const Widget* GetWidget() const OVERRIDE;
+ virtual void GetAccessiblePanes(std::vector<View*>* panes) OVERRIDE;
protected:
// Called after the Widget is initialized and the content view is added.
@@ -40,6 +41,9 @@ class FocusManagerTest : public ViewsTestBase,
void AddFocusChangeListener(FocusChangeListener* listener);
void AddWidgetFocusChangeListener(WidgetFocusChangeListener* listener);
+ // For testing FocusManager::RotatePaneFocus().
+ void SetAccessiblePanes(const std::vector<View*>& panes);
+
#if defined(OS_WIN) && !defined(USE_AURA)
// Mocks activating/deactivating the window.
void SimulateActivateWindow();
@@ -53,6 +57,7 @@ class FocusManagerTest : public ViewsTestBase,
View* contents_view_;
FocusChangeListener* focus_change_listener_;
WidgetFocusChangeListener* widget_focus_change_listener_;
+ std::vector<View*> accessible_panes_;
DISALLOW_COPY_AND_ASSIGN(FocusManagerTest);
};
diff --git a/ui/views/focus/focus_manager_unittest.cc b/ui/views/focus/focus_manager_unittest.cc
index 44b5028..4f09d8b 100644
--- a/ui/views/focus/focus_manager_unittest.cc
+++ b/ui/views/focus/focus_manager_unittest.cc
@@ -8,6 +8,7 @@
#include "base/utf_string_conversions.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/views/accessible_pane_view.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/focus/accelerator_handler.h"
@@ -646,4 +647,76 @@ TEST_F(FocusManagerTest, FocusInAboutToRequestFocusFromTabTraversal) {
EXPECT_TRUE(v1->HasFocus());
}
+TEST_F(FocusManagerTest, RotatePaneFocus) {
+ views::AccessiblePaneView* pane1 = new AccessiblePaneView();
+ GetContentsView()->AddChildView(pane1);
+
+ views::View* v1 = new View;
+ v1->set_focusable(true);
+ pane1->AddChildView(v1);
+
+ views::View* v2 = new View;
+ v2->set_focusable(true);
+ pane1->AddChildView(v2);
+
+ views::AccessiblePaneView* pane2 = new AccessiblePaneView();
+ GetContentsView()->AddChildView(pane2);
+
+ views::View* v3 = new View;
+ v3->set_focusable(true);
+ pane2->AddChildView(v3);
+
+ views::View* v4 = new View;
+ v4->set_focusable(true);
+ pane2->AddChildView(v4);
+
+ std::vector<views::View*> panes;
+ panes.push_back(pane1);
+ panes.push_back(pane2);
+ SetAccessiblePanes(panes);
+
+ FocusManager* focus_manager = GetWidget()->GetFocusManager();
+
+ // Advance forwards. Focus should stay trapped within each pane.
+ EXPECT_TRUE(focus_manager->RotatePaneFocus(
+ FocusManager::kForward, FocusManager::kWrap));
+ EXPECT_EQ(v1, focus_manager->GetFocusedView());
+ focus_manager->AdvanceFocus(false);
+ EXPECT_EQ(v2, focus_manager->GetFocusedView());
+ focus_manager->AdvanceFocus(false);
+ EXPECT_EQ(v1, focus_manager->GetFocusedView());
+
+ EXPECT_TRUE(focus_manager->RotatePaneFocus(
+ FocusManager::kForward, FocusManager::kWrap));
+ EXPECT_EQ(v3, focus_manager->GetFocusedView());
+ focus_manager->AdvanceFocus(false);
+ EXPECT_EQ(v4, focus_manager->GetFocusedView());
+ focus_manager->AdvanceFocus(false);
+ EXPECT_EQ(v3, focus_manager->GetFocusedView());
+
+ EXPECT_TRUE(focus_manager->RotatePaneFocus(
+ FocusManager::kForward, FocusManager::kWrap));
+ EXPECT_EQ(v1, focus_manager->GetFocusedView());
+
+ // Advance backwards.
+ EXPECT_TRUE(focus_manager->RotatePaneFocus(
+ FocusManager::kBackward, FocusManager::kWrap));
+ EXPECT_EQ(v3, focus_manager->GetFocusedView());
+
+ EXPECT_TRUE(focus_manager->RotatePaneFocus(
+ FocusManager::kBackward, FocusManager::kWrap));
+ EXPECT_EQ(v1, focus_manager->GetFocusedView());
+
+ // Advance without wrap. When it gets to the end of the list of
+ // panes, RotatePaneFocus should return false but the current
+ // focused view shouldn't change.
+ EXPECT_TRUE(focus_manager->RotatePaneFocus(
+ FocusManager::kForward, FocusManager::kNoWrap));
+ EXPECT_EQ(v3, focus_manager->GetFocusedView());
+
+ EXPECT_FALSE(focus_manager->RotatePaneFocus(
+ FocusManager::kForward, FocusManager::kNoWrap));
+ EXPECT_EQ(v3, focus_manager->GetFocusedView());
+}
+
} // namespace views
diff --git a/ui/views/view.h b/ui/views/view.h
index 8bd5603..306e3a9 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -732,7 +732,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate,
virtual const FocusManager* GetFocusManager() const;
// Request keyboard focus. The receiving view will become the focused view.
- void RequestFocus();
+ virtual void RequestFocus();
// Invoked when a view is about to be requested for focus due to the focus
// traversal. Reverse is this request was generated going backward
diff --git a/ui/views/widget/widget_delegate.h b/ui/views/widget/widget_delegate.h
index a23f39a..6bf4d16 100644
--- a/ui/views/widget/widget_delegate.h
+++ b/ui/views/widget/widget_delegate.h
@@ -6,6 +6,7 @@
#define UI_VIEWS_WIDGET_WIDGET_DELEGATE_H_
#include <string>
+#include <vector>
#include "ui/base/accessibility/accessibility_types.h"
#include "ui/base/ui_base_types.h"
@@ -155,6 +156,10 @@ class VIEWS_EXPORT WidgetDelegate {
gfx::NativeView child,
const gfx::Point& location);
+ // Populates |panes| with accessible panes in this window that can
+ // be cycled through with keyboard focus.
+ virtual void GetAccessiblePanes(std::vector<View*>* panes) {}
+
protected:
virtual ~WidgetDelegate() {}