2 * Copyright (C) 2008, 2009, 2010 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/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/PlatformEvent.h>
46 #include <WebCore/RenderFrame.h>
47 #include <WebCore/RenderObject.h>
48 #include <WebCore/RenderView.h>
50 #include <wtf/RefPtr.h>
52 using namespace WebCore;
54 AccessibleBase::AccessibleBase(AccessibilityObject* obj)
55 : AccessibilityObjectWrapper(obj)
59 m_object->setWrapper(this);
61 gClassNameCount.add("AccessibleBase");
64 AccessibleBase::~AccessibleBase()
67 gClassNameCount.remove("AccessibleBase");
70 AccessibleBase* AccessibleBase::createInstance(AccessibilityObject* obj)
75 return new AccessibleImage(obj);
77 return new AccessibleBase(obj);
80 HRESULT AccessibleBase::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
82 if (!IsEqualGUID(guidService, SID_AccessibleComparable)) {
86 return QueryInterface(riid, ppvObject);
90 HRESULT STDMETHODCALLTYPE AccessibleBase::QueryInterface(REFIID riid, void** ppvObject)
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);
106 return E_NOINTERFACE;
112 ULONG STDMETHODCALLTYPE AccessibleBase::Release(void)
114 ASSERT(m_refCount > 0);
122 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accParent(IDispatch** parent)
129 AccessibilityObject* parentObject = m_object->parentObjectUnignored();
131 *parent = wrapper(parentObject);
136 if (!m_object->topDocumentFrameView())
139 return WebView::AccessibleObjectFromWindow(m_object->topDocumentFrameView()->hostWindow()->platformPageClient(),
140 OBJID_WINDOW, __uuidof(IAccessible), reinterpret_cast<void**>(parent));
143 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accChildCount(long* count)
149 *count = static_cast<long>(m_object->children().size());
153 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accChild(VARIANT vChild, IDispatch** ppChild)
160 AccessibilityObject* childObj;
162 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
166 *ppChild = static_cast<IDispatch*>(wrapper(childObj));
167 (*ppChild)->AddRef();
171 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accName(VARIANT vChild, BSTR* name)
178 AccessibilityObject* childObj;
179 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
184 if (*name = BString(wrapper(childObj)->name()).release())
189 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accValue(VARIANT vChild, BSTR* value)
196 AccessibilityObject* childObj;
197 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
202 if (*value = BString(wrapper(childObj)->value()).release())
207 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accDescription(VARIANT vChild, BSTR* description)
214 AccessibilityObject* childObj;
215 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
220 if (*description = BString(childObj->descriptionForMSAA()).release())
226 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accRole(VARIANT vChild, VARIANT* pvRole)
231 ::VariantInit(pvRole);
233 AccessibilityObject* childObj;
234 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
239 String roleString = childObj->stringRoleForMSAA();
240 if (!roleString.isEmpty()) {
241 V_VT(pvRole) = VT_BSTR;
242 V_BSTR(pvRole) = BString(roleString).release();
247 pvRole->lVal = wrapper(childObj)->role();
251 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accState(VARIANT vChild, VARIANT* pvState)
256 ::VariantInit(pvState);
258 AccessibilityObject* childObj;
259 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
267 if (childObj->isLinked())
268 pvState->lVal |= STATE_SYSTEM_LINKED;
270 if (childObj->isHovered())
271 pvState->lVal |= STATE_SYSTEM_HOTTRACKED;
273 if (!childObj->isEnabled())
274 pvState->lVal |= STATE_SYSTEM_UNAVAILABLE;
276 if (childObj->isReadOnly())
277 pvState->lVal |= STATE_SYSTEM_READONLY;
279 if (childObj->isOffScreen())
280 pvState->lVal |= STATE_SYSTEM_OFFSCREEN;
282 if (childObj->isPasswordField())
283 pvState->lVal |= STATE_SYSTEM_PROTECTED;
285 if (childObj->isIndeterminate())
286 pvState->lVal |= STATE_SYSTEM_INDETERMINATE;
288 if (childObj->isChecked())
289 pvState->lVal |= STATE_SYSTEM_CHECKED;
291 if (childObj->isPressed())
292 pvState->lVal |= STATE_SYSTEM_PRESSED;
294 if (childObj->isFocused())
295 pvState->lVal |= STATE_SYSTEM_FOCUSED;
297 if (childObj->isVisited())
298 pvState->lVal |= STATE_SYSTEM_TRAVERSED;
300 if (childObj->canSetFocusAttribute())
301 pvState->lVal |= STATE_SYSTEM_FOCUSABLE;
303 if (childObj->isSelected())
304 pvState->lVal |= STATE_SYSTEM_SELECTED;
306 if (childObj->canSetSelectedAttribute())
307 pvState->lVal |= STATE_SYSTEM_SELECTABLE;
309 if (childObj->isMultiSelectable())
310 pvState->lVal |= STATE_SYSTEM_EXTSELECTABLE | STATE_SYSTEM_MULTISELECTABLE;
312 if (!childObj->isVisible())
313 pvState->lVal |= STATE_SYSTEM_INVISIBLE;
315 if (childObj->isCollapsed())
316 pvState->lVal |= STATE_SYSTEM_COLLAPSED;
318 if (childObj->roleValue() == PopUpButtonRole) {
319 pvState->lVal |= STATE_SYSTEM_HASPOPUP;
321 if (!childObj->isCollapsed())
322 pvState->lVal |= STATE_SYSTEM_EXPANDED;
328 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accHelp(VARIANT vChild, BSTR* helpText)
335 AccessibilityObject* childObj;
336 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
341 if (*helpText = BString(childObj->helpText()).release())
346 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accKeyboardShortcut(VARIANT vChild, BSTR* shortcut)
353 AccessibilityObject* childObj;
354 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
359 String accessKey = childObj->accessKey();
360 if (accessKey.isNull())
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 & PlatformEvent::CtrlKey)
370 accessKeyModifiers += "Ctrl+";
371 if (modifiers & PlatformEvent::AltKey)
372 accessKeyModifiers += "Alt+";
373 if (modifiers & PlatformEvent::ShiftKey)
374 accessKeyModifiers += "Shift+";
375 if (modifiers & PlatformEvent::MetaKey)
376 accessKeyModifiers += "Win+";
378 *shortcut = BString(String(accessKeyModifiers + accessKey)).release();
382 HRESULT STDMETHODCALLTYPE AccessibleBase::accSelect(long selectionFlags, VARIANT vChild)
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)))
391 AccessibilityObject* childObject;
392 HRESULT hr = getAccessibilityObjectForChild(vChild, childObject);
397 if (selectionFlags & SELFLAG_TAKEFOCUS)
398 childObject->setFocused(true);
400 AccessibilityObject* parentObject = childObject->parentObject();
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);
415 // MSDN says that ADD, REMOVE, and EXTENDSELECTION with no other flags are invalid for
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)))
424 if (selectionFlags & SELFLAG_ADDSELECTION)
425 childObject->setSelected(true);
427 if (selectionFlags & SELFLAG_REMOVESELECTION)
428 childObject->setSelected(false);
430 // FIXME: Should implement SELFLAG_EXTENDSELECTION. For now, we just return
431 // S_OK, matching Firefox.
436 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accSelection(VARIANT*)
441 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accFocus(VARIANT* pvFocusedChild)
446 ::VariantInit(pvFocusedChild);
451 AccessibilityObject* focusedObj = m_object->focusedUIElement();
455 if (focusedObj == m_object) {
456 V_VT(pvFocusedChild) = VT_I4;
457 V_I4(pvFocusedChild) = CHILDID_SELF;
461 V_VT(pvFocusedChild) = VT_DISPATCH;
462 V_DISPATCH(pvFocusedChild) = wrapper(focusedObj);
463 V_DISPATCH(pvFocusedChild)->AddRef();
467 HRESULT STDMETHODCALLTYPE AccessibleBase::get_accDefaultAction(VARIANT vChild, BSTR* action)
474 AccessibilityObject* childObj;
475 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
480 if (*action = BString(childObj->actionVerb()).release())
485 HRESULT STDMETHODCALLTYPE AccessibleBase::accLocation(long* left, long* top, long* width, long* height, VARIANT vChild)
487 if (!left || !top || !width || !height)
490 *left = *top = *width = *height = 0;
492 AccessibilityObject* childObj;
493 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
498 if (!childObj->documentFrameView())
501 IntRect screenRect(childObj->documentFrameView()->contentsToScreen(childObj->pixelSnappedElementRect()));
502 *left = screenRect.x();
503 *top = screenRect.y();
504 *width = screenRect.width();
505 *height = screenRect.height();
509 HRESULT STDMETHODCALLTYPE AccessibleBase::accNavigate(long direction, VARIANT vFromChild, VARIANT* pvNavigatedTo)
514 ::VariantInit(pvNavigatedTo);
516 AccessibilityObject* childObj = 0;
523 // These directions are not implemented, matching Mozilla and IE.
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)
534 if (direction == NAVDIR_FIRSTCHILD)
535 childObj = m_object->firstChild();
537 childObj = m_object->lastChild();
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);
546 if (direction == NAVDIR_NEXT)
547 childObj = childObj->nextSibling();
549 childObj = childObj->previousSibling();
553 ASSERT_NOT_REACHED();
560 V_VT(pvNavigatedTo) = VT_DISPATCH;
561 V_DISPATCH(pvNavigatedTo) = wrapper(childObj);
562 V_DISPATCH(pvNavigatedTo)->AddRef();
566 HRESULT STDMETHODCALLTYPE AccessibleBase::accHitTest(long x, long y, VARIANT* pvChildAtPoint)
571 ::VariantInit(pvChildAtPoint);
573 if (!m_object || !m_object->documentFrameView())
576 IntPoint point = m_object->documentFrameView()->screenToContents(IntPoint(x, y));
577 AccessibilityObject* childObj = m_object->accessibilityHitTest(point);
580 // If we did not hit any child objects, test whether the point hit us, and
582 if (!m_object->boundingBoxRect().contains(point))
587 if (childObj == m_object) {
588 V_VT(pvChildAtPoint) = VT_I4;
589 V_I4(pvChildAtPoint) = CHILDID_SELF;
591 V_VT(pvChildAtPoint) = VT_DISPATCH;
592 V_DISPATCH(pvChildAtPoint) = static_cast<IDispatch*>(wrapper(childObj));
593 V_DISPATCH(pvChildAtPoint)->AddRef();
598 HRESULT STDMETHODCALLTYPE AccessibleBase::accDoDefaultAction(VARIANT vChild)
600 AccessibilityObject* childObj;
601 HRESULT hr = getAccessibilityObjectForChild(vChild, childObj);
606 if (!childObj->performDefaultAction())
613 String AccessibleBase::name() const
615 return m_object->nameForMSAA();
618 String AccessibleBase::value() const
620 return m_object->stringValueForMSAA();
623 static long MSAARole(AccessibilityRole 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;
670 // This is the default role for MSAA.
671 return ROLE_SYSTEM_CLIENT;
675 long AccessibleBase::role() const
677 return MSAARole(m_object->roleValueForMSAA());
680 HRESULT AccessibleBase::getAccessibilityObjectForChild(VARIANT vChild, AccessibilityObject*& childObj) const
687 if (vChild.vt != VT_I4)
690 if (vChild.lVal == CHILDID_SELF)
692 else if (vChild.lVal < 0) {
693 // When broadcasting MSAA events, we negate the AXID and pass it as the
695 Document* document = m_object->document();
699 childObj = document->axObjectCache()->objectFromAXID(-vChild.lVal);
701 size_t childIndex = static_cast<size_t>(vChild.lVal - 1);
703 if (childIndex >= m_object->children().size())
705 childObj = m_object->children().at(childIndex).get();
714 AccessibleBase* AccessibleBase::wrapper(AccessibilityObject* obj)
716 AccessibleBase* result = static_cast<AccessibleBase*>(obj->wrapper());
718 result = createInstance(obj);
722 HRESULT AccessibleBase::isSameObject(IAccessibleComparable* other, BOOL* result)
724 COMPtr<AccessibleBase> otherAccessibleBase(Query, other);
725 *result = (otherAccessibleBase == this || otherAccessibleBase->m_object == m_object);