2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "WebKitDLL.h"
28 #include "AccessibleBase.h"
30 #include "AccessibleImage.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>
48 #include <wtf/RefPtr.h>
50 using namespace WebCore;
52 AccessibleBase::AccessibleBase(AccessibilityObject* obj)
53 : AccessibilityObjectWrapper(obj)
57 m_object->setWrapper(this);
59 gClassNameCount.add("AccessibleBase");
62 AccessibleBase::~AccessibleBase()
65 gClassNameCount.remove("AccessibleBase");
68 AccessibleBase* AccessibleBase::createInstance(AccessibilityObject* obj)
73 return new AccessibleImage(obj);
75 return new AccessibleBase(obj);
79 HRESULT STDMETHODCALLTYPE AccessibleBase::QueryInterface(REFIID riid, void** ppvObject)
81 if (IsEqualGUID(riid, __uuidof(IAccessible)))
83 else if (IsEqualGUID(riid, __uuidof(IDispatch)))
85 else if (IsEqualGUID(riid, __uuidof(IUnknown)))
95 ULONG STDMETHODCALLTYPE AccessibleBase::Release(void)
97 ASSERT(m_refCount > 0);
105 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accParent(IDispatch** parent)
112 AccessibilityObject* parentObject = m_object->parentObjectUnignored();
114 *parent = wrapper(parentObject);
119 if (!m_object->topDocumentFrameView())
122 return WebView::AccessibleObjectFromWindow(m_object->topDocumentFrameView()->hostWindow()->platformPageClient(),
123 OBJID_WINDOW, __uuidof(IAccessible), reinterpret_cast<void**>(parent));
126 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accChildCount(long* count)
132 *count = static_cast<long>(m_object->children().size());
136 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accChild(VARIANT vChild, IDispatch** ppChild)
143 AccessibilityObject* childObj;
145 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
149 *ppChild = static_cast<IDispatch*>(wrapper(childObj));
150 (*ppChild)->AddRef();
154 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accName(VARIANT vChild, BSTR* name)
161 AccessibilityObject* childObj;
162 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
167 if (*name = BString(wrapper(childObj)->name()).release())
172 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accValue(VARIANT vChild, BSTR* value)
179 AccessibilityObject* childObj;
180 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
185 if (*value = BString(wrapper(childObj)->value()).release())
190 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accDescription(VARIANT vChild, BSTR* description)
197 AccessibilityObject* childObj;
198 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
203 if (*description = BString(childObj->descriptionForMSAA()).release())
209 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accRole(VARIANT vChild, VARIANT* pvRole)
214 ::VariantInit(pvRole);
216 AccessibilityObject* childObj;
217 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
222 String roleString = childObj->stringRoleForMSAA();
223 if (!roleString.isEmpty()) {
224 V_VT(pvRole) = VT_BSTR;
225 V_BSTR(pvRole) = BString(roleString).release();
230 pvRole->lVal = wrapper(childObj)->role();
234 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accState(VARIANT vChild, VARIANT* pvState)
239 ::VariantInit(pvState);
241 AccessibilityObject* childObj;
242 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
250 if (childObj->isLinked())
251 pvState->lVal |= STATE_SYSTEM_LINKED;
253 if (childObj->isHovered())
254 pvState->lVal |= STATE_SYSTEM_HOTTRACKED;
256 if (!childObj->isEnabled())
257 pvState->lVal |= STATE_SYSTEM_UNAVAILABLE;
259 if (childObj->isReadOnly())
260 pvState->lVal |= STATE_SYSTEM_READONLY;
262 if (childObj->isOffScreen())
263 pvState->lVal |= STATE_SYSTEM_OFFSCREEN;
265 if (childObj->isPasswordField())
266 pvState->lVal |= STATE_SYSTEM_PROTECTED;
268 if (childObj->isIndeterminate())
269 pvState->lVal |= STATE_SYSTEM_INDETERMINATE;
271 if (childObj->isChecked())
272 pvState->lVal |= STATE_SYSTEM_CHECKED;
274 if (childObj->isPressed())
275 pvState->lVal |= STATE_SYSTEM_PRESSED;
277 if (childObj->isFocused())
278 pvState->lVal |= STATE_SYSTEM_FOCUSED;
280 if (childObj->isVisited())
281 pvState->lVal |= STATE_SYSTEM_TRAVERSED;
283 if (childObj->canSetFocusAttribute())
284 pvState->lVal |= STATE_SYSTEM_FOCUSABLE;
286 if (childObj->isSelected())
287 pvState->lVal |= STATE_SYSTEM_SELECTED;
289 if (childObj->canSetSelectedAttribute())
290 pvState->lVal |= STATE_SYSTEM_SELECTABLE;
292 if (childObj->isMultiSelectable())
293 pvState->lVal |= STATE_SYSTEM_EXTSELECTABLE | STATE_SYSTEM_MULTISELECTABLE;
298 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accHelp(VARIANT vChild, BSTR* helpText)
305 AccessibilityObject* childObj;
306 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
311 if (*helpText = BString(childObj->helpText()).release())
316 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accKeyboardShortcut(VARIANT vChild, BSTR* shortcut)
323 AccessibilityObject* childObj;
324 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
329 String accessKey = childObj->accessKey();
330 if (accessKey.isNull())
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+";
348 *shortcut = BString(accessKeyModifiers + accessKey).release();
352 HRESULT STDMETHODCALLTYPE AccessibleBase::accSelect(long, VARIANT)
357 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accSelection(VARIANT*)
362 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accFocus(VARIANT* pvFocusedChild)
367 ::VariantInit(pvFocusedChild);
372 AccessibilityObject* focusedObj = m_object->focusedUIElement();
376 if (focusedObj == m_object) {
377 V_VT(pvFocusedChild) = VT_I4;
378 V_I4(pvFocusedChild) = CHILDID_SELF;
382 V_VT(pvFocusedChild) = VT_DISPATCH;
383 V_DISPATCH(pvFocusedChild) = wrapper(focusedObj);
384 V_DISPATCH(pvFocusedChild)->AddRef();
388 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accDefaultAction(VARIANT vChild, BSTR* action)
395 AccessibilityObject* childObj;
396 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
401 if (*action = BString(childObj->actionVerb()).release())
406 HRESULT STDMETHODCALLTYPE AccessibleBase::accLocation(long* left, long* top, long* width, long* height, VARIANT vChild)
408 if (!left || !top || !width || !height)
411 *left = *top = *width = *height = 0;
413 AccessibilityObject* childObj;
414 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
419 if (!childObj->documentFrameView())
422 IntRect screenRect(childObj->documentFrameView()->contentsToScreen(childObj->elementRect()));
423 *left = screenRect.x();
424 *top = screenRect.y();
425 *width = screenRect.width();
426 *height = screenRect.height();
430 HRESULT STDMETHODCALLTYPE AccessibleBase::accNavigate(long direction, VARIANT vFromChild, VARIANT* pvNavigatedTo)
435 ::VariantInit(pvNavigatedTo);
437 AccessibilityObject* childObj = 0;
444 // These directions are not implemented, matching Mozilla and IE.
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)
455 if (direction == NAVDIR_FIRSTCHILD)
456 childObj = m_object->firstChild();
458 childObj = m_object->lastChild();
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);
467 if (direction == NAVDIR_NEXT)
468 childObj = childObj->nextSibling();
470 childObj = childObj->previousSibling();
474 ASSERT_NOT_REACHED();
481 V_VT(pvNavigatedTo) = VT_DISPATCH;
482 V_DISPATCH(pvNavigatedTo) = wrapper(childObj);
483 V_DISPATCH(pvNavigatedTo)->AddRef();
487 HRESULT STDMETHODCALLTYPE AccessibleBase::accHitTest(long x, long y, VARIANT* pvChildAtPoint)
492 ::VariantInit(pvChildAtPoint);
494 if (!m_object || !m_object->documentFrameView())
497 IntPoint point = m_object->documentFrameView()->screenToContents(IntPoint(x, y));
498 AccessibilityObject* childObj = m_object->doAccessibilityHitTest(point);
501 // If we did not hit any child objects, test whether the point hit us, and
503 if (!m_object->boundingBoxRect().contains(point))
508 if (childObj == m_object) {
509 V_VT(pvChildAtPoint) = VT_I4;
510 V_I4(pvChildAtPoint) = CHILDID_SELF;
512 V_VT(pvChildAtPoint) = VT_DISPATCH;
513 V_DISPATCH(pvChildAtPoint) = static_cast<IDispatch*>(wrapper(childObj));
514 V_DISPATCH(pvChildAtPoint)->AddRef();
519 HRESULT STDMETHODCALLTYPE AccessibleBase::accDoDefaultAction(VARIANT vChild)
521 AccessibilityObject* childObj;
522 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
527 if (!childObj->performDefaultAction())
534 String AccessibleBase::name() const
536 return m_object->nameForMSAA();
539 String AccessibleBase::value() const
541 return m_object->stringValueForMSAA();
544 static long MSAARole(AccessibilityRole 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;
589 // This is the default role for MSAA.
590 return ROLE_SYSTEM_CLIENT;
594 long AccessibleBase::role() const
596 return MSAARole(m_object->roleValueForMSAA());
599 HRESULT AccessibleBase::getAccessibilityObjectForChild(VARIANT vChild, AccessibilityObject*& childObj) const
606 if (vChild.vt != VT_I4)
609 if (vChild.lVal == CHILDID_SELF)
611 else if (vChild.lVal < 0) {
612 // When broadcasting MSAA events, we negate the AXID and pass it as the
614 Document* document = m_object->document();
618 childObj = document->axObjectCache()->objectFromAXID(-vChild.lVal);
620 size_t childIndex = static_cast<size_t>(vChild.lVal - 1);
622 if (childIndex >= m_object->children().size())
624 childObj = m_object->children().at(childIndex).get();
633 AccessibleBase* AccessibleBase::wrapper(AccessibilityObject* obj)
635 AccessibleBase* result = static_cast<AccessibleBase*>(obj->wrapper());
637 result = createInstance(obj);