| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2009 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 "config.h" | |
| 6 | |
| 7 #include "AccessibilityObject.h" | |
| 8 #include "EventHandler.h" | |
| 9 #include "FrameView.h" | |
| 10 #include "PlatformKeyboardEvent.h" | |
| 11 | |
| 12 #include "webkit/glue/glue_accessibility_object.h" | |
| 13 | |
| 14 using WebCore::AccessibilityObject; | |
| 15 using WebCore::String; | |
| 16 using webkit_glue::WebAccessibility; | |
| 17 | |
| 18 GlueAccessibilityObject::GlueAccessibilityObject(AccessibilityObject* obj) | |
| 19 : AccessibilityObjectWrapper(obj) { | |
| 20 m_object->setWrapper(this); | |
| 21 } | |
| 22 | |
| 23 GlueAccessibilityObject* GlueAccessibilityObject::CreateInstance( | |
| 24 AccessibilityObject* obj) { | |
| 25 if (!obj) | |
| 26 return NULL; | |
| 27 | |
| 28 return new GlueAccessibilityObject(obj); | |
| 29 } | |
| 30 | |
| 31 bool GlueAccessibilityObject::DoDefaultAction(int child_id) { | |
| 32 AccessibilityObject* child_obj; | |
| 33 | |
| 34 if (!GetAccessibilityObjectForChild(child_id, child_obj) || | |
| 35 !child_obj->performDefaultAction()) { | |
| 36 return false; | |
| 37 } | |
| 38 return true; | |
| 39 } | |
| 40 | |
| 41 GlueAccessibilityObject* GlueAccessibilityObject::HitTest(long x, long y) { | |
| 42 if (!m_object) | |
| 43 return NULL; | |
| 44 | |
| 45 // x, y - coordinates are passed in as window coordinates, to maintain | |
| 46 // sandbox functionality. | |
| 47 WebCore::IntPoint point = | |
| 48 m_object->documentFrameView()->windowToContents(WebCore::IntPoint(x, y)); | |
| 49 AccessibilityObject* child_obj = m_object->doAccessibilityHitTest(point); | |
| 50 | |
| 51 if (!child_obj) { | |
| 52 // If we did not hit any child objects, test whether the point hit us, and | |
| 53 // report that. | |
| 54 if (!m_object->boundingBoxRect().contains(point)) | |
| 55 return NULL; | |
| 56 child_obj = m_object; | |
| 57 } | |
| 58 // TODO(klink): simple object child? | |
| 59 ToWrapper(child_obj)->ref(); | |
| 60 return ToWrapper(child_obj); | |
| 61 } | |
| 62 | |
| 63 bool GlueAccessibilityObject::Location(long* left, long* top, long* width, | |
| 64 long* height, int child_id) { | |
| 65 if (!left || !top || !width || !height) | |
| 66 return false; | |
| 67 | |
| 68 *left = *top = *width = *height = 0; | |
| 69 | |
| 70 AccessibilityObject* child_obj; | |
| 71 if (!GetAccessibilityObjectForChild(child_id, child_obj)) | |
| 72 return false; | |
| 73 | |
| 74 // Returning window coordinates, to be handled and converted appropriately by | |
| 75 // the client. | |
| 76 WebCore::IntRect window_rect(child_obj->documentFrameView()->contentsToWindow( | |
| 77 child_obj->boundingBoxRect())); | |
| 78 *left = window_rect.x(); | |
| 79 *top = window_rect.y(); | |
| 80 *width = window_rect.width(); | |
| 81 *height = window_rect.height(); | |
| 82 return true; | |
| 83 } | |
| 84 | |
| 85 GlueAccessibilityObject* GlueAccessibilityObject::Navigate( | |
| 86 WebAccessibility::Direction dir, int start_child_id) { | |
| 87 AccessibilityObject* child_obj = 0; | |
| 88 | |
| 89 switch (dir) { | |
| 90 case WebAccessibility::DIRECTION_DOWN: | |
| 91 case WebAccessibility::DIRECTION_UP: | |
| 92 case WebAccessibility::DIRECTION_LEFT: | |
| 93 case WebAccessibility::DIRECTION_RIGHT: | |
| 94 // These directions are not implemented, matching Mozilla and IE. | |
| 95 return NULL; | |
| 96 case WebAccessibility::DIRECTION_LASTCHILD: | |
| 97 case WebAccessibility::DIRECTION_FIRSTCHILD: | |
| 98 // MSDN states that navigating to first/last child can only be from self. | |
| 99 if (start_child_id != 0 || !m_object) | |
| 100 return NULL; | |
| 101 | |
| 102 if (dir == WebAccessibility::DIRECTION_FIRSTCHILD) { | |
| 103 child_obj = m_object->firstChild(); | |
| 104 } else { | |
| 105 child_obj = m_object->lastChild(); | |
| 106 } | |
| 107 break; | |
| 108 case WebAccessibility::DIRECTION_NEXT: | |
| 109 case WebAccessibility::DIRECTION_PREVIOUS: { | |
| 110 // Navigating to next and previous is allowed from self or any of our | |
| 111 // children. | |
| 112 if (!GetAccessibilityObjectForChild(start_child_id, child_obj)) | |
| 113 return NULL; | |
| 114 | |
| 115 if (dir == WebAccessibility::DIRECTION_NEXT) { | |
| 116 child_obj = child_obj->nextSibling(); | |
| 117 } else { | |
| 118 child_obj = child_obj->previousSibling(); | |
| 119 } | |
| 120 break; | |
| 121 } | |
| 122 default: | |
| 123 return NULL; | |
| 124 } | |
| 125 | |
| 126 if (!child_obj) | |
| 127 return NULL; | |
| 128 | |
| 129 // TODO(klink): simple object child? | |
| 130 ToWrapper(child_obj)->ref(); | |
| 131 return ToWrapper(child_obj); | |
| 132 } | |
| 133 | |
| 134 GlueAccessibilityObject* GlueAccessibilityObject::GetChild(int child_id) { | |
| 135 AccessibilityObject* child_obj; | |
| 136 if (!GetAccessibilityObjectForChild(child_id, child_obj)) | |
| 137 return NULL; | |
| 138 | |
| 139 // TODO(klink): simple object child? | |
| 140 ToWrapper(child_obj)->ref(); | |
| 141 return ToWrapper(child_obj); | |
| 142 } | |
| 143 | |
| 144 bool GlueAccessibilityObject::ChildCount(long* count) { | |
| 145 if (!m_object || !count) | |
| 146 return false; | |
| 147 | |
| 148 *count = static_cast<long>(m_object->children().size()); | |
| 149 return true; | |
| 150 } | |
| 151 | |
| 152 bool GlueAccessibilityObject::DefaultAction(int child_id, String* action) { | |
| 153 if (!action) | |
| 154 return false; | |
| 155 | |
| 156 AccessibilityObject* child_obj; | |
| 157 if (!GetAccessibilityObjectForChild(child_id, child_obj)) | |
| 158 return false; | |
| 159 | |
| 160 *action = child_obj->actionVerb(); | |
| 161 return !action->isEmpty(); | |
| 162 } | |
| 163 | |
| 164 bool GlueAccessibilityObject::Description(int child_id, String* description) { | |
| 165 if (!description) | |
| 166 return false; | |
| 167 | |
| 168 AccessibilityObject* child_obj; | |
| 169 if (!GetAccessibilityObjectForChild(child_id, child_obj)) | |
| 170 return false; | |
| 171 | |
| 172 // TODO(klink): Description, for SELECT subitems, should be a string | |
| 173 // describing the position of the item in its group and of the group in the | |
| 174 // list (see Firefox). | |
| 175 *description = ToWrapper(child_obj)->description(); | |
| 176 return !description->isEmpty(); | |
| 177 } | |
| 178 | |
| 179 GlueAccessibilityObject* GlueAccessibilityObject::GetFocusedChild() { | |
| 180 if (!m_object) | |
| 181 return NULL; | |
| 182 | |
| 183 AccessibilityObject* focused_obj = m_object->focusedUIElement(); | |
| 184 if (!focused_obj) | |
| 185 return NULL; | |
| 186 | |
| 187 // Only return the focused child if it's us or a child of us. | |
| 188 if (focused_obj == m_object || focused_obj->parentObject() == m_object) { | |
| 189 ToWrapper(focused_obj)->ref(); | |
| 190 return ToWrapper(focused_obj); | |
| 191 } | |
| 192 return NULL; | |
| 193 } | |
| 194 | |
| 195 bool GlueAccessibilityObject::HelpText(int child_id, String* help) { | |
| 196 if (!help) | |
| 197 return false; | |
| 198 | |
| 199 AccessibilityObject* child_obj; | |
| 200 if (!GetAccessibilityObjectForChild(child_id, child_obj)) | |
| 201 return false; | |
| 202 | |
| 203 *help = child_obj->helpText(); | |
| 204 return !help->isEmpty(); | |
| 205 } | |
| 206 | |
| 207 bool GlueAccessibilityObject::KeyboardShortcut(int child_id, String* shortcut) { | |
| 208 if (!shortcut) | |
| 209 return false; | |
| 210 | |
| 211 AccessibilityObject* child_obj; | |
| 212 if (!GetAccessibilityObjectForChild(child_id, child_obj)) | |
| 213 return false; | |
| 214 | |
| 215 String access_key = child_obj->accessKey(); | |
| 216 if (access_key.isNull()) | |
| 217 return false; | |
| 218 | |
| 219 static String access_key_modifiers; | |
| 220 if (access_key_modifiers.isNull()) { | |
| 221 unsigned modifiers = WebCore::EventHandler::accessKeyModifiers(); | |
| 222 // Follow the same order as Mozilla MSAA implementation: | |
| 223 // Ctrl+Alt+Shift+Meta+key. MSDN states that keyboard shortcut strings | |
| 224 // should not be localized and defines the separator as "+". | |
| 225 if (modifiers & WebCore::PlatformKeyboardEvent::CtrlKey) | |
| 226 access_key_modifiers += "Ctrl+"; | |
| 227 if (modifiers & WebCore::PlatformKeyboardEvent::AltKey) | |
| 228 access_key_modifiers += "Alt+"; | |
| 229 if (modifiers & WebCore::PlatformKeyboardEvent::ShiftKey) | |
| 230 access_key_modifiers += "Shift+"; | |
| 231 if (modifiers & WebCore::PlatformKeyboardEvent::MetaKey) | |
| 232 access_key_modifiers += "Win+"; | |
| 233 } | |
| 234 *shortcut = access_key_modifiers + access_key; | |
| 235 return !shortcut->isEmpty(); | |
| 236 } | |
| 237 | |
| 238 bool GlueAccessibilityObject::Name(int child_id, String* name) { | |
| 239 if (!name) | |
| 240 return false; | |
| 241 | |
| 242 AccessibilityObject* child_obj; | |
| 243 if (!GetAccessibilityObjectForChild(child_id, child_obj)) | |
| 244 return false; | |
| 245 | |
| 246 *name = ToWrapper(child_obj)->name(); | |
| 247 return !name->isEmpty(); | |
| 248 } | |
| 249 | |
| 250 GlueAccessibilityObject* GlueAccessibilityObject::GetParent() { | |
| 251 if (!m_object) | |
| 252 return NULL; | |
| 253 | |
| 254 AccessibilityObject* parent_obj = m_object->parentObject(); | |
| 255 | |
| 256 if (parent_obj) { | |
| 257 ToWrapper(parent_obj)->ref(); | |
| 258 return ToWrapper(parent_obj); | |
| 259 } | |
| 260 // No valid parent, or parent is the containing window. | |
| 261 return NULL; | |
| 262 } | |
| 263 | |
| 264 bool GlueAccessibilityObject::Role(int child_id, long* role) { | |
| 265 if (!role) | |
| 266 return false; | |
| 267 | |
| 268 AccessibilityObject* child_obj; | |
| 269 if (!GetAccessibilityObjectForChild(child_id, child_obj)) | |
| 270 return false; | |
| 271 | |
| 272 *role = ToWrapper(child_obj)->role(); | |
| 273 return true; | |
| 274 } | |
| 275 | |
| 276 bool GlueAccessibilityObject::Value(int child_id, String* value) { | |
| 277 if (!value) | |
| 278 return false; | |
| 279 | |
| 280 AccessibilityObject* child_obj; | |
| 281 if (!GetAccessibilityObjectForChild(child_id, child_obj)) | |
| 282 return false; | |
| 283 | |
| 284 *value = ToWrapper(child_obj)->value(); | |
| 285 return !value->isEmpty(); | |
| 286 } | |
| 287 | |
| 288 bool GlueAccessibilityObject::State(int child_id, long* state) { | |
| 289 if (!state) | |
| 290 return false; | |
| 291 | |
| 292 *state = 0; | |
| 293 AccessibilityObject* child_obj; | |
| 294 if (!GetAccessibilityObjectForChild(child_id, child_obj)) | |
| 295 return false; | |
| 296 | |
| 297 if (child_obj->isChecked()) | |
| 298 *state |= static_cast<long>(1 << WebAccessibility::STATE_CHECKED); | |
| 299 | |
| 300 if (child_obj->canSetFocusAttribute()) | |
| 301 *state |= static_cast<long>(1 << WebAccessibility::STATE_FOCUSABLE); | |
| 302 | |
| 303 if (child_obj->isFocused()) | |
| 304 *state |= static_cast<long>(1 << WebAccessibility::STATE_FOCUSED); | |
| 305 | |
| 306 if (child_obj->isHovered()) | |
| 307 *state |= static_cast<long>(1 << WebAccessibility::STATE_HOTTRACKED); | |
| 308 | |
| 309 if (child_obj->isIndeterminate()) | |
| 310 *state |= static_cast<long>(1 << WebAccessibility::STATE_INDETERMINATE); | |
| 311 | |
| 312 if (child_obj->isAnchor()) | |
| 313 *state |= static_cast<long>(1 << WebAccessibility::STATE_LINKED); | |
| 314 | |
| 315 if (child_obj->isMultiSelect()) | |
| 316 *state |= static_cast<long>(1 << WebAccessibility::STATE_MULTISELECTABLE); | |
| 317 | |
| 318 if (child_obj->isOffScreen()) | |
| 319 *state |= static_cast<long>(1 << WebAccessibility::STATE_OFFSCREEN); | |
| 320 | |
| 321 if (child_obj->isPressed()) | |
| 322 *state |= static_cast<long>(1 << WebAccessibility::STATE_PRESSED); | |
| 323 | |
| 324 if (child_obj->isPasswordField()) | |
| 325 *state |= static_cast<long>(1 << WebAccessibility::STATE_PROTECTED); | |
| 326 | |
| 327 if (child_obj->isReadOnly()) | |
| 328 *state |= static_cast<long>(1 << WebAccessibility::STATE_READONLY); | |
| 329 | |
| 330 if (child_obj->isVisited()) | |
| 331 *state |= static_cast<long>(1 << WebAccessibility::STATE_TRAVERSED); | |
| 332 | |
| 333 if (!child_obj->isEnabled()) | |
| 334 *state |= static_cast<long>(1 << WebAccessibility::STATE_UNAVAILABLE); | |
| 335 | |
| 336 // TODO(klink): Add selected and selectable states. | |
| 337 | |
| 338 return true; | |
| 339 } | |
| 340 | |
| 341 // Helper functions | |
| 342 String GlueAccessibilityObject::name() const { | |
| 343 return m_object->title(); | |
| 344 } | |
| 345 | |
| 346 String GlueAccessibilityObject::value() const { | |
| 347 return m_object->stringValue(); | |
| 348 } | |
| 349 | |
| 350 String GlueAccessibilityObject::description() const { | |
| 351 String desc = m_object->accessibilityDescription(); | |
| 352 if (desc.isNull()) | |
| 353 return desc; | |
| 354 | |
| 355 // From the Mozilla MSAA implementation: | |
| 356 // "Signal to screen readers that this description is speakable and is not | |
| 357 // a formatted positional information description. Don't localize the | |
| 358 // 'Description: ' part of this string, it will be parsed out by assistive | |
| 359 // technologies." | |
| 360 return "Description: " + desc; | |
| 361 } | |
| 362 | |
| 363 // Provides a conversion between the WebCore::AccessibilityRole and a | |
| 364 // role supported on the Browser side. Listed alphabetically by the | |
| 365 // WebAccessibility role (except for default role). Static function. | |
| 366 static WebAccessibility::Role SupportedRole(WebCore::AccessibilityRole role) { | |
| 367 switch (role) { | |
| 368 case WebCore::LandmarkApplicationRole: | |
| 369 return WebAccessibility::ROLE_APPLICATION; | |
| 370 case WebCore::CellRole: | |
| 371 return WebAccessibility::ROLE_CELL; | |
| 372 case WebCore::CheckBoxRole: | |
| 373 return WebAccessibility::ROLE_CHECKBUTTON; | |
| 374 case WebCore::ColumnRole: | |
| 375 return WebAccessibility::ROLE_COLUMN; | |
| 376 case WebCore::ColumnHeaderRole: | |
| 377 return WebAccessibility::ROLE_COLUMNHEADER; | |
| 378 case WebCore::DocumentArticleRole: | |
| 379 case WebCore::WebAreaRole: | |
| 380 return WebAccessibility::ROLE_DOCUMENT; | |
| 381 case WebCore::ImageMapRole: | |
| 382 case WebCore::ImageRole: | |
| 383 return WebAccessibility::ROLE_GRAPHIC; | |
| 384 case WebCore::DocumentRegionRole: | |
| 385 case WebCore::RadioGroupRole: | |
| 386 case WebCore::GroupRole: | |
| 387 return WebAccessibility::ROLE_GROUPING; | |
| 388 case WebCore::LinkRole: | |
| 389 case WebCore::WebCoreLinkRole: | |
| 390 return WebAccessibility::ROLE_LINK; | |
| 391 case WebCore::ListRole: | |
| 392 return WebAccessibility::ROLE_LIST; | |
| 393 case WebCore::ListBoxRole: | |
| 394 return WebAccessibility::ROLE_LISTBOX; | |
| 395 case WebCore::ListBoxOptionRole: | |
| 396 return WebAccessibility::ROLE_LISTITEM; | |
| 397 case WebCore::MenuBarRole: | |
| 398 return WebAccessibility::ROLE_MENUBAR; | |
| 399 case WebCore::MenuButtonRole: | |
| 400 case WebCore::MenuItemRole: | |
| 401 return WebAccessibility::ROLE_MENUITEM; | |
| 402 case WebCore::MenuRole: | |
| 403 return WebAccessibility::ROLE_MENUPOPUP; | |
| 404 case WebCore::OutlineRole: | |
| 405 return WebAccessibility::ROLE_OUTLINE; | |
| 406 case WebCore::TabGroupRole: | |
| 407 return WebAccessibility::ROLE_PAGETABLIST; | |
| 408 case WebCore::ProgressIndicatorRole: | |
| 409 return WebAccessibility::ROLE_PROGRESSBAR; | |
| 410 case WebCore::ButtonRole: | |
| 411 return WebAccessibility::ROLE_PUSHBUTTON; | |
| 412 case WebCore::RadioButtonRole: | |
| 413 return WebAccessibility::ROLE_RADIOBUTTON; | |
| 414 case WebCore::RowRole: | |
| 415 return WebAccessibility::ROLE_ROW; | |
| 416 case WebCore::RowHeaderRole: | |
| 417 return WebAccessibility::ROLE_ROWHEADER; | |
| 418 case WebCore::SplitterRole: | |
| 419 return WebAccessibility::ROLE_SEPARATOR; | |
| 420 case WebCore::SliderRole: | |
| 421 return WebAccessibility::ROLE_SLIDER; | |
| 422 case WebCore::StaticTextRole: | |
| 423 return WebAccessibility::ROLE_STATICTEXT; | |
| 424 case WebCore::ApplicationStatusRole: | |
| 425 return WebAccessibility::ROLE_STATUSBAR; | |
| 426 case WebCore::TableRole: | |
| 427 return WebAccessibility::ROLE_TABLE; | |
| 428 case WebCore::ListMarkerRole: | |
| 429 case WebCore::TextFieldRole: | |
| 430 case WebCore::TextAreaRole: | |
| 431 return WebAccessibility::ROLE_TEXT; | |
| 432 case WebCore::ToolbarRole: | |
| 433 return WebAccessibility::ROLE_TOOLBAR; | |
| 434 case WebCore::UserInterfaceTooltipRole: | |
| 435 return WebAccessibility::ROLE_TOOLTIP; | |
| 436 case WebCore::DocumentRole: | |
| 437 case WebCore::UnknownRole: | |
| 438 default: | |
| 439 // This is the default role. | |
| 440 return WebAccessibility::ROLE_CLIENT; | |
| 441 } | |
| 442 } | |
| 443 | |
| 444 WebAccessibility::Role GlueAccessibilityObject::role() const { | |
| 445 return SupportedRole(m_object->roleValue()); | |
| 446 } | |
| 447 | |
| 448 bool GlueAccessibilityObject::GetAccessibilityObjectForChild(int child_id, | |
| 449 AccessibilityObject*& child_obj) const { | |
| 450 child_obj = 0; | |
| 451 | |
| 452 if (!m_object || child_id < 0) | |
| 453 return false; | |
| 454 | |
| 455 if (child_id == 0) { | |
| 456 child_obj = m_object; | |
| 457 } else { | |
| 458 size_t child_index = static_cast<size_t>(child_id - 1); | |
| 459 | |
| 460 if (child_index >= m_object->children().size()) | |
| 461 return false; | |
| 462 child_obj = m_object->children().at(child_index).get(); | |
| 463 } | |
| 464 | |
| 465 if (!child_obj) | |
| 466 return false; | |
| 467 | |
| 468 return true; | |
| 469 } | |
| 470 | |
| 471 GlueAccessibilityObject* GlueAccessibilityObject::ToWrapper( | |
| 472 AccessibilityObject* obj) { | |
| 473 if (!obj) | |
| 474 return NULL; | |
| 475 | |
| 476 GlueAccessibilityObject* result = | |
| 477 static_cast<GlueAccessibilityObject*>(obj->wrapper()); | |
| 478 if (!result) | |
| 479 result = CreateInstance(obj); | |
| 480 | |
| 481 return result; | |
| 482 } | |
| OLD | NEW |