diff options
Diffstat (limited to 'chrome')
29 files changed, 560 insertions, 471 deletions
diff --git a/chrome/app/chrome_dll.rc b/chrome/app/chrome_dll.rc index 03aa3b8..ab241c6 100644 --- a/chrome/app/chrome_dll.rc +++ b/chrome/app/chrome_dll.rc @@ -45,7 +45,8 @@ BEGIN VK_F3, IDC_FIND_NEXT, VIRTKEY "G", IDC_FIND_PREVIOUS, VIRTKEY, CONTROL, SHIFT VK_F3, IDC_FIND_PREVIOUS, VIRTKEY, SHIFT - VK_F6, IDC_FOCUS_LOCATION, VIRTKEY + VK_F6, IDC_FOCUS_NEXT_PANE, VIRTKEY + VK_F6, IDC_FOCUS_PREVIOUS_PANE, VIRTKEY, SHIFT "D", IDC_FOCUS_LOCATION, VIRTKEY, ALT "L", IDC_FOCUS_LOCATION, VIRTKEY, CONTROL VK_F10, IDC_FOCUS_MENU_BAR, VIRTKEY @@ -53,6 +54,7 @@ BEGIN "K", IDC_FOCUS_SEARCH, VIRTKEY, CONTROL "E", IDC_FOCUS_SEARCH, VIRTKEY, CONTROL "T", IDC_FOCUS_TOOLBAR, VIRTKEY, SHIFT, ALT + "B", IDC_FOCUS_BOOKMARKS, VIRTKEY, SHIFT, ALT VK_RIGHT, IDC_FORWARD, VIRTKEY, ALT VK_BACK, IDC_FORWARD, VIRTKEY, SHIFT VK_F11, IDC_FULLSCREEN, VIRTKEY diff --git a/chrome/app/chrome_dll_resource.h b/chrome/app/chrome_dll_resource.h index 8a02d90..ba0756a 100644 --- a/chrome/app/chrome_dll_resource.h +++ b/chrome/app/chrome_dll_resource.h @@ -179,6 +179,10 @@ #define IDC_FOCUS_LOCATION 39001 #define IDC_FOCUS_SEARCH 39002 #define IDC_FOCUS_MENU_BAR 39003 +#define IDC_FOCUS_NEXT_PANE 39004 +#define IDC_FOCUS_PREVIOUS_PANE 39005 +#define IDC_FOCUS_BOOKMARKS 39006 +#define IDC_FOCUS_CHROMEOS_STATUS 39007 // Show various bits of UI #define IDC_OPEN_FILE 40000 diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc index a85e72a..eea0ec5 100644 --- a/chrome/browser/browser.cc +++ b/chrome/browser/browser.cc @@ -1024,6 +1024,9 @@ void Browser::UpdateCommandsForFullscreenMode(bool is_fullscreen) { const bool show_main_ui = (type() == TYPE_NORMAL); #endif + bool main_not_fullscreen_or_popup = + show_main_ui && !is_fullscreen && (type() & TYPE_POPUP) == 0; + // Navigation commands command_updater_.UpdateCommandEnabled(IDC_OPEN_CURRENT_URL, show_main_ui); @@ -1036,8 +1039,15 @@ void Browser::UpdateCommandsForFullscreenMode(bool is_fullscreen) { command_updater_.UpdateCommandEnabled(IDC_FOCUS_LOCATION, show_main_ui); command_updater_.UpdateCommandEnabled(IDC_FOCUS_SEARCH, show_main_ui); command_updater_.UpdateCommandEnabled( - IDC_FOCUS_MENU_BAR, - show_main_ui && !is_fullscreen && (type() & TYPE_POPUP) == 0); + IDC_FOCUS_MENU_BAR, main_not_fullscreen_or_popup); + command_updater_.UpdateCommandEnabled( + IDC_FOCUS_NEXT_PANE, main_not_fullscreen_or_popup); + command_updater_.UpdateCommandEnabled( + IDC_FOCUS_PREVIOUS_PANE, main_not_fullscreen_or_popup); + command_updater_.UpdateCommandEnabled( + IDC_FOCUS_BOOKMARKS, main_not_fullscreen_or_popup); + command_updater_.UpdateCommandEnabled( + IDC_FOCUS_CHROMEOS_STATUS, main_not_fullscreen_or_popup); // Show various bits of UI command_updater_.UpdateCommandEnabled(IDC_DEVELOPER_MENU, show_main_ui); @@ -1547,6 +1557,27 @@ void Browser::FocusLocationBar() { window_->SetFocusToLocationBar(true); } +void Browser::FocusBookmarksToolbar() { + UserMetrics::RecordAction(UserMetricsAction("FocusBookmarksToolbar"), + profile_); + window_->FocusBookmarksToolbar(); +} + +void Browser::FocusChromeOSStatus() { + UserMetrics::RecordAction(UserMetricsAction("FocusChromeOSStatus"), profile_); + window_->FocusChromeOSStatus(); +} + +void Browser::FocusNextPane() { + UserMetrics::RecordAction(UserMetricsAction("FocusNextPane"), profile_); + window_->RotatePaneFocus(true); +} + +void Browser::FocusPreviousPane() { + UserMetrics::RecordAction(UserMetricsAction("FocusPreviousPane"), profile_); + window_->RotatePaneFocus(false); +} + void Browser::FocusSearch() { // TODO(beng): replace this with FocusLocationBar UserMetrics::RecordAction(UserMetricsAction("FocusSearch"), profile_); @@ -1993,6 +2024,10 @@ void Browser::ExecuteCommandWithDisposition( case IDC_FOCUS_LOCATION: FocusLocationBar(); break; case IDC_FOCUS_SEARCH: FocusSearch(); break; case IDC_FOCUS_MENU_BAR: FocusPageAndAppMenus(); break; + case IDC_FOCUS_BOOKMARKS: FocusBookmarksToolbar(); break; + case IDC_FOCUS_CHROMEOS_STATUS: FocusChromeOSStatus(); break; + case IDC_FOCUS_NEXT_PANE: FocusNextPane(); break; + case IDC_FOCUS_PREVIOUS_PANE: FocusPreviousPane(); break; // Show various bits of UI case IDC_OPEN_FILE: OpenFile(); break; diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h index da30b6e..6b7671a 100644 --- a/chrome/browser/browser.h +++ b/chrome/browser/browser.h @@ -503,6 +503,10 @@ class Browser : public TabStripModelDelegate, void FocusLocationBar(); // Also selects any existing text. void FocusSearch(); void FocusPageAndAppMenus(); + void FocusBookmarksToolbar(); + void FocusChromeOSStatus(); + void FocusNextPane(); + void FocusPreviousPane(); // Show various bits of UI void OpenFile(); diff --git a/chrome/browser/browser_window.h b/chrome/browser/browser_window.h index bc2305a..d806e62 100644 --- a/chrome/browser/browser_window.h +++ b/chrome/browser/browser_window.h @@ -148,6 +148,15 @@ class BrowserWindow { // Not used on the Mac, which has a "normal" menu bar. virtual void FocusPageAndAppMenus() = 0; + // Focuses the bookmarks toolbar (for accessibility). + virtual void FocusBookmarksToolbar() = 0; + + // Focuses the Chrome OS status view (for accessibility). + virtual void FocusChromeOSStatus() = 0; + + // Moves keyboard focus to the next pane. + virtual void RotatePaneFocus(bool forwards) = 0; + // Returns whether the bookmark bar is visible or not. virtual bool IsBookmarkBarVisible() const = 0; diff --git a/chrome/browser/chromeos/frame/browser_view.cc b/chrome/browser/chromeos/frame/browser_view.cc index c03141a..124ecca 100644 --- a/chrome/browser/chromeos/frame/browser_view.cc +++ b/chrome/browser/chromeos/frame/browser_view.cc @@ -490,6 +490,11 @@ void BrowserView::SetFocusToLocationBar(bool select_all) { ::BrowserView::SetFocusToLocationBar(select_all); } +void BrowserView::FocusChromeOSStatus() { + SaveFocusedView(); + status_area_->SetToolbarFocus(last_focused_view_storage_id(), NULL); +} + void BrowserView::ToggleCompactNavigationBar() { UIStyle new_style = static_cast<UIStyle>((ui_style_ + 1) % 2); if (new_style != StandardStyle && UseVerticalTabs()) @@ -595,6 +600,15 @@ void BrowserView::ShowCompactLocationBarUnderSelectedTab(bool select_all) { } //////////////////////////////////////////////////////////////////////////////// +// BrowserView protected: + +void BrowserView::GetAccessibleToolbars( + std::vector<AccessibleToolbarView*>* toolbars) { + ::BrowserView::GetAccessibleToolbars(toolbars); + toolbars->push_back(status_area_); +} + +//////////////////////////////////////////////////////////////////////////////// // BrowserView private: void BrowserView::InitSystemMenu() { diff --git a/chrome/browser/chromeos/frame/browser_view.h b/chrome/browser/chromeos/frame/browser_view.h index 2bdb029..8a53c97 100644 --- a/chrome/browser/chromeos/frame/browser_view.h +++ b/chrome/browser/chromeos/frame/browser_view.h @@ -5,9 +5,12 @@ #ifndef CHROME_BROWSER_CHROMEOS_FRAME_BROWSER_VIEW_H_ #define CHROME_BROWSER_CHROMEOS_FRAME_BROWSER_VIEW_H_ +#include <vector> + #include "chrome/browser/chromeos/status/status_area_host.h" #include "chrome/browser/views/frame/browser_view.h" +class AccessibleToolbarView; class TabStripModel; namespace menus { @@ -59,6 +62,7 @@ class BrowserView : public ::BrowserView, virtual void Show(); virtual bool IsToolbarVisible() const; virtual void SetFocusToLocationBar(bool select_all); + virtual void FocusChromeOSStatus(); virtual void ToggleCompactNavigationBar(); virtual views::LayoutManager* CreateLayoutManager() const; virtual void InitTabStrip(TabStripModel* tab_strip_model); @@ -88,6 +92,10 @@ class BrowserView : public ::BrowserView, return ui_style_ == CompactStyle; } + protected: + virtual void GetAccessibleToolbars( + std::vector<AccessibleToolbarView*>* toolbars); + private: friend class CompactLocationBarHostTest; diff --git a/chrome/browser/chromeos/status/status_area_view.h b/chrome/browser/chromeos/status/status_area_view.h index c760779..076ed34 100644 --- a/chrome/browser/chromeos/status/status_area_view.h +++ b/chrome/browser/chromeos/status/status_area_view.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_CHROMEOS_STATUS_STATUS_AREA_VIEW_H_ #include "base/basictypes.h" +#include "chrome/browser/views/accessible_toolbar_view.h" #include "views/view.h" namespace chromeos { @@ -19,7 +20,7 @@ class StatusAreaHost; // This class is used to wrap the small informative widgets in the upper-right // of the window title bar. It is used on ChromeOS only. -class StatusAreaView : public views::View { +class StatusAreaView : public AccessibleToolbarView { public: enum OpenTabsMode { OPEN_TABS_ON_LEFT = 1, diff --git a/chrome/browser/cocoa/browser_window_cocoa.h b/chrome/browser/cocoa/browser_window_cocoa.h index 4d8f017..ec1e0e5 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.h +++ b/chrome/browser/cocoa/browser_window_cocoa.h @@ -58,6 +58,9 @@ class BrowserWindowCocoa : public BrowserWindow, bool should_restore_state); virtual void FocusToolbar(); virtual void FocusPageAndAppMenus(); + virtual void FocusBookmarksToolbar(); + virtual void FocusChromeOSStatus(); + virtual void RotatePaneFocus(bool forwards); virtual bool IsBookmarkBarVisible() const; virtual bool IsBookmarkBarAnimating() const; virtual bool IsToolbarVisible() const; diff --git a/chrome/browser/cocoa/browser_window_cocoa.mm b/chrome/browser/cocoa/browser_window_cocoa.mm index 0b8111e..9dbe789 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.mm +++ b/chrome/browser/cocoa/browser_window_cocoa.mm @@ -217,13 +217,25 @@ void BrowserWindowCocoa::UpdateToolbar(TabContents* contents, } void BrowserWindowCocoa::FocusToolbar() { - NOTIMPLEMENTED(); + // Not needed on the Mac. } void BrowserWindowCocoa::FocusPageAndAppMenus() { // Chrome uses the standard Mac OS X menu bar, so this isn't needed. } +void BrowserWindowCocoa::RotatePaneFocus(bool forwards) { + // Not needed on the Mac. +} + +void BrowserWindowCocoa::FocusBookmarksToolbar() { + // Not needed on the Mac. +} + +void BrowserWindowCocoa::FocusChromeOSStatus() { + // Not needed on the Mac. +} + bool BrowserWindowCocoa::IsBookmarkBarVisible() const { return browser_->profile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar); } diff --git a/chrome/browser/gtk/browser_window_gtk.cc b/chrome/browser/gtk/browser_window_gtk.cc index eb708f5..c8a42ff 100644 --- a/chrome/browser/gtk/browser_window_gtk.cc +++ b/chrome/browser/gtk/browser_window_gtk.cc @@ -847,6 +847,18 @@ void BrowserWindowGtk::FocusPageAndAppMenus() { NOTIMPLEMENTED(); } +void BrowserWindowGtk::FocusBookmarksToolbar() { + NOTIMPLEMENTED(); +} + +void BrowserWindowGtk::FocusChromeOSStatus() { + NOTIMPLEMENTED(); +} + +void BrowserWindowGtk::RotatePaneFocus(bool forwards) { + NOTIMPLEMENTED(); +} + bool BrowserWindowGtk::IsBookmarkBarVisible() const { return browser_->SupportsWindowFeature(Browser::FEATURE_BOOKMARKBAR) && bookmark_bar_.get() && diff --git a/chrome/browser/gtk/browser_window_gtk.h b/chrome/browser/gtk/browser_window_gtk.h index 3f86f43..b62c16e 100644 --- a/chrome/browser/gtk/browser_window_gtk.h +++ b/chrome/browser/gtk/browser_window_gtk.h @@ -76,6 +76,9 @@ class BrowserWindowGtk : public BrowserWindow, bool should_restore_state); virtual void FocusToolbar(); virtual void FocusPageAndAppMenus(); + virtual void FocusBookmarksToolbar(); + virtual void FocusChromeOSStatus(); + virtual void RotatePaneFocus(bool forwards); virtual bool IsBookmarkBarVisible() const; virtual bool IsBookmarkBarAnimating() const; virtual bool IsToolbarVisible() const; diff --git a/chrome/browser/views/accelerator_table_gtk.cc b/chrome/browser/views/accelerator_table_gtk.cc index f755de7..dfa5744 100644 --- a/chrome/browser/views/accelerator_table_gtk.cc +++ b/chrome/browser/views/accelerator_table_gtk.cc @@ -19,7 +19,11 @@ const AcceleratorMapping kAcceleratorMap[] = { { base::VKEY_BROWSER_SEARCH, false, false, false, IDC_FOCUS_SEARCH }, { base::VKEY_L, false, true, false, IDC_FOCUS_LOCATION }, { base::VKEY_D, false, false, true, IDC_FOCUS_LOCATION }, - { base::VKEY_F6, false, false, false, IDC_FOCUS_LOCATION }, + { base::VKEY_T, true, false, true, IDC_FOCUS_TOOLBAR }, + { base::VKEY_B, true, false, true, IDC_FOCUS_BOOKMARKS }, + { base::VKEY_S, true, false, true, IDC_FOCUS_CHROMEOS_STATUS }, + { base::VKEY_F6, false, false, false, IDC_FOCUS_NEXT_PANE }, + { base::VKEY_F6, true, false, false, IDC_FOCUS_PREVIOUS_PANE }, { base::VKEY_F10, false, false, false, IDC_FOCUS_MENU_BAR }, { base::VKEY_MENU, false, false, false, IDC_FOCUS_MENU_BAR }, diff --git a/chrome/browser/views/accessible_toolbar_view.cc b/chrome/browser/views/accessible_toolbar_view.cc index fead6c0..3edd8cf 100644 --- a/chrome/browser/views/accessible_toolbar_view.cc +++ b/chrome/browser/views/accessible_toolbar_view.cc @@ -7,201 +7,178 @@ #include "chrome/browser/views/frame/browser_view.h" #include "chrome/browser/views/accessible_toolbar_view.h" #include "views/controls/button/menu_button.h" +#include "views/controls/native/native_view_host.h" +#include "views/focus/focus_search.h" #include "views/focus/view_storage.h" #include "views/widget/tooltip_manager.h" #include "views/widget/widget.h" AccessibleToolbarView::AccessibleToolbarView() - : selected_focused_view_(NULL), + : toolbar_has_focus_(false), + ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), + focus_manager_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(focus_search_(this, true, true)), + home_key_(base::VKEY_HOME, false, false, false), + end_key_(base::VKEY_END, false, false, false), + escape_key_(base::VKEY_ESCAPE, false, false, false), + left_key_(base::VKEY_LEFT, false, false, false), + right_key_(base::VKEY_RIGHT, false, false, false), last_focused_view_storage_id_(-1) { } AccessibleToolbarView::~AccessibleToolbarView() { + if (toolbar_has_focus_) { + focus_manager_->RemoveFocusChangeListener(this); + } } -void AccessibleToolbarView::InitiateTraversal(int view_storage_id) { - // We only traverse if accessibility is active. - if (selected_focused_view_) - return; +bool AccessibleToolbarView::SetToolbarFocus(int view_storage_id, + views::View* initial_focus) { + if (!IsVisible()) + return false; // Save the storage id to the last focused view. This would be used to request // focus to the view when the traversal is ended. last_focused_view_storage_id_ = view_storage_id; - // Request focus to the toolbar. - RequestFocus(); -} + if (!focus_manager_) + focus_manager_ = GetFocusManager(); -views::View* AccessibleToolbarView::GetNextAccessibleView(int view_index, - bool forward) { - int modifier = forward ? 1 : -1; - int current_view_index = view_index + modifier; - - while ((current_view_index >= 0) && - (current_view_index < GetChildViewCount())) { - // Try to find the next available view that can be interacted with. That - // view must be enabled, visible, and traversable. - views::View* current_view = GetChildViewAt(current_view_index); - if (current_view->IsEnabled() && current_view->IsVisible() && - IsAccessibleViewTraversable(current_view)) { - return current_view; - } - current_view_index += modifier; + // Use the provided initial focus if it's visible and enabled, otherwise + // use the first focusable child. + if (!initial_focus || + !IsParentOf(initial_focus) || + !initial_focus->IsVisible() || + !initial_focus->IsEnabled()) { + initial_focus = GetFirstFocusableChild(); } - // No button is available in the specified direction. - return NULL; -} + // Return false if there are no focusable children. + if (!initial_focus) + return false; -bool AccessibleToolbarView::IsAccessibleViewTraversable(views::View* view) { - return true; -} + // Set focus to the initial view + focus_manager_->SetFocusedView(initial_focus); -void AccessibleToolbarView::DidGainFocus() { - // Check to see if the accessible focus should be restored to previously - // focused button. The must be enabled and visible in the toolbar. - if (!selected_focused_view_ || - !selected_focused_view_->IsEnabled() || - !selected_focused_view_->IsVisible()) { - // Find first accessible child (-1 to start search at parent). - selected_focused_view_ = GetNextAccessibleView(-1, true); - - // No buttons enabled or visible. - if (!selected_focused_view_) - return; - } + // If we already have toolbar focus, we're done. + if (toolbar_has_focus_) + return true; - // Set the focus to the current accessible view. - SetFocusToAccessibleView(); -} + // Otherwise, set accelerators and start listening for focus change events. + toolbar_has_focus_ = true; + focus_manager_->RegisterAccelerator(home_key_, this); + focus_manager_->RegisterAccelerator(end_key_, this); + focus_manager_->RegisterAccelerator(escape_key_, this); + focus_manager_->RegisterAccelerator(left_key_, this); + focus_manager_->RegisterAccelerator(right_key_, this); + focus_manager_->AddFocusChangeListener(this); -void AccessibleToolbarView::WillLoseFocus() { - // Any tooltips that are active should be hidden when toolbar loses focus. - if (GetWidget() && GetWidget()->GetTooltipManager()) - GetWidget()->GetTooltipManager()->HideKeyboardTooltip(); - - // Removes the child accessibility view's focus when toolbar loses focus. It - // will not remove the view from the ViewStorage because it might be - // traversing to another toolbar hence the last focused view should not be - // removed. - if (selected_focused_view_) { - selected_focused_view_->SetHotTracked(false); - selected_focused_view_ = NULL; - } + return true; } -void AccessibleToolbarView::ShowContextMenu(const gfx::Point& p, - bool is_mouse_gesture) { - if (selected_focused_view_) - selected_focused_view_->ShowContextMenu(p, is_mouse_gesture); +bool AccessibleToolbarView::SetToolbarFocusAndFocusDefault( + int view_storage_id) { + return SetToolbarFocus(view_storage_id, GetDefaultFocusableChild()); } -void AccessibleToolbarView::RequestFocus() { - // When the toolbar needs to request focus, the default implementation of - // View::RequestFocus requires the View to be focusable. Since ToolbarView is - // not technically focused, we need to temporarily set and remove focus so - // that it can focus back to its focused state. |selected_focused_view_| is - // not necessarily set since it can be null if this view has already lost - // focus, such as traversing through the context menu. - SetFocusable(true); - View::RequestFocus(); - SetFocusable(false); +void AccessibleToolbarView::RemoveToolbarFocusIfNoChildHasFocus() { + views::View* focused_view = focus_manager_->GetFocusedView(); + if (toolbar_has_focus_ && (!focused_view || !IsParentOf(focused_view))) + RemoveToolbarFocus(); } -bool AccessibleToolbarView::OnKeyPressed(const views::KeyEvent& e) { - if (!HasFocus()) - return View::OnKeyPressed(e); +void AccessibleToolbarView::RemoveToolbarFocus() { + focus_manager_->RemoveFocusChangeListener(this); + toolbar_has_focus_ = false; - int focused_view = GetChildIndex(selected_focused_view_); - views::View* next_view = NULL; + focus_manager_->UnregisterAccelerator(home_key_, this); + focus_manager_->UnregisterAccelerator(end_key_, this); + focus_manager_->UnregisterAccelerator(escape_key_, this); + focus_manager_->UnregisterAccelerator(left_key_, this); + focus_manager_->UnregisterAccelerator(right_key_, this); +} - switch (e.GetKeyCode()) { - case base::VKEY_LEFT: - next_view = GetNextAccessibleView(focused_view, false); - break; - case base::VKEY_RIGHT: - next_view = GetNextAccessibleView(focused_view, true); - break; - case base::VKEY_DOWN: - case base::VKEY_RETURN: - if (selected_focused_view_ && selected_focused_view_->GetClassName() == - views::MenuButton::kViewClassName) { - // If a menu button is activated and its menu is displayed, then the - // active tooltip should be hidden. - if (GetWidget()->GetTooltipManager()) - GetWidget()->GetTooltipManager()->HideKeyboardTooltip(); - - // Safe to cast, given to above check. - static_cast<views::MenuButton*>(selected_focused_view_)->Activate(); - - // If activate did not trigger a focus change, the menu button should - // remain hot tracked since the view is still focused. - if (selected_focused_view_) - selected_focused_view_->SetHotTracked(true); - return true; - } - default: - // If key is not handled explicitly, pass it on to view. - if (selected_focused_view_) - return selected_focused_view_->OnKeyPressed(e); - else - return View::OnKeyPressed(e); +void AccessibleToolbarView::RestoreLastFocusedView() { + views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance(); + views::View* last_focused_view = + view_storage->RetrieveView(last_focused_view_storage_id_); + if (last_focused_view) { + focus_manager_->SetFocusedView(last_focused_view); + } else { + // Focus the location bar + views::View* view = GetAncestorWithClassName(BrowserView::kViewClassName); + if (view) { + BrowserView* browser_view = static_cast<BrowserView*>(view); + browser_view->SetFocusToLocationBar(false); + } } +} - // No buttons enabled, visible, or focus hasn't moved. - if (!next_view || !selected_focused_view_) - return false; - - // Remove hot-tracking from old focused button. - selected_focused_view_->SetHotTracked(false); - - // All is well, update the focused child member variable. - selected_focused_view_ = next_view; +views::View* AccessibleToolbarView::GetFirstFocusableChild() { + FocusTraversable* dummy_focus_traversable; + views::View* dummy_focus_traversable_view; + return focus_search_.FindNextFocusableView( + NULL, false, views::FocusSearch::DOWN, false, + &dummy_focus_traversable, &dummy_focus_traversable_view); +} - // Set the focus to the current accessible view. - SetFocusToAccessibleView(); - return true; +views::View* AccessibleToolbarView::GetLastFocusableChild() { + FocusTraversable* dummy_focus_traversable; + views::View* dummy_focus_traversable_view; + return focus_search_.FindNextFocusableView( + this, true, views::FocusSearch::DOWN, false, + &dummy_focus_traversable, &dummy_focus_traversable_view); } -bool AccessibleToolbarView::OnKeyReleased(const views::KeyEvent& e) { - if (!selected_focused_view_) - return false; +//////////////////////////////////////////////////////////////////////////////// +// View overrides: - // Have keys be handled by the views themselves. - return selected_focused_view_->OnKeyReleased(e); +views::FocusTraversable* AccessibleToolbarView::GetPaneFocusTraversable() { + if (toolbar_has_focus_) + return this; + else + return NULL; } -bool AccessibleToolbarView::SkipDefaultKeyEventProcessing( - const views::KeyEvent& e) { - // Accessibility focus must be present in order to handle ESC and TAB related - // key events. - if (!HasFocus()) - return false; - - // The ancestor *must* be a BrowserView. - views::View* view = GetAncestorWithClassName(BrowserView::kViewClassName); - if (!view) +bool AccessibleToolbarView::AcceleratorPressed( + const views::Accelerator& accelerator) { + // Special case: don't handle arrows for native views, like the + // location bar's edit text view, which needs them for text editing. + views::View* focused_view = focus_manager_->GetFocusedView(); + if (focused_view->GetClassName() == views::NativeViewHost::kViewClassName && + (accelerator.GetKeyCode() == base::VKEY_LEFT || + accelerator.GetKeyCode() == base::VKEY_RIGHT)) { return false; + } - // Given the check above, we can ensure its a BrowserView. - BrowserView* browser_view = static_cast<BrowserView*>(view); - - // Handle ESC and TAB events. - switch (e.GetKeyCode()) { - case base::VKEY_ESCAPE: { - SetFocusToLastFocusedView(); + switch (accelerator.GetKeyCode()) { + case base::VKEY_ESCAPE: + RemoveToolbarFocus(); + RestoreLastFocusedView(); return true; - } - case base::VKEY_TAB: { - if (e.IsShiftDown()) { - browser_view->TraverseNextAccessibleToolbar(false); - } else { - browser_view->TraverseNextAccessibleToolbar(true); - } + case base::VKEY_LEFT: + focus_manager_->AdvanceFocus(true); return true; - } - default: return false; + case base::VKEY_RIGHT: + focus_manager_->AdvanceFocus(false); + return true; + case base::VKEY_HOME: + focus_manager_->SetFocusedView(GetFirstFocusableChild()); + return true; + case base::VKEY_END: + focus_manager_->SetFocusedView(GetLastFocusableChild()); + return true; + default: + return false; + } +} + +void AccessibleToolbarView::SetVisible(bool flag) { + if (IsVisible() && !flag && toolbar_has_focus_) { + RemoveToolbarFocus(); + RestoreLastFocusedView(); } + View::SetVisible(flag); } bool AccessibleToolbarView::GetAccessibleRole(AccessibilityTypes::Role* role) { @@ -211,53 +188,41 @@ bool AccessibleToolbarView::GetAccessibleRole(AccessibilityTypes::Role* role) { return true; } -void AccessibleToolbarView::ViewHierarchyChanged(bool is_add, View* parent, - View* child) { - // When the toolbar is removed, traverse to the next accessible toolbar. - if (child == selected_focused_view_ && !is_add) { - selected_focused_view_->SetHotTracked(false); - selected_focused_view_ = NULL; - SetFocusToLastFocusedView(); +//////////////////////////////////////////////////////////////////////////////// +// FocusChangeListener overrides: + +void AccessibleToolbarView::FocusWillChange(views::View* focused_before, + views::View* focused_now) { + if (!focused_now || !IsParentOf(focused_now)) { + // The focus is no longer in the toolbar, so we should remove toolbar + // focus (i.e. make most of the controls not focusable again). + // Defer this rather than running it right away, for two reasons: + // 1. Sometimes the focus gets sets to NULL and then immediately back + // to something in this toolbar. We don't want to do anything in + // that case. + // 2. If we do want to remove toolbar focus, we can't remove this as + // a focus change listener while FocusManager is in the middle of + // iterating over the list of listeners. + MessageLoop::current()->PostTask( + FROM_HERE, method_factory_.NewRunnableMethod( + &AccessibleToolbarView::RemoveToolbarFocusIfNoChildHasFocus)); } } -void AccessibleToolbarView::SetFocusToAccessibleView() { - // Hot-track new focused button. - selected_focused_view_->SetHotTracked(true); - - // Show the tooltip for the view that got the focus. - if (GetWidget()->GetTooltipManager()) { - GetWidget()->GetTooltipManager()->ShowKeyboardTooltip( - selected_focused_view_); - } +//////////////////////////////////////////////////////////////////////////////// +// FocusTraversable overrides: -#if defined(OS_WIN) - // Retrieve information to generate an accessible focus event. - gfx::NativeView wnd = GetWidget()->GetNativeView(); - int view_id = selected_focused_view_->GetID(); - // Notify Access Technology that there was a change in keyboard focus. - ::NotifyWinEvent(EVENT_OBJECT_FOCUS, wnd, OBJID_CLIENT, - static_cast<LONG>(view_id)); -#else - NOTIMPLEMENTED(); -#endif +views::FocusSearch* AccessibleToolbarView::GetFocusSearch() { + DCHECK(toolbar_has_focus_); + return &focus_search_; } -void AccessibleToolbarView::SetFocusToLastFocusedView() { - views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance(); - views::View* focused_view = - view_storage->RetrieveView(last_focused_view_storage_id_); - if (focused_view) { - view_storage->RemoveView(last_focused_view_storage_id_); - focused_view->RequestFocus(); - } else { - // The ancestor *must* be a BrowserView. The check below can ensure that. - views::View* view = GetAncestorWithClassName(BrowserView::kViewClassName); - if (!view) - return; - BrowserView* browser_view = static_cast<BrowserView*>(view); +views::FocusTraversable* AccessibleToolbarView::GetFocusTraversableParent() { + DCHECK(toolbar_has_focus_); + return NULL; +} - // Force the focus to be set on the location bar. - browser_view->SetFocusToLocationBar(false); - } +views::View* AccessibleToolbarView::GetFocusTraversableParentView() { + DCHECK(toolbar_has_focus_); + return NULL; } diff --git a/chrome/browser/views/accessible_toolbar_view.h b/chrome/browser/views/accessible_toolbar_view.h index 0af7b01..7ecb572 100644 --- a/chrome/browser/views/accessible_toolbar_view.h +++ b/chrome/browser/views/accessible_toolbar_view.h @@ -5,58 +5,90 @@ #ifndef CHROME_BROWSER_VIEWS_ACCESSIBLE_TOOLBAR_VIEW_H_ #define CHROME_BROWSER_VIEWS_ACCESSIBLE_TOOLBAR_VIEW_H_ +#include "base/hash_tables.h" +#include "base/task.h" +#include "chrome/browser/views/accessibility_event_router_views.h" +#include "views/focus/focus_manager.h" #include "views/view.h" -// This class provides keyboard access to any view that extends it by intiating -// ALT+SHIFT+T. And once you press TAB or SHIFT-TAB, it will traverse all the -// toolbars within Chrome. Child views are traversed in the order they were -// added. +namespace views { +class FocusSearch; +} -class AccessibleToolbarView : public views::View { +// This class provides keyboard access to any view that extends it, typically +// a toolbar. The user sets focus to a control in this view by pressing +// F6 to traverse all panes, or by pressing a shortcut that jumps directly +// to this toolbar. +class AccessibleToolbarView : public views::View, + public views::FocusChangeListener, + public views::FocusTraversable { public: AccessibleToolbarView(); virtual ~AccessibleToolbarView(); - // Initiate the traversal on the toolbar. The last focused view is stored in - // the ViewStorage with the corresponding |view_storage_id|. - void InitiateTraversal(int view_storage_id); + // Set focus to the toolbar with complete keyboard access. + // Focus will be restored to the ViewStorage with id |view_storage_id| + // if the user escapes. If |initial_focus| is not NULL, that control will get + // the initial focus, if it's enabled and focusable. Returns true if + // the toolbar was able to receive focus. + bool SetToolbarFocus(int view_storage_id, View* initial_focus); + + // Set focus to the toolbar with complete keyboard access, with the + // focus initially set to the default child. Focus will be restored + // to the ViewStorage with id |view_storage_id| if the user escapes. + // Returns true if the toolbar was able to receive focus. + bool SetToolbarFocusAndFocusDefault(int view_storage_id); // Overridden from views::View: - virtual void DidGainFocus(); - virtual void WillLoseFocus(); - virtual bool OnKeyPressed(const views::KeyEvent& e); - virtual bool OnKeyReleased(const views::KeyEvent& e); - virtual bool SkipDefaultKeyEventProcessing(const views::KeyEvent& e); - virtual void ShowContextMenu(const gfx::Point& p, bool is_mouse_gesture); - virtual void RequestFocus(); + virtual FocusTraversable* GetPaneFocusTraversable(); + virtual bool AcceleratorPressed(const views::Accelerator& accelerator); + virtual void SetVisible(bool flag); virtual bool GetAccessibleRole(AccessibilityTypes::Role* role); - virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child); - virtual View* GetAccFocusedChildView() { return selected_focused_view_; } + + // Overridden from views::FocusChangeListener: + virtual void FocusWillChange(View* focused_before, + View* focused_now); + + // Overridden from views::FocusTraversable: + virtual views::FocusSearch* GetFocusSearch(); + virtual FocusTraversable* GetFocusTraversableParent(); + virtual View* GetFocusTraversableParentView(); protected: - // Returns the next accessible view on the toolbar, starting from the given - // |view_index|. |forward| when true means it will navigate from left to right - // and vice versa when false. If |view_index| is -1 the first accessible child - // is returned. - views::View* GetNextAccessibleView(int view_index, bool forward); - - // Invoked from GetNextAccessibleViewIndex to determine if |view| can be - // traversed to. Default implementation returns true, override to return false - // for views you don't want reachable. - virtual bool IsAccessibleViewTraversable(views::View* view); - - private: - // Sets the focus to the currently |acc_focused_view_| view. - void SetFocusToAccessibleView(); - - // Retrieve the focused view from the storage so we can request focus back - // to it. If |focus_view| is null, we place focus on the default view. - // |selected_focused_view_| doesn't need to reset here since it will be - // dealt within the WillLoseFocus method. - void SetFocusToLastFocusedView(); - - // Selected child view currently having accessibility focus. - views::View* selected_focused_view_; + // A subclass can override this to provide a default focusable child + // other than the first focusable child. + virtual views::View* GetDefaultFocusableChild() { return NULL; } + + // Remove toolbar focus unless a child (including indirect children) + // still has the focus. + void RemoveToolbarFocusIfNoChildHasFocus(); + + void RemoveToolbarFocus(); + + void RestoreLastFocusedView(); + + View* GetFirstFocusableChild(); + View* GetLastFocusableChild(); + + bool toolbar_has_focus_; + + ScopedRunnableMethodFactory<AccessibleToolbarView> method_factory_; + + // Save the focus manager rather than calling GetFocusManager(), + // so that we can remove focus listeners in the destructor. + views::FocusManager* focus_manager_; + + // Our custom focus search implementation that traps focus in this + // toolbar and traverses all views that are focusable for accessibility, + // not just those that are normally focusable. + views::FocusSearch focus_search_; + + // Registered accelerators + views::Accelerator home_key_; + views::Accelerator end_key_; + views::Accelerator escape_key_; + views::Accelerator left_key_; + views::Accelerator right_key_; // Last focused view that issued this traversal. int last_focused_view_storage_id_; diff --git a/chrome/browser/views/browser_actions_container.cc b/chrome/browser/views/browser_actions_container.cc index 142cceb..18eef4e 100644 --- a/chrome/browser/views/browser_actions_container.cc +++ b/chrome/browser/views/browser_actions_container.cc @@ -227,10 +227,7 @@ bool BrowserActionButton::Activate() { } bool BrowserActionButton::OnMousePressed(const views::MouseEvent& e) { - showing_context_menu_ = e.IsRightMouseButton(); - if (showing_context_menu_) { - SetButtonPushed(); - + if (e.IsRightMouseButton()) { // Get the top left point of this button in screen coordinates. gfx::Point point = gfx::Point(0, 0); ConvertPointToScreen(this, &point); @@ -238,15 +235,7 @@ bool BrowserActionButton::OnMousePressed(const views::MouseEvent& e) { // Make the menu appear below the button. point.Offset(0, height()); - // Reconstructs the menu every time because the menu's contents are dynamic. - context_menu_contents_ = new ExtensionContextMenuModel( - extension(), panel_->browser(), panel_); - context_menu_menu_.reset(new views::Menu2(context_menu_contents_.get())); - context_menu_menu_->RunContextMenuAt(point); - - SetButtonNotPushed(); - showing_context_menu_ = false; - + ShowContextMenu(point, true); return false; } else if (IsPopup()) { return MenuButton::OnMousePressed(e); @@ -278,6 +267,21 @@ void BrowserActionButton::OnMouseExited(const views::MouseEvent& e) { TextButton::OnMouseExited(e); } +void BrowserActionButton::ShowContextMenu(const gfx::Point& p, + bool is_mouse_gesture) { + showing_context_menu_ = true; + SetButtonPushed(); + + // Reconstructs the menu every time because the menu's contents are dynamic. + context_menu_contents_ = new ExtensionContextMenuModel( + extension(), panel_->browser(), panel_); + context_menu_menu_.reset(new views::Menu2(context_menu_contents_.get())); + context_menu_menu_->RunContextMenuAt(p); + + SetButtonNotPushed(); + showing_context_menu_ = false; +} + void BrowserActionButton::SetButtonPushed() { SetState(views::CustomButton::BS_PUSHED); menu_visible_ = true; diff --git a/chrome/browser/views/browser_actions_container.h b/chrome/browser/views/browser_actions_container.h index 918a927..1b7dbed 100644 --- a/chrome/browser/views/browser_actions_container.h +++ b/chrome/browser/views/browser_actions_container.h @@ -89,6 +89,7 @@ class BrowserActionButton : public views::MenuButton, virtual void OnMouseReleased(const views::MouseEvent& e, bool canceled); virtual bool OnKeyReleased(const views::KeyEvent& e); virtual void OnMouseExited(const views::MouseEvent& event); + virtual void ShowContextMenu(const gfx::Point& p, bool is_mouse_gesture); // Does this button's action have a popup? virtual bool IsPopup(); diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc index b6d5c94..a325e52 100644 --- a/chrome/browser/views/frame/browser_view.cc +++ b/chrome/browser/views/frame/browser_view.cc @@ -407,6 +407,8 @@ void BrowserView::SetShowState(int state) { BrowserView::BrowserView(Browser* browser) : views::ClientView(NULL, NULL), + last_focused_view_storage_id_( + views::ViewStorage::GetSharedInstance()->CreateStorageID()), frame_(NULL), browser_(browser), active_bookmark_bar_(NULL), @@ -425,8 +427,6 @@ BrowserView::BrowserView(Browser* browser) ticker_(0), #endif extension_shelf_(NULL), - last_focused_view_storage_id_( - views::ViewStorage::GetSharedInstance()->CreateStorageID()), extension_app_icon_loader_(this) { browser_->tabstrip_model()->AddObserver(this); } @@ -648,17 +648,6 @@ void BrowserView::PrepareToRunSystemMenu(HMENU menu) { } #endif -void BrowserView::TraverseNextAccessibleToolbar(bool forward) { - // TODO(mohamed) This needs to be smart, that applies to all toolbars. - // Currently it just traverses between bookmarks and toolbar. - if (!forward && toolbar_->IsVisible() && toolbar_->IsEnabled()) { - toolbar_->RequestFocus(); - } else if (forward && bookmark_bar_view_->IsVisible() && - bookmark_bar_view_->IsEnabled()) { - bookmark_bar_view_->RequestFocus(); - } -} - // static void BrowserView::RegisterBrowserViewPrefs(PrefService* prefs) { prefs->RegisterIntegerPref(prefs::kPluginMessageResponseTimeout, @@ -872,7 +861,14 @@ void BrowserView::FocusToolbar() { // Start the traversal within the main toolbar, passing it the storage id // of the view where focus should be returned if the user exits the toolbar. SaveFocusedView(); - toolbar_->InitiateTraversal(last_focused_view_storage_id_); + toolbar_->SetToolbarFocus(last_focused_view_storage_id_, NULL); +} + +void BrowserView::FocusBookmarksToolbar() { + if (active_bookmark_bar_ && bookmark_bar_view_->IsVisible()) { + SaveFocusedView(); + bookmark_bar_view_->SetToolbarFocus(last_focused_view_storage_id_, NULL); + } } void BrowserView::FocusPageAndAppMenus() { @@ -884,7 +880,65 @@ void BrowserView::FocusPageAndAppMenus() { // // Not used on the Mac, which has a normal menu bar. SaveFocusedView(); - toolbar_->EnterMenuBarEmulationMode(last_focused_view_storage_id_, NULL); + toolbar_->SetToolbarFocusAndFocusPageMenu(last_focused_view_storage_id_); +} + +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. When a toolbar has + // pane 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 - + // each of the accessible toolbars, then 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<AccessibleToolbarView*> accessible_toolbars; + GetAccessibleToolbars(&accessible_toolbars); + // Add NULL, which represents the tab contents getting focus + accessible_toolbars.push_back(NULL); + + // Figure out which toolbar (if any) currently has the focus. + AccessibleToolbarView* current_toolbar = NULL; + views::View* focused_view = GetRootView()->GetFocusedView(); + int index = -1; + int count = static_cast<int>(accessible_toolbars.size()); + if (focused_view) { + for (int i = 0; i < count; i++) { + if (accessible_toolbars[i]->IsParentOf(focused_view)) { + current_toolbar = accessible_toolbars[i]; + index = i; + break; + } + } + } + + // If the focus isn't currently in a toolbar, save the focus so we + // can restore it if the user presses Escape. + if (focused_view && !current_toolbar) + SaveFocusedView(); + + // Try to focus the next pane; if SetToolbarFocusAndFocusDefault returns + // false it means the toolbar 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) % count; + AccessibleToolbarView* next_toolbar = accessible_toolbars[index]; + + if (next_toolbar) { + if (next_toolbar->SetToolbarFocusAndFocusDefault( + last_focused_view_storage_id_)) { + break; + } + } else { + GetTabContentsContainerView()->RequestFocus(); + break; + } + } } void BrowserView::SaveFocusedView() { @@ -1603,6 +1657,18 @@ gfx::Size BrowserView::GetMinimumSize() { } /////////////////////////////////////////////////////////////////////////////// +// BrowserView, protected + +void BrowserView::GetAccessibleToolbars( + std::vector<AccessibleToolbarView*>* toolbars) { + // This should be in the order of pane traversal of the toolbars using F6. + // If one of these is invisible or has no focusable children, it will be + // automatically skipped. + toolbars->push_back(toolbar_); + toolbars->push_back(bookmark_bar_view_.get()); +} + +/////////////////////////////////////////////////////////////////////////////// // BrowserView, views::View overrides: std::string BrowserView::GetClassName() const { diff --git a/chrome/browser/views/frame/browser_view.h b/chrome/browser/views/frame/browser_view.h index 6c6c4b4..96a6f86 100644 --- a/chrome/browser/views/frame/browser_view.h +++ b/chrome/browser/views/frame/browser_view.h @@ -8,6 +8,7 @@ #include <map> #include <set> #include <string> +#include <vector> #include "app/menus/simple_menu_model.h" #include "base/scoped_ptr.h" @@ -38,6 +39,7 @@ // NOTE: For more information about the objects and files in this directory, // view: http://dev.chromium.org/developers/design-documents/browser-window +class AccessibleToolbarView; class AccessibleViewHelper; class BookmarkBarView; class Browser; @@ -211,10 +213,6 @@ class BrowserView : public BrowserBubbleHost, void PrepareToRunSystemMenu(HMENU menu); #endif - // Traverses to the next toolbar. |forward| when true, will navigate from left - // to right and vice versa when false. - void TraverseNextAccessibleToolbar(bool forward); - // Returns true if the Browser object associated with this BrowserView is a // normal-type window (i.e. a browser window, not an app or popup). bool IsBrowserTypeNormal() const { @@ -287,6 +285,9 @@ class BrowserView : public BrowserBubbleHost, virtual void UpdateToolbar(TabContents* contents, bool should_restore_state); virtual void FocusToolbar(); virtual void FocusPageAndAppMenus(); + virtual void FocusBookmarksToolbar(); + virtual void FocusChromeOSStatus() {} + virtual void RotatePaneFocus(bool forwards); virtual void DestroyBrowser(); virtual bool IsBookmarkBarVisible() const; virtual bool IsBookmarkBarAnimating() const; @@ -397,6 +398,19 @@ class BrowserView : public BrowserBubbleHost, virtual void InfoBarSizeChanged(bool is_animating); protected: + // Appends to |toolbars| a pointer to each AccessibleToolbarView that + // can be traversed using F6, in the order they should be traversed. + // Abstracted here so that it can be extended for Chrome OS. + virtual void GetAccessibleToolbars( + std::vector<AccessibleToolbarView*>* toolbars); + + // Save the current focused view to view storage + void SaveFocusedView(); + + int last_focused_view_storage_id() const { + return last_focused_view_storage_id_; + } + // Overridden from views::View: virtual std::string GetClassName() const; virtual void Layout(); @@ -487,12 +501,12 @@ class BrowserView : public BrowserBubbleHost, // Initialize the hung plugin detector. void InitHangMonitor(); - // Save the current focused view to view storage - void SaveFocusedView(); - // Initialize class statics. static void InitClass(); + // Last focused view that issued a tab traversal. + int last_focused_view_storage_id_; + // The BrowserFrame that hosts this view. BrowserFrame* frame_; @@ -592,9 +606,6 @@ class BrowserView : public BrowserBubbleHost, scoped_ptr<BrowserExtender> browser_extender_; - // Last focused view that issued a tab traversal. - int last_focused_view_storage_id_; - UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_; scoped_ptr<AccessibleViewHelper> accessible_view_helper_; diff --git a/chrome/browser/views/location_bar/location_bar_view.cc b/chrome/browser/views/location_bar/location_bar_view.cc index 71c7d68..b387d4a 100644 --- a/chrome/browser/views/location_bar/location_bar_view.cc +++ b/chrome/browser/views/location_bar/location_bar_view.cc @@ -764,11 +764,13 @@ void LocationBarView::RefreshPageActionViews() { page_action_views_.resize(page_actions.size()); - for (size_t i = 0; i < page_actions.size(); ++i) { + // Add the page actions in reverse order, so that the child views are + // inserted in left-to-right order for accessibility. + for (int i = page_actions.size() - 1; i >= 0; --i) { page_action_views_[i] = new PageActionWithBadgeView( new PageActionImageView(this, profile_, page_actions[i])); page_action_views_[i]->SetVisible(false); - AddChildView(page_action_views_[i]); + AddChildView(GetChildIndex(star_view_), page_action_views_[i]); } } diff --git a/chrome/browser/views/location_bar/page_action_image_view.cc b/chrome/browser/views/location_bar/page_action_image_view.cc index 16623d4..e954da7 100644 --- a/chrome/browser/views/location_bar/page_action_image_view.cc +++ b/chrome/browser/views/location_bar/page_action_image_view.cc @@ -42,6 +42,8 @@ PageActionImageView::PageActionImageView(LocationBarView* owner, Extension::kPageActionIconMaxSize), ImageLoadingTracker::DONT_CACHE); } + + set_accessibility_focusable(true); } PageActionImageView::~PageActionImageView() { @@ -98,6 +100,11 @@ void PageActionImageView::ExecuteAction(int button, } } +bool PageActionImageView::GetAccessibleRole(AccessibilityTypes::Role* role) { + *role = AccessibilityTypes::ROLE_PUSHBUTTON; + return true; +} + bool PageActionImageView::OnMousePressed(const views::MouseEvent& event) { // We want to show the bubble on mouse release; that is the standard behavior // for buttons. (Also, triggering on mouse press causes bugs like @@ -119,24 +126,36 @@ void PageActionImageView::OnMouseReleased(const views::MouseEvent& event, // Get the top left point of this button in screen coordinates. gfx::Point menu_origin; ConvertPointToScreen(this, &menu_origin); - // Make the menu appear below the button. menu_origin.Offset(0, height()); - - Extension* extension = profile_->GetExtensionsService()->GetExtensionById( - page_action()->extension_id(), false); - Browser* browser = BrowserView::GetBrowserViewForNativeWindow( - platform_util::GetTopLevel(GetWidget()->GetNativeView()))->browser(); - context_menu_contents_ = - new ExtensionContextMenuModel(extension, browser, this); - context_menu_menu_.reset(new views::Menu2(context_menu_contents_.get())); - context_menu_menu_->RunContextMenuAt(menu_origin); + ShowContextMenu(menu_origin, true); return; } ExecuteAction(button, false); // inspect_with_devtools } +bool PageActionImageView::OnKeyPressed(const views::KeyEvent& e) { + if (e.GetKeyCode() == base::VKEY_SPACE || + e.GetKeyCode() == base::VKEY_RETURN) { + ExecuteAction(1, false); + return true; + } + return false; +} + +void PageActionImageView::ShowContextMenu(const gfx::Point& p, + bool is_mouse_gesture) { + Extension* extension = profile_->GetExtensionsService()->GetExtensionById( + page_action()->extension_id(), false); + Browser* browser = BrowserView::GetBrowserViewForNativeWindow( + platform_util::GetTopLevel(GetWidget()->GetNativeView()))->browser(); + context_menu_contents_ = + new ExtensionContextMenuModel(extension, browser, this); + context_menu_menu_.reset(new views::Menu2(context_menu_contents_.get())); + context_menu_menu_->RunContextMenuAt(p); +} + void PageActionImageView::OnImageLoaded( SkBitmap* image, ExtensionResource resource, int index) { // We loaded icons()->size() icons, plus one extra if the page action had diff --git a/chrome/browser/views/location_bar/page_action_image_view.h b/chrome/browser/views/location_bar/page_action_image_view.h index 64c537d..e61cb3e 100644 --- a/chrome/browser/views/location_bar/page_action_image_view.h +++ b/chrome/browser/views/location_bar/page_action_image_view.h @@ -40,8 +40,11 @@ class PageActionImageView : public views::ImageView, } // Overridden from view. + virtual bool GetAccessibleRole(AccessibilityTypes::Role* role); virtual bool OnMousePressed(const views::MouseEvent& event); virtual void OnMouseReleased(const views::MouseEvent& event, bool canceled); + virtual bool OnKeyPressed(const views::KeyEvent& e); + virtual void ShowContextMenu(const gfx::Point& p, bool is_mouse_gesture); // Overridden from ImageLoadingTracker. virtual void OnImageLoaded( diff --git a/chrome/browser/views/location_bar/page_action_with_badge_view.cc b/chrome/browser/views/location_bar/page_action_with_badge_view.cc index 6d890f9..603b4f9 100644 --- a/chrome/browser/views/location_bar/page_action_with_badge_view.cc +++ b/chrome/browser/views/location_bar/page_action_with_badge_view.cc @@ -13,6 +13,12 @@ PageActionWithBadgeView::PageActionWithBadgeView( AddChildView(image_view_); } +bool PageActionWithBadgeView::GetAccessibleRole( + AccessibilityTypes::Role* role) { + *role = AccessibilityTypes::ROLE_GROUPING; + return true; +} + gfx::Size PageActionWithBadgeView::GetPreferredSize() { return gfx::Size(Extension::kPageActionIconMaxSize, Extension::kPageActionIconMaxSize); diff --git a/chrome/browser/views/location_bar/page_action_with_badge_view.h b/chrome/browser/views/location_bar/page_action_with_badge_view.h index 2df76bb..c54573e 100644 --- a/chrome/browser/views/location_bar/page_action_with_badge_view.h +++ b/chrome/browser/views/location_bar/page_action_with_badge_view.h @@ -19,6 +19,7 @@ class PageActionWithBadgeView : public views::View { PageActionImageView* image_view() { return image_view_; } + virtual bool GetAccessibleRole(AccessibilityTypes::Role* role); virtual gfx::Size GetPreferredSize(); void UpdateVisibility(TabContents* contents, const GURL& url); diff --git a/chrome/browser/views/location_bar/star_view.cc b/chrome/browser/views/location_bar/star_view.cc index 30c7888..c559a27 100644 --- a/chrome/browser/views/location_bar/star_view.cc +++ b/chrome/browser/views/location_bar/star_view.cc @@ -16,6 +16,7 @@ StarView::StarView(CommandUpdater* command_updater) : command_updater_(command_updater) { SetID(VIEW_ID_STAR_BUTTON); SetToggled(false); + set_accessibility_focusable(true); } StarView::~StarView() { @@ -48,6 +49,15 @@ void StarView::OnMouseReleased(const views::MouseEvent& event, bool canceled) { command_updater_->ExecuteCommand(IDC_BOOKMARK_PAGE); } +bool StarView::OnKeyPressed(const views::KeyEvent& e) { + if (e.GetKeyCode() == base::VKEY_SPACE || + e.GetKeyCode() == base::VKEY_RETURN) { + command_updater_->ExecuteCommand(IDC_BOOKMARK_PAGE); + return true; + } + return false; +} + void StarView::InfoBubbleClosing(InfoBubble* info_bubble, bool closed_by_escape) { } @@ -55,4 +65,3 @@ void StarView::InfoBubbleClosing(InfoBubble* info_bubble, bool StarView::CloseOnEscape() { return true; } - diff --git a/chrome/browser/views/location_bar/star_view.h b/chrome/browser/views/location_bar/star_view.h index ce8aadc3..485cfb5 100644 --- a/chrome/browser/views/location_bar/star_view.h +++ b/chrome/browser/views/location_bar/star_view.h @@ -12,6 +12,7 @@ class CommandUpdater; class InfoBubble; namespace views { +class KeyEvent; class MouseEvent; } @@ -28,6 +29,7 @@ class StarView : public views::ImageView, public InfoBubbleDelegate { virtual bool GetAccessibleRole(AccessibilityTypes::Role* role); virtual bool OnMousePressed(const views::MouseEvent& event); virtual void OnMouseReleased(const views::MouseEvent& event, bool canceled); + virtual bool OnKeyPressed(const views::KeyEvent& e); // InfoBubbleDelegate overrides: virtual void InfoBubbleClosing(InfoBubble* info_bubble, diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc index 2db68f8..1eee8f8 100644 --- a/chrome/browser/views/toolbar_view.cc +++ b/chrome/browser/views/toolbar_view.cc @@ -81,8 +81,6 @@ ToolbarView::ToolbarView(Browser* browser) profile_(NULL), browser_(browser), profiles_menu_contents_(NULL), - last_focused_view_storage_id_(-1), - menu_bar_emulation_mode_(false), ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), destroyed_flag_(NULL), collapsed_(false) { @@ -113,11 +111,6 @@ ToolbarView::~ToolbarView() { // NOTE: Don't remove the command observers here. This object gets destroyed // after the Browser (which owns the CommandUpdater), so the CommandUpdater is // already gone. - - if (menu_bar_emulation_mode_) { - focus_manager_->UnregisterAccelerators(this); - focus_manager_->RemoveFocusChangeListener(this); - } } void ToolbarView::Init(Profile* profile) { @@ -175,7 +168,6 @@ void ToolbarView::Init(Profile* profile) { page_menu_->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_PAGE)); page_menu_->SetTooltipText(l10n_util::GetString(IDS_PAGEMENU_TOOLTIP)); page_menu_->SetID(VIEW_ID_PAGE_MENU); - AddChildView(page_menu_); } app_menu_ = new views::MenuButton(NULL, std::wstring(), this, false); @@ -186,19 +178,23 @@ void ToolbarView::Init(Profile* profile) { if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kBookmarkMenu)) { bookmark_menu_ = new BookmarkMenuButton(browser_); - AddChildView(bookmark_menu_); } else { bookmark_menu_ = NULL; } LoadImages(); + // Always add children in order from left to right, for accessibility. AddChildView(back_); AddChildView(forward_); AddChildView(home_); AddChildView(reload_); AddChildView(location_bar_); AddChildView(browser_actions_); + if (bookmark_menu_) + AddChildView(bookmark_menu_); + if (page_menu_) + AddChildView(page_menu_); AddChildView(app_menu_); location_bar_->Init(); @@ -216,8 +212,6 @@ void ToolbarView::Init(Profile* profile) { SetAppMenuModel(new AppMenuModel(this, browser_)); } } - - focus_manager_ = GetFocusManager(); } void ToolbarView::SetProfile(Profile* profile) { @@ -240,44 +234,15 @@ void ToolbarView::SetAppMenuModel(menus::SimpleMenuModel* model) { app_menu_menu_.reset(new views::Menu2(app_menu_model_.get())); } -void ToolbarView::EnterMenuBarEmulationMode(int last_focused_view_storage_id, - views::MenuButton* menu_to_focus) { - last_focused_view_storage_id_ = last_focused_view_storage_id; - if (!menu_to_focus) - menu_to_focus = page_menu_; - - // If we're already in the menu bar emulation mode, just set the focus. - if (menu_bar_emulation_mode_) { - menu_to_focus->RequestFocus(); - return; - } +void ToolbarView::SetToolbarFocusAndFocusLocationBar(int view_storage_id) { + SetToolbarFocus(view_storage_id, location_bar_); +} - // Make the menus focusable and set focus to the initial menu. - menu_bar_emulation_mode_ = true; - page_menu_->SetFocusable(true); - app_menu_->SetFocusable(true); - menu_to_focus->RequestFocus(); - - // Listen so we know when focus has moved to something other than one - // of these menus. - focus_manager_->AddFocusChangeListener(this); - - // Add accelerators so that the usual keys used to interact with a - // menu bar work as expected. - views::Accelerator return_key(base::VKEY_RETURN, false, false, false); - focus_manager_->RegisterAccelerator(return_key, this); - views::Accelerator space(base::VKEY_SPACE, false, false, false); - focus_manager_->RegisterAccelerator(space, this); - views::Accelerator escape(base::VKEY_ESCAPE, false, false, false); - focus_manager_->RegisterAccelerator(escape, this); - views::Accelerator down(base::VKEY_DOWN, false, false, false); - focus_manager_->RegisterAccelerator(down, this); - views::Accelerator up(base::VKEY_UP, false, false, false); - focus_manager_->RegisterAccelerator(up, this); - views::Accelerator left(base::VKEY_LEFT, false, false, false); - focus_manager_->RegisterAccelerator(left, this); - views::Accelerator right(base::VKEY_RIGHT, false, false, false); - focus_manager_->RegisterAccelerator(right, this); +void ToolbarView::SetToolbarFocusAndFocusPageMenu(int view_storage_id) { + if (page_menu_) + SetToolbarFocus(view_storage_id, page_menu_); + else + SetToolbarFocus(view_storage_id, app_menu_); } void ToolbarView::AddMenuListener(views::MenuListener* listener) { @@ -311,32 +276,6 @@ void ToolbarView::SetCollapsed(bool val) { } //////////////////////////////////////////////////////////////////////////////// -// ToolbarView, FocusChangeListener overrides: - -void ToolbarView::FocusWillChange(views::View* focused_before, - views::View* focused_now) { - // If the focus is switching to something outside the menu bar, - // take it out of the focus traversal. - if ((focused_now != NULL) && (focused_now != page_menu_) && - (focused_now != app_menu_)) { - // Post ExitMenuBarEmulationMode to the queue rather than running it - // right away, because otherwise we'll remove ourselves from the - // list of listeners while FocusManager is in the middle of iterating - // over that list. - MessageLoop::current()->PostTask( - FROM_HERE, method_factory_.NewRunnableMethod( - &ToolbarView::ExitMenuBarEmulationMode)); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// ToolbarView, AccessibleToolbarView overrides: - -bool ToolbarView::IsAccessibleViewTraversable(views::View* view) { - return view != location_bar_; -} - -//////////////////////////////////////////////////////////////////////////////// // ToolbarView, Menu::BaseControllerDelegate overrides: bool ToolbarView::GetAcceleratorInfo(int id, menus::Accelerator* accel) { @@ -476,42 +415,6 @@ void ToolbarView::ExecuteCommand(int command_id) { //////////////////////////////////////////////////////////////////////////////// // ToolbarView, views::View overrides: -bool ToolbarView::AcceleratorPressed( - const views::Accelerator& accelerator) { - // The only accelerators we handle here are if the menus are focused. - views::View* focused_view = GetFocusManager()->GetFocusedView(); - if (focused_view != page_menu_ && focused_view != app_menu_) { - ExitMenuBarEmulationMode(); - return false; - } - - // Safe to cast, given the check above. - views::MenuButton* menu = static_cast<views::MenuButton*>(focused_view); - switch (accelerator.GetKeyCode()) { - case base::VKEY_ESCAPE: - RestoreLastFocusedView(); - return true; - case base::VKEY_LEFT: - case base::VKEY_RIGHT: - if (page_menu_) { - ((menu == app_menu_) ? page_menu_ : app_menu_)->RequestFocus(); - return true; - } - return false; - case base::VKEY_UP: - case base::VKEY_DOWN: - case base::VKEY_RETURN: - case base::VKEY_SPACE: - // Hide the tooltip before activating a menu button. - if (GetWidget()->GetTooltipManager()) - GetWidget()->GetTooltipManager()->HideKeyboardTooltip(); - ActivateMenuButton(menu); - return true; - default: - return false; - } -} - gfx::Size ToolbarView::GetPreferredSize() { if (IsDisplayModeNormal()) { int min_width = kControlIndent + back_->GetPreferredSize().width() + @@ -661,6 +564,13 @@ void ToolbarView::ThemeChanged() { } //////////////////////////////////////////////////////////////////////////////// +// ToolbarView, protected: + +views::View* ToolbarView::GetDefaultFocusableChild() { + return location_bar_; +} + +//////////////////////////////////////////////////////////////////////////////// // ToolbarView, private: int ToolbarView::PopupTopSpacing() const { @@ -875,34 +785,6 @@ void ToolbarView::ActivateMenuButton(views::MenuButton* menu_button) { menu_button->Activate(); #if defined(OS_WIN) - EnterMenuBarEmulationMode(last_focused_view_storage_id_, menu_button); + SetToolbarFocus(NULL, menu_button); #endif } - -void ToolbarView::ExitMenuBarEmulationMode() { - if ((page_menu_ && page_menu_->HasFocus()) || app_menu_->HasFocus()) - RestoreLastFocusedView(); - - focus_manager_->UnregisterAccelerators(this); - focus_manager_->RemoveFocusChangeListener(this); - if (page_menu_) - page_menu_->SetFocusable(false); - app_menu_->SetFocusable(false); - menu_bar_emulation_mode_ = false; -} - -void ToolbarView::RestoreLastFocusedView() { - views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance(); - views::View* last_focused_view = - view_storage->RetrieveView(last_focused_view_storage_id_); - if (last_focused_view) { - last_focused_view->RequestFocus(); - } else { - // Focus the location bar - views::View* view = GetAncestorWithClassName(BrowserView::kViewClassName); - if (view) { - BrowserView* browser_view = static_cast<BrowserView*>(view); - browser_view->SetFocusToLocationBar(false); - } - } -} diff --git a/chrome/browser/views/toolbar_view.h b/chrome/browser/views/toolbar_view.h index a99d658..d343546 100644 --- a/chrome/browser/views/toolbar_view.h +++ b/chrome/browser/views/toolbar_view.h @@ -22,7 +22,6 @@ #include "views/controls/menu/menu.h" #include "views/controls/menu/menu_wrapper.h" #include "views/controls/menu/view_menu_delegate.h" -#include "views/focus/focus_manager.h" #include "views/view.h" class BrowserActionsContainer; @@ -36,7 +35,6 @@ class Menu2; // The Browser Window's toolbar. class ToolbarView : public AccessibleToolbarView, public views::ViewMenuDelegate, - public views::FocusChangeListener, public menus::SimpleMenuModel::Delegate, public LocationBarView::Delegate, public AnimationDelegate, @@ -63,18 +61,15 @@ class ToolbarView : public AccessibleToolbarView, // Sets the app menu model. void SetAppMenuModel(menus::SimpleMenuModel* model); - // Focuses the page menu and enters a special mode where the page - // and app menus are focusable and allow for keyboard navigation just - // like a normal menu bar. As soon as focus leaves one of the menus, - // the special mode is exited. - // - // Pass it the storage id of the view where focus should be returned - // if the user escapes, and the menu button to focus initially. If - // |menu_to_focus| is NULL, it will focus the page menu by default. - // - // Not used on the Mac, which has a "normal" menu bar. - void EnterMenuBarEmulationMode(int last_focused_view_storage_id, - views::MenuButton* menu_to_focus); + // Set focus to the toolbar with complete keyboard access, with the + // focus initially set to the location bar. Focus will be restored + // to the ViewStorage with id |view_storage_id| if the user escapes. + void SetToolbarFocusAndFocusLocationBar(int view_storage_id); + + // Set focus to the toolbar with complete keyboard access, with the + // focus initially set to the page menu. Focus will be restored + // to the ViewStorage with id |view_storage_id| if the user escapes. + void SetToolbarFocusAndFocusPageMenu(int view_storage_id); // Add a listener to receive a callback when the menu opens. void AddMenuListener(views::MenuListener* listener); @@ -92,13 +87,6 @@ class ToolbarView : public AccessibleToolbarView, bool collapsed() const { return collapsed_; } void SetCollapsed(bool val); - // Overridden from views::FocusChangeListener: - virtual void FocusWillChange(views::View* focused_before, - views::View* focused_now); - - // Overridden from AccessibleToolbarView: - virtual bool IsAccessibleViewTraversable(views::View* view); - // Overridden from Menu::BaseControllerDelegate: virtual bool GetAcceleratorInfo(int id, menus::Accelerator* accel); @@ -131,12 +119,16 @@ class ToolbarView : public AccessibleToolbarView, virtual void ExecuteCommand(int command_id); // Overridden from views::View: - virtual bool AcceleratorPressed(const views::Accelerator& accelerator); virtual gfx::Size GetPreferredSize(); virtual void Layout(); virtual void Paint(gfx::Canvas* canvas); virtual void ThemeChanged(); + protected: + // Override this so that when the user presses F6 to rotate toolbar panes, + // the location bar gets focus, not the first control in the toolbar. + virtual views::View* GetDefaultFocusableChild(); + private: // Returns the number of pixels above the location bar in non-normal display. int PopupTopSpacing() const; @@ -165,14 +157,6 @@ class ToolbarView : public AccessibleToolbarView, return display_mode_ == DISPLAYMODE_NORMAL; } - // Take the menus out of the focus traversal, unregister accelerators, - // and stop listening to focus change events. - void ExitMenuBarEmulationMode(); - - // Restore the view that was focused before EnterMenuBarEmulationMode - // was called. - void RestoreLastFocusedView(); - // Starts the recurring timer that periodically asks the upgrade notifier // to pulsate. void ShowUpgradeReminder(); @@ -221,14 +205,6 @@ class ToolbarView : public AccessibleToolbarView, scoped_ptr<views::Menu2> page_menu_menu_; scoped_ptr<views::Menu2> app_menu_menu_; - // Save the focus manager rather than calling GetFocusManager(), - // so that we can remove focus listeners in the destructor. - views::FocusManager* focus_manager_; - - // Storage id for the last view that was focused before focus - // was given to one of the toolbar views. - int last_focused_view_storage_id_; - // Vector of listeners to receive callbacks when the menu opens. std::vector<views::MenuListener*> menu_listeners_; @@ -239,10 +215,6 @@ class ToolbarView : public AccessibleToolbarView, // once, to create a pulsating effect. base::RepeatingTimer<ToolbarView> upgrade_reminder_pulse_timer_; - // Are we in the menu bar emulation mode, where the app and page menu - // are temporarily focusable? - bool menu_bar_emulation_mode_; - // Used to post tasks to switch to the next/previous menu. ScopedRunnableMethodFactory<ToolbarView> method_factory_; diff --git a/chrome/test/test_browser_window.h b/chrome/test/test_browser_window.h index adf3277..1039a81 100644 --- a/chrome/test/test_browser_window.h +++ b/chrome/test/test_browser_window.h @@ -48,6 +48,9 @@ class TestBrowserWindow : public BrowserWindow { bool should_restore_state) {} virtual void FocusToolbar() {} virtual void FocusPageAndAppMenus() {} + virtual void FocusBookmarksToolbar() {} + virtual void FocusChromeOSStatus() {} + virtual void RotatePaneFocus(bool forwards) {} virtual void ShowPageMenu() {} virtual void ShowAppMenu() {} virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event, |