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