зеркало из https://github.com/mozilla/gecko-dev.git
394 строки
10 KiB
C++
394 строки
10 KiB
C++
/* -*- 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(); }
|