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 false; |
| 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->isAnchor()) |
| 298 *state |= static_cast<long>(1 << WebAccessibility::STATE_LINKED); |
| 299 |
| 300 if (child_obj->isHovered()) |
| 301 *state |= static_cast<long>(1 << WebAccessibility::STATE_HOTTRACKED); |
| 302 |
| 303 if (!child_obj->isEnabled()) |
| 304 *state |= static_cast<long>(1 << WebAccessibility::STATE_UNAVAILABLE); |
| 305 |
| 306 if (child_obj->isReadOnly()) |
| 307 *state |= static_cast<long>(1 << WebAccessibility::STATE_READONLY); |
| 308 |
| 309 if (child_obj->isOffScreen()) |
| 310 *state |= static_cast<long>(1 << WebAccessibility::STATE_OFFSCREEN); |
| 311 |
| 312 if (child_obj->isMultiSelect()) |
| 313 *state |= static_cast<long>(1 << WebAccessibility::STATE_MULTISELECTABLE); |
| 314 |
| 315 if (child_obj->isPasswordField()) |
| 316 *state |= static_cast<long>(1 << WebAccessibility::STATE_PROTECTED); |
| 317 |
| 318 if (child_obj->isIndeterminate()) |
| 319 *state |= static_cast<long>(1 << WebAccessibility::STATE_INDETERMINATE); |
| 320 |
| 321 if (child_obj->isChecked()) |
| 322 *state |= static_cast<long>(1 << WebAccessibility::STATE_CHECKED); |
| 323 |
| 324 if (child_obj->isPressed()) |
| 325 *state |= static_cast<long>(1 << WebAccessibility::STATE_PRESSED); |
| 326 |
| 327 if (child_obj->isFocused()) |
| 328 *state |= static_cast<long>(1 << WebAccessibility::STATE_FOCUSED); |
| 329 |
| 330 if (child_obj->isVisited()) |
| 331 *state |= static_cast<long>(1 << WebAccessibility::STATE_TRAVERSED); |
| 332 |
| 333 if (child_obj->canSetFocusAttribute()) |
| 334 *state |= static_cast<long>(1 << WebAccessibility::STATE_FOCUSABLE); |
| 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. Static function. |
| 365 static WebAccessibility::Role SupportedRole(WebCore::AccessibilityRole role) { |
| 366 switch (role) { |
| 367 case WebCore::ButtonRole: |
| 368 return WebAccessibility::ROLE_PUSHBUTTON; |
| 369 case WebCore::RadioButtonRole: |
| 370 return WebAccessibility::ROLE_RADIOBUTTON; |
| 371 case WebCore::CheckBoxRole: |
| 372 return WebAccessibility::ROLE_CHECKBUTTON; |
| 373 case WebCore::SliderRole: |
| 374 return WebAccessibility::ROLE_SLIDER; |
| 375 case WebCore::TabGroupRole: |
| 376 return WebAccessibility::ROLE_PAGETABLIST; |
| 377 case WebCore::TextFieldRole: |
| 378 case WebCore::TextAreaRole: |
| 379 case WebCore::ListMarkerRole: |
| 380 return WebAccessibility::ROLE_TEXT; |
| 381 case WebCore::StaticTextRole: |
| 382 return WebAccessibility::ROLE_STATICTEXT; |
| 383 case WebCore::OutlineRole: |
| 384 return WebAccessibility::ROLE_OUTLINE; |
| 385 case WebCore::ColumnRole: |
| 386 return WebAccessibility::ROLE_COLUMN; |
| 387 case WebCore::RowRole: |
| 388 return WebAccessibility::ROLE_ROW; |
| 389 case WebCore::GroupRole: |
| 390 return WebAccessibility::ROLE_GROUPING; |
| 391 case WebCore::ListRole: |
| 392 return WebAccessibility::ROLE_LIST; |
| 393 case WebCore::TableRole: |
| 394 return WebAccessibility::ROLE_TABLE; |
| 395 case WebCore::LinkRole: |
| 396 case WebCore::WebCoreLinkRole: |
| 397 return WebAccessibility::ROLE_LINK; |
| 398 case WebCore::ImageMapRole: |
| 399 case WebCore::ImageRole: |
| 400 return WebAccessibility::ROLE_GRAPHIC; |
| 401 default: |
| 402 // This is the default role. |
| 403 return WebAccessibility::ROLE_CLIENT; |
| 404 } |
| 405 } |
| 406 |
| 407 WebAccessibility::Role GlueAccessibilityObject::role() const { |
| 408 return SupportedRole(m_object->roleValue()); |
| 409 } |
| 410 |
| 411 bool GlueAccessibilityObject::GetAccessibilityObjectForChild(int child_id, |
| 412 AccessibilityObject*& child_obj) const { |
| 413 child_obj = 0; |
| 414 |
| 415 if (!m_object || child_id < 0) |
| 416 return false; |
| 417 |
| 418 if (child_id == 0) { |
| 419 child_obj = m_object; |
| 420 } else { |
| 421 size_t child_index = static_cast<size_t>(child_id - 1); |
| 422 |
| 423 if (child_index >= m_object->children().size()) |
| 424 return false; |
| 425 child_obj = m_object->children().at(child_index).get(); |
| 426 } |
| 427 |
| 428 if (!child_obj) |
| 429 return false; |
| 430 |
| 431 return true; |
| 432 } |
| 433 |
| 434 GlueAccessibilityObject* GlueAccessibilityObject::ToWrapper( |
| 435 AccessibilityObject* obj) { |
| 436 if (!obj) |
| 437 return NULL; |
| 438 |
| 439 GlueAccessibilityObject* result = |
| 440 static_cast<GlueAccessibilityObject*>(obj->wrapper()); |
| 441 if (!result) |
| 442 result = CreateInstance(obj); |
| 443 |
| 444 return result; |
| 445 } |
OLD | NEW |