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