forked from mirrors/gecko-dev
1143 lines
26 KiB
C++
1143 lines
26 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#if defined(MOZILLA_INTERNAL_API)
|
|
#error This code is NOT for internal Gecko use!
|
|
#endif // defined(MOZILLA_INTERNAL_API)
|
|
|
|
#define INITGUID
|
|
|
|
#include "AccessibleHandler.h"
|
|
#include "AccessibleHandlerControl.h"
|
|
#include "AccessibleTextTearoff.h"
|
|
|
|
#include "Factory.h"
|
|
#include "HandlerData.h"
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "mozilla/mscom/Registration.h"
|
|
#include "mozilla/UniquePtr.h"
|
|
|
|
#include <objbase.h>
|
|
#include <uiautomation.h>
|
|
#include <winreg.h>
|
|
|
|
#include "AccessibleHypertext.h"
|
|
#include "Accessible2_i.c"
|
|
#include "Accessible2_2_i.c"
|
|
#include "Accessible2_3_i.c"
|
|
|
|
namespace mozilla {
|
|
namespace a11y {
|
|
|
|
static mscom::Factory<AccessibleHandler> sHandlerFactory;
|
|
|
|
HRESULT
|
|
AccessibleHandler::Create(IUnknown* aOuter, REFIID aIid, void** aOutInterface)
|
|
{
|
|
if (!aOutInterface || !aOuter || aIid != IID_IUnknown) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
*aOutInterface = nullptr;
|
|
|
|
HRESULT hr;
|
|
RefPtr<AccessibleHandler> handler(new AccessibleHandler(aOuter, &hr));
|
|
if (!handler) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
return handler->InternalQueryInterface(aIid, aOutInterface);
|
|
}
|
|
|
|
AccessibleHandler::AccessibleHandler(IUnknown* aOuter, HRESULT* aResult)
|
|
: mscom::Handler(aOuter, aResult)
|
|
, mDispatch(nullptr)
|
|
, mIA2PassThru(nullptr)
|
|
, mServProvPassThru(nullptr)
|
|
, mCachedData()
|
|
, mCacheGen(0)
|
|
{
|
|
RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
|
|
MOZ_ASSERT(ctl);
|
|
if (!ctl) {
|
|
if (aResult) {
|
|
*aResult = E_UNEXPECTED;
|
|
}
|
|
return;
|
|
}
|
|
|
|
mCacheGen = ctl->GetCacheGen();
|
|
}
|
|
|
|
AccessibleHandler::~AccessibleHandler()
|
|
{
|
|
if (mCachedData.mGeckoBackChannel) {
|
|
mCachedData.mGeckoBackChannel->Release();
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::ResolveIA2()
|
|
{
|
|
if (mIA2PassThru) {
|
|
return S_OK;
|
|
}
|
|
|
|
RefPtr<IUnknown> proxy(GetProxy());
|
|
if (!proxy) {
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT hr = proxy->QueryInterface(NEWEST_IA2_IID,
|
|
reinterpret_cast<void**>(&mIA2PassThru));
|
|
if (SUCCEEDED(hr)) {
|
|
// mIA2PassThru is a weak reference (see comments in AccesssibleHandler.h)
|
|
mIA2PassThru->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::MaybeUpdateCachedData()
|
|
{
|
|
RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
|
|
if (!ctl) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
uint32_t gen = ctl->GetCacheGen();
|
|
if (gen == mCacheGen) {
|
|
return S_OK;
|
|
}
|
|
|
|
if (!mCachedData.mGeckoBackChannel) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
return mCachedData.mGeckoBackChannel->Refresh(&mCachedData.mData);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::ResolveIDispatch()
|
|
{
|
|
if (mDispatch) {
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT hr;
|
|
|
|
if (!mDispatchUnk) {
|
|
RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
|
|
if (!ctl) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
RefPtr<ITypeInfo> typeinfo;
|
|
hr = ctl->GetHandlerTypeInfo(getter_AddRefs(typeinfo));
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
hr = ::CreateStdDispatch(GetOuter(), static_cast<NEWEST_IA2_INTERFACE*>(this),
|
|
typeinfo, getter_AddRefs(mDispatchUnk));
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
hr = mDispatchUnk->QueryInterface(IID_IDispatch,
|
|
reinterpret_cast<void**>(&mDispatch));
|
|
if (SUCCEEDED(hr)) {
|
|
// mDispatch is weak (see comments in AccessibleHandler.h)
|
|
mDispatch->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::QueryHandlerInterface(IUnknown* aProxyUnknown, REFIID aIid,
|
|
void** aOutInterface)
|
|
{
|
|
MOZ_ASSERT(aProxyUnknown);
|
|
|
|
static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
|
|
"You have modified NEWEST_IA2_IID. This code needs updating.");
|
|
if (aIid == IID_IDispatch || aIid == IID_IAccessible2_3 ||
|
|
aIid == IID_IAccessible2_2 || aIid == IID_IAccessible2 ||
|
|
aIid == IID_IAccessible) {
|
|
RefPtr<NEWEST_IA2_INTERFACE> ia2(static_cast<NEWEST_IA2_INTERFACE*>(this));
|
|
ia2.forget(aOutInterface);
|
|
return S_OK;
|
|
}
|
|
|
|
if (aIid == IID_IServiceProvider) {
|
|
RefPtr<IServiceProvider> svcProv(static_cast<IServiceProvider*>(this));
|
|
svcProv.forget(aOutInterface);
|
|
return S_OK;
|
|
}
|
|
|
|
if (aIid == IID_IAccessibleText || aIid == IID_IAccessibleHypertext) {
|
|
RefPtr<IAccessibleHypertext> textTearoff(new AccessibleTextTearoff(this));
|
|
textTearoff.forget(aOutInterface);
|
|
return S_OK;
|
|
}
|
|
|
|
if (aIid == IID_IProvideClassInfo) {
|
|
RefPtr<IProvideClassInfo> clsInfo(this);
|
|
clsInfo.forget(aOutInterface);
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::ReadHandlerPayload(IStream* aStream, REFIID aIid)
|
|
{
|
|
if (!aStream) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
mscom::StructFromStream deserializer(aStream);
|
|
if (!deserializer) {
|
|
return E_FAIL;
|
|
}
|
|
if (deserializer.IsEmpty()) {
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (!deserializer.Read(&mCachedData, &IA2Payload_Decode)) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (!mCachedData.mGeckoBackChannel) {
|
|
return S_OK;
|
|
}
|
|
|
|
RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
|
|
if (!ctl) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return ctl->Register(WrapNotNull(mCachedData.mGeckoBackChannel));
|
|
}
|
|
|
|
REFIID
|
|
AccessibleHandler::MarshalAs(REFIID aIid)
|
|
{
|
|
static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
|
|
"You have modified NEWEST_IA2_IID. This code needs updating.");
|
|
if (aIid == IID_IAccessible2_3 || aIid == IID_IAccessible2_2 ||
|
|
aIid == IID_IAccessible2 || aIid == IID_IAccessible ||
|
|
aIid == IID_IDispatch) {
|
|
return NEWEST_IA2_IID;
|
|
}
|
|
|
|
return aIid;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::GetMarshalInterface(REFIID aMarshalAsIid,
|
|
NotNull<IUnknown*> aProxy,
|
|
NotNull<IID*> aOutIid,
|
|
NotNull<IUnknown**> aOutUnk)
|
|
{
|
|
if (aMarshalAsIid == NEWEST_IA2_IID) {
|
|
*aOutIid = IID_IAccessible;
|
|
} else {
|
|
*aOutIid = aMarshalAsIid;
|
|
}
|
|
|
|
return aProxy->QueryInterface(aMarshalAsIid,
|
|
reinterpret_cast<void**>(static_cast<IUnknown**>(aOutUnk)));
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::GetHandlerPayloadSize(REFIID aIid, DWORD* aOutPayloadSize)
|
|
{
|
|
if (!aOutPayloadSize) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// If we're sending the payload to somebody else, we'd better make sure that
|
|
// it is up to date. If the cache update fails then we'll return a 0 payload
|
|
// size so that we don't transfer obsolete data.
|
|
if (FAILED(MaybeUpdateCachedData())) {
|
|
*aOutPayloadSize = mscom::StructToStream::GetEmptySize();
|
|
return S_OK;
|
|
}
|
|
|
|
mSerializer = MakeUnique<mscom::StructToStream>(mCachedData, &IA2Payload_Encode);
|
|
if (!mSerializer) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
*aOutPayloadSize = mSerializer->GetSize();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::WriteHandlerPayload(IStream* aStream, REFIID aIid)
|
|
{
|
|
if (!aStream) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!mSerializer) {
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT hr = mSerializer->Write(aStream);
|
|
mSerializer.reset();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
return Handler::QueryInterface(riid, ppv);
|
|
}
|
|
|
|
ULONG
|
|
AccessibleHandler::AddRef()
|
|
{
|
|
return Handler::AddRef();
|
|
}
|
|
|
|
ULONG
|
|
AccessibleHandler::Release()
|
|
{
|
|
return Handler::Release();
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::GetTypeInfoCount(UINT *pctinfo)
|
|
{
|
|
HRESULT hr = ResolveIDispatch();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
return mDispatch->GetTypeInfoCount(pctinfo);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
|
|
{
|
|
HRESULT hr = ResolveIDispatch();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
return mDispatch->GetTypeInfo(iTInfo, lcid, ppTInfo);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
AccessibleHandler::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames,
|
|
LCID lcid, DISPID *rgDispId)
|
|
{
|
|
HRESULT hr = ResolveIDispatch();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
return mDispatch->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
|
|
WORD wFlags, DISPPARAMS *pDispParams,
|
|
VARIANT *pVarResult, EXCEPINFO *pExcepInfo,
|
|
UINT *puArgErr)
|
|
{
|
|
HRESULT hr = ResolveIDispatch();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
return mDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams,
|
|
pVarResult, pExcepInfo, puArgErr);
|
|
}
|
|
|
|
inline static BSTR
|
|
CopyBSTR(BSTR aSrc)
|
|
{
|
|
return ::SysAllocStringLen(aSrc, ::SysStringLen(aSrc));
|
|
}
|
|
|
|
#define BEGIN_CACHE_ACCESS \
|
|
{ \
|
|
HRESULT hr; \
|
|
if (FAILED(hr = MaybeUpdateCachedData())) { \
|
|
return hr; \
|
|
} \
|
|
}
|
|
|
|
#define GET_FIELD(member, assignTo) \
|
|
{ \
|
|
assignTo = mCachedData.mData.member; \
|
|
}
|
|
|
|
#define GET_BSTR(member, assignTo) \
|
|
{ \
|
|
assignTo = CopyBSTR(mCachedData.mData.member); \
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accParent(IDispatch **ppdispParent)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accParent(ppdispParent);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accChildCount(long *pcountChildren)
|
|
{
|
|
if (!pcountChildren) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accChildCount(pcountChildren);
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
GET_FIELD(mChildCount, *pcountChildren);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accChild(VARIANT varChild, IDispatch **ppdispChild)
|
|
{
|
|
if (!ppdispChild) {
|
|
return E_INVALIDARG;
|
|
}
|
|
// Unlikely, but we might as well optimize for it
|
|
if (varChild.vt == VT_I4 && varChild.lVal == CHILDID_SELF) {
|
|
RefPtr<IDispatch> disp(this);
|
|
disp.forget(ppdispChild);
|
|
return S_OK;
|
|
}
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accChild(varChild, ppdispChild);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accName(VARIANT varChild, BSTR *pszName)
|
|
{
|
|
if (!pszName) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accName(varChild, pszName);
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
GET_BSTR(mName, *pszName);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accValue(VARIANT varChild, BSTR *pszValue)
|
|
{
|
|
if (!pszValue) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accValue(varChild, pszValue);
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
GET_BSTR(mValue, *pszValue);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accDescription(VARIANT varChild, BSTR *pszDescription)
|
|
{
|
|
if (!pszDescription) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accDescription(varChild, pszDescription);
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
GET_BSTR(mDescription, *pszDescription);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accRole(VARIANT varChild, VARIANT *pvarRole)
|
|
{
|
|
if (!pvarRole) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accRole(varChild, pvarRole);
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
return ::VariantCopy(pvarRole, &mCachedData.mData.mRole);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accState(VARIANT varChild, VARIANT *pvarState)
|
|
{
|
|
if (!pvarState) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accState(varChild, pvarState);
|
|
}
|
|
|
|
pvarState->vt = VT_I4;
|
|
BEGIN_CACHE_ACCESS;
|
|
GET_FIELD(mState, pvarState->lVal);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accHelp(VARIANT varChild, BSTR *pszHelp)
|
|
{
|
|
// This matches what AccessibleWrap does
|
|
if (!pszHelp) {
|
|
return E_INVALIDARG;
|
|
}
|
|
*pszHelp = nullptr;
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild,
|
|
long *pidTopic)
|
|
{
|
|
// This matches what AccessibleWrap does
|
|
if (!pszHelpFile || !pidTopic) {
|
|
return E_INVALIDARG;
|
|
}
|
|
*pszHelpFile = nullptr;
|
|
*pidTopic = 0;
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accKeyboardShortcut(VARIANT varChild,
|
|
BSTR *pszKeyboardShortcut)
|
|
{
|
|
if (!pszKeyboardShortcut) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
GET_BSTR(mKeyboardShortcut, *pszKeyboardShortcut);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accFocus(VARIANT *pvarChild)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accFocus(pvarChild);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accSelection(VARIANT *pvarChildren)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accSelection(pvarChildren);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accDefaultAction(VARIANT varChild,
|
|
BSTR *pszDefaultAction)
|
|
{
|
|
if (!pszDefaultAction) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accDefaultAction(varChild, pszDefaultAction);
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
GET_BSTR(mDefaultAction, *pszDefaultAction);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::accSelect(long flagsSelect, VARIANT varChild)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->accSelect(flagsSelect, varChild);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::accLocation(long *pxLeft, long *pyTop, long *pcxWidth,
|
|
long *pcyHeight, VARIANT varChild)
|
|
{
|
|
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight,
|
|
varChild);
|
|
}
|
|
|
|
if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
GET_FIELD(mLeft, *pxLeft);
|
|
GET_FIELD(mTop, *pyTop);
|
|
GET_FIELD(mWidth, *pcxWidth);
|
|
GET_FIELD(mHeight, *pcyHeight);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::accNavigate(long navDir, VARIANT varStart,
|
|
VARIANT *pvarEndUpAt)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->accNavigate(navDir, varStart, pvarEndUpAt);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::accHitTest(long xLeft, long yTop, VARIANT *pvarChild)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->accHitTest(xLeft, yTop, pvarChild);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::accDoDefaultAction(VARIANT varChild)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->accDoDefaultAction(varChild);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::put_accName(VARIANT varChild, BSTR szName)
|
|
{
|
|
// This matches AccessibleWrap
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::put_accValue(VARIANT varChild, BSTR szValue)
|
|
{
|
|
// This matches AccessibleWrap
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_nRelations(long* nRelations)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_nRelations(nRelations);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_relation(long relationIndex,
|
|
IAccessibleRelation** relation)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_relation(relationIndex, relation);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_relations(long maxRelations,
|
|
IAccessibleRelation** relations,
|
|
long* nRelations)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_relations(maxRelations, relations, nRelations);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::role(long* role)
|
|
{
|
|
if (!role) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->role(role);
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
GET_FIELD(mIA2Role, *role);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::scrollTo(IA2ScrollType scrollType)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->scrollTo(scrollType);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::scrollToPoint(IA2CoordinateType coordinateType, long x,
|
|
long y)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->scrollToPoint(coordinateType, x, y);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_groupPosition(long* groupLevel, long* similarItemsInGroup,
|
|
long* positionInGroup)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_groupPosition(groupLevel, similarItemsInGroup,
|
|
positionInGroup);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_states(AccessibleStates* states)
|
|
{
|
|
if (!states) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_states(states);
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
GET_FIELD(mIA2States, *states);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_extendedRole(BSTR* extendedRole)
|
|
{
|
|
// This matches ia2Accessible
|
|
if (!extendedRole) {
|
|
return E_INVALIDARG;
|
|
}
|
|
*extendedRole = nullptr;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_localizedExtendedRole(BSTR* localizedExtendedRole)
|
|
{
|
|
// This matches ia2Accessible
|
|
if (!localizedExtendedRole) {
|
|
return E_INVALIDARG;
|
|
}
|
|
*localizedExtendedRole = nullptr;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_nExtendedStates(long* nExtendedStates)
|
|
{
|
|
// This matches ia2Accessible
|
|
if (!nExtendedStates) {
|
|
return E_INVALIDARG;
|
|
}
|
|
*nExtendedStates = 0;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_extendedStates(long maxExtendedStates, BSTR** extendedStates,
|
|
long* nExtendedStates)
|
|
{
|
|
// This matches ia2Accessible
|
|
if (!extendedStates || !nExtendedStates) {
|
|
return E_INVALIDARG;
|
|
}
|
|
*extendedStates = nullptr;
|
|
*nExtendedStates = 0;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_localizedExtendedStates(long maxLocalizedExtendedStates,
|
|
BSTR** localizedExtendedStates,
|
|
long* nLocalizedExtendedStates)
|
|
{
|
|
// This matches ia2Accessible
|
|
if (!localizedExtendedStates || !nLocalizedExtendedStates) {
|
|
return E_INVALIDARG;
|
|
}
|
|
*localizedExtendedStates = nullptr;
|
|
*nLocalizedExtendedStates = 0;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_uniqueID(long* uniqueID)
|
|
{
|
|
if (!uniqueID) {
|
|
return E_INVALIDARG;
|
|
}
|
|
if (!HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_uniqueID(uniqueID);
|
|
}
|
|
*uniqueID = mCachedData.mData.mUniqueId;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_windowHandle(HWND* windowHandle)
|
|
{
|
|
if (!windowHandle) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_windowHandle(windowHandle);
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
long hwnd = 0;
|
|
GET_FIELD(mHwnd, hwnd);
|
|
*windowHandle = reinterpret_cast<HWND>(uintptr_t(hwnd));
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_indexInParent(long* indexInParent)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_indexInParent(indexInParent);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_locale(IA2Locale* locale)
|
|
{
|
|
if (!locale) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_locale(locale);
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
GET_BSTR(mIA2Locale.language, locale->language);
|
|
GET_BSTR(mIA2Locale.country, locale->country);
|
|
GET_BSTR(mIA2Locale.variant, locale->variant);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_attributes(BSTR* attributes)
|
|
{
|
|
if (!attributes) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if (!HasPayload()) {
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_attributes(attributes);
|
|
}
|
|
|
|
BEGIN_CACHE_ACCESS;
|
|
GET_BSTR(mAttributes, *attributes);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_attribute(BSTR name, VARIANT* attribute)
|
|
{
|
|
// We could extract these individually from cached mAttributes.
|
|
// Consider it if traffic warrants it
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_attribute(name, attribute);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_accessibleWithCaret(IUnknown** accessible,
|
|
long* caretOffset)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_accessibleWithCaret(accessible, caretOffset);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_relationTargetsOfType(BSTR type, long maxTargets,
|
|
IUnknown*** targets,
|
|
long* nTargets)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_relationTargetsOfType(type, maxTargets, targets,
|
|
nTargets);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::get_selectionRanges(IA2Range** ranges, long* nRanges)
|
|
{
|
|
HRESULT hr = ResolveIA2();
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
return mIA2PassThru->get_selectionRanges(ranges, nRanges);
|
|
}
|
|
|
|
static const GUID kUnsupportedServices[] = {
|
|
// Unknown, queried by Windows
|
|
{0x33f139ee, 0xe509, 0x47f7, {0xbf, 0x39, 0x83, 0x76, 0x44, 0xf7, 0x45, 0x76}},
|
|
// Unknown, queried by Windows
|
|
{0xFDA075CF, 0x7C8B, 0x498C, { 0xB5, 0x14, 0xA9, 0xCB, 0x52, 0x1B, 0xBF, 0xB4 }},
|
|
// Unknown, queried by Windows
|
|
{0x8EDAA462, 0x21F4, 0x4C87, { 0xA0, 0x12, 0xB3, 0xCD, 0xA3, 0xAB, 0x01, 0xFC }},
|
|
// Unknown, queried by Windows
|
|
{0xacd46652, 0x829d, 0x41cb, { 0xa5, 0xfc, 0x17, 0xac, 0xf4, 0x36, 0x61, 0xac }},
|
|
// Unknown, queried by Windows
|
|
{0xb96fdb85, 0x7204, 0x4724, { 0x84, 0x2b, 0xc7, 0x05, 0x9d, 0xed, 0xb9, 0xd0 }}
|
|
};
|
|
|
|
HRESULT
|
|
AccessibleHandler::QueryService(REFGUID aServiceId, REFIID aIid,
|
|
void** aOutInterface)
|
|
{
|
|
static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
|
|
"You have modified NEWEST_IA2_IID. This code needs updating.");
|
|
/* We're taking advantage of the fact that we are implementing IA2 as part
|
|
of our own object to implement this just like a QI. */
|
|
if (aIid == IID_IAccessible2_3 || aIid == IID_IAccessible2_2 ||
|
|
aIid == IID_IAccessible2) {
|
|
RefPtr<NEWEST_IA2_INTERFACE> ia2(this);
|
|
ia2.forget(aOutInterface);
|
|
return S_OK;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < ArrayLength(kUnsupportedServices); ++i) {
|
|
if (aServiceId == kUnsupportedServices[i]) {
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
|
|
if (!mServProvPassThru) {
|
|
RefPtr<IUnknown> proxy(GetProxy());
|
|
if (!proxy) {
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
HRESULT hr = proxy->QueryInterface(IID_IServiceProvider,
|
|
reinterpret_cast<void**>(&mServProvPassThru));
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
// mServProvPassThru is a weak reference (see comments in
|
|
// AccessibleHandler.h)
|
|
mServProvPassThru->Release();
|
|
}
|
|
|
|
return mServProvPassThru->QueryService(aServiceId, aIid, aOutInterface);
|
|
}
|
|
|
|
HRESULT
|
|
AccessibleHandler::GetClassInfo(ITypeInfo** aOutTypeInfo)
|
|
{
|
|
RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
|
|
if (!ctl) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return ctl->GetHandlerTypeInfo(aOutTypeInfo);
|
|
}
|
|
|
|
} // namespace a11y
|
|
} // namespace mozilla
|
|
|
|
extern "C" HRESULT __stdcall
|
|
ProxyDllCanUnloadNow();
|
|
|
|
extern "C" HRESULT __stdcall
|
|
DllCanUnloadNow()
|
|
{
|
|
return mozilla::mscom::Module::CanUnload() && ProxyDllCanUnloadNow();
|
|
}
|
|
|
|
extern "C" HRESULT __stdcall
|
|
ProxyDllGetClassObject(REFCLSID aClsid, REFIID aIid, LPVOID* aOutInterface);
|
|
|
|
extern "C" HRESULT __stdcall
|
|
DllGetClassObject(REFCLSID aClsid, REFIID aIid, LPVOID* aOutInterface)
|
|
{
|
|
if (aClsid == CLSID_AccessibleHandler) {
|
|
return mozilla::a11y::sHandlerFactory.QueryInterface(aIid, aOutInterface);
|
|
}
|
|
return ProxyDllGetClassObject(aClsid, aIid, aOutInterface);
|
|
}
|
|
|
|
extern "C" BOOL WINAPI
|
|
ProxyDllMain(HINSTANCE aInstDll, DWORD aReason, LPVOID aReserved);
|
|
|
|
BOOL WINAPI
|
|
DllMain(HINSTANCE aInstDll, DWORD aReason, LPVOID aReserved)
|
|
{
|
|
if (aReason == DLL_PROCESS_ATTACH) {
|
|
DisableThreadLibraryCalls((HMODULE)aInstDll);
|
|
}
|
|
// This is required for ProxyDllRegisterServer to work correctly
|
|
return ProxyDllMain(aInstDll, aReason, aReserved);
|
|
}
|
|
|
|
extern "C" HRESULT __stdcall
|
|
ProxyDllRegisterServer();
|
|
|
|
extern "C" HRESULT __stdcall
|
|
DllRegisterServer()
|
|
{
|
|
HRESULT hr = mozilla::mscom::Handler::Register(CLSID_AccessibleHandler);
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
return ProxyDllRegisterServer();
|
|
}
|
|
|
|
extern "C" HRESULT __stdcall
|
|
ProxyDllUnregisterServer();
|
|
|
|
extern "C" HRESULT __stdcall
|
|
DllUnregisterServer()
|
|
{
|
|
HRESULT hr = mozilla::mscom::Handler::Unregister(CLSID_AccessibleHandler);
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
return ProxyDllUnregisterServer();
|
|
}
|