summaryrefslogtreecommitdiffstats
path: root/ipc/mscom/ProxyStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/mscom/ProxyStream.cpp')
-rw-r--r--ipc/mscom/ProxyStream.cpp202
1 files changed, 202 insertions, 0 deletions
diff --git a/ipc/mscom/ProxyStream.cpp b/ipc/mscom/ProxyStream.cpp
new file mode 100644
index 000000000..9bc471583
--- /dev/null
+++ b/ipc/mscom/ProxyStream.cpp
@@ -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
+