MSAA: Screen rect for <option> elements is always the zero rect
[WebKit-https.git] / WebKit / win / AccessibleBase.cpp
1 /*
2  * Copyright (C) 2008 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/AccessibilityObject.h>
33 #include <WebCore/AXObjectCache.h>
34 #include <WebCore/BString.h>
35 #include <WebCore/Element.h>
36 #include <WebCore/EventHandler.h>
37 #include <WebCore/FrameView.h>
38 #include <WebCore/HostWindow.h>
39 #include <WebCore/HTMLNames.h>
40 #include <WebCore/HTMLFrameElementBase.h>
41 #include <WebCore/HTMLInputElement.h>
42 #include <WebCore/IntRect.h>
43 #include <WebCore/PlatformKeyboardEvent.h>
44 #include <WebCore/RenderFrame.h>
45 #include <WebCore/RenderObject.h>
46 #include <WebCore/RenderView.h>
47 #include <oleacc.h>
48 #include <wtf/RefPtr.h>
49
50 using namespace WebCore;
51
52 AccessibleBase::AccessibleBase(AccessibilityObject* obj)
53     : AccessibilityObjectWrapper(obj)
54     , m_refCount(0)
55 {
56     ASSERT_ARG(obj, obj);
57     m_object->setWrapper(this);
58     ++gClassCount;
59     gClassNameCount.add("AccessibleBase");
60 }
61
62 AccessibleBase::~AccessibleBase()
63 {
64     --gClassCount;
65     gClassNameCount.remove("AccessibleBase");
66 }
67
68 AccessibleBase* AccessibleBase::createInstance(AccessibilityObject* obj)
69 {
70     ASSERT_ARG(obj, obj);
71
72     if (obj->isImage())
73         return new AccessibleImage(obj);
74
75     return new AccessibleBase(obj);
76 }
77
78 // IUnknown
79 HRESULT STDMETHODCALLTYPE AccessibleBase::QueryInterface(REFIID riid, void** ppvObject)
80 {
81     if (IsEqualGUID(riid, __uuidof(IAccessible)))
82         *ppvObject = this;
83     else if (IsEqualGUID(riid, __uuidof(IDispatch)))
84         *ppvObject = this;
85     else if (IsEqualGUID(riid, __uuidof(IUnknown)))
86         *ppvObject = this;
87     else {
88         *ppvObject = 0;
89         return E_NOINTERFACE;
90     }
91     AddRef();
92     return S_OK;
93 }
94
95 ULONG STDMETHODCALLTYPE AccessibleBase::Release(void)
96 {
97     ASSERT(m_refCount > 0);
98     if (--m_refCount)
99         return m_refCount;
100     delete this;
101     return 0;
102 }
103
104 // IAccessible
105 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accParent(IDispatch** parent)
106 {
107     *parent = 0;
108
109     if (!m_object)
110         return E_FAIL;
111
112     AccessibilityObject* parentObject = m_object->parentObjectUnignored();
113     if (parentObject) {
114         *parent = wrapper(parentObject);
115         (*parent)->AddRef();
116         return S_OK;
117     }
118
119     if (!m_object->topDocumentFrameView())
120         return E_FAIL;
121
122     return WebView::AccessibleObjectFromWindow(m_object->topDocumentFrameView()->hostWindow()->platformPageClient(),
123         OBJID_WINDOW, __uuidof(IAccessible), reinterpret_cast<void**>(parent));
124 }
125
126 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accChildCount(long* count)
127 {
128     if (!m_object)
129         return E_FAIL;
130     if (!count)
131         return E_POINTER;
132     *count = static_cast<long>(m_object->children().size());
133     return S_OK;
134 }
135
136 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accChild(VARIANT vChild, IDispatch** ppChild)
137 {
138     if (!ppChild)
139         return E_POINTER;
140
141     *ppChild = 0;
142
143     AccessibilityObject* childObj;
144
145     HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
146     if (FAILED(hr))
147         return hr;
148
149     *ppChild = static_cast<IDispatch*>(wrapper(childObj));
150     (*ppChild)->AddRef();
151     return S_OK;
152 }
153
154 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accName(VARIANT vChild, BSTR* name)
155 {
156     if (!name)
157         return E_POINTER;
158
159     *name = 0;
160
161     AccessibilityObject* childObj;
162     HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
163
164     if (FAILED(hr))
165         return hr;
166
167     if (*name = BString(wrapper(childObj)->name()).release())
168         return S_OK;
169     return S_FALSE;
170 }
171
172 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accValue(VARIANT vChild, BSTR* value)
173 {
174     if (!value)
175         return E_POINTER;
176
177     *value = 0;
178
179     AccessibilityObject* childObj;
180     HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
181
182     if (FAILED(hr))
183         return hr;
184
185     if (*value = BString(wrapper(childObj)->value()).release())
186         return S_OK;
187     return S_FALSE;
188 }
189
190 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accDescription(VARIANT vChild, BSTR* description)
191 {
192     if (!description)
193         return E_POINTER;
194
195     *description = 0;
196
197     AccessibilityObject* childObj;
198     HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
199
200     if (FAILED(hr))
201         return hr;
202
203     if (*description = BString(childObj->descriptionForMSAA()).release())
204         return S_OK;
205
206     return S_FALSE;
207 }
208
209 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accRole(VARIANT vChild, VARIANT* pvRole)
210 {
211     if (!pvRole)
212         return E_POINTER;
213
214     ::VariantInit(pvRole);
215
216     AccessibilityObject* childObj;
217     HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
218
219     if (FAILED(hr))
220         return hr;
221
222     String roleString = childObj->stringRoleForMSAA();
223     if (!roleString.isEmpty()) {
224         V_VT(pvRole) = VT_BSTR;
225         V_BSTR(pvRole) = BString(roleString).release();
226         return S_OK;
227     }
228
229     pvRole->vt = VT_I4;
230     pvRole->lVal = wrapper(childObj)->role();
231     return S_OK;
232 }
233
234 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accState(VARIANT vChild, VARIANT* pvState)
235 {
236     if (!pvState)
237         return E_POINTER;
238
239     ::VariantInit(pvState);
240
241     AccessibilityObject* childObj;
242     HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
243
244     if (FAILED(hr))
245         return hr;
246
247     pvState->vt = VT_I4;
248     pvState->lVal = 0;
249
250     if (childObj->isLinked())
251         pvState->lVal |= STATE_SYSTEM_LINKED;
252
253     if (childObj->isHovered())
254         pvState->lVal |= STATE_SYSTEM_HOTTRACKED;
255
256     if (!childObj->isEnabled())
257         pvState->lVal |= STATE_SYSTEM_UNAVAILABLE;
258
259     if (childObj->isReadOnly())
260         pvState->lVal |= STATE_SYSTEM_READONLY;
261
262     if (childObj->isOffScreen())
263         pvState->lVal |= STATE_SYSTEM_OFFSCREEN;
264
265     if (childObj->isPasswordField())
266         pvState->lVal |= STATE_SYSTEM_PROTECTED;
267
268     if (childObj->isIndeterminate())
269         pvState->lVal |= STATE_SYSTEM_INDETERMINATE;
270
271     if (childObj->isChecked())
272         pvState->lVal |= STATE_SYSTEM_CHECKED;
273
274     if (childObj->isPressed())
275         pvState->lVal |= STATE_SYSTEM_PRESSED;
276
277     if (childObj->isFocused())
278         pvState->lVal |= STATE_SYSTEM_FOCUSED;
279
280     if (childObj->isVisited())
281         pvState->lVal |= STATE_SYSTEM_TRAVERSED;
282
283     if (childObj->canSetFocusAttribute())
284         pvState->lVal |= STATE_SYSTEM_FOCUSABLE;
285
286     if (childObj->isSelected())
287         pvState->lVal |= STATE_SYSTEM_SELECTED;
288
289     if (childObj->canSetSelectedAttribute())
290         pvState->lVal |= STATE_SYSTEM_SELECTABLE;
291
292     if (childObj->isMultiSelectable())
293         pvState->lVal |= STATE_SYSTEM_EXTSELECTABLE | STATE_SYSTEM_MULTISELECTABLE;
294
295     return S_OK;
296 }
297
298 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accHelp(VARIANT vChild, BSTR* helpText)
299 {
300     if (!helpText)
301         return E_POINTER;
302
303     *helpText = 0;
304
305     AccessibilityObject* childObj;
306     HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
307
308     if (FAILED(hr))
309         return hr;
310
311     if (*helpText = BString(childObj->helpText()).release())
312         return S_OK;
313     return S_FALSE;
314 }
315
316 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accKeyboardShortcut(VARIANT vChild, BSTR* shortcut)
317 {
318     if (!shortcut)
319         return E_POINTER;
320
321     *shortcut = 0;
322
323     AccessibilityObject* childObj;
324     HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
325
326     if (FAILED(hr))
327         return hr;
328
329     String accessKey = childObj->accessKey();
330     if (accessKey.isNull())
331         return S_FALSE;
332
333     static String accessKeyModifiers;
334     if (accessKeyModifiers.isNull()) {
335         unsigned modifiers = EventHandler::accessKeyModifiers();
336         // Follow the same order as Mozilla MSAA implementation:
337         // Ctrl+Alt+Shift+Meta+key. MSDN states that keyboard shortcut strings
338         // should not be localized and defines the separator as "+".
339         if (modifiers & PlatformKeyboardEvent::CtrlKey)
340             accessKeyModifiers += "Ctrl+";
341         if (modifiers & PlatformKeyboardEvent::AltKey)
342             accessKeyModifiers += "Alt+";
343         if (modifiers & PlatformKeyboardEvent::ShiftKey)
344             accessKeyModifiers += "Shift+";
345         if (modifiers & PlatformKeyboardEvent::MetaKey)
346             accessKeyModifiers += "Win+";
347     }
348     *shortcut = BString(accessKeyModifiers + accessKey).release();
349     return S_OK;
350 }
351
352 HRESULT STDMETHODCALLTYPE AccessibleBase::accSelect(long, VARIANT)
353 {
354     return E_NOTIMPL;
355 }
356
357 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accSelection(VARIANT*)
358 {
359     return E_NOTIMPL;
360 }
361
362 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accFocus(VARIANT* pvFocusedChild)
363 {
364     if (!pvFocusedChild)
365         return E_POINTER;
366
367     ::VariantInit(pvFocusedChild);
368
369     if (!m_object)
370         return E_FAIL;
371
372     AccessibilityObject* focusedObj = m_object->focusedUIElement();
373     if (!focusedObj)
374         return S_FALSE;
375
376     if (focusedObj == m_object) {
377         V_VT(pvFocusedChild) = VT_I4;
378         V_I4(pvFocusedChild) = CHILDID_SELF;
379         return S_OK;
380     }
381
382     V_VT(pvFocusedChild) = VT_DISPATCH;
383     V_DISPATCH(pvFocusedChild) = wrapper(focusedObj);
384     V_DISPATCH(pvFocusedChild)->AddRef();
385     return S_OK;
386 }
387
388 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accDefaultAction(VARIANT vChild, BSTR* action)
389 {
390     if (!action)
391         return E_POINTER;
392
393     *action = 0;
394
395     AccessibilityObject* childObj;
396     HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
397
398     if (FAILED(hr))
399         return hr;
400
401     if (*action = BString(childObj->actionVerb()).release())
402         return S_OK;
403     return S_FALSE;
404 }
405
406 HRESULT STDMETHODCALLTYPE AccessibleBase::accLocation(long* left, long* top, long* width, long* height, VARIANT vChild)
407 {
408     if (!left || !top || !width || !height)
409         return E_POINTER;
410
411     *left = *top = *width = *height = 0;
412
413     AccessibilityObject* childObj;
414     HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
415
416     if (FAILED(hr))
417         return hr;
418
419     if (!childObj->documentFrameView())
420         return E_FAIL;
421
422     IntRect screenRect(childObj->documentFrameView()->contentsToScreen(childObj->elementRect()));
423     *left = screenRect.x();
424     *top = screenRect.y();
425     *width = screenRect.width();
426     *height = screenRect.height();
427     return S_OK;
428 }
429
430 HRESULT STDMETHODCALLTYPE AccessibleBase::accNavigate(long direction, VARIANT vFromChild, VARIANT* pvNavigatedTo)
431 {
432     if (!pvNavigatedTo)
433         return E_POINTER;
434
435     ::VariantInit(pvNavigatedTo);
436
437     AccessibilityObject* childObj = 0;
438
439     switch (direction) {
440         case NAVDIR_DOWN:
441         case NAVDIR_UP:
442         case NAVDIR_LEFT:
443         case NAVDIR_RIGHT:
444             // These directions are not implemented, matching Mozilla and IE.
445             return E_NOTIMPL;
446         case NAVDIR_LASTCHILD:
447         case NAVDIR_FIRSTCHILD:
448             // MSDN states that navigating to first/last child can only be from self.
449             if (vFromChild.lVal != CHILDID_SELF)
450                 return E_INVALIDARG;
451
452             if (!m_object)
453                 return E_FAIL;
454
455             if (direction == NAVDIR_FIRSTCHILD)
456                 childObj = m_object->firstChild();
457             else
458                 childObj = m_object->lastChild();
459             break;
460         case NAVDIR_NEXT:
461         case NAVDIR_PREVIOUS: {
462             // Navigating to next and previous is allowed from self or any of our children.
463             HRESULT hr = getAccessibilityObjectForChild(vFromChild, childObj);
464             if (FAILED(hr))
465                 return hr;
466
467             if (direction == NAVDIR_NEXT)
468                 childObj = childObj->nextSibling();
469             else
470                 childObj = childObj->previousSibling();
471             break;
472         }
473         default:
474             ASSERT_NOT_REACHED();
475             return E_INVALIDARG;
476     }
477
478     if (!childObj)
479         return S_FALSE;
480
481     V_VT(pvNavigatedTo) = VT_DISPATCH;
482     V_DISPATCH(pvNavigatedTo) = wrapper(childObj);
483     V_DISPATCH(pvNavigatedTo)->AddRef();
484     return S_OK;
485 }
486
487 HRESULT STDMETHODCALLTYPE AccessibleBase::accHitTest(long x, long y, VARIANT* pvChildAtPoint)
488 {
489     if (!pvChildAtPoint)
490         return E_POINTER;
491
492     ::VariantInit(pvChildAtPoint);
493
494     if (!m_object || !m_object->documentFrameView())
495         return E_FAIL;
496
497     IntPoint point = m_object->documentFrameView()->screenToContents(IntPoint(x, y));
498     AccessibilityObject* childObj = m_object->doAccessibilityHitTest(point);
499
500     if (!childObj) {
501         // If we did not hit any child objects, test whether the point hit us, and
502         // report that.
503         if (!m_object->boundingBoxRect().contains(point))
504             return S_FALSE;
505         childObj = m_object;
506     }
507
508     if (childObj == m_object) {
509         V_VT(pvChildAtPoint) = VT_I4;
510         V_I4(pvChildAtPoint) = CHILDID_SELF;
511     } else {
512         V_VT(pvChildAtPoint) = VT_DISPATCH;
513         V_DISPATCH(pvChildAtPoint) = static_cast<IDispatch*>(wrapper(childObj));
514         V_DISPATCH(pvChildAtPoint)->AddRef();
515     }
516     return S_OK;
517 }
518
519 HRESULT STDMETHODCALLTYPE AccessibleBase::accDoDefaultAction(VARIANT vChild)
520 {
521     AccessibilityObject* childObj;
522     HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
523
524     if (FAILED(hr))
525         return hr;
526
527     if (!childObj->performDefaultAction())
528         return S_FALSE;
529
530     return S_OK;
531 }
532
533 // AccessibleBase
534 String AccessibleBase::name() const
535 {
536     return m_object->nameForMSAA();
537 }
538
539 String AccessibleBase::value() const
540 {
541     return m_object->stringValueForMSAA();
542 }
543
544 static long MSAARole(AccessibilityRole role)
545 {
546     switch (role) {
547         case WebCore::ButtonRole:
548             return ROLE_SYSTEM_PUSHBUTTON;
549         case WebCore::RadioButtonRole:
550             return ROLE_SYSTEM_RADIOBUTTON;
551         case WebCore::CheckBoxRole:
552             return ROLE_SYSTEM_CHECKBUTTON;
553         case WebCore::SliderRole:
554             return ROLE_SYSTEM_SLIDER;
555         case WebCore::TabGroupRole:
556             return ROLE_SYSTEM_PAGETABLIST;
557         case WebCore::TextFieldRole:
558         case WebCore::TextAreaRole:
559         case WebCore::EditableTextRole:
560             return ROLE_SYSTEM_TEXT;
561         case WebCore::ListMarkerRole:
562         case WebCore::StaticTextRole:
563             return ROLE_SYSTEM_STATICTEXT;
564         case WebCore::OutlineRole:
565             return ROLE_SYSTEM_OUTLINE;
566         case WebCore::ColumnRole:
567             return ROLE_SYSTEM_COLUMN;
568         case WebCore::RowRole:
569             return ROLE_SYSTEM_ROW;
570         case WebCore::GroupRole:
571             return ROLE_SYSTEM_GROUPING;
572         case WebCore::ListRole:
573         case WebCore::ListBoxRole:
574             return ROLE_SYSTEM_LIST;
575         case WebCore::TableRole:
576             return ROLE_SYSTEM_TABLE;
577         case WebCore::LinkRole:
578         case WebCore::WebCoreLinkRole:
579             return ROLE_SYSTEM_LINK;
580         case WebCore::ImageMapRole:
581         case WebCore::ImageRole:
582             return ROLE_SYSTEM_GRAPHIC;
583         case WebCore::ListItemRole:
584         case WebCore::ListBoxOptionRole:
585             return ROLE_SYSTEM_LISTITEM;
586         case WebCore::PopUpButtonRole:
587             return ROLE_SYSTEM_COMBOBOX;
588         default:
589             // This is the default role for MSAA.
590             return ROLE_SYSTEM_CLIENT;
591     }
592 }
593
594 long AccessibleBase::role() const
595 {
596     return MSAARole(m_object->roleValueForMSAA());
597 }
598
599 HRESULT AccessibleBase::getAccessibilityObjectForChild(VARIANT vChild, AccessibilityObject*& childObj) const
600 {
601     childObj = 0;
602
603     if (!m_object)
604         return E_FAIL;
605
606     if (vChild.vt != VT_I4)
607         return E_INVALIDARG;
608
609     if (vChild.lVal == CHILDID_SELF)
610         childObj = m_object;
611     else if (vChild.lVal < 0) {
612         // When broadcasting MSAA events, we negate the AXID and pass it as the
613         // child ID.
614         Document* document = m_object->document();
615         if (!document)
616             return E_FAIL;
617
618         childObj = document->axObjectCache()->objectFromAXID(-vChild.lVal);
619     } else {
620         size_t childIndex = static_cast<size_t>(vChild.lVal - 1);
621
622         if (childIndex >= m_object->children().size())
623             return E_FAIL;
624         childObj = m_object->children().at(childIndex).get();
625     }
626
627     if (!childObj)
628         return E_FAIL;
629
630     return S_OK;
631 }
632
633 AccessibleBase* AccessibleBase::wrapper(AccessibilityObject* obj)
634 {
635     AccessibleBase* result = static_cast<AccessibleBase*>(obj->wrapper());
636     if (!result)
637         result = createInstance(obj);
638     return result;
639 }