import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View 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

View 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

View 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
View 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

View 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

View 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

View 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
View 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
View 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
View 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
View 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

View 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, &paramInfo);
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, &paramValue);
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

View 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

View 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(&paramVal);
HRESULT hr = aFrame->GetParam(aArrayData.mLengthParamIndex, &paramVal);
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(&paramVal);
PVOID arrayPtr = nullptr;
hr = aFrame->GetParam(aArrayData.mArrayParamIndex, &paramVal);
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, &paramInfo);
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

View 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

View 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

View 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

View 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

View 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
View 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
View 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
View 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
View 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, &regCookie);
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
View 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
View 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
View 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
View 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
View 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
View 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'