summaryrefslogtreecommitdiffstats
path: root/ipc/mscom
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /ipc/mscom
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'ipc/mscom')
-rw-r--r--ipc/mscom/ActivationContext.cpp46
-rw-r--r--ipc/mscom/ActivationContext.h35
-rw-r--r--ipc/mscom/COMApartmentRegion.h62
-rw-r--r--ipc/mscom/COMPtrHolder.h144
-rw-r--r--ipc/mscom/DispatchForwarder.cpp167
-rw-r--r--ipc/mscom/DispatchForwarder.h81
-rw-r--r--ipc/mscom/DynamicallyLinkedFunctionPtr.h68
-rw-r--r--ipc/mscom/EnsureMTA.cpp78
-rw-r--r--ipc/mscom/EnsureMTA.h87
-rw-r--r--ipc/mscom/Interceptor.cpp313
-rw-r--r--ipc/mscom/Interceptor.h125
-rw-r--r--ipc/mscom/InterceptorLog.cpp466
-rw-r--r--ipc/mscom/InterceptorLog.h28
-rw-r--r--ipc/mscom/MainThreadHandoff.cpp398
-rw-r--r--ipc/mscom/MainThreadHandoff.h71
-rw-r--r--ipc/mscom/MainThreadInvoker.cpp180
-rw-r--r--ipc/mscom/MainThreadInvoker.h38
-rw-r--r--ipc/mscom/MainThreadRuntime.cpp180
-rw-r--r--ipc/mscom/MainThreadRuntime.h42
-rw-r--r--ipc/mscom/ProxyStream.cpp202
-rw-r--r--ipc/mscom/ProxyStream.h64
-rw-r--r--ipc/mscom/Ptr.h111
-rw-r--r--ipc/mscom/Registration.cpp346
-rw-r--r--ipc/mscom/Registration.h145
-rw-r--r--ipc/mscom/Utils.cpp94
-rw-r--r--ipc/mscom/Utils.h22
-rw-r--r--ipc/mscom/WeakRef.cpp191
-rw-r--r--ipc/mscom/WeakRef.h104
-rw-r--r--ipc/mscom/moz.build60
29 files changed, 3948 insertions, 0 deletions
diff --git a/ipc/mscom/ActivationContext.cpp b/ipc/mscom/ActivationContext.cpp
new file mode 100644
index 000000000..2d0261038
--- /dev/null
+++ b/ipc/mscom/ActivationContext.cpp
@@ -0,0 +1,46 @@
+/* -*- 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/ActivationContext.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+
+namespace mozilla {
+namespace mscom {
+
+ActivationContext::ActivationContext(HMODULE aLoadFromModule)
+ : mActCtx(INVALID_HANDLE_VALUE)
+ , mActivationCookie(0)
+{
+ ACTCTX actCtx = {sizeof(actCtx)};
+ actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID | ACTCTX_FLAG_HMODULE_VALID;
+ actCtx.lpResourceName = MAKEINTRESOURCE(2);
+ actCtx.hModule = aLoadFromModule;
+
+ mActCtx = ::CreateActCtx(&actCtx);
+ MOZ_ASSERT(mActCtx != INVALID_HANDLE_VALUE);
+ if (mActCtx == INVALID_HANDLE_VALUE) {
+ return;
+ }
+ if (!::ActivateActCtx(mActCtx, &mActivationCookie)) {
+ ::ReleaseActCtx(mActCtx);
+ mActCtx = INVALID_HANDLE_VALUE;
+ }
+}
+
+ActivationContext::~ActivationContext()
+{
+ if (mActCtx == INVALID_HANDLE_VALUE) {
+ return;
+ }
+ DebugOnly<BOOL> deactivated = ::DeactivateActCtx(0, mActivationCookie);
+ MOZ_ASSERT(deactivated);
+ ::ReleaseActCtx(mActCtx);
+}
+
+} // namespace mscom
+} // namespace mozilla
diff --git a/ipc/mscom/ActivationContext.h b/ipc/mscom/ActivationContext.h
new file mode 100644
index 000000000..08eda7772
--- /dev/null
+++ b/ipc/mscom/ActivationContext.h
@@ -0,0 +1,35 @@
+/* -*- 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_ActivationContext_h
+#define mozilla_mscom_ActivationContext_h
+
+#include <windows.h>
+
+namespace mozilla {
+namespace mscom {
+
+class ActivationContext
+{
+public:
+ explicit ActivationContext(HMODULE aLoadFromModule);
+ ~ActivationContext();
+
+ explicit operator bool() const
+ {
+ return mActCtx != INVALID_HANDLE_VALUE;
+ }
+
+private:
+ HANDLE mActCtx;
+ ULONG_PTR mActivationCookie;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_ActivationContext_h
+
diff --git a/ipc/mscom/COMApartmentRegion.h b/ipc/mscom/COMApartmentRegion.h
new file mode 100644
index 000000000..dc7ddf9ae
--- /dev/null
+++ b/ipc/mscom/COMApartmentRegion.h
@@ -0,0 +1,62 @@
+/* -*- 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_COMApartmentRegion_h
+#define mozilla_mscom_COMApartmentRegion_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+
+#include <objbase.h>
+
+namespace mozilla {
+namespace mscom {
+
+template<COINIT T>
+class MOZ_NON_TEMPORARY_CLASS COMApartmentRegion
+{
+public:
+ COMApartmentRegion()
+ : mInitResult(::CoInitializeEx(nullptr, T))
+ {
+ // If this fires then we're probably mixing apartments on the same thread
+ MOZ_ASSERT(IsValid());
+ }
+
+ ~COMApartmentRegion()
+ {
+ if (IsValid()) {
+ ::CoUninitialize();
+ }
+ }
+
+ bool IsValidOutermost() const
+ {
+ return mInitResult == S_OK;
+ }
+
+ bool IsValid() const
+ {
+ return SUCCEEDED(mInitResult);
+ }
+
+private:
+ COMApartmentRegion(const COMApartmentRegion&) = delete;
+ COMApartmentRegion& operator=(const COMApartmentRegion&) = delete;
+ COMApartmentRegion(COMApartmentRegion&&) = delete;
+ COMApartmentRegion& operator=(COMApartmentRegion&&) = delete;
+
+ HRESULT mInitResult;
+};
+
+typedef COMApartmentRegion<COINIT_APARTMENTTHREADED> STARegion;
+typedef COMApartmentRegion<COINIT_MULTITHREADED> MTARegion;
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_COMApartmentRegion_h
+
diff --git a/ipc/mscom/COMPtrHolder.h b/ipc/mscom/COMPtrHolder.h
new file mode 100644
index 000000000..88d372a27
--- /dev/null
+++ b/ipc/mscom/COMPtrHolder.h
@@ -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 || !bufLen);
+ 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;
+ }
+ typename paramType::COMPtrType ptr(rawInterface);
+ aResult->Set(mozilla::Move(ptr));
+ return true;
+ }
+};
+
+} // namespace IPC
+
+#endif // mozilla_mscom_COMPtrHolder_h
+
diff --git a/ipc/mscom/DispatchForwarder.cpp b/ipc/mscom/DispatchForwarder.cpp
new file mode 100644
index 000000000..acc76f383
--- /dev/null
+++ b/ipc/mscom/DispatchForwarder.cpp
@@ -0,0 +1,167 @@
+/* -*- 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/Move.h"
+#include "mozilla/mscom/DispatchForwarder.h"
+#include "mozilla/mscom/MainThreadInvoker.h"
+
+#include <oleauto.h>
+
+namespace mozilla {
+namespace mscom {
+
+/* static */ HRESULT
+DispatchForwarder::Create(IInterceptor* aInterceptor,
+ STAUniquePtr<IDispatch>& aTarget, IUnknown** aOutput)
+{
+ MOZ_ASSERT(aInterceptor && aOutput);
+ if (!aOutput) {
+ return E_INVALIDARG;
+ }
+ *aOutput = nullptr;
+ if (!aInterceptor) {
+ return E_INVALIDARG;
+ }
+ DispatchForwarder* forwarder = new DispatchForwarder(aInterceptor, aTarget);
+ HRESULT hr = forwarder->QueryInterface(IID_IDispatch, (void**) aOutput);
+ forwarder->Release();
+ return hr;
+}
+
+DispatchForwarder::DispatchForwarder(IInterceptor* aInterceptor,
+ STAUniquePtr<IDispatch>& aTarget)
+ : mRefCnt(1)
+ , mInterceptor(aInterceptor)
+ , mTarget(Move(aTarget))
+{
+}
+
+DispatchForwarder::~DispatchForwarder()
+{
+}
+
+HRESULT
+DispatchForwarder::QueryInterface(REFIID riid, void** ppv)
+{
+ if (!ppv) {
+ return E_INVALIDARG;
+ }
+
+ // Since this class implements a tearoff, any interfaces that are not
+ // IDispatch must be routed to the original object's QueryInterface.
+ // This is especially important for IUnknown since COM uses that interface
+ // to determine object identity.
+ if (riid != IID_IDispatch) {
+ return mInterceptor->QueryInterface(riid, ppv);
+ }
+
+ IUnknown* punk = static_cast<IDispatch*>(this);
+ *ppv = punk;
+ if (!punk) {
+ return E_NOINTERFACE;
+ }
+
+ punk->AddRef();
+ return S_OK;
+}
+
+ULONG
+DispatchForwarder::AddRef()
+{
+ return (ULONG) InterlockedIncrement((LONG*)&mRefCnt);
+}
+
+ULONG
+DispatchForwarder::Release()
+{
+ ULONG newRefCnt = (ULONG) InterlockedDecrement((LONG*)&mRefCnt);
+ if (newRefCnt == 0) {
+ delete this;
+ }
+ return newRefCnt;
+}
+
+HRESULT
+DispatchForwarder::GetTypeInfoCount(UINT *pctinfo)
+{
+ if (!pctinfo) {
+ return E_INVALIDARG;
+ }
+ *pctinfo = 1;
+ return S_OK;
+}
+
+HRESULT
+DispatchForwarder::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
+{
+ // ITypeInfo as implemented by COM is apartment-neutral, so we don't need
+ // to wrap it (yay!)
+ if (mTypeInfo) {
+ *ppTInfo = mTypeInfo.get();
+ mTypeInfo->AddRef();
+ return S_OK;
+ }
+ HRESULT hr = E_UNEXPECTED;
+ auto fn = [&]() -> void {
+ hr = mTarget->GetTypeInfo(iTInfo, lcid, ppTInfo);
+ };
+ MainThreadInvoker invoker;
+ if (!invoker.Invoke(NS_NewRunnableFunction(fn))) {
+ return E_UNEXPECTED;
+ }
+ if (FAILED(hr)) {
+ return hr;
+ }
+ mTypeInfo = *ppTInfo;
+ return hr;
+}
+
+HRESULT
+DispatchForwarder::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames,
+ LCID lcid, DISPID *rgDispId)
+{
+ HRESULT hr = E_UNEXPECTED;
+ auto fn = [&]() -> void {
+ hr = mTarget->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
+ };
+ MainThreadInvoker invoker;
+ if (!invoker.Invoke(NS_NewRunnableFunction(fn))) {
+ return E_UNEXPECTED;
+ }
+ return hr;
+}
+
+HRESULT
+DispatchForwarder::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
+ WORD wFlags, DISPPARAMS *pDispParams,
+ VARIANT *pVarResult, EXCEPINFO *pExcepInfo,
+ UINT *puArgErr)
+{
+ HRESULT hr;
+ if (!mInterface) {
+ if (!mTypeInfo) {
+ return E_UNEXPECTED;
+ }
+ TYPEATTR* typeAttr = nullptr;
+ hr = mTypeInfo->GetTypeAttr(&typeAttr);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ hr = mInterceptor->QueryInterface(typeAttr->guid,
+ (void**)getter_AddRefs(mInterface));
+ mTypeInfo->ReleaseTypeAttr(typeAttr);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+ // We don't invoke IDispatch on the target, but rather on the interceptor!
+ hr = ::DispInvoke(mInterface.get(), mTypeInfo, dispIdMember, wFlags,
+ pDispParams, pVarResult, pExcepInfo, puArgErr);
+ return hr;
+}
+
+} // namespace mscom
+} // namespace mozilla
diff --git a/ipc/mscom/DispatchForwarder.h b/ipc/mscom/DispatchForwarder.h
new file mode 100644
index 000000000..0da5f8548
--- /dev/null
+++ b/ipc/mscom/DispatchForwarder.h
@@ -0,0 +1,81 @@
+/* -*- 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_DispatchForwarder_h
+#define mozilla_mscom_DispatchForwarder_h
+
+#include <oaidl.h>
+
+#include "mozilla/mscom/Interceptor.h"
+#include "mozilla/mscom/Ptr.h"
+
+namespace mozilla {
+namespace mscom {
+
+class DispatchForwarder final : public IDispatch
+{
+public:
+ static HRESULT Create(IInterceptor* aInterceptor,
+ STAUniquePtr<IDispatch>& aTarget, IUnknown** aOutput);
+
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
+ STDMETHODIMP_(ULONG) AddRef() override;
+ STDMETHODIMP_(ULONG) Release() override;
+
+ // IDispatch
+ STDMETHODIMP GetTypeInfoCount(
+ /* [out] */ __RPC__out UINT *pctinfo) override;
+
+ STDMETHODIMP GetTypeInfo(
+ /* [in] */ UINT iTInfo,
+ /* [in] */ LCID lcid,
+ /* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo) override;
+
+ STDMETHODIMP GetIDsOfNames(
+ /* [in] */ __RPC__in REFIID riid,
+ /* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
+ /* [range][in] */ __RPC__in_range(0,16384) UINT cNames,
+ /* [in] */ LCID lcid,
+ /* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId)
+ override;
+
+ STDMETHODIMP Invoke(
+ /* [annotation][in] */
+ _In_ DISPID dispIdMember,
+ /* [annotation][in] */
+ _In_ REFIID riid,
+ /* [annotation][in] */
+ _In_ LCID lcid,
+ /* [annotation][in] */
+ _In_ WORD wFlags,
+ /* [annotation][out][in] */
+ _In_ DISPPARAMS *pDispParams,
+ /* [annotation][out] */
+ _Out_opt_ VARIANT *pVarResult,
+ /* [annotation][out] */
+ _Out_opt_ EXCEPINFO *pExcepInfo,
+ /* [annotation][out] */
+ _Out_opt_ UINT *puArgErr) override;
+
+private:
+ DispatchForwarder(IInterceptor* aInterceptor,
+ STAUniquePtr<IDispatch>& aTarget);
+ ~DispatchForwarder();
+
+private:
+ ULONG mRefCnt;
+ RefPtr<IInterceptor> mInterceptor;
+ STAUniquePtr<IDispatch> mTarget;
+ RefPtr<ITypeInfo> mTypeInfo;
+ RefPtr<IUnknown> mInterface;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_DispatchForwarder_h
+
diff --git a/ipc/mscom/DynamicallyLinkedFunctionPtr.h b/ipc/mscom/DynamicallyLinkedFunctionPtr.h
new file mode 100644
index 000000000..c774119c8
--- /dev/null
+++ b/ipc/mscom/DynamicallyLinkedFunctionPtr.h
@@ -0,0 +1,68 @@
+/* -*- 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_DynamicallyLinkedFunctionPtr_h
+#define mozilla_mscom_DynamicallyLinkedFunctionPtr_h
+
+#include "mozilla/Move.h"
+#include <windows.h>
+
+namespace mozilla {
+namespace mscom {
+
+template <typename T>
+class DynamicallyLinkedFunctionPtr;
+
+template <typename R, typename... Args>
+class DynamicallyLinkedFunctionPtr<R (__stdcall*)(Args...)>
+{
+ typedef R (__stdcall* FunctionPtrT)(Args...);
+
+public:
+ DynamicallyLinkedFunctionPtr(const wchar_t* aLibName, const char* aFuncName)
+ : mModule(NULL)
+ , mFunction(nullptr)
+ {
+ mModule = ::LoadLibraryW(aLibName);
+ if (mModule) {
+ mFunction = reinterpret_cast<FunctionPtrT>(
+ ::GetProcAddress(mModule, aFuncName));
+ }
+ }
+
+ DynamicallyLinkedFunctionPtr(const DynamicallyLinkedFunctionPtr&) = delete;
+ DynamicallyLinkedFunctionPtr& operator=(const DynamicallyLinkedFunctionPtr&) = delete;
+
+ DynamicallyLinkedFunctionPtr(DynamicallyLinkedFunctionPtr&&) = delete;
+ DynamicallyLinkedFunctionPtr& operator=(DynamicallyLinkedFunctionPtr&&) = delete;
+
+ ~DynamicallyLinkedFunctionPtr()
+ {
+ if (mModule) {
+ ::FreeLibrary(mModule);
+ }
+ }
+
+ R operator()(Args... args)
+ {
+ return mFunction(mozilla::Forward<Args>(args)...);
+ }
+
+ explicit operator bool() const
+ {
+ return !!mFunction;
+ }
+
+private:
+ HMODULE mModule;
+ FunctionPtrT mFunction;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_DynamicallyLinkedFunctionPtr_h
+
diff --git a/ipc/mscom/EnsureMTA.cpp b/ipc/mscom/EnsureMTA.cpp
new file mode 100644
index 000000000..f4bc911e9
--- /dev/null
+++ b/ipc/mscom/EnsureMTA.cpp
@@ -0,0 +1,78 @@
+/* -*- 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/EnsureMTA.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
+#include "nsThreadUtils.h"
+
+#include "private/pprthred.h"
+
+namespace {
+
+class EnterMTARunnable : public mozilla::Runnable
+{
+public:
+ NS_IMETHOD Run() override
+ {
+ mozilla::DebugOnly<HRESULT> hr = ::CoInitializeEx(nullptr,
+ COINIT_MULTITHREADED);
+ MOZ_ASSERT(SUCCEEDED(hr));
+ return NS_OK;
+ }
+};
+
+class BackgroundMTAData
+{
+public:
+ BackgroundMTAData()
+ {
+ nsCOMPtr<nsIRunnable> runnable = new EnterMTARunnable();
+ nsresult rv = NS_NewNamedThread("COM MTA",
+ getter_AddRefs(mThread), runnable);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_NewNamedThread failed");
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+
+ ~BackgroundMTAData()
+ {
+ if (mThread) {
+ mThread->Dispatch(NS_NewRunnableFunction(&::CoUninitialize),
+ NS_DISPATCH_NORMAL);
+ mThread->Shutdown();
+ }
+ }
+
+ nsCOMPtr<nsIThread> GetThread() const
+ {
+ return mThread;
+ }
+
+private:
+ nsCOMPtr<nsIThread> mThread;
+};
+
+} // anonymous namespace
+
+static mozilla::StaticAutoPtr<BackgroundMTAData> sMTAData;
+
+namespace mozilla {
+namespace mscom {
+
+/* static */ nsCOMPtr<nsIThread>
+EnsureMTA::GetMTAThread()
+{
+ if (!sMTAData) {
+ sMTAData = new BackgroundMTAData();
+ ClearOnShutdown(&sMTAData, ShutdownPhase::ShutdownThreads);
+ }
+ return sMTAData->GetThread();
+}
+
+} // namespace mscom
+} // namespace mozilla
+
diff --git a/ipc/mscom/EnsureMTA.h b/ipc/mscom/EnsureMTA.h
new file mode 100644
index 000000000..1a8331daa
--- /dev/null
+++ b/ipc/mscom/EnsureMTA.h
@@ -0,0 +1,87 @@
+/* -*- 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_EnsureMTA_h
+#define mozilla_mscom_EnsureMTA_h
+
+#include "MainThreadUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Function.h"
+#include "mozilla/mscom/COMApartmentRegion.h"
+#include "mozilla/mscom/Utils.h"
+#include "nsCOMPtr.h"
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+
+#include <windows.h>
+
+namespace mozilla {
+namespace mscom {
+
+// This class is OK to use as a temporary on the stack.
+class MOZ_STACK_CLASS EnsureMTA
+{
+public:
+ /**
+ * This constructor just ensures that the MTA thread is up and running.
+ */
+ EnsureMTA()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIThread> thread = GetMTAThread();
+ MOZ_ASSERT(thread);
+ }
+
+ template <typename FuncT>
+ EnsureMTA(const FuncT& aClosure)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (IsCurrentThreadMTA()) {
+ // We're already on the MTA, we can run aClosure directly
+ aClosure();
+ return;
+ }
+
+ // In this case we need to run aClosure on a background thread in the MTA
+ nsCOMPtr<nsIThread> thread = GetMTAThread();
+ MOZ_ASSERT(thread);
+
+ HANDLE event = ::CreateEventW(nullptr, FALSE, FALSE, nullptr);
+ if (!event) {
+ return;
+ }
+
+ auto eventSetter = [&]() -> void {
+ aClosure();
+ ::SetEvent(event);
+ };
+
+ nsresult rv =
+ thread->Dispatch(NS_NewRunnableFunction(eventSetter), NS_DISPATCH_NORMAL);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ ::CloseHandle(event);
+ return;
+ }
+
+ DWORD waitResult;
+ while ((waitResult = ::WaitForSingleObjectEx(event, INFINITE, TRUE)) ==
+ WAIT_IO_COMPLETION) {
+ }
+ MOZ_ASSERT(waitResult == WAIT_OBJECT_0);
+ ::CloseHandle(event);
+ }
+
+private:
+ static nsCOMPtr<nsIThread> GetMTAThread();
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_EnsureMTA_h
+
diff --git a/ipc/mscom/Interceptor.cpp b/ipc/mscom/Interceptor.cpp
new file mode 100644
index 000000000..0361cfc6d
--- /dev/null
+++ b/ipc/mscom/Interceptor.cpp
@@ -0,0 +1,313 @@
+/* -*- 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/. */
+
+#define INITGUID
+#include "mozilla/mscom/Interceptor.h"
+#include "mozilla/mscom/InterceptorLog.h"
+
+#include "mozilla/mscom/DispatchForwarder.h"
+#include "mozilla/mscom/MainThreadInvoker.h"
+#include "mozilla/mscom/Registration.h"
+#include "mozilla/mscom/Utils.h"
+#include "MainThreadUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace mscom {
+
+/* static */ HRESULT
+Interceptor::Create(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink,
+ REFIID aIid, void** aOutput)
+{
+ MOZ_ASSERT(aOutput && aTarget && aSink);
+ if (!aOutput) {
+ return E_INVALIDARG;
+ }
+ *aOutput = nullptr;
+ if (!aTarget || !aSink) {
+ return E_INVALIDARG;
+ }
+ Interceptor* intcpt = new Interceptor(Move(aTarget), aSink);
+ HRESULT hr = intcpt->QueryInterface(aIid, aOutput);
+ static_cast<WeakReferenceSupport*>(intcpt)->Release();
+ return hr;
+}
+
+Interceptor::Interceptor(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink)
+ : WeakReferenceSupport(WeakReferenceSupport::Flags::eDestroyOnMainThread)
+ , mTarget(Move(aTarget))
+ , mEventSink(aSink)
+ , mMutex("mozilla::mscom::Interceptor::mMutex")
+{
+ MOZ_ASSERT(aSink);
+ MOZ_ASSERT(!IsProxy(mTarget.get()));
+ RefPtr<IWeakReference> weakRef;
+ if (SUCCEEDED(GetWeakReference(getter_AddRefs(weakRef)))) {
+ aSink->SetInterceptor(weakRef);
+ }
+}
+
+Interceptor::~Interceptor()
+{
+ // This needs to run on the main thread because it releases target interface
+ // reference counts which may not be thread-safe.
+ MOZ_ASSERT(NS_IsMainThread());
+ for (uint32_t index = 0, len = mInterceptorMap.Length(); index < len; ++index) {
+ MapEntry& entry = mInterceptorMap[index];
+ entry.mInterceptor->Release();
+ entry.mTargetInterface->Release();
+ }
+}
+
+Interceptor::MapEntry*
+Interceptor::Lookup(REFIID aIid)
+{
+ mMutex.AssertCurrentThreadOwns();
+ for (uint32_t index = 0, len = mInterceptorMap.Length(); index < len; ++index) {
+ if (mInterceptorMap[index].mIID == aIid) {
+ return &mInterceptorMap[index];
+ }
+ }
+ return nullptr;
+}
+
+HRESULT
+Interceptor::GetTargetForIID(REFIID aIid, InterceptorTargetPtr& aTarget)
+{
+ MutexAutoLock lock(mMutex);
+ MapEntry* entry = Lookup(aIid);
+ if (entry) {
+ aTarget.reset(entry->mTargetInterface);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+}
+
+// CoGetInterceptor requires information from a typelib to be able to
+// generate its emulated vtable. If a typelib is unavailable,
+// CoGetInterceptor returns 0x80070002.
+static const HRESULT kFileNotFound = 0x80070002;
+
+HRESULT
+Interceptor::CreateInterceptor(REFIID aIid, IUnknown* aOuter, IUnknown** aOutput)
+{
+ // In order to aggregate, we *must* request IID_IUnknown as the initial
+ // interface for the interceptor, as that IUnknown is non-delegating.
+ // This is a fundamental rule for creating aggregated objects in COM.
+ HRESULT hr = ::CoGetInterceptor(aIid, aOuter, IID_IUnknown, (void**)aOutput);
+ if (hr != kFileNotFound) {
+ return hr;
+ }
+
+ // In the case that CoGetInterceptor returns kFileNotFound, we can try to
+ // explicitly load typelib data from our runtime registration facility and
+ // pass that into CoGetInterceptorFromTypeInfo.
+
+ RefPtr<ITypeInfo> typeInfo;
+ bool found = RegisteredProxy::Find(aIid, getter_AddRefs(typeInfo));
+ // If this assert fires then we have omitted registering the typelib for a
+ // required interface. To fix this, review our calls to mscom::RegisterProxy
+ // and mscom::RegisterTypelib, and add the additional typelib as necessary.
+ MOZ_ASSERT(found);
+ if (!found) {
+ return kFileNotFound;
+ }
+
+ hr = ::CoGetInterceptorFromTypeInfo(aIid, aOuter, typeInfo, IID_IUnknown,
+ (void**)aOutput);
+ // If this assert fires then the interceptor doesn't like something about
+ // the format of the typelib. One thing in particular that it doesn't like
+ // is complex types that contain unions.
+ MOZ_ASSERT(SUCCEEDED(hr));
+ return hr;
+}
+
+/**
+ * This method contains the core guts of the handling of QueryInterface calls
+ * that are delegated to us from the ICallInterceptor.
+ *
+ * @param aIid ID of the desired interface
+ * @param aOutInterceptor The resulting emulated vtable that corresponds to
+ * the interface specified by aIid.
+ */
+HRESULT
+Interceptor::GetInterceptorForIID(REFIID aIid, void** aOutInterceptor)
+{
+ if (!aOutInterceptor) {
+ return E_INVALIDARG;
+ }
+
+ if (aIid == IID_IUnknown) {
+ // Special case: When we see IUnknown, we just provide a reference to this
+ *aOutInterceptor = static_cast<IInterceptor*>(this);
+ AddRef();
+ return S_OK;
+ }
+
+ RefPtr<IUnknown> unkInterceptor;
+ IUnknown* interfaceForQILog = nullptr;
+
+ // (1) Check to see if we already have an existing interceptor for aIid.
+
+ { // Scope for lock
+ MutexAutoLock lock(mMutex);
+ MapEntry* entry = Lookup(aIid);
+ if (entry) {
+ unkInterceptor = entry->mInterceptor;
+ interfaceForQILog = entry->mTargetInterface;
+ }
+ }
+
+ // (1a) A COM interceptor already exists for this interface, so all we need
+ // to do is run a QI on it.
+ if (unkInterceptor) {
+ // Technically we didn't actually execute a QI on the target interface, but
+ // for logging purposes we would like to record the fact that this interface
+ // was requested.
+ InterceptorLog::QI(S_OK, mTarget.get(), aIid, interfaceForQILog);
+
+ return unkInterceptor->QueryInterface(aIid, aOutInterceptor);
+ }
+
+ // (2) Obtain a new target interface.
+
+ // (2a) First, make sure that the target interface is available
+ // NB: We *MUST* query the correct interface! ICallEvents::Invoke casts its
+ // pvReceiver argument directly to the required interface! DO NOT assume
+ // that COM will use QI or upcast/downcast!
+ HRESULT hr;
+
+ STAUniquePtr<IUnknown> targetInterface;
+ IUnknown* rawTargetInterface = nullptr;
+ hr = QueryInterfaceTarget(aIid, (void**)&rawTargetInterface);
+ targetInterface.reset(rawTargetInterface);
+ InterceptorLog::QI(hr, mTarget.get(), aIid, targetInterface.get());
+ MOZ_ASSERT(SUCCEEDED(hr) || hr == E_NOINTERFACE);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // We *really* shouldn't be adding interceptors to proxies
+ MOZ_ASSERT(aIid != IID_IMarshal);
+
+ // (3) Create a new COM interceptor to that interface that delegates its
+ // IUnknown to |this|.
+
+ // Raise the refcount for stabilization purposes during aggregation
+ RefPtr<IUnknown> kungFuDeathGrip(static_cast<IUnknown*>(
+ static_cast<WeakReferenceSupport*>(this)));
+
+ hr = CreateInterceptor(aIid, kungFuDeathGrip, getter_AddRefs(unkInterceptor));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // (4) Obtain the interceptor's ICallInterceptor interface and register our
+ // event sink.
+ RefPtr<ICallInterceptor> interceptor;
+ hr = unkInterceptor->QueryInterface(IID_ICallInterceptor,
+ (void**)getter_AddRefs(interceptor));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ hr = interceptor->RegisterSink(mEventSink);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // (5) Now that we have this new COM interceptor, insert it into the map.
+
+ { // Scope for lock
+ MutexAutoLock lock(mMutex);
+ // We might have raced with another thread, so first check that we don't
+ // already have an entry for this
+ MapEntry* entry = Lookup(aIid);
+ if (entry && entry->mInterceptor) {
+ unkInterceptor = entry->mInterceptor;
+ } else {
+ // We're inserting unkInterceptor into the map but we still want to hang
+ // onto it locally so that we can QI it below.
+ unkInterceptor->AddRef();
+ // OTOH we must not touch the refcount for the target interface
+ // because we are just moving it into the map and its refcounting might
+ // not be thread-safe.
+ IUnknown* rawTargetInterface = targetInterface.release();
+ mInterceptorMap.AppendElement(MapEntry(aIid,
+ unkInterceptor,
+ rawTargetInterface));
+ }
+ }
+
+ return unkInterceptor->QueryInterface(aIid, aOutInterceptor);
+}
+
+HRESULT
+Interceptor::QueryInterfaceTarget(REFIID aIid, void** aOutput)
+{
+ // NB: This QI needs to run on the main thread because the target object
+ // is probably Gecko code that is not thread-safe. Note that this main
+ // thread invocation is *synchronous*.
+ MainThreadInvoker invoker;
+ HRESULT hr;
+ auto runOnMainThread = [&]() -> void {
+ MOZ_ASSERT(NS_IsMainThread());
+ hr = mTarget->QueryInterface(aIid, aOutput);
+ };
+ if (!invoker.Invoke(NS_NewRunnableFunction(runOnMainThread))) {
+ return E_FAIL;
+ }
+ return hr;
+}
+
+HRESULT
+Interceptor::QueryInterface(REFIID riid, void** ppv)
+{
+ return WeakReferenceSupport::QueryInterface(riid, ppv);
+}
+
+HRESULT
+Interceptor::ThreadSafeQueryInterface(REFIID aIid, IUnknown** aOutInterface)
+{
+ if (aIid == IID_IInterceptor) {
+ *aOutInterface = static_cast<IInterceptor*>(this);
+ (*aOutInterface)->AddRef();
+ return S_OK;
+ }
+
+ if (aIid == IID_IDispatch) {
+ STAUniquePtr<IDispatch> disp;
+ IDispatch* rawDisp = nullptr;
+ HRESULT hr = QueryInterfaceTarget(aIid, (void**)&rawDisp);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ disp.reset(rawDisp);
+ return DispatchForwarder::Create(this, disp, aOutInterface);
+ }
+
+ return GetInterceptorForIID(aIid, (void**)aOutInterface);
+}
+
+ULONG
+Interceptor::AddRef()
+{
+ return WeakReferenceSupport::AddRef();
+}
+
+ULONG
+Interceptor::Release()
+{
+ return WeakReferenceSupport::Release();
+}
+
+} // namespace mscom
+} // namespace mozilla
diff --git a/ipc/mscom/Interceptor.h b/ipc/mscom/Interceptor.h
new file mode 100644
index 000000000..ecddbddf5
--- /dev/null
+++ b/ipc/mscom/Interceptor.h
@@ -0,0 +1,125 @@
+/* -*- 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_interceptor_h
+#define mozilla_mscom_interceptor_h
+
+#include "mozilla/Move.h"
+#include "mozilla/Mutex.h"
+#include "nsTArray.h"
+#include "mozilla/mscom/Ptr.h"
+#include "mozilla/mscom/WeakRef.h"
+#include "mozilla/RefPtr.h"
+
+#include <callobj.h>
+
+namespace mozilla {
+namespace mscom {
+
+// {8831EB53-A937-42BC-9921-B3E1121FDF86}
+DEFINE_GUID(IID_IInterceptorSink,
+0x8831eb53, 0xa937, 0x42bc, 0x99, 0x21, 0xb3, 0xe1, 0x12, 0x1f, 0xdf, 0x86);
+
+struct IInterceptorSink : public ICallFrameEvents
+{
+ virtual STDMETHODIMP SetInterceptor(IWeakReference* aInterceptor) = 0;
+};
+
+// {3710799B-ECA2-4165-B9B0-3FA1E4A9B230}
+DEFINE_GUID(IID_IInterceptor,
+0x3710799b, 0xeca2, 0x4165, 0xb9, 0xb0, 0x3f, 0xa1, 0xe4, 0xa9, 0xb2, 0x30);
+
+struct IInterceptor : public IUnknown
+{
+ virtual STDMETHODIMP GetTargetForIID(REFIID aIid, InterceptorTargetPtr& aTarget) = 0;
+ virtual STDMETHODIMP GetInterceptorForIID(REFIID aIid,
+ void** aOutInterceptor) = 0;
+};
+
+/**
+ * The COM interceptor is the core functionality in mscom that allows us to
+ * redirect method calls to different threads. It emulates the vtable of a
+ * target interface. When a call is made on this emulated vtable, the call is
+ * packaged up into an instance of the ICallFrame interface which may be passed
+ * to other contexts for execution.
+ *
+ * In order to accomplish this, COM itself provides the CoGetInterceptor
+ * function, which instantiates an ICallInterceptor. Note, however, that
+ * ICallInterceptor only works on a single interface; we need to be able to
+ * interpose QueryInterface calls so that we can instantiate a new
+ * ICallInterceptor for each new interface that is requested.
+ *
+ * We accomplish this by using COM aggregation, which means that the
+ * ICallInterceptor delegates its IUnknown implementation to its outer object
+ * (the mscom::Interceptor we implement and control).
+ */
+class Interceptor final : public WeakReferenceSupport
+ , public IInterceptor
+{
+public:
+ static HRESULT Create(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink,
+ REFIID aIid, void** aOutput);
+
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
+ STDMETHODIMP_(ULONG) AddRef() override;
+ STDMETHODIMP_(ULONG) Release() override;
+
+ // IInterceptor
+ STDMETHODIMP GetTargetForIID(REFIID aIid, InterceptorTargetPtr& aTarget) override;
+ STDMETHODIMP GetInterceptorForIID(REFIID aIid, void** aOutInterceptor) override;
+
+private:
+ struct MapEntry
+ {
+ MapEntry(REFIID aIid, IUnknown* aInterceptor, IUnknown* aTargetInterface)
+ : mIID(aIid)
+ , mInterceptor(aInterceptor)
+ , mTargetInterface(aTargetInterface)
+ {}
+ IID mIID;
+ IUnknown* mInterceptor;
+ IUnknown* mTargetInterface;
+ };
+
+private:
+ Interceptor(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink);
+ ~Interceptor();
+ MapEntry* Lookup(REFIID aIid);
+ HRESULT QueryInterfaceTarget(REFIID aIid, void** aOutput);
+ HRESULT ThreadSafeQueryInterface(REFIID aIid,
+ IUnknown** aOutInterface) override;
+ HRESULT CreateInterceptor(REFIID aIid, IUnknown* aOuter, IUnknown** aOutput);
+
+private:
+ STAUniquePtr<IUnknown> mTarget;
+ RefPtr<IInterceptorSink> mEventSink;
+ mozilla::Mutex mMutex; // Guards mInterceptorMap
+ // Using a nsTArray since the # of interfaces is not going to be very high
+ nsTArray<MapEntry> mInterceptorMap;
+};
+
+template <typename InterfaceT>
+inline HRESULT
+CreateInterceptor(STAUniquePtr<InterfaceT> aTargetInterface,
+ IInterceptorSink* aEventSink,
+ InterfaceT** aOutInterface)
+{
+ if (!aTargetInterface || !aEventSink) {
+ return E_INVALIDARG;
+ }
+
+ REFIID iidTarget = __uuidof(aTargetInterface);
+
+ STAUniquePtr<IUnknown> targetUnknown(aTargetInterface.release());
+ return Interceptor::Create(Move(targetUnknown), aEventSink, iidTarget,
+ (void**)aOutInterface);
+}
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_interceptor_h
diff --git a/ipc/mscom/InterceptorLog.cpp b/ipc/mscom/InterceptorLog.cpp
new file mode 100644
index 000000000..c2cd3c7df
--- /dev/null
+++ b/ipc/mscom/InterceptorLog.cpp
@@ -0,0 +1,466 @@
+/* -*- 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/InterceptorLog.h"
+
+#include "MainThreadUtils.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/mscom/Registration.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Unused.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIFileStreams.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsNetUtil.h"
+#include "nsPrintfCString.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOMPrivate.h"
+#include "nsXULAppAPI.h"
+#include "prenv.h"
+
+#include <callobj.h>
+
+using mozilla::DebugOnly;
+using mozilla::mscom::ArrayData;
+using mozilla::mscom::FindArrayData;
+using mozilla::Mutex;
+using mozilla::MutexAutoLock;
+using mozilla::NewNonOwningRunnableMethod;
+using mozilla::services::GetObserverService;
+using mozilla::StaticAutoPtr;
+using mozilla::TimeDuration;
+using mozilla::TimeStamp;
+using mozilla::Unused;
+
+namespace {
+
+class ShutdownEvent final : public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+private:
+ ~ShutdownEvent() {}
+};
+
+NS_IMPL_ISUPPORTS(ShutdownEvent, nsIObserver)
+
+class Logger
+{
+public:
+ explicit Logger(const nsACString& aLeafBaseName);
+ bool IsValid()
+ {
+ MutexAutoLock lock(mMutex);
+ return !!mThread;
+ }
+ void LogQI(HRESULT aResult, IUnknown* aTarget, REFIID aIid, IUnknown* aInterface);
+ void LogEvent(ICallFrame* aCallFrame, IUnknown* aTargetInterface);
+ nsresult Shutdown();
+
+private:
+ void OpenFile();
+ void Flush();
+ void CloseFile();
+ void AssertRunningOnLoggerThread();
+ bool VariantToString(const VARIANT& aVariant, nsACString& aOut, LONG aIndex = 0);
+ static double GetElapsedTime();
+
+ nsCOMPtr<nsIFile> mLogFileName;
+ nsCOMPtr<nsIOutputStream> mLogFile; // Only accessed by mThread
+ Mutex mMutex; // Guards mThread and mEntries
+ nsCOMPtr<nsIThread> mThread;
+ nsTArray<nsCString> mEntries;
+};
+
+Logger::Logger(const nsACString& aLeafBaseName)
+ : mMutex("mozilla::com::InterceptorLog::Logger")
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIFile> logFileName;
+ GeckoProcessType procType = XRE_GetProcessType();
+ nsAutoCString leafName(aLeafBaseName);
+ nsresult rv;
+ if (procType == GeckoProcessType_Default) {
+ leafName.AppendLiteral("-Parent-");
+ rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(logFileName));
+ } else if (procType == GeckoProcessType_Content) {
+ leafName.AppendLiteral("-Content-");
+#if defined(MOZ_CONTENT_SANDBOX)
+ rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
+ getter_AddRefs(logFileName));
+#else
+ rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
+ getter_AddRefs(logFileName));
+#endif // defined(MOZ_CONTENT_SANDBOX)
+ } else {
+ return;
+ }
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ DWORD pid = GetCurrentProcessId();
+ leafName.AppendPrintf("%u.log", pid);
+ // Using AppendNative here because Windows
+ rv = logFileName->AppendNative(leafName);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ mLogFileName.swap(logFileName);
+
+ nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
+ nsCOMPtr<nsIObserver> shutdownEvent = new ShutdownEvent();
+ rv = obsSvc->AddObserver(shutdownEvent, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
+ false);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsCOMPtr<nsIRunnable> openRunnable(
+ NewNonOwningRunnableMethod(this, &Logger::OpenFile));
+ rv = NS_NewNamedThread("COM Intcpt Log", getter_AddRefs(mThread),
+ openRunnable);
+ if (NS_FAILED(rv)) {
+ obsSvc->RemoveObserver(shutdownEvent, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID);
+ }
+}
+
+void
+Logger::AssertRunningOnLoggerThread()
+{
+#if defined(DEBUG)
+ nsCOMPtr<nsIThread> curThread;
+ if (NS_FAILED(NS_GetCurrentThread(getter_AddRefs(curThread)))) {
+ return;
+ }
+ MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(curThread == mThread);
+#endif
+}
+
+void
+Logger::OpenFile()
+{
+ AssertRunningOnLoggerThread();
+ MOZ_ASSERT(mLogFileName && !mLogFile);
+ NS_NewLocalFileOutputStream(getter_AddRefs(mLogFile), mLogFileName,
+ PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
+ PR_IRUSR | PR_IWUSR | PR_IRGRP);
+}
+
+void
+Logger::CloseFile()
+{
+ AssertRunningOnLoggerThread();
+ MOZ_ASSERT(mLogFile);
+ if (!mLogFile) {
+ return;
+ }
+ Flush();
+ mLogFile->Close();
+ mLogFile = nullptr;
+}
+
+nsresult
+Logger::Shutdown()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ nsresult rv = mThread->Dispatch(NewNonOwningRunnableMethod(this,
+ &Logger::CloseFile),
+ NS_DISPATCH_NORMAL);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Dispatch failed");
+
+ rv = mThread->Shutdown();
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Shutdown failed");
+ return NS_OK;
+}
+
+bool
+Logger::VariantToString(const VARIANT& aVariant, nsACString& aOut, LONG aIndex)
+{
+ switch (aVariant.vt) {
+ case VT_DISPATCH: {
+ aOut.AppendPrintf("(IDispatch*) 0x%0p", aVariant.pdispVal);
+ return true;
+ }
+ case VT_DISPATCH | VT_BYREF: {
+ aOut.AppendPrintf("(IDispatch*) 0x%0p", (aVariant.ppdispVal)[aIndex]);
+ return true;
+ }
+ case VT_UNKNOWN: {
+ aOut.AppendPrintf("(IUnknown*) 0x%0p", aVariant.punkVal);
+ return true;
+ }
+ case VT_UNKNOWN | VT_BYREF: {
+ aOut.AppendPrintf("(IUnknown*) 0x%0p", (aVariant.ppunkVal)[aIndex]);
+ return true;
+ }
+ case VT_VARIANT | VT_BYREF: {
+ return VariantToString((aVariant.pvarVal)[aIndex], aOut);
+ }
+ case VT_I4 | VT_BYREF: {
+ aOut.AppendPrintf("%d", aVariant.plVal[aIndex]);
+ return true;
+ }
+ case VT_UI4 | VT_BYREF: {
+ aOut.AppendPrintf("%u", aVariant.pulVal[aIndex]);
+ return true;
+ }
+ case VT_I4: {
+ aOut.AppendPrintf("%d", aVariant.lVal);
+ return true;
+ }
+ case VT_UI4: {
+ aOut.AppendPrintf("%u", aVariant.ulVal);
+ return true;
+ }
+ case VT_EMPTY: {
+ aOut.AppendLiteral("(empty VARIANT)");
+ return true;
+ }
+ case VT_NULL: {
+ aOut.AppendLiteral("(null VARIANT)");
+ return true;
+ }
+ case VT_BSTR: {
+ aOut.AppendPrintf("\"%S\"", aVariant.bstrVal);
+ return true;
+ }
+ case VT_BSTR | VT_BYREF: {
+ aOut.AppendPrintf("\"%S\"", *aVariant.pbstrVal);
+ return true;
+ }
+ default: {
+ aOut.AppendPrintf("(VariantToString failed, VARTYPE == 0x%04hx)",
+ aVariant.vt);
+ return false;
+ }
+ }
+}
+
+/* static */ double
+Logger::GetElapsedTime()
+{
+ TimeStamp ts = TimeStamp::Now();
+ bool inconsistent;
+ TimeDuration duration = ts - TimeStamp::ProcessCreation(inconsistent);
+ return duration.ToMicroseconds();
+}
+
+void
+Logger::LogQI(HRESULT aResult, IUnknown* aTarget, REFIID aIid, IUnknown* aInterface)
+{
+ if (FAILED(aResult)) {
+ return;
+ }
+ double elapsed = GetElapsedTime();
+
+ nsPrintfCString line("%fus\t0x%0p\tIUnknown::QueryInterface\t([in] ", elapsed,
+ aTarget);
+
+ WCHAR buf[39] = {0};
+ if (StringFromGUID2(aIid, buf, mozilla::ArrayLength(buf))) {
+ line.AppendPrintf("%S", buf);
+ } else {
+ line.AppendLiteral("(IID Conversion Failed)");
+ }
+ line.AppendPrintf(", [out] 0x%p)\t0x%08X\n", aInterface, aResult);
+
+ MutexAutoLock lock(mMutex);
+ mEntries.AppendElement(line);
+ mThread->Dispatch(NewNonOwningRunnableMethod(this, &Logger::Flush),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+Logger::LogEvent(ICallFrame* aCallFrame, IUnknown* aTargetInterface)
+{
+ // (1) Gather info about the call
+ double elapsed = GetElapsedTime();
+
+ CALLFRAMEINFO callInfo;
+ HRESULT hr = aCallFrame->GetInfo(&callInfo);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ PWSTR interfaceName = nullptr;
+ PWSTR methodName = nullptr;
+ hr = aCallFrame->GetNames(&interfaceName, &methodName);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ // (2) Serialize the call
+ nsPrintfCString line("%fus\t0x%p\t%S::%S\t(", elapsed,
+ aTargetInterface, interfaceName, methodName);
+
+ CoTaskMemFree(interfaceName);
+ interfaceName = nullptr;
+ CoTaskMemFree(methodName);
+ methodName = nullptr;
+
+ // Check for supplemental array data
+ const ArrayData* arrayData = FindArrayData(callInfo.iid, callInfo.iMethod);
+
+ for (ULONG paramIndex = 0; paramIndex < callInfo.cParams; ++paramIndex) {
+ CALLFRAMEPARAMINFO paramInfo;
+ hr = aCallFrame->GetParamInfo(paramIndex, &paramInfo);
+ if (SUCCEEDED(hr)) {
+ line.AppendLiteral("[");
+ if (paramInfo.fIn) {
+ line.AppendLiteral("in");
+ }
+ if (paramInfo.fOut) {
+ line.AppendLiteral("out");
+ }
+ line.AppendLiteral("] ");
+ }
+ VARIANT paramValue;
+ hr = aCallFrame->GetParam(paramIndex, &paramValue);
+ if (SUCCEEDED(hr)) {
+ if (arrayData && paramIndex == arrayData->mArrayParamIndex) {
+ VARIANT lengthParam;
+ hr = aCallFrame->GetParam(arrayData->mLengthParamIndex, &lengthParam);
+ if (SUCCEEDED(hr)) {
+ line.AppendLiteral("{ ");
+ for (LONG i = 0; i < *lengthParam.plVal; ++i) {
+ VariantToString(paramValue, line, i);
+ if (i < *lengthParam.plVal - 1) {
+ line.AppendLiteral(", ");
+ }
+ }
+ line.AppendLiteral(" }");
+ } else {
+ line.AppendPrintf("(GetParam failed with HRESULT 0x%08X)", hr);
+ }
+ } else {
+ VariantToString(paramValue, line);
+ }
+ } else {
+ line.AppendPrintf("(GetParam failed with HRESULT 0x%08X)", hr);
+ }
+ if (paramIndex < callInfo.cParams - 1) {
+ line.AppendLiteral(", ");
+ }
+ }
+ line.AppendLiteral(")\t");
+
+ HRESULT callResult = aCallFrame->GetReturnValue();
+ line.AppendPrintf("0x%08X\n", callResult);
+
+ // (3) Enqueue event for logging
+ MutexAutoLock lock(mMutex);
+ mEntries.AppendElement(line);
+ mThread->Dispatch(NewNonOwningRunnableMethod(this, &Logger::Flush),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+Logger::Flush()
+{
+ AssertRunningOnLoggerThread();
+ MOZ_ASSERT(mLogFile);
+ if (!mLogFile) {
+ return;
+ }
+ nsTArray<nsCString> linesToWrite;
+ { // Scope for lock
+ MutexAutoLock lock(mMutex);
+ linesToWrite.SwapElements(mEntries);
+ }
+
+ for (uint32_t i = 0, len = linesToWrite.Length(); i < len; ++i) {
+ uint32_t bytesWritten;
+ nsCString& line = linesToWrite[i];
+ nsresult rv = mLogFile->Write(line.get(), line.Length(), &bytesWritten);
+ NS_WARN_IF(NS_FAILED(rv));
+ }
+}
+
+StaticAutoPtr<Logger> sLogger;
+
+NS_IMETHODIMP
+ShutdownEvent::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) {
+ MOZ_ASSERT(false);
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ MOZ_ASSERT(sLogger);
+ Unused << NS_WARN_IF(NS_FAILED(sLogger->Shutdown()));
+ nsCOMPtr<nsIObserver> kungFuDeathGrip(this);
+ nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
+ obsSvc->RemoveObserver(this, aTopic);
+ return NS_OK;
+}
+} // anonymous namespace
+
+
+static bool
+MaybeCreateLog(const char* aEnvVarName)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(XRE_IsContentProcess() || XRE_IsParentProcess());
+ MOZ_ASSERT(!sLogger);
+ const char* leafBaseName = PR_GetEnv(aEnvVarName);
+ if (!leafBaseName) {
+ return false;
+ }
+ nsDependentCString strLeafBaseName(leafBaseName);
+ if (strLeafBaseName.IsEmpty()) {
+ return false;
+ }
+ sLogger = new Logger(strLeafBaseName);
+ if (!sLogger->IsValid()) {
+ sLogger = nullptr;
+ return false;
+ }
+ ClearOnShutdown(&sLogger);
+ return true;
+}
+
+namespace mozilla {
+namespace mscom {
+
+/* static */ bool
+InterceptorLog::Init()
+{
+ static const bool isEnabled = MaybeCreateLog("MOZ_MSCOM_LOG_BASENAME");
+ return isEnabled;
+}
+
+/* static */ void
+InterceptorLog::QI(HRESULT aResult, IUnknown* aTarget, REFIID aIid, IUnknown* aInterface)
+{
+ if (!sLogger) {
+ return;
+ }
+ sLogger->LogQI(aResult, aTarget, aIid, aInterface);
+}
+
+/* static */ void
+InterceptorLog::Event(ICallFrame* aCallFrame, IUnknown* aTargetInterface)
+{
+ if (!sLogger) {
+ return;
+ }
+ sLogger->LogEvent(aCallFrame, aTargetInterface);
+}
+
+} // namespace mscom
+} // namespace mozilla
+
diff --git a/ipc/mscom/InterceptorLog.h b/ipc/mscom/InterceptorLog.h
new file mode 100644
index 000000000..6469bd0ed
--- /dev/null
+++ b/ipc/mscom/InterceptorLog.h
@@ -0,0 +1,28 @@
+/* -*- 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_InterceptorLog_h
+#define mozilla_mscom_InterceptorLog_h
+
+struct ICallFrame;
+struct IUnknown;
+
+namespace mozilla {
+namespace mscom {
+
+class InterceptorLog
+{
+public:
+ static bool Init();
+ static void QI(HRESULT aResult, IUnknown* aTarget, REFIID aIid, IUnknown* aInterface);
+ static void Event(ICallFrame* aCallFrame, IUnknown* aTarget);
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_InterceptorLog_h
+
diff --git a/ipc/mscom/MainThreadHandoff.cpp b/ipc/mscom/MainThreadHandoff.cpp
new file mode 100644
index 000000000..21ac20faa
--- /dev/null
+++ b/ipc/mscom/MainThreadHandoff.cpp
@@ -0,0 +1,398 @@
+/* -*- 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/MainThreadHandoff.h"
+
+#include "mozilla/Move.h"
+#include "mozilla/mscom/InterceptorLog.h"
+#include "mozilla/mscom/Registration.h"
+#include "mozilla/mscom/Utils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "nsThreadUtils.h"
+#include "nsProxyRelease.h"
+
+using mozilla::DebugOnly;
+
+namespace {
+
+class HandoffRunnable : public mozilla::Runnable
+{
+public:
+ explicit HandoffRunnable(ICallFrame* aCallFrame, IUnknown* aTargetInterface)
+ : mCallFrame(aCallFrame)
+ , mTargetInterface(aTargetInterface)
+ , mResult(E_UNEXPECTED)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ mResult = mCallFrame->Invoke(mTargetInterface);
+ return NS_OK;
+ }
+
+ HRESULT GetResult() const
+ {
+ return mResult;
+ }
+
+private:
+ ICallFrame* mCallFrame;
+ IUnknown* mTargetInterface;
+ HRESULT mResult;
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace mscom {
+
+/* static */ HRESULT
+MainThreadHandoff::Create(IInterceptorSink** aOutput)
+{
+ *aOutput = nullptr;
+ MainThreadHandoff* handoff = new MainThreadHandoff();
+ HRESULT hr = handoff->QueryInterface(IID_IInterceptorSink, (void**) aOutput);
+ handoff->Release();
+ return hr;
+}
+
+MainThreadHandoff::MainThreadHandoff()
+ : mRefCnt(1)
+{
+}
+
+MainThreadHandoff::~MainThreadHandoff()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+HRESULT
+MainThreadHandoff::QueryInterface(REFIID riid, void** ppv)
+{
+ IUnknown* punk = nullptr;
+ if (!ppv) {
+ return E_INVALIDARG;
+ }
+
+ if (riid == IID_IUnknown || riid == IID_ICallFrameEvents ||
+ riid == IID_IInterceptorSink) {
+ punk = static_cast<IInterceptorSink*>(this);
+ } else if (riid == IID_ICallFrameWalker) {
+ punk = static_cast<ICallFrameWalker*>(this);
+ }
+
+ *ppv = punk;
+ if (!punk) {
+ return E_NOINTERFACE;
+ }
+
+ punk->AddRef();
+ return S_OK;
+}
+
+ULONG
+MainThreadHandoff::AddRef()
+{
+ return (ULONG) InterlockedIncrement((LONG*)&mRefCnt);
+}
+
+ULONG
+MainThreadHandoff::Release()
+{
+ ULONG newRefCnt = (ULONG) InterlockedDecrement((LONG*)&mRefCnt);
+ if (newRefCnt == 0) {
+ // It is possible for the last Release() call to happen off-main-thread.
+ // If so, we need to dispatch an event to delete ourselves.
+ if (NS_IsMainThread()) {
+ delete this;
+ } else {
+ // We need to delete this object on the main thread, but we aren't on the
+ // main thread right now, so we send a reference to ourselves to the main
+ // thread to be re-released there.
+ RefPtr<MainThreadHandoff> self = this;
+ NS_ReleaseOnMainThread(self.forget());
+ }
+ }
+ return newRefCnt;
+}
+
+HRESULT
+MainThreadHandoff::OnCall(ICallFrame* aFrame)
+{
+ // (1) Get info about the method call
+ HRESULT hr;
+ IID iid;
+ ULONG method;
+ hr = aFrame->GetIIDAndMethod(&iid, &method);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ RefPtr<IInterceptor> interceptor;
+ hr = mInterceptor->Resolve(IID_IInterceptor,
+ (void**)getter_AddRefs(interceptor));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ InterceptorTargetPtr targetInterface;
+ hr = interceptor->GetTargetForIID(iid, targetInterface);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // (2) Execute the method call syncrhonously on the main thread
+ RefPtr<HandoffRunnable> handoffInfo(new HandoffRunnable(aFrame,
+ targetInterface.get()));
+ MainThreadInvoker invoker;
+ if (!invoker.Invoke(do_AddRef(handoffInfo))) {
+ MOZ_ASSERT(false);
+ return E_UNEXPECTED;
+ }
+ hr = handoffInfo->GetResult();
+ MOZ_ASSERT(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // (3) Log *before* wrapping outputs so that the log will contain pointers to
+ // the true target interface, not the wrapped ones.
+ InterceptorLog::Event(aFrame, targetInterface.get());
+
+ // (4) Scan the function call for outparams that contain interface pointers.
+ // Those will need to be wrapped with MainThreadHandoff so that they too will
+ // be exeuted on the main thread.
+
+ hr = aFrame->GetReturnValue();
+ if (FAILED(hr)) {
+ // If the call resulted in an error then there's not going to be anything
+ // that needs to be wrapped.
+ return S_OK;
+ }
+
+ // (5) Unfortunately ICallFrame::WalkFrame does not correctly handle array
+ // outparams. Instead, we find out whether anybody has called
+ // mscom::RegisterArrayData to supply array parameter information and use it
+ // if available. This is a terrible hack, but it works for the short term. In
+ // the longer term we want to be able to use COM proxy/stub metadata to
+ // resolve array information for us.
+ const ArrayData* arrayData = FindArrayData(iid, method);
+ if (arrayData) {
+ hr = FixArrayElements(aFrame, *arrayData);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ } else {
+ // (6) Scan the outputs looking for any outparam interfaces that need wrapping.
+ // NB: WalkFrame does not correctly handle array outparams. It processes the
+ // first element of an array but not the remaining elements (if any).
+ hr = aFrame->WalkFrame(CALLFRAME_WALK_OUT, this);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+
+ return S_OK;
+}
+
+static PVOID
+ResolveArrayPtr(VARIANT& aVariant)
+{
+ if (!(aVariant.vt & VT_BYREF)) {
+ return nullptr;
+ }
+ return aVariant.byref;
+}
+
+static PVOID*
+ResolveInterfacePtr(PVOID aArrayPtr, VARTYPE aVartype, LONG aIndex)
+{
+ if (aVartype != (VT_VARIANT | VT_BYREF)) {
+ IUnknown** ifaceArray = reinterpret_cast<IUnknown**>(aArrayPtr);
+ return reinterpret_cast<PVOID*>(&ifaceArray[aIndex]);
+ }
+ VARIANT* variantArray = reinterpret_cast<VARIANT*>(aArrayPtr);
+ VARIANT& element = variantArray[aIndex];
+ return &element.byref;
+}
+
+HRESULT
+MainThreadHandoff::FixArrayElements(ICallFrame* aFrame,
+ const ArrayData& aArrayData)
+{
+ // Extract the array length
+ VARIANT paramVal;
+ VariantInit(&paramVal);
+ HRESULT hr = aFrame->GetParam(aArrayData.mLengthParamIndex, &paramVal);
+ MOZ_ASSERT(SUCCEEDED(hr) &&
+ (paramVal.vt == (VT_I4 | VT_BYREF) ||
+ paramVal.vt == (VT_UI4 | VT_BYREF)));
+ if (FAILED(hr) || (paramVal.vt != (VT_I4 | VT_BYREF) &&
+ paramVal.vt != (VT_UI4 | VT_BYREF))) {
+ return hr;
+ }
+
+ const LONG arrayLength = *(paramVal.plVal);
+ if (!arrayLength) {
+ // Nothing to do
+ return S_OK;
+ }
+
+ // Extract the array parameter
+ VariantInit(&paramVal);
+ PVOID arrayPtr = nullptr;
+ hr = aFrame->GetParam(aArrayData.mArrayParamIndex, &paramVal);
+ if (hr == DISP_E_BADVARTYPE) {
+ // ICallFrame::GetParam is not able to coerce the param into a VARIANT.
+ // That's ok, we can try to do it ourselves.
+ CALLFRAMEPARAMINFO paramInfo;
+ hr = aFrame->GetParamInfo(aArrayData.mArrayParamIndex, &paramInfo);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ PVOID stackBase = aFrame->GetStackLocation();
+ if (aArrayData.mFlag == ArrayData::Flag::eAllocatedByServer) {
+ // In order for the server to allocate the array's buffer and store it in
+ // an outparam, the parameter must be typed as Type***. Since the base
+ // of the array is Type*, we must dereference twice.
+ arrayPtr = **reinterpret_cast<PVOID**>(reinterpret_cast<PBYTE>(stackBase) +
+ paramInfo.stackOffset);
+ } else {
+ // We dereference because we need to obtain the value of a parameter
+ // from a stack offset. This pointer is the base of the array.
+ arrayPtr = *reinterpret_cast<PVOID*>(reinterpret_cast<PBYTE>(stackBase) +
+ paramInfo.stackOffset);
+ }
+ } else if (FAILED(hr)) {
+ return hr;
+ } else {
+ arrayPtr = ResolveArrayPtr(paramVal);
+ }
+
+ MOZ_ASSERT(arrayPtr);
+ if (!arrayPtr) {
+ return DISP_E_BADVARTYPE;
+ }
+
+ // We walk the elements of the array and invoke OnWalkInterface to wrap each
+ // one, just as ICallFrame::WalkFrame would do.
+ for (LONG index = 0; index < arrayLength; ++index) {
+ hr = OnWalkInterface(aArrayData.mArrayParamIid,
+ ResolveInterfacePtr(arrayPtr, paramVal.vt, index),
+ FALSE, TRUE);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+ return S_OK;
+}
+
+HRESULT
+MainThreadHandoff::SetInterceptor(IWeakReference* aInterceptor)
+{
+ mInterceptor = aInterceptor;
+ return S_OK;
+}
+
+HRESULT
+MainThreadHandoff::OnWalkInterface(REFIID aIid, PVOID* aInterface,
+ BOOL aIsInParam, BOOL aIsOutParam)
+{
+ MOZ_ASSERT(aInterface && aIsOutParam);
+ if (!aInterface || !aIsOutParam) {
+ return E_UNEXPECTED;
+ }
+
+ // Adopt aInterface for the time being. We can't touch its refcount off
+ // the main thread, so we'll use STAUniquePtr so that we can safely
+ // Release() it if necessary.
+ STAUniquePtr<IUnknown> origInterface(static_cast<IUnknown*>(*aInterface));
+ *aInterface = nullptr;
+
+ if (!origInterface) {
+ // Nothing to wrap.
+ return S_OK;
+ }
+
+ // First make sure that aInterface isn't a proxy - we don't want to wrap
+ // those.
+ if (IsProxy(origInterface.get())) {
+ *aInterface = origInterface.release();
+ return S_OK;
+ }
+
+ RefPtr<IInterceptor> interceptor;
+ HRESULT hr = mInterceptor->Resolve(IID_IInterceptor,
+ (void**) getter_AddRefs(interceptor));
+ MOZ_ASSERT(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // Now make sure that origInterface isn't referring to the same IUnknown
+ // as an interface that we are already managing. We can determine this by
+ // querying (NOT casting!) both objects for IUnknown and then comparing the
+ // resulting pointers.
+ InterceptorTargetPtr existingTarget;
+ hr = interceptor->GetTargetForIID(aIid, existingTarget);
+ if (SUCCEEDED(hr)) {
+ bool areIUnknownsEqual = false;
+
+ // This check must be done on the main thread
+ auto checkFn = [&existingTarget, &origInterface, &areIUnknownsEqual]() -> void {
+ RefPtr<IUnknown> unkExisting;
+ HRESULT hrExisting =
+ existingTarget->QueryInterface(IID_IUnknown,
+ (void**)getter_AddRefs(unkExisting));
+ RefPtr<IUnknown> unkNew;
+ HRESULT hrNew =
+ origInterface->QueryInterface(IID_IUnknown,
+ (void**)getter_AddRefs(unkNew));
+ areIUnknownsEqual = SUCCEEDED(hrExisting) && SUCCEEDED(hrNew) &&
+ unkExisting == unkNew;
+ };
+
+ MainThreadInvoker invoker;
+ if (invoker.Invoke(NS_NewRunnableFunction(checkFn)) && areIUnknownsEqual) {
+ // The existing interface and the new interface both belong to the same
+ // target object. Let's just use the existing one.
+ void* intercepted = nullptr;
+ hr = interceptor->GetInterceptorForIID(aIid, &intercepted);
+ MOZ_ASSERT(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ return hr;
+ }
+ *aInterface = intercepted;
+ return S_OK;
+ }
+ }
+
+ // Now create a new MainThreadHandoff wrapper...
+ RefPtr<IInterceptorSink> handoff;
+ hr = MainThreadHandoff::Create(getter_AddRefs(handoff));
+ MOZ_ASSERT(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ RefPtr<IUnknown> wrapped;
+ hr = Interceptor::Create(Move(origInterface), handoff, aIid,
+ getter_AddRefs(wrapped));
+ MOZ_ASSERT(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // And replace the original interface pointer with the wrapped one.
+ wrapped.forget(reinterpret_cast<IUnknown**>(aInterface));
+
+ return S_OK;
+}
+
+} // namespace mscom
+} // namespace mozilla
diff --git a/ipc/mscom/MainThreadHandoff.h b/ipc/mscom/MainThreadHandoff.h
new file mode 100644
index 000000000..a3ae47e1b
--- /dev/null
+++ b/ipc/mscom/MainThreadHandoff.h
@@ -0,0 +1,71 @@
+/* -*- 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_MainThreadHandoff_h
+#define mozilla_mscom_MainThreadHandoff_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Move.h"
+#include "mozilla/mscom/Interceptor.h"
+#include "mozilla/mscom/MainThreadInvoker.h"
+#include "mozilla/mscom/Utils.h"
+#include "mozilla/Mutex.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace mscom {
+
+struct ArrayData;
+
+class MainThreadHandoff final : public IInterceptorSink
+ , public ICallFrameWalker
+{
+public:
+ static HRESULT Create(IInterceptorSink** aOutput);
+
+ template <typename Interface>
+ static HRESULT WrapInterface(STAUniquePtr<Interface> aTargetInterface,
+ Interface** aOutInterface)
+ {
+ MOZ_ASSERT(!IsProxy(aTargetInterface.get()));
+ RefPtr<IInterceptorSink> handoff;
+ HRESULT hr = MainThreadHandoff::Create(getter_AddRefs(handoff));
+ if (FAILED(hr)) {
+ return hr;
+ }
+ return CreateInterceptor(Move(aTargetInterface), handoff, aOutInterface);
+ }
+
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
+ STDMETHODIMP_(ULONG) AddRef() override;
+ STDMETHODIMP_(ULONG) Release() override;
+
+ // ICallFrameEvents
+ STDMETHODIMP OnCall(ICallFrame* aFrame) override;
+
+ // IInterceptorSink
+ STDMETHODIMP SetInterceptor(IWeakReference* aInterceptor) override;
+
+ // ICallFrameWalker
+ STDMETHODIMP OnWalkInterface(REFIID aIid, PVOID* aInterface, BOOL aIsInParam,
+ BOOL aIsOutParam) override;
+
+private:
+ MainThreadHandoff();
+ ~MainThreadHandoff();
+ HRESULT FixArrayElements(ICallFrame* aFrame,
+ const ArrayData& aArrayData);
+
+private:
+ ULONG mRefCnt;
+ RefPtr<IWeakReference> mInterceptor;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_MainThreadHandoff_h
diff --git a/ipc/mscom/MainThreadInvoker.cpp b/ipc/mscom/MainThreadInvoker.cpp
new file mode 100644
index 000000000..cab62a727
--- /dev/null
+++ b/ipc/mscom/MainThreadInvoker.cpp
@@ -0,0 +1,180 @@
+/* -*- 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/MainThreadInvoker.h"
+
+#include "GeckoProfiler.h"
+#include "MainThreadUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/HangMonitor.h"
+#include "mozilla/RefPtr.h"
+#include "nsServiceManagerUtils.h"
+#include "nsSystemInfo.h"
+#include "private/prpriv.h" // For PR_GetThreadID
+#include "WinUtils.h"
+
+// This gives us compiler intrinsics for the x86 PAUSE instruction
+#if defined(_MSC_VER)
+#include <intrin.h>
+#pragma intrinsic(_mm_pause)
+#define CPU_PAUSE() _mm_pause()
+#elif defined(__GNUC__) || defined(__clang__)
+#define CPU_PAUSE() __builtin_ia32_pause()
+#endif
+
+static bool sIsMulticore;
+
+namespace {
+
+/**
+ * SyncRunnable implements different code paths depending on whether or not
+ * we are running on a multiprocessor system. In the multiprocessor case, we
+ * leave the thread in a spin loop while waiting for the main thread to execute
+ * our runnable. Since spinning is pointless in the uniprocessor case, we block
+ * on an event that is set by the main thread once it has finished the runnable.
+ */
+class MOZ_RAII SyncRunnable
+{
+public:
+ explicit SyncRunnable(already_AddRefed<nsIRunnable>&& aRunnable)
+ : mDoneEvent(sIsMulticore ? nullptr :
+ ::CreateEventW(nullptr, FALSE, FALSE, nullptr))
+ , mDone(false)
+ , mRunnable(aRunnable)
+ {
+ MOZ_ASSERT(sIsMulticore || mDoneEvent);
+ MOZ_ASSERT(mRunnable);
+ }
+
+ ~SyncRunnable()
+ {
+ if (mDoneEvent) {
+ ::CloseHandle(mDoneEvent);
+ }
+ }
+
+ void Run()
+ {
+ mRunnable->Run();
+
+ if (mDoneEvent) {
+ ::SetEvent(mDoneEvent);
+ } else {
+ mDone = true;
+ }
+ }
+
+ bool WaitUntilComplete()
+ {
+ if (mDoneEvent) {
+ HANDLE handles[] = {mDoneEvent,
+ mozilla::mscom::MainThreadInvoker::GetTargetThread()};
+ DWORD waitResult = ::WaitForMultipleObjects(mozilla::ArrayLength(handles),
+ handles, FALSE, INFINITE);
+ return waitResult == WAIT_OBJECT_0;
+ }
+
+ while (!mDone) {
+ // The PAUSE instruction is a hint to the CPU that we're doing a spin
+ // loop. It is a no-op on older processors that don't support it, so
+ // it is safe to use here without any CPUID checks.
+ CPU_PAUSE();
+ }
+ return true;
+ }
+
+private:
+ HANDLE mDoneEvent;
+ mozilla::Atomic<bool> mDone;
+ nsCOMPtr<nsIRunnable> mRunnable;
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace mscom {
+
+HANDLE MainThreadInvoker::sMainThread = nullptr;
+
+/* static */ bool
+MainThreadInvoker::InitStatics()
+{
+ nsCOMPtr<nsIThread> mainThread;
+ nsresult rv = ::NS_GetMainThread(getter_AddRefs(mainThread));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ PRThread* mainPrThread = nullptr;
+ rv = mainThread->GetPRThread(&mainPrThread);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ PRUint32 tid = ::PR_GetThreadID(mainPrThread);
+ sMainThread = ::OpenThread(SYNCHRONIZE | THREAD_SET_CONTEXT, FALSE, tid);
+
+ nsCOMPtr<nsIPropertyBag2> infoService = do_GetService(NS_SYSTEMINFO_CONTRACTID);
+ if (infoService) {
+ uint32_t cpuCount;
+ nsresult rv = infoService->GetPropertyAsUint32(NS_LITERAL_STRING("cpucount"),
+ &cpuCount);
+ sIsMulticore = NS_SUCCEEDED(rv) && cpuCount > 1;
+ }
+
+ return !!sMainThread;
+}
+
+MainThreadInvoker::MainThreadInvoker()
+{
+ static const bool gotStatics = InitStatics();
+ MOZ_ASSERT(gotStatics);
+}
+
+bool
+MainThreadInvoker::Invoke(already_AddRefed<nsIRunnable>&& aRunnable)
+{
+ nsCOMPtr<nsIRunnable> runnable(Move(aRunnable));
+ if (!runnable) {
+ return false;
+ }
+
+ if (NS_IsMainThread()) {
+ runnable->Run();
+ return true;
+ }
+
+ SyncRunnable syncRunnable(runnable.forget());
+
+ if (!::QueueUserAPC(&MainThreadAPC, sMainThread,
+ reinterpret_cast<UINT_PTR>(&syncRunnable))) {
+ return false;
+ }
+
+ // We should ensure a call to NtTestAlert() is made on the main thread so
+ // that the main thread will check for APCs during event processing. If we
+ // omit this then the main thread will not check its APC queue until it is
+ // idle.
+ widget::WinUtils::SetAPCPending();
+
+ return syncRunnable.WaitUntilComplete();
+}
+
+/* static */ VOID CALLBACK
+MainThreadInvoker::MainThreadAPC(ULONG_PTR aParam)
+{
+ GeckoProfilerWakeRAII wakeProfiler;
+ mozilla::HangMonitor::NotifyActivity(mozilla::HangMonitor::kGeneralActivity);
+ MOZ_ASSERT(NS_IsMainThread());
+ auto runnable = reinterpret_cast<SyncRunnable*>(aParam);
+ runnable->Run();
+}
+
+} // namespace mscom
+} // namespace mozilla
diff --git a/ipc/mscom/MainThreadInvoker.h b/ipc/mscom/MainThreadInvoker.h
new file mode 100644
index 000000000..4bd1f803f
--- /dev/null
+++ b/ipc/mscom/MainThreadInvoker.h
@@ -0,0 +1,38 @@
+/* -*- 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_MainThreadInvoker_h
+#define mozilla_mscom_MainThreadInvoker_h
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/StaticPtr.h"
+
+#include <windows.h>
+
+class nsIRunnable;
+
+namespace mozilla {
+namespace mscom {
+
+class MainThreadInvoker
+{
+public:
+ MainThreadInvoker();
+
+ bool Invoke(already_AddRefed<nsIRunnable>&& aRunnable);
+ static HANDLE GetTargetThread() { return sMainThread; }
+
+private:
+ static bool InitStatics();
+ static VOID CALLBACK MainThreadAPC(ULONG_PTR aParam);
+
+ static HANDLE sMainThread;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_MainThreadInvoker_h
diff --git a/ipc/mscom/MainThreadRuntime.cpp b/ipc/mscom/MainThreadRuntime.cpp
new file mode 100644
index 000000000..d86317966
--- /dev/null
+++ b/ipc/mscom/MainThreadRuntime.cpp
@@ -0,0 +1,180 @@
+/* -*- 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/MainThreadRuntime.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WindowsVersion.h"
+#include "nsDebug.h"
+#include "nsWindowsHelpers.h"
+
+#include <accctrl.h>
+#include <aclapi.h>
+#include <objbase.h>
+#include <objidl.h>
+
+namespace {
+
+struct LocalFreeDeleter
+{
+ void operator()(void* aPtr)
+ {
+ ::LocalFree(aPtr);
+ }
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace mscom {
+
+MainThreadRuntime::MainThreadRuntime()
+ : mInitResult(E_UNEXPECTED)
+{
+ // We must be the outermost COM initialization on this thread. The COM runtime
+ // cannot be configured once we start manipulating objects
+ MOZ_ASSERT(mStaRegion.IsValidOutermost());
+ if (NS_WARN_IF(!mStaRegion.IsValidOutermost())) {
+ return;
+ }
+
+ // Windows XP doesn't support setting of the COM exception policy, so we'll
+ // just stop here in that case.
+ if (!IsVistaOrLater()) {
+ mInitResult = S_OK;
+ return;
+ }
+
+ // We are required to initialize security in order to configure global options.
+ mInitResult = InitializeSecurity();
+ MOZ_ASSERT(SUCCEEDED(mInitResult));
+ if (FAILED(mInitResult)) {
+ return;
+ }
+
+ RefPtr<IGlobalOptions> globalOpts;
+ mInitResult = ::CoCreateInstance(CLSID_GlobalOptions, nullptr,
+ CLSCTX_INPROC_SERVER, IID_IGlobalOptions,
+ (void**)getter_AddRefs(globalOpts));
+ MOZ_ASSERT(SUCCEEDED(mInitResult));
+ if (FAILED(mInitResult)) {
+ return;
+ }
+
+ // Windows 7 has a policy that is even more strict. We should use that one
+ // whenever possible.
+ ULONG_PTR exceptionSetting = IsWin7OrLater() ?
+ COMGLB_EXCEPTION_DONOT_HANDLE_ANY :
+ COMGLB_EXCEPTION_DONOT_HANDLE;
+ mInitResult = globalOpts->Set(COMGLB_EXCEPTION_HANDLING, exceptionSetting);
+ MOZ_ASSERT(SUCCEEDED(mInitResult));
+}
+
+HRESULT
+MainThreadRuntime::InitializeSecurity()
+{
+ HANDLE rawToken = nullptr;
+ BOOL ok = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken);
+ if (!ok) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+ nsAutoHandle token(rawToken);
+
+ DWORD len = 0;
+ ok = ::GetTokenInformation(token, TokenUser, nullptr, len, &len);
+ DWORD win32Error = ::GetLastError();
+ if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
+ return HRESULT_FROM_WIN32(win32Error);
+ }
+
+ auto tokenUserBuf = MakeUnique<BYTE[]>(len);
+ TOKEN_USER& tokenUser = *reinterpret_cast<TOKEN_USER*>(tokenUserBuf.get());
+ ok = ::GetTokenInformation(token, TokenUser, tokenUserBuf.get(), len, &len);
+ if (!ok) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ len = 0;
+ ok = ::GetTokenInformation(token, TokenPrimaryGroup, nullptr, len, &len);
+ win32Error = ::GetLastError();
+ if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
+ return HRESULT_FROM_WIN32(win32Error);
+ }
+
+ auto tokenPrimaryGroupBuf = MakeUnique<BYTE[]>(len);
+ TOKEN_PRIMARY_GROUP& tokenPrimaryGroup =
+ *reinterpret_cast<TOKEN_PRIMARY_GROUP*>(tokenPrimaryGroupBuf.get());
+ ok = ::GetTokenInformation(token, TokenPrimaryGroup, tokenPrimaryGroupBuf.get(),
+ len, &len);
+ if (!ok) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ SECURITY_DESCRIPTOR sd;
+ if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ BYTE systemSid[SECURITY_MAX_SID_SIZE];
+ DWORD systemSidSize = sizeof(systemSid);
+ if (!::CreateWellKnownSid(WinLocalSystemSid, nullptr, systemSid,
+ &systemSidSize)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ BYTE adminSid[SECURITY_MAX_SID_SIZE];
+ DWORD adminSidSize = sizeof(adminSid);
+ if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, nullptr, adminSid,
+ &adminSidSize)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ // Grant access to SYSTEM, Administrators, and the user.
+ EXPLICIT_ACCESS entries[] = {
+ {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+ {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
+ reinterpret_cast<LPWSTR>(systemSid)}},
+ {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+ {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP,
+ reinterpret_cast<LPWSTR>(adminSid)}},
+ {COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
+ {nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
+ reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}}
+ };
+
+ PACL rawDacl = nullptr;
+ win32Error = ::SetEntriesInAcl(ArrayLength(entries), entries, nullptr,
+ &rawDacl);
+ if (win32Error != ERROR_SUCCESS) {
+ return HRESULT_FROM_WIN32(win32Error);
+ }
+
+ UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl);
+
+ if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ if (!::SetSecurityDescriptorOwner(&sd, tokenUser.User.Sid, FALSE)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ if (!::SetSecurityDescriptorGroup(&sd, tokenPrimaryGroup.PrimaryGroup, FALSE)) {
+ return HRESULT_FROM_WIN32(::GetLastError());
+ }
+
+ return ::CoInitializeSecurity(&sd, -1, nullptr, nullptr,
+ RPC_C_AUTHN_LEVEL_DEFAULT,
+ RPC_C_IMP_LEVEL_IDENTIFY, nullptr, EOAC_NONE,
+ nullptr);
+}
+
+} // namespace mscom
+} // namespace mozilla
+
diff --git a/ipc/mscom/MainThreadRuntime.h b/ipc/mscom/MainThreadRuntime.h
new file mode 100644
index 000000000..0e3e52c5f
--- /dev/null
+++ b/ipc/mscom/MainThreadRuntime.h
@@ -0,0 +1,42 @@
+/* -*- 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_MainThreadRuntime_h
+#define mozilla_mscom_MainThreadRuntime_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/mscom/COMApartmentRegion.h"
+
+namespace mozilla {
+namespace mscom {
+
+class MOZ_NON_TEMPORARY_CLASS MainThreadRuntime
+{
+public:
+ MainThreadRuntime();
+
+ explicit operator bool() const
+ {
+ return mStaRegion.IsValidOutermost() && SUCCEEDED(mInitResult);
+ }
+
+ MainThreadRuntime(MainThreadRuntime&) = delete;
+ MainThreadRuntime(MainThreadRuntime&&) = delete;
+ MainThreadRuntime& operator=(MainThreadRuntime&) = delete;
+ MainThreadRuntime& operator=(MainThreadRuntime&&) = delete;
+
+private:
+ HRESULT InitializeSecurity();
+
+ STARegion mStaRegion;
+ HRESULT mInitResult;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_MainThreadRuntime_h
+
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
+
diff --git a/ipc/mscom/ProxyStream.h b/ipc/mscom/ProxyStream.h
new file mode 100644
index 000000000..392fdd2d8
--- /dev/null
+++ b/ipc/mscom/ProxyStream.h
@@ -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
diff --git a/ipc/mscom/Ptr.h b/ipc/mscom/Ptr.h
new file mode 100644
index 000000000..c77e570b8
--- /dev/null
+++ b/ipc/mscom/Ptr.h
@@ -0,0 +1,111 @@
+/* -*- 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_Ptr_h
+#define mozilla_mscom_Ptr_h
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/mscom/EnsureMTA.h"
+#include "mozilla/UniquePtr.h"
+#include "nsError.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
+/**
+ * The glue code in mozilla::mscom often needs to pass around interface pointers
+ * belonging to a different apartment from the current one. We must not touch
+ * the reference counts of those objects on the wrong apartment. By using these
+ * UniquePtr specializations, we may ensure that the reference counts are always
+ * handled correctly.
+ */
+
+namespace mozilla {
+namespace mscom {
+
+namespace detail {
+
+template <typename T>
+struct MainThreadRelease
+{
+ void operator()(T* aPtr)
+ {
+ if (!aPtr) {
+ return;
+ }
+ if (NS_IsMainThread()) {
+ aPtr->Release();
+ return;
+ }
+ DebugOnly<nsresult> rv =
+ NS_DispatchToMainThread(NewNonOwningRunnableMethod(aPtr,
+ &T::Release));
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+};
+
+template <typename T>
+struct MTARelease
+{
+ void operator()(T* aPtr)
+ {
+ if (!aPtr) {
+ return;
+ }
+ EnsureMTA([&]() -> void
+ {
+ aPtr->Release();
+ });
+ }
+};
+
+template <typename T>
+struct MTAReleaseInChildProcess
+{
+ void operator()(T* aPtr)
+ {
+ if (!aPtr) {
+ return;
+ }
+ if (XRE_IsParentProcess()) {
+ MOZ_ASSERT(NS_IsMainThread());
+ aPtr->Release();
+ return;
+ }
+ EnsureMTA([&]() -> void
+ {
+ aPtr->Release();
+ });
+ }
+};
+
+struct InterceptorTargetDeleter
+{
+ void operator()(IUnknown* aPtr)
+ {
+ // We intentionally do not touch the refcounts of interceptor targets!
+ }
+};
+
+} // namespace detail
+
+template <typename T>
+using STAUniquePtr = mozilla::UniquePtr<T, detail::MainThreadRelease<T>>;
+
+template <typename T>
+using MTAUniquePtr = mozilla::UniquePtr<T, detail::MTARelease<T>>;
+
+template <typename T>
+using ProxyUniquePtr = mozilla::UniquePtr<T, detail::MTAReleaseInChildProcess<T>>;
+
+using InterceptorTargetPtr =
+ mozilla::UniquePtr<IUnknown, detail::InterceptorTargetDeleter>;
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_Ptr_h
+
diff --git a/ipc/mscom/Registration.cpp b/ipc/mscom/Registration.cpp
new file mode 100644
index 000000000..811989272
--- /dev/null
+++ b/ipc/mscom/Registration.cpp
@@ -0,0 +1,346 @@
+/* -*- 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/. */
+
+// COM registration data structures are built with C code, so we need to
+// simulate that in our C++ code by defining CINTERFACE before including
+// anything else that could possibly pull in Windows header files.
+#define CINTERFACE
+
+#include "mozilla/mscom/ActivationContext.h"
+#include "mozilla/mscom/EnsureMTA.h"
+#include "mozilla/mscom/Registration.h"
+#include "mozilla/mscom/Utils.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Move.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Pair.h"
+#include "mozilla/StaticPtr.h"
+#include "nsTArray.h"
+#include "nsWindowsHelpers.h"
+
+#include <oaidl.h>
+#include <objidl.h>
+#include <rpcproxy.h>
+#include <shlwapi.h>
+
+/* This code MUST NOT use any non-inlined internal Mozilla APIs, as it will be
+ compiled into DLLs that COM may load into non-Mozilla processes! */
+
+namespace {
+
+// This function is defined in generated code for proxy DLLs but is not declared
+// in rpcproxy.h, so we need this typedef.
+typedef void (RPC_ENTRY *GetProxyDllInfoFnPtr)(const ProxyFileInfo*** aInfo,
+ const CLSID** aId);
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace mscom {
+
+static bool
+BuildLibPath(RegistrationFlags aFlags, wchar_t* aBuffer, size_t aBufferLen,
+ const wchar_t* aLeafName)
+{
+ if (aFlags == RegistrationFlags::eUseBinDirectory) {
+ HMODULE thisModule = nullptr;
+ if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<LPCTSTR>(&RegisterProxy),
+ &thisModule)) {
+ return false;
+ }
+ DWORD fileNameResult = GetModuleFileName(thisModule, aBuffer, aBufferLen);
+ if (!fileNameResult || (fileNameResult == aBufferLen &&
+ ::GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
+ return false;
+ }
+ if (!PathRemoveFileSpec(aBuffer)) {
+ return false;
+ }
+ } else if (aFlags == RegistrationFlags::eUseSystemDirectory) {
+ UINT result = GetSystemDirectoryW(aBuffer, static_cast<UINT>(aBufferLen));
+ if (!result || result > aBufferLen) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ if (!PathAppend(aBuffer, aLeafName)) {
+ return false;
+ }
+ return true;
+}
+
+UniquePtr<RegisteredProxy>
+RegisterProxy(const wchar_t* aLeafName, RegistrationFlags aFlags)
+{
+ wchar_t modulePathBuf[MAX_PATH + 1] = {0};
+ if (!BuildLibPath(aFlags, modulePathBuf, ArrayLength(modulePathBuf),
+ aLeafName)) {
+ return nullptr;
+ }
+
+ nsModuleHandle proxyDll(LoadLibrary(modulePathBuf));
+ if (!proxyDll.get()) {
+ return nullptr;
+ }
+
+ // Instantiate an activation context so that CoGetClassObject will use any
+ // COM metadata embedded in proxyDll's manifest to resolve CLSIDs.
+ ActivationContext actCtx(proxyDll);
+ if (!actCtx) {
+ return nullptr;
+ }
+
+ auto GetProxyDllInfoFn = reinterpret_cast<GetProxyDllInfoFnPtr>(
+ GetProcAddress(proxyDll, "GetProxyDllInfo"));
+ if (!GetProxyDllInfoFn) {
+ return nullptr;
+ }
+
+ const ProxyFileInfo** proxyInfo = nullptr;
+ const CLSID* proxyClsid = nullptr;
+ GetProxyDllInfoFn(&proxyInfo, &proxyClsid);
+ if (!proxyInfo || !proxyClsid) {
+ return nullptr;
+ }
+
+ // We call CoGetClassObject instead of DllGetClassObject because it forces
+ // the COM runtime to manage the lifetime of the DLL.
+ IUnknown* classObject = nullptr;
+ HRESULT hr = CoGetClassObject(*proxyClsid, CLSCTX_INPROC_SERVER, nullptr,
+ IID_IUnknown, (void**) &classObject);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ DWORD regCookie;
+ hr = CoRegisterClassObject(*proxyClsid, classObject, CLSCTX_INPROC_SERVER,
+ REGCLS_MULTIPLEUSE, &regCookie);
+ if (FAILED(hr)) {
+ classObject->lpVtbl->Release(classObject);
+ return nullptr;
+ }
+
+ ITypeLib* typeLib = nullptr;
+ hr = LoadTypeLibEx(modulePathBuf, REGKIND_NONE, &typeLib);
+ MOZ_ASSERT(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ CoRevokeClassObject(regCookie);
+ classObject->lpVtbl->Release(classObject);
+ return nullptr;
+ }
+
+ // RegisteredProxy takes ownership of proxyDll, classObject, and typeLib
+ // references
+ auto result(MakeUnique<RegisteredProxy>(reinterpret_cast<uintptr_t>(proxyDll.disown()),
+ classObject, regCookie, typeLib));
+
+ while (*proxyInfo) {
+ const ProxyFileInfo& curInfo = **proxyInfo;
+ for (unsigned short i = 0, e = curInfo.TableSize; i < e; ++i) {
+ hr = CoRegisterPSClsid(*(curInfo.pStubVtblList[i]->header.piid),
+ *proxyClsid);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+ }
+ ++proxyInfo;
+ }
+
+ return result;
+}
+
+UniquePtr<RegisteredProxy>
+RegisterTypelib(const wchar_t* aLeafName, RegistrationFlags aFlags)
+{
+ wchar_t modulePathBuf[MAX_PATH + 1] = {0};
+ if (!BuildLibPath(aFlags, modulePathBuf, ArrayLength(modulePathBuf),
+ aLeafName)) {
+ return nullptr;
+ }
+
+ ITypeLib* typeLib = nullptr;
+ HRESULT hr = LoadTypeLibEx(modulePathBuf, REGKIND_NONE, &typeLib);
+ if (FAILED(hr)) {
+ return nullptr;
+ }
+
+ // RegisteredProxy takes ownership of typeLib reference
+ auto result(MakeUnique<RegisteredProxy>(typeLib));
+ return result;
+}
+
+RegisteredProxy::RegisteredProxy(uintptr_t aModule, IUnknown* aClassObject,
+ uint32_t aRegCookie, ITypeLib* aTypeLib)
+ : mModule(aModule)
+ , mClassObject(aClassObject)
+ , mRegCookie(aRegCookie)
+ , mTypeLib(aTypeLib)
+ , mIsRegisteredInMTA(IsCurrentThreadMTA())
+{
+ MOZ_ASSERT(aClassObject);
+ MOZ_ASSERT(aTypeLib);
+ AddToRegistry(this);
+}
+
+// If we're initializing from a typelib, it doesn't matter which apartment we
+// run in, so mIsRegisteredInMTA may always be set to false in this case.
+RegisteredProxy::RegisteredProxy(ITypeLib* aTypeLib)
+ : mModule(0)
+ , mClassObject(nullptr)
+ , mRegCookie(0)
+ , mTypeLib(aTypeLib)
+ , mIsRegisteredInMTA(false)
+{
+ MOZ_ASSERT(aTypeLib);
+ AddToRegistry(this);
+}
+
+RegisteredProxy::~RegisteredProxy()
+{
+ DeleteFromRegistry(this);
+ if (mTypeLib) {
+ mTypeLib->lpVtbl->Release(mTypeLib);
+ }
+ if (mClassObject) {
+ // NB: mClassObject and mRegCookie must be freed from inside the apartment
+ // which they were created in.
+ auto cleanupFn = [&]() -> void {
+ ::CoRevokeClassObject(mRegCookie);
+ mClassObject->lpVtbl->Release(mClassObject);
+ };
+ if (mIsRegisteredInMTA) {
+ EnsureMTA mta(cleanupFn);
+ } else {
+ cleanupFn();
+ }
+ }
+ if (mModule) {
+ ::FreeLibrary(reinterpret_cast<HMODULE>(mModule));
+ }
+}
+
+RegisteredProxy::RegisteredProxy(RegisteredProxy&& aOther)
+{
+ *this = mozilla::Forward<RegisteredProxy>(aOther);
+}
+
+RegisteredProxy&
+RegisteredProxy::operator=(RegisteredProxy&& aOther)
+{
+ mModule = aOther.mModule;
+ aOther.mModule = 0;
+ mClassObject = aOther.mClassObject;
+ aOther.mClassObject = nullptr;
+ mRegCookie = aOther.mRegCookie;
+ aOther.mRegCookie = 0;
+ mTypeLib = aOther.mTypeLib;
+ aOther.mTypeLib = nullptr;
+ return *this;
+}
+
+HRESULT
+RegisteredProxy::GetTypeInfoForInterface(REFIID aIid,
+ ITypeInfo** aOutTypeInfo) const
+{
+ if (!aOutTypeInfo) {
+ return E_INVALIDARG;
+ }
+ if (!mTypeLib) {
+ return E_UNEXPECTED;
+ }
+ return mTypeLib->lpVtbl->GetTypeInfoOfGuid(mTypeLib, aIid, aOutTypeInfo);
+}
+
+static StaticAutoPtr<nsTArray<RegisteredProxy*>> sRegistry;
+static StaticAutoPtr<Mutex> sRegMutex;
+static StaticAutoPtr<nsTArray<Pair<const ArrayData*, size_t>>> sArrayData;
+
+static Mutex&
+GetMutex()
+{
+ static Mutex& mutex = []() -> Mutex& {
+ if (!sRegMutex) {
+ sRegMutex = new Mutex("RegisteredProxy::sRegMutex");
+ ClearOnShutdown(&sRegMutex, ShutdownPhase::ShutdownThreads);
+ }
+ return *sRegMutex;
+ }();
+ return mutex;
+}
+
+/* static */ bool
+RegisteredProxy::Find(REFIID aIid, ITypeInfo** aTypeInfo)
+{
+ MutexAutoLock lock(GetMutex());
+ nsTArray<RegisteredProxy*>& registry = *sRegistry;
+ for (uint32_t idx = 0, len = registry.Length(); idx < len; ++idx) {
+ if (SUCCEEDED(registry[idx]->GetTypeInfoForInterface(aIid, aTypeInfo))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* static */ void
+RegisteredProxy::AddToRegistry(RegisteredProxy* aProxy)
+{
+ MutexAutoLock lock(GetMutex());
+ if (!sRegistry) {
+ sRegistry = new nsTArray<RegisteredProxy*>();
+ ClearOnShutdown(&sRegistry);
+ }
+ sRegistry->AppendElement(aProxy);
+}
+
+/* static */ void
+RegisteredProxy::DeleteFromRegistry(RegisteredProxy* aProxy)
+{
+ MutexAutoLock lock(GetMutex());
+ sRegistry->RemoveElement(aProxy);
+}
+
+void
+RegisterArrayData(const ArrayData* aArrayData, size_t aLength)
+{
+ MutexAutoLock lock(GetMutex());
+ if (!sArrayData) {
+ sArrayData = new nsTArray<Pair<const ArrayData*, size_t>>();
+ ClearOnShutdown(&sArrayData, ShutdownPhase::ShutdownThreads);
+ }
+ sArrayData->AppendElement(MakePair(aArrayData, aLength));
+}
+
+const ArrayData*
+FindArrayData(REFIID aIid, ULONG aMethodIndex)
+{
+ MutexAutoLock lock(GetMutex());
+ if (!sArrayData) {
+ return nullptr;
+ }
+ for (uint32_t outerIdx = 0, outerLen = sArrayData->Length();
+ outerIdx < outerLen; ++outerIdx) {
+ auto& data = sArrayData->ElementAt(outerIdx);
+ for (size_t innerIdx = 0, innerLen = data.second(); innerIdx < innerLen;
+ ++innerIdx) {
+ const ArrayData* array = data.first();
+ if (aIid == array[innerIdx].mIid &&
+ aMethodIndex == array[innerIdx].mMethodIndex) {
+ return &array[innerIdx];
+ }
+ }
+ }
+ return nullptr;
+}
+
+} // namespace mscom
+} // namespace mozilla
diff --git a/ipc/mscom/Registration.h b/ipc/mscom/Registration.h
new file mode 100644
index 000000000..930225ca7
--- /dev/null
+++ b/ipc/mscom/Registration.h
@@ -0,0 +1,145 @@
+/* -*- 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_Registration_h
+#define mozilla_mscom_Registration_h
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+
+#include <objbase.h>
+
+struct ITypeInfo;
+struct ITypeLib;
+
+namespace mozilla {
+namespace mscom {
+
+/**
+ * Assumptions:
+ * (1) The DLL exports GetProxyDllInfo. This is not exported by default; it must
+ * be specified in the EXPORTS section of the DLL's module definition file.
+ */
+class RegisteredProxy
+{
+public:
+ RegisteredProxy(uintptr_t aModule, IUnknown* aClassObject,
+ uint32_t aRegCookie, ITypeLib* aTypeLib);
+ explicit RegisteredProxy(ITypeLib* aTypeLib);
+ RegisteredProxy(RegisteredProxy&& aOther);
+ RegisteredProxy& operator=(RegisteredProxy&& aOther);
+
+ ~RegisteredProxy();
+
+ HRESULT GetTypeInfoForInterface(REFIID aIid, ITypeInfo** aOutTypeInfo) const;
+
+ static bool Find(REFIID aIid, ITypeInfo** aOutTypeInfo);
+
+private:
+ RegisteredProxy() = delete;
+ RegisteredProxy(RegisteredProxy&) = delete;
+ RegisteredProxy& operator=(RegisteredProxy&) = delete;
+
+ static void AddToRegistry(RegisteredProxy* aProxy);
+ static void DeleteFromRegistry(RegisteredProxy* aProxy);
+
+private:
+ // Not using Windows types here: We shouldn't #include windows.h
+ // since it might pull in COM code which we want to do very carefully in
+ // Registration.cpp.
+ uintptr_t mModule;
+ IUnknown* mClassObject;
+ uint32_t mRegCookie;
+ ITypeLib* mTypeLib;
+ bool mIsRegisteredInMTA;
+};
+
+enum class RegistrationFlags
+{
+ eUseBinDirectory,
+ eUseSystemDirectory
+};
+
+// For DLL files. Assumes corresponding TLB is embedded in resources.
+UniquePtr<RegisteredProxy> RegisterProxy(const wchar_t* aLeafName,
+ RegistrationFlags aFlags =
+ RegistrationFlags::eUseBinDirectory);
+// For standalone TLB files.
+UniquePtr<RegisteredProxy> RegisterTypelib(const wchar_t* aLeafName,
+ RegistrationFlags aFlags =
+ RegistrationFlags::eUseBinDirectory);
+
+/**
+ * The COM interceptor uses type library information to build its interface
+ * proxies. Unfortunately type libraries do not encode size_is and length_is
+ * annotations that have been specified in IDL. This structure allows us to
+ * explicitly declare such relationships so that the COM interceptor may
+ * be made aware of them.
+ */
+struct ArrayData
+{
+ enum class Flag
+ {
+ eNone = 0,
+ eAllocatedByServer = 1 // This implies an extra level of indirection
+ };
+
+ ArrayData(REFIID aIid, ULONG aMethodIndex, ULONG aArrayParamIndex,
+ VARTYPE aArrayParamType, REFIID aArrayParamIid,
+ ULONG aLengthParamIndex, Flag aFlag = Flag::eNone)
+ : mIid(aIid)
+ , mMethodIndex(aMethodIndex)
+ , mArrayParamIndex(aArrayParamIndex)
+ , mArrayParamType(aArrayParamType)
+ , mArrayParamIid(aArrayParamIid)
+ , mLengthParamIndex(aLengthParamIndex)
+ , mFlag(aFlag)
+ {
+ }
+
+ ArrayData(const ArrayData& aOther)
+ {
+ *this = aOther;
+ }
+
+ ArrayData& operator=(const ArrayData& aOther)
+ {
+ mIid = aOther.mIid;
+ mMethodIndex = aOther.mMethodIndex;
+ mArrayParamIndex = aOther.mArrayParamIndex;
+ mArrayParamType = aOther.mArrayParamType;
+ mArrayParamIid = aOther.mArrayParamIid;
+ mLengthParamIndex = aOther.mLengthParamIndex;
+ mFlag = aOther.mFlag;
+ return *this;
+ }
+
+ IID mIid;
+ ULONG mMethodIndex;
+ ULONG mArrayParamIndex;
+ VARTYPE mArrayParamType;
+ IID mArrayParamIid;
+ ULONG mLengthParamIndex;
+ Flag mFlag;
+};
+
+void RegisterArrayData(const ArrayData* aArrayData, size_t aLength);
+
+template <size_t N>
+inline void
+RegisterArrayData(const ArrayData (&aData)[N])
+{
+ RegisterArrayData(aData, N);
+}
+
+const ArrayData*
+FindArrayData(REFIID aIid, ULONG aMethodIndex);
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_Registration_h
+
diff --git a/ipc/mscom/Utils.cpp b/ipc/mscom/Utils.cpp
new file mode 100644
index 000000000..7c031bb52
--- /dev/null
+++ b/ipc/mscom/Utils.cpp
@@ -0,0 +1,94 @@
+/* -*- 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/. */
+
+// We need Windows 7 headers
+#ifdef NTDDI_VERSION
+#undef NTDDI_VERSION
+#endif
+#define NTDDI_VERSION 0x06010000
+#ifdef _WIN32_WINNT
+#undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0601
+
+#include "DynamicallyLinkedFunctionPtr.h"
+#include "mozilla/mscom/Utils.h"
+#include "mozilla/RefPtr.h"
+
+#include <objidl.h>
+
+static bool
+IsCurrentThreadMTALegacy()
+{
+ // We don't use RefPtr for token because CoGetContextToken does *not*
+ // increment its refcount!
+ IUnknown* token = nullptr;
+ HRESULT hr =
+ CoGetContextToken(reinterpret_cast<ULONG_PTR*>(&token));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ RefPtr<IComThreadingInfo> threadingInfo;
+ hr = token->QueryInterface(IID_IComThreadingInfo,
+ getter_AddRefs(threadingInfo));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ APTTYPE aptType;
+ hr = threadingInfo->GetCurrentApartmentType(&aptType);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ return aptType == APTTYPE_MTA;
+}
+
+namespace mozilla {
+namespace mscom {
+
+bool
+IsCurrentThreadMTA()
+{
+ static DynamicallyLinkedFunctionPtr<decltype(&::CoGetApartmentType)>
+ pCoGetApartmentType(L"ole32.dll", "CoGetApartmentType");
+
+ if (!pCoGetApartmentType) {
+ // XP and Vista do not expose the newer API.
+ return IsCurrentThreadMTALegacy();
+ }
+
+ APTTYPE aptType;
+ APTTYPEQUALIFIER aptTypeQualifier;
+ HRESULT hr = pCoGetApartmentType(&aptType, &aptTypeQualifier);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ return aptType == APTTYPE_MTA;
+}
+
+bool
+IsProxy(IUnknown* aUnknown)
+{
+ if (!aUnknown) {
+ return false;
+ }
+
+ // Only proxies implement this interface, so if it is present then we must
+ // be dealing with a proxied object.
+ RefPtr<IClientSecurity> clientSecurity;
+ HRESULT hr = aUnknown->QueryInterface(IID_IClientSecurity,
+ (void**)getter_AddRefs(clientSecurity));
+ if (SUCCEEDED(hr) || hr == RPC_E_WRONG_THREAD) {
+ return true;
+ }
+ return false;
+}
+
+} // namespace mscom
+} // namespace mozilla
diff --git a/ipc/mscom/Utils.h b/ipc/mscom/Utils.h
new file mode 100644
index 000000000..eb1a37f3a
--- /dev/null
+++ b/ipc/mscom/Utils.h
@@ -0,0 +1,22 @@
+/* -*- 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_Utils_h
+#define mozilla_mscom_Utils_h
+
+struct IUnknown;
+
+namespace mozilla {
+namespace mscom {
+
+bool IsCurrentThreadMTA();
+bool IsProxy(IUnknown* aUnknown);
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_Utils_h
+
diff --git a/ipc/mscom/WeakRef.cpp b/ipc/mscom/WeakRef.cpp
new file mode 100644
index 000000000..f19310264
--- /dev/null
+++ b/ipc/mscom/WeakRef.cpp
@@ -0,0 +1,191 @@
+/* -*- 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/. */
+
+#define INITGUID
+#include "mozilla/mscom/WeakRef.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/RefPtr.h"
+#include "nsThreadUtils.h"
+#include "nsWindowsHelpers.h"
+#include "nsProxyRelease.h"
+
+namespace mozilla {
+namespace mscom {
+
+WeakReferenceSupport::WeakReferenceSupport(Flags aFlags)
+ : mRefCnt(1)
+ , mFlags(aFlags)
+{
+ ::InitializeCriticalSectionAndSpinCount(&mCS, 4000);
+}
+
+WeakReferenceSupport::~WeakReferenceSupport()
+{
+ MOZ_ASSERT(mWeakRefs.IsEmpty());
+ ::DeleteCriticalSection(&mCS);
+}
+
+HRESULT
+WeakReferenceSupport::QueryInterface(REFIID riid, void** ppv)
+{
+ AutoCriticalSection lock(&mCS);
+ RefPtr<IUnknown> punk;
+ if (!ppv) {
+ return E_INVALIDARG;
+ }
+ *ppv = nullptr;
+
+ // Raise the refcount for stabilization purposes during aggregation
+ RefPtr<IUnknown> kungFuDeathGrip(static_cast<IUnknown*>(this));
+
+ if (riid == IID_IUnknown || riid == IID_IWeakReferenceSource) {
+ punk = static_cast<IUnknown*>(this);
+ } else {
+ HRESULT hr = ThreadSafeQueryInterface(riid, getter_AddRefs(punk));
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+
+ if (!punk) {
+ return E_NOINTERFACE;
+ }
+
+ punk.forget(ppv);
+ return S_OK;
+}
+
+ULONG
+WeakReferenceSupport::AddRef()
+{
+ AutoCriticalSection lock(&mCS);
+ return ++mRefCnt;
+}
+
+ULONG
+WeakReferenceSupport::Release()
+{
+ ULONG newRefCnt;
+ { // Scope for lock
+ AutoCriticalSection lock(&mCS);
+ newRefCnt = --mRefCnt;
+ if (newRefCnt == 0) {
+ ClearWeakRefs();
+ }
+ }
+ if (newRefCnt == 0) {
+ if (mFlags != Flags::eDestroyOnMainThread || NS_IsMainThread()) {
+ delete this;
+ } else {
+ // We need to delete this object on the main thread, but we aren't on the
+ // main thread right now, so we send a reference to ourselves to the main
+ // thread to be re-released there.
+ RefPtr<WeakReferenceSupport> self = this;
+ NS_ReleaseOnMainThread(self.forget());
+ }
+ }
+ return newRefCnt;
+}
+
+void
+WeakReferenceSupport::ClearWeakRefs()
+{
+ for (uint32_t i = 0, len = mWeakRefs.Length(); i < len; ++i) {
+ mWeakRefs[i]->Clear();
+ mWeakRefs[i]->Release();
+ }
+ mWeakRefs.Clear();
+}
+
+HRESULT
+WeakReferenceSupport::GetWeakReference(IWeakReference** aOutWeakRef)
+{
+ if (!aOutWeakRef) {
+ return E_INVALIDARG;
+ }
+ *aOutWeakRef = nullptr;
+
+ AutoCriticalSection lock(&mCS);
+ RefPtr<WeakRef> weakRef = MakeAndAddRef<WeakRef>(this);
+
+ HRESULT hr = weakRef->QueryInterface(IID_IWeakReference, (void**)aOutWeakRef);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ mWeakRefs.AppendElement(weakRef.get());
+ weakRef->AddRef();
+ return S_OK;
+}
+
+WeakRef::WeakRef(WeakReferenceSupport* aSupport)
+ : mRefCnt(1)
+ , mMutex("mozilla::mscom::WeakRef::mMutex")
+ , mSupport(aSupport)
+{
+ MOZ_ASSERT(aSupport);
+}
+
+HRESULT
+WeakRef::QueryInterface(REFIID riid, void** ppv)
+{
+ IUnknown* punk = nullptr;
+ if (!ppv) {
+ return E_INVALIDARG;
+ }
+
+ if (riid == IID_IUnknown || riid == IID_IWeakReference) {
+ punk = static_cast<IUnknown*>(this);
+ }
+
+ *ppv = punk;
+ if (!punk) {
+ return E_NOINTERFACE;
+ }
+
+ punk->AddRef();
+ return S_OK;
+}
+
+ULONG
+WeakRef::AddRef()
+{
+ return (ULONG) InterlockedIncrement((LONG*)&mRefCnt);
+}
+
+ULONG
+WeakRef::Release()
+{
+ ULONG newRefCnt = (ULONG) InterlockedDecrement((LONG*)&mRefCnt);
+ if (newRefCnt == 0) {
+ delete this;
+ }
+ return newRefCnt;
+}
+
+HRESULT
+WeakRef::Resolve(REFIID aIid, void** aOutStrongReference)
+{
+ MutexAutoLock lock(mMutex);
+ if (!mSupport) {
+ return E_FAIL;
+ }
+ return mSupport->QueryInterface(aIid, aOutStrongReference);
+}
+
+void
+WeakRef::Clear()
+{
+ MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(mSupport);
+ mSupport = nullptr;
+}
+
+} // namespace mscom
+} // namespace mozilla
+
diff --git a/ipc/mscom/WeakRef.h b/ipc/mscom/WeakRef.h
new file mode 100644
index 000000000..4b4835aa5
--- /dev/null
+++ b/ipc/mscom/WeakRef.h
@@ -0,0 +1,104 @@
+/* -*- 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_WeakRef_h
+#define mozilla_mscom_WeakRef_h
+
+#include <guiddef.h>
+#include <unknwn.h>
+
+#include "mozilla/Mutex.h"
+#include "nsTArray.h"
+
+/**
+ * Thread-safe weak references for COM that works pre-Windows 8 and do not
+ * require WinRT.
+ */
+
+namespace mozilla {
+namespace mscom {
+
+// {F841AEFA-064C-49A4-B73D-EBD14A90F012}
+DEFINE_GUID(IID_IWeakReference,
+0xf841aefa, 0x64c, 0x49a4, 0xb7, 0x3d, 0xeb, 0xd1, 0x4a, 0x90, 0xf0, 0x12);
+
+struct IWeakReference : public IUnknown
+{
+ virtual STDMETHODIMP Resolve(REFIID aIid, void** aOutStringReference) = 0;
+};
+
+// {87611F0C-9BBB-4F78-9D43-CAC5AD432CA1}
+DEFINE_GUID(IID_IWeakReferenceSource,
+0x87611f0c, 0x9bbb, 0x4f78, 0x9d, 0x43, 0xca, 0xc5, 0xad, 0x43, 0x2c, 0xa1);
+
+struct IWeakReferenceSource : public IUnknown
+{
+ virtual STDMETHODIMP GetWeakReference(IWeakReference** aOutWeakRef) = 0;
+};
+
+class WeakRef;
+
+class WeakReferenceSupport : public IWeakReferenceSource
+{
+public:
+ enum class Flags
+ {
+ eNone = 0,
+ eDestroyOnMainThread = 1
+ };
+
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
+ STDMETHODIMP_(ULONG) AddRef() override;
+ STDMETHODIMP_(ULONG) Release() override;
+
+ // IWeakReferenceSource
+ STDMETHODIMP GetWeakReference(IWeakReference** aOutWeakRef) override;
+
+protected:
+ explicit WeakReferenceSupport(Flags aFlags);
+ virtual ~WeakReferenceSupport();
+
+ virtual HRESULT ThreadSafeQueryInterface(REFIID aIid,
+ IUnknown** aOutInterface) = 0;
+
+private:
+ void ClearWeakRefs();
+
+private:
+ // Using a raw CRITICAL_SECTION here because it can be reentered
+ CRITICAL_SECTION mCS;
+ ULONG mRefCnt;
+ nsTArray<WeakRef*> mWeakRefs;
+ Flags mFlags;
+};
+
+class WeakRef : public IWeakReference
+{
+public:
+ // IUnknown
+ STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
+ STDMETHODIMP_(ULONG) AddRef() override;
+ STDMETHODIMP_(ULONG) Release() override;
+
+ // IWeakReference
+ STDMETHODIMP Resolve(REFIID aIid, void** aOutStrongReference) override;
+
+ explicit WeakRef(WeakReferenceSupport* aSupport);
+
+ void Clear();
+
+private:
+ ULONG mRefCnt;
+ mozilla::Mutex mMutex; // Protects mSupport
+ WeakReferenceSupport* mSupport;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_WeakRef_h
+
diff --git a/ipc/mscom/moz.build b/ipc/mscom/moz.build
new file mode 100644
index 000000000..e5edbf591
--- /dev/null
+++ b/ipc/mscom/moz.build
@@ -0,0 +1,60 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.mscom += [
+ 'COMApartmentRegion.h',
+ 'COMPtrHolder.h',
+ 'EnsureMTA.h',
+ 'MainThreadRuntime.h',
+ 'ProxyStream.h',
+ 'Ptr.h',
+ 'Utils.h',
+]
+
+SOURCES += [
+ 'Utils.cpp',
+]
+
+UNIFIED_SOURCES += [
+ 'EnsureMTA.cpp',
+ 'MainThreadRuntime.cpp',
+ 'ProxyStream.cpp',
+]
+
+if CONFIG['ACCESSIBILITY']:
+ EXPORTS.mozilla.mscom += [
+ 'ActivationContext.h',
+ 'DispatchForwarder.h',
+ 'Interceptor.h',
+ 'InterceptorLog.h',
+ 'MainThreadHandoff.h',
+ 'MainThreadInvoker.h',
+ 'Registration.h',
+ 'WeakRef.h',
+ ]
+
+ SOURCES += [
+ 'Interceptor.cpp',
+ 'Registration.cpp',
+ 'WeakRef.cpp',
+ ]
+
+ UNIFIED_SOURCES += [
+ 'ActivationContext.cpp',
+ 'DispatchForwarder.cpp',
+ 'InterceptorLog.cpp',
+ 'MainThreadHandoff.cpp',
+ 'MainThreadInvoker.cpp',
+ ]
+
+LOCAL_INCLUDES += [
+ '/xpcom/base',
+ '/xpcom/build',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'