diff --git a/accessible/windows/msaa/AccessibleWrap.cpp b/accessible/windows/msaa/AccessibleWrap.cpp index ad23d1a56838..342880ca98f0 100644 --- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -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 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 xpRelation; diff --git a/accessible/windows/msaa/ChildIDThunk.cpp b/accessible/windows/msaa/ChildIDThunk.cpp new file mode 100644 index 000000000000..788b643440e3 --- /dev/null +++ b/accessible/windows/msaa/ChildIDThunk.cpp @@ -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(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 +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( + reinterpret_cast(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 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 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 target; + hr = ResolveTargetInterface(iid, getter_AddRefs(target)); + if (FAILED(hr)) { + return hr; + } + + Maybe 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 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 disp; + hr = acc->get_accChild(vChildID, getter_AddRefs(disp)); + if (FAILED(hr)) { + aFrame->SetReturnValue(hr); + return S_OK; + } + + RefPtr 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 + diff --git a/accessible/windows/msaa/ChildIDThunk.h b/accessible/windows/msaa/ChildIDThunk.h new file mode 100644 index 000000000000..515e4042eb4b --- /dev/null +++ b/accessible/windows/msaa/ChildIDThunk.h @@ -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 +#include + +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 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 mInterceptor; +}; + +} // namespace a11y +} // namespace mozilla + +#endif // mozilla_a11y_ChildIDThunk_h + diff --git a/accessible/windows/msaa/RootAccessibleWrap.cpp b/accessible/windows/msaa/RootAccessibleWrap.cpp index 3d0592f1ded4..3061efd6df65 100644 --- a/accessible/windows/msaa/RootAccessibleWrap.cpp +++ b/accessible/windows/msaa/RootAccessibleWrap.cpp @@ -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 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 eventSink(MakeAndAddRef()); + + RefPtr interceptor; + HRESULT hr = CreateInterceptor( + STAUniquePtr(rootAccessible.forget().take()), eventSink, + getter_AddRefs(interceptor)); + if (FAILED(hr)) { + return; + } + + RefPtr 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 diff --git a/accessible/windows/msaa/RootAccessibleWrap.h b/accessible/windows/msaa/RootAccessibleWrap.h index 6aa6fefe38f6..708844afa504 100644 --- a/accessible/windows/msaa/RootAccessibleWrap.h +++ b/accessible/windows/msaa/RootAccessibleWrap.h @@ -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 mInterceptor; }; } // namespace a11y diff --git a/accessible/windows/msaa/moz.build b/accessible/windows/msaa/moz.build index 3143a171c6aa..4cbaacd211e6 100644 --- a/accessible/windows/msaa/moz.build +++ b/accessible/windows/msaa/moz.build @@ -19,6 +19,7 @@ UNIFIED_SOURCES += [ 'AccessibleWrap.cpp', 'ApplicationAccessibleWrap.cpp', 'ARIAGridAccessibleWrap.cpp', + 'ChildIDThunk.cpp', 'Compatibility.cpp', 'DocAccessibleWrap.cpp', 'EnumVariant.cpp',