1 // Copyright (c) 2012 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 "ui/views/accessibility/native_view_accessibility_win.h" 6 7 #include <UIAutomationClient.h> 8 #include <oleacc.h> 9 10 #include <set> 11 #include <vector> 12 13 #include "base/memory/singleton.h" 14 #include "base/win/windows_version.h" 15 #include "third_party/iaccessible2/ia2_api_all.h" 16 #include "ui/base/accessibility/accessible_text_utils.h" 17 #include "ui/base/accessibility/accessible_view_state.h" 18 #include "ui/base/win/accessibility_ids_win.h" 19 #include "ui/base/win/accessibility_misc_utils.h" 20 #include "ui/base/win/atl_module.h" 21 #include "ui/views/controls/button/custom_button.h" 22 #include "ui/views/focus/focus_manager.h" 23 #include "ui/views/focus/view_storage.h" 24 #include "ui/views/win/hwnd_util.h" 25 26 using ui::AccessibilityTypes; 27 28 namespace views { 29 namespace { 30 31 class AccessibleWebViewRegistry { 32 public: 33 static AccessibleWebViewRegistry* GetInstance(); 34 35 void RegisterWebView(AccessibleWebView* web_view); 36 37 void UnregisterWebView(AccessibleWebView* web_view); 38 39 // Given the view that received the request for the accessible 40 // id in |top_view|, and the child id requested, return the native 41 // accessible object with that child id from one of the WebViews in 42 // |top_view|'s view hierarchy, if any. 43 IAccessible* GetAccessibleFromWebView(View* top_view, long child_id); 44 45 private: 46 friend struct DefaultSingletonTraits<AccessibleWebViewRegistry>; 47 AccessibleWebViewRegistry(); 48 ~AccessibleWebViewRegistry() {} 49 50 // Set of all web views. We check whether each one is contained in a 51 // top view dynamically rather than keeping track of a map. 52 std::set<AccessibleWebView*> web_views_; 53 54 // The most recent top view used in a call to GetAccessibleFromWebView. 55 View* last_top_view_; 56 57 // The most recent web view where an accessible object was found, 58 // corresponding to |last_top_view_|. 59 AccessibleWebView* last_web_view_; 60 61 DISALLOW_COPY_AND_ASSIGN(AccessibleWebViewRegistry); 62 }; 63 64 AccessibleWebViewRegistry::AccessibleWebViewRegistry() 65 : last_top_view_(NULL), 66 last_web_view_(NULL) { 67 } 68 69 AccessibleWebViewRegistry* AccessibleWebViewRegistry::GetInstance() { 70 return Singleton<AccessibleWebViewRegistry>::get(); 71 } 72 73 void AccessibleWebViewRegistry::RegisterWebView(AccessibleWebView* web_view) { 74 DCHECK(web_views_.find(web_view) == web_views_.end()); 75 web_views_.insert(web_view); 76 } 77 78 void AccessibleWebViewRegistry::UnregisterWebView(AccessibleWebView* web_view) { 79 DCHECK(web_views_.find(web_view) != web_views_.end()); 80 web_views_.erase(web_view); 81 if (last_web_view_ == web_view) { 82 last_top_view_ = NULL; 83 last_web_view_ = NULL; 84 } 85 } 86 87 IAccessible* AccessibleWebViewRegistry::GetAccessibleFromWebView( 88 View* top_view, long child_id) { 89 // This function gets called frequently, so try to avoid searching all 90 // of the web views if the notification is on the same web view that 91 // sent the last one. 92 if (last_top_view_ == top_view) { 93 IAccessible* accessible = 94 last_web_view_->AccessibleObjectFromChildId(child_id); 95 if (accessible) 96 return accessible; 97 } 98 99 // Search all web views. For each one, first ensure it's a descendant 100 // of this view where the event was posted - and if so, see if it owns 101 // an accessible object with that child id. If so, save the view to speed 102 // up the next notification. 103 for (std::set<AccessibleWebView*>::iterator iter = web_views_.begin(); 104 iter != web_views_.end(); ++iter) { 105 AccessibleWebView* web_view = *iter; 106 if (!top_view->Contains(web_view->AsView())) 107 continue; 108 IAccessible* accessible = web_view->AccessibleObjectFromChildId(child_id); 109 if (accessible) { 110 last_top_view_ = top_view; 111 last_web_view_ = web_view; 112 return accessible; 113 } 114 } 115 return NULL; 116 } 117 118 } // anonymous namespace 119 120 // static 121 long NativeViewAccessibilityWin::next_unique_id_ = 1; 122 int NativeViewAccessibilityWin::view_storage_ids_[kMaxViewStorageIds] = {0}; 123 int NativeViewAccessibilityWin::next_view_storage_id_index_ = 0; 124 125 // static 126 NativeViewAccessibility* NativeViewAccessibility::Create(View* view) { 127 // Make sure ATL is initialized in this module. 128 ui::win::CreateATLModuleIfNeeded(); 129 130 CComObject<NativeViewAccessibilityWin>* instance = NULL; 131 HRESULT hr = CComObject<NativeViewAccessibilityWin>::CreateInstance( 132 &instance); 133 DCHECK(SUCCEEDED(hr)); 134 instance->set_view(view); 135 instance->AddRef(); 136 return instance; 137 } 138 139 NativeViewAccessibilityWin::NativeViewAccessibilityWin() 140 : view_(NULL), 141 unique_id_(next_unique_id_++) { 142 } 143 144 NativeViewAccessibilityWin::~NativeViewAccessibilityWin() { 145 } 146 147 void NativeViewAccessibilityWin::NotifyAccessibilityEvent( 148 ui::AccessibilityTypes::Event event_type) { 149 if (!view_) 150 return; 151 152 ViewStorage* view_storage = ViewStorage::GetInstance(); 153 HWND hwnd = HWNDForView(view_); 154 int view_storage_id = view_storage_ids_[next_view_storage_id_index_]; 155 if (view_storage_id == 0) { 156 view_storage_id = view_storage->CreateStorageID(); 157 view_storage_ids_[next_view_storage_id_index_] = view_storage_id; 158 } else { 159 view_storage->RemoveView(view_storage_id); 160 } 161 view_storage->StoreView(view_storage_id, view_); 162 163 // Positive child ids are used for enumerating direct children, 164 // negative child ids can be used as unique ids to refer to a specific 165 // descendants. Make index into view_storage_ids_ into a negative child id. 166 int child_id = 167 base::win::kFirstViewsAccessibilityId - next_view_storage_id_index_; 168 ::NotifyWinEvent(MSAAEvent(event_type), hwnd, OBJID_CLIENT, child_id); 169 next_view_storage_id_index_ = 170 (next_view_storage_id_index_ + 1) % kMaxViewStorageIds; 171 } 172 173 gfx::NativeViewAccessible NativeViewAccessibilityWin::GetNativeObject() { 174 return this; 175 } 176 177 void NativeViewAccessibilityWin::Destroy() { 178 view_ = NULL; 179 Release(); 180 } 181 182 // TODO(ctguil): Handle case where child View is not contained by parent. 183 STDMETHODIMP NativeViewAccessibilityWin::accHitTest( 184 LONG x_left, LONG y_top, VARIANT* child) { 185 if (!child) 186 return E_INVALIDARG; 187 188 if (!view_) 189 return E_FAIL; 190 191 gfx::Point point(x_left, y_top); 192 View::ConvertPointToTarget(NULL, view_, &point); 193 194 if (!view_->HitTestPoint(point)) { 195 // If containing parent is not hit, return with failure. 196 child->vt = VT_EMPTY; 197 return S_FALSE; 198 } 199 200 View* view = view_->GetEventHandlerForPoint(point); 201 if (view == view_) { 202 // No child hit, return parent id. 203 child->vt = VT_I4; 204 child->lVal = CHILDID_SELF; 205 } else { 206 child->vt = VT_DISPATCH; 207 child->pdispVal = view->GetNativeViewAccessible(); 208 child->pdispVal->AddRef(); 209 } 210 return S_OK; 211 } 212 213 HRESULT NativeViewAccessibilityWin::accDoDefaultAction(VARIANT var_id) { 214 if (!IsValidId(var_id)) 215 return E_INVALIDARG; 216 217 // The object does not support the method. This value is returned for 218 // controls that do not perform actions, such as edit fields. 219 return DISP_E_MEMBERNOTFOUND; 220 } 221 222 STDMETHODIMP NativeViewAccessibilityWin::accLocation( 223 LONG* x_left, LONG* y_top, LONG* width, LONG* height, VARIANT var_id) { 224 if (!IsValidId(var_id) || !x_left || !y_top || !width || !height) 225 return E_INVALIDARG; 226 227 if (!view_) 228 return E_FAIL; 229 230 if (!view_->bounds().IsEmpty()) { 231 *width = view_->width(); 232 *height = view_->height(); 233 gfx::Point topleft(view_->bounds().origin()); 234 View::ConvertPointToScreen( 235 view_->parent() ? view_->parent() : view_, &topleft); 236 *x_left = topleft.x(); 237 *y_top = topleft.y(); 238 } else { 239 return E_FAIL; 240 } 241 return S_OK; 242 } 243 244 STDMETHODIMP NativeViewAccessibilityWin::accNavigate( 245 LONG nav_dir, VARIANT start, VARIANT* end) { 246 if (start.vt != VT_I4 || !end) 247 return E_INVALIDARG; 248 249 if (!view_) 250 return E_FAIL; 251 252 switch (nav_dir) { 253 case NAVDIR_FIRSTCHILD: 254 case NAVDIR_LASTCHILD: { 255 if (start.lVal != CHILDID_SELF) { 256 // Start of navigation must be on the View itself. 257 return E_INVALIDARG; 258 } else if (!view_->has_children()) { 259 // No children found. 260 return S_FALSE; 261 } 262 263 // Set child_id based on first or last child. 264 int child_id = 0; 265 if (nav_dir == NAVDIR_LASTCHILD) 266 child_id = view_->child_count() - 1; 267 268 View* child = view_->child_at(child_id); 269 end->vt = VT_DISPATCH; 270 end->pdispVal = child->GetNativeViewAccessible(); 271 end->pdispVal->AddRef(); 272 return S_OK; 273 } 274 case NAVDIR_LEFT: 275 case NAVDIR_UP: 276 case NAVDIR_PREVIOUS: 277 case NAVDIR_RIGHT: 278 case NAVDIR_DOWN: 279 case NAVDIR_NEXT: { 280 // Retrieve parent to access view index and perform bounds checking. 281 View* parent = view_->parent(); 282 if (!parent) { 283 return E_FAIL; 284 } 285 286 if (start.lVal == CHILDID_SELF) { 287 int view_index = parent->GetIndexOf(view_); 288 // Check navigation bounds, adjusting for View child indexing (MSAA 289 // child indexing starts with 1, whereas View indexing starts with 0). 290 if (!IsValidNav(nav_dir, view_index, -1, 291 parent->child_count() - 1)) { 292 // Navigation attempted to go out-of-bounds. 293 end->vt = VT_EMPTY; 294 return S_FALSE; 295 } else { 296 if (IsNavDirNext(nav_dir)) { 297 view_index += 1; 298 } else { 299 view_index -=1; 300 } 301 } 302 303 View* child = parent->child_at(view_index); 304 end->pdispVal = child->GetNativeViewAccessible(); 305 end->vt = VT_DISPATCH; 306 end->pdispVal->AddRef(); 307 return S_OK; 308 } else { 309 // Check navigation bounds, adjusting for MSAA child indexing (MSAA 310 // child indexing starts with 1, whereas View indexing starts with 0). 311 if (!IsValidNav(nav_dir, start.lVal, 0, parent->child_count() + 1)) { 312 // Navigation attempted to go out-of-bounds. 313 end->vt = VT_EMPTY; 314 return S_FALSE; 315 } else { 316 if (IsNavDirNext(nav_dir)) { 317 start.lVal += 1; 318 } else { 319 start.lVal -= 1; 320 } 321 } 322 323 HRESULT result = this->get_accChild(start, &end->pdispVal); 324 if (result == S_FALSE) { 325 // Child is a leaf. 326 end->vt = VT_I4; 327 end->lVal = start.lVal; 328 } else if (result == E_INVALIDARG) { 329 return E_INVALIDARG; 330 } else { 331 // Child is not a leaf. 332 end->vt = VT_DISPATCH; 333 } 334 } 335 break; 336 } 337 default: 338 return E_INVALIDARG; 339 } 340 // Navigation performed correctly. Global return for this function, if no 341 // error triggered an escape earlier. 342 return S_OK; 343 } 344 345 STDMETHODIMP NativeViewAccessibilityWin::get_accChild(VARIANT var_child, 346 IDispatch** disp_child) { 347 if (var_child.vt != VT_I4 || !disp_child) 348 return E_INVALIDARG; 349 350 if (!view_) 351 return E_FAIL; 352 353 LONG child_id = V_I4(&var_child); 354 355 if (child_id == CHILDID_SELF) { 356 // Remain with the same dispatch. 357 return S_OK; 358 } 359 360 View* child_view = NULL; 361 if (child_id > 0) { 362 // Positive child ids are a 1-based child index, used by clients 363 // that want to enumerate all immediate children. 364 int child_id_as_index = child_id - 1; 365 if (child_id_as_index < view_->child_count()) 366 child_view = view_->child_at(child_id_as_index); 367 } else { 368 // Negative child ids can be used to map to any descendant; 369 // we map child ids to a view storage id that can refer to a 370 // specific view (if that view still exists). 371 int view_storage_id_index = 372 base::win::kFirstViewsAccessibilityId - child_id; 373 if (view_storage_id_index >= 0 && 374 view_storage_id_index < kMaxViewStorageIds) { 375 int view_storage_id = view_storage_ids_[view_storage_id_index]; 376 ViewStorage* view_storage = ViewStorage::GetInstance(); 377 child_view = view_storage->RetrieveView(view_storage_id); 378 } else { 379 *disp_child = AccessibleWebViewRegistry::GetInstance()-> 380 GetAccessibleFromWebView(view_, child_id); 381 if (*disp_child) { 382 (*disp_child)->AddRef(); 383 return S_OK; 384 } 385 } 386 } 387 388 if (!child_view) { 389 // No child found. 390 *disp_child = NULL; 391 return E_FAIL; 392 } 393 394 *disp_child = child_view->GetNativeViewAccessible(); 395 (*disp_child)->AddRef(); 396 return S_OK; 397 } 398 399 STDMETHODIMP NativeViewAccessibilityWin::get_accChildCount(LONG* child_count) { 400 if (!child_count || !view_) 401 return E_INVALIDARG; 402 403 if (!view_) 404 return E_FAIL; 405 406 *child_count = view_->child_count(); 407 return S_OK; 408 } 409 410 STDMETHODIMP NativeViewAccessibilityWin::get_accDefaultAction( 411 VARIANT var_id, BSTR* def_action) { 412 if (!IsValidId(var_id) || !def_action) 413 return E_INVALIDARG; 414 415 if (!view_) 416 return E_FAIL; 417 418 ui::AccessibleViewState state; 419 view_->GetAccessibleState(&state); 420 string16 temp_action = state.default_action; 421 422 if (!temp_action.empty()) { 423 *def_action = SysAllocString(temp_action.c_str()); 424 } else { 425 return S_FALSE; 426 } 427 428 return S_OK; 429 } 430 431 STDMETHODIMP NativeViewAccessibilityWin::get_accDescription( 432 VARIANT var_id, BSTR* desc) { 433 if (!IsValidId(var_id) || !desc) 434 return E_INVALIDARG; 435 436 if (!view_) 437 return E_FAIL; 438 439 string16 temp_desc; 440 441 view_->GetTooltipText(gfx::Point(), &temp_desc); 442 if (!temp_desc.empty()) { 443 *desc = SysAllocString(temp_desc.c_str()); 444 } else { 445 return S_FALSE; 446 } 447 448 return S_OK; 449 } 450 451 STDMETHODIMP NativeViewAccessibilityWin::get_accFocus(VARIANT* focus_child) { 452 if (!focus_child) 453 return E_INVALIDARG; 454 455 if (!view_) 456 return E_FAIL; 457 458 FocusManager* focus_manager = view_->GetFocusManager(); 459 View* focus = focus_manager ? focus_manager->GetFocusedView() : NULL; 460 if (focus == view_) { 461 // This view has focus. 462 focus_child->vt = VT_I4; 463 focus_child->lVal = CHILDID_SELF; 464 } else if (focus && view_->Contains(focus)) { 465 // Return the child object that has the keyboard focus. 466 focus_child->vt = VT_DISPATCH; 467 focus_child->pdispVal = focus->GetNativeViewAccessible(); 468 focus_child->pdispVal->AddRef(); 469 return S_OK; 470 } else { 471 // Neither this object nor any of its children has the keyboard focus. 472 focus_child->vt = VT_EMPTY; 473 } 474 return S_OK; 475 } 476 477 STDMETHODIMP NativeViewAccessibilityWin::get_accKeyboardShortcut( 478 VARIANT var_id, BSTR* acc_key) { 479 if (!IsValidId(var_id) || !acc_key) 480 return E_INVALIDARG; 481 482 if (!view_) 483 return E_FAIL; 484 485 ui::AccessibleViewState state; 486 view_->GetAccessibleState(&state); 487 string16 temp_key = state.keyboard_shortcut; 488 489 if (!temp_key.empty()) { 490 *acc_key = SysAllocString(temp_key.c_str()); 491 } else { 492 return S_FALSE; 493 } 494 495 return S_OK; 496 } 497 498 STDMETHODIMP NativeViewAccessibilityWin::get_accName( 499 VARIANT var_id, BSTR* name) { 500 if (!IsValidId(var_id) || !name) 501 return E_INVALIDARG; 502 503 if (!view_) 504 return E_FAIL; 505 506 // Retrieve the current view's name. 507 ui::AccessibleViewState state; 508 view_->GetAccessibleState(&state); 509 string16 temp_name = state.name; 510 if (!temp_name.empty()) { 511 // Return name retrieved. 512 *name = SysAllocString(temp_name.c_str()); 513 } else { 514 // If view has no name, return S_FALSE. 515 return S_FALSE; 516 } 517 518 return S_OK; 519 } 520 521 STDMETHODIMP NativeViewAccessibilityWin::get_accParent( 522 IDispatch** disp_parent) { 523 if (!disp_parent) 524 return E_INVALIDARG; 525 526 if (!view_) 527 return E_FAIL; 528 529 *disp_parent = NULL; 530 View* parent_view = view_->parent(); 531 532 if (!parent_view) { 533 HWND hwnd = HWNDForView(view_); 534 if (!hwnd) 535 return S_FALSE; 536 537 return ::AccessibleObjectFromWindow( 538 hwnd, OBJID_WINDOW, IID_IAccessible, 539 reinterpret_cast<void**>(disp_parent)); 540 } 541 542 *disp_parent = parent_view->GetNativeViewAccessible(); 543 (*disp_parent)->AddRef(); 544 return S_OK; 545 } 546 547 STDMETHODIMP NativeViewAccessibilityWin::get_accRole( 548 VARIANT var_id, VARIANT* role) { 549 if (!IsValidId(var_id) || !role) 550 return E_INVALIDARG; 551 552 if (!view_) 553 return E_FAIL; 554 555 ui::AccessibleViewState state; 556 view_->GetAccessibleState(&state); 557 role->vt = VT_I4; 558 role->lVal = MSAARole(state.role); 559 return S_OK; 560 } 561 562 STDMETHODIMP NativeViewAccessibilityWin::get_accState( 563 VARIANT var_id, VARIANT* state) { 564 // This returns MSAA states. See also the IAccessible2 interface 565 // get_states(). 566 567 if (!IsValidId(var_id) || !state) 568 return E_INVALIDARG; 569 570 if (!view_) 571 return E_FAIL; 572 573 state->vt = VT_I4; 574 575 // Retrieve all currently applicable states of the parent. 576 SetState(state, view_); 577 578 // Make sure that state is not empty, and has the proper type. 579 if (state->vt == VT_EMPTY) 580 return E_FAIL; 581 582 return S_OK; 583 } 584 585 STDMETHODIMP NativeViewAccessibilityWin::get_accValue(VARIANT var_id, 586 BSTR* value) { 587 if (!IsValidId(var_id) || !value) 588 return E_INVALIDARG; 589 590 if (!view_) 591 return E_FAIL; 592 593 // Retrieve the current view's value. 594 ui::AccessibleViewState state; 595 view_->GetAccessibleState(&state); 596 string16 temp_value = state.value; 597 598 if (!temp_value.empty()) { 599 // Return value retrieved. 600 *value = SysAllocString(temp_value.c_str()); 601 } else { 602 // If view has no value, fall back into the default implementation. 603 *value = NULL; 604 return E_NOTIMPL; 605 } 606 607 return S_OK; 608 } 609 610 STDMETHODIMP NativeViewAccessibilityWin::put_accValue(VARIANT var_id, 611 BSTR new_value) { 612 if (!IsValidId(var_id) || !new_value) 613 return E_INVALIDARG; 614 615 if (!view_) 616 return E_FAIL; 617 618 // Return an error if the view can't set the value. 619 ui::AccessibleViewState state; 620 view_->GetAccessibleState(&state); 621 if (state.set_value_callback.is_null()) 622 return E_FAIL; 623 624 state.set_value_callback.Run(new_value); 625 return S_OK; 626 } 627 628 // IAccessible functions not supported. 629 630 STDMETHODIMP NativeViewAccessibilityWin::get_accSelection(VARIANT* selected) { 631 if (selected) 632 selected->vt = VT_EMPTY; 633 return E_NOTIMPL; 634 } 635 636 STDMETHODIMP NativeViewAccessibilityWin::accSelect( 637 LONG flagsSelect, VARIANT var_id) { 638 return E_NOTIMPL; 639 } 640 641 STDMETHODIMP NativeViewAccessibilityWin::get_accHelp( 642 VARIANT var_id, BSTR* help) { 643 if (help) 644 *help = NULL; 645 return E_NOTIMPL; 646 } 647 648 STDMETHODIMP NativeViewAccessibilityWin::get_accHelpTopic( 649 BSTR* help_file, VARIANT var_id, LONG* topic_id) { 650 if (help_file) { 651 *help_file = NULL; 652 } 653 if (topic_id) { 654 *topic_id = static_cast<LONG>(-1); 655 } 656 return E_NOTIMPL; 657 } 658 659 STDMETHODIMP NativeViewAccessibilityWin::put_accName( 660 VARIANT var_id, BSTR put_name) { 661 // Deprecated. 662 return E_NOTIMPL; 663 } 664 665 // 666 // IAccessible2 667 // 668 669 STDMETHODIMP NativeViewAccessibilityWin::role(LONG* role) { 670 if (!view_) 671 return E_FAIL; 672 673 if (!role) 674 return E_INVALIDARG; 675 676 ui::AccessibleViewState state; 677 view_->GetAccessibleState(&state); 678 *role = MSAARole(state.role); 679 return S_OK; 680 } 681 682 STDMETHODIMP NativeViewAccessibilityWin::get_states(AccessibleStates* states) { 683 // This returns IAccessible2 states, which supplement MSAA states. 684 // See also the MSAA interface get_accState. 685 686 if (!view_) 687 return E_FAIL; 688 689 if (!states) 690 return E_INVALIDARG; 691 692 ui::AccessibleViewState state; 693 view_->GetAccessibleState(&state); 694 695 // There are only a couple of states we need to support 696 // in IAccessible2. If any more are added, we may want to 697 // add a helper function like MSAAState. 698 *states = IA2_STATE_OPAQUE; 699 if (state.state & AccessibilityTypes::STATE_EDITABLE) 700 *states |= IA2_STATE_EDITABLE; 701 702 return S_OK; 703 } 704 705 STDMETHODIMP NativeViewAccessibilityWin::get_uniqueID(LONG* unique_id) { 706 if (!view_) 707 return E_FAIL; 708 709 if (!unique_id) 710 return E_INVALIDARG; 711 712 *unique_id = unique_id_; 713 return S_OK; 714 } 715 716 STDMETHODIMP NativeViewAccessibilityWin::get_windowHandle(HWND* window_handle) { 717 if (!view_) 718 return E_FAIL; 719 720 if (!window_handle) 721 return E_INVALIDARG; 722 723 *window_handle = HWNDForView(view_); 724 return *window_handle ? S_OK : S_FALSE; 725 } 726 727 // 728 // IAccessibleText 729 // 730 731 STDMETHODIMP NativeViewAccessibilityWin::get_nCharacters(LONG* n_characters) { 732 if (!view_) 733 return E_FAIL; 734 735 if (!n_characters) 736 return E_INVALIDARG; 737 738 string16 text = TextForIAccessibleText(); 739 *n_characters = static_cast<LONG>(text.size()); 740 return S_OK; 741 } 742 743 STDMETHODIMP NativeViewAccessibilityWin::get_caretOffset(LONG* offset) { 744 if (!view_) 745 return E_FAIL; 746 747 if (!offset) 748 return E_INVALIDARG; 749 750 ui::AccessibleViewState state; 751 view_->GetAccessibleState(&state); 752 *offset = static_cast<LONG>(state.selection_end); 753 return S_OK; 754 } 755 756 STDMETHODIMP NativeViewAccessibilityWin::get_nSelections(LONG* n_selections) { 757 if (!view_) 758 return E_FAIL; 759 760 if (!n_selections) 761 return E_INVALIDARG; 762 763 ui::AccessibleViewState state; 764 view_->GetAccessibleState(&state); 765 if (state.selection_start != state.selection_end) 766 *n_selections = 1; 767 else 768 *n_selections = 0; 769 return S_OK; 770 } 771 772 STDMETHODIMP NativeViewAccessibilityWin::get_selection(LONG selection_index, 773 LONG* start_offset, 774 LONG* end_offset) { 775 if (!view_) 776 return E_FAIL; 777 778 if (!start_offset || !end_offset || selection_index != 0) 779 return E_INVALIDARG; 780 781 ui::AccessibleViewState state; 782 view_->GetAccessibleState(&state); 783 *start_offset = static_cast<LONG>(state.selection_start); 784 *end_offset = static_cast<LONG>(state.selection_end); 785 return S_OK; 786 } 787 788 STDMETHODIMP NativeViewAccessibilityWin::get_text(LONG start_offset, 789 LONG end_offset, 790 BSTR* text) { 791 if (!view_) 792 return E_FAIL; 793 794 ui::AccessibleViewState state; 795 view_->GetAccessibleState(&state); 796 string16 text_str = TextForIAccessibleText(); 797 LONG len = static_cast<LONG>(text_str.size()); 798 799 if (start_offset == IA2_TEXT_OFFSET_LENGTH) { 800 start_offset = len; 801 } else if (start_offset == IA2_TEXT_OFFSET_CARET) { 802 start_offset = static_cast<LONG>(state.selection_end); 803 } 804 if (end_offset == IA2_TEXT_OFFSET_LENGTH) { 805 end_offset = static_cast<LONG>(text_str.size()); 806 } else if (end_offset == IA2_TEXT_OFFSET_CARET) { 807 end_offset = static_cast<LONG>(state.selection_end); 808 } 809 810 // The spec allows the arguments to be reversed. 811 if (start_offset > end_offset) { 812 LONG tmp = start_offset; 813 start_offset = end_offset; 814 end_offset = tmp; 815 } 816 817 // The spec does not allow the start or end offsets to be out or range; 818 // we must return an error if so. 819 if (start_offset < 0) 820 return E_INVALIDARG; 821 if (end_offset > len) 822 return E_INVALIDARG; 823 824 string16 substr = text_str.substr(start_offset, end_offset - start_offset); 825 if (substr.empty()) 826 return S_FALSE; 827 828 *text = SysAllocString(substr.c_str()); 829 DCHECK(*text); 830 return S_OK; 831 } 832 833 STDMETHODIMP NativeViewAccessibilityWin::get_textAtOffset( 834 LONG offset, 835 enum IA2TextBoundaryType boundary_type, 836 LONG* start_offset, LONG* end_offset, 837 BSTR* text) { 838 if (!start_offset || !end_offset || !text) 839 return E_INVALIDARG; 840 841 // The IAccessible2 spec says we don't have to implement the "sentence" 842 // boundary type, we can just let the screenreader handle it. 843 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { 844 *start_offset = 0; 845 *end_offset = 0; 846 *text = NULL; 847 return S_FALSE; 848 } 849 850 const string16& text_str = TextForIAccessibleText(); 851 852 *start_offset = FindBoundary( 853 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); 854 *end_offset = FindBoundary( 855 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION); 856 return get_text(*start_offset, *end_offset, text); 857 } 858 859 STDMETHODIMP NativeViewAccessibilityWin::get_textBeforeOffset( 860 LONG offset, 861 enum IA2TextBoundaryType boundary_type, 862 LONG* start_offset, LONG* end_offset, 863 BSTR* text) { 864 if (!start_offset || !end_offset || !text) 865 return E_INVALIDARG; 866 867 // The IAccessible2 spec says we don't have to implement the "sentence" 868 // boundary type, we can just let the screenreader handle it. 869 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { 870 *start_offset = 0; 871 *end_offset = 0; 872 *text = NULL; 873 return S_FALSE; 874 } 875 876 const string16& text_str = TextForIAccessibleText(); 877 878 *start_offset = FindBoundary( 879 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); 880 *end_offset = offset; 881 return get_text(*start_offset, *end_offset, text); 882 } 883 884 STDMETHODIMP NativeViewAccessibilityWin::get_textAfterOffset( 885 LONG offset, 886 enum IA2TextBoundaryType boundary_type, 887 LONG* start_offset, LONG* end_offset, 888 BSTR* text) { 889 if (!start_offset || !end_offset || !text) 890 return E_INVALIDARG; 891 892 // The IAccessible2 spec says we don't have to implement the "sentence" 893 // boundary type, we can just let the screenreader handle it. 894 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { 895 *start_offset = 0; 896 *end_offset = 0; 897 *text = NULL; 898 return S_FALSE; 899 } 900 901 const string16& text_str = TextForIAccessibleText(); 902 903 *start_offset = offset; 904 *end_offset = FindBoundary( 905 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION); 906 return get_text(*start_offset, *end_offset, text); 907 } 908 909 STDMETHODIMP NativeViewAccessibilityWin::get_offsetAtPoint( 910 LONG x, LONG y, enum IA2CoordinateType coord_type, LONG* offset) { 911 if (!view_) 912 return E_FAIL; 913 914 if (!offset) 915 return E_INVALIDARG; 916 917 // We don't support this method, but we have to return something 918 // rather than E_NOTIMPL or screen readers will complain. 919 *offset = 0; 920 return S_OK; 921 } 922 923 // 924 // IServiceProvider methods. 925 // 926 927 STDMETHODIMP NativeViewAccessibilityWin::QueryService( 928 REFGUID guidService, REFIID riid, void** object) { 929 if (!view_) 930 return E_FAIL; 931 932 if (guidService == IID_IAccessible || 933 guidService == IID_IAccessible2 || 934 guidService == IID_IAccessibleText) { 935 return QueryInterface(riid, object); 936 } 937 938 // We only support the IAccessibleEx interface on Windows 8 and above. This 939 // is needed for the On screen Keyboard to show up in metro mode, when the 940 // user taps an editable region in the window. 941 // All methods in the IAccessibleEx interface are unimplemented. 942 if (riid == IID_IAccessibleEx && 943 base::win::GetVersion() >= base::win::VERSION_WIN8) { 944 return QueryInterface(riid, object); 945 } 946 947 *object = NULL; 948 return E_FAIL; 949 } 950 951 STDMETHODIMP NativeViewAccessibilityWin::GetPatternProvider( 952 PATTERNID id, IUnknown** provider) { 953 DVLOG(1) << "In Function: " 954 << __FUNCTION__ 955 << " for pattern id: " 956 << id; 957 if (id == UIA_ValuePatternId || id == UIA_TextPatternId) { 958 ui::AccessibleViewState state; 959 view_->GetAccessibleState(&state); 960 long role = MSAARole(state.role); 961 962 if (role == ROLE_SYSTEM_TEXT) { 963 DVLOG(1) << "Returning UIA text provider"; 964 base::win::UIATextProvider::CreateTextProvider(true, provider); 965 return S_OK; 966 } 967 } 968 return E_NOTIMPL; 969 } 970 971 STDMETHODIMP NativeViewAccessibilityWin::GetPropertyValue(PROPERTYID id, 972 VARIANT* ret) { 973 DVLOG(1) << "In Function: " 974 << __FUNCTION__ 975 << " for property id: " 976 << id; 977 if (id == UIA_ControlTypePropertyId) { 978 ui::AccessibleViewState state; 979 view_->GetAccessibleState(&state); 980 long role = MSAARole(state.role); 981 if (role == ROLE_SYSTEM_TEXT) { 982 V_VT(ret) = VT_I4; 983 ret->lVal = UIA_EditControlTypeId; 984 DVLOG(1) << "Returning Edit control type"; 985 } else { 986 DVLOG(1) << "Returning empty control type"; 987 V_VT(ret) = VT_EMPTY; 988 } 989 } else { 990 V_VT(ret) = VT_EMPTY; 991 } 992 return S_OK; 993 } 994 995 // 996 // Static methods. 997 // 998 999 void NativeViewAccessibility::RegisterWebView(AccessibleWebView* web_view) { 1000 AccessibleWebViewRegistry::GetInstance()->RegisterWebView(web_view); 1001 } 1002 1003 void NativeViewAccessibility::UnregisterWebView(AccessibleWebView* web_view) { 1004 AccessibleWebViewRegistry::GetInstance()->UnregisterWebView(web_view); 1005 } 1006 1007 int32 NativeViewAccessibilityWin::MSAAEvent(AccessibilityTypes::Event event) { 1008 switch (event) { 1009 case AccessibilityTypes::EVENT_ALERT: 1010 return EVENT_SYSTEM_ALERT; 1011 case AccessibilityTypes::EVENT_FOCUS: 1012 return EVENT_OBJECT_FOCUS; 1013 case AccessibilityTypes::EVENT_MENUSTART: 1014 return EVENT_SYSTEM_MENUSTART; 1015 case AccessibilityTypes::EVENT_MENUEND: 1016 return EVENT_SYSTEM_MENUEND; 1017 case AccessibilityTypes::EVENT_MENUPOPUPSTART: 1018 return EVENT_SYSTEM_MENUPOPUPSTART; 1019 case AccessibilityTypes::EVENT_MENUPOPUPEND: 1020 return EVENT_SYSTEM_MENUPOPUPEND; 1021 case AccessibilityTypes::EVENT_NAME_CHANGED: 1022 return EVENT_OBJECT_NAMECHANGE; 1023 case AccessibilityTypes::EVENT_TEXT_CHANGED: 1024 return EVENT_OBJECT_VALUECHANGE; 1025 case AccessibilityTypes::EVENT_SELECTION_CHANGED: 1026 return IA2_EVENT_TEXT_CARET_MOVED; 1027 case AccessibilityTypes::EVENT_VALUE_CHANGED: 1028 return EVENT_OBJECT_VALUECHANGE; 1029 default: 1030 // Not supported or invalid event. 1031 NOTREACHED(); 1032 return -1; 1033 } 1034 } 1035 1036 int32 NativeViewAccessibilityWin::MSAARole(AccessibilityTypes::Role role) { 1037 switch (role) { 1038 case AccessibilityTypes::ROLE_ALERT: 1039 return ROLE_SYSTEM_ALERT; 1040 case AccessibilityTypes::ROLE_APPLICATION: 1041 return ROLE_SYSTEM_APPLICATION; 1042 case AccessibilityTypes::ROLE_BUTTONDROPDOWN: 1043 return ROLE_SYSTEM_BUTTONDROPDOWN; 1044 case AccessibilityTypes::ROLE_BUTTONMENU: 1045 return ROLE_SYSTEM_BUTTONMENU; 1046 case AccessibilityTypes::ROLE_CHECKBUTTON: 1047 return ROLE_SYSTEM_CHECKBUTTON; 1048 case AccessibilityTypes::ROLE_COMBOBOX: 1049 return ROLE_SYSTEM_COMBOBOX; 1050 case AccessibilityTypes::ROLE_DIALOG: 1051 return ROLE_SYSTEM_DIALOG; 1052 case AccessibilityTypes::ROLE_GRAPHIC: 1053 return ROLE_SYSTEM_GRAPHIC; 1054 case AccessibilityTypes::ROLE_GROUPING: 1055 return ROLE_SYSTEM_GROUPING; 1056 case AccessibilityTypes::ROLE_LINK: 1057 return ROLE_SYSTEM_LINK; 1058 case AccessibilityTypes::ROLE_LOCATION_BAR: 1059 return ROLE_SYSTEM_GROUPING; 1060 case AccessibilityTypes::ROLE_MENUBAR: 1061 return ROLE_SYSTEM_MENUBAR; 1062 case AccessibilityTypes::ROLE_MENUITEM: 1063 return ROLE_SYSTEM_MENUITEM; 1064 case AccessibilityTypes::ROLE_MENUPOPUP: 1065 return ROLE_SYSTEM_MENUPOPUP; 1066 case AccessibilityTypes::ROLE_OUTLINE: 1067 return ROLE_SYSTEM_OUTLINE; 1068 case AccessibilityTypes::ROLE_OUTLINEITEM: 1069 return ROLE_SYSTEM_OUTLINEITEM; 1070 case AccessibilityTypes::ROLE_PAGETAB: 1071 return ROLE_SYSTEM_PAGETAB; 1072 case AccessibilityTypes::ROLE_PAGETABLIST: 1073 return ROLE_SYSTEM_PAGETABLIST; 1074 case AccessibilityTypes::ROLE_PANE: 1075 return ROLE_SYSTEM_PANE; 1076 case AccessibilityTypes::ROLE_PROGRESSBAR: 1077 return ROLE_SYSTEM_PROGRESSBAR; 1078 case AccessibilityTypes::ROLE_PUSHBUTTON: 1079 return ROLE_SYSTEM_PUSHBUTTON; 1080 case AccessibilityTypes::ROLE_RADIOBUTTON: 1081 return ROLE_SYSTEM_RADIOBUTTON; 1082 case AccessibilityTypes::ROLE_SCROLLBAR: 1083 return ROLE_SYSTEM_SCROLLBAR; 1084 case AccessibilityTypes::ROLE_SEPARATOR: 1085 return ROLE_SYSTEM_SEPARATOR; 1086 case AccessibilityTypes::ROLE_SLIDER: 1087 return ROLE_SYSTEM_SLIDER; 1088 case AccessibilityTypes::ROLE_STATICTEXT: 1089 return ROLE_SYSTEM_STATICTEXT; 1090 case AccessibilityTypes::ROLE_TEXT: 1091 return ROLE_SYSTEM_TEXT; 1092 case AccessibilityTypes::ROLE_TITLEBAR: 1093 return ROLE_SYSTEM_TITLEBAR; 1094 case AccessibilityTypes::ROLE_TOOLBAR: 1095 return ROLE_SYSTEM_TOOLBAR; 1096 case AccessibilityTypes::ROLE_WINDOW: 1097 return ROLE_SYSTEM_WINDOW; 1098 case AccessibilityTypes::ROLE_CLIENT: 1099 default: 1100 // This is the default role for MSAA. 1101 return ROLE_SYSTEM_CLIENT; 1102 } 1103 } 1104 1105 int32 NativeViewAccessibilityWin::MSAAState(AccessibilityTypes::State state) { 1106 // This maps MSAA states for get_accState(). See also the IAccessible2 1107 // interface get_states(). 1108 1109 int32 msaa_state = 0; 1110 if (state & AccessibilityTypes::STATE_CHECKED) 1111 msaa_state |= STATE_SYSTEM_CHECKED; 1112 if (state & AccessibilityTypes::STATE_COLLAPSED) 1113 msaa_state |= STATE_SYSTEM_COLLAPSED; 1114 if (state & AccessibilityTypes::STATE_DEFAULT) 1115 msaa_state |= STATE_SYSTEM_DEFAULT; 1116 if (state & AccessibilityTypes::STATE_EXPANDED) 1117 msaa_state |= STATE_SYSTEM_EXPANDED; 1118 if (state & AccessibilityTypes::STATE_HASPOPUP) 1119 msaa_state |= STATE_SYSTEM_HASPOPUP; 1120 if (state & AccessibilityTypes::STATE_HOTTRACKED) 1121 msaa_state |= STATE_SYSTEM_HOTTRACKED; 1122 if (state & AccessibilityTypes::STATE_INVISIBLE) 1123 msaa_state |= STATE_SYSTEM_INVISIBLE; 1124 if (state & AccessibilityTypes::STATE_LINKED) 1125 msaa_state |= STATE_SYSTEM_LINKED; 1126 if (state & AccessibilityTypes::STATE_OFFSCREEN) 1127 msaa_state |= STATE_SYSTEM_OFFSCREEN; 1128 if (state & AccessibilityTypes::STATE_PRESSED) 1129 msaa_state |= STATE_SYSTEM_PRESSED; 1130 if (state & AccessibilityTypes::STATE_PROTECTED) 1131 msaa_state |= STATE_SYSTEM_PROTECTED; 1132 if (state & AccessibilityTypes::STATE_READONLY) 1133 msaa_state |= STATE_SYSTEM_READONLY; 1134 if (state & AccessibilityTypes::STATE_SELECTED) 1135 msaa_state |= STATE_SYSTEM_SELECTED; 1136 if (state & AccessibilityTypes::STATE_FOCUSED) 1137 msaa_state |= STATE_SYSTEM_FOCUSED; 1138 if (state & AccessibilityTypes::STATE_UNAVAILABLE) 1139 msaa_state |= STATE_SYSTEM_UNAVAILABLE; 1140 return msaa_state; 1141 } 1142 1143 // 1144 // Private methods. 1145 // 1146 1147 bool NativeViewAccessibilityWin::IsNavDirNext(int nav_dir) const { 1148 return (nav_dir == NAVDIR_RIGHT || 1149 nav_dir == NAVDIR_DOWN || 1150 nav_dir == NAVDIR_NEXT); 1151 } 1152 1153 bool NativeViewAccessibilityWin::IsValidNav( 1154 int nav_dir, int start_id, int lower_bound, int upper_bound) const { 1155 if (IsNavDirNext(nav_dir)) { 1156 if ((start_id + 1) > upper_bound) { 1157 return false; 1158 } 1159 } else { 1160 if ((start_id - 1) <= lower_bound) { 1161 return false; 1162 } 1163 } 1164 return true; 1165 } 1166 1167 bool NativeViewAccessibilityWin::IsValidId(const VARIANT& child) const { 1168 // View accessibility returns an IAccessible for each view so we only support 1169 // the CHILDID_SELF id. 1170 return (VT_I4 == child.vt) && (CHILDID_SELF == child.lVal); 1171 } 1172 1173 void NativeViewAccessibilityWin::SetState( 1174 VARIANT* msaa_state, View* view) { 1175 // Ensure the output param is initialized to zero. 1176 msaa_state->lVal = 0; 1177 1178 // Default state; all views can have accessibility focus. 1179 msaa_state->lVal |= STATE_SYSTEM_FOCUSABLE; 1180 1181 if (!view) 1182 return; 1183 1184 if (!view->enabled()) 1185 msaa_state->lVal |= STATE_SYSTEM_UNAVAILABLE; 1186 if (!view->visible()) 1187 msaa_state->lVal |= STATE_SYSTEM_INVISIBLE; 1188 if (!strcmp(view->GetClassName(), CustomButton::kViewClassName)) { 1189 CustomButton* button = static_cast<CustomButton*>(view); 1190 if (button->IsHotTracked()) 1191 msaa_state->lVal |= STATE_SYSTEM_HOTTRACKED; 1192 } 1193 if (view->HasFocus()) 1194 msaa_state->lVal |= STATE_SYSTEM_FOCUSED; 1195 1196 // Add on any view-specific states. 1197 ui::AccessibleViewState view_state; 1198 view->GetAccessibleState(&view_state); 1199 msaa_state->lVal |= MSAAState(view_state.state); 1200 } 1201 1202 string16 NativeViewAccessibilityWin::TextForIAccessibleText() { 1203 ui::AccessibleViewState state; 1204 view_->GetAccessibleState(&state); 1205 if (state.role == AccessibilityTypes::ROLE_TEXT) 1206 return state.value; 1207 else 1208 return state.name; 1209 } 1210 1211 void NativeViewAccessibilityWin::HandleSpecialTextOffset( 1212 const string16& text, LONG* offset) { 1213 if (*offset == IA2_TEXT_OFFSET_LENGTH) { 1214 *offset = static_cast<LONG>(text.size()); 1215 } else if (*offset == IA2_TEXT_OFFSET_CARET) { 1216 get_caretOffset(offset); 1217 } 1218 } 1219 1220 ui::TextBoundaryType NativeViewAccessibilityWin::IA2TextBoundaryToTextBoundary( 1221 IA2TextBoundaryType ia2_boundary) { 1222 switch(ia2_boundary) { 1223 case IA2_TEXT_BOUNDARY_CHAR: return ui::CHAR_BOUNDARY; 1224 case IA2_TEXT_BOUNDARY_WORD: return ui::WORD_BOUNDARY; 1225 case IA2_TEXT_BOUNDARY_LINE: return ui::LINE_BOUNDARY; 1226 case IA2_TEXT_BOUNDARY_SENTENCE: return ui::SENTENCE_BOUNDARY; 1227 case IA2_TEXT_BOUNDARY_PARAGRAPH: return ui::PARAGRAPH_BOUNDARY; 1228 case IA2_TEXT_BOUNDARY_ALL: return ui::ALL_BOUNDARY; 1229 default: 1230 NOTREACHED(); 1231 return ui::CHAR_BOUNDARY; 1232 } 1233 } 1234 1235 LONG NativeViewAccessibilityWin::FindBoundary( 1236 const string16& text, 1237 IA2TextBoundaryType ia2_boundary, 1238 LONG start_offset, 1239 ui::TextBoundaryDirection direction) { 1240 HandleSpecialTextOffset(text, &start_offset); 1241 ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary); 1242 std::vector<int32> line_breaks; 1243 return ui::FindAccessibleTextBoundary( 1244 text, line_breaks, boundary, start_offset, direction); 1245 } 1246 1247 } // namespace views 1248