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