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 "content/browser/accessibility/browser_accessibility_win.h" 6 7 #include <UIAutomationClient.h> 8 #include <UIAutomationCoreApi.h> 9 10 #include "base/strings/string_number_conversions.h" 11 #include "base/strings/string_split.h" 12 #include "base/strings/string_util.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "base/win/enum_variant.h" 15 #include "base/win/scoped_comptr.h" 16 #include "base/win/windows_version.h" 17 #include "content/browser/accessibility/browser_accessibility_manager_win.h" 18 #include "content/browser/accessibility/browser_accessibility_state_impl.h" 19 #include "content/common/accessibility_messages.h" 20 #include "content/public/common/content_client.h" 21 #include "ui/accessibility/ax_text_utils.h" 22 #include "ui/base/win/accessibility_ids_win.h" 23 #include "ui/base/win/accessibility_misc_utils.h" 24 25 namespace content { 26 27 // These nonstandard GUIDs are taken directly from the Mozilla sources 28 // (accessible/src/msaa/nsAccessNodeWrap.cpp); some documentation is here: 29 // http://developer.mozilla.org/en/Accessibility/AT-APIs/ImplementationFeatures/MSAA 30 const GUID GUID_ISimpleDOM = { 31 0x0c539790, 0x12e4, 0x11cf, 32 0xb6, 0x61, 0x00, 0xaa, 0x00, 0x4c, 0xd6, 0xd8}; 33 const GUID GUID_IAccessibleContentDocument = { 34 0xa5d8e1f3, 0x3571, 0x4d8f, 35 0x95, 0x21, 0x07, 0xed, 0x28, 0xfb, 0x07, 0x2e}; 36 37 const base::char16 BrowserAccessibilityWin::kEmbeddedCharacter[] = L"\xfffc"; 38 39 // static 40 LONG BrowserAccessibilityWin::next_unique_id_win_ = 41 base::win::kFirstBrowserAccessibilityManagerAccessibilityId; 42 43 // 44 // BrowserAccessibilityRelation 45 // 46 // A simple implementation of IAccessibleRelation, used to represent 47 // a relationship between two accessible nodes in the tree. 48 // 49 50 class BrowserAccessibilityRelation 51 : public CComObjectRootEx<CComMultiThreadModel>, 52 public IAccessibleRelation { 53 BEGIN_COM_MAP(BrowserAccessibilityRelation) 54 COM_INTERFACE_ENTRY(IAccessibleRelation) 55 END_COM_MAP() 56 57 CONTENT_EXPORT BrowserAccessibilityRelation() {} 58 CONTENT_EXPORT virtual ~BrowserAccessibilityRelation() {} 59 60 CONTENT_EXPORT void Initialize(BrowserAccessibilityWin* owner, 61 const base::string16& type); 62 CONTENT_EXPORT void AddTarget(int target_id); 63 64 // IAccessibleRelation methods. 65 CONTENT_EXPORT STDMETHODIMP get_relationType(BSTR* relation_type); 66 CONTENT_EXPORT STDMETHODIMP get_nTargets(long* n_targets); 67 CONTENT_EXPORT STDMETHODIMP get_target(long target_index, IUnknown** target); 68 CONTENT_EXPORT STDMETHODIMP get_targets(long max_targets, 69 IUnknown** targets, 70 long* n_targets); 71 72 // IAccessibleRelation methods not implemented. 73 CONTENT_EXPORT STDMETHODIMP get_localizedRelationType(BSTR* relation_type) { 74 return E_NOTIMPL; 75 } 76 77 private: 78 base::string16 type_; 79 base::win::ScopedComPtr<BrowserAccessibilityWin> owner_; 80 std::vector<int> target_ids_; 81 }; 82 83 void BrowserAccessibilityRelation::Initialize(BrowserAccessibilityWin* owner, 84 const base::string16& type) { 85 owner_ = owner; 86 type_ = type; 87 } 88 89 void BrowserAccessibilityRelation::AddTarget(int target_id) { 90 target_ids_.push_back(target_id); 91 } 92 93 STDMETHODIMP BrowserAccessibilityRelation::get_relationType( 94 BSTR* relation_type) { 95 if (!relation_type) 96 return E_INVALIDARG; 97 98 if (!owner_->instance_active()) 99 return E_FAIL; 100 101 *relation_type = SysAllocString(type_.c_str()); 102 DCHECK(*relation_type); 103 return S_OK; 104 } 105 106 STDMETHODIMP BrowserAccessibilityRelation::get_nTargets(long* n_targets) { 107 if (!n_targets) 108 return E_INVALIDARG; 109 110 if (!owner_->instance_active()) 111 return E_FAIL; 112 113 *n_targets = static_cast<long>(target_ids_.size()); 114 115 BrowserAccessibilityManager* manager = owner_->manager(); 116 for (long i = *n_targets - 1; i >= 0; --i) { 117 BrowserAccessibility* result = manager->GetFromID(target_ids_[i]); 118 if (!result || !result->instance_active()) { 119 *n_targets = 0; 120 break; 121 } 122 } 123 return S_OK; 124 } 125 126 STDMETHODIMP BrowserAccessibilityRelation::get_target(long target_index, 127 IUnknown** target) { 128 if (!target) 129 return E_INVALIDARG; 130 131 if (!owner_->instance_active()) 132 return E_FAIL; 133 134 if (target_index < 0 || 135 target_index >= static_cast<long>(target_ids_.size())) { 136 return E_INVALIDARG; 137 } 138 139 BrowserAccessibilityManager* manager = owner_->manager(); 140 BrowserAccessibility* result = 141 manager->GetFromID(target_ids_[target_index]); 142 if (!result || !result->instance_active()) 143 return E_FAIL; 144 145 *target = static_cast<IAccessible*>( 146 result->ToBrowserAccessibilityWin()->NewReference()); 147 return S_OK; 148 } 149 150 STDMETHODIMP BrowserAccessibilityRelation::get_targets(long max_targets, 151 IUnknown** targets, 152 long* n_targets) { 153 if (!targets || !n_targets) 154 return E_INVALIDARG; 155 156 if (!owner_->instance_active()) 157 return E_FAIL; 158 159 long count = static_cast<long>(target_ids_.size()); 160 if (count > max_targets) 161 count = max_targets; 162 163 *n_targets = count; 164 if (count == 0) 165 return S_FALSE; 166 167 for (long i = 0; i < count; ++i) { 168 HRESULT result = get_target(i, &targets[i]); 169 if (result != S_OK) 170 return result; 171 } 172 173 return S_OK; 174 } 175 176 // 177 // BrowserAccessibilityWin 178 // 179 180 // static 181 BrowserAccessibility* BrowserAccessibility::Create() { 182 CComObject<BrowserAccessibilityWin>* instance; 183 HRESULT hr = CComObject<BrowserAccessibilityWin>::CreateInstance(&instance); 184 DCHECK(SUCCEEDED(hr)); 185 return instance->NewReference(); 186 } 187 188 BrowserAccessibilityWin* BrowserAccessibility::ToBrowserAccessibilityWin() { 189 return static_cast<BrowserAccessibilityWin*>(this); 190 } 191 192 BrowserAccessibilityWin::BrowserAccessibilityWin() 193 : ia_role_(0), 194 ia_state_(0), 195 ia2_role_(0), 196 ia2_state_(0), 197 first_time_(true), 198 old_ia_state_(0), 199 previous_scroll_x_(0), 200 previous_scroll_y_(0) { 201 // Start unique IDs at -1 and decrement each time, because get_accChild 202 // uses positive IDs to enumerate children, so we use negative IDs to 203 // clearly distinguish between indices and unique IDs. 204 unique_id_win_ = next_unique_id_win_; 205 if (next_unique_id_win_ == 206 base::win::kLastBrowserAccessibilityManagerAccessibilityId) { 207 next_unique_id_win_ = 208 base::win::kFirstBrowserAccessibilityManagerAccessibilityId; 209 } 210 next_unique_id_win_--; 211 } 212 213 BrowserAccessibilityWin::~BrowserAccessibilityWin() { 214 for (size_t i = 0; i < relations_.size(); ++i) 215 relations_[i]->Release(); 216 } 217 218 // 219 // IAccessible methods. 220 // 221 // Conventions: 222 // * Always test for instance_active() first and return E_FAIL if it's false. 223 // * Always check for invalid arguments first, even if they're unused. 224 // * Return S_FALSE if the only output is a string argument and it's empty. 225 // 226 227 HRESULT BrowserAccessibilityWin::accDoDefaultAction(VARIANT var_id) { 228 if (!instance_active()) 229 return E_FAIL; 230 231 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); 232 if (!target) 233 return E_INVALIDARG; 234 235 manager()->DoDefaultAction(*target); 236 return S_OK; 237 } 238 239 STDMETHODIMP BrowserAccessibilityWin::accHitTest(LONG x_left, 240 LONG y_top, 241 VARIANT* child) { 242 if (!instance_active()) 243 return E_FAIL; 244 245 if (!child) 246 return E_INVALIDARG; 247 248 gfx::Point point(x_left, y_top); 249 if (!GetGlobalBoundsRect().Contains(point)) { 250 // Return S_FALSE and VT_EMPTY when the outside the object's boundaries. 251 child->vt = VT_EMPTY; 252 return S_FALSE; 253 } 254 255 BrowserAccessibility* result = BrowserAccessibilityForPoint(point); 256 if (result == this) { 257 // Point is within this object. 258 child->vt = VT_I4; 259 child->lVal = CHILDID_SELF; 260 } else { 261 child->vt = VT_DISPATCH; 262 child->pdispVal = result->ToBrowserAccessibilityWin()->NewReference(); 263 } 264 return S_OK; 265 } 266 267 STDMETHODIMP BrowserAccessibilityWin::accLocation(LONG* x_left, 268 LONG* y_top, 269 LONG* width, 270 LONG* height, 271 VARIANT var_id) { 272 if (!instance_active()) 273 return E_FAIL; 274 275 if (!x_left || !y_top || !width || !height) 276 return E_INVALIDARG; 277 278 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); 279 if (!target) 280 return E_INVALIDARG; 281 282 gfx::Rect bounds = target->GetGlobalBoundsRect(); 283 *x_left = bounds.x(); 284 *y_top = bounds.y(); 285 *width = bounds.width(); 286 *height = bounds.height(); 287 288 return S_OK; 289 } 290 291 STDMETHODIMP BrowserAccessibilityWin::accNavigate(LONG nav_dir, 292 VARIANT start, 293 VARIANT* end) { 294 BrowserAccessibilityWin* target = GetTargetFromChildID(start); 295 if (!target) 296 return E_INVALIDARG; 297 298 if ((nav_dir == NAVDIR_LASTCHILD || nav_dir == NAVDIR_FIRSTCHILD) && 299 start.lVal != CHILDID_SELF) { 300 // MSAA states that navigating to first/last child can only be from self. 301 return E_INVALIDARG; 302 } 303 304 uint32 child_count = target->PlatformChildCount(); 305 306 BrowserAccessibility* result = NULL; 307 switch (nav_dir) { 308 case NAVDIR_DOWN: 309 case NAVDIR_UP: 310 case NAVDIR_LEFT: 311 case NAVDIR_RIGHT: 312 // These directions are not implemented, matching Mozilla and IE. 313 return E_NOTIMPL; 314 case NAVDIR_FIRSTCHILD: 315 if (child_count > 0) 316 result = target->PlatformGetChild(0); 317 break; 318 case NAVDIR_LASTCHILD: 319 if (child_count > 0) 320 result = target->PlatformGetChild(child_count - 1); 321 break; 322 case NAVDIR_NEXT: 323 result = target->GetNextSibling(); 324 break; 325 case NAVDIR_PREVIOUS: 326 result = target->GetPreviousSibling(); 327 break; 328 } 329 330 if (!result) { 331 end->vt = VT_EMPTY; 332 return S_FALSE; 333 } 334 335 end->vt = VT_DISPATCH; 336 end->pdispVal = result->ToBrowserAccessibilityWin()->NewReference(); 337 return S_OK; 338 } 339 340 STDMETHODIMP BrowserAccessibilityWin::get_accChild(VARIANT var_child, 341 IDispatch** disp_child) { 342 if (!instance_active()) 343 return E_FAIL; 344 345 if (!disp_child) 346 return E_INVALIDARG; 347 348 *disp_child = NULL; 349 350 BrowserAccessibilityWin* target = GetTargetFromChildID(var_child); 351 if (!target) 352 return E_INVALIDARG; 353 354 (*disp_child) = target->NewReference(); 355 return S_OK; 356 } 357 358 STDMETHODIMP BrowserAccessibilityWin::get_accChildCount(LONG* child_count) { 359 if (!instance_active()) 360 return E_FAIL; 361 362 if (!child_count) 363 return E_INVALIDARG; 364 365 *child_count = PlatformChildCount(); 366 367 return S_OK; 368 } 369 370 STDMETHODIMP BrowserAccessibilityWin::get_accDefaultAction(VARIANT var_id, 371 BSTR* def_action) { 372 if (!instance_active()) 373 return E_FAIL; 374 375 if (!def_action) 376 return E_INVALIDARG; 377 378 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); 379 if (!target) 380 return E_INVALIDARG; 381 382 return target->GetStringAttributeAsBstr( 383 ui::AX_ATTR_SHORTCUT, def_action); 384 } 385 386 STDMETHODIMP BrowserAccessibilityWin::get_accDescription(VARIANT var_id, 387 BSTR* desc) { 388 if (!instance_active()) 389 return E_FAIL; 390 391 if (!desc) 392 return E_INVALIDARG; 393 394 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); 395 if (!target) 396 return E_INVALIDARG; 397 398 return target->GetStringAttributeAsBstr( 399 ui::AX_ATTR_DESCRIPTION, desc); 400 } 401 402 STDMETHODIMP BrowserAccessibilityWin::get_accFocus(VARIANT* focus_child) { 403 if (!instance_active()) 404 return E_FAIL; 405 406 if (!focus_child) 407 return E_INVALIDARG; 408 409 BrowserAccessibilityWin* focus = static_cast<BrowserAccessibilityWin*>( 410 manager()->GetFocus(this)); 411 if (focus == this) { 412 focus_child->vt = VT_I4; 413 focus_child->lVal = CHILDID_SELF; 414 } else if (focus == NULL) { 415 focus_child->vt = VT_EMPTY; 416 } else { 417 focus_child->vt = VT_DISPATCH; 418 focus_child->pdispVal = focus->NewReference(); 419 } 420 421 return S_OK; 422 } 423 424 STDMETHODIMP BrowserAccessibilityWin::get_accHelp(VARIANT var_id, BSTR* help) { 425 if (!instance_active()) 426 return E_FAIL; 427 428 if (!help) 429 return E_INVALIDARG; 430 431 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); 432 if (!target) 433 return E_INVALIDARG; 434 435 return target->GetStringAttributeAsBstr( 436 ui::AX_ATTR_HELP, help); 437 } 438 439 STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id, 440 BSTR* acc_key) { 441 if (!instance_active()) 442 return E_FAIL; 443 444 if (!acc_key) 445 return E_INVALIDARG; 446 447 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); 448 if (!target) 449 return E_INVALIDARG; 450 451 return target->GetStringAttributeAsBstr( 452 ui::AX_ATTR_SHORTCUT, acc_key); 453 } 454 455 STDMETHODIMP BrowserAccessibilityWin::get_accName(VARIANT var_id, BSTR* name) { 456 if (!instance_active()) 457 return E_FAIL; 458 459 if (!name) 460 return E_INVALIDARG; 461 462 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); 463 if (!target) 464 return E_INVALIDARG; 465 466 std::string name_str = target->name(); 467 468 // If the name is empty, see if it's labeled by another element. 469 if (name_str.empty()) { 470 int title_elem_id; 471 if (target->GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT, 472 &title_elem_id)) { 473 BrowserAccessibility* title_elem = 474 manager()->GetFromID(title_elem_id); 475 if (title_elem) 476 name_str = title_elem->GetTextRecursive(); 477 } 478 } 479 480 if (name_str.empty()) 481 return S_FALSE; 482 483 *name = SysAllocString(base::UTF8ToUTF16(name_str).c_str()); 484 485 DCHECK(*name); 486 return S_OK; 487 } 488 489 STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) { 490 if (!instance_active()) 491 return E_FAIL; 492 493 if (!disp_parent) 494 return E_INVALIDARG; 495 496 IAccessible* parent_obj = GetParent()->ToBrowserAccessibilityWin(); 497 if (parent_obj == NULL) { 498 // This happens if we're the root of the tree; 499 // return the IAccessible for the window. 500 parent_obj = 501 manager()->ToBrowserAccessibilityManagerWin()->parent_iaccessible(); 502 // |parent| can only be NULL if the manager was created before the parent 503 // IAccessible was known and it wasn't subsequently set before a client 504 // requested it. This has been fixed. |parent| may also be NULL during 505 // destruction. Possible cases where this could occur include tabs being 506 // dragged to a new window, etc. 507 if (!parent_obj) { 508 DVLOG(1) << "In Function: " 509 << __FUNCTION__ 510 << ". Parent IAccessible interface is NULL. Returning failure"; 511 return E_FAIL; 512 } 513 } 514 parent_obj->AddRef(); 515 *disp_parent = parent_obj; 516 return S_OK; 517 } 518 519 STDMETHODIMP BrowserAccessibilityWin::get_accRole(VARIANT var_id, 520 VARIANT* role) { 521 if (!instance_active()) 522 return E_FAIL; 523 524 if (!role) 525 return E_INVALIDARG; 526 527 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); 528 if (!target) 529 return E_INVALIDARG; 530 531 if (!target->role_name_.empty()) { 532 role->vt = VT_BSTR; 533 role->bstrVal = SysAllocString(target->role_name_.c_str()); 534 } else { 535 role->vt = VT_I4; 536 role->lVal = target->ia_role_; 537 } 538 return S_OK; 539 } 540 541 STDMETHODIMP BrowserAccessibilityWin::get_accState(VARIANT var_id, 542 VARIANT* state) { 543 if (!instance_active()) 544 return E_FAIL; 545 546 if (!state) 547 return E_INVALIDARG; 548 549 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); 550 if (!target) 551 return E_INVALIDARG; 552 553 state->vt = VT_I4; 554 state->lVal = target->ia_state_; 555 if (manager()->GetFocus(NULL) == this) 556 state->lVal |= STATE_SYSTEM_FOCUSED; 557 558 return S_OK; 559 } 560 561 STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id, 562 BSTR* value) { 563 if (!instance_active()) 564 return E_FAIL; 565 566 if (!value) 567 return E_INVALIDARG; 568 569 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); 570 if (!target) 571 return E_INVALIDARG; 572 573 if (target->ia_role() == ROLE_SYSTEM_PROGRESSBAR || 574 target->ia_role() == ROLE_SYSTEM_SCROLLBAR || 575 target->ia_role() == ROLE_SYSTEM_SLIDER) { 576 base::string16 value_text = target->GetValueText(); 577 *value = SysAllocString(value_text.c_str()); 578 DCHECK(*value); 579 return S_OK; 580 } 581 582 // Expose color well value. 583 if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) { 584 int r = target->GetIntAttribute( 585 ui::AX_ATTR_COLOR_VALUE_RED); 586 int g = target->GetIntAttribute( 587 ui::AX_ATTR_COLOR_VALUE_GREEN); 588 int b = target->GetIntAttribute( 589 ui::AX_ATTR_COLOR_VALUE_BLUE); 590 base::string16 value_text; 591 value_text = base::IntToString16((r * 100) / 255) + L"% red " + 592 base::IntToString16((g * 100) / 255) + L"% green " + 593 base::IntToString16((b * 100) / 255) + L"% blue"; 594 *value = SysAllocString(value_text.c_str()); 595 DCHECK(*value); 596 return S_OK; 597 } 598 599 *value = SysAllocString(base::UTF8ToUTF16(target->value()).c_str()); 600 DCHECK(*value); 601 return S_OK; 602 } 603 604 STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic(BSTR* help_file, 605 VARIANT var_id, 606 LONG* topic_id) { 607 return E_NOTIMPL; 608 } 609 610 STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) { 611 if (!instance_active()) 612 return E_FAIL; 613 614 if (GetRole() != ui::AX_ROLE_LIST_BOX) 615 return E_NOTIMPL; 616 617 unsigned long selected_count = 0; 618 for (size_t i = 0; i < InternalChildCount(); ++i) { 619 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) 620 ++selected_count; 621 } 622 623 if (selected_count == 0) { 624 selected->vt = VT_EMPTY; 625 return S_OK; 626 } 627 628 if (selected_count == 1) { 629 for (size_t i = 0; i < InternalChildCount(); ++i) { 630 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) { 631 selected->vt = VT_DISPATCH; 632 selected->pdispVal = 633 InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference(); 634 return S_OK; 635 } 636 } 637 } 638 639 // Multiple items are selected. 640 base::win::EnumVariant* enum_variant = 641 new base::win::EnumVariant(selected_count); 642 enum_variant->AddRef(); 643 unsigned long index = 0; 644 for (size_t i = 0; i < InternalChildCount(); ++i) { 645 if (InternalGetChild(i)->HasState(ui::AX_STATE_SELECTED)) { 646 enum_variant->ItemAt(index)->vt = VT_DISPATCH; 647 enum_variant->ItemAt(index)->pdispVal = 648 InternalGetChild(i)->ToBrowserAccessibilityWin()->NewReference(); 649 ++index; 650 } 651 } 652 selected->vt = VT_UNKNOWN; 653 selected->punkVal = static_cast<IUnknown*>( 654 static_cast<base::win::IUnknownImpl*>(enum_variant)); 655 return S_OK; 656 } 657 658 STDMETHODIMP BrowserAccessibilityWin::accSelect( 659 LONG flags_sel, VARIANT var_id) { 660 if (!instance_active()) 661 return E_FAIL; 662 663 if (flags_sel & SELFLAG_TAKEFOCUS) { 664 manager()->SetFocus(this, true); 665 return S_OK; 666 } 667 668 return S_FALSE; 669 } 670 671 // 672 // IAccessible2 methods. 673 // 674 675 STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) { 676 if (!instance_active()) 677 return E_FAIL; 678 679 if (!role) 680 return E_INVALIDARG; 681 682 *role = ia2_role_; 683 684 return S_OK; 685 } 686 687 STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) { 688 if (!instance_active()) 689 return E_FAIL; 690 691 if (!attributes) 692 return E_INVALIDARG; 693 694 // The iaccessible2 attributes are a set of key-value pairs 695 // separated by semicolons, with a colon between the key and the value. 696 base::string16 str; 697 for (unsigned int i = 0; i < ia2_attributes_.size(); ++i) { 698 if (i != 0) 699 str += L';'; 700 str += ia2_attributes_[i]; 701 } 702 703 if (str.empty()) 704 return S_FALSE; 705 706 *attributes = SysAllocString(str.c_str()); 707 DCHECK(*attributes); 708 return S_OK; 709 } 710 711 STDMETHODIMP BrowserAccessibilityWin::get_states(AccessibleStates* states) { 712 if (!instance_active()) 713 return E_FAIL; 714 715 if (!states) 716 return E_INVALIDARG; 717 718 *states = ia2_state_; 719 720 return S_OK; 721 } 722 723 STDMETHODIMP BrowserAccessibilityWin::get_uniqueID(LONG* unique_id) { 724 if (!instance_active()) 725 return E_FAIL; 726 727 if (!unique_id) 728 return E_INVALIDARG; 729 730 *unique_id = unique_id_win_; 731 return S_OK; 732 } 733 734 STDMETHODIMP BrowserAccessibilityWin::get_windowHandle(HWND* window_handle) { 735 if (!instance_active()) 736 return E_FAIL; 737 738 if (!window_handle) 739 return E_INVALIDARG; 740 741 *window_handle = manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd(); 742 if (!*window_handle) 743 return E_FAIL; 744 745 return S_OK; 746 } 747 748 STDMETHODIMP BrowserAccessibilityWin::get_indexInParent(LONG* index_in_parent) { 749 if (!instance_active()) 750 return E_FAIL; 751 752 if (!index_in_parent) 753 return E_INVALIDARG; 754 755 *index_in_parent = this->GetIndexInParent(); 756 return S_OK; 757 } 758 759 STDMETHODIMP BrowserAccessibilityWin::get_nRelations(LONG* n_relations) { 760 if (!instance_active()) 761 return E_FAIL; 762 763 if (!n_relations) 764 return E_INVALIDARG; 765 766 *n_relations = relations_.size(); 767 return S_OK; 768 } 769 770 STDMETHODIMP BrowserAccessibilityWin::get_relation( 771 LONG relation_index, 772 IAccessibleRelation** relation) { 773 if (!instance_active()) 774 return E_FAIL; 775 776 if (relation_index < 0 || 777 relation_index >= static_cast<long>(relations_.size())) { 778 return E_INVALIDARG; 779 } 780 781 if (!relation) 782 return E_INVALIDARG; 783 784 relations_[relation_index]->AddRef(); 785 *relation = relations_[relation_index]; 786 return S_OK; 787 } 788 789 STDMETHODIMP BrowserAccessibilityWin::get_relations( 790 LONG max_relations, 791 IAccessibleRelation** relations, 792 LONG* n_relations) { 793 if (!instance_active()) 794 return E_FAIL; 795 796 if (!relations || !n_relations) 797 return E_INVALIDARG; 798 799 long count = static_cast<long>(relations_.size()); 800 *n_relations = count; 801 if (count == 0) 802 return S_FALSE; 803 804 for (long i = 0; i < count; ++i) { 805 relations_[i]->AddRef(); 806 relations[i] = relations_[i]; 807 } 808 809 return S_OK; 810 } 811 812 STDMETHODIMP BrowserAccessibilityWin::scrollTo(enum IA2ScrollType scroll_type) { 813 if (!instance_active()) 814 return E_FAIL; 815 816 gfx::Rect r = GetLocation(); 817 switch(scroll_type) { 818 case IA2_SCROLL_TYPE_TOP_LEFT: 819 manager()->ScrollToMakeVisible(*this, gfx::Rect(r.x(), r.y(), 0, 0)); 820 break; 821 case IA2_SCROLL_TYPE_BOTTOM_RIGHT: 822 manager()->ScrollToMakeVisible( 823 *this, gfx::Rect(r.right(), r.bottom(), 0, 0)); 824 break; 825 case IA2_SCROLL_TYPE_TOP_EDGE: 826 manager()->ScrollToMakeVisible( 827 *this, gfx::Rect(r.x(), r.y(), r.width(), 0)); 828 break; 829 case IA2_SCROLL_TYPE_BOTTOM_EDGE: 830 manager()->ScrollToMakeVisible( 831 *this, gfx::Rect(r.x(), r.bottom(), r.width(), 0)); 832 break; 833 case IA2_SCROLL_TYPE_LEFT_EDGE: 834 manager()->ScrollToMakeVisible( 835 *this, gfx::Rect(r.x(), r.y(), 0, r.height())); 836 break; 837 case IA2_SCROLL_TYPE_RIGHT_EDGE: 838 manager()->ScrollToMakeVisible( 839 *this, gfx::Rect(r.right(), r.y(), 0, r.height())); 840 break; 841 case IA2_SCROLL_TYPE_ANYWHERE: 842 default: 843 manager()->ScrollToMakeVisible(*this, r); 844 break; 845 } 846 847 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this); 848 849 return S_OK; 850 } 851 852 STDMETHODIMP BrowserAccessibilityWin::scrollToPoint( 853 enum IA2CoordinateType coordinate_type, 854 LONG x, 855 LONG y) { 856 if (!instance_active()) 857 return E_FAIL; 858 859 gfx::Point scroll_to(x, y); 860 861 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) { 862 scroll_to -= manager()->GetViewBounds().OffsetFromOrigin(); 863 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) { 864 if (GetParent()) 865 scroll_to += GetParent()->GetLocation().OffsetFromOrigin(); 866 } else { 867 return E_INVALIDARG; 868 } 869 870 manager()->ScrollToPoint(*this, scroll_to); 871 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this); 872 873 return S_OK; 874 } 875 876 STDMETHODIMP BrowserAccessibilityWin::get_groupPosition( 877 LONG* group_level, 878 LONG* similar_items_in_group, 879 LONG* position_in_group) { 880 if (!instance_active()) 881 return E_FAIL; 882 883 if (!group_level || !similar_items_in_group || !position_in_group) 884 return E_INVALIDARG; 885 886 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION && 887 GetParent() && 888 GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) { 889 *group_level = 0; 890 *similar_items_in_group = GetParent()->PlatformChildCount(); 891 *position_in_group = GetIndexInParent() + 1; 892 return S_OK; 893 } 894 895 return E_NOTIMPL; 896 } 897 898 // 899 // IAccessibleApplication methods. 900 // 901 902 STDMETHODIMP BrowserAccessibilityWin::get_appName(BSTR* app_name) { 903 // No need to check |instance_active()| because this interface is 904 // global, and doesn't depend on any local state. 905 906 if (!app_name) 907 return E_INVALIDARG; 908 909 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out 910 // the part before the "/". 911 std::vector<std::string> product_components; 912 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components); 913 DCHECK_EQ(2U, product_components.size()); 914 if (product_components.size() != 2) 915 return E_FAIL; 916 *app_name = SysAllocString(base::UTF8ToUTF16(product_components[0]).c_str()); 917 DCHECK(*app_name); 918 return *app_name ? S_OK : E_FAIL; 919 } 920 921 STDMETHODIMP BrowserAccessibilityWin::get_appVersion(BSTR* app_version) { 922 // No need to check |instance_active()| because this interface is 923 // global, and doesn't depend on any local state. 924 925 if (!app_version) 926 return E_INVALIDARG; 927 928 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out 929 // the part after the "/". 930 std::vector<std::string> product_components; 931 base::SplitString(GetContentClient()->GetProduct(), '/', &product_components); 932 DCHECK_EQ(2U, product_components.size()); 933 if (product_components.size() != 2) 934 return E_FAIL; 935 *app_version = 936 SysAllocString(base::UTF8ToUTF16(product_components[1]).c_str()); 937 DCHECK(*app_version); 938 return *app_version ? S_OK : E_FAIL; 939 } 940 941 STDMETHODIMP BrowserAccessibilityWin::get_toolkitName(BSTR* toolkit_name) { 942 // No need to check |instance_active()| because this interface is 943 // global, and doesn't depend on any local state. 944 945 if (!toolkit_name) 946 return E_INVALIDARG; 947 948 // This is hard-coded; all products based on the Chromium engine 949 // will have the same toolkit name, so that assistive technology can 950 // detect any Chrome-based product. 951 *toolkit_name = SysAllocString(L"Chrome"); 952 DCHECK(*toolkit_name); 953 return *toolkit_name ? S_OK : E_FAIL; 954 } 955 956 STDMETHODIMP BrowserAccessibilityWin::get_toolkitVersion( 957 BSTR* toolkit_version) { 958 // No need to check |instance_active()| because this interface is 959 // global, and doesn't depend on any local state. 960 961 if (!toolkit_version) 962 return E_INVALIDARG; 963 964 std::string user_agent = GetContentClient()->GetUserAgent(); 965 *toolkit_version = SysAllocString(base::UTF8ToUTF16(user_agent).c_str()); 966 DCHECK(*toolkit_version); 967 return *toolkit_version ? S_OK : E_FAIL; 968 } 969 970 // 971 // IAccessibleImage methods. 972 // 973 974 STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) { 975 if (!instance_active()) 976 return E_FAIL; 977 978 if (!desc) 979 return E_INVALIDARG; 980 981 return GetStringAttributeAsBstr( 982 ui::AX_ATTR_DESCRIPTION, desc); 983 } 984 985 STDMETHODIMP BrowserAccessibilityWin::get_imagePosition( 986 enum IA2CoordinateType coordinate_type, 987 LONG* x, 988 LONG* y) { 989 if (!instance_active()) 990 return E_FAIL; 991 992 if (!x || !y) 993 return E_INVALIDARG; 994 995 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) { 996 HWND parent_hwnd = 997 manager()->ToBrowserAccessibilityManagerWin()->parent_hwnd(); 998 if (!parent_hwnd) 999 return E_FAIL; 1000 POINT top_left = {0, 0}; 1001 ::ClientToScreen(parent_hwnd, &top_left); 1002 *x = GetLocation().x() + top_left.x; 1003 *y = GetLocation().y() + top_left.y; 1004 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) { 1005 *x = GetLocation().x(); 1006 *y = GetLocation().y(); 1007 if (GetParent()) { 1008 *x -= GetParent()->GetLocation().x(); 1009 *y -= GetParent()->GetLocation().y(); 1010 } 1011 } else { 1012 return E_INVALIDARG; 1013 } 1014 1015 return S_OK; 1016 } 1017 1018 STDMETHODIMP BrowserAccessibilityWin::get_imageSize(LONG* height, LONG* width) { 1019 if (!instance_active()) 1020 return E_FAIL; 1021 1022 if (!height || !width) 1023 return E_INVALIDARG; 1024 1025 *height = GetLocation().height(); 1026 *width = GetLocation().width(); 1027 return S_OK; 1028 } 1029 1030 // 1031 // IAccessibleTable methods. 1032 // 1033 1034 STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt( 1035 long row, 1036 long column, 1037 IUnknown** accessible) { 1038 if (!instance_active()) 1039 return E_FAIL; 1040 1041 if (!accessible) 1042 return E_INVALIDARG; 1043 1044 int columns; 1045 int rows; 1046 if (!GetIntAttribute( 1047 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) || 1048 !GetIntAttribute( 1049 ui::AX_ATTR_TABLE_ROW_COUNT, &rows) || 1050 columns <= 0 || 1051 rows <= 0) { 1052 return S_FALSE; 1053 } 1054 1055 if (row < 0 || row >= rows || column < 0 || column >= columns) 1056 return E_INVALIDARG; 1057 1058 const std::vector<int32>& cell_ids = GetIntListAttribute( 1059 ui::AX_ATTR_CELL_IDS); 1060 DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size())); 1061 1062 int cell_id = cell_ids[row * columns + column]; 1063 BrowserAccessibilityWin* cell = GetFromID(cell_id); 1064 if (cell) { 1065 *accessible = static_cast<IAccessible*>(cell->NewReference()); 1066 return S_OK; 1067 } 1068 1069 *accessible = NULL; 1070 return E_INVALIDARG; 1071 } 1072 1073 STDMETHODIMP BrowserAccessibilityWin::get_caption(IUnknown** accessible) { 1074 if (!instance_active()) 1075 return E_FAIL; 1076 1077 if (!accessible) 1078 return E_INVALIDARG; 1079 1080 // TODO(dmazzoni): implement 1081 return S_FALSE; 1082 } 1083 1084 STDMETHODIMP BrowserAccessibilityWin::get_childIndex(long row, 1085 long column, 1086 long* cell_index) { 1087 if (!instance_active()) 1088 return E_FAIL; 1089 1090 if (!cell_index) 1091 return E_INVALIDARG; 1092 1093 int columns; 1094 int rows; 1095 if (!GetIntAttribute( 1096 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) || 1097 !GetIntAttribute( 1098 ui::AX_ATTR_TABLE_ROW_COUNT, &rows) || 1099 columns <= 0 || 1100 rows <= 0) { 1101 return S_FALSE; 1102 } 1103 1104 if (row < 0 || row >= rows || column < 0 || column >= columns) 1105 return E_INVALIDARG; 1106 1107 const std::vector<int32>& cell_ids = GetIntListAttribute( 1108 ui::AX_ATTR_CELL_IDS); 1109 const std::vector<int32>& unique_cell_ids = GetIntListAttribute( 1110 ui::AX_ATTR_UNIQUE_CELL_IDS); 1111 DCHECK_EQ(columns * rows, static_cast<int>(cell_ids.size())); 1112 int cell_id = cell_ids[row * columns + column]; 1113 for (size_t i = 0; i < unique_cell_ids.size(); ++i) { 1114 if (unique_cell_ids[i] == cell_id) { 1115 *cell_index = (long)i; 1116 return S_OK; 1117 } 1118 } 1119 1120 return S_FALSE; 1121 } 1122 1123 STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column, 1124 BSTR* description) { 1125 if (!instance_active()) 1126 return E_FAIL; 1127 1128 if (!description) 1129 return E_INVALIDARG; 1130 1131 int columns; 1132 int rows; 1133 if (!GetIntAttribute( 1134 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) || 1135 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) || 1136 columns <= 0 || 1137 rows <= 0) { 1138 return S_FALSE; 1139 } 1140 1141 if (column < 0 || column >= columns) 1142 return E_INVALIDARG; 1143 1144 const std::vector<int32>& cell_ids = GetIntListAttribute( 1145 ui::AX_ATTR_CELL_IDS); 1146 for (int i = 0; i < rows; ++i) { 1147 int cell_id = cell_ids[i * columns + column]; 1148 BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>( 1149 manager()->GetFromID(cell_id)); 1150 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) { 1151 base::string16 cell_name = cell->GetString16Attribute( 1152 ui::AX_ATTR_NAME); 1153 if (cell_name.size() > 0) { 1154 *description = SysAllocString(cell_name.c_str()); 1155 return S_OK; 1156 } 1157 1158 return cell->GetStringAttributeAsBstr( 1159 ui::AX_ATTR_DESCRIPTION, description); 1160 } 1161 } 1162 1163 return S_FALSE; 1164 } 1165 1166 STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt( 1167 long row, 1168 long column, 1169 long* n_columns_spanned) { 1170 if (!instance_active()) 1171 return E_FAIL; 1172 1173 if (!n_columns_spanned) 1174 return E_INVALIDARG; 1175 1176 int columns; 1177 int rows; 1178 if (!GetIntAttribute( 1179 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) || 1180 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) || 1181 columns <= 0 || 1182 rows <= 0) { 1183 return S_FALSE; 1184 } 1185 1186 if (row < 0 || row >= rows || column < 0 || column >= columns) 1187 return E_INVALIDARG; 1188 1189 const std::vector<int32>& cell_ids = GetIntListAttribute( 1190 ui::AX_ATTR_CELL_IDS); 1191 int cell_id = cell_ids[row * columns + column]; 1192 BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>( 1193 manager()->GetFromID(cell_id)); 1194 int colspan; 1195 if (cell && 1196 cell->GetIntAttribute( 1197 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) && 1198 colspan >= 1) { 1199 *n_columns_spanned = colspan; 1200 return S_OK; 1201 } 1202 1203 return S_FALSE; 1204 } 1205 1206 STDMETHODIMP BrowserAccessibilityWin::get_columnHeader( 1207 IAccessibleTable** accessible_table, 1208 long* starting_row_index) { 1209 // TODO(dmazzoni): implement 1210 return E_NOTIMPL; 1211 } 1212 1213 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index, 1214 long* column_index) { 1215 if (!instance_active()) 1216 return E_FAIL; 1217 1218 if (!column_index) 1219 return E_INVALIDARG; 1220 1221 const std::vector<int32>& unique_cell_ids = GetIntListAttribute( 1222 ui::AX_ATTR_UNIQUE_CELL_IDS); 1223 int cell_id_count = static_cast<int>(unique_cell_ids.size()); 1224 if (cell_index < 0) 1225 return E_INVALIDARG; 1226 if (cell_index >= cell_id_count) 1227 return S_FALSE; 1228 1229 int cell_id = unique_cell_ids[cell_index]; 1230 BrowserAccessibilityWin* cell = 1231 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin(); 1232 int col_index; 1233 if (cell && 1234 cell->GetIntAttribute( 1235 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &col_index)) { 1236 *column_index = col_index; 1237 return S_OK; 1238 } 1239 1240 return S_FALSE; 1241 } 1242 1243 STDMETHODIMP BrowserAccessibilityWin::get_nColumns(long* column_count) { 1244 if (!instance_active()) 1245 return E_FAIL; 1246 1247 if (!column_count) 1248 return E_INVALIDARG; 1249 1250 int columns; 1251 if (GetIntAttribute( 1252 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns)) { 1253 *column_count = columns; 1254 return S_OK; 1255 } 1256 1257 return S_FALSE; 1258 } 1259 1260 STDMETHODIMP BrowserAccessibilityWin::get_nRows(long* row_count) { 1261 if (!instance_active()) 1262 return E_FAIL; 1263 1264 if (!row_count) 1265 return E_INVALIDARG; 1266 1267 int rows; 1268 if (GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) { 1269 *row_count = rows; 1270 return S_OK; 1271 } 1272 1273 return S_FALSE; 1274 } 1275 1276 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedChildren(long* cell_count) { 1277 if (!instance_active()) 1278 return E_FAIL; 1279 1280 if (!cell_count) 1281 return E_INVALIDARG; 1282 1283 // TODO(dmazzoni): add support for selected cells/rows/columns in tables. 1284 *cell_count = 0; 1285 return S_OK; 1286 } 1287 1288 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedColumns(long* column_count) { 1289 if (!instance_active()) 1290 return E_FAIL; 1291 1292 if (!column_count) 1293 return E_INVALIDARG; 1294 1295 *column_count = 0; 1296 return S_OK; 1297 } 1298 1299 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedRows(long* row_count) { 1300 if (!instance_active()) 1301 return E_FAIL; 1302 1303 if (!row_count) 1304 return E_INVALIDARG; 1305 1306 *row_count = 0; 1307 return S_OK; 1308 } 1309 1310 STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row, 1311 BSTR* description) { 1312 if (!instance_active()) 1313 return E_FAIL; 1314 1315 if (!description) 1316 return E_INVALIDARG; 1317 1318 int columns; 1319 int rows; 1320 if (!GetIntAttribute( 1321 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) || 1322 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) || 1323 columns <= 0 || 1324 rows <= 0) { 1325 return S_FALSE; 1326 } 1327 1328 if (row < 0 || row >= rows) 1329 return E_INVALIDARG; 1330 1331 const std::vector<int32>& cell_ids = GetIntListAttribute( 1332 ui::AX_ATTR_CELL_IDS); 1333 for (int i = 0; i < columns; ++i) { 1334 int cell_id = cell_ids[row * columns + i]; 1335 BrowserAccessibilityWin* cell = 1336 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin(); 1337 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) { 1338 base::string16 cell_name = cell->GetString16Attribute( 1339 ui::AX_ATTR_NAME); 1340 if (cell_name.size() > 0) { 1341 *description = SysAllocString(cell_name.c_str()); 1342 return S_OK; 1343 } 1344 1345 return cell->GetStringAttributeAsBstr( 1346 ui::AX_ATTR_DESCRIPTION, description); 1347 } 1348 } 1349 1350 return S_FALSE; 1351 } 1352 1353 STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row, 1354 long column, 1355 long* n_rows_spanned) { 1356 if (!instance_active()) 1357 return E_FAIL; 1358 1359 if (!n_rows_spanned) 1360 return E_INVALIDARG; 1361 1362 int columns; 1363 int rows; 1364 if (!GetIntAttribute( 1365 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) || 1366 !GetIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, &rows) || 1367 columns <= 0 || 1368 rows <= 0) { 1369 return S_FALSE; 1370 } 1371 1372 if (row < 0 || row >= rows || column < 0 || column >= columns) 1373 return E_INVALIDARG; 1374 1375 const std::vector<int32>& cell_ids = GetIntListAttribute( 1376 ui::AX_ATTR_CELL_IDS); 1377 int cell_id = cell_ids[row * columns + column]; 1378 BrowserAccessibilityWin* cell = 1379 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin(); 1380 int rowspan; 1381 if (cell && 1382 cell->GetIntAttribute( 1383 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) && 1384 rowspan >= 1) { 1385 *n_rows_spanned = rowspan; 1386 return S_OK; 1387 } 1388 1389 return S_FALSE; 1390 } 1391 1392 STDMETHODIMP BrowserAccessibilityWin::get_rowHeader( 1393 IAccessibleTable** accessible_table, 1394 long* starting_column_index) { 1395 // TODO(dmazzoni): implement 1396 return E_NOTIMPL; 1397 } 1398 1399 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index, 1400 long* row_index) { 1401 if (!instance_active()) 1402 return E_FAIL; 1403 1404 if (!row_index) 1405 return E_INVALIDARG; 1406 1407 const std::vector<int32>& unique_cell_ids = GetIntListAttribute( 1408 ui::AX_ATTR_UNIQUE_CELL_IDS); 1409 int cell_id_count = static_cast<int>(unique_cell_ids.size()); 1410 if (cell_index < 0) 1411 return E_INVALIDARG; 1412 if (cell_index >= cell_id_count) 1413 return S_FALSE; 1414 1415 int cell_id = unique_cell_ids[cell_index]; 1416 BrowserAccessibilityWin* cell = 1417 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin(); 1418 int cell_row_index; 1419 if (cell && 1420 cell->GetIntAttribute( 1421 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &cell_row_index)) { 1422 *row_index = cell_row_index; 1423 return S_OK; 1424 } 1425 1426 return S_FALSE; 1427 } 1428 1429 STDMETHODIMP BrowserAccessibilityWin::get_selectedChildren(long max_children, 1430 long** children, 1431 long* n_children) { 1432 if (!instance_active()) 1433 return E_FAIL; 1434 1435 if (!children || !n_children) 1436 return E_INVALIDARG; 1437 1438 // TODO(dmazzoni): Implement this. 1439 *n_children = 0; 1440 return S_OK; 1441 } 1442 1443 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long max_columns, 1444 long** columns, 1445 long* n_columns) { 1446 if (!instance_active()) 1447 return E_FAIL; 1448 1449 if (!columns || !n_columns) 1450 return E_INVALIDARG; 1451 1452 // TODO(dmazzoni): Implement this. 1453 *n_columns = 0; 1454 return S_OK; 1455 } 1456 1457 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long max_rows, 1458 long** rows, 1459 long* n_rows) { 1460 if (!instance_active()) 1461 return E_FAIL; 1462 1463 if (!rows || !n_rows) 1464 return E_INVALIDARG; 1465 1466 // TODO(dmazzoni): Implement this. 1467 *n_rows = 0; 1468 return S_OK; 1469 } 1470 1471 STDMETHODIMP BrowserAccessibilityWin::get_summary(IUnknown** accessible) { 1472 if (!instance_active()) 1473 return E_FAIL; 1474 1475 if (!accessible) 1476 return E_INVALIDARG; 1477 1478 // TODO(dmazzoni): implement 1479 return S_FALSE; 1480 } 1481 1482 STDMETHODIMP BrowserAccessibilityWin::get_isColumnSelected( 1483 long column, 1484 boolean* is_selected) { 1485 if (!instance_active()) 1486 return E_FAIL; 1487 1488 if (!is_selected) 1489 return E_INVALIDARG; 1490 1491 // TODO(dmazzoni): Implement this. 1492 *is_selected = false; 1493 return S_OK; 1494 } 1495 1496 STDMETHODIMP BrowserAccessibilityWin::get_isRowSelected(long row, 1497 boolean* is_selected) { 1498 if (!instance_active()) 1499 return E_FAIL; 1500 1501 if (!is_selected) 1502 return E_INVALIDARG; 1503 1504 // TODO(dmazzoni): Implement this. 1505 *is_selected = false; 1506 return S_OK; 1507 } 1508 1509 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(long row, 1510 long column, 1511 boolean* is_selected) { 1512 if (!instance_active()) 1513 return E_FAIL; 1514 1515 if (!is_selected) 1516 return E_INVALIDARG; 1517 1518 // TODO(dmazzoni): Implement this. 1519 *is_selected = false; 1520 return S_OK; 1521 } 1522 1523 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex( 1524 long index, 1525 long* row, 1526 long* column, 1527 long* row_extents, 1528 long* column_extents, 1529 boolean* is_selected) { 1530 if (!instance_active()) 1531 return E_FAIL; 1532 1533 if (!row || !column || !row_extents || !column_extents || !is_selected) 1534 return E_INVALIDARG; 1535 1536 const std::vector<int32>& unique_cell_ids = GetIntListAttribute( 1537 ui::AX_ATTR_UNIQUE_CELL_IDS); 1538 int cell_id_count = static_cast<int>(unique_cell_ids.size()); 1539 if (index < 0) 1540 return E_INVALIDARG; 1541 if (index >= cell_id_count) 1542 return S_FALSE; 1543 1544 int cell_id = unique_cell_ids[index]; 1545 BrowserAccessibilityWin* cell = 1546 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin(); 1547 int rowspan; 1548 int colspan; 1549 if (cell && 1550 cell->GetIntAttribute( 1551 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) && 1552 cell->GetIntAttribute( 1553 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) && 1554 rowspan >= 1 && 1555 colspan >= 1) { 1556 *row_extents = rowspan; 1557 *column_extents = colspan; 1558 return S_OK; 1559 } 1560 1561 return S_FALSE; 1562 } 1563 1564 // 1565 // IAccessibleTable2 methods. 1566 // 1567 1568 STDMETHODIMP BrowserAccessibilityWin::get_cellAt(long row, 1569 long column, 1570 IUnknown** cell) { 1571 return get_accessibleAt(row, column, cell); 1572 } 1573 1574 STDMETHODIMP BrowserAccessibilityWin::get_nSelectedCells(long* cell_count) { 1575 return get_nSelectedChildren(cell_count); 1576 } 1577 1578 STDMETHODIMP BrowserAccessibilityWin::get_selectedCells( 1579 IUnknown*** cells, 1580 long* n_selected_cells) { 1581 if (!instance_active()) 1582 return E_FAIL; 1583 1584 if (!cells || !n_selected_cells) 1585 return E_INVALIDARG; 1586 1587 // TODO(dmazzoni): Implement this. 1588 *n_selected_cells = 0; 1589 return S_OK; 1590 } 1591 1592 STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns(long** columns, 1593 long* n_columns) { 1594 if (!instance_active()) 1595 return E_FAIL; 1596 1597 if (!columns || !n_columns) 1598 return E_INVALIDARG; 1599 1600 // TODO(dmazzoni): Implement this. 1601 *n_columns = 0; 1602 return S_OK; 1603 } 1604 1605 STDMETHODIMP BrowserAccessibilityWin::get_selectedRows(long** rows, 1606 long* n_rows) { 1607 if (!instance_active()) 1608 return E_FAIL; 1609 1610 if (!rows || !n_rows) 1611 return E_INVALIDARG; 1612 1613 // TODO(dmazzoni): Implement this. 1614 *n_rows = 0; 1615 return S_OK; 1616 } 1617 1618 1619 // 1620 // IAccessibleTableCell methods. 1621 // 1622 1623 STDMETHODIMP BrowserAccessibilityWin::get_columnExtent( 1624 long* n_columns_spanned) { 1625 if (!instance_active()) 1626 return E_FAIL; 1627 1628 if (!n_columns_spanned) 1629 return E_INVALIDARG; 1630 1631 int colspan; 1632 if (GetIntAttribute( 1633 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan) && 1634 colspan >= 1) { 1635 *n_columns_spanned = colspan; 1636 return S_OK; 1637 } 1638 1639 return S_FALSE; 1640 } 1641 1642 STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells( 1643 IUnknown*** cell_accessibles, 1644 long* n_column_header_cells) { 1645 if (!instance_active()) 1646 return E_FAIL; 1647 1648 if (!cell_accessibles || !n_column_header_cells) 1649 return E_INVALIDARG; 1650 1651 *n_column_header_cells = 0; 1652 1653 int column; 1654 if (!GetIntAttribute( 1655 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) { 1656 return S_FALSE; 1657 } 1658 1659 BrowserAccessibility* table = GetParent(); 1660 while (table && table->GetRole() != ui::AX_ROLE_TABLE) 1661 table = table->GetParent(); 1662 if (!table) { 1663 NOTREACHED(); 1664 return S_FALSE; 1665 } 1666 1667 int columns; 1668 int rows; 1669 if (!table->GetIntAttribute( 1670 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) || 1671 !table->GetIntAttribute( 1672 ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) { 1673 return S_FALSE; 1674 } 1675 if (columns <= 0 || rows <= 0 || column < 0 || column >= columns) 1676 return S_FALSE; 1677 1678 const std::vector<int32>& cell_ids = table->GetIntListAttribute( 1679 ui::AX_ATTR_CELL_IDS); 1680 1681 for (int i = 0; i < rows; ++i) { 1682 int cell_id = cell_ids[i * columns + column]; 1683 BrowserAccessibilityWin* cell = 1684 manager()->GetFromID(cell_id)->ToBrowserAccessibilityWin(); 1685 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) 1686 (*n_column_header_cells)++; 1687 } 1688 1689 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc( 1690 (*n_column_header_cells) * sizeof(cell_accessibles[0]))); 1691 int index = 0; 1692 for (int i = 0; i < rows; ++i) { 1693 int cell_id = cell_ids[i * columns + column]; 1694 BrowserAccessibility* cell = manager()->GetFromID(cell_id); 1695 if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) { 1696 (*cell_accessibles)[index] = static_cast<IAccessible*>( 1697 cell->ToBrowserAccessibilityWin()->NewReference()); 1698 ++index; 1699 } 1700 } 1701 1702 return S_OK; 1703 } 1704 1705 STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long* column_index) { 1706 if (!instance_active()) 1707 return E_FAIL; 1708 1709 if (!column_index) 1710 return E_INVALIDARG; 1711 1712 int column; 1713 if (GetIntAttribute( 1714 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column)) { 1715 *column_index = column; 1716 return S_OK; 1717 } 1718 1719 return S_FALSE; 1720 } 1721 1722 STDMETHODIMP BrowserAccessibilityWin::get_rowExtent(long* n_rows_spanned) { 1723 if (!instance_active()) 1724 return E_FAIL; 1725 1726 if (!n_rows_spanned) 1727 return E_INVALIDARG; 1728 1729 int rowspan; 1730 if (GetIntAttribute( 1731 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) && 1732 rowspan >= 1) { 1733 *n_rows_spanned = rowspan; 1734 return S_OK; 1735 } 1736 1737 return S_FALSE; 1738 } 1739 1740 STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells( 1741 IUnknown*** cell_accessibles, 1742 long* n_row_header_cells) { 1743 if (!instance_active()) 1744 return E_FAIL; 1745 1746 if (!cell_accessibles || !n_row_header_cells) 1747 return E_INVALIDARG; 1748 1749 *n_row_header_cells = 0; 1750 1751 int row; 1752 if (!GetIntAttribute( 1753 ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) { 1754 return S_FALSE; 1755 } 1756 1757 BrowserAccessibility* table = GetParent(); 1758 while (table && table->GetRole() != ui::AX_ROLE_TABLE) 1759 table = table->GetParent(); 1760 if (!table) { 1761 NOTREACHED(); 1762 return S_FALSE; 1763 } 1764 1765 int columns; 1766 int rows; 1767 if (!table->GetIntAttribute( 1768 ui::AX_ATTR_TABLE_COLUMN_COUNT, &columns) || 1769 !table->GetIntAttribute( 1770 ui::AX_ATTR_TABLE_ROW_COUNT, &rows)) { 1771 return S_FALSE; 1772 } 1773 if (columns <= 0 || rows <= 0 || row < 0 || row >= rows) 1774 return S_FALSE; 1775 1776 const std::vector<int32>& cell_ids = table->GetIntListAttribute( 1777 ui::AX_ATTR_CELL_IDS); 1778 1779 for (int i = 0; i < columns; ++i) { 1780 int cell_id = cell_ids[row * columns + i]; 1781 BrowserAccessibility* cell = manager()->GetFromID(cell_id); 1782 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) 1783 (*n_row_header_cells)++; 1784 } 1785 1786 *cell_accessibles = static_cast<IUnknown**>(CoTaskMemAlloc( 1787 (*n_row_header_cells) * sizeof(cell_accessibles[0]))); 1788 int index = 0; 1789 for (int i = 0; i < columns; ++i) { 1790 int cell_id = cell_ids[row * columns + i]; 1791 BrowserAccessibility* cell = manager()->GetFromID(cell_id); 1792 if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) { 1793 (*cell_accessibles)[index] = static_cast<IAccessible*>( 1794 cell->ToBrowserAccessibilityWin()->NewReference()); 1795 ++index; 1796 } 1797 } 1798 1799 return S_OK; 1800 } 1801 1802 STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long* row_index) { 1803 if (!instance_active()) 1804 return E_FAIL; 1805 1806 if (!row_index) 1807 return E_INVALIDARG; 1808 1809 int row; 1810 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row)) { 1811 *row_index = row; 1812 return S_OK; 1813 } 1814 return S_FALSE; 1815 } 1816 1817 STDMETHODIMP BrowserAccessibilityWin::get_isSelected(boolean* is_selected) { 1818 if (!instance_active()) 1819 return E_FAIL; 1820 1821 if (!is_selected) 1822 return E_INVALIDARG; 1823 1824 *is_selected = false; 1825 return S_OK; 1826 } 1827 1828 STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtents( 1829 long* row_index, 1830 long* column_index, 1831 long* row_extents, 1832 long* column_extents, 1833 boolean* is_selected) { 1834 if (!instance_active()) 1835 return E_FAIL; 1836 1837 if (!row_index || 1838 !column_index || 1839 !row_extents || 1840 !column_extents || 1841 !is_selected) { 1842 return E_INVALIDARG; 1843 } 1844 1845 int row; 1846 int column; 1847 int rowspan; 1848 int colspan; 1849 if (GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row) && 1850 GetIntAttribute( 1851 ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column) && 1852 GetIntAttribute( 1853 ui::AX_ATTR_TABLE_CELL_ROW_SPAN, &rowspan) && 1854 GetIntAttribute( 1855 ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, &colspan)) { 1856 *row_index = row; 1857 *column_index = column; 1858 *row_extents = rowspan; 1859 *column_extents = colspan; 1860 *is_selected = false; 1861 return S_OK; 1862 } 1863 1864 return S_FALSE; 1865 } 1866 1867 STDMETHODIMP BrowserAccessibilityWin::get_table(IUnknown** table) { 1868 if (!instance_active()) 1869 return E_FAIL; 1870 1871 if (!table) 1872 return E_INVALIDARG; 1873 1874 1875 int row; 1876 int column; 1877 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, &row); 1878 GetIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, &column); 1879 1880 BrowserAccessibility* find_table = GetParent(); 1881 while (find_table && find_table->GetRole() != ui::AX_ROLE_TABLE) 1882 find_table = find_table->GetParent(); 1883 if (!find_table) { 1884 NOTREACHED(); 1885 return S_FALSE; 1886 } 1887 1888 *table = static_cast<IAccessibleTable*>( 1889 find_table->ToBrowserAccessibilityWin()->NewReference()); 1890 1891 return S_OK; 1892 } 1893 1894 // 1895 // IAccessibleText methods. 1896 // 1897 1898 STDMETHODIMP BrowserAccessibilityWin::get_nCharacters(LONG* n_characters) { 1899 if (!instance_active()) 1900 return E_FAIL; 1901 1902 if (!n_characters) 1903 return E_INVALIDARG; 1904 1905 *n_characters = TextForIAccessibleText().length(); 1906 return S_OK; 1907 } 1908 1909 STDMETHODIMP BrowserAccessibilityWin::get_caretOffset(LONG* offset) { 1910 if (!instance_active()) 1911 return E_FAIL; 1912 1913 if (!offset) 1914 return E_INVALIDARG; 1915 1916 *offset = 0; 1917 if (GetRole() == ui::AX_ROLE_TEXT_FIELD || 1918 GetRole() == ui::AX_ROLE_TEXT_AREA) { 1919 int sel_start = 0; 1920 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, 1921 &sel_start)) 1922 *offset = sel_start; 1923 } 1924 1925 return S_OK; 1926 } 1927 1928 STDMETHODIMP BrowserAccessibilityWin::get_characterExtents( 1929 LONG offset, 1930 enum IA2CoordinateType coordinate_type, 1931 LONG* out_x, 1932 LONG* out_y, 1933 LONG* out_width, 1934 LONG* out_height) { 1935 if (!instance_active()) 1936 return E_FAIL; 1937 1938 if (!out_x || !out_y || !out_width || !out_height) 1939 return E_INVALIDARG; 1940 1941 const base::string16& text_str = TextForIAccessibleText(); 1942 HandleSpecialTextOffset(text_str, &offset); 1943 1944 if (offset < 0 || offset > static_cast<LONG>(text_str.size())) 1945 return E_INVALIDARG; 1946 1947 gfx::Rect character_bounds; 1948 if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) { 1949 character_bounds = GetGlobalBoundsForRange(offset, 1); 1950 } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) { 1951 character_bounds = GetLocalBoundsForRange(offset, 1); 1952 character_bounds -= GetLocation().OffsetFromOrigin(); 1953 } else { 1954 return E_INVALIDARG; 1955 } 1956 1957 *out_x = character_bounds.x(); 1958 *out_y = character_bounds.y(); 1959 *out_width = character_bounds.width(); 1960 *out_height = character_bounds.height(); 1961 1962 return S_OK; 1963 } 1964 1965 STDMETHODIMP BrowserAccessibilityWin::get_nSelections(LONG* n_selections) { 1966 if (!instance_active()) 1967 return E_FAIL; 1968 1969 if (!n_selections) 1970 return E_INVALIDARG; 1971 1972 *n_selections = 0; 1973 if (GetRole() == ui::AX_ROLE_TEXT_FIELD || 1974 GetRole() == ui::AX_ROLE_TEXT_AREA) { 1975 int sel_start = 0; 1976 int sel_end = 0; 1977 if (GetIntAttribute(ui::AX_ATTR_TEXT_SEL_START, 1978 &sel_start) && 1979 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end) && 1980 sel_start != sel_end) 1981 *n_selections = 1; 1982 } 1983 1984 return S_OK; 1985 } 1986 1987 STDMETHODIMP BrowserAccessibilityWin::get_selection(LONG selection_index, 1988 LONG* start_offset, 1989 LONG* end_offset) { 1990 if (!instance_active()) 1991 return E_FAIL; 1992 1993 if (!start_offset || !end_offset || selection_index != 0) 1994 return E_INVALIDARG; 1995 1996 *start_offset = 0; 1997 *end_offset = 0; 1998 if (GetRole() == ui::AX_ROLE_TEXT_FIELD || 1999 GetRole() == ui::AX_ROLE_TEXT_AREA) { 2000 int sel_start = 0; 2001 int sel_end = 0; 2002 if (GetIntAttribute( 2003 ui::AX_ATTR_TEXT_SEL_START, &sel_start) && 2004 GetIntAttribute(ui::AX_ATTR_TEXT_SEL_END, &sel_end)) { 2005 *start_offset = sel_start; 2006 *end_offset = sel_end; 2007 } 2008 } 2009 2010 return S_OK; 2011 } 2012 2013 STDMETHODIMP BrowserAccessibilityWin::get_text(LONG start_offset, 2014 LONG end_offset, 2015 BSTR* text) { 2016 if (!instance_active()) 2017 return E_FAIL; 2018 2019 if (!text) 2020 return E_INVALIDARG; 2021 2022 const base::string16& text_str = TextForIAccessibleText(); 2023 2024 // Handle special text offsets. 2025 HandleSpecialTextOffset(text_str, &start_offset); 2026 HandleSpecialTextOffset(text_str, &end_offset); 2027 2028 // The spec allows the arguments to be reversed. 2029 if (start_offset > end_offset) { 2030 LONG tmp = start_offset; 2031 start_offset = end_offset; 2032 end_offset = tmp; 2033 } 2034 2035 // The spec does not allow the start or end offsets to be out or range; 2036 // we must return an error if so. 2037 LONG len = text_str.length(); 2038 if (start_offset < 0) 2039 return E_INVALIDARG; 2040 if (end_offset > len) 2041 return E_INVALIDARG; 2042 2043 base::string16 substr = text_str.substr(start_offset, 2044 end_offset - start_offset); 2045 if (substr.empty()) 2046 return S_FALSE; 2047 2048 *text = SysAllocString(substr.c_str()); 2049 DCHECK(*text); 2050 return S_OK; 2051 } 2052 2053 STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset( 2054 LONG offset, 2055 enum IA2TextBoundaryType boundary_type, 2056 LONG* start_offset, 2057 LONG* end_offset, 2058 BSTR* text) { 2059 if (!instance_active()) 2060 return E_FAIL; 2061 2062 if (!start_offset || !end_offset || !text) 2063 return E_INVALIDARG; 2064 2065 // The IAccessible2 spec says we don't have to implement the "sentence" 2066 // boundary type, we can just let the screenreader handle it. 2067 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { 2068 *start_offset = 0; 2069 *end_offset = 0; 2070 *text = NULL; 2071 return S_FALSE; 2072 } 2073 2074 const base::string16& text_str = TextForIAccessibleText(); 2075 2076 *start_offset = FindBoundary( 2077 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); 2078 *end_offset = FindBoundary( 2079 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION); 2080 return get_text(*start_offset, *end_offset, text); 2081 } 2082 2083 STDMETHODIMP BrowserAccessibilityWin::get_textBeforeOffset( 2084 LONG offset, 2085 enum IA2TextBoundaryType boundary_type, 2086 LONG* start_offset, 2087 LONG* end_offset, 2088 BSTR* text) { 2089 if (!instance_active()) 2090 return E_FAIL; 2091 2092 if (!start_offset || !end_offset || !text) 2093 return E_INVALIDARG; 2094 2095 // The IAccessible2 spec says we don't have to implement the "sentence" 2096 // boundary type, we can just let the screenreader handle it. 2097 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { 2098 *start_offset = 0; 2099 *end_offset = 0; 2100 *text = NULL; 2101 return S_FALSE; 2102 } 2103 2104 const base::string16& text_str = TextForIAccessibleText(); 2105 2106 *start_offset = FindBoundary( 2107 text_str, boundary_type, offset, ui::BACKWARDS_DIRECTION); 2108 *end_offset = offset; 2109 return get_text(*start_offset, *end_offset, text); 2110 } 2111 2112 STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset( 2113 LONG offset, 2114 enum IA2TextBoundaryType boundary_type, 2115 LONG* start_offset, 2116 LONG* end_offset, 2117 BSTR* text) { 2118 if (!instance_active()) 2119 return E_FAIL; 2120 2121 if (!start_offset || !end_offset || !text) 2122 return E_INVALIDARG; 2123 2124 // The IAccessible2 spec says we don't have to implement the "sentence" 2125 // boundary type, we can just let the screenreader handle it. 2126 if (boundary_type == IA2_TEXT_BOUNDARY_SENTENCE) { 2127 *start_offset = 0; 2128 *end_offset = 0; 2129 *text = NULL; 2130 return S_FALSE; 2131 } 2132 2133 const base::string16& text_str = TextForIAccessibleText(); 2134 2135 *start_offset = offset; 2136 *end_offset = FindBoundary( 2137 text_str, boundary_type, offset, ui::FORWARDS_DIRECTION); 2138 return get_text(*start_offset, *end_offset, text); 2139 } 2140 2141 STDMETHODIMP BrowserAccessibilityWin::get_newText(IA2TextSegment* new_text) { 2142 if (!instance_active()) 2143 return E_FAIL; 2144 2145 if (!new_text) 2146 return E_INVALIDARG; 2147 2148 base::string16 text = TextForIAccessibleText(); 2149 2150 new_text->text = SysAllocString(text.c_str()); 2151 new_text->start = 0; 2152 new_text->end = static_cast<long>(text.size()); 2153 return S_OK; 2154 } 2155 2156 STDMETHODIMP BrowserAccessibilityWin::get_oldText(IA2TextSegment* old_text) { 2157 if (!instance_active()) 2158 return E_FAIL; 2159 2160 if (!old_text) 2161 return E_INVALIDARG; 2162 2163 old_text->text = SysAllocString(old_text_.c_str()); 2164 old_text->start = 0; 2165 old_text->end = static_cast<long>(old_text_.size()); 2166 return S_OK; 2167 } 2168 2169 STDMETHODIMP BrowserAccessibilityWin::get_offsetAtPoint( 2170 LONG x, 2171 LONG y, 2172 enum IA2CoordinateType coord_type, 2173 LONG* offset) { 2174 if (!instance_active()) 2175 return E_FAIL; 2176 2177 if (!offset) 2178 return E_INVALIDARG; 2179 2180 // TODO(dmazzoni): implement this. We're returning S_OK for now so that 2181 // screen readers still return partially accurate results rather than 2182 // completely failing. 2183 *offset = 0; 2184 return S_OK; 2185 } 2186 2187 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringTo( 2188 LONG start_index, 2189 LONG end_index, 2190 enum IA2ScrollType scroll_type) { 2191 // TODO(dmazzoni): adjust this for the start and end index, too. 2192 return scrollTo(scroll_type); 2193 } 2194 2195 STDMETHODIMP BrowserAccessibilityWin::scrollSubstringToPoint( 2196 LONG start_index, 2197 LONG end_index, 2198 enum IA2CoordinateType coordinate_type, 2199 LONG x, LONG y) { 2200 // TODO(dmazzoni): adjust this for the start and end index, too. 2201 return scrollToPoint(coordinate_type, x, y); 2202 } 2203 2204 STDMETHODIMP BrowserAccessibilityWin::addSelection(LONG start_offset, 2205 LONG end_offset) { 2206 if (!instance_active()) 2207 return E_FAIL; 2208 2209 const base::string16& text_str = TextForIAccessibleText(); 2210 HandleSpecialTextOffset(text_str, &start_offset); 2211 HandleSpecialTextOffset(text_str, &end_offset); 2212 2213 manager()->SetTextSelection(*this, start_offset, end_offset); 2214 return S_OK; 2215 } 2216 2217 STDMETHODIMP BrowserAccessibilityWin::removeSelection(LONG selection_index) { 2218 if (!instance_active()) 2219 return E_FAIL; 2220 2221 if (selection_index != 0) 2222 return E_INVALIDARG; 2223 2224 manager()->SetTextSelection(*this, 0, 0); 2225 return S_OK; 2226 } 2227 2228 STDMETHODIMP BrowserAccessibilityWin::setCaretOffset(LONG offset) { 2229 if (!instance_active()) 2230 return E_FAIL; 2231 2232 const base::string16& text_str = TextForIAccessibleText(); 2233 HandleSpecialTextOffset(text_str, &offset); 2234 manager()->SetTextSelection(*this, offset, offset); 2235 return S_OK; 2236 } 2237 2238 STDMETHODIMP BrowserAccessibilityWin::setSelection(LONG selection_index, 2239 LONG start_offset, 2240 LONG end_offset) { 2241 if (!instance_active()) 2242 return E_FAIL; 2243 2244 if (selection_index != 0) 2245 return E_INVALIDARG; 2246 2247 const base::string16& text_str = TextForIAccessibleText(); 2248 HandleSpecialTextOffset(text_str, &start_offset); 2249 HandleSpecialTextOffset(text_str, &end_offset); 2250 2251 manager()->SetTextSelection(*this, start_offset, end_offset); 2252 return S_OK; 2253 } 2254 2255 // 2256 // IAccessibleHypertext methods. 2257 // 2258 2259 STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) { 2260 if (!instance_active()) 2261 return E_FAIL; 2262 2263 if (!hyperlink_count) 2264 return E_INVALIDARG; 2265 2266 *hyperlink_count = hyperlink_offset_to_index_.size(); 2267 return S_OK; 2268 } 2269 2270 STDMETHODIMP BrowserAccessibilityWin::get_hyperlink( 2271 long index, 2272 IAccessibleHyperlink** hyperlink) { 2273 if (!instance_active()) 2274 return E_FAIL; 2275 2276 if (!hyperlink || 2277 index < 0 || 2278 index >= static_cast<long>(hyperlinks_.size())) { 2279 return E_INVALIDARG; 2280 } 2281 2282 BrowserAccessibilityWin* child = 2283 InternalGetChild(hyperlinks_[index])->ToBrowserAccessibilityWin(); 2284 *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference()); 2285 return S_OK; 2286 } 2287 2288 STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex( 2289 long char_index, 2290 long* hyperlink_index) { 2291 if (!instance_active()) 2292 return E_FAIL; 2293 2294 if (!hyperlink_index) 2295 return E_INVALIDARG; 2296 2297 *hyperlink_index = -1; 2298 2299 if (char_index < 0 || char_index >= static_cast<long>(hypertext_.size())) 2300 return E_INVALIDARG; 2301 2302 std::map<int32, int32>::iterator it = 2303 hyperlink_offset_to_index_.find(char_index); 2304 if (it == hyperlink_offset_to_index_.end()) 2305 return E_FAIL; 2306 2307 *hyperlink_index = it->second; 2308 return S_OK; 2309 } 2310 2311 // 2312 // IAccessibleValue methods. 2313 // 2314 2315 STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) { 2316 if (!instance_active()) 2317 return E_FAIL; 2318 2319 if (!value) 2320 return E_INVALIDARG; 2321 2322 float float_val; 2323 if (GetFloatAttribute( 2324 ui::AX_ATTR_VALUE_FOR_RANGE, &float_val)) { 2325 value->vt = VT_R8; 2326 value->dblVal = float_val; 2327 return S_OK; 2328 } 2329 2330 value->vt = VT_EMPTY; 2331 return S_FALSE; 2332 } 2333 2334 STDMETHODIMP BrowserAccessibilityWin::get_minimumValue(VARIANT* value) { 2335 if (!instance_active()) 2336 return E_FAIL; 2337 2338 if (!value) 2339 return E_INVALIDARG; 2340 2341 float float_val; 2342 if (GetFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE, 2343 &float_val)) { 2344 value->vt = VT_R8; 2345 value->dblVal = float_val; 2346 return S_OK; 2347 } 2348 2349 value->vt = VT_EMPTY; 2350 return S_FALSE; 2351 } 2352 2353 STDMETHODIMP BrowserAccessibilityWin::get_maximumValue(VARIANT* value) { 2354 if (!instance_active()) 2355 return E_FAIL; 2356 2357 if (!value) 2358 return E_INVALIDARG; 2359 2360 float float_val; 2361 if (GetFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE, 2362 &float_val)) { 2363 value->vt = VT_R8; 2364 value->dblVal = float_val; 2365 return S_OK; 2366 } 2367 2368 value->vt = VT_EMPTY; 2369 return S_FALSE; 2370 } 2371 2372 STDMETHODIMP BrowserAccessibilityWin::setCurrentValue(VARIANT new_value) { 2373 // TODO(dmazzoni): Implement this. 2374 return E_NOTIMPL; 2375 } 2376 2377 // 2378 // ISimpleDOMDocument methods. 2379 // 2380 2381 STDMETHODIMP BrowserAccessibilityWin::get_URL(BSTR* url) { 2382 if (!instance_active()) 2383 return E_FAIL; 2384 2385 if (!url) 2386 return E_INVALIDARG; 2387 2388 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_URL, url); 2389 } 2390 2391 STDMETHODIMP BrowserAccessibilityWin::get_title(BSTR* title) { 2392 if (!instance_active()) 2393 return E_FAIL; 2394 2395 if (!title) 2396 return E_INVALIDARG; 2397 2398 return GetStringAttributeAsBstr(ui::AX_ATTR_DOC_TITLE, title); 2399 } 2400 2401 STDMETHODIMP BrowserAccessibilityWin::get_mimeType(BSTR* mime_type) { 2402 if (!instance_active()) 2403 return E_FAIL; 2404 2405 if (!mime_type) 2406 return E_INVALIDARG; 2407 2408 return GetStringAttributeAsBstr( 2409 ui::AX_ATTR_DOC_MIMETYPE, mime_type); 2410 } 2411 2412 STDMETHODIMP BrowserAccessibilityWin::get_docType(BSTR* doc_type) { 2413 if (!instance_active()) 2414 return E_FAIL; 2415 2416 if (!doc_type) 2417 return E_INVALIDARG; 2418 2419 return GetStringAttributeAsBstr( 2420 ui::AX_ATTR_DOC_DOCTYPE, doc_type); 2421 } 2422 2423 // 2424 // ISimpleDOMNode methods. 2425 // 2426 2427 STDMETHODIMP BrowserAccessibilityWin::get_nodeInfo( 2428 BSTR* node_name, 2429 short* name_space_id, 2430 BSTR* node_value, 2431 unsigned int* num_children, 2432 unsigned int* unique_id, 2433 unsigned short* node_type) { 2434 if (!instance_active()) 2435 return E_FAIL; 2436 2437 if (!node_name || !name_space_id || !node_value || !num_children || 2438 !unique_id || !node_type) { 2439 return E_INVALIDARG; 2440 } 2441 2442 base::string16 tag; 2443 if (GetString16Attribute(ui::AX_ATTR_HTML_TAG, &tag)) 2444 *node_name = SysAllocString(tag.c_str()); 2445 else 2446 *node_name = NULL; 2447 2448 *name_space_id = 0; 2449 *node_value = SysAllocString(base::UTF8ToUTF16(value()).c_str()); 2450 *num_children = PlatformChildCount(); 2451 *unique_id = unique_id_win_; 2452 2453 if (ia_role_ == ROLE_SYSTEM_DOCUMENT) { 2454 *node_type = NODETYPE_DOCUMENT; 2455 } else if (ia_role_ == ROLE_SYSTEM_TEXT && 2456 ((ia2_state_ & IA2_STATE_EDITABLE) == 0)) { 2457 *node_type = NODETYPE_TEXT; 2458 } else { 2459 *node_type = NODETYPE_ELEMENT; 2460 } 2461 2462 return S_OK; 2463 } 2464 2465 STDMETHODIMP BrowserAccessibilityWin::get_attributes( 2466 unsigned short max_attribs, 2467 BSTR* attrib_names, 2468 short* name_space_id, 2469 BSTR* attrib_values, 2470 unsigned short* num_attribs) { 2471 if (!instance_active()) 2472 return E_FAIL; 2473 2474 if (!attrib_names || !name_space_id || !attrib_values || !num_attribs) 2475 return E_INVALIDARG; 2476 2477 *num_attribs = max_attribs; 2478 if (*num_attribs > GetHtmlAttributes().size()) 2479 *num_attribs = GetHtmlAttributes().size(); 2480 2481 for (unsigned short i = 0; i < *num_attribs; ++i) { 2482 attrib_names[i] = SysAllocString( 2483 base::UTF8ToUTF16(GetHtmlAttributes()[i].first).c_str()); 2484 name_space_id[i] = 0; 2485 attrib_values[i] = SysAllocString( 2486 base::UTF8ToUTF16(GetHtmlAttributes()[i].second).c_str()); 2487 } 2488 return S_OK; 2489 } 2490 2491 STDMETHODIMP BrowserAccessibilityWin::get_attributesForNames( 2492 unsigned short num_attribs, 2493 BSTR* attrib_names, 2494 short* name_space_id, 2495 BSTR* attrib_values) { 2496 if (!instance_active()) 2497 return E_FAIL; 2498 2499 if (!attrib_names || !name_space_id || !attrib_values) 2500 return E_INVALIDARG; 2501 2502 for (unsigned short i = 0; i < num_attribs; ++i) { 2503 name_space_id[i] = 0; 2504 bool found = false; 2505 std::string name = base::UTF16ToUTF8((LPCWSTR)attrib_names[i]); 2506 for (unsigned int j = 0; j < GetHtmlAttributes().size(); ++j) { 2507 if (GetHtmlAttributes()[j].first == name) { 2508 attrib_values[i] = SysAllocString( 2509 base::UTF8ToUTF16(GetHtmlAttributes()[j].second).c_str()); 2510 found = true; 2511 break; 2512 } 2513 } 2514 if (!found) { 2515 attrib_values[i] = NULL; 2516 } 2517 } 2518 return S_OK; 2519 } 2520 2521 STDMETHODIMP BrowserAccessibilityWin::get_computedStyle( 2522 unsigned short max_style_properties, 2523 boolean use_alternate_view, 2524 BSTR* style_properties, 2525 BSTR* style_values, 2526 unsigned short *num_style_properties) { 2527 if (!instance_active()) 2528 return E_FAIL; 2529 2530 if (!style_properties || !style_values) 2531 return E_INVALIDARG; 2532 2533 // We only cache a single style property for now: DISPLAY 2534 2535 base::string16 display; 2536 if (max_style_properties == 0 || 2537 !GetString16Attribute(ui::AX_ATTR_DISPLAY, &display)) { 2538 *num_style_properties = 0; 2539 return S_OK; 2540 } 2541 2542 *num_style_properties = 1; 2543 style_properties[0] = SysAllocString(L"display"); 2544 style_values[0] = SysAllocString(display.c_str()); 2545 2546 return S_OK; 2547 } 2548 2549 STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties( 2550 unsigned short num_style_properties, 2551 boolean use_alternate_view, 2552 BSTR* style_properties, 2553 BSTR* style_values) { 2554 if (!instance_active()) 2555 return E_FAIL; 2556 2557 if (!style_properties || !style_values) 2558 return E_INVALIDARG; 2559 2560 // We only cache a single style property for now: DISPLAY 2561 2562 for (unsigned short i = 0; i < num_style_properties; ++i) { 2563 base::string16 name = (LPCWSTR)style_properties[i]; 2564 StringToLowerASCII(&name); 2565 if (name == L"display") { 2566 base::string16 display = GetString16Attribute( 2567 ui::AX_ATTR_DISPLAY); 2568 style_values[i] = SysAllocString(display.c_str()); 2569 } else { 2570 style_values[i] = NULL; 2571 } 2572 } 2573 2574 return S_OK; 2575 } 2576 2577 STDMETHODIMP BrowserAccessibilityWin::scrollTo(boolean placeTopLeft) { 2578 return scrollTo(placeTopLeft ? 2579 IA2_SCROLL_TYPE_TOP_LEFT : IA2_SCROLL_TYPE_ANYWHERE); 2580 } 2581 2582 STDMETHODIMP BrowserAccessibilityWin::get_parentNode(ISimpleDOMNode** node) { 2583 if (!instance_active()) 2584 return E_FAIL; 2585 2586 if (!node) 2587 return E_INVALIDARG; 2588 2589 *node = GetParent()->ToBrowserAccessibilityWin()->NewReference(); 2590 return S_OK; 2591 } 2592 2593 STDMETHODIMP BrowserAccessibilityWin::get_firstChild(ISimpleDOMNode** node) { 2594 if (!instance_active()) 2595 return E_FAIL; 2596 2597 if (!node) 2598 return E_INVALIDARG; 2599 2600 if (PlatformChildCount() == 0) { 2601 *node = NULL; 2602 return S_FALSE; 2603 } 2604 2605 *node = PlatformGetChild(0)->ToBrowserAccessibilityWin()->NewReference(); 2606 return S_OK; 2607 } 2608 2609 STDMETHODIMP BrowserAccessibilityWin::get_lastChild(ISimpleDOMNode** node) { 2610 if (!instance_active()) 2611 return E_FAIL; 2612 2613 if (!node) 2614 return E_INVALIDARG; 2615 2616 if (PlatformChildCount() == 0) { 2617 *node = NULL; 2618 return S_FALSE; 2619 } 2620 2621 *node = PlatformGetChild(PlatformChildCount() - 1) 2622 ->ToBrowserAccessibilityWin()->NewReference(); 2623 return S_OK; 2624 } 2625 2626 STDMETHODIMP BrowserAccessibilityWin::get_previousSibling( 2627 ISimpleDOMNode** node) { 2628 if (!instance_active()) 2629 return E_FAIL; 2630 2631 if (!node) 2632 return E_INVALIDARG; 2633 2634 if (!GetParent() || GetIndexInParent() <= 0) { 2635 *node = NULL; 2636 return S_FALSE; 2637 } 2638 2639 *node = GetParent()->InternalGetChild(GetIndexInParent() - 1)-> 2640 ToBrowserAccessibilityWin()->NewReference(); 2641 return S_OK; 2642 } 2643 2644 STDMETHODIMP BrowserAccessibilityWin::get_nextSibling(ISimpleDOMNode** node) { 2645 if (!instance_active()) 2646 return E_FAIL; 2647 2648 if (!node) 2649 return E_INVALIDARG; 2650 2651 if (!GetParent() || 2652 GetIndexInParent() < 0 || 2653 GetIndexInParent() >= static_cast<int>( 2654 GetParent()->InternalChildCount()) - 1) { 2655 *node = NULL; 2656 return S_FALSE; 2657 } 2658 2659 *node = GetParent()->InternalGetChild(GetIndexInParent() + 1)-> 2660 ToBrowserAccessibilityWin()->NewReference(); 2661 return S_OK; 2662 } 2663 2664 STDMETHODIMP BrowserAccessibilityWin::get_childAt( 2665 unsigned int child_index, 2666 ISimpleDOMNode** node) { 2667 if (!instance_active()) 2668 return E_FAIL; 2669 2670 if (!node) 2671 return E_INVALIDARG; 2672 2673 if (child_index >= PlatformChildCount()) 2674 return E_INVALIDARG; 2675 2676 BrowserAccessibility* child = PlatformGetChild(child_index); 2677 if (!child) { 2678 *node = NULL; 2679 return S_FALSE; 2680 } 2681 2682 *node = child->ToBrowserAccessibilityWin()->NewReference(); 2683 return S_OK; 2684 } 2685 2686 // 2687 // ISimpleDOMText methods. 2688 // 2689 2690 STDMETHODIMP BrowserAccessibilityWin::get_domText(BSTR* dom_text) { 2691 if (!instance_active()) 2692 return E_FAIL; 2693 2694 if (!dom_text) 2695 return E_INVALIDARG; 2696 2697 return GetStringAttributeAsBstr( 2698 ui::AX_ATTR_NAME, dom_text); 2699 } 2700 2701 STDMETHODIMP BrowserAccessibilityWin::get_clippedSubstringBounds( 2702 unsigned int start_index, 2703 unsigned int end_index, 2704 int* out_x, 2705 int* out_y, 2706 int* out_width, 2707 int* out_height) { 2708 // TODO(dmazzoni): fully support this API by intersecting the 2709 // rect with the container's rect. 2710 return get_unclippedSubstringBounds( 2711 start_index, end_index, out_x, out_y, out_width, out_height); 2712 } 2713 2714 STDMETHODIMP BrowserAccessibilityWin::get_unclippedSubstringBounds( 2715 unsigned int start_index, 2716 unsigned int end_index, 2717 int* out_x, 2718 int* out_y, 2719 int* out_width, 2720 int* out_height) { 2721 if (!instance_active()) 2722 return E_FAIL; 2723 2724 if (!out_x || !out_y || !out_width || !out_height) 2725 return E_INVALIDARG; 2726 2727 const base::string16& text_str = TextForIAccessibleText(); 2728 if (start_index > text_str.size() || 2729 end_index > text_str.size() || 2730 start_index > end_index) { 2731 return E_INVALIDARG; 2732 } 2733 2734 gfx::Rect bounds = GetGlobalBoundsForRange( 2735 start_index, end_index - start_index); 2736 *out_x = bounds.x(); 2737 *out_y = bounds.y(); 2738 *out_width = bounds.width(); 2739 *out_height = bounds.height(); 2740 return S_OK; 2741 } 2742 2743 STDMETHODIMP BrowserAccessibilityWin::scrollToSubstring( 2744 unsigned int start_index, 2745 unsigned int end_index) { 2746 if (!instance_active()) 2747 return E_FAIL; 2748 2749 const base::string16& text_str = TextForIAccessibleText(); 2750 if (start_index > text_str.size() || 2751 end_index > text_str.size() || 2752 start_index > end_index) { 2753 return E_INVALIDARG; 2754 } 2755 2756 manager()->ScrollToMakeVisible(*this, GetLocalBoundsForRange( 2757 start_index, end_index - start_index)); 2758 manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this); 2759 2760 return S_OK; 2761 } 2762 2763 // 2764 // IServiceProvider methods. 2765 // 2766 2767 STDMETHODIMP BrowserAccessibilityWin::QueryService(REFGUID guidService, 2768 REFIID riid, 2769 void** object) { 2770 if (!instance_active()) 2771 return E_FAIL; 2772 2773 // The system uses IAccessible APIs for many purposes, but only 2774 // assistive technology like screen readers uses IAccessible2. 2775 // Enable full accessibility support when IAccessible2 APIs are queried. 2776 if (riid == IID_IAccessible2) 2777 BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); 2778 2779 if (guidService == GUID_IAccessibleContentDocument) { 2780 // Special Mozilla extension: return the accessible for the root document. 2781 // Screen readers use this to distinguish between a document loaded event 2782 // on the root document vs on an iframe. 2783 return manager()->GetRoot()->ToBrowserAccessibilityWin()->QueryInterface( 2784 IID_IAccessible2, object); 2785 } 2786 2787 if (guidService == IID_IAccessible || 2788 guidService == IID_IAccessible2 || 2789 guidService == IID_IAccessibleAction || 2790 guidService == IID_IAccessibleApplication || 2791 guidService == IID_IAccessibleHyperlink || 2792 guidService == IID_IAccessibleHypertext || 2793 guidService == IID_IAccessibleImage || 2794 guidService == IID_IAccessibleTable || 2795 guidService == IID_IAccessibleTable2 || 2796 guidService == IID_IAccessibleTableCell || 2797 guidService == IID_IAccessibleText || 2798 guidService == IID_IAccessibleValue || 2799 guidService == IID_ISimpleDOMDocument || 2800 guidService == IID_ISimpleDOMNode || 2801 guidService == IID_ISimpleDOMText || 2802 guidService == GUID_ISimpleDOM) { 2803 return QueryInterface(riid, object); 2804 } 2805 2806 // We only support the IAccessibleEx interface on Windows 8 and above. This 2807 // is needed for the on-screen Keyboard to show up in metro mode, when the 2808 // user taps an editable portion on the page. 2809 // All methods in the IAccessibleEx interface are unimplemented. 2810 if (riid == IID_IAccessibleEx && 2811 base::win::GetVersion() >= base::win::VERSION_WIN8) { 2812 return QueryInterface(riid, object); 2813 } 2814 2815 *object = NULL; 2816 return E_FAIL; 2817 } 2818 2819 STDMETHODIMP BrowserAccessibilityWin::GetPatternProvider(PATTERNID id, 2820 IUnknown** provider) { 2821 DVLOG(1) << "In Function: " 2822 << __FUNCTION__ 2823 << " for pattern id: " 2824 << id; 2825 if (id == UIA_ValuePatternId || id == UIA_TextPatternId) { 2826 if (IsEditableText()) { 2827 DVLOG(1) << "Returning UIA text provider"; 2828 base::win::UIATextProvider::CreateTextProvider( 2829 GetValueText(), true, provider); 2830 return S_OK; 2831 } 2832 } 2833 return E_NOTIMPL; 2834 } 2835 2836 STDMETHODIMP BrowserAccessibilityWin::GetPropertyValue(PROPERTYID id, 2837 VARIANT* ret) { 2838 DVLOG(1) << "In Function: " 2839 << __FUNCTION__ 2840 << " for property id: " 2841 << id; 2842 V_VT(ret) = VT_EMPTY; 2843 if (id == UIA_ControlTypePropertyId) { 2844 if (IsEditableText()) { 2845 V_VT(ret) = VT_I4; 2846 ret->lVal = UIA_EditControlTypeId; 2847 DVLOG(1) << "Returning Edit control type"; 2848 } else { 2849 DVLOG(1) << "Returning empty control type"; 2850 } 2851 } 2852 return S_OK; 2853 } 2854 2855 // 2856 // CComObjectRootEx methods. 2857 // 2858 2859 HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface( 2860 void* this_ptr, 2861 const _ATL_INTMAP_ENTRY* entries, 2862 REFIID iid, 2863 void** object) { 2864 if (iid == IID_IAccessibleImage) { 2865 if (ia_role_ != ROLE_SYSTEM_GRAPHIC) { 2866 *object = NULL; 2867 return E_NOINTERFACE; 2868 } 2869 } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) { 2870 if (ia_role_ != ROLE_SYSTEM_TABLE) { 2871 *object = NULL; 2872 return E_NOINTERFACE; 2873 } 2874 } else if (iid == IID_IAccessibleTableCell) { 2875 if (ia_role_ != ROLE_SYSTEM_CELL) { 2876 *object = NULL; 2877 return E_NOINTERFACE; 2878 } 2879 } else if (iid == IID_IAccessibleValue) { 2880 if (ia_role_ != ROLE_SYSTEM_PROGRESSBAR && 2881 ia_role_ != ROLE_SYSTEM_SCROLLBAR && 2882 ia_role_ != ROLE_SYSTEM_SLIDER) { 2883 *object = NULL; 2884 return E_NOINTERFACE; 2885 } 2886 } else if (iid == IID_ISimpleDOMDocument) { 2887 if (ia_role_ != ROLE_SYSTEM_DOCUMENT) { 2888 *object = NULL; 2889 return E_NOINTERFACE; 2890 } 2891 } 2892 2893 return CComObjectRootBase::InternalQueryInterface( 2894 this_ptr, entries, iid, object); 2895 } 2896 2897 // 2898 // Private methods. 2899 // 2900 2901 // Called every time this node's data changes. 2902 void BrowserAccessibilityWin::OnDataChanged() { 2903 BrowserAccessibility::OnDataChanged(); 2904 2905 InitRoleAndState(); 2906 2907 // Expose the "display" and "tag" attributes. 2908 StringAttributeToIA2(ui::AX_ATTR_DISPLAY, "display"); 2909 StringAttributeToIA2(ui::AX_ATTR_HTML_TAG, "tag"); 2910 StringAttributeToIA2(ui::AX_ATTR_ROLE, "xml-roles"); 2911 2912 // Expose "level" attribute for headings, trees, etc. 2913 IntAttributeToIA2(ui::AX_ATTR_HIERARCHICAL_LEVEL, "level"); 2914 2915 // Expose the set size and position in set for listbox options. 2916 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION && 2917 GetParent() && 2918 GetParent()->GetRole() == ui::AX_ROLE_LIST_BOX) { 2919 ia2_attributes_.push_back( 2920 L"setsize:" + base::IntToString16(GetParent()->PlatformChildCount())); 2921 ia2_attributes_.push_back( 2922 L"setsize:" + base::IntToString16(GetIndexInParent() + 1)); 2923 } 2924 2925 if (ia_role_ == ROLE_SYSTEM_CHECKBUTTON || 2926 ia_role_ == ROLE_SYSTEM_RADIOBUTTON || 2927 ia2_role_ == IA2_ROLE_TOGGLE_BUTTON) { 2928 ia2_attributes_.push_back(L"checkable:true"); 2929 } 2930 2931 // Expose live region attributes. 2932 StringAttributeToIA2(ui::AX_ATTR_LIVE_STATUS, "live"); 2933 StringAttributeToIA2(ui::AX_ATTR_LIVE_RELEVANT, "relevant"); 2934 BoolAttributeToIA2(ui::AX_ATTR_LIVE_ATOMIC, "atomic"); 2935 BoolAttributeToIA2(ui::AX_ATTR_LIVE_BUSY, "busy"); 2936 2937 // Expose container live region attributes. 2938 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_STATUS, 2939 "container-live"); 2940 StringAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT, 2941 "container-relevant"); 2942 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC, 2943 "container-atomic"); 2944 BoolAttributeToIA2(ui::AX_ATTR_CONTAINER_LIVE_BUSY, 2945 "container-busy"); 2946 2947 // Expose slider value. 2948 if (ia_role_ == ROLE_SYSTEM_PROGRESSBAR || 2949 ia_role_ == ROLE_SYSTEM_SCROLLBAR || 2950 ia_role_ == ROLE_SYSTEM_SLIDER) { 2951 ia2_attributes_.push_back(L"valuetext:" + GetValueText()); 2952 } 2953 2954 // Expose table cell index. 2955 if (ia_role_ == ROLE_SYSTEM_CELL) { 2956 BrowserAccessibility* table = GetParent(); 2957 while (table && table->GetRole() != ui::AX_ROLE_TABLE) 2958 table = table->GetParent(); 2959 if (table) { 2960 const std::vector<int32>& unique_cell_ids = table->GetIntListAttribute( 2961 ui::AX_ATTR_UNIQUE_CELL_IDS); 2962 for (size_t i = 0; i < unique_cell_ids.size(); ++i) { 2963 if (unique_cell_ids[i] == GetId()) { 2964 ia2_attributes_.push_back( 2965 base::string16(L"table-cell-index:") + base::IntToString16(i)); 2966 } 2967 } 2968 } 2969 } 2970 2971 // The calculation of the accessible name of an element has been 2972 // standardized in the HTML to Platform Accessibility APIs Implementation 2973 // Guide (http://www.w3.org/TR/html-aapi/). In order to return the 2974 // appropriate accessible name on Windows, we need to apply some logic 2975 // to the fields we get from WebKit. 2976 // 2977 // TODO(dmazzoni): move most of this logic into WebKit. 2978 // 2979 // WebKit gives us: 2980 // 2981 // name: the default name, e.g. inner text 2982 // title ui element: a reference to a <label> element on the same 2983 // page that labels this node. 2984 // description: accessible labels that override the default name: 2985 // aria-label or aria-labelledby or aria-describedby 2986 // help: the value of the "title" attribute 2987 // 2988 // On Windows, the logic we apply lets some fields take precedence and 2989 // always returns the primary name in "name" and the secondary name, 2990 // if any, in "description". 2991 2992 int title_elem_id = GetIntAttribute( 2993 ui::AX_ATTR_TITLE_UI_ELEMENT); 2994 std::string help = GetStringAttribute(ui::AX_ATTR_HELP); 2995 std::string description = GetStringAttribute( 2996 ui::AX_ATTR_DESCRIPTION); 2997 2998 // WebKit annoyingly puts the title in the description if there's no other 2999 // description, which just confuses the rest of the logic. Put it back. 3000 // Now "help" is always the value of the "title" attribute, if present. 3001 std::string title_attr; 3002 if (GetHtmlAttribute("title", &title_attr) && 3003 description == title_attr && 3004 help.empty()) { 3005 help = description; 3006 description.clear(); 3007 } 3008 3009 // Now implement the main logic: the descripion should become the name if 3010 // it's nonempty, and the help should become the description if 3011 // there's no description - or the name if there's no name or description. 3012 if (!description.empty()) { 3013 set_name(description); 3014 description.clear(); 3015 } 3016 if (!help.empty() && description.empty()) { 3017 description = help; 3018 help.clear(); 3019 } 3020 if (!description.empty() && name().empty() && !title_elem_id) { 3021 set_name(description); 3022 description.clear(); 3023 } 3024 3025 // If it's a text field, also consider the placeholder. 3026 std::string placeholder; 3027 if (GetRole() == ui::AX_ROLE_TEXT_FIELD && 3028 HasState(ui::AX_STATE_FOCUSABLE) && 3029 GetHtmlAttribute("placeholder", &placeholder)) { 3030 if (name().empty() && !title_elem_id) { 3031 set_name(placeholder); 3032 } else if (description.empty()) { 3033 description = placeholder; 3034 } 3035 } 3036 3037 SetStringAttribute(ui::AX_ATTR_DESCRIPTION, description); 3038 SetStringAttribute(ui::AX_ATTR_HELP, help); 3039 3040 // On Windows, the value of a document should be its url. 3041 if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA || 3042 GetRole() == ui::AX_ROLE_WEB_AREA) { 3043 set_value(GetStringAttribute(ui::AX_ATTR_DOC_URL)); 3044 } 3045 3046 // For certain roles (listbox option, static text, and list marker) 3047 // WebKit stores the main accessible text in the "value" - swap it so 3048 // that it's the "name". 3049 if (name().empty() && 3050 (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION || 3051 GetRole() == ui::AX_ROLE_STATIC_TEXT || 3052 GetRole() == ui::AX_ROLE_LIST_MARKER)) { 3053 std::string tmp = value(); 3054 set_value(name()); 3055 set_name(tmp); 3056 } 3057 3058 // If this doesn't have a value and is linked then set its value to the url 3059 // attribute. This allows screen readers to read an empty link's destination. 3060 if (value().empty() && (ia_state_ & STATE_SYSTEM_LINKED)) 3061 set_value(GetStringAttribute(ui::AX_ATTR_URL)); 3062 3063 // Clear any old relationships between this node and other nodes. 3064 for (size_t i = 0; i < relations_.size(); ++i) 3065 relations_[i]->Release(); 3066 relations_.clear(); 3067 3068 // Handle title UI element. 3069 if (title_elem_id) { 3070 // Add a labelled by relationship. 3071 CComObject<BrowserAccessibilityRelation>* relation; 3072 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance( 3073 &relation); 3074 DCHECK(SUCCEEDED(hr)); 3075 relation->AddRef(); 3076 relation->Initialize(this, IA2_RELATION_LABELLED_BY); 3077 relation->AddTarget(title_elem_id); 3078 relations_.push_back(relation); 3079 } 3080 } 3081 3082 void BrowserAccessibilityWin::OnUpdateFinished() { 3083 // Construct the hypertext for this node. 3084 hyperlink_offset_to_index_.clear(); 3085 hyperlinks_.clear(); 3086 hypertext_.clear(); 3087 for (unsigned int i = 0; i < PlatformChildCount(); ++i) { 3088 BrowserAccessibility* child = PlatformGetChild(i); 3089 if (child->GetRole() == ui::AX_ROLE_STATIC_TEXT) { 3090 hypertext_ += base::UTF8ToUTF16(child->name()); 3091 } else { 3092 hyperlink_offset_to_index_[hypertext_.size()] = hyperlinks_.size(); 3093 hypertext_ += kEmbeddedCharacter; 3094 hyperlinks_.push_back(i); 3095 } 3096 } 3097 DCHECK_EQ(hyperlink_offset_to_index_.size(), hyperlinks_.size()); 3098 3099 // Fire an event when an alert first appears. 3100 if (GetRole() == ui::AX_ROLE_ALERT && first_time_) 3101 manager()->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, this); 3102 3103 // Fire events if text has changed. 3104 base::string16 text = TextForIAccessibleText(); 3105 if (previous_text_ != text) { 3106 if (!previous_text_.empty() && !text.empty()) { 3107 manager()->NotifyAccessibilityEvent( 3108 ui::AX_EVENT_SHOW, this); 3109 } 3110 3111 // TODO(dmazzoni): Look into HIDE events, too. 3112 3113 old_text_ = previous_text_; 3114 previous_text_ = text; 3115 } 3116 3117 BrowserAccessibilityManagerWin* manager = 3118 this->manager()->ToBrowserAccessibilityManagerWin(); 3119 3120 // Fire events if the state has changed. 3121 if (!first_time_ && ia_state_ != old_ia_state_) { 3122 // Normally focus events are handled elsewhere, however 3123 // focus for managed descendants is platform-specific. 3124 // Fire a focus event if the focused descendant in a multi-select 3125 // list box changes. 3126 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION && 3127 (ia_state_ & STATE_SYSTEM_FOCUSABLE) && 3128 (ia_state_ & STATE_SYSTEM_SELECTABLE) && 3129 (ia_state_ & STATE_SYSTEM_FOCUSED) && 3130 !(old_ia_state_ & STATE_SYSTEM_FOCUSED)) { 3131 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_FOCUS, unique_id_win()); 3132 } 3133 3134 if ((ia_state_ & STATE_SYSTEM_SELECTED) && 3135 !(old_ia_state_ & STATE_SYSTEM_SELECTED)) { 3136 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONADD, 3137 unique_id_win()); 3138 } else if (!(ia_state_ & STATE_SYSTEM_SELECTED) && 3139 (old_ia_state_ & STATE_SYSTEM_SELECTED)) { 3140 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE, 3141 unique_id_win()); 3142 } 3143 3144 old_ia_state_ = ia_state_; 3145 } 3146 3147 // Fire an event if this container object has scrolled. 3148 int sx = 0; 3149 int sy = 0; 3150 if (GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) && 3151 GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) { 3152 if (!first_time_ && 3153 (sx != previous_scroll_x_ || sy != previous_scroll_y_)) { 3154 manager->MaybeCallNotifyWinEvent(EVENT_SYSTEM_SCROLLINGEND, 3155 unique_id_win()); 3156 } 3157 previous_scroll_x_ = sx; 3158 previous_scroll_y_ = sy; 3159 } 3160 3161 first_time_ = false; 3162 } 3163 3164 void BrowserAccessibilityWin::NativeAddReference() { 3165 AddRef(); 3166 } 3167 3168 void BrowserAccessibilityWin::NativeReleaseReference() { 3169 Release(); 3170 } 3171 3172 bool BrowserAccessibilityWin::IsNative() const { 3173 return true; 3174 } 3175 3176 void BrowserAccessibilityWin::OnLocationChanged() const { 3177 manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent( 3178 EVENT_OBJECT_LOCATIONCHANGE, unique_id_win()); 3179 } 3180 3181 BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() { 3182 AddRef(); 3183 return this; 3184 } 3185 3186 BrowserAccessibilityWin* BrowserAccessibilityWin::GetTargetFromChildID( 3187 const VARIANT& var_id) { 3188 if (var_id.vt != VT_I4) 3189 return NULL; 3190 3191 LONG child_id = var_id.lVal; 3192 if (child_id == CHILDID_SELF) 3193 return this; 3194 3195 if (child_id >= 1 && child_id <= static_cast<LONG>(PlatformChildCount())) 3196 return PlatformGetChild(child_id - 1)->ToBrowserAccessibilityWin(); 3197 3198 return manager()->ToBrowserAccessibilityManagerWin()-> 3199 GetFromUniqueIdWin(child_id); 3200 } 3201 3202 HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr( 3203 ui::AXStringAttribute attribute, 3204 BSTR* value_bstr) { 3205 base::string16 str; 3206 3207 if (!GetString16Attribute(attribute, &str)) 3208 return S_FALSE; 3209 3210 if (str.empty()) 3211 return S_FALSE; 3212 3213 *value_bstr = SysAllocString(str.c_str()); 3214 DCHECK(*value_bstr); 3215 3216 return S_OK; 3217 } 3218 3219 void BrowserAccessibilityWin::StringAttributeToIA2( 3220 ui::AXStringAttribute attribute, 3221 const char* ia2_attr) { 3222 base::string16 value; 3223 if (GetString16Attribute(attribute, &value)) 3224 ia2_attributes_.push_back(base::ASCIIToUTF16(ia2_attr) + L":" + value); 3225 } 3226 3227 void BrowserAccessibilityWin::BoolAttributeToIA2( 3228 ui::AXBoolAttribute attribute, 3229 const char* ia2_attr) { 3230 bool value; 3231 if (GetBoolAttribute(attribute, &value)) { 3232 ia2_attributes_.push_back((base::ASCIIToUTF16(ia2_attr) + L":") + 3233 (value ? L"true" : L"false")); 3234 } 3235 } 3236 3237 void BrowserAccessibilityWin::IntAttributeToIA2( 3238 ui::AXIntAttribute attribute, 3239 const char* ia2_attr) { 3240 int value; 3241 if (GetIntAttribute(attribute, &value)) { 3242 ia2_attributes_.push_back(base::ASCIIToUTF16(ia2_attr) + L":" + 3243 base::IntToString16(value)); 3244 } 3245 } 3246 3247 base::string16 BrowserAccessibilityWin::GetValueText() { 3248 float fval; 3249 base::string16 value = base::UTF8ToUTF16(this->value()); 3250 3251 if (value.empty() && 3252 GetFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, &fval)) { 3253 value = base::UTF8ToUTF16(base::DoubleToString(fval)); 3254 } 3255 return value; 3256 } 3257 3258 base::string16 BrowserAccessibilityWin::TextForIAccessibleText() { 3259 if (IsEditableText()) 3260 return base::UTF8ToUTF16(value()); 3261 return (GetRole() == ui::AX_ROLE_STATIC_TEXT) ? 3262 base::UTF8ToUTF16(name()) : hypertext_; 3263 } 3264 3265 void BrowserAccessibilityWin::HandleSpecialTextOffset( 3266 const base::string16& text, 3267 LONG* offset) { 3268 if (*offset == IA2_TEXT_OFFSET_LENGTH) 3269 *offset = static_cast<LONG>(text.size()); 3270 else if (*offset == IA2_TEXT_OFFSET_CARET) 3271 get_caretOffset(offset); 3272 } 3273 3274 ui::TextBoundaryType BrowserAccessibilityWin::IA2TextBoundaryToTextBoundary( 3275 IA2TextBoundaryType ia2_boundary) { 3276 switch(ia2_boundary) { 3277 case IA2_TEXT_BOUNDARY_CHAR: return ui::CHAR_BOUNDARY; 3278 case IA2_TEXT_BOUNDARY_WORD: return ui::WORD_BOUNDARY; 3279 case IA2_TEXT_BOUNDARY_LINE: return ui::LINE_BOUNDARY; 3280 case IA2_TEXT_BOUNDARY_SENTENCE: return ui::SENTENCE_BOUNDARY; 3281 case IA2_TEXT_BOUNDARY_PARAGRAPH: return ui::PARAGRAPH_BOUNDARY; 3282 case IA2_TEXT_BOUNDARY_ALL: return ui::ALL_BOUNDARY; 3283 default: 3284 NOTREACHED(); 3285 return ui::CHAR_BOUNDARY; 3286 } 3287 } 3288 3289 LONG BrowserAccessibilityWin::FindBoundary( 3290 const base::string16& text, 3291 IA2TextBoundaryType ia2_boundary, 3292 LONG start_offset, 3293 ui::TextBoundaryDirection direction) { 3294 HandleSpecialTextOffset(text, &start_offset); 3295 ui::TextBoundaryType boundary = IA2TextBoundaryToTextBoundary(ia2_boundary); 3296 const std::vector<int32>& line_breaks = GetIntListAttribute( 3297 ui::AX_ATTR_LINE_BREAKS); 3298 return ui::FindAccessibleTextBoundary( 3299 text, line_breaks, boundary, start_offset, direction); 3300 } 3301 3302 BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromID(int32 id) { 3303 return manager()->GetFromID(id)->ToBrowserAccessibilityWin(); 3304 } 3305 3306 void BrowserAccessibilityWin::InitRoleAndState() { 3307 ia_state_ = 0; 3308 ia2_state_ = IA2_STATE_OPAQUE; 3309 ia2_attributes_.clear(); 3310 3311 if (HasState(ui::AX_STATE_BUSY)) 3312 ia_state_ |= STATE_SYSTEM_BUSY; 3313 if (HasState(ui::AX_STATE_CHECKED)) 3314 ia_state_ |= STATE_SYSTEM_CHECKED; 3315 if (HasState(ui::AX_STATE_COLLAPSED)) 3316 ia_state_ |= STATE_SYSTEM_COLLAPSED; 3317 if (HasState(ui::AX_STATE_EXPANDED)) 3318 ia_state_ |= STATE_SYSTEM_EXPANDED; 3319 if (HasState(ui::AX_STATE_FOCUSABLE)) 3320 ia_state_ |= STATE_SYSTEM_FOCUSABLE; 3321 if (HasState(ui::AX_STATE_HASPOPUP)) 3322 ia_state_ |= STATE_SYSTEM_HASPOPUP; 3323 if (HasState(ui::AX_STATE_HOVERED)) 3324 ia_state_ |= STATE_SYSTEM_HOTTRACKED; 3325 if (HasState(ui::AX_STATE_INDETERMINATE)) 3326 ia_state_ |= STATE_SYSTEM_INDETERMINATE; 3327 if (HasState(ui::AX_STATE_INVISIBLE)) 3328 ia_state_ |= STATE_SYSTEM_INVISIBLE; 3329 if (HasState(ui::AX_STATE_LINKED)) 3330 ia_state_ |= STATE_SYSTEM_LINKED; 3331 if (HasState(ui::AX_STATE_MULTISELECTABLE)) { 3332 ia_state_ |= STATE_SYSTEM_EXTSELECTABLE; 3333 ia_state_ |= STATE_SYSTEM_MULTISELECTABLE; 3334 } 3335 // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect. 3336 if (HasState(ui::AX_STATE_OFFSCREEN)) 3337 ia_state_ |= STATE_SYSTEM_OFFSCREEN; 3338 if (HasState(ui::AX_STATE_PRESSED)) 3339 ia_state_ |= STATE_SYSTEM_PRESSED; 3340 if (HasState(ui::AX_STATE_PROTECTED)) 3341 ia_state_ |= STATE_SYSTEM_PROTECTED; 3342 if (HasState(ui::AX_STATE_REQUIRED)) 3343 ia2_state_ |= IA2_STATE_REQUIRED; 3344 if (HasState(ui::AX_STATE_SELECTABLE)) 3345 ia_state_ |= STATE_SYSTEM_SELECTABLE; 3346 if (HasState(ui::AX_STATE_SELECTED)) 3347 ia_state_ |= STATE_SYSTEM_SELECTED; 3348 if (HasState(ui::AX_STATE_VISITED)) 3349 ia_state_ |= STATE_SYSTEM_TRAVERSED; 3350 if (!HasState(ui::AX_STATE_ENABLED)) 3351 ia_state_ |= STATE_SYSTEM_UNAVAILABLE; 3352 if (HasState(ui::AX_STATE_VERTICAL)) { 3353 ia2_state_ |= IA2_STATE_VERTICAL; 3354 } else { 3355 ia2_state_ |= IA2_STATE_HORIZONTAL; 3356 } 3357 if (HasState(ui::AX_STATE_VISITED)) 3358 ia_state_ |= STATE_SYSTEM_TRAVERSED; 3359 3360 // WebKit marks everything as readonly unless it's editable text, so if it's 3361 // not readonly, mark it as editable now. The final computation of the 3362 // READONLY state for MSAA is below, after the switch. 3363 if (!HasState(ui::AX_STATE_READ_ONLY)) 3364 ia2_state_ |= IA2_STATE_EDITABLE; 3365 3366 base::string16 invalid; 3367 if (GetHtmlAttribute("aria-invalid", &invalid)) 3368 ia2_state_ |= IA2_STATE_INVALID_ENTRY; 3369 3370 if (GetBoolAttribute(ui::AX_ATTR_BUTTON_MIXED)) 3371 ia_state_ |= STATE_SYSTEM_MIXED; 3372 3373 if (GetBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE)) 3374 ia2_state_ |= IA2_STATE_EDITABLE; 3375 3376 base::string16 html_tag = GetString16Attribute( 3377 ui::AX_ATTR_HTML_TAG); 3378 ia_role_ = 0; 3379 ia2_role_ = 0; 3380 switch (GetRole()) { 3381 case ui::AX_ROLE_ALERT: 3382 ia_role_ = ROLE_SYSTEM_ALERT; 3383 break; 3384 case ui::AX_ROLE_ALERT_DIALOG: 3385 ia_role_ = ROLE_SYSTEM_DIALOG; 3386 break; 3387 case ui::AX_ROLE_APPLICATION: 3388 ia_role_ = ROLE_SYSTEM_APPLICATION; 3389 break; 3390 case ui::AX_ROLE_ARTICLE: 3391 ia_role_ = ROLE_SYSTEM_GROUPING; 3392 ia2_role_ = IA2_ROLE_SECTION; 3393 ia_state_ |= STATE_SYSTEM_READONLY; 3394 break; 3395 case ui::AX_ROLE_BUSY_INDICATOR: 3396 ia_role_ = ROLE_SYSTEM_ANIMATION; 3397 ia_state_ |= STATE_SYSTEM_READONLY; 3398 break; 3399 case ui::AX_ROLE_BUTTON: 3400 ia_role_ = ROLE_SYSTEM_PUSHBUTTON; 3401 bool is_aria_pressed_defined; 3402 bool is_mixed; 3403 if (GetAriaTristate("aria-pressed", &is_aria_pressed_defined, &is_mixed)) 3404 ia_state_ |= STATE_SYSTEM_PRESSED; 3405 if (is_aria_pressed_defined) 3406 ia2_role_ = IA2_ROLE_TOGGLE_BUTTON; 3407 if (is_mixed) 3408 ia_state_ |= STATE_SYSTEM_MIXED; 3409 break; 3410 case ui::AX_ROLE_CANVAS: 3411 if (GetBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK)) { 3412 role_name_ = L"canvas"; 3413 ia2_role_ = IA2_ROLE_CANVAS; 3414 } else { 3415 ia_role_ = ROLE_SYSTEM_GRAPHIC; 3416 } 3417 break; 3418 case ui::AX_ROLE_CELL: 3419 ia_role_ = ROLE_SYSTEM_CELL; 3420 break; 3421 case ui::AX_ROLE_CHECK_BOX: 3422 ia_role_ = ROLE_SYSTEM_CHECKBUTTON; 3423 break; 3424 case ui::AX_ROLE_COLOR_WELL: 3425 ia_role_ = ROLE_SYSTEM_CLIENT; 3426 ia2_role_ = IA2_ROLE_COLOR_CHOOSER; 3427 break; 3428 case ui::AX_ROLE_COLUMN: 3429 ia_role_ = ROLE_SYSTEM_COLUMN; 3430 ia_state_ |= STATE_SYSTEM_READONLY; 3431 break; 3432 case ui::AX_ROLE_COLUMN_HEADER: 3433 ia_role_ = ROLE_SYSTEM_COLUMNHEADER; 3434 ia_state_ |= STATE_SYSTEM_READONLY; 3435 break; 3436 case ui::AX_ROLE_COMBO_BOX: 3437 ia_role_ = ROLE_SYSTEM_COMBOBOX; 3438 break; 3439 case ui::AX_ROLE_DIV: 3440 role_name_ = L"div"; 3441 ia2_role_ = IA2_ROLE_SECTION; 3442 break; 3443 case ui::AX_ROLE_DEFINITION: 3444 role_name_ = html_tag; 3445 ia2_role_ = IA2_ROLE_PARAGRAPH; 3446 ia_state_ |= STATE_SYSTEM_READONLY; 3447 break; 3448 case ui::AX_ROLE_DESCRIPTION_LIST_DETAIL: 3449 role_name_ = html_tag; 3450 ia2_role_ = IA2_ROLE_PARAGRAPH; 3451 ia_state_ |= STATE_SYSTEM_READONLY; 3452 break; 3453 case ui::AX_ROLE_DESCRIPTION_LIST_TERM: 3454 ia_role_ = ROLE_SYSTEM_LISTITEM; 3455 ia_state_ |= STATE_SYSTEM_READONLY; 3456 break; 3457 case ui::AX_ROLE_DIALOG: 3458 ia_role_ = ROLE_SYSTEM_DIALOG; 3459 ia_state_ |= STATE_SYSTEM_READONLY; 3460 break; 3461 case ui::AX_ROLE_DISCLOSURE_TRIANGLE: 3462 ia_role_ = ROLE_SYSTEM_OUTLINEBUTTON; 3463 ia_state_ |= STATE_SYSTEM_READONLY; 3464 break; 3465 case ui::AX_ROLE_DOCUMENT: 3466 case ui::AX_ROLE_ROOT_WEB_AREA: 3467 case ui::AX_ROLE_WEB_AREA: 3468 ia_role_ = ROLE_SYSTEM_DOCUMENT; 3469 ia_state_ |= STATE_SYSTEM_READONLY; 3470 ia_state_ |= STATE_SYSTEM_FOCUSABLE; 3471 break; 3472 case ui::AX_ROLE_EDITABLE_TEXT: 3473 ia_role_ = ROLE_SYSTEM_TEXT; 3474 ia2_state_ |= IA2_STATE_SINGLE_LINE; 3475 ia2_state_ |= IA2_STATE_EDITABLE; 3476 break; 3477 case ui::AX_ROLE_FORM: 3478 role_name_ = L"form"; 3479 ia2_role_ = IA2_ROLE_FORM; 3480 break; 3481 case ui::AX_ROLE_FOOTER: 3482 ia_role_ = IA2_ROLE_FOOTER; 3483 ia_state_ |= STATE_SYSTEM_READONLY; 3484 break; 3485 case ui::AX_ROLE_GRID: 3486 ia_role_ = ROLE_SYSTEM_TABLE; 3487 ia_state_ |= STATE_SYSTEM_READONLY; 3488 break; 3489 case ui::AX_ROLE_GROUP: { 3490 base::string16 aria_role = GetString16Attribute( 3491 ui::AX_ATTR_ROLE); 3492 if (aria_role == L"group" || html_tag == L"fieldset") { 3493 ia_role_ = ROLE_SYSTEM_GROUPING; 3494 } else if (html_tag == L"li") { 3495 ia_role_ = ROLE_SYSTEM_LISTITEM; 3496 } else { 3497 if (html_tag.empty()) 3498 role_name_ = L"div"; 3499 else 3500 role_name_ = html_tag; 3501 ia2_role_ = IA2_ROLE_SECTION; 3502 } 3503 ia_state_ |= STATE_SYSTEM_READONLY; 3504 break; 3505 } 3506 case ui::AX_ROLE_GROW_AREA: 3507 ia_role_ = ROLE_SYSTEM_GRIP; 3508 ia_state_ |= STATE_SYSTEM_READONLY; 3509 break; 3510 case ui::AX_ROLE_HEADING: 3511 role_name_ = html_tag; 3512 ia2_role_ = IA2_ROLE_HEADING; 3513 ia_state_ |= STATE_SYSTEM_READONLY; 3514 break; 3515 case ui::AX_ROLE_HORIZONTAL_RULE: 3516 ia_role_ = ROLE_SYSTEM_SEPARATOR; 3517 break; 3518 case ui::AX_ROLE_IFRAME: 3519 ia_role_ = ROLE_SYSTEM_CLIENT; 3520 ia2_role_ = IA2_ROLE_INTERNAL_FRAME; 3521 break; 3522 case ui::AX_ROLE_IMAGE: 3523 ia_role_ = ROLE_SYSTEM_GRAPHIC; 3524 ia_state_ |= STATE_SYSTEM_READONLY; 3525 break; 3526 case ui::AX_ROLE_IMAGE_MAP: 3527 role_name_ = html_tag; 3528 ia2_role_ = IA2_ROLE_IMAGE_MAP; 3529 ia_state_ |= STATE_SYSTEM_READONLY; 3530 break; 3531 case ui::AX_ROLE_IMAGE_MAP_LINK: 3532 ia_role_ = ROLE_SYSTEM_LINK; 3533 ia_state_ |= STATE_SYSTEM_LINKED; 3534 ia_state_ |= STATE_SYSTEM_READONLY; 3535 break; 3536 case ui::AX_ROLE_LABEL_TEXT: 3537 ia_role_ = ROLE_SYSTEM_TEXT; 3538 ia2_role_ = IA2_ROLE_LABEL; 3539 break; 3540 case ui::AX_ROLE_BANNER: 3541 case ui::AX_ROLE_COMPLEMENTARY: 3542 case ui::AX_ROLE_CONTENT_INFO: 3543 case ui::AX_ROLE_MAIN: 3544 case ui::AX_ROLE_NAVIGATION: 3545 case ui::AX_ROLE_SEARCH: 3546 ia_role_ = ROLE_SYSTEM_GROUPING; 3547 ia2_role_ = IA2_ROLE_SECTION; 3548 ia_state_ |= STATE_SYSTEM_READONLY; 3549 break; 3550 case ui::AX_ROLE_LINK: 3551 ia_role_ = ROLE_SYSTEM_LINK; 3552 ia_state_ |= STATE_SYSTEM_LINKED; 3553 break; 3554 case ui::AX_ROLE_LIST: 3555 ia_role_ = ROLE_SYSTEM_LIST; 3556 ia_state_ |= STATE_SYSTEM_READONLY; 3557 break; 3558 case ui::AX_ROLE_LIST_BOX: 3559 ia_role_ = ROLE_SYSTEM_LIST; 3560 break; 3561 case ui::AX_ROLE_LIST_BOX_OPTION: 3562 ia_role_ = ROLE_SYSTEM_LISTITEM; 3563 if (ia_state_ & STATE_SYSTEM_SELECTABLE) { 3564 ia_state_ |= STATE_SYSTEM_FOCUSABLE; 3565 if (HasState(ui::AX_STATE_FOCUSED)) 3566 ia_state_ |= STATE_SYSTEM_FOCUSED; 3567 } 3568 break; 3569 case ui::AX_ROLE_LIST_ITEM: 3570 ia_role_ = ROLE_SYSTEM_LISTITEM; 3571 ia_state_ |= STATE_SYSTEM_READONLY; 3572 break; 3573 case ui::AX_ROLE_MATH_ELEMENT: 3574 ia_role_ = ROLE_SYSTEM_EQUATION; 3575 ia_state_ |= STATE_SYSTEM_READONLY; 3576 break; 3577 case ui::AX_ROLE_MENU: 3578 case ui::AX_ROLE_MENU_BUTTON: 3579 ia_role_ = ROLE_SYSTEM_MENUPOPUP; 3580 break; 3581 case ui::AX_ROLE_MENU_BAR: 3582 ia_role_ = ROLE_SYSTEM_MENUBAR; 3583 break; 3584 case ui::AX_ROLE_MENU_ITEM: 3585 ia_role_ = ROLE_SYSTEM_MENUITEM; 3586 break; 3587 case ui::AX_ROLE_MENU_LIST_POPUP: 3588 ia_role_ = ROLE_SYSTEM_CLIENT; 3589 break; 3590 case ui::AX_ROLE_MENU_LIST_OPTION: 3591 ia_role_ = ROLE_SYSTEM_LISTITEM; 3592 if (ia_state_ & STATE_SYSTEM_SELECTABLE) { 3593 ia_state_ |= STATE_SYSTEM_FOCUSABLE; 3594 if (HasState(ui::AX_STATE_FOCUSED)) 3595 ia_state_ |= STATE_SYSTEM_FOCUSED; 3596 } 3597 break; 3598 case ui::AX_ROLE_NOTE: 3599 ia_role_ = ROLE_SYSTEM_GROUPING; 3600 ia2_role_ = IA2_ROLE_NOTE; 3601 ia_state_ |= STATE_SYSTEM_READONLY; 3602 break; 3603 case ui::AX_ROLE_OUTLINE: 3604 ia_role_ = ROLE_SYSTEM_OUTLINE; 3605 ia_state_ |= STATE_SYSTEM_READONLY; 3606 break; 3607 case ui::AX_ROLE_PARAGRAPH: 3608 role_name_ = L"P"; 3609 ia2_role_ = IA2_ROLE_PARAGRAPH; 3610 break; 3611 case ui::AX_ROLE_POP_UP_BUTTON: 3612 if (html_tag == L"select") { 3613 ia_role_ = ROLE_SYSTEM_COMBOBOX; 3614 } else { 3615 ia_role_ = ROLE_SYSTEM_BUTTONMENU; 3616 } 3617 break; 3618 case ui::AX_ROLE_PROGRESS_INDICATOR: 3619 ia_role_ = ROLE_SYSTEM_PROGRESSBAR; 3620 ia_state_ |= STATE_SYSTEM_READONLY; 3621 break; 3622 case ui::AX_ROLE_RADIO_BUTTON: 3623 ia_role_ = ROLE_SYSTEM_RADIOBUTTON; 3624 break; 3625 case ui::AX_ROLE_RADIO_GROUP: 3626 ia_role_ = ROLE_SYSTEM_GROUPING; 3627 ia2_role_ = IA2_ROLE_SECTION; 3628 break; 3629 case ui::AX_ROLE_REGION: 3630 ia_role_ = ROLE_SYSTEM_GROUPING; 3631 ia2_role_ = IA2_ROLE_SECTION; 3632 ia_state_ |= STATE_SYSTEM_READONLY; 3633 break; 3634 case ui::AX_ROLE_ROW: 3635 ia_role_ = ROLE_SYSTEM_ROW; 3636 ia_state_ |= STATE_SYSTEM_READONLY; 3637 break; 3638 case ui::AX_ROLE_ROW_HEADER: 3639 ia_role_ = ROLE_SYSTEM_ROWHEADER; 3640 ia_state_ |= STATE_SYSTEM_READONLY; 3641 break; 3642 case ui::AX_ROLE_RULER: 3643 ia_role_ = ROLE_SYSTEM_CLIENT; 3644 ia2_role_ = IA2_ROLE_RULER; 3645 ia_state_ |= STATE_SYSTEM_READONLY; 3646 break; 3647 case ui::AX_ROLE_SCROLL_AREA: 3648 ia_role_ = ROLE_SYSTEM_CLIENT; 3649 ia2_role_ = IA2_ROLE_SCROLL_PANE; 3650 ia_state_ |= STATE_SYSTEM_READONLY; 3651 ia2_state_ &= ~(IA2_STATE_EDITABLE); 3652 break; 3653 case ui::AX_ROLE_SCROLL_BAR: 3654 ia_role_ = ROLE_SYSTEM_SCROLLBAR; 3655 break; 3656 case ui::AX_ROLE_SLIDER: 3657 ia_role_ = ROLE_SYSTEM_SLIDER; 3658 break; 3659 case ui::AX_ROLE_SPIN_BUTTON: 3660 ia_role_ = ROLE_SYSTEM_SPINBUTTON; 3661 break; 3662 case ui::AX_ROLE_SPIN_BUTTON_PART: 3663 ia_role_ = ROLE_SYSTEM_PUSHBUTTON; 3664 break; 3665 case ui::AX_ROLE_SPLIT_GROUP: 3666 ia_role_ = ROLE_SYSTEM_CLIENT; 3667 ia2_role_ = IA2_ROLE_SPLIT_PANE; 3668 ia_state_ |= STATE_SYSTEM_READONLY; 3669 break; 3670 case ui::AX_ROLE_ANNOTATION: 3671 case ui::AX_ROLE_LIST_MARKER: 3672 case ui::AX_ROLE_STATIC_TEXT: 3673 ia_role_ = ROLE_SYSTEM_STATICTEXT; 3674 break; 3675 case ui::AX_ROLE_STATUS: 3676 ia_role_ = ROLE_SYSTEM_STATUSBAR; 3677 ia_state_ |= STATE_SYSTEM_READONLY; 3678 break; 3679 case ui::AX_ROLE_SPLITTER: 3680 ia_role_ = ROLE_SYSTEM_SEPARATOR; 3681 break; 3682 case ui::AX_ROLE_SVG_ROOT: 3683 ia_role_ = ROLE_SYSTEM_GRAPHIC; 3684 break; 3685 case ui::AX_ROLE_TAB: 3686 ia_role_ = ROLE_SYSTEM_PAGETAB; 3687 break; 3688 case ui::AX_ROLE_TABLE: { 3689 base::string16 aria_role = GetString16Attribute( 3690 ui::AX_ATTR_ROLE); 3691 if (aria_role == L"treegrid") { 3692 ia_role_ = ROLE_SYSTEM_OUTLINE; 3693 } else { 3694 ia_role_ = ROLE_SYSTEM_TABLE; 3695 ia_state_ |= STATE_SYSTEM_READONLY; 3696 } 3697 break; 3698 } 3699 case ui::AX_ROLE_TABLE_HEADER_CONTAINER: 3700 ia_role_ = ROLE_SYSTEM_GROUPING; 3701 ia2_role_ = IA2_ROLE_SECTION; 3702 ia_state_ |= STATE_SYSTEM_READONLY; 3703 break; 3704 case ui::AX_ROLE_TAB_LIST: 3705 ia_role_ = ROLE_SYSTEM_PAGETABLIST; 3706 break; 3707 case ui::AX_ROLE_TAB_PANEL: 3708 ia_role_ = ROLE_SYSTEM_PROPERTYPAGE; 3709 break; 3710 case ui::AX_ROLE_TOGGLE_BUTTON: 3711 ia_role_ = ROLE_SYSTEM_PUSHBUTTON; 3712 ia2_role_ = IA2_ROLE_TOGGLE_BUTTON; 3713 break; 3714 case ui::AX_ROLE_TEXT_AREA: 3715 ia_role_ = ROLE_SYSTEM_TEXT; 3716 ia2_state_ |= IA2_STATE_MULTI_LINE; 3717 ia2_state_ |= IA2_STATE_EDITABLE; 3718 ia2_state_ |= IA2_STATE_SELECTABLE_TEXT; 3719 break; 3720 case ui::AX_ROLE_TEXT_FIELD: 3721 ia_role_ = ROLE_SYSTEM_TEXT; 3722 ia2_state_ |= IA2_STATE_SINGLE_LINE; 3723 ia2_state_ |= IA2_STATE_EDITABLE; 3724 ia2_state_ |= IA2_STATE_SELECTABLE_TEXT; 3725 break; 3726 case ui::AX_ROLE_TIMER: 3727 ia_role_ = ROLE_SYSTEM_CLOCK; 3728 ia_state_ |= STATE_SYSTEM_READONLY; 3729 break; 3730 case ui::AX_ROLE_TOOLBAR: 3731 ia_role_ = ROLE_SYSTEM_TOOLBAR; 3732 ia_state_ |= STATE_SYSTEM_READONLY; 3733 break; 3734 case ui::AX_ROLE_TOOLTIP: 3735 ia_role_ = ROLE_SYSTEM_TOOLTIP; 3736 ia_state_ |= STATE_SYSTEM_READONLY; 3737 break; 3738 case ui::AX_ROLE_TREE: 3739 ia_role_ = ROLE_SYSTEM_OUTLINE; 3740 ia_state_ |= STATE_SYSTEM_READONLY; 3741 break; 3742 case ui::AX_ROLE_TREE_GRID: 3743 ia_role_ = ROLE_SYSTEM_OUTLINE; 3744 ia_state_ |= STATE_SYSTEM_READONLY; 3745 break; 3746 case ui::AX_ROLE_TREE_ITEM: 3747 ia_role_ = ROLE_SYSTEM_OUTLINEITEM; 3748 ia_state_ |= STATE_SYSTEM_READONLY; 3749 break; 3750 case ui::AX_ROLE_WINDOW: 3751 ia_role_ = ROLE_SYSTEM_WINDOW; 3752 break; 3753 3754 // TODO(dmazzoni): figure out the proper MSAA role for all of these. 3755 case ui::AX_ROLE_BROWSER: 3756 case ui::AX_ROLE_DIRECTORY: 3757 case ui::AX_ROLE_DRAWER: 3758 case ui::AX_ROLE_HELP_TAG: 3759 case ui::AX_ROLE_IGNORED: 3760 case ui::AX_ROLE_INCREMENTOR: 3761 case ui::AX_ROLE_LOG: 3762 case ui::AX_ROLE_MARQUEE: 3763 case ui::AX_ROLE_MATTE: 3764 case ui::AX_ROLE_PRESENTATIONAL: 3765 case ui::AX_ROLE_RULER_MARKER: 3766 case ui::AX_ROLE_SHEET: 3767 case ui::AX_ROLE_SLIDER_THUMB: 3768 case ui::AX_ROLE_SYSTEM_WIDE: 3769 case ui::AX_ROLE_VALUE_INDICATOR: 3770 default: 3771 ia_role_ = ROLE_SYSTEM_CLIENT; 3772 break; 3773 } 3774 3775 // Compute the final value of READONLY for MSAA. 3776 // 3777 // We always set the READONLY state for elements that have the 3778 // aria-readonly attribute and for a few roles (in the switch above). 3779 // We clear the READONLY state on focusable controls and on a document. 3780 // Everything else, the majority of objects, do not have this state set. 3781 if (HasState(ui::AX_STATE_FOCUSABLE) && 3782 ia_role_ != ROLE_SYSTEM_DOCUMENT) { 3783 ia_state_ &= ~(STATE_SYSTEM_READONLY); 3784 } 3785 if (!HasState(ui::AX_STATE_READ_ONLY)) 3786 ia_state_ &= ~(STATE_SYSTEM_READONLY); 3787 if (GetBoolAttribute(ui::AX_ATTR_ARIA_READONLY)) 3788 ia_state_ |= STATE_SYSTEM_READONLY; 3789 3790 // The role should always be set. 3791 DCHECK(!role_name_.empty() || ia_role_); 3792 3793 // If we didn't explicitly set the IAccessible2 role, make it the same 3794 // as the MSAA role. 3795 if (!ia2_role_) 3796 ia2_role_ = ia_role_; 3797 } 3798 3799 } // namespace content 3800