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 #include "mozilla/mscom/PassthruProxy.h"
8 #include "mozilla/mscom/ProxyStream.h"
9 #include "VTableBuilder.h"
11 // {96EF5801-CE6D-416E-A50A-0C2959AEAE1C}
12 static const GUID CLSID_PassthruProxy
= {
13 0x96ef5801, 0xce6d, 0x416e, {0xa5, 0xa, 0xc, 0x29, 0x59, 0xae, 0xae, 0x1c}};
18 PassthruProxy::PassthruProxy()
23 mForgetPreservedStream(false) {}
25 PassthruProxy::PassthruProxy(ProxyStream::Environment
* aEnv
, REFIID aIidToWrap
,
27 NotNull
<IUnknown
*> aObjToWrap
)
29 mWrappedIid(aIidToWrap
),
30 mVTableSize(aVTableSize
),
32 mForgetPreservedStream(false) {
33 ProxyStream
proxyStream(aIidToWrap
, aObjToWrap
, aEnv
,
34 ProxyStreamFlags::ePreservable
);
35 mPreservedStream
= proxyStream
.GetPreservedStream();
36 MOZ_ASSERT(mPreservedStream
);
39 PassthruProxy::~PassthruProxy() {
40 if (mForgetPreservedStream
) {
41 // We want to release the ref without clearing marshal data
42 IStream
* stream
= mPreservedStream
.release();
47 DeleteNullVTable(mVTable
);
52 PassthruProxy::QueryProxyInterface(void** aOutInterface
) {
53 // Even though we don't really provide the methods for the interface that
54 // we are proxying, we need to support it in QueryInterface. Instead we
55 // return an interface that, other than IUnknown, contains nullptr for all of
56 // its vtable entires. Obviously this interface is not intended to actually
57 // be called, it just has to be there.
60 MOZ_ASSERT(mVTableSize
);
61 mVTable
= BuildNullVTable(static_cast<IMarshal
*>(this), mVTableSize
);
65 *aOutInterface
= mVTable
;
71 PassthruProxy::QueryInterface(REFIID aIid
, void** aOutInterface
) {
76 *aOutInterface
= nullptr;
78 if (aIid
== IID_IUnknown
|| aIid
== IID_IMarshal
) {
79 RefPtr
<IMarshal
> ptr(this);
80 ptr
.forget(aOutInterface
);
84 if (!IsInitialMarshal()) {
85 // We implement IClientSecurity so that IsProxy() recognizes us as such
86 if (aIid
== IID_IClientSecurity
) {
87 RefPtr
<IClientSecurity
> ptr(this);
88 ptr
.forget(aOutInterface
);
92 if (aIid
== mWrappedIid
) {
93 return QueryProxyInterface(aOutInterface
);
101 PassthruProxy::AddRef() { return ++mRefCnt
; }
104 PassthruProxy::Release() {
105 ULONG result
= --mRefCnt
;
114 PassthruProxy::GetUnmarshalClass(REFIID riid
, void* pv
, DWORD dwDestContext
,
115 void* pvDestContext
, DWORD mshlflags
,
121 if (IsInitialMarshal()) {
122 // To properly use this class we need to be using TABLESTRONG marshaling
123 MOZ_ASSERT(mshlflags
& MSHLFLAGS_TABLESTRONG
);
125 // When we're marshaling for the first time, we identify ourselves as the
126 // class to use for unmarshaling.
127 *pCid
= CLSID_PassthruProxy
;
129 // Subsequent marshals use the standard marshaler.
130 *pCid
= CLSID_StdMarshal
;
137 PassthruProxy::GetMarshalSizeMax(REFIID riid
, void* pv
, DWORD dwDestContext
,
138 void* pvDestContext
, DWORD mshlflags
,
143 if (!IsInitialMarshal()) {
144 // If we are not the initial marshal then we are just copying mStream out
145 // to the marshal stream, so we just use mStream's size.
146 hr
= mStream
->Stat(&statstg
, STATFLAG_NONAME
);
151 *pSize
= statstg
.cbSize
.LowPart
;
156 // To properly use this class we need to be using TABLESTRONG marshaling
157 MOZ_ASSERT(mshlflags
& MSHLFLAGS_TABLESTRONG
);
159 if (!mPreservedStream
) {
163 hr
= mPreservedStream
->Stat(&statstg
, STATFLAG_NONAME
);
168 *pSize
= statstg
.cbSize
.LowPart
+ sizeof(mVTableSize
) + sizeof(mWrappedIid
);
173 PassthruProxy::MarshalInterface(IStream
* pStm
, REFIID riid
, void* pv
,
174 DWORD dwDestContext
, void* pvDestContext
,
176 MOZ_ASSERT(riid
== mWrappedIid
);
177 if (riid
!= mWrappedIid
) {
178 return E_NOINTERFACE
;
181 MOZ_ASSERT(pv
== mVTable
);
187 RefPtr
<IStream
> cloned
;
189 if (IsInitialMarshal()) {
190 // To properly use this class we need to be using TABLESTRONG marshaling
191 MOZ_ASSERT(mshlflags
& MSHLFLAGS_TABLESTRONG
);
193 if (!mPreservedStream
) {
197 // We write out the vtable size and the IID so that the wrapped proxy knows
198 // how to build its vtable on the content side.
200 hr
= pStm
->Write(&mVTableSize
, sizeof(mVTableSize
), &bytesWritten
);
204 if (bytesWritten
!= sizeof(mVTableSize
)) {
208 hr
= pStm
->Write(&mWrappedIid
, sizeof(mWrappedIid
), &bytesWritten
);
212 if (bytesWritten
!= sizeof(mWrappedIid
)) {
216 hr
= mPreservedStream
->Clone(getter_AddRefs(cloned
));
218 hr
= mStream
->Clone(getter_AddRefs(cloned
));
226 hr
= cloned
->Stat(&statstg
, STATFLAG_NONAME
);
231 // Copy the proxy data
232 hr
= cloned
->CopyTo(pStm
, statstg
.cbSize
, nullptr, nullptr);
234 if (SUCCEEDED(hr
) && IsInitialMarshal() && mPreservedStream
&&
235 (mshlflags
& MSHLFLAGS_TABLESTRONG
)) {
236 // If we have successfully copied mPreservedStream at least once for a
237 // MSHLFLAGS_TABLESTRONG marshal, then we want to forget our reference to
238 // it. This is because the COM runtime will manage it from here on out.
239 mForgetPreservedStream
= true;
246 PassthruProxy::UnmarshalInterface(IStream
* pStm
, REFIID riid
, void** ppv
) {
247 // Read out the interface info that we copied during marshaling
249 HRESULT hr
= pStm
->Read(&mVTableSize
, sizeof(mVTableSize
), &bytesRead
);
253 if (bytesRead
!= sizeof(mVTableSize
)) {
257 hr
= pStm
->Read(&mWrappedIid
, sizeof(mWrappedIid
), &bytesRead
);
261 if (bytesRead
!= sizeof(mWrappedIid
)) {
265 // Now we copy the proxy inside pStm into mStream
266 hr
= CopySerializedProxy(pStm
, getter_AddRefs(mStream
));
271 return QueryInterface(riid
, ppv
);
275 PassthruProxy::ReleaseMarshalData(IStream
* pStm
) {
276 if (!IsInitialMarshal()) {
284 if (mPreservedStream
) {
285 // If we still have mPreservedStream, then simply clearing it will release
286 // its marshal data automagically.
287 mPreservedStream
= nullptr;
291 // Skip past the metadata that we wrote during initial marshaling.
292 LARGE_INTEGER seekTo
;
293 seekTo
.QuadPart
= sizeof(mVTableSize
) + sizeof(mWrappedIid
);
294 HRESULT hr
= pStm
->Seek(seekTo
, STREAM_SEEK_CUR
, nullptr);
299 // Now release the "inner" marshal data
300 return ::CoReleaseMarshalData(pStm
);
304 PassthruProxy::DisconnectObject(DWORD dwReserved
) { return S_OK
; }
306 // The remainder of this code is just boilerplate COM stuff that provides the
307 // association between CLSID_PassthruProxy and the PassthruProxy class itself.
309 class PassthruProxyClassObject final
: public IClassFactory
{
311 PassthruProxyClassObject();
314 STDMETHODIMP
QueryInterface(REFIID aIid
, void** aOutInterface
) override
;
315 STDMETHODIMP_(ULONG
) AddRef() override
;
316 STDMETHODIMP_(ULONG
) Release() override
;
319 STDMETHODIMP
CreateInstance(IUnknown
* aOuter
, REFIID aIid
,
320 void** aOutObject
) override
;
321 STDMETHODIMP
LockServer(BOOL aLock
) override
;
324 ~PassthruProxyClassObject() = default;
326 Atomic
<ULONG
> mRefCnt
;
329 PassthruProxyClassObject::PassthruProxyClassObject() : mRefCnt(0) {}
332 PassthruProxyClassObject::QueryInterface(REFIID aIid
, void** aOutInterface
) {
333 if (!aOutInterface
) {
337 *aOutInterface
= nullptr;
339 if (aIid
== IID_IUnknown
|| aIid
== IID_IClassFactory
) {
340 RefPtr
<IClassFactory
> ptr(this);
341 ptr
.forget(aOutInterface
);
345 return E_NOINTERFACE
;
349 PassthruProxyClassObject::AddRef() { return ++mRefCnt
; }
352 PassthruProxyClassObject::Release() {
353 ULONG result
= --mRefCnt
;
362 PassthruProxyClassObject::CreateInstance(IUnknown
* aOuter
, REFIID aIid
,
364 // We don't expect to aggregate
370 RefPtr
<PassthruProxy
> ptr(new PassthruProxy());
371 return ptr
->QueryInterface(aIid
, aOutObject
);
375 PassthruProxyClassObject::LockServer(BOOL aLock
) {
376 // No-op since xul.dll is always in memory
381 HRESULT
PassthruProxy::Register() {
383 RefPtr
<IClassFactory
> classObj(new PassthruProxyClassObject());
384 return ::CoRegisterClassObject(CLSID_PassthruProxy
, classObj
,
385 CLSCTX_INPROC_SERVER
, REGCLS_MULTIPLEUSE
,
390 } // namespace mozilla
393 RegisterPassthruProxy() { return mozilla::mscom::PassthruProxy::Register(); }