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