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