зеркало из https://github.com/mozilla/gecko-dev.git
203 строки
5.4 KiB
C++
203 строки
5.4 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 "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
|
|
|