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