1012 lines
27 KiB
C++
1012 lines
27 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.1 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the NPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the NPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
#include "nsIAccessible.h"
|
|
#include "nsIAccessibleDocument.h"
|
|
#include "nsIAccessibleSelectable.h"
|
|
#include "nsIAccessibilityService.h"
|
|
#include "Accessible.h"
|
|
#include "nsIWidget.h"
|
|
#include "nsWindow.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsIAccessibleEventReceiver.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsITextContent.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIXULDocument.h"
|
|
#include "nsIDOMDocument.h"
|
|
#include "nsIDOMDocumentType.h"
|
|
#include "nsINameSpaceManager.h"
|
|
#include "String.h"
|
|
|
|
// for the COM IEnumVARIANT solution in get_AccSelection()
|
|
#define _ATLBASE_IMPL
|
|
#include <atlbase.h>
|
|
extern CComModule _Module;
|
|
#define _ATLCOM_IMPL
|
|
#include <atlcom.h>
|
|
|
|
/* For documentation of the accessibility architecture,
|
|
* see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
|
|
*/
|
|
|
|
//#define DEBUG_LEAKS
|
|
|
|
#ifdef DEBUG_LEAKS
|
|
static gAccessibles = 0;
|
|
#endif
|
|
|
|
// {61044601-A811-4e2b-BBBA-17BFABD329D7}
|
|
EXTERN_C GUID CDECL CLSID_Accessible =
|
|
{ 0x61044601, 0xa811, 0x4e2b, { 0xbb, 0xba, 0x17, 0xbf, 0xab, 0xd3, 0x29, 0xd7 } };
|
|
|
|
/*
|
|
* Class Accessible
|
|
*/
|
|
|
|
CComModule _Module;
|
|
|
|
//-----------------------------------------------------
|
|
// construction
|
|
//-----------------------------------------------------
|
|
Accessible::Accessible(nsIAccessible* aAcc, nsIDOMNode* aNode, HWND aWnd): SimpleDOMNode(aAcc, aNode, aWnd)
|
|
{
|
|
mAccessible = aAcc; // The nsIAccessible we're proxying from
|
|
|
|
mWnd = aWnd; // The window handle, for NotifyWinEvent, or getting the accessible parent thru the window parent
|
|
m_cRef = 0; // for reference counting, so we know when to delete ourselves
|
|
|
|
// mCachedIndex and mCachedChild allows fast order(N) indexing of children when moving forward through the
|
|
// list 1 at a time,rather than going back to the first child each time, it can just get the next sibling
|
|
mCachedIndex = 0;
|
|
mCachedChild = NULL;
|
|
|
|
#ifdef DEBUG_LEAKS
|
|
printf("Accessibles=%d\n", ++gAccessibles);
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------
|
|
// destruction
|
|
//-----------------------------------------------------
|
|
Accessible::~Accessible()
|
|
{
|
|
m_cRef = 0;
|
|
mAccessible = mCachedChild = nsnull;
|
|
#ifdef DEBUG_LEAKS
|
|
printf("Accessibles=%d\n", --gAccessibles);
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------
|
|
// IUnknown interface methods - see iunknown.h for documentation
|
|
//-----------------------------------------------------
|
|
STDMETHODIMP Accessible::QueryInterface(REFIID iid, void** ppv)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
if (IID_IUnknown == iid || IID_IDispatch == iid || IID_IAccessible == iid)
|
|
*ppv = NS_STATIC_CAST(IAccessible*, this);
|
|
|
|
if (NULL == *ppv)
|
|
return SimpleDOMNode::QueryInterface(iid,ppv);
|
|
|
|
(NS_REINTERPRET_CAST(IUnknown*, *ppv))->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
STDMETHODIMP_(ULONG) Accessible::AddRef()
|
|
{
|
|
return SimpleDOMNode::AddRef();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------
|
|
STDMETHODIMP_(ULONG) Accessible::Release()
|
|
{
|
|
return SimpleDOMNode::Release();
|
|
}
|
|
|
|
HINSTANCE Accessible::gmAccLib = 0;
|
|
HINSTANCE Accessible::gmUserLib = 0;
|
|
LPFNACCESSIBLEOBJECTFROMWINDOW Accessible::gmAccessibleObjectFromWindow = 0;
|
|
LPFNLRESULTFROMOBJECT Accessible::gmLresultFromObject = 0;
|
|
|
|
|
|
LPFNNOTIFYWINEVENT Accessible::gmNotifyWinEvent = 0;
|
|
|
|
|
|
|
|
//-----------------------------------------------------
|
|
// IAccessible methods
|
|
//-----------------------------------------------------
|
|
|
|
|
|
STDMETHODIMP Accessible::AccessibleObjectFromWindow(
|
|
HWND hwnd,
|
|
DWORD dwObjectID,
|
|
REFIID riid,
|
|
void **ppvObject)
|
|
{
|
|
|
|
// open the dll dynamically
|
|
if (!gmAccLib)
|
|
gmAccLib =::LoadLibrary("OLEACC.DLL");
|
|
|
|
if (gmAccLib) {
|
|
if (!gmAccessibleObjectFromWindow)
|
|
gmAccessibleObjectFromWindow = (LPFNACCESSIBLEOBJECTFROMWINDOW)GetProcAddress(gmAccLib,"AccessibleObjectFromWindow");
|
|
|
|
if (gmAccessibleObjectFromWindow)
|
|
return gmAccessibleObjectFromWindow(hwnd, dwObjectID, riid, ppvObject);
|
|
}
|
|
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::NotifyWinEvent(
|
|
DWORD event,
|
|
HWND hwnd,
|
|
LONG idObjectType,
|
|
LONG idObject)
|
|
{
|
|
|
|
// open the dll dynamically
|
|
if (!gmUserLib)
|
|
gmUserLib =::LoadLibrary("USER32.DLL");
|
|
|
|
if (gmUserLib) {
|
|
if (!gmNotifyWinEvent)
|
|
gmNotifyWinEvent = (LPFNNOTIFYWINEVENT)GetProcAddress(gmUserLib,"NotifyWinEvent");
|
|
|
|
if (gmNotifyWinEvent)
|
|
return gmNotifyWinEvent(event, hwnd, idObjectType, idObject);
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP_(LRESULT) Accessible::LresultFromObject(
|
|
REFIID riid,
|
|
WPARAM wParam,
|
|
LPUNKNOWN pAcc)
|
|
{
|
|
// open the dll dynamically
|
|
if (!gmAccLib)
|
|
gmAccLib =::LoadLibrary("OLEACC.DLL");
|
|
|
|
if (gmAccLib) {
|
|
if (!gmAccessibleObjectFromWindow)
|
|
gmLresultFromObject = (LPFNLRESULTFROMOBJECT)GetProcAddress(gmAccLib,"LresultFromObject");
|
|
|
|
if (gmLresultFromObject)
|
|
return gmLresultFromObject(riid,wParam,pAcc);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
STDMETHODIMP Accessible::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
|
|
{
|
|
nsCOMPtr<nsIAccessible> parent(nsnull);
|
|
mAccessible->GetAccParent(getter_AddRefs(parent));
|
|
|
|
if (parent) {
|
|
IAccessible* a = NewAccessible(parent, nsnull, mWnd);
|
|
a->AddRef();
|
|
*ppdispParent = a;
|
|
return S_OK;
|
|
}
|
|
|
|
// if we have a widget but no parent nsIAccessible then we might have a native
|
|
// widget parent that could give us a IAccessible. Lets check.
|
|
HWND pWnd = ::GetParent(mWnd);
|
|
if (pWnd) {
|
|
// get the accessible.
|
|
void* ptr = nsnull;
|
|
HRESULT result = AccessibleObjectFromWindow(pWnd, OBJID_WINDOW, IID_IAccessible, &ptr);
|
|
if (SUCCEEDED(result)) {
|
|
IAccessible* a = (IAccessible*)ptr;
|
|
// got one? return it.
|
|
if (a) {
|
|
*ppdispParent = a;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
*ppdispParent = NULL;
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::get_accChildCount( long __RPC_FAR *pcountChildren)
|
|
{
|
|
PRInt32 count = 0;
|
|
mAccessible->GetAccChildCount(&count);
|
|
*pcountChildren = count;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::get_accChild(
|
|
/* [in] */ VARIANT varChild,
|
|
/* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild)
|
|
{
|
|
nsCOMPtr<nsIAccessible> a;
|
|
GetNSAccessibleFor(varChild,a);
|
|
|
|
if (a)
|
|
{
|
|
// if its us return us
|
|
if (a == mAccessible) {
|
|
*ppdispChild = this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
// create a new one.
|
|
IAccessible* ia = NewAccessible(a, nsnull, mWnd);
|
|
ia->AddRef();
|
|
*ppdispChild = ia;
|
|
return S_OK;
|
|
}
|
|
|
|
*ppdispChild = NULL;
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::get_accName(
|
|
/* [optional][in] */ VARIANT varChild,
|
|
/* [retval][out] */ BSTR __RPC_FAR *pszName)
|
|
{
|
|
*pszName = NULL;
|
|
nsCOMPtr<nsIAccessible> a;
|
|
GetNSAccessibleFor(varChild,a);
|
|
if (a) {
|
|
nsAutoString name;
|
|
nsresult rv = a->GetAccName(name);
|
|
if (NS_FAILED(rv))
|
|
return S_FALSE;
|
|
|
|
*pszName = ::SysAllocString(name.get());
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP Accessible::get_accValue(
|
|
/* [optional][in] */ VARIANT varChild,
|
|
/* [retval][out] */ BSTR __RPC_FAR *pszValue)
|
|
{
|
|
*pszValue = NULL;
|
|
nsCOMPtr<nsIAccessible> a;
|
|
GetNSAccessibleFor(varChild,a);
|
|
if (a) {
|
|
nsAutoString value;
|
|
nsresult rv = a->GetAccValue(value);
|
|
if (NS_FAILED(rv))
|
|
return S_FALSE;
|
|
|
|
*pszValue = ::SysAllocString(value.get());
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP Accessible::get_accDescription(
|
|
/* [optional][in] */ VARIANT varChild,
|
|
/* [retval][out] */ BSTR __RPC_FAR *pszDescription)
|
|
{
|
|
*pszDescription = NULL;
|
|
nsCOMPtr<nsIAccessible> a;
|
|
GetNSAccessibleFor(varChild,a);
|
|
if (a) {
|
|
nsAutoString description;
|
|
nsresult rv = a->GetAccDescription(description);
|
|
if (NS_FAILED(rv))
|
|
return S_FALSE;
|
|
|
|
*pszDescription = ::SysAllocString(description.get());
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::get_accRole(
|
|
/* [optional][in] */ VARIANT varChild,
|
|
/* [retval][out] */ VARIANT __RPC_FAR *pvarRole)
|
|
{
|
|
VariantInit(pvarRole);
|
|
pvarRole->vt = VT_I4;
|
|
|
|
nsCOMPtr<nsIAccessible> a;
|
|
GetNSAccessibleFor(varChild,a);
|
|
|
|
if (!a)
|
|
return E_FAIL;
|
|
|
|
PRUint32 role = 0;
|
|
nsresult rv = a->GetAccRole(&role);
|
|
if (NS_FAILED(rv))
|
|
return E_FAIL;
|
|
|
|
pvarRole->lVal = role;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::get_accState(
|
|
/* [optional][in] */ VARIANT varChild,
|
|
/* [retval][out] */ VARIANT __RPC_FAR *pvarState)
|
|
{
|
|
VariantInit(pvarState);
|
|
pvarState->vt = VT_I4;
|
|
pvarState->lVal = 0;
|
|
|
|
nsCOMPtr<nsIAccessible> a;
|
|
GetNSAccessibleFor(varChild,a);
|
|
if (!a)
|
|
return E_FAIL;
|
|
|
|
PRUint32 state;
|
|
nsresult rv = a->GetAccState(&state);
|
|
if (NS_FAILED(rv))
|
|
return E_FAIL;
|
|
|
|
pvarState->lVal = state;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
STDMETHODIMP Accessible::get_accHelp(
|
|
/* [optional][in] */ VARIANT varChild,
|
|
/* [retval][out] */ BSTR __RPC_FAR *pszHelp)
|
|
{
|
|
*pszHelp = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::get_accHelpTopic(
|
|
/* [out] */ BSTR __RPC_FAR *pszHelpFile,
|
|
/* [optional][in] */ VARIANT varChild,
|
|
/* [retval][out] */ long __RPC_FAR *pidTopic)
|
|
{
|
|
*pszHelpFile = NULL;
|
|
*pidTopic = 0;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::get_accKeyboardShortcut(
|
|
/* [optional][in] */ VARIANT varChild,
|
|
/* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut)
|
|
{
|
|
*pszKeyboardShortcut = NULL;
|
|
return S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::get_accFocus(
|
|
/* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
|
|
{
|
|
// Return the current nsIAccessible that has focus
|
|
VariantInit(pvarChild);
|
|
|
|
nsCOMPtr<nsIAccessible> focusedAccessible;
|
|
if (NS_SUCCEEDED(mAccessible->GetAccFocused(getter_AddRefs(focusedAccessible)))) {
|
|
pvarChild->vt = VT_DISPATCH;
|
|
pvarChild->pdispVal = NewAccessible(focusedAccessible, nsnull, mWnd);
|
|
pvarChild->pdispVal->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
pvarChild->vt = VT_EMPTY;
|
|
return E_FAIL;
|
|
}
|
|
/**
|
|
* This method is called when a client wants to know which children of a node
|
|
* are selected. Currently we only handle this for HTML selects, which are the
|
|
* only nsIAccessible objects to implement nsIAccessibleSelectable.
|
|
*
|
|
* The VARIANT return value arguement is expected to either contain a single IAccessible
|
|
* or an IEnumVARIANT of IAccessibles. We return the IEnumVARIANT regardless of the number
|
|
* of options selected, unless there are none selected in which case we return an empty
|
|
* VARIANT.
|
|
*
|
|
* The typedefs at the beginning set up the structure that will contain an array
|
|
* of the IAccessibles. It implements the IEnumVARIANT interface, allowing us to
|
|
* use it to return the IAccessibles in the VARIANT.
|
|
*
|
|
* We get the selected options from the select's accessible object and then put create
|
|
* IAccessible objects for them and put those in the CComObject<EnumeratorType>
|
|
* object. Then we put the CComObject<EnumeratorType> object in the VARIANT and return.
|
|
*
|
|
* returns a VT_EMPTY VARIANT if:
|
|
* - there are no options in the select
|
|
* - none of the options are selected
|
|
* - there is an error QIing to IEnumVARIANT
|
|
* - The object is not the type that can have children selected
|
|
*/
|
|
STDMETHODIMP Accessible::get_accSelection(
|
|
/* [retval][out] */ VARIANT __RPC_FAR *pvarChildren)
|
|
{
|
|
typedef VARIANT ItemType; /* type of the object to be stored in container */
|
|
typedef ItemType EnumeratorExposedType; /* the type of the item exposed by the enumerator interface */
|
|
typedef IEnumVARIANT EnumeratorInterface; /* a COM enumerator ( IEnumXXXXX ) interface */
|
|
typedef _Copy<EnumeratorExposedType> EnumeratorCopyPolicy; /* Copy policy class */
|
|
typedef CComEnum<EnumeratorInterface,
|
|
&__uuidof(EnumeratorInterface),
|
|
EnumeratorExposedType,
|
|
EnumeratorCopyPolicy > EnumeratorType;
|
|
|
|
IEnumVARIANT* pUnk = NULL;
|
|
CComObject<EnumeratorType>* pEnum = NULL;
|
|
VariantInit(pvarChildren);
|
|
pvarChildren->vt = VT_EMPTY;
|
|
|
|
nsCOMPtr<nsIAccessibleSelectable> select(do_QueryInterface(mAccessible));
|
|
if (select) { // do we have an nsIAccessibleSelectable?
|
|
// we have an accessible that can have children selected
|
|
nsCOMPtr<nsISupportsArray> selectedOptions;
|
|
// gets the selected options as nsIAccessibles.
|
|
select->GetSelectedChildren(getter_AddRefs(selectedOptions));
|
|
if (selectedOptions) { // false if the select has no children or none are selected
|
|
PRUint32 length;
|
|
selectedOptions->Count(&length);
|
|
CComVariant* optionArray = new CComVariant[length]; // needs to be a CComVariant to go into the EnumeratorType object
|
|
|
|
// 1) Populate an array to store in the enumeration
|
|
for (PRUint32 i = 0 ; i < length ; i++) {
|
|
nsCOMPtr<nsISupports> tempOption;
|
|
selectedOptions->GetElementAt(i,getter_AddRefs(tempOption)); // this expects an nsISupports
|
|
if (tempOption) {
|
|
nsCOMPtr<nsIAccessible> tempAccess(do_QueryInterface(tempOption));
|
|
if ( tempAccess ) {
|
|
optionArray[i] = NewAccessible(tempAccess, nsnull, mWnd);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2) Create and initialize the enumeration
|
|
HRESULT hr = CComObject<EnumeratorType>::CreateInstance(&pEnum);
|
|
pEnum->Init(&optionArray[0], &optionArray[length], NULL, AtlFlagCopy);
|
|
pEnum->QueryInterface(IID_IEnumVARIANT, reinterpret_cast<void**>(&pUnk));
|
|
delete [] optionArray; // clean up, the Init call copies the data (AtlFlagCopy)
|
|
|
|
// 3) Put the enumerator in the VARIANT
|
|
if (!pUnk)
|
|
return NS_ERROR_FAILURE;
|
|
pvarChildren->vt = VT_UNKNOWN; // this must be VT_UNKNOWN for an IEnumVARIANT
|
|
pvarChildren->punkVal = pUnk;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::get_accDefaultAction(
|
|
/* [optional][in] */ VARIANT varChild,
|
|
/* [retval][out] */ BSTR __RPC_FAR *pszDefaultAction)
|
|
{
|
|
*pszDefaultAction = NULL;
|
|
nsCOMPtr<nsIAccessible> a;
|
|
GetNSAccessibleFor(varChild,a);
|
|
if (a) {
|
|
nsAutoString defaultAction;
|
|
nsresult rv = a->GetAccActionName(0,defaultAction);
|
|
if (NS_FAILED(rv))
|
|
return S_FALSE;
|
|
|
|
*pszDefaultAction = ::SysAllocString(defaultAction.get());
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::accSelect(
|
|
/* [in] */ long flagsSelect,
|
|
/* [optional][in] */ VARIANT varChild)
|
|
{
|
|
// currently only handle focus and selection
|
|
if (flagsSelect & (SELFLAG_TAKEFOCUS|SELFLAG_TAKESELECTION|SELFLAG_REMOVESELECTION))
|
|
{
|
|
if (flagsSelect & SELFLAG_TAKEFOCUS)
|
|
mAccessible->AccTakeFocus();
|
|
|
|
if (flagsSelect & SELFLAG_TAKESELECTION)
|
|
mAccessible->AccTakeSelection();
|
|
|
|
if (flagsSelect & SELFLAG_REMOVESELECTION)
|
|
mAccessible->AccRemoveSelection();
|
|
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::accLocation(
|
|
/* [out] */ long __RPC_FAR *pxLeft,
|
|
/* [out] */ long __RPC_FAR *pyTop,
|
|
/* [out] */ long __RPC_FAR *pcxWidth,
|
|
/* [out] */ long __RPC_FAR *pcyHeight,
|
|
/* [optional][in] */ VARIANT varChild)
|
|
{
|
|
nsCOMPtr<nsIAccessible> a;
|
|
GetNSAccessibleFor(varChild,a);
|
|
|
|
if (a) {
|
|
PRInt32 x,y,w,h;
|
|
a->AccGetBounds(&x,&y,&w,&h);
|
|
|
|
POINT cpos;
|
|
cpos.x = x;
|
|
cpos.y = y;
|
|
|
|
*pxLeft = x;
|
|
*pyTop = y;
|
|
*pcxWidth = w;
|
|
*pcyHeight = h;
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::accNavigate(
|
|
/* [in] */ long navDir,
|
|
/* [optional][in] */ VARIANT varStart,
|
|
/* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt)
|
|
{
|
|
VariantInit(pvarEndUpAt);
|
|
|
|
nsCOMPtr<nsIAccessible> acc;
|
|
|
|
switch(navDir) {
|
|
case NAVDIR_DOWN:
|
|
mAccessible->AccNavigateDown(getter_AddRefs(acc));
|
|
break;
|
|
case NAVDIR_FIRSTCHILD:
|
|
mAccessible->GetAccFirstChild(getter_AddRefs(acc));
|
|
break;
|
|
case NAVDIR_LASTCHILD:
|
|
mAccessible->GetAccLastChild(getter_AddRefs(acc));
|
|
break;
|
|
case NAVDIR_LEFT:
|
|
mAccessible->AccNavigateLeft(getter_AddRefs(acc));
|
|
break;
|
|
case NAVDIR_NEXT:
|
|
mAccessible->GetAccNextSibling(getter_AddRefs(acc));
|
|
break;
|
|
case NAVDIR_PREVIOUS:
|
|
mAccessible->GetAccPreviousSibling(getter_AddRefs(acc));
|
|
break;
|
|
case NAVDIR_RIGHT:
|
|
mAccessible->AccNavigateRight(getter_AddRefs(acc));
|
|
break;
|
|
case NAVDIR_UP:
|
|
mAccessible->AccNavigateUp(getter_AddRefs(acc));
|
|
break;
|
|
}
|
|
|
|
if (acc) {
|
|
IAccessible* a = NewAccessible(acc, nsnull, mWnd);
|
|
a->AddRef();
|
|
pvarEndUpAt->vt = VT_DISPATCH;
|
|
pvarEndUpAt->pdispVal = a;
|
|
return NS_OK;
|
|
} else {
|
|
pvarEndUpAt->vt = VT_EMPTY;
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP Accessible::accHitTest(
|
|
/* [in] */ long xLeft,
|
|
/* [in] */ long yTop,
|
|
/* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
|
|
{
|
|
VariantInit(pvarChild);
|
|
|
|
// convert to window coords
|
|
nsCOMPtr<nsIAccessible> a;
|
|
|
|
xLeft = xLeft;
|
|
yTop = yTop;
|
|
|
|
mAccessible->AccGetAt(xLeft,yTop, getter_AddRefs(a));
|
|
|
|
// if we got a child
|
|
if (a)
|
|
{
|
|
// if the child is us
|
|
if (a == mAccessible) {
|
|
pvarChild->vt = VT_I4;
|
|
pvarChild->lVal = CHILDID_SELF;
|
|
} else { // its not create an Accessible for it.
|
|
pvarChild->vt = VT_DISPATCH;
|
|
pvarChild->pdispVal = NewAccessible(a, nsnull, mWnd);
|
|
pvarChild->pdispVal->AddRef();
|
|
}
|
|
} else {
|
|
// no child at that point
|
|
pvarChild->vt = VT_EMPTY;
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::accDoDefaultAction(
|
|
/* [optional][in] */ VARIANT varChild)
|
|
{
|
|
return NS_SUCCEEDED(mAccessible->AccDoAction(0))? NS_OK: DISP_E_MEMBERNOTFOUND;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::put_accName(
|
|
/* [optional][in] */ VARIANT varChild,
|
|
/* [in] */ BSTR szName)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP Accessible::put_accValue(
|
|
/* [optional][in] */ VARIANT varChild,
|
|
/* [in] */ BSTR szValue)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
// For IDispatch support
|
|
STDMETHODIMP
|
|
Accessible::GetTypeInfoCount(UINT *p)
|
|
{
|
|
*p = 0;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
// For IDispatch support
|
|
STDMETHODIMP Accessible::GetTypeInfo(UINT i, LCID lcid, ITypeInfo **ppti)
|
|
{
|
|
*ppti = 0;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
// For IDispatch support
|
|
STDMETHODIMP
|
|
Accessible::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames,
|
|
UINT cNames, LCID lcid, DISPID *rgDispId)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
// For IDispatch support
|
|
STDMETHODIMP Accessible::Invoke(DISPID dispIdMember, REFIID riid,
|
|
LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
|
|
VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
//------- Helper methods ---------
|
|
|
|
IAccessible *Accessible::NewAccessible(nsIAccessible *aNSAcc, nsIDOMNode *aNode, HWND aWnd)
|
|
{
|
|
IAccessible *retval = nsnull;
|
|
if (aNSAcc) {
|
|
nsCOMPtr<nsIAccessibleDocument> accDoc(do_QueryInterface(aNSAcc));
|
|
if (accDoc) {
|
|
nsCOMPtr<nsIDocument> doc;
|
|
accDoc->GetDocument(getter_AddRefs(doc));
|
|
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(doc));
|
|
retval = new DocAccessible(aNSAcc, node, aWnd);
|
|
}
|
|
else
|
|
retval = new Accessible(aNSAcc, aNode, aWnd);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
void Accessible::GetNSAccessibleFor(VARIANT varChild, nsCOMPtr<nsIAccessible>& aAcc)
|
|
{
|
|
// if its us real easy
|
|
if (varChild.lVal == CHILDID_SELF) {
|
|
aAcc = mAccessible;
|
|
} else if (mCachedChild && varChild.lVal == mCachedIndex+1) {
|
|
// if the cachedIndex is not self and the if its the one right
|
|
// before the one we want to get. Then just ask it for its next.
|
|
nsCOMPtr<nsIAccessible> next;
|
|
mCachedChild->GetAccNextSibling(getter_AddRefs(next));
|
|
if (next) {
|
|
mCachedIndex++;
|
|
mCachedChild = next;
|
|
}
|
|
|
|
aAcc = next;
|
|
return;
|
|
|
|
} else {
|
|
// if the cachedindex is not the one right before we have to start at the begining
|
|
nsCOMPtr<nsIAccessible> a;
|
|
mAccessible->GetAccFirstChild(getter_AddRefs(mCachedChild));
|
|
mCachedIndex = 1;
|
|
while(mCachedChild)
|
|
{
|
|
if (varChild.lVal == mCachedIndex)
|
|
{
|
|
aAcc = mCachedChild;
|
|
return;
|
|
}
|
|
|
|
mCachedChild->GetAccNextSibling(getter_AddRefs(a));
|
|
mCachedChild = a;
|
|
mCachedIndex++;
|
|
}
|
|
|
|
aAcc = nsnull;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//----- DocAccessible -----
|
|
|
|
// Microsoft COM QueryInterface
|
|
STDMETHODIMP DocAccessible::QueryInterface(REFIID iid, void** ppv)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
if (IID_IUnknown == iid || IID_IDispatch == iid || IID_ISimpleDOMDocument == iid)
|
|
*ppv = NS_STATIC_CAST(ISimpleDOMDocument*, this);
|
|
|
|
if (NULL == *ppv)
|
|
return Accessible::QueryInterface(iid,ppv);
|
|
|
|
(NS_REINTERPRET_CAST(IUnknown*, *ppv))->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
DocAccessible::AddRef(void)
|
|
{
|
|
return Accessible::AddRef();
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
DocAccessible::Release(void)
|
|
{
|
|
return Accessible::Release();
|
|
}
|
|
|
|
|
|
DocAccessible::DocAccessible(nsIAccessible* aAcc, nsIDOMNode *aNode, HWND aWnd):Accessible(aAcc,aNode,aWnd)
|
|
{
|
|
}
|
|
|
|
DocAccessible::~DocAccessible()
|
|
{
|
|
}
|
|
|
|
|
|
STDMETHODIMP DocAccessible::get_URL(/* [out] */ BSTR __RPC_FAR *aURL)
|
|
{
|
|
nsCOMPtr<nsIAccessibleDocument> accDoc(do_QueryInterface(mAccessible));
|
|
if (accDoc) {
|
|
nsAutoString URL;
|
|
if (NS_SUCCEEDED(accDoc->GetURL(URL))) {
|
|
*aURL= ::SysAllocString(URL.get());
|
|
return S_OK;
|
|
}
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP DocAccessible::get_title( /* [out] */ BSTR __RPC_FAR *aTitle)
|
|
{
|
|
nsCOMPtr<nsIAccessibleDocument> accDoc(do_QueryInterface(mAccessible));
|
|
if (accDoc) {
|
|
nsAutoString title;
|
|
if (NS_SUCCEEDED(accDoc->GetTitle(title))) { // getter_Copies(pszTitle)))) {
|
|
*aTitle= ::SysAllocString(title.get());
|
|
return S_OK;
|
|
}
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP DocAccessible::get_mimeType(/* [out] */ BSTR __RPC_FAR *aMimeType)
|
|
{
|
|
nsCOMPtr<nsIAccessibleDocument> accDoc(do_QueryInterface(mAccessible));
|
|
if (accDoc) {
|
|
nsAutoString mimeType;
|
|
if (NS_SUCCEEDED(accDoc->GetMimeType(mimeType))) {
|
|
*aMimeType= ::SysAllocString(mimeType.get());
|
|
return S_OK;
|
|
}
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP DocAccessible::get_docType(/* [out] */ BSTR __RPC_FAR *aDocType)
|
|
{
|
|
nsCOMPtr<nsIAccessibleDocument> accDoc(do_QueryInterface(mAccessible));
|
|
if (accDoc) {
|
|
nsAutoString docType;
|
|
if (NS_SUCCEEDED(accDoc->GetDocType(docType))) {
|
|
*aDocType= ::SysAllocString(docType.get());
|
|
return S_OK;
|
|
}
|
|
}
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP DocAccessible::get_nameSpaceURIForID(/* [in] */ short aNameSpaceID,
|
|
/* [out] */ BSTR __RPC_FAR *aNameSpaceURI)
|
|
{
|
|
nsCOMPtr<nsIAccessibleDocument> accDoc(do_QueryInterface(mAccessible));
|
|
if (accDoc) {
|
|
*aNameSpaceURI = NULL;
|
|
nsAutoString nameSpaceURI;
|
|
if (NS_SUCCEEDED(accDoc->GetNameSpaceURIForID(aNameSpaceID, nameSpaceURI)))
|
|
*aNameSpaceURI = ::SysAllocString(nameSpaceURI.get());
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
STDMETHODIMP DocAccessible::put_alternateViewMediaTypes( /* [in] */ BSTR __RPC_FAR *commaSeparatedMediaTypes)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//----- Root Accessible -----
|
|
|
|
// XPCOM QueryInterface
|
|
NS_IMPL_QUERY_INTERFACE1(RootAccessible, nsIAccessibleEventListener)
|
|
|
|
// Microsoft COM QueryInterface
|
|
STDMETHODIMP RootAccessible::QueryInterface(REFIID iid, void** ppv)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
if (IID_IUnknown == iid || IID_IDispatch == iid || IID_ISimpleDOMDocument == iid)
|
|
*ppv = NS_STATIC_CAST(ISimpleDOMDocument*, this);
|
|
|
|
if (NULL == *ppv)
|
|
return DocAccessible::QueryInterface(iid, ppv);
|
|
|
|
(NS_REINTERPRET_CAST(IUnknown*, *ppv))->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
RootAccessible::AddRef(void)
|
|
{
|
|
return DocAccessible::AddRef();
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
RootAccessible::Release(void)
|
|
{
|
|
return DocAccessible::Release();
|
|
}
|
|
|
|
RootAccessible::RootAccessible(nsIAccessible* aAcc, HWND aWnd):DocAccessible(aAcc,nsnull,aWnd)
|
|
{
|
|
mListCount = 0;
|
|
mNextPos = 0;
|
|
|
|
nsCOMPtr<nsIAccessibleEventReceiver> r(do_QueryInterface(mAccessible));
|
|
if (r)
|
|
r->AddAccessibleEventListener(this);
|
|
}
|
|
|
|
RootAccessible::~RootAccessible()
|
|
{
|
|
nsCOMPtr<nsIAccessibleEventReceiver> r(do_QueryInterface(mAccessible));
|
|
if (r)
|
|
r->RemoveAccessibleEventListener();
|
|
|
|
// free up accessibles
|
|
for (int i=0; i < mListCount; i++)
|
|
mList[i].mAccessible = nsnull;
|
|
}
|
|
|
|
void RootAccessible::GetNSAccessibleFor(VARIANT varChild, nsCOMPtr<nsIAccessible>& aAcc)
|
|
{
|
|
// Check our circular array of event information, because the child ID
|
|
// asked for corresponds to an event target. See RootAccessible::HandleEvent to see how we provide this unique ID.
|
|
|
|
aAcc = nsnull;
|
|
if (varChild.lVal < 0) {
|
|
for (int i=0; i < mListCount; i++) {
|
|
if (varChild.lVal == mList[i].mId) {
|
|
aAcc = mList[i].mAccessible;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Accessible::GetNSAccessibleFor(varChild, aAcc);
|
|
}
|
|
|
|
NS_IMETHODIMP RootAccessible::HandleEvent(PRUint32 aEvent, nsIAccessible* aAccessible)
|
|
{
|
|
// get the id for the accessible
|
|
PRInt32 id = GetIdFor(aAccessible);
|
|
|
|
// notify the window system
|
|
NotifyWinEvent(aEvent, mWnd, OBJID_CLIENT, id);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
PRUint32 RootAccessible::GetIdFor(nsIAccessible* aAccessible)
|
|
{
|
|
// A child ID of the window is required, when we use NotifyWinEvent, so that the 3rd party application
|
|
// can call back and get the IAccessible the event occured on.
|
|
// We use the unique ID exposed through nsIContent::GetContentID()
|
|
|
|
nsCOMPtr<nsIDOMNode> domNode;
|
|
aAccessible->AccGetDOMNode(getter_AddRefs(domNode));
|
|
PRUint32 uniqueID = - NS_REINTERPRET_CAST(PRInt32, (domNode.get()));
|
|
|
|
mList[mNextPos].mId = uniqueID;
|
|
mList[mNextPos].mAccessible = aAccessible;
|
|
|
|
if (++mNextPos >= MAX_LIST_SIZE)
|
|
mNextPos = 0;
|
|
if (mListCount < MAX_LIST_SIZE)
|
|
mListCount++;
|
|
|
|
return uniqueID;
|
|
}
|
|
|