1/* 2 * Copyright (C) 2008, 2009, 2010, 2013 Apple Inc. All Rights Reserved. 3 * Copyright (C) 2012 Serotek Corporation. All Rights Reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "WebKitDLL.h" 29#include "AccessibleBase.h" 30 31#include "AccessibleImage.h" 32#include "AccessibleTextImpl.h" 33#include "WebView.h" 34#include <WebCore/AccessibilityListBox.h> 35#include <WebCore/AccessibilityMenuListPopup.h> 36#include <WebCore/AccessibilityObject.h> 37#include <WebCore/AXObjectCache.h> 38#include <WebCore/BString.h> 39#include <WebCore/Element.h> 40#include <WebCore/EventHandler.h> 41#include <WebCore/FrameView.h> 42#include <WebCore/HostWindow.h> 43#include <WebCore/HTMLNames.h> 44#include <WebCore/HTMLFrameElementBase.h> 45#include <WebCore/HTMLInputElement.h> 46#include <WebCore/IntRect.h> 47#include <WebCore/NotImplemented.h> 48#include <WebCore/PlatformEvent.h> 49#include <WebCore/RenderFrame.h> 50#include <WebCore/RenderObject.h> 51#include <WebCore/RenderView.h> 52#include <comutil.h> 53#include <oleacc.h> 54#include <wtf/RefPtr.h> 55#include <wtf/text/StringBuilder.h> 56 57using namespace WebCore; 58 59AccessibleBase::AccessibleBase(AccessibilityObject* obj, HWND window) 60 : AccessibilityObjectWrapper(obj) 61 , m_window(window) 62 , m_refCount(0) 63{ 64 ASSERT_ARG(obj, obj); 65 m_object->setWrapper(this); 66 ++gClassCount; 67 gClassNameCount.add("AccessibleBase"); 68} 69 70AccessibleBase::~AccessibleBase() 71{ 72 --gClassCount; 73 gClassNameCount.remove("AccessibleBase"); 74} 75 76AccessibleBase* AccessibleBase::createInstance(AccessibilityObject* obj, HWND window) 77{ 78 ASSERT_ARG(obj, obj); 79 80 if (obj->isImage()) 81 return new AccessibleImage(obj, window); 82 else if (obj->isStaticText() || obj->isTextControl() || (obj->node() && obj->node()->isTextNode())) 83 return new AccessibleText(obj, window); 84 85 return new AccessibleBase(obj, window); 86} 87 88HRESULT AccessibleBase::QueryService(REFGUID guidService, REFIID riid, void **ppvObject) 89{ 90 if (!IsEqualGUID(guidService, SID_AccessibleComparable) 91 && !IsEqualGUID(guidService, IID_IAccessible2_2) 92 && !IsEqualGUID(guidService, IID_IAccessible2) 93 && !IsEqualGUID(guidService, IID_IAccessibleApplication) 94 && !IsEqualGUID(guidService, IID_IAccessible) 95 && !IsEqualGUID(guidService, IID_IAccessibleText) 96 && !IsEqualGUID(guidService, IID_IAccessibleText2) 97 && !IsEqualGUID(guidService, IID_IAccessibleEditableText)) { 98 *ppvObject = 0; 99 return E_INVALIDARG; 100 } 101 return QueryInterface(riid, ppvObject); 102} 103 104// IUnknown 105HRESULT STDMETHODCALLTYPE AccessibleBase::QueryInterface(REFIID riid, void** ppvObject) 106{ 107 if (IsEqualGUID(riid, __uuidof(IAccessible))) 108 *ppvObject = static_cast<IAccessible*>(this); 109 else if (IsEqualGUID(riid, __uuidof(IDispatch))) 110 *ppvObject = static_cast<IAccessible*>(this); 111 else if (IsEqualGUID(riid, __uuidof(IUnknown))) 112 *ppvObject = static_cast<IAccessible*>(this); 113 else if (IsEqualGUID(riid, __uuidof(IAccessible2_2))) 114 *ppvObject = static_cast<IAccessible2_2*>(this); 115 else if (IsEqualGUID(riid, __uuidof(IAccessible2))) 116 *ppvObject = static_cast<IAccessible2*>(this); 117 else if (IsEqualGUID(riid, __uuidof(IAccessibleComparable))) 118 *ppvObject = static_cast<IAccessibleComparable*>(this); 119 else if (IsEqualGUID(riid, __uuidof(IServiceProvider))) 120 *ppvObject = static_cast<IServiceProvider*>(this); 121 else if (IsEqualGUID(riid, __uuidof(AccessibleBase))) 122 *ppvObject = static_cast<AccessibleBase*>(this); 123 else { 124 *ppvObject = 0; 125 return E_NOINTERFACE; 126 } 127 AddRef(); 128 return S_OK; 129} 130 131ULONG STDMETHODCALLTYPE AccessibleBase::Release(void) 132{ 133 ASSERT(m_refCount > 0); 134 if (--m_refCount) 135 return m_refCount; 136 delete this; 137 return 0; 138} 139 140// IAccessible2_2 141HRESULT AccessibleBase::get_attribute(BSTR key, VARIANT* value) 142{ 143 if (!value) 144 return E_POINTER; 145 146 AtomicString keyAtomic(key, ::SysStringLen(key)); 147 148 accessibilityAttributeValue(keyAtomic, value); 149 150 return S_OK; 151} 152 153HRESULT AccessibleBase::get_accessibleWithCaret(IUnknown** accessible, long* caretOffset) 154{ 155 notImplemented(); 156 return E_NOTIMPL; 157} 158 159HRESULT AccessibleBase::get_relationTargetsOfType(BSTR type, long maxTargets, IUnknown*** targets, long* nTargets) 160{ 161 notImplemented(); 162 return E_NOTIMPL; 163} 164 165// IAccessible2 166HRESULT AccessibleBase::get_nRelations(long* nRelations) 167{ 168 if (!nRelations) 169 return E_POINTER; 170 171 if (!m_object) 172 return E_FAIL; 173 notImplemented(); 174 *nRelations = 0; 175 return S_OK; 176} 177 178HRESULT AccessibleBase::get_relation(long relationIndex, IAccessibleRelation** relation) 179{ 180 if (!relation) 181 return E_POINTER; 182 183 notImplemented(); 184 return E_NOTIMPL; 185} 186 187HRESULT AccessibleBase::get_relations(long maxRelations, IAccessibleRelation** relations, long* nRelations) 188{ 189 if (!relations || !nRelations) 190 return E_POINTER; 191 192 notImplemented(); 193 return E_NOTIMPL; 194} 195 196HRESULT AccessibleBase::role(long* role) 197{ 198 if (!role) 199 return E_POINTER; 200 201 if (!m_object) 202 return E_FAIL; 203 204 *role = wrapper(m_object)->role(); 205 return S_OK; 206} 207 208HRESULT AccessibleBase::scrollTo(IA2ScrollType scrollType) 209{ 210 if (!m_object) 211 return E_FAIL; 212 return S_FALSE; 213} 214 215HRESULT AccessibleBase::scrollToPoint(IA2CoordinateType coordinateType, long x, long y) 216{ 217 if (!m_object) 218 return E_FAIL; 219 return S_FALSE; 220} 221 222HRESULT AccessibleBase::get_groupPosition(long* groupLevel, long* similarItemsInGroup, long* positionInGroup) 223{ 224 notImplemented(); 225 return E_NOTIMPL; 226} 227 228HRESULT AccessibleBase::get_states(AccessibleStates* states) 229{ 230 if (!states) 231 return E_POINTER; 232 233 if (!m_object) 234 return E_FAIL; 235 236 *states = 0; 237 notImplemented(); 238 return S_OK; 239} 240 241HRESULT AccessibleBase::get_extendedRole(BSTR* extendedRole) 242{ 243 if (!extendedRole) 244 return E_POINTER; 245 246 if (!m_object) 247 return E_FAIL; 248 249 notImplemented(); 250 return S_FALSE; 251} 252 253HRESULT AccessibleBase::get_localizedExtendedRole(BSTR* localizedExtendedRole) 254{ 255 if (!localizedExtendedRole) 256 return E_POINTER; 257 258 if (!m_object) 259 return E_FAIL; 260 261 notImplemented(); 262 return S_FALSE; 263} 264 265HRESULT AccessibleBase::get_nExtendedStates(long* nExtendedStates) 266{ 267 if (!nExtendedStates) 268 return E_POINTER; 269 270 if (!m_object) 271 return E_FAIL; 272 273 notImplemented(); 274 *nExtendedStates = 0; 275 return S_OK; 276} 277 278HRESULT AccessibleBase::get_extendedStates(long maxExtendedStates, BSTR** extendedStates, long* nExtendedStates) 279{ 280 notImplemented(); 281 return E_NOTIMPL; 282} 283 284HRESULT AccessibleBase::get_localizedExtendedStates(long maxLocalizedExtendedStates, BSTR** localizedExtendedStates, long* nLocalizedExtendedStates) 285{ 286 notImplemented(); 287 return E_NOTIMPL; 288} 289 290HRESULT AccessibleBase::get_uniqueID(long* uniqueID) 291{ 292 if (!uniqueID) 293 return E_POINTER; 294 295 if (!m_object) 296 return E_FAIL; 297 298 *uniqueID = static_cast<long>(m_object->axObjectID()); 299 return S_OK; 300} 301 302HRESULT AccessibleBase::get_windowHandle(HWND* windowHandle) 303{ 304 *windowHandle = m_window; 305 return S_OK; 306} 307 308HRESULT AccessibleBase::get_indexInParent(long* indexInParent) 309{ 310 return E_NOTIMPL; 311} 312 313HRESULT AccessibleBase::get_locale(IA2Locale* locale) 314{ 315 notImplemented(); 316 return E_NOTIMPL; 317} 318 319HRESULT AccessibleBase::get_attributes(BSTR* attributes) 320{ 321 if (!m_object) 322 return E_FAIL; 323 notImplemented(); 324 return S_FALSE; 325} 326 327// IAccessible 328HRESULT AccessibleBase::get_accParent(IDispatch** parent) 329{ 330 *parent = 0; 331 332 if (!m_object) 333 return E_FAIL; 334 335 AccessibilityObject* parentObject = m_object->parentObjectUnignored(); 336 if (parentObject) { 337 *parent = wrapper(parentObject); 338 (*parent)->AddRef(); 339 return S_OK; 340 } 341 342 if (!m_window) 343 return E_FAIL; 344 345 return WebView::AccessibleObjectFromWindow(m_window, 346 OBJID_WINDOW, __uuidof(IAccessible), reinterpret_cast<void**>(parent)); 347} 348 349HRESULT STDMETHODCALLTYPE AccessibleBase::get_accChildCount(long* count) 350{ 351 if (!m_object) 352 return E_FAIL; 353 if (!count) 354 return E_POINTER; 355 *count = static_cast<long>(m_object->children().size()); 356 return S_OK; 357} 358 359HRESULT STDMETHODCALLTYPE AccessibleBase::get_accChild(VARIANT vChild, IDispatch** ppChild) 360{ 361 if (!ppChild) 362 return E_POINTER; 363 364 *ppChild = 0; 365 366 AccessibilityObject* childObj; 367 368 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj); 369 if (FAILED(hr)) 370 return hr; 371 372 *ppChild = static_cast<IDispatch*>(wrapper(childObj)); 373 (*ppChild)->AddRef(); 374 return S_OK; 375} 376 377HRESULT STDMETHODCALLTYPE AccessibleBase::get_accName(VARIANT vChild, BSTR* name) 378{ 379 if (!name) 380 return E_POINTER; 381 382 *name = 0; 383 384 AccessibilityObject* childObj; 385 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj); 386 387 if (FAILED(hr)) 388 return hr; 389 390 if (*name = BString(wrapper(childObj)->name()).release()) 391 return S_OK; 392 return S_FALSE; 393} 394 395HRESULT STDMETHODCALLTYPE AccessibleBase::get_accValue(VARIANT vChild, BSTR* value) 396{ 397 if (!value) 398 return E_POINTER; 399 400 *value = 0; 401 402 AccessibilityObject* childObj; 403 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj); 404 405 if (FAILED(hr)) 406 return hr; 407 408 if (*value = BString(wrapper(childObj)->value()).release()) 409 return S_OK; 410 return S_FALSE; 411} 412 413HRESULT STDMETHODCALLTYPE AccessibleBase::get_accDescription(VARIANT vChild, BSTR* description) 414{ 415 if (!description) 416 return E_POINTER; 417 418 *description = 0; 419 420 AccessibilityObject* childObj; 421 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj); 422 423 if (FAILED(hr)) 424 return hr; 425 426 if (*description = BString(childObj->descriptionForMSAA()).release()) 427 return S_OK; 428 429 return S_FALSE; 430} 431 432HRESULT STDMETHODCALLTYPE AccessibleBase::get_accRole(VARIANT vChild, VARIANT* pvRole) 433{ 434 if (!pvRole) 435 return E_POINTER; 436 437 ::VariantInit(pvRole); 438 439 AccessibilityObject* childObj; 440 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj); 441 442 if (FAILED(hr)) 443 return hr; 444 445 String roleString = childObj->stringRoleForMSAA(); 446 if (!roleString.isEmpty()) { 447 V_VT(pvRole) = VT_BSTR; 448 V_BSTR(pvRole) = BString(roleString).release(); 449 return S_OK; 450 } 451 452 pvRole->vt = VT_I4; 453 pvRole->lVal = wrapper(childObj)->role(); 454 return S_OK; 455} 456 457long AccessibleBase::state() const 458{ 459 long state = 0; 460 if (m_object->isLinked()) 461 state |= STATE_SYSTEM_LINKED; 462 463 if (m_object->isHovered()) 464 state |= STATE_SYSTEM_HOTTRACKED; 465 466 if (!m_object->isEnabled()) 467 state |= STATE_SYSTEM_UNAVAILABLE; 468 469 if (m_object->isReadOnly()) 470 state |= STATE_SYSTEM_READONLY; 471 472 if (m_object->isOffScreen()) 473 state |= STATE_SYSTEM_OFFSCREEN; 474 475 if (m_object->isPasswordField()) 476 state |= STATE_SYSTEM_PROTECTED; 477 478 if (m_object->isIndeterminate()) 479 state |= STATE_SYSTEM_INDETERMINATE; 480 481 if (m_object->isChecked()) 482 state |= STATE_SYSTEM_CHECKED; 483 484 if (m_object->isPressed()) 485 state |= STATE_SYSTEM_PRESSED; 486 487 if (m_object->isFocused()) 488 state |= STATE_SYSTEM_FOCUSED; 489 490 if (m_object->isVisited()) 491 state |= STATE_SYSTEM_TRAVERSED; 492 493 if (m_object->canSetFocusAttribute()) 494 state |= STATE_SYSTEM_FOCUSABLE; 495 496 if (m_object->isSelected()) 497 state |= STATE_SYSTEM_SELECTED; 498 499 if (m_object->canSetSelectedAttribute()) 500 state |= STATE_SYSTEM_SELECTABLE; 501 502 if (m_object->isMultiSelectable()) 503 state |= STATE_SYSTEM_EXTSELECTABLE | STATE_SYSTEM_MULTISELECTABLE; 504 505 if (!m_object->isVisible()) 506 state |= STATE_SYSTEM_INVISIBLE; 507 508 if (m_object->isCollapsed()) 509 state |= STATE_SYSTEM_COLLAPSED; 510 511 if (m_object->roleValue() == PopUpButtonRole) { 512 state |= STATE_SYSTEM_HASPOPUP; 513 514 if (!m_object->isCollapsed()) 515 state |= STATE_SYSTEM_EXPANDED; 516 } 517 518 return state; 519} 520 521HRESULT AccessibleBase::get_accState(VARIANT vChild, VARIANT* pvState) 522{ 523 if (!pvState) 524 return E_POINTER; 525 526 ::VariantInit(pvState); 527 528 AccessibilityObject* childObj; 529 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj); 530 531 if (FAILED(hr)) 532 return hr; 533 534 pvState->vt = VT_I4; 535 pvState->lVal = wrapper(childObj)->state(); 536 537 return S_OK; 538} 539 540HRESULT STDMETHODCALLTYPE AccessibleBase::get_accHelp(VARIANT vChild, BSTR* helpText) 541{ 542 if (!helpText) 543 return E_POINTER; 544 545 *helpText = 0; 546 547 AccessibilityObject* childObj; 548 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj); 549 550 if (FAILED(hr)) 551 return hr; 552 553 if (*helpText = BString(childObj->helpText()).release()) 554 return S_OK; 555 return S_FALSE; 556} 557 558HRESULT STDMETHODCALLTYPE AccessibleBase::get_accKeyboardShortcut(VARIANT vChild, BSTR* shortcut) 559{ 560 if (!shortcut) 561 return E_POINTER; 562 563 *shortcut = 0; 564 565 AccessibilityObject* childObj; 566 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj); 567 568 if (FAILED(hr)) 569 return hr; 570 571 String accessKey = childObj->accessKey(); 572 if (accessKey.isNull()) 573 return S_FALSE; 574 575 static String accessKeyModifiers; 576 if (accessKeyModifiers.isNull()) { 577 StringBuilder accessKeyModifiersBuilder; 578 unsigned modifiers = EventHandler::accessKeyModifiers(); 579 // Follow the same order as Mozilla MSAA implementation: 580 // Ctrl+Alt+Shift+Meta+key. MSDN states that keyboard shortcut strings 581 // should not be localized and defines the separator as "+". 582 if (modifiers & PlatformEvent::CtrlKey) 583 accessKeyModifiersBuilder.appendLiteral("Ctrl+"); 584 if (modifiers & PlatformEvent::AltKey) 585 accessKeyModifiersBuilder.appendLiteral("Alt+"); 586 if (modifiers & PlatformEvent::ShiftKey) 587 accessKeyModifiersBuilder.appendLiteral("Shift+"); 588 if (modifiers & PlatformEvent::MetaKey) 589 accessKeyModifiersBuilder.appendLiteral("Win+"); 590 accessKeyModifiers = accessKeyModifiersBuilder.toString(); 591 } 592 *shortcut = BString(String(accessKeyModifiers + accessKey)).release(); 593 return S_OK; 594} 595 596HRESULT STDMETHODCALLTYPE AccessibleBase::accSelect(long selectionFlags, VARIANT vChild) 597{ 598 // According to MSDN, these combinations are invalid. 599 if (((selectionFlags & (SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION)) == (SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION)) 600 || ((selectionFlags & (SELFLAG_ADDSELECTION | SELFLAG_TAKESELECTION)) == (SELFLAG_ADDSELECTION | SELFLAG_TAKESELECTION)) 601 || ((selectionFlags & (SELFLAG_REMOVESELECTION | SELFLAG_TAKESELECTION)) == (SELFLAG_REMOVESELECTION | SELFLAG_TAKESELECTION)) 602 || ((selectionFlags & (SELFLAG_EXTENDSELECTION | SELFLAG_TAKESELECTION)) == (SELFLAG_EXTENDSELECTION | SELFLAG_TAKESELECTION))) 603 return E_INVALIDARG; 604 605 AccessibilityObject* childObject; 606 HRESULT hr = getAccessibilityObjectForChild(vChild, childObject); 607 608 if (FAILED(hr)) 609 return hr; 610 611 if (selectionFlags & SELFLAG_TAKEFOCUS) 612 childObject->setFocused(true); 613 614 AccessibilityObject* parentObject = childObject->parentObject(); 615 if (!parentObject) 616 return E_INVALIDARG; 617 618 if (selectionFlags & SELFLAG_TAKESELECTION) { 619 if (parentObject->isListBox()) { 620 Vector<RefPtr<AccessibilityObject> > selectedChildren(1); 621 selectedChildren[0] = childObject; 622 static_cast<AccessibilityListBox*>(parentObject)->setSelectedChildren(selectedChildren); 623 } else { // any element may be selectable by virtue of it having the aria-selected property 624 ASSERT(!parentObject->isMultiSelectable()); 625 childObject->setSelected(true); 626 } 627 } 628 629 // MSDN says that ADD, REMOVE, and EXTENDSELECTION with no other flags are invalid for 630 // single-select. 631 const long allSELFLAGs = SELFLAG_TAKEFOCUS | SELFLAG_TAKESELECTION | SELFLAG_EXTENDSELECTION | SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION; 632 if (!parentObject->isMultiSelectable() 633 && (((selectionFlags & allSELFLAGs) == SELFLAG_ADDSELECTION) 634 || ((selectionFlags & allSELFLAGs) == SELFLAG_REMOVESELECTION) 635 || ((selectionFlags & allSELFLAGs) == SELFLAG_EXTENDSELECTION))) 636 return E_INVALIDARG; 637 638 if (selectionFlags & SELFLAG_ADDSELECTION) 639 childObject->setSelected(true); 640 641 if (selectionFlags & SELFLAG_REMOVESELECTION) 642 childObject->setSelected(false); 643 644 // FIXME: Should implement SELFLAG_EXTENDSELECTION. For now, we just return 645 // S_OK, matching Firefox. 646 647 return S_OK; 648} 649 650HRESULT STDMETHODCALLTYPE AccessibleBase::get_accSelection(VARIANT*) 651{ 652 return E_NOTIMPL; 653} 654 655HRESULT STDMETHODCALLTYPE AccessibleBase::get_accFocus(VARIANT* pvFocusedChild) 656{ 657 if (!pvFocusedChild) 658 return E_POINTER; 659 660 ::VariantInit(pvFocusedChild); 661 662 if (!m_object) 663 return E_FAIL; 664 665 AccessibilityObject* focusedObj = m_object->focusedUIElement(); 666 if (!focusedObj) 667 return S_FALSE; 668 669 if (focusedObj == m_object) { 670 V_VT(pvFocusedChild) = VT_I4; 671 V_I4(pvFocusedChild) = CHILDID_SELF; 672 return S_OK; 673 } 674 675 V_VT(pvFocusedChild) = VT_DISPATCH; 676 V_DISPATCH(pvFocusedChild) = wrapper(focusedObj); 677 V_DISPATCH(pvFocusedChild)->AddRef(); 678 return S_OK; 679} 680 681HRESULT STDMETHODCALLTYPE AccessibleBase::get_accDefaultAction(VARIANT vChild, BSTR* action) 682{ 683 if (!action) 684 return E_POINTER; 685 686 *action = 0; 687 688 AccessibilityObject* childObj; 689 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj); 690 691 if (FAILED(hr)) 692 return hr; 693 694 if (*action = BString(childObj->actionVerb()).release()) 695 return S_OK; 696 return S_FALSE; 697} 698 699HRESULT STDMETHODCALLTYPE AccessibleBase::accLocation(long* left, long* top, long* width, long* height, VARIANT vChild) 700{ 701 if (!left || !top || !width || !height) 702 return E_POINTER; 703 704 *left = *top = *width = *height = 0; 705 706 AccessibilityObject* childObj; 707 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj); 708 709 if (FAILED(hr)) 710 return hr; 711 712 if (!childObj->documentFrameView()) 713 return E_FAIL; 714 715 IntRect screenRect(childObj->documentFrameView()->contentsToScreen(childObj->pixelSnappedElementRect())); 716 *left = screenRect.x(); 717 *top = screenRect.y(); 718 *width = screenRect.width(); 719 *height = screenRect.height(); 720 return S_OK; 721} 722 723HRESULT STDMETHODCALLTYPE AccessibleBase::accNavigate(long direction, VARIANT vFromChild, VARIANT* pvNavigatedTo) 724{ 725 if (!pvNavigatedTo) 726 return E_POINTER; 727 728 ::VariantInit(pvNavigatedTo); 729 730 AccessibilityObject* childObj = 0; 731 732 switch (direction) { 733 case NAVDIR_DOWN: 734 case NAVDIR_UP: 735 case NAVDIR_LEFT: 736 case NAVDIR_RIGHT: 737 // These directions are not implemented, matching Mozilla and IE. 738 return E_NOTIMPL; 739 case NAVDIR_LASTCHILD: 740 case NAVDIR_FIRSTCHILD: 741 // MSDN states that navigating to first/last child can only be from self. 742 if (vFromChild.lVal != CHILDID_SELF) 743 return E_INVALIDARG; 744 745 if (!m_object) 746 return E_FAIL; 747 748 if (direction == NAVDIR_FIRSTCHILD) 749 childObj = m_object->firstChild(); 750 else 751 childObj = m_object->lastChild(); 752 break; 753 case NAVDIR_NEXT: 754 case NAVDIR_PREVIOUS: { 755 // Navigating to next and previous is allowed from self or any of our children. 756 HRESULT hr = getAccessibilityObjectForChild(vFromChild, childObj); 757 if (FAILED(hr)) 758 return hr; 759 760 if (direction == NAVDIR_NEXT) 761 childObj = childObj->nextSibling(); 762 else 763 childObj = childObj->previousSibling(); 764 break; 765 } 766 default: 767 ASSERT_NOT_REACHED(); 768 return E_INVALIDARG; 769 } 770 771 if (!childObj) 772 return S_FALSE; 773 774 V_VT(pvNavigatedTo) = VT_DISPATCH; 775 V_DISPATCH(pvNavigatedTo) = wrapper(childObj); 776 V_DISPATCH(pvNavigatedTo)->AddRef(); 777 return S_OK; 778} 779 780HRESULT STDMETHODCALLTYPE AccessibleBase::accHitTest(long x, long y, VARIANT* pvChildAtPoint) 781{ 782 if (!pvChildAtPoint) 783 return E_POINTER; 784 785 ::VariantInit(pvChildAtPoint); 786 787 if (!m_object || !m_object->documentFrameView()) 788 return E_FAIL; 789 790 IntPoint point = m_object->documentFrameView()->screenToContents(IntPoint(x, y)); 791 AccessibilityObject* childObj = m_object->accessibilityHitTest(point); 792 793 if (!childObj) { 794 // If we did not hit any child objects, test whether the point hit us, and 795 // report that. 796 if (!m_object->boundingBoxRect().contains(point)) 797 return S_FALSE; 798 childObj = m_object; 799 } 800 801 if (childObj == m_object) { 802 V_VT(pvChildAtPoint) = VT_I4; 803 V_I4(pvChildAtPoint) = CHILDID_SELF; 804 } else { 805 V_VT(pvChildAtPoint) = VT_DISPATCH; 806 V_DISPATCH(pvChildAtPoint) = static_cast<IDispatch*>(wrapper(childObj)); 807 V_DISPATCH(pvChildAtPoint)->AddRef(); 808 } 809 return S_OK; 810} 811 812HRESULT STDMETHODCALLTYPE AccessibleBase::accDoDefaultAction(VARIANT vChild) 813{ 814 AccessibilityObject* childObj; 815 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj); 816 817 if (FAILED(hr)) 818 return hr; 819 820 if (!childObj->performDefaultAction()) 821 return S_FALSE; 822 823 return S_OK; 824} 825 826// AccessibleBase 827String AccessibleBase::name() const 828{ 829 return m_object->nameForMSAA(); 830} 831 832String AccessibleBase::value() const 833{ 834 return m_object->stringValueForMSAA(); 835} 836 837static long MSAARole(AccessibilityRole role) 838{ 839 switch (role) { 840 case WebCore::ButtonRole: 841 return ROLE_SYSTEM_PUSHBUTTON; 842 case WebCore::RadioButtonRole: 843 return ROLE_SYSTEM_RADIOBUTTON; 844 case WebCore::CheckBoxRole: 845 return ROLE_SYSTEM_CHECKBUTTON; 846 case WebCore::SliderRole: 847 return ROLE_SYSTEM_SLIDER; 848 case WebCore::TabGroupRole: 849 case WebCore::TabListRole: 850 return ROLE_SYSTEM_PAGETABLIST; 851 case WebCore::TextFieldRole: 852 case WebCore::TextAreaRole: 853 case WebCore::EditableTextRole: 854 return ROLE_SYSTEM_TEXT; 855 case WebCore::HeadingRole: 856 case WebCore::ListMarkerRole: 857 case WebCore::StaticTextRole: 858 return ROLE_SYSTEM_STATICTEXT; 859 case WebCore::OutlineRole: 860 return ROLE_SYSTEM_OUTLINE; 861 case WebCore::ColumnRole: 862 return ROLE_SYSTEM_COLUMN; 863 case WebCore::RowRole: 864 return ROLE_SYSTEM_ROW; 865 case WebCore::GroupRole: 866 case WebCore::RadioGroupRole: 867 return ROLE_SYSTEM_GROUPING; 868 case WebCore::DescriptionListRole: 869 case WebCore::DirectoryRole: 870 case WebCore::ListRole: 871 case WebCore::ListBoxRole: 872 case WebCore::MenuListPopupRole: 873 return ROLE_SYSTEM_LIST; 874 case WebCore::GridRole: 875 case WebCore::TableRole: 876 return ROLE_SYSTEM_TABLE; 877 case WebCore::ImageMapLinkRole: 878 case WebCore::LinkRole: 879 case WebCore::WebCoreLinkRole: 880 return ROLE_SYSTEM_LINK; 881 case WebCore::CanvasRole: 882 case WebCore::ImageMapRole: 883 case WebCore::ImageRole: 884 return ROLE_SYSTEM_GRAPHIC; 885 case WebCore::ListItemRole: 886 return ROLE_SYSTEM_LISTITEM; 887 case WebCore::ListBoxOptionRole: 888 case WebCore::MenuListOptionRole: 889 return ROLE_SYSTEM_STATICTEXT; 890 case WebCore::ComboBoxRole: 891 case WebCore::PopUpButtonRole: 892 return ROLE_SYSTEM_COMBOBOX; 893 case WebCore::DivRole: 894 case WebCore::FooterRole: 895 case WebCore::FormRole: 896 case WebCore::LabelRole: 897 case WebCore::ParagraphRole: 898 return ROLE_SYSTEM_GROUPING; 899 case WebCore::HorizontalRuleRole: 900 case WebCore::SplitterRole: 901 return ROLE_SYSTEM_SEPARATOR; 902 case WebCore::ApplicationAlertRole: 903 case WebCore::ApplicationAlertDialogRole: 904 return ROLE_SYSTEM_ALERT; 905 case WebCore::DisclosureTriangleRole: 906 return ROLE_SYSTEM_BUTTONDROPDOWN; 907 case WebCore::SeamlessWebAreaRole: 908 return ROLE_SYSTEM_GROUPING; 909 case WebCore::IncrementorRole: 910 case WebCore::SpinButtonRole: 911 return ROLE_SYSTEM_SPINBUTTON; 912 case WebCore::SpinButtonPartRole: 913 return ROLE_SYSTEM_PUSHBUTTON; 914 case WebCore::ToggleButtonRole: 915 return ROLE_SYSTEM_PUSHBUTTON; 916 case WebCore::ToolbarRole: 917 return ROLE_SYSTEM_TOOLBAR; 918 case WebCore::UserInterfaceTooltipRole: 919 return ROLE_SYSTEM_TOOLTIP; 920 case WebCore::TreeRole: 921 case WebCore::TreeGridRole: 922 return ROLE_SYSTEM_OUTLINE; 923 case WebCore::TreeItemRole: 924 return ROLE_SYSTEM_OUTLINEITEM; 925 case WebCore::TabPanelRole: 926 return ROLE_SYSTEM_GROUPING; 927 case WebCore::TabRole: 928 return ROLE_SYSTEM_PAGETAB; 929 case WebCore::ApplicationRole: 930 return ROLE_SYSTEM_APPLICATION; 931 case WebCore::ApplicationDialogRole: 932 return ROLE_SYSTEM_DIALOG; 933 case WebCore::ApplicationLogRole: 934 case WebCore::ApplicationMarqueeRole: 935 return ROLE_SYSTEM_GROUPING; 936 case WebCore::ApplicationStatusRole: 937 return ROLE_SYSTEM_STATUSBAR; 938 case WebCore::ApplicationTimerRole: 939 return ROLE_SYSTEM_CLOCK; 940 case WebCore::CellRole: 941 return ROLE_SYSTEM_CELL; 942 case WebCore::ColumnHeaderRole: 943 return ROLE_SYSTEM_COLUMNHEADER; 944 case WebCore::DefinitionRole: 945 case WebCore::DescriptionListDetailRole: 946 case WebCore::DescriptionListTermRole: 947 case WebCore::DocumentRole: 948 case WebCore::DocumentArticleRole: 949 case WebCore::DocumentNoteRole: 950 case WebCore::DocumentRegionRole: 951 return ROLE_SYSTEM_GROUPING; 952 case WebCore::DocumentMathRole: 953 case WebCore::MathElementRole: 954 return ROLE_SYSTEM_EQUATION; 955 case WebCore::HelpTagRole: 956 return ROLE_SYSTEM_HELPBALLOON; 957 case WebCore::LandmarkApplicationRole: 958 case WebCore::LandmarkBannerRole: 959 case WebCore::LandmarkComplementaryRole: 960 case WebCore::LandmarkContentInfoRole: 961 case WebCore::LandmarkMainRole: 962 case WebCore::LandmarkNavigationRole: 963 case WebCore::LandmarkSearchRole: 964 case WebCore::LegendRole: 965 return ROLE_SYSTEM_GROUPING; 966 case WebCore::MenuRole: 967 return ROLE_SYSTEM_MENUPOPUP; 968 case WebCore::MenuItemRole: 969 case WebCore::MenuButtonRole: 970 return ROLE_SYSTEM_MENUITEM; 971 case WebCore::MenuBarRole: 972 return ROLE_SYSTEM_MENUBAR; 973 case WebCore::ProgressIndicatorRole: 974 return ROLE_SYSTEM_PROGRESSBAR; 975 case WebCore::RowHeaderRole: 976 return ROLE_SYSTEM_ROWHEADER; 977 case WebCore::ScrollBarRole: 978 return ROLE_SYSTEM_SCROLLBAR; 979 case WebCore::SVGRootRole: 980 return ROLE_SYSTEM_GROUPING; 981 case WebCore::TableHeaderContainerRole: 982 return ROLE_SYSTEM_GROUPING; 983 case WebCore::WindowRole: 984 return ROLE_SYSTEM_WINDOW; 985 default: 986 // This is the default role for MSAA. 987 return ROLE_SYSTEM_CLIENT; 988 } 989} 990 991long AccessibleBase::role() const 992{ 993 return MSAARole(m_object->roleValueForMSAA()); 994} 995 996HRESULT AccessibleBase::getAccessibilityObjectForChild(VARIANT vChild, AccessibilityObject*& childObj) const 997{ 998 childObj = 0; 999 1000 if (!m_object) 1001 return E_FAIL; 1002 1003 if (vChild.vt != VT_I4) 1004 return E_INVALIDARG; 1005 1006 if (vChild.lVal == CHILDID_SELF) 1007 childObj = m_object; 1008 else if (vChild.lVal < 0) { 1009 // When broadcasting MSAA events, we negate the AXID and pass it as the 1010 // child ID. 1011 Document* document = m_object->document(); 1012 if (!document) 1013 return E_FAIL; 1014 1015 childObj = document->axObjectCache()->objectFromAXID(-vChild.lVal); 1016 } else { 1017 size_t childIndex = static_cast<size_t>(vChild.lVal - 1); 1018 1019 if (childIndex >= m_object->children().size()) 1020 return E_FAIL; 1021 childObj = m_object->children().at(childIndex).get(); 1022 } 1023 1024 if (!childObj) 1025 return E_FAIL; 1026 1027 return S_OK; 1028} 1029 1030AccessibleBase* AccessibleBase::wrapper(AccessibilityObject* obj) const 1031{ 1032 AccessibleBase* result = static_cast<AccessibleBase*>(obj->wrapper()); 1033 if (!result) 1034 result = createInstance(obj, m_window); 1035 return result; 1036} 1037 1038HRESULT AccessibleBase::isSameObject(IAccessibleComparable* other, BOOL* result) 1039{ 1040 COMPtr<AccessibleBase> otherAccessibleBase(Query, other); 1041 *result = (otherAccessibleBase == this || otherAccessibleBase->m_object == m_object); 1042 return S_OK; 1043} 1044