2016-07-27 20:44:56 +03:00
|
|
|
/* -*- 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/. */
|
|
|
|
|
|
|
|
#define INITGUID
|
|
|
|
|
2017-06-07 02:35:51 +03:00
|
|
|
#include "mozilla/dom/ContentChild.h"
|
2017-03-07 03:22:43 +03:00
|
|
|
#include "mozilla/Move.h"
|
2016-08-12 21:12:48 +03:00
|
|
|
#include "mozilla/mscom/DispatchForwarder.h"
|
2017-08-18 00:54:28 +03:00
|
|
|
#include "mozilla/mscom/FastMarshaler.h"
|
2017-03-07 03:22:43 +03:00
|
|
|
#include "mozilla/mscom/Interceptor.h"
|
|
|
|
#include "mozilla/mscom/InterceptorLog.h"
|
2016-07-27 20:44:56 +03:00
|
|
|
#include "mozilla/mscom/MainThreadInvoker.h"
|
2017-06-07 02:35:51 +03:00
|
|
|
#include "mozilla/mscom/Objref.h"
|
2016-07-27 20:44:56 +03:00
|
|
|
#include "mozilla/mscom/Registration.h"
|
2016-08-30 04:55:37 +03:00
|
|
|
#include "mozilla/mscom/Utils.h"
|
2016-07-27 20:44:56 +03:00
|
|
|
#include "MainThreadUtils.h"
|
|
|
|
#include "mozilla/Assertions.h"
|
|
|
|
#include "mozilla/DebugOnly.h"
|
|
|
|
#include "nsDirectoryServiceDefs.h"
|
|
|
|
#include "nsDirectoryServiceUtils.h"
|
2017-05-18 22:04:26 +03:00
|
|
|
#include "nsRefPtrHashtable.h"
|
2016-07-27 20:44:56 +03:00
|
|
|
#include "nsThreadUtils.h"
|
2017-06-07 02:35:51 +03:00
|
|
|
#include "nsXULAppAPI.h"
|
2016-07-27 20:44:56 +03:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace mscom {
|
2017-06-08 20:52:53 +03:00
|
|
|
namespace detail {
|
2016-07-27 20:44:56 +03:00
|
|
|
|
2017-05-18 22:04:26 +03:00
|
|
|
class LiveSet final
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
LiveSet()
|
|
|
|
: mMutex("mozilla::mscom::LiveSet::mMutex")
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void Lock()
|
|
|
|
{
|
|
|
|
mMutex.Lock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Unlock()
|
|
|
|
{
|
|
|
|
mMutex.Unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Put(IUnknown* aKey, already_AddRefed<IWeakReference> aValue)
|
|
|
|
{
|
|
|
|
mMutex.AssertCurrentThreadOwns();
|
|
|
|
mLiveSet.Put(aKey, Move(aValue));
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<IWeakReference> Get(IUnknown* aKey)
|
|
|
|
{
|
|
|
|
mMutex.AssertCurrentThreadOwns();
|
|
|
|
RefPtr<IWeakReference> result;
|
|
|
|
mLiveSet.Get(aKey, getter_AddRefs(result));
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Remove(IUnknown* aKey)
|
|
|
|
{
|
|
|
|
mMutex.AssertCurrentThreadOwns();
|
|
|
|
mLiveSet.Remove(aKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Mutex mMutex;
|
|
|
|
nsRefPtrHashtable<nsPtrHashKey<IUnknown>, IWeakReference> mLiveSet;
|
|
|
|
};
|
|
|
|
|
2017-06-08 20:52:53 +03:00
|
|
|
/**
|
|
|
|
* We don't use the normal XPCOM BaseAutoLock because we need the ability
|
|
|
|
* to explicitly Unlock.
|
|
|
|
*/
|
|
|
|
class MOZ_RAII LiveSetAutoLock final
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit LiveSetAutoLock(LiveSet& aLiveSet)
|
|
|
|
: mLiveSet(&aLiveSet)
|
|
|
|
{
|
|
|
|
aLiveSet.Lock();
|
|
|
|
}
|
|
|
|
|
|
|
|
~LiveSetAutoLock()
|
|
|
|
{
|
|
|
|
if (mLiveSet) {
|
|
|
|
mLiveSet->Unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Unlock()
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mLiveSet);
|
|
|
|
mLiveSet->Unlock();
|
|
|
|
mLiveSet = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
LiveSetAutoLock(const LiveSetAutoLock& aOther) = delete;
|
|
|
|
LiveSetAutoLock(LiveSetAutoLock&& aOther) = delete;
|
|
|
|
LiveSetAutoLock& operator=(const LiveSetAutoLock& aOther) = delete;
|
|
|
|
LiveSetAutoLock& operator=(LiveSetAutoLock&& aOther) = delete;
|
|
|
|
|
|
|
|
private:
|
|
|
|
LiveSet* mLiveSet;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
static detail::LiveSet&
|
2017-05-18 22:04:26 +03:00
|
|
|
GetLiveSet()
|
|
|
|
{
|
2017-06-08 20:52:53 +03:00
|
|
|
static detail::LiveSet sLiveSet;
|
2017-05-18 22:04:26 +03:00
|
|
|
return sLiveSet;
|
|
|
|
}
|
|
|
|
|
2016-07-27 20:44:56 +03:00
|
|
|
/* static */ HRESULT
|
2016-08-26 19:03:17 +03:00
|
|
|
Interceptor::Create(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink,
|
2017-06-07 03:30:19 +03:00
|
|
|
REFIID aInitialIid, void** aOutInterface)
|
2016-07-27 20:44:56 +03:00
|
|
|
{
|
2017-06-07 03:30:19 +03:00
|
|
|
MOZ_ASSERT(aOutInterface && aTarget && aSink);
|
|
|
|
if (!aOutInterface) {
|
2016-07-27 20:44:56 +03:00
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
2017-02-18 03:35:01 +03:00
|
|
|
|
2017-06-08 20:52:53 +03:00
|
|
|
detail::LiveSetAutoLock lock(GetLiveSet());
|
2017-05-18 22:04:26 +03:00
|
|
|
|
2017-06-08 20:52:53 +03:00
|
|
|
RefPtr<IWeakReference> existingWeak(Move(GetLiveSet().Get(aTarget.get())));
|
|
|
|
if (existingWeak) {
|
|
|
|
RefPtr<IWeakReferenceSource> existingStrong;
|
|
|
|
if (SUCCEEDED(existingWeak->ToStrongRef(getter_AddRefs(existingStrong)))) {
|
|
|
|
// QI on existingStrong may touch other threads. Since we now hold a
|
|
|
|
// strong ref on the interceptor, we may now release the lock.
|
|
|
|
lock.Unlock();
|
|
|
|
return existingStrong->QueryInterface(aInitialIid, aOutInterface);
|
|
|
|
}
|
2017-05-18 22:04:26 +03:00
|
|
|
}
|
|
|
|
|
2017-06-07 03:30:19 +03:00
|
|
|
*aOutInterface = nullptr;
|
2017-02-18 03:35:01 +03:00
|
|
|
|
2016-07-27 20:44:56 +03:00
|
|
|
if (!aTarget || !aSink) {
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
2017-02-18 03:35:01 +03:00
|
|
|
|
2017-06-07 03:30:19 +03:00
|
|
|
RefPtr<Interceptor> intcpt(new Interceptor(aSink));
|
2017-06-08 20:52:53 +03:00
|
|
|
return intcpt->GetInitialInterceptorForIID(lock, aInitialIid, Move(aTarget),
|
2017-06-07 03:30:19 +03:00
|
|
|
aOutInterface);
|
2016-07-27 20:44:56 +03:00
|
|
|
}
|
|
|
|
|
2017-06-07 03:30:19 +03:00
|
|
|
Interceptor::Interceptor(IInterceptorSink* aSink)
|
2016-07-27 20:44:56 +03:00
|
|
|
: WeakReferenceSupport(WeakReferenceSupport::Flags::eDestroyOnMainThread)
|
|
|
|
, mEventSink(aSink)
|
|
|
|
, mMutex("mozilla::mscom::Interceptor::mMutex")
|
2017-02-18 03:30:03 +03:00
|
|
|
, mStdMarshal(nullptr)
|
2016-07-27 20:44:56 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(aSink);
|
|
|
|
RefPtr<IWeakReference> weakRef;
|
|
|
|
if (SUCCEEDED(GetWeakReference(getter_AddRefs(weakRef)))) {
|
|
|
|
aSink->SetInterceptor(weakRef);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Interceptor::~Interceptor()
|
|
|
|
{
|
2017-05-18 22:04:26 +03:00
|
|
|
{ // Scope for lock
|
2017-06-08 20:52:53 +03:00
|
|
|
detail::LiveSetAutoLock lock(GetLiveSet());
|
2017-05-18 22:04:26 +03:00
|
|
|
GetLiveSet().Remove(mTarget.get());
|
|
|
|
}
|
|
|
|
|
2016-07-27 20:44:56 +03:00
|
|
|
// This needs to run on the main thread because it releases target interface
|
|
|
|
// reference counts which may not be thread-safe.
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
for (uint32_t index = 0, len = mInterceptorMap.Length(); index < len; ++index) {
|
|
|
|
MapEntry& entry = mInterceptorMap[index];
|
2016-12-14 09:34:26 +03:00
|
|
|
entry.mInterceptor = nullptr;
|
2016-07-27 20:44:56 +03:00
|
|
|
entry.mTargetInterface->Release();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-18 03:30:03 +03:00
|
|
|
HRESULT
|
|
|
|
Interceptor::GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
|
|
|
|
CLSID* aHandlerClsid)
|
|
|
|
{
|
|
|
|
if (aDestContextPtr || !aHandlerClsid ||
|
|
|
|
aDestContext == MSHCTX_DIFFERENTMACHINE) {
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
2017-06-07 02:35:51 +03:00
|
|
|
|
2017-02-18 03:30:03 +03:00
|
|
|
MOZ_ASSERT(mEventSink);
|
Bug 1303060: Changes to a11y to enable the serving of a COM handler; r=tbsaunde
MozReview-Commit-ID: GTQF3x1pBtX
A general outline of the COM handler (a.k.a. the "smart proxy"):
COM handlers are pieces of code that are loaded by the COM runtime along with
a proxy and are layered above that proxy. This enables the COM handler to
interpose itself between the caller and the proxy, thus providing the
opportunity for the handler to manipulate an interface's method calls before
those calls reach the proxy.
Handlers are regular COM components that live in DLLs and are declared in the
Windows registry. In order to allow for the specifying of a handler (and an
optional payload to be sent with the proxy), the mscom library allows its
clients to specify an implementation of the IHandlerProvider interface.
IHandlerProvider consists of 5 functions:
* GetHandler returns the CLSID of the component that should be loaded into
the COM client's process. If GetHandler returns a failure code, then no
handler is loaded.
* GetHandlerPayloadSize and WriteHandlerPayload are for obtaining the payload
data. These calls are made on a background thread but need to do their work
on the main thread. We declare the payload struct in IDL. MIDL generates two
functions, IA2Payload_Encode and IA2Payload_Decode, which are used by
mscom::StructToStream to read and write that struct to and from buffers.
* The a11y payload struct also includes an interface, IGeckoBackChannel, that
allows the handler to communicate directly with Gecko. IGeckoBackChannel
currently provides two methods: one to allow the handler to request fresh
cache information, and the other to provide Gecko with its IHandlerControl
interface.
* MarshalAs accepts an IID that specifies the interface that is about to be
proxied. We may want to send a more sophisticated proxy than the one that
is requested. The desired IID is returned by this function. In the case of
a11y interfaces, we should always return IAccessible2_3 if we are asked for
one of its parent interfaces. This allows us to eliminate round trips to
resolve more sophisticated interfaces later on.
* NewInstance, which is needed to ensure that all descendent proxies are also
imbued with the same handler code.
The main focus of this patch is as follows:
1. Provide an implementation of the IHandlerProvider interface;
2. Populate the handler payload (ie, the cache) with data;
3. Modify CreateHolderFromAccessible to specify the HandlerPayload object;
4. Receive the IHandlerControl interface from the handler DLL and move it
into the chrome process.
Some more information about IHandlerControl:
There is one IHandlerControl per handler DLL instance. It is the interface that
we call in Gecko when we need to dispatch an event to the handler. In order to
ensure that events are dispatched in the correct order, we need to dispatch
those events from the chrome main thread so that they occur in sequential order
with calls to NotifyWinEvent.
--HG--
extra : rebase_source : acb44dead7cc5488424720e1bf58862b7b30374f
2017-04-05 00:23:55 +03:00
|
|
|
return mEventSink->GetHandler(WrapNotNull(aHandlerClsid));
|
2017-02-18 03:30:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
Interceptor::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
|
|
|
|
void* pvDestContext, DWORD mshlflags,
|
|
|
|
CLSID* pCid)
|
|
|
|
{
|
2017-09-01 19:44:41 +03:00
|
|
|
return mStdMarshal->GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext,
|
|
|
|
mshlflags, pCid);
|
2017-02-18 03:30:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
Interceptor::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
|
|
|
|
void* pvDestContext, DWORD mshlflags,
|
|
|
|
DWORD* pSize)
|
|
|
|
{
|
2017-09-01 19:44:41 +03:00
|
|
|
HRESULT hr = mStdMarshal->GetMarshalSizeMax(riid, pv, dwDestContext,
|
2017-08-18 00:54:28 +03:00
|
|
|
pvDestContext, mshlflags, pSize);
|
2017-02-18 03:30:03 +03:00
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
DWORD payloadSize = 0;
|
Bug 1303060: Changes to a11y to enable the serving of a COM handler; r=tbsaunde
MozReview-Commit-ID: GTQF3x1pBtX
A general outline of the COM handler (a.k.a. the "smart proxy"):
COM handlers are pieces of code that are loaded by the COM runtime along with
a proxy and are layered above that proxy. This enables the COM handler to
interpose itself between the caller and the proxy, thus providing the
opportunity for the handler to manipulate an interface's method calls before
those calls reach the proxy.
Handlers are regular COM components that live in DLLs and are declared in the
Windows registry. In order to allow for the specifying of a handler (and an
optional payload to be sent with the proxy), the mscom library allows its
clients to specify an implementation of the IHandlerProvider interface.
IHandlerProvider consists of 5 functions:
* GetHandler returns the CLSID of the component that should be loaded into
the COM client's process. If GetHandler returns a failure code, then no
handler is loaded.
* GetHandlerPayloadSize and WriteHandlerPayload are for obtaining the payload
data. These calls are made on a background thread but need to do their work
on the main thread. We declare the payload struct in IDL. MIDL generates two
functions, IA2Payload_Encode and IA2Payload_Decode, which are used by
mscom::StructToStream to read and write that struct to and from buffers.
* The a11y payload struct also includes an interface, IGeckoBackChannel, that
allows the handler to communicate directly with Gecko. IGeckoBackChannel
currently provides two methods: one to allow the handler to request fresh
cache information, and the other to provide Gecko with its IHandlerControl
interface.
* MarshalAs accepts an IID that specifies the interface that is about to be
proxied. We may want to send a more sophisticated proxy than the one that
is requested. The desired IID is returned by this function. In the case of
a11y interfaces, we should always return IAccessible2_3 if we are asked for
one of its parent interfaces. This allows us to eliminate round trips to
resolve more sophisticated interfaces later on.
* NewInstance, which is needed to ensure that all descendent proxies are also
imbued with the same handler code.
The main focus of this patch is as follows:
1. Provide an implementation of the IHandlerProvider interface;
2. Populate the handler payload (ie, the cache) with data;
3. Modify CreateHolderFromAccessible to specify the HandlerPayload object;
4. Receive the IHandlerControl interface from the handler DLL and move it
into the chrome process.
Some more information about IHandlerControl:
There is one IHandlerControl per handler DLL instance. It is the interface that
we call in Gecko when we need to dispatch an event to the handler. In order to
ensure that events are dispatched in the correct order, we need to dispatch
those events from the chrome main thread so that they occur in sequential order
with calls to NotifyWinEvent.
--HG--
extra : rebase_source : acb44dead7cc5488424720e1bf58862b7b30374f
2017-04-05 00:23:55 +03:00
|
|
|
hr = mEventSink->GetHandlerPayloadSize(WrapNotNull(&payloadSize));
|
2017-08-02 00:11:34 +03:00
|
|
|
if (hr == E_NOTIMPL) {
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
*pSize += payloadSize;
|
|
|
|
}
|
2017-02-18 03:30:03 +03:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
|
|
|
|
DWORD dwDestContext, void* pvDestContext,
|
|
|
|
DWORD mshlflags)
|
|
|
|
{
|
2017-06-07 02:35:51 +03:00
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
|
|
|
// Save the current stream position
|
|
|
|
LARGE_INTEGER seekTo;
|
|
|
|
seekTo.QuadPart = 0;
|
|
|
|
|
|
|
|
ULARGE_INTEGER objrefPos;
|
|
|
|
|
|
|
|
hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
|
|
|
|
|
|
|
hr = mStdMarshal->MarshalInterface(pStm, riid, pv, dwDestContext,
|
2017-08-18 00:54:28 +03:00
|
|
|
pvDestContext, mshlflags);
|
2017-02-18 03:30:03 +03:00
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2017-06-07 02:35:51 +03:00
|
|
|
#if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
2017-08-18 00:54:28 +03:00
|
|
|
if (XRE_IsContentProcess() && IsCallerExternalProcess()) {
|
|
|
|
// The caller isn't our chrome process, so do not provide a handler.
|
2017-06-07 02:35:51 +03:00
|
|
|
|
2017-08-18 00:54:28 +03:00
|
|
|
// First, save the current position that marks the current end of the
|
|
|
|
// OBJREF in the stream.
|
|
|
|
ULARGE_INTEGER endPos;
|
|
|
|
hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &endPos);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
2017-06-07 02:35:51 +03:00
|
|
|
|
2017-08-18 00:54:28 +03:00
|
|
|
// Now strip out the handler.
|
|
|
|
if (!StripHandlerFromOBJREF(WrapNotNull(pStm), objrefPos.QuadPart,
|
|
|
|
endPos.QuadPart)) {
|
|
|
|
return E_FAIL;
|
2017-06-07 02:35:51 +03:00
|
|
|
}
|
2017-08-18 00:54:28 +03:00
|
|
|
|
|
|
|
return S_OK;
|
2017-06-07 02:35:51 +03:00
|
|
|
}
|
|
|
|
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
|
|
|
|
2017-08-02 00:11:34 +03:00
|
|
|
hr = mEventSink->WriteHandlerPayload(WrapNotNull(pStm));
|
|
|
|
if (hr == E_NOTIMPL) {
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
2017-02-18 03:30:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
Interceptor::UnmarshalInterface(IStream* pStm, REFIID riid,
|
|
|
|
void** ppv)
|
|
|
|
{
|
|
|
|
return mStdMarshal->UnmarshalInterface(pStm, riid, ppv);
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
Interceptor::ReleaseMarshalData(IStream* pStm)
|
|
|
|
{
|
|
|
|
return mStdMarshal->ReleaseMarshalData(pStm);
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
Interceptor::DisconnectObject(DWORD dwReserved)
|
|
|
|
{
|
|
|
|
return mStdMarshal->DisconnectObject(dwReserved);
|
|
|
|
}
|
|
|
|
|
2016-07-27 20:44:56 +03:00
|
|
|
Interceptor::MapEntry*
|
|
|
|
Interceptor::Lookup(REFIID aIid)
|
|
|
|
{
|
|
|
|
mMutex.AssertCurrentThreadOwns();
|
2017-08-31 01:34:43 +03:00
|
|
|
|
2016-07-27 20:44:56 +03:00
|
|
|
for (uint32_t index = 0, len = mInterceptorMap.Length(); index < len; ++index) {
|
|
|
|
if (mInterceptorMap[index].mIID == aIid) {
|
|
|
|
return &mInterceptorMap[index];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
2017-03-07 03:22:43 +03:00
|
|
|
Interceptor::GetTargetForIID(REFIID aIid,
|
|
|
|
InterceptorTargetPtr<IUnknown>& aTarget)
|
2016-07-27 20:44:56 +03:00
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
MapEntry* entry = Lookup(aIid);
|
|
|
|
if (entry) {
|
|
|
|
aTarget.reset(entry->mTargetInterface);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
2017-03-07 03:22:43 +03:00
|
|
|
// CoGetInterceptor requires type metadata to be able to generate its emulated
|
|
|
|
// vtable. If no registered metadata is available, CoGetInterceptor returns
|
|
|
|
// kFileNotFound.
|
|
|
|
static const HRESULT kFileNotFound = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
2016-07-27 20:44:56 +03:00
|
|
|
|
|
|
|
HRESULT
|
|
|
|
Interceptor::CreateInterceptor(REFIID aIid, IUnknown* aOuter, IUnknown** aOutput)
|
|
|
|
{
|
|
|
|
// In order to aggregate, we *must* request IID_IUnknown as the initial
|
|
|
|
// interface for the interceptor, as that IUnknown is non-delegating.
|
|
|
|
// This is a fundamental rule for creating aggregated objects in COM.
|
|
|
|
HRESULT hr = ::CoGetInterceptor(aIid, aOuter, IID_IUnknown, (void**)aOutput);
|
|
|
|
if (hr != kFileNotFound) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// In the case that CoGetInterceptor returns kFileNotFound, we can try to
|
|
|
|
// explicitly load typelib data from our runtime registration facility and
|
|
|
|
// pass that into CoGetInterceptorFromTypeInfo.
|
|
|
|
|
|
|
|
RefPtr<ITypeInfo> typeInfo;
|
|
|
|
bool found = RegisteredProxy::Find(aIid, getter_AddRefs(typeInfo));
|
|
|
|
// If this assert fires then we have omitted registering the typelib for a
|
|
|
|
// required interface. To fix this, review our calls to mscom::RegisterProxy
|
|
|
|
// and mscom::RegisterTypelib, and add the additional typelib as necessary.
|
|
|
|
MOZ_ASSERT(found);
|
|
|
|
if (!found) {
|
|
|
|
return kFileNotFound;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = ::CoGetInterceptorFromTypeInfo(aIid, aOuter, typeInfo, IID_IUnknown,
|
|
|
|
(void**)aOutput);
|
|
|
|
// If this assert fires then the interceptor doesn't like something about
|
|
|
|
// the format of the typelib. One thing in particular that it doesn't like
|
|
|
|
// is complex types that contain unions.
|
|
|
|
MOZ_ASSERT(SUCCEEDED(hr));
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2017-06-07 03:30:19 +03:00
|
|
|
HRESULT
|
2017-08-31 01:34:43 +03:00
|
|
|
Interceptor::PublishTarget(detail::LiveSetAutoLock& aLiveSetLock,
|
|
|
|
RefPtr<IUnknown> aInterceptor,
|
|
|
|
REFIID aTargetIid,
|
|
|
|
STAUniquePtr<IUnknown> aTarget)
|
|
|
|
{
|
|
|
|
RefPtr<IWeakReference> weakRef;
|
|
|
|
HRESULT hr = GetWeakReference(getter_AddRefs(weakRef));
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// mTarget is a weak reference to aTarget. This is safe because we transfer
|
|
|
|
// ownership of aTarget into mInterceptorMap which remains live for the
|
|
|
|
// lifetime of this Interceptor.
|
|
|
|
mTarget = ToInterceptorTargetPtr(aTarget);
|
|
|
|
GetLiveSet().Put(mTarget.get(), weakRef.forget());
|
|
|
|
|
|
|
|
// Now we transfer aTarget's ownership into mInterceptorMap.
|
|
|
|
mInterceptorMap.AppendElement(MapEntry(aTargetIid,
|
|
|
|
aInterceptor,
|
|
|
|
aTarget.release()));
|
|
|
|
|
|
|
|
// Release the live set lock because subsequent operations may post work to
|
|
|
|
// the main thread, creating potential for deadlocks.
|
|
|
|
aLiveSetLock.Unlock();
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
Interceptor::GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLiveSetLock,
|
2017-06-08 20:52:53 +03:00
|
|
|
REFIID aTargetIid,
|
2017-06-07 03:30:19 +03:00
|
|
|
STAUniquePtr<IUnknown> aTarget,
|
|
|
|
void** aOutInterceptor)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aOutInterceptor);
|
2017-08-31 01:34:43 +03:00
|
|
|
MOZ_ASSERT(aTargetIid != IID_IMarshal);
|
2017-06-07 03:30:19 +03:00
|
|
|
MOZ_ASSERT(!IsProxy(aTarget.get()));
|
|
|
|
|
2017-08-31 01:34:43 +03:00
|
|
|
if (aTargetIid == IID_IUnknown) {
|
|
|
|
// We must lock ourselves so that nothing can race with us once we have been
|
|
|
|
// published to the live set.
|
|
|
|
AutoLock lock(*this);
|
|
|
|
|
|
|
|
HRESULT hr = PublishTarget(aLiveSetLock, nullptr, aTargetIid, Move(aTarget));
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QueryInterface(aTargetIid, aOutInterceptor);
|
|
|
|
}
|
|
|
|
|
2017-06-07 03:30:19 +03:00
|
|
|
// Raise the refcount for stabilization purposes during aggregation
|
|
|
|
RefPtr<IUnknown> kungFuDeathGrip(static_cast<IUnknown*>(
|
|
|
|
static_cast<WeakReferenceSupport*>(this)));
|
|
|
|
|
|
|
|
RefPtr<IUnknown> unkInterceptor;
|
|
|
|
HRESULT hr = CreateInterceptor(aTargetIid, kungFuDeathGrip,
|
|
|
|
getter_AddRefs(unkInterceptor));
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<ICallInterceptor> interceptor;
|
|
|
|
hr = unkInterceptor->QueryInterface(IID_ICallInterceptor,
|
|
|
|
getter_AddRefs(interceptor));
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = interceptor->RegisterSink(mEventSink);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2017-08-31 01:34:43 +03:00
|
|
|
// We must lock ourselves so that nothing can race with us once we have been
|
|
|
|
// published to the live set.
|
|
|
|
AutoLock lock(*this);
|
|
|
|
|
|
|
|
hr = PublishTarget(aLiveSetLock, unkInterceptor, aTargetIid, Move(aTarget));
|
2017-05-18 22:04:26 +03:00
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2017-09-01 19:44:41 +03:00
|
|
|
if (mEventSink->MarshalAs(aTargetIid) == aTargetIid) {
|
2017-06-07 03:30:19 +03:00
|
|
|
return unkInterceptor->QueryInterface(aTargetIid, aOutInterceptor);
|
|
|
|
}
|
|
|
|
|
|
|
|
return GetInterceptorForIID(aTargetIid, aOutInterceptor);
|
|
|
|
}
|
|
|
|
|
2016-07-27 20:44:56 +03:00
|
|
|
/**
|
|
|
|
* This method contains the core guts of the handling of QueryInterface calls
|
|
|
|
* that are delegated to us from the ICallInterceptor.
|
|
|
|
*
|
|
|
|
* @param aIid ID of the desired interface
|
|
|
|
* @param aOutInterceptor The resulting emulated vtable that corresponds to
|
|
|
|
* the interface specified by aIid.
|
|
|
|
*/
|
|
|
|
HRESULT
|
|
|
|
Interceptor::GetInterceptorForIID(REFIID aIid, void** aOutInterceptor)
|
|
|
|
{
|
|
|
|
if (!aOutInterceptor) {
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
2016-08-18 18:48:48 +03:00
|
|
|
if (aIid == IID_IUnknown) {
|
|
|
|
// Special case: When we see IUnknown, we just provide a reference to this
|
2017-02-18 03:30:03 +03:00
|
|
|
RefPtr<IInterceptor> intcpt(this);
|
|
|
|
intcpt.forget(aOutInterceptor);
|
2016-08-18 18:48:48 +03:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2017-09-01 19:44:41 +03:00
|
|
|
REFIID interceptorIid = mEventSink->MarshalAs(aIid);
|
2017-02-18 03:30:03 +03:00
|
|
|
|
2016-07-27 20:44:56 +03:00
|
|
|
RefPtr<IUnknown> unkInterceptor;
|
|
|
|
IUnknown* interfaceForQILog = nullptr;
|
|
|
|
|
2017-02-18 03:30:03 +03:00
|
|
|
// (1) Check to see if we already have an existing interceptor for
|
|
|
|
// interceptorIid.
|
2016-07-27 20:44:56 +03:00
|
|
|
|
|
|
|
{ // Scope for lock
|
|
|
|
MutexAutoLock lock(mMutex);
|
2017-02-18 03:30:03 +03:00
|
|
|
MapEntry* entry = Lookup(interceptorIid);
|
2016-07-27 20:44:56 +03:00
|
|
|
if (entry) {
|
|
|
|
unkInterceptor = entry->mInterceptor;
|
|
|
|
interfaceForQILog = entry->mTargetInterface;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// (1a) A COM interceptor already exists for this interface, so all we need
|
|
|
|
// to do is run a QI on it.
|
|
|
|
if (unkInterceptor) {
|
|
|
|
// Technically we didn't actually execute a QI on the target interface, but
|
|
|
|
// for logging purposes we would like to record the fact that this interface
|
|
|
|
// was requested.
|
|
|
|
InterceptorLog::QI(S_OK, mTarget.get(), aIid, interfaceForQILog);
|
|
|
|
|
2017-02-18 03:30:03 +03:00
|
|
|
return unkInterceptor->QueryInterface(interceptorIid, aOutInterceptor);
|
2016-07-27 20:44:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// (2) Obtain a new target interface.
|
|
|
|
|
|
|
|
// (2a) First, make sure that the target interface is available
|
|
|
|
// NB: We *MUST* query the correct interface! ICallEvents::Invoke casts its
|
|
|
|
// pvReceiver argument directly to the required interface! DO NOT assume
|
|
|
|
// that COM will use QI or upcast/downcast!
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
STAUniquePtr<IUnknown> targetInterface;
|
|
|
|
IUnknown* rawTargetInterface = nullptr;
|
2017-02-18 03:30:03 +03:00
|
|
|
hr = QueryInterfaceTarget(interceptorIid, (void**)&rawTargetInterface);
|
2016-07-27 20:44:56 +03:00
|
|
|
targetInterface.reset(rawTargetInterface);
|
|
|
|
InterceptorLog::QI(hr, mTarget.get(), aIid, targetInterface.get());
|
|
|
|
MOZ_ASSERT(SUCCEEDED(hr) || hr == E_NOINTERFACE);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We *really* shouldn't be adding interceptors to proxies
|
|
|
|
MOZ_ASSERT(aIid != IID_IMarshal);
|
|
|
|
|
|
|
|
// (3) Create a new COM interceptor to that interface that delegates its
|
|
|
|
// IUnknown to |this|.
|
|
|
|
|
|
|
|
// Raise the refcount for stabilization purposes during aggregation
|
|
|
|
RefPtr<IUnknown> kungFuDeathGrip(static_cast<IUnknown*>(
|
|
|
|
static_cast<WeakReferenceSupport*>(this)));
|
|
|
|
|
2017-02-18 03:30:03 +03:00
|
|
|
hr = CreateInterceptor(interceptorIid, kungFuDeathGrip,
|
|
|
|
getter_AddRefs(unkInterceptor));
|
2016-07-27 20:44:56 +03:00
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// (4) Obtain the interceptor's ICallInterceptor interface and register our
|
|
|
|
// event sink.
|
|
|
|
RefPtr<ICallInterceptor> interceptor;
|
|
|
|
hr = unkInterceptor->QueryInterface(IID_ICallInterceptor,
|
|
|
|
(void**)getter_AddRefs(interceptor));
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = interceptor->RegisterSink(mEventSink);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// (5) Now that we have this new COM interceptor, insert it into the map.
|
|
|
|
|
|
|
|
{ // Scope for lock
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
// We might have raced with another thread, so first check that we don't
|
|
|
|
// already have an entry for this
|
2017-02-18 03:30:03 +03:00
|
|
|
MapEntry* entry = Lookup(interceptorIid);
|
2016-07-27 20:44:56 +03:00
|
|
|
if (entry && entry->mInterceptor) {
|
|
|
|
unkInterceptor = entry->mInterceptor;
|
|
|
|
} else {
|
2016-12-14 09:34:26 +03:00
|
|
|
// MapEntry has a RefPtr to unkInterceptor, OTOH we must not touch the
|
|
|
|
// refcount for the target interface because we are just moving it into
|
|
|
|
// the map and its refcounting might not be thread-safe.
|
2016-07-27 20:44:56 +03:00
|
|
|
IUnknown* rawTargetInterface = targetInterface.release();
|
2017-02-18 03:30:03 +03:00
|
|
|
mInterceptorMap.AppendElement(MapEntry(interceptorIid,
|
2016-07-27 20:44:56 +03:00
|
|
|
unkInterceptor,
|
|
|
|
rawTargetInterface));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-18 03:30:03 +03:00
|
|
|
return unkInterceptor->QueryInterface(interceptorIid, aOutInterceptor);
|
2016-07-27 20:44:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
Interceptor::QueryInterfaceTarget(REFIID aIid, void** aOutput)
|
|
|
|
{
|
|
|
|
// NB: This QI needs to run on the main thread because the target object
|
|
|
|
// is probably Gecko code that is not thread-safe. Note that this main
|
|
|
|
// thread invocation is *synchronous*.
|
|
|
|
MainThreadInvoker invoker;
|
|
|
|
HRESULT hr;
|
|
|
|
auto runOnMainThread = [&]() -> void {
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
hr = mTarget->QueryInterface(aIid, aOutput);
|
|
|
|
};
|
2017-06-12 22:34:10 +03:00
|
|
|
if (!invoker.Invoke(NS_NewRunnableFunction("Interceptor::QueryInterface", runOnMainThread))) {
|
2016-07-27 20:44:56 +03:00
|
|
|
return E_FAIL;
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
Interceptor::QueryInterface(REFIID riid, void** ppv)
|
|
|
|
{
|
|
|
|
return WeakReferenceSupport::QueryInterface(riid, ppv);
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
|
|
Interceptor::ThreadSafeQueryInterface(REFIID aIid, IUnknown** aOutInterface)
|
|
|
|
{
|
2017-02-18 03:30:03 +03:00
|
|
|
if (aIid == IID_INoMarshal) {
|
|
|
|
// This entire library is designed around marshaling, so there's no point
|
|
|
|
// propagating this QI request all over the place!
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aIid == IID_IStdMarshalInfo) {
|
|
|
|
// Do not indicate that this interface is available unless we actually
|
|
|
|
// support it. We'll check that by looking for a successful call to
|
|
|
|
// IInterceptorSink::GetHandler()
|
|
|
|
CLSID dummy;
|
Bug 1303060: Changes to a11y to enable the serving of a COM handler; r=tbsaunde
MozReview-Commit-ID: GTQF3x1pBtX
A general outline of the COM handler (a.k.a. the "smart proxy"):
COM handlers are pieces of code that are loaded by the COM runtime along with
a proxy and are layered above that proxy. This enables the COM handler to
interpose itself between the caller and the proxy, thus providing the
opportunity for the handler to manipulate an interface's method calls before
those calls reach the proxy.
Handlers are regular COM components that live in DLLs and are declared in the
Windows registry. In order to allow for the specifying of a handler (and an
optional payload to be sent with the proxy), the mscom library allows its
clients to specify an implementation of the IHandlerProvider interface.
IHandlerProvider consists of 5 functions:
* GetHandler returns the CLSID of the component that should be loaded into
the COM client's process. If GetHandler returns a failure code, then no
handler is loaded.
* GetHandlerPayloadSize and WriteHandlerPayload are for obtaining the payload
data. These calls are made on a background thread but need to do their work
on the main thread. We declare the payload struct in IDL. MIDL generates two
functions, IA2Payload_Encode and IA2Payload_Decode, which are used by
mscom::StructToStream to read and write that struct to and from buffers.
* The a11y payload struct also includes an interface, IGeckoBackChannel, that
allows the handler to communicate directly with Gecko. IGeckoBackChannel
currently provides two methods: one to allow the handler to request fresh
cache information, and the other to provide Gecko with its IHandlerControl
interface.
* MarshalAs accepts an IID that specifies the interface that is about to be
proxied. We may want to send a more sophisticated proxy than the one that
is requested. The desired IID is returned by this function. In the case of
a11y interfaces, we should always return IAccessible2_3 if we are asked for
one of its parent interfaces. This allows us to eliminate round trips to
resolve more sophisticated interfaces later on.
* NewInstance, which is needed to ensure that all descendent proxies are also
imbued with the same handler code.
The main focus of this patch is as follows:
1. Provide an implementation of the IHandlerProvider interface;
2. Populate the handler payload (ie, the cache) with data;
3. Modify CreateHolderFromAccessible to specify the HandlerPayload object;
4. Receive the IHandlerControl interface from the handler DLL and move it
into the chrome process.
Some more information about IHandlerControl:
There is one IHandlerControl per handler DLL instance. It is the interface that
we call in Gecko when we need to dispatch an event to the handler. In order to
ensure that events are dispatched in the correct order, we need to dispatch
those events from the chrome main thread so that they occur in sequential order
with calls to NotifyWinEvent.
--HG--
extra : rebase_source : acb44dead7cc5488424720e1bf58862b7b30374f
2017-04-05 00:23:55 +03:00
|
|
|
if (FAILED(mEventSink->GetHandler(WrapNotNull(&dummy)))) {
|
2017-02-18 03:30:03 +03:00
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<IStdMarshalInfo> std(this);
|
|
|
|
std.forget(aOutInterface);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aIid == IID_IMarshal) {
|
2017-08-18 00:54:28 +03:00
|
|
|
HRESULT hr;
|
|
|
|
|
2017-02-18 03:30:03 +03:00
|
|
|
if (!mStdMarshalUnk) {
|
2017-08-18 00:54:28 +03:00
|
|
|
if (XRE_IsContentProcess()) {
|
|
|
|
hr = FastMarshaler::Create(static_cast<IWeakReferenceSource*>(this),
|
|
|
|
getter_AddRefs(mStdMarshalUnk));
|
|
|
|
} else {
|
|
|
|
hr = ::CoGetStdMarshalEx(static_cast<IWeakReferenceSource*>(this),
|
|
|
|
SMEXF_SERVER, getter_AddRefs(mStdMarshalUnk));
|
|
|
|
}
|
|
|
|
|
2017-02-18 03:30:03 +03:00
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mStdMarshal) {
|
2017-08-18 00:54:28 +03:00
|
|
|
hr = mStdMarshalUnk->QueryInterface(IID_IMarshal, (void**)&mStdMarshal);
|
2017-02-18 03:30:03 +03:00
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// mStdMarshal is weak, so drop its refcount
|
|
|
|
mStdMarshal->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<IMarshal> marshal(this);
|
|
|
|
marshal.forget(aOutInterface);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2016-07-27 20:44:56 +03:00
|
|
|
if (aIid == IID_IInterceptor) {
|
2017-02-18 03:30:03 +03:00
|
|
|
RefPtr<IInterceptor> intcpt(this);
|
|
|
|
intcpt.forget(aOutInterface);
|
2016-07-27 20:44:56 +03:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2016-08-12 21:12:48 +03:00
|
|
|
if (aIid == IID_IDispatch) {
|
|
|
|
STAUniquePtr<IDispatch> disp;
|
|
|
|
IDispatch* rawDisp = nullptr;
|
|
|
|
HRESULT hr = QueryInterfaceTarget(aIid, (void**)&rawDisp);
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
disp.reset(rawDisp);
|
|
|
|
return DispatchForwarder::Create(this, disp, aOutInterface);
|
|
|
|
}
|
|
|
|
|
2016-07-27 20:44:56 +03:00
|
|
|
return GetInterceptorForIID(aIid, (void**)aOutInterface);
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
|
|
Interceptor::AddRef()
|
|
|
|
{
|
|
|
|
return WeakReferenceSupport::AddRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
|
|
Interceptor::Release()
|
|
|
|
{
|
|
|
|
return WeakReferenceSupport::Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace mscom
|
|
|
|
} // namespace mozilla
|