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/. */
6 
7 #ifndef mozilla_mscom_Interceptor_h
8 #define mozilla_mscom_Interceptor_h
9 
10 #include <callobj.h>
11 #include <objidl.h>
12 
13 #include <utility>
14 
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"
21 
22 namespace mozilla {
23 namespace mscom {
24 namespace detail {
25 
26 class LiveSetAutoLock;
27 
28 }  // namespace detail
29 
30 // {8831EB53-A937-42BC-9921-B3E1121FDF86}
31 DEFINE_GUID(IID_IInterceptorSink, 0x8831eb53, 0xa937, 0x42bc, 0x99, 0x21, 0xb3,
32             0xe1, 0x12, 0x1f, 0xdf, 0x86);
33 
34 struct IInterceptorSink : public ICallFrameEvents, public HandlerProvider {
35   virtual STDMETHODIMP SetInterceptor(IWeakReference* aInterceptor) = 0;
36 };
37 
38 // {3710799B-ECA2-4165-B9B0-3FA1E4A9B230}
39 DEFINE_GUID(IID_IInterceptor, 0x3710799b, 0xeca2, 0x4165, 0xb9, 0xb0, 0x3f,
40             0xa1, 0xe4, 0xa9, 0xb2, 0x30);
41 
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;
48 };
49 
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.
56  *
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.
62  *
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).
66  */
67 class Interceptor final : public WeakReferenceSupport,
68                           public IStdMarshalInfo,
69                           public IMarshal,
70                           public IInterceptor {
71  public:
72   static HRESULT Create(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink,
73                         REFIID aInitialIid, void** aOutInterface);
74 
75   /**
76    * Disconnect all remote clients for a given target.
77    * Because Interceptors disable COM garbage collection to improve
78    * performance, they never receive Release calls from remote clients. If
79    * the object can be shut down while clients still hold a reference, this
80    * function can be used to force COM to disconnect all remote connections
81    * (using CoDisconnectObject) and thus release the associated references to
82    * the Interceptor, its target and any objects associated with the
83    * HandlerProvider.
84    * Note that the specified target must be the same IUnknown pointer used to
85    * create the Interceptor. Where there is multiple inheritance, querying for
86    * IID_IUnknown and calling this function with that pointer alone will not
87    * disconnect remotes for all interfaces. If you expect that the same object
88    * may be fetched with different initial interfaces, you should call this
89    * function once for each possible IUnknown pointer.
90    * @return S_OK if there was an Interceptor for the given target,
91    *         S_FALSE if there was not.
92    */
93   static HRESULT DisconnectRemotesForTarget(IUnknown* aTarget);
94 
95   // IUnknown
96   STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
97   STDMETHODIMP_(ULONG) AddRef() override;
98   STDMETHODIMP_(ULONG) Release() override;
99 
100   // IStdMarshalInfo
101   STDMETHODIMP GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
102                                   CLSID* aHandlerClsid) override;
103 
104   // IMarshal
105   STDMETHODIMP GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
106                                  void* pvDestContext, DWORD mshlflags,
107                                  CLSID* pCid) override;
108   STDMETHODIMP GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
109                                  void* pvDestContext, DWORD mshlflags,
110                                  DWORD* pSize) override;
111   STDMETHODIMP MarshalInterface(IStream* pStm, REFIID riid, void* pv,
112                                 DWORD dwDestContext, void* pvDestContext,
113                                 DWORD mshlflags) override;
114   STDMETHODIMP UnmarshalInterface(IStream* pStm, REFIID riid,
115                                   void** ppv) override;
116   STDMETHODIMP ReleaseMarshalData(IStream* pStm) override;
117   STDMETHODIMP DisconnectObject(DWORD dwReserved) override;
118 
119   // IInterceptor
120   STDMETHODIMP GetTargetForIID(
121       REFIID aIid, InterceptorTargetPtr<IUnknown>& aTarget) override;
122   STDMETHODIMP GetInterceptorForIID(REFIID aIid,
123                                     void** aOutInterceptor) override;
124 
GetEventSink(IInterceptorSink ** aSink)125   STDMETHODIMP GetEventSink(IInterceptorSink** aSink) override {
126     RefPtr<IInterceptorSink> sink = mEventSink;
127     sink.forget(aSink);
128     return mEventSink ? S_OK : S_FALSE;
129   }
130 
131  private:
132   struct MapEntry {
MapEntryMapEntry133     MapEntry(REFIID aIid, IUnknown* aInterceptor, IUnknown* aTargetInterface)
134         : mIID(aIid),
135           mInterceptor(aInterceptor),
136           mTargetInterface(aTargetInterface) {}
137 
138     IID mIID;
139     RefPtr<IUnknown> mInterceptor;
140     IUnknown* mTargetInterface;
141   };
142 
143  private:
144   explicit Interceptor(IInterceptorSink* aSink);
145   ~Interceptor();
146   HRESULT GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLiveSetLock,
147                                       REFIID aTargetIid,
148                                       STAUniquePtr<IUnknown> aTarget,
149                                       void** aOutInterface);
150   HRESULT GetInterceptorForIID(REFIID aIid, void** aOutInterceptor,
151                                MutexAutoLock* aAlreadyLocked);
152   MapEntry* Lookup(REFIID aIid);
153   HRESULT QueryInterfaceTarget(REFIID aIid, void** aOutput,
154                                TimeDuration* aOutDuration = nullptr);
155   HRESULT WeakRefQueryInterface(REFIID aIid, IUnknown** aOutInterface) override;
156   HRESULT CreateInterceptor(REFIID aIid, IUnknown* aOuter, IUnknown** aOutput);
157   REFIID MarshalAs(REFIID aIid) const;
158   HRESULT PublishTarget(detail::LiveSetAutoLock& aLiveSetLock,
159                         RefPtr<IUnknown> aInterceptor, REFIID aTargetIid,
160                         STAUniquePtr<IUnknown> aTarget);
161 
162  private:
163   InterceptorTargetPtr<IUnknown> mTarget;
164   RefPtr<IInterceptorSink> mEventSink;
165   mozilla::Mutex mInterceptorMapMutex;  // Guards mInterceptorMap
166   // Using a nsTArray since the # of interfaces is not going to be very high
167   nsTArray<MapEntry> mInterceptorMap;
168   mozilla::Mutex mStdMarshalMutex;  // Guards mStdMarshalUnk and mStdMarshal
169   RefPtr<IUnknown> mStdMarshalUnk;
170   IMarshal* mStdMarshal;  // WEAK
171   static MOZ_THREAD_LOCAL(bool) tlsCreatingStdMarshal;
172 };
173 
174 template <typename InterfaceT>
CreateInterceptor(STAUniquePtr<InterfaceT> aTargetInterface,IInterceptorSink * aEventSink,InterfaceT ** aOutInterface)175 inline HRESULT CreateInterceptor(STAUniquePtr<InterfaceT> aTargetInterface,
176                                  IInterceptorSink* aEventSink,
177                                  InterfaceT** aOutInterface) {
178   if (!aTargetInterface || !aEventSink) {
179     return E_INVALIDARG;
180   }
181 
182   REFIID iidTarget = __uuidof(InterfaceT);
183 
184   STAUniquePtr<IUnknown> targetUnknown(aTargetInterface.release());
185   return Interceptor::Create(std::move(targetUnknown), aEventSink, iidTarget,
186                              (void**)aOutInterface);
187 }
188 
189 }  // namespace mscom
190 }  // namespace mozilla
191 
192 #endif  // mozilla_mscom_Interceptor_h
193