1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
|
/* -*- 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/. */
#include "mozilla/mscom/PassthruProxy.h"
#include "mozilla/mscom/ProxyStream.h"
#include "VTableBuilder.h"
// {96EF5801-CE6D-416E-A50A-0C2959AEAE1C}
static const GUID CLSID_PassthruProxy = {
0x96ef5801, 0xce6d, 0x416e, {0xa5, 0xa, 0xc, 0x29, 0x59, 0xae, 0xae, 0x1c}};
namespace mozilla {
namespace mscom {
PassthruProxy::PassthruProxy()
: mRefCnt(0),
mWrappedIid(),
mVTableSize(0),
mVTable(nullptr),
mForgetPreservedStream(false) {}
PassthruProxy::PassthruProxy(ProxyStream::Environment* aEnv, REFIID aIidToWrap,
uint32_t aVTableSize,
NotNull<IUnknown*> aObjToWrap)
: mRefCnt(0),
mWrappedIid(aIidToWrap),
mVTableSize(aVTableSize),
mVTable(nullptr),
mForgetPreservedStream(false) {
ProxyStream proxyStream(aIidToWrap, aObjToWrap, aEnv,
ProxyStreamFlags::ePreservable);
mPreservedStream = proxyStream.GetPreservedStream();
MOZ_ASSERT(mPreservedStream);
}
PassthruProxy::~PassthruProxy() {
if (mForgetPreservedStream) {
// We want to release the ref without clearing marshal data
IStream* stream = mPreservedStream.release();
stream->Release();
}
if (mVTable) {
DeleteNullVTable(mVTable);
}
}
HRESULT
PassthruProxy::QueryProxyInterface(void** aOutInterface) {
// Even though we don't really provide the methods for the interface that
// we are proxying, we need to support it in QueryInterface. Instead we
// return an interface that, other than IUnknown, contains nullptr for all of
// its vtable entires. Obviously this interface is not intended to actually
// be called, it just has to be there.
if (!mVTable) {
MOZ_ASSERT(mVTableSize);
mVTable = BuildNullVTable(static_cast<IMarshal*>(this), mVTableSize);
MOZ_ASSERT(mVTable);
}
*aOutInterface = mVTable;
mVTable->AddRef();
return S_OK;
}
HRESULT
PassthruProxy::QueryInterface(REFIID aIid, void** aOutInterface) {
if (!aOutInterface) {
return E_INVALIDARG;
}
*aOutInterface = nullptr;
if (aIid == IID_IUnknown || aIid == IID_IMarshal) {
RefPtr<IMarshal> ptr(this);
ptr.forget(aOutInterface);
return S_OK;
}
if (!IsInitialMarshal()) {
// We implement IClientSecurity so that IsProxy() recognizes us as such
if (aIid == IID_IClientSecurity) {
RefPtr<IClientSecurity> ptr(this);
ptr.forget(aOutInterface);
return S_OK;
}
if (aIid == mWrappedIid) {
return QueryProxyInterface(aOutInterface);
}
}
return E_NOINTERFACE;
}
ULONG
PassthruProxy::AddRef() { return ++mRefCnt; }
ULONG
PassthruProxy::Release() {
ULONG result = --mRefCnt;
if (!result) {
delete this;
}
return result;
}
HRESULT
PassthruProxy::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
CLSID* pCid) {
if (!pCid) {
return E_INVALIDARG;
}
if (IsInitialMarshal()) {
// To properly use this class we need to be using TABLESTRONG marshaling
MOZ_ASSERT(mshlflags & MSHLFLAGS_TABLESTRONG);
// When we're marshaling for the first time, we identify ourselves as the
// class to use for unmarshaling.
*pCid = CLSID_PassthruProxy;
} else {
// Subsequent marshals use the standard marshaler.
*pCid = CLSID_StdMarshal;
}
return S_OK;
}
HRESULT
PassthruProxy::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
void* pvDestContext, DWORD mshlflags,
DWORD* pSize) {
STATSTG statstg;
HRESULT hr;
if (!IsInitialMarshal()) {
// If we are not the initial marshal then we are just copying mStream out
// to the marshal stream, so we just use mStream's size.
hr = mStream->Stat(&statstg, STATFLAG_NONAME);
if (FAILED(hr)) {
return hr;
}
*pSize = statstg.cbSize.LowPart;
return hr;
}
// To properly use this class we need to be using TABLESTRONG marshaling
MOZ_ASSERT(mshlflags & MSHLFLAGS_TABLESTRONG);
if (!mPreservedStream) {
return E_POINTER;
}
hr = mPreservedStream->Stat(&statstg, STATFLAG_NONAME);
if (FAILED(hr)) {
return hr;
}
*pSize = statstg.cbSize.LowPart + sizeof(mVTableSize) + sizeof(mWrappedIid);
return hr;
}
HRESULT
PassthruProxy::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
DWORD dwDestContext, void* pvDestContext,
DWORD mshlflags) {
MOZ_ASSERT(riid == mWrappedIid);
if (riid != mWrappedIid) {
return E_NOINTERFACE;
}
MOZ_ASSERT(pv == mVTable);
if (pv != mVTable) {
return E_INVALIDARG;
}
HRESULT hr;
RefPtr<IStream> cloned;
if (IsInitialMarshal()) {
// To properly use this class we need to be using TABLESTRONG marshaling
MOZ_ASSERT(mshlflags & MSHLFLAGS_TABLESTRONG);
if (!mPreservedStream) {
return E_POINTER;
}
// We write out the vtable size and the IID so that the wrapped proxy knows
// how to build its vtable on the content side.
ULONG bytesWritten;
hr = pStm->Write(&mVTableSize, sizeof(mVTableSize), &bytesWritten);
if (FAILED(hr)) {
return hr;
}
if (bytesWritten != sizeof(mVTableSize)) {
return E_UNEXPECTED;
}
hr = pStm->Write(&mWrappedIid, sizeof(mWrappedIid), &bytesWritten);
if (FAILED(hr)) {
return hr;
}
if (bytesWritten != sizeof(mWrappedIid)) {
return E_UNEXPECTED;
}
hr = mPreservedStream->Clone(getter_AddRefs(cloned));
} else {
hr = mStream->Clone(getter_AddRefs(cloned));
}
if (FAILED(hr)) {
return hr;
}
STATSTG statstg;
hr = cloned->Stat(&statstg, STATFLAG_NONAME);
if (FAILED(hr)) {
return hr;
}
// Copy the proxy data
hr = cloned->CopyTo(pStm, statstg.cbSize, nullptr, nullptr);
if (SUCCEEDED(hr) && IsInitialMarshal() && mPreservedStream &&
(mshlflags & MSHLFLAGS_TABLESTRONG)) {
// If we have successfully copied mPreservedStream at least once for a
// MSHLFLAGS_TABLESTRONG marshal, then we want to forget our reference to
// it. This is because the COM runtime will manage it from here on out.
mForgetPreservedStream = true;
}
return hr;
}
HRESULT
PassthruProxy::UnmarshalInterface(IStream* pStm, REFIID riid, void** ppv) {
// Read out the interface info that we copied during marshaling
ULONG bytesRead;
HRESULT hr = pStm->Read(&mVTableSize, sizeof(mVTableSize), &bytesRead);
if (FAILED(hr)) {
return hr;
}
if (bytesRead != sizeof(mVTableSize)) {
return E_UNEXPECTED;
}
hr = pStm->Read(&mWrappedIid, sizeof(mWrappedIid), &bytesRead);
if (FAILED(hr)) {
return hr;
}
if (bytesRead != sizeof(mWrappedIid)) {
return E_UNEXPECTED;
}
// Now we copy the proxy inside pStm into mStream
hr = CopySerializedProxy(pStm, getter_AddRefs(mStream));
if (FAILED(hr)) {
return hr;
}
return QueryInterface(riid, ppv);
}
HRESULT
PassthruProxy::ReleaseMarshalData(IStream* pStm) {
if (!IsInitialMarshal()) {
return S_OK;
}
if (!pStm) {
return E_INVALIDARG;
}
if (mPreservedStream) {
// If we still have mPreservedStream, then simply clearing it will release
// its marshal data automagically.
mPreservedStream = nullptr;
return S_OK;
}
// Skip past the metadata that we wrote during initial marshaling.
LARGE_INTEGER seekTo;
seekTo.QuadPart = sizeof(mVTableSize) + sizeof(mWrappedIid);
HRESULT hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, nullptr);
if (FAILED(hr)) {
return hr;
}
// Now release the "inner" marshal data
return ::CoReleaseMarshalData(pStm);
}
HRESULT
PassthruProxy::DisconnectObject(DWORD dwReserved) { return S_OK; }
// The remainder of this code is just boilerplate COM stuff that provides the
// association between CLSID_PassthruProxy and the PassthruProxy class itself.
class PassthruProxyClassObject final : public IClassFactory {
public:
PassthruProxyClassObject();
// IUnknown
STDMETHODIMP QueryInterface(REFIID aIid, void** aOutInterface) override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
// IClassFactory
STDMETHODIMP CreateInstance(IUnknown* aOuter, REFIID aIid,
void** aOutObject) override;
STDMETHODIMP LockServer(BOOL aLock) override;
private:
~PassthruProxyClassObject() = default;
Atomic<ULONG> mRefCnt;
};
PassthruProxyClassObject::PassthruProxyClassObject() : mRefCnt(0) {}
HRESULT
PassthruProxyClassObject::QueryInterface(REFIID aIid, void** aOutInterface) {
if (!aOutInterface) {
return E_INVALIDARG;
}
*aOutInterface = nullptr;
if (aIid == IID_IUnknown || aIid == IID_IClassFactory) {
RefPtr<IClassFactory> ptr(this);
ptr.forget(aOutInterface);
return S_OK;
}
return E_NOINTERFACE;
}
ULONG
PassthruProxyClassObject::AddRef() { return ++mRefCnt; }
ULONG
PassthruProxyClassObject::Release() {
ULONG result = --mRefCnt;
if (!result) {
delete this;
}
return result;
}
HRESULT
PassthruProxyClassObject::CreateInstance(IUnknown* aOuter, REFIID aIid,
void** aOutObject) {
// We don't expect to aggregate
MOZ_ASSERT(!aOuter);
if (aOuter) {
return E_INVALIDARG;
}
RefPtr<PassthruProxy> ptr(new PassthruProxy());
return ptr->QueryInterface(aIid, aOutObject);
}
HRESULT
PassthruProxyClassObject::LockServer(BOOL aLock) {
// No-op since xul.dll is always in memory
return S_OK;
}
/* static */
HRESULT PassthruProxy::Register() {
DWORD cookie;
RefPtr<IClassFactory> classObj(new PassthruProxyClassObject());
return ::CoRegisterClassObject(CLSID_PassthruProxy, classObj,
CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE,
&cookie);
}
} // namespace mscom
} // namespace mozilla
HRESULT
RegisterPassthruProxy() { return mozilla::mscom::PassthruProxy::Register(); }
|