1/*
2 * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebKitDLL.h"
28#include "AccessibleBase.h"
29
30#include "AccessibleImage.h"
31#include "WebView.h"
32#include <WebCore/AccessibilityListBox.h>
33#include <WebCore/AccessibilityMenuListPopup.h>
34#include <WebCore/AccessibilityObject.h>
35#include <WebCore/AXObjectCache.h>
36#include <WebCore/BString.h>
37#include <WebCore/Element.h>
38#include <WebCore/EventHandler.h>
39#include <WebCore/FrameView.h>
40#include <WebCore/HostWindow.h>
41#include <WebCore/HTMLNames.h>
42#include <WebCore/HTMLFrameElementBase.h>
43#include <WebCore/HTMLInputElement.h>
44#include <WebCore/IntRect.h>
45#include <WebCore/PlatformKeyboardEvent.h>
46#include <WebCore/RenderFrame.h>
47#include <WebCore/RenderObject.h>
48#include <WebCore/RenderView.h>
49#include <oleacc.h>
50#include <wtf/RefPtr.h>
51
52using namespace WebCore;
53
54AccessibleBase::AccessibleBase(AccessibilityObject* obj)
55    : AccessibilityObjectWrapper(obj)
56    , m_refCount(0)
57{
58    ASSERT_ARG(obj, obj);
59    m_object->setWrapper(this);
60    ++gClassCount;
61    gClassNameCount.add("AccessibleBase");
62}
63
64AccessibleBase::~AccessibleBase()
65{
66    --gClassCount;
67    gClassNameCount.remove("AccessibleBase");
68}
69
70AccessibleBase* AccessibleBase::createInstance(AccessibilityObject* obj)
71{
72    ASSERT_ARG(obj, obj);
73
74    if (obj->isImage())
75        return new AccessibleImage(obj);
76
77    return new AccessibleBase(obj);
78}
79
80HRESULT AccessibleBase::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
81{
82    if (!IsEqualGUID(guidService, SID_AccessibleComparable)) {
83        *ppvObject = 0;
84        return E_INVALIDARG;
85    }
86    return QueryInterface(riid, ppvObject);
87}
88
89// IUnknown
90HRESULT STDMETHODCALLTYPE AccessibleBase::QueryInterface(REFIID riid, void** ppvObject)
91{
92    if (IsEqualGUID(riid, __uuidof(IAccessible)))
93        *ppvObject = static_cast<IAccessible*>(this);
94    else if (IsEqualGUID(riid, __uuidof(IDispatch)))
95        *ppvObject = static_cast<IAccessible*>(this);
96    else if (IsEqualGUID(riid, __uuidof(IUnknown)))
97        *ppvObject = static_cast<IAccessible*>(this);
98    else if (IsEqualGUID(riid, __uuidof(IAccessibleComparable)))
99        *ppvObject = static_cast<IAccessibleComparable*>(this);
100    else if (IsEqualGUID(riid, __uuidof(IServiceProvider)))
101        *ppvObject = static_cast<IServiceProvider*>(this);
102    else if (IsEqualGUID(riid, __uuidof(AccessibleBase)))
103        *ppvObject = static_cast<AccessibleBase*>(this);
104    else {
105        *ppvObject = 0;
106        return E_NOINTERFACE;
107    }
108    AddRef();
109    return S_OK;
110}
111
112ULONG STDMETHODCALLTYPE AccessibleBase::Release(void)
113{
114    ASSERT(m_refCount > 0);
115    if (--m_refCount)
116        return m_refCount;
117    delete this;
118    return 0;
119}
120
121// IAccessible
122HRESULT STDMETHODCALLTYPE AccessibleBase::get_accParent(IDispatch** parent)
123{
124    *parent = 0;
125
126    if (!m_object)
127        return E_FAIL;
128
129    AccessibilityObject* parentObject = m_object->parentObjectUnignored();
130    if (parentObject) {
131        *parent = wrapper(parentObject);
132        (*parent)->AddRef();
133        return S_OK;
134    }
135
136    if (!m_object->topDocumentFrameView())
137        return E_FAIL;
138
139    return WebView::AccessibleObjectFromWindow(m_object->topDocumentFrameView()->hostWindow()->platformPageClient(),
140        OBJID_WINDOW, __uuidof(IAccessible), reinterpret_cast<void**>(parent));
141}
142
143HRESULT STDMETHODCALLTYPE AccessibleBase::get_accChildCount(long* count)
144{
145    if (!m_object)
146        return E_FAIL;
147    if (!count)
148        return E_POINTER;
149    *count = static_cast<long>(m_object->children().size());
150    return S_OK;
151}
152
153HRESULT STDMETHODCALLTYPE AccessibleBase::get_accChild(VARIANT vChild, IDispatch** ppChild)
154{
155    if (!ppChild)
156        return E_POINTER;
157
158    *ppChild = 0;
159
160    AccessibilityObject* childObj;
161
162    HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
163    if (FAILED(hr))
164        return hr;
165
166    *ppChild = static_cast<IDispatch*>(wrapper(childObj));
167    (*ppChild)->AddRef();
168    return S_OK;
169}
170
171HRESULT STDMETHODCALLTYPE AccessibleBase::get_accName(VARIANT vChild, BSTR* name)
172{
173    if (!name)
174        return E_POINTER;
175
176    *name = 0;
177
178    AccessibilityObject* childObj;
179    HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
180
181    if (FAILED(hr))
182        return hr;
183
184    if (*name = BString(wrapper(childObj)->name()).release())
185        return S_OK;
186    return S_FALSE;
187}
188
189HRESULT STDMETHODCALLTYPE AccessibleBase::get_accValue(VARIANT vChild, BSTR* value)
190{
191    if (!value)
192        return E_POINTER;
193
194    *value = 0;
195
196    AccessibilityObject* childObj;
197    HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
198
199    if (FAILED(hr))
200        return hr;
201
202    if (*value = BString(wrapper(childObj)->value()).release())
203        return S_OK;
204    return S_FALSE;
205}
206
207HRESULT STDMETHODCALLTYPE AccessibleBase::get_accDescription(VARIANT vChild, BSTR* description)
208{
209    if (!description)
210        return E_POINTER;
211
212    *description = 0;
213
214    AccessibilityObject* childObj;
215    HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
216
217    if (FAILED(hr))
218        return hr;
219
220    if (*description = BString(childObj->descriptionForMSAA()).release())
221        return S_OK;
222
223    return S_FALSE;
224}
225
226HRESULT STDMETHODCALLTYPE AccessibleBase::get_accRole(VARIANT vChild, VARIANT* pvRole)
227{
228    if (!pvRole)
229        return E_POINTER;
230
231    ::VariantInit(pvRole);
232
233    AccessibilityObject* childObj;
234    HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
235
236    if (FAILED(hr))
237        return hr;
238
239    String roleString = childObj->stringRoleForMSAA();
240    if (!roleString.isEmpty()) {
241        V_VT(pvRole) = VT_BSTR;
242        V_BSTR(pvRole) = BString(roleString).release();
243        return S_OK;
244    }
245
246    pvRole->vt = VT_I4;
247    pvRole->lVal = wrapper(childObj)->role();
248    return S_OK;
249}
250
251HRESULT STDMETHODCALLTYPE AccessibleBase::get_accState(VARIANT vChild, VARIANT* pvState)
252{
253    if (!pvState)
254        return E_POINTER;
255
256    ::VariantInit(pvState);
257
258    AccessibilityObject* childObj;
259    HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
260
261    if (FAILED(hr))
262        return hr;
263
264    pvState->vt = VT_I4;
265    pvState->lVal = 0;
266
267    if (childObj->isLinked())
268        pvState->lVal |= STATE_SYSTEM_LINKED;
269
270    if (childObj->isHovered())
271        pvState->lVal |= STATE_SYSTEM_HOTTRACKED;
272
273    if (!childObj->isEnabled())
274        pvState->lVal |= STATE_SYSTEM_UNAVAILABLE;
275
276    if (childObj->isReadOnly())
277        pvState->lVal |= STATE_SYSTEM_READONLY;
278
279    if (childObj->isOffScreen())
280        pvState->lVal |= STATE_SYSTEM_OFFSCREEN;
281
282    if (childObj->isPasswordField())
283        pvState->lVal |= STATE_SYSTEM_PROTECTED;
284
285    if (childObj->isIndeterminate())
286        pvState->lVal |= STATE_SYSTEM_INDETERMINATE;
287
288    if (childObj->isChecked())
289        pvState->lVal |= STATE_SYSTEM_CHECKED;
290
291    if (childObj->isPressed())
292        pvState->lVal |= STATE_SYSTEM_PRESSED;
293
294    if (childObj->isFocused())
295        pvState->lVal |= STATE_SYSTEM_FOCUSED;
296
297    if (childObj->isVisited())
298        pvState->lVal |= STATE_SYSTEM_TRAVERSED;
299
300    if (childObj->canSetFocusAttribute())
301        pvState->lVal |= STATE_SYSTEM_FOCUSABLE;
302
303    if (childObj->isSelected())
304        pvState->lVal |= STATE_SYSTEM_SELECTED;
305
306    if (childObj->canSetSelectedAttribute())
307        pvState->lVal |= STATE_SYSTEM_SELECTABLE;
308
309    if (childObj->isMultiSelectable())
310        pvState->lVal |= STATE_SYSTEM_EXTSELECTABLE | STATE_SYSTEM_MULTISELECTABLE;
311
312    if (!childObj->isVisible())
313        pvState->lVal |= STATE_SYSTEM_INVISIBLE;
314
315    if (childObj->isCollapsed())
316        pvState->lVal |= STATE_SYSTEM_COLLAPSED;
317
318    if (childObj->roleValue() == PopUpButtonRole) {
319        pvState->lVal |= STATE_SYSTEM_HASPOPUP;
320
321        if (!childObj->isCollapsed())
322            pvState->lVal |= STATE_SYSTEM_EXPANDED;
323    }
324
325    return S_OK;
326}
327
328HRESULT STDMETHODCALLTYPE AccessibleBase::get_accHelp(VARIANT vChild, BSTR* helpText)
329{
330    if (!helpText)
331        return E_POINTER;
332
333    *helpText = 0;
334
335    AccessibilityObject* childObj;
336    HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
337
338    if (FAILED(hr))
339        return hr;
340
341    if (*helpText = BString(childObj->helpText()).release())
342        return S_OK;
343    return S_FALSE;
344}
345
346HRESULT STDMETHODCALLTYPE AccessibleBase::get_accKeyboardShortcut(VARIANT vChild, BSTR* shortcut)
347{
348    if (!shortcut)
349        return E_POINTER;
350
351    *shortcut = 0;
352
353    AccessibilityObject* childObj;
354    HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
355
356    if (FAILED(hr))
357        return hr;
358
359    String accessKey = childObj->accessKey();
360    if (accessKey.isNull())
361        return S_FALSE;
362
363    static String accessKeyModifiers;
364    if (accessKeyModifiers.isNull()) {
365        unsigned modifiers = EventHandler::accessKeyModifiers();
366        // Follow the same order as Mozilla MSAA implementation:
367        // Ctrl+Alt+Shift+Meta+key. MSDN states that keyboard shortcut strings
368        // should not be localized and defines the separator as "+".
369        if (modifiers & PlatformKeyboardEvent::CtrlKey)
370            accessKeyModifiers += "Ctrl+";
371        if (modifiers & PlatformKeyboardEvent::AltKey)
372            accessKeyModifiers += "Alt+";
373        if (modifiers & PlatformKeyboardEvent::ShiftKey)
374            accessKeyModifiers += "Shift+";
375        if (modifiers & PlatformKeyboardEvent::MetaKey)
376            accessKeyModifiers += "Win+";
377    }
378    *shortcut = BString(accessKeyModifiers + accessKey).release();
379    return S_OK;
380}
381
382HRESULT STDMETHODCALLTYPE AccessibleBase::accSelect(long selectionFlags, VARIANT vChild)
383{
384    // According to MSDN, these combinations are invalid.
385    if (((selectionFlags & (SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION)) == (SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION))
386        || ((selectionFlags & (SELFLAG_ADDSELECTION | SELFLAG_TAKESELECTION)) == (SELFLAG_ADDSELECTION | SELFLAG_TAKESELECTION))
387        || ((selectionFlags & (SELFLAG_REMOVESELECTION | SELFLAG_TAKESELECTION)) == (SELFLAG_REMOVESELECTION | SELFLAG_TAKESELECTION))
388        || ((selectionFlags & (SELFLAG_EXTENDSELECTION | SELFLAG_TAKESELECTION)) == (SELFLAG_EXTENDSELECTION | SELFLAG_TAKESELECTION)))
389        return E_INVALIDARG;
390
391    AccessibilityObject* childObject;
392    HRESULT hr = getAccessibilityObjectForChild(vChild, childObject);
393
394    if (FAILED(hr))
395        return hr;
396
397    if (selectionFlags & SELFLAG_TAKEFOCUS)
398        childObject->setFocused(true);
399
400    AccessibilityObject* parentObject = childObject->parentObject();
401    if (!parentObject)
402        return E_INVALIDARG;
403
404    if (selectionFlags & SELFLAG_TAKESELECTION) {
405        if (parentObject->isListBox()) {
406            Vector<RefPtr<AccessibilityObject> > selectedChildren(1);
407            selectedChildren[0] = childObject;
408            static_cast<AccessibilityListBox*>(parentObject)->setSelectedChildren(selectedChildren);
409        } else { // any element may be selectable by virtue of it having the aria-selected property
410            ASSERT(!parentObject->isMultiSelectable());
411            childObject->setSelected(true);
412        }
413    }
414
415    // MSDN says that ADD, REMOVE, and EXTENDSELECTION with no other flags are invalid for
416    // single-select.
417    const long allSELFLAGs = SELFLAG_TAKEFOCUS | SELFLAG_TAKESELECTION | SELFLAG_EXTENDSELECTION | SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION;
418    if (!parentObject->isMultiSelectable()
419        && (((selectionFlags & allSELFLAGs) == SELFLAG_ADDSELECTION)
420        || ((selectionFlags & allSELFLAGs) == SELFLAG_REMOVESELECTION)
421        || ((selectionFlags & allSELFLAGs) == SELFLAG_EXTENDSELECTION)))
422        return E_INVALIDARG;
423
424    if (selectionFlags & SELFLAG_ADDSELECTION)
425        childObject->setSelected(true);
426
427    if (selectionFlags & SELFLAG_REMOVESELECTION)
428        childObject->setSelected(false);
429
430    // FIXME: Should implement SELFLAG_EXTENDSELECTION. For now, we just return
431    // S_OK, matching Firefox.
432
433    return S_OK;
434}
435
436HRESULT STDMETHODCALLTYPE AccessibleBase::get_accSelection(VARIANT*)
437{
438    return E_NOTIMPL;
439}
440
441HRESULT STDMETHODCALLTYPE AccessibleBase::get_accFocus(VARIANT* pvFocusedChild)
442{
443    if (!pvFocusedChild)
444        return E_POINTER;
445
446    ::VariantInit(pvFocusedChild);
447
448    if (!m_object)
449        return E_FAIL;
450
451    AccessibilityObject* focusedObj = m_object->focusedUIElement();
452    if (!focusedObj)
453        return S_FALSE;
454
455    if (focusedObj == m_object) {
456        V_VT(pvFocusedChild) = VT_I4;
457        V_I4(pvFocusedChild) = CHILDID_SELF;
458        return S_OK;
459    }
460
461    V_VT(pvFocusedChild) = VT_DISPATCH;
462    V_DISPATCH(pvFocusedChild) = wrapper(focusedObj);
463    V_DISPATCH(pvFocusedChild)->AddRef();
464    return S_OK;
465}
466
467HRESULT STDMETHODCALLTYPE AccessibleBase::get_accDefaultAction(VARIANT vChild, BSTR* action)
468{
469    if (!action)
470        return E_POINTER;
471
472    *action = 0;
473
474    AccessibilityObject* childObj;
475    HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
476
477    if (FAILED(hr))
478        return hr;
479
480    if (*action = BString(childObj->actionVerb()).release())
481        return S_OK;
482    return S_FALSE;
483}
484
485HRESULT STDMETHODCALLTYPE AccessibleBase::accLocation(long* left, long* top, long* width, long* height, VARIANT vChild)
486{
487    if (!left || !top || !width || !height)
488        return E_POINTER;
489
490    *left = *top = *width = *height = 0;
491
492    AccessibilityObject* childObj;
493    HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
494
495    if (FAILED(hr))
496        return hr;
497
498    if (!childObj->documentFrameView())
499        return E_FAIL;
500
501    IntRect screenRect(childObj->documentFrameView()->contentsToScreen(childObj->elementRect()));
502    *left = screenRect.x();
503    *top = screenRect.y();
504    *width = screenRect.width();
505    *height = screenRect.height();
506    return S_OK;
507}
508
509HRESULT STDMETHODCALLTYPE AccessibleBase::accNavigate(long direction, VARIANT vFromChild, VARIANT* pvNavigatedTo)
510{
511    if (!pvNavigatedTo)
512        return E_POINTER;
513
514    ::VariantInit(pvNavigatedTo);
515
516    AccessibilityObject* childObj = 0;
517
518    switch (direction) {
519        case NAVDIR_DOWN:
520        case NAVDIR_UP:
521        case NAVDIR_LEFT:
522        case NAVDIR_RIGHT:
523            // These directions are not implemented, matching Mozilla and IE.
524            return E_NOTIMPL;
525        case NAVDIR_LASTCHILD:
526        case NAVDIR_FIRSTCHILD:
527            // MSDN states that navigating to first/last child can only be from self.
528            if (vFromChild.lVal != CHILDID_SELF)
529                return E_INVALIDARG;
530
531            if (!m_object)
532                return E_FAIL;
533
534            if (direction == NAVDIR_FIRSTCHILD)
535                childObj = m_object->firstChild();
536            else
537                childObj = m_object->lastChild();
538            break;
539        case NAVDIR_NEXT:
540        case NAVDIR_PREVIOUS: {
541            // Navigating to next and previous is allowed from self or any of our children.
542            HRESULT hr = getAccessibilityObjectForChild(vFromChild, childObj);
543            if (FAILED(hr))
544                return hr;
545
546            if (direction == NAVDIR_NEXT)
547                childObj = childObj->nextSibling();
548            else
549                childObj = childObj->previousSibling();
550            break;
551        }
552        default:
553            ASSERT_NOT_REACHED();
554            return E_INVALIDARG;
555    }
556
557    if (!childObj)
558        return S_FALSE;
559
560    V_VT(pvNavigatedTo) = VT_DISPATCH;
561    V_DISPATCH(pvNavigatedTo) = wrapper(childObj);
562    V_DISPATCH(pvNavigatedTo)->AddRef();
563    return S_OK;
564}
565
566HRESULT STDMETHODCALLTYPE AccessibleBase::accHitTest(long x, long y, VARIANT* pvChildAtPoint)
567{
568    if (!pvChildAtPoint)
569        return E_POINTER;
570
571    ::VariantInit(pvChildAtPoint);
572
573    if (!m_object || !m_object->documentFrameView())
574        return E_FAIL;
575
576    IntPoint point = m_object->documentFrameView()->screenToContents(IntPoint(x, y));
577    AccessibilityObject* childObj = m_object->accessibilityHitTest(point);
578
579    if (!childObj) {
580        // If we did not hit any child objects, test whether the point hit us, and
581        // report that.
582        if (!m_object->boundingBoxRect().contains(point))
583            return S_FALSE;
584        childObj = m_object;
585    }
586
587    if (childObj == m_object) {
588        V_VT(pvChildAtPoint) = VT_I4;
589        V_I4(pvChildAtPoint) = CHILDID_SELF;
590    } else {
591        V_VT(pvChildAtPoint) = VT_DISPATCH;
592        V_DISPATCH(pvChildAtPoint) = static_cast<IDispatch*>(wrapper(childObj));
593        V_DISPATCH(pvChildAtPoint)->AddRef();
594    }
595    return S_OK;
596}
597
598HRESULT STDMETHODCALLTYPE AccessibleBase::accDoDefaultAction(VARIANT vChild)
599{
600    AccessibilityObject* childObj;
601    HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
602
603    if (FAILED(hr))
604        return hr;
605
606    if (!childObj->performDefaultAction())
607        return S_FALSE;
608
609    return S_OK;
610}
611
612// AccessibleBase
613String AccessibleBase::name() const
614{
615    return m_object->nameForMSAA();
616}
617
618String AccessibleBase::value() const
619{
620    return m_object->stringValueForMSAA();
621}
622
623static long MSAARole(AccessibilityRole role)
624{
625    switch (role) {
626        case WebCore::ButtonRole:
627            return ROLE_SYSTEM_PUSHBUTTON;
628        case WebCore::RadioButtonRole:
629            return ROLE_SYSTEM_RADIOBUTTON;
630        case WebCore::CheckBoxRole:
631            return ROLE_SYSTEM_CHECKBUTTON;
632        case WebCore::SliderRole:
633            return ROLE_SYSTEM_SLIDER;
634        case WebCore::TabGroupRole:
635            return ROLE_SYSTEM_PAGETABLIST;
636        case WebCore::TextFieldRole:
637        case WebCore::TextAreaRole:
638        case WebCore::EditableTextRole:
639            return ROLE_SYSTEM_TEXT;
640        case WebCore::ListMarkerRole:
641        case WebCore::StaticTextRole:
642            return ROLE_SYSTEM_STATICTEXT;
643        case WebCore::OutlineRole:
644            return ROLE_SYSTEM_OUTLINE;
645        case WebCore::ColumnRole:
646            return ROLE_SYSTEM_COLUMN;
647        case WebCore::RowRole:
648            return ROLE_SYSTEM_ROW;
649        case WebCore::GroupRole:
650            return ROLE_SYSTEM_GROUPING;
651        case WebCore::ListRole:
652        case WebCore::ListBoxRole:
653        case WebCore::MenuListPopupRole:
654            return ROLE_SYSTEM_LIST;
655        case WebCore::TableRole:
656            return ROLE_SYSTEM_TABLE;
657        case WebCore::LinkRole:
658        case WebCore::WebCoreLinkRole:
659            return ROLE_SYSTEM_LINK;
660        case WebCore::ImageMapRole:
661        case WebCore::ImageRole:
662            return ROLE_SYSTEM_GRAPHIC;
663        case WebCore::MenuListOptionRole:
664        case WebCore::ListItemRole:
665        case WebCore::ListBoxOptionRole:
666            return ROLE_SYSTEM_LISTITEM;
667        case WebCore::PopUpButtonRole:
668            return ROLE_SYSTEM_COMBOBOX;
669        default:
670            // This is the default role for MSAA.
671            return ROLE_SYSTEM_CLIENT;
672    }
673}
674
675long AccessibleBase::role() const
676{
677    return MSAARole(m_object->roleValueForMSAA());
678}
679
680HRESULT AccessibleBase::getAccessibilityObjectForChild(VARIANT vChild, AccessibilityObject*& childObj) const
681{
682    childObj = 0;
683
684    if (!m_object)
685        return E_FAIL;
686
687    if (vChild.vt != VT_I4)
688        return E_INVALIDARG;
689
690    if (vChild.lVal == CHILDID_SELF)
691        childObj = m_object;
692    else if (vChild.lVal < 0) {
693        // When broadcasting MSAA events, we negate the AXID and pass it as the
694        // child ID.
695        Document* document = m_object->document();
696        if (!document)
697            return E_FAIL;
698
699        childObj = document->axObjectCache()->objectFromAXID(-vChild.lVal);
700    } else {
701        size_t childIndex = static_cast<size_t>(vChild.lVal - 1);
702
703        if (childIndex >= m_object->children().size())
704            return E_FAIL;
705        childObj = m_object->children().at(childIndex).get();
706    }
707
708    if (!childObj)
709        return E_FAIL;
710
711    return S_OK;
712}
713
714AccessibleBase* AccessibleBase::wrapper(AccessibilityObject* obj)
715{
716    AccessibleBase* result = static_cast<AccessibleBase*>(obj->wrapper());
717    if (!result)
718        result = createInstance(obj);
719    return result;
720}
721
722HRESULT AccessibleBase::isSameObject(IAccessibleComparable* other, BOOL* result)
723{
724    COMPtr<AccessibleBase> otherAccessibleBase(Query, other);
725    *result = (otherAccessibleBase == this || otherAccessibleBase->m_object == m_object);
726    return S_OK;
727}
728