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