diff options
Diffstat (limited to 'ipc/mscom/DispatchForwarder.cpp')
-rw-r--r-- | ipc/mscom/DispatchForwarder.cpp | 167 |
1 files changed, 167 insertions, 0 deletions
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 |