Bug 575693 - Download Manager and Page Info no longer have an accessible tree, r...
[mozilla-central.git] / accessible / src / msaa / nsAccessibleWrap.cpp
blob72597bf7500fe307c78276c33f61ccac254f1b27
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2003
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Original Author: Aaron Leventhal (aaronl@netscape.com)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsAccessibleWrap.h"
41 #include "nsAccessibilityAtoms.h"
42 #include "nsAccUtils.h"
43 #include "nsCoreUtils.h"
44 #include "nsRelUtils.h"
46 #include "nsIAccessibleDocument.h"
47 #include "nsIAccessibleSelectable.h"
48 #include "nsIAccessibleEvent.h"
49 #include "nsIAccessibleWin32Object.h"
51 #include "Accessible2_i.c"
52 #include "AccessibleStates.h"
54 #include "nsIMutableArray.h"
55 #include "nsIDOMDocument.h"
56 #include "nsIFrame.h"
57 #include "nsIScrollableFrame.h"
58 #include "nsINameSpaceManager.h"
59 #include "nsINodeInfo.h"
60 #include "nsIPrefService.h"
61 #include "nsRootAccessible.h"
62 #include "nsIServiceManager.h"
63 #include "nsTextFormatter.h"
64 #include "nsIView.h"
65 #include "nsIViewManager.h"
66 #include "nsRoleMap.h"
67 #include "nsEventMap.h"
68 #include "nsArrayUtils.h"
70 /* For documentation of the accessibility architecture,
71 * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
74 //#define DEBUG_LEAKS
76 #ifdef DEBUG_LEAKS
77 static gAccessibles = 0;
78 #endif
80 EXTERN_C GUID CDECL CLSID_Accessible =
81 { 0x61044601, 0xa811, 0x4e2b, { 0xbb, 0xba, 0x17, 0xbf, 0xab, 0xd3, 0x29, 0xd7 } };
83 static const PRInt32 kIEnumVariantDisconnected = -1;
85 ////////////////////////////////////////////////////////////////////////////////
86 // nsAccessibleWrap
87 ////////////////////////////////////////////////////////////////////////////////
89 //-----------------------------------------------------
90 // construction
91 //-----------------------------------------------------
92 nsAccessibleWrap::
93 nsAccessibleWrap(nsIContent *aContent, nsIWeakReference *aShell) :
94 nsAccessible(aContent, aShell), mEnumVARIANTPosition(0), mTypeInfo(NULL)
98 //-----------------------------------------------------
99 // destruction
100 //-----------------------------------------------------
101 nsAccessibleWrap::~nsAccessibleWrap()
103 if (mTypeInfo)
104 mTypeInfo->Release();
107 NS_IMPL_ISUPPORTS_INHERITED0(nsAccessibleWrap, nsAccessible);
109 //-----------------------------------------------------
110 // IUnknown interface methods - see iunknown.h for documentation
111 //-----------------------------------------------------
113 // Microsoft COM QueryInterface
114 STDMETHODIMP nsAccessibleWrap::QueryInterface(REFIID iid, void** ppv)
116 __try {
117 *ppv = NULL;
119 if (IID_IUnknown == iid || IID_IDispatch == iid || IID_IAccessible == iid)
120 *ppv = static_cast<IAccessible*>(this);
121 else if (IID_IEnumVARIANT == iid && !gIsEnumVariantSupportDisabled) {
122 long numChildren;
123 get_accChildCount(&numChildren);
124 if (numChildren > 0) // Don't support this interface for leaf elements
125 *ppv = static_cast<IEnumVARIANT*>(this);
126 } else if (IID_IServiceProvider == iid)
127 *ppv = static_cast<IServiceProvider*>(this);
128 else if (IID_IAccessible2 == iid && !gIsIA2Disabled)
129 *ppv = static_cast<IAccessible2*>(this);
131 if (NULL == *ppv) {
132 HRESULT hr = CAccessibleComponent::QueryInterface(iid, ppv);
133 if (SUCCEEDED(hr))
134 return hr;
137 if (NULL == *ppv) {
138 HRESULT hr = CAccessibleHyperlink::QueryInterface(iid, ppv);
139 if (SUCCEEDED(hr))
140 return hr;
143 if (NULL == *ppv) {
144 HRESULT hr = CAccessibleValue::QueryInterface(iid, ppv);
145 if (SUCCEEDED(hr))
146 return hr;
149 if (NULL == *ppv)
150 return nsAccessNodeWrap::QueryInterface(iid, ppv);
152 (reinterpret_cast<IUnknown*>(*ppv))->AddRef();
153 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
154 return S_OK;
157 //-----------------------------------------------------
158 // IAccessible methods
159 //-----------------------------------------------------
162 STDMETHODIMP nsAccessibleWrap::AccessibleObjectFromWindow(HWND hwnd,
163 DWORD dwObjectID,
164 REFIID riid,
165 void **ppvObject)
167 // open the dll dynamically
168 if (!gmAccLib)
169 gmAccLib =::LoadLibraryW(L"OLEACC.DLL");
171 if (gmAccLib) {
172 if (!gmAccessibleObjectFromWindow)
173 gmAccessibleObjectFromWindow = (LPFNACCESSIBLEOBJECTFROMWINDOW)GetProcAddress(gmAccLib,"AccessibleObjectFromWindow");
175 if (gmAccessibleObjectFromWindow)
176 return gmAccessibleObjectFromWindow(hwnd, dwObjectID, riid, ppvObject);
179 return E_FAIL;
182 STDMETHODIMP nsAccessibleWrap::NotifyWinEvent(DWORD event,
183 HWND hwnd,
184 LONG idObjectType,
185 LONG idObject)
187 if (gmNotifyWinEvent)
188 return gmNotifyWinEvent(event, hwnd, idObjectType, idObject);
190 return E_FAIL;
193 STDMETHODIMP nsAccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
195 __try {
196 *ppdispParent = NULL;
197 if (!mWeakShell)
198 return E_FAIL; // We've been shut down
200 nsIFrame *frame = GetFrame();
201 HWND hwnd = 0;
202 if (frame) {
203 nsIView *view = frame->GetViewExternal();
204 if (view) {
205 // This code is essentially our implementation of WindowFromAccessibleObject,
206 // because MSAA iterates get_accParent() until it sees an object of ROLE_WINDOW
207 // to know where the window for a given accessible is. We must expose the native
208 // window accessible that MSAA creates for us. This must be done for the document
209 // object as well as any layout that creates its own window (e.g. via overflow: scroll)
210 nsIWidget *widget = view->GetWidget();
211 if (widget) {
212 hwnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW);
213 NS_ASSERTION(hwnd, "No window handle for window");
215 nsIViewManager* viewManager = view->GetViewManager();
216 if (!viewManager)
217 return E_UNEXPECTED;
219 nsIView *rootView;
220 viewManager->GetRootView(rootView);
221 if (rootView == view) {
222 // If the client accessible (OBJID_CLIENT) has a window but its window
223 // was created by an outer window then we want the native accessible
224 // for that outer window. If the accessible was created for outer
225 // window (if the outer window has inner windows then they share the
226 // same client accessible with it) then return native accessible for
227 // the outer window.
228 HWND parenthwnd = ::GetParent(hwnd);
229 if (parenthwnd)
230 hwnd = parenthwnd;
232 NS_ASSERTION(hwnd, "No window handle for window");
235 else {
236 // If a frame is a scrollable frame, then it has one window for the client area,
237 // not an extra parent window for just the scrollbars
238 nsIScrollableFrame *scrollFrame = do_QueryFrame(frame);
239 if (scrollFrame) {
240 hwnd = (HWND)scrollFrame->GetScrolledFrame()->GetNearestWidget()->GetNativeData(NS_NATIVE_WINDOW);
241 NS_ASSERTION(hwnd, "No window handle for window");
246 if (hwnd && SUCCEEDED(AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible,
247 (void**)ppdispParent))) {
248 return S_OK;
252 nsAccessible* xpParentAcc = GetParent();
253 NS_ASSERTION(xpParentAcc,
254 "No parent accessible where we're not direct child of window");
256 if (!xpParentAcc)
257 return E_UNEXPECTED;
259 *ppdispParent = NativeAccessible(xpParentAcc);
261 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
262 return S_OK;
265 STDMETHODIMP nsAccessibleWrap::get_accChildCount( long __RPC_FAR *pcountChildren)
267 __try {
268 *pcountChildren = 0;
269 if (nsAccUtils::MustPrune(this))
270 return NS_OK;
272 PRInt32 numChildren;
273 GetChildCount(&numChildren);
274 *pcountChildren = numChildren;
275 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
277 return S_OK;
280 STDMETHODIMP nsAccessibleWrap::get_accChild(
281 /* [in] */ VARIANT varChild,
282 /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild)
284 __try {
285 *ppdispChild = NULL;
286 if (!mWeakShell || varChild.vt != VT_I4)
287 return E_FAIL;
289 if (varChild.lVal == CHILDID_SELF) {
290 *ppdispChild = static_cast<IDispatch*>(this);
291 AddRef();
292 return S_OK;
295 if (!nsAccUtils::MustPrune(this)) {
296 nsAccessible* child = GetChildAt(varChild.lVal - 1);
297 if (child) {
298 *ppdispChild = NativeAccessible(child);
301 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
303 return (*ppdispChild)? S_OK: E_FAIL;
306 STDMETHODIMP nsAccessibleWrap::get_accName(
307 /* [optional][in] */ VARIANT varChild,
308 /* [retval][out] */ BSTR __RPC_FAR *pszName)
310 __try {
311 *pszName = NULL;
312 nsAccessible *xpAccessible = GetXPAccessibleFor(varChild);
313 if (!xpAccessible)
314 return E_FAIL;
315 nsAutoString name;
316 nsresult rv = xpAccessible->GetName(name);
317 if (NS_FAILED(rv))
318 return GetHRESULT(rv);
320 if (name.IsVoid()) {
321 // Valid return value for the name:
322 // The name was not provided, e.g. no alt attribute for an image.
323 // A screen reader may choose to invent its own accessible name, e.g. from
324 // an image src attribute.
325 // See nsHTMLImageAccessible::GetName()
326 return S_OK;
329 *pszName = ::SysAllocStringLen(name.get(), name.Length());
330 if (!*pszName)
331 return E_OUTOFMEMORY;
333 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
335 return S_OK;
339 STDMETHODIMP nsAccessibleWrap::get_accValue(
340 /* [optional][in] */ VARIANT varChild,
341 /* [retval][out] */ BSTR __RPC_FAR *pszValue)
343 __try {
344 *pszValue = NULL;
345 nsAccessible *xpAccessible = GetXPAccessibleFor(varChild);
346 if (xpAccessible) {
347 nsAutoString value;
348 if (NS_FAILED(xpAccessible->GetValue(value)))
349 return E_FAIL;
351 // see bug 438784: Need to expose URL on doc's value attribute.
352 // For this, reverting part of fix for bug 425693 to make this MSAA method
353 // behave IAccessible2-style.
354 if (value.IsEmpty())
355 return S_FALSE;
357 *pszValue = ::SysAllocStringLen(value.get(), value.Length());
358 if (!*pszValue)
359 return E_OUTOFMEMORY;
361 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
362 return S_OK;
365 STDMETHODIMP
366 nsAccessibleWrap::get_accDescription(VARIANT varChild,
367 BSTR __RPC_FAR *pszDescription)
369 __try {
370 *pszDescription = NULL;
372 nsAccessible *xpAccessible = GetXPAccessibleFor(varChild);
373 if (!xpAccessible)
374 return E_FAIL;
376 // For items that are a choice in a list of choices, use MSAA description
377 // field to shoehorn positional info, it's becoming a defacto standard use for
378 // the field.
380 nsAutoString description;
382 // Try to get group position to make a positional description string.
383 PRInt32 groupLevel = 0;
384 PRInt32 itemsInGroup = 0;
385 PRInt32 positionInGroup = 0;
386 GroupPosition(&groupLevel, &itemsInGroup, &positionInGroup);
388 if (positionInGroup > 0) {
389 if (groupLevel > 0) {
390 // XXX: How do we calculate the number of children? Now we append
391 // " with [numChildren]c" for tree item. In the future we may need to
392 // use the ARIA owns property to calculate that if it's present.
393 PRInt32 numChildren = 0;
395 PRUint32 currentRole = nsAccUtils::Role(xpAccessible);
396 if (currentRole == nsIAccessibleRole::ROLE_OUTLINEITEM) {
397 PRInt32 childCount = xpAccessible->GetChildCount();
398 for (PRInt32 childIdx = 0; childIdx < childCount; childIdx++) {
399 nsAccessible *child = xpAccessible->GetChildAt(childIdx);
400 currentRole = nsAccUtils::Role(child);
401 if (currentRole == nsIAccessibleRole::ROLE_GROUPING) {
402 PRInt32 groupChildCount = child->GetChildCount();
403 for (PRInt32 groupChildIdx = 0; groupChildIdx < groupChildCount;
404 groupChildIdx++) {
405 nsAccessible *groupChild = child->GetChildAt(groupChildIdx);
406 currentRole = nsAccUtils::Role(groupChild);
407 numChildren +=
408 (currentRole == nsIAccessibleRole::ROLE_OUTLINEITEM);
410 break;
415 if (numChildren) {
416 nsTextFormatter::ssprintf(description,
417 NS_LITERAL_STRING("L%d, %d of %d with %d").get(),
418 groupLevel, positionInGroup, itemsInGroup,
419 numChildren);
420 } else {
421 nsTextFormatter::ssprintf(description,
422 NS_LITERAL_STRING("L%d, %d of %d").get(),
423 groupLevel, positionInGroup, itemsInGroup);
425 } else { // Position has no level
426 nsTextFormatter::ssprintf(description,
427 NS_LITERAL_STRING("%d of %d").get(),
428 positionInGroup, itemsInGroup);
430 } else if (groupLevel > 0) {
431 nsTextFormatter::ssprintf(description, NS_LITERAL_STRING("L%d").get(),
432 groupLevel);
435 if (description.IsEmpty())
436 xpAccessible->GetDescription(description);
438 *pszDescription = ::SysAllocStringLen(description.get(),
439 description.Length());
440 return *pszDescription ? S_OK : E_OUTOFMEMORY;
442 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
443 return E_FAIL;
446 STDMETHODIMP nsAccessibleWrap::get_accRole(
447 /* [optional][in] */ VARIANT varChild,
448 /* [retval][out] */ VARIANT __RPC_FAR *pvarRole)
450 __try {
451 VariantInit(pvarRole);
453 nsAccessible *xpAccessible = GetXPAccessibleFor(varChild);
454 if (!xpAccessible)
455 return E_FAIL;
457 #ifdef DEBUG_A11Y
458 NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible),
459 "Does not support nsIAccessibleText when it should");
460 #endif
462 PRUint32 xpRole = 0, msaaRole = 0;
463 if (NS_FAILED(xpAccessible->GetRole(&xpRole)))
464 return E_FAIL;
466 msaaRole = gWindowsRoleMap[xpRole].msaaRole;
467 NS_ASSERTION(gWindowsRoleMap[nsIAccessibleRole::ROLE_LAST_ENTRY].msaaRole == ROLE_WINDOWS_LAST_ENTRY,
468 "MSAA role map skewed");
470 // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call the MSAA role
471 // a ROLE_OUTLINEITEM for consistency and compatibility.
472 // We need this because ARIA has a role of "row" for both grid and treegrid
473 if (xpRole == nsIAccessibleRole::ROLE_ROW) {
474 if (nsAccUtils::Role(GetParent()) == nsIAccessibleRole::ROLE_TREE_TABLE)
475 msaaRole = ROLE_SYSTEM_OUTLINEITEM;
478 // -- Try enumerated role
479 if (msaaRole != USE_ROLE_STRING) {
480 pvarRole->vt = VT_I4;
481 pvarRole->lVal = msaaRole; // Normal enumerated role
482 return S_OK;
485 // -- Try BSTR role
486 // Could not map to known enumerated MSAA role like ROLE_BUTTON
487 // Use BSTR role to expose role attribute or tag name + namespace
488 nsIContent *content = xpAccessible->GetContent();
489 if (!content)
490 return E_FAIL;
492 if (content->IsElement()) {
493 nsAutoString roleString;
494 if (msaaRole != ROLE_SYSTEM_CLIENT &&
495 !content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::role, roleString)) {
496 nsIDocument * document = content->GetCurrentDoc();
497 if (!document)
498 return E_FAIL;
500 nsINodeInfo *nodeInfo = content->NodeInfo();
501 nodeInfo->GetName(roleString);
503 // Only append name space if different from that of current document.
504 if (!nodeInfo->NamespaceEquals(document->GetDefaultNamespaceID())) {
505 nsAutoString nameSpaceURI;
506 nodeInfo->GetNamespaceURI(nameSpaceURI);
507 roleString += NS_LITERAL_STRING(", ") + nameSpaceURI;
511 if (!roleString.IsEmpty()) {
512 pvarRole->vt = VT_BSTR;
513 pvarRole->bstrVal = ::SysAllocString(roleString.get());
514 return S_OK;
517 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
518 return E_FAIL;
521 STDMETHODIMP nsAccessibleWrap::get_accState(
522 /* [optional][in] */ VARIANT varChild,
523 /* [retval][out] */ VARIANT __RPC_FAR *pvarState)
525 __try {
526 VariantInit(pvarState);
527 pvarState->vt = VT_I4;
528 pvarState->lVal = 0;
530 nsAccessible *xpAccessible = GetXPAccessibleFor(varChild);
531 if (!xpAccessible)
532 return E_FAIL;
534 PRUint32 state = 0;
535 if (NS_FAILED(xpAccessible->GetState(&state, nsnull)))
536 return E_FAIL;
538 pvarState->lVal = state;
539 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
540 return S_OK;
544 STDMETHODIMP nsAccessibleWrap::get_accHelp(
545 /* [optional][in] */ VARIANT varChild,
546 /* [retval][out] */ BSTR __RPC_FAR *pszHelp)
548 __try {
549 *pszHelp = NULL;
550 return S_FALSE;
552 } __except(FilterA11yExceptions(::GetExceptionCode(),
553 GetExceptionInformation())) { }
554 return E_FAIL;
557 STDMETHODIMP nsAccessibleWrap::get_accHelpTopic(
558 /* [out] */ BSTR __RPC_FAR *pszHelpFile,
559 /* [optional][in] */ VARIANT varChild,
560 /* [retval][out] */ long __RPC_FAR *pidTopic)
562 __try {
563 *pszHelpFile = NULL;
564 *pidTopic = 0;
565 return S_FALSE;
567 } __except(FilterA11yExceptions(::GetExceptionCode(),
568 GetExceptionInformation())) { }
569 return E_FAIL;
572 STDMETHODIMP nsAccessibleWrap::get_accKeyboardShortcut(
573 /* [optional][in] */ VARIANT varChild,
574 /* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut)
576 __try {
577 *pszKeyboardShortcut = NULL;
578 nsAccessible *xpAccessible = GetXPAccessibleFor(varChild);
579 if (xpAccessible) {
580 nsAutoString shortcut;
581 nsresult rv = xpAccessible->GetKeyboardShortcut(shortcut);
582 if (NS_FAILED(rv))
583 return E_FAIL;
585 *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(),
586 shortcut.Length());
587 return *pszKeyboardShortcut ? S_OK : E_OUTOFMEMORY;
589 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
590 return E_FAIL;
593 STDMETHODIMP nsAccessibleWrap::get_accFocus(
594 /* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
596 // VT_EMPTY: None. This object does not have the keyboard focus itself
597 // and does not contain a child that has the keyboard focus.
598 // VT_I4: lVal is CHILDID_SELF. The object itself has the keyboard focus.
599 // VT_I4: lVal contains the child ID of the child element with the keyboard focus.
600 // VT_DISPATCH: pdispVal member is the address of the IDispatch interface
601 // for the child object with the keyboard focus.
602 __try {
603 if (IsDefunct())
604 return E_FAIL;
606 VariantInit(pvarChild);
608 // Return the current IAccessible child that has focus
609 nsCOMPtr<nsIAccessible> focusedAccessible;
610 GetFocusedChild(getter_AddRefs(focusedAccessible));
611 if (focusedAccessible == this) {
612 pvarChild->vt = VT_I4;
613 pvarChild->lVal = CHILDID_SELF;
615 else if (focusedAccessible) {
616 pvarChild->vt = VT_DISPATCH;
617 pvarChild->pdispVal = NativeAccessible(focusedAccessible);
619 else {
620 pvarChild->vt = VT_EMPTY; // No focus or focus is not a child
623 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
624 return S_OK;
627 // This helper class implements IEnumVARIANT for a nsIArray containing nsIAccessible objects.
629 class AccessibleEnumerator : public IEnumVARIANT
631 public:
632 AccessibleEnumerator(nsIArray* aArray) : mArray(aArray), mCurIndex(0) { }
633 AccessibleEnumerator(const AccessibleEnumerator& toCopy) :
634 mArray(toCopy.mArray), mCurIndex(toCopy.mCurIndex) { }
635 ~AccessibleEnumerator() { }
637 // IUnknown
638 STDMETHODIMP QueryInterface(REFIID iid, void ** ppvObject);
639 STDMETHODIMP_(ULONG) AddRef(void);
640 STDMETHODIMP_(ULONG) Release(void);
642 // IEnumVARIANT
643 STDMETHODIMP Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched);
644 STDMETHODIMP Skip(unsigned long celt);
645 STDMETHODIMP Reset()
647 mCurIndex = 0;
648 return S_OK;
650 STDMETHODIMP Clone(IEnumVARIANT FAR* FAR* ppenum);
652 private:
653 nsCOMPtr<nsIArray> mArray;
654 PRUint32 mCurIndex;
655 nsAutoRefCnt mRefCnt;
658 HRESULT
659 AccessibleEnumerator::QueryInterface(REFIID iid, void ** ppvObject)
661 __try {
662 if (iid == IID_IEnumVARIANT) {
663 *ppvObject = static_cast<IEnumVARIANT*>(this);
664 AddRef();
665 return S_OK;
667 if (iid == IID_IUnknown) {
668 *ppvObject = static_cast<IUnknown*>(this);
669 AddRef();
670 return S_OK;
673 *ppvObject = NULL;
674 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
675 return E_NOINTERFACE;
678 STDMETHODIMP_(ULONG)
679 AccessibleEnumerator::AddRef(void)
681 return ++mRefCnt;
684 STDMETHODIMP_(ULONG)
685 AccessibleEnumerator::Release(void)
687 ULONG r = --mRefCnt;
688 if (r == 0)
689 delete this;
690 return r;
693 STDMETHODIMP
694 AccessibleEnumerator::Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched)
696 __try {
697 PRUint32 length = 0;
698 mArray->GetLength(&length);
700 HRESULT hr = S_OK;
702 // Can't get more elements than there are...
703 if (celt > length - mCurIndex) {
704 hr = S_FALSE;
705 celt = length - mCurIndex;
708 for (PRUint32 i = 0; i < celt; ++i, ++mCurIndex) {
709 // Copy the elements of the array into rgvar
710 nsCOMPtr<nsIAccessible> accel(do_QueryElementAt(mArray, mCurIndex));
711 NS_ASSERTION(accel, "Invalid pointer in mArray");
713 if (accel) {
714 rgvar[i].vt = VT_DISPATCH;
715 rgvar[i].pdispVal = nsAccessibleWrap::NativeAccessible(accel);
719 if (pceltFetched)
720 *pceltFetched = celt;
722 return hr;
723 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
725 return S_OK;
728 STDMETHODIMP
729 AccessibleEnumerator::Clone(IEnumVARIANT FAR* FAR* ppenum)
731 __try {
732 *ppenum = new AccessibleEnumerator(*this);
733 if (!*ppenum)
734 return E_OUTOFMEMORY;
735 NS_ADDREF(*ppenum);
736 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
737 return S_OK;
740 STDMETHODIMP
741 AccessibleEnumerator::Skip(unsigned long celt)
743 __try {
744 PRUint32 length = 0;
745 mArray->GetLength(&length);
746 // Check if we can skip the requested number of elements
747 if (celt > length - mCurIndex) {
748 mCurIndex = length;
749 return S_FALSE;
751 mCurIndex += celt;
752 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
753 return S_OK;
757 * This method is called when a client wants to know which children of a node
758 * are selected. Note that this method can only find selected children for
759 * nsIAccessible object which implement nsIAccessibleSelectable.
761 * The VARIANT return value arguement is expected to either contain a single IAccessible
762 * or an IEnumVARIANT of IAccessibles. We return the IEnumVARIANT regardless of the number
763 * of children selected, unless there are none selected in which case we return an empty
764 * VARIANT.
766 * We get the selected options from the select's accessible object and wrap
767 * those in an AccessibleEnumerator which we then put in the return VARIANT.
769 * returns a VT_EMPTY VARIANT if:
770 * - there are no selected children for this object
771 * - the object is not the type that can have children selected
773 STDMETHODIMP nsAccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren)
775 __try {
776 VariantInit(pvarChildren);
777 pvarChildren->vt = VT_EMPTY;
779 nsCOMPtr<nsIAccessibleSelectable>
780 select(do_QueryInterface(static_cast<nsIAccessible*>(this)));
782 if (select) { // do we have an nsIAccessibleSelectable?
783 // we have an accessible that can have children selected
784 nsCOMPtr<nsIArray> selectedOptions;
785 // gets the selected options as nsIAccessibles.
786 select->GetSelectedChildren(getter_AddRefs(selectedOptions));
787 if (selectedOptions) { // false if the select has no children or none are selected
788 // 1) Create and initialize the enumeration
789 nsRefPtr<AccessibleEnumerator> pEnum = new AccessibleEnumerator(selectedOptions);
791 // 2) Put the enumerator in the VARIANT
792 if (!pEnum)
793 return E_OUTOFMEMORY;
794 pvarChildren->vt = VT_UNKNOWN; // this must be VT_UNKNOWN for an IEnumVARIANT
795 NS_ADDREF(pvarChildren->punkVal = pEnum);
798 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
799 return S_OK;
802 STDMETHODIMP nsAccessibleWrap::get_accDefaultAction(
803 /* [optional][in] */ VARIANT varChild,
804 /* [retval][out] */ BSTR __RPC_FAR *pszDefaultAction)
806 __try {
807 *pszDefaultAction = NULL;
808 nsAccessible *xpAccessible = GetXPAccessibleFor(varChild);
809 if (xpAccessible) {
810 nsAutoString defaultAction;
811 if (NS_FAILED(xpAccessible->GetActionName(0, defaultAction)))
812 return E_FAIL;
814 *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(),
815 defaultAction.Length());
816 return *pszDefaultAction ? S_OK : E_OUTOFMEMORY;
819 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
820 return E_FAIL;
823 STDMETHODIMP nsAccessibleWrap::accSelect(
824 /* [in] */ long flagsSelect,
825 /* [optional][in] */ VARIANT varChild)
827 __try {
828 // currently only handle focus and selection
829 nsAccessible *xpAccessible = GetXPAccessibleFor(varChild);
830 NS_ENSURE_TRUE(xpAccessible, E_FAIL);
832 if (flagsSelect & (SELFLAG_TAKEFOCUS|SELFLAG_TAKESELECTION|SELFLAG_REMOVESELECTION))
834 if (flagsSelect & SELFLAG_TAKEFOCUS)
835 xpAccessible->TakeFocus();
837 if (flagsSelect & SELFLAG_TAKESELECTION)
838 xpAccessible->TakeSelection();
840 if (flagsSelect & SELFLAG_ADDSELECTION)
841 xpAccessible->SetSelected(PR_TRUE);
843 if (flagsSelect & SELFLAG_REMOVESELECTION)
844 xpAccessible->SetSelected(PR_FALSE);
846 if (flagsSelect & SELFLAG_EXTENDSELECTION)
847 xpAccessible->ExtendSelection();
849 return S_OK;
852 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
853 return E_FAIL;
856 STDMETHODIMP nsAccessibleWrap::accLocation(
857 /* [out] */ long __RPC_FAR *pxLeft,
858 /* [out] */ long __RPC_FAR *pyTop,
859 /* [out] */ long __RPC_FAR *pcxWidth,
860 /* [out] */ long __RPC_FAR *pcyHeight,
861 /* [optional][in] */ VARIANT varChild)
863 __try {
864 nsAccessible *xpAccessible = GetXPAccessibleFor(varChild);
866 if (xpAccessible) {
867 PRInt32 x, y, width, height;
868 if (NS_FAILED(xpAccessible->GetBounds(&x, &y, &width, &height)))
869 return E_FAIL;
871 *pxLeft = x;
872 *pyTop = y;
873 *pcxWidth = width;
874 *pcyHeight = height;
875 return S_OK;
877 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
879 return E_FAIL;
882 STDMETHODIMP nsAccessibleWrap::accNavigate(
883 /* [in] */ long navDir,
884 /* [optional][in] */ VARIANT varStart,
885 /* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt)
887 __try {
888 nsAccessible *xpAccessibleStart = GetXPAccessibleFor(varStart);
889 if (!xpAccessibleStart)
890 return E_FAIL;
892 VariantInit(pvarEndUpAt);
894 nsCOMPtr<nsIAccessible> xpAccessibleResult;
895 PRUint32 xpRelation = 0;
897 switch(navDir) {
898 case NAVDIR_DOWN:
899 xpAccessibleStart->GetAccessibleBelow(getter_AddRefs(xpAccessibleResult));
900 break;
901 case NAVDIR_FIRSTCHILD:
902 if (!nsAccUtils::MustPrune(xpAccessibleStart))
903 xpAccessibleStart->GetFirstChild(getter_AddRefs(xpAccessibleResult));
904 break;
905 case NAVDIR_LASTCHILD:
906 if (!nsAccUtils::MustPrune(xpAccessibleStart))
907 xpAccessibleStart->GetLastChild(getter_AddRefs(xpAccessibleResult));
908 break;
909 case NAVDIR_LEFT:
910 xpAccessibleStart->GetAccessibleToLeft(getter_AddRefs(xpAccessibleResult));
911 break;
912 case NAVDIR_NEXT:
913 xpAccessibleStart->GetNextSibling(getter_AddRefs(xpAccessibleResult));
914 break;
915 case NAVDIR_PREVIOUS:
916 xpAccessibleStart->GetPreviousSibling(getter_AddRefs(xpAccessibleResult));
917 break;
918 case NAVDIR_RIGHT:
919 xpAccessibleStart->GetAccessibleToRight(getter_AddRefs(xpAccessibleResult));
920 break;
921 case NAVDIR_UP:
922 xpAccessibleStart->GetAccessibleAbove(getter_AddRefs(xpAccessibleResult));
923 break;
925 // MSAA relationship extensions to accNavigate
926 case NAVRELATION_CONTROLLED_BY:
927 xpRelation = nsIAccessibleRelation::RELATION_CONTROLLED_BY;
928 break;
929 case NAVRELATION_CONTROLLER_FOR:
930 xpRelation = nsIAccessibleRelation::RELATION_CONTROLLER_FOR;
931 break;
932 case NAVRELATION_LABEL_FOR:
933 xpRelation = nsIAccessibleRelation::RELATION_LABEL_FOR;
934 break;
935 case NAVRELATION_LABELLED_BY:
936 xpRelation = nsIAccessibleRelation::RELATION_LABELLED_BY;
937 break;
938 case NAVRELATION_MEMBER_OF:
939 xpRelation = nsIAccessibleRelation::RELATION_MEMBER_OF;
940 break;
941 case NAVRELATION_NODE_CHILD_OF:
942 xpRelation = nsIAccessibleRelation::RELATION_NODE_CHILD_OF;
943 break;
944 case NAVRELATION_FLOWS_TO:
945 xpRelation = nsIAccessibleRelation::RELATION_FLOWS_TO;
946 break;
947 case NAVRELATION_FLOWS_FROM:
948 xpRelation = nsIAccessibleRelation::RELATION_FLOWS_FROM;
949 break;
950 case NAVRELATION_SUBWINDOW_OF:
951 xpRelation = nsIAccessibleRelation::RELATION_SUBWINDOW_OF;
952 break;
953 case NAVRELATION_EMBEDS:
954 xpRelation = nsIAccessibleRelation::RELATION_EMBEDS;
955 break;
956 case NAVRELATION_EMBEDDED_BY:
957 xpRelation = nsIAccessibleRelation::RELATION_EMBEDDED_BY;
958 break;
959 case NAVRELATION_POPUP_FOR:
960 xpRelation = nsIAccessibleRelation::RELATION_POPUP_FOR;
961 break;
962 case NAVRELATION_PARENT_WINDOW_OF:
963 xpRelation = nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF;
964 break;
965 case NAVRELATION_DEFAULT_BUTTON:
966 xpRelation = nsIAccessibleRelation::RELATION_DEFAULT_BUTTON;
967 break;
968 case NAVRELATION_DESCRIBED_BY:
969 xpRelation = nsIAccessibleRelation::RELATION_DESCRIBED_BY;
970 break;
971 case NAVRELATION_DESCRIPTION_FOR:
972 xpRelation = nsIAccessibleRelation::RELATION_DESCRIPTION_FOR;
973 break;
976 pvarEndUpAt->vt = VT_EMPTY;
978 if (xpRelation)
979 xpAccessibleResult = nsRelUtils::GetRelatedAccessible(this, xpRelation);
981 if (xpAccessibleResult) {
982 pvarEndUpAt->pdispVal = NativeAccessible(xpAccessibleResult);
983 pvarEndUpAt->vt = VT_DISPATCH;
984 return NS_OK;
986 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
987 return E_FAIL;
990 STDMETHODIMP nsAccessibleWrap::accHitTest(
991 /* [in] */ long xLeft,
992 /* [in] */ long yTop,
993 /* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
995 __try {
996 VariantInit(pvarChild);
998 // convert to window coords
999 nsCOMPtr<nsIAccessible> xpAccessible;
1001 xLeft = xLeft;
1002 yTop = yTop;
1004 if (nsAccUtils::MustPrune(this)) {
1005 xpAccessible = this;
1007 else {
1008 GetChildAtPoint(xLeft, yTop, getter_AddRefs(xpAccessible));
1011 // if we got a child
1012 if (xpAccessible) {
1013 // if the child is us
1014 if (xpAccessible == static_cast<nsIAccessible*>(this)) {
1015 pvarChild->vt = VT_I4;
1016 pvarChild->lVal = CHILDID_SELF;
1017 } else { // its not create an Accessible for it.
1018 pvarChild->vt = VT_DISPATCH;
1019 pvarChild->pdispVal = NativeAccessible(xpAccessible);
1020 nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(xpAccessible));
1021 NS_ASSERTION(accessNode, "Unable to QI to nsIAccessNode");
1022 nsCOMPtr<nsIDOMNode> domNode;
1023 accessNode->GetDOMNode(getter_AddRefs(domNode));
1024 if (!domNode) {
1025 // Has already been shut down
1026 pvarChild->vt = VT_EMPTY;
1027 return E_FAIL;
1030 } else {
1031 // no child at that point
1032 pvarChild->vt = VT_EMPTY;
1033 return S_FALSE;
1035 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1037 return S_OK;
1040 STDMETHODIMP nsAccessibleWrap::accDoDefaultAction(
1041 /* [optional][in] */ VARIANT varChild)
1043 __try {
1044 nsAccessible *xpAccessible = GetXPAccessibleFor(varChild);
1045 if (!xpAccessible || FAILED(xpAccessible->DoAction(0))) {
1046 return E_FAIL;
1048 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1049 return S_OK;
1052 STDMETHODIMP nsAccessibleWrap::put_accName(
1053 /* [optional][in] */ VARIANT varChild,
1054 /* [in] */ BSTR szName)
1056 return E_NOTIMPL;
1059 STDMETHODIMP nsAccessibleWrap::put_accValue(
1060 /* [optional][in] */ VARIANT varChild,
1061 /* [in] */ BSTR szValue)
1063 return E_NOTIMPL;
1066 #include "mshtml.h"
1068 ////////////////////////////////////////////////////////////////////////////////
1069 // nsAccessibleWrap. IEnumVariant
1071 STDMETHODIMP
1072 nsAccessibleWrap::Next(ULONG aNumElementsRequested, VARIANT FAR* aPVar,
1073 ULONG FAR* aNumElementsFetched)
1075 // Children already cached via QI to IEnumVARIANT
1076 __try {
1077 *aNumElementsFetched = 0;
1079 if (aNumElementsRequested <= 0 || !aPVar)
1080 return E_INVALIDARG;
1082 if (mEnumVARIANTPosition == kIEnumVariantDisconnected)
1083 return CO_E_OBJNOTCONNECTED;
1085 PRUint32 numElementsFetched = 0;
1086 for (; numElementsFetched < aNumElementsRequested;
1087 numElementsFetched++, mEnumVARIANTPosition++) {
1089 nsAccessible* accessible = GetChildAt(mEnumVARIANTPosition);
1090 if (!accessible)
1091 break;
1093 VariantInit(&aPVar[numElementsFetched]);
1095 aPVar[numElementsFetched].pdispVal = NativeAccessible(accessible);
1096 aPVar[numElementsFetched].vt = VT_DISPATCH;
1099 (*aNumElementsFetched) = numElementsFetched;
1101 return numElementsFetched < aNumElementsRequested ? S_FALSE : S_OK;
1103 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1104 return E_FAIL;
1107 STDMETHODIMP
1108 nsAccessibleWrap::Skip(ULONG aNumElements)
1110 __try {
1111 if (mEnumVARIANTPosition == kIEnumVariantDisconnected)
1112 return CO_E_OBJNOTCONNECTED;
1114 mEnumVARIANTPosition += aNumElements;
1116 PRInt32 numChildren;
1117 GetChildCount(&numChildren);
1119 if (mEnumVARIANTPosition > numChildren)
1121 mEnumVARIANTPosition = numChildren;
1122 return S_FALSE;
1124 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1125 return NOERROR;
1128 STDMETHODIMP
1129 nsAccessibleWrap::Reset(void)
1131 mEnumVARIANTPosition = 0;
1132 return NOERROR;
1135 STDMETHODIMP
1136 nsAccessibleWrap::Clone(IEnumVARIANT FAR* FAR* ppenum)
1138 __try {
1139 *ppenum = nsnull;
1141 nsCOMPtr<nsIArray> childArray;
1142 nsresult rv = GetChildren(getter_AddRefs(childArray));
1144 *ppenum = new AccessibleEnumerator(childArray);
1145 if (!*ppenum)
1146 return E_OUTOFMEMORY;
1147 NS_ADDREF(*ppenum);
1149 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1150 return NOERROR;
1153 ////////////////////////////////////////////////////////////////////////////////
1154 // nsAccessibleWrap. IAccessible2
1156 STDMETHODIMP
1157 nsAccessibleWrap::get_nRelations(long *aNRelations)
1159 __try {
1160 PRUint32 count = 0;
1161 nsresult rv = GetRelationsCount(&count);
1162 *aNRelations = count;
1164 return GetHRESULT(rv);
1166 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1167 return E_FAIL;
1170 STDMETHODIMP
1171 nsAccessibleWrap::get_relation(long aRelationIndex,
1172 IAccessibleRelation **aRelation)
1174 __try {
1175 *aRelation = NULL;
1177 nsCOMPtr<nsIAccessibleRelation> relation;
1178 nsresult rv = GetRelation(aRelationIndex, getter_AddRefs(relation));
1179 if (NS_FAILED(rv))
1180 return GetHRESULT(rv);
1182 nsCOMPtr<nsIWinAccessNode> winAccessNode(do_QueryInterface(relation));
1183 if (!winAccessNode)
1184 return E_FAIL;
1186 void *instancePtr = NULL;
1187 rv = winAccessNode->QueryNativeInterface(IID_IAccessibleRelation,
1188 &instancePtr);
1189 if (NS_FAILED(rv))
1190 return GetHRESULT(rv);
1192 *aRelation = static_cast<IAccessibleRelation*>(instancePtr);
1193 return S_OK;
1195 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1196 return E_FAIL;
1199 STDMETHODIMP
1200 nsAccessibleWrap::get_relations(long aMaxRelations,
1201 IAccessibleRelation **aRelation,
1202 long *aNRelations)
1204 __try {
1205 *aRelation = NULL;
1206 *aNRelations = 0;
1208 nsCOMPtr<nsIArray> relations;
1209 nsresult rv = GetRelations(getter_AddRefs(relations));
1210 if (NS_FAILED(rv))
1211 return GetHRESULT(rv);
1213 PRUint32 length = 0;
1214 rv = relations->GetLength(&length);
1215 if (NS_FAILED(rv))
1216 return GetHRESULT(rv);
1218 if (length == 0)
1219 return S_FALSE;
1221 PRUint32 count = length < (PRUint32)aMaxRelations ? length : aMaxRelations;
1223 PRUint32 index = 0;
1224 for (; index < count; index++) {
1225 nsCOMPtr<nsIWinAccessNode> winAccessNode =
1226 do_QueryElementAt(relations, index, &rv);
1227 if (NS_FAILED(rv))
1228 break;
1230 void *instancePtr = NULL;
1231 nsresult rv = winAccessNode->QueryNativeInterface(IID_IAccessibleRelation,
1232 &instancePtr);
1233 if (NS_FAILED(rv))
1234 break;
1236 aRelation[index] = static_cast<IAccessibleRelation*>(instancePtr);
1239 if (NS_FAILED(rv)) {
1240 for (PRUint32 index2 = 0; index2 < index; index2++) {
1241 aRelation[index2]->Release();
1242 aRelation[index2] = NULL;
1244 return GetHRESULT(rv);
1247 *aNRelations = count;
1248 return S_OK;
1250 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1251 return E_FAIL;
1254 STDMETHODIMP
1255 nsAccessibleWrap::role(long *aRole)
1257 __try {
1258 *aRole = 0;
1260 PRUint32 xpRole = 0;
1261 nsresult rv = GetRole(&xpRole);
1262 if (NS_FAILED(rv))
1263 return GetHRESULT(rv);
1265 NS_ASSERTION(gWindowsRoleMap[nsIAccessibleRole::ROLE_LAST_ENTRY].ia2Role == ROLE_WINDOWS_LAST_ENTRY,
1266 "MSAA role map skewed");
1268 *aRole = gWindowsRoleMap[xpRole].ia2Role;
1270 // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call
1271 // the IA2 role a ROLE_OUTLINEITEM.
1272 if (xpRole == nsIAccessibleRole::ROLE_ROW) {
1273 if (nsAccUtils::Role(GetParent()) == nsIAccessibleRole::ROLE_TREE_TABLE)
1274 *aRole = ROLE_SYSTEM_OUTLINEITEM;
1277 return S_OK;
1279 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1280 return E_FAIL;
1283 STDMETHODIMP
1284 nsAccessibleWrap::scrollTo(enum IA2ScrollType aScrollType)
1286 __try {
1287 nsresult rv = ScrollTo(aScrollType);
1288 return GetHRESULT(rv);
1290 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1291 return E_FAIL;
1294 STDMETHODIMP
1295 nsAccessibleWrap::scrollToPoint(enum IA2CoordinateType aCoordType,
1296 long aX, long aY)
1298 __try {
1299 PRUint32 geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
1300 nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
1301 nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
1303 nsresult rv = ScrollToPoint(geckoCoordType, aX, aY);
1304 return GetHRESULT(rv);
1306 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1307 return E_FAIL;
1310 STDMETHODIMP
1311 nsAccessibleWrap::get_groupPosition(long *aGroupLevel,
1312 long *aSimilarItemsInGroup,
1313 long *aPositionInGroup)
1315 __try {
1316 PRInt32 groupLevel = 0;
1317 PRInt32 similarItemsInGroup = 0;
1318 PRInt32 positionInGroup = 0;
1320 nsresult rv = GroupPosition(&groupLevel, &similarItemsInGroup,
1321 &positionInGroup);
1322 if (NS_FAILED(rv))
1323 return GetHRESULT(rv);
1325 // Group information for accessibles having level only (like html headings
1326 // elements) isn't exposed by this method. AT should look for 'level' object
1327 // attribute.
1328 if (!similarItemsInGroup && !positionInGroup)
1329 return S_FALSE;
1331 *aGroupLevel = groupLevel;
1332 *aSimilarItemsInGroup = similarItemsInGroup;
1333 *aPositionInGroup = positionInGroup;
1335 return S_OK;
1337 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1338 return E_FAIL;
1341 STDMETHODIMP
1342 nsAccessibleWrap::get_states(AccessibleStates *aStates)
1344 __try {
1345 *aStates = 0;
1347 // XXX: bug 344674 should come with better approach that we have here.
1349 PRUint32 states = 0, extraStates = 0;
1350 nsresult rv = GetState(&states, &extraStates);
1351 if (NS_FAILED(rv))
1352 return GetHRESULT(rv);
1354 if (states & nsIAccessibleStates::STATE_INVALID)
1355 *aStates |= IA2_STATE_INVALID_ENTRY;
1356 if (states & nsIAccessibleStates::STATE_REQUIRED)
1357 *aStates |= IA2_STATE_REQUIRED;
1359 // The following IA2 states are not supported by Gecko
1360 // IA2_STATE_ARMED
1361 // IA2_STATE_MANAGES_DESCENDANTS
1362 // IA2_STATE_ICONIFIED
1363 // IA2_STATE_INVALID // This is not a state, it is the absence of a state
1365 if (extraStates & nsIAccessibleStates::EXT_STATE_ACTIVE)
1366 *aStates |= IA2_STATE_ACTIVE;
1367 if (extraStates & nsIAccessibleStates::EXT_STATE_DEFUNCT)
1368 *aStates |= IA2_STATE_DEFUNCT;
1369 if (extraStates & nsIAccessibleStates::EXT_STATE_EDITABLE)
1370 *aStates |= IA2_STATE_EDITABLE;
1371 if (extraStates & nsIAccessibleStates::EXT_STATE_HORIZONTAL)
1372 *aStates |= IA2_STATE_HORIZONTAL;
1373 if (extraStates & nsIAccessibleStates::EXT_STATE_MODAL)
1374 *aStates |= IA2_STATE_MODAL;
1375 if (extraStates & nsIAccessibleStates::EXT_STATE_MULTI_LINE)
1376 *aStates |= IA2_STATE_MULTI_LINE;
1377 if (extraStates & nsIAccessibleStates::EXT_STATE_OPAQUE)
1378 *aStates |= IA2_STATE_OPAQUE;
1379 if (extraStates & nsIAccessibleStates::EXT_STATE_SELECTABLE_TEXT)
1380 *aStates |= IA2_STATE_SELECTABLE_TEXT;
1381 if (extraStates & nsIAccessibleStates::EXT_STATE_SINGLE_LINE)
1382 *aStates |= IA2_STATE_SINGLE_LINE;
1383 if (extraStates & nsIAccessibleStates::EXT_STATE_STALE)
1384 *aStates |= IA2_STATE_STALE;
1385 if (extraStates & nsIAccessibleStates::EXT_STATE_SUPPORTS_AUTOCOMPLETION)
1386 *aStates |= IA2_STATE_SUPPORTS_AUTOCOMPLETION;
1387 if (extraStates & nsIAccessibleStates::EXT_STATE_TRANSIENT)
1388 *aStates |= IA2_STATE_TRANSIENT;
1389 if (extraStates & nsIAccessibleStates::EXT_STATE_VERTICAL)
1390 *aStates |= IA2_STATE_VERTICAL;
1392 return S_OK;
1394 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1395 return E_FAIL;
1398 STDMETHODIMP
1399 nsAccessibleWrap::get_extendedRole(BSTR *aExtendedRole)
1401 __try {
1402 *aExtendedRole = NULL;
1403 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1405 return E_NOTIMPL;
1408 STDMETHODIMP
1409 nsAccessibleWrap::get_localizedExtendedRole(BSTR *aLocalizedExtendedRole)
1411 __try {
1412 *aLocalizedExtendedRole = NULL;
1413 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1415 return E_NOTIMPL;
1418 STDMETHODIMP
1419 nsAccessibleWrap::get_nExtendedStates(long *aNExtendedStates)
1421 __try {
1422 *aNExtendedStates = 0;
1423 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1425 return E_NOTIMPL;
1428 STDMETHODIMP
1429 nsAccessibleWrap::get_extendedStates(long aMaxExtendedStates,
1430 BSTR **aExtendedStates,
1431 long *aNExtendedStates)
1433 __try {
1434 *aExtendedStates = NULL;
1435 *aNExtendedStates = 0;
1436 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1438 return E_NOTIMPL;
1441 STDMETHODIMP
1442 nsAccessibleWrap::get_localizedExtendedStates(long aMaxLocalizedExtendedStates,
1443 BSTR **aLocalizedExtendedStates,
1444 long *aNLocalizedExtendedStates)
1446 __try {
1447 *aLocalizedExtendedStates = NULL;
1448 *aNLocalizedExtendedStates = 0;
1449 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1451 return E_NOTIMPL;
1454 STDMETHODIMP
1455 nsAccessibleWrap::get_uniqueID(long *uniqueID)
1457 __try {
1458 void *id = nsnull;
1459 nsresult rv = GetUniqueID(&id);
1460 if (NS_FAILED(rv))
1461 return GetHRESULT(rv);
1463 *uniqueID = - reinterpret_cast<long>(id);
1464 return S_OK;
1466 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1467 return E_FAIL;
1470 STDMETHODIMP
1471 nsAccessibleWrap::get_windowHandle(HWND *aWindowHandle)
1473 __try {
1474 *aWindowHandle = 0;
1476 if (IsDefunct())
1477 return E_FAIL;
1479 void *handle = nsnull;
1480 nsresult rv = GetOwnerWindow(&handle);
1481 if (NS_FAILED(rv))
1482 return GetHRESULT(rv);
1484 *aWindowHandle = reinterpret_cast<HWND>(handle);
1485 return S_OK;
1487 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1488 return E_FAIL;
1491 STDMETHODIMP
1492 nsAccessibleWrap::get_indexInParent(long *aIndexInParent)
1494 __try {
1495 *aIndexInParent = -1;
1497 PRInt32 index = -1;
1498 nsresult rv = GetIndexInParent(&index);
1499 if (NS_FAILED(rv))
1500 return GetHRESULT(rv);
1502 if (index == -1)
1503 return S_FALSE;
1505 *aIndexInParent = index;
1506 return S_OK;
1508 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1509 return E_FAIL;
1512 STDMETHODIMP
1513 nsAccessibleWrap::get_locale(IA2Locale *aLocale)
1515 __try {
1516 // Language codes consist of a primary code and a possibly empty series of
1517 // subcodes: language-code = primary-code ( "-" subcode )*
1518 // Two-letter primary codes are reserved for [ISO639] language abbreviations.
1519 // Any two-letter subcode is understood to be a [ISO3166] country code.
1521 nsAutoString lang;
1522 nsresult rv = GetLanguage(lang);
1523 if (NS_FAILED(rv))
1524 return GetHRESULT(rv);
1526 // If primary code consists from two letters then expose it as language.
1527 PRInt32 offset = lang.FindChar('-', 0);
1528 if (offset == -1) {
1529 if (lang.Length() == 2) {
1530 aLocale->language = ::SysAllocString(lang.get());
1531 return S_OK;
1533 } else if (offset == 2) {
1534 aLocale->language = ::SysAllocStringLen(lang.get(), 2);
1536 // If the first subcode consists from two letters then expose it as
1537 // country.
1538 offset = lang.FindChar('-', 3);
1539 if (offset == -1) {
1540 if (lang.Length() == 5) {
1541 aLocale->country = ::SysAllocString(lang.get() + 3);
1542 return S_OK;
1544 } else if (offset == 5) {
1545 aLocale->country = ::SysAllocStringLen(lang.get() + 3, 2);
1549 // Expose as a string if primary code or subcode cannot point to language or
1550 // country abbreviations or if there are more than one subcode.
1551 aLocale->variant = ::SysAllocString(lang.get());
1552 return S_OK;
1554 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1555 return E_FAIL;
1558 STDMETHODIMP
1559 nsAccessibleWrap::get_attributes(BSTR *aAttributes)
1561 // The format is name:value;name:value; with \ for escaping these
1562 // characters ":;=,\".
1563 __try {
1564 *aAttributes = NULL;
1566 nsCOMPtr<nsIPersistentProperties> attributes;
1567 nsresult rv = GetAttributes(getter_AddRefs(attributes));
1568 if (NS_FAILED(rv))
1569 return GetHRESULT(rv);
1571 return ConvertToIA2Attributes(attributes, aAttributes);
1573 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1574 return E_FAIL;
1577 ////////////////////////////////////////////////////////////////////////////////
1578 // IDispatch
1580 STDMETHODIMP
1581 nsAccessibleWrap::GetTypeInfoCount(UINT *pctinfo)
1583 *pctinfo = 1;
1584 return S_OK;
1587 STDMETHODIMP
1588 nsAccessibleWrap::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
1590 *ppTInfo = NULL;
1592 if (iTInfo != 0)
1593 return DISP_E_BADINDEX;
1595 ITypeInfo * typeInfo = GetTI(lcid);
1596 if (!typeInfo)
1597 return E_FAIL;
1599 typeInfo->AddRef();
1600 *ppTInfo = typeInfo;
1602 return S_OK;
1605 STDMETHODIMP
1606 nsAccessibleWrap::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames,
1607 UINT cNames, LCID lcid, DISPID *rgDispId)
1609 ITypeInfo *typeInfo = GetTI(lcid);
1610 if (!typeInfo)
1611 return E_FAIL;
1613 HRESULT hr = DispGetIDsOfNames(typeInfo, rgszNames, cNames, rgDispId);
1614 return hr;
1617 STDMETHODIMP
1618 nsAccessibleWrap::Invoke(DISPID dispIdMember, REFIID riid,
1619 LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
1620 VARIANT *pVarResult, EXCEPINFO *pExcepInfo,
1621 UINT *puArgErr)
1623 ITypeInfo *typeInfo = GetTI(lcid);
1624 if (!typeInfo)
1625 return E_FAIL;
1627 return typeInfo->Invoke(static_cast<IAccessible*>(this), dispIdMember,
1628 wFlags, pDispParams, pVarResult, pExcepInfo,
1629 puArgErr);
1633 // nsIAccessible method
1634 NS_IMETHODIMP nsAccessibleWrap::GetNativeInterface(void **aOutAccessible)
1636 *aOutAccessible = static_cast<IAccessible*>(this);
1637 NS_ADDREF_THIS();
1638 return NS_OK;
1641 // nsAccessible
1643 nsresult
1644 nsAccessibleWrap::HandleAccEvent(nsAccEvent *aEvent)
1646 nsresult rv = nsAccessible::HandleAccEvent(aEvent);
1647 NS_ENSURE_SUCCESS(rv, rv);
1649 return FirePlatformEvent(aEvent);
1652 nsresult
1653 nsAccessibleWrap::FirePlatformEvent(nsAccEvent *aEvent)
1655 PRUint32 eventType = aEvent->GetEventType();
1657 NS_ENSURE_TRUE(eventType > 0 &&
1658 eventType < nsIAccessibleEvent::EVENT_LAST_ENTRY,
1659 NS_ERROR_FAILURE);
1661 PRUint32 winLastEntry = gWinEventMap[nsIAccessibleEvent::EVENT_LAST_ENTRY];
1662 NS_ASSERTION(winLastEntry == kEVENT_LAST_ENTRY,
1663 "MSAA event map skewed");
1665 PRUint32 winEvent = gWinEventMap[eventType];
1666 if (!winEvent)
1667 return NS_OK;
1669 // Means we're not active.
1670 NS_ENSURE_TRUE(mWeakShell, NS_ERROR_FAILURE);
1672 nsAccessible *accessible = aEvent->GetAccessible();
1673 if (!accessible)
1674 return NS_OK;
1676 if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED ||
1677 eventType == nsIAccessibleEvent::EVENT_FOCUS) {
1678 UpdateSystemCaret();
1681 PRInt32 childID = GetChildIDFor(accessible); // get the id for the accessible
1682 if (!childID)
1683 return NS_OK; // Can't fire an event without a child ID
1685 // See if we're in a scrollable area with its own window
1686 nsAccessible *newAccessible = nsnull;
1687 if (eventType == nsIAccessibleEvent::EVENT_HIDE) {
1688 // Don't use frame from current accessible when we're hiding that
1689 // accessible.
1690 newAccessible = accessible->GetCachedParent();
1691 } else {
1692 newAccessible = accessible;
1695 HWND hWnd = GetHWNDFor(newAccessible);
1696 NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE);
1698 // Gecko uses two windows for every scrollable area. One window contains
1699 // scrollbars and the child window contains only the client area.
1700 // Details of the 2 window system:
1701 // * Scrollbar window: caret drawing window & return value for WindowFromAccessibleObject()
1702 // * Client area window: text drawing window & MSAA event window
1704 // Fire MSAA event for client area window.
1705 NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
1707 // If the accessible children are changed then drop the IEnumVariant current
1708 // position of the accessible.
1709 if (eventType == nsIAccessibleEvent::EVENT_REORDER)
1710 UnattachIEnumVariant();
1712 return NS_OK;
1715 //------- Helper methods ---------
1717 PRInt32 nsAccessibleWrap::GetChildIDFor(nsIAccessible* aAccessible)
1719 // A child ID of the window is required, when we use NotifyWinEvent,
1720 // so that the 3rd party application can call back and get the IAccessible
1721 // the event occurred on.
1723 void *uniqueID = nsnull;
1724 nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
1725 if (!accessNode) {
1726 return 0;
1728 accessNode->GetUniqueID(&uniqueID);
1730 // Yes, this means we're only compatibible with 32 bit
1731 // MSAA is only available for 32 bit windows, so it's okay
1732 return - NS_PTR_TO_INT32(uniqueID);
1735 HWND
1736 nsAccessibleWrap::GetHWNDFor(nsAccessible *aAccessible)
1738 HWND hWnd = 0;
1739 if (!aAccessible)
1740 return hWnd;
1742 nsIFrame *frame = aAccessible->GetFrame();
1743 if (frame) {
1744 nsIWidget *window = frame->GetNearestWidget();
1745 PRBool isVisible;
1746 window->IsVisible(isVisible);
1747 if (isVisible) {
1748 // Short explanation:
1749 // If HWND for frame is inside a hidden window, fire the event on the
1750 // containing document's visible window.
1752 // Long explanation:
1753 // This is really just to fix combo boxes with JAWS. Window-Eyes already
1754 // worked with combo boxes because they use the value change event in
1755 // the closed combo box case. JAWS will only pay attention to the focus
1756 // events on the list items. The JAWS developers haven't fixed that, so
1757 // we'll use the focus events to make JAWS work. However, JAWS is
1758 // ignoring events on a hidden window. So, in order to fix the bug where
1759 // JAWS doesn't echo the current option as it changes in a closed
1760 // combo box, we need to use an ensure that we never fire an event with
1761 // an HWND for a hidden window.
1762 hWnd = (HWND)frame->GetNearestWidget()->GetNativeData(NS_NATIVE_WINDOW);
1766 if (!hWnd) {
1767 void* handle = nsnull;
1768 nsDocAccessible *accessibleDoc = aAccessible->GetDocAccessible();
1769 if (!accessibleDoc)
1770 return 0;
1772 accessibleDoc->GetWindowHandle(&handle);
1773 hWnd = (HWND)handle;
1776 return hWnd;
1779 HRESULT
1780 nsAccessibleWrap::ConvertToIA2Attributes(nsIPersistentProperties *aAttributes,
1781 BSTR *aIA2Attributes)
1783 *aIA2Attributes = NULL;
1785 // The format is name:value;name:value; with \ for escaping these
1786 // characters ":;=,\".
1788 if (!aAttributes)
1789 return S_FALSE;
1791 nsCOMPtr<nsISimpleEnumerator> propEnum;
1792 aAttributes->Enumerate(getter_AddRefs(propEnum));
1793 if (!propEnum)
1794 return E_FAIL;
1796 nsAutoString strAttrs;
1798 const char kCharsToEscape[] = ":;=,\\";
1800 PRBool hasMore = PR_FALSE;
1801 while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
1802 nsCOMPtr<nsISupports> propSupports;
1803 propEnum->GetNext(getter_AddRefs(propSupports));
1805 nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(propSupports));
1806 if (!propElem)
1807 return E_FAIL;
1809 nsCAutoString name;
1810 if (NS_FAILED(propElem->GetKey(name)))
1811 return E_FAIL;
1813 PRUint32 offset = 0;
1814 while ((offset = name.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
1815 name.Insert('\\', offset);
1816 offset += 2;
1819 nsAutoString value;
1820 if (NS_FAILED(propElem->GetValue(value)))
1821 return E_FAIL;
1823 offset = 0;
1824 while ((offset = value.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
1825 value.Insert('\\', offset);
1826 offset += 2;
1829 AppendUTF8toUTF16(name, strAttrs);
1830 strAttrs.Append(':');
1831 strAttrs.Append(value);
1832 strAttrs.Append(';');
1835 if (strAttrs.IsEmpty())
1836 return S_FALSE;
1838 *aIA2Attributes = ::SysAllocStringLen(strAttrs.get(), strAttrs.Length());
1839 return *aIA2Attributes ? S_OK : E_OUTOFMEMORY;
1842 IDispatch *nsAccessibleWrap::NativeAccessible(nsIAccessible *aXPAccessible)
1844 if (!aXPAccessible) {
1845 NS_WARNING("Not passing in an aXPAccessible");
1846 return NULL;
1849 nsCOMPtr<nsIAccessibleWin32Object> accObject(do_QueryInterface(aXPAccessible));
1850 if (accObject) {
1851 void* hwnd = nsnull;
1852 accObject->GetHwnd(&hwnd);
1853 if (hwnd) {
1854 IDispatch *retval = nsnull;
1855 AccessibleObjectFromWindow(reinterpret_cast<HWND>(hwnd),
1856 OBJID_WINDOW, IID_IAccessible, (void **) &retval);
1857 return retval;
1861 IAccessible *msaaAccessible;
1862 aXPAccessible->GetNativeInterface((void**)&msaaAccessible);
1864 return static_cast<IDispatch*>(msaaAccessible);
1867 void
1868 nsAccessibleWrap::UnattachIEnumVariant()
1870 if (mEnumVARIANTPosition > 0)
1871 mEnumVARIANTPosition = kIEnumVariantDisconnected;
1874 nsAccessible*
1875 nsAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
1877 if (IsDefunct())
1878 return nsnull;
1880 // if its us real easy - this seems to always be the case
1881 if (aVarChild.lVal == CHILDID_SELF)
1882 return this;
1884 if (nsAccUtils::MustPrune(this))
1885 return nsnull;
1887 return GetChildAt(aVarChild.lVal);
1890 void nsAccessibleWrap::UpdateSystemCaret()
1892 // Move the system caret so that Windows Tablet Edition and tradional ATs with
1893 // off-screen model can follow the caret
1894 ::DestroyCaret();
1896 nsRefPtr<nsRootAccessible> rootAccessible = GetRootAccessible();
1897 if (!rootAccessible) {
1898 return;
1901 nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
1902 if (!caretAccessible) {
1903 return;
1906 nsIWidget *widget;
1907 nsIntRect caretRect = caretAccessible->GetCaretRect(&widget);
1908 HWND caretWnd;
1909 if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) {
1910 return;
1913 // Create invisible bitmap for caret, otherwise its appearance interferes
1914 // with Gecko caret
1915 HBITMAP caretBitMap = CreateBitmap(1, caretRect.height, 1, 1, NULL);
1916 if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) { // Also destroys the last caret
1917 ::ShowCaret(caretWnd);
1918 RECT windowRect;
1919 ::GetWindowRect(caretWnd, &windowRect);
1920 ::SetCaretPos(caretRect.x - windowRect.left, caretRect.y - windowRect.top);
1921 ::DeleteObject(caretBitMap);
1925 ITypeInfo*
1926 nsAccessibleWrap::GetTI(LCID lcid)
1928 if (mTypeInfo)
1929 return mTypeInfo;
1931 ITypeLib *typeLib = NULL;
1932 HRESULT hr = LoadRegTypeLib(LIBID_Accessibility, 1, 0, lcid, &typeLib);
1933 if (FAILED(hr))
1934 return NULL;
1936 hr = typeLib->GetTypeInfoOfGuid(IID_IAccessible, &mTypeInfo);
1937 typeLib->Release();
1939 if (FAILED(hr))
1940 return NULL;
1942 return mTypeInfo;