Bug 1761357 [wpt PR 33355] - Fix #33204: Move Safari stable runs to Big Sur, a=testonly
[gecko.git] / ipc / mscom / Interceptor.h
blob8ab578092bd3fbdff4e52934f9150d097af6f3ae
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
10 #include <callobj.h>
11 #include <objidl.h>
13 #include <utility>
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"
20 #include "nsTArray.h"
22 namespace mozilla {
23 namespace mscom {
24 namespace detail {
26 class LiveSetAutoLock;
28 } // namespace detail
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;
50 /**
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,
74 public IMarshal,
75 public IInterceptor {
76 public:
77 static HRESULT Create(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink,
78 REFIID aInitialIid, void** aOutInterface);
80 /**
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
88 * HandlerProvider.
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);
100 // IUnknown
101 STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
102 STDMETHODIMP_(ULONG) AddRef() override;
103 STDMETHODIMP_(ULONG) Release() override;
105 // IStdMarshalInfo
106 STDMETHODIMP GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
107 CLSID* aHandlerClsid) override;
109 // IMarshal
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;
124 // IInterceptor
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;
132 sink.forget(aSink);
133 return mEventSink ? S_OK : S_FALSE;
136 private:
137 struct MapEntry {
138 MapEntry(REFIID aIid, IUnknown* aInterceptor, IUnknown* aTargetInterface)
139 : mIID(aIid),
140 mInterceptor(aInterceptor),
141 mTargetInterface(aTargetInterface) {}
143 IID mIID;
144 RefPtr<IUnknown> mInterceptor;
145 IUnknown* mTargetInterface;
148 private:
149 explicit Interceptor(IInterceptorSink* aSink);
150 ~Interceptor();
151 HRESULT GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLiveSetLock,
152 REFIID aTargetIid,
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);
167 private:
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) {
186 return E_INVALIDARG;
189 REFIID iidTarget = __uuidof(InterfaceT);
191 STAUniquePtr<IUnknown> targetUnknown(aTargetInterface.release());
192 return Interceptor::Create(std::move(targetUnknown), aEventSink, iidTarget,
193 (void**)aOutInterface);
196 } // namespace mscom
197 } // namespace mozilla
199 #endif // mozilla_mscom_Interceptor_h