mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1261107: Adds code to marshal a Microsoft COM object and transfer its serialized proxy across IPDL; r=billm,jimm
MozReview-Commit-ID: BpSpue4Fq6G --HG-- extra : rebase_source : 32a33339b3c6dcd69fadd6d2987bafd2ded9c689
This commit is contained in:
parent
39c0509759
commit
b99626af7a
144
ipc/mscom/COMPtrHolder.h
Normal file
144
ipc/mscom/COMPtrHolder.h
Normal file
@ -0,0 +1,144 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_mscom_COMPtrHolder_h
|
||||
#define mozilla_mscom_COMPtrHolder_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/mscom/ProxyStream.h"
|
||||
#include "mozilla/mscom/Ptr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
template<typename Interface, const IID& _IID>
|
||||
class COMPtrHolder
|
||||
{
|
||||
public:
|
||||
typedef ProxyUniquePtr<Interface> COMPtrType;
|
||||
typedef COMPtrHolder<Interface, _IID> ThisType;
|
||||
|
||||
COMPtrHolder() {}
|
||||
|
||||
MOZ_IMPLICIT COMPtrHolder(decltype(nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
explicit COMPtrHolder(COMPtrType&& aPtr)
|
||||
: mPtr(Forward<COMPtrType>(aPtr))
|
||||
{
|
||||
}
|
||||
|
||||
Interface* Get() const
|
||||
{
|
||||
return mPtr.get();
|
||||
}
|
||||
|
||||
MOZ_MUST_USE Interface* Release()
|
||||
{
|
||||
return mPtr.release();
|
||||
}
|
||||
|
||||
void Set(COMPtrType&& aPtr)
|
||||
{
|
||||
mPtr = Forward<COMPtrType>(aPtr);
|
||||
}
|
||||
|
||||
COMPtrHolder(const COMPtrHolder& aOther) = delete;
|
||||
|
||||
COMPtrHolder(COMPtrHolder&& aOther)
|
||||
: mPtr(Move(aOther.mPtr))
|
||||
{
|
||||
}
|
||||
|
||||
// COMPtrHolder is eventually added as a member of a struct that is declared
|
||||
// in IPDL. The generated C++ code for that IPDL struct includes copy
|
||||
// constructors and assignment operators that assume that all members are
|
||||
// copyable. I don't think that those copy constructors and operator= are
|
||||
// actually used by any generated code, but they are made available. Since no
|
||||
// move semantics are available, this terrible hack makes COMPtrHolder build
|
||||
// when used as a member of an IPDL struct.
|
||||
ThisType& operator=(const ThisType& aOther)
|
||||
{
|
||||
Set(Move(aOther.mPtr));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ThisType& operator=(ThisType&& aOther)
|
||||
{
|
||||
Set(Move(aOther.mPtr));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const ThisType& aOther) const
|
||||
{
|
||||
return mPtr == aOther.mPtr;
|
||||
}
|
||||
|
||||
bool IsNull() const
|
||||
{
|
||||
return !mPtr;
|
||||
}
|
||||
|
||||
private:
|
||||
// This is mutable to facilitate the above operator= hack
|
||||
mutable COMPtrType mPtr;
|
||||
};
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template<typename Interface, const IID& _IID>
|
||||
struct ParamTraits<mozilla::mscom::COMPtrHolder<Interface, _IID>>
|
||||
{
|
||||
typedef mozilla::mscom::COMPtrHolder<Interface, _IID> paramType;
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
mozilla::mscom::ProxyStream proxyStream(_IID, aParam.Get());
|
||||
int bufLen;
|
||||
const BYTE* buf = proxyStream.GetBuffer(bufLen);
|
||||
MOZ_ASSERT(buf);
|
||||
aMsg->WriteInt(bufLen);
|
||||
aMsg->WriteBytes(reinterpret_cast<const char*>(buf), bufLen);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
int length;
|
||||
if (!aMsg->ReadLength(aIter, &length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mozilla::UniquePtr<BYTE[]> buf;
|
||||
if (length) {
|
||||
buf = mozilla::MakeUnique<BYTE[]>(length);
|
||||
if (!aMsg->ReadBytesInto(aIter, buf.get(), length)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::mscom::ProxyStream proxyStream(buf.get(), length);
|
||||
if (!proxyStream.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
Interface* rawInterface = nullptr;
|
||||
if (!proxyStream.GetInterface(_IID, (void**)&rawInterface)) {
|
||||
return false;
|
||||
}
|
||||
paramType::COMPtrType ptr(rawInterface);
|
||||
aResult->Set(mozilla::Move(ptr));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
#endif // mozilla_mscom_COMPtrHolder_h
|
||||
|
202
ipc/mscom/ProxyStream.cpp
Normal file
202
ipc/mscom/ProxyStream.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
/* -*- 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 "DynamicallyLinkedFunctionPtr.h"
|
||||
#include "mozilla/mscom/EnsureMTA.h"
|
||||
#include "mozilla/mscom/ProxyStream.h"
|
||||
#include "mozilla/mscom/utils.h"
|
||||
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <objbase.h>
|
||||
#include <shlwapi.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
ProxyStream::ProxyStream()
|
||||
: mGlobalLockedBuf(nullptr)
|
||||
, mHGlobal(nullptr)
|
||||
, mBufSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
// GetBuffer() fails with this variant, but that's okay because we're just
|
||||
// reconstructing the stream from a buffer anyway.
|
||||
ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
|
||||
: mStream(InitStream(aInitBuf, static_cast<const UINT>(aInitBufSize)))
|
||||
, mGlobalLockedBuf(nullptr)
|
||||
, mHGlobal(nullptr)
|
||||
, mBufSize(aInitBufSize)
|
||||
{
|
||||
if (!aInitBufSize) {
|
||||
// We marshaled a nullptr. Nothing else to do here.
|
||||
return;
|
||||
}
|
||||
// NB: We can't check for a null mStream until after we have checked for
|
||||
// the zero aInitBufSize above. This is because InitStream will also fail
|
||||
// in that case, even though marshaling a nullptr is allowable.
|
||||
MOZ_ASSERT(mStream);
|
||||
if (!mStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to convert to an interface here otherwise we mess up const
|
||||
// correctness with IPDL. We'll request an IUnknown and then QI the
|
||||
// actual interface later.
|
||||
|
||||
auto marshalFn = [&]() -> void
|
||||
{
|
||||
IUnknown* rawUnmarshaledProxy = nullptr;
|
||||
// OK to forget mStream when calling into this function because the stream
|
||||
// gets released even if the unmarshaling part fails.
|
||||
DebugOnly<HRESULT> hr =
|
||||
::CoGetInterfaceAndReleaseStream(mStream.forget().take(), IID_IUnknown,
|
||||
(void**)&rawUnmarshaledProxy);
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
mUnmarshaledProxy.reset(rawUnmarshaledProxy);
|
||||
};
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
// We'll marshal this stuff directly using the current thread, therefore its
|
||||
// proxy will reside in the same apartment as the current thread.
|
||||
marshalFn();
|
||||
} else {
|
||||
// When marshaling in child processes, we want to force the MTA.
|
||||
EnsureMTA mta(marshalFn);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<IStream>
|
||||
ProxyStream::InitStream(const BYTE* aInitBuf, const UINT aInitBufSize)
|
||||
{
|
||||
// Need to link to this as ordinal 12 for Windows XP
|
||||
static DynamicallyLinkedFunctionPtr<decltype(&::SHCreateMemStream)>
|
||||
pSHCreateMemStream(L"shlwapi.dll", reinterpret_cast<const char*>(12));
|
||||
if (!pSHCreateMemStream) {
|
||||
return nullptr;
|
||||
}
|
||||
return already_AddRefed<IStream>(pSHCreateMemStream(aInitBuf, aInitBufSize));
|
||||
}
|
||||
|
||||
ProxyStream::ProxyStream(ProxyStream&& aOther)
|
||||
{
|
||||
*this = mozilla::Move(aOther);
|
||||
}
|
||||
|
||||
ProxyStream&
|
||||
ProxyStream::operator=(ProxyStream&& aOther)
|
||||
{
|
||||
mStream = mozilla::Move(aOther.mStream);
|
||||
mGlobalLockedBuf = aOther.mGlobalLockedBuf;
|
||||
aOther.mGlobalLockedBuf = nullptr;
|
||||
mHGlobal = aOther.mHGlobal;
|
||||
aOther.mHGlobal = nullptr;
|
||||
mBufSize = aOther.mBufSize;
|
||||
aOther.mBufSize = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ProxyStream::~ProxyStream()
|
||||
{
|
||||
if (mHGlobal && mGlobalLockedBuf) {
|
||||
DebugOnly<BOOL> result = ::GlobalUnlock(mHGlobal);
|
||||
MOZ_ASSERT(!result && ::GetLastError() == NO_ERROR);
|
||||
// ::GlobalFree() is called implicitly when mStream is released
|
||||
}
|
||||
}
|
||||
|
||||
const BYTE*
|
||||
ProxyStream::GetBuffer(int& aReturnedBufSize) const
|
||||
{
|
||||
aReturnedBufSize = 0;
|
||||
if (!mStream) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!mGlobalLockedBuf) {
|
||||
return nullptr;
|
||||
}
|
||||
aReturnedBufSize = mBufSize;
|
||||
return mGlobalLockedBuf;
|
||||
}
|
||||
|
||||
bool
|
||||
ProxyStream::GetInterface(REFIID aIID, void** aOutInterface) const
|
||||
{
|
||||
// We should not have a locked buffer on this side
|
||||
MOZ_ASSERT(!mGlobalLockedBuf);
|
||||
MOZ_ASSERT(aOutInterface);
|
||||
|
||||
if (!aOutInterface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mUnmarshaledProxy) {
|
||||
*aOutInterface = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
HRESULT hr = E_UNEXPECTED;
|
||||
auto qiFn = [&]() -> void
|
||||
{
|
||||
hr = mUnmarshaledProxy->QueryInterface(aIID, aOutInterface);
|
||||
};
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
qiFn();
|
||||
} else {
|
||||
// mUnmarshaledProxy requires that we execute this in the MTA
|
||||
EnsureMTA mta(qiFn);
|
||||
}
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
ProxyStream::ProxyStream(REFIID aIID, IUnknown* aObject)
|
||||
: mGlobalLockedBuf(nullptr)
|
||||
, mHGlobal(nullptr)
|
||||
, mBufSize(0)
|
||||
{
|
||||
RefPtr<IStream> stream;
|
||||
HGLOBAL hglobal = NULL;
|
||||
|
||||
auto marshalFn = [&]() -> void
|
||||
{
|
||||
HRESULT hr = ::CreateStreamOnHGlobal(nullptr, TRUE, getter_AddRefs(stream));
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = ::CoMarshalInterface(stream, aIID, aObject, MSHCTX_LOCAL, nullptr,
|
||||
MSHLFLAGS_NORMAL);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = ::GetHGlobalFromStream(stream, &hglobal);
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
};
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
// We'll marshal this stuff directly using the current thread, therefore its
|
||||
// stub will reside in the same apartment as the current thread.
|
||||
marshalFn();
|
||||
} else {
|
||||
// When marshaling in child processes, we want to force the MTA.
|
||||
EnsureMTA mta(marshalFn);
|
||||
}
|
||||
|
||||
mStream = mozilla::Move(stream);
|
||||
if (hglobal) {
|
||||
mGlobalLockedBuf = reinterpret_cast<BYTE*>(::GlobalLock(hglobal));
|
||||
mHGlobal = hglobal;
|
||||
mBufSize = static_cast<int>(::GlobalSize(hglobal));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
64
ipc/mscom/ProxyStream.h
Normal file
64
ipc/mscom/ProxyStream.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_mscom_ProxyStream_h
|
||||
#define mozilla_mscom_ProxyStream_h
|
||||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
|
||||
#include "mozilla/mscom/Ptr.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
class ProxyStream
|
||||
{
|
||||
public:
|
||||
ProxyStream();
|
||||
ProxyStream(REFIID aIID, IUnknown* aObject);
|
||||
ProxyStream(const BYTE* aInitBuf, const int aInitBufSize);
|
||||
|
||||
~ProxyStream();
|
||||
|
||||
// Not copyable because this would mess up the COM marshaling.
|
||||
ProxyStream(const ProxyStream& aOther) = delete;
|
||||
ProxyStream& operator=(const ProxyStream& aOther) = delete;
|
||||
|
||||
ProxyStream(ProxyStream&& aOther);
|
||||
ProxyStream& operator=(ProxyStream&& aOther);
|
||||
|
||||
inline bool IsValid() const
|
||||
{
|
||||
// This check must be exclusive OR
|
||||
return (mStream && !mUnmarshaledProxy) || (mUnmarshaledProxy && !mStream);
|
||||
}
|
||||
|
||||
bool GetInterface(REFIID aIID, void** aOutInterface) const;
|
||||
const BYTE* GetBuffer(int& aReturnedBufSize) const;
|
||||
|
||||
bool operator==(const ProxyStream& aOther) const
|
||||
{
|
||||
return this == &aOther;
|
||||
}
|
||||
|
||||
private:
|
||||
already_AddRefed<IStream> InitStream(const BYTE* aInitBuf,
|
||||
const UINT aInitBufSize);
|
||||
|
||||
private:
|
||||
RefPtr<IStream> mStream;
|
||||
BYTE* mGlobalLockedBuf;
|
||||
HGLOBAL mHGlobal;
|
||||
int mBufSize;
|
||||
ProxyUniquePtr<IUnknown> mUnmarshaledProxy;
|
||||
};
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_ProxyStream_h
|
@ -6,8 +6,10 @@
|
||||
|
||||
EXPORTS.mozilla.mscom += [
|
||||
'COMApartmentRegion.h',
|
||||
'COMPtrHolder.h',
|
||||
'EnsureMTA.h',
|
||||
'MainThreadRuntime.h',
|
||||
'ProxyStream.h',
|
||||
'Utils.h',
|
||||
]
|
||||
|
||||
@ -18,6 +20,7 @@ SOURCES += [
|
||||
UNIFIED_SOURCES += [
|
||||
'EnsureMTA.cpp',
|
||||
'MainThreadRuntime.cpp',
|
||||
'ProxyStream.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
Loading…
Reference in New Issue
Block a user