summaryrefslogtreecommitdiffstats
path: root/ipc/mscom/PassthruProxy.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /ipc/mscom/PassthruProxy.cpp
parentInitial commit. (diff)
downloadfirefox-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.cpp393
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(); }