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 35 AccessibilityEventRouterViews::AccessibilityEventRouterViews() 36 : most_recent_profile_(NULL), 37 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { 38 } 39 40 AccessibilityEventRouterViews::~AccessibilityEventRouterViews() { 41 } 42 43 // static 44 AccessibilityEventRouterViews* AccessibilityEventRouterViews::GetInstance() { 45 return Singleton<AccessibilityEventRouterViews>::get(); 46 } 47 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 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 117 std::string AccessibilityEventRouterViews::GetViewName(views::View* view) { 118 ui::AccessibleViewState state; 119 view->GetAccessibleState(&state); 120 return UTF16ToUTF8(state.name); 121 } 122 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 170 void AccessibilityEventRouterViews::SendButtonNotification( 171 views::View* view, NotificationType type, Profile* profile) { 172 AccessibilityButtonInfo info(profile, GetViewName(view)); 173 SendAccessibilityNotification(type, &info); 174 } 175 176 void AccessibilityEventRouterViews::SendLinkNotification( 177 views::View* view, NotificationType type, Profile* profile) { 178 AccessibilityLinkInfo info(profile, GetViewName(view)); 179 SendAccessibilityNotification(type, &info); 180 } 181 182 void AccessibilityEventRouterViews::SendMenuNotification( 183 views::View* view, NotificationType type, Profile* profile) { 184 AccessibilityMenuInfo info(profile, GetViewName(view)); 185 SendAccessibilityNotification(type, &info); 186 } 187 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 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 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 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 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 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 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