Bug 453883, ensure true/false marcos are available, r=joshmoz, sr=jst
[wine-gecko.git] / accessible / src / msaa / nsAccessibleWrap.cpp
blob775e086e97806bca6e09da158c8b40dcacc53df1
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"
40 #include "nsAccessibilityAtoms.h"
42 #include "nsIAccessibleDocument.h"
43 #include "nsIAccessibleSelectable.h"
44 #include "nsIAccessibleEvent.h"
45 #include "nsIAccessibleWin32Object.h"
47 #include "Accessible2_i.c"
48 #include "AccessibleStates.h"
50 #include "nsIMutableArray.h"
51 #include "nsIDOMDocument.h"
52 #include "nsIFrame.h"
53 #include "nsIScrollableFrame.h"
54 #include "nsINameSpaceManager.h"
55 #include "nsINodeInfo.h"
56 #include "nsIPrefService.h"
57 #include "nsRootAccessible.h"
58 #include "nsIServiceManager.h"
59 #include "nsTextFormatter.h"
60 #include "nsIView.h"
61 #include "nsRoleMap.h"
62 #include "nsEventMap.h"
63 #include "nsArrayUtils.h"
65 /* For documentation of the accessibility architecture,
66 * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
69 //#define DEBUG_LEAKS
71 #ifdef DEBUG_LEAKS
72 static gAccessibles = 0;
73 #endif
75 EXTERN_C GUID CDECL CLSID_Accessible =
76 { 0x61044601, 0xa811, 0x4e2b, { 0xbb, 0xba, 0x17, 0xbf, 0xab, 0xd3, 0x29, 0xd7 } };
78 static const PRInt32 kIEnumVariantDisconnected = -1;
81 * Class nsAccessibleWrap
84 //-----------------------------------------------------
85 // construction
86 //-----------------------------------------------------
87 nsAccessibleWrap::nsAccessibleWrap(nsIDOMNode* aNode, nsIWeakReference *aShell):
88 nsAccessible(aNode, aShell), mEnumVARIANTPosition(0)
92 //-----------------------------------------------------
93 // destruction
94 //-----------------------------------------------------
95 nsAccessibleWrap::~nsAccessibleWrap()
99 NS_IMPL_ISUPPORTS_INHERITED0(nsAccessibleWrap, nsAccessible);
101 //-----------------------------------------------------
102 // IUnknown interface methods - see iunknown.h for documentation
103 //-----------------------------------------------------
105 // Microsoft COM QueryInterface
106 STDMETHODIMP nsAccessibleWrap::QueryInterface(REFIID iid, void** ppv)
108 __try {
109 *ppv = NULL;
111 if (IID_IUnknown == iid || IID_IDispatch == iid || IID_IAccessible == iid)
112 *ppv = static_cast<IAccessible*>(this);
113 else if (IID_IEnumVARIANT == iid && !gIsEnumVariantSupportDisabled) {
114 long numChildren;
115 get_accChildCount(&numChildren);
116 if (numChildren > 0) // Don't support this interface for leaf elements
117 *ppv = static_cast<IEnumVARIANT*>(this);
118 } else if (IID_IServiceProvider == iid)
119 *ppv = static_cast<IServiceProvider*>(this);
120 else if (IID_IAccessible2 == iid && !gIsIA2Disabled)
121 *ppv = static_cast<IAccessible2*>(this);
123 if (NULL == *ppv) {
124 HRESULT hr = CAccessibleComponent::QueryInterface(iid, ppv);
125 if (SUCCEEDED(hr))
126 return hr;
129 if (NULL == *ppv) {
130 HRESULT hr = CAccessibleHyperlink::QueryInterface(iid, ppv);
131 if (SUCCEEDED(hr))
132 return hr;
135 if (NULL == *ppv) {
136 HRESULT hr = CAccessibleValue::QueryInterface(iid, ppv);
137 if (SUCCEEDED(hr))
138 return hr;
141 if (NULL == *ppv)
142 return nsAccessNodeWrap::QueryInterface(iid, ppv);
144 (reinterpret_cast<IUnknown*>(*ppv))->AddRef();
145 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
146 return S_OK;
149 //-----------------------------------------------------
150 // IAccessible methods
151 //-----------------------------------------------------
154 STDMETHODIMP nsAccessibleWrap::AccessibleObjectFromWindow(HWND hwnd,
155 DWORD dwObjectID,
156 REFIID riid,
157 void **ppvObject)
159 // open the dll dynamically
160 if (!gmAccLib)
161 gmAccLib =::LoadLibraryW(L"OLEACC.DLL");
163 if (gmAccLib) {
164 if (!gmAccessibleObjectFromWindow)
165 gmAccessibleObjectFromWindow = (LPFNACCESSIBLEOBJECTFROMWINDOW)GetProcAddress(gmAccLib,"AccessibleObjectFromWindow");
167 if (gmAccessibleObjectFromWindow)
168 return gmAccessibleObjectFromWindow(hwnd, dwObjectID, riid, ppvObject);
171 return E_FAIL;
174 STDMETHODIMP nsAccessibleWrap::NotifyWinEvent(DWORD event,
175 HWND hwnd,
176 LONG idObjectType,
177 LONG idObject)
179 if (gmNotifyWinEvent)
180 return gmNotifyWinEvent(event, hwnd, idObjectType, idObject);
182 return E_FAIL;
185 STDMETHODIMP nsAccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
187 __try {
188 *ppdispParent = NULL;
189 if (!mWeakShell)
190 return E_FAIL; // We've been shut down
192 nsIFrame *frame = GetFrame();
193 HWND hwnd = 0;
194 if (frame) {
195 nsIView *view = frame->GetViewExternal();
196 if (view) {
197 // This code is essentially our implementation of WindowFromAccessibleObject,
198 // because MSAA iterates get_accParent() until it sees an object of ROLE_WINDOW
199 // to know where the window for a given accessible is. We must expose the native
200 // window accessible that MSAA creates for us. This must be done for the document
201 // object as well as any layout that creates its own window (e.g. via overflow: scroll)
202 nsIWidget *widget = view->GetWidget();
203 if (widget) {
204 hwnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW);
205 NS_ASSERTION(hwnd, "No window handle for window");
206 nsIView *rootView;
207 view->GetViewManager()->GetRootView(rootView);
208 if (rootView == view) {
209 // If the current object has a widget but was created by an
210 // outer object with its own outer window, then
211 // we want the native accessible for that outer window
212 hwnd = ::GetParent(hwnd);
213 NS_ASSERTION(hwnd, "No window handle for window");
216 else {
217 // If a frame is a scrollable frame, then it has one window for the client area,
218 // not an extra parent window for just the scrollbars
219 nsIScrollableFrame *scrollFrame = nsnull;
220 CallQueryInterface(frame, &scrollFrame);
221 if (scrollFrame) {
222 hwnd = (HWND)scrollFrame->GetScrolledFrame()->GetWindow()->GetNativeData(NS_NATIVE_WINDOW);
223 NS_ASSERTION(hwnd, "No window handle for window");
228 if (hwnd && SUCCEEDED(AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible,
229 (void**)ppdispParent))) {
230 return S_OK;
234 nsCOMPtr<nsIAccessible> xpParentAccessible(GetParent());
235 NS_ASSERTION(xpParentAccessible, "No parent accessible where we're not direct child of window");
236 if (!xpParentAccessible) {
237 return E_UNEXPECTED;
239 *ppdispParent = NativeAccessible(xpParentAccessible);
241 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
242 return S_OK;
245 STDMETHODIMP nsAccessibleWrap::get_accChildCount( long __RPC_FAR *pcountChildren)
247 __try {
248 *pcountChildren = 0;
249 if (MustPrune(this)) {
250 return NS_OK;
253 PRInt32 numChildren;
254 GetChildCount(&numChildren);
255 *pcountChildren = numChildren;
256 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
258 return S_OK;
261 STDMETHODIMP nsAccessibleWrap::get_accChild(
262 /* [in] */ VARIANT varChild,
263 /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild)
265 __try {
266 *ppdispChild = NULL;
267 if (!mWeakShell || varChild.vt != VT_I4)
268 return E_FAIL;
270 if (varChild.lVal == CHILDID_SELF) {
271 *ppdispChild = static_cast<IDispatch*>(this);
272 AddRef();
273 return S_OK;
276 nsCOMPtr<nsIAccessible> childAccessible;
277 if (!MustPrune(this)) {
278 GetChildAt(varChild.lVal - 1, getter_AddRefs(childAccessible));
279 if (childAccessible) {
280 *ppdispChild = NativeAccessible(childAccessible);
283 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
285 return (*ppdispChild)? S_OK: E_FAIL;
288 STDMETHODIMP nsAccessibleWrap::get_accName(
289 /* [optional][in] */ VARIANT varChild,
290 /* [retval][out] */ BSTR __RPC_FAR *pszName)
292 __try {
293 *pszName = NULL;
294 nsCOMPtr<nsIAccessible> xpAccessible;
295 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
296 if (!xpAccessible)
297 return E_FAIL;
298 nsAutoString name;
299 nsresult rv = xpAccessible->GetName(name);
300 if (NS_FAILED(rv))
301 return GetHRESULT(rv);
303 if (name.IsVoid()) {
304 // Valid return value for the name:
305 // The name was not provided, e.g. no alt attribute for an image.
306 // A screen reader may choose to invent its own accessible name, e.g. from
307 // an image src attribute.
308 // See nsHTMLImageAccessible::GetName()
309 return S_OK;
312 *pszName = ::SysAllocStringLen(name.get(), name.Length());
313 if (!*pszName)
314 return E_OUTOFMEMORY;
316 #ifdef DEBUG_A11Y
317 NS_ASSERTION(mIsInitialized, "Access node was not initialized");
318 #endif
319 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
321 return S_OK;
325 STDMETHODIMP nsAccessibleWrap::get_accValue(
326 /* [optional][in] */ VARIANT varChild,
327 /* [retval][out] */ BSTR __RPC_FAR *pszValue)
329 __try {
330 *pszValue = NULL;
331 nsCOMPtr<nsIAccessible> xpAccessible;
332 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
333 if (xpAccessible) {
334 nsAutoString value;
335 if (NS_FAILED(xpAccessible->GetValue(value)))
336 return E_FAIL;
338 // see bug 438784: Need to expose URL on doc's value attribute.
339 // For this, reverting part of fix for bug 425693 to make this MSAA method
340 // behave IAccessible2-style.
341 if (value.IsEmpty())
342 return S_FALSE;
344 *pszValue = ::SysAllocStringLen(value.get(), value.Length());
345 if (!*pszValue)
346 return E_OUTOFMEMORY;
348 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
349 return S_OK;
352 STDMETHODIMP
353 nsAccessibleWrap::get_accDescription(VARIANT varChild,
354 BSTR __RPC_FAR *pszDescription)
356 __try {
357 *pszDescription = NULL;
359 nsCOMPtr<nsIAccessible> xpAccessible;
360 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
361 if (!xpAccessible)
362 return E_FAIL;
364 // For items that are a choice in a list of choices, use MSAA description
365 // field to shoehorn positional info, it's becoming a defacto standard use for
366 // the field.
368 nsAutoString description;
370 // Try to get group attributes to make a positional description string. We
371 // can't use nsIAccessible::groupPosition because the method isn't supposed
372 // to work with elements exposing 'level' attribute only (like HTML headings).
373 nsCOMPtr<nsIPersistentProperties> attributes;
374 nsresult rv = xpAccessible->GetAttributes(getter_AddRefs(attributes));
375 NS_ENSURE_SUCCESS(rv, rv);
376 if (!attributes)
377 return NS_ERROR_FAILURE;
379 PRInt32 groupLevel = 0;
380 PRInt32 itemsInGroup = 0;
381 PRInt32 positionInGroup = 0;
382 nsAccUtils::GetAccGroupAttrs(attributes, &groupLevel, &positionInGroup,
383 &itemsInGroup);
385 if (positionInGroup > 0) {
386 if (groupLevel > 0) {
387 // XXX: How do we calculate the number of children? Now we append
388 // " with [numChildren]c" for tree item. In the future we may need to
389 // use the ARIA owns property to calculate that if it's present.
390 PRInt32 numChildren = 0;
392 PRUint32 currentRole = 0;
393 rv = xpAccessible->GetFinalRole(&currentRole);
394 if (NS_SUCCEEDED(rv) &&
395 currentRole == nsIAccessibleRole::ROLE_OUTLINEITEM) {
396 nsCOMPtr<nsIAccessible> child;
397 xpAccessible->GetFirstChild(getter_AddRefs(child));
398 while (child) {
399 child->GetFinalRole(&currentRole);
400 if (currentRole == nsIAccessibleRole::ROLE_GROUPING) {
401 nsCOMPtr<nsIAccessible> groupChild;
402 child->GetFirstChild(getter_AddRefs(groupChild));
403 while (groupChild) {
404 groupChild->GetFinalRole(&currentRole);
405 numChildren +=
406 (currentRole == nsIAccessibleRole::ROLE_OUTLINEITEM);
407 nsCOMPtr<nsIAccessible> nextGroupChild;
408 groupChild->GetNextSibling(getter_AddRefs(nextGroupChild));
409 groupChild.swap(nextGroupChild);
411 break;
413 nsCOMPtr<nsIAccessible> nextChild;
414 child->GetNextSibling(getter_AddRefs(nextChild));
415 child.swap(nextChild);
419 if (numChildren) {
420 nsTextFormatter::ssprintf(description,
421 NS_LITERAL_STRING("L%d, %d of %d with %d").get(),
422 groupLevel, positionInGroup, itemsInGroup,
423 numChildren);
424 } else {
425 nsTextFormatter::ssprintf(description,
426 NS_LITERAL_STRING("L%d, %d of %d").get(),
427 groupLevel, positionInGroup, itemsInGroup);
429 } else { // Position has no level
430 nsTextFormatter::ssprintf(description,
431 NS_LITERAL_STRING("%d of %d").get(),
432 positionInGroup, itemsInGroup);
434 } else if (groupLevel > 0) {
435 nsTextFormatter::ssprintf(description, NS_LITERAL_STRING("L%d").get(),
436 groupLevel);
439 if (!description.IsEmpty()) {
440 *pszDescription = ::SysAllocStringLen(description.get(),
441 description.Length());
442 return *pszDescription ? S_OK : E_OUTOFMEMORY;
445 xpAccessible->GetDescription(description);
446 if (!description.IsEmpty()) {
447 // Signal to screen readers that this description is speakable
448 // and is not a formatted positional information description
449 // Don't localize the "Description: " part of this string, it will be
450 // parsed out by assistive technologies.
451 description = NS_LITERAL_STRING("Description: ") + description;
454 *pszDescription = ::SysAllocStringLen(description.get(),
455 description.Length());
456 return *pszDescription ? S_OK : E_OUTOFMEMORY;
458 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
459 return E_FAIL;
462 STDMETHODIMP nsAccessibleWrap::get_accRole(
463 /* [optional][in] */ VARIANT varChild,
464 /* [retval][out] */ VARIANT __RPC_FAR *pvarRole)
466 __try {
467 VariantInit(pvarRole);
469 nsCOMPtr<nsIAccessible> xpAccessible;
470 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
472 if (!xpAccessible)
473 return E_FAIL;
475 #ifdef DEBUG_A11Y
476 NS_ASSERTION(nsAccessible::IsTextInterfaceSupportCorrect(xpAccessible), "Does not support nsIAccessibleText when it should");
477 #endif
479 PRUint32 xpRole = 0, msaaRole = 0;
480 if (NS_FAILED(xpAccessible->GetFinalRole(&xpRole)))
481 return E_FAIL;
483 msaaRole = gWindowsRoleMap[xpRole].msaaRole;
484 NS_ASSERTION(gWindowsRoleMap[nsIAccessibleRole::ROLE_LAST_ENTRY].msaaRole == ROLE_WINDOWS_LAST_ENTRY,
485 "MSAA role map skewed");
487 // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call the MSAA role
488 // a ROLE_OUTLINEITEM for consistency and compatibility.
489 // We need this because ARIA has a role of "row" for both grid and treegrid
490 if (xpRole == nsIAccessibleRole::ROLE_ROW) {
491 nsCOMPtr<nsIAccessible> parent = GetParent();
492 if (parent && Role(parent) == nsIAccessibleRole::ROLE_TREE_TABLE) {
493 msaaRole = ROLE_SYSTEM_OUTLINEITEM;
497 // -- Try enumerated role
498 if (msaaRole != USE_ROLE_STRING) {
499 pvarRole->vt = VT_I4;
500 pvarRole->lVal = msaaRole; // Normal enumerated role
501 return S_OK;
504 // -- Try BSTR role
505 // Could not map to known enumerated MSAA role like ROLE_BUTTON
506 // Use BSTR role to expose role attribute or tag name + namespace
507 nsCOMPtr<nsIDOMNode> domNode;
508 nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(xpAccessible));
509 if (!accessNode)
510 return E_FAIL;
512 accessNode->GetDOMNode(getter_AddRefs(domNode));
513 nsIContent *content = GetRoleContent(domNode);
514 if (!content)
515 return E_FAIL;
517 if (content->IsNodeOfType(nsINode::eELEMENT)) {
518 nsAutoString roleString;
519 if (msaaRole != ROLE_SYSTEM_CLIENT &&
520 !content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::role, roleString)) {
521 nsINodeInfo *nodeInfo = content->NodeInfo();
522 nodeInfo->GetName(roleString);
523 nsAutoString nameSpaceURI;
524 nodeInfo->GetNamespaceURI(nameSpaceURI);
525 if (!nameSpaceURI.IsEmpty()) {
526 // Only append name space if different from that of current document
527 roleString += NS_LITERAL_STRING(", ") + nameSpaceURI;
530 if (!roleString.IsEmpty()) {
531 pvarRole->vt = VT_BSTR;
532 pvarRole->bstrVal = ::SysAllocString(roleString.get());
533 return S_OK;
536 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
537 return E_FAIL;
540 STDMETHODIMP nsAccessibleWrap::get_accState(
541 /* [optional][in] */ VARIANT varChild,
542 /* [retval][out] */ VARIANT __RPC_FAR *pvarState)
544 __try {
545 VariantInit(pvarState);
546 pvarState->vt = VT_I4;
547 pvarState->lVal = 0;
549 nsCOMPtr<nsIAccessible> xpAccessible;
550 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
551 if (!xpAccessible)
552 return E_FAIL;
554 PRUint32 state = 0;
555 if (NS_FAILED(xpAccessible->GetFinalState(&state, nsnull)))
556 return E_FAIL;
558 pvarState->lVal = state;
559 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
560 return S_OK;
564 STDMETHODIMP nsAccessibleWrap::get_accHelp(
565 /* [optional][in] */ VARIANT varChild,
566 /* [retval][out] */ BSTR __RPC_FAR *pszHelp)
568 *pszHelp = NULL;
569 return S_FALSE;
572 STDMETHODIMP nsAccessibleWrap::get_accHelpTopic(
573 /* [out] */ BSTR __RPC_FAR *pszHelpFile,
574 /* [optional][in] */ VARIANT varChild,
575 /* [retval][out] */ long __RPC_FAR *pidTopic)
577 *pszHelpFile = NULL;
578 *pidTopic = 0;
579 return E_NOTIMPL;
582 STDMETHODIMP nsAccessibleWrap::get_accKeyboardShortcut(
583 /* [optional][in] */ VARIANT varChild,
584 /* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut)
586 __try {
587 *pszKeyboardShortcut = NULL;
588 nsCOMPtr<nsIAccessible> xpAccessible;
589 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
590 if (xpAccessible) {
591 nsAutoString shortcut;
592 nsresult rv = xpAccessible->GetKeyboardShortcut(shortcut);
593 if (NS_FAILED(rv))
594 return E_FAIL;
596 *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(),
597 shortcut.Length());
598 return *pszKeyboardShortcut ? S_OK : E_OUTOFMEMORY;
600 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
601 return E_FAIL;
604 STDMETHODIMP nsAccessibleWrap::get_accFocus(
605 /* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
607 // VT_EMPTY: None. This object does not have the keyboard focus itself
608 // and does not contain a child that has the keyboard focus.
609 // VT_I4: lVal is CHILDID_SELF. The object itself has the keyboard focus.
610 // VT_I4: lVal contains the child ID of the child element with the keyboard focus.
611 // VT_DISPATCH: pdispVal member is the address of the IDispatch interface
612 // for the child object with the keyboard focus.
613 __try {
614 if (!mDOMNode) {
615 return E_FAIL; // This node is shut down
618 VariantInit(pvarChild);
620 // Return the current IAccessible child that has focus
621 nsCOMPtr<nsIAccessible> focusedAccessible;
622 GetFocusedChild(getter_AddRefs(focusedAccessible));
623 if (focusedAccessible == this) {
624 pvarChild->vt = VT_I4;
625 pvarChild->lVal = CHILDID_SELF;
627 else if (focusedAccessible) {
628 pvarChild->vt = VT_DISPATCH;
629 pvarChild->pdispVal = NativeAccessible(focusedAccessible);
631 else {
632 pvarChild->vt = VT_EMPTY; // No focus or focus is not a child
635 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
636 return S_OK;
639 // This helper class implements IEnumVARIANT for a nsIArray containing nsIAccessible objects.
641 class AccessibleEnumerator : public IEnumVARIANT
643 public:
644 AccessibleEnumerator(nsIArray* aArray) : mArray(aArray), mCurIndex(0) { }
645 AccessibleEnumerator(const AccessibleEnumerator& toCopy) :
646 mArray(toCopy.mArray), mCurIndex(toCopy.mCurIndex) { }
647 ~AccessibleEnumerator() { }
649 // IUnknown
650 STDMETHODIMP QueryInterface(REFIID iid, void ** ppvObject);
651 STDMETHODIMP_(ULONG) AddRef(void);
652 STDMETHODIMP_(ULONG) Release(void);
654 // IEnumVARIANT
655 STDMETHODIMP Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched);
656 STDMETHODIMP Skip(unsigned long celt);
657 STDMETHODIMP Reset()
659 mCurIndex = 0;
660 return S_OK;
662 STDMETHODIMP Clone(IEnumVARIANT FAR* FAR* ppenum);
664 private:
665 nsCOMPtr<nsIArray> mArray;
666 PRUint32 mCurIndex;
667 nsAutoRefCnt mRefCnt;
670 HRESULT
671 AccessibleEnumerator::QueryInterface(REFIID iid, void ** ppvObject)
673 __try {
674 if (iid == IID_IEnumVARIANT) {
675 *ppvObject = static_cast<IEnumVARIANT*>(this);
676 AddRef();
677 return S_OK;
679 if (iid == IID_IUnknown) {
680 *ppvObject = static_cast<IUnknown*>(this);
681 AddRef();
682 return S_OK;
685 *ppvObject = NULL;
686 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
687 return E_NOINTERFACE;
690 STDMETHODIMP_(ULONG)
691 AccessibleEnumerator::AddRef(void)
693 return ++mRefCnt;
696 STDMETHODIMP_(ULONG)
697 AccessibleEnumerator::Release(void)
699 ULONG r = --mRefCnt;
700 if (r == 0)
701 delete this;
702 return r;
705 STDMETHODIMP
706 AccessibleEnumerator::Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched)
708 __try {
709 PRUint32 length = 0;
710 mArray->GetLength(&length);
712 HRESULT hr = S_OK;
714 // Can't get more elements than there are...
715 if (celt > length - mCurIndex) {
716 hr = S_FALSE;
717 celt = length - mCurIndex;
720 for (PRUint32 i = 0; i < celt; ++i, ++mCurIndex) {
721 // Copy the elements of the array into rgvar
722 nsCOMPtr<nsIAccessible> accel(do_QueryElementAt(mArray, mCurIndex));
723 NS_ASSERTION(accel, "Invalid pointer in mArray");
725 if (accel) {
726 rgvar[i].vt = VT_DISPATCH;
727 rgvar[i].pdispVal = nsAccessibleWrap::NativeAccessible(accel);
731 if (pceltFetched)
732 *pceltFetched = celt;
734 return hr;
735 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
737 return S_OK;
740 STDMETHODIMP
741 AccessibleEnumerator::Clone(IEnumVARIANT FAR* FAR* ppenum)
743 __try {
744 *ppenum = new AccessibleEnumerator(*this);
745 if (!*ppenum)
746 return E_OUTOFMEMORY;
747 NS_ADDREF(*ppenum);
748 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
749 return S_OK;
752 STDMETHODIMP
753 AccessibleEnumerator::Skip(unsigned long celt)
755 __try {
756 PRUint32 length = 0;
757 mArray->GetLength(&length);
758 // Check if we can skip the requested number of elements
759 if (celt > length - mCurIndex) {
760 mCurIndex = length;
761 return S_FALSE;
763 mCurIndex += celt;
764 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
765 return S_OK;
769 * This method is called when a client wants to know which children of a node
770 * are selected. Note that this method can only find selected children for
771 * nsIAccessible object which implement nsIAccessibleSelectable.
773 * The VARIANT return value arguement is expected to either contain a single IAccessible
774 * or an IEnumVARIANT of IAccessibles. We return the IEnumVARIANT regardless of the number
775 * of children selected, unless there are none selected in which case we return an empty
776 * VARIANT.
778 * We get the selected options from the select's accessible object and wrap
779 * those in an AccessibleEnumerator which we then put in the return VARIANT.
781 * returns a VT_EMPTY VARIANT if:
782 * - there are no selected children for this object
783 * - the object is not the type that can have children selected
785 STDMETHODIMP nsAccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren)
787 __try {
788 VariantInit(pvarChildren);
789 pvarChildren->vt = VT_EMPTY;
791 nsCOMPtr<nsIAccessibleSelectable>
792 select(do_QueryInterface(static_cast<nsIAccessible*>(this)));
794 if (select) { // do we have an nsIAccessibleSelectable?
795 // we have an accessible that can have children selected
796 nsCOMPtr<nsIArray> selectedOptions;
797 // gets the selected options as nsIAccessibles.
798 select->GetSelectedChildren(getter_AddRefs(selectedOptions));
799 if (selectedOptions) { // false if the select has no children or none are selected
800 // 1) Create and initialize the enumeration
801 nsRefPtr<AccessibleEnumerator> pEnum = new AccessibleEnumerator(selectedOptions);
803 // 2) Put the enumerator in the VARIANT
804 if (!pEnum)
805 return E_OUTOFMEMORY;
806 pvarChildren->vt = VT_UNKNOWN; // this must be VT_UNKNOWN for an IEnumVARIANT
807 NS_ADDREF(pvarChildren->punkVal = pEnum);
810 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
811 return S_OK;
814 STDMETHODIMP nsAccessibleWrap::get_accDefaultAction(
815 /* [optional][in] */ VARIANT varChild,
816 /* [retval][out] */ BSTR __RPC_FAR *pszDefaultAction)
818 __try {
819 *pszDefaultAction = NULL;
820 nsCOMPtr<nsIAccessible> xpAccessible;
821 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
822 if (xpAccessible) {
823 nsAutoString defaultAction;
824 if (NS_FAILED(xpAccessible->GetActionName(0, defaultAction)))
825 return E_FAIL;
827 *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(),
828 defaultAction.Length());
829 return *pszDefaultAction ? S_OK : E_OUTOFMEMORY;
832 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
833 return E_FAIL;
836 STDMETHODIMP nsAccessibleWrap::accSelect(
837 /* [in] */ long flagsSelect,
838 /* [optional][in] */ VARIANT varChild)
840 __try {
841 // currently only handle focus and selection
842 nsCOMPtr<nsIAccessible> xpAccessible;
843 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
844 NS_ENSURE_TRUE(xpAccessible, E_FAIL);
846 if (flagsSelect & (SELFLAG_TAKEFOCUS|SELFLAG_TAKESELECTION|SELFLAG_REMOVESELECTION))
848 if (flagsSelect & SELFLAG_TAKEFOCUS)
849 xpAccessible->TakeFocus();
851 if (flagsSelect & SELFLAG_TAKESELECTION)
852 xpAccessible->TakeSelection();
854 if (flagsSelect & SELFLAG_ADDSELECTION)
855 xpAccessible->SetSelected(PR_TRUE);
857 if (flagsSelect & SELFLAG_REMOVESELECTION)
858 xpAccessible->SetSelected(PR_FALSE);
860 if (flagsSelect & SELFLAG_EXTENDSELECTION)
861 xpAccessible->ExtendSelection();
863 return S_OK;
866 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
867 return E_FAIL;
870 STDMETHODIMP nsAccessibleWrap::accLocation(
871 /* [out] */ long __RPC_FAR *pxLeft,
872 /* [out] */ long __RPC_FAR *pyTop,
873 /* [out] */ long __RPC_FAR *pcxWidth,
874 /* [out] */ long __RPC_FAR *pcyHeight,
875 /* [optional][in] */ VARIANT varChild)
877 __try {
878 nsCOMPtr<nsIAccessible> xpAccessible;
879 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
881 if (xpAccessible) {
882 PRInt32 x, y, width, height;
883 if (NS_FAILED(xpAccessible->GetBounds(&x, &y, &width, &height)))
884 return E_FAIL;
886 *pxLeft = x;
887 *pyTop = y;
888 *pcxWidth = width;
889 *pcyHeight = height;
890 return S_OK;
892 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
894 return E_FAIL;
897 STDMETHODIMP nsAccessibleWrap::accNavigate(
898 /* [in] */ long navDir,
899 /* [optional][in] */ VARIANT varStart,
900 /* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt)
902 __try {
903 nsCOMPtr<nsIAccessible> xpAccessibleStart, xpAccessibleResult;
904 GetXPAccessibleFor(varStart, getter_AddRefs(xpAccessibleStart));
905 if (!xpAccessibleStart)
906 return E_FAIL;
908 VariantInit(pvarEndUpAt);
909 PRUint32 xpRelation = 0;
911 switch(navDir) {
912 case NAVDIR_DOWN:
913 xpAccessibleStart->GetAccessibleBelow(getter_AddRefs(xpAccessibleResult));
914 break;
915 case NAVDIR_FIRSTCHILD:
916 if (!MustPrune(xpAccessibleStart)) {
917 xpAccessibleStart->GetFirstChild(getter_AddRefs(xpAccessibleResult));
919 break;
920 case NAVDIR_LASTCHILD:
921 if (!MustPrune(xpAccessibleStart)) {
922 xpAccessibleStart->GetLastChild(getter_AddRefs(xpAccessibleResult));
924 break;
925 case NAVDIR_LEFT:
926 xpAccessibleStart->GetAccessibleToLeft(getter_AddRefs(xpAccessibleResult));
927 break;
928 case NAVDIR_NEXT:
929 xpAccessibleStart->GetNextSibling(getter_AddRefs(xpAccessibleResult));
930 break;
931 case NAVDIR_PREVIOUS:
932 xpAccessibleStart->GetPreviousSibling(getter_AddRefs(xpAccessibleResult));
933 break;
934 case NAVDIR_RIGHT:
935 xpAccessibleStart->GetAccessibleToRight(getter_AddRefs(xpAccessibleResult));
936 break;
937 case NAVDIR_UP:
938 xpAccessibleStart->GetAccessibleAbove(getter_AddRefs(xpAccessibleResult));
939 break;
941 // MSAA relationship extensions to accNavigate
942 case NAVRELATION_CONTROLLED_BY:
943 xpRelation = nsIAccessibleRelation::RELATION_CONTROLLED_BY;
944 break;
945 case NAVRELATION_CONTROLLER_FOR:
946 xpRelation = nsIAccessibleRelation::RELATION_CONTROLLER_FOR;
947 break;
948 case NAVRELATION_LABEL_FOR:
949 xpRelation = nsIAccessibleRelation::RELATION_LABEL_FOR;
950 break;
951 case NAVRELATION_LABELLED_BY:
952 xpRelation = nsIAccessibleRelation::RELATION_LABELLED_BY;
953 break;
954 case NAVRELATION_MEMBER_OF:
955 xpRelation = nsIAccessibleRelation::RELATION_MEMBER_OF;
956 break;
957 case NAVRELATION_NODE_CHILD_OF:
958 xpRelation = nsIAccessibleRelation::RELATION_NODE_CHILD_OF;
959 break;
960 case NAVRELATION_FLOWS_TO:
961 xpRelation = nsIAccessibleRelation::RELATION_FLOWS_TO;
962 break;
963 case NAVRELATION_FLOWS_FROM:
964 xpRelation = nsIAccessibleRelation::RELATION_FLOWS_FROM;
965 break;
966 case NAVRELATION_SUBWINDOW_OF:
967 xpRelation = nsIAccessibleRelation::RELATION_SUBWINDOW_OF;
968 break;
969 case NAVRELATION_EMBEDS:
970 xpRelation = nsIAccessibleRelation::RELATION_EMBEDS;
971 break;
972 case NAVRELATION_EMBEDDED_BY:
973 xpRelation = nsIAccessibleRelation::RELATION_EMBEDDED_BY;
974 break;
975 case NAVRELATION_POPUP_FOR:
976 xpRelation = nsIAccessibleRelation::RELATION_POPUP_FOR;
977 break;
978 case NAVRELATION_PARENT_WINDOW_OF:
979 xpRelation = nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF;
980 break;
981 case NAVRELATION_DEFAULT_BUTTON:
982 xpRelation = nsIAccessibleRelation::RELATION_DEFAULT_BUTTON;
983 break;
984 case NAVRELATION_DESCRIBED_BY:
985 xpRelation = nsIAccessibleRelation::RELATION_DESCRIBED_BY;
986 break;
987 case NAVRELATION_DESCRIPTION_FOR:
988 xpRelation = nsIAccessibleRelation::RELATION_DESCRIPTION_FOR;
989 break;
992 pvarEndUpAt->vt = VT_EMPTY;
994 if (xpRelation) {
995 nsresult rv = GetAccessibleRelated(xpRelation,
996 getter_AddRefs(xpAccessibleResult));
997 if (rv == NS_ERROR_NOT_IMPLEMENTED) {
998 return E_NOTIMPL;
1002 if (xpAccessibleResult) {
1003 pvarEndUpAt->pdispVal = NativeAccessible(xpAccessibleResult);
1004 pvarEndUpAt->vt = VT_DISPATCH;
1005 return NS_OK;
1007 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1008 return E_FAIL;
1011 STDMETHODIMP nsAccessibleWrap::accHitTest(
1012 /* [in] */ long xLeft,
1013 /* [in] */ long yTop,
1014 /* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
1016 __try {
1017 VariantInit(pvarChild);
1019 // convert to window coords
1020 nsCOMPtr<nsIAccessible> xpAccessible;
1022 xLeft = xLeft;
1023 yTop = yTop;
1025 if (MustPrune(this)) {
1026 xpAccessible = this;
1028 else {
1029 GetChildAtPoint(xLeft, yTop, getter_AddRefs(xpAccessible));
1032 // if we got a child
1033 if (xpAccessible) {
1034 // if the child is us
1035 if (xpAccessible == static_cast<nsIAccessible*>(this)) {
1036 pvarChild->vt = VT_I4;
1037 pvarChild->lVal = CHILDID_SELF;
1038 } else { // its not create an Accessible for it.
1039 pvarChild->vt = VT_DISPATCH;
1040 pvarChild->pdispVal = NativeAccessible(xpAccessible);
1041 nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(xpAccessible));
1042 NS_ASSERTION(accessNode, "Unable to QI to nsIAccessNode");
1043 nsCOMPtr<nsIDOMNode> domNode;
1044 accessNode->GetDOMNode(getter_AddRefs(domNode));
1045 if (!domNode) {
1046 // Has already been shut down
1047 pvarChild->vt = VT_EMPTY;
1048 return E_FAIL;
1051 } else {
1052 // no child at that point
1053 pvarChild->vt = VT_EMPTY;
1054 return S_FALSE;
1056 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1058 return S_OK;
1061 STDMETHODIMP nsAccessibleWrap::accDoDefaultAction(
1062 /* [optional][in] */ VARIANT varChild)
1064 __try {
1065 nsCOMPtr<nsIAccessible> xpAccessible;
1066 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
1068 if (!xpAccessible || FAILED(xpAccessible->DoAction(0))) {
1069 return E_FAIL;
1071 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1072 return S_OK;
1075 STDMETHODIMP nsAccessibleWrap::put_accName(
1076 /* [optional][in] */ VARIANT varChild,
1077 /* [in] */ BSTR szName)
1079 return E_NOTIMPL;
1082 STDMETHODIMP nsAccessibleWrap::put_accValue(
1083 /* [optional][in] */ VARIANT varChild,
1084 /* [in] */ BSTR szValue)
1086 return E_NOTIMPL;
1089 #include "mshtml.h"
1091 ////////////////////////////////////////////////////////////////////////////////
1092 // nsAccessibleWrap. IEnumVariant
1094 STDMETHODIMP
1095 nsAccessibleWrap::Next(ULONG aNumElementsRequested, VARIANT FAR* aPVar,
1096 ULONG FAR* aNumElementsFetched)
1098 // Children already cached via QI to IEnumVARIANT
1099 __try {
1100 *aNumElementsFetched = 0;
1102 if (aNumElementsRequested <= 0 || !aPVar)
1103 return E_INVALIDARG;
1105 if (mEnumVARIANTPosition == kIEnumVariantDisconnected)
1106 return CO_E_OBJNOTCONNECTED;
1108 nsCOMPtr<nsIAccessible> traversedAcc;
1109 nsresult rv = GetChildAt(mEnumVARIANTPosition, getter_AddRefs(traversedAcc));
1110 if (!traversedAcc)
1111 return S_FALSE;
1113 for (PRUint32 i = 0; i < aNumElementsRequested; i++) {
1114 VariantInit(&aPVar[i]);
1116 aPVar[i].pdispVal = NativeAccessible(traversedAcc);
1117 aPVar[i].vt = VT_DISPATCH;
1118 (*aNumElementsFetched)++;
1120 nsCOMPtr<nsIAccessible> nextAcc;
1121 traversedAcc->GetNextSibling(getter_AddRefs(nextAcc));
1122 if (!nextAcc)
1123 break;
1125 traversedAcc = nextAcc;
1128 mEnumVARIANTPosition += *aNumElementsFetched;
1129 return (*aNumElementsFetched) < aNumElementsRequested ? S_FALSE : S_OK;
1131 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1132 return E_FAIL;
1135 STDMETHODIMP
1136 nsAccessibleWrap::Skip(ULONG aNumElements)
1138 __try {
1139 if (mEnumVARIANTPosition == kIEnumVariantDisconnected)
1140 return CO_E_OBJNOTCONNECTED;
1142 mEnumVARIANTPosition += aNumElements;
1144 PRInt32 numChildren;
1145 GetChildCount(&numChildren);
1147 if (mEnumVARIANTPosition > numChildren)
1149 mEnumVARIANTPosition = numChildren;
1150 return S_FALSE;
1152 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1153 return NOERROR;
1156 STDMETHODIMP
1157 nsAccessibleWrap::Reset(void)
1159 mEnumVARIANTPosition = 0;
1160 return NOERROR;
1163 STDMETHODIMP
1164 nsAccessibleWrap::Clone(IEnumVARIANT FAR* FAR* ppenum)
1166 __try {
1167 *ppenum = nsnull;
1169 nsCOMPtr<nsIArray> childArray;
1170 nsresult rv = GetChildren(getter_AddRefs(childArray));
1172 *ppenum = new AccessibleEnumerator(childArray);
1173 if (!*ppenum)
1174 return E_OUTOFMEMORY;
1175 NS_ADDREF(*ppenum);
1177 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1178 return NOERROR;
1181 ////////////////////////////////////////////////////////////////////////////////
1182 // nsAccessibleWrap. IAccessible2
1184 STDMETHODIMP
1185 nsAccessibleWrap::get_nRelations(long *aNRelations)
1187 __try {
1188 PRUint32 count = 0;
1189 nsresult rv = GetRelationsCount(&count);
1190 *aNRelations = count;
1192 return GetHRESULT(rv);
1194 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1195 return E_FAIL;
1198 STDMETHODIMP
1199 nsAccessibleWrap::get_relation(long aRelationIndex,
1200 IAccessibleRelation **aRelation)
1202 __try {
1203 *aRelation = NULL;
1205 nsCOMPtr<nsIAccessibleRelation> relation;
1206 nsresult rv = GetRelation(aRelationIndex, getter_AddRefs(relation));
1207 if (NS_FAILED(rv))
1208 return GetHRESULT(rv);
1210 nsCOMPtr<nsIWinAccessNode> winAccessNode(do_QueryInterface(relation));
1211 if (!winAccessNode)
1212 return E_FAIL;
1214 void *instancePtr = NULL;
1215 rv = winAccessNode->QueryNativeInterface(IID_IAccessibleRelation,
1216 &instancePtr);
1217 if (NS_FAILED(rv))
1218 return GetHRESULT(rv);
1220 *aRelation = static_cast<IAccessibleRelation*>(instancePtr);
1221 return S_OK;
1223 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1224 return E_FAIL;
1227 STDMETHODIMP
1228 nsAccessibleWrap::get_relations(long aMaxRelations,
1229 IAccessibleRelation **aRelation,
1230 long *aNRelations)
1232 __try {
1233 *aRelation = NULL;
1234 *aNRelations = 0;
1236 nsCOMPtr<nsIArray> relations;
1237 nsresult rv = GetRelations(getter_AddRefs(relations));
1238 if (NS_FAILED(rv))
1239 return GetHRESULT(rv);
1241 PRUint32 length = 0;
1242 rv = relations->GetLength(&length);
1243 if (NS_FAILED(rv))
1244 return GetHRESULT(rv);
1246 if (length == 0)
1247 return S_FALSE;
1249 PRUint32 count = length < (PRUint32)aMaxRelations ? length : aMaxRelations;
1251 PRUint32 index = 0;
1252 for (; index < count; index++) {
1253 nsCOMPtr<nsIWinAccessNode> winAccessNode =
1254 do_QueryElementAt(relations, index, &rv);
1255 if (NS_FAILED(rv))
1256 break;
1258 void *instancePtr = NULL;
1259 nsresult rv = winAccessNode->QueryNativeInterface(IID_IAccessibleRelation,
1260 &instancePtr);
1261 if (NS_FAILED(rv))
1262 break;
1264 aRelation[index] = static_cast<IAccessibleRelation*>(instancePtr);
1267 if (NS_FAILED(rv)) {
1268 for (PRUint32 index2 = 0; index2 < index; index2++) {
1269 aRelation[index2]->Release();
1270 aRelation[index2] = NULL;
1272 return GetHRESULT(rv);
1275 *aNRelations = count;
1276 return S_OK;
1278 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1279 return E_FAIL;
1282 STDMETHODIMP
1283 nsAccessibleWrap::role(long *aRole)
1285 __try {
1286 *aRole = 0;
1288 PRUint32 xpRole = 0;
1289 nsresult rv = GetFinalRole(&xpRole);
1290 if (NS_FAILED(rv))
1291 return GetHRESULT(rv);
1293 NS_ASSERTION(gWindowsRoleMap[nsIAccessibleRole::ROLE_LAST_ENTRY].ia2Role == ROLE_WINDOWS_LAST_ENTRY,
1294 "MSAA role map skewed");
1296 *aRole = gWindowsRoleMap[xpRole].ia2Role;
1297 return S_OK;
1299 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1300 return E_FAIL;
1303 STDMETHODIMP
1304 nsAccessibleWrap::scrollTo(enum IA2ScrollType aScrollType)
1306 __try {
1307 nsresult rv = ScrollTo(aScrollType);
1308 return GetHRESULT(rv);
1310 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1311 return E_FAIL;
1314 STDMETHODIMP
1315 nsAccessibleWrap::scrollToPoint(enum IA2CoordinateType aCoordType,
1316 long aX, long aY)
1318 __try {
1319 PRUint32 geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
1320 nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
1321 nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
1323 nsresult rv = ScrollToPoint(geckoCoordType, aX, aY);
1324 return GetHRESULT(rv);
1326 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1327 return E_FAIL;
1330 STDMETHODIMP
1331 nsAccessibleWrap::get_groupPosition(long *aGroupLevel,
1332 long *aSimilarItemsInGroup,
1333 long *aPositionInGroup)
1335 __try {
1336 PRInt32 groupLevel = 0;
1337 PRInt32 similarItemsInGroup = 0;
1338 PRInt32 positionInGroup = 0;
1339 nsresult rv = GroupPosition(&groupLevel, &similarItemsInGroup,
1340 &positionInGroup);
1342 *aGroupLevel = groupLevel;
1343 *aSimilarItemsInGroup = similarItemsInGroup;
1344 *aPositionInGroup = positionInGroup;
1346 if (NS_FAILED(rv))
1347 return GetHRESULT(rv);
1349 if (groupLevel ==0 && similarItemsInGroup == 0 && positionInGroup == 0)
1350 return S_FALSE;
1351 return S_OK;
1353 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1354 return E_FAIL;
1357 STDMETHODIMP
1358 nsAccessibleWrap::get_states(AccessibleStates *aStates)
1360 __try {
1361 *aStates = 0;
1363 // XXX: bug 344674 should come with better approach that we have here.
1365 PRUint32 states = 0, extraStates = 0;
1366 nsresult rv = GetFinalState(&states, &extraStates);
1367 if (NS_FAILED(rv))
1368 return GetHRESULT(rv);
1370 if (states & nsIAccessibleStates::STATE_INVALID)
1371 *aStates |= IA2_STATE_INVALID_ENTRY;
1372 if (states & nsIAccessibleStates::STATE_REQUIRED)
1373 *aStates |= IA2_STATE_REQUIRED;
1375 // The following IA2 states are not supported by Gecko
1376 // IA2_STATE_ARMED
1377 // IA2_STATE_MANAGES_DESCENDANTS
1378 // IA2_STATE_ICONIFIED
1379 // IA2_STATE_INVALID // This is not a state, it is the absence of a state
1381 if (extraStates & nsIAccessibleStates::EXT_STATE_ACTIVE)
1382 *aStates |= IA2_STATE_ACTIVE;
1383 if (extraStates & nsIAccessibleStates::EXT_STATE_DEFUNCT)
1384 *aStates |= IA2_STATE_DEFUNCT;
1385 if (extraStates & nsIAccessibleStates::EXT_STATE_EDITABLE)
1386 *aStates |= IA2_STATE_EDITABLE;
1387 if (extraStates & nsIAccessibleStates::EXT_STATE_HORIZONTAL)
1388 *aStates |= IA2_STATE_HORIZONTAL;
1389 if (extraStates & nsIAccessibleStates::EXT_STATE_MODAL)
1390 *aStates |= IA2_STATE_MODAL;
1391 if (extraStates & nsIAccessibleStates::EXT_STATE_MULTI_LINE)
1392 *aStates |= IA2_STATE_MULTI_LINE;
1393 if (extraStates & nsIAccessibleStates::EXT_STATE_OPAQUE)
1394 *aStates |= IA2_STATE_OPAQUE;
1395 if (extraStates & nsIAccessibleStates::EXT_STATE_SELECTABLE_TEXT)
1396 *aStates |= IA2_STATE_SELECTABLE_TEXT;
1397 if (extraStates & nsIAccessibleStates::EXT_STATE_SINGLE_LINE)
1398 *aStates |= IA2_STATE_SINGLE_LINE;
1399 if (extraStates & nsIAccessibleStates::EXT_STATE_STALE)
1400 *aStates |= IA2_STATE_STALE;
1401 if (extraStates & nsIAccessibleStates::EXT_STATE_SUPPORTS_AUTOCOMPLETION)
1402 *aStates |= IA2_STATE_SUPPORTS_AUTOCOMPLETION;
1403 if (extraStates & nsIAccessibleStates::EXT_STATE_TRANSIENT)
1404 *aStates |= IA2_STATE_TRANSIENT;
1405 if (extraStates & nsIAccessibleStates::EXT_STATE_VERTICAL)
1406 *aStates |= IA2_STATE_VERTICAL;
1408 return S_OK;
1410 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1411 return E_FAIL;
1414 STDMETHODIMP
1415 nsAccessibleWrap::get_extendedRole(BSTR *aExtendedRole)
1417 __try {
1418 *aExtendedRole = NULL;
1419 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1421 return E_NOTIMPL;
1424 STDMETHODIMP
1425 nsAccessibleWrap::get_localizedExtendedRole(BSTR *aLocalizedExtendedRole)
1427 __try {
1428 *aLocalizedExtendedRole = NULL;
1429 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1431 return E_NOTIMPL;
1434 STDMETHODIMP
1435 nsAccessibleWrap::get_nExtendedStates(long *aNExtendedStates)
1437 __try {
1438 *aNExtendedStates = 0;
1439 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1441 return E_NOTIMPL;
1444 STDMETHODIMP
1445 nsAccessibleWrap::get_extendedStates(long aMaxExtendedStates,
1446 BSTR **aExtendedStates,
1447 long *aNExtendedStates)
1449 __try {
1450 *aExtendedStates = NULL;
1451 *aNExtendedStates = 0;
1452 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1454 return E_NOTIMPL;
1457 STDMETHODIMP
1458 nsAccessibleWrap::get_localizedExtendedStates(long aMaxLocalizedExtendedStates,
1459 BSTR **aLocalizedExtendedStates,
1460 long *aNLocalizedExtendedStates)
1462 __try {
1463 *aLocalizedExtendedStates = NULL;
1464 *aNLocalizedExtendedStates = 0;
1465 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1467 return E_NOTIMPL;
1470 STDMETHODIMP
1471 nsAccessibleWrap::get_uniqueID(long *uniqueID)
1473 __try {
1474 void *id = nsnull;
1475 nsresult rv = GetUniqueID(&id);
1476 if (NS_FAILED(rv))
1477 return GetHRESULT(rv);
1479 *uniqueID = - reinterpret_cast<long>(id);
1480 return S_OK;
1482 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1483 return E_FAIL;
1486 STDMETHODIMP
1487 nsAccessibleWrap::get_windowHandle(HWND *aWindowHandle)
1489 __try {
1490 *aWindowHandle = 0;
1492 if (!mDOMNode)
1493 return E_FAIL;
1495 void *handle = nsnull;
1496 nsresult rv = GetOwnerWindow(&handle);
1497 if (NS_FAILED(rv))
1498 return GetHRESULT(rv);
1500 *aWindowHandle = reinterpret_cast<HWND>(handle);
1501 return S_OK;
1503 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1504 return E_FAIL;
1507 STDMETHODIMP
1508 nsAccessibleWrap::get_indexInParent(long *aIndexInParent)
1510 __try {
1511 *aIndexInParent = -1;
1513 PRInt32 index = -1;
1514 nsresult rv = GetIndexInParent(&index);
1515 if (NS_FAILED(rv))
1516 return GetHRESULT(rv);
1518 if (index == -1)
1519 return S_FALSE;
1521 *aIndexInParent = index;
1522 return S_OK;
1524 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1525 return E_FAIL;
1528 STDMETHODIMP
1529 nsAccessibleWrap::get_locale(IA2Locale *aLocale)
1531 __try {
1532 // Language codes consist of a primary code and a possibly empty series of
1533 // subcodes: language-code = primary-code ( "-" subcode )*
1534 // Two-letter primary codes are reserved for [ISO639] language abbreviations.
1535 // Any two-letter subcode is understood to be a [ISO3166] country code.
1537 nsAutoString lang;
1538 nsresult rv = GetLanguage(lang);
1539 if (NS_FAILED(rv))
1540 return GetHRESULT(rv);
1542 // If primary code consists from two letters then expose it as language.
1543 PRInt32 offset = lang.FindChar('-', 0);
1544 if (offset == -1) {
1545 if (lang.Length() == 2) {
1546 aLocale->language = ::SysAllocString(lang.get());
1547 return S_OK;
1549 } else if (offset == 2) {
1550 aLocale->language = ::SysAllocStringLen(lang.get(), 2);
1552 // If the first subcode consists from two letters then expose it as
1553 // country.
1554 offset = lang.FindChar('-', 3);
1555 if (offset == -1) {
1556 if (lang.Length() == 5) {
1557 aLocale->country = ::SysAllocString(lang.get() + 3);
1558 return S_OK;
1560 } else if (offset == 5) {
1561 aLocale->country = ::SysAllocStringLen(lang.get() + 3, 2);
1565 // Expose as a string if primary code or subcode cannot point to language or
1566 // country abbreviations or if there are more than one subcode.
1567 aLocale->variant = ::SysAllocString(lang.get());
1568 return S_OK;
1570 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1571 return E_FAIL;
1574 STDMETHODIMP
1575 nsAccessibleWrap::get_attributes(BSTR *aAttributes)
1577 // The format is name:value;name:value; with \ for escaping these
1578 // characters ":;=,\".
1579 __try {
1580 *aAttributes = NULL;
1582 nsCOMPtr<nsIPersistentProperties> attributes;
1583 nsresult rv = GetAttributes(getter_AddRefs(attributes));
1584 if (NS_FAILED(rv))
1585 return GetHRESULT(rv);
1587 return ConvertToIA2Attributes(attributes, aAttributes);
1589 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1590 return E_FAIL;
1593 // For IDispatch support
1594 STDMETHODIMP
1595 nsAccessibleWrap::GetTypeInfoCount(UINT *p)
1597 *p = 0;
1598 return E_NOTIMPL;
1601 // For IDispatch support
1602 STDMETHODIMP nsAccessibleWrap::GetTypeInfo(UINT i, LCID lcid, ITypeInfo **ppti)
1604 *ppti = 0;
1605 return E_NOTIMPL;
1608 // For IDispatch support
1609 STDMETHODIMP
1610 nsAccessibleWrap::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames,
1611 UINT cNames, LCID lcid, DISPID *rgDispId)
1613 return E_NOTIMPL;
1616 // For IDispatch support
1617 STDMETHODIMP nsAccessibleWrap::Invoke(DISPID dispIdMember, REFIID riid,
1618 LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
1619 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1621 return E_NOTIMPL;
1625 NS_IMETHODIMP nsAccessibleWrap::GetNativeInterface(void **aOutAccessible)
1627 *aOutAccessible = static_cast<IAccessible*>(this);
1628 NS_ADDREF_THIS();
1629 return NS_OK;
1632 // nsPIAccessible
1634 NS_IMETHODIMP
1635 nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent)
1637 NS_ENSURE_ARG(aEvent);
1639 nsresult rv = nsAccessible::FireAccessibleEvent(aEvent);
1640 NS_ENSURE_SUCCESS(rv, rv);
1642 return FirePlatformEvent(aEvent);
1645 nsresult
1646 nsAccessibleWrap::FirePlatformEvent(nsIAccessibleEvent *aEvent)
1648 PRUint32 eventType = 0;
1649 aEvent->GetEventType(&eventType);
1651 NS_ENSURE_TRUE(eventType > 0 &&
1652 eventType < nsIAccessibleEvent::EVENT_LAST_ENTRY,
1653 NS_ERROR_FAILURE);
1655 PRUint32 winLastEntry = gWinEventMap[nsIAccessibleEvent::EVENT_LAST_ENTRY];
1656 NS_ASSERTION(winLastEntry == kEVENT_LAST_ENTRY,
1657 "MSAA event map skewed");
1659 PRUint32 winEvent = gWinEventMap[eventType];
1660 if (!winEvent)
1661 return NS_OK;
1663 // Means we're not active.
1664 NS_ENSURE_TRUE(mWeakShell, NS_ERROR_FAILURE);
1666 nsCOMPtr<nsIAccessible> accessible;
1667 aEvent->GetAccessible(getter_AddRefs(accessible));
1668 if (!accessible)
1669 return NS_OK;
1671 if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED ||
1672 eventType == nsIAccessibleEvent::EVENT_FOCUS) {
1673 UpdateSystemCaret();
1676 PRInt32 childID = GetChildIDFor(accessible); // get the id for the accessible
1677 if (!childID)
1678 return NS_OK; // Can't fire an event without a child ID
1680 // See if we're in a scrollable area with its own window
1681 nsCOMPtr<nsIAccessible> newAccessible;
1682 if (eventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
1683 eventType == nsIAccessibleEvent::EVENT_DOM_DESTROY) {
1684 // Don't use frame from current accessible when we're hiding that
1685 // accessible.
1686 accessible->GetParent(getter_AddRefs(newAccessible));
1687 } else {
1688 newAccessible = accessible;
1691 HWND hWnd = GetHWNDFor(newAccessible);
1692 NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE);
1694 // Gecko uses two windows for every scrollable area. One window contains
1695 // scrollbars and the child window contains only the client area.
1696 // Details of the 2 window system:
1697 // * Scrollbar window: caret drawing window & return value for WindowFromAccessibleObject()
1698 // * Client area window: text drawing window & MSAA event window
1700 // Fire MSAA event for client area window.
1701 NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
1703 // If the accessible children are changed then drop the IEnumVariant current
1704 // position of the accessible.
1705 if (eventType == nsIAccessibleEvent::EVENT_REORDER)
1706 UnattachIEnumVariant();
1708 return NS_OK;
1711 //------- Helper methods ---------
1713 PRInt32 nsAccessibleWrap::GetChildIDFor(nsIAccessible* aAccessible)
1715 // A child ID of the window is required, when we use NotifyWinEvent,
1716 // so that the 3rd party application can call back and get the IAccessible
1717 // the event occured on.
1719 void *uniqueID = nsnull;
1720 nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
1721 if (!accessNode) {
1722 return 0;
1724 accessNode->GetUniqueID(&uniqueID);
1726 // Yes, this means we're only compatibible with 32 bit
1727 // MSAA is only available for 32 bit windows, so it's okay
1728 return - NS_PTR_TO_INT32(uniqueID);
1731 HWND
1732 nsAccessibleWrap::GetHWNDFor(nsIAccessible *aAccessible)
1734 nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
1735 nsCOMPtr<nsPIAccessNode> privateAccessNode(do_QueryInterface(accessNode));
1736 if (!privateAccessNode)
1737 return 0;
1739 HWND hWnd = 0;
1741 nsIFrame *frame = privateAccessNode->GetFrame();
1742 if (frame) {
1743 nsIWidget *window = frame->GetWindow();
1744 PRBool isVisible;
1745 window->IsVisible(isVisible);
1746 if (isVisible) {
1747 // Short explanation:
1748 // If HWND for frame is inside a hidden window, fire the event on the
1749 // containing document's visible window.
1751 // Long explanation:
1752 // This is really just to fix combo boxes with JAWS. Window-Eyes already
1753 // worked with combo boxes because they use the value change event in
1754 // the closed combo box case. JAWS will only pay attention to the focus
1755 // events on the list items. The JAWS developers haven't fixed that, so
1756 // we'll use the focus events to make JAWS work. However, JAWS is
1757 // ignoring events on a hidden window. So, in order to fix the bug where
1758 // JAWS doesn't echo the current option as it changes in a closed
1759 // combo box, we need to use an ensure that we never fire an event with
1760 // an HWND for a hidden window.
1761 hWnd = (HWND)frame->GetWindow()->GetNativeData(NS_NATIVE_WINDOW);
1765 if (!hWnd) {
1766 void* handle = nsnull;
1767 nsCOMPtr<nsIAccessibleDocument> accessibleDoc;
1768 accessNode->GetAccessibleDocument(getter_AddRefs(accessibleDoc));
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 void nsAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild, nsIAccessible **aXPAccessible)
1876 *aXPAccessible = nsnull;
1877 if (!mWeakShell)
1878 return; // Fail, we don't want to do anything after we've shut down
1880 // if its us real easy - this seems to always be the case
1881 if (aVarChild.lVal == CHILDID_SELF) {
1882 *aXPAccessible = static_cast<nsIAccessible*>(this);
1884 else if (MustPrune(this)) {
1885 return;
1887 else {
1888 // XXX It is rare to use a VARIANT with a child num
1889 // so optimizing this is not a priority
1890 // We can come back it do it later, if there are perf problems
1891 // with a specific assistive technology
1892 nsCOMPtr<nsIAccessible> xpAccessible, nextAccessible;
1893 GetFirstChild(getter_AddRefs(xpAccessible));
1894 for (PRInt32 index = 0; xpAccessible; index ++) {
1895 if (!xpAccessible)
1896 break; // Failed
1897 if (aVarChild.lVal == index) {
1898 *aXPAccessible = xpAccessible;
1899 break;
1901 nextAccessible = xpAccessible;
1902 nextAccessible->GetNextSibling(getter_AddRefs(xpAccessible));
1905 NS_IF_ADDREF(*aXPAccessible);
1908 void nsAccessibleWrap::UpdateSystemCaret()
1910 // Move the system caret so that Windows Tablet Edition and tradional ATs with
1911 // off-screen model can follow the caret
1912 ::DestroyCaret();
1914 nsRefPtr<nsRootAccessible> rootAccessible = GetRootAccessible();
1915 if (!rootAccessible) {
1916 return;
1919 nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
1920 if (!caretAccessible) {
1921 return;
1924 nsIWidget *widget;
1925 nsRect caretRect = caretAccessible->GetCaretRect(&widget);
1926 HWND caretWnd;
1927 if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) {
1928 return;
1931 // Create invisible bitmap for caret, otherwise its appearance interferes
1932 // with Gecko caret
1933 HBITMAP caretBitMap = CreateBitmap(1, caretRect.height, 1, 1, NULL);
1934 if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) { // Also destroys the last caret
1935 ::ShowCaret(caretWnd);
1936 RECT windowRect;
1937 ::GetWindowRect(caretWnd, &windowRect);
1938 ::SetCaretPos(caretRect.x - windowRect.left, caretRect.y - windowRect.top);
1939 ::DeleteObject(caretBitMap);