Bug 1293486: Add tearoff IDispatch handler to mscom interceptor; r=jimm
authorAaron Klotz <aklotz@mozilla.com>
Fri, 12 Aug 2016 12:12:48 -0600
changeset 310099 cd144a0012a5d8ce1626c3f47d3eac262a6e3a82
parent 310098 46132442a61f61af64ebb0234392d51379fa40d4
child 310100 d0ba9997681a36d6772b809e87077718eafff1f4
push id80771
push userkwierso@gmail.com
push dateThu, 18 Aug 2016 23:33:08 +0000
treeherdermozilla-inbound@cb1295738c37 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1293486
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1293486: Add tearoff IDispatch handler to mscom interceptor; r=jimm MozReview-Commit-ID: 9PSbpkNcOOh
ipc/mscom/DispatchForwarder.cpp
ipc/mscom/DispatchForwarder.h
ipc/mscom/Interceptor.cpp
ipc/mscom/moz.build
new file mode 100644
--- /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
new file mode 100644
--- /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 : 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
+
--- a/ipc/mscom/Interceptor.cpp
+++ b/ipc/mscom/Interceptor.cpp
@@ -3,16 +3,17 @@
 /* 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"
@@ -269,16 +270,27 @@ 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();
 }
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -2,16 +2,17 @@
 # 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',
+    'DispatchForwarder.h',
     'EnsureMTA.h',
     'Interceptor.h',
     'InterceptorLog.h',
     'MainThreadHandoff.h',
     'MainThreadInvoker.h',
     'MainThreadRuntime.h',
     'ProxyStream.h',
     'Ptr.h',
@@ -23,16 +24,17 @@ EXPORTS.mozilla.mscom += [
 SOURCES += [
     'Interceptor.cpp',
     'Registration.cpp',
     'Utils.cpp',
     'WeakRef.cpp',
 ]
 
 UNIFIED_SOURCES += [
+    'DispatchForwarder.cpp',
     'EnsureMTA.cpp',
     'InterceptorLog.cpp',
     'MainThreadHandoff.cpp',
     'MainThreadInvoker.cpp',
     'MainThreadRuntime.cpp',
     'ProxyStream.cpp',
 ]