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:
Aaron Klotz 2016-08-18 00:50:03 -06:00
Родитель 05bd3da901
Коммит cfad6766e2
6 изменённых файлов: 407 добавлений и 17 удалений

Просмотреть файл

@ -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, &paramValue);
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, &paramInfo);
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',