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