• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/ui/views/accessibility_event_router_views.h"
6 
7 #include "base/basictypes.h"
8 #include "base/callback.h"
9 #include "base/message_loop.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/extensions/extension_accessibility_api.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/profiles/profile_manager.h"
15 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
16 #include "content/common/notification_type.h"
17 #include "ui/base/models/combobox_model.h"
18 #include "ui/base/accessibility/accessible_view_state.h"
19 #include "views/controls/button/checkbox.h"
20 #include "views/controls/button/custom_button.h"
21 #include "views/controls/button/menu_button.h"
22 #include "views/controls/button/native_button.h"
23 #include "views/controls/combobox/combobox.h"
24 #include "views/controls/link.h"
25 #include "views/controls/menu/menu_item_view.h"
26 #include "views/controls/menu/submenu_view.h"
27 #include "views/controls/textfield/textfield.h"
28 #include "views/view.h"
29 #include "views/widget/native_widget.h"
30 #include "views/widget/widget.h"
31 #include "views/window/window.h"
32 
33 using views::FocusManager;
34 
AccessibilityEventRouterViews()35 AccessibilityEventRouterViews::AccessibilityEventRouterViews()
36     : most_recent_profile_(NULL),
37       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
38 }
39 
~AccessibilityEventRouterViews()40 AccessibilityEventRouterViews::~AccessibilityEventRouterViews() {
41 }
42 
43 // static
GetInstance()44 AccessibilityEventRouterViews* AccessibilityEventRouterViews::GetInstance() {
45   return Singleton<AccessibilityEventRouterViews>::get();
46 }
47 
HandleAccessibilityEvent(views::View * view,ui::AccessibilityTypes::Event event_type)48 void AccessibilityEventRouterViews::HandleAccessibilityEvent(
49     views::View* view, ui::AccessibilityTypes::Event event_type) {
50   if (!ExtensionAccessibilityEventRouter::GetInstance()->
51       IsAccessibilityEnabled()) {
52     return;
53   }
54 
55   switch (event_type) {
56     case ui::AccessibilityTypes::EVENT_FOCUS:
57       DispatchAccessibilityNotification(
58           view, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED);
59       break;
60     case ui::AccessibilityTypes::EVENT_MENUSTART:
61     case ui::AccessibilityTypes::EVENT_MENUPOPUPSTART:
62       DispatchAccessibilityNotification(
63           view, NotificationType::ACCESSIBILITY_MENU_OPENED);
64       break;
65     case ui::AccessibilityTypes::EVENT_MENUEND:
66     case ui::AccessibilityTypes::EVENT_MENUPOPUPEND:
67       DispatchAccessibilityNotification(
68           view, NotificationType::ACCESSIBILITY_MENU_CLOSED);
69       break;
70     case ui::AccessibilityTypes::EVENT_TEXT_CHANGED:
71     case ui::AccessibilityTypes::EVENT_SELECTION_CHANGED:
72       DispatchAccessibilityNotification(
73           view, NotificationType::ACCESSIBILITY_TEXT_CHANGED);
74       break;
75     case ui::AccessibilityTypes::EVENT_VALUE_CHANGED:
76       DispatchAccessibilityNotification(
77           view, NotificationType::ACCESSIBILITY_CONTROL_ACTION);
78       break;
79     case ui::AccessibilityTypes::EVENT_ALERT:
80     case ui::AccessibilityTypes::EVENT_NAME_CHANGED:
81       // TODO(dmazzoni): re-evaluate this list later and see
82       // if supporting any of these would be useful feature requests or
83       // they'd just be superfluous.
84       NOTIMPLEMENTED();
85       break;
86   }
87 }
88 
HandleMenuItemFocused(const std::wstring & menu_name,const std::wstring & menu_item_name,int item_index,int item_count,bool has_submenu)89 void AccessibilityEventRouterViews::HandleMenuItemFocused(
90     const std::wstring& menu_name,
91     const std::wstring& menu_item_name,
92     int item_index,
93     int item_count,
94     bool has_submenu) {
95   if (!ExtensionAccessibilityEventRouter::GetInstance()->
96       IsAccessibilityEnabled()) {
97     return;
98   }
99 
100   if (!most_recent_profile_)
101     return;
102 
103   AccessibilityMenuItemInfo info(
104       most_recent_profile_,
105       WideToUTF8(menu_item_name),
106       has_submenu,
107       item_index,
108       item_count);
109   SendAccessibilityNotification(
110       NotificationType::ACCESSIBILITY_CONTROL_FOCUSED, &info);
111 }
112 
113 //
114 // Private methods
115 //
116 
GetViewName(views::View * view)117 std::string AccessibilityEventRouterViews::GetViewName(views::View* view) {
118   ui::AccessibleViewState state;
119   view->GetAccessibleState(&state);
120   return UTF16ToUTF8(state.name);
121 }
122 
DispatchAccessibilityNotification(views::View * view,NotificationType type)123 void AccessibilityEventRouterViews::DispatchAccessibilityNotification(
124     views::View* view, NotificationType type) {
125   // Get the profile associated with this view. If it's not found, use
126   // the most recent profile where accessibility events were sent, or
127   // the default profile.
128   Profile* profile = NULL;
129   views::Window* window = view->GetWindow();
130   if (window) {
131     profile = reinterpret_cast<Profile*>(
132         window->AsWidget()->native_widget()->GetNativeWindowProperty(
133             Profile::kProfileKey));
134   }
135   if (!profile)
136     profile = most_recent_profile_;
137   if (!profile)
138     profile = g_browser_process->profile_manager()->GetDefaultProfile();
139   if (!profile) {
140     NOTREACHED();
141     return;
142   }
143 
144   most_recent_profile_ = profile;
145   std::string class_name = view->GetClassName();
146 
147   if (class_name == views::Checkbox::kViewClassName) {
148     SendCheckboxNotification(view, type, profile);
149   } else if (class_name == views::MenuButton::kViewClassName ||
150       type == NotificationType::ACCESSIBILITY_MENU_OPENED ||
151       type == NotificationType::ACCESSIBILITY_MENU_CLOSED) {
152     SendMenuNotification(view, type, profile);
153   } else if (IsMenuEvent(view, type)) {
154     SendMenuItemNotification(view, type, profile);
155   } else if (class_name == views::CustomButton::kViewClassName ||
156              class_name == views::NativeButton::kViewClassName ||
157              class_name == views::TextButton::kViewClassName) {
158     SendButtonNotification(view, type, profile);
159   } else if (class_name == views::Link::kViewClassName) {
160     SendLinkNotification(view, type, profile);
161   } else if (class_name == LocationBarView::kViewClassName) {
162     SendLocationBarNotification(view, type, profile);
163   } else if (class_name == views::Textfield::kViewClassName) {
164     SendTextfieldNotification(view, type, profile);
165   } else if (class_name == views::Combobox::kViewClassName) {
166     SendComboboxNotification(view, type, profile);
167   }
168 }
169 
SendButtonNotification(views::View * view,NotificationType type,Profile * profile)170 void AccessibilityEventRouterViews::SendButtonNotification(
171     views::View* view, NotificationType type, Profile* profile) {
172   AccessibilityButtonInfo info(profile, GetViewName(view));
173   SendAccessibilityNotification(type, &info);
174 }
175 
SendLinkNotification(views::View * view,NotificationType type,Profile * profile)176 void AccessibilityEventRouterViews::SendLinkNotification(
177     views::View* view, NotificationType type, Profile* profile) {
178   AccessibilityLinkInfo info(profile, GetViewName(view));
179   SendAccessibilityNotification(type, &info);
180 }
181 
SendMenuNotification(views::View * view,NotificationType type,Profile * profile)182 void AccessibilityEventRouterViews::SendMenuNotification(
183     views::View* view, NotificationType type, Profile* profile) {
184   AccessibilityMenuInfo info(profile, GetViewName(view));
185   SendAccessibilityNotification(type, &info);
186 }
187 
SendMenuItemNotification(views::View * view,NotificationType type,Profile * profile)188 void AccessibilityEventRouterViews::SendMenuItemNotification(
189     views::View* view, NotificationType type, Profile* profile) {
190   std::string name = GetViewName(view);
191 
192   bool has_submenu = false;
193   int index = -1;
194   int count = -1;
195 
196   if (view->GetClassName() == views::MenuItemView::kViewClassName)
197     has_submenu = static_cast<views::MenuItemView*>(view)->HasSubmenu();
198 
199   views::View* parent_menu = view->parent();
200   while (parent_menu != NULL && parent_menu->GetClassName() !=
201          views::SubmenuView::kViewClassName) {
202     parent_menu = parent_menu->parent();
203   }
204   if (parent_menu) {
205     count = 0;
206     RecursiveGetMenuItemIndexAndCount(parent_menu, view, &index, &count);
207   }
208 
209   AccessibilityMenuItemInfo info(profile, name, has_submenu, index, count);
210   SendAccessibilityNotification(type, &info);
211 }
212 
RecursiveGetMenuItemIndexAndCount(views::View * menu,views::View * item,int * index,int * count)213 void AccessibilityEventRouterViews::RecursiveGetMenuItemIndexAndCount(
214     views::View* menu, views::View* item, int* index, int* count) {
215   for (int i = 0; i < menu->child_count(); ++i) {
216     views::View* child = menu->GetChildViewAt(i);
217     int previous_count = *count;
218     RecursiveGetMenuItemIndexAndCount(child, item, index, count);
219     if (child->GetClassName() == views::MenuItemView::kViewClassName &&
220         *count == previous_count) {
221       if (item == child)
222         *index = *count;
223       (*count)++;
224     } else if (child->GetClassName() == views::TextButton::kViewClassName) {
225       if (item == child)
226         *index = *count;
227       (*count)++;
228     }
229   }
230 }
231 
IsMenuEvent(views::View * view,NotificationType type)232 bool AccessibilityEventRouterViews::IsMenuEvent(
233     views::View* view, NotificationType type) {
234   if (type == NotificationType::ACCESSIBILITY_MENU_OPENED ||
235       type == NotificationType::ACCESSIBILITY_MENU_CLOSED)
236     return true;
237 
238   while (view) {
239     ui::AccessibleViewState state;
240     view->GetAccessibleState(&state);
241     ui::AccessibilityTypes::Role role = state.role;
242     if (role == ui::AccessibilityTypes::ROLE_MENUITEM ||
243         role == ui::AccessibilityTypes::ROLE_MENUPOPUP) {
244       return true;
245     }
246     view = view->parent();
247   }
248 
249   return false;
250 }
251 
SendLocationBarNotification(views::View * view,NotificationType type,Profile * profile)252 void AccessibilityEventRouterViews::SendLocationBarNotification(
253     views::View* view, NotificationType type, Profile* profile) {
254   ui::AccessibleViewState state;
255   view->GetAccessibleState(&state);
256   std::string name = UTF16ToUTF8(state.name);
257   AccessibilityTextBoxInfo info(profile, name, false);
258   std::string value = UTF16ToUTF8(state.value);
259   info.SetValue(value, state.selection_start, state.selection_end);
260   SendAccessibilityNotification(type, &info);
261 }
262 
SendTextfieldNotification(views::View * view,NotificationType type,Profile * profile)263 void AccessibilityEventRouterViews::SendTextfieldNotification(
264     views::View* view, NotificationType type, Profile* profile) {
265   ui::AccessibleViewState state;
266   view->GetAccessibleState(&state);
267   std::string name = UTF16ToUTF8(state.name);
268   views::Textfield* textfield = static_cast<views::Textfield*>(view);
269   bool password = textfield->IsPassword();
270   AccessibilityTextBoxInfo info(profile, name, password);
271   std::string value = UTF16ToUTF8(state.value);
272   info.SetValue(value, state.selection_start, state.selection_end);
273   SendAccessibilityNotification(type, &info);
274 }
275 
SendComboboxNotification(views::View * view,NotificationType type,Profile * profile)276 void AccessibilityEventRouterViews::SendComboboxNotification(
277     views::View* view, NotificationType type, Profile* profile) {
278   ui::AccessibleViewState state;
279   view->GetAccessibleState(&state);
280   std::string name = UTF16ToUTF8(state.name);
281   std::string value = UTF16ToUTF8(state.value);
282   AccessibilityComboBoxInfo info(
283       profile, name, value, state.index, state.count);
284   SendAccessibilityNotification(type, &info);
285 }
286 
SendCheckboxNotification(views::View * view,NotificationType type,Profile * profile)287 void AccessibilityEventRouterViews::SendCheckboxNotification(
288     views::View* view, NotificationType type, Profile* profile) {
289   ui::AccessibleViewState state;
290   view->GetAccessibleState(&state);
291   std::string name = UTF16ToUTF8(state.name);
292   std::string value = UTF16ToUTF8(state.value);
293   AccessibilityCheckboxInfo info(
294       profile, name, state.state == ui::AccessibilityTypes::STATE_CHECKED);
295   SendAccessibilityNotification(type, &info);
296 }
297 
298