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