mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-03 13:57:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
46
ipc/mscom/ActivationContext.cpp
Normal file
46
ipc/mscom/ActivationContext.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- 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/ActivationContext.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
ActivationContext::ActivationContext(HMODULE aLoadFromModule)
|
||||
: mActCtx(INVALID_HANDLE_VALUE)
|
||||
, mActivationCookie(0)
|
||||
{
|
||||
ACTCTX actCtx = {sizeof(actCtx)};
|
||||
actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID | ACTCTX_FLAG_HMODULE_VALID;
|
||||
actCtx.lpResourceName = MAKEINTRESOURCE(2);
|
||||
actCtx.hModule = aLoadFromModule;
|
||||
|
||||
mActCtx = ::CreateActCtx(&actCtx);
|
||||
MOZ_ASSERT(mActCtx != INVALID_HANDLE_VALUE);
|
||||
if (mActCtx == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
if (!::ActivateActCtx(mActCtx, &mActivationCookie)) {
|
||||
::ReleaseActCtx(mActCtx);
|
||||
mActCtx = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
ActivationContext::~ActivationContext()
|
||||
{
|
||||
if (mActCtx == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
DebugOnly<BOOL> deactivated = ::DeactivateActCtx(0, mActivationCookie);
|
||||
MOZ_ASSERT(deactivated);
|
||||
::ReleaseActCtx(mActCtx);
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
35
ipc/mscom/ActivationContext.h
Normal file
35
ipc/mscom/ActivationContext.h
Normal file
|
|
@ -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_ActivationContext_h
|
||||
#define mozilla_mscom_ActivationContext_h
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
class ActivationContext
|
||||
{
|
||||
public:
|
||||
explicit ActivationContext(HMODULE aLoadFromModule);
|
||||
~ActivationContext();
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return mActCtx != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE mActCtx;
|
||||
ULONG_PTR mActivationCookie;
|
||||
};
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_ActivationContext_h
|
||||
|
||||
62
ipc/mscom/COMApartmentRegion.h
Normal file
62
ipc/mscom/COMApartmentRegion.h
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/* -*- 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_COMApartmentRegion_h
|
||||
#define mozilla_mscom_COMApartmentRegion_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
#include <objbase.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
template<COINIT T>
|
||||
class MOZ_NON_TEMPORARY_CLASS COMApartmentRegion
|
||||
{
|
||||
public:
|
||||
COMApartmentRegion()
|
||||
: mInitResult(::CoInitializeEx(nullptr, T))
|
||||
{
|
||||
// If this fires then we're probably mixing apartments on the same thread
|
||||
MOZ_ASSERT(IsValid());
|
||||
}
|
||||
|
||||
~COMApartmentRegion()
|
||||
{
|
||||
if (IsValid()) {
|
||||
::CoUninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValidOutermost() const
|
||||
{
|
||||
return mInitResult == S_OK;
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return SUCCEEDED(mInitResult);
|
||||
}
|
||||
|
||||
private:
|
||||
COMApartmentRegion(const COMApartmentRegion&) = delete;
|
||||
COMApartmentRegion& operator=(const COMApartmentRegion&) = delete;
|
||||
COMApartmentRegion(COMApartmentRegion&&) = delete;
|
||||
COMApartmentRegion& operator=(COMApartmentRegion&&) = delete;
|
||||
|
||||
HRESULT mInitResult;
|
||||
};
|
||||
|
||||
typedef COMApartmentRegion<COINIT_APARTMENTTHREADED> STARegion;
|
||||
typedef COMApartmentRegion<COINIT_MULTITHREADED> MTARegion;
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_COMApartmentRegion_h
|
||||
|
||||
144
ipc/mscom/COMPtrHolder.h
Normal file
144
ipc/mscom/COMPtrHolder.h
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/* -*- 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_COMPtrHolder_h
|
||||
#define mozilla_mscom_COMPtrHolder_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/mscom/ProxyStream.h"
|
||||
#include "mozilla/mscom/Ptr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
template<typename Interface, const IID& _IID>
|
||||
class COMPtrHolder
|
||||
{
|
||||
public:
|
||||
typedef ProxyUniquePtr<Interface> COMPtrType;
|
||||
typedef COMPtrHolder<Interface, _IID> ThisType;
|
||||
|
||||
COMPtrHolder() {}
|
||||
|
||||
MOZ_IMPLICIT COMPtrHolder(decltype(nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
explicit COMPtrHolder(COMPtrType&& aPtr)
|
||||
: mPtr(Forward<COMPtrType>(aPtr))
|
||||
{
|
||||
}
|
||||
|
||||
Interface* Get() const
|
||||
{
|
||||
return mPtr.get();
|
||||
}
|
||||
|
||||
MOZ_MUST_USE Interface* Release()
|
||||
{
|
||||
return mPtr.release();
|
||||
}
|
||||
|
||||
void Set(COMPtrType&& aPtr)
|
||||
{
|
||||
mPtr = Forward<COMPtrType>(aPtr);
|
||||
}
|
||||
|
||||
COMPtrHolder(const COMPtrHolder& aOther) = delete;
|
||||
|
||||
COMPtrHolder(COMPtrHolder&& aOther)
|
||||
: mPtr(Move(aOther.mPtr))
|
||||
{
|
||||
}
|
||||
|
||||
// COMPtrHolder is eventually added as a member of a struct that is declared
|
||||
// in IPDL. The generated C++ code for that IPDL struct includes copy
|
||||
// constructors and assignment operators that assume that all members are
|
||||
// copyable. I don't think that those copy constructors and operator= are
|
||||
// actually used by any generated code, but they are made available. Since no
|
||||
// move semantics are available, this terrible hack makes COMPtrHolder build
|
||||
// when used as a member of an IPDL struct.
|
||||
ThisType& operator=(const ThisType& aOther)
|
||||
{
|
||||
Set(Move(aOther.mPtr));
|
||||
return *this;
|
||||
}
|
||||
|
||||
ThisType& operator=(ThisType&& aOther)
|
||||
{
|
||||
Set(Move(aOther.mPtr));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const ThisType& aOther) const
|
||||
{
|
||||
return mPtr == aOther.mPtr;
|
||||
}
|
||||
|
||||
bool IsNull() const
|
||||
{
|
||||
return !mPtr;
|
||||
}
|
||||
|
||||
private:
|
||||
// This is mutable to facilitate the above operator= hack
|
||||
mutable COMPtrType mPtr;
|
||||
};
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template<typename Interface, const IID& _IID>
|
||||
struct ParamTraits<mozilla::mscom::COMPtrHolder<Interface, _IID>>
|
||||
{
|
||||
typedef mozilla::mscom::COMPtrHolder<Interface, _IID> paramType;
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
mozilla::mscom::ProxyStream proxyStream(_IID, aParam.Get());
|
||||
int bufLen;
|
||||
const BYTE* buf = proxyStream.GetBuffer(bufLen);
|
||||
MOZ_ASSERT(buf || !bufLen);
|
||||
aMsg->WriteInt(bufLen);
|
||||
aMsg->WriteBytes(reinterpret_cast<const char*>(buf), bufLen);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
|
||||
{
|
||||
int length;
|
||||
if (!aMsg->ReadLength(aIter, &length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mozilla::UniquePtr<BYTE[]> buf;
|
||||
if (length) {
|
||||
buf = mozilla::MakeUnique<BYTE[]>(length);
|
||||
if (!aMsg->ReadBytesInto(aIter, buf.get(), length)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::mscom::ProxyStream proxyStream(buf.get(), length);
|
||||
if (!proxyStream.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
Interface* rawInterface = nullptr;
|
||||
if (!proxyStream.GetInterface(_IID, (void**)&rawInterface)) {
|
||||
return false;
|
||||
}
|
||||
typename paramType::COMPtrType ptr(rawInterface);
|
||||
aResult->Set(mozilla::Move(ptr));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
#endif // mozilla_mscom_COMPtrHolder_h
|
||||
|
||||
167
ipc/mscom/DispatchForwarder.cpp
Normal file
167
ipc/mscom/DispatchForwarder.cpp
Normal file
|
|
@ -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
|
||||
81
ipc/mscom/DispatchForwarder.h
Normal file
81
ipc/mscom/DispatchForwarder.h
Normal file
|
|
@ -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 final : 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
|
||||
|
||||
68
ipc/mscom/DynamicallyLinkedFunctionPtr.h
Normal file
68
ipc/mscom/DynamicallyLinkedFunctionPtr.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/* -*- 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_DynamicallyLinkedFunctionPtr_h
|
||||
#define mozilla_mscom_DynamicallyLinkedFunctionPtr_h
|
||||
|
||||
#include "mozilla/Move.h"
|
||||
#include <windows.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
template <typename T>
|
||||
class DynamicallyLinkedFunctionPtr;
|
||||
|
||||
template <typename R, typename... Args>
|
||||
class DynamicallyLinkedFunctionPtr<R (__stdcall*)(Args...)>
|
||||
{
|
||||
typedef R (__stdcall* FunctionPtrT)(Args...);
|
||||
|
||||
public:
|
||||
DynamicallyLinkedFunctionPtr(const wchar_t* aLibName, const char* aFuncName)
|
||||
: mModule(NULL)
|
||||
, mFunction(nullptr)
|
||||
{
|
||||
mModule = ::LoadLibraryW(aLibName);
|
||||
if (mModule) {
|
||||
mFunction = reinterpret_cast<FunctionPtrT>(
|
||||
::GetProcAddress(mModule, aFuncName));
|
||||
}
|
||||
}
|
||||
|
||||
DynamicallyLinkedFunctionPtr(const DynamicallyLinkedFunctionPtr&) = delete;
|
||||
DynamicallyLinkedFunctionPtr& operator=(const DynamicallyLinkedFunctionPtr&) = delete;
|
||||
|
||||
DynamicallyLinkedFunctionPtr(DynamicallyLinkedFunctionPtr&&) = delete;
|
||||
DynamicallyLinkedFunctionPtr& operator=(DynamicallyLinkedFunctionPtr&&) = delete;
|
||||
|
||||
~DynamicallyLinkedFunctionPtr()
|
||||
{
|
||||
if (mModule) {
|
||||
::FreeLibrary(mModule);
|
||||
}
|
||||
}
|
||||
|
||||
R operator()(Args... args)
|
||||
{
|
||||
return mFunction(mozilla::Forward<Args>(args)...);
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return !!mFunction;
|
||||
}
|
||||
|
||||
private:
|
||||
HMODULE mModule;
|
||||
FunctionPtrT mFunction;
|
||||
};
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_DynamicallyLinkedFunctionPtr_h
|
||||
|
||||
78
ipc/mscom/EnsureMTA.cpp
Normal file
78
ipc/mscom/EnsureMTA.cpp
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/* -*- 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/EnsureMTA.h"
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "private/pprthred.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class EnterMTARunnable : public mozilla::Runnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mozilla::DebugOnly<HRESULT> hr = ::CoInitializeEx(nullptr,
|
||||
COINIT_MULTITHREADED);
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class BackgroundMTAData
|
||||
{
|
||||
public:
|
||||
BackgroundMTAData()
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable = new EnterMTARunnable();
|
||||
nsresult rv = NS_NewNamedThread("COM MTA",
|
||||
getter_AddRefs(mThread), runnable);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_NewNamedThread failed");
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
|
||||
~BackgroundMTAData()
|
||||
{
|
||||
if (mThread) {
|
||||
mThread->Dispatch(NS_NewRunnableFunction(&::CoUninitialize),
|
||||
NS_DISPATCH_NORMAL);
|
||||
mThread->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> GetThread() const
|
||||
{
|
||||
return mThread;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
static mozilla::StaticAutoPtr<BackgroundMTAData> sMTAData;
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
/* static */ nsCOMPtr<nsIThread>
|
||||
EnsureMTA::GetMTAThread()
|
||||
{
|
||||
if (!sMTAData) {
|
||||
sMTAData = new BackgroundMTAData();
|
||||
ClearOnShutdown(&sMTAData, ShutdownPhase::ShutdownThreads);
|
||||
}
|
||||
return sMTAData->GetThread();
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
87
ipc/mscom/EnsureMTA.h
Normal file
87
ipc/mscom/EnsureMTA.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* -*- 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_EnsureMTA_h
|
||||
#define mozilla_mscom_EnsureMTA_h
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Function.h"
|
||||
#include "mozilla/mscom/COMApartmentRegion.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
// This class is OK to use as a temporary on the stack.
|
||||
class MOZ_STACK_CLASS EnsureMTA
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This constructor just ensures that the MTA thread is up and running.
|
||||
*/
|
||||
EnsureMTA()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsIThread> thread = GetMTAThread();
|
||||
MOZ_ASSERT(thread);
|
||||
}
|
||||
|
||||
template <typename FuncT>
|
||||
EnsureMTA(const FuncT& aClosure)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (IsCurrentThreadMTA()) {
|
||||
// We're already on the MTA, we can run aClosure directly
|
||||
aClosure();
|
||||
return;
|
||||
}
|
||||
|
||||
// In this case we need to run aClosure on a background thread in the MTA
|
||||
nsCOMPtr<nsIThread> thread = GetMTAThread();
|
||||
MOZ_ASSERT(thread);
|
||||
|
||||
HANDLE event = ::CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto eventSetter = [&]() -> void {
|
||||
aClosure();
|
||||
::SetEvent(event);
|
||||
};
|
||||
|
||||
nsresult rv =
|
||||
thread->Dispatch(NS_NewRunnableFunction(eventSetter), NS_DISPATCH_NORMAL);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
::CloseHandle(event);
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD waitResult;
|
||||
while ((waitResult = ::WaitForSingleObjectEx(event, INFINITE, TRUE)) ==
|
||||
WAIT_IO_COMPLETION) {
|
||||
}
|
||||
MOZ_ASSERT(waitResult == WAIT_OBJECT_0);
|
||||
::CloseHandle(event);
|
||||
}
|
||||
|
||||
private:
|
||||
static nsCOMPtr<nsIThread> GetMTAThread();
|
||||
};
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_EnsureMTA_h
|
||||
|
||||
313
ipc/mscom/Interceptor.cpp
Normal file
313
ipc/mscom/Interceptor.cpp
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#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"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
/* static */ HRESULT
|
||||
Interceptor::Create(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink,
|
||||
REFIID aIid, void** aOutput)
|
||||
{
|
||||
MOZ_ASSERT(aOutput && aTarget && aSink);
|
||||
if (!aOutput) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
*aOutput = nullptr;
|
||||
if (!aTarget || !aSink) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
Interceptor* intcpt = new Interceptor(Move(aTarget), aSink);
|
||||
HRESULT hr = intcpt->QueryInterface(aIid, aOutput);
|
||||
static_cast<WeakReferenceSupport*>(intcpt)->Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
Interceptor::Interceptor(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink)
|
||||
: WeakReferenceSupport(WeakReferenceSupport::Flags::eDestroyOnMainThread)
|
||||
, mTarget(Move(aTarget))
|
||||
, mEventSink(aSink)
|
||||
, mMutex("mozilla::mscom::Interceptor::mMutex")
|
||||
{
|
||||
MOZ_ASSERT(aSink);
|
||||
MOZ_ASSERT(!IsProxy(mTarget.get()));
|
||||
RefPtr<IWeakReference> weakRef;
|
||||
if (SUCCEEDED(GetWeakReference(getter_AddRefs(weakRef)))) {
|
||||
aSink->SetInterceptor(weakRef);
|
||||
}
|
||||
}
|
||||
|
||||
Interceptor::~Interceptor()
|
||||
{
|
||||
// This needs to run on the main thread because it releases target interface
|
||||
// reference counts which may not be thread-safe.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
for (uint32_t index = 0, len = mInterceptorMap.Length(); index < len; ++index) {
|
||||
MapEntry& entry = mInterceptorMap[index];
|
||||
entry.mInterceptor->Release();
|
||||
entry.mTargetInterface->Release();
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
Interceptor::GetTargetForIID(REFIID aIid, InterceptorTargetPtr& aTarget)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MapEntry* entry = Lookup(aIid);
|
||||
if (entry) {
|
||||
aTarget.reset(entry->mTargetInterface);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
// CoGetInterceptor requires information from a typelib to be able to
|
||||
// generate its emulated vtable. If a typelib is unavailable,
|
||||
// CoGetInterceptor returns 0x80070002.
|
||||
static const HRESULT kFileNotFound = 0x80070002;
|
||||
|
||||
HRESULT
|
||||
Interceptor::CreateInterceptor(REFIID aIid, IUnknown* aOuter, IUnknown** aOutput)
|
||||
{
|
||||
// In order to aggregate, we *must* request IID_IUnknown as the initial
|
||||
// interface for the interceptor, as that IUnknown is non-delegating.
|
||||
// This is a fundamental rule for creating aggregated objects in COM.
|
||||
HRESULT hr = ::CoGetInterceptor(aIid, aOuter, IID_IUnknown, (void**)aOutput);
|
||||
if (hr != kFileNotFound) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// In the case that CoGetInterceptor returns kFileNotFound, we can try to
|
||||
// explicitly load typelib data from our runtime registration facility and
|
||||
// pass that into CoGetInterceptorFromTypeInfo.
|
||||
|
||||
RefPtr<ITypeInfo> typeInfo;
|
||||
bool found = RegisteredProxy::Find(aIid, getter_AddRefs(typeInfo));
|
||||
// If this assert fires then we have omitted registering the typelib for a
|
||||
// required interface. To fix this, review our calls to mscom::RegisterProxy
|
||||
// and mscom::RegisterTypelib, and add the additional typelib as necessary.
|
||||
MOZ_ASSERT(found);
|
||||
if (!found) {
|
||||
return kFileNotFound;
|
||||
}
|
||||
|
||||
hr = ::CoGetInterceptorFromTypeInfo(aIid, aOuter, typeInfo, IID_IUnknown,
|
||||
(void**)aOutput);
|
||||
// If this assert fires then the interceptor doesn't like something about
|
||||
// the format of the typelib. One thing in particular that it doesn't like
|
||||
// is complex types that contain unions.
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
return hr;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method contains the core guts of the handling of QueryInterface calls
|
||||
* that are delegated to us from the ICallInterceptor.
|
||||
*
|
||||
* @param aIid ID of the desired interface
|
||||
* @param aOutInterceptor The resulting emulated vtable that corresponds to
|
||||
* the interface specified by aIid.
|
||||
*/
|
||||
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();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
RefPtr<IUnknown> unkInterceptor;
|
||||
IUnknown* interfaceForQILog = nullptr;
|
||||
|
||||
// (1) Check to see if we already have an existing interceptor for aIid.
|
||||
|
||||
{ // Scope for lock
|
||||
MutexAutoLock lock(mMutex);
|
||||
MapEntry* entry = Lookup(aIid);
|
||||
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);
|
||||
}
|
||||
|
||||
// (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);
|
||||
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
|
||||
MOZ_ASSERT(aIid != IID_IMarshal);
|
||||
|
||||
// (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));
|
||||
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,
|
||||
(void**)getter_AddRefs(interceptor));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = interceptor->RegisterSink(mEventSink);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// (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);
|
||||
if (entry && entry->mInterceptor) {
|
||||
unkInterceptor = entry->mInterceptor;
|
||||
} else {
|
||||
// We're inserting unkInterceptor into the map but we still want to hang
|
||||
// onto it locally so that we can QI it below.
|
||||
unkInterceptor->AddRef();
|
||||
// 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,
|
||||
unkInterceptor,
|
||||
rawTargetInterface));
|
||||
}
|
||||
}
|
||||
|
||||
return unkInterceptor->QueryInterface(aIid, 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*.
|
||||
MainThreadInvoker invoker;
|
||||
HRESULT hr;
|
||||
auto runOnMainThread = [&]() -> void {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
hr = mTarget->QueryInterface(aIid, aOutput);
|
||||
};
|
||||
if (!invoker.Invoke(NS_NewRunnableFunction(runOnMainThread))) {
|
||||
return E_FAIL;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
Interceptor::QueryInterface(REFIID riid, void** ppv)
|
||||
{
|
||||
return WeakReferenceSupport::QueryInterface(riid, ppv);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
ULONG
|
||||
Interceptor::Release()
|
||||
{
|
||||
return WeakReferenceSupport::Release();
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
125
ipc/mscom/Interceptor.h
Normal file
125
ipc/mscom/Interceptor.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/* -*- 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
|
||||
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/mscom/Ptr.h"
|
||||
#include "mozilla/mscom/WeakRef.h"
|
||||
#include "mozilla/RefPtr.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
|
||||
{
|
||||
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);
|
||||
|
||||
struct IInterceptor : public IUnknown
|
||||
{
|
||||
virtual STDMETHODIMP GetTargetForIID(REFIID aIid, InterceptorTargetPtr& aTarget) = 0;
|
||||
virtual STDMETHODIMP GetInterceptorForIID(REFIID aIid,
|
||||
void** aOutInterceptor) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* The COM interceptor is the core functionality in mscom that allows us to
|
||||
* redirect method calls to different threads. It emulates the vtable of a
|
||||
* target interface. When a call is made on this emulated vtable, the call is
|
||||
* packaged up into an instance of the ICallFrame interface which may be passed
|
||||
* to other contexts for execution.
|
||||
*
|
||||
* In order to accomplish this, COM itself provides the CoGetInterceptor
|
||||
* function, which instantiates an ICallInterceptor. Note, however, that
|
||||
* ICallInterceptor only works on a single interface; we need to be able to
|
||||
* 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 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;
|
||||
|
||||
// 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)
|
||||
: mIID(aIid)
|
||||
, mInterceptor(aInterceptor)
|
||||
, mTargetInterface(aTargetInterface)
|
||||
{}
|
||||
IID mIID;
|
||||
IUnknown* mInterceptor;
|
||||
IUnknown* mTargetInterface;
|
||||
};
|
||||
|
||||
private:
|
||||
Interceptor(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink);
|
||||
~Interceptor();
|
||||
MapEntry* Lookup(REFIID aIid);
|
||||
HRESULT QueryInterfaceTarget(REFIID aIid, void** aOutput);
|
||||
HRESULT ThreadSafeQueryInterface(REFIID aIid,
|
||||
IUnknown** aOutInterface) override;
|
||||
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;
|
||||
};
|
||||
|
||||
template <typename InterfaceT>
|
||||
inline HRESULT
|
||||
CreateInterceptor(STAUniquePtr<InterfaceT> aTargetInterface,
|
||||
IInterceptorSink* aEventSink,
|
||||
InterfaceT** aOutInterface)
|
||||
{
|
||||
if (!aTargetInterface || !aEventSink) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
REFIID iidTarget = __uuidof(aTargetInterface);
|
||||
|
||||
STAUniquePtr<IUnknown> targetUnknown(aTargetInterface.release());
|
||||
return Interceptor::Create(Move(targetUnknown), aEventSink, iidTarget,
|
||||
(void**)aOutInterface);
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_interceptor_h
|
||||
466
ipc/mscom/InterceptorLog.cpp
Normal file
466
ipc/mscom/InterceptorLog.cpp
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
/* -*- 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/InterceptorLog.h"
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/mscom/Registration.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXPCOMPrivate.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "prenv.h"
|
||||
|
||||
#include <callobj.h>
|
||||
|
||||
using mozilla::DebugOnly;
|
||||
using mozilla::mscom::ArrayData;
|
||||
using mozilla::mscom::FindArrayData;
|
||||
using mozilla::Mutex;
|
||||
using mozilla::MutexAutoLock;
|
||||
using mozilla::NewNonOwningRunnableMethod;
|
||||
using mozilla::services::GetObserverService;
|
||||
using mozilla::StaticAutoPtr;
|
||||
using mozilla::TimeDuration;
|
||||
using mozilla::TimeStamp;
|
||||
using mozilla::Unused;
|
||||
|
||||
namespace {
|
||||
|
||||
class ShutdownEvent final : public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
private:
|
||||
~ShutdownEvent() {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ShutdownEvent, nsIObserver)
|
||||
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
explicit Logger(const nsACString& aLeafBaseName);
|
||||
bool IsValid()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
return !!mThread;
|
||||
}
|
||||
void LogQI(HRESULT aResult, IUnknown* aTarget, REFIID aIid, IUnknown* aInterface);
|
||||
void LogEvent(ICallFrame* aCallFrame, IUnknown* aTargetInterface);
|
||||
nsresult Shutdown();
|
||||
|
||||
private:
|
||||
void OpenFile();
|
||||
void Flush();
|
||||
void CloseFile();
|
||||
void AssertRunningOnLoggerThread();
|
||||
bool VariantToString(const VARIANT& aVariant, nsACString& aOut, LONG aIndex = 0);
|
||||
static double GetElapsedTime();
|
||||
|
||||
nsCOMPtr<nsIFile> mLogFileName;
|
||||
nsCOMPtr<nsIOutputStream> mLogFile; // Only accessed by mThread
|
||||
Mutex mMutex; // Guards mThread and mEntries
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
nsTArray<nsCString> mEntries;
|
||||
};
|
||||
|
||||
Logger::Logger(const nsACString& aLeafBaseName)
|
||||
: mMutex("mozilla::com::InterceptorLog::Logger")
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsCOMPtr<nsIFile> logFileName;
|
||||
GeckoProcessType procType = XRE_GetProcessType();
|
||||
nsAutoCString leafName(aLeafBaseName);
|
||||
nsresult rv;
|
||||
if (procType == GeckoProcessType_Default) {
|
||||
leafName.AppendLiteral("-Parent-");
|
||||
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(logFileName));
|
||||
} else if (procType == GeckoProcessType_Content) {
|
||||
leafName.AppendLiteral("-Content-");
|
||||
#if defined(MOZ_CONTENT_SANDBOX)
|
||||
rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
|
||||
getter_AddRefs(logFileName));
|
||||
#else
|
||||
rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
|
||||
getter_AddRefs(logFileName));
|
||||
#endif // defined(MOZ_CONTENT_SANDBOX)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
DWORD pid = GetCurrentProcessId();
|
||||
leafName.AppendPrintf("%u.log", pid);
|
||||
// Using AppendNative here because Windows
|
||||
rv = logFileName->AppendNative(leafName);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
mLogFileName.swap(logFileName);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
|
||||
nsCOMPtr<nsIObserver> shutdownEvent = new ShutdownEvent();
|
||||
rv = obsSvc->AddObserver(shutdownEvent, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
|
||||
false);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> openRunnable(
|
||||
NewNonOwningRunnableMethod(this, &Logger::OpenFile));
|
||||
rv = NS_NewNamedThread("COM Intcpt Log", getter_AddRefs(mThread),
|
||||
openRunnable);
|
||||
if (NS_FAILED(rv)) {
|
||||
obsSvc->RemoveObserver(shutdownEvent, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Logger::AssertRunningOnLoggerThread()
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
nsCOMPtr<nsIThread> curThread;
|
||||
if (NS_FAILED(NS_GetCurrentThread(getter_AddRefs(curThread)))) {
|
||||
return;
|
||||
}
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(curThread == mThread);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Logger::OpenFile()
|
||||
{
|
||||
AssertRunningOnLoggerThread();
|
||||
MOZ_ASSERT(mLogFileName && !mLogFile);
|
||||
NS_NewLocalFileOutputStream(getter_AddRefs(mLogFile), mLogFileName,
|
||||
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
|
||||
PR_IRUSR | PR_IWUSR | PR_IRGRP);
|
||||
}
|
||||
|
||||
void
|
||||
Logger::CloseFile()
|
||||
{
|
||||
AssertRunningOnLoggerThread();
|
||||
MOZ_ASSERT(mLogFile);
|
||||
if (!mLogFile) {
|
||||
return;
|
||||
}
|
||||
Flush();
|
||||
mLogFile->Close();
|
||||
mLogFile = nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Logger::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsresult rv = mThread->Dispatch(NewNonOwningRunnableMethod(this,
|
||||
&Logger::CloseFile),
|
||||
NS_DISPATCH_NORMAL);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Dispatch failed");
|
||||
|
||||
rv = mThread->Shutdown();
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Shutdown failed");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
Logger::VariantToString(const VARIANT& aVariant, nsACString& aOut, LONG aIndex)
|
||||
{
|
||||
switch (aVariant.vt) {
|
||||
case VT_DISPATCH: {
|
||||
aOut.AppendPrintf("(IDispatch*) 0x%0p", aVariant.pdispVal);
|
||||
return true;
|
||||
}
|
||||
case VT_DISPATCH | VT_BYREF: {
|
||||
aOut.AppendPrintf("(IDispatch*) 0x%0p", (aVariant.ppdispVal)[aIndex]);
|
||||
return true;
|
||||
}
|
||||
case VT_UNKNOWN: {
|
||||
aOut.AppendPrintf("(IUnknown*) 0x%0p", aVariant.punkVal);
|
||||
return true;
|
||||
}
|
||||
case VT_UNKNOWN | VT_BYREF: {
|
||||
aOut.AppendPrintf("(IUnknown*) 0x%0p", (aVariant.ppunkVal)[aIndex]);
|
||||
return true;
|
||||
}
|
||||
case VT_VARIANT | VT_BYREF: {
|
||||
return VariantToString((aVariant.pvarVal)[aIndex], aOut);
|
||||
}
|
||||
case VT_I4 | VT_BYREF: {
|
||||
aOut.AppendPrintf("%d", aVariant.plVal[aIndex]);
|
||||
return true;
|
||||
}
|
||||
case VT_UI4 | VT_BYREF: {
|
||||
aOut.AppendPrintf("%u", aVariant.pulVal[aIndex]);
|
||||
return true;
|
||||
}
|
||||
case VT_I4: {
|
||||
aOut.AppendPrintf("%d", aVariant.lVal);
|
||||
return true;
|
||||
}
|
||||
case VT_UI4: {
|
||||
aOut.AppendPrintf("%u", aVariant.ulVal);
|
||||
return true;
|
||||
}
|
||||
case VT_EMPTY: {
|
||||
aOut.AppendLiteral("(empty VARIANT)");
|
||||
return true;
|
||||
}
|
||||
case VT_NULL: {
|
||||
aOut.AppendLiteral("(null VARIANT)");
|
||||
return true;
|
||||
}
|
||||
case VT_BSTR: {
|
||||
aOut.AppendPrintf("\"%S\"", aVariant.bstrVal);
|
||||
return true;
|
||||
}
|
||||
case VT_BSTR | VT_BYREF: {
|
||||
aOut.AppendPrintf("\"%S\"", *aVariant.pbstrVal);
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
aOut.AppendPrintf("(VariantToString failed, VARTYPE == 0x%04hx)",
|
||||
aVariant.vt);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ double
|
||||
Logger::GetElapsedTime()
|
||||
{
|
||||
TimeStamp ts = TimeStamp::Now();
|
||||
bool inconsistent;
|
||||
TimeDuration duration = ts - TimeStamp::ProcessCreation(inconsistent);
|
||||
return duration.ToMicroseconds();
|
||||
}
|
||||
|
||||
void
|
||||
Logger::LogQI(HRESULT aResult, IUnknown* aTarget, REFIID aIid, IUnknown* aInterface)
|
||||
{
|
||||
if (FAILED(aResult)) {
|
||||
return;
|
||||
}
|
||||
double elapsed = GetElapsedTime();
|
||||
|
||||
nsPrintfCString line("%fus\t0x%0p\tIUnknown::QueryInterface\t([in] ", elapsed,
|
||||
aTarget);
|
||||
|
||||
WCHAR buf[39] = {0};
|
||||
if (StringFromGUID2(aIid, buf, mozilla::ArrayLength(buf))) {
|
||||
line.AppendPrintf("%S", buf);
|
||||
} else {
|
||||
line.AppendLiteral("(IID Conversion Failed)");
|
||||
}
|
||||
line.AppendPrintf(", [out] 0x%p)\t0x%08X\n", aInterface, aResult);
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
mEntries.AppendElement(line);
|
||||
mThread->Dispatch(NewNonOwningRunnableMethod(this, &Logger::Flush),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
Logger::LogEvent(ICallFrame* aCallFrame, IUnknown* aTargetInterface)
|
||||
{
|
||||
// (1) Gather info about the call
|
||||
double elapsed = GetElapsedTime();
|
||||
|
||||
CALLFRAMEINFO callInfo;
|
||||
HRESULT hr = aCallFrame->GetInfo(&callInfo);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PWSTR interfaceName = nullptr;
|
||||
PWSTR methodName = nullptr;
|
||||
hr = aCallFrame->GetNames(&interfaceName, &methodName);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// (2) Serialize the call
|
||||
nsPrintfCString line("%fus\t0x%p\t%S::%S\t(", elapsed,
|
||||
aTargetInterface, interfaceName, methodName);
|
||||
|
||||
CoTaskMemFree(interfaceName);
|
||||
interfaceName = nullptr;
|
||||
CoTaskMemFree(methodName);
|
||||
methodName = nullptr;
|
||||
|
||||
// Check for supplemental array data
|
||||
const ArrayData* arrayData = FindArrayData(callInfo.iid, callInfo.iMethod);
|
||||
|
||||
for (ULONG paramIndex = 0; paramIndex < callInfo.cParams; ++paramIndex) {
|
||||
CALLFRAMEPARAMINFO paramInfo;
|
||||
hr = aCallFrame->GetParamInfo(paramIndex, ¶mInfo);
|
||||
if (SUCCEEDED(hr)) {
|
||||
line.AppendLiteral("[");
|
||||
if (paramInfo.fIn) {
|
||||
line.AppendLiteral("in");
|
||||
}
|
||||
if (paramInfo.fOut) {
|
||||
line.AppendLiteral("out");
|
||||
}
|
||||
line.AppendLiteral("] ");
|
||||
}
|
||||
VARIANT paramValue;
|
||||
hr = aCallFrame->GetParam(paramIndex, ¶mValue);
|
||||
if (SUCCEEDED(hr)) {
|
||||
if (arrayData && paramIndex == arrayData->mArrayParamIndex) {
|
||||
VARIANT lengthParam;
|
||||
hr = aCallFrame->GetParam(arrayData->mLengthParamIndex, &lengthParam);
|
||||
if (SUCCEEDED(hr)) {
|
||||
line.AppendLiteral("{ ");
|
||||
for (LONG i = 0; i < *lengthParam.plVal; ++i) {
|
||||
VariantToString(paramValue, line, i);
|
||||
if (i < *lengthParam.plVal - 1) {
|
||||
line.AppendLiteral(", ");
|
||||
}
|
||||
}
|
||||
line.AppendLiteral(" }");
|
||||
} else {
|
||||
line.AppendPrintf("(GetParam failed with HRESULT 0x%08X)", hr);
|
||||
}
|
||||
} else {
|
||||
VariantToString(paramValue, line);
|
||||
}
|
||||
} else {
|
||||
line.AppendPrintf("(GetParam failed with HRESULT 0x%08X)", hr);
|
||||
}
|
||||
if (paramIndex < callInfo.cParams - 1) {
|
||||
line.AppendLiteral(", ");
|
||||
}
|
||||
}
|
||||
line.AppendLiteral(")\t");
|
||||
|
||||
HRESULT callResult = aCallFrame->GetReturnValue();
|
||||
line.AppendPrintf("0x%08X\n", callResult);
|
||||
|
||||
// (3) Enqueue event for logging
|
||||
MutexAutoLock lock(mMutex);
|
||||
mEntries.AppendElement(line);
|
||||
mThread->Dispatch(NewNonOwningRunnableMethod(this, &Logger::Flush),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
Logger::Flush()
|
||||
{
|
||||
AssertRunningOnLoggerThread();
|
||||
MOZ_ASSERT(mLogFile);
|
||||
if (!mLogFile) {
|
||||
return;
|
||||
}
|
||||
nsTArray<nsCString> linesToWrite;
|
||||
{ // Scope for lock
|
||||
MutexAutoLock lock(mMutex);
|
||||
linesToWrite.SwapElements(mEntries);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0, len = linesToWrite.Length(); i < len; ++i) {
|
||||
uint32_t bytesWritten;
|
||||
nsCString& line = linesToWrite[i];
|
||||
nsresult rv = mLogFile->Write(line.get(), line.Length(), &bytesWritten);
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
}
|
||||
|
||||
StaticAutoPtr<Logger> sLogger;
|
||||
|
||||
NS_IMETHODIMP
|
||||
ShutdownEvent::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) {
|
||||
MOZ_ASSERT(false);
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
MOZ_ASSERT(sLogger);
|
||||
Unused << NS_WARN_IF(NS_FAILED(sLogger->Shutdown()));
|
||||
nsCOMPtr<nsIObserver> kungFuDeathGrip(this);
|
||||
nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
|
||||
obsSvc->RemoveObserver(this, aTopic);
|
||||
return NS_OK;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
static bool
|
||||
MaybeCreateLog(const char* aEnvVarName)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(XRE_IsContentProcess() || XRE_IsParentProcess());
|
||||
MOZ_ASSERT(!sLogger);
|
||||
const char* leafBaseName = PR_GetEnv(aEnvVarName);
|
||||
if (!leafBaseName) {
|
||||
return false;
|
||||
}
|
||||
nsDependentCString strLeafBaseName(leafBaseName);
|
||||
if (strLeafBaseName.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
sLogger = new Logger(strLeafBaseName);
|
||||
if (!sLogger->IsValid()) {
|
||||
sLogger = nullptr;
|
||||
return false;
|
||||
}
|
||||
ClearOnShutdown(&sLogger);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
/* static */ bool
|
||||
InterceptorLog::Init()
|
||||
{
|
||||
static const bool isEnabled = MaybeCreateLog("MOZ_MSCOM_LOG_BASENAME");
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
InterceptorLog::QI(HRESULT aResult, IUnknown* aTarget, REFIID aIid, IUnknown* aInterface)
|
||||
{
|
||||
if (!sLogger) {
|
||||
return;
|
||||
}
|
||||
sLogger->LogQI(aResult, aTarget, aIid, aInterface);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
InterceptorLog::Event(ICallFrame* aCallFrame, IUnknown* aTargetInterface)
|
||||
{
|
||||
if (!sLogger) {
|
||||
return;
|
||||
}
|
||||
sLogger->LogEvent(aCallFrame, aTargetInterface);
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
28
ipc/mscom/InterceptorLog.h
Normal file
28
ipc/mscom/InterceptorLog.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* -*- 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_InterceptorLog_h
|
||||
#define mozilla_mscom_InterceptorLog_h
|
||||
|
||||
struct ICallFrame;
|
||||
struct IUnknown;
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
class InterceptorLog
|
||||
{
|
||||
public:
|
||||
static bool Init();
|
||||
static void QI(HRESULT aResult, IUnknown* aTarget, REFIID aIid, IUnknown* aInterface);
|
||||
static void Event(ICallFrame* aCallFrame, IUnknown* aTarget);
|
||||
};
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_InterceptorLog_h
|
||||
|
||||
398
ipc/mscom/MainThreadHandoff.cpp
Normal file
398
ipc/mscom/MainThreadHandoff.cpp
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
/* -*- 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/Move.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;
|
||||
|
||||
namespace {
|
||||
|
||||
class HandoffRunnable : public mozilla::Runnable
|
||||
{
|
||||
public:
|
||||
explicit HandoffRunnable(ICallFrame* aCallFrame, IUnknown* aTargetInterface)
|
||||
: mCallFrame(aCallFrame)
|
||||
, mTargetInterface(aTargetInterface)
|
||||
, mResult(E_UNEXPECTED)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mResult = mCallFrame->Invoke(mTargetInterface);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
HRESULT GetResult() const
|
||||
{
|
||||
return mResult;
|
||||
}
|
||||
|
||||
private:
|
||||
ICallFrame* mCallFrame;
|
||||
IUnknown* mTargetInterface;
|
||||
HRESULT mResult;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
/* static */ HRESULT
|
||||
MainThreadHandoff::Create(IInterceptorSink** aOutput)
|
||||
{
|
||||
*aOutput = nullptr;
|
||||
MainThreadHandoff* handoff = new MainThreadHandoff();
|
||||
HRESULT hr = handoff->QueryInterface(IID_IInterceptorSink, (void**) aOutput);
|
||||
handoff->Release();
|
||||
return hr;
|
||||
}
|
||||
|
||||
MainThreadHandoff::MainThreadHandoff()
|
||||
: mRefCnt(1)
|
||||
{
|
||||
}
|
||||
|
||||
MainThreadHandoff::~MainThreadHandoff()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MainThreadHandoff::QueryInterface(REFIID riid, void** ppv)
|
||||
{
|
||||
IUnknown* punk = nullptr;
|
||||
if (!ppv) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (riid == IID_IUnknown || riid == IID_ICallFrameEvents ||
|
||||
riid == IID_IInterceptorSink) {
|
||||
punk = static_cast<IInterceptorSink*>(this);
|
||||
} else if (riid == IID_ICallFrameWalker) {
|
||||
punk = static_cast<ICallFrameWalker*>(this);
|
||||
}
|
||||
|
||||
*ppv = punk;
|
||||
if (!punk) {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
punk->AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ULONG
|
||||
MainThreadHandoff::AddRef()
|
||||
{
|
||||
return (ULONG) InterlockedIncrement((LONG*)&mRefCnt);
|
||||
}
|
||||
|
||||
ULONG
|
||||
MainThreadHandoff::Release()
|
||||
{
|
||||
ULONG newRefCnt = (ULONG) InterlockedDecrement((LONG*)&mRefCnt);
|
||||
if (newRefCnt == 0) {
|
||||
// It is possible for the last Release() call to happen off-main-thread.
|
||||
// If so, we need to dispatch an event to delete ourselves.
|
||||
if (NS_IsMainThread()) {
|
||||
delete this;
|
||||
} else {
|
||||
// We need to delete this object on the main thread, but we aren't on the
|
||||
// main thread right now, so we send a reference to ourselves to the main
|
||||
// thread to be re-released there.
|
||||
RefPtr<MainThreadHandoff> self = this;
|
||||
NS_ReleaseOnMainThread(self.forget());
|
||||
}
|
||||
}
|
||||
return newRefCnt;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MainThreadHandoff::OnCall(ICallFrame* aFrame)
|
||||
{
|
||||
// (1) Get info about the method call
|
||||
HRESULT hr;
|
||||
IID iid;
|
||||
ULONG method;
|
||||
hr = aFrame->GetIIDAndMethod(&iid, &method);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
RefPtr<IInterceptor> interceptor;
|
||||
hr = mInterceptor->Resolve(IID_IInterceptor,
|
||||
(void**)getter_AddRefs(interceptor));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
InterceptorTargetPtr targetInterface;
|
||||
hr = interceptor->GetTargetForIID(iid, targetInterface);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// (2) Execute the method call syncrhonously 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();
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// (3) Log *before* wrapping outputs so that the log will contain pointers to
|
||||
// the true target interface, not the wrapped ones.
|
||||
InterceptorLog::Event(aFrame, targetInterface.get());
|
||||
|
||||
// (4) Scan the function call for outparams that contain interface pointers.
|
||||
// Those will need to be wrapped with MainThreadHandoff so that they too will
|
||||
// be exeuted on the main thread.
|
||||
|
||||
hr = aFrame->GetReturnValue();
|
||||
if (FAILED(hr)) {
|
||||
// If the call resulted in an error then there's not going to be anything
|
||||
// that needs to be wrapped.
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// (5) Unfortunately ICallFrame::WalkFrame does not correctly handle array
|
||||
// outparams. Instead, we find out whether anybody has called
|
||||
// mscom::RegisterArrayData to supply array parameter information and use it
|
||||
// if available. This is a terrible hack, but it works for the short term. In
|
||||
// the longer term we want to be able to use COM proxy/stub metadata to
|
||||
// resolve array information for us.
|
||||
const ArrayData* arrayData = FindArrayData(iid, method);
|
||||
if (arrayData) {
|
||||
hr = FixArrayElements(aFrame, *arrayData);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
} else {
|
||||
// (6) Scan the outputs looking for any outparam interfaces that need wrapping.
|
||||
// NB: WalkFrame does not correctly handle array outparams. It processes the
|
||||
// first element of an array but not the remaining elements (if any).
|
||||
hr = aFrame->WalkFrame(CALLFRAME_WALK_OUT, this);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static PVOID
|
||||
ResolveArrayPtr(VARIANT& aVariant)
|
||||
{
|
||||
if (!(aVariant.vt & VT_BYREF)) {
|
||||
return nullptr;
|
||||
}
|
||||
return aVariant.byref;
|
||||
}
|
||||
|
||||
static PVOID*
|
||||
ResolveInterfacePtr(PVOID aArrayPtr, VARTYPE aVartype, LONG aIndex)
|
||||
{
|
||||
if (aVartype != (VT_VARIANT | VT_BYREF)) {
|
||||
IUnknown** ifaceArray = reinterpret_cast<IUnknown**>(aArrayPtr);
|
||||
return reinterpret_cast<PVOID*>(&ifaceArray[aIndex]);
|
||||
}
|
||||
VARIANT* variantArray = reinterpret_cast<VARIANT*>(aArrayPtr);
|
||||
VARIANT& element = variantArray[aIndex];
|
||||
return &element.byref;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MainThreadHandoff::FixArrayElements(ICallFrame* aFrame,
|
||||
const ArrayData& aArrayData)
|
||||
{
|
||||
// Extract the array length
|
||||
VARIANT paramVal;
|
||||
VariantInit(¶mVal);
|
||||
HRESULT hr = aFrame->GetParam(aArrayData.mLengthParamIndex, ¶mVal);
|
||||
MOZ_ASSERT(SUCCEEDED(hr) &&
|
||||
(paramVal.vt == (VT_I4 | VT_BYREF) ||
|
||||
paramVal.vt == (VT_UI4 | VT_BYREF)));
|
||||
if (FAILED(hr) || (paramVal.vt != (VT_I4 | VT_BYREF) &&
|
||||
paramVal.vt != (VT_UI4 | VT_BYREF))) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
const LONG arrayLength = *(paramVal.plVal);
|
||||
if (!arrayLength) {
|
||||
// Nothing to do
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Extract the array parameter
|
||||
VariantInit(¶mVal);
|
||||
PVOID arrayPtr = nullptr;
|
||||
hr = aFrame->GetParam(aArrayData.mArrayParamIndex, ¶mVal);
|
||||
if (hr == DISP_E_BADVARTYPE) {
|
||||
// ICallFrame::GetParam is not able to coerce the param into a VARIANT.
|
||||
// That's ok, we can try to do it ourselves.
|
||||
CALLFRAMEPARAMINFO paramInfo;
|
||||
hr = aFrame->GetParamInfo(aArrayData.mArrayParamIndex, ¶mInfo);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
PVOID stackBase = aFrame->GetStackLocation();
|
||||
if (aArrayData.mFlag == ArrayData::Flag::eAllocatedByServer) {
|
||||
// In order for the server to allocate the array's buffer and store it in
|
||||
// an outparam, the parameter must be typed as Type***. Since the base
|
||||
// of the array is Type*, we must dereference twice.
|
||||
arrayPtr = **reinterpret_cast<PVOID**>(reinterpret_cast<PBYTE>(stackBase) +
|
||||
paramInfo.stackOffset);
|
||||
} else {
|
||||
// We dereference because we need to obtain the value of a parameter
|
||||
// from a stack offset. This pointer is the base of the array.
|
||||
arrayPtr = *reinterpret_cast<PVOID*>(reinterpret_cast<PBYTE>(stackBase) +
|
||||
paramInfo.stackOffset);
|
||||
}
|
||||
} else if (FAILED(hr)) {
|
||||
return hr;
|
||||
} else {
|
||||
arrayPtr = ResolveArrayPtr(paramVal);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(arrayPtr);
|
||||
if (!arrayPtr) {
|
||||
return DISP_E_BADVARTYPE;
|
||||
}
|
||||
|
||||
// We walk the elements of the array and invoke OnWalkInterface to wrap each
|
||||
// one, just as ICallFrame::WalkFrame would do.
|
||||
for (LONG index = 0; index < arrayLength; ++index) {
|
||||
hr = OnWalkInterface(aArrayData.mArrayParamIid,
|
||||
ResolveInterfacePtr(arrayPtr, paramVal.vt, index),
|
||||
FALSE, TRUE);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MainThreadHandoff::SetInterceptor(IWeakReference* aInterceptor)
|
||||
{
|
||||
mInterceptor = aInterceptor;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MainThreadHandoff::OnWalkInterface(REFIID aIid, PVOID* aInterface,
|
||||
BOOL aIsInParam, BOOL aIsOutParam)
|
||||
{
|
||||
MOZ_ASSERT(aInterface && aIsOutParam);
|
||||
if (!aInterface || !aIsOutParam) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Adopt aInterface for the time being. We can't touch its refcount off
|
||||
// the main thread, so we'll use STAUniquePtr so that we can safely
|
||||
// Release() it if necessary.
|
||||
STAUniquePtr<IUnknown> origInterface(static_cast<IUnknown*>(*aInterface));
|
||||
*aInterface = nullptr;
|
||||
|
||||
if (!origInterface) {
|
||||
// Nothing to wrap.
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// First make sure that aInterface isn't a proxy - we don't want to wrap
|
||||
// those.
|
||||
if (IsProxy(origInterface.get())) {
|
||||
*aInterface = origInterface.release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
RefPtr<IInterceptor> interceptor;
|
||||
HRESULT hr = mInterceptor->Resolve(IID_IInterceptor,
|
||||
(void**) getter_AddRefs(interceptor));
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Now make sure that origInterface isn't referring to the same IUnknown
|
||||
// as an interface that we are already managing. We can determine this by
|
||||
// querying (NOT casting!) both objects for IUnknown and then comparing the
|
||||
// resulting pointers.
|
||||
InterceptorTargetPtr existingTarget;
|
||||
hr = interceptor->GetTargetForIID(aIid, existingTarget);
|
||||
if (SUCCEEDED(hr)) {
|
||||
bool areIUnknownsEqual = false;
|
||||
|
||||
// This check must be done on the main thread
|
||||
auto checkFn = [&existingTarget, &origInterface, &areIUnknownsEqual]() -> void {
|
||||
RefPtr<IUnknown> unkExisting;
|
||||
HRESULT hrExisting =
|
||||
existingTarget->QueryInterface(IID_IUnknown,
|
||||
(void**)getter_AddRefs(unkExisting));
|
||||
RefPtr<IUnknown> unkNew;
|
||||
HRESULT hrNew =
|
||||
origInterface->QueryInterface(IID_IUnknown,
|
||||
(void**)getter_AddRefs(unkNew));
|
||||
areIUnknownsEqual = SUCCEEDED(hrExisting) && SUCCEEDED(hrNew) &&
|
||||
unkExisting == unkNew;
|
||||
};
|
||||
|
||||
MainThreadInvoker invoker;
|
||||
if (invoker.Invoke(NS_NewRunnableFunction(checkFn)) && areIUnknownsEqual) {
|
||||
// The existing interface and the new interface both belong to the same
|
||||
// target object. Let's just use the existing one.
|
||||
void* intercepted = nullptr;
|
||||
hr = interceptor->GetInterceptorForIID(aIid, &intercepted);
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
*aInterface = intercepted;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Now create a new MainThreadHandoff wrapper...
|
||||
RefPtr<IInterceptorSink> handoff;
|
||||
hr = MainThreadHandoff::Create(getter_AddRefs(handoff));
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
RefPtr<IUnknown> wrapped;
|
||||
hr = Interceptor::Create(Move(origInterface), handoff, aIid,
|
||||
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));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
71
ipc/mscom/MainThreadHandoff.h
Normal file
71
ipc/mscom/MainThreadHandoff.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/* -*- 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_MainThreadHandoff_h
|
||||
#define mozilla_mscom_MainThreadHandoff_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/mscom/Interceptor.h"
|
||||
#include "mozilla/mscom/MainThreadInvoker.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
struct ArrayData;
|
||||
|
||||
class MainThreadHandoff final : public IInterceptorSink
|
||||
, public ICallFrameWalker
|
||||
{
|
||||
public:
|
||||
static HRESULT Create(IInterceptorSink** aOutput);
|
||||
|
||||
template <typename Interface>
|
||||
static HRESULT WrapInterface(STAUniquePtr<Interface> aTargetInterface,
|
||||
Interface** aOutInterface)
|
||||
{
|
||||
MOZ_ASSERT(!IsProxy(aTargetInterface.get()));
|
||||
RefPtr<IInterceptorSink> handoff;
|
||||
HRESULT hr = MainThreadHandoff::Create(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;
|
||||
|
||||
// ICallFrameWalker
|
||||
STDMETHODIMP OnWalkInterface(REFIID aIid, PVOID* aInterface, BOOL aIsInParam,
|
||||
BOOL aIsOutParam) override;
|
||||
|
||||
private:
|
||||
MainThreadHandoff();
|
||||
~MainThreadHandoff();
|
||||
HRESULT FixArrayElements(ICallFrame* aFrame,
|
||||
const ArrayData& aArrayData);
|
||||
|
||||
private:
|
||||
ULONG mRefCnt;
|
||||
RefPtr<IWeakReference> mInterceptor;
|
||||
};
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_MainThreadHandoff_h
|
||||
180
ipc/mscom/MainThreadInvoker.cpp
Normal file
180
ipc/mscom/MainThreadInvoker.cpp
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/* -*- 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/MainThreadInvoker.h"
|
||||
|
||||
#include "GeckoProfiler.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/HangMonitor.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsSystemInfo.h"
|
||||
#include "private/prpriv.h" // For PR_GetThreadID
|
||||
#include "WinUtils.h"
|
||||
|
||||
// This gives us compiler intrinsics for the x86 PAUSE instruction
|
||||
#if defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_mm_pause)
|
||||
#define CPU_PAUSE() _mm_pause()
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define CPU_PAUSE() __builtin_ia32_pause()
|
||||
#endif
|
||||
|
||||
static bool sIsMulticore;
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* SyncRunnable implements different code paths depending on whether or not
|
||||
* we are running on a multiprocessor system. In the multiprocessor case, we
|
||||
* leave the thread in a spin loop while waiting for the main thread to execute
|
||||
* our runnable. Since spinning is pointless in the uniprocessor case, we block
|
||||
* on an event that is set by the main thread once it has finished the runnable.
|
||||
*/
|
||||
class MOZ_RAII SyncRunnable
|
||||
{
|
||||
public:
|
||||
explicit SyncRunnable(already_AddRefed<nsIRunnable>&& aRunnable)
|
||||
: mDoneEvent(sIsMulticore ? nullptr :
|
||||
::CreateEventW(nullptr, FALSE, FALSE, nullptr))
|
||||
, mDone(false)
|
||||
, mRunnable(aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(sIsMulticore || mDoneEvent);
|
||||
MOZ_ASSERT(mRunnable);
|
||||
}
|
||||
|
||||
~SyncRunnable()
|
||||
{
|
||||
if (mDoneEvent) {
|
||||
::CloseHandle(mDoneEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void Run()
|
||||
{
|
||||
mRunnable->Run();
|
||||
|
||||
if (mDoneEvent) {
|
||||
::SetEvent(mDoneEvent);
|
||||
} else {
|
||||
mDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitUntilComplete()
|
||||
{
|
||||
if (mDoneEvent) {
|
||||
HANDLE handles[] = {mDoneEvent,
|
||||
mozilla::mscom::MainThreadInvoker::GetTargetThread()};
|
||||
DWORD waitResult = ::WaitForMultipleObjects(mozilla::ArrayLength(handles),
|
||||
handles, FALSE, INFINITE);
|
||||
return waitResult == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
while (!mDone) {
|
||||
// The PAUSE instruction is a hint to the CPU that we're doing a spin
|
||||
// loop. It is a no-op on older processors that don't support it, so
|
||||
// it is safe to use here without any CPUID checks.
|
||||
CPU_PAUSE();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE mDoneEvent;
|
||||
mozilla::Atomic<bool> mDone;
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
HANDLE MainThreadInvoker::sMainThread = nullptr;
|
||||
|
||||
/* static */ bool
|
||||
MainThreadInvoker::InitStatics()
|
||||
{
|
||||
nsCOMPtr<nsIThread> mainThread;
|
||||
nsresult rv = ::NS_GetMainThread(getter_AddRefs(mainThread));
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PRThread* mainPrThread = nullptr;
|
||||
rv = mainThread->GetPRThread(&mainPrThread);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PRUint32 tid = ::PR_GetThreadID(mainPrThread);
|
||||
sMainThread = ::OpenThread(SYNCHRONIZE | THREAD_SET_CONTEXT, FALSE, tid);
|
||||
|
||||
nsCOMPtr<nsIPropertyBag2> infoService = do_GetService(NS_SYSTEMINFO_CONTRACTID);
|
||||
if (infoService) {
|
||||
uint32_t cpuCount;
|
||||
nsresult rv = infoService->GetPropertyAsUint32(NS_LITERAL_STRING("cpucount"),
|
||||
&cpuCount);
|
||||
sIsMulticore = NS_SUCCEEDED(rv) && cpuCount > 1;
|
||||
}
|
||||
|
||||
return !!sMainThread;
|
||||
}
|
||||
|
||||
MainThreadInvoker::MainThreadInvoker()
|
||||
{
|
||||
static const bool gotStatics = InitStatics();
|
||||
MOZ_ASSERT(gotStatics);
|
||||
}
|
||||
|
||||
bool
|
||||
MainThreadInvoker::Invoke(already_AddRefed<nsIRunnable>&& aRunnable)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable(Move(aRunnable));
|
||||
if (!runnable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
runnable->Run();
|
||||
return true;
|
||||
}
|
||||
|
||||
SyncRunnable syncRunnable(runnable.forget());
|
||||
|
||||
if (!::QueueUserAPC(&MainThreadAPC, sMainThread,
|
||||
reinterpret_cast<UINT_PTR>(&syncRunnable))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We should ensure a call to NtTestAlert() is made on the main thread so
|
||||
// that the main thread will check for APCs during event processing. If we
|
||||
// omit this then the main thread will not check its APC queue until it is
|
||||
// idle.
|
||||
widget::WinUtils::SetAPCPending();
|
||||
|
||||
return syncRunnable.WaitUntilComplete();
|
||||
}
|
||||
|
||||
/* static */ VOID CALLBACK
|
||||
MainThreadInvoker::MainThreadAPC(ULONG_PTR aParam)
|
||||
{
|
||||
GeckoProfilerWakeRAII wakeProfiler;
|
||||
mozilla::HangMonitor::NotifyActivity(mozilla::HangMonitor::kGeneralActivity);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
auto runnable = reinterpret_cast<SyncRunnable*>(aParam);
|
||||
runnable->Run();
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
38
ipc/mscom/MainThreadInvoker.h
Normal file
38
ipc/mscom/MainThreadInvoker.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* -*- 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_MainThreadInvoker_h
|
||||
#define mozilla_mscom_MainThreadInvoker_h
|
||||
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
class nsIRunnable;
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
class MainThreadInvoker
|
||||
{
|
||||
public:
|
||||
MainThreadInvoker();
|
||||
|
||||
bool Invoke(already_AddRefed<nsIRunnable>&& aRunnable);
|
||||
static HANDLE GetTargetThread() { return sMainThread; }
|
||||
|
||||
private:
|
||||
static bool InitStatics();
|
||||
static VOID CALLBACK MainThreadAPC(ULONG_PTR aParam);
|
||||
|
||||
static HANDLE sMainThread;
|
||||
};
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_MainThreadInvoker_h
|
||||
180
ipc/mscom/MainThreadRuntime.cpp
Normal file
180
ipc/mscom/MainThreadRuntime.cpp
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/* -*- 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/MainThreadRuntime.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
||||
#include <accctrl.h>
|
||||
#include <aclapi.h>
|
||||
#include <objbase.h>
|
||||
#include <objidl.h>
|
||||
|
||||
namespace {
|
||||
|
||||
struct LocalFreeDeleter
|
||||
{
|
||||
void operator()(void* aPtr)
|
||||
{
|
||||
::LocalFree(aPtr);
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
MainThreadRuntime::MainThreadRuntime()
|
||||
: mInitResult(E_UNEXPECTED)
|
||||
{
|
||||
// We must be the outermost COM initialization on this thread. The COM runtime
|
||||
// cannot be configured once we start manipulating objects
|
||||
MOZ_ASSERT(mStaRegion.IsValidOutermost());
|
||||
if (NS_WARN_IF(!mStaRegion.IsValidOutermost())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Windows XP doesn't support setting of the COM exception policy, so we'll
|
||||
// just stop here in that case.
|
||||
if (!IsVistaOrLater()) {
|
||||
mInitResult = S_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
// We are required to initialize security in order to configure global options.
|
||||
mInitResult = InitializeSecurity();
|
||||
MOZ_ASSERT(SUCCEEDED(mInitResult));
|
||||
if (FAILED(mInitResult)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<IGlobalOptions> globalOpts;
|
||||
mInitResult = ::CoCreateInstance(CLSID_GlobalOptions, nullptr,
|
||||
CLSCTX_INPROC_SERVER, IID_IGlobalOptions,
|
||||
(void**)getter_AddRefs(globalOpts));
|
||||
MOZ_ASSERT(SUCCEEDED(mInitResult));
|
||||
if (FAILED(mInitResult)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Windows 7 has a policy that is even more strict. We should use that one
|
||||
// whenever possible.
|
||||
ULONG_PTR exceptionSetting = IsWin7OrLater() ?
|
||||
COMGLB_EXCEPTION_DONOT_HANDLE_ANY :
|
||||
COMGLB_EXCEPTION_DONOT_HANDLE;
|
||||
mInitResult = globalOpts->Set(COMGLB_EXCEPTION_HANDLING, exceptionSetting);
|
||||
MOZ_ASSERT(SUCCEEDED(mInitResult));
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MainThreadRuntime::InitializeSecurity()
|
||||
{
|
||||
HANDLE rawToken = nullptr;
|
||||
BOOL ok = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &rawToken);
|
||||
if (!ok) {
|
||||
return HRESULT_FROM_WIN32(::GetLastError());
|
||||
}
|
||||
nsAutoHandle token(rawToken);
|
||||
|
||||
DWORD len = 0;
|
||||
ok = ::GetTokenInformation(token, TokenUser, nullptr, len, &len);
|
||||
DWORD win32Error = ::GetLastError();
|
||||
if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
|
||||
return HRESULT_FROM_WIN32(win32Error);
|
||||
}
|
||||
|
||||
auto tokenUserBuf = MakeUnique<BYTE[]>(len);
|
||||
TOKEN_USER& tokenUser = *reinterpret_cast<TOKEN_USER*>(tokenUserBuf.get());
|
||||
ok = ::GetTokenInformation(token, TokenUser, tokenUserBuf.get(), len, &len);
|
||||
if (!ok) {
|
||||
return HRESULT_FROM_WIN32(::GetLastError());
|
||||
}
|
||||
|
||||
len = 0;
|
||||
ok = ::GetTokenInformation(token, TokenPrimaryGroup, nullptr, len, &len);
|
||||
win32Error = ::GetLastError();
|
||||
if (!ok && win32Error != ERROR_INSUFFICIENT_BUFFER) {
|
||||
return HRESULT_FROM_WIN32(win32Error);
|
||||
}
|
||||
|
||||
auto tokenPrimaryGroupBuf = MakeUnique<BYTE[]>(len);
|
||||
TOKEN_PRIMARY_GROUP& tokenPrimaryGroup =
|
||||
*reinterpret_cast<TOKEN_PRIMARY_GROUP*>(tokenPrimaryGroupBuf.get());
|
||||
ok = ::GetTokenInformation(token, TokenPrimaryGroup, tokenPrimaryGroupBuf.get(),
|
||||
len, &len);
|
||||
if (!ok) {
|
||||
return HRESULT_FROM_WIN32(::GetLastError());
|
||||
}
|
||||
|
||||
SECURITY_DESCRIPTOR sd;
|
||||
if (!::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
|
||||
return HRESULT_FROM_WIN32(::GetLastError());
|
||||
}
|
||||
|
||||
BYTE systemSid[SECURITY_MAX_SID_SIZE];
|
||||
DWORD systemSidSize = sizeof(systemSid);
|
||||
if (!::CreateWellKnownSid(WinLocalSystemSid, nullptr, systemSid,
|
||||
&systemSidSize)) {
|
||||
return HRESULT_FROM_WIN32(::GetLastError());
|
||||
}
|
||||
|
||||
BYTE adminSid[SECURITY_MAX_SID_SIZE];
|
||||
DWORD adminSidSize = sizeof(adminSid);
|
||||
if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, nullptr, adminSid,
|
||||
&adminSidSize)) {
|
||||
return HRESULT_FROM_WIN32(::GetLastError());
|
||||
}
|
||||
|
||||
// Grant access to SYSTEM, Administrators, and the user.
|
||||
EXPLICIT_ACCESS entries[] = {
|
||||
{COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
|
||||
{nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
|
||||
reinterpret_cast<LPWSTR>(systemSid)}},
|
||||
{COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
|
||||
{nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP,
|
||||
reinterpret_cast<LPWSTR>(adminSid)}},
|
||||
{COM_RIGHTS_EXECUTE, GRANT_ACCESS, NO_INHERITANCE,
|
||||
{nullptr, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
|
||||
reinterpret_cast<LPWSTR>(tokenUser.User.Sid)}}
|
||||
};
|
||||
|
||||
PACL rawDacl = nullptr;
|
||||
win32Error = ::SetEntriesInAcl(ArrayLength(entries), entries, nullptr,
|
||||
&rawDacl);
|
||||
if (win32Error != ERROR_SUCCESS) {
|
||||
return HRESULT_FROM_WIN32(win32Error);
|
||||
}
|
||||
|
||||
UniquePtr<ACL, LocalFreeDeleter> dacl(rawDacl);
|
||||
|
||||
if (!::SetSecurityDescriptorDacl(&sd, TRUE, dacl.get(), FALSE)) {
|
||||
return HRESULT_FROM_WIN32(::GetLastError());
|
||||
}
|
||||
|
||||
if (!::SetSecurityDescriptorOwner(&sd, tokenUser.User.Sid, FALSE)) {
|
||||
return HRESULT_FROM_WIN32(::GetLastError());
|
||||
}
|
||||
|
||||
if (!::SetSecurityDescriptorGroup(&sd, tokenPrimaryGroup.PrimaryGroup, FALSE)) {
|
||||
return HRESULT_FROM_WIN32(::GetLastError());
|
||||
}
|
||||
|
||||
return ::CoInitializeSecurity(&sd, -1, nullptr, nullptr,
|
||||
RPC_C_AUTHN_LEVEL_DEFAULT,
|
||||
RPC_C_IMP_LEVEL_IDENTIFY, nullptr, EOAC_NONE,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
42
ipc/mscom/MainThreadRuntime.h
Normal file
42
ipc/mscom/MainThreadRuntime.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* -*- 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_MainThreadRuntime_h
|
||||
#define mozilla_mscom_MainThreadRuntime_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/mscom/COMApartmentRegion.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
class MOZ_NON_TEMPORARY_CLASS MainThreadRuntime
|
||||
{
|
||||
public:
|
||||
MainThreadRuntime();
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return mStaRegion.IsValidOutermost() && SUCCEEDED(mInitResult);
|
||||
}
|
||||
|
||||
MainThreadRuntime(MainThreadRuntime&) = delete;
|
||||
MainThreadRuntime(MainThreadRuntime&&) = delete;
|
||||
MainThreadRuntime& operator=(MainThreadRuntime&) = delete;
|
||||
MainThreadRuntime& operator=(MainThreadRuntime&&) = delete;
|
||||
|
||||
private:
|
||||
HRESULT InitializeSecurity();
|
||||
|
||||
STARegion mStaRegion;
|
||||
HRESULT mInitResult;
|
||||
};
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_MainThreadRuntime_h
|
||||
|
||||
202
ipc/mscom/ProxyStream.cpp
Normal file
202
ipc/mscom/ProxyStream.cpp
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* -*- 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 "DynamicallyLinkedFunctionPtr.h"
|
||||
#include "mozilla/mscom/EnsureMTA.h"
|
||||
#include "mozilla/mscom/ProxyStream.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <objbase.h>
|
||||
#include <shlwapi.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
ProxyStream::ProxyStream()
|
||||
: mGlobalLockedBuf(nullptr)
|
||||
, mHGlobal(nullptr)
|
||||
, mBufSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
// GetBuffer() fails with this variant, but that's okay because we're just
|
||||
// reconstructing the stream from a buffer anyway.
|
||||
ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
|
||||
: mStream(InitStream(aInitBuf, static_cast<const UINT>(aInitBufSize)))
|
||||
, mGlobalLockedBuf(nullptr)
|
||||
, mHGlobal(nullptr)
|
||||
, mBufSize(aInitBufSize)
|
||||
{
|
||||
if (!aInitBufSize) {
|
||||
// We marshaled a nullptr. Nothing else to do here.
|
||||
return;
|
||||
}
|
||||
// NB: We can't check for a null mStream until after we have checked for
|
||||
// the zero aInitBufSize above. This is because InitStream will also fail
|
||||
// in that case, even though marshaling a nullptr is allowable.
|
||||
MOZ_ASSERT(mStream);
|
||||
if (!mStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to convert to an interface here otherwise we mess up const
|
||||
// correctness with IPDL. We'll request an IUnknown and then QI the
|
||||
// actual interface later.
|
||||
|
||||
auto marshalFn = [&]() -> void
|
||||
{
|
||||
IUnknown* rawUnmarshaledProxy = nullptr;
|
||||
// OK to forget mStream when calling into this function because the stream
|
||||
// gets released even if the unmarshaling part fails.
|
||||
DebugOnly<HRESULT> hr =
|
||||
::CoGetInterfaceAndReleaseStream(mStream.forget().take(), IID_IUnknown,
|
||||
(void**)&rawUnmarshaledProxy);
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
mUnmarshaledProxy.reset(rawUnmarshaledProxy);
|
||||
};
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
// We'll marshal this stuff directly using the current thread, therefore its
|
||||
// proxy will reside in the same apartment as the current thread.
|
||||
marshalFn();
|
||||
} else {
|
||||
// When marshaling in child processes, we want to force the MTA.
|
||||
EnsureMTA mta(marshalFn);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<IStream>
|
||||
ProxyStream::InitStream(const BYTE* aInitBuf, const UINT aInitBufSize)
|
||||
{
|
||||
// Need to link to this as ordinal 12 for Windows XP
|
||||
static DynamicallyLinkedFunctionPtr<decltype(&::SHCreateMemStream)>
|
||||
pSHCreateMemStream(L"shlwapi.dll", reinterpret_cast<const char*>(12));
|
||||
if (!pSHCreateMemStream) {
|
||||
return nullptr;
|
||||
}
|
||||
return already_AddRefed<IStream>(pSHCreateMemStream(aInitBuf, aInitBufSize));
|
||||
}
|
||||
|
||||
ProxyStream::ProxyStream(ProxyStream&& aOther)
|
||||
{
|
||||
*this = mozilla::Move(aOther);
|
||||
}
|
||||
|
||||
ProxyStream&
|
||||
ProxyStream::operator=(ProxyStream&& aOther)
|
||||
{
|
||||
mStream = mozilla::Move(aOther.mStream);
|
||||
mGlobalLockedBuf = aOther.mGlobalLockedBuf;
|
||||
aOther.mGlobalLockedBuf = nullptr;
|
||||
mHGlobal = aOther.mHGlobal;
|
||||
aOther.mHGlobal = nullptr;
|
||||
mBufSize = aOther.mBufSize;
|
||||
aOther.mBufSize = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ProxyStream::~ProxyStream()
|
||||
{
|
||||
if (mHGlobal && mGlobalLockedBuf) {
|
||||
DebugOnly<BOOL> result = ::GlobalUnlock(mHGlobal);
|
||||
MOZ_ASSERT(!result && ::GetLastError() == NO_ERROR);
|
||||
// ::GlobalFree() is called implicitly when mStream is released
|
||||
}
|
||||
}
|
||||
|
||||
const BYTE*
|
||||
ProxyStream::GetBuffer(int& aReturnedBufSize) const
|
||||
{
|
||||
aReturnedBufSize = 0;
|
||||
if (!mStream) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!mGlobalLockedBuf) {
|
||||
return nullptr;
|
||||
}
|
||||
aReturnedBufSize = mBufSize;
|
||||
return mGlobalLockedBuf;
|
||||
}
|
||||
|
||||
bool
|
||||
ProxyStream::GetInterface(REFIID aIID, void** aOutInterface) const
|
||||
{
|
||||
// We should not have a locked buffer on this side
|
||||
MOZ_ASSERT(!mGlobalLockedBuf);
|
||||
MOZ_ASSERT(aOutInterface);
|
||||
|
||||
if (!aOutInterface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mUnmarshaledProxy) {
|
||||
*aOutInterface = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
HRESULT hr = E_UNEXPECTED;
|
||||
auto qiFn = [&]() -> void
|
||||
{
|
||||
hr = mUnmarshaledProxy->QueryInterface(aIID, aOutInterface);
|
||||
};
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
qiFn();
|
||||
} else {
|
||||
// mUnmarshaledProxy requires that we execute this in the MTA
|
||||
EnsureMTA mta(qiFn);
|
||||
}
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
ProxyStream::ProxyStream(REFIID aIID, IUnknown* aObject)
|
||||
: mGlobalLockedBuf(nullptr)
|
||||
, mHGlobal(nullptr)
|
||||
, mBufSize(0)
|
||||
{
|
||||
RefPtr<IStream> stream;
|
||||
HGLOBAL hglobal = NULL;
|
||||
|
||||
auto marshalFn = [&]() -> void
|
||||
{
|
||||
HRESULT hr = ::CreateStreamOnHGlobal(nullptr, TRUE, getter_AddRefs(stream));
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = ::CoMarshalInterface(stream, aIID, aObject, MSHCTX_LOCAL, nullptr,
|
||||
MSHLFLAGS_NORMAL);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = ::GetHGlobalFromStream(stream, &hglobal);
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
};
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
// We'll marshal this stuff directly using the current thread, therefore its
|
||||
// stub will reside in the same apartment as the current thread.
|
||||
marshalFn();
|
||||
} else {
|
||||
// When marshaling in child processes, we want to force the MTA.
|
||||
EnsureMTA mta(marshalFn);
|
||||
}
|
||||
|
||||
mStream = mozilla::Move(stream);
|
||||
if (hglobal) {
|
||||
mGlobalLockedBuf = reinterpret_cast<BYTE*>(::GlobalLock(hglobal));
|
||||
mHGlobal = hglobal;
|
||||
mBufSize = static_cast<int>(::GlobalSize(hglobal));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
64
ipc/mscom/ProxyStream.h
Normal file
64
ipc/mscom/ProxyStream.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* -*- 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_ProxyStream_h
|
||||
#define mozilla_mscom_ProxyStream_h
|
||||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
|
||||
#include "mozilla/mscom/Ptr.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
class ProxyStream
|
||||
{
|
||||
public:
|
||||
ProxyStream();
|
||||
ProxyStream(REFIID aIID, IUnknown* aObject);
|
||||
ProxyStream(const BYTE* aInitBuf, const int aInitBufSize);
|
||||
|
||||
~ProxyStream();
|
||||
|
||||
// Not copyable because this would mess up the COM marshaling.
|
||||
ProxyStream(const ProxyStream& aOther) = delete;
|
||||
ProxyStream& operator=(const ProxyStream& aOther) = delete;
|
||||
|
||||
ProxyStream(ProxyStream&& aOther);
|
||||
ProxyStream& operator=(ProxyStream&& aOther);
|
||||
|
||||
inline bool IsValid() const
|
||||
{
|
||||
// This check must be exclusive OR
|
||||
return (mStream && !mUnmarshaledProxy) || (mUnmarshaledProxy && !mStream);
|
||||
}
|
||||
|
||||
bool GetInterface(REFIID aIID, void** aOutInterface) const;
|
||||
const BYTE* GetBuffer(int& aReturnedBufSize) const;
|
||||
|
||||
bool operator==(const ProxyStream& aOther) const
|
||||
{
|
||||
return this == &aOther;
|
||||
}
|
||||
|
||||
private:
|
||||
already_AddRefed<IStream> InitStream(const BYTE* aInitBuf,
|
||||
const UINT aInitBufSize);
|
||||
|
||||
private:
|
||||
RefPtr<IStream> mStream;
|
||||
BYTE* mGlobalLockedBuf;
|
||||
HGLOBAL mHGlobal;
|
||||
int mBufSize;
|
||||
ProxyUniquePtr<IUnknown> mUnmarshaledProxy;
|
||||
};
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_ProxyStream_h
|
||||
111
ipc/mscom/Ptr.h
Normal file
111
ipc/mscom/Ptr.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/* -*- 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_Ptr_h
|
||||
#define mozilla_mscom_Ptr_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/mscom/EnsureMTA.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsError.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
/**
|
||||
* The glue code in mozilla::mscom often needs to pass around interface pointers
|
||||
* belonging to a different apartment from the current one. We must not touch
|
||||
* the reference counts of those objects on the wrong apartment. By using these
|
||||
* UniquePtr specializations, we may ensure that the reference counts are always
|
||||
* handled correctly.
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
struct MainThreadRelease
|
||||
{
|
||||
void operator()(T* aPtr)
|
||||
{
|
||||
if (!aPtr) {
|
||||
return;
|
||||
}
|
||||
if (NS_IsMainThread()) {
|
||||
aPtr->Release();
|
||||
return;
|
||||
}
|
||||
DebugOnly<nsresult> rv =
|
||||
NS_DispatchToMainThread(NewNonOwningRunnableMethod(aPtr,
|
||||
&T::Release));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct MTARelease
|
||||
{
|
||||
void operator()(T* aPtr)
|
||||
{
|
||||
if (!aPtr) {
|
||||
return;
|
||||
}
|
||||
EnsureMTA([&]() -> void
|
||||
{
|
||||
aPtr->Release();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct MTAReleaseInChildProcess
|
||||
{
|
||||
void operator()(T* aPtr)
|
||||
{
|
||||
if (!aPtr) {
|
||||
return;
|
||||
}
|
||||
if (XRE_IsParentProcess()) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aPtr->Release();
|
||||
return;
|
||||
}
|
||||
EnsureMTA([&]() -> void
|
||||
{
|
||||
aPtr->Release();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
struct InterceptorTargetDeleter
|
||||
{
|
||||
void operator()(IUnknown* aPtr)
|
||||
{
|
||||
// We intentionally do not touch the refcounts of interceptor targets!
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
using STAUniquePtr = mozilla::UniquePtr<T, detail::MainThreadRelease<T>>;
|
||||
|
||||
template <typename T>
|
||||
using MTAUniquePtr = mozilla::UniquePtr<T, detail::MTARelease<T>>;
|
||||
|
||||
template <typename T>
|
||||
using ProxyUniquePtr = mozilla::UniquePtr<T, detail::MTAReleaseInChildProcess<T>>;
|
||||
|
||||
using InterceptorTargetPtr =
|
||||
mozilla::UniquePtr<IUnknown, detail::InterceptorTargetDeleter>;
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_Ptr_h
|
||||
|
||||
346
ipc/mscom/Registration.cpp
Normal file
346
ipc/mscom/Registration.cpp
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
// COM registration data structures are built with C code, so we need to
|
||||
// simulate that in our C++ code by defining CINTERFACE before including
|
||||
// anything else that could possibly pull in Windows header files.
|
||||
#define CINTERFACE
|
||||
|
||||
#include "mozilla/mscom/ActivationContext.h"
|
||||
#include "mozilla/mscom/EnsureMTA.h"
|
||||
#include "mozilla/mscom/Registration.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/Pair.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
|
||||
#include <oaidl.h>
|
||||
#include <objidl.h>
|
||||
#include <rpcproxy.h>
|
||||
#include <shlwapi.h>
|
||||
|
||||
/* This code MUST NOT use any non-inlined internal Mozilla APIs, as it will be
|
||||
compiled into DLLs that COM may load into non-Mozilla processes! */
|
||||
|
||||
namespace {
|
||||
|
||||
// This function is defined in generated code for proxy DLLs but is not declared
|
||||
// in rpcproxy.h, so we need this typedef.
|
||||
typedef void (RPC_ENTRY *GetProxyDllInfoFnPtr)(const ProxyFileInfo*** aInfo,
|
||||
const CLSID** aId);
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
static bool
|
||||
BuildLibPath(RegistrationFlags aFlags, wchar_t* aBuffer, size_t aBufferLen,
|
||||
const wchar_t* aLeafName)
|
||||
{
|
||||
if (aFlags == RegistrationFlags::eUseBinDirectory) {
|
||||
HMODULE thisModule = nullptr;
|
||||
if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
||||
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
reinterpret_cast<LPCTSTR>(&RegisterProxy),
|
||||
&thisModule)) {
|
||||
return false;
|
||||
}
|
||||
DWORD fileNameResult = GetModuleFileName(thisModule, aBuffer, aBufferLen);
|
||||
if (!fileNameResult || (fileNameResult == aBufferLen &&
|
||||
::GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
|
||||
return false;
|
||||
}
|
||||
if (!PathRemoveFileSpec(aBuffer)) {
|
||||
return false;
|
||||
}
|
||||
} else if (aFlags == RegistrationFlags::eUseSystemDirectory) {
|
||||
UINT result = GetSystemDirectoryW(aBuffer, static_cast<UINT>(aBufferLen));
|
||||
if (!result || result > aBufferLen) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PathAppend(aBuffer, aLeafName)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
UniquePtr<RegisteredProxy>
|
||||
RegisterProxy(const wchar_t* aLeafName, RegistrationFlags aFlags)
|
||||
{
|
||||
wchar_t modulePathBuf[MAX_PATH + 1] = {0};
|
||||
if (!BuildLibPath(aFlags, modulePathBuf, ArrayLength(modulePathBuf),
|
||||
aLeafName)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsModuleHandle proxyDll(LoadLibrary(modulePathBuf));
|
||||
if (!proxyDll.get()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Instantiate an activation context so that CoGetClassObject will use any
|
||||
// COM metadata embedded in proxyDll's manifest to resolve CLSIDs.
|
||||
ActivationContext actCtx(proxyDll);
|
||||
if (!actCtx) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto GetProxyDllInfoFn = reinterpret_cast<GetProxyDllInfoFnPtr>(
|
||||
GetProcAddress(proxyDll, "GetProxyDllInfo"));
|
||||
if (!GetProxyDllInfoFn) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ProxyFileInfo** proxyInfo = nullptr;
|
||||
const CLSID* proxyClsid = nullptr;
|
||||
GetProxyDllInfoFn(&proxyInfo, &proxyClsid);
|
||||
if (!proxyInfo || !proxyClsid) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We call CoGetClassObject instead of DllGetClassObject because it forces
|
||||
// the COM runtime to manage the lifetime of the DLL.
|
||||
IUnknown* classObject = nullptr;
|
||||
HRESULT hr = CoGetClassObject(*proxyClsid, CLSCTX_INPROC_SERVER, nullptr,
|
||||
IID_IUnknown, (void**) &classObject);
|
||||
if (FAILED(hr)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DWORD regCookie;
|
||||
hr = CoRegisterClassObject(*proxyClsid, classObject, CLSCTX_INPROC_SERVER,
|
||||
REGCLS_MULTIPLEUSE, ®Cookie);
|
||||
if (FAILED(hr)) {
|
||||
classObject->lpVtbl->Release(classObject);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ITypeLib* typeLib = nullptr;
|
||||
hr = LoadTypeLibEx(modulePathBuf, REGKIND_NONE, &typeLib);
|
||||
MOZ_ASSERT(SUCCEEDED(hr));
|
||||
if (FAILED(hr)) {
|
||||
CoRevokeClassObject(regCookie);
|
||||
classObject->lpVtbl->Release(classObject);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// RegisteredProxy takes ownership of proxyDll, classObject, and typeLib
|
||||
// references
|
||||
auto result(MakeUnique<RegisteredProxy>(reinterpret_cast<uintptr_t>(proxyDll.disown()),
|
||||
classObject, regCookie, typeLib));
|
||||
|
||||
while (*proxyInfo) {
|
||||
const ProxyFileInfo& curInfo = **proxyInfo;
|
||||
for (unsigned short i = 0, e = curInfo.TableSize; i < e; ++i) {
|
||||
hr = CoRegisterPSClsid(*(curInfo.pStubVtblList[i]->header.piid),
|
||||
*proxyClsid);
|
||||
if (FAILED(hr)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
++proxyInfo;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UniquePtr<RegisteredProxy>
|
||||
RegisterTypelib(const wchar_t* aLeafName, RegistrationFlags aFlags)
|
||||
{
|
||||
wchar_t modulePathBuf[MAX_PATH + 1] = {0};
|
||||
if (!BuildLibPath(aFlags, modulePathBuf, ArrayLength(modulePathBuf),
|
||||
aLeafName)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ITypeLib* typeLib = nullptr;
|
||||
HRESULT hr = LoadTypeLibEx(modulePathBuf, REGKIND_NONE, &typeLib);
|
||||
if (FAILED(hr)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// RegisteredProxy takes ownership of typeLib reference
|
||||
auto result(MakeUnique<RegisteredProxy>(typeLib));
|
||||
return result;
|
||||
}
|
||||
|
||||
RegisteredProxy::RegisteredProxy(uintptr_t aModule, IUnknown* aClassObject,
|
||||
uint32_t aRegCookie, ITypeLib* aTypeLib)
|
||||
: mModule(aModule)
|
||||
, mClassObject(aClassObject)
|
||||
, mRegCookie(aRegCookie)
|
||||
, mTypeLib(aTypeLib)
|
||||
, mIsRegisteredInMTA(IsCurrentThreadMTA())
|
||||
{
|
||||
MOZ_ASSERT(aClassObject);
|
||||
MOZ_ASSERT(aTypeLib);
|
||||
AddToRegistry(this);
|
||||
}
|
||||
|
||||
// If we're initializing from a typelib, it doesn't matter which apartment we
|
||||
// run in, so mIsRegisteredInMTA may always be set to false in this case.
|
||||
RegisteredProxy::RegisteredProxy(ITypeLib* aTypeLib)
|
||||
: mModule(0)
|
||||
, mClassObject(nullptr)
|
||||
, mRegCookie(0)
|
||||
, mTypeLib(aTypeLib)
|
||||
, mIsRegisteredInMTA(false)
|
||||
{
|
||||
MOZ_ASSERT(aTypeLib);
|
||||
AddToRegistry(this);
|
||||
}
|
||||
|
||||
RegisteredProxy::~RegisteredProxy()
|
||||
{
|
||||
DeleteFromRegistry(this);
|
||||
if (mTypeLib) {
|
||||
mTypeLib->lpVtbl->Release(mTypeLib);
|
||||
}
|
||||
if (mClassObject) {
|
||||
// NB: mClassObject and mRegCookie must be freed from inside the apartment
|
||||
// which they were created in.
|
||||
auto cleanupFn = [&]() -> void {
|
||||
::CoRevokeClassObject(mRegCookie);
|
||||
mClassObject->lpVtbl->Release(mClassObject);
|
||||
};
|
||||
if (mIsRegisteredInMTA) {
|
||||
EnsureMTA mta(cleanupFn);
|
||||
} else {
|
||||
cleanupFn();
|
||||
}
|
||||
}
|
||||
if (mModule) {
|
||||
::FreeLibrary(reinterpret_cast<HMODULE>(mModule));
|
||||
}
|
||||
}
|
||||
|
||||
RegisteredProxy::RegisteredProxy(RegisteredProxy&& aOther)
|
||||
{
|
||||
*this = mozilla::Forward<RegisteredProxy>(aOther);
|
||||
}
|
||||
|
||||
RegisteredProxy&
|
||||
RegisteredProxy::operator=(RegisteredProxy&& aOther)
|
||||
{
|
||||
mModule = aOther.mModule;
|
||||
aOther.mModule = 0;
|
||||
mClassObject = aOther.mClassObject;
|
||||
aOther.mClassObject = nullptr;
|
||||
mRegCookie = aOther.mRegCookie;
|
||||
aOther.mRegCookie = 0;
|
||||
mTypeLib = aOther.mTypeLib;
|
||||
aOther.mTypeLib = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
RegisteredProxy::GetTypeInfoForInterface(REFIID aIid,
|
||||
ITypeInfo** aOutTypeInfo) const
|
||||
{
|
||||
if (!aOutTypeInfo) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (!mTypeLib) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
return mTypeLib->lpVtbl->GetTypeInfoOfGuid(mTypeLib, aIid, aOutTypeInfo);
|
||||
}
|
||||
|
||||
static StaticAutoPtr<nsTArray<RegisteredProxy*>> sRegistry;
|
||||
static StaticAutoPtr<Mutex> sRegMutex;
|
||||
static StaticAutoPtr<nsTArray<Pair<const ArrayData*, size_t>>> sArrayData;
|
||||
|
||||
static Mutex&
|
||||
GetMutex()
|
||||
{
|
||||
static Mutex& mutex = []() -> Mutex& {
|
||||
if (!sRegMutex) {
|
||||
sRegMutex = new Mutex("RegisteredProxy::sRegMutex");
|
||||
ClearOnShutdown(&sRegMutex, ShutdownPhase::ShutdownThreads);
|
||||
}
|
||||
return *sRegMutex;
|
||||
}();
|
||||
return mutex;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
RegisteredProxy::Find(REFIID aIid, ITypeInfo** aTypeInfo)
|
||||
{
|
||||
MutexAutoLock lock(GetMutex());
|
||||
nsTArray<RegisteredProxy*>& registry = *sRegistry;
|
||||
for (uint32_t idx = 0, len = registry.Length(); idx < len; ++idx) {
|
||||
if (SUCCEEDED(registry[idx]->GetTypeInfoForInterface(aIid, aTypeInfo))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
RegisteredProxy::AddToRegistry(RegisteredProxy* aProxy)
|
||||
{
|
||||
MutexAutoLock lock(GetMutex());
|
||||
if (!sRegistry) {
|
||||
sRegistry = new nsTArray<RegisteredProxy*>();
|
||||
ClearOnShutdown(&sRegistry);
|
||||
}
|
||||
sRegistry->AppendElement(aProxy);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
RegisteredProxy::DeleteFromRegistry(RegisteredProxy* aProxy)
|
||||
{
|
||||
MutexAutoLock lock(GetMutex());
|
||||
sRegistry->RemoveElement(aProxy);
|
||||
}
|
||||
|
||||
void
|
||||
RegisterArrayData(const ArrayData* aArrayData, size_t aLength)
|
||||
{
|
||||
MutexAutoLock lock(GetMutex());
|
||||
if (!sArrayData) {
|
||||
sArrayData = new nsTArray<Pair<const ArrayData*, size_t>>();
|
||||
ClearOnShutdown(&sArrayData, ShutdownPhase::ShutdownThreads);
|
||||
}
|
||||
sArrayData->AppendElement(MakePair(aArrayData, aLength));
|
||||
}
|
||||
|
||||
const ArrayData*
|
||||
FindArrayData(REFIID aIid, ULONG aMethodIndex)
|
||||
{
|
||||
MutexAutoLock lock(GetMutex());
|
||||
if (!sArrayData) {
|
||||
return nullptr;
|
||||
}
|
||||
for (uint32_t outerIdx = 0, outerLen = sArrayData->Length();
|
||||
outerIdx < outerLen; ++outerIdx) {
|
||||
auto& data = sArrayData->ElementAt(outerIdx);
|
||||
for (size_t innerIdx = 0, innerLen = data.second(); innerIdx < innerLen;
|
||||
++innerIdx) {
|
||||
const ArrayData* array = data.first();
|
||||
if (aIid == array[innerIdx].mIid &&
|
||||
aMethodIndex == array[innerIdx].mMethodIndex) {
|
||||
return &array[innerIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
145
ipc/mscom/Registration.h
Normal file
145
ipc/mscom/Registration.h
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/* -*- 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_Registration_h
|
||||
#define mozilla_mscom_Registration_h
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include <objbase.h>
|
||||
|
||||
struct ITypeInfo;
|
||||
struct ITypeLib;
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
/**
|
||||
* Assumptions:
|
||||
* (1) The DLL exports GetProxyDllInfo. This is not exported by default; it must
|
||||
* be specified in the EXPORTS section of the DLL's module definition file.
|
||||
*/
|
||||
class RegisteredProxy
|
||||
{
|
||||
public:
|
||||
RegisteredProxy(uintptr_t aModule, 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;
|
||||
|
||||
static bool Find(REFIID aIid, ITypeInfo** aOutTypeInfo);
|
||||
|
||||
private:
|
||||
RegisteredProxy() = delete;
|
||||
RegisteredProxy(RegisteredProxy&) = delete;
|
||||
RegisteredProxy& operator=(RegisteredProxy&) = delete;
|
||||
|
||||
static void AddToRegistry(RegisteredProxy* aProxy);
|
||||
static void DeleteFromRegistry(RegisteredProxy* aProxy);
|
||||
|
||||
private:
|
||||
// Not using Windows types here: We shouldn't #include windows.h
|
||||
// since it might pull in COM code which we want to do very carefully in
|
||||
// Registration.cpp.
|
||||
uintptr_t mModule;
|
||||
IUnknown* mClassObject;
|
||||
uint32_t mRegCookie;
|
||||
ITypeLib* mTypeLib;
|
||||
bool mIsRegisteredInMTA;
|
||||
};
|
||||
|
||||
enum class RegistrationFlags
|
||||
{
|
||||
eUseBinDirectory,
|
||||
eUseSystemDirectory
|
||||
};
|
||||
|
||||
// For DLL files. Assumes corresponding TLB is embedded in resources.
|
||||
UniquePtr<RegisteredProxy> RegisterProxy(const wchar_t* aLeafName,
|
||||
RegistrationFlags aFlags =
|
||||
RegistrationFlags::eUseBinDirectory);
|
||||
// For standalone TLB files.
|
||||
UniquePtr<RegisteredProxy> RegisterTypelib(const wchar_t* aLeafName,
|
||||
RegistrationFlags aFlags =
|
||||
RegistrationFlags::eUseBinDirectory);
|
||||
|
||||
/**
|
||||
* The COM interceptor uses type library information to build its interface
|
||||
* proxies. Unfortunately type libraries do not encode size_is and length_is
|
||||
* annotations that have been specified in IDL. This structure allows us to
|
||||
* explicitly declare such relationships so that the COM interceptor may
|
||||
* be made aware of them.
|
||||
*/
|
||||
struct ArrayData
|
||||
{
|
||||
enum class Flag
|
||||
{
|
||||
eNone = 0,
|
||||
eAllocatedByServer = 1 // This implies an extra level of indirection
|
||||
};
|
||||
|
||||
ArrayData(REFIID aIid, ULONG aMethodIndex, ULONG aArrayParamIndex,
|
||||
VARTYPE aArrayParamType, REFIID aArrayParamIid,
|
||||
ULONG aLengthParamIndex, Flag aFlag = Flag::eNone)
|
||||
: mIid(aIid)
|
||||
, mMethodIndex(aMethodIndex)
|
||||
, mArrayParamIndex(aArrayParamIndex)
|
||||
, mArrayParamType(aArrayParamType)
|
||||
, mArrayParamIid(aArrayParamIid)
|
||||
, mLengthParamIndex(aLengthParamIndex)
|
||||
, mFlag(aFlag)
|
||||
{
|
||||
}
|
||||
|
||||
ArrayData(const ArrayData& aOther)
|
||||
{
|
||||
*this = aOther;
|
||||
}
|
||||
|
||||
ArrayData& operator=(const ArrayData& aOther)
|
||||
{
|
||||
mIid = aOther.mIid;
|
||||
mMethodIndex = aOther.mMethodIndex;
|
||||
mArrayParamIndex = aOther.mArrayParamIndex;
|
||||
mArrayParamType = aOther.mArrayParamType;
|
||||
mArrayParamIid = aOther.mArrayParamIid;
|
||||
mLengthParamIndex = aOther.mLengthParamIndex;
|
||||
mFlag = aOther.mFlag;
|
||||
return *this;
|
||||
}
|
||||
|
||||
IID mIid;
|
||||
ULONG mMethodIndex;
|
||||
ULONG mArrayParamIndex;
|
||||
VARTYPE mArrayParamType;
|
||||
IID mArrayParamIid;
|
||||
ULONG mLengthParamIndex;
|
||||
Flag mFlag;
|
||||
};
|
||||
|
||||
void RegisterArrayData(const ArrayData* aArrayData, size_t aLength);
|
||||
|
||||
template <size_t N>
|
||||
inline void
|
||||
RegisterArrayData(const ArrayData (&aData)[N])
|
||||
{
|
||||
RegisterArrayData(aData, N);
|
||||
}
|
||||
|
||||
const ArrayData*
|
||||
FindArrayData(REFIID aIid, ULONG aMethodIndex);
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_Registration_h
|
||||
|
||||
94
ipc/mscom/Utils.cpp
Normal file
94
ipc/mscom/Utils.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
// We need Windows 7 headers
|
||||
#ifdef NTDDI_VERSION
|
||||
#undef NTDDI_VERSION
|
||||
#endif
|
||||
#define NTDDI_VERSION 0x06010000
|
||||
#ifdef _WIN32_WINNT
|
||||
#undef _WIN32_WINNT
|
||||
#endif
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include "DynamicallyLinkedFunctionPtr.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
#include <objidl.h>
|
||||
|
||||
static bool
|
||||
IsCurrentThreadMTALegacy()
|
||||
{
|
||||
// We don't use RefPtr for token because CoGetContextToken does *not*
|
||||
// increment its refcount!
|
||||
IUnknown* token = nullptr;
|
||||
HRESULT hr =
|
||||
CoGetContextToken(reinterpret_cast<ULONG_PTR*>(&token));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<IComThreadingInfo> threadingInfo;
|
||||
hr = token->QueryInterface(IID_IComThreadingInfo,
|
||||
getter_AddRefs(threadingInfo));
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
APTTYPE aptType;
|
||||
hr = threadingInfo->GetCurrentApartmentType(&aptType);
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return aptType == APTTYPE_MTA;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
bool
|
||||
IsCurrentThreadMTA()
|
||||
{
|
||||
static DynamicallyLinkedFunctionPtr<decltype(&::CoGetApartmentType)>
|
||||
pCoGetApartmentType(L"ole32.dll", "CoGetApartmentType");
|
||||
|
||||
if (!pCoGetApartmentType) {
|
||||
// XP and Vista do not expose the newer API.
|
||||
return IsCurrentThreadMTALegacy();
|
||||
}
|
||||
|
||||
APTTYPE aptType;
|
||||
APTTYPEQUALIFIER aptTypeQualifier;
|
||||
HRESULT hr = pCoGetApartmentType(&aptType, &aptTypeQualifier);
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return aptType == APTTYPE_MTA;
|
||||
}
|
||||
|
||||
bool
|
||||
IsProxy(IUnknown* aUnknown)
|
||||
{
|
||||
if (!aUnknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only proxies implement this interface, so if it is present then we must
|
||||
// be dealing with a proxied object.
|
||||
RefPtr<IClientSecurity> clientSecurity;
|
||||
HRESULT hr = aUnknown->QueryInterface(IID_IClientSecurity,
|
||||
(void**)getter_AddRefs(clientSecurity));
|
||||
if (SUCCEEDED(hr) || hr == RPC_E_WRONG_THREAD) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
22
ipc/mscom/Utils.h
Normal file
22
ipc/mscom/Utils.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* -*- 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_Utils_h
|
||||
#define mozilla_mscom_Utils_h
|
||||
|
||||
struct IUnknown;
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
bool IsCurrentThreadMTA();
|
||||
bool IsProxy(IUnknown* aUnknown);
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_Utils_h
|
||||
|
||||
191
ipc/mscom/WeakRef.cpp
Normal file
191
ipc/mscom/WeakRef.cpp
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#define INITGUID
|
||||
#include "mozilla/mscom/WeakRef.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsWindowsHelpers.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
WeakReferenceSupport::WeakReferenceSupport(Flags aFlags)
|
||||
: mRefCnt(1)
|
||||
, mFlags(aFlags)
|
||||
{
|
||||
::InitializeCriticalSectionAndSpinCount(&mCS, 4000);
|
||||
}
|
||||
|
||||
WeakReferenceSupport::~WeakReferenceSupport()
|
||||
{
|
||||
MOZ_ASSERT(mWeakRefs.IsEmpty());
|
||||
::DeleteCriticalSection(&mCS);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WeakReferenceSupport::QueryInterface(REFIID riid, void** ppv)
|
||||
{
|
||||
AutoCriticalSection lock(&mCS);
|
||||
RefPtr<IUnknown> punk;
|
||||
if (!ppv) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
*ppv = nullptr;
|
||||
|
||||
// Raise the refcount for stabilization purposes during aggregation
|
||||
RefPtr<IUnknown> kungFuDeathGrip(static_cast<IUnknown*>(this));
|
||||
|
||||
if (riid == IID_IUnknown || riid == IID_IWeakReferenceSource) {
|
||||
punk = static_cast<IUnknown*>(this);
|
||||
} else {
|
||||
HRESULT hr = ThreadSafeQueryInterface(riid, getter_AddRefs(punk));
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!punk) {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
punk.forget(ppv);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ULONG
|
||||
WeakReferenceSupport::AddRef()
|
||||
{
|
||||
AutoCriticalSection lock(&mCS);
|
||||
return ++mRefCnt;
|
||||
}
|
||||
|
||||
ULONG
|
||||
WeakReferenceSupport::Release()
|
||||
{
|
||||
ULONG newRefCnt;
|
||||
{ // Scope for lock
|
||||
AutoCriticalSection lock(&mCS);
|
||||
newRefCnt = --mRefCnt;
|
||||
if (newRefCnt == 0) {
|
||||
ClearWeakRefs();
|
||||
}
|
||||
}
|
||||
if (newRefCnt == 0) {
|
||||
if (mFlags != Flags::eDestroyOnMainThread || NS_IsMainThread()) {
|
||||
delete this;
|
||||
} else {
|
||||
// We need to delete this object on the main thread, but we aren't on the
|
||||
// main thread right now, so we send a reference to ourselves to the main
|
||||
// thread to be re-released there.
|
||||
RefPtr<WeakReferenceSupport> self = this;
|
||||
NS_ReleaseOnMainThread(self.forget());
|
||||
}
|
||||
}
|
||||
return newRefCnt;
|
||||
}
|
||||
|
||||
void
|
||||
WeakReferenceSupport::ClearWeakRefs()
|
||||
{
|
||||
for (uint32_t i = 0, len = mWeakRefs.Length(); i < len; ++i) {
|
||||
mWeakRefs[i]->Clear();
|
||||
mWeakRefs[i]->Release();
|
||||
}
|
||||
mWeakRefs.Clear();
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WeakReferenceSupport::GetWeakReference(IWeakReference** aOutWeakRef)
|
||||
{
|
||||
if (!aOutWeakRef) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
*aOutWeakRef = nullptr;
|
||||
|
||||
AutoCriticalSection lock(&mCS);
|
||||
RefPtr<WeakRef> weakRef = MakeAndAddRef<WeakRef>(this);
|
||||
|
||||
HRESULT hr = weakRef->QueryInterface(IID_IWeakReference, (void**)aOutWeakRef);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
mWeakRefs.AppendElement(weakRef.get());
|
||||
weakRef->AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
WeakRef::WeakRef(WeakReferenceSupport* aSupport)
|
||||
: mRefCnt(1)
|
||||
, mMutex("mozilla::mscom::WeakRef::mMutex")
|
||||
, mSupport(aSupport)
|
||||
{
|
||||
MOZ_ASSERT(aSupport);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WeakRef::QueryInterface(REFIID riid, void** ppv)
|
||||
{
|
||||
IUnknown* punk = nullptr;
|
||||
if (!ppv) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (riid == IID_IUnknown || riid == IID_IWeakReference) {
|
||||
punk = static_cast<IUnknown*>(this);
|
||||
}
|
||||
|
||||
*ppv = punk;
|
||||
if (!punk) {
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
punk->AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ULONG
|
||||
WeakRef::AddRef()
|
||||
{
|
||||
return (ULONG) InterlockedIncrement((LONG*)&mRefCnt);
|
||||
}
|
||||
|
||||
ULONG
|
||||
WeakRef::Release()
|
||||
{
|
||||
ULONG newRefCnt = (ULONG) InterlockedDecrement((LONG*)&mRefCnt);
|
||||
if (newRefCnt == 0) {
|
||||
delete this;
|
||||
}
|
||||
return newRefCnt;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
WeakRef::Resolve(REFIID aIid, void** aOutStrongReference)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (!mSupport) {
|
||||
return E_FAIL;
|
||||
}
|
||||
return mSupport->QueryInterface(aIid, aOutStrongReference);
|
||||
}
|
||||
|
||||
void
|
||||
WeakRef::Clear()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(mSupport);
|
||||
mSupport = nullptr;
|
||||
}
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
104
ipc/mscom/WeakRef.h
Normal file
104
ipc/mscom/WeakRef.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/* -*- 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_WeakRef_h
|
||||
#define mozilla_mscom_WeakRef_h
|
||||
|
||||
#include <guiddef.h>
|
||||
#include <unknwn.h>
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
/**
|
||||
* Thread-safe weak references for COM that works pre-Windows 8 and do not
|
||||
* require WinRT.
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
// {F841AEFA-064C-49A4-B73D-EBD14A90F012}
|
||||
DEFINE_GUID(IID_IWeakReference,
|
||||
0xf841aefa, 0x64c, 0x49a4, 0xb7, 0x3d, 0xeb, 0xd1, 0x4a, 0x90, 0xf0, 0x12);
|
||||
|
||||
struct IWeakReference : public IUnknown
|
||||
{
|
||||
virtual STDMETHODIMP Resolve(REFIID aIid, void** aOutStringReference) = 0;
|
||||
};
|
||||
|
||||
// {87611F0C-9BBB-4F78-9D43-CAC5AD432CA1}
|
||||
DEFINE_GUID(IID_IWeakReferenceSource,
|
||||
0x87611f0c, 0x9bbb, 0x4f78, 0x9d, 0x43, 0xca, 0xc5, 0xad, 0x43, 0x2c, 0xa1);
|
||||
|
||||
struct IWeakReferenceSource : public IUnknown
|
||||
{
|
||||
virtual STDMETHODIMP GetWeakReference(IWeakReference** aOutWeakRef) = 0;
|
||||
};
|
||||
|
||||
class WeakRef;
|
||||
|
||||
class WeakReferenceSupport : public IWeakReferenceSource
|
||||
{
|
||||
public:
|
||||
enum class Flags
|
||||
{
|
||||
eNone = 0,
|
||||
eDestroyOnMainThread = 1
|
||||
};
|
||||
|
||||
// IUnknown
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
|
||||
STDMETHODIMP_(ULONG) AddRef() override;
|
||||
STDMETHODIMP_(ULONG) Release() override;
|
||||
|
||||
// IWeakReferenceSource
|
||||
STDMETHODIMP GetWeakReference(IWeakReference** aOutWeakRef) override;
|
||||
|
||||
protected:
|
||||
explicit WeakReferenceSupport(Flags aFlags);
|
||||
virtual ~WeakReferenceSupport();
|
||||
|
||||
virtual HRESULT ThreadSafeQueryInterface(REFIID aIid,
|
||||
IUnknown** aOutInterface) = 0;
|
||||
|
||||
private:
|
||||
void ClearWeakRefs();
|
||||
|
||||
private:
|
||||
// Using a raw CRITICAL_SECTION here because it can be reentered
|
||||
CRITICAL_SECTION mCS;
|
||||
ULONG mRefCnt;
|
||||
nsTArray<WeakRef*> mWeakRefs;
|
||||
Flags mFlags;
|
||||
};
|
||||
|
||||
class WeakRef : public IWeakReference
|
||||
{
|
||||
public:
|
||||
// IUnknown
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
|
||||
STDMETHODIMP_(ULONG) AddRef() override;
|
||||
STDMETHODIMP_(ULONG) Release() override;
|
||||
|
||||
// IWeakReference
|
||||
STDMETHODIMP Resolve(REFIID aIid, void** aOutStrongReference) override;
|
||||
|
||||
explicit WeakRef(WeakReferenceSupport* aSupport);
|
||||
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
ULONG mRefCnt;
|
||||
mozilla::Mutex mMutex; // Protects mSupport
|
||||
WeakReferenceSupport* mSupport;
|
||||
};
|
||||
|
||||
} // namespace mscom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_mscom_WeakRef_h
|
||||
|
||||
60
ipc/mscom/moz.build
Normal file
60
ipc/mscom/moz.build
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# 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',
|
||||
'EnsureMTA.h',
|
||||
'MainThreadRuntime.h',
|
||||
'ProxyStream.h',
|
||||
'Ptr.h',
|
||||
'Utils.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'Utils.cpp',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'EnsureMTA.cpp',
|
||||
'MainThreadRuntime.cpp',
|
||||
'ProxyStream.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['ACCESSIBILITY']:
|
||||
EXPORTS.mozilla.mscom += [
|
||||
'ActivationContext.h',
|
||||
'DispatchForwarder.h',
|
||||
'Interceptor.h',
|
||||
'InterceptorLog.h',
|
||||
'MainThreadHandoff.h',
|
||||
'MainThreadInvoker.h',
|
||||
'Registration.h',
|
||||
'WeakRef.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'Interceptor.cpp',
|
||||
'Registration.cpp',
|
||||
'WeakRef.cpp',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ActivationContext.cpp',
|
||||
'DispatchForwarder.cpp',
|
||||
'InterceptorLog.cpp',
|
||||
'MainThreadHandoff.cpp',
|
||||
'MainThreadInvoker.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/xpcom/base',
|
||||
'/xpcom/build',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
Loading…
Add table
Add a link
Reference in a new issue