Back to index

lightning-sunbird  0.9+nobinonly
nsAccessibleWrap.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2003
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Original Author: Aaron Leventhal (aaronl@netscape.com)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsAccessibleWrap.h"
00040 #include "nsAccessibilityAtoms.h"
00041 #include "nsIAccessibleSelectable.h"
00042 #include "nsIAccessibleWin32Object.h"
00043 #include "nsArray.h"
00044 #include "nsIDOMDocument.h"
00045 #include "nsIFrame.h"
00046 #include "nsIScrollableFrame.h"
00047 #include "nsIScrollableView.h"
00048 #include "nsINameSpaceManager.h"
00049 #include "nsINodeInfo.h"
00050 #include "nsIPrefService.h"
00051 #include "nsIServiceManager.h"
00052 #include "nsTextFormatter.h"
00053 #include "nsIView.h"
00054 
00055 // for the COM IEnumVARIANT solution in get_AccSelection()
00056 #define _ATLBASE_IMPL
00057 #include <atlbase.h>
00058 extern CComModule _Module;
00059 #define _ATLCOM_IMPL
00060 #include <atlcom.h>
00061 
00062 /* For documentation of the accessibility architecture, 
00063  * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
00064  */
00065 
00066 //#define DEBUG_LEAKS
00067 
00068 #ifdef DEBUG_LEAKS
00069 static gAccessibles = 0;
00070 #endif
00071 
00072 CComModule _Module;
00073   
00074 EXTERN_C GUID CDECL CLSID_Accessible =
00075 { 0x61044601, 0xa811, 0x4e2b, { 0xbb, 0xba, 0x17, 0xbf, 0xab, 0xd3, 0x29, 0xd7 } };
00076 
00077 /*
00078  * Class nsAccessibleWrap
00079  */
00080 
00081 //-----------------------------------------------------
00082 // construction 
00083 //-----------------------------------------------------
00084 nsAccessibleWrap::nsAccessibleWrap(nsIDOMNode* aNode, nsIWeakReference *aShell): 
00085   nsAccessible(aNode, aShell), mEnumVARIANTPosition(0)
00086 {
00087 }
00088 
00089 //-----------------------------------------------------
00090 // destruction
00091 //-----------------------------------------------------
00092 nsAccessibleWrap::~nsAccessibleWrap()
00093 {
00094 }
00095 
00096 //-----------------------------------------------------
00097 // IUnknown interface methods - see iunknown.h for documentation
00098 //-----------------------------------------------------
00099 STDMETHODIMP_(ULONG) nsAccessibleWrap::AddRef()
00100 {
00101   return nsAccessNode::AddRef();
00102 }
00103 
00104 STDMETHODIMP_(ULONG) nsAccessibleWrap::Release()
00105 {
00106   return nsAccessNode::Release();
00107 }
00108 
00109 // Microsoft COM QueryInterface
00110 STDMETHODIMP nsAccessibleWrap::QueryInterface(REFIID iid, void** ppv)
00111 {
00112   *ppv = NULL;
00113 
00114   if (IID_IUnknown == iid || IID_IDispatch == iid || IID_IAccessible == iid)
00115     *ppv = NS_STATIC_CAST(IAccessible*, this);
00116   else if (IID_IEnumVARIANT == iid && !gIsEnumVariantSupportDisabled) {
00117     PRInt32 numChildren;
00118     GetChildCount(&numChildren);
00119     if (numChildren > 0)  // Don't support this interface for leaf elements
00120       *ppv = NS_STATIC_CAST(IEnumVARIANT*, this);
00121   }
00122   else if (IID_IServiceProvider == iid) {
00123     *ppv = NS_STATIC_CAST(IServiceProvider*, this);
00124   }
00125 
00126   if (NULL == *ppv)
00127     return nsAccessNodeWrap::QueryInterface(iid, ppv);
00128     
00129   (NS_REINTERPRET_CAST(IUnknown*, *ppv))->AddRef();
00130   return S_OK;
00131 }
00132 
00133 //-----------------------------------------------------
00134 // IAccessible methods
00135 //-----------------------------------------------------
00136 
00137 
00138 STDMETHODIMP nsAccessibleWrap::AccessibleObjectFromWindow(HWND hwnd,
00139                                                           DWORD dwObjectID,
00140                                                           REFIID riid,
00141                                                           void **ppvObject)
00142 {
00143   // open the dll dynamically
00144   if (!gmAccLib) 
00145     gmAccLib =::LoadLibrary("OLEACC.DLL");  
00146 
00147   if (gmAccLib) {
00148     if (!gmAccessibleObjectFromWindow)
00149       gmAccessibleObjectFromWindow = (LPFNACCESSIBLEOBJECTFROMWINDOW)GetProcAddress(gmAccLib,"AccessibleObjectFromWindow");
00150 
00151     if (gmAccessibleObjectFromWindow)
00152       return gmAccessibleObjectFromWindow(hwnd, dwObjectID, riid, ppvObject);
00153   }
00154 
00155   return E_FAIL;
00156 }
00157 
00158 STDMETHODIMP nsAccessibleWrap::NotifyWinEvent(DWORD event,
00159                                               HWND hwnd,
00160                                               LONG idObjectType,
00161                                               LONG idObject)
00162 {
00163   if (gmNotifyWinEvent)
00164     return gmNotifyWinEvent(event, hwnd, idObjectType, idObject);
00165     
00166   return E_FAIL;
00167 }
00168 
00169 STDMETHODIMP nsAccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
00170 {
00171   *ppdispParent = NULL;
00172   if (!mWeakShell)
00173     return E_FAIL;  // We've been shut down
00174 
00175   nsIFrame *frame = GetFrame();
00176   HWND hwnd = 0;
00177   if (frame) {
00178     nsIView *view = frame->GetViewExternal();
00179     if (view) {
00180       // This code is essentially our implementation of WindowFromAccessibleObject,
00181       // because MSAA iterates get_accParent() until it sees an object of ROLE_WINDOW
00182       // to know where the window for a given accessible is. We must expose the native 
00183       // window accessible that MSAA creates for us. This must be done for the document
00184       // object as well as any layout that creates its own window (e.g. via overflow: scroll)
00185       nsIWidget *widget = view->GetWidget();
00186       if (widget) {
00187         hwnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW);
00188         NS_ASSERTION(hwnd, "No window handle for window");
00189         nsIView *rootView;
00190         view->GetViewManager()->GetRootView(rootView);
00191         if (rootView == view) {
00192           // If the current object has a widget but was created by an
00193           // outer object with its own outer window, then
00194           // we want the native accessible for that outer window
00195           hwnd = ::GetParent(hwnd);
00196           NS_ASSERTION(hwnd, "No window handle for window");
00197         }
00198       }
00199       else {
00200         // If a frame is a scrollable frame, then it has one window for the client area,
00201         // not an extra parent window for just the scrollbars
00202         nsIScrollableFrame *scrollFrame = nsnull;
00203         CallQueryInterface(frame, &scrollFrame);
00204         if (scrollFrame) {
00205           hwnd = (HWND)scrollFrame->GetScrolledFrame()->GetWindow()->GetNativeData(NS_NATIVE_WINDOW);
00206           NS_ASSERTION(hwnd, "No window handle for window");
00207         }
00208       }
00209     }
00210 
00211     if (hwnd && SUCCEEDED(AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible,
00212                                               (void**)ppdispParent))) {
00213       return S_OK;
00214     }
00215   }
00216 
00217   nsCOMPtr<nsIAccessible> xpParentAccessible;
00218   GetParent(getter_AddRefs(xpParentAccessible));
00219   NS_ASSERTION(xpParentAccessible, "No parent accessible where we're not direct child of window");
00220   if (!xpParentAccessible) {
00221     return E_UNEXPECTED;
00222   }
00223   *ppdispParent = NativeAccessible(xpParentAccessible);
00224 
00225   return S_OK;
00226 }
00227 
00228 STDMETHODIMP nsAccessibleWrap::get_accChildCount( long __RPC_FAR *pcountChildren)
00229 {
00230   PRInt32 numChildren;
00231   GetChildCount(&numChildren);
00232   *pcountChildren = numChildren;
00233 
00234   return S_OK;
00235 }
00236 
00237 STDMETHODIMP nsAccessibleWrap::get_accChild( 
00238       /* [in] */ VARIANT varChild,
00239       /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild)
00240 {
00241   *ppdispChild = NULL;
00242 
00243   if (!mWeakShell || varChild.vt != VT_I4)
00244     return E_FAIL;
00245 
00246   if (varChild.lVal == CHILDID_SELF) {
00247     *ppdispChild = NS_STATIC_CAST(IDispatch*, this);
00248     AddRef();
00249     return S_OK;
00250   }
00251 
00252   nsCOMPtr<nsIAccessible> childAccessible;
00253   GetChildAt(varChild.lVal - 1, getter_AddRefs(childAccessible));
00254   if (childAccessible) {
00255     *ppdispChild = NativeAccessible(childAccessible);
00256   }
00257 
00258   return (*ppdispChild)? S_OK: E_FAIL;
00259 }
00260 
00261 STDMETHODIMP nsAccessibleWrap::get_accName( 
00262       /* [optional][in] */ VARIANT varChild,
00263       /* [retval][out] */ BSTR __RPC_FAR *pszName)
00264 {
00265   *pszName = NULL;
00266   nsCOMPtr<nsIAccessible> xpAccessible;
00267   GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
00268   if (xpAccessible) {
00269     nsAutoString name;
00270     if (NS_FAILED(xpAccessible->GetName(name)))
00271       return S_FALSE;
00272     if (!name.IsVoid()) {
00273       *pszName = ::SysAllocString(name.get());
00274     }
00275     NS_ASSERTION(mIsInitialized, "Access node was not initialized");
00276   }
00277 
00278   return S_OK;
00279 }
00280 
00281 
00282 STDMETHODIMP nsAccessibleWrap::get_accValue( 
00283       /* [optional][in] */ VARIANT varChild,
00284       /* [retval][out] */ BSTR __RPC_FAR *pszValue)
00285 {
00286   *pszValue = NULL;
00287   nsCOMPtr<nsIAccessible> xpAccessible;
00288   GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
00289   if (xpAccessible) {
00290     nsAutoString value;
00291     if (NS_FAILED(xpAccessible->GetFinalValue(value)))
00292       return S_FALSE;
00293 
00294     *pszValue = ::SysAllocString(value.get());
00295   }
00296 
00297   return S_OK;
00298 }
00299 
00300 NS_IMETHODIMP nsAccessibleWrap::GetDescription(nsAString& aDescription)
00301 {
00302   // For items that are a choice in a list of choices, 
00303   // use MSAA description field to shoehorn positional info, it's becoming
00304   // a defacto standard use for the field.
00305   // Tree items override this, because they provide the current level as well
00306 
00307   aDescription.Truncate();
00308   PRUint32 currentRole;
00309   nsresult rv = GetFinalRole(&currentRole);
00310   if (NS_FAILED(rv) ||
00311       (currentRole != ROLE_LISTITEM && currentRole != ROLE_MENUITEM &&
00312        currentRole != ROLE_RADIOBUTTON && currentRole != ROLE_PAGETAB &&
00313        currentRole != ROLE_OUTLINEITEM)) {
00314     nsAutoString description;
00315     nsAccessible::GetDescription(description);
00316     if (!description.IsEmpty()) {
00317       // Signal to screen readers that this description is speakable
00318       // and is not a formatted positional information description
00319       // Don't localize the "Description: " part of this string, it will be 
00320       // parsed out by assistive technologies.
00321       aDescription = NS_LITERAL_STRING("Description: ") + description;
00322     }
00323     return NS_OK;
00324   }
00325   
00326   nsCOMPtr<nsIAccessible> parent;
00327   GetParent(getter_AddRefs(parent));
00328   if (!parent) {
00329     return NS_ERROR_FAILURE;
00330   }
00331   
00332   PRInt32 indexInParent = 0, numSiblings = 0;
00333   
00334   nsCOMPtr<nsIAccessible> sibling, nextSibling;
00335   parent->GetFirstChild(getter_AddRefs(sibling));
00336   NS_ENSURE_TRUE(sibling, NS_ERROR_FAILURE);
00337   
00338   PRBool foundCurrent = PR_FALSE;
00339   PRUint32 siblingRole;
00340   while (sibling) {
00341     sibling->GetFinalRole(&siblingRole);
00342     if (siblingRole == currentRole) {
00343       ++ numSiblings;
00344       if (!foundCurrent) {
00345         ++ indexInParent;
00346         if (sibling == this) {
00347           foundCurrent = PR_TRUE;        
00348         }
00349       }
00350     }
00351     sibling->GetNextSibling(getter_AddRefs(nextSibling));
00352     sibling = nextSibling;
00353   }
00354   
00355   // Don't localize the string "of" -- that's just the format of this string.
00356   // The AT will parse the relevant numbers out and add its own localization.
00357   if (currentRole == ROLE_OUTLINEITEM) {
00358     PRUint32 level = 1;
00359     nsCOMPtr<nsIAccessible> nextParent;
00360     while (parent) {
00361       parent->GetFinalRole(&currentRole);
00362       if (currentRole != ROLE_GROUPING) {
00363         break;
00364       }
00365       ++level;
00366       parent->GetParent(getter_AddRefs(nextParent));
00367       parent.swap(nextParent);
00368     }
00369 
00370     // Count the number of tree item children
00371     PRInt32 numChildren = 0;
00372     nsCOMPtr<nsIAccessible> groupSibling;
00373     GetNextSibling(getter_AddRefs(groupSibling));
00374     if (groupSibling) {
00375       groupSibling->GetFinalRole(&currentRole);
00376       if (currentRole == ROLE_GROUPING) {
00377         // Accessible that groups child tree items
00378         nsCOMPtr<nsIAccessible> child;
00379         groupSibling->GetFirstChild(getter_AddRefs(child));
00380         while (child) {
00381           child->GetFinalRole(&currentRole);
00382           numChildren += (currentRole == ROLE_OUTLINEITEM);
00383           nsCOMPtr<nsIAccessible> nextChild;
00384           child->GetNextSibling(getter_AddRefs(nextChild));
00385           child.swap(nextChild);
00386         }
00387       }
00388     }
00389 
00390     // This must be a DHTML tree item -- XUL tree items impl GetDescription()
00391     nsTextFormatter::ssprintf(aDescription,
00392                               NS_LITERAL_STRING("L%d, %d of %d with %d").get(),
00393                               level, indexInParent, numSiblings, numChildren);
00394   }
00395   else {
00396     nsTextFormatter::ssprintf(aDescription, NS_LITERAL_STRING("%d of %d").get(),
00397                               indexInParent, numSiblings);
00398   }
00399   return NS_OK;
00400 }
00401 
00402 STDMETHODIMP nsAccessibleWrap::get_accDescription( 
00403       /* [optional][in] */ VARIANT varChild,
00404       /* [retval][out] */ BSTR __RPC_FAR *pszDescription)
00405 {
00406   *pszDescription = NULL;
00407   nsCOMPtr<nsIAccessible> xpAccessible;
00408   GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
00409   if (xpAccessible) {
00410      nsAutoString description;
00411      if (NS_FAILED(xpAccessible->GetDescription(description)))
00412        return S_FALSE;
00413 
00414      *pszDescription = ::SysAllocString(description.get());
00415   }
00416 
00417   return S_OK;
00418 }
00419 
00420 STDMETHODIMP nsAccessibleWrap::get_accRole( 
00421       /* [optional][in] */ VARIANT varChild,
00422       /* [retval][out] */ VARIANT __RPC_FAR *pvarRole)
00423 {
00424   VariantInit(pvarRole);
00425 
00426   nsCOMPtr<nsIAccessible> xpAccessible;
00427   GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
00428 
00429   if (!xpAccessible)
00430     return E_FAIL;
00431 
00432   PRUint32 role = 0;
00433   if (NS_FAILED(xpAccessible->GetFinalRole(&role)))
00434     return E_FAIL;
00435 
00436   // -- Try enumerated role
00437   if (role != ROLE_NOTHING && role != ROLE_CLIENT) {
00438     pvarRole->vt = VT_I4;
00439     pvarRole->lVal = role;  // Normal enumerated role
00440     return S_OK;
00441   }
00442 
00443   // -- Try BSTR role
00444   // Could not map to known enumerated MSAA role like ROLE_BUTTON
00445   // Use BSTR role to expose role attribute or tag name + namespace
00446   nsCOMPtr<nsIDOMNode> domNode;
00447   nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(xpAccessible));
00448   NS_ASSERTION(accessNode, "No accessnode for accessible");
00449   accessNode->GetDOMNode(getter_AddRefs(domNode));  
00450   nsIContent *content = GetRoleContent(domNode);
00451   NS_ASSERTION(content, "No content for accessible");
00452   if (content) {
00453     nsAutoString roleString;
00454     if (role != ROLE_CLIENT && GetRoleAttribute(content, roleString)) {
00455       content->GetAttr(kNameSpaceID_XHTML2_Unofficial, nsAccessibilityAtoms::role, roleString);
00456     }
00457     if (roleString.IsEmpty()) {
00458       nsINodeInfo *nodeInfo = content->GetNodeInfo();
00459       if (nodeInfo) {
00460         nodeInfo->GetName(roleString);
00461         nsAutoString nameSpaceURI;
00462         nodeInfo->GetNamespaceURI(nameSpaceURI);
00463         if (!nameSpaceURI.IsEmpty()) {
00464           // Only append name space if different from that of current document
00465           roleString += NS_LITERAL_STRING(", ") + nameSpaceURI;
00466         }
00467         if (!roleString.IsEmpty()) {
00468           pvarRole->vt = VT_BSTR;
00469           pvarRole->bstrVal = ::SysAllocString(roleString.get());
00470           return S_OK;
00471         }
00472       }
00473     }
00474   }
00475   return E_FAIL;
00476 }
00477 
00478 STDMETHODIMP nsAccessibleWrap::get_accState( 
00479       /* [optional][in] */ VARIANT varChild,
00480       /* [retval][out] */ VARIANT __RPC_FAR *pvarState)
00481 {
00482   VariantInit(pvarState);
00483   pvarState->vt = VT_I4;
00484   pvarState->lVal = 0;
00485 
00486   nsCOMPtr<nsIAccessible> xpAccessible;
00487   GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
00488   if (!xpAccessible)
00489     return E_FAIL;
00490 
00491   PRUint32 state;
00492   if (NS_FAILED(xpAccessible->GetFinalState(&state)))
00493     return E_FAIL;
00494 
00495   pvarState->lVal = state;
00496 
00497   return S_OK;
00498 }
00499 
00500 
00501 STDMETHODIMP nsAccessibleWrap::get_accHelp( 
00502       /* [optional][in] */ VARIANT varChild,
00503       /* [retval][out] */ BSTR __RPC_FAR *pszHelp)
00504 {
00505   *pszHelp = NULL;
00506   return S_FALSE;
00507 }
00508 
00509 STDMETHODIMP nsAccessibleWrap::get_accHelpTopic( 
00510       /* [out] */ BSTR __RPC_FAR *pszHelpFile,
00511       /* [optional][in] */ VARIANT varChild,
00512       /* [retval][out] */ long __RPC_FAR *pidTopic)
00513 {
00514   *pszHelpFile = NULL;
00515   *pidTopic = 0;
00516   return E_NOTIMPL;
00517 }
00518 
00519 STDMETHODIMP nsAccessibleWrap::get_accKeyboardShortcut( 
00520       /* [optional][in] */ VARIANT varChild,
00521       /* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut)
00522 {
00523   *pszKeyboardShortcut = NULL;
00524   nsCOMPtr<nsIAccessible> xpAccessible;
00525   GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
00526   if (xpAccessible) {
00527     nsAutoString shortcut;
00528     nsresult rv = xpAccessible->GetKeyboardShortcut(shortcut);
00529     if (NS_FAILED(rv))
00530       return S_FALSE;
00531 
00532     *pszKeyboardShortcut = ::SysAllocString(shortcut.get());
00533     return S_OK;
00534   }
00535   return S_FALSE;
00536 }
00537 
00538 STDMETHODIMP nsAccessibleWrap::get_accFocus( 
00539       /* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
00540 {
00541   // VT_EMPTY:    None. This object does not have the keyboard focus itself
00542   //              and does not contain a child that has the keyboard focus.
00543   // VT_I4:       lVal is CHILDID_SELF. The object itself has the keyboard focus.
00544   // VT_I4:       lVal contains the child ID of the child element with the keyboard focus.
00545   // VT_DISPATCH: pdispVal member is the address of the IDispatch interface
00546   //              for the child object with the keyboard focus.
00547 
00548   if (!mDOMNode) {
00549     return E_FAIL; // This node is shut down
00550   }
00551 
00552   VariantInit(pvarChild);
00553 
00554   // Return the current IAccessible child that has focus
00555   nsCOMPtr<nsIAccessible> focusedAccessible;
00556   GetFocusedChild(getter_AddRefs(focusedAccessible));
00557   if (focusedAccessible == this) {
00558     pvarChild->vt = VT_I4;
00559     pvarChild->lVal = CHILDID_SELF;
00560   }
00561   else if (focusedAccessible) {
00562     pvarChild->vt = VT_DISPATCH;
00563     pvarChild->pdispVal = NativeAccessible(focusedAccessible);
00564   }
00565   else {
00566     pvarChild->vt = VT_EMPTY;   // No focus or focus is not a child
00567   }
00568 
00569   return S_OK;
00570 }
00571 
00596 STDMETHODIMP nsAccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren)
00597 {
00598   typedef VARIANT                      ItemType;              /* type of the object to be stored in container */
00599   typedef ItemType                     EnumeratorExposedType; /* the type of the item exposed by the enumerator interface */
00600   typedef IEnumVARIANT                 EnumeratorInterface;   /* a COM enumerator ( IEnumXXXXX ) interface */
00601   typedef _Copy<EnumeratorExposedType> EnumeratorCopyPolicy;  /* Copy policy class */
00602   typedef CComEnum<EnumeratorInterface,
00603                    &__uuidof(EnumeratorInterface),
00604                    EnumeratorExposedType,
00605                    EnumeratorCopyPolicy > EnumeratorType;
00606 
00607   IEnumVARIANT* pUnk = NULL;
00608   CComObject<EnumeratorType>* pEnum = NULL;
00609   VariantInit(pvarChildren);
00610   pvarChildren->vt = VT_EMPTY;
00611 
00612   nsCOMPtr<nsIAccessibleSelectable> select;
00613   nsAccessNode::QueryInterface(NS_GET_IID(nsIAccessibleSelectable), getter_AddRefs(select));
00614 
00615   if (select) {  // do we have an nsIAccessibleSelectable?
00616     // we have an accessible that can have children selected
00617     nsCOMPtr<nsIArray> selectedOptions;
00618     // gets the selected options as nsIAccessibles.
00619     select->GetSelectedChildren(getter_AddRefs(selectedOptions));
00620     if (selectedOptions) { // false if the select has no children or none are selected
00621       PRUint32 length;
00622       selectedOptions->GetLength(&length);
00623       CComVariant* optionArray = new CComVariant[length]; // needs to be a CComVariant to go into the EnumeratorType object
00624 
00625       // 1) Populate an array to store in the enumeration
00626       for (PRUint32 i = 0 ; i < length ; i++) {
00627         nsCOMPtr<nsIAccessible> tempAccess;
00628         selectedOptions->QueryElementAt(i, NS_GET_IID(nsIAccessible), 
00629                                         getter_AddRefs(tempAccess));
00630         if (tempAccess) {
00631           optionArray[i] = NativeAccessible(tempAccess);
00632         }
00633       }
00634 
00635       // 2) Create and initialize the enumeration
00636       HRESULT hr = CComObject<EnumeratorType>::CreateInstance(&pEnum);
00637       pEnum->Init(&optionArray[0], &optionArray[length], NULL, AtlFlagCopy);
00638       pEnum->QueryInterface(IID_IEnumVARIANT, reinterpret_cast<void**>(&pUnk));
00639       delete [] optionArray; // clean up, the Init call copies the data (AtlFlagCopy)
00640 
00641       // 3) Put the enumerator in the VARIANT
00642       if (!pUnk)
00643         return NS_ERROR_FAILURE;
00644       pvarChildren->vt = VT_UNKNOWN;    // this must be VT_UNKNOWN for an IEnumVARIANT
00645       pvarChildren->punkVal = pUnk;
00646     }
00647   }
00648   return S_OK;
00649 }
00650 
00651 STDMETHODIMP nsAccessibleWrap::get_accDefaultAction( 
00652       /* [optional][in] */ VARIANT varChild,
00653       /* [retval][out] */ BSTR __RPC_FAR *pszDefaultAction)
00654 {
00655   *pszDefaultAction = NULL;
00656   nsCOMPtr<nsIAccessible> xpAccessible;
00657   GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
00658   if (xpAccessible) {
00659     nsAutoString defaultAction;
00660     if (NS_FAILED(xpAccessible->GetActionName(0, defaultAction)))
00661       return S_FALSE;
00662 
00663     *pszDefaultAction = ::SysAllocString(defaultAction.get());
00664   }
00665 
00666   return S_OK;
00667 }
00668 
00669 STDMETHODIMP nsAccessibleWrap::accSelect( 
00670       /* [in] */ long flagsSelect,
00671       /* [optional][in] */ VARIANT varChild)
00672 {
00673   // currently only handle focus and selection
00674   nsCOMPtr<nsIAccessible> xpAccessible;
00675   GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
00676 
00677   if (flagsSelect & (SELFLAG_TAKEFOCUS|SELFLAG_TAKESELECTION|SELFLAG_REMOVESELECTION))
00678   {
00679     if (flagsSelect & SELFLAG_TAKEFOCUS)
00680       xpAccessible->TakeFocus();
00681 
00682     if (flagsSelect & SELFLAG_TAKESELECTION)
00683       xpAccessible->TakeSelection();
00684 
00685     if (flagsSelect & SELFLAG_REMOVESELECTION)
00686       xpAccessible->RemoveSelection();
00687 
00688     return S_OK;
00689   }
00690 
00691   return E_FAIL;
00692 }
00693 
00694 STDMETHODIMP nsAccessibleWrap::accLocation( 
00695       /* [out] */ long __RPC_FAR *pxLeft,
00696       /* [out] */ long __RPC_FAR *pyTop,
00697       /* [out] */ long __RPC_FAR *pcxWidth,
00698       /* [out] */ long __RPC_FAR *pcyHeight,
00699       /* [optional][in] */ VARIANT varChild)
00700 {
00701   nsCOMPtr<nsIAccessible> xpAccessible;
00702   GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
00703 
00704   if (xpAccessible) {
00705     PRInt32 x, y, width, height;
00706     if (NS_FAILED(xpAccessible->GetBounds(&x, &y, &width, &height)))
00707       return E_FAIL;
00708 
00709     *pxLeft = x;
00710     *pyTop = y;
00711     *pcxWidth = width;
00712     *pcyHeight = height;
00713     return S_OK;
00714   }
00715 
00716   return E_FAIL;  
00717 }
00718 
00719 STDMETHODIMP nsAccessibleWrap::accNavigate( 
00720       /* [in] */ long navDir,
00721       /* [optional][in] */ VARIANT varStart,
00722       /* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt)
00723 {
00724   nsCOMPtr<nsIAccessible> xpAccessibleStart, xpAccessibleResult;
00725   GetXPAccessibleFor(varStart, getter_AddRefs(xpAccessibleStart));
00726   if (!xpAccessibleStart)
00727     return E_FAIL;
00728 
00729   VariantInit(pvarEndUpAt);
00730   PRUint32 xpRelation = 0;
00731 
00732   switch(navDir) {
00733     case NAVDIR_DOWN: 
00734       xpAccessibleStart->GetAccessibleBelow(getter_AddRefs(xpAccessibleResult));
00735       break;
00736     case NAVDIR_FIRSTCHILD:
00737       xpAccessibleStart->GetFirstChild(getter_AddRefs(xpAccessibleResult));
00738       break;
00739     case NAVDIR_LASTCHILD:
00740       xpAccessibleStart->GetLastChild(getter_AddRefs(xpAccessibleResult));
00741       break;
00742     case NAVDIR_LEFT:
00743       xpAccessibleStart->GetAccessibleToLeft(getter_AddRefs(xpAccessibleResult));
00744       break;
00745     case NAVDIR_NEXT:
00746       xpAccessibleStart->GetNextSibling(getter_AddRefs(xpAccessibleResult));
00747       break;
00748     case NAVDIR_PREVIOUS:
00749       xpAccessibleStart->GetPreviousSibling(getter_AddRefs(xpAccessibleResult));
00750       break;
00751     case NAVDIR_RIGHT:
00752       xpAccessibleStart->GetAccessibleToRight(getter_AddRefs(xpAccessibleResult));
00753       break;
00754     case NAVDIR_UP:
00755       xpAccessibleStart->GetAccessibleAbove(getter_AddRefs(xpAccessibleResult));
00756       break;
00757     // MSAA relationship extensions to accNavigate
00758     case NAVRELATION_CONTROLLED_BY:    xpRelation = RELATION_CONTROLLED_BY;    break;
00759     case NAVRELATION_CONTROLLER_FOR:   xpRelation = RELATION_CONTROLLER_FOR;   break;
00760     case NAVRELATION_LABEL_FOR:        xpRelation = RELATION_LABEL_FOR;        break;
00761     case NAVRELATION_LABELLED_BY:      xpRelation = RELATION_LABELLED_BY;      break;
00762     case NAVRELATION_MEMBER_OF:        xpRelation = RELATION_MEMBER_OF;        break;
00763     case NAVRELATION_NODE_CHILD_OF:    xpRelation = RELATION_NODE_CHILD_OF;    break;
00764     case NAVRELATION_FLOWS_TO:         xpRelation = RELATION_FLOWS_TO;         break;
00765     case NAVRELATION_FLOWS_FROM:       xpRelation = RELATION_FLOWS_FROM;       break;
00766     case NAVRELATION_SUBWINDOW_OF:     xpRelation = RELATION_SUBWINDOW_OF;     break;
00767     case NAVRELATION_EMBEDS:           xpRelation = RELATION_EMBEDS;           break;
00768     case NAVRELATION_EMBEDDED_BY:      xpRelation = RELATION_EMBEDDED_BY;      break;
00769     case NAVRELATION_POPUP_FOR:        xpRelation = RELATION_POPUP_FOR;        break;
00770     case NAVRELATION_PARENT_WINDOW_OF: xpRelation = RELATION_PARENT_WINDOW_OF; break;
00771     case NAVRELATION_DEFAULT_BUTTON:   xpRelation = RELATION_DEFAULT_BUTTON;   break;
00772     case NAVRELATION_DESCRIBED_BY:     xpRelation = RELATION_DESCRIBED_BY;     break;
00773     case NAVRELATION_DESCRIPTION_FOR:  xpRelation = RELATION_DESCRIPTION_FOR;  break;
00774   }
00775 
00776   pvarEndUpAt->vt = VT_EMPTY;
00777 
00778   if (xpRelation) {
00779     nsresult rv = GetAccessibleRelated(xpRelation,
00780                                        getter_AddRefs(xpAccessibleResult));
00781     if (rv == NS_ERROR_NOT_IMPLEMENTED) {
00782       return E_NOTIMPL;
00783     }
00784   }
00785 
00786   if (xpAccessibleResult) {
00787     pvarEndUpAt->pdispVal = NativeAccessible(xpAccessibleResult);
00788     pvarEndUpAt->vt = VT_DISPATCH;
00789     return NS_OK;
00790   } 
00791   return E_FAIL;
00792 }
00793 
00794 STDMETHODIMP nsAccessibleWrap::accHitTest( 
00795       /* [in] */ long xLeft,
00796       /* [in] */ long yTop,
00797       /* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
00798 {
00799   VariantInit(pvarChild);
00800 
00801   // convert to window coords
00802   nsCOMPtr<nsIAccessible> xpAccessible;
00803 
00804   xLeft = xLeft;
00805   yTop = yTop;
00806 
00807   GetChildAtPoint(xLeft, yTop, getter_AddRefs(xpAccessible));
00808 
00809   // if we got a child
00810   if (xpAccessible) {
00811     // if the child is us
00812     if (xpAccessible == NS_STATIC_CAST(nsIAccessible*, this)) {
00813       pvarChild->vt = VT_I4;
00814       pvarChild->lVal = CHILDID_SELF;
00815     } else { // its not create an Accessible for it.
00816       pvarChild->vt = VT_DISPATCH;
00817       pvarChild->pdispVal = NativeAccessible(xpAccessible);
00818       nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(xpAccessible));
00819       NS_ASSERTION(accessNode, "Unable to QI to nsIAccessNode");
00820       nsCOMPtr<nsIDOMNode> domNode;
00821       accessNode->GetDOMNode(getter_AddRefs(domNode));
00822       if (!domNode) {
00823         // Has already been shut down
00824         pvarChild->vt = VT_EMPTY;
00825         return E_FAIL;
00826       }
00827     }
00828   } else {
00829     // no child at that point
00830     pvarChild->vt = VT_EMPTY;
00831     return E_FAIL;
00832   }
00833 
00834   return S_OK;
00835 }
00836 
00837 STDMETHODIMP nsAccessibleWrap::accDoDefaultAction( 
00838       /* [optional][in] */ VARIANT varChild)
00839 {
00840   nsCOMPtr<nsIAccessible> xpAccessible;
00841   GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible));
00842 
00843   if (!xpAccessible || FAILED(xpAccessible->DoAction(0))) {
00844     return E_FAIL;
00845   }
00846   return S_OK;
00847 }
00848 
00849 STDMETHODIMP nsAccessibleWrap::put_accName( 
00850       /* [optional][in] */ VARIANT varChild,
00851       /* [in] */ BSTR szName)
00852 {
00853   return E_NOTIMPL;
00854 }
00855 
00856 STDMETHODIMP nsAccessibleWrap::put_accValue( 
00857       /* [optional][in] */ VARIANT varChild,
00858       /* [in] */ BSTR szValue)
00859 {
00860   return E_NOTIMPL;
00861 }
00862 
00863 #include "mshtml.h"
00864 
00865 STDMETHODIMP
00866 nsAccessibleWrap::QueryService(REFGUID guidService, REFIID iid, void** ppv)
00867 {
00882   return QueryInterface(iid, ppv);
00883 }
00884 
00885 
00886 STDMETHODIMP
00887 nsAccessibleWrap::Next(ULONG aNumElementsRequested, VARIANT FAR* pvar, ULONG FAR* aNumElementsFetched)
00888 {
00889   // If there are two clients using this at the same time, and they are
00890   // each using a different mEnumVariant position it would be bad, because
00891   // we have only 1 object and can only keep of mEnumVARIANT position once
00892 
00893   // Children already cached via QI to IEnumVARIANT
00894   *aNumElementsFetched = 0;
00895 
00896   PRInt32 numChildren;
00897   GetChildCount(&numChildren);
00898 
00899   if (aNumElementsRequested <= 0 || !pvar ||
00900       mEnumVARIANTPosition >= numChildren) {
00901     return E_FAIL;
00902   }
00903 
00904   VARIANT varStart;
00905   VariantInit(&varStart);
00906   varStart.lVal = CHILDID_SELF;
00907   varStart.vt = VT_I4;
00908 
00909   accNavigate(NAVDIR_FIRSTCHILD, varStart, &pvar[0]);
00910 
00911   for (long childIndex = 0; pvar[*aNumElementsFetched].vt == VT_DISPATCH; ++childIndex) {
00912     PRBool wasAccessibleFetched = PR_FALSE;
00913     nsAccessibleWrap *msaaAccessible = 
00914       NS_STATIC_CAST(nsAccessibleWrap*, pvar[*aNumElementsFetched].pdispVal);
00915     if (!msaaAccessible)
00916       break;
00917     if (childIndex >= mEnumVARIANTPosition) {
00918       if (++*aNumElementsFetched >= aNumElementsRequested)
00919         break;
00920       wasAccessibleFetched = PR_TRUE;
00921     }
00922     msaaAccessible->accNavigate(NAVDIR_NEXT, varStart, &pvar[*aNumElementsFetched] );
00923     if (!wasAccessibleFetched)
00924       msaaAccessible->nsAccessNode::Release(); // this accessible will not be received by the caller
00925   }
00926 
00927   mEnumVARIANTPosition += NS_STATIC_CAST(PRUint16, *aNumElementsFetched);
00928   return NOERROR;
00929 }
00930 
00931 STDMETHODIMP
00932 nsAccessibleWrap::Skip(ULONG aNumElements)
00933 {
00934   mEnumVARIANTPosition += NS_STATIC_CAST(PRUint16, aNumElements);
00935 
00936   PRInt32 numChildren;
00937   GetChildCount(&numChildren);
00938   
00939   if (mEnumVARIANTPosition > numChildren)
00940   {
00941     mEnumVARIANTPosition = numChildren;
00942     return S_FALSE;
00943   }
00944   return NOERROR;
00945 }
00946 
00947 STDMETHODIMP 
00948 nsAccessibleWrap::Reset(void)
00949 {
00950   mEnumVARIANTPosition = 0;
00951   return NOERROR;
00952 }
00953 
00954 STDMETHODIMP
00955 nsAccessibleWrap::Clone(IEnumVARIANT FAR* FAR* ppenum)
00956 {
00957   // Clone could be bad, the cloned items aren't tracked for shutdown
00958   // Then again, as long as the client releases the items in time, we're okay
00959   *ppenum = nsnull;
00960 
00961   nsAccessibleWrap *accessibleWrap = new nsAccessibleWrap(mDOMNode, mWeakShell);
00962   if (!accessibleWrap)
00963     return E_FAIL;
00964 
00965   IAccessible *msaaAccessible = NS_STATIC_CAST(IAccessible*, accessibleWrap);
00966   msaaAccessible->AddRef();
00967   QueryInterface(IID_IEnumVARIANT, (void**)ppenum);
00968   if (*ppenum)
00969     (*ppenum)->Skip(mEnumVARIANTPosition); // QI addrefed
00970   msaaAccessible->Release();
00971 
00972   return NOERROR;
00973 }
00974         
00975 
00976 // For IDispatch support
00977 STDMETHODIMP 
00978 nsAccessibleWrap::GetTypeInfoCount(UINT *p)
00979 {
00980   *p = 0;
00981   return E_NOTIMPL;
00982 }
00983 
00984 // For IDispatch support
00985 STDMETHODIMP nsAccessibleWrap::GetTypeInfo(UINT i, LCID lcid, ITypeInfo **ppti)
00986 {
00987   *ppti = 0;
00988   return E_NOTIMPL;
00989 }
00990 
00991 // For IDispatch support
00992 STDMETHODIMP 
00993 nsAccessibleWrap::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames,
00994                            UINT cNames, LCID lcid, DISPID *rgDispId)
00995 {
00996   return E_NOTIMPL;
00997 }
00998 
00999 // For IDispatch support
01000 STDMETHODIMP nsAccessibleWrap::Invoke(DISPID dispIdMember, REFIID riid,
01001     LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
01002     VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
01003 {
01004   return E_NOTIMPL;
01005 }
01006 
01007 
01008 NS_IMETHODIMP nsAccessibleWrap::GetNativeInterface(void **aOutAccessible)
01009 {
01010   *aOutAccessible = NS_STATIC_CAST(IAccessible*, this);
01011   NS_ADDREF_THIS();
01012   return NS_OK;
01013 }
01014 
01015         
01016 //------- Helper methods ---------
01017 
01018 IDispatch *nsAccessibleWrap::NativeAccessible(nsIAccessible *aXPAccessible)
01019 {
01020   if (!aXPAccessible) {
01021     return NULL;
01022   }
01023   
01024   nsCOMPtr<nsIAccessibleWin32Object> accObject(do_QueryInterface(aXPAccessible));
01025   if (accObject) {
01026     void* hwnd;
01027     accObject->GetHwnd(&hwnd);
01028     if (hwnd) {
01029       IDispatch *retval = nsnull;
01030       AccessibleObjectFromWindow(NS_REINTERPRET_CAST(HWND, hwnd), 
01031         OBJID_WINDOW, IID_IAccessible, (void **) &retval);
01032       return retval;
01033     }
01034   }
01035 
01036   IAccessible *msaaAccessible;
01037   aXPAccessible->GetNativeInterface((void**)&msaaAccessible);
01038 
01039   return NS_STATIC_CAST(IDispatch*, msaaAccessible);
01040 }
01041 
01042 
01043 void nsAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild, nsIAccessible **aXPAccessible)
01044 {
01045   *aXPAccessible = nsnull;
01046   if (!mWeakShell)
01047     return; // Fail, we don't want to do anything after we've shut down
01048 
01049   // if its us real easy - this seems to always be the case
01050   if (aVarChild.lVal == CHILDID_SELF) {
01051     *aXPAccessible = NS_STATIC_CAST(nsIAccessible*, this);
01052   } 
01053   else {
01054     // XXX It is rare to use a VARIANT with a child num
01055     // so optimizing this is not a priority
01056     // We can come back it do it later, if there are perf problems
01057     // with a specific assistive technology
01058     nsCOMPtr<nsIAccessible> xpAccessible, nextAccessible;
01059     GetFirstChild(getter_AddRefs(xpAccessible));
01060     for (PRInt32 index = 0; xpAccessible; index ++) {
01061       if (!xpAccessible)
01062         break; // Failed
01063       if (aVarChild.lVal == index) {
01064         *aXPAccessible = xpAccessible;
01065         break;
01066       }
01067       nextAccessible = xpAccessible;
01068       nextAccessible->GetNextSibling(getter_AddRefs(xpAccessible));
01069     }
01070   }
01071   NS_IF_ADDREF(*aXPAccessible);
01072 }
01073