Bug 1303060: ipc/mscom changes for handler and payload support; r=jimm
authorAaron Klotz <aklotz@mozilla.com>
Fri, 17 Feb 2017 17:30:03 -0700
changeset 374464 b5c379a1a3cb4d4303214217d557232b7f0e03db
parent 374463 0ae0446f58c340060db184d6760755a3b289d731
child 374465 e08dabc92ecd313587800d8e8d2d81a9f6e4ba90
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1303060
milestone54.0a1
Bug 1303060: ipc/mscom changes for handler and payload support; r=jimm MozReview-Commit-ID: 13NFW1pgxix
ipc/mscom/Aggregation.h
ipc/mscom/IHandlerPayload.h
ipc/mscom/Interceptor.cpp
ipc/mscom/Interceptor.h
ipc/mscom/MainThreadHandoff.cpp
ipc/mscom/MainThreadHandoff.h
ipc/mscom/Registration.cpp
ipc/mscom/Registration.h
ipc/mscom/moz.build
--- a/ipc/mscom/Aggregation.h
+++ b/ipc/mscom/Aggregation.h
@@ -4,16 +4,19 @@
  * 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_Aggregation_h
 #define mozilla_mscom_Aggregation_h
 
 #include "mozilla/Attributes.h"
 
+#include <stddef.h>
+#include <unknwn.h>
+
 namespace mozilla {
 namespace mscom {
 
 /**
  * This is used for stabilizing a COM object's reference count during
  * construction when that object aggregates other objects. Since the aggregated
  * object(s) may AddRef() or Release(), we need to artifically boost the
  * refcount to prevent premature destruction. Note that we increment/decrement
@@ -39,13 +42,58 @@ public:
   StabilizedRefCount(StabilizedRefCount&&) = delete;
   StabilizedRefCount& operator=(const StabilizedRefCount&) = delete;
   StabilizedRefCount& operator=(StabilizedRefCount&&) = delete;
 
 private:
   RefCntT& mRefCnt;
 };
 
+namespace detail {
+
+template <typename T>
+class InternalUnknown : public IUnknown
+{
+public:
+  STDMETHODIMP QueryInterface(REFIID aIid, void** aOutInterface) override
+  {
+    return This()->InternalQueryInterface(aIid, aOutInterface);
+  }
+
+  STDMETHODIMP_(ULONG) AddRef() override
+  {
+    return This()->InternalAddRef();
+  }
+
+  STDMETHODIMP_(ULONG) Release() override
+  {
+    return This()->InternalRelease();
+  }
+
+private:
+  T* This()
+  {
+    return reinterpret_cast<T*>(reinterpret_cast<char*>(this) -
+                                offsetof(T, mInternalUnknown));
+  }
+};
+
+} // namespace detail
 } // namespace mscom
 } // namespace mozilla
 
+#define DECLARE_AGGREGATABLE(Type) \
+  public: \
+    STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override \
+    { return mOuter->QueryInterface(riid, ppv); } \
+    STDMETHODIMP_(ULONG) AddRef() override \
+    { return mOuter->AddRef(); } \
+    STDMETHODIMP_(ULONG) Release() override \
+    { return mOuter->Release(); } \
+  protected: \
+    STDMETHODIMP InternalQueryInterface(REFIID riid, void** ppv); \
+    STDMETHODIMP_(ULONG) InternalAddRef(); \
+    STDMETHODIMP_(ULONG) InternalRelease(); \
+    friend class mozilla::mscom::detail::InternalUnknown<Type>; \
+    mozilla::mscom::detail::InternalUnknown<Type> mInternalUnknown
+
 #endif // mozilla_mscom_Aggregation_h
 
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/IHandlerPayload.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_IHandlerPayload_h
+#define mozilla_mscom_IHandlerPayload_h
+
+#include <objidl.h>
+
+namespace mozilla {
+namespace mscom {
+
+struct HandlerPayload
+{
+  virtual STDMETHODIMP GetHandler(CLSID* aHandlerClsid) = 0;
+  virtual STDMETHODIMP GetHandlerPayloadSize(REFIID aIid,
+                                             IUnknown* aTarget,
+                                             DWORD* aOutPayloadSize) = 0;
+  virtual STDMETHODIMP WriteHandlerPayload(IStream* aStream, REFIID aIid,
+                                           IUnknown* aTarget) = 0;
+  virtual REFIID MarshalAs(REFIID aIid) = 0;
+};
+
+struct IHandlerPayload : public IUnknown
+                       , public HandlerPayload
+{
+  virtual STDMETHODIMP Clone(IHandlerPayload** aOutNewPayload) = 0;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_IHandlerPayload_h
--- a/ipc/mscom/Interceptor.cpp
+++ b/ipc/mscom/Interceptor.cpp
@@ -41,16 +41,17 @@ Interceptor::Create(STAUniquePtr<IUnknow
   return intcpt->QueryInterface(aIid, aOutput);
 }
 
 Interceptor::Interceptor(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink)
   : WeakReferenceSupport(WeakReferenceSupport::Flags::eDestroyOnMainThread)
   , mTarget(Move(aTarget))
   , mEventSink(aSink)
   , mMutex("mozilla::mscom::Interceptor::mMutex")
+  , mStdMarshal(nullptr)
 {
   MOZ_ASSERT(aSink);
   MOZ_ASSERT(!IsProxy(mTarget.get()));
   RefPtr<IWeakReference> weakRef;
   if (SUCCEEDED(GetWeakReference(getter_AddRefs(weakRef)))) {
     aSink->SetInterceptor(weakRef);
   }
 }
@@ -62,16 +63,87 @@ Interceptor::~Interceptor()
   MOZ_ASSERT(NS_IsMainThread());
   for (uint32_t index = 0, len = mInterceptorMap.Length(); index < len; ++index) {
     MapEntry& entry = mInterceptorMap[index];
     entry.mInterceptor = nullptr;
     entry.mTargetInterface->Release();
   }
 }
 
+HRESULT
+Interceptor::GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
+                                CLSID* aHandlerClsid)
+{
+  if (aDestContextPtr || !aHandlerClsid ||
+      aDestContext == MSHCTX_DIFFERENTMACHINE) {
+    return E_INVALIDARG;
+  }
+  MOZ_ASSERT(mEventSink);
+  return mEventSink->GetHandler(aHandlerClsid);
+}
+
+HRESULT
+Interceptor::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
+                               void* pvDestContext, DWORD mshlflags,
+                               CLSID* pCid)
+{
+  return mStdMarshal->GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext,
+                                        mshlflags, pCid);
+}
+
+HRESULT
+Interceptor::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
+                               void* pvDestContext, DWORD mshlflags,
+                               DWORD* pSize)
+{
+  HRESULT hr = mStdMarshal->GetMarshalSizeMax(riid, pv, dwDestContext,
+                                              pvDestContext, mshlflags, pSize);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  DWORD payloadSize = 0;
+  hr = mEventSink->GetHandlerPayloadSize(riid, mTarget.get(), &payloadSize);
+  *pSize += payloadSize;
+  return hr;
+}
+
+HRESULT
+Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
+                              DWORD dwDestContext, void* pvDestContext,
+                              DWORD mshlflags)
+{
+  HRESULT hr = mStdMarshal->MarshalInterface(pStm, riid, pv, dwDestContext,
+                                             pvDestContext, mshlflags);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return mEventSink->WriteHandlerPayload(pStm, riid, mTarget.get());
+}
+
+HRESULT
+Interceptor::UnmarshalInterface(IStream* pStm, REFIID riid,
+                                void** ppv)
+{
+  return mStdMarshal->UnmarshalInterface(pStm, riid, ppv);
+}
+
+HRESULT
+Interceptor::ReleaseMarshalData(IStream* pStm)
+{
+  return mStdMarshal->ReleaseMarshalData(pStm);
+}
+
+HRESULT
+Interceptor::DisconnectObject(DWORD dwReserved)
+{
+  return mStdMarshal->DisconnectObject(dwReserved);
+}
+
 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];
     }
@@ -143,57 +215,60 @@ 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();
+    RefPtr<IInterceptor> intcpt(this);
+    intcpt.forget(aOutInterceptor);
     return S_OK;
   }
 
+  REFIID interceptorIid = mEventSink->MarshalAs(aIid);
+
   RefPtr<IUnknown> unkInterceptor;
   IUnknown* interfaceForQILog = nullptr;
 
-  // (1) Check to see if we already have an existing interceptor for aIid.
+  // (1) Check to see if we already have an existing interceptor for
+  // interceptorIid.
 
   { // Scope for lock
     MutexAutoLock lock(mMutex);
-    MapEntry* entry = Lookup(aIid);
+    MapEntry* entry = Lookup(interceptorIid);
     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);
+    return unkInterceptor->QueryInterface(interceptorIid, 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);
+  hr = QueryInterfaceTarget(interceptorIid, (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
@@ -201,17 +276,18 @@ Interceptor::GetInterceptorForIID(REFIID
 
   // (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));
+  hr = CreateInterceptor(interceptorIid, 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,
@@ -226,31 +302,31 @@ Interceptor::GetInterceptorForIID(REFIID
   }
 
   // (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);
+    MapEntry* entry = Lookup(interceptorIid);
     if (entry && entry->mInterceptor) {
       unkInterceptor = entry->mInterceptor;
     } else {
       // MapEntry has a RefPtr to unkInterceptor, 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,
+      mInterceptorMap.AppendElement(MapEntry(interceptorIid,
                                              unkInterceptor,
                                              rawTargetInterface));
     }
   }
 
-  return unkInterceptor->QueryInterface(aIid, aOutInterceptor);
+  return unkInterceptor->QueryInterface(interceptorIid, 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*.
@@ -270,19 +346,73 @@ HRESULT
 Interceptor::QueryInterface(REFIID riid, void** ppv)
 {
   return WeakReferenceSupport::QueryInterface(riid, ppv);
 }
 
 HRESULT
 Interceptor::ThreadSafeQueryInterface(REFIID aIid, IUnknown** aOutInterface)
 {
+  if (aIid == IID_INoMarshal) {
+    // This entire library is designed around marshaling, so there's no point
+    // propagating this QI request all over the place!
+    return E_NOINTERFACE;
+  }
+
+  if (aIid == IID_IStdMarshalInfo) {
+    // Do not indicate that this interface is available unless we actually
+    // support it. We'll check that by looking for a successful call to
+    // IInterceptorSink::GetHandler()
+    CLSID dummy;
+    if (FAILED(mEventSink->GetHandler(&dummy))) {
+      return E_NOINTERFACE;
+    }
+
+    RefPtr<IStdMarshalInfo> std(this);
+    std.forget(aOutInterface);
+    return S_OK;
+  }
+
+  if (aIid == IID_IMarshal) {
+    // Do not indicate that this interface is available unless we actually
+    // support it. We'll check that by looking for a successful call to
+    // IInterceptorSink::GetHandler()
+    CLSID dummy;
+    if (FAILED(mEventSink->GetHandler(&dummy))) {
+      return E_NOINTERFACE;
+    }
+
+    if (!mStdMarshalUnk) {
+      HRESULT hr = ::CoGetStdMarshalEx(static_cast<IWeakReferenceSource*>(this),
+                                       SMEXF_SERVER,
+                                       getter_AddRefs(mStdMarshalUnk));
+      if (FAILED(hr)) {
+        return hr;
+      }
+    }
+
+    if (!mStdMarshal) {
+      HRESULT hr = mStdMarshalUnk->QueryInterface(IID_IMarshal,
+                                                  (void**)&mStdMarshal);
+      if (FAILED(hr)) {
+        return hr;
+      }
+
+      // mStdMarshal is weak, so drop its refcount
+      mStdMarshal->Release();
+    }
+
+    RefPtr<IMarshal> marshal(this);
+    marshal.forget(aOutInterface);
+    return S_OK;
+  }
+
   if (aIid == IID_IInterceptor) {
-    *aOutInterface = static_cast<IInterceptor*>(this);
-    (*aOutInterface)->AddRef();
+    RefPtr<IInterceptor> intcpt(this);
+    intcpt.forget(aOutInterface);
     return S_OK;
   }
 
   if (aIid == IID_IDispatch) {
     STAUniquePtr<IDispatch> disp;
     IDispatch* rawDisp = nullptr;
     HRESULT hr = QueryInterfaceTarget(aIid, (void**)&rawDisp);
     if (FAILED(hr)) {
--- a/ipc/mscom/Interceptor.h
+++ b/ipc/mscom/Interceptor.h
@@ -1,34 +1,37 @@
 /* -*- 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
+#ifndef mozilla_mscom_Interceptor_h
+#define mozilla_mscom_Interceptor_h
 
 #include "mozilla/Move.h"
 #include "mozilla/Mutex.h"
 #include "nsTArray.h"
+#include "mozilla/mscom/IHandlerPayload.h"
 #include "mozilla/mscom/Ptr.h"
 #include "mozilla/mscom/WeakRef.h"
 #include "mozilla/RefPtr.h"
 
+#include <objidl.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
+                        , public HandlerPayload
 {
   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);
 
@@ -52,27 +55,48 @@ struct IInterceptor : public IUnknown
  * 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 IStdMarshalInfo
+                        , public IMarshal
                         , 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;
 
+  // IStdMarshalInfo
+  STDMETHODIMP GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
+                                  CLSID* aHandlerClsid) override;
+
+  // IMarshal
+  STDMETHODIMP GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
+                                 void* pvDestContext, DWORD mshlflags,
+                                 CLSID* pCid) override;
+  STDMETHODIMP GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
+                                 void* pvDestContext, DWORD mshlflags,
+                                 DWORD* pSize) override;
+  STDMETHODIMP MarshalInterface(IStream* pStm, REFIID riid, void* pv,
+                                DWORD dwDestContext, void* pvDestContext,
+                                DWORD mshlflags) override;
+  STDMETHODIMP UnmarshalInterface(IStream* pStm, REFIID riid,
+                                  void** ppv) override;
+  STDMETHODIMP ReleaseMarshalData(IStream* pStm) override;
+  STDMETHODIMP DisconnectObject(DWORD dwReserved) 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)
@@ -95,16 +119,18 @@ private:
   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;
+  RefPtr<IUnknown>          mStdMarshalUnk;
+  IMarshal*                 mStdMarshal; // WEAK
 };
 
 template <typename InterfaceT>
 inline HRESULT
 CreateInterceptor(STAUniquePtr<InterfaceT> aTargetInterface,
                   IInterceptorSink* aEventSink,
                   InterfaceT** aOutInterface)
 {
@@ -117,9 +143,9 @@ CreateInterceptor(STAUniquePtr<Interface
   STAUniquePtr<IUnknown> targetUnknown(aTargetInterface.release());
   return Interceptor::Create(Move(targetUnknown), aEventSink, iidTarget,
                              (void**)aOutInterface);
 }
 
 } // namespace mscom
 } // namespace mozilla
 
-#endif // mozilla_mscom_interceptor_h
+#endif // mozilla_mscom_Interceptor_h
--- a/ipc/mscom/MainThreadHandoff.cpp
+++ b/ipc/mscom/MainThreadHandoff.cpp
@@ -1,70 +1,179 @@
 /* -*- 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/Attributes.h"
 #include "mozilla/Move.h"
+#include "mozilla/mscom/AgileReference.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;
+using mozilla::mscom::AgileReference;
 
 namespace {
 
+class MOZ_NONHEAP_CLASS InParamWalker : private ICallFrameWalker
+{
+public:
+  InParamWalker()
+    : mPreHandoff(true)
+  {
+  }
+
+  void SetHandoffDone()
+  {
+    mPreHandoff = false;
+    mAgileRefsItr = mAgileRefs.begin();
+  }
+
+  HRESULT Walk(ICallFrame* aFrame)
+  {
+    MOZ_ASSERT(aFrame);
+    if (!aFrame) {
+      return E_INVALIDARG;
+    }
+
+    return aFrame->WalkFrame(CALLFRAME_WALK_IN, this);
+  }
+
+private:
+  // IUnknown
+  STDMETHODIMP QueryInterface(REFIID aIid, void** aOutInterface) override
+  {
+    if (!aOutInterface) {
+      return E_INVALIDARG;
+    }
+    *aOutInterface = nullptr;
+
+    if (aIid == IID_IUnknown || aIid == IID_ICallFrameWalker) {
+      *aOutInterface = static_cast<ICallFrameWalker*>(this);
+      return S_OK;
+    }
+
+    return E_NOINTERFACE;
+  }
+
+  STDMETHODIMP_(ULONG) AddRef() override
+  {
+    return 2;
+  }
+
+  STDMETHODIMP_(ULONG) Release() override
+  {
+    return 1;
+  }
+
+  // ICallFrameWalker
+  STDMETHODIMP OnWalkInterface(REFIID aIid, PVOID* aInterface, BOOL aIn,
+                               BOOL aOut) override
+  {
+    MOZ_ASSERT(aIn);
+    if (!aIn) {
+      return E_UNEXPECTED;
+    }
+
+    IUnknown* origInterface = static_cast<IUnknown*>(*aInterface);
+    if (!origInterface) {
+      // Nothing to do
+      return S_OK;
+    }
+
+    if (mPreHandoff) {
+      mAgileRefs.AppendElement(AgileReference(aIid, origInterface));
+      return S_OK;
+    }
+
+    MOZ_ASSERT(mAgileRefsItr != mAgileRefs.end());
+    if (mAgileRefsItr == mAgileRefs.end()) {
+      return E_UNEXPECTED;
+    }
+
+    HRESULT hr = mAgileRefsItr->Resolve(aIid, aInterface);
+    MOZ_ASSERT(SUCCEEDED(hr));
+    if (SUCCEEDED(hr)) {
+      ++mAgileRefsItr;
+    }
+
+    return hr;
+  }
+
+  InParamWalker(const InParamWalker&) = delete;
+  InParamWalker(InParamWalker&&) = delete;
+  InParamWalker& operator=(const InParamWalker&) = delete;
+  InParamWalker& operator=(InParamWalker&&) = delete;
+
+private:
+  bool                                mPreHandoff;
+  AutoTArray<AgileReference, 1>       mAgileRefs;
+  nsTArray<AgileReference>::iterator  mAgileRefsItr;
+};
+
 class HandoffRunnable : public mozilla::Runnable
 {
 public:
   explicit HandoffRunnable(ICallFrame* aCallFrame, IUnknown* aTargetInterface)
     : mCallFrame(aCallFrame)
     , mTargetInterface(aTargetInterface)
     , mResult(E_UNEXPECTED)
   {
+    DebugOnly<HRESULT> hr = mInParamWalker.Walk(aCallFrame);
+    MOZ_ASSERT(SUCCEEDED(hr));
   }
 
   NS_IMETHOD Run() override
   {
+    mInParamWalker.SetHandoffDone();
+    // We declare hr a DebugOnly because if mInParamWalker.Walk() fails, then
+    // mCallFrame->Invoke will fail anyway.
+    DebugOnly<HRESULT> hr = mInParamWalker.Walk(mCallFrame);
+    MOZ_ASSERT(SUCCEEDED(hr));
     mResult = mCallFrame->Invoke(mTargetInterface);
     return NS_OK;
   }
 
   HRESULT GetResult() const
   {
     return mResult;
   }
 
 private:
-  ICallFrame* mCallFrame;
-  IUnknown*   mTargetInterface;
-  HRESULT     mResult;
+  ICallFrame*   mCallFrame;
+  InParamWalker mInParamWalker;
+  IUnknown*     mTargetInterface;
+  HRESULT       mResult;
 };
 
 } // anonymous namespace
 
 namespace mozilla {
 namespace mscom {
 
 /* static */ HRESULT
-MainThreadHandoff::Create(IInterceptorSink** aOutput)
+MainThreadHandoff::Create(IHandlerPayload* aHandlerPayload,
+                          IInterceptorSink** aOutput)
 {
-  RefPtr<MainThreadHandoff> handoff(new MainThreadHandoff());
+  RefPtr<MainThreadHandoff> handoff(new MainThreadHandoff(aHandlerPayload));
   return handoff->QueryInterface(IID_IInterceptorSink, (void**) aOutput);
 }
 
-MainThreadHandoff::MainThreadHandoff()
+MainThreadHandoff::MainThreadHandoff(IHandlerPayload* aHandlerPayload)
   : mRefCnt(0)
+  , mHandlerPayload(aHandlerPayload)
 {
 }
 
 MainThreadHandoff::~MainThreadHandoff()
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
@@ -138,17 +247,17 @@ MainThreadHandoff::OnCall(ICallFrame* aF
   }
 
   InterceptorTargetPtr targetInterface;
   hr = interceptor->GetTargetForIID(iid, targetInterface);
   if (FAILED(hr)) {
     return hr;
   }
 
-  // (2) Execute the method call syncrhonously on the main thread
+  // (2) Execute the method call synchronously 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();
@@ -292,16 +401,54 @@ MainThreadHandoff::FixArrayElements(ICal
 HRESULT
 MainThreadHandoff::SetInterceptor(IWeakReference* aInterceptor)
 {
   mInterceptor = aInterceptor;
   return S_OK;
 }
 
 HRESULT
+MainThreadHandoff::GetHandler(CLSID* aHandlerClsid)
+{
+  if (!mHandlerPayload) {
+    return E_NOTIMPL;
+  }
+  return mHandlerPayload->GetHandler(aHandlerClsid);
+}
+
+HRESULT
+MainThreadHandoff::GetHandlerPayloadSize(REFIID aIid, IUnknown* aTarget,
+                                         DWORD* aOutPayloadSize)
+{
+  if (!mHandlerPayload) {
+    return E_NOTIMPL;
+  }
+  return mHandlerPayload->GetHandlerPayloadSize(aIid, aTarget, aOutPayloadSize);
+}
+
+HRESULT
+MainThreadHandoff::WriteHandlerPayload(IStream* aStream, REFIID aIid,
+                                       IUnknown* aTarget)
+{
+  if (!mHandlerPayload) {
+    return E_NOTIMPL;
+  }
+  return mHandlerPayload->WriteHandlerPayload(aStream, aIid, aTarget);
+}
+
+REFIID
+MainThreadHandoff::MarshalAs(REFIID aIid)
+{
+  if (!mHandlerPayload) {
+    return aIid;
+  }
+  return mHandlerPayload->MarshalAs(aIid);
+}
+
+HRESULT
 MainThreadHandoff::OnWalkInterface(REFIID aIid, PVOID* aInterface,
                                    BOOL aIsInParam, BOOL aIsOutParam)
 {
   MOZ_ASSERT(aInterface && aIsOutParam);
   if (!aInterface || !aIsOutParam) {
     return E_UNEXPECTED;
   }
 
@@ -364,26 +511,37 @@ MainThreadHandoff::OnWalkInterface(REFII
       if (FAILED(hr)) {
         return hr;
       }
       *aInterface = intercepted;
       return S_OK;
     }
   }
 
+  RefPtr<IHandlerPayload> payload;
+  if (mHandlerPayload) {
+    hr = mHandlerPayload->Clone(getter_AddRefs(payload));
+    MOZ_ASSERT(SUCCEEDED(hr));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
   // Now create a new MainThreadHandoff wrapper...
   RefPtr<IInterceptorSink> handoff;
-  hr = MainThreadHandoff::Create(getter_AddRefs(handoff));
+  hr = MainThreadHandoff::Create(payload, getter_AddRefs(handoff));
   MOZ_ASSERT(SUCCEEDED(hr));
   if (FAILED(hr)) {
     return hr;
   }
 
+  REFIID interceptorIid = payload ? payload->MarshalAs(aIid) : aIid;
+
   RefPtr<IUnknown> wrapped;
-  hr = Interceptor::Create(Move(origInterface), handoff, aIid,
+  hr = Interceptor::Create(Move(origInterface), handoff, interceptorIid,
                            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));
--- a/ipc/mscom/MainThreadHandoff.h
+++ b/ipc/mscom/MainThreadHandoff.h
@@ -19,53 +19,72 @@ namespace mozilla {
 namespace mscom {
 
 struct ArrayData;
 
 class MainThreadHandoff final : public IInterceptorSink
                               , public ICallFrameWalker
 {
 public:
-  static HRESULT Create(IInterceptorSink** aOutput);
+  static HRESULT Create(IHandlerPayload* aHandlerPayload,
+                        IInterceptorSink** aOutput);
 
   template <typename Interface>
   static HRESULT WrapInterface(STAUniquePtr<Interface> aTargetInterface,
                                Interface** aOutInterface)
   {
+    return WrapInterface<Interface>(Move(aTargetInterface), nullptr,
+                                    aOutInterface);
+  }
+
+  template <typename Interface>
+  static HRESULT WrapInterface(STAUniquePtr<Interface> aTargetInterface,
+                               IHandlerPayload* aHandlerPayload,
+                               Interface** aOutInterface)
+  {
     MOZ_ASSERT(!IsProxy(aTargetInterface.get()));
     RefPtr<IInterceptorSink> handoff;
-    HRESULT hr = MainThreadHandoff::Create(getter_AddRefs(handoff));
+    HRESULT hr = MainThreadHandoff::Create(aHandlerPayload,
+                                           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;
+  STDMETHODIMP GetHandler(CLSID* aHandlerClsid) override;
+  STDMETHODIMP GetHandlerPayloadSize(REFIID aIid,
+                                     IUnknown* aTarget,
+                                     DWORD* aOutPayloadSize) override;
+  STDMETHODIMP WriteHandlerPayload(IStream* aStream, REFIID aIid,
+                                   IUnknown* aTarget) override;
+  REFIID MarshalAs(REFIID aIid) override;
 
   // ICallFrameWalker
   STDMETHODIMP OnWalkInterface(REFIID aIid, PVOID* aInterface, BOOL aIsInParam,
                                BOOL aIsOutParam) override;
 
 private:
-  MainThreadHandoff();
+  explicit MainThreadHandoff(IHandlerPayload* aHandlerPayload);
   ~MainThreadHandoff();
   HRESULT FixArrayElements(ICallFrame* aFrame,
                            const ArrayData& aArrayData);
 
 private:
   ULONG                   mRefCnt;
   RefPtr<IWeakReference>  mInterceptor;
+  RefPtr<IHandlerPayload> mHandlerPayload;
 };
 
 } // namespace mscom
 } // namespace mozilla
 
 #endif // mozilla_mscom_MainThreadHandoff_h
--- a/ipc/mscom/Registration.cpp
+++ b/ipc/mscom/Registration.cpp
@@ -364,26 +364,26 @@ RegisteredProxy::operator=(RegisteredPro
   mRegCookie = aOther.mRegCookie;
   aOther.mRegCookie = 0;
   mTypeLib = aOther.mTypeLib;
   aOther.mTypeLib = nullptr;
   return *this;
 }
 
 HRESULT
-RegisteredProxy::GetTypeInfoForInterface(REFIID aIid,
-                                         ITypeInfo** aOutTypeInfo) const
+RegisteredProxy::GetTypeInfoForGuid(REFGUID aGuid,
+                                    ITypeInfo** aOutTypeInfo) const
 {
   if (!aOutTypeInfo) {
     return E_INVALIDARG;
   }
   if (!mTypeLib) {
     return E_UNEXPECTED;
   }
-  return mTypeLib->lpVtbl->GetTypeInfoOfGuid(mTypeLib, aIid, aOutTypeInfo);
+  return mTypeLib->lpVtbl->GetTypeInfoOfGuid(mTypeLib, aGuid, aOutTypeInfo);
 }
 
 static StaticAutoPtr<Vector<RegisteredProxy*>> sRegistry;
 
 namespace UseGetMutexForAccess {
 
 // This must not be accessed directly; use GetMutex() instead
 static CRITICAL_SECTION sMutex;
@@ -413,17 +413,17 @@ RegisteredProxy::Find(REFIID aIid, IType
 {
   AutoCriticalSection lock(GetMutex());
 
   if (!sRegistry) {
     return false;
   }
 
   for (auto&& proxy : *sRegistry) {
-    if (SUCCEEDED(proxy->GetTypeInfoForInterface(aIid, aTypeInfo))) {
+    if (SUCCEEDED(proxy->GetTypeInfoForGuid(aIid, aTypeInfo))) {
       return true;
     }
   }
 
   return false;
 }
 
 /* static */ void
--- a/ipc/mscom/Registration.h
+++ b/ipc/mscom/Registration.h
@@ -31,17 +31,17 @@ public:
   RegisteredProxy(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;
+  HRESULT GetTypeInfoForGuid(REFGUID aGuid, ITypeInfo** aOutTypeInfo) const;
 
   static bool Find(REFIID aIid, ITypeInfo** aOutTypeInfo);
 
 private:
   RegisteredProxy() = delete;
   RegisteredProxy(RegisteredProxy&) = delete;
   RegisteredProxy& operator=(RegisteredProxy&) = delete;
 
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -24,16 +24,17 @@ UNIFIED_SOURCES += [
     'ProxyStream.cpp',
     'Utils.cpp',
 ]
 
 if CONFIG['ACCESSIBILITY']:
     EXPORTS.mozilla.mscom += [
         'ActivationContext.h',
         'DispatchForwarder.h',
+        'IHandlerPayload.h',
         'Interceptor.h',
         'InterceptorLog.h',
         'MainThreadHandoff.h',
         'MainThreadInvoker.h',
         'Registration.h',
         'SpinEvent.h',
         'StructStream.h',
         'WeakRef.h',