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