Bug 1293486: Add tearoff IDispatch handler to mscom interceptor; r?jimm draft
authorAaron Klotz <aklotz@mozilla.com>
Fri, 12 Aug 2016 12:12:48 -0600
changeset 400203 5272ebcd007ba61f396b3fb1ffb331156c5f5038
parent 400202 4558131ecc3a280cf591de235969c23f744f5d9c
child 528151 fda2e5dc969519413c6f36e7f09f1cad8829a2fd
push id26087
push useraklotz@mozilla.com
push dateFri, 12 Aug 2016 18:14:01 +0000
reviewersjimm
bugs1293486
milestone51.0a1
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',
 ]