Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / accessible / src / msaa / nsAccessibleWrap.cpp
blob262bb782bce92ac925c4b3ebe4578623df00e23f
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 (nsAccUtils::MustPrune(this))
250 return NS_OK;
252 PRInt32 numChildren;
253 GetChildCount(&numChildren);
254 *pcountChildren = numChildren;
255 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
257 return S_OK;
260 STDMETHODIMP nsAccessibleWrap::get_accChild(
261 /* [in] */ VARIANT varChild,
262 /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild)
264 __try {
265 *ppdispChild = NULL;
266 if (!mWeakShell || varChild.vt != VT_I4)
267 return E_FAIL;
269 if (varChild.lVal == CHILDID_SELF) {
270 *ppdispChild = static_cast<IDispatch*>(this);
271 AddRef();
272 return S_OK;
275 nsCOMPtr<nsIAccessible> childAccessible;
276 if (!nsAccUtils::MustPrune(this)) {
277 GetChildAt(varChild.lVal - 1, getter_AddRefs(childAccessible));
278 if (childAccessible) {
279 *ppdispChild = NativeAccessible(childAccessible);
282 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
284 return (*ppdispChild)? S_OK: E_FAIL;
287 STDMETHODIMP nsAccessibleWrap::get_accName(
288 /* [optional][in] */ VARIANT varChild,
289 /* [retval][out] */ BSTR __RPC_FAR *pszName)
291 __try {
292 *pszName = NULL;
293 nsCOMPtr<nsIAccessible> xpAccessible;
294 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
295 if (!xpAccessible)
296 return E_FAIL;
297 nsAutoString name;
298 nsresult rv = xpAccessible->GetName(name);
299 if (NS_FAILED(rv))
300 return GetHRESULT(rv);
302 if (name.IsVoid()) {
303 // Valid return value for the name:
304 // The name was not provided, e.g. no alt attribute for an image.
305 // A screen reader may choose to invent its own accessible name, e.g. from
306 // an image src attribute.
307 // See nsHTMLImageAccessible::GetName()
308 return S_OK;
311 *pszName = ::SysAllocStringLen(name.get(), name.Length());
312 if (!*pszName)
313 return E_OUTOFMEMORY;
315 #ifdef DEBUG_A11Y
316 NS_ASSERTION(mIsInitialized, "Access node was not initialized");
317 #endif
318 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
320 return S_OK;
324 STDMETHODIMP nsAccessibleWrap::get_accValue(
325 /* [optional][in] */ VARIANT varChild,
326 /* [retval][out] */ BSTR __RPC_FAR *pszValue)
328 __try {
329 *pszValue = NULL;
330 nsCOMPtr<nsIAccessible> xpAccessible;
331 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
332 if (xpAccessible) {
333 nsAutoString value;
334 if (NS_FAILED(xpAccessible->GetValue(value)))
335 return E_FAIL;
337 // see bug 438784: Need to expose URL on doc's value attribute.
338 // For this, reverting part of fix for bug 425693 to make this MSAA method
339 // behave IAccessible2-style.
340 if (value.IsEmpty())
341 return S_FALSE;
343 *pszValue = ::SysAllocStringLen(value.get(), value.Length());
344 if (!*pszValue)
345 return E_OUTOFMEMORY;
347 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
348 return S_OK;
351 STDMETHODIMP
352 nsAccessibleWrap::get_accDescription(VARIANT varChild,
353 BSTR __RPC_FAR *pszDescription)
355 __try {
356 *pszDescription = NULL;
358 nsCOMPtr<nsIAccessible> xpAccessible;
359 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
360 if (!xpAccessible)
361 return E_FAIL;
363 // For items that are a choice in a list of choices, use MSAA description
364 // field to shoehorn positional info, it's becoming a defacto standard use for
365 // the field.
367 nsAutoString description;
369 // Try to get group attributes to make a positional description string. We
370 // can't use nsIAccessible::groupPosition because the method isn't supposed
371 // to work with elements exposing 'level' attribute only (like HTML headings).
372 nsCOMPtr<nsIPersistentProperties> attributes;
373 nsresult rv = xpAccessible->GetAttributes(getter_AddRefs(attributes));
374 NS_ENSURE_SUCCESS(rv, rv);
375 if (!attributes)
376 return NS_ERROR_FAILURE;
378 PRInt32 groupLevel = 0;
379 PRInt32 itemsInGroup = 0;
380 PRInt32 positionInGroup = 0;
381 nsAccUtils::GetAccGroupAttrs(attributes, &groupLevel, &positionInGroup,
382 &itemsInGroup);
384 if (positionInGroup > 0) {
385 if (groupLevel > 0) {
386 // XXX: How do we calculate the number of children? Now we append
387 // " with [numChildren]c" for tree item. In the future we may need to
388 // use the ARIA owns property to calculate that if it's present.
389 PRInt32 numChildren = 0;
391 PRUint32 currentRole = 0;
392 rv = xpAccessible->GetFinalRole(&currentRole);
393 if (NS_SUCCEEDED(rv) &&
394 currentRole == nsIAccessibleRole::ROLE_OUTLINEITEM) {
395 nsCOMPtr<nsIAccessible> child;
396 xpAccessible->GetFirstChild(getter_AddRefs(child));
397 while (child) {
398 child->GetFinalRole(&currentRole);
399 if (currentRole == nsIAccessibleRole::ROLE_GROUPING) {
400 nsCOMPtr<nsIAccessible> groupChild;
401 child->GetFirstChild(getter_AddRefs(groupChild));
402 while (groupChild) {
403 groupChild->GetFinalRole(&currentRole);
404 numChildren +=
405 (currentRole == nsIAccessibleRole::ROLE_OUTLINEITEM);
406 nsCOMPtr<nsIAccessible> nextGroupChild;
407 groupChild->GetNextSibling(getter_AddRefs(nextGroupChild));
408 groupChild.swap(nextGroupChild);
410 break;
412 nsCOMPtr<nsIAccessible> nextChild;
413 child->GetNextSibling(getter_AddRefs(nextChild));
414 child.swap(nextChild);
418 if (numChildren) {
419 nsTextFormatter::ssprintf(description,
420 NS_LITERAL_STRING("L%d, %d of %d with %d").get(),
421 groupLevel, positionInGroup, itemsInGroup,
422 numChildren);
423 } else {
424 nsTextFormatter::ssprintf(description,
425 NS_LITERAL_STRING("L%d, %d of %d").get(),
426 groupLevel, positionInGroup, itemsInGroup);
428 } else { // Position has no level
429 nsTextFormatter::ssprintf(description,
430 NS_LITERAL_STRING("%d of %d").get(),
431 positionInGroup, itemsInGroup);
433 } else if (groupLevel > 0) {
434 nsTextFormatter::ssprintf(description, NS_LITERAL_STRING("L%d").get(),
435 groupLevel);
438 if (!description.IsEmpty()) {
439 *pszDescription = ::SysAllocStringLen(description.get(),
440 description.Length());
441 return *pszDescription ? S_OK : E_OUTOFMEMORY;
444 xpAccessible->GetDescription(description);
445 if (!description.IsEmpty()) {
446 // Signal to screen readers that this description is speakable
447 // and is not a formatted positional information description
448 // Don't localize the "Description: " part of this string, it will be
449 // parsed out by assistive technologies.
450 description = NS_LITERAL_STRING("Description: ") + description;
453 *pszDescription = ::SysAllocStringLen(description.get(),
454 description.Length());
455 return *pszDescription ? S_OK : E_OUTOFMEMORY;
457 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
458 return E_FAIL;
461 STDMETHODIMP nsAccessibleWrap::get_accRole(
462 /* [optional][in] */ VARIANT varChild,
463 /* [retval][out] */ VARIANT __RPC_FAR *pvarRole)
465 __try {
466 VariantInit(pvarRole);
468 nsCOMPtr<nsIAccessible> xpAccessible;
469 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
471 if (!xpAccessible)
472 return E_FAIL;
474 #ifdef DEBUG_A11Y
475 NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible),
476 "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 (nsAccUtils::Role(parent) == nsIAccessibleRole::ROLE_TREE_TABLE)
493 msaaRole = ROLE_SYSTEM_OUTLINEITEM;
496 // -- Try enumerated role
497 if (msaaRole != USE_ROLE_STRING) {
498 pvarRole->vt = VT_I4;
499 pvarRole->lVal = msaaRole; // Normal enumerated role
500 return S_OK;
503 // -- Try BSTR role
504 // Could not map to known enumerated MSAA role like ROLE_BUTTON
505 // Use BSTR role to expose role attribute or tag name + namespace
506 nsCOMPtr<nsIDOMNode> domNode;
507 nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(xpAccessible));
508 if (!accessNode)
509 return E_FAIL;
511 accessNode->GetDOMNode(getter_AddRefs(domNode));
512 nsIContent *content = nsCoreUtils::GetRoleContent(domNode);
513 if (!content)
514 return E_FAIL;
516 if (content->IsNodeOfType(nsINode::eELEMENT)) {
517 nsAutoString roleString;
518 if (msaaRole != ROLE_SYSTEM_CLIENT &&
519 !content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::role, roleString)) {
520 nsINodeInfo *nodeInfo = content->NodeInfo();
521 nodeInfo->GetName(roleString);
522 nsAutoString nameSpaceURI;
523 nodeInfo->GetNamespaceURI(nameSpaceURI);
524 if (!nameSpaceURI.IsEmpty()) {
525 // Only append name space if different from that of current document
526 roleString += NS_LITERAL_STRING(", ") + nameSpaceURI;
529 if (!roleString.IsEmpty()) {
530 pvarRole->vt = VT_BSTR;
531 pvarRole->bstrVal = ::SysAllocString(roleString.get());
532 return S_OK;
535 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
536 return E_FAIL;
539 STDMETHODIMP nsAccessibleWrap::get_accState(
540 /* [optional][in] */ VARIANT varChild,
541 /* [retval][out] */ VARIANT __RPC_FAR *pvarState)
543 __try {
544 VariantInit(pvarState);
545 pvarState->vt = VT_I4;
546 pvarState->lVal = 0;
548 nsCOMPtr<nsIAccessible> xpAccessible;
549 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
550 if (!xpAccessible)
551 return E_FAIL;
553 PRUint32 state = 0;
554 if (NS_FAILED(xpAccessible->GetState(&state, nsnull)))
555 return E_FAIL;
557 pvarState->lVal = state;
558 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
559 return S_OK;
563 STDMETHODIMP nsAccessibleWrap::get_accHelp(
564 /* [optional][in] */ VARIANT varChild,
565 /* [retval][out] */ BSTR __RPC_FAR *pszHelp)
567 *pszHelp = NULL;
568 return S_FALSE;
571 STDMETHODIMP nsAccessibleWrap::get_accHelpTopic(
572 /* [out] */ BSTR __RPC_FAR *pszHelpFile,
573 /* [optional][in] */ VARIANT varChild,
574 /* [retval][out] */ long __RPC_FAR *pidTopic)
576 *pszHelpFile = NULL;
577 *pidTopic = 0;
578 return E_NOTIMPL;
581 STDMETHODIMP nsAccessibleWrap::get_accKeyboardShortcut(
582 /* [optional][in] */ VARIANT varChild,
583 /* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut)
585 __try {
586 *pszKeyboardShortcut = NULL;
587 nsCOMPtr<nsIAccessible> xpAccessible;
588 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
589 if (xpAccessible) {
590 nsAutoString shortcut;
591 nsresult rv = xpAccessible->GetKeyboardShortcut(shortcut);
592 if (NS_FAILED(rv))
593 return E_FAIL;
595 *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(),
596 shortcut.Length());
597 return *pszKeyboardShortcut ? S_OK : E_OUTOFMEMORY;
599 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
600 return E_FAIL;
603 STDMETHODIMP nsAccessibleWrap::get_accFocus(
604 /* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
606 // VT_EMPTY: None. This object does not have the keyboard focus itself
607 // and does not contain a child that has the keyboard focus.
608 // VT_I4: lVal is CHILDID_SELF. The object itself has the keyboard focus.
609 // VT_I4: lVal contains the child ID of the child element with the keyboard focus.
610 // VT_DISPATCH: pdispVal member is the address of the IDispatch interface
611 // for the child object with the keyboard focus.
612 __try {
613 if (!mDOMNode) {
614 return E_FAIL; // This node is shut down
617 VariantInit(pvarChild);
619 // Return the current IAccessible child that has focus
620 nsCOMPtr<nsIAccessible> focusedAccessible;
621 GetFocusedChild(getter_AddRefs(focusedAccessible));
622 if (focusedAccessible == this) {
623 pvarChild->vt = VT_I4;
624 pvarChild->lVal = CHILDID_SELF;
626 else if (focusedAccessible) {
627 pvarChild->vt = VT_DISPATCH;
628 pvarChild->pdispVal = NativeAccessible(focusedAccessible);
630 else {
631 pvarChild->vt = VT_EMPTY; // No focus or focus is not a child
634 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
635 return S_OK;
638 // This helper class implements IEnumVARIANT for a nsIArray containing nsIAccessible objects.
640 class AccessibleEnumerator : public IEnumVARIANT
642 public:
643 AccessibleEnumerator(nsIArray* aArray) : mArray(aArray), mCurIndex(0) { }
644 AccessibleEnumerator(const AccessibleEnumerator& toCopy) :
645 mArray(toCopy.mArray), mCurIndex(toCopy.mCurIndex) { }
646 ~AccessibleEnumerator() { }
648 // IUnknown
649 STDMETHODIMP QueryInterface(REFIID iid, void ** ppvObject);
650 STDMETHODIMP_(ULONG) AddRef(void);
651 STDMETHODIMP_(ULONG) Release(void);
653 // IEnumVARIANT
654 STDMETHODIMP Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched);
655 STDMETHODIMP Skip(unsigned long celt);
656 STDMETHODIMP Reset()
658 mCurIndex = 0;
659 return S_OK;
661 STDMETHODIMP Clone(IEnumVARIANT FAR* FAR* ppenum);
663 private:
664 nsCOMPtr<nsIArray> mArray;
665 PRUint32 mCurIndex;
666 nsAutoRefCnt mRefCnt;
669 HRESULT
670 AccessibleEnumerator::QueryInterface(REFIID iid, void ** ppvObject)
672 __try {
673 if (iid == IID_IEnumVARIANT) {
674 *ppvObject = static_cast<IEnumVARIANT*>(this);
675 AddRef();
676 return S_OK;
678 if (iid == IID_IUnknown) {
679 *ppvObject = static_cast<IUnknown*>(this);
680 AddRef();
681 return S_OK;
684 *ppvObject = NULL;
685 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
686 return E_NOINTERFACE;
689 STDMETHODIMP_(ULONG)
690 AccessibleEnumerator::AddRef(void)
692 return ++mRefCnt;
695 STDMETHODIMP_(ULONG)
696 AccessibleEnumerator::Release(void)
698 ULONG r = --mRefCnt;
699 if (r == 0)
700 delete this;
701 return r;
704 STDMETHODIMP
705 AccessibleEnumerator::Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched)
707 __try {
708 PRUint32 length = 0;
709 mArray->GetLength(&length);
711 HRESULT hr = S_OK;
713 // Can't get more elements than there are...
714 if (celt > length - mCurIndex) {
715 hr = S_FALSE;
716 celt = length - mCurIndex;
719 for (PRUint32 i = 0; i < celt; ++i, ++mCurIndex) {
720 // Copy the elements of the array into rgvar
721 nsCOMPtr<nsIAccessible> accel(do_QueryElementAt(mArray, mCurIndex));
722 NS_ASSERTION(accel, "Invalid pointer in mArray");
724 if (accel) {
725 rgvar[i].vt = VT_DISPATCH;
726 rgvar[i].pdispVal = nsAccessibleWrap::NativeAccessible(accel);
730 if (pceltFetched)
731 *pceltFetched = celt;
733 return hr;
734 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
736 return S_OK;
739 STDMETHODIMP
740 AccessibleEnumerator::Clone(IEnumVARIANT FAR* FAR* ppenum)
742 __try {
743 *ppenum = new AccessibleEnumerator(*this);
744 if (!*ppenum)
745 return E_OUTOFMEMORY;
746 NS_ADDREF(*ppenum);
747 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
748 return S_OK;
751 STDMETHODIMP
752 AccessibleEnumerator::Skip(unsigned long celt)
754 __try {
755 PRUint32 length = 0;
756 mArray->GetLength(&length);
757 // Check if we can skip the requested number of elements
758 if (celt > length - mCurIndex) {
759 mCurIndex = length;
760 return S_FALSE;
762 mCurIndex += celt;
763 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
764 return S_OK;
768 * This method is called when a client wants to know which children of a node
769 * are selected. Note that this method can only find selected children for
770 * nsIAccessible object which implement nsIAccessibleSelectable.
772 * The VARIANT return value arguement is expected to either contain a single IAccessible
773 * or an IEnumVARIANT of IAccessibles. We return the IEnumVARIANT regardless of the number
774 * of children selected, unless there are none selected in which case we return an empty
775 * VARIANT.
777 * We get the selected options from the select's accessible object and wrap
778 * those in an AccessibleEnumerator which we then put in the return VARIANT.
780 * returns a VT_EMPTY VARIANT if:
781 * - there are no selected children for this object
782 * - the object is not the type that can have children selected
784 STDMETHODIMP nsAccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren)
786 __try {
787 VariantInit(pvarChildren);
788 pvarChildren->vt = VT_EMPTY;
790 nsCOMPtr<nsIAccessibleSelectable>
791 select(do_QueryInterface(static_cast<nsIAccessible*>(this)));
793 if (select) { // do we have an nsIAccessibleSelectable?
794 // we have an accessible that can have children selected
795 nsCOMPtr<nsIArray> selectedOptions;
796 // gets the selected options as nsIAccessibles.
797 select->GetSelectedChildren(getter_AddRefs(selectedOptions));
798 if (selectedOptions) { // false if the select has no children or none are selected
799 // 1) Create and initialize the enumeration
800 nsRefPtr<AccessibleEnumerator> pEnum = new AccessibleEnumerator(selectedOptions);
802 // 2) Put the enumerator in the VARIANT
803 if (!pEnum)
804 return E_OUTOFMEMORY;
805 pvarChildren->vt = VT_UNKNOWN; // this must be VT_UNKNOWN for an IEnumVARIANT
806 NS_ADDREF(pvarChildren->punkVal = pEnum);
809 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
810 return S_OK;
813 STDMETHODIMP nsAccessibleWrap::get_accDefaultAction(
814 /* [optional][in] */ VARIANT varChild,
815 /* [retval][out] */ BSTR __RPC_FAR *pszDefaultAction)
817 __try {
818 *pszDefaultAction = NULL;
819 nsCOMPtr<nsIAccessible> xpAccessible;
820 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
821 if (xpAccessible) {
822 nsAutoString defaultAction;
823 if (NS_FAILED(xpAccessible->GetActionName(0, defaultAction)))
824 return E_FAIL;
826 *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(),
827 defaultAction.Length());
828 return *pszDefaultAction ? S_OK : E_OUTOFMEMORY;
831 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
832 return E_FAIL;
835 STDMETHODIMP nsAccessibleWrap::accSelect(
836 /* [in] */ long flagsSelect,
837 /* [optional][in] */ VARIANT varChild)
839 __try {
840 // currently only handle focus and selection
841 nsCOMPtr<nsIAccessible> xpAccessible;
842 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
843 NS_ENSURE_TRUE(xpAccessible, E_FAIL);
845 if (flagsSelect & (SELFLAG_TAKEFOCUS|SELFLAG_TAKESELECTION|SELFLAG_REMOVESELECTION))
847 if (flagsSelect & SELFLAG_TAKEFOCUS)
848 xpAccessible->TakeFocus();
850 if (flagsSelect & SELFLAG_TAKESELECTION)
851 xpAccessible->TakeSelection();
853 if (flagsSelect & SELFLAG_ADDSELECTION)
854 xpAccessible->SetSelected(PR_TRUE);
856 if (flagsSelect & SELFLAG_REMOVESELECTION)
857 xpAccessible->SetSelected(PR_FALSE);
859 if (flagsSelect & SELFLAG_EXTENDSELECTION)
860 xpAccessible->ExtendSelection();
862 return S_OK;
865 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
866 return E_FAIL;
869 STDMETHODIMP nsAccessibleWrap::accLocation(
870 /* [out] */ long __RPC_FAR *pxLeft,
871 /* [out] */ long __RPC_FAR *pyTop,
872 /* [out] */ long __RPC_FAR *pcxWidth,
873 /* [out] */ long __RPC_FAR *pcyHeight,
874 /* [optional][in] */ VARIANT varChild)
876 __try {
877 nsCOMPtr<nsIAccessible> xpAccessible;
878 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
880 if (xpAccessible) {
881 PRInt32 x, y, width, height;
882 if (NS_FAILED(xpAccessible->GetBounds(&x, &y, &width, &height)))
883 return E_FAIL;
885 *pxLeft = x;
886 *pyTop = y;
887 *pcxWidth = width;
888 *pcyHeight = height;
889 return S_OK;
891 } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
893 return E_FAIL;
896 STDMETHODIMP nsAccessibleWrap::accNavigate(
897 /* [in] */ long navDir,
898 /* [optional][in] */ VARIANT varStart,
899 /* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt)
901 __try {
902 nsCOMPtr<nsIAccessible> xpAccessibleStart, xpAccessibleResult;
903 GetXPAccessibleFor(varStart, getter_AddRefs(xpAccessibleStart));
904 if (!xpAccessibleStart)
905 return E_FAIL;
907 VariantInit(pvarEndUpAt);
908 PRUint32 xpRelation = 0;
910 switch(navDir) {
911 case NAVDIR_DOWN:
912 xpAccessibleStart->GetAccessibleBelow(getter_AddRefs(xpAccessibleResult));
913 break;
914 case NAVDIR_FIRSTCHILD:
915 if (!nsAccUtils::MustPrune(xpAccessibleStart))
916 xpAccessibleStart->GetFirstChild(getter_AddRefs(xpAccessibleResult));
917 break;
918 case NAVDIR_LASTCHILD:
919 if (!nsAccUtils::MustPrune(xpAccessibleStart))
920 xpAccessibleStart->GetLastChild(getter_AddRefs(xpAccessibleResult));
921 break;
922 case NAVDIR_LEFT:
923 xpAccessibleStart->GetAccessibleToLeft(getter_AddRefs(xpAccessibleResult));
924 break;
925 case NAVDIR_NEXT:
926 xpAccessibleStart->GetNextSibling(getter_AddRefs(xpAccessibleResult));
927 break;
928 case NAVDIR_PREVIOUS:
929 xpAccessibleStart->GetPreviousSibling(getter_AddRefs(xpAccessibleResult));
930 break;
931 case NAVDIR_RIGHT:
932 xpAccessibleStart->GetAccessibleToRight(getter_AddRefs(xpAccessibleResult));
933 break;
934 case NAVDIR_UP:
935 xpAccessibleStart->GetAccessibleAbove(getter_AddRefs(xpAccessibleResult));
936 break;
938 // MSAA relationship extensions to accNavigate
939 case NAVRELATION_CONTROLLED_BY:
940 xpRelation = nsIAccessibleRelation::RELATION_CONTROLLED_BY;
941 break;
942 case NAVRELATION_CONTROLLER_FOR:
943 xpRelation = nsIAccessibleRelation::RELATION_CONTROLLER_FOR;
944 break;
945 case NAVRELATION_LABEL_FOR:
946 xpRelation = nsIAccessibleRelation::RELATION_LABEL_FOR;
947 break;
948 case NAVRELATION_LABELLED_BY:
949 xpRelation = nsIAccessibleRelation::RELATION_LABELLED_BY;
950 break;
951 case NAVRELATION_MEMBER_OF:
952 xpRelation = nsIAccessibleRelation::RELATION_MEMBER_OF;
953 break;
954 case NAVRELATION_NODE_CHILD_OF:
955 xpRelation = nsIAccessibleRelation::RELATION_NODE_CHILD_OF;
956 break;
957 case NAVRELATION_FLOWS_TO:
958 xpRelation = nsIAccessibleRelation::RELATION_FLOWS_TO;
959 break;
960 case NAVRELATION_FLOWS_FROM:
961 xpRelation = nsIAccessibleRelation::RELATION_FLOWS_FROM;
962 break;
963 case NAVRELATION_SUBWINDOW_OF:
964 xpRelation = nsIAccessibleRelation::RELATION_SUBWINDOW_OF;
965 break;
966 case NAVRELATION_EMBEDS:
967 xpRelation = nsIAccessibleRelation::RELATION_EMBEDS;
968 break;
969 case NAVRELATION_EMBEDDED_BY:
970 xpRelation = nsIAccessibleRelation::RELATION_EMBEDDED_BY;
971 break;
972 case NAVRELATION_POPUP_FOR:
973 xpRelation = nsIAccessibleRelation::RELATION_POPUP_FOR;
974 break;
975 case NAVRELATION_PARENT_WINDOW_OF:
976 xpRelation = nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF;
977 break;
978 case NAVRELATION_DEFAULT_BUTTON:
979 xpRelation = nsIAccessibleRelation::RELATION_DEFAULT_BUTTON;
980 break;
981 case NAVRELATION_DESCRIBED_BY:
982 xpRelation = nsIAccessibleRelation::RELATION_DESCRIBED_BY;
983 break;
984 case NAVRELATION_DESCRIPTION_FOR:
985 xpRelation = nsIAccessibleRelation::RELATION_DESCRIPTION_FOR;
986 break;
989 pvarEndUpAt->vt = VT_EMPTY;
991 if (xpRelation) {
992 nsresult rv = GetAccessibleRelated(xpRelation,
993 getter_AddRefs(xpAccessibleResult));
994 if (rv == NS_ERROR_NOT_IMPLEMENTED) {
995 return E_NOTIMPL;
999 if (xpAccessibleResult) {
1000 pvarEndUpAt->pdispVal = NativeAccessible(xpAccessibleResult);
1001 pvarEndUpAt->vt = VT_DISPATCH;
1002 return NS_OK;
1004 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1005 return E_FAIL;
1008 STDMETHODIMP nsAccessibleWrap::accHitTest(
1009 /* [in] */ long xLeft,
1010 /* [in] */ long yTop,
1011 /* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
1013 __try {
1014 VariantInit(pvarChild);
1016 // convert to window coords
1017 nsCOMPtr<nsIAccessible> xpAccessible;
1019 xLeft = xLeft;
1020 yTop = yTop;
1022 if (nsAccUtils::MustPrune(this)) {
1023 xpAccessible = this;
1025 else {
1026 GetChildAtPoint(xLeft, yTop, getter_AddRefs(xpAccessible));
1029 // if we got a child
1030 if (xpAccessible) {
1031 // if the child is us
1032 if (xpAccessible == static_cast<nsIAccessible*>(this)) {
1033 pvarChild->vt = VT_I4;
1034 pvarChild->lVal = CHILDID_SELF;
1035 } else { // its not create an Accessible for it.
1036 pvarChild->vt = VT_DISPATCH;
1037 pvarChild->pdispVal = NativeAccessible(xpAccessible);
1038 nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(xpAccessible));
1039 NS_ASSERTION(accessNode, "Unable to QI to nsIAccessNode");
1040 nsCOMPtr<nsIDOMNode> domNode;
1041 accessNode->GetDOMNode(getter_AddRefs(domNode));
1042 if (!domNode) {
1043 // Has already been shut down
1044 pvarChild->vt = VT_EMPTY;
1045 return E_FAIL;
1048 } else {
1049 // no child at that point
1050 pvarChild->vt = VT_EMPTY;
1051 return S_FALSE;
1053 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1055 return S_OK;
1058 STDMETHODIMP nsAccessibleWrap::accDoDefaultAction(
1059 /* [optional][in] */ VARIANT varChild)
1061 __try {
1062 nsCOMPtr<nsIAccessible> xpAccessible;
1063 GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
1065 if (!xpAccessible || FAILED(xpAccessible->DoAction(0))) {
1066 return E_FAIL;
1068 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1069 return S_OK;
1072 STDMETHODIMP nsAccessibleWrap::put_accName(
1073 /* [optional][in] */ VARIANT varChild,
1074 /* [in] */ BSTR szName)
1076 return E_NOTIMPL;
1079 STDMETHODIMP nsAccessibleWrap::put_accValue(
1080 /* [optional][in] */ VARIANT varChild,
1081 /* [in] */ BSTR szValue)
1083 return E_NOTIMPL;
1086 #include "mshtml.h"
1088 ////////////////////////////////////////////////////////////////////////////////
1089 // nsAccessibleWrap. IEnumVariant
1091 STDMETHODIMP
1092 nsAccessibleWrap::Next(ULONG aNumElementsRequested, VARIANT FAR* aPVar,
1093 ULONG FAR* aNumElementsFetched)
1095 // Children already cached via QI to IEnumVARIANT
1096 __try {
1097 *aNumElementsFetched = 0;
1099 if (aNumElementsRequested <= 0 || !aPVar)
1100 return E_INVALIDARG;
1102 if (mEnumVARIANTPosition == kIEnumVariantDisconnected)
1103 return CO_E_OBJNOTCONNECTED;
1105 nsCOMPtr<nsIAccessible> traversedAcc;
1106 nsresult rv = GetChildAt(mEnumVARIANTPosition, getter_AddRefs(traversedAcc));
1107 if (!traversedAcc)
1108 return S_FALSE;
1110 for (PRUint32 i = 0; i < aNumElementsRequested; i++) {
1111 VariantInit(&aPVar[i]);
1113 aPVar[i].pdispVal = NativeAccessible(traversedAcc);
1114 aPVar[i].vt = VT_DISPATCH;
1115 (*aNumElementsFetched)++;
1117 nsCOMPtr<nsIAccessible> nextAcc;
1118 traversedAcc->GetNextSibling(getter_AddRefs(nextAcc));
1119 if (!nextAcc)
1120 break;
1122 traversedAcc = nextAcc;
1125 mEnumVARIANTPosition += *aNumElementsFetched;
1126 return (*aNumElementsFetched) < aNumElementsRequested ? S_FALSE : S_OK;
1128 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1129 return E_FAIL;
1132 STDMETHODIMP
1133 nsAccessibleWrap::Skip(ULONG aNumElements)
1135 __try {
1136 if (mEnumVARIANTPosition == kIEnumVariantDisconnected)
1137 return CO_E_OBJNOTCONNECTED;
1139 mEnumVARIANTPosition += aNumElements;
1141 PRInt32 numChildren;
1142 GetChildCount(&numChildren);
1144 if (mEnumVARIANTPosition > numChildren)
1146 mEnumVARIANTPosition = numChildren;
1147 return S_FALSE;
1149 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1150 return NOERROR;
1153 STDMETHODIMP
1154 nsAccessibleWrap::Reset(void)
1156 mEnumVARIANTPosition = 0;
1157 return NOERROR;
1160 STDMETHODIMP
1161 nsAccessibleWrap::Clone(IEnumVARIANT FAR* FAR* ppenum)
1163 __try {
1164 *ppenum = nsnull;
1166 nsCOMPtr<nsIArray> childArray;
1167 nsresult rv = GetChildren(getter_AddRefs(childArray));
1169 *ppenum = new AccessibleEnumerator(childArray);
1170 if (!*ppenum)
1171 return E_OUTOFMEMORY;
1172 NS_ADDREF(*ppenum);
1174 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1175 return NOERROR;
1178 ////////////////////////////////////////////////////////////////////////////////
1179 // nsAccessibleWrap. IAccessible2
1181 STDMETHODIMP
1182 nsAccessibleWrap::get_nRelations(long *aNRelations)
1184 __try {
1185 PRUint32 count = 0;
1186 nsresult rv = GetRelationsCount(&count);
1187 *aNRelations = count;
1189 return GetHRESULT(rv);
1191 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1192 return E_FAIL;
1195 STDMETHODIMP
1196 nsAccessibleWrap::get_relation(long aRelationIndex,
1197 IAccessibleRelation **aRelation)
1199 __try {
1200 *aRelation = NULL;
1202 nsCOMPtr<nsIAccessibleRelation> relation;
1203 nsresult rv = GetRelation(aRelationIndex, getter_AddRefs(relation));
1204 if (NS_FAILED(rv))
1205 return GetHRESULT(rv);
1207 nsCOMPtr<nsIWinAccessNode> winAccessNode(do_QueryInterface(relation));
1208 if (!winAccessNode)
1209 return E_FAIL;
1211 void *instancePtr = NULL;
1212 rv = winAccessNode->QueryNativeInterface(IID_IAccessibleRelation,
1213 &instancePtr);
1214 if (NS_FAILED(rv))
1215 return GetHRESULT(rv);
1217 *aRelation = static_cast<IAccessibleRelation*>(instancePtr);
1218 return S_OK;
1220 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1221 return E_FAIL;
1224 STDMETHODIMP
1225 nsAccessibleWrap::get_relations(long aMaxRelations,
1226 IAccessibleRelation **aRelation,
1227 long *aNRelations)
1229 __try {
1230 *aRelation = NULL;
1231 *aNRelations = 0;
1233 nsCOMPtr<nsIArray> relations;
1234 nsresult rv = GetRelations(getter_AddRefs(relations));
1235 if (NS_FAILED(rv))
1236 return GetHRESULT(rv);
1238 PRUint32 length = 0;
1239 rv = relations->GetLength(&length);
1240 if (NS_FAILED(rv))
1241 return GetHRESULT(rv);
1243 if (length == 0)
1244 return S_FALSE;
1246 PRUint32 count = length < (PRUint32)aMaxRelations ? length : aMaxRelations;
1248 PRUint32 index = 0;
1249 for (; index < count; index++) {
1250 nsCOMPtr<nsIWinAccessNode> winAccessNode =
1251 do_QueryElementAt(relations, index, &rv);
1252 if (NS_FAILED(rv))
1253 break;
1255 void *instancePtr = NULL;
1256 nsresult rv = winAccessNode->QueryNativeInterface(IID_IAccessibleRelation,
1257 &instancePtr);
1258 if (NS_FAILED(rv))
1259 break;
1261 aRelation[index] = static_cast<IAccessibleRelation*>(instancePtr);
1264 if (NS_FAILED(rv)) {
1265 for (PRUint32 index2 = 0; index2 < index; index2++) {
1266 aRelation[index2]->Release();
1267 aRelation[index2] = NULL;
1269 return GetHRESULT(rv);
1272 *aNRelations = count;
1273 return S_OK;
1275 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1276 return E_FAIL;
1279 STDMETHODIMP
1280 nsAccessibleWrap::role(long *aRole)
1282 __try {
1283 *aRole = 0;
1285 PRUint32 xpRole = 0;
1286 nsresult rv = GetFinalRole(&xpRole);
1287 if (NS_FAILED(rv))
1288 return GetHRESULT(rv);
1290 NS_ASSERTION(gWindowsRoleMap[nsIAccessibleRole::ROLE_LAST_ENTRY].ia2Role == ROLE_WINDOWS_LAST_ENTRY,
1291 "MSAA role map skewed");
1293 *aRole = gWindowsRoleMap[xpRole].ia2Role;
1294 return S_OK;
1296 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1297 return E_FAIL;
1300 STDMETHODIMP
1301 nsAccessibleWrap::scrollTo(enum IA2ScrollType aScrollType)
1303 __try {
1304 nsresult rv = ScrollTo(aScrollType);
1305 return GetHRESULT(rv);
1307 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1308 return E_FAIL;
1311 STDMETHODIMP
1312 nsAccessibleWrap::scrollToPoint(enum IA2CoordinateType aCoordType,
1313 long aX, long aY)
1315 __try {
1316 PRUint32 geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ?
1317 nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
1318 nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE;
1320 nsresult rv = ScrollToPoint(geckoCoordType, aX, aY);
1321 return GetHRESULT(rv);
1323 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1324 return E_FAIL;
1327 STDMETHODIMP
1328 nsAccessibleWrap::get_groupPosition(long *aGroupLevel,
1329 long *aSimilarItemsInGroup,
1330 long *aPositionInGroup)
1332 __try {
1333 PRInt32 groupLevel = 0;
1334 PRInt32 similarItemsInGroup = 0;
1335 PRInt32 positionInGroup = 0;
1336 nsresult rv = GroupPosition(&groupLevel, &similarItemsInGroup,
1337 &positionInGroup);
1339 *aGroupLevel = groupLevel;
1340 *aSimilarItemsInGroup = similarItemsInGroup;
1341 *aPositionInGroup = positionInGroup;
1343 if (NS_FAILED(rv))
1344 return GetHRESULT(rv);
1346 if (groupLevel ==0 && similarItemsInGroup == 0 && positionInGroup == 0)
1347 return S_FALSE;
1348 return S_OK;
1350 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1351 return E_FAIL;
1354 STDMETHODIMP
1355 nsAccessibleWrap::get_states(AccessibleStates *aStates)
1357 __try {
1358 *aStates = 0;
1360 // XXX: bug 344674 should come with better approach that we have here.
1362 PRUint32 states = 0, extraStates = 0;
1363 nsresult rv = GetState(&states, &extraStates);
1364 if (NS_FAILED(rv))
1365 return GetHRESULT(rv);
1367 if (states & nsIAccessibleStates::STATE_INVALID)
1368 *aStates |= IA2_STATE_INVALID_ENTRY;
1369 if (states & nsIAccessibleStates::STATE_REQUIRED)
1370 *aStates |= IA2_STATE_REQUIRED;
1372 // The following IA2 states are not supported by Gecko
1373 // IA2_STATE_ARMED
1374 // IA2_STATE_MANAGES_DESCENDANTS
1375 // IA2_STATE_ICONIFIED
1376 // IA2_STATE_INVALID // This is not a state, it is the absence of a state
1378 if (extraStates & nsIAccessibleStates::EXT_STATE_ACTIVE)
1379 *aStates |= IA2_STATE_ACTIVE;
1380 if (extraStates & nsIAccessibleStates::EXT_STATE_DEFUNCT)
1381 *aStates |= IA2_STATE_DEFUNCT;
1382 if (extraStates & nsIAccessibleStates::EXT_STATE_EDITABLE)
1383 *aStates |= IA2_STATE_EDITABLE;
1384 if (extraStates & nsIAccessibleStates::EXT_STATE_HORIZONTAL)
1385 *aStates |= IA2_STATE_HORIZONTAL;
1386 if (extraStates & nsIAccessibleStates::EXT_STATE_MODAL)
1387 *aStates |= IA2_STATE_MODAL;
1388 if (extraStates & nsIAccessibleStates::EXT_STATE_MULTI_LINE)
1389 *aStates |= IA2_STATE_MULTI_LINE;
1390 if (extraStates & nsIAccessibleStates::EXT_STATE_OPAQUE)
1391 *aStates |= IA2_STATE_OPAQUE;
1392 if (extraStates & nsIAccessibleStates::EXT_STATE_SELECTABLE_TEXT)
1393 *aStates |= IA2_STATE_SELECTABLE_TEXT;
1394 if (extraStates & nsIAccessibleStates::EXT_STATE_SINGLE_LINE)
1395 *aStates |= IA2_STATE_SINGLE_LINE;
1396 if (extraStates & nsIAccessibleStates::EXT_STATE_STALE)
1397 *aStates |= IA2_STATE_STALE;
1398 if (extraStates & nsIAccessibleStates::EXT_STATE_SUPPORTS_AUTOCOMPLETION)
1399 *aStates |= IA2_STATE_SUPPORTS_AUTOCOMPLETION;
1400 if (extraStates & nsIAccessibleStates::EXT_STATE_TRANSIENT)
1401 *aStates |= IA2_STATE_TRANSIENT;
1402 if (extraStates & nsIAccessibleStates::EXT_STATE_VERTICAL)
1403 *aStates |= IA2_STATE_VERTICAL;
1405 return S_OK;
1407 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1408 return E_FAIL;
1411 STDMETHODIMP
1412 nsAccessibleWrap::get_extendedRole(BSTR *aExtendedRole)
1414 __try {
1415 *aExtendedRole = NULL;
1416 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1418 return E_NOTIMPL;
1421 STDMETHODIMP
1422 nsAccessibleWrap::get_localizedExtendedRole(BSTR *aLocalizedExtendedRole)
1424 __try {
1425 *aLocalizedExtendedRole = NULL;
1426 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1428 return E_NOTIMPL;
1431 STDMETHODIMP
1432 nsAccessibleWrap::get_nExtendedStates(long *aNExtendedStates)
1434 __try {
1435 *aNExtendedStates = 0;
1436 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1438 return E_NOTIMPL;
1441 STDMETHODIMP
1442 nsAccessibleWrap::get_extendedStates(long aMaxExtendedStates,
1443 BSTR **aExtendedStates,
1444 long *aNExtendedStates)
1446 __try {
1447 *aExtendedStates = NULL;
1448 *aNExtendedStates = 0;
1449 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1451 return E_NOTIMPL;
1454 STDMETHODIMP
1455 nsAccessibleWrap::get_localizedExtendedStates(long aMaxLocalizedExtendedStates,
1456 BSTR **aLocalizedExtendedStates,
1457 long *aNLocalizedExtendedStates)
1459 __try {
1460 *aLocalizedExtendedStates = NULL;
1461 *aNLocalizedExtendedStates = 0;
1462 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1464 return E_NOTIMPL;
1467 STDMETHODIMP
1468 nsAccessibleWrap::get_uniqueID(long *uniqueID)
1470 __try {
1471 void *id = nsnull;
1472 nsresult rv = GetUniqueID(&id);
1473 if (NS_FAILED(rv))
1474 return GetHRESULT(rv);
1476 *uniqueID = - reinterpret_cast<long>(id);
1477 return S_OK;
1479 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1480 return E_FAIL;
1483 STDMETHODIMP
1484 nsAccessibleWrap::get_windowHandle(HWND *aWindowHandle)
1486 __try {
1487 *aWindowHandle = 0;
1489 if (!mDOMNode)
1490 return E_FAIL;
1492 void *handle = nsnull;
1493 nsresult rv = GetOwnerWindow(&handle);
1494 if (NS_FAILED(rv))
1495 return GetHRESULT(rv);
1497 *aWindowHandle = reinterpret_cast<HWND>(handle);
1498 return S_OK;
1500 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1501 return E_FAIL;
1504 STDMETHODIMP
1505 nsAccessibleWrap::get_indexInParent(long *aIndexInParent)
1507 __try {
1508 *aIndexInParent = -1;
1510 PRInt32 index = -1;
1511 nsresult rv = GetIndexInParent(&index);
1512 if (NS_FAILED(rv))
1513 return GetHRESULT(rv);
1515 if (index == -1)
1516 return S_FALSE;
1518 *aIndexInParent = index;
1519 return S_OK;
1521 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1522 return E_FAIL;
1525 STDMETHODIMP
1526 nsAccessibleWrap::get_locale(IA2Locale *aLocale)
1528 __try {
1529 // Language codes consist of a primary code and a possibly empty series of
1530 // subcodes: language-code = primary-code ( "-" subcode )*
1531 // Two-letter primary codes are reserved for [ISO639] language abbreviations.
1532 // Any two-letter subcode is understood to be a [ISO3166] country code.
1534 nsAutoString lang;
1535 nsresult rv = GetLanguage(lang);
1536 if (NS_FAILED(rv))
1537 return GetHRESULT(rv);
1539 // If primary code consists from two letters then expose it as language.
1540 PRInt32 offset = lang.FindChar('-', 0);
1541 if (offset == -1) {
1542 if (lang.Length() == 2) {
1543 aLocale->language = ::SysAllocString(lang.get());
1544 return S_OK;
1546 } else if (offset == 2) {
1547 aLocale->language = ::SysAllocStringLen(lang.get(), 2);
1549 // If the first subcode consists from two letters then expose it as
1550 // country.
1551 offset = lang.FindChar('-', 3);
1552 if (offset == -1) {
1553 if (lang.Length() == 5) {
1554 aLocale->country = ::SysAllocString(lang.get() + 3);
1555 return S_OK;
1557 } else if (offset == 5) {
1558 aLocale->country = ::SysAllocStringLen(lang.get() + 3, 2);
1562 // Expose as a string if primary code or subcode cannot point to language or
1563 // country abbreviations or if there are more than one subcode.
1564 aLocale->variant = ::SysAllocString(lang.get());
1565 return S_OK;
1567 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1568 return E_FAIL;
1571 STDMETHODIMP
1572 nsAccessibleWrap::get_attributes(BSTR *aAttributes)
1574 // The format is name:value;name:value; with \ for escaping these
1575 // characters ":;=,\".
1576 __try {
1577 *aAttributes = NULL;
1579 nsCOMPtr<nsIPersistentProperties> attributes;
1580 nsresult rv = GetAttributes(getter_AddRefs(attributes));
1581 if (NS_FAILED(rv))
1582 return GetHRESULT(rv);
1584 return ConvertToIA2Attributes(attributes, aAttributes);
1586 } __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
1587 return E_FAIL;
1590 // For IDispatch support
1591 STDMETHODIMP
1592 nsAccessibleWrap::GetTypeInfoCount(UINT *p)
1594 *p = 0;
1595 return E_NOTIMPL;
1598 // For IDispatch support
1599 STDMETHODIMP nsAccessibleWrap::GetTypeInfo(UINT i, LCID lcid, ITypeInfo **ppti)
1601 *ppti = 0;
1602 return E_NOTIMPL;
1605 // For IDispatch support
1606 STDMETHODIMP
1607 nsAccessibleWrap::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames,
1608 UINT cNames, LCID lcid, DISPID *rgDispId)
1610 return E_NOTIMPL;
1613 // For IDispatch support
1614 STDMETHODIMP nsAccessibleWrap::Invoke(DISPID dispIdMember, REFIID riid,
1615 LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
1616 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1618 return E_NOTIMPL;
1622 NS_IMETHODIMP nsAccessibleWrap::GetNativeInterface(void **aOutAccessible)
1624 *aOutAccessible = static_cast<IAccessible*>(this);
1625 NS_ADDREF_THIS();
1626 return NS_OK;
1629 // nsPIAccessible
1631 NS_IMETHODIMP
1632 nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent)
1634 NS_ENSURE_ARG(aEvent);
1636 nsresult rv = nsAccessible::FireAccessibleEvent(aEvent);
1637 NS_ENSURE_SUCCESS(rv, rv);
1639 return FirePlatformEvent(aEvent);
1642 nsresult
1643 nsAccessibleWrap::FirePlatformEvent(nsIAccessibleEvent *aEvent)
1645 PRUint32 eventType = 0;
1646 aEvent->GetEventType(&eventType);
1648 NS_ENSURE_TRUE(eventType > 0 &&
1649 eventType < nsIAccessibleEvent::EVENT_LAST_ENTRY,
1650 NS_ERROR_FAILURE);
1652 PRUint32 winLastEntry = gWinEventMap[nsIAccessibleEvent::EVENT_LAST_ENTRY];
1653 NS_ASSERTION(winLastEntry == kEVENT_LAST_ENTRY,
1654 "MSAA event map skewed");
1656 PRUint32 winEvent = gWinEventMap[eventType];
1657 if (!winEvent)
1658 return NS_OK;
1660 // Means we're not active.
1661 NS_ENSURE_TRUE(mWeakShell, NS_ERROR_FAILURE);
1663 nsCOMPtr<nsIAccessible> accessible;
1664 aEvent->GetAccessible(getter_AddRefs(accessible));
1665 if (!accessible)
1666 return NS_OK;
1668 if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED ||
1669 eventType == nsIAccessibleEvent::EVENT_FOCUS) {
1670 UpdateSystemCaret();
1673 PRInt32 childID = GetChildIDFor(accessible); // get the id for the accessible
1674 if (!childID)
1675 return NS_OK; // Can't fire an event without a child ID
1677 // See if we're in a scrollable area with its own window
1678 nsCOMPtr<nsIAccessible> newAccessible;
1679 if (eventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
1680 eventType == nsIAccessibleEvent::EVENT_DOM_DESTROY) {
1681 // Don't use frame from current accessible when we're hiding that
1682 // accessible.
1683 accessible->GetParent(getter_AddRefs(newAccessible));
1684 } else {
1685 newAccessible = accessible;
1688 HWND hWnd = GetHWNDFor(newAccessible);
1689 NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE);
1691 // Gecko uses two windows for every scrollable area. One window contains
1692 // scrollbars and the child window contains only the client area.
1693 // Details of the 2 window system:
1694 // * Scrollbar window: caret drawing window & return value for WindowFromAccessibleObject()
1695 // * Client area window: text drawing window & MSAA event window
1697 // Fire MSAA event for client area window.
1698 NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
1700 // If the accessible children are changed then drop the IEnumVariant current
1701 // position of the accessible.
1702 if (eventType == nsIAccessibleEvent::EVENT_REORDER)
1703 UnattachIEnumVariant();
1705 return NS_OK;
1708 //------- Helper methods ---------
1710 PRInt32 nsAccessibleWrap::GetChildIDFor(nsIAccessible* aAccessible)
1712 // A child ID of the window is required, when we use NotifyWinEvent,
1713 // so that the 3rd party application can call back and get the IAccessible
1714 // the event occured on.
1716 void *uniqueID = nsnull;
1717 nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
1718 if (!accessNode) {
1719 return 0;
1721 accessNode->GetUniqueID(&uniqueID);
1723 // Yes, this means we're only compatibible with 32 bit
1724 // MSAA is only available for 32 bit windows, so it's okay
1725 return - NS_PTR_TO_INT32(uniqueID);
1728 HWND
1729 nsAccessibleWrap::GetHWNDFor(nsIAccessible *aAccessible)
1731 nsRefPtr<nsAccessNode> accessNode = nsAccUtils::QueryAccessNode(aAccessible);
1732 if (!accessNode)
1733 return 0;
1735 HWND hWnd = 0;
1736 nsIFrame *frame = accessNode->GetFrame();
1737 if (frame) {
1738 nsIWidget *window = frame->GetWindow();
1739 PRBool isVisible;
1740 window->IsVisible(isVisible);
1741 if (isVisible) {
1742 // Short explanation:
1743 // If HWND for frame is inside a hidden window, fire the event on the
1744 // containing document's visible window.
1746 // Long explanation:
1747 // This is really just to fix combo boxes with JAWS. Window-Eyes already
1748 // worked with combo boxes because they use the value change event in
1749 // the closed combo box case. JAWS will only pay attention to the focus
1750 // events on the list items. The JAWS developers haven't fixed that, so
1751 // we'll use the focus events to make JAWS work. However, JAWS is
1752 // ignoring events on a hidden window. So, in order to fix the bug where
1753 // JAWS doesn't echo the current option as it changes in a closed
1754 // combo box, we need to use an ensure that we never fire an event with
1755 // an HWND for a hidden window.
1756 hWnd = (HWND)frame->GetWindow()->GetNativeData(NS_NATIVE_WINDOW);
1760 if (!hWnd) {
1761 void* handle = nsnull;
1762 nsCOMPtr<nsIAccessibleDocument> accessibleDoc;
1763 accessNode->GetAccessibleDocument(getter_AddRefs(accessibleDoc));
1764 if (!accessibleDoc)
1765 return 0;
1767 accessibleDoc->GetWindowHandle(&handle);
1768 hWnd = (HWND)handle;
1771 return hWnd;
1774 HRESULT
1775 nsAccessibleWrap::ConvertToIA2Attributes(nsIPersistentProperties *aAttributes,
1776 BSTR *aIA2Attributes)
1778 *aIA2Attributes = NULL;
1780 // The format is name:value;name:value; with \ for escaping these
1781 // characters ":;=,\".
1783 if (!aAttributes)
1784 return S_FALSE;
1786 nsCOMPtr<nsISimpleEnumerator> propEnum;
1787 aAttributes->Enumerate(getter_AddRefs(propEnum));
1788 if (!propEnum)
1789 return E_FAIL;
1791 nsAutoString strAttrs;
1793 const char kCharsToEscape[] = ":;=,\\";
1795 PRBool hasMore = PR_FALSE;
1796 while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
1797 nsCOMPtr<nsISupports> propSupports;
1798 propEnum->GetNext(getter_AddRefs(propSupports));
1800 nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(propSupports));
1801 if (!propElem)
1802 return E_FAIL;
1804 nsCAutoString name;
1805 if (NS_FAILED(propElem->GetKey(name)))
1806 return E_FAIL;
1808 PRUint32 offset = 0;
1809 while ((offset = name.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
1810 name.Insert('\\', offset);
1811 offset += 2;
1814 nsAutoString value;
1815 if (NS_FAILED(propElem->GetValue(value)))
1816 return E_FAIL;
1818 offset = 0;
1819 while ((offset = value.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
1820 value.Insert('\\', offset);
1821 offset += 2;
1824 AppendUTF8toUTF16(name, strAttrs);
1825 strAttrs.Append(':');
1826 strAttrs.Append(value);
1827 strAttrs.Append(';');
1830 if (strAttrs.IsEmpty())
1831 return S_FALSE;
1833 *aIA2Attributes = ::SysAllocStringLen(strAttrs.get(), strAttrs.Length());
1834 return *aIA2Attributes ? S_OK : E_OUTOFMEMORY;
1837 IDispatch *nsAccessibleWrap::NativeAccessible(nsIAccessible *aXPAccessible)
1839 if (!aXPAccessible) {
1840 NS_WARNING("Not passing in an aXPAccessible");
1841 return NULL;
1844 nsCOMPtr<nsIAccessibleWin32Object> accObject(do_QueryInterface(aXPAccessible));
1845 if (accObject) {
1846 void* hwnd = nsnull;
1847 accObject->GetHwnd(&hwnd);
1848 if (hwnd) {
1849 IDispatch *retval = nsnull;
1850 AccessibleObjectFromWindow(reinterpret_cast<HWND>(hwnd),
1851 OBJID_WINDOW, IID_IAccessible, (void **) &retval);
1852 return retval;
1856 IAccessible *msaaAccessible;
1857 aXPAccessible->GetNativeInterface((void**)&msaaAccessible);
1859 return static_cast<IDispatch*>(msaaAccessible);
1862 void
1863 nsAccessibleWrap::UnattachIEnumVariant()
1865 if (mEnumVARIANTPosition > 0)
1866 mEnumVARIANTPosition = kIEnumVariantDisconnected;
1869 void nsAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild, nsIAccessible **aXPAccessible)
1871 *aXPAccessible = nsnull;
1872 if (!mWeakShell)
1873 return; // Fail, we don't want to do anything after we've shut down
1875 // if its us real easy - this seems to always be the case
1876 if (aVarChild.lVal == CHILDID_SELF) {
1877 *aXPAccessible = static_cast<nsIAccessible*>(this);
1879 else if (nsAccUtils::MustPrune(this)) {
1880 return;
1882 else {
1883 // XXX It is rare to use a VARIANT with a child num
1884 // so optimizing this is not a priority
1885 // We can come back it do it later, if there are perf problems
1886 // with a specific assistive technology
1887 nsCOMPtr<nsIAccessible> xpAccessible, nextAccessible;
1888 GetFirstChild(getter_AddRefs(xpAccessible));
1889 for (PRInt32 index = 0; xpAccessible; index ++) {
1890 if (!xpAccessible)
1891 break; // Failed
1892 if (aVarChild.lVal == index) {
1893 *aXPAccessible = xpAccessible;
1894 break;
1896 nextAccessible = xpAccessible;
1897 nextAccessible->GetNextSibling(getter_AddRefs(xpAccessible));
1900 NS_IF_ADDREF(*aXPAccessible);
1903 void nsAccessibleWrap::UpdateSystemCaret()
1905 // Move the system caret so that Windows Tablet Edition and tradional ATs with
1906 // off-screen model can follow the caret
1907 ::DestroyCaret();
1909 nsRefPtr<nsRootAccessible> rootAccessible = GetRootAccessible();
1910 if (!rootAccessible) {
1911 return;
1914 nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
1915 if (!caretAccessible) {
1916 return;
1919 nsIWidget *widget;
1920 nsRect caretRect = caretAccessible->GetCaretRect(&widget);
1921 HWND caretWnd;
1922 if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) {
1923 return;
1926 // Create invisible bitmap for caret, otherwise its appearance interferes
1927 // with Gecko caret
1928 HBITMAP caretBitMap = CreateBitmap(1, caretRect.height, 1, 1, NULL);
1929 if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) { // Also destroys the last caret
1930 ::ShowCaret(caretWnd);
1931 RECT windowRect;
1932 ::GetWindowRect(caretWnd, &windowRect);
1933 ::SetCaretPos(caretRect.x - windowRect.left, caretRect.y - windowRect.top);
1934 ::DeleteObject(caretBitMap);