Index: chrome/browser/views/toolbar_view.cc |
diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc |
index 20346c2ab3c14e2373e2229eac6853a6614b49fe..0747331f29b90e51a49a8c3e5e2d569bd732a03b 100644 |
--- a/chrome/browser/views/toolbar_view.cc |
+++ b/chrome/browser/views/toolbar_view.cc |
@@ -51,6 +51,9 @@ |
#include "views/controls/button/button_dropdown.h" |
#include "views/controls/label.h" |
#include "views/drag_utils.h" |
+#include "views/focus/view_storage.h" |
+#include "views/widget/root_view.h" |
+#include "views/widget/tooltip_manager.h" |
#include "views/window/non_client_view.h" |
#include "views/window/window.h" |
@@ -150,6 +153,9 @@ void ZoomMenuModel::Build() { |
ToolbarView::ToolbarView(Browser* browser) |
: model_(browser->toolbar_model()), |
+ acc_focused_view_(NULL), |
+ last_focused_view_storage_id_( |
+ views::ViewStorage::GetSharedInstance()->CreateStorageID()), |
back_(NULL), |
forward_(NULL), |
reload_(NULL), |
@@ -218,11 +224,48 @@ void ToolbarView::Update(TabContents* tab, bool should_restore_state) { |
browser_actions_->RefreshBrowserActionViews(); |
} |
-//////////////////////////////////////////////////////////////////////////////// |
-// ToolbarView, AccessibleToolbarView overrides: |
+int ToolbarView::GetNextAccessibleViewIndex(int view_index, bool nav_left) { |
+ int modifier = 1; |
+ |
+ if (nav_left) |
+ modifier = -1; |
+ |
+ int current_view_index = view_index + modifier; |
+ |
+ while ((current_view_index >= 0) && |
+ (current_view_index < GetChildViewCount())) { |
+ // Skip the location bar, as it has its own keyboard navigation. Also skip |
+ // any views that cannot be interacted with. |
+ if (current_view_index == GetChildIndex(location_bar_) || |
+ !GetChildViewAt(current_view_index)->IsEnabled() || |
+ !GetChildViewAt(current_view_index)->IsVisible()) { |
+ current_view_index += modifier; |
+ continue; |
+ } |
+ // Update view_index with the available button index found. |
+ view_index = current_view_index; |
+ break; |
+ } |
+ // Returns the next available button index, or if no button is available in |
+ // the specified direction, remains where it was. |
+ return view_index; |
+} |
+ |
+void ToolbarView::InitializeTraversal() { |
+ // If MSAA focus exists, we don't need to traverse, since its already active. |
+ if (acc_focused_view_ != NULL) |
+ return; |
-bool ToolbarView::IsAccessibleViewTraversable(views::View* view) { |
- return view != location_bar_; |
+ // Save the last focused view so that when the user presses ESC, it will |
+ // return back to the last focus. |
+ views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance(); |
+ view_storage->StoreView(last_focused_view_storage_id_, |
+ GetRootView()->GetFocusedView()); |
+ |
+ // HACK: Do not use RequestFocus() here, as the toolbar is not marked as |
+ // "focusable". Instead bypass the sanity check in RequestFocus() and just |
+ // force it to focus, which will do the right thing. |
+ GetRootView()->FocusView(this); |
} |
//////////////////////////////////////////////////////////////////////////////// |
@@ -586,6 +629,204 @@ void ToolbarView::ThemeChanged() { |
LoadRightSideControlsImages(); |
} |
+void ToolbarView::ShowContextMenu(int x, int y, bool is_mouse_gesture) { |
+ if (acc_focused_view_) |
+ acc_focused_view_->ShowContextMenu(x, y, is_mouse_gesture); |
+} |
+ |
+void ToolbarView::DidGainFocus() { |
+ // Check to see if MSAA focus should be restored to previously focused button, |
+ // and if button is an enabled, visibled child of toolbar. |
+ if (!acc_focused_view_ || |
+ (acc_focused_view_->GetParent()->GetID() != VIEW_ID_TOOLBAR) || |
+ !acc_focused_view_->IsEnabled() || |
+ !acc_focused_view_->IsVisible()) { |
+ // Find first accessible child (-1 to start search at parent). |
+ int first_acc_child = GetNextAccessibleViewIndex(-1, false); |
+ |
+ // No buttons enabled or visible. |
+ if (first_acc_child == -1) |
+ return; |
+ |
+ set_acc_focused_view(GetChildViewAt(first_acc_child)); |
+ } |
+ |
+ // Default focus is on the toolbar. |
+ int view_index = VIEW_ID_TOOLBAR; |
+ |
+ // Set hot-tracking for child, and update focused_view for MSAA focus event. |
+ if (acc_focused_view_) { |
+ acc_focused_view_->SetHotTracked(true); |
+ |
+ // Show the tooltip for the view that got the focus. |
+ if (GetWidget()->GetTooltipManager()) |
+ GetWidget()->GetTooltipManager()->ShowKeyboardTooltip(acc_focused_view_); |
+ |
+ // Update focused_view with MSAA-adjusted child id. |
+ view_index = acc_focused_view_->GetID(); |
+ } |
+ |
+#if defined(OS_WIN) |
+ gfx::NativeView wnd = GetWidget()->GetNativeView(); |
+ |
+ // Notify Access Technology that there was a change in keyboard focus. |
+ ::NotifyWinEvent(EVENT_OBJECT_FOCUS, wnd, OBJID_CLIENT, |
+ static_cast<LONG>(view_index)); |
+#else |
+ // TODO(port): deal with toolbar a11y focus. |
+ NOTIMPLEMENTED(); |
+#endif |
+} |
+ |
+void ToolbarView::WillLoseFocus() { |
+ // Any tooltips that are active should be hidden when toolbar loses focus. |
+ if (GetWidget() && GetWidget()->GetTooltipManager()) |
+ GetWidget()->GetTooltipManager()->HideKeyboardTooltip(); |
+ |
+ // Removes the Child MSAA view's focus and the view from the ViewStorage, |
+ // when toolbar loses focus. |
+ if (acc_focused_view_) { |
+ acc_focused_view_->SetHotTracked(false); |
+ acc_focused_view_ = NULL; |
+ views::ViewStorage* view_storage = views::ViewStorage::GetSharedInstance(); |
+ view_storage->RemoveView(last_focused_view_storage_id_); |
+ } |
+} |
+ |
+void ToolbarView::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 MSAA focused state. |acc_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); |
+} |
+ |
+bool ToolbarView::OnKeyPressed(const views::KeyEvent& e) { |
+ // Paranoia check, button should be initialized upon toolbar gaining focus. |
+ if (!acc_focused_view_) |
+ return false; |
+ |
+ int focused_view = GetChildIndex(acc_focused_view_); |
+ int next_view = focused_view; |
+ |
+ switch (e.GetKeyCode()) { |
+ case base::VKEY_LEFT: |
+ next_view = GetNextAccessibleViewIndex(focused_view, true); |
+ break; |
+ case base::VKEY_RIGHT: |
+ next_view = GetNextAccessibleViewIndex(focused_view, false); |
+ break; |
+ case base::VKEY_DOWN: |
+ case base::VKEY_RETURN: |
+ // VKEY_SPACE is already handled by the default case. |
+ if (acc_focused_view_->GetID() == VIEW_ID_PAGE_MENU || |
+ acc_focused_view_->GetID() == VIEW_ID_APP_MENU) { |
+ // If a menu button in toolbar is activated and its menu is displayed, |
+ // then active tooltip should be hidden. |
+ if (GetWidget()->GetTooltipManager()) |
+ GetWidget()->GetTooltipManager()->HideKeyboardTooltip(); |
+ // Safe to cast, given to above view id check. |
+ static_cast<views::MenuButton*>(acc_focused_view_)->Activate(); |
+ if (!acc_focused_view_) { |
+ // Activate triggered a focus change, don't try to change focus. |
+ return true; |
+ } |
+ // Re-enable hot-tracking, as Activate() will disable it. |
+ acc_focused_view_->SetHotTracked(true); |
+ break; |
+ } |
+ default: |
+ // If key is not handled explicitly, pass it on to view. |
+ return acc_focused_view_->OnKeyPressed(e); |
+ } |
+ |
+ // No buttons enabled or visible. |
+ if (next_view == -1) |
+ return false; |
+ |
+ // Only send an event if focus moved. |
+ if (next_view != focused_view) { |
+ // Remove hot-tracking from old focused button. |
+ acc_focused_view_->SetHotTracked(false); |
+ |
+ // All is well, update the focused child member variable. |
+ acc_focused_view_ = GetChildViewAt(next_view); |
+ |
+ // Hot-track new focused button. |
+ acc_focused_view_->SetHotTracked(true); |
+ |
+ // Show the tooltip for the view that got the focus. |
+ if (GetWidget()->GetTooltipManager()) { |
+ GetWidget()->GetTooltipManager()-> |
+ ShowKeyboardTooltip(GetChildViewAt(next_view)); |
+ } |
+#if defined(OS_WIN) |
+ // Retrieve information to generate an MSAA focus event. |
+ gfx::NativeView wnd = GetWidget()->GetNativeView(); |
+ int view_id = acc_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 |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool ToolbarView::OnKeyReleased(const views::KeyEvent& e) { |
+ // Paranoia check, button should be initialized upon toolbar gaining focus. |
+ if (!acc_focused_view_) |
+ return false; |
+ |
+ // Have keys be handled by the views themselves. |
+ return acc_focused_view_->OnKeyReleased(e); |
+} |
+ |
+bool ToolbarView::SkipDefaultKeyEventProcessing(const views::KeyEvent& e) { |
+ if (acc_focused_view_ && e.GetKeyCode() == base::VKEY_ESCAPE) { |
+ // 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 location bar. |
+ // |acc_focused_view_| doesn't need to be resetted here since it will be |
+ // dealt within the WillLoseFocus method. |
+ 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 { |
+ location_bar_->RequestFocus(); |
+ } |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool ToolbarView::GetAccessibleName(std::wstring* name) { |
+ if (!accessible_name_.empty()) { |
+ (*name).assign(accessible_name_); |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool ToolbarView::GetAccessibleRole(AccessibilityTypes::Role* role) { |
+ DCHECK(role); |
+ |
+ *role = AccessibilityTypes::ROLE_TOOLBAR; |
+ return true; |
+} |
+ |
+void ToolbarView::SetAccessibleName(const std::wstring& name) { |
+ accessible_name_.assign(name); |
+} |
+ |
//////////////////////////////////////////////////////////////////////////////// |
// ToolbarView, views::DragController implementation: |