зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1272146: Add thunk for IAccessible property accesses that pass non-self child IDs; r=tbsaunde
MozReview-Commit-ID: Kx8UVGP2q7h --HG-- extra : histedit_source : 39006b47f24874b4c58b9548b4fd1f18f57f1172
This commit is contained in:
Родитель
05bd3da901
Коммит
cfad6766e2
|
@ -923,22 +923,6 @@ AccessibleWrap::accNavigate(
|
|||
if (accessible->IsDefunct())
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
// Make sure that varStart != CHILDID_SELF so we don't infinitely recurse
|
||||
if (accessible->IsProxy() && varStart.vt == VT_I4 &&
|
||||
varStart.lVal != CHILDID_SELF) {
|
||||
// Now that we have the starting object, delegate this request to that
|
||||
// object as a self-relative request...
|
||||
RefPtr<IAccessible> comProxy;
|
||||
accessible->GetNativeInterface((void**) getter_AddRefs(comProxy));
|
||||
if (!comProxy) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
VARIANT selfChildId;
|
||||
selfChildId.vt = VT_I4;
|
||||
selfChildId.lVal = CHILDID_SELF;
|
||||
return comProxy->accNavigate(navDir, selfChildId, pvarEndUpAt);
|
||||
}
|
||||
|
||||
Accessible* navAccessible = nullptr;
|
||||
Maybe<RelationType> xpRelation;
|
||||
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "ChildIDThunk.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/mscom/InterceptorLog.h"
|
||||
|
||||
using namespace mozilla::mscom;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
ChildIDThunk::ChildIDThunk()
|
||||
: mRefCnt(0)
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT
|
||||
ChildIDThunk::QueryInterface(REFIID riid, void** ppv)
|
||||
{
|
||||
if (!ppv) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (riid == IID_IUnknown || riid == IID_ICallFrameEvents ||
|
||||
riid == IID_IInterceptorSink) {
|
||||
*ppv = static_cast<IInterceptorSink*>(this);
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppv = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG
|
||||
ChildIDThunk::AddRef()
|
||||
{
|
||||
return (ULONG) InterlockedIncrement((LONG*)&mRefCnt);
|
||||
}
|
||||
|
||||
ULONG
|
||||
ChildIDThunk::Release()
|
||||
{
|
||||
ULONG newRefCnt = (ULONG) InterlockedDecrement((LONG*)&mRefCnt);
|
||||
if (newRefCnt == 0) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
delete this;
|
||||
}
|
||||
return newRefCnt;
|
||||
}
|
||||
|
||||
enum IAccessibleVTableIndex
|
||||
{
|
||||
eget_accName = 10,
|
||||
eget_accValue = 11,
|
||||
eget_accDescription = 12,
|
||||
eget_accRole = 13,
|
||||
eget_accState = 14,
|
||||
eget_accHelp = 15,
|
||||
eget_accHelpTopic = 16,
|
||||
eget_accKeyboardShortcut = 17,
|
||||
eget_accDefaultAction = 20,
|
||||
eaccSelect = 21,
|
||||
eaccLocation = 22,
|
||||
eaccNavigate = 23,
|
||||
eaccDoDefaultAction = 25,
|
||||
eput_accName = 26,
|
||||
eput_accValue = 27
|
||||
};
|
||||
|
||||
Maybe<ULONG>
|
||||
ChildIDThunk::IsMethodInteresting(const ULONG aMethodIdx)
|
||||
{
|
||||
switch (aMethodIdx) {
|
||||
case eget_accName:
|
||||
case eget_accValue:
|
||||
case eget_accDescription:
|
||||
case eget_accRole:
|
||||
case eget_accState:
|
||||
case eget_accHelp:
|
||||
case eget_accKeyboardShortcut:
|
||||
case eget_accDefaultAction:
|
||||
case eaccDoDefaultAction:
|
||||
case eput_accName:
|
||||
case eput_accValue:
|
||||
return Some(0UL);
|
||||
case eget_accHelpTopic:
|
||||
case eaccNavigate:
|
||||
return Some(1UL);
|
||||
case eaccSelect:
|
||||
return Some(2UL);
|
||||
case eaccLocation:
|
||||
return Some(4UL);
|
||||
default:
|
||||
return Nothing();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ChildIDThunk::IsChildIDSelf(ICallFrame* aFrame, const ULONG aChildIDIdx,
|
||||
LONG& aOutChildID)
|
||||
{
|
||||
VARIANT paramValue;
|
||||
HRESULT hr = aFrame->GetParam(aChildIDIdx, ¶mValue);
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool isVariant = paramValue.vt & VT_VARIANT;
|
||||
MOZ_ASSERT(isVariant);
|
||||
if (!isVariant) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VARIANT& childID = *(paramValue.pvarVal);
|
||||
if (childID.vt != VT_I4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aOutChildID = childID.lVal;
|
||||
return childID.lVal == CHILDID_SELF;
|
||||
}
|
||||
|
||||
/**
|
||||
* ICallFrame::SetParam always returns E_NOTIMPL, so we'll just have to work
|
||||
* around this by manually poking at the parameter's location on the stack.
|
||||
*/
|
||||
/* static */ HRESULT
|
||||
ChildIDThunk::SetChildIDParam(ICallFrame* aFrame, const ULONG aParamIdx,
|
||||
const LONG aChildID)
|
||||
{
|
||||
void* stackBase = aFrame->GetStackLocation();
|
||||
MOZ_ASSERT(stackBase);
|
||||
if (!stackBase) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
CALLFRAMEPARAMINFO paramInfo;
|
||||
HRESULT hr = aFrame->GetParamInfo(aParamIdx, ¶mInfo);
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// We expect this childID to be a VARIANT in-parameter
|
||||
MOZ_ASSERT(paramInfo.fIn);
|
||||
MOZ_ASSERT(paramInfo.cbParam == sizeof(VARIANT));
|
||||
if (!paramInfo.fIn || paramInfo.cbParam != sizeof(VARIANT)) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Given the stack base and param offset, calculate the address of the param
|
||||
// and replace its value
|
||||
VARIANT* pInParam = reinterpret_cast<VARIANT*>(
|
||||
reinterpret_cast<PBYTE>(stackBase) + paramInfo.stackOffset);
|
||||
MOZ_ASSERT(pInParam->vt == VT_I4);
|
||||
if (pInParam->vt != VT_I4) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
pInParam->lVal = aChildID;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
ChildIDThunk::ResolveTargetInterface(REFIID aIid, IUnknown** aOut)
|
||||
{
|
||||
MOZ_ASSERT(aOut);
|
||||
*aOut = nullptr;
|
||||
|
||||
RefPtr<IInterceptor> interceptor;
|
||||
HRESULT hr = mInterceptor->Resolve(IID_IInterceptor,
|
||||
(void**)getter_AddRefs(interceptor));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
InterceptorTargetPtr target;
|
||||
hr = interceptor->GetTargetForIID(aIid, target);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
RefPtr<IUnknown> refTarget = target.get();
|
||||
refTarget.forget(aOut);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
ChildIDThunk::OnCall(ICallFrame* aFrame)
|
||||
{
|
||||
#if defined(MOZILLA_INTERNAL_API)
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
#endif
|
||||
|
||||
IID iid;
|
||||
ULONG method;
|
||||
HRESULT hr = aFrame->GetIIDAndMethod(&iid, &method);
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
RefPtr<IUnknown> target;
|
||||
hr = ResolveTargetInterface(iid, getter_AddRefs(target));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
Maybe<ULONG> childIDIdx;
|
||||
LONG childID;
|
||||
if (iid != IID_IAccessible ||
|
||||
(childIDIdx = IsMethodInteresting(method)).isNothing() ||
|
||||
IsChildIDSelf(aFrame, childIDIdx.value(), childID)) {
|
||||
// We're only interested in methods which accept a child ID parameter which
|
||||
// is not equal to CHILDID_SELF. Otherwise we just invoke against the
|
||||
// original target interface.
|
||||
hr = aFrame->Invoke(target);
|
||||
if (SUCCEEDED(hr)) {
|
||||
InterceptorLog::Event(aFrame, target);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Otherwise we need to resolve the child node...
|
||||
RefPtr<IAccessible> acc;
|
||||
hr = target->QueryInterface(iid, (void**)getter_AddRefs(acc));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
VARIANT vChildID;
|
||||
VariantInit(&vChildID);
|
||||
vChildID.vt = VT_I4;
|
||||
vChildID.lVal = childID;
|
||||
|
||||
RefPtr<IDispatch> disp;
|
||||
hr = acc->get_accChild(vChildID, getter_AddRefs(disp));
|
||||
if (FAILED(hr)) {
|
||||
aFrame->SetReturnValue(hr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
RefPtr<IAccessible> directAccessible;
|
||||
// Yes, given our implementation we could just downcast, but that's not very COMy
|
||||
hr = disp->QueryInterface(IID_IAccessible,
|
||||
(void**)getter_AddRefs(directAccessible));
|
||||
if (FAILED(hr)) {
|
||||
aFrame->SetReturnValue(hr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Now that we have the IAccessible for the child ID we need to replace it
|
||||
// in the activation record with a self-referant child ID
|
||||
hr = SetChildIDParam(aFrame, childIDIdx.value(), CHILDID_SELF);
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = aFrame->Invoke(directAccessible);
|
||||
if (SUCCEEDED(hr)) {
|
||||
InterceptorLog::Event(aFrame, directAccessible);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
ChildIDThunk::SetInterceptor(IWeakReference* aInterceptor)
|
||||
{
|
||||
mInterceptor = aInterceptor;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_a11y_ChildIDThunk_h
|
||||
#define mozilla_a11y_ChildIDThunk_h
|
||||
|
||||
#include "mozilla/mscom/Interceptor.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
#include <oleacc.h>
|
||||
#include <callobj.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class ChildIDThunk : public mozilla::mscom::IInterceptorSink
|
||||
{
|
||||
public:
|
||||
ChildIDThunk();
|
||||
|
||||
// IUnknown
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
|
||||
STDMETHODIMP_(ULONG) AddRef() override;
|
||||
STDMETHODIMP_(ULONG) Release() override;
|
||||
|
||||
// ICallFrameEvents
|
||||
STDMETHODIMP OnCall(ICallFrame* aFrame) override;
|
||||
|
||||
// IInterceptorSink
|
||||
STDMETHODIMP SetInterceptor(mozilla::mscom::IWeakReference* aInterceptor) override;
|
||||
|
||||
private:
|
||||
HRESULT ResolveTargetInterface(REFIID aIid, IUnknown** aOut);
|
||||
|
||||
static mozilla::Maybe<ULONG> IsMethodInteresting(const ULONG aMethodIdx);
|
||||
static bool IsChildIDSelf(ICallFrame* aFrame, const ULONG aChildIdIdx,
|
||||
LONG& aOutChildID);
|
||||
static HRESULT SetChildIDParam(ICallFrame* aFrame, const ULONG aParamIdx,
|
||||
const LONG aChildID);
|
||||
|
||||
private:
|
||||
ULONG mRefCnt;
|
||||
RefPtr<mozilla::mscom::IWeakReference> mInterceptor;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_a11y_ChildIDThunk_h
|
||||
|
|
@ -5,14 +5,18 @@
|
|||
|
||||
#include "RootAccessibleWrap.h"
|
||||
|
||||
#include "ChildIDThunk.h"
|
||||
#include "Compatibility.h"
|
||||
#include "mozilla/mscom/interceptor.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
#include "nsWinUtils.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
using namespace mozilla::mscom;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor/desctructor
|
||||
// Constructor/destructor
|
||||
|
||||
RootAccessibleWrap::
|
||||
RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell) :
|
||||
|
@ -24,6 +28,59 @@ RootAccessibleWrap::~RootAccessibleWrap()
|
|||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Accessible
|
||||
|
||||
void
|
||||
RootAccessibleWrap::GetNativeInterface(void** aOutAccessible)
|
||||
{
|
||||
MOZ_ASSERT(aOutAccessible);
|
||||
if (!aOutAccessible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mInterceptor &&
|
||||
SUCCEEDED(mInterceptor->Resolve(IID_IAccessible, aOutAccessible))) {
|
||||
return;
|
||||
}
|
||||
|
||||
*aOutAccessible = nullptr;
|
||||
|
||||
RefPtr<IAccessible> rootAccessible;
|
||||
RootAccessible::GetNativeInterface((void**)getter_AddRefs(rootAccessible));
|
||||
|
||||
if (!mozilla::BrowserTabsRemoteAutostart() || XRE_IsContentProcess()) {
|
||||
// We only need to wrap this interface if our process is non-content e10s
|
||||
rootAccessible.forget(aOutAccessible);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, we need to wrap that IAccessible with an interceptor
|
||||
RefPtr<IInterceptorSink> eventSink(MakeAndAddRef<ChildIDThunk>());
|
||||
|
||||
RefPtr<IAccessible> interceptor;
|
||||
HRESULT hr = CreateInterceptor<IAccessible>(
|
||||
STAUniquePtr<IAccessible>(rootAccessible.forget().take()), eventSink,
|
||||
getter_AddRefs(interceptor));
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<IWeakReferenceSource> weakRefSrc;
|
||||
hr = interceptor->QueryInterface(IID_IWeakReferenceSource,
|
||||
(void**)getter_AddRefs(weakRefSrc));
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = weakRefSrc->GetWeakReference(getter_AddRefs(mInterceptor));
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
interceptor.forget(aOutAccessible);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// RootAccessible
|
||||
|
||||
|
|
|
@ -9,6 +9,12 @@
|
|||
#include "RootAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
struct IWeakReference;
|
||||
|
||||
} // namespace mscom
|
||||
|
||||
namespace a11y {
|
||||
|
||||
class RootAccessibleWrap : public RootAccessible
|
||||
|
@ -17,8 +23,14 @@ public:
|
|||
RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell);
|
||||
virtual ~RootAccessibleWrap();
|
||||
|
||||
// Accessible
|
||||
virtual void GetNativeInterface(void** aOutAccessible) override;
|
||||
|
||||
// RootAccessible
|
||||
virtual void DocumentActivated(DocAccessible* aDocument);
|
||||
|
||||
private:
|
||||
RefPtr<mscom::IWeakReference> mInterceptor;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
|
|
@ -19,6 +19,7 @@ UNIFIED_SOURCES += [
|
|||
'AccessibleWrap.cpp',
|
||||
'ApplicationAccessibleWrap.cpp',
|
||||
'ARIAGridAccessibleWrap.cpp',
|
||||
'ChildIDThunk.cpp',
|
||||
'Compatibility.cpp',
|
||||
'DocAccessibleWrap.cpp',
|
||||
'EnumVariant.cpp',
|
||||
|
|
Загрузка…
Ссылка в новой задаче