diff options
Diffstat (limited to 'ipc/mscom/DispatchForwarder.cpp')
-rw-r--r-- | ipc/mscom/DispatchForwarder.cpp | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/ipc/mscom/DispatchForwarder.cpp b/ipc/mscom/DispatchForwarder.cpp new file mode 100644 index 0000000000..e55b1316d0 --- /dev/null +++ b/ipc/mscom/DispatchForwarder.cpp @@ -0,0 +1,156 @@ +/* -*- 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/DispatchForwarder.h" + +#include <oleauto.h> + +#include <utility> + +#include "mozilla/mscom/MainThreadInvoker.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(std::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) { + RefPtr<ITypeInfo> copy(mTypeInfo); + copy.forget(ppTInfo); + return S_OK; + } + HRESULT hr = E_UNEXPECTED; + auto fn = [&]() -> void { hr = mTarget->GetTypeInfo(iTInfo, lcid, ppTInfo); }; + MainThreadInvoker invoker; + if (!invoker.Invoke( + NS_NewRunnableFunction("DispatchForwarder::GetTypeInfo", 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("DispatchForwarder::GetIDsOfNames", 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 |