diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /ipc/mscom/PassthruProxy.cpp | |
parent | Initial commit. (diff) | |
download | firefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ipc/mscom/PassthruProxy.cpp')
-rw-r--r-- | ipc/mscom/PassthruProxy.cpp | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/ipc/mscom/PassthruProxy.cpp b/ipc/mscom/PassthruProxy.cpp new file mode 100644 index 0000000000..39bc7dba7e --- /dev/null +++ b/ipc/mscom/PassthruProxy.cpp @@ -0,0 +1,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(); } |