Bug 1660755 [wpt PR 25207] - [scroll-animations] Allow null source in ScrollTimeline...
[gecko.git] / ipc / mscom / PassthruProxy.cpp
blob39bc7dba7e87d6ee1985a1902a0502da99f4888b
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}};
15 namespace mozilla {
16 namespace mscom {
18 PassthruProxy::PassthruProxy()
19 : mRefCnt(0),
20 mWrappedIid(),
21 mVTableSize(0),
22 mVTable(nullptr),
23 mForgetPreservedStream(false) {}
25 PassthruProxy::PassthruProxy(ProxyStream::Environment* aEnv, REFIID aIidToWrap,
26 uint32_t aVTableSize,
27 NotNull<IUnknown*> aObjToWrap)
28 : mRefCnt(0),
29 mWrappedIid(aIidToWrap),
30 mVTableSize(aVTableSize),
31 mVTable(nullptr),
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();
43 stream->Release();
46 if (mVTable) {
47 DeleteNullVTable(mVTable);
51 HRESULT
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.
59 if (!mVTable) {
60 MOZ_ASSERT(mVTableSize);
61 mVTable = BuildNullVTable(static_cast<IMarshal*>(this), mVTableSize);
62 MOZ_ASSERT(mVTable);
65 *aOutInterface = mVTable;
66 mVTable->AddRef();
67 return S_OK;
70 HRESULT
71 PassthruProxy::QueryInterface(REFIID aIid, void** aOutInterface) {
72 if (!aOutInterface) {
73 return E_INVALIDARG;
76 *aOutInterface = nullptr;
78 if (aIid == IID_IUnknown || aIid == IID_IMarshal) {
79 RefPtr<IMarshal> ptr(this);
80 ptr.forget(aOutInterface);
81 return S_OK;
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);
89 return S_OK;
92 if (aIid == mWrappedIid) {
93 return QueryProxyInterface(aOutInterface);
97 return E_NOINTERFACE;
100 ULONG
101 PassthruProxy::AddRef() { return ++mRefCnt; }
103 ULONG
104 PassthruProxy::Release() {
105 ULONG result = --mRefCnt;
106 if (!result) {
107 delete this;
110 return result;
113 HRESULT
114 PassthruProxy::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
115 void* pvDestContext, DWORD mshlflags,
116 CLSID* pCid) {
117 if (!pCid) {
118 return E_INVALIDARG;
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;
128 } else {
129 // Subsequent marshals use the standard marshaler.
130 *pCid = CLSID_StdMarshal;
133 return S_OK;
136 HRESULT
137 PassthruProxy::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
138 void* pvDestContext, DWORD mshlflags,
139 DWORD* pSize) {
140 STATSTG statstg;
141 HRESULT hr;
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);
147 if (FAILED(hr)) {
148 return hr;
151 *pSize = statstg.cbSize.LowPart;
153 return hr;
156 // To properly use this class we need to be using TABLESTRONG marshaling
157 MOZ_ASSERT(mshlflags & MSHLFLAGS_TABLESTRONG);
159 if (!mPreservedStream) {
160 return E_POINTER;
163 hr = mPreservedStream->Stat(&statstg, STATFLAG_NONAME);
164 if (FAILED(hr)) {
165 return hr;
168 *pSize = statstg.cbSize.LowPart + sizeof(mVTableSize) + sizeof(mWrappedIid);
169 return hr;
172 HRESULT
173 PassthruProxy::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
174 DWORD dwDestContext, void* pvDestContext,
175 DWORD mshlflags) {
176 MOZ_ASSERT(riid == mWrappedIid);
177 if (riid != mWrappedIid) {
178 return E_NOINTERFACE;
181 MOZ_ASSERT(pv == mVTable);
182 if (pv != mVTable) {
183 return E_INVALIDARG;
186 HRESULT hr;
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) {
194 return E_POINTER;
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.
199 ULONG bytesWritten;
200 hr = pStm->Write(&mVTableSize, sizeof(mVTableSize), &bytesWritten);
201 if (FAILED(hr)) {
202 return hr;
204 if (bytesWritten != sizeof(mVTableSize)) {
205 return E_UNEXPECTED;
208 hr = pStm->Write(&mWrappedIid, sizeof(mWrappedIid), &bytesWritten);
209 if (FAILED(hr)) {
210 return hr;
212 if (bytesWritten != sizeof(mWrappedIid)) {
213 return E_UNEXPECTED;
216 hr = mPreservedStream->Clone(getter_AddRefs(cloned));
217 } else {
218 hr = mStream->Clone(getter_AddRefs(cloned));
221 if (FAILED(hr)) {
222 return hr;
225 STATSTG statstg;
226 hr = cloned->Stat(&statstg, STATFLAG_NONAME);
227 if (FAILED(hr)) {
228 return hr;
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;
242 return hr;
245 HRESULT
246 PassthruProxy::UnmarshalInterface(IStream* pStm, REFIID riid, void** ppv) {
247 // Read out the interface info that we copied during marshaling
248 ULONG bytesRead;
249 HRESULT hr = pStm->Read(&mVTableSize, sizeof(mVTableSize), &bytesRead);
250 if (FAILED(hr)) {
251 return hr;
253 if (bytesRead != sizeof(mVTableSize)) {
254 return E_UNEXPECTED;
257 hr = pStm->Read(&mWrappedIid, sizeof(mWrappedIid), &bytesRead);
258 if (FAILED(hr)) {
259 return hr;
261 if (bytesRead != sizeof(mWrappedIid)) {
262 return E_UNEXPECTED;
265 // Now we copy the proxy inside pStm into mStream
266 hr = CopySerializedProxy(pStm, getter_AddRefs(mStream));
267 if (FAILED(hr)) {
268 return hr;
271 return QueryInterface(riid, ppv);
274 HRESULT
275 PassthruProxy::ReleaseMarshalData(IStream* pStm) {
276 if (!IsInitialMarshal()) {
277 return S_OK;
280 if (!pStm) {
281 return E_INVALIDARG;
284 if (mPreservedStream) {
285 // If we still have mPreservedStream, then simply clearing it will release
286 // its marshal data automagically.
287 mPreservedStream = nullptr;
288 return S_OK;
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);
295 if (FAILED(hr)) {
296 return hr;
299 // Now release the "inner" marshal data
300 return ::CoReleaseMarshalData(pStm);
303 HRESULT
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 {
310 public:
311 PassthruProxyClassObject();
313 // IUnknown
314 STDMETHODIMP QueryInterface(REFIID aIid, void** aOutInterface) override;
315 STDMETHODIMP_(ULONG) AddRef() override;
316 STDMETHODIMP_(ULONG) Release() override;
318 // IClassFactory
319 STDMETHODIMP CreateInstance(IUnknown* aOuter, REFIID aIid,
320 void** aOutObject) override;
321 STDMETHODIMP LockServer(BOOL aLock) override;
323 private:
324 ~PassthruProxyClassObject() = default;
326 Atomic<ULONG> mRefCnt;
329 PassthruProxyClassObject::PassthruProxyClassObject() : mRefCnt(0) {}
331 HRESULT
332 PassthruProxyClassObject::QueryInterface(REFIID aIid, void** aOutInterface) {
333 if (!aOutInterface) {
334 return E_INVALIDARG;
337 *aOutInterface = nullptr;
339 if (aIid == IID_IUnknown || aIid == IID_IClassFactory) {
340 RefPtr<IClassFactory> ptr(this);
341 ptr.forget(aOutInterface);
342 return S_OK;
345 return E_NOINTERFACE;
348 ULONG
349 PassthruProxyClassObject::AddRef() { return ++mRefCnt; }
351 ULONG
352 PassthruProxyClassObject::Release() {
353 ULONG result = --mRefCnt;
354 if (!result) {
355 delete this;
358 return result;
361 HRESULT
362 PassthruProxyClassObject::CreateInstance(IUnknown* aOuter, REFIID aIid,
363 void** aOutObject) {
364 // We don't expect to aggregate
365 MOZ_ASSERT(!aOuter);
366 if (aOuter) {
367 return E_INVALIDARG;
370 RefPtr<PassthruProxy> ptr(new PassthruProxy());
371 return ptr->QueryInterface(aIid, aOutObject);
374 HRESULT
375 PassthruProxyClassObject::LockServer(BOOL aLock) {
376 // No-op since xul.dll is always in memory
377 return S_OK;
380 /* static */
381 HRESULT PassthruProxy::Register() {
382 DWORD cookie;
383 RefPtr<IClassFactory> classObj(new PassthruProxyClassObject());
384 return ::CoRegisterClassObject(CLSID_PassthruProxy, classObj,
385 CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE,
386 &cookie);
389 } // namespace mscom
390 } // namespace mozilla
392 HRESULT
393 RegisterPassthruProxy() { return mozilla::mscom::PassthruProxy::Register(); }