1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_mscom_Interceptor_h
8 #define mozilla_mscom_Interceptor_h
15 #include "mozilla/Mutex.h"
16 #include "mozilla/RefPtr.h"
17 #include "mozilla/mscom/IHandlerProvider.h"
18 #include "mozilla/mscom/Ptr.h"
19 #include "mozilla/mscom/WeakRef.h"
26 class LiveSetAutoLock
;
30 // {8831EB53-A937-42BC-9921-B3E1121FDF86}
31 DEFINE_GUID(IID_IInterceptorSink
, 0x8831eb53, 0xa937, 0x42bc, 0x99, 0x21, 0xb3,
32 0xe1, 0x12, 0x1f, 0xdf, 0x86);
34 struct IInterceptorSink
: public ICallFrameEvents
, public HandlerProvider
{
35 virtual STDMETHODIMP
SetInterceptor(IWeakReference
* aInterceptor
) = 0;
38 // {3710799B-ECA2-4165-B9B0-3FA1E4A9B230}
39 DEFINE_GUID(IID_IInterceptor
, 0x3710799b, 0xeca2, 0x4165, 0xb9, 0xb0, 0x3f,
40 0xa1, 0xe4, 0xa9, 0xb2, 0x30);
42 struct IInterceptor
: public IUnknown
{
43 virtual STDMETHODIMP
GetTargetForIID(
44 REFIID aIid
, InterceptorTargetPtr
<IUnknown
>& aTarget
) = 0;
45 virtual STDMETHODIMP
GetInterceptorForIID(REFIID aIid
,
46 void** aOutInterceptor
) = 0;
47 virtual STDMETHODIMP
GetEventSink(IInterceptorSink
** aSink
) = 0;
51 * The COM interceptor is the core functionality in mscom that allows us to
52 * redirect method calls to different threads. It emulates the vtable of a
53 * target interface. When a call is made on this emulated vtable, the call is
54 * packaged up into an instance of the ICallFrame interface which may be passed
55 * to other contexts for execution.
57 * In order to accomplish this, COM itself provides the CoGetInterceptor
58 * function, which instantiates an ICallInterceptor. Note, however, that
59 * ICallInterceptor only works on a single interface; we need to be able to
60 * interpose QueryInterface calls so that we can instantiate a new
61 * ICallInterceptor for each new interface that is requested.
63 * We accomplish this by using COM aggregation, which means that the
64 * ICallInterceptor delegates its IUnknown implementation to its outer object
65 * (the mscom::Interceptor we implement and control).
67 * ACHTUNG! mscom::Interceptor uses FastMarshaler to disable COM garbage
68 * collection. If you use this class, you *must* call
69 * Interceptor::DisconnectRemotesForTarget when an object is no longer relevant.
70 * Otherwise, the object will never be released, causing a leak.
72 class Interceptor final
: public WeakReferenceSupport
,
73 public IStdMarshalInfo
,
77 static HRESULT
Create(STAUniquePtr
<IUnknown
> aTarget
, IInterceptorSink
* aSink
,
78 REFIID aInitialIid
, void** aOutInterface
);
81 * Disconnect all remote clients for a given target.
82 * Because Interceptors disable COM garbage collection to improve
83 * performance, they never receive Release calls from remote clients. If
84 * the object can be shut down while clients still hold a reference, this
85 * function can be used to force COM to disconnect all remote connections
86 * (using CoDisconnectObject) and thus release the associated references to
87 * the Interceptor, its target and any objects associated with the
89 * Note that the specified target must be the same IUnknown pointer used to
90 * create the Interceptor. Where there is multiple inheritance, querying for
91 * IID_IUnknown and calling this function with that pointer alone will not
92 * disconnect remotes for all interfaces. If you expect that the same object
93 * may be fetched with different initial interfaces, you should call this
94 * function once for each possible IUnknown pointer.
95 * @return S_OK if there was an Interceptor for the given target,
96 * S_FALSE if there was not.
98 static HRESULT
DisconnectRemotesForTarget(IUnknown
* aTarget
);
101 STDMETHODIMP
QueryInterface(REFIID riid
, void** ppv
) override
;
102 STDMETHODIMP_(ULONG
) AddRef() override
;
103 STDMETHODIMP_(ULONG
) Release() override
;
106 STDMETHODIMP
GetClassForHandler(DWORD aDestContext
, void* aDestContextPtr
,
107 CLSID
* aHandlerClsid
) override
;
110 STDMETHODIMP
GetUnmarshalClass(REFIID riid
, void* pv
, DWORD dwDestContext
,
111 void* pvDestContext
, DWORD mshlflags
,
112 CLSID
* pCid
) override
;
113 STDMETHODIMP
GetMarshalSizeMax(REFIID riid
, void* pv
, DWORD dwDestContext
,
114 void* pvDestContext
, DWORD mshlflags
,
115 DWORD
* pSize
) override
;
116 STDMETHODIMP
MarshalInterface(IStream
* pStm
, REFIID riid
, void* pv
,
117 DWORD dwDestContext
, void* pvDestContext
,
118 DWORD mshlflags
) override
;
119 STDMETHODIMP
UnmarshalInterface(IStream
* pStm
, REFIID riid
,
120 void** ppv
) override
;
121 STDMETHODIMP
ReleaseMarshalData(IStream
* pStm
) override
;
122 STDMETHODIMP
DisconnectObject(DWORD dwReserved
) override
;
125 STDMETHODIMP
GetTargetForIID(
126 REFIID aIid
, InterceptorTargetPtr
<IUnknown
>& aTarget
) override
;
127 STDMETHODIMP
GetInterceptorForIID(REFIID aIid
,
128 void** aOutInterceptor
) override
;
130 STDMETHODIMP
GetEventSink(IInterceptorSink
** aSink
) override
{
131 RefPtr
<IInterceptorSink
> sink
= mEventSink
;
133 return mEventSink
? S_OK
: S_FALSE
;
138 MapEntry(REFIID aIid
, IUnknown
* aInterceptor
, IUnknown
* aTargetInterface
)
140 mInterceptor(aInterceptor
),
141 mTargetInterface(aTargetInterface
) {}
144 RefPtr
<IUnknown
> mInterceptor
;
145 IUnknown
* mTargetInterface
;
149 explicit Interceptor(IInterceptorSink
* aSink
);
151 HRESULT
GetInitialInterceptorForIID(detail::LiveSetAutoLock
& aLiveSetLock
,
153 STAUniquePtr
<IUnknown
> aTarget
,
154 void** aOutInterface
);
155 HRESULT
GetInterceptorForIID(REFIID aIid
, void** aOutInterceptor
,
156 MutexAutoLock
* aAlreadyLocked
);
157 MapEntry
* Lookup(REFIID aIid
);
158 HRESULT
QueryInterfaceTarget(REFIID aIid
, void** aOutput
,
159 TimeDuration
* aOutDuration
= nullptr);
160 HRESULT
WeakRefQueryInterface(REFIID aIid
, IUnknown
** aOutInterface
) override
;
161 HRESULT
CreateInterceptor(REFIID aIid
, IUnknown
* aOuter
, IUnknown
** aOutput
);
162 REFIID
MarshalAs(REFIID aIid
) const;
163 HRESULT
PublishTarget(detail::LiveSetAutoLock
& aLiveSetLock
,
164 RefPtr
<IUnknown
> aInterceptor
, REFIID aTargetIid
,
165 STAUniquePtr
<IUnknown
> aTarget
);
168 InterceptorTargetPtr
<IUnknown
> mTarget
;
169 RefPtr
<IInterceptorSink
> mEventSink
;
170 mozilla::Mutex mInterceptorMapMutex
171 MOZ_UNANNOTATED
; // Guards mInterceptorMap
172 // Using a nsTArray since the # of interfaces is not going to be very high
173 nsTArray
<MapEntry
> mInterceptorMap
;
174 mozilla::Mutex mStdMarshalMutex
175 MOZ_UNANNOTATED
; // Guards mStdMarshalUnk and mStdMarshal
176 RefPtr
<IUnknown
> mStdMarshalUnk
;
177 IMarshal
* mStdMarshal
; // WEAK
178 static MOZ_THREAD_LOCAL(bool) tlsCreatingStdMarshal
;
181 template <typename InterfaceT
>
182 inline HRESULT
CreateInterceptor(STAUniquePtr
<InterfaceT
> aTargetInterface
,
183 IInterceptorSink
* aEventSink
,
184 InterfaceT
** aOutInterface
) {
185 if (!aTargetInterface
|| !aEventSink
) {
189 REFIID iidTarget
= __uuidof(InterfaceT
);
191 STAUniquePtr
<IUnknown
> targetUnknown(aTargetInterface
.release());
192 return Interceptor::Create(std::move(targetUnknown
), aEventSink
, iidTarget
,
193 (void**)aOutInterface
);
197 } // namespace mozilla
199 #endif // mozilla_mscom_Interceptor_h