diff options
author | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-06 07:07:18 +0000 |
---|---|---|
committer | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-06 07:07:18 +0000 |
commit | 6a054ffaae82f5ac8d6b876d7c85f0d87d892e42 (patch) | |
tree | 86b4d5a0c5386f3d50742af099b5009ad1dad14d /chrome/browser/views | |
parent | 6ca800b9b2618e6ae4b92e189a9b1ae73746e49f (diff) | |
download | chromium_src-6a054ffaae82f5ac8d6b876d7c85f0d87d892e42.zip chromium_src-6a054ffaae82f5ac8d6b876d7c85f0d87d892e42.tar.gz chromium_src-6a054ffaae82f5ac8d6b876d7c85f0d87d892e42.tar.bz2 |
Improvements to accessibility extension api support for "views":
1. Handles the new wrench menu.
2. Uses NotifyAccessibilityEvent to find out when focus changes, rather
than installing focus change listeners that need to be cleaned up.
BUG=none
TEST=Updated AccessibilityEventRouterViewsTest.TestFocusNotification
Review URL: http://codereview.chromium.org/3056045
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@55196 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/views')
5 files changed, 193 insertions, 60 deletions
diff --git a/chrome/browser/views/accessibility_event_router_views.cc b/chrome/browser/views/accessibility_event_router_views.cc index 32dfaeb..670e2f0 100644 --- a/chrome/browser/views/accessibility_event_router_views.cc +++ b/chrome/browser/views/accessibility_event_router_views.cc @@ -11,10 +11,13 @@ #include "chrome/browser/extensions/extension_accessibility_api.h" #include "chrome/browser/profile.h" #include "chrome/common/notification_type.h" +#include "views/accessibility/accessibility_types.h" #include "views/controls/button/image_button.h" #include "views/controls/button/menu_button.h" #include "views/controls/button/native_button.h" #include "views/controls/link.h" +#include "views/controls/menu/menu_item_view.h" +#include "views/controls/menu/submenu_view.h" using views::FocusManager; using views::View; @@ -36,18 +39,6 @@ bool AccessibilityEventRouterViews::AddViewTree(View* view, Profile* profile) { return false; view_tree_profile_map_[view] = profile; - FocusManager* focus_manager = view->GetFocusManager(); - - // Add this object as a listener for this focus manager, but use a ref - // count to ensure we only call AddFocusChangeListener on any given - // focus manager once. Note that hash_map<FocusManager*, int>::operator[] - // will initialize the ref count to zero if it's not already in the map. - if (focus_manager_ref_count_[focus_manager] == 0) { - focus_manager->AddFocusChangeListener(this); - } - focus_manager_ref_count_[focus_manager]++; - - view_info_map_[view].focus_manager = focus_manager; return true; } @@ -55,15 +46,6 @@ void AccessibilityEventRouterViews::RemoveViewTree(View* view) { DCHECK(view_tree_profile_map_.find(view) != view_tree_profile_map_.end()); view_tree_profile_map_.erase(view); - - // Decrement the ref count of the focus manager, and remove this object - // as a listener if the count reaches zero. - FocusManager* focus_manager = view_info_map_[view].focus_manager; - DCHECK(focus_manager); - focus_manager_ref_count_[focus_manager]--; - if (focus_manager_ref_count_[focus_manager] == 0) { - focus_manager->RemoveFocusChangeListener(this); - } } void AccessibilityEventRouterViews::IgnoreView(View* view) { @@ -79,15 +61,23 @@ void AccessibilityEventRouterViews::RemoveView(View* view) { view_info_map_.erase(view); } -// -// views::FocusChangeListener -// - -void AccessibilityEventRouterViews::FocusWillChange( - View* focused_before, View* focused_now) { - if (focused_now) { - DispatchAccessibilityNotification( - focused_now, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED); +void AccessibilityEventRouterViews::HandleAccessibilityEvent( + views::View* view, AccessibilityTypes::Event event_type) { + switch (event_type) { + case AccessibilityTypes::EVENT_FOCUS: + DispatchAccessibilityNotification( + view, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED); + break; + case AccessibilityTypes::EVENT_MENUSTART: + case AccessibilityTypes::EVENT_MENUPOPUPSTART: + DispatchAccessibilityNotification( + view, NotificationType::ACCESSIBILITY_MENU_OPENED); + break; + case AccessibilityTypes::EVENT_MENUEND: + case AccessibilityTypes::EVENT_MENUPOPUPEND: + DispatchAccessibilityNotification( + view, NotificationType::ACCESSIBILITY_MENU_CLOSED); + break; } } @@ -145,17 +135,38 @@ void AccessibilityEventRouterViews::DispatchAccessibilityNotification( Profile* profile = NULL; bool is_accessible; FindView(view, &profile, &is_accessible); + + // Special case: a menu isn't associated with any particular top-level + // window, so menu events get routed to the profile of the most recent + // event that was associated with a window, which should be the window + // that triggered opening the menu. + bool is_menu_event = IsMenuEvent(view, type); + if (is_menu_event && !profile && most_recent_profile_) { + profile = most_recent_profile_; + is_accessible = true; + } + if (!is_accessible) return; - if (view->GetClassName() == views::ImageButton::kViewClassName) { - SendButtonNotification(view, type, profile); - } else if (view->GetClassName() == views::NativeButton::kViewClassName) { + most_recent_profile_ = profile; + + AccessibilityTypes::Role role; + view->GetAccessibleRole(&role); + std::string class_name = view->GetClassName(); + + if (class_name == views::MenuButton::kViewClassName || + type == NotificationType::ACCESSIBILITY_MENU_OPENED || + type == NotificationType::ACCESSIBILITY_MENU_CLOSED) { + SendMenuNotification(view, type, profile); + } else if (is_menu_event) { + SendMenuItemNotification(view, type, profile); + } else if (class_name == views::ImageButton::kViewClassName || + class_name == views::NativeButton::kViewClassName || + class_name == views::TextButton::kViewClassName) { SendButtonNotification(view, type, profile); - } else if (view->GetClassName() == views::Link::kViewClassName) { + } else if (class_name == views::Link::kViewClassName) { SendLinkNotification(view, type, profile); - } else if (view->GetClassName() == views::MenuButton::kViewClassName) { - SendMenuNotification(view, type, profile); } } @@ -176,3 +187,66 @@ void AccessibilityEventRouterViews::SendMenuNotification( AccessibilityMenuInfo info(profile, GetViewName(view)); SendAccessibilityNotification(type, &info); } + +void AccessibilityEventRouterViews::SendMenuItemNotification( + View* view, NotificationType type, Profile* profile) { + std::string name = GetViewName(view); + + bool has_submenu = false; + int index = -1; + int count = -1; + + if (view->GetClassName() == views::MenuItemView::kViewClassName) + has_submenu = static_cast<views::MenuItemView*>(view)->HasSubmenu(); + + View* parent_menu = view->GetParent(); + while (parent_menu != NULL && parent_menu->GetClassName() != + views::SubmenuView::kViewClassName) { + parent_menu = parent_menu->GetParent(); + } + if (parent_menu) { + count = 0; + RecursiveGetMenuItemIndexAndCount(parent_menu, view, &index, &count); + } + + AccessibilityMenuItemInfo info(profile, name, has_submenu, index, count); + SendAccessibilityNotification(type, &info); +} + +void AccessibilityEventRouterViews::RecursiveGetMenuItemIndexAndCount( + views::View* menu, views::View* item, int* index, int* count) { + for (int i = 0; i < menu->GetChildViewCount(); ++i) { + views::View* child = menu->GetChildViewAt(i); + int previous_count = *count; + RecursiveGetMenuItemIndexAndCount(child, item, index, count); + if (child->GetClassName() == views::MenuItemView::kViewClassName && + *count == previous_count) { + if (item == child) + *index = *count; + (*count)++; + } else if (child->GetClassName() == views::TextButton::kViewClassName) { + if (item == child) + *index = *count; + (*count)++; + } + } +} + +bool AccessibilityEventRouterViews::IsMenuEvent( + View* view, NotificationType type) { + if (type == NotificationType::ACCESSIBILITY_MENU_OPENED || + type == NotificationType::ACCESSIBILITY_MENU_CLOSED) + return true; + + while (view) { + AccessibilityTypes::Role role; + view->GetAccessibleRole(&role); + if (role == AccessibilityTypes::ROLE_MENUITEM || + role == AccessibilityTypes::ROLE_MENUPOPUP) { + return true; + } + view = view->GetParent(); + } + + return false; +} diff --git a/chrome/browser/views/accessibility_event_router_views.h b/chrome/browser/views/accessibility_event_router_views.h index 403e011..aa2054f 100644 --- a/chrome/browser/views/accessibility_event_router_views.h +++ b/chrome/browser/views/accessibility_event_router_views.h @@ -9,6 +9,7 @@ #include <string> #include "base/basictypes.h" +#include "base/gtest_prod_util.h" #include "base/hash_tables.h" #include "base/singleton.h" #include "base/task.h" @@ -18,16 +19,10 @@ class Profile; -// Allows us to use (View*) and (FocusManager*) in a hash_map with gcc. +// Allows us to use (View*) in a hash_map with gcc. #if defined(COMPILER_GCC) namespace __gnu_cxx { template<> -struct hash<views::FocusManager*> { - size_t operator()(views::FocusManager* focus_manager) const { - return reinterpret_cast<size_t>(focus_manager); - } -}; -template<> struct hash<views::View*> { size_t operator()(views::View* view) const { return reinterpret_cast<size_t>(view); @@ -51,23 +46,18 @@ struct hash<views::View*> { // // You can use Profile::PauseAccessibilityEvents to prevent a flurry // of accessibility events when a window is being created or initialized. -class AccessibilityEventRouterViews - : public views::FocusChangeListener { +class AccessibilityEventRouterViews { public: // Internal information about a particular view to override the // information we get directly from the view. struct ViewInfo { - ViewInfo() : ignore(false), focus_manager(NULL) {} + ViewInfo() : ignore(false) {} // If nonempty, will use this name instead of the view's label. std::string name; // If true, will ignore this widget and not send accessibility events. bool ignore; - - // The focus manager that this view is part of - saved because - // GetFocusManager may not succeed while a view is being deleted. - views::FocusManager* focus_manager; }; // Get the single instance of this class. @@ -94,11 +84,18 @@ class AccessibilityEventRouterViews // Forget all information about this view. void RemoveView(views::View* view); - // Implementation of views::FocusChangeListener: - virtual void FocusWillChange( - views::View* focused_before, views::View* focused_now); + // Handle an accessibility event generated by a view. + void HandleAccessibilityEvent( + views::View* view, AccessibilityTypes::Event event_type); private: + AccessibilityEventRouterViews(); + virtual ~AccessibilityEventRouterViews(); + + friend struct DefaultSingletonTraits<AccessibilityEventRouterViews>; + FRIEND_TEST_ALL_PREFIXES(AccessibilityEventRouterViewsTest, + TestFocusNotification); + // Given a view, determine if it's part of a view tree that's mapped to // a profile and if so, if it's marked as accessible. void FindView(views::View* view, Profile** profile, bool* is_accessible); @@ -119,12 +116,19 @@ class AccessibilityEventRouterViews views::View* view, NotificationType type, Profile* profile); void SendMenuNotification( views::View* view, NotificationType type, Profile* profile); + void SendMenuItemNotification( + views::View* view, NotificationType type, Profile* profile); - private: - AccessibilityEventRouterViews(); - virtual ~AccessibilityEventRouterViews(); + // Return true if it's an event on a menu. + bool IsMenuEvent(views::View* view, NotificationType type); - friend struct DefaultSingletonTraits<AccessibilityEventRouterViews>; + // Recursively explore all menu items of |menu| and return in |count| + // the total number of items, and in |index| the 0-based index of + // |item|, if found. Initialize |count| to zero before calling this + // method. |index| will be unchanged if the item is not found, so + // initialize it to -1 to detect this case. + void RecursiveGetMenuItemIndexAndCount( + views::View* menu, views::View* item, int* index, int* count); // The set of all view tree roots; only descendants of these will generate // accessibility notifications. @@ -133,8 +137,10 @@ class AccessibilityEventRouterViews // Extra information about specific views. base::hash_map<views::View*, ViewInfo> view_info_map_; - // Count of the number of references to each focus manager. - base::hash_map<views::FocusManager*, int> focus_manager_ref_count_; + // The profile associated with the most recent window event - used to + // figure out where to route a few events that can't be directly traced + // to a window with a profile (like menu events). + Profile* most_recent_profile_; // Used to defer handling of some events until the next time // through the event loop. diff --git a/chrome/browser/views/accessibility_event_router_views_unittest.cc b/chrome/browser/views/accessibility_event_router_views_unittest.cc index 4788a40..700499d 100644 --- a/chrome/browser/views/accessibility_event_router_views_unittest.cc +++ b/chrome/browser/views/accessibility_event_router_views_unittest.cc @@ -16,6 +16,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "views/controls/button/native_button.h" #include "views/grid_layout.h" +#include "views/views_delegate.h" #include "views/widget/root_view.h" #include "views/window/window.h" @@ -27,10 +28,54 @@ #if defined(TOOLKIT_VIEWS) +class AccessibilityViewsDelegate : public views::ViewsDelegate { + public: + AccessibilityViewsDelegate() {} + virtual ~AccessibilityViewsDelegate() {} + + // Overridden from views::ViewsDelegate: + virtual Clipboard* GetClipboard() const { return NULL; } + virtual void SaveWindowPlacement(const std::wstring& window_name, + const gfx::Rect& bounds, + bool maximized) { + } + virtual bool GetSavedWindowBounds(const std::wstring& window_name, + gfx::Rect* bounds) const { + return false; + } + virtual bool GetSavedMaximizedState(const std::wstring& window_name, + bool* maximized) const { + return false; + } + virtual void NotifyAccessibilityEvent( + views::View* view, AccessibilityTypes::Event event_type) { + AccessibilityEventRouterViews::GetInstance()->HandleAccessibilityEvent( + view, event_type); + } +#if defined(OS_WIN) + virtual HICON GetDefaultWindowIcon() const { + return NULL; + } +#endif + virtual void AddRef() {} + virtual void ReleaseRef() {} + + DISALLOW_COPY_AND_ASSIGN(AccessibilityViewsDelegate); +}; + class AccessibilityEventRouterViewsTest : public testing::Test, public NotificationObserver { public: + virtual void SetUp() { + views::ViewsDelegate::views_delegate = new AccessibilityViewsDelegate(); + } + + virtual void TearDown() { + delete views::ViewsDelegate::views_delegate; + views::ViewsDelegate::views_delegate = NULL; + } + views::Widget* CreateWidget() { #if defined(OS_WIN) return new views::WidgetWin(); @@ -38,6 +83,7 @@ class AccessibilityEventRouterViewsTest return new views::WidgetGtk(views::WidgetGtk::TYPE_WINDOW); #endif } + protected: // Implement NotificationObserver::Observe and store information about a // ACCESSIBILITY_CONTROL_FOCUSED event. diff --git a/chrome/browser/views/chrome_views_delegate.cc b/chrome/browser/views/chrome_views_delegate.cc index b0f8028..de824db 100644 --- a/chrome/browser/views/chrome_views_delegate.cc +++ b/chrome/browser/views/chrome_views_delegate.cc @@ -8,6 +8,7 @@ #include "base/scoped_ptr.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/pref_service.h" +#include "chrome/browser/views/accessibility_event_router_views.h" #include "chrome/browser/window_sizer.h" #include "gfx/rect.h" @@ -77,6 +78,12 @@ bool ChromeViewsDelegate::GetSavedMaximizedState( maximized; } +void ChromeViewsDelegate::NotifyAccessibilityEvent( + views::View* view, AccessibilityTypes::Event event_type) { + AccessibilityEventRouterViews::GetInstance()->HandleAccessibilityEvent( + view, event_type); +} + #if defined(OS_WIN) HICON ChromeViewsDelegate::GetDefaultWindowIcon() const { return GetAppIcon(); diff --git a/chrome/browser/views/chrome_views_delegate.h b/chrome/browser/views/chrome_views_delegate.h index 285d6b5..2d14c9c 100644 --- a/chrome/browser/views/chrome_views_delegate.h +++ b/chrome/browser/views/chrome_views_delegate.h @@ -24,7 +24,7 @@ class ChromeViewsDelegate : public views::ViewsDelegate { virtual bool GetSavedMaximizedState(const std::wstring& window_name, bool* maximized) const; virtual void NotifyAccessibilityEvent( - views::View* view, AccessibilityTypes::Event event_type) {} + views::View* view, AccessibilityTypes::Event event_type); #if defined(OS_WIN) virtual HICON GetDefaultWindowIcon() const; #endif |